@apia/tree 2.0.11 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/index.d.ts +415 -7
  2. package/dist/index.js +1547 -6
  3. package/dist/index.js.map +1 -1
  4. package/package.json +11 -7
  5. package/dist/OOTree/OOTreeChildren.d.ts +0 -14
  6. package/dist/OOTree/OOTreeChildren.d.ts.map +0 -1
  7. package/dist/OOTree/OOTreeChildren.js +0 -17
  8. package/dist/OOTree/OOTreeChildren.js.map +0 -1
  9. package/dist/OOTree/OOTreeNode.d.ts +0 -30
  10. package/dist/OOTree/OOTreeNode.d.ts.map +0 -1
  11. package/dist/OOTree/OOTreeNode.js +0 -133
  12. package/dist/OOTree/OOTreeNode.js.map +0 -1
  13. package/dist/OOTree/index.d.ts +0 -36
  14. package/dist/OOTree/index.d.ts.map +0 -1
  15. package/dist/OOTree/index.js +0 -126
  16. package/dist/OOTree/index.js.map +0 -1
  17. package/dist/OOTree/types.d.ts +0 -11
  18. package/dist/OOTree/types.d.ts.map +0 -1
  19. package/dist/SearchLabel.js +0 -31
  20. package/dist/SearchLabel.js.map +0 -1
  21. package/dist/Tree.d.ts +0 -7
  22. package/dist/Tree.d.ts.map +0 -1
  23. package/dist/Tree.js +0 -131
  24. package/dist/Tree.js.map +0 -1
  25. package/dist/TreeContext.d.ts +0 -13
  26. package/dist/TreeContext.d.ts.map +0 -1
  27. package/dist/TreeContext.js +0 -22
  28. package/dist/TreeContext.js.map +0 -1
  29. package/dist/TreeDataController.d.ts +0 -116
  30. package/dist/TreeDataController.d.ts.map +0 -1
  31. package/dist/TreeDataController.js +0 -616
  32. package/dist/TreeDataController.js.map +0 -1
  33. package/dist/TreeItem.js +0 -51
  34. package/dist/TreeItem.js.map +0 -1
  35. package/dist/TreeItemChildren.js +0 -20
  36. package/dist/TreeItemChildren.js.map +0 -1
  37. package/dist/TreeItemLabel.js +0 -72
  38. package/dist/TreeItemLabel.js.map +0 -1
  39. package/dist/getDomProps.js +0 -37
  40. package/dist/getDomProps.js.map +0 -1
  41. package/dist/renderers/DefaultIconRenderer.js +0 -18
  42. package/dist/renderers/DefaultIconRenderer.js.map +0 -1
  43. package/dist/renderers/DefaultLabelRenderer.js +0 -10
  44. package/dist/renderers/DefaultLabelRenderer.js.map +0 -1
  45. package/dist/renderers/Spacer.js +0 -10
  46. package/dist/renderers/Spacer.js.map +0 -1
  47. package/dist/types.d.ts +0 -211
  48. package/dist/types.d.ts.map +0 -1
  49. package/dist/useTreeData.d.ts +0 -25
  50. package/dist/useTreeData.d.ts.map +0 -1
  51. package/dist/useTreeData.js +0 -131
  52. package/dist/useTreeData.js.map +0 -1
  53. package/dist/util.js +0 -220
  54. package/dist/util.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,7 +1,1548 @@
