@penn-libraries/web 1.1.0-dev.6 → 1.1.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/cjs/{index-C0A2bnrZ.js → index-4ixBJUwu.js} +284 -191
- package/dist/cjs/index-4ixBJUwu.js.map +1 -0
- package/dist/cjs/index.cjs.js +1 -3
- package/dist/cjs/loader.cjs.js +3 -6
- package/dist/cjs/loader.cjs.js.map +1 -1
- package/dist/cjs/pennlibs-autocomplete.pennlibs-footer.pennlibs-header.entry.cjs.js.map +1 -1
- package/dist/cjs/pennlibs-autocomplete_3.cjs.entry.js +56 -52
- package/dist/cjs/pennlibs-banner.cjs.entry.js +1 -3
- package/dist/cjs/pennlibs-chat.cjs.entry.js +1 -3
- package/dist/cjs/pennlibs-fallback-img.cjs.entry.js +1 -3
- package/dist/cjs/pennlibs-feedback.cjs.entry.js +1 -3
- package/dist/cjs/pennlibs-hero.cjs.entry.js +1 -3
- package/dist/cjs/web.cjs.js +4 -7
- package/dist/cjs/web.cjs.js.map +1 -1
- package/dist/collection/collection-manifest.json +2 -2
- package/dist/collection/components/pennlibs-autocomplete/pennlibs-autocomplete.js +71 -39
- package/dist/collection/components/pennlibs-autocomplete/pennlibs-autocomplete.js.map +1 -1
- package/dist/collection/components/pennlibs-header/pennlibs-header.js +6 -6
- package/dist/components/index.js +1 -1
- package/dist/components/pennlibs-autocomplete.js +59 -36
- package/dist/components/pennlibs-autocomplete.js.map +1 -1
- package/dist/components/pennlibs-banner.js +5 -3
- package/dist/components/pennlibs-banner.js.map +1 -1
- package/dist/components/pennlibs-chat.js +5 -3
- package/dist/components/pennlibs-chat.js.map +1 -1
- package/dist/components/pennlibs-fallback-img.js +5 -3
- package/dist/components/pennlibs-fallback-img.js.map +1 -1
- package/dist/components/pennlibs-feedback.js +5 -3
- package/dist/components/pennlibs-feedback.js.map +1 -1
- package/dist/components/pennlibs-footer.js +5 -3
- package/dist/components/pennlibs-footer.js.map +1 -1
- package/dist/components/pennlibs-header.js +6 -19
- package/dist/components/pennlibs-header.js.map +1 -1
- package/dist/components/pennlibs-hero.js +5 -3
- package/dist/components/pennlibs-hero.js.map +1 -1
- package/dist/docs.json +30 -10
- package/dist/esm/{index-CE_ILdTo.js → index-Di48-Vtw.js} +284 -192
- package/dist/esm/index-Di48-Vtw.js.map +1 -0
- package/dist/esm/index.js +1 -3
- package/dist/esm/loader.js +3 -6
- package/dist/esm/loader.js.map +1 -1
- package/dist/esm/pennlibs-autocomplete.pennlibs-footer.pennlibs-header.entry.js.map +1 -1
- package/dist/esm/pennlibs-autocomplete_3.entry.js +56 -52
- package/dist/esm/pennlibs-banner.entry.js +1 -3
- package/dist/esm/pennlibs-chat.entry.js +1 -3
- package/dist/esm/pennlibs-fallback-img.entry.js +1 -3
- package/dist/esm/pennlibs-feedback.entry.js +1 -3
- package/dist/esm/pennlibs-hero.entry.js +1 -3
- package/dist/esm/web.js +4 -7
- package/dist/esm/web.js.map +1 -1
- package/dist/types/components/pennlibs-autocomplete/pennlibs-autocomplete.d.ts +19 -3
- package/dist/types/components.d.ts +24 -0
- package/dist/types/stencil-public-runtime.d.ts +73 -9
- package/dist/web/index.esm.js +1 -3
- package/dist/web/loader.esm.js.map +1 -1
- package/dist/web/{p-5e385536.entry.js → p-09e4a02c.entry.js} +1 -3
- package/dist/web/{p-909144f6.entry.js → p-269363bf.entry.js} +56 -52
- package/dist/web/{p-370e32b1.entry.js → p-56b9f10c.entry.js} +1 -3
- package/dist/web/{p-07ad051f.entry.js → p-6503bb09.entry.js} +1 -3
- package/dist/web/{p-cbae5952.entry.js → p-783e2a87.entry.js} +1 -3
- package/dist/web/{p-20c0bd7a.entry.js → p-7cbd16c0.entry.js} +1 -3
- package/dist/web/{p-CE_ILdTo.js → p-Di48-Vtw.js} +284 -192
- package/dist/web/p-Di48-Vtw.js.map +1 -0
- package/dist/web/pennlibs-autocomplete.pennlibs-footer.pennlibs-header.entry.esm.js.map +1 -1
- package/dist/web/web.esm.js +4 -7
- package/dist/web/web.esm.js.map +1 -1
- package/hydrate/index.d.ts +28 -24
- package/hydrate/index.js +478 -257
- package/hydrate/index.mjs +478 -257
- package/package.json +3 -9
- package/dist/cjs/app-globals-V2Kpy_OQ.js +0 -8
- package/dist/cjs/app-globals-V2Kpy_OQ.js.map +0 -1
- package/dist/cjs/index-C0A2bnrZ.js.map +0 -1
- package/dist/cjs/pennlibs-autocomplete_3.cjs.entry.js.map +0 -1
- package/dist/cjs/pennlibs-banner.cjs.entry.js.map +0 -1
- package/dist/cjs/pennlibs-chat.cjs.entry.js.map +0 -1
- package/dist/cjs/pennlibs-fallback-img.cjs.entry.js.map +0 -1
- package/dist/cjs/pennlibs-feedback.cjs.entry.js.map +0 -1
- package/dist/cjs/pennlibs-hero.cjs.entry.js.map +0 -1
- package/dist/collection/utils/utils.js +0 -4
- package/dist/collection/utils/utils.js.map +0 -1
- package/dist/esm/app-globals-DQuL1Twl.js +0 -6
- package/dist/esm/app-globals-DQuL1Twl.js.map +0 -1
- package/dist/esm/index-CE_ILdTo.js.map +0 -1
- package/dist/esm/pennlibs-autocomplete_3.entry.js.map +0 -1
- package/dist/types/utils/utils.d.ts +0 -1
- package/dist/web/p-07ad051f.entry.js.map +0 -1
- package/dist/web/p-20c0bd7a.entry.js.map +0 -1
- package/dist/web/p-370e32b1.entry.js.map +0 -1
- package/dist/web/p-5e385536.entry.js.map +0 -1
- package/dist/web/p-909144f6.entry.js.map +0 -1
- package/dist/web/p-CE_ILdTo.js.map +0 -1
- package/dist/web/p-DQuL1Twl.js +0 -6
- package/dist/web/p-DQuL1Twl.js.map +0 -1
- package/dist/web/p-cbae5952.entry.js.map +0 -1
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { h } from "@stencil/core";
|
|
2
|
+
/**
|
|
3
|
+
* Offer predictive suggestions while typing your search query.
|
|
4
|
+
*
|
|
5
|
+
* @slot start - Content to display at the start (top).
|
|
6
|
+
*/
|
|
2
7
|
export class Autocomplete {
|
|
3
8
|
constructor() {
|
|
4
9
|
this.showSuggestions = false;
|
|
5
10
|
this.currentIndex = -1;
|
|
6
11
|
this.originalValue = '';
|
|
7
12
|
this.options = [];
|
|
13
|
+
this.keyHandlerMap = {
|
|
14
|
+
'Escape': () => this.handleEscape(),
|
|
15
|
+
'ArrowDown': () => this.handleArrowDown(),
|
|
16
|
+
'ArrowUp': () => this.handleArrowUp(),
|
|
17
|
+
'Enter': () => this.handleEnter()
|
|
18
|
+
};
|
|
8
19
|
this.handleOptionClick = (index) => {
|
|
9
20
|
this.currentIndex = index;
|
|
10
21
|
this.selectCurrent();
|
|
@@ -56,7 +67,12 @@ export class Autocomplete {
|
|
|
56
67
|
if (!this.for)
|
|
57
68
|
return null;
|
|
58
69
|
const slot = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('slot[name="start"]');
|
|
59
|
-
|
|
70
|
+
if (!slot)
|
|
71
|
+
return null;
|
|
72
|
+
const assignedElements = slot.assignedElements();
|
|
73
|
+
if (assignedElements.length === 0)
|
|
74
|
+
return null;
|
|
75
|
+
const input = assignedElements[0].querySelector(`input#${this.for}`);
|
|
60
76
|
return input || null;
|
|
61
77
|
}
|
|
62
78
|
isInputFocused() {
|
|
@@ -115,35 +131,30 @@ export class Autocomplete {
|
|
|
115
131
|
return;
|
|
116
132
|
if (event.metaKey || event.ctrlKey)
|
|
117
133
|
return; // Ignore keyboard shortcuts
|
|
118
|
-
const handler = this.
|
|
134
|
+
const handler = this.keyHandlerMap[event.key];
|
|
119
135
|
if (handler) {
|
|
120
|
-
|
|
136
|
+
// Don't prevent default for Enter - allow form submission
|
|
137
|
+
if (event.key !== 'Enter') {
|
|
138
|
+
event.preventDefault();
|
|
139
|
+
}
|
|
121
140
|
handler();
|
|
122
141
|
}
|
|
123
142
|
}
|
|
124
|
-
keyHandlers() {
|
|
125
|
-
return {
|
|
126
|
-
'Escape': () => this.handleEscape(),
|
|
127
|
-
'ArrowDown': () => this.handleArrowDown(),
|
|
128
|
-
'ArrowUp': () => this.handleArrowUp(),
|
|
129
|
-
'Enter': () => this.handleEnter()
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
143
|
handleEscape() {
|
|
133
144
|
this.reset();
|
|
134
|
-
this.
|
|
145
|
+
this.syncAll();
|
|
135
146
|
}
|
|
136
147
|
handleArrowDown() {
|
|
137
148
|
if (!this.showSuggestions)
|
|
138
149
|
return;
|
|
139
150
|
this.moveNext();
|
|
140
|
-
this.
|
|
151
|
+
this.syncAll();
|
|
141
152
|
}
|
|
142
153
|
handleArrowUp() {
|
|
143
154
|
if (!this.showSuggestions)
|
|
144
155
|
return;
|
|
145
156
|
this.movePrevious();
|
|
146
|
-
this.
|
|
157
|
+
this.syncAll();
|
|
147
158
|
}
|
|
148
159
|
handleEnter() {
|
|
149
160
|
if (this.canSelect())
|
|
@@ -165,16 +176,22 @@ export class Autocomplete {
|
|
|
165
176
|
if (this.currentIndex < 0)
|
|
166
177
|
return;
|
|
167
178
|
const selectedOption = this.options[this.currentIndex];
|
|
179
|
+
this.applySelection(selectedOption);
|
|
180
|
+
this.emitActivation(selectedOption);
|
|
181
|
+
this.closeSuggestions();
|
|
182
|
+
}
|
|
183
|
+
applySelection(option) {
|
|
168
184
|
const input = this.getInput();
|
|
169
|
-
if (input)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
185
|
+
if (!input)
|
|
186
|
+
return;
|
|
187
|
+
input.value = option.value;
|
|
188
|
+
this.originalValue = option.value;
|
|
189
|
+
}
|
|
190
|
+
emitActivation(option) {
|
|
173
191
|
this.activated.emit({
|
|
174
192
|
index: this.currentIndex,
|
|
175
|
-
value:
|
|
193
|
+
value: option.value
|
|
176
194
|
});
|
|
177
|
-
this.closeSuggestions();
|
|
178
195
|
}
|
|
179
196
|
openSuggestions() {
|
|
180
197
|
if (!this.isInputFocused())
|
|
@@ -183,17 +200,18 @@ export class Autocomplete {
|
|
|
183
200
|
if (input)
|
|
184
201
|
this.originalValue = input.value;
|
|
185
202
|
this.showSuggestionsPanel();
|
|
186
|
-
this.syncInputState();
|
|
187
203
|
}
|
|
188
204
|
showSuggestionsPanel() {
|
|
189
205
|
this.showSuggestions = true;
|
|
190
|
-
this.
|
|
206
|
+
this.syncAriaAttributes();
|
|
191
207
|
}
|
|
192
208
|
closeSuggestions() {
|
|
193
209
|
this.reset();
|
|
194
|
-
this.
|
|
210
|
+
this.syncAll();
|
|
195
211
|
}
|
|
196
212
|
deferCloseSuggestions() {
|
|
213
|
+
if (this.blurTimeout)
|
|
214
|
+
clearTimeout(this.blurTimeout);
|
|
197
215
|
this.blurTimeout = setTimeout(() => {
|
|
198
216
|
if (this.isFocusOutsideComponent())
|
|
199
217
|
this.closeSuggestions();
|
|
@@ -214,36 +232,44 @@ export class Autocomplete {
|
|
|
214
232
|
const active = document.activeElement;
|
|
215
233
|
return !((_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(active)) && active !== this.getInput();
|
|
216
234
|
}
|
|
217
|
-
|
|
235
|
+
syncAriaAttributes() {
|
|
218
236
|
const input = this.getInput();
|
|
219
237
|
if (!input)
|
|
220
238
|
return;
|
|
221
239
|
this.updateAriaExpanded(input);
|
|
222
240
|
this.updateAriaActiveDescendant(input);
|
|
223
|
-
|
|
241
|
+
}
|
|
242
|
+
syncInputValueToSelection() {
|
|
243
|
+
const input = this.getInput();
|
|
244
|
+
if (!input)
|
|
245
|
+
return;
|
|
246
|
+
input.value = this.currentIndex >= 0 && this.currentIndex < this.options.length
|
|
247
|
+
? this.options[this.currentIndex].value
|
|
248
|
+
: this.originalValue;
|
|
249
|
+
}
|
|
250
|
+
syncAll() {
|
|
251
|
+
this.syncAriaAttributes();
|
|
252
|
+
this.syncInputValueToSelection();
|
|
224
253
|
}
|
|
225
254
|
updateAriaExpanded(input) {
|
|
226
255
|
input.setAttribute('aria-expanded', this.showSuggestions.toString());
|
|
227
256
|
}
|
|
228
257
|
updateAriaActiveDescendant(input) {
|
|
229
|
-
const hasSelection = this.showSuggestions &&
|
|
230
|
-
|
|
258
|
+
const hasSelection = this.showSuggestions &&
|
|
259
|
+
this.currentIndex >= 0 &&
|
|
260
|
+
this.currentIndex < this.options.length;
|
|
261
|
+
if (hasSelection) {
|
|
231
262
|
input.setAttribute('aria-activedescendant', this.options[this.currentIndex].id);
|
|
232
263
|
}
|
|
233
264
|
else {
|
|
234
265
|
input.removeAttribute('aria-activedescendant');
|
|
235
266
|
}
|
|
236
267
|
}
|
|
237
|
-
updateInputValue(input) {
|
|
238
|
-
input.value = this.currentIndex >= 0
|
|
239
|
-
? this.options[this.currentIndex].value
|
|
240
|
-
: this.originalValue;
|
|
241
|
-
}
|
|
242
268
|
shouldShowListbox() {
|
|
243
269
|
return this.showSuggestions && this.options.length > 0 && this.isInputFocused();
|
|
244
270
|
}
|
|
245
271
|
render() {
|
|
246
|
-
return (h("div", { key: '
|
|
272
|
+
return (h("div", { key: 'ee4ead1b5bec44a561901a29d3d5e119da0d09f8' }, h("slot", { key: '7d7128ab7968544ab25125d30a648136f8d8a566', name: "start" }), this.shouldShowListbox() && (h("ol", { key: '95d45d524847f83b646853a8d919bc88f471f150', role: "listbox", id: "listbox" }, this.options.map((option, index) => (h("li", { role: "option", id: option.id, "aria-selected": this.currentIndex === index ? 'true' : 'false', tabindex: "-1", onClick: () => this.handleOptionClick(index), innerHTML: option.html })))))));
|
|
247
273
|
}
|
|
248
274
|
static get is() { return "pennlibs-autocomplete"; }
|
|
249
275
|
static get encapsulation() { return "shadow"; }
|
|
@@ -261,7 +287,6 @@ export class Autocomplete {
|
|
|
261
287
|
return {
|
|
262
288
|
"for": {
|
|
263
289
|
"type": "string",
|
|
264
|
-
"attribute": "for",
|
|
265
290
|
"mutable": false,
|
|
266
291
|
"complexType": {
|
|
267
292
|
"original": "string",
|
|
@@ -271,12 +296,16 @@ export class Autocomplete {
|
|
|
271
296
|
"required": false,
|
|
272
297
|
"optional": true,
|
|
273
298
|
"docs": {
|
|
274
|
-
"tags": [
|
|
275
|
-
|
|
299
|
+
"tags": [{
|
|
300
|
+
"name": "prop",
|
|
301
|
+
"text": "for"
|
|
302
|
+
}],
|
|
303
|
+
"text": "The `id` of the input that this autocomplete is attached to."
|
|
276
304
|
},
|
|
277
305
|
"getter": false,
|
|
278
306
|
"setter": false,
|
|
279
|
-
"reflect": false
|
|
307
|
+
"reflect": false,
|
|
308
|
+
"attribute": "for"
|
|
280
309
|
}
|
|
281
310
|
};
|
|
282
311
|
}
|
|
@@ -296,8 +325,11 @@ export class Autocomplete {
|
|
|
296
325
|
"cancelable": true,
|
|
297
326
|
"composed": true,
|
|
298
327
|
"docs": {
|
|
299
|
-
"tags": [
|
|
300
|
-
|
|
328
|
+
"tags": [{
|
|
329
|
+
"name": "event",
|
|
330
|
+
"text": "pl:activated"
|
|
331
|
+
}],
|
|
332
|
+
"text": "Emitted when a user activates (selects) an autocomplete suggestion."
|
|
301
333
|
},
|
|
302
334
|
"complexType": {
|
|
303
335
|
"original": "ActivatedEvent",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pennlibs-autocomplete.js","sourceRoot":"","sources":["../../../src/components/pennlibs-autocomplete/pennlibs-autocomplete.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component, h, Element, State, Listen, Event, EventEmitter, Prop } from '@stencil/core';\n\ninterface ListboxOption {\n id: string;\n html: string;\n value: string;\n}\n\nexport interface ActivatedEvent {\n value: string;\n index: number;\n}\n\n@Component({\n tag: 'pennlibs-autocomplete',\n styleUrl: 'pennlibs-autocomplete.css',\n shadow: true\n})\nexport class Autocomplete {\n @Element() el!: HTMLElement;\n\n @Prop() for?: string;\n\n @State() showSuggestions: boolean = false;\n @State() currentIndex: number = -1;\n @State() originalValue: string = '';\n @State() options: ListboxOption[] = [];\n\n @Event({ eventName: 'pl:activated' }) activated: EventEmitter<ActivatedEvent>;\n\n private blurTimeout: number;\n private mutationObserver: MutationObserver;\n\n componentWillLoad() {\n this.options = this.parseOptionsFromDOM();\n }\n\n componentDidLoad() {\n this.setupInput();\n this.setupMutationObserver();\n }\n\n disconnectedCallback() {\n if (this.blurTimeout) clearTimeout(this.blurTimeout);\n if (this.mutationObserver) this.mutationObserver.disconnect();\n }\n\n private setupMutationObserver(): void {\n this.mutationObserver = new MutationObserver(() => {\n this.options = this.parseOptionsFromDOM();\n });\n\n this.mutationObserver.observe(this.el, {\n childList: true,\n subtree: true,\n characterData: true\n });\n }\n\n private parseOptionsFromDOM(): ListboxOption[] {\n const listbox = this.findListbox();\n if (!listbox) return [];\n\n const elements = Array.from(listbox.querySelectorAll('[role=\"option\"]')) as HTMLElement[];\n return elements.map((el, i) => this.createOption(el, i));\n }\n\n private findListbox(): HTMLOListElement | null {\n return Array.from(this.el.children).find(child =>\n child.matches('ol[role=\"listbox\"]')\n ) as HTMLOListElement;\n }\n\n private createOption(element: HTMLElement, index: number): ListboxOption {\n return {\n id: element.id || `option-${index}`,\n html: element.innerHTML,\n value: element.getAttribute('data-pl-value') || element.textContent?.trim() || ''\n };\n }\n\n private getInput(): HTMLInputElement | null {\n if (!this.for) return null;\n\n const slot = this.el.shadowRoot?.querySelector('slot[name=\"start\"]') as HTMLSlotElement;\n const input = slot.assignedElements()[0].querySelector(`input#${this.for}`) as HTMLInputElement;\n\n return input || null\n }\n\n private isInputFocused(): boolean {\n return this.getInput() === document.activeElement;\n }\n\n private isTrackedInput(input: HTMLInputElement): boolean {\n return input?.getAttribute('data-pl-autocomplete-connected') === 'true' &&\n this.el.contains(input);\n }\n\n private setupInput(): void {\n if (!this.for) {\n console.warn('<pennlibs-autocomplete> Missing \"for\" attribute. Please add for=\"input-id\" to specify which input to attach to.');\n return;\n }\n\n const input = this.getInput();\n if (!input) {\n console.warn(`<pennlibs-autocomplete> No input element found with id=\"${this.for}\". Ensure an input with this id exists.`);\n return;\n }\n\n this.setComboboxAttributes(input);\n input.setAttribute('data-pl-autocomplete-connected', 'true');\n }\n\n private setComboboxAttributes(input: HTMLInputElement): void {\n input.setAttribute('role', 'combobox');\n input.setAttribute('aria-autocomplete', 'list');\n input.setAttribute('aria-expanded', 'false');\n input.setAttribute('aria-controls', 'listbox');\n }\n\n @Listen('slotchange')\n handleSlotChange() {\n this.options = this.parseOptionsFromDOM();\n this.setupInput();\n }\n\n @Listen('input', { target: 'body' })\n handleInputEvent(event: Event) {\n const input = event.target as HTMLInputElement;\n if (this.isTrackedInput(input)) {\n this.currentIndex = -1;\n this.openSuggestions();\n }\n }\n\n @Listen('focus', { target: 'body' })\n handleFocusEvent(event: Event) {\n const input = event.target as HTMLInputElement;\n if (this.isTrackedInput(input)) this.showSuggestionsPanel();\n }\n\n @Listen('blur', { target: 'body' })\n handleBlurEvent(event: Event) {\n const input = event.target as HTMLInputElement;\n if (this.isTrackedInput(input)) this.deferCloseSuggestions();\n }\n\n @Listen('focusout')\n handleFocusOut(event: FocusEvent) {\n if (this.isFocusLeavingComponent(event)) this.closeSuggestions();\n }\n\n @Listen('keydown', { target: 'document' })\n handleKeyDown(event: KeyboardEvent) {\n if (!this.isInputFocused()) return;\n if (event.metaKey || event.ctrlKey) return; // Ignore keyboard shortcuts\n\n const handler = this.keyHandlers()[event.key];\n if (handler) {\n event.preventDefault();\n handler();\n }\n }\n\n private keyHandlers() {\n return {\n 'Escape': () => this.handleEscape(),\n 'ArrowDown': () => this.handleArrowDown(),\n 'ArrowUp': () => this.handleArrowUp(),\n 'Enter': () => this.handleEnter()\n };\n }\n\n private handleEscape() {\n this.reset();\n this.syncInputState();\n }\n\n private handleArrowDown() {\n if (!this.showSuggestions) return;\n this.moveNext();\n this.syncInputState();\n }\n\n private handleArrowUp() {\n if (!this.showSuggestions) return;\n this.movePrevious();\n this.syncInputState();\n }\n\n private handleEnter() {\n if (this.canSelect()) this.selectCurrent();\n }\n\n // Navigation\n private moveNext(): void {\n const lastIndex = this.options.length - 1;\n this.currentIndex = this.currentIndex < lastIndex ? this.currentIndex + 1 : -1;\n }\n\n private movePrevious(): void {\n const lastIndex = this.options.length - 1;\n this.currentIndex = this.currentIndex > -1 ? this.currentIndex - 1 : lastIndex;\n }\n\n private canSelect(): boolean {\n return this.showSuggestions && this.currentIndex >= 0;\n }\n\n private selectCurrent(): void {\n if (this.currentIndex < 0) return;\n\n const selectedOption = this.options[this.currentIndex];\n const input = this.getInput();\n\n if (input) {\n input.value = selectedOption.value;\n this.originalValue = selectedOption.value;\n }\n\n this.activated.emit({\n index: this.currentIndex,\n value: selectedOption.value\n });\n\n this.closeSuggestions();\n }\n\n private handleOptionClick = (index: number): void => {\n this.currentIndex = index;\n this.selectCurrent();\n }\n\n private openSuggestions(): void {\n if (!this.isInputFocused()) return;\n\n const input = this.getInput();\n if (input) this.originalValue = input.value;\n\n this.showSuggestionsPanel();\n this.syncInputState();\n }\n\n private showSuggestionsPanel(): void {\n this.showSuggestions = true;\n this.syncInputState();\n }\n\n private closeSuggestions(): void {\n this.reset();\n this.syncInputState();\n }\n\n private deferCloseSuggestions(): void {\n this.blurTimeout = setTimeout(() => {\n if (this.isFocusOutsideComponent()) this.closeSuggestions();\n }, 150) as unknown as number;\n }\n\n private reset(): void {\n this.showSuggestions = false;\n this.currentIndex = -1;\n }\n\n private isFocusLeavingComponent(event: FocusEvent): boolean {\n const target = event.relatedTarget as HTMLElement;\n return !target ||\n (!this.el.contains(target) && !this.el.shadowRoot?.contains(target));\n }\n\n private isFocusOutsideComponent(): boolean {\n const active = document.activeElement;\n return !this.el.shadowRoot?.contains(active) && active !== this.getInput();\n }\n\n private syncInputState(): void {\n const input = this.getInput();\n if (!input) return;\n\n this.updateAriaExpanded(input);\n this.updateAriaActiveDescendant(input);\n this.updateInputValue(input);\n }\n\n private updateAriaExpanded(input: HTMLInputElement): void {\n input.setAttribute('aria-expanded', this.showSuggestions.toString());\n }\n\n private updateAriaActiveDescendant(input: HTMLInputElement): void {\n const hasSelection = this.showSuggestions && this.currentIndex >= 0;\n if (hasSelection && this.options[this.currentIndex]) {\n input.setAttribute('aria-activedescendant', this.options[this.currentIndex].id);\n } else {\n input.removeAttribute('aria-activedescendant');\n }\n }\n\n private updateInputValue(input: HTMLInputElement): void {\n input.value = this.currentIndex >= 0\n ? this.options[this.currentIndex].value\n : this.originalValue;\n }\n\n private shouldShowListbox(): boolean {\n return this.showSuggestions && this.options.length > 0 && this.isInputFocused();\n }\n\n render() {\n return (\n <div>\n <slot name=\"start\" />\n {this.shouldShowListbox() && (\n <ol role=\"listbox\" id=\"listbox\">\n {this.options.map((option, index) => (\n <li\n role=\"option\"\n id={option.id}\n aria-selected={this.currentIndex === index ? 'true' : 'false'}\n tabindex=\"-1\"\n onClick={() => this.handleOptionClick(index)}\n innerHTML={option.html}\n />\n ))}\n </ol>\n )}\n </div>\n )\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pennlibs-autocomplete.js","sourceRoot":"","sources":["../../../src/components/pennlibs-autocomplete/pennlibs-autocomplete.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component, h, Element, State, Listen, Event, EventEmitter, Prop } from '@stencil/core';\n\ninterface ListboxOption {\n id: string;\n html: string;\n value: string;\n}\n\nexport interface ActivatedEvent {\n value: string;\n index: number;\n}\n\n/**\n * Offer predictive suggestions while typing your search query.\n *\n * @slot start - Content to display at the start (top).\n */\n@Component({\n tag: 'pennlibs-autocomplete',\n styleUrl: 'pennlibs-autocomplete.css',\n shadow: true\n})\nexport class Autocomplete {\n @Element() el!: HTMLElement;\n\n /**\n * The `id` of the input that this autocomplete is attached to.\n * @prop for\n */\n @Prop() for?: string;\n\n @State() showSuggestions: boolean = false;\n @State() currentIndex: number = -1;\n @State() originalValue: string = '';\n @State() options: ListboxOption[] = [];\n\n /**\n * Emitted when a user activates (selects) an autocomplete suggestion.\n * @event pl:activated\n */\n @Event({ eventName: 'pl:activated' }) activated: EventEmitter<ActivatedEvent>;\n\n private blurTimeout: number;\n private mutationObserver: MutationObserver;\n private readonly keyHandlerMap = {\n 'Escape': () => this.handleEscape(),\n 'ArrowDown': () => this.handleArrowDown(),\n 'ArrowUp': () => this.handleArrowUp(),\n 'Enter': () => this.handleEnter()\n };\n\n componentWillLoad() {\n this.options = this.parseOptionsFromDOM();\n }\n\n componentDidLoad() {\n this.setupInput();\n this.setupMutationObserver();\n }\n\n disconnectedCallback() {\n if (this.blurTimeout) clearTimeout(this.blurTimeout);\n if (this.mutationObserver) this.mutationObserver.disconnect();\n }\n\n private setupMutationObserver(): void {\n this.mutationObserver = new MutationObserver(() => {\n this.options = this.parseOptionsFromDOM();\n });\n\n this.mutationObserver.observe(this.el, {\n childList: true,\n subtree: true,\n characterData: true\n });\n }\n\n private parseOptionsFromDOM(): ListboxOption[] {\n const listbox = this.findListbox();\n if (!listbox) return [];\n\n const elements = Array.from(listbox.querySelectorAll('[role=\"option\"]')) as HTMLElement[];\n return elements.map((el, i) => this.createOption(el, i));\n }\n\n private findListbox(): HTMLOListElement | null {\n return Array.from(this.el.children).find(child =>\n child.matches('ol[role=\"listbox\"]')\n ) as HTMLOListElement;\n }\n\n private createOption(element: HTMLElement, index: number): ListboxOption {\n return {\n id: element.id || `option-${index}`,\n html: element.innerHTML,\n value: element.getAttribute('data-pl-value') || element.textContent?.trim() || ''\n };\n }\n\n private getInput(): HTMLInputElement | null {\n if (!this.for) return null;\n\n const slot = this.el.shadowRoot?.querySelector('slot[name=\"start\"]') as HTMLSlotElement;\n if (!slot) return null;\n\n const assignedElements = slot.assignedElements();\n if (assignedElements.length === 0) return null;\n\n const input = assignedElements[0].querySelector(`input#${this.for}`) as HTMLInputElement;\n return input || null;\n }\n\n private isInputFocused(): boolean {\n return this.getInput() === document.activeElement;\n }\n\n private isTrackedInput(input: HTMLInputElement): boolean {\n return input?.getAttribute('data-pl-autocomplete-connected') === 'true' &&\n this.el.contains(input);\n }\n\n private setupInput(): void {\n if (!this.for) {\n console.warn('<pennlibs-autocomplete> Missing \"for\" attribute. Please add for=\"input-id\" to specify which input to attach to.');\n return;\n }\n\n const input = this.getInput();\n if (!input) {\n console.warn(`<pennlibs-autocomplete> No input element found with id=\"${this.for}\". Ensure an input with this id exists.`);\n return;\n }\n\n this.setComboboxAttributes(input);\n input.setAttribute('data-pl-autocomplete-connected', 'true');\n }\n\n private setComboboxAttributes(input: HTMLInputElement): void {\n input.setAttribute('role', 'combobox');\n input.setAttribute('aria-autocomplete', 'list');\n input.setAttribute('aria-expanded', 'false');\n input.setAttribute('aria-controls', 'listbox');\n }\n\n @Listen('slotchange')\n handleSlotChange() {\n this.options = this.parseOptionsFromDOM();\n this.setupInput();\n }\n\n @Listen('input', { target: 'body' })\n handleInputEvent(event: Event) {\n const input = event.target as HTMLInputElement;\n if (this.isTrackedInput(input)) {\n this.currentIndex = -1;\n this.openSuggestions();\n }\n }\n\n @Listen('focus', { target: 'body' })\n handleFocusEvent(event: Event) {\n const input = event.target as HTMLInputElement;\n if (this.isTrackedInput(input)) this.showSuggestionsPanel();\n }\n\n @Listen('blur', { target: 'body' })\n handleBlurEvent(event: Event) {\n const input = event.target as HTMLInputElement;\n if (this.isTrackedInput(input)) this.deferCloseSuggestions();\n }\n\n @Listen('focusout')\n handleFocusOut(event: FocusEvent) {\n if (this.isFocusLeavingComponent(event)) this.closeSuggestions();\n }\n\n @Listen('keydown', { target: 'document' })\n handleKeyDown(event: KeyboardEvent) {\n if (!this.isInputFocused()) return;\n if (event.metaKey || event.ctrlKey) return; // Ignore keyboard shortcuts\n\n const handler = this.keyHandlerMap[event.key];\n if (handler) {\n // Don't prevent default for Enter - allow form submission\n if (event.key !== 'Enter') {\n event.preventDefault();\n }\n handler();\n }\n }\n\n private handleEscape() {\n this.reset();\n this.syncAll();\n }\n\n private handleArrowDown() {\n if (!this.showSuggestions) return;\n this.moveNext();\n this.syncAll();\n }\n\n private handleArrowUp() {\n if (!this.showSuggestions) return;\n this.movePrevious();\n this.syncAll();\n }\n\n private handleEnter() {\n if (this.canSelect()) this.selectCurrent();\n }\n\n // Navigation\n private moveNext(): void {\n const lastIndex = this.options.length - 1;\n this.currentIndex = this.currentIndex < lastIndex ? this.currentIndex + 1 : -1;\n }\n\n private movePrevious(): void {\n const lastIndex = this.options.length - 1;\n this.currentIndex = this.currentIndex > -1 ? this.currentIndex - 1 : lastIndex;\n }\n\n private canSelect(): boolean {\n return this.showSuggestions && this.currentIndex >= 0;\n }\n\n private selectCurrent(): void {\n if (this.currentIndex < 0) return;\n\n const selectedOption = this.options[this.currentIndex];\n this.applySelection(selectedOption);\n this.emitActivation(selectedOption);\n this.closeSuggestions();\n }\n\n private applySelection(option: ListboxOption): void {\n const input = this.getInput();\n if (!input) return;\n\n input.value = option.value;\n this.originalValue = option.value;\n }\n\n private emitActivation(option: ListboxOption): void {\n this.activated.emit({\n index: this.currentIndex,\n value: option.value\n });\n }\n\n private handleOptionClick = (index: number): void => {\n this.currentIndex = index;\n this.selectCurrent();\n }\n\n private openSuggestions(): void {\n if (!this.isInputFocused()) return;\n\n const input = this.getInput();\n if (input) this.originalValue = input.value;\n\n this.showSuggestionsPanel();\n }\n\n private showSuggestionsPanel(): void {\n this.showSuggestions = true;\n this.syncAriaAttributes();\n }\n\n private closeSuggestions(): void {\n this.reset();\n this.syncAll();\n }\n\n private deferCloseSuggestions(): void {\n if (this.blurTimeout) clearTimeout(this.blurTimeout);\n this.blurTimeout = setTimeout(() => {\n if (this.isFocusOutsideComponent()) this.closeSuggestions();\n }, 150) as unknown as number;\n }\n\n private reset(): void {\n this.showSuggestions = false;\n this.currentIndex = -1;\n }\n\n private isFocusLeavingComponent(event: FocusEvent): boolean {\n const target = event.relatedTarget as HTMLElement;\n return !target ||\n (!this.el.contains(target) && !this.el.shadowRoot?.contains(target));\n }\n\n private isFocusOutsideComponent(): boolean {\n const active = document.activeElement;\n return !this.el.shadowRoot?.contains(active) && active !== this.getInput();\n }\n\n private syncAriaAttributes(): void {\n const input = this.getInput();\n if (!input) return;\n\n this.updateAriaExpanded(input);\n this.updateAriaActiveDescendant(input);\n }\n\n private syncInputValueToSelection(): void {\n const input = this.getInput();\n if (!input) return;\n\n input.value = this.currentIndex >= 0 && this.currentIndex < this.options.length\n ? this.options[this.currentIndex].value\n : this.originalValue;\n }\n\n private syncAll(): void {\n this.syncAriaAttributes();\n this.syncInputValueToSelection();\n }\n\n private updateAriaExpanded(input: HTMLInputElement): void {\n input.setAttribute('aria-expanded', this.showSuggestions.toString());\n }\n\n private updateAriaActiveDescendant(input: HTMLInputElement): void {\n const hasSelection = this.showSuggestions &&\n this.currentIndex >= 0 &&\n this.currentIndex < this.options.length;\n if (hasSelection) {\n input.setAttribute('aria-activedescendant', this.options[this.currentIndex].id);\n } else {\n input.removeAttribute('aria-activedescendant');\n }\n }\n\n private shouldShowListbox(): boolean {\n return this.showSuggestions && this.options.length > 0 && this.isInputFocused();\n }\n\n render() {\n return (\n <div>\n <slot name=\"start\" />\n {this.shouldShowListbox() && (\n <ol role=\"listbox\" id=\"listbox\">\n {this.options.map((option, index) => (\n <li\n role=\"option\"\n id={option.id}\n aria-selected={this.currentIndex === index ? 'true' : 'false'}\n tabindex=\"-1\"\n onClick={() => this.handleOptionClick(index)}\n innerHTML={option.html}\n />\n ))}\n </ol>\n )}\n </div>\n )\n }\n}\n"]}
|
|
@@ -82,7 +82,6 @@ export class Header {
|
|
|
82
82
|
return {
|
|
83
83
|
"serviceName": {
|
|
84
84
|
"type": "string",
|
|
85
|
-
"attribute": "service-name",
|
|
86
85
|
"mutable": false,
|
|
87
86
|
"complexType": {
|
|
88
87
|
"original": "string",
|
|
@@ -97,11 +96,11 @@ export class Header {
|
|
|
97
96
|
},
|
|
98
97
|
"getter": false,
|
|
99
98
|
"setter": false,
|
|
100
|
-
"reflect": false
|
|
99
|
+
"reflect": false,
|
|
100
|
+
"attribute": "service-name"
|
|
101
101
|
},
|
|
102
102
|
"serviceLede": {
|
|
103
103
|
"type": "string",
|
|
104
|
-
"attribute": "service-lede",
|
|
105
104
|
"mutable": false,
|
|
106
105
|
"complexType": {
|
|
107
106
|
"original": "string",
|
|
@@ -116,11 +115,11 @@ export class Header {
|
|
|
116
115
|
},
|
|
117
116
|
"getter": false,
|
|
118
117
|
"setter": false,
|
|
119
|
-
"reflect": false
|
|
118
|
+
"reflect": false,
|
|
119
|
+
"attribute": "service-lede"
|
|
120
120
|
},
|
|
121
121
|
"serviceHref": {
|
|
122
122
|
"type": "string",
|
|
123
|
-
"attribute": "service-href",
|
|
124
123
|
"mutable": false,
|
|
125
124
|
"complexType": {
|
|
126
125
|
"original": "string",
|
|
@@ -139,11 +138,11 @@ export class Header {
|
|
|
139
138
|
"getter": false,
|
|
140
139
|
"setter": false,
|
|
141
140
|
"reflect": false,
|
|
141
|
+
"attribute": "service-href",
|
|
142
142
|
"defaultValue": "\"/\""
|
|
143
143
|
},
|
|
144
144
|
"theme": {
|
|
145
145
|
"type": "string",
|
|
146
|
-
"attribute": "theme",
|
|
147
146
|
"mutable": false,
|
|
148
147
|
"complexType": {
|
|
149
148
|
"original": "'light' | 'dark'",
|
|
@@ -162,6 +161,7 @@ export class Header {
|
|
|
162
161
|
"getter": false,
|
|
163
162
|
"setter": false,
|
|
164
163
|
"reflect": false,
|
|
164
|
+
"attribute": "theme",
|
|
165
165
|
"defaultValue": "'light'"
|
|
166
166
|
}
|
|
167
167
|
};
|
package/dist/components/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { getAssetPath, setAssetPath, setNonce, setPlatformOptions } from '@stencil/core/internal/client';
|
|
1
|
+
export { getAssetPath, render, setAssetPath, setNonce, setPlatformOptions } from '@stencil/core/internal/client';
|
|
2
2
|
export { PennlibsAutocomplete, defineCustomElement as defineCustomElementPennlibsAutocomplete } from './pennlibs-autocomplete.js';
|
|
3
3
|
export { PennlibsBanner, defineCustomElement as defineCustomElementPennlibsBanner } from './pennlibs-banner.js';
|
|
4
4
|
export { PennlibsChat, defineCustomElement as defineCustomElementPennlibsChat } from './pennlibs-chat.js';
|
|
@@ -3,15 +3,23 @@ import { proxyCustomElement, HTMLElement, createEvent, h } from '@stencil/core/i
|
|
|
3
3
|
const pennlibsAutocompleteCss = ":host {\n display: block;\n width: 100%;\n border-radius: 1.25rem;\n padding: 0;\n border-top: 0;\n position: relative;\n}\n\n[role=listbox] {\n position: absolute;\n margin-top: var(--pl-space-xs);\n background: var(--pl-color-bg-default);\n border-radius: 1.25rem;\n box-shadow: rgba(140, 149, 159, 0.3) 0px 8px 24px 0px;\n width: 100%;\n overflow: hidden;\n z-index: 1;\n\n display: flex;\n flex-direction: column;\n}\n\np {\n margin: 0;\n font-size: var(--pl-font-size-s);\n color: var(--pl-color-fg-subtle);\n padding: var(--pl-space-xs) calc(var(--pl-space-m) + var(--pl-space-2xs));\n font-size: var(--pl-font-size-s);\n order: 2;\n font-weight: 500;\n background: var(--pl-color-bg-subtle);\n border-radius: 0 0 1.25rem 1.25rem;\n\n display: flex;\n gap: var(--pl-space-s) var(--pl-space-l);\n flex-wrap: wrap;\n}\n\nol {\n list-style: none;\n margin: 0;\n padding: var(--pl-space-xs) 0;\n order: 1;\n}\n\n[role=option] {\n color: var(--pl-color-fg-default);\n padding: var(--pl-space-s) calc(var(--pl-space-m) + var(--pl-space-2xs));\n text-decoration: none;\n font-weight: 700; \n\n &:hover {\n cursor: pointer;\n }\n\n &:hover,\n &:focus {\n text-decoration-thickness: 2px;\n text-underline-offset: 2px;\n text-decoration: underline;\n }\n}\n\n[aria-selected=true] {\n text-decoration-thickness: 2px;\n text-underline-offset: 2px;\n text-decoration: underline;\n}\n\nmark,\nb {\n background: none;\n font-weight: 400;\n}\n\n.suggestion--border {\n border-bottom: solid 1px rgb(from var(--pl-color-fg-default) r g b / 0.2);\n padding-bottom: calc(var(--pl-space-2xs) + var(--pl-space-s));\n margin-bottom: var(--pl-space-2xs);\n}";
|
|
4
4
|
|
|
5
5
|
const Autocomplete = /*@__PURE__*/ proxyCustomElement(class Autocomplete extends HTMLElement {
|
|
6
|
-
constructor() {
|
|
6
|
+
constructor(registerHost) {
|
|
7
7
|
super();
|
|
8
|
-
|
|
8
|
+
if (registerHost !== false) {
|
|
9
|
+
this.__registerHost();
|
|
10
|
+
}
|
|
9
11
|
this.__attachShadow();
|
|
10
12
|
this.activated = createEvent(this, "pl:activated", 7);
|
|
11
13
|
this.showSuggestions = false;
|
|
12
14
|
this.currentIndex = -1;
|
|
13
15
|
this.originalValue = '';
|
|
14
16
|
this.options = [];
|
|
17
|
+
this.keyHandlerMap = {
|
|
18
|
+
'Escape': () => this.handleEscape(),
|
|
19
|
+
'ArrowDown': () => this.handleArrowDown(),
|
|
20
|
+
'ArrowUp': () => this.handleArrowUp(),
|
|
21
|
+
'Enter': () => this.handleEnter()
|
|
22
|
+
};
|
|
15
23
|
this.handleOptionClick = (index) => {
|
|
16
24
|
this.currentIndex = index;
|
|
17
25
|
this.selectCurrent();
|
|
@@ -63,7 +71,12 @@ const Autocomplete = /*@__PURE__*/ proxyCustomElement(class Autocomplete extends
|
|
|
63
71
|
if (!this.for)
|
|
64
72
|
return null;
|
|
65
73
|
const slot = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('slot[name="start"]');
|
|
66
|
-
|
|
74
|
+
if (!slot)
|
|
75
|
+
return null;
|
|
76
|
+
const assignedElements = slot.assignedElements();
|
|
77
|
+
if (assignedElements.length === 0)
|
|
78
|
+
return null;
|
|
79
|
+
const input = assignedElements[0].querySelector(`input#${this.for}`);
|
|
67
80
|
return input || null;
|
|
68
81
|
}
|
|
69
82
|
isInputFocused() {
|
|
@@ -122,35 +135,30 @@ const Autocomplete = /*@__PURE__*/ proxyCustomElement(class Autocomplete extends
|
|
|
122
135
|
return;
|
|
123
136
|
if (event.metaKey || event.ctrlKey)
|
|
124
137
|
return; // Ignore keyboard shortcuts
|
|
125
|
-
const handler = this.
|
|
138
|
+
const handler = this.keyHandlerMap[event.key];
|
|
126
139
|
if (handler) {
|
|
127
|
-
|
|
140
|
+
// Don't prevent default for Enter - allow form submission
|
|
141
|
+
if (event.key !== 'Enter') {
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
}
|
|
128
144
|
handler();
|
|
129
145
|
}
|
|
130
146
|
}
|
|
131
|
-
keyHandlers() {
|
|
132
|
-
return {
|
|
133
|
-
'Escape': () => this.handleEscape(),
|
|
134
|
-
'ArrowDown': () => this.handleArrowDown(),
|
|
135
|
-
'ArrowUp': () => this.handleArrowUp(),
|
|
136
|
-
'Enter': () => this.handleEnter()
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
147
|
handleEscape() {
|
|
140
148
|
this.reset();
|
|
141
|
-
this.
|
|
149
|
+
this.syncAll();
|
|
142
150
|
}
|
|
143
151
|
handleArrowDown() {
|
|
144
152
|
if (!this.showSuggestions)
|
|
145
153
|
return;
|
|
146
154
|
this.moveNext();
|
|
147
|
-
this.
|
|
155
|
+
this.syncAll();
|
|
148
156
|
}
|
|
149
157
|
handleArrowUp() {
|
|
150
158
|
if (!this.showSuggestions)
|
|
151
159
|
return;
|
|
152
160
|
this.movePrevious();
|
|
153
|
-
this.
|
|
161
|
+
this.syncAll();
|
|
154
162
|
}
|
|
155
163
|
handleEnter() {
|
|
156
164
|
if (this.canSelect())
|
|
@@ -172,16 +180,22 @@ const Autocomplete = /*@__PURE__*/ proxyCustomElement(class Autocomplete extends
|
|
|
172
180
|
if (this.currentIndex < 0)
|
|
173
181
|
return;
|
|
174
182
|
const selectedOption = this.options[this.currentIndex];
|
|
183
|
+
this.applySelection(selectedOption);
|
|
184
|
+
this.emitActivation(selectedOption);
|
|
185
|
+
this.closeSuggestions();
|
|
186
|
+
}
|
|
187
|
+
applySelection(option) {
|
|
175
188
|
const input = this.getInput();
|
|
176
|
-
if (input)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
189
|
+
if (!input)
|
|
190
|
+
return;
|
|
191
|
+
input.value = option.value;
|
|
192
|
+
this.originalValue = option.value;
|
|
193
|
+
}
|
|
194
|
+
emitActivation(option) {
|
|
180
195
|
this.activated.emit({
|
|
181
196
|
index: this.currentIndex,
|
|
182
|
-
value:
|
|
197
|
+
value: option.value
|
|
183
198
|
});
|
|
184
|
-
this.closeSuggestions();
|
|
185
199
|
}
|
|
186
200
|
openSuggestions() {
|
|
187
201
|
if (!this.isInputFocused())
|
|
@@ -190,17 +204,18 @@ const Autocomplete = /*@__PURE__*/ proxyCustomElement(class Autocomplete extends
|
|
|
190
204
|
if (input)
|
|
191
205
|
this.originalValue = input.value;
|
|
192
206
|
this.showSuggestionsPanel();
|
|
193
|
-
this.syncInputState();
|
|
194
207
|
}
|
|
195
208
|
showSuggestionsPanel() {
|
|
196
209
|
this.showSuggestions = true;
|
|
197
|
-
this.
|
|
210
|
+
this.syncAriaAttributes();
|
|
198
211
|
}
|
|
199
212
|
closeSuggestions() {
|
|
200
213
|
this.reset();
|
|
201
|
-
this.
|
|
214
|
+
this.syncAll();
|
|
202
215
|
}
|
|
203
216
|
deferCloseSuggestions() {
|
|
217
|
+
if (this.blurTimeout)
|
|
218
|
+
clearTimeout(this.blurTimeout);
|
|
204
219
|
this.blurTimeout = setTimeout(() => {
|
|
205
220
|
if (this.isFocusOutsideComponent())
|
|
206
221
|
this.closeSuggestions();
|
|
@@ -221,40 +236,48 @@ const Autocomplete = /*@__PURE__*/ proxyCustomElement(class Autocomplete extends
|
|
|
221
236
|
const active = document.activeElement;
|
|
222
237
|
return !((_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(active)) && active !== this.getInput();
|
|
223
238
|
}
|
|
224
|
-
|
|
239
|
+
syncAriaAttributes() {
|
|
225
240
|
const input = this.getInput();
|
|
226
241
|
if (!input)
|
|
227
242
|
return;
|
|
228
243
|
this.updateAriaExpanded(input);
|
|
229
244
|
this.updateAriaActiveDescendant(input);
|
|
230
|
-
|
|
245
|
+
}
|
|
246
|
+
syncInputValueToSelection() {
|
|
247
|
+
const input = this.getInput();
|
|
248
|
+
if (!input)
|
|
249
|
+
return;
|
|
250
|
+
input.value = this.currentIndex >= 0 && this.currentIndex < this.options.length
|
|
251
|
+
? this.options[this.currentIndex].value
|
|
252
|
+
: this.originalValue;
|
|
253
|
+
}
|
|
254
|
+
syncAll() {
|
|
255
|
+
this.syncAriaAttributes();
|
|
256
|
+
this.syncInputValueToSelection();
|
|
231
257
|
}
|
|
232
258
|
updateAriaExpanded(input) {
|
|
233
259
|
input.setAttribute('aria-expanded', this.showSuggestions.toString());
|
|
234
260
|
}
|
|
235
261
|
updateAriaActiveDescendant(input) {
|
|
236
|
-
const hasSelection = this.showSuggestions &&
|
|
237
|
-
|
|
262
|
+
const hasSelection = this.showSuggestions &&
|
|
263
|
+
this.currentIndex >= 0 &&
|
|
264
|
+
this.currentIndex < this.options.length;
|
|
265
|
+
if (hasSelection) {
|
|
238
266
|
input.setAttribute('aria-activedescendant', this.options[this.currentIndex].id);
|
|
239
267
|
}
|
|
240
268
|
else {
|
|
241
269
|
input.removeAttribute('aria-activedescendant');
|
|
242
270
|
}
|
|
243
271
|
}
|
|
244
|
-
updateInputValue(input) {
|
|
245
|
-
input.value = this.currentIndex >= 0
|
|
246
|
-
? this.options[this.currentIndex].value
|
|
247
|
-
: this.originalValue;
|
|
248
|
-
}
|
|
249
272
|
shouldShowListbox() {
|
|
250
273
|
return this.showSuggestions && this.options.length > 0 && this.isInputFocused();
|
|
251
274
|
}
|
|
252
275
|
render() {
|
|
253
|
-
return (h("div", { key: '
|
|
276
|
+
return (h("div", { key: 'ee4ead1b5bec44a561901a29d3d5e119da0d09f8' }, h("slot", { key: '7d7128ab7968544ab25125d30a648136f8d8a566', name: "start" }), this.shouldShowListbox() && (h("ol", { key: '95d45d524847f83b646853a8d919bc88f471f150', role: "listbox", id: "listbox" }, this.options.map((option, index) => (h("li", { role: "option", id: option.id, "aria-selected": this.currentIndex === index ? 'true' : 'false', tabindex: "-1", onClick: () => this.handleOptionClick(index), innerHTML: option.html })))))));
|
|
254
277
|
}
|
|
255
278
|
get el() { return this; }
|
|
256
279
|
static get style() { return pennlibsAutocompleteCss; }
|
|
257
|
-
}, [
|
|
280
|
+
}, [257, "pennlibs-autocomplete", {
|
|
258
281
|
"for": [1],
|
|
259
282
|
"showSuggestions": [32],
|
|
260
283
|
"currentIndex": [32],
|