添加列表样式及缩进菜单

This commit is contained in:
wux_labs
2025-02-18 11:30:25 +08:00
parent 7ebb20813c
commit 52131519e3
15 changed files with 1135 additions and 13 deletions
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-present AI-Labs
import BulletList from '@tiptap/extension-bullet-list'
// https://www.npmjs.com/package/tiptap-extension-bullet-list
export default BulletList.extend({
content: 'listItem*',
addAttributes() {
return {
...this.parent?.(),
listType: {
default: 'disc',
parseHTML: (element) =>
element.style.getPropertyValue('list-style-type') || 'disc',
renderHTML: ({ listType }) => {
return {
style: `list-style-type: ${listType}`,
}
},
},
}
},
})
+147
View File
@@ -0,0 +1,147 @@
// Copyright (c) 2024-present AI-Labs
import { Command, Extension } from '@tiptap/core';
import { AllSelection, TextSelection, Transaction } from 'prosemirror-state';
export interface IndentOptions {
types: string[];
minLevel: number;
maxLevel: number;
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
indent: {
/**
* 向右缩进
* @returns
*/
indent: () => ReturnType;
/**
* 向左缩进
* @returns
*/
outdent: () => ReturnType;
};
}
}
/**
* 缩进样式前缀
*/
const classAttrPrefix = 'indent-';
export default Extension.create<IndentOptions>({
name: 'indent',
priority: 99,
addOptions() {
return {
types: ['heading', 'listItem', 'taskItem', 'paragraph'],
minLevel: 0,
maxLevel: 8,
}
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
indent: {
default: 0,
parseHTML: element => {
let level = 0
for (const className of element.classList) {
if (className.startsWith(classAttrPrefix)) {
level = +(className.split("-")[1])
break
}
}
return level && level > this.options.minLevel ? level : null;
},
renderHTML: attributes => {
if (!attributes.indent) {
return {}
}
return {
class: `${classAttrPrefix}${attributes.indent}`,
}
},
},
},
},
];
},
addCommands() {
const setNodeIndentMarkup = (tr: Transaction, pos: number, delta: number): Transaction => {
const node = tr?.doc?.nodeAt(pos);
if (node) {
const nextLevel = (node.attrs.indent || 0) + delta;
const { minLevel, maxLevel } = this.options;
const indent = nextLevel < minLevel ? minLevel : nextLevel > maxLevel ? maxLevel : nextLevel;
if (indent !== node.attrs.indent) {
const { indent: oldIndent, ...currentAttrs } = node.attrs;
const nodeAttrs = indent > minLevel ? { ...currentAttrs, indent } : currentAttrs;
return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks);
}
}
return tr;
};
const updateIndentLevel = (tr: Transaction, delta: number): Transaction => {
const { doc, selection } = tr;
if (doc && selection && (selection instanceof TextSelection || selection instanceof AllSelection)) {
const { from, to } = selection;
doc.nodesBetween(from, to, (node, pos) => {
if (this.options.types.includes(node.type.name)) {
tr = setNodeIndentMarkup(tr, pos, delta);
return false;
}
return true;
});
}
return tr;
};
const applyIndent: (direction: number) => () => Command =
direction =>
() =>
({ tr, state, dispatch }) => {
const { selection } = state;
tr = tr.setSelection(selection);
tr = updateIndentLevel(tr, direction);
if (tr.docChanged) {
dispatch?.(tr);
return true;
}
return false;
};
return {
indent: applyIndent(1),
outdent: applyIndent(-1),
};
},
addKeyboardShortcuts() {
return {
Tab: () => {
return this.editor.commands.indent();
},
'Shift-Tab': () => {
return this.editor.commands.outdent();
},
};
},
});
+76
View File
@@ -0,0 +1,76 @@
// Copyright (c) 2024-present AI-Labs
import { Extension } from '@tiptap/core'
export interface LineHeightOptions {
types: string[];
defaultLineHeight: number;
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
setLineHeight: {
/**
* 设置行高
* @param lineHeight
* @returns
*/
setLineHeight: (lineHeight: number) => ReturnType
}
unsetLineHeight: {
/**
* 取消设置行高
* @returns
*/
unsetLineHeight: () => ReturnType
}
}
}
export default Extension.create<LineHeightOptions>({
name: 'lineHeight',
addOptions() {
return {
types: ["heading", "paragraph"],
defaultLineHeight: 1.5,
}
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
lineHeight: {
default: this.options.defaultLineHeight,
parseHTML: (element) =>
element.style.lineHeight || this.options.defaultLineHeight,
renderHTML: (attributes) => {
if (attributes.lineHeight === this.options.defaultLineHeight) {
return {}
}
return { style: `line-height: ${attributes.lineHeight}` }
},
},
},
},
]
},
addCommands() {
return {
setLineHeight:
(lineHeight) =>
({ commands }) => {
return this.options.types.every((type: string) =>
commands.updateAttributes(type, { lineHeight: lineHeight }),
)
},
unsetLineHeight:
() =>
({ commands }) => {
return this.options.types.every((type: string) =>
commands.resetAttributes(type, 'lineHeight'),
)
},
}
},
})
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-present AI-Labs
import OrderedList from '@tiptap/extension-ordered-list'
// https://www.npmjs.com/package/tiptap-extension-ordered-list
export default OrderedList.extend({
content: 'listItem*',
addAttributes() {
return {
...this.parent?.(),
listType: {
default: 'decimal',
parseHTML: (element) =>
element.style.getPropertyValue('list-style-type') || 'decimal',
renderHTML: ({ listType }) => {
return {
style: `list-style-type: ${listType}`,
}
},
},
}
},
})