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

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
@@ -0,0 +1,105 @@
1
+ <script>
2
+ import { VSkeletonLoader } from 'vuetify/components/VSkeletonLoader'
3
+ import { VInput } from 'vuetify/components/VInput'
4
+ import { VLabel } from 'vuetify/components/VLabel'
5
+ import { VCheckbox } from 'vuetify/components/VCheckbox'
6
+ import { VSwitch } from 'vuetify/components/VSwitch'
7
+ import { defineComponent, h, computed, toRef } from 'vue'
8
+ import useField from '../../composables/use-node.js'
9
+ import useGetItems from '../../composables/use-get-items.js'
10
+
11
+ export default defineComponent({
12
+ props: {
13
+ modelValue: {
14
+ /** @type import('vue').PropType<import('../../types.js').VjsfCheckboxGroupNode> */
15
+ type: Object,
16
+ required: true
17
+ },
18
+ statefulLayout: {
19
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
20
+ type: Object,
21
+ required: true
22
+ },
23
+ type: {
24
+ type: String,
25
+ required: true
26
+ }
27
+ },
28
+ setup (props) {
29
+ const nodeRef = toRef(props, 'modelValue')
30
+ const getItems = useGetItems(nodeRef, props.statefulLayout)
31
+ const { inputProps, compSlots, localData, layout } = useField(nodeRef, props.statefulLayout, { bindData: false })
32
+
33
+ const fieldProps = computed(() => {
34
+ const fieldProps = { ...inputProps.value }
35
+ fieldProps.class.push('v-radio-group') // reuse some styles from radio-group
36
+ fieldProps.class.push('vjsf-selection-group')
37
+ return fieldProps
38
+ })
39
+
40
+ const fieldSlots = computed(() => {
41
+ const slots = { ...compSlots.value }
42
+
43
+ if (!slots.default) {
44
+ slots.default = () => {
45
+ /** @type {import('vue').VNode[]} */
46
+ const children = [h(VLabel, { text: fieldProps.value.label })]
47
+ if (getItems.loading.value) {
48
+ children.push(h(VSkeletonLoader, { type: 'chip' }))
49
+ } else {
50
+ /** @type {import('vue').VNode[]} */
51
+ const checkboxes = []
52
+ for (const item of getItems.items.value) {
53
+ let modelValue = false
54
+ if (layout.value.multiple) {
55
+ modelValue = localData.value?.includes(item.value)
56
+ } else {
57
+ modelValue = localData.value === item.value
58
+ }
59
+ checkboxes.push(h(props.type === 'switch' ? VSwitch : VCheckbox, {
60
+ label: item.title,
61
+ hideDetails: true,
62
+ key: item.key,
63
+ modelValue,
64
+ onClick: () => {
65
+ let newValue
66
+ if (layout.value.multiple) {
67
+ newValue = props.modelValue.data ? [...props.modelValue.data] : []
68
+ if (newValue.includes(item.value)) {
69
+ newValue = newValue.filter((/** @type {any} */v) => v !== item.value)
70
+ } else {
71
+ newValue.push(item.value)
72
+ }
73
+ } else {
74
+ if (props.modelValue.data === item.value) {
75
+ newValue = undefined
76
+ } else {
77
+ newValue = item.value
78
+ }
79
+ }
80
+ props.statefulLayout.input(props.modelValue, newValue)
81
+ }
82
+ }))
83
+ }
84
+ children.push(h('div', { class: 'v-selection-control-group' }, checkboxes))
85
+ }
86
+ return children
87
+ }
88
+ }
89
+
90
+ return slots
91
+ })
92
+
93
+ return () => {
94
+ return h(VInput, fieldProps.value, fieldSlots.value)
95
+ }
96
+ }
97
+ })
98
+
99
+ </script>
100
+
101
+ <style>
102
+ .vjsf-selection-group .v-selection-control-group>.v-input .v-selection-control {
103
+ min-height: auto;
104
+ }
105
+ </style>
@@ -1,7 +1,8 @@
1
1
  <script setup>
2
- import { VMenu, VTextField } from 'vuetify/components'
3
- import { computed, ref } from 'vue'
4
- import { getCompProps, getInputProps } from '../../utils/index.js'
2
+ import { VMenu } from 'vuetify/components/VMenu'
3
+ import { VTextField } from 'vuetify/components/VTextField'
4
+ import { computed, ref, toRef } from 'vue'
5
+ import useField from '../../composables/use-node.js'
5
6
 
