@ditojs/admin 2.2.12 → 2.2.13
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 +2011 -1961
- package/dist/dito-admin.umd.js +4 -4
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/components/DitoButtons.vue +1 -1
- package/src/components/DitoContainer.vue +9 -7
- package/src/components/DitoCreateButton.vue +2 -2
- package/src/components/DitoDialog.vue +1 -0
- package/src/components/DitoForm.vue +10 -0
- package/src/components/DitoPane.vue +1 -1
- package/src/components/DitoPanel.vue +1 -0
- package/src/components/DitoPanels.vue +1 -1
- package/src/components/DitoRoot.vue +6 -1
- package/src/components/DitoSchema.vue +28 -11
- package/src/components/DitoSidebar.vue +1 -1
- package/src/components/DitoTableHead.vue +1 -1
- package/src/components/DitoTabs.vue +1 -1
- package/src/components/DitoView.vue +6 -3
- package/src/mixins/DitoMixin.js +11 -46
- package/src/mixins/SourceMixin.js +1 -1
- package/src/types/DitoTypeList.vue +1 -1
- package/src/utils/schema.js +80 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/admin",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.13",
|
|
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/master/packages/admin",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"vite": "^4.3.1"
|
|
83
83
|
},
|
|
84
84
|
"types": "types",
|
|
85
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "cb2613f69dcb1837db22d95c736ea2d414326f5c",
|
|
86
86
|
"scripts": {
|
|
87
87
|
"build": "vite build",
|
|
88
88
|
"watch": "yarn build --mode 'development' --watch",
|
|
@@ -226,13 +226,15 @@ export default DitoComponent.component('DitoContainer', {
|
|
|
226
226
|
--justify: flex-end;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
229
|
+
&:not(#{$self}--only-in-row) {
|
|
230
|
+
// Now only apply alignment if there are neighbouring components no the
|
|
231
|
+
// same row that also align.
|
|
232
|
+
// Look ahead:
|
|
233
|
+
&:not(#{$self}--last-in-row) + #{&}:not(#{$self}--first-in-row),
|
|
234
|
+
// Look behind:
|
|
235
|
+
&:not(#{$self}--last-in-row):has(+ #{&}:not(#{$self}--first-in-row)) {
|
|
236
|
+
justify-content: var(--justify);
|
|
237
|
+
}
|
|
236
238
|
}
|
|
237
239
|
}
|
|
238
240
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
v-for="(form, type) in forms"
|
|
15
15
|
)
|
|
16
16
|
a(
|
|
17
|
-
v-if="
|
|
17
|
+
v-if="shouldRenderSchema(form)"
|
|
18
18
|
v-show="shouldShow(form)"
|
|
19
19
|
:class="getFormClass(form, type)"
|
|
20
20
|
@mousedown.stop="onPulldownMouseDown(type)"
|
|
@@ -61,7 +61,7 @@ export default DitoComponent.component('DitoCreateButton', {
|
|
|
61
61
|
|
|
62
62
|
methods: {
|
|
63
63
|
createItem(form, type = null) {
|
|
64
|
-
if (this.
|
|
64
|
+
if (this.shouldRenderSchema(form) && !this.shouldDisable(form)) {
|
|
65
65
|
if (this.isInlined) {
|
|
66
66
|
this.sourceComponent.createItem(form, type)
|
|
67
67
|
} else {
|
|
@@ -283,6 +283,16 @@ export default DitoComponent.component('DitoForm', {
|
|
|
283
283
|
},
|
|
284
284
|
|
|
285
285
|
watch: {
|
|
286
|
+
$route(to, from) {
|
|
287
|
+
// Reload form data when navigating to a different entity in same form.
|
|
288
|
+
if (this.providesData) {
|
|
289
|
+
const { param } = this.meta
|
|
290
|
+
if (param && to.params[param] !== from.params[param]) {
|
|
291
|
+
this.loadData(true)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
|
|
286
296
|
sourceData: 'clearClonedData',
|
|
287
297
|
// Needed for the 'create' redirect in `inheritedData()` to work:
|
|
288
298
|
create: 'setupData'
|
|
@@ -172,7 +172,12 @@ export default DitoComponent.component('DitoRoot', {
|
|
|
172
172
|
async (resolve, reject) => {
|
|
173
173
|
// Process components to resolve async schemas.
|
|
174
174
|
const routes = []
|
|
175
|
-
await processSchemaComponents(
|
|
175
|
+
await processSchemaComponents(
|
|
176
|
+
this.api,
|
|
177
|
+
{ type: 'dialog', components },
|
|
178
|
+
routes,
|
|
179
|
+
0
|
|
180
|
+
)
|
|
176
181
|
if (routes.length > 0) {
|
|
177
182
|
throw new Error(
|
|
178
183
|
'Dialogs do not support components that produce routes'
|
|
@@ -155,6 +155,7 @@ export default DitoComponent.component('DitoSchema', {
|
|
|
155
155
|
? data(this.context)
|
|
156
156
|
: data
|
|
157
157
|
),
|
|
158
|
+
currentTab: null,
|
|
158
159
|
componentsRegistry: {},
|
|
159
160
|
panesRegistry: {},
|
|
160
161
|
panelsRegistry: {}
|
|
@@ -187,17 +188,7 @@ export default DitoComponent.component('DitoSchema', {
|
|
|
187
188
|
},
|
|
188
189
|
|
|
189
190
|
selectedTab() {
|
|
190
|
-
|
|
191
|
-
const tab =
|
|
192
|
-
currentTab && this.shouldRender(this.tabs[currentTab])
|
|
193
|
-
? currentTab
|
|
194
|
-
: this.defaultTab?.name || null
|
|
195
|
-
if (tab !== currentTab) {
|
|
196
|
-
// TODO: Move this watcher!
|
|
197
|
-
// Any tab change needs to be reflected in the router also.
|
|
198
|
-
this.$router.replace({ hash: `#${tab}` })
|
|
199
|
-
}
|
|
200
|
-
return tab
|
|
191
|
+
return this.currentTab || this.defaultTab?.name || null
|
|
201
192
|
},
|
|
202
193
|
|
|
203
194
|
defaultTab() {
|
|
@@ -336,6 +327,32 @@ export default DitoComponent.component('DitoSchema', {
|
|
|
336
327
|
}
|
|
337
328
|
},
|
|
338
329
|
|
|
330
|
+
watch: {
|
|
331
|
+
'$route.hash': {
|
|
332
|
+
immediate: true,
|
|
333
|
+
handler(hash) {
|
|
334
|
+
if (this.hasTabs) {
|
|
335
|
+
this.currentTab = hash?.slice(1) || null
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
'selectedTab'(selectedTab) {
|
|
341
|
+
if (this.hasTabs) {
|
|
342
|
+
let tab = null
|
|
343
|
+
if (selectedTab !== this.currentTab) {
|
|
344
|
+
// Any tab change needs to be reflected in the router also.
|
|
345
|
+
tab = selectedTab
|
|
346
|
+
} else if (!this.shouldRenderSchema(this.tabs[selectedTab])) {
|
|
347
|
+
tab = this.defaultTab?.name
|
|
348
|
+
}
|
|
349
|
+
if (tab) {
|
|
350
|
+
this.$router.replace({ hash: `#${tab}` })
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
|
|
339
356
|
created() {
|
|
340
357
|
this._register(true)
|
|
341
358
|
this.setupSchemaFields()
|
|
@@ -11,7 +11,7 @@ template(
|
|
|
11
11
|
:key="name"
|
|
12
12
|
)
|
|
13
13
|
.dito-view.dito-scroll-parent(
|
|
14
|
-
v-else-if="
|
|
14
|
+
v-else-if="shouldRenderSchema(viewSchema)"
|
|
15
15
|
:data-resource="sourceSchema.path"
|
|
16
16
|
)
|
|
17
17
|
DitoSchema(
|
|
@@ -28,7 +28,10 @@ template(
|
|
|
28
28
|
<script>
|
|
29
29
|
import DitoComponent from '../DitoComponent.js'
|
|
30
30
|
import RouteMixin from '../mixins/RouteMixin.js'
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
isSingleComponentView,
|
|
33
|
+
someNestedSchemaComponent
|
|
34
|
+
} from '../utils/schema.js'
|
|
32
35
|
import { hasResource } from '../utils/resource.js'
|
|
33
36
|
|
|
34
37
|
// @vue/component
|
|
@@ -93,7 +96,7 @@ export default DitoComponent.component('DitoView', {
|
|
|
93
96
|
},
|
|
94
97
|
|
|
95
98
|
providesData() {
|
|
96
|
-
return
|
|
99
|
+
return someNestedSchemaComponent(this.viewSchema, hasResource)
|
|
97
100
|
}
|
|
98
101
|
},
|
|
99
102
|
|
package/src/mixins/DitoMixin.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isObject,
|
|
3
|
-
isArray,
|
|
4
3
|
isString,
|
|
5
4
|
isFunction,
|
|
6
|
-
asArray,
|
|
7
5
|
equals,
|
|
8
|
-
getValueAtDataPath,
|
|
9
6
|
labelize,
|
|
10
7
|
hyphenate,
|
|
11
8
|
format
|
|
@@ -13,7 +10,7 @@ import {
|
|
|
13
10
|
import appState from '../appState.js'
|
|
14
11
|
import DitoContext from '../DitoContext.js'
|
|
15
12
|
import EmitterMixin from './EmitterMixin.js'
|
|
16
|
-
import {
|
|
13
|
+
import { getSchemaValue, shouldRenderSchema } from '../utils/schema.js'
|
|
17
14
|
import { getResource, getMemberResource } from '../utils/resource.js'
|
|
18
15
|
import { computed, reactive } from 'vue'
|
|
19
16
|
|
|
@@ -201,43 +198,15 @@ export default {
|
|
|
201
198
|
|
|
202
199
|
getSchemaValue(
|
|
203
200
|
keyOrDataPath,
|
|
204
|
-
{ type,
|
|
201
|
+
{ type, schema = this.schema, callback = true, default: def } = {}
|
|
205
202
|
) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
: undefined
|
|
214
|
-
|
|
215
|
-
if (value === undefined && def !== undefined) {
|
|
216
|
-
if (callback && isFunction(def) && !isMatchingType(types, def)) {
|
|
217
|
-
// Support `default()` functions for any type except `Function`:
|
|
218
|
-
def = def.call(this)
|
|
219
|
-
}
|
|
220
|
-
return def
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (isMatchingType(types, value)) {
|
|
224
|
-
return value
|
|
225
|
-
}
|
|
226
|
-
// Any schema value handled through `getSchemaValue()` can provide
|
|
227
|
-
// a function that's resolved when the value is evaluated:
|
|
228
|
-
if (callback && isFunction(value)) {
|
|
229
|
-
value = value(this.context)
|
|
230
|
-
}
|
|
231
|
-
// Now finally see if we can convert to the expect types.
|
|
232
|
-
if (types && value != null && !isMatchingType(types, value)) {
|
|
233
|
-
for (const type of types) {
|
|
234
|
-
const converted = convertType(type, value)
|
|
235
|
-
if (converted !== value) {
|
|
236
|
-
return converted
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return value
|
|
203
|
+
return getSchemaValue(keyOrDataPath, {
|
|
204
|
+
type,
|
|
205
|
+
schema,
|
|
206
|
+
callback,
|
|
207
|
+
context: this.context,
|
|
208
|
+
default: isFunction(def) ? () => def.call(this) : def
|
|
209
|
+
})
|
|
241
210
|
},
|
|
242
211
|
|
|
243
212
|
getLabel(schema, name) {
|
|
@@ -263,12 +232,8 @@ export default {
|
|
|
263
232
|
}
|
|
264
233
|
},
|
|
265
234
|
|
|
266
|
-
|
|
267
|
-
return
|
|
268
|
-
type: Boolean,
|
|
269
|
-
default: true,
|
|
270
|
-
schema
|
|
271
|
-
})
|
|
235
|
+
shouldRenderSchema(schema = null) {
|
|
236
|
+
return shouldRenderSchema(schema, this.context)
|
|
272
237
|
},
|
|
273
238
|
|
|
274
239
|
shouldShow(schema = null) {
|
|
@@ -314,7 +314,7 @@ export default {
|
|
|
314
314
|
|
|
315
315
|
watch: {
|
|
316
316
|
$route(to, from) {
|
|
317
|
-
if (from.path === to.path && from.hash === to.hash) {
|
|
317
|
+
if (this.providesData && from.path === to.path && from.hash === to.hash) {
|
|
318
318
|
// Paths and hashes remain the same, so only queries have changed.
|
|
319
319
|
// Update filter and reload data without clearing.
|
|
320
320
|
this.query = to.query
|
package/src/utils/schema.js
CHANGED
|
@@ -4,6 +4,7 @@ import TypeMixin from '../mixins/TypeMixin.js'
|
|
|
4
4
|
import { getUid } from './uid.js'
|
|
5
5
|
import { SchemaGraph } from './SchemaGraph.js'
|
|
6
6
|
import { appendDataPath, isTemporaryId } from './data.js'
|
|
7
|
+
import { isMatchingType, convertType } from './type.js'
|
|
7
8
|
import {
|
|
8
9
|
isObject,
|
|
9
10
|
isString,
|
|
@@ -11,10 +12,12 @@ import {
|
|
|
11
12
|
isFunction,
|
|
12
13
|
isPromise,
|
|
13
14
|
isModule,
|
|
15
|
+
asArray,
|
|
14
16
|
clone,
|
|
15
17
|
merge,
|
|
16
18
|
camelize,
|
|
17
|
-
mapConcurrently
|
|
19
|
+
mapConcurrently,
|
|
20
|
+
getValueAtDataPath
|
|
18
21
|
} from '@ditojs/utils'
|
|
19
22
|
import { markRaw } from 'vue'
|
|
20
23
|
|
|
@@ -42,7 +45,7 @@ export function iterateSchemaComponents(schemas, callback) {
|
|
|
42
45
|
if (res !== undefined) {
|
|
43
46
|
return res
|
|
44
47
|
}
|
|
45
|
-
} else {
|
|
48
|
+
} else if (isSchema(schema)) {
|
|
46
49
|
for (const [name, component] of Object.entries(schema.components || {})) {
|
|
47
50
|
const res = callback(component, name, 1)
|
|
48
51
|
if (res !== undefined) {
|
|
@@ -59,33 +62,37 @@ export function iterateNestedSchemaComponents(schema, callback) {
|
|
|
59
62
|
: undefined
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
export function
|
|
65
|
+
export function findNestedSchemaComponent(schema, callback) {
|
|
63
66
|
return (
|
|
64
67
|
iterateNestedSchemaComponents(
|
|
65
68
|
schema,
|
|
66
69
|
component => (callback(component) ? component : undefined)
|
|
67
|
-
)
|
|
70
|
+
) ?? null
|
|
68
71
|
)
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
export function
|
|
74
|
+
export function someNestedSchemaComponent(schema, callback) {
|
|
72
75
|
return (
|
|
73
76
|
iterateNestedSchemaComponents(
|
|
74
77
|
schema,
|
|
75
78
|
component => (callback(component) ? true : undefined)
|
|
76
|
-
)
|
|
79
|
+
) ?? false
|
|
77
80
|
)
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
export function
|
|
83
|
+
export function everyNestedSchemaComponent(schema, callback) {
|
|
81
84
|
return (
|
|
82
85
|
iterateNestedSchemaComponents(
|
|
83
86
|
schema,
|
|
84
87
|
component => (callback(component) ? undefined : false)
|
|
85
|
-
)
|
|
88
|
+
) ?? true
|
|
86
89
|
)
|
|
87
90
|
}
|
|
88
91
|
|
|
92
|
+
export function hasNestedSchemaComponents(schema) {
|
|
93
|
+
return someNestedSchemaComponent(schema, () => true) ?? false
|
|
94
|
+
}
|
|
95
|
+
|
|
89
96
|
export function isSchema(schema) {
|
|
90
97
|
return isObject(schema) && isString(schema.type)
|
|
91
98
|
}
|
|
@@ -107,7 +114,7 @@ export function isPanel(schema) {
|
|
|
107
114
|
}
|
|
108
115
|
|
|
109
116
|
export function getSchemaIdentifier(schema) {
|
|
110
|
-
return
|
|
117
|
+
return JSON.stringify(schema)
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
export async function resolveSchema(schema, unwrapModule = false) {
|
|
@@ -309,11 +316,12 @@ export async function processForms(api, schema, level) {
|
|
|
309
316
|
)
|
|
310
317
|
} else if (form) {
|
|
311
318
|
form = schema.form = await processForm(api, form)
|
|
312
|
-
} else if (components) {
|
|
319
|
+
} else if (isObject(components)) {
|
|
313
320
|
// NOTE: Processing forms in computed components is not supported, since it
|
|
314
321
|
// only can be computed in conjunction with actual data.
|
|
315
|
-
|
|
316
|
-
form
|
|
322
|
+
form = {
|
|
323
|
+
type: 'form',
|
|
324
|
+
components
|
|
317
325
|
}
|
|
318
326
|
}
|
|
319
327
|
|
|
@@ -398,7 +406,7 @@ export function getViewFormSchema(schema, context) {
|
|
|
398
406
|
return viewSchema
|
|
399
407
|
? // NOTE: Views can have tabs, in which case the view component is nested
|
|
400
408
|
// in one of the tabs, go find it.
|
|
401
|
-
|
|
409
|
+
findNestedSchemaComponent(viewSchema, hasFormSchema) || null
|
|
402
410
|
: null
|
|
403
411
|
}
|
|
404
412
|
|
|
@@ -478,6 +486,63 @@ export function keepAligned(schema) {
|
|
|
478
486
|
return !!getTypeOptions(schema)?.keepAligned
|
|
479
487
|
}
|
|
480
488
|
|
|
489
|
+
export function getSchemaValue(
|
|
490
|
+
keyOrDataPath,
|
|
491
|
+
{ type, schema, callback = true, default: def, context } = {}
|
|
492
|
+
) {
|
|
493
|
+
const types = type && asArray(type)
|
|
494
|
+
// For performance reasons, data-paths in `keyOrDataPath` can only be
|
|
495
|
+
// provided in in array format here:
|
|
496
|
+
let value = schema
|
|
497
|
+
? isArray(keyOrDataPath)
|
|
498
|
+
? getValueAtDataPath(schema, keyOrDataPath, () => undefined)
|
|
499
|
+
: schema[keyOrDataPath]
|
|
500
|
+
: undefined
|
|
501
|
+
|
|
502
|
+
if (value === undefined && def !== undefined) {
|
|
503
|
+
if (callback && isFunction(def) && !isMatchingType(types, def)) {
|
|
504
|
+
// Support `default()` functions for any type except `Function`:
|
|
505
|
+
def = def(context)
|
|
506
|
+
}
|
|
507
|
+
return def
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (isMatchingType(types, value)) {
|
|
511
|
+
return value
|
|
512
|
+
}
|
|
513
|
+
// Any schema value handled through `getSchemaValue()` can provide
|
|
514
|
+
// a function that's resolved when the value is evaluated:
|
|
515
|
+
if (callback && isFunction(value)) {
|
|
516
|
+
value = value(context)
|
|
517
|
+
}
|
|
518
|
+
// Now finally see if we can convert to the expect types.
|
|
519
|
+
if (types && value != null && !isMatchingType(types, value)) {
|
|
520
|
+
for (const type of types) {
|
|
521
|
+
const converted = convertType(type, value)
|
|
522
|
+
if (converted !== value) {
|
|
523
|
+
return converted
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return value
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export function shouldRenderSchema(schema, context) {
|
|
531
|
+
return (
|
|
532
|
+
getSchemaValue('if', {
|
|
533
|
+
type: Boolean,
|
|
534
|
+
schema,
|
|
535
|
+
context,
|
|
536
|
+
default: true
|
|
537
|
+
}) && (
|
|
538
|
+
!hasNestedSchemaComponents(schema) ||
|
|
539
|
+
someNestedSchemaComponent(schema, component =>
|
|
540
|
+
shouldRenderSchema(component, context)
|
|
541
|
+
)
|
|
542
|
+
)
|
|
543
|
+
)
|
|
544
|
+
}
|
|
545
|
+
|
|
481
546
|
export function getDefaultValue(schema) {
|
|
482
547
|
// Support default values both on schema and on component level.
|
|
483
548
|
// NOTE: At the time of creation, components may not be instantiated, (e.g. if
|
|
@@ -542,8 +607,8 @@ export function computeValue(schema, data, name, dataPath, {
|
|
|
542
607
|
})
|
|
543
608
|
)
|
|
544
609
|
if (value !== undefined) {
|
|
545
|
-
// Access `data[name]` directly
|
|
546
|
-
//
|
|
610
|
+
// Access `data[name]` directly to update the value without calling
|
|
611
|
+
// parse():
|
|
547
612
|
data[name] = value
|
|
548
613
|
}
|
|
549
614
|
}
|