@ditojs/admin 1.30.0 → 2.0.1

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 (78) hide show
  1. package/dist/dito-admin.es.js +5291 -6856
  2. package/dist/dito-admin.umd.js +5 -5
  3. package/dist/style.css +1 -1
  4. package/package.json +42 -27
  5. package/src/DitoAdmin.js +44 -58
  6. package/src/DitoComponent.js +18 -50
  7. package/src/DitoContext.js +7 -3
  8. package/src/TypeComponent.js +15 -13
  9. package/src/appState.js +4 -2
  10. package/src/components/DitoAccount.vue +14 -14
  11. package/src/components/DitoButtons.vue +18 -10
  12. package/src/components/DitoClipboard.vue +16 -16
  13. package/src/components/DitoContainer.vue +32 -32
  14. package/src/components/DitoCreateButton.vue +22 -23
  15. package/src/components/DitoDialog.vue +73 -18
  16. package/src/components/DitoEditButtons.vue +31 -31
  17. package/src/components/DitoElement.vue +6 -6
  18. package/src/components/DitoErrors.vue +6 -6
  19. package/src/components/DitoForm.vue +42 -43
  20. package/src/components/DitoFormNested.vue +7 -3
  21. package/src/components/DitoHeader.vue +19 -19
  22. package/src/components/DitoLabel.vue +25 -25
  23. package/src/components/DitoMenu.vue +9 -9
  24. package/src/components/DitoPagination.vue +5 -5
  25. package/src/components/DitoPane.vue +32 -32
  26. package/src/components/DitoPanel.vue +18 -18
  27. package/src/components/DitoPanels.vue +5 -3
  28. package/src/components/DitoRoot.vue +107 -30
  29. package/src/components/DitoSchema.vue +76 -74
  30. package/src/components/DitoSchemaInlined.vue +29 -29
  31. package/src/components/DitoScopes.vue +14 -13
  32. package/src/components/DitoSpinner.vue +101 -0
  33. package/src/components/DitoTableCell.vue +19 -25
  34. package/src/components/DitoTableHead.vue +10 -7
  35. package/src/components/DitoTabs.vue +7 -6
  36. package/src/components/DitoTreeItem.vue +89 -85
  37. package/src/components/DitoVNode.vue +9 -7
  38. package/src/components/DitoView.vue +25 -21
  39. package/src/mixins/DataMixin.js +2 -2
  40. package/src/mixins/DitoMixin.js +43 -46
  41. package/src/mixins/DomMixin.js +1 -1
  42. package/src/mixins/EmitterMixin.js +11 -11
  43. package/src/mixins/RouteMixin.js +20 -10
  44. package/src/mixins/SchemaParentMixin.js +2 -2
  45. package/src/mixins/SourceMixin.js +7 -9
  46. package/src/mixins/TypeMixin.js +29 -34
  47. package/src/mixins/ValidationMixin.js +4 -19
  48. package/src/types/TypeButton.vue +11 -15
  49. package/src/types/TypeCheckbox.vue +7 -8
  50. package/src/types/TypeCheckboxes.vue +14 -15
  51. package/src/types/TypeCode.vue +5 -5
  52. package/src/types/TypeColor.vue +9 -12
  53. package/src/types/TypeComponent.vue +12 -7
  54. package/src/types/TypeComputed.vue +13 -12
  55. package/src/types/TypeDate.vue +10 -11
  56. package/src/types/TypeLabel.vue +1 -1
  57. package/src/types/TypeList.vue +115 -92
  58. package/src/types/TypeMarkup.vue +166 -125
  59. package/src/types/TypeMultiselect.vue +37 -47
  60. package/src/types/TypeNumber.vue +10 -11
  61. package/src/types/TypeObject.vue +62 -46
  62. package/src/types/TypeProgress.vue +7 -8
  63. package/src/types/TypeRadio.vue +15 -14
  64. package/src/types/TypeSection.vue +10 -10
  65. package/src/types/TypeSelect.vue +32 -33
  66. package/src/types/TypeSlider.vue +20 -22
  67. package/src/types/TypeSwitch.vue +8 -9
  68. package/src/types/TypeText.vue +7 -8
  69. package/src/types/TypeTextarea.vue +8 -9
  70. package/src/types/TypeTreeList.vue +40 -34
  71. package/src/types/TypeUpload.vue +61 -61
  72. package/src/utils/accessor.js +1 -1
  73. package/src/utils/data.js +0 -4
  74. package/src/utils/options.js +48 -0
  75. package/src/utils/path.js +5 -0
  76. package/src/utils/schema.js +73 -56
  77. package/src/utils/vue.js +11 -0
  78. package/types/index.d.ts +1 -1
