@koumoul/vjsf 3.0.0-beta.5 → 3.0.0-beta.50

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 (164) hide show
  1. package/README.md +21 -0
  2. package/package.json +12 -17
  3. package/src/compat/v2.js +132 -27
  4. package/src/compile/index.js +19 -4
  5. package/src/compile/options.js +4 -9
  6. package/src/compile/v-jsf-compiled.vue.ejs +1 -2
  7. package/src/components/fragments/child-subtitle.vue +25 -0
  8. package/src/components/fragments/help-message.vue +33 -8
  9. package/src/components/fragments/section-header.vue +9 -7
  10. package/src/components/fragments/select-item-icon.vue +2 -2
  11. package/src/components/fragments/select-item.vue +2 -1
  12. package/src/components/fragments/select-selection.vue +2 -1
  13. package/src/components/fragments/selection-group.vue +105 -0
  14. package/src/components/fragments/text-field-menu.vue +16 -7
  15. package/src/components/node.vue +58 -41
  16. package/src/components/nodes/autocomplete.vue +14 -60
  17. package/src/components/nodes/card.vue +39 -0
  18. package/src/components/nodes/checkbox-group.vue +39 -0
  19. package/src/components/nodes/checkbox.vue +31 -26
  20. package/src/components/nodes/color-picker.vue +10 -4
  21. package/src/components/nodes/combobox.vue +17 -40
  22. package/src/components/nodes/date-picker.vue +30 -13
  23. package/src/components/nodes/date-time-picker.vue +83 -3
  24. package/src/components/nodes/expansion-panels.vue +34 -16
  25. package/src/components/nodes/file-input.vue +15 -11
  26. package/src/components/nodes/list.vue +251 -111
  27. package/src/components/nodes/number-combobox.vue +18 -39
  28. package/src/components/nodes/number-field.vue +17 -11
  29. package/src/components/nodes/one-of-select.vue +53 -27
  30. package/src/components/nodes/radio-group.vue +58 -0
  31. package/src/components/nodes/section.vue +4 -1
  32. package/src/components/nodes/select.vue +15 -54
  33. package/src/components/nodes/slider.vue +32 -29
  34. package/src/components/nodes/stepper.vue +10 -2
  35. package/src/components/nodes/switch-group.vue +39 -0
  36. package/src/components/nodes/switch.vue +31 -26
  37. package/src/components/nodes/tabs.vue +20 -8
  38. package/src/components/nodes/text-field.vue +10 -7
  39. package/src/components/nodes/textarea.vue +20 -12
  40. package/src/components/nodes/time-picker.vue +41 -1
  41. package/src/components/nodes/vertical-tabs.vue +16 -6
  42. package/src/components/tree.vue +1 -1
  43. package/src/components/vjsf.vue +11 -1
  44. package/src/composables/use-comp-defaults.js +19 -0
  45. package/src/composables/use-dnd.js +2 -1
  46. package/src/composables/use-get-items.js +53 -0
  47. package/src/composables/use-node.js +136 -0
  48. package/src/composables/use-select-node.js +67 -0
  49. package/src/composables/use-vjsf.js +70 -40
  50. package/src/index.js +5 -2
  51. package/src/options.js +65 -0
  52. package/src/types.ts +64 -33
  53. package/src/utils/arrays.js +37 -6
  54. package/src/utils/build.js +1 -1
  55. package/types/compat/v2.d.ts.map +1 -1
  56. package/types/compile/index.d.ts +2 -2
  57. package/types/compile/index.d.ts.map +1 -1
  58. package/types/compile/options.d.ts +3 -2
  59. package/types/compile/options.d.ts.map +1 -1
  60. package/types/components/fragments/child-subtitle.vue.d.ts +8 -0
  61. package/types/components/fragments/child-subtitle.vue.d.ts.map +1 -0
  62. package/types/components/fragments/help-message.vue.d.ts +2 -2
  63. package/types/components/fragments/node-slot.vue.d.ts +2 -44
  64. package/types/components/fragments/node-slot.vue.d.ts.map +1 -1
  65. package/types/components/fragments/section-header.vue.d.ts +4 -2
  66. package/types/components/fragments/select-item-icon.vue.d.ts +2 -12
  67. package/types/components/fragments/select-item.vue.d.ts +2 -2
  68. package/types/components/fragments/select-selection.vue.d.ts +2 -2
  69. package/types/components/fragments/selection-group.vue.d.ts +5 -0
  70. package/types/components/fragments/selection-group.vue.d.ts.map +1 -0
  71. package/types/components/fragments/text-field-menu.vue.d.ts +2 -2
  72. package/types/components/fragments/text-field-menu.vue.d.ts.map +1 -1
  73. package/types/components/node.vue.d.ts +2 -2
  74. package/types/components/nodes/autocomplete.vue.d.ts +2 -24
  75. package/types/components/nodes/autocomplete.vue.d.ts.map +1 -1
  76. package/types/components/nodes/card.vue.d.ts +10 -0
  77. package/types/components/nodes/card.vue.d.ts.map +1 -0
  78. package/types/components/nodes/checkbox-group.vue.d.ts +5 -0
  79. package/types/components/nodes/checkbox-group.vue.d.ts.map +1 -0
  80. package/types/components/nodes/checkbox.vue.d.ts +3 -8
  81. package/types/components/nodes/color-picker.vue.d.ts +2 -2
  82. package/types/components/nodes/combobox.vue.d.ts +2 -24
  83. package/types/components/nodes/combobox.vue.d.ts.map +1 -1
  84. package/types/components/nodes/date-picker.vue.d.ts +2 -2
  85. package/types/components/nodes/date-time-picker.vue.d.ts +4 -4
  86. package/types/components/nodes/expansion-panels.vue.d.ts +2 -2
  87. package/types/components/nodes/file-input.vue.d.ts +2 -24
  88. package/types/components/nodes/file-input.vue.d.ts.map +1 -1
  89. package/types/components/nodes/list.vue.d.ts +2 -2
  90. package/types/components/nodes/number-combobox.vue.d.ts +2 -24
  91. package/types/components/nodes/number-combobox.vue.d.ts.map +1 -1
  92. package/types/components/nodes/number-field.vue.d.ts +2 -24
  93. package/types/components/nodes/number-field.vue.d.ts.map +1 -1
  94. package/types/components/nodes/one-of-select.vue.d.ts +2 -2
  95. package/types/components/nodes/radio-group.vue.d.ts +5 -0
  96. package/types/components/nodes/radio-group.vue.d.ts.map +1 -0
  97. package/types/components/nodes/section.vue.d.ts +2 -2
  98. package/types/components/nodes/select.vue.d.ts +2 -24
  99. package/types/components/nodes/select.vue.d.ts.map +1 -1
  100. package/types/components/nodes/slider.vue.d.ts +3 -8
  101. package/types/components/nodes/stepper.vue.d.ts +2 -2
  102. package/types/components/nodes/switch-group.vue.d.ts +5 -0
  103. package/types/components/nodes/switch-group.vue.d.ts.map +1 -0
  104. package/types/components/nodes/switch.vue.d.ts +3 -8
  105. package/types/components/nodes/tabs.vue.d.ts +2 -2
  106. package/types/components/nodes/text-field.vue.d.ts +2 -24
  107. package/types/components/nodes/text-field.vue.d.ts.map +1 -1
  108. package/types/components/nodes/textarea.vue.d.ts +2 -24
  109. package/types/components/nodes/textarea.vue.d.ts.map +1 -1
  110. package/types/components/nodes/time-picker.vue.d.ts +8 -1
  111. package/types/components/nodes/vertical-tabs.vue.d.ts +2 -2
  112. package/types/components/options.d.ts +1 -1
  113. package/types/components/options.d.ts.map +1 -1
  114. package/types/components/tree.vue.d.ts +2 -2
  115. package/types/components/vjsf.vue.d.ts +4 -4
  116. package/types/composables/use-comp-defaults.d.ts +8 -0
  117. package/types/composables/use-comp-defaults.d.ts.map +1 -0
  118. package/types/composables/use-dnd.d.ts +3 -3
  119. package/types/composables/use-dnd.d.ts.map +1 -1
  120. package/types/composables/use-field-props.d.ts +30 -0
  121. package/types/composables/use-field-props.d.ts.map +1 -0
  122. package/types/composables/use-field.d.ts +31 -0
  123. package/types/composables/use-field.d.ts.map +1 -0
  124. package/types/composables/use-get-items.d.ts +12 -0
  125. package/types/composables/use-get-items.d.ts.map +1 -0
  126. package/types/composables/use-node.d.ts +32 -0
  127. package/types/composables/use-node.d.ts.map +1 -0
  128. package/types/composables/use-select-field.d.ts +21 -0
  129. package/types/composables/use-select-field.d.ts.map +1 -0
  130. package/types/composables/use-select-node.d.ts +27 -0
  131. package/types/composables/use-select-node.d.ts.map +1 -0
  132. package/types/composables/use-select-props.d.ts +21 -0
  133. package/types/composables/use-select-props.d.ts.map +1 -0
  134. package/types/composables/use-select.d.ts +21 -0
  135. package/types/composables/use-select.d.ts.map +1 -0
  136. package/types/composables/use-vjsf.d.ts +2 -2
  137. package/types/composables/use-vjsf.d.ts.map +1 -1
  138. package/types/iconsets/default-aliases.d.ts +10 -0
  139. package/types/iconsets/default-aliases.d.ts.map +1 -0
  140. package/types/iconsets/mdi-svg.d.ts +3 -0
  141. package/types/iconsets/mdi-svg.d.ts.map +1 -0
  142. package/types/iconsets/mdi.d.ts +3 -0
  143. package/types/iconsets/mdi.d.ts.map +1 -0
  144. package/types/index.d.ts +5 -2
  145. package/types/index.d.ts.map +1 -1
  146. package/types/options.d.ts +9 -0
  147. package/types/options.d.ts.map +1 -0
  148. package/types/types.d.ts +65 -33
  149. package/types/types.d.ts.map +1 -1
  150. package/types/utils/arrays.d.ts +17 -4
  151. package/types/utils/arrays.d.ts.map +1 -1
  152. package/types/utils/build.d.ts +1 -1
  153. package/types/utils/index.d.ts +0 -3
  154. package/types/utils/props.d.ts +8 -2
  155. package/types/utils/props.d.ts.map +1 -1
  156. package/types/utils/slots.d.ts +8 -0
  157. package/types/utils/slots.d.ts.map +1 -1
  158. package/src/components/options.js +0 -27
  159. package/src/utils/global-register.js +0 -13
  160. package/src/utils/index.js +0 -5
  161. package/src/utils/props.js +0 -109
  162. package/src/utils/slots.js +0 -18
  163. package/types/utils/global-register.d.ts +0 -8
  164. package/types/utils/global-register.d.ts.map +0 -1
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # VJSF
2
+
3
+ *vuetify-json-schema-form* - *@koumoul/vjsf on npm*
4
+
5
+ Easily create beautiful forms that output valid data.
6
+
7
+ Based on [Vue.js](https://vuejs.org/) / [Vuetify](https://vuetifyjs.com/) / [JSON Schema](https://json-schema.org/) / [JSON Layout](https://github.com/json-layout/json-layout).
8
+
9
+ See [the documentation](https://koumoul-dev.github.io/vuetify-jsonschema-form/latest/).
10
+
11
+ See [the documentation for deprecated v2](https://koumoul-dev.github.io/vuetify-jsonschema-form/2.x/).
12
+
13
+ ![](doc/static/vjsf.gif)
14
+
15
+ ## Bug reports
16
+
17
+ Bug reports are created using github issues. The examples in the documentation include codepen links, as much as possible please save a duplicate codepen with the minimal schema/config to reproduce your problem.
18
+
19
+ ## Contribute
20
+
21
+ See [CONTRIBUTE.md](./CONTRIBUTE.md).
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@koumoul/vjsf",
3
- "version": "3.0.0-beta.5",
3
+ "version": "3.0.0-beta.50",
4
4
  "description": "Generate forms for the vuetify UI library (vuejs) based on annotated JSON schemas.",
5
5
  "scripts": {
6
6
  "test": "vitest",
7
7
  "build": "vue-tsc",
8
- "watch:build": "vue-tsc --watch"
8
+ "watch:build": "vue-tsc --watch",
9
+ "prepublishOnly": "npm run build && cp ../README.md README.md && cp ../LICENSE LICENSE"
9
10
  },
10
11
  "author": "Alban Mouton <alban.mouton@gmail.com>",
11
12
  "license": "MIT",
@@ -34,22 +35,16 @@
34
35
  "default": "./src/components/*"
35
36
  }
36
37
  },
37
- "./composables/*": {
38
+ "./utils/*.js": {
38
39
  "import": {
39
- "types": "./types/composables/*.d.ts",
40
- "default": "./src/composables/*"
41
- }
42
- },
43
- "./utils": {
44
- "import": {
45
- "types": "./types/utils/index.d.ts",
46
- "default": "./src/utils/index.js"
40
+ "types": "./types/utils/*.d.ts",
41
+ "default": "./src/utils/*.js"
47
42
  }
48
43
  },
49
- "./utils/*": {
44
+ "./composables/*.js": {
50
45
  "import": {
51
- "types": "./types/utils/*.d.ts",
52
- "default": "./src/utils/*"
46
+ "types": "./types/composables/*.d.ts",
47
+ "default": "./src/composables/*.js"
53
48
  }
54
49
  },
55
50
  "./styles/*": {
@@ -72,10 +67,10 @@
72
67
  },
73
68
  "peerDependencies": {
74
69
  "vue": "^3.4.3",
75
- "vuetify": "^3.4.9"
70
+ "vuetify": "^3.6.13"
76
71
  },
77
72
  "dependencies": {
78
- "@json-layout/core": "0.11.0",
73
+ "@json-layout/core": "0.33.1",
79
74
  "@vueuse/core": "^10.5.0",
80
75
  "debug": "^4.3.4",
81
76
  "ejs": "^3.1.9"
@@ -84,7 +79,7 @@
84
79
  "@types/debug": "^4.1.8",
85
80
  "@types/ejs": "^3.1.2",
86
81
  "vitest": "^1.1.1",
87
- "vue": "^3.4.3",
82
+ "vue": "^3.5.6",
88
83
  "vue-tsc": "^1.8.27"
89
84
  }
90
85
  }
package/src/compat/v2.js CHANGED
@@ -1,23 +1,99 @@
1
1
  import ajvModule from 'ajv'
2
2
  import addFormats from 'ajv-formats'
3
- import { resolveRefs, clone } from '@json-layout/core'
3
+ import { resolveLocaleRefs, clone } from '@json-layout/core'
4
4
  import { isPartialGetItemsObj } from '@json-layout/vocabulary'
5
5
 
6
6
  // @ts-ignore
7
7
  const Ajv = /** @type {typeof ajvModule.default} */ (ajvModule)
8
8
 
9
- const processFragment = (/** @type {import("ajv").SchemaObject} */schema) => {
9
+ const expressionKeyMappings = {
10
+ modelRoot: 'rootData',
11
+ root: 'rootData',
12
+ model: 'data',
13
+ value: 'data'
14
+ }
15
+
16
+ /**
17
+ * @param {string} expression
18
+ * @returns {string}
19
+ */
20
+ const prefixExpression = (expression) => {
21
+ // looks like a simple expression missing the data. prefix
22
+ if (expression.match(/^[a-z.]*$/i) && !['data', 'context', 'rootData', 'parent'].some(key => expression.startsWith(key + '.'))) {
23
+ return 'rootData.' + expression
24
+ }
25
+ return expression
26
+ }
27
+
28
+ /**
29
+ *
30
+ * @param {string} expression
31
+ * @param {'js-eval' | 'js-tpl'} [type]
32
+ * @returns {{type: 'js-eval' | 'js-tpl', expr: string, pure: boolean}}
33
+ */
34
+ const fixExpression = (expression, type = 'js-eval') => {
35
+ let expr = expression
36
+ let pure = true
37
+
38
+ if (expr.includes('parent.value')) {
39
+ pure = false
40
+ expr = expr.replace(/parent\.value/g, 'parent.data')
41
+ }
42
+
43
+ for (const [key, value] of Object.entries(expressionKeyMappings)) {
44
+ expr = expr.replace(new RegExp(`${key}\\.`, 'g'), value + '.')
45
+ }
46
+ if (type === 'js-eval') {
47
+ expr = prefixExpression(expr)
48
+ }
49
+ if (type === 'js-tpl') {
50
+ for (const expressionMatch of expr.matchAll(/\{(.*?)\}/g)) {
51
+ if (expressionMatch[1] !== 'q') expr = expr.replace(expressionMatch[0], '${' + prefixExpression(expressionMatch[1]) + '}')
52
+ }
53
+ }
54
+
55
+ if (expr.includes('rootData')) {
56
+ pure = false
57
+ }
58
+
59
+ return { type, expr, pure }
60
+ }
61
+
62
+ /**
63
+ *
64
+ * @param {import("ajv").SchemaObject} schema
65
+ * @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
66
+ * @param {string} schemaId
67
+ * @param {any[]} processed
68
+ */
69
+ const processFragment = (schema, getJSONRef, schemaId, processed) => {
70
+ if (processed.includes(schema)) return
71
+ processed.push(schema)
72
+ if (schema.$ref) {
73
+ const [refFragment, refSchemaId] = getJSONRef(schemaId, schema.$ref)
74
+ processFragment(refFragment, getJSONRef, refSchemaId, processed)
75
+ }
10
76
  if (!schema.layout) {
11
77
  /** @type import('@json-layout/vocabulary').PartialCompObject */
12
78
  const layout = {}
13
79
 
80
+ if (schema.separator || schema['x-separator']) {
81
+ layout.separator = schema.separator || schema['x-separator']
82
+ delete schema.separator
83
+ delete schema['x-separator']
84
+ }
85
+
14
86
  if (schema['x-display'] === 'icon' && (schema.enum || schema.items?.enum)) {
15
87
  layout.getItems = { itemIcon: schema['x-itemIcon'] || 'data.value' }
16
88
  delete schema['x-display']
17
89
  }
18
90
 
19
91
  if (schema['x-display']) {
20
- layout.comp = schema['x-display']
92
+ let display = schema['x-display']
93
+ if (display === 'radio') display = 'radio-group'
94
+ if (display === 'checkbox' && schema.type !== 'boolean') display = 'checkbox-group'
95
+ if (display === 'switch' && schema.type !== 'boolean') display = 'switch-group'
96
+ layout.comp = display
21
97
  delete schema['x-display']
22
98
  }
23
99
 
@@ -32,18 +108,20 @@ const processFragment = (/** @type {import("ajv").SchemaObject} */schema) => {
32
108
  }
33
109
 
34
110
  if (schema['x-fromData']) {
35
- layout.comp = 'select'
36
- layout.getItems = { expr: schema['x-fromData'] }
111
+ layout.comp = layout.comp ?? 'select'
112
+ layout.getItems = fixExpression(schema['x-fromData'])
37
113
  delete schema['x-fromData']
38
114
  }
39
115
 
116
+ if (schema['x-if']) {
117
+ layout.if = fixExpression(schema['x-if'])
118
+ delete schema['x-if']
119
+ }
120
+
40
121
  if (schema['x-fromUrl']) {
41
122
  /** @type string */
42
- let url = schema['x-fromUrl']
43
- for (const expressionMatch of url.matchAll(/\{(.*?)\}/g)) {
44
- if (expressionMatch[1] !== 'q') url = url.replace(expressionMatch[0], '$' + expressionMatch[0])
45
- }
46
- layout.getItems = { url }
123
+ const url = schema['x-fromUrl']
124
+ layout.getItems = { url: fixExpression(url, 'js-tpl') }
47
125
  delete schema['x-fromUrl']
48
126
  }
49
127
  if (layout.getItems && isPartialGetItemsObj(layout.getItems)) {
@@ -58,6 +136,10 @@ const processFragment = (/** @type {import("ajv").SchemaObject} */schema) => {
58
136
  delete schema['x-itemsProp']
59
137
  }
60
138
 
139
+ if (schema['x-cols']) {
140
+ layout.cols = schema['x-cols']
141
+ }
142
+
61
143
  // compact the layout keyword if possible
62
144
  if (Object.keys(layout).length === 1 && 'comp' in layout) {
63
145
  schema.layout = layout.comp
@@ -66,30 +148,51 @@ const processFragment = (/** @type {import("ajv").SchemaObject} */schema) => {
66
148
  }
67
149
  }
68
150
 
69
- if (schema.type === 'object') {
70
- if (schema.properties) {
71
- for (const propertyKey of Object.keys(schema.properties)) {
72
- processFragment(schema.properties[propertyKey])
73
- }
74
- }
75
- if (schema.allOf) {
76
- for (const item of schema.allOf) processFragment(item)
151
+ if (schema.properties) {
152
+ for (const propertyKey of Object.keys(schema.properties)) {
153
+ processFragment(schema.properties[propertyKey], getJSONRef, schemaId, processed)
77
154
  }
78
- if (schema.oneOf) {
79
- for (const item of schema.oneOf) processFragment(item)
80
- }
81
- if (schema.anyOf) {
82
- for (const item of schema.anyOf) processFragment(item)
155
+ }
156
+
157
+ if (schema.allOf) {
158
+ for (const item of schema.allOf) processFragment(item, getJSONRef, schemaId, processed)
159
+ }
160
+
161
+ if (schema.oneOf) {
162
+ if (!schema.oneOfLayout) {
163
+ const constPropertyKey = Object.keys(schema.oneOf[0]?.properties || {})
164
+ .find(key => !!schema.oneOf[0]?.properties[key].const)
165
+ if (constPropertyKey) {
166
+ const constProperty = schema.oneOf[0]?.properties[constPropertyKey]
167
+ if (constProperty?.title) schema.oneOfLayout = { label: constProperty.title }
168
+ if (schema.required && Array.isArray(schema.required)) {
169
+ schema.required = schema.required.filter(key => key !== constPropertyKey)
170
+ }
171
+ }
83
172
  }
173
+ for (const item of schema.oneOf) processFragment(item, getJSONRef, schemaId, processed)
174
+ }
175
+
176
+ if (schema.anyOf) {
177
+ for (const item of schema.anyOf) processFragment(item, getJSONRef, schemaId, processed)
84
178
  }
85
179
 
86
180
  if (schema.type === 'array' && schema.items) {
87
181
  if (Array.isArray(schema.items)) {
88
- for (const item of schema.items) processFragment(item)
182
+ for (const item of schema.items) processFragment(item, getJSONRef, schemaId, processed)
89
183
  } else {
90
- processFragment(schema.items)
184
+ processFragment(schema.items, getJSONRef, schemaId, processed)
91
185
  }
92
186
  }
187
+ if (schema.dependencies) {
188
+ for (const key of Object.keys(schema.dependencies)) {
189
+ processFragment(schema.dependencies[key], getJSONRef, schemaId, processed)
190
+ }
191
+ }
192
+ if (schema.if) {
193
+ if (schema.then) processFragment(schema.then, getJSONRef, schemaId, processed)
194
+ if (schema.else) processFragment(schema.else, getJSONRef, schemaId, processed)
195
+ }
93
196
  }
94
197
 
95
198
  /**
@@ -111,7 +214,9 @@ export function v2compat (_schema, _ajv, lang = 'en') {
111
214
 
112
215
  const schema = /** @type {import("ajv").SchemaObject} */ (clone(_schema))
113
216
  schema.$id = schema.$id ?? '_jl'
114
- resolveRefs(schema, ajv, lang)
115
- processFragment(schema)
217
+ const getJSONRef = resolveLocaleRefs(schema, ajv, lang)
218
+ /** @type {any[]} */
219
+ const processed = []
220
+ processFragment(schema, getJSONRef, schema.$id, processed)
116
221
  return schema
117
222
  }
@@ -37,10 +37,18 @@ function listComps (comps, layout) {
37
37
  * @param {object} schema
38
38
  * @param {import('../types.js').PartialVjsfCompileOptions} [options]
39
39
  * @param {string} [baseImport]
40
- * @returns {string}
40
+ * @returns {Promise<string>}
41
41
  */
42
- export function compile (schema, options = {}, baseImport = '@koumoul/vjsf') {
42
+ export async function compile (schema, options = {}, baseImport = '@koumoul/vjsf') {
43
43
  const fullOptions = getFullOptions(options)
44
+ /** @type {Record<string, string>} */
45
+ const pluginsImportsByName = {}
46
+ for (const pluginImport of fullOptions.pluginsImports) {
47
+ const componentInfo = /** @type {import('@json-layout/vocabulary').ComponentInfo} */((await import(pluginImport + '/info.js')).default)
48
+ fullOptions.components = fullOptions.components ?? {}
49
+ fullOptions.components[componentInfo.name] = componentInfo
50
+ pluginsImportsByName[componentInfo.name] = pluginImport
51
+ }
44
52
  const compiledLayout = compileLayout(schema, { ...fullOptions, code: true })
45
53
  const compiledLayoutCode = serializeCompiledLayout(compiledLayout)
46
54
  /** @type Set<string> */
@@ -50,9 +58,16 @@ export function compile (schema, options = {}, baseImport = '@koumoul/vjsf') {
50
58
  }
51
59
  comps.delete('none')
52
60
 
61
+ /** @type {Record<string, any>} */
62
+ const pluginsComponents = {}
63
+
53
64
  const compImports = [...comps].map(comp => {
54
65
  const compName = comp.replace(/-/g, '') + 'Node'
55
- const compImport = fullOptions.nodeComponentImports[comp] ?? `${baseImport}/components/nodes/${comp}.vue`
66
+ let compImport = `${baseImport}/components/nodes/${comp}.vue`
67
+ if (pluginsImportsByName[comp]) {
68
+ compImport = `${pluginsImportsByName[comp]}/node.vue`
69
+ pluginsComponents[comp] = fullOptions.components?.[comp]
70
+ }
56
71
  return {
57
72
  comp,
58
73
  compName,
@@ -60,6 +75,6 @@ export function compile (schema, options = {}, baseImport = '@koumoul/vjsf') {
60
75
  }
61
76
  })
62
77
 
63
- const code = ejs.render(template, { compiledLayoutCode, compImports, baseImport })
78
+ const code = ejs.render(template, { compiledLayoutCode, compImports, baseImport, pluginsComponents })
64
79
  return code
65
80
  }
@@ -1,8 +1,5 @@
1
- /** @type import("../types.js").PartialVjsfCompileOptions */
2
1
  export const defaultOptions = {
3
- nodeComponentImports: {
4
- markdown: '@koumoul/vjsf-markdown/components/nodes/markdown.vue'
5
- }
2
+ pluginsImports: ['@koumoul/vjsf-markdown']
6
3
  }
7
4
 
8
5
  /**
@@ -11,9 +8,7 @@ export const defaultOptions = {
11
8
  * @returns
12
9
  */
13
10
  export const getFullOptions = (options) => {
14
- const fullOptions = {
15
- ...defaultOptions,
16
- nodeComponentImports: { ...defaultOptions.nodeComponentImports, ...options.nodeComponentImports }
17
- }
18
- return /** @type import('../types.js').VjsfCompileOptions */ (fullOptions)
11
+ /** @type {import('../types.js').VjsfCompileOptions} */
12
+ const fullOptions = { ...defaultOptions, ...options }
13
+ return fullOptions
19
14
  }
@@ -5,7 +5,6 @@ import { StatefulLayout } from '@json-layout/core'
5
5
  import { ref, shallowRef, getCurrentInstance, useSlots, computed } from 'vue'
6
6
  import { useElementSize } from '@vueuse/core'
7
7
 
8
- import { defaultOptions } from '<%- baseImport %>/components/options.js'
9
8
  import Tree from '<%- baseImport %>/components/tree.vue'
10
9
  import { useVjsf, emits } from '<%- baseImport %>/composables/use-vjsf.js'
11
10
  import '<%- baseImport %>/styles/vjsf.css'
@@ -39,7 +38,7 @@ const emit = defineEmits(emits)
39
38
  const { el, statefulLayout, stateTree } = useVjsf(
40
39
  null,
41
40
  computed(() => props.modelValue),
42
- computed(() => props.options),
41
+ computed(() => ({...props.options, components: <%- JSON.stringify(pluginsComponents) %>})),
43
42
  nodeComponents,
44
43
  emit,
45
44
  null,
@@ -0,0 +1,25 @@
1
+ <script setup>
2
+ import { computed } from 'vue'
3
+ import { isSection } from '@json-layout/core'
4
+
5
+ const { modelValue } = defineProps({
6
+ modelValue: {
7
+ /** @type import('vue').PropType<import('@json-layout/core').StateNode> */
8
+ type: Object,
9
+ required: true
10
+ }
11
+ })
12
+
13
+ const pClass = computed(() => {
14
+ if (modelValue.options.density === 'default') return 'mt-1 mb-5'
15
+ if (modelValue.options.density === 'comfortable') return 'mb-4'
16
+ return 'mb-3'
17
+ })
18
+ </script>
19
+ <template>
20
+ <p
21
+ v-if="isSection(modelValue) && modelValue.layout.subtitle"
22
+ :class="`text-subtitle ${pClass}`"
23
+ v-html="modelValue.layout.subtitle"
24
+ />
25
+ </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="vjsf-help-message">
2
+ <div :class="`vjsf-help-message vjsf-help-message-${node.options.density}`">
3
3
  <v-slide-x-reverse-transition>
4
4
  <v-alert
5
5
  v-show="show"
@@ -10,9 +10,12 @@
10
10
  </v-slide-x-reverse-transition>
11
11
  <v-btn
12
12
  color="info"
13
- class="vjsf-help-message-toggle"
14
- :icon="show ? 'mdi-close-circle' : 'mdi-information'"
13
+ :class="`vjsf-help-message-toggle vjsf-help-message-toggle-${node.options.density}`"
14
+ :icon="show ? node.options.icons.close : node.options.icons.infoSymbol"
15
+ :border="0"
16
+ :elevation="show ? 0 : 2"
15
17
  density="compact"
18
+ :size="node.options.density === 'default' ? 28 : 24"
16
19
  :title="show ? '' : node.messages.showHelp"
17
20
  @click="show = !show"
18
21
  />
@@ -20,7 +23,9 @@
20
23
  </template>
21
24
 
22
25
  <script setup>
23
- import { VAlert, VBtn, VSlideXReverseTransition } from 'vuetify/components'
26
+ import { VSlideXReverseTransition } from 'vuetify/components/transitions'
27
+ import { VAlert } from 'vuetify/components/VAlert'
28
+ import { VBtn } from 'vuetify/components/VBtn'
24
29
  import { ref } from 'vue'
25
30
 
26
31
  defineProps({
@@ -37,13 +42,33 @@ const show = ref(false)
37
42
  <style>
38
43
  .vjsf-help-message {
39
44
  position: relative;
40
- min-height: 10px;
45
+ }
46
+ .vjsf-help-message-compact {
47
+ margin-top: 2px;
48
+ margin-bottom: 2px;
49
+ min-height:24px;
50
+ }
51
+ .vjsf-help-message-comfortable {
52
+ margin-top: 4px;
53
+ margin-bottom: 4px;
54
+ min-height:24px;
55
+ }
56
+ .vjsf-help-message-default {
57
+ margin-top: 6px;
58
+ margin-bottom: 6px;
59
+ min-height:28px;
41
60
  }
42
61
  .vjsf-help-message-toggle {
43
62
  position: absolute;
44
- top: -10px;
45
- right: -4px;
63
+ top: 0px;
64
+ right: 0px;
46
65
  z-index: 1;
47
66
  }
67
+ .vjsf-help-message .v-alert {
68
+ padding-right: 22px;
69
+ }
70
+ .vjsf-help-message-default .v-alert {
71
+ padding-right: 26px;
72
+ }
73
+
48
74
  </style>
49
- ../../../types.js
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import { VAlert } from 'vuetify/components'
2
+ import { VAlert } from 'vuetify/components/VAlert'
3
3
  import { computed } from 'vue'
4
4
 
5
5
  const props = defineProps({
@@ -7,6 +7,10 @@ const props = defineProps({
7
7
  /** @type import('vue').PropType<import('../../types.js').VjsfNode> */
8
8
  type: Object,
9
9
  required: true
10
+ },
11
+ hideTitle: {
12
+ type: Boolean,
13
+ default: false
10
14
  }
11
15
  })
12
16
 
@@ -27,12 +31,12 @@ const titleClass = computed(() => {
27
31
 
28
32
  <template>
29
33
  <div
30
- v-if="node.layout.title ?? node.layout.subtitle ?? (node.error && node.validated)"
34
+ v-if="(node.layout.title && !hideTitle) || node.layout.subtitle || (node.error && node.validated)"
31
35
  :class="`mb-${titleDepthBase - node.options.titleDepth} mt-${titleDepthBase - node.options.titleDepth}`"
32
36
  >
33
37
  <component
34
38
  :is="`h${node.options.titleDepth}`"
35
- v-if="node.layout.title"
39
+ v-if="node.layout.title && !hideTitle"
36
40
  :class="`${titleClass}`"
37
41
  >
38
42
  {{ node.layout.title }}
@@ -40,14 +44,12 @@ const titleClass = computed(() => {
40
44
  <p
41
45
  v-if="node.layout.subtitle"
42
46
  :class="`text-subtitle mt-${titleDepthBase - node.options.titleDepth}`"
43
- >
44
- {{ node.layout.subtitle }}
45
- </p>
47
+ v-html="node.layout.subtitle"
48
+ />
46
49
  <v-alert
47
50
  v-if="node.error && node.validated"
48
51
  type="error"
49
52
  :class="`mt-${titleDepthBase - node.options.titleDepth}`"
50
- :density="node.options.density"
51
53
  >
52
54
  {{ node.error }}
53
55
  </v-alert>
@@ -2,7 +2,7 @@
2
2
  // cf https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VSelect/VSelect.tsx#L374
3
3
 
4
4
  import { defineComponent, h, computed } from 'vue'
5
- import { VIcon } from 'vuetify/components'
5
+ import { VIcon } from 'vuetify/components/VIcon'
6
6
 
7
7
  export default defineComponent({
8
8
  props: {
@@ -20,7 +20,7 @@ export default defineComponent({
20
20
  } else if (isSVG.value) {
21
21
  return h('div', { innerHTML: props.icon.replace('<svg ', '<svg class="v-icon__svg" '), class: 'v-icon' })
22
22
  } else {
23
- return h(VIcon, null, props.icon)
23
+ return h(VIcon, null, () => props.icon)
24
24
  }
25
25
  }
26
26
  }
@@ -1,7 +1,8 @@
1
1
  <script setup>
2
2
  // cf https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VSelect/VSelect.tsx#L374
3
3
 
4
- import { VListItem, VCheckboxBtn } from 'vuetify/components'
4
+ import { VCheckboxBtn } from 'vuetify/components/VCheckbox'
5
+ import { VListItem } from 'vuetify/components/VList'
5
6
  import VSelectItemIcon from './select-item-icon.vue'
6
7
 
7
8
  defineProps({
@@ -18,6 +18,7 @@ defineProps({
18
18
  required: true
19
19
  }
20
20
  })
21
+
21
22
  </script>
22
23
 
23
24
  <template>
@@ -26,7 +27,7 @@ defineProps({
26
27
  v-if="item.icon"
27
28
  :icon="item.icon"
28
29
  />
29
- {{ item.title }}
30
+ {{ item.title ?? item.key ?? item.value }}
30
31
  <span
31
32
  v-if="multiple && !last"
32
33
  class="v-select__selection-comma"