Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the ComponentPicker independent of the Toolbar #3142

Merged
merged 4 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import * as ReactDOM from 'react-dom';

import useModal from '../../hooks/useModal';
import Button from '../../ui/Button';
import {DialogActions} from '../../ui/Dialog';
import {INSERT_FIGMA_COMMAND} from '../FigmaPlugin';
import {INSERT_TWEET_COMMAND} from '../TwitterPlugin';
import {INSERT_YOUTUBE_COMMAND} from '../YouTubePlugin';
Expand Down Expand Up @@ -248,14 +249,14 @@ export function AutoEmbedDialog({
}}
/>
</div>
<div className="ToolbarPlugin__dialogActions">
<DialogActions>
<Button
disabled={!embedResult}
onClick={onClick}
data-test-id={`${embedConfig.type}-embed-modal-submit-btn`}>
Embed
</Button>
</div>
</DialogActions>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,11 @@ import useModal from '../../hooks/useModal';
import catTypingGif from '../../images/cat-typing.gif';
import {EmbedConfigs} from '../AutoEmbedPlugin';
import {INSERT_COLLAPSIBLE_COMMAND} from '../CollapsiblePlugin';
import {InsertEquationDialog} from '../EquationsPlugin';
import {INSERT_EXCALIDRAW_COMMAND} from '../ExcalidrawPlugin';
import {INSERT_IMAGE_COMMAND} from '../ImagesPlugin';
import {
InsertEquationDialog,
InsertImageDialog,
InsertPollDialog,
InsertTableDialog,
} from '../ToolbarPlugin';
import {INSERT_IMAGE_COMMAND, InsertImageDialog} from '../ImagesPlugin';
import {InsertPollDialog} from '../PollPlugin';
import {InsertTableDialog} from '../TablePlugin';

class ComponentPickerOption extends TypeaheadOption {
// What shows up in the editor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ import {
COMMAND_PRIORITY_EDITOR,
createCommand,
LexicalCommand,
LexicalEditor,
} from 'lexical';
import {useEffect} from 'react';
import {useCallback, useEffect} from 'react';
import * as React from 'react';

import {$createEquationNode, EquationNode} from '../../nodes/EquationNode';
import KatexEquationAlterer from '../../ui/KatexEquationAlterer';

