@koumoul/vjsf 3.0.0-alpha.1 → 3.0.0-alpha.2

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 (38) hide show
  1. package/package.json +8 -8
  2. package/src/compile/index.js +1 -1
  3. package/src/compile/v-jsf-compiled.vue.ejs +14 -52
  4. package/src/components/nodes/combobox.vue +1 -1
  5. package/src/components/nodes/list.vue +170 -86
  6. package/src/components/nodes/markdown.vue +209 -5
  7. package/src/components/nodes/number-field.vue +1 -1
  8. package/src/components/nodes/stepper.vue +98 -0
  9. package/src/components/nodes/text-field.vue +1 -1
  10. package/src/components/nodes/textarea.vue +1 -1
  11. package/src/components/options.js +22 -1
  12. package/src/components/types.ts +4 -0
  13. package/src/components/vjsf.vue +21 -104
  14. package/src/composables/use-dnd.js +54 -0
  15. package/src/composables/use-vjsf.js +115 -0
  16. package/src/styles/vjsf.css +10 -0
  17. package/src/utils/arrays.js +15 -0
  18. package/src/utils/props.js +16 -5
  19. package/types/components/fragments/select-item.vue.d.ts +2 -2
  20. package/types/components/fragments/select-selection.vue.d.ts +2 -2
  21. package/types/components/nodes/markdown.vue.d.ts.map +1 -1
  22. package/types/components/nodes/stepper.vue.d.ts +10 -0
  23. package/types/components/nodes/stepper.vue.d.ts.map +1 -0
  24. package/types/components/options.d.ts +1 -0
  25. package/types/components/options.d.ts.map +1 -1
  26. package/types/components/tree.vue.d.ts +2 -2
  27. package/types/components/types.d.ts +5 -1
  28. package/types/components/types.d.ts.map +1 -1
  29. package/types/components/vjsf.vue.d.ts +5 -6
  30. package/types/components/vjsf.vue.d.ts.map +1 -1
  31. package/types/composables/use-dnd.d.ts +21 -0
  32. package/types/composables/use-dnd.d.ts.map +1 -0
  33. package/types/composables/use-vjsf.d.ts +17 -0
  34. package/types/composables/use-vjsf.d.ts.map +1 -0
  35. package/types/utils/arrays.d.ts +9 -0
  36. package/types/utils/arrays.d.ts.map +1 -0
  37. package/types/utils/props.d.ts +4 -2
  38. package/types/utils/props.d.ts.map +1 -1
@@ -0,0 +1,98 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { VStepper, VStepperHeader, VStepperItem, VContainer } from 'vuetify/components'
4
+ import { isSection } from '@json-layout/core'
5
+ import Node from '../node.vue'
6
+ import SectionHeader from '../fragments/section-header.vue'
7
+
8
+ const props = defineProps({
9
+ modelValue: {
10
+ /** @type import('vue').PropType<import('../types.js').VjsfStepperNode> */
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 step = ref(0)
22
+
23
+ const firstErrorIndex = computed(() => {
24
+ const index = props.modelValue.children.findIndex(child => child.validated && !!(child.error || child.childError))
25
+ return index === -1 ? props.modelValue.children.length : index
26
+ })
27
+
28
+ const goNext = () => {
29
+ console.log(props.statefulLayout.validationState)
30
+ const child = props.modelValue.children[step.value]
31
+ props.statefulLayout.validateNodeRecurse(child)
32
+ console.log(props.statefulLayout.validationState)
33
+ if (!(child.error || child.childError)) step.value++
34
+ }
35
+ </script>
36
+
37
+ <template>
38
+ <section-header :node="modelValue" />
39
+ <v-stepper v-model="step">
40
+ <v-stepper-header>
41
+ <template
42
+ v-for="(child, i) of modelValue.children"
43
+ :key="child.key"
44
+ >
45
+ <v-stepper-item
46
+ :value="i"
47
+ :title="/** @type {string | undefined} */(child.layout.title ?? child.layout.label)"
48
+ :error="child.validated && !!(child.error || child.childError)"
49
+ :complete="child.validated && !(child.error || child.childError)"
50
+ :editable="i <= firstErrorIndex"
51
+ />
52
+ <v-divider />
53
+ </template>
54
+ </v-stepper-header>
55
+ <v-stepper-window>
56
+ <v-stepper-window-item
57
+ v-for="(child) of modelValue.children"
58
+ :key="child.key"
59
+ >
60
+ <v-container
61
+ fluid
62
+ class="pa-0"
63
+ >
64
+ <v-row>
65
+ <node
66
+ v-for="grandChild of isSection(child) ? child.children : [child]"
67
+ :key="grandChild.fullKey"
68
+ :model-value="/** @type import('../types.js').VjsfNode */(grandChild)"
69
+ :stateful-layout="statefulLayout"
70
+ />
71
+ </v-row>
72
+ </v-container>
73
+ </v-stepper-window-item>
74
+ </v-stepper-window>
75
+ <v-stepper-actions>
76
+ <template #prev>
77
+ <v-btn
78
+ v-if="step > 0"
79
+ variant="text"
80
+ @click="step--"
81
+ >
82
+ Back
83
+ </v-btn>
84
+ </template>
85
+ <template #next>
86
+ <v-spacer />
87
+ <v-btn
88
+ v-if="step < modelValue.children.length - 1"
89
+ variant="flat"
90
+ color="primary"
91
+ @click="goNext"
92
+ >
93
+ Next
94
+ </v-btn>
95
+ </template>
96
+ </v-stepper-actions>
97
+ </v-stepper>
98
+ </template>
@@ -18,7 +18,7 @@ export default defineComponent({
18
18
  }
19
19
  },
20
20
  setup (props) {
21
- const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout))
21
+ const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout, ['placeholder']))
22
22
  const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
