@ederzeel/nuxt-schema-form-nightly 0.1.0-29119026.5bb3293 → 0.1.0-29119255.9390cd6

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>
@@ -17,16 +17,17 @@ export type SComponentProps = {
17
17
  id?: string
18
18
  title?: string
19
19
  renderer?: string
20
- type?: string
20
+ type?: string | unknown[]
21
21
  properties?: Properties
22
22
  enum?: string[]
23
23
  enum_titles?: string[]
24
24
  format?: string
25
25
  description?: string
26
+ placeholder?: string
26
27
  jsonSchemaPath?: string
27
28
  required?: string[]
28
29
  isRequired: boolean
29
- items?: PropertiesType[]
30
+ items?: PropertiesType
30
31
  edit?: boolean
31
32
  minItems?: number
32
33
  maxItems?: number
@@ -44,7 +45,8 @@ const onSubmit = () => {
44
45
  }
45
46
 
46
47
  const Render = () => {
47
- const { type, renderer, format } = props
48
+ const { type: t, renderer, format } = props
49
+ const type = Array.isArray(t) ? t[0] : t
48
50
  if (type == null || (renderer != null && renderer === 'none')) {
49
51
  return undefined
50
52
  } else if (renderer) {
@@ -144,9 +146,9 @@ const Render = () => {
144
146
  editInline: props.editInline,
145
147
  jsonSchemaPath: `${props.jsonSchemaPath}.${props.id}`,
146
148
  onSubmit,
147
- modelValue: value.value as Record<string, unknown>[],
148
- 'onUpdate:modelValue': (v: string) => {
149
- value.value = v
149
+ modelValue: value.value as unknown[],
150
+ 'onUpdate:modelValue': (v: unknown) => {
151
+ value.value = v as unknown[]
150
152
  }
151
153
  })
152
154
  }
@@ -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,49 @@
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
+ placeholder?: string
9
+ jsonSchemaPath: string
10
+ items: {
11
+ id: string
12
+ enum: string[]
13
+ enum_titles?: string[]
14
+ }
15
+ modelValue: string[]
16
+ isRequired: boolean
17
+ minItems?: number
18
+ maxItems?: number
19
+ edit?: boolean
20
+ editInline?: boolean
21
+ }>()
22
+
23
+ const options = computed(() =>
24
+ props.items.enum.map((x, i) => ({ label: props?.items.enum_titles?.[i] ?? x, value: x }))
25
+ )
26
+
27
+ const value = defineModel<string[]>({ required: true })
28
+ </script>
29
+
30
+ <template>
31
+ <div>
32
+ <UFormField
33
+ :label="props.title || id"
34
+ :hint="!props.isRequired ? 'optional' : undefined"
35
+ :description="props.description"
36
+ :name="props.id"
37
+ >
38
+ <USelect
39
+ v-model="value"
40
+ :placeholder="props.placeholder || 'select size'"
41
+ :items="options"
42
+ multiple
43
+ value-attribute="value"
44
+ option-attribute="key"
45
+ class="w-full"
46
+ />
47
+ </UFormField>
48
+ </div>
49
+ </template>
@@ -5,6 +5,7 @@ const props = defineProps<{
5
5
  id: string
6
6
  title?: string
7
7
  description?: string
8
+ placeholder?: string
8
9
  enum: string[]
9
10
  enum_titles?: string[]
10
11
  isRequired: boolean
@@ -25,7 +26,7 @@ const value = defineModel<string>({ required: true })
25
26
  >
26
27
  <USelect
27
28
  v-model="value"
28
- placeholder="select size"
29
+ :placeholder="props.placeholder || 'Make a seletion'"
29
30
  :items="options"
30
31
  value-attribute="value"
31
32
  option-attribute="key"
@@ -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-29119255.9390cd6",
4
4
  "description": "A runtime form generator for nuxt",
5
5
  "type": "module",
6
6
  "exports": {