@koumoul/vjsf 3.0.0-beta.8 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +21 -0
  2. package/package.json +13 -20
  3. package/src/compat/v2.js +126 -27
  4. package/src/components/fragments/child-subtitle.vue +25 -0
  5. package/src/components/fragments/help-message.vue +33 -8
  6. package/src/components/fragments/section-header.vue +9 -7
  7. package/src/components/fragments/select-item-icon.vue +2 -2
  8. package/src/components/fragments/select-item.vue +2 -1
  9. package/src/components/fragments/select-selection.vue +2 -1
  10. package/src/components/fragments/selection-group.vue +105 -0
  11. package/src/components/fragments/text-field-menu.vue +16 -7
  12. package/src/components/node.vue +58 -41
  13. package/src/components/nodes/autocomplete.vue +14 -60
  14. package/src/components/nodes/card.vue +39 -0
  15. package/src/components/nodes/checkbox-group.vue +39 -0
  16. package/src/components/nodes/checkbox.vue +31 -26
  17. package/src/components/nodes/color-picker.vue +10 -5
  18. package/src/components/nodes/combobox.vue +17 -40
  19. package/src/components/nodes/date-picker.vue +30 -13
  20. package/src/components/nodes/date-time-picker.vue +83 -3
  21. package/src/components/nodes/expansion-panels.vue +17 -9
  22. package/src/components/nodes/file-input.vue +15 -11
  23. package/src/components/nodes/list.vue +246 -112
  24. package/src/components/nodes/number-combobox.vue +18 -39
  25. package/src/components/nodes/number-field.vue +17 -11
  26. package/src/components/nodes/one-of-select.vue +53 -27
  27. package/src/components/nodes/radio-group.vue +58 -0
  28. package/src/components/nodes/section.vue +4 -1
  29. package/src/components/nodes/select.vue +15 -54
  30. package/src/components/nodes/slider.vue +32 -29
  31. package/src/components/nodes/stepper.vue +10 -2
  32. package/src/components/nodes/switch-group.vue +39 -0
  33. package/src/components/nodes/switch.vue +31 -26
  34. package/src/components/nodes/tabs.vue +20 -8
  35. package/src/components/nodes/text-field.vue +10 -7
  36. package/src/components/nodes/textarea.vue +20 -12
  37. package/src/components/nodes/time-picker.vue +41 -1
  38. package/src/components/nodes/vertical-tabs.vue +16 -6
  39. package/src/components/tree.vue +1 -1
  40. package/src/components/vjsf.vue +11 -1
  41. package/src/composables/use-comp-defaults.js +19 -0
  42. package/src/composables/use-dnd.js +2 -1
  43. package/src/composables/use-get-items.js +53 -0
  44. package/src/composables/use-node.js +136 -0
  45. package/src/composables/use-select-node.js +67 -0
  46. package/src/composables/use-vjsf.js +74 -42
  47. package/src/index.js +5 -2
  48. package/src/options.js +67 -0
  49. package/src/types.ts +64 -33
  50. package/src/utils/arrays.js +37 -6
  51. package/types/compat/v2.d.ts.map +1 -1
  52. package/types/compile/index.d.ts +2 -2
  53. package/types/compile/index.d.ts.map +1 -1
  54. package/types/compile/options.d.ts +3 -2
  55. package/types/compile/options.d.ts.map +1 -1
  56. package/types/components/fragments/child-subtitle.vue.d.ts +8 -0
  57. package/types/components/fragments/child-subtitle.vue.d.ts.map +1 -0
  58. package/types/components/fragments/help-message.vue.d.ts +2 -2
  59. package/types/components/fragments/node-slot.vue.d.ts +2 -44
  60. package/types/components/fragments/node-slot.vue.d.ts.map +1 -1
  61. package/types/components/fragments/section-header.vue.d.ts +4 -2
  62. package/types/components/fragments/select-item-icon.vue.d.ts +2 -12
  63. package/types/components/fragments/select-item.vue.d.ts +2 -2
  64. package/types/components/fragments/select-selection.vue.d.ts +2 -2
  65. package/types/components/fragments/selection-group.vue.d.ts +5 -0
  66. package/types/components/fragments/selection-group.vue.d.ts.map +1 -0
  67. package/types/components/fragments/text-field-menu.vue.d.ts +2 -2
  68. package/types/components/fragments/text-field-menu.vue.d.ts.map +1 -1
  69. package/types/components/node.vue.d.ts +2 -2
  70. package/types/components/nodes/autocomplete.vue.d.ts +2 -24
  71. package/types/components/nodes/autocomplete.vue.d.ts.map +1 -1
  72. package/types/components/nodes/card.vue.d.ts +10 -0
  73. package/types/components/nodes/card.vue.d.ts.map +1 -0
  74. package/types/components/nodes/checkbox-group.vue.d.ts +5 -0
  75. package/types/components/nodes/checkbox-group.vue.d.ts.map +1 -0
  76. package/types/components/nodes/checkbox.vue.d.ts +3 -8
  77. package/types/components/nodes/color-picker.vue.d.ts +2 -2
  78. package/types/components/nodes/combobox.vue.d.ts +2 -24
  79. package/types/components/nodes/combobox.vue.d.ts.map +1 -1
  80. package/types/components/nodes/date-picker.vue.d.ts +2 -2
  81. package/types/components/nodes/date-time-picker.vue.d.ts +4 -4
  82. package/types/components/nodes/expansion-panels.vue.d.ts +2 -2
  83. package/types/components/nodes/file-input.vue.d.ts +2 -24
  84. package/types/components/nodes/file-input.vue.d.ts.map +1 -1
  85. package/types/components/nodes/list.vue.d.ts +2 -2
  86. package/types/components/nodes/number-combobox.vue.d.ts +2 -24
  87. package/types/components/nodes/number-combobox.vue.d.ts.map +1 -1
  88. package/types/components/nodes/number-field.vue.d.ts +2 -24
  89. package/types/components/nodes/number-field.vue.d.ts.map +1 -1
  90. package/types/components/nodes/one-of-select.vue.d.ts +2 -2
  91. package/types/components/nodes/radio-group.vue.d.ts +5 -0
  92. package/types/components/nodes/radio-group.vue.d.ts.map +1 -0
  93. package/types/components/nodes/section.vue.d.ts +2 -2
  94. package/types/components/nodes/select.vue.d.ts +2 -24
  95. package/types/components/nodes/select.vue.d.ts.map +1 -1
  96. package/types/components/nodes/slider.vue.d.ts +3 -8
  97. package/types/components/nodes/stepper.vue.d.ts +2 -2
  98. package/types/components/nodes/switch-group.vue.d.ts +5 -0
  99. package/types/components/nodes/switch-group.vue.d.ts.map +1 -0
  100. package/types/components/nodes/switch.vue.d.ts +3 -8
  101. package/types/components/nodes/tabs.vue.d.ts +2 -2
  102. package/types/components/nodes/text-field.vue.d.ts +2 -24
  103. package/types/components/nodes/text-field.vue.d.ts.map +1 -1
  104. package/types/components/nodes/textarea.vue.d.ts +2 -24
  105. package/types/components/nodes/textarea.vue.d.ts.map +1 -1
  106. package/types/components/nodes/time-picker.vue.d.ts +8 -1
  107. package/types/components/nodes/vertical-tabs.vue.d.ts +2 -2
  108. package/types/components/options.d.ts +1 -1
  109. package/types/components/options.d.ts.map +1 -1
  110. package/types/components/tree.vue.d.ts +2 -2
  111. package/types/components/vjsf.vue.d.ts +4 -4
  112. package/types/composables/use-comp-defaults.d.ts +8 -0
  113. package/types/composables/use-comp-defaults.d.ts.map +1 -0
  114. package/types/composables/use-dnd.d.ts +3 -3
  115. package/types/composables/use-dnd.d.ts.map +1 -1
  116. package/types/composables/use-field-props.d.ts +30 -0
  117. package/types/composables/use-field-props.d.ts.map +1 -0
  118. package/types/composables/use-field.d.ts +31 -0
  119. package/types/composables/use-field.d.ts.map +1 -0
  120. package/types/composables/use-get-items.d.ts +12 -0
  121. package/types/composables/use-get-items.d.ts.map +1 -0
  122. package/types/composables/use-node.d.ts +32 -0
  123. package/types/composables/use-node.d.ts.map +1 -0
  124. package/types/composables/use-select-field.d.ts +21 -0
  125. package/types/composables/use-select-field.d.ts.map +1 -0
  126. package/types/composables/use-select-node.d.ts +27 -0
  127. package/types/composables/use-select-node.d.ts.map +1 -0
  128. package/types/composables/use-select-props.d.ts +21 -0
  129. package/types/composables/use-select-props.d.ts.map +1 -0
  130. package/types/composables/use-select.d.ts +21 -0
  131. package/types/composables/use-select.d.ts.map +1 -0
  132. package/types/composables/use-vjsf.d.ts +2 -2
  133. package/types/composables/use-vjsf.d.ts.map +1 -1
  134. package/types/iconsets/default-aliases.d.ts +10 -0
  135. package/types/iconsets/default-aliases.d.ts.map +1 -0
  136. package/types/iconsets/mdi-svg.d.ts +3 -0
  137. package/types/iconsets/mdi-svg.d.ts.map +1 -0
  138. package/types/iconsets/mdi.d.ts +3 -0
  139. package/types/iconsets/mdi.d.ts.map +1 -0
  140. package/types/index.d.ts +5 -2
  141. package/types/index.d.ts.map +1 -1
  142. package/types/options.d.ts +9 -0
  143. package/types/options.d.ts.map +1 -0
  144. package/types/types.d.ts +65 -33
  145. package/types/types.d.ts.map +1 -1
  146. package/types/utils/arrays.d.ts +17 -4
  147. package/types/utils/arrays.d.ts.map +1 -1
  148. package/types/utils/index.d.ts +0 -3
  149. package/types/utils/props.d.ts +7 -0
  150. package/types/utils/props.d.ts.map +1 -1
  151. package/types/utils/slots.d.ts +8 -0
  152. package/types/utils/slots.d.ts.map +1 -1
  153. package/src/compile/index.js +0 -65
  154. package/src/compile/options.js +0 -19
  155. package/src/compile/v-jsf-compiled.vue.ejs +0 -61
  156. package/src/components/options.js +0 -27
  157. package/src/utils/global-register.js +0 -13
  158. package/src/utils/index.js +0 -5
  159. package/src/utils/props.js +0 -107
  160. package/src/utils/slots.js +0 -18
  161. package/types/utils/global-register.d.ts +0 -8
  162. package/types/utils/global-register.d.ts.map +0 -1
