@ditojs/admin 1.30.0 → 2.0.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.
Files changed (78) hide show
  1. package/dist/dito-admin.es.js +5291 -6856
  2. package/dist/dito-admin.umd.js +5 -5
  3. package/dist/style.css +1 -1
  4. package/package.json +37 -22
  5. package/src/DitoAdmin.js +44 -58
  6. package/src/DitoComponent.js +18 -50
  7. package/src/DitoContext.js +7 -3
  8. package/src/TypeComponent.js +15 -13
  9. package/src/appState.js +4 -2
  10. package/src/components/DitoAccount.vue +14 -14
  11. package/src/components/DitoButtons.vue +18 -10
  12. package/src/components/DitoClipboard.vue +16 -16
  13. package/src/components/DitoContainer.vue +32 -32
  14. package/src/components/DitoCreateButton.vue +22 -23
  15. package/src/components/DitoDialog.vue +73 -18
  16. package/src/components/DitoEditButtons.vue +31 -31
  17. package/src/components/DitoElement.vue +6 -6
  18. package/src/components/DitoErrors.vue +6 -6
  19. package/src/components/DitoForm.vue +42 -43
  20. package/src/components/DitoFormNested.vue +7 -3
  21. package/src/components/DitoHeader.vue +19 -19
  22. package/src/components/DitoLabel.vue +25 -25
  23. package/src/components/DitoMenu.vue +9 -9
  24. package/src/components/DitoPagination.vue +5 -5
  25. package/src/components/DitoPane.vue +32 -32
  26. package/src/components/DitoPanel.vue +18 -18
  27. package/src/components/DitoPanels.vue +5 -3
  28. package/src/components/DitoRoot.vue +107 -30
  29. package/src/components/DitoSchema.vue +76 -74
  30. package/src/components/DitoSchemaInlined.vue +29 -29
  31. package/src/components/DitoScopes.vue +14 -13
  32. package/src/components/DitoSpinner.vue +101 -0
  33. package/src/components/DitoTableCell.vue +19 -25
  34. package/src/components/DitoTableHead.vue +10 -7
  35. package/src/components/DitoTabs.vue +7 -6
  36. package/src/components/DitoTreeItem.vue +89 -85
  37. package/src/components/DitoVNode.vue +9 -7
  38. package/src/components/DitoView.vue +25 -21
  39. package/src/mixins/DataMixin.js +2 -2
  40. package/src/mixins/DitoMixin.js +43 -46
  41. package/src/mixins/DomMixin.js +1 -1
  42. package/src/mixins/EmitterMixin.js +11 -11
  43. package/src/mixins/RouteMixin.js +20 -10
  44. package/src/mixins/SchemaParentMixin.js +2 -2
  45. package/src/mixins/SourceMixin.js +7 -9
  46. package/src/mixins/TypeMixin.js +29 -34
  47. package/src/mixins/ValidationMixin.js +4 -19
  48. package/src/types/TypeButton.vue +11 -15
  49. package/src/types/TypeCheckbox.vue +7 -8
  50. package/src/types/TypeCheckboxes.vue +14 -15
  51. package/src/types/TypeCode.vue +5 -5
  52. package/src/types/TypeColor.vue +9 -12
  53. package/src/types/TypeComponent.vue +12 -7
  54. package/src/types/TypeComputed.vue +13 -12
  55. package/src/types/TypeDate.vue +10 -11
  56. package/src/types/TypeLabel.vue +1 -1
  57. package/src/types/TypeList.vue +115 -92
  58. package/src/types/TypeMarkup.vue +166 -125
  59. package/src/types/TypeMultiselect.vue +37 -47
  60. package/src/types/TypeNumber.vue +10 -11
  61. package/src/types/TypeObject.vue +62 -46
  62. package/src/types/TypeProgress.vue +7 -8
  63. package/src/types/TypeRadio.vue +15 -14
  64. package/src/types/TypeSection.vue +10 -10
  65. package/src/types/TypeSelect.vue +32 -33
  66. package/src/types/TypeSlider.vue +20 -22
  67. package/src/types/TypeSwitch.vue +8 -9
  68. package/src/types/TypeText.vue +7 -8
  69. package/src/types/TypeTextarea.vue +8 -9
  70. package/src/types/TypeTreeList.vue +40 -34
  71. package/src/types/TypeUpload.vue +61 -61
  72. package/src/utils/accessor.js +1 -1
  73. package/src/utils/data.js +0 -4
  74. package/src/utils/options.js +48 -0
  75. package/src/utils/path.js +5 -0
  76. package/src/utils/schema.js +73 -56
  77. package/src/utils/vue.js +11 -0
  78. package/types/index.d.ts +1 -1
