@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.
Files changed (90) hide show
  1. package/README.md +251 -0
  2. package/dist/src/display/index.d.ts +5 -0
  3. package/dist/src/index.d.ts +9 -0
  4. package/dist/src/input/index.d.ts +3 -0
  5. package/dist/src/lib/builder.svelte.d.ts +114 -4
  6. package/dist/src/lib/lookup.svelte.d.ts +87 -0
  7. package/dist/src/lib/renderers.d.ts +23 -0
  8. package/package.json +6 -4
  9. package/src/FieldLayout.svelte +4 -11
  10. package/src/FormRenderer.svelte +202 -61
  11. package/src/InfoField.svelte +26 -0
  12. package/src/Input.svelte +17 -61
  13. package/src/InputField.svelte +15 -11
  14. package/src/ValidationReport.svelte +52 -0
  15. package/src/display/DisplayCardGrid.svelte +68 -0
  16. package/src/display/DisplayList.svelte +31 -0
  17. package/src/display/DisplaySection.svelte +20 -0
  18. package/src/display/DisplayTable.svelte +68 -0
  19. package/src/display/DisplayValue.svelte +44 -0
  20. package/src/display/index.js +5 -0
  21. package/src/index.js +14 -0
  22. package/src/input/ArrayEditor.svelte +108 -0
  23. package/src/input/InputCheckbox.svelte +4 -5
  24. package/src/input/InputColor.svelte +6 -1
  25. package/src/input/InputDate.svelte +6 -1
  26. package/src/input/InputDateTime.svelte +6 -1
  27. package/src/input/InputEmail.svelte +6 -1
  28. package/src/input/InputFile.svelte +6 -2
  29. package/src/input/InputMonth.svelte +6 -1
  30. package/src/input/InputNumber.svelte +6 -1
  31. package/src/input/InputPassword.svelte +6 -1
  32. package/src/input/InputRadio.svelte +3 -3
  33. package/src/input/InputRange.svelte +6 -1
  34. package/src/input/InputSelect.svelte +31 -53
  35. package/src/input/InputSwitch.svelte +4 -15
  36. package/src/input/InputTel.svelte +6 -1
  37. package/src/input/InputText.svelte +6 -1
  38. package/src/input/InputTextArea.svelte +6 -1
  39. package/src/input/InputTime.svelte +6 -1
  40. package/src/input/InputToggle.svelte +28 -0
  41. package/src/input/InputUrl.svelte +6 -1
  42. package/src/input/InputWeek.svelte +6 -1
  43. package/src/input/index.js +3 -1
  44. package/src/lib/Input.svelte +3 -3
  45. package/src/lib/builder.svelte.js +425 -30
  46. package/src/lib/fields.js +2 -2
  47. package/src/lib/layout.js +2 -2
  48. package/src/lib/lookup.svelte.js +334 -0
  49. package/src/lib/renderers.js +83 -0
  50. package/src/lib/schema.js +1 -1
  51. package/src/types.js +0 -9
  52. package/dist/src/forms-old/input/types.d.ts +0 -7
  53. package/dist/src/forms-old/lib/form.d.ts +0 -95
  54. package/dist/src/forms-old/lib/index.d.ts +0 -1
  55. package/dist/src/lib/deprecated/nested.d.ts +0 -48
  56. package/dist/src/lib/deprecated/nested.spec.d.ts +0 -1
  57. package/dist/src/lib/deprecated/validator.d.ts +0 -30
  58. package/dist/src/lib/deprecated/validator.spec.d.ts +0 -1
  59. package/src/DataEditor.svelte +0 -30
  60. package/src/ListEditor.svelte +0 -44
  61. package/src/NestedEditor.svelte +0 -85
  62. package/src/forms-old/CheckBox.svelte +0 -56
  63. package/src/forms-old/DataEditor.svelte +0 -30
  64. package/src/forms-old/FieldLayout.svelte +0 -48
  65. package/src/forms-old/Form.svelte +0 -17
  66. package/src/forms-old/Icon.svelte +0 -76
  67. package/src/forms-old/Item.svelte +0 -25
  68. package/src/forms-old/ListEditor.svelte +0 -44
  69. package/src/forms-old/Tabs.svelte +0 -57
  70. package/src/forms-old/Wrapper.svelte +0 -12
  71. package/src/forms-old/input/Input.svelte +0 -17
  72. package/src/forms-old/input/InputField.svelte +0 -70
  73. package/src/forms-old/input/InputSelect.svelte +0 -23
  74. package/src/forms-old/input/InputSwitch.svelte +0 -19
  75. package/src/forms-old/input/types.js +0 -29
  76. package/src/forms-old/lib/form.js +0 -72
  77. package/src/forms-old/lib/index.js +0 -12
  78. package/src/forms-old/mocks/CustomField.svelte +0 -7
  79. package/src/forms-old/mocks/CustomWrapper.svelte +0 -8
  80. package/src/forms-old/mocks/Register.svelte +0 -25
  81. package/src/inp/Input.svelte +0 -17
  82. package/src/inp/InputField.svelte +0 -69
  83. package/src/inp/InputSelect.svelte +0 -23
  84. package/src/inp/InputSwitch.svelte +0 -19
  85. package/src/lib/deprecated/Form.svelte +0 -17
  86. package/src/lib/deprecated/FormRenderer.svelte +0 -121
  87. package/src/lib/deprecated/nested.js +0 -192
  88. package/src/lib/deprecated/nested.spec.js +0 -512
  89. package/src/lib/deprecated/validator.js +0 -137
  90. package/src/lib/deprecated/validator.spec.js +0 -348
