@ditojs/admin 3.2.2 → 3.2.4
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.es.js +1485 -1492
- package/dist/dito-admin.umd.js +5 -5
- package/package.json +2 -2
- package/src/DitoContext.js +10 -10
- package/src/components/DitoDialog.vue +9 -12
- package/src/components/DitoForm.vue +9 -17
- package/src/components/DitoView.vue +9 -12
- package/src/mixins/DitoMixin.js +11 -12
- package/src/mixins/OptionsMixin.js +20 -4
- package/src/utils/DataModel.js +18 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/admin",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dito.js Admin is a schema based admin interface for Dito.js Server, featuring auto-generated views and forms and built with Vue.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/main/packages/admin",
|
|
@@ -131,5 +131,5 @@
|
|
|
131
131
|
"vue-router": "^5.3.1",
|
|
132
132
|
"vue-upload-component": "^3.2.2"
|
|
133
133
|
},
|
|
134
|
-
"gitHead": "
|
|
134
|
+
"gitHead": "32147a4ee71f5bca6c051af04be9c15a16f37567"
|
|
135
135
|
}
|
package/src/DitoContext.js
CHANGED
|
@@ -149,16 +149,16 @@ export default class DitoContext {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
get value() {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
152
|
+
const object = toObject(this)
|
|
153
|
+
// Type components hold their value, also while it's `undefined`, e.g. a
|
|
154
|
+
// computed value that the data model hasn't written yet. Other components
|
|
155
|
+
// don't hold a value, but if they're nested, we can get it from the root,
|
|
156
|
+
// or `undefined` if it's missing there.
|
|
157
|
+
return 'value' in object
|
|
158
|
+
? object.value
|
|
159
|
+
: this.nested
|
|
160
|
+
? getValueAtDataPath(this.rootItem, this.dataPath, () => undefined)
|
|
161
|
+
: undefined
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
get dataPath() {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
</template>
|
|
33
33
|
|
|
34
34
|
<script>
|
|
35
|
+
import { markRaw } from 'vue'
|
|
35
36
|
import { clone } from '@ditojs/utils'
|
|
36
37
|
import DitoComponent from '../DitoComponent.js'
|
|
37
38
|
import DomMixin from '../mixins/DomMixin.js'
|
|
@@ -47,8 +48,7 @@ export default DitoComponent.component('DitoDialog', {
|
|
|
47
48
|
|
|
48
49
|
provide() {
|
|
49
50
|
return {
|
|
50
|
-
$dialogComponent: () => this
|
|
51
|
-
$dataModel: () => this.ownDataModel
|
|
51
|
+
$dialogComponent: () => this
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
|
|
@@ -80,11 +80,6 @@ export default DitoComponent.component('DitoDialog', {
|
|
|
80
80
|
},
|
|
81
81
|
|
|
82
82
|
computed: {
|
|
83
|
-
// @override DitoMixin.dataModel
|
|
84
|
-
dataModel() {
|
|
85
|
-
return this.ownDataModel
|
|
86
|
-
},
|
|
87
|
-
|
|
88
83
|
dialogComponent() {
|
|
89
84
|
return this
|
|
90
85
|
},
|
|
@@ -136,11 +131,13 @@ export default DitoComponent.component('DitoDialog', {
|
|
|
136
131
|
created() {
|
|
137
132
|
// Writes defaults and computed values into the dialog's data and resolves
|
|
138
133
|
// options, see `DataModel`:
|
|
139
|
-
this.ownDataModel =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
134
|
+
this.ownDataModel = markRaw(
|
|
135
|
+
new DataModel({
|
|
136
|
+
component: this,
|
|
137
|
+
getSchema: () => this.schema,
|
|
138
|
+
getData: () => this.dialogData
|
|
139
|
+
})
|
|
140
|
+
)
|
|
144
141
|
},
|
|
145
142
|
|
|
146
143
|
mounted() {
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
</template>
|
|
52
52
|
|
|
53
53
|
<script>
|
|
54
|
+
import { markRaw } from 'vue'
|
|
54
55
|
import { clone, capitalize, parseDataPath, assignDeeply } from '@ditojs/utils'
|
|
55
56
|
import DitoComponent from '../DitoComponent.js'
|
|
56
57
|
import RouteMixin from '../mixins/RouteMixin.js'
|
|
@@ -66,12 +67,6 @@ import { DataModel } from '../utils/DataModel.js'
|
|
|
66
67
|
export default DitoComponent.component('DitoForm', {
|
|
67
68
|
mixins: [RouteMixin, ResourceMixin],
|
|
68
69
|
|
|
69
|
-
provide() {
|
|
70
|
-
return {
|
|
71
|
-
$dataModel: () => this.ownDataModel
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
|
|
75
70
|
data() {
|
|
76
71
|
return {
|
|
77
72
|
createdData: null,
|
|
@@ -80,11 +75,6 @@ export default DitoComponent.component('DitoForm', {
|
|
|
80
75
|
},
|
|
81
76
|
|
|
82
77
|
computed: {
|
|
83
|
-
// @override DitoMixin.dataModel
|
|
84
|
-
dataModel() {
|
|
85
|
-
return this.ownDataModel
|
|
86
|
-
},
|
|
87
|
-
|
|
88
78
|
isForm() {
|
|
89
79
|
return true
|
|
90
80
|
},
|
|
@@ -366,12 +356,14 @@ export default DitoComponent.component('DitoForm', {
|
|
|
366
356
|
created() {
|
|
367
357
|
// Writes defaults and computed values into the form's data, resolves
|
|
368
358
|
// options and tracks whether the data is dirty, see `DataModel`:
|
|
369
|
-
this.ownDataModel =
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
359
|
+
this.ownDataModel = markRaw(
|
|
360
|
+
new DataModel({
|
|
361
|
+
component: this,
|
|
362
|
+
getSchema: () => this.schema,
|
|
363
|
+
getData: () => this.data,
|
|
364
|
+
getSourceSchema: () => this.sourceSchema
|
|
365
|
+
})
|
|
366
|
+
)
|
|
375
367
|
},
|
|
376
368
|
|
|
377
369
|
beforeUnmount() {
|
|
@@ -28,6 +28,7 @@ template(
|
|
|
28
28
|
</template>
|
|
29
29
|
|
|
30
30
|
<script>
|
|
31
|
+
import { markRaw } from 'vue'
|
|
31
32
|
import DitoComponent from '../DitoComponent.js'
|
|
32
33
|
import RouteMixin from '../mixins/RouteMixin.js'
|
|
33
34
|
import {
|
|
@@ -45,8 +46,7 @@ export default DitoComponent.component('DitoView', {
|
|
|
45
46
|
// Redirect $sourceComponent and $resourceComponent to the main component:
|
|
46
47
|
return {
|
|
47
48
|
$sourceComponent: () => this.mainComponent?.sourceComponent || null,
|
|
48
|
-
$resourceComponent: () => this.mainComponent?.resourceComponent || null
|
|
49
|
-
$dataModel: () => this.ownDataModel
|
|
49
|
+
$resourceComponent: () => this.mainComponent?.resourceComponent || null
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
|
|
@@ -61,11 +61,6 @@ export default DitoComponent.component('DitoView', {
|
|
|
61
61
|
},
|
|
62
62
|
|
|
63
63
|
computed: {
|
|
64
|
-
// @override DitoMixin.dataModel
|
|
65
|
-
dataModel() {
|
|
66
|
-
return this.ownDataModel
|
|
67
|
-
},
|
|
68
|
-
|
|
69
64
|
schema() {
|
|
70
65
|
return this.meta.schema ?? {}
|
|
71
66
|
},
|
|
@@ -126,11 +121,13 @@ export default DitoComponent.component('DitoView', {
|
|
|
126
121
|
created() {
|
|
127
122
|
// Writes defaults and computed values into the view's data and resolves
|
|
128
123
|
// options, see `DataModel`:
|
|
129
|
-
this.ownDataModel =
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
this.ownDataModel = markRaw(
|
|
125
|
+
new DataModel({
|
|
126
|
+
component: this,
|
|
127
|
+
getSchema: () => this.viewSchema,
|
|
128
|
+
getData: () => this.data
|
|
129
|
+
})
|
|
130
|
+
)
|
|
134
131
|
},
|
|
135
132
|
|
|
136
133
|
mounted() {
|
package/src/mixins/DitoMixin.js
CHANGED
|
@@ -39,29 +39,28 @@ export default {
|
|
|
39
39
|
|
|
40
40
|
provide() {
|
|
41
41
|
const self = () => this
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
: {
|
|
48
|
-
$parentComponent: self
|
|
49
|
-
}
|
|
42
|
+
return {
|
|
43
|
+
$parentComponent: self,
|
|
44
|
+
...(this.providesData && { $dataComponent: self }),
|
|
45
|
+
$dataModel: () => this.dataModel
|
|
46
|
+
}
|
|
50
47
|
},
|
|
51
48
|
|
|
52
49
|
data() {
|
|
53
50
|
return {
|
|
54
51
|
appState,
|
|
55
52
|
isMounted: false,
|
|
56
|
-
overrides: null // See accessor.js
|
|
53
|
+
overrides: null, // See accessor.js
|
|
54
|
+
// The data model that the component owns, see `dataModel`.
|
|
55
|
+
ownDataModel: null
|
|
57
56
|
}
|
|
58
57
|
},
|
|
59
58
|
|
|
60
59
|
computed: {
|
|
61
|
-
// The data model
|
|
62
|
-
//
|
|
60
|
+
// The data model of the data: the component's own, e.g. of a form, view
|
|
61
|
+
// or dialog, or else the one of its closest ancestor that owns one.
|
|
63
62
|
dataModel() {
|
|
64
|
-
return this.$dataModel()
|
|
63
|
+
return this.ownDataModel ?? this.$dataModel()
|
|
65
64
|
},
|
|
66
65
|
|
|
67
66
|
providesData() {
|
|
@@ -68,10 +68,12 @@ export default {
|
|
|
68
68
|
// Whether the value needs to be replaced with the selected value, once the
|
|
69
69
|
// options are available: if the value is forced to `null` because its
|
|
70
70
|
// option disappeared, or if the value is a reference, so that it'll hold
|
|
71
|
-
// actual data, not just a reference id. See `watch`.
|
|
71
|
+
// actual data, not just a reference id. See `watch`. Filtered options, e.g.
|
|
72
|
+
// the results of a search, don't tell whether the value's option exists.
|
|
72
73
|
shouldReplaceValueWithSelectedValue() {
|
|
73
74
|
return (
|
|
74
|
-
this.hasOptions &&
|
|
75
|
+
this.hasOptions &&
|
|
76
|
+
!this.areOptionsFiltered && (
|
|
75
77
|
this.selectedValue === null && this.value !== null ||
|
|
76
78
|
isReference(this.value)
|
|
77
79
|
)
|
|
@@ -114,6 +116,12 @@ export default {
|
|
|
114
116
|
return this.options
|
|
115
117
|
},
|
|
116
118
|
|
|
119
|
+
// Whether the active options are a filtered subset of the options, e.g.
|
|
120
|
+
// the results of a search, see `activeOptions`.
|
|
121
|
+
areOptionsFiltered() {
|
|
122
|
+
return this.activeOptions !== this.options
|
|
123
|
+
},
|
|
124
|
+
|
|
117
125
|
hasOptions() {
|
|
118
126
|
return this.activeOptions.length > 0
|
|
119
127
|
},
|
|
@@ -304,8 +312,16 @@ export default {
|
|
|
304
312
|
}
|
|
305
313
|
}
|
|
306
314
|
|
|
307
|
-
|
|
308
|
-
|
|
315
|
+
const { optionValue, groupBy } = this
|
|
316
|
+
return optionValue
|
|
317
|
+
? (findOption(this.activeOptions, value, groupBy) ??
|
|
318
|
+
// Options filtered out of the active options, e.g. by a search, are
|
|
319
|
+
// still options of the value.
|
|
320
|
+
(
|
|
321
|
+
this.areOptionsFiltered
|
|
322
|
+
? findOption(this.options, value, groupBy)
|
|
323
|
+
: undefined
|
|
324
|
+
))
|
|
309
325
|
: value
|
|
310
326
|
},
|
|
311
327
|
|
package/src/utils/DataModel.js
CHANGED
|
@@ -92,7 +92,7 @@ export class DataModel {
|
|
|
92
92
|
watch(
|
|
93
93
|
[getSchema, getData],
|
|
94
94
|
([schema, data]) => this.initializeData(schema, data),
|
|
95
|
-
|
|
95
|
+
modelSetupWatchOptions
|
|
96
96
|
)
|
|
97
97
|
// The entries are read through a computed property, so that the watchers
|
|
98
98
|
// of the entries can check synchronously whether their entry is still
|
|
@@ -101,19 +101,18 @@ export class DataModel {
|
|
|
101
101
|
watch(
|
|
102
102
|
() => this.dataEntries.value.computedValueEntries,
|
|
103
103
|
entries => this.updateComputedValueRecords(entries),
|
|
104
|
-
|
|
104
|
+
modelSetupWatchOptions
|
|
105
105
|
)
|
|
106
106
|
// Values that go missing after the data was set up, e.g. in items that
|
|
107
107
|
// code adds, get their defaults too:
|
|
108
108
|
watch(
|
|
109
109
|
() => this.dataEntries.value.entriesWithMissingValues,
|
|
110
110
|
entries => this.setDefaultValues(entries),
|
|
111
|
-
|
|
111
|
+
modelSetupWatchOptions
|
|
112
112
|
)
|
|
113
113
|
if (getSourceSchema) {
|
|
114
114
|
// Data that is set up, e.g. loaded, saved or applied, isn't dirty:
|
|
115
|
-
// After the data is set up,
|
|
116
|
-
// they're created, see `modelWatchOptions`.
|
|
115
|
+
// After the data is set up, see `modelSetupWatchOptions`.
|
|
117
116
|
watch(
|
|
118
117
|
getData,
|
|
119
118
|
() => this.takeProcessedDataSnapshot().catch(console.error),
|
|
@@ -641,13 +640,20 @@ function takeOverChangedValues(target, before, after) {
|
|
|
641
640
|
|
|
642
641
|
const notFound = Symbol('notFound')
|
|
643
642
|
|
|
644
|
-
// The watchers of the model
|
|
645
|
-
//
|
|
646
|
-
//
|
|
647
|
-
//
|
|
648
|
-
//
|
|
649
|
-
|
|
650
|
-
|
|
643
|
+
// The watchers of the model that set up the data and the scopes of its
|
|
644
|
+
// components run before the components render, so that components of new
|
|
645
|
+
// data, e.g. loaded data or added items, receive their defaults and computed
|
|
646
|
+
// values from the start: The scopes compute their values right away, see
|
|
647
|
+
// `createComputedValueScope()`.
|
|
648
|
+
const modelSetupWatchOptions = { immediate: true, flush: 'pre' }
|
|
649
|
+
|
|
650
|
+
// The other watchers of the model run after the post-flush hooks, so that they
|
|
651
|
+
// write the values that they derive from the data after the components that
|
|
652
|
+
// display them are mounted. Otherwise, the `mounted` hooks of `v-model`
|
|
653
|
+
// directives would restore the values that inputs had when they were mounted,
|
|
654
|
+
// see https://github.com/vuejs/core/issues/15774. Vue processes the updates
|
|
655
|
+
// that the written values cause in the same flush, before the browser renders
|
|
656
|
+
// them.
|
|
651
657
|
const modelWatchOptions = { immediate: true, flush: 'post' }
|
|
652
658
|
|
|
653
659
|
// Aborts `compute()` when it reads options that aren't loaded, see
|