@ederzeel/nuxt-schema-form-nightly 0.1.0-29118956.9ee4a3d → 0.1.0-29119142.f6bc066

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.
@@ -1,12 +1,9 @@
1
1
  <script lang="ts" setup>
2
- import { Draft2019 } from 'json-schema-library'
3
- import SComponent from './Component.vue'
4
- import { ref, computed, h, resolveComponent } from 'vue'
2
+ import { h } from 'vue'
5
3
  import type { TableColumn } from '@nuxt/ui'
6
- import type { Row } from '@tanstack/vue-table'
7
-
8
- const UDropdownMenu = resolveComponent('UDropdownMenu')
9
- const UButton = resolveComponent('UButton')
4
+ import type { PropertiesType } from '../types'
5
+ import SObject from './array/ArrayObject.vue'
6
+ import SMultiSelect from './MultiSelect.vue'
10
7
 
11
8
  const props = defineProps<{
12
9
  id: string
@@ -14,236 +11,53 @@ const props = defineProps<{
14
11
  description?: string
15
12
  jsonSchemaPath: string
16
13
  items: PropertiesType
17
- modelValue: Record<string, unknown>[]
18
14
  isRequired: boolean
19
15
  minItems?: number
20
16
  maxItems?: number
21
17
  edit?: boolean
22
18
  columns?: TableColumn<{}>[]
23
19
  editInline?: boolean
20
+ modelValue: unknown[]
24
21
  }>()
22
+
25
23
  const emit = defineEmits(['update:modelValue', 'submit'])
26
24
 
27
- type PropertiesType = {
28
- properties: {
29
- [key: string]: unknown
30
- }
31
- [key: string]: unknown
25
+ const onInput = (value: Record<string, unknown>, index: number) => {
26
+ props.modelValue[index] = value
27
+ emit('update:modelValue', props.modelValue)
32
28
  }
33
29
 
34
- const massActions = [
35
- [
36
- {
37
- label: 'delete',
38
- click: () => {
39
- massRemove()
40
- selectedRows.value = []
41
- }
42
- }
43
- ]
44
- ]
45
-
46
- const selectedRows = ref<{ __id: any }[]>([])
47
- const schemaObject = ref(new Draft2019(props.items))
48
- const model = ref<{ open: boolean; index: number; value: Record<string, unknown> }>({
49
- open: false,
50
- index: 0,
51
- value: {}
52
- })
53
- const expand = ref({
54
- openedRows: [],
55
- row: null
56
- })
57
-
58
- const values = computed(() => {
59
- if (props.modelValue.length > 0) {
60
- return props.modelValue.map((x, i) => ({ ...x, __id: i }))
61
- } else if (props.minItems && props.minItems > 0) {
62
- const template = schemaObject.value.getTemplate()
63
- return [...Array(props.minItems)].map((_, i) => ({ ...template, __id: i }))
64
- }
65
-
66
- return []
67
- })
68
-
69
- const columns = computed<TableColumn<{}>[]>(() => {
70
- const columns = props.columns ?? []
71
- if (props.editInline) {
72
- columns.push({
73
- id: 'expand',
74
- cell: ({ row }) =>
75
- h(UButton, {
76
- color: 'neutral',
77
- variant: 'ghost',
78
- icon: 'i-lucide-chevron-down',
79
- square: true,
80
- 'aria-label': 'Expand',
81
- ui: {
82
- leadingIcon: ['transition-transform', row.getIsExpanded() ? 'duration-200 rotate-180' : '']
83
- },
84
- onClick: () => row.toggleExpanded()
85
- })
86
- })
87
- }
88
-
30
+ const Renderer = () => {
89
31
  if (props.items.type === 'object') {
90
- for (const column of Object.keys(props?.items?.properties).map<TableColumn<{}>>(x => ({
91
- header: x,
92
- accessorKey: x,
93
- cell: ({ row }) => row.getValue(x)
94
- }))) {
95
- columns.push(column)
96
- }
97
- }
98
-
99
- if (props.edit) {
100
- columns.push({
101
- id: 'actions',
102
- cell: ({ row }) => {
103
- return h(
104
- 'div',
105
- { class: 'text-right' },
106
- h(
107
- UDropdownMenu,
108
- {
109
- content: {
110
- align: 'end'
111
- },
112
- items: getRowItems(row),
113
- 'aria-label': 'Actions dropdown'
114
- },
115
- () =>
116
- h(UButton, {
117
- icon: 'i-lucide-ellipsis-vertical',
118
- color: 'neutral',
119
- variant: 'ghost',
120
- class: 'ml-auto',
121
- 'aria-label': 'Actions dropdown'
122
- })
123
- )
124
- )
32
+ return h(SObject, {
33
+ ...props,
34
+ required: props.items.required,
35
+ isRequired: props.isRequired,
36
+ modelValue: props.modelValue as Record<string, unknown>[],
37
+ 'onUpdate:modelValue': (v: Record<string, unknown>[]) => {
38
+ emit('update:modelValue', v)
125
39
  }
126
40
  })
127
- }
128
-
129
- return columns
130
- })
131
-
132
- function getRowItems(row: Row<{}>) {
133
- return [
134
- {
135
- label: 'edit',
136
- onSelect: () => open(row.index)
137
- },
138
- {
139
- label: 'delete',
140
- onSelect: () => {
141
- remove(row.index)
142
- selectedRows.value = []
41
+ } else if (props.items.type === 'string' && props.items.enum) {
42
+ return h(SMultiSelect, {
43
+ ...props,
44
+ items: props.items as unknown as {
45
+ id: string
46
+ enum: string[]
47
+ enum_titles?: string[]
48
+ },
49
+ isRequired: props.isRequired,
50
+ modelValue: props.modelValue as string[],
51
+ 'onUpdate:modelValue': (v: string[]) => {
52
+ emit('update:modelValue', v)
143
53
  }
144
- }
145
- ]
146
- }
147
-
148
- const options = computed(() => {
149
- let res: {
150
- modelValue?: any
151
- 'onUpdate:modelValue'?: (v: any) => void
152
- expand?: {
153
- openedRows: any[]
154
- row: null
155
- }
156
- 'update:expand'?: (v: any) => void
157
- } = {}
158
-
159
- if (props.edit) {
160
- res.modelValue = selectedRows.value
161
- res['onUpdate:modelValue'] = value => (selectedRows.value = value)
162
- }
163
- if (props.editInline) {
164
- res.expand = expand.value
165
- res['update:expand'] = value => (expand.value = value)
166
- }
167
-
168
- return res
169
- })
170
-
171
- const open = (index: number) => {
172
- model.value = { open: true, index: index, value: structuredClone(values.value[index]) }
173
- }
174
-
175
- const onInput = (value: Record<string, unknown>, index: number) => {
176
- props.modelValue[index] = value
177
- emit('update:modelValue', props.modelValue)
178
- }
179
-
180
- const add = () => {
181
- emit('update:modelValue', [...props.modelValue, schemaObject.value.getTemplate()])
182
- }
183
-
184
- const remove = (index: number) => {
185
- props.modelValue.splice(index, 1)
186
- emit('update:modelValue', props.modelValue)
187
- }
188
-
189
- const massRemove = () => {
190
- let res = props.modelValue
191
- for (const index of selectedRows.value.map(x => x.__id)) {
192
- res.splice(index, 1)
54
+ })
193
55
  }
194
-
195
- emit('update:modelValue', res)
196
56
  }
197
57
  </script>
198
58
 
199
59
  <template>
200
60
  <div>
201
- <h2 v-if="props.title" class="mb-4 text-2xl leading-none tracking-tight">{{ props.title }}</h2>
202
- <p v-if="props.description" class="mb-4">{{ props.description }}</p>
203
-
204
- <div>
205
- <div class="flex justify-between items-center w-full px-4 py-3">
206
- <div class="flex gap-1.5 items-center">
207
- <UButton v-if="edit" :disabled="maxItems && values.length >= maxItems" @click="add" icon="i-heroicons-plus"
208
- trailing color="neutral" size="xs" />
209
- </div>
210
- <div class="flex gap-1.5 items-center">
211
- <UDropdownMenu v-if="selectedRows.length > 0" :items="massActions">
212
- <UButton icon="i-heroicons-chevron-down" trailing color="neutral" size="xs"> Action </UButton>
213
- </UDropdownMenu>
214
- </div>
215
- </div>
216
- <UTable v-bind="options" by="__id" :data="values" :columns="columns" class="flex-1">
217
- <template #expanded="{ row }">
218
- {{ row }}
219
- <div class="p-4">
220
- <SComponent :id="id.length <= 0 ? `${row.index}` : `${id}[${row.index}].${items.id}`"
221
- :model-value="row.original" :json-schema-path="jsonSchemaPath?.length <= 0 ? `properties.${row.index}` : `${jsonSchemaPath}[${row.index}]`
222
- " v-bind="items" :isRequired="false" class="mt-4"
223
- @update:model-value="(event: Record<string, unknown>) => onInput(event, row.index)"
224
- @submit="() => emit('submit')" />
225
- </div>
226
- </template>
227
- </UTable>
228
- </div>
229
- <UModal v-model:open="model.open">
230
- <template #content>
231
- <UCard>
232
- <SComponent :id="id.length <= 0 ? `${model.index}` : `${id}[0].${items.id}`" :model-value="model.value"
233
- :json-schema-path="jsonSchemaPath?.length <= 0 ? `properties.${model.index}` : `${jsonSchemaPath}[${model.index}]`
234
- " v-bind="items" :isRequired="false" class="mt-4"
235
- @update:model-value="(event: Record<string, unknown>) => (model.value = event)"
236
- @submit="() => emit('submit')" />
237
- <template #footer>
238
- <UButton @click="
239
- () => {
240
- onInput(model.value, model.index)
241
- model.open = false
242
- }
243
- ">Save</UButton>
244
- </template>
245
- </UCard>
246
- </template>
247
- </UModal>
61
+ <Renderer />
248
62
  </div>
249
63
  </template>
@@ -1,11 +1,13 @@
1
1
  <script lang="ts" setup>
2
- import { computed } from 'vue'
2
+ import { h, resolveComponent } from 'vue'
3
3
  import SObject from './Object.vue'
4
4
  import SDate from './Date.vue'
5
5
  import SToggle from './Toggle.vue'
6
6
  import SArray from './Array.vue'
7
7
  import SInputNumber from './InputNumber.vue'
8
8
  import SInputField from './InputField.vue'
9
+ import type { PropertiesType } from '../types'
10
+ import type { TableColumn } from '@nuxt/ui'
9
11
 
10
12
  type Properties = {
11
13
  properties?: { [key: string]: unknown }
@@ -24,11 +26,12 @@ export type SComponentProps = {
24
26
  jsonSchemaPath?: string
25
27
  required?: string[]
26
28
  isRequired: boolean
27
- items?: { [key: string]: unknown }
29
+ items?: PropertiesType
28
30
  edit?: boolean
29
31
  minItems?: number
30
32
  maxItems?: number
31
33
  editInline?: boolean
34
+ columns?: TableColumn<{}>[]
32
35
  validateFields?: (fields: string[]) => Promise<boolean>
33
36
  }
34
37
 
@@ -36,20 +39,133 @@ const props = withDefaults(defineProps<SComponentProps>(), { id: '', jsonSchemaP
36
39
  const value = defineModel<unknown>({ required: true })
37
40
  const emit = defineEmits(['submit'])
38
41
 
39
- const options = computed(() => ({
40
- ...props,
41
- jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`
42
- }))
43
42
  const onSubmit = () => {
44
43
  emit('submit')
45
44
  }
45
+
46
+ const Render = () => {
47
+ const { type, renderer, format } = props
48
+ if (type == null || (renderer != null && renderer === 'none')) {
49
+ return undefined
50
+ } else if (renderer) {
51
+ return h(resolveComponent(renderer), {
52
+ ...props,
53
+ jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`,
54
+ onSubmit,
55
+ // is: typeof renderer === 'string' ? renderer : undefined,
56
+ modelValue: value.value,
57
+ 'onUpdate:modelValue': (v: unknown) => {
58
+ value.value = v
59
+ }
60
+ })
61
+ } else if (type === 'string') {
62
+ if (format === 'full-date') {
63
+ return h(SDate, {
64
+ id: props.id,
65
+ title: props.title,
66
+ description: props.description,
67
+ type: props.type,
68
+ isRequired: props.isRequired,
69
+ jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`,
70
+ modelValue: value.value as string,
71
+ 'onUpdate:modelValue': (v: string) => {
72
+ value.value = v
73
+ }
74
+ })
75
+ } else {
76
+ return h(SInputField, {
77
+ id: props.id,
78
+ title: props.title,
79
+ isRequired: props.isRequired,
80
+ type,
81
+ jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`,
82
+ modelValue: value.value as string,
83
+ 'onUpdate:modelValue': (v: string) => {
84
+ value.value = v
85
+ }
86
+ })
87
+ }
88
+ } else if (type === 'number' || type === 'integer') {
89
+ return h(SInputNumber, {
90
+ id: props.id,
91
+ title: props.title,
92
+ description: props.description,
93
+ type,
94
+ isRequired: props.isRequired,
95
+ jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`,
96
+ modelValue: value.value as number,
97
+ 'onUpdate:modelValue': (v: number) => {
98
+ value.value = v
99
+ }
100
+ })
101
+ } else if (type === 'boolean') {
102
+ return h(SToggle, {
103
+ id: props.id,
104
+ title: props.title,
105
+ description: props.description,
106
+ isRequired: props.isRequired,
107
+ jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`,
108
+ modelValue: value.value as boolean,
109
+ 'onUpdate:modelValue': (v: boolean) => {
110
+ value.value = v
111
+ }
112
+ })
113
+ } else if (type === 'object') {
114
+ if (!props.properties) throw new Error('object is missing properties')
115
+ if (!props.required) throw new Error('object is missing required')
116
+
117
+ return h(SObject, {
118
+ id: props.id,
119
+ title: props.title,
120
+ description: props.description,
121
+ properties: props.properties,
122
+ required: props.required,
123
+ isRequired: props.isRequired,
124
+ jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`,
125
+ onSubmit,
126
+ modelValue: value.value as Record<string, unknown>,
127
+ 'onUpdate:modelValue': (v: Record<string, unknown>) => {
128
+ value.value = v
129
+ }
130
+ })
131
+ } else if (type === 'array') {
132
+ if (!props.items) throw new Error('array has no items')
133
+
134
+ return h(SArray, {
135
+ id: props.id,
136
+ title: props.title,
137
+ description: props.description,
138
+ items: props.items,
139
+ isRequired: props.isRequired,
140
+ minItems: props.minItems,
141
+ maxItems: props.maxItems,
142
+ edit: props.edit,
143
+ columns: props.columns,
144
+ editInline: props.editInline,
145
+ jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`,
146
+ onSubmit,
147
+ modelValue: value.value as unknown[],
148
+ 'onUpdate:modelValue': (v: unknown) => {
149
+ value.value = v as unknown[]
150
+ }
151
+ })
152
+ }
153
+ return undefined
154
+ }
46
155
  </script>
47
156
 
48
157
  <template>
49
158
  <div class="s-form-group">
50
- <template v-if="renderer != null && renderer === 'none'">{{ undefined }}</template>
51
- <component v-else-if="renderer" v-bind="options" v-model="value"
52
- :is="typeof renderer === 'string' ? renderer : undefined" @submit="onSubmit" />
159
+ <Render />
160
+
161
+ <!--<template v-if="renderer != null && renderer === 'none'">{{ undefined }}</template>
162
+ <component
163
+ v-else-if="renderer"
164
+ v-bind="options"
165
+ v-model="value"
166
+ :is="typeof renderer === 'string' ? renderer : undefined"
167
+ @submit="onSubmit"
168
+ />
53
169
 
54
170
  <SDate v-else-if="type === 'string' && format === 'full-date'" v-bind="options" v-model="value" />
55
171
  <SInputNumber v-else-if="type === 'number' || type === 'integer'" v-bind="options" v-model="value" />
@@ -57,6 +173,6 @@ const onSubmit = () => {
57
173
  <SToggle v-else-if="type === 'boolean'" v-bind="options" v-model="value" />
58
174
  <SObject v-else-if="type === 'object'" v-bind="options" v-model="value" @submit="onSubmit" />
59
175
  <SArray v-else-if="type === 'array'" v-bind="options" v-model="value" />
60
- <div v-else>else</div>
176
+ <div v-else>else</div>-->
61
177
  </div>
62
178
  </template>
@@ -6,7 +6,6 @@ const props = defineProps<{
6
6
  id: string
7
7
  title?: string
8
8
  description?: string
9
- type: string
10
9
  isRequired: boolean
11
10
  }>()
12
11
 
@@ -23,13 +22,14 @@ const open = ref(false)
23
22
 
24
23
  <template>
25
24
  <div>
25
+ {{ open }}
26
26
  <UFormField
27
27
  :label="props.title || id"
28
28
  :hint="!props.isRequired ? 'optional' : undefined"
29
29
  :description="props.description"
30
30
  :name="props.id"
31
31
  >
32
- <UPopover :open="open">
32
+ <UPopover v-model="open" :open="open" :dismissable="true">
33
33
  <UButton color="neutral" variant="subtle" icon="i-lucide-calendar" class="w-full" @click="() => (open = true)">
34
34
  {{ value ? df.format(date.toDate(getLocalTimeZone())) : 'Select a date' }}
35
35
  </UButton>
@@ -0,0 +1,48 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+
4
+ const props = defineProps<{
5
+ id: string
6
+ title?: string
7
+ description?: string
8
+ jsonSchemaPath: string
9
+ items: {
10
+ id: string
11
+ enum: string[]
12
+ enum_titles?: string[]
13
+ }
14
+ modelValue: string[]
15
+ isRequired: boolean
16
+ minItems?: number
17
+ maxItems?: number
18
+ edit?: boolean
19
+ editInline?: boolean
20
+ }>()
21
+
22
+ const options = computed(() =>
23
+ props.items.enum.map((x, i) => ({ label: props?.items.enum_titles?.[i] ?? x, value: x }))
24
+ )
25
+
26
+ const value = defineModel<string[]>({ required: true })
27
+ </script>
28
+
29
+ <template>
30
+ <div>
31
+ <UFormField
32
+ :label="props.title || id"
33
+ :hint="!props.isRequired ? 'optional' : undefined"
34
+ :description="props.description"
35
+ :name="props.id"
36
+ >
37
+ <USelect
38
+ v-model="value"
39
+ placeholder="select size"
40
+ :items="options"
41
+ multiple
42
+ value-attribute="value"
43
+ option-attribute="key"
44
+ class="w-full"
45
+ />
46
+ </UFormField>
47
+ </div>
48
+ </template>
@@ -26,10 +26,17 @@ const onInput = (value: unknown, key: string) => {
26
26
  <h2 v-if="props.title" class="mb-4 text-2xl leading-none tracking-tight">{{ props.title }}</h2>
27
27
  <p v-if="props.description" class="mb-4">{{ props.description }}</p>
28
28
 
29
- <SComponent v-for="[key, options] of Object.entries(properties)" :id="id.length <= 0 ? `${key}` : `${id}.${key}`"
30
- :key="key" :model-value="modelValue[key]"
29
+ <SComponent
30
+ v-for="[key, options] of Object.entries(properties)"
31
+ :id="id.length <= 0 ? `${key}` : `${id}.${key}`"
32
+ :key="key"
33
+ :model-value="modelValue[key]"
31
34
  :json-schema-path="jsonSchemaPath?.length <= 0 ? `properties.${key}` : `${jsonSchemaPath}.properties.${key}`"
32
- v-bind="options" :isRequired="required.includes(key)" class="mt-4"
33
- @update:model-value="(event: unknown) => onInput(event, key)" @submit="() => emit('submit')" />
35
+ v-bind="options"
36
+ :isRequired="required.includes(key)"
37
+ class="mt-4"
38
+ @update:model-value="(event: unknown) => onInput(event, key)"
39
+ @submit="() => emit('submit')"
40
+ />
34
41
  </div>
35
42
  </template>
@@ -3,7 +3,6 @@ const props = defineProps<{
3
3
  id: string
4
4
  title?: string
5
5
  description?: string
6
- enum?: boolean[]
7
6
  isRequired: boolean
8
7
  }>()
9
8
 
@@ -0,0 +1,266 @@
1
+ <script lang="ts" setup>
2
+ import { Draft2019 } from 'json-schema-library'
3
+ import SComponent from '../Component.vue'
4
+ import { ref, computed, h, resolveComponent } from 'vue'
5
+ import type { TableColumn } from '@nuxt/ui'
6
+ import type { Row } from '@tanstack/vue-table'
7
+ import type { PropertiesType } from '../../types'
8
+
9
+ const UDropdownMenu = resolveComponent('UDropdownMenu')
10
+ const UButton = resolveComponent('UButton')
11
+
12
+ const props = defineProps<{
13
+ id: string
14
+ title?: string
15
+ description?: string
16
+ jsonSchemaPath: string
17
+ items: PropertiesType
18
+ modelValue: Record<string, unknown>[]
19
+ isRequired: boolean
20
+ minItems?: number
21
+ maxItems?: number
22
+ edit?: boolean
23
+ columns?: TableColumn<{}>[]
24
+ editInline?: boolean
25
+ }>()
26
+ const emit = defineEmits(['update:modelValue', 'submit'])
27
+
28
+ const massActions = [
29
+ [
30
+ {
31
+ label: 'delete',
32
+ click: () => {
33
+ massRemove()
34
+ selectedRows.value = []
35
+ }
36
+ }
37
+ ]
38
+ ]
39
+
40
+ const selectedRows = ref<{ __id: any }[]>([])
41
+ const schemaObject = ref(new Draft2019(props.items))
42
+ const model = ref<{ open: boolean; index: number; value: Record<string, unknown> }>({
43
+ open: false,
44
+ index: 0,
45
+ value: {}
46
+ })
47
+ const expand = ref({
48
+ openedRows: [],
49
+ row: null
50
+ })
51
+
52
+ const values = computed(() => {
53
+ if (props.modelValue.length > 0) {
54
+ return props.modelValue.map((x, i) => ({ ...x, __id: i }))
55
+ } else if (props.minItems && props.minItems > 0) {
56
+ const template = schemaObject.value.getTemplate()
57
+ return [...Array(props.minItems)].map((_, i) => ({ ...template, __id: i }))
58
+ }
59
+
60
+ return []
61
+ })
62
+
63
+ const columns = computed<TableColumn<{}>[]>(() => {
64
+ const columns = props.columns ?? []
65
+ if (props.editInline) {
66
+ columns.push({
67
+ id: 'expand',
68
+ cell: ({ row }) =>
69
+ h(UButton, {
70
+ color: 'neutral',
71
+ variant: 'ghost',
72
+ icon: 'i-lucide-chevron-down',
73
+ square: true,
74
+ 'aria-label': 'Expand',
75
+ ui: {
76
+ leadingIcon: ['transition-transform', row.getIsExpanded() ? 'duration-200 rotate-180' : '']
77
+ },
78
+ onClick: () => row.toggleExpanded()
79
+ })
80
+ })
81
+ }
82
+
83
+ if (props.items.type === 'object') {
84
+ for (const column of Object.keys(props?.items?.properties).map<TableColumn<{}>>(x => ({
85
+ header: x,
86
+ accessorKey: x,
87
+ cell: ({ row }) => row.getValue(x)
88
+ }))) {
89
+ columns.push(column)
90
+ }
91
+ }
92
+
93
+ if (props.edit) {
94
+ columns.push({
95
+ id: 'actions',
96
+ cell: ({ row }) => {
97
+ return h(
98
+ 'div',
99
+ { class: 'text-right' },
100
+ h(
101
+ UDropdownMenu,
102
+ {
103
+ content: {
104
+ align: 'end'
105
+ },
106
+ items: getRowItems(row),
107
+ 'aria-label': 'Actions dropdown'
108
+ },
109
+ () =>
110
+ h(UButton, {
111
+ icon: 'i-lucide-ellipsis-vertical',
112
+ color: 'neutral',
113
+ variant: 'ghost',
114
+ class: 'ml-auto',
115
+ 'aria-label': 'Actions dropdown'
116
+ })
117
+ )
118
+ )
119
+ }
120
+ })
121
+ }
122
+
123
+ return columns
124
+ })
125
+
126
+ function getRowItems(row: Row<{}>) {
127
+ return [
128
+ {
129
+ label: 'edit',
130
+ onSelect: () => open(row.index)
131
+ },
132
+ {
133
+ label: 'delete',
134
+ onSelect: () => {
135
+ remove(row.index)
136
+ selectedRows.value = []
137
+ }
138
+ }
139
+ ]
140
+ }
141
+
142
+ const options = computed(() => {
143
+ let res: {
144
+ modelValue?: any
145
+ 'onUpdate:modelValue'?: (v: any) => void
146
+ expand?: {
147
+ openedRows: any[]
148
+ row: null
149
+ }
150
+ 'update:expand'?: (v: any) => void
151
+ } = {}
152
+
153
+ if (props.edit) {
154
+ res.modelValue = selectedRows.value
155
+ res['onUpdate:modelValue'] = value => (selectedRows.value = value)
156
+ }
157
+ if (props.editInline) {
158
+ res.expand = expand.value
159
+ res['update:expand'] = value => (expand.value = value)
160
+ }
161
+
162
+ return res
163
+ })
164
+
165
+ const open = (index: number) => {
166
+ model.value = { open: true, index: index, value: structuredClone(values.value[index]) }
167
+ }
168
+
169
+ const onInput = (value: Record<string, unknown>, index: number) => {
170
+ props.modelValue[index] = value
171
+ emit('update:modelValue', props.modelValue)
172
+ }
173
+
174
+ const add = () => {
175
+ emit('update:modelValue', [...props.modelValue, schemaObject.value.getTemplate()])
176
+ }
177
+
178
+ const remove = (index: number) => {
179
+ props.modelValue.splice(index, 1)
180
+ emit('update:modelValue', props.modelValue)
181
+ }
182
+
183
+ const massRemove = () => {
184
+ let res = props.modelValue
185
+ for (const index of selectedRows.value.map(x => x.__id)) {
186
+ res.splice(index, 1)
187
+ }
188
+
189
+ emit('update:modelValue', res)
190
+ }
191
+ </script>
192
+
193
+ <template>
194
+ <div>
195
+ <h2 v-if="props.title" class="mb-4 text-2xl leading-none tracking-tight">{{ props.title }}</h2>
196
+ <p v-if="props.description" class="mb-4">{{ props.description }}</p>
197
+
198
+ <div>
199
+ <div class="flex justify-between items-center w-full px-4 py-3">
200
+ <div class="flex gap-1.5 items-center">
201
+ <UButton
202
+ v-if="edit"
203
+ :disabled="maxItems && values.length >= maxItems"
204
+ @click="add"
205
+ icon="i-heroicons-plus"
206
+ trailing
207
+ color="neutral"
208
+ size="xs"
209
+ />
210
+ </div>
211
+ <div class="flex gap-1.5 items-center">
212
+ <UDropdownMenu v-if="selectedRows.length > 0" :items="massActions">
213
+ <UButton icon="i-heroicons-chevron-down" trailing color="neutral" size="xs"> Action </UButton>
214
+ </UDropdownMenu>
215
+ </div>
216
+ </div>
217
+ <UTable v-bind="options" by="__id" :data="values" :columns="columns" class="flex-1">
218
+ <template #expanded="{ row }">
219
+ <div class="p-4">
220
+ <SComponent
221
+ :id="id.length <= 0 ? `${row.index}` : `${id}[${row.index}]`"
222
+ :model-value="row.original"
223
+ :json-schema-path="
224
+ jsonSchemaPath?.length <= 0 ? `properties.${row.index}` : `${jsonSchemaPath}[${row.index}]`
225
+ "
226
+ v-bind="items"
227
+ :isRequired="false"
228
+ class="mt-4"
229
+ @update:model-value="(event: Record<string, unknown>) => onInput(event, row.index)"
230
+ @submit="() => emit('submit')"
231
+ />
232
+ </div>
233
+ </template>
234
+ </UTable>
235
+ </div>
236
+ <UModal v-model:open="model.open">
237
+ <template #content>
238
+ <UCard>
239
+ <SComponent
240
+ :id="id.length <= 0 ? `${model.index}` : `${id}[0]`"
241
+ :model-value="model.value"
242
+ :json-schema-path="
243
+ jsonSchemaPath?.length <= 0 ? `properties.${model.index}` : `${jsonSchemaPath}[${model.index}]`
244
+ "
245
+ v-bind="items"
246
+ :isRequired="false"
247
+ class="mt-4"
248
+ @update:model-value="(event: Record<string, unknown>) => (model.value = event)"
249
+ @submit="() => emit('submit')"
250
+ />
251
+ <template #footer>
252
+ <UButton
253
+ @click="
254
+ () => {
255
+ onInput(model.value, model.index)
256
+ model.open = false
257
+ }
258
+ "
259
+ >Save</UButton
260
+ >
261
+ </template>
262
+ </UCard>
263
+ </template>
264
+ </UModal>
265
+ </div>
266
+ </template>
@@ -3,3 +3,9 @@ export type CustomValidation = {
3
3
  message: string;
4
4
  validate(state: any): boolean;
5
5
  };
6
+ export type PropertiesType = {
7
+ properties: {
8
+ [key: string]: unknown;
9
+ };
10
+ [key: string]: unknown;
11
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ederzeel/nuxt-schema-form-nightly",
3
- "version": "0.1.0-29118956.9ee4a3d",
3
+ "version": "0.1.0-29119142.f6bc066",
4
4
  "description": "A runtime form generator for nuxt",
5
5
  "type": "module",
6
6
  "exports": {