@ni/nimble-components 21.8.1 → 21.9.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/dist/all-components-bundle.js +226 -116
- package/dist/all-components-bundle.js.map +1 -1
- package/dist/all-components-bundle.min.js +1016 -980
- package/dist/all-components-bundle.min.js.map +1 -1
- package/dist/esm/list-option/index.d.ts +17 -0
- package/dist/esm/list-option/index.js +24 -1
- package/dist/esm/list-option/index.js.map +1 -1
- package/dist/esm/list-option/styles.js +15 -11
- package/dist/esm/list-option/styles.js.map +1 -1
- package/dist/esm/select/index.d.ts +10 -14
- package/dist/esm/select/index.js +143 -86
- package/dist/esm/select/index.js.map +1 -1
- package/dist/esm/select/styles.js +6 -1
- package/dist/esm/select/styles.js.map +1 -1
- package/dist/esm/select/template.js +3 -3
- package/dist/esm/select/template.js.map +1 -1
- package/dist/esm/select/testing/select.pageobject.d.ts +7 -0
- package/dist/esm/select/testing/select.pageobject.js +28 -12
- package/dist/esm/select/testing/select.pageobject.js.map +1 -1
- package/dist/esm/table/index.d.ts +4 -0
- package/dist/esm/table/index.js +10 -2
- package/dist/esm/table/index.js.map +1 -1
- package/dist/esm/table/models/table-layout-manager.d.ts +1 -1
- package/dist/esm/table/models/table-layout-manager.js +4 -0
- package/dist/esm/table/models/table-layout-manager.js.map +1 -1
- package/dist/esm/table/styles.js +12 -12
- package/dist/esm/table/styles.js.map +1 -1
- package/dist/esm/table/template.js +15 -3
- package/dist/esm/table/template.js.map +1 -1
- package/package.json +1 -1
|
@@ -10,6 +10,23 @@ declare global {
|
|
|
10
10
|
export declare class ListOption extends FoundationListboxOption {
|
|
11
11
|
/** @internal */
|
|
12
12
|
contentSlot: HTMLSlotElement;
|
|
13
|
+
/**
|
|
14
|
+
* The hidden state of the element.
|
|
15
|
+
*
|
|
16
|
+
* @public
|
|
17
|
+
* @defaultValue - false
|
|
18
|
+
* @remarks
|
|
19
|
+
* HTML Attribute: hidden
|
|
20
|
+
*/
|
|
21
|
+
hidden: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* @internal
|
|
24
|
+
* This attribute is required to allow use-cases that offer dynamic filtering
|
|
25
|
+
* (like the Select) to visually hide options that are filtered out, but still
|
|
26
|
+
* allow users to use the native 'hidden' attribute without it being affected
|
|
27
|
+
* by the filtering process.
|
|
28
|
+
*/
|
|
29
|
+
visuallyHidden: boolean;
|
|
13
30
|
/** @internal */
|
|
14
31
|
hasOverflow: boolean;
|
|
15
32
|
/** @internal */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { DesignSystem, ListboxOption as FoundationListboxOption } from '@microsoft/fast-foundation';
|
|
3
|
-
import { observable } from '@microsoft/fast-element';
|
|
3
|
+
import { observable, attr } from '@microsoft/fast-element';
|
|
4
4
|
import { styles } from './styles';
|
|
5
5
|
import { template } from './template';
|
|
6
6
|
/**
|
|
@@ -9,6 +9,23 @@ import { template } from './template';
|
|
|
9
9
|
export class ListOption extends FoundationListboxOption {
|
|
10
10
|
constructor() {
|
|
11
11
|
super(...arguments);
|
|
12
|
+
/**
|
|
13
|
+
* The hidden state of the element.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
* @defaultValue - false
|
|
17
|
+
* @remarks
|
|
18
|
+
* HTML Attribute: hidden
|
|
19
|
+
*/
|
|
20
|
+
this.hidden = false;
|
|
21
|
+
/**
|
|
22
|
+
* @internal
|
|
23
|
+
* This attribute is required to allow use-cases that offer dynamic filtering
|
|
24
|
+
* (like the Select) to visually hide options that are filtered out, but still
|
|
25
|
+
* allow users to use the native 'hidden' attribute without it being affected
|
|
26
|
+
* by the filtering process.
|
|
27
|
+
*/
|
|
28
|
+
this.visuallyHidden = false;
|
|
12
29
|
/** @internal */
|
|
13
30
|
this.hasOverflow = false;
|
|
14
31
|
}
|
|
@@ -20,6 +37,12 @@ export class ListOption extends FoundationListboxOption {
|
|
|
20
37
|
.join(' ');
|
|
21
38
|
}
|
|
22
39
|
}
|
|
40
|
+
__decorate([
|
|
41
|
+
attr({ mode: 'boolean' })
|
|
42
|
+
], ListOption.prototype, "hidden", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
attr({ attribute: 'visually-hidden', mode: 'boolean' })
|
|
45
|
+
], ListOption.prototype, "visuallyHidden", void 0);
|
|
23
46
|
__decorate([
|
|
24
47
|
observable
|
|
25
48
|
], ListOption.prototype, "hasOverflow", void 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/list-option/index.ts"],"names":[],"mappings":";AAAA,OAAO,EACH,YAAY,EACZ,aAAa,IAAI,uBAAuB,EAC3C,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/list-option/index.ts"],"names":[],"mappings":";AAAA,OAAO,EACH,YAAY,EACZ,aAAa,IAAI,uBAAuB,EAC3C,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAQtC;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,uBAAuB;IAAvD;;QAII;;;;;;;WAOG;QAEa,WAAM,GAAG,KAAK,CAAC;QAE/B;;;;;;WAMG;QAEI,mBAAc,GAAG,KAAK,CAAC;QAE9B,gBAAgB;QAET,gBAAW,GAAG,KAAK,CAAC;IAS/B,CAAC;IAPG,gBAAgB;IAChB,IAAW,kBAAkB;QACzB,OAAO,IAAI,CAAC,WAAW;aAClB,aAAa,EAAE;aACf,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;aACrC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;CACJ;AAvBG;IADC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;0CACK;AAU/B;IADC,IAAI,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;kDAC1B;AAI9B;IADC,UAAU;+CACgB;AAW/B,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,CAAC;IACxC,QAAQ,EAAE,aAAa;IACvB,SAAS,EAAE,uBAAuB;IAClC,QAAQ;IACR,MAAM;CACT,CAAC,CAAC;AAEH,YAAY,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAC7E,MAAM,CAAC,MAAM,aAAa,GAAG,oBAAoB,CAAC","sourcesContent":["import {\n DesignSystem,\n ListboxOption as FoundationListboxOption\n} from '@microsoft/fast-foundation';\nimport { observable, attr } from '@microsoft/fast-element';\nimport { styles } from './styles';\nimport { template } from './template';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'nimble-list-option': ListOption;\n }\n}\n\n/**\n * A nimble-styled HTML listbox option\n */\nexport class ListOption extends FoundationListboxOption {\n /** @internal */\n public contentSlot!: HTMLSlotElement;\n\n /**\n * The hidden state of the element.\n *\n * @public\n * @defaultValue - false\n * @remarks\n * HTML Attribute: hidden\n */\n @attr({ mode: 'boolean' })\n public override hidden = false;\n\n /**\n * @internal\n * This attribute is required to allow use-cases that offer dynamic filtering\n * (like the Select) to visually hide options that are filtered out, but still\n * allow users to use the native 'hidden' attribute without it being affected\n * by the filtering process.\n */\n @attr({ attribute: 'visually-hidden', mode: 'boolean' })\n public visuallyHidden = false;\n\n /** @internal */\n @observable\n public hasOverflow = false;\n\n /** @internal */\n public get elementTextContent(): string {\n return this.contentSlot\n .assignedNodes()\n .map(node => node.textContent?.trim())\n .join(' ');\n }\n}\n\nconst nimbleListOption = ListOption.compose({\n baseName: 'list-option',\n baseClass: FoundationListboxOption,\n template,\n styles\n});\n\nDesignSystem.getOrCreate().withPrefix('nimble').register(nimbleListOption());\nexport const listOptionTag = 'nimble-list-option';\n"]}
|
|
@@ -12,17 +12,6 @@ export const styles = css `
|
|
|
12
12
|
height: ${controlHeight};
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
[part='start'] {
|
|
16
|
-
display: none;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.content {
|
|
20
|
-
padding: 8px 4px;
|
|
21
|
-
white-space: nowrap;
|
|
22
|
-
overflow: hidden;
|
|
23
|
-
text-overflow: ellipsis;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
15
|
:host([aria-selected='true']) {
|
|
27
16
|
box-shadow: none;
|
|
28
17
|
outline: none;
|
|
@@ -59,6 +48,21 @@ export const styles = css `
|
|
|
59
48
|
cursor: default;
|
|
60
49
|
}
|
|
61
50
|
|
|
51
|
+
:host([visually-hidden]) {
|
|
52
|
+
display: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[part='start'] {
|
|
56
|
+
display: none;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.content {
|
|
60
|
+
padding: 8px 4px;
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
text-overflow: ellipsis;
|
|
64
|
+
}
|
|
65
|
+
|
|
62
66
|
.content[disabled] {
|
|
63
67
|
box-shadow: none;
|
|
64
68
|
outline: none;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/list-option/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EACH,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,QAAQ,EACR,qBAAqB,EACrB,aAAa,EAChB,MAAM,iCAAiC,CAAC;AAEzC,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,CAAA;MACnB,OAAO,CAAC,MAAM,CAAC;;;gBAGL,QAAQ;;;kBAGN,aAAa
|
|
1
|
+
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/list-option/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EACH,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,QAAQ,EACR,qBAAqB,EACrB,aAAa,EAChB,MAAM,iCAAiC,CAAC;AAEzC,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,CAAA;MACnB,OAAO,CAAC,MAAM,CAAC;;;gBAGL,QAAQ;;;kBAGN,aAAa;;;;;;4BAMH,iBAAiB;;;;4BAIjB,sBAAsB;;;;4BAItB,cAAc;;;;;;;;aAQ7B,YAAY;sCACa,gBAAgB;6BACzB,gBAAgB;;;;;;;4BAOjB,iBAAiB;;;;iBAI5B,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BrC,CAAC","sourcesContent":["import { css } from '@microsoft/fast-element';\nimport { display } from '@microsoft/fast-foundation';\nimport { focusVisible } from '../utilities/style/focus';\n\nimport {\n borderHoverColor,\n fillSelectedColor,\n fillHoverSelectedColor,\n fillHoverColor,\n bodyFont,\n bodyDisabledFontColor,\n controlHeight\n} from '../theme-provider/design-tokens';\n\nexport const styles = css`\n ${display('flex')}\n\n :host {\n font: ${bodyFont};\n cursor: pointer;\n justify-content: left;\n height: ${controlHeight};\n }\n\n :host([aria-selected='true']) {\n box-shadow: none;\n outline: none;\n background-color: ${fillSelectedColor};\n }\n\n :host([aria-selected='true']:hover) {\n background-color: ${fillHoverSelectedColor};\n }\n\n :host(:hover) {\n background-color: ${fillHoverColor};\n }\n\n :host(:hover):host([disabled]) {\n box-shadow: none;\n background-color: transparent;\n }\n\n :host(:${focusVisible}) {\n box-shadow: 0px 0px 0px 1px ${borderHoverColor} inset;\n outline: 1px solid ${borderHoverColor};\n outline-offset: -4px;\n }\n\n :host(:active) {\n box-shadow: none;\n outline: none;\n background-color: ${fillSelectedColor};\n }\n\n :host([disabled]) {\n color: ${bodyDisabledFontColor};\n cursor: default;\n }\n\n :host([visually-hidden]) {\n display: none;\n }\n\n [part='start'] {\n display: none;\n }\n\n .content {\n padding: 8px 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .content[disabled] {\n box-shadow: none;\n outline: none;\n }\n\n [part='end'] {\n display: none;\n }\n`;\n"]}
|
|
@@ -30,6 +30,10 @@ export declare class Select extends FormAssociatedSelect implements ErrorPattern
|
|
|
30
30
|
errorText: string | undefined;
|
|
31
31
|
errorVisible: boolean;
|
|
32
32
|
filterMode: FilterMode;
|
|
33
|
+
/**
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
displayPlaceholder: boolean;
|
|
33
37
|
/**
|
|
34
38
|
* @internal
|
|
35
39
|
*/
|
|
@@ -83,7 +87,7 @@ export declare class Select extends FormAssociatedSelect implements ErrorPattern
|
|
|
83
87
|
/**
|
|
84
88
|
* @internal
|
|
85
89
|
*/
|
|
86
|
-
committedSelectedOption
|
|
90
|
+
committedSelectedOption?: ListboxOption;
|
|
87
91
|
/**
|
|
88
92
|
* The max height for the listbox when opened.
|
|
89
93
|
*
|
|
@@ -103,16 +107,6 @@ export declare class Select extends FormAssociatedSelect implements ErrorPattern
|
|
|
103
107
|
* @internal
|
|
104
108
|
*/
|
|
105
109
|
connectedCallback(): void;
|
|
106
|
-
/**
|
|
107
|
-
* The list of options. This mirrors FAST's override implementation for this
|
|
108
|
-
* member for the Combobox to support a filtered list in the dropdown.
|
|
109
|
-
*
|
|
110
|
-
* @public
|
|
111
|
-
* @remarks
|
|
112
|
-
* Overrides `Listbox.options`.
|
|
113
|
-
*/
|
|
114
|
-
get options(): ListboxOption[];
|
|
115
|
-
set options(value: ListboxOption[]);
|
|
116
110
|
get value(): string;
|
|
117
111
|
set value(next: string);
|
|
118
112
|
/**
|
|
@@ -200,7 +194,7 @@ export declare class Select extends FormAssociatedSelect implements ErrorPattern
|
|
|
200
194
|
*
|
|
201
195
|
* @internal
|
|
202
196
|
*/
|
|
203
|
-
selectedIndexChanged(
|
|
197
|
+
selectedIndexChanged(_: number | undefined, __: number): void;
|
|
204
198
|
/**
|
|
205
199
|
* Synchronize the `aria-disabled` property when the `disabled` property changes.
|
|
206
200
|
*
|
|
@@ -216,6 +210,8 @@ export declare class Select extends FormAssociatedSelect implements ErrorPattern
|
|
|
216
210
|
* @internal
|
|
217
211
|
*/
|
|
218
212
|
formResetCallback(): void;
|
|
213
|
+
selectNextOption(): void;
|
|
214
|
+
selectPreviousOption(): void;
|
|
219
215
|
protected setSelectedOptions(): void;
|
|
220
216
|
protected focusAndScrollOptionIntoView(): void;
|
|
221
217
|
protected positionChanged(_: SelectPosition | undefined, next: SelectPosition | undefined): void;
|
|
@@ -239,7 +235,7 @@ export declare class Select extends FormAssociatedSelect implements ErrorPattern
|
|
|
239
235
|
* @override
|
|
240
236
|
* @internal
|
|
241
237
|
*/
|
|
242
|
-
protected selectedOptionsChanged(
|
|
238
|
+
protected selectedOptionsChanged(_prev: ListboxOption[] | undefined, next: ListboxOption[]): void;
|
|
243
239
|
/**
|
|
244
240
|
* Sets the selected index to match the first option with the selected attribute, or
|
|
245
241
|
* the first selectable option.
|
|
@@ -248,6 +244,7 @@ export declare class Select extends FormAssociatedSelect implements ErrorPattern
|
|
|
248
244
|
* @internal
|
|
249
245
|
*/
|
|
250
246
|
protected setDefaultSelectedOption(): void;
|
|
247
|
+
private committedSelectedOptionChanged;
|
|
251
248
|
private setPositioning;
|
|
252
249
|
/**
|
|
253
250
|
* Filter available options by text value.
|
|
@@ -274,7 +271,6 @@ export declare class Select extends FormAssociatedSelect implements ErrorPattern
|
|
|
274
271
|
private maxHeightChanged;
|
|
275
272
|
private initializeOpenState;
|
|
276
273
|
private updateListboxMaxHeightCssVariable;
|
|
277
|
-
private updateSelectedIndexFromFilteredSet;
|
|
278
274
|
}
|
|
279
275
|
export declare const selectTag = "nimble-select";
|
|
280
276
|
export {};
|
package/dist/esm/select/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
// Based on: https://github.com/microsoft/fast/blob/%40microsoft/fast-foundation_v2.49.5/packages/web-components/fast-foundation/src/select/select.ts
|
|
3
3
|
import { attr, html, observable, Observable, volatile } from '@microsoft/fast-element';
|
|
4
|
-
import { DesignSystem, Select as FoundationSelect, SelectPosition, applyMixins, StartEnd, DelegatesARIASelect
|
|
4
|
+
import { DesignSystem, Select as FoundationSelect, SelectPosition, applyMixins, StartEnd, DelegatesARIASelect } from '@microsoft/fast-foundation';
|
|
5
5
|
import { keyArrowDown, keyArrowUp, keyEnd, keyEnter, keyEscape, keyHome, keySpace, keyTab, uniqueId } from '@microsoft/fast-web-utilities';
|
|
6
6
|
import { arrowExpanderDown16X16 } from '@ni/nimble-tokens/dist/icons/js';
|
|
7
7
|
import { styles } from './styles';
|
|
@@ -9,9 +9,13 @@ import { DropdownAppearance } from '../patterns/dropdown/types';
|
|
|
9
9
|
import { errorTextTemplate } from '../patterns/error/template';
|
|
10
10
|
import { iconExclamationMarkTag } from '../icons/exclamation-mark';
|
|
11
11
|
import { template } from './template';
|
|
12
|
+
import { ListOption } from '../list-option';
|
|
12
13
|
import { FilterMode } from './types';
|
|
13
14
|
import { diacriticInsensitiveStringNormalizer } from '../utilities/models/string-normalizers';
|
|
14
15
|
import { FormAssociatedSelect } from './models/select-form-associated';
|
|
16
|
+
const isNimbleListOption = (el) => {
|
|
17
|
+
return el instanceof ListOption;
|
|
18
|
+
};
|
|
15
19
|
/**
|
|
16
20
|
* A nimble-styled HTML select.
|
|
17
21
|
*/
|
|
@@ -21,6 +25,10 @@ export class Select extends FormAssociatedSelect {
|
|
|
21
25
|
this.appearance = DropdownAppearance.underline;
|
|
22
26
|
this.errorVisible = false;
|
|
23
27
|
this.filterMode = FilterMode.none;
|
|
28
|
+
/**
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
this.displayPlaceholder = false;
|
|
24
32
|
/**
|
|
25
33
|
* @internal
|
|
26
34
|
*/
|
|
@@ -41,10 +49,6 @@ export class Select extends FormAssociatedSelect {
|
|
|
41
49
|
* @internal
|
|
42
50
|
*/
|
|
43
51
|
this.filter = '';
|
|
44
|
-
/**
|
|
45
|
-
* @internal
|
|
46
|
-
*/
|
|
47
|
-
this.committedSelectedOption = undefined;
|
|
48
52
|
/**
|
|
49
53
|
* The max height for the listbox when opened.
|
|
50
54
|
*
|
|
@@ -70,24 +74,6 @@ export class Select extends FormAssociatedSelect {
|
|
|
70
74
|
this.forcedPosition = !!this.positionAttribute;
|
|
71
75
|
this.initializeOpenState();
|
|
72
76
|
}
|
|
73
|
-
/**
|
|
74
|
-
* The list of options. This mirrors FAST's override implementation for this
|
|
75
|
-
* member for the Combobox to support a filtered list in the dropdown.
|
|
76
|
-
*
|
|
77
|
-
* @public
|
|
78
|
-
* @remarks
|
|
79
|
-
* Overrides `Listbox.options`.
|
|
80
|
-
*/
|
|
81
|
-
get options() {
|
|
82
|
-
Observable.track(this, 'options');
|
|
83
|
-
return this.filteredOptions?.length
|
|
84
|
-
? this.filteredOptions
|
|
85
|
-
: this._options;
|
|
86
|
-
}
|
|
87
|
-
set options(value) {
|
|
88
|
-
this._options = value;
|
|
89
|
-
Observable.notify(this, 'options');
|
|
90
|
-
}
|
|
91
77
|
get value() {
|
|
92
78
|
Observable.track(this, 'value');
|
|
93
79
|
return this._value;
|
|
@@ -95,8 +81,6 @@ export class Select extends FormAssociatedSelect {
|
|
|
95
81
|
set value(next) {
|
|
96
82
|
const prev = this._value;
|
|
97
83
|
let newValue = next;
|
|
98
|
-
// use 'options' here instead of '_options' as 'selectedIndex' may be relative
|
|
99
|
-
// to filtered set
|
|
100
84
|
if (this.options?.length) {
|
|
101
85
|
const newValueIndex = this.options.findIndex(el => el.value === newValue);
|
|
102
86
|
const prevSelectedValue = this.options[this.selectedIndex]?.value ?? null;
|
|
@@ -112,7 +96,7 @@ export class Select extends FormAssociatedSelect {
|
|
|
112
96
|
this._value = newValue;
|
|
113
97
|
super.valueChanged(prev, newValue);
|
|
114
98
|
if (!this.open) {
|
|
115
|
-
this.committedSelectedOption = this.
|
|
99
|
+
this.committedSelectedOption = this.options.find(o => o.value === newValue);
|
|
116
100
|
}
|
|
117
101
|
Observable.notify(this, 'value');
|
|
118
102
|
if (this.collapsible) {
|
|
@@ -148,14 +132,18 @@ export class Select extends FormAssociatedSelect {
|
|
|
148
132
|
*/
|
|
149
133
|
slottedOptionsChanged(prev, next) {
|
|
150
134
|
const value = this.value;
|
|
151
|
-
this.
|
|
135
|
+
this.options.forEach(o => {
|
|
152
136
|
const notifier = Observable.getNotifier(o);
|
|
153
137
|
notifier.unsubscribe(this, 'value');
|
|
138
|
+
notifier.unsubscribe(this, 'hidden');
|
|
139
|
+
notifier.unsubscribe(this, 'disabled');
|
|
154
140
|
});
|
|
155
141
|
super.slottedOptionsChanged(prev, next);
|
|
156
|
-
this.
|
|
142
|
+
this.options.forEach(o => {
|
|
157
143
|
const notifier = Observable.getNotifier(o);
|
|
158
144
|
notifier.subscribe(this, 'value');
|
|
145
|
+
notifier.subscribe(this, 'hidden');
|
|
146
|
+
notifier.subscribe(this, 'disabled');
|
|
159
147
|
});
|
|
160
148
|
this.setProxyOptions();
|
|
161
149
|
this.updateValue();
|
|
@@ -177,9 +165,6 @@ export class Select extends FormAssociatedSelect {
|
|
|
177
165
|
}
|
|
178
166
|
if (this.open) {
|
|
179
167
|
const captured = e.target.closest('option,[role=option]');
|
|
180
|
-
if (!captured?.disabled) {
|
|
181
|
-
this.updateSelectedIndexFromFilteredSet();
|
|
182
|
-
}
|
|
183
168
|
if (captured?.disabled) {
|
|
184
169
|
return;
|
|
185
170
|
}
|
|
@@ -200,9 +185,34 @@ export class Select extends FormAssociatedSelect {
|
|
|
200
185
|
* @override
|
|
201
186
|
*/
|
|
202
187
|
handleChange(source, propertyName) {
|
|
203
|
-
super.handleChange
|
|
204
|
-
|
|
205
|
-
|
|
188
|
+
// don't call super.handleChange so hidden options can be selected programmatically
|
|
189
|
+
const sourceElement = source;
|
|
190
|
+
switch (propertyName) {
|
|
191
|
+
case 'value': {
|
|
192
|
+
this.updateValue();
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case 'selected': {
|
|
196
|
+
if (isNimbleListOption(sourceElement)) {
|
|
197
|
+
this.selectedIndex = this.options.indexOf(sourceElement);
|
|
198
|
+
}
|
|
199
|
+
this.setSelectedOptions();
|
|
200
|
+
this.updateDisplayValue();
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case 'hidden': {
|
|
204
|
+
if (isNimbleListOption(sourceElement)) {
|
|
205
|
+
sourceElement.visuallyHidden = sourceElement.hidden;
|
|
206
|
+
}
|
|
207
|
+
this.updateDisplayValue();
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
case 'disabled': {
|
|
211
|
+
this.updateDisplayValue();
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
default:
|
|
215
|
+
break;
|
|
206
216
|
}
|
|
207
217
|
}
|
|
208
218
|
/**
|
|
@@ -253,6 +263,14 @@ export class Select extends FormAssociatedSelect {
|
|
|
253
263
|
* @internal
|
|
254
264
|
*/
|
|
255
265
|
updateDisplayValue() {
|
|
266
|
+
if (this.committedSelectedOption?.disabled
|
|
267
|
+
&& this.committedSelectedOption?.hidden
|
|
268
|
+
&& this.committedSelectedOption?.selected) {
|
|
269
|
+
this.displayPlaceholder = true;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
this.displayPlaceholder = false;
|
|
273
|
+
}
|
|
256
274
|
if (this.collapsible) {
|
|
257
275
|
Observable.notify(this, 'displayValue');
|
|
258
276
|
}
|
|
@@ -265,14 +283,9 @@ export class Select extends FormAssociatedSelect {
|
|
|
265
283
|
*/
|
|
266
284
|
inputHandler(e) {
|
|
267
285
|
this.filter = this.filterInput?.value ?? '';
|
|
268
|
-
if (!this.committedSelectedOption) {
|
|
269
|
-
this.committedSelectedOption = this._options.find(option => option.selected);
|
|
270
|
-
}
|
|
271
286
|
this.clearSelection();
|
|
272
287
|
this.filterOptions();
|
|
273
|
-
if (this.filteredOptions.length > 0
|
|
274
|
-
&& this.committedSelectedOption
|
|
275
|
-
&& !this.filteredOptions.includes(this.committedSelectedOption)) {
|
|
288
|
+
if (this.filteredOptions.length > 0) {
|
|
276
289
|
const enabledOptions = this.filteredOptions.filter(o => !o.disabled);
|
|
277
290
|
if (enabledOptions.length > 0) {
|
|
278
291
|
enabledOptions[0].selected = true;
|
|
@@ -296,7 +309,6 @@ export class Select extends FormAssociatedSelect {
|
|
|
296
309
|
* @internal
|
|
297
310
|
*/
|
|
298
311
|
focusoutHandler(e) {
|
|
299
|
-
this.updateSelectedIndexFromFilteredSet();
|
|
300
312
|
super.focusoutHandler(e);
|
|
301
313
|
if (!this.open) {
|
|
302
314
|
return true;
|
|
@@ -308,6 +320,9 @@ export class Select extends FormAssociatedSelect {
|
|
|
308
320
|
}
|
|
309
321
|
if (!this.options?.includes(focusTarget)) {
|
|
310
322
|
this.open = false;
|
|
323
|
+
if (this.selectedIndex === -1) {
|
|
324
|
+
this.selectedIndex = this.indexWhenOpened;
|
|
325
|
+
}
|
|
311
326
|
if (this.indexWhenOpened !== this.selectedIndex) {
|
|
312
327
|
this.updateValue(true);
|
|
313
328
|
}
|
|
@@ -349,7 +364,6 @@ export class Select extends FormAssociatedSelect {
|
|
|
349
364
|
|| this.filteredOptions.every(o => o.disabled)) {
|
|
350
365
|
return false;
|
|
351
366
|
}
|
|
352
|
-
this.updateSelectedIndexFromFilteredSet();
|
|
353
367
|
this.open = !this.open;
|
|
354
368
|
if (!this.open) {
|
|
355
369
|
this.focus();
|
|
@@ -357,21 +371,16 @@ export class Select extends FormAssociatedSelect {
|
|
|
357
371
|
break;
|
|
358
372
|
}
|
|
359
373
|
case keyEscape: {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
this.filter = '';
|
|
363
|
-
if (this.committedSelectedOption) {
|
|
364
|
-
this.clearSelection();
|
|
365
|
-
this.selectedIndex = this._options.indexOf(this.committedSelectedOption);
|
|
374
|
+
if (!this.open) {
|
|
375
|
+
break;
|
|
366
376
|
}
|
|
367
377
|
if (this.collapsible && this.open) {
|
|
368
378
|
e.preventDefault();
|
|
369
379
|
this.open = false;
|
|
370
380
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
selectedOption.selected = true;
|
|
381
|
+
if (this.selectedIndex !== this.indexWhenOpened) {
|
|
382
|
+
this.options[this.selectedIndex].selected = false;
|
|
383
|
+
this.selectedIndex = this.indexWhenOpened;
|
|
375
384
|
}
|
|
376
385
|
this.focus();
|
|
377
386
|
break;
|
|
@@ -401,8 +410,12 @@ export class Select extends FormAssociatedSelect {
|
|
|
401
410
|
*
|
|
402
411
|
* @internal
|
|
403
412
|
*/
|
|
404
|
-
selectedIndexChanged(
|
|
405
|
-
super.selectedIndexChanged
|
|
413
|
+
selectedIndexChanged(_, __) {
|
|
414
|
+
// Don't call super.selectedIndexChanged as this will disallow disabled options
|
|
415
|
+
// from being valid initial selected values. Our setDefaultSelectedOption
|
|
416
|
+
// implementation handles skipping non-selected disabled options for the initial
|
|
417
|
+
// selected value.
|
|
418
|
+
this.setSelectedOptions();
|
|
406
419
|
this.updateValue();
|
|
407
420
|
}
|
|
408
421
|
/**
|
|
@@ -433,6 +446,26 @@ export class Select extends FormAssociatedSelect {
|
|
|
433
446
|
this.selectedIndex = 0;
|
|
434
447
|
}
|
|
435
448
|
}
|
|
449
|
+
selectNextOption() {
|
|
450
|
+
// don't call super.selectNextOption as that relies on side-effecty
|
|
451
|
+
// behavior to not select disabled option (which no longer works)
|
|
452
|
+
for (let i = this.selectedIndex + 1; i < this.options.length; i++) {
|
|
453
|
+
if (!this.options[i]?.disabled) {
|
|
454
|
+
this.selectedIndex = i;
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
selectPreviousOption() {
|
|
460
|
+
// don't call super.selectPreviousOption as that relies on side-effecty
|
|
461
|
+
// behavior to not select disabled option (which no longer works)
|
|
462
|
+
for (let i = this.selectedIndex - 1; i >= 0; i--) {
|
|
463
|
+
if (!this.options[i]?.disabled) {
|
|
464
|
+
this.selectedIndex = i;
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
436
469
|
// Prevents parent classes from resetting selectedIndex to a positive
|
|
437
470
|
// value while filtering, which can result in a disabled option being
|
|
438
471
|
// selected.
|
|
@@ -494,9 +527,14 @@ export class Select extends FormAssociatedSelect {
|
|
|
494
527
|
* @override
|
|
495
528
|
* @internal
|
|
496
529
|
*/
|
|
497
|
-
selectedOptionsChanged(
|
|
498
|
-
super.selectedOptionsChanged
|
|
530
|
+
selectedOptionsChanged(_prev, next) {
|
|
531
|
+
// don't call super.selectedOptionsChanged so we don't filter out hidden elements
|
|
532
|
+
// when updating 'selected' state (copied relevant super implementation)
|
|
499
533
|
this.options?.forEach((o, i) => {
|
|
534
|
+
const notifier = Observable.getNotifier(o);
|
|
535
|
+
notifier.unsubscribe(this, 'selected');
|
|
536
|
+
o.selected = next.includes(o);
|
|
537
|
+
notifier.subscribe(this, 'selected');
|
|
500
538
|
const proxyOption = this.proxy?.options.item(i);
|
|
501
539
|
if (proxyOption) {
|
|
502
540
|
proxyOption.selected = o.selected;
|
|
@@ -512,15 +550,37 @@ export class Select extends FormAssociatedSelect {
|
|
|
512
550
|
*/
|
|
513
551
|
setDefaultSelectedOption() {
|
|
514
552
|
const options = this.options
|
|
515
|
-
?? Array.from(this.children).filter(o =>
|
|
516
|
-
const
|
|
517
|
-
||
|
|
518
|
-
|
|
553
|
+
?? Array.from(this.children).filter(o => isNimbleListOption(o));
|
|
554
|
+
const optionIsSelected = (option) => {
|
|
555
|
+
return option.hasAttribute('selected') || option.selected;
|
|
556
|
+
};
|
|
557
|
+
const optionIsDisabled = (option) => {
|
|
558
|
+
return option.hasAttribute('disabled') || option.disabled;
|
|
559
|
+
};
|
|
560
|
+
let selectedIndex = -1;
|
|
561
|
+
let firstValidOptionIndex = -1;
|
|
562
|
+
for (let i = 0; i < options?.length; i++) {
|
|
563
|
+
const option = options[i];
|
|
564
|
+
if (optionIsSelected(option) || option?.value === this.value) {
|
|
565
|
+
selectedIndex = i;
|
|
566
|
+
}
|
|
567
|
+
if (firstValidOptionIndex === -1 && !optionIsDisabled(option)) {
|
|
568
|
+
firstValidOptionIndex = i;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
519
571
|
if (selectedIndex !== -1) {
|
|
520
572
|
this.selectedIndex = selectedIndex;
|
|
521
|
-
return;
|
|
522
573
|
}
|
|
523
|
-
|
|
574
|
+
else if (firstValidOptionIndex !== -1) {
|
|
575
|
+
this.selectedIndex = firstValidOptionIndex;
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
this.selectedIndex = 0;
|
|
579
|
+
}
|
|
580
|
+
this.committedSelectedOption = options[this.selectedIndex];
|
|
581
|
+
}
|
|
582
|
+
committedSelectedOptionChanged() {
|
|
583
|
+
this.updateDisplayValue();
|
|
524
584
|
}
|
|
525
585
|
setPositioning() {
|
|
526
586
|
if (!this.$fastController.isConnected) {
|
|
@@ -556,15 +616,24 @@ export class Select extends FormAssociatedSelect {
|
|
|
556
616
|
filterOptions() {
|
|
557
617
|
const filter = this.filter.toLowerCase();
|
|
558
618
|
if (filter) {
|
|
559
|
-
this.filteredOptions = this.
|
|
560
|
-
|
|
619
|
+
this.filteredOptions = this.options.filter(option => {
|
|
620
|
+
const normalizedFilter = diacriticInsensitiveStringNormalizer(filter);
|
|
621
|
+
return (!option.hidden
|
|
622
|
+
&& diacriticInsensitiveStringNormalizer(option.text).includes(normalizedFilter));
|
|
561
623
|
});
|
|
562
624
|
}
|
|
563
625
|
else {
|
|
564
|
-
this.filteredOptions = this.
|
|
626
|
+
this.filteredOptions = this.options.filter(option => !option.hidden);
|
|
565
627
|
}
|
|
566
|
-
this.
|
|
567
|
-
|
|
628
|
+
this.options.forEach(o => {
|
|
629
|
+
if (isNimbleListOption(o)) {
|
|
630
|
+
if (!this.filteredOptions.includes(o)) {
|
|
631
|
+
o.visuallyHidden = true;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
o.visuallyHidden = false;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
568
637
|
});
|
|
569
638
|
}
|
|
570
639
|
/**
|
|
@@ -622,7 +691,7 @@ export class Select extends FormAssociatedSelect {
|
|
|
622
691
|
this.ariaControls = '';
|
|
623
692
|
return;
|
|
624
693
|
}
|
|
625
|
-
this.committedSelectedOption = this.
|
|
694
|
+
this.committedSelectedOption = this.options[this.selectedIndex];
|
|
626
695
|
this.ariaControls = this.listboxId;
|
|
627
696
|
this.ariaExpanded = 'true';
|
|
628
697
|
this.setPositioning();
|
|
@@ -633,24 +702,6 @@ export class Select extends FormAssociatedSelect {
|
|
|
633
702
|
this.listbox.style.setProperty('--ni-private-select-max-height', `${this.maxHeight}px`);
|
|
634
703
|
}
|
|
635
704
|
}
|
|
636
|
-
updateSelectedIndexFromFilteredSet() {
|
|
637
|
-
const selectedItem = this.filteredOptions.length > 0
|
|
638
|
-
? this.options[this.selectedIndex]
|
|
639
|
-
?? this.committedSelectedOption
|
|
640
|
-
: this.committedSelectedOption;
|
|
641
|
-
if (!selectedItem) {
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
// Clear filter so any logic resolving against 'this.options' resolves against all options,
|
|
645
|
-
// since selectedIndex should be relative to entire set.
|
|
646
|
-
this.filter = '';
|
|
647
|
-
// translate selectedIndex for filtered list to selectedIndex for all items
|
|
648
|
-
this.selectedIndex = this._options.indexOf(selectedItem);
|
|
649
|
-
// force selected to true again if the selection hasn't actually changed
|
|
650
|
-
if (selectedItem === this.committedSelectedOption) {
|
|
651
|
-
selectedItem.selected = true;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
705
|
}
|
|
655
706
|
__decorate([
|
|
656
707
|
attr
|
|
@@ -667,6 +718,9 @@ __decorate([
|
|
|
667
718
|
__decorate([
|
|
668
719
|
attr({ attribute: 'filter-mode' })
|
|
669
720
|
], Select.prototype, "filterMode", void 0);
|
|
721
|
+
__decorate([
|
|
722
|
+
observable
|
|
723
|
+
], Select.prototype, "displayPlaceholder", void 0);
|
|
670
724
|
__decorate([
|
|
671
725
|
attr({ attribute: 'open', mode: 'boolean' })
|
|
672
726
|
], Select.prototype, "open", void 0);
|
|
@@ -703,6 +757,9 @@ __decorate([
|
|
|
703
757
|
__decorate([
|
|
704
758
|
volatile
|
|
705
759
|
], Select.prototype, "collapsible", null);
|
|
760
|
+
__decorate([
|
|
761
|
+
volatile
|
|
762
|
+
], Select.prototype, "displayValue", null);
|
|
706
763
|
const nimbleSelect = Select.compose({
|
|
707
764
|
baseName: 'select',
|
|
708
765
|
baseClass: FoundationSelect,
|