@@ -1,512 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
-
3
- import { deriveNestedSchema, flattenElement, generateTreeTable, generateIndex } from './nested'
4
-
5
- describe('nested', () => {
6
- describe('flattenElement', () => {
7
- it('should flatten an element', () => {
8
- const input = {
9
- key: 'test',
10
- value: 'test',
11
- type: 'string',
12
- scope: '#/test'
13
- }
14
- const result = flattenElement(input)
15
- expect(result).toEqual({
16
- [input.scope]: input
17
- })
18
- })
19
-
20
- it('should flatten an array element', () => {
21
- const input = {
22
- key: 'test',
23
- value: ['red', 'blue', 'green'],
24
- type: 'array',
25
- scope: '#/test'
26
- }
27
- const result = flattenElement(input)
28
- expect(result).toEqual({
29
- '#/test': {
30
- key: 'test',
31
- scope: '#/test',
32
- type: 'array',
33
- value: ['red', 'blue', 'green']
34
- },
35
- '#/test/[0]': { value: 'red', type: 'string', scope: '#/test/[0]', key: '[0]' },
36
- '#/test/[1]': { value: 'blue', type: 'string', scope: '#/test/[1]', key: '[1]' },
37
- '#/test/[2]': { value: 'green', type: 'string', scope: '#/test/[2]', key: '[2]' }
38
- })
39
- })
40
-
41
- it('should flatten an object element', () => {
42
- const input = {
43
- key: 'test',
44
- value: {
45
- red: '#FF0000',
46
- blue: '#0000FF',
47
- green: '#00FF00'
48
- },
49
- type: 'object',
50
- scope: '#/test'
51
- }
52
- const result = flattenElement(input)
53
- expect(result).toEqual({
54
- '#/test': {
55
- key: 'test',
56
- scope: '#/test',
57
- type: 'object',
58
- value: {
59
- blue: '#0000FF',
60
- green: '#00FF00',
61
- red: '#FF0000'
62
- }
63
- },
64
- '#/test/red': {
65
- key: 'red',
66
- scope: '#/test/red',
67
- type: 'string',
68
- value: '#FF0000'
69
- },
70
- '#/test/blue': {
71
- key: 'blue',
72
- scope: '#/test/blue',
73
- type: 'string',
74
- value: '#0000FF'
75
- },
76
- '#/test/green': {
77
- key: 'green',
78
- scope: '#/test/green',
79
- type: 'string',
80
- value: '#00FF00'
81
- }
82
- })
83
- })
84
- })
85
-
86
- describe('deriveNestedSchema', () => {
87
- it('should handle simple object', () => {
88
- const result = deriveNestedSchema({ count: 10, enabled: true, label: 'test' })
89
- expect(result).toEqual([
90
- {
91
- layout: {
92
- elements: [
93
- {
94
- label: 'count',
95
- scope: '#/count'
96
- },
97
- {
98
- label: 'enabled',
99
- scope: '#/enabled'
100
- },
101
- {
102
- label: 'label',
103
- scope: '#/label'
104
- }
105
- ],
106
- type: 'vertical'
107
- },
108
- properties: {
109
- count: {
110
- default: 10,
111
- type: 'integer'
112
- },
113
- enabled: {
114
- default: true,
115
- type: 'boolean'
116
- },
117
- label: {
118
- default: 'test',
119
- type: 'string'
120
- }
121
- },
122
- type: 'object'
123
- }
124
- ])
125
- })
126
-
127
- it('should handle empty array', () => {
128
- const result = deriveNestedSchema({ items: [] })
129
- expect(result).toEqual([
130
- {
131
- default: [],
132
- items: {
133
- type: 'string'
134
- },
135
- layout: {
136
- type: 'vertical',
137
- elements: [
138
- {
139
- scope: '#'
140
- }
141
- ]
142
- },
143
- key: 'items',
144
- scope: '#/items',
145
- type: 'array'
146
- }
147
- ])
148
- })
149
-
150
- it('should handle string array', () => {
151
- const result = deriveNestedSchema({ items: ['red', 'blue', 'green'] })
152
- expect(result).toEqual([
153
- {
154
- default: [],
155
- items: {
156
- type: 'string'
157
- },
158
- layout: {
159
- type: 'vertical',
160
- elements: [
161
- {
162
- scope: '#'
163
- }
164
- ]
165
- },
166
- key: 'items',
167
- scope: '#/items',
168
- type: 'array'
169
- }
170
- ])
171
- })
172
-
173
- it('should handle object array', () => {
174
- const result = deriveNestedSchema({
175
- modules: [
176
- {
177
- name: 'module1',
178
- enabled: true
179
- },
180
- {
181
- name: 'module2',
182
- enabled: false
183
- }
184
- ]
185
- })
186
- expect(result).toEqual([
187
- {
188
- default: [],
189
- items: {
190
- type: 'object',
191
-
192
- properties: {
193
- enabled: {
194
- type: 'boolean'
195
- },
196
- name: {
197
- type: 'string'
198
- }
199
- }
200
- },
201
- layout: {
202
- type: 'vertical',
203
- elements: [
204
- {
205
- label: 'name',
206
- scope: '#/name'
207
- },
208
- {
209
- label: 'enabled',
210
- scope: '#/enabled'
211
- }
212
- ]
213
- },
214
- key: 'modules',
215
- scope: '#/modules',
216
- type: 'array'
217
- }
218
- ])
219
- })
220
-
221
- it('should generate a nested schema', () => {
222
- const result = deriveNestedSchema({
223
- theme: {
224
- primary: {
225
- main: '#2196F3',
226
- count: 10,
227
- enabled: true
228
- }
229
- }
230
- })
231
-
232
- expect(result).toEqual([
233
- {
234
- children: [
235
- {
236
- key: 'primary',
237
- layout: {
238
- elements: [
239
- {
240
- label: 'main',
241
- scope: '#/main'
242
- },
243
- {
244
- label: 'count',
245
- scope: '#/count'
246
- },
247
- {
248
- label: 'enabled',
249
- scope: '#/enabled'
250
- }
251
- ],
252
- type: 'vertical'
253
- },
254
- scope: '#/theme/primary',
255
- properties: {
256
- count: {
257
- default: 10,
258
- type: 'integer'
259
- },
260
- enabled: {
261
- default: true,
262
- type: 'boolean'
263
- },
264
- main: {
265
- default: '#2196F3',
266
- type: 'string'
267
- }
268
- },
269
- type: 'object'
270
- }
271
- ],
272
- key: 'theme',
273
- scope: '#/theme',
274
- type: 'object'
275
- }
276
- ])
277
- })
278
- })
279
- describe('generateIndex', () => {
280
- it('should generate an index', () => {
281
- const input = [
282
- { scope: '#/root' },
283
- { scope: '#/root/child1' },
284
- { scope: '#/root/child2' },
285
- { scope: '#/anotherRoot' },
286
- { scope: '#/anotherRoot/child1' }
287
- ]
288
- const result = generateIndex(input)
289
- expect(result).toEqual([
290
- {
291
- _depth: 0,
292
- _isExpanded: true,
293
- _isParent: true,
294
-
295
- _levels: [0],
296
- _path: '#/anotherRoot',
297
- scope: '#/anotherRoot'
298
- },
299
- {
300
- _depth: 1,
301
- _isExpanded: true,
302
- _isParent: false,
303
-
304
- _levels: [0, 0],
305
- _path: '#/anotherRoot/child1',
306
- scope: '#/anotherRoot/child1'
307
- },
308
- {
309
- _depth: 0,
310
- _isExpanded: true,
311
- _isParent: true,
312
-
313
- _levels: [1],
314
- _path: '#/root',
315
- scope: '#/root'
316
- },
317
- {
318
- _depth: 1,
319
- _isExpanded: true,
320
- _isParent: false,
321
-
322
- _levels: [1, 0],
323
- _path: '#/root/child1',
324
- scope: '#/root/child1'
325
- },
326
- {
327
- _depth: 1,
328
- _isExpanded: true,
329
- _isParent: false,
330
- _levels: [1, 1],
331
- _path: '#/root/child2',
332
- scope: '#/root/child2'
333
- }
334
- ])
335
- })
336
- })
337
-
338
- describe('generateTreeTable', () => {
339
- it('should generate an empty tree table', () => {
340
- let result = generateTreeTable(null)
341
- expect(result).toEqual([])
342
- result = generateTreeTable('')
343
- expect(result).toEqual([])
344
- })
345
- it('should generate a tree table for object array', () => {
346
- const result = generateTreeTable([
347
- { scope: '#/color', value: 'red' },
348
- { scope: '#/theme', value: 'ocean' }
349
- ])
350
- expect(result).toEqual([
351
- {
352
- _depth: 0,
353
- _isExpanded: true,
354
- _isParent: true,
355
-
356
- _levels: [0],
357
- _path: '#/0',
358
- key: '0',
359
- scope: '#/0',
360
- type: 'object',
361
- value: { scope: '#/color', value: 'red' }
362
- },
363
- {
364
- _depth: 1,
365
- _isExpanded: true,
366
- _isParent: false,
367
- _levels: [0, 0],
368
- _path: '#/0/scope',
369
- key: 'scope',
370
- scope: '#/0/scope',
371
- type: 'string',
372
- value: '#/color'
373
- },
374
- {
375
- _depth: 1,
376
- _isExpanded: true,
377
- _isParent: false,
378
- _levels: [0, 1],
379
- _path: '#/0/value',
380
- key: 'value',
381
- scope: '#/0/value',
382
- type: 'string',
383
- value: 'red'
384
- },
385
- {
386
- _depth: 0,
387
- _isExpanded: true,
388
- _isParent: true,
389
- _levels: [1],
390
- _path: '#/1',
391
- key: '1',
392
- scope: '#/1',
393
- type: 'object',
394
- value: {
395
- scope: '#/theme',
396
- value: 'ocean'
397
- }
398
- },
399
- {
400
- _depth: 1,
401
- _isExpanded: true,
402
- _isParent: false,
403
-
404
- _levels: [1, 0],
405
- _path: '#/1/scope',
406
- key: 'scope',
407
- scope: '#/1/scope',
408
- type: 'string',
409
- value: '#/theme'
410
- },
411
- {
412
- _depth: 1,
413
- _isExpanded: true,
414
- _isParent: false,
415
- _levels: [1, 1],
416
- _path: '#/1/value',
417
- key: 'value',
418
- scope: '#/1/value',
419
- type: 'string',
420
- value: 'ocean'
421
- }
422
- ])
423
- })
424
- it('should generate a tree table for object', () => {
425
- const result = generateTreeTable({
426
- red: '#FF0000',
427
- blue: '#0000FF',
428
- green: '#00FF00'
429
- })
430
- expect(result).toEqual([
431
- {
432
- _depth: 0,
433
- _isExpanded: true,
434
- _isParent: false,
435
-
436
- _levels: [0],
437
- _path: '#/blue',
438
- scope: '#/blue',
439
- key: 'blue',
440
- type: 'string',
441
- value: '#0000FF'
442
- },
443
- {
444
- _depth: 0,
445
- _isExpanded: true,
446
- _isParent: false,
447
-
448
- _levels: [1],
449
- _path: '#/green',
450
- scope: '#/green',
451
- key: 'green',
452
- type: 'string',
453
- value: '#00FF00'
454
- },
455
- {
456
- _depth: 0,
457
- _isExpanded: true,
458
- _isParent: false,
459
-
460
- _levels: [2],
461
- _path: '#/red',
462
- scope: '#/red',
463
- key: 'red',
464
- type: 'string',
465
- value: '#FF0000'
466
- }
467
- ])
468
- })
469
-
470
- it('should use ellipsis to truncate data', () => {
471
- const data = {
472
- red: [1, 2]
473
- }
474
- const result = generateTreeTable(data, 'scope', true)
475
- expect(result).toEqual([
476
- {
477
- _depth: 0,
478
- _isExpanded: true,
479
- _isParent: true,
480
- _levels: [0],
481
- _path: '#/red',
482
- key: 'red',
483
- scope: '#/red',
484
- type: 'array',
485
- value: '...'
486
- },
487
- {
488
- _depth: 1,
489
- _isExpanded: true,
490
- _isParent: false,
491
- _levels: [0, 0],
492
- _path: '#/red/[0]',
493
- key: '[0]',
494
- scope: '#/red/[0]',
495
- type: 'integer',
496
- value: 1
497
- },
498
- {
499
- _depth: 1,
500
- _isExpanded: true,
501
- _isParent: false,
502
- _levels: [0, 1],
503
- _path: '#/red/[1]',
504
- key: '[1]',
505
- scope: '#/red/[1]',
506
- type: 'integer',
507
- value: 2
508
- }
509
- ])
510
- })
511
- })
512
- })
@@ -1,137 +0,0 @@
1
- import { writable } from 'svelte/store'
2
- import { omit } from 'ramda'
3
-
4
- /**
5
- * Get a validator function that takes a regex expression and returns a validation function
6
- *
7
- * @param {string|RegExp} pattern
8
- * @returns {import('./types').ValidationFunction}
9
- */
10
- export function getPatternValidator(pattern) {
11
- if (typeof pattern === 'string') {
12
- return (input) => input && input.match(pattern) !== null
13
- } else if (pattern instanceof RegExp) {
14
- return (input) => input && pattern.test(input)
15
- } else {
16
- throw new Error('Invalid pattern')
17
- }
18
- }
19
-
20
- const TYPE_VALIDATORS = {
21
- string: (input) => typeof input === 'string',
22
- number: (input) => input !== null && !isNaN(input),
23
- email: getPatternValidator(/^[^@]+@[^@]+\.[^@]+$/),
24
- url: getPatternValidator(/^(https?:\/\/\S+)$/),
25
- color: getPatternValidator(/^#[0-9a-fA-F]{6}$/),
26
- date: (input) => input !== null && !isNaN(new Date(input).getTime()),
27
- array: (input) => Array.isArray(input),
28
- object: (input) => input !== null && typeof input === 'object' && !Array.isArray(input)
29
- }
30
-
31
- /**
32
- * Check if the input is a valid number
33
- *
34
- * @param {*} input
35
- * @returns {boolean}
36
- */
37
- function isValidNumber(input) {
38
- return input !== null && input !== undefined && !isNaN(input)
39
- }
40
-
41
- /**
42
- * Get a validator function that takes a min and max value and returns a validation function
43
- *
44
- * @param {number} min
45
- * @param {number} max
46
- * @returns {import('./types').ValidationFunction}
47
- */
48
- export function getRangeValidator(min, max) {
49
- if (!isValidNumber(min)) return (input) => isValidNumber(input) && input <= max
50
- if (!isValidNumber(max)) return (input) => isValidNumber(input) && input >= min
51
- return (input) => isValidNumber(input) && input >= min && input <= max
52
- }
53
-
54
- /**
55
- * Get a validator function that takes a type and returns a validation function
56
- *
57
- * @param {string} type
58
- * @returns {import('./types').ValidationFunction}
59
- * @throws {Error} - If the type is invalid
60
- */
61
- export function getTypeValidator(type) {
62
- if (type in TYPE_VALIDATORS) return TYPE_VALIDATORS[type]
63
-
64
- return (input) => typeof input === type
65
- }
66
-
67
- /**
68
- * Get a validator function for the given rule.
69
- *
70
- * @param {import('./types').ValidationRule} rule - The rule to get the validator for.
71
- * @returns {import('./types').ValidationFunction} - The validator function.
72
- * @throws {Error} - If the rule is invalid.
73
- */
74
- function getValidator(rule) {
75
- const { validator, pattern = null, min = null, max = null, type = null } = rule
76
- const strategies = [
77
- { satisfies: typeof validator === 'function', validator },
78
- { satisfies: pattern !== null, validator: getPatternValidator(pattern ?? '*') },
79
- { satisfies: min !== null || max !== null, validator: getRangeValidator(min, max) },
80
- { satisfies: type !== null, validator: getTypeValidator(type) }
81
- ]
82
-
83
- const result = strategies.find((x) => x.satisfies)
84
- if (result) return result.validator
85
-
86
- throw new Error('Invalid rule')
87
- }
88
-
89
- /**
90
- * Evaluate the validation rules for the given value.
91
- *
92
- * @param {*} value - The value to evaluate.
93
- * @param {Array<import('./types').ValidationRule>} rules - An array of validation rules.
94
- * @returns {import('./types').ValidationResult} - The result of the evaluation.
95
- */
96
- function evaluateRules(value, rules) {
97
- let status = 'pass'
98
- const validations = []
99
-
100
- rules.forEach((rule) => {
101
- const valid = rule.validator(value)
102
- const result = {
103
- text: rule.text,
104
- valid,
105
- status: valid ? 'pass' : rule.optional ? 'warn' : 'fail'
106
- }
107
-
108
- if (status !== 'fail' && !valid) status = result.status
109
- validations.push(result)
110
- })
111
-
112
- return { value, status, validations, isValid: status === 'pass' }
113
- }
114
-
115
- /**
116
- * Create a custom Svelte store with validation rules.
117
- * @param {*} value - The initial value for the store.
118
- * @param {Array<import('./types').ValidationRule>} rules - An array of validation rules.
119
- * @returns {import('svelte').Writable<import('./types').ValidationResult>} - The custom Svelte store.
120
- */
121
- export function verifiable(input, rules) {
122
- const result = writable({ value: input })
123
-
124
- rules = (rules ?? []).map((rule) => ({
125
- ...rule,
126
- validator: getValidator(rule)
127
- }))
128
-
129
- const evaluate = (value) => result.set(evaluateRules(value, rules))
130
-
131
- evaluate(input)
132
-
133
- return {
134
- ...omit(['set'], result),
135
- update: evaluate
136
- }
137
- }