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==