@api-client/ui 0.2.3 → 0.2.5
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/.aiexclude +3 -0
- package/.vscode/settings.json +6 -3
- package/build/src/elements/authorization/ui/ApiKeyAuthorization.d.ts +1 -1
- package/build/src/elements/authorization/ui/ApiKeyAuthorization.d.ts.map +1 -1
- package/build/src/elements/authorization/ui/ApiKeyAuthorization.js +7 -7
- package/build/src/elements/authorization/ui/ApiKeyAuthorization.js.map +1 -1
- package/build/src/elements/authorization/ui/Authorization.styles.js +4 -4
- package/build/src/elements/authorization/ui/Authorization.styles.js.map +1 -1
- package/build/src/elements/authorization/ui/BasicAuthorization.d.ts +1 -1
- package/build/src/elements/authorization/ui/BasicAuthorization.d.ts.map +1 -1
- package/build/src/elements/authorization/ui/BasicAuthorization.js +5 -5
- package/build/src/elements/authorization/ui/BasicAuthorization.js.map +1 -1
- package/build/src/elements/authorization/ui/BearerAuthorization.d.ts +1 -1
- package/build/src/elements/authorization/ui/BearerAuthorization.d.ts.map +1 -1
- package/build/src/elements/authorization/ui/BearerAuthorization.js +3 -3
- package/build/src/elements/authorization/ui/BearerAuthorization.js.map +1 -1
- package/build/src/elements/authorization/ui/NtlmAuthorization.d.ts +1 -1
- package/build/src/elements/authorization/ui/NtlmAuthorization.d.ts.map +1 -1
- package/build/src/elements/authorization/ui/NtlmAuthorization.js +7 -7
- package/build/src/elements/authorization/ui/NtlmAuthorization.js.map +1 -1
- package/build/src/elements/authorization/ui/OAuth2Authorization.d.ts +1 -1
- package/build/src/elements/authorization/ui/OAuth2Authorization.d.ts.map +1 -1
- package/build/src/elements/authorization/ui/OAuth2Authorization.js +32 -27
- package/build/src/elements/authorization/ui/OAuth2Authorization.js.map +1 -1
- package/build/src/elements/authorization/ui/OidcAuthorization.js +4 -4
- package/build/src/elements/authorization/ui/OidcAuthorization.js.map +1 -1
- package/build/src/elements/autocomplete/autocomplete-input.d.ts +10 -0
- package/build/src/elements/autocomplete/autocomplete-input.d.ts.map +1 -0
- package/build/src/{md/text-field/ui-text-field.js → elements/autocomplete/autocomplete-input.js} +9 -9
- package/build/src/elements/autocomplete/autocomplete-input.js.map +1 -0
- package/build/src/elements/autocomplete/internals/autocomplete.d.ts +257 -0
- package/build/src/elements/autocomplete/internals/autocomplete.d.ts.map +1 -0
- package/build/src/elements/autocomplete/internals/autocomplete.js +619 -0
- package/build/src/elements/autocomplete/internals/autocomplete.js.map +1 -0
- package/build/src/elements/autocomplete/internals/autocomplete.styles.d.ts +3 -0
- package/build/src/elements/autocomplete/internals/autocomplete.styles.d.ts.map +1 -0
- package/build/src/elements/autocomplete/internals/autocomplete.styles.js +25 -0
- package/build/src/elements/autocomplete/internals/autocomplete.styles.js.map +1 -0
- package/build/src/elements/dialog/internals/DeleteCookieAction.element.d.ts +1 -1
- package/build/src/elements/dialog/internals/DeleteCookieAction.element.d.ts.map +1 -1
- package/build/src/elements/dialog/internals/DeleteCookieAction.element.js +5 -5
- package/build/src/elements/dialog/internals/DeleteCookieAction.element.js.map +1 -1
- package/build/src/elements/dialog/internals/Rename.d.ts +1 -1
- package/build/src/elements/dialog/internals/Rename.d.ts.map +1 -1
- package/build/src/elements/dialog/internals/Rename.js +3 -3
- package/build/src/elements/dialog/internals/Rename.js.map +1 -1
- package/build/src/elements/dialog/internals/SetCookieAction.element.d.ts +1 -1
- package/build/src/elements/dialog/internals/SetCookieAction.element.d.ts.map +1 -1
- package/build/src/elements/dialog/internals/SetCookieAction.element.js +9 -9
- package/build/src/elements/dialog/internals/SetCookieAction.element.js.map +1 -1
- package/build/src/elements/environment/EnvironmentEditor.d.ts +1 -1
- package/build/src/elements/environment/EnvironmentEditor.d.ts.map +1 -1
- package/build/src/elements/environment/EnvironmentEditor.js +3 -3
- package/build/src/elements/environment/EnvironmentEditor.js.map +1 -1
- package/build/src/elements/environment/EnvironmentEditor.styles.js +1 -1
- package/build/src/elements/environment/EnvironmentEditor.styles.js.map +1 -1
- package/build/src/elements/environment/ServerEditor.d.ts +1 -1
- package/build/src/elements/environment/ServerEditor.d.ts.map +1 -1
- package/build/src/elements/environment/ServerEditor.js +7 -7
- package/build/src/elements/environment/ServerEditor.js.map +1 -1
- package/build/src/elements/environment/ServerEditor.styles.js +1 -1
- package/build/src/elements/environment/ServerEditor.styles.js.map +1 -1
- package/build/src/elements/http/BodyMultipartEditor.d.ts.map +1 -1
- package/build/src/elements/http/BodyMultipartEditor.js +4 -0
- package/build/src/elements/http/BodyMultipartEditor.js.map +1 -1
- package/build/src/elements/http/CertificateAdd.element.d.ts +1 -1
- package/build/src/elements/http/CertificateAdd.element.d.ts.map +1 -1
- package/build/src/elements/http/CertificateAdd.element.js +8 -8
- package/build/src/elements/http/CertificateAdd.element.js.map +1 -1
- package/build/src/elements/http/CertificateAdd.styles.js +1 -1
- package/build/src/elements/http/CertificateAdd.styles.js.map +1 -1
- package/build/src/elements/http/HttpAssertions.element.js +3 -3
- package/build/src/elements/http/HttpAssertions.element.js.map +1 -1
- package/build/src/elements/http/HttpFlows.element.js +3 -3
- package/build/src/elements/http/HttpFlows.element.js.map +1 -1
- package/build/src/elements/http/HttpFlowsUi.d.ts +1 -1
- package/build/src/elements/http/HttpFlowsUi.d.ts.map +1 -1
- package/build/src/elements/http/HttpFlowsUi.js +31 -31
- package/build/src/elements/http/HttpFlowsUi.js.map +1 -1
- package/build/src/elements/http/RequestConfigElement.d.ts +1 -1
- package/build/src/elements/http/RequestConfigElement.d.ts.map +1 -1
- package/build/src/elements/http/RequestConfigElement.js +7 -7
- package/build/src/elements/http/RequestConfigElement.js.map +1 -1
- package/build/src/elements/http/UrlParamsForm.d.ts +1 -1
- package/build/src/elements/http/UrlParamsForm.d.ts.map +1 -1
- package/build/src/elements/http/UrlParamsForm.js +1 -1
- package/build/src/elements/http/UrlParamsForm.js.map +1 -1
- package/build/src/elements/project/ProjectRunner.d.ts +1 -1
- package/build/src/elements/project/ProjectRunner.d.ts.map +1 -1
- package/build/src/elements/project/ProjectRunner.js +5 -5
- package/build/src/elements/project/ProjectRunner.js.map +1 -1
- package/build/src/md/input/Input.d.ts +0 -15
- package/build/src/md/input/Input.d.ts.map +1 -1
- package/build/src/md/input/Input.js +7 -42
- package/build/src/md/input/Input.js.map +1 -1
- package/build/src/md/list/internals/List.d.ts +7 -2
- package/build/src/md/list/internals/List.d.ts.map +1 -1
- package/build/src/md/list/internals/List.js +6 -0
- package/build/src/md/list/internals/List.js.map +1 -1
- package/build/src/md/list/internals/ListItem.styles.d.ts.map +1 -1
- package/build/src/md/list/internals/ListItem.styles.js +8 -0
- package/build/src/md/list/internals/ListItem.styles.js.map +1 -1
- package/build/src/md/listbox/internals/Listbox.d.ts +2 -2
- package/build/src/md/listbox/internals/Listbox.d.ts.map +1 -1
- package/build/src/md/listbox/internals/Listbox.js.map +1 -1
- package/build/src/md/text-area/internals/TextAreaElement.d.ts.map +1 -1
- package/build/src/md/text-area/internals/TextAreaElement.js +0 -5
- package/build/src/md/text-area/internals/TextAreaElement.js.map +1 -1
- package/build/src/md/text-area/ui-text-area.d.ts.map +1 -1
- package/build/src/md/text-area/ui-text-area.js +3 -2
- package/build/src/md/text-area/ui-text-area.js.map +1 -1
- package/build/src/md/text-field/internals/{TextFieldElement.d.ts → TextField.d.ts} +2 -2
- package/build/src/md/text-field/internals/TextField.d.ts.map +1 -0
- package/build/src/md/text-field/internals/{TextFieldElement.js → TextField.js} +2 -5
- package/build/src/md/text-field/internals/TextField.js.map +1 -0
- package/build/src/md/text-field/internals/{TextField.styles.d.ts → common.styles.d.ts} +1 -1
- package/build/src/md/text-field/internals/common.styles.d.ts.map +1 -0
- package/build/src/md/text-field/internals/{TextField.styles.js → common.styles.js} +8 -94
- package/build/src/md/text-field/internals/common.styles.js.map +1 -0
- package/build/src/md/text-field/internals/filled.styles.d.ts +3 -0
- package/build/src/md/text-field/internals/filled.styles.d.ts.map +1 -0
- package/build/src/md/text-field/internals/filled.styles.js +107 -0
- package/build/src/md/text-field/internals/filled.styles.js.map +1 -0
- package/build/src/md/text-field/internals/outlined.styles.d.ts +3 -0
- package/build/src/md/text-field/internals/outlined.styles.d.ts.map +1 -0
- package/build/src/md/text-field/internals/outlined.styles.js +43 -0
- package/build/src/md/text-field/internals/outlined.styles.js.map +1 -0
- package/build/src/md/text-field/ui-filled-text-field.d.ts +11 -0
- package/build/src/md/text-field/ui-filled-text-field.d.ts.map +1 -0
- package/build/src/md/text-field/ui-filled-text-field.js +28 -0
- package/build/src/md/text-field/ui-filled-text-field.js.map +1 -0
- package/build/src/md/text-field/ui-outlined-text-field.d.ts +11 -0
- package/build/src/md/text-field/ui-outlined-text-field.d.ts.map +1 -0
- package/build/src/md/text-field/ui-outlined-text-field.js +28 -0
- package/build/src/md/text-field/ui-outlined-text-field.js.map +1 -0
- package/build/src/types/input.d.ts +1 -1
- package/build/src/types/input.d.ts.map +1 -1
- package/build/src/types/input.js.map +1 -1
- package/demo/elements/authorization/oauth-authorize.html +4 -4
- package/demo/elements/authorization/oauth-authorize.ts +1 -1
- package/demo/elements/autocomplete/index.html +64 -0
- package/demo/elements/autocomplete/index.ts +171 -0
- package/demo/elements/http/body-editor.ts +3 -3
- package/demo/elements/index.html +15 -11
- package/demo/md/index.html +1 -1
- package/demo/md/inputs/input.html +10 -15
- package/demo/md/inputs/input.ts +389 -101
- package/demo/page.css +4 -0
- package/package.json +1 -1
- package/src/elements/authorization/ui/ApiKeyAuthorization.ts +7 -7
- package/src/elements/authorization/ui/Authorization.styles.ts +4 -4
- package/src/elements/authorization/ui/BasicAuthorization.ts +5 -5
- package/src/elements/authorization/ui/BearerAuthorization.ts +3 -3
- package/src/elements/authorization/ui/NtlmAuthorization.ts +7 -7
- package/src/elements/authorization/ui/OAuth2Authorization.ts +32 -27
- package/src/elements/authorization/ui/OidcAuthorization.ts +4 -4
- package/src/elements/autocomplete/autocomplete-input.ts +14 -0
- package/src/elements/autocomplete/internals/autocomplete.styles.ts +25 -0
- package/src/elements/autocomplete/internals/autocomplete.ts +599 -0
- package/src/elements/dialog/internals/DeleteCookieAction.element.ts +5 -5
- package/src/elements/dialog/internals/Rename.ts +3 -3
- package/src/elements/dialog/internals/SetCookieAction.element.ts +9 -9
- package/src/elements/environment/EnvironmentEditor.styles.ts +1 -1
- package/src/elements/environment/EnvironmentEditor.ts +3 -3
- package/src/elements/environment/ServerEditor.styles.ts +1 -1
- package/src/elements/environment/ServerEditor.ts +7 -7
- package/src/elements/http/BodyMultipartEditor.ts +4 -0
- package/src/elements/http/CertificateAdd.element.ts +8 -8
- package/src/elements/http/CertificateAdd.styles.ts +1 -1
- package/src/elements/http/HttpAssertions.element.ts +3 -3
- package/src/elements/http/HttpFlows.element.ts +3 -3
- package/src/elements/http/HttpFlowsUi.ts +31 -31
- package/src/elements/http/RequestConfigElement.ts +7 -7
- package/src/elements/http/UrlParamsForm.ts +1 -1
- package/src/elements/project/ProjectRunner.ts +5 -5
- package/src/md/input/Input.ts +6 -21
- package/src/md/list/internals/List.ts +14 -2
- package/src/md/list/internals/ListItem.styles.ts +8 -0
- package/src/md/listbox/internals/Listbox.ts +2 -2
- package/src/md/text-area/internals/TextAreaElement.ts +0 -5
- package/src/md/text-area/ui-text-area.ts +3 -2
- package/src/md/text-field/internals/{TextFieldElement.ts → TextField.ts} +1 -4
- package/src/md/text-field/internals/{TextField.styles.ts → common.styles.ts} +7 -93
- package/src/md/text-field/internals/filled.styles.ts +107 -0
- package/src/md/text-field/internals/outlined.styles.ts +43 -0
- package/src/md/text-field/ui-filled-text-field.ts +16 -0
- package/src/md/text-field/ui-outlined-text-field.ts +16 -0
- package/src/types/input.ts +0 -1
- package/test/elements/authorization/basic-method.test.ts +3 -3
- package/test/elements/authorization/bearer-method.test.ts +2 -2
- package/test/elements/authorization/ntlm-method.test.ts +4 -4
- package/test/elements/autocomplete/autocomplete-input.spec.ts +643 -0
- package/test/elements/http/BodyMultipartEditorElement.test.ts +15 -16
- package/test/elements/http/CertificateAdd.test.ts +11 -11
- package/test/elements/http/HttpAssertions.test.ts +9 -9
- package/test/elements/http/HttpFlows.test.ts +4 -4
- package/build/src/md/text-field/internals/TextField.styles.d.ts.map +0 -1
- package/build/src/md/text-field/internals/TextField.styles.js.map +0 -1
- package/build/src/md/text-field/internals/TextFieldElement.d.ts.map +0 -1
- package/build/src/md/text-field/internals/TextFieldElement.js.map +0 -1
- package/build/src/md/text-field/ui-text-field.d.ts +0 -11
- package/build/src/md/text-field/ui-text-field.d.ts.map +0 -1
- package/build/src/md/text-field/ui-text-field.js.map +0 -1
- package/src/md/text-field/ui-text-field.ts +0 -15
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
import { __esDecorate, __runInitializers } from "tslib";
|
|
2
|
+
import { html, LitElement } from 'lit';
|
|
3
|
+
import { property, state } from 'lit/decorators.js';
|
|
4
|
+
import { bound } from '../../../decorators/bound.js';
|
|
5
|
+
let Autocomplete = (() => {
|
|
6
|
+
let _classSuper = LitElement;
|
|
7
|
+
let _instanceExtraInitializers = [];
|
|
8
|
+
let _inputId_decorators;
|
|
9
|
+
let _inputId_initializers = [];
|
|
10
|
+
let _inputId_extraInitializers = [];
|
|
11
|
+
let _positionArea_decorators;
|
|
12
|
+
let _positionArea_initializers = [];
|
|
13
|
+
let _positionArea_extraInitializers = [];
|
|
14
|
+
let _handleInputFocus_decorators;
|
|
15
|
+
let _handleInput_decorators;
|
|
16
|
+
let _handleKeydown_decorators;
|
|
17
|
+
let _handleInputBlur_decorators;
|
|
18
|
+
let _handleSuggestionSelect_decorators;
|
|
19
|
+
let _handleSuggestionsSlotChange_decorators;
|
|
20
|
+
return class Autocomplete extends _classSuper {
|
|
21
|
+
static {
|
|
22
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
23
|
+
_inputId_decorators = [state()];
|
|
24
|
+
_positionArea_decorators = [property()];
|
|
25
|
+
_handleInputFocus_decorators = [bound];
|
|
26
|
+
_handleInput_decorators = [bound];
|
|
27
|
+
_handleKeydown_decorators = [bound];
|
|
28
|
+
_handleInputBlur_decorators = [bound];
|
|
29
|
+
_handleSuggestionSelect_decorators = [bound];
|
|
30
|
+
_handleSuggestionsSlotChange_decorators = [bound];
|
|
31
|
+
__esDecorate(this, null, _inputId_decorators, { kind: "accessor", name: "inputId", static: false, private: false, access: { has: obj => "inputId" in obj, get: obj => obj.inputId, set: (obj, value) => { obj.inputId = value; } }, metadata: _metadata }, _inputId_initializers, _inputId_extraInitializers);
|
|
32
|
+
__esDecorate(this, null, _positionArea_decorators, { kind: "accessor", name: "positionArea", static: false, private: false, access: { has: obj => "positionArea" in obj, get: obj => obj.positionArea, set: (obj, value) => { obj.positionArea = value; } }, metadata: _metadata }, _positionArea_initializers, _positionArea_extraInitializers);
|
|
33
|
+
__esDecorate(this, null, _handleInputFocus_decorators, { kind: "method", name: "handleInputFocus", static: false, private: false, access: { has: obj => "handleInputFocus" in obj, get: obj => obj.handleInputFocus }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
34
|
+
__esDecorate(this, null, _handleInput_decorators, { kind: "method", name: "handleInput", static: false, private: false, access: { has: obj => "handleInput" in obj, get: obj => obj.handleInput }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
35
|
+
__esDecorate(this, null, _handleKeydown_decorators, { kind: "method", name: "handleKeydown", static: false, private: false, access: { has: obj => "handleKeydown" in obj, get: obj => obj.handleKeydown }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
36
|
+
__esDecorate(this, null, _handleInputBlur_decorators, { kind: "method", name: "handleInputBlur", static: false, private: false, access: { has: obj => "handleInputBlur" in obj, get: obj => obj.handleInputBlur }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
37
|
+
__esDecorate(this, null, _handleSuggestionSelect_decorators, { kind: "method", name: "handleSuggestionSelect", static: false, private: false, access: { has: obj => "handleSuggestionSelect" in obj, get: obj => obj.handleSuggestionSelect }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
38
|
+
__esDecorate(this, null, _handleSuggestionsSlotChange_decorators, { kind: "method", name: "handleSuggestionsSlotChange", static: false, private: false, access: { has: obj => "handleSuggestionsSlotChange" in obj, get: obj => obj.handleSuggestionsSlotChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
39
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
40
|
+
}
|
|
41
|
+
createRenderRoot() {
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The MutationObserver instance used to watch for changes in slotted children.
|
|
46
|
+
*/
|
|
47
|
+
observer = __runInitializers(this, _instanceExtraInitializers);
|
|
48
|
+
#inputId_accessor_storage = __runInitializers(this, _inputId_initializers, ''
|
|
49
|
+
/**
|
|
50
|
+
* The position area for the suggestions popover.
|
|
51
|
+
* This can be 'bottom' or 'top', depending on available space.
|
|
52
|
+
* Default is 'bottom'.
|
|
53
|
+
*
|
|
54
|
+
* Note, this is set dynamically based on available space
|
|
55
|
+
* and the position of the input element. This only sets the initial value.
|
|
56
|
+
*
|
|
57
|
+
* @attribute
|
|
58
|
+
* @type {'bottom' | 'top'}
|
|
59
|
+
* @default 'bottom'
|
|
60
|
+
*/
|
|
61
|
+
);
|
|
62
|
+
/**
|
|
63
|
+
* The ID of the input element, generated if not provided.
|
|
64
|
+
* This is used for CSS anchoring and to ensure unique IDs for accessibility.
|
|
65
|
+
*/
|
|
66
|
+
get inputId() { return this.#inputId_accessor_storage; }
|
|
67
|
+
set inputId(value) { this.#inputId_accessor_storage = value; }
|
|
68
|
+
#positionArea_accessor_storage = (__runInitializers(this, _inputId_extraInitializers), __runInitializers(this, _positionArea_initializers, 'bottom'
|
|
69
|
+
/**
|
|
70
|
+
* The reference to the slotted input element.
|
|
71
|
+
* This should be an `HTMLInputElement` or behave like one.
|
|
72
|
+
*/
|
|
73
|
+
));
|
|
74
|
+
/**
|
|
75
|
+
* The position area for the suggestions popover.
|
|
76
|
+
* This can be 'bottom' or 'top', depending on available space.
|
|
77
|
+
* Default is 'bottom'.
|
|
78
|
+
*
|
|
79
|
+
* Note, this is set dynamically based on available space
|
|
80
|
+
* and the position of the input element. This only sets the initial value.
|
|
81
|
+
*
|
|
82
|
+
* @attribute
|
|
83
|
+
* @type {'bottom' | 'top'}
|
|
84
|
+
* @default 'bottom'
|
|
85
|
+
*/
|
|
86
|
+
get positionArea() { return this.#positionArea_accessor_storage; }
|
|
87
|
+
set positionArea(value) { this.#positionArea_accessor_storage = value; }
|
|
88
|
+
/**
|
|
89
|
+
* The reference to the slotted input element.
|
|
90
|
+
* This should be an `HTMLInputElement` or behave like one.
|
|
91
|
+
*/
|
|
92
|
+
inputRef = __runInitializers(this, _positionArea_extraInitializers);
|
|
93
|
+
/**
|
|
94
|
+
* The reference to the slotted suggestions element, which should be a `ui-listbox`.
|
|
95
|
+
* This is used to manage the suggestions popover and filtering.
|
|
96
|
+
*/
|
|
97
|
+
suggestionsRef;
|
|
98
|
+
/**
|
|
99
|
+
* Checks if the suggestions popover is currently open.
|
|
100
|
+
*/
|
|
101
|
+
get opened() {
|
|
102
|
+
return this.suggestionsRef?.matches(':popover-open') || false;
|
|
103
|
+
}
|
|
104
|
+
connectedCallback() {
|
|
105
|
+
super.connectedCallback();
|
|
106
|
+
this.observer = new MutationObserver(this.handleMutations.bind(this));
|
|
107
|
+
this.observer.observe(this, { childList: true });
|
|
108
|
+
if (!this.id) {
|
|
109
|
+
this.id = `autocomplete-${Math.random().toString(36).substring(2, 15)}`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
disconnectedCallback() {
|
|
113
|
+
super.disconnectedCallback();
|
|
114
|
+
this.observer?.disconnect();
|
|
115
|
+
this.observer = undefined;
|
|
116
|
+
this.clearInputListeners();
|
|
117
|
+
this.clearSuggestionsListeners();
|
|
118
|
+
}
|
|
119
|
+
firstUpdated(cp) {
|
|
120
|
+
super.firstUpdated(cp);
|
|
121
|
+
this.firstSetup();
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Performs initial setup after the first update, ensuring that slotted input
|
|
125
|
+
* and suggestions elements are configured.
|
|
126
|
+
*/
|
|
127
|
+
async firstSetup() {
|
|
128
|
+
await this.updateComplete;
|
|
129
|
+
const input = this.querySelector('[slot="input"]');
|
|
130
|
+
const suggestions = this.querySelector('[slot="suggestions"]');
|
|
131
|
+
if (input) {
|
|
132
|
+
this.setupInput(input);
|
|
133
|
+
}
|
|
134
|
+
if (suggestions) {
|
|
135
|
+
this.setupSuggestions(suggestions);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Handles mutations observed on the component's slotted children.
|
|
140
|
+
* This is used to set up or tear down input and suggestions elements when they are added or removed.
|
|
141
|
+
* @param mutations An array of MutationRecord objects.
|
|
142
|
+
*/
|
|
143
|
+
handleMutations(mutations) {
|
|
144
|
+
for (const mutation of mutations) {
|
|
145
|
+
if (mutation.type === 'childList') {
|
|
146
|
+
for (const node of mutation.removedNodes) {
|
|
147
|
+
if (node instanceof HTMLElement) {
|
|
148
|
+
if (node.slot === 'input' && this.inputRef === node) {
|
|
149
|
+
this.clearInputListeners();
|
|
150
|
+
}
|
|
151
|
+
else if (node.slot === 'suggestions' && this.suggestionsRef === node) {
|
|
152
|
+
this.clearSuggestionsListeners();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
for (const node of mutation.addedNodes) {
|
|
157
|
+
if (node instanceof HTMLElement) {
|
|
158
|
+
if (node.slot === 'input') {
|
|
159
|
+
this.setupInput(node);
|
|
160
|
+
}
|
|
161
|
+
else if (node.slot === 'suggestions') {
|
|
162
|
+
this.setupSuggestions(node);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Clears event listeners from the current input reference and resets it.
|
|
171
|
+
*/
|
|
172
|
+
clearInputListeners() {
|
|
173
|
+
if (this.inputRef) {
|
|
174
|
+
this.inputRef.removeEventListener('focus', this.handleInputFocus);
|
|
175
|
+
this.inputRef.removeEventListener('input', this.handleInput);
|
|
176
|
+
this.inputRef.removeEventListener('keydown', this.handleKeydown);
|
|
177
|
+
this.inputRef.removeEventListener('blur', this.handleInputBlur);
|
|
178
|
+
}
|
|
179
|
+
this.inputRef = null;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Clears event listeners from the current suggestions reference and resets it.
|
|
183
|
+
*/
|
|
184
|
+
clearSuggestionsListeners() {
|
|
185
|
+
if (this.suggestionsRef) {
|
|
186
|
+
this.suggestionsRef.removeEventListener('select', this.handleSuggestionSelect);
|
|
187
|
+
// If ui-listbox uses a slot for its items, listen to its slotchange
|
|
188
|
+
this.suggestionsRef.removeEventListener('itemschange', this.handleSuggestionsSlotChange);
|
|
189
|
+
if (this.suggestionsRef.matches(':popover-open')) {
|
|
190
|
+
this.suggestionsRef.hidePopover();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
this.suggestionsRef = null;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Sets up the slotted input element.
|
|
197
|
+
* Assigns an ID if necessary, sets up CSS anchoring, and attaches event listeners.
|
|
198
|
+
* @param input The HTMLElement to be used as the input.
|
|
199
|
+
*/
|
|
200
|
+
setupInput(input) {
|
|
201
|
+
this.clearInputListeners(); // Clear any old listeners
|
|
202
|
+
if (!input.id) {
|
|
203
|
+
this.inputId = `autocomplete-input-${Math.random().toString(36).substring(2, 15)}`;
|
|
204
|
+
input.id = this.inputId;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
this.inputId = input.id;
|
|
208
|
+
}
|
|
209
|
+
const anchorElement = this.querySelector('[slot="anchor"]');
|
|
210
|
+
// Ensure CSS anchor positioning can work
|
|
211
|
+
const anchor = anchorElement || input;
|
|
212
|
+
anchor.style.setProperty('anchor-name', `--${this.inputId}`);
|
|
213
|
+
this.inputRef = input; // Assuming it behaves like an input
|
|
214
|
+
this.inputRef.addEventListener('focus', this.handleInputFocus);
|
|
215
|
+
this.inputRef.addEventListener('input', this.handleInput);
|
|
216
|
+
this.inputRef.addEventListener('keydown', this.handleKeydown);
|
|
217
|
+
this.inputRef.addEventListener('blur', this.handleInputBlur);
|
|
218
|
+
// Apply initial filtering if suggestions are already present
|
|
219
|
+
if (this.suggestionsRef) {
|
|
220
|
+
this.suggestionsRef.style.setProperty('position-anchor', `--${this.inputId}`);
|
|
221
|
+
this.filterSuggestions(this.inputRef.value || '');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Sets up the slotted suggestions element (assumed to be a `UiListbox`).
|
|
226
|
+
* Configures popover behavior, CSS anchoring, and attaches event listeners.
|
|
227
|
+
* @param suggestionsElement The `UiListbox` element to be used for suggestions.
|
|
228
|
+
*/
|
|
229
|
+
setupSuggestions(suggestionsElement) {
|
|
230
|
+
this.clearSuggestionsListeners(); // Clear any old listeners
|
|
231
|
+
this.suggestionsRef = suggestionsElement;
|
|
232
|
+
this.suggestionsRef.popover = 'manual';
|
|
233
|
+
this.suggestionsRef.tabIndex = -1; // Prevent direct focus
|
|
234
|
+
if (this.inputId) {
|
|
235
|
+
this.suggestionsRef.style.setProperty('position-anchor', `--${this.inputId}`);
|
|
236
|
+
}
|
|
237
|
+
this.suggestionsRef.addEventListener('select', this.handleSuggestionSelect);
|
|
238
|
+
// The `List` dispatches `itemschange` when the slot changes, so we can listen to it.
|
|
239
|
+
this.suggestionsRef.addEventListener('itemschange', this.handleSuggestionsSlotChange);
|
|
240
|
+
// Initial filter
|
|
241
|
+
this.filterSuggestions(this.inputRef ? this.inputRef.value : '');
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Handles the focus event on the input element.
|
|
245
|
+
* Opens the suggestions popover if there are items to display.
|
|
246
|
+
*/
|
|
247
|
+
handleInputFocus() {
|
|
248
|
+
const items = this.suggestionsRef?.items;
|
|
249
|
+
if (!items || items.length === 0) {
|
|
250
|
+
// If no suggestions, do not open
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const active = items.find((item) => !item.hidden);
|
|
254
|
+
if (active) {
|
|
255
|
+
this.openSuggestions();
|
|
256
|
+
this.filterSuggestions(this.inputRef ? this.inputRef.value : '');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Handles the input event on the input element.
|
|
261
|
+
* Filters suggestions based on the input query and opens/closes the popover accordingly.
|
|
262
|
+
* @param event The input event.
|
|
263
|
+
*/
|
|
264
|
+
handleInput(event) {
|
|
265
|
+
this.openSuggestions();
|
|
266
|
+
const query = event.target.value;
|
|
267
|
+
this.filterSuggestions(query);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Handles keydown events on the input element for navigating and selecting suggestions.
|
|
271
|
+
* - ArrowDown/ArrowUp: Navigates the suggestion list.
|
|
272
|
+
* - Enter: Selects the highlighted suggestion.
|
|
273
|
+
* - Escape: Closes the suggestions popover.
|
|
274
|
+
* @param event The keyboard event.
|
|
275
|
+
*/
|
|
276
|
+
handleKeydown(event) {
|
|
277
|
+
if (!this.suggestionsRef)
|
|
278
|
+
return;
|
|
279
|
+
const { key } = event;
|
|
280
|
+
const isSuggestionsOpen = this.suggestionsRef.matches(':popover-open');
|
|
281
|
+
if (['ArrowDown', 'ArrowUp'].includes(key)) {
|
|
282
|
+
event.preventDefault();
|
|
283
|
+
if (!isSuggestionsOpen) {
|
|
284
|
+
this.openSuggestions();
|
|
285
|
+
// Give popover a moment to open before trying to highlight
|
|
286
|
+
requestAnimationFrame(() => this.navigateList(key));
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
this.navigateList(key);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else if (key === 'Enter') {
|
|
293
|
+
if (isSuggestionsOpen && this.suggestionsRef.highlightListItem) {
|
|
294
|
+
event.preventDefault();
|
|
295
|
+
this.suggestionsRef.notifySelect(this.suggestionsRef.highlightListItem);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else if (key === 'Escape') {
|
|
299
|
+
if (isSuggestionsOpen) {
|
|
300
|
+
event.preventDefault();
|
|
301
|
+
this.closeSuggestions();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Navigates the suggestion list based on the pressed key.
|
|
307
|
+
* @param key The key that was pressed (ArrowDown or ArrowUp).
|
|
308
|
+
*/
|
|
309
|
+
navigateList(key) {
|
|
310
|
+
if (!this.suggestionsRef)
|
|
311
|
+
return;
|
|
312
|
+
switch (key) {
|
|
313
|
+
case 'ArrowDown':
|
|
314
|
+
this.suggestionsRef.highlightNext();
|
|
315
|
+
break;
|
|
316
|
+
case 'ArrowUp':
|
|
317
|
+
this.suggestionsRef.highlightPrevious();
|
|
318
|
+
break;
|
|
319
|
+
// Don't handle Home or End keys here, as they break the usability of the input.
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Handles the blur event on the input element.
|
|
324
|
+
* Closes the suggestions popover if focus moves outside the autocomplete component.
|
|
325
|
+
*/
|
|
326
|
+
handleInputBlur() {
|
|
327
|
+
// We use the manual popover mode, so we need to close suggestions
|
|
328
|
+
// when the input loses focus, but only the current active element is not part of the suggestions.
|
|
329
|
+
requestAnimationFrame(() => {
|
|
330
|
+
if (!this.inputRef?.contains(document.activeElement) &&
|
|
331
|
+
(!this.suggestionsRef || !this.suggestionsRef.contains(document.activeElement))) {
|
|
332
|
+
this.closeSuggestions();
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Handles the `select` event dispatched by the `ui-listbox` when a suggestion is chosen.
|
|
338
|
+
* Dispatches an `autocomplete` event and closes the popover.
|
|
339
|
+
* @param event The custom event from `ui-listbox`.
|
|
340
|
+
*/
|
|
341
|
+
handleSuggestionSelect(event) {
|
|
342
|
+
event.stopPropagation();
|
|
343
|
+
const selectedItem = event.detail.item;
|
|
344
|
+
this.dispatchEvent(new CustomEvent('autocomplete', {
|
|
345
|
+
detail: { item: selectedItem },
|
|
346
|
+
bubbles: false,
|
|
347
|
+
composed: false,
|
|
348
|
+
}));
|
|
349
|
+
this.closeSuggestions();
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Handles the `itemschange` event dispatched by the `ui-listbox` when its slotted items change.
|
|
353
|
+
* Re-filters the suggestions.
|
|
354
|
+
*/
|
|
355
|
+
handleSuggestionsSlotChange() {
|
|
356
|
+
const value = this.inputRef ? this.inputRef.value : '';
|
|
357
|
+
if (!value) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
this.openSuggestions();
|
|
361
|
+
this.filterSuggestions(value);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Opens the suggestions popover if it's not already open and there are visible items.
|
|
365
|
+
*/
|
|
366
|
+
openSuggestions() {
|
|
367
|
+
const popover = this.suggestionsRef;
|
|
368
|
+
if (!popover || popover.matches(':popover-open')) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
// we need to open the popover first to make any measurements
|
|
372
|
+
popover.showPopover();
|
|
373
|
+
const anchor = this.querySelector('[slot="anchor"]') || this.inputRef;
|
|
374
|
+
if (!anchor) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
this.positionArea = this.decidePositionArea(popover, anchor);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Decides the position area for the popover based on available space.
|
|
381
|
+
* It checks if there is enough space below or above the anchor element and decides accordingly.
|
|
382
|
+
*
|
|
383
|
+
* We need to do this because we set the popover height to `100%`, `-webkit-fill-available`, or `-moz-available`
|
|
384
|
+
* and it makes the popover to always open at the bottom, even if there is no space. The `position-try-fallbacks`
|
|
385
|
+
* will not work in this case, because from its perspective there is always enough space, even if that will cause
|
|
386
|
+
* the popover to have a height of just a few pixels.
|
|
387
|
+
*
|
|
388
|
+
* @param popover The popover element to position.
|
|
389
|
+
* @param anchor The anchor element relative to which the popover will be positioned.
|
|
390
|
+
* @returns 'top' or 'bottom' based on available space.
|
|
391
|
+
*/
|
|
392
|
+
decidePositionArea(popover, anchor) {
|
|
393
|
+
let newArea = 'bottom';
|
|
394
|
+
// Get bounding rectangles
|
|
395
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
396
|
+
const viewportHeight = window.innerHeight;
|
|
397
|
+
const spaceBelow = viewportHeight - anchorRect.bottom;
|
|
398
|
+
const spaceAbove = anchorRect.top;
|
|
399
|
+
// Estimate a typical/minimum height the popover might need to be useful
|
|
400
|
+
const popoverThresholdHeight = popover.scrollHeight || 150;
|
|
401
|
+
if (spaceBelow < popoverThresholdHeight && spaceAbove > spaceBelow && spaceAbove > popoverThresholdHeight) {
|
|
402
|
+
// Not enough space below, but more (and sufficient) space above
|
|
403
|
+
newArea = 'top';
|
|
404
|
+
}
|
|
405
|
+
else if (spaceBelow < popoverThresholdHeight && spaceAbove > spaceBelow) {
|
|
406
|
+
// Not enough space below, and space above is more than space below (even if not "sufficient")
|
|
407
|
+
newArea = 'top';
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
newArea = 'bottom'; // Default to bottom if enough space or if top is worse
|
|
411
|
+
}
|
|
412
|
+
return newArea;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Closes the suggestions popover if it's open and clears any highlighted item.
|
|
416
|
+
*/
|
|
417
|
+
closeSuggestions() {
|
|
418
|
+
if (this.suggestionsRef && this.suggestionsRef.matches(':popover-open')) {
|
|
419
|
+
this.suggestionsRef.hidePopover();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Filters the suggestions based on the provided query.
|
|
424
|
+
* Hides items that do not match and manages the highlighted item state.
|
|
425
|
+
* @param query The search query string.
|
|
426
|
+
*/
|
|
427
|
+
filterSuggestions(query) {
|
|
428
|
+
if (!this.suggestionsRef) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const lowerCaseQuery = query.toLowerCase().trim();
|
|
432
|
+
let firstVisibleItem = null;
|
|
433
|
+
// The `items` getter in `List.ts` (parent of UiListbox) correctly gets assigned elements.
|
|
434
|
+
const items = this.suggestionsRef.items;
|
|
435
|
+
for (const item of items) {
|
|
436
|
+
let matches = false;
|
|
437
|
+
if (lowerCaseQuery === '') {
|
|
438
|
+
matches = true;
|
|
439
|
+
}
|
|
440
|
+
else if (item.dataset.index) {
|
|
441
|
+
const indexFields = item.dataset.index.split(' ').filter(Boolean);
|
|
442
|
+
for (const field of indexFields) {
|
|
443
|
+
const valueToSearch = item.dataset[field] || '';
|
|
444
|
+
if (valueToSearch.toLowerCase().includes(lowerCaseQuery)) {
|
|
445
|
+
matches = true;
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
const valueToSearch = item.dataset.value || item.textContent || '';
|
|
452
|
+
matches = valueToSearch.toLowerCase().includes(lowerCaseQuery);
|
|
453
|
+
}
|
|
454
|
+
item.hidden = !matches;
|
|
455
|
+
if (matches && !firstVisibleItem) {
|
|
456
|
+
firstVisibleItem = item;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// If the currently highlighted item is now hidden, try to highlight the first visible one or clear.
|
|
460
|
+
if (this.suggestionsRef.highlightListItem?.hidden) {
|
|
461
|
+
// the highlightListItem clears the highlighted item if it is not passed an argument.
|
|
462
|
+
this.suggestionsRef?.highlightItem(firstVisibleItem);
|
|
463
|
+
}
|
|
464
|
+
if (!firstVisibleItem) {
|
|
465
|
+
// Close if no items are visible
|
|
466
|
+
this.closeSuggestions();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
render() {
|
|
470
|
+
const { id, positionArea } = this;
|
|
471
|
+
return html `
|
|
472
|
+
<style>
|
|
473
|
+
#${id} {
|
|
474
|
+
display: inline-block;
|
|
475
|
+
|
|
476
|
+
[popover] {
|
|
477
|
+
border: none;
|
|
478
|
+
margin: 0;
|
|
479
|
+
position-area: ${positionArea};
|
|
480
|
+
position-try-fallbacks:
|
|
481
|
+
flip-block,
|
|
482
|
+
flip-inline,
|
|
483
|
+
flip-block flip-inline;
|
|
484
|
+
width: anchor-size(width);
|
|
485
|
+
|
|
486
|
+
box-shadow: var(--md-sys-elevation-1);
|
|
487
|
+
border-radius: var(--md-sys-shape-corner-medium);
|
|
488
|
+
|
|
489
|
+
overflow: auto;
|
|
490
|
+
/* We try 100% and then vendor options which are more accurate */
|
|
491
|
+
height: 100%;
|
|
492
|
+
height: -webkit-fill-available;
|
|
493
|
+
height: -moz-available;
|
|
494
|
+
max-height: max-content;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
[popover]:not(:popover-open) {
|
|
498
|
+
display: none;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
</style>
|
|
502
|
+
`;
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
})();
|
|
506
|
+
/**
|
|
507
|
+
* An accessible and performant autocomplete component that enhances a text input with a list of suggestions.
|
|
508
|
+
*
|
|
509
|
+
* The `autocomplete-input` component provides a flexible way to add autocomplete functionality
|
|
510
|
+
* to any text input. It works by coordinating a slotted input element with a slotted `ui-listbox`
|
|
511
|
+
* containing suggestions.
|
|
512
|
+
*
|
|
513
|
+
* Key Features:
|
|
514
|
+
*
|
|
515
|
+
* - **Popover Management**: Automatically shows and hides the suggestions popover based on input focus
|
|
516
|
+
* and user interaction.
|
|
517
|
+
* - **Keyboard Navigation**: Allows users to navigate suggestions using ArrowUp, ArrowDown keys directly
|
|
518
|
+
* from the input. Enter selects a suggestion, and Escape closes the popover.
|
|
519
|
+
* The suggestion list itself never gains focus.
|
|
520
|
+
* - **Dynamic Filtering**: Filters the list of suggestions as the user types into the input. Items not matching
|
|
521
|
+
* the query are hidden (not removed).
|
|
522
|
+
* - By default, filtering checks `item.dataset.value` and then `item.textContent`.
|
|
523
|
+
* - If a `ui-list-item` has a `data-index` attribute (e.g., `data-index="name email"`), filtering will search
|
|
524
|
+
* within the specified `data-*` attributes (e.g., `data-name`, `data-email`).
|
|
525
|
+
* - **Mutation Awareness**: Reacts to changes in slotted suggestions, re-filtering them if necessary.
|
|
526
|
+
* - **Event-Driven**: Notifies the application via an `autocomplete` event when a suggestion is selected.
|
|
527
|
+
* The component itself does not modify the input's value, giving the application author full control.
|
|
528
|
+
*
|
|
529
|
+
* ### Accessibility
|
|
530
|
+
*
|
|
531
|
+
* The `autocomplete-input` component is designed with accessibility at its core:
|
|
532
|
+
* - **Keyboard Navigation**: Full keyboard support for navigating suggestions (ArrowUp, ArrowDown),
|
|
533
|
+
* selecting (Enter), and closing (Escape), all while focus remains on the input field.
|
|
534
|
+
* - **ARIA Attributes**: While the `autocomplete-input` manages ARIA attributes related to the popover's
|
|
535
|
+
* state, the slotted `ui-listbox` is responsible for its internal ARIA roles and states
|
|
536
|
+
* (e.g., `role="listbox"`, `aria-activedescendant`).
|
|
537
|
+
* - **Labeling Suggestions**: It is **crucial** for accessibility that the slotted `ui-listbox`
|
|
538
|
+
* has an `aria-label` attribute. This provides a descriptive name for the list of suggestions,
|
|
539
|
+
* which is announced by screen readers.
|
|
540
|
+
*
|
|
541
|
+
* The component uses CSS anchor positioning to place the suggestions popover relative to the input.
|
|
542
|
+
* Ensure your `ui-listbox` is styled appropriately for popover display (e.g., `popover="manual"`, `position-anchor`).
|
|
543
|
+
* The component will manage `showPopover()` and `hidePopover()` calls.
|
|
544
|
+
*
|
|
545
|
+
* @slot input - The input element that will be used for autocomplete. This element should be an `HTMLInputElement`
|
|
546
|
+
* or behave like one (have a `value` property and dispatch `input`, `focus`, `blur`, and `keydown`
|
|
547
|
+
* events).
|
|
548
|
+
* @slot suggestions - The `ui-listbox` element containing `ui-list-item` elements as suggestions.
|
|
549
|
+
* @slot anchor - An optional element that points to element that will be used as the anchor for the popover.
|
|
550
|
+
* This is useful if you want to position the suggestions relative to a different element than the input.
|
|
551
|
+
* If not provided, the input element will be used as the anchor.
|
|
552
|
+
* @slot Any additional content that should be rendered inside the component.
|
|
553
|
+
*
|
|
554
|
+
* @fires autocomplete - Dispatched when a suggestion is selected by the user (e.g., via click or Enter key).
|
|
555
|
+
* The `event.detail` object contains:
|
|
556
|
+
* - `item`: The selected `UiListItem` instance.
|
|
557
|
+
*
|
|
558
|
+
* @example
|
|
559
|
+
* ```html
|
|
560
|
+
* <autocomplete-input @autocomplete="${this.handleAutocompleteSelection}">
|
|
561
|
+
* <input slot="input" type="text" placeholder="Search fruits..." />
|
|
562
|
+
* <ui-listbox slot="suggestions">
|
|
563
|
+
* <ui-list-item data-value="apple">Apple</ui-list-item>
|
|
564
|
+
* <ui-list-item data-value="banana">Banana</ui-list-item>
|
|
565
|
+
* <ui-list-item data-value="cherry">Cherry</ui-list-item>
|
|
566
|
+
* </ui-listbox>
|
|
567
|
+
* <!-- With aria-label for accessibility -->
|
|
568
|
+
* <ui-listbox slot="suggestions" aria-label="Fruit suggestions">
|
|
569
|
+
* <ui-list-item data-value="apple">Apple</ui-list-item>
|
|
570
|
+
* <ui-list-item data-value="banana">Banana</ui-list-item>
|
|
571
|
+
* <ui-list-item data-value="cherry">Cherry</ui-list-item>
|
|
572
|
+
* </ui-listbox>
|
|
573
|
+
* </autocomplete-input>
|
|
574
|
+
*
|
|
575
|
+
* <script>
|
|
576
|
+
* // In your component/script
|
|
577
|
+
* handleAutocompleteSelection(event) {
|
|
578
|
+
* const selectedItem = event.detail.item;
|
|
579
|
+
* const inputElement = this.shadowRoot.querySelector('input[slot="input"]');
|
|
580
|
+
* if (inputElement) {
|
|
581
|
+
* inputElement.value = selectedItem.dataset.value || selectedItem.textContent;
|
|
582
|
+
* }
|
|
583
|
+
* console.log('Selected:', selectedItem.dataset.value);
|
|
584
|
+
* }
|
|
585
|
+
* </script>
|
|
586
|
+
* ```
|
|
587
|
+
*
|
|
588
|
+
* @example Dynamic filtering with `data-index`
|
|
589
|
+
* ```html
|
|
590
|
+
* <autocomplete-input @autocomplete="${this.handleUserSelection}">
|
|
591
|
+
* <input slot="input" type="text" placeholder="Search users..." />
|
|
592
|
+
* <ui-listbox slot="suggestions" aria-label="User suggestions">
|
|
593
|
+
* <ui-list-item data-id="1" data-name="Alice Wonderland" data-email="alice@example.com" data-index="name email">
|
|
594
|
+
* Alice Wonderland
|
|
595
|
+
* <span slot="supporting-text">alice@example.com</span>
|
|
596
|
+
* </ui-list-item>
|
|
597
|
+
* <ui-list-item data-id="2" data-name="Bob The Builder" data-email="bob@example.com" data-index="name email">
|
|
598
|
+
* Bob The Builder
|
|
599
|
+
* <span slot="supporting-text">bob@example.com</span>
|
|
600
|
+
* </ui-list-item>
|
|
601
|
+
* </ui-listbox>
|
|
602
|
+
* </autocomplete-input>
|
|
603
|
+
*
|
|
604
|
+
* <script>
|
|
605
|
+
* // In your component/script
|
|
606
|
+
* handleUserSelection(event) {
|
|
607
|
+
* const selectedItem = event.detail.item;
|
|
608
|
+
* const inputElement = this.shadowRoot.querySelector('input[slot="input"]');
|
|
609
|
+
* if (inputElement) {
|
|
610
|
+
* // You might want to display the name, but store the ID
|
|
611
|
+
* inputElement.value = selectedItem.dataset.name;
|
|
612
|
+
* }
|
|
613
|
+
* console.log('Selected user ID:', selectedItem.dataset.id);
|
|
614
|
+
* }
|
|
615
|
+
* </script>
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
618
|
+
export default Autocomplete;
|
|
619
|
+
//# sourceMappingURL=autocomplete.js.map
|