@penn-libraries/web 1.1.1-dev.1 → 1.2.0-dev.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-C0qvW4Ra.js → index-DJo51Q03.js} +58 -4
- package/dist/cjs/index-DJo51Q03.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 +2 -2
- package/dist/cjs/pennlibs-hero.entry.cjs.js.map +1 -1
- package/dist/cjs/web.cjs.js +2 -2
- package/dist/collection/assets/fonts/perpetua.woff +0 -0
- package/dist/collection/assets/fonts/perpetua.woff2 +0 -0
- package/dist/collection/components/pennlibs-hero/pennlibs-hero.css +1 -1
- package/dist/collection/components/pennlibs-iiif-img/pennlibs-iiif-img.css +9 -2
- package/dist/collection/components/pennlibs-iiif-img/pennlibs-iiif-img.js +147 -25
- package/dist/collection/components/pennlibs-iiif-img/pennlibs-iiif-img.js.map +1 -1
- package/dist/components/pennlibs-hero.js +1 -1
- package/dist/components/pennlibs-hero.js.map +1 -1
- package/dist/components/pennlibs-iiif-img.js +144 -23
- package/dist/components/pennlibs-iiif-img.js.map +1 -1
- package/dist/docs.json +4 -4
- package/dist/{web/p-D9dYrmUF.js → esm/index-RqnbThKP.js} +58 -4
- package/dist/esm/index-RqnbThKP.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 +2 -2
- package/dist/esm/pennlibs-hero.entry.js.map +1 -1
- package/dist/esm/web.js +3 -3
- package/dist/types/components/pennlibs-iiif-img/pennlibs-iiif-img.d.ts +45 -4
- package/dist/types/components.d.ts +10 -10
- package/dist/web/assets/fonts/perpetua.woff +0 -0
- package/dist/web/assets/fonts/perpetua.woff2 +0 -0
- package/dist/web/index.esm.js +1 -1
- package/dist/web/{p-b4b58af0.entry.js → p-780e656e.entry.js} +1 -1
- package/dist/web/{p-43d9c2d4.entry.js → p-8ac5ef70.entry.js} +1 -1
- package/dist/{esm/index-D9dYrmUF.js → web/p-RqnbThKP.js} +58 -4
- package/dist/web/p-RqnbThKP.js.map +1 -0
- package/dist/web/{p-cb2584da.entry.js → p-b7b01d67.entry.js} +2 -2
- package/dist/web/{p-ad92090a.entry.js → p-ce09ae2e.entry.js} +1 -1
- package/dist/web/{p-e6188c30.entry.js → p-f37f3865.entry.js} +260 -3
- package/dist/web/pennlibs-autocomplete.pennlibs-fallback-img.pennlibs-footer.pennlibs-header.pennlibs-iiif-img.entry.esm.js.map +1 -0
- package/dist/web/pennlibs-hero.entry.esm.js.map +1 -1
- package/dist/web/web.css +36 -15
- package/dist/web/web.esm.js +3 -3
- package/hydrate/index.js +190 -31
- package/hydrate/index.mjs +190 -31
- package/package.json +1 -1
- package/dist/cjs/index-C0qvW4Ra.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/cjs/pennlibs-iiif-img.cjs.entry.js +0 -132
- package/dist/cjs/pennlibs-iiif-img.entry.cjs.js.map +0 -1
- package/dist/esm/index-D9dYrmUF.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/esm/pennlibs-iiif-img.entry.js +0 -130
- package/dist/esm/pennlibs-iiif-img.entry.js.map +0 -1
- package/dist/web/p-D9dYrmUF.js.map +0 -1
- package/dist/web/p-c4074cf1.entry.js +0 -130
- package/dist/web/p-ce97059c.entry.js +0 -18
- 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/dist/web/pennlibs-iiif-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
|
|
@@ -2103,6 +2103,14 @@ var renderVdom = (hostRef, renderFnResults, isInitialLoad = false) => {
|
|
|
2103
2103
|
const isHostElement = isHost(renderFnResults);
|
|
2104
2104
|
const rootVnode = isHostElement ? renderFnResults : h(null, null, renderFnResults);
|
|
2105
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
|
+
}
|
|
2106
2114
|
if (isInitialLoad && rootVnode.$attrs$) {
|
|
2107
2115
|
for (const key of Object.keys(rootVnode.$attrs$)) {
|
|
2108
2116
|
if (hostElm.hasAttribute(key) && !["key", "ref", "style", "class"].includes(key)) {
|
|
@@ -2404,6 +2412,7 @@ var setValue = (ref, propName, newVal, cmpMeta) => {
|
|
|
2404
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).`
|
|
2405
2413
|
);
|
|
2406
2414
|
}
|
|
2415
|
+
const elm = hostRef.$hostElement$ ;
|
|
2407
2416
|
const oldVal = hostRef.$instanceValues$.get(propName);
|
|
2408
2417
|
const flags = hostRef.$flags$;
|
|
2409
2418
|
const instance = hostRef.$lazyInstance$ ;
|
|
@@ -2415,6 +2424,18 @@ var setValue = (ref, propName, newVal, cmpMeta) => {
|
|
|
2415
2424
|
if ((!(flags & 8 /* isConstructingInstance */) || oldVal === void 0) && didValueChange) {
|
|
2416
2425
|
hostRef.$instanceValues$.set(propName, newVal);
|
|
2417
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
|
+
}
|
|
2418
2439
|
if ((flags & (2 /* hasRendered */ | 16 /* isQueuedForUpdate */)) === 2 /* hasRendered */) {
|
|
2419
2440
|
if (instance.componentShouldUpdate) {
|
|
2420
2441
|
if (instance.componentShouldUpdate(newVal, oldVal, propName) === false) {
|
|
@@ -2431,7 +2452,18 @@ var setValue = (ref, propName, newVal, cmpMeta) => {
|
|
|
2431
2452
|
var proxyComponent = (Cstr, cmpMeta, flags) => {
|
|
2432
2453
|
var _a;
|
|
2433
2454
|
const prototype = Cstr.prototype;
|
|
2434
|
-
|
|
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
|
+
}
|
|
2435
2467
|
const members = Object.entries((_a = cmpMeta.$members$) != null ? _a : {});
|
|
2436
2468
|
members.map(([memberName, [memberFlags]]) => {
|
|
2437
2469
|
if ((memberFlags & 31 /* Prop */ || memberFlags & 32 /* State */)) {
|
|
@@ -2508,6 +2540,11 @@ var initializeComponent = async (elm, hostRef, cmpMeta, hmrVersionId) => {
|
|
|
2508
2540
|
throw new Error(`Constructor for "${cmpMeta.$tagName$}#${hostRef.$modeName$}" was not found`);
|
|
2509
2541
|
}
|
|
2510
2542
|
if (!Cstr.isProxied) {
|
|
2543
|
+
{
|
|
2544
|
+
cmpMeta.$watchers$ = Cstr.watchers;
|
|
2545
|
+
cmpMeta.$serializers$ = Cstr.serializers;
|
|
2546
|
+
cmpMeta.$deserializers$ = Cstr.deserializers;
|
|
2547
|
+
}
|
|
2511
2548
|
proxyComponent(Cstr, cmpMeta);
|
|
2512
2549
|
Cstr.isProxied = true;
|
|
2513
2550
|
}
|
|
@@ -2523,6 +2560,9 @@ var initializeComponent = async (elm, hostRef, cmpMeta, hmrVersionId) => {
|
|
|
2523
2560
|
{
|
|
2524
2561
|
hostRef.$flags$ &= -9 /* isConstructingInstance */;
|
|
2525
2562
|
}
|
|
2563
|
+
{
|
|
2564
|
+
hostRef.$flags$ |= 128 /* isWatchReady */;
|
|
2565
|
+
}
|
|
2526
2566
|
endNewInstance();
|
|
2527
2567
|
fireConnectedCallback(hostRef.$lazyInstance$, elm);
|
|
2528
2568
|
} else {
|
|
@@ -3798,7 +3838,7 @@ class Header {
|
|
|
3798
3838
|
}; }
|
|
3799
3839
|
}
|
|
3800
3840
|
|
|
3801
|
-
const pennlibsHeroCss = ":host{--pl-hero-height:clamp(42vh, 32rem, 26rem);--pl-hero-heading-font:var(--pl-font-serif);--pl-hero-color:var(--pl-color-fg-on-emphasis)}*,*:before,*:after{box-sizing:inherit}.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.viewport-margins{width:100%;max-width:var(--pl-viewport-margins-max-width);margin:0 auto;padding:0 var(--pl-viewport-margins-gutter, 1em)}.hero{position:relative;min-height:var(--pl-hero-height);height:100%;background-size:cover;background-repeat:no-repeat;background-position:50% 33%;display:flex}.hero::before{content:\"\";display:flex;width:100%;height:100%;top:0;position:absolute;background:linear-gradient(360deg, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.7) 20%, rgba(0, 0, 0, 0.4) 40%, rgba(0, 0, 0, 0.05) 100%);z-index:0}.hero::after{content:\"\";display:flex;width:100%;height:100%;top:0;position:absolute;background:linear-gradient(180deg, rgba(1, 31, 91, 1) 0%, rgba(1, 31, 91, 0.9) 10%, rgba(1, 31, 91, 0.8) 20%, rgba(1, 31, 91, 0.1) 50%, rgba(1, 31, 91, 0.0) 100%);z-index:0}.hero__content{position:relative;display:flex;flex-direction:column;width:100%;z-index:1}.hero__heading-container{margin-top:auto;padding-top:var(--pl-space-3xl);padding-bottom:var(--pl-space-3xl)}.hero__heading{text-shadow:1px 1px 2px var(--pl-color-fg-default);line-height:1.1;font-size:3em;font-weight:
|
|
3841
|
+
const pennlibsHeroCss = ":host{--pl-hero-height:clamp(42vh, 32rem, 26rem);--pl-hero-heading-font:var(--pl-font-serif);--pl-hero-color:var(--pl-color-fg-on-emphasis)}*,*:before,*:after{box-sizing:inherit}.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.viewport-margins{width:100%;max-width:var(--pl-viewport-margins-max-width);margin:0 auto;padding:0 var(--pl-viewport-margins-gutter, 1em)}.hero{position:relative;min-height:var(--pl-hero-height);height:100%;background-size:cover;background-repeat:no-repeat;background-position:50% 33%;display:flex}.hero::before{content:\"\";display:flex;width:100%;height:100%;top:0;position:absolute;background:linear-gradient(360deg, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.7) 20%, rgba(0, 0, 0, 0.4) 40%, rgba(0, 0, 0, 0.05) 100%);z-index:0}.hero::after{content:\"\";display:flex;width:100%;height:100%;top:0;position:absolute;background:linear-gradient(180deg, rgba(1, 31, 91, 1) 0%, rgba(1, 31, 91, 0.9) 10%, rgba(1, 31, 91, 0.8) 20%, rgba(1, 31, 91, 0.1) 50%, rgba(1, 31, 91, 0.0) 100%);z-index:0}.hero__content{position:relative;display:flex;flex-direction:column;width:100%;z-index:1}.hero__heading-container{margin-top:auto;padding-top:var(--pl-space-3xl);padding-bottom:var(--pl-space-3xl)}.hero__heading{text-shadow:1px 1px 2px var(--pl-color-fg-default);line-height:1.1;font-size:3em;font-weight:400;font-family:var(--pl-hero-heading-font);text-wrap:pretty;max-width:30ch;margin:0;color:var(--pl-hero-color)}@media (max-width: 920px){.hero__heading{font-size:2.5em}}.hero__sub-heading{font-size:1.25em;font-family:var(--pl-font-family);font-weight:500;color:var(--pl-hero-color);max-width:52ch;text-wrap:pretty;margin-top:1em;margin-bottom:0}.hero__sub-heading a{text-decoration:underline;text-underline-offset:var(--pl-link-text-underline-offset);text-decoration-thickness:var(--pl-link-text-decoration-thickness);color:var(--pl-hero-color)}.hero__sub-heading a:hover{text-decoration-thickness:var(--pl-link-hover-text-decoration-thickness)}.hero__sub-heading strong{font-weight:bold}@media (max-width: 620px){.hero__heading{font-size:2.75em}.hero__sub-heading{font-size:1em}}";
|
|
3802
3842
|
|
|
3803
3843
|
const getCurrentImageSource = (pictureElement) => {
|
|
3804
3844
|
const imgElement = pictureElement.querySelector('img');
|
|
@@ -3873,14 +3913,13 @@ class Hero {
|
|
|
3873
3913
|
}; }
|
|
3874
3914
|
}
|
|
3875
3915
|
|
|
3876
|
-
const pennlibsIiifImgCss = ":host{display:block;width:100%;max-width:100
|
|
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%}";
|
|
3877
3917
|
|
|
3878
|
-
const
|
|
3879
|
-
const DEFAULT_IMAGE_SIZES = [400, 600, 800, 1200, 1600, 2400];
|
|
3918
|
+
const PIXEL_DENSITIES = [1, 1.5, 2];
|
|
3880
3919
|
/**
|
|
3881
3920
|
* Display responsive, high-quality images from Penn Libraries' IIIF Image API 3.0 server.
|
|
3882
|
-
*
|
|
3883
|
-
* with modern WebP format and JPG fallback support.
|
|
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.
|
|
3884
3923
|
*
|
|
3885
3924
|
* @component
|
|
3886
3925
|
* @example
|
|
@@ -3900,6 +3939,10 @@ class IIIFImg {
|
|
|
3900
3939
|
*
|
|
3901
3940
|
* `square`: A square area where width and height equal the shorter dimension.
|
|
3902
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
|
+
*
|
|
3903
3946
|
* `x,y,w,h`: Absolute pixel coordinates (x, y position; w, h dimensions).
|
|
3904
3947
|
*
|
|
3905
3948
|
* `pct:x,y,w,h`: Percentage-based coordinates of full image dimensions.
|
|
@@ -3954,26 +3997,137 @@ class IIIFImg {
|
|
|
3954
3997
|
this.hasError = true;
|
|
3955
3998
|
};
|
|
3956
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
|
+
}
|
|
3957
4098
|
buildIIIFUrl(width, format = 'jpg') {
|
|
3958
4099
|
const sizeParam = width ? `${width},` : 'max';
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
const
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
}
|
|
3969
|
-
|
|
3970
|
-
if
|
|
3971
|
-
|
|
3972
|
-
return
|
|
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();
|
|
3973
4123
|
}
|
|
3974
|
-
return [...DEFAULT_IMAGE_SIZES];
|
|
3975
4124
|
}
|
|
3976
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
|
+
}
|
|
3977
4131
|
if ('ResizeObserver' in window) {
|
|
3978
4132
|
this.resizeObserver = new ResizeObserver(entries => {
|
|
3979
4133
|
for (const entry of entries) {
|
|
@@ -3982,8 +4136,6 @@ class IIIFImg {
|
|
|
3982
4136
|
this.resizeObserver.disconnect();
|
|
3983
4137
|
this.resizeObserver = undefined;
|
|
3984
4138
|
}
|
|
3985
|
-
// Trigger re-render with optimized sizes
|
|
3986
|
-
this.isLoaded = this.isLoaded;
|
|
3987
4139
|
}
|
|
3988
4140
|
});
|
|
3989
4141
|
this.resizeObserver.observe(this.hostElement);
|
|
@@ -3996,7 +4148,10 @@ class IIIFImg {
|
|
|
3996
4148
|
}
|
|
3997
4149
|
}
|
|
3998
4150
|
render() {
|
|
3999
|
-
|
|
4151
|
+
// Show placeholder until we've measured the component width
|
|
4152
|
+
if (!this.observedWidth) {
|
|
4153
|
+
return hAsync("pennlibs-fallback-img", null);
|
|
4154
|
+
}
|
|
4000
4155
|
if (this.hasError && this.showFallback) {
|
|
4001
4156
|
return (hAsync("div", { class: "fallback-container" }, hAsync("pennlibs-fallback-img", null)));
|
|
4002
4157
|
}
|
|
@@ -4004,10 +4159,12 @@ class IIIFImg {
|
|
|
4004
4159
|
'iiif-img': true,
|
|
4005
4160
|
'loaded': this.isLoaded
|
|
4006
4161
|
};
|
|
4007
|
-
|
|
4008
|
-
return (hAsync("picture", null, hAsync("source", { type: "image/webp", srcSet: this.generateSrcset(sizes, 'webp'), sizes: "100vw" }), hAsync("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 })));
|
|
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 })));
|
|
4009
4163
|
}
|
|
4010
4164
|
get hostElement() { return getElement(this); }
|
|
4165
|
+
static get watchers() { return {
|
|
4166
|
+
"region": ["handleRegionChange"]
|
|
4167
|
+
}; }
|
|
4011
4168
|
static get style() { return pennlibsIiifImgCss; }
|
|
4012
4169
|
static get cmpMeta() { return {
|
|
4013
4170
|
"$flags$": 265,
|
|
@@ -4015,17 +4172,19 @@ class IIIFImg {
|
|
|
4015
4172
|
"$members$": {
|
|
4016
4173
|
"uuid": [1],
|
|
4017
4174
|
"alt": [1],
|
|
4018
|
-
"region": [
|
|
4175
|
+
"region": [513],
|
|
4019
4176
|
"rotation": [1],
|
|
4020
4177
|
"quality": [1],
|
|
4021
4178
|
"loading": [1],
|
|
4022
4179
|
"showFallback": [4, "show-fallback"],
|
|
4023
4180
|
"isLoaded": [32],
|
|
4024
|
-
"hasError": [32]
|
|
4181
|
+
"hasError": [32],
|
|
4182
|
+
"imageDimensions": [32],
|
|
4183
|
+
"observedWidth": [32]
|
|
4025
4184
|
},
|
|
4026
4185
|
"$listeners$": undefined,
|
|
4027
4186
|
"$lazyBundleId$": "-",
|
|
4028
|
-
"$attrsToReflect$": []
|
|
4187
|
+
"$attrsToReflect$": [["region", "region"]]
|
|
4029
4188
|
}; }
|
|
4030
4189
|
}
|
|
4031
4190
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@penn-libraries/web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0-dev.0",
|
|
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",
|