@refinitiv-ui/elements 7.4.0 → 7.6.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +1 -1
  3. package/README.md +5 -5
  4. package/lib/calendar/constants.d.ts +1 -1
  5. package/lib/calendar/constants.js +7 -6
  6. package/lib/calendar/custom-elements.json +4 -0
  7. package/lib/calendar/custom-elements.md +5 -4
  8. package/lib/calendar/index.d.ts +13 -3
  9. package/lib/calendar/index.js +52 -29
  10. package/lib/calendar/types.d.ts +21 -2
  11. package/lib/calendar/utils.d.ts +9 -1
  12. package/lib/calendar/utils.js +24 -1
  13. package/lib/combo-box/index.js +2 -0
  14. package/lib/datetime-field/custom-elements.json +0 -5
  15. package/lib/datetime-field/custom-elements.md +4 -5
  16. package/lib/datetime-field/index.d.ts +0 -22
  17. package/lib/datetime-field/index.js +2 -38
  18. package/lib/email-field/custom-elements.json +12 -0
  19. package/lib/email-field/custom-elements.md +7 -0
  20. package/lib/email-field/index.d.ts +10 -7
  21. package/lib/email-field/index.js +14 -13
  22. package/lib/interactive-chart/index.js +1 -0
  23. package/lib/number-field/custom-elements.json +4 -7
  24. package/lib/number-field/custom-elements.md +2 -3
  25. package/lib/number-field/index.d.ts +6 -11
  26. package/lib/number-field/index.js +19 -33
  27. package/lib/password-field/custom-elements.json +12 -0
  28. package/lib/password-field/custom-elements.md +7 -0
  29. package/lib/password-field/index.d.ts +10 -0
  30. package/lib/password-field/index.js +14 -0
  31. package/lib/search-field/custom-elements.json +12 -0
  32. package/lib/search-field/custom-elements.md +7 -0
  33. package/lib/search-field/index.d.ts +10 -0
  34. package/lib/search-field/index.js +14 -0
  35. package/lib/slider/custom-elements.json +1 -1
  36. package/lib/slider/custom-elements.md +1 -1
  37. package/lib/slider/index.d.ts +6 -1
  38. package/lib/slider/index.js +14 -2
  39. package/lib/text-field/custom-elements.json +12 -0
  40. package/lib/text-field/custom-elements.md +7 -0
  41. package/lib/text-field/index.d.ts +17 -12
  42. package/lib/text-field/index.js +29 -31
  43. package/lib/tree/elements/tree.js +4 -4
  44. package/lib/tree/managers/tree-manager.d.ts +8 -0
  45. package/lib/tree/managers/tree-manager.js +19 -1
  46. package/lib/tree-select/index.d.ts +12 -3
  47. package/lib/tree-select/index.js +55 -20
  48. package/lib/version.js +1 -1
  49. package/package.json +17 -17
@@ -1,5 +1,5 @@
1
1
  import { __decorate } from "tslib";
2
- import { FormFieldElement, css, html, nothing } from '@refinitiv-ui/core';
2
+ import { FocusedPropertyKey, FormFieldElement, css, html, nothing } from '@refinitiv-ui/core';
3
3
  import { customElement } from '@refinitiv-ui/core/decorators/custom-element.js';
4
4
  import { property } from '@refinitiv-ui/core/decorators/property.js';
5
5
  import { isElementOverflown } from '@refinitiv-ui/utils/element.js';
@@ -108,6 +108,17 @@ let TextField = class TextField extends FormFieldElement {
108
108
  super.firstUpdated(changedProperties);
109
109
  registerOverflowTooltip(this, () => this.inputValue, () => (this.inputElement ? isElementOverflown(this.inputElement) : false));
110
110
  }
