Skip to content

Commit cd7f170

Browse files
committed
WIP
1 parent 5dd5414 commit cd7f170

25 files changed

+1110
-9
lines changed

packages/example-editor/index.html

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@
1414
import { defineUtrechtOrderedListItem } from './src/custom-element/utrecht-ordered-list-item.mjs';
1515
import { defineUtrechtHeading } from './src/custom-element/utrecht-heading.mjs';
1616
import { defineExampleEditorElement } from './src/tiptap/example-editor.js';
17+
import { defineUtrechtLink } from './src/custom-element/utrecht-link.ts';
18+
import { defineUtrechtSafeLink } from './src/custom-element/utrecht-safe-link.ts';
1719
defineExampleEditorElement();
1820
defineUtrechtParagraph();
1921
defineUtrechtHeading();
2022
defineUtrechtUnorderedList();
2123
defineUtrechtUnorderedListItem();
2224
defineUtrechtOrderedList();
2325
defineUtrechtOrderedListItem();
26+
defineUtrechtLink();
27+
defineUtrechtSafeLink();
2428
document.addEventListener('validationError', (evt) => {
2529
const result = document.getElementById('error-list');
2630
while (result.lastChild) {
@@ -41,7 +45,19 @@
4145
<utrecht-heading level="5">Heading</utrecht-heading>
4246
<utrecht-heading level="6">Heading</utrecht-heading>
4347
<utrecht-heading level="7">Heading 7</utrecht-heading>
48+
<utrecht-heading level="6"><b>Bold move to use b in an heading</b></utrecht-heading>
49+
<utrecht-heading level="6"><i>Bold move to use i in an heading</i></utrecht-heading>
4450
<utrecht-paragraph>Edit me!</utrecht-paragraph>
51+
<utrecht-paragraph>Text <b>in bold</b></utrecht-paragraph>
52+
<utrecht-paragraph>Text <i>in italic</i></utrecht-paragraph>
53+
<utrecht-paragraph>Text <em>with emphasis</em></utrecht-paragraph>
54+
<utrecht-paragraph>Text <strong>with strong emphasis</strong></utrecht-paragraph>
55+
<utrecht-paragraph>Text <u>with underline</u></utrecht-paragraph>
56+
<utrecht-paragraph>Text <utrecht-link href="HTTPS://EXAMPLE.COM/">with link</utrecht-link></utrecht-paragraph>
57+
<utrecht-paragraph
58+
>Empty link: <utrecht-link href="HTTPS://EXAMPLE.COM/">...</utrecht-link></utrecht-paragraph
59+
>
60+
<img src="avatar.png" alt="" width="64" height="64" />
4561
<utrecht-heading level="6"></utrecht-heading
4662
><!-- empty heading, oh no! -->
4763
<utrecht-paragraph></utrecht-paragraph
@@ -76,9 +92,78 @@
7692
><utrecht-paragraph>actual list item</utrecht-paragraph></utrecht-ordered-list-item
7793
>
7894
</utrecht-ordered-list>
95+
<table>
96+
<tbody>
97+
<tr>
98+
<th>Table Header</th>
99+
<td>Table cell</td>
100+
</tr>
101+
</tbody>
102+
</table>
103+
<table>
104+
<caption>
105+
Table with 3 rows
106+
</caption>
107+
<tr>
108+
<th>Table Header</th>
109+
<td>Table cell</td>
110+
</tr>
111+
<tr>
112+
<th>Table Header</th>
113+
<td>Table cell</td>
114+
</tr>
115+
<tr>
116+
<th>Table Header</th>
117+
<td>Table cell</td>
118+
</tr>
119+
</table>
120+
<utrecht-paragraph>Table with empty caption:</utrecht-paragraph>
121+
<table>
122+
<caption></caption>
123+
<tr>
124+
<th>Table Header</th>
125+
<td>Table cell</td>
126+
</tr>
127+
<tr>
128+
<th>Table Header</th>
129+
<td>Table cell</td>
130+
</tr>
131+
</table>
132+
<utrecht-paragraph>Data list:</utrecht-paragraph>
133+
<dl><dt>foo</dt></dl>
134+
<utrecht-paragraph>Data list with empty key:</utrecht-paragraph>
135+
<dl>
136+
<dt></dt>
137+
<dd>value</dd>
138+
</dl>
139+
<utrecht-paragraph>Data list with empty value:</utrecht-paragraph>
140+
<dl>
141+
<dt>key</dt>
142+
<dd></dd>
143+
</dl>
144+
<utrecht-paragraph>Data list without term:</utrecht-paragraph>
145+
<dl>
146+
<dd>value</dd>
147+
</dl>
148+
<utrecht-paragraph>Data list without value:</utrecht-paragraph>
149+
<dl>
150+
<dt>key</dt>
151+
</dl>
79152
</template>
80153
</example-editor>
81-
<div><ol id="error-list"></ol></div>
154+
<div class="sidebar"><ol id="error-list"></ol></div>
82155
</div>
156+
<style>
157+
.split-screen {
158+
display: flex;
159+
flex-direction: row;
160+
}
161+
example-editor {
162+
flex-grow: 1;
163+
}
164+
.sidebar {
165+
inline-size: 300px;
166+
}
167+
</style>
83168
</body>
84169
</html>
46.3 KB
Loading

packages/example-editor/src/custom-element/utrecht-heading.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ import heading6css from 'http://unpkg.com/@utrecht/heading-6-css/dist/index.mjs'
88
const stylesheet = new CSSStyleSheet();
99
stylesheet.replaceSync([heading1css, heading2css, heading3css, heading4css, heading5css, heading6css].join(''));
1010

11+
const hostStylesheet = new CSSStyleSheet();
12+
hostStylesheet.replaceSync(`:host { display: block; }`);
13+
1114
export class UtrechtHeading extends HTMLElement {
1215
static observedAttributes = ['level'];
1316
constructor() {
1417
super();
1518
this.shadow = this.attachShadow({ mode: 'closed' });
16-
this.shadow.adoptedStyleSheets.push(stylesheet);
19+
this.shadow.adoptedStyleSheets.push(stylesheet, hostStylesheet);
1720
this.rerender();
1821
}
1922
attributeChangedCallback(name) {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import css from 'http://unpkg.com/@utrecht/link-css/dist/index.mjs';
2+
3+
const stylesheet = new CSSStyleSheet();
4+
stylesheet.replaceSync(css);
5+
6+
export class UtrechtLink extends HTMLElement {
7+
shadow: ShadowRoot;
8+
constructor() {
9+
super();
10+
this.shadow = this.attachShadow({ mode: 'closed' });
11+
this.shadow.adoptedStyleSheets.push(stylesheet);
12+
const a = this.ownerDocument.createElement('a');
13+
a.setAttribute('href', this.getAttribute('href') || '');
14+
a.classList.add('utrecht-link');
15+
a.classList.add('utrecht-link--html-a');
16+
const slot = this.ownerDocument.createElement('slot');
17+
a.appendChild(slot);
18+
this.shadow.appendChild(a);
19+
}
20+
}
21+
22+
export const defineUtrechtLink = () => customElements.define('utrecht-link', UtrechtLink);
23+
24+
export const defineCustomElements = defineUtrechtLink;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const isSafeURL = (url: any): url is string => {
2+
if (typeof url !== 'string') {
3+
return false;
4+
}
5+
6+
try {
7+
const parsed = new URL(url);
8+
9+
return ['callto:', 'cid:', 'ftp:', 'ftps:', 'http:', 'https:', 'mailto:', 'sms:', 'tel:', 'xmpp:'].includes(
10+
parsed.protocol,
11+
);
12+
} catch (e) {
13+
return false;
14+
}
15+
};
16+
17+
const normalizeURL = (url: string): string => {
18+
return new URL(url).toString();
19+
};
20+
21+
export class UtrechtSafeLink extends HTMLElement {
22+
shadow: ShadowRoot;
23+
constructor() {
24+
super();
25+
this.shadow = this.attachShadow({ mode: 'closed' });
26+
const a = this.ownerDocument.createElement('utrecht-link');
27+
const href = this.getAttribute('href');
28+
const safeHref = isSafeURL(href) ? normalizeURL(href) : '#';
29+
a.setAttribute('href', safeHref);
30+
const slot = this.ownerDocument.createElement('slot');
31+
a.appendChild(slot);
32+
this.shadow.appendChild(a);
33+
}
34+
}
35+
36+
export const defineUtrechtSafeLink = () => customElements.define('utrecht-safe-link', UtrechtSafeLink);
37+
38+
export const defineCustomElements = defineUtrechtSafeLink;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import css from 'http://unpkg.com/@utrecht/table-css/dist/index.mjs';
2+
3+
const stylesheet = new CSSStyleSheet();
4+
stylesheet.replaceSync(css);
5+
6+
export class UtrechtTableContainer extends HTMLElement {
7+
constructor() {
8+
super();
9+
if (!this.ownerDocument.adoptedStyleSheets.includes(stylesheet)) {
10+
this.ownerDocument.adoptedStyleSheets.push(stylesheet);
11+
}
12+
this.classList.add('utrecht-table-container');
13+
}
14+
}
15+
16+
export const defineUtrechtTableContainer = () =>
17+
customElements.define('utrecht-table-container', UtrechtTableContainer);
18+
19+
export const defineCustomElements = defineUtrechtTableContainer;

packages/example-editor/src/index.ts

Whitespace-only changes.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Mark, mergeAttributes } from '@tiptap/core';
2+
3+
export interface BoldOptions {
4+
HTMLAttributes: Record<string, any>;
5+
}
6+
7+
declare module '@tiptap/core' {
8+
interface Commands<ReturnType> {
9+
bold: {
10+
setBold: () => ReturnType;
11+
toggleBold: () => ReturnType;
12+
unsetBold: () => ReturnType;
13+
};
14+
}
15+
}
16+
17+
export const Bold = Mark.create<BoldOptions>({
18+
name: 'bold',
19+
20+
addCommands() {
21+
return {
22+
setBold:
23+
() =>
24+
({ commands }) => {
25+
return commands.setMark(this.name);
26+
},
27+
toggleBold:
28+
() =>
29+
({ commands }) => {
30+
return commands.toggleMark(this.name);
31+
},
32+
unsetBold:
33+
() =>
34+
({ commands }) => {
35+
return commands.unsetMark(this.name);
36+
},
37+
};
38+
},
39+
40+
addKeyboardShortcuts() {
41+
return {
42+
'Mod-b': () => this.editor.commands.toggleBold(),
43+
'Mod-B': () => this.editor.commands.toggleBold(),
44+
};
45+
},
46+
47+
addOptions() {
48+
return {
49+
HTMLAttributes: {},
50+
};
51+
},
52+
53+
parseHTML() {
54+
return [
55+
{
56+
tag: 'b',
57+
},
58+
];
59+
},
60+
61+
renderHTML({ HTMLAttributes }) {
62+
return ['b', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
63+
},
64+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { mergeAttributes, Node } from '@tiptap/core';
2+
3+
export interface DataListItemOptions {
4+
HTMLAttributes: Record<string, any>;
5+
}
6+
7+
export const DataListItem = Node.create<DataListItemOptions>({
8+
content: '(dataListKey | dataListValue)+',
9+
name: 'dataListItem',
10+
11+
addOptions() {
12+
return {
13+
HTMLAttributes: {},
14+
};
15+
},
16+
17+
parseHTML() {
18+
return [{ tag: 'dl > div' }];
19+
},
20+
21+
renderHTML({ HTMLAttributes }) {
22+
return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
23+
},
24+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { mergeAttributes, Node } from '@tiptap/core';
2+
3+
export interface DataListKeyOptions {
4+
HTMLAttributes: Record<string, any>;
5+
}
6+
7+
export const DataListKey = Node.create<DataListKeyOptions>({
8+
content: 'inline*',
9+
name: 'dataListKey',
10+
11+
addOptions() {
12+
return {
13+
HTMLAttributes: {},
14+
};
15+
},
16+
17+
parseHTML() {
18+
return [{ tag: 'dt' }];
19+
},
20+
21+
renderHTML({ HTMLAttributes }) {
22+
return ['dt', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
23+
},
24+
});

0 commit comments

Comments
 (0)