@tai42/studio-sdk 17.1.2 → 17.1.5
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/schema-form/array-field.d.ts.map +1 -1
- package/dist/schema-form/array-field.js +3 -3
- package/dist/schema-form/array-field.js.map +1 -1
- package/dist/schema-form/field-node.d.ts +10 -3
- package/dist/schema-form/field-node.d.ts.map +1 -1
- package/dist/schema-form/field-node.js +16 -2
- package/dist/schema-form/field-node.js.map +1 -1
- package/dist/schema-form/keyed-callbacks.d.ts +2 -0
- package/dist/schema-form/keyed-callbacks.d.ts.map +1 -0
- package/dist/schema-form/keyed-callbacks.js +45 -0
- package/dist/schema-form/keyed-callbacks.js.map +1 -0
- package/dist/schema-form/object-fields.d.ts.map +1 -1
- package/dist/schema-form/object-fields.js +5 -3
- package/dist/schema-form/object-fields.js.map +1 -1
- package/dist/schema-form/record-field.d.ts.map +1 -1
- package/dist/schema-form/record-field.js +4 -6
- package/dist/schema-form/record-field.js.map +1 -1
- package/package.json +2 -2
- package/src/schema-form/array-field.tsx +3 -3
- package/src/schema-form/field-node.tsx +11 -3
- package/src/schema-form/keyed-callbacks.ts +46 -0
- package/src/schema-form/object-fields.tsx +8 -3
- package/src/schema-form/record-field.tsx +7 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array-field.d.ts","sourceRoot":"","sources":["../../src/schema-form/array-field.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"array-field.d.ts","sourceRoot":"","sources":["../../src/schema-form/array-field.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AASvC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE5D,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,WAAW,EACX,KAAK,EACL,KAAK,EACL,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,SAAS,CAyDZ"}
|
|
@@ -5,6 +5,7 @@ import { classifySchema } from './classify';
|
|
|
5
5
|
import { defaultValueForSchema } from './default-value';
|
|
6
6
|
import { FieldGroup } from './field-group';
|
|
7
7
|
import { FieldNode } from './field-node';
|
|
8
|
+
import { useKeyedCallbacks } from './keyed-callbacks';
|
|
8
9
|
export function ArrayField({ heading, description, error, items, root, value, onChange, path, errors, idPrefix, }) {
|
|
9
10
|
const list = Array.isArray(value) ? value : [];
|
|
10
11
|
const addItem = () => {
|
|
@@ -16,9 +17,8 @@ export function ArrayField({ heading, description, error, items, root, value, on
|
|
|
16
17
|
const setItem = (index, next) => {
|
|
17
18
|
onChange(list.map((item, position) => (position === index ? next : item)));
|
|
18
19
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}, path: `${path}[${String(index)}]`, label: `Item ${String(index + 1)}`, required: true, errors: errors, idPrefix: idPrefix }) }), _jsxs(Button, { type: "button", variant: "secondary", "aria-label": `Remove item ${String(index + 1)}`, onClick: () => {
|
|
20
|
+
const changeFor = useKeyedCallbacks(setItem, list.keys());
|
|
21
|
+
return (_jsxs(FieldGroup, { heading: heading, description: description, error: error, atRoot: false, children: [list.length === 0 ? _jsx("span", { className: "tai-field-hint", children: "No items" }) : null, list.map((item, index) => (_jsxs("div", { className: "tai-row", children: [_jsx("div", { style: { flex: 1 }, children: _jsx(FieldNode, { schema: items, root: root, value: item, onChange: changeFor(index), path: `${path}[${String(index)}]`, label: `Item ${String(index + 1)}`, required: true, errors: errors, idPrefix: idPrefix }) }), _jsxs(Button, { type: "button", variant: "secondary", "aria-label": `Remove item ${String(index + 1)}`, onClick: () => {
|
|
22
22
|
removeItem(index);
|
|
23
23
|
}, children: [_jsx(CloseIcon, {}), "Remove"] })] }, index))), _jsx("div", { children: _jsx(Button, { type: "button", variant: "secondary", onClick: addItem, children: "Add item" }) })] }));
|
|
24
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array-field.js","sourceRoot":"","sources":["../../src/schema-form/array-field.tsx"],"names":[],"mappings":";AAOA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"array-field.js","sourceRoot":"","sources":["../../src/schema-form/array-field.tsx"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* inline; every other kind delegates to a dedicated field component). Object,
|
|
5
5
|
* array, and union renderers recurse back through here for their children.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type ReactNode } from 'react';
|
|
8
8
|
import type { JsonSchema, SchemaFormErrors } from './types';
|
|
9
9
|
interface FieldNodeProps {
|
|
10
10
|
readonly schema: JsonSchema;
|
|
@@ -25,7 +25,14 @@ interface FieldNodeProps {
|
|
|
25
25
|
readonly errors: SchemaFormErrors | undefined;
|
|
26
26
|
readonly idPrefix: string;
|
|
27
27
|
}
|
|
28
|
-
/**
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* A single schema node, dispatched on its classified model kind. Wrapped in
|
|
30
|
+
* `memo` so a field whose props are unchanged is skipped: the containers hand
|
|
31
|
+
* each child a referentially-stable `onChange` (see {@link useKeyedCallbacks})
|
|
32
|
+
* and the controlled value keeps unedited branches identity-equal, so editing
|
|
33
|
+
* one field re-renders only that field's subtree, not every sibling.
|
|
34
|
+
*/
|
|
35
|
+
declare function FieldNodeImpl(props: FieldNodeProps): ReactNode;
|
|
36
|
+
export declare const FieldNode: import("react").MemoExoticComponent<typeof FieldNodeImpl>;
|
|
30
37
|
export {};
|
|
31
38
|
//# sourceMappingURL=field-node.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-node.d.ts","sourceRoot":"","sources":["../../src/schema-form/field-node.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"field-node.d.ts","sourceRoot":"","sources":["../../src/schema-form/field-node.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAc7C,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG5D,UAAU,cAAc;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAMD;;;;;;GAMG;AACH,iBAAS,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CA2IvD;AAED,eAAO,MAAM,SAAS,2DAAsB,CAAC"}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The recursive dispatcher: classifies a single schema node and renders the
|
|
4
|
+
* matching field component for its model kind (const/number/boolean render
|
|
5
|
+
* inline; every other kind delegates to a dedicated field component). Object,
|
|
6
|
+
* array, and union renderers recurse back through here for their children.
|
|
7
|
+
*/
|
|
8
|
+
import { memo } from 'react';
|
|
2
9
|
import { Checkbox } from '../components/checkbox';
|
|
3
10
|
import { Field } from '../components/field';
|
|
4
11
|
import { NumberInput, TextInput } from '../components/inputs';
|
|
@@ -15,8 +22,14 @@ import { UnionField } from './union-field';
|
|
|
15
22
|
function labelText(explicit, label) {
|
|
16
23
|
return explicit ?? label ?? 'Value';
|
|
17
24
|
}
|
|
18
|
-
/**
|
|
19
|
-
|
|
25
|
+
/**
|
|
26
|
+
* A single schema node, dispatched on its classified model kind. Wrapped in
|
|
27
|
+
* `memo` so a field whose props are unchanged is skipped: the containers hand
|
|
28
|
+
* each child a referentially-stable `onChange` (see {@link useKeyedCallbacks})
|
|
29
|
+
* and the controlled value keeps unedited branches identity-equal, so editing
|
|
30
|
+
* one field re-renders only that field's subtree, not every sibling.
|
|
31
|
+
*/
|
|
32
|
+
function FieldNodeImpl(props) {
|
|
20
33
|
const { root, value, onChange, path, required, idPrefix, errors } = props;
|
|
21
34
|
const classified = classifySchema(props.schema, root);
|
|
22
35
|
const base = labelText(classified.title, props.label);
|
|
@@ -46,6 +59,7 @@ export function FieldNode(props) {
|
|
|
46
59
|
return (_jsx(UnionField, { heading: heading, description: description, error: error, model: model, root: root, value: value, onChange: onChange, path: path, errors: errors, idPrefix: idPrefix }));
|
|
47
60
|
}
|
|
48
61
|
}
|
|
62
|
+
export const FieldNode = memo(FieldNodeImpl);
|
|
49
63
|
/** A `const`-pinned value: shown read-only, never editable. */
|
|
50
64
|
function ConstField({ heading, description, error, constValue, }) {
|
|
51
65
|
return (_jsx(Field, { label: heading, description: description, error: error, children: _jsx(TextInput, { value: scalarLabel(constValue), readOnly: true, disabled: true }) }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-node.js","sourceRoot":"","sources":["../../src/schema-form/field-node.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"field-node.js","sourceRoot":"","sources":["../../src/schema-form/field-node.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyed-callbacks.d.ts","sourceRoot":"","sources":["../../src/schema-form/keyed-callbacks.ts"],"names":[],"mappings":"AAoBA,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EACpC,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,EAC9B,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GACpB,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAsB9B"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stable per-key callback factory. A container (object, array, record) renders
|
|
3
|
+
* one child per key and folds that child's edits back into the container value.
|
|
4
|
+
* Written inline, each child's `onChange` is a fresh closure every render, which
|
|
5
|
+
* defeats {@link FieldNode}'s `memo`: an untouched sibling re-renders whenever any
|
|
6
|
+
* field changes. This returns the SAME function identity for a given key across
|
|
7
|
+
* renders — so a memoized child keyed on that identity is skipped when a sibling
|
|
8
|
+
* changes — while each call reads the latest folding logic through a ref, so the
|
|
9
|
+
* callback never closes over a stale container value.
|
|
10
|
+
*
|
|
11
|
+
* The cache is bounded by `liveKeys`, the container's current key set: every
|
|
12
|
+
* render evicts each cached handler whose key is not live, so a mounted form that
|
|
13
|
+
* adds and drops entries holds at most one handler per live key (the record
|
|
14
|
+
* field keys by a monotonic row id, so without eviction it would leak one dead
|
|
15
|
+
* handler per id ever created). Eviction runs during render on the ref-held Map —
|
|
16
|
+
* no effect, no extra render — and never touches a live key's handler, so
|
|
17
|
+
* identity stays stable for every key that survives the render.
|
|
18
|
+
*/
|
|
19
|
+
import { useRef } from 'react';
|
|
20
|
+
export function useKeyedCallbacks(fold, liveKeys) {
|
|
21
|
+
const foldRef = useRef(fold);
|
|
22
|
+
foldRef.current = fold;
|
|
23
|
+
// The cache lives in a ref, lazily initialised, so its identity is HARD-stable
|
|
24
|
+
// for the component's whole life: a `useMemo(() => new Map(), [])` cache is only
|
|
25
|
+
// a hint React may discard, which would drop every cached handler and re-render
|
|
26
|
+
// every sibling — the regression the memo is here to prevent.
|
|
27
|
+
const cacheRef = useRef(undefined);
|
|
28
|
+
const cache = (cacheRef.current ??= new Map());
|
|
29
|
+
const live = new Set(liveKeys);
|
|
30
|
+
for (const key of cache.keys()) {
|
|
31
|
+
if (!live.has(key))
|
|
32
|
+
cache.delete(key);
|
|
33
|
+
}
|
|
34
|
+
return (key) => {
|
|
35
|
+
const cached = cache.get(key);
|
|
36
|
+
if (cached !== undefined)
|
|
37
|
+
return cached;
|
|
38
|
+
const handler = (arg) => {
|
|
39
|
+
foldRef.current(key, arg);
|
|
40
|
+
};
|
|
41
|
+
cache.set(key, handler);
|
|
42
|
+
return handler;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=keyed-callbacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyed-callbacks.js","sourceRoot":"","sources":["../../src/schema-form/keyed-callbacks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE/B,MAAM,UAAU,iBAAiB,CAC/B,IAA8B,EAC9B,QAAqB;IAErB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IACvB,+EAA+E;IAC/E,iFAAiF;IACjF,gFAAgF;IAChF,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,MAAM,CAAuC,SAAS,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,OAAO,KAAK,IAAI,GAAG,EAAuB,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAI,QAAQ,CAAC,CAAC;IAClC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,CAAC,GAAM,EAAE,EAAE;QAChB,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QACxC,MAAM,OAAO,GAAG,CAAC,GAAM,EAAQ,EAAE;YAC/B,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC,CAAC;QACF,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACxB,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object-fields.d.ts","sourceRoot":"","sources":["../../src/schema-form/object-fields.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"object-fields.d.ts","sourceRoot":"","sources":["../../src/schema-form/object-fields.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAM5D,wBAAgB,YAAY,CAAC,EAC3B,UAAU,EACV,YAAY,EACZ,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,YAAoB,GACrB,EAAE;IACD,UAAU,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;IACvD,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,uEAAuE;IACvE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GAAG,SAAS,CA+BZ"}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { FieldNode } from './field-node';
|
|
3
|
+
import { useKeyedCallbacks } from './keyed-callbacks';
|
|
3
4
|
function isPlainObject(value) {
|
|
4
5
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
5
6
|
}
|
|
6
7
|
export function ObjectFields({ properties, requiredKeys, root, value, onChange, path, errors, idPrefix, skip, markRequired = false, }) {
|
|
7
8
|
const obj = isPlainObject(value) ? value : {};
|
|
9
|
+
const changeFor = useKeyedCallbacks((name, next) => {
|
|
10
|
+
onChange(setKey(obj, name, next));
|
|
11
|
+
}, properties.map(([name]) => name));
|
|
8
12
|
return (_jsx(_Fragment, { children: properties.map(([name, propSchema]) => {
|
|
9
13
|
if (skip !== undefined && name === skip)
|
|
10
14
|
return null;
|
|
11
15
|
const childPath = path === '' ? name : `${path}.${name}`;
|
|
12
|
-
return (_jsx(FieldNode, { schema: propSchema, root: root, value: obj[name], onChange: (
|
|
13
|
-
onChange(setKey(obj, name, next));
|
|
14
|
-
}, path: childPath, label: name, required: requiredKeys.has(name), markRequired: markRequired, errors: errors, idPrefix: idPrefix }, name));
|
|
16
|
+
return (_jsx(FieldNode, { schema: propSchema, root: root, value: obj[name], onChange: changeFor(name), path: childPath, label: name, required: requiredKeys.has(name), markRequired: markRequired, errors: errors, idPrefix: idPrefix }, name));
|
|
15
17
|
}) }));
|
|
16
18
|
}
|
|
17
19
|
function setKey(obj, name, next) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object-fields.js","sourceRoot":"","sources":["../../src/schema-form/object-fields.tsx"],"names":[],"mappings":";AAQA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"object-fields.js","sourceRoot":"","sources":["../../src/schema-form/object-fields.tsx"],"names":[],"mappings":";AAQA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAC3B,UAAU,EACV,YAAY,EACZ,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,YAAY,GAAG,KAAK,GAarB;IACC,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,MAAM,SAAS,GAAG,iBAAiB,CACjC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QACb,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC,EACD,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CACjC,CAAC;IACF,OAAO,CACL,4BACG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE;YACrC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAC;YACrD,MAAM,SAAS,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;YACzD,OAAO,CACL,KAAC,SAAS,IAER,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,EAChB,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,EACzB,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAChC,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,IAVb,IAAI,CAWT,CACH,CAAC;QACJ,CAAC,CAAC,GACD,CACJ,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CACb,GAA4B,EAC5B,IAAY,EACZ,IAAa;IAEb,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,GAAG,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,EAAE,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-field.d.ts","sourceRoot":"","sources":["../../src/schema-form/record-field.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"record-field.d.ts","sourceRoot":"","sources":["../../src/schema-form/record-field.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAavC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAuC5D,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,WAAW,EACX,KAAK,EACL,MAAM,EACN,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,SAAS,CA2HZ"}
|
|
@@ -9,6 +9,7 @@ import { RecordEntryRendererContext } from './context';
|
|
|
9
9
|
import { defaultValueForSchema } from './default-value';
|
|
10
10
|
import { FieldGroup } from './field-group';
|
|
11
11
|
import { FieldNode } from './field-node';
|
|
12
|
+
import { useKeyedCallbacks } from './keyed-callbacks';
|
|
12
13
|
function isPlainObject(value) {
|
|
13
14
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
14
15
|
}
|
|
@@ -63,6 +64,7 @@ export function RecordField({ heading, description, error, values, root, value,
|
|
|
63
64
|
const setValue = (id, next) => {
|
|
64
65
|
commit(rows.map((row) => (row.id === id ? { ...row, value: next } : row)));
|
|
65
66
|
};
|
|
67
|
+
const valueChangeFor = useKeyedCallbacks(setValue, rows.map((row) => row.id));
|
|
66
68
|
const addRow = () => {
|
|
67
69
|
commit([...rows, { id: nextId.current++, key: '', value: newEntryValue(values, root) }]);
|
|
68
70
|
};
|
|
@@ -75,9 +77,7 @@ export function RecordField({ heading, description, error, values, root, value,
|
|
|
75
77
|
const position = index + 1;
|
|
76
78
|
const keyFault = rowKeyFault(row.id, blankRowIds, duplicateRowIds);
|
|
77
79
|
const entryPath = path === '' ? row.key : `${path}.${row.key}`;
|
|
78
|
-
const defaultField = (_jsx(FieldNode, { schema: values, root: root, value: row.value, onChange: (
|
|
79
|
-
setValue(row.id, next);
|
|
80
|
-
}, path: entryPath, label: undefined, required: true, errors: errors, idPrefix: idPrefix }));
|
|
80
|
+
const defaultField = (_jsx(FieldNode, { schema: values, root: root, value: row.value, onChange: valueChangeFor(row.id), path: entryPath, label: undefined, required: true, errors: errors, idPrefix: idPrefix }));
|
|
81
81
|
return (_jsxs("div", { className: "tai-row", children: [_jsx(Field, { label: `Key ${String(position)}`, error: keyFault, children: _jsx(TextInput, { value: row.key, onChange: (event) => {
|
|
82
82
|
setKey(row.id, event.target.value);
|
|
83
83
|
} }) }), _jsx("div", { style: { flex: 1 }, children: renderEntry === undefined
|
|
@@ -87,9 +87,7 @@ export function RecordField({ heading, description, error, values, root, value,
|
|
|
87
87
|
path: entryPath,
|
|
88
88
|
valueSchema: values,
|
|
89
89
|
value: row.value,
|
|
90
|
-
onChange: (
|
|
91
|
-
setValue(row.id, next);
|
|
92
|
-
},
|
|
90
|
+
onChange: valueChangeFor(row.id),
|
|
93
91
|
defaultField,
|
|
94
92
|
}) }), _jsxs(Button, { type: "button", variant: "secondary", "aria-label": `Remove entry ${String(position)}`, onClick: () => {
|
|
95
93
|
removeRow(row.id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-field.js","sourceRoot":"","sources":["../../src/schema-form/record-field.tsx"],"names":[],"mappings":";AAaA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"record-field.js","sourceRoot":"","sources":["../../src/schema-form/record-field.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tai42/studio-sdk",
|
|
3
|
-
"version": "17.1.
|
|
3
|
+
"version": "17.1.5",
|
|
4
4
|
"description": "The Studio plugin contract, shared UI components, and hooks that every feature builds on.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "tai42 <oss@tai42.ai>",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"@radix-ui/react-select": "^2.3.2",
|
|
97
97
|
"@radix-ui/react-tabs": "^1.1.21",
|
|
98
98
|
"@radix-ui/react-tooltip": "^1.2.11",
|
|
99
|
-
"@tai42/api-client": "17.1.
|
|
99
|
+
"@tai42/api-client": "17.1.5"
|
|
100
100
|
},
|
|
101
101
|
"scripts": {
|
|
102
102
|
"build": "tsc -b tsconfig.build.json && node scripts/copy-assets.mjs",
|
|
@@ -11,6 +11,7 @@ import { classifySchema } from './classify';
|
|
|
11
11
|
import { defaultValueForSchema } from './default-value';
|
|
12
12
|
import { FieldGroup } from './field-group';
|
|
13
13
|
import { FieldNode } from './field-node';
|
|
14
|
+
import { useKeyedCallbacks } from './keyed-callbacks';
|
|
14
15
|
import type { JsonSchema, SchemaFormErrors } from './types';
|
|
15
16
|
|
|
16
17
|
export function ArrayField({
|
|
@@ -46,6 +47,7 @@ export function ArrayField({
|
|
|
46
47
|
const setItem = (index: number, next: unknown): void => {
|
|
47
48
|
onChange(list.map((item, position) => (position === index ? next : item)));
|
|
48
49
|
};
|
|
50
|
+
const changeFor = useKeyedCallbacks<number, unknown>(setItem, list.keys());
|
|
49
51
|
|
|
50
52
|
return (
|
|
51
53
|
<FieldGroup heading={heading} description={description} error={error} atRoot={false}>
|
|
@@ -63,9 +65,7 @@ export function ArrayField({
|
|
|
63
65
|
schema={items}
|
|
64
66
|
root={root}
|
|
65
67
|
value={item}
|
|
66
|
-
onChange={(
|
|
67
|
-
setItem(index, next);
|
|
68
|
-
}}
|
|
68
|
+
onChange={changeFor(index)}
|
|
69
69
|
path={`${path}[${String(index)}]`}
|
|
70
70
|
label={`Item ${String(index + 1)}`}
|
|
71
71
|
required
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* inline; every other kind delegates to a dedicated field component). Object,
|
|
5
5
|
* array, and union renderers recurse back through here for their children.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { memo, type ReactNode } from 'react';
|
|
8
8
|
|
|
9
9
|
import { Checkbox } from '../components/checkbox';
|
|
10
10
|
import { Field } from '../components/field';
|
|
@@ -45,8 +45,14 @@ function labelText(explicit: string | undefined, label: string | undefined): str
|
|
|
45
45
|
return explicit ?? label ?? 'Value';
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/**
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* A single schema node, dispatched on its classified model kind. Wrapped in
|
|
50
|
+
* `memo` so a field whose props are unchanged is skipped: the containers hand
|
|
51
|
+
* each child a referentially-stable `onChange` (see {@link useKeyedCallbacks})
|
|
52
|
+
* and the controlled value keeps unedited branches identity-equal, so editing
|
|
53
|
+
* one field re-renders only that field's subtree, not every sibling.
|
|
54
|
+
*/
|
|
55
|
+
function FieldNodeImpl(props: FieldNodeProps): ReactNode {
|
|
50
56
|
const { root, value, onChange, path, required, idPrefix, errors } = props;
|
|
51
57
|
const classified = classifySchema(props.schema, root);
|
|
52
58
|
const base = labelText(classified.title, props.label);
|
|
@@ -187,6 +193,8 @@ export function FieldNode(props: FieldNodeProps): ReactNode {
|
|
|
187
193
|
}
|
|
188
194
|
}
|
|
189
195
|
|
|
196
|
+
export const FieldNode = memo(FieldNodeImpl);
|
|
197
|
+
|
|
190
198
|
/** The label/description/error chrome every scalar leaf field shares. */
|
|
191
199
|
interface ScalarFieldChrome {
|
|
192
200
|
readonly heading: string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stable per-key callback factory. A container (object, array, record) renders
|
|
3
|
+
* one child per key and folds that child's edits back into the container value.
|
|
4
|
+
* Written inline, each child's `onChange` is a fresh closure every render, which
|
|
5
|
+
* defeats {@link FieldNode}'s `memo`: an untouched sibling re-renders whenever any
|
|
6
|
+
* field changes. This returns the SAME function identity for a given key across
|
|
7
|
+
* renders — so a memoized child keyed on that identity is skipped when a sibling
|
|
8
|
+
* changes — while each call reads the latest folding logic through a ref, so the
|
|
9
|
+
* callback never closes over a stale container value.
|
|
10
|
+
*
|
|
11
|
+
* The cache is bounded by `liveKeys`, the container's current key set: every
|
|
12
|
+
* render evicts each cached handler whose key is not live, so a mounted form that
|
|
13
|
+
* adds and drops entries holds at most one handler per live key (the record
|
|
14
|
+
* field keys by a monotonic row id, so without eviction it would leak one dead
|
|
15
|
+
* handler per id ever created). Eviction runs during render on the ref-held Map —
|
|
16
|
+
* no effect, no extra render — and never touches a live key's handler, so
|
|
17
|
+
* identity stays stable for every key that survives the render.
|
|
18
|
+
*/
|
|
19
|
+
import { useRef } from 'react';
|
|
20
|
+
|
|
21
|
+
export function useKeyedCallbacks<K, A>(
|
|
22
|
+
fold: (key: K, arg: A) => void,
|
|
23
|
+
liveKeys: Iterable<K>,
|
|
24
|
+
): (key: K) => (arg: A) => void {
|
|
25
|
+
const foldRef = useRef(fold);
|
|
26
|
+
foldRef.current = fold;
|
|
27
|
+
// The cache lives in a ref, lazily initialised, so its identity is HARD-stable
|
|
28
|
+
// for the component's whole life: a `useMemo(() => new Map(), [])` cache is only
|
|
29
|
+
// a hint React may discard, which would drop every cached handler and re-render
|
|
30
|
+
// every sibling — the regression the memo is here to prevent.
|
|
31
|
+
const cacheRef = useRef<Map<K, (arg: A) => void> | undefined>(undefined);
|
|
32
|
+
const cache = (cacheRef.current ??= new Map<K, (arg: A) => void>());
|
|
33
|
+
const live = new Set<K>(liveKeys);
|
|
34
|
+
for (const key of cache.keys()) {
|
|
35
|
+
if (!live.has(key)) cache.delete(key);
|
|
36
|
+
}
|
|
37
|
+
return (key: K) => {
|
|
38
|
+
const cached = cache.get(key);
|
|
39
|
+
if (cached !== undefined) return cached;
|
|
40
|
+
const handler = (arg: A): void => {
|
|
41
|
+
foldRef.current(key, arg);
|
|
42
|
+
};
|
|
43
|
+
cache.set(key, handler);
|
|
44
|
+
return handler;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { ReactNode } from 'react';
|
|
8
8
|
|
|
9
9
|
import { FieldNode } from './field-node';
|
|
10
|
+
import { useKeyedCallbacks } from './keyed-callbacks';
|
|
10
11
|
import type { JsonSchema, SchemaFormErrors } from './types';
|
|
11
12
|
|
|
12
13
|
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
@@ -38,6 +39,12 @@ export function ObjectFields({
|
|
|
38
39
|
markRequired?: boolean;
|
|
39
40
|
}): ReactNode {
|
|
40
41
|
const obj = isPlainObject(value) ? value : {};
|
|
42
|
+
const changeFor = useKeyedCallbacks<string, unknown>(
|
|
43
|
+
(name, next) => {
|
|
44
|
+
onChange(setKey(obj, name, next));
|
|
45
|
+
},
|
|
46
|
+
properties.map(([name]) => name),
|
|
47
|
+
);
|
|
41
48
|
return (
|
|
42
49
|
<>
|
|
43
50
|
{properties.map(([name, propSchema]) => {
|
|
@@ -49,9 +56,7 @@ export function ObjectFields({
|
|
|
49
56
|
schema={propSchema}
|
|
50
57
|
root={root}
|
|
51
58
|
value={obj[name]}
|
|
52
|
-
onChange={(
|
|
53
|
-
onChange(setKey(obj, name, next));
|
|
54
|
-
}}
|
|
59
|
+
onChange={changeFor(name)}
|
|
55
60
|
path={childPath}
|
|
56
61
|
label={name}
|
|
57
62
|
required={requiredKeys.has(name)}
|
|
@@ -22,6 +22,7 @@ import { RecordEntryRendererContext } from './context';
|
|
|
22
22
|
import { defaultValueForSchema } from './default-value';
|
|
23
23
|
import { FieldGroup } from './field-group';
|
|
24
24
|
import { FieldNode } from './field-node';
|
|
25
|
+
import { useKeyedCallbacks } from './keyed-callbacks';
|
|
25
26
|
import type { JsonSchema, SchemaFormErrors } from './types';
|
|
26
27
|
|
|
27
28
|
interface Row {
|
|
@@ -119,6 +120,10 @@ export function RecordField({
|
|
|
119
120
|
const setValue = (id: number, next: unknown): void => {
|
|
120
121
|
commit(rows.map((row) => (row.id === id ? { ...row, value: next } : row)));
|
|
121
122
|
};
|
|
123
|
+
const valueChangeFor = useKeyedCallbacks<number, unknown>(
|
|
124
|
+
setValue,
|
|
125
|
+
rows.map((row) => row.id),
|
|
126
|
+
);
|
|
122
127
|
const addRow = (): void => {
|
|
123
128
|
commit([...rows, { id: nextId.current++, key: '', value: newEntryValue(values, root) }]);
|
|
124
129
|
};
|
|
@@ -146,9 +151,7 @@ export function RecordField({
|
|
|
146
151
|
schema={values}
|
|
147
152
|
root={root}
|
|
148
153
|
value={row.value}
|
|
149
|
-
onChange={(
|
|
150
|
-
setValue(row.id, next);
|
|
151
|
-
}}
|
|
154
|
+
onChange={valueChangeFor(row.id)}
|
|
152
155
|
path={entryPath}
|
|
153
156
|
label={undefined}
|
|
154
157
|
required
|
|
@@ -179,9 +182,7 @@ export function RecordField({
|
|
|
179
182
|
path: entryPath,
|
|
180
183
|
valueSchema: values,
|
|
181
184
|
value: row.value,
|
|
182
|
-
onChange: (
|
|
183
|
-
setValue(row.id, next);
|
|
184
|
-
},
|
|
185
|
+
onChange: valueChangeFor(row.id),
|
|
185
186
|
defaultField,
|
|
186
187
|
})}
|
|
187
188
|
</div>
|