@ditojs/admin 3.1.1 → 3.1.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": "3.1.1",
3
+ "version": "3.1.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/main/packages/admin",
@@ -41,8 +41,8 @@
41
41
  "not ie_mob > 0"
42
42
  ],
43
43
  "dependencies": {
44
- "@ditojs/ui": "^3.1.1",
45
- "@ditojs/utils": "^3.1.1"
44
+ "@ditojs/ui": "^3.1.2",
45
+ "@ditojs/utils": "^3.1.2"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@kyvg/vue3-notification": "^3.4.2",
@@ -87,7 +87,7 @@
87
87
  "vue-upload-component": "^3.2.2"
88
88
  },
89
89
  "devDependencies": {
90
- "@ditojs/build": "^3.1.1",
90
+ "@ditojs/build": "^3.1.2",
91
91
  "@kyvg/vue3-notification": "^3.4.2",
92
92
  "@tiptap/core": "^3.31.4",
93
93
  "@tiptap/extension-blockquote": "^3.31.4",
@@ -131,5 +131,5 @@
131
131
  "vue-router": "^5.3.1",
132
132
  "vue-upload-component": "^3.2.2"
133
133
  },
134
- "gitHead": "acce85bb38d817aab18f9d5d03b3aa9dff00c3f9"
134
+ "gitHead": "a87ccff01f65386d9363366fb19bb993d38e8fbc"
135
135
  }
@@ -584,7 +584,11 @@ function takeOverChangedValues(target, before, after) {
584
584
  return target
585
585
  }