23
23
 
24
24
  // @ts-ignore
@@ -18,7 +18,7 @@ export default defineComponent({
18
18
  }
19
19
  },
20
20
  setup (props) {
21
- const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout))
21
+ const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout, ['placeholder']))
22
22
  const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
23
23
 
24
24
  // @ts-ignore
@@ -21,5 +21,26 @@ export const defaultOptions = {
21
21
  checkboxPropsReadOnly: {},
22
22
  switchProps: { hideDetails: 'auto' },
23
23
  switchPropsReadOnly: {},
24
- errorAlertProps: { type: 'error', variant: 'tonal' }
24
+ errorAlertProps: { type: 'error', variant: 'tonal' },
25
+ easyMDEOptions: {}
26
+ }
27
+
28
+ /**
29
+ *
30
+ * @param {Partial<import("./types.js").VjsfOptions>} options
31
+ * @param {any} form
32
+ * @param {number} width
33
+ * @param {import("vue").Slots} slots
34
+ * @returns
35
+ */
36
+ export const getFullOptions = (options, form, width, slots) => {
37
+ const fullOptions = {
38
+ ...defaultOptions,
39
+ readOnly: !!(form && (form.isDisabled.value || form.isReadonly.value)),
40
+ ...options,
41
+ context: options.context ? JSON.parse(JSON.stringify(options.context)) : {},
42
+ width: Math.round(width ?? 0),
43
+ vjsfSlots: { ...slots }
44
+ }
45
+ return /** @type import('./types.js').VjsfOptions */ (fullOptions)
25
46
  }
@@ -17,6 +17,7 @@ import {
17
17
  TextFieldNode,
18
18
  TextareaNode,
19
19
  VerticalTabsNode,
20
+ StepperNode,
20
21
  ComboboxNode,
21
22
  CompileOptions
22
23
  } from '@json-layout/core'
@@ -39,6 +40,7 @@ export type VjsfOptions = StatefulLayoutOptions & CompileOptions & {
39
40
  switchPropsReadOnly: Record<string, unknown>,
40
41
  errorAlertProps: Record<string, unknown>,
41
42
  vjsfSlots: Record<string, () => unknown>,
43
+ easyMDEOptions: Record<string, unknown>,
42
44
  }
43
45
 
44
46
  export type PartialVjsfOptions = Partial<Omit<VjsfOptions, 'width'>>
