@ditojs/admin 3.2.7 → 3.3.0

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 (112) hide show
  1. package/dist/dito-admin.css +1 -1
  2. package/dist/dito-admin.es.js +6011 -5826
  3. package/dist/dito-admin.umd.js +3 -7
  4. package/package.json +18 -17
  5. package/src/DitoAdmin.js +44 -24
  6. package/src/DitoContext.js +28 -2
  7. package/src/DitoTypeComponent.js +1 -0
  8. package/src/appState.js +6 -3
  9. package/src/components/DitoAccount.vue +35 -27
  10. package/src/components/DitoAffixes.vue +9 -2
  11. package/src/components/DitoClipboard.vue +59 -93
  12. package/src/components/DitoConfirmMessage.vue +18 -0
  13. package/src/components/DitoContainer.vue +56 -18
  14. package/src/components/DitoCreateButton.vue +36 -84
  15. package/src/components/DitoDialog.vue +20 -15
  16. package/src/components/DitoDragHandle.vue +44 -0
  17. package/src/components/DitoDraggable.vue +43 -16
  18. package/src/components/DitoEditButtons.vue +48 -24
  19. package/src/components/DitoErrors.vue +9 -56
  20. package/src/components/DitoForm.vue +82 -42
  21. package/src/components/DitoInputAffixes.vue +50 -0
  22. package/src/components/DitoLabel.vue +1 -15
  23. package/src/components/DitoMenu.vue +45 -52
  24. package/src/components/DitoNotifications.vue +38 -14
  25. package/src/components/DitoOptionList.vue +81 -0
  26. package/src/components/DitoOptionsEditButtons.vue +57 -0
  27. package/src/components/DitoPagination.vue +1 -8
  28. package/src/components/DitoPane.vue +51 -39
  29. package/src/components/DitoPanel.vue +12 -26
  30. package/src/components/DitoPanels.vue +11 -5
  31. package/src/components/DitoResizeHandle.vue +47 -0
  32. package/src/components/DitoRoot.vue +113 -320
  33. package/src/components/DitoSchema.vue +72 -63
  34. package/src/components/DitoSchemaInlined.vue +8 -9
  35. package/src/components/DitoScopes.vue +14 -18
  36. package/src/components/DitoSidebar.vue +4 -29
  37. package/src/components/DitoSourceItem.vue +114 -0
  38. package/src/components/DitoTableCell.vue +4 -0
  39. package/src/components/DitoTableHead.vue +15 -17
  40. package/src/components/DitoTabs.vue +25 -0
  41. package/src/components/DitoTrail.vue +25 -25
  42. package/src/components/DitoTreeItem.vue +102 -141
  43. package/src/components/DitoUploadFile.vue +5 -2
  44. package/src/components/DitoView.vue +33 -24
  45. package/src/index.js +4 -1
  46. package/src/mixins/DitoMixin.js +77 -110
  47. package/src/mixins/DomMixin.js +12 -14
  48. package/src/mixins/EmitterMixin.js +3 -2
  49. package/src/mixins/ItemMixin.js +38 -94
  50. package/src/mixins/OptionsMixin.js +38 -25
  51. package/src/mixins/ResourceMixin.js +182 -111
  52. package/src/mixins/RouteMixin.js +61 -44
  53. package/src/mixins/SchemaParentMixin.js +4 -1
  54. package/src/mixins/SourceMixin.js +118 -100
  55. package/src/mixins/TypeMixin.js +42 -40
  56. package/src/mixins/ValidationMixin.js +16 -4
  57. package/src/mixins/ValidatorMixin.js +1 -1
  58. package/src/styles/_settings.scss +4 -0
  59. package/src/styles/_tippy.scss +0 -11
  60. package/src/styles/style.scss +0 -1
  61. package/src/types/DitoTypeButton.vue +11 -14
  62. package/src/types/DitoTypeCheckboxes.vue +35 -17
  63. package/src/types/DitoTypeCode.vue +19 -41
  64. package/src/types/DitoTypeColor.vue +67 -19
  65. package/src/types/DitoTypeDate.vue +11 -14
  66. package/src/types/DitoTypeList.vue +50 -62
  67. package/src/types/DitoTypeMarkup.vue +41 -54
  68. package/src/types/DitoTypeMultiselect.vue +82 -52
  69. package/src/types/DitoTypeNumber.vue +12 -15
  70. package/src/types/DitoTypeObject.vue +4 -38
  71. package/src/types/DitoTypeRadio.vue +19 -6
  72. package/src/types/DitoTypeSection.vue +23 -9
  73. package/src/types/DitoTypeSelect.vue +39 -23
  74. package/src/types/DitoTypeSlider.vue +9 -1
  75. package/src/types/DitoTypeSwitch.vue +1 -1
  76. package/src/types/DitoTypeText.vue +11 -14
  77. package/src/types/DitoTypeTreeList.vue +46 -8
  78. package/src/types/DitoTypeUpload.vue +190 -67
  79. package/src/utils/DataModel.js +368 -146
  80. package/src/utils/FileDropTargets.js +97 -0
  81. package/src/utils/ItemStores.js +31 -0
  82. package/src/utils/ListQuery.js +115 -0
  83. package/src/utils/LoadingTracker.js +76 -0
  84. package/src/utils/SchemaGraph.js +3 -2
  85. package/src/utils/Session.js +151 -0
  86. package/src/utils/ViewRegistry.js +85 -0
  87. package/src/utils/accessor.js +12 -4
  88. package/src/utils/affixes.js +33 -0
  89. package/src/utils/changeOnceEdited.js +33 -0
  90. package/src/utils/clipboard.js +55 -0
  91. package/src/utils/data.js +10 -5
  92. package/src/utils/dialogs.js +59 -0
  93. package/src/utils/dragResize.js +61 -0
  94. package/src/utils/filter.js +36 -2
  95. package/src/utils/html.js +9 -0
  96. package/src/utils/list.js +10 -0
  97. package/src/utils/options.js +4 -0
  98. package/src/utils/registration.js +18 -0
  99. package/src/utils/request.js +108 -13
  100. package/src/utils/route.js +6 -17
  101. package/src/utils/schema/data.js +24 -2
  102. package/src/utils/schema/events.js +19 -0
  103. package/src/utils/schema/item.js +114 -0
  104. package/src/utils/schema/lookup.js +39 -14
  105. package/src/utils/schema/setup.js +5 -5
  106. package/src/utils/schema/validation.js +16 -2
  107. package/src/utils/uid.js +105 -8
  108. package/src/validators/_decimals.js +16 -5
  109. package/types/index.d.ts +168 -41
  110. package/src/mixins/LoadingMixin.js +0 -23
  111. package/src/mixins/PulldownMixin.js +0 -72
  112. package/src/styles/_pulldown.scss +0 -38