111
+ /**
112
+ * Updates the element
113
+ * @param changedProperties Properties that has changed
114
+ * @returns {void}
115
+ */
116
+ update(changedProperties) {
117
+ if (changedProperties.has(FocusedPropertyKey) && !this.focused) {
118
+ this.reportValidity();
119
+ }
120
+ super.update(changedProperties);
121
+ }
111
122
  /**
112
123
  * Called when the element’s DOM has been updated and rendered
113
124
  * @param changedProperties Properties that has changed
@@ -116,12 +127,23 @@ let TextField = class TextField extends FormFieldElement {
116
127
  updated(changedProperties) {
117
128
  super.updated(changedProperties);
118
129
  if (this.shouldSyncInputValue(changedProperties)) {
119
- this.syncInputValue(changedProperties);
120
- }
121
- if (this.shouldValidateInput(changedProperties)) {
122
- this.validateInput();
130
+ this.syncInputValue();
123
131
  }
124
132
  }
133
+ /**
134
+ * Returns `true` if the element input is valid; otherwise, returns `false`.
135
+ * @returns element input validity
136
+ */
137
+ checkValidity() {
138
+ return super.checkValidity();
139
+ }
140
+ /**
141
+ * Validate the element input and mark it as error if its input is invalid.
142
+ * @returns `true` if the element input is valid; otherwise, returns `false`.
143
+ */
144
+ reportValidity() {
145
+ return super.reportValidity();
146
+ }
125
147
  /**
126
148
  * Check if input value should be synchronised with component value
127
149
  * @param changedProperties Properties that has changed
@@ -137,26 +159,9 @@ let TextField = class TextField extends FormFieldElement {
137
159
  * @param changedProperties Properties that has changed
138
160
  * @returns {void}
139
161
  */