@@ -1,29 +1,29 @@
1
1
  <template lang="pug">
2
- .dito-markup(
3
- :id="dataPath"
4
- )
5
- editor-menu-bar.dito-markup-toolbar(:editor="editor")
6
- .dito-buttons.dito-buttons-toolbar(
7
- v-if="groupedButtons.length > 0"
2
+ .dito-markup(
3
+ :id="dataPath"
4
+ )
5
+ .dito-markup-toolbar(:editor="editor")
6
+ .dito-buttons.dito-buttons-toolbar(
7
+ v-if="groupedButtons.length > 0"
8
+ )
9
+ .dito-button-group(
10
+ v-for="buttons in groupedButtons"
8
11
  )
9
- .dito-button-group(
10
- v-for="buttons in groupedButtons"
12
+ button.dito-button(
13
+ v-for="{ name, icon, isActive, onClick } in buttons"
14
+ :class="{ 'dito-active': isActive }",
15
+ @click="onClick"
11
16
  )
12
- button.dito-button(
13
- v-for="{ name, icon, isActive, onClick } in buttons"
14
- :class="{ 'dito-active': isActive }",
15
- @click="onClick"
16
- )
17
- icon(:name="icon")
18
- editor-content.dito-markup-editor(
19
- ref="editor"
20
- :editor="editor"
21
- :style="styles"
22
- )
23
- .dito-resize(
24
- v-if="resizable"
25
- @mousedown.stop.prevent="onDragResize"
26
- )
17
+ icon(:name="icon")
18
+ editor-content.dito-markup-editor(
19
+ ref="editor"
20
+ :editor="editor"
21
+ :style="styles"
22
+ )
23
+ .dito-resize(
24
+ v-if="resizable"
25
+ @mousedown.stop.prevent="onDragResize"
26
+ )
27
27
  </template>
28
28
 
29
29
  <style lang="sass">
@@ -120,31 +120,51 @@
120
120
  import TypeComponent from '../TypeComponent.js'
121
121
  import DomMixin from '../mixins/DomMixin.js'
122
122
  import { getSchemaAccessor } from '../utils/accessor.js'
123
- import { Editor, EditorContent, EditorMenuBar, Mark } from 'tiptap'
124
- import { toggleMark } from 'tiptap-commands'
125
- import {
126
- // Marks:
127
- Bold, Code, Italic, Link, Strike, Underline,
128
- // Nodes:
129
- Blockquote, CodeBlock, HardBreak, Heading, HorizontalRule,
130
- OrderedList, BulletList, ListItem,
131
- // TODO:
132
- // - Image, Mention, CodeBlockHighlight
133
- // - Table, TableCell, TableHeader, TableNodes, TableRow,
134
- // - TodoItem, TodoList
135
- // Tools:
136
- History
137
- } from 'tiptap-extensions'
123
+ import { Editor, EditorContent, Mark, getMarkAttributes } from '@tiptap/vue-3'
124
+ // import { toggleMark } from 'tiptap-commands'
125
+ // Essentials:
126
+ import { Document } from '@tiptap/extension-document'
127
+ import { Text } from '@tiptap/extension-text'
128
+ // Marks:
129
+ import { Bold } from '@tiptap/extension-bold'
130
+ import { Code } from '@tiptap/extension-code'
131
+ import { Italic } from '@tiptap/extension-italic'
132
+ import { Link } from '@tiptap/extension-link'
133
+ import { Strike } from '@tiptap/extension-strike'
134
+ import { Underline } from '@tiptap/extension-underline'
135
+ // Nodes:
136
+ import { Blockquote } from '@tiptap/extension-blockquote'
137
+ import { CodeBlock } from '@tiptap/extension-code-block'
138
+ import { HardBreak } from '@tiptap/extension-hard-break'
139
+ import { Heading } from '@tiptap/extension-heading'
140
+ import { Paragraph } from '@tiptap/extension-paragraph'
141
+ import { HorizontalRule } from '@tiptap/extension-horizontal-rule'
142
+ import { OrderedList } from '@tiptap/extension-ordered-list'
143
+ import { BulletList } from '@tiptap/extension-bullet-list'
144
+ import { ListItem } from '@tiptap/extension-list-item'
145
+ // TODO:
146
+ // import { Image } from '@tiptap/extension-image'
147
+ // import { Mention } from '@tiptap/extension-mention'
148
+ // import { CodeBlockHighlight } from '@tiptap/extension-code-block-highlight'
149
+ // import { Table } from '@tiptap/extension-table'
150
+ // import { TableCell } from '@tiptap/extension-table-cell'
151
+ // import { TableHeader } from '@tiptap/extension-table-header'
152
+ // import { TableNodes } from '@tiptap/extension-table-nodes'
153
+ // import { TableRow } from '@tiptap/extension-table-row'
154
+ // import { TaskList } from '@tiptap/extension-task-list'
155
+ // import { TaskItem } from '@tiptap/extension-task-item'
156
+ // Tools:
157
+ import { History } from '@tiptap/extension-history'
158
+
138
159
  import { Icon } from '@ditojs/ui/src'
139
160
  import {
140
- isArray, isObject, underscore, hyphenate, debounce
161
+ isArray, isObject, underscore, hyphenate, debounce, camelize
141
162
  } from '@ditojs/utils'
142
163
 
143
164
  // @vue/component
144
165
  export default TypeComponent.register('markup', {
145
166
  components: {
146
167
  EditorContent,
147
- EditorMenuBar,
148
168
  Icon
149
169
  },
150
170
 
@@ -177,7 +197,7 @@ export default TypeComponent.register('markup', {
177
197
  small: true,
178
198
  code: true,
179
199
  link: {
180
- onClick: command => this.onClickLink(command)
200
+ onClick: editor => this.onClickLink(editor)
181
201
  }
182
202
  })
183
203
  },
@@ -191,7 +211,7 @@ export default TypeComponent.register('markup', {
191
211
  this.otherNodeButtons.some(button => button.isActive)
192
212
  },
193
213
  heading: {
194
- attr: 'level',
214
+ attribute: 'level',
195
215
  values: [1, 2, 3, 4, 5, 6]
196
216
  }
197
217
  })
@@ -297,15 +317,15 @@ export default TypeComponent.register('markup', {
297
317
  onChange()
298
318
  }
299
319
 
300
- const setValueDebounced = debounce(getValue => {
320
+ const setValueDebounced = debounce(editor => {
301
321
  ignoreWatch = true
302
- this.value = getValue()
322
+ this.value = editor.getHTML()
303
323
  changed = true
304
324
  onChange()
305
325
  }, 100)
306
326
 
307
- const onUpdate = ({ getHTML }) => {
308
- setValueDebounced(getHTML)
327
+ const onUpdate = ({ editor }) => {
328
+ setValueDebounced(editor)
309
329
  this.onInput()
310
330
  }
311
331
 
@@ -322,12 +342,12 @@ export default TypeComponent.register('markup', {
322
342
  onFocus,
323
343
  onBlur,
324
344
  onUpdate,
325
- extensions: this.createExtensions(),
345
+ extensions: this.getExtensions(),
326
346
  content: this.value || ''
327
347
  })
328
348
  },
329
349
 
330
- beforeDestroy() {
350
+ beforeUnmount() {
331
351
  this.editor.destroy()
332
352
  },
333
353
 
@@ -359,8 +379,8 @@ export default TypeComponent.register('markup', {
359
379
  this.editor.setOptions(this.editorOptions)
360
380
  },
361
381
 
362
- async onClickLink(command) {
363
- const attrs = await this.showDialog({
382
+ async onClickLink(editor) {
383
+ const attributes = await this.rootComponent.showDialog({
364
384
  components: {
365
385
  href: {
366
386
  type: 'url',
@@ -378,81 +398,90 @@ export default TypeComponent.register('markup', {
378
398
  remove: {
379
399
  events: {
380
400
  click({ dialogComponent }) {
381
- dialogComponent.resolve({
382
- href: null,
383
- title: null
384
- })
401
+ dialogComponent.resolve(null)
385
402
  }
386
403
  }
387
404
  }
388
405
  },
389
- data: this.editor.getMarkAttrs('link')
406
+ data: getMarkAttributes(this.editor.state, 'link')
390
407
  })
391
- if (attrs) {
392
- let { href, title } = attrs
408
+ if (attributes) {
409
+ let { href, title } = attributes
393
410
  if (href) {
394
- // See if `href` can be parsed as a URL, and if not, prefix it with
395
- // a default protocol.
411
+ // See if `href` can be parsed as a URL, and if not,
412
+ // prefix it with a default protocol.
396
413
  try {
397
414
  new URL(href)
398
415
  } catch {
399
- href = `http://${href}`
416
+ href = `https://${href}`
400
417
  }
401
418
  }
402
- command({ href, title })
419
+ editor.commands.setLink({ href, title })
420
+ } else {
421
+ editor.commands.unsetLink()
403
422
  }
404
423
  },
405
424
 
406
- createExtensions() {
425
+ getExtensions() {
407
426
  const {
408
427
  marks = {},
409
428
  nodes = {},
410
429
  tools = {}
411
430
  } = this.schema
412
431
  return [
413
- // schema.marks:
414
- marks.bold && new Bold(),
415
- marks.italic && new Italic(),
416
- marks.underline && new Underline(),
417
- marks.strike && new Strike(),
418
- marks.small && new Small(),
419
- marks.code && new Code(),
420
- marks.link && new LinkWithTitle(),
421
-
422
- // schema.nodes:
423
- nodes.blockquote && new Blockquote(),
424
- nodes.codeBlock && new CodeBlock(),
425
- new HardBreak(), // TODO: Should this always on?
426
- nodes.heading && new Heading({ levels: nodes.heading }),
427
- nodes.horizontalRule && new HorizontalRule(),
428
- (nodes.orderedList || nodes.bulletList) && new ListItem(),
429
- nodes.bulletList && new BulletList(),
430
- nodes.orderedList && new OrderedList(),
432
+ // Essentials:
433
+ Document,
434
+ Text,
435
+ Paragraph, // button can be controlled, but node needs to be on.
436
+
437
+ // Marks: `schema.marks`
438
+ marks.bold && Bold,
439
+ marks.italic && Italic,
440
+ marks.underline && Underline,
441
+ marks.strike && Strike,
442
+ marks.small && Small,
443
+ marks.code && Code,
444
+ marks.link && LinkWithTitle,
445
+
446
+ // Nodes: `schema.nodes`
447
+ nodes.blockquote && Blockquote,
448
+ nodes.codeBlock && CodeBlock,
449
+ HardBreak, // TODO: Should this always on?
450
+ nodes.heading && Heading.configure({ levels: nodes.heading }),
451
+ nodes.horizontalRule && HorizontalRule,
452
+ (nodes.orderedList || nodes.bulletList) && ListItem,
453
+ nodes.bulletList && BulletList,
454
+ nodes.orderedList && OrderedList,
431
455
  // TODO:
432
- // nodes.todoList && new TodoItem(),
433
- // nodes.todoList && new TodoList(),
456
+ // nodes.todoList && TodoItem,
457
+ // nodes.todoList && TodoList,
434
458
 
435
- // schema.tools:
436
- tools.history && new History()
459
+ // Tools: `schema.tools`
460
+ tools.history && History
437
461
  ].filter(extension => !!extension)
438
462
  },
439
463
 
440
464
  getButtons(settingsName, descriptions) {
441
465
  const list = []
442
466
 
443
- const addButton = ({ name, icon, attrs, ignoreActive, onClick }) => {
444
- const isActive = this.editor.isActive[name]
445
- const command = this.editor.commands[name]
467
+ const addButton = ({ name, icon, attributes, ignoreActive, onClick }) => {
446
468
  list.push({
447
469
  name,
448
470
  icon,
449
471
  isActive: (
450
- isActive?.(attrs) &&
472
+ this.editor.isActive(name, attributes) &&
451
473
  (ignoreActive == null || !ignoreActive())
452
474
  ),
453
- onClick: () => onClick
454
- ? onClick(command, attrs)
455
- : command(attrs)
475
+ onClick: () => {
476
+ const key = `toggle${camelize(name, true)}`
477
+ if (this.editor.commands[key]) {
478
+ const command = attributes =>
479
+ this.editor.chain()[key](attributes).focus().run()
480
+ onClick
481
+ ? onClick(this.editor, attributes)
482
+ : command(attributes)
483
+ }
484
+ }
456
485
  })
457
486
  }
458
487
 
@@ -467,8 +496,8 @@ export default TypeComponent.register('markup', {
467
496
  if (description === true) {
468
497
  addButton({ name, icon })
469
498
  } else if (isObject(description)) {
470
- const { attr, values, ignoreActive, onClick } = description
471
- if (attr) {
499
+ const { attribute, values, ignoreActive, onClick } = description
500
+ if (attribute) {
472
501
  if (isArray(values) && isArray(setting)) {
473
502
  // Support heading level attrs:
474
503
  for (const value of values) {
@@ -476,7 +505,7 @@ export default TypeComponent.register('markup', {
476
505
  addButton({
477
506
  name,
478
507
  icon: `${icon}-${value}`,
479
- attrs: { [attr]: value },
508
+ attributes: { [attribute]: value },
480
509
  ignoreActive,
481
510
  onClick
482
511
  })
@@ -500,45 +529,57 @@ export default TypeComponent.register('markup', {
500
529
  }
501
530
  })
502
531
 
503
- class Small extends Mark {
504
- get name() {
505
- return 'small'
506
- }
532
+ const Small = Mark.create({
533
+ name: 'small',
534
+
535
+ parseHTML() {
536
+ return [{ tag: 'small' }]
537
+ },
538
+
539
+ renderHTML() {
540
+ return ['small', 0]
541
+ },
507
542
 
508
- get schema() {
543
+ addCommands() {
509
544
  return {
510
- parseDOM: [
511
- { tag: 'small' }
512
- ],
513
- toDOM: () => ['small', 0]
545
+ setSmall: attributes => ({ commands }) => {
546
+ return commands.setMark(this.name, attributes)
547
+ },
548
+ toggleSmall: attributes => ({ commands }) => {
549
+ return commands.toggleMark(this.name, attributes)
550
+ },
551
+ unsetSmall: () => ({ commands }) => {
552
+ return commands.unsetMark(this.name)
553
+ }
514
554
  }
515
555
  }
556
+ })
516
557
 
517
- commands({ type }) {
518
- return () => toggleMark(type)
519
- }
520
- }
521
- class LinkWithTitle extends Link {
522
- get schema() {
523
- return {
524
- attrs: {
525
- href: {
526
- default: null
527
- },
528
- title: {
529
- default: null
530
- }
558
+ const LinkWithTitle = Link.extend({
559
+ inclusive: false,
560
+ schema: {
561
+ attrs: {
562
+ href: {
563
+ default: null
531
564
  },
532
- inclusive: false,
533
- parseDOM: [{
565
+ title: {
566
+ default: null
567
+ }
568
+ },
569
+
570
+ parseHTML() {
571
+ return [{
534
572
  tag: 'a',
535
- getAttrs: dom => ({
536
- href: dom.getAttribute('href'),
537
- title: dom.getAttribute('title')
573
+ getAttrs: element => ({
574
+ href: element.getAttribute('href'),
575
+ title: element.getAttribute('title')
538
576
  })
539
- }],
540
- toDOM: node => ['a', node.attrs, 0]
577
+ }]
578
+ },
579
+
580
+ renderHTML(node) {
581
+ return ['a', node.attrs, 0]
541
582
  }
542
583
  }
543
- }
584
+ })
544
585
  </script>
@@ -1,46 +1,45 @@
1
1
  <template lang="pug">
2
- .dito-multiselect(
3
- :class=`{
4
- 'dito-multiselect-single': !multiple,
5
- 'dito-multiselect-multiple': multiple
6
- }`
2
+ .dito-multiselect(
3
+ :class=`{
4
+ 'dito-multiselect-single': !multiple,
5
+ 'dito-multiselect-multiple': multiple
6
+ }`
7
+ )
8
+ vue-multiselect(
9
+ ref="element"
10
+ v-model="selectedOptions"
11
+ v-bind="attributes"
12
+ :show-labels="false"
13
+ :placeholder="placeholder"
14
+ tag-placeholder="Press enter to add new tag",
15
+ :options="populate && activeOptions || []"
16
+ :custom-label="getLabelForOption"
17
+ :track-by="optionValue"
18
+ :group-label="groupByLabel"
19
+ :group-values="groupByOptions"
20
+ :multiple="multiple"
21
+ :taggable="taggable"
22
+ :searchable="searchable"
23
+ :internal-search="!searchFilter"
24
+ :preserve-search="!!searchFilter"
25
+ :clear-on-select="!searchFilter"
26
+ :close-on-select="!stayOpen"
27
+ :loading="isLoading"
28
+ @open="populate = true"
29
+ @tag="onAddTag"
30
+ @search-change="onSearchChange"
31
+ )
32
+ button.dito-button-clear.dito-button-overlay(
33
+ type="button"
34
+ v-if="showClearButton"
35
+ @click="clear"
36
+ :disabled="disabled"
7
37
  )
8
- vue-multiselect(
9
- ref="element"
10
- v-model="selectedOptions"
11
- v-bind="attributes"
12
- v-on="listeners"
13
- :show-labels="false"
14
- :placeholder="placeholder"
15
- tag-placeholder="Press enter to add new tag",
16
- :options="populate && activeOptions || []"
17
- :custom-label="getLabelForOption"
18
- :track-by="optionValue"
19
- :group-label="groupByLabel"
20
- :group-values="groupByOptions"
21
- :multiple="multiple"
22
- :taggable="taggable"
23
- :searchable="searchable"
24
- :internal-search="!searchFilter"
25
- :preserve-search="!!searchFilter"
26
- :clear-on-select="!searchFilter"
27
- :close-on-select="!stayOpen"
28
- :loading="isLoading"
29
- @open="populate = true"
30
- @tag="onAddTag"
31
- @search-change="onSearchChange"
32
- )
33
- button.dito-button-clear.dito-button-overlay(
34
- type="button"
35
- v-if="showClearButton"
36
- @click="clear"
37
- :disabled="disabled"
38
- )
39
38
  </template>
40
39
 
41
40
  <style lang="sass">
42
41
  @import '../styles/_imports'
43
- @import 'vue-multiselect/dist/vue-multiselect.min.css'
42
+ @import 'vue-multiselect/dist/vue-multiselect.css'
44
43
 
45
44
  $spinner-width: $select-arrow-width
46
45
  $tag-icon-width: 1.8em
@@ -263,6 +262,7 @@ export default TypeComponent.register('multiselect', {
263
262
  this.selectedValue = this.multiple
264
263
  ? (option || []).map(value => this.getValueForOption(value))
265
264
  : this.getValueForOption(option)
265
+ this.onChange()
266
266
  }
267
267
  },
268
268
 
@@ -309,16 +309,6 @@ export default TypeComponent.register('multiselect', {
309
309
  },
310
310
 
311
311
  methods: {
312
- // @override
313
- getListeners() {
314
- // override `TypeMixin.getListeners()` to re-route 'input' to `onChange()`
315
- return {
316
- focus: this.onFocus,
317
- blur: this.onBlur,
318
- input: this.onChange
319
- }
320
- },
321
-
322
312
  addTagOption(tag) {
323
313
  if (this.taggable) {
324
314
  const { optionLabel, optionValue } = this
@@ -1,15 +1,14 @@
1
1
  <template lang="pug">
2
- input-field.dito-number(
3
- ref="element"
4
- :id="dataPath"
5
- type="number"
6
- v-model="inputValue"
7
- v-bind="attributes"
8
- v-on="listeners"
9
- :min="min"
10
- :max="max"
11
- :step="stepValue"
12
- )
2
+ input-field.dito-number(
3
+ ref="element"
4
+ :id="dataPath"
5
+ type="number"
6
+ v-model="inputValue"
7
+ v-bind="attributes"
8
+ :min="min"
9
+ :max="max"
10
+ :step="stepValue"
11
+ )
13
12
  </template>
14
13
 
15
14
  <style lang="sass">