Skip to content

Commit 9b584f5

Browse files
committed
WIP
1 parent 4ad0352 commit 9b584f5

17 files changed

+659
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<link href="https://unpkg.com/@nl-design-system-unstable/basis-design-tokens/dist/variables.css" rel="stylesheet" />
6+
</head>
7+
<body>
8+
<div class="element"></div>
9+
<script type="module">
10+
import { defineUtrechtParagraph } from './utrecht-paragraph.mjs';
11+
import { defineUtrechtUnorderedList } from './utrecht-unordered-list.mjs';
12+
import { defineUtrechtUnorderedListItem } from './utrecht-unordered-list-item.mjs';
13+
import { defineUtrechtOrderedList } from './utrecht-ordered-list.mjs';
14+
import { defineUtrechtOrderedListItem } from './utrecht-ordered-list-item.mjs';
15+
import { defineUtrechtHeading } from './utrecht-heading.mjs';
16+
import { defineExampleEditorElement } from './example-editor.js';
17+
defineExampleEditorElement();
18+
defineUtrechtParagraph();
19+
defineUtrechtHeading();
20+
defineUtrechtUnorderedList();
21+
defineUtrechtUnorderedListItem();
22+
defineUtrechtOrderedList();
23+
defineUtrechtOrderedListItem();
24+
</script>
25+
<example-editor>
26+
<template>
27+
<utrecht-heading level="1">Heading</utrecht-heading>
28+
<utrecht-heading level="2">Heading</utrecht-heading>
29+
<utrecht-heading level="3">Heading</utrecht-heading>
30+
<utrecht-heading level="4">Heading</utrecht-heading>
31+
<utrecht-heading level="5">Heading</utrecht-heading>
32+
<utrecht-heading level="6">Heading</utrecht-heading>
33+
<utrecht-paragraph>Edit me!</utrecht-paragraph>
34+
<utrecht-paragraph>&nbsp;</utrecht-paragraph
35+
><!-- empty paragraph, oh no! -->
36+
<utrecht-paragraph>- fake list item</utrecht-paragraph>
37+
<utrecht-paragraph>- fake list item</utrecht-paragraph>
38+
<utrecht-paragraph>- fake list item</utrecht-paragraph>
39+
<utrecht-paragraph>- fake list item</utrecht-paragraph>
40+
<utrecht-heading level="2">Unordered List example</utrecht-heading>
41+
<utrecht-unordered-list>
42+
<utrecht-unordered-list-item
43+
><utrecht-paragraph>actual list item</utrecht-paragraph></utrecht-unordered-list-item
44+
>
45+
<utrecht-unordered-list-item
46+
><utrecht-paragraph>actual list item</utrecht-paragraph></utrecht-unordered-list-item
47+
>
48+
<utrecht-unordered-list-item
49+
><utrecht-paragraph>actual list item</utrecht-paragraph></utrecht-unordered-list-item
50+
>
51+
</utrecht-unordered-list>
52+
<utrecht-heading level="2">Ordered List example</utrecht-heading>
53+
<utrecht-ordered-list>
54+
<utrecht-ordered-list-item><utrecht-paragraph>actual list item</utrecht-paragraph></utrecht-ordered-list-item>
55+
<utrecht-ordered-list-item><utrecht-paragraph>actual list item</utrecht-paragraph></utrecht-ordered-list-item>
56+
<utrecht-ordered-list-item><utrecht-paragraph>actual list item</utrecht-paragraph></utrecht-ordered-list-item>
57+
</utrecht-ordered-list>
58+
</template>
59+
</example-editor>
60+
</body>
61+
</html>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Node } from 'https://esm.sh/@tiptap/core';
2+
3+
export const Document = Node.create({
4+
content: 'block+',
5+
name: 'document',
6+
topNode: true,
7+
});
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { mergeAttributes, Node, textblockTypeInputRule } from 'https://esm.sh/@tiptap/core';
2+
3+
export const Heading = Node.create({
4+
name: 'heading',
5+
6+
addOptions() {
7+
return {
8+
levels: [1, 2, 3, 4, 5, 6],
9+
HTMLAttributes: {},
10+
};
11+
},
12+
13+
content: 'inline*',
14+
15+
group: 'block',
16+
17+
defining: true,
18+
19+
addAttributes() {
20+
return {
21+
level: {
22+
default: 1,
23+
rendered: false,
24+
},
25+
};
26+
},
27+
28+
parseHTML() {
29+
return this.options.levels.map((level) => ({
30+
tag: 'utrecht-heading',
31+
attrs: { level },
32+
}));
33+
},
34+
35+
renderHTML({ node, HTMLAttributes }) {
36+
const hasLevel = this.options.levels.includes(node.attrs.level);
37+
const level = hasLevel ? node.attrs.level : this.options.levels[0];
38+
39+
return ['utrecht-heading', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { level }), 0];
40+
},
41+
42+
addCommands() {
43+
return {
44+
setHeading:
45+
(attributes) =>
46+
({ commands }) => {
47+
if (!this.options.levels.includes(attributes.level)) {
48+
return false;
49+
}
50+
51+
return commands.setNode(this.name, attributes);
52+
},
53+
toggleHeading:
54+
(attributes) =>
55+
({ commands }) => {
56+
if (!this.options.levels.includes(attributes.level)) {
57+
return false;
58+
}
59+
60+
return commands.toggleNode(this.name, 'paragraph', attributes);
61+
},
62+
};
63+
},
64+
65+
addKeyboardShortcuts() {
66+
return this.options.levels.reduce(
67+
(items, level) => ({
68+
...items,
69+
...{
70+
[`Mod-Alt-${level}`]: () => this.editor.commands.toggleHeading({ level }),
71+
},
72+
}),
73+
{},
74+
);
75+
},
76+
77+
addInputRules() {
78+
return this.options.levels.map((level) => {
79+
return textblockTypeInputRule({
80+
find: new RegExp(`^(#{${Math.min(...this.options.levels)},${level}})\\s$`),
81+
type: this.type,
82+
getAttributes: {
83+
level,
84+
},
85+
});
86+
});
87+
},
88+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { mergeAttributes, Node } from 'https://esm.sh/@tiptap/core';
2+
3+
export const OrderedListItem = Node.create({
4+
name: 'orderedListItem',
5+
6+
addOptions() {
7+
return {
8+
HTMLAttributes: {},
9+
bulletListTypeName: 'bulletList',
10+
orderedListTypeName: 'orderedList',
11+
};
12+
},
13+
14+
content: 'paragraph block*',
15+
16+
defining: true,
17+
18+
parseHTML() {
19+
return [
20+
{
21+
tag: 'utrecht-ordered-list-item',
22+
},
23+
];
24+
},
25+
26+
renderHTML({ HTMLAttributes }) {
27+
return ['utrecht-ordered-list-item', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
28+
},
29+
30+
addKeyboardShortcuts() {
31+
return {
32+
Enter: () => this.editor.commands.splitListItem(this.name),
33+
Tab: () => this.editor.commands.sinkListItem(this.name),
34+
'Shift-Tab': () => this.editor.commands.liftListItem(this.name),
35+
};
36+
},
37+
});
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { mergeAttributes, Node, wrappingInputRule } from 'https://esm.sh/@tiptap/core';
2+
3+
const ListItemName = 'listItem';
4+
const TextStyleName = 'textStyle';
5+
6+
export const inputRegex = /^(\d+)\.\s$/;
7+
8+
export const OrderedList = Node.create({
9+
name: 'orderedList',
10+
11+
addOptions() {
12+
return {
13+
itemTypeName: 'orderedListItem',
14+
HTMLAttributes: {},
15+
keepMarks: false,
16+
keepAttributes: false,
17+
};
18+
},
19+
20+
group: 'block list',
21+
22+
content() {
23+
return `${this.options.itemTypeName}+`;
24+
},
25+
26+
addAttributes() {
27+
return {
28+
start: {
29+
default: 1,
30+
parseHTML: (element) => {
31+
return element.hasAttribute('start') ? parseInt(element.getAttribute('start') || '', 10) : 1;
32+
},
33+
},
34+
type: {
35+
default: null,
36+
parseHTML: (element) => element.getAttribute('type'),
37+
},
38+
};
39+
},
40+
41+
parseHTML() {
42+
return [
43+
{
44+
tag: 'utrecht-ordered-list',
45+
},
46+
];
47+
},
48+
49+
renderHTML({ HTMLAttributes }) {
50+
const { start, ...attributesWithoutStart } = HTMLAttributes;
51+
52+
return start === 1
53+
? ['utrecht-ordered-list', mergeAttributes(this.options.HTMLAttributes, attributesWithoutStart), 0]
54+
: ['utrecht-ordered-list', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
55+
},
56+
57+
addCommands() {
58+
return {
59+
toggleOrderedList:
60+
() =>
61+
({ commands, chain }) => {
62+
if (this.options.keepAttributes) {
63+
return chain()
64+
.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks)
65+
.updateAttributes(ListItemName, this.editor.getAttributes(TextStyleName))
66+
.run();
67+
}
68+
return commands.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks);
69+
},
70+
};
71+
},
72+
73+
addKeyboardShortcuts() {
74+
return {
75+
'Mod-Shift-7': () => this.editor.commands.toggleOrderedList(),
76+
};
77+
},
78+
79+
addInputRules() {
80+
let inputRule = wrappingInputRule({
81+
find: inputRegex,
82+
type: this.type,
83+
getAttributes: (match) => ({ start: +match[1] }),
84+
joinPredicate: (match, node) => node.childCount + node.attrs.start === +match[1],
85+
});
86+
87+
if (this.options.keepMarks || this.options.keepAttributes) {
88+
inputRule = wrappingInputRule({
89+
find: inputRegex,
90+
type: this.type,
91+
keepMarks: this.options.keepMarks,
92+
keepAttributes: this.options.keepAttributes,
93+
getAttributes: (match) => ({ start: +match[1], ...this.editor.getAttributes(TextStyleName) }),
94+
joinPredicate: (match, node) => node.childCount + node.attrs.start === +match[1],
95+
editor: this.editor,
96+
});
97+
}
98+
return [inputRule];
99+
},
100+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { mergeAttributes, Node } from 'https://esm.sh/@tiptap/core';
2+
3+
export const Paragraph = Node.create({
4+
name: 'paragraph',
5+
6+
priority: 1000,
7+
8+
addOptions() {
9+
return {
10+
HTMLAttributes: {},
11+
};
12+
},
13+
14+
group: 'block',
15+
16+
content: 'inline*',
17+
18+
parseHTML() {
19+
return [{ tag: 'utrecht-paragraph' }];
20+
},
21+
22+
renderHTML({ HTMLAttributes }) {
23+
return ['utrecht-paragraph', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
24+
},
25+
26+
addCommands() {
27+
return {
28+
setParagraph:
29+
() =>
30+
({ commands }) => {
31+
return commands.setNode(this.name);
32+
},
33+
};
34+
},
35+
36+
addKeyboardShortcuts() {
37+
return {
38+
'Mod-Alt-0': () => this.editor.commands.setParagraph(),
39+
};
40+
},
41+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { Node } from 'https://esm.sh/@tiptap/core';
2+
3+
export const Text = Node.create({
4+
group: 'inline',
5+
name: 'text',
6+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { mergeAttributes, Node } from 'https://esm.sh/@tiptap/core';
2+
3+
export const UnorderedListItem = Node.create({
4+
name: 'unorderedListItem',
5+
6+
addOptions() {
7+
return {
8+
HTMLAttributes: {},
9+
bulletListTypeName: 'bulletList',
10+
orderedListTypeName: 'orderedList',
11+
};
12+
},
13+
14+
content: 'paragraph block*',
15+
16+
defining: true,
17+
18+
parseHTML() {
19+
return [
20+
{
21+
tag: 'utrecht-unordered-list-item',
22+
},
23+
];
24+
},
25+
26+
renderHTML({ HTMLAttributes }) {
27+
return ['utrecht-unordered-list-item', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
28+
},
29+
30+
addKeyboardShortcuts() {
31+
return {
32+
Enter: () => this.editor.commands.splitListItem(this.name),
33+
Tab: () => this.editor.commands.sinkListItem(this.name),
34+
'Shift-Tab': () => this.editor.commands.liftListItem(this.name),
35+
};
36+
},
37+
});

0 commit comments

Comments
 (0)