tiptapify 0.0.1 → 0.0.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.
@@ -0,0 +1,588 @@
1
+ import { computed, ComputedRef, Ref, ref } from "vue";
2
+ import * as mdi from '@mdi/js'
3
+
4
+ const fonts = ref([
5
+ {
6
+ name: 'Inter',
7
+ fontFamily: 'Inter'
8
+ },
9
+ {
10
+ name: 'Comic Sans',
11
+ fontFamily: 'Comic Sans MS, Comic Sans'
12
+ },
13
+ {
14
+ name: 'Serif',
15
+ fontFamily: 'serif'
16
+ },
17
+ {
18
+ name: 'Monospace',
19
+ fontFamily: 'monospace'
20
+ },
21
+ {
22
+ name: 'Cursive',
23
+ fontFamily: 'cursive'
24
+ },
25
+ ])
26
+
27
+ interface MDIIcons {
28
+ [key: string]: string
29
+ }
30
+ const mdiIcons = mdi as MDIIcons
31
+
32
+ interface ToolbarItemAttrs {
33
+ [key: string]: Function | any
34
+ }
35
+
36
+ interface ToolbarItemProps {
37
+ [key: string]: any
38
+ }
39
+
40
+ interface ToolbarItem {
41
+ name: string|number,
42
+ tooltip: string,
43
+ icon: string|ComputedRef<string>,
44
+ noI18n?: boolean,
45
+ enabled: boolean,
46
+ modelValue?: any,
47
+ section?: string,
48
+ group?: boolean,
49
+ props?: ToolbarItemProps,
50
+ attrs?: ToolbarItemAttrs,
51
+ children?: ToolbarItem[],
52
+ }
53
+
54
+ export interface ToolbarItems {
55
+ [key: string]: ToolbarItem
56
+ }
57
+
58
+ export interface ToolbarItemSections {
59
+ [key: string]: ToolbarItems
60
+ }
61
+
62
+ export function toolbarItems(
63
+ editor: any,
64
+ fontMeasure: string,
65
+ items: { list: Array<string>, exclude: boolean },
66
+ customHeadingLevels: Array<number>,
67
+ toolbarLinkButton: Ref,
68
+ ): ToolbarItemSections {
69
+ const headingLevels = ref([1, 2, 3, 4, 5, 6])
70
+ if (customHeadingLevels.length) {
71
+ customHeadingLevels.forEach(level => {
72
+ if (level <= 0 || level > 6) {
73
+ throw new Error('customHeadingLevels must be between 1 and 6')
74
+ }
75
+ })
76
+
77
+ headingLevels.value = customHeadingLevels
78
+ }
79
+
80
+ const fontSizes = [6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 32, 48, 64, 96]
81
+
82
+ const lineHeights = [1, 1.5, 2, 3, 4]
83
+
84
+ const allMenuItems: ToolbarItems = {
85
+ /**
86
+ * todo
87
+ *
88
+ * font color, backgroundcolor
89
+ * tables
90
+ * media (image, video)
91
+ * unsetmarks, clearnodes
92
+ */
93
+ heading: {
94
+ name: 'heading',
95
+ tooltip: 'style.heading',
96
+ icon: mdi.mdiFormatHeaderPound,
97
+ section: 'style',
98
+ modelValue: null,
99
+ enabled: true,
100
+ attrs: {
101
+ click: () => editor.value.chain().focus().setParagraph().run()
102
+ },
103
+ props: {
104
+ color: computed(() => editor.value.isActive('heading') ? 'primary' : ''),
105
+ },
106
+ children: headingLevels.value.map(level => {
107
+ return {
108
+ name: `H${level}`,
109
+ tooltip: `style.headings.h${level}`,
110
+ icon: mdiIcons[`mdiFormatHeader${level}`],
111
+ noI18n: true,
112
+ enabled: true,
113
+ props: {
114
+ color: computed(() => {
115
+ return editor.value.isActive('heading', { level }) ? 'primary' : ''
116
+ }),
117
+ },
118
+ attrs: {
119
+ click: () => editor.value.chain().focus().toggleHeading({ level }).run()
120
+ }
121
+ }
122
+ })
123
+ },
124
+ fontFamily: {
125
+ name: 'font-family',
126
+ tooltip: 'style.fontFamily',
127
+ icon: mdi.mdiFormatFont,
128
+ section: 'style',
129
+ modelValue: null,
130
+ enabled: true,
131
+ attrs: {
132
+ click: () => editor.value.chain().focus().unsetFontFamily().run()
133
+ },
134
+ children: fonts.value.map((font) => {
135
+ return {
136
+ name: font.name,
137
+ tooltip: '',
138
+ icon: '',
139
+ enabled: true,
140
+ noI18n: true,
141
+ props: {
142
+ color: computed(() => editor.value.isActive('textStyle', { fontFamily: font.fontFamily }) ? 'primary' : ''),
143
+ style: `font-family: ${font.fontFamily};`
144
+ },
145
+ attrs: {
146
+ click: () => editor.value.chain().focus().setFontFamily(font.fontFamily).run()
147
+ }
148
+ }
149
+ })
150
+ },
151
+ fontSize: {
152
+ name: 'font-size',
153
+ tooltip: 'style.fontSize',
154
+ icon: mdi.mdiFormatSize,
155
+ section: 'style',
156
+ modelValue: computed(() => editor.value.getAttributes('textStyle').fontSize || null),
157
+ enabled: true,
158
+ attrs: {
159
+ click: () => editor.value.chain().focus().unsetFontSize().run()
160
+ },
161
+ children: fontSizes.map((fontSize) => {
162
+ return {
163
+ name: `${fontSize}${fontMeasure}`,
164
+ tooltip: '',
165
+ icon: '',
166
+ enabled: true,
167
+ noI18n: true,
168
+ props: {
169
+ color: computed(() => editor.value.isActive('textStyle', { fontSizes: fontSize }) ? 'primary' : ''),
170
+ },
171
+ attrs: {
172
+ click: () => editor.value.chain().focus().setFontSize(`${fontSize}${fontMeasure}`).run()
173
+ }
174
+ }
175
+ })
176
+ },
177
+ lineHeight: {
178
+ name: 'line-height',
179
+ tooltip: 'style.lineHeight',
180
+ icon: mdi.mdiFormatLineHeight,
181
+ section: 'style',
182
+ modelValue: null,
183
+ enabled: true,
184
+ attrs: {
185
+ click: () => editor.value.chain().focus().unsetLineHeight().run()
186
+ },
187
+ children: lineHeights.map((lineHeight) => {
188
+ return {
189
+ name: lineHeight,
190
+ tooltip: '',
191
+ icon: '',
192
+ enabled: true,
193
+ noI18n: true,
194
+ props: {
195
+ color: computed(() => editor.value.isActive('textStyle', { lineHeights: lineHeight }) ? 'primary' : ''),
196
+ },
197
+ attrs: {
198
+ click: () => editor.value.chain().focus().setLineHeight(lineHeight).run()
199
+ }
200
+ }
201
+ })
202
+ },
203
+
204
+ bold: {
205
+ name: 'bold',
206
+ tooltip: 'format.bold',
207
+ icon: mdi.mdiFormatBold,
208
+ section: 'format',
209
+ enabled: true,
210
+ props: {
211
+ disabled: computed(() => !editor.value.can().chain().focus().toggleBold().run()),
212
+ color: computed(() => editor.value.isActive('bold') ? 'primary' : ''),
213
+ },
214
+ attrs: {
215
+ click: () => editor.value.chain().focus().toggleBold().run()
216
+ }
217
+ },
218
+ italic: {
219
+ name: 'italic',
220
+ tooltip: 'format.italic',
221
+ icon: mdi.mdiFormatItalic,
222
+ section: 'format',
223
+ enabled: true,
224
+ props: {
225
+ disabled: computed(() => !editor.value.can().chain().focus().toggleItalic().run()),
226
+ color: computed(() => editor.value.isActive('italic') ? 'primary' : ''),
227
+ },
228
+ attrs: {
229
+ click: () => editor.value.chain().focus().toggleItalic().run()
230
+ }
231
+ },
232
+ strike: {
233
+ name: 'strike',
234
+ tooltip: 'format.strike',
235
+ icon: mdi.mdiFormatStrikethroughVariant,
236
+ section: 'format',
237
+ enabled: true,
238
+ props: {
239
+ disabled: computed(() => !editor.value.can().chain().focus().toggleStrike().run()),
240
+ color: computed(() => editor.value.isActive('strike') ? 'primary' : ''),
241
+ },
242
+ attrs: {
243
+ click: () => editor.value.chain().focus().toggleStrike().run()
244
+ }
245
+ },
246
+ underline: {
247
+ name: 'underline',
248
+ tooltip: 'format.underline',
249
+ icon: mdi.mdiFormatUnderline,
250
+ section: 'format',
251
+ enabled: true,
252
+ props: {
253
+ disabled: computed(() => !editor.value.can().chain().focus().toggleUnderline().run()),
254
+ color: computed(() => editor.value.isActive('underline') ? 'primary' : ''),
255
+ },
256
+ attrs: {
257
+ click: () => editor.value.chain().focus().toggleUnderline().run()
258
+ }
259
+ },
260
+ highlight: {
261
+ name: 'highlight',
262
+ tooltip: 'format.highlight',
263
+ icon: mdi.mdiMarker,
264
+ section: 'format',
265
+ enabled: true,
266
+ props: {
267
+ disabled: computed(() => !editor.value.can().chain().focus().toggleHighlight().run()),
268
+ color: computed(() => editor.value.isActive('highlight') ? 'primary' : ''),
269
+ },
270
+ attrs: {
271
+ click: () => editor.value.chain().focus().toggleHighlight().run()
272
+ }
273
+ },
274
+
275
+ code: {
276
+ name: 'code',
277
+ tooltip: 'format.code',
278
+ icon: mdi.mdiXml,
279
+ section: 'format_extra',
280
+ enabled: true,
281
+ props: {
282
+ disabled: computed(() => !editor.value.can().chain().focus().toggleCode().run()),
283
+ color: computed(() => editor.value.isActive('code') ? 'primary' : ''),
284
+ },
285
+ attrs: {
286
+ click: () => editor.value.chain().focus().toggleCode().run()
287
+ }
288
+ },
289
+ codeBlock: {
290
+ name: 'codeblock',
291
+ tooltip: 'format.codeblock',
292
+ icon: mdi.mdiCodeBlockTags,
293
+ section: 'format_extra',
294
+ enabled: true,
295
+ props: {
296
+ disabled: computed(() => !editor.value.can().chain().focus().toggleCodeBlock().run()),
297
+ color: computed(() => editor.value.isActive('codeBlock') ? 'primary' : ''),
298
+ },
299
+ attrs: {
300
+ click: () => editor.value.chain().focus().toggleCodeBlock().run()
301
+ }
302
+ },
303
+ blockquote: {
304
+ name: 'blockquote',
305
+ tooltip: 'format.blockquote',
306
+ icon: mdi.mdiCommentQuote,
307
+ section: 'format_extra',
308
+ enabled: true,
309
+ props: {
310
+ disabled: computed(() => !editor.value.can().chain().focus().toggleBlockquote().run()),
311
+ color: computed(() => editor.value.isActive('blockquote') ? 'primary' : ''),
312
+ },
313
+ attrs: {
314
+ click: () => editor.value.chain().focus().toggleBlockquote().run()
315
+ }
316
+ },
317
+
318
+ sup: {
319
+ name: 'sup',
320
+ tooltip: 'format.sup',
321
+ icon: mdi.mdiFormatSuperscript,
322
+ enabled: true,
323
+ props: {
324
+ disabled: computed(() => !editor.value.can().chain().focus().toggleSuperscript().run()),
325
+ color: computed(() => editor.value.isActive('superscript') ? 'primary' : ''),
326
+ },
327
+ attrs: {
328
+ click: () => editor.value.chain().focus().toggleSuperscript().run()
329
+ }
330
+ },
331
+ sub: {
332
+ name: 'sub',
333
+ tooltip: 'format.sub',
334
+ icon: mdi.mdiFormatSubscript,
335
+ enabled: true,
336
+ props: {
337
+ disabled: computed(() => !editor.value.can().chain().focus().toggleSubscript().run()),
338
+ color: computed(() => editor.value.isActive('subscript') ? 'primary' : ''),
339
+ },
340
+ attrs: {
341
+ click: () => editor.value.chain().focus().toggleSubscript().run()
342
+ }
343
+ },
344
+
345
+ alignment: {
346
+ name: 'alignment',
347
+ tooltip: 'alignment',
348
+ icon: '',
349
+ section: 'alignment',
350
+ enabled: true,
351
+ group: true,
352
+ children: [
353
+ {
354
+ name: 'alignments.left',
355
+ tooltip: 'alignments.left',
356
+ icon: mdi.mdiFormatAlignLeft,
357
+ enabled: true,
358
+ props: {
359
+ color: computed(() => editor.value.isActive('text-align', { align: 'left' }) ? 'primary' : ''),
360
+ },
361
+ attrs: {
362
+ click: () => editor.value.chain().focus().toggleTextAlign('left').run()
363
+ }
364
+ },
365
+ {
366
+ name: 'alignments.center',
367
+ tooltip: 'alignments.center',
368
+ icon: mdi.mdiFormatAlignCenter,
369
+ enabled: true,
370
+ props: {
371
+ color: computed(() => editor.value.isActive('text-align', { align: 'center' }) ? 'primary' : ''),
372
+ },
373
+ attrs: {
374
+ click: () => editor.value.chain().focus().toggleTextAlign('center').run()
375
+ }
376
+ },
377
+ {
378
+ name: 'alignments.right',
379
+ tooltip: 'alignments.right',
380
+ icon: mdi.mdiFormatAlignRight,
381
+ enabled: true,
382
+ props: {
383
+ color: computed(() => editor.value.isActive('text-align', { align: 'right' }) ? 'primary' : ''),
384
+ },
385
+ attrs: {
386
+ click: () => editor.value.chain().focus().toggleTextAlign('right').run()
387
+ }
388
+ },
389
+ {
390
+ name: 'alignments.justify',
391
+ tooltip: 'alignments.justify',
392
+ icon: mdi.mdiFormatAlignJustify,
393
+ enabled: true,
394
+ props: {
395
+ color: computed(() => editor.value.isActive('text-align', { align: 'justify' }) ? 'primary' : ''),
396
+ },
397
+ attrs: {
398
+ click: () => editor.value.chain().focus().toggleTextAlign('justify').run()
399
+ }
400
+ },
401
+ ]
402
+ },
403
+
404
+ list: {
405
+ name: 'list',
406
+ tooltip: 'list',
407
+ icon: mdi.mdiFormatListGroup,
408
+ section: 'list',
409
+ enabled: true,
410
+ group: true,
411
+ children: [
412
+ {
413
+ name: 'lists.bullet',
414
+ tooltip: 'lists.bullet',
415
+ icon: mdi.mdiFormatListBulleted,
416
+ enabled: true,
417
+ props: {
418
+ color: computed(() => editor.value.isActive('bulletList') ? 'primary' : ''),
419
+ },
420
+ attrs: {
421
+ click: () => editor.value.chain().focus().toggleBulletList().run()
422
+ }
423
+ },
424
+ {
425
+ name: 'lists.numbered',
426
+ tooltip: 'lists.numbered',
427
+ icon: mdi.mdiFormatListNumbered,
428
+ enabled: true,
429
+ props: {
430
+ color: computed(() => editor.value.isActive('orderedList') ? 'primary' : ''),
431
+ },
432
+ attrs: {
433
+ click: () => editor.value.chain().focus().toggleOrderedList().run()
434
+ }
435
+ },
436
+ {
437
+ name: 'lists.task',
438
+ tooltip: 'lists.task',
439
+ icon: mdi.mdiFormatListChecks,
440
+ enabled: true,
441
+ props: {
442
+ color: computed(() => editor.value.isActive('taskList') ? 'primary' : ''),
443
+ },
444
+ attrs: {
445
+ click: () => editor.value.chain().focus().toggleTaskList().run()
446
+ }
447
+ },
448
+ {
449
+ name: 'lists.indent',
450
+ tooltip: 'lists.indent',
451
+ icon: mdi.mdiFormatIndentIncrease,
452
+ enabled: true,
453
+ props: {
454
+ disabled: computed(() => !editor.value.can().sinkListItem('listItem')),
455
+ active: false,
456
+ },
457
+ attrs: {
458
+ click: () => editor.value.chain().focus().sinkListItem('listItem').run()
459
+ }
460
+ },
461
+ {
462
+ name: 'lists.outdent',
463
+ tooltip: 'lists.outdent',
464
+ icon: mdi.mdiFormatIndentDecrease,
465
+ enabled: true,
466
+ props: {
467
+ disabled: computed(() => !editor.value.can().liftListItem('listItem')),
468
+ active: false,
469
+ },
470
+ attrs: {
471
+ click: () => editor.value.chain().focus().liftListItem('listItem').run()
472
+ }
473
+ },
474
+ ]
475
+ },
476
+
477
+ link: {
478
+ name: 'format.link',
479
+ tooltip: 'format.link',
480
+ icon: computed(() => editor.value.isActive('link') ? mdi.mdiLinkOff : mdi.mdiLink),
481
+ section: 'media',
482
+ enabled: true,
483
+ props: {
484
+ color: computed(() => editor.value.isActive('link') ? 'primary' : ''),
485
+ disabled: computed(() => editor.value.isActive('code') || editor.value.isActive('codeBlock')),
486
+ },
487
+ attrs: {
488
+ click: computed(() => {
489
+ return editor.value.isActive('link')
490
+ ? editor.value.chain().focus().unsetLink().run
491
+ : toolbarLinkButton.value?.open
492
+ })
493
+ }
494
+ },
495
+
496
+ undo: {
497
+ name: 'undo',
498
+ tooltip: 'action.undo',
499
+ icon: mdi.mdiUndo,
500
+ section: 'actions',
501
+ enabled: true,
502
+ props: {
503
+ disabled: computed(() => !editor.value.can().chain().focus().undo().run()),
504
+ },
505
+ attrs: {
506
+ click: () => editor.value.chain().focus().undo().run()
507
+ }
508
+ },
509
+ redo: {
510
+ name: 'redo',
511
+ tooltip: 'action.redo',
512
+ icon: mdi.mdiRedo,
513
+ section: 'actions',
514
+ enabled: true,
515
+ props: {
516
+ disabled: computed(() => !editor.value.can().chain().focus().redo().run()),
517
+ },
518
+ attrs: {
519
+ click: () => editor.value.chain().focus().redo().run()
520
+ }
521
+ },
522
+
523
+ line: {
524
+ name: 'line',
525
+ tooltip: 'format.line',
526
+ icon: mdi.mdiMinus,
527
+ section: 'misc',
528
+ enabled: true,
529
+ props: {},
530
+ attrs: {
531
+ click: () => editor.value.chain().focus().setHorizontalRule().run()
532
+ }
533
+ },
534
+ break: {
535
+ name: 'break',
536
+ tooltip: 'format.break',
537
+ icon: mdi.mdiFormatPageBreak,
538
+ section: 'misc',
539
+ enabled: true,
540
+ props: {},
541
+ attrs: {
542
+ click: () => editor.value.chain().focus().setHardBreak().run()
543
+ }
544
+ },
545
+ }
546
+
547
+
548
+
549
+ const pluginsList = Object.keys(allMenuItems)
550
+
551
+ if (items.list.length) {
552
+ items.list.forEach(item => {
553
+ if (!pluginsList.includes(item)) {
554
+ throw new Error(`Unknown plugin name: ${item}! Supported plugins: ${pluginsList.join(', ')}`)
555
+ }
556
+ })
557
+ }
558
+
559
+ const sections = {}
560
+ const toolbarItems: { [key: string]: ToolbarItems } = {}
561
+ pluginsList.forEach(key => {
562
+ const item = allMenuItems[key]
563
+
564
+ if (items.list.length) {
565
+ item.enabled = items.list.includes(key)
566
+ if (items.exclude) {
567
+ item.enabled = !item.enabled
568
+ }
569
+ }
570
+ const section = item.section ?? 'misc'
571
+ if (typeof toolbarItems[section] === 'undefined') {
572
+ sections[section] = 0
573
+ toolbarItems[section] = {}
574
+ }
575
+ toolbarItems[section][key] = allMenuItems[key]
576
+ if (allMenuItems[key].enabled) {
577
+ sections[section]++
578
+ }
579
+ })
580
+
581
+ Object.keys(sections).forEach(key => {
582
+ if (sections[key] === 0) {
583
+ delete toolbarItems[key]
584
+ }
585
+ })
586
+
587
+ return toolbarItems
588
+ }
@@ -35,7 +35,7 @@ import { common, createLowlight } from 'lowlight'
35
35
  // using all available languages
