@koumoul/vjsf 3.0.0-alpha.3 → 3.0.0-alpha.5

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 (107) hide show
  1. package/package.json +27 -6
  2. package/src/compile/index.js +18 -4
  3. package/src/compile/options.js +19 -0
  4. package/src/compile/v-jsf-compiled.vue.ejs +28 -10
  5. package/src/components/fragments/help-message.vue +3 -2
  6. package/src/components/fragments/node-slot.vue +3 -3
  7. package/src/components/fragments/section-header.vue +2 -1
  8. package/src/components/fragments/text-field-menu.vue +3 -3
  9. package/src/components/node.vue +27 -10
  10. package/src/components/nodes/autocomplete.vue +3 -10
  11. package/src/components/nodes/checkbox.vue +3 -3
  12. package/src/components/nodes/color-picker.vue +3 -3
  13. package/src/components/nodes/combobox.vue +3 -4
  14. package/src/components/nodes/date-picker.vue +3 -4
  15. package/src/components/nodes/date-time-picker.vue +2 -2
  16. package/src/components/nodes/expansion-panels.vue +4 -4
  17. package/src/components/nodes/file-input.vue +43 -0
  18. package/src/components/nodes/list.vue +14 -11
  19. package/src/components/nodes/number-combobox.vue +3 -4
  20. package/src/components/nodes/number-field.vue +3 -4
  21. package/src/components/nodes/one-of-select.vue +4 -4
  22. package/src/components/nodes/section.vue +4 -3
  23. package/src/components/nodes/select.vue +3 -4
  24. package/src/components/nodes/slider.vue +3 -3
  25. package/src/components/nodes/stepper.vue +4 -6
  26. package/src/components/nodes/switch.vue +3 -3
  27. package/src/components/nodes/tabs.vue +4 -4
  28. package/src/components/nodes/text-field.vue +9 -4
  29. package/src/components/nodes/textarea.vue +26 -6
  30. package/src/components/nodes/vertical-tabs.vue +4 -4
  31. package/src/components/options.js +9 -6
  32. package/src/components/tree.vue +2 -2
  33. package/src/components/vjsf.vue +9 -13
  34. package/src/composables/use-dnd.js +16 -1
  35. package/src/composables/use-vjsf.js +11 -9
  36. package/src/index.js +1 -0
  37. package/src/{components/types.ts → types.ts} +18 -2
  38. package/src/utils/global-register.js +10 -0
  39. package/src/utils/index.js +5 -0
  40. package/src/utils/props.js +4 -4
  41. package/src/utils/slots.js +2 -3
  42. package/types/compile/index.d.ts +3 -2
  43. package/types/compile/index.d.ts.map +1 -1
  44. package/types/compile/options.d.ts +4 -0
  45. package/types/compile/options.d.ts.map +1 -0
  46. package/types/components/fragments/help-message.vue.d.ts +2 -2
  47. package/types/components/fragments/node-slot.vue.d.ts +8 -8
  48. package/types/components/fragments/node-slot.vue.d.ts.map +1 -1
  49. package/types/components/fragments/section-header.vue.d.ts +2 -2
  50. package/types/components/fragments/select-item.vue.d.ts +2 -2
  51. package/types/components/fragments/text-field-menu.vue.d.ts +4 -4
  52. package/types/components/fragments/text-field-menu.vue.d.ts.map +1 -1
  53. package/types/components/global-register.d.ts +8 -0
  54. package/types/components/global-register.d.ts.map +1 -0
  55. package/types/components/node.vue.d.ts +4 -4
  56. package/types/components/nodes/autocomplete.vue.d.ts +8 -8
  57. package/types/components/nodes/autocomplete.vue.d.ts.map +1 -1
  58. package/types/components/nodes/checkbox.vue.d.ts +4 -4
  59. package/types/components/nodes/color-picker.vue.d.ts +4 -4
  60. package/types/components/nodes/combobox.vue.d.ts +8 -8
  61. package/types/components/nodes/combobox.vue.d.ts.map +1 -1
  62. package/types/components/nodes/date-picker.vue.d.ts +4 -4
  63. package/types/components/nodes/date-time-picker.vue.d.ts +4 -4
  64. package/types/components/nodes/expansion-panels.vue.d.ts +4 -4
  65. package/types/components/nodes/file-input.vue.d.ts +27 -0
  66. package/types/components/nodes/file-input.vue.d.ts.map +1 -0
  67. package/types/components/nodes/list.vue.d.ts +4 -4
  68. package/types/components/nodes/number-combobox.vue.d.ts +8 -8
  69. package/types/components/nodes/number-combobox.vue.d.ts.map +1 -1
  70. package/types/components/nodes/number-field.vue.d.ts +8 -8
  71. package/types/components/nodes/number-field.vue.d.ts.map +1 -1
  72. package/types/components/nodes/one-of-select.vue.d.ts +4 -4
  73. package/types/components/nodes/section.vue.d.ts +4 -4
  74. package/types/components/nodes/select.vue.d.ts +8 -8
  75. package/types/components/nodes/select.vue.d.ts.map +1 -1
  76. package/types/components/nodes/slider.vue.d.ts +4 -4
  77. package/types/components/nodes/stepper.vue.d.ts +4 -4
  78. package/types/components/nodes/switch.vue.d.ts +4 -4
  79. package/types/components/nodes/tabs.vue.d.ts +4 -4
  80. package/types/components/nodes/text-field.vue.d.ts +8 -8
  81. package/types/components/nodes/text-field.vue.d.ts.map +1 -1
  82. package/types/components/nodes/textarea.vue.d.ts +8 -8
  83. package/types/components/nodes/textarea.vue.d.ts.map +1 -1
  84. package/types/components/nodes/vertical-tabs.vue.d.ts +4 -4
  85. package/types/components/options.d.ts +3 -3
  86. package/types/components/options.d.ts.map +1 -1
  87. package/types/components/tree.vue.d.ts +2 -2
  88. package/types/components/types.d.ts +14 -2
  89. package/types/components/types.d.ts.map +1 -1
  90. package/types/components/vjsf.vue.d.ts +3 -3
  91. package/types/composables/use-dnd.d.ts +5 -1
  92. package/types/composables/use-dnd.d.ts.map +1 -1
  93. package/types/composables/use-vjsf.d.ts +4 -5
  94. package/types/composables/use-vjsf.d.ts.map +1 -1
  95. package/types/index.d.ts +1 -0
  96. package/types/index.d.ts.map +1 -1
  97. package/types/types.d.ts +95 -0
  98. package/types/types.d.ts.map +1 -0
  99. package/types/utils/global-register.d.ts +8 -0
  100. package/types/utils/global-register.d.ts.map +1 -0
  101. package/types/utils/index.d.ts +6 -0
  102. package/types/utils/index.d.ts.map +1 -0
  103. package/types/utils/props.d.ts +3 -3
  104. package/types/utils/props.d.ts.map +1 -1
  105. package/types/utils/slots.d.ts +3 -3
  106. package/types/utils/slots.d.ts.map +1 -1
  107. package/src/components/nodes/markdown.vue +0 -242
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koumoul/vjsf",
3
- "version": "3.0.0-alpha.3",
3
+ "version": "3.0.0-alpha.5",
4
4
  "description": "Generate forms for the vuetify UI library (vuejs) based on annotated JSON schemas.",
