@json-render/svelte 0.6.1
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/LICENSE +201 -0
- package/dist/CatalogRenderer.svelte +55 -0
- package/dist/CatalogRenderer.svelte.d.ts +20 -0
- package/dist/CatalogRenderer.svelte.d.ts.map +1 -0
- package/dist/ConfirmDialog.svelte +98 -0
- package/dist/ConfirmDialog.svelte.d.ts +10 -0
- package/dist/ConfirmDialog.svelte.d.ts.map +1 -0
- package/dist/ConfirmDialogManager.svelte +15 -0
- package/dist/ConfirmDialogManager.svelte.d.ts +4 -0
- package/dist/ConfirmDialogManager.svelte.d.ts.map +1 -0
- package/dist/ElementRenderer.svelte +147 -0
- package/dist/ElementRenderer.svelte.d.ts +13 -0
- package/dist/ElementRenderer.svelte.d.ts.map +1 -0
- package/dist/JsonUIProvider.svelte +68 -0
- package/dist/JsonUIProvider.svelte.d.ts +37 -0
- package/dist/JsonUIProvider.svelte.d.ts.map +1 -0
- package/dist/Renderer.svelte +34 -0
- package/dist/Renderer.svelte.d.ts +19 -0
- package/dist/Renderer.svelte.d.ts.map +1 -0
- package/dist/RendererWithProvider.test.svelte +37 -0
- package/dist/RendererWithProvider.test.svelte.d.ts +14 -0
- package/dist/RendererWithProvider.test.svelte.d.ts.map +1 -0
- package/dist/RepeatChildren.svelte +50 -0
- package/dist/RepeatChildren.svelte.d.ts +13 -0
- package/dist/RepeatChildren.svelte.d.ts.map +1 -0
- package/dist/TestButton.svelte +11 -0
- package/dist/TestButton.svelte.d.ts +9 -0
- package/dist/TestButton.svelte.d.ts.map +1 -0
- package/dist/TestContainer.svelte +19 -0
- package/dist/TestContainer.svelte.d.ts +11 -0
- package/dist/TestContainer.svelte.d.ts.map +1 -0
- package/dist/TestText.svelte +9 -0
- package/dist/TestText.svelte.d.ts +9 -0
- package/dist/TestText.svelte.d.ts.map +1 -0
- package/dist/catalog-types.d.ts +63 -0
- package/dist/catalog-types.d.ts.map +1 -0
- package/dist/catalog-types.js +1 -0
- package/dist/contexts/ActionProvider.svelte +344 -0
- package/dist/contexts/ActionProvider.svelte.d.ts +62 -0
- package/dist/contexts/ActionProvider.svelte.d.ts.map +1 -0
- package/dist/contexts/FunctionsContextProvider.svelte +44 -0
- package/dist/contexts/FunctionsContextProvider.svelte.d.ts +21 -0
- package/dist/contexts/FunctionsContextProvider.svelte.d.ts.map +1 -0
- package/dist/contexts/RepeatScopeProvider.svelte +49 -0
- package/dist/contexts/RepeatScopeProvider.svelte.d.ts +26 -0
- package/dist/contexts/RepeatScopeProvider.svelte.d.ts.map +1 -0
- package/dist/contexts/StateProvider.svelte +182 -0
- package/dist/contexts/StateProvider.svelte.d.ts +47 -0
- package/dist/contexts/StateProvider.svelte.d.ts.map +1 -0
- package/dist/contexts/ValidationProvider.svelte +198 -0
- package/dist/contexts/ValidationProvider.svelte.d.ts +57 -0
- package/dist/contexts/ValidationProvider.svelte.d.ts.map +1 -0
- package/dist/contexts/VisibilityProvider.svelte +82 -0
- package/dist/contexts/VisibilityProvider.svelte.d.ts +29 -0
- package/dist/contexts/VisibilityProvider.svelte.d.ts.map +1 -0
- package/dist/contexts/actions.test.js +156 -0
- package/dist/contexts/state.test.js +118 -0
- package/dist/contexts/visibility.test.js +71 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/renderer.d.ts +128 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +124 -0
- package/dist/renderer.test.js +168 -0
- package/dist/schema.d.ts +57 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +81 -0
- package/dist/streaming.svelte.d.ts +75 -0
- package/dist/streaming.svelte.d.ts.map +1 -0
- package/dist/streaming.svelte.js +404 -0
- package/dist/utils.svelte.d.ts +26 -0
- package/dist/utils.svelte.d.ts.map +1 -0
- package/dist/utils.svelte.js +92 -0
- package/dist/utils.test.js +307 -0
- package/package.json +70 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { getContext } from "svelte";
|
|
3
|
+
import type { StateModel } from "@json-render/core";
|
|
4
|
+
|
|
5
|
+
const STATE_KEY = Symbol.for("json-render-state");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* State context value
|
|
9
|
+
*/
|
|
10
|
+
export interface StateContext {
|
|
11
|
+
/** The current state model (reactive) */
|
|
12
|
+
readonly state: StateModel;
|
|
13
|
+
/** Get a value by path */
|
|
14
|
+
get: (path: string) => unknown;
|
|
15
|
+
/** Set a value by path */
|
|
16
|
+
set: (path: string, value: unknown) => void;
|
|
17
|
+
/** Update multiple values at once */
|
|
18
|
+
update: (updates: Record<string, unknown>) => void;
|
|
19
|
+
/** Return the live state snapshot from the underlying store. */
|
|
20
|
+
getSnapshot: () => StateModel;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CurrentValue<T> {
|
|
24
|
+
current: T;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get the state context from component tree
|
|
29
|
+
*/
|
|
30
|
+
export function getStateContext(): StateContext {
|
|
31
|
+
const ctx = getContext<StateContext>(STATE_KEY);
|
|
32
|
+
if (!ctx) {
|
|
33
|
+
throw new Error("getStateContext must be called within a StateProvider");
|
|
34
|
+
}
|
|
35
|
+
return ctx;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convenience helper to read a value from the state context
|
|
40
|
+
*/
|
|
41
|
+
export function getStateValue(path: string): CurrentValue<unknown> {
|
|
42
|
+
const context = getStateContext();
|
|
43
|
+
return {
|
|
44
|
+
get current() {
|
|
45
|
+
return context.get(path);
|
|
46
|
+
},
|
|
47
|
+
set current(value: unknown) {
|
|
48
|
+
context.set(path, value);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Two-way helper for `$bindState` / `$bindItem` bindings.
|
|
55
|
+
* Mirrors `useBoundProp` from React packages.
|
|
56
|
+
*/
|
|
57
|
+
export function getBoundProp<T>(
|
|
58
|
+
propValue: () => T | undefined,
|
|
59
|
+
bindingPath: () => string | undefined,
|
|
60
|
+
): CurrentValue<T | undefined> {
|
|
61
|
+
const context = getStateContext();
|
|
62
|
+
return {
|
|
63
|
+
get current() {
|
|
64
|
+
return propValue();
|
|
65
|
+
},
|
|
66
|
+
set current(value: T | undefined) {
|
|
67
|
+
const path = bindingPath();
|
|
68
|
+
if (path) {
|
|
69
|
+
context.set(path, value);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<script lang="ts">
|
|
77
|
+
import { setContext, type Snippet } from "svelte";
|
|
78
|
+
import {
|
|
79
|
+
createStateStore,
|
|
80
|
+
getByPath,
|
|
81
|
+
type StateModel as CoreStateModel,
|
|
82
|
+
type StateStore as CoreStateStore,
|
|
83
|
+
} from "@json-render/core";
|
|
84
|
+
import { flattenToPointers } from "@json-render/core/store-utils";
|
|
85
|
+
|
|
86
|
+
interface Props {
|
|
87
|
+
store?: CoreStateStore;
|
|
88
|
+
initialState?: CoreStateModel;
|
|
89
|
+
onStateChange?: (changes: Array<{ path: string; value: unknown }>) => void;
|
|
90
|
+
children?: Snippet;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let { store, initialState = {}, onStateChange, children }: Props = $props();
|
|
94
|
+
|
|
95
|
+
// svelte-ignore state_referenced_locally
|
|
96
|
+
const internalStore = createStateStore(initialState);
|
|
97
|
+
|
|
98
|
+
function activeStore(): CoreStateStore {
|
|
99
|
+
return store ?? internalStore;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Keep a reactive copy of the current store snapshot.
|
|
103
|
+
let model: CoreStateModel = $state.raw(activeStore().getSnapshot());
|
|
104
|
+
|
|
105
|
+
$effect(() => {
|
|
106
|
+
const currentStore = activeStore();
|
|
107
|
+
model = currentStore.getSnapshot();
|
|
108
|
+
const unsubscribe = currentStore.subscribe(() => {
|
|
109
|
+
model = currentStore.getSnapshot();
|
|
110
|
+
});
|
|
111
|
+
return unsubscribe;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// In uncontrolled mode, support reactive initialState updates.
|
|
115
|
+
// svelte-ignore state_referenced_locally
|
|
116
|
+
let prevFlat: Record<string, unknown> =
|
|
117
|
+
initialState && Object.keys(initialState).length > 0
|
|
118
|
+
? flattenToPointers(initialState)
|
|
119
|
+
: {};
|
|
120
|
+
$effect(() => {
|
|
121
|
+
if (store) return;
|
|
122
|
+
const nextInitialState = initialState ?? {};
|
|
123
|
+
const nextFlat =
|
|
124
|
+
Object.keys(nextInitialState).length > 0
|
|
125
|
+
? flattenToPointers(nextInitialState)
|
|
126
|
+
: {};
|
|
127
|
+
const allKeys = new Set([
|
|
128
|
+
...Object.keys(prevFlat),
|
|
129
|
+
...Object.keys(nextFlat),
|
|
130
|
+
]);
|
|
131
|
+
const updates: Record<string, unknown> = {};
|
|
132
|
+
for (const key of allKeys) {
|
|
133
|
+
if (prevFlat[key] !== nextFlat[key]) {
|
|
134
|
+
updates[key] = key in nextFlat ? nextFlat[key] : undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
prevFlat = nextFlat;
|
|
138
|
+
if (Object.keys(updates).length > 0) {
|
|
139
|
+
internalStore.update(updates);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const ctx: StateContext = {
|
|
144
|
+
get state() {
|
|
145
|
+
return model;
|
|
146
|
+
},
|
|
147
|
+
get: (path: string) => activeStore().get(path),
|
|
148
|
+
getSnapshot: () => activeStore().getSnapshot(),
|
|
149
|
+
set: (path: string, value: unknown) => {
|
|
150
|
+
const currentStore = activeStore();
|
|
151
|
+
const prev = currentStore.getSnapshot();
|
|
152
|
+
currentStore.set(path, value);
|
|
153
|
+
const next = currentStore.getSnapshot();
|
|
154
|
+
model = next;
|
|
155
|
+
if (!store && next !== prev) {
|
|
156
|
+
onStateChange?.([{ path, value }]);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
update: (updates: Record<string, unknown>) => {
|
|
160
|
+
const currentStore = activeStore();
|
|
161
|
+
const prev = currentStore.getSnapshot();
|
|
162
|
+
currentStore.update(updates);
|
|
163
|
+
const next = currentStore.getSnapshot();
|
|
164
|
+
model = next;
|
|
165
|
+
if (!store && next !== prev) {
|
|
166
|
+
const changes: Array<{ path: string; value: unknown }> = [];
|
|
167
|
+
for (const [path, value] of Object.entries(updates)) {
|
|
168
|
+
if (getByPath(prev, path) !== value) {
|
|
169
|
+
changes.push({ path, value });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (changes.length > 0) {
|
|
173
|
+
onStateChange?.(changes);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
setContext(STATE_KEY, ctx);
|
|
180
|
+
</script>
|
|
181
|
+
|
|
182
|
+
{@render children?.()}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { StateModel } from "@json-render/core";
|
|
2
|
+
/**
|
|
3
|
+
* State context value
|
|
4
|
+
*/
|
|
5
|
+
export interface StateContext {
|
|
6
|
+
/** The current state model (reactive) */
|
|
7
|
+
readonly state: StateModel;
|
|
8
|
+
/** Get a value by path */
|
|
9
|
+
get: (path: string) => unknown;
|
|
10
|
+
/** Set a value by path */
|
|
11
|
+
set: (path: string, value: unknown) => void;
|
|
12
|
+
/** Update multiple values at once */
|
|
13
|
+
update: (updates: Record<string, unknown>) => void;
|
|
14
|
+
/** Return the live state snapshot from the underlying store. */
|
|
15
|
+
getSnapshot: () => StateModel;
|
|
16
|
+
}
|
|
17
|
+
export interface CurrentValue<T> {
|
|
18
|
+
current: T;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the state context from component tree
|
|
22
|
+
*/
|
|
23
|
+
export declare function getStateContext(): StateContext;
|
|
24
|
+
/**
|
|
25
|
+
* Convenience helper to read a value from the state context
|
|
26
|
+
*/
|
|
27
|
+
export declare function getStateValue(path: string): CurrentValue<unknown>;
|
|
28
|
+
/**
|
|
29
|
+
* Two-way helper for `$bindState` / `$bindItem` bindings.
|
|
30
|
+
* Mirrors `useBoundProp` from React packages.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getBoundProp<T>(propValue: () => T | undefined, bindingPath: () => string | undefined): CurrentValue<T | undefined>;
|
|
33
|
+
import { type Snippet } from "svelte";
|
|
34
|
+
import { type StateModel as CoreStateModel, type StateStore as CoreStateStore } from "@json-render/core";
|
|
35
|
+
interface Props {
|
|
36
|
+
store?: CoreStateStore;
|
|
37
|
+
initialState?: CoreStateModel;
|
|
38
|
+
onStateChange?: (changes: Array<{
|
|
39
|
+
path: string;
|
|
40
|
+
value: unknown;
|
|
41
|
+
}>) => void;
|
|
42
|
+
children?: Snippet;
|
|
43
|
+
}
|
|
44
|
+
declare const StateProvider: import("svelte").Component<Props, {}, "">;
|
|
45
|
+
type StateProvider = ReturnType<typeof StateProvider>;
|
|
46
|
+
export default StateProvider;
|
|
47
|
+
//# sourceMappingURL=StateProvider.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StateProvider.svelte.d.ts","sourceRoot":"","sources":["../../src/contexts/StateProvider.svelte.ts"],"names":[],"mappings":"AAIE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,0BAA0B;IAC1B,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/B,0BAA0B;IAC1B,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,qCAAqC;IACrC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACnD,gEAAgE;IAChE,WAAW,EAAE,MAAM,UAAU,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,OAAO,EAAE,CAAC,CAAC;CACZ;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAM9C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAUjE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,SAAS,EAAE,MAAM,CAAC,GAAG,SAAS,EAC9B,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS,GACpC,YAAY,CAAC,CAAC,GAAG,SAAS,CAAC,CAa7B;AAGH,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAGH,KAAK,UAAU,IAAI,cAAc,EACjC,KAAK,UAAU,IAAI,cAAc,EAClC,MAAM,mBAAmB,CAAC;AAI3B,UAAU,KAAK;IACb,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IAC3E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAsGH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { getContext, hasContext } from "svelte";
|
|
3
|
+
import type {
|
|
4
|
+
ValidationConfig,
|
|
5
|
+
ValidationFunction,
|
|
6
|
+
ValidationResult,
|
|
7
|
+
} from "@json-render/core";
|
|
8
|
+
|
|
9
|
+
const VALIDATION_KEY = Symbol.for("json-render-validation");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Field validation state
|
|
13
|
+
*/
|
|
14
|
+
export interface FieldValidationState {
|
|
15
|
+
touched: boolean;
|
|
16
|
+
validated: boolean;
|
|
17
|
+
result: ValidationResult | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validation context value
|
|
22
|
+
*/
|
|
23
|
+
export interface ValidationContext {
|
|
24
|
+
/** Custom validation functions from catalog */
|
|
25
|
+
customFunctions: Record<string, ValidationFunction>;
|
|
26
|
+
/** Validation state by field path */
|
|
27
|
+
fieldStates: Record<string, FieldValidationState>;
|
|
28
|
+
/** Validate a field */
|
|
29
|
+
validate: (path: string, config: ValidationConfig) => ValidationResult;
|
|
30
|
+
/** Mark field as touched */
|
|
31
|
+
touch: (path: string) => void;
|
|
32
|
+
/** Clear validation for a field */
|
|
33
|
+
clear: (path: string) => void;
|
|
34
|
+
/** Validate all fields */
|
|
35
|
+
validateAll: () => boolean;
|
|
36
|
+
/** Register field config */
|
|
37
|
+
registerField: (path: string, config: ValidationConfig) => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the validation context from component tree
|
|
42
|
+
*/
|
|
43
|
+
export function getValidationContext(): ValidationContext {
|
|
44
|
+
const ctx = getContext<ValidationContext>(VALIDATION_KEY);
|
|
45
|
+
if (!ctx) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"getValidationContext must be called within a ValidationProvider",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return ctx;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get validation context if present.
|
|
55
|
+
*/
|
|
56
|
+
export function getOptionalValidationContext(): ValidationContext | null {
|
|
57
|
+
return hasContext(VALIDATION_KEY)
|
|
58
|
+
? getContext<ValidationContext>(VALIDATION_KEY)
|
|
59
|
+
: null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Helper to get field validation state
|
|
64
|
+
*/
|
|
65
|
+
export function getFieldValidation(
|
|
66
|
+
ctx: ValidationContext,
|
|
67
|
+
path: string,
|
|
68
|
+
config?: ValidationConfig,
|
|
69
|
+
): {
|
|
70
|
+
state: FieldValidationState;
|
|
71
|
+
validate: () => ValidationResult;
|
|
72
|
+
touch: () => void;
|
|
73
|
+
clear: () => void;
|
|
74
|
+
errors: string[];
|
|
75
|
+
isValid: boolean;
|
|
76
|
+
} {
|
|
77
|
+
const state = ctx.fieldStates[path] ?? {
|
|
78
|
+
touched: false,
|
|
79
|
+
validated: false,
|
|
80
|
+
result: null,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
state,
|
|
85
|
+
validate: () => ctx.validate(path, config ?? { checks: [] }),
|
|
86
|
+
touch: () => ctx.touch(path),
|
|
87
|
+
clear: () => ctx.clear(path),
|
|
88
|
+
errors: state.result?.errors ?? [],
|
|
89
|
+
isValid: state.result?.valid ?? true,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<script lang="ts">
|
|
95
|
+
import { setContext, type Snippet } from "svelte";
|
|
96
|
+
import {
|
|
97
|
+
runValidation,
|
|
98
|
+
type ValidationConfig as CoreValidationConfig,
|
|
99
|
+
type ValidationFunction as CoreValidationFunction,
|
|
100
|
+
type ValidationResult as CoreValidationResult,
|
|
101
|
+
} from "@json-render/core";
|
|
102
|
+
import { getStateContext } from "./StateProvider.svelte";
|
|
103
|
+
|
|
104
|
+
interface Props {
|
|
105
|
+
customFunctions?: Record<string, CoreValidationFunction>;
|
|
106
|
+
children?: Snippet;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let { customFunctions = {}, children }: Props = $props();
|
|
110
|
+
|
|
111
|
+
const stateCtx = getStateContext();
|
|
112
|
+
|
|
113
|
+
let fieldStates = $state.raw<Record<string, FieldValidationState>>({});
|
|
114
|
+
let fieldConfigs = $state.raw<Record<string, CoreValidationConfig>>({});
|
|
115
|
+
|
|
116
|
+
const validate = (
|
|
117
|
+
path: string,
|
|
118
|
+
config: CoreValidationConfig,
|
|
119
|
+
): CoreValidationResult => {
|
|
120
|
+
const segments = path.split("/").filter(Boolean);
|
|
121
|
+
let value: unknown = stateCtx.state;
|
|
122
|
+
for (const seg of segments) {
|
|
123
|
+
if (value != null && typeof value === "object") {
|
|
124
|
+
value = (value as Record<string, unknown>)[seg];
|
|
125
|
+
} else {
|
|
126
|
+
value = undefined;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const result = runValidation(config, {
|
|
132
|
+
value,
|
|
133
|
+
stateModel: stateCtx.state,
|
|
134
|
+
customFunctions,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
fieldStates = {
|
|
138
|
+
...fieldStates,
|
|
139
|
+
[path]: {
|
|
140
|
+
touched: fieldStates[path]?.touched ?? true,
|
|
141
|
+
validated: true,
|
|
142
|
+
result,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return result;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const touch = (path: string): void => {
|
|
150
|
+
fieldStates = {
|
|
151
|
+
...fieldStates,
|
|
152
|
+
[path]: {
|
|
153
|
+
...fieldStates[path],
|
|
154
|
+
touched: true,
|
|
155
|
+
validated: fieldStates[path]?.validated ?? false,
|
|
156
|
+
result: fieldStates[path]?.result ?? null,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const clear = (path: string): void => {
|
|
162
|
+
const { [path]: _, ...rest } = fieldStates;
|
|
163
|
+
fieldStates = rest;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const validateAll = (): boolean => {
|
|
167
|
+
let allValid = true;
|
|
168
|
+
for (const [path, config] of Object.entries(fieldConfigs)) {
|
|
169
|
+
const result = validate(path, config);
|
|
170
|
+
if (!result.valid) {
|
|
171
|
+
allValid = false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return allValid;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const registerField = (path: string, config: CoreValidationConfig): void => {
|
|
178
|
+
fieldConfigs = { ...fieldConfigs, [path]: config };
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const ctx: ValidationContext = {
|
|
182
|
+
get customFunctions() {
|
|
183
|
+
return customFunctions;
|
|
184
|
+
},
|
|
185
|
+
get fieldStates() {
|
|
186
|
+
return fieldStates;
|
|
187
|
+
},
|
|
188
|
+
validate,
|
|
189
|
+
touch,
|
|
190
|
+
clear,
|
|
191
|
+
validateAll,
|
|
192
|
+
registerField,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
setContext(VALIDATION_KEY, ctx);
|
|
196
|
+
</script>
|
|
197
|
+
|
|
198
|
+
{@render children?.()}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ValidationConfig, ValidationFunction, ValidationResult } from "@json-render/core";
|
|
2
|
+
/**
|
|
3
|
+
* Field validation state
|
|
4
|
+
*/
|
|
5
|
+
export interface FieldValidationState {
|
|
6
|
+
touched: boolean;
|
|
7
|
+
validated: boolean;
|
|
8
|
+
result: ValidationResult | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validation context value
|
|
12
|
+
*/
|
|
13
|
+
export interface ValidationContext {
|
|
14
|
+
/** Custom validation functions from catalog */
|
|
15
|
+
customFunctions: Record<string, ValidationFunction>;
|
|
16
|
+
/** Validation state by field path */
|
|
17
|
+
fieldStates: Record<string, FieldValidationState>;
|
|
18
|
+
/** Validate a field */
|
|
19
|
+
validate: (path: string, config: ValidationConfig) => ValidationResult;
|
|
20
|
+
/** Mark field as touched */
|
|
21
|
+
touch: (path: string) => void;
|
|
22
|
+
/** Clear validation for a field */
|
|
23
|
+
clear: (path: string) => void;
|
|
24
|
+
/** Validate all fields */
|
|
25
|
+
validateAll: () => boolean;
|
|
26
|
+
/** Register field config */
|
|
27
|
+
registerField: (path: string, config: ValidationConfig) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get the validation context from component tree
|
|
31
|
+
*/
|
|
32
|
+
export declare function getValidationContext(): ValidationContext;
|
|
33
|
+
/**
|
|
34
|
+
* Get validation context if present.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getOptionalValidationContext(): ValidationContext | null;
|
|
37
|
+
/**
|
|
38
|
+
* Helper to get field validation state
|
|
39
|
+
*/
|
|
40
|
+
export declare function getFieldValidation(ctx: ValidationContext, path: string, config?: ValidationConfig): {
|
|
41
|
+
state: FieldValidationState;
|
|
42
|
+
validate: () => ValidationResult;
|
|
43
|
+
touch: () => void;
|
|
44
|
+
clear: () => void;
|
|
45
|
+
errors: string[];
|
|
46
|
+
isValid: boolean;
|
|
47
|
+
};
|
|
48
|
+
import { type Snippet } from "svelte";
|
|
49
|
+
import { type ValidationFunction as CoreValidationFunction } from "@json-render/core";
|
|
50
|
+
interface Props {
|
|
51
|
+
customFunctions?: Record<string, CoreValidationFunction>;
|
|
52
|
+
children?: Snippet;
|
|
53
|
+
}
|
|
54
|
+
declare const ValidationProvider: import("svelte").Component<Props, {}, "">;
|
|
55
|
+
type ValidationProvider = ReturnType<typeof ValidationProvider>;
|
|
56
|
+
export default ValidationProvider;
|
|
57
|
+
//# sourceMappingURL=ValidationProvider.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ValidationProvider.svelte.d.ts","sourceRoot":"","sources":["../../src/contexts/ValidationProvider.svelte.ts"],"names":[],"mappings":"AAIE,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAI3B;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACpD,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAClD,uBAAuB;IACvB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;IACvE,4BAA4B;IAC5B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,mCAAmC;IACnC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,0BAA0B;IAC1B,WAAW,EAAE,MAAM,OAAO,CAAC;IAC3B,4BAA4B;IAC5B,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACjE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CAQxD;AAED;;GAEG;AACH,wBAAgB,4BAA4B,IAAI,iBAAiB,GAAG,IAAI,CAIvE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,iBAAiB,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,gBAAgB,GACxB;IACD,KAAK,EAAE,oBAAoB,CAAC;IAC5B,QAAQ,EAAE,MAAM,gBAAgB,CAAC;IACjC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB,CAeA;AAGH,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAGH,KAAK,kBAAkB,IAAI,sBAAsB,EAElD,MAAM,mBAAmB,CAAC;AAI3B,UAAU,KAAK;IACb,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAsGH,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { getContext } from "svelte";
|
|
3
|
+
import {
|
|
4
|
+
type VisibilityCondition,
|
|
5
|
+
type VisibilityContext as CoreVisibilityContext,
|
|
6
|
+
} from "@json-render/core";
|
|
7
|
+
|
|
8
|
+
const VISIBILITY_KEY = Symbol.for("json-render-visibility");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Visibility context value
|
|
12
|
+
*/
|
|
13
|
+
export interface VisibilityContext {
|
|
14
|
+
/** Evaluate a visibility condition */
|
|
15
|
+
isVisible: (condition: VisibilityCondition | undefined) => boolean;
|
|
16
|
+
/** The underlying visibility context (for advanced use) */
|
|
17
|
+
ctx: CoreVisibilityContext;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CurrentValue<T> {
|
|
21
|
+
readonly current: T;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the visibility context from component tree
|
|
26
|
+
*/
|
|
27
|
+
export function getVisibilityContext(): VisibilityContext {
|
|
28
|
+
const ctx = getContext<VisibilityContext>(VISIBILITY_KEY);
|
|
29
|
+
if (!ctx) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"getVisibilityContext must be called within a VisibilityProvider",
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return ctx;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Convenience helper to evaluate visibility from context
|
|
39
|
+
*/
|
|
40
|
+
export function isVisible(
|
|
41
|
+
condition: VisibilityCondition | undefined,
|
|
42
|
+
): CurrentValue<boolean> {
|
|
43
|
+
const context = getVisibilityContext();
|
|
44
|
+
return {
|
|
45
|
+
get current() {
|
|
46
|
+
return context.isVisible(condition);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<script lang="ts">
|
|
53
|
+
import { setContext as setContextInstance, type Snippet } from "svelte";
|
|
54
|
+
import {
|
|
55
|
+
evaluateVisibility as evaluateVisibilityInstance,
|
|
56
|
+
type VisibilityCondition as VisibilityConditionInstance,
|
|
57
|
+
type VisibilityContext as CoreVisibilityContextInstance,
|
|
58
|
+
} from "@json-render/core";
|
|
59
|
+
import { getStateContext } from "./StateProvider.svelte";
|
|
60
|
+
|
|
61
|
+
interface Props {
|
|
62
|
+
children?: Snippet;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let { children }: Props = $props();
|
|
66
|
+
const stateCtx = getStateContext();
|
|
67
|
+
|
|
68
|
+
const ctx = {
|
|
69
|
+
get ctx(): CoreVisibilityContextInstance {
|
|
70
|
+
return { stateModel: stateCtx.state };
|
|
71
|
+
},
|
|
72
|
+
isVisible: (condition: VisibilityConditionInstance | undefined) => {
|
|
73
|
+
return evaluateVisibilityInstance(condition, {
|
|
74
|
+
stateModel: stateCtx.state,
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
setContextInstance(VISIBILITY_KEY, ctx);
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
{@render children?.()}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type VisibilityCondition, type VisibilityContext as CoreVisibilityContext } from "@json-render/core";
|
|
2
|
+
/**
|
|
3
|
+
* Visibility context value
|
|
4
|
+
*/
|
|
5
|
+
export interface VisibilityContext {
|
|
6
|
+
/** Evaluate a visibility condition */
|
|
7
|
+
isVisible: (condition: VisibilityCondition | undefined) => boolean;
|
|
8
|
+
/** The underlying visibility context (for advanced use) */
|
|
9
|
+
ctx: CoreVisibilityContext;
|
|
10
|
+
}
|
|
11
|
+
export interface CurrentValue<T> {
|
|
12
|
+
readonly current: T;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the visibility context from component tree
|
|
16
|
+
*/
|
|
17
|
+
export declare function getVisibilityContext(): VisibilityContext;
|
|
18
|
+
/**
|
|
19
|
+
* Convenience helper to evaluate visibility from context
|
|
20
|
+
*/
|
|
21
|
+
export declare function isVisible(condition: VisibilityCondition | undefined): CurrentValue<boolean>;
|
|
22
|
+
import { type Snippet } from "svelte";
|
|
23
|
+
interface Props {
|
|
24
|
+
children?: Snippet;
|
|
25
|
+
}
|
|
26
|
+
declare const VisibilityProvider: import("svelte").Component<Props, {}, "">;
|
|
27
|
+
type VisibilityProvider = ReturnType<typeof VisibilityProvider>;
|
|
28
|
+
export default VisibilityProvider;
|
|
29
|
+
//# sourceMappingURL=VisibilityProvider.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VisibilityProvider.svelte.d.ts","sourceRoot":"","sources":["../../src/contexts/VisibilityProvider.svelte.ts"],"names":[],"mappings":"AAIE,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,IAAI,qBAAqB,EAChD,MAAM,mBAAmB,CAAC;AAI3B;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,SAAS,EAAE,CAAC,SAAS,EAAE,mBAAmB,GAAG,SAAS,KAAK,OAAO,CAAC;IACnE,2DAA2D;IAC3D,GAAG,EAAE,qBAAqB,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CAQxD;AAED;;GAEG;AACH,wBAAgB,SAAS,CACvB,SAAS,EAAE,mBAAmB,GAAG,SAAS,GACzC,YAAY,CAAC,OAAO,CAAC,CAOvB;AAGH,OAAO,EAAoC,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAStE,UAAU,KAAK;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA8BH,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|