type CommandPayload = {
equation: string;
Expand All @@ -30,6 +33,24 @@ type CommandPayload = {
export const INSERT_EQUATION_COMMAND: LexicalCommand<CommandPayload> =
createCommand();

export function InsertEquationDialog({
activeEditor,
onClose,
}: {
activeEditor: LexicalEditor;
onClose: () => void;
}): JSX.Element {
const onEquationConfirm = useCallback(
(equation: string, inline: boolean) => {
activeEditor.dispatchCommand(INSERT_EQUATION_COMMAND, {equation, inline});
onClose();
},
[activeEditor, onClose],
);

return <KatexEquationAlterer onConfirm={onEquationConfirm} />;
}

export default function EquationsPlugin(): JSX.Element | null {
const [editor] = useLexicalComposerContext();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,182 @@ import {
LexicalCommand,
LexicalEditor,
} from 'lexical';
import {useEffect} from 'react';
import {useEffect, useRef, useState} from 'react';
import * as React from 'react';
import getSelection from 'shared/getDOMSelection';

import landscapeImage from '../../images/landscape.jpg';
import yellowFlowerImage from '../../images/yellow-flower.jpg';
import {
$createImageNode,
$isImageNode,
ImageNode,
ImagePayload,
} from '../../nodes/ImageNode';
import Button from '../../ui/Button';
import {DialogActions, DialogButtonsList} from '../../ui/Dialog';
import FileInput from '../../ui/FileInput';
import TextInput from '../../ui/TextInput';

export type InsertImagePayload = Readonly<ImagePayload>;

export const INSERT_IMAGE_COMMAND: LexicalCommand<InsertImagePayload> =
createCommand();

export function InsertImageUriDialogBody({
onClick,
}: {
onClick: (payload: InsertImagePayload) => void;
}) {
const [src, setSrc] = useState('');
const [altText, setAltText] = useState('');

const isDisabled = src === '';

return (
<>
<TextInput
label="Image URL"
placeholder="i.e. https://source.unsplash.com/random"
onChange={setSrc}
value={src}
data-test-id="image-modal-url-input"
/>
<TextInput
label="Alt Text"
placeholder="Random unsplash image"
onChange={setAltText}
value={altText}
data-test-id="image-modal-alt-text-input"
/>
<DialogActions>
<Button
data-test-id="image-modal-confirm-btn"
disabled={isDisabled}
onClick={() => onClick({altText, src})}>
Confirm
</Button>
</DialogActions>
</>
);
}

export function InsertImageUploadedDialogBody({
onClick,
}: {
onClick: (payload: InsertImagePayload) => void;
}) {
const [src, setSrc] = useState('');
const [altText, setAltText] = useState('');

const isDisabled = src === '';

const loadImage = (files: FileList | null) => {
const reader = new FileReader();
reader.onload = function () {
if (typeof reader.result === 'string') {
setSrc(reader.result);
}
return '';
};
if (files !== null) {
reader.readAsDataURL(files[0]);
}
};

return (
<>
<FileInput
label="Image Upload"
onChange={loadImage}
accept="image/*"
data-test-id="image-modal-file-upload"
/>
<TextInput
label="Alt Text"
placeholder="Descriptive alternative text"
onChange={setAltText}
value={altText}
data-test-id="image-modal-alt-text-input"
/>
<DialogActions>
<Button
data-test-id="image-modal-file-upload-btn"
disabled={isDisabled}
onClick={() => onClick({altText, src})}>
Confirm
</Button>
</DialogActions>
</>
);
}

export function InsertImageDialog({
activeEditor,
onClose,
}: {
activeEditor: LexicalEditor;
onClose: () => void;
}): JSX.Element {
const [mode, setMode] = useState<null | 'url' | 'file'>(null);
const hasModifier = useRef(false);

useEffect(() => {
hasModifier.current = false;
const handler = (e: KeyboardEvent) => {
hasModifier.current = e.altKey;
};
document.addEventListener('keydown', handler);
return () => {
document.removeEventListener('keydown', handler);
};
}, [activeEditor]);

const onClick = (payload: InsertImagePayload) => {
activeEditor.dispatchCommand(INSERT_IMAGE_COMMAND, payload);
onClose();
};

return (
<>
{!mode && (
<DialogButtonsList>
<Button
data-test-id="image-modal-option-sample"
onClick={() =>
onClick(
hasModifier.current
? {
altText:
'Daylight fir trees forest glacier green high ice landscape',
src: landscapeImage,
}
: {
altText: 'Yellow flower in tilt shift lens',
src: yellowFlowerImage,
},
)
}>
Sample
</Button>
<Button
data-test-id="image-modal-option-url"
onClick={() => setMode('url')}>
URL
</Button>
<Button
data-test-id="image-modal-option-file"
onClick={() => setMode('file')}>
File
</Button>
</DialogButtonsList>
)}
{mode === 'url' && <InsertImageUriDialogBody onClick={onClick} />}
{mode === 'file' && <InsertImageUploadedDialogBody onClick={onClick} />}
</>
);
}

export default function ImagesPlugin({
captionsEnabled,
}: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,44 @@ import {
COMMAND_PRIORITY_EDITOR,
createCommand,
LexicalCommand,
LexicalEditor,
} from 'lexical';
import {useEffect} from 'react';
import {useEffect, useState} from 'react';
import * as React from 'react';

import {$createPollNode, PollNode} from '../../nodes/PollNode';
import Button from '../../ui/Button';
import {DialogActions} from '../../ui/Dialog';
import TextInput from '../../ui/TextInput';

export const INSERT_POLL_COMMAND: LexicalCommand<string> = createCommand();

export function InsertPollDialog({
activeEditor,
onClose,
}: {
activeEditor: LexicalEditor;
onClose: () => void;
}): JSX.Element {
const [question, setQuestion] = useState('');

const onClick = () => {
activeEditor.dispatchCommand(INSERT_POLL_COMMAND, question);
onClose();
};

return (
<>
<TextInput label="Question" onChange={setQuestion} value={question} />
<DialogActions>
<Button disabled={question.trim() === ''} onClick={onClick}>
Confirm
</Button>
</DialogActions>
</>
);
}

export default function PollPlugin(): JSX.Element | null {
const [editor] = useLexicalComposerContext();
useEffect(() => {
Expand Down
55 changes: 55 additions & 0 deletions packages/lexical-playground/src/plugins/TablePlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import * as React from 'react';
import invariant from 'shared/invariant';

import {$createTableNodeWithDimensions, TableNode} from '../nodes/TableNode';
import Button from '../ui/Button';
import {DialogActions} from '../ui/Dialog';
import TextInput from '../ui/TextInput';

export type InsertTableCommandPayload = Readonly<{
columns: string;
Expand Down Expand Up @@ -88,6 +91,58 @@ export function TableContext({children}: {children: JSX.Element}) {
);
}

export function InsertTableDialog({
GermanJablo marked this conversation as resolved.
Show resolved Hide resolved
activeEditor,
onClose,
}: {
activeEditor: LexicalEditor;
onClose: () => void;
}): JSX.Element {
const [rows, setRows] = useState('5');
const [columns, setColumns] = useState('5');

const onClick = () => {
activeEditor.dispatchCommand(INSERT_TABLE_COMMAND, {columns, rows});
onClose();
};

return (
<>
<TextInput label="No of rows" onChange={setRows} value={rows} />
<TextInput label="No of columns" onChange={setColumns} value={columns} />
<DialogActions data-test-id="table-modal-confirm-insert">
<Button onClick={onClick}>Confirm</Button>
</DialogActions>
</>
);
}

export function InsertNewTableDialog({
activeEditor,
onClose,
}: {
activeEditor: LexicalEditor;
onClose: () => void;
}): JSX.Element {
const [rows, setRows] = useState('5');
const [columns, setColumns] = useState('5');

const onClick = () => {
activeEditor.dispatchCommand(INSERT_TABLE_COMMAND, {columns, rows});
onClose();
};

return (
<>
<TextInput label="No of rows" onChange={setRows} value={rows} />
<TextInput label="No of columns" onChange={setColumns} value={columns} />
<DialogActions data-test-id="table-modal-confirm-insert">
<Button onClick={onClick}>Confirm</Button>
</DialogActions>
</>
);
}

export function TablePlugin({
cellEditorConfig,
children,
Expand Down