[WEB-2494] dev: custom text color and background color extensions (#5786)

* dev: created custom text color and background color extensions

* chore: update slash commands icon style

* chore: update constants

* chore: update variables css file selectors
This commit is contained in:
Aaryan Khandelwal 2024-10-11 19:11:39 +05:30 committed by GitHub
parent 74695e561a
commit e7065af358
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 604 additions and 393 deletions

View file

@ -42,8 +42,6 @@
"@tiptap/extension-blockquote": "^2.1.13",
"@tiptap/extension-character-count": "^2.6.5",
"@tiptap/extension-collaboration": "^2.3.2",
"@tiptap/extension-color": "^2.7.1",
"@tiptap/extension-highlight": "^2.7.1",
"@tiptap/extension-image": "^2.1.13",
"@tiptap/extension-list-item": "^2.1.13",
"@tiptap/extension-mention": "^2.1.13",

View file

@ -16,12 +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.textColor);
const activeBackgroundColor = COLORS_LIST.find((c) =>
editor.isActive("highlight", {
color: c.backgroundColor,
})
);
const activeTextColor = COLORS_LIST.find((c) => editor.getAttributes("textStyle").color === c.key);
const activeBackgroundColor = COLORS_LIST.find((c) => editor.getAttributes("textStyle").backgroundColor === c.key);
return (
<div className="relative h-full">
@ -41,25 +37,17 @@ export const BubbleMenuColorSelector: FC<Props> = (props) => {
"bg-custom-background-100": !activeBackgroundColor,
}
)}
style={
activeBackgroundColor
? {
backgroundColor: activeBackgroundColor.backgroundColor,
}
: {}
}
style={{
backgroundColor: activeBackgroundColor ? activeBackgroundColor.backgroundColor : "transparent",
}}
>
<ALargeSmall
className={cn("size-3.5", {
"text-custom-text-100": !activeTextColor,
})}
style={
activeTextColor
? {
color: activeTextColor.textColor,
}
: {}
}
style={{
color: activeTextColor ? activeTextColor.textColor : "inherit",
}}
/>
</span>
</button>
@ -70,13 +58,13 @@ export const BubbleMenuColorSelector: FC<Props> = (props) => {
<div className="flex items-center gap-2">
{COLORS_LIST.map((color) => (
<button
key={color.textColor}
key={color.key}
type="button"
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
style={{
backgroundColor: color.textColor,
}}
onClick={() => TextColorItem(editor).command(color.textColor)}
onClick={() => TextColorItem(editor).command(color.key)}
/>
))}
<button
@ -93,13 +81,13 @@ export const BubbleMenuColorSelector: FC<Props> = (props) => {
<div className="flex items-center gap-2">
{COLORS_LIST.map((color) => (
<button
key={color.backgroundColor}
key={color.key}
type="button"
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
style={{
backgroundColor: color.backgroundColor,
}}
onClick={() => BackgroundColorItem(editor).command(color.backgroundColor)}
onClick={() => BackgroundColorItem(editor).command(color.key)}
/>
))}
<button

View file

@ -219,7 +219,7 @@ export const TextColorItem = (editor: Editor): EditorMenuItem => ({
export const BackgroundColorItem = (editor: Editor): EditorMenuItem => ({
key: "background-color",
name: "Background color",
isActive: (color) => editor.isActive("highlight", { color }),
isActive: (color) => editor.getAttributes("textStyle").backgroundColor === color,
command: (color: string) => toggleBackgroundColor(color, editor),
icon: Palette,
});

View file

@ -1,51 +1,61 @@
export const COLORS_LIST: {
backgroundColor: string;
textColor: string;
key: string;
label: string;
textColor: string;
backgroundColor: string;
}[] = [
// {
// backgroundColor: "#1c202426",
// textColor: "#1c2024",
// label: "Black",
// },
{
backgroundColor: "#5c5e6326",
textColor: "#5c5e63",
key: "gray",
label: "Gray",
textColor: "var(--editor-colors-gray-text)",
backgroundColor: "var(--editor-colors-gray-background)",
},
{
backgroundColor: "#ff5b5926",
textColor: "#ff5b59",
key: "peach",
label: "Peach",
textColor: "var(--editor-colors-peach-text)",
backgroundColor: "var(--editor-colors-peach-background)",
},
{
backgroundColor: "#f6538526",
textColor: "#f65385",
key: "pink",
label: "Pink",
textColor: "var(--editor-colors-pink-text)",
backgroundColor: "var(--editor-colors-pink-background)",
},
{
backgroundColor: "#fd903826",
textColor: "#fd9038",
key: "orange",
label: "Orange",
textColor: "var(--editor-colors-orange-text)",
backgroundColor: "var(--editor-colors-orange-background)",
},
{
backgroundColor: "#0fc27b26",
textColor: "#0fc27b",
key: "green",
label: "Green",
textColor: "var(--editor-colors-green-text)",
backgroundColor: "var(--editor-colors-green-background)",
},
{
backgroundColor: "#17bee926",
textColor: "#17bee9",
key: "light-blue",
label: "Light blue",
textColor: "var(--editor-colors-light-blue-text)",
backgroundColor: "var(--editor-colors-light-blue-background)",
},
{
backgroundColor: "#266df026",
textColor: "#266df0",
key: "dark-blue",
label: "Dark blue",
textColor: "var(--editor-colors-dark-blue-text)",
backgroundColor: "var(--editor-colors-dark-blue-background)",
},
{
backgroundColor: "#9162f926",
textColor: "#9162f9",
key: "purple",
label: "Purple",
textColor: "var(--editor-colors-purple-text)",
backgroundColor: "var(--editor-colors-purple-background)",
},
// {
// key: "pink-blue-gradient",
// label: "Pink blue gradient",
// textColor: "var(--editor-colors-pink-blue-gradient-text)",
// backgroundColor: "var(--editor-colors-pink-blue-gradient-background)",
// },
];

View file

@ -1,5 +1,3 @@
import { Color } from "@tiptap/extension-color";
import Highlight from "@tiptap/extension-highlight";
import TaskItem from "@tiptap/extension-task-item";
import TaskList from "@tiptap/extension-task-list";
import TextStyle from "@tiptap/extension-text-style";
@ -18,6 +16,8 @@ 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";
export const CoreEditorExtensionsWithoutProps = [
StarterKit.configure({
@ -85,10 +85,8 @@ export const CoreEditorExtensionsWithoutProps = [
TableCell,
TableRow,
CustomMentionWithoutProps(),
Color,
Highlight.configure({
multicolor: true,
}),
CustomTextColorExtension,
CustomBackgroundColorExtension,
];
export const DocumentEditorExtensionsWithoutProps = [IssueWidgetWithoutProps()];

View file

@ -0,0 +1,78 @@
import { Extension } from "@tiptap/core";
// constants
import { COLORS_LIST } from "@/constants/common";
declare module "@tiptap/core" {
interface Commands<ReturnType> {
backgroundColor: {
/**
* Set the background color
* @param color The color to set
* @example editor.commands.setBackgroundColor('red')
*/
setBackgroundColor: (color: string) => ReturnType;
/**
* Unset the background color
* @example editor.commands.unsetBackgroundColor()
*/
unsetBackgroundColor: () => ReturnType;
};
}
}
export const CustomBackgroundColorExtension = Extension.create({
name: "customBackgroundColor",
addOptions() {
return {
types: ["textStyle"],
};
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
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;
},
},
},
},
];
},
addCommands() {
return {
setBackgroundColor:
(backgroundColor: string) =>
({ chain }) =>
chain().setMark("textStyle", { backgroundColor }).run(),
unsetBackgroundColor:
() =>
({ chain }) =>
chain().setMark("textStyle", { backgroundColor: null }).run(),
};
},
});

View file

@ -0,0 +1,78 @@
import { Extension } from "@tiptap/core";
// constants
import { COLORS_LIST } from "@/constants/common";
declare module "@tiptap/core" {
interface Commands<ReturnType> {
color: {
/**
* Set the text color
* @param color The color to set
* @example editor.commands.setColor('red')
*/
setTextColor: (color: string) => ReturnType;
/**
* Unset the text color
* @example editor.commands.unsetColor()
*/
unsetTextColor: () => ReturnType;
};
}
}
export const CustomTextColorExtension = Extension.create({
name: "customTextColor",
addOptions() {
return {
types: ["textStyle"],
};
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
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;
},
},
},
},
];
},
addCommands() {
return {
setTextColor:
(color: string) =>
({ chain }) =>
chain().setMark("textStyle", { color }).run(),
unsetTextColor:
() =>
({ chain }) =>
chain().setMark("textStyle", { color: null }).run(),
};
},
});