586
586
  const isSameShape = (
587
- isPlainObject(before) && isPlainObject(after) && isPlainObject(target) || (
587
+ (
588
+ isPlainObject(before) &&
589
+ isPlainObject(after) &&
590
+ isPlainObject(target)
591
+ ) || (
588
592
  isArray(before) &&
589
593
  isArray(after) &&
590
594
  isArray(target) &&
@@ -23,6 +23,7 @@ import {
23
23
  hasFormSchema,
24
24
  hasMultipleFormSchemas,
25
25
  getFormSchemas,
26
+ getItemFormSchema,
26
27
  getItemFormSchemaFromForms
27
28
  } from './lookup.js'
28
29
 
@@ -605,7 +606,20 @@ export function processSchemaData(schema, data, {
605
606
  }
606
607
  }
607
608
 
608
- processComponents(schema.components, componentPath)
609
+ const components = isFunction(schema.components)
610
+ ? // Unnested components with `components()` callbacks, e.g. sections:
611
+ getItemFormSchema(
612
+ schema,
613
+ data,
614
+ new DitoContext(options.component, {
615
+ schema,
616
+ data,
617
+ dataPath,
618
+ rootData: options.rootData
619
+ })
620
+ ).components
621
+ : schema.components
622
+ processComponents(components, componentPath)
609
623
  processTabOrPanelSchemas(schema.tabs, componentPath)
610
624
  processTabOrPanelSchemas(schema.panels, componentPath)
611
625
 
@@ -6,26 +6,32 @@ import {
6
6
  isFunction,
7
7
  camelize
8
8
  } from '@ditojs/utils'
9
- import { reactive } from 'vue'
9
+ import { reactive, computed } from 'vue'
10
10
  import { getTypeOptions } from './types.js'
11
11
  import {
12
12
  findNestedSchemaComponent,
13
13
  isSchema,
14
14
  isSingleComponentView
15
15
  } from './structure.js'
16
- // TODO: `getFormSchemas()` processes forms with `components()` callbacks on
17
- // every call, which also causes the only circular import between these
18
- // modules. Move this out of the lookup, see the form model.
16
+ // TODO: Setting up the forms of `components()` callbacks causes the only
17
+ // circular import between these modules.
19
18
  import { setupForm } from './setup.js'
20
19
 
21
20
  const emptySchema = {}
22
21
 
22
+ // The forms with the components that `components()` callbacks create, by
23
+ // callback and by the data that they create them for, see
24
+ // `getFormWithCreatedComponents()`.
25
+ const formsByComponentsCallback = new WeakMap()
26
+
23
27
  export function hasFormSchema(schema) {
24
28
  // Support both single form and multiple forms notation, as well as inlined
25
- // components.
29
+ // components, also created by `components()` callbacks.
26
30
  return (
27
- isSchema(schema) &&
28
- isObject(schema.form || schema.forms || schema.components)
31
+ isSchema(schema) && (
32
+ isObject(schema.form || schema.forms || schema.components) ||
33
+ isFunction(schema.components)
34
+ )
29
35
  )
30
36
  }
31
37
 
@@ -96,20 +102,51 @@ export function getFormSchemas(schema, context, modifyForm) {
96
102
  Object.entries(forms).map(([type, form]) => {
97
103
  // Support `schema.components` callbacks to create components on the fly.
98
104
  if (context && isFunction(form.components)) {
99
- // Make the form schema reactive since `setupForm()` is async, so that
100
- // the setting of defaults will be picked up by downstream code.
101
- form = reactive({
102
- ...form,
103
- components: form.components(context)
104
- })
105
- // Process the form again, now that we have the components.
106
- setupForm(context.api, form).catch(console.error)
105
+ form = getFormWithCreatedComponents(form, context)
107
106
  }
108
107
  return [type, modifyForm?.(form) ?? form]
109
108
  })
110
109
  )
111
110
  }
112
111
 
112
+ // Returns the form with the components that its `components()` callback
113
+ // creates for the data of `context`. The forms are cached per data, and the
114
+ // callback only creates new components when the data that it reads changes,
115
+ // so that rendering and the walks of the form model, see `FormModel`, share
116
+ // the same component schemas. `components()` callbacks therefore derive the
117
+ // components from the data of their context, not from its components.
118
+ function getFormWithCreatedComponents(form, context) {
119
+ const { item } = context
120
+ if (!isObject(item)) {
121
+ return createFormWithComponents(form, context)
122
+ }
123
+ let formsByItem = formsByComponentsCallback.get(form.components)
124
+ if (!formsByItem) {
125
+ formsByItem = new WeakMap()
126
+ formsByComponentsCallback.set(form.components, formsByItem)
127
+ }
128
+ let formWithCreatedComponents = formsByItem.get(item)
129
+ if (!formWithCreatedComponents) {
130
+ formWithCreatedComponents = computed(() =>
131
+ createFormWithComponents(form, context)
132
+ )
133
+ formsByItem.set(item, formWithCreatedComponents)
134
+ }
135
+ return formWithCreatedComponents.value
136
+ }
137
+
138
+ function createFormWithComponents(form, context) {
139
+ // Make the form schema reactive since `setupForm()` is async, so that the
140
+ // setting of defaults will be picked up by downstream code.
141
+ const formWithComponents = reactive({
142
+ ...form,
143
+ components: form.components(context)
144
+ })
145
+ // Process the form again, now that it has its components.
146
+ setupForm(context.api, formWithComponents).catch(console.error)
147
+ return formWithComponents
148
+ }
149
+
113
150
  export function getItemFormSchemaFromForms(forms, item) {
114
151
  return forms[item?.type] || forms.default || null
115
152
  }
package/types/index.d.ts CHANGED
@@ -1548,9 +1548,18 @@ export interface RadioSchema<$Item = any>
1548
1548
  layout?: 'horizontal' | 'vertical'
1549
1549
  }
1550
1550
 
1551
- type SectionContent<$Data> = {
1552
- /** The section's field components. */
1553
- components?: Components<$Data>
1551
+ type SectionContent<$Data, $Item = $Data> = {
1552
+ /**
1553
+ * The section's field components, or a callback that
1554
+ * creates them from the data, e.g. fields that depend on
1555
+ * a selected template. The callback runs again when the
1556
+ * data that it reads changes, and receives the context of
1557
+ * the section, so that `item` is the item that contains
1558
+ * it, also for nested sections.
1559
+ */
1560
+ components?:
1561
+ | Components<$Data>
1562
+ | ItemAccessor<$Item, {}, Components<$Data>>
1554
1563
  /**
1555
1564
  * A form schema for the section's content. Use this
1556
1565
  * instead of `components` to get form-level options
@@ -1690,7 +1699,7 @@ export type SectionSchema<$Item = any, $Nested = $Item> = BaseSchema<$Item> &
1690
1699
  * @defaultValue `false`
1691
1700
  */
1692
1701
  nested: true
1693
- } & SectionContent<$Nested>)
1702
+ } & SectionContent<$Nested, $Item>)
1694
1703
  )
1695
1704
 
1696
1705
  export interface CheckboxSchema<$Item = any> extends BaseSchema<$Item> {