@@ -1,7 +1,8 @@
1
1
  <script>
2
- import { defineComponent, h, computed } from 'vue'
3
- import { VTextField } from 'vuetify/components'
4
- import { getInputProps, getCompSlots } from '../../utils/index.js'
2
+ import { defineComponent, h, computed, toRef } from 'vue'
3
+ import { VTextField } from 'vuetify/components/VTextField'
4
+ import useNode from '../../composables/use-node.js'
5
+ import { useDefaults } from 'vuetify'
5
6
 
6
7
  export default defineComponent({
7
8
  props: {
@@ -17,16 +18,21 @@ export default defineComponent({
17
18
  }
18
19
  },
19
20
  setup (props) {
20
- const fieldProps = computed(() => {
21
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['step', 'min', 'max', 'placeholder'])
22
- fieldProps.type = 'number'
23
- fieldProps['onUpdate:modelValue'] = (/** @type string */value) => props.statefulLayout.input(props.modelValue, value && Number(value))
24
- return fieldProps
21
+ useDefaults({}, 'VjsfNumberField')
22
+
23
+ const { inputProps, localData, compSlots } = useNode(
24
+ toRef(props, 'modelValue'), props.statefulLayout, { layoutPropsMap: ['step', 'min', 'max', 'placeholder'], bindData: false }
25
+ )
26
+
27
+ const fullProps = computed(() => {
28
+ const fullProps = { ...inputProps.value }
29
+ fullProps.type = 'number'
30
+ fullProps['onUpdate:modelValue'] = (/** @type string */value) => props.statefulLayout.input(props.modelValue, value && Number(value))
31
+ fullProps.modelValue = localData.value
32
+ return fullProps
25
33
  })
26
- const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
27
34
 
28
- // @ts-ignore
29
- return () => h(VTextField, fieldProps.value, fieldSlots.value)
35
+ return () => h(VTextField, fullProps.value, compSlots.value)
30
36
  }
31
37
  })
32
38
 
@@ -1,8 +1,14 @@
1
1
  <script setup>
2
- import { VSelect, VRow } from 'vuetify/components'
3
- import { shallowRef, watch } from 'vue'
2
+ import { VRow, VCol } from 'vuetify/components/VGrid'
3
+ import { VSelect } from 'vuetify/components/VSelect'
4
+ import { ref, watch, computed, toRef } from 'vue'
4
5
  import { isSection } from '@json-layout/core'
6
+ import { isCompObject } from '@json-layout/vocabulary'
7
+ import useNode from '../../composables/use-node.js'
5
8
  import Node from '../node.vue'
9
+ import { useDefaults } from 'vuetify'
10
+
11
+ useDefaults({}, 'VjsfOneOfSelect')
6
12
 
7
13
  const props = defineProps({
8
14
  modelValue: {
@@ -17,40 +23,60 @@ const props = defineProps({
17
23
  }
18
24
  })
19
25
 
20
- /** @type import('vue').ShallowRef<import('@json-layout/core').SkeletonTree | undefined> */
21
- const activeChildTree = shallowRef(undefined)
22
- watch(() => props.modelValue, () => {
23
- // we set the active oneOf child as the one whose schema validates on the current data
24
- if (props.modelValue.fullKey in props.statefulLayout.activeItems) {
25
- activeChildTree.value = props.modelValue.skeleton.childrenTrees?.[props.statefulLayout.activeItems[props.modelValue.fullKey]]
26
+ const { inputProps, localData, skeleton, children } = useNode(
27
+ toRef(props, 'modelValue'), props.statefulLayout, { bindData: false }
28
+ )
29
+
30
+ /** @type import('vue').Ref<string | undefined> */
31
+ const activeChildTree = ref(undefined)
32
+ watch(() => children.value?.[0]?.key, () => {
33
+ if (props.modelValue.children?.length === 1) {
34
+ if (typeof props.modelValue.children[0].key === 'number') {
35
+ activeChildTree.value = skeleton.value.childrenTrees?.[props.modelValue.children[0].key]
36
+ }
26
37
  } else {
27
38
  activeChildTree.value = undefined
28
39
  }
29
40
  }, { immediate: true })
30
41
 
31
- const onChange = (/** @type import('@json-layout/core').SkeletonTree */childTree) => {
32
- if (!props.modelValue.skeleton.childrenTrees) return
33
- props.statefulLayout.activateItem(props.modelValue, props.modelValue.skeleton.childrenTrees.indexOf(childTree))
42
+ const onChange = (/** @type {string} */childTree) => {
43
+ if (!skeleton.value.childrenTrees) return
44
+ props.statefulLayout.activateItem(props.modelValue, skeleton.value.childrenTrees.indexOf(childTree))
34
45
  }
35
46
 
47
+ const fieldProps = computed(() => {
48
+ const fieldProps = { ...inputProps.value }
49
+ fieldProps['onUpdate:modelValue'] = onChange
50
+ const items = []
51
+ for (const childTreePointer of skeleton.value.childrenTrees || []) {
52
+ const childTree = props.statefulLayout.compiledLayout.skeletonTrees[childTreePointer]
53
+ const childLayout = props.statefulLayout.compiledLayout.normalizedLayouts[childTree.root]
54
+ if (!isCompObject(childLayout) || !childLayout.if || !!props.statefulLayout.evalNodeExpression(props.modelValue, childLayout.if, localData.value)) {
55
+ items.push(childTree)
56
+ }
57
+ }
58
+ fieldProps.items = items
59
+ fieldProps.itemTitle = 'title'
60
+ fieldProps.itemValue = (/** @type {import('@json-layout/core').SkeletonTree} */childTree) => childTree.root
61
+ return fieldProps
62
+ })
36
63
  </script>
37
64
 
38
65
  <template>
39
- <v-select
40
- v-if="modelValue.skeleton.childrenTrees"
41
- v-model="activeChildTree"
42
- :items="modelValue.skeleton.childrenTrees"
43
- item-title="title"
44
- return-object
45
- :error-messages="modelValue.validated ? modelValue.error : null"
46
- @update:model-value="onChange"
47
- />
48
- <v-row v-if="modelValue.children?.[0]">
49
- <node
50
- v-for="grandChild of isSection(modelValue.children?.[0]) ? modelValue.children?.[0].children : modelValue.children"
51
- :key="grandChild.fullKey"
52
- :model-value="/** @type import('../../types.js').VjsfNode */(grandChild)"
53
- :stateful-layout="statefulLayout"
54
- />
66
+ <v-row>
67
+ <v-col v-if="modelValue.skeleton.childrenTrees">
68
+ <v-select
69
+ v-bind="fieldProps"
70
+ :model-value="activeChildTree"
71
+ />
72
+ </v-col>
73
+ <template v-if="modelValue.children?.[0]">
74
+ <node
75
+ v-for="grandChild of isSection(modelValue.children?.[0]) ? modelValue.children?.[0].children : modelValue.children"
76
+ :key="grandChild.fullKey"
77
+ :model-value="/** @type import('../../types.js').VjsfNode */(grandChild)"
78
+ :stateful-layout="statefulLayout"
79
+ />
80
+ </template>
55
81
  </v-row>
56
82
  </template>
@@ -0,0 +1,58 @@
1
+ <script>
2
+ import { VRadio } from 'vuetify/components/VRadio'
3
+ import { VRadioGroup } from 'vuetify/components/VRadioGroup'
4
+ import { VSkeletonLoader } from 'vuetify/components/VSkeletonLoader'
5
+ import { defineComponent, h, computed, toRef } from 'vue'
6
+ import useNode from '../../composables/use-node.js'
7
+ import useGetItems from '../../composables/use-get-items.js'
8
+ import { useDefaults } from 'vuetify'
9
+
10
+ export default defineComponent({
11
+ props: {
12
+ modelValue: {
13
+ /** @type import('vue').PropType<import('../../types.js').VjsfRadioGroupNode> */
14
+ type: Object,
15
+ required: true
16
+ },
17
+ statefulLayout: {
18
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
19
+ type: Object,
20
+ required: true
21
+ }
22
+ },
23
+ setup (props) {
24
+ useDefaults({}, 'VjsfRadioGroup')
25
+
26
+ const nodeRef = toRef(props, 'modelValue')
27
+ const getItems = useGetItems(nodeRef, props.statefulLayout)
28
+ const { inputProps, compSlots, localData } = useNode(nodeRef, props.statefulLayout)
29
+
30
+ const fieldProps = computed(() => {
31
+ const fieldProps = { ...inputProps.value }
32
+ fieldProps.modelValue = localData.value
33
+ return fieldProps
34
+ })
35
+
36
+ const fieldSlots = computed(() => {
37
+ const slots = { ...compSlots.value }
38
+ /** @type {import('vue').VNode[]} */
39
+ const children = []
40
+ if (getItems.loading.value) {
41
+ children.push(h(VSkeletonLoader, { type: 'chip' }))
42
+ } else {
43
+ for (const item of getItems.items.value) {
44
+ children.push(h(VRadio, { label: item.title, value: item.value }))
45
+ }
46
+ }
47
+ slots.default = () => children
48
+ return slots
49
+ })
50
+
51
+ // @ts-ignore
52
+ return () => {
53
+ return h(VRadioGroup, fieldProps.value, fieldSlots.value)
54
+ }
55
+ }
56
+ })
57
+
58
+ </script>
@@ -1,7 +1,10 @@
1
1
  <script setup>
2
- import { VRow } from 'vuetify/components'
2
+ import { VRow } from 'vuetify/components/VGrid'
3
3
  import Node from '../node.vue'
4
4
  import SectionHeader from '../fragments/section-header.vue'
5
+ import { useDefaults } from 'vuetify'
6
+
7
+ useDefaults({}, 'VjsfSection')
5
8
 
6
9
  defineProps({
7
10
  modelValue: {
@@ -1,77 +1,38 @@
1
1
  <script>
2
- import { VSelect } from 'vuetify/components'
3
- import { defineComponent, h, computed, ref, shallowRef } 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 { VSelect } from 'vuetify/components/VSelect'
3
+ import { defineComponent, h, computed, toRef } from 'vue'
4
+ import useSelectNode from '../../composables/use-select-node.js'
5
+
6
+ import { useDefaults } from 'vuetify'
7
7
 
8
8
  export default defineComponent({
9
9
  props: {
10
10
  modelValue: {
11
- /** @type import('vue').PropType<import('../../types.js').VjsfSelectNode> */
11
+ /** @type import('vue').PropType<import('../../types.js').VjsfSelectNode> */
12
12
  type: Object,
13
13
  required: true
14
14
  },
15
15
  statefulLayout: {
16
- /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
16
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
17
17
  type: Object,
18
18
  required: true
19
19
  }
20
20
  },
21
21
  setup (props) {
22
- /** @type import('vue').Ref<import('@json-layout/vocabulary').SelectItems> */
23
- const items = shallowRef([])
24
- /** @type import('vue').Ref<boolean> */
25
- const loading = ref(false)
22
+ useDefaults({}, 'VjsfSelect')
23
+
24
+ const { getItems, selectProps, selectSlots, localData } = useSelectNode(toRef(props, 'modelValue'), props.statefulLayout)
26
25
 
27
26
  const fieldProps = computed(() => {
28
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['multiple'])
29
- if (props.modelValue.options.readOnly) fieldProps.menuProps = { modelValue: false }
30
- fieldProps.loading = loading.value
31
- fieldProps.items = items.value
32
- fieldProps['onUpdate:menu'] = refresh
27
+ const fieldProps = { ...selectProps.value }
28
+ fieldProps.loading = getItems.loading.value
29
+ fieldProps.items = getItems.items.value
30
+ fieldProps.modelValue = localData.value
33
31
  return fieldProps
34
32
  })
35
33
 
36
- /** @type import('@json-layout/core').StateTree | null */
37
- let lastStateTree = null
38
- /** @type Record<string, any> | null */
39
- let lastContext = null
40
-
41
- const refresh = async () => {
42
- if (props.statefulLayout.stateTree === lastStateTree && props.statefulLayout.options.context === lastContext) return
43
- lastStateTree = props.statefulLayout.stateTree
44
- lastContext = props.statefulLayout.options.context ?? null
45
- loading.value = true
46
- items.value = await props.statefulLayout.getItems(props.modelValue)
47
- loading.value = false
48
- }
49
-
50
- if (!props.modelValue.layout.items) {
51
- refresh()
52
- }
53
-
54
- const fieldSlots = computed(() => {
55
- const slots = getCompSlots(props.modelValue, props.statefulLayout)
56
- if (!slots.item) {
57
- slots.item = (/** @type {any} */ context) => h(SelectItem, {
58
- multiple: props.modelValue.layout.multiple,
59
- itemProps: context.props,
60
- item: context.item.raw
61
- })
62
- }
63
- if (!slots.selection) {
64
- slots.selection = (/** @type {any} */ context) => h(SelectSelection, {
65
- multiple: props.modelValue.layout.multiple,
66
- last: props.modelValue.layout.multiple && context.index === props.modelValue.data.length - 1,
67
- item: context.item.raw
68
- })
69
- }
70
- return slots
71
- })
72
-
73
34
  // @ts-ignore
74
- return () => h(VSelect, fieldProps.value, fieldSlots.value)
35
+ return () => h(VSelect, fieldProps.value, selectSlots.value)
75
36
  }
76
37
  })
77
38
 
@@ -1,34 +1,37 @@
1
- <script setup>
2
- import { VSlider } from 'vuetify/components'
3
- import { computed } from 'vue'
4
- import { getInputProps } from '../../utils/index.js'
1
+ <script>
2
+ import { VSlider } from 'vuetify/components/VSlider'
3
+ import { defineComponent, computed, toRef, h } from 'vue'
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').VjsfSliderNode> */
9
- type: Object,
10
- required: true
7
+ export default defineComponent({
8
+ props: {
9
+ modelValue: {
10
+ /** @type import('vue').PropType<import('../../types.js').VjsfSliderNode> */
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
16
- }
17
- })
20
+ setup (props) {
21
+ useDefaults({}, 'VjsfSlider')
22
+
23
+ const { inputProps, localData, compSlots } = useNode(
24
+ toRef(props, 'modelValue'), props.statefulLayout, { layoutPropsMap: ['step', 'min', 'max'] }
25
+ )
18
26
 
19
- const fieldProps = computed(() => {
20
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
21
- if ('step' in props.modelValue.layout) fieldProps.step = props.modelValue.layout.step
22
- fieldProps.min = props.modelValue.layout.min
23
- fieldProps.max = props.modelValue.layout.max
24
- return fieldProps
27
+ const fullProps = computed(() => {
28
+ const fullProps = { ...inputProps.value }
29
+ fullProps.modelValue = localData.value
30
+ fullProps['onUpdate:modelValue'] = (/** @type string */value) => props.statefulLayout.input(props.modelValue, value && Number(value))
31
+ return fullProps
32
+ })
33
+
34
+ return () => h(VSlider, fullProps.value, compSlots.value)
35
+ }
25
36
  })
26
37
  </script>
27
-
28
- <template>
29
- <v-slider
30
- type="number"
31
- v-bind="fieldProps"
32
- @update:model-value="value => statefulLayout.input(modelValue, value && Number(value))"
33
- />
34
- </template>
@@ -1,9 +1,16 @@
1
1
  <script setup>
2
2
  import { ref, computed } from 'vue'
3
- import { VStepper, VStepperHeader, VStepperItem, VStepperWindow, VStepperWindowItem, VStepperActions, VContainer, VRow, VSpacer, VBtn, VDivider } from 'vuetify/components'
3
+ import { VStepper, VStepperHeader, VStepperItem, VStepperWindow, VStepperWindowItem, VStepperActions } from 'vuetify/components/VStepper'
4
+ import { VContainer, VRow, VSpacer } from 'vuetify/components/VGrid'
5
+ import { VBtn } from 'vuetify/components/VBtn'
6
+ import { VDivider } from 'vuetify/components/VDivider'
4
7
  import { isSection } from '@json-layout/core'
5
8
  import Node from '../node.vue'
6
9
  import SectionHeader from '../fragments/section-header.vue'
10
+ import ChildSubtitle from '../fragments/child-subtitle.vue'
11
+ import { useDefaults } from 'vuetify'
12
+
13
+ useDefaults({}, 'VjsfStepper')
7
14
 
8
15
  const props = defineProps({
9
16
  modelValue: {
@@ -59,7 +66,8 @@ const goNext = () => {
59
66
  fluid
60
67
  class="pa-0"
61
68
  >
62
- <v-row>
69
+ <child-subtitle :model-value="child" />
70
+ <v-row :dense="modelValue.options?.density === 'compact' || modelValue.options?.density === 'comfortable'">
63
71
  <node
64
72
  v-for="grandChild of isSection(child) ? child.children : [child]"
65
73
  :key="grandChild.fullKey"
@@ -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({}, 'VjsfSwitchGroup')
21
+
22
+ // @ts-ignore
23
+ return () => {
24
+ return h(SelectionGroup, {
25
+ modelValue: props.modelValue,
26
+ statefulLayout: props.statefulLayout,
27
+ type: 'switch'
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 { VSwitch } 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 { VSwitch } from 'vuetify/components/VSwitch'
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').VjsfSwitchNode> */
9
- type: Object,
10
- required: true
7
+ export default defineComponent({
8
+ props: {
9
+ modelValue: {
10
+ /** @type import('vue').PropType<import('../../types.js').VjsfSwitchNode> */
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({}, 'VjsfSwitch')
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(VSwitch, 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-switch
29
- v-bind="fieldProps"
30
- @update:model-value="value => statefulLayout.input(modelValue, value)"
31
- />
32
- </template>
@@ -1,9 +1,19 @@
1
1
  <script setup>
2
- import { VTabs, VTab, VContainer, VSheet, VWindow, VWindowItem, VRow, VIcon } from 'vuetify/components'
2
+ import { VTabs, VTab } from 'vuetify/components/VTabs'
3
+ import { VContainer, VRow } from 'vuetify/components/VGrid'
4
+ import { VIcon } from 'vuetify/components/VIcon'
5
+ import { VSheet } from 'vuetify/components/VSheet'
6
+ import { VWindow, VWindowItem } from 'vuetify/components/VWindow'
7
+ import { useDefaults } from 'vuetify'
3
8
  import { ref } from 'vue'
4
9
  import { isSection } from '@json-layout/core'
5
10
  import Node from '../node.vue'
6
11
  import SectionHeader from '../fragments/section-header.vue'
12
+ import ChildSubtitle from '../fragments/child-subtitle.vue'
13
+ import useCompDefaults from '../../composables/use-comp-defaults.js'
14
+
15
+ useDefaults({}, 'VjsfTabs')
16
+ const vSheetProps = useCompDefaults('VjsfTabs-VSheet', { border: true })
7
17
 
8
18
  defineProps({
9
19
  modelValue: {
@@ -23,21 +33,22 @@ const tab = ref(0)
23
33
 
24
34
  <template>
25
35
  <section-header :node="modelValue" />
26
- <v-sheet border>
27
- <v-tabs v-model="tab">
36
+ <v-sheet v-bind="vSheetProps">
37
+ <v-tabs
38
+ v-model="tab"
39
+ direction="horizontal"
40
+ >
28
41
  <v-tab
29
42
  v-for="(child, i) of modelValue.children"
30
43
  :key="child.key"
31
44
  :value="i"
32
- :density="modelValue.options.density"
33
45
  :color="child.validated && (child.error || child.childError) ? 'error' : undefined"
34
46
  >
35
47
  <v-icon
36
48
  v-if="child.validated && (child.error || child.childError)"
37
49
  color="error"
38
- >
39
- mdi-alert
40
- </v-icon>
50
+ :icon="statefulLayout.options.icons.alert"
51
+ />
41
52
  {{ child.layout.title ?? child.layout.label }}
42
53
  </v-tab>
43
54
  </v-tabs>
@@ -48,7 +59,8 @@ const tab = ref(0)
48
59
  :value="i"
49
60
  >
50
61
  <v-container fluid>
51
- <v-row>
62
+ <child-subtitle :model-value="child" />
63
+ <v-row :dense="modelValue.options?.density === 'compact' || modelValue.options?.density === 'comfortable'">
52
64
  <node
53
65
  v-for="grandChild of isSection(child) ? child.children : [child]"
54
66
  :key="grandChild.fullKey"
@@ -1,7 +1,8 @@
1
1
  <script>
2
- import { defineComponent, h, computed } from 'vue'
3
- import { VTextField } from 'vuetify/components'
4
- import { getInputProps, getCompSlots } from '../../utils/index.js'
2
+ import { defineComponent, h, toRef } from 'vue'
3
+ import { VTextField } from 'vuetify/components/VTextField'
4
+ import useNode from '../../composables/use-node.js'
5
+ import { useDefaults } from 'vuetify'
5
6
 
6
7
  export default defineComponent({
7
8
  props: {
@@ -17,11 +18,13 @@ export default defineComponent({
17
18
  }
18
19
  },
19
20
  setup (props) {
20
- const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout, ['placeholder']))
21
- const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
21
+ useDefaults({}, 'VjsfTextField')
22
22
 
23
- // @ts-ignore
24
- return () => h(VTextField, fieldProps.value, fieldSlots.value)
23
+ const { inputProps, localData, compSlots } = useNode(
24
+ toRef(props, 'modelValue'), props.statefulLayout, { layoutPropsMap: ['placeholder'] }
25
+ )
26
+
27
+ return () => h(VTextField, { ...inputProps.value, modelValue: localData.value }, compSlots.value)
25
28
  }
26
29
  })
27
30
 
@@ -1,7 +1,8 @@
1
1
  <script>
2
- import { defineComponent, h, computed, ref, watch } from 'vue'
3
- import { VTextarea } from 'vuetify/components'
4
- import { getInputProps, getCompSlots } from '../../utils/index.js'
2
+ import { defineComponent, h, computed, ref, watch, toRef } from 'vue'
3
+ import { VTextarea } from 'vuetify/components/VTextarea'
4
+ import useNode from '../../composables/use-node.js'
5
+ import { useDefaults } from 'vuetify'
5
6
 
6
7
  export default defineComponent({
7
8
  props: {
@@ -17,25 +18,32 @@ export default defineComponent({
17
18
  }
18
19
  },
19
20
  setup (props) {
21
+ useDefaults({}, 'VjsfTextArea')
22
+
20
23
  /** @type {import('vue').Ref<null | HTMLElement>} */
21
24
  const textarea = ref(null)
22
25
 
23
- const fieldProps = computed(() => {
24
- const inputProps = getInputProps(props.modelValue, props.statefulLayout, ['placeholder'])
25
- inputProps.ref = textarea
26
- if (props.modelValue.options.readOnly && props.modelValue.options.summary) inputProps.rows = 3
27
- return inputProps
26
+ const { inputProps, localData, compSlots, options } = useNode(
27
+ toRef(props, 'modelValue'), props.statefulLayout, { layoutPropsMap: ['placeholder'] }
28
+ )
29
+
30
+ const rows = computed(() => options.value.readOnly && options.value.summary ? 3 : undefined)
31
+
32
+ const fullProps = computed(() => {
33
+ const fullProps = { ...inputProps.value }
34
+ fullProps.ref = textarea
35
+ fullProps.rows = rows.value
36
+ fullProps.modelValue = localData.value
37
+ return fullProps
28
38
  })
29
- const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
30
39
 
31
- watch(() => props.modelValue.options.readOnly, (readOnly) => {
40
+ watch(() => options.value.readOnly, (readOnly) => {
32
41
  if (readOnly && textarea.value) {
33
42
  textarea.value.scrollTop = 0
34
43
  }
35
44
  })
36
45
 
37
- // @ts-ignore
38
- return () => h(VTextarea, fieldProps.value, fieldSlots.value)
46
+ return () => h(VTextarea, fullProps.value, compSlots.value)
39
47
  }
40
48
  })
41
49