36
36
  const lowlight = createLowlight(common)
37
37
 
38
- // or you can register specific languages
38
+ // or register specific languages
39
39
  // const lowlight = createLowlight()
40
40
  //
41
41
  // import language example
@@ -44,55 +44,60 @@ const lowlight = createLowlight(common)
44
44
  // register language example
45
45
  // lowlight.register('ts', ts)
46
46
 
47
- export const editorExtensions = (placeholder: string = '') => [
48
- TextStyleKit,
49
- Document,
50
- Text,
51
- Paragraph,
52
- Heading,
53
- Bold,
54
- Italic,
55
- Strike,
56
- Blockquote,
57
- OrderedList,
58
- BulletList,
59
- TaskList,
60
- TaskItem,
61
- ListItem,
62
- ListKeymap,
63
- HardBreak,
64
- HorizontalRule,
65
- Dropcursor,
66
- Typography,
67
- Underline,
68
- Highlight,
69
- Link.configure({
70
- openOnClick: false,
71
- defaultProtocol: 'https',
72
- }),
73
- Image,
74
- Superscript,
75
- Subscript,
76
- TableKit,
77
- Code,
78
- UndoRedo,
79
- Focus,
80
- CodeBlockLowlight
81
- .extend({
82
- addNodeView() {
83
- return VueNodeViewRenderer(CodeBlockComponent)
84
- },
85
- })
86
- .configure({ lowlight }),
87
- Selection.configure({
88
- className: 'selection',
89
- }),
90
- TextAlign.configure({
91
- types: ['heading', 'paragraph'],
92
- }),
93
- Placeholder.configure({ placeholder }),
94
- SlashCommands.configure({
95
- suggestion,
96
- }),
97
- CharacterCount
98
- ]
47
+ export function editorExtensions (placeholder: string, slashCommands: boolean) {
48
+ const extensions = [
49
+ TextStyleKit,
50
+ Document,
51
+ Text,
52
+ Paragraph,
53
+ Heading,
54
+ Bold,
55
+ Italic,
56
+ Strike,
57
+ Blockquote,
58
+ OrderedList,
59
+ BulletList,
60
+ TaskList,
61
+ TaskItem,
62
+ ListItem,
63
+ ListKeymap,
64
+ HardBreak,
65
+ HorizontalRule,
66
+ Dropcursor,
67
+ Typography,
68
+ Underline,
69
+ Highlight,
70
+ Link.configure({
71
+ openOnClick: false,
72
+ defaultProtocol: 'https',
73
+ }),
74
+ Image,
75
+ Superscript,
76
+ Subscript,
77
+ TableKit,
78
+ Code,
79
+ UndoRedo,
80
+ Focus,
81
+ CodeBlockLowlight
82
+ .extend({
83
+ addNodeView() {
84
+ return VueNodeViewRenderer(CodeBlockComponent)
85
+ },
86
+ })
87
+ .configure({ lowlight }),
88
+ Selection.configure({
89
+ className: 'selection',
90
+ }),
91
+ TextAlign.configure({
92
+ types: ['heading', 'paragraph'],
93
+ }),
94
+ Placeholder.configure({ placeholder }),
95
+ CharacterCount
96
+ ]
97
+
98
+ if (slashCommands) {
99
+ extensions.push(SlashCommands.configure({ suggestion }))
100
+ }
101
+
102
+ return extensions
103
+ }