6
7
  const props = defineProps({
7
8
  modelValue: {
@@ -21,21 +22,29 @@ const props = defineProps({
21
22
  }
22
23
  })
23
24
 
25
+ const { inputProps, skeleton, compProps, data } = useField(
26
+ toRef(props, 'modelValue'), props.statefulLayout, { isMainComp: false, bindData: false }
27
+ )
28
+
24
29
  const fieldProps = computed(() => {
25
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout, [], false)
30
+ const fieldProps = { ...inputProps.value }
26
31
  fieldProps.readonly = true
32
+ fieldProps.clearable = fieldProps.clearable ?? !skeleton.value.required
33
+ fieldProps['onClick:clear'] = () => {
34
+ props.statefulLayout.input(props.modelValue, null)
35
+ }
27
36
  return fieldProps
28
37
  })
29
38
 
30
39
  const menuProps = computed(() => {
31
- const menuProps = getCompProps(props.modelValue, 'menu', false)
40
+ const menuProps = { ...compProps.value }
32
41
  menuProps.closeOnContentClick = false
33
42
  menuProps.disabled = true
34
43
  return menuProps
35
44
  })
36
45
 
37
46
  const textField = ref(null)
38
- const menuOpened = ref(false)
47
+ const menuOpened = defineModel('menuOpened', { type: Boolean, default: false })
39
48
 
40
49
  </script>
41
50
 
@@ -43,7 +52,7 @@ const menuOpened = ref(false)
43
52
  <v-text-field
44
53
  ref="textField"
45
54
  v-bind="fieldProps"
46
- :model-value="formattedValue ?? modelValue.data"
55
+ :model-value="formattedValue ?? data"
47
56
  @click:control="e => {menuOpened = !menuOpened; e.stopPropagation()}"
48
57
  >
49
58
  <template #prepend-inner>
@@ -1,10 +1,13 @@
1
1
  <script setup>
2
2
  import { computed } from 'vue'
3
- import { useTheme } from 'vuetify'
4
- import { VCol } from 'vuetify/components'
3
+ import { useTheme, useDefaults } from 'vuetify'
4
+ import { VCol } from 'vuetify/components/VGrid'
5
+ import { VDefaultsProvider } from 'vuetify/components/VDefaultsProvider'
5
6
  import NodeSlot from './fragments/node-slot.vue'
6
7
  import HelpMessage from './fragments/help-message.vue'
7
8
 
9
+ useDefaults({}, 'VjsfNode')
10
+
8
11
  const props = defineProps({
9
12
  modelValue: {
10
13
  /** @type import('vue').PropType<import('../types.js').VjsfNode> */
@@ -27,11 +30,23 @@ const beforeAfterClasses = {
27
30
 
28
31
  const theme = useTheme()
29
32
 
33
+ const indent = computed(() => {
34
+ if (props.modelValue.parentFullKey === null) return 0
35
+ if (!props.modelValue.options.indent) return 0
36
+ if (props.modelValue.layout.comp !== 'section') return 0
37
+ if (!props.modelValue.layout.title) return 0
38
+ if (typeof props.modelValue.options.indent === 'number') return props.modelValue.options.indent
39
+ if (props.modelValue.options.density === 'compact') return 2
40
+ if (props.modelValue.options.density === 'comfortable') return 4
41
+ return 6
42
+ })
43
+
30
44
  const nodeClasses = computed(() => {
31
45
  let classes = `vjsf-node vjsf-node-${props.modelValue.layout.comp} vjsf-density-${props.modelValue.options.density}`
32
46
  if (props.modelValue.options.readOnly) classes += ' vjsf-readonly'
33
47
  if (props.modelValue.options.summary) classes += ' vjsf-summary'
34
48
  if (theme.current.value.dark) classes += ' vjsf-dark'
49
+ if (indent.value) classes += ' ml-' + indent.value
35
50
  return classes
36
51
  })
37
52
 
@@ -42,45 +57,47 @@ if (props.modelValue.layout.comp !== 'none' && !props.statefulLayout.options.nod
42
57
  </script>
43
58
 
44
59
  <template>
45
- <v-col
46
- :cols="modelValue.cols"
47
- :class="nodeClasses"
48
- >
49
- <node-slot
50
- v-if="modelValue.layout.slots?.before"
51
- key="before"
52
- :layout-slot="modelValue.layout.slots?.before"
53
- :node="modelValue"
54
- :stateful-layout="statefulLayout"
55
- :class="beforeAfterClasses[modelValue.options.density]"
56
- />
60
+ <v-defaults-provider :defaults="{global: { density: props.modelValue.options.density }}">
61
+ <v-col
62
+ v-if="modelValue.layout.comp !== 'none'"
63
+ :cols="modelValue.cols"
64
+ :class="nodeClasses"
65
+ >
66
+ <node-slot
67
+ v-if="modelValue.layout.slots?.before"
68
+ key="before"
69
+ :layout-slot="modelValue.layout.slots?.before"
70
+ :node="modelValue"
71
+ :stateful-layout="statefulLayout"
72
+ :class="beforeAfterClasses[modelValue.options.density]"
73
+ />
57
74
 
58
- <help-message
59
- v-if="modelValue.layout.help"
60
- :node="modelValue"
61
- :class="beforeAfterClasses[modelValue.options.density]"
62
- />
63
- <node-slot
64
- v-if="modelValue.layout.slots?.component"
65
- key="component"
66
- :layout-slot="modelValue.layout.slots?.component"
67
- :node="modelValue"
68
- :stateful-layout="statefulLayout"
69
- />
70
- <component
71
- :is="props.statefulLayout.options.nodeComponents[modelValue.layout.comp]"
72
- v-else-if="modelValue.layout.comp !== 'none' "
73
- :model-value="modelValue"
74
- :stateful-layout="statefulLayout"
75
- />
75
+ <help-message
76
+ v-if="modelValue.layout.help && !modelValue.options.summary"
77
+ :node="modelValue"
78
+ />
79
+ <node-slot
80
+ v-if="modelValue.layout.slots?.component"
81
+ key="component"
82
+ :layout-slot="modelValue.layout.slots?.component"
83
+ :node="modelValue"
84
+ :stateful-layout="statefulLayout"
85
+ />
86
+ <component
87
+ :is="props.statefulLayout.options.nodeComponents[modelValue.layout.comp]"
88
+ v-else
89
+ :model-value="modelValue"
90
+ :stateful-layout="statefulLayout"
91
+ />
76
92
 
77
- <node-slot
78
- v-if="modelValue.layout.slots?.after"
79
- key="after"
80
- :layout-slot="modelValue.layout.slots?.after"
81
- :node="modelValue"
82
- :stateful-layout="statefulLayout"
83
- :class="beforeAfterClasses[modelValue.options.density]"
84
- />
85
- </v-col>
93
+ <node-slot
94
+ v-if="modelValue.layout.slots?.after"
95
+ key="after"
96
+ :layout-slot="modelValue.layout.slots?.after"
97
+ :node="modelValue"
98
+ :stateful-layout="statefulLayout"
99
+ :class="beforeAfterClasses[modelValue.options.density]"
100
+ />
101
+ </v-col>
102
+ </v-defaults-provider>
86
103
  </template>
@@ -1,9 +1,8 @@
1
1
  <script>
2
- import { VAutocomplete } from 'vuetify/components'
3
- import { defineComponent, computed, ref, shallowRef, h } from 'vue'
4
- import { getInputProps, getCompSlots } from '../../utils/index.js'
5
- import SelectItem from '../fragments/select-item.vue'
6
- import SelectSelection from '../fragments/select-selection.vue'
2
+ import { VAutocomplete } from 'vuetify/components/VAutocomplete'
3
+ import { useDefaults } from 'vuetify'
4
+ import { defineComponent, computed, h, toRef } from 'vue'
5
+ import useSelectNode from '../../composables/use-select-node.js'
7
6
 
8
7
  export default defineComponent({
9
8
  props: {
@@ -13,75 +12,30 @@ export default defineComponent({
13
12
  required: true
14
13
  },
15
14
  statefulLayout: {
16
- /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
15
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
17
16
  type: Object,
18
17
  required: true
19
18
  }
20
19
  },
21
20
  setup (props) {
22
- /** @type import('vue').ShallowRef<import('@json-layout/vocabulary').SelectItems> */
23
- const items = shallowRef([])
24
- /** @type import('vue').Ref<boolean> */
25
- const loading = ref(false)
26
- /** @type import('vue').Ref<string> */
27
- const search = ref('')
21
+ useDefaults({}, 'VjsfAutocomplete')
22
+
23
+ const { getItems, selectProps, selectSlots, localData } = useSelectNode(toRef(props, 'modelValue'), props.statefulLayout)
28
24
 
29
25
  const fieldProps = computed(() => {
30
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['multiple'])
31
- if (props.modelValue.options.readOnly) fieldProps.menuProps = { modelValue: false }
26
+ const fieldProps = { ...selectProps.value }
32
27
  fieldProps.noFilter = true
33
28
  fieldProps['onUpdate:search'] = (/** @type string */searchValue) => {
34
- search.value = searchValue
35
- refresh()
29
+ getItems.search.value = searchValue
36
30
  }
37
- fieldProps['onUpdate:menu'] = refresh
38
- fieldProps.items = items.value
39
- fieldProps.loading = loading.value
31
+ fieldProps.items = getItems.items.value
32
+ fieldProps.loading = getItems.loading.value
33
+ fieldProps.modelValue = localData.value
40
34
  return fieldProps
41
35
  })
42
36
 
43
- /** @type import('@json-layout/core').StateTree | null */
44
- let lastStateTree = null
45
- /** @type Record<string, any> | null */
46
- let lastContext = null
47
- /** @type string */
48
- let lastSearch = ''
49
-
50
- const refresh = async () => {
51
- if (props.statefulLayout.stateTree === lastStateTree && props.statefulLayout.options.context === lastContext && search.value === lastSearch) return
52
- loading.value = true
53
- items.value = await props.statefulLayout.getItems(props.modelValue, search.value)
54
- lastStateTree = props.statefulLayout.stateTree
55
- lastContext = props.statefulLayout.options.context ?? null
56
- lastSearch = search.value
57
- loading.value = false
58
- }
59
-
60
- if (!props.modelValue.layout.items) {
61
- refresh()
62
- }
63
-
64
- const fieldSlots = computed(() => {
65
- const slots = getCompSlots(props.modelValue, props.statefulLayout)
66
- if (!slots.item) {
67
- slots.item = (/** @type {any} */ context) => h(SelectItem, {
68
- multiple: props.modelValue.layout.multiple,
69
- itemProps: context.props,
70
- item: context.item.raw
71
- })
72
- }
73
- if (!slots.selection) {
74
- slots.selection = (/** @type {any} */ context) => h(SelectSelection, {
75
- multiple: props.modelValue.layout.multiple,
76
- last: props.modelValue.layout.multiple && context.index === props.modelValue.data.length - 1,
77
- item: context.item.raw
78
- })
79
- }
80
- return slots
81
- })
82
-
83
37
  // @ts-ignore
84
- return () => h(VAutocomplete, fieldProps.value, fieldSlots.value)
38
+ return () => h(VAutocomplete, fieldProps.value, selectSlots.value)
85
39
  }
86
40
  })
87
41
 
@@ -0,0 +1,39 @@
1
+ <script setup>
2
+ import { VCard } from 'vuetify/components/VCard'
3
+ import { VContainer, VRow } from 'vuetify/components/VGrid'
4
+ import Node from '../node.vue'
5
+ import SectionHeader from '../fragments/section-header.vue'
6
+ import { useDefaults } from 'vuetify'
7
+
8
+ useDefaults({}, 'VjsfCard')
9
+
10
+ defineProps({
11
+ modelValue: {
12
+ /** @type import('vue').PropType<import('../../types.js').VjsfCardNode> */
13
+ type: Object,
14
+ required: true
15
+ },
16
+ statefulLayout: {
17
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
18
+ type: Object,
19
+ required: true
20
+ }
21
+ })
22
+
23
+ </script>
24
+
25
+ <template>
26
+ <v-card :title="modelValue.layout.title ?? ''">
27
+ <v-container fluid>
28
+ <section-header :node="modelValue" hide-title />
29
+ <v-row :dense="modelValue.options?.density === 'compact' || modelValue.options?.density === 'comfortable'">
30
+ <node
31
+ v-for="child of modelValue.children"
32
+ :key="child.fullKey"
33
+ :model-value="/** @type import('../../types.js').VjsfNode */(child)"
34
+ :stateful-layout="statefulLayout"
35
+ />
36
+ </v-row>
37
+ </v-container>
38
+ </v-card>
39
+ </template>
@@ -0,0 +1,39 @@
1
+ <script>
2
+ import { useDefaults } from 'vuetify'
3
+ import SelectionGroup from '../fragments/selection-group.vue'
4
+ import { defineComponent, h } from 'vue'
5
+
6
+ export default defineComponent({
7
+ props: {
8
+ modelValue: {
9
+ /** @type import('vue').PropType<import('../../types.js').VjsfCheckboxGroupNode> */
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
+ useDefaults({}, 'VjsfCheckboxGroup')
21
+
22
+ // @ts-ignore
23
+ return () => {
24
+ return h(SelectionGroup, {
25
+ modelValue: props.modelValue,
26
+ statefulLayout: props.statefulLayout,
27
+ type: 'checkbox'
28
+ })
29
+ }
30
+ }
31
+ })
32
+
33
+ </script>
34
+
35
+ <style>
36
+ .vjsf-node-checkbox-group .v-selection-control-group .v-checkbox .v-selection-control {
37
+ min-height: auto;
38
+ }
39
+ </style>
@@ -1,32 +1,37 @@
1
- <script setup>
2
- import { VCheckbox } from 'vuetify/components'
3
- import { computed } from 'vue'
4
- import { getInputProps } from '../../utils/index.js'
1
+ <script>
2
+ import { defineComponent, h, computed, toRef } from 'vue'
3
+ import { VCheckbox } from 'vuetify/components/VCheckbox'
4
+ import useNode from '../../composables/use-node.js'
5
+ import { useDefaults } from 'vuetify'
5
6
 
6
- const props = defineProps({
7
- modelValue: {
8
- /** @type import('vue').PropType<import('../../types.js').VjsfCheckboxNode> */
9
- type: Object,
10
- required: true
7
+ export default defineComponent({
8
+ props: {
9
+ modelValue: {
10
+ /** @type import('vue').PropType<import('../../types.js').VjsfCheckboxNode> */
11
+ type: Object,
12
+ required: true
13
+ },
14
+ statefulLayout: {
15
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
16
+ type: Object,
17
+ required: true
18
+ }
11
19
  },
12
- statefulLayout: {
13
- /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
14
- type: Object,
15
- required: true
20
+ setup (props) {
21
+ useDefaults({}, 'VjsfCheckbox')
22
+
23
+ const { inputProps, localData, compSlots } = useNode(toRef(props, 'modelValue'), props.statefulLayout)
24
+
25
+ const fullProps = computed(() => {
26
+ const fullProps = { ...inputProps.value }
27
+ // it is not very common to show an error below checkboxes and switches and without hide-details=auto they take a lot of space
28
+ if (!('hideDetails' in inputProps)) fullProps.hideDetails = 'auto'
29
+ fullProps.modelValue = localData.value
30
+ return fullProps
31
+ })
32
+
33
+ return () => h(VCheckbox, fullProps.value, compSlots.value)
16
34
  }
17
35
  })
18
36
 
19
- const fieldProps = computed(() => {
20
- const inputProps = getInputProps(props.modelValue, props.statefulLayout)
21
- // it is not very common to show an error below checkboxes and switches and without hide-details=auto they take a lot of space
22
- if (!('hideDetails' in inputProps)) inputProps.hideDetails = 'auto'
23
- return inputProps
24
- })
25
37
  </script>
26
-
27
- <template>
28
- <v-checkbox
29
- v-bind="fieldProps"
30
- @update:model-value="value => statefulLayout.input(modelValue, value)"
31
- />
32
- </template>
@@ -1,8 +1,11 @@
1
1
  <script setup>
2
2
  import TextFieldMenu from '../fragments/text-field-menu.vue'
3
- import { VColorPicker } from 'vuetify/components'
4
- import { computed } from 'vue'
5
- import { getCompProps } from '../../utils/index.js'
3
+ import { VColorPicker } from 'vuetify/components/VColorPicker'
4
+ import { computed, toRef } from 'vue'
5
+ import useNode from '../../composables/use-node.js'
6
+ import { useDefaults } from 'vuetify'
7
+
8
+ useDefaults({}, 'VjsfColorPicker')
6
9
 
7
10
  const props = defineProps({
8
11
  modelValue: {
@@ -17,8 +20,11 @@ const props = defineProps({
17
20
  }
18
21
  })
19
22
 
23
+ const { compProps, localData } = useNode(toRef(props, 'modelValue'), props.statefulLayout)
24
+
20
25
  const colorPickerProps = computed(() => {
21
- const colorPickerProps = getCompProps(props.modelValue, 'colorPicker', true)
26
+ const colorPickerProps = { ...compProps.value }
27
+ colorPickerProps.modelValue = localData.value
22
28
  return colorPickerProps
23
29
  })
24
30
  </script>
@@ -1,7 +1,9 @@
1
1
  <script>
2
- import { defineComponent, h, computed, shallowRef, ref } from 'vue'
3
- import { VCombobox } from 'vuetify/components'
4
- import { getInputProps, getCompSlots } from '../../utils/index.js'
2
+ import { defineComponent, h, computed, toRef } from 'vue'
3
+ import { VCombobox } from 'vuetify/components/VCombobox'
4
+ import useNode from '../../composables/use-node.js'
5
+ import useGetItems from '../../composables/use-get-items.js'
6
+ import { useDefaults } from 'vuetify'
5
7
 
6
8
  export default defineComponent({
7
9
  props: {
@@ -17,55 +19,30 @@ export default defineComponent({
17
19
  }
18
20
  },
19
21
  setup (props) {
20
- /** @type import('vue').Ref<import('@json-layout/vocabulary').SelectItems> */
21
- const items = shallowRef(props.modelValue.layout.items ?? [])
22
- /** @type import('vue').Ref<boolean> */
23
- const loading = ref(false)
22
+ useDefaults({}, 'VjsfCombobox')
24
23
 
25
- /** @type import('@json-layout/core').StateTree | null */
26
- let lastStateTree = null
27
- /** @type Record<string, any> | null */
28
- let lastContext = null
29
-
30
- const hasItems = computed(() => {
31
- return !!(props.modelValue.layout.items || props.modelValue.layout.getItems)
32
- })
33
-
34
- const refresh = async () => {
35
- if (props.modelValue.layout.items) return
36
- if (props.statefulLayout.stateTree === lastStateTree && props.statefulLayout.options.context === lastContext) return
37
- lastStateTree = props.statefulLayout.stateTree
38
- lastContext = props.statefulLayout.options.context ?? null
39
- if (hasItems.value) {
40
- loading.value = true
41
- items.value = await props.statefulLayout.getItems(props.modelValue)
42
- loading.value = false
43
- }
44
- }
45
-
46
- if (!props.modelValue.layout.items) {
47
- refresh()
48
- }
24
+ const nodeRef = toRef(props, 'modelValue')
25
+ const getItems = useGetItems(nodeRef, props.statefulLayout)
26
+ const { inputProps, compSlots, localData, layout, options } = useNode(nodeRef, props.statefulLayout)
49
27
 
50
28
  const fieldProps = computed(() => {
51
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
52
- fieldProps.loading = loading.value
29
+ const fieldProps = { ...inputProps.value }
53
30
  fieldProps.returnObject = false
54
- if (hasItems.value) fieldProps.items = items.value
55
- if (props.modelValue.options.readOnly) fieldProps.menuProps = { modelValue: false }
56
- if (props.modelValue.layout.multiple) {
31
+ if (options.value.readOnly) fieldProps.menuProps = { modelValue: false }
32
+ if (getItems.hasItems.value) {
33
+ fieldProps.items = getItems.items.value
34
+ fieldProps.loading = getItems.loading.value
35
+ }
36
+ if (layout.value.multiple) {
57
37
  fieldProps.multiple = true
58
38
  fieldProps.chips = true
59
39
  fieldProps.closableChips = true
60
40
  }
61
- fieldProps['onUpdate:menu'] = () => refresh()
62
41
  return fieldProps
63
42
  })
64
43
 
65
- const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
66
-
67
44
  // @ts-ignore
68
- return () => h(VCombobox, fieldProps.value, fieldSlots.value)
45
+ return () => h(VCombobox, { ...fieldProps.value, modelValue: localData.value }, compSlots.value)
69
46
  }
70
47
  })
71
48