@refinitiv-ui/elements 7.10.10-next.1 → 7.11.0

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.
@@ -1,3 +1,5 @@
1
+ import { CollectionComposer } from '@refinitiv-ui/utils/collection.js';
2
+ import { TreeNode } from './tree-node.js';
1
3
  export var CheckedState;
2
4
  (function (CheckedState) {
3
5
  CheckedState[CheckedState["CHECKED"] = 1] = "CHECKED";
@@ -17,17 +19,61 @@ export var TreeManagerMode;
17
19
  })(TreeManagerMode || (TreeManagerMode = {}));
18
20
  export class TreeManager {
19
21
  /**
20
- * @param composer CollectionComposer to be managed.
21
- * @param mode TreeManager mode which is Relational or Independent.
22
+ * Collection composer used for managing the data
22
23
  */
23
- constructor(composer, mode = TreeManagerMode.RELATIONAL) {
24
+ get composer() {
25
+ return this._composer;
26
+ }
27
+ /**
28
+ * Most of the time, there is no need to create a new instance of Tree Manager manually.
29
+ * Use the existing instance in components instead.
30
+ * @param input Items or CollectionComposer to be managed.
31
+ * @param mode A mode describing how items are managed either relationally or independently.
32
+ */
33
+ constructor(input, mode = TreeManagerMode.RELATIONAL) {
24
34
  /**
25
35
  * Mode (algorithm) the tree manage is using
26
36
  */
27
37
  this.mode = TreeManagerMode.RELATIONAL;
28
- this.composer = composer;
38
+ /** Cache map of TreeNode improving performance */
39
+ this.treeNodeCache = new Map();
40
+ this._composer = input instanceof CollectionComposer ? input : new CollectionComposer(input);
29
41
  this.mode = mode;
30
42
  }
43
+ /**
44
+ * Returns all items as an array of `TreeNode`.
45
+ * @returns Array of `TreeNode` representing all items
46
+ */
47
+ getTreeNodes() {
48
+ const result = [];
49
+ for (const item of this.items) {
50
+ let treeNode = this.treeNodeCache.get(item);
51
+ if (!treeNode) {
52
+ treeNode = new TreeNode(item, this);
53
+ this.treeNodeCache.set(item, treeNode);
54
+ }
55
+ result.push(treeNode);
56
+ }
57
+ return result;
58
+ }
59
+ /**
60
+ * Returns a `TreeNode` of the original data item.
61
+ * If the item doesn't exist, returns `null`.
62
+ * @param item Original data item
63
+ * @returns `TreeNode` of the original data item or `null`
64
+ */
65
+ getTreeNode(item) {
66
+ let treeNode = this.treeNodeCache.get(item);
67
+ if (!treeNode) {
68
+ const existingItems = this._composer.queryItems((_item) => item === _item, Infinity);
69
+ if (existingItems.length === 0) {
70
+ return null;
71
+ }
72
+ treeNode = new TreeNode(item, this);
73
+ this.treeNodeCache.set(item, treeNode);
74
+ }
75
+ return treeNode;
76
+ }
31
77
  /**
32
78
  * Is the manager maintaining parent/child relationships
33
79
  */
@@ -38,20 +84,20 @@ export class TreeManager {
38
84
  * Returns all items in the tree
39
85
  */
40
86
  get items() {
41
- return this.composer.queryItems(() => true, Infinity);
87
+ return this._composer.queryItems(() => true, Infinity);
42
88
  }
43
89
  /**
44
- * Return all items which have children
90
+ * Returns all items with children
45
91
  */
46
92
  get parentItems() {
47
93
  return this.items.filter((item) => this.isItemParent(item));
48
94
  }
49
95
  /**
50
- * Returns all checked items.
96
+ * Returns all selected items.
51
97
  * When managing relationships, this excludes groups/parents from the result.
52
98
  */
53
99
  get checkedItems() {
54
- const items = this.composer.queryItems((item) => {
100
+ const items = this._composer.queryItems((item) => {
55
101
  if (this.manageRelationships && this.isItemParent(item)) {
56
102
  return false;
57
103
  }
@@ -65,17 +111,17 @@ export class TreeManager {
65
111
  get orderBySelectedAt() {
66
112
  // Order by sequential selected timestamp
67
113
  return (itemA, itemB) => {
68
- const timeA = this.composer.getItemPropertyValue(itemA, 'selectedAt') ?? 0;
69
- const timeB = this.composer.getItemPropertyValue(itemB, 'selectedAt') ?? 0;
114
+ const timeA = this._composer.getItemPropertyValue(itemA, 'selectedAt') ?? 0;
115
+ const timeB = this._composer.getItemPropertyValue(itemB, 'selectedAt') ?? 0;
70
116
  return timeA - timeB;
71
117
  };
72
118
  }
73
119
  /**
74
- * Items which should be visibly displayed.
75
- * This can be used to render items.
120
+ * Returns items which their selected state can be changed.
121
+ * Hidden, disabled or readonly items are not included.
76
122
  */
77
123
  get editableItems() {
78
- const topLevel = this.composer.queryItems(() => true, 0);
124
+ const topLevel = this._composer.queryItems(() => true, 0);
79
125
  return this.getEditableItems(topLevel);
80
126
  }
81
127
  /**
@@ -95,11 +141,11 @@ export class TreeManager {
95
141
  return result;
96
142
  }
97
143
  /**
98
- * Items which should be visibly displayed.
99
- * This can be used to render items.
144
+ * Returns currently displayed items.
145
+ * Hidden and children of unexpanded items are not included.
100
146
  */
101
147
  get visibleItems() {
102
- const topLevel = this.composer.queryItems(() => true, 0);
148
+ const topLevel = this._composer.queryItems(() => true, 0);
103
149
  return this.getVisibleItems(topLevel);
104
150
  }
105
151
  /**
@@ -123,7 +169,7 @@ export class TreeManager {
123
169
  * @returns `True` if the item is hidden
124
170
  */
125
171
  isItemHidden(item) {
126
- return this.composer.getItemPropertyValue(item, 'hidden') === true;
172
+ return this._composer.getItemPropertyValue(item, 'hidden') === true;
127
173
  }
128
174
  /**
129
175
  * Is the item checked?
@@ -134,7 +180,7 @@ export class TreeManager {
134
180
  if (this.manageRelationships && this.isItemParent(item)) {
135
181
  return !this.getItemChildren(item).some((child) => !this.isItemChecked(child));
136
182
  }
137
- return this.composer.getItemPropertyValue(item, 'selected') === true;
183
+ return this._composer.getItemPropertyValue(item, 'selected') === true;
138
184
  }
139
185
  /**
140
186
  * Is the item checked indeterminately?
@@ -156,7 +202,7 @@ export class TreeManager {
156
202
  if (this.manageRelationships && this.isItemParent(item)) {
157
203
  return this.getItemChildren(item).some((child) => this.canCheckItem(child));
158
204
  }
159
- return this.isItemCheckable(item) && this.composer.getItemPropertyValue(item, 'selected') !== true;
205
+ return this.isItemCheckable(item) && this._composer.getItemPropertyValue(item, 'selected') !== true;
160
206
  }
161
207
  /**
162
208
  * Determines whether the item is checked and can be changed to an unchecked state.
@@ -167,7 +213,7 @@ export class TreeManager {
167
213
  if (this.manageRelationships && this.isItemParent(item)) {
168
214
  return this.getItemChildren(item).some((child) => this.canUncheckItem(child));
169
215
  }
170
- return this.isItemCheckable(item) && this.composer.getItemPropertyValue(item, 'selected') === true;
216
+ return this.isItemCheckable(item) && this._composer.getItemPropertyValue(item, 'selected') === true;
171
217
  }
172
218
  /**
173
219
  * Makes an item visible
@@ -175,7 +221,7 @@ export class TreeManager {
175
221
  * @returns {void}
176
222
  */
177
223
  showItem(item) {
178
- this.composer.setItemPropertyValue(item, 'hidden', false);
224
+ this._composer.setItemPropertyValue(item, 'hidden', false);
179
225
  this.updateItem(item); // Make sure the item is updated
180
226
  }
181
227
  /**
@@ -184,7 +230,7 @@ export class TreeManager {
184
230
  * @returns {void}
185
231
  */
186
232
  hideItem(item) {
187
- this.composer.setItemPropertyValue(item, 'hidden', true);
233
+ this._composer.setItemPropertyValue(item, 'hidden', true);
188
234
  }
189
235
  /**
190
236
  * Forces a modification event, so that the renderer can update.
@@ -193,10 +239,12 @@ export class TreeManager {
193
239
  */
194
240
  forceUpdateOnPath(item) {
195
241
  const path = [...this.getItemAncestors(item), item];
196
- path.forEach((item) => this.composer.updateItemTimestamp(item));
242
+ path.forEach((item) => this._composer.updateItemTimestamp(item));
197
243
  }
198
244
  /**
245
+ * TODO: find a way to keep `noRelation` of Tree & Tree Select component in-sync
199
246
  * Sets the mode (algorithm) the manager should use
247
+ * @hidden Mode updating doesn't sync back up Tree component.
200
248
  * @param mode Tree manager mode
201
249
  * @returns {void}
202
250
  */
@@ -206,70 +254,73 @@ export class TreeManager {
206
254
  this.parentItems.forEach((item) => this.updateItem(item));
207
255
  }
208
256
  /**
209
- * Updates the data item, forcing a render
257
+ * Requests the item to be rerendered manually.
258
+ * Typically, this is not required. The render is triggered automatically when item's properties are updated.
210
259
  * @param item Original data item
211
260
  * @returns {void}
212
261
  */
213
262
  updateItem(item) {
214
- this.composer.updateItemTimestamp(item);
263
+ this._composer.updateItemTimestamp(item);
215
264
  }
216
265
  /**
217
- * Includes an item as part of the tree.
218
- * @param item Item to include
266
+ * Shows the item.
267
+ * @hidden `hidden` usage in filterItems of Tree & Tree Select component conflicts with this API
268
+ * @param item Original data item
219
269
  * @returns `True` if the item is newly included
220
270
  */
221
271
  includeItem(item) {
222
- const result = this.composer.unlockItem(item);
272
+ const result = this._composer.unlockItem(item);
223
273
  this.showItem(item); // Item must be unlocked first
224
274
  return result;
225
275
  }
226
276
  /**
227
- * Excludes an item as part of the tree.
228
- * @param item Item to exclude
277
+ * Hides the item.
278
+ * @hidden `hidden` usage in filterItems of Tree & Tree Select component conflicts with this API
279
+ * @param item Original data item
229
280
  * @returns `True` if the item is newly excluded
230
281
  */
231
282
  excludeItem(item) {
232
283
  this.hideItem(item);
233
- return this.composer.lockItem(item);
284
+ return this._composer.lockItem(item);
234
285
  }
235
286
  /**
236
- * Is the item checkable?
287
+ * Returns whether the selected state of item can be changed or not.
237
288
  * @param item Original data item
238
289
  * @returns `True` if the item is not disabled or readonly
239
290
  */
240
291
  isItemCheckable(item) {
241
- return (!this.composer.isItemLocked(item) &&
242
- this.composer.getItemPropertyValue(item, 'disabled') !== true &&
243
- this.composer.getItemPropertyValue(item, 'readonly') !== true);
292
+ return (!this._composer.isItemLocked(item) &&
293
+ this._composer.getItemPropertyValue(item, 'disabled') !== true &&
294
+ this._composer.getItemPropertyValue(item, 'readonly') !== true);
244
295
  }
245
296
  /**
246
- * Is the item expanded?
297
+ * Returns the current expanded state of the item.
247
298
  * @param item Original data item
248
- * @returns `True` if the item is expanded and its children should be visible
299
+ * @returns `True` if the item is currently expanded so its children are visible.
249
300
  */
250
301
  isItemExpanded(item) {
251
- return this.isItemParent(item) && this.composer.getItemPropertyValue(item, 'expanded') === true;
302
+ return this.isItemParent(item) && this._composer.getItemPropertyValue(item, 'expanded') === true;
252
303
  }
253
304
  /**
254
- * Is the item a parent?
305
+ * Returns whether the item contains any children or not.
255
306
  * @param item Original data item
256
307
  * @returns `True` if the item has children
257
308
  */
258
309
  isItemParent(item) {
259
- return this.composer.isItemParent(item);
310
+ return this._composer.isItemParent(item);
260
311
  }
261
312
  /**
262
- * Is the item a child?
313
+ * Returns whether the item has a parent or not.
263
314
  * @param item Original data item
264
315
  * @returns `True` if the item has a parent
265
316
  */
266
317
  isItemChild(item) {
267
- return this.composer.isItemChild(item);
318
+ return this._composer.isItemChild(item);
268
319
  }
269
320
  /**
270
- * Calculates the checked stated of the item
321
+ * Return checked state of the item.
271
322
  * @param item Original data item
272
- * @returns Checked state of the item
323
+ * @returns item checked state: CHECKED (1), UNCHECKED (0), INDETERMINATE (-1)
273
324
  */
274
325
  getItemCheckedState(item) {
275
326
  if (this.isItemChecked(item)) {
@@ -281,74 +332,74 @@ export class TreeManager {
281
332
  return CheckedState.UNCHECKED;
282
333
  }
283
334
  /**
284
- * Gets an item's ancestors
335
+ * Returns all ancestors of the item.
285
336
  * @param item Original data item
286
- * @returns A list of ancestors
337
+ * @returns An array of ancestors
287
338
  */
288
339
  getItemAncestors(item) {
289
- return this.composer.getItemAncestors(item);
340
+ return this._composer.getItemAncestors(item);
290
341
  }
291
342
  /**
292
- * Gets an item's descendants
343
+ * Returns all descendants of the item.
293
344
  * @param item Original data item
294
- * @param depth Depth to retrieve
295
- * @returns A list of descendants
345
+ * @param depth Depth of descendants to get. If it's `undefined`, get all descendants.
346
+ * @returns An array of descendants
296
347
  */
297
348
  getItemDescendants(item, depth) {
298
- return this.composer.getItemDescendants(item, depth);
349
+ return this._composer.getItemDescendants(item, depth);
299
350
  }
300
351
  /**
301
- * Gets an item's parent, if it has one.
352
+ * Returns the parent of the item, if it has one.
302
353
  * @param item Original data item
303
354
  * @returns Item parent or `null`
304
355
  */
305
356
  getItemParent(item) {
306
- return this.composer.getItemParent(item);
357
+ return this._composer.getItemParent(item);
307
358
  }
308
359
  /**
309
- * Gets an item's child collection
360
+ * Returns the children of the item as an array.
310
361
  * @param item Original data item
311
- * @returns A list of children
362
+ * @returns An array of children
312
363
  */
313
364
  getItemChildren(item) {
314
- return this.composer.getItemChildren(item);
365
+ return this._composer.getItemChildren(item);
315
366
  }
316
367
  /**
317
- * Expand an item to show its children
368
+ * Expands the item to show its children.
318
369
  * @param item Original data item
319
370
  * @returns {void}
320
371
  */
321
372
  expandItem(item) {
322
373
  if (this.isItemParent(item)) {
323
- this.composer.setItemPropertyValue(item, 'expanded', true);
374
+ this._composer.setItemPropertyValue(item, 'expanded', true);
324
375
  }
325
376
  }
326
377
  /**
327
- * Collapse an item to hide its children
378
+ * Collapses the item to hide its children.
328
379
  * @param item Original data item
329
380
  * @returns {void}
330
381
  */
331
382
  collapseItem(item) {
332
383
  if (this.isItemParent(item)) {
333
- this.composer.setItemPropertyValue(item, 'expanded', false);
384
+ this._composer.setItemPropertyValue(item, 'expanded', false);
334
385
  }
335
386
  }
336
387
  /**
337
- * Expands all items in the tree
388
+ * Expands all items.
338
389
  * @returns {void}
339
390
  */
340
391
  expandAllItems() {
341
392
  this.parentItems.forEach((item) => this.expandItem(item));
342
393
  }
343
394
  /**
344
- * Collapses all items in the tree
395
+ * Collapses all items.
345
396
  * @returns {void}
346
397
  */
347
398
  collapseAllItems() {
348
399
  this.parentItems.forEach((item) => this.collapseItem(item));
349
400
  }
350
401
  /**
351
- * Checks the item
402
+ * Selects the item.
352
403
  * @param item Original data item
353
404
  * @returns `True` if the item is modified
354
405
  */
@@ -362,8 +413,8 @@ export class TreeManager {
362
413
  this.lastSelectedAt =
363
414
  this.lastSelectedAt && this.lastSelectedAt >= timestamp ? this.lastSelectedAt + 1 : timestamp;
364
415
  // Set item selected with timestamp
365
- this.composer.setItemPropertyValue(item, 'selected', true);
366
- this.composer.setItemPropertyValue(item, 'selectedAt', this.lastSelectedAt);
416
+ this._composer.setItemPropertyValue(item, 'selected', true);
417
+ this._composer.setItemPropertyValue(item, 'selectedAt', this.lastSelectedAt);
367
418
  if (manageRelationships) {
368
419
  this.forceUpdateOnPath(item);
369
420
  this.getItemDescendants(item).forEach((descendant) => this._checkItem(descendant, false));
@@ -373,7 +424,7 @@ export class TreeManager {
373
424
  return false;
374
425
  }
375
426
  /**
376
- * Unchecks the item
427
+ * Deselects the item.
377
428
  * @param item Original data item
378
429
  * @returns `True` if the item is modified
379
430
  */
@@ -382,7 +433,7 @@ export class TreeManager {
382
433
  }
383
434
  _uncheckItem(item, manageRelationships = this.manageRelationships) {
384
435
  if (this.canUncheckItem(item)) {
385
- this.composer.setItemPropertyValue(item, 'selected', false);
436
+ this._composer.setItemPropertyValue(item, 'selected', false);
386
437
  if (manageRelationships) {
387
438
  this.forceUpdateOnPath(item);
388
439
  this.getItemDescendants(item).forEach((descendant) => this._uncheckItem(descendant, false));
@@ -393,27 +444,27 @@ export class TreeManager {
393
444
  return false;
394
445
  }
395
446
  /**
396
- * Toggles the item checked state
447
+ * Toggle the selected state of the item.
397
448
  * @param item Original data item
398
- * @returns `True` if the item is modified
449
+ * @returns `true` if the item is modified successfully.
399
450
  */
400
451
  toggleItem(item) {
401
452
  return this.checkItem(item) || this.uncheckItem(item);
402
453
  }
403
454
  /**
404
- * Checks all items
455
+ * Selects all items.
405
456
  * @returns {void}
406
457
  */
407
458
  checkAllItems() {
408
459
  this.editableItems.forEach((item) => this.checkItem(item));
409
460
  }
410
461
  /**
411
- * Unchecks all items
462
+ * Deselects all items.
412
463
  * @returns {void}
413
464
  */
414
465
  uncheckAllItems() {
415
466
  // uncheck items from top levels when manage relationships to avoid redundant re-renders
416
- const items = this.manageRelationships ? this.composer.queryItems(() => true, 0) : this.checkedItems;
467
+ const items = this.manageRelationships ? this._composer.queryItems(() => true, 0) : this.checkedItems;
417
468
  items.forEach((item) => this.uncheckItem(item));
418
469
  }
419
470
  }
@@ -0,0 +1,141 @@
1
+ import type { TreeDataItem } from '../helpers/types';
2
+ import { CheckedState, TreeManager } from './tree-manager.js';
3
+ /**
4
+ * `TreeNode` is expected to be used with `TreeManager` in tandem with Tree & Tree Select components.
5
+ * Accordingly, only accessors for `TreeDataItem`'s properties are implemented.
6
+ */
7
+ export declare class TreeNode<T extends TreeDataItem = TreeDataItem> {
8
+ /** An item managed by Tree Node */
9
+ protected item: T;
10
+ /** Tree Manager of the item to be managed */
11
+ protected manager: TreeManager<T>;
12
+ /**
13
+ * Create a new Tree Node managing an item & its Tree Manager.
14
+ * @param item item to be managed
15
+ * @param manager `TreeManager` of the item to be managed
16
+ * @hidden this constructor should be used internally & only by `TreeManager`
17
+ */
18
+ constructor(item: T, manager: TreeManager<T>);
19
+ /** Icon to show, when rendering the item. */
20
+ get icon(): string | undefined;
21
+ set icon(icon: string | undefined);
22
+ /** Label to show, when rendering the item. */
23
+ get label(): string;
24
+ set label(value: string);
25
+ /**
26
+ * Value of the data item.
27
+ * This is usually the value which is returned,
28
+ * when the item is selected.
29
+ */
30
+ get value(): string;
31
+ set value(value: string);
32
+ /**
33
+ * Sets the item to be readonly.
34
+ * Read only items cannot be selected by a user.
35
+ */
36
+ get readonly(): boolean;
37
+ set readonly(value: boolean);
38
+ /**
39
+ * Sets the highlight state of the item.
40
+ * This is usually used for navigating over items,
41
+ * without affecting focus or multiple item selection.
42
+ */
43
+ get highlighted(): boolean;
44
+ set highlighted(value: boolean);
45
+ /**
46
+ * Sets the item to be disabled.
47
+ * This completely prevents the item from being interacted with.
48
+ */
49
+ get disabled(): boolean;
50
+ set disabled(value: boolean);
51
+ /**
52
+ * Whether to show or hide the item from the renderer.
53
+ * @readonly
54
+ */
55
+ get hidden(): boolean;
56
+ /** Expanded state of child items. If `true`, child items will be visible. */
57
+ get expanded(): boolean;
58
+ set expanded(value: boolean);
59
+ /**
60
+ * Timestamp indicating the order of sequential selection.
61
+ * @readonly
62
+ */
63
+ get selectedAt(): number | undefined;
64
+ /**
65
+ * Selection state of the item.
66
+ * If its `TreeManager` is in relationship mode, value would be get/set hierarchically.
67
+ * For instance, items with children would be considered selected when all children are selected.
68
+ *
69
+ * For indeterminate state support, use `getCheckedState()` instead.
70
+ */
71
+ get selected(): boolean;
72
+ set selected(value: boolean);
73
+ /**
74
+ * Return checked state of the item.
75
+ * Apart from checked & unchecked state of `selected` accessor,
76
+ * this method supports indeterminate state for items that some but not all of their children are selected.
77
+ * @returns item checked state: CHECKED (1), UNCHECKED (0), INDETERMINATE (-1)
78
+ */
79
+ getCheckedState(): CheckedState;
80
+ /**
81
+ * Returns all ancestors of the item.
82
+ * @returns An array of ancestors as Tree Node
83
+ */
84
+ getAncestors(): TreeNode<T>[];
85
+ /**
86
+ * Returns the children of the item.
87
+ * @returns An array of children as Tree Node
88
+ */
89
+ getChildren(): TreeNode<T>[];
90
+ /**
91
+ * Returns all descendants of the item.
92
+ * @param depth Depth of descendants to get. If it's `undefined`, get all descendants.
93
+ * @returns An array of descendants as Tree Node
94
+ */
95
+ getDescendants(depth?: number): TreeNode<T>[];
96
+ /**
97
+ * Returns the parent of the item, if it has one.
98
+ * @returns Item parent as Tree Node or `null`
99
+ */
100
+ getParent(): TreeNode<T> | null;
101
+ /**
102
+ * Returns whether the selected state of the item can be changed or not.
103
+ * @returns `True` if the item is not disabled or readonly
104
+ */
105
+ isSelectable(): boolean;
106
+ /**
107
+ * Returns whether the item contains any children or not.
108
+ * @returns `True` if the item has children
109
+ */
110
+ isParent(): boolean;
111
+ /**
112
+ * Returns whether the item has a parent or not.
113
+ * @returns `True` if the item has a parent
114
+ */
115
+ isChild(): boolean;
116
+ /**
117
+ * Return the depth of the item starting from 0 for root items,
118
+ * 1 for children of root items, 2 for grandchildren of root items and so on.
119
+ * @returns depth of the item
120
+ */
121
+ getDepth(): number;
122
+ /**
123
+ * Requests the item to be rerendered manually.
124
+ * Typically, this is not required. The render is triggered automatically when item's properties are updated.
125
+ * @returns {void}
126
+ */
127
+ rerender(): void;
128
+ /**
129
+ * Retrieve a value of the specified property.
130
+ * @param prop property key
131
+ * @returns property value
132
+ */
133
+ private getPropertyValue;
134
+ /**
135
+ * Set a value of the specified property.
136
+ * @param prop property key
137
+ * @param value property value
138
+ * @returns {void}
139
+ */
140
+ private setProperty;
141
+ }