@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
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.dito-trail(aria-label="Breadcrumb")
|
|
2
|
+
nav.dito-trail(aria-label="Breadcrumb")
|
|
3
3
|
ul
|
|
4
4
|
li(
|
|
5
5
|
v-for="component in trail"
|
|
6
6
|
)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
:
|
|
10
|
-
|
|
11
|
-
@click.prevent.stop="onClickComponent(component)"
|
|
7
|
+
RouterLink(
|
|
8
|
+
v-slot="{ href, navigate }"
|
|
9
|
+
:to="getComponentRoute(component)"
|
|
10
|
+
custom
|
|
12
11
|
)
|
|
13
|
-
|
|
14
|
-
:class="{ 'dito-
|
|
12
|
+
a.dito-trail__link(
|
|
13
|
+
:class="{ 'dito-trail__link--active': isCurrentComponent(component) }"
|
|
14
|
+
:aria-current="isCurrentComponent(component) ? 'page' : null"
|
|
15
|
+
:href="href"
|
|
16
|
+
@click="navigate"
|
|
15
17
|
)
|
|
16
|
-
|
|
18
|
+
span.dito-trail__text(
|
|
19
|
+
:class="{ 'dito-trail__text--dirty': component.isDirty }"
|
|
20
|
+
)
|
|
21
|
+
| {{ component.breadcrumb }}
|
|
17
22
|
slot
|
|
18
23
|
</template>
|
|
19
24
|
|
|
20
25
|
<script>
|
|
21
26
|
import DitoComponent from '../DitoComponent.js'
|
|
27
|
+
import { getViewLinkPath } from '../utils/schema/lookup.js'
|
|
22
28
|
|
|
23
29
|
// @vue/component
|
|
24
30
|
export default DitoComponent.component('DitoTrail', {
|
|
25
31
|
computed: {
|
|
26
32
|
trail() {
|
|
27
|
-
// Leave out the components that are
|
|
33
|
+
// The route components by route level. Leave out the components that are
|
|
34
|
+
// being left, until they're unmounted.
|
|
28
35
|
return this.appState.routeComponents.filter(
|
|
29
36
|
component => !component.isLeavingRoute
|
|
30
37
|
)
|
|
@@ -32,23 +39,16 @@ export default DitoComponent.component('DitoTrail', {
|
|
|
32
39
|
},
|
|
33
40
|
|
|
34
41
|
methods: {
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
getComponentHref(component) {
|
|
44
|
-
return this.$router.resolve(this.getComponentRoutePath(component)).href
|
|
42
|
+
getComponentRoute(component) {
|
|
43
|
+
// `force` reloads the view even if it is already the current route.
|
|
44
|
+
return {
|
|
45
|
+
path: getViewLinkPath(component.schema, component.path),
|
|
46
|
+
force: true
|
|
47
|
+
}
|
|
45
48
|
},
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
this.$
|
|
49
|
-
path: this.getComponentRoutePath(component),
|
|
50
|
-
force: true
|
|
51
|
-
})
|
|
50
|
+
isCurrentComponent(component) {
|
|
51
|
+
return component.path === this.$route.path
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
})
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
'dito-tree-item--active': active
|
|
7
7
|
}`
|
|
8
8
|
:style="level > 0 && { '--level': level }"
|
|
9
|
-
:data-path="path"
|
|
10
9
|
)
|
|
11
10
|
.dito-tree-header(
|
|
12
11
|
v-if="label"
|
|
@@ -36,26 +35,24 @@
|
|
|
36
35
|
.dito-tree-label(
|
|
37
36
|
v-html="label"
|
|
38
37
|
)
|
|
39
|
-
.dito-buttons
|
|
38
|
+
DitoEditButtons.dito-buttons--small(
|
|
40
39
|
v-if="hasEditButtons"
|
|
40
|
+
:round="false"
|
|
41
|
+
:schema="schema"
|
|
42
|
+
:formSchema="getItemFormSchema(schema, data, context)"
|
|
43
|
+
:dataPath="dataPath"
|
|
44
|
+
:data="data"
|
|
45
|
+
:meta="nestedMeta"
|
|
46
|
+
:store="store"
|
|
47
|
+
:disabled="disabled"
|
|
48
|
+
:draggable="draggable"
|
|
49
|
+
:editable="editable"
|
|
50
|
+
:deletable="deletable"
|
|
51
|
+
:editPath="editPath"
|
|
52
|
+
:editQuery="$route.query"
|
|
53
|
+
@delete="$emit('delete')"
|
|
54
|
+
@move="delta => $emit('move', delta)"
|
|
41
55
|
)
|
|
42
|
-
//- Firefox doesn't like <button> here, so use <a> instead:
|
|
43
|
-
a.dito-button(
|
|
44
|
-
v-if="draggable"
|
|
45
|
-
v-bind="getButtonAttributes(verbs.drag)"
|
|
46
|
-
)
|
|
47
|
-
button.dito-button(
|
|
48
|
-
v-if="editable"
|
|
49
|
-
type="button"
|
|
50
|
-
v-bind="getButtonAttributes(verbs.edit)"
|
|
51
|
-
@click="onEdit"
|
|
52
|
-
)
|
|
53
|
-
button.dito-button(
|
|
54
|
-
v-if="deletable"
|
|
55
|
-
type="button"
|
|
56
|
-
v-bind="getButtonAttributes(verbs.delete)"
|
|
57
|
-
@click="onDelete"
|
|
58
|
-
)
|
|
59
56
|
table.dito-properties(
|
|
60
57
|
v-if="properties"
|
|
61
58
|
v-show="opened"
|
|
@@ -70,8 +67,8 @@
|
|
|
70
67
|
)
|
|
71
68
|
DitoTableCell(
|
|
72
69
|
:cell="property"
|
|
73
|
-
:schema="
|
|
74
|
-
:dataPath="
|
|
70
|
+
:schema="schema"
|
|
71
|
+
:dataPath="dataPath"
|
|
75
72
|
:data="data"
|
|
76
73
|
:meta="nestedMeta"
|
|
77
74
|
:store="store"
|
|
@@ -83,7 +80,7 @@
|
|
|
83
80
|
:options="getDraggableOptions(true)"
|
|
84
81
|
:draggable="childrenDraggable"
|
|
85
82
|
:modelValue="childrenList"
|
|
86
|
-
@update:modelValue="
|
|
83
|
+
@update:modelValue="changeChildren"
|
|
87
84
|
)
|
|
88
85
|
DitoTreeItem(
|
|
89
86
|
v-for="(item, index) in childrenItems"
|
|
@@ -97,7 +94,9 @@
|
|
|
97
94
|
:draggable="childrenDraggable"
|
|
98
95
|
:label="getItemLabel(childrenSchema, item.data, { index })"
|
|
99
96
|
:level="level + 1"
|
|
100
|
-
@delete="deleteChild(
|
|
97
|
+
@delete="deleteChild(item.data)"
|
|
98
|
+
@move="delta => moveChild(item.data, delta)"
|
|
99
|
+
@change-children="change => $emit('changeChildren', change)"
|
|
101
100
|
)
|
|
102
101
|
//- TODO: Convert dito-tree-item to use dito-label internally, and then
|
|
103
102
|
//- pass `asObject: true` in the `getItemLabel()` call above.
|
|
@@ -105,19 +104,25 @@
|
|
|
105
104
|
|
|
106
105
|
<script>
|
|
107
106
|
import DitoComponent from '../DitoComponent.js'
|
|
107
|
+
import DitoEditButtons from './DitoEditButtons.vue'
|
|
108
108
|
import ItemMixin from '../mixins/ItemMixin'
|
|
109
109
|
import SortableMixin from '../mixins/SortableMixin.js'
|
|
110
110
|
import { appendDataPath, getRelativeDataPath } from '../utils/data.js'
|
|
111
111
|
import { getSchemaAccessor } from '../utils/accessor.js'
|
|
112
112
|
import { getNamedSchemas, hasFormSchema } from '../utils/schema/lookup.js'
|
|
113
|
-
import {
|
|
114
|
-
import {
|
|
115
|
-
|
|
113
|
+
import { getTextFromHtml } from '../utils/html.js'
|
|
114
|
+
import { getListWithMovedItem } from '../utils/list.js'
|
|
115
|
+
import { confirmAndRemove } from '../utils/dialogs.js'
|
|
116
|
+
import { isPathWithin } from '../utils/route.js'
|
|
117
|
+
|
|
118
|
+
// Renders an item of a tree list or tree object, see `DitoTypeTreeList`, which
|
|
119
|
+
// is the item's `sourceComponent`. Items don't write the tree: They emit the
|
|
120
|
+
// changed children with `changeChildren`, which the tree list applies.
|
|
116
121
|
// @vue/component
|
|
117
122
|
export default DitoComponent.component('DitoTreeItem', {
|
|
118
123
|
mixins: [ItemMixin, SortableMixin],
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
components: { DitoEditButtons },
|
|
125
|
+
emits: ['delete', 'move', 'changeChildren'],
|
|
121
126
|
|
|
122
127
|
props: {
|
|
123
128
|
schema: { type: Object, required: true },
|
|
@@ -142,7 +147,7 @@ export default DitoComponent.component('DitoTreeItem', {
|
|
|
142
147
|
// Continue the path of the tree list with the item indices and children
|
|
143
148
|
// names of the data path. The root item's data path doesn't contain the
|
|
144
149
|
// list's name, see `DitoTypeTreeList.treeDataPath`.
|
|
145
|
-
const { componentPath, dataPath } = this.
|
|
150
|
+
const { componentPath, dataPath } = this.sourceComponent
|
|
146
151
|
const relativeDataPath = getRelativeDataPath(this.dataPath, dataPath)
|
|
147
152
|
return relativeDataPath
|
|
148
153
|
? appendDataPath(componentPath, relativeDataPath)
|
|
@@ -150,15 +155,15 @@ export default DitoComponent.component('DitoTreeItem', {
|
|
|
150
155
|
},
|
|
151
156
|
|
|
152
157
|
meta() {
|
|
153
|
-
return this.
|
|
158
|
+
return this.sourceComponent.meta
|
|
154
159
|
},
|
|
155
160
|
|
|
156
161
|
store() {
|
|
157
|
-
return this.
|
|
162
|
+
return this.sourceComponent.store
|
|
158
163
|
},
|
|
159
164
|
|
|
160
165
|
disabled() {
|
|
161
|
-
return this.
|
|
166
|
+
return this.sourceComponent.disabled
|
|
162
167
|
},
|
|
163
168
|
|
|
164
169
|
nestedMeta() {
|
|
@@ -172,26 +177,13 @@ export default DitoComponent.component('DitoTreeItem', {
|
|
|
172
177
|
return getNamedSchemas(this.schema.properties)
|
|
173
178
|
},
|
|
174
179
|
|
|
175
|
-
// TODO: Should this be named `sourceSchema` instead? Use SourceMixin?
|
|
176
180
|
childrenSchema() {
|
|
177
181
|
return this.schema.children
|
|
178
182
|
},
|
|
179
183
|
|
|
180
|
-
childrenList
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return name && this.data[name]
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
set(value) {
|
|
187
|
-
const name = this.childrenSchema?.name
|
|
188
|
-
if (name) {
|
|
189
|
-
updateOrder(this.childrenSchema, value)
|
|
190
|
-
// eslint-disable-next-line vue/no-mutating-props
|
|
191
|
-
this.data[name] = value
|
|
192
|
-
this.$emit('update:data', value)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
184
|
+
childrenList() {
|
|
185
|
+
const name = this.childrenSchema?.name
|
|
186
|
+
return name && this.data[name]
|
|
195
187
|
},
|
|
196
188
|
|
|
197
189
|
childrenDraggable() {
|
|
@@ -220,7 +212,7 @@ export default DitoComponent.component('DitoTreeItem', {
|
|
|
220
212
|
childrenItems() {
|
|
221
213
|
const { childrenSchema, childrenList } = this
|
|
222
214
|
if (childrenSchema && childrenList) {
|
|
223
|
-
const { editPath } = this.
|
|
215
|
+
const { editPath } = this.sourceComponent
|
|
224
216
|
const childrenOpen = !this.path && childrenSchema.open
|
|
225
217
|
// Build a children list with child meta information for the template.
|
|
226
218
|
return childrenList.map((data, index) => {
|
|
@@ -231,7 +223,7 @@ export default DitoComponent.component('DitoTreeItem', {
|
|
|
231
223
|
const open = (
|
|
232
224
|
childrenOpen ||
|
|
233
225
|
// Only count as "in edit path" when it's not the full edit path.
|
|
234
|
-
|
|
226
|
+
!!path && editPath !== path && isPathWithin(editPath, path)
|
|
235
227
|
)
|
|
236
228
|
const active = editPath === path
|
|
237
229
|
return { data, path, open, active }
|
|
@@ -241,7 +233,7 @@ export default DitoComponent.component('DitoTreeItem', {
|
|
|
241
233
|
},
|
|
242
234
|
|
|
243
235
|
ariaLabel() {
|
|
244
|
-
return this.label ?
|
|
236
|
+
return this.label ? getTextFromHtml(this.label) : null
|
|
245
237
|
},
|
|
246
238
|
|
|
247
239
|
details() {
|
|
@@ -258,17 +250,13 @@ export default DitoComponent.component('DitoTreeItem', {
|
|
|
258
250
|
return this.draggable || this.editable || this.deletable
|
|
259
251
|
},
|
|
260
252
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
get(creatable) {
|
|
269
|
-
return creatable && hasFormSchema(this.schema)
|
|
270
|
-
}
|
|
271
|
-
}),
|
|
253
|
+
editPath() {
|
|
254
|
+
// The edit routes of the items are handled by the routes of the tree
|
|
255
|
+
// list, allowing reloads as well, see `DitoTypeTreeList.processSchema()`.
|
|
256
|
+
return this.editable && this.path
|
|
257
|
+
? `${this.sourceComponent.path}${this.path}`
|
|
258
|
+
: null
|
|
259
|
+
},
|
|
272
260
|
|
|
273
261
|
editable: getSchemaAccessor('editable', {
|
|
274
262
|
type: Boolean,
|
|
@@ -284,91 +272,60 @@ export default DitoComponent.component('DitoTreeItem', {
|
|
|
284
272
|
})
|
|
285
273
|
},
|
|
286
274
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
path: `${this.container.path}${path}`,
|
|
297
|
-
// Preserve current query
|
|
298
|
-
query: this.$route.query
|
|
299
|
-
})
|
|
300
|
-
},
|
|
301
|
-
|
|
302
|
-
onEdit() {
|
|
303
|
-
this.editPath(this.path)
|
|
304
|
-
},
|
|
305
|
-
|
|
306
|
-
// Items are removed from the children of their parent, see
|
|
307
|
-
// `deleteChild()`.
|
|
308
|
-
onDelete() {
|
|
309
|
-
this.$emit('delete')
|
|
310
|
-
},
|
|
275
|
+
watch: {
|
|
276
|
+
open(open) {
|
|
277
|
+
// Open the item also when it comes into the edit path later, e.g. when
|
|
278
|
+
// navigating to the form of one of its children.
|
|
279
|
+
if (open) {
|
|
280
|
+
this.opened = true
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
},
|
|
311
284
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const label = this.getItemLabel(childrenSchema, item, {
|
|
320
|
-
index,
|
|
321
|
-
extended: true
|
|
285
|
+
methods: {
|
|
286
|
+
changeChildren(children) {
|
|
287
|
+
this.$emit('changeChildren', {
|
|
288
|
+
data: this.data,
|
|
289
|
+
childrenSchema: this.childrenSchema,
|
|
290
|
+
path: this.path,
|
|
291
|
+
children
|
|
322
292
|
})
|
|
323
|
-
if (
|
|
324
|
-
window.confirm(`Do you really want to ${this.verbs.delete} ${label}?`)
|
|
325
|
-
) {
|
|
326
|
-
if (this.isEditingChildFrom(index)) {
|
|
327
|
-
this.$router.push({
|
|
328
|
-
path: container.path,
|
|
329
|
-
query: this.$route.query
|
|
330
|
-
})
|
|
331
|
-
}
|
|
332
|
-
this.childrenList = this.childrenList.toSpliced(index, 1)
|
|
333
|
-
this.onChange()
|
|
334
|
-
this.notify({
|
|
335
|
-
type: 'info',
|
|
336
|
-
title: 'Successfully Removed',
|
|
337
|
-
text: [`${label} was ${this.verbs.deleted}.`, container.transientNote]
|
|
338
|
-
})
|
|
339
|
-
}
|
|
340
293
|
},
|
|
341
294
|
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return false
|
|
295
|
+
// Moves the child by `delta` positions, see `DitoDragHandle`.
|
|
296
|
+
moveChild(child, delta) {
|
|
297
|
+
const children = getListWithMovedItem(this.childrenList, child, delta)
|
|
298
|
+
if (children) {
|
|
299
|
+
this.changeChildren(children)
|
|
348
300
|
}
|
|
349
|
-
const prefix = `${this.path}/${childrenPath}/`
|
|
350
|
-
const { editPath } = this.container
|
|
351
|
-
if (!editPath.startsWith(prefix)) {
|
|
352
|
-
return false
|
|
353
|
-
}
|
|
354
|
-
const [childIndex] = editPath.slice(prefix.length).split('/')
|
|
355
|
-
return Number(childIndex) >= index
|
|
356
301
|
},
|
|
357
302
|
|
|
358
|
-
|
|
359
|
-
|
|
303
|
+
// Removes the child after confirming it, like inlined lists do. The removal
|
|
304
|
+
// is transient, as the tree is part of its form's data.
|
|
305
|
+
async deleteChild(child) {
|
|
306
|
+
await confirmAndRemove(this, {
|
|
307
|
+
label: this.getItemLabel(this.childrenSchema, child, {
|
|
308
|
+
index: this.childrenList.indexOf(child),
|
|
309
|
+
extended: true
|
|
310
|
+
}),
|
|
311
|
+
isTransient: true,
|
|
312
|
+
remove: () => {
|
|
313
|
+
// Look up the child by identity once confirmed, as the children may
|
|
314
|
+
// have changed while the dialog was open.
|
|
315
|
+
const children = this.childrenList.filter(it => it !== child)
|
|
316
|
+
if (children.length === this.childrenList.length) {
|
|
317
|
+
return false
|
|
318
|
+
}
|
|
319
|
+
this.changeChildren(children)
|
|
320
|
+
}
|
|
321
|
+
})
|
|
360
322
|
},
|
|
361
323
|
|
|
362
324
|
// @override
|
|
363
|
-
onEndDrag(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (item.classList.contains('dito-tree-item--active')) {
|
|
368
|
-
this.$nextTick(() => {
|
|
369
|
-
this.editPath(event.item.dataset.path)
|
|
370
|
-
})
|
|
371
|
-
}
|
|
325
|
+
onEndDrag() {
|
|
326
|
+
// The tree list applies the reordered children and notifies of the
|
|
327
|
+
// change, see `DitoTypeTreeList.changeChildren()`.
|
|
328
|
+
this.isDragging = false
|
|
372
329
|
}
|
|
373
330
|
}
|
|
374
331
|
})
|
|
@@ -444,18 +401,22 @@ export default DitoComponent.component('DitoTreeItem', {
|
|
|
444
401
|
.dito-buttons {
|
|
445
402
|
flex: auto;
|
|
446
403
|
display: flex;
|
|
447
|
-
|
|
404
|
+
// Transparent rather than hidden, so the buttons can be focused with the
|
|
405
|
+
// keyboard, which shows them, see below.
|
|
406
|
+
opacity: 0;
|
|
448
407
|
height: 100%;
|
|
449
408
|
margin: 1px;
|
|
450
409
|
margin-right: 0;
|
|
451
410
|
}
|
|
452
411
|
|
|
453
|
-
.dito-tree-header:hover
|
|
412
|
+
.dito-tree-header:hover,
|
|
413
|
+
.dito-tree-header:focus-within {
|
|
454
414
|
> .dito-buttons {
|
|
455
|
-
|
|
456
|
-
}
|
|
415
|
+
opacity: 1;
|
|
416
|
+
}
|
|
457
417
|
}
|
|
458
418
|
|
|
419
|
+
// Hide buttons during dragging
|
|
459
420
|
&--dragging {
|
|
460
421
|
.dito-tree-header {
|
|
461
422
|
> .dito-buttons {
|
|
@@ -24,6 +24,9 @@ import DitoComponent from '../DitoComponent.js'
|
|
|
24
24
|
export default DitoComponent.component('DitoUploadFile', {
|
|
25
25
|
props: {
|
|
26
26
|
file: { type: Object, required: true },
|
|
27
|
+
// The file object of the upload component while the file is added through
|
|
28
|
+
// it, see `DitoTypeUpload.getUpload()`.
|
|
29
|
+
upload: { type: Object, default: null },
|
|
27
30
|
thumbnail: { type: String, default: null },
|
|
28
31
|
thumbnailUrl: { type: String, default: null }
|
|
29
32
|
},
|
|
@@ -48,7 +51,7 @@ export default DitoComponent.component('DitoUploadFile', {
|
|
|
48
51
|
},
|
|
49
52
|
|
|
50
53
|
watch: {
|
|
51
|
-
'
|
|
54
|
+
'upload.file': {
|
|
52
55
|
immediate: true,
|
|
53
56
|
handler(file) {
|
|
54
57
|
if (this.thumbnail && file?.type.startsWith('image/')) {
|
|
@@ -74,7 +77,7 @@ const TYPES = {
|
|
|
74
77
|
'image/png': 'PNG',
|
|
75
78
|
'image/gif': 'GIF',
|
|
76
79
|
'image/svg+xml': 'SVG',
|
|
77
|
-
'
|
|
80
|
+
'video/mp4': 'MP4',
|
|
78
81
|
'audio/mpeg': 'MP3',
|
|
79
82
|
'application/json': 'JSON',
|
|
80
83
|
'application/xml': 'XML',
|
|
@@ -29,6 +29,7 @@ template(
|
|
|
29
29
|
|
|
30
30
|
<script>
|
|
31
31
|
import { markRaw } from 'vue'
|
|
32
|
+
import { deprecate } from '@ditojs/utils'
|
|
32
33
|
import DitoComponent from '../DitoComponent.js'
|
|
33
34
|
import RouteMixin from '../mixins/RouteMixin.js'
|
|
34
35
|
import {
|
|
@@ -37,25 +38,31 @@ import {
|
|
|
37
38
|
} from '../utils/schema/structure.js'
|
|
38
39
|
import { hasResource } from '../utils/resource.js'
|
|
39
40
|
import { DataModel } from '../utils/DataModel.js'
|
|
41
|
+
import { LoadingTracker, LoadingSwitch } from '../utils/LoadingTracker.js'
|
|
40
42
|
|
|
41
43
|
// @vue/component
|
|
42
44
|
export default DitoComponent.component('DitoView', {
|
|
43
45
|
mixins: [RouteMixin],
|
|
44
46
|
|
|
45
47
|
provide() {
|
|
46
|
-
// Redirect $sourceComponent and $resourceComponent to the main component:
|
|
47
48
|
return {
|
|
49
|
+
// Redirect $sourceComponent and $resourceComponent to the main component:
|
|
48
50
|
$sourceComponent: () => this.mainComponent?.sourceComponent || null,
|
|
49
|
-
$resourceComponent: () => this.mainComponent?.resourceComponent || null
|
|
51
|
+
$resourceComponent: () => this.mainComponent?.resourceComponent || null,
|
|
52
|
+
$loadingTracker: () => this.loadingTracker
|
|
50
53
|
}
|
|
51
54
|
},
|
|
52
55
|
|
|
53
56
|
data() {
|
|
57
|
+
// Tracks the requests of all resource components in the view, which is
|
|
58
|
+
// disabled while any of them is pending, see `isLoading`:
|
|
59
|
+
const loadingTracker = new LoadingTracker(this.$loadingTracker())
|
|
54
60
|
return {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
//
|
|
61
|
+
loadingTracker: markRaw(loadingTracker),
|
|
62
|
+
// The loading operation of the deprecated `setLoading()`.
|
|
63
|
+
setLoadingSwitch: markRaw(new LoadingSwitch(loadingTracker)),
|
|
64
|
+
// NOTE: Each view has its own data, as `DitoRouterView` renders each
|
|
65
|
+
// route record with its own component instance.
|
|
59
66
|
data: {}
|
|
60
67
|
}
|
|
61
68
|
},
|
|
@@ -73,6 +80,10 @@ export default DitoComponent.component('DitoView', {
|
|
|
73
80
|
return true
|
|
74
81
|
},
|
|
75
82
|
|
|
83
|
+
isLoading() {
|
|
84
|
+
return this.loadingTracker.isLoading
|
|
85
|
+
},
|
|
86
|
+
|
|
76
87
|
isSingleComponentView() {
|
|
77
88
|
return isSingleComponentView(this.schema)
|
|
78
89
|
},
|
|
@@ -104,20 +115,6 @@ export default DitoComponent.component('DitoView', {
|
|
|
104
115
|
}
|
|
105
116
|
},
|
|
106
117
|
|
|
107
|
-
watch: {
|
|
108
|
-
$route: {
|
|
109
|
-
// https://github.com/vuejs/vue-router/issues/3393#issuecomment-1158470149
|
|
110
|
-
flush: 'post',
|
|
111
|
-
handler(to, from) {
|
|
112
|
-
// See if the route changes completely, and clear the data if it does.
|
|
113
|
-
if (this.isFullRouteChange(to, from)) {
|
|
114
|
-
this.isLoading = false
|
|
115
|
-
this.data = {}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
|
|
121
118
|
created() {
|
|
122
119
|
// Writes defaults and computed values into the view's data and resolves
|
|
123
120
|
// options, see `DataModel`:
|
|
@@ -141,9 +138,25 @@ export default DitoComponent.component('DitoView', {
|
|
|
141
138
|
// Stop the model before the component's own watchers are stopped, as the
|
|
142
139
|
// model's watchers aren't part of the component, see `DataModel`.
|
|
143
140
|
this.ownDataModel.stop()
|
|
141
|
+
// Don't leave the root loading after the view is gone, see the deprecated
|
|
142
|
+
// `setLoading()`.
|
|
143
|
+
this.setLoadingSwitch.set(false)
|
|
144
144
|
},
|
|
145
145
|
|
|
146
146
|
methods: {
|
|
147
|
+
/**
|
|
148
|
+
* @deprecated `isLoading` is tracked by `loadingTracker` now. Use
|
|
149
|
+
* `loadingTracker.begin()` and call the function that it returns, or
|
|
150
|
+
* `loadingTracker.track(callback)` instead.
|
|
151
|
+
*/
|
|
152
|
+
setLoading(isLoading) {
|
|
153
|
+
deprecate(
|
|
154
|
+
'DitoView.setLoading() is deprecated, use ' +
|
|
155
|
+
'`loadingTracker.begin()` or `loadingTracker.track()` instead.'
|
|
156
|
+
)
|
|
157
|
+
this.setLoadingSwitch.set(isLoading)
|
|
158
|
+
},
|
|
159
|
+
|
|
147
160
|
setData(data) {
|
|
148
161
|
this.data = data
|
|
149
162
|
return this.data
|
|
@@ -154,10 +167,6 @@ export default DitoComponent.component('DitoView', {
|
|
|
154
167
|
return this.isSingleComponentView
|
|
155
168
|
? this.path
|
|
156
169
|
: `${this.path}/${path}`
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
setLoading(isLoading) {
|
|
160
|
-
this.isLoading = !!isLoading
|
|
161
170
|
}
|
|
162
171
|
}
|
|
163
172
|
})
|
package/src/index.js
CHANGED