package/src/utils/data.js CHANGED
@@ -83,10 +83,6 @@ export function isTemporaryId(id) {
83
83
  return /^@/.test(id)
84
84
  }
85
85
 
86
- export function hasTemporaryId(data, idKey = 'id') {
87
- return isTemporaryId(data?.[idKey])
88
- }
89
-
90
86
  export function isReference(data, idKey = 'id') {
91
87
  // Returns true if value is an object that holds nothing more than an id.
92
88
  return data?.[idKey] != null && Object.keys(data).length === 1
@@ -0,0 +1,48 @@
1
+ // A mini-replication of vue's internal `resolveMergedOptions()` but only
2
+ // handling our own added options properties and merging them early instead
3
+ // of lazily.
4
+
5
+ export function resolveMergedOptions(options) {
6
+ const { mixins } = options
7
+ return mixins || options.extends
8
+ ? mergeOptions(
9
+ { ...options },
10
+ options
11
+ )
12
+ : options
13
+ }
14
+
15
+ export function mergeOptions(to, from) {
16
+ if (from.extends) {
17
+ mergeOptions(to, from.extends)
18
+ }
19
+ if (from.mixins) {
20
+ for (const mixin of from.mixins) {
21
+ mergeOptions(to, mixin)
22
+ }
23
+ }
24
+ for (const key of ditoOptionKeys) {
25
+ if (key in from) {
26
+ to[key] = from[key]
27
+ }
28
+ }
29
+ return to
30
+ }
31
+
32
+ const ditoOptionKeys = [
33
+ 'defaultValue',
34
+ 'defaultNested',
35
+ 'defaultVisible',
36
+ 'generateLabel',
37
+ 'excludeValue',
38
+ 'ignoreMissingValue',
39
+ 'omitPadding',
40
+ 'processValue',
41
+ 'processSchema',
42
+ 'getPanelSchema',
43
+ 'getFormSchemasForProcessing',
44
+ // Vue 3 / Vue-router 4 forgets these.
45
+ // TODO: Create bug-report?
46
+ 'beforeRouteUpdate',
47
+ 'beforeRouteLeave'
48
+ ]
@@ -0,0 +1,5 @@
1
+ export function resolvePath(path) {
2
+ // For paths staring with `/`, we can use the native `URL()` class to resolve
3
+ // the path, which will also handle `..` and `.` segments:
4
+ return new URL(`file:${path}`).pathname
5
+ }
@@ -1,5 +1,6 @@
1
- import Vue from 'vue'
2
1
  import DitoContext from '../DitoContext.js'
2
+ import DitoMixin from '../mixins/DitoMixin.js'
3
+ import TypeMixin from '../mixins/TypeMixin.js'
3
4
  import { getUid } from './uid.js'
4
5
  import { SchemaGraph } from './SchemaGraph.js'
5
6
  import { appendDataPath, isTemporaryId } from './data.js'
@@ -7,6 +8,7 @@ import {
7
8
  isObject, isString, isArray, isFunction, isPromise, clone, camelize, isModule,
8
9
  mapConcurrently
9
10
  } from '@ditojs/utils'
11
+ import { markRaw } from 'vue'
10
12
 
11
13
  const typeComponents = {}
12
14
  const unknownTypeReported = {}
@@ -25,21 +27,6 @@ export function getTypeComponent(type, allowNull = false) {
25
27
  return component
26
28
  }
27
29
 
28
- export function forEachSchema(schema, callback) {
29
- const schemas = [
30
- ...Object.values(schema?.tabs || {}),
31
- schema
32
- ]
33
- for (const schema of schemas) {
34
- if (schema) {
35
- const res = callback(schema)
36
- if (res !== undefined) {
37
- return res
38
- }
39
- }
40
- }
41
- }
42
-
43
30
  export function forEachSchemaComponent(schema, callback) {
44
31
  if (isSingleComponentView(schema)) {
45
32
  const res = callback(schema.component, schema.name)
@@ -47,14 +34,17 @@ export function forEachSchemaComponent(schema, callback) {
47
34
  return res
48
35
  }
49
36
  } else {
50
- return forEachSchema(schema, schema => {
37
+ const schemas = schema
38
+ ? [...Object.values(schema.tabs || {}), schema]
39
+ : []
40
+ for (const schema of schemas) {
51
41
  for (const [name, component] of Object.entries(schema.components || {})) {
52
42
  const res = callback(component, name)
53
43
  if (res !== undefined) {
54
44
  return res
55
45
  }
56
46
  }
57
- })
47
+ }
58
48
  }
59
49
  }
60
50
 
@@ -161,21 +151,45 @@ export async function resolvePanels(schema) {
161
151
  }
162
152
  }