View file

@ -1,6 +1,4 @@
import CharacterCount from "@tiptap/extension-character-count";
import { Color } from "@tiptap/extension-color";
import Highlight from "@tiptap/extension-highlight";
import Placeholder from "@tiptap/extension-placeholder";
import TaskItem from "@tiptap/extension-task-item";
import TaskList from "@tiptap/extension-task-list";
@ -10,6 +8,7 @@ import StarterKit from "@tiptap/starter-kit";
import { Markdown } from "tiptap-markdown";
// extensions
import {
CustomBackgroundColorExtension,
CustomCodeBlockExtension,
CustomCodeInlineExtension,
CustomCodeMarkPlugin,
@ -19,6 +18,7 @@ import {
CustomLinkExtension,
CustomMention,
CustomQuoteExtension,
CustomTextColorExtension,
CustomTypographyExtension,
DropHandlerExtension,
ImageExtension,
@ -168,8 +168,6 @@ export const CoreEditorExtensions = ({
includeChildren: true,
}),
CharacterCount,
Color,
Highlight.configure({
multicolor: true,
}),
CustomTextColorExtension,
CustomBackgroundColorExtension,
];

View file

@ -10,7 +10,9 @@ 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 "./drop";
export * from "./enter-key-extension";
export * from "./extensions";

View file

@ -1,6 +1,4 @@
import CharacterCount from "@tiptap/extension-character-count";
import { Color } from "@tiptap/extension-color";
import Highlight from "@tiptap/extension-highlight";
import TaskItem from "@tiptap/extension-task-item";
import TaskList from "@tiptap/extension-task-list";
import TextStyle from "@tiptap/extension-text-style";
@ -23,6 +21,8 @@ import {
CustomMention,
HeadingListExtension,
CustomReadOnlyImageExtension,
CustomTextColorExtension,
CustomBackgroundColorExtension,
} from "@/extensions";
// helpers
import { isValidHttpUrl } from "@/helpers/common";
@ -111,9 +111,7 @@ export const CoreReadOnlyEditorExtensions = (mentionConfig: {
readonly: true,
}),
CharacterCount,
Color,
Highlight.configure({
multicolor: true,
}),
CustomTextColorExtension,
CustomBackgroundColorExtension,
HeadingListExtension,
];

