Files

223 lines
4.6 KiB
TypeScript

// extensions/customMarks.ts
import {
Mark,
markInputRule,
markPasteRule,
mergeAttributes,
} from "@tiptap/core";
// Define available size options
const sizeOptions = {
xs: "text-xs",
sm: "text-sm",
base: "text-base",
lg: "text-lg",
xl: "text-xl",
} as const;
export type SizeOption = keyof typeof sizeOptions;
export interface FontSizeOptions {
HTMLAttributes: Record<string, any>;
}
declare module "@tiptap/core" {
interface Commands<ReturnType> {
fontSize: {
setFontSize: (attributes: { size: SizeOption }) => ReturnType;
toggleFontSize: (attributes: { size: SizeOption }) => ReturnType;
unsetFontSize: () => ReturnType;
};
}
}
export const fontSizeInputRegex = /(?:^|\s)(~((?:[^~]+))~)$/;
export const fontSizePasteRegex = /(?:^|\s)(~((?:[^~]+))~)/g;
export const FontSize = Mark.create<FontSizeOptions>({
name: "fontSize",
addOptions() {
return {
HTMLAttributes: {},
};
},
addAttributes() {
return {
size: {
default: "base",
parseHTML: (element: HTMLElement) => {
const sizeClass = Object.entries(sizeOptions).find(
([, className]) =>
element.classList.contains(className),
)?.[0];
return sizeClass || "base";
},
renderHTML: (attributes: { size: SizeOption }) => ({
class: sizeOptions[attributes.size],
}),
},
};
},
parseHTML() {
return [
{
tag: "span",
getAttrs: (node: HTMLElement) => {
const hasSizeClass = Object.values(sizeOptions).some(
(className) => node.classList.contains(className),
);
return hasSizeClass ? {} : null;
},
},
];
},
renderHTML({ HTMLAttributes }) {
return [
"span",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
0,
];
},
addCommands() {
return {
setFontSize:
(attributes: { size: SizeOption }) =>
({ commands }) => {
return commands.setMark(this.name, attributes);
},
toggleFontSize:
(attributes: { size: SizeOption }) =>
({ commands }) => {
return commands.toggleMark(this.name, attributes);
},
unsetFontSize:
() =>
({ commands }) => {
return commands.unsetMark(this.name);
},
};
},
addKeyboardShortcuts() {
return {
"Mod-s": () => this.editor.commands.toggleFontSize({ size: "sm" }),
};
},
addInputRules() {
return [
markInputRule({
find: fontSizeInputRegex,
type: this.type,
getAttributes: { size: "sm" }, // Default size for input rule
}),
];
},
addPasteRules() {
return [
markPasteRule({
find: fontSizePasteRegex,
type: this.type,
getAttributes: { size: "sm" }, // Default size for paste rule
}),
];
},
});
// TextColor remains unchanged
export interface TextColorOptions {
HTMLAttributes: Record<string, any>;
}
declare module "@tiptap/core" {
interface Commands<ReturnType> {
textColor: {
setTextColor: (attributes: { color: string }) => ReturnType;
toggleTextColor: (attributes: { color: string }) => ReturnType;
unsetTextColor: () => ReturnType;
};
}
}
export const TextColor = Mark.create<TextColorOptions>({
name: "textColor",
addOptions() {
return {
HTMLAttributes: {},
};
},
addAttributes() {
return {
color: {
default: null,
parseHTML: (element: HTMLElement) => {
const colorClass = Array.from(element.classList).find(
(cls) => cls.startsWith("text-"),
);
return colorClass ? colorClass.replace("text-", "") : null;
},
renderHTML: (attributes) => {
if (!attributes.color) return {};
// Use !important to override prose styles
return {
class: `text-${attributes.color} !text-${attributes.color}`,
};
},
},
};
},
parseHTML() {
return [
{
tag: "span",
getAttrs: (node: HTMLElement) => {
const colorClass = Array.from(node.classList).find((cls) =>
cls.startsWith("text-"),
);
return colorClass
? { color: colorClass.replace("text-", "") }
: null;
},
},
];
},
renderHTML({ HTMLAttributes }) {
return [
"span",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
0,
];
},
addCommands() {
return {
setTextColor:
(attributes: { color: string }) =>
({ commands }) => {
return commands.setMark(this.name, attributes);
},
toggleTextColor:
(attributes: { color: string }) =>
({ commands }) => {
return commands.toggleMark(this.name, attributes);
},
unsetTextColor:
() =>
({ commands }) => {
return commands.unsetMark(this.name);
},
};
},
addKeyboardShortcuts() {
return {
"Mod-Shift-c": () =>
this.editor.commands.toggleTextColor({ color: "gray-500" }),
};
},
});