@rokkit/forms 1.0.0-next.145 → 1.0.0-next.146
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/dist/src/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/{ValidationReport.svelte → StatusList.svelte} +7 -7
- package/src/display/DisplayTable.svelte +2 -1
- package/src/display/DisplayValue.svelte +2 -1
- package/src/index.js +1 -1
- package/src/lib/builder.svelte.js +4 -7
- package/src/lib/conditions.js +5 -5
- package/src/lib/conditions.spec.js +53 -53
- package/src/lib/lookup.svelte.js +39 -11
package/dist/src/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { default as FormRenderer } from "./FormRenderer.svelte";
|
|
|
5
5
|
export { default as Input } from "./Input.svelte";
|
|
6
6
|
export { default as InputField } from "./InputField.svelte";
|
|
7
7
|
export { default as InfoField } from "./InfoField.svelte";
|
|
8
|
-
export { default as
|
|
8
|
+
export { default as StatusList } from "./StatusList.svelte";
|
|
9
9
|
export * from "./display";
|
|
10
10
|
export * from "./input";
|
|
11
11
|
export { createLookup, createLookupManager, clearLookupCache } from "./lib/lookup.svelte.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* StatusList — grouped summary of validation messages
|
|
4
4
|
* Groups items by severity (error, warning, info, success) with count headers.
|
|
5
5
|
* Supports click-to-focus via onclick callback.
|
|
6
6
|
*/
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
21
|
{#if items.length > 0}
|
|
22
|
-
<div data-
|
|
22
|
+
<div data-status-list class={className} role="status">
|
|
23
23
|
{#each grouped as group (group.state)}
|
|
24
|
-
<div data-
|
|
25
|
-
<div data-
|
|
26
|
-
<span data-
|
|
24
|
+
<div data-status-group data-severity={group.state}>
|
|
25
|
+
<div data-status-header>
|
|
26
|
+
<span data-status-count>{group.items.length}</span>
|
|
27
27
|
<span
|
|
28
28
|
>{group.items.length === 1
|
|
29
29
|
? SEVERITY_LABELS[group.state]
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
{#each group.items as item (item.path)}
|
|
34
34
|
{#if onclick}
|
|
35
35
|
<button
|
|
36
|
-
data-
|
|
36
|
+
data-status-item
|
|
37
37
|
data-status={item.state}
|
|
38
38
|
onclick={() => onclick(item.path)}
|
|
39
39
|
type="button"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
{item.text}
|
|
42
42
|
</button>
|
|
43
43
|
{:else}
|
|
44
|
-
<div data-
|
|
44
|
+
<div data-status-item data-status={item.state}>
|
|
45
45
|
{item.text}
|
|
46
46
|
</div>
|
|
47
47
|
{/if}
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const formatters = {
|
|
31
|
-
currency: (v) =>
|
|
31
|
+
currency: (v) =>
|
|
32
|
+
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(v),
|
|
32
33
|
datetime: (v) => new Date(v).toLocaleString(),
|
|
33
34
|
duration: (v) => formatDuration(v),
|
|
34
35
|
number: (v) => new Intl.NumberFormat().format(v),
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const valueFormatters = {
|
|
24
|
-
currency: (v) =>
|
|
24
|
+
currency: (v) =>
|
|
25
|
+
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(v),
|
|
25
26
|
datetime: (v) => new Date(v).toLocaleString(),
|
|
26
27
|
duration: (v) => formatDuration(v),
|
|
27
28
|
number: (v) => new Intl.NumberFormat().format(v),
|
package/src/index.js
CHANGED
|
@@ -13,7 +13,7 @@ export { default as FormRenderer } from './FormRenderer.svelte'
|
|
|
13
13
|
export { default as Input } from './Input.svelte'
|
|
14
14
|
export { default as InputField } from './InputField.svelte'
|
|
15
15
|
export { default as InfoField } from './InfoField.svelte'
|
|
16
|
-
export { default as
|
|
16
|
+
export { default as StatusList } from './StatusList.svelte'
|
|
17
17
|
|
|
18
18
|
// Display Components
|
|
19
19
|
export * from './display'
|
|
@@ -658,7 +658,8 @@ export class FormBuilder {
|
|
|
658
658
|
const value = this.getValue(fieldPath)
|
|
659
659
|
|
|
660
660
|
if (element.elements) return this.#convertNestedElement(element, fieldPath, scope, value)
|
|
661
|
-
if (element.props.readonly)
|
|
661
|
+
if (element.props.readonly)
|
|
662
|
+
return this.#convertReadonlyElement(element, fieldPath, scope, value)
|
|
662
663
|
return this.#buildStandardElement(element, fieldPath, scope, value)
|
|
663
664
|
}
|
|
664
665
|
|
|
@@ -726,13 +727,9 @@ export class FormBuilder {
|
|
|
726
727
|
*/
|
|
727
728
|
getVisibleData() {
|
|
728
729
|
const visiblePaths = new SvelteSet(
|
|
729
|
-
this.elements
|
|
730
|
-
.filter((el) => el.scope)
|
|
731
|
-
.map((el) => el.scope.replace(/^#\//, ''))
|
|
732
|
-
)
|
|
733
|
-
return Object.fromEntries(
|
|
734
|
-
Object.entries(this.#data).filter(([key]) => visiblePaths.has(key))
|
|
730
|
+
this.elements.filter((el) => el.scope).map((el) => el.scope.replace(/^#\//, ''))
|
|
735
731
|
)
|
|
732
|
+
return Object.fromEntries(Object.entries(this.#data).filter(([key]) => visiblePaths.has(key)))
|
|
736
733
|
}
|
|
737
734
|
|
|
738
735
|
/**
|
package/src/lib/conditions.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* @returns {boolean}
|
|
8
8
|
*/
|
|
9
9
|
export function evaluateCondition(condition, data) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
if (!condition) return true
|
|
11
|
+
const value = data[condition.field]
|
|
12
|
+
if ('equals' in condition) return value === condition.equals
|
|
13
|
+
if ('notEquals' in condition) return value !== condition.notEquals
|
|
14
|
+
return true
|
|
15
15
|
}
|
|
@@ -2,57 +2,57 @@ import { describe, it, expect } from 'vitest'
|
|
|
2
2
|
import { evaluateCondition } from './conditions.js'
|
|
3
3
|
|
|
4
4
|
describe('evaluateCondition', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
5
|
+
it('returns true when condition is null', () => {
|
|
6
|
+
expect(evaluateCondition(null, { accountType: 'personal' })).toBe(true)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('returns true when condition is undefined', () => {
|
|
10
|
+
expect(evaluateCondition(undefined, { accountType: 'personal' })).toBe(true)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('returns true when neither operator is set', () => {
|
|
14
|
+
expect(evaluateCondition({ field: 'accountType' }, { accountType: 'business' })).toBe(true)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
describe('equals operator', () => {
|
|
18
|
+
it('returns true when value matches', () => {
|
|
19
|
+
expect(
|
|
20
|
+
evaluateCondition({ field: 'accountType', equals: 'business' }, { accountType: 'business' })
|
|
21
|
+
).toBe(true)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('returns false when value does not match', () => {
|
|
25
|
+
expect(
|
|
26
|
+
evaluateCondition({ field: 'accountType', equals: 'business' }, { accountType: 'personal' })
|
|
27
|
+
).toBe(false)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('returns false when field is absent (undefined)', () => {
|
|
31
|
+
expect(evaluateCondition({ field: 'accountType', equals: 'business' }, {})).toBe(false)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('notEquals operator', () => {
|
|
36
|
+
it('returns true when value does not match', () => {
|
|
37
|
+
expect(
|
|
38
|
+
evaluateCondition(
|
|
39
|
+
{ field: 'accountType', notEquals: 'business' },
|
|
40
|
+
{ accountType: 'personal' }
|
|
41
|
+
)
|
|
42
|
+
).toBe(true)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('returns false when value matches', () => {
|
|
46
|
+
expect(
|
|
47
|
+
evaluateCondition(
|
|
48
|
+
{ field: 'accountType', notEquals: 'business' },
|
|
49
|
+
{ accountType: 'business' }
|
|
50
|
+
)
|
|
51
|
+
).toBe(false)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('returns true when field is absent (undefined !== value)', () => {
|
|
55
|
+
expect(evaluateCondition({ field: 'accountType', notEquals: 'business' }, {})).toBe(true)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
58
|
})
|
package/src/lib/lookup.svelte.js
CHANGED
|
@@ -186,12 +186,24 @@ async function runWithLoadingState(asyncFn, state) {
|
|
|
186
186
|
function buildLookupApi(ctx, fns) {
|
|
187
187
|
const { state, meta } = ctx
|
|
188
188
|
return {
|
|
189
|
-
get options() {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
get
|
|
193
|
-
|
|
194
|
-
|
|
189
|
+
get options() {
|
|
190
|
+
return state.options
|
|
191
|
+
},
|
|
192
|
+
get loading() {
|
|
193
|
+
return state.loading
|
|
194
|
+
},
|
|
195
|
+
get error() {
|
|
196
|
+
return state.error
|
|
197
|
+
},
|
|
198
|
+
get disabled() {
|
|
199
|
+
return state.disabled
|
|
200
|
+
},
|
|
201
|
+
get dependsOn() {
|
|
202
|
+
return meta.dependsOn
|
|
203
|
+
},
|
|
204
|
+
get fields() {
|
|
205
|
+
return meta.fields
|
|
206
|
+
},
|
|
195
207
|
...fns
|
|
196
208
|
}
|
|
197
209
|
}
|
|
@@ -241,7 +253,10 @@ function fetchDispatch(params, ctx) {
|
|
|
241
253
|
}
|
|
242
254
|
|
|
243
255
|
if (fetchHook) {
|
|
244
|
-
return runWithLoadingState(
|
|
256
|
+
return runWithLoadingState(
|
|
257
|
+
() => fetchFromHook({ fetchHook, cacheKeyFn, cacheTime, transform }, params),
|
|
258
|
+
state
|
|
259
|
+
)
|
|
245
260
|
}
|
|
246
261
|
|
|
247
262
|
return runWithLoadingState(() => fetchFromUrl({ url, cacheTime, transform }, params), state)
|
|
@@ -253,8 +268,17 @@ function fetchDispatch(params, ctx) {
|
|
|
253
268
|
* @returns {Object} Lookup provider with reactive state
|
|
254
269
|
*/
|
|
255
270
|
export function createLookup(config) {
|
|
256
|
-
const {
|
|
257
|
-
|
|
271
|
+
const {
|
|
272
|
+
url,
|
|
273
|
+
fetch: fetchHook,
|
|
274
|
+
source,
|
|
275
|
+
filter,
|
|
276
|
+
cacheKey: cacheKeyFn,
|
|
277
|
+
dependsOn = [],
|
|
278
|
+
fields = {},
|
|
279
|
+
cacheTime = DEFAULT_CACHE_TIME,
|
|
280
|
+
transform
|
|
281
|
+
} = config
|
|
258
282
|
|
|
259
283
|
let state = $state({ options: [], loading: false, error: null, disabled: false })
|
|
260
284
|
const ctx = { state, dependsOn, source, filter, fetchHook, cacheKeyFn, cacheTime, transform, url }
|
|
@@ -319,8 +343,12 @@ export function createLookupManager(lookupConfigs) {
|
|
|
319
343
|
handleFieldChange: (changedField, formData) =>
|
|
320
344
|
triggerDependentLookups(lookups, changedField, formData),
|
|
321
345
|
initialize: (formData) => initializeAllLookups(lookups, formData),
|
|
322
|
-
clearAllCaches: () => {
|
|
323
|
-
|
|
346
|
+
clearAllCaches: () => {
|
|
347
|
+
for (const lookup of lookups.values()) lookup.clearCache()
|
|
348
|
+
},
|
|
349
|
+
get lookups() {
|
|
350
|
+
return lookups
|
|
351
|
+
}
|
|
324
352
|
}
|
|
325
353
|
}
|
|
326
354
|
|