From f7e140bf68b8e457c537ae81d82ef69e049ceeba Mon Sep 17 00:00:00 2001
From: Henit Chobisa
Date: Thu, 7 Dec 2023 12:04:21 +0530
Subject: [PATCH] [ FEATURE ] New Issue Widget for displaying issues inside
`document-editor` (#2920)
* feat: added heading 3 in the editor summary markings
* feat: fixed editor and summary bar sizing
* feat: added `issue-embed` extension
* feat: exposed issue embed extension
* feat: added main embed config configuration to document editor body
* feat: added peek overview and issue embed fetch function
* feat: enabled slash commands to take additonal suggestions from editors
* chore: replaced `IssueEmbedWidget` into widget extension
* chore: removed issue embed from previous places
* feat: added issue embed suggestion extension
* feat: added issue embed suggestion renderer
* feat: added issue embed suggestions into extensions module
* feat: added issues in issueEmbedConfiguration in document editor
* chore: package fixes
* chore: removed log statements
* feat: added title updation logic into document editor
* fix: issue suggestion items, not rendering issue widget on enter
* feat: added error card for issue widget
* feat: improved focus logic for issue search and navigate
* feat: appended transactionid for issueWidgetTransaction
* chore: packages update
* feat: disabled editing of title in readonly mode
* feat: added issueEmbedConfig in readonly editor
* fix: issue suggestions not loading after structure changed to object
* feat: added toast messages for success/error messages from doc editor
* fix: issue suggestions sorting issue
* fix: formatting errors resolved
* fix: infinite reloading of the readonly document editor
* fix: css in avatar of issue widget card
* feat: added show alert on pages reload
* feat: added saving state for the pages editor
* fix: issue with heading 3 in side bar view
* style: updated issue suggestions dropdown ui
* fix: Pages intiliazation and mutation with updated MobX store
* fixed image uploads being cancelled on refocus due to swr
* fix: issue with same description rerendering empty content fixed
* fix: scroll in issue suggestion view
* fix: added submission prop
* fix: Updated the comment update to take issue id in inbox issues
* feat:changed date representation in IssueEmbedCard
* fix: page details mutation with optimistic updates using swr
* fix: menu options in read only editor with auth fixed
* fix: add error handling for title and page desc
* fixed yarn.lock
* fix: read-only editor title wrapping
* fix: build error with rich text editor
---------
Co-authored-by: Aaryan Khandelwal
Co-authored-by: Palanikannan1437 <73993394+Palanikannan1437@users.noreply.github.com>
---
.../editor/core/src/ui/hooks/use-editor.tsx | 9 +-
.../src/ui/hooks/use-read-only-editor.tsx | 53 +-
packages/editor/document-editor/package.json | 11 +-
.../src/ui/components/content-browser.tsx | 13 +-
.../src/ui/components/editor-header.tsx | 22 +-
.../src/ui/components/heading-component.tsx | 16 +
.../src/ui/components/info-popover.tsx | 2 +-
.../src/ui/components/page-renderer.tsx | 47 +-
.../ui/components/vertical-dropdown-menu.tsx | 4 +-
.../src/ui/extensions/index.tsx | 66 ++-
.../IssueEmbedSuggestionList/index.tsx | 56 +++
.../issue-suggestion-extension.tsx | 38 ++
.../issue-suggestion-items.tsx | 18 +
.../issue-suggestion-renderer.tsx | 279 +++++++++++
.../widgets/IssueEmbedWidget/index.tsx | 12 +
.../IssueEmbedWidget/issue-widget-card.tsx | 89 ++++
.../IssueEmbedWidget/issue-widget-node.tsx | 68 +++
.../widgets/IssueEmbedWidget/types.ts | 9 +
.../src/ui/hooks/use-editor-markings.tsx | 12 +-
.../editor/document-editor/src/ui/index.tsx | 40 +-
.../document-editor/src/ui/readonly/index.tsx | 24 +-
.../src/ui/utils/menu-options.ts | 84 +++-
.../src/extensions/slash-commands.tsx | 254 +++++-----
.../editor/rich-text-editor/src/ui/index.tsx | 9 +-
packages/editor/types/package.json | 1 +
packages/editor/types/src/index.ts | 1 +
.../src/types/slash-commands-suggestion.ts | 15 +
web/components/inbox/issue-activity.tsx | 34 +-
web/components/issues/description-form.tsx | 13 +-
.../issue-peek-overview/issue-detail.tsx | 13 +-
.../projects/[projectId]/pages/[pageId].tsx | 457 ++++++++++++------
web/services/page.service.ts | 9 +-
yarn.lock | 15 +-
33 files changed, 1412 insertions(+), 381 deletions(-)
create mode 100644 packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/index.tsx
create mode 100644 packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-extension.tsx
create mode 100644 packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-items.tsx
create mode 100644 packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-renderer.tsx
create mode 100644 packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/index.tsx
create mode 100644 packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/issue-widget-card.tsx
create mode 100644 packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/issue-widget-node.tsx
create mode 100644 packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/types.ts
create mode 100644 packages/editor/types/src/types/slash-commands-suggestion.ts
diff --git a/packages/editor/core/src/ui/hooks/use-editor.tsx b/packages/editor/core/src/ui/hooks/use-editor.tsx
index bd349f3ef..d30e8ca89 100644
--- a/packages/editor/core/src/ui/hooks/use-editor.tsx
+++ b/packages/editor/core/src/ui/hooks/use-editor.tsx
@@ -14,7 +14,10 @@ import {
interface CustomEditorProps {
uploadFile: UploadImage;
restoreFile: RestoreImage;
- text_html?: string;
+ rerenderOnPropsChange?: {
+ id: string;
+ description_html: string;
+ };
deleteFile: DeleteImage;
cancelUploadImage?: () => any;
setIsSubmitting?: (
@@ -38,7 +41,7 @@ export const useEditor = ({
cancelUploadImage,
editorProps = {},
value,
- text_html,
+ rerenderOnPropsChange,
extensions = [],
onStart,
onChange,
@@ -79,7 +82,7 @@ export const useEditor = ({
onChange?.(editor.getJSON(), getTrimmedHTML(editor.getHTML()));
},
},
- [text_html],
+ [rerenderOnPropsChange],
);
const editorRef: MutableRefObject = useRef(null);
diff --git a/packages/editor/core/src/ui/hooks/use-read-only-editor.tsx b/packages/editor/core/src/ui/hooks/use-read-only-editor.tsx
index 3207e5e56..3339e095f 100644
--- a/packages/editor/core/src/ui/hooks/use-read-only-editor.tsx
+++ b/packages/editor/core/src/ui/hooks/use-read-only-editor.tsx
@@ -1,10 +1,5 @@
import { useEditor as useCustomEditor, Editor } from "@tiptap/react";
-import {
- useImperativeHandle,
- useRef,
- MutableRefObject,
- useEffect,
-} from "react";
+import { useImperativeHandle, useRef, MutableRefObject } from "react";
import { CoreReadOnlyEditorExtensions } from "../read-only/extensions";
import { CoreReadOnlyEditorProps } from "../read-only/props";
import { EditorProps } from "@tiptap/pm/view";
@@ -15,6 +10,10 @@ interface CustomReadOnlyEditorProps {
forwardedRef?: any;
extensions?: any;
editorProps?: EditorProps;
+ rerenderOnPropsChange?: {
+ id: string;
+ description_html: string;
+ };
mentionHighlights?: string[];
mentionSuggestions?: IMentionSuggestion[];
}
@@ -24,33 +23,29 @@ export const useReadOnlyEditor = ({
forwardedRef,
extensions = [],
editorProps = {},
+ rerenderOnPropsChange,
mentionHighlights,
mentionSuggestions,
}: CustomReadOnlyEditorProps) => {
- const editor = useCustomEditor({
- editable: false,
- content:
- typeof value === "string" && value.trim() !== "" ? value : "",
- editorProps: {
- ...CoreReadOnlyEditorProps,
- ...editorProps,
+ const editor = useCustomEditor(
+ {
+ editable: false,
+ content:
+ typeof value === "string" && value.trim() !== "" ? value : "",
+ editorProps: {
+ ...CoreReadOnlyEditorProps,
+ ...editorProps,
+ },
+ extensions: [
+ ...CoreReadOnlyEditorExtensions({
+ mentionSuggestions: mentionSuggestions ?? [],
+ mentionHighlights: mentionHighlights ?? [],
+ }),
+ ...extensions,
+ ],
},
- extensions: [
- ...CoreReadOnlyEditorExtensions({
- mentionSuggestions: mentionSuggestions ?? [],
- mentionHighlights: mentionHighlights ?? [],
- }),
- ...extensions,
- ],
- });
-
- const hasIntiliazedContent = useRef(false);
- useEffect(() => {
- if (editor && !value && !hasIntiliazedContent.current) {
- editor.commands.setContent(value);
- hasIntiliazedContent.current = true;
- }
- }, [value]);
+ [rerenderOnPropsChange],
+ );
const editorRef: MutableRefObject = useRef(null);
editorRef.current = editor;
diff --git a/packages/editor/document-editor/package.json b/packages/editor/document-editor/package.json
index 8789f37ee..4e0eeffb2 100644
--- a/packages/editor/document-editor/package.json
+++ b/packages/editor/document-editor/package.json
@@ -28,15 +28,22 @@
"react-dom": "18.2.0"
},
"dependencies": {
- "@plane/ui": "*",
"@plane/editor-core": "*",
"@plane/editor-extensions": "*",
"@plane/editor-types": "*",
+ "@plane/ui": "*",
"@tiptap/core": "^2.1.7",
"@tiptap/extension-placeholder": "^2.1.11",
+ "@tiptap/pm": "^2.1.12",
+ "@tiptap/suggestion": "^2.1.12",
+ "@types/node": "18.15.3",
+ "@types/react": "^18.2.39",
+ "@types/react-dom": "18.0.11",
"eslint": "8.36.0",
"eslint-config-next": "13.2.4",
- "react-popper": "^2.3.0"
+ "react-popper": "^2.3.0",
+ "tippy.js": "^6.3.7",
+ "uuid": "^9.0.1"
},
"devDependencies": {
"@types/node": "18.15.3",
diff --git a/packages/editor/document-editor/src/ui/components/content-browser.tsx b/packages/editor/document-editor/src/ui/components/content-browser.tsx
index bb0e3fb8b..68f6469b8 100644
--- a/packages/editor/document-editor/src/ui/components/content-browser.tsx
+++ b/packages/editor/document-editor/src/ui/components/content-browser.tsx
@@ -1,4 +1,8 @@
-import { HeadingComp, SubheadingComp } from "./heading-component";
+import {
+ HeadingComp,
+ HeadingThreeComp,
+ SubheadingComp,
+} from "./heading-component";
import { IMarking } from "..";
import { Editor } from "@tiptap/react";
import { scrollSummary } from "../utils/editor-summary-utils";
@@ -22,11 +26,16 @@ export const ContentBrowser = (props: ContentBrowserProps) => {
onClick={() => scrollSummary(editor, marking)}
heading={marking.text}
/>
- ) : (
+ ) : marking.level === 2 ? (
scrollSummary(editor, marking)}
subHeading={marking.text}
/>
+ ) : (
+ scrollSummary(editor, marking)}
+ />
),
)
) : (
diff --git a/packages/editor/document-editor/src/ui/components/editor-header.tsx b/packages/editor/document-editor/src/ui/components/editor-header.tsx
index 74372ae16..6d548669e 100644
--- a/packages/editor/document-editor/src/ui/components/editor-header.tsx
+++ b/packages/editor/document-editor/src/ui/components/editor-header.tsx
@@ -1,7 +1,8 @@
import { Editor } from "@tiptap/react";
-import { Archive, Info, Lock } from "lucide-react";
-import { IMarking, UploadImage } from "..";
+import { Archive, RefreshCw, Lock } from "lucide-react";
+import { IMarking } from "..";
import { FixedMenu } from "../menu";
+import { UploadImage } from "@plane/editor-types";
import { DocumentDetails } from "../types/editor-types";
import { AlertLabel } from "./alert-label";
import {
@@ -26,6 +27,7 @@ interface IEditorHeader {
isSubmitting: "submitting" | "submitted" | "saved",
) => void;
documentDetails: DocumentDetails;
+ isSubmitting?: "submitting" | "submitted" | "saved";
}
export const EditorHeader = (props: IEditorHeader) => {
@@ -42,6 +44,7 @@ export const EditorHeader = (props: IEditorHeader) => {
KanbanMenuOptions,
isArchived,
isLocked,
+ isSubmitting,
} = props;
return (
@@ -82,6 +85,21 @@ export const EditorHeader = (props: IEditorHeader) => {
label={`Archived at ${new Date(archivedAt).toLocaleString()}`}
/>
)}
+
+ {!isLocked && !isArchived ? (
+
+ {isSubmitting !== "submitted" && isSubmitting !== "saved" && (
+
+ )}
+
+ {isSubmitting === "submitting" ? "Saving..." : "Saved"}
+
+
+ ) : null}
{!isArchived && }
diff --git a/packages/editor/document-editor/src/ui/components/heading-component.tsx b/packages/editor/document-editor/src/ui/components/heading-component.tsx
index ea31b67c1..d8ceea8f9 100644
--- a/packages/editor/document-editor/src/ui/components/heading-component.tsx
+++ b/packages/editor/document-editor/src/ui/components/heading-component.tsx
@@ -29,3 +29,19 @@ export const SubheadingComp = ({
{subHeading}
);
+
+export const HeadingThreeComp = ({
+ heading,
+ onClick,
+}: {
+ heading: string;
+ onClick: (event: React.MouseEvent) => void;
+}) => (
+
+ {heading}
+
+);
diff --git a/packages/editor/document-editor/src/ui/components/info-popover.tsx b/packages/editor/document-editor/src/ui/components/info-popover.tsx
index d42e32a8d..41d131afb 100644
--- a/packages/editor/document-editor/src/ui/components/info-popover.tsx
+++ b/packages/editor/document-editor/src/ui/components/info-popover.tsx
@@ -48,7 +48,7 @@ export const InfoPopover: React.FC = (props) => {
onMouseEnter={() => setIsPopoverOpen(true)}
onMouseLeave={() => setIsPopoverOpen(false)}
>
-