223 lines
4.6 KiB
TypeScript
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" }),
|
|
};
|
|
},
|
|
});
|