@koumoul/vjsf 3.0.0-beta.4 → 3.0.0-beta.40

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 +5 -4
  3. package/src/compat/v2.js +132 -27
  4. package/src/compile/index.js +18 -4
  5. package/src/compile/options.js +3 -7
  6. package/src/compile/v-jsf-compiled.vue.ejs +1 -1
  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 +104 -0
  14. package/src/components/fragments/text-field-menu.vue +8 -3
  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 +4 -1
  20. package/src/components/nodes/color-picker.vue +6 -2
  21. package/src/components/nodes/combobox.vue +4 -1
  22. package/src/components/nodes/date-picker.vue +19 -10
  23. package/src/components/nodes/date-time-picker.vue +80 -3
  24. package/src/components/nodes/expansion-panels.vue +28 -12
  25. package/src/components/nodes/file-input.vue +4 -1
  26. package/src/components/nodes/list.vue +239 -104
  27. package/src/components/nodes/number-combobox.vue +4 -1
  28. package/src/components/nodes/number-field.vue +4 -1
  29. package/src/components/nodes/one-of-select.vue +46 -24
  30. package/src/components/nodes/radio-group.vue +55 -0
  31. package/src/components/nodes/section.vue +4 -1
  32. package/src/components/nodes/select.vue +13 -52
  33. package/src/components/nodes/slider.vue +4 -1
  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 +4 -1
  37. package/src/components/nodes/tabs.vue +18 -5
  38. package/src/components/nodes/text-field.vue +4 -1
  39. package/src/components/nodes/textarea.vue +4 -1
  40. package/src/components/nodes/time-picker.vue +38 -1
  41. package/src/components/nodes/vertical-tabs.vue +14 -3
  42. package/src/components/options.js +21 -4
  43. package/src/components/tree.vue +1 -1
  44. package/src/components/vjsf.vue +11 -1
  45. package/src/composables/use-comp-defaults.js +19 -0
  46. package/src/composables/use-dnd.js +1 -0
  47. package/src/composables/use-get-items.js +48 -0
  48. package/src/composables/use-vjsf.js +76 -40
  49. package/src/index.js +3 -0
  50. package/src/types.ts +21 -7
  51. package/src/utils/arrays.js +37 -6
  52. package/src/utils/build.js +1 -1
  53. package/src/utils/index.js +0 -1
  54. package/src/utils/props.js +36 -12
  55. package/src/utils/slots.js +28 -0
  56. package/types/compat/v2.d.ts +10 -0
  57. package/types/compat/v2.d.ts.map +1 -0
  58. package/types/compile/index.d.ts +8 -0
  59. package/types/compile/index.d.ts.map +1 -0
  60. package/types/compile/options.d.ts +4 -0
  61. package/types/compile/options.d.ts.map +1 -0
  62. package/types/components/fragments/child-subtitle.vue.d.ts +8 -0
  63. package/types/components/fragments/child-subtitle.vue.d.ts.map +1 -0
  64. package/types/components/fragments/help-message.vue.d.ts +8 -0
  65. package/types/components/fragments/help-message.vue.d.ts.map +1 -0
  66. package/types/components/fragments/node-slot.vue.d.ts +47 -0
  67. package/types/components/fragments/node-slot.vue.d.ts.map +1 -0
  68. package/types/components/fragments/section-header.vue.d.ts +10 -0
  69. package/types/components/fragments/section-header.vue.d.ts.map +1 -0
  70. package/types/components/fragments/select-item-icon.vue.d.ts +15 -0
  71. package/types/components/fragments/select-item-icon.vue.d.ts.map +1 -0
  72. package/types/components/fragments/select-item.vue.d.ts +12 -0
  73. package/types/components/fragments/select-item.vue.d.ts.map +1 -0
  74. package/types/components/fragments/select-selection.vue.d.ts +12 -0
  75. package/types/components/fragments/select-selection.vue.d.ts.map +1 -0
  76. package/types/components/fragments/selection-group.vue.d.ts +35 -0
  77. package/types/components/fragments/selection-group.vue.d.ts.map +1 -0
  78. package/types/components/fragments/text-field-menu.vue.d.ts +20 -0
  79. package/types/components/fragments/text-field-menu.vue.d.ts.map +1 -0
  80. package/types/components/node.vue.d.ts +10 -0
  81. package/types/components/node.vue.d.ts.map +1 -0
  82. package/types/components/nodes/autocomplete.vue.d.ts +27 -0
  83. package/types/components/nodes/autocomplete.vue.d.ts.map +1 -0
  84. package/types/components/nodes/card.vue.d.ts +10 -0
  85. package/types/components/nodes/card.vue.d.ts.map +1 -0
  86. package/types/components/nodes/checkbox-group.vue.d.ts +27 -0
  87. package/types/components/nodes/checkbox-group.vue.d.ts.map +1 -0
  88. package/types/components/nodes/checkbox.vue.d.ts +10 -0
  89. package/types/components/nodes/checkbox.vue.d.ts.map +1 -0
  90. package/types/components/nodes/color-picker.vue.d.ts +10 -0
  91. package/types/components/nodes/color-picker.vue.d.ts.map +1 -0
  92. package/types/components/nodes/combobox.vue.d.ts +27 -0
  93. package/types/components/nodes/combobox.vue.d.ts.map +1 -0
  94. package/types/components/nodes/date-picker.vue.d.ts +10 -0
  95. package/types/components/nodes/date-picker.vue.d.ts.map +1 -0
  96. package/types/components/nodes/date-time-picker.vue.d.ts +10 -0
  97. package/types/components/nodes/date-time-picker.vue.d.ts.map +1 -0
  98. package/types/components/nodes/expansion-panels.vue.d.ts +10 -0
  99. package/types/components/nodes/expansion-panels.vue.d.ts.map +1 -0
  100. package/types/components/nodes/file-input.vue.d.ts +27 -0
  101. package/types/components/nodes/file-input.vue.d.ts.map +1 -0
  102. package/types/components/nodes/list.vue.d.ts +10 -0
  103. package/types/components/nodes/list.vue.d.ts.map +1 -0
  104. package/types/components/nodes/number-combobox.vue.d.ts +27 -0
  105. package/types/components/nodes/number-combobox.vue.d.ts.map +1 -0
  106. package/types/components/nodes/number-field.vue.d.ts +27 -0
  107. package/types/components/nodes/number-field.vue.d.ts.map +1 -0
  108. package/types/components/nodes/one-of-select.vue.d.ts +10 -0
  109. package/types/components/nodes/one-of-select.vue.d.ts.map +1 -0
  110. package/types/components/nodes/radio-group.vue.d.ts +27 -0
  111. package/types/components/nodes/radio-group.vue.d.ts.map +1 -0
  112. package/types/components/nodes/section.vue.d.ts +10 -0
  113. package/types/components/nodes/section.vue.d.ts.map +1 -0
  114. package/types/components/nodes/select.vue.d.ts +27 -0
  115. package/types/components/nodes/select.vue.d.ts.map +1 -0
  116. package/types/components/nodes/slider.vue.d.ts +10 -0
  117. package/types/components/nodes/slider.vue.d.ts.map +1 -0
  118. package/types/components/nodes/stepper.vue.d.ts +10 -0
  119. package/types/components/nodes/stepper.vue.d.ts.map +1 -0
  120. package/types/components/nodes/switch-group.vue.d.ts +27 -0
  121. package/types/components/nodes/switch-group.vue.d.ts.map +1 -0
  122. package/types/components/nodes/switch.vue.d.ts +10 -0
  123. package/types/components/nodes/switch.vue.d.ts.map +1 -0
  124. package/types/components/nodes/tabs.vue.d.ts +10 -0
  125. package/types/components/nodes/tabs.vue.d.ts.map +1 -0
  126. package/types/components/nodes/text-field.vue.d.ts +27 -0
  127. package/types/components/nodes/text-field.vue.d.ts.map +1 -0
  128. package/types/components/nodes/textarea.vue.d.ts +27 -0
  129. package/types/components/nodes/textarea.vue.d.ts.map +1 -0
  130. package/types/components/nodes/time-picker.vue.d.ts +10 -0
  131. package/types/components/nodes/time-picker.vue.d.ts.map +1 -0
  132. package/types/components/nodes/vertical-tabs.vue.d.ts +10 -0
  133. package/types/components/nodes/vertical-tabs.vue.d.ts.map +1 -0
  134. package/types/components/options.d.ts +4 -0
  135. package/types/components/options.d.ts.map +1 -0
  136. package/types/components/tree.vue.d.ts +10 -0
  137. package/types/components/tree.vue.d.ts.map +1 -0
  138. package/types/components/vjsf.vue.d.ts +15 -0
  139. package/types/components/vjsf.vue.d.ts.map +1 -0
  140. package/types/composables/use-comp-defaults.d.ts +8 -0
  141. package/types/composables/use-comp-defaults.d.ts.map +1 -0
  142. package/types/composables/use-dnd.d.ts +25 -0
  143. package/types/composables/use-dnd.d.ts.map +1 -0
  144. package/types/composables/use-get-items.d.ts +13 -0
  145. package/types/composables/use-get-items.d.ts.map +1 -0
  146. package/types/composables/use-vjsf.d.ts +16 -0
  147. package/types/composables/use-vjsf.d.ts.map +1 -0
  148. package/types/index.d.ts +7 -0
  149. package/types/index.d.ts.map +1 -0
  150. package/types/types.d.ts +96 -0
  151. package/types/types.d.ts.map +1 -0
  152. package/types/utils/arrays.d.ts +22 -0
  153. package/types/utils/arrays.d.ts.map +1 -0
  154. package/types/utils/build.d.ts +2 -0
  155. package/types/utils/build.d.ts.map +1 -0
  156. package/types/utils/dates.d.ts +7 -0
  157. package/types/utils/dates.d.ts.map +1 -0
  158. package/types/utils/index.d.ts +5 -0
  159. package/types/utils/index.d.ts.map +1 -0
  160. package/types/utils/props.d.ts +29 -0
  161. package/types/utils/props.d.ts.map +1 -0
  162. package/types/utils/slots.d.ts +15 -0
  163. package/types/utils/slots.d.ts.map +1 -0
  164. package/src/utils/global-register.js +0 -13