163
153
 
164
- export async function processView(component, api, schema, name, routes) {
154
+ export async function resolveSchemaComponent(schema) {
155
+ // Resolves async schema components and adds DitoMixin and TypeMixin to them.
156
+ let { component } = schema
157
+ if (component) {
158
+ component = await resolveSchema(component, true)
159
+ if (component) {
160
+ // Prevent warning: "Vue received a Component which was made a reactive
161
+ // object. This can lead to unnecessary performance overhead, and should
162
+ // be avoided by marking the component with `markRaw`":
163
+ schema.component = markRaw({
164
+ ...component,
165
+ mixins: [DitoMixin, TypeMixin, ...(component.mixins || [])]
166
+ })
167
+ }
168
+ }
169
+ }
170
+
171
+ export async function resolveSchemaComponents(schemas) {
172
+ // `schemas` are of the same possible forms as passed to `getNamedSchemas()`
173
+ await mapConcurrently(Object.values(schemas || {}), resolveSchemaComponent)
174
+ }
175
+
176
+ export async function processView(component, api, schema, name) {
165
177
  const children = []
166
178
  processRouteSchema(api, schema, name)
167
179
  await resolvePanels(schema)
168
180
  if (isView(schema)) {
181
+ let level = 0
169
182
  if (isSingleComponentView(schema)) {
170
- await processComponent(api, schema.component, name, children, 0)
183
+ await processComponent(api, schema.component, name, children, level)
171
184
  } else {
172
185
  // A multi-component view, start at level 1
173
- await processSchemaComponents(api, schema, children, 1)
186
+ await processSchemaComponents(api, schema, children, ++level)
174
187
  }
188
+ schema.level = level
175
189
  } else {
176
190
  throw new Error(`Invalid view schema: '${getSchemaIdentifier(schema)}'`)
177
191
  }
178
- routes.push({
192
+ return {
179
193
  path: `/${schema.path}`,
180
194
  children,
181
195
  component,
@@ -183,13 +197,13 @@ export async function processView(component, api, schema, name, routes) {
183
197
  api,
184
198
  schema
185
199
  }
186
- })
200
+ }
187
201
  }
188
202
 
189
- export function processComponent(api, schema, name, routes, level) {
203
+ export async function processComponent(api, schema, name, routes, level) {
190
204
  schema.level = level
191
205
  // Delegate schema processing to the actual type components.
192
- return getTypeOptions(schema)?.processSchema?.(
206
+ await getTypeOptions(schema)?.processSchema?.(
193
207
  api, schema, name, routes, level
194
208
  )
195
209
  }
@@ -230,15 +244,15 @@ export async function processForms(api, schema, level) {
230
244
  return children
231
245
  }
232
246
 
233
- export async function resolveForm(form) {
234
- form = await resolveSchema(form, true)
235
- if (!isForm(form)) {
236
- throw new Error(`Invalid form schema: '${getSchemaIdentifier(form)}'`)
247
+ export async function resolveForm(schema) {
248
+ schema = await resolveSchema(schema, true)
249
+ if (!isForm(schema)) {
250
+ throw new Error(`Invalid form schema: '${getSchemaIdentifier(schema)}'`)
237
251
  }
238
- if (form) {
239
- await resolvePanels(form)
252
+ if (schema) {
253
+ await resolvePanels(schema)
240
254
  }
241
- return form
255
+ return schema
242
256
  }
243
257
 
244
258
  export function hasFormSchema(schema) {
@@ -264,20 +278,23 @@ export function isSingleComponentView(schema) {
264
278
  )
265
279
  }
266
280
 
267
- export function getViewSchema(schema, context) {
281
+ export function getViewFormSchema(schema, context) {
268
282
  const { view } = schema
269
283
  const viewSchema = view && context.views[view]
270
284
  return viewSchema
271
- ? hasFormSchema(viewSchema)
272
- ? viewSchema
273
- // NOTE: Views can have tabs, in which case the view component is nested
274
- // in one of the tabs, go find it.
275
- : forEachSchema(viewSchema, schema => {
276
- const viewComponent = schema.components?.[view]
277
- if (hasFormSchema(viewComponent)) {
278
- return viewComponent
279
- }
280
- })
285
+ // NOTE: Views can have tabs, in which case the view component is nested
286
+ // in one of the tabs, go find it.
287
+ ? forEachSchemaComponent(viewSchema, schema => {
288
+ if (hasFormSchema(schema)) {
289
+ return schema
290
+ }
291
+ }) || null
292
+ : null
293
+ }
294
+
295
+ export function getViewSchema(schema, context) {
296
+ return getViewFormSchema(schema, context)
297
+ ? context.views[schema.view]
281
298
  : null
282
299
  }
283
300
 
@@ -291,9 +308,9 @@ export function getViewEditPath(schema, context) {
291
308
  }
292
309
 
293
310
  export function getFormSchemas(schema, context, modifyForm) {
294
- const view = getViewSchema(schema, context)
295
- if (view) {
296
- schema = view
311
+ const viewSchema = getViewFormSchema(schema, context)
312
+ if (viewSchema) {
313
+ schema = viewSchema
297
314
  } else if (schema.view) {
298
315
  throw new Error(`Unknown view: '${schema.view}'`)
299
316
  }
@@ -419,17 +436,17 @@ export function computeValue(schema, data, name, dataPath, {
419
436
  rootData
420
437
  }))
421
438
  if (value !== undefined) {
422
- // Use `$set()` directly instead of `this.value = …` to update the
439
+ // Access `data[name]` directly instead of `this.value = …` to update the
423
440
  // value without calling parse():
424
- Vue.set(data, name, value)
441
+ data[name] = value
425
442
  }
426
443
  }
427
444
  // If the value is still missing after compute, set the default for it:
428
445
  if (!(name in data) && !ignoreMissingValue(schema)) {
429
- Vue.set(data, name, getDefaultValue(schema))
446
+ data[name] = getDefaultValue(schema)
430
447
  }
431
448
  // Now access the value. This is important for reactivity and needs to
432
- // happen after all prior manipulation through `$set()`, see above:
449
+ // happen after all prior manipulation of `data[name]`, see above:
433
450
  return data[name]
434
451
  }
435
452
 
@@ -662,7 +679,7 @@ export function processSchemaData(
662
679
  return processedData || data
663
680
  }
664
681
 
665
- export function getNamedSchemas(descriptions, defaults) {
682
+ export function getNamedSchemas(schemas, defaults) {
666
683
  const toObject = (array, toSchema) => {
667
684
  return array.length > 0
668
685
  ? array.reduce((object, value) => {
@@ -677,15 +694,15 @@ export function getNamedSchemas(descriptions, defaults) {
677
694
  : null
678
695
  }
679
696
 
680
- return isArray(descriptions)
681
- ? toObject(descriptions, value => (
697
+ return isArray(schemas)
698
+ ? toObject(schemas, value => (
682
699
  isObject(value) ? value : {
683
700
  name: camelize(value, false)
684
701
  }
685
702
  ))
686
- : isObject(descriptions)
703
+ : isObject(schemas)
687
704
  ? toObject(
688
- Object.entries(descriptions),
705
+ Object.entries(schemas),
689
706
  ([name, value]) =>
690
707
  isObject(value) ? {
691
708
  name,
@@ -712,7 +729,7 @@ function getType(schemaOrType) {
712
729
  }
713
730
 
714
731
  export function getTypeOptions(schemaOrType) {
715
- return getTypeComponent(getType(schemaOrType), true)?.options ?? null
732
+ return getTypeComponent(getType(schemaOrType), true) ?? null
716
733
  }
717
734
 
718
735
  export function getSourceType(schemaOrType) {
@@ -0,0 +1,11 @@
1
+ import { useSlots, Comment } from 'vue'
2
+ import { isString, asArray } from '@ditojs/utils'
3
+
4
+ export function hasVNodeContent(vnode) {
5
+ return vnode && asArray(vnode).some(vnode => vnode.type !== Comment)
6
+ }
7
+
8
+ export function hasSlotContent(slot, props = {}) {
9
+ slot = isString(slot) ? useSlots()[slot] : slot
10
+ return hasVNodeContent(slot?.(props))
11
+ }
package/types/index.d.ts CHANGED
@@ -1093,7 +1093,7 @@ export type Component<$Item = any> =
1093
1093
  | SectionSchema<$Item>
1094
1094
  | HiddenSchema<$Item>
1095
1095
 
1096
- export type Components<$Item = any> = Component<$Item>[] | {
1096
+ export type Components<$Item = any> = Component<$Item>[] | {
1097
1097
  [$name: string]: Component<$Item>
1098
1098
  }
1099
1099