[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-list-item": "^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-list": "^2.1.13",
"@tiptap/extension-text-style": "^2.1.13",
"@tiptap/extension-underline": "^2.1.13",
"prosemirror-codemark": "^0.4.2",
"@tiptap/pm": "^2.1.13",
"@tiptap/react": "^2.1.13",
"@tiptap/starter-kit": "^2.1.13",
@ -52,6 +52,7 @@
"linkifyjs": "^4.1.3",
"lowlight": "^3.0.0",
"lucide-react": "^0.294.0",
"prosemirror-codemark": "^0.4.2",
"react-moveable": "^0.54.2",
"tailwind-merge": "^1.14.0",
"tippy.js": "^6.3.7",

View File

@ -34,6 +34,7 @@ interface CustomEditorProps {
suggestions?: () => Promise<IMentionSuggestion[]>;
};
handleEditorReady?: (value: boolean) => void;
placeholder?: string | ((isFocused: boolean) => string);
}
export const useEditor = ({
@ -51,6 +52,7 @@ export const useEditor = ({
restoreFile,
handleEditorReady,
mentionHandler,
placeholder,
}: CustomEditorProps) => {
const editor = useCustomEditor({
editorProps: {
@ -58,15 +60,18 @@ export const useEditor = ({
...editorProps,
},
extensions: [
...CoreEditorExtensions(
{
...CoreEditorExtensions({
mentionConfig: {
mentionSuggestions: mentionHandler.suggestions ?? (() => Promise.resolve<IMentionSuggestion[]>([])),
mentionHighlights: mentionHandler.highlights ?? [],
},
fileConfig: {
deleteFile,
restoreFile,
cancelUploadImage
),
cancelUploadImage,
},
placeholder,
}),
...extensions,
],
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 TextStyle from "@tiptap/extension-text-style";
import TiptapUnderline from "@tiptap/extension-underline";
import Placeholder from "@tiptap/extension-placeholder";
import StarterKit from "@tiptap/starter-kit";
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 { CustomCodeMarkPlugin } from "./custom-code-inline/inline-code-plugin";
export const CoreEditorExtensions = (
type TArguments = {
mentionConfig: {
mentionSuggestions?: () => Promise<IMentionSuggestion[]>;
mentionHighlights?: () => Promise<IMentionHighlight[]>;
},
deleteFile: DeleteImage,
restoreFile: RestoreImage,
cancelUploadImage?: () => any
) => [
};
fileConfig: {
deleteFile: DeleteImage;
restoreFile: RestoreImage;
cancelUploadImage?: () => any;
};
placeholder?: string | ((isFocused: boolean) => string);
};
export const CoreEditorExtensions = ({
mentionConfig,
fileConfig: { deleteFile, restoreFile, cancelUploadImage },
placeholder,
}: TArguments) => [
StarterKit.configure({
bulletList: {
HTMLAttributes: {
@ -124,4 +134,21 @@ export const CoreEditorExtensions = (
mentionHighlights: mentionConfig.mentionHighlights,
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": "*",
"@tippyjs/react": "^4.2.6",
"@tiptap/core": "^2.1.13",
"@tiptap/extension-placeholder": "^2.1.13",
"@tiptap/pm": "^2.1.13",
"@tiptap/suggestion": "^2.1.13",
"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 { SlashCommand, DragAndDrop } from "@plane/editor-extensions";
import { UploadImage } from "@plane/editor-core";
export const DocumentEditorExtensions = (
uploadFile: UploadImage,
setHideDragHandle?: (hideDragHandlerFromDragDrop: () => void) => void
) => [
type TArguments = {
uploadFile: UploadImage;
setHideDragHandle?: (hideDragHandlerFromDragDrop: () => void) => void;
};
export const DocumentEditorExtensions = ({ uploadFile, setHideDragHandle }: TArguments) => [
SlashCommand(uploadFile),
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(),
];

View File

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

View File

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

View File

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

View File

@ -1,26 +1,13 @@
import { UploadImage } from "@plane/editor-core";
import { DragAndDrop, SlashCommand } from "@plane/editor-extensions";
import Placeholder from "@tiptap/extension-placeholder";
export const RichTextEditorExtensions = (
uploadFile: UploadImage,
dragDropEnabled?: boolean,
setHideDragHandle?: (hideDragHandlerFromDragDrop: () => void) => void
) => [
type TArguments = {
uploadFile: UploadImage;
dragDropEnabled?: boolean;
setHideDragHandle?: (hideDragHandlerFromDragDrop: () => void) => void;
};
export const RichTextEditorExtensions = ({ uploadFile, dragDropEnabled, setHideDragHandle }: TArguments) => [
SlashCommand(uploadFile),
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[]>;
suggestions: () => Promise<IMentionSuggestion[]>;
};
placeholder?: string | ((isFocused: boolean) => string);
tabIndex?: number;
};
@ -50,6 +51,7 @@ const RichTextEditor = (props: IRichTextEditor) => {
forwardedRef,
// rerenderOnPropsChange,
id = "",
placeholder,
tabIndex,
mentionHandler,
} = props;
@ -74,8 +76,13 @@ const RichTextEditor = (props: IRichTextEditor) => {
value,
forwardedRef,
// rerenderOnPropsChange,
extensions: RichTextEditorExtensions(fileHandler.upload, dragDropEnabled, setHideDragHandleFunction),
extensions: RichTextEditorExtensions({
uploadFile: fileHandler.upload,
dragDropEnabled,
setHideDragHandle: setHideDragHandleFunction,
}),
mentionHandler,
placeholder,
});
const editorContainerClassName = getEditorClassNames({

View File

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

View File

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

View File

@ -477,6 +477,10 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
}}
ref={editorRef}
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"
integrity sha512-cEoZBJrsQn69FPpUMePXG/ltGXtqKISgypj70PEHXt5meKDjpmMVSY4/8cXvFYEYsI9GvIwyAK0OrfAHiSoROA==
"@tiptap/extension-placeholder@^2.1.13":
version "2.1.13"
resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.1.13.tgz#b735591f719b9fe89c90dcc6327d2ef2851be510"
integrity sha512-vIY7y7UbqsrAW/y8bDE9eRenbQEU16kNHB5Wri8RU1YiUZpkPgdXP/pLqyjIIq95SwP/vdTIHjHoQ77VLRl1hA==
"@tiptap/extension-placeholder@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.3.0.tgz#db1f4375c8365c491211457c6d13f6efa486cd3a"
integrity sha512-1BOyxVLzyUYf6yOOeJ8CfpP6DSCS4L6HjBZqj6WP1z1NyBV8RAfhf3UuLNcimfSWAETXFR3g0ZbaxxWffI1cEg==
"@tiptap/extension-strike@^2.1.13":
version "2.1.13"
@ -7854,16 +7854,8 @@ streamx@^2.15.0:
fast-fifo "^1.1.0"
queue-tick "^1.0.1"
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
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:
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
name string-width-cjs
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -7939,14 +7931,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
"strip-ansi-cjs@npm: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:
"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==