[WEB-3489] improvement: add support to disable extensions in rich and lite text editor (#6721)

* [WEB-3489] improvement: add support to disable extensions in rich text editor

* improvements: disabled extensions prop for all editor components
This commit is contained in:
Prateek Shourya 2025-03-07 16:34:07 +05:30 committed by GitHub
parent a77fe7aa90
commit a953013f70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 58 additions and 40 deletions

View file

@ -3,3 +3,5 @@ export type PartialDeep<K> = {
}; };
export type CompleteOrEmpty<T> = T | Record<string, never>; export type CompleteOrEmpty<T> = T | Record<string, never>;
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

View file

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
// editor // plane imports
import { EditorRefApi, ILiteTextEditor, LiteTextEditorWithRef, TFileHandler } from "@plane/editor"; import { EditorRefApi, ILiteTextEditor, LiteTextEditorWithRef, TFileHandler } from "@plane/editor";
import { MakeOptional } from "@plane/types";
// components // components
import { EditorMentionsRoot, IssueCommentToolbar } from "@/components/editor"; import { EditorMentionsRoot, IssueCommentToolbar } from "@/components/editor";
// helpers // helpers
@ -9,7 +10,7 @@ import { getEditorFileHandlers } from "@/helpers/editor.helper";
import { isCommentEmpty } from "@/helpers/string.helper"; import { isCommentEmpty } from "@/helpers/string.helper";
interface LiteTextEditorWrapperProps interface LiteTextEditorWrapperProps
extends Omit<ILiteTextEditor, "disabledExtensions" | "fileHandler" | "mentionHandler"> { extends MakeOptional<Omit<ILiteTextEditor, "fileHandler" | "mentionHandler">, "disabledExtensions"> {
anchor: string; anchor: string;
workspaceId: string; workspaceId: string;
isSubmitting?: boolean; isSubmitting?: boolean;
@ -25,6 +26,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
isSubmitting = false, isSubmitting = false,
showSubmitButton = true, showSubmitButton = true,
uploadFile, uploadFile,
disabledExtensions,
...rest ...rest
} = props; } = props;
function isMutableRefObject<T>(ref: React.ForwardedRef<T>): ref is React.MutableRefObject<T | null> { function isMutableRefObject<T>(ref: React.ForwardedRef<T>): ref is React.MutableRefObject<T | null> {
@ -38,7 +40,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
<div className="border border-custom-border-200 rounded p-3 space-y-3"> <div className="border border-custom-border-200 rounded p-3 space-y-3">
<LiteTextEditorWithRef <LiteTextEditorWithRef
ref={ref} ref={ref}
disabledExtensions={[]} disabledExtensions={disabledExtensions ?? []}
fileHandler={getEditorFileHandlers({ fileHandler={getEditorFileHandlers({
anchor, anchor,
uploadFile, uploadFile,

View file

@ -1,25 +1,26 @@
import React from "react"; import React from "react";
// editor // plane imports
import { EditorReadOnlyRefApi, ILiteTextReadOnlyEditor, LiteTextReadOnlyEditorWithRef } from "@plane/editor"; import { EditorReadOnlyRefApi, ILiteTextReadOnlyEditor, LiteTextReadOnlyEditorWithRef } from "@plane/editor";
import { MakeOptional } from "@plane/types";
// components // components
import { EditorMentionsRoot } from "@/components/editor"; import { EditorMentionsRoot } from "@/components/editor";
// helpers // helpers
import { cn } from "@/helpers/common.helper"; import { cn } from "@/helpers/common.helper";
import { getReadOnlyEditorFileHandlers } from "@/helpers/editor.helper"; import { getReadOnlyEditorFileHandlers } from "@/helpers/editor.helper";
type LiteTextReadOnlyEditorWrapperProps = Omit< type LiteTextReadOnlyEditorWrapperProps = MakeOptional<
ILiteTextReadOnlyEditor, Omit<ILiteTextReadOnlyEditor, "fileHandler" | "mentionHandler">,
"disabledExtensions" | "fileHandler" | "mentionHandler" "disabledExtensions"
> & { > & {
anchor: string; anchor: string;
workspaceId: string; workspaceId: string;
}; };
export const LiteTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, LiteTextReadOnlyEditorWrapperProps>( export const LiteTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, LiteTextReadOnlyEditorWrapperProps>(
({ anchor, workspaceId, ...props }, ref) => ( ({ anchor, workspaceId, disabledExtensions, ...props }, ref) => (
<LiteTextReadOnlyEditorWithRef <LiteTextReadOnlyEditorWithRef
ref={ref} ref={ref}
disabledExtensions={[]} disabledExtensions={disabledExtensions ?? []}
fileHandler={getReadOnlyEditorFileHandlers({ fileHandler={getReadOnlyEditorFileHandlers({
anchor, anchor,
workspaceId, workspaceId,

View file

@ -1,20 +1,21 @@
import React, { forwardRef } from "react"; import React, { forwardRef } from "react";
// editor // plane imports
import { EditorRefApi, IRichTextEditor, RichTextEditorWithRef, TFileHandler } from "@plane/editor"; import { EditorRefApi, IRichTextEditor, RichTextEditorWithRef, TFileHandler } from "@plane/editor";
import { MakeOptional } from "@plane/types";
// components // components
import { EditorMentionsRoot } from "@/components/editor"; import { EditorMentionsRoot } from "@/components/editor";
// helpers // helpers
import { getEditorFileHandlers } from "@/helpers/editor.helper"; import { getEditorFileHandlers } from "@/helpers/editor.helper";
interface RichTextEditorWrapperProps interface RichTextEditorWrapperProps
extends Omit<IRichTextEditor, "disabledExtensions" | "fileHandler" | "mentionHandler"> { extends MakeOptional<Omit<IRichTextEditor, "fileHandler" | "mentionHandler">, "disabledExtensions"> {
anchor: string; anchor: string;
uploadFile: TFileHandler["upload"]; uploadFile: TFileHandler["upload"];
workspaceId: string; workspaceId: string;
} }
export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProps>((props, ref) => { export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProps>((props, ref) => {
const { anchor, containerClassName, uploadFile, workspaceId, ...rest } = props; const { anchor, containerClassName, uploadFile, workspaceId, disabledExtensions, ...rest } = props;
return ( return (
<RichTextEditorWithRef <RichTextEditorWithRef
@ -22,7 +23,7 @@ export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProp
renderComponent: (props) => <EditorMentionsRoot {...props} />, renderComponent: (props) => <EditorMentionsRoot {...props} />,
}} }}
ref={ref} ref={ref}
disabledExtensions={[]} disabledExtensions={disabledExtensions ?? []}
fileHandler={getEditorFileHandlers({ fileHandler={getEditorFileHandlers({
anchor, anchor,
uploadFile, uploadFile,

View file

@ -1,25 +1,26 @@
import React from "react"; import React from "react";
// editor // plane imports
import { EditorReadOnlyRefApi, IRichTextReadOnlyEditor, RichTextReadOnlyEditorWithRef } from "@plane/editor"; import { EditorReadOnlyRefApi, IRichTextReadOnlyEditor, RichTextReadOnlyEditorWithRef } from "@plane/editor";
import { MakeOptional } from "@plane/types";
// components // components
import { EditorMentionsRoot } from "@/components/editor"; import { EditorMentionsRoot } from "@/components/editor";
// helpers // helpers
import { cn } from "@/helpers/common.helper"; import { cn } from "@/helpers/common.helper";
import { getReadOnlyEditorFileHandlers } from "@/helpers/editor.helper"; import { getReadOnlyEditorFileHandlers } from "@/helpers/editor.helper";
type RichTextReadOnlyEditorWrapperProps = Omit< type RichTextReadOnlyEditorWrapperProps = MakeOptional<
IRichTextReadOnlyEditor, Omit<IRichTextReadOnlyEditor, "fileHandler" | "mentionHandler">,
"disabledExtensions" | "fileHandler" | "mentionHandler" "disabledExtensions"
> & { > & {
anchor: string; anchor: string;
workspaceId: string; workspaceId: string;
}; };
export const RichTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, RichTextReadOnlyEditorWrapperProps>( export const RichTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, RichTextReadOnlyEditorWrapperProps>(
({ anchor, workspaceId, ...props }, ref) => ( ({ anchor, workspaceId, disabledExtensions, ...props }, ref) => (
<RichTextReadOnlyEditorWithRef <RichTextReadOnlyEditorWithRef
ref={ref} ref={ref}
disabledExtensions={[]} disabledExtensions={disabledExtensions ?? []}
fileHandler={getReadOnlyEditorFileHandlers({ fileHandler={getReadOnlyEditorFileHandlers({
anchor, anchor,
workspaceId, workspaceId,

View file

@ -6,6 +6,7 @@ import { EditorRefApi, ILiteTextEditor, LiteTextEditorWithRef, TFileHandler } fr
// i18n // i18n
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
// components // components
import { MakeOptional } from "@plane/types";
import { EditorMentionsRoot, IssueCommentToolbar } from "@/components/editor"; import { EditorMentionsRoot, IssueCommentToolbar } from "@/components/editor";
// helpers // helpers
import { cn } from "@/helpers/common.helper"; import { cn } from "@/helpers/common.helper";
@ -19,7 +20,7 @@ import { WorkspaceService } from "@/plane-web/services";
const workspaceService = new WorkspaceService(); const workspaceService = new WorkspaceService();
interface LiteTextEditorWrapperProps interface LiteTextEditorWrapperProps
extends Omit<ILiteTextEditor, "disabledExtensions" | "fileHandler" | "mentionHandler"> { extends MakeOptional<Omit<ILiteTextEditor, "fileHandler" | "mentionHandler">, "disabledExtensions"> {
workspaceSlug: string; workspaceSlug: string;
workspaceId: string; workspaceId: string;
projectId: string; projectId: string;
@ -49,6 +50,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
showToolbarInitially = true, showToolbarInitially = true,
placeholder = t("issue.comments.placeholder"), placeholder = t("issue.comments.placeholder"),
uploadFile, uploadFile,
disabledExtensions: additionalDisabledExtensions,
...rest ...rest
} = props; } = props;
// states // states
@ -81,7 +83,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
> >
<LiteTextEditorWithRef <LiteTextEditorWithRef
ref={ref} ref={ref}
disabledExtensions={disabledExtensions} disabledExtensions={[...disabledExtensions, ...(additionalDisabledExtensions ?? [])]}
fileHandler={getEditorFileHandlers({ fileHandler={getEditorFileHandlers({
projectId, projectId,
uploadFile, uploadFile,

View file

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
// plane editor // plane imports
import { EditorReadOnlyRefApi, ILiteTextReadOnlyEditor, LiteTextReadOnlyEditorWithRef } from "@plane/editor"; import { EditorReadOnlyRefApi, ILiteTextReadOnlyEditor, LiteTextReadOnlyEditorWithRef } from "@plane/editor";
import { MakeOptional } from "@plane/types";
// components // components
import { EditorMentionsRoot } from "@/components/editor"; import { EditorMentionsRoot } from "@/components/editor";
// helpers // helpers
@ -10,9 +11,9 @@ import { useEditorConfig } from "@/hooks/editor";
// plane web hooks // plane web hooks
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
type LiteTextReadOnlyEditorWrapperProps = Omit< type LiteTextReadOnlyEditorWrapperProps = MakeOptional<
ILiteTextReadOnlyEditor, Omit<ILiteTextReadOnlyEditor, "fileHandler" | "mentionHandler">,
"disabledExtensions" | "fileHandler" | "mentionHandler" "disabledExtensions"
> & { > & {
workspaceId: string; workspaceId: string;
workspaceSlug: string; workspaceSlug: string;
@ -20,7 +21,7 @@ type LiteTextReadOnlyEditorWrapperProps = Omit<
}; };
export const LiteTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, LiteTextReadOnlyEditorWrapperProps>( export const LiteTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, LiteTextReadOnlyEditorWrapperProps>(
({ workspaceId, workspaceSlug, projectId, ...props }, ref) => { ({ workspaceId, workspaceSlug, projectId, disabledExtensions: additionalDisabledExtensions, ...props }, ref) => {
// editor flaggings // editor flaggings
const { liteTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString()); const { liteTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString());
// editor config // editor config
@ -29,7 +30,7 @@ export const LiteTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, Lit
return ( return (
<LiteTextReadOnlyEditorWithRef <LiteTextReadOnlyEditorWithRef
ref={ref} ref={ref}
disabledExtensions={disabledExtensions} disabledExtensions={[...disabledExtensions, ...(additionalDisabledExtensions ?? [])]}
fileHandler={getReadOnlyEditorFileHandlers({ fileHandler={getReadOnlyEditorFileHandlers({
projectId, projectId,
workspaceId, workspaceId,

View file

@ -1,8 +1,7 @@
import React, { forwardRef } from "react"; import React, { forwardRef } from "react";
// editor // plane imports
import { EditorRefApi, IRichTextEditor, RichTextEditorWithRef, TFileHandler } from "@plane/editor"; import { EditorRefApi, IRichTextEditor, RichTextEditorWithRef, TFileHandler } from "@plane/editor";
// plane types import { MakeOptional, TSearchEntityRequestPayload, TSearchResponse } from "@plane/types";
import { TSearchEntityRequestPayload, TSearchResponse } from "@plane/types";
// components // components
import { EditorMentionsRoot } from "@/components/editor"; import { EditorMentionsRoot } from "@/components/editor";
// helpers // helpers
@ -13,7 +12,7 @@ import { useEditorConfig, useEditorMention } from "@/hooks/editor";
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
interface RichTextEditorWrapperProps interface RichTextEditorWrapperProps
extends Omit<IRichTextEditor, "disabledExtensions" | "fileHandler" | "mentionHandler"> { extends MakeOptional<Omit<IRichTextEditor, "fileHandler" | "mentionHandler">, "disabledExtensions"> {
searchMentionCallback: (payload: TSearchEntityRequestPayload) => Promise<TSearchResponse>; searchMentionCallback: (payload: TSearchEntityRequestPayload) => Promise<TSearchResponse>;
workspaceSlug: string; workspaceSlug: string;
workspaceId: string; workspaceId: string;
@ -22,8 +21,16 @@ interface RichTextEditorWrapperProps
} }
export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProps>((props, ref) => { export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProps>((props, ref) => {
const { containerClassName, workspaceSlug, workspaceId, projectId, searchMentionCallback, uploadFile, ...rest } = const {
props; containerClassName,
workspaceSlug,
workspaceId,
projectId,
searchMentionCallback,
uploadFile,
disabledExtensions: additionalDisabledExtensions,
...rest
} = props;
// editor flaggings // editor flaggings
const { richTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString()); const { richTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString());
// use editor mention // use editor mention
@ -36,7 +43,7 @@ export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProp
return ( return (
<RichTextEditorWithRef <RichTextEditorWithRef
ref={ref} ref={ref}
disabledExtensions={disabledExtensions} disabledExtensions={[...disabledExtensions, ...(additionalDisabledExtensions ?? [])]}
fileHandler={getEditorFileHandlers({ fileHandler={getEditorFileHandlers({
projectId, projectId,
uploadFile, uploadFile,

View file

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
// editor // plane imports
import { EditorReadOnlyRefApi, IRichTextReadOnlyEditor, RichTextReadOnlyEditorWithRef } from "@plane/editor"; import { EditorReadOnlyRefApi, IRichTextReadOnlyEditor, RichTextReadOnlyEditorWithRef } from "@plane/editor";
import { MakeOptional } from "@plane/types";
// components // components
import { EditorMentionsRoot } from "@/components/editor"; import { EditorMentionsRoot } from "@/components/editor";
// helpers // helpers
@ -10,9 +11,9 @@ import { useEditorConfig } from "@/hooks/editor";
// plane web hooks // plane web hooks
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
type RichTextReadOnlyEditorWrapperProps = Omit< type RichTextReadOnlyEditorWrapperProps = MakeOptional<
IRichTextReadOnlyEditor, Omit<IRichTextReadOnlyEditor, "fileHandler" | "mentionHandler">,
"disabledExtensions" | "fileHandler" | "mentionHandler" "disabledExtensions"
> & { > & {
workspaceId: string; workspaceId: string;
workspaceSlug: string; workspaceSlug: string;
@ -20,7 +21,7 @@ type RichTextReadOnlyEditorWrapperProps = Omit<
}; };
export const RichTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, RichTextReadOnlyEditorWrapperProps>( export const RichTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, RichTextReadOnlyEditorWrapperProps>(
({ workspaceId, workspaceSlug, projectId, ...props }, ref) => { ({ workspaceId, workspaceSlug, projectId, disabledExtensions: additionalDisabledExtensions, ...props }, ref) => {
// editor flaggings // editor flaggings
const { richTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString()); const { richTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString());
// editor config // editor config
@ -29,7 +30,7 @@ export const RichTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, Ric
return ( return (
<RichTextReadOnlyEditorWithRef <RichTextReadOnlyEditorWithRef
ref={ref} ref={ref}
disabledExtensions={disabledExtensions} disabledExtensions={[...disabledExtensions, ...(additionalDisabledExtensions ?? [])]}
fileHandler={getReadOnlyEditorFileHandlers({ fileHandler={getReadOnlyEditorFileHandlers({
projectId, projectId,
workspaceId, workspaceId,