@ditojs/admin 3.2.6 → 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.
- package/dist/dito-admin.css +1 -1
- package/dist/dito-admin.es.js +6012 -5827
- package/dist/dito-admin.umd.js +3 -7
- package/package.json +18 -17
- package/src/DitoAdmin.js +44 -24
- package/src/DitoContext.js +28 -2
- package/src/DitoTypeComponent.js +1 -0
- package/src/appState.js +6 -3
- package/src/components/DitoAccount.vue +35 -27
- package/src/components/DitoAffixes.vue +9 -2
- package/src/components/DitoClipboard.vue +59 -93
- package/src/components/DitoConfirmMessage.vue +18 -0
- package/src/components/DitoContainer.vue +56 -18
- package/src/components/DitoCreateButton.vue +36 -84
- package/src/components/DitoDialog.vue +20 -15
- package/src/components/DitoDragHandle.vue +44 -0
- package/src/components/DitoDraggable.vue +43 -16
- package/src/components/DitoEditButtons.vue +48 -24
- package/src/components/DitoErrors.vue +9 -56
- package/src/components/DitoForm.vue +82 -42
- package/src/components/DitoInputAffixes.vue +50 -0
- package/src/components/DitoLabel.vue +1 -15
- package/src/components/DitoMenu.vue +45 -52
- package/src/components/DitoNotifications.vue +38 -14
- package/src/components/DitoOptionList.vue +81 -0
- package/src/components/DitoOptionsEditButtons.vue +57 -0
- package/src/components/DitoPagination.vue +1 -8
- package/src/components/DitoPane.vue +51 -39
- package/src/components/DitoPanel.vue +12 -26
- package/src/components/DitoPanels.vue +11 -5
- package/src/components/DitoResizeHandle.vue +47 -0
- package/src/components/DitoRoot.vue +113 -320
- package/src/components/DitoSchema.vue +72 -63
- package/src/components/DitoSchemaInlined.vue +8 -9
- package/src/components/DitoScopes.vue +14 -18
- package/src/components/DitoSidebar.vue +4 -29
- package/src/components/DitoSourceItem.vue +114 -0
- package/src/components/DitoTableCell.vue +4 -0
- package/src/components/DitoTableHead.vue +15 -17
- package/src/components/DitoTabs.vue +25 -0
- package/src/components/DitoTrail.vue +25 -25
- package/src/components/DitoTreeItem.vue +102 -141
- package/src/components/DitoUploadFile.vue +5 -2
- package/src/components/DitoView.vue +33 -24
- package/src/index.js +4 -1
- package/src/mixins/DitoMixin.js +77 -110
- package/src/mixins/DomMixin.js +12 -14
- package/src/mixins/EmitterMixin.js +3 -2
- package/src/mixins/ItemMixin.js +38 -94
- package/src/mixins/OptionsMixin.js +38 -25
- package/src/mixins/ResourceMixin.js +182 -111
- package/src/mixins/RouteMixin.js +61 -44
- package/src/mixins/SchemaParentMixin.js +4 -1
- package/src/mixins/SourceMixin.js +118 -100
- package/src/mixins/TypeMixin.js +42 -40
- package/src/mixins/ValidationMixin.js +16 -4
- package/src/mixins/ValidatorMixin.js +1 -1
- package/src/styles/_settings.scss +4 -0
- package/src/styles/_tippy.scss +0 -11
- package/src/styles/style.scss +0 -1
- package/src/types/DitoTypeButton.vue +11 -14
- package/src/types/DitoTypeCheckboxes.vue +35 -17
- package/src/types/DitoTypeCode.vue +19 -41
- package/src/types/DitoTypeColor.vue +67 -19
- package/src/types/DitoTypeDate.vue +11 -14
- package/src/types/DitoTypeList.vue +50 -62
- package/src/types/DitoTypeMarkup.vue +41 -54
- package/src/types/DitoTypeMultiselect.vue +82 -52
- package/src/types/DitoTypeNumber.vue +12 -15
- package/src/types/DitoTypeObject.vue +4 -38
- package/src/types/DitoTypeRadio.vue +19 -6
- package/src/types/DitoTypeSection.vue +23 -9
- package/src/types/DitoTypeSelect.vue +39 -23
- package/src/types/DitoTypeSlider.vue +9 -1
- package/src/types/DitoTypeSwitch.vue +1 -1
- package/src/types/DitoTypeText.vue +11 -14
- package/src/types/DitoTypeTreeList.vue +46 -8
- package/src/types/DitoTypeUpload.vue +190 -67
- package/src/utils/DataModel.js +368 -146
- package/src/utils/FileDropTargets.js +97 -0
- package/src/utils/ItemStores.js +31 -0
- package/src/utils/ListQuery.js +115 -0
- package/src/utils/LoadingTracker.js +76 -0
- package/src/utils/SchemaGraph.js +3 -2
- package/src/utils/Session.js +151 -0
- package/src/utils/ViewRegistry.js +85 -0
- package/src/utils/accessor.js +12 -4
- package/src/utils/affixes.js +33 -0
- package/src/utils/changeOnceEdited.js +33 -0
- package/src/utils/clipboard.js +55 -0
- package/src/utils/data.js +10 -5
- package/src/utils/dialogs.js +59 -0
- package/src/utils/dragResize.js +61 -0
- package/src/utils/filter.js +36 -2
- package/src/utils/html.js +9 -0
- package/src/utils/list.js +10 -0
- package/src/utils/options.js +4 -0
- package/src/utils/registration.js +18 -0
- package/src/utils/request.js +108 -13
- package/src/utils/route.js +6 -17
- package/src/utils/schema/data.js +29 -2
- package/src/utils/schema/events.js +19 -0
- package/src/utils/schema/item.js +114 -0
- package/src/utils/schema/lookup.js +39 -14
- package/src/utils/schema/setup.js +5 -5
- package/src/utils/schema/validation.js +16 -2
- package/src/utils/uid.js +105 -8
- package/src/validators/_decimals.js +16 -5
- package/types/index.d.ts +168 -41
- package/src/mixins/LoadingMixin.js +0 -23
- package/src/mixins/PulldownMixin.js +0 -72
- 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
|
-
|
|
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
|
-
|
|
23
|
+
DitoResizeHandle(
|
|
24
24
|
v-if="resizable"
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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 &&
|
|
415
|
-
marks.superscript &&
|
|
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:
|
|
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
|
-
|
|
5
|
-
:items="schema.prefix"
|
|
4
|
+
DitoInputAffixes(
|
|
6
5
|
position="prefix"
|
|
7
|
-
|
|
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="
|
|
48
|
+
tabindex="0"
|
|
52
49
|
@keydown.enter.prevent="remove(option)"
|
|
53
50
|
@mousedown.prevent="remove(option)"
|
|
54
51
|
)
|
|
55
|
-
|
|
56
|
-
:items="schema.suffix"
|
|
52
|
+
DitoInputAffixes(
|
|
57
53
|
position="suffix"
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
:
|
|
75
|
-
:
|
|
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
|
|
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: {
|
|
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
|
-
//
|
|
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
|
-
?
|
|
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
|
-
?
|
|
199
|
+
? label
|
|
200
|
+
? `Search or add a ${label}`
|
|
201
|
+
: 'Search or add'
|
|
181
202
|
: searchable
|
|
182
|
-
?
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
this.
|
|
269
|
-
|
|
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
|
-
|
|
14
|
-
:items="schema.prefix"
|
|
13
|
+
DitoInputAffixes(
|
|
15
14
|
position="prefix"
|
|
16
|
-
|
|
17
|
-
:disabled="disabled"
|
|
18
|
-
:parentContext="context"
|
|
15
|
+
v-bind="inputAffixesProps.prefix"
|
|
19
16
|
)
|
|
20
17
|
template(#suffix)
|
|
21
|
-
|
|
22
|
-
:items="schema.suffix"
|
|
18
|
+
DitoInputAffixes(
|
|
23
19
|
position="suffix"
|
|
24
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
10
|
-
|
|
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
|
|
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
|
-
|
|
2
|
+
DitoOptionList.dito-radio-buttons(
|
|
3
3
|
:id="componentPath"
|
|
4
4
|
role="radiogroup"
|
|
5
5
|
:aria-label="label"
|
|
6
|
-
:
|
|
6
|
+
:options="options"
|
|
7
|
+
:isGrouped="!!groupBy"
|
|
8
|
+
:layout="schema.layout"
|
|
7
9
|
)
|
|
8
|
-
|
|
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'
|