feat: add autocorrect for horizontal lines
This commit is contained in:
1 parent
f1ba14c948
commit
2cbc69bfd1
2 files changed
+41
-1
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
import { Extension, InputRule } from '@tiptap/core';
|
||||
import type { CommandProps } from '@tiptap/core';
|
||||
import type { EditorState } from '@tiptap/pm/state';
|
||||
import type { Node as PMNode } from '@tiptap/pm/model';
|
||||
@@ -77,6 +77,8 @@ export const ParagraphBox = Extension.create({
|
||||
attributes: {
|
||||
backgroundColor: {
|
||||
default: null,
|
||||
// Decorative box shouldn't cascade onto the next paragraph on Enter.
|
||||
keepOnSplit: false,
|
||||
parseHTML: (element: HTMLElement) => element.getAttribute('data-bg') || null,
|
||||
renderHTML: (attributes: Record<string, unknown>) => {
|
||||
const c = attributes.backgroundColor as string | null;
|
||||
@@ -89,6 +91,7 @@ export const ParagraphBox = Extension.create({
|
||||
side,
|
||||
{
|
||||
default: null,
|
||||
keepOnSplit: false,
|
||||
parseHTML: (element: HTMLElement) => element.getAttribute(SIDE_META[side].data),
|
||||
renderHTML: (attributes: Record<string, unknown>) => {
|
||||
const v = attributes[side] as string | null;
|
||||
@@ -138,6 +141,27 @@ export const ParagraphBox = Extension.create({
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
// Word/LibreOffice "border lines" AutoCorrect: a paragraph whose only text is 3+ of
|
||||
// -, _ or = becomes an empty bottom-rule line (thin / thick / thick — no double style).
|
||||
addInputRules() {
|
||||
const types = this.options.types;
|
||||
const WIDTH_PT: Record<string, number> = { '-': 0.5, _: 1.5, '=': 1.5 };
|
||||
return [
|
||||
new InputRule({
|
||||
find: /^(-{3,}|_{3,}|={3,})$/,
|
||||
handler: ({ state, range, match }) => {
|
||||
const value = `${WIDTH_PT[match[1][0]] ?? 0.5}pt solid #000000`;
|
||||
const $start = state.doc.resolve(range.from);
|
||||
const node = $start.parent;
|
||||
if (!types.includes(node.type.name)) return null;
|
||||
const paraPos = $start.before();
|
||||
state.tr.delete(range.from, range.to);
|
||||
state.tr.setNodeMarkup(paraPos, undefined, { ...node.attrs, borderBottom: value });
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
// Word-like active states for the paragraph border picker: a preset is active when every
|
||||
|
||||
@@ -70,6 +70,22 @@ describe('paragraph background + borders round trip', () => {
|
||||
expect(hfIsEmpty({ type: 'doc', content: [{ type: 'paragraph' }] } as any)).toBe(true);
|
||||
});
|
||||
|
||||
it('round-trips an empty bottom-rule paragraph (the --- autocorrect output)', async () => {
|
||||
const doc: N = { type: 'doc', content: [
|
||||
{ type: 'paragraph', content: [{ type: 'text', text: 'above' }] },
|
||||
{ type: 'paragraph', attrs: { borderBottom: '0.5pt solid #000000' } },
|
||||
{ type: 'paragraph', content: [{ type: 'text', text: 'below' }] },
|
||||
]};
|
||||
const findRule = (d: any) => d.content.find((n: any) => n.type === 'paragraph' && !n.content?.length && n.attrs?.borderBottom);
|
||||
const odtBack: any = importOdt(await buildOdt(doc as any, DEFAULT_MARGINS, 'portrait'));
|
||||
const docxBack: any = importDocx(await buildDocx(doc as any, DEFAULT_MARGINS, 'portrait'));
|
||||
for (const back of [odtBack.content, docxBack.content]) {
|
||||
const rule = findRule(back);
|
||||
expect(rule, 'empty bottom-rule paragraph survives').toBeTruthy();
|
||||
expect(rule.attrs.borderBottom).toMatch(/pt solid #000000/i);
|
||||
}
|
||||
});
|
||||
|
||||
it('drops background/borders that are absent (no accretion)', async () => {
|
||||
const plain: N = { type: 'doc', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'x' }] }] };
|
||||
const odt = await buildOdt(plain as any, DEFAULT_MARGINS, 'portrait');
|
||||
|
||||
Reference in new issue
Block a user