cx 26.1.13 → 26.2.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/build/charts/Marker.d.ts +1 -1
- package/build/charts/Marker.d.ts.map +1 -1
- package/build/charts/Marker.js +16 -6
- package/build/charts/MouseTracker.d.ts +2 -0
- package/build/charts/MouseTracker.d.ts.map +1 -1
- package/build/charts/helpers/PointReducer.d.ts +2 -2
- package/build/charts/helpers/PointReducer.d.ts.map +1 -1
- package/build/data/View.d.ts +5 -3
- package/build/data/View.d.ts.map +1 -1
- package/build/data/View.js +3 -1
- package/build/data/ops/findTreeNode.d.ts +20 -1
- package/build/data/ops/findTreeNode.d.ts.map +1 -1
- package/build/data/ops/findTreeNode.js +19 -0
- package/build/data/ops/findTreePath.d.ts +1 -1
- package/build/data/ops/findTreePath.d.ts.map +1 -1
- package/build/data/ops/findTreePath.js +1 -1
- package/build/data/ops/removeTreeNodes.d.ts +14 -1
- package/build/data/ops/removeTreeNodes.d.ts.map +1 -1
- package/build/data/ops/removeTreeNodes.js +13 -0
- package/build/data/ops/updateArray.d.ts +1 -1
- package/build/data/ops/updateArray.d.ts.map +1 -1
- package/build/data/ops/updateArray.js +1 -1
- package/build/data/ops/updateTree.d.ts +20 -1
- package/build/data/ops/updateTree.d.ts.map +1 -1
- package/build/data/ops/updateTree.js +19 -0
- package/build/jsx-runtime.d.ts +1 -0
- package/build/jsx-runtime.d.ts.map +1 -1
- package/build/jsx-runtime.js +3 -1
- package/build/svg/Rectangle.d.ts +6 -4
- package/build/svg/Rectangle.d.ts.map +1 -1
- package/build/svg/Rectangle.js +9 -7
- package/build/ui/Instance.d.ts +1 -1
- package/build/ui/Instance.d.ts.map +1 -1
- package/build/ui/Instance.js +18 -8
- package/build/ui/IsolatedScope.d.ts +2 -1
- package/build/ui/IsolatedScope.d.ts.map +1 -1
- package/build/ui/Prop.d.ts +1 -1
- package/build/ui/Prop.d.ts.map +1 -1
- package/build/ui/Widget.d.ts +2 -0
- package/build/ui/Widget.d.ts.map +1 -1
- package/build/ui/Widget.js +4 -0
- package/build/ui/adapter/GroupAdapter.d.ts +4 -4
- package/build/ui/adapter/GroupAdapter.d.ts.map +1 -1
- package/build/ui/adapter/GroupAdapter.js +4 -4
- package/build/ui/adapter/TreeAdapter.d.ts +5 -3
- package/build/ui/adapter/TreeAdapter.d.ts.map +1 -1
- package/build/ui/adapter/TreeAdapter.js +12 -5
- package/build/ui/app/startAppLoop.d.ts +2 -2
- package/build/ui/app/startAppLoop.d.ts.map +1 -1
- package/build/ui/app/startHotAppLoop.d.ts +4 -4
- package/build/ui/app/startHotAppLoop.d.ts.map +1 -1
- package/build/ui/app/startHotAppLoop.js +1 -1
- package/build/ui/batchUpdates.d.ts.map +1 -1
- package/build/ui/batchUpdates.js +3 -4
- package/build/widgets/Button.d.ts +0 -7
- package/build/widgets/Button.d.ts.map +1 -1
- package/build/widgets/HtmlElement.d.ts +2 -2
- package/build/widgets/HtmlElement.d.ts.map +1 -1
- package/build/widgets/form/Checkbox.d.ts +3 -3
- package/build/widgets/form/Checkbox.d.ts.map +1 -1
- package/build/widgets/form/Checkbox.js +11 -6
- package/build/widgets/form/DateTimeField.d.ts +4 -0
- package/build/widgets/form/DateTimeField.d.ts.map +1 -1
- package/build/widgets/form/LookupField.d.ts +14 -27
- package/build/widgets/form/LookupField.d.ts.map +1 -1
- package/build/widgets/form/TextField.d.ts +2 -2
- package/build/widgets/form/TextField.d.ts.map +1 -1
- package/build/widgets/grid/Grid.d.ts +20 -16
- package/build/widgets/grid/Grid.d.ts.map +1 -1
- package/build/widgets/grid/Grid.js +200 -86
- package/build/widgets/nav/Menu.d.ts +2 -0
- package/build/widgets/nav/Menu.d.ts.map +1 -1
- package/build/widgets/nav/Route.js +1 -1
- package/build/widgets/overlay/FlyweightTooltipTracker.d.ts +6 -4
- package/build/widgets/overlay/FlyweightTooltipTracker.d.ts.map +1 -1
- package/build/widgets/overlay/FlyweightTooltipTracker.js +3 -0
- package/build/widgets/overlay/Overlay.d.ts +2 -2
- package/build/widgets/overlay/Overlay.d.ts.map +1 -1
- package/dist/data.js +52 -1
- package/dist/jsx-runtime.js +4 -2
- package/dist/manifest.js +879 -873
- package/dist/svg.js +3 -0
- package/dist/ui.js +1548 -1544
- package/dist/widgets.css +1 -1
- package/dist/widgets.js +395 -4
- package/package.json +2 -2
- package/src/charts/Marker.tsx +448 -394
- package/src/charts/MouseTracker.tsx +3 -0
- package/src/charts/helpers/PointReducer.ts +2 -2
- package/src/data/View.ts +76 -19
- package/src/data/ops/findTreeNode.ts +20 -1
- package/src/data/ops/findTreePath.ts +7 -2
- package/src/data/ops/removeTreeNodes.ts +14 -1
- package/src/data/ops/updateArray.ts +4 -4
- package/src/data/ops/updateTree.ts +32 -6
- package/src/index.scss +6 -6
- package/src/jsx-runtime.spec.tsx +40 -0
- package/src/jsx-runtime.ts +87 -84
- package/src/svg/Rectangle.tsx +80 -73
- package/src/ui/DataProxy.ts +55 -55
- package/src/ui/Instance.ts +142 -45
- package/src/ui/IsolatedScope.ts +4 -2
- package/src/ui/Prop.ts +141 -141
- package/src/ui/Rescope.ts +50 -50
- package/src/ui/Widget.tsx +292 -234
- package/src/ui/adapter/ArrayAdapter.ts +229 -229
- package/src/ui/adapter/GroupAdapter.ts +8 -10
- package/src/ui/adapter/TreeAdapter.ts +75 -15
- package/src/ui/app/Url.spec.ts +1 -1
- package/src/ui/app/startAppLoop.tsx +56 -45
- package/src/ui/app/startHotAppLoop.ts +4 -4
- package/src/ui/batchUpdates.ts +16 -21
- package/src/ui/exprHelpers.ts +96 -96
- package/src/widgets/Button.tsx +0 -8
- package/src/widgets/HtmlElement.spec.tsx +100 -72
- package/src/widgets/HtmlElement.tsx +11 -10
- package/src/widgets/Sandbox.ts +104 -104
- package/src/widgets/Section.scss +55 -55
- package/src/widgets/drag-drop/DropZone.scss +74 -74
- package/src/widgets/form/Checkbox.tsx +296 -243
- package/src/widgets/form/DateTimeField.tsx +6 -0
- package/src/widgets/form/LookupField.tsx +70 -73
- package/src/widgets/form/TextField.tsx +2 -2
- package/src/widgets/grid/Grid.scss +43 -10
- package/src/widgets/grid/Grid.tsx +4401 -3848
- package/src/widgets/nav/Menu.tsx +3 -0
- package/src/widgets/nav/Route.ts +1 -1
- package/src/widgets/overlay/FlyweightTooltipTracker.ts +15 -4
- package/src/widgets/overlay/Overlay.tsx +2 -1
- package/src/widgets/overlay/index.d.ts +11 -11
|
@@ -24,6 +24,9 @@ export interface MouseTrackerConfig extends BoundedObjectConfig {
|
|
|
24
24
|
|
|
25
25
|
/** Name of the y-axis. Default is 'y'. */
|
|
26
26
|
yAxis?: string;
|
|
27
|
+
|
|
28
|
+
/** Tooltip configuration. */
|
|
29
|
+
tooltip?: any;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export interface MouseTrackerInstance extends BoundedObjectInstance, TooltipParentInstance {
|
|
@@ -28,7 +28,7 @@ export interface PointReducerInstance<TAccumulator extends PointReducerAccumulat
|
|
|
28
28
|
|
|
29
29
|
export interface PointReducerConfig extends PureContainerConfig {
|
|
30
30
|
/** A callback function used to initialize the accumulator. */
|
|
31
|
-
onInitAccumulator?: string | ((accumulator: DataRecord, instance
|
|
31
|
+
onInitAccumulator?: string | ((accumulator: DataRecord, instance: Instance) => void);
|
|
32
32
|
|
|
33
33
|
/** A callback function used to collect information about all data points. */
|
|
34
34
|
onMap?:
|
|
@@ -36,7 +36,7 @@ export interface PointReducerConfig extends PureContainerConfig {
|
|
|
36
36
|
| ((accumulator: DataRecord, x?: any, y?: any, name?: string, data?: any, array?: any[], index?: number) => void);
|
|
37
37
|
|
|
38
38
|
/** A callback function used to process accumulated information and write results. */
|
|
39
|
-
onReduce?: string | ((accumulator: DataRecord, instance
|
|
39
|
+
onReduce?: string | ((accumulator: DataRecord, instance: Instance) => void);
|
|
40
40
|
|
|
41
41
|
/** Parameters that trigger filter predicate re-creation. */
|
|
42
42
|
filterParams?: StructuredProp;
|
package/src/data/View.ts
CHANGED
|
@@ -42,25 +42,44 @@ export interface ViewMethods<D = Record<string, any>> {
|
|
|
42
42
|
delete(paths: Path[]): boolean;
|
|
43
43
|
delete(...paths: Path[]): boolean;
|
|
44
44
|
delete<V>(path: AccessorChain<V>): boolean;
|
|
45
|
-
delete<T extends any[]>(
|
|
46
|
-
|
|
45
|
+
delete<T extends any[]>(
|
|
46
|
+
...paths: { [K in keyof T]: AccessorChain<T[K]> }
|
|
47
|
+
): boolean;
|
|
48
|
+
delete<T extends any[]>(paths: {
|
|
49
|
+
[K in keyof T]: AccessorChain<T[K]>;
|
|
50
|
+
}): boolean;
|
|
47
51
|
|
|
48
52
|
toggle(path: Path): boolean;
|
|
49
53
|
toggle(path: AccessorChain<boolean>): boolean;
|
|
50
54
|
|
|
51
|
-
update(updateFn: (currentValue:
|
|
52
|
-
update<A extends any[]>(path: Path, updateFn: (currentValue: any, ...args: A) => any, ...args: A): boolean;
|
|
55
|
+
update<V>(path: AccessorChain<V>, updateFn: (currentValue: V) => V): boolean;
|
|
53
56
|
update<V, A extends any[]>(
|
|
54
57
|
path: AccessorChain<V>,
|
|
55
58
|
updateFn: (currentValue: V, ...args: A) => V,
|
|
56
59
|
...args: A
|
|
57
60
|
): boolean;
|
|
61
|
+
update<A extends any[]>(
|
|
62
|
+
updateFn: (currentValue: D, ...args: A) => D,
|
|
63
|
+
...args: any
|
|
64
|
+
): boolean;
|
|
65
|
+
update<A extends any[]>(
|
|
66
|
+
path: Path,
|
|
67
|
+
updateFn: (currentValue: any, ...args: A) => any,
|
|
68
|
+
...args: A
|
|
69
|
+
): boolean;
|
|
58
70
|
|
|
59
71
|
/**
|
|
60
72
|
* Mutates the content of the store using Immer
|
|
61
73
|
*/
|
|
62
|
-
mutate(
|
|
63
|
-
|
|
74
|
+
mutate(
|
|
75
|
+
updateFn: (currentValue: D, ...args: any[]) => void,
|
|
76
|
+
...args: any[]
|
|
77
|
+
): boolean;
|
|
78
|
+
mutate<A extends any[]>(
|
|
79
|
+
path: Path,
|
|
80
|
+
updateFn: (currentValue: any, ...args: A) => void,
|
|
81
|
+
...args: A
|
|
82
|
+
): boolean;
|
|
64
83
|
mutate<V, A extends any[]>(
|
|
65
84
|
path: AccessorChain<V>,
|
|
66
85
|
updateFn: (currentValue: V, ...args: A) => void,
|
|
@@ -74,7 +93,14 @@ export class View<D = any> implements ViewMethods<D> {
|
|
|
74
93
|
declare store?: View;
|
|
75
94
|
declare meta: any;
|
|
76
95
|
declare sealed: boolean;
|
|
77
|
-
cache: {
|
|
96
|
+
cache: {
|
|
97
|
+
version: number;
|
|
98
|
+
data?: any;
|
|
99
|
+
result?: any;
|
|
100
|
+
itemIndex?: number;
|
|
101
|
+
key?: string;
|
|
102
|
+
parentStoreData?: any;
|
|
103
|
+
};
|
|
78
104
|
notificationsSuspended: number = 0;
|
|
79
105
|
dirty: boolean = false;
|
|
80
106
|
|
|
@@ -97,11 +123,17 @@ export class View<D = any> implements ViewMethods<D> {
|
|
|
97
123
|
if (typeof path == "object" && path != null) {
|
|
98
124
|
let changed = false;
|
|
99
125
|
for (let key in path)
|
|
100
|
-
if (
|
|
126
|
+
if (
|
|
127
|
+
path.hasOwnProperty(key) &&
|
|
128
|
+
this.get(key) === undefined &&
|
|
129
|
+
this.setItem(key, path[key])
|
|
130
|
+
)
|
|
131
|
+
changed = true;
|
|
101
132
|
return changed;
|
|
102
133
|
}
|
|
103
134
|
let binding = Binding.get(path);
|
|
104
|
-
if (this.get(binding.path) === undefined)
|
|
135
|
+
if (this.get(binding.path) === undefined)
|
|
136
|
+
return this.setItem(binding.path, value);
|
|
105
137
|
return false;
|
|
106
138
|
}
|
|
107
139
|
|
|
@@ -111,7 +143,9 @@ export class View<D = any> implements ViewMethods<D> {
|
|
|
111
143
|
set(path: any, value?: any): boolean {
|
|
112
144
|
if (typeof path == "object" && path != null) {
|
|
113
145
|
let changed = false;
|
|
114
|
-
for (let key in path)
|
|
146
|
+
for (let key in path)
|
|
147
|
+
if (path.hasOwnProperty(key) && this.setItem(key, path[key]))
|
|
148
|
+
changed = true;
|
|
115
149
|
return changed;
|
|
116
150
|
}
|
|
117
151
|
let binding = Binding.get(path);
|
|
@@ -144,11 +178,16 @@ export class View<D = any> implements ViewMethods<D> {
|
|
|
144
178
|
delete(paths: Path[]): boolean;
|
|
145
179
|
delete(...paths: Path[]): boolean;
|
|
146
180
|
delete<V>(path: AccessorChain<V>): boolean;
|
|
147
|
-
delete<T extends any[]>(
|
|
148
|
-
|
|
181
|
+
delete<T extends any[]>(
|
|
182
|
+
...paths: { [K in keyof T]: AccessorChain<T[K]> }
|
|
183
|
+
): boolean;
|
|
184
|
+
delete<T extends any[]>(paths: {
|
|
185
|
+
[K in keyof T]: AccessorChain<T[K]>;
|
|
186
|
+
}): boolean;
|
|
149
187
|
delete(path?: any): boolean {
|
|
150
188
|
if (arguments.length > 1) path = Array.from(arguments);
|
|
151
|
-
if (isArray(path))
|
|
189
|
+
if (isArray(path))
|
|
190
|
+
return path.map((arg) => this.delete(arg as Path)).some(Boolean);
|
|
152
191
|
|
|
153
192
|
let binding = Binding.get(path);
|
|
154
193
|
return this.deleteItem(binding.path);
|
|
@@ -178,7 +217,8 @@ export class View<D = any> implements ViewMethods<D> {
|
|
|
178
217
|
|
|
179
218
|
if (arguments.length > 1) path = Array.from(arguments);
|
|
180
219
|
|
|
181
|
-
if (isArray(path))
|
|
220
|
+
if (isArray(path))
|
|
221
|
+
return path.map((arg) => Binding.get(arg as any).value(storeData));
|
|
182
222
|
|
|
183
223
|
return Binding.get(path).value(storeData);
|
|
184
224
|
}
|
|
@@ -189,21 +229,38 @@ export class View<D = any> implements ViewMethods<D> {
|
|
|
189
229
|
return this.set(path, !this.get(path));
|
|
190
230
|
}
|
|
191
231
|
|
|
192
|
-
update(updateFn: (currentValue:
|
|
193
|
-
update<A extends any[]>(path: Path, updateFn: (currentValue: any, ...args: A) => any, ...args: A): boolean;
|
|
232
|
+
update<V>(path: AccessorChain<V>, updateFn: (currentValue: V) => V): boolean;
|
|
194
233
|
update<V, A extends any[]>(
|
|
195
234
|
path: AccessorChain<V>,
|
|
196
235
|
updateFn: (currentValue: V, ...args: A) => V,
|
|
197
236
|
...args: A
|
|
198
237
|
): boolean;
|
|
238
|
+
update(
|
|
239
|
+
updateFn: (currentValue: D, ...args: any[]) => any,
|
|
240
|
+
...args: any
|
|
241
|
+
): boolean;
|
|
242
|
+
update<A extends any[]>(
|
|
243
|
+
path: Path,
|
|
244
|
+
updateFn: (currentValue: any, ...args: A) => any,
|
|
245
|
+
...args: A
|
|
246
|
+
): boolean;
|
|
199
247
|
update(path: any, updateFn?: any, ...args: any[]): boolean {
|
|
200
248
|
if (arguments.length == 1 && isFunction(path))
|
|
201
|
-
return this.load(
|
|
249
|
+
return this.load(
|
|
250
|
+
path.apply(null, [this.getData(), updateFn, ...args]),
|
|
251
|
+
);
|
|
202
252
|
return this.set(path, updateFn.apply(null, [this.get(path), ...args]));
|
|
203
253
|
}
|
|
204
254
|
|
|
205
|
-
mutate(
|
|
206
|
-
|
|
255
|
+
mutate(
|
|
256
|
+
updateFn: (currentValue: D, ...args: any[]) => void,
|
|
257
|
+
...args: any[]
|
|
258
|
+
): boolean;
|
|
259
|
+
mutate<A extends any[]>(
|
|
260
|
+
path: Path,
|
|
261
|
+
updateFn: (currentValue: any, ...args: A) => void,
|
|
262
|
+
...args: A
|
|
263
|
+
): boolean;
|
|
207
264
|
mutate<V, A extends any[]>(
|
|
208
265
|
path: AccessorChain<V>,
|
|
209
266
|
updateFn: (currentValue: V, ...args: A) => void,
|
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Recursively searches a tree structure for a node matching the criteria.
|
|
3
|
+
*
|
|
4
|
+
* @param array - The tree data array to search.
|
|
5
|
+
* @param criteria - Predicate that returns `true` when a matching node is found.
|
|
6
|
+
* @param childrenField - Name of the property containing child nodes.
|
|
7
|
+
* @returns The first matching node, or `false` if not found.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Find node by ID
|
|
11
|
+
* const node = findTreeNode(data, n => n.id === targetId, "$children");
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Check if selected node is a leaf
|
|
15
|
+
* const node = findTreeNode(data, n => n.id === selectedId, "$children");
|
|
16
|
+
* if (node && node.$leaf) {
|
|
17
|
+
* // Cannot add children to a leaf node
|
|
18
|
+
* }
|
|
19
|
+
*/
|
|
20
|
+
export function findTreeNode<T = any>(array: T[], criteria: (node: T) => boolean, childrenField: NoInfer<keyof T>): T | false {
|
|
2
21
|
if (!Array.isArray(array)) return false;
|
|
3
22
|
|
|
4
23
|
for (const node of array) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function findTreePath<T = any>(
|
|
2
2
|
array: T[] | undefined,
|
|
3
3
|
criteria: (node: T) => boolean,
|
|
4
|
-
childrenField: keyof T
|
|
4
|
+
childrenField: NoInfer<keyof T>,
|
|
5
5
|
currentPath: T[] = [],
|
|
6
6
|
): T[] | false {
|
|
7
7
|
if (!Array.isArray(array)) return false;
|
|
@@ -13,7 +13,12 @@ export function findTreePath<T = any>(
|
|
|
13
13
|
|
|
14
14
|
const children = node[childrenField] as T[] | undefined;
|
|
15
15
|
|
|
16
|
-
const childPath = findTreePath(
|
|
16
|
+
const childPath = findTreePath(
|
|
17
|
+
children,
|
|
18
|
+
criteria,
|
|
19
|
+
childrenField,
|
|
20
|
+
currentPath,
|
|
21
|
+
);
|
|
17
22
|
if (childPath) return childPath;
|
|
18
23
|
|
|
19
24
|
currentPath.pop();
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { updateTree } from "./updateTree";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Recursively removes nodes from a tree structure that match the criteria.
|
|
5
|
+
* Returns a new tree without the matching nodes, or the original tree if no nodes were removed.
|
|
6
|
+
*
|
|
7
|
+
* @param array - The tree data array to filter.
|
|
8
|
+
* @param criteria - Predicate that returns `true` for nodes to remove.
|
|
9
|
+
* @param childrenField - Name of the property containing child nodes.
|
|
10
|
+
* @returns A new tree with matching nodes removed, or the original array if unchanged.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Remove node by ID
|
|
14
|
+
* removeTreeNodes(data, node => node.id === targetId, "$children");
|
|
15
|
+
*/
|
|
3
16
|
export function removeTreeNodes<T = any>(
|
|
4
17
|
array: T[] | undefined,
|
|
5
18
|
criteria: (node: T) => boolean,
|
|
6
|
-
childrenField: keyof T
|
|
19
|
+
childrenField: NoInfer<keyof T>,
|
|
7
20
|
): T[] | undefined {
|
|
8
21
|
return updateTree(
|
|
9
22
|
array,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export function updateArray<T = any>(
|
|
2
|
-
array:
|
|
1
|
+
export function updateArray<T = any, A extends T[] | undefined = T[] | undefined>(
|
|
2
|
+
array: A,
|
|
3
3
|
updateCallback: (item: T, index: number) => T,
|
|
4
4
|
itemFilter?: null | ((item: T, index: number) => boolean),
|
|
5
5
|
removeFilter?: (item: T, index: number) => boolean,
|
|
6
|
-
):
|
|
6
|
+
): A {
|
|
7
7
|
if (!array) return array;
|
|
8
8
|
|
|
9
9
|
const newArray: T[] = [];
|
|
@@ -27,5 +27,5 @@ export function updateArray<T = any>(
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
return dirty ? newArray : array;
|
|
30
|
+
return (dirty ? newArray : array) as A;
|
|
31
31
|
}
|
|
@@ -1,20 +1,46 @@
|
|
|
1
1
|
import { updateArray } from "./updateArray";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Recursively updates nodes in a tree structure that match a filter.
|
|
5
|
+
* Returns a new tree with updated nodes, or the original tree if no changes were made.
|
|
6
|
+
*
|
|
7
|
+
* @param array - The tree data array to update.
|
|
8
|
+
* @param updateCallback - Function that receives a node and returns the updated node.
|
|
9
|
+
* @param itemFilter - Predicate that returns `true` for nodes to update. If `null`, all nodes are updated.
|
|
10
|
+
* @param childrenField - Name of the property containing child nodes.
|
|
11
|
+
* @param removeFilter - Optional predicate that returns `true` for nodes to remove.
|
|
12
|
+
* @returns A new tree with updates applied, or the original array if unchanged.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Expand all non-leaf nodes
|
|
16
|
+
* updateTree(data, node => ({ ...node, $expanded: true }), node => !node.$leaf, "$children");
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Add a child to a specific node
|
|
20
|
+
* updateTree(data, node => ({ ...node, $children: [...node.$children, newChild] }), node => node.id === parentId, "$children");
|
|
21
|
+
*/
|
|
22
|
+
export function updateTree<T = any, A extends T[] | undefined = T[] | undefined>(
|
|
23
|
+
array: A,
|
|
5
24
|
updateCallback: (item: T) => T,
|
|
6
25
|
itemFilter: ((item: T) => boolean) | null,
|
|
7
|
-
childrenField: keyof T
|
|
26
|
+
childrenField: NoInfer<keyof T>,
|
|
8
27
|
removeFilter?: (item: T) => boolean,
|
|
9
|
-
):
|
|
28
|
+
): A {
|
|
10
29
|
return updateArray(
|
|
11
30
|
array,
|
|
12
31
|
(item: T) => {
|
|
13
32
|
if (!itemFilter || itemFilter(item)) item = updateCallback(item);
|
|
14
33
|
const children = item[childrenField];
|
|
15
34
|
if (!Array.isArray(children)) return item;
|
|
16
|
-
const updatedChildren = updateTree(
|
|
17
|
-
|
|
35
|
+
const updatedChildren = updateTree(
|
|
36
|
+
children,
|
|
37
|
+
updateCallback,
|
|
38
|
+
itemFilter,
|
|
39
|
+
childrenField,
|
|
40
|
+
removeFilter,
|
|
41
|
+
);
|
|
42
|
+
if (updatedChildren != children)
|
|
43
|
+
return { ...item, [childrenField]: updatedChildren };
|
|
18
44
|
return item;
|
|
19
45
|
},
|
|
20
46
|
null,
|
package/src/index.scss
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
@import "global";
|
|
2
|
-
@import "util/index";
|
|
3
|
-
@import "ui/index";
|
|
4
|
-
@import "widgets/index";
|
|
5
|
-
@import "svg/index";
|
|
6
|
-
@import "charts/index";
|
|
1
|
+
@import "global";
|
|
2
|
+
@import "util/index";
|
|
3
|
+
@import "ui/index";
|
|
4
|
+
@import "widgets/index";
|
|
5
|
+
@import "svg/index";
|
|
6
|
+
@import "charts/index";
|
package/src/jsx-runtime.spec.tsx
CHANGED
|
@@ -465,4 +465,44 @@ describe("jsx-runtime type inference", () => {
|
|
|
465
465
|
assert.ok(widget.jsxAttributes.includes("key"));
|
|
466
466
|
});
|
|
467
467
|
});
|
|
468
|
+
|
|
469
|
+
describe("Fragment", () => {
|
|
470
|
+
it("returns single child directly", () => {
|
|
471
|
+
const widget = (
|
|
472
|
+
<cx>
|
|
473
|
+
<>
|
|
474
|
+
<div text="Hello" />
|
|
475
|
+
</>
|
|
476
|
+
</cx>
|
|
477
|
+
);
|
|
478
|
+
assert.ok(widget);
|
|
479
|
+
assert.equal(widget.$type.name, "HtmlElement");
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it("returns array of children", () => {
|
|
483
|
+
const widget = (
|
|
484
|
+
<cx>
|
|
485
|
+
<>
|
|
486
|
+
<div text="Hello" />
|
|
487
|
+
<div text="World" />
|
|
488
|
+
</>
|
|
489
|
+
</cx>
|
|
490
|
+
);
|
|
491
|
+
assert.ok(Array.isArray(widget));
|
|
492
|
+
assert.equal(widget.length, 2);
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it("works with widgets inside fragment", () => {
|
|
496
|
+
const widget = (
|
|
497
|
+
<cx>
|
|
498
|
+
<>
|
|
499
|
+
<Button text="Click" />
|
|
500
|
+
<TextField value={bind("name")} />
|
|
501
|
+
</>
|
|
502
|
+
</cx>
|
|
503
|
+
);
|
|
504
|
+
assert.ok(Array.isArray(widget));
|
|
505
|
+
assert.equal(widget.length, 2);
|
|
506
|
+
});
|
|
507
|
+
});
|
|
468
508
|
});
|
package/src/jsx-runtime.ts
CHANGED
|
@@ -1,84 +1,87 @@
|
|
|
1
|
-
import type { ComponentClass, FunctionComponent, JSX as ReactJSX } from "react";
|
|
2
|
-
import type { CxFunctionalComponent } from "./ui/createFunctionalComponent";
|
|
3
|
-
import { Widget } from "./ui/Widget";
|
|
4
|
-
import { isArray } from "./util/isArray";
|
|
5
|
-
import { isString } from "./util/isString";
|
|
6
|
-
import { HtmlElement, HtmlElementConfig } from "./widgets/HtmlElement";
|
|
7
|
-
import { ReactElementWrapper, ReactElementWrapperConfig } from "./widgets/ReactElementWrapper";
|
|
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
|
-
type
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
import type { ComponentClass, FunctionComponent, JSX as ReactJSX } from "react";
|
|
2
|
+
import type { CxFunctionalComponent } from "./ui/createFunctionalComponent";
|
|
3
|
+
import { Widget } from "./ui/Widget";
|
|
4
|
+
import { isArray } from "./util/isArray";
|
|
5
|
+
import { isString } from "./util/isString";
|
|
6
|
+
import { HtmlElement, HtmlElementConfig } from "./widgets/HtmlElement";
|
|
7
|
+
import { ReactElementWrapper, ReactElementWrapperConfig } from "./widgets/ReactElementWrapper";
|
|
8
|
+
|
|
9
|
+
// Fragment symbol for supporting <>...</> syntax
|
|
10
|
+
export const Fragment = Symbol.for("cx.fragment");
|
|
11
|
+
|
|
12
|
+
export function jsx(typeName: any, props: any, key?: string): any {
|
|
13
|
+
if (isArray(typeName)) return typeName;
|
|
14
|
+
|
|
15
|
+
if (key) {
|
|
16
|
+
// key is allowed in CxJS, i.e. Sandbox use it
|
|
17
|
+
props = {
|
|
18
|
+
...props,
|
|
19
|
+
key,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (typeName.type || typeName.$type) return typeName;
|
|
24
|
+
|
|
25
|
+
if (props.children && isArray(props.children) && props.children.length == 0) props.children = null;
|
|
26
|
+
|
|
27
|
+
if (props.children && props.children.length == 1) props.children = props.children[0];
|
|
28
|
+
|
|
29
|
+
if (typeName == "cx" || typeName === Fragment) return props.children;
|
|
30
|
+
|
|
31
|
+
if (isString(typeName) && typeName[0] == typeName[0].toLowerCase()) {
|
|
32
|
+
props.tag = typeName;
|
|
33
|
+
typeName = HtmlElement;
|
|
34
|
+
}
|
|
35
|
+
// React components (functions/classes without isComponentType and not CxJS functional components) should go through ReactElementWrapper
|
|
36
|
+
else if (typeof typeName === "function" && !typeName.isComponentType && !typeName.$isComponentFactory) {
|
|
37
|
+
props.componentType = typeName;
|
|
38
|
+
typeName = ReactElementWrapper;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
$type: typeName,
|
|
43
|
+
...props,
|
|
44
|
+
jsxAttributes: props && Object.keys(props),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const jsxs = jsx;
|
|
49
|
+
|
|
50
|
+
type ReactIntrinsicElements = ReactJSX.IntrinsicElements;
|
|
51
|
+
|
|
52
|
+
type CxIntrinsicElements = {
|
|
53
|
+
[K in keyof ReactIntrinsicElements]: HtmlElementConfig<K>;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Helper type to transform props for React components (but not CxJS functional components)
|
|
57
|
+
// Uses a workaround to avoid TypeScript inference issues with conditional types in LibraryManagedAttributes
|
|
58
|
+
// We use Omit to exclude componentType since it's added automatically by the jsx-runtime
|
|
59
|
+
type TransformReactComponentProps<C, P> = [C] extends [CxFunctionalComponent<any>]
|
|
60
|
+
? P // CxJS functional components already have proper types
|
|
61
|
+
: [C] extends [FunctionComponent<any>]
|
|
62
|
+
? ReactElementWrapperConfig<P>
|
|
63
|
+
: [C] extends [ComponentClass<any>]
|
|
64
|
+
? ReactElementWrapperConfig<P>
|
|
65
|
+
: P;
|
|
66
|
+
|
|
67
|
+
export namespace JSX {
|
|
68
|
+
/**
|
|
69
|
+
* Base class for JSX element instances.
|
|
70
|
+
* All widgets must extend from Widget class or React.Component.
|
|
71
|
+
*/
|
|
72
|
+
export type ElementClass = Widget<any> | React.Component<any, any>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Intrinsic JSX elements (HTML-like tags).
|
|
76
|
+
* Supports HTML elements that map to HtmlElement widgets.
|
|
77
|
+
*/
|
|
78
|
+
export interface IntrinsicElements extends CxIntrinsicElements {
|
|
79
|
+
cx: any;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Transform props for React function components used in CxJS JSX.
|
|
84
|
+
* Adds standard WidgetConfig properties and transforms React props to Prop<X>.
|
|
85
|
+
*/
|
|
86
|
+
export type LibraryManagedAttributes<C, P> = TransformReactComponentProps<C, P>;
|
|
87
|
+
}
|