@refinitiv-ui/elements 7.4.0 → 7.5.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [7.5.0](https://github.com/Refinitiv/refinitiv-ui/compare/@refinitiv-ui/elements@7.4.0...@refinitiv-ui/elements@7.5.0) (2023-09-18)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **slider:** incorrect updating `to` value ([#938](https://github.com/Refinitiv/refinitiv-ui/issues/938)) ([8479abc](https://github.com/Refinitiv/refinitiv-ui/commit/8479abc828571e06379b5f389058f60d5ba8443b))
11
+ - **slider:** number field not revalidate when error state changes ([#942](https://github.com/Refinitiv/refinitiv-ui/issues/942)) ([5fc64a6](https://github.com/Refinitiv/refinitiv-ui/commit/5fc64a6ac21cdbbf820ae97de04ec19443755e50))
12
+
13
+ ### Features
14
+
15
+ - **fields:** improve validation consistency for field elements ([#937](https://github.com/Refinitiv/refinitiv-ui/issues/937)) ([d58051c](https://github.com/Refinitiv/refinitiv-ui/commit/d58051c0b4d3508ac55d223299fd8bf267458ddf))
16
+ - **utils:** add `selectedAt` property to `DataItem` interface ([4d69419](https://github.com/Refinitiv/refinitiv-ui/commit/4d69419107d8ed8dd554cb7cf0ecadd327d02f18))
17
+
6
18
  # [7.4.0](https://github.com/Refinitiv/refinitiv-ui/compare/@refinitiv-ui/elements@7.3.2...@refinitiv-ui/elements@7.4.0) (2023-09-11)
7
19
 
8
20
  ### Features
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2023, Refinitiv.
1
+ Copyright © 2023, LSEG
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Element Framework
2
2
 
3
- Element Framework (EF) is a collection of elements that includes theming capability within the Refinitiv Workspace's design system. The elements are a set of web components which is a standard web technology and can be utilized across all browsers.
3
+ Element Framework (EF) is a collection of elements that includes theming capability within the LSEG Workspace's design system. The elements are a set of web components which is a standard web technology and can be utilized across all browsers.
4
4
 
5
5
  ## Usage
6
6
 
@@ -10,17 +10,17 @@ EF elements are published under single package.
10
10
  npm install @refinitiv-ui/elements
11
11
  ```
12
12
 
13
- The elements are required theme to instantiate itself in the app. Refinitiv Workspace's design system is called Halo theme and you can install it from npm command.
13
+ The elements are required theme to instantiate itself in the app. LSEG Workspace's design system is called Halo theme and you can install it from npm command.
14
14
 
15
15
  ```sh
16
16
  npm install @refinitiv-ui/halo-theme
17
17
  ```
18
18
 
19
- Finally, import both elements that you want to use and its themes into your application and it is ready to go. To follow Refinitiv Workspace's design system, it is required styles of some native elements e.g. typography.
19
+ Finally, import both elements that you want to use and its themes into your application and it is ready to go. To follow LSEG Workspace's design system, it is required styles of some native elements e.g. typography.
20
20
 
21
21
  <br>
22
22
 
23
- > The font "Proxima Nova Fin" shall only be used within Refinitiv products or services. The copyright owner must approve any use of such font outside of Refinitiv products or services, which may be subject to a fee. Please see https://www.fontspring.com/lic/fontspring/webfont#license_text
23
+ > The font "Proxima Nova Fin" shall only be used within LSEG products or services. The copyright owner must approve any use of such font outside of LSEG products or services, which may be subject to a fee. Please see https://www.fontspring.com/lic/fontspring/webfont#license_text
24
24
 
25
25
  <br>
26
26
 
@@ -57,4 +57,4 @@ See list of elements, demo and more tutorial by visiting [EF Documentation](http
57
57
 
58
58
  # License
59
59
 
60
- Apache License 2.0. However, Halo theme shall only be used within Refinitiv products or services due to license of the font "Proxima Nova Fin".
60
+ Apache License 2.0. However, Halo theme shall only be used within LSEG products or services due to license of the font "Proxima Nova Fin".
@@ -288,9 +288,11 @@ let ComboBox = class ComboBox extends FormFieldElement {
288
288
  * @returns {void}
289
289
  */
290
290
  updateComposerValues(newValues) {
291
+ const selectedAt = Date.now();
291
292
  this.queryItems((item, composer) => {
292
293
  if (newValues.includes(composer.getItemPropertyValue(item, 'value'))) {
293
294
  composer.setItemPropertyValue(item, 'selected', true);
295
+ composer.setItemPropertyValue(item, 'selectedAt', selectedAt + newValues.indexOf(item.value ?? '')); // Sequential selection
294
296
  return true;
295
297
  }
296
298
  composer.setItemPropertyValue(item, 'selected', false);
@@ -258,11 +258,6 @@
258
258
  "name": "checkValidity",
259
259
  "description": "Returns true if an input element contains valid data.",
260
260
  "params": []
261
- },
262
- {
263
- "name": "reportValidity",
264
- "description": "Validate input. Mark as error if input is invalid",
265
- "params": []
266
261
  }
267
262
  ]
268
263
  }
@@ -37,11 +37,10 @@ A form control element for datetime input.
37
37
 
38
38
  ## Methods
39
39
 
40
- | Method | Type | Description |
41
- |------------------|----------------------------------|--------------------------------------------------|
42
- | `checkValidity` | `(): boolean` | Returns true if an input element contains valid data. |
43
- | `reportValidity` | `(): boolean` | Validate input. Mark as error if input is invalid |
44
- | `willUpdate` | `(changedProperties: any): void` | Updates the element<br /><br />**changedProperties**: Properties that has changed |
40
+ | Method | Type | Description |
41
+ |-----------------|----------------------------------|--------------------------------------------------|
42
+ | `checkValidity` | `(): boolean` | Returns true if an input element contains valid data. |
43
+ | `willUpdate` | `(changedProperties: any): void` | Updates the element<br /><br />**changedProperties**: Properties that has changed |
45
44
 
46
45
  ## Events
47
46
 
@@ -186,18 +186,6 @@ export declare class DatetimeField extends TextField {
186
186
  * @returns {void}
187
187
  */
188
188
  protected syncInputValue(changedProperties?: PropertyValues): void;
189
- /**
190
- * Check if input should be re-validated
191
- * @param changedProperties Properties that has changed
192
- * @returns True if input should be re-validated
193
- */
194
- protected shouldValidateInput(changedProperties: PropertyValues): boolean;
195
- /**
196
- * Validate input according `pattern`, `minLength` and `maxLength` properties
197
- * change state of `error` property according pattern validation
198
- * @returns {void}
199
- */
200
- protected validateInput(): void;
201
189
  /**
202
190
  * Override validation method for value
203
191
  * @param value value
@@ -244,21 +232,11 @@ export declare class DatetimeField extends TextField {
244
232
  * @returns {void}
245
233
  */
246
234
  protected setValueAndNotify(value: string): void;
247
- /**
248
- * Reset error state on input
249
- * @returns {void}
250
- */
251
- protected resetError(): void;
252
235
  /**
253
236
  * Returns true if an input element contains valid data.
254
237
  * @returns true if input is valid
255
238
  */
256
239
  checkValidity(): boolean;
257
- /**
258
- * Validate input. Mark as error if input is invalid
259
- * @returns false if there is an error
260
- */
261
- reportValidity(): boolean;
262
240
  /**
263
241
  * Select part
264
242
  * @param parts The list of parts
@@ -294,23 +294,6 @@ let DatetimeField = class DatetimeField extends TextField {
294
294
  this.inputValue = inputValue; // setting the value resets selection
295
295
  }
296
296
  }
297
- /**
298
- * Check if input should be re-validated
299
- * @param changedProperties Properties that has changed
300
- * @returns True if input should be re-validated
301
- */
302
- shouldValidateInput(changedProperties) {
303
- // TODO: this needs refactoring with all other fields to support common validation patterns
304
- return changedProperties.has(FocusedPropertyKey) && !this.focused;
305
- }
306
- /**
307
- * Validate input according `pattern`, `minLength` and `maxLength` properties
308
- * change state of `error` property according pattern validation
309
- * @returns {void}
310
- */
311
- validateInput() {
312
- this.reportValidity();
313
- }
314
297
  /**
315
298
  * Override validation method for value
316
299
  * @param value value
@@ -399,15 +382,6 @@ let DatetimeField = class DatetimeField extends TextField {
399
382
  this.notifyPropertyChange('value', value);
400
383
  }
401
384
  }
402
- /**
403
- * Reset error state on input
404
- * @returns {void}
405
- */
406
- resetError() {
407
- if (this.error && this.checkValidity()) {
408
- this.reportValidity();
409
- }
410
- }
411
385
  /**
412
386
  * Returns true if an input element contains valid data.
413
387
  * @returns true if input is valid
@@ -433,15 +407,6 @@ let DatetimeField = class DatetimeField extends TextField {
433
407
  }
434
408
  return true;
435
409
  }
436
- /**
437
- * Validate input. Mark as error if input is invalid
438
- * @returns false if there is an error
439
- */
440
- reportValidity() {
441
- const hasError = !this.checkValidity();
442
- this.notifyErrorChange(hasError);
443
- return !hasError;
444
- }
445
410
  /**
446
411
  * Select part
447
412
  * @param parts The list of parts
@@ -527,7 +492,7 @@ let DatetimeField = class DatetimeField extends TextField {
527
492
  this.setValueAndNotify(newValue);
528
493
  this.syncInputValue();
529
494
  this.selectPart(this.formatToParts(newValue), selectedPartIndex);
530
- this.resetError();
495
+ this.reportValidity();
531
496
  event.preventDefault();
532
497
  }
533
498
  break;
@@ -547,8 +512,7 @@ let DatetimeField = class DatetimeField extends TextField {
547
512
  */
548
513
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
549
514
  onPossibleValueChange(event) {
550
- // Nobody likes to see a red border
551
- this.resetError();
515
+ this.reportValidity();
552
516
  this.selectPartFrame.cancel(); // ensure no pending selection
553
517
  this.partLabel = '';
554
518
  const inputValue = this.inputElement?.value || '';
@@ -1,5 +1,4 @@
1
1
  import { JSXInterface } from '../jsx';
2
- import { PropertyValues } from '@refinitiv-ui/core';
3
2
  import { TemplateMap } from '@refinitiv-ui/core/directives/template-map.js';
4
3
  import '../icon/index.js';
5
4
  import { TextField } from '../text-field/index.js';
@@ -57,12 +56,6 @@ export declare class EmailField extends TextField {
57
56
  * @returns template map
58
57
  */
59
58
  protected get decorateInputMap(): TemplateMap;
60
- /**
61
- * Check if input should be re-validated
62
- * @param changedProperties Properties that has changed
63
- * @returns True if input should be re-validated
64
- */
65
- protected shouldValidateInput(changedProperties: PropertyValues): boolean;
66
59
  }
67
60
  declare global {
68
61
  interface HTMLElementTagNameMap {
@@ -52,7 +52,6 @@ let EmailField = class EmailField extends TextField {
52
52
  * Set to multiple mode, allows multiple emails in a single input
53
53
  */
54
54
  this.multiple = false;
55
- /* c8 ignore stop */
56
55
  }
57
56
  /**
58
57
  * Decorate `<input>` element with common properties extended from text-field:
@@ -68,18 +67,6 @@ let EmailField = class EmailField extends TextField {
68
67
  multiple: this.multiple
69
68
  };
70
69
  }
71
- /**
72
- * Check if input should be re-validated
73
- * @param changedProperties Properties that has changed
74
- * @returns True if input should be re-validated
75
- */
76
- /* c8 ignore start */
77
- shouldValidateInput(changedProperties) {
78
- // TODO: This validation should be refactored
79
- return (changedProperties.has('value') ||
80
- changedProperties.has('multiple') ||
81
- super.shouldValidateInput(changedProperties));
82
- }
83
70
  };
84
71
  __decorate([
85
72
  property({ type: Boolean, reflect: true })
@@ -196,11 +196,6 @@
196
196
  "name": "checkValidity",
197
197
  "description": "Returns true if an input element contains valid data.",
198
198
  "params": []
199
- },
200
- {
201
- "name": "reportValidity",
202
- "description": "Validate input. Mark as error if input is invalid",
203
- "params": []
204
199
  }
205
200
  ]
206
201
  }
@@ -21,12 +21,11 @@ Form control element for numbers.
21
21
 
22
22
  ## Methods
23
23
 
24
- | Method | Type | Description |
25
- |------------------|-----------------------------------------------|--------------------------------------------------|
26
- | `checkValidity` | `(): boolean` | Returns true if an input element contains valid data. |
27
- | `reportValidity` | `(): boolean` | Validate input. Mark as error if input is invalid |
28
- | `stepDown` | `(stepIncrement?: number \| undefined): void` | Decreases the input value by amount of step<br /><br />**stepIncrement**: The stepIncrement parameter is a numeric value. If no parameter is passed, stepIncrement defaults to 1. |
29
- | `stepUp` | `(stepIncrement?: number \| undefined): void` | Increases the input value by amount of step<br /><br />**stepIncrement**: The stepIncrement parameter is a numeric value. If no parameter is passed, stepIncrement defaults to 1. |
24
+ | Method | Type | Description |
25
+ |-----------------|-----------------------------------------------|--------------------------------------------------|
26
+ | `checkValidity` | `(): boolean` | Returns true if an input element contains valid data. |
27
+ | `stepDown` | `(stepIncrement?: number \| undefined): void` | Decreases the input value by amount of step<br /><br />**stepIncrement**: The stepIncrement parameter is a numeric value. If no parameter is passed, stepIncrement defaults to 1. |
28
+ | `stepUp` | `(stepIncrement?: number \| undefined): void` | Increases the input value by amount of step<br /><br />**stepIncrement**: The stepIncrement parameter is a numeric value. If no parameter is passed, stepIncrement defaults to 1. |
30
29
 
31
30
  ## Events
32
31
 
@@ -209,11 +209,6 @@ export declare class NumberField extends FormFieldElement {
209
209
  * @returns {void}
210
210
  */
211
211
  private setSilentlyValueAndNotify;
212
- /**
213
- * Reset error state on input
214
- * @returns {void}
215
- */
216
- private resetError;
217
212
  /**
218
213
  * Get the allowed step value
219
214
  * @returns allowed step
@@ -271,11 +266,6 @@ export declare class NumberField extends FormFieldElement {
271
266
  * @returns true if input is valid
272
267
  */
273
268
  checkValidity(): boolean;
274
- /**
275
- * Validate input. Mark as error if input is invalid
276
- * @returns false if there is an error
277
- */
278
- reportValidity(): boolean;
279
269
  /**
280
270
  * @ignore
281
271
  * @inheritDoc
@@ -138,8 +138,6 @@ let NumberField = class NumberField extends FormFieldElement {
138
138
  * @returns {void}
139
139
  */
140
140
  update(changedProperties) {
141
- // This code probably should not be here, as validation must be instantiated by the app developer
142
- // Keep the element inline with others for now
143
141
  if (changedProperties.has(FocusedPropertyKey) && !this.focused) {
144
142
  this.reportValidity();
145
143
  }
@@ -418,7 +416,7 @@ let NumberField = class NumberField extends FormFieldElement {
418
416
  */
419
417
  setSilentlyValueAndNotify() {
420
418
  // Nobody likes to see a red border
421
- this.resetError();
419
+ this.reportValidity();
422
420
  const value = this.valueAsNumberString(this.inputValue);
423
421
  if (super.value !== value) {
424
422
  // here we must set the value silently to avoid re-rendering of input
@@ -426,15 +424,6 @@ let NumberField = class NumberField extends FormFieldElement {
426
424
  this.notifyPropertyChange('value', value);
427
425
  }
428
426
  }
429
- /**
430
- * Reset error state on input
431
- * @returns {void}
432
- */
433
- resetError() {
434
- if (this.error && this.checkValidity()) {
435
- this.reportValidity();
436
- }
437
- }
438
427
  /**
439
428
  * Get the allowed step value
440
429
  * @returns allowed step
@@ -645,18 +634,6 @@ let NumberField = class NumberField extends FormFieldElement {
645
634
  }
646
635
  return true;
647
636
  }
648
- /**
649
- * Validate input. Mark as error if input is invalid
650
- * @returns false if there is an error
651
- */
652
- reportValidity() {
653
- const hasError = !this.checkValidity();
654
- if (this.error !== hasError) {
655
- this.error = hasError;
656
- this.notifyPropertyChange('error', this.error);
657
- }
658
- return !hasError;
659
- }
660
637
  /**
661
638
  * @ignore
662
639
  * @inheritDoc
@@ -177,7 +177,7 @@
177
177
  },
178
178
  {
179
179
  "name": "input",
180
- "description": "Fired when the user inputs a value by interacting with the slider or updating its input field."
180
+ "description": "Fired with the value of the input in `e.detail.value` like another custom events when the user inputs a value by interacting with the slider or updating its input field."
181
181
  },
182
182
  {
183
183
  "name": "from-input",
@@ -25,7 +25,7 @@ Allows users to make selections from a range of values
25
25
  |-----------------|--------------------------------------------------|
26
26
  | `from-changed` | Fired when the user changes from's value. The event is not triggered if `from` property is changed programmatically. |
27
27
  | `from-input` | Fired when the user inputs from's value by interacting with the slider or updating its input field. |
28
- | `input` | Fired when the user inputs a value by interacting with the slider or updating its input field. |
28
+ | `input` | Fired with the value of the input in `e.detail.value` like another custom events when the user inputs a value by interacting with the slider or updating its input field. |
29
29
  | `to-changed` | Fired when the user changes to's value. The event is not triggered if `to` property is changed programmatically. |
30
30
  | `to-input` | Fired when the user inputs to's value by interacting with the slider or updating its input field. |
31
31
  | `value-changed` | Fired when the user commits a value change. The event is not triggered if `value` property is changed programmatically. |
@@ -18,7 +18,7 @@ import '../number-field/index.js';
18
18
  * @fires value-changed - Fired when the user commits a value change. The event is not triggered if `value` property is changed programmatically.
19
19
  * @fires from-changed - Fired when the user changes from's value. The event is not triggered if `from` property is changed programmatically.
20
20
  * @fires to-changed - Fired when the user changes to's value. The event is not triggered if `to` property is changed programmatically.
21
- * @fires input - Fired when the user inputs a value by interacting with the slider or updating its input field.
21
+ * @fires input - Fired with the value of the input in `e.detail.value` like another custom events when the user inputs a value by interacting with the slider or updating its input field.
22
22
  * @fires from-input - Fired when the user inputs from's value by interacting with the slider or updating its input field.
23
23
  * @fires to-input - Fired when the user inputs to's value by interacting with the slider or updating its input field.
24
24
  */
@@ -341,6 +341,11 @@ export declare class Slider extends ControlElement {
341
341
  * @returns validated to value.
342
342
  */
343
343
  private validateTo;
344
+ /**
345
+ * Validate number field from changed thumb
346
+ * @returns {void}
347
+ */
348
+ private validateNumberField;
344
349
  /**
345
350
  * Calculate the nearest possible step value depending on step interval
346
351
  * @param thumbPosition current thumb position in fraction
@@ -27,7 +27,7 @@ import { clamp, countDecimalPlace, isDecimalNumber, preventDefault } from './uti
27
27
  * @fires value-changed - Fired when the user commits a value change. The event is not triggered if `value` property is changed programmatically.
28
28
  * @fires from-changed - Fired when the user changes from's value. The event is not triggered if `from` property is changed programmatically.
29
29
  * @fires to-changed - Fired when the user changes to's value. The event is not triggered if `to` property is changed programmatically.
30
- * @fires input - Fired when the user inputs a value by interacting with the slider or updating its input field.
30
+ * @fires input - Fired with the value of the input in `e.detail.value` like another custom events when the user inputs a value by interacting with the slider or updating its input field.
31
31
  * @fires from-input - Fired when the user inputs from's value by interacting with the slider or updating its input field.
32
32
  * @fires to-input - Fired when the user inputs to's value by interacting with the slider or updating its input field.
33
33
  */
@@ -762,6 +762,7 @@ let Slider = class Slider extends ControlElement {
762
762
  this.valuePreviousInput = this.value;
763
763
  }
764
764
  this.onDrag(event);
765
+ this.validateNumberField();
765
766
  if (event.changedTouches) {
766
767
  this.addEventListener('touchmove', this.onDrag);
767
768
  this.addEventListener('touchend', this.onDragEnd);
@@ -855,6 +856,17 @@ let Slider = class Slider extends ControlElement {
855
856
  }
856
857
  return this.fromNumber + this.minRangeNumber;
857
858
  }
859
+ /**
860
+ * Validate number field from changed thumb
861
+ * @returns {void}
862
+ */
863
+ validateNumberField() {
864
+ if (this.isShowInputField) {
865
+ const name = this.changedThumb?.getAttribute('name');
866
+ const numberField = this[`${name}Input`];
867
+ requestAnimationFrame(() => numberField.reportValidity());
868
+ }
869
+ }
858
870
  /**
859
871
  * Calculate the nearest possible step value depending on step interval
860
872
  * @param thumbPosition current thumb position in fraction
@@ -996,7 +1008,7 @@ let Slider = class Slider extends ControlElement {
996
1008
  if (valueFor === SliderDataName.to && value < this.fromNumber + this.minRangeNumber) {
997
1009
  return false;
998
1010
  }
999
- else if (value > this.toNumber - this.minRangeNumber) {
1011
+ else if (valueFor === SliderDataName.from && value > this.toNumber - this.minRangeNumber) {
1000
1012
  return false;
1001
1013
  }
1002
1014
  }
@@ -68,6 +68,12 @@ export declare class TextField extends FormFieldElement {
68
68
  * @returns {void}
69
69
  */
70
70
  protected firstUpdated(changedProperties: PropertyValues): void;
71
+ /**
72
+ * Updates the element
73
+ * @param changedProperties Properties that has changed
74
+ * @returns {void}
75
+ */
76
+ protected update(changedProperties: PropertyValues): void;
71
77
  /**
72
78
  * Called when the element’s DOM has been updated and rendered
73
79
  * @param changedProperties Properties that has changed
@@ -86,13 +92,7 @@ export declare class TextField extends FormFieldElement {
86
92
  * @param changedProperties Properties that has changed
87
93
  * @returns {void}
88
94
  */
89
- protected syncInputValue(changedProperties: PropertyValues): void;
90
- /**
91
- * Check if input should be re-validated
92
- * @param changedProperties Properties that has changed
93
- * @returns True if input should be re-validated
94
- */
95
- protected shouldValidateInput(changedProperties: PropertyValues): boolean;
95
+ protected syncInputValue(): void;
96
96
  /**
97
97
  * Runs on input element `input` event
98
98
  * @param event `input` event
@@ -110,11 +110,6 @@ export declare class TextField extends FormFieldElement {
110
110
  * @returns {void}
111
111
  */
112
112
  protected onPossibleValueChange(event: InputEvent): void;
113
- /**
114
- * Uses native `checkValidity()` function to validate input
115
- * @returns {void}
116
- */
117
- protected validateInput(): void;
118
113
  /**
119
114
  * Fires event on `icon` click
120
115
  * @returns {void}
@@ -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,10 +127,7 @@ 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
  }
125
133
  /**
@@ -137,26 +145,9 @@ let TextField = class TextField extends FormFieldElement {
137
145
  * @param changedProperties Properties that has changed
138
146
  * @returns {void}
139
147
  */
140
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
141
- syncInputValue(changedProperties) {
148
+ syncInputValue() {
142
149
  this.inputValue = this.value;
143
150
  }
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
151
  /**
161
152
  * Runs on input element `input` event
162
153
  * @param event `input` event
@@ -181,14 +172,7 @@ let TextField = class TextField extends FormFieldElement {
181
172
  onPossibleValueChange(event) {
182
173
  const value = this.inputElement?.value || '';
183
174
  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);
175
+ this.reportValidity();
192
176
  }
193
177
  /**
194
178
  * 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
  }
@@ -504,7 +540,7 @@ let TreeSelect = class TreeSelect extends ComboBox {
504
540
  onPopupClosed() {
505
541
  super.onPopupClosed();
506
542
  this.updateMemo();
507
- this.cancelSelection();
543
+ this.revertModifiedSelection();
508
544
  this.exitEditSelection();
509
545
  }
510
546
  /**
@@ -733,7 +769,7 @@ let TreeSelect = class TreeSelect extends ComboBox {
733
769
  */
734
770
  updatePills() {
735
771
  if (this.showPills) {
736
- this.pillsData = this.checkedGroupedItems.slice();
772
+ this.pillsData = this.checkedGroupedItems;
737
773
  this.hasPills = !!this.pillsData.length;
738
774
  }
739
775
  }
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '7.4.0';
1
+ export const VERSION = '7.5.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.5.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": "e162ec78601e5f97201d53d740925ee2075fd95e"
375
375
  }