[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:
parent
74695e561a
commit
e7065af358
19 changed files with 604 additions and 393 deletions
|
|
@ -42,8 +42,6 @@
|
||||||
"@tiptap/extension-blockquote": "^2.1.13",
|
"@tiptap/extension-blockquote": "^2.1.13",
|
||||||
"@tiptap/extension-character-count": "^2.6.5",
|
"@tiptap/extension-character-count": "^2.6.5",
|
||||||
"@tiptap/extension-collaboration": "^2.3.2",
|
"@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-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",
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,8 @@ type Props = {
|
||||||
export const BubbleMenuColorSelector: FC<Props> = (props) => {
|
export const BubbleMenuColorSelector: FC<Props> = (props) => {
|
||||||
const { editor, isOpen, setIsOpen } = props;
|
const { editor, isOpen, setIsOpen } = props;
|
||||||
|
|
||||||
const activeTextColor = COLORS_LIST.find((c) => editor.getAttributes("textStyle").color === c.textColor);
|
const activeTextColor = COLORS_LIST.find((c) => editor.getAttributes("textStyle").color === c.key);
|
||||||
const activeBackgroundColor = COLORS_LIST.find((c) =>
|
const activeBackgroundColor = COLORS_LIST.find((c) => editor.getAttributes("textStyle").backgroundColor === c.key);
|
||||||
editor.isActive("highlight", {
|
|
||||||
color: c.backgroundColor,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full">
|
<div className="relative h-full">
|
||||||
|
|
@ -41,25 +37,17 @@ export const BubbleMenuColorSelector: FC<Props> = (props) => {
|
||||||
"bg-custom-background-100": !activeBackgroundColor,
|
"bg-custom-background-100": !activeBackgroundColor,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
style={
|
style={{
|
||||||
activeBackgroundColor
|
backgroundColor: activeBackgroundColor ? activeBackgroundColor.backgroundColor : "transparent",
|
||||||
? {
|
}}
|
||||||
backgroundColor: activeBackgroundColor.backgroundColor,
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<ALargeSmall
|
<ALargeSmall
|
||||||
className={cn("size-3.5", {
|
className={cn("size-3.5", {
|
||||||
"text-custom-text-100": !activeTextColor,
|
"text-custom-text-100": !activeTextColor,
|
||||||
})}
|
})}
|
||||||
style={
|
style={{
|
||||||
activeTextColor
|
color: activeTextColor ? activeTextColor.textColor : "inherit",
|
||||||
? {
|
}}
|
||||||
color: activeTextColor.textColor,
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -70,13 +58,13 @@ export const BubbleMenuColorSelector: FC<Props> = (props) => {
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{COLORS_LIST.map((color) => (
|
{COLORS_LIST.map((color) => (
|
||||||
<button
|
<button
|
||||||
key={color.textColor}
|
key={color.key}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: color.textColor,
|
backgroundColor: color.textColor,
|
||||||
}}
|
}}
|
||||||
onClick={() => TextColorItem(editor).command(color.textColor)}
|
onClick={() => TextColorItem(editor).command(color.key)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<button
|
<button
|
||||||
|
|
@ -93,13 +81,13 @@ export const BubbleMenuColorSelector: FC<Props> = (props) => {
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{COLORS_LIST.map((color) => (
|
{COLORS_LIST.map((color) => (
|
||||||
<button
|
<button
|
||||||
key={color.backgroundColor}
|
key={color.key}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: color.backgroundColor,
|
backgroundColor: color.backgroundColor,
|
||||||
}}
|
}}
|
||||||
onClick={() => BackgroundColorItem(editor).command(color.backgroundColor)}
|
onClick={() => BackgroundColorItem(editor).command(color.key)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ export const TextColorItem = (editor: Editor): EditorMenuItem => ({
|
||||||
export const BackgroundColorItem = (editor: Editor): EditorMenuItem => ({
|
export const BackgroundColorItem = (editor: Editor): EditorMenuItem => ({
|
||||||
key: "background-color",
|
key: "background-color",
|
||||||
name: "Background color",
|
name: "Background color",
|
||||||
isActive: (color) => editor.isActive("highlight", { color }),
|
isActive: (color) => editor.getAttributes("textStyle").backgroundColor === color,
|
||||||
command: (color: string) => toggleBackgroundColor(color, editor),
|
command: (color: string) => toggleBackgroundColor(color, editor),
|
||||||
icon: Palette,
|
icon: Palette,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,61 @@
|
||||||
export const COLORS_LIST: {
|
export const COLORS_LIST: {
|
||||||
backgroundColor: string;
|
key: string;
|
||||||
textColor: string;
|
|
||||||
label: string;
|
label: string;
|
||||||
|
textColor: string;
|
||||||
|
backgroundColor: string;
|
||||||
}[] = [
|
}[] = [
|
||||||
// {
|
|
||||||
// backgroundColor: "#1c202426",
|
|
||||||
// textColor: "#1c2024",
|
|
||||||
// label: "Black",
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
backgroundColor: "#5c5e6326",
|
key: "gray",
|
||||||
textColor: "#5c5e63",
|
|
||||||
label: "Gray",
|
label: "Gray",
|
||||||
|
textColor: "var(--editor-colors-gray-text)",
|
||||||
|
backgroundColor: "var(--editor-colors-gray-background)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
backgroundColor: "#ff5b5926",
|
key: "peach",
|
||||||
textColor: "#ff5b59",
|
|
||||||
label: "Peach",
|
label: "Peach",
|
||||||
|
textColor: "var(--editor-colors-peach-text)",
|
||||||
|
backgroundColor: "var(--editor-colors-peach-background)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
backgroundColor: "#f6538526",
|
key: "pink",
|
||||||
textColor: "#f65385",
|
|
||||||
label: "Pink",
|
label: "Pink",
|
||||||
|
textColor: "var(--editor-colors-pink-text)",
|
||||||
|
backgroundColor: "var(--editor-colors-pink-background)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
backgroundColor: "#fd903826",
|
key: "orange",
|
||||||
textColor: "#fd9038",
|
|
||||||
label: "Orange",
|
label: "Orange",
|
||||||
|
textColor: "var(--editor-colors-orange-text)",
|
||||||
|
backgroundColor: "var(--editor-colors-orange-background)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
backgroundColor: "#0fc27b26",
|
key: "green",
|
||||||
textColor: "#0fc27b",
|
|
||||||
label: "Green",
|
label: "Green",
|
||||||
|
textColor: "var(--editor-colors-green-text)",
|
||||||
|
backgroundColor: "var(--editor-colors-green-background)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
backgroundColor: "#17bee926",
|
key: "light-blue",
|
||||||
textColor: "#17bee9",
|
|
||||||
label: "Light blue",
|
label: "Light blue",
|
||||||
|
textColor: "var(--editor-colors-light-blue-text)",
|
||||||
|
backgroundColor: "var(--editor-colors-light-blue-background)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
backgroundColor: "#266df026",
|
key: "dark-blue",
|
||||||
textColor: "#266df0",
|
|
||||||
label: "Dark blue",
|
label: "Dark blue",
|
||||||
|
textColor: "var(--editor-colors-dark-blue-text)",
|
||||||
|
backgroundColor: "var(--editor-colors-dark-blue-background)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
backgroundColor: "#9162f926",
|
key: "purple",
|
||||||
textColor: "#9162f9",
|
|
||||||
label: "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)",
|
||||||
|
// },
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import { Color } from "@tiptap/extension-color";
|
|
||||||
import Highlight from "@tiptap/extension-highlight";
|
|
||||||
import TaskItem from "@tiptap/extension-task-item";
|
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";
|
||||||
|
|
@ -18,6 +16,8 @@ import { IssueWidgetWithoutProps } from "./issue-embed/issue-embed-without-props
|
||||||
import { CustomMentionWithoutProps } from "./mentions/mentions-without-props";
|
import { CustomMentionWithoutProps } from "./mentions/mentions-without-props";
|
||||||
import { CustomQuoteExtension } from "./quote";
|
import { CustomQuoteExtension } from "./quote";
|
||||||
import { TableHeader, TableCell, TableRow, Table } from "./table";
|
import { TableHeader, TableCell, TableRow, Table } from "./table";
|
||||||
|
import { CustomTextColorExtension } from "./custom-text-color";
|
||||||
|
import { CustomBackgroundColorExtension } from "./custom-background-color";
|
||||||
|
|
||||||
export const CoreEditorExtensionsWithoutProps = [
|
export const CoreEditorExtensionsWithoutProps = [
|
||||||
StarterKit.configure({
|
StarterKit.configure({
|
||||||
|
|
@ -85,10 +85,8 @@ export const CoreEditorExtensionsWithoutProps = [
|
||||||
TableCell,
|
TableCell,
|
||||||
TableRow,
|
TableRow,
|
||||||
CustomMentionWithoutProps(),
|
CustomMentionWithoutProps(),
|
||||||
Color,
|
CustomTextColorExtension,
|
||||||
Highlight.configure({
|
CustomBackgroundColorExtension,
|
||||||
multicolor: true,
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const DocumentEditorExtensionsWithoutProps = [IssueWidgetWithoutProps()];
|
export const DocumentEditorExtensionsWithoutProps = [IssueWidgetWithoutProps()];
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
78
packages/editor/src/core/extensions/custom-text-color.ts
Normal file
78
packages/editor/src/core/extensions/custom-text-color.ts
Normal 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(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import CharacterCount from "@tiptap/extension-character-count";
|
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 Placeholder from "@tiptap/extension-placeholder";
|
||||||
import TaskItem from "@tiptap/extension-task-item";
|
import TaskItem from "@tiptap/extension-task-item";
|
||||||
import TaskList from "@tiptap/extension-task-list";
|
import TaskList from "@tiptap/extension-task-list";
|
||||||
|
|
@ -10,6 +8,7 @@ import StarterKit from "@tiptap/starter-kit";
|
||||||
import { Markdown } from "tiptap-markdown";
|
import { Markdown } from "tiptap-markdown";
|
||||||
// extensions
|
// extensions
|
||||||
import {
|
import {
|
||||||
|
CustomBackgroundColorExtension,
|
||||||
CustomCodeBlockExtension,
|
CustomCodeBlockExtension,
|
||||||
CustomCodeInlineExtension,
|
CustomCodeInlineExtension,
|
||||||
CustomCodeMarkPlugin,
|
CustomCodeMarkPlugin,
|
||||||
|
|
@ -19,6 +18,7 @@ import {
|
||||||
CustomLinkExtension,
|
CustomLinkExtension,
|
||||||
CustomMention,
|
CustomMention,
|
||||||
CustomQuoteExtension,
|
CustomQuoteExtension,
|
||||||
|
CustomTextColorExtension,
|
||||||
CustomTypographyExtension,
|
CustomTypographyExtension,
|
||||||
DropHandlerExtension,
|
DropHandlerExtension,
|
||||||
ImageExtension,
|
ImageExtension,
|
||||||
|
|
@ -168,8 +168,6 @@ export const CoreEditorExtensions = ({
|
||||||
includeChildren: true,
|
includeChildren: true,
|
||||||
}),
|
}),
|
||||||
CharacterCount,
|
CharacterCount,
|
||||||
Color,
|
CustomTextColorExtension,
|
||||||
Highlight.configure({
|
CustomBackgroundColorExtension,
|
||||||
multicolor: true,
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ export * from "./slash-commands";
|
||||||
export * from "./table";
|
export * from "./table";
|
||||||
export * from "./typography";
|
export * from "./typography";
|
||||||
export * from "./core-without-props";
|
export * from "./core-without-props";
|
||||||
|
export * from "./custom-background-color";
|
||||||
export * from "./custom-code-inline";
|
export * from "./custom-code-inline";
|
||||||
|
export * from "./custom-text-color";
|
||||||
export * from "./drop";
|
export * from "./drop";
|
||||||
export * from "./enter-key-extension";
|
export * from "./enter-key-extension";
|
||||||
export * from "./extensions";
|
export * from "./extensions";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import CharacterCount from "@tiptap/extension-character-count";
|
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 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";
|
||||||
|
|
@ -23,6 +21,8 @@ import {
|
||||||
CustomMention,
|
CustomMention,
|
||||||
HeadingListExtension,
|
HeadingListExtension,
|
||||||
CustomReadOnlyImageExtension,
|
CustomReadOnlyImageExtension,
|
||||||
|
CustomTextColorExtension,
|
||||||
|
CustomBackgroundColorExtension,
|
||||||
} from "@/extensions";
|
} from "@/extensions";
|
||||||
// helpers
|
// helpers
|
||||||
import { isValidHttpUrl } from "@/helpers/common";
|
import { isValidHttpUrl } from "@/helpers/common";
|
||||||
|
|
@ -111,9 +111,7 @@ export const CoreReadOnlyEditorExtensions = (mentionConfig: {
|
||||||
readonly: true,
|
readonly: true,
|
||||||
}),
|
}),
|
||||||
CharacterCount,
|
CharacterCount,
|
||||||
Color,
|
CustomTextColorExtension,
|
||||||
Highlight.configure({
|
CustomBackgroundColorExtension,
|
||||||
multicolor: true,
|
|
||||||
}),
|
|
||||||
HeadingListExtension,
|
HeadingListExtension,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -44,237 +44,237 @@ export type TSlashCommandSection = {
|
||||||
items: ISlashCommandItem[];
|
items: ISlashCommandItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const SLASH_COMMAND_SECTIONS: TSlashCommandSection[] = [
|
export const getSlashCommandFilteredSections =
|
||||||
{
|
(additionalOptions?: ISlashCommandItem[]) =>
|
||||||
key: "general",
|
({ query }: { query: string }): TSlashCommandSection[] => {
|
||||||
items: [
|
const SLASH_COMMAND_SECTIONS: TSlashCommandSection[] = [
|
||||||
{
|
{
|
||||||
commandKey: "text",
|
key: "general",
|
||||||
key: "text",
|
items: [
|
||||||
title: "Text",
|
{
|
||||||
description: "Just start typing with plain text.",
|
commandKey: "text",
|
||||||
searchTerms: ["p", "paragraph"],
|
key: "text",
|
||||||
icon: <CaseSensitive className="size-3.5" />,
|
title: "Text",
|
||||||
command: ({ editor, range }: CommandProps) => {
|
description: "Just start typing with plain text.",
|
||||||
if (range) {
|
searchTerms: ["p", "paragraph"],
|
||||||
editor.chain().focus().deleteRange(range).clearNodes().run();
|
icon: <CaseSensitive className="size-3.5" />,
|
||||||
}
|
command: ({ editor, range }: CommandProps) => {
|
||||||
editor.chain().focus().clearNodes().run();
|
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: "text-color",
|
||||||
key: "h1",
|
title: "Colors",
|
||||||
title: "Heading 1",
|
items: [
|
||||||
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) =>
|
|
||||||
({
|
|
||||||
commandKey: "text-color",
|
commandKey: "text-color",
|
||||||
key: `text-color-${color.textColor}`,
|
key: "text-color-default",
|
||||||
title: color.label,
|
title: "Default",
|
||||||
description: "Change text color",
|
description: "Change text color",
|
||||||
searchTerms: ["color", "text", color.label],
|
searchTerms: ["color", "text", "default"],
|
||||||
icon: (
|
icon: (
|
||||||
<ALargeSmall
|
<ALargeSmall
|
||||||
className="size-3.5"
|
className="size-3.5"
|
||||||
style={{
|
style={{
|
||||||
color: color.textColor,
|
color: "rgba(var(--color-text-100))",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
command: ({ editor, range }) => toggleTextColor(color.textColor, editor, range),
|
command: ({ editor, range }) => toggleTextColor(undefined, editor, range),
|
||||||
}) as ISlashCommandItem
|
},
|
||||||
),
|
...COLORS_LIST.map(
|
||||||
],
|
(color) =>
|
||||||
},
|
({
|
||||||
{
|
commandKey: "text-color",
|
||||||
key: "background-color",
|
key: `text-color-${color.key}`,
|
||||||
title: "Background colors",
|
title: color.label,
|
||||||
items: [
|
description: "Change text color",
|
||||||
{
|
searchTerms: ["color", "text", color.label],
|
||||||
commandKey: "background-color",
|
icon: (
|
||||||
key: "background-color-default",
|
<ALargeSmall
|
||||||
title: "Default background",
|
className="size-3.5"
|
||||||
description: "Change background color",
|
style={{
|
||||||
searchTerms: ["color", "bg", "background", "default"],
|
color: color.textColor,
|
||||||
icon: <ALargeSmall className="size-3.5" />,
|
}}
|
||||||
iconContainerStyle: {
|
/>
|
||||||
borderRadius: "4px",
|
),
|
||||||
backgroundColor: "rgba(var(--color-background-100))",
|
command: ({ editor, range }) => toggleTextColor(color.key, editor, range),
|
||||||
border: "1px solid rgba(var(--color-border-300))",
|
}) as ISlashCommandItem
|
||||||
},
|
),
|
||||||
command: ({ editor, range }) => toggleTextColor(undefined, editor, range),
|
],
|
||||||
},
|
},
|
||||||
...COLORS_LIST.map(
|
{
|
||||||
(color) =>
|
key: "background-color",
|
||||||
({
|
title: "Background colors",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
commandKey: "background-color",
|
commandKey: "background-color",
|
||||||
key: `background-color-${color.backgroundColor}`,
|
key: "background-color-default",
|
||||||
title: `${color.label} background`,
|
title: "Default background",
|
||||||
description: "Change background color",
|
description: "Change background color",
|
||||||
searchTerms: ["color", "bg", "background", color.label],
|
searchTerms: ["color", "bg", "background", "default"],
|
||||||
icon: <ALargeSmall className="size-3.5" />,
|
icon: <ALargeSmall className="size-3.5" />,
|
||||||
iconContainerStyle: {
|
iconContainerStyle: {
|
||||||
borderRadius: "4px",
|
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),
|
command: ({ editor, range }) => toggleTextColor(undefined, editor, range),
|
||||||
}) as ISlashCommandItem
|
},
|
||||||
),
|
...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?.map((item) => {
|
||||||
(additionalOptions?: ISlashCommandItem[]) =>
|
SLASH_COMMAND_SECTIONS?.[0]?.items.push(item);
|
||||||
({ query }: { query: string }): TSlashCommandSection[] => {
|
});
|
||||||
if (additionalOptions) {
|
|
||||||
additionalOptions.map((item) => SLASH_COMMAND_SECTIONS?.[0]?.items.push(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
const filteredSlashSections = SLASH_COMMAND_SECTIONS.map((section) => ({
|
const filteredSlashSections = SLASH_COMMAND_SECTIONS.map((section) => ({
|
||||||
...section,
|
...section,
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ import { CommandMenuItem } from "./command-menu-item";
|
||||||
type Props = {
|
type Props = {
|
||||||
items: TSlashCommandSection[];
|
items: TSlashCommandSection[];
|
||||||
command: any;
|
command: any;
|
||||||
editor: any;
|
|
||||||
range: any;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SlashCommandsMenu = (props: Props) => {
|
export const SlashCommandsMenu = (props: Props) => {
|
||||||
|
|
@ -22,7 +20,7 @@ export const SlashCommandsMenu = (props: Props) => {
|
||||||
|
|
||||||
const selectItem = useCallback(
|
const selectItem = useCallback(
|
||||||
(sectionIndex: number, itemIndex: number) => {
|
(sectionIndex: number, itemIndex: number) => {
|
||||||
const item = sections[sectionIndex].items[itemIndex];
|
const item = sections[sectionIndex]?.items?.[itemIndex];
|
||||||
if (item) command(item);
|
if (item) command(item);
|
||||||
},
|
},
|
||||||
[command, sections]
|
[command, sections]
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,6 @@ const renderItems = () => {
|
||||||
|
|
||||||
const tippyContainer =
|
const tippyContainer =
|
||||||
document.querySelector(".active-editor") ?? document.querySelector('[id^="editor-container"]');
|
document.querySelector(".active-editor") ?? document.querySelector('[id^="editor-container"]');
|
||||||
|
|
||||||
// @ts-expect-error Tippy overloads are messed up
|
|
||||||
popup = tippy("body", {
|
popup = tippy("body", {
|
||||||
getReferenceClientRect: props.clientRect,
|
getReferenceClientRect: props.clientRect,
|
||||||
appendTo: tippyContainer,
|
appendTo: tippyContainer,
|
||||||
|
|
|
||||||
|
|
@ -157,39 +157,26 @@ export const setLinkEditor = (editor: Editor, url: string) => {
|
||||||
|
|
||||||
export const toggleTextColor = (color: string | undefined, editor: Editor, range?: Range) => {
|
export const toggleTextColor = (color: string | undefined, editor: Editor, range?: Range) => {
|
||||||
if (color) {
|
if (color) {
|
||||||
if (range) editor.chain().focus().deleteRange(range).setColor(color).run();
|
if (range) editor.chain().focus().deleteRange(range).setTextColor(color).run();
|
||||||
else editor.chain().focus().setColor(color).run();
|
else editor.chain().focus().setTextColor(color).run();
|
||||||
} else {
|
} else {
|
||||||
if (range) editor.chain().focus().deleteRange(range).unsetColor().run();
|
if (range) editor.chain().focus().deleteRange(range).unsetTextColor().run();
|
||||||
else editor.chain().focus().unsetColor().run();
|
else editor.chain().focus().unsetTextColor().run();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toggleBackgroundColor = (color: string | undefined, editor: Editor, range?: Range) => {
|
export const toggleBackgroundColor = (color: string | undefined, editor: Editor, range?: Range) => {
|
||||||
if (color) {
|
if (color) {
|
||||||
if (range) {
|
if (range) {
|
||||||
editor
|
editor.chain().focus().deleteRange(range).setBackgroundColor(color).run();
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.deleteRange(range)
|
|
||||||
.setHighlight({
|
|
||||||
color,
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
} else {
|
} else {
|
||||||
editor
|
editor.chain().focus().setBackgroundColor(color).run();
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.setHighlight({
|
|
||||||
color,
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (range) {
|
if (range) {
|
||||||
editor.chain().focus().deleteRange(range).unsetHighlight().run();
|
editor.chain().focus().deleteRange(range).unsetBackgroundColor().run();
|
||||||
} else {
|
} else {
|
||||||
editor.chain().focus().unsetHighlight().run();
|
editor.chain().focus().unsetBackgroundColor().run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// styles
|
// styles
|
||||||
// import "./styles/tailwind.css";
|
// import "./styles/tailwind.css";
|
||||||
|
import "src/styles/variables.css";
|
||||||
import "src/styles/editor.css";
|
import "src/styles/editor.css";
|
||||||
import "src/styles/table.css";
|
import "src/styles/table.css";
|
||||||
import "src/styles/github-dark.css";
|
import "src/styles/github-dark.css";
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
.ProseMirror {
|
||||||
position: relative;
|
position: relative;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
@ -439,3 +381,62 @@ ul[data-type="taskList"] ul[data-type="taskList"] {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
/* end tailwind typography */
|
/* 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 */
|
||||||
|
|
|
||||||
96
packages/editor/src/styles/variables.css
Normal file
96
packages/editor/src/styles/variables.css
Normal 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 */
|
||||||
|
}
|
||||||
|
|
@ -16,8 +16,8 @@ type Props = {
|
||||||
export const ColorDropdown: React.FC<Props> = memo((props) => {
|
export const ColorDropdown: React.FC<Props> = memo((props) => {
|
||||||
const { handleColorSelect, isColorActive } = props;
|
const { handleColorSelect, isColorActive } = props;
|
||||||
|
|
||||||
const activeTextColor = COLORS_LIST.find((c) => isColorActive("text-color", c.textColor));
|
const activeTextColor = COLORS_LIST.find((c) => isColorActive("text-color", c.key));
|
||||||
const activeBackgroundColor = COLORS_LIST.find((c) => isColorActive("background-color", c.backgroundColor));
|
const activeBackgroundColor = COLORS_LIST.find((c) => isColorActive("background-color", c.key));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover as="div" className="h-7 px-2">
|
<Popover as="div" className="h-7 px-2">
|
||||||
|
|
@ -47,25 +47,17 @@ export const ColorDropdown: React.FC<Props> = memo((props) => {
|
||||||
"bg-custom-background-100": !activeBackgroundColor,
|
"bg-custom-background-100": !activeBackgroundColor,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
style={
|
style={{
|
||||||
activeBackgroundColor
|
backgroundColor: activeBackgroundColor ? activeBackgroundColor.backgroundColor : "transparent",
|
||||||
? {
|
}}
|
||||||
backgroundColor: activeBackgroundColor.backgroundColor,
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<ALargeSmall
|
<ALargeSmall
|
||||||
className={cn("size-3.5", {
|
className={cn("size-3.5", {
|
||||||
"text-custom-text-100": !activeTextColor,
|
"text-custom-text-100": !activeTextColor,
|
||||||
})}
|
})}
|
||||||
style={
|
style={{
|
||||||
activeTextColor
|
color: activeTextColor ? activeTextColor.textColor : "inherit",
|
||||||
? {
|
}}
|
||||||
color: activeTextColor.textColor,
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -80,13 +72,13 @@ export const ColorDropdown: React.FC<Props> = memo((props) => {
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{COLORS_LIST.map((color) => (
|
{COLORS_LIST.map((color) => (
|
||||||
<button
|
<button
|
||||||
key={color.textColor}
|
key={color.key}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: color.textColor,
|
backgroundColor: color.textColor,
|
||||||
}}
|
}}
|
||||||
onClick={() => handleColorSelect("text-color", color.textColor)}
|
onClick={() => handleColorSelect("text-color", color.key)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<button
|
<button
|
||||||
|
|
@ -103,13 +95,13 @@ export const ColorDropdown: React.FC<Props> = memo((props) => {
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{COLORS_LIST.map((color) => (
|
{COLORS_LIST.map((color) => (
|
||||||
<button
|
<button
|
||||||
key={color.backgroundColor}
|
key={color.key}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
className="flex-shrink-0 size-6 rounded border-[0.5px] border-custom-border-400 hover:opacity-60 transition-opacity"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: color.backgroundColor,
|
backgroundColor: color.backgroundColor,
|
||||||
}}
|
}}
|
||||||
onClick={() => handleColorSelect("background-color", color.backgroundColor)}
|
onClick={() => handleColorSelect("background-color", color.key)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
10
yarn.lock
10
yarn.lock
|
|
@ -3636,11 +3636,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-collaboration/-/extension-collaboration-2.8.0.tgz#db7a1e600c80229ed24a9d004f290d9e8bd4d0f6"
|
resolved "https://registry.yarnpkg.com/@tiptap/extension-collaboration/-/extension-collaboration-2.8.0.tgz#db7a1e600c80229ed24a9d004f290d9e8bd4d0f6"
|
||||||
integrity sha512-Ae5NZWj2aq8ZElsGxQiq3cAxxbb0cR7VHvmIG1mPA6USvrQL6/xtBVutersBqINFELmIuxh/jm8qVffBm2qXyg==
|
integrity sha512-Ae5NZWj2aq8ZElsGxQiq3cAxxbb0cR7VHvmIG1mPA6USvrQL6/xtBVutersBqINFELmIuxh/jm8qVffBm2qXyg==
|
||||||
|
|
||||||
"@tiptap/extension-color@^2.7.1":
|
|
||||||
version "2.8.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.8.0.tgz#597e1ea2e675e3c01ba64933008eacd296913abd"
|
|
||||||
integrity sha512-b0ZIDaZKTDVdTb0PMgtOiPzgCkYhvDldjzdWyPLsjWup5x9/zPasH5X/2SfMuwtjt+cKj6YBPveJjF7w5ApK7w==
|
|
||||||
|
|
||||||
"@tiptap/extension-document@^2.8.0":
|
"@tiptap/extension-document@^2.8.0":
|
||||||
version "2.8.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.8.0.tgz#7dc5d2622168ad5b81134a92fccf49d7be53f141"
|
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.8.0.tgz#7dc5d2622168ad5b81134a92fccf49d7be53f141"
|
||||||
|
|
@ -3673,11 +3668,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.8.0.tgz#1b7711860fe9f4336fb8933110a129150faa4e39"
|
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.8.0.tgz#1b7711860fe9f4336fb8933110a129150faa4e39"
|
||||||
integrity sha512-4inWgrTPiqlivPmEHFOM5ck2UsmOsbKKPtqga6bALvWPmCv24S6/EBwFp8Jz4YABabXDnkviihmGu0LpP9D69w==
|
integrity sha512-4inWgrTPiqlivPmEHFOM5ck2UsmOsbKKPtqga6bALvWPmCv24S6/EBwFp8Jz4YABabXDnkviihmGu0LpP9D69w==
|
||||||
|
|
||||||
"@tiptap/extension-highlight@^2.7.1":
|
|
||||||
version "2.8.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.8.0.tgz#3970f42a5a116745fbb2b82cfc5055adb04158e9"
|
|
||||||
integrity sha512-vyqX7D449nuARhI0AyRqtIZReFg3sfc/U/q1p3JOjtUoW6z2jmDTzshiKRrSg+Jf7Hhzj1pqwU+6+CpelPPDpA==
|
|
||||||
|
|
||||||
"@tiptap/extension-history@^2.8.0":
|
"@tiptap/extension-history@^2.8.0":
|
||||||
version "2.8.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.8.0.tgz#06505cbdaa29a9791911eddbee54304ee32b1d5c"
|
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.8.0.tgz#06505cbdaa29a9791911eddbee54304ee32b1d5c"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue