diff --git a/package-lock.json b/package-lock.json index 90380e21120..6553ad7b38f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26436,7 +26436,7 @@ "dev": true, "requires": { "@types/node": "*", - "playwright-core": "1.23.0-next-alpha-trueadm-fork" + "playwright-core": "1.23.1" } }, "@polka/url": { diff --git a/packages/lexical-playground/src/plugins/AutoEmbedPlugin/index.tsx b/packages/lexical-playground/src/plugins/AutoEmbedPlugin/index.tsx index 177fcc2e295..0110fc6f878 100644 --- a/packages/lexical-playground/src/plugins/AutoEmbedPlugin/index.tsx +++ b/packages/lexical-playground/src/plugins/AutoEmbedPlugin/index.tsx @@ -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'; @@ -248,14 +249,14 @@ export function AutoEmbedDialog({ }} /> -
+ -
+ ); } diff --git a/packages/lexical-playground/src/plugins/ComponentPickerPlugin/index.tsx b/packages/lexical-playground/src/plugins/ComponentPickerPlugin/index.tsx index 5df0bf4fe08..6a95eac6a16 100644 --- a/packages/lexical-playground/src/plugins/ComponentPickerPlugin/index.tsx +++ b/packages/lexical-playground/src/plugins/ComponentPickerPlugin/index.tsx @@ -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 diff --git a/packages/lexical-playground/src/plugins/EquationsPlugin/index.ts b/packages/lexical-playground/src/plugins/EquationsPlugin/index.tsx similarity index 72% rename from packages/lexical-playground/src/plugins/EquationsPlugin/index.ts rename to packages/lexical-playground/src/plugins/EquationsPlugin/index.tsx index 13881b553fb..11c84c16061 100644 --- a/packages/lexical-playground/src/plugins/EquationsPlugin/index.ts +++ b/packages/lexical-playground/src/plugins/EquationsPlugin/index.tsx @@ -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; @@ -30,6 +33,24 @@ type CommandPayload = { export const INSERT_EQUATION_COMMAND: LexicalCommand = 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 ; +} + export default function EquationsPlugin(): JSX.Element | null { const [editor] = useLexicalComposerContext(); diff --git a/packages/lexical-playground/src/plugins/ImagesPlugin/index.ts b/packages/lexical-playground/src/plugins/ImagesPlugin/index.tsx similarity index 56% rename from packages/lexical-playground/src/plugins/ImagesPlugin/index.ts rename to packages/lexical-playground/src/plugins/ImagesPlugin/index.tsx index 9de793e7af3..f632b0da9ed 100644 --- a/packages/lexical-playground/src/plugins/ImagesPlugin/index.ts +++ b/packages/lexical-playground/src/plugins/ImagesPlugin/index.tsx @@ -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; export const INSERT_IMAGE_COMMAND: LexicalCommand = createCommand(); + +export function InsertImageUriDialogBody({ + onClick, +}: { + onClick: (payload: InsertImagePayload) => void; +}) { + const [src, setSrc] = useState(''); + const [altText, setAltText] = useState(''); + + const isDisabled = src === ''; + + return ( + <> + + + + + + + ); +} + +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 ( + <> + + + + + + + ); +} + +export function InsertImageDialog({ + activeEditor, + onClose, +}: { + activeEditor: LexicalEditor; + onClose: () => void; +}): JSX.Element { + const [mode, setMode] = useState(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 && ( + + + + + + )} + {mode === 'url' && } + {mode === 'file' && } + + ); +} + export default function ImagesPlugin({ captionsEnabled, }: { diff --git a/packages/lexical-playground/src/plugins/PollPlugin/index.ts b/packages/lexical-playground/src/plugins/PollPlugin/index.tsx similarity index 62% rename from packages/lexical-playground/src/plugins/PollPlugin/index.ts rename to packages/lexical-playground/src/plugins/PollPlugin/index.tsx index 7067f6b0580..c5a60c08f43 100644 --- a/packages/lexical-playground/src/plugins/PollPlugin/index.ts +++ b/packages/lexical-playground/src/plugins/PollPlugin/index.tsx @@ -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 = 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 ( + <> + + + + + + ); +} + export default function PollPlugin(): JSX.Element | null { const [editor] = useLexicalComposerContext(); useEffect(() => { diff --git a/packages/lexical-playground/src/plugins/TablePlugin.tsx b/packages/lexical-playground/src/plugins/TablePlugin.tsx index fc23657770f..185629f6b1b 100644 --- a/packages/lexical-playground/src/plugins/TablePlugin.tsx +++ b/packages/lexical-playground/src/plugins/TablePlugin.tsx @@ -7,6 +7,7 @@ */ import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; +import {INSERT_TABLE_COMMAND} from '@lexical/table'; import { $createNodeSelection, $createParagraphNode, @@ -27,6 +28,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; @@ -51,7 +55,7 @@ export type CellEditorConfig = Readonly<{ theme?: EditorThemeClasses; }>; -export const INSERT_TABLE_COMMAND: LexicalCommand = +export const INSERT_NEW_TABLE_COMMAND: LexicalCommand = createCommand(); // @ts-ignore: not sure why TS doesn't like using null as the value? @@ -88,6 +92,58 @@ export function TableContext({children}: {children: JSX.Element}) { ); } +export function InsertTableDialog({ + 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 ( + <> + + + + + + + ); +} + +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_NEW_TABLE_COMMAND, {columns, rows}); + onClose(); + }; + + return ( + <> + + + + + + + ); +} + export function TablePlugin({ cellEditorConfig, children, diff --git a/packages/lexical-playground/src/plugins/ToolbarPlugin/index.css b/packages/lexical-playground/src/plugins/ToolbarPlugin/index.css deleted file mode 100644 index 1d3eb153aab..00000000000 --- a/packages/lexical-playground/src/plugins/ToolbarPlugin/index.css +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * - */ -.ToolbarPlugin__dialogActions { - display: flex; - flex-direction: row; - justify-content: right; - margin-top: 20px; -} - -.ToolbarPlugin__dialogButtonsList { - display: flex; - flex-direction: column; - justify-content: right; - margin-top: 20px; -} - -.ToolbarPlugin__dialogButtonsList button { - margin-bottom: 20px; -} diff --git a/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx b/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx index 61035ba94bd..f19120f0b62 100644 --- a/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx +++ b/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx @@ -6,11 +6,8 @@ * */ -import type {InsertImagePayload} from '../ImagesPlugin'; import type {LexicalEditor, NodeKey} from 'lexical'; -import './index.css'; - import { $createCodeNode, $isCodeNode, @@ -44,7 +41,6 @@ import { $selectAll, $wrapNodes, } from '@lexical/selection'; -import {INSERT_TABLE_COMMAND} from '@lexical/table'; import { $findMatchingParent, $getNearestBlockElementAncestorOrThrow, @@ -70,30 +66,28 @@ import { SELECTION_CHANGE_COMMAND, UNDO_COMMAND, } from 'lexical'; +import {useCallback, useEffect, useState} from 'react'; import * as React from 'react'; -import {useCallback, useEffect, useRef, useState} from 'react'; import {IS_APPLE} from 'shared/environment'; import useModal from '../../hooks/useModal'; import catTypingGif from '../../images/cat-typing.gif'; -import landscapeImage from '../../images/landscape.jpg'; -import yellowFlowerImage from '../../images/yellow-flower.jpg'; import {$createStickyNode} from '../../nodes/StickyNode'; -import Button from '../../ui/Button'; import ColorPicker from '../../ui/ColorPicker'; import DropDown, {DropDownItem} from '../../ui/DropDown'; -import FileInput from '../../ui/FileInput'; -import KatexEquationAlterer from '../../ui/KatexEquationAlterer'; -import TextInput from '../../ui/TextInput'; import {getSelectedNode} from '../../utils/getSelectedNode'; import {sanitizeUrl} from '../../utils/sanitizeUrl'; import {EmbedConfigs} from '../AutoEmbedPlugin'; import {INSERT_COLLAPSIBLE_COMMAND} from '../CollapsiblePlugin'; -import {INSERT_EQUATION_COMMAND} from '../EquationsPlugin'; +import {InsertEquationDialog} from '../EquationsPlugin'; import {INSERT_EXCALIDRAW_COMMAND} from '../ExcalidrawPlugin'; -import {INSERT_IMAGE_COMMAND} from '../ImagesPlugin'; -import {INSERT_POLL_COMMAND} from '../PollPlugin'; -import {INSERT_TABLE_COMMAND as INSERT_NEW_TABLE_COMMAND} from '../TablePlugin'; +import { + INSERT_IMAGE_COMMAND, + InsertImageDialog, + InsertImagePayload, +} from '../ImagesPlugin'; +import {InsertPollDialog} from '../PollPlugin'; +import {InsertNewTableDialog, InsertTableDialog} from '../TablePlugin'; const blockTypeToBlockName = { bullet: 'Bulleted List', @@ -147,260 +141,6 @@ const FONT_SIZE_OPTIONS: [string, string][] = [ ['20px', '20px'], ]; -export function InsertImageUriDialogBody({ - onClick, -}: { - onClick: (payload: InsertImagePayload) => void; -}) { - const [src, setSrc] = useState(''); - const [altText, setAltText] = useState(''); - - const isDisabled = src === ''; - - return ( - <> - - -
- -
- - ); -} - -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 ( - <> - - -
- -
- - ); -} - -export function InsertImageDialog({ - activeEditor, - onClose, -}: { - activeEditor: LexicalEditor; - onClose: () => void; -}): JSX.Element { - const [mode, setMode] = useState(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 && ( -
- - - -
- )} - {mode === 'url' && } - {mode === 'file' && } - - ); -} - -export function InsertTableDialog({ - 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 ( - <> - - -
- -
- - ); -} - -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_NEW_TABLE_COMMAND, {columns, rows}); - onClose(); - }; - - return ( - <> - - -
- -
- - ); -} - -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 ( - <> - -
- -
- - ); -} - -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 ; -} - function dropDownActiveClass(active: boolean) { if (active) return 'active dropdown-item-active'; else return ''; diff --git a/packages/lexical-playground/src/ui/Dialog.css b/packages/lexical-playground/src/ui/Dialog.css new file mode 100644 index 00000000000..9474211e5aa --- /dev/null +++ b/packages/lexical-playground/src/ui/Dialog.css @@ -0,0 +1,17 @@ +.DialogActions { + display: flex; + flex-direction: row; + justify-content: right; + margin-top: 20px; +} + +.DialogButtonsList { + display: flex; + flex-direction: column; + justify-content: right; + margin-top: 20px; +} + +.DialogButtonsList button { + margin-bottom: 20px; +} diff --git a/packages/lexical-playground/src/ui/Dialog.tsx b/packages/lexical-playground/src/ui/Dialog.tsx new file mode 100644 index 00000000000..36e3b8c5938 --- /dev/null +++ b/packages/lexical-playground/src/ui/Dialog.tsx @@ -0,0 +1,32 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import './Dialog.css'; + +import * as React from 'react'; +import {ReactNode} from 'react'; + +type Props = Readonly<{ + 'data-test-id'?: string; + children: ReactNode; +}>; + +export function DialogButtonsList({children}: Props): JSX.Element { + return
{children}
; +} + +export function DialogActions({ + 'data-test-id': dataTestId, + children, +}: Props): JSX.Element { + return ( +
+ {children} +
+ ); +}