@@ -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,9 @@
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 } from 'vue'
5
+ import { getSelectProps, getSelectSlots } from '../../utils/index.js'
6
+ import useGetItems from '../../composables/use-get-items.js'
7
7
 
8
8
  export default defineComponent({
9
9
  props: {
@@ -13,75 +13,29 @@ export default defineComponent({
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').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('')
22
+ useDefaults({}, 'VjsfAutocomplete')
23
+
24
+ const getItems = useGetItems(props)
28
25
 
29
26
  const fieldProps = computed(() => {
30
- const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['multiple'])
31
- if (props.modelValue.options.readOnly) fieldProps.menuProps = { modelValue: false }
27
+ const fieldProps = getSelectProps(props.modelValue, props.statefulLayout)
32
28
  fieldProps.noFilter = true
33
29
  fieldProps['onUpdate:search'] = (/** @type string */searchValue) => {
34
- search.value = searchValue
35
- refresh()
30
+ getItems.search.value = searchValue
36
31
  }
37
- fieldProps['onUpdate:menu'] = refresh
38
- fieldProps.items = items.value
39
- fieldProps.loading = loading.value
32
+ fieldProps.items = getItems.items.value
33
+ fieldProps.loading = getItems.loading.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, getSelectSlots(props.modelValue, props.statefulLayout, getItems))
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,7 +1,10 @@
1
1
  <script setup>
2
- import { VCheckbox } from 'vuetify/components'
2
+ import { VCheckbox } from 'vuetify/components/VCheckbox'
3
3
  import { computed } from 'vue'
4
4
  import { getInputProps } from '../../utils/index.js'
5
+ import { useDefaults } from 'vuetify'
6
+
7
+ useDefaults({}, 'VjsfCheckbox')
5
8
 
6
9
  const props = defineProps({
7
10
  modelValue: {
@@ -1,8 +1,11 @@
1
1
  <script setup>
2
2
  import TextFieldMenu from '../fragments/text-field-menu.vue'
3
- import { VColorPicker } from 'vuetify/components'
3
+ import { VColorPicker } from 'vuetify/components/VColorPicker'
4
4
  import { computed } from 'vue'
5
5
  import { getCompProps } from '../../utils/index.js'
6
+ import { useDefaults } from 'vuetify'
7
+
8
+ useDefaults({}, 'VjsfColorPicker')
6
9
 
7
10
  const props = defineProps({
8
11
  modelValue: {
@@ -18,7 +21,8 @@ const props = defineProps({
18
21
  })
19
22
 
20
23
  const colorPickerProps = computed(() => {
21
- const colorPickerProps = getCompProps(props.modelValue, 'colorPicker', true)
24
+ const colorPickerProps = getCompProps(props.modelValue, true)
25
+ colorPickerProps.modelValue = props.modelValue.data
22
26
  return colorPickerProps
23
27
  })
24
28
  </script>
@@ -1,7 +1,8 @@
1
1
  <script>
2
2
  import { defineComponent, h, computed, shallowRef, ref } from 'vue'
3
- import { VCombobox } from 'vuetify/components'
3
+ import { VCombobox } from 'vuetify/components/VCombobox'
4
4
  import { getInputProps, getCompSlots } from '../../utils/index.js'
5
+ import { useDefaults } from 'vuetify'
5
6
 
6
7
  export default defineComponent({
7
8
  props: {
@@ -17,6 +18,8 @@ export default defineComponent({
17
18
  }
18
19
  },
19
20
  setup (props) {
21
+ useDefaults({}, 'VjsfCombobox')
22
+
20
23
  /** @type import('vue').Ref<import('@json-layout/vocabulary').SelectItems> */
21
24
  const items = shallowRef(props.modelValue.layout.items ?? [])
22
25
  /** @type import('vue').Ref<boolean> */
@@ -1,9 +1,11 @@
1
1
  <script setup>
2
2
  import TextFieldMenu from '../fragments/text-field-menu.vue'
3
3
  import { VDatePicker } from 'vuetify/components/VDatePicker'
4
- import { useDate } from 'vuetify'
5
- import { computed } from 'vue'
6
- import { getCompProps, getDateTimeParts } from '../../utils/index.js'
4
+ import { useDate, useDefaults } from 'vuetify'
5
+ import { computed, ref } from 'vue'
6
+ import { getCompProps, getDateTimeParts, getDateTimeWithOffset } from '../../utils/index.js'
7
+
8
+ useDefaults({}, 'VjsfDatePicker')
7
9
 
8
10
  const props = defineProps({
9
11
  modelValue: {
@@ -20,25 +22,32 @@ const props = defineProps({
20
22
 
21
23
  const vDate = useDate()
22
24
 
25
+ const menuOpened = ref(false)
26
+
23
27
  const datePickerProps = computed(() => {
24
- const datePickerProps = getCompProps(props.modelValue, 'datePicker', true)
28
+ const datePickerProps = getCompProps(props.modelValue, true)
25
29
  datePickerProps.hideActions = true
26
30
  if (props.modelValue.data) datePickerProps.modelValue = new Date(props.modelValue.data)
31
+ datePickerProps['onUpdate:modelValue'] = (/** @type {Date} */value) => {
32
+ if (!value) return
33
+ if (props.modelValue.layout.format === 'date-time') {
34
+ props.statefulLayout.input(props.modelValue, getDateTimeWithOffset(value))
35
+ } else {
36
+ props.statefulLayout.input(props.modelValue, getDateTimeParts(/** @type Date */(/** @type unknown */(value)))[0])
37
+ }
38
+ menuOpened.value = false
39
+ }
27
40
  return datePickerProps
28
41
  })
29
42
  </script>
30
43
 
31
44
  <template>
32
45
  <text-field-menu
46
+ v-model:menu-opened="menuOpened"
33
47
  :model-value="modelValue"
34
48
  :stateful-layout="statefulLayout"
35
49
  :formatted-value="modelValue.data && vDate.format(modelValue.data, 'fullDateWithWeekday')"
36
50
  >
37
- <template #default="{close}">
38
- <v-date-picker
39
- v-bind="datePickerProps"
40
- @update:model-value="value => {statefulLayout.input(modelValue, value && getDateTimeParts(/** @type Date */(/** @type unknown */(value)))[0]); close()}"
41
- />
42
- </template>
51
+ <v-date-picker v-bind="datePickerProps" />
43
52
  </text-field-menu>
44
53
  </template>
@@ -1,8 +1,19 @@
1
1
  <script setup>
2
+ import TextFieldMenu from '../fragments/text-field-menu.vue'
3
+ import { VDatePicker } from 'vuetify/components/VDatePicker'
4
+ import { VTimePicker } from 'vuetify/labs/VTimePicker'
5
+ import { VTabs, VTab, VTabsWindow, VTabsWindowItem } from 'vuetify/components/VTabs'
6
+ import { VIcon } from 'vuetify/components/VIcon'
7
+ import { VSheet } from 'vuetify/components/VSheet'
8
+ import { useDate, useDefaults } from 'vuetify'
9
+ import { computed, ref, watch } from 'vue'
10
+ import { getCompProps, getDateTimeParts, getDateTimeWithOffset, getShortTime } from '../../utils/index.js'
2
11
 
3
- defineProps({
12
+ useDefaults({}, 'VjsfDatePicker')
13
+
14
+ const props = defineProps({
4
15
  modelValue: {
5
- /** @type import('vue').PropType<import('../../types.js').VjsfDateTimePickerNode> */
16
+ /** @type import('vue').PropType<import('../../types.js').VjsfDatePickerNode> */
6
17
  type: Object,
7
18
  required: true
8
19
  },
@@ -13,8 +24,74 @@ defineProps({
13
24
  }
14
25
  })
15
26
 
27
+ const vDate = useDate()
28
+
29
+ const tab = ref('date')
30
+ const menuOpened = ref(false)
31
+ watch(menuOpened, () => { tab.value = 'date' })
32
+
33
+ const datePickerProps = computed(() => {
34
+ const datePickerProps = getCompProps(props.modelValue, false)
35
+ datePickerProps.hideActions = true
36
+ if (props.modelValue.data) datePickerProps.modelValue = new Date(props.modelValue.data)
37
+ datePickerProps['onUpdate:modelValue'] = (/** @type {Date} */value) => {
38
+ if (!value) return
39
+
40
+ if (props.modelValue.data) {
41
+ // replace date part of current value
42
+ const datePart = value && getDateTimeParts(/** @type Date */(/** @type unknown */(value)))[0]
43
+ props.statefulLayout.input(props.modelValue, datePart + props.modelValue.data.slice(10))
44
+ } else {
45
+ props.statefulLayout.input(props.modelValue, getDateTimeWithOffset(value))
46
+ }
47
+ tab.value = 'time'
48
+ }
49
+ return datePickerProps
50
+ })
51
+
52
+ const timePickerProps = computed(() => {
53
+ const timePickerProps = getCompProps(props.modelValue, false)
54
+ timePickerProps['ampm-in-title'] = true
55
+ if (props.modelValue.data) timePickerProps.modelValue = getShortTime(props.modelValue.data.slice(11))
56
+ timePickerProps['onUpdate:modelValue'] = (/** @type {string} */value) => {
57
+ if (!props.modelValue.data) return
58
+ console.log('set time', value, props.modelValue.data.slice(0, 10), props.modelValue.data.slice(15))
59
+ props.statefulLayout.input(props.modelValue, props.modelValue.data.slice(0, 11) + value + props.modelValue.data.slice(16))
60
+ }
61
+ return timePickerProps
62
+ })
16
63
  </script>
17
64
 
18
65
  <template>
19
- TODO date-time
66
+ <text-field-menu
67
+ v-model:menu-opened="menuOpened"
68
+ :model-value="modelValue"
69
+ :stateful-layout="statefulLayout"
70
+ :formatted-value="modelValue.data && vDate.format(modelValue.data, 'fullDateTime')"
71
+ >
72
+ <v-sheet style="width: 328px">
73
+ <v-tabs
74
+ v-model="tab"
75
+ align-tabs="center"
76
+ >
77
+ <v-tab value="date">
78
+ <v-icon>mdi-calendar</v-icon>
79
+ </v-tab>
80
+ <v-tab
81
+ value="time"
82
+ :disabled="!modelValue.data"
83
+ >
84
+ <v-icon>mdi-clock</v-icon>
85
+ </v-tab>
86
+ </v-tabs>
87
+ <v-tabs-window v-model="tab">
88
+ <v-tabs-window-item value="date">
89
+ <v-date-picker v-bind="datePickerProps" />
90
+ </v-tabs-window-item>
91
+ <v-tabs-window-item value="time">
92
+ <v-time-picker v-bind="timePickerProps" />
93
+ </v-tabs-window-item>
94
+ </v-tabs-window>
95
+ </v-sheet>
96
+ </text-field-menu>
20
97
  </template>
@@ -1,8 +1,15 @@
1
1
  <script setup>
2
- import { VExpansionPanels, VExpansionPanel, VExpansionPanelTitle, VContainer, VRow, VIcon } from 'vuetify/components'
2
+ import { VExpansionPanels, VExpansionPanel, VExpansionPanelTitle, VExpansionPanelText } from 'vuetify/components/VExpansionPanel'
3
+ import { VContainer, VRow } from 'vuetify/components/VGrid'
4
+ import { VIcon } from 'vuetify/components/VIcon'
3
5
  import { isSection } from '@json-layout/core'
4
6
  import Node from '../node.vue'
5
7
  import SectionHeader from '../fragments/section-header.vue'
8
+ import ChildSubtitle from '../fragments/child-subtitle.vue'
9
+ import { getCompProps } from '../../utils/index.js'
10
+ import { useDefaults } from 'vuetify'
11
+
12
+ useDefaults({}, 'VjsfExpansionPanels')
6
13
 
7
14
  defineProps({
8
15
  modelValue: {
@@ -21,7 +28,7 @@ defineProps({
21
28
 
22
29
  <template>
23
30
  <section-header :node="modelValue" />
24
- <v-expansion-panels>
31
+ <v-expansion-panels v-bind="getCompProps(modelValue, true)">
25
32
  <v-expansion-panel
26
33
  v-for="(child, i) of modelValue.children"
27
34
  :key="child.key"
@@ -37,16 +44,25 @@ defineProps({
37
44
  </v-icon>
38
45
  {{ child.layout.title ?? child.layout.label }}
39
46
  </v-expansion-panel-title>
40
- <v-container fluid>
41
- <v-row>
42
- <node
43
- v-for="grandChild of isSection(child) ? child.children : [child]"
44
- :key="grandChild.fullKey"
45
- :model-value="/** @type import('../../types.js').VjsfNode */(grandChild)"
46
- :stateful-layout="statefulLayout"
47
- />
48
- </v-row>
49
- </v-container>
47
+ <v-expansion-panel-text>
48
+ <v-container fluid>
49
+ <child-subtitle :model-value="child" />
50
+ <v-row :dense="modelValue.options?.density === 'compact' || modelValue.options?.density === 'comfortable'">
51
+ <node
52
+ v-for="grandChild of isSection(child) ? child.children : [child]"
53
+ :key="grandChild.fullKey"
54
+ :model-value="/** @type import('../../types.js').VjsfNode */(grandChild)"
55
+ :stateful-layout="statefulLayout"
56
+ />
57
+ </v-row>
58
+ </v-container>
59
+ </v-expansion-panel-text>
50
60
  </v-expansion-panel>
51
61
  </v-expansion-panels>
52
62
  </template>
63
+
64
+ <style>
65
+ .vjsf-node-expansion-panels .v-expansion-panel-text__wrapper {
66
+ padding: 0;
67
+ }
68
+ </style>
@@ -1,7 +1,8 @@
1
1
  <script>
2
2
  import { defineComponent, h, computed } from 'vue'
3
- import { VFileInput } from 'vuetify/components'
3
+ import { VFileInput } from 'vuetify/components/VFileInput'
4
4
  import { getInputProps, getCompSlots } from '../../utils/index.js'
5
+ import { useDefaults } from 'vuetify'
5
6
 
6
7
  export default defineComponent({
7
8
  props: {
@@ -17,6 +18,8 @@ export default defineComponent({
17
18
  }
18
19
  },
19
20
  setup (props) {
21
+ useDefaults({}, 'VjsfFileInput')
22
+
20
23
  const fieldProps = computed(() => {
21
24
  const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['placeholder', 'accept'])
22
25
  if (props.modelValue.layout.multiple) {