@page-speed/img 0.4.6 → 0.4.8

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  BSD 3-Clause License
2
2
 
3
- Copyright (c) 2025, OpenSite AI. All rights reserved.
3
+ Copyright (c) 2026, OpenSite AI. All rights reserved.
4
4
 
5
5
  Redistribution and use in source and binary forms, with or without
6
6
  modification, are permitted provided that the following conditions are met:
package/README.md CHANGED
@@ -1,24 +1,21 @@
1
1
  # ⚡ @page-speed/img
2
2
 
3
- ![Page Speed React Image Component](https://octane.cdn.ing/api/v1/images/transform?url=https://toastability-production.s3.amazonaws.com/gghtx0lxw9f2zigs427qc2phu024&q=85&f=avif)
3
+ ## Performance-optimized React Image component - Drop-in Image implementation of [web.dev](https://web.dev) best practices with zero configuration. Also a great alternative to [next/image](https://nextjs.org/docs/api-reference/next/image) for non-Next.js projects that still need the automated image optimization tools that the Next `Image` component provides. Utilized throughout the [OpenSite Semantic UI Platform](https://opensite.ai).
4
4
 
5
- > **Performance-optimized React Image component**
6
-
7
- Drop-in Image implementation of [web.dev](https://web.dev) best practices with zero configuration. Also a great alternative to [next/image](https://nextjs.org/docs/api-reference/next/image) for non-Next.js projects that still need the automated image optimization tools that the Next `Image` component provides.
5
+ ![Page Speed React Image Component](https://octane.cdn.ing/api/v1/images/transform?url=https://toastability-production.s3.amazonaws.com/gghtx0lxw9f2zigs427qc2phu024&f=webp)
8
6
 
9
7
  [![npm version](https://img.shields.io/npm/v/@page-speed/img?style=flat-square)](https://www.npmjs.com/package/@page-speed/hooks)
10
8
  [![npm downloads](https://img.shields.io/npm/dm/@page-speed/img?style=flat-square)](https://www.npmjs.com/package/@page-speed/hooks)
11
9
  [![License](https://img.shields.io/npm/l/@page-speed/img?style=flat-square)](./LICENSE)
12
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square)](./tsconfig.json)
13
- [![Tree-Shakeable](https://img.shields.io/badge/Tree%20Shakeable-Yes-brightgreen?style=flat-square)](#tree-shaking)
14
11
 
15
- [Documentation](#documentation) · [Quick Start](#quick-start) · [Hooks](#hooks) · [Examples](#examples) · [Contributing](./CONTRIBUTING.md)
12
+ [Documentation](#documentation) · [Quick Start](#quick-start) · [Global Defaults](#setting-global-defaults) · [Examples](#examples) · [Contributing](./CONTRIBUTING.md)
16
13
 
17
14
  ---
18
15
 
19
16
  ## Documentation
20
17
 
21
- `@page-speed/img` is a React-first, OptixFlow-enabled image component that ships Lighthouse-friendly markup by default. It uses `useOptimizedImage` from `@page-speed/hooks` to compute pixel-perfect `src`, DPR-aware `srcset`, and `sizes` on the client. Tree shaking keeps the bundle lean—only the media hook is pulled in.
18
+ `@page-speed/img` is a React-first, OptixFlow-enabled image component that ships Lighthouse-friendly markup by default. It uses `useOptimizedImage` from `@page-speed/hooks` to compute pixel-perfect `src`, DPR-aware `srcset`, and `sizes` on the client. Tree shaking keeps the bundle lean only the media hook is pulled in.
22
19
 
23
20
  ### Installation
24
21
 
@@ -28,13 +25,114 @@ pnpm add @page-speed/img
28
25
 
29
26
  Peer deps: `react` and `react-dom` (17+). For OptixFlow optimization, supply an API key.
30
27
 
28
+ ---
29
+
31
30
  ### Usage (React / Next.js)
32
31
 
32
+ ```tsx
33
+ import { Img } from "@page-speed/img";
34
+
35
+ export function HeroImage() {
36
+ return (
37
+ <Img
38
+ src="https://images.example.com/hero.jpg"
39
+ alt="Hero"
40
+ width={1280}
41
+ height={720}
42
+ eager
43
+ />
44
+ );
45
+ }
46
+ ```
47
+
48
+ What you get automatically:
49
+
50
+ - Pixel-perfect primary `src` sized to the rendered element (Lighthouse "Properly size images" pass).
51
+ - DPR-aware `srcset` (1x/2x) for AVIF, WebP, and JPEG.
52
+ - Lazy loading via IntersectionObserver; set `eager` for above-the-fold images.
53
+ - Optional OptixFlow compression and format selection with a single prop.
54
+
55
+ ---
56
+
57
+ ### Setting Global Defaults
58
+
59
+ When all or most images in your app share the same OptixFlow configuration, you can set it once globally instead of repeating `optixFlowConfig` on every `<Img />`.
60
+
61
+ #### ✅ Recommended: `<ImgDefaults />` component (SSR-safe)
62
+
63
+ The `ImgDefaults` component is the preferred approach for React and Next.js apps. It applies the default config inside a `useEffect`, which means it **never runs during server-side rendering** — making it safe to place in any server component tree without wrapping in a `"use client"` boundary at the call site.
64
+
65
+ **As a wrapper around your app:**
66
+
67
+ ```tsx
68
+ // app/layout.tsx (Next.js App Router) or src/App.tsx
69
+ import { ImgDefaults } from "@page-speed/img";
70
+
71
+ export default function RootLayout({
72
+ children,
73
+ }: {
74
+ children: React.ReactNode;
75
+ }) {
76
+ return (
77
+ <html lang="en">
78
+ <body>
79
+ <ImgDefaults
80
+ config={{
81
+ apiKey: process.env.NEXT_PUBLIC_OPTIX_API_KEY!,
82
+ compressionLevel: 80,
83
+ }}
84
+ >
85
+ {children}
86
+ </ImgDefaults>
87
+ </body>
88
+ </html>
89
+ );
90
+ }
91
+ ```
92
+
93
+ **Standalone (no children required):**
94
+
95
+ ```tsx
96
+ // Place anywhere in the tree; renders null, no visual output
97
+ import { ImgDefaults } from "@page-speed/img";
98
+
99
+ export function Providers({ children }: { children: React.ReactNode }) {
100
+ return (
101
+ <>
102
+ <ImgDefaults
103
+ config={{ apiKey: process.env.NEXT_PUBLIC_OPTIX_API_KEY! }}
104
+ />
105
+ {children}
106
+ </>
107
+ );
108
+ }
109
+ ```
110
+
111
+ Individual `<Img />` components can always override the default with their own `optixFlowConfig` prop:
112
+
113
+ ```tsx
114
+ <Img
115
+ src="https://images.example.com/hero.jpg"
116
+ alt="Hero"
117
+ width={1280}
118
+ height={720}
119
+ // Overrides the global default for this image only
120
+ optixFlowConfig={{ renderedFileType: "jpeg", objectFit: "cover" }}
121
+ />
122
+ ```
123
+
124
+ #### Alternative: `setDefaultOptixFlowConfig()` function
125
+
126
+ For purely client-side apps, scripts, or imperative initialization (e.g. outside of React's render tree), the `setDefaultOptixFlowConfig` function is available. Call it once at app startup before any `<Img />` components mount.
127
+
33
128
  ```tsx
34
129
  import { Img, setDefaultOptixFlowConfig } from "@page-speed/img";
35
130
 
36
- // Optional: set once at app start
37
- setDefaultOptixFlowConfig({ apiKey: process.env.NEXT_PUBLIC_OPTIX_API_KEY!, compressionLevel: 80 });
131
+ // Call once, e.g. in main.tsx / index.tsx before rendering
132
+ setDefaultOptixFlowConfig({
133
+ apiKey: process.env.VITE_OPTIX_API_KEY!,
134
+ compressionLevel: 80,
135
+ });
38
136
 
39
137
  export function HeroImage() {
40
138
  return (
@@ -43,33 +141,22 @@ export function HeroImage() {
43
141
  alt="Hero"
44
142
  width={1280}
45
143
  height={720}
46
- // Per-image override (optional)
47
- optixFlowConfig={{ renderedFileType: "jpeg", objectFit: "cover" }}
48
144
  />
49
145
  );
50
146
  }
51
147
  ```
52
148
 
53
- What you get:
54
- - Pixel-perfect primary `src` sized to the rendered element (Lighthouse “Properly size images” pass).
55
- - DPR-aware `srcset` (1x/2x) for AVIF/WebP/JPEG.
56
- - Lazy loading with IntersectionObserver; set `eager` for above-the-fold.
57
- - Optional OptixFlow compression/format selection with a single prop.
149
+ > **Note:** `setDefaultOptixFlowConfig` is not SSR-safe on its own. In server-rendered environments (Next.js, Remix, etc.) prefer the `<ImgDefaults />` component, which handles the client/server boundary automatically.
58
150
 
59
- ### Props
151
+ To clear or reset the global default at any point:
60
152
 
61
- - `src` (string, required): Image URL.
62
- - `alt`, `title`, standard `<img>` attributes.
63
- - `width`, `height`: Set for CLS prevention; used as hints for sizing. Actual rendered size is measured to generate the pixel-perfect URL.
64
- - `loading`, `decoding`: Defaults `lazy` / `async`. Set `eager` to force above-the-fold fetch.
65
- - `sizes`: Override the auto-generated `sizes` from `useOptimizedImage`.
66
- - `intersectionMargin`, `intersectionThreshold`: Tweak lazy-load observer.
67
- - `optixFlowConfig`: `{ apiKey: string; compressionLevel?: number; renderedFileType?: 'avif' | 'webp' | 'jpeg' | 'png'; objectFit?: 'cover' | 'contain' | 'fill'; }`.
153
+ ```ts
154
+ setDefaultOptixFlowConfig(null);
155
+ ```
68
156
 
69
- ### Global defaults (React & UMD)
157
+ #### Browser global (UMD / inline script)
70
158
 
71
- - Programmatic: `setDefaultOptixFlowConfig({ apiKey: '...' })` once during app init.
72
- - Browser global (UMD/inline):
159
+ For vanilla HTML pages or CMS integrations using the UMD build, set the global before the component renders:
73
160
 
74
161
  ```html
75
162
  <script>
@@ -77,44 +164,100 @@ What you get:
77
164
  optixFlowConfig: {
78
165
  apiKey: "YOUR_OPTIX_KEY",
79
166
  compressionLevel: 80,
80
- objectFit: "cover"
81
- }
167
+ objectFit: "cover",
168
+ },
82
169
  };
83
170
  </script>
84
171
  ```
85
172
 
86
173
  `window.OpensiteImgDefaults` and `window.PAGE_SPEED_IMG_DEFAULTS` are also honored for backward compatibility.
87
174
 
175
+ ---
176
+
177
+ ### `<Img />` Props
178
+
179
+ | Prop | Type | Default | Description |
180
+ | ----------------------- | ----------------------------- | ------------------- | ---------------------------------------------------------------------- |
181
+ | `src` | `string` | — | **(Required)** Image URL. |
182
+ | `alt` | `string` | — | Alt text (passed through to `<img>`). |
183
+ | `width` | `number \| string` | — | Hint for sizing and CLS prevention. |
184
+ | `height` | `number \| string` | — | Hint for sizing and CLS prevention. |
185
+ | `eager` | `boolean` | `false` | Force eager loading (above-the-fold). Equivalent to `loading="eager"`. |
186
+ | `loading` | `"lazy" \| "eager"` | `"lazy"` | Native loading attribute. `eager` prop takes precedence. |
187
+ | `decoding` | `"async" \| "sync" \| "auto"` | `"async"` | Native decoding attribute. |
188
+ | `fetchPriority` | `"high" \| "low" \| "auto"` | `"high"` when eager | Native fetch priority. |
189
+ | `sizes` | `string` | auto-computed | Override the `sizes` attribute generated by `useOptimizedImage`. |
190
+ | `intersectionMargin` | `string` | `"200px"` | Root margin for the lazy-load IntersectionObserver. |
191
+ | `intersectionThreshold` | `number` | `0.1` | Threshold for the lazy-load IntersectionObserver. |
192
+ | `optixFlowConfig` | `OptixFlowConfig` | global default | Per-image OptixFlow config. Overrides any global default. |
193
+ | `useDebugMode` | `boolean` | `false` | Log image request details to the console. |
194
+
195
+ **`optixFlowConfig` shape:**
196
+
197
+ ```ts
198
+ {
199
+ apiKey: string;
200
+ compressionLevel?: number; // 1–100
201
+ renderedFileType?: "avif" | "webp" | "jpeg" | "png";
202
+ objectFit?: "cover" | "contain" | "fill";
203
+ }
204
+ ```
205
+
206
+ ### `<ImgDefaults />` Props
207
+
208
+ | Prop | Type | Description |
209
+ | ---------- | ----------------- | --------------------------------------------------------------------------------------- |
210
+ | `config` | `OptixFlowConfig` | The OptixFlow configuration to apply as the global default. |
211
+ | `children` | `React.ReactNode` | Optional. When provided, children are rendered; otherwise the component renders `null`. |
212
+
213
+ ---
214
+
88
215
  ### UMD usage
89
216
 
90
217
  ```html
91
- <script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
92
- <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
93
- <script src="https://cdn.jsdelivr.net/npm/@page-speed/img@0.0.2/dist/browser/page-speed-img.umd.js" crossorigin></script>
218
+ <script
219
+ src="https://unpkg.com/react@18/umd/react.production.min.js"
220
+ crossorigin
221
+ ></script>
222
+ <script
223
+ src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"
224
+ crossorigin
225
+ ></script>
226
+ <script
227
+ src="https://cdn.jsdelivr.net/npm/@page-speed/img@0.4.7/dist/browser/page-speed-img.umd.js"
228
+ crossorigin
229
+ ></script>
94
230
 
95
231
  <div id="app"></div>
96
232
  <script>
97
- const root = ReactDOM.createRoot(document.getElementById('app'));
233
+ const root = ReactDOM.createRoot(document.getElementById("app"));
98
234
  root.render(
99
235
  React.createElement(PageSpeedImg.Img, {
100
- src: 'https://images.example.com/card.jpg',
101
- alt: 'Card',
236
+ src: "https://images.example.com/card.jpg",
237
+ alt: "Card",
102
238
  width: 800,
103
239
  height: 600,
104
- optixFlowConfig: { apiKey: 'YOUR_OPTIX_KEY', compressionLevel: 70 }
105
- })
240
+ optixFlowConfig: { apiKey: "YOUR_OPTIX_KEY", compressionLevel: 70 },
241
+ }),
106
242
  );
107
243
  </script>
108
244
  ```
109
245
 
246
+ ---
247
+
110
248
  ### SSR considerations
111
249
 
112
- - The component is client-only (`"use client"`). For SSR apps, render it in client components/entry points.
113
- - Safe in non-browser contexts: guards exist for `window`/`IntersectionObserver`.
250
+ - `<Img />` is marked `"use client"`. In Next.js App Router, place it inside a Client Component or a shared layout where the `"use client"` boundary is already established.
251
+ - `<ImgDefaults />` is also marked `"use client"` and applies its config exclusively via `useEffect`, so it is safe to import and render from a Server Component tree — the config call never executes on the server.
252
+ - Guards exist for `window` and `IntersectionObserver` throughout the library, so modules can be imported safely in SSR environments without crashing.
253
+
254
+ ---
114
255
 
115
256
  ### Tree shaking
116
257
 
117
- `@page-speed/img` only imports `useOptimizedImage` from `@page-speed/hooks`, keeping bundles small. Both ESM and CJS builds are emitted; UMD is externalized to React/ReactDOM.
258
+ `@page-speed/img` only imports `useOptimizedImage` from `@page-speed/hooks`, keeping bundles small. Both ESM and CJS builds are emitted; the UMD build externalizes React and ReactDOM.
259
+
260
+ ---
118
261
 
119
262
  ### Testing
120
263
 
@@ -122,12 +265,18 @@ What you get:
122
265
  pnpm test
123
266
  ```
124
267
 
268
+ ---
269
+
125
270
  ### Roadmap
126
271
 
127
- - Add storybook examples for common layouts (hero, gallery, card).
272
+ - Add Storybook examples for common layouts (hero, gallery, card).
128
273
 
129
274
  ---
130
275
 
131
276
  ## Contributing
132
277
 
133
- PRs welcome. Please run `pnpm test` before submitting.
278
+ PRs welcome. Please run `pnpm test` before submitting. See [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
279
+
280
+ ## License
281
+
282
+ BSD 3-Clause
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).PageSpeedImg={},e.React)}(this,function(e,t){"use strict";var i=[1,2],n="undefined"!=typeof window?t.useLayoutEffect:t.useEffect;const r="dt:media-selected",o=()=>{};let s=0,u=!1;function d(e){e&&e.querySelectorAll("source").forEach(e=>{const t=e.getAttribute("srcset");t&&(e.setAttribute("data-srcset",t),e.removeAttribute("srcset"),requestAnimationFrame(()=>{e.setAttribute("srcset",t)}))})}const l="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";let c;const a=e=>e??c??(()=>{var e,t,i;if("undefined"==typeof globalThis)return;const n=globalThis;return(null==(e=n.PageSpeedImgDefaults)?void 0:e.optixFlowConfig)||(null==(t=n.OpensiteImgDefaults)?void 0:t.optixFlowConfig)||(null==(i=n.PAGE_SPEED_IMG_DEFAULTS)?void 0:i.optixFlowConfig)})(),f=e=>{if(""!==e&&null!=e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e){const t=Number(e);if(Number.isFinite(t))return t}}},g=({sizes:e,loading:c,decoding:g,alt:h,title:p,src:w,eager:m,width:v,height:b,fetchPriority:y,intersectionMargin:E,intersectionThreshold:M,optixFlowConfig:A,useDebugMode:I,forwardedRef:S,...L})=>{const j=t.useRef(null),x=t.useRef(null);var F;F=x,t.useEffect(()=>{const e=F.current;e&&(e instanceof HTMLPictureElement?d(e):e.parentElement instanceof HTMLPictureElement&&d(e.parentElement))},[F]),t.useEffect(()=>{if("undefined"!=typeof window)return u||(window.addEventListener(r,o),u=!0),s+=1,()=>{s-=1,s<=0&&u&&(window.removeEventListener(r,o),u=!1)}},[]);const P=t.useMemo(()=>"string"==typeof w?w.trim():"",[w]),z=t.useMemo(()=>f(v),[v]),V=t.useMemo(()=>f(b),[b]),R=t.useMemo(()=>a(A),[A]),T=t.useMemo(()=>m??"eager"===c,[m,c]),C=t.useMemo(()=>({src:P,eager:T,width:z,height:V,rootMargin:E??"200px",threshold:M??.1,optixFlowConfig:R}),[P,T,z,V,E,M,R]),{ref:N,src:O,srcset:D,sizes:q,loading:_,isInView:k,size:H}=function(e){const{src:r,eager:o=!1,threshold:s=.1,rootMargin:u="50px",width:d,height:l,optixFlowConfig:c}=e,a=t.useMemo(()=>null==c?void 0:c.apiKey,[null==c?void 0:c.apiKey]),f=t.useMemo(()=>!!a,[a]),[g,h]=t.useState({isLoaded:!1,isInView:!1}),[p,w]=t.useState({width:0,height:0}),m=t.useRef(null),v=t.useRef(null),b=t.useMemo(()=>({width:d??p.width,height:l??p.height}),[d,l,p.width,p.height]);n(()=>{if(!m.current)return;if(void 0!==d&&void 0!==l)return;const e=()=>{const e=m.current;if(!e)return;const t=d??(Math.round(e.clientWidth)||e.naturalWidth||0),i=l??(Math.round(e.clientHeight)||e.naturalHeight||0);(t>0||i>0)&&w(e=>e.width!==t||e.height!==i?{width:t,height:i}:e)};m.current.clientWidth>0&&e();const t=m.current;t.addEventListener("load",e);let i=null;return"undefined"!=typeof ResizeObserver&&(i=new ResizeObserver(()=>{e()}),i.observe(t)),()=>{t.removeEventListener("load",e),null==i||i.disconnect()}},[d,l]);const y=t.useCallback((e,t,i)=>{if(!f)return r;if(!e||!t)return r;const n=new URLSearchParams;return n.set("url",r),n.set("fit",String((null==c?void 0:c.objectFit)??"cover")),n.set("w",String(e)),n.set("h",String(t)),n.set("q",String((null==c?void 0:c.compressionLevel)??75)),n.set("f",i),n.set("apiKey",a),`https://octane.cdn.ing/api/v1/images/transform?${n.toString()}`},[f,r,null==c?void 0:c.compressionLevel,a,null==c?void 0:c.objectFit]),E=t.useCallback((e,t,n)=>f&&0!==e&&0!==t?i.map(i=>{const r=Math.round(e*i),o=Math.round(t*i);return`${y(r,o,n)} ${i}x`}).join(", "):"",[f,y]),M=t.useMemo(()=>{const e=b.width>0&&b.height>0;if(!f||!e)return r;const t=(null==c?void 0:c.renderedFileType)??"jpeg";return y(b.width,b.height,t)},[f,r,b.width,b.height,null==c?void 0:c.renderedFileType,null==c?void 0:c.compressionLevel,null==c?void 0:c.objectFit,y]),A=t.useMemo(()=>({avif:E(b.width,b.height,"avif"),webp:E(b.width,b.height,"webp"),jpeg:E(b.width,b.height,"jpeg")}),[b.width,b.height,E]),I=t.useMemo(()=>0===b.width?"":`${b.width}px`,[b.width]);return t.useEffect(()=>{if("undefined"!=typeof window&&m.current){if(!o)return v.current=new IntersectionObserver(([e])=>{var t;e.isIntersecting&&(h(e=>({...e,isInView:!0})),null==(t=v.current)||t.disconnect())},{threshold:s,rootMargin:u}),v.current.observe(m.current),()=>{var e;null==(e=v.current)||e.disconnect()};h({isLoaded:!1,isInView:!0})}},[o,s,u]),t.useEffect(()=>{if(!m.current)return;const e=()=>{h(e=>({...e,isLoaded:!0}))},t=m.current;if(!t.complete)return t.addEventListener("load",e),()=>t.removeEventListener("load",e);e()},[g.isInView]),{ref:t.useCallback(e=>{m.current=e},[]),src:g.isInView||o?M:r,srcset:g.isInView||o?A:{avif:"",webp:"",jpeg:""},sizes:g.isInView||o?I:"",isLoaded:g.isLoaded,isInView:g.isInView,loading:o?"eager":"lazy",size:b}}(C),$=((e,i,n)=>t.useCallback(t=>{e(t),n.current=t,"function"==typeof i?i(t):i&&"object"==typeof i&&(i.current=t)},[e,i,n]))(N,S,j),B=t.useMemo(()=>e??(q||void 0),[e,q]),G=t.useMemo(()=>c??_??"lazy",[c,_]),K=t.useMemo(()=>g??"async",[g]),U=t.useMemo(()=>y??(T?"high":void 0),[y,T]),W=t.useMemo(()=>Boolean(D.avif||D.webp||D.jpeg),[D.avif,D.webp,D.jpeg]),Q=t.useMemo(()=>O||P||l,[O,P]),J=t.useMemo(()=>!W||D.avif||D.webp?"":D.jpeg,[W,D.avif,D.webp,D.jpeg]),X=t.useMemo(()=>z??(H.width||void 0),[z,null==H?void 0:H.width]),Y=t.useMemo(()=>V??(H.height||void 0),[V,null==H?void 0:H.height]);return function({enabled:e,eagerLoad:i,isInView:n,imgSrc:r,transparentPixel:o,srcset:s,sizesAttr:u}){const d=t.useRef(null);t.useEffect(()=>{if(!e)return;if("undefined"==typeof window)return;if(!i&&!n)return;if(!r||r===o)return;const t=[r,s.avif,s.webp,s.jpeg,u??""].join("|");d.current!==t&&(d.current=t,"undefined"!=typeof console&&console.info&&console.info("[PageSpeedImg] image request",{src:r,srcset:s,sizes:u}))},[e,i,r,n,u,s.avif,s.webp,s.jpeg,o])}({enabled:I??!1,eagerLoad:T,isInView:k,imgSrc:Q,transparentPixel:l,srcset:D,sizesAttr:B}),W?t.createElement("picture",{ref:x},D.avif?t.createElement("source",{type:"image/avif",srcSet:D.avif,sizes:B}):null,D.webp?t.createElement("source",{type:"image/webp",srcSet:D.webp,sizes:B}):null,t.createElement("img",{ref:$,src:Q,srcSet:J||void 0,sizes:J?B:void 0,loading:G,decoding:K,fetchPriority:U,alt:h,title:p,width:X,height:Y,...L})):t.createElement("img",{ref:$,src:Q,loading:G,decoding:K,fetchPriority:U,alt:h,title:p,width:X,height:Y,...L})},h=t.forwardRef(function(e,i){return"string"==typeof e.src&&e.src.trim().length>0?t.createElement(g,{...e,forwardedRef:i}):("undefined"!=typeof console&&console.warn&&console.warn("<Img /> requires src. No src provided, rendering null."),null)}),p=t.memo(h);p.displayName="PageSpeedImg";const w="undefined"!=typeof globalThis?globalThis:void 0;if(w)if(w.process){const e=w.process.env??(w.process.env={});void 0===e.NODE_ENV&&(e.NODE_ENV="production")}else w.process={env:{NODE_ENV:"production"}};e.Img=p,e.setDefaultOptixFlowConfig=e=>{c=e??void 0},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).PageSpeedImg={},e.React)}(this,function(e,t){"use strict";function i(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const i in e)if("default"!==i){const n=Object.getOwnPropertyDescriptor(e,i);Object.defineProperty(t,i,n.get?n:{enumerable:!0,get:()=>e[i]})}return t.default=e,Object.freeze(t)}const n=i(t);var r=[1,2],o="undefined"!=typeof window?t.useLayoutEffect:t.useEffect;const s="dt:media-selected",u=()=>{};let l=0,c=!1;function d(e){e&&e.querySelectorAll("source").forEach(e=>{const t=e.getAttribute("srcset");t&&(e.setAttribute("data-srcset",t),e.removeAttribute("srcset"),requestAnimationFrame(()=>{e.setAttribute("srcset",t)}))})}const a="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";let f;const g=e=>e??f??(()=>{var e,t,i;if("undefined"==typeof globalThis)return;const n=globalThis;return(null==(e=n.PageSpeedImgDefaults)?void 0:e.optixFlowConfig)||(null==(t=n.OpensiteImgDefaults)?void 0:t.optixFlowConfig)||(null==(i=n.PAGE_SPEED_IMG_DEFAULTS)?void 0:i.optixFlowConfig)})(),h=e=>{f=e??void 0},p=e=>{if(""!==e&&null!=e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e){const t=Number(e);if(Number.isFinite(t))return t}}},w=({sizes:e,loading:i,decoding:n,alt:f,title:h,src:w,eager:m,width:v,height:b,fetchPriority:y,intersectionMargin:E,intersectionThreshold:M,optixFlowConfig:A,useDebugMode:I,forwardedRef:S,...j})=>{const L=t.useRef(null),P=t.useRef(null);var F;F=P,t.useEffect(()=>{const e=F.current;e&&(e instanceof HTMLPictureElement?d(e):e.parentElement instanceof HTMLPictureElement&&d(e.parentElement))},[F]),t.useEffect(()=>{if("undefined"!=typeof window)return c||(window.addEventListener(s,u),c=!0),l+=1,()=>{l-=1,l<=0&&c&&(window.removeEventListener(s,u),c=!1)}},[]);const x=t.useMemo(()=>"string"==typeof w?w.trim():"",[w]),z=t.useMemo(()=>p(v),[v]),O=t.useMemo(()=>p(b),[b]),V=t.useMemo(()=>g(A),[A]),T=t.useMemo(()=>m??"eager"===i,[m,i]),R=t.useMemo(()=>({src:x,eager:T,width:z,height:O,rootMargin:E??"200px",threshold:M??.1,optixFlowConfig:V}),[x,T,z,O,E,M,V]),{ref:C,src:D,srcset:N,sizes:q,loading:_,isInView:k,size:H}=function(e){const{src:i,eager:n=!1,threshold:s=.1,rootMargin:u="50px",width:l,height:c,optixFlowConfig:d}=e,a=t.useMemo(()=>null==d?void 0:d.apiKey,[null==d?void 0:d.apiKey]),f=t.useMemo(()=>!!a,[a]),[g,h]=t.useState({isLoaded:!1,isInView:!1}),[p,w]=t.useState({width:0,height:0}),m=t.useRef(null),v=t.useRef(null),b=t.useMemo(()=>({width:l??p.width,height:c??p.height}),[l,c,p.width,p.height]);o(()=>{if(!m.current)return;if(void 0!==l&&void 0!==c)return;const e=()=>{const e=m.current;if(!e)return;const t=l??(Math.round(e.clientWidth)||e.naturalWidth||0),i=c??(Math.round(e.clientHeight)||e.naturalHeight||0);(t>0||i>0)&&w(e=>e.width!==t||e.height!==i?{width:t,height:i}:e)};m.current.clientWidth>0&&e();const t=m.current;t.addEventListener("load",e);let i=null;return"undefined"!=typeof ResizeObserver&&(i=new ResizeObserver(()=>{e()}),i.observe(t)),()=>{t.removeEventListener("load",e),null==i||i.disconnect()}},[l,c]);const y=t.useCallback((e,t,n)=>{if(!f)return i;if(!e||!t)return i;const r=new URLSearchParams;return r.set("url",i),r.set("fit",String((null==d?void 0:d.objectFit)??"cover")),r.set("w",String(e)),r.set("h",String(t)),r.set("q",String((null==d?void 0:d.compressionLevel)??75)),r.set("f",n),r.set("apiKey",a),`https://octane.cdn.ing/api/v1/images/transform?${r.toString()}`},[f,i,null==d?void 0:d.compressionLevel,a,null==d?void 0:d.objectFit]),E=t.useCallback((e,t,i)=>f&&0!==e&&0!==t?r.map(n=>{const r=Math.round(e*n),o=Math.round(t*n);return`${y(r,o,i)} ${n}x`}).join(", "):"",[f,y]),M=t.useMemo(()=>{const e=b.width>0&&b.height>0;if(!f||!e)return i;const t=(null==d?void 0:d.renderedFileType)??"jpeg";return y(b.width,b.height,t)},[f,i,b.width,b.height,null==d?void 0:d.renderedFileType,null==d?void 0:d.compressionLevel,null==d?void 0:d.objectFit,y]),A=t.useMemo(()=>({avif:E(b.width,b.height,"avif"),webp:E(b.width,b.height,"webp"),jpeg:E(b.width,b.height,"jpeg")}),[b.width,b.height,E]),I=t.useMemo(()=>0===b.width?"":`${b.width}px`,[b.width]);return t.useEffect(()=>{if("undefined"!=typeof window&&m.current){if(!n)return v.current=new IntersectionObserver(([e])=>{var t;e.isIntersecting&&(h(e=>({...e,isInView:!0})),null==(t=v.current)||t.disconnect())},{threshold:s,rootMargin:u}),v.current.observe(m.current),()=>{var e;null==(e=v.current)||e.disconnect()};h({isLoaded:!1,isInView:!0})}},[n,s,u]),t.useEffect(()=>{if(!m.current)return;const e=()=>{h(e=>({...e,isLoaded:!0}))},t=m.current;if(!t.complete)return t.addEventListener("load",e),()=>t.removeEventListener("load",e);e()},[g.isInView]),{ref:t.useCallback(e=>{m.current=e},[]),src:g.isInView||n?M:i,srcset:g.isInView||n?A:{avif:"",webp:"",jpeg:""},sizes:g.isInView||n?I:"",isLoaded:g.isLoaded,isInView:g.isInView,loading:n?"eager":"lazy",size:b}}(R),$=((e,i,n)=>t.useCallback(t=>{e(t),n.current=t,"function"==typeof i?i(t):i&&"object"==typeof i&&(i.current=t)},[e,i,n]))(C,S,L),B=t.useMemo(()=>e??(q||void 0),[e,q]),G=t.useMemo(()=>i??_??"lazy",[i,_]),K=t.useMemo(()=>n??"async",[n]),U=t.useMemo(()=>y??(T?"high":void 0),[y,T]),W=t.useMemo(()=>Boolean(N.avif||N.webp||N.jpeg),[N.avif,N.webp,N.jpeg]),Q=t.useMemo(()=>D||x||a,[D,x]),J=t.useMemo(()=>!W||N.avif||N.webp?"":N.jpeg,[W,N.avif,N.webp,N.jpeg]),X=t.useMemo(()=>z??(H.width||void 0),[z,null==H?void 0:H.width]),Y=t.useMemo(()=>O??(H.height||void 0),[O,null==H?void 0:H.height]);return function({enabled:e,eagerLoad:i,isInView:n,imgSrc:r,transparentPixel:o,srcset:s,sizesAttr:u}){const l=t.useRef(null);t.useEffect(()=>{if(!e)return;if("undefined"==typeof window)return;if(!i&&!n)return;if(!r||r===o)return;const t=[r,s.avif,s.webp,s.jpeg,u??""].join("|");l.current!==t&&(l.current=t,"undefined"!=typeof console&&console.info&&console.info("[PageSpeedImg] image request",{src:r,srcset:s,sizes:u}))},[e,i,r,n,u,s.avif,s.webp,s.jpeg,o])}({enabled:I??!1,eagerLoad:T,isInView:k,imgSrc:Q,transparentPixel:a,srcset:N,sizesAttr:B}),W?t.createElement("picture",{ref:P},N.avif?t.createElement("source",{type:"image/avif",srcSet:N.avif,sizes:B}):null,N.webp?t.createElement("source",{type:"image/webp",srcSet:N.webp,sizes:B}):null,t.createElement("img",{ref:$,src:Q,srcSet:J||void 0,sizes:J?B:void 0,loading:G,decoding:K,fetchPriority:U,alt:f,title:h,width:X,height:Y,...j})):t.createElement("img",{ref:$,src:Q,loading:G,decoding:K,fetchPriority:U,alt:f,title:h,width:X,height:Y,...j})},m=t.forwardRef(function(e,i){return"string"==typeof e.src&&e.src.trim().length>0?t.createElement(w,{...e,forwardedRef:i}):("undefined"!=typeof console&&console.warn&&console.warn("<Img /> requires src. No src provided, rendering null."),null)}),v=t.memo(m);v.displayName="PageSpeedImg";const b="undefined"!=typeof globalThis?globalThis:void 0;if(b)if(b.process){const e=b.process.env??(b.process.env={});void 0===e.NODE_ENV&&(e.NODE_ENV="production")}else b.process={env:{NODE_ENV:"production"}};e.Img=v,e.ImgDefaults=function({config:e,children:t}){return n.useEffect(()=>{h(e??null)},[e]),t?n.createElement(n.Fragment,null,t):null},e.setDefaultOptixFlowConfig=h,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
2
2
  //# sourceMappingURL=page-speed-img.umd.js.map
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).PageSpeedImg={},e.React)}(this,function(e,t){"use strict";var i=[1,2],n="undefined"!=typeof window?t.useLayoutEffect:t.useEffect;const r="dt:media-selected",o=()=>{};let s=0,u=!1;function d(e){e&&e.querySelectorAll("source").forEach(e=>{const t=e.getAttribute("srcset");t&&(e.setAttribute("data-srcset",t),e.removeAttribute("srcset"),requestAnimationFrame(()=>{e.setAttribute("srcset",t)}))})}const l="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";let c;const a=e=>e??c??(()=>{var e,t,i;if("undefined"==typeof globalThis)return;const n=globalThis;return(null==(e=n.PageSpeedImgDefaults)?void 0:e.optixFlowConfig)||(null==(t=n.OpensiteImgDefaults)?void 0:t.optixFlowConfig)||(null==(i=n.PAGE_SPEED_IMG_DEFAULTS)?void 0:i.optixFlowConfig)})(),f=e=>{if(""!==e&&null!=e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e){const t=Number(e);if(Number.isFinite(t))return t}}},g=({sizes:e,loading:c,decoding:g,alt:h,title:p,src:w,eager:m,width:v,height:b,fetchPriority:y,intersectionMargin:E,intersectionThreshold:M,optixFlowConfig:A,useDebugMode:I,forwardedRef:S,...L})=>{const j=t.useRef(null),x=t.useRef(null);var F;F=x,t.useEffect(()=>{const e=F.current;e&&(e instanceof HTMLPictureElement?d(e):e.parentElement instanceof HTMLPictureElement&&d(e.parentElement))},[F]),t.useEffect(()=>{if("undefined"!=typeof window)return u||(window.addEventListener(r,o),u=!0),s+=1,()=>{s-=1,s<=0&&u&&(window.removeEventListener(r,o),u=!1)}},[]);const P=t.useMemo(()=>"string"==typeof w?w.trim():"",[w]),z=t.useMemo(()=>f(v),[v]),V=t.useMemo(()=>f(b),[b]),R=t.useMemo(()=>a(A),[A]),T=t.useMemo(()=>m??"eager"===c,[m,c]),C=t.useMemo(()=>({src:P,eager:T,width:z,height:V,rootMargin:E??"200px",threshold:M??.1,optixFlowConfig:R}),[P,T,z,V,E,M,R]),{ref:N,src:O,srcset:D,sizes:q,loading:_,isInView:k,size:H}=function(e){const{src:r,eager:o=!1,threshold:s=.1,rootMargin:u="50px",width:d,height:l,optixFlowConfig:c}=e,a=t.useMemo(()=>null==c?void 0:c.apiKey,[null==c?void 0:c.apiKey]),f=t.useMemo(()=>!!a,[a]),[g,h]=t.useState({isLoaded:!1,isInView:!1}),[p,w]=t.useState({width:0,height:0}),m=t.useRef(null),v=t.useRef(null),b=t.useMemo(()=>({width:d??p.width,height:l??p.height}),[d,l,p.width,p.height]);n(()=>{if(!m.current)return;if(void 0!==d&&void 0!==l)return;const e=()=>{const e=m.current;if(!e)return;const t=d??(Math.round(e.clientWidth)||e.naturalWidth||0),i=l??(Math.round(e.clientHeight)||e.naturalHeight||0);(t>0||i>0)&&w(e=>e.width!==t||e.height!==i?{width:t,height:i}:e)};m.current.clientWidth>0&&e();const t=m.current;t.addEventListener("load",e);let i=null;return"undefined"!=typeof ResizeObserver&&(i=new ResizeObserver(()=>{e()}),i.observe(t)),()=>{t.removeEventListener("load",e),null==i||i.disconnect()}},[d,l]);const y=t.useCallback((e,t,i)=>{if(!f)return r;if(!e||!t)return r;const n=new URLSearchParams;return n.set("url",r),n.set("fit",String((null==c?void 0:c.objectFit)??"cover")),n.set("w",String(e)),n.set("h",String(t)),n.set("q",String((null==c?void 0:c.compressionLevel)??75)),n.set("f",i),n.set("apiKey",a),`https://octane.cdn.ing/api/v1/images/transform?${n.toString()}`},[f,r,null==c?void 0:c.compressionLevel,a,null==c?void 0:c.objectFit]),E=t.useCallback((e,t,n)=>f&&0!==e&&0!==t?i.map(i=>{const r=Math.round(e*i),o=Math.round(t*i);return`${y(r,o,n)} ${i}x`}).join(", "):"",[f,y]),M=t.useMemo(()=>{const e=b.width>0&&b.height>0;if(!f||!e)return r;const t=(null==c?void 0:c.renderedFileType)??"jpeg";return y(b.width,b.height,t)},[f,r,b.width,b.height,null==c?void 0:c.renderedFileType,null==c?void 0:c.compressionLevel,null==c?void 0:c.objectFit,y]),A=t.useMemo(()=>({avif:E(b.width,b.height,"avif"),webp:E(b.width,b.height,"webp"),jpeg:E(b.width,b.height,"jpeg")}),[b.width,b.height,E]),I=t.useMemo(()=>0===b.width?"":`${b.width}px`,[b.width]);return t.useEffect(()=>{if("undefined"!=typeof window&&m.current){if(!o)return v.current=new IntersectionObserver(([e])=>{var t;e.isIntersecting&&(h(e=>({...e,isInView:!0})),null==(t=v.current)||t.disconnect())},{threshold:s,rootMargin:u}),v.current.observe(m.current),()=>{var e;null==(e=v.current)||e.disconnect()};h({isLoaded:!1,isInView:!0})}},[o,s,u]),t.useEffect(()=>{if(!m.current)return;const e=()=>{h(e=>({...e,isLoaded:!0}))},t=m.current;if(!t.complete)return t.addEventListener("load",e),()=>t.removeEventListener("load",e);e()},[g.isInView]),{ref:t.useCallback(e=>{m.current=e},[]),src:g.isInView||o?M:r,srcset:g.isInView||o?A:{avif:"",webp:"",jpeg:""},sizes:g.isInView||o?I:"",isLoaded:g.isLoaded,isInView:g.isInView,loading:o?"eager":"lazy",size:b}}(C),$=((e,i,n)=>t.useCallback(t=>{e(t),n.current=t,"function"==typeof i?i(t):i&&"object"==typeof i&&(i.current=t)},[e,i,n]))(N,S,j),B=t.useMemo(()=>e??(q||void 0),[e,q]),G=t.useMemo(()=>c??_??"lazy",[c,_]),K=t.useMemo(()=>g??"async",[g]),U=t.useMemo(()=>y??(T?"high":void 0),[y,T]),W=t.useMemo(()=>Boolean(D.avif||D.webp||D.jpeg),[D.avif,D.webp,D.jpeg]),Q=t.useMemo(()=>O||P||l,[O,P]),J=t.useMemo(()=>!W||D.avif||D.webp?"":D.jpeg,[W,D.avif,D.webp,D.jpeg]),X=t.useMemo(()=>z??(H.width||void 0),[z,null==H?void 0:H.width]),Y=t.useMemo(()=>V??(H.height||void 0),[V,null==H?void 0:H.height]);return function({enabled:e,eagerLoad:i,isInView:n,imgSrc:r,transparentPixel:o,srcset:s,sizesAttr:u}){const d=t.useRef(null);t.useEffect(()=>{if(!e)return;if("undefined"==typeof window)return;if(!i&&!n)return;if(!r||r===o)return;const t=[r,s.avif,s.webp,s.jpeg,u??""].join("|");d.current!==t&&(d.current=t,"undefined"!=typeof console&&console.info&&console.info("[PageSpeedImg] image request",{src:r,srcset:s,sizes:u}))},[e,i,r,n,u,s.avif,s.webp,s.jpeg,o])}({enabled:I??!1,eagerLoad:T,isInView:k,imgSrc:Q,transparentPixel:l,srcset:D,sizesAttr:B}),W?t.createElement("picture",{ref:x},D.avif?t.createElement("source",{type:"image/avif",srcSet:D.avif,sizes:B}):null,D.webp?t.createElement("source",{type:"image/webp",srcSet:D.webp,sizes:B}):null,t.createElement("img",{ref:$,src:Q,srcSet:J||void 0,sizes:J?B:void 0,loading:G,decoding:K,fetchPriority:U,alt:h,title:p,width:X,height:Y,...L})):t.createElement("img",{ref:$,src:Q,loading:G,decoding:K,fetchPriority:U,alt:h,title:p,width:X,height:Y,...L})},h=t.forwardRef(function(e,i){return"string"==typeof e.src&&e.src.trim().length>0?t.createElement(g,{...e,forwardedRef:i}):("undefined"!=typeof console&&console.warn&&console.warn("<Img /> requires src. No src provided, rendering null."),null)}),p=t.memo(h);p.displayName="PageSpeedImg";const w="undefined"!=typeof globalThis?globalThis:void 0;if(w)if(w.process){const e=w.process.env??(w.process.env={});void 0===e.NODE_ENV&&(e.NODE_ENV="production")}else w.process={env:{NODE_ENV:"production"}};e.Img=p,e.setDefaultOptixFlowConfig=e=>{c=e??void 0},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).PageSpeedImg={},e.React)}(this,function(e,t){"use strict";function i(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const i in e)if("default"!==i){const n=Object.getOwnPropertyDescriptor(e,i);Object.defineProperty(t,i,n.get?n:{enumerable:!0,get:()=>e[i]})}return t.default=e,Object.freeze(t)}const n=i(t);var r=[1,2],o="undefined"!=typeof window?t.useLayoutEffect:t.useEffect;const s="dt:media-selected",u=()=>{};let l=0,c=!1;function d(e){e&&e.querySelectorAll("source").forEach(e=>{const t=e.getAttribute("srcset");t&&(e.setAttribute("data-srcset",t),e.removeAttribute("srcset"),requestAnimationFrame(()=>{e.setAttribute("srcset",t)}))})}const a="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";let f;const g=e=>e??f??(()=>{var e,t,i;if("undefined"==typeof globalThis)return;const n=globalThis;return(null==(e=n.PageSpeedImgDefaults)?void 0:e.optixFlowConfig)||(null==(t=n.OpensiteImgDefaults)?void 0:t.optixFlowConfig)||(null==(i=n.PAGE_SPEED_IMG_DEFAULTS)?void 0:i.optixFlowConfig)})(),h=e=>{f=e??void 0},p=e=>{if(""!==e&&null!=e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e){const t=Number(e);if(Number.isFinite(t))return t}}},w=({sizes:e,loading:i,decoding:n,alt:f,title:h,src:w,eager:m,width:v,height:b,fetchPriority:y,intersectionMargin:E,intersectionThreshold:M,optixFlowConfig:A,useDebugMode:I,forwardedRef:S,...j})=>{const L=t.useRef(null),P=t.useRef(null);var F;F=P,t.useEffect(()=>{const e=F.current;e&&(e instanceof HTMLPictureElement?d(e):e.parentElement instanceof HTMLPictureElement&&d(e.parentElement))},[F]),t.useEffect(()=>{if("undefined"!=typeof window)return c||(window.addEventListener(s,u),c=!0),l+=1,()=>{l-=1,l<=0&&c&&(window.removeEventListener(s,u),c=!1)}},[]);const x=t.useMemo(()=>"string"==typeof w?w.trim():"",[w]),z=t.useMemo(()=>p(v),[v]),O=t.useMemo(()=>p(b),[b]),V=t.useMemo(()=>g(A),[A]),T=t.useMemo(()=>m??"eager"===i,[m,i]),R=t.useMemo(()=>({src:x,eager:T,width:z,height:O,rootMargin:E??"200px",threshold:M??.1,optixFlowConfig:V}),[x,T,z,O,E,M,V]),{ref:C,src:D,srcset:N,sizes:q,loading:_,isInView:k,size:H}=function(e){const{src:i,eager:n=!1,threshold:s=.1,rootMargin:u="50px",width:l,height:c,optixFlowConfig:d}=e,a=t.useMemo(()=>null==d?void 0:d.apiKey,[null==d?void 0:d.apiKey]),f=t.useMemo(()=>!!a,[a]),[g,h]=t.useState({isLoaded:!1,isInView:!1}),[p,w]=t.useState({width:0,height:0}),m=t.useRef(null),v=t.useRef(null),b=t.useMemo(()=>({width:l??p.width,height:c??p.height}),[l,c,p.width,p.height]);o(()=>{if(!m.current)return;if(void 0!==l&&void 0!==c)return;const e=()=>{const e=m.current;if(!e)return;const t=l??(Math.round(e.clientWidth)||e.naturalWidth||0),i=c??(Math.round(e.clientHeight)||e.naturalHeight||0);(t>0||i>0)&&w(e=>e.width!==t||e.height!==i?{width:t,height:i}:e)};m.current.clientWidth>0&&e();const t=m.current;t.addEventListener("load",e);let i=null;return"undefined"!=typeof ResizeObserver&&(i=new ResizeObserver(()=>{e()}),i.observe(t)),()=>{t.removeEventListener("load",e),null==i||i.disconnect()}},[l,c]);const y=t.useCallback((e,t,n)=>{if(!f)return i;if(!e||!t)return i;const r=new URLSearchParams;return r.set("url",i),r.set("fit",String((null==d?void 0:d.objectFit)??"cover")),r.set("w",String(e)),r.set("h",String(t)),r.set("q",String((null==d?void 0:d.compressionLevel)??75)),r.set("f",n),r.set("apiKey",a),`https://octane.cdn.ing/api/v1/images/transform?${r.toString()}`},[f,i,null==d?void 0:d.compressionLevel,a,null==d?void 0:d.objectFit]),E=t.useCallback((e,t,i)=>f&&0!==e&&0!==t?r.map(n=>{const r=Math.round(e*n),o=Math.round(t*n);return`${y(r,o,i)} ${n}x`}).join(", "):"",[f,y]),M=t.useMemo(()=>{const e=b.width>0&&b.height>0;if(!f||!e)return i;const t=(null==d?void 0:d.renderedFileType)??"jpeg";return y(b.width,b.height,t)},[f,i,b.width,b.height,null==d?void 0:d.renderedFileType,null==d?void 0:d.compressionLevel,null==d?void 0:d.objectFit,y]),A=t.useMemo(()=>({avif:E(b.width,b.height,"avif"),webp:E(b.width,b.height,"webp"),jpeg:E(b.width,b.height,"jpeg")}),[b.width,b.height,E]),I=t.useMemo(()=>0===b.width?"":`${b.width}px`,[b.width]);return t.useEffect(()=>{if("undefined"!=typeof window&&m.current){if(!n)return v.current=new IntersectionObserver(([e])=>{var t;e.isIntersecting&&(h(e=>({...e,isInView:!0})),null==(t=v.current)||t.disconnect())},{threshold:s,rootMargin:u}),v.current.observe(m.current),()=>{var e;null==(e=v.current)||e.disconnect()};h({isLoaded:!1,isInView:!0})}},[n,s,u]),t.useEffect(()=>{if(!m.current)return;const e=()=>{h(e=>({...e,isLoaded:!0}))},t=m.current;if(!t.complete)return t.addEventListener("load",e),()=>t.removeEventListener("load",e);e()},[g.isInView]),{ref:t.useCallback(e=>{m.current=e},[]),src:g.isInView||n?M:i,srcset:g.isInView||n?A:{avif:"",webp:"",jpeg:""},sizes:g.isInView||n?I:"",isLoaded:g.isLoaded,isInView:g.isInView,loading:n?"eager":"lazy",size:b}}(R),$=((e,i,n)=>t.useCallback(t=>{e(t),n.current=t,"function"==typeof i?i(t):i&&"object"==typeof i&&(i.current=t)},[e,i,n]))(C,S,L),B=t.useMemo(()=>e??(q||void 0),[e,q]),G=t.useMemo(()=>i??_??"lazy",[i,_]),K=t.useMemo(()=>n??"async",[n]),U=t.useMemo(()=>y??(T?"high":void 0),[y,T]),W=t.useMemo(()=>Boolean(N.avif||N.webp||N.jpeg),[N.avif,N.webp,N.jpeg]),Q=t.useMemo(()=>D||x||a,[D,x]),J=t.useMemo(()=>!W||N.avif||N.webp?"":N.jpeg,[W,N.avif,N.webp,N.jpeg]),X=t.useMemo(()=>z??(H.width||void 0),[z,null==H?void 0:H.width]),Y=t.useMemo(()=>O??(H.height||void 0),[O,null==H?void 0:H.height]);return function({enabled:e,eagerLoad:i,isInView:n,imgSrc:r,transparentPixel:o,srcset:s,sizesAttr:u}){const l=t.useRef(null);t.useEffect(()=>{if(!e)return;if("undefined"==typeof window)return;if(!i&&!n)return;if(!r||r===o)return;const t=[r,s.avif,s.webp,s.jpeg,u??""].join("|");l.current!==t&&(l.current=t,"undefined"!=typeof console&&console.info&&console.info("[PageSpeedImg] image request",{src:r,srcset:s,sizes:u}))},[e,i,r,n,u,s.avif,s.webp,s.jpeg,o])}({enabled:I??!1,eagerLoad:T,isInView:k,imgSrc:Q,transparentPixel:a,srcset:N,sizesAttr:B}),W?t.createElement("picture",{ref:P},N.avif?t.createElement("source",{type:"image/avif",srcSet:N.avif,sizes:B}):null,N.webp?t.createElement("source",{type:"image/webp",srcSet:N.webp,sizes:B}):null,t.createElement("img",{ref:$,src:Q,srcSet:J||void 0,sizes:J?B:void 0,loading:G,decoding:K,fetchPriority:U,alt:f,title:h,width:X,height:Y,...j})):t.createElement("img",{ref:$,src:Q,loading:G,decoding:K,fetchPriority:U,alt:f,title:h,width:X,height:Y,...j})},m=t.forwardRef(function(e,i){return"string"==typeof e.src&&e.src.trim().length>0?t.createElement(w,{...e,forwardedRef:i}):("undefined"!=typeof console&&console.warn&&console.warn("<Img /> requires src. No src provided, rendering null."),null)}),v=t.memo(m);v.displayName="PageSpeedImg";const b="undefined"!=typeof globalThis?globalThis:void 0;if(b)if(b.process){const e=b.process.env??(b.process.env={});void 0===e.NODE_ENV&&(e.NODE_ENV="production")}else b.process={env:{NODE_ENV:"production"}};e.Img=v,e.ImgDefaults=function({config:e,children:t}){return n.useEffect(()=>{h(e??null)},[e]),t?n.createElement(n.Fragment,null,t):null},e.setDefaultOptixFlowConfig=h,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
2
2
  //# sourceMappingURL=page-speed-img.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"page-speed-img.umd.js","sources":["../../node_modules/.pnpm/@page-speed+hooks@0.4.5_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@page-speed/hooks/dist/chunk-ZTHTGLZO.js","../../src/core/useMediaSelectionEffect.ts","../../src/core/useResponsiveReset.ts","../../src/core/Img.tsx","../../src/core/useImgDebugLog.ts","../../src/index.ts"],"sourcesContent":["import { useMemo, useState, useRef, useLayoutEffect, useEffect, useCallback } from 'react';\n\nvar BASE_URL = \"https://octane.cdn.ing/api/v1/images/transform?\";\nvar DPR_MULTIPLIERS = [1, 2];\nvar useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\nfunction useOptimizedImage(options) {\n const {\n src,\n eager = false,\n threshold = 0.1,\n rootMargin = \"50px\",\n width,\n height,\n optixFlowConfig\n } = options;\n const optixFlowApiKey = useMemo(() => {\n return optixFlowConfig?.apiKey;\n }, [optixFlowConfig?.apiKey]);\n const useOptixFlow = useMemo(() => {\n return optixFlowApiKey ? true : false;\n }, [optixFlowApiKey]);\n const [state, setState] = useState({\n isLoaded: false,\n isInView: false\n });\n const [measuredSize, setMeasuredSize] = useState({\n width: 0,\n height: 0\n });\n const imgRef = useRef(null);\n const observerRef = useRef(null);\n const size = useMemo(\n () => ({\n width: width ?? measuredSize.width,\n height: height ?? measuredSize.height\n }),\n [width, height, measuredSize.width, measuredSize.height]\n );\n useIsomorphicLayoutEffect(() => {\n if (!imgRef.current) return;\n if (width !== void 0 && height !== void 0) return;\n const calculateRenderedSize = () => {\n const img2 = imgRef.current;\n if (!img2) return;\n const renderedWidth = width ?? (Math.round(img2.clientWidth) || img2.naturalWidth || 0);\n const renderedHeight = height ?? (Math.round(img2.clientHeight) || img2.naturalHeight || 0);\n if (renderedWidth > 0 || renderedHeight > 0) {\n setMeasuredSize((prev) => {\n if (prev.width !== renderedWidth || prev.height !== renderedHeight) {\n return { width: renderedWidth, height: renderedHeight };\n }\n return prev;\n });\n }\n };\n if (imgRef.current.clientWidth > 0) {\n calculateRenderedSize();\n }\n const img = imgRef.current;\n img.addEventListener(\"load\", calculateRenderedSize);\n let resizeObserver = null;\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(() => {\n calculateRenderedSize();\n });\n resizeObserver.observe(img);\n }\n return () => {\n img.removeEventListener(\"load\", calculateRenderedSize);\n resizeObserver?.disconnect();\n };\n }, [width, height]);\n const buildOptixFlowUrl = useCallback(\n (imgWidth, imgHeight, format) => {\n if (!useOptixFlow) return src;\n if (!imgWidth || !imgHeight) return src;\n const params = new URLSearchParams();\n params.set(\"url\", src);\n params.set(\"fit\", String(optixFlowConfig?.objectFit ?? \"cover\"));\n params.set(\"w\", String(imgWidth));\n params.set(\"h\", String(imgHeight));\n params.set(\"q\", String(optixFlowConfig?.compressionLevel ?? 75));\n params.set(\"f\", format);\n params.set(\"apiKey\", optixFlowApiKey);\n return `${BASE_URL}${params.toString()}`;\n },\n [\n useOptixFlow,\n src,\n optixFlowConfig?.compressionLevel,\n optixFlowApiKey,\n optixFlowConfig?.objectFit\n ]\n );\n const generateSrcset = useCallback(\n (baseWidth, baseHeight, format) => {\n if (!useOptixFlow || baseWidth === 0 || baseHeight === 0) return \"\";\n return DPR_MULTIPLIERS.map((dpr) => {\n const scaledWidth = Math.round(baseWidth * dpr);\n const scaledHeight = Math.round(baseHeight * dpr);\n const url = buildOptixFlowUrl(scaledWidth, scaledHeight, format);\n return `${url} ${dpr}x`;\n }).join(\", \");\n },\n [useOptixFlow, buildOptixFlowUrl]\n );\n const primarySrc = useMemo(() => {\n const hasDimensions = size.width > 0 && size.height > 0;\n if (!useOptixFlow || !hasDimensions) return src;\n const fallbackFormat = optixFlowConfig?.renderedFileType ?? \"jpeg\";\n return buildOptixFlowUrl(size.width, size.height, fallbackFormat);\n }, [\n useOptixFlow,\n src,\n size.width,\n size.height,\n optixFlowConfig?.renderedFileType,\n optixFlowConfig?.compressionLevel,\n optixFlowConfig?.objectFit,\n buildOptixFlowUrl\n ]);\n const srcset = useMemo(() => {\n return {\n avif: generateSrcset(size.width, size.height, \"avif\"),\n webp: generateSrcset(size.width, size.height, \"webp\"),\n jpeg: generateSrcset(size.width, size.height, \"jpeg\")\n };\n }, [size.width, size.height, generateSrcset]);\n const sizes = useMemo(() => {\n if (size.width === 0) return \"\";\n return `${size.width}px`;\n }, [size.width]);\n useEffect(() => {\n if (typeof window === \"undefined\" || !imgRef.current) {\n return;\n }\n if (eager) {\n setState({ isLoaded: false, isInView: true });\n return;\n }\n observerRef.current = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setState((prev) => ({ ...prev, isInView: true }));\n observerRef.current?.disconnect();\n }\n },\n { threshold, rootMargin }\n );\n observerRef.current.observe(imgRef.current);\n return () => {\n observerRef.current?.disconnect();\n };\n }, [eager, threshold, rootMargin]);\n useEffect(() => {\n if (!imgRef.current) return;\n const handleLoad = () => {\n setState((prev) => ({ ...prev, isLoaded: true }));\n };\n const img = imgRef.current;\n if (img.complete) {\n handleLoad();\n } else {\n img.addEventListener(\"load\", handleLoad);\n return () => img.removeEventListener(\"load\", handleLoad);\n }\n }, [state.isInView]);\n const ref = useCallback((node) => {\n imgRef.current = node;\n }, []);\n const emptySrcset = { avif: \"\", webp: \"\", jpeg: \"\" };\n return {\n ref,\n // Primary src uses exact rendered dimensions for Lighthouse \"Properly size images\" compliance\n src: state.isInView || eager ? primarySrc : src,\n // Srcset with format variants and DPR multipliers for <picture> element\n srcset: state.isInView || eager ? srcset : emptySrcset,\n // Sizes attribute for responsive image selection\n sizes: state.isInView || eager ? sizes : \"\",\n isLoaded: state.isLoaded,\n isInView: state.isInView,\n loading: eager ? \"eager\" : \"lazy\",\n size\n };\n}\n\nexport { useOptimizedImage };\n//# sourceMappingURL=chunk-ZTHTGLZO.js.map\n//# sourceMappingURL=chunk-ZTHTGLZO.js.map","import { useEffect } from 'react';\n\nconst MEDIA_SELECTED_EVENT = 'dt:media-selected';\nconst mediaSelectionHandler = () => {\n // no-op: the real handler is attached in the builder via addEventListener\n};\n\nlet mediaSelectionListenerCount = 0;\nlet isMediaSelectionListenerAttached = false;\n\nexport function sendMediaSelection(blockId: string, payload: unknown) {\n if (typeof window === 'undefined') return;\n window.dispatchEvent(\n new CustomEvent(MEDIA_SELECTED_EVENT, {\n detail: { blockId, payload },\n })\n );\n}\n\nexport function useMediaSelectionEffect() {\n useEffect(() => {\n if (typeof window === 'undefined') return;\n if (!isMediaSelectionListenerAttached) {\n window.addEventListener(MEDIA_SELECTED_EVENT, mediaSelectionHandler);\n isMediaSelectionListenerAttached = true;\n }\n mediaSelectionListenerCount += 1;\n\n return () => {\n mediaSelectionListenerCount -= 1;\n if (mediaSelectionListenerCount <= 0 && isMediaSelectionListenerAttached) {\n window.removeEventListener(MEDIA_SELECTED_EVENT, mediaSelectionHandler);\n isMediaSelectionListenerAttached = false;\n }\n };\n }, []);\n}\n","import { useEffect } from 'react';\n\nexport function resetResponsivePictureState(element: HTMLPictureElement | null) {\n if (!element) return;\n element.querySelectorAll('source').forEach((source) => {\n // force browser to reconsider responsive sources\n const srcset = source.getAttribute('srcset');\n if (srcset) {\n source.setAttribute('data-srcset', srcset);\n source.removeAttribute('srcset');\n requestAnimationFrame(() => {\n source.setAttribute('srcset', srcset);\n });\n }\n });\n}\n\nexport function useResponsiveReset(ref: React.RefObject<HTMLPictureElement | HTMLImageElement>) {\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n if (element instanceof HTMLPictureElement) {\n resetResponsivePictureState(element);\n } else if (element.parentElement instanceof HTMLPictureElement) {\n resetResponsivePictureState(element.parentElement);\n }\n }, [ref]);\n}\n\n","\"use client\";\n\nimport React, { forwardRef, memo, useCallback, useMemo, useRef } from \"react\";\nimport { useOptimizedImage } from \"@page-speed/hooks/media\";\nimport type { UseOptimizedImageOptions } from \"@page-speed/hooks/media\";\nimport { useImgDebugLog } from \"./useImgDebugLog.js\";\nimport { useMediaSelectionEffect } from \"./useMediaSelectionEffect.js\";\nimport { useResponsiveReset } from \"./useResponsiveReset.js\";\n\ntype NativeImgProps = Omit<\n React.ImgHTMLAttributes<HTMLImageElement>,\n \"src\" | \"srcSet\" | \"sizes\"\n> & {\n src?: string;\n};\n\nexport type ImgProps = NativeImgProps & {\n /** Explicit sizes attribute (otherwise derived from useOptimizedImage) */\n sizes?: string;\n /** Force eager load (alias for loading=\"eager\") */\n eager?: boolean;\n /** Intersection observer threshold for lazy loading */\n intersectionThreshold?: number;\n /** Intersection observer root margin for lazy loading */\n intersectionMargin?: string;\n /** OptixFlow integration options */\n optixFlowConfig?: UseOptimizedImageOptions[\"optixFlowConfig\"];\n /** Enable debug logging for image requests */\n useDebugMode?: boolean;\n};\n\ntype ForwardedImgProps = ImgProps & {\n forwardedRef: React.Ref<HTMLImageElement | null>;\n};\n\nconst TRANSPARENT_PIXEL =\n \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==\";\n\nlet defaultOptixFlowConfig:\n | UseOptimizedImageOptions[\"optixFlowConfig\"]\n | undefined;\n\nconst readGlobalOptixFlowConfig = ():\n | UseOptimizedImageOptions[\"optixFlowConfig\"]\n | undefined => {\n if (typeof globalThis === \"undefined\") return undefined;\n const globalAny = globalThis as any;\n return (\n globalAny.PageSpeedImgDefaults?.optixFlowConfig ||\n globalAny.OpensiteImgDefaults?.optixFlowConfig ||\n globalAny.PAGE_SPEED_IMG_DEFAULTS?.optixFlowConfig\n );\n};\n\nconst resolveOptixFlowConfig = (\n config?: UseOptimizedImageOptions[\"optixFlowConfig\"],\n): UseOptimizedImageOptions[\"optixFlowConfig\"] | undefined => {\n return config ?? defaultOptixFlowConfig ?? readGlobalOptixFlowConfig();\n};\n\nexport const setDefaultOptixFlowConfig = (\n config?: UseOptimizedImageOptions[\"optixFlowConfig\"] | null,\n) => {\n defaultOptixFlowConfig = config ?? undefined;\n};\n\nconst parseDimension = (value: unknown): number | undefined => {\n if (value === \"\" || value === null || typeof value === \"undefined\")\n return undefined;\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\") {\n const numeric = Number(value);\n if (Number.isFinite(numeric)) return numeric;\n }\n return undefined;\n};\n\nconst composeRefs = (\n hookRef: (node: HTMLImageElement | null) => void,\n forwardedRef: React.Ref<HTMLImageElement | null>,\n localRef: React.RefObject<HTMLImageElement>,\n) =>\n useCallback(\n (node: HTMLImageElement | null) => {\n hookRef(node);\n // eslint-disable-next-line no-param-reassign\n (localRef as any).current = node;\n if (typeof forwardedRef === \"function\") {\n forwardedRef(node);\n } else if (forwardedRef && typeof (forwardedRef as any) === \"object\") {\n (forwardedRef as any).current = node;\n }\n },\n [hookRef, forwardedRef, localRef],\n );\n\nconst ModernImg: React.FC<ForwardedImgProps> = ({\n sizes,\n loading,\n decoding,\n alt,\n title,\n src: directSrc,\n eager,\n width,\n height,\n fetchPriority,\n intersectionMargin,\n intersectionThreshold,\n optixFlowConfig,\n useDebugMode,\n forwardedRef,\n ...restProps\n}) => {\n const imgRef = useRef<HTMLImageElement | null>(null);\n const pictureRef = useRef<HTMLPictureElement | null>(null);\n\n useResponsiveReset(pictureRef);\n useMediaSelectionEffect();\n\n const normalizedSrc = useMemo(\n () => (typeof directSrc === \"string\" ? directSrc.trim() : \"\"),\n [directSrc],\n );\n const numericWidth = useMemo(() => parseDimension(width), [width]);\n const numericHeight = useMemo(() => parseDimension(height), [height]);\n const resolvedOptixConfig = useMemo(\n () => resolveOptixFlowConfig(optixFlowConfig),\n [optixFlowConfig],\n );\n const eagerLoad = useMemo(() => {\n return eager ?? loading === \"eager\";\n }, [eager, loading]);\n\n const hookOptions = useMemo(\n () => ({\n src: normalizedSrc,\n eager: eagerLoad,\n width: numericWidth,\n height: numericHeight,\n rootMargin: intersectionMargin ?? \"200px\",\n threshold: intersectionThreshold ?? 0.1,\n optixFlowConfig: resolvedOptixConfig,\n }),\n [\n normalizedSrc,\n eagerLoad,\n numericWidth,\n numericHeight,\n intersectionMargin,\n intersectionThreshold,\n resolvedOptixConfig,\n ],\n );\n\n const {\n ref: hookRef,\n src,\n srcset,\n sizes: computedSizes,\n loading: hookLoading,\n isInView,\n size,\n } = useOptimizedImage(hookOptions);\n\n const mergedRef = composeRefs(hookRef, forwardedRef, imgRef);\n\n const sizesAttr = useMemo(() => {\n return sizes ?? (computedSizes || undefined);\n }, [sizes, computedSizes]);\n const loadingAttr = useMemo(() => {\n return loading ?? hookLoading ?? \"lazy\";\n }, [loading, hookLoading]);\n const decodingAttr = useMemo(() => {\n return decoding ?? \"async\";\n }, [decoding]);\n const fetchPriorityAttr = useMemo(() => {\n return fetchPriority ?? (eagerLoad ? \"high\" : undefined);\n }, [fetchPriority, eagerLoad]);\n\n const hasSrcSet = useMemo(() => {\n return Boolean(srcset.avif || srcset.webp || srcset.jpeg);\n }, [srcset.avif, srcset.webp, srcset.jpeg]);\n const imgSrc = useMemo(() => {\n return src || normalizedSrc || TRANSPARENT_PIXEL;\n }, [src, normalizedSrc]);\n const inlineSrcSet = useMemo(() => {\n return hasSrcSet && !srcset.avif && !srcset.webp ? srcset.jpeg : \"\";\n }, [hasSrcSet, srcset.avif, srcset.webp, srcset.jpeg]);\n\n const widthAttr = useMemo(() => {\n return numericWidth ?? (size.width || undefined);\n }, [numericWidth, size?.width]);\n const heightAttr = useMemo(() => {\n return numericHeight ?? (size.height || undefined);\n }, [numericHeight, size?.height]);\n\n useImgDebugLog({\n enabled: useDebugMode ?? false,\n eagerLoad,\n isInView,\n imgSrc,\n transparentPixel: TRANSPARENT_PIXEL,\n srcset,\n sizesAttr,\n });\n\n if (!hasSrcSet) {\n return (\n <img\n ref={mergedRef}\n src={imgSrc}\n loading={loadingAttr}\n decoding={decodingAttr}\n fetchPriority={fetchPriorityAttr}\n alt={alt}\n title={title}\n width={widthAttr}\n height={heightAttr}\n {...restProps}\n />\n );\n }\n\n return (\n <picture ref={pictureRef}>\n {srcset.avif ? (\n <source type=\"image/avif\" srcSet={srcset.avif} sizes={sizesAttr} />\n ) : null}\n {srcset.webp ? (\n <source type=\"image/webp\" srcSet={srcset.webp} sizes={sizesAttr} />\n ) : null}\n <img\n ref={mergedRef}\n src={imgSrc}\n srcSet={inlineSrcSet || undefined}\n sizes={inlineSrcSet ? sizesAttr : undefined}\n loading={loadingAttr}\n decoding={decodingAttr}\n fetchPriority={fetchPriorityAttr}\n alt={alt}\n title={title}\n width={widthAttr}\n height={heightAttr}\n {...restProps}\n />\n </picture>\n );\n};\n\nconst ImgBase = forwardRef<HTMLImageElement, ImgProps>(\n function Img(props, ref) {\n const hasSrc = typeof props.src === \"string\" && props.src.trim().length > 0;\n if (!hasSrc) {\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\"<Img /> requires src. No src provided, rendering null.\");\n }\n return null;\n }\n\n return <ModernImg {...props} forwardedRef={ref} />;\n },\n);\n\nexport const Img = memo(ImgBase);\nImg.displayName = \"PageSpeedImg\";\n","import { useEffect, useRef } from \"react\";\n\ninterface ImgDebugLogParams {\n enabled: boolean;\n eagerLoad: boolean;\n isInView: boolean;\n imgSrc: string;\n transparentPixel: string;\n srcset: { avif: string; webp: string; jpeg: string };\n sizesAttr: string | undefined;\n}\n\n/**\n * Logs image-request details to the console when `enabled` is true.\n * When disabled (the default), the hook short-circuits on the very first\n * line so there is no meaningful runtime cost.\n */\nexport function useImgDebugLog({\n enabled,\n eagerLoad,\n isInView,\n imgSrc,\n transparentPixel,\n srcset,\n sizesAttr,\n}: ImgDebugLogParams): void {\n const logKeyRef = useRef<string | null>(null);\n\n useEffect(() => {\n if (!enabled) return;\n if (typeof window === \"undefined\") return;\n if (!eagerLoad && !isInView) return;\n if (!imgSrc || imgSrc === transparentPixel) return;\n\n const logKey = [\n imgSrc,\n srcset.avif,\n srcset.webp,\n srcset.jpeg,\n sizesAttr ?? \"\",\n ].join(\"|\");\n\n if (logKeyRef.current === logKey) return;\n logKeyRef.current = logKey;\n\n if (typeof console !== \"undefined\" && console.info) {\n console.info(\"[PageSpeedImg] image request\", {\n src: imgSrc,\n srcset,\n sizes: sizesAttr,\n });\n }\n }, [\n enabled,\n eagerLoad,\n imgSrc,\n isInView,\n sizesAttr,\n srcset.avif,\n srcset.webp,\n srcset.jpeg,\n transparentPixel,\n ]);\n}\n","// Ensure process.env exists when the module is loaded directly in the browser UMD build.\ntype GlobalWithProcess = typeof globalThis & { process?: NodeJS.Process };\n\nconst globalObject =\n typeof globalThis !== 'undefined' ? (globalThis as GlobalWithProcess) : undefined;\n\nif (globalObject) {\n if (!globalObject.process) {\n globalObject.process = {\n env: { NODE_ENV: 'production' } as NodeJS.ProcessEnv,\n } as NodeJS.Process;\n } else {\n const env = globalObject.process.env ?? (globalObject.process.env = {} as NodeJS.ProcessEnv);\n if (typeof env.NODE_ENV === 'undefined') {\n env.NODE_ENV = 'production';\n }\n }\n}\n\nimport type { UseOptimizedImageOptions } from '@page-speed/hooks/media';\n\nexport * from './core/index.js';\nexport type {\n ImageFormat,\n SrcsetByFormat,\n UseOptimizedImageOptions,\n UseOptimizedImageState,\n} from '@page-speed/hooks/media';\nexport type OptixFlowConfig = UseOptimizedImageOptions['optixFlowConfig'];\n"],"names":["DPR_MULTIPLIERS","useIsomorphicLayoutEffect","window","useLayoutEffect","useEffect","MEDIA_SELECTED_EVENT","mediaSelectionHandler","mediaSelectionListenerCount","isMediaSelectionListenerAttached","resetResponsivePictureState","element","querySelectorAll","forEach","source","srcset","getAttribute","setAttribute","removeAttribute","requestAnimationFrame","TRANSPARENT_PIXEL","defaultOptixFlowConfig","resolveOptixFlowConfig","config","globalThis","globalAny","_a","PageSpeedImgDefaults","optixFlowConfig","_b","OpensiteImgDefaults","_c","PAGE_SPEED_IMG_DEFAULTS","readGlobalOptixFlowConfig","parseDimension","value","Number","isFinite","numeric","ModernImg","sizes","loading","decoding","alt","title","src","directSrc","eager","width","height","fetchPriority","intersectionMargin","intersectionThreshold","useDebugMode","forwardedRef","restProps","imgRef","useRef","pictureRef","ref","current","HTMLPictureElement","parentElement","addEventListener","removeEventListener","normalizedSrc","useMemo","trim","numericWidth","numericHeight","resolvedOptixConfig","eagerLoad","hookOptions","rootMargin","threshold","hookRef","computedSizes","hookLoading","isInView","size","options","optixFlowApiKey","apiKey","useOptixFlow","state","setState","useState","isLoaded","measuredSize","setMeasuredSize","observerRef","calculateRenderedSize","img2","renderedWidth","Math","round","clientWidth","naturalWidth","renderedHeight","clientHeight","naturalHeight","prev","img","resizeObserver","ResizeObserver","observe","disconnect","buildOptixFlowUrl","useCallback","imgWidth","imgHeight","format","params","URLSearchParams","set","String","objectFit","compressionLevel","toString","generateSrcset","baseWidth","baseHeight","map","dpr","scaledWidth","scaledHeight","join","primarySrc","hasDimensions","fallbackFormat","renderedFileType","avif","webp","jpeg","IntersectionObserver","entry","isIntersecting","handleLoad","complete","node","useOptimizedImage","mergedRef","localRef","composeRefs","sizesAttr","loadingAttr","decodingAttr","fetchPriorityAttr","hasSrcSet","Boolean","imgSrc","inlineSrcSet","widthAttr","heightAttr","enabled","transparentPixel","logKeyRef","logKey","console","info","useImgDebugLog","createElement","React","type","srcSet","ImgBase","forwardRef","props","length","warn","Img","memo","displayName","globalObject","process","env","NODE_ENV"],"mappings":"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","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"page-speed-img.umd.js","sources":["../../node_modules/.pnpm/@page-speed+hooks@0.4.5_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@page-speed/hooks/dist/chunk-ZTHTGLZO.js","../../src/core/useMediaSelectionEffect.ts","../../src/core/useResponsiveReset.ts","../../src/core/Img.tsx","../../src/core/useImgDebugLog.ts","../../src/index.ts","../../src/core/ImgDefaults.tsx"],"sourcesContent":["import { useMemo, useState, useRef, useLayoutEffect, useEffect, useCallback } from 'react';\n\nvar BASE_URL = \"https://octane.cdn.ing/api/v1/images/transform?\";\nvar DPR_MULTIPLIERS = [1, 2];\nvar useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\nfunction useOptimizedImage(options) {\n const {\n src,\n eager = false,\n threshold = 0.1,\n rootMargin = \"50px\",\n width,\n height,\n optixFlowConfig\n } = options;\n const optixFlowApiKey = useMemo(() => {\n return optixFlowConfig?.apiKey;\n }, [optixFlowConfig?.apiKey]);\n const useOptixFlow = useMemo(() => {\n return optixFlowApiKey ? true : false;\n }, [optixFlowApiKey]);\n const [state, setState] = useState({\n isLoaded: false,\n isInView: false\n });\n const [measuredSize, setMeasuredSize] = useState({\n width: 0,\n height: 0\n });\n const imgRef = useRef(null);\n const observerRef = useRef(null);\n const size = useMemo(\n () => ({\n width: width ?? measuredSize.width,\n height: height ?? measuredSize.height\n }),\n [width, height, measuredSize.width, measuredSize.height]\n );\n useIsomorphicLayoutEffect(() => {\n if (!imgRef.current) return;\n if (width !== void 0 && height !== void 0) return;\n const calculateRenderedSize = () => {\n const img2 = imgRef.current;\n if (!img2) return;\n const renderedWidth = width ?? (Math.round(img2.clientWidth) || img2.naturalWidth || 0);\n const renderedHeight = height ?? (Math.round(img2.clientHeight) || img2.naturalHeight || 0);\n if (renderedWidth > 0 || renderedHeight > 0) {\n setMeasuredSize((prev) => {\n if (prev.width !== renderedWidth || prev.height !== renderedHeight) {\n return { width: renderedWidth, height: renderedHeight };\n }\n return prev;\n });\n }\n };\n if (imgRef.current.clientWidth > 0) {\n calculateRenderedSize();\n }\n const img = imgRef.current;\n img.addEventListener(\"load\", calculateRenderedSize);\n let resizeObserver = null;\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(() => {\n calculateRenderedSize();\n });\n resizeObserver.observe(img);\n }\n return () => {\n img.removeEventListener(\"load\", calculateRenderedSize);\n resizeObserver?.disconnect();\n };\n }, [width, height]);\n const buildOptixFlowUrl = useCallback(\n (imgWidth, imgHeight, format) => {\n if (!useOptixFlow) return src;\n if (!imgWidth || !imgHeight) return src;\n const params = new URLSearchParams();\n params.set(\"url\", src);\n params.set(\"fit\", String(optixFlowConfig?.objectFit ?? \"cover\"));\n params.set(\"w\", String(imgWidth));\n params.set(\"h\", String(imgHeight));\n params.set(\"q\", String(optixFlowConfig?.compressionLevel ?? 75));\n params.set(\"f\", format);\n params.set(\"apiKey\", optixFlowApiKey);\n return `${BASE_URL}${params.toString()}`;\n },\n [\n useOptixFlow,\n src,\n optixFlowConfig?.compressionLevel,\n optixFlowApiKey,\n optixFlowConfig?.objectFit\n ]\n );\n const generateSrcset = useCallback(\n (baseWidth, baseHeight, format) => {\n if (!useOptixFlow || baseWidth === 0 || baseHeight === 0) return \"\";\n return DPR_MULTIPLIERS.map((dpr) => {\n const scaledWidth = Math.round(baseWidth * dpr);\n const scaledHeight = Math.round(baseHeight * dpr);\n const url = buildOptixFlowUrl(scaledWidth, scaledHeight, format);\n return `${url} ${dpr}x`;\n }).join(\", \");\n },\n [useOptixFlow, buildOptixFlowUrl]\n );\n const primarySrc = useMemo(() => {\n const hasDimensions = size.width > 0 && size.height > 0;\n if (!useOptixFlow || !hasDimensions) return src;\n const fallbackFormat = optixFlowConfig?.renderedFileType ?? \"jpeg\";\n return buildOptixFlowUrl(size.width, size.height, fallbackFormat);\n }, [\n useOptixFlow,\n src,\n size.width,\n size.height,\n optixFlowConfig?.renderedFileType,\n optixFlowConfig?.compressionLevel,\n optixFlowConfig?.objectFit,\n buildOptixFlowUrl\n ]);\n const srcset = useMemo(() => {\n return {\n avif: generateSrcset(size.width, size.height, \"avif\"),\n webp: generateSrcset(size.width, size.height, \"webp\"),\n jpeg: generateSrcset(size.width, size.height, \"jpeg\")\n };\n }, [size.width, size.height, generateSrcset]);\n const sizes = useMemo(() => {\n if (size.width === 0) return \"\";\n return `${size.width}px`;\n }, [size.width]);\n useEffect(() => {\n if (typeof window === \"undefined\" || !imgRef.current) {\n return;\n }\n if (eager) {\n setState({ isLoaded: false, isInView: true });\n return;\n }\n observerRef.current = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setState((prev) => ({ ...prev, isInView: true }));\n observerRef.current?.disconnect();\n }\n },\n { threshold, rootMargin }\n );\n observerRef.current.observe(imgRef.current);\n return () => {\n observerRef.current?.disconnect();\n };\n }, [eager, threshold, rootMargin]);\n useEffect(() => {\n if (!imgRef.current) return;\n const handleLoad = () => {\n setState((prev) => ({ ...prev, isLoaded: true }));\n };\n const img = imgRef.current;\n if (img.complete) {\n handleLoad();\n } else {\n img.addEventListener(\"load\", handleLoad);\n return () => img.removeEventListener(\"load\", handleLoad);\n }\n }, [state.isInView]);\n const ref = useCallback((node) => {\n imgRef.current = node;\n }, []);\n const emptySrcset = { avif: \"\", webp: \"\", jpeg: \"\" };\n return {\n ref,\n // Primary src uses exact rendered dimensions for Lighthouse \"Properly size images\" compliance\n src: state.isInView || eager ? primarySrc : src,\n // Srcset with format variants and DPR multipliers for <picture> element\n srcset: state.isInView || eager ? srcset : emptySrcset,\n // Sizes attribute for responsive image selection\n sizes: state.isInView || eager ? sizes : \"\",\n isLoaded: state.isLoaded,\n isInView: state.isInView,\n loading: eager ? \"eager\" : \"lazy\",\n size\n };\n}\n\nexport { useOptimizedImage };\n//# sourceMappingURL=chunk-ZTHTGLZO.js.map\n//# sourceMappingURL=chunk-ZTHTGLZO.js.map","import { useEffect } from 'react';\n\nconst MEDIA_SELECTED_EVENT = 'dt:media-selected';\nconst mediaSelectionHandler = () => {\n // no-op: the real handler is attached in the builder via addEventListener\n};\n\nlet mediaSelectionListenerCount = 0;\nlet isMediaSelectionListenerAttached = false;\n\nexport function sendMediaSelection(blockId: string, payload: unknown) {\n if (typeof window === 'undefined') return;\n window.dispatchEvent(\n new CustomEvent(MEDIA_SELECTED_EVENT, {\n detail: { blockId, payload },\n })\n );\n}\n\nexport function useMediaSelectionEffect() {\n useEffect(() => {\n if (typeof window === 'undefined') return;\n if (!isMediaSelectionListenerAttached) {\n window.addEventListener(MEDIA_SELECTED_EVENT, mediaSelectionHandler);\n isMediaSelectionListenerAttached = true;\n }\n mediaSelectionListenerCount += 1;\n\n return () => {\n mediaSelectionListenerCount -= 1;\n if (mediaSelectionListenerCount <= 0 && isMediaSelectionListenerAttached) {\n window.removeEventListener(MEDIA_SELECTED_EVENT, mediaSelectionHandler);\n isMediaSelectionListenerAttached = false;\n }\n };\n }, []);\n}\n","import { useEffect } from 'react';\n\nexport function resetResponsivePictureState(element: HTMLPictureElement | null) {\n if (!element) return;\n element.querySelectorAll('source').forEach((source) => {\n // force browser to reconsider responsive sources\n const srcset = source.getAttribute('srcset');\n if (srcset) {\n source.setAttribute('data-srcset', srcset);\n source.removeAttribute('srcset');\n requestAnimationFrame(() => {\n source.setAttribute('srcset', srcset);\n });\n }\n });\n}\n\nexport function useResponsiveReset(ref: React.RefObject<HTMLPictureElement | HTMLImageElement>) {\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n if (element instanceof HTMLPictureElement) {\n resetResponsivePictureState(element);\n } else if (element.parentElement instanceof HTMLPictureElement) {\n resetResponsivePictureState(element.parentElement);\n }\n }, [ref]);\n}\n\n","\"use client\";\n\nimport React, { forwardRef, memo, useCallback, useMemo, useRef } from \"react\";\nimport { useOptimizedImage } from \"@page-speed/hooks/media\";\nimport type { UseOptimizedImageOptions } from \"@page-speed/hooks/media\";\nimport { useImgDebugLog } from \"./useImgDebugLog.js\";\nimport { useMediaSelectionEffect } from \"./useMediaSelectionEffect.js\";\nimport { useResponsiveReset } from \"./useResponsiveReset.js\";\n\ntype NativeImgProps = Omit<\n React.ImgHTMLAttributes<HTMLImageElement>,\n \"src\" | \"srcSet\" | \"sizes\"\n> & {\n src?: string;\n};\n\nexport type ImgProps = NativeImgProps & {\n /** Explicit sizes attribute (otherwise derived from useOptimizedImage) */\n sizes?: string;\n /** Force eager load (alias for loading=\"eager\") */\n eager?: boolean;\n /** Intersection observer threshold for lazy loading */\n intersectionThreshold?: number;\n /** Intersection observer root margin for lazy loading */\n intersectionMargin?: string;\n /** OptixFlow integration options */\n optixFlowConfig?: UseOptimizedImageOptions[\"optixFlowConfig\"];\n /** Enable debug logging for image requests */\n useDebugMode?: boolean;\n};\n\ntype ForwardedImgProps = ImgProps & {\n forwardedRef: React.Ref<HTMLImageElement | null>;\n};\n\nconst TRANSPARENT_PIXEL =\n \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==\";\n\nlet defaultOptixFlowConfig:\n | UseOptimizedImageOptions[\"optixFlowConfig\"]\n | undefined;\n\nconst readGlobalOptixFlowConfig = ():\n | UseOptimizedImageOptions[\"optixFlowConfig\"]\n | undefined => {\n if (typeof globalThis === \"undefined\") return undefined;\n const globalAny = globalThis as any;\n return (\n globalAny.PageSpeedImgDefaults?.optixFlowConfig ||\n globalAny.OpensiteImgDefaults?.optixFlowConfig ||\n globalAny.PAGE_SPEED_IMG_DEFAULTS?.optixFlowConfig\n );\n};\n\nconst resolveOptixFlowConfig = (\n config?: UseOptimizedImageOptions[\"optixFlowConfig\"],\n): UseOptimizedImageOptions[\"optixFlowConfig\"] | undefined => {\n return config ?? defaultOptixFlowConfig ?? readGlobalOptixFlowConfig();\n};\n\nexport const setDefaultOptixFlowConfig = (\n config?: UseOptimizedImageOptions[\"optixFlowConfig\"] | null,\n) => {\n defaultOptixFlowConfig = config ?? undefined;\n};\n\nconst parseDimension = (value: unknown): number | undefined => {\n if (value === \"\" || value === null || typeof value === \"undefined\")\n return undefined;\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\") {\n const numeric = Number(value);\n if (Number.isFinite(numeric)) return numeric;\n }\n return undefined;\n};\n\nconst composeRefs = (\n hookRef: (node: HTMLImageElement | null) => void,\n forwardedRef: React.Ref<HTMLImageElement | null>,\n localRef: React.RefObject<HTMLImageElement>,\n) =>\n useCallback(\n (node: HTMLImageElement | null) => {\n hookRef(node);\n // eslint-disable-next-line no-param-reassign\n (localRef as any).current = node;\n if (typeof forwardedRef === \"function\") {\n forwardedRef(node);\n } else if (forwardedRef && typeof (forwardedRef as any) === \"object\") {\n (forwardedRef as any).current = node;\n }\n },\n [hookRef, forwardedRef, localRef],\n );\n\nconst ModernImg: React.FC<ForwardedImgProps> = ({\n sizes,\n loading,\n decoding,\n alt,\n title,\n src: directSrc,\n eager,\n width,\n height,\n fetchPriority,\n intersectionMargin,\n intersectionThreshold,\n optixFlowConfig,\n useDebugMode,\n forwardedRef,\n ...restProps\n}) => {\n const imgRef = useRef<HTMLImageElement | null>(null);\n const pictureRef = useRef<HTMLPictureElement | null>(null);\n\n useResponsiveReset(pictureRef);\n useMediaSelectionEffect();\n\n const normalizedSrc = useMemo(\n () => (typeof directSrc === \"string\" ? directSrc.trim() : \"\"),\n [directSrc],\n );\n const numericWidth = useMemo(() => parseDimension(width), [width]);\n const numericHeight = useMemo(() => parseDimension(height), [height]);\n const resolvedOptixConfig = useMemo(\n () => resolveOptixFlowConfig(optixFlowConfig),\n [optixFlowConfig],\n );\n const eagerLoad = useMemo(() => {\n return eager ?? loading === \"eager\";\n }, [eager, loading]);\n\n const hookOptions = useMemo(\n () => ({\n src: normalizedSrc,\n eager: eagerLoad,\n width: numericWidth,\n height: numericHeight,\n rootMargin: intersectionMargin ?? \"200px\",\n threshold: intersectionThreshold ?? 0.1,\n optixFlowConfig: resolvedOptixConfig,\n }),\n [\n normalizedSrc,\n eagerLoad,\n numericWidth,\n numericHeight,\n intersectionMargin,\n intersectionThreshold,\n resolvedOptixConfig,\n ],\n );\n\n const {\n ref: hookRef,\n src,\n srcset,\n sizes: computedSizes,\n loading: hookLoading,\n isInView,\n size,\n } = useOptimizedImage(hookOptions);\n\n const mergedRef = composeRefs(hookRef, forwardedRef, imgRef);\n\n const sizesAttr = useMemo(() => {\n return sizes ?? (computedSizes || undefined);\n }, [sizes, computedSizes]);\n const loadingAttr = useMemo(() => {\n return loading ?? hookLoading ?? \"lazy\";\n }, [loading, hookLoading]);\n const decodingAttr = useMemo(() => {\n return decoding ?? \"async\";\n }, [decoding]);\n const fetchPriorityAttr = useMemo(() => {\n return fetchPriority ?? (eagerLoad ? \"high\" : undefined);\n }, [fetchPriority, eagerLoad]);\n\n const hasSrcSet = useMemo(() => {\n return Boolean(srcset.avif || srcset.webp || srcset.jpeg);\n }, [srcset.avif, srcset.webp, srcset.jpeg]);\n const imgSrc = useMemo(() => {\n return src || normalizedSrc || TRANSPARENT_PIXEL;\n }, [src, normalizedSrc]);\n const inlineSrcSet = useMemo(() => {\n return hasSrcSet && !srcset.avif && !srcset.webp ? srcset.jpeg : \"\";\n }, [hasSrcSet, srcset.avif, srcset.webp, srcset.jpeg]);\n\n const widthAttr = useMemo(() => {\n return numericWidth ?? (size.width || undefined);\n }, [numericWidth, size?.width]);\n const heightAttr = useMemo(() => {\n return numericHeight ?? (size.height || undefined);\n }, [numericHeight, size?.height]);\n\n useImgDebugLog({\n enabled: useDebugMode ?? false,\n eagerLoad,\n isInView,\n imgSrc,\n transparentPixel: TRANSPARENT_PIXEL,\n srcset,\n sizesAttr,\n });\n\n if (!hasSrcSet) {\n return (\n <img\n ref={mergedRef}\n src={imgSrc}\n loading={loadingAttr}\n decoding={decodingAttr}\n fetchPriority={fetchPriorityAttr}\n alt={alt}\n title={title}\n width={widthAttr}\n height={heightAttr}\n {...restProps}\n />\n );\n }\n\n return (\n <picture ref={pictureRef}>\n {srcset.avif ? (\n <source type=\"image/avif\" srcSet={srcset.avif} sizes={sizesAttr} />\n ) : null}\n {srcset.webp ? (\n <source type=\"image/webp\" srcSet={srcset.webp} sizes={sizesAttr} />\n ) : null}\n <img\n ref={mergedRef}\n src={imgSrc}\n srcSet={inlineSrcSet || undefined}\n sizes={inlineSrcSet ? sizesAttr : undefined}\n loading={loadingAttr}\n decoding={decodingAttr}\n fetchPriority={fetchPriorityAttr}\n alt={alt}\n title={title}\n width={widthAttr}\n height={heightAttr}\n {...restProps}\n />\n </picture>\n );\n};\n\nconst ImgBase = forwardRef<HTMLImageElement, ImgProps>(\n function Img(props, ref) {\n const hasSrc = typeof props.src === \"string\" && props.src.trim().length > 0;\n if (!hasSrc) {\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\"<Img /> requires src. No src provided, rendering null.\");\n }\n return null;\n }\n\n return <ModernImg {...props} forwardedRef={ref} />;\n },\n);\n\nexport const Img = memo(ImgBase);\nImg.displayName = \"PageSpeedImg\";\n","import { useEffect, useRef } from \"react\";\n\ninterface ImgDebugLogParams {\n enabled: boolean;\n eagerLoad: boolean;\n isInView: boolean;\n imgSrc: string;\n transparentPixel: string;\n srcset: { avif: string; webp: string; jpeg: string };\n sizesAttr: string | undefined;\n}\n\n/**\n * Logs image-request details to the console when `enabled` is true.\n * When disabled (the default), the hook short-circuits on the very first\n * line so there is no meaningful runtime cost.\n */\nexport function useImgDebugLog({\n enabled,\n eagerLoad,\n isInView,\n imgSrc,\n transparentPixel,\n srcset,\n sizesAttr,\n}: ImgDebugLogParams): void {\n const logKeyRef = useRef<string | null>(null);\n\n useEffect(() => {\n if (!enabled) return;\n if (typeof window === \"undefined\") return;\n if (!eagerLoad && !isInView) return;\n if (!imgSrc || imgSrc === transparentPixel) return;\n\n const logKey = [\n imgSrc,\n srcset.avif,\n srcset.webp,\n srcset.jpeg,\n sizesAttr ?? \"\",\n ].join(\"|\");\n\n if (logKeyRef.current === logKey) return;\n logKeyRef.current = logKey;\n\n if (typeof console !== \"undefined\" && console.info) {\n console.info(\"[PageSpeedImg] image request\", {\n src: imgSrc,\n srcset,\n sizes: sizesAttr,\n });\n }\n }, [\n enabled,\n eagerLoad,\n imgSrc,\n isInView,\n sizesAttr,\n srcset.avif,\n srcset.webp,\n srcset.jpeg,\n transparentPixel,\n ]);\n}\n","// Ensure process.env exists when the module is loaded directly in the browser UMD build.\ntype GlobalWithProcess = typeof globalThis & { process?: NodeJS.Process };\n\nconst globalObject =\n typeof globalThis !== \"undefined\"\n ? (globalThis as GlobalWithProcess)\n : undefined;\n\nif (globalObject) {\n if (!globalObject.process) {\n globalObject.process = {\n env: { NODE_ENV: \"production\" } as NodeJS.ProcessEnv,\n } as NodeJS.Process;\n } else {\n const env =\n globalObject.process.env ??\n (globalObject.process.env = {} as NodeJS.ProcessEnv);\n if (typeof env.NODE_ENV === \"undefined\") {\n env.NODE_ENV = \"production\";\n }\n }\n}\n\nimport type { UseOptimizedImageOptions } from \"@page-speed/hooks/media\";\n\nexport * from \"./core/index.js\";\nexport type {\n ImageFormat,\n SrcsetByFormat,\n UseOptimizedImageOptions,\n UseOptimizedImageState,\n} from \"@page-speed/hooks/media\";\nexport type OptixFlowConfig = UseOptimizedImageOptions[\"optixFlowConfig\"];\n// Re-export specific items for clarity and CDN usage\nexport { Img, setDefaultOptixFlowConfig, ImgDefaults } from \"./core/index.js\";\nexport type { ImgProps, ImgDefaultsProps } from \"./core/index.js\";\n","\"use client\";\n\nimport * as React from \"react\";\nimport type { UseOptimizedImageOptions } from \"@page-speed/hooks/media\";\nimport { setDefaultOptixFlowConfig } from \"./Img.js\";\n\n/**\n * Props for the ImgDefaults component\n */\nexport interface ImgDefaultsProps {\n /**\n * OptixFlow configuration to set as default for all images.\n * Pass null or omit to clear any previously set default.\n * @example { apiKey: 'your-api-key', compressionLevel: 80 }\n */\n config?: UseOptimizedImageOptions[\"optixFlowConfig\"] | null;\n /**\n * Optional children — rendered as-is; the component itself has no DOM output.\n */\n children?: React.ReactNode;\n}\n\n/**\n * SSR-safe component that sets the default OptixFlow configuration for all\n * <Img /> components in the tree. Place it once at the app root level.\n *\n * Config is applied inside a useEffect, so it **never** runs during\n * server-side rendering and never causes hydration mismatches.\n * When unmounted (or when `config` becomes null/undefined) the default is\n * cleared automatically.\n *\n * @example\n * // As a wrapper (with children):\n * <ImgDefaults config={{ apiKey: 'your-api-key', compressionLevel: 80 }}>\n * <App />\n * </ImgDefaults>\n *\n * @example\n * // Standalone (no children required):\n * <ImgDefaults config={{ apiKey: 'your-api-key' }} />\n */\nexport function ImgDefaults({\n config,\n children,\n}: ImgDefaultsProps): React.ReactElement | null {\n React.useEffect(() => {\n setDefaultOptixFlowConfig(config ?? null);\n }, [config]);\n\n return children ? <>{children}</> : null;\n}\n"],"names":["DPR_MULTIPLIERS","useIsomorphicLayoutEffect","window","useLayoutEffect","useEffect","MEDIA_SELECTED_EVENT","mediaSelectionHandler","mediaSelectionListenerCount","isMediaSelectionListenerAttached","resetResponsivePictureState","element","querySelectorAll","forEach","source","srcset","getAttribute","setAttribute","removeAttribute","requestAnimationFrame","TRANSPARENT_PIXEL","defaultOptixFlowConfig","resolveOptixFlowConfig","config","globalThis","globalAny","_a","PageSpeedImgDefaults","optixFlowConfig","_b","OpensiteImgDefaults","_c","PAGE_SPEED_IMG_DEFAULTS","readGlobalOptixFlowConfig","setDefaultOptixFlowConfig","parseDimension","value","Number","isFinite","numeric","ModernImg","sizes","loading","decoding","alt","title","src","directSrc","eager","width","height","fetchPriority","intersectionMargin","intersectionThreshold","useDebugMode","forwardedRef","restProps","imgRef","useRef","pictureRef","ref","current","HTMLPictureElement","parentElement","addEventListener","removeEventListener","normalizedSrc","useMemo","trim","numericWidth","numericHeight","resolvedOptixConfig","eagerLoad","hookOptions","rootMargin","threshold","hookRef","computedSizes","hookLoading","isInView","size","options","optixFlowApiKey","apiKey","useOptixFlow","state","setState","useState","isLoaded","measuredSize","setMeasuredSize","observerRef","calculateRenderedSize","img2","renderedWidth","Math","round","clientWidth","naturalWidth","renderedHeight","clientHeight","naturalHeight","prev","img","resizeObserver","ResizeObserver","observe","disconnect","buildOptixFlowUrl","useCallback","imgWidth","imgHeight","format","params","URLSearchParams","set","String","objectFit","compressionLevel","toString","generateSrcset","baseWidth","baseHeight","map","dpr","scaledWidth","scaledHeight","join","primarySrc","hasDimensions","fallbackFormat","renderedFileType","avif","webp","jpeg","IntersectionObserver","entry","isIntersecting","handleLoad","complete","node","useOptimizedImage","mergedRef","localRef","composeRefs","sizesAttr","loadingAttr","decodingAttr","fetchPriorityAttr","hasSrcSet","Boolean","imgSrc","inlineSrcSet","widthAttr","heightAttr","enabled","transparentPixel","logKeyRef","logKey","console","info","useImgDebugLog","createElement","React","type","srcSet","ImgBase","forwardRef","props","length","warn","Img","memo","displayName","globalObject","process","env","NODE_ENV","children","Fragment"],"mappings":"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","x_google_ignoreList":[0]}
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { setDefaultOptixFlowConfig } from "./Img.js";
5
+ /**
6
+ * SSR-safe component that sets the default OptixFlow configuration for all
7
+ * <Img /> components in the tree. Place it once at the app root level.
8
+ *
9
+ * Config is applied inside a useEffect, so it **never** runs during
10
+ * server-side rendering and never causes hydration mismatches.
11
+ * When unmounted (or when `config` becomes null/undefined) the default is
12
+ * cleared automatically.
13
+ *
14
+ * @example
15
+ * // As a wrapper (with children):
16
+ * <ImgDefaults config={{ apiKey: 'your-api-key', compressionLevel: 80 }}>
17
+ * <App />
18
+ * </ImgDefaults>
19
+ *
20
+ * @example
21
+ * // Standalone (no children required):
22
+ * <ImgDefaults config={{ apiKey: 'your-api-key' }} />
23
+ */
24
+ export function ImgDefaults({ config, children, }) {
25
+ React.useEffect(() => {
26
+ setDefaultOptixFlowConfig(config ?? null);
27
+ }, [config]);
28
+ return children ? _jsx(_Fragment, { children: children }) : null;
29
+ }
@@ -0,0 +1,37 @@
1
+ import * as React from "react";
2
+ import type { UseOptimizedImageOptions } from "@page-speed/hooks/media";
3
+ /**
4
+ * Props for the ImgDefaults component
5
+ */
6
+ export interface ImgDefaultsProps {
7
+ /**
8
+ * OptixFlow configuration to set as default for all images.
9
+ * Pass null or omit to clear any previously set default.
10
+ * @example { apiKey: 'your-api-key', compressionLevel: 80 }
11
+ */
12
+ config?: UseOptimizedImageOptions["optixFlowConfig"] | null;
13
+ /**
14
+ * Optional children — rendered as-is; the component itself has no DOM output.
15
+ */
16
+ children?: React.ReactNode;
17
+ }
18
+ /**
19
+ * SSR-safe component that sets the default OptixFlow configuration for all
20
+ * <Img /> components in the tree. Place it once at the app root level.
21
+ *
22
+ * Config is applied inside a useEffect, so it **never** runs during
23
+ * server-side rendering and never causes hydration mismatches.
24
+ * When unmounted (or when `config` becomes null/undefined) the default is
25
+ * cleared automatically.
26
+ *
27
+ * @example
28
+ * // As a wrapper (with children):
29
+ * <ImgDefaults config={{ apiKey: 'your-api-key', compressionLevel: 80 }}>
30
+ * <App />
31
+ * </ImgDefaults>
32
+ *
33
+ * @example
34
+ * // Standalone (no children required):
35
+ * <ImgDefaults config={{ apiKey: 'your-api-key' }} />
36
+ */
37
+ export declare function ImgDefaults({ config, children, }: ImgDefaultsProps): React.ReactElement | null;
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { setDefaultOptixFlowConfig } from "./Img.js";
5
+ /**
6
+ * SSR-safe component that sets the default OptixFlow configuration for all
7
+ * <Img /> components in the tree. Place it once at the app root level.
8
+ *
9
+ * Config is applied inside a useEffect, so it **never** runs during
10
+ * server-side rendering and never causes hydration mismatches.
11
+ * When unmounted (or when `config` becomes null/undefined) the default is
12
+ * cleared automatically.
13
+ *
14
+ * @example
15
+ * // As a wrapper (with children):
16
+ * <ImgDefaults config={{ apiKey: 'your-api-key', compressionLevel: 80 }}>
17
+ * <App />
18
+ * </ImgDefaults>
19
+ *
20
+ * @example
21
+ * // Standalone (no children required):
22
+ * <ImgDefaults config={{ apiKey: 'your-api-key' }} />
23
+ */
24
+ export function ImgDefaults({ config, children, }) {
25
+ React.useEffect(() => {
26
+ setDefaultOptixFlowConfig(config ?? null);
27
+ }, [config]);
28
+ return children ? _jsx(_Fragment, { children: children }) : null;
29
+ }
@@ -0,0 +1,30 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { setDefaultOptixFlowConfig } from "./Img.js";
5
+ /**
6
+ * A component that sets the default OptixFlow configuration for all Img components
7
+ * in an SSR-safe way. This component should be rendered once at the app root level.
8
+ *
9
+ * The config is applied inside a useEffect, which means it only runs on the client
10
+ * and is never executed during server-side rendering.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * // In your app root
15
+ * <OptixFlowConfig config={{ apiKey: 'your-api-key' }}>
16
+ * <App />
17
+ * </OptixFlowConfig>
18
+ * ```
19
+ *
20
+ * Or without children:
21
+ * ```tsx
22
+ * <OptixFlowConfig config={{ apiKey: 'your-api-key' }} />
23
+ * ```
24
+ */
25
+ export function OptixFlowConfig({ config, children, }) {
26
+ React.useEffect(() => {
27
+ setDefaultOptixFlowConfig(config ?? null);
28
+ }, [config]);
29
+ return children ? _jsx(_Fragment, { children: children }) : null;
30
+ }
@@ -0,0 +1,37 @@
1
+ import * as React from "react";
2
+ import type { UseOptimizedImageOptions } from "@page-speed/hooks/media";
3
+ /**
4
+ * Props for the OptixFlowConfig component
5
+ */
6
+ export interface OptixFlowConfigProps {
7
+ /**
8
+ * OptixFlow configuration to set as default for all images
9
+ * @example { apiKey: 'your-api-key', compressionLevel: 80 }
10
+ */
11
+ config: UseOptimizedImageOptions["optixFlowConfig"];
12
+ /**
13
+ * Optional children (component returns null regardless)
14
+ */
15
+ children?: React.ReactNode;
16
+ }
17
+ /**
18
+ * A component that sets the default OptixFlow configuration for all Img components
19
+ * in an SSR-safe way. This component should be rendered once at the app root level.
20
+ *
21
+ * The config is applied inside a useEffect, which means it only runs on the client
22
+ * and is never executed during server-side rendering.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * // In your app root
27
+ * <OptixFlowConfig config={{ apiKey: 'your-api-key' }}>
28
+ * <App />
29
+ * </OptixFlowConfig>
30
+ * ```
31
+ *
32
+ * Or without children:
33
+ * ```tsx
34
+ * <OptixFlowConfig config={{ apiKey: 'your-api-key' }} />
35
+ * ```
36
+ */
37
+ export declare function OptixFlowConfig({ config, children, }: OptixFlowConfigProps): React.ReactElement | null;
@@ -0,0 +1,30 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { setDefaultOptixFlowConfig } from "./Img.js";
5
+ /**
6
+ * A component that sets the default OptixFlow configuration for all Img components
7
+ * in an SSR-safe way. This component should be rendered once at the app root level.
8
+ *
9
+ * The config is applied inside a useEffect, which means it only runs on the client
10
+ * and is never executed during server-side rendering.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * // In your app root
15
+ * <OptixFlowConfig config={{ apiKey: 'your-api-key' }}>
16
+ * <App />
17
+ * </OptixFlowConfig>
18
+ * ```
19
+ *
20
+ * Or without children:
21
+ * ```tsx
22
+ * <OptixFlowConfig config={{ apiKey: 'your-api-key' }} />
23
+ * ```
24
+ */
25
+ export function OptixFlowConfig({ config, children, }) {
26
+ React.useEffect(() => {
27
+ setDefaultOptixFlowConfig(config ?? null);
28
+ }, [config]);
29
+ return children ? _jsx(_Fragment, { children: children }) : null;
30
+ }
@@ -1 +1,2 @@
1
- export { Img, setDefaultOptixFlowConfig } from './Img.js';
1
+ export { Img, setDefaultOptixFlowConfig } from "./Img.js";
2
+ export { ImgDefaults } from "./ImgDefaults.js";
@@ -1 +1,4 @@
1
- export { Img, setDefaultOptixFlowConfig } from './Img.js';
1
+ export { Img, setDefaultOptixFlowConfig } from "./Img.js";
2
+ export type { ImgProps } from "./Img.js";
3
+ export { ImgDefaults } from "./ImgDefaults.js";
4
+ export type { ImgDefaultsProps } from "./ImgDefaults.js";
@@ -1 +1,2 @@
1
- export { Img, setDefaultOptixFlowConfig } from './Img.js';
1
+ export { Img, setDefaultOptixFlowConfig } from "./Img.js";
2
+ export { ImgDefaults } from "./ImgDefaults.js";
package/dist/index.cjs CHANGED
@@ -1,15 +1,20 @@
1
- const globalObject = typeof globalThis !== 'undefined' ? globalThis : undefined;
1
+ const globalObject = typeof globalThis !== "undefined"
2
+ ? globalThis
3
+ : undefined;
2
4
  if (globalObject) {
3
5
  if (!globalObject.process) {
4
6
  globalObject.process = {
5
- env: { NODE_ENV: 'production' },
7
+ env: { NODE_ENV: "production" },
6
8
  };
7
9
  }
8
10
  else {
9
- const env = globalObject.process.env ?? (globalObject.process.env = {});
10
- if (typeof env.NODE_ENV === 'undefined') {
11
- env.NODE_ENV = 'production';
11
+ const env = globalObject.process.env ??
12
+ (globalObject.process.env = {});
13
+ if (typeof env.NODE_ENV === "undefined") {
14
+ env.NODE_ENV = "production";
12
15
  }
13
16
  }
14
17
  }
15
- export * from './core/index.js';
18
+ export * from "./core/index.js";
19
+ // Re-export specific items for clarity and CDN usage
20
+ export { Img, setDefaultOptixFlowConfig, ImgDefaults } from "./core/index.js";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import type { UseOptimizedImageOptions } from '@page-speed/hooks/media';
2
- export * from './core/index.js';
3
- export type { ImageFormat, SrcsetByFormat, UseOptimizedImageOptions, UseOptimizedImageState, } from '@page-speed/hooks/media';
4
- export type OptixFlowConfig = UseOptimizedImageOptions['optixFlowConfig'];
1
+ import type { UseOptimizedImageOptions } from "@page-speed/hooks/media";
2
+ export * from "./core/index.js";
3
+ export type { ImageFormat, SrcsetByFormat, UseOptimizedImageOptions, UseOptimizedImageState, } from "@page-speed/hooks/media";
4
+ export type OptixFlowConfig = UseOptimizedImageOptions["optixFlowConfig"];
5
+ export { Img, setDefaultOptixFlowConfig, ImgDefaults } from "./core/index.js";
6
+ export type { ImgProps, ImgDefaultsProps } from "./core/index.js";
package/dist/index.js CHANGED
@@ -1,15 +1,20 @@
1
- const globalObject = typeof globalThis !== 'undefined' ? globalThis : undefined;
1
+ const globalObject = typeof globalThis !== "undefined"
2
+ ? globalThis
3
+ : undefined;
2
4
  if (globalObject) {
3
5
  if (!globalObject.process) {
4
6
  globalObject.process = {
5
- env: { NODE_ENV: 'production' },
7
+ env: { NODE_ENV: "production" },
6
8
  };
7
9
  }
8
10
  else {
9
- const env = globalObject.process.env ?? (globalObject.process.env = {});
10
- if (typeof env.NODE_ENV === 'undefined') {
11
- env.NODE_ENV = 'production';
11
+ const env = globalObject.process.env ??
12
+ (globalObject.process.env = {});
13
+ if (typeof env.NODE_ENV === "undefined") {
14
+ env.NODE_ENV = "production";
12
15
  }
13
16
  }
14
17
  }
15
- export * from './core/index.js';
18
+ export * from "./core/index.js";
19
+ // Re-export specific items for clarity and CDN usage
20
+ export { Img, setDefaultOptixFlowConfig, ImgDefaults } from "./core/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@page-speed/img",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Performance-optimized React Image component. Drop-in image implementation of web.dev best practices with zero configuration.",
5
5
  "keywords": [
6
6
  "react",
@@ -25,7 +25,7 @@
25
25
  "url": "https://github.com/opensite-ai/page-speed-img/issues"
26
26
  },
27
27
  "author": "OpenSite AI (https://opensite.ai)",
28
- "license": "BSD 3",
28
+ "license": "BSD-3-Clause",
29
29
  "private": false,
30
30
  "type": "module",
31
31
  "main": "dist/index.cjs",
@@ -66,7 +66,7 @@
66
66
  "bundle-analysis": "node scripts/analyze-bundle.js || true",
67
67
  "prepare": "husky",
68
68
  "prepack": "pnpm run build",
69
- "prepublishOnly": "pnpm run build"
69
+ "prepublishOnly": "pnpm run build && pnpm run test"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "react": ">=17.0.0",
@@ -75,6 +75,7 @@
75
75
  "devDependencies": {
76
76
  "@commitlint/cli": "^20.1.0",
77
77
  "@commitlint/config-conventional": "^20.0.0",
78
+ "@testing-library/react": "^16.3.2",
78
79
  "@types/node": "^20.17.6",
79
80
  "@types/react": "^18.3.3",
80
81
  "@types/react-dom": "^18.3.0",
@@ -90,7 +91,7 @@
90
91
  "vitest": "^3.2.4"
91
92
  },
92
93
  "dependencies": {
93
- "@opensite/hooks": "2.0.8",
94
+ "@opensite/hooks": "2.1.0",
94
95
  "@page-speed/hooks": "0.4.5"
95
96
  },
96
97
  "packageManager": "pnpm@10.24.0",