From 9800570fb1773781762cfd2cac6d9f42730d4907 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Sat, 13 Apr 2024 12:39:38 +0530 Subject: [PATCH] feat: Added "Upload file" option to the super note Insert menu and block picker menu (#2869) --- .../SuperEditor/Lexical/Hooks/useModal.tsx | 2 +- .../SuperEditor/Lexical/UI/Button.css | 36 -------- .../SuperEditor/Lexical/UI/Button.tsx | 44 ---------- .../SuperEditor/Lexical/UI/Dialog.css | 17 ---- .../SuperEditor/Lexical/UI/Dialog.tsx | 28 ------ .../SuperEditor/Lexical/UI/Input.css | 33 ------- .../SuperEditor/Lexical/UI/Modal.css | 64 -------------- .../SuperEditor/Lexical/UI/Modal.tsx | 42 +++++---- .../SuperEditor/Lexical/UI/TextInput.tsx | 41 --------- .../Plugins/AutoEmbedPlugin/index.tsx | 42 ++++----- .../BlockPickerPlugin/BlockPickerPlugin.tsx | 2 + .../SuperEditor/Plugins/Blocks/File.tsx | 12 +++ .../{FilePlugin.ts => FilePlugin.tsx} | 85 ++++++++++++++++++- .../RemoteImagePlugin/RemoteImagePlugin.tsx | 27 ++++-- .../SuperEditor/Plugins/TablePlugin.tsx | 32 +++++-- .../Plugins/ToolbarPlugin/ToolbarPlugin.tsx | 6 ++ 16 files changed, 196 insertions(+), 317 deletions(-) delete mode 100644 packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Button.css delete mode 100644 packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Button.tsx delete mode 100644 packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Dialog.css delete mode 100644 packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Dialog.tsx delete mode 100644 packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Input.css delete mode 100644 packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Modal.css delete mode 100644 packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/TextInput.tsx create mode 100644 packages/web/src/javascripts/Components/SuperEditor/Plugins/Blocks/File.tsx rename packages/web/src/javascripts/Components/SuperEditor/Plugins/EncryptedFilePlugin/{FilePlugin.ts => FilePlugin.tsx} (60%) diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/Hooks/useModal.tsx b/packages/web/src/javascripts/Components/SuperEditor/Lexical/Hooks/useModal.tsx index 310c0e830..cd26cd2a7 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/Hooks/useModal.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Lexical/Hooks/useModal.tsx @@ -41,7 +41,7 @@ export default function useModal(): [ title: string, // eslint-disable-next-line no-shadow getContent: (onClose: () => void) => JSX.Element, - closeOnClickOutside = false, + closeOnClickOutside = true, ) => { setModalContent({ closeOnClickOutside, diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Button.css b/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Button.css deleted file mode 100644 index f98f623b9..000000000 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Button.css +++ /dev/null @@ -1,36 +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. - * - * - */ - -.Button__root { - padding-top: 10px; - padding-bottom: 10px; - padding-left: 15px; - padding-right: 15px; - border: 0px; - background-color: var(--sn-stylekit-contrast-background-color); - cursor: pointer; - font-size: 14px; -} -.Button__root:hover { - background-color: var(--sn-stylekit-info-color); - color: var(--sn-stylekit-info-contrast-color); -} -.Button__small { - padding-top: 5px; - padding-bottom: 5px; - padding-left: 10px; - padding-right: 10px; - font-size: 13px; -} -.Button__disabled { - cursor: not-allowed; -} -.Button__disabled:hover { - background-color: var(--sn-stylekit-secondary-background-color); -} diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Button.tsx b/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Button.tsx deleted file mode 100644 index e26a71eb8..000000000 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Button.tsx +++ /dev/null @@ -1,44 +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. - * - */ - -import './Button.css' - -import { ReactNode } from 'react' - -import joinClasses from '../Utils/join-classes' - -export default function Button({ - 'data-test-id': dataTestId, - children, - className, - onClick, - disabled, - small, - title, -}: { - 'data-test-id'?: string - children: ReactNode - className?: string - disabled?: boolean - onClick: () => void - small?: boolean - title?: string -}): JSX.Element { - return ( - - ) -} diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Dialog.css b/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Dialog.css deleted file mode 100644 index 9474211e5..000000000 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Dialog.css +++ /dev/null @@ -1,17 +0,0 @@ -.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/web/src/javascripts/Components/SuperEditor/Lexical/UI/Dialog.tsx b/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Dialog.tsx deleted file mode 100644 index 3eb3172be..000000000 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Dialog.tsx +++ /dev/null @@ -1,28 +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. - * - */ - -import './Dialog.css' - -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} -
- ) -} diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Input.css b/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Input.css deleted file mode 100644 index 9f85532e1..000000000 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Input.css +++ /dev/null @@ -1,33 +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. - * - * - */ - -.Input__wrapper { - display: flex; - flex-direction: row; - align-items: center; - margin-bottom: 10px; -} -.Input__label { - display: flex; - flex: 1; - color: #666; - margin-right: 20px; -} -.Input__input { - display: flex; - flex: 2; - border: 1px solid var(--sn-stylekit-contrast-border-color); - background-color: var(--sn-stylekit-contrast-background-color); - padding-top: 7px; - padding-bottom: 7px; - padding-left: 10px; - padding-right: 10px; - font-size: 16px; - min-width: 0; -} diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Modal.css b/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Modal.css deleted file mode 100644 index 37dabeff6..000000000 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Modal.css +++ /dev/null @@ -1,64 +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. - * - * - */ - -.Modal__overlay { - display: flex; - justify-content: center; - align-items: center; - position: absolute; - flex-direction: column; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - background-color: rgba(0, 0, 0, 0.7); - flex-grow: 0px; - flex-shrink: 1px; - z-index: 100; -} -.Modal__modal { - padding: 20px; - min-height: 100px; - min-width: 300px; - display: flex; - flex-grow: 0px; - background-color: var(--sn-stylekit-background-color); - flex-direction: column; - position: relative; - box-shadow: 0 0px 0 var(--sn-stylekit-shadow-color); - border-radius: 0px; -} -.Modal__title { - color: var(--sn-stylekit-foreground-color); - margin: 0px; - padding-bottom: 15px; - border-bottom: 1px solid var(--sn-stylekit-border-color); -} -.Modal__closeButton { - border: 0px; - position: absolute; - right: 20px; - top: 15px; - border-radius: 20px; - justify-content: center; - align-items: center; - display: flex; - width: 30px; - height: 30px; - text-align: center; - cursor: pointer; - background-color: var(--sn-stylekit-contrast-background-color); -} -.Modal__closeButton:hover { - background-color: var(--sn-stylekit-info-color); - color: var(--sn-stylekit-info-contrast-color); -} -.Modal__content { - padding-top: 20px; -} diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Modal.tsx b/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Modal.tsx index 5b6951765..70eb57f35 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Modal.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/Modal.tsx @@ -7,10 +7,11 @@ */ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' -import './Modal.css' import { ReactNode, useEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' +import Icon from '@/Components/Icon/Icon' +import { KeyboardKey } from '@standardnotes/ui-services' function PortalImpl({ onClose, @@ -33,42 +34,51 @@ function PortalImpl({ useEffect(() => { let modalOverlayElement: HTMLElement | null = null - const handler = (event: KeyboardEvent) => { - if (event.keyCode === 27) { + + const keydownHandler = (event: KeyboardEvent) => { + if (event.key === KeyboardKey.Escape) { onClose() } } + const clickOutsideHandler = (event: MouseEvent) => { const target = event.target if (modalRef.current !== null && !modalRef.current.contains(target as Node) && closeOnClickOutside) { onClose() } } + if (modalRef.current !== null) { - modalOverlayElement = modalRef.current?.parentElement + modalOverlayElement = modalRef.current.parentElement if (modalOverlayElement !== null) { - modalOverlayElement?.addEventListener('click', clickOutsideHandler) + modalOverlayElement.addEventListener('click', clickOutsideHandler) } } - window.addEventListener('keydown', handler) + window.addEventListener('keydown', keydownHandler) return () => { - window.removeEventListener('keydown', handler) + window.removeEventListener('keydown', keydownHandler) if (modalOverlayElement !== null) { - modalOverlayElement?.removeEventListener('click', clickOutsideHandler) + modalOverlayElement.removeEventListener('click', clickOutsideHandler) } } }, [closeOnClickOutside, onClose]) return ( -
-
-

{title}

- -
{children}
+
+
+
+
{title}
+ +
+
{children}
) @@ -78,7 +88,7 @@ export default function Modal({ onClose, children, title, - closeOnClickOutside = false, + closeOnClickOutside = true, }: { children: ReactNode closeOnClickOutside?: boolean diff --git a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/TextInput.tsx b/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/TextInput.tsx deleted file mode 100644 index e66fe0f83..000000000 --- a/packages/web/src/javascripts/Components/SuperEditor/Lexical/UI/TextInput.tsx +++ /dev/null @@ -1,41 +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. - * - */ - -import './Input.css' - -type Props = Readonly<{ - 'data-test-id'?: string - label: string - onChange: (val: string) => void - placeholder?: string - value: string -}> - -export default function TextInput({ - label, - value, - onChange, - placeholder = '', - 'data-test-id': dataTestId, -}: Props): JSX.Element { - return ( -
- - { - onChange(e.target.value) - }} - data-test-id={dataTestId} - /> -
- ) -} diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/AutoEmbedPlugin/index.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/AutoEmbedPlugin/index.tsx index 7a5ff4052..1908fac71 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/AutoEmbedPlugin/index.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/AutoEmbedPlugin/index.tsx @@ -16,15 +16,16 @@ import { URL_MATCHER, } from '@lexical/react/LexicalAutoEmbedPlugin' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' -import { useMemo, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import * as ReactDOM from 'react-dom' import useModal from '../../Lexical/Hooks/useModal' -import Button from '../../Lexical/UI/Button' -import { DialogActions } from '../../Lexical/UI/Dialog' import { INSERT_TWEET_COMMAND } from '../TwitterPlugin' import { INSERT_YOUTUBE_COMMAND } from '../YouTubePlugin' import { classNames } from '@standardnotes/snjs' +import DecoratedInput from '@/Components/Input/DecoratedInput' +import Button from '@/Components/Button/Button' +import { isMobileScreen } from '../../../../Utils' interface PlaygroundEmbedConfig extends EmbedConfig { // Human readable name of the embeded content e.g. Tweet or Google Map. @@ -219,28 +220,31 @@ export function AutoEmbedDialog({ } } + const focusOnMount = useCallback((element: HTMLInputElement | null) => { + if (element) { + setTimeout(() => element.focus()) + } + }, []) + return ( -
-
- +
- - - -
+
+ ) } diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/BlockPickerPlugin/BlockPickerPlugin.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/BlockPickerPlugin/BlockPickerPlugin.tsx index 0254f5f12..77eb2b155 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/BlockPickerPlugin/BlockPickerPlugin.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/BlockPickerPlugin/BlockPickerPlugin.tsx @@ -29,6 +29,7 @@ import { GetQuoteBlockOption } from '../Blocks/Quote' import { GetDividerBlockOption } from '../Blocks/Divider' import { GetCollapsibleBlockOption } from '../Blocks/Collapsible' import { GetEmbedsBlockOptions } from '../Blocks/Embeds' +import { GetUploadFileOption } from '../Blocks/File' export default function BlockPickerMenuPlugin({ popoverZIndex }: { popoverZIndex?: string }): JSX.Element { const [editor] = useLexicalComposerContext() @@ -57,6 +58,7 @@ export default function BlockPickerMenuPlugin({ popoverZIndex }: { popoverZIndex GetRemoteImageBlockOption(() => { showModal('Insert image from URL', (onClose) => ) }), + GetUploadFileOption(editor), GetNumberedListBlockOption(editor), GetBulletedListBlockOption(editor), GetChecklistBlockOption(editor), diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/Blocks/File.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/Blocks/File.tsx new file mode 100644 index 000000000..86cb61389 --- /dev/null +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/Blocks/File.tsx @@ -0,0 +1,12 @@ +import { LexicalIconName } from '@/Components/Icon/LexicalIcons' +import { BlockPickerOption } from '../BlockPickerPlugin/BlockPickerOption' +import { LexicalEditor } from 'lexical' +import { OPEN_FILE_UPLOAD_MODAL_COMMAND } from '../EncryptedFilePlugin/FilePlugin' + +export function GetUploadFileOption(editor: LexicalEditor) { + return new BlockPickerOption('Upload file', { + iconName: 'file' as LexicalIconName, + keywords: ['image', 'upload', 'file'], + onSelect: () => editor.dispatchCommand(OPEN_FILE_UPLOAD_MODAL_COMMAND, undefined), + }) +} diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.ts b/packages/web/src/javascripts/Components/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.tsx similarity index 60% rename from packages/web/src/javascripts/Components/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.ts rename to packages/web/src/javascripts/Components/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.tsx index a299fd3c6..9648eff9a 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.ts +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/EncryptedFilePlugin/FilePlugin.tsx @@ -1,7 +1,7 @@ import { INSERT_FILE_COMMAND } from '../Commands' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' -import { useEffect } from 'react' +import { useEffect, useState } from 'react' import { FileNode } from './Nodes/FileNode' import { $createParagraphNode, @@ -10,6 +10,7 @@ import { COMMAND_PRIORITY_NORMAL, PASTE_COMMAND, $isRootOrShadowRoot, + createCommand, } from 'lexical' import { $createFileNode } from './Nodes/FileUtils' import { mergeRegister, $wrapNodeInElement } from '@lexical/utils' @@ -18,6 +19,70 @@ import { FilesControllerEvent } from '@/Controllers/FilesController' import { useLinkingController } from '@/Controllers/LinkingControllerProvider' import { useApplication } from '@/Components/ApplicationProvider' import { SNNote } from '@standardnotes/snjs' +import Spinner from '../../../Spinner/Spinner' +import Modal from '../../Lexical/UI/Modal' +import Button from '@/Components/Button/Button' +import { isMobileScreen } from '../../../../Utils' + +export const OPEN_FILE_UPLOAD_MODAL_COMMAND = createCommand('OPEN_FILE_UPLOAD_MODAL_COMMAND') + +function UploadFileDialog({ currentNote, onClose }: { currentNote: SNNote; onClose: () => void }) { + const application = useApplication() + const [editor] = useLexicalComposerContext() + const filesController = useFilesController() + const linkingController = useLinkingController() + + const [file, setFile] = useState() + const [isUploadingFile, setIsUploadingFile] = useState(false) + + const onClick = () => { + if (!file) { + return + } + + setIsUploadingFile(true) + filesController + .uploadNewFile(file) + .then((uploadedFile) => { + if (!uploadedFile) { + return + } + editor.dispatchCommand(INSERT_FILE_COMMAND, uploadedFile.uuid) + void linkingController.linkItemToSelectedItem(uploadedFile) + void application.changeAndSaveItem.execute(uploadedFile, (mutator) => { + mutator.protected = currentNote.protected + }) + }) + .catch(console.error) + .finally(() => { + setIsUploadingFile(false) + onClose() + }) + } + + return ( + <> + { + const filesList = event.target.files + if (filesList && filesList.length === 1) { + setFile(filesList[0]) + } + }} + /> +
+ {isUploadingFile ? ( + + ) : ( + + )} +
+ + ) +} export default function FilePlugin({ currentNote }: { currentNote: SNNote }): JSX.Element | null { const application = useApplication() @@ -25,6 +90,8 @@ export default function FilePlugin({ currentNote }: { currentNote: SNNote }): JS const filesController = useFilesController() const linkingController = useLinkingController() + const [showFileUploadModal, setShowFileUploadModal] = useState(false) + useEffect(() => { if (!editor.hasNodes([FileNode])) { throw new Error('FilePlugin: FileNode not registered on editor') @@ -63,6 +130,14 @@ export default function FilePlugin({ currentNote }: { currentNote: SNNote }): JS }, COMMAND_PRIORITY_EDITOR, ), + editor.registerCommand( + OPEN_FILE_UPLOAD_MODAL_COMMAND, + () => { + setShowFileUploadModal(true) + return true + }, + COMMAND_PRIORITY_NORMAL, + ), editor.registerCommand( PASTE_COMMAND, (payload) => { @@ -103,5 +178,13 @@ export default function FilePlugin({ currentNote }: { currentNote: SNNote }): JS return disposer }, [filesController, editor]) + if (showFileUploadModal) { + return ( + setShowFileUploadModal(false)} title="Upload File"> + setShowFileUploadModal(false)} /> + + ) + } + return null } diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/RemoteImagePlugin/RemoteImagePlugin.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/RemoteImagePlugin/RemoteImagePlugin.tsx index a399bc2a0..ee9266b1a 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/RemoteImagePlugin/RemoteImagePlugin.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/RemoteImagePlugin/RemoteImagePlugin.tsx @@ -1,12 +1,12 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' import { $createParagraphNode, $insertNodes, $isRootOrShadowRoot, COMMAND_PRIORITY_NORMAL } from 'lexical' -import { useEffect, useState } from 'react' -import Button from '../../Lexical/UI/Button' -import { DialogActions } from '../../Lexical/UI/Dialog' -import TextInput from '../../Lexical/UI/TextInput' +import { useCallback, useEffect, useState } from 'react' import { INSERT_REMOTE_IMAGE_COMMAND } from '../Commands' import { $createRemoteImageNode, RemoteImageNode } from './RemoteImageNode' import { mergeRegister, $wrapNodeInElement } from '@lexical/utils' +import DecoratedInput from '@/Components/Input/DecoratedInput' +import Button from '@/Components/Button/Button' +import { isMobileScreen } from '../../../../Utils' export function InsertRemoteImageDialog({ onClose }: { onClose: () => void }) { const [url, setURL] = useState('') @@ -21,12 +21,23 @@ export function InsertRemoteImageDialog({ onClose }: { onClose: () => void }) { onClose() } + const focusOnMount = useCallback((element: HTMLInputElement | null) => { + if (element) { + setTimeout(() => element.focus()) + } + }, []) + return ( <> - - - - + +
+ +
) } diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/TablePlugin.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/TablePlugin.tsx index b14184f1a..e502af642 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/TablePlugin.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/TablePlugin.tsx @@ -8,12 +8,12 @@ import { INSERT_TABLE_COMMAND, TableNode, TableRowNode } from '@lexical/table' import { $createParagraphNode, LexicalEditor } from 'lexical' -import { useEffect, useState } from 'react' -import Button from '../Lexical/UI/Button' -import { DialogActions } from '../Lexical/UI/Dialog' -import TextInput from '../Lexical/UI/TextInput' +import { useCallback, useEffect, useState } from 'react' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' import { mergeRegister } from '@lexical/utils' +import DecoratedInput from '@/Components/Input/DecoratedInput' +import Button from '@/Components/Button/Button' +import { isMobileScreen } from '../../../Utils' export function InsertTableDialog({ activeEditor, @@ -30,13 +30,27 @@ export function InsertTableDialog({ onClose() } + const focusOnMount = useCallback((element: HTMLInputElement | null) => { + if (element) { + setTimeout(() => element.focus()) + } + }, []) + return ( <> - - - - - + + +
+ +
) } diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx index f1e29d18d..cb3092d8e 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/ToolbarPlugin/ToolbarPlugin.tsx @@ -76,6 +76,7 @@ import usePreference from '@/Hooks/usePreference' import { ElementIds } from '@/Constants/ElementIDs' import { $isDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode' import LinkViewer from './LinkViewer' +import { OPEN_FILE_UPLOAD_MODAL_COMMAND } from '../EncryptedFilePlugin/FilePlugin' const TOGGLE_LINK_AND_EDIT_COMMAND = createCommand('TOGGLE_LINK_AND_EDIT_COMMAND') @@ -1048,6 +1049,11 @@ const ToolbarPlugin = () => { showModal('Insert Table', (onClose) => ) } /> + activeEditor.dispatchCommand(OPEN_FILE_UPLOAD_MODAL_COMMAND, undefined)} + />