@ederzeel/nuxt-schema-form-nightly 0.1.0-29119026.5bb3293 → 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,13 +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
4
  import type { PropertiesType } from '../types'
8
-
9
- const UDropdownMenu = resolveComponent('UDropdownMenu')
10
- const UButton = resolveComponent('UButton')
5
+ import SObject from './array/ArrayObject.vue'
6
+ import SMultiSelect from './MultiSelect.vue'
11
7
 
12
8
  const props = defineProps<{
13
9
  id: string
@@ -15,252 +11,53 @@ const props = defineProps<{
15
11
  description?: string
16
12
  jsonSchemaPath: string
17
13
  items: PropertiesType
18
- modelValue: Record<string, unknown>[]
19
14
  isRequired: boolean
20
15
  minItems?: number
21
16
  maxItems?: number
22
17
  edit?: boolean
23
18
  columns?: TableColumn<{}>[]
24
19
  editInline?: boolean
20
+ modelValue: unknown[]
25
21
  }>()
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
22
 
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
- }
23
+ const emit = defineEmits(['update:modelValue', 'submit'])
168
24
 
169
25
  const onInput = (value: Record<string, unknown>, index: number) => {
170
26
  props.modelValue[index] = value
171
27
  emit('update:modelValue', props.modelValue)
172
28
  }
173
29
 
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)
30
+ const Renderer = () => {
31
+ if (props.items.type === 'object') {
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)
39
+ }
40
+ })
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)
53
+ }
54
+ })
187
55
  }
188
-
189
- emit('update:modelValue', res)
190
56
  }
191
57
  </script>
192
58
 
193
59
  <template>
194
60
  <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>
61
+ <Renderer />
265
62
  </div>
266
63
  </template>
@@ -26,7 +26,7 @@ export type SComponentProps = {
26
26
  jsonSchemaPath?: string
27
27
  required?: string[]
28
28
  isRequired: boolean
29
- items?: PropertiesType[]
29
+ items?: PropertiesType
30
30
  edit?: boolean
31
31
  minItems?: number
32
32
  maxItems?: number
@@ -144,9 +144,9 @@ const Render = () => {
144
144
  editInline: props.editInline,
145
145
  jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`,
146
146
  onSubmit,
147
- modelValue: value.value as Record<string, unknown>[],
148
- 'onUpdate:modelValue': (v: string) => {
149
- value.value = v
147
+ modelValue: value.value as unknown[],
148
+ 'onUpdate:modelValue': (v: unknown) => {
149
+ value.value = v as unknown[]
150
150
  }
151
151
  })
152
152
  }
@@ -22,13 +22,14 @@ const open = ref(false)
22
22
 
23
23
  <template>
24
24
  <div>
25
+ {{ open }}
25
26
  <UFormField
26
27
  :label="props.title || id"
27
28
  :hint="!props.isRequired ? 'optional' : undefined"
28
29
  :description="props.description"
29
30
  :name="props.id"
30
31
  >
31
- <UPopover :open="open">
32
+ <UPopover v-model="open" :open="open" :dismissable="true">
32
33
  <UButton color="neutral" variant="subtle" icon="i-lucide-calendar" class="w-full" @click="() => (open = true)">
33
34
  {{ value ? df.format(date.toDate(getLocalTimeZone())) : 'Select a date' }}
34
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>
@@ -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>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ederzeel/nuxt-schema-form-nightly",
3
- "version": "0.1.0-29119026.5bb3293",
3
+ "version": "0.1.0-29119142.f6bc066",
4
4
  "description": "A runtime form generator for nuxt",
5
5
  "type": "module",
6
6
  "exports": {