@penn-libraries/web 1.1.0-dev.7 → 1.1.1-dev.1
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-C0qvW4Ra.js} +289 -191
- package/dist/cjs/index-C0qvW4Ra.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 +25 -36
- 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/pennlibs-iiif-img.cjs.entry.js +132 -0
- package/dist/cjs/pennlibs-iiif-img.entry.cjs.js.map +1 -0
- package/dist/cjs/web.cjs.js +4 -7
- package/dist/cjs/web.cjs.js.map +1 -1
- package/dist/collection/collection-manifest.json +4 -3
- package/dist/collection/components/pennlibs-autocomplete/pennlibs-autocomplete.js +40 -23
- 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/collection/components/pennlibs-iiif-img/pennlibs-iiif-img.css +37 -0
- package/dist/collection/components/pennlibs-iiif-img/pennlibs-iiif-img.js +310 -0
- package/dist/collection/components/pennlibs-iiif-img/pennlibs-iiif-img.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/pennlibs-autocomplete.js +28 -20
- 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 +1 -30
- package/dist/components/pennlibs-fallback-img.js.map +1 -1
- package/dist/components/pennlibs-fallback-img2.js +37 -0
- package/dist/components/pennlibs-fallback-img2.js.map +1 -0
- 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/components/pennlibs-iiif-img.d.ts +11 -0
- package/dist/components/pennlibs-iiif-img.js +168 -0
- package/dist/components/pennlibs-iiif-img.js.map +1 -0
- package/dist/docs.json +268 -12
- package/dist/esm/{index-CE_ILdTo.js → index-D9dYrmUF.js} +289 -192
- package/dist/esm/index-D9dYrmUF.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 +25 -36
- 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/pennlibs-iiif-img.entry.js +130 -0
- package/dist/esm/pennlibs-iiif-img.entry.js.map +1 -0
- 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 +17 -1
- package/dist/types/components/pennlibs-iiif-img/pennlibs-iiif-img.d.ts +89 -0
- package/dist/types/components.d.ts +143 -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-20c0bd7a.entry.js → p-43d9c2d4.entry.js} +1 -3
- package/dist/web/{p-CE_ILdTo.js → p-D9dYrmUF.js} +289 -192
- package/dist/web/p-D9dYrmUF.js.map +1 -0
- package/dist/web/{p-cbae5952.entry.js → p-ad92090a.entry.js} +1 -3
- package/dist/web/{p-370e32b1.entry.js → p-b4b58af0.entry.js} +1 -3
- package/dist/web/p-c4074cf1.entry.js +130 -0
- package/dist/web/{p-07ad051f.entry.js → p-cb2584da.entry.js} +1 -3
- package/dist/web/{p-5e385536.entry.js → p-ce97059c.entry.js} +1 -3
- package/dist/web/{p-e1215158.entry.js → p-e6188c30.entry.js} +25 -36
- package/dist/web/pennlibs-autocomplete.pennlibs-footer.pennlibs-header.entry.esm.js.map +1 -1
- package/dist/web/pennlibs-iiif-img.entry.esm.js.map +1 -0
- 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 +609 -241
- package/hydrate/index.mjs +609 -241
- 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-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
- package/dist/web/p-e1215158.entry.js.map +0 -1
|
@@ -1,4 +1,9 @@
|
|
|
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;
|
|
@@ -137,22 +142,19 @@ export class Autocomplete {
|
|
|
137
142
|
}
|
|
138
143
|
handleEscape() {
|
|
139
144
|
this.reset();
|
|
140
|
-
this.
|
|
141
|
-
this.syncInputValueToSelection();
|
|
145
|
+
this.syncAll();
|
|
142
146
|
}
|
|
143
147
|
handleArrowDown() {
|
|
144
148
|
if (!this.showSuggestions)
|
|
145
149
|
return;
|
|
146
150
|
this.moveNext();
|
|
147
|
-
this.
|
|
148
|
-
this.syncInputValueToSelection();
|
|
151
|
+
this.syncAll();
|
|
149
152
|
}
|
|
150
153
|
handleArrowUp() {
|
|
151
154
|
if (!this.showSuggestions)
|
|
152
155
|
return;
|
|
153
156
|
this.movePrevious();
|
|
154
|
-
this.
|
|
155
|
-
this.syncInputValueToSelection();
|
|
157
|
+
this.syncAll();
|
|
156
158
|
}
|
|
157
159
|
handleEnter() {
|
|
158
160
|
if (this.canSelect())
|
|
@@ -174,16 +176,22 @@ export class Autocomplete {
|
|
|
174
176
|
if (this.currentIndex < 0)
|
|
175
177
|
return;
|
|
176
178
|
const selectedOption = this.options[this.currentIndex];
|
|
179
|
+
this.applySelection(selectedOption);
|
|
180
|
+
this.emitActivation(selectedOption);
|
|
181
|
+
this.closeSuggestions();
|
|
182
|
+
}
|
|
183
|
+
applySelection(option) {
|
|
177
184
|
const input = this.getInput();
|
|
178
|
-
if (input)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
185
|
+
if (!input)
|
|
186
|
+
return;
|
|
187
|
+
input.value = option.value;
|
|
188
|
+
this.originalValue = option.value;
|
|
189
|
+
}
|
|
190
|
+
emitActivation(option) {
|
|
182
191
|
this.activated.emit({
|
|
183
192
|
index: this.currentIndex,
|
|
184
|
-
value:
|
|
193
|
+
value: option.value
|
|
185
194
|
});
|
|
186
|
-
this.closeSuggestions();
|
|
187
195
|
}
|
|
188
196
|
openSuggestions() {
|
|
189
197
|
if (!this.isInputFocused())
|
|
@@ -195,12 +203,11 @@ export class Autocomplete {
|
|
|
195
203
|
}
|
|
196
204
|
showSuggestionsPanel() {
|
|
197
205
|
this.showSuggestions = true;
|
|
198
|
-
this.
|
|
206
|
+
this.syncAriaAttributes();
|
|
199
207
|
}
|
|
200
208
|
closeSuggestions() {
|
|
201
209
|
this.reset();
|
|
202
|
-
this.
|
|
203
|
-
this.syncInputValueToSelection();
|
|
210
|
+
this.syncAll();
|
|
204
211
|
}
|
|
205
212
|
deferCloseSuggestions() {
|
|
206
213
|
if (this.blurTimeout)
|
|
@@ -225,7 +232,7 @@ export class Autocomplete {
|
|
|
225
232
|
const active = document.activeElement;
|
|
226
233
|
return !((_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(active)) && active !== this.getInput();
|
|
227
234
|
}
|
|
228
|
-
|
|
235
|
+
syncAriaAttributes() {
|
|
229
236
|
const input = this.getInput();
|
|
230
237
|
if (!input)
|
|
231
238
|
return;
|
|
@@ -240,6 +247,10 @@ export class Autocomplete {
|
|
|
240
247
|
? this.options[this.currentIndex].value
|
|
241
248
|
: this.originalValue;
|
|
242
249
|
}
|
|
250
|
+
syncAll() {
|
|
251
|
+
this.syncAriaAttributes();
|
|
252
|
+
this.syncInputValueToSelection();
|
|
253
|
+
}
|
|
243
254
|
updateAriaExpanded(input) {
|
|
244
255
|
input.setAttribute('aria-expanded', this.showSuggestions.toString());
|
|
245
256
|
}
|
|
@@ -258,7 +269,7 @@ export class Autocomplete {
|
|
|
258
269
|
return this.showSuggestions && this.options.length > 0 && this.isInputFocused();
|
|
259
270
|
}
|
|
260
271
|
render() {
|
|
261
|
-
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 })))))));
|
|
262
273
|
}
|
|
263
274
|
static get is() { return "pennlibs-autocomplete"; }
|
|
264
275
|
static get encapsulation() { return "shadow"; }
|
|
@@ -276,7 +287,6 @@ export class Autocomplete {
|
|
|
276
287
|
return {
|
|
277
288
|
"for": {
|
|
278
289
|
"type": "string",
|
|
279
|
-
"attribute": "for",
|
|
280
290
|
"mutable": false,
|
|
281
291
|
"complexType": {
|
|
282
292
|
"original": "string",
|
|
@@ -286,12 +296,16 @@ export class Autocomplete {
|
|
|
286
296
|
"required": false,
|
|
287
297
|
"optional": true,
|
|
288
298
|
"docs": {
|
|
289
|
-
"tags": [
|
|
290
|
-
|
|
299
|
+
"tags": [{
|
|
300
|
+
"name": "prop",
|
|
301
|
+
"text": "for"
|
|
302
|
+
}],
|
|
303
|
+
"text": "The `id` of the input that this autocomplete is attached to."
|
|
291
304
|
},
|
|
292
305
|
"getter": false,
|
|
293
306
|
"setter": false,
|
|
294
|
-
"reflect": false
|
|
307
|
+
"reflect": false,
|
|
308
|
+
"attribute": "for"
|
|
295
309
|
}
|
|
296
310
|
};
|
|
297
311
|
}
|
|
@@ -311,8 +325,11 @@ export class Autocomplete {
|
|
|
311
325
|
"cancelable": true,
|
|
312
326
|
"composed": true,
|
|
313
327
|
"docs": {
|
|
314
|
-
"tags": [
|
|
315
|
-
|
|
328
|
+
"tags": [{
|
|
329
|
+
"name": "event",
|
|
330
|
+
"text": "pl:activated"
|
|
331
|
+
}],
|
|
332
|
+
"text": "Emitted when a user activates (selects) an autocomplete suggestion."
|
|
316
333
|
},
|
|
317
334
|
"complexType": {
|
|
318
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 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.syncInputState();\n this.syncInputValueToSelection();\n }\n\n private handleArrowDown() {\n if (!this.showSuggestions) return;\n this.moveNext();\n this.syncInputState();\n this.syncInputValueToSelection();\n }\n\n private handleArrowUp() {\n if (!this.showSuggestions) return;\n this.movePrevious();\n this.syncInputState();\n this.syncInputValueToSelection();\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 }\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 this.syncInputValueToSelection();\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 syncInputState(): 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 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"]}
|
|
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
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
width: 100%;
|
|
4
|
+
max-width: 100%;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
picture {
|
|
8
|
+
display: block;
|
|
9
|
+
line-height: 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.iiif-img {
|
|
13
|
+
display: block;
|
|
14
|
+
width: 100%;
|
|
15
|
+
max-width: 100%;
|
|
16
|
+
height: auto;
|
|
17
|
+
opacity: 0;
|
|
18
|
+
filter: blur(5px);
|
|
19
|
+
transition: opacity 0.15s ease-in, filter 0.15s ease-in;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.iiif-img.loaded {
|
|
23
|
+
opacity: 1;
|
|
24
|
+
filter: blur(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.fallback-container {
|
|
28
|
+
display: block;
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: auto;
|
|
31
|
+
min-height: 200px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.fallback-container pennlibs-fallback-img {
|
|
35
|
+
width: 100%;
|
|
36
|
+
height: 100%;
|
|
37
|
+
}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
const PIXEL_DENSITY_MULTIPLIERS = [0.5, 0.75, 1, 1.5, 2];
|
|
3
|
+
const DEFAULT_IMAGE_SIZES = [400, 600, 800, 1200, 1600, 2400];
|
|
4
|
+
/**
|
|
5
|
+
* Display responsive, high-quality images from Penn Libraries' IIIF Image API 3.0 server.
|
|
6
|
+
* Automatically generates optimal image sources for different viewport sizes and pixel densities,
|
|
7
|
+
* with modern WebP format and JPG fallback support.
|
|
8
|
+
*
|
|
9
|
+
* @component
|
|
10
|
+
* @example
|
|
11
|
+
* <pennlibs-iiif-img
|
|
12
|
+
* uuid="063ff35c-bbdd-4b63-bbdd-6206590e20d5"
|
|
13
|
+
* alt="">
|
|
14
|
+
* </pennlibs-iiif-img>
|
|
15
|
+
*/
|
|
16
|
+
export class IIIFImg {
|
|
17
|
+
constructor() {
|
|
18
|
+
/**
|
|
19
|
+
* The IIIF [region](https://iiif.io/api/image/3.0/#41-region) of the image to display.
|
|
20
|
+
* Defines the rectangular portion of the underlying image to return.
|
|
21
|
+
*
|
|
22
|
+
* `full`: The full image is returned, without any cropping.
|
|
23
|
+
*
|
|
24
|
+
* `square`: A square area where width and height equal the shorter dimension.
|
|
25
|
+
*
|
|
26
|
+
* `x,y,w,h`: Absolute pixel coordinates (x, y position; w, h dimensions).
|
|
27
|
+
*
|
|
28
|
+
* `pct:x,y,w,h`: Percentage-based coordinates of full image dimensions.
|
|
29
|
+
*
|
|
30
|
+
* @default 'full'
|
|
31
|
+
*/
|
|
32
|
+
this.region = 'full';
|
|
33
|
+
/**
|
|
34
|
+
* The IIIF [rotation](https://iiif.io/api/image/3.0/#44-rotation) to apply to the image.
|
|
35
|
+
* Specifies mirroring and clockwise rotation in degrees (0-360).
|
|
36
|
+
*
|
|
37
|
+
* `n`: Rotation in degrees only.
|
|
38
|
+
*
|
|
39
|
+
* `!n`: Mirror the image vertically, then rotate by n degrees.
|
|
40
|
+
*
|
|
41
|
+
* @default '0'
|
|
42
|
+
*/
|
|
43
|
+
this.rotation = '0';
|
|
44
|
+
/**
|
|
45
|
+
* The IIIF [quality](https://iiif.io/api/image/3.0/#quality) of the image.
|
|
46
|
+
* Controls the color delivery mode.
|
|
47
|
+
*
|
|
48
|
+
* `default`: The server's default quality.
|
|
49
|
+
*
|
|
50
|
+
* `color`: Full color information.
|
|
51
|
+
*
|
|
52
|
+
* `gray`: Grayscale rendering.
|
|
53
|
+
*
|
|
54
|
+
* `bitonal`: Black and white only.
|
|
55
|
+
*
|
|
56
|
+
* @default 'default'
|
|
57
|
+
*/
|
|
58
|
+
this.quality = 'default';
|
|
59
|
+
/**
|
|
60
|
+
* Native browser lazy loading behavior. Use "lazy" to defer loading until the image is near the viewport,
|
|
61
|
+
* or "eager" to load immediately.
|
|
62
|
+
* @default 'lazy'
|
|
63
|
+
*/
|
|
64
|
+
this.loading = 'lazy';
|
|
65
|
+
/**
|
|
66
|
+
* Whether to display a fallback placeholder image when the IIIF image fails to load.
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
this.showFallback = true;
|
|
70
|
+
this.isLoaded = false;
|
|
71
|
+
this.hasError = false;
|
|
72
|
+
this.baseUrl = 'https://iiif-images.library.upenn.edu/iiif/3';
|
|
73
|
+
this.handleLoad = () => {
|
|
74
|
+
this.isLoaded = true;
|
|
75
|
+
};
|
|
76
|
+
this.handleError = () => {
|
|
77
|
+
this.hasError = true;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
buildIIIFUrl(width, format = 'jpg') {
|
|
81
|
+
const sizeParam = width ? `${width},` : 'max';
|
|
82
|
+
return `${this.baseUrl}/${this.uuid}/${this.region}/${sizeParam}/${this.rotation}/${this.quality}.${format}`;
|
|
83
|
+
}
|
|
84
|
+
generateSrcset(sizes, format) {
|
|
85
|
+
const uniqueSizes = [...new Set(sizes)].sort((a, b) => a - b);
|
|
86
|
+
const srcsetEntries = uniqueSizes.map(size => {
|
|
87
|
+
const url = this.buildIIIFUrl(size, format);
|
|
88
|
+
return `${url} ${size}w`;
|
|
89
|
+
});
|
|
90
|
+
return srcsetEntries.join(', ');
|
|
91
|
+
}
|
|
92
|
+
getOptimalSizes() {
|
|
93
|
+
if (this.observedWidth) {
|
|
94
|
+
const baseSizes = PIXEL_DENSITY_MULTIPLIERS.map(multiplier => Math.round(this.observedWidth * multiplier));
|
|
95
|
+
return baseSizes;
|
|
96
|
+
}
|
|
97
|
+
return [...DEFAULT_IMAGE_SIZES];
|
|
98
|
+
}
|
|
99
|
+
componentDidLoad() {
|
|
100
|
+
if ('ResizeObserver' in window) {
|
|
101
|
+
this.resizeObserver = new ResizeObserver(entries => {
|
|
102
|
+
for (const entry of entries) {
|
|
103
|
+
this.observedWidth = Math.round(entry.contentRect.width);
|
|
104
|
+
if (this.resizeObserver) {
|
|
105
|
+
this.resizeObserver.disconnect();
|
|
106
|
+
this.resizeObserver = undefined;
|
|
107
|
+
}
|
|
108
|
+
// Trigger re-render with optimized sizes
|
|
109
|
+
this.isLoaded = this.isLoaded;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
this.resizeObserver.observe(this.hostElement);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
disconnectedCallback() {
|
|
116
|
+
if (this.resizeObserver) {
|
|
117
|
+
this.resizeObserver.disconnect();
|
|
118
|
+
this.resizeObserver = undefined;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
render() {
|
|
122
|
+
const sizes = this.getOptimalSizes();
|
|
123
|
+
if (this.hasError && this.showFallback) {
|
|
124
|
+
return (h("div", { class: "fallback-container" }, h("pennlibs-fallback-img", null)));
|
|
125
|
+
}
|
|
126
|
+
const imgClasses = {
|
|
127
|
+
'iiif-img': true,
|
|
128
|
+
'loaded': this.isLoaded
|
|
129
|
+
};
|
|
130
|
+
const defaultSize = sizes[0];
|
|
131
|
+
return (h("picture", null, h("source", { type: "image/webp", srcSet: this.generateSrcset(sizes, 'webp'), sizes: "100vw" }), h("img", { class: imgClasses, src: this.buildIIIFUrl(defaultSize, 'jpg'), srcSet: this.generateSrcset(sizes, 'jpg'), sizes: "100vw", alt: this.alt, loading: this.loading, onLoad: this.handleLoad, onError: this.handleError })));
|
|
132
|
+
}
|
|
133
|
+
static get is() { return "pennlibs-iiif-img"; }
|
|
134
|
+
static get encapsulation() { return "shadow"; }
|
|
135
|
+
static get originalStyleUrls() {
|
|
136
|
+
return {
|
|
137
|
+
"$": ["pennlibs-iiif-img.css"]
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
static get styleUrls() {
|
|
141
|
+
return {
|
|
142
|
+
"$": ["pennlibs-iiif-img.css"]
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
static get properties() {
|
|
146
|
+
return {
|
|
147
|
+
"uuid": {
|
|
148
|
+
"type": "string",
|
|
149
|
+
"mutable": false,
|
|
150
|
+
"complexType": {
|
|
151
|
+
"original": "string",
|
|
152
|
+
"resolved": "string",
|
|
153
|
+
"references": {}
|
|
154
|
+
},
|
|
155
|
+
"required": true,
|
|
156
|
+
"optional": false,
|
|
157
|
+
"docs": {
|
|
158
|
+
"tags": [],
|
|
159
|
+
"text": "The IIIF [UUID identifier](https://iiif.io/api/image/3.0/#3-identifier) of the image resource on the Penn Libraries IIIF server."
|
|
160
|
+
},
|
|
161
|
+
"getter": false,
|
|
162
|
+
"setter": false,
|
|
163
|
+
"reflect": false,
|
|
164
|
+
"attribute": "uuid"
|
|
165
|
+
},
|
|
166
|
+
"alt": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"mutable": false,
|
|
169
|
+
"complexType": {
|
|
170
|
+
"original": "string",
|
|
171
|
+
"resolved": "string",
|
|
172
|
+
"references": {}
|
|
173
|
+
},
|
|
174
|
+
"required": true,
|
|
175
|
+
"optional": false,
|
|
176
|
+
"docs": {
|
|
177
|
+
"tags": [],
|
|
178
|
+
"text": "Alternative text that describes the image content for screen readers and when images fail to load.\nUse an empty string for purely decorative images."
|
|
179
|
+
},
|
|
180
|
+
"getter": false,
|
|
181
|
+
"setter": false,
|
|
182
|
+
"reflect": false,
|
|
183
|
+
"attribute": "alt"
|
|
184
|
+
},
|
|
185
|
+
"region": {
|
|
186
|
+
"type": "string",
|
|
187
|
+
"mutable": false,
|
|
188
|
+
"complexType": {
|
|
189
|
+
"original": "string",
|
|
190
|
+
"resolved": "string",
|
|
191
|
+
"references": {}
|
|
192
|
+
},
|
|
193
|
+
"required": false,
|
|
194
|
+
"optional": true,
|
|
195
|
+
"docs": {
|
|
196
|
+
"tags": [{
|
|
197
|
+
"name": "default",
|
|
198
|
+
"text": "'full'"
|
|
199
|
+
}],
|
|
200
|
+
"text": "The IIIF [region](https://iiif.io/api/image/3.0/#41-region) of the image to display.\nDefines the rectangular portion of the underlying image to return.\n\n`full`: The full image is returned, without any cropping.\n\n`square`: A square area where width and height equal the shorter dimension.\n\n`x,y,w,h`: Absolute pixel coordinates (x, y position; w, h dimensions).\n\n`pct:x,y,w,h`: Percentage-based coordinates of full image dimensions."
|
|
201
|
+
},
|
|
202
|
+
"getter": false,
|
|
203
|
+
"setter": false,
|
|
204
|
+
"reflect": false,
|
|
205
|
+
"attribute": "region",
|
|
206
|
+
"defaultValue": "'full'"
|
|
207
|
+
},
|
|
208
|
+
"rotation": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"mutable": false,
|
|
211
|
+
"complexType": {
|
|
212
|
+
"original": "string",
|
|
213
|
+
"resolved": "string",
|
|
214
|
+
"references": {}
|
|
215
|
+
},
|
|
216
|
+
"required": false,
|
|
217
|
+
"optional": true,
|
|
218
|
+
"docs": {
|
|
219
|
+
"tags": [{
|
|
220
|
+
"name": "default",
|
|
221
|
+
"text": "'0'"
|
|
222
|
+
}],
|
|
223
|
+
"text": "The IIIF [rotation](https://iiif.io/api/image/3.0/#44-rotation) to apply to the image.\nSpecifies mirroring and clockwise rotation in degrees (0-360).\n\n`n`: Rotation in degrees only.\n\n`!n`: Mirror the image vertically, then rotate by n degrees."
|
|
224
|
+
},
|
|
225
|
+
"getter": false,
|
|
226
|
+
"setter": false,
|
|
227
|
+
"reflect": false,
|
|
228
|
+
"attribute": "rotation",
|
|
229
|
+
"defaultValue": "'0'"
|
|
230
|
+
},
|
|
231
|
+
"quality": {
|
|
232
|
+
"type": "string",
|
|
233
|
+
"mutable": false,
|
|
234
|
+
"complexType": {
|
|
235
|
+
"original": "string",
|
|
236
|
+
"resolved": "string",
|
|
237
|
+
"references": {}
|
|
238
|
+
},
|
|
239
|
+
"required": false,
|
|
240
|
+
"optional": true,
|
|
241
|
+
"docs": {
|
|
242
|
+
"tags": [{
|
|
243
|
+
"name": "default",
|
|
244
|
+
"text": "'default'"
|
|
245
|
+
}],
|
|
246
|
+
"text": "The IIIF [quality](https://iiif.io/api/image/3.0/#quality) of the image.\nControls the color delivery mode.\n\n`default`: The server's default quality.\n\n`color`: Full color information.\n\n`gray`: Grayscale rendering.\n\n`bitonal`: Black and white only."
|
|
247
|
+
},
|
|
248
|
+
"getter": false,
|
|
249
|
+
"setter": false,
|
|
250
|
+
"reflect": false,
|
|
251
|
+
"attribute": "quality",
|
|
252
|
+
"defaultValue": "'default'"
|
|
253
|
+
},
|
|
254
|
+
"loading": {
|
|
255
|
+
"type": "string",
|
|
256
|
+
"mutable": false,
|
|
257
|
+
"complexType": {
|
|
258
|
+
"original": "'lazy' | 'eager'",
|
|
259
|
+
"resolved": "\"eager\" | \"lazy\"",
|
|
260
|
+
"references": {}
|
|
261
|
+
},
|
|
262
|
+
"required": false,
|
|
263
|
+
"optional": true,
|
|
264
|
+
"docs": {
|
|
265
|
+
"tags": [{
|
|
266
|
+
"name": "default",
|
|
267
|
+
"text": "'lazy'"
|
|
268
|
+
}],
|
|
269
|
+
"text": "Native browser lazy loading behavior. Use \"lazy\" to defer loading until the image is near the viewport,\nor \"eager\" to load immediately."
|
|
270
|
+
},
|
|
271
|
+
"getter": false,
|
|
272
|
+
"setter": false,
|
|
273
|
+
"reflect": false,
|
|
274
|
+
"attribute": "loading",
|
|
275
|
+
"defaultValue": "'lazy'"
|
|
276
|
+
},
|
|
277
|
+
"showFallback": {
|
|
278
|
+
"type": "boolean",
|
|
279
|
+
"mutable": false,
|
|
280
|
+
"complexType": {
|
|
281
|
+
"original": "boolean",
|
|
282
|
+
"resolved": "boolean",
|
|
283
|
+
"references": {}
|
|
284
|
+
},
|
|
285
|
+
"required": false,
|
|
286
|
+
"optional": true,
|
|
287
|
+
"docs": {
|
|
288
|
+
"tags": [{
|
|
289
|
+
"name": "default",
|
|
290
|
+
"text": "true"
|
|
291
|
+
}],
|
|
292
|
+
"text": "Whether to display a fallback placeholder image when the IIIF image fails to load."
|
|
293
|
+
},
|
|
294
|
+
"getter": false,
|
|
295
|
+
"setter": false,
|
|
296
|
+
"reflect": false,
|
|
297
|
+
"attribute": "show-fallback",
|
|
298
|
+
"defaultValue": "true"
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
static get states() {
|
|
303
|
+
return {
|
|
304
|
+
"isLoaded": {},
|
|
305
|
+
"hasError": {}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
static get elementRef() { return "hostElement"; }
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=pennlibs-iiif-img.js.map
|