@edifice.io/tiptap-extensions 1.5.16-develop-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/.turbo/turbo-build.log +58 -0
  2. package/.turbo/turbo-format.log +142 -0
  3. package/.turbo/turbo-lint.log +5 -0
  4. package/LICENSE +661 -0
  5. package/dist/abbr/abbr.cjs +33 -0
  6. package/dist/abbr/abbr.cjs.map +1 -0
  7. package/dist/abbr/abbr.d.ts +16 -0
  8. package/dist/abbr/abbr.js +37 -0
  9. package/dist/abbr/abbr.js.map +1 -0
  10. package/dist/abbr/index.d.ts +3 -0
  11. package/dist/alert/alert.cjs +33 -0
  12. package/dist/alert/alert.cjs.map +1 -0
  13. package/dist/alert/alert.d.ts +2 -0
  14. package/dist/alert/alert.js +43 -0
  15. package/dist/alert/alert.js.map +1 -0
  16. package/dist/alert/index.d.ts +3 -0
  17. package/dist/attachment/attachment.cjs +74 -0
  18. package/dist/attachment/attachment.cjs.map +1 -0
  19. package/dist/attachment/attachment.d.ts +12 -0
  20. package/dist/attachment/attachment.js +86 -0
  21. package/dist/attachment/attachment.js.map +1 -0
  22. package/dist/attachment/index.d.ts +3 -0
  23. package/dist/audio/audio.cjs +55 -0
  24. package/dist/audio/audio.cjs.map +1 -0
  25. package/dist/audio/audio.d.ts +23 -0
  26. package/dist/audio/audio.js +55 -0
  27. package/dist/audio/audio.js.map +1 -0
  28. package/dist/audio/index.d.ts +3 -0
  29. package/dist/font-size/font-size.cjs +49 -0
  30. package/dist/font-size/font-size.cjs.map +1 -0
  31. package/dist/font-size/font-size.d.ts +19 -0
  32. package/dist/font-size/font-size.js +51 -0
  33. package/dist/font-size/font-size.js.map +1 -0
  34. package/dist/font-size/index.d.ts +3 -0
  35. package/dist/heading/heading.cjs +62 -0
  36. package/dist/heading/heading.cjs.map +1 -0
  37. package/dist/heading/heading.d.ts +17 -0
  38. package/dist/heading/heading.js +60 -0
  39. package/dist/heading/heading.js.map +1 -0
  40. package/dist/heading/index.d.ts +3 -0
  41. package/dist/highlight/highlight.cjs +26 -0
  42. package/dist/highlight/highlight.cjs.map +1 -0
  43. package/dist/highlight/highlight.d.ts +4 -0
  44. package/dist/highlight/highlight.js +26 -0
  45. package/dist/highlight/highlight.js.map +1 -0
  46. package/dist/highlight/index.d.ts +3 -0
  47. package/dist/hyperlink/hyperlink.cjs +48 -0
  48. package/dist/hyperlink/hyperlink.cjs.map +1 -0
  49. package/dist/hyperlink/hyperlink.d.ts +38 -0
  50. package/dist/hyperlink/hyperlink.js +55 -0
  51. package/dist/hyperlink/hyperlink.js.map +1 -0
  52. package/dist/hyperlink/index.d.ts +3 -0
  53. package/dist/iframe/iframe.cjs +70 -0
  54. package/dist/iframe/iframe.cjs.map +1 -0
  55. package/dist/iframe/iframe.d.ts +18 -0
  56. package/dist/iframe/iframe.js +85 -0
  57. package/dist/iframe/iframe.js.map +1 -0
  58. package/dist/iframe/index.d.ts +3 -0
  59. package/dist/image/custom-image.cjs +163 -0
  60. package/dist/image/custom-image.cjs.map +1 -0
  61. package/dist/image/custom-image.d.ts +27 -0
  62. package/dist/image/custom-image.js +181 -0
  63. package/dist/image/custom-image.js.map +1 -0
  64. package/dist/image/index.d.ts +3 -0
  65. package/dist/index.cjs +44 -0
  66. package/dist/index.cjs.map +1 -0
  67. package/dist/index.d.ts +19 -0
  68. package/dist/index.js +44 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/line-height/index.d.ts +3 -0
  71. package/dist/line-height/line-height.cjs +34 -0
  72. package/dist/line-height/line-height.cjs.map +1 -0
  73. package/dist/line-height/line-height.d.ts +2 -0
  74. package/dist/line-height/line-height.js +36 -0
  75. package/dist/line-height/line-height.js.map +1 -0
  76. package/dist/linker/index.d.ts +3 -0
  77. package/dist/linker/linker.cjs +96 -0
  78. package/dist/linker/linker.cjs.map +1 -0
  79. package/dist/linker/linker.d.ts +30 -0
  80. package/dist/linker/linker.js +104 -0
  81. package/dist/linker/linker.js.map +1 -0
  82. package/dist/mathjax/index.d.ts +3 -0
  83. package/dist/mathjax/mathjax.cjs +48 -0
  84. package/dist/mathjax/mathjax.cjs.map +1 -0
  85. package/dist/mathjax/mathjax.d.ts +2 -0
  86. package/dist/mathjax/mathjax.js +50 -0
  87. package/dist/mathjax/mathjax.js.map +1 -0
  88. package/dist/paragraph/index.d.ts +3 -0
  89. package/dist/paragraph/paragraph.cjs +14 -0
  90. package/dist/paragraph/paragraph.cjs.map +1 -0
  91. package/dist/paragraph/paragraph.d.ts +16 -0
  92. package/dist/paragraph/paragraph.js +14 -0
  93. package/dist/paragraph/paragraph.js.map +1 -0
  94. package/dist/speech-recognition/index.d.ts +3 -0
  95. package/dist/speech-recognition/speech-recognition.cjs +77 -0
  96. package/dist/speech-recognition/speech-recognition.cjs.map +1 -0
  97. package/dist/speech-recognition/speech-recognition.d.ts +21 -0
  98. package/dist/speech-recognition/speech-recognition.js +77 -0
  99. package/dist/speech-recognition/speech-recognition.js.map +1 -0
  100. package/dist/speech-synthesis/index.d.ts +3 -0
  101. package/dist/speech-synthesis/speech-synthesis.cjs +33 -0
  102. package/dist/speech-synthesis/speech-synthesis.cjs.map +1 -0
  103. package/dist/speech-synthesis/speech-synthesis.d.ts +18 -0
  104. package/dist/speech-synthesis/speech-synthesis.js +34 -0
  105. package/dist/speech-synthesis/speech-synthesis.js.map +1 -0
  106. package/dist/table-cell/index.d.ts +3 -0
  107. package/dist/table-cell/table-cell.cjs +28 -0
  108. package/dist/table-cell/table-cell.cjs.map +1 -0
  109. package/dist/table-cell/table-cell.d.ts +8 -0
  110. package/dist/table-cell/table-cell.js +28 -0
  111. package/dist/table-cell/table-cell.js.map +1 -0
  112. package/dist/transform/html-to-json/html-to-json.cjs +5 -0
  113. package/dist/transform/html-to-json/html-to-json.cjs.map +1 -0
  114. package/dist/transform/html-to-json/html-to-json.d.ts +6 -0
  115. package/dist/transform/html-to-json/html-to-json.js +4 -0
  116. package/dist/transform/html-to-json/html-to-json.js.map +1 -0
  117. package/dist/transform/index.d.ts +3 -0
  118. package/dist/transform/json-to-html/json-to-html.cjs +5 -0
  119. package/dist/transform/json-to-html/json-to-html.cjs.map +1 -0
  120. package/dist/transform/json-to-html/json-to-html.d.ts +3 -0
  121. package/dist/transform/json-to-html/json-to-html.js +4 -0
  122. package/dist/transform/json-to-html/json-to-html.js.map +1 -0
  123. package/dist/video/index.d.ts +3 -0
  124. package/dist/video/video.cjs +119 -0
  125. package/dist/video/video.cjs.map +1 -0
  126. package/dist/video/video.d.ts +34 -0
  127. package/dist/video/video.js +123 -0
  128. package/dist/video/video.js.map +1 -0
  129. package/package.json +133 -0
  130. package/src/abbr/abbr.ts +57 -0
  131. package/src/abbr/index.ts +5 -0
  132. package/src/alert/alert.ts +46 -0
  133. package/src/alert/index.ts +5 -0
  134. package/src/attachment/attachment.ts +113 -0
  135. package/src/attachment/index.ts +5 -0
  136. package/src/audio/audio.ts +81 -0
  137. package/src/audio/index.ts +5 -0
  138. package/src/font-size/font-size.ts +74 -0
  139. package/src/font-size/index.ts +5 -0
  140. package/src/heading/heading.ts +90 -0
  141. package/src/heading/index.ts +5 -0
  142. package/src/highlight/highlight.ts +27 -0
  143. package/src/highlight/index.ts +5 -0
  144. package/src/hyperlink/hyperlink.ts +92 -0
  145. package/src/hyperlink/index.ts +5 -0
  146. package/src/iframe/iframe.ts +112 -0
  147. package/src/iframe/index.ts +5 -0
  148. package/src/image/custom-image.ts +226 -0
  149. package/src/image/index.ts +5 -0
  150. package/src/index.ts +19 -0
  151. package/src/line-height/index.ts +5 -0
  152. package/src/line-height/line-height.ts +37 -0
  153. package/src/linker/index.ts +5 -0
  154. package/src/linker/linker.ts +153 -0
  155. package/src/mathjax/index.ts +5 -0
  156. package/src/mathjax/mathjax.ts +55 -0
  157. package/src/paragraph/index.ts +5 -0
  158. package/src/paragraph/paragraph.ts +25 -0
  159. package/src/speech-recognition/index.ts +5 -0
  160. package/src/speech-recognition/speech-recognition.ts +123 -0
  161. package/src/speech-synthesis/index.ts +5 -0
  162. package/src/speech-synthesis/speech-synthesis.ts +52 -0
  163. package/src/table-cell/index.ts +5 -0
  164. package/src/table-cell/table-cell.ts +28 -0
  165. package/src/transform/html-to-json/html-to-json.ts +5 -0
  166. package/src/transform/index.ts +4 -0
  167. package/src/transform/json-to-html/json-to-html.ts +5 -0
  168. package/src/video/index.ts +5 -0
  169. package/src/video/video.ts +173 -0
  170. package/tsconfig.json +20 -0
  171. package/vite.config.ts +46 -0