View file

@ -44,237 +44,237 @@ export type TSlashCommandSection = {
items: ISlashCommandItem[];
};
const SLASH_COMMAND_SECTIONS: TSlashCommandSection[] = [
{
key: "general",
items: [
export const getSlashCommandFilteredSections =
(additionalOptions?: ISlashCommandItem[]) =>
({ query }: { query: string }): TSlashCommandSection[] => {
const SLASH_COMMAND_SECTIONS: TSlashCommandSection[] = [
{
commandKey: "text",
key: "text",
title: "Text",
description: "Just start typing with plain text.",
searchTerms: ["p", "paragraph"],
icon: <CaseSensitive className="size-3.5" />,
command: ({ editor, range }: CommandProps) => {
if (range) {
editor.chain().focus().deleteRange(range).clearNodes().run();
}
editor.chain().focus().clearNodes().run();
},
key: "general",
items: [
{
commandKey: "text",
key: "text",
title: "Text",
description: "Just start typing with plain text.",
searchTerms: ["p", "paragraph"],
icon: <CaseSensitive className="size-3.5" />,
command: ({ editor, range }: CommandProps) => {
if (range) {
editor.chain().focus().deleteRange(range).clearNodes().run();
}
editor.chain().focus().clearNodes().run();
},
},
{
commandKey: "h1",
key: "h1",
title: "Heading 1",
description: "Big section heading.",
searchTerms: ["title", "big", "large"],
icon: <Heading1 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingOne(editor, range),
},
{
commandKey: "h2",
key: "h2",
title: "Heading 2",
description: "Medium section heading.",
searchTerms: ["subtitle", "medium"],
icon: <Heading2 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingTwo(editor, range),
},
{
commandKey: "h3",
key: "h3",
title: "Heading 3",
description: "Small section heading.",
searchTerms: ["subtitle", "small"],
icon: <Heading3 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingThree(editor, range),
},
{
commandKey: "h4",
key: "h4",
title: "Heading 4",
description: "Small section heading.",
searchTerms: ["subtitle", "small"],
icon: <Heading4 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingFour(editor, range),
},
{
commandKey: "h5",
key: "h5",
title: "Heading 5",
description: "Small section heading.",
searchTerms: ["subtitle", "small"],
icon: <Heading5 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingFive(editor, range),
},
{
commandKey: "h6",
key: "h6",
title: "Heading 6",
description: "Small section heading.",
searchTerms: ["subtitle", "small"],
icon: <Heading6 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingSix(editor, range),
},
{
commandKey: "to-do-list",
key: "to-do-list",
title: "To do",
description: "Track tasks with a to-do list.",
searchTerms: ["todo", "task", "list", "check", "checkbox"],
icon: <ListTodo className="size-3.5" />,
command: ({ editor, range }) => toggleTaskList(editor, range),
},
{
commandKey: "bulleted-list",
key: "bulleted-list",
title: "Bullet list",
description: "Create a simple bullet list.",
searchTerms: ["unordered", "point"],
icon: <List className="size-3.5" />,
command: ({ editor, range }) => toggleBulletList(editor, range),
},
{
commandKey: "numbered-list",
key: "numbered-list",
title: "Numbered list",
description: "Create a list with numbering.",
searchTerms: ["ordered"],
icon: <ListOrdered className="size-3.5" />,
command: ({ editor, range }) => toggleOrderedList(editor, range),
},
{
commandKey: "table",
key: "table",
title: "Table",
description: "Create a table",
searchTerms: ["table", "cell", "db", "data", "tabular"],
icon: <Table className="size-3.5" />,
command: ({ editor, range }) => insertTableCommand(editor, range),
},
{
commandKey: "quote",
key: "quote",
title: "Quote",
description: "Capture a quote.",
searchTerms: ["blockquote"],
icon: <Quote className="size-3.5" />,
command: ({ editor, range }) => toggleBlockquote(editor, range),
},
{
commandKey: "code",
key: "code",
title: "Code",
description: "Capture a code snippet.",
searchTerms: ["codeblock"],
icon: <Code2 className="size-3.5" />,
command: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleCodeBlock().run(),
},
{
commandKey: "image",
key: "image",
title: "Image",
icon: <ImageIcon className="size-3.5" />,
description: "Insert an image",
searchTerms: ["img", "photo", "picture", "media", "upload"],
command: ({ editor, range }: CommandProps) => insertImage({ editor, event: "insert", range }),
},
{
commandKey: "divider",
key: "divider",
title: "Divider",
description: "Visually divide blocks.",
searchTerms: ["line", "divider", "horizontal", "rule", "separate"],
icon: <MinusSquare className="size-3.5" />,
command: ({ editor, range }) => editor.chain().focus().deleteRange(range).setHorizontalRule().run(),
},
],
},
{
commandKey: "h1",
key: "h1",
title: "Heading 1",
description: "Big section heading.",
searchTerms: ["title", "big", "large"],
icon: <Heading1 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingOne(editor, range),
},
{
commandKey: "h2",
key: "h2",
title: "Heading 2",
description: "Medium section heading.",
searchTerms: ["subtitle", "medium"],
icon: <Heading2 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingTwo(editor, range),
},
{
commandKey: "h3",
key: "h3",
title: "Heading 3",
description: "Small section heading.",
searchTerms: ["subtitle", "small"],
icon: <Heading3 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingThree(editor, range),
},
{
commandKey: "h4",
key: "h4",
title: "Heading 4",
description: "Small section heading.",
searchTerms: ["subtitle", "small"],
icon: <Heading4 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingFour(editor, range),
},
{
commandKey: "h5",
key: "h5",
title: "Heading 5",
description: "Small section heading.",
searchTerms: ["subtitle", "small"],
icon: <Heading5 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingFive(editor, range),
},
{
commandKey: "h6",
key: "h6",
title: "Heading 6",
description: "Small section heading.",
searchTerms: ["subtitle", "small"],
icon: <Heading6 className="size-3.5" />,
command: ({ editor, range }) => toggleHeadingSix(editor, range),
},
{
commandKey: "to-do-list",
key: "to-do-list",
title: "To do",
description: "Track tasks with a to-do list.",
searchTerms: ["todo", "task", "list", "check", "checkbox"],
icon: <ListTodo className="size-3.5" />,
command: ({ editor, range }) => toggleTaskList(editor, range),
},
{
commandKey: "bulleted-list",
key: "bulleted-list",
title: "Bullet list",
description: "Create a simple bullet list.",
searchTerms: ["unordered", "point"],
icon: <List className="size-3.5" />,
command: ({ editor, range }) => toggleBulletList(editor, range),
},
{
commandKey: "numbered-list",
key: "numbered-list",
title: "Numbered list",
description: "Create a list with numbering.",
searchTerms: ["ordered"],
icon: <ListOrdered className="size-3.5" />,
command: ({ editor, range }) => toggleOrderedList(editor, range),
},
{
commandKey: "table",
key: "table",
title: "Table",
description: "Create a table",
searchTerms: ["table", "cell", "db", "data", "tabular"],
icon: <Table className="size-3.5" />,
command: ({ editor, range }) => insertTableCommand(editor, range),
},
{
commandKey: "quote",
key: "quote",
title: "Quote",
description: "Capture a quote.",
searchTerms: ["blockquote"],
icon: <Quote className="size-3.5" />,
command: ({ editor, range }) => toggleBlockquote(editor, range),
},
{
commandKey: "code",
key: "code",
title: "Code",
description: "Capture a code snippet.",
searchTerms: ["codeblock"],
icon: <Code2 className="size-3.5" />,
command: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleCodeBlock().run(),
},
{
commandKey: "image",
key: "image",
title: "Image",
icon: <ImageIcon className="size-3.5" />,
description: "Insert an image",
searchTerms: ["img", "photo", "picture", "media", "upload"],
command: ({ editor, range }: CommandProps) => insertImage({ editor, event: "insert", range }),
},
{
commandKey: "divider",
key: "divider",
title: "Divider",
description: "Visually divide blocks.",
searchTerms: ["line", "divider", "horizontal", "rule", "separate"],
icon: <MinusSquare className="size-3.5" />,
command: ({ editor, range }) => editor.chain().focus().deleteRange(range).setHorizontalRule().run(),
},
],
},
{
key: "text-color",
title: "Colors",
items: [
{
commandKey: "text-color",
key: "text-color-default",
title: "Default",
description: "Change text color",
searchTerms: ["color", "text", "default"],
icon: (
<ALargeSmall
className="size-3.5"
style={{
color: "rgba(var(--color-text-100))",
}}
/>
),
command: ({ editor, range }) => toggleTextColor(undefined, editor, range),
},
...COLORS_LIST.map(
(color) =>
({
key: "text-color",
title: "Colors",
items: [
{
commandKey: "text-color",
key: `text-color-${color.textColor}`,
title: color.label,
key: "text-color-default",
title: "Default",
description: "Change text color",
searchTerms: ["color", "text", color.label],
searchTerms: ["color", "text", "default"],
icon: (
<ALargeSmall
className="size-3.5"
style={{
color: color.textColor,
color: "rgba(var(--color-text-100))",
}}
/>
),
command: ({ editor, range }) => toggleTextColor(color.textColor, editor, range),
}) as ISlashCommandItem
),
],
},
{
key: "background-color",
title: "Background colors",
items: [
{
commandKey: "background-color",
key: "background-color-default",
title: "Default background",
description: "Change background color",
searchTerms: ["color", "bg", "background", "default"],
icon: <ALargeSmall className="size-3.5" />,
iconContainerStyle: {
borderRadius: "4px",
backgroundColor: "rgba(var(--color-background-100))",
border: "1px solid rgba(var(--color-border-300))",
},
command: ({ editor, range }) => toggleTextColor(undefined, editor, range),
command: ({ editor, range }) => toggleTextColor(undefined, editor, range),
},
...COLORS_LIST.map(
(color) =>
({
commandKey: "text-color",
key: `text-color-${color.key}`,
title: color.label,
description: "Change text color",
searchTerms: ["color", "text", color.label],
icon: (
<ALargeSmall
className="size-3.5"
style={{
color: color.textColor,
}}
/>
),
command: ({ editor, range }) => toggleTextColor(color.key, editor, range),
}) as ISlashCommandItem
),
],
},
...COLORS_LIST.map(
(color) =>
({
{
key: "background-color",
title: "Background colors",
items: [
{
commandKey: "background-color",
key: `background-color-${color.backgroundColor}`,
title: `${color.label} background`,
key: "background-color-default",
title: "Default background",
description: "Change background color",
searchTerms: ["color", "bg", "background", color.label],
searchTerms: ["color", "bg", "background", "default"],
icon: <ALargeSmall className="size-3.5" />,
iconContainerStyle: {
borderRadius: "4px",
backgroundColor: color.backgroundColor,
backgroundColor: "rgba(var(--color-background-100))",
border: "1px solid rgba(var(--color-border-300))",
},
command: ({ editor, range }) => toggleBackgroundColor(color.backgroundColor, editor, range),
}) as ISlashCommandItem
),
],
},
];
command: ({ editor, range }) => toggleTextColor(undefined, editor, range),
},
...COLORS_LIST.map(
(color) =>
({
commandKey: "background-color",
key: `background-color-${color.key}`,
title: color.label,
description: "Change background color",
searchTerms: ["color", "bg", "background", color.label],
icon: <ALargeSmall className="size-3.5" />,
iconContainerStyle: {
borderRadius: "4px",
backgroundColor: color.backgroundColor,
},
command: ({ editor, range }) => toggleBackgroundColor(color.key, editor, range),
}) as ISlashCommandItem
),
],
},
];
export const getSlashCommandFilteredSections =
(additionalOptions?: ISlashCommandItem[]) =>
({ query }: { query: string }): TSlashCommandSection[] => {
if (additionalOptions) {
additionalOptions.map((item) => SLASH_COMMAND_SECTIONS?.[0]?.items.push(item));
}
additionalOptions?.map((item) => {
SLASH_COMMAND_SECTIONS?.[0]?.items.push(item);
});
const filteredSlashSections = SLASH_COMMAND_SECTIONS.map((section) => ({
...section,

View file

@ -6,8 +6,6 @@ import { CommandMenuItem } from "./command-menu-item";
type Props = {
items: TSlashCommandSection[];
command: any;
editor: any;
range: any;
};
export const SlashCommandsMenu = (props: Props) => {
@ -22,7 +20,7 @@ export const SlashCommandsMenu = (props: Props) => {
const selectItem = useCallback(
(sectionIndex: number, itemIndex: number) => {
const item = sections[sectionIndex].items[itemIndex];
const item = sections[sectionIndex]?.items?.[itemIndex];
if (item) command(item);
},
[command, sections]

View file

@ -66,8 +66,6 @@ const renderItems = () => {
const tippyContainer =
document.querySelector(".active-editor") ?? document.querySelector('[id^="editor-container"]');
// @ts-expect-error Tippy overloads are messed up
popup = tippy("body", {
getReferenceClientRect: props.clientRect,
appendTo: tippyContainer,

View file

@ -157,39 +157,26 @@ export const setLinkEditor = (editor: Editor, url: string) => {
export const toggleTextColor = (color: string | undefined, editor: Editor, range?: Range) => {
if (color) {
if (range) editor.chain().focus().deleteRange(range).setColor(color).run();
else editor.chain().focus().setColor(color).run();
if (range) editor.chain().focus().deleteRange(range).setTextColor(color).run();
else editor.chain().focus().setTextColor(color).run();
} else {
if (range) editor.chain().focus().deleteRange(range).unsetColor().run();
else editor.chain().focus().unsetColor().run();
if (range) editor.chain().focus().deleteRange(range).unsetTextColor().run();
else editor.chain().focus().unsetTextColor().run();
}
};
export const toggleBackgroundColor = (color: string | undefined, editor: Editor, range?: Range) => {
if (color) {
if (range) {
editor
.chain()
.focus()
.deleteRange(range)
.setHighlight({
color,
})
.run();
editor.chain().focus().deleteRange(range).setBackgroundColor(color).run();
} else {
editor
.chain()
.focus()
.setHighlight({
color,
})
.run();
editor.chain().focus().setBackgroundColor(color).run();
}
} else {
if (range) {
editor.chain().focus().deleteRange(range).unsetHighlight().run();
editor.chain().focus().deleteRange(range).unsetBackgroundColor().run();
} else {
editor.chain().focus().unsetHighlight().run();
editor.chain().focus().unsetBackgroundColor().run();
}
}
};

View file

@ -1,5 +1,6 @@
// styles
// import "./styles/tailwind.css";
import "src/styles/variables.css";
import "src/styles/editor.css";
import "src/styles/table.css";
import "src/styles/github-dark.css";

View file

@ -1,61 +1,3 @@
.editor-container {
&.large-font {
--font-size-h1: 1.75rem;
--font-size-h2: 1.5rem;
--font-size-h3: 1.375rem;
--font-size-h4: 1.25rem;
--font-size-h5: 1.125rem;
--font-size-h6: 1rem;
--font-size-regular: 1rem;
--font-size-list: var(--font-size-regular);
--font-size-code: var(--font-size-regular);
--line-height-h1: 2.25rem;
--line-height-h2: 2rem;
--line-height-h3: 1.75rem;
--line-height-h4: 1.5rem;
--line-height-h5: 1.5rem;
--line-height-h6: 1.5rem;
--line-height-regular: 1.5rem;
--line-height-list: var(--line-height-regular);
--line-height-code: var(--line-height-regular);
}
&.small-font {
--font-size-h1: 1.4rem;
--font-size-h2: 1.2rem;
--font-size-h3: 1.1rem;
--font-size-h4: 1rem;
--font-size-h5: 0.9rem;
--font-size-h6: 0.8rem;
--font-size-regular: 0.8rem;
--font-size-list: var(--font-size-regular);
--font-size-code: var(--font-size-regular);
--line-height-h1: 1.8rem;
--line-height-h2: 1.6rem;
--line-height-h3: 1.4rem;
--line-height-h4: 1.2rem;
--line-height-h5: 1.2rem;
--line-height-h6: 1.2rem;
--line-height-regular: 1.2rem;
--line-height-list: var(--line-height-regular);
--line-height-code: var(--line-height-regular);
}
&.sans-serif {
--font-style: "Inter", sans-serif;
}
&.serif {
--font-style: serif;
}
&.monospace {
--font-style: monospace;
}
}
.ProseMirror {
position: relative;
word-wrap: break-word;
@ -439,3 +381,62 @@ ul[data-type="taskList"] ul[data-type="taskList"] {
margin-top: 0;
}
/* end tailwind typography */
/* text colors */
[data-text-color="gray"] {
color: var(--editor-colors-gray-text);
}
[data-text-color="peach"] {
color: var(--editor-colors-peach-text);
}
[data-text-color="pink"] {
color: var(--editor-colors-pink-text);
}
[data-text-color="orange"] {
color: var(--editor-colors-orange-text);
}
[data-text-color="green"] {
color: var(--editor-colors-green-text);
}
[data-text-color="light-blue"] {
color: var(--editor-colors-light-blue-text);
}
[data-text-color="dark-blue"] {
color: var(--editor-colors-dark-blue-text);
}
[data-text-color="purple"] {
color: var(--editor-colors-purple-text);
}
/* [data-text-color="pink-blue-gradient"] {
background-clip: text;
color: transparent;
background-image: linear-gradient(90deg, #a961cd 50%, #e75962 100%);
} */
/* end text colors */
/* background colors */
[data-background-color="gray"] {
background-color: var(--editor-colors-gray-background);
}
[data-background-color="peach"] {
background-color: var(--editor-colors-peach-background);
}
[data-background-color="pink"] {
background-color: var(--editor-colors-pink-background);
}
[data-background-color="orange"] {
background-color: var(--editor-colors-orange-background);
}
[data-background-color="green"] {
background-color: var(--editor-colors-green-background);
}
[data-background-color="light-blue"] {
background-color: var(--editor-colors-light-blue-background);
}
[data-background-color="dark-blue"] {
background-color: var(--editor-colors-dark-blue-background);
}
[data-background-color="purple"] {
background-color: var(--editor-colors-purple-background);
}
/* end background colors */

View file

@ -0,0 +1,96 @@
:root {
/* text colors */
--editor-colors-gray-text: #5c5e63;
--editor-colors-peach-text: #ff5b59;
--editor-colors-pink-text: #f65385;
--editor-colors-orange-text: #fd9038;
--editor-colors-green-text: #0fc27b;
--editor-colors-light-blue-text: #17bee9;
--editor-colors-dark-blue-text: #266df0;
--editor-colors-purple-text: #9162f9;
/* end text colors */
}
/* text background colors */
[data-theme="light"],
[data-theme="light-contrast"] {
--editor-colors-gray-background: #d6d6d8;
--editor-colors-peach-background: #ffd5d7;
--editor-colors-pink-background: #fdd4e3;
--editor-colors-orange-background: #ffe3cd;
--editor-colors-green-background: #c3f0de;
--editor-colors-light-blue-background: #c5eff9;
--editor-colors-dark-blue-background: #c9dafb;
--editor-colors-purple-background: #e3d8fd;
}
[data-theme="dark"],
[data-theme="dark-contrast"] {
--editor-colors-gray-background: #404144;
--editor-colors-peach-background: #593032;
--editor-colors-pink-background: #562e3d;
--editor-colors-orange-background: #583e2a;
--editor-colors-green-background: #1d4a3b;
--editor-colors-light-blue-background: #1f495c;
--editor-colors-dark-blue-background: #223558;
--editor-colors-purple-background: #3d325a;
}
/* end text background colors */
.editor-container {
/* font sizes and line heights */
&.large-font {
--font-size-h1: 1.75rem;
--font-size-h2: 1.5rem;
--font-size-h3: 1.375rem;
--font-size-h4: 1.25rem;
--font-size-h5: 1.125rem;
--font-size-h6: 1rem;
--font-size-regular: 1rem;
--font-size-list: var(--font-size-regular);
--font-size-code: var(--font-size-regular);
--line-height-h1: 2.25rem;
--line-height-h2: 2rem;
--line-height-h3: 1.75rem;
--line-height-h4: 1.5rem;
--line-height-h5: 1.5rem;
--line-height-h6: 1.5rem;
--line-height-regular: 1.5rem;
--line-height-list: var(--line-height-regular);
--line-height-code: var(--line-height-regular);
}
&.small-font {
--font-size-h1: 1.4rem;
--font-size-h2: 1.2rem;
--font-size-h3: 1.1rem;
--font-size-h4: 1rem;
--font-size-h5: 0.9rem;
--font-size-h6: 0.8rem;
--font-size-regular: 0.8rem;
--font-size-list: var(--font-size-regular);
--font-size-code: var(--font-size-regular);
--line-height-h1: 1.8rem;
--line-height-h2: 1.6rem;
--line-height-h3: 1.4rem;
--line-height-h4: 1.2rem;
--line-height-h5: 1.2rem;
--line-height-h6: 1.2rem;
--line-height-regular: 1.2rem;
--line-height-list: var(--line-height-regular);
--line-height-code: var(--line-height-regular);
}
/* end font sizes and line heights */
/* font styles */
&.sans-serif {
--font-style: "Inter", sans-serif;
}
&.serif {
--font-style: serif;
}
&.monospace {
--font-style: monospace;
}
/* end font styles */
}