@rokkit/forms 1.0.0-next.125 → 1.0.0-next.128
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.
- package/README.md +251 -0
- package/dist/src/display/index.d.ts +5 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/input/index.d.ts +3 -0
- package/dist/src/lib/builder.svelte.d.ts +114 -4
- package/dist/src/lib/lookup.svelte.d.ts +87 -0
- package/dist/src/lib/renderers.d.ts +23 -0
- package/package.json +6 -4
- package/src/FieldLayout.svelte +4 -11
- package/src/FormRenderer.svelte +202 -61
- package/src/InfoField.svelte +26 -0
- package/src/Input.svelte +17 -61
- package/src/InputField.svelte +15 -11
- package/src/ValidationReport.svelte +52 -0
- package/src/display/DisplayCardGrid.svelte +68 -0
- package/src/display/DisplayList.svelte +31 -0
- package/src/display/DisplaySection.svelte +20 -0
- package/src/display/DisplayTable.svelte +68 -0
- package/src/display/DisplayValue.svelte +44 -0
- package/src/display/index.js +5 -0
- package/src/index.js +14 -0
- package/src/input/ArrayEditor.svelte +108 -0
- package/src/input/InputCheckbox.svelte +4 -5
- package/src/input/InputColor.svelte +6 -1
- package/src/input/InputDate.svelte +6 -1
- package/src/input/InputDateTime.svelte +6 -1
- package/src/input/InputEmail.svelte +6 -1
- package/src/input/InputFile.svelte +6 -2
- package/src/input/InputMonth.svelte +6 -1
- package/src/input/InputNumber.svelte +6 -1
- package/src/input/InputPassword.svelte +6 -1
- package/src/input/InputRadio.svelte +3 -3
- package/src/input/InputRange.svelte +6 -1
- package/src/input/InputSelect.svelte +31 -53
- package/src/input/InputSwitch.svelte +4 -15
- package/src/input/InputTel.svelte +6 -1
- package/src/input/InputText.svelte +6 -1
- package/src/input/InputTextArea.svelte +6 -1
- package/src/input/InputTime.svelte +6 -1
- package/src/input/InputToggle.svelte +28 -0
- package/src/input/InputUrl.svelte +6 -1
- package/src/input/InputWeek.svelte +6 -1
- package/src/input/index.js +3 -1
- package/src/lib/Input.svelte +3 -3
- package/src/lib/builder.svelte.js +425 -30
- package/src/lib/fields.js +2 -2
- package/src/lib/layout.js +2 -2
- package/src/lib/lookup.svelte.js +334 -0
- package/src/lib/renderers.js +83 -0
- package/src/lib/schema.js +1 -1
- package/src/types.js +0 -9
- package/dist/src/forms-old/input/types.d.ts +0 -7
- package/dist/src/forms-old/lib/form.d.ts +0 -95
- package/dist/src/forms-old/lib/index.d.ts +0 -1
- package/dist/src/lib/deprecated/nested.d.ts +0 -48
- package/dist/src/lib/deprecated/nested.spec.d.ts +0 -1
- package/dist/src/lib/deprecated/validator.d.ts +0 -30
- package/dist/src/lib/deprecated/validator.spec.d.ts +0 -1
- package/src/DataEditor.svelte +0 -30
- package/src/ListEditor.svelte +0 -44
- package/src/NestedEditor.svelte +0 -85
- package/src/forms-old/CheckBox.svelte +0 -56
- package/src/forms-old/DataEditor.svelte +0 -30
- package/src/forms-old/FieldLayout.svelte +0 -48
- package/src/forms-old/Form.svelte +0 -17
- package/src/forms-old/Icon.svelte +0 -76
- package/src/forms-old/Item.svelte +0 -25
- package/src/forms-old/ListEditor.svelte +0 -44
- package/src/forms-old/Tabs.svelte +0 -57
- package/src/forms-old/Wrapper.svelte +0 -12
- package/src/forms-old/input/Input.svelte +0 -17
- package/src/forms-old/input/InputField.svelte +0 -70
- package/src/forms-old/input/InputSelect.svelte +0 -23
- package/src/forms-old/input/InputSwitch.svelte +0 -19
- package/src/forms-old/input/types.js +0 -29
- package/src/forms-old/lib/form.js +0 -72
- package/src/forms-old/lib/index.js +0 -12
- package/src/forms-old/mocks/CustomField.svelte +0 -7
- package/src/forms-old/mocks/CustomWrapper.svelte +0 -8
- package/src/forms-old/mocks/Register.svelte +0 -25
- package/src/inp/Input.svelte +0 -17
- package/src/inp/InputField.svelte +0 -69
- package/src/inp/InputSelect.svelte +0 -23
- package/src/inp/InputSwitch.svelte +0 -19
- package/src/lib/deprecated/Form.svelte +0 -17
- package/src/lib/deprecated/FormRenderer.svelte +0 -121
- package/src/lib/deprecated/nested.js +0 -192
- package/src/lib/deprecated/nested.spec.js +0 -512
- package/src/lib/deprecated/validator.js +0 -137
- package/src/lib/deprecated/validator.spec.js +0 -348
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} LookupConfig
|
|
3
|
+
* @property {string} [url] - URL template with optional placeholders (e.g., '/api/cities?country={country}')
|
|
4
|
+
* @property {(formData: any) => Promise<any[]>} [fetch] - Async function replacing URL template
|
|
5
|
+
* @property {any[]} [source] - Pre-loaded array for filter pattern
|
|
6
|
+
* @property {(items: any[], formData: any) => any[]} [filter] - Client-side filter applied to source
|
|
7
|
+
* @property {(formData: any) => string} [cacheKey] - Custom cache key for fetch hooks (no caching when absent)
|
|
8
|
+
* @property {string[]} [dependsOn] - Field paths this lookup depends on
|
|
9
|
+
* @property {Object} [fields] - Field mapping for the response data
|
|
10
|
+
* @property {number} [cacheTime] - Cache duration in milliseconds (default: 5 minutes)
|
|
11
|
+
* @property {(data: any) => any[]} [transform] - Transform response data to options array
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} LookupState
|
|
16
|
+
* @property {any[]} options - Current options
|
|
17
|
+
* @property {boolean} loading - Whether lookup is in progress
|
|
18
|
+
* @property {string|null} error - Error message if lookup failed
|
|
19
|
+
* @property {boolean} disabled - Whether field is disabled (deps not met)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Cache entry structure
|
|
24
|
+
* @typedef {Object} CacheEntry
|
|
25
|
+
* @property {any[]} data - Cached options
|
|
26
|
+
* @property {number} timestamp - When the cache was created
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const DEFAULT_CACHE_TIME = 5 * 60 * 1000 // 5 minutes
|
|
30
|
+
|
|
31
|
+
/** @type {Map<string, CacheEntry>} */
|
|
32
|
+
const cache = new Map()
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Interpolates URL template with values
|
|
36
|
+
* @param {string} template - URL template with {placeholders}
|
|
37
|
+
* @param {Object} values - Values to interpolate
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
function interpolateUrl(template, values) {
|
|
41
|
+
return template.replace(/\{(\w+)\}/g, (_, key) => {
|
|
42
|
+
const value = values[key]
|
|
43
|
+
return value !== undefined && value !== null ? encodeURIComponent(String(value)) : ''
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Generates a cache key from URL and parameters
|
|
49
|
+
* @param {string} url - The resolved URL
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
function getCacheKey(url) {
|
|
53
|
+
return url
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a cache entry is still valid
|
|
58
|
+
* @param {CacheEntry} entry
|
|
59
|
+
* @param {number} cacheTime
|
|
60
|
+
* @returns {boolean}
|
|
61
|
+
*/
|
|
62
|
+
function isCacheValid(entry, cacheTime) {
|
|
63
|
+
return Date.now() - entry.timestamp < cacheTime
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Creates a lookup provider for a field
|
|
68
|
+
* @param {LookupConfig} config - Lookup configuration
|
|
69
|
+
* @returns {Object} Lookup provider with reactive state
|
|
70
|
+
*/
|
|
71
|
+
export function createLookup(config) {
|
|
72
|
+
const {
|
|
73
|
+
url,
|
|
74
|
+
fetch: fetchHook,
|
|
75
|
+
source,
|
|
76
|
+
filter,
|
|
77
|
+
cacheKey: cacheKeyFn,
|
|
78
|
+
dependsOn = [],
|
|
79
|
+
fields = {},
|
|
80
|
+
cacheTime = DEFAULT_CACHE_TIME,
|
|
81
|
+
transform
|
|
82
|
+
} = config
|
|
83
|
+
|
|
84
|
+
let options = $state([])
|
|
85
|
+
let loading = $state(false)
|
|
86
|
+
let error = $state(null)
|
|
87
|
+
let disabled = $state(false)
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Fetches options from the configured source (URL, fetch hook, or filter)
|
|
91
|
+
* @param {Object} params - Parameter values for URL interpolation / filter context
|
|
92
|
+
* @returns {Promise<any[]>}
|
|
93
|
+
*/
|
|
94
|
+
async function fetch(params = {}) {
|
|
95
|
+
// Check if all dependencies have values
|
|
96
|
+
const missingDeps = dependsOn.filter((dep) => !params[dep] && params[dep] !== 0)
|
|
97
|
+
if (missingDeps.length > 0) {
|
|
98
|
+
options = []
|
|
99
|
+
disabled = true
|
|
100
|
+
return []
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
disabled = false
|
|
104
|
+
|
|
105
|
+
// Branch: synchronous filter over pre-loaded source
|
|
106
|
+
if (filter !== undefined && source !== undefined) {
|
|
107
|
+
options = filter(source, params)
|
|
108
|
+
return options
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Branch: async fetch hook with optional caching
|
|
112
|
+
if (fetchHook) {
|
|
113
|
+
const key = cacheKeyFn?.(params)
|
|
114
|
+
|
|
115
|
+
if (key !== undefined) {
|
|
116
|
+
const cached = cache.get(key)
|
|
117
|
+
if (cached && isCacheValid(cached, cacheTime)) {
|
|
118
|
+
options = cached.data
|
|
119
|
+
return cached.data
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
loading = true
|
|
124
|
+
error = null
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
let data = await fetchHook(params)
|
|
128
|
+
|
|
129
|
+
if (transform) {
|
|
130
|
+
data = transform(data)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!Array.isArray(data)) {
|
|
134
|
+
data = data?.data || data?.items || data?.results || []
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (key !== undefined) {
|
|
138
|
+
cache.set(key, { data, timestamp: Date.now() })
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
options = data
|
|
142
|
+
return data
|
|
143
|
+
} catch (err) {
|
|
144
|
+
error = err.message || 'Failed to load options'
|
|
145
|
+
options = []
|
|
146
|
+
return []
|
|
147
|
+
} finally {
|
|
148
|
+
loading = false
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Branch: URL-based fetch (original behavior)
|
|
153
|
+
const resolvedUrl = interpolateUrl(url, params)
|
|
154
|
+
const urlCacheKey = getCacheKey(resolvedUrl)
|
|
155
|
+
|
|
156
|
+
// Check cache first
|
|
157
|
+
const cached = cache.get(urlCacheKey)
|
|
158
|
+
if (cached && isCacheValid(cached, cacheTime)) {
|
|
159
|
+
options = cached.data
|
|
160
|
+
return cached.data
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
loading = true
|
|
164
|
+
error = null
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const response = await globalThis.fetch(resolvedUrl)
|
|
168
|
+
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let data = await response.json()
|
|
174
|
+
|
|
175
|
+
// Apply transform if provided
|
|
176
|
+
if (transform) {
|
|
177
|
+
data = transform(data)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Ensure data is an array
|
|
181
|
+
if (!Array.isArray(data)) {
|
|
182
|
+
data = data.data || data.items || data.results || []
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Cache the result
|
|
186
|
+
cache.set(urlCacheKey, { data, timestamp: Date.now() })
|
|
187
|
+
|
|
188
|
+
options = data
|
|
189
|
+
return data
|
|
190
|
+
} catch (err) {
|
|
191
|
+
error = err.message || 'Failed to load options'
|
|
192
|
+
options = []
|
|
193
|
+
return []
|
|
194
|
+
} finally {
|
|
195
|
+
loading = false
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Clears the cache for this lookup
|
|
201
|
+
*/
|
|
202
|
+
function clearCache() {
|
|
203
|
+
if (!url) return // fetch/filter hooks have no URL-keyed cache entries
|
|
204
|
+
// Clear all cache entries that match this URL pattern
|
|
205
|
+
const baseUrl = url.split('?')[0]
|
|
206
|
+
for (const key of cache.keys()) {
|
|
207
|
+
if (key.startsWith(baseUrl)) {
|
|
208
|
+
cache.delete(key)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Resets the lookup state
|
|
215
|
+
*/
|
|
216
|
+
function reset() {
|
|
217
|
+
options = []
|
|
218
|
+
loading = false
|
|
219
|
+
error = null
|
|
220
|
+
disabled = false
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
get options() {
|
|
225
|
+
return options
|
|
226
|
+
},
|
|
227
|
+
get loading() {
|
|
228
|
+
return loading
|
|
229
|
+
},
|
|
230
|
+
get error() {
|
|
231
|
+
return error
|
|
232
|
+
},
|
|
233
|
+
get disabled() {
|
|
234
|
+
return disabled
|
|
235
|
+
},
|
|
236
|
+
get dependsOn() {
|
|
237
|
+
return dependsOn
|
|
238
|
+
},
|
|
239
|
+
get fields() {
|
|
240
|
+
return fields
|
|
241
|
+
},
|
|
242
|
+
fetch,
|
|
243
|
+
clearCache,
|
|
244
|
+
reset
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Creates a lookup manager for a form with multiple lookups
|
|
250
|
+
* @param {Object<string, LookupConfig>} lookupConfigs - Map of field paths to lookup configs
|
|
251
|
+
* @returns {Object} Lookup manager
|
|
252
|
+
*/
|
|
253
|
+
export function createLookupManager(lookupConfigs) {
|
|
254
|
+
/** @type {Map<string, ReturnType<typeof createLookup>>} */
|
|
255
|
+
const lookups = new Map()
|
|
256
|
+
|
|
257
|
+
// Create lookup providers for each configured field
|
|
258
|
+
for (const [fieldPath, config] of Object.entries(lookupConfigs)) {
|
|
259
|
+
lookups.set(fieldPath, createLookup(config))
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Gets the lookup provider for a field
|
|
264
|
+
* @param {string} fieldPath
|
|
265
|
+
* @returns {ReturnType<typeof createLookup> | undefined}
|
|
266
|
+
*/
|
|
267
|
+
function getLookup(fieldPath) {
|
|
268
|
+
return lookups.get(fieldPath)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Checks if a field has a lookup configured
|
|
273
|
+
* @param {string} fieldPath
|
|
274
|
+
* @returns {boolean}
|
|
275
|
+
*/
|
|
276
|
+
function hasLookup(fieldPath) {
|
|
277
|
+
return lookups.has(fieldPath)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Handles a field value change and triggers dependent lookups
|
|
282
|
+
* @param {string} changedField - The field that changed
|
|
283
|
+
* @param {Object} formData - Current form data
|
|
284
|
+
*/
|
|
285
|
+
async function handleFieldChange(changedField, formData) {
|
|
286
|
+
// Find all lookups that depend on this field
|
|
287
|
+
for (const [_fieldPath, lookup] of lookups) {
|
|
288
|
+
if (lookup.dependsOn.includes(changedField)) {
|
|
289
|
+
// Reset dependent field value and fetch new options
|
|
290
|
+
await lookup.fetch(formData)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Initializes all lookups with current form data.
|
|
297
|
+
* Calls fetch() for every lookup so disabled state is set correctly.
|
|
298
|
+
* @param {Object} formData - Current form data
|
|
299
|
+
*/
|
|
300
|
+
async function initialize(formData) {
|
|
301
|
+
const promises = []
|
|
302
|
+
for (const [_fieldPath, lookup] of lookups) {
|
|
303
|
+
promises.push(lookup.fetch(formData))
|
|
304
|
+
}
|
|
305
|
+
await Promise.all(promises)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Clears all caches
|
|
310
|
+
*/
|
|
311
|
+
function clearAllCaches() {
|
|
312
|
+
for (const lookup of lookups.values()) {
|
|
313
|
+
lookup.clearCache()
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
getLookup,
|
|
319
|
+
hasLookup,
|
|
320
|
+
handleFieldChange,
|
|
321
|
+
initialize,
|
|
322
|
+
clearAllCaches,
|
|
323
|
+
get lookups() {
|
|
324
|
+
return lookups
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Clears the entire lookup cache
|
|
331
|
+
*/
|
|
332
|
+
export function clearLookupCache() {
|
|
333
|
+
cache.clear()
|
|
334
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type Renderer Registry
|
|
3
|
+
*
|
|
4
|
+
* Maps element type strings to Svelte input components.
|
|
5
|
+
* Consumers can extend via the `renderers` prop on FormRenderer.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import ArrayEditor from '../input/ArrayEditor.svelte'
|
|
9
|
+
import InputCheckbox from '../input/InputCheckbox.svelte'
|
|
10
|
+
import InputColor from '../input/InputColor.svelte'
|
|
11
|
+
import InputDate from '../input/InputDate.svelte'
|
|
12
|
+
import InputDateTime from '../input/InputDateTime.svelte'
|
|
13
|
+
import InputEmail from '../input/InputEmail.svelte'
|
|
14
|
+
import InputFile from '../input/InputFile.svelte'
|
|
15
|
+
import InputMonth from '../input/InputMonth.svelte'
|
|
16
|
+
import InputNumber from '../input/InputNumber.svelte'
|
|
17
|
+
import InputPassword from '../input/InputPassword.svelte'
|
|
18
|
+
import InputRadio from '../input/InputRadio.svelte'
|
|
19
|
+
import InputRange from '../input/InputRange.svelte'
|
|
20
|
+
import InputSelect from '../input/InputSelect.svelte'
|
|
21
|
+
import InputSwitch from '../input/InputSwitch.svelte'
|
|
22
|
+
import InputToggle from '../input/InputToggle.svelte'
|
|
23
|
+
import InputTel from '../input/InputTel.svelte'
|
|
24
|
+
import InputText from '../input/InputText.svelte'
|
|
25
|
+
import InputTextArea from '../input/InputTextArea.svelte'
|
|
26
|
+
import InputTime from '../input/InputTime.svelte'
|
|
27
|
+
import InputUrl from '../input/InputUrl.svelte'
|
|
28
|
+
import InputWeek from '../input/InputWeek.svelte'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Default renderer registry mapping type strings to components.
|
|
32
|
+
* @type {Record<string, import('svelte').Component>}
|
|
33
|
+
*/
|
|
34
|
+
export const defaultRenderers = {
|
|
35
|
+
array: ArrayEditor,
|
|
36
|
+
text: InputText,
|
|
37
|
+
number: InputNumber,
|
|
38
|
+
integer: InputNumber,
|
|
39
|
+
email: InputEmail,
|
|
40
|
+
password: InputPassword,
|
|
41
|
+
tel: InputTel,
|
|
42
|
+
url: InputUrl,
|
|
43
|
+
color: InputColor,
|
|
44
|
+
date: InputDate,
|
|
45
|
+
'datetime-local': InputDateTime,
|
|
46
|
+
datetime: InputDateTime,
|
|
47
|
+
time: InputTime,
|
|
48
|
+
month: InputMonth,
|
|
49
|
+
week: InputWeek,
|
|
50
|
+
range: InputRange,
|
|
51
|
+
textarea: InputTextArea,
|
|
52
|
+
file: InputFile,
|
|
53
|
+
checkbox: InputCheckbox,
|
|
54
|
+
radio: InputRadio,
|
|
55
|
+
select: InputSelect,
|
|
56
|
+
switch: InputSwitch,
|
|
57
|
+
toggle: InputToggle
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a renderer component for a form element.
|
|
62
|
+
*
|
|
63
|
+
* Resolution order:
|
|
64
|
+
* 1. Explicit `renderer` name in element props → custom registry
|
|
65
|
+
* 2. Element type → registry lookup
|
|
66
|
+
* 3. Fallback → InputText
|
|
67
|
+
*
|
|
68
|
+
* @param {{ type: string, props?: { renderer?: string } }} element
|
|
69
|
+
* @param {Record<string, import('svelte').Component>} renderers - Merged registry
|
|
70
|
+
* @returns {import('svelte').Component}
|
|
71
|
+
*/
|
|
72
|
+
export function resolveRenderer(element, renderers) {
|
|
73
|
+
// 1. Explicit renderer name from layout
|
|
74
|
+
if (element.props?.renderer && renderers[element.props.renderer]) {
|
|
75
|
+
return renderers[element.props.renderer]
|
|
76
|
+
}
|
|
77
|
+
// 2. Element type
|
|
78
|
+
if (renderers[element.type]) {
|
|
79
|
+
return renderers[element.type]
|
|
80
|
+
}
|
|
81
|
+
// 3. Fallback
|
|
82
|
+
return InputText
|
|
83
|
+
}
|
package/src/lib/schema.js
CHANGED
|
@@ -9,7 +9,7 @@ import { typeOf } from '@rokkit/data'
|
|
|
9
9
|
function deriveObjectProperties(data) {
|
|
10
10
|
const properties = {}
|
|
11
11
|
for (const [key, value] of Object.entries(data)) {
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
properties[key] = deriveSchemaFromValue(value)
|
|
14
14
|
}
|
|
15
15
|
return properties
|
package/src/types.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { toHyphenCase } from '@rokkit/core'
|
|
2
2
|
// skipcq: JS-C1003 - Importing all components from atoms
|
|
3
3
|
import * as NativeInput from '@rokkit/forms'
|
|
4
|
-
// skipcq: JS-C1003 - Importing all components from molecules
|
|
5
|
-
// import CheckBox from '@rokkit/ui'
|
|
6
|
-
|
|
7
|
-
// import InputSelect from './InputSelect.svelte'
|
|
8
|
-
// import InputSwitch from './InputSwitch.svelte'
|
|
9
|
-
|
|
10
4
|
function extractComponentMap(components, prefix = /^Input/) {
|
|
11
5
|
return Object.entries(components).reduce(
|
|
12
6
|
(acc, [name, component]) => ({
|
|
@@ -21,9 +15,6 @@ const native = extractComponentMap(NativeInput)
|
|
|
21
15
|
export const types = {
|
|
22
16
|
string: NativeInput.InputText,
|
|
23
17
|
integer: NativeInput.InputNumber,
|
|
24
|
-
// boolean: CheckBox,
|
|
25
|
-
// enum: InputSelect,
|
|
26
18
|
phone: NativeInput.InputTel,
|
|
27
19
|
...native
|
|
28
|
-
// ...extractComponentMap({ InputSelect, InputSwitch })
|
|
29
20
|
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export namespace types {
|
|
2
|
-
export let string: import("svelte/legacy").LegacyComponentType;
|
|
3
|
-
export let integer: import("svelte/legacy").LegacyComponentType;
|
|
4
|
-
export { CheckBox as boolean };
|
|
5
|
-
export let phone: import("svelte/legacy").LegacyComponentType;
|
|
6
|
-
}
|
|
7
|
-
import CheckBox from '../CheckBox.svelte';
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
export namespace messages {
|
|
2
|
-
let required: string;
|
|
3
|
-
let email: string;
|
|
4
|
-
let url: string;
|
|
5
|
-
let color: string;
|
|
6
|
-
let number: string;
|
|
7
|
-
let min: string;
|
|
8
|
-
let max: string;
|
|
9
|
-
let pattern: string;
|
|
10
|
-
let exclusiveMin: string;
|
|
11
|
-
let exclusiveMax: string;
|
|
12
|
-
let minLength: string;
|
|
13
|
-
let maxLength: string;
|
|
14
|
-
let minItems: string;
|
|
15
|
-
let maxItems: string;
|
|
16
|
-
let uniqueItems: string;
|
|
17
|
-
let contains: string;
|
|
18
|
-
let exclude: string;
|
|
19
|
-
let integer: string;
|
|
20
|
-
}
|
|
21
|
-
export namespace dataTypes {
|
|
22
|
-
export namespace integer_1 {
|
|
23
|
-
let editor: string;
|
|
24
|
-
namespace props {
|
|
25
|
-
let type: string;
|
|
26
|
-
let step: number;
|
|
27
|
-
}
|
|
28
|
-
let availableProps: string[];
|
|
29
|
-
}
|
|
30
|
-
export { integer_1 as integer };
|
|
31
|
-
export namespace number_1 {
|
|
32
|
-
let editor_1: string;
|
|
33
|
-
export { editor_1 as editor };
|
|
34
|
-
export namespace props_1 {
|
|
35
|
-
let type_1: string;
|
|
36
|
-
export { type_1 as type };
|
|
37
|
-
let step_1: number;
|
|
38
|
-
export { step_1 as step };
|
|
39
|
-
}
|
|
40
|
-
export { props_1 as props };
|
|
41
|
-
let availableProps_1: string[];
|
|
42
|
-
export { availableProps_1 as availableProps };
|
|
43
|
-
}
|
|
44
|
-
export { number_1 as number };
|
|
45
|
-
export namespace range {
|
|
46
|
-
let editor_2: string;
|
|
47
|
-
export { editor_2 as editor };
|
|
48
|
-
export namespace props_2 {
|
|
49
|
-
let type_2: string;
|
|
50
|
-
export { type_2 as type };
|
|
51
|
-
}
|
|
52
|
-
export { props_2 as props };
|
|
53
|
-
}
|
|
54
|
-
export let string: {
|
|
55
|
-
default: string;
|
|
56
|
-
text: string;
|
|
57
|
-
password: string;
|
|
58
|
-
email: string;
|
|
59
|
-
url: string;
|
|
60
|
-
tel: string;
|
|
61
|
-
date: string;
|
|
62
|
-
'datetime-local': string;
|
|
63
|
-
time: string;
|
|
64
|
-
week: string;
|
|
65
|
-
month: string;
|
|
66
|
-
file: string;
|
|
67
|
-
hidden: string;
|
|
68
|
-
color: string;
|
|
69
|
-
colorpicker: string;
|
|
70
|
-
};
|
|
71
|
-
export namespace _enum {
|
|
72
|
-
let _default: string;
|
|
73
|
-
export { _default as default };
|
|
74
|
-
export let select: string;
|
|
75
|
-
export let radio: string;
|
|
76
|
-
}
|
|
77
|
-
export { _enum as enum };
|
|
78
|
-
export namespace boolean {
|
|
79
|
-
let _default_1: string;
|
|
80
|
-
export { _default_1 as default };
|
|
81
|
-
export let checkbox: string;
|
|
82
|
-
let _switch: string;
|
|
83
|
-
export { _switch as switch };
|
|
84
|
-
let radio_1: string;
|
|
85
|
-
export { radio_1 as radio };
|
|
86
|
-
}
|
|
87
|
-
export namespace array {
|
|
88
|
-
let _default_2: string;
|
|
89
|
-
export { _default_2 as default };
|
|
90
|
-
}
|
|
91
|
-
export namespace object {
|
|
92
|
-
let _default_3: string;
|
|
93
|
-
export { _default_3 as default };
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { messages, dataTypes } from "./form";
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Flattens an object into a flat object
|
|
3
|
-
*
|
|
4
|
-
* @param {Object} input - The object to flatten
|
|
5
|
-
* @param {String} scope - The scope of the object
|
|
6
|
-
*/
|
|
7
|
-
export function flattenObject(input: Object, scope?: string): any;
|
|
8
|
-
/**
|
|
9
|
-
* Flattens an object into an array of key-value pairs
|
|
10
|
-
*
|
|
11
|
-
* @param {Object} input - The object to flatten
|
|
12
|
-
* @param {String} scope - The scope of the object
|
|
13
|
-
*/
|
|
14
|
-
export function flattenAttributes(input: Object, scope?: string): {
|
|
15
|
-
key: string;
|
|
16
|
-
value: any;
|
|
17
|
-
type: string;
|
|
18
|
-
scope: string;
|
|
19
|
-
}[];
|
|
20
|
-
/**
|
|
21
|
-
* Derives a nested schema from an object
|
|
22
|
-
*
|
|
23
|
-
* @param {Object} input - The object to derive the schema from
|
|
24
|
-
* @param {String} scope - The scope of the object
|
|
25
|
-
* @returns {Object} The derived schema
|
|
26
|
-
*/
|
|
27
|
-
export function deriveNestedSchema(input: Object, scope?: string): Object;
|
|
28
|
-
/**
|
|
29
|
-
* Flattens an element into a flat object
|
|
30
|
-
*
|
|
31
|
-
* @param {Object} element - The element to flatten
|
|
32
|
-
*/
|
|
33
|
-
export function flattenElement(element: Object): any;
|
|
34
|
-
/**
|
|
35
|
-
* Generates an index array referencing the input data
|
|
36
|
-
*
|
|
37
|
-
* @param {Object} data - The flat object to index
|
|
38
|
-
* @param {String} key - The key to use as index
|
|
39
|
-
*/
|
|
40
|
-
export function generateIndex(data: Object, key?: string): any;
|
|
41
|
-
/**
|
|
42
|
-
* Generates a tree table from the input data
|
|
43
|
-
*
|
|
44
|
-
* @param {Object} data - The data to generate the tree table from
|
|
45
|
-
* @param {String} key - The key to use as index
|
|
46
|
-
* @param {Boolean} ellipsis - Whether to truncate the value
|
|
47
|
-
*/
|
|
48
|
-
export function generateTreeTable(data: Object, key?: string, ellipsis?: boolean): any;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get a validator function that takes a regex expression and returns a validation function
|
|
3
|
-
*
|
|
4
|
-
* @param {string|RegExp} pattern
|
|
5
|
-
* @returns {import('./types').ValidationFunction}
|
|
6
|
-
*/
|
|
7
|
-
export function getPatternValidator(pattern: string | RegExp): any;
|
|
8
|
-
/**
|
|
9
|
-
* Get a validator function that takes a min and max value and returns a validation function
|
|
10
|
-
*
|
|
11
|
-
* @param {number} min
|
|
12
|
-
* @param {number} max
|
|
13
|
-
* @returns {import('./types').ValidationFunction}
|
|
14
|
-
*/
|
|
15
|
-
export function getRangeValidator(min: number, max: number): any;
|
|
16
|
-
/**
|
|
17
|
-
* Get a validator function that takes a type and returns a validation function
|
|
18
|
-
*
|
|
19
|
-
* @param {string} type
|
|
20
|
-
* @returns {import('./types').ValidationFunction}
|
|
21
|
-
* @throws {Error} - If the type is invalid
|
|
22
|
-
*/
|
|
23
|
-
export function getTypeValidator(type: string): any;
|
|
24
|
-
/**
|
|
25
|
-
* Create a custom Svelte store with validation rules.
|
|
26
|
-
* @param {*} value - The initial value for the store.
|
|
27
|
-
* @param {Array<import('./types').ValidationRule>} rules - An array of validation rules.
|
|
28
|
-
* @returns {import('svelte').Writable<import('./types').ValidationResult>} - The custom Svelte store.
|
|
29
|
-
*/
|
|
30
|
-
export function verifiable(input: any, rules: Array<any>): import("svelte").Writable<any>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/src/DataEditor.svelte
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { setContext } from 'svelte'
|
|
3
|
-
import { types } from './input/types'
|
|
4
|
-
import Wrapper from './wrappers/Wrapper.svelte'
|
|
5
|
-
import Item from './Item.svelte'
|
|
6
|
-
import Tabs from './Tabs.svelte'
|
|
7
|
-
import FieldLayout from './FieldLayout.svelte'
|
|
8
|
-
import { noop } from '@rokkit/core'
|
|
9
|
-
|
|
10
|
-
const registry = $state({})
|
|
11
|
-
setContext('registry', registry)
|
|
12
|
-
|
|
13
|
-
import { deriveSchemaFromValue, deriveLayoutFromValue, getSchemaWithLayout } from './lib'
|
|
14
|
-
|
|
15
|
-
let { value, schema = null, layout = null, using = {}, onchange = noop } = $props()
|
|
16
|
-
|
|
17
|
-
registry.editors = { ...types, ...using?.editors }
|
|
18
|
-
registry.components = { default: Item, ...using?.components }
|
|
19
|
-
registry.wrappers = { default: Wrapper, ...using?.wrappers }
|
|
20
|
-
registry.navigators = { default: Tabs, ...using?.navigators }
|
|
21
|
-
|
|
22
|
-
let schemaWithLayout = $derived.by(() => {
|
|
23
|
-
return getSchemaWithLayout(
|
|
24
|
-
schema ?? deriveSchemaFromValue(value),
|
|
25
|
-
layout ?? deriveLayoutFromValue(value)
|
|
26
|
-
)
|
|
27
|
-
})
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
<FieldLayout schema={schemaWithLayout} bind:value {onchange} />
|