1
- export { default as Tree } from './Tree.js';
2
- export { useTreeContext } from './TreeContext.js';
3
- export { default as TreeDataController, useTreeDataController, useTreeSelector, useTreeSelectorByName } from './TreeDataController.js';
4
- export { default as useTreeData } from './useTreeData.js';
5
- export { OOTree } from './OOTree/index.js';
6
- export { OOTreeNode, isOOTreeNode } from './OOTree/OOTreeNode.js';
1
+ import { jsx, jsxs, Fragment } from '@apia/theme/jsx-runtime';
2
+ import { useMount as useMount$1, useDebounceFn, useUnmount } from 'ahooks';
3
+ import React, { useState, useEffect, memo } from 'react';
4
+ import { shallowEqual as shallowEqual$1 } from 'react-redux';
5
+ import { Box, getVariant, Spinner } from '@apia/theme';
6
+ import { EventEmitter, PropsStore, addBoundary, getSpecificParent, usePropsSelector, useLatest, useMount, shallowEqual, formatMessage } from '@apia/util';
7
+ import { LoaderSpinner, IconButton } from '@apia/components';
8
+ import { Icon } from '@apia/icons';
9
+ import uniqueId from 'lodash-es/uniqueId';
10
+
11
+ const getLastVisibleChild = (handler, id, avoidFiltered) => {
12
+ const nodeProps = handler.propsStore.getFieldProps(id);
13
+ if (id !== "root" && (nodeProps.isDisabled || !nodeProps.isExpanded) || nodeProps.children.length === 0)
14
+ return null;
15
+ for (let i = nodeProps.children.length - 1; i >= 0; i--) {
16
+ const currentId = nodeProps.children[i];
17
+ const currentProps = handler.propsStore.getFieldProps(currentId);
18
+ if (!avoidFiltered || !currentProps.isFiltered) {
19
+ const lastChildrenLastChildren = getLastVisibleChild(
20
+ handler,
21
+ currentId,
22
+ avoidFiltered
23
+ );
24
+ return lastChildrenLastChildren !== null ? lastChildrenLastChildren : currentId;
25
+ }
26
+ }
27
+ return null;
28
+ };
29
+ const getPreviousChild = (handler, id, avoidFiltered) => {
30
+ const nodeProps = handler.propsStore.getFieldProps(id);
31
+ const fatherProps = handler.propsStore.getFieldProps(nodeProps.parentId);
32
+ let childPosition = fatherProps.children?.findIndex((current) => current === nodeProps.id) ?? -1;
33
+ while (childPosition > 0) {
34
+ const prevSibling = handler.propsStore.getFieldProps(
35
+ fatherProps.children[childPosition - 1]
36
+ );
37
+ const prevSiblingLastChild = getLastVisibleChild(
38
+ handler,
39
+ prevSibling.id,
40
+ avoidFiltered
41
+ );
42
+ if (prevSiblingLastChild !== null)
43
+ return prevSiblingLastChild;
44
+ if (!avoidFiltered || !prevSibling.isFiltered)
45
+ return prevSibling.id;
46
+ childPosition--;
47
+ }
48
+ if (nodeProps.parentId === "root")
49
+ return null;
50
+ return nodeProps.parentId;
51
+ };
52
+ const getFirstNonFilteredChild = (handler, id) => {
53
+ const nodeProps = handler.propsStore.getFieldProps(id);
54
+ for (const child of nodeProps.children) {
55
+ if (!handler.propsStore.getFieldProps(child).isFiltered)
56
+ return child;
57
+ }
58
+ return null;
59
+ };
60
+ const getNextChild = (handler, id, avoidChildren, avoidFiltered) => {
61
+ const nodeProps = handler.propsStore.getFieldProps(id);
62
+ const fatherProps = handler.propsStore.getFieldProps(nodeProps.parentId);
63
+ let childPosition = fatherProps.children?.findIndex((current) => current === nodeProps.id) ?? Infinity;
64
+ if (!avoidChildren && nodeProps.isExpanded) {
65
+ if (!avoidFiltered && nodeProps.children.length > 0)
66
+ return nodeProps.children[0];
67
+ const firstNonFilteredChild = getFirstNonFilteredChild(
68
+ handler,
69
+ nodeProps.id
70
+ );
71
+ if (firstNonFilteredChild !== null)
72
+ return firstNonFilteredChild;
73
+ }
74
+ while (childPosition < fatherProps.children.length - 1) {
75
+ const nextSibling = fatherProps.children[childPosition + 1];
76
+ if (!avoidFiltered || !handler.propsStore.getFieldProps(nextSibling).isFiltered)
77
+ return nextSibling;
78
+ childPosition++;
79
+ }
80
+ if (nodeProps.parentId === "root")
81
+ return null;
82
+ return getNextChild(handler, fatherProps.id, true, avoidFiltered);
83
+ };
84
+ const getNextNodeWithKey = (handler, id, firstKey, avoidFiltered) => {
85
+ let nextChildWithKey = id;
86
+ do {
87
+ nextChildWithKey = getNextChild(
88
+ handler,
89
+ nextChildWithKey,
90
+ false,
91
+ avoidFiltered
92
+ );
93
+ if (nextChildWithKey !== null) {
94
+ const nodeProps2 = handler.propsStore.getFieldProps(nextChildWithKey);
95
+ if ((!nodeProps2.isFiltered || !avoidFiltered) && nodeProps2.label.toUpperCase().startsWith(firstKey.toUpperCase()))
96
+ return nextChildWithKey;
97
+ }
98
+ } while (nextChildWithKey !== null);
99
+ const nodeProps = handler.propsStore.getFieldProps(id);
100
+ const positionInParent = handler.propsStore.getFieldProps(nodeProps.parentId).children.findIndex((current) => current === id);
101
+ if (nodeProps.parentId !== "root" || positionInParent > 0) {
102
+ const [firstChildOfTree] = handler.propsStore.getFieldProps("root").children;
103
+ const firstChildProps = handler.propsStore.getFieldProps(firstChildOfTree);
104
+ if (firstChildOfTree && (!avoidFiltered || !firstChildProps.isFiltered) && firstChildProps.label.toUpperCase().startsWith(firstKey.toUpperCase()))
105
+ return firstChildOfTree;
106
+ nextChildWithKey = firstChildOfTree;
107
+ do {
108
+ nextChildWithKey = getNextChild(
109
+ handler,
110
+ nextChildWithKey,
111
+ false,
112
+ avoidFiltered
113
+ );
114
+ if (nextChildWithKey) {
115
+ const currentNodeProps = handler.propsStore.getFieldProps(nextChildWithKey);
116
+ if ((!avoidFiltered || !currentNodeProps.isFiltered) && currentNodeProps.label.toUpperCase().startsWith(firstKey.toUpperCase()))
117
+ return nextChildWithKey;
118
+ }
119
+ } while (nextChildWithKey !== id && nextChildWithKey !== null);
120
+ }
121
+ return null;
122
+ };
123
+ const getNodePath = (handler, nodeId) => {
124
+ const path = [nodeId];
125
+ let currentStep = nodeId;
126
+ do {
127
+ currentStep = handler.propsStore.getFieldProps(currentStep).parentId;
128
+ path.unshift(currentStep);
129
+ } while (currentStep !== "root");
130
+ return path;
131
+ };
132
+ const getCommonAncestor = (handler, oneNodeId, anotherNodeId) => {
133
+ const oneNodePath = getNodePath(handler, oneNodeId);
134
+ const anotherNodePath = getNodePath(handler, anotherNodeId);
135
+ for (let i = oneNodePath.length - 1; i >= 0; i--) {
136
+ const anotherNodeIndex = anotherNodePath.indexOf(oneNodePath[i]);
137
+ if (anotherNodeIndex !== -1)
138
+ return {
139
+ commonAncestor: oneNodePath[i],
140
+ oneNodeAncestorChild: oneNodePath[i + 1],
141
+ anotherNodeAncestorChild: anotherNodePath[anotherNodeIndex + 1]
142
+ };
143
+ }
144
+ console.warn(
145
+ "This point should have never been reached since the root node is common to all nodes",
146
+ { oneNodeId, anotherNodeId, oneNodePath, anotherNodePath }
147
+ );
148
+ return null;
149
+ };
150
+ const unfilterNodes = (handler) => {
151
+ handler.state.filteredNodes.forEach((current) => {
152
+ handler.propsStore.updateField(
153
+ current,
154
+ { isFiltered: false },
155
+ { noEmit: true }
156
+ );
157
+ });
158
+ const firstChild = handler.propsStore.getFieldProps("root").children[0];
159
+ if (firstChild)
160
+ handler.setFocusedNode(firstChild);
161
+ handler.setState({ filteredNodes: [] });
162
+ };
163
+ const matchNodesAgainstString = (handler, searchString, nodeId = "root") => {
164
+ let hasMatched = false;
165
+ let matchNode = null;
166
+ const nodeProps = handler.propsStore.getFieldProps(nodeId);
167
+ if (nodeId !== "root" && nodeProps.label.match(new RegExp(searchString, "i"))) {
168
+ handler.propsStore.updateField(nodeId, { isFiltered: false });
169
+ hasMatched = true;
170
+ matchNode = nodeProps.id;
171
+ handler.setState({
172
+ filteredNodes: handler.state.filteredNodes.filter(
173
+ (current) => current !== nodeId
174
+ )
175
+ });
176
+ }
177
+ let hasChildrenMatched = false;
178
+ nodeProps.children.forEach((current) => {
179
+ hasChildrenMatched = matchNodesAgainstString(handler, searchString, current) || hasMatched;
180
+ hasMatched = hasChildrenMatched || hasMatched;
181
+ if (hasMatched && !matchNode)
182
+ matchNode = current;
183
+ });
184
+ if (hasChildrenMatched) {
185
+ handler.propsStore.updateField(nodeId, { isExpanded: true });
186
+ if (hasMatched && !matchNode)
187
+ matchNode = nodeId;
188
+ }
189
+ if (!hasMatched) {
190
+ handler.propsStore.updateField(
191
+ nodeId,
192
+ { isFiltered: true },
193
+ { noEmit: true }
194
+ );
195
+ handler.setState({
196
+ filteredNodes: [...handler.state.filteredNodes, nodeId]
197
+ });
198
+ }
199
+ if (matchNode !== null)
200
+ handler.setFocusedNode(matchNode);
201
+ return hasMatched;
202
+ };
203
+ const selectAllNodesBetweenTwoNodes = (handler, oneNodeId, anotherNodeId, avoidFiltered) => {
204
+ const commonAncestorData = getCommonAncestor(
205
+ handler,
206
+ oneNodeId,
207
+ anotherNodeId
208
+ );
209
+ if (commonAncestorData) {
210
+ const { anotherNodeAncestorChild, commonAncestor, oneNodeAncestorChild } = commonAncestorData;
211
+ const ancestorProps = handler.propsStore.getFieldProps(commonAncestor);
212
+ const oneNodePosition = ancestorProps.children.findIndex(
213
+ (current) => current === oneNodeAncestorChild
214
+ );
215
+ const anotherNodePosition = ancestorProps.children.findIndex(
216
+ (current) => current === anotherNodeAncestorChild
217
+ );
218
+ let currentId = oneNodePosition >= anotherNodePosition ? anotherNodeId : oneNodeId;
219
+ const lastNodeId = oneNodePosition < anotherNodePosition ? anotherNodeId : oneNodeId;
220
+ handler.toggleNodeSelectedState(currentId, true);
221
+ do {
222
+ currentId = getNextChild(handler, currentId, false, avoidFiltered);
223
+ if (currentId !== null)
224
+ handler.toggleNodeSelectedState(currentId, true);
225
+ } while (currentId !== lastNodeId && currentId !== null);
226
+ }
227
+ };
228
+
229
+ var __defProp$2 = Object.defineProperty;
230
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
231
+ var __publicField$2 = (obj, key, value) => {
232
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
233
+ return value;
234
+ };
235
+ const trees = {};
236
+ const treesRecordEmitter = new class TreesRecordEmitter extends EventEmitter {
237
+ emit(eventName, params) {
238
+ super.emit(eventName, params);
239
+ trees[params.name] = params.controller;
240
+ }
241
+ }();
242
+ function useTreeDataController(name) {
243
+ const [controller, setDataController] = useState(
244
+ trees[name]
245
+ );
246
+ useEffect(() => {
247
+ if (trees[name] !== controller)
248
+ setDataController(trees[name]);
249
+ return treesRecordEmitter.on("addController", (ev) => {
250
+ if (ev.name === name)
251
+ setDataController(ev.controller);
252
+ });
253
+ }, []);
254
+ return controller;
255
+ }
256
+ function getTreeDataController(name) {
257
+ return trees[name];
258
+ }
259
+ class TreeDataController extends EventEmitter {
260
+ constructor(name, configuration, propsStore = new PropsStore({
261
+ logCommands: {
262
+ propsStore: `treeProps_${name}`,
263
+ propsLog: `treeLog_${name}`,
264
+ propsSuscriptors: `propsSuscriptors_${name}`,
265
+ updateProp: `updateProp_${name}`
266
+ }
267
+ })) {
268
+ super();
269
+ this.name = name;
270
+ this.propsStore = propsStore;
271
+ __publicField$2(this, "_configuration");
272
+ __publicField$2(this, "hasApendedFirstChild", false);
273
+ /**
274
+ * Este array se usa para conocer los padres faltantes al momento de la
275
+ * construcción del árbol, de forma de optimizar el proceso
276
+ */
277
+ __publicField$2(this, "missingParents", []);
278
+ __publicField$2(this, "nonEmittedUpdates", []);
279
+ __publicField$2(this, "stateKey", "treeState");
280
+ __publicField$2(this, "previousNodes", []);
281
+ __publicField$2(this, "setState", (updateProps, conf) => {
282
+ this.propsStore.updateField(
283
+ this.stateKey,
284
+ {
285
+ ...this.state,
286
+ ...updateProps
287
+ },
288
+ conf
289
+ );
290
+ this.emit("stateUpdate", null);
291
+ });
292
+ __publicField$2(this, "useState", (selector) => {
293
+ const [selectedState, setSelectedState] = useState(selector(this.state));
294
+ const latestState = useLatest(selectedState);
295
+ useMount(() => {
296
+ return this.on("stateUpdate", () => {
297
+ const newSelection = selector(this.state);
298
+ if (!shallowEqual(newSelection, latestState.current)) {
299
+ setSelectedState(newSelection);
300
+ }
301
+ });
302
+ });
303
+ return selectedState;
304
+ });
305
+ this._configuration = configuration ?? {
306
+ current: { emitUpdates: true }
307
+ };
308
+ this._configuration.current.emitUpdates = this._configuration.current.emitUpdates ?? true;
309
+ this.setState(this.getInitialState());
310
+ treesRecordEmitter.emit("addController", {
311
+ name,
312
+ controller: this
313
+ });
314
+ this.initRoot();
315
+ }
316
+ get configuration() {
317
+ return { ...this._configuration };
318
+ }
319
+ get state() {
320
+ return this.propsStore.getFieldProps(
321
+ this.stateKey
322
+ );
323
+ }
324
+ get stateStore() {
325
+ return this.propsStore;
326
+ }
327
+ destructor() {
328
+ this.propsStore.destructor();
329
+ }
330
+ append(node) {
331
+ const newNode = { ...node, nodeProps: node.nodeProps ?? {} };
332
+ let father;
333
+ if (newNode.parentId !== void 0 && this.propsStore.getFieldProps(newNode.parentId)?.children) {
334
+ father = newNode.parentId;
335
+ } else {
336
+ father = "root";
337
+ if (newNode.parentId)
338
+ this.missingParents.push(newNode.parentId);
339
+ newNode.actualParentId = newNode.parentId;
340
+ newNode.parentId = "root";
341
+ }
342
+ this.propsStore.updateField(
343
+ newNode.id,
344
+ {
345
+ ...newNode,
346
+ children: newNode.children ?? []
347
+ },
348
+ { isUrgent: true }
349
+ );
350
+ this.setState({ length: this.state.length + 1 });
351
+ if (this.missingParents.includes(newNode.id)) {
352
+ this.propsStore.getFieldProps("root").children.forEach((currentChild) => {
353
+ if (this.propsStore.getFieldProps(currentChild).actualParentId === newNode.id) {
354
+ this.move(currentChild, newNode.id);
355
+ }
356
+ });
357
+ this.missingParents = this.missingParents.filter(
358
+ (current) => current !== newNode.id
359
+ );
360
+ }
361
+ this.propsStore.updateField(
362
+ father,
363
+ {
364
+ children: [
365
+ ...this.propsStore.getFieldProps(father).children,
366
+ newNode.id
367
+ ],
368
+ hasLoaded: true
369
+ },
370
+ { noEmit: this._configuration.current?.emitUpdates === false }
371
+ );
372
+ if (this._configuration.current?.emitUpdates === false)
373
+ this.nonEmittedUpdates.push(father);
374
+ if (!this.hasApendedFirstChild) {
375
+ this.hasApendedFirstChild = true;
376
+ this.setFocusedNode(newNode.id, true);
377
+ }
378
+ if (newNode.isExpanded)
379
+ this.toggleNodeExpandedState(newNode.id, true);
380
+ }
381
+ /**
382
+ * Cuando se quieren agregar muchos nodos, es conveniente llamar primero al
383
+ * método batchInit y luego de finalizar, al método batchFinish()
384
+ */
385
+ batchInit() {
386
+ this.config({ emitUpdates: false });
387
+ this.setState({ isLoading: true }, { isUrgent: true });
388
+ }
389
+ getNodesRecursive(nodeId = "root") {
390
+ const node = this.propsStore.getFieldProps(nodeId);
391
+ return node.children.reduce(
392
+ (prev, current) => [...prev, ...this.getNodesRecursive(current)],
393
+ [...node.children]
394
+ );
395
+ }
396
+ /**
397
+ * Cuando se quieren agregar muchos nodos, es conveniente llamar primero al
398
+ * método batchInit y luego de finalizar, al método batchFinish()
399
+ */
400
+ batchFinish() {
401
+ this.config({ emitUpdates: true });
402
+ setTimeout(() => this.setState({ isLoading: false }), 0);
403
+ const children = [...this.getNodesRecursive()];
404
+ const deleteNodes = this.previousNodes.filter(
405
+ (current) => !children.find((search) => search === current)
406
+ );
407
+ deleteNodes.forEach((current) => this.remove(current));
408
+ this.config({ emitUpdates: true });
409
+ setTimeout(() => this.setState({ isLoading: false }), 0);
410
+ }
411
+ config(newConf) {
412
+ if (this._configuration.current?.emitUpdates === false && newConf.emitUpdates !== false) {
413
+ this.nonEmittedUpdates.forEach(
414
+ (current) => this.propsStore.updateField(current, {
415
+ children: [...this.propsStore.getFieldProps(current).children]
416
+ })
417
+ );
418
+ this.nonEmittedUpdates = [];
419
+ }
420
+ Object.assign(
421
+ this._configuration.current,
422
+ newConf
423
+ );
424
+ }
425
+ deselectAll() {
426
+ this.state.selectedNodes.forEach(
427
+ (currentId) => this.propsStore.updateField(currentId, { isSelected: false })
428
+ );
429
+ this.setState({ selectedNodes: [] });
430
+ }
431
+ forceEmitUpdate() {
432
+ this.propsStore.updateField("root", {
433
+ forceUpdate: (this.propsStore.getFieldProps("root").forceUpdate ?? 0) + 1
434
+ });
435
+ }
436
+ getInitialState() {
437
+ return {
438
+ expandedNodes: [],
439
+ filteredNodes: [],
440
+ focusedNode: null,
441
+ isLoading: false,
442
+ length: 0,
443
+ selectedNodes: []
444
+ };
445
+ }
446
+ getNodesAsArray() {
447
+ const allFields = { ...this.propsStore.fields };
448
+ delete allFields[this.stateKey];
449
+ delete allFields.root;
450
+ return Object.values(allFields);
451
+ }
452
+ /**
453
+ * Devuelve un array que contiene el id de todos los nodos pertenecientes al
454
+ * árbol
455
+ */
456
+ getNodesIds() {
457
+ const allFields = { ...this.propsStore.fields };
458
+ delete allFields[this.stateKey];
459
+ delete allFields.root;
460
+ return Object.keys(allFields);
461
+ }
462
+ getParentId(parentId) {
463
+ return this.propsStore.getFieldProps(parentId).parentId;
464
+ }
465
+ /**
466
+ * Este método permite controlar el comportamiento con el teclado desde fuera
467
+ * del componente.
468
+ */
469
+ handleKey(ev) {
470
+ const focusedId = this.state.focusedNode;
471
+ const nodeProps = this.propsStore.getFieldProps(focusedId);
472
+ if (ev.key === "*") {
473
+ if (this.state.focusedNode === null)
474
+ return;
475
+ const parent = this.propsStore.getFieldProps(
476
+ this.propsStore.getFieldProps(this.state.focusedNode).parentId
477
+ );
478
+ parent.children.forEach(
479
+ (current) => this.toggleNodeExpandedState(current, true)
480
+ );
481
+ } else if (ev.key.match(/^\w$/)) {
482
+ const nextChildWithKey = getNextNodeWithKey(
483
+ this,
484
+ focusedId,
485
+ ev.key,
486
+ true
487
+ );
488
+ if (nextChildWithKey !== null) {
489
+ this.setFocusedNode(nextChildWithKey);
490
+ this.emit("mustFocus", nextChildWithKey);
491
+ }
492
+ } else
493
+ switch (ev.code) {
494
+ case "Home": {
495
+ const firstChild = getFirstNonFilteredChild(this, "root");
496
+ if (firstChild === null)
497
+ return;
498
+ if (ev.shiftKey && ev.ctrlKey && this.state.focusedNode !== null) {
499
+ selectAllNodesBetweenTwoNodes(
500
+ this,
501
+ firstChild,
502
+ this.state.focusedNode,
503
+ true
504
+ );
505
+ }
506
+ this.setFocusedNode(firstChild);
507
+ this.emit("mustFocus", firstChild);
508
+ break;
509
+ }
510
+ case "End": {
511
+ const lastVisibleChild = getLastVisibleChild(this, "root", true);
512
+ if (lastVisibleChild !== null) {
513
+ if (ev.shiftKey && ev.ctrlKey && this.state.focusedNode !== null) {
514
+ selectAllNodesBetweenTwoNodes(
515
+ this,
516
+ lastVisibleChild,
517
+ this.state.focusedNode,
518
+ true
519
+ );
520
+ }
521
+ this.setFocusedNode(lastVisibleChild);
522
+ this.emit("mustFocus", lastVisibleChild);
523
+ }
524
+ break;
525
+ }
526
+ case "ArrowRight": {
527
+ if (nodeProps.isLeaf)
528
+ return;
529
+ ev.preventDefault();
530
+ if (nodeProps.isExpanded) {
531
+ const firstChild = getFirstNonFilteredChild(this, nodeProps.id);
532
+ if (firstChild !== null) {
533
+ this.setFocusedNode(firstChild);
534
+ this.emit("mustFocus", firstChild);
535
+ }
536
+ } else
537
+ this.toggleNodeExpandedState(nodeProps.id, true);
538
+ break;
539
+ }
540
+ case "ArrowLeft": {
541
+ ev.preventDefault();
542
+ if (nodeProps.isLeaf || !nodeProps.isExpanded) {
543
+ if (nodeProps.parentId !== "root" && nodeProps.parentId !== void 0) {
544
+ this.setFocusedNode(nodeProps.parentId);
545
+ this.emit("mustFocus", nodeProps.parentId);
546
+ }
547
+ } else
548
+ this.toggleNodeExpandedState(nodeProps.id, false);
549
+ break;
550
+ }
551
+ case "ArrowUp": {
552
+ ev.preventDefault();
553
+ const prevChild = getPreviousChild(this, nodeProps.id, true);
554
+ if (prevChild !== null) {
555
+ if (ev.shiftKey)
556
+ this.toggleNodeSelectedState(prevChild, true);
557
+ this.setFocusedNode(prevChild);
558
+ this.emit("mustFocus", prevChild);
559
+ } else
560
+ this.emit("onArrowUpOnFirstElement", true);
561
+ break;
562
+ }
563
+ case "ArrowDown": {
564
+ ev.preventDefault();
565
+ const nextChild = getNextChild(this, nodeProps.id, false, true);
566
+ if (nextChild !== null) {
567
+ if (ev.shiftKey)
568
+ this.toggleNodeSelectedState(nextChild, true);
569
+ this.setFocusedNode(nextChild);
570
+ this.emit("mustFocus", nextChild);
571
+ }
572
+ break;
573
+ }
574
+ case "Space": {
575
+ if (this._configuration.current?.disableSelection)
576
+ return;
577
+ ev.preventDefault();
578
+ this.toggleNodeSelectedState(nodeProps.id);
579
+ break;
580
+ }
581
+ }
582
+ }
583
+ isNode(node) {
584
+ return typeof node === "object" && "parentId" in node;
585
+ }
586
+ isNodeContainer(node) {
587
+ return typeof node === "object" && !this.isNode(node);
588
+ }
589
+ includes(searchNode) {
590
+ return !!this.propsStore.getFieldProps(searchNode.id);
591
+ }
592
+ initRoot() {
593
+ this.propsStore.updateField(
594
+ "root",
595
+ { children: [], id: "root" },
596
+ { isUrgent: true }
597
+ );
598
+ }
599
+ move(moveNode, destinationNode, afterOrBefore) {
600
+ const currentFather = this.propsStore.getFieldProps(
601
+ this.propsStore.getFieldProps(moveNode).parentId
602
+ );
603
+ const newFather = this.propsStore.getFieldProps(destinationNode);
604
+ if (!currentFather) {
605
+ console.warn("The current node does not belong to the tree.", moveNode);
606
+ return;
607
+ }
608
+ if (!newFather) {
609
+ console.warn(
610
+ "The destination node does not belong to the tree. No action will be made",
611
+ destinationNode
612
+ );
613
+ return;
614
+ }
615
+ currentFather.children = currentFather.children?.filter(
616
+ (search) => search !== moveNode
617
+ );
618
+ if (!newFather.children)
619
+ newFather.children = [];
620
+ if (!afterOrBefore)
621
+ newFather.children.push(moveNode);
622
+ else if (afterOrBefore.position) {
623
+ newFather.children.splice(afterOrBefore.position - 1, 0, moveNode);
624
+ } else {
625
+ const key = afterOrBefore.after ? afterOrBefore.after : afterOrBefore.before;
626
+ const relatedNodeKey = key && this.isNode(key) ? key.id : key;
627
+ const relatedNodeIndex = newFather.children.findIndex(
628
+ (search) => search === relatedNodeKey
629
+ );
630
+ const actualIndex = addBoundary(
631
+ afterOrBefore.before !== void 0 ? relatedNodeIndex : relatedNodeIndex + 1,
632
+ 0
633
+ );
634
+ if (actualIndex === -1 || actualIndex === newFather.children.length)
635
+ newFather.children.push(moveNode);
636
+ else
637
+ newFather.children.splice(actualIndex, 0, moveNode);
638
+ }
639
+ this.propsStore.updateField(currentFather.id, {
640
+ children: [...currentFather.children ?? []]
641
+ });
642
+ this.propsStore.updateField(newFather.id, {
643
+ children: [...newFather.children]
644
+ });
645
+ this.propsStore.updateField(moveNode, {
646
+ parentId: newFather.id
647
+ });
648
+ }
649
+ /**
650
+ * Borra el nodo del árbol, y dependiendo del parámetro removeChildren, borra
651
+ * también sus hijos.
652
+ *
653
+ * @param removeChildren - Si se pasa en false, los nodos hijos son movidos al root
654
+ */
655
+ remove(removeNode, removeChildren = true) {
656
+ const removeNodeId = this.isNode(removeNode) ? removeNode.id : removeNode;
657
+ const removingNode = this.propsStore.getFieldProps(removeNodeId);
658
+ if (!removingNode)
659
+ return;
660
+ const father = this.propsStore.getFieldProps(removingNode.parentId);
661
+ if (father) {
662
+ this.propsStore.updateField(father.id, {
663
+ children: father.children?.filter((search) => search !== removeNodeId)
664
+ });
665
+ }
666
+ removingNode.children?.forEach((current) => {
667
+ if (removeChildren) {
668
+ this.remove(current);
669
+ } else {
670
+ this.move(current, "root");
671
+ }
672
+ });
673
+ this.setState({
674
+ selectedNodes: this.state.selectedNodes.filter(
675
+ (current) => current !== removeNodeId
676
+ ),
677
+ focusedNode: this.state.focusedNode === removeNodeId ? null : this.state.focusedNode,
678
+ length: this.state.length - 1
679
+ });
680
+ this.propsStore.removeField(removeNodeId);
681
+ }
682
+ removeMultiple(nodes, removeChildren = true) {
683
+ nodes.forEach((current) => this.remove(current, removeChildren));
684
+ }
685
+ /**
686
+ * Borra todos los nodos del árbol.
687
+ */
688
+ removeAll() {
689
+ if (this.configuration.current?.emitUpdates === false) {
690
+ this.previousNodes = this.getNodesIds();
691
+ this.propsStore.updateField(
692
+ "root",
693
+ { children: [], id: "root" },
694
+ { isUrgent: true, noEmit: true }
695
+ );
696
+ } else {
697
+ this.hasApendedFirstChild = false;
698
+ this.setState({ focusedNode: null }, { isUrgent: true });
699
+ this.setSelectedNodes([]);
700
+ this.initRoot();
701
+ Object.keys(this.propsStore.fields).forEach((current) => {
702
+ if (!["root", this.stateKey].includes(current))
703
+ this.propsStore.removeField(current);
704
+ });
705
+ }
706
+ }
707
+ selectAll() {
708
+ this.setState({
709
+ selectedNodes: this.getNodesIds()
710
+ });
711
+ this.state.selectedNodes.forEach(
712
+ (currentId) => this.propsStore.updateField(currentId, { isSelected: true })
713
+ );
714
+ }
715
+ setExpandedNodes(nodes) {
716
+ this.state.expandedNodes.forEach(
717
+ (current) => this.propsStore.updateField(current, { isExpanded: false })
718
+ );
719
+ this.setState({ expandedNodes: nodes });
720
+ nodes.forEach(
721
+ (current) => this.propsStore.updateField(current, { isExpanded: true })
722
+ );
723
+ }
724
+ setFocusedNode(key, avoidSelection) {
725
+ if (this.state.focusedNode !== null)
726
+ this.propsStore.updateField(this.state.focusedNode, { isFocused: false });
727
+ this.setState({
728
+ focusedNode: key
729
+ });
730
+ if (!this._configuration.current?.isMultiple && !avoidSelection && (this._configuration.current?.selectionMode ?? "onFocus") === "onFocus")
731
+ this.setSelectedNodes([key]);
732
+ this.propsStore.updateField(key, { isFocused: true });
733
+ }
734
+ setSelectedNodes(nodes, force = false) {
735
+ if (this._configuration.current?.disableSelection && !force)
736
+ return;
737
+ this.state.selectedNodes.forEach(
738
+ (current) => this.propsStore.updateField(current, { isSelected: false })
739
+ );
740
+ this.setState({
741
+ selectedNodes: nodes.filter((current) => {
742
+ const { isSelectable } = this.propsStore.getFieldProps(current);
743
+ if (isSelectable !== false)
744
+ this.propsStore.updateField(current, { isSelected: true });
745
+ return isSelectable !== false;
746
+ })
747
+ });
748
+ }
749
+ setSelectedNodesByClickEvent(ev) {
750
+ if (this._configuration.current?.disableSelection)
751
+ return;
752
+ const nodeKey = getSpecificParent(
753
+ ev.target,
754
+ (current) => current.getAttribute("role") === "treeitem"
755
+ )?.getAttribute("data-key");
756
+ if (nodeKey) {
757
+ const nodeProps = this.propsStore.getFieldProps(nodeKey);
758
+ if (nodeProps.isDisabled || nodeProps.isSelectable === false)
759
+ return;
760
+ const previousSelectionKeys = [...this.state.selectedNodes];
761
+ const allowMultiple = this._configuration.current?.isMultiple && (this._configuration.current?.selectionMode !== "explicitMultiple" || ev.ctrlKey || ev.shiftKey);
762
+ const newSelection = allowMultiple ? nodeProps.isSelected ? previousSelectionKeys.filter((current) => current !== nodeKey) : [...previousSelectionKeys, nodeKey] : [nodeKey];
763
+ this.setSelectedNodes(newSelection);
764
+ } else
765
+ console.warn("Cannot set selection, no treeitem found", ev);
766
+ }
767
+ toggleNodeExpandedState(key, shouldExpand) {
768
+ const nodeProps = this.propsStore.getFieldProps(key);
769
+ if (nodeProps.isDisabled || nodeProps.isLeaf || nodeProps.isLoading)
770
+ return;
771
+ if (this._configuration.current?.onLoadData && !nodeProps.hasLoaded) {
772
+ this.propsStore.updateField(key, { isLoading: true });
773
+ this._configuration.current?.onLoadData(nodeProps).finally(() => {
774
+ this.propsStore.updateField(key, {
775
+ isLoading: false,
776
+ isExpanded: true,
777
+ hasLoaded: true
778
+ });
779
+ this.setState({
780
+ expandedNodes: [...this.state.expandedNodes, key]
781
+ });
782
+ this._configuration.current?.onExpand?.(
783
+ this.propsStore.getFieldProps(key)
784
+ );
785
+ });
786
+ } else {
787
+ const { expandedNodes } = this.state;
788
+ const shouldExpandInner = shouldExpand !== void 0 ? shouldExpand : !expandedNodes.includes(key);
789
+ if (this.propsStore.getFieldProps(key)?.isDisabled)
790
+ return;
791
+ this.setState({
792
+ expandedNodes: shouldExpandInner ? [...expandedNodes, key] : expandedNodes.filter((current) => current !== key)
793
+ });
794
+ this.propsStore.updateField(key, { isExpanded: shouldExpandInner });
795
+ this._configuration.current?.onExpand?.(
796
+ this.propsStore.getFieldProps(key)
797
+ );
798
+ }
799
+ }
800
+ toggleNodeSelectedState(key, isSelected) {
801
+ if (this._configuration.current?.disableSelection)
802
+ return;
803
+ const nodeProps = this.propsStore.getFieldProps(key);
804
+ if (nodeProps.isDisabled || nodeProps.isSelectable === false)
805
+ return;
806
+ const shouldSelect = isSelected !== void 0 ? isSelected : !this.state.selectedNodes.includes(key);
807
+ if (shouldSelect && nodeProps.isSelected || isSelected === false && !nodeProps.isSelected)
808
+ return;
809
+ if (shouldSelect && !this._configuration.current?.isMultiple && this.state.selectedNodes[0])
810
+ this.toggleNodeSelectedState(this.state.selectedNodes[0], false);
811
+ this.propsStore.updateField(key, { isSelected: shouldSelect });
812
+ this.setState({
813
+ selectedNodes: shouldSelect ? [...this.state.selectedNodes, key] : this.state.selectedNodes.filter((current) => current !== key)
814
+ });
815
+ }
816
+ }
817
+ function useTreeSelector(handler, configuration) {
818
+ return usePropsSelector(
819
+ handler?.stateKey ?? "__NO__TREE__KEY__",
820
+ {
821
+ propsStore: handler?.propsStore,
822
+ ...configuration
823
+ }
824
+ );
825
+ }
826
+ function useTreeSelectorByName(treeName, configuration) {
827
+ const handler = useTreeDataController(treeName);
828
+ const selection = usePropsSelector(
829
+ handler?.stateKey ?? "__NO__TREE__KEY__",
830
+ {
831
+ propsStore: handler?.propsStore,
832
+ ...configuration
833
+ }
834
+ );
835
+ return { selection, handler };
836
+ }
837
+ var TreeDataController$1 = TreeDataController;
838
+
839
+ function getActiveDescendantName(treeName, nodeId) {
840
+ return `${treeName}__${nodeId}`;
841
+ }
842
+ function getDomProps(_, treeName, type, par) {
843
+ switch (type) {
844
+ case "node": {
845
+ const node = par;
846
+ const tree = getTreeDataController(treeName);
847
+ return {
848
+ "aria-disabled": node.isDisabled,
849
+ "aria-expanded": node.isLeaf ? void 0 : !!node.isExpanded,
850
+ "aria-label": node.label,
851
+ ...tree.configuration.current?.disableSelection ? void 0 : {
852
+ "aria-selected": node.isSelectable !== false && !node.isDisabled ? !!node.isSelected : void 0
853
+ },
854
+ className: node.className,
855
+ color: node.color,
856
+ "data-key": node.id,
857
+ id: getActiveDescendantName(treeName, node.id),
858
+ role: "treeitem",
859
+ title: node.title
860
+ };
861
+ }
862
+ default: {
863
+ const tree = par;
864
+ return {
865
+ role: "tree",
866
+ "aria-multiselectable": tree.isMultiple
867
+ };
868
+ }
869
+ }
870
+ }
871
+
872
+ const SearchLabel = ({
873
+ isLoading: isSearching,
874
+ onDelete,
875
+ searchString
876
+ }) => {
877
+ if (!searchString && !isSearching)
878
+ return null;
879
+ return /* @__PURE__ */ jsx(Box, { className: `tree__searchLabelBox ${isSearching ? "isLoading" : ""}`, children: isSearching ? /* @__PURE__ */ jsx(LoaderSpinner, { className: "tree__loading" }) : /* @__PURE__ */ jsxs(Box, { as: "label", className: "tree__searchLabel", children: [
880
+ formatMessage(window.LBL_FILTERING_BY, { TOK1: searchString }),
881
+ /* @__PURE__ */ jsx(
882
+ IconButton,
883
+ {
884
+ icon: "Close",
885
+ "aria-label": window.LBL_DELETE_FILTER,
886
+ onClick: onDelete,
887
+ title: window.LBL_DELETE_FILTER,
888
+ size: "Sm",
889
+ ...getVariant("icon-inherit")
890
+ }
891
+ )
892
+ ] }) });
893
+ };
894
+ var SearchLabel$1 = SearchLabel;
895
+
896
+ const TreeContext = React.createContext({});
897
+ function useTreeContext() {
898
+ const context = React.useContext(TreeContext);
899
+ if (!context)
900
+ throw new Error("There is no tree context");
901
+ return context;
902
+ }
903
+ const TreeContextProvider = ({
904
+ children,
905
+ value
906
+ }) => {
907
+ const Provider = React.useMemo(() => {
908
+ return TreeContext.Provider;
909
+ }, []);
910
+ return /* @__PURE__ */ jsx(Provider, { value, children });
911
+ };
912
+
913
+ const DefaultIconRenderer = (props) => {
914
+ return /* @__PURE__ */ jsx(
915
+ Icon,
916
+ {
917
+ name: props.icon,
918
+ title: "",
919
+ size: props.iconSize ?? "iconSm",
920
+ className: "tree__node__icon"
921
+ }
922
+ );
923
+ };
924
+ var DefaultIconRenderer$1 = DefaultIconRenderer;
925
+
926
+ const DefaultLabelRenderer = (props) => {
927
+ return /* @__PURE__ */ jsx(Box, { as: "span", children: props.label });
928
+ };
929
+ var DefaultLabelRenderer$1 = DefaultLabelRenderer;
930
+
931
+ const Spacer = ({ level }) => {
932
+ return /* @__PURE__ */ jsx(Fragment, { children: Array(level).fill("").map((_, i) => i).map((current) => /* @__PURE__ */ jsx(Box, { className: "spacer" }, current)) });
933
+ };
934
+ var Spacer$1 = Spacer;
935
+
936
+ const TreeItemLabel = ({ level, treeKey }) => {
937
+ const {
938
+ handler,
939
+ treeProps: { toggleNodesOnLabelClick }
940
+ } = useTreeContext();
941
+ const props = usePropsSelector(treeKey, {
942
+ selector: (current) => current,
943
+ comparator: (prevProps, newProps) => {
944
+ return shallowEqual$1(prevProps, newProps) && shallowEqual$1(prevProps?.children, newProps?.children);
945
+ },
946
+ propsStore: handler.propsStore
947
+ });
948
+ const Renderer = React.useMemo(
949
+ () => props.labelRenderer ?? DefaultLabelRenderer$1,
950
+ [props.labelRenderer]
951
+ );
952
+ const handleToggle = React.useCallback(() => {
953
+ handler.toggleNodeExpandedState(treeKey);
954
+ }, [handler, treeKey]);
955
+ const IconRenderer = React.useMemo(() => {
956
+ return typeof props.icon === "string" ? DefaultIconRenderer$1 : props.icon;
957
+ }, [props.icon]);
958
+ const onClick = React.useCallback(() => {
959
+ if (props.allowToggleExpandedFromLabel !== false)
960
+ handler.toggleNodeExpandedState(treeKey);
961
+ }, [handler, props.allowToggleExpandedFromLabel, treeKey]);
962
+ return /* @__PURE__ */ jsxs(
963
+ Box,
964
+ {
965
+ as: "span",
966
+ className: `tree__nodeItemLabel ${props.isFocused ? "focus" : ""}`,
967
+ children: [
968
+ /* @__PURE__ */ jsx(Spacer$1, { level }),
969
+ (props.isLoading || props.isExpanded && props.isLeaf !== true || props.isLeaf === false || (props.children?.length ?? 0) > 0 || props.isLeaf === void 0 && !props.hasLoaded && handler.configuration.current?.onLoadData) && /* @__PURE__ */ jsx(Box, { className: "tree__expanderWrapper", children: props.isLoading ? /* @__PURE__ */ jsx(Spinner, { sx: { width: "iconSm", height: "iconSm" } }) : /* @__PURE__ */ jsx(
970
+ Icon,
971
+ {
972
+ className: "tree__expandIcon",
973
+ onClick: handleToggle,
974
+ name: props.isExpanded ? "ArrowDownThin" : "ArrowRightThin",
975
+ title: "",
976
+ size: 20
977
+ }
978
+ ) }),
979
+ props.icon && IconRenderer && /* @__PURE__ */ jsx(IconRenderer, { ...props }),
980
+ /* @__PURE__ */ jsx(
981
+ Box,
982
+ {
983
+ as: "span",
984
+ className: "tree__nodeItemLabelRenderer",
985
+ onClick: toggleNodesOnLabelClick !== false ? onClick : void 0,
986
+ children: /* @__PURE__ */ jsx(Renderer, { ...props })
987
+ }
988
+ )
989
+ ]
990
+ }
991
+ );
992
+ };
993
+ var TreeItemLabel$1 = React.memo(TreeItemLabel);
994
+
995
+ const TreeItem = ({ level, treeKey }) => {
996
+ const { handler, name, forceUpdate, treeProps } = useTreeContext();
997
+ const props = usePropsSelector(treeKey, {
998
+ selector: (current) => current,
999
+ comparator: (prevProps, newProps) => {
1000
+ return shallowEqual$1(prevProps, newProps) && shallowEqual$1(prevProps?.children, newProps?.children);
1001
+ },
1002
+ propsStore: handler.propsStore
1003
+ });
1004
+ const nodes = React.useMemo(
1005
+ () => props.children?.map(
1006
+ (current) => handler.propsStore.getFieldProps(current)
1007
+ ) ?? [],
1008
+ [props.children, handler.propsStore]
1009
+ );
1010
+ const domProps = getDomProps(treeProps, name, "node", props);
1011
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
1012
+ Box,
1013
+ {
1014
+ as: "li",
1015
+ ...domProps,
1016
+ className: `${domProps.className ?? ""} tree__item`,
1017
+ children: [
1018
+ /* @__PURE__ */ jsx(TreeItemLabel$1, { level, treeKey }),
1019
+ props.isExpanded && /* @__PURE__ */ jsx(
1020
+ TreeItemChildren$1,
1021
+ {
1022
+ forceUpdate,
1023
+ role: "group",
1024
+ level,
1025
+ nodes
1026
+ }
1027
+ )
1028
+ ]
1029
+ }
1030
+ ) });
1031
+ };
1032
+ var TreeItem$1 = React.memo(TreeItem);
1033
+
1034
+ const TreeItemChildren = React.forwardRef(({ level, nodes, forceUpdate, ...props }, ref) => {
1035
+ const { handler } = useTreeContext();
1036
+ return /* @__PURE__ */ jsx(Box, { ref, as: "ul", ...props, children: nodes?.map((current) => {
1037
+ const currentProps = handler.propsStore.getFieldProps(current.id);
1038
+ if (!currentProps)
1039
+ return null;
1040
+ return currentProps.isFiltered ? null : /* @__PURE__ */ jsx(TreeItem$1, { level: level + 1, treeKey: current.id }, current.id);
1041
+ }) });
1042
+ });
1043
+ TreeItemChildren.displayName = "TreeItemChildren";
1044
+ var TreeItemChildren$1 = React.memo(TreeItemChildren);
1045
+
1046
+ function useTreeData({ name, treeProps }) {
1047
+ const props = useLatest(treeProps);
1048
+ const handler = React.useMemo(
1049
+ () => props?.current.controller ?? new TreeDataController$1(name, props),
1050
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1051
+ []
1052
+ );
1053
+ useMount$1(() => {
1054
+ treeProps?.initialNodes?.forEach((current) => handler.append(current));
1055
+ });
1056
+ const data = usePropsSelector(
1057
+ "root",
1058
+ {
1059
+ comparator: shallowEqual$1,
1060
+ propsStore: handler.propsStore,
1061
+ selector: (current) => ({
1062
+ children: current.children ?? [],
1063
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
1064
+ forceUpdate: current.forceUpdate
1065
+ })
1066
+ }
1067
+ );
1068
+ const keyHandlerId = React.useMemo(() => `keyHandler${uniqueId()}`, []);
1069
+ const keyHandlerRef = React.useRef(null);
1070
+ const focusOnNode = React.useCallback(
1071
+ (key, retry = 3) => {
1072
+ if (keyHandlerRef.current) {
1073
+ const focusElement = keyHandlerRef.current.querySelector(
1074
+ `[data-key="${key}"]`
1075
+ );
1076
+ keyHandlerRef.current?.focus();
1077
+ if (focusElement) {
1078
+ const actualFocusElement = (treeProps.focusGetter ?? ((liElement) => liElement.querySelector(
1079
+ ":scope > .tree__nodeItemLabel"
1080
+ )))(focusElement);
1081
+ const nodeProps = handler.propsStore.getFieldProps(key);
1082
+ if (!treeProps.focusGetter || !nodeProps.labelRenderer)
1083
+ actualFocusElement.classList.add("focused");
1084
+ actualFocusElement.scrollIntoView({
1085
+ inline: "nearest",
1086
+ block: "nearest"
1087
+ });
1088
+ } else if (retry > 0)
1089
+ setTimeout(() => focusOnNode(key, retry - 1), 30);
1090
+ }
1091
+ },
1092
+ [handler.propsStore, treeProps.focusGetter]
1093
+ );
1094
+ React.useEffect(() => {
1095
+ const unsuscribe1 = handler.on("mustFocus", (node) => focusOnNode(node));
1096
+ const unsuscribe2 = handler.on(
1097
+ "onArrowUpOnFirstElement",
1098
+ () => treeProps.onArrowUpOnFirstElement?.()
1099
+ );
1100
+ return () => {
1101
+ unsuscribe1();
1102
+ unsuscribe2();
1103
+ };
1104
+ }, [focusOnNode, handler, treeProps]);
1105
+ return {
1106
+ data,
1107
+ handler,
1108
+ keyHandler: {
1109
+ id: keyHandlerId,
1110
+ onKeyDown: React.useCallback(
1111
+ (ev) => {
1112
+ if (ev.key === "Enter") {
1113
+ const key = handler.state.focusedNode;
1114
+ if (key) {
1115
+ const nodeProps = handler.propsStore.getFieldProps(key);
1116
+ treeProps.onNodeClick?.(ev, nodeProps);
1117
+ }
1118
+ } else {
1119
+ handler.handleKey(ev);
1120
+ }
1121
+ },
1122
+ [handler, treeProps]
1123
+ ),
1124
+ onMouseDown: React.useCallback(
1125
+ (ev) => {
1126
+ const previousFocused = handler.state.focusedNode;
1127
+ if (previousFocused !== null && ev.shiftKey && handler.configuration.current?.isMultiple)
1128
+ ev.preventDefault();
1129
+ },
1130
+ [handler.configuration, handler.state.focusedNode]
1131
+ ),
1132
+ onClick: React.useCallback(
1133
+ (ev) => {
1134
+ if (getSpecificParent(
1135
+ ev.target,
1136
+ (current) => current.classList.contains("tree__expandIcon")
1137
+ ))
1138
+ return;
1139
+ const node = getSpecificParent(
1140
+ ev.target,
1141
+ (current) => !!current.getAttribute("data-key")
1142
+ );
1143
+ if (node) {
1144
+ const previousFocused = handler.state.focusedNode;
1145
+ const key = node.getAttribute("data-key");
1146
+ const nodeProps = handler.propsStore.getFieldProps(key);
1147
+ treeProps.onNodeClick?.(ev, nodeProps);
1148
+ if (previousFocused !== null && ev.shiftKey && handler.configuration.current?.isMultiple) {
1149
+ selectAllNodesBetweenTwoNodes(
1150
+ handler,
1151
+ previousFocused,
1152
+ key,
1153
+ true
1154
+ );
1155
+ } else {
1156
+ handler.setFocusedNode(key);
1157
+ handler.setSelectedNodesByClickEvent(ev);
1158
+ }
1159
+ if (key)
1160
+ focusOnNode(key);
1161
+ }
1162
+ },
1163
+ [focusOnNode, handler, treeProps]
1164
+ ),
1165
+ ref: keyHandlerRef
1166
+ }
1167
+ };
1168
+ }
1169
+
1170
+ const Tree = (props) => {
1171
+ const { data, handler, keyHandler } = useTreeData({
1172
+ name: props.name,
1173
+ treeProps: { ...props }
1174
+ });
1175
+ const [isLoading, setIsLoading] = React.useState(false);
1176
+ const [currentSearchString, setCurrentSearchString] = React.useState("");
1177
+ const isTreeLoading = useTreeSelector(handler, {
1178
+ selector: (current) => current.isLoading
1179
+ });
1180
+ React.useEffect(() => {
1181
+ setIsLoading(isTreeLoading);
1182
+ }, [isTreeLoading]);
1183
+ const search = useDebounceFn(
1184
+ () => {
1185
+ if (props.filterString === void 0)
1186
+ return;
1187
+ setIsLoading(true);
1188
+ React.startTransition(() => {
1189
+ setCurrentSearchString(props.filterString);
1190
+ });
1191
+ if (props.filterString !== void 0 && props.filterString !== "") {
1192
+ unfilterNodes(handler);
1193
+ matchNodesAgainstString(handler, props.filterString);
1194
+ handler.forceEmitUpdate();
1195
+ } else {
1196
+ unfilterNodes(handler);
1197
+ handler.forceEmitUpdate();
1198
+ }
1199
+ setTimeout(() => setIsLoading(false), 0);
1200
+ },
1201
+ { wait: 200 }
1202
+ );
1203
+ React.useEffect(() => {
1204
+ search.run();
1205
+ if (!props.filterString)
1206
+ search.flush();
1207
+ }, [props.filterString]);
1208
+ usePropsSelector(
1209
+ handler.stateKey,
1210
+ {
1211
+ propsStore: handler.propsStore,
1212
+ selector: (current) => current,
1213
+ comparator: (prevProps, newProps) => {
1214
+ if (props.onSelect && !shallowEqual$1(prevProps?.selectedNodes, newProps?.selectedNodes)) {
1215
+ props.onSelect(
1216
+ (newProps?.selectedNodes ?? []).map(
1217
+ (currentId) => handler.propsStore.getFieldProps(currentId)
1218
+ )
1219
+ );
1220
+ }
1221
+ return true;
1222
+ }
1223
+ }
1224
+ );
1225
+ const { focusedNode } = usePropsSelector(handler.stateKey, {
1226
+ propsStore: handler.propsStore,
1227
+ selector: (current) => ({
1228
+ focusedNode: current.focusedNode
1229
+ })
1230
+ });
1231
+ useMount$1(() => {
1232
+ if (props.getHandler)
1233
+ props.getHandler(handler);
1234
+ });
1235
+ useUnmount(() => handler.destructor());
1236
+ const contextValue = React.useMemo(() => {
1237
+ const actualValue = {
1238
+ handler,
1239
+ name: props.name,
1240
+ forceUpdate: data.forceUpdate,
1241
+ focusGetter: props.focusGetter,
1242
+ treeProps: props
1243
+ };
1244
+ return actualValue;
1245
+ }, [handler, props, data.forceUpdate]);
1246
+ return /* @__PURE__ */ jsx(TreeContextProvider, { value: contextValue, children: /* @__PURE__ */ jsxs(
1247
+ Box,
1248
+ {
1249
+ className: `tree ${props.className ?? ""}`,
1250
+ ...getVariant(props.variant ?? "layout.common.trees.primary"),
1251
+ children: [
1252
+ /* @__PURE__ */ jsx(
1253
+ SearchLabel$1,
1254
+ {
1255
+ isLoading: props.isLoading ?? isLoading,
1256
+ onDelete: props.onDeleteFilterString,
1257
+ searchString: props.hideSearchLabel ? "" : currentSearchString
1258
+ }
1259
+ ),
1260
+ /* @__PURE__ */ jsx(
1261
+ TreeItemChildren$1,
1262
+ {
1263
+ "aria-activedescendant": focusedNode !== null ? getActiveDescendantName(props.name, focusedNode) : void 0,
1264
+ "aria-label": props.label,
1265
+ as: "ul",
1266
+ forceUpdate: data.forceUpdate,
1267
+ level: -1,
1268
+ nodes: React.useMemo(
1269
+ () => data.children.map(
1270
+ (current) => handler.propsStore.getFieldProps(current)
1271
+ ),
1272
+ [data.children, handler.propsStore]
1273
+ ),
1274
+ role: "tree",
1275
+ tabIndex: 0,
1276
+ ...keyHandler
1277
+ }
1278
+ )
1279
+ ]
1280
+ }
1281
+ ) });
1282
+ };
1283
+ var Tree$1 = memo(Tree);
1284
+
1285
+ var __defProp$1 = Object.defineProperty;
1286
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1287
+ var __publicField$1 = (obj, key, value) => {
1288
+ __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1289
+ return value;
1290
+ };
1291
+ var __accessCheck$1 = (obj, member, msg) => {
1292
+ if (!member.has(obj))
1293
+ throw TypeError("Cannot " + msg);
1294
+ };
1295
+ var __privateGet$1 = (obj, member, getter) => {
1296
+ __accessCheck$1(obj, member, "read from private field");
1297
+ return getter ? getter.call(obj) : member.get(obj);
1298
+ };
1299
+ var __privateAdd$1 = (obj, member, value) => {
1300
+ if (member.has(obj))
1301
+ throw TypeError("Cannot add the same private member more than once");
1302
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1303
+ };
1304
+ var _getByAbsoluteId;
1305
+ function isOOTreeNode(element) {
1306
+ return typeof element.attachToTree === "function";
1307
+ }
1308
+ const _OOTreeNode = class _OOTreeNode {
1309
+ constructor(tree, parent, props) {
1310
+ this.tree = tree;
1311
+ this.parent = parent;
1312
+ this.props = props;
1313
+ __publicField$1(this, "id", "");
1314
+ __publicField$1(this, "prefix", "");
1315
+ __privateAdd$1(this, _getByAbsoluteId, (id) => {
1316
+ const props = this.tree.getController().propsStore.getFieldProps(id);
1317
+ if (props === void 0)
1318
+ return void 0;
1319
+ return new _OOTreeNode(this.tree, props, props);
1320
+ });
1321
+ if (parent) {
1322
+ if (isOOTreeNode(parent)) {
1323
+ Object.assign(
1324
+ this,
1325
+ this.tree.calculateNodePrefix(props.id, parent?.prefix)
1326
+ );
1327
+ } else {
1328
+ this.prefix = props.nodeProps.__prefix;
1329
+ this.id = String(props.id);
1330
+ }
1331
+ }
1332
+ }
1333
+ attachToTree() {
1334
+ const parentId = this.parent?.id ?? void 0;
1335
+ this.tree.getController().append({
1336
+ ...this.props,
1337
+ id: this.id,
1338
+ label: this.props.label,
1339
+ parentId,
1340
+ nodeProps: {
1341
+ ...this.props.nodeProps,
1342
+ __prefix: this.prefix,
1343
+ __id: this.props.id
1344
+ }
1345
+ });
1346
+ }
1347
+ append(props) {
1348
+ const newNode = new _OOTreeNode(this.tree, this, props);
1349
+ newNode.attachToTree();
1350
+ return newNode;
1351
+ }
1352
+ collapse() {
1353
+ this.tree.getController().setExpandedNodes(
1354
+ this.tree.getController().state.expandedNodes.filter(
1355
+ (current) => current !== this.getProps().id
1356
+ )
1357
+ );
1358
+ }
1359
+ expand() {
1360
+ this.tree.getController().setExpandedNodes([
1361
+ ...this.tree.getController().state.expandedNodes,
1362
+ this.getProps().id
1363
+ ]);
1364
+ this.parent.expand?.();
1365
+ }
1366
+ focus(expand) {
1367
+ this.tree.getController().setFocusedNode(this.getProps().id);
1368
+ if (expand) {
1369
+ this.expand();
1370
+ }
1371
+ }
1372
+ getAll() {
1373
+ return this.tree.getController().propsStore.getFieldProps(this.id).children.map((current) => {
1374
+ return () => {
1375
+ return __privateGet$1(this, _getByAbsoluteId).call(this, String(current));
1376
+ };
1377
+ });
1378
+ }
1379
+ getById(id) {
1380
+ const { id: actualId } = this.tree.calculateNodePrefix(id, this.prefix);
1381
+ const props = this.tree.getController().propsStore.getFieldProps(actualId);
1382
+ if (props === void 0)
1383
+ return void 0;
1384
+ return new _OOTreeNode(this.tree, props, props);
1385
+ }
1386
+ getOriginalId() {
1387
+ return this.getProps().nodeProps.__id;
1388
+ }
1389
+ getParent() {
1390
+ if (!this.parent)
1391
+ return null;
1392
+ const props = this.tree.getController().propsStore.getFieldProps(this.parent.id);
1393
+ const parent = props.parentId;
1394
+ const parentProps = this.tree.getController().propsStore.getFieldProps(parent);
1395
+ return new _OOTreeNode(this.tree, parentProps, props);
1396
+ }
1397
+ getProps() {
1398
+ return this.tree.getController().propsStore.getFieldProps(this.id);
1399
+ }
1400
+ remove() {
1401
+ this.tree.getController().remove(this.id, true);
1402
+ }
1403
+ sort(sortFunction) {
1404
+ const children = this.getProps().children.map(
1405
+ (current) => this.tree.getController().propsStore.getFieldProps(current)
1406
+ ).sort((a, b) => sortFunction(a, b)).map((current) => current.id);
1407
+ this.tree.getController().propsStore.updateField(this.id, { children });
1408
+ }
1409
+ update(props) {
1410
+ this.tree.getController().propsStore.updateField(this.id, props);
1411
+ }
1412
+ };
1413
+ _getByAbsoluteId = new WeakMap();
1414
+ let OOTreeNode = _OOTreeNode;
1415
+
1416
+ class OOTreeChildren extends OOTreeNode {
1417
+ constructor(tree) {
1418
+ super(tree, null, {});
1419
+ this.tree = tree;
1420
+ this.prefix = "c";
1421
+ }
1422
+ /**
1423
+ * Se sobreescribe para evitar problemas
1424
+ */
1425
+ expand() {
1426
+ }
1427
+ }
1428
+
1429
+ var __defProp = Object.defineProperty;
1430
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1431
+ var __publicField = (obj, key, value) => {
1432
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1433
+ return value;
1434
+ };
1435
+ var __accessCheck = (obj, member, msg) => {
1436
+ if (!member.has(obj))
1437
+ throw TypeError("Cannot " + msg);
1438
+ };
1439
+ var __privateGet = (obj, member, getter) => {
1440
+ __accessCheck(obj, member, "read from private field");
1441
+ return getter ? getter.call(obj) : member.get(obj);
1442
+ };
1443
+ var __privateAdd = (obj, member, value) => {
1444
+ if (member.has(obj))
1445
+ throw TypeError("Cannot add the same private member more than once");
1446
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1447
+ };
1448
+ var __privateSet = (obj, member, value, setter) => {
1449
+ __accessCheck(obj, member, "write to private field");
1450
+ setter ? setter.call(obj, value) : member.set(obj, value);
1451
+ return value;
1452
+ };
1453
+ var _children, _controller, _makeHandler;
1454
+ class OOTree extends EventEmitter {
1455
+ constructor(props) {
1456
+ super();
1457
+ this.props = props;
1458
+ /**
1459
+ * Accessors
1460
+ */
1461
+ __privateAdd(this, _children, null);
1462
+ __privateAdd(this, _controller, {});
1463
+ __publicField(this, "getChildren", () => {
1464
+ if (__privateGet(this, _children) === null)
1465
+ throw new Error(
1466
+ "Children was not assigned yet, maybe you forgot to listen to onReady event on OOTree component."
1467
+ );
1468
+ return __privateGet(this, _children);
1469
+ });
1470
+ __publicField(this, "getController", () => {
1471
+ if (__privateGet(this, _controller) === null)
1472
+ throw new Error(
1473
+ "Controller was not assigned yet, maybe you forgot to listen to onReady event on OOTree component."
1474
+ );
1475
+ const c = this;
1476
+ return new Proxy(__privateGet(this, _controller), {
1477
+ get(target, prop) {
1478
+ if (prop === "removeAll") {
1479
+ return () => {
1480
+ c.prefixesMap = {};
1481
+ __privateGet(c, _controller)?.removeAll();
1482
+ };
1483
+ }
1484
+ return target[prop];
1485
+ }
1486
+ });
1487
+ });
1488
+ /**
1489
+ * Component
1490
+ */
1491
+ __privateAdd(this, _makeHandler, () => {
1492
+ __privateSet(this, _controller, new TreeDataController$1(this.props.name, {
1493
+ current: this.props
1494
+ }));
1495
+ __privateSet(this, _children, new OOTreeChildren(this));
1496
+ });
1497
+ __publicField(this, "Component", (props) => {
1498
+ return /* @__PURE__ */ jsx(
1499
+ Tree$1,
1500
+ {
1501
+ controller: __privateGet(this, _controller),
1502
+ ...this.props,
1503
+ ...props
1504
+ }
1505
+ );
1506
+ });
1507
+ /**
1508
+ * Data manipulation
1509
+ */
1510
+ __publicField(this, "prefixJoinCharacter", "$$");
1511
+ __publicField(this, "prefixWithIdJoinCharacter", "__");
1512
+ __publicField(this, "prefixesMap", {});
1513
+ __publicField(this, "calculateDepth", (id) => {
1514
+ return [...String(id).matchAll(/\$\$/g)].length;
1515
+ });
1516
+ /**
1517
+ * Calcula el prefijo de un nodo que se está agregando al árbol evitando
1518
+ * colisiones. Ese prefijo estará asociado a cada nodo y se utilizará para dar
1519
+ * nombres únicos a sus hijos, no para el nodo en sí. Entonces, cada vez que
1520
+ * se construye un nodo, se le asigna un prefijo considerando el prefijo de su
1521
+ * padre, pero su nombre se hace a partir del prefijo del padre y su id.
1522
+ */
1523
+ __publicField(this, "calculateNodePrefix", (id, prefix) => {
1524
+ const expectedPrefix = [prefix, String(id).charAt(0)].filter(Boolean).join(this.prefixJoinCharacter);
1525
+ let i = 1;
1526
+ let actualPrefix = expectedPrefix;
1527
+ while (this.prefixesMap[actualPrefix] !== void 0) {
1528
+ actualPrefix = `${expectedPrefix}${i++}`;
1529
+ }
1530
+ this.prefixesMap[actualPrefix] = {
1531
+ nodeId: id,
1532
+ prefix: actualPrefix
1533
+ };
1534
+ const data = {
1535
+ prefix: actualPrefix,
1536
+ id: [[prefix].filter(Boolean).join(this.prefixJoinCharacter), id].filter(Boolean).join(this.prefixWithIdJoinCharacter)
1537
+ };
1538
+ return data;
1539
+ });
1540
+ __privateGet(this, _makeHandler).call(this);
1541
+ }
1542
+ }
1543
+ _children = new WeakMap();
1544
+ _controller = new WeakMap();
1545
+ _makeHandler = new WeakMap();
1546
+
1547
+ export { OOTree, OOTreeNode, Tree$1 as Tree, TreeDataController$1 as TreeDataController, isOOTreeNode, useTreeContext, useTreeData, useTreeDataController, useTreeSelector, useTreeSelectorByName };
7
1548
  //# sourceMappingURL=index.js.map