[WEB-735] chore: added a custom placeholder prop to all the editors (#4194)

* chore: added a custom placeholder prop to all the editors

* chore: inbox issue create modal placeholder
This commit is contained in:
Aaryan Khandelwal 2024-04-15 20:00:02 +05:30 committed by GitHub
parent 77b323f562
commit abce0ed946
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 102 additions and 80 deletions

View File

@ -36,11 +36,11 @@
"@tiptap/extension-image": "^2.1.13", "@tiptap/extension-image": "^2.1.13",
"@tiptap/extension-list-item": "^2.1.13", "@tiptap/extension-list-item": "^2.1.13",
"@tiptap/extension-mention": "^2.1.13", "@tiptap/extension-mention": "^2.1.13",
"@tiptap/extension-placeholder": "^2.3.0",
"@tiptap/extension-task-item": "^2.1.13", "@tiptap/extension-task-item": "^2.1.13",
"@tiptap/extension-task-list": "^2.1.13", "@tiptap/extension-task-list": "^2.1.13",
"@tiptap/extension-text-style": "^2.1.13", "@tiptap/extension-text-style": "^2.1.13",
"@tiptap/extension-underline": "^2.1.13", "@tiptap/extension-underline": "^2.1.13",
"prosemirror-codemark": "^0.4.2",
"@tiptap/pm": "^2.1.13", "@tiptap/pm": "^2.1.13",
"@tiptap/react": "^2.1.13", "@tiptap/react": "^2.1.13",
"@tiptap/starter-kit": "^2.1.13", "@tiptap/starter-kit": "^2.1.13",
@ -52,6 +52,7 @@
"linkifyjs": "^4.1.3", "linkifyjs": "^4.1.3",
"lowlight": "^3.0.0", "lowlight": "^3.0.0",
"lucide-react": "^0.294.0", "lucide-react": "^0.294.0",
"prosemirror-codemark": "^0.4.2",
"react-moveable": "^0.54.2", "react-moveable": "^0.54.2",
"tailwind-merge": "^1.14.0", "tailwind-merge": "^1.14.0",
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",

View File

@ -34,6 +34,7 @@ interface CustomEditorProps {
suggestions?: () => Promise<IMentionSuggestion[]>; suggestions?: () => Promise<IMentionSuggestion[]>;
}; };
handleEditorReady?: (value: boolean) => void; handleEditorReady?: (value: boolean) => void;
placeholder?: string | ((isFocused: boolean) => string);
} }
export const useEditor = ({ export const useEditor = ({
@ -51,6 +52,7 @@ export const useEditor = ({
restoreFile, restoreFile,
handleEditorReady, handleEditorReady,
mentionHandler, mentionHandler,
placeholder,
}: CustomEditorProps) => { }: CustomEditorProps) => {
const editor = useCustomEditor({ const editor = useCustomEditor({
editorProps: { editorProps: {
@ -58,15 +60,18 @@ export const useEditor = ({
...editorProps, ...editorProps,
}, },
extensions: [ extensions: [
...CoreEditorExtensions( ...CoreEditorExtensions({
{ mentionConfig: {
mentionSuggestions: mentionHandler.suggestions ?? (() => Promise.resolve<IMentionSuggestion[]>([])), mentionSuggestions: mentionHandler.suggestions ?? (() => Promise.resolve<IMentionSuggestion[]>([])),
mentionHighlights: mentionHandler.highlights ?? [], mentionHighlights: mentionHandler.highlights ?? [],
}, },
deleteFile, fileConfig: {
restoreFile, deleteFile,
cancelUploadImage restoreFile,
), cancelUploadImage,
},
placeholder,
}),
...extensions, ...extensions,
], ],
content: typeof initialValue === "string" && initialValue.trim() !== "" ? initialValue : "<p></p>", content: typeof initialValue === "string" && initialValue.trim() !== "" ? initialValue : "<p></p>",

View File

@ -2,6 +2,7 @@ import TaskItem from "@tiptap/extension-task-item";
import TaskList from "@tiptap/extension-task-list"; import TaskList from "@tiptap/extension-task-list";
import TextStyle from "@tiptap/extension-text-style"; import TextStyle from "@tiptap/extension-text-style";
import TiptapUnderline from "@tiptap/extension-underline"; import TiptapUnderline from "@tiptap/extension-underline";
import Placeholder from "@tiptap/extension-placeholder";
import StarterKit from "@tiptap/starter-kit"; import StarterKit from "@tiptap/starter-kit";
import { Markdown } from "tiptap-markdown"; import { Markdown } from "tiptap-markdown";
@ -29,15 +30,24 @@ import { CustomTypographyExtension } from "src/ui/extensions/typography";
import { CustomHorizontalRule } from "src/ui/extensions/horizontal-rule/horizontal-rule"; import { CustomHorizontalRule } from "src/ui/extensions/horizontal-rule/horizontal-rule";
import { CustomCodeMarkPlugin } from "./custom-code-inline/inline-code-plugin"; import { CustomCodeMarkPlugin } from "./custom-code-inline/inline-code-plugin";
export const CoreEditorExtensions = ( type TArguments = {
mentionConfig: { mentionConfig: {
mentionSuggestions?: () => Promise<IMentionSuggestion[]>; mentionSuggestions?: () => Promise<IMentionSuggestion[]>;
mentionHighlights?: () => Promise<IMentionHighlight[]>; mentionHighlights?: () => Promise<IMentionHighlight[]>;
}, };
deleteFile: DeleteImage, fileConfig: {
restoreFile: RestoreImage, deleteFile: DeleteImage;
cancelUploadImage?: () => any restoreFile: RestoreImage;
) => [ cancelUploadImage?: () => any;
};
placeholder?: string | ((isFocused: boolean) => string);
};
export const CoreEditorExtensions = ({
mentionConfig,
fileConfig: { deleteFile, restoreFile, cancelUploadImage },
placeholder,
}: TArguments) => [
StarterKit.configure({ StarterKit.configure({
bulletList: { bulletList: {
HTMLAttributes: { HTMLAttributes: {
@ -124,4 +134,21 @@ export const CoreEditorExtensions = (
mentionHighlights: mentionConfig.mentionHighlights, mentionHighlights: mentionConfig.mentionHighlights,
readonly: false, readonly: false,
}), }),
Placeholder.configure({
placeholder: ({ editor, node }) => {
if (node.type.name === "heading") return `Heading ${node.attrs.level}`;
const shouldHidePlaceholder =
editor.isActive("table") || editor.isActive("codeBlock") || editor.isActive("image");
if (shouldHidePlaceholder) return "";
if (placeholder) {
if (typeof placeholder === "string") return placeholder;
else return placeholder(editor.isFocused);
}
return "Press '/' for commands...";
},
includeChildren: true,
}),
]; ];

View File

@ -34,7 +34,6 @@
"@plane/ui": "*", "@plane/ui": "*",
"@tippyjs/react": "^4.2.6", "@tippyjs/react": "^4.2.6",
"@tiptap/core": "^2.1.13", "@tiptap/core": "^2.1.13",
"@tiptap/extension-placeholder": "^2.1.13",
"@tiptap/pm": "^2.1.13", "@tiptap/pm": "^2.1.13",
"@tiptap/suggestion": "^2.1.13", "@tiptap/suggestion": "^2.1.13",
"lucide-react": "^0.309.0", "lucide-react": "^0.309.0",

View File

@ -1,28 +1,15 @@
import Placeholder from "@tiptap/extension-placeholder";
import { IssueWidgetPlaceholder } from "src/ui/extensions/widgets/issue-embed-widget"; import { IssueWidgetPlaceholder } from "src/ui/extensions/widgets/issue-embed-widget";
import { SlashCommand, DragAndDrop } from "@plane/editor-extensions"; import { SlashCommand, DragAndDrop } from "@plane/editor-extensions";
import { UploadImage } from "@plane/editor-core"; import { UploadImage } from "@plane/editor-core";
export const DocumentEditorExtensions = ( type TArguments = {
uploadFile: UploadImage, uploadFile: UploadImage;
setHideDragHandle?: (hideDragHandlerFromDragDrop: () => void) => void setHideDragHandle?: (hideDragHandlerFromDragDrop: () => void) => void;
) => [ };
export const DocumentEditorExtensions = ({ uploadFile, setHideDragHandle }: TArguments) => [
SlashCommand(uploadFile), SlashCommand(uploadFile),
DragAndDrop(setHideDragHandle), DragAndDrop(setHideDragHandle),
Placeholder.configure({
placeholder: ({ editor, node }) => {
if (node.type.name === "heading") {
return `Heading ${node.attrs.level}`;
}
if (editor.isActive("table") || editor.isActive("codeBlock") || editor.isActive("image")) {
return "";
}
return "Press '/' for commands...";
},
includeChildren: true,
}),
IssueWidgetPlaceholder(), IssueWidgetPlaceholder(),
]; ];

View File

@ -31,6 +31,7 @@ interface IDocumentEditor {
suggestions: () => Promise<IMentionSuggestion[]>; suggestions: () => Promise<IMentionSuggestion[]>;
}; };
tabIndex?: number; tabIndex?: number;
placeholder?: string | ((isFocused: boolean) => string);
} }
const DocumentEditor = (props: IDocumentEditor) => { const DocumentEditor = (props: IDocumentEditor) => {
@ -45,6 +46,7 @@ const DocumentEditor = (props: IDocumentEditor) => {
handleEditorReady, handleEditorReady,
forwardedRef, forwardedRef,
tabIndex, tabIndex,
placeholder,
} = props; } = props;
// states // states
const [hideDragHandleOnMouseLeave, setHideDragHandleOnMouseLeave] = useState<() => void>(() => {}); const [hideDragHandleOnMouseLeave, setHideDragHandleOnMouseLeave] = useState<() => void>(() => {});
@ -69,7 +71,11 @@ const DocumentEditor = (props: IDocumentEditor) => {
handleEditorReady, handleEditorReady,
forwardedRef, forwardedRef,
mentionHandler, mentionHandler,
extensions: DocumentEditorExtensions(fileHandler.upload, setHideDragHandleFunction), extensions: DocumentEditorExtensions({
uploadFile: fileHandler.upload,
setHideDragHandle: setHideDragHandleFunction,
}),
placeholder,
}); });
const editorContainerClassNames = getEditorClassNames({ const editorContainerClassNames = getEditorClassNames({

View File

@ -32,6 +32,7 @@ export interface ILiteTextEditor {
suggestions?: () => Promise<IMentionSuggestion[]>; suggestions?: () => Promise<IMentionSuggestion[]>;
}; };
tabIndex?: number; tabIndex?: number;
placeholder?: string | ((isFocused: boolean) => string);
} }
const LiteTextEditor = (props: ILiteTextEditor) => { const LiteTextEditor = (props: ILiteTextEditor) => {
@ -46,6 +47,7 @@ const LiteTextEditor = (props: ILiteTextEditor) => {
onEnterKeyPress, onEnterKeyPress,
tabIndex, tabIndex,
mentionHandler, mentionHandler,
placeholder = "Add comment...",
} = props; } = props;
const editor = useEditor({ const editor = useEditor({
@ -60,6 +62,7 @@ const LiteTextEditor = (props: ILiteTextEditor) => {
forwardedRef, forwardedRef,
extensions: LiteTextEditorExtensions(onEnterKeyPress), extensions: LiteTextEditorExtensions(onEnterKeyPress),
mentionHandler, mentionHandler,
placeholder,
}); });
const editorContainerClassName = getEditorClassNames({ const editorContainerClassName = getEditorClassNames({

View File

@ -32,7 +32,6 @@
"@plane/editor-core": "*", "@plane/editor-core": "*",
"@plane/editor-extensions": "*", "@plane/editor-extensions": "*",
"@tiptap/core": "^2.1.13", "@tiptap/core": "^2.1.13",
"@tiptap/extension-placeholder": "^2.1.13",
"lucide-react": "^0.294.0" "lucide-react": "^0.294.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,26 +1,13 @@
import { UploadImage } from "@plane/editor-core"; import { UploadImage } from "@plane/editor-core";
import { DragAndDrop, SlashCommand } from "@plane/editor-extensions"; import { DragAndDrop, SlashCommand } from "@plane/editor-extensions";
import Placeholder from "@tiptap/extension-placeholder";
export const RichTextEditorExtensions = ( type TArguments = {
uploadFile: UploadImage, uploadFile: UploadImage;
dragDropEnabled?: boolean, dragDropEnabled?: boolean;
setHideDragHandle?: (hideDragHandlerFromDragDrop: () => void) => void setHideDragHandle?: (hideDragHandlerFromDragDrop: () => void) => void;
) => [ };
export const RichTextEditorExtensions = ({ uploadFile, dragDropEnabled, setHideDragHandle }: TArguments) => [
SlashCommand(uploadFile), SlashCommand(uploadFile),
dragDropEnabled === true && DragAndDrop(setHideDragHandle), dragDropEnabled === true && DragAndDrop(setHideDragHandle),
Placeholder.configure({
placeholder: ({ editor, node }) => {
if (node.type.name === "heading") {
return `Heading ${node.attrs.level}`;
}
if (editor.isActive("table") || editor.isActive("codeBlock") || editor.isActive("image")) {
return "";
}
return "Press '/' for commands...";
},
includeChildren: true,
}),
]; ];

View File

@ -35,6 +35,7 @@ export type IRichTextEditor = {
highlights: () => Promise<IMentionHighlight[]>; highlights: () => Promise<IMentionHighlight[]>;
suggestions: () => Promise<IMentionSuggestion[]>; suggestions: () => Promise<IMentionSuggestion[]>;
}; };
placeholder?: string | ((isFocused: boolean) => string);
tabIndex?: number; tabIndex?: number;
}; };
@ -50,6 +51,7 @@ const RichTextEditor = (props: IRichTextEditor) => {
forwardedRef, forwardedRef,
// rerenderOnPropsChange, // rerenderOnPropsChange,
id = "", id = "",
placeholder,
tabIndex, tabIndex,
mentionHandler, mentionHandler,
} = props; } = props;
@ -74,8 +76,13 @@ const RichTextEditor = (props: IRichTextEditor) => {
value, value,
forwardedRef, forwardedRef,
// rerenderOnPropsChange, // rerenderOnPropsChange,
extensions: RichTextEditorExtensions(fileHandler.upload, dragDropEnabled, setHideDragHandleFunction), extensions: RichTextEditorExtensions({
uploadFile: fileHandler.upload,
dragDropEnabled,
setHideDragHandle: setHideDragHandleFunction,
}),
mentionHandler, mentionHandler,
placeholder,
}); });
const editorContainerClassName = getEditorClassNames({ const editorContainerClassName = getEditorClassNames({

View File

@ -38,8 +38,10 @@ export const InboxIssueDescription: FC<TInboxIssueDescription> = observer((props
workspaceId={workspaceId} workspaceId={workspaceId}
projectId={projectId} projectId={projectId}
dragDropEnabled={false} dragDropEnabled={false}
onChange={(_description: object, description_html: string) => { onChange={(_description: object, description_html: string) => handleData("description_html", description_html)}
handleData("description_html", description_html); placeholder={(isFocused) => {
if (isFocused) return "Press '/' for commands...";
else return "Click to add description";
}} }}
/> />
</div> </div>

View File

@ -19,6 +19,7 @@ export type IssueDescriptionInputProps = {
initialValue: string | undefined; initialValue: string | undefined;
disabled?: boolean; disabled?: boolean;
issueOperations: TIssueOperations; issueOperations: TIssueOperations;
placeholder?: string | ((isFocused: boolean) => string);
setIsSubmitting: (initialValue: "submitting" | "submitted" | "saved") => void; setIsSubmitting: (initialValue: "submitting" | "submitted" | "saved") => void;
swrIssueDescription: string | null | undefined; swrIssueDescription: string | null | undefined;
}; };
@ -33,6 +34,7 @@ export const IssueDescriptionInput: FC<IssueDescriptionInputProps> = observer((p
initialValue, initialValue,
issueOperations, issueOperations,
setIsSubmitting, setIsSubmitting,
placeholder,
} = props; } = props;
const { handleSubmit, reset, control } = useForm<TIssue>({ const { handleSubmit, reset, control } = useForm<TIssue>({
@ -103,6 +105,14 @@ export const IssueDescriptionInput: FC<IssueDescriptionInputProps> = observer((p
onChange(description_html); onChange(description_html);
debouncedFormSave(); debouncedFormSave();
}} }}
placeholder={
placeholder
? placeholder
: (isFocused) => {
if (isFocused) return "Press '/' for commands...";
else return "Click to add description";
}
}
/> />
) : ( ) : (
<RichTextReadOnlyEditor <RichTextReadOnlyEditor

View File

@ -477,6 +477,10 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
}} }}
ref={editorRef} ref={editorRef}
tabIndex={getTabIndex("description_html")} tabIndex={getTabIndex("description_html")}
placeholder={(isFocused) => {
if (isFocused) return "Press '/' for commands...";
else return "Click to add description";
}}
/> />
)} )}
/> />

View File

@ -2477,10 +2477,10 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.1.13.tgz#30f8ae3f8833c606b339f3554b9ffdbe1e604463" resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.1.13.tgz#30f8ae3f8833c606b339f3554b9ffdbe1e604463"
integrity sha512-cEoZBJrsQn69FPpUMePXG/ltGXtqKISgypj70PEHXt5meKDjpmMVSY4/8cXvFYEYsI9GvIwyAK0OrfAHiSoROA== integrity sha512-cEoZBJrsQn69FPpUMePXG/ltGXtqKISgypj70PEHXt5meKDjpmMVSY4/8cXvFYEYsI9GvIwyAK0OrfAHiSoROA==
"@tiptap/extension-placeholder@^2.1.13": "@tiptap/extension-placeholder@^2.3.0":
version "2.1.13" version "2.3.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.1.13.tgz#b735591f719b9fe89c90dcc6327d2ef2851be510" resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.3.0.tgz#db1f4375c8365c491211457c6d13f6efa486cd3a"
integrity sha512-vIY7y7UbqsrAW/y8bDE9eRenbQEU16kNHB5Wri8RU1YiUZpkPgdXP/pLqyjIIq95SwP/vdTIHjHoQ77VLRl1hA== integrity sha512-1BOyxVLzyUYf6yOOeJ8CfpP6DSCS4L6HjBZqj6WP1z1NyBV8RAfhf3UuLNcimfSWAETXFR3g0ZbaxxWffI1cEg==
"@tiptap/extension-strike@^2.1.13": "@tiptap/extension-strike@^2.1.13":
version "2.1.13" version "2.1.13"
@ -7854,16 +7854,8 @@ streamx@^2.15.0:
fast-fifo "^1.1.0" fast-fifo "^1.1.0"
queue-tick "^1.0.1" queue-tick "^1.0.1"
"string-width-cjs@npm:string-width@^4.2.0": "string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
version "4.2.3" name string-width-cjs
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0:
version "4.2.3" version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -7939,14 +7931,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1" is-obj "^1.0.1"
is-regexp "^1.0.0" is-regexp "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1": "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==