@@ -60,4 +62,6 @@ export type VjsfSwitchNode = Omit<SwitchNode, 'options'> & {options: VjsfOptions
60
62
  export type VjsfTextFieldNode = Omit<TextFieldNode, 'options'> & {options: VjsfOptions}
61
63
  export type VjsfTextareaNode = Omit<TextareaNode, 'options'> & {options: VjsfOptions}
62
64
  export type VjsfVerticalTabsNode = Omit<VerticalTabsNode, 'options'> & {options: VjsfOptions}
65
+ export type VjsfStepperNode = Omit<StepperNode, 'options'> & {options: VjsfOptions}
66
+
63
67
  export type VjsfComboboxNode = Omit<ComboboxNode, 'options'> & {options: VjsfOptions}
@@ -1,8 +1,10 @@
1
1
  <script setup>
2
- import { ref, shallowRef, computed, getCurrentInstance, watch, useSlots, inject, toRaw } from 'vue'
3
- import { useElementSize } from '@vueuse/core'
4
- import { StatefulLayout, compile } from '@json-layout/core'
2
+ import { computed, getCurrentInstance } from 'vue'
3
+
4
+ import { compile } from '@json-layout/core'
5
5
  import Tree from './tree.vue'
6
+ import { useVjsf, emits } from '../composables/use-vjsf.js'
7
+ import '../styles/vjsf.css'
6
8
 
7
9
  import NodeSection from './nodes/section.vue'
8
10
  import NodeTextField from './nodes/text-field.vue'
@@ -22,8 +24,9 @@ import NodeVerticalTabs from './nodes/vertical-tabs.vue'
22
24
  import NodeCombobox from './nodes/combobox.vue'
23
25
  import NodeNumberCombobox from './nodes/number-combobox.vue'
24
26
  import NodeExpansionPanels from './nodes/expansion-panels.vue'
27
+ import NodeStepper from './nodes/stepper.vue'
25
28
  import NodeList from './nodes/list.vue'
26
- import { defaultOptions } from './options.js'
29
+ import NodeMarkdown from './nodes/markdown.vue'
27
30
 
28
31
  const comps = {
29
32
  section: NodeSection,
@@ -42,9 +45,11 @@ const comps = {
42
45
  tabs: NodeTabs,
43
46
  'vertical-tabs': NodeVerticalTabs,
44
47
  'expansion-panels': NodeExpansionPanels,
48
+ stepper: NodeStepper,
45
49
  list: NodeList,
46
50
  combobox: NodeCombobox,
47
- 'number-combobox': NodeNumberCombobox
51
+ 'number-combobox': NodeNumberCombobox,
52
+ markdown: NodeMarkdown
48
53
  }
49
54
 
50
55
  const instance = getCurrentInstance()
@@ -65,7 +70,7 @@ const props = defineProps({
65
70
  default: null
66
71
  },
67
72
  modelValue: {
68
- type: [Object, String, Number, Boolean],
73
+ type: null,
69
74
  default: null
70
75
  },
71
76
  options: {
@@ -75,95 +80,16 @@ const props = defineProps({
75
80
  }
76
81
  })
77
82
 
78
- // const emit = defineEmits(['update:modelValue', 'update:state'])
79
- const emit = defineEmits({
80
- /**
81
- * @arg {any} data
82
- */
83
- 'update:modelValue': (data) => true,
84
- /**
85
- * @arg {StatefulLayout} state
86
- */
87
- 'update:state': (state) => true
88
- })
89
-
90
- /** @type import('vue').ShallowRef<StatefulLayout | null> */
91
- const statefulLayout = shallowRef(null)
92
- /** @type import('vue').ShallowRef<import('@json-layout/core').StateTree | null> */
93
- const stateTree = shallowRef(null)
94
-
95
- // cf https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/composables/form.ts
96
- const form = inject(Symbol.for('vuetify:form'))
97
- if (form) {
98
- form.register({
99
- id: 'vjsf', // TODO: a unique random id ?
100
- validate: () => statefulLayout.value?.validate(),
101
- reset: () => statefulLayout.value?.resetValidation(), // TODO: also empty the data ?
102
- resetValidation: () => statefulLayout.value?.resetValidation()
103
- })
104
- }
105
-
106
- const el = ref(null)
107
- const { width } = useElementSize(el)
108
-
109
- const slots = useSlots()
110
-
111
- const fullOptions = computed(() => {
112
- const options = {
113
- ...defaultOptions,
114
- readOnly: !!(form && (form.isDisabled.value || form.isReadonly.value)),
115
- ...props.options,
116
- context: props.options.context ? JSON.parse(JSON.stringify(props.options.context)) : {},
117
- width: Math.round(width.value ?? 0),
118
- vjsfSlots: { ...slots }
119
- }
120
- return /** @type import('./types.js').VjsfOptions */ (options)
121
- })
122
-
123
- const compiledLayout = computed(() => {
124
- if (props.precompiledLayout) return props.precompiledLayout
125
- return compile(props.schema, fullOptions.value)
126
- })
127
-
128
- const onStatefulLayoutUpdate = () => {
129
- if (!statefulLayout.value) return
130
- stateTree.value = statefulLayout.value.stateTree
131
- emit('update:modelValue', statefulLayout.value.data)
132
- emit('update:state', statefulLayout.value)
133
- if (form) {
134
- // cf https://vuetifyjs.com/en/components/forms/#validation-state
135
- if (statefulLayout.value.valid) form.update('vjsf', true, [])
136
- else if (statefulLayout.value.hasHiddenError) form.update('vjsf', null, [])
137
- else form.update('vjsf', false, [])
138
- }
139
- }
140
-
141
- const initStatefulLayout = () => {
142
- if (!width.value) return
143
- const _statefulLayout = new StatefulLayout(toRaw(compiledLayout.value), toRaw(compiledLayout.value.skeletonTree), toRaw(fullOptions.value), toRaw(props.modelValue))
144
- statefulLayout.value = _statefulLayout
145
- onStatefulLayoutUpdate()
146
- _statefulLayout.events.on('update', () => {
147
- onStatefulLayoutUpdate()
148
- })
149
- emit('update:state', _statefulLayout)
150
- }
151
-
152
- watch(fullOptions, (newOptions) => {
153
- if (statefulLayout.value) {
154
- statefulLayout.value.options = newOptions
155
- } else {
156
- initStatefulLayout()
157
- }
158
- })
159
-
160
- // case where data is updated from outside
161
- watch(() => props.modelValue, (newData) => {
162
- if (statefulLayout.value && statefulLayout.value.data !== newData) statefulLayout.value.data = newData
163
- })
83
+ const emit = defineEmits(emits)
164
84
 
165
- // case where schema is updated from outside
166
- watch(compiledLayout, (newCompiledLayout) => initStatefulLayout())
85
+ const { el, statefulLayout, stateTree } = useVjsf(
86
+ computed(() => props.schema),
87
+ computed(() => props.modelValue),
88
+ computed(() => props.options),
89
+ emit,
90
+ compile,
91
+ props.precompiledLayout
92
+ )
167
93
 
168
94
  </script>
169
95
 
@@ -182,14 +108,5 @@ watch(compiledLayout, (newCompiledLayout) => initStatefulLayout())
182
108
  </template>
183
109
 
184
110
  <style lang="css">
185
- /* override vuetify styles to manage readOnly fields more usable than the default disabled fields */
186
- .vjsf-input--readonly.v-input--disabled.v-text-field .v-field--disabled input {
187
- pointer-events: auto;
188
- }
189
- .vjsf-input--readonly.v-input--disabled .v-field--disabled,
190
- .vjsf-input--readonly.v-input--disabled .v-input__details,
191
- .vjsf-input--readonly.v-input--disabled .v-input__append,
192
- .vjsf-input--readonly.v-input--disabled .v-input__prepend {
193
- opacity: inherit;
194
- }
111
+ /* nothing here, use ../styles/vjsf.css */
195
112
  </style>
@@ -0,0 +1,54 @@
1
+ import { shallowRef, ref, computed } from 'vue'
2
+ import { moveArrayItem } from '../utils/arrays.js'
3
+
4
+ /**
5
+ * @template T
6
+ * @param {T[]} array
7
+ * @param {() => void} callback
8
+ * @returns
9
+ */
10
+ export default function useDnd (array, callback) {
11
+ const activeDnd = computed(() => {
12
+ // cf https://ultimatecourses.com/blog/feature-detect-javascript-drag-drop-api
13
+ if (!('draggable' in document.createElement('div'))) return false
14
+ // cf https://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
15
+ if (window.matchMedia('(pointer: coarse)').matches) return false
16
+ return true
17
+ })
18
+
19
+ const sortableArray = shallowRef(array)
20
+
21
+ const draggable = ref(-1)
22
+ const dragging = ref(-1)
23
+
24
+ const itemBind = (/** @type {number} */itemIndex) => ({
25
+ onDragstart: () => {
26
+ dragging.value = itemIndex
27
+ },
28
+ onDragover: () => {
29
+ sortableArray.value = moveArrayItem(sortableArray.value, dragging.value, itemIndex)
30
+ dragging.value = itemIndex
31
+ },
32
+ onDragend: () => {
33
+ dragging.value = -1
34
+ callback()
35
+ }
36
+ })
37
+
38
+ const handleBind = (/** @type {number} */itemIndex) => ({
39
+ onMouseover () {
40
+ draggable.value = itemIndex
41
+ },
42
+ onMouseout () {
43
+ draggable.value = -1
44
+ }
45
+ })
46
+
47
+ return {
48
+ activeDnd,
49
+ sortableArray,
50
+ draggable,
51
+ itemBind,
52
+ handleBind
53
+ }
54
+ }
@@ -0,0 +1,115 @@
1
+ import { StatefulLayout } from '@json-layout/core'
2
+ import { inject, toRaw, shallowRef, computed, ref, watch, useSlots } from 'vue'
3
+ import { useElementSize } from '@vueuse/core'
4
+ import { getFullOptions } from '../components/options.js'
5
+
6
+ export const emits = {
7
+ /**
8
+ * @arg {any} data
9
+ */
10
+ 'update:modelValue': (data) => true,
11
+ /**
12
+ * @arg {StatefulLayout} state
13
+ */
14
+ 'update:state': (state) => true
15
+ }
16
+
17
+ /**
18
+ * @param {import('vue').Ref<Object>} schema
19
+ * @param {import('vue').Ref<any>} modelValue
20
+ * @param {import('vue').Ref<import("../components/types.js").PartialVjsfOptions>} options
21
+ * @param {any} emit
22
+ * @param {typeof import('@json-layout/core').compile} compile
23
+ * @param {import('@json-layout/core').CompiledLayout} [precompiledLayout]
24
+ */
25
+ export const useVjsf = (schema, modelValue, options, emit, compile, precompiledLayout) => {
26
+ const el = ref(null)
27
+ const { width } = useElementSize(el)
28
+
29
+ /** @type import('vue').ShallowRef<StatefulLayout | null> */
30
+ const statefulLayout = shallowRef(null)
31
+ /** @type import('vue').ShallowRef<import('@json-layout/core').StateTree | null> */
32
+ const stateTree = shallowRef(null)
33
+
34
+ // cf https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/composables/form.ts
35
+ const form = inject(Symbol.for('vuetify:form'))
36
+ if (form) {
37
+ form.register({
38
+ id: 'vjsf', // TODO: a unique random id ?
39
+ validate: () => {
40
+ statefulLayout.value?.validate()
41
+ return statefulLayout.value?.errors
42
+ },
43
+ reset: () => statefulLayout.value?.resetValidation(), // TODO: also empty the data ?
44
+ resetValidation: () => statefulLayout.value?.resetValidation()
45
+ })
46
+ }
47
+
48
+ const slots = useSlots()
49
+
50
+ const fullOptions = computed(() => getFullOptions(options.value, form, width.value, slots))
51
+
52
+ const compiledLayout = computed(() => {
53
+ if (precompiledLayout) return precompiledLayout
54
+ const compiledLayout = compile(schema.value, fullOptions.value)
55
+ return compiledLayout
56
+ })
57
+
58
+ const onStatefulLayoutUpdate = () => {
59
+ if (!statefulLayout.value) return
60
+ stateTree.value = statefulLayout.value.stateTree
61
+ emit('update:modelValue', statefulLayout.value.data)
62
+ emit('update:state', statefulLayout.value)
63
+ if (form) {
64
+ // cf https://vuetifyjs.com/en/components/forms/#validation-state
65
+ if (statefulLayout.value.valid) form.update('vjsf', true, [])
66
+ else if (statefulLayout.value.hasHiddenError) form.update('vjsf', null, [])
67
+ else form.update('vjsf', false, [])
68
+ }
69
+ }
70
+
71
+ const initStatefulLayout = () => {
72
+ if (!width.value) return
73
+ const _statefulLayout = new StatefulLayout(
74
+ toRaw(compiledLayout.value),
75
+ toRaw(compiledLayout.value.skeletonTree),
76
+ toRaw(fullOptions.value),
77
+ toRaw(modelValue.value)
78
+ )
79
+ statefulLayout.value = _statefulLayout
80
+ onStatefulLayoutUpdate()
81
+ _statefulLayout.events.on('update', () => {
82
+ onStatefulLayoutUpdate()
83
+ })
84
+ emit('update:state', _statefulLayout)
85
+ _statefulLayout.events.on('autofocus', () => {
86
+ if (!el.value) return
87
+ // @ts-ignore
88
+ const autofocusNodeElement = el.value.querySelector('.vjsf-input--autofocus')
89
+ if (autofocusNodeElement) {
90
+ const autofocusInputElement = autofocusNodeElement.querySelector('input')
91
+ if (autofocusInputElement) autofocusInputElement.focus()
92
+ }
93
+ })
94
+ }
95
+
96
+ watch(fullOptions, (newOptions) => {
97
+ if (statefulLayout.value) {
98
+ statefulLayout.value.options = newOptions
99
+ } else {
100
+ initStatefulLayout()
101
+ }
102
+ })
103
+
104
+ // case where data is updated from outside
105
+ watch(modelValue, (newData) => {
106
+ if (statefulLayout.value && statefulLayout.value.data !== newData) statefulLayout.value.data = toRaw(newData)
107
+ })
108
+
109
+ // case where schema is updated from outside
110
+ watch(compiledLayout, (newCompiledLayout) => {
111
+ initStatefulLayout()
112
+ }, { immediate: true })
113
+
114
+ return { el, statefulLayout, stateTree }
115
+ }
@@ -0,0 +1,10 @@
1
+ /* override vuetify styles to manage readOnly fields more usable than the default disabled fields */
2
+ .vjsf-input--readonly.v-input--disabled.v-text-field .v-field--disabled input {
3
+ pointer-events: auto;
4
+ }
5
+ .vjsf-input--readonly.v-input--disabled .v-field--disabled,
6
+ .vjsf-input--readonly.v-input--disabled .v-input__details,
7
+ .vjsf-input--readonly.v-input--disabled .v-input__append,
8
+ .vjsf-input--readonly.v-input--disabled .v-input__prepend {
9
+ opacity: inherit;
10
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @template T
3
+ * @param {T[]} array
4
+ * @param {number} fromIndex
5
+ * @param {number} toIndex
6
+ * @return {T[]}
7
+ */
8
+ export function moveArrayItem (array, fromIndex, toIndex) {
9
+ if (fromIndex === toIndex || fromIndex === -1 || toIndex === -1) return array
10
+ const newArray = [...array]
11
+ const element = newArray[fromIndex]
12
+ newArray.splice(fromIndex, 1)
13
+ newArray.splice(toIndex, 0, element)
14
+ return newArray
15
+ }
@@ -3,15 +3,23 @@ import { camelize } from 'vue'
3
3
 
4
4
  /**
5
5
  * @param {(Record<string, any> | undefined)[]} propsLevels
6
- * @returns Record<string, any>
6
+ * @returns {Record<string, any> & {class: string[]}}
7
7
  */
8
8
  export function mergePropsLevels (propsLevels) {
9
- /** @type Record<string, any> */
10
- const fullProps = {}
9
+ /** @type {Record<string, any> & {class: string[]}} */
10
+ const fullProps = { class: [] }
11
11
  for (const propsLevel of propsLevels) {
12
12
  if (propsLevel) {
13
13
  for (const key of Object.keys(propsLevel)) {
14
- fullProps[camelize(key)] = propsLevel[key]
14
+ if (key === 'class') {
15
+ // a small convention for merging/overwriting classes:
16
+ // a class defined as a simple string overwrites the previous ones
17
+ // a class defined as an array is merged with the previous ones
18
+ if (Array.isArray(propsLevel.class)) fullProps.class = fullProps.class.concat(propsLevel.class)
19
+ else fullProps.class = [propsLevel.class]
20
+ } else {
21
+ fullProps[camelize(key)] = propsLevel[key]
22
+ }
15
23
  }
16
24
  }
17
25
  }
@@ -49,7 +57,10 @@ export function getInputProps (node, statefulLayout, layoutPropsMap, isMainComp
49
57
  fullProps.modelValue = node.data
50
58
  if (node.options.readOnly) {
51
59
  fullProps.disabled = true
52
- fullProps.class = 'vjsf-input--readonly'
60
+ fullProps.class.push('vjsf-input--readonly')
61
+ }
62
+ if (node.autofocus) {
63
+ fullProps.class.push('vjsf-input--autofocus')
53
64
  }
54
65
 
55
66
  if (layoutPropsMap) {
@@ -1,11 +1,11 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
2
2
  multiple: boolean;
3
3
  itemProps: Record<string, any>;
4
- item: import("../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem;
4
+ item: import("../../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem;
5
5
  $props: {
6
6
  readonly multiple?: boolean | undefined;
7
7
  readonly itemProps?: Record<string, any> | undefined;
8
- readonly item?: import("../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem | undefined;
8
+ readonly item?: import("../../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem | undefined;
9
9
  };
10
10
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
11
11
  export default _default;
@@ -1,10 +1,10 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
2
2
  multiple: boolean;
3
- item: import("../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem;
3
+ item: import("../../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem;
4
4
  last: boolean;
5
5
  $props: {
6
6
  readonly multiple?: boolean | undefined;
7
- readonly item?: import("../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem | undefined;
7
+ readonly item?: import("../../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem | undefined;
8
8
  readonly last?: boolean | undefined;
9
9
  };
10
10
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/markdown.vue.js"],"names":[],"mappings":";;QAUM,2EAA2E;cAAjE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,aAAa,EAAE,gBAAgB,CAAC;;;;QAKxE,+EAA+E;cAArE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,mBAAmB,EAAE,cAAc,CAAC;;;;;;;QAL5E,2EAA2E;cAAjE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,aAAa,EAAE,gBAAgB,CAAC;;;;QAKxE,+EAA+E;cAArE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,mBAAmB,EAAE,cAAc,CAAC"}
1
+ {"version":3,"file":"markdown.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/markdown.vue.js"],"names":[],"mappings":";;QAYM,2EAA2E;cAAjE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,aAAa,EAAE,gBAAgB,CAAC;;;;QAKxE,+EAA+E;cAArE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,mBAAmB,EAAE,cAAc,CAAC;;;;;;;QAL5E,2EAA2E;cAAjE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,aAAa,EAAE,gBAAgB,CAAC;;;;QAKxE,+EAA+E;cAArE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,mBAAmB,EAAE,cAAc,CAAC"}
@@ -0,0 +1,10 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {
2
+ modelValue: import("../types.js").VjsfStepperNode;
3
+ statefulLayout: import("@json-layout/core").StatefulLayout;
4
+ $props: {
5
+ readonly modelValue?: import("../types.js").VjsfStepperNode | undefined;
6
+ readonly statefulLayout?: import("@json-layout/core").StatefulLayout | undefined;
7
+ };
8
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
9
+ export default _default;
10
+ //# sourceMappingURL=stepper.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepper.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/stepper.vue.js"],"names":[],"mappings":""}
@@ -1,3 +1,4 @@
1
1
  /** @type import("./types.js").PartialVjsfOptions */
2
2
  export const defaultOptions: import("./types.js").PartialVjsfOptions;
3
+ export function getFullOptions(options: Partial<import("./types.js").VjsfOptions>, form: any, width: number, slots: import("vue").Slots): import("./types.js").VjsfOptions;
3
4
  //# sourceMappingURL=options.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/components/options.js"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,6BADU,OAAO,YAAY,EAAE,kBAAkB,CAwBhD"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/components/options.js"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,6BADU,OAAO,YAAY,EAAE,kBAAkB,CAyBhD;AAUM,wCANI,QAAQ,OAAO,YAAY,EAAE,WAAW,CAAC,QACzC,GAAG,SACH,MAAM,SACN,OAAO,KAAK,EAAE,KAAK,oCAa7B"}
@@ -1,8 +1,8 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
2
- modelValue: import("../../node_modules/@json-layout/core/types/state/types.js").StateTree;
2
+ modelValue: import("../../../node_modules/@json-layout/core/types/state/types.js").StateTree;
3
3
  statefulLayout: import("@json-layout/core").StatefulLayout;
4
4
  $props: {
5
- readonly modelValue?: import("../../node_modules/@json-layout/core/types/state/types.js").StateTree | undefined;
5
+ readonly modelValue?: import("../../../node_modules/@json-layout/core/types/state/types.js").StateTree | undefined;
6
6
  readonly statefulLayout?: import("@json-layout/core").StatefulLayout | undefined;
7
7
  };
8
8
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;