From f361cd045ea4df2e770895fee0e1f036dc2c1f66 Mon Sep 17 00:00:00 2001 From: "M. Palanikannan" <73993394+Palanikannan1437@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:37:40 +0530 Subject: [PATCH] image can't be inserted inside table (#2904) * image can't be inserted inside table Now we've diabled image icon from showing up if the cursor is inside a table node or if a table cell is selected * added drag drop support for document editor * fixed missing dependencies --- packages/editor/core/package.json | 17 +- packages/editor/document-editor/package.json | 20 +- .../src/ui/extensions/index.tsx | 73 ++-- .../src/ui/extensions/slash-command.tsx | 343 ------------------ .../src/ui/menu/fixed-menu.tsx | 36 +- packages/editor/extensions/Readme.md | 97 +++++ packages/editor/extensions/package.json | 60 +++ packages/editor/extensions/postcss.config.js | 9 + .../src}/extensions/drag-drop.tsx | 29 +- .../src/extensions/slash-commands.tsx} | 6 +- packages/editor/extensions/src/index.ts | 2 + packages/editor/extensions/tailwind.config.js | 6 + packages/editor/extensions/tsconfig.json | 5 + packages/editor/extensions/tsup.config.ts | 11 + packages/editor/lite-text-editor/package.json | 16 +- .../src/ui/menus/fixed-menu/index.tsx | 47 ++- packages/editor/rich-text-editor/package.json | 7 +- .../src/lib/utils/DragHandleElement.ts | 23 -- .../src/ui/extensions/index.tsx | 5 +- packages/editor/types/Readme.md | 97 +++++ packages/editor/types/package.json | 50 +++ packages/editor/types/postcss.config.js | 9 + packages/editor/types/src/index.ts | 6 + .../editor/types/src/types/delete-image.ts | 1 + .../types/src/types/mention-suggestion.ts | 10 + .../editor/types/src/types/upload-image.ts | 1 + packages/editor/types/tailwind.config.js | 6 + packages/editor/types/tsconfig.json | 5 + packages/editor/types/tsup.config.ts | 11 + turbo.json | 29 +- web/package.json | 6 +- yarn.lock | 65 +--- 32 files changed, 555 insertions(+), 553 deletions(-) delete mode 100644 packages/editor/document-editor/src/ui/extensions/slash-command.tsx create mode 100644 packages/editor/extensions/Readme.md create mode 100644 packages/editor/extensions/package.json create mode 100644 packages/editor/extensions/postcss.config.js rename packages/editor/{rich-text-editor/src/ui => extensions/src}/extensions/drag-drop.tsx (87%) rename packages/editor/{rich-text-editor/src/ui/extensions/slash-command.tsx => extensions/src/extensions/slash-commands.tsx} (98%) create mode 100644 packages/editor/extensions/src/index.ts create mode 100644 packages/editor/extensions/tailwind.config.js create mode 100644 packages/editor/extensions/tsconfig.json create mode 100644 packages/editor/extensions/tsup.config.ts delete mode 100644 packages/editor/rich-text-editor/src/lib/utils/DragHandleElement.ts create mode 100644 packages/editor/types/Readme.md create mode 100644 packages/editor/types/package.json create mode 100644 packages/editor/types/postcss.config.js create mode 100644 packages/editor/types/src/index.ts create mode 100644 packages/editor/types/src/types/delete-image.ts create mode 100644 packages/editor/types/src/types/mention-suggestion.ts create mode 100644 packages/editor/types/src/types/upload-image.ts create mode 100644 packages/editor/types/tailwind.config.js create mode 100644 packages/editor/types/tsconfig.json create mode 100644 packages/editor/types/tsup.config.ts diff --git a/packages/editor/core/package.json b/packages/editor/core/package.json index 04100a729..2efb5805e 100644 --- a/packages/editor/core/package.json +++ b/packages/editor/core/package.json @@ -23,12 +23,10 @@ }, "peerDependencies": { "next": "12.3.2", - "next-themes": "^0.2.1", "react": "^18.2.0", "react-dom": "18.2.0" }, "dependencies": { - "@blueprintjs/popover2": "^2.0.10", "@tiptap/core": "^2.1.7", "@tiptap/extension-code-block-lowlight": "^2.1.12", "@tiptap/extension-color": "^2.1.11", @@ -49,29 +47,24 @@ "@tiptap/react": "^2.1.7", "@tiptap/starter-kit": "^2.1.10", "@tiptap/suggestion": "^2.0.4", - "@types/node": "18.15.3", - "@types/react": "^18.2.5", - "@types/react-dom": "18.0.11", "class-variance-authority": "^0.7.0", "clsx": "^1.2.1", - "eslint": "8.36.0", - "eslint-config-next": "13.2.4", - "eventsource-parser": "^0.1.0", "highlight.js": "^11.8.0", "jsx-dom-cjs": "^8.0.3", "lowlight": "^3.0.0", "lucide-react": "^0.244.0", - "prosemirror-async-query": "^0.0.4", - "react-markdown": "^8.0.7", "react-moveable": "^0.54.2", "tailwind-merge": "^1.14.0", "tippy.js": "^6.3.7", - "tiptap-markdown": "^0.8.2", - "use-debounce": "^9.0.4" + "tiptap-markdown": "^0.8.2" }, "devDependencies": { "eslint": "^7.32.0", "postcss": "^8.4.29", + "eslint-config-next": "13.2.4", + "@types/node": "18.15.3", + "@types/react": "^18.2.5", + "@types/react-dom": "18.0.11", "tailwind-config-custom": "*", "tsconfig": "*", "tsup": "^7.2.0", diff --git a/packages/editor/document-editor/package.json b/packages/editor/document-editor/package.json index 0016d5261..4c7ffb35e 100644 --- a/packages/editor/document-editor/package.json +++ b/packages/editor/document-editor/package.json @@ -27,33 +27,17 @@ "react-dom": "18.2.0" }, "dependencies": { - "@headlessui/react": "^1.7.17", "@plane/ui": "*", "@plane/editor-core": "*", - "@popperjs/core": "^2.11.8", + "@plane/editor-extensions": "*", "@tiptap/core": "^2.1.7", - "@tiptap/extension-code-block-lowlight": "^2.1.11", - "@tiptap/extension-horizontal-rule": "^2.1.11", - "@tiptap/extension-list-item": "^2.1.11", "@tiptap/extension-placeholder": "^2.1.11", - "@tiptap/suggestion": "^2.1.7", "@types/node": "18.15.3", "@types/react": "^18.2.5", "@types/react-dom": "18.0.11", - "class-variance-authority": "^0.7.0", - "clsx": "^1.2.1", "eslint": "8.36.0", "eslint-config-next": "13.2.4", - "eventsource-parser": "^0.1.0", - "highlight.js": "^11.8.0", - "lowlight": "^3.0.0", - "lucide-react": "^0.244.0", - "react-markdown": "^8.0.7", - "react-popper": "^2.3.0", - "tailwind-merge": "^1.14.0", - "tippy.js": "^6.3.7", - "tiptap-markdown": "^0.8.2", - "use-debounce": "^9.0.4" + "react-popper": "^2.3.0" }, "devDependencies": { "eslint": "^7.32.0", diff --git a/packages/editor/document-editor/src/ui/extensions/index.tsx b/packages/editor/document-editor/src/ui/extensions/index.tsx index cf5dc9e71..6ae2f0795 100644 --- a/packages/editor/document-editor/src/ui/extensions/index.tsx +++ b/packages/editor/document-editor/src/ui/extensions/index.tsx @@ -1,59 +1,28 @@ -import HorizontalRule from "@tiptap/extension-horizontal-rule"; import Placeholder from "@tiptap/extension-placeholder"; -import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight"; -import { common, createLowlight } from 'lowlight' -import { InputRule } from "@tiptap/core"; +import { SlashCommand } from "@plane/editor-extensions"; -import ts from "highlight.js/lib/languages/typescript"; - -import SlashCommand from "./slash-command"; -import { UploadImage } from "../"; - -const lowlight = createLowlight(common) -lowlight.register("ts", ts); +import { UploadImage } from "@plane/editor-types"; +import { DragAndDrop } from "@plane/editor-extensions"; export const DocumentEditorExtensions = ( uploadFile: UploadImage, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void + setIsSubmitting?: ( + isSubmitting: "submitting" | "submitted" | "saved", + ) => void, ) => [ - HorizontalRule.extend({ - addInputRules() { - return [ - new InputRule({ - find: /^(?:---|—-|___\s|\*\*\*\s)$/, - handler: ({ state, range, commands }) => { - commands.splitBlock(); + SlashCommand(uploadFile, setIsSubmitting), + DragAndDrop, + Placeholder.configure({ + placeholder: ({ node }) => { + if (node.type.name === "heading") { + return `Heading ${node.attrs.level}`; + } + if (node.type.name === "image" || node.type.name === "table") { + return ""; + } - const attributes = {}; - const { tr } = state; - const start = range.from; - const end = range.to; - // @ts-ignore - tr.replaceWith(start - 1, end, this.type.create(attributes)); - }, - }), - ]; - }, - }).configure({ - HTMLAttributes: { - class: "mb-6 border-t border-custom-border-300", - }, - }), - SlashCommand(uploadFile, setIsSubmitting), - CodeBlockLowlight.configure({ - lowlight, - }), - Placeholder.configure({ - placeholder: ({ node }) => { - if (node.type.name === "heading") { - return `Heading ${node.attrs.level}`; - } - if (node.type.name === "image" || node.type.name === "table") { - return ""; - } - - return "Press '/' for commands..."; - }, - includeChildren: true, - }), - ]; + return "Press '/' for commands..."; + }, + includeChildren: true, + }), +]; diff --git a/packages/editor/document-editor/src/ui/extensions/slash-command.tsx b/packages/editor/document-editor/src/ui/extensions/slash-command.tsx deleted file mode 100644 index e00585dd8..000000000 --- a/packages/editor/document-editor/src/ui/extensions/slash-command.tsx +++ /dev/null @@ -1,343 +0,0 @@ -import { useState, useEffect, useCallback, ReactNode, useRef, useLayoutEffect } from "react"; -import { Editor, Range, Extension } from "@tiptap/core"; -import Suggestion from "@tiptap/suggestion"; -import { ReactRenderer } from "@tiptap/react"; -import tippy from "tippy.js"; -import { - Heading1, - Heading2, - Heading3, - List, - ListOrdered, - Text, - TextQuote, - Code, - MinusSquare, - CheckSquare, - ImageIcon, - Table, -} from "lucide-react"; -import { UploadImage } from "../"; -import { cn, insertTableCommand, toggleBlockquote, toggleBulletList, toggleOrderedList, toggleTaskList, insertImageCommand, toggleHeadingOne, toggleHeadingTwo, toggleHeadingThree } from "@plane/editor-core"; - -interface CommandItemProps { - title: string; - description: string; - icon: ReactNode; -} - -interface CommandProps { - editor: Editor; - range: Range; -} - -const Command = Extension.create({ - name: "slash-command", - addOptions() { - return { - suggestion: { - char: "/", - command: ({ editor, range, props }: { editor: Editor; range: Range; props: any }) => { - props.command({ editor, range }); - }, - }, - }; - }, - addProseMirrorPlugins() { - return [ - Suggestion({ - editor: this.editor, - allow({ editor }) { - return !editor.isActive("table"); - }, - ...this.options.suggestion, - }), - ]; - }, -}); - -const getSuggestionItems = - ( - uploadFile: UploadImage, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void - ) => - ({ query }: { query: string }) => - [ - { - title: "Text", - description: "Just start typing with plain text.", - searchTerms: ["p", "paragraph"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run(); - }, - }, - { - title: "Heading 1", - description: "Big section heading.", - searchTerms: ["title", "big", "large"], - icon: , - command: ({ editor, range }: CommandProps) => { - toggleHeadingOne(editor, range); - }, - }, - { - title: "Heading 2", - description: "Medium section heading.", - searchTerms: ["subtitle", "medium"], - icon: , - command: ({ editor, range }: CommandProps) => { - toggleHeadingTwo(editor, range); - }, - }, - { - title: "Heading 3", - description: "Small section heading.", - searchTerms: ["subtitle", "small"], - icon: , - command: ({ editor, range }: CommandProps) => { - toggleHeadingThree(editor, range); - }, - }, - { - title: "To-do List", - description: "Track tasks with a to-do list.", - searchTerms: ["todo", "task", "list", "check", "checkbox"], - icon: , - command: ({ editor, range }: CommandProps) => { - toggleTaskList(editor, range) - }, - }, - { - title: "Bullet List", - description: "Create a simple bullet list.", - searchTerms: ["unordered", "point"], - icon: , - command: ({ editor, range }: CommandProps) => { - toggleBulletList(editor, range); - }, - }, - { - title: "Divider", - description: "Visually divide blocks", - searchTerms: ["line", "divider", "horizontal", "rule", "separate"], - icon: , - command: ({ editor, range }: CommandProps) => { - editor.chain().focus().deleteRange(range).setHorizontalRule().run(); - }, - }, - { - title: "Table", - description: "Create a Table", - searchTerms: ["table", "cell", "db", "data", "tabular"], - icon: , - command: ({ editor, range }: CommandProps) => { - insertTableCommand(editor, range); - }, - }, - { - title: "Numbered List", - description: "Create a list with numbering.", - searchTerms: ["ordered"], - icon: , - command: ({ editor, range }: CommandProps) => { - toggleOrderedList(editor, range) - }, - }, - { - title: "Quote", - description: "Capture a quote.", - searchTerms: ["blockquote"], - icon: , - command: ({ editor, range }: CommandProps) => - toggleBlockquote(editor, range) - }, - { - title: "Code", - description: "Capture a code snippet.", - searchTerms: ["codeblock"], - icon: , - command: ({ editor, range }: CommandProps) => - editor.chain().focus().deleteRange(range).toggleCodeBlock().run(), - }, - { - title: "Image", - description: "Upload an image from your computer.", - searchTerms: ["photo", "picture", "media"], - icon: , - command: ({ editor, range }: CommandProps) => { - insertImageCommand(editor, uploadFile, setIsSubmitting, range); - }, - }, - ].filter((item) => { - if (typeof query === "string" && query.length > 0) { - const search = query.toLowerCase(); - return ( - item.title.toLowerCase().includes(search) || - item.description.toLowerCase().includes(search) || - (item.searchTerms && item.searchTerms.some((term: string) => term.includes(search))) - ); - } - return true; - }); - -export const updateScrollView = (container: HTMLElement, item: HTMLElement) => { - const containerHeight = container.offsetHeight; - const itemHeight = item ? item.offsetHeight : 0; - - const top = item.offsetTop; - const bottom = top + itemHeight; - - if (top < container.scrollTop) { - container.scrollTop -= container.scrollTop - top + 5; - } else if (bottom > containerHeight + container.scrollTop) { - container.scrollTop += bottom - containerHeight - container.scrollTop + 5; - } -}; - -const CommandList = ({ - items, - command, -}: { - items: CommandItemProps[]; - command: any; - editor: any; - range: any; -}) => { - const [selectedIndex, setSelectedIndex] = useState(0); - - const selectItem = useCallback( - (index: number) => { - const item = items[index]; - if (item) { - command(item); - } - }, - [command, items] - ); - - useEffect(() => { - const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"]; - const onKeyDown = (e: KeyboardEvent) => { - if (navigationKeys.includes(e.key)) { - e.preventDefault(); - if (e.key === "ArrowUp") { - setSelectedIndex((selectedIndex + items.length - 1) % items.length); - return true; - } - if (e.key === "ArrowDown") { - setSelectedIndex((selectedIndex + 1) % items.length); - return true; - } - if (e.key === "Enter") { - selectItem(selectedIndex); - return true; - } - return false; - } - }; - document.addEventListener("keydown", onKeyDown); - return () => { - document.removeEventListener("keydown", onKeyDown); - }; - }, [items, selectedIndex, setSelectedIndex, selectItem]); - - useEffect(() => { - setSelectedIndex(0); - }, [items]); - - const commandListContainer = useRef(null); - - useLayoutEffect(() => { - const container = commandListContainer?.current; - - const item = container?.children[selectedIndex] as HTMLElement; - - if (item && container) updateScrollView(container, item); - }, [selectedIndex]); - - return items.length > 0 ? ( -
- {items.map((item: CommandItemProps, index: number) => ( - - ))} -
- ) : null; -}; - -const renderItems = () => { - let component: ReactRenderer | null = null; - let popup: any | null = null; - - return { - onStart: (props: { editor: Editor; clientRect: DOMRect }) => { - component = new ReactRenderer(CommandList, { - props, - // @ts-ignore - editor: props.editor, - }); - - // @ts-ignore - popup = tippy("body", { - getReferenceClientRect: props.clientRect, - appendTo: () => document.querySelector("#editor-container"), - content: component.element, - showOnCreate: true, - interactive: true, - trigger: "manual", - placement: "bottom-start", - }); - }, - onUpdate: (props: { editor: Editor; clientRect: DOMRect }) => { - component?.updateProps(props); - - popup && - popup[0].setProps({ - getReferenceClientRect: props.clientRect, - }); - }, - onKeyDown: (props: { event: KeyboardEvent }) => { - if (props.event.key === "Escape") { - popup?.[0].hide(); - - return true; - } - - // @ts-ignore - return component?.ref?.onKeyDown(props); - }, - onExit: () => { - popup?.[0].destroy(); - component?.destroy(); - }, - }; -}; - -export const SlashCommand = ( - uploadFile: UploadImage, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void -) => - Command.configure({ - suggestion: { - items: getSuggestionItems(uploadFile, setIsSubmitting), - render: renderItems, - }, - }); - -export default SlashCommand; diff --git a/packages/editor/document-editor/src/ui/menu/fixed-menu.tsx b/packages/editor/document-editor/src/ui/menu/fixed-menu.tsx index 8080f7c63..9bafc7983 100644 --- a/packages/editor/document-editor/src/ui/menu/fixed-menu.tsx +++ b/packages/editor/document-editor/src/ui/menu/fixed-menu.tsx @@ -4,6 +4,7 @@ import { BoldIcon } from "lucide-react"; import { BoldItem, BulletListItem, + isCellSelection, cn, CodeItem, ImageItem, @@ -16,6 +17,7 @@ import { HeadingOneItem, HeadingTwoItem, HeadingThreeItem, + findTableAncestor, } from "@plane/editor-core"; import { UploadImage } from ".."; @@ -57,10 +59,36 @@ export const FixedMenu = (props: EditorBubbleMenuProps) => { CodeItem(editor), ]; - const complexItems: BubbleMenuItem[] = [ - TableItem(editor), - ImageItem(editor, uploadFile, setIsSubmitting), - ]; + function getComplexItems(): BubbleMenuItem[] { + const items: BubbleMenuItem[] = [TableItem(editor)]; + + if (shouldShowImageItem()) { + items.push(ImageItem(editor, uploadFile, setIsSubmitting)); + } + + return items; + } + + const complexItems: BubbleMenuItem[] = getComplexItems(); + + function shouldShowImageItem(): boolean { + if (typeof window !== "undefined") { + const selectionRange: any = window?.getSelection(); + const { selection } = props.editor.state; + + if (selectionRange.rangeCount !== 0) { + const range = selectionRange.getRangeAt(0); + if (findTableAncestor(range.startContainer)) { + return false; + } + if (isCellSelection(selection)) { + return false; + } + } + return true; + } + return false; + } return (
diff --git a/packages/editor/extensions/Readme.md b/packages/editor/extensions/Readme.md new file mode 100644 index 000000000..39aca1226 --- /dev/null +++ b/packages/editor/extensions/Readme.md @@ -0,0 +1,97 @@ +# @plane/editor-extensions + +## Description + +The `@plane/lite-text-editor` package extends from the `editor-core` package, inheriting its base functionality while adding its own unique features of Custom control over Enter key, etc. + +## Key Features + +- **Exported Components**: There are two components exported from the Lite text editor (with and without Ref), you can choose to use the `withRef` instance whenever you want to control the Editor’s state via a side effect of some external action from within the application code. + + `LiteTextEditor` & `LiteTextEditorWithRef` + +- **Read Only Editor Instances**: We have added a really light weight _Read Only_ Editor instance for the Lite editor types (with and without Ref) + `LiteReadOnlyEditor` &`LiteReadOnlyEditorWithRef` + +## LiteTextEditor + +| Prop | Type | Description | +| ------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `uploadFile` | `(file: File) => Promise` | A function that handles file upload. It takes a file as input and handles the process of uploading that file. | +| `deleteFile` | `(assetUrlWithWorkspaceId: string) => Promise` | A function that handles deleting an image. It takes the asset url from your bucket and handles the process of deleting that image. | +| `value` | `html string` | The initial content of the editor. | +| `onEnterKeyPress` | `(e) => void` | The event that happens on Enter key press | +| `debouncedUpdatesEnabled` | `boolean` | If set to true, the `onChange` event handler is debounced, meaning it will only be invoked after the specified delay (default 1500ms) once the user has stopped typing. | +| `onChange` | `(json: any, html: string) => void` | This function is invoked whenever the content of the editor changes. It is passed the new content in both JSON and HTML formats. | +| `setIsSubmitting` | `(isSubmitting: "submitting" \| "submitted" \| "saved") => void` | This function is called to update the submission status. | +| `setShouldShowAlert` | `(showAlert: boolean) => void` | This function is used to show or hide an alert incase of content not being "saved". | +| `noBorder` | `boolean` | If set to true, the editor will not have a border. | +| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. | +| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. | +| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. | + +### Usage + +1. Here is an example of how to use the `RichTextEditor` component + +```tsx + { + onChange(comment_html); + }} +/> +``` + +2. Example of how to use the `LiteTextEditorWithRef` component + +```tsx +const editorRef = useRef(null); + +// can use it to set the editor's value +editorRef.current?.setEditorValue(`${watch("description_html")}`); + +// can use it to clear the editor +editorRef?.current?.clearEditor(); + +return ( + { + onChange(comment_html); + }} + /> +); +``` + +## LiteReadOnlyEditor + +| Prop | Type | Description | +| ------------------------------- | ------------- | --------------------------------------------------------------------- | +| `value` | `html string` | The initial content of the editor. | +| `noBorder` | `boolean` | If set to true, the editor will not have a border. | +| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. | +| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. | +| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. | + +### Usage + +Here is an example of how to use the `RichReadOnlyEditor` component + +```tsx + +``` diff --git a/packages/editor/extensions/package.json b/packages/editor/extensions/package.json new file mode 100644 index 000000000..54733949e --- /dev/null +++ b/packages/editor/extensions/package.json @@ -0,0 +1,60 @@ +{ + "name": "@plane/editor-extensions", + "version": "0.1.0", + "description": "Package that powers Plane's Editor with extensions", + "private": true, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "files": [ + "dist/**/*" + ], + "exports": { + ".": { + "types": "./dist/index.d.mts", + "import": "./dist/index.mjs", + "module": "./dist/index.mjs" + } + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "check-types": "tsc --noEmit" + }, + "peerDependencies": { + "next": "12.3.2", + "next-themes": "^0.2.1", + "react": "^18.2.0", + "react-dom": "18.2.0" + }, + "dependencies": { + "@tiptap/react": "^2.1.7", + "@tiptap/core": "^2.1.7", + "@tiptap/suggestion": "^2.0.4", + "@plane/editor-types": "*", + "@plane/editor-core": "*", + "eslint": "8.36.0", + "eslint-config-next": "13.2.4", + "lucide-react": "^0.244.0", + "tippy.js": "^6.3.7", + "@tiptap/pm": "^2.1.7" + }, + "devDependencies": { + "@types/node": "18.15.3", + "@types/react": "^18.2.35", + "@types/react-dom": "^18.2.14", + "eslint": "^7.32.0", + "postcss": "^8.4.29", + "tailwind-config-custom": "*", + "tsconfig": "*", + "tsup": "^7.2.0", + "typescript": "4.9.5" + }, + "keywords": [ + "editor", + "rich-text", + "markdown", + "nextjs", + "react" + ] +} diff --git a/packages/editor/extensions/postcss.config.js b/packages/editor/extensions/postcss.config.js new file mode 100644 index 000000000..07aa434b2 --- /dev/null +++ b/packages/editor/extensions/postcss.config.js @@ -0,0 +1,9 @@ +// If you want to use other PostCSS plugins, see the following: +// https://tailwindcss.com/docs/using-with-preprocessors + +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/editor/rich-text-editor/src/ui/extensions/drag-drop.tsx b/packages/editor/extensions/src/extensions/drag-drop.tsx similarity index 87% rename from packages/editor/rich-text-editor/src/ui/extensions/drag-drop.tsx rename to packages/editor/extensions/src/extensions/drag-drop.tsx index 60153daa9..33e7dcc34 100644 --- a/packages/editor/rich-text-editor/src/ui/extensions/drag-drop.tsx +++ b/packages/editor/extensions/src/extensions/drag-drop.tsx @@ -3,7 +3,30 @@ import { Extension } from "@tiptap/core"; import { PluginKey, NodeSelection, Plugin } from "@tiptap/pm/state"; // @ts-ignore import { __serializeForClipboard, EditorView } from "@tiptap/pm/view"; -import { createDragHandleElement } from "../../lib/utils/DragHandleElement"; + +function createDragHandleElement(): HTMLElement { + let dragHandleElement = document.createElement("div"); + dragHandleElement.draggable = true; + dragHandleElement.dataset.dragHandle = ""; + dragHandleElement.classList.add("drag-handle"); + + const dragHandleContainer = document.createElement("div"); + dragHandleContainer.classList.add("drag-handle-container"); + dragHandleElement.appendChild(dragHandleContainer); + + const dotsContainer = document.createElement("div"); + dotsContainer.classList.add("drag-handle-dots"); + + for (let i = 0; i < 6; i++) { + const spanElement = document.createElement("span"); + spanElement.classList.add("drag-handle-dot"); + dotsContainer.appendChild(spanElement); + } + + dragHandleContainer.appendChild(dotsContainer); + + return dragHandleElement; +} export interface DragHandleOptions { dragHandleWidth: number; @@ -220,7 +243,7 @@ function DragHandle(options: DragHandleOptions) { }); } -const DragAndDrop = Extension.create({ +export const DragAndDrop = Extension.create({ name: "dragAndDrop", addProseMirrorPlugins() { @@ -231,5 +254,3 @@ const DragAndDrop = Extension.create({ ]; }, }); - -export default DragAndDrop; diff --git a/packages/editor/rich-text-editor/src/ui/extensions/slash-command.tsx b/packages/editor/extensions/src/extensions/slash-commands.tsx similarity index 98% rename from packages/editor/rich-text-editor/src/ui/extensions/slash-command.tsx rename to packages/editor/extensions/src/extensions/slash-commands.tsx index bab13304a..399651c5a 100644 --- a/packages/editor/rich-text-editor/src/ui/extensions/slash-command.tsx +++ b/packages/editor/extensions/src/extensions/slash-commands.tsx @@ -10,6 +10,7 @@ import { Editor, Range, Extension } from "@tiptap/core"; import Suggestion from "@tiptap/suggestion"; import { ReactRenderer } from "@tiptap/react"; import tippy from "tippy.js"; +import type { UploadImage } from "@plane/editor-types"; import { Heading1, Heading2, @@ -24,7 +25,6 @@ import { ImageIcon, Table, } from "lucide-react"; -import { UploadImage } from "../"; import { cn, insertTableCommand, @@ -156,6 +156,7 @@ const getSuggestionItems = searchTerms: ["line", "divider", "horizontal", "rule", "separate"], icon: , command: ({ editor, range }: CommandProps) => { + // @ts-expect-error I have to move this to the core editor.chain().focus().deleteRange(range).setHorizontalRule().run(); }, }, @@ -191,6 +192,7 @@ const getSuggestionItems = searchTerms: ["codeblock"], icon: , command: ({ editor, range }: CommandProps) => + // @ts-expect-error I have to move this to the core editor.chain().focus().deleteRange(range).toggleCodeBlock().run(), }, { @@ -381,5 +383,3 @@ export const SlashCommand = ( render: renderItems, }, }); - -export default SlashCommand; diff --git a/packages/editor/extensions/src/index.ts b/packages/editor/extensions/src/index.ts new file mode 100644 index 000000000..76461c2e6 --- /dev/null +++ b/packages/editor/extensions/src/index.ts @@ -0,0 +1,2 @@ +export { SlashCommand } from "./extensions/slash-commands"; +export { DragAndDrop } from "./extensions/drag-drop"; diff --git a/packages/editor/extensions/tailwind.config.js b/packages/editor/extensions/tailwind.config.js new file mode 100644 index 000000000..f32063158 --- /dev/null +++ b/packages/editor/extensions/tailwind.config.js @@ -0,0 +1,6 @@ +const sharedConfig = require("tailwind-config-custom/tailwind.config.js"); + +module.exports = { + // prefix ui lib classes to avoid conflicting with the app + ...sharedConfig, +}; diff --git a/packages/editor/extensions/tsconfig.json b/packages/editor/extensions/tsconfig.json new file mode 100644 index 000000000..57d0e9a74 --- /dev/null +++ b/packages/editor/extensions/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "tsconfig/react-library.json", + "include": ["src/**/*", "index.d.ts"], + "exclude": ["dist", "build", "node_modules"] +} diff --git a/packages/editor/extensions/tsup.config.ts b/packages/editor/extensions/tsup.config.ts new file mode 100644 index 000000000..5e89e04af --- /dev/null +++ b/packages/editor/extensions/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, Options } from "tsup"; + +export default defineConfig((options: Options) => ({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + clean: false, + external: ["react"], + injectStyle: true, + ...options, +})); diff --git a/packages/editor/lite-text-editor/package.json b/packages/editor/lite-text-editor/package.json index fd07a2d0d..d5c0a4a6f 100644 --- a/packages/editor/lite-text-editor/package.json +++ b/packages/editor/lite-text-editor/package.json @@ -29,20 +29,7 @@ }, "dependencies": { "@plane/editor-core": "*", - "@plane/ui": "*", - "@tiptap/extension-list-item": "^2.1.11", - "class-variance-authority": "^0.7.0", - "clsx": "^1.2.1", - "eslint": "8.36.0", - "eslint-config-next": "13.2.4", - "eventsource-parser": "^0.1.0", - "lowlight": "^2.9.0", - "lucide-react": "^0.244.0", - "react-markdown": "^8.0.7", - "tailwind-merge": "^1.14.0", - "tippy.js": "^6.3.7", - "tiptap-markdown": "^0.8.2", - "use-debounce": "^9.0.4" + "@plane/ui": "*" }, "devDependencies": { "@types/node": "18.15.3", @@ -51,6 +38,7 @@ "eslint": "^7.32.0", "postcss": "^8.4.29", "tailwind-config-custom": "*", + "eslint-config-custom": "*", "tsconfig": "*", "tsup": "^7.2.0", "typescript": "4.9.5" diff --git a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx index 9bca18ef0..94d0b2d4e 100644 --- a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx @@ -1,12 +1,13 @@ import { Editor } from "@tiptap/react"; -import { BoldIcon } from "lucide-react"; import { BoldItem, BulletListItem, cn, CodeItem, + findTableAncestor, ImageItem, + isCellSelection, ItalicItem, NumberedListItem, QuoteItem, @@ -16,12 +17,18 @@ import { } from "@plane/editor-core"; import { Tooltip } from "@plane/ui"; import { UploadImage } from "../../"; +import type { SVGProps } from "react"; +interface LucideProps extends Partial> { + size?: string | number + absoluteStrokeWidth?: boolean +} +type LucideIcon = (props: LucideProps) => JSX.Element; export interface BubbleMenuItem { name: string; isActive: () => boolean; command: () => void; - icon: typeof BoldIcon; + icon: LucideIcon; } type EditorBubbleMenuProps = { @@ -63,10 +70,38 @@ export const FixedMenu = (props: EditorBubbleMenuProps) => { CodeItem(props.editor), ]; - const complexItems: BubbleMenuItem[] = [ - TableItem(props.editor), - ImageItem(props.editor, props.uploadFile, props.setIsSubmitting), - ]; + function getComplexItems(): BubbleMenuItem[] { + const items: BubbleMenuItem[] = [TableItem(props.editor)]; + + if (shouldShowImageItem()) { + items.push( + ImageItem(props.editor, props.uploadFile, props.setIsSubmitting), + ); + } + + return items; + } + + const complexItems: BubbleMenuItem[] = getComplexItems(); + + function shouldShowImageItem(): boolean { + if (typeof window !== "undefined") { + const selectionRange: any = window?.getSelection(); + const { selection } = props.editor.state; + + if (selectionRange.rangeCount !== 0) { + const range = selectionRange.getRangeAt(0); + if (findTableAncestor(range.startContainer)) { + return false; + } + if (isCellSelection(selection)) { + return false; + } + } + return true; + } + return false; + } const handleAccessChange = (accessKey: string) => { props.commentAccessSpecifier?.onAccessChange(accessKey); diff --git a/packages/editor/rich-text-editor/package.json b/packages/editor/rich-text-editor/package.json index c3e3d0def..b79376279 100644 --- a/packages/editor/rich-text-editor/package.json +++ b/packages/editor/rich-text-editor/package.json @@ -22,7 +22,6 @@ "check-types": "tsc --noEmit" }, "peerDependencies": { - "@tiptap/core": "^2.1.11", "next": "12.3.2", "next-themes": "^0.2.1", "react": "^18.2.0", @@ -30,11 +29,9 @@ }, "dependencies": { "@plane/editor-core": "*", - "@tiptap/extension-horizontal-rule": "^2.1.11", + "@tiptap/core": "^2.1.11", + "@plane/editor-extensions": "*", "@tiptap/extension-placeholder": "^2.1.11", - "@tiptap/suggestion": "^2.1.7", - "class-variance-authority": "^0.7.0", - "clsx": "^1.2.1", "lucide-react": "^0.244.0" }, "devDependencies": { diff --git a/packages/editor/rich-text-editor/src/lib/utils/DragHandleElement.ts b/packages/editor/rich-text-editor/src/lib/utils/DragHandleElement.ts deleted file mode 100644 index a84ca1bb0..000000000 --- a/packages/editor/rich-text-editor/src/lib/utils/DragHandleElement.ts +++ /dev/null @@ -1,23 +0,0 @@ -export function createDragHandleElement(): HTMLElement { - let dragHandleElement = document.createElement("div"); - dragHandleElement.draggable = true; - dragHandleElement.dataset.dragHandle = ""; - dragHandleElement.classList.add("drag-handle"); - - const dragHandleContainer = document.createElement("div"); - dragHandleContainer.classList.add("drag-handle-container"); - dragHandleElement.appendChild(dragHandleContainer); - - const dotsContainer = document.createElement("div"); - dotsContainer.classList.add("drag-handle-dots"); - - for (let i = 0; i < 6; i++) { - const spanElement = document.createElement("span"); - spanElement.classList.add("drag-handle-dot"); - dotsContainer.appendChild(spanElement); - } - - dragHandleContainer.appendChild(dotsContainer); - - return dragHandleElement; -} diff --git a/packages/editor/rich-text-editor/src/ui/extensions/index.tsx b/packages/editor/rich-text-editor/src/ui/extensions/index.tsx index e1b1e9b3d..0531e3355 100644 --- a/packages/editor/rich-text-editor/src/ui/extensions/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/extensions/index.tsx @@ -1,8 +1,7 @@ +import { SlashCommand } from "@plane/editor-extensions"; import Placeholder from "@tiptap/extension-placeholder"; - -import SlashCommand from "./slash-command"; +import { DragAndDrop } from "@plane/editor-extensions"; import { UploadImage } from "../"; -import DragAndDrop from "./drag-drop"; export const RichTextEditorExtensions = ( uploadFile: UploadImage, diff --git a/packages/editor/types/Readme.md b/packages/editor/types/Readme.md new file mode 100644 index 000000000..39aca1226 --- /dev/null +++ b/packages/editor/types/Readme.md @@ -0,0 +1,97 @@ +# @plane/editor-extensions + +## Description + +The `@plane/lite-text-editor` package extends from the `editor-core` package, inheriting its base functionality while adding its own unique features of Custom control over Enter key, etc. + +## Key Features + +- **Exported Components**: There are two components exported from the Lite text editor (with and without Ref), you can choose to use the `withRef` instance whenever you want to control the Editor’s state via a side effect of some external action from within the application code. + + `LiteTextEditor` & `LiteTextEditorWithRef` + +- **Read Only Editor Instances**: We have added a really light weight _Read Only_ Editor instance for the Lite editor types (with and without Ref) + `LiteReadOnlyEditor` &`LiteReadOnlyEditorWithRef` + +## LiteTextEditor + +| Prop | Type | Description | +| ------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `uploadFile` | `(file: File) => Promise` | A function that handles file upload. It takes a file as input and handles the process of uploading that file. | +| `deleteFile` | `(assetUrlWithWorkspaceId: string) => Promise` | A function that handles deleting an image. It takes the asset url from your bucket and handles the process of deleting that image. | +| `value` | `html string` | The initial content of the editor. | +| `onEnterKeyPress` | `(e) => void` | The event that happens on Enter key press | +| `debouncedUpdatesEnabled` | `boolean` | If set to true, the `onChange` event handler is debounced, meaning it will only be invoked after the specified delay (default 1500ms) once the user has stopped typing. | +| `onChange` | `(json: any, html: string) => void` | This function is invoked whenever the content of the editor changes. It is passed the new content in both JSON and HTML formats. | +| `setIsSubmitting` | `(isSubmitting: "submitting" \| "submitted" \| "saved") => void` | This function is called to update the submission status. | +| `setShouldShowAlert` | `(showAlert: boolean) => void` | This function is used to show or hide an alert incase of content not being "saved". | +| `noBorder` | `boolean` | If set to true, the editor will not have a border. | +| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. | +| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. | +| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. | + +### Usage + +1. Here is an example of how to use the `RichTextEditor` component + +```tsx + { + onChange(comment_html); + }} +/> +``` + +2. Example of how to use the `LiteTextEditorWithRef` component + +```tsx +const editorRef = useRef(null); + +// can use it to set the editor's value +editorRef.current?.setEditorValue(`${watch("description_html")}`); + +// can use it to clear the editor +editorRef?.current?.clearEditor(); + +return ( + { + onChange(comment_html); + }} + /> +); +``` + +## LiteReadOnlyEditor + +| Prop | Type | Description | +| ------------------------------- | ------------- | --------------------------------------------------------------------- | +| `value` | `html string` | The initial content of the editor. | +| `noBorder` | `boolean` | If set to true, the editor will not have a border. | +| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. | +| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. | +| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. | + +### Usage + +Here is an example of how to use the `RichReadOnlyEditor` component + +```tsx + +``` diff --git a/packages/editor/types/package.json b/packages/editor/types/package.json new file mode 100644 index 000000000..97c243690 --- /dev/null +++ b/packages/editor/types/package.json @@ -0,0 +1,50 @@ +{ + "name": "@plane/editor-types", + "version": "0.1.0", + "description": "Package that powers Plane's Editor with extensions", + "private": true, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "files": [ + "dist/**/*" + ], + "exports": { + ".": { + "types": "./dist/index.d.mts", + "import": "./dist/index.mjs", + "module": "./dist/index.mjs" + } + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "check-types": "tsc --noEmit" + }, + "peerDependencies": { + "next": "12.3.2", + "next-themes": "^0.2.1", + "react": "^18.2.0", + "react-dom": "18.2.0" + }, + "dependencies": { + "eslint": "8.36.0", + "eslint-config-next": "13.2.4" + }, + "devDependencies": { + "@types/node": "18.15.3", + "@types/react": "^18.2.35", + "@types/react-dom": "^18.2.14", + "eslint": "^7.32.0", + "tsconfig": "*", + "tsup": "^7.2.0", + "typescript": "4.9.5" + }, + "keywords": [ + "editor", + "rich-text", + "markdown", + "nextjs", + "react" + ] +} diff --git a/packages/editor/types/postcss.config.js b/packages/editor/types/postcss.config.js new file mode 100644 index 000000000..07aa434b2 --- /dev/null +++ b/packages/editor/types/postcss.config.js @@ -0,0 +1,9 @@ +// If you want to use other PostCSS plugins, see the following: +// https://tailwindcss.com/docs/using-with-preprocessors + +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/editor/types/src/index.ts b/packages/editor/types/src/index.ts new file mode 100644 index 000000000..3cadcc3d5 --- /dev/null +++ b/packages/editor/types/src/index.ts @@ -0,0 +1,6 @@ +export type { DeleteImage } from "./types/delete-image"; +export type { UploadImage } from "./types/upload-image"; +export type { + IMentionHighlight, + IMentionSuggestion, +} from "./types/mention-suggestion"; diff --git a/packages/editor/types/src/types/delete-image.ts b/packages/editor/types/src/types/delete-image.ts new file mode 100644 index 000000000..40bfffe2f --- /dev/null +++ b/packages/editor/types/src/types/delete-image.ts @@ -0,0 +1 @@ +export type DeleteImage = (assetUrlWithWorkspaceId: string) => Promise; diff --git a/packages/editor/types/src/types/mention-suggestion.ts b/packages/editor/types/src/types/mention-suggestion.ts new file mode 100644 index 000000000..dcaa3148d --- /dev/null +++ b/packages/editor/types/src/types/mention-suggestion.ts @@ -0,0 +1,10 @@ +export type IMentionSuggestion = { + id: string; + type: string; + avatar: string; + title: string; + subtitle: string; + redirect_uri: string; +}; + +export type IMentionHighlight = string; diff --git a/packages/editor/types/src/types/upload-image.ts b/packages/editor/types/src/types/upload-image.ts new file mode 100644 index 000000000..3cf1408d2 --- /dev/null +++ b/packages/editor/types/src/types/upload-image.ts @@ -0,0 +1 @@ +export type UploadImage = (file: File) => Promise; diff --git a/packages/editor/types/tailwind.config.js b/packages/editor/types/tailwind.config.js new file mode 100644 index 000000000..f32063158 --- /dev/null +++ b/packages/editor/types/tailwind.config.js @@ -0,0 +1,6 @@ +const sharedConfig = require("tailwind-config-custom/tailwind.config.js"); + +module.exports = { + // prefix ui lib classes to avoid conflicting with the app + ...sharedConfig, +}; diff --git a/packages/editor/types/tsconfig.json b/packages/editor/types/tsconfig.json new file mode 100644 index 000000000..57d0e9a74 --- /dev/null +++ b/packages/editor/types/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "tsconfig/react-library.json", + "include": ["src/**/*", "index.d.ts"], + "exclude": ["dist", "build", "node_modules"] +} diff --git a/packages/editor/types/tsup.config.ts b/packages/editor/types/tsup.config.ts new file mode 100644 index 000000000..5e89e04af --- /dev/null +++ b/packages/editor/types/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, Options } from "tsup"; + +export default defineConfig((options: Options) => ({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + clean: false, + external: ["react"], + injectStyle: true, + ...options, +})); diff --git a/turbo.json b/turbo.json index c732dae7b..ac0959c42 100644 --- a/turbo.json +++ b/turbo.json @@ -22,8 +22,13 @@ ], "pipeline": { "build": { - "dependsOn": ["^build"], - "outputs": [".next/**", "dist/**"] + "dependsOn": [ + "^build" + ], + "outputs": [ + ".next/**", + "dist/**" + ] }, "web#develop": { "cache": false, @@ -65,20 +70,32 @@ }, "@plane/lite-text-editor#build": { "cache": true, - "dependsOn": ["@plane/editor-core#build"] + "dependsOn": [ + "@plane/editor-core#build", + "@plane/editor-extensions#build", + "@plane/editor-types#build" + ] }, "@plane/rich-text-editor#build": { "cache": true, - "dependsOn": ["@plane/editor-core#build"] + "dependsOn": [ + "@plane/editor-core#build", + "@plane/editor-extensions#build", + "@plane/editor-types#build" + ] }, "@plane/document-editor#build": { "cache": true, "dependsOn": [ - "@plane/editor-core#build" + "@plane/editor-core#build", + "@plane/editor-extensions#build", + "@plane/editor-types#build" ] }, "test": { - "dependsOn": ["^build"], + "dependsOn": [ + "^build" + ], "outputs": [] }, "lint": { diff --git a/web/package.json b/web/package.json index 23ed714ea..135a913a4 100644 --- a/web/package.json +++ b/web/package.json @@ -8,7 +8,8 @@ "build": "next build", "start": "next start", "lint": "next lint", - "export": "next export" + "export": "next export", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist" }, "dependencies": { "@blueprintjs/popover2": "^1.13.3", @@ -57,7 +58,8 @@ "sharp": "^0.32.1", "swr": "^2.1.3", "tailwind-merge": "^2.0.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "use-debounce": "^9.0.4" }, "devDependencies": { "@types/js-cookie": "^3.0.2", diff --git a/yarn.lock b/yarn.lock index 285df96a9..ae75bb269 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,13 +974,6 @@ dependencies: tslib "~2.5.0" -"@blueprintjs/colors@^5.0.5": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@blueprintjs/colors/-/colors-5.0.5.tgz#3a8faa640dd2877aa4fd00b886cf8e58daf5f868" - integrity sha512-UcCsBxE8GTF6GW1oHBb+cuhPpKiJFWbIRkemwcRkp9HvXXQHxEaXlFFC6jAx5pf3JmRwde5/ck3r+lJFP1YqzA== - dependencies: - tslib "~2.6.2" - "@blueprintjs/core@^4.16.3", "@blueprintjs/core@^4.20.2": version "4.20.2" resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-4.20.2.tgz#ae1bbaf13bd1bf887b506760c478cc940f6d6e20" @@ -998,20 +991,6 @@ react-transition-group "^4.4.5" tslib "~2.5.0" -"@blueprintjs/core@^5.6.0": - version "5.6.0" - resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-5.6.0.tgz#915b708a8c2d7e916d30dae704829e2d948301d9" - integrity sha512-NtQL/iu8P8DhHUCWCstc9Ps+JkRZCPRJ2ZoxubOt21pfxN50CN0sKHkDETHUQyZ73RviveVIIK+m32mT5Wwdqg== - dependencies: - "@blueprintjs/colors" "^5.0.5" - "@blueprintjs/icons" "^5.3.0" - "@popperjs/core" "^2.11.7" - classnames "^2.3.1" - normalize.css "^8.0.1" - react-popper "^2.3.0" - react-transition-group "^4.4.5" - tslib "~2.6.2" - "@blueprintjs/icons@^4.16.0": version "4.16.0" resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-4.16.0.tgz#47f9e8abe64d84fc18721080b8f191d8aac075d8" @@ -1021,15 +1000,6 @@ classnames "^2.3.1" tslib "~2.5.0" -"@blueprintjs/icons@^5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-5.3.0.tgz#9c9498df415bf7e028ceac15f90228d0ffd9a521" - integrity sha512-PGZHbWZ41b/SDOENlZQE1pAab4eluzf/hZ6sHB5nPrQNJuGNr94yaPp6u//Tu24iqVFFP20Soi3+ckhf/o3V/g== - dependencies: - change-case "^4.1.2" - classnames "^2.3.1" - tslib "~2.6.2" - "@blueprintjs/popover2@^1.13.3": version "1.14.11" resolved "https://registry.yarnpkg.com/@blueprintjs/popover2/-/popover2-1.14.11.tgz#0698fdeaf6710460cef0b71bed592ca37f40d1f9" @@ -1043,15 +1013,6 @@ react-popper "^2.3.0" tslib "~2.5.0" -"@blueprintjs/popover2@^2.0.10": - version "2.0.17" - resolved "https://registry.yarnpkg.com/@blueprintjs/popover2/-/popover2-2.0.17.tgz#ed1fd3a6fbb0a7ae623ccb649b150a7f22a09534" - integrity sha512-sJTlLD9ihKUITC8xEm1lzae4IumXkwoTEu/ajUTgd3GlLyQsUSC5CdlBihX7LlA60WbuyVSE3Jbazhqw2Fud2w== - dependencies: - "@blueprintjs/core" "^5.6.0" - classnames "^2.3.1" - tslib "~2.6.2" - "@cfcs/core@^0.0.6": version "0.0.6" resolved "https://registry.yarnpkg.com/@cfcs/core/-/core-0.0.6.tgz#9f8499dcd2ad29fd96d8fa72055411cd4a249121" @@ -2356,7 +2317,7 @@ lodash.merge "^4.6.2" postcss-selector-parser "6.0.10" -"@tiptap/core@^2.1.12", "@tiptap/core@^2.1.7": +"@tiptap/core@^2.1.11", "@tiptap/core@^2.1.12", "@tiptap/core@^2.1.7": version "2.1.12" resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.1.12.tgz#904fdf147e91b5e60561c76e7563c1b5a32f54ab" integrity sha512-ZGc3xrBJA9KY8kln5AYTj8y+GDrKxi7u95xIl2eccrqTY5CQeRu6HRNM1yT4mAjuSaG9jmazyjGRlQuhyxCKxQ== @@ -2383,7 +2344,7 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.1.12.tgz#7c905a577ce30ef2cb335870a23f9d24fd26f6aa" integrity sha512-vtD8vWtNlmAZX8LYqt2yU9w3mU9rPCiHmbp4hDXJs2kBnI0Ju/qAyXFx6iJ3C3XyuMnMbJdDI9ee0spAvFz7cQ== -"@tiptap/extension-code-block-lowlight@^2.1.11", "@tiptap/extension-code-block-lowlight@^2.1.12": +"@tiptap/extension-code-block-lowlight@^2.1.12": version "2.1.12" resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.1.12.tgz#ccbca5d0d92bee373dc8e2e2ae6c27f62f66437c" integrity sha512-dtIbpI9QrWa9TzNO4v5q/zf7+d83wpy5i9PEccdJAVtRZ0yOI8JIZAWzG5ex3zAoCA0CnQFdsPSVykYSDdxtDA== @@ -2440,7 +2401,7 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.1.12.tgz#03bcb9422e8ea2b82dc45207d1a1b0bc0241b055" integrity sha512-6b7UFVkvPjq3LVoCTrYZAczt5sQrQUaoDWAieVClVZoFLfjga2Fwjcfgcie8IjdPt8YO2hG/sar/c07i9vM0Sg== -"@tiptap/extension-horizontal-rule@^2.1.11", "@tiptap/extension-horizontal-rule@^2.1.12": +"@tiptap/extension-horizontal-rule@^2.1.12": version "2.1.12" resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.1.12.tgz#2191d4ff68ed39381d65971ad8e2aa1be43e6d6b" integrity sha512-RRuoK4KxrXRrZNAjJW5rpaxjiP0FJIaqpi7nFbAua2oHXgsCsG8qbW2Y0WkbIoS8AJsvLZ3fNGsQ8gpdliuq3A== @@ -2462,7 +2423,7 @@ dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-list-item@^2.1.11", "@tiptap/extension-list-item@^2.1.12": +"@tiptap/extension-list-item@^2.1.12": version "2.1.12" resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.1.12.tgz#3eb28dc998490a98f14765783770b3cf6587d39e" integrity sha512-Gk7hBFofAPmNQ8+uw8w5QSsZOMEGf7KQXJnx5B022YAUJTYYxO3jYVuzp34Drk9p+zNNIcXD4kc7ff5+nFOTrg== @@ -2599,7 +2560,7 @@ "@tiptap/extension-strike" "^2.1.12" "@tiptap/extension-text" "^2.1.12" -"@tiptap/suggestion@^2.0.4", "@tiptap/suggestion@^2.1.7": +"@tiptap/suggestion@^2.0.4": version "2.1.12" resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.1.12.tgz#a13782d1e625ec03b3f61b6839ecc95b6b685d3f" integrity sha512-rhlLWwVkOodBGRMK0mAmE34l2a+BqM2Y7q1ViuQRBhs/6sZ8d83O4hARHKVwqT5stY4i1l7d7PoemV3uAGI6+g== @@ -4840,11 +4801,6 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -eventsource-parser@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-0.1.0.tgz#4a6b84751ca8e704040e6f7f50e7d77344fa1b7c" - integrity sha512-M9QjFtEIkwytUarnx113HGmgtk52LSn3jNAtnWKi3V+b9rqSfQeVdLsaD5AG/O4IrGQwmAAHBIsqbmURPTd2rA== - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -6509,7 +6465,7 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nanoid@^3.1.30, nanoid@^3.3.4, nanoid@^3.3.6: +nanoid@^3.3.4, nanoid@^3.3.6: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== @@ -7084,13 +7040,6 @@ property-information@^6.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.4.0.tgz#6bc4c618b0c2d68b3bb8b552cbb97f8e300a0f82" integrity sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ== -prosemirror-async-query@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/prosemirror-async-query/-/prosemirror-async-query-0.0.4.tgz#4fedbee082692e659ab1f472645aac7765133b1d" - integrity sha512-eliJ722n+fVuChcvoZeS3pE/mpN/TJnqMkhIfVSTAH8Vd9S7aGfT9t31idD+mwnptgIc7OUPy56UdYN+ph++TQ== - dependencies: - nanoid "^3.1.30" - prosemirror-changeset@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz#dae94b63aec618fac7bb9061648e6e2a79988383" @@ -8408,7 +8357,7 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@~2.6.2: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==