@@ -6,7 +6,7 @@
6
6
  .dito-buttons__group(
7
7
  v-for="buttons in groupedButtons"
8
8
  )
9
- button.dito-button(
9
+ DitoButton(
10
10
  v-for="{ id, label, icon, isActive, onClick } in buttons"
11
11
  :key="id"
12
12
  :class="{ 'dito-button--active': isActive }"
@@ -20,16 +20,18 @@
20
20
  :editor="editor"
21
21
  :style="styles"
22
22
  )
23
- .dito-resize(
23
+ DitoResizeHandle(
24
24
  v-if="resizable"
25
- @mousedown.stop.prevent="onDragResize"
25
+ :getElement="() => $refs.editor.$el"
26
+ @resize="height = `${$event}px`"
26
27
  )
27
28
  </template>
28
29
 
29
30
  <script>
30
31
  import DitoTypeComponent from '../DitoTypeComponent.js'
31
- import DomMixin from '../mixins/DomMixin.js'
32
32
  import { getSchemaAccessor } from '../utils/accessor.js'
33
+ import { createChangeOnceEdited } from '../utils/changeOnceEdited.js'
34
+ import DitoResizeHandle from '../components/DitoResizeHandle.vue'
33
35
  // Tiptap:
34
36
  import { Editor, EditorContent, Mark, getMarkAttributes } from '@tiptap/vue-3'
35
37
  import { Slice, Fragment } from '@tiptap/pm/model'
@@ -70,15 +72,16 @@ import { Footnotes, FootnoteReference, Footnote } from 'tiptap-footnotes'
70
72
  // Tools:
71
73
  import { History } from '@tiptap/extension-history'
72
74
 
73
- import { DitoIcon } from '@ditojs/ui/src'
75
+ import { DitoButton, DitoIcon } from '@ditojs/ui/src'
74
76
  import { isArray, isObject, hyphenate, debounce, camelize } from '@ditojs/utils'
75
77
 
76
78
  // @vue/component
77
79
  export default DitoTypeComponent.register('markup', {
78
- mixins: [DomMixin],
79
80
  components: {
80
81
  EditorContent,
81
- DitoIcon
82
+ DitoButton,
83
+ DitoIcon,
84
+ DitoResizeHandle
82
85
  },
83
86
 
84
87
  data() {
@@ -171,10 +174,10 @@ export default DitoTypeComponent.register('markup', {
171
174
 
172
175
  editorOptions() {
173
176
  return {
174
- editable: !this.readonly,
175
- autoFocus: this.autofocus,
176
- disableInputRules: !this.enableRules.input,
177
- disablePasteRules: !this.enableRules.paste,
177
+ editable: !this.readonly && !this.disabled,
178
+ autofocus: this.autofocus,
179
+ enableInputRules: this.enableRules.input,
180
+ enablePasteRules: this.enableRules.paste,
178
181
  parseOptions: this.parseOptions,
179
182
  editorProps: this.hardBreak
180
183
  ? {
@@ -240,14 +243,20 @@ export default DitoTypeComponent.register('markup', {
240
243
 
241
244
  watch: {
242
245
  readonly: 'updateEditorOptions',
246
+ disabled: 'updateEditorOptions',
243
247
  autofocus: 'updateEditorOptions',
244
248
  enableRules: 'updateEditorOptions'
245
249
  },
246
250
 
247
251
  created() {
248
- let changed = false
249
252
  let ignoreWatch = false
250
253
 
254
+ // Emits the change of the value once the editor isn't focused anymore.
255
+ const changeOnceEdited = createChangeOnceEdited({
256
+ isEditing: () => this.focused,
257
+ emitChange: () => this.onChange()
258
+ })
259
+
251
260
  const onFocus = () => this.onFocus()
252
261
 
253
262
  const onBlur = () => {
@@ -255,7 +264,7 @@ export default DitoTypeComponent.register('markup', {
255
264
  // emit the change once the editor isn't focused anymore.
256
265
  updateValue()
257
266
  this.onBlur()
258
- emitChange()
267
+ changeOnceEdited.emitIfDone()
259
268
  }
260
269
 
261
270
  const onUpdate = () => {
@@ -264,6 +273,13 @@ export default DitoTypeComponent.register('markup', {
264
273
  }
265
274
 
266
275
  const setValueDebounced = debounce(() => updateValue(), 100)
276
+ // Writes the pending edits before unmounting, as the destroyed editor
277
+ // can't be read anymore, see `beforeUnmount()`:
278
+ this.writePendingValue = () => {
279
+ if (setValueDebounced.cancel()) {
280
+ updateValue()
281
+ }
282
+ }
267
283
 
268
284
  const updateValue = () => {
269
285
  const content = this.editor.getHTML()
@@ -271,23 +287,13 @@ export default DitoTypeComponent.register('markup', {
271
287
  ? content.replace(/^<p>(.*?)<\/p>$/s, '$1')
272
288
  : content
273
289
  if (value !== this.value) {
274
- changed = true
290
+ changeOnceEdited.markEdited()
275
291
  // The value comes from the editor, so don't set it back as content,
276
292
  // which would re-parse it and e.g. collapse trailing whitespace.
277
293
  ignoreWatch = true
278
294
  this.value = value
279
295
  }
280
- if (!this.focused) {
281
- emitChange()
282
- }
283
- }
284
-
285
- // Emits the change of the value, once the editing is done.
286
- const emitChange = () => {
287
- if (changed) {
288
- changed = false
289
- this.onChange()
290
- }
296
+ changeOnceEdited.emitIfDone()
291
297
  }
292
298
 
293
299
  this.$watch('value', value => {
@@ -295,8 +301,8 @@ export default DitoTypeComponent.register('markup', {
295
301
  ignoreWatch = false
296
302
  } else {
297
303
  const content = this.hardBreak
298
- ? `<p>${value}</p>`
299
- : value
304
+ ? `<p>${value ?? ''}</p>`
305
+ : value ?? ''
300
306
  this.editor.commands.setContent(content, {
301
307
  emitUpdate: false,
302
308
  parseOptions: this.parseOptions
@@ -314,40 +320,21 @@ export default DitoTypeComponent.register('markup', {
314
320
  })
315
321
  },
316
322
 
323
+ beforeUnmount() {
324
+ this.writePendingValue()
325
+ },
326
+
317
327
  unmounted() {
318
328
  this.editor.destroy()
319
329
  },
320
330
 
321
331
  methods: {
322
- onDragResize(event) {
323
- const getPoint = ({ clientX: x, clientY: y }) => ({ x, y })
324
-
325
- let prevY = getPoint(event).y
326
- let height = parseFloat(getComputedStyle(this.$refs.editor.$el).height)
327
-
328
- const mousemove = event => {
329
- const { y } = getPoint(event)
330
- height += y - prevY
331
- prevY = y
332
- this.height = `${Math.max(height, 0)}px`
333
- }
334
-
335
- const handlers = this.domOn(document, {
336
- mousemove,
337
-
338
- mouseup(event) {
339
- mousemove(event)
340
- handlers.remove()
341
- }
342
- })
343
- },
344
-
345
332
  updateEditorOptions() {
346
333
  this.editor.setOptions(this.editorOptions)
347
334
  },
348
335
 
349
336
  async onClickLink(editor) {
350
- const attributes = await this.rootComponent.showDialog({
337
+ const attributes = await this.showDialog({
351
338
  components: {
352
339
  href: {
353
340
  type: 'url',
@@ -411,8 +398,8 @@ export default DitoTypeComponent.register('markup', {
411
398
  marks.strike && Strike,
412
399
  marks.small && Small,
413
400
  marks.code && Code,
414
- marks.subscript && Superscript,
415
- marks.superscript && Subscript,
401
+ marks.subscript && Subscript,
402
+ marks.superscript && Superscript,
416
403
  marks.link && LinkWithTitle,
417
404
 
418
405
  // Nodes: `schema.nodes`
@@ -681,7 +668,7 @@ const LinkWithTitle = Link.extend({
681
668
  a {
682
669
  pointer-events: none;
683
670
  cursor: default;
684
- color: blue;
671
+ color: $color-active;
685
672
  text-decoration: underline;
686
673
  }
687
674
 
@@ -1,13 +1,10 @@
1
1
  <template lang="pug">
2
2
  .dito-multiselect
3
3
  .dito-multiselect__inner
4
- DitoAffixes(
5
- :items="schema.prefix"
4
+ DitoInputAffixes(
6
5
  position="prefix"
7
- mode="input"
6
+ v-bind="inputAffixesProps.prefix"
8
7
  absolute
9
- :disabled="disabled"
10
- :parentContext="context"
11
8
  )
12
9
  VueMultiselect(
13
10
  :id="componentPath"
@@ -48,32 +45,25 @@
48
45
  )
49
46
  span {{ getLabelForOption(option) }}
50
47
  i.multiselect__tag-icon(
51
- tabindex="1"
48
+ tabindex="0"
52
49
  @keydown.enter.prevent="remove(option)"
53
50
  @mousedown.prevent="remove(option)"
54
51
  )
55
- DitoAffixes(
56
- :items="schema.suffix"
52
+ DitoInputAffixes(
57
53
  position="suffix"
58
- mode="input"
54
+ v-bind="inputAffixesProps.suffix"
59
55
  absolute
60
- :clearable="showClearButton"
61
- :disabled="disabled"
62
- :inlineInfo="inlineInfo"
63
- :parentContext="context"
64
56
  @clear="clear"
65
57
  )
66
- //- Edit button is never disabled, even if the field is disabled.
67
- DitoEditButtons(
58
+ DitoOptionsEditButtons(
68
59
  v-if="editable"
69
60
  :schema="schema"
70
61
  :dataPath="dataPath"
71
62
  :data="data"
72
63
  :meta="meta"
73
64
  :store="store"
74
- :disabled="false"
75
- :editable="editable"
76
- :editPath="editPath"
65
+ :parentContext="context"
66
+ :optionValue="selectedValue"
77
67
  )
78
68
  </template>
79
69
 
@@ -82,7 +72,9 @@ import DitoTypeComponent from '../DitoTypeComponent.js'
82
72
  import DitoContext from '../DitoContext.js'
83
73
  import TypeMixin from '../mixins/TypeMixin.js'
84
74
  import OptionsMixin from '../mixins/OptionsMixin.js'
85
- import DitoAffixes from '../components/DitoAffixes.vue'
75
+ import DitoInputAffixes from '../components/DitoInputAffixes.vue'
76
+ import { getInputAffixesProps } from '../utils/affixes.js'
77
+ import DitoOptionsEditButtons from '../components/DitoOptionsEditButtons.vue'
86
78
  import VueMultiselect from 'vue-multiselect'
87
79
  import { getSchemaAccessor } from '../utils/accessor.js'
88
80
  import { isArray, isBoolean, isObject, isString } from '@ditojs/utils'
@@ -90,7 +82,7 @@ import { isArray, isBoolean, isObject, isString } from '@ditojs/utils'
90
82
  // @vue/component
91
83
  export default DitoTypeComponent.register('multiselect', {
92
84
  mixins: [OptionsMixin],
93
- components: { DitoAffixes, VueMultiselect },
85
+ components: { DitoInputAffixes, DitoOptionsEditButtons, VueMultiselect },
94
86
 
95
87
  data() {
96
88
  return {
@@ -98,25 +90,35 @@ export default DitoTypeComponent.register('multiselect', {
98
90
  isLoadingSearchedOptions: false,
99
91
  // The term of the current search, see `onSearchChange()`:
100
92
  currentSearchTerm: null,
93
+ // The options of the tags that were added, see `onAddTag()`:
94
+ addedTagOptions: [],
101
95
  populate: false
102
96
  }
103
97
  },
104
98
 
105
99
  computed: {
100
+ inputAffixesProps() {
101
+ return getInputAffixesProps(this)
102
+ },
103
+
106
104
  selectedOptions: {
107
105
  get() {
108
106
  // Values without options stay visible once the options are loaded,
109
- // and are kept when the selection changes, see `getFallbackOption()`,
110
- // unless the multiselect is taggable and can add them as options.
107
+ // and are kept when the selection changes, see `getFallbackOption()`.
108
+ // Taggable multiselects may have no options besides their values.
111
109
  return this.multiple
112
- ? this.hasOptions && isArray(this.value)
110
+ ? (
111
+ isArray(this.value) && (
112
+ this.hasOptions ||
113
+ this.taggable && !this.isLoadingOptions
114
+ )
115
+ )
113
116
  ? this.value.map(value => {
114
117
  const selectedValue = this.relate
115
118
  ? this.getValueForOption(value)
116
119
  : value
117
120
  return (
118
121
  this.getOptionForValue(selectedValue) ||
119
- this.addTagOption(selectedValue) ||
120
122
  this.getFallbackOption(value)
121
123
  )
122
124
  })
@@ -134,6 +136,22 @@ export default DitoTypeComponent.register('multiselect', {
134
136
  }
135
137
  },
136
138
 
139
+ // @override
140
+ options() {
141
+ const options = OptionsMixin.computed.options.call(this)
142
+ // Add the options of the added tags that the options don't contain yet,
143
+ // without modifying the options of the data model. Grouped options have
144
+ // no group to add them to.
145
+ const addedTagOptions = this.groupBy
146
+ ? []
147
+ : this.addedTagOptions.filter(
148
+ option => !this.isOptionAmong(option, options)
149
+ )
150
+ return addedTagOptions.length > 0
151
+ ? [...options, ...addedTagOptions]
152
+ : options
153
+ },
154
+
137
155
  activeOptions() {
138
156
  return this.searchedOptions || this.options
139
157
  },
@@ -175,11 +193,16 @@ export default DitoTypeComponent.register('multiselect', {
175
193
  if (isBoolean(placeholder)) {
176
194
  placeholder = placeholder ? undefined : null
177
195
  }
196
+ const { label } = this
178
197
  return placeholder === undefined
179
198
  ? searchable && taggable
180
- ? `Search or add a ${this.label}`
199
+ ? label
200
+ ? `Search or add a ${label}`
201
+ : 'Search or add'
181
202
  : searchable
182
- ? `Select or search ${this.label}`
203
+ ? label
204
+ ? `Select or search ${label}`
205
+ : 'Select or search'
183
206
  : undefined
184
207
  : placeholder
185
208
  },
@@ -198,29 +221,37 @@ export default DitoTypeComponent.register('multiselect', {
198
221
  },
199
222
 
200
223
  methods: {
201
- addTagOption(tag) {
202
- if (this.taggable) {
203
- const { optionLabel, optionValue } = this
204
- const option =
205
- optionLabel && optionValue
206
- ? {
207
- [optionLabel]: tag,
208
- // TODO: Define a simple schema option to convert the tag value
209
- // to something else, e.g. `toTag: tag => underscore(tag)`
210
- [optionValue]: tag
211
- }
212
- : tag
213
- this.options.push(option)
214
- return option
215
- }
224
+ createTagOption(tag) {
225
+ const { optionLabel, optionValue } = this
226
+ return optionLabel && optionValue
227
+ ? {
228
+ [optionLabel]: tag,
229
+ // TODO: Define a simple schema option to convert the tag value
230
+ // to something else, e.g. `toTag: tag => underscore(tag)`
231
+ [optionValue]: tag
232
+ }
233
+ : tag
234
+ },
235
+
236
+ // Returns whether `options` contain an option with the value of `option`.
237
+ isOptionAmong(option, options) {
238
+ const value = this.getValueForOption(option)
239
+ return options.some(
240
+ otherOption => this.getValueForOption(otherOption) === value
241
+ )
216
242
  },
217
243
 
218
244
  // Returns whether the option is a fallback option for a value without
219
245
  // one, see `getFallbackOption()`. Plain options are their own values.
246
+ // Taggable multiselects can hold any value.
220
247
  isUnavailableOption(option) {
221
- return this.optionValue
222
- ? !this.getOptionForValue(this.getValueForOption(option))
223
- : !this.options.includes(option)
248
+ return (
249
+ !this.taggable && (
250
+ this.optionValue
251
+ ? !this.getOptionForValue(this.getValueForOption(option))
252
+ : !this.options.includes(option)
253
+ )
254
+ )
224
255
  },
225
256
 
226
257
  // Returns an option for a value without one, e.g. of options that only
@@ -263,10 +294,13 @@ export default DitoTypeComponent.register('multiselect', {
263
294
  },
264
295
 
265
296
  onAddTag(tag) {
266
- const option = this.addTagOption(tag)
267
- if (option) {
268
- this.value ??= []
269
- this.value.push(this.getValueForOption(option))
297
+ if (this.taggable) {
298
+ const option = this.createTagOption(tag)
299
+ this.addedTagOptions.push(option)
300
+ // Select the option through `selectedOptions`, which emits the change:
301
+ this.selectedOptions = this.multiple
302
+ ? [...this.selectedOptions, option]
303
+ : option
270
304
  }
271
305
  },
272
306
 
@@ -346,10 +380,6 @@ $tag-line-height: 1em;
346
380
  align-items: center;
347
381
  }
348
382
 
349
- .dito-edit-buttons {
350
- margin-left: $form-spacing-half;
351
- }
352
-
353
383
  .multiselect {
354
384
  $self: last-selector(&);
355
385
 
@@ -10,22 +10,14 @@ DitoInput.dito-number(
10
10
  :step="stepValue"
11
11
  )
12
12
  template(#prefix)
13
- DitoAffixes(
14
- :items="schema.prefix"
13
+ DitoInputAffixes(
15
14
  position="prefix"
16
- mode="input"
17
- :disabled="disabled"
18
- :parentContext="context"
15
+ v-bind="inputAffixesProps.prefix"
19
16
  )
20
17
  template(#suffix)
21
- DitoAffixes(
22
- :items="schema.suffix"
18
+ DitoInputAffixes(
23
19
  position="suffix"
24
- mode="input"
25
- :clearable="showClearButton"
26
- :disabled="disabled"
27
- :inlineInfo="inlineInfo"
28
- :parentContext="context"
20
+ v-bind="inputAffixesProps.suffix"
29
21
  @clear="clear"
30
22
  )
31
23
  </template>
@@ -33,7 +25,8 @@ DitoInput.dito-number(
33
25
  <script>
34
26
  import DitoTypeComponent from '../DitoTypeComponent.js'
35
27
  import NumberMixin from '../mixins/NumberMixin.js'
36
- import DitoAffixes from '../components/DitoAffixes.vue'
28
+ import DitoInputAffixes from '../components/DitoInputAffixes.vue'
29
+ import { getInputAffixesProps } from '../utils/affixes.js'
37
30
  import { DitoInput } from '@ditojs/ui/src'
38
31
 
39
32
  export default DitoTypeComponent.register(
@@ -41,11 +34,15 @@ export default DitoTypeComponent.register(
41
34
  // @vue/component
42
35
  {
43
36
  mixins: [NumberMixin],
44
- components: { DitoInput, DitoAffixes },
37
+ components: { DitoInput, DitoInputAffixes },
45
38
  nativeField: true,
46
39
  textField: true,
47
40
 
48
41
  computed: {
42
+ inputAffixesProps() {
43
+ return getInputAffixesProps(this)
44
+ },
45
+
49
46
  isInteger() {
50
47
  return this.type === 'integer'
51
48
  }
@@ -56,7 +53,7 @@ export default DitoTypeComponent.register(
56
53
 
57
54
  <style lang="scss">
58
55
  // Only show spin buttons if the number component defines a step size.
59
- input[type='number']:not([step]) {
56
+ .dito-number input[type='number']:not([step]) {
60
57
  &::-webkit-inner-spin-button,
61
58
  &::-webkit-outer-spin-button {
62
59
  -webkit-appearance: none;
@@ -6,11 +6,8 @@
6
6
  .dito-object-content(
7
7
  v-if="objectData"
8
8
  )
9
- //- Support the same rendering options as TypeList:
10
- DitoSchemaInlined(
11
- v-if="isInlined"
12
- :label="objectLabel"
13
- :schema="getItemFormSchema(schema, objectData, context)"
9
+ DitoSourceItem(
10
+ :schema="schema"
14
11
  :dataPath="dataPath"
15
12
  :data="objectData"
16
13
  :meta="nestedMeta"
@@ -19,24 +16,8 @@
19
16
  :collapsed="collapsed"
20
17
  :collapsible="collapsible"
21
18
  :deletable="deletable"
22
- :accumulatedBasis="accumulatedBasis"
23
19
  @delete="deleteItem(objectData)"
24
20
  )
25
- component(
26
- v-else-if="schema.component"
27
- :is="schema.component"
28
- :dataPath="dataPath"
29
- :data="objectData"
30
- :nested="false"
31
- )
32
- span(
33
- v-else-if="render"
34
- v-html="render(getContext())"
35
- )
36
- span(
37
- v-else
38
- v-html="getItemLabel(schema, objectData)"
39
- )
40
21
  //- NOTE: For inlined objects, `DitoEditButtons` here only handle the create
41
22
  //- button outside of the schema, the other edit buttons inside are handled by
42
23
  //- `DitoSchemaInlined`.
@@ -45,7 +26,6 @@
45
26
  :schema="schema"
46
27
  :dataPath="dataPath"
47
28
  :data="objectData"
48
- :path="path"
49
29
  :meta="meta"
50
30
  :store="store"
51
31
  :disabled="disabled || isLoading"
@@ -60,34 +40,20 @@
60
40
 
61
41
  <script>
62
42
  import DitoTypeComponent from '../DitoTypeComponent.js'
63
- import DitoContext from '../DitoContext.js'
43
+ import DitoSourceItem from '../components/DitoSourceItem.vue'
64
44
  import SourceMixin from '../mixins/SourceMixin.js'
65
45
  import { resolveSchemaComponent } from '../utils/schema/setup.js'
66
46
 
67
47
  // @vue/component
68
48
  export default DitoTypeComponent.register('object', {
69
49
  mixins: [SourceMixin],
50
+ components: { DitoSourceItem },
70
51
 
71
52
  getSourceType(type) {
72
53
  // No need for transformation here. See TypeTreeList for details.
73
54
  return type
74
55
  },
75
56
 
76
- computed: {
77
- objectLabel() {
78
- // Only show a label if the object is collapsible.
79
- return this.collapsible
80
- ? this.getItemLabel(this.schema, this.objectData, { asObject: true })
81
- : null
82
- }
83
- },
84
-
85
- methods: {
86
- getContext() {
87
- return new DitoContext(this, { data: this.objectData })
88
- }
89
- },
90
-
91
57
  async processSchema(
92
58
  api,
93
59
  schema,
@@ -1,31 +1,44 @@
1
1
  <template lang="pug">
2
- ul.dito-radio-buttons(
2
+ DitoOptionList.dito-radio-buttons(
3
3
  :id="componentPath"
4
4
  role="radiogroup"
5
5
  :aria-label="label"
6
- :class="`dito-layout--${schema.layout || 'vertical'}`"
6
+ :options="options"
7
+ :isGrouped="!!groupBy"
8
+ :layout="schema.layout"
7
9
  )
8
- li(
9
- v-for="option in options"
10
- )
10
+ template(#option="{ option, isFirstOption }")
11
11
  label
12
12
  input.dito-radio-button(
13
- ref="element"
13
+ :ref="isFirstOption ? 'element' : undefined"
14
14
  v-model="selectedValue"
15
15
  type="radio"
16
16
  :value="getValueForOption(option)"
17
17
  v-bind="attributes"
18
18
  )
19
19
  | {{ getLabelForOption(option) }}
20
+ DitoOptionsEditButtons(
21
+ v-if="editable"
22
+ :schema="schema"
23
+ :dataPath="dataPath"
24
+ :data="data"
25
+ :meta="meta"
26
+ :store="store"
27
+ :parentContext="context"
28
+ :optionValue="getValueForOption(option)"
29
+ )
20
30
  </template>
21
31
 
22
32
  <script>
23
33
  import DitoTypeComponent from '../DitoTypeComponent.js'
24
34
  import OptionsMixin from '../mixins/OptionsMixin.js'
35
+ import DitoOptionList from '../components/DitoOptionList.vue'
36
+ import DitoOptionsEditButtons from '../components/DitoOptionsEditButtons.vue'
25
37
 
26
38
  // @vue/component
27
39
  export default DitoTypeComponent.register('radio', {
28
40
  mixins: [OptionsMixin],
41
+ components: { DitoOptionList, DitoOptionsEditButtons },
29
42
 
30
43
  nativeField: true,
31
44
  defaultWidth: 'auto'