Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ type Props = {
export const BubbleMenuColorSelector: FC<Props> = (props) => {
const { editor, isOpen, setIsOpen } = props;

const activeTextColor = COLORS_LIST.find((c) => editor.getAttributes("textStyle").color === c.key);
const activeBackgroundColor = COLORS_LIST.find((c) => editor.getAttributes("textStyle").backgroundColor === c.key);
const activeTextColor = COLORS_LIST.find((c) => TextColorItem(editor).isActive(c.key));
const activeBackgroundColor = COLORS_LIST.find((c) => BackgroundColorItem(editor).isActive(c.key));

return (
<div className="relative h-full">
Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/core/components/menus/menu-items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,15 +211,15 @@ export const ImageItem = (editor: Editor) =>
export const TextColorItem = (editor: Editor): EditorMenuItem => ({
key: "text-color",
name: "Color",
isActive: (color) => editor.getAttributes("textStyle").color === color,
isActive: (color) => editor.isActive("customColor", { color }),
command: (color: string) => toggleTextColor(color, editor),
icon: Palette,
});

export const BackgroundColorItem = (editor: Editor): EditorMenuItem => ({
key: "background-color",
name: "Background color",
isActive: (color) => editor.getAttributes("textStyle").backgroundColor === color,
isActive: (color) => editor.isActive("customColor", { backgroundColor: color }),
command: (color: string) => toggleBackgroundColor(color, editor),
icon: Palette,
});
Expand Down
6 changes: 2 additions & 4 deletions packages/editor/src/core/extensions/core-without-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import { IssueWidgetWithoutProps } from "./issue-embed/issue-embed-without-props
import { CustomMentionWithoutProps } from "./mentions/mentions-without-props";
import { CustomQuoteExtension } from "./quote";
import { TableHeader, TableCell, TableRow, Table } from "./table";
import { CustomTextColorExtension } from "./custom-text-color";
import { CustomBackgroundColorExtension } from "./custom-background-color";
import { CustomColorExtension } from "./custom-color";

export const CoreEditorExtensionsWithoutProps = [
StarterKit.configure({
Expand Down Expand Up @@ -85,8 +84,7 @@ export const CoreEditorExtensionsWithoutProps = [
TableCell,
TableRow,
CustomMentionWithoutProps(),
CustomTextColorExtension,
CustomBackgroundColorExtension,
CustomColorExtension,
];

export const DocumentEditorExtensionsWithoutProps = [IssueWidgetWithoutProps()];
78 changes: 0 additions & 78 deletions packages/editor/src/core/extensions/custom-background-color.ts

This file was deleted.

133 changes: 133 additions & 0 deletions packages/editor/src/core/extensions/custom-color.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Mark, mergeAttributes } from "@tiptap/core";
// constants
import { COLORS_LIST } from "@/constants/common";

declare module "@tiptap/core" {
interface Commands<ReturnType> {
color: {
/**
* Set the text color
* @param {string} color The color to set
* @example editor.commands.setTextColor('red')
*/
setTextColor: (color: string) => ReturnType;

/**
* Unset the text color
* @example editor.commands.unsetTextColor()
*/
unsetTextColor: () => ReturnType;
/**
* Set the background color
* @param {string} backgroundColor The color to set
* @example editor.commands.setBackgroundColor('red')
*/
setBackgroundColor: (backgroundColor: string) => ReturnType;

/**
* Unset the background color
* @example editor.commands.unsetBackgroundColorColor()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in unsetBackgroundColor example.

There's an extra "Color" in the example usage of unsetBackgroundColor.

Apply this diff to correct the typo:

 /**
  * Unset the background color
- * @example editor.commands.unsetBackgroundColorColor()
+ * @example editor.commands.unsetBackgroundColor()
  */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* @example editor.commands.unsetBackgroundColorColor()
* @example editor.commands.unsetBackgroundColor()

*/
unsetBackgroundColor: () => ReturnType;
};
}
Comment on lines +5 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Reconsider command grouping under color.

Grouping the commands under color might lead to confusion or conflicts with existing commands. It's more intuitive to place the commands directly under Commands<ReturnType> or use a more specific group name like customColor.

Apply this diff to adjust the command grouping:

 declare module "@tiptap/core" {
   interface Commands<ReturnType> {
-    color: {
+    customColor: {
       /**
        * Set the text color
        * @param {string} color The color to set
        * @example editor.commands.setTextColor('red')
        */
       setTextColor: (color: string) => ReturnType;

       /**
        * Unset the text color
        * @example editor.commands.unsetTextColor()
        */
       unsetTextColor: () => ReturnType;

       /**
        * Set the background color
        * @param {string} backgroundColor The color to set
        * @example editor.commands.setBackgroundColor('red')
        */
       setBackgroundColor: (backgroundColor: string) => ReturnType;

       /**
        * Unset the background color
        * @example editor.commands.unsetBackgroundColor()
        */
       unsetBackgroundColor: () => ReturnType;
     };
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
declare module "@tiptap/core" {
interface Commands<ReturnType> {
color: {
/**
* Set the text color
* @param {string} color The color to set
* @example editor.commands.setTextColor('red')
*/
setTextColor: (color: string) => ReturnType;
/**
* Unset the text color
* @example editor.commands.unsetTextColor()
*/
unsetTextColor: () => ReturnType;
/**
* Set the background color
* @param {string} backgroundColor The color to set
* @example editor.commands.setBackgroundColor('red')
*/
setBackgroundColor: (backgroundColor: string) => ReturnType;
/**
* Unset the background color
* @example editor.commands.unsetBackgroundColorColor()
*/
unsetBackgroundColor: () => ReturnType;
};
}
declare module "@tiptap/core" {
interface Commands<ReturnType> {
customColor: {
/**
* Set the text color
* @param {string} color The color to set
* @example editor.commands.setTextColor('red')
*/
setTextColor: (color: string) => ReturnType;
/**
* Unset the text color
* @example editor.commands.unsetTextColor()
*/
unsetTextColor: () => ReturnType;
/**
* Set the background color
* @param {string} backgroundColor The color to set
* @example editor.commands.setBackgroundColor('red')
*/
setBackgroundColor: (backgroundColor: string) => ReturnType;
/**
* Unset the background color
* @example editor.commands.unsetBackgroundColor()
*/
unsetBackgroundColor: () => ReturnType;
};
}

}

export const CustomColorExtension = Mark.create({
name: "customColor",

addOptions() {
return {
HTMLAttributes: {},
};
},

addAttributes() {
return {
color: {
default: null,
parseHTML: (element: HTMLElement) => element.getAttribute("data-text-color"),
renderHTML: (attributes: { color: string }) => {
const { color } = attributes;
if (!color) {
return {};
}

let elementAttributes: Record<string, string> = {
"data-text-color": color,
};

if (!COLORS_LIST.find((c) => c.key === color)) {
elementAttributes = {
...elementAttributes,
style: `color: ${color}`,
};
}

return elementAttributes;
},
},
backgroundColor: {
default: null,
parseHTML: (element: HTMLElement) => element.getAttribute("data-background-color"),
renderHTML: (attributes: { backgroundColor: string }) => {
const { backgroundColor } = attributes;
if (!backgroundColor) {
return {};
}

let elementAttributes: Record<string, string> = {
"data-background-color": backgroundColor,
};

if (!COLORS_LIST.find((c) => c.key === backgroundColor)) {
elementAttributes = {
...elementAttributes,
style: `background-color: ${backgroundColor}`,
};
}

return elementAttributes;
},
},
};
},

parseHTML() {
return [
{
tag: "span",
getAttrs: (node) => node.getAttribute("data-text-color") && null,
},
{
tag: "span",
getAttrs: (node) => node.getAttribute("data-background-color") && null,
},
];
},

renderHTML({ HTMLAttributes }) {
return ["span", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
},

addCommands() {
return {
setTextColor:
(color: string) =>
({ chain }) =>
chain().setMark(this.name, { color }).run(),
unsetTextColor:
() =>
({ chain }) =>
chain().setMark(this.name, { color: null }).run(),
setBackgroundColor:
(backgroundColor: string) =>
({ chain }) =>
chain().setMark(this.name, { backgroundColor }).run(),
unsetBackgroundColor:
() =>
({ chain }) =>
chain().setMark(this.name, { backgroundColor: null }).run(),
};
Comment on lines +115 to +131
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add validation for color inputs in commands.

The setTextColor and setBackgroundColor commands accept any string, which might lead to invalid CSS values or security issues like CSS injection.

Consider adding validation to ensure that:

  • The color values are valid CSS color strings.
  • Or the color values are within the predefined COLORS_LIST.

This improves robustness and security.

},
});
78 changes: 0 additions & 78 deletions packages/editor/src/core/extensions/custom-text-color.ts

This file was deleted.

6 changes: 2 additions & 4 deletions packages/editor/src/core/extensions/extensions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ import StarterKit from "@tiptap/starter-kit";
import { Markdown } from "tiptap-markdown";
// extensions
import {
CustomBackgroundColorExtension,
CustomCodeBlockExtension,
CustomCodeInlineExtension,
CustomCodeMarkPlugin,
CustomColorExtension,
CustomHorizontalRule,
CustomImageExtension,
CustomKeymap,
CustomLinkExtension,
CustomMention,
CustomQuoteExtension,
CustomTextColorExtension,
CustomTypographyExtension,
DropHandlerExtension,
ImageExtension,
Expand Down Expand Up @@ -155,7 +154,6 @@ export const CoreEditorExtensions = (args: TArguments) => {
includeChildren: true,
}),
CharacterCount,
CustomTextColorExtension,
CustomBackgroundColorExtension,
CustomColorExtension,
];
};
3 changes: 1 addition & 2 deletions packages/editor/src/core/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ export * from "./slash-commands";
export * from "./table";
export * from "./typography";
export * from "./core-without-props";
export * from "./custom-background-color";
export * from "./custom-code-inline";
export * from "./custom-text-color";
export * from "./custom-color";
export * from "./drop";
export * from "./enter-key-extension";
export * from "./extensions";
Expand Down
6 changes: 2 additions & 4 deletions packages/editor/src/core/extensions/read-only-extensions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ import {
CustomMention,
HeadingListExtension,
CustomReadOnlyImageExtension,
CustomTextColorExtension,
CustomBackgroundColorExtension,
CustomColorExtension,
} from "@/extensions";
// helpers
import { isValidHttpUrl } from "@/helpers/common";
Expand Down Expand Up @@ -123,8 +122,7 @@ export const CoreReadOnlyEditorExtensions = (props: Props) => {
readonly: true,
}),
CharacterCount,
CustomTextColorExtension,
CustomBackgroundColorExtension,
CustomColorExtension,
HeadingListExtension,
];
};