5
5
  "scripts": {
6
6
  "test": "vitest",
@@ -22,6 +22,12 @@
22
22
  "types": "./types/index.d.ts"
23
23
  }
24
24
  },
25
+ "./types.js": {
26
+ "import": {
27
+ "default": "./src/types.js",
28
+ "types": "./types/types.d.ts"
29
+ }
30
+ },
25
31
  "./components/*": {
26
32
  "import": {
27
33
  "default": "./src/components/*",
@@ -34,6 +40,23 @@
34
40
  "types": "./types/composables/*.d.ts"
35
41
  }
36
42
  },
43
+ "./utils": {
44
+ "import": {
45
+ "default": "./src/utils/index.js",
46
+ "types": "./types/utils/index.d.ts"
47
+ }
48
+ },
49
+ "./utils/*": {
50
+ "import": {
51
+ "default": "./src/utils/*",
52
+ "types": "./types/utils/*.d.ts"
53
+ }
54
+ },
55
+ "./styles/*": {
56
+ "import": {
57
+ "default": "./src/styles/*"
58
+ }
59
+ },
37
60
  "./compile": {
38
61
  "import": {
39
62
  "default": "./src/compile/index.js",
@@ -49,15 +72,13 @@
49
72
  },
50
73
  "peerDependencies": {
51
74
  "vue": "^3.3.4",
52
- "vuetify": "^3.4.2"
75
+ "vuetify": "^3.4.8"
53
76
  },
54
77
  "dependencies": {
55
- "@json-layout/core": "0.4.0",
78
+ "@json-layout/core": "0.7.0",
56
79
  "@vueuse/core": "^10.5.0",
57
80
  "debug": "^4.3.4",
58
- "easymde": "^2.18.0",
59
- "ejs": "^3.1.9",
60
- "rfdc": "^1.3.0"
81
+ "ejs": "^3.1.9"
61
82
  },
62
83
  "devDependencies": {
63
84
  "@types/debug": "^4.1.8",
@@ -5,6 +5,7 @@ import ejs from 'ejs'
5
5
  import { compile as compileLayout } from '@json-layout/core'
6
6
  import { serialize as serializeCompiledLayout } from '@json-layout/core/src/compile/serialize'
7
7
  import { childIsCompObject, isCompObject, isSwitchStruct } from '@json-layout/vocabulary'
8
+ import { getFullOptions } from './options.js'
8
9
 
9
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
10
11
 
@@ -34,11 +35,13 @@ function listComps (comps, layout) {
34
35
 
35
36
  /**
36
37
  * @param {object} schema
37
- * @param {string} baseImport
38
+ * @param {import('../types.js').PartialVjsfCompileOptions} [options]
39
+ * @param {string} [baseImport]
38
40
  * @returns {string}
39
41
  */
40
- export function compile (schema, baseImport = '@koumoul/vjsf') {
41
- const compiledLayout = compileLayout(schema, { code: true })
42
+ export function compile (schema, options = {}, baseImport = '@koumoul/vjsf') {
43
+ const fullOptions = getFullOptions(options)
44
+ const compiledLayout = compileLayout(schema, { ...fullOptions, code: true })
42
45
  const compiledLayoutCode = serializeCompiledLayout(compiledLayout)
43
46
  /** @type Set<string> */
44
47
  const comps = new Set([])
@@ -46,6 +49,17 @@ export function compile (schema, baseImport = '@koumoul/vjsf') {
46
49
  listComps(comps, layout)
47
50
  }
48
51
  comps.delete('none')
49
- const code = ejs.render(template, { compiledLayoutCode, comps, baseImport })
52
+
53
+ const compImports = [...comps].map(comp => {
54
+ const compName = comp.replace(/-/g, '') + 'Node'
55
+ const compImport = fullOptions.nodeComponentImports[comp] ?? `${baseImport}/components/nodes/${comp}.vue`
56
+ return {
57
+ comp,
58
+ compName,
59
+ compImport
60
+ }
61
+ })
62
+
63
+ const code = ejs.render(template, { compiledLayoutCode, compImports, baseImport })
50
64
  return code
51
65
  }
@@ -0,0 +1,19 @@
1
+ /** @type import("../types.js").PartialVjsfCompileOptions */
2
+ export const defaultOptions = {
3
+ nodeComponentImports: {
4
+ markdown: '@koumoul/vjsf-markdown/components/nodes/markdown.vue'
5
+ }
6
+ }
7
+
8
+ /**
9
+ *
10
+ * @param {import("../types.js").PartialVjsfCompileOptions} options
11
+ * @returns
12
+ */
13
+ export const getFullOptions = (options) => {
14
+ const fullOptions = {
15
+ ...defaultOptions,
16
+ nodeComponentImports: { ...defaultOptions.nodeComponentImports, ...options.nodeComponentImports }
17
+ }
18
+ return /** @type import('../types.js').VjsfCompileOptions */ (fullOptions)
19
+ }
@@ -8,26 +8,41 @@ import { useElementSize } from '@vueuse/core'
8
8
  import { defaultOptions } from '<%- baseImport %>/components/options.js'
9
9
  import Tree from '<%- baseImport %>/components/tree.vue'
10
10
  import { useVjsf, emits } from '<%- baseImport %>/composables/use-vjsf.js'
11
- <% comps.forEach(function(comp){ %>
12
- import <%= comp.replace(/-/g, '') %>Node from '<%- baseImport %>/components/nodes/<%= comp %>.vue'
11
+ import '<%- baseImport %>/styles/vjsf.css'
12
+ import { registeredNodeComponents } from '<%- baseImport %>/utils'
13
+
14
+ <% compImports.forEach(function({compName, compImport}){ %>
15
+ import <%= compName %> from '<%- compImport %>'
13
16
  <% }); %>
14
17
 
15
18
  <%- compiledLayoutCode %>
16
19
 
17
- const vueInstance = getCurrentInstance()
18
- <% comps.forEach(function(comp){ %>
19
- if (!vueInstance?.appContext.app.component('vjsf-node-<%= comp %>')) {
20
- vueInstance.appContext.app.component('vjsf-node-<%= comp %>', <%= comp.replace(/-/g, '') %>Node)
20
+ const nodeComponents = {
21
+ <% compImports.forEach(function({comp, compName}){ %>
22
+ "<%= comp %>": <%= compName %>,
23
+ <% }); %>
24
+ ...registeredNodeComponents
21
25
  }
22
- <% }); %>
23
26
 
24
- const props = defineProps(['modelValue', 'options'])
25
- const emit = defineEmits(Object.keys(emits))
27
+ const props = defineProps({
28
+ modelValue: {
29
+ type: null,
30
+ default: null
31
+ },
32
+ options: {
33
+ /** @type import('vue').PropType<import('<%- baseImport %>/types.js').PartialVjsfOptions> */
34
+ type: Object,
35
+ required: true
36
+ }
37
+ })
38
+
39
+ const emit = defineEmits(emits)
26
40
 
27
41
  const { el, statefulLayout, stateTree } = useVjsf(
28
42
  null,
29
43
  computed(() => props.modelValue),
30
44
  computed(() => props.options),
45
+ nodeComponents,
31
46
  emit,
32
47
  null,
33
48
  computed(() => compiledLayout)
@@ -35,7 +50,10 @@ const { el, statefulLayout, stateTree } = useVjsf(
35
50
  </script>
36
51
 
37
52
  <template>
38
- <div ref="el">
53
+ <div
54
+ ref="el"
55
+ class="vjsf"
56
+ >
39
57
  <tree
40
58
  v-if="statefulLayout && stateTree"
41
59
  :model-value="stateTree"
@@ -20,12 +20,12 @@
20
20
  </template>
21
21
 
22
22
  <script setup>
23
- import { VAlert, VBtn } from 'vuetify/components'
23
+ import { VAlert, VBtn, VSlideXReverseTransition } from 'vuetify/components'
24
24
  import { ref } from 'vue'
25
25
 
26
26
  defineProps({
27
27
  node: {
28
- /** @type import('vue').PropType<import('../types.js').VjsfNode> */
28
+ /** @type import('vue').PropType<import('../../types.js').VjsfNode> */
29
29
  type: Object,
30
30
  required: true
31
31
  }
@@ -46,3 +46,4 @@ const show = ref(false)
46
46
  z-index: 1;
47
47
  }
48
48
  </style>
49
+ ../../../types.js
@@ -12,12 +12,12 @@ export default {
12
12
  required: true
13
13
  },
14
14
  node: {
15
- /** @type import('vue').PropType<import('../types.js').VjsfNode> */
15
+ /** @type import('vue').PropType<import('../../types.js').VjsfNode> */
16
16
  type: Object,
17
17
  required: true
18
18
  },
19
19
  statefulLayout: {
20
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
20
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
21
21
  type: Object,
22
22
  required: true
23
23
  },
@@ -38,7 +38,7 @@ export default {
38
38
  }
39
39
  if (isNameSlot(this.layoutSlot)) {
40
40
  if (!this.statefulLayout.options.vjsfSlots[this.layoutSlot.name]) {
41
- console.error(`layout references a code slot "${this.layoutSlot.name}" that was not provided.`)
41
+ console.error(`vjsf: layout references a code slot "${this.layoutSlot.name}" that was not provided.`)
42
42
  } else {
43
43
  return h(renderTag, this.statefulLayout.options.vjsfSlots[this.layoutSlot.name]({ node: this.node, statefulLayout: this.statefulLayout }))
44
44
  }
@@ -4,7 +4,7 @@ import { computed } from 'vue'
4
4
 
5
5
  const props = defineProps({
6
6
  node: {
7
- /** @type import('vue').PropType<import('../types.js').VjsfNode> */
7
+ /** @type import('vue').PropType<import('../../types.js').VjsfNode> */
8
8
  type: Object,
9
9
  required: true
10
10
  }
@@ -53,3 +53,4 @@ const titleClass = computed(() => {
53
53
  </v-alert>
54
54
  </div>
55
55
  </template>
56
+ ../../../types.js
@@ -1,16 +1,16 @@
1
1
  <script setup>
2
2
  import { VMenu, VTextField } from 'vuetify/components'
3
3
  import { computed, ref } from 'vue'
4
- import { getCompProps, getInputProps } from '../../utils/props.js'
4
+ import { getCompProps, getInputProps } from '../../utils/index.js'
5
5
 
6
6
  const props = defineProps({
7
7
  modelValue: {
8
- /** @type import('vue').PropType<import('../types.js').VjsfNode> */
8
+ /** @type import('vue').PropType<import('../../types.js').VjsfNode> */
9
9
  type: Object,
10
10
  required: true
11
11
  },
12
12
  statefulLayout: {
13
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
13
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
14
14
  type: Object,
15
15
  required: true
16
16
  },
@@ -1,33 +1,50 @@
1
1
  <script setup>
2
+ import { computed } from 'vue'
3
+ import { useTheme } from 'vuetify'
2
4
  import { VCol } from 'vuetify/components'
3
5
  import NodeSlot from './fragments/node-slot.vue'
4
6
  import HelpMessage from './fragments/help-message.vue'
5
7
 
6
- defineProps({
8
+ const props = defineProps({
7
9
  modelValue: {
8
- /** @type import('vue').PropType<import('./types.js').VjsfNode> */
10
+ /** @type import('vue').PropType<import('../types.js').VjsfNode> */
9
11
  type: Object,
10
12
  required: true
11
13
  },
12
14
  statefulLayout: {
13
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
15
+ /** @type import('vue').PropType<import('../types.js').VjsfStatefulLayout> */
14
16
  type: Object,
15
17
  required: true
16
18
  }
17
19
  })
18
20
 
19
- /** @type Record<import('./types.js').Density, string> */
21
+ /** @type Record<import('../types.js').Density, string> */
20
22
  const beforeAfterClasses = {
21
23
  compact: 'my-1',
22
24
  comfortable: 'my-2',
23
25
  default: 'my-3'
24
26
  }
27
+
28
+ const theme = useTheme()
29
+
30
+ const nodeClasses = computed(() => {
31
+ let classes = `vjsf-node vjsf-node-${props.modelValue.layout.comp} vjsf-density-${props.modelValue.options.density}`
32
+ if (props.modelValue.options.readOnly) classes += ' vjsf-readonly'
33
+ if (props.modelValue.options.summary) classes += ' vjsf-summary'
34
+ if (theme.current.value.dark) classes += ' vjsf-dark'
35
+ return classes
36
+ })
37
+
38
+ if (!props.statefulLayout.options.nodeComponents[props.modelValue.layout.comp]) {
39
+ console.error(`vjsf: missing component to render vjsf node "${props.modelValue.layout.comp}", maybe you forgot to register a component from a plugin ?`)
40
+ }
41
+
25
42
  </script>
26
43
 
27
44
  <template>
28
45
  <v-col
29
46
  :cols="modelValue.cols"
30
- :class="`vjsf-node vjsf-node-${modelValue.layout.comp}`"
47
+ :class="nodeClasses"
31
48
  >
32
49
  <node-slot
33
50
  v-if="modelValue.layout.slots?.before"
@@ -35,13 +52,13 @@ const beforeAfterClasses = {
35
52
  :layout-slot="modelValue.layout.slots?.before"
36
53
  :node="modelValue"
37
54
  :stateful-layout="statefulLayout"
38
- :class="beforeAfterClasses[/** @type import('./types.js').VjsfOptions */(modelValue.options).density]"
55
+ :class="beforeAfterClasses[modelValue.options.density]"
39
56
  />
40
57
 
41
58
  <help-message
42
59
  v-if="modelValue.layout.help"
43
60
  :node="modelValue"
44
- :class="beforeAfterClasses[/** @type import('./types.js').VjsfOptions */(modelValue.options).density]"
61
+ :class="beforeAfterClasses[modelValue.options.density]"
45
62
  />
46
63
  <node-slot
47
64
  v-if="modelValue.layout.slots?.component"
@@ -51,8 +68,8 @@ const beforeAfterClasses = {
51
68
  :stateful-layout="statefulLayout"
52
69
  />
53
70
  <component
54
- :is="`vjsf-node-${modelValue.layout.comp}`"
55
- v-else-if="modelValue.layout.comp !== 'none'"
71
+ :is="props.statefulLayout.options.nodeComponents[modelValue.layout.comp]"
72
+ v-else-if="modelValue.layout.comp !== 'none' "
56
73
  :model-value="modelValue"
57
74
  :stateful-layout="statefulLayout"
58
75
  />
@@ -63,7 +80,7 @@ const beforeAfterClasses = {
63
80
  :layout-slot="modelValue.layout.slots?.after"
64
81
  :node="modelValue"
65
82
  :stateful-layout="statefulLayout"
66
- :class="beforeAfterClasses[/** @type import('./types.js').VjsfOptions */(modelValue.options).density]"
83
+ :class="beforeAfterClasses[modelValue.options.density]"
67
84
  />
68
85
  </v-col>
69
86
  </template>
@@ -1,20 +1,19 @@
1
1
  <script>
2
2
  import { VAutocomplete } from 'vuetify/components'
3
3
  import { defineComponent, computed, ref, shallowRef, h } from 'vue'
4
- import { getInputProps } from '../../utils/props.js'
5
- import { getCompSlots } from '../../utils/slots.js'
4
+ import { getInputProps, getCompSlots } from '../../utils/index.js'
6
5
  import SelectItem from '../fragments/select-item.vue'
7
6
  import SelectSelection from '../fragments/select-selection.vue'
8
7
 
9
8
  export default defineComponent({
10
9
  props: {
11
10
  modelValue: {
12
- /** @type import('vue').PropType<import('../types.js').VjsfSelectNode> */
11
+ /** @type import('vue').PropType<import('../../types.js').VjsfSelectNode> */
13
12
  type: Object,
14
13
  required: true
15
14
  },
16
15
  statefulLayout: {
17
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
16
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
18
17
  type: Object,
19
18
  required: true
20
19
  }
@@ -87,9 +86,3 @@ export default defineComponent({
87
86
  })
88
87
 
89
88
  </script>
90
-
91
- <template>
92
- <v-select
93
- v-bind="fieldProps"
94
- />
95
- </template>
@@ -1,16 +1,16 @@
1
1
  <script setup>
2
2
  import { VCheckbox } from 'vuetify/components'
3
3
  import { computed } from 'vue'
4
- import { getInputProps } from '../../utils/props.js'
4
+ import { getInputProps } from '../../utils/index.js'
5
5
 
6
6
  const props = defineProps({
7
7
  modelValue: {
8
- /** @type import('vue').PropType<import('../types.js').VjsfCheckboxNode> */
8
+ /** @type import('vue').PropType<import('../../types.js').VjsfCheckboxNode> */
9
9
  type: Object,
10
10
  required: true
11
11
  },
12
12
  statefulLayout: {
13
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
13
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
14
14
  type: Object,
15
15
  required: true
16
16
  }
@@ -2,16 +2,16 @@
2
2
  import TextFieldMenu from '../fragments/text-field-menu.vue'
3
3
  import { VColorPicker } from 'vuetify/components'
4
4
  import { computed } from 'vue'
5
- import { getCompProps } from '../../utils/props.js'
5
+ import { getCompProps } from '../../utils/index.js'
6
6
 
7
7
  const props = defineProps({
8
8
  modelValue: {
9
- /** @type import('vue').PropType<import('../types.js').VjsfColorPickerNode> */
9
+ /** @type import('vue').PropType<import('../../types.js').VjsfColorPickerNode> */
10
10
  type: Object,
11
11
  required: true
12
12
  },
13
13
  statefulLayout: {
14
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
14
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
15
15
  type: Object,
16
16
  required: true
17
17
  }
@@ -1,18 +1,17 @@
1
1
  <script>
2
2
  import { defineComponent, h, computed, shallowRef, ref } from 'vue'
3
3
  import { VCombobox } from 'vuetify/components'
4
- import { getInputProps } from '../../utils/props.js'
5
- import { getCompSlots } from '../../utils/slots.js'
4
+ import { getInputProps, getCompSlots } from '../../utils/index.js'
6
5
 
7
6
  export default defineComponent({
8
7
  props: {
9
8
  modelValue: {
10
- /** @type import('vue').PropType<import('../types.js').VjsfComboboxNode> */
9
+ /** @type import('vue').PropType<import('../../types.js').VjsfComboboxNode> */
11
10
  type: Object,
12
11
  required: true
13
12
  },
14
13
  statefulLayout: {
15
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
14
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
16
15
  type: Object,
17
16
  required: true
18
17
  }
@@ -3,17 +3,16 @@ import TextFieldMenu from '../fragments/text-field-menu.vue'
3
3
  import { VDatePicker } from 'vuetify/components/VDatePicker'
4
4
  import { useDate } from 'vuetify'
5
5
  import { computed } from 'vue'
6
- import { getCompProps } from '../../utils/props.js'
7
- import { getDateTimeParts } from '../../utils/dates.js'
6
+ import { getCompProps, getDateTimeParts } from '../../utils/index.js'
8
7
 
9
8
  const props = defineProps({
10
9
  modelValue: {
11
- /** @type import('vue').PropType<import('../types.js').VjsfDatePickerNode> */
10
+ /** @type import('vue').PropType<import('../../types.js').VjsfDatePickerNode> */
12
11
  type: Object,
13
12
  required: true
14
13
  },
15
14
  statefulLayout: {
16
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
15
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
17
16
  type: Object,
18
17
  required: true
19
18
  }
@@ -2,12 +2,12 @@
2
2
 
3
3
  defineProps({
4
4
  modelValue: {
5
- /** @type import('vue').PropType<import('../types.js').VjsfDateTimePickerNode> */
5
+ /** @type import('vue').PropType<import('../../types.js').VjsfDateTimePickerNode> */
6
6
  type: Object,
7
7
  required: true
8
8
  },
9
9
  statefulLayout: {
10
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
10
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
11
11
  type: Object,
12
12
  required: true
13
13
  }
@@ -1,17 +1,17 @@
1
1
  <script setup>
2
- import { VExpansionPanels, VExpansionPanel, VExpansionPanelTitle, VContainer } from 'vuetify/components'
2
+ import { VExpansionPanels, VExpansionPanel, VExpansionPanelTitle, VContainer, VRow, VIcon } from 'vuetify/components'
3
3
  import { isSection } from '@json-layout/core'
4
4
  import Node from '../node.vue'
5
5
  import SectionHeader from '../fragments/section-header.vue'
6
6
 
7
7
  defineProps({
8
8
  modelValue: {
9
- /** @type import('vue').PropType<import('../types.js').VjsfExpansionPanelsNode> */
9
+ /** @type import('vue').PropType<import('../../types.js').VjsfExpansionPanelsNode> */
10
10
  type: Object,
11
11
  required: true
12
12
  },
13
13
  statefulLayout: {
14
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
14
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
15
15
  type: Object,
16
16
  required: true
17
17
  }
@@ -42,7 +42,7 @@ defineProps({
42
42
  <node
43
43
  v-for="grandChild of isSection(child) ? child.children : [child]"
44
44
  :key="grandChild.fullKey"
45
- :model-value="/** @type import('../types.js').VjsfNode */(grandChild)"
45
+ :model-value="/** @type import('../../types.js').VjsfNode */(grandChild)"
46
46
  :stateful-layout="statefulLayout"
47
47
  />
48
48
  </v-row>
@@ -0,0 +1,43 @@
1
+ <script>
2
+ import { defineComponent, h, computed } from 'vue'
3
+ import { VFileInput } from 'vuetify/components'
4
+ import { getInputProps, getCompSlots } from '../../utils/index.js'
5
+
6
+ export default defineComponent({
7
+ props: {
8
+ modelValue: {
9
+ /** @type import('vue').PropType<import('../../types.js').VjsfFileInputNode> */
10
+ type: Object,
11
+ required: true
12
+ },
13
+ statefulLayout: {
14
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
15
+ type: Object,
16
+ required: true
17
+ }
18
+ },
19
+ setup (props) {
20
+ const fieldProps = computed(() => {
21
+ const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['placeholder', 'accept'])
22
+ if (props.modelValue.layout.multiple) {
23
+ fieldProps.multiple = true
24
+ } else {
25
+ fieldProps.modelValue = props.modelValue.data ? [props.modelValue.data] : props.modelValue.data
26
+ fieldProps['onUpdate:modelValue'] = (/** @type string */value) => props.statefulLayout.input(props.modelValue, Array.isArray(value) ? value[0] : value)
27
+ }
28
+ return fieldProps
29
+ })
30
+ const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
31
+
32
+ // @ts-ignore
33
+ return () => h(VFileInput, fieldProps.value, fieldSlots.value)
34
+ }
35
+ })
36
+
37
+ </script>
38
+
39
+ <style>
40
+ .vjsf-node-text-field.vjsf-readonly.vjsf-summary input {
41
+ text-overflow: ellipsis;
42
+ }
43
+ </style>
@@ -1,26 +1,26 @@
1
1
  <script setup>
2
2
  import { watch, computed, ref } from 'vue'
3
- import { VList, VListItem, VListItemAction, VBtn, VMenu, VIcon } from 'vuetify/components'
3
+ import { VList, VListItem, VListItemAction, VBtn, VMenu, VIcon, VSheet, VSpacer, VDivider, VRow, VListSubheader } from 'vuetify/components'
4
4
  import { isSection, clone } from '@json-layout/core'
5
5
  import Node from '../node.vue'
6
- import { moveArrayItem } from '../../utils/arrays.js'
6
+ import { moveArrayItem } from '../../utils/index.js'
7
7
  import useDnd from '../../composables/use-dnd.js'
8
8
 
9
9
  const props = defineProps({
10
10
  modelValue: {
11
- /** @type import('vue').PropType<import('../types.js').VjsfListNode> */
11
+ /** @type import('vue').PropType<import('../../types.js').VjsfListNode> */
12
12
  type: Object,
13
13
  required: true
14
14
  },
15
15
  statefulLayout: {
16
- /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
16
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
17
17
  type: Object,
18
18
  required: true
19
19
  }
20
20
  })
21
21
 
22
22
  /* use composable for drag and drop */
23
- const { activeDnd, sortableArray, draggable, itemBind, handleBind } = useDnd(props.modelValue.children, () => {
23
+ const { activeDnd, sortableArray, draggable, hovered, dragging, itemBind, handleBind } = useDnd(props.modelValue.children, () => {
24
24
  props.statefulLayout.input(props.modelValue, sortableArray.value.map((child) => child.data))
25
25
  })
26
26
  watch(() => props.modelValue.children, (array) => { sortableArray.value = array })
@@ -29,7 +29,7 @@ watch(() => props.modelValue.children, (array) => { sortableArray.value = array
29
29
  const editedItem = computed(() => {
30
30
  return props.statefulLayout.activeItems[props.modelValue.fullKey]
31
31
  })
32
- const hoveredItem = ref(1)
32
+ const menuOpened = ref(-1)
33
33
  const activeItem = computed(() => {
34
34
  if (
35
35
  props.modelValue.layout.listActions.includes('edit') &&
@@ -38,7 +38,9 @@ const activeItem = computed(() => {
38
38
  ) {
39
39
  return editedItem.value
40
40
  }
41
- return hoveredItem.value
41
+ if (dragging.value !== -1) return -1
42
+ if (menuOpened.value !== -1) return menuOpened.value
43
+ return hovered.value
42
44
  })
43
45
 
44
46
  const buttonDensity = computed(() => {
@@ -64,14 +66,12 @@ const buttonDensity = computed(() => {
64
66
  :draggable="draggable === childIndex"
65
67
  :variant="editedItem === childIndex ? 'outlined' : 'flat'"
66
68
  class="pa-1 vjsf-list-item"
67
- @mouseenter="hoveredItem = childIndex"
68
- @mouseleave="hoveredItem = -1"
69
69
  >
70
70
  <v-row class="ma-0">
71
71
  <node
72
72
  v-for="grandChild of isSection(child) ? child.children : [child]"
73
73
  :key="grandChild.fullKey"
74
- :model-value="/** @type import('../types.js').VjsfNode */(grandChild)"
74
+ :model-value="/** @type import('../../types.js').VjsfNode */(grandChild)"
75
75
  :stateful-layout="statefulLayout"
76
76
  />
77
77
  </v-row>
@@ -116,7 +116,10 @@ const buttonDensity = computed(() => {
116
116
  <v-list-item-action
117
117
  v-if="editedItem === undefined && (modelValue.layout.listActions.includes('delete') || modelValue.layout.listActions.includes('duplicate') || modelValue.layout.listActions.includes('sort'))"
118
118
  >
119
- <v-menu location="bottom end">
119
+ <v-menu
120
+ location="bottom end"
121
+ @update:model-value="value => {menuOpened = value ? childIndex : -1}"
122
+ >
120
123
  <template #activator="{props: activatorProps}">
121
124
  <v-btn
122
125
  v-bind="activatorProps"