@penn-libraries/web 1.1.0 → 1.1.1-dev.2
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-4ixBJUwu.js → index-DVr0pLZy.js} +62 -3
- package/dist/cjs/index-DVr0pLZy.js.map +1 -0
- package/dist/cjs/index.cjs.js +1 -1
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/pennlibs-autocomplete.pennlibs-fallback-img.pennlibs-footer.pennlibs-header.pennlibs-iiif-img.entry.cjs.js.map +1 -0
- package/dist/cjs/{pennlibs-autocomplete_3.cjs.entry.js → pennlibs-autocomplete_5.cjs.entry.js} +261 -2
- package/dist/cjs/pennlibs-banner.cjs.entry.js +1 -1
- package/dist/cjs/pennlibs-chat.cjs.entry.js +1 -1
- package/dist/cjs/pennlibs-feedback.cjs.entry.js +1 -1
- package/dist/cjs/pennlibs-hero.cjs.entry.js +1 -1
- package/dist/cjs/web.cjs.js +2 -2
- package/dist/collection/collection-manifest.json +2 -1
- package/dist/collection/components/pennlibs-iiif-img/pennlibs-iiif-img.css +44 -0
- package/dist/collection/components/pennlibs-iiif-img/pennlibs-iiif-img.js +432 -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 +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/pennlibs-fallback-img.js +1 -32
- 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-iiif-img.d.ts +11 -0
- package/dist/components/pennlibs-iiif-img.js +289 -0
- package/dist/components/pennlibs-iiif-img.js.map +1 -0
- package/dist/docs.json +239 -3
- package/dist/{web/p-Di48-Vtw.js → esm/index-Cst_89-s.js} +62 -3
- package/dist/esm/index-Cst_89-s.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/loader.js +3 -3
- package/dist/esm/pennlibs-autocomplete.pennlibs-fallback-img.pennlibs-footer.pennlibs-header.pennlibs-iiif-img.entry.js.map +1 -0
- package/dist/esm/{pennlibs-autocomplete_3.entry.js → pennlibs-autocomplete_5.entry.js} +260 -3
- package/dist/esm/pennlibs-banner.entry.js +1 -1
- package/dist/esm/pennlibs-chat.entry.js +1 -1
- package/dist/esm/pennlibs-feedback.entry.js +1 -1
- package/dist/esm/pennlibs-hero.entry.js +1 -1
- package/dist/esm/web.js +3 -3
- package/dist/types/components/pennlibs-iiif-img/pennlibs-iiif-img.d.ts +130 -0
- package/dist/types/components.d.ts +119 -0
- package/dist/web/index.esm.js +1 -1
- package/dist/web/{p-6503bb09.entry.js → p-4ffdbc93.entry.js} +1 -1
- package/dist/web/{p-56b9f10c.entry.js → p-621f166e.entry.js} +1 -1
- package/dist/web/{p-269363bf.entry.js → p-6e0c2de9.entry.js} +260 -3
- package/dist/web/{p-783e2a87.entry.js → p-848d9acc.entry.js} +1 -1
- package/dist/{esm/index-Di48-Vtw.js → web/p-Cst_89-s.js} +62 -3
- package/dist/web/p-Cst_89-s.js.map +1 -0
- package/dist/web/{p-7cbd16c0.entry.js → p-a9c79310.entry.js} +1 -1
- package/dist/web/pennlibs-autocomplete.pennlibs-fallback-img.pennlibs-footer.pennlibs-header.pennlibs-iiif-img.entry.esm.js.map +1 -0
- package/dist/web/web.esm.js +3 -3
- package/hydrate/index.js +323 -2
- package/hydrate/index.mjs +323 -2
- package/package.json +1 -1
- package/dist/cjs/index-4ixBJUwu.js.map +0 -1
- package/dist/cjs/pennlibs-autocomplete.pennlibs-footer.pennlibs-header.entry.cjs.js.map +0 -1
- package/dist/cjs/pennlibs-fallback-img.cjs.entry.js +0 -20
- package/dist/cjs/pennlibs-fallback-img.entry.cjs.js.map +0 -1
- package/dist/esm/index-Di48-Vtw.js.map +0 -1
- package/dist/esm/pennlibs-autocomplete.pennlibs-footer.pennlibs-header.entry.js.map +0 -1
- package/dist/esm/pennlibs-fallback-img.entry.js +0 -18
- package/dist/esm/pennlibs-fallback-img.entry.js.map +0 -1
- package/dist/web/p-09e4a02c.entry.js +0 -18
- package/dist/web/p-Di48-Vtw.js.map +0 -1
- package/dist/web/pennlibs-autocomplete.pennlibs-footer.pennlibs-header.entry.esm.js.map +0 -1
- package/dist/web/pennlibs-fallback-img.entry.esm.js.map +0 -1
package/hydrate/index.mjs
CHANGED
|
@@ -125,7 +125,7 @@ function hydrateFactory($stencilWindow, $stencilHydrateOpts, $stencilHydrateResu
|
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
const NAMESPACE = 'web';
|
|
128
|
-
const BUILD = /* web */ { hydratedSelectorName: "hydrated",
|
|
128
|
+
const BUILD = /* web */ { hydratedSelectorName: "hydrated", slotRelocation: true, updatable: true};
|
|
129
129
|
|
|
130
130
|
/*
|
|
131
131
|
Stencil Hydrate Platform v4.38.1 | MIT Licensed | https://stenciljs.com
|
|
@@ -1528,6 +1528,11 @@ var parsePropertyValue = (propValue, propType, isFormAssociated) => {
|
|
|
1528
1528
|
return propValue;
|
|
1529
1529
|
}
|
|
1530
1530
|
if (propValue != null && !isComplexType(propValue)) {
|
|
1531
|
+
if (propType & 4 /* Boolean */) {
|
|
1532
|
+
{
|
|
1533
|
+
return propValue === "false" ? false : propValue === "" || !!propValue;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1531
1536
|
if (propType & 1 /* String */) {
|
|
1532
1537
|
return String(propValue);
|
|
1533
1538
|
}
|
|
@@ -2098,6 +2103,14 @@ var renderVdom = (hostRef, renderFnResults, isInitialLoad = false) => {
|
|
|
2098
2103
|
const isHostElement = isHost(renderFnResults);
|
|
2099
2104
|
const rootVnode = isHostElement ? renderFnResults : h(null, null, renderFnResults);
|
|
2100
2105
|
hostTagName = hostElm.tagName;
|
|
2106
|
+
if (cmpMeta.$attrsToReflect$) {
|
|
2107
|
+
rootVnode.$attrs$ = rootVnode.$attrs$ || {};
|
|
2108
|
+
cmpMeta.$attrsToReflect$.forEach(([propName, attribute]) => {
|
|
2109
|
+
{
|
|
2110
|
+
rootVnode.$attrs$[attribute] = hostElm[propName];
|
|
2111
|
+
}
|
|
2112
|
+
});
|
|
2113
|
+
}
|
|
2101
2114
|
if (isInitialLoad && rootVnode.$attrs$) {
|
|
2102
2115
|
for (const key of Object.keys(rootVnode.$attrs$)) {
|
|
2103
2116
|
if (hostElm.hasAttribute(key) && !["key", "ref", "style", "class"].includes(key)) {
|
|
@@ -2399,6 +2412,7 @@ var setValue = (ref, propName, newVal, cmpMeta) => {
|
|
|
2399
2412
|
`Couldn't find host element for "${cmpMeta.$tagName$}" as it is unknown to this Stencil runtime. This usually happens when integrating a 3rd party Stencil component with another Stencil component or application. Please reach out to the maintainers of the 3rd party Stencil component or report this on the Stencil Discord server (https://chat.stenciljs.com) or comment on this similar [GitHub issue](https://github.com/stenciljs/core/issues/5457).`
|
|
2400
2413
|
);
|
|
2401
2414
|
}
|
|
2415
|
+
const elm = hostRef.$hostElement$ ;
|
|
2402
2416
|
const oldVal = hostRef.$instanceValues$.get(propName);
|
|
2403
2417
|
const flags = hostRef.$flags$;
|
|
2404
2418
|
const instance = hostRef.$lazyInstance$ ;
|
|
@@ -2410,6 +2424,18 @@ var setValue = (ref, propName, newVal, cmpMeta) => {
|
|
|
2410
2424
|
if ((!(flags & 8 /* isConstructingInstance */) || oldVal === void 0) && didValueChange) {
|
|
2411
2425
|
hostRef.$instanceValues$.set(propName, newVal);
|
|
2412
2426
|
if (instance) {
|
|
2427
|
+
if (cmpMeta.$watchers$ && flags & 128 /* isWatchReady */) {
|
|
2428
|
+
const watchMethods = cmpMeta.$watchers$[propName];
|
|
2429
|
+
if (watchMethods) {
|
|
2430
|
+
watchMethods.map((watchMethodName) => {
|
|
2431
|
+
try {
|
|
2432
|
+
instance[watchMethodName](newVal, oldVal, propName);
|
|
2433
|
+
} catch (e) {
|
|
2434
|
+
consoleError(e, elm);
|
|
2435
|
+
}
|
|
2436
|
+
});
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2413
2439
|
if ((flags & (2 /* hasRendered */ | 16 /* isQueuedForUpdate */)) === 2 /* hasRendered */) {
|
|
2414
2440
|
if (instance.componentShouldUpdate) {
|
|
2415
2441
|
if (instance.componentShouldUpdate(newVal, oldVal, propName) === false) {
|
|
@@ -2426,7 +2452,18 @@ var setValue = (ref, propName, newVal, cmpMeta) => {
|
|
|
2426
2452
|
var proxyComponent = (Cstr, cmpMeta, flags) => {
|
|
2427
2453
|
var _a;
|
|
2428
2454
|
const prototype = Cstr.prototype;
|
|
2429
|
-
|
|
2455
|
+
{
|
|
2456
|
+
{
|
|
2457
|
+
if (Cstr.watchers && !cmpMeta.$watchers$) {
|
|
2458
|
+
cmpMeta.$watchers$ = Cstr.watchers;
|
|
2459
|
+
}
|
|
2460
|
+
if (Cstr.deserializers && !cmpMeta.$deserializers$) {
|
|
2461
|
+
cmpMeta.$deserializers$ = Cstr.deserializers;
|
|
2462
|
+
}
|
|
2463
|
+
if (Cstr.serializers && !cmpMeta.$serializers$) {
|
|
2464
|
+
cmpMeta.$serializers$ = Cstr.serializers;
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2430
2467
|
const members = Object.entries((_a = cmpMeta.$members$) != null ? _a : {});
|
|
2431
2468
|
members.map(([memberName, [memberFlags]]) => {
|
|
2432
2469
|
if ((memberFlags & 31 /* Prop */ || memberFlags & 32 /* State */)) {
|
|
@@ -2503,6 +2540,11 @@ var initializeComponent = async (elm, hostRef, cmpMeta, hmrVersionId) => {
|
|
|
2503
2540
|
throw new Error(`Constructor for "${cmpMeta.$tagName$}#${hostRef.$modeName$}" was not found`);
|
|
2504
2541
|
}
|
|
2505
2542
|
if (!Cstr.isProxied) {
|
|
2543
|
+
{
|
|
2544
|
+
cmpMeta.$watchers$ = Cstr.watchers;
|
|
2545
|
+
cmpMeta.$serializers$ = Cstr.serializers;
|
|
2546
|
+
cmpMeta.$deserializers$ = Cstr.deserializers;
|
|
2547
|
+
}
|
|
2506
2548
|
proxyComponent(Cstr, cmpMeta);
|
|
2507
2549
|
Cstr.isProxied = true;
|
|
2508
2550
|
}
|
|
@@ -2518,6 +2560,9 @@ var initializeComponent = async (elm, hostRef, cmpMeta, hmrVersionId) => {
|
|
|
2518
2560
|
{
|
|
2519
2561
|
hostRef.$flags$ &= -9 /* isConstructingInstance */;
|
|
2520
2562
|
}
|
|
2563
|
+
{
|
|
2564
|
+
hostRef.$flags$ |= 128 /* isWatchReady */;
|
|
2565
|
+
}
|
|
2521
2566
|
endNewInstance();
|
|
2522
2567
|
fireConnectedCallback(hostRef.$lazyInstance$, elm);
|
|
2523
2568
|
} else {
|
|
@@ -3868,6 +3913,281 @@ class Hero {
|
|
|
3868
3913
|
}; }
|
|
3869
3914
|
}
|
|
3870
3915
|
|
|
3916
|
+
const pennlibsIiifImgCss = ":host{display:block;width:100%;max-width:100%;align-self:flex-start;aspect-ratio:var(--aspect-ratio, auto)}picture{display:block;line-height:0}.iiif-img{display:block;width:100%;max-width:100%;height:auto;opacity:0;filter:blur(5px);transition:opacity 0.15s ease-in, filter 0.15s ease-in}.iiif-img.loaded{opacity:1;filter:blur(0)}.fallback-container{display:block;width:100%;height:100%}.fallback-container pennlibs-fallback-img{width:100%;height:100%}pennlibs-fallback-img{display:block;width:100%;height:100%}";
|
|
3917
|
+
|
|
3918
|
+
const PIXEL_DENSITIES = [1, 1.5, 2];
|
|
3919
|
+
/**
|
|
3920
|
+
* Display responsive, high-quality images from Penn Libraries' IIIF Image API 3.0 server.
|
|
3921
|
+
* Measures its own rendered width and automatically generates optimal image sources at
|
|
3922
|
+
* multiple pixel densities (1x, 1.5x, 2x) with modern WebP format and JPG fallback support.
|
|
3923
|
+
*
|
|
3924
|
+
* @component
|
|
3925
|
+
* @example
|
|
3926
|
+
* <pennlibs-iiif-img
|
|
3927
|
+
* uuid="063ff35c-bbdd-4b63-bbdd-6206590e20d5"
|
|
3928
|
+
* alt="">
|
|
3929
|
+
* </pennlibs-iiif-img>
|
|
3930
|
+
*/
|
|
3931
|
+
class IIIFImg {
|
|
3932
|
+
constructor(hostRef) {
|
|
3933
|
+
registerInstance(this, hostRef);
|
|
3934
|
+
/**
|
|
3935
|
+
* The IIIF [region](https://iiif.io/api/image/3.0/#41-region) of the image to display.
|
|
3936
|
+
* Defines the rectangular portion of the underlying image to return.
|
|
3937
|
+
*
|
|
3938
|
+
* `full`: The full image is returned, without any cropping.
|
|
3939
|
+
*
|
|
3940
|
+
* `square`: A square area where width and height equal the shorter dimension.
|
|
3941
|
+
*
|
|
3942
|
+
* `width:height`: Any aspect ratio format (e.g., `16:9`, `4:3`, `3:2`, `21:9`) applies
|
|
3943
|
+
* a centered crop based on the source image dimensions and sets the CSS aspect-ratio
|
|
3944
|
+
* property for layout reservation.
|
|
3945
|
+
*
|
|
3946
|
+
* `x,y,w,h`: Absolute pixel coordinates (x, y position; w, h dimensions).
|
|
3947
|
+
*
|
|
3948
|
+
* `pct:x,y,w,h`: Percentage-based coordinates of full image dimensions.
|
|
3949
|
+
*
|
|
3950
|
+
* @default 'full'
|
|
3951
|
+
*/
|
|
3952
|
+
this.region = 'full';
|
|
3953
|
+
/**
|
|
3954
|
+
* The IIIF [rotation](https://iiif.io/api/image/3.0/#44-rotation) to apply to the image.
|
|
3955
|
+
* Specifies mirroring and clockwise rotation in degrees (0-360).
|
|
3956
|
+
*
|
|
3957
|
+
* `n`: Rotation in degrees only.
|
|
3958
|
+
*
|
|
3959
|
+
* `!n`: Mirror the image vertically, then rotate by n degrees.
|
|
3960
|
+
*
|
|
3961
|
+
* @default '0'
|
|
3962
|
+
*/
|
|
3963
|
+
this.rotation = '0';
|
|
3964
|
+
/**
|
|
3965
|
+
* The IIIF [quality](https://iiif.io/api/image/3.0/#quality) of the image.
|
|
3966
|
+
* Controls the color delivery mode.
|
|
3967
|
+
*
|
|
3968
|
+
* `default`: The server's default quality.
|
|
3969
|
+
*
|
|
3970
|
+
* `color`: Full color information.
|
|
3971
|
+
*
|
|
3972
|
+
* `gray`: Grayscale rendering.
|
|
3973
|
+
*
|
|
3974
|
+
* `bitonal`: Black and white only.
|
|
3975
|
+
*
|
|
3976
|
+
* @default 'default'
|
|
3977
|
+
*/
|
|
3978
|
+
this.quality = 'default';
|
|
3979
|
+
/**
|
|
3980
|
+
* Native browser lazy loading behavior. Use "lazy" to defer loading until the image is near the viewport,
|
|
3981
|
+
* or "eager" to load immediately.
|
|
3982
|
+
* @default 'lazy'
|
|
3983
|
+
*/
|
|
3984
|
+
this.loading = 'lazy';
|
|
3985
|
+
/**
|
|
3986
|
+
* Whether to display a fallback placeholder image when the IIIF image fails to load.
|
|
3987
|
+
* @default true
|
|
3988
|
+
*/
|
|
3989
|
+
this.showFallback = true;
|
|
3990
|
+
this.isLoaded = false;
|
|
3991
|
+
this.hasError = false;
|
|
3992
|
+
this.baseUrl = 'https://iiif-images.library.upenn.edu/iiif/3';
|
|
3993
|
+
this.handleLoad = () => {
|
|
3994
|
+
this.isLoaded = true;
|
|
3995
|
+
};
|
|
3996
|
+
this.handleError = () => {
|
|
3997
|
+
this.hasError = true;
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
/**
|
|
4001
|
+
* Fetch IIIF image info to get actual dimensions.
|
|
4002
|
+
*/
|
|
4003
|
+
async fetchImageInfo() {
|
|
4004
|
+
try {
|
|
4005
|
+
const currentUuid = this.uuid; // Capture current UUID
|
|
4006
|
+
const infoUrl = `${this.baseUrl}/${currentUuid}/info.json`;
|
|
4007
|
+
const response = await fetch(infoUrl);
|
|
4008
|
+
if (!response.ok) {
|
|
4009
|
+
console.error(`Failed to fetch IIIF info.json for ${currentUuid}`);
|
|
4010
|
+
return;
|
|
4011
|
+
}
|
|
4012
|
+
const info = await response.json();
|
|
4013
|
+
// Only update dimensions if UUID hasn't changed during fetch
|
|
4014
|
+
// (protects against race conditions if component updates)
|
|
4015
|
+
if (this.uuid === currentUuid) {
|
|
4016
|
+
this.imageDimensions = {
|
|
4017
|
+
width: info.width,
|
|
4018
|
+
height: info.height,
|
|
4019
|
+
};
|
|
4020
|
+
this.fetchedUuid = currentUuid;
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
catch (error) {
|
|
4024
|
+
console.error(`Error fetching IIIF image info for ${this.uuid}:`, error);
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
/**
|
|
4028
|
+
* Check if a region value matches aspect ratio format (e.g., "16:9", "4:3", "3:2").
|
|
4029
|
+
*/
|
|
4030
|
+
isAspectRatio(value) {
|
|
4031
|
+
return /^\d+:\d+$/.test(value);
|
|
4032
|
+
}
|
|
4033
|
+
/**
|
|
4034
|
+
* Set CSS custom property for aspect ratio on the host element.
|
|
4035
|
+
*/
|
|
4036
|
+
setAspectRatioCss(aspectRatio) {
|
|
4037
|
+
const [widthRatio, heightRatio] = aspectRatio.split(':');
|
|
4038
|
+
this.hostElement.style.setProperty('--aspect-ratio', `${widthRatio} / ${heightRatio}`);
|
|
4039
|
+
}
|
|
4040
|
+
/**
|
|
4041
|
+
* Clear CSS custom property for aspect ratio from the host element.
|
|
4042
|
+
*/
|
|
4043
|
+
clearAspectRatioCss() {
|
|
4044
|
+
this.hostElement.style.removeProperty('--aspect-ratio');
|
|
4045
|
+
}
|
|
4046
|
+
/**
|
|
4047
|
+
* Format a number for IIIF percentage values (remove trailing zeros).
|
|
4048
|
+
* IIIF spec requires no trailing zeros per section 4.7.
|
|
4049
|
+
*/
|
|
4050
|
+
formatPercent(value) {
|
|
4051
|
+
return value.toFixed(2).replace(/\.?0+$/, '');
|
|
4052
|
+
}
|
|
4053
|
+
/**
|
|
4054
|
+
* Calculate a centered crop region for a given aspect ratio.
|
|
4055
|
+
* Requires actual image dimensions to calculate correctly.
|
|
4056
|
+
* @param aspectRatio - The target aspect ratio (e.g., "3:2" or "2:3")
|
|
4057
|
+
* @returns A IIIF percentage-based region string (e.g., "pct:0,16.67,100,66.67")
|
|
4058
|
+
*/
|
|
4059
|
+
calculateCenteredCrop(aspectRatio) {
|
|
4060
|
+
if (!this.imageDimensions) {
|
|
4061
|
+
throw new Error('Image dimensions required for aspect ratio calculation');
|
|
4062
|
+
}
|
|
4063
|
+
const [widthRatio, heightRatio] = aspectRatio.split(':').map(Number);
|
|
4064
|
+
const targetRatio = widthRatio / heightRatio;
|
|
4065
|
+
const sourceRatio = this.imageDimensions.width / this.imageDimensions.height;
|
|
4066
|
+
// Determine if we need to crop width or height
|
|
4067
|
+
if (sourceRatio > targetRatio) {
|
|
4068
|
+
// Source is wider than target - crop left and right
|
|
4069
|
+
const widthPercent = (targetRatio / sourceRatio) * 100;
|
|
4070
|
+
const xOffset = (100 - widthPercent) / 2;
|
|
4071
|
+
return `pct:${this.formatPercent(xOffset)},0,${this.formatPercent(widthPercent)},100`;
|
|
4072
|
+
}
|
|
4073
|
+
else {
|
|
4074
|
+
// Source is taller than target - crop top and bottom
|
|
4075
|
+
const heightPercent = (sourceRatio / targetRatio) * 100;
|
|
4076
|
+
const yOffset = (100 - heightPercent) / 2;
|
|
4077
|
+
return `pct:0,${this.formatPercent(yOffset)},100,${this.formatPercent(heightPercent)}`;
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
/**
|
|
4081
|
+
* Get the region parameter for the IIIF URL.
|
|
4082
|
+
*/
|
|
4083
|
+
getRegionParam() {
|
|
4084
|
+
// Handle custom aspect ratios
|
|
4085
|
+
let regionParam = this.region || 'full';
|
|
4086
|
+
if (this.isAspectRatio(regionParam)) {
|
|
4087
|
+
// Only apply crop if we have dimensions for the current UUID
|
|
4088
|
+
if (this.imageDimensions && this.fetchedUuid === this.uuid) {
|
|
4089
|
+
regionParam = this.calculateCenteredCrop(regionParam);
|
|
4090
|
+
}
|
|
4091
|
+
else {
|
|
4092
|
+
// Use full image until dimensions are loaded
|
|
4093
|
+
regionParam = 'full';
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
return regionParam;
|
|
4097
|
+
}
|
|
4098
|
+
buildIIIFUrl(width, format = 'jpg') {
|
|
4099
|
+
const sizeParam = width ? `${width},` : 'max';
|
|
4100
|
+
const regionParam = this.getRegionParam();
|
|
4101
|
+
return `${this.baseUrl}/${this.uuid}/${regionParam}/${sizeParam}/${this.rotation}/${this.quality}.${format}`;
|
|
4102
|
+
}
|
|
4103
|
+
generateSrcset(baseWidth, format) {
|
|
4104
|
+
return PIXEL_DENSITIES.map(density => {
|
|
4105
|
+
const width = Math.round(baseWidth * density);
|
|
4106
|
+
const url = this.buildIIIFUrl(width, format);
|
|
4107
|
+
return `${url} ${density}x`;
|
|
4108
|
+
}).join(', ');
|
|
4109
|
+
}
|
|
4110
|
+
handleRegionChange(newValue, oldValue) {
|
|
4111
|
+
// Only process if region actually changed
|
|
4112
|
+
if (newValue === oldValue) {
|
|
4113
|
+
return;
|
|
4114
|
+
}
|
|
4115
|
+
// If new region is an aspect ratio, set CSS and fetch info
|
|
4116
|
+
if (newValue && this.isAspectRatio(newValue)) {
|
|
4117
|
+
this.setAspectRatioCss(newValue);
|
|
4118
|
+
this.fetchImageInfo();
|
|
4119
|
+
}
|
|
4120
|
+
else {
|
|
4121
|
+
// Clear aspect ratio CSS if no longer using aspect ratio format
|
|
4122
|
+
this.clearAspectRatioCss();
|
|
4123
|
+
}
|
|
4124
|
+
}
|
|
4125
|
+
componentDidLoad() {
|
|
4126
|
+
// Set CSS aspect ratio and fetch image info if using aspect ratio format
|
|
4127
|
+
if (this.region && this.isAspectRatio(this.region)) {
|
|
4128
|
+
this.setAspectRatioCss(this.region);
|
|
4129
|
+
this.fetchImageInfo();
|
|
4130
|
+
}
|
|
4131
|
+
if ('ResizeObserver' in window) {
|
|
4132
|
+
this.resizeObserver = new ResizeObserver(entries => {
|
|
4133
|
+
for (const entry of entries) {
|
|
4134
|
+
this.observedWidth = Math.round(entry.contentRect.width);
|
|
4135
|
+
if (this.resizeObserver) {
|
|
4136
|
+
this.resizeObserver.disconnect();
|
|
4137
|
+
this.resizeObserver = undefined;
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4140
|
+
});
|
|
4141
|
+
this.resizeObserver.observe(this.hostElement);
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
disconnectedCallback() {
|
|
4145
|
+
if (this.resizeObserver) {
|
|
4146
|
+
this.resizeObserver.disconnect();
|
|
4147
|
+
this.resizeObserver = undefined;
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
render() {
|
|
4151
|
+
// Show placeholder until we've measured the component width
|
|
4152
|
+
if (!this.observedWidth) {
|
|
4153
|
+
return hAsync("pennlibs-fallback-img", null);
|
|
4154
|
+
}
|
|
4155
|
+
if (this.hasError && this.showFallback) {
|
|
4156
|
+
return (hAsync("div", { class: "fallback-container" }, hAsync("pennlibs-fallback-img", null)));
|
|
4157
|
+
}
|
|
4158
|
+
const imgClasses = {
|
|
4159
|
+
'iiif-img': true,
|
|
4160
|
+
'loaded': this.isLoaded
|
|
4161
|
+
};
|
|
4162
|
+
return (hAsync("picture", null, hAsync("source", { type: "image/webp", srcSet: this.generateSrcset(this.observedWidth, 'webp') }), hAsync("img", { class: imgClasses, src: this.buildIIIFUrl(this.observedWidth, 'jpg'), srcSet: this.generateSrcset(this.observedWidth, 'jpg'), alt: this.alt, loading: this.loading, onLoad: this.handleLoad, onError: this.handleError })));
|
|
4163
|
+
}
|
|
4164
|
+
get hostElement() { return getElement(this); }
|
|
4165
|
+
static get watchers() { return {
|
|
4166
|
+
"region": ["handleRegionChange"]
|
|
4167
|
+
}; }
|
|
4168
|
+
static get style() { return pennlibsIiifImgCss; }
|
|
4169
|
+
static get cmpMeta() { return {
|
|
4170
|
+
"$flags$": 265,
|
|
4171
|
+
"$tagName$": "pennlibs-iiif-img",
|
|
4172
|
+
"$members$": {
|
|
4173
|
+
"uuid": [1],
|
|
4174
|
+
"alt": [1],
|
|
4175
|
+
"region": [513],
|
|
4176
|
+
"rotation": [1],
|
|
4177
|
+
"quality": [1],
|
|
4178
|
+
"loading": [1],
|
|
4179
|
+
"showFallback": [4, "show-fallback"],
|
|
4180
|
+
"isLoaded": [32],
|
|
4181
|
+
"hasError": [32],
|
|
4182
|
+
"imageDimensions": [32],
|
|
4183
|
+
"observedWidth": [32]
|
|
4184
|
+
},
|
|
4185
|
+
"$listeners$": undefined,
|
|
4186
|
+
"$lazyBundleId$": "-",
|
|
4187
|
+
"$attrsToReflect$": [["region", "region"]]
|
|
4188
|
+
}; }
|
|
4189
|
+
}
|
|
4190
|
+
|
|
3871
4191
|
const pennlibsFallbackImgCss = ":host{font-family:var(--pl-font-family);font-size:var(--pl-font-size);display:flex;align-items:center;justify-content:center;padding:1rem;background:var(--pl-color-bg-subtle);aspect-ratio:3/2}.no-image__img{width:100%;max-width:150px;filter:grayscale(1) opacity(0.3)}";
|
|
3872
4192
|
|
|
3873
4193
|
class NoImage {
|
|
@@ -3960,6 +4280,7 @@ registerComponents([
|
|
|
3960
4280
|
Footer,
|
|
3961
4281
|
Header,
|
|
3962
4282
|
Hero,
|
|
4283
|
+
IIIFImg,
|
|
3963
4284
|
NoImage,
|
|
3964
4285
|
PennlibsFeedback,
|
|
3965
4286
|
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@penn-libraries/web",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1-dev.2",
|
|
4
4
|
"description": "Reusable web designs for University of Pennsylvania Libraries websites. This package contains our code for shared Web Components and Cascading Style Sheets (CSS).",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.js",
|