@koumoul/vjsf 2.22.0 → 3.0.0-alpha.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 (138) hide show
  1. package/package.json +50 -86
  2. package/src/compat/v2.js +82 -0
  3. package/src/compile/index.js +32 -0
  4. package/src/compile/v-jsf-compiled.vue.ejs +83 -0
  5. package/src/components/fragments/node-slot.vue +49 -0
  6. package/src/components/fragments/section-header.vue +52 -0
  7. package/src/components/fragments/text-field-menu.vue +68 -0
  8. package/src/components/node.vue +60 -0
  9. package/src/components/nodes/checkbox.vue +27 -0
  10. package/src/components/nodes/color-picker.vue +43 -0
  11. package/src/components/nodes/date-picker.vue +45 -0
  12. package/src/components/nodes/date-time-picker.vue +20 -0
  13. package/src/components/nodes/expansion-panels.vue +52 -0
  14. package/src/components/nodes/list.vue +112 -0
  15. package/src/components/nodes/number-field.vue +35 -0
  16. package/src/components/nodes/one-of-select.vue +56 -0
  17. package/src/components/nodes/section.vue +30 -0
  18. package/src/components/nodes/select.vue +59 -0
  19. package/src/components/nodes/slider.vue +34 -0
  20. package/src/components/nodes/switch.vue +29 -0
  21. package/src/components/nodes/tabs.vue +63 -0
  22. package/src/components/nodes/text-field.vue +29 -0
  23. package/src/components/nodes/textarea.vue +29 -0
  24. package/src/components/nodes/time-picker.vue +7 -0
  25. package/src/components/nodes/vertical-tabs.vue +70 -0
  26. package/src/components/options.js +25 -0
  27. package/src/components/tree.vue +25 -0
  28. package/src/components/types.ts +59 -0
  29. package/src/components/vjsf.vue +168 -0
  30. package/src/index.js +2 -0
  31. package/src/utils/clone.js +3 -0
  32. package/src/utils/dates.js +52 -0
  33. package/src/utils/props.js +79 -0
  34. package/src/utils/slots.js +19 -0
  35. package/types/compat/v2.d.ts +10 -0
  36. package/types/compat/v2.d.ts.map +1 -0
  37. package/types/compile/index.d.ts +7 -0
  38. package/types/compile/index.d.ts.map +1 -0
  39. package/types/components/fragments/node-slot.vue.d.ts +47 -0
  40. package/types/components/fragments/node-slot.vue.d.ts.map +1 -0
  41. package/types/components/fragments/section-header.vue.d.ts +8 -0
  42. package/types/components/fragments/section-header.vue.d.ts.map +1 -0
  43. package/types/components/fragments/text-field-menu.vue.d.ts +20 -0
  44. package/types/components/fragments/text-field-menu.vue.d.ts.map +1 -0
  45. package/types/components/node.vue.d.ts +10 -0
  46. package/types/components/node.vue.d.ts.map +1 -0
  47. package/types/components/nodes/checkbox.vue.d.ts +10 -0
  48. package/types/components/nodes/checkbox.vue.d.ts.map +1 -0
  49. package/types/components/nodes/color-picker.vue.d.ts +10 -0
  50. package/types/components/nodes/color-picker.vue.d.ts.map +1 -0
  51. package/types/components/nodes/date-picker.vue.d.ts +10 -0
  52. package/types/components/nodes/date-picker.vue.d.ts.map +1 -0
  53. package/types/components/nodes/date-time-picker.vue.d.ts +10 -0
  54. package/types/components/nodes/date-time-picker.vue.d.ts.map +1 -0
  55. package/types/components/nodes/expansion-panels.vue.d.ts +10 -0
  56. package/types/components/nodes/expansion-panels.vue.d.ts.map +1 -0
  57. package/types/components/nodes/list.vue.d.ts +10 -0
  58. package/types/components/nodes/list.vue.d.ts.map +1 -0
  59. package/types/components/nodes/number-field.vue.d.ts +27 -0
  60. package/types/components/nodes/number-field.vue.d.ts.map +1 -0
  61. package/types/components/nodes/one-of-select.vue.d.ts +10 -0
  62. package/types/components/nodes/one-of-select.vue.d.ts.map +1 -0
  63. package/types/components/nodes/section.vue.d.ts +10 -0
  64. package/types/components/nodes/section.vue.d.ts.map +1 -0
  65. package/types/components/nodes/select.vue.d.ts +10 -0
  66. package/types/components/nodes/select.vue.d.ts.map +1 -0
  67. package/types/components/nodes/slider.vue.d.ts +10 -0
  68. package/types/components/nodes/slider.vue.d.ts.map +1 -0
  69. package/types/components/nodes/switch.vue.d.ts +10 -0
  70. package/types/components/nodes/switch.vue.d.ts.map +1 -0
  71. package/types/components/nodes/tabs.vue.d.ts +10 -0
  72. package/types/components/nodes/tabs.vue.d.ts.map +1 -0
  73. package/types/components/nodes/text-field copy.vue.d.ts +10 -0
  74. package/types/components/nodes/text-field copy.vue.d.ts.map +1 -0
  75. package/types/components/nodes/text-field.vue.d.ts +27 -0
  76. package/types/components/nodes/text-field.vue.d.ts.map +1 -0
  77. package/types/components/nodes/textarea.vue.d.ts +27 -0
  78. package/types/components/nodes/textarea.vue.d.ts.map +1 -0
  79. package/types/components/nodes/time-picker.vue.d.ts +3 -0
  80. package/types/components/nodes/time-picker.vue.d.ts.map +1 -0
  81. package/types/components/nodes/vertical-tabs.vue.d.ts +10 -0
  82. package/types/components/nodes/vertical-tabs.vue.d.ts.map +1 -0
  83. package/types/components/options.d.ts +3 -0
  84. package/types/components/options.d.ts.map +1 -0
  85. package/types/components/tree.vue.d.ts +10 -0
  86. package/types/components/tree.vue.d.ts.map +1 -0
  87. package/types/components/types.d.ts +71 -0
  88. package/types/components/types.d.ts.map +1 -0
  89. package/types/components/v-jsf.vue.d.ts +13 -0
  90. package/types/components/v-jsf.vue.d.ts.map +1 -0
  91. package/types/components/vjsf.vue.d.ts +13 -0
  92. package/types/components/vjsf.vue.d.ts.map +1 -0
  93. package/types/index.d.ts +3 -0
  94. package/types/index.d.ts.map +1 -0
  95. package/types/utils/clone.d.ts +3 -0
  96. package/types/utils/clone.d.ts.map +1 -0
  97. package/types/utils/dates.d.ts +7 -0
  98. package/types/utils/dates.d.ts.map +1 -0
  99. package/types/utils/props.d.ts +20 -0
  100. package/types/utils/props.d.ts.map +1 -0
  101. package/types/utils/slots.d.ts +7 -0
  102. package/types/utils/slots.d.ts.map +1 -0
  103. package/.eslintignore +0 -9
  104. package/.eslintrc.js +0 -38
  105. package/.github/workflows/scrape-doc.yml +0 -14
  106. package/.nvmrc +0 -1
  107. package/CONTRIBUTE.md +0 -61
  108. package/FUNDING.yml +0 -1
  109. package/README.md +0 -19
  110. package/babel.config.js +0 -4
  111. package/dist/main.css +0 -63
  112. package/dist/main.js +0 -2
  113. package/dist/main.js.LICENSE.txt +0 -10
  114. package/dist/third-party.js +0 -8
  115. package/dist/third-party.js.LICENSE.txt +0 -8
  116. package/lib/VJsf.css +0 -63
  117. package/lib/VJsf.js +0 -117
  118. package/lib/VJsfNoDeps.js +0 -517
  119. package/lib/deps/third-party.js +0 -16
  120. package/lib/mixins/ColorProperty.js +0 -45
  121. package/lib/mixins/DateProperty.js +0 -170
  122. package/lib/mixins/Dependent.js +0 -69
  123. package/lib/mixins/EditableArray.js +0 -418
  124. package/lib/mixins/FileProperty.js +0 -81
  125. package/lib/mixins/MarkdownEditor.js +0 -183
  126. package/lib/mixins/ObjectContainer.js +0 -351
  127. package/lib/mixins/SelectProperty.js +0 -400
  128. package/lib/mixins/SimpleProperty.js +0 -165
  129. package/lib/mixins/Tooltip.js +0 -42
  130. package/lib/mixins/Validatable.js +0 -119
  131. package/lib/utils/expr-eval-parser.js +0 -21
  132. package/lib/utils/is-cyclic.js +0 -34
  133. package/lib/utils/json-refs.js +0 -209
  134. package/lib/utils/options.js +0 -328
  135. package/lib/utils/rules.js +0 -81
  136. package/lib/utils/schema.js +0 -100
  137. package/lib/utils/select.js +0 -141
  138. package/webpack.config.js +0 -46
