@refinitiv-ui/elements 6.0.1 → 6.0.4

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 (151) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/lib/accordion/index.js +2 -2
  3. package/lib/autosuggest/helpers/renderer.d.ts +1 -1
  4. package/lib/autosuggest/helpers/utils.d.ts +1 -1
  5. package/lib/autosuggest/index.d.ts +2 -1
  6. package/lib/autosuggest/themes/halo/dark/index.js +1 -1
  7. package/lib/autosuggest/themes/halo/light/index.js +1 -1
  8. package/lib/button/index.js +2 -1
  9. package/lib/button/themes/halo/dark/index.js +1 -1
  10. package/lib/button/themes/halo/light/index.js +1 -1
  11. package/lib/button/themes/solar/charcoal/index.js +1 -1
  12. package/lib/button/themes/solar/pearl/index.js +1 -1
  13. package/lib/calendar/index.d.ts +1 -1
  14. package/lib/calendar/index.js +6 -5
  15. package/lib/calendar/themes/halo/dark/index.js +1 -1
  16. package/lib/calendar/themes/halo/light/index.js +1 -1
  17. package/lib/calendar/themes/solar/charcoal/index.js +1 -1
  18. package/lib/calendar/themes/solar/pearl/index.js +1 -1
  19. package/lib/checkbox/index.d.ts +8 -15
  20. package/lib/checkbox/index.js +19 -41
  21. package/lib/checkbox/themes/halo/dark/index.js +1 -1
  22. package/lib/checkbox/themes/halo/light/index.js +1 -1
  23. package/lib/checkbox/themes/solar/charcoal/index.js +1 -1
  24. package/lib/checkbox/themes/solar/pearl/index.js +1 -1
  25. package/lib/clock/themes/halo/dark/index.js +1 -1
  26. package/lib/clock/themes/halo/light/index.js +1 -1
  27. package/lib/clock/themes/solar/charcoal/index.js +1 -1
  28. package/lib/clock/themes/solar/pearl/index.js +1 -1
  29. package/lib/combo-box/index.d.ts +4 -3
  30. package/lib/combo-box/index.js +7 -3
  31. package/lib/combo-box/themes/halo/dark/index.js +1 -1
  32. package/lib/combo-box/themes/halo/light/index.js +1 -1
  33. package/lib/combo-box/themes/solar/charcoal/index.js +1 -1
  34. package/lib/combo-box/themes/solar/pearl/index.js +1 -1
  35. package/lib/datetime-field/index.d.ts +1 -1
  36. package/lib/datetime-field/utils.d.ts +1 -1
  37. package/lib/datetime-picker/themes/halo/dark/index.js +1 -1
  38. package/lib/datetime-picker/themes/halo/light/index.js +1 -1
  39. package/lib/datetime-picker/themes/solar/charcoal/index.js +1 -1
  40. package/lib/datetime-picker/themes/solar/pearl/index.js +1 -1
  41. package/lib/email-field/themes/halo/dark/index.js +1 -1
  42. package/lib/email-field/themes/halo/light/index.js +1 -1
  43. package/lib/email-field/themes/solar/charcoal/index.js +1 -1
  44. package/lib/email-field/themes/solar/pearl/index.js +1 -1
  45. package/lib/heatmap/index.d.ts +2 -2
  46. package/lib/heatmap/themes/halo/light/index.js +1 -1
  47. package/lib/index.d.ts +1 -1
  48. package/lib/index.js +1 -1
  49. package/lib/interactive-chart/index.js +4 -3
  50. package/lib/item/helpers/types.d.ts +6 -6
  51. package/lib/item/index.d.ts +2 -2
  52. package/lib/item/index.js +0 -1
  53. package/lib/item/themes/halo/dark/index.js +1 -1
  54. package/lib/item/themes/halo/light/index.js +1 -1
  55. package/lib/list/elements/list-item.d.ts +30 -0
  56. package/lib/list/elements/list-item.js +19 -0
  57. package/lib/list/elements/list.d.ts +307 -0
  58. package/lib/list/elements/list.js +632 -0
  59. package/lib/list/helpers/renderer.d.ts +0 -1
  60. package/lib/list/helpers/renderer.js +1 -3
  61. package/lib/list/index.d.ts +3 -317
  62. package/lib/list/index.js +3 -641
  63. package/lib/list/themes/halo/dark/index.js +4 -1
  64. package/lib/list/themes/halo/light/index.js +5 -2
  65. package/lib/list/themes/solar/charcoal/index.js +4 -1
  66. package/lib/list/themes/solar/pearl/index.js +4 -1
  67. package/lib/multi-input/index.d.ts +1 -5
  68. package/lib/multi-input/index.js +17 -18
  69. package/lib/notification/themes/halo/dark/index.js +1 -1
  70. package/lib/notification/themes/halo/light/index.js +1 -1
  71. package/lib/notification/themes/solar/charcoal/index.js +1 -1
  72. package/lib/notification/themes/solar/pearl/index.js +1 -1
  73. package/lib/number-field/themes/halo/dark/index.js +1 -1
  74. package/lib/number-field/themes/halo/light/index.js +1 -1
  75. package/lib/number-field/themes/solar/charcoal/index.js +1 -1
  76. package/lib/number-field/themes/solar/pearl/index.js +1 -1
  77. package/lib/overlay/index.d.ts +2 -1
  78. package/lib/overlay-menu/helpers/constants.d.ts +6 -0
  79. package/lib/overlay-menu/helpers/constants.js +7 -0
  80. package/lib/overlay-menu/helpers/types.d.ts +0 -6
  81. package/lib/overlay-menu/helpers/types.js +1 -7
  82. package/lib/overlay-menu/index.d.ts +1 -1
  83. package/lib/overlay-menu/index.js +1 -1
  84. package/lib/password-field/themes/halo/dark/index.js +1 -1
  85. package/lib/password-field/themes/halo/light/index.js +1 -1
  86. package/lib/password-field/themes/solar/charcoal/index.js +1 -1
  87. package/lib/password-field/themes/solar/pearl/index.js +1 -1
  88. package/lib/pill/index.d.ts +11 -3
  89. package/lib/pill/index.js +25 -11
  90. package/lib/radio-button/index.d.ts +7 -11
  91. package/lib/radio-button/index.js +14 -25
  92. package/lib/radio-button/themes/halo/dark/index.js +1 -1
  93. package/lib/radio-button/themes/halo/light/index.js +1 -1
  94. package/lib/radio-button/themes/solar/charcoal/index.js +1 -1
  95. package/lib/radio-button/themes/solar/pearl/index.js +1 -1
  96. package/lib/search-field/themes/halo/dark/index.js +1 -1
  97. package/lib/search-field/themes/halo/light/index.js +1 -1
  98. package/lib/search-field/themes/solar/charcoal/index.js +1 -1
  99. package/lib/search-field/themes/solar/pearl/index.js +1 -1
  100. package/lib/select/index.d.ts +13 -1
  101. package/lib/select/index.js +25 -7
  102. package/lib/select/themes/halo/dark/index.js +1 -1
  103. package/lib/select/themes/halo/light/index.js +1 -1
  104. package/lib/select/themes/solar/charcoal/index.js +1 -1
  105. package/lib/select/themes/solar/pearl/index.js +1 -1
  106. package/lib/slider/themes/halo/dark/index.js +1 -1
  107. package/lib/slider/themes/halo/light/index.js +1 -1
  108. package/lib/slider/themes/solar/charcoal/index.js +1 -1
  109. package/lib/slider/themes/solar/pearl/index.js +1 -1
  110. package/lib/tab/themes/halo/dark/index.js +1 -1
  111. package/lib/tab/themes/halo/light/index.js +1 -1
  112. package/lib/tab/themes/solar/charcoal/index.js +1 -1
  113. package/lib/tab/themes/solar/pearl/index.js +1 -1
  114. package/lib/tab-bar/index.d.ts +13 -8
  115. package/lib/tab-bar/index.js +26 -15
  116. package/lib/tab-bar/themes/halo/dark/index.js +1 -0
  117. package/lib/tab-bar/themes/halo/light/index.js +1 -0
  118. package/lib/tab-bar/themes/solar/charcoal/index.js +1 -0
  119. package/lib/tab-bar/themes/solar/pearl/index.js +1 -0
  120. package/lib/text-field/index.js +2 -3
  121. package/lib/text-field/themes/halo/dark/index.js +1 -1
  122. package/lib/text-field/themes/halo/light/index.js +1 -1
  123. package/lib/text-field/themes/solar/charcoal/index.js +1 -1
  124. package/lib/text-field/themes/solar/pearl/index.js +1 -1
  125. package/lib/toggle/index.d.ts +7 -10
  126. package/lib/toggle/index.js +14 -24
  127. package/lib/toggle/themes/halo/dark/index.js +1 -1
  128. package/lib/toggle/themes/halo/light/index.js +1 -1
  129. package/lib/toggle/themes/solar/charcoal/index.js +1 -1
  130. package/lib/toggle/themes/solar/pearl/index.js +1 -1
  131. package/lib/tooltip/helpers/overflow-tooltip.d.ts +11 -4
  132. package/lib/tooltip/helpers/overflow-tooltip.js +34 -6
  133. package/lib/tooltip/index.d.ts +2 -2
  134. package/lib/tooltip/index.js +2 -2
  135. package/lib/tree/elements/tree-item.d.ts +4 -0
  136. package/lib/tree/elements/tree-item.js +4 -0
  137. package/lib/tree/elements/tree.d.ts +1 -0
  138. package/lib/tree/elements/tree.js +1 -0
  139. package/lib/tree/helpers/renderer.d.ts +0 -1
  140. package/lib/tree/helpers/renderer.js +0 -2
  141. package/lib/tree/index.d.ts +2 -1
  142. package/lib/tree/themes/halo/dark/index.js +2 -3
  143. package/lib/tree/themes/halo/light/index.js +2 -3
  144. package/lib/tree/themes/solar/charcoal/index.js +2 -3
  145. package/lib/tree/themes/solar/pearl/index.js +2 -3
  146. package/lib/tree-select/themes/halo/dark/index.js +1 -1
  147. package/lib/tree-select/themes/halo/light/index.js +1 -1
  148. package/lib/tree-select/themes/solar/charcoal/index.js +1 -1
  149. package/lib/tree-select/themes/solar/pearl/index.js +1 -1
  150. package/lib/version.js +1 -1
  151. package/package.json +17 -17