140
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
141
- syncInputValue(changedProperties) {
162
+ syncInputValue() {
142
163
  this.inputValue = this.value;
143
164
  }
144
- /**
145
- * Check if input should be re-validated
146
- * @param changedProperties Properties that has changed
147
- * @returns True if input should be re-validated
148
- */
149
- /* c8 ignore start */
150
- shouldValidateInput(changedProperties) {
151
- // TODO: This validation should be refactored
152
- return (changedProperties.has('pattern') ||
153
- !!(this.pattern && changedProperties.has('value')) ||
154
- changedProperties.has('minLength') ||
155
- !!(this.minLength && changedProperties.has('value')) ||
156
- changedProperties.has('maxLength') ||
157
- !!(this.maxLength && changedProperties.has('value')));
158
- }
159
- /* c8 ignore stop */
160
165
  /**
161
166
  * Runs on input element `input` event
162
167
  * @param event `input` event
@@ -181,14 +186,7 @@ let TextField = class TextField extends FormFieldElement {
181
186
  onPossibleValueChange(event) {
182
187
  const value = this.inputElement?.value || '';
183
188
  this.setValueAndNotify(value);
184
- }
185
- /**
186
- * Uses native `checkValidity()` function to validate input
187
- * @returns {void}
188
- */
189
- validateInput() {
190
- const error = !this.inputElement?.checkValidity();
191
- this.notifyErrorChange(error);
189
+ this.reportValidity();
192
190
  }
193
191
  /**
194
192
  * Fires event on `icon` click
@@ -374,7 +374,7 @@ let Tree = class Tree extends List {
374
374
  */
375
375
  get values() {
376
376
  return this.manager.checkedItems.map((item) => {
377
- return this.composer.getItemPropertyValue(item, 'value') || '';
377
+ return this.composer.getItemPropertyValue(item, 'value') ?? '';
378
378
  });
379
379
  }
380
380
  set values(values) {
@@ -384,9 +384,9 @@ let Tree = class Tree extends List {
384
384
  }
385
385
  else {
386
386
  // Clone value arrays
387
- const newValue = [...values].sort().toString();
388
- const oldValue = [...this.values].sort().toString();
389
- if (newValue !== oldValue) {
387
+ const newValue = [...values];
388
+ const oldValue = [...this.values];
389
+ if (newValue.toString() !== oldValue.toString()) {
390
390
  this.manager.uncheckAllItems();
391
391
  values.some((value) => {
392
392
  this.queryItemsByPropertyValue('value', value).forEach((item) => {
@@ -24,6 +24,10 @@ export declare class TreeManager<T extends TreeDataItem> {
24
24
  * Mode (algorithm) the tree manage is using
25
25
  */
26
26
  private mode;
27
+ /**
28
+ * Last selected item timestamp
29
+ */
30
+ private lastSelectedAt?;
27
31
  constructor(composer: CollectionComposer<T>, mode?: TreeManagerMode);
28
32
  /**
29
33
  * Is the manager maintaining parent/child relationships
@@ -42,6 +46,10 @@ export declare class TreeManager<T extends TreeDataItem> {
42
46
  * When managing relationships, this excludes groups/parents from the result.
43
47
  */
44
48
  get checkedItems(): readonly T[];
49
+ /**
50
+ * Compare items function order by sequential selected timestamp
51
+ */
52
+ protected get orderBySelectedAt(): (itemA: T, itemB: T) => number;
45
53
  /**
46
54
  * Items which should be visibly displayed.
47
55
  * This can be used to render items.
@@ -47,12 +47,24 @@ export class TreeManager {
47
47
  * When managing relationships, this excludes groups/parents from the result.
48
48
  */
49
49
  get checkedItems() {
50
- return this.composer.queryItems((item) => {
50
+ const items = this.composer.queryItems((item) => {
51
51
  if (this.manageRelationships && this.isItemParent(item)) {
52
52
  return false;
53
53
  }
54
54
  return this.isItemChecked(item);
55
55
  }, Infinity);
56
+ return Object.freeze(items.slice().sort(this.orderBySelectedAt));
57
+ }
58
+ /**
59
+ * Compare items function order by sequential selected timestamp
60
+ */
61
+ get orderBySelectedAt() {
62
+ // Order by sequential selected timestamp
63
+ return (itemA, itemB) => {
64
+ const timeA = this.composer.getItemPropertyValue(itemA, 'selectedAt') ?? 0;
65
+ const timeB = this.composer.getItemPropertyValue(itemB, 'selectedAt') ?? 0;
66
+ return timeA - timeB;
67
+ };
56
68
  }
57
69
  /**
58
70
  * Items which should be visibly displayed.
@@ -341,7 +353,13 @@ export class TreeManager {
341
353
  }
342
354
  _checkItem(item, manageRelationships = this.manageRelationships) {
343
355
  if (this.canCheckItem(item)) {
356
+ // Create unique timestamp base on the latest selection for sequential selection.
357
+ const timestamp = Date.now();
358
+ this.lastSelectedAt =
359
+ this.lastSelectedAt && this.lastSelectedAt >= timestamp ? this.lastSelectedAt + 1 : timestamp;
360
+ // Set item selected with timestamp
344
361
  this.composer.setItemPropertyValue(item, 'selected', true);
362
+ this.composer.setItemPropertyValue(item, 'selectedAt', this.lastSelectedAt);
345
363
  if (manageRelationships) {
346
364
  this.forceUpdateOnPath(item);
347
365
  this.getItemDescendants(item).forEach((descendant) => this._checkItem(descendant, false));
@@ -59,7 +59,11 @@ export declare class TreeSelect extends ComboBox<TreeSelectDataItem> {
59
59
  /**
60
60
  * Extracted values from {@link this.checkedGroupedItems}
61
61
  */
62
- protected pillsData: TreeSelectDataItem[];
62
+ protected pillsData: Readonly<TreeSelectDataItem[]>;
63
+ /**
64
+ * Cache old selection timestamps for revert selected timestamps when cancel selection
65
+ */
66
+ protected oldSelection: Map<string, number | undefined>;
63
67
  /**
64
68
  * Are there pills visible
65
69
  */
@@ -207,10 +211,15 @@ export declare class TreeSelect extends ComboBox<TreeSelectDataItem> {
207
211
  */
208
212
  protected persistSelection(): void;
209
213
  /**
210
- * Reverse selection. Run on Esc or Cancel
214
+ * Revert modified selection to old values. Run on Esc or Cancel
215
+ * @returns {void}
216
+ */
217
+ protected revertModifiedSelection(): void;
218
+ /**
219
+ * Revert all selected timestamps to the previous state which affect to item sorting
211
220
  * @returns {void}
212
221
  */
213
- protected cancelSelection(): void;
222
+ protected revertSelectionTimestamps(): void;
214
223
  /**
215
224
  * Update memoized track
216
225
  *
@@ -1,5 +1,5 @@
1
1
  import { __decorate } from "tslib";
2
- import { css, html, nothing, triggerResize } from '@refinitiv-ui/core';
2
+ import { WarningNotice, css, html, nothing, triggerResize } from '@refinitiv-ui/core';
3
3
  import { customElement } from '@refinitiv-ui/core/decorators/custom-element.js';
4
4
  import { property } from '@refinitiv-ui/core/decorators/property.js';
5
5
  import { query } from '@refinitiv-ui/core/decorators/query.js';
@@ -21,6 +21,7 @@ import { VERSION } from '../version.js';
21
21
  export { TreeSelectRenderer };
22
22
  const MEMO_THROTTLE = 16;
23
23
  const POPUP_POSITION = ['bottom-start', 'top-start'];
24
+ const valueFormatWarning = new WarningNotice("The specified 'values' format does not conform to the required format.");
24
25
  /**
25
26
  * Dropdown control that allows selection from the tree list
26
27
  *
@@ -65,6 +66,10 @@ let TreeSelect = class TreeSelect extends ComboBox {
65
66
  * Extracted values from {@link this.checkedGroupedItems}
66
67
  */
67
68
  this.pillsData = [];
69
+ /**
70
+ * Cache old selection timestamps for revert selected timestamps when cancel selection
71
+ */
72
+ this.oldSelection = new Map();
68
73
  /**
69
74
  * Are there pills visible
70
75
  */
@@ -165,8 +170,26 @@ let TreeSelect = class TreeSelect extends ComboBox {
165
170
  return this._values;
166
171
  }
167
172
  set values(values) {
168
- super.values = values;
169
- this._values = values;
173
+ if (!Array.isArray(values)) {
174
+ valueFormatWarning.show();
175
+ this.values = [];
176
+ return;
177
+ }
178
+ // Update the selection state when found new value
179
+ const newValues = values.slice(0, this.multiple ? values.length : 1);
180
+ const oldValues = this.values.slice();
181
+ if (newValues.toString() !== oldValues.toString()) {
182
+ this._values = values;
183
+ this.updateComposerValues(values);
184
+ this.updatePills();
185
+ if (this.freeText) {
186
+ // free text mode is only supported in single selection mode
187
+ // so if there is no valid selection in the composer, we can assume
188
+ // the first item can be used as the free text item.
189
+ this.freeTextValue = !this.composerValues.length ? newValues[0] : '';
190
+ }
191
+ this.requestUpdate('values', oldValues);
192
+ }
170
193
  }
171
194
  /**
172
195
  * Set maximum number of selected items
@@ -228,7 +251,7 @@ let TreeSelect = class TreeSelect extends ComboBox {
228
251
  * @override
229
252
  */
230
253
  get selectedLabels() {
231
- return this.checkedGroupedItems.map((selected) => this.getItemPropertyValue(selected, 'label') || '');
254
+ return this.checkedGroupedItems.map((selected) => this.getItemPropertyValue(selected, 'label') ?? '');
232
255
  }
233
256
  /**
234
257
  * Returns memoized selected state
@@ -332,29 +355,37 @@ let TreeSelect = class TreeSelect extends ComboBox {
332
355
  * @returns {void}
333
356
  */
334
357
  persistSelection() {
335
- const oldValues = this.values.slice();
358
+ const oldValues = this.values;
336
359
  const newValues = this.composerValues;
337
- const oldComparison = oldValues.sort().toString();
338
- const newComparison = newValues.sort().toString();
339
- if (oldComparison !== newComparison) {
360
+ if (oldValues.toString() !== newValues.toString()) {
361
+ // Set new values order by sequential selection
340
362
  this.values = newValues;
341
363
  this.notifyPropertyChange('value', this.value);
342
364
  }
343
365
  }
344
366
  /**
345
- * Reverse selection. Run on Esc or Cancel
367
+ * Revert modified selection to old values. Run on Esc or Cancel
346
368
  * @returns {void}
347
369
  */
348
- cancelSelection() {
349
- const oldValues = this.values.slice();
370
+ revertModifiedSelection() {
371
+ const oldValues = this.values;
350
372
  const newValues = this.composerValues;
351
- const oldComparison = oldValues.sort().toString();
352
- const newComparison = newValues.sort().toString();
353
- if (oldComparison !== newComparison) {
354
- // revert selected item by updating the collection composer
355
- this.updateComposerValues(this._values);
373
+ if (oldValues.toString() !== newValues.toString()) {
374
+ // Revert selected item by updating the collection composer
375
+ this.updateComposerValues(oldValues);
356
376
  }
357
377
  }
378
+ /**
379
+ * Revert all selected timestamps to the previous state which affect to item sorting
380
+ * @returns {void}
381
+ */
382
+ revertSelectionTimestamps() {
383
+ this.selection.forEach((item) => {
384
+ const oldSelectedAt = this.oldSelection.get(item.value);
385
+ this.composer.setItemPropertyValue(item, 'selectedAt', oldSelectedAt);
386
+ });
387
+ this.oldSelection.clear(); // Clear cache old selection
388
+ }
358
389
  /**
359
390
  * Update memoized track
360
391
  *
@@ -419,6 +450,7 @@ let TreeSelect = class TreeSelect extends ComboBox {
419
450
  const event = new CustomEvent('cancel');
420
451
  this.dispatchEvent(event);
421
452
  if (!event.defaultPrevented) {
453
+ this.revertSelectionTimestamps();
422
454
  this.closeAndReset();
423
455
  // reset always happens on popup close action
424
456
  }
@@ -487,6 +519,10 @@ let TreeSelect = class TreeSelect extends ComboBox {
487
519
  onPopupOpened() {
488
520
  super.onPopupOpened();
489
521
  this.clearSelectionFilter();
522
+ // Cache current selection timestamps for reverting modified selection when user cancel
523
+ this.selection.forEach((item) => {
524
+ this.oldSelection.set(item.value, this.getItemPropertyValue(item, 'selectedAt'));
525
+ });
490
526
  this.updatePills();
491
527
  this.updateMemo();
492
528
  }
@@ -503,9 +539,8 @@ let TreeSelect = class TreeSelect extends ComboBox {
503
539
  */
504
540
  onPopupClosed() {
505
541
  super.onPopupClosed();
506
- this.updateMemo();
507
- this.cancelSelection();
508
- this.exitEditSelection();
542
+ this.exitEditSelection(); // selection filter should be removed before reverting modified selection
543
+ this.revertModifiedSelection();
509
544
  }
510
545
  /**
511
546
  * Filter the internal items by query. Changes items' hidden state.
@@ -733,7 +768,7 @@ let TreeSelect = class TreeSelect extends ComboBox {
733
768
  */
734
769
  updatePills() {
735
770
  if (this.showPills) {
736
- this.pillsData = this.checkedGroupedItems.slice();
771
+ this.pillsData = this.checkedGroupedItems;
737
772
  this.hasPills = !!this.pillsData.length;
738
773
  }
739
774
  }
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '7.4.0';
1
+ export const VERSION = '7.6.0';
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@refinitiv-ui/elements",
3
- "version": "7.4.0",
3
+ "version": "7.6.0",
4
4
  "description": "Element Framework Elements",
5
- "author": "Refinitiv",
5
+ "author": "LSEG",
6
6
  "license": "Apache-2.0",
7
7
  "main": "./lib/index.js",
8
8
  "module": "./lib/index.js",
@@ -341,8 +341,8 @@
341
341
  },
342
342
  "dependencies": {
343
343
  "@lit-labs/context": "^0.3.1",
344
- "@refinitiv-ui/halo-theme": "^7.1.0",
345
- "@refinitiv-ui/solar-theme": "^7.1.0",
344
+ "@refinitiv-ui/halo-theme": "^7.1.1",
345
+ "@refinitiv-ui/solar-theme": "^7.1.1",
346
346
  "chart.js": "^4.3.0",
347
347
  "chartjs-adapter-date-fns": "^3.0.0",
348
348
  "d3-interpolate": "^3.0.1",
@@ -351,25 +351,25 @@
351
351
  "tslib": "^2.3.1"
352
352
  },
353
353
  "devDependencies": {
354
- "@refinitiv-ui/core": "^7.1.0",
355
- "@refinitiv-ui/demo-block": "^7.0.5",
356
- "@refinitiv-ui/i18n": "^7.0.3",
357
- "@refinitiv-ui/phrasebook": "^7.0.3",
358
- "@refinitiv-ui/test-helpers": "^7.0.3",
359
- "@refinitiv-ui/translate": "^7.0.3",
360
- "@refinitiv-ui/utils": "^7.0.3",
354
+ "@refinitiv-ui/core": "^7.2.0",
355
+ "@refinitiv-ui/demo-block": "^7.0.6",
356
+ "@refinitiv-ui/i18n": "^7.0.4",
357
+ "@refinitiv-ui/phrasebook": "^7.0.4",
358
+ "@refinitiv-ui/test-helpers": "^7.0.4",
359
+ "@refinitiv-ui/translate": "^7.0.4",
360
+ "@refinitiv-ui/utils": "^7.1.0",
361
361
  "@types/d3-interpolate": "^3.0.1"
362
362
  },
363
363
  "peerDependencies": {
364
364
  "@refinitiv-ui/browser-sparkline": "^1.0.0 || ^2.0.0",
365
- "@refinitiv-ui/core": "^7.1.0",
366
- "@refinitiv-ui/i18n": "^7.0.3",
367
- "@refinitiv-ui/phrasebook": "^7.0.3",
368
- "@refinitiv-ui/translate": "^7.0.3",
369
- "@refinitiv-ui/utils": "^7.0.3"
365
+ "@refinitiv-ui/core": "^7.2.0",
366
+ "@refinitiv-ui/i18n": "^7.0.4",
367
+ "@refinitiv-ui/phrasebook": "^7.0.4",
368
+ "@refinitiv-ui/translate": "^7.0.4",
369
+ "@refinitiv-ui/utils": "^7.1.0"
370
370
  },
371
371
  "publishConfig": {
372
372
  "access": "public"
373
373
  },
374
- "gitHead": "d220dde5331f239eb4d3ca99ed4972ff3d290f9a"
374
+ "gitHead": "4b172b7204c0d71ba30a01777ec1a00d0e35996a"
375
375
  }