@ditojs/admin 2.27.0 → 2.27.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/admin",
3
- "version": "2.27.0",
3
+ "version": "2.27.2",
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",
@@ -83,7 +83,7 @@
83
83
  "vite": "^5.2.8"
84
84
  },
85
85
  "types": "types",
86
- "gitHead": "e80d56a1887faa60f61f4c642502687d50ead333",
86
+ "gitHead": "d15a80e1ecdf916fd087ad7c5b21eee0b2ee9daf",
87
87
  "scripts": {
88
88
  "build": "vite build",
89
89
  "watch": "yarn build --mode 'development' --watch",
@@ -140,6 +140,10 @@ export default class DitoContext {
140
140
  return get(this, 'processedData', null)
141
141
  }
142
142
 
143
+ get processedRootItem() {
144
+ return get(this, 'processedRootData', null)
145
+ }
146
+
143
147
  get clipboardItem() {
144
148
  return get(this, 'clipboardData', null)
145
149
  }
@@ -255,6 +255,12 @@ export default DitoComponent.component('DitoContainer', {
255
255
  // percentages in flex-basis to work.
256
256
  padding: $form-spacing-half;
257
257
 
258
+ > .dito-label:only-child {
259
+ // Used e.g. when sources hide themselves due to maxDepth, but the label
260
+ // is rendered above it.
261
+ display: none;
262
+ }
263
+
258
264
  .dito-pane > & {
259
265
  .dito-page--width-80 & {
260
266
  flex-grow: 1;
@@ -31,7 +31,7 @@ export default {
31
31
  routeLevel() {
32
32
  let level = 0
33
33
  let routeComponent = this
34
- while ((routeComponent = routeComponent.parentComponent.routeComponent)) {
34
+ while ((routeComponent = routeComponent.parentRouteComponent)) {
35
35
  level++
36
36
  }
37
37
  return level
@@ -68,10 +68,17 @@ export default {
68
68
  return !!this.value
69
69
  },
70
70
 
71
+ shouldRender() {
72
+ return this.sourceDepth < this.maxDepth
73
+ },
74
+
71
75
  isReady() {
72
76
  // Lists that have no data and no associated resource should still render,
73
77
  // as they may be getting their data elsewhere, e.g. `compute()`.
74
- return this.hasData || !this.providesData
78
+ return (
79
+ this.shouldRender &&
80
+ (this.hasData || !this.providesData)
81
+ )
75
82
  },
76
83
 
77
84
  isInView() {
@@ -147,6 +154,15 @@ export default {
147
154
  return this.schema
148
155
  },
149
156
 
157
+ sourceDepth() {
158
+ return this.$route.matched.reduce(
159
+ (depth, record) => (
160
+ depth + (record.meta.schema === this.sourceSchema ? 1 : 0)
161
+ ),
162
+ 0
163
+ )
164
+ },
165
+
150
166
  path() {
151
167
  // This is used in TypeList for DitoFormChooser.
152
168
  return this.routeComponent.getChildPath(this.schema.path)
@@ -313,6 +329,11 @@ export default {
313
329
  collapsed: getSchemaAccessor('collapsed', {
314
330
  type: Boolean,
315
331
  default: null
332
+ }),
333
+
334
+ maxDepth: getSchemaAccessor('maxDepth', {
335
+ type: Number,
336
+ default: 1
316
337
  })
317
338
  },
318
339
 
@@ -77,16 +77,11 @@ export default {
77
77
  },
78
78
 
79
79
  processedData() {
80
- // We can only get the processed data through the schemaComponent, but
81
- // that's not necessarily the item represented by this component.
82
- // Solution: Find the relative path and the processed sub-item from there:
83
- const { schemaComponent } = this
84
- return getItem(
85
- schemaComponent.processedData,
86
- // Get the dataPath relative to the schemaComponent's data:
87
- this.dataPath.slice(schemaComponent.dataPath.length),
88
- this.nested
89
- )
80
+ return getProcessedParentData(this, this.dataPath, this.nested)
81
+ },
82
+
83
+ processedRootData() {
84
+ return getProcessedParentData(this, '', false)
90
85
  },
91
86
 
92
87
  // The following computed properties are similar to `DitoContext`
@@ -107,6 +102,10 @@ export default {
107
102
  return this.processedData
108
103
  },
109
104
 
105
+ processedRootItem() {
106
+ return this.processedRootData
107
+ },
108
+
110
109
  labelNode() {
111
110
  const node = this.isMounted ? this.$el.previousElementSibling : null
112
111
  return node?.matches('.dito-label') ? node : null
@@ -327,3 +326,20 @@ export default {
327
326
  }
328
327
  }
329
328
  }
329
+
330
+ function getProcessedParentData(component, dataPath, nested = false) {
331
+ // We can only get the processed data through the schemaComponent, but
332
+ // that's not necessarily the item represented by this component.
333
+ // Solution: Find the relative path and the processed sub-item from there:
334
+ let { schemaComponent } = component
335
+ // Find the schema component that contains the desired data-path:
336
+ while (schemaComponent.dataPath.length > dataPath.length) {
337
+ schemaComponent = schemaComponent.parentSchemaComponent
338
+ }
339
+ return getItem(
340
+ schemaComponent.processedData,
341
+ // Get the dataPath relative to the schemaComponent's data:
342
+ dataPath.slice(schemaComponent.dataPath.length),
343
+ nested
344
+ )
345
+ }
@@ -1,5 +1,5 @@
1
1
  import { isFunction } from '@ditojs/utils'
2
- import * as validations from '../validations/index.js'
2
+ import * as validators from '../validators/index.js'
3
3
 
4
4
  // @vue/component
5
5
  export default {
@@ -36,11 +36,15 @@ export default {
36
36
  this.clearErrors()
37
37
  }
38
38
  const { value } = this
39
- // console.log('validate', this.dataPath, value, this.validations)
40
39
  for (const [rule, setting] of Object.entries(this.validations)) {
41
40
  // eslint-disable-next-line import/namespace
42
- const validator = validations[rule]
43
- if (validator && (validator.nullish || value != null)) {
41
+ const validator = validators[rule]
42
+ if (
43
+ validator &&
44
+ // Only apply 'required' validator to empty values.
45
+ // Apply all other validators only to non-empty values.
46
+ (rule === 'required' || value != null && value !== '')
47
+ ) {
44
48
  const { validate, message } = validator
45
49
  if (!validate(value, setting, this.validations)) {
46
50
  isValid = false
@@ -19,7 +19,7 @@ import {
19
19
  mapConcurrently,
20
20
  getValueAtDataPath
21
21
  } from '@ditojs/utils'
22
- import { markRaw } from 'vue'
22
+ import { markRaw, reactive } from 'vue'
23
23
 
24
24
  const typeComponents = {}
25
25
  const unknownTypeReported = {}
@@ -242,37 +242,40 @@ export async function resolveSchemaComponents(schemas) {
242
242
  await mapConcurrently(Object.values(schemas || {}), resolveSchemaComponent)
243
243
  }
244
244
 
245
- const processedSchemas = new WeakSet()
246
- export async function processSchemaComponents(
245
+ const processedSchemaDepths = new WeakMap()
246
+ export function processSchemaComponents(
247
247
  api,
248
248
  schema,
249
249
  routes = null,
250
- level = 0
250
+ level = 0,
251
+ maxDepth = 1
251
252
  ) {
252
- if (schema && !processedSchemas.has(schema)) {
253
- processedSchemas.add(schema)
254
-
255
- const promises = []
256
- const process = (component, name, relativeLevel) => {
257
- promises.push(
258
- processSchemaComponent(
259
- api,
260
- component,
261
- name,
262
- routes,
263
- level + relativeLevel
253
+ if (schema) {
254
+ const depth = processedSchemaDepths.get(schema) ?? 0
255
+ if (depth < maxDepth) {
256
+ processedSchemaDepths.set(schema, depth + 1)
257
+ const promises = []
258
+ const process = (component, name, relativeLevel) => {
259
+ promises.push(
260
+ processSchemaComponent(
261
+ api,
262
+ component,
263
+ name,
264
+ routes,
265
+ level + relativeLevel
266
+ )
264
267
  )
265
- )
266
- }
268
+ }
267
269
 
268
- iterateNestedSchemaComponents(schema, process)
269
- iterateSchemaComponents(getPanelSchemas(schema), process)
270
+ iterateNestedSchemaComponents(schema, process)
271
+ iterateSchemaComponents(getPanelSchemas(schema), process)
270
272
 
271
- await Promise.all(promises)
273
+ return Promise.all(promises)
274
+ }
272
275
  }
273
276
  }
274
277
 
275
- export async function processSchemaComponent(
278
+ export function processSchemaComponent(
276
279
  api,
277
280
  schema,
278
281
  name,
@@ -281,7 +284,7 @@ export async function processSchemaComponent(
281
284
  ) {
282
285
  processSchemaDefaults(api, schema)
283
286
 
284
- await Promise.all([
287
+ return Promise.all([
285
288
  // Also process nested panel schemas.
286
289
  mapConcurrently(
287
290
  getPanelSchemas(schema),
@@ -367,38 +370,41 @@ export function processRouteSchema(api, schema, name, fullPath = null) {
367
370
  }
368
371
 
369
372
  export async function processForms(api, schema, level) {
373
+ const routes = []
370
374
  // First resolve the forms and store the results back on the schema.
371
- let { form, forms, components } = schema
375
+ const { form, forms, components, maxDepth = 1 } = schema
372
376
  if (forms) {
373
- forms = schema.forms = await resolveSchemas(forms, form =>
374
- processForm(api, form)
377
+ schema.forms = await resolveSchemas(forms, form =>
378
+ processForm(api, form, routes, level, maxDepth)
375
379
  )
376
380
  } else if (form) {
377
- form = schema.form = await processForm(api, form)
381
+ schema.form = await processForm(api, form, routes, level, maxDepth)
378
382
  } else if (isObject(components)) {
379
383
  // NOTE: Processing forms in computed components is not supported, since it
380
384
  // only can be computed in conjunction with actual data.
381
- form = {
385
+ const form = {
382
386
  type: 'form',
383
387
  components
384
388
  }
389
+ await processForm(api, form, routes, level, maxDepth)
385
390
  }
386
-
387
- forms ||= { default: form } // Only used for process loop below.
388
- const children = []
389
- for (const form of Object.values(forms)) {
390
- await processSchemaComponents(api, form, children, level)
391
- }
392
- return children
391
+ return routes
393
392
  }
394
393
 
395
- export async function processForm(api, schema) {
394
+ export async function processForm(
395
+ api,
396
+ schema,
397
+ routes = null,
398
+ level = 0,
399
+ maxDepth = 1
400
+ ) {
396
401
  schema = await resolveSchema(schema, true)
397
402
  if (!isForm(schema)) {
398
403
  throw new Error(`Invalid form schema: '${getSchemaIdentifier(schema)}'`)
399
404
  }
400
405
  processSchemaDefaults(api, schema)
401
406
  await processNestedSchemas(api, schema)
407
+ await processSchemaComponents(api, schema, routes, level, maxDepth)
402
408
  return schema
403
409
  }
404
410
 
@@ -515,10 +521,14 @@ export function getFormSchemas(schema, context, modifyForm) {
515
521
  Object.entries(forms).map(([type, form]) => {
516
522
  // Support `schema.components` callbacks to create components on the fly.
517
523
  if (context && isFunction(form.components)) {
518
- form = {
524
+ // Make the form schema reactive since `processForm()` is async, so that
525
+ // the setting of defaults will be picked up by downstream code.
526
+ form = reactive({
519
527
  ...form,
520
528
  components: form.components(context)
521
- }
529
+ })
530
+ // Process the form again, now that we have the components.
531
+ processForm(context.api, form).catch(console.error)
522
532
  }
523
533
  return [type, modifyForm?.(form) ?? form]
524
534
  })
@@ -5,6 +5,5 @@ export const required = {
5
5
  // but unchanged, allow this to pass through:
6
6
  (password && value === undefined)
7
7
  ),
8
- message: 'is required',
9
- nullish: true
8
+ message: 'is required'
10
9
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes