@koumoul/vjsf 3.0.0-beta.44 → 3.0.0-beta.45

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 (96) hide show
  1. package/package.json +12 -18
  2. package/src/components/fragments/selection-group.vue +11 -10
  3. package/src/components/fragments/text-field-menu.vue +10 -6
  4. package/src/components/nodes/autocomplete.vue +6 -6
  5. package/src/components/nodes/checkbox.vue +29 -27
  6. package/src/components/nodes/color-picker.vue +6 -4
  7. package/src/components/nodes/combobox.vue +14 -40
  8. package/src/components/nodes/date-picker.vue +14 -6
  9. package/src/components/nodes/date-time-picker.vue +14 -11
  10. package/src/components/nodes/expansion-panels.vue +6 -3
  11. package/src/components/nodes/file-input.vue +11 -10
  12. package/src/components/nodes/list.vue +21 -15
  13. package/src/components/nodes/number-combobox.vue +16 -40
  14. package/src/components/nodes/number-field.vue +13 -10
  15. package/src/components/nodes/one-of-select.vue +14 -10
  16. package/src/components/nodes/radio-group.vue +8 -5
  17. package/src/components/nodes/select.vue +8 -8
  18. package/src/components/nodes/slider.vue +30 -30
  19. package/src/components/nodes/switch.vue +29 -27
  20. package/src/components/nodes/text-field.vue +6 -6
  21. package/src/components/nodes/textarea.vue +16 -11
  22. package/src/components/nodes/time-picker.vue +9 -6
  23. package/src/composables/use-dnd.js +1 -1
  24. package/src/composables/use-get-items.js +12 -7
  25. package/src/composables/use-node.js +136 -0
  26. package/src/composables/use-select-node.js +67 -0
  27. package/src/composables/use-vjsf.js +3 -2
  28. package/types/components/fragments/child-subtitle.vue.d.ts +1 -1
  29. package/types/components/fragments/help-message.vue.d.ts +1 -1
  30. package/types/components/fragments/node-slot.vue.d.ts +4 -4
  31. package/types/components/fragments/section-header.vue.d.ts +1 -1
  32. package/types/components/fragments/select-item-icon.vue.d.ts +4 -4
  33. package/types/components/fragments/select-item.vue.d.ts +3 -3
  34. package/types/components/fragments/select-selection.vue.d.ts +1 -1
  35. package/types/components/fragments/selection-group.vue.d.ts +4 -4
  36. package/types/components/fragments/text-field-menu.vue.d.ts +1 -1
  37. package/types/components/fragments/text-field-menu.vue.d.ts.map +1 -1
  38. package/types/components/node.vue.d.ts +1 -1
  39. package/types/components/nodes/autocomplete.vue.d.ts +4 -4
  40. package/types/components/nodes/autocomplete.vue.d.ts.map +1 -1
  41. package/types/components/nodes/card.vue.d.ts +1 -1
  42. package/types/components/nodes/checkbox-group.vue.d.ts +4 -4
  43. package/types/components/nodes/checkbox.vue.d.ts +25 -8
  44. package/types/components/nodes/checkbox.vue.d.ts.map +1 -1
  45. package/types/components/nodes/color-picker.vue.d.ts +1 -1
  46. package/types/components/nodes/combobox.vue.d.ts +4 -4
  47. package/types/components/nodes/combobox.vue.d.ts.map +1 -1
  48. package/types/components/nodes/date-picker.vue.d.ts +1 -1
  49. package/types/components/nodes/date-time-picker.vue.d.ts +1 -1
  50. package/types/components/nodes/expansion-panels.vue.d.ts +1 -1
  51. package/types/components/nodes/file-input.vue.d.ts +4 -4
  52. package/types/components/nodes/list.vue.d.ts +1 -1
  53. package/types/components/nodes/number-combobox.vue.d.ts +4 -4
  54. package/types/components/nodes/number-combobox.vue.d.ts.map +1 -1
  55. package/types/components/nodes/number-field.vue.d.ts +4 -4
  56. package/types/components/nodes/one-of-select.vue.d.ts +1 -1
  57. package/types/components/nodes/radio-group.vue.d.ts +4 -4
  58. package/types/components/nodes/section.vue.d.ts +1 -1
  59. package/types/components/nodes/select.vue.d.ts +4 -4
  60. package/types/components/nodes/select.vue.d.ts.map +1 -1
  61. package/types/components/nodes/slider.vue.d.ts +25 -8
  62. package/types/components/nodes/slider.vue.d.ts.map +1 -1
  63. package/types/components/nodes/stepper.vue.d.ts +1 -1
  64. package/types/components/nodes/switch-group.vue.d.ts +4 -4
  65. package/types/components/nodes/switch.vue.d.ts +25 -8
  66. package/types/components/nodes/switch.vue.d.ts.map +1 -1
  67. package/types/components/nodes/tabs.vue.d.ts +1 -1
  68. package/types/components/nodes/text-field.vue.d.ts +4 -4
  69. package/types/components/nodes/textarea.vue.d.ts +4 -4
  70. package/types/components/nodes/time-picker.vue.d.ts +1 -1
  71. package/types/components/nodes/vertical-tabs.vue.d.ts +1 -1
  72. package/types/components/tree.vue.d.ts +1 -1
  73. package/types/components/vjsf.vue.d.ts +1 -1
  74. package/types/composables/use-dnd.d.ts +3 -3
  75. package/types/composables/use-field-props.d.ts +30 -0
  76. package/types/composables/use-field-props.d.ts.map +1 -0
  77. package/types/composables/use-field.d.ts +31 -0
  78. package/types/composables/use-field.d.ts.map +1 -0
  79. package/types/composables/use-get-items.d.ts +7 -8
  80. package/types/composables/use-get-items.d.ts.map +1 -1
  81. package/types/composables/use-node.d.ts +32 -0
  82. package/types/composables/use-node.d.ts.map +1 -0
  83. package/types/composables/use-select-field.d.ts +21 -0
  84. package/types/composables/use-select-field.d.ts.map +1 -0
  85. package/types/composables/use-select-node.d.ts +27 -0
  86. package/types/composables/use-select-node.d.ts.map +1 -0
  87. package/types/composables/use-select-props.d.ts +21 -0
  88. package/types/composables/use-select-props.d.ts.map +1 -0
  89. package/types/composables/use-select.d.ts +21 -0
  90. package/types/composables/use-select.d.ts.map +1 -0
  91. package/types/composables/use-vjsf.d.ts +2 -2
  92. package/types/composables/use-vjsf.d.ts.map +1 -1
  93. package/types/utils/index.d.ts +0 -2
  94. package/src/utils/index.js +0 -4
  95. package/src/utils/props.js +0 -136
  96. package/src/utils/slots.js +0 -46
@@ -12,12 +12,13 @@ import { VMenu } from 'vuetify/components/VMenu'
12
12
  import { VForm } from 'vuetify/components/VForm'
13
13
  import { isSection, clone, getRegexp } from '@json-layout/core'
14
14
  import Node from '../node.vue'
15
- import { moveDataItem } from '../../utils/index.js'
15
+ import { moveDataItem } from '../../utils/arrays.js'
16
16
  import useDnd from '../../composables/use-dnd.js'
17
17
  import useCompDefaults from '../../composables/use-comp-defaults.js'
18
18
 
19
19
  useDefaults({}, 'VjsfList')
20
20
  const vSheetProps = useCompDefaults('VjsfList-VSheet', { border: true })
21
+ const theme = useTheme()
21
22
 
22
23
  const props = defineProps({
23
24
  modelValue: {
@@ -32,27 +33,32 @@ const props = defineProps({
32
33
  }
33
34
  })
34
35
 
35
- const theme = useTheme()
36
+ // we access vjsfNode properties through computeds so that the parts without mutations do not trigger reactivity
37
+ // this is to leverage the immutability provided by immer in json-layout
38
+ const options = computed(() => props.modelValue.options)
39
+ const layout = computed(() => props.modelValue.layout)
40
+ const children = computed(() => props.modelValue.children)
36
41
 
37
42
  /* use composable for drag and drop */
38
43
  const { activeDnd, sortableArray, draggable, hovered, dragging, itemBind, handleBind } = useDnd(props.modelValue.children, () => {
39
- const newData = props.modelValue.layout.indexed
44
+ const newData = layout.value.indexed
40
45
  ? sortableArray.value.reduce((a, child) => { a[child.key] = child.data; return a }, /** @type {Record<string, any>} */({}))
41
46
  : sortableArray.value.map((child) => child.data)
42
- console.log(newData)
43
47
  props.statefulLayout.input(props.modelValue, newData)
44
48
  })
45
- watch(() => props.modelValue.children, (array) => { sortableArray.value = array })
49
+ watch(children, (array) => { sortableArray.value = array })
46
50
 
47
51
  /* manage hovered and edited items */
52
+ // const editedItem = computed(() => activatedItems.value[fullKey.value])
48
53
  const editedItem = computed(() => {
49
54
  return props.statefulLayout.activatedItems[props.modelValue.fullKey]
50
55
  })
56
+
51
57
  const menuOpened = ref(-1)
52
58
  const activeItem = computed(() => {
53
59
  if (
54
- props.modelValue.layout.listActions.includes('edit') &&
55
- props.modelValue.layout.listEditMode === 'inline-single' &&
60
+ layout.value.listActions.includes('edit') &&
61
+ layout.value.listEditMode === 'inline-single' &&
56
62
  editedItem.value !== undefined
57
63
  ) {
58
64
  return editedItem.value
@@ -63,14 +69,14 @@ const activeItem = computed(() => {
63
69
  })
64
70
 
65
71
  const buttonDensity = computed(() => {
66
- if (props.modelValue.options.density === 'default') return 'comfortable'
67
- return props.modelValue.options.density
72
+ if (options.value.density === 'default') return 'comfortable'
73
+ return options.value.density
68
74
  })
69
75
 
70
76
  const pushEmptyItem = () => {
71
77
  const newData = (props.modelValue.data ?? []).concat([undefined])
72
78
  props.statefulLayout.input(props.modelValue, newData)
73
- if (props.modelValue.layout.listEditMode === 'inline-single') {
79
+ if (layout.value.listEditMode === 'inline-single') {
74
80
  props.statefulLayout.activateItem(props.modelValue, newData.length - 1)
75
81
  }
76
82
  }
@@ -84,7 +90,7 @@ const pushEmptyIndexedItem = () => {
84
90
  if (!newKeyForm.value.isValid) return
85
91
  const newData = { ...(props.modelValue.data ?? {}), [newKey.value]: null }
86
92
  props.statefulLayout.input(props.modelValue, newData)
87
- if (props.modelValue.layout.listEditMode === 'inline-single') {
93
+ if (layout.value.listEditMode === 'inline-single') {
88
94
  props.statefulLayout.activateItem(props.modelValue, Object.keys(newData).length - 1)
89
95
  }
90
96
  newKey.value = ''
@@ -95,7 +101,7 @@ const pushEmptyIndexedItem = () => {
95
101
  * @param {number} childIndex
96
102
  */
97
103
  const deleteItem = (childIndex) => {
98
- if (props.modelValue.layout.indexed) {
104
+ if (layout.value.indexed) {
99
105
  const oldData = /** @type {Record<string, any>} */(props.modelValue.data)
100
106
  const keys = Object.keys(props.modelValue.data)
101
107
  /** @type {Record<string, any>} */
@@ -119,7 +125,7 @@ const deleteItem = (childIndex) => {
119
125
  const duplicateItem = (child, childIndex) => {
120
126
  const newData = [...props.modelValue.data.slice(0, childIndex), clone(child.data), ...props.modelValue.data.slice(childIndex)]
121
127
  props.statefulLayout.input(props.modelValue, newData)
122
- if (props.modelValue.layout.listEditMode === 'inline-single') {
128
+ if (layout.value.listEditMode === 'inline-single') {
123
129
  props.statefulLayout.activateItem(props.modelValue, childIndex + 1)
124
130
  }
125
131
  menuOpened.value = -1
@@ -128,7 +134,7 @@ const duplicateItem = (child, childIndex) => {
128
134
  const itemBorderColor = computed(() => (/** @type {import('@json-layout/core').StateNode} */child, /** @type {number} */childIndex) => {
129
135
  if (editedItem.value === childIndex) return theme.current.value.colors.primary
130
136
  if (child.validated && (child.error || child.childError)) return theme.current.value.colors.error
131
- if (props.modelValue.options.readOnly) return 'transparent'
137
+ if (options.value.readOnly) return 'transparent'
132
138
  if (activeItem.value === childIndex) return theme.current.value.colors.primary
133
139
  return 'transparent'
134
140
  })
@@ -143,7 +149,7 @@ const itemBorderColor = computed(() => (/** @type {import('@json-layout/core').S
143
149
  </v-list-subheader>
144
150
  <template
145
151
  v-for="(child, childIndex) of sortableArray"
146
- :key="props.modelValue.children.findIndex(c => c === child)"
152
+ :key="children.findIndex(c => c === child)"
147
153
  >
148
154
  <v-list-item
149
155
  v-bind="itemBind(childIndex)"
@@ -1,7 +1,8 @@
1
1
  <script>
2
- import { defineComponent, h, computed, shallowRef, ref } from 'vue'
2
+ import { defineComponent, h, computed, toRef } from 'vue'
3
3
  import { VCombobox } from 'vuetify/components/VCombobox'
4
- import { getInputProps, getCompSlots } from '../../utils/index.js'
4
+ import useNode from '../../composables/use-node.js'
5
+ import useGetItems from '../../composables/use-get-items.js'
5
6
  import { useDefaults } from 'vuetify'
6
7
 
7
8
  export default defineComponent({
@@ -18,57 +19,32 @@ export default defineComponent({
18
19
  }
19
20
  },
20
21
  setup (props) {
21
- useDefaults({}, 'VjsfNumberCombobox')
22
+ useDefaults({}, 'VjsfCombobox')
22
23
 
23
- /** @type import('vue').Ref<import('@json-layout/vocabulary').SelectItems> */
24
- const items = shallowRef(props.modelValue.layout.items ?? [])
25
- /** @type import('vue').Ref<boolean> */
26
- const loading = ref(false)
27
-
28
- /** @type import('@json-layout/core').StateTree | null */
29
- let lastStateTree = null
30
- /** @type Record<string, any> | null */
31
- let lastContext = null
32
-
33
- const hasItems = computed(() => {
34
- return !!(props.modelValue.layout.items || props.modelValue.layout.getItems)
35
- })
36
-
37
- const refresh = async () => {
38
- if (props.modelValue.layout.items) return
39
- if (props.statefulLayout.stateTree === lastStateTree && props.statefulLayout.options.context === lastContext) return
40
- lastStateTree = props.statefulLayout.stateTree
41
- lastContext = props.statefulLayout.options.context ?? null
42
- if (hasItems.value) {
43
- loading.value = true
44
- items.value = await props.statefulLayout.getItems(props.modelValue)
45
- loading.value = false
46
- }
47
- }
48
-
49
- if (!props.modelValue.layout.items) {
50
- refresh()
51
- }
24
+ const nodeRef = toRef(props, 'modelValue')
25
+ const getItems = useGetItems(nodeRef, props.statefulLayout)
26
+ const { inputProps, compSlots, localData, layout, options } = useNode(nodeRef, props.statefulLayout, { bindData: false, layoutPropsMap: ['step', 'min', 'max'] })
52
27
 
53
28
  const fieldProps = computed(() => {
54
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['step', 'min', 'max'])
29
+ const fieldProps = { ...inputProps.value }
55
30
  fieldProps.type = 'number'
56
- fieldProps.loading = loading.value
57
- if (hasItems.value) fieldProps.items = items.value
58
- if (props.modelValue.options.readOnly) fieldProps.menuProps = { modelValue: false }
59
- if (props.modelValue.layout.multiple) {
31
+ fieldProps.returnObject = false
32
+ if (options.value.readOnly) fieldProps.menuProps = { modelValue: false }
33
+ if (getItems.hasItems.value) {
34
+ fieldProps.items = getItems.items.value
35
+ fieldProps.loading = getItems.loading.value
36
+ }
37
+ if (layout.value.multiple) {
60
38
  fieldProps.multiple = true
61
39
  fieldProps.chips = true
62
40
  fieldProps.closableChips = true
63
41
  }
64
- fieldProps['onUpdate:menu'] = () => refresh()
65
42
  fieldProps['onUpdate:modelValue'] = (/** @type string[] */value) => props.statefulLayout.input(props.modelValue, value && value.map(Number))
66
43
  return fieldProps
67
44
  })
68
- const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
69
45
 
70
46
  // @ts-ignore
71
- return () => h(VCombobox, fieldProps.value, fieldSlots.value)
47
+ return () => h(VCombobox, { ...fieldProps.value, modelValue: localData.value }, compSlots.value)
72
48
  }
73
49
  })
74
50
 
@@ -1,7 +1,7 @@
1
1
  <script>
2
- import { defineComponent, h, computed } from 'vue'
2
+ import { defineComponent, h, computed, toRef } from 'vue'
3
3
  import { VTextField } from 'vuetify/components/VTextField'
4
- import { getInputProps, getCompSlots } from '../../utils/index.js'
4
+ import useNode from '../../composables/use-node.js'
5
5
  import { useDefaults } from 'vuetify'
6
6
 
7
7
  export default defineComponent({
@@ -20,16 +20,19 @@ export default defineComponent({
20
20
  setup (props) {
21
21
  useDefaults({}, 'VjsfNumberField')
22
22
 
23
- const fieldProps = computed(() => {
24
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['step', 'min', 'max', 'placeholder'])
25
- fieldProps.type = 'number'
26
- fieldProps['onUpdate:modelValue'] = (/** @type string */value) => props.statefulLayout.input(props.modelValue, value && Number(value))
27
- return fieldProps
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
28
33
  })
29
- const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
30
34
 
31
- // @ts-ignore
32
- return () => h(VTextField, fieldProps.value, fieldSlots.value)
35
+ return () => h(VTextField, fullProps.value, compSlots.value)
33
36
  }
34
37
  })
35
38
 
@@ -1,10 +1,10 @@
1
1
  <script setup>
2
2
  import { VRow, VCol } from 'vuetify/components/VGrid'
3
3
  import { VSelect } from 'vuetify/components/VSelect'
4
- import { ref, watch, computed, h } from 'vue'
4
+ import { ref, watch, computed, toRef } from 'vue'
5
5
  import { isSection } from '@json-layout/core'
6
6
  import { isCompObject } from '@json-layout/vocabulary'
7
- import { getInputProps } from '../../utils/index.js'
7
+ import useNode from '../../composables/use-node.js'
8
8
  import Node from '../node.vue'
9
9
  import { useDefaults } from 'vuetify'
10
10
 
@@ -23,12 +23,16 @@ const props = defineProps({
23
23
  }
24
24
  })
25
25
 
26
+ const { inputProps, localData, skeleton, children } = useNode(
27
+ toRef(props, 'modelValue'), props.statefulLayout, { bindData: false }
28
+ )
29
+
26
30
  /** @type import('vue').Ref<string | undefined> */
27
31
  const activeChildTree = ref(undefined)
28
- watch(() => props.modelValue, () => {
32
+ watch(() => children.value?.[0]?.key, () => {
29
33
  if (props.modelValue.children?.length === 1) {
30
34
  if (typeof props.modelValue.children[0].key === 'number') {
31
- activeChildTree.value = props.modelValue.skeleton.childrenTrees?.[props.modelValue.children[0].key]
35
+ activeChildTree.value = skeleton.value.childrenTrees?.[props.modelValue.children[0].key]
32
36
  }
33
37
  } else {
34
38
  activeChildTree.value = undefined
@@ -36,19 +40,18 @@ watch(() => props.modelValue, () => {
36
40
  }, { immediate: true })
37
41
 
38
42
  const onChange = (/** @type {string} */childTree) => {
39
- if (!props.modelValue.skeleton.childrenTrees) return
40
- props.statefulLayout.activateItem(props.modelValue, props.modelValue.skeleton.childrenTrees.indexOf(childTree))
43
+ if (!skeleton.value.childrenTrees) return
44
+ props.statefulLayout.activateItem(props.modelValue, skeleton.value.childrenTrees.indexOf(childTree))
41
45
  }
42
46
 
43
47
  const fieldProps = computed(() => {
44
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
45
- fieldProps.modelValue = activeChildTree.value
48
+ const fieldProps = { ...inputProps.value }
46
49
  fieldProps['onUpdate:modelValue'] = onChange
47
50
  const items = []
48
- for (const childTreePointer of props.modelValue.skeleton.childrenTrees || []) {
51
+ for (const childTreePointer of skeleton.value.childrenTrees || []) {
49
52
  const childTree = props.statefulLayout.compiledLayout.skeletonTrees[childTreePointer]
50
53
  const childLayout = props.statefulLayout.compiledLayout.normalizedLayouts[childTree.root]
51
- if (!isCompObject(childLayout) || !childLayout.if || !!props.statefulLayout.evalNodeExpression(props.modelValue, childLayout.if, props.modelValue.data)) {
54
+ if (!isCompObject(childLayout) || !childLayout.if || !!props.statefulLayout.evalNodeExpression(props.modelValue, childLayout.if, localData.value)) {
52
55
  items.push(childTree)
53
56
  }
54
57
  }
@@ -64,6 +67,7 @@ const fieldProps = computed(() => {
64
67
  <v-col v-if="modelValue.skeleton.childrenTrees">
65
68
  <v-select
66
69
  v-bind="fieldProps"
70
+ :model-value="activeChildTree"
67
71
  />
68
72
  </v-col>
69
73
  <template v-if="modelValue.children?.[0]">
@@ -2,8 +2,8 @@
2
2
  import { VRadio } from 'vuetify/components/VRadio'
3
3
  import { VRadioGroup } from 'vuetify/components/VRadioGroup'
4
4
  import { VSkeletonLoader } from 'vuetify/components/VSkeletonLoader'
5
- import { defineComponent, h, computed } from 'vue'
6
- import { getInputProps, getCompSlots } from '../../utils/index.js'
5
+ import { defineComponent, h, computed, toRef } from 'vue'
6
+ import useNode from '../../composables/use-node.js'
7
7
  import useGetItems from '../../composables/use-get-items.js'
8
8
  import { useDefaults } from 'vuetify'
9
9
 
@@ -23,15 +23,18 @@ export default defineComponent({
23
23
  setup (props) {
24
24
  useDefaults({}, 'VjsfRadioGroup')
25
25
 
26
- const getItems = useGetItems(props)
26
+ const nodeRef = toRef(props, 'modelValue')
27
+ const getItems = useGetItems(nodeRef, props.statefulLayout)
28
+ const { inputProps, compSlots, localData } = useNode(nodeRef, props.statefulLayout)
27
29
 
28
30
  const fieldProps = computed(() => {
29
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
31
+ const fieldProps = { ...inputProps.value }
32
+ fieldProps.modelValue = localData.value
30
33
  return fieldProps
31
34
  })
32
35
 
33
36
  const fieldSlots = computed(() => {
34
- const slots = getCompSlots(props.modelValue, props.statefulLayout)
37
+ const slots = { ...compSlots.value }
35
38
  /** @type {import('vue').VNode[]} */
36
39
  const children = []
37
40
  if (getItems.loading.value) {
@@ -1,20 +1,19 @@
1
1
  <script>
2
2
  import { VSelect } from 'vuetify/components/VSelect'
3
- import { defineComponent, h, computed } from 'vue'
4
- import { getSelectProps, getSelectSlots } from '../../utils/index.js'
5
- import useGetItems from '../../composables/use-get-items.js'
3
+ import { defineComponent, h, computed, toRef } from 'vue'
4
+ import useSelectNode from '../../composables/use-select-node.js'
6
5
 
7
6
  import { useDefaults } from 'vuetify'
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('../../types.js').VjsfStatefulLayout> */
16
+ /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
18
17
  type: Object,
19
18
  required: true
20
19
  }
@@ -22,17 +21,18 @@ export default defineComponent({
22
21
  setup (props) {
23
22
  useDefaults({}, 'VjsfSelect')
24
23
 
25
- const getItems = useGetItems(props)
24
+ const { getItems, selectProps, selectSlots, localData } = useSelectNode(toRef(props, 'modelValue'), props.statefulLayout)
26
25
 
27
26
  const fieldProps = computed(() => {
28
- const fieldProps = getSelectProps(props.modelValue, props.statefulLayout)
27
+ const fieldProps = { ...selectProps.value }
29
28
  fieldProps.loading = getItems.loading.value
30
29
  fieldProps.items = getItems.items.value
30
+ fieldProps.modelValue = localData.value
31
31
  return fieldProps
32
32
  })
33
33
 
34
34
  // @ts-ignore
35
- return () => h(VSelect, fieldProps.value, getSelectSlots(props.modelValue, props.statefulLayout, getItems))
35
+ return () => h(VSelect, fieldProps.value, selectSlots.value)
36
36
  }
37
37
  })
38
38
 
@@ -1,37 +1,37 @@
1
- <script setup>
1
+ <script>
2
2
  import { VSlider } from 'vuetify/components/VSlider'
3
- import { computed } from 'vue'
4
- import { getInputProps } from '../../utils/index.js'
3
+ import { defineComponent, computed, toRef, h } from 'vue'
4
+ import useNode from '../../composables/use-node.js'
5
5
  import { useDefaults } from 'vuetify'
6
6
 
7
- useDefaults({}, 'VjsfSlider')
8
-
9
- const props = defineProps({
10
- modelValue: {
11
- /** @type import('vue').PropType<import('../../types.js').VjsfSliderNode> */
12
- type: Object,
13
- 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
+ }
14
19
  },
15
- statefulLayout: {
16
- /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
17
- type: Object,
18
- required: true
19
- }
20
- })
20
+ setup (props) {
21
+ useDefaults({}, 'VjsfSlider')
22
+
23
+ const { inputProps, localData, compSlots } = useNode(
24
+ toRef(props, 'modelValue'), props.statefulLayout, { layoutPropsMap: ['step', 'min', 'max'] }
25
+ )
21
26
 
22
- const fieldProps = computed(() => {
23
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
24
- if ('step' in props.modelValue.layout) fieldProps.step = props.modelValue.layout.step
25
- fieldProps.min = props.modelValue.layout.min
26
- fieldProps.max = props.modelValue.layout.max
27
- 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
+ }
28
36
  })
29
37
  </script>
30
-
31
- <template>
32
- <v-slider
33
- type="number"
34
- v-bind="fieldProps"
35
- @update:model-value="value => statefulLayout.input(modelValue, value && Number(value))"
36
- />
37
- </template>
@@ -1,35 +1,37 @@
1
- <script setup>
1
+ <script>
2
+ import { defineComponent, h, computed, toRef } from 'vue'
2
3
  import { VSwitch } from 'vuetify/components/VSwitch'
3
- import { computed } from 'vue'
4
- import { getInputProps } from '../../utils/index.js'
4
+ import useNode from '../../composables/use-node.js'
5
5
  import { useDefaults } from 'vuetify'
6
6
 
7
- useDefaults({}, 'VjsfSwitch')
8
-
9
- const props = defineProps({
10
- modelValue: {
11
- /** @type import('vue').PropType<import('../../types.js').VjsfSwitchNode> */
12
- type: Object,
13
- 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
+ }
14
19
  },
15
- statefulLayout: {
16
- /** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
17
- type: Object,
18
- 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)
19
34
  }
20
35
  })
21
36
 
22
- const fieldProps = computed(() => {
23
- const inputProps = getInputProps(props.modelValue, props.statefulLayout)
24
- // it is not very common to show an error below checkboxes and switches and without hide-details=auto they take a lot of space
25
- if (!('hideDetails' in inputProps)) inputProps.hideDetails = 'auto'
26
- return inputProps
27
- })
28
37
  </script>
29
-
30
- <template>
31
- <v-switch
32
- v-bind="fieldProps"
33
- @update:model-value="value => statefulLayout.input(modelValue, value)"
34
- />
35
- </template>
@@ -1,7 +1,7 @@
1
1
  <script>
2
- import { defineComponent, h, computed } from 'vue'
2
+ import { defineComponent, h, toRef } from 'vue'
3
3
  import { VTextField } from 'vuetify/components/VTextField'
4
- import { getInputProps, getCompSlots } from '../../utils/index.js'
4
+ import useNode from '../../composables/use-node.js'
5
5
  import { useDefaults } from 'vuetify'
6
6
 
7
7
  export default defineComponent({
@@ -20,11 +20,11 @@ export default defineComponent({
20
20
  setup (props) {
21
21
  useDefaults({}, 'VjsfTextField')
22
22
 
23
- const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout, ['placeholder']))
24
- const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
23
+ const { inputProps, localData, compSlots } = useNode(
24
+ toRef(props, 'modelValue'), props.statefulLayout, { layoutPropsMap: ['placeholder'] }
25
+ )
25
26
 
26
- // @ts-ignore
27
- return () => h(VTextField, fieldProps.value, fieldSlots.value)
27
+ return () => h(VTextField, { ...inputProps.value, modelValue: localData.value }, compSlots.value)
28
28
  }
29
29
  })
30
30
 
@@ -1,7 +1,7 @@
1
1
  <script>
2
- import { defineComponent, h, computed, ref, watch } from 'vue'
2
+ import { defineComponent, h, computed, ref, watch, toRef } from 'vue'
3
3
  import { VTextarea } from 'vuetify/components/VTextarea'
4
- import { getInputProps, getCompSlots } from '../../utils/index.js'
4
+ import useNode from '../../composables/use-node.js'
5
5
  import { useDefaults } from 'vuetify'
6
6
 
7
7
  export default defineComponent({
@@ -23,22 +23,27 @@ export default defineComponent({
23
23
  /** @type {import('vue').Ref<null | HTMLElement>} */
24
24
  const textarea = ref(null)
25
25
 
26
- const fieldProps = computed(() => {
27
- const inputProps = getInputProps(props.modelValue, props.statefulLayout, ['placeholder'])
28
- inputProps.ref = textarea
29
- if (props.modelValue.options.readOnly && props.modelValue.options.summary) inputProps.rows = 3
30
- 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
31
38
  })
32
- const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
33
39
 
34
- watch(() => props.modelValue.options.readOnly, (readOnly) => {
40
+ watch(() => options.value.readOnly, (readOnly) => {
35
41
  if (readOnly && textarea.value) {
36
42
  textarea.value.scrollTop = 0
37
43
  }
38
44
  })
39
45
 
40
- // @ts-ignore
41
- return () => h(VTextarea, fieldProps.value, fieldSlots.value)
46
+ return () => h(VTextarea, fullProps.value, compSlots.value)
42
47
  }
43
48
  })
44
49
 
@@ -2,8 +2,9 @@
2
2
  import TextFieldMenu from '../fragments/text-field-menu.vue'
3
3
  import { VTimePicker } from 'vuetify/labs/VTimePicker'
4
4
  import { useDate, useDefaults } from 'vuetify'
5
- import { computed } from 'vue'
6
- import { getCompProps, getShortTime, getLongTime } from '../../utils/index.js'
5
+ import { computed, toRef } from 'vue'
6
+ import { getShortTime, getLongTime } from '../../utils/dates.js'
7
+ import useNode from '../../composables/use-node.js'
7
8
 
8
9
  useDefaults({}, 'VjsfDatePicker')
9
10
 
@@ -22,23 +23,25 @@ const props = defineProps({
22
23
 
23
24
  const vDate = useDate()
24
25
 
26
+ const { compProps, localData } = useNode(toRef(props, 'modelValue'), props.statefulLayout)
27
+
25
28
  const timePickerProps = computed(() => {
26
- const timePickerProps = getCompProps(props.modelValue, true)
29
+ const timePickerProps = { ...compProps.value }
27
30
  timePickerProps['ampm-in-title'] = true
28
- if (props.modelValue.data) timePickerProps.modelValue = getShortTime(props.modelValue.data)
31
+ if (localData.value) timePickerProps.modelValue = getShortTime(localData.value)
29
32
  return timePickerProps
30
33
  })
31
34
  </script>
32
35
 
33
36
  <template>
34
37
  <text-field-menu
35
- :model-value="modelValue"
38
+ :model-value="props.modelValue"
36
39
  :stateful-layout="statefulLayout"
37
40
  :formatted-value="timePickerProps.modelValue && vDate.format('2010-04-13T' + timePickerProps.modelValue, 'fullTime')"
38
41
  >
39
42
  <v-time-picker
40
43
  v-bind="timePickerProps"
41
- @update:model-value="value => {statefulLayout.input(modelValue, value && getLongTime(value))}"
44
+ @update:model-value="value => {statefulLayout.input(props.modelValue, value && getLongTime(value))}"
42
45
  />
43
46
  </text-field-menu>
44
47
  </template>
@@ -1,5 +1,5 @@
1
1
  import { shallowRef, ref, computed } from 'vue'
2
- import { moveArrayItem } from '../utils/index.js'
2
+ import { moveArrayItem } from '../utils/arrays.js'
3
3
 
4
4
  /**
5
5
  * @template T