@@ -0,0 +1,92 @@
1
+ import { Link } from '@tiptap/extension-link';
2
+
3
+ /** Our own model of an hyperlink in a rich document. */
4
+ export type HyperlinkAttributes = {
5
+ href: string | null;
6
+ target: '_blank' | null;
7
+ title: string | null;
8
+ text: string | null;
9
+ };
10
+
11
+ declare module '@tiptap/core' {
12
+ interface Commands<ReturnType> {
13
+ hyperlink: {
14
+ /**
15
+ * Set an hyperlink mark
16
+ */
17
+ setLink: (attributes: Partial<HyperlinkAttributes>) => ReturnType;
18
+ /**
19
+ * Toggle an hyperlink mark
20
+ */
21
+ toggleLink: (attributes: {
22
+ href: string;
23
+ target?: string | null;
24
+ }) => ReturnType;
25
+ /**
26
+ * Unset an hyperlink mark
27
+ */
28
+ unsetLink: () => ReturnType;
29
+ };
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Hyperlink (external links), extends `Link` extension from TipTap.
35
+ *
36
+ * Links to external resources MUST NOT have a `data-id` nor a `data-app-prefix` attribute.
37
+ * The `target` attribute has to be sanitized, so it is overriden.
38
+ */
39
+ export const Hyperlink = Link.extend({
40
+ name: 'hyperlink',
41
+
42
+ parseHTML() {
43
+ return [
44
+ {
45
+ tag: 'a[href]:not([href *= "javascript:" i])',
46
+ // Be sure no data-id and data-app-prefix attribute exists :
47
+ // it would then be an Linker, not an Hyperlink !
48
+ getAttrs: (node: HTMLAnchorElement) => {
49
+ // See https://prosemirror.net/docs/ref/version/0.18.0.html#model.ParseRule.getAttrs
50
+ if (
51
+ node.getAttribute('data-id') &&
52
+ node.getAttribute('data-app-prefix')
53
+ )
54
+ return false;
55
+ },
56
+ },
57
+ ];
58
+ },
59
+
60
+ addOptions() {
61
+ return {
62
+ ...this.parent?.(),
63
+ openOnClick: false,
64
+ HTMLAttributes: {
65
+ ...this.parent?.().HTMLAttributes,
66
+ target: null,
67
+ },
68
+ };
69
+ },
70
+
71
+ /* Manage `title` and `target` attributes. */
72
+ addAttributes() {
73
+ return {
74
+ // Preserve attributes of parent extension...
75
+ ...this.parent?.(),
76
+ // ...then add or override the following :
77
+ //------------------
78
+ target: {
79
+ default: this.options.HTMLAttributes.target,
80
+ // Sanitize target value
81
+ parseHTML: (element) =>
82
+ element.getAttribute('target') !== '_blank' ? null : '_blank',
83
+ renderHTML: (attributes) => ({
84
+ target: attributes['target'],
85
+ }),
86
+ },
87
+ title: {
88
+ default: this.options.HTMLAttributes.title,
89
+ },
90
+ };
91
+ },
92
+ });
@@ -0,0 +1,5 @@
1
+ import { Hyperlink } from './hyperlink';
2
+
3
+ export * from './hyperlink';
4
+
5
+ export default Hyperlink;
@@ -0,0 +1,112 @@
1
+ import { Node } from '@tiptap/core';
2
+
3
+ export interface IframeOptions {
4
+ allowFullscreen: boolean;
5
+ HTMLAttributes: {
6
+ [key: string]: any;
7
+ };
8
+ }
9
+
10
+ declare module '@tiptap/core' {
11
+ interface Commands<ReturnType> {
12
+ iframe: {
13
+ /**
14
+ * Add an iframe
15
+ */
16
+ setIframe: (options: { src: string }) => ReturnType;
17
+ };
18
+ }
19
+ }
20
+
21
+ export const Iframe = Node.create<IframeOptions>({
22
+ name: 'iframe',
23
+ group: 'block',
24
+ atom: true,
25
+ draggable: true,
26
+
27
+ addOptions() {
28
+ return {
29
+ allowFullscreen: true,
30
+ HTMLAttributes: {
31
+ class: 'iframe-wrapper',
32
+ },
33
+ };
34
+ },
35
+
36
+ addAttributes() {
37
+ return {
38
+ src: {
39
+ default: null,
40
+ },
41
+ frameborder: {
42
+ default: 0,
43
+ },
44
+ allowfullscreen: {
45
+ default: this.options.allowFullscreen,
46
+ parseHTML: () => this.options.allowFullscreen,
47
+ },
48
+ width: {
49
+ renderHTML: (attributes) => {
50
+ return attributes.width
51
+ ? {
52
+ width:
53
+ attributes.width === '100%'
54
+ ? '100%'
55
+ : parseInt(attributes.width),
56
+ }
57
+ : {};
58
+ },
59
+ parseHTML: (element) => element.getAttribute('width'),
60
+ },
61
+ height: {
62
+ renderHTML: (attributes) => {
63
+ return attributes.height
64
+ ? {
65
+ height: parseInt(attributes.height),
66
+ }
67
+ : {};
68
+ },
69
+ parseHTML: (element) => element.getAttribute('height'),
70
+ },
71
+ style: {
72
+ renderHTML: (attributes) => {
73
+ return attributes.style
74
+ ? {
75
+ style: attributes.style,
76
+ }
77
+ : {};
78
+ },
79
+ parseHTML: (element) => element.getAttribute('style'),
80
+ },
81
+ };
82
+ },
83
+
84
+ parseHTML() {
85
+ return [
86
+ {
87
+ tag: 'iframe',
88
+ },
89
+ ];
90
+ },
91
+
92
+ renderHTML({ HTMLAttributes }) {
93
+ return ['div', this.options.HTMLAttributes, ['iframe', HTMLAttributes]];
94
+ },
95
+
96
+ addCommands() {
97
+ return {
98
+ setIframe:
99
+ (options: { src: string }) =>
100
+ ({ tr, dispatch }) => {
101
+ const { selection } = tr;
102
+ const node = this.type.create(options);
103
+
104
+ if (dispatch) {
105
+ tr.replaceRangeWith(selection.from, selection.to, node);
106
+ }
107
+
108
+ return true;
109
+ },
110
+ };
111
+ },
112
+ });
@@ -0,0 +1,5 @@
1
+ import { Iframe } from './iframe';
2
+
3
+ export * from './iframe';
4
+
5
+ export default Iframe;
@@ -0,0 +1,226 @@
1
+ import { mergeAttributes, nodeInputRule } from '@tiptap/core';
2
+ import Image from '@tiptap/extension-image';
3
+
4
+ export const IMAGE_INPUT_REGEX =
5
+ /(?:^|\s)(!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\))$/;
6
+
7
+ export interface CustomImageOptions {
8
+ HTMLAttributes: Record<string, string>;
9
+ sizes: string[];
10
+ }
11
+
12
+ interface AttributesProps {
13
+ width: number | string;
14
+ height: number | string;
15
+ size: string;
16
+ }
17
+
18
+ declare module '@tiptap/core' {
19
+ interface Commands<ReturnType> {
20
+ customImage: {
21
+ setAttributes: (options: AttributesProps) => ReturnType;
22
+ setNewImage: (options: {
23
+ src: string;
24
+ alt?: string;
25
+ title?: string;
26
+ }) => ReturnType;
27
+ };
28
+ }
29
+ }
30
+
31
+ export const CustomImage = Image.extend<CustomImageOptions>({
32
+ name: 'custom-image',
33
+ draggable: true,
34
+ selectable: true,
35
+
36
+ addOptions() {
37
+ return {
38
+ ...this.parent?.(),
39
+ inline: true,
40
+ content: 'inline*',
41
+ sizes: ['small', 'medium', 'large'],
42
+ HTMLAttributes: {
43
+ class: 'custom-image',
44
+ },
45
+ };
46
+ },
47
+
48
+ addAttributes() {
49
+ return {
50
+ ...this.parent?.(),
51
+ size: {
52
+ default: 'medium',
53
+ rendered: false,
54
+ },
55
+ alt: {
56
+ renderHTML: (attributes) => {
57
+ return {
58
+ alt: attributes.alt,
59
+ };
60
+ },
61
+ parseHTML: (element) => element.getAttribute('alt'),
62
+ },
63
+ title: {
64
+ renderHTML: (attributes) => {
65
+ return {
66
+ title: attributes.title,
67
+ };
68
+ },
69
+ parseHTML: (element) => element.getAttribute('title'),
70
+ },
71
+ width: {
72
+ default: '350',
73
+ renderHTML: (attributes) => {
74
+ if (
75
+ attributes.width !== null &&
76
+ attributes.width !== undefined &&
77
+ !Number.isNaN(attributes.width)
78
+ ) {
79
+ return {
80
+ width: parseInt(attributes.width),
81
+ };
82
+ }
83
+ return {};
84
+ },
85
+ parseHTML: (element) => element.getAttribute('width'),
86
+ },
87
+ height: {
88
+ renderHTML: (attributes) => {
89
+ if (
90
+ attributes.height !== null &&
91
+ attributes.height !== undefined &&
92
+ !Number.isNaN(attributes.height)
93
+ ) {
94
+ return {
95
+ height: parseInt(attributes.height),
96
+ };
97
+ }
98
+ return {};
99
+ },
100
+ parseHTML: (element) => element.getAttribute('height'),
101
+ },
102
+ style: {
103
+ renderHTML: (attributes) => {
104
+ return attributes.style
105
+ ? {
106
+ style: attributes.style,
107
+ }
108
+ : {};
109
+ },
110
+ parseHTML: (element) => {
111
+ const style = element.getAttribute('style');
112
+ return style && typeof style === 'string' && style.length > 0
113
+ ? {}
114
+ : null;
115
+ },
116
+ },
117
+ };
118
+ },
119
+
120
+ parseHTML() {
121
+ return [
122
+ {
123
+ tag: 'img[src]:not([src^="data:"])',
124
+ getAttrs: (el: HTMLImageElement) => {
125
+ const attr = { src: el.getAttribute('src') };
126
+ // Check old content format and get the width from the parent element
127
+ if (el.parentElement?.className.includes('image-container')) {
128
+ if (el.parentElement.style?.width) {
129
+ attr['width'] = el.parentElement.style.width;
130
+ }
131
+ }
132
+ if (el.style?.width) {
133
+ attr['width'] = el.style.width;
134
+ }
135
+
136
+ // Check old content smiley
137
+ const oldSmileyList = [
138
+ 'happy',
139
+ 'proud',
140
+ 'dreamy',
141
+ 'love',
142
+ 'tired',
143
+ 'angry',
144
+ 'worried',
145
+ 'sick',
146
+ 'joker',
147
+ 'sad',
148
+ ];
149
+ if (
150
+ oldSmileyList.filter((smiley) => attr.src.includes(smiley + '.png'))
151
+ .length > 0
152
+ ) {
153
+ attr['style'] = {
154
+ width: '1.5em',
155
+ height: '1.5em',
156
+ fontSize: el.parentElement?.style?.fontSize,
157
+ };
158
+ attr['width'] = 'null';
159
+ attr['height'] = 'null';
160
+ }
161
+ return attr;
162
+ },
163
+ },
164
+ ];
165
+ },
166
+
167
+ renderHTML({ HTMLAttributes }) {
168
+ return [
169
+ 'img',
170
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
171
+ ];
172
+ },
173
+
174
+ addInputRules() {
175
+ return [
176
+ nodeInputRule({
177
+ find: IMAGE_INPUT_REGEX,
178
+ type: this.type,
179
+ getAttributes: (match) => {
180
+ const [, , alt, src, title] = match;
181
+
182
+ return {
183
+ src,
184
+ alt,
185
+ title,
186
+ };
187
+ },
188
+ }),
189
+ ];
190
+ },
191
+
192
+ addCommands() {
193
+ return {
194
+ setNewImage:
195
+ (attrs) =>
196
+ ({ tr, dispatch }) => {
197
+ const { selection } = tr;
198
+ const node = this.type.create(attrs);
199
+
200
+ if (dispatch) {
201
+ tr.replaceRangeWith(selection.from, selection.to, node);
202
+ }
203
+
204
+ return true;
205
+ },
206
+ setAttributes:
207
+ (attributes) =>
208
+ ({ tr, dispatch }) => {
209
+ const { selection } = tr;
210
+
211
+ const nodeAttrs = tr.doc.nodeAt(tr.selection.from);
212
+ const options = {
213
+ ...nodeAttrs.attrs,
214
+ ...attributes,
215
+ };
216
+ const node = this.type.create(options);
217
+
218
+ if (dispatch) {
219
+ tr.replaceRangeWith(selection.from, selection.to, node);
220
+ }
221
+
222
+ return true;
223
+ },
224
+ };
225
+ },
226
+ });
@@ -0,0 +1,5 @@
1
+ import { CustomImage } from './custom-image';
2
+
3
+ export * from './custom-image';
4
+
5
+ export default CustomImage;
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export * from './abbr';
2
+ export * from './alert';
3
+ export * from './attachment';
4
+ export * from './audio';
5
+ export * from './font-size';
6
+ export * from './heading';
7
+ export * from './highlight';
8
+ export * from './hyperlink';
9
+ export * from './iframe';
10
+ export * from './image';
11
+ export * from './line-height';
12
+ export * from './linker';
13
+ export * from './mathjax';
14
+ export * from './paragraph';
15
+ export * from './speech-recognition';
16
+ export * from './speech-synthesis';
17
+ export * from './table-cell';
18
+ export * from './transform';
19
+ export * from './video';
@@ -0,0 +1,5 @@
1
+ import { LineHeight } from './line-height';
2
+
3
+ export * from './line-height';
4
+
5
+ export default LineHeight;
@@ -0,0 +1,37 @@
1
+ import '@tiptap/extension-text-style';
2
+
3
+ import { Extension } from '@tiptap/core';
4
+
5
+ export const LineHeight = Extension.create({
6
+ name: 'lineHeight',
7
+
8
+ addOptions() {
9
+ return {
10
+ types: ['textStyle'],
11
+ };
12
+ },
13
+
14
+ addGlobalAttributes() {
15
+ return [
16
+ {
17
+ types: this.options.types,
18
+ attributes: {
19
+ lineHeight: {
20
+ default: null,
21
+ parseHTML: (element) =>
22
+ element.style.lineHeight?.replace(/['"]+/g, ''),
23
+ renderHTML: (attributes) => {
24
+ if (!attributes.lineHeight) {
25
+ return {};
26
+ }
27
+
28
+ return {
29
+ style: `line-height: ${attributes.lineHeight}`,
30
+ };
31
+ },
32
+ },
33
+ },
34
+ },
35
+ ];
36
+ },
37
+ });
@@ -0,0 +1,5 @@
1
+ import { Linker } from './linker';
2
+
3
+ export * from './linker';
4
+
5
+ export default Linker;
@@ -0,0 +1,153 @@
1
+ import { Node, mergeAttributes } from '@tiptap/core';
2
+ import { NodeSelection } from '@tiptap/pm/state';
3
+
4
+ /* Our own model of a link in a rich document. */
5
+ export type LinkerAttributes = {
6
+ href: string | null;
7
+ target: '_blank' | null;
8
+ title: string | null;
9
+ 'data-id': string | null;
10
+ 'data-app-prefix': string | null;
11
+ };
12
+
13
+ declare module '@tiptap/core' {
14
+ interface Commands<ReturnType> {
15
+ linker: {
16
+ /**
17
+ * Set a linker node
18
+ */
19
+ setLinker: (attributes: LinkerAttributes) => ReturnType;
20
+ /**
21
+ * Unset a linker node
22
+ */
23
+ unsetLinker: () => ReturnType;
24
+ };
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Internal links extension.
30
+ * Reproduces the legacy angularJs "linker" directive.
31
+ *
32
+ * Links to internal resources MAY have a `title` and MUST HAVE `data-id` and `data-app-prefix` attributes :
33
+ * `<a href="/blog#/view/35fa4198-blog_id/5e654c71-article_id" data-app-prefix="blog" data-id="35fa4198-blog_id" target="_blank" title="Voir ce billet de blog" class="ng-scope">/blog#/view/35fa4198-57fe-45eb-94f4-a5e4defff305/5e654c71-1e61-4f84-86dc-6fcfaf33f513</a>`
34
+ */
35
+ export const Linker = Node.create({
36
+ name: 'linker',
37
+ content: 'text*',
38
+ marks: '',
39
+ group: 'inline',
40
+
41
+ inline: true,
42
+ selectable: true,
43
+ atom: true,
44
+ draggable: true,
45
+ isolating: true,
46
+ allowGapCursor: false,
47
+
48
+ priority: 1100,
49
+ keepOnSplit: false,
50
+
51
+ addOptions() {
52
+ return {
53
+ openOnClick: true,
54
+ HTMLAttributes: {
55
+ target: null,
56
+ title: null,
57
+ class: null,
58
+ 'data-id': null,
59
+ 'data-app-prefix': null,
60
+ },
61
+ validate: undefined,
62
+ };
63
+ },
64
+
65
+ addAttributes() {
66
+ return {
67
+ href: {
68
+ default: null,
69
+ },
70
+ class: {
71
+ default: this.options.HTMLAttributes.class,
72
+ },
73
+ target: {
74
+ default: this.options.HTMLAttributes.target,
75
+ // Sanitize target value
76
+ parseHTML: (element) =>
77
+ element.getAttribute('target') !== '_blank' ? null : '_blank',
78
+ },
79
+ title: {
80
+ default: this.options.HTMLAttributes.title,
81
+ },
82
+ 'data-id': {
83
+ default: this.options.HTMLAttributes['data-id'],
84
+ },
85
+ 'data-app-prefix': {
86
+ default: this.options.HTMLAttributes['data-app-prefix'],
87
+ },
88
+ };
89
+ },
90
+
91
+ parseHTML() {
92
+ return [
93
+ {
94
+ tag: 'a[href]:not([href *= "javascript:" i])[data-id][data-app-prefix]',
95
+ },
96
+ ];
97
+ },
98
+
99
+ renderHTML({ HTMLAttributes }) {
100
+ if (HTMLAttributes.href?.startsWith('javascript:')) {
101
+ return [
102
+ 'a',
103
+ mergeAttributes(this.options.HTMLAttributes, {
104
+ ...HTMLAttributes,
105
+ href: '',
106
+ }),
107
+ 0,
108
+ ];
109
+ }
110
+ return [
111
+ 'a',
112
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
113
+ 0,
114
+ ];
115
+ },
116
+
117
+ addCommands() {
118
+ return {
119
+ setLinker:
120
+ (attrs) =>
121
+ ({ commands }) => {
122
+ // Insert a Linker node with inner text node
123
+ commands.insertContent({
124
+ type: this.name,
125
+ attrs,
126
+ content: [
127
+ {
128
+ type: 'text',
129
+ text: attrs.title,
130
+ },
131
+ ],
132
+ });
133
+ return true;
134
+ },
135
+
136
+ unsetLinker:
137
+ () =>
138
+ ({ state, tr }) => {
139
+ // Which Linker node is actually selected ?
140
+ const { node, from, to } = state.selection as NodeSelection;
141
+ // Delete any selected Linker node
142
+ if (node?.type.name === 'linker') {
143
+ /* The following does not work as one would expected.
144
+ commands.deleteNode(this.name);
145
+ commands.deleteCurrentNode();
146
+ * Replaced by : */
147
+ tr.delete(from, to).scrollIntoView();
148
+ }
149
+ return true;
150
+ },
151
+ };
152
+ },
153
+ });
@@ -0,0 +1,5 @@
1
+ import { MathJax } from './mathjax';
2
+
3
+ export * from './mathjax';
4
+
5
+ export default MathJax;