@@ -0,0 +1,632 @@
1
+ import { __decorate } from "tslib";
2
+ import { ControlElement, css, html, WarningNotice } from '@refinitiv-ui/core';
3
+ import { customElement } from '@refinitiv-ui/core/decorators/custom-element.js';
4
+ import { property } from '@refinitiv-ui/core/decorators/property.js';
5
+ import { VERSION } from '../../version.js';
6
+ import { CollectionComposer } from '@refinitiv-ui/utils/collection.js';
7
+ import { getItemId } from '../helpers/item-id.js';
8
+ import { ListRenderer } from '../helpers/renderer.js';
9
+ import './list-item.js';
10
+ /**
11
+ * Key direction
12
+ */
13
+ var Direction;
14
+ (function (Direction) {
15
+ Direction[Direction["UP"] = -1] = "UP";
16
+ Direction[Direction["DOWN"] = 1] = "DOWN";
17
+ })(Direction || (Direction = {}));
18
+ const valueFormatWarning = new WarningNotice('The specified \'values\' format does not conform to the required format.');
19
+ /**
20
+ * Provides listing and immutable selection
21
+ * @fires value-changed - Dispatched when value changes
22
+ */
23
+ let List = class List extends ControlElement {
24
+ constructor() {
25
+ super(...arguments);
26
+ this.defaultRole = 'listbox';
27
+ /**
28
+ * Used to timestamp renders.
29
+ * This enables diff checking against item updates,
30
+ * rendering only items which have updated since the last render cycle.
31
+ */
32
+ this.renderTimestamp = new Map();
33
+ /**
34
+ * Requests an update after a composer modification.
35
+ * @returns Update promise.
36
+ */
37
+ this.modificationUpdate = () => {
38
+ this.requestUpdate();
39
+ };
40
+ /**
41
+ * Item map; used to link element nodes to data items.
42
+ */
43
+ this.itemMap = new Map();
44
+ /**
45
+ * Element map; used to link data items to element nodes.
46
+ */
47
+ this.elementMap = new Map();
48
+ /**
49
+ * Composer used to query and modify item state.
50
+ */
51
+ this.composer = new CollectionComposer([]);
52
+ /**
53
+ * Element focus delegation.
54
+ * Set to `false` and relies on native focusing.
55
+ */
56
+ this.delegatesFocus = false;
57
+ /**
58
+ * Renderer used to render list item elements
59
+ * @type {ListRenderer}
60
+ */
61
+ this.renderer = new ListRenderer(this);
62
+ /**
63
+ * Disable selections
64
+ */
65
+ this.stateless = false;
66
+ /**
67
+ * Allow multiple selections
68
+ */
69
+ this.multiple = false;
70
+ this._data = null;
71
+ }
72
+ /**
73
+ * Element version number
74
+ * @returns version number
75
+ */
76
+ static get version() {
77
+ return VERSION;
78
+ }
79
+ /**
80
+ * The data object, used to render the list.
81
+ * @type {ListData}
82
+ * @default null
83
+ */
84
+ get data() {
85
+ return this._data;
86
+ }
87
+ set data(value) {
88
+ const oldValue = this._data;
89
+ if (oldValue === value) {
90
+ return;
91
+ }
92
+ if (value instanceof CollectionComposer) {
93
+ this.composer = value;
94
+ }
95
+ else if (Array.isArray(value)) {
96
+ this.composer = new CollectionComposer(value);
97
+ }
98
+ else {
99
+ this.composer = new CollectionComposer([]);
100
+ }
101
+ this.composer.on('modification', // Listen for modifications
102
+ this.modificationUpdate // Update the template
103
+ );
104
+ this.clearMaps();
105
+ this._data = value;
106
+ this.requestUpdate('data', oldValue);
107
+ }
108
+ /**
109
+ * Returns the first selected item value.
110
+ * Use `values` when multiple selection mode is enabled.
111
+ * @default -
112
+ */
113
+ get value() {
114
+ return this.values[0] || '';
115
+ }
116
+ set value(value) {
117
+ const oldValue = this.value;
118
+ if (value !== oldValue || this.values.length > 1) {
119
+ this.clearSelection();
120
+ const item = this.queryItemsByPropertyValue('value', value)[0];
121
+ if (item) {
122
+ this.composer.setItemPropertyValue(item, 'selected', true);
123
+ }
124
+ this.requestUpdate('value', oldValue);
125
+ }
126
+ }
127
+ /**
128
+ * Returns a values collection of the currently
129
+ * selected item values
130
+ * @type {string[]}
131
+ * @default []
132
+ * @readonly
133
+ */
134
+ get values() {
135
+ return this.queryItemsByPropertyValue('selected', true)
136
+ .map((item) => this.composer.getItemPropertyValue(item, 'value'));
137
+ }
138
+ set values(values) {
139
+ if (!Array.isArray(values)) {
140
+ valueFormatWarning.show();
141
+ this.values = [];
142
+ }
143
+ else {
144
+ // Clone value arrays
145
+ const newValue = values.slice();
146
+ const oldValue = this.values.slice();
147
+ newValue.sort();
148
+ oldValue.sort();
149
+ // Create comparison strings to check for differences
150
+ const newComparison = newValue.toString();
151
+ const oldComparison = oldValue.toString();
152
+ // Should we update the selection state?
153
+ if (newComparison !== oldComparison) {
154
+ this.clearSelection();
155
+ values.some((value) => {
156
+ const matches = this.queryItemsByPropertyValue('value', value);
157
+ matches.forEach((match) => this.composer.setItemPropertyValue(match, 'selected', true));
158
+ return !this.multiple; // Only set the fist value if multiple is not enabled
159
+ });
160
+ this.requestUpdate('values', oldValue);
161
+ }
162
+ }
163
+ }
164
+ /**
165
+ * Selects an item in the list
166
+ * @param item Data Item or Item Element
167
+ * @returns If a selection has been made or not
168
+ */
169
+ selectItem(item) {
170
+ if (!this.stateless) {
171
+ if (item instanceof HTMLElement) {
172
+ item = this.itemFromElement(item);
173
+ }
174
+ if (item && this.multiple) {
175
+ const value = this.composer.getItemPropertyValue(item, 'selected');
176
+ this.composer.setItemPropertyValue(item, 'selected', !value);
177
+ return true;
178
+ }
179
+ if (item && this.composer.getItemPropertyValue(item, 'selected') !== true) {
180
+ this.clearSelection();
181
+ this.composer.setItemPropertyValue(item, 'selected', true);
182
+ return true;
183
+ }
184
+ }
185
+ return false;
186
+ }
187
+ /**
188
+ * Navigate up through the list items
189
+ * @returns {void}
190
+ */
191
+ up() {
192
+ this.highlightItem(this.getNextHighlightItem(Direction.UP), true);
193
+ }
194
+ /**
195
+ * Navigate down through the list items
196
+ * @returns {void}
197
+ */
198
+ down() {
199
+ this.highlightItem(this.getNextHighlightItem(Direction.DOWN), true);
200
+ }
201
+ /**
202
+ * Navigate to first focusable item of the list
203
+ * @returns {void}
204
+ */
205
+ first() {
206
+ const firstItem = this.itemMap.get(this.tabbableItems[0]);
207
+ this.highlightItem(firstItem, true);
208
+ }
209
+ /**
210
+ * Navigate to first focusable item of the list
211
+ * @returns {void}
212
+ */
213
+ last() {
214
+ const lastItem = this.itemMap.get(this.tabbableItems[this.tabbableItems.length - 1]);
215
+ this.highlightItem(lastItem, true);
216
+ }
217
+ /**
218
+ * Proxy for querying the composer
219
+ * @param engine composer querying engine
220
+ * @returns Collection of queried items
221
+ */
222
+ queryItems(engine) {
223
+ return this.composer.queryItems(engine);
224
+ }
225
+ /**
226
+ * Proxy for querying the composer by property and value
227
+ * @param name Property name
228
+ * @param value Property value
229
+ * @returns Collection of queried items
230
+ */
231
+ queryItemsByPropertyValue(name, value) {
232
+ return this.composer.queryItemsByPropertyValue(name, value);
233
+ }
234
+ /**
235
+ * Gets the associated element for the data item provided,
236
+ * if there is one available.
237
+ * @param item Item to map element to
238
+ * @returns Associated element
239
+ */
240
+ elementFromItem(item) {
241
+ return this.elementMap.get(item);
242
+ }
243
+ /**
244
+ * Gets the associated data item for the provided element,
245
+ * if there is one available.
246
+ * @param element Element to map item to
247
+ * @returns Associated date item
248
+ */
249
+ itemFromElement(element) {
250
+ return this.itemMap.get(element);
251
+ }
252
+ /**
253
+ * Tries to find the next focusable element.
254
+ * @param direction Direction to search
255
+ * @param element Starting element
256
+ * @returns Next logical element to focus
257
+ */
258
+ getNextFocusableItem(direction, element) {
259
+ if (!element) {
260
+ return;
261
+ }
262
+ const children = this.tabbableItems;
263
+ if (children.length > 1) {
264
+ let index = children.indexOf(element) + direction;
265
+ if (index < 0) {
266
+ index = children.length - 1;
267
+ }
268
+ else if (index >= children.length) {
269
+ index = 0;
270
+ }
271
+ return children[index];
272
+ }
273
+ }
274
+ /**
275
+ * Tries to find the next highlight item
276
+ * @param direction Direction to search
277
+ * @returns A data item, if found.
278
+ */
279
+ getNextHighlightItem(direction) {
280
+ const highlightItem = this.queryItemsByPropertyValue('highlighted', true)[0];
281
+ const nextElement = this.getNextFocusableItem(direction) || this.getNextFocusableItem(direction, this.elementFromItem(highlightItem));
282
+ const backupElement = this.tabbableItems[0];
283
+ if (nextElement) {
284
+ return this.itemFromElement(nextElement);
285
+ }
286
+ else if (backupElement) {
287
+ return this.itemFromElement(backupElement);
288
+ }
289
+ return undefined;
290
+ }
291
+ /**
292
+ * Clears any highlighted item
293
+ * @returns {void}
294
+ */
295
+ clearHighlighted() {
296
+ this.queryItemsByPropertyValue('highlighted', true)
297
+ .forEach(item => this.composer.setItemPropertyValue(item, 'highlighted', false));
298
+ }
299
+ /**
300
+ * Highlights a single item.
301
+ * Used for navigation.
302
+ * @param item Item to highlight
303
+ * @param scrollToItem Scroll the item into view?
304
+ * @returns {void}
305
+ */
306
+ highlightItem(item, scrollToItem = false) {
307
+ if (item) {
308
+ this.clearHighlighted();
309
+ this.composer.setItemPropertyValue(item, 'highlighted', true);
310
+ if (this.tabIndex >= 0) {
311
+ const id = getItemId(this.renderer.key, item.value);
312
+ this.setAttribute('aria-activedescendant', id);
313
+ }
314
+ scrollToItem && this.scrollToItem(item);
315
+ }
316
+ }
317
+ /**
318
+ * Gets the available tabbable elements
319
+ */
320
+ get tabbableItems() {
321
+ return Array.from(this.children).filter((item) => {
322
+ if (item instanceof HTMLElement) {
323
+ const role = item.getAttribute('role');
324
+ const isEnabled = !item.hasAttribute('disabled');
325
+ const isOption = role ? ['option', 'treeitem'].includes(role) : false;
326
+ return isOption && isEnabled;
327
+ }
328
+ return false;
329
+ });
330
+ }
331
+ /**
332
+ * Returns the current focused element
333
+ */
334
+ get highlightElement() {
335
+ const item = this.queryItemsByPropertyValue('highlighted', true)[0];
336
+ return this.elementFromItem(item) || null;
337
+ }
338
+ /**
339
+ * Tries to select the current highlighted element
340
+ * @returns {void}
341
+ */
342
+ triggerActiveItem() {
343
+ const element = this.highlightElement;
344
+ const item = element && this.itemFromElement(element);
345
+ item && this.selectItem(item) && this.fireSelectionUpdate();
346
+ }
347
+ /**
348
+ * Scroll to list item element
349
+ * @param item Data item to scroll to
350
+ * @returns {void}
351
+ */
352
+ scrollToItem(item) {
353
+ const element = this.elementFromItem(item);
354
+ if (element) {
355
+ const minPosition = this.scrollTop;
356
+ const maxPosition = this.scrollTop + this.clientHeight - element.offsetHeight;
357
+ const position = element.offsetTop;
358
+ let scrollPosition;
359
+ if (position > maxPosition) {
360
+ scrollPosition = element.offsetTop - this.clientHeight + element.offsetHeight;
361
+ }
362
+ else if (position < minPosition) {
363
+ scrollPosition = element.offsetTop;
364
+ }
365
+ if (scrollPosition) {
366
+ this.scrollTop = scrollPosition;
367
+ }
368
+ }
369
+ }
370
+ /**
371
+ * Handles key input
372
+ * @param event Key down event object
373
+ * @returns {void}
374
+ */
375
+ onKeyDown(event) {
376
+ switch (event.key) {
377
+ case ' ':
378
+ case 'Spacebar':
379
+ case 'Enter':
380
+ this.triggerActiveItem();
381
+ break;
382
+ case 'Up':
383
+ case 'ArrowUp':
384
+ this.up();
385
+ break;
386
+ case 'Down':
387
+ case 'ArrowDown':
388
+ this.down();
389
+ break;
390
+ case 'Home':
391
+ this.first();
392
+ break;
393
+ case 'End':
394
+ this.last();
395
+ break;
396
+ default:
397
+ return;
398
+ }
399
+ event.preventDefault();
400
+ }
401
+ /**
402
+ * Handle list on tap
403
+ * Typically it will select an item
404
+ * @param event Event to handle
405
+ * @returns {void}
406
+ */
407
+ onTap(event) {
408
+ const element = this.findItemElementFromTarget(event.target);
409
+ const item = element && this.itemFromElement(element);
410
+ if (item) {
411
+ this.highlightItem(item);
412
+ if (this.selectItem(item)) {
413
+ this.fireSelectionUpdate();
414
+ }
415
+ }
416
+ }
417
+ /**
418
+ * Handles mouse move
419
+ * Typically it will highlight an item
420
+ * @param event Event to handle
421
+ * @returns {void}
422
+ */
423
+ onMouse(event) {
424
+ const element = this.findItemElementFromTarget(event.target);
425
+ const item = element && this.itemFromElement(element);
426
+ if (item && element !== this.highlightElement) {
427
+ this.highlightItem(item);
428
+ }
429
+ }
430
+ /**
431
+ * Handles item focus out
432
+ * Typically it will remove highlighting
433
+ * @returns {void}
434
+ */
435
+ onBlur() {
436
+ this.clearHighlighted();
437
+ this.removeAttribute('aria-activedescendant');
438
+ }
439
+ /**
440
+ * Tries to find a known item element,
441
+ * from an event target
442
+ * @param target Event target
443
+ * @returns Found element, if available
444
+ */
445
+ findItemElementFromTarget(target) {
446
+ let element = target;
447
+ while (element) {
448
+ if (this.itemMap.has(element)) {
449
+ break; // known rendered item
450
+ }
451
+ element = element.parentElement;
452
+ }
453
+ return element;
454
+ }
455
+ /**
456
+ * Clears the current selected items
457
+ * @returns {void}
458
+ */
459
+ clearSelection() {
460
+ this.queryItemsByPropertyValue('selected', true)
461
+ .forEach((item) => this.composer.setItemPropertyValue(item, 'selected', false));
462
+ this.requestUpdate();
463
+ }
464
+ /**
465
+ * Queries and returns all renderable items.
466
+ * @returns Collection of renderable items
467
+ */
468
+ get renderItems() {
469
+ return this.queryItems((item, composer) => {
470
+ return composer.getItemPropertyValue(item, 'hidden') !== true;
471
+ });
472
+ }
473
+ /**
474
+ * Proxy for creating list item elements.
475
+ * Allows for a mapping to be created between
476
+ * Data Item and Item Element.
477
+ * @param item Data item context
478
+ * @param recyclableElements Child elements available for reuse
479
+ * @returns List item element
480
+ */
481
+ createListItem(item, recyclableElements) {
482
+ const cachedElement = this.elementFromItem(item);
483
+ const previousTimestamp = this.renderTimestamp.get(item) || NaN;
484
+ if (cachedElement && previousTimestamp > this.composer.getItemTimestamp(item)) {
485
+ return cachedElement; // don't re-render if the item hasn't changed
486
+ }
487
+ if (!cachedElement && recyclableElements.length) {
488
+ // Remove any old ties with the reusable element.
489
+ const recycledElement = recyclableElements.pop();
490
+ const previousItem = this.itemFromElement(recycledElement);
491
+ this.itemMap.delete(recycledElement);
492
+ previousItem && this.elementMap.delete(previousItem);
493
+ this.elementMap.set(item, recycledElement);
494
+ }
495
+ const freshElement = this.renderer(item, this.composer, this.elementFromItem(item));
496
+ if (cachedElement && cachedElement !== freshElement) {
497
+ // Renderer returned a new element, so remove the old link.
498
+ this.itemMap.delete(cachedElement);
499
+ }
500
+ this.itemMap.set(freshElement, item); // Link element to item
501
+ this.elementMap.set(item, freshElement); // Link item to element
502
+ this.renderTimestamp.set(item, performance.now());
503
+ return freshElement;
504
+ }
505
+ /**
506
+ * Clears all item-element and timestamp maps
507
+ * @returns {void}
508
+ */
509
+ clearMaps() {
510
+ this.itemMap.clear();
511
+ this.elementMap.clear();
512
+ this.renderTimestamp.clear();
513
+ }
514
+ /**
515
+ * Fire value changed event
516
+ * @returns {void}
517
+ */
518
+ fireSelectionUpdate() {
519
+ /**
520
+ * @event List#value-changed
521
+ */
522
+ this.notifyPropertyChange('value', this.value);
523
+ }
524
+ /**
525
+ * Calculates what elements can be recycled safely
526
+ * @param renderItems Current items to render
527
+ * @returns Collection of elements to be recycled
528
+ */
529
+ calculateRecyclableElements(renderItems) {
530
+ const result = [];
531
+ for (const element of this.children) {
532
+ const item = this.itemFromElement(element);
533
+ if (item && !renderItems.includes(item)) {
534
+ result.push(element);
535
+ }
536
+ }
537
+ return result;
538
+ }
539
+ /**
540
+ * Renders updates to light DOM
541
+ * @returns {void}
542
+ */
543
+ renderLightDOM() {
544
+ const renderItems = this.renderItems;
545
+ const currentChildren = Array.from(this.children);
546
+ const recyclableElements = this.calculateRecyclableElements(renderItems);
547
+ const renderChildren = renderItems.map((item) => this.createListItem(item, recyclableElements));
548
+ const deletions = currentChildren.filter(element => !renderChildren.includes(element));
549
+ deletions.forEach(element => this.removeChild(element));
550
+ renderChildren.forEach((element, index) => {
551
+ if (this.children.length === index) {
552
+ this.appendChild(element);
553
+ }
554
+ else if (element !== this.children[index]) {
555
+ this.insertBefore(element, this.children[index]);
556
+ }
557
+ });
558
+ }
559
+ /**
560
+ * Invoked when the element is first updated. Implement to perform one time work on the element after update.
561
+ * @param changeProperties changed properties
562
+ * @returns {void}
563
+ */
564
+ firstUpdated(changeProperties) {
565
+ super.firstUpdated(changeProperties);
566
+ this.addEventListener('keydown', this.onKeyDown);
567
+ this.addEventListener('tap', this.onTap);
568
+ this.addEventListener('mousemove', this.onMouse);
569
+ this.addEventListener('mouseleave', this.clearHighlighted);
570
+ this.addEventListener('focusout', this.onBlur);
571
+ }
572
+ /**
573
+ * Invoked before update() to compute values needed during the update.
574
+ * @param changeProperties changed properties
575
+ * @returns {void}
576
+ */
577
+ willUpdate(changeProperties) {
578
+ if (changeProperties.has('multiple')) {
579
+ this.renderTimestamp.clear(); // force render of all items
580
+ this.setAttribute('aria-multiselectable', this.multiple ? 'true' : 'false');
581
+ }
582
+ }
583
+ /**
584
+ * A `CSSResultGroup` that will be used
585
+ * to style the host, slotted children
586
+ * and the internal template of the element.
587
+ * @return CSS template
588
+ */
589
+ static get styles() {
590
+ return css `
591
+ :host {
592
+ display: block;
593
+ max-height: 600px;
594
+ overflow-y: auto;
595
+ position: relative; /* required for scrollToItem */
596
+ }
597
+ `;
598
+ }
599
+ /**
600
+ * A `TemplateResult` that will be used
601
+ * to render the updated internal template.
602
+ * @return Render template
603
+ */
604
+ render() {
605
+ this.renderLightDOM();
606
+ return html `<slot></slot>`;
607
+ }
608
+ };
609
+ __decorate([
610
+ property({ type: Function, attribute: false })
611
+ ], List.prototype, "renderer", void 0);
612
+ __decorate([
613
+ property({ type: Boolean })
614
+ ], List.prototype, "stateless", void 0);
615
+ __decorate([
616
+ property({ type: Boolean })
617
+ ], List.prototype, "multiple", void 0);
618
+ __decorate([
619
+ property({ attribute: false })
620
+ ], List.prototype, "data", null);
621
+ __decorate([
622
+ property({ type: String })
623
+ ], List.prototype, "value", null);
624
+ __decorate([
625
+ property({ type: Array, attribute: false })
626
+ ], List.prototype, "values", null);
627
+ List = __decorate([
628
+ customElement('ef-list', {
629
+ alias: 'coral-list'
630
+ })
631
+ ], List);
632
+ export { List };
@@ -1,4 +1,3 @@
1
- import '../../item/index.js';
2
1
  import { Renderer } from '../renderer.js';
3
2
  /**
4
3
  * Renders list items as `ef-item` elements.
@@ -1,5 +1,4 @@
1
1
  import { uuid } from '@refinitiv-ui/utils/uuid.js';
2
- import '../../item/index.js';
3
2
  import { getItemId } from './item-id.js';
4
3
  import { Renderer } from '../renderer.js';
5
4
  /**
@@ -15,7 +14,7 @@ export class ListRenderer extends Renderer {
15
14
  /**
16
15
  * Element to render
17
16
  */
18
- const el = (element || document.createElement('ef-item'));
17
+ const el = (element || document.createElement('ef-list-item'));
19
18
  /**
20
19
  * Tooltip value to be used, if any.
21
20
  */
@@ -32,7 +31,6 @@ export class ListRenderer extends Renderer {
32
31
  el.type = composer.getItemPropertyValue(item, 'type');
33
32
  el.multiple = !!context && context.multiple === true;
34
33
  const itemRole = el.type === 'text' || !el.type ? 'option' : 'presentation';
35
- el.tabIndex = -1;
36
34
  el.setAttribute('role', itemRole);
37
35
  tooltip ? el.setAttribute('title', tooltip) : el.removeAttribute('title');
38
36
  return el;