@ederzeel/nuxt-schema-form-nightly 0.1.0-29031029.c108247 → 0.1.0-29032610.195dce6
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.
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { Draft2019 } from 'json-schema-library'
|
|
3
|
+
import SComponent from './SComponent.vue'
|
|
4
|
+
import { ref, computed } from 'vue'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
id: string
|
|
8
|
+
title?: string
|
|
9
|
+
description?: string
|
|
10
|
+
jsonSchemaPath: string
|
|
11
|
+
items: PropertiesType
|
|
12
|
+
modelValue: unknown[]
|
|
13
|
+
isRequired: boolean
|
|
14
|
+
minItems?: number
|
|
15
|
+
maxItems?: number
|
|
16
|
+
edit?: boolean
|
|
17
|
+
columns?: ColumnItem[]
|
|
18
|
+
editInline?: boolean
|
|
19
|
+
}>()
|
|
20
|
+
|
|
21
|
+
type ColumnItem = {
|
|
22
|
+
key: string
|
|
23
|
+
label?: string
|
|
24
|
+
class?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type PropertiesType = {
|
|
28
|
+
properties: {
|
|
29
|
+
[key: string]: unknown
|
|
30
|
+
}
|
|
31
|
+
[key: string]: unknown
|
|
32
|
+
}
|
|
33
|
+
const emit = defineEmits(['update:modelValue', 'submit'])
|
|
34
|
+
const onInput = (value: unknown, index: number) => {
|
|
35
|
+
props.modelValue[index] = value
|
|
36
|
+
emit('update:modelValue', props.modelValue)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const schemaObject = ref(new Draft2019(props.items))
|
|
40
|
+
|
|
41
|
+
const values = computed(() => {
|
|
42
|
+
if (props.modelValue.length > 0) {
|
|
43
|
+
return props.modelValue.map((x, i) => ({ ...x, __id: i }))
|
|
44
|
+
} else if (props.minItems && props.minItems > 0) {
|
|
45
|
+
const template = schemaObject.value.getTemplate()
|
|
46
|
+
return [...Array(props.minItems)].map((_, i) => ({ ...template, __id: i }))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return []
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const add = () => {
|
|
53
|
+
emit('update:modelValue', [...props.modelValue, schemaObject.value.getTemplate()])
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const remove = (index: number) => {
|
|
57
|
+
const res = props.modelValue.splice(index, 1)
|
|
58
|
+
emit('update:modelValue', res)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const massRemove = () => {
|
|
62
|
+
let res = props.modelValue
|
|
63
|
+
for (const index of selectedRows.value.map(x => x.__id)) {
|
|
64
|
+
res.splice(index, 1)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
emit('update:modelValue', res)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const model = ref({
|
|
71
|
+
open: false,
|
|
72
|
+
index: 0
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const open = (index: number) => {
|
|
76
|
+
model.value = { open: true, index: index }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const actions = (i: number) => [
|
|
80
|
+
[
|
|
81
|
+
{
|
|
82
|
+
label: 'edit',
|
|
83
|
+
click: () => open(i)
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
{
|
|
88
|
+
label: 'delete',
|
|
89
|
+
click: () => {
|
|
90
|
+
remove(i)
|
|
91
|
+
selectedRows.value = []
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
const massActions = [
|
|
98
|
+
[
|
|
99
|
+
{
|
|
100
|
+
label: 'delete',
|
|
101
|
+
click: () => {
|
|
102
|
+
massRemove()
|
|
103
|
+
selectedRows.value = []
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
const columns = computed(() => {
|
|
110
|
+
const columns = props.columns ?? []
|
|
111
|
+
|
|
112
|
+
if (props.items.type === 'object') {
|
|
113
|
+
for (const column of Object.keys(props?.items?.properties).map(x => ({ key: x, label: x }))) {
|
|
114
|
+
columns.push(column)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (props.edit) {
|
|
119
|
+
columns.push({
|
|
120
|
+
key: 'actions',
|
|
121
|
+
class: 'w-2'
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return columns
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const selectedRows = ref([])
|
|
129
|
+
|
|
130
|
+
const todoStatus = [
|
|
131
|
+
{
|
|
132
|
+
key: 'uncompleted',
|
|
133
|
+
label: 'In Progress',
|
|
134
|
+
value: false
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
key: 'completed',
|
|
138
|
+
label: 'Completed',
|
|
139
|
+
value: true
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
const expand = ref({
|
|
144
|
+
openedRows: [],
|
|
145
|
+
row: null
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const options = computed(() => {
|
|
149
|
+
const res = {}
|
|
150
|
+
if (props.edit) {
|
|
151
|
+
res.modelValue = selectedRows.value
|
|
152
|
+
res['onUpdate:modelValue'] = value => (selectedRows.value = value)
|
|
153
|
+
}
|
|
154
|
+
if (props.editInline) {
|
|
155
|
+
res.expand = expand.value
|
|
156
|
+
res['update:expand'] = value => (expand.value = value)
|
|
157
|
+
}
|
|
158
|
+
return res
|
|
159
|
+
})
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<template>
|
|
163
|
+
<div>
|
|
164
|
+
<h2 v-if="props.title" class="mb-4 text-2xl leading-none tracking-tight">{{ props.title }}</h2>
|
|
165
|
+
<p v-if="props.description" class="mb-4">{{ props.description }}</p>
|
|
166
|
+
|
|
167
|
+
<div>
|
|
168
|
+
<div class="flex justify-between items-center w-full px-4 py-3">
|
|
169
|
+
<div class="flex gap-1.5 items-center">
|
|
170
|
+
<UButton
|
|
171
|
+
v-if="edit"
|
|
172
|
+
:disabled="maxItems && values.length >= maxItems"
|
|
173
|
+
@click="add"
|
|
174
|
+
icon="i-heroicons-plus"
|
|
175
|
+
trailing
|
|
176
|
+
color="gray"
|
|
177
|
+
size="xs"
|
|
178
|
+
/>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="flex gap-1.5 items-center">
|
|
181
|
+
<UDropdown v-if="selectedRows.length > 0" :items="massActions">
|
|
182
|
+
<UButton icon="i-heroicons-chevron-down" trailing color="gray" size="xs"> Action </UButton>
|
|
183
|
+
</UDropdown>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
<UTable v-bind="options" by="__id" :rows="values" :columns="columns">
|
|
187
|
+
<template #name-data="{ row }">
|
|
188
|
+
<span :class="[selected.find(person => person.id === row.id) && 'text-primary-500 dark:text-primary-400']">{{
|
|
189
|
+
row.name
|
|
190
|
+
}}</span>
|
|
191
|
+
</template>
|
|
192
|
+
|
|
193
|
+
<template #expand="{ row, index }">
|
|
194
|
+
<div class="p-4">
|
|
195
|
+
<SComponent
|
|
196
|
+
:id="id.length <= 0 ? `${index}` : `${id}[${index}].${items.id}`"
|
|
197
|
+
:model-value="row"
|
|
198
|
+
:json-schema-path="jsonSchemaPath?.length <= 0 ? `properties.${index}` : `${jsonSchemaPath}[${index}]`"
|
|
199
|
+
v-bind="items"
|
|
200
|
+
:isRequired="false"
|
|
201
|
+
class="mt-4"
|
|
202
|
+
@update:model-value="event => onInput(event, index)"
|
|
203
|
+
@submit="() => emit('submit')"
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
</template>
|
|
207
|
+
|
|
208
|
+
<template #actions-data="{ index }">
|
|
209
|
+
<UDropdown :items="actions(index)">
|
|
210
|
+
<UButton color="gray" variant="ghost" icon="i-heroicons-ellipsis-horizontal-20-solid" />
|
|
211
|
+
</UDropdown>
|
|
212
|
+
</template>
|
|
213
|
+
</UTable>
|
|
214
|
+
</div>
|
|
215
|
+
<UModal v-model="model.open">
|
|
216
|
+
<UCard>
|
|
217
|
+
<SComponent
|
|
218
|
+
:id="id.length <= 0 ? `${model.index}` : `${id}[0].${items.id}`"
|
|
219
|
+
:model-value="values[model.index]"
|
|
220
|
+
:json-schema-path="
|
|
221
|
+
jsonSchemaPath?.length <= 0 ? `properties.${model.index}` : `${jsonSchemaPath}[${model.index}]`
|
|
222
|
+
"
|
|
223
|
+
v-bind="items"
|
|
224
|
+
:isRequired="false"
|
|
225
|
+
class="mt-4"
|
|
226
|
+
@update:model-value="event => onInput(event, model.index)"
|
|
227
|
+
@submit="() => emit('submit')"
|
|
228
|
+
/>
|
|
229
|
+
<template #footer>
|
|
230
|
+
<UButton>Save</UButton>
|
|
231
|
+
</template>
|
|
232
|
+
</UCard>
|
|
233
|
+
</UModal>
|
|
234
|
+
</div>
|
|
235
|
+
</template>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { computed
|
|
2
|
+
import { computed } from 'vue'
|
|
3
3
|
|
|
4
4
|
type Properties = {
|
|
5
5
|
properties?: { [key: string]: unknown }
|
|
@@ -9,7 +9,7 @@ export type SComponentProps = {
|
|
|
9
9
|
id?: string
|
|
10
10
|
title?: string
|
|
11
11
|
renderer?: string
|
|
12
|
-
type
|
|
12
|
+
type?: string
|
|
13
13
|
properties?: Properties
|
|
14
14
|
enum?: string[]
|
|
15
15
|
enum_titles?: string[]
|
|
@@ -18,10 +18,15 @@ export type SComponentProps = {
|
|
|
18
18
|
jsonSchemaPath?: string
|
|
19
19
|
required?: string[]
|
|
20
20
|
isRequired: boolean
|
|
21
|
+
items?: { [key: string]: unknown }
|
|
22
|
+
edit?: boolean
|
|
23
|
+
minItems?: number
|
|
24
|
+
maxItems?: number
|
|
25
|
+
editInline?: boolean
|
|
21
26
|
validateFields?: (fields: string[]) => Promise<boolean>
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
const props = withDefaults(defineProps<SComponentProps>(), { id: '', jsonSchemaPath: '
|
|
29
|
+
const props = withDefaults(defineProps<SComponentProps>(), { id: '', jsonSchemaPath: '' })
|
|
25
30
|
const value = defineModel<unknown>({ required: true, default: '' })
|
|
26
31
|
const emit = defineEmits(['submit'])
|
|
27
32
|
|
|
@@ -36,13 +41,19 @@ const onSubmit = () => {
|
|
|
36
41
|
|
|
37
42
|
<template>
|
|
38
43
|
<div class="s-form-group">
|
|
39
|
-
<component
|
|
40
|
-
|
|
44
|
+
<component
|
|
45
|
+
v-if="renderer"
|
|
46
|
+
v-bind="options"
|
|
47
|
+
v-model="value"
|
|
48
|
+
:is="typeof renderer === 'string' ? renderer : undefined"
|
|
49
|
+
@submit="onSubmit"
|
|
50
|
+
/>
|
|
41
51
|
|
|
42
52
|
<SDate v-else-if="type === 'string' && format === 'full-date'" v-bind="options" v-model="value" />
|
|
43
53
|
<SInputField v-else-if="type === 'string'" v-bind="options" v-model="value" />
|
|
44
54
|
<SToggle v-else-if="type === 'boolean'" v-bind="options" v-model="value" />
|
|
45
55
|
<SObject v-else-if="type === 'object'" v-bind="options" v-model="value" @submit="onSubmit" />
|
|
56
|
+
<SArray v-else-if="type === 'array'" v-bind="options" v-model="value" />
|
|
46
57
|
<div v-else>else</div>
|
|
47
58
|
</div>
|
|
48
59
|
</template>
|
|
@@ -52,14 +52,19 @@ const submit = () => {
|
|
|
52
52
|
|
|
53
53
|
<template>
|
|
54
54
|
<div>
|
|
55
|
-
|
|
55
|
+
{{ formRef ? formRef.errors : undefined }}
|
|
56
56
|
<UForm ref="formRef" :state="value" :schema="formValidationSchema" autocomplete="on" @submit="onSubmit">
|
|
57
|
-
<SComponent
|
|
58
|
-
|
|
57
|
+
<SComponent
|
|
58
|
+
v-model="value"
|
|
59
|
+
v-bind="schema"
|
|
60
|
+
:validate-fields="validateFields"
|
|
61
|
+
@submit="submit"
|
|
62
|
+
:isRequired="true"
|
|
63
|
+
/>
|
|
59
64
|
<slot />
|
|
60
65
|
|
|
61
66
|
<slot name="submit">
|
|
62
|
-
<UButton class="btn" type="submit"> Submit </UButton>
|
|
67
|
+
<UButton class="btn mt-4" type="submit"> Submit </UButton>
|
|
63
68
|
</slot>
|
|
64
69
|
</UForm>
|
|
65
70
|
</div>
|