@@ -0,0 +1,45 @@
1
+ <script setup>
2
+ import TextFieldMenu from '../fragments/text-field-menu.vue'
3
+ import { VDatePicker } from 'vuetify/labs/VDatePicker'
4
+ import { useDate } from 'vuetify/labs/date'
5
+ import { computed } from 'vue'
6
+ import { getCompProps } from '../../utils/props.js'
7
+ import { getDateTimeParts } from '../../utils/dates.js'
8
+
9
+ const props = defineProps({
10
+ modelValue: {
11
+ /** @type import('vue').PropType<import('../types.js').VjsfDatePickerNode> */
12
+ type: Object,
13
+ required: true
14
+ },
15
+ statefulLayout: {
16
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
17
+ type: Object,
18
+ required: true
19
+ }
20
+ })
21
+
22
+ const vDate = useDate()
23
+
24
+ const datePickerProps = computed(() => {
25
+ const datePickerProps = getCompProps(props.modelValue, 'datePicker', true)
26
+ datePickerProps.hideActions = true
27
+ if (props.modelValue.data) datePickerProps.modelValue = new Date(props.modelValue.data)
28
+ return datePickerProps
29
+ })
30
+ </script>
31
+
32
+ <template>
33
+ <text-field-menu
34
+ :model-value="modelValue"
35
+ :stateful-layout="statefulLayout"
36
+ :formatted-value="modelValue.data && vDate.format(modelValue.data, 'fullDateWithWeekday')"
37
+ >
38
+ <template #default="{close}">
39
+ <v-date-picker
40
+ v-bind="datePickerProps"
41
+ @update:model-value="value => {statefulLayout.input(modelValue, value && getDateTimeParts(/** @type Date */(/** @type unknown */(value)))[0]); close()}"
42
+ />
43
+ </template>
44
+ </text-field-menu>
45
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup>
2
+
3
+ defineProps({
4
+ modelValue: {
5
+ /** @type import('vue').PropType<import('../types.js').VjsfDateTimePickerNode> */
6
+ type: Object,
7
+ required: true
8
+ },
9
+ statefulLayout: {
10
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
11
+ type: Object,
12
+ required: true
13
+ }
14
+ })
15
+
16
+ </script>
17
+
18
+ <template>
19
+ TODO date-time
20
+ </template>
@@ -0,0 +1,52 @@
1
+ <script setup>
2
+ import { VExpansionPanels, VExpansionPanel, VExpansionPanelTitle, VContainer } from 'vuetify/components'
3
+ import { isSection } from '@json-layout/core'
4
+ import Node from '../node.vue'
5
+ import SectionHeader from '../fragments/section-header.vue'
6
+
7
+ defineProps({
8
+ modelValue: {
9
+ /** @type import('vue').PropType<import('../types.js').VjsfExpansionPanelsNode> */
10
+ type: Object,
11
+ required: true
12
+ },
13
+ statefulLayout: {
14
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
15
+ type: Object,
16
+ required: true
17
+ }
18
+ })
19
+
20
+ </script>
21
+
22
+ <template>
23
+ <section-header :node="modelValue" />
24
+ <v-expansion-panels>
25
+ <v-expansion-panel
26
+ v-for="(child, i) of modelValue.children"
27
+ :key="child.key"
28
+ :value="i"
29
+ >
30
+ <v-expansion-panel-title>
31
+ <v-icon
32
+ v-if="child.validated && (child.error || child.childError)"
33
+ color="error"
34
+ class="mr-2"
35
+ >
36
+ mdi-alert
37
+ </v-icon>
38
+ {{ child.layout.title ?? child.layout.label }}
39
+ </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>
50
+ </v-expansion-panel>
51
+ </v-expansion-panels>
52
+ </template>
@@ -0,0 +1,112 @@
1
+ <script setup>
2
+ import { VList, VListItem, VListItemAction, VBtn, VMenu, VIcon } from 'vuetify/components'
3
+ import { isSection } from '@json-layout/core'
4
+ import Node from '../node.vue'
5
+ import clone from '../../utils/clone.js'
6
+
7
+ defineProps({
8
+ modelValue: {
9
+ /** @type import('vue').PropType<import('../types.js').VjsfListNode> */
10
+ type: Object,
11
+ required: true
12
+ },
13
+ statefulLayout: {
14
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
15
+ type: Object,
16
+ required: true
17
+ }
18
+ })
19
+
20
+ </script>
21
+
22
+ <template>
23
+ <v-list :density="modelValue.options.density">
24
+ <v-list-subheader v-if="modelValue.layout.title">
25
+ {{ modelValue.layout.title }}
26
+ </v-list-subheader>
27
+ <v-list-item
28
+ v-for="(child, childIndex) of modelValue.children"
29
+ :key="child.key"
30
+ >
31
+ <v-row>
32
+ <node
33
+ v-for="grandChild of isSection(child) ? child.children : [child]"
34
+ :key="grandChild.fullKey"
35
+ :model-value="/** @type import('../types.js').VjsfNode */(grandChild)"
36
+ :stateful-layout="statefulLayout"
37
+ />
38
+ </v-row>
39
+ <template #append>
40
+ <v-list-item-action
41
+ v-if="modelValue.layout.listActions.includes('edit') && modelValue.layout.listEditMode === 'inline-single'"
42
+ class="ml-1"
43
+ >
44
+ <v-btn
45
+ v-if="child.options.readOnly"
46
+ :title="modelValue.messages.edit"
47
+ icon="mdi-pencil"
48
+ variant="text"
49
+ color="primary"
50
+ :density="modelValue.options.density"
51
+ @click="statefulLayout.activateItem(modelValue, childIndex)"
52
+ />
53
+ <v-btn
54
+ v-else
55
+ :title="modelValue.messages.edit"
56
+ icon="mdi-pencil"
57
+ variant="flat"
58
+ color="primary"
59
+ :density="modelValue.options.density"
60
+ @click="statefulLayout.deactivateItem(modelValue)"
61
+ />
62
+ </v-list-item-action>
63
+ <v-list-item-action
64
+ v-if="modelValue.layout.listActions.includes('delete') || modelValue.layout.listActions.includes('duplicate')"
65
+ class="ml-1"
66
+ >
67
+ <v-menu location="bottom end">
68
+ <template #activator="{props: activatorProps}">
69
+ <v-btn
70
+ v-bind="activatorProps"
71
+ icon="mdi-dots-vertical"
72
+ variant="plain"
73
+ :density="modelValue.options.density"
74
+ />
75
+ </template>
76
+ <v-list :density="modelValue.options.density">
77
+ <v-list-item
78
+ v-if="modelValue.layout.listActions.includes('delete')"
79
+ base-color="warning"
80
+ @click="statefulLayout.input(modelValue, [...modelValue.data.slice(0, childIndex), ...modelValue.data.slice(childIndex + 1)])"
81
+ >
82
+ <template #prepend>
83
+ <v-icon icon="mdi-delete" />
84
+ </template>
85
+ {{ modelValue.messages.delete }}
86
+ </v-list-item>
87
+
88
+ <v-list-item
89
+ v-if="modelValue.layout.listActions.includes('duplicate')"
90
+ @click="statefulLayout.input(modelValue, [...modelValue.data.slice(0, childIndex), clone(child.data), ...modelValue.data.slice(childIndex)])"
91
+ >
92
+ <template #prepend>
93
+ <v-icon icon="mdi-content-duplicate" />
94
+ </template>
95
+ {{ modelValue.messages.duplicate }}
96
+ </v-list-item>
97
+ </v-list>
98
+ </v-menu>
99
+ </v-list-item-action>
100
+ </template>
101
+ </v-list-item>
102
+ <v-list-item v-if="!modelValue.options.readOnly && modelValue.layout.listActions.includes('add')">
103
+ <v-btn
104
+ color="primary"
105
+ :density="modelValue.options.density"
106
+ @click="statefulLayout.input(modelValue, (modelValue.data || []).concat([clone(modelValue.skeleton.childrenTrees?.[0]?.root.defaultData)]), (modelValue.data || []).length)"
107
+ >
108
+ {{ modelValue.messages.addItem }}
109
+ </v-btn>
110
+ </v-list-item>
111
+ </v-list>
112
+ </template>
@@ -0,0 +1,35 @@
1
+ <script>
2
+ import { defineComponent, h, computed } from 'vue'
3
+ import { VTextField } from 'vuetify/components'
4
+ import { getInputProps } from '../../utils/props.js'
5
+ import { getCompSlots } from '../../utils/slots.js'
6
+
7
+ export default defineComponent({
8
+ props: {
9
+ modelValue: {
10
+ /** @type import('vue').PropType<import('../types.js').VjsfNumberFieldNode> */
11
+ type: Object,
12
+ required: true
13
+ },
14
+ statefulLayout: {
15
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
16
+ type: Object,
17
+ required: true
18
+ }
19
+ },
20
+ setup (props) {
21
+ const fieldProps = computed(() => {
22
+ const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
23
+ fieldProps.type = 'number'
24
+ if ('step' in props.modelValue.layout) fieldProps.step = props.modelValue.layout.step
25
+ fieldProps['onUpdate:modelValue'] = (/** @type string */value) => props.statefulLayout.input(props.modelValue, value && Number(value))
26
+ return fieldProps
27
+ })
28
+ const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
29
+
30
+ // @ts-ignore
31
+ return () => h(VTextField, fieldProps.value, fieldSlots.value)
32
+ }
33
+ })
34
+
35
+ </script>
@@ -0,0 +1,56 @@
1
+ <script setup>
2
+ import { VSelect } from 'vuetify/components'
3
+ import { shallowRef, watch } from 'vue'
4
+ import { isSection } from '@json-layout/core'
5
+ import Node from '../node.vue'
6
+
7
+ const props = defineProps({
8
+ modelValue: {
9
+ /** @type import('vue').PropType<import('../types.js').VjsfOneOfSelectNode> */
10
+ type: Object,
11
+ required: true
12
+ },
13
+ statefulLayout: {
14
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
15
+ type: Object,
16
+ required: true
17
+ }
18
+ })
19
+
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
+ } else {
27
+ activeChildTree.value = undefined
28
+ }
29
+ }, { immediate: true })
30
+
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))
34
+ }
35
+
36
+ </script>
37
+
38
+ <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
+ />
55
+ </v-row>
56
+ </template>
@@ -0,0 +1,30 @@
1
+ <script setup>
2
+ import Node from '../node.vue'
3
+ import SectionHeader from '../fragments/section-header.vue'
4
+
5
+ defineProps({
6
+ modelValue: {
7
+ /** @type import('vue').PropType<import('../types.js').VjsfSectionNode> */
8
+ type: Object,
9
+ required: true
10
+ },
11
+ statefulLayout: {
12
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
13
+ type: Object,
14
+ required: true
15
+ }
16
+ })
17
+
18
+ </script>
19
+
20
+ <template>
21
+ <section-header :node="modelValue" />
22
+ <v-row :dense="modelValue.options?.density === 'compact' || modelValue.options?.density === 'comfortable'">
23
+ <node
24
+ v-for="child of modelValue.children"
25
+ :key="child.fullKey"
26
+ :model-value="/** @type import('../types.js').VjsfNode */(child)"
27
+ :stateful-layout="statefulLayout"
28
+ />
29
+ </v-row>
30
+ </template>
@@ -0,0 +1,59 @@
1
+ <script setup>
2
+ import { VSelect } from 'vuetify/components'
3
+ import { computed, ref } from 'vue'
4
+ import { getInputProps } from '../../utils/props.js'
5
+
6
+ const props = defineProps({
7
+ modelValue: {
8
+ /** @type import('vue').PropType<import('../types.js').VjsfSelectNode> */
9
+ type: Object,
10
+ required: true
11
+ },
12
+ statefulLayout: {
13
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
14
+ type: Object,
15
+ required: true
16
+ }
17
+ })
18
+
19
+ const fieldProps = computed(() => {
20
+ const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
21
+ if (props.modelValue.options.readOnly) fieldProps.menuProps = { modelValue: false }
22
+ return fieldProps
23
+ })
24
+
25
+ /** @type import('vue').Ref<import('@json-layout/vocabulary').SelectItems> */
26
+ const items = ref(props.modelValue.layout.items ?? [])
27
+ /** @type import('vue').Ref<boolean> */
28
+ const loading = ref(false)
29
+
30
+ /** @type import('@json-layout/core').StateTree | null */
31
+ let lastStateTree = null
32
+ /** @type Record<string, any> | null */
33
+ let lastContext = null
34
+
35
+ const refresh = async () => {
36
+ if (props.modelValue.layout.items) return
37
+ if (props.statefulLayout.stateTree === lastStateTree && props.statefulLayout.options.context === lastContext) return
38
+ lastStateTree = props.statefulLayout.stateTree
39
+ lastContext = props.statefulLayout.options.context ?? null
40
+ loading.value = true
41
+ items.value = await props.statefulLayout.getSelectItems(props.modelValue)
42
+ loading.value = false
43
+ }
44
+
45
+ if (!props.modelValue.layout.items) {
46
+ refresh()
47
+ }
48
+
49
+ </script>
50
+
51
+ <template>
52
+ <v-select
53
+ v-bind="fieldProps"
54
+ :loading="loading"
55
+ :items="items"
56
+ @update:model-value="value => statefulLayout.input(modelValue, value)"
57
+ @update:menu="refresh"
58
+ />
59
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup>
2
+ import { VSlider } from 'vuetify/components'
3
+ import { computed } from 'vue'
4
+ import { getInputProps } from '../../utils/props.js'
5
+
6
+ const props = defineProps({
7
+ modelValue: {
8
+ /** @type import('vue').PropType<import('../types.js').VjsfSliderNode> */
9
+ type: Object,
10
+ required: true
11
+ },
12
+ statefulLayout: {
13
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
14
+ type: Object,
15
+ required: true
16
+ }
17
+ })
18
+
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
25
+ })
26
+ </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>
@@ -0,0 +1,29 @@
1
+ <script setup>
2
+ import { VSwitch } from 'vuetify/components'
3
+ import { computed } from 'vue'
4
+ import { getInputProps } from '../../utils/props.js'
5
+
6
+ const props = defineProps({
7
+ modelValue: {
8
+ /** @type import('vue').PropType<import('../types.js').VjsfSwitchNode> */
9
+ type: Object,
10
+ required: true
11
+ },
12
+ statefulLayout: {
13
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
14
+ type: Object,
15
+ required: true
16
+ }
17
+ })
18
+
19
+ const fieldProps = computed(() =>
20
+ getInputProps(props.modelValue, props.statefulLayout)
21
+ )
22
+ </script>
23
+
24
+ <template>
25
+ <v-switch
26
+ v-bind="fieldProps"
27
+ @update:model-value="value => statefulLayout.input(modelValue, value)"
28
+ />
29
+ </template>
@@ -0,0 +1,63 @@
1
+ <script setup>
2
+ import { VTabs, VTab, VContainer } from 'vuetify/components'
3
+ import { ref } from 'vue'
4
+ import { isSection } from '@json-layout/core'
5
+ import Node from '../node.vue'
6
+ import SectionHeader from '../fragments/section-header.vue'
7
+
8
+ defineProps({
9
+ modelValue: {
10
+ /** @type import('vue').PropType<import('../types.js').VjsfTabsNode> */
11
+ type: Object,
12
+ required: true
13
+ },
14
+ statefulLayout: {
15
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
16
+ type: Object,
17
+ required: true
18
+ }
19
+ })
20
+
21
+ const tab = ref(0)
22
+ </script>
23
+
24
+ <template>
25
+ <section-header :node="modelValue" />
26
+ <v-sheet border>
27
+ <v-tabs v-model="tab">
28
+ <v-tab
29
+ v-for="(child, i) of modelValue.children"
30
+ :key="child.key"
31
+ :value="i"
32
+ :density="modelValue.options.density"
33
+ :color="child.validated && (child.error || child.childError) ? 'error' : undefined"
34
+ >
35
+ <v-icon
36
+ v-if="child.validated && (child.error || child.childError)"
37
+ color="error"
38
+ >
39
+ mdi-alert
40
+ </v-icon>
41
+ {{ child.layout.title ?? child.layout.label }}
42
+ </v-tab>
43
+ </v-tabs>
44
+ <v-window v-model="tab">
45
+ <v-window-item
46
+ v-for="(child, i) of modelValue.children"
47
+ :key="child.key"
48
+ :value="i"
49
+ >
50
+ <v-container fluid>
51
+ <v-row>
52
+ <node
53
+ v-for="grandChild of isSection(child) ? child.children : [child]"
54
+ :key="grandChild.fullKey"
55
+ :model-value="/** @type import('../types.js').VjsfNode */(grandChild)"
56
+ :stateful-layout="statefulLayout"
57
+ />
58
+ </v-row>
59
+ </v-container>
60
+ </v-window-item>
61
+ </v-window>
62
+ </v-sheet>
63
+ </template>
@@ -0,0 +1,29 @@
1
+ <script>
2
+ import { defineComponent, h, computed } from 'vue'
3
+ import { VTextField } from 'vuetify/components'
4
+ import { getInputProps } from '../../utils/props.js'
5
+ import { getCompSlots } from '../../utils/slots.js'
6
+
7
+ export default defineComponent({
8
+ props: {
9
+ modelValue: {
10
+ /** @type import('vue').PropType<import('../types.js').VjsfTextFieldNode> */
11
+ type: Object,
12
+ required: true
13
+ },
14
+ statefulLayout: {
15
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
16
+ type: Object,
17
+ required: true
18
+ }
19
+ },
20
+ setup (props) {
21
+ const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout))
22
+ const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
23
+
24
+ // @ts-ignore
25
+ return () => h(VTextField, fieldProps.value, fieldSlots.value)
26
+ }
27
+ })
28
+
29
+ </script>
@@ -0,0 +1,29 @@
1
+ <script>
2
+ import { defineComponent, h, computed } from 'vue'
3
+ import { VTextarea } from 'vuetify/components'
4
+ import { getInputProps } from '../../utils/props.js'
5
+ import { getCompSlots } from '../../utils/slots.js'
6
+
7
+ export default defineComponent({
8
+ props: {
9
+ modelValue: {
10
+ /** @type import('vue').PropType<import('../types.js').VjsfTextareaNode> */
11
+ type: Object,
12
+ required: true
13
+ },
14
+ statefulLayout: {
15
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
16
+ type: Object,
17
+ required: true
18
+ }
19
+ },
20
+ setup (props) {
21
+ const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout))
22
+ const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
23
+
24
+ // @ts-ignore
25
+ return () => h(VTextarea, fieldProps.value, fieldSlots.value)
26
+ }
27
+ })
28
+
29
+ </script>
@@ -0,0 +1,7 @@
1
+ <script setup>
2
+
3
+ </script>
4
+
5
+ <template>
6
+ TODO time
7
+ </template>
@@ -0,0 +1,70 @@
1
+ <script setup>
2
+ import { isSection } from '@json-layout/core'
3
+ import { VTabs, VTab, VContainer } from 'vuetify/components'
4
+ import { ref } from 'vue'
5
+ import Node from '../node.vue'
6
+ import SectionHeader from '../fragments/section-header.vue'
7
+
8
+ defineProps({
9
+ modelValue: {
10
+ /** @type import('vue').PropType<import('../types.js').VjsfVerticalTabsNode> */
11
+ type: Object,
12
+ required: true
13
+ },
14
+ statefulLayout: {
15
+ /** @type import('vue').PropType<import('@json-layout/core').StatefulLayout> */
16
+ type: Object,
17
+ required: true
18
+ }
19
+ })
20
+
21
+ const tab = ref(0)
22
+ </script>
23
+
24
+ <template>
25
+ <section-header :node="modelValue" />
26
+ <v-sheet border>
27
+ <div class="d-flex flex-row">
28
+ <v-tabs
29
+ v-model="tab"
30
+ direction="vertical"
31
+ >
32
+ <v-tab
33
+ v-for="(child, i) of modelValue.children"
34
+ :key="child.key"
35
+ :value="i"
36
+ :color="child.validated && (child.error || child.childError) ? 'error' : undefined"
37
+ >
38
+ <v-icon
39
+ v-if="child.validated && (child.error || child.childError)"
40
+ color="error"
41
+ >
42
+ mdi-alert
43
+ </v-icon>
44
+ {{ child.layout.title ?? child.layout.label }}
45
+ </v-tab>
46
+ </v-tabs>
47
+ <v-window
48
+ v-model="tab"
49
+ class="flex-fill"
50
+ >
51
+ <v-window-item
52
+ v-for="(child, i) of modelValue.children"
53
+ :key="child.key"
54
+ :value="i"
55
+ >
56
+ <v-container fluid>
57
+ <v-row>
58
+ <node
59
+ v-for="grandChild of isSection(child) ? child.children : [child]"
60
+ :key="grandChild.fullKey"
61
+ :model-value="/** @type import('../types.js').VjsfNode */(grandChild)"
62
+ :stateful-layout="statefulLayout"
63
+ />
64
+ </v-row>
65
+ </v-container>
66
+ </v-window-item>
67
+ </v-window>
68
+ </div>
69
+ </v-sheet>
70
+ </template>