@page-speed/img 0.4.7 → 0.4.9

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/README.md CHANGED
@@ -58,23 +58,32 @@ What you get automatically:
58
58
 
59
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
60
 
61
- #### ✅ Recommended: `<OptixFlowConfig />` component (SSR-safe)
61
+ #### ✅ Recommended: `<ImgDefaults />` component (SSR-safe)
62
62
 
63
- The `OptixFlowConfig` 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.
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
64
 
65
65
  **As a wrapper around your app:**
66
66
 
67
67
  ```tsx
68
68
  // app/layout.tsx (Next.js App Router) or src/App.tsx
69
- import { OptixFlowConfig } from "@page-speed/img";
69
+ import { ImgDefaults } from "@page-speed/img";
70
70
 
71
- export default function RootLayout({ children }: { children: React.ReactNode }) {
71
+ export default function RootLayout({
72
+ children,
73
+ }: {
74
+ children: React.ReactNode;
75
+ }) {
72
76
  return (
73
77
  <html lang="en">
74
78
  <body>
75
- <OptixFlowConfig config={{ apiKey: process.env.NEXT_PUBLIC_OPTIX_API_KEY!, compressionLevel: 80 }}>
79
+ <ImgDefaults
80
+ config={{
81
+ apiKey: process.env.NEXT_PUBLIC_OPTIX_API_KEY!,
82
+ compressionLevel: 80,
83
+ }}
84
+ >
76
85
  {children}
77
- </OptixFlowConfig>
86
+ </ImgDefaults>
78
87
  </body>
79
88
  </html>
80
89
  );
@@ -85,12 +94,14 @@ export default function RootLayout({ children }: { children: React.ReactNode })
85
94
 
86
95
  ```tsx
87
96
  // Place anywhere in the tree; renders null, no visual output
88
- import { OptixFlowConfig } from "@page-speed/img";
97
+ import { ImgDefaults } from "@page-speed/img";
89
98
 
90
99
  export function Providers({ children }: { children: React.ReactNode }) {
91
100
  return (
92
101
  <>
93
- <OptixFlowConfig config={{ apiKey: process.env.NEXT_PUBLIC_OPTIX_API_KEY! }} />
102
+ <ImgDefaults
103
+ config={{ apiKey: process.env.NEXT_PUBLIC_OPTIX_API_KEY! }}
104
+ />
94
105
  {children}
95
106
  </>
96
107
  );
@@ -135,7 +146,7 @@ export function HeroImage() {
135
146
  }
136
147
  ```
137
148
 
138
- > **Note:** `setDefaultOptixFlowConfig` is not SSR-safe on its own. In server-rendered environments (Next.js, Remix, etc.) prefer the `<OptixFlowConfig />` component, which handles the client/server boundary automatically.
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.
139
150
 
140
151
  To clear or reset the global default at any point:
141
152
 
@@ -153,8 +164,8 @@ For vanilla HTML pages or CMS integrations using the UMD build, set the global b
153
164
  optixFlowConfig: {
154
165
  apiKey: "YOUR_OPTIX_KEY",
155
166
  compressionLevel: 80,
156
- objectFit: "cover"
157
- }
167
+ objectFit: "cover",
168
+ },
158
169
  };
159
170
  </script>
160
171
  ```
@@ -165,21 +176,21 @@ For vanilla HTML pages or CMS integrations using the UMD build, set the global b
165
176
 
166
177
  ### `<Img />` Props
167
178
 
168
- | Prop | Type | Default | Description |
169
- |---|---|---|---|
170
- | `src` | `string` | — | **(Required)** Image URL. |
171
- | `alt` | `string` | — | Alt text (passed through to `<img>`). |
172
- | `width` | `number \| string` | — | Hint for sizing and CLS prevention. |
173
- | `height` | `number \| string` | — | Hint for sizing and CLS prevention. |
174
- | `eager` | `boolean` | `false` | Force eager loading (above-the-fold). Equivalent to `loading="eager"`. |
175
- | `loading` | `"lazy" \| "eager"` | `"lazy"` | Native loading attribute. `eager` prop takes precedence. |
176
- | `decoding` | `"async" \| "sync" \| "auto"` | `"async"` | Native decoding attribute. |
177
- | `fetchPriority` | `"high" \| "low" \| "auto"` | `"high"` when eager | Native fetch priority. |
178
- | `sizes` | `string` | auto-computed | Override the `sizes` attribute generated by `useOptimizedImage`. |
179
- | `intersectionMargin` | `string` | `"200px"` | Root margin for the lazy-load IntersectionObserver. |
180
- | `intersectionThreshold` | `number` | `0.1` | Threshold for the lazy-load IntersectionObserver. |
181
- | `optixFlowConfig` | `OptixFlowConfig` | global default | Per-image OptixFlow config. Overrides any global default. |
182
- | `useDebugMode` | `boolean` | `false` | Log image request details to the console. |
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. |
183
194
 
184
195
  **`optixFlowConfig` shape:**
185
196
 
@@ -192,11 +203,11 @@ For vanilla HTML pages or CMS integrations using the UMD build, set the global b
192
203
  }
193
204
  ```
194
205
 
195
- ### `<OptixFlowConfig />` Props
206
+ ### `<ImgDefaults />` Props
196
207
 
197
- | Prop | Type | Description |
198
- |---|---|---|
199
- | `config` | `OptixFlowConfig` | The OptixFlow configuration to apply as the global default. |
208
+ | Prop | Type | Description |
209
+ | ---------- | ----------------- | --------------------------------------------------------------------------------------- |
210
+ | `config` | `OptixFlowConfig` | The OptixFlow configuration to apply as the global default. |
200
211
  | `children` | `React.ReactNode` | Optional. When provided, children are rendered; otherwise the component renders `null`. |
201
212
 
202
213
  ---
@@ -204,21 +215,30 @@ For vanilla HTML pages or CMS integrations using the UMD build, set the global b
204
215
  ### UMD usage
205
216
 
206
217
  ```html
207
- <script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
208
- <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
209
- <script src="https://cdn.jsdelivr.net/npm/@page-speed/img@0.4.7/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>
210
230
 
211
231
  <div id="app"></div>
212
232
  <script>
213
- const root = ReactDOM.createRoot(document.getElementById('app'));
233
+ const root = ReactDOM.createRoot(document.getElementById("app"));
214
234
  root.render(
215
235
  React.createElement(PageSpeedImg.Img, {
216
- src: 'https://images.example.com/card.jpg',
217
- alt: 'Card',
236
+ src: "https://images.example.com/card.jpg",
237
+ alt: "Card",
218
238
  width: 800,
219
239
  height: 600,
220
- optixFlowConfig: { apiKey: 'YOUR_OPTIX_KEY', compressionLevel: 70 }
221
- })
240
+ optixFlowConfig: { apiKey: "YOUR_OPTIX_KEY", compressionLevel: 70 },
241
+ }),
222
242
  );
223
243
  </script>
224
244
  ```
@@ -228,7 +248,7 @@ For vanilla HTML pages or CMS integrations using the UMD build, set the global b
228
248
  ### SSR considerations
229
249
 
230
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.
231
- - `<OptixFlowConfig />` 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.
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.
232
252
  - Guards exist for `window` and `IntersectionObserver` throughout the library, so modules can be imported safely in SSR environments without crashing.
233
253
 
234
254
  ---
@@ -259,4 +279,4 @@ PRs welcome. Please run `pnpm test` before submitting. See [CONTRIBUTING.md](./C
259
279
 
260
280
  ## License
261
281
 
262
- BSD 3-Clause
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";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:S,forwardedRef:I,...j})=>{const L=t.useRef(null),F=t.useRef(null);var P;P=F,t.useEffect(()=>{const e=P.current;e&&(e instanceof HTMLPictureElement?d(e):e.parentElement instanceof HTMLPictureElement&&d(e.parentElement))},[P]),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]),O=t.useMemo(()=>p(v),[v]),z=t.useMemo(()=>p(b),[b]),V=t.useMemo(()=>g(A),[A]),T=t.useMemo(()=>m??"eager"===i,[m,i]),C=t.useMemo(()=>({src:x,eager:T,width:O,height:z,rootMargin:E??"200px",threshold:M??.1,optixFlowConfig:V}),[x,T,O,z,E,M,V]),{ref:R,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]),S=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?S:"",isLoaded:g.isLoaded,isInView:g.isInView,loading:n?"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]))(R,I,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(()=>O??(H.width||void 0),[O,null==H?void 0:H.width]),Y=t.useMemo(()=>z??(H.height||void 0),[z,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:S??!1,eagerLoad:T,isInView:k,imgSrc:Q,transparentPixel:a,srcset:N,sizesAttr:B}),W?t.createElement("picture",{ref:F},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.OptixFlowConfig=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"})});
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",c=()=>{};let u=0,l=!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}}};const w=({sizes:e,loading:i,decoding:n,alt:f,title:h,src:w,width:m,height:v,fetchPriority:b,intersectionMargin:y,intersectionThreshold:E,optixFlowConfig:A,useDebugMode:I,forwardedRef:S,...M})=>{const L=t.useRef(null),j=t.useRef(null);var F;F=j,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 l||(window.addEventListener(s,c),l=!0),u+=1,()=>{u-=1,u<=0&&l&&(window.removeEventListener(s,c),l=!1)}},[]);const x=t.useMemo(()=>"string"==typeof w?w.trim():"",[w]),z=p(m),O=p(v),P=t.useMemo(()=>g(A),[A]),V="eager"===i,T=t.useMemo(()=>({src:x,eager:V,width:z,height:O,rootMargin:y??"200px",threshold:E??.1,optixFlowConfig:P}),[x,V,z,O,y,E,P]),{ref:R,src:C,srcset:D,sizes:N,loading:q,isInView:_,size:k}=function(e){const{src:i,eager:n=!1,threshold:s=.1,rootMargin:c="50px",width:u,height:l,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:u??p.width,height:l??p.height}),[u,l,p.width,p.height]);o(()=>{if(!m.current)return;if(void 0!==u&&void 0!==l)return;const e=()=>{const e=m.current;if(!e)return;const t=u??(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()}},[u,l]);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]),A=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]),I=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]),S=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:c}),v.current.observe(m.current),()=>{var e;null==(e=v.current)||e.disconnect()};h({isLoaded:!1,isInView:!0})}},[n,s,c]),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?A:i,srcset:g.isInView||n?I:{avif:"",webp:"",jpeg:""},sizes:g.isInView||n?S:"",isLoaded:g.isLoaded,isInView:g.isInView,loading:n?"eager":"lazy",size:b}}(T),H=function(e,i,n){return t.useCallback(t=>{e(t),n.current=t,"function"==typeof i?i(t):i&&"object"==typeof i&&(i.current=t)},[e,i,n])}(R,S,L),$=b??(V?"high":void 0);t.useEffect(()=>{const e=L.current;e&&($?e.setAttribute("fetchpriority",$):e.removeAttribute("fetchpriority"))},[$]);const B=C||x||a,G=Boolean(D.avif||D.webp||D.jpeg),K=!G||D.avif||D.webp?void 0:D.jpeg,U=e??N??void 0;!function({enabled:e,eagerLoad:i,isInView:n,imgSrc:r,transparentPixel:o,srcset:s,sizesAttr:c}){const u=t.useRef(null);t.useEffect(()=>{if(!e)return;if("undefined"==typeof window)return;if(!i&&!n)return;if(r===o)return;const t=[r,s.avif,s.webp,s.jpeg,c??""].join("|");u.current!==t&&(u.current=t,"undefined"!=typeof console&&console.info&&console.info("[PageSpeedImg] image request",{src:r,srcset:s,sizes:c}))},[e,i,r,n,c,s.avif,s.webp,s.jpeg,o])}({enabled:I??!1,eagerLoad:V,isInView:_,imgSrc:B,transparentPixel:a,srcset:D,sizesAttr:U});const W={...M,ref:H,src:B,loading:i??q??"lazy",decoding:n??"async",alt:f,title:h,width:z??k.width??void 0,height:O??k.height??void 0};return G?t.createElement("picture",{ref:j},D.avif&&t.createElement("source",{type:"image/avif",srcSet:D.avif,sizes:U}),D.webp&&t.createElement("source",{type:"image/webp",srcSet:D.webp,sizes:U}),t.createElement("img",{...W,srcSet:K,sizes:K?U:void 0})):t.createElement("img",{...W})},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";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:S,forwardedRef:I,...j})=>{const L=t.useRef(null),F=t.useRef(null);var P;P=F,t.useEffect(()=>{const e=P.current;e&&(e instanceof HTMLPictureElement?d(e):e.parentElement instanceof HTMLPictureElement&&d(e.parentElement))},[P]),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]),O=t.useMemo(()=>p(v),[v]),z=t.useMemo(()=>p(b),[b]),V=t.useMemo(()=>g(A),[A]),T=t.useMemo(()=>m??"eager"===i,[m,i]),C=t.useMemo(()=>({src:x,eager:T,width:O,height:z,rootMargin:E??"200px",threshold:M??.1,optixFlowConfig:V}),[x,T,O,z,E,M,V]),{ref:R,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]),S=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?S:"",isLoaded:g.isLoaded,isInView:g.isInView,loading:n?"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]))(R,I,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(()=>O??(H.width||void 0),[O,null==H?void 0:H.width]),Y=t.useMemo(()=>z??(H.height||void 0),[z,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:S??!1,eagerLoad:T,isInView:k,imgSrc:Q,transparentPixel:a,srcset:N,sizesAttr:B}),W?t.createElement("picture",{ref:F},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.OptixFlowConfig=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"})});
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",c=()=>{};let u=0,l=!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}}};const w=({sizes:e,loading:i,decoding:n,alt:f,title:h,src:w,width:m,height:v,fetchPriority:b,intersectionMargin:y,intersectionThreshold:E,optixFlowConfig:A,useDebugMode:I,forwardedRef:S,...M})=>{const L=t.useRef(null),j=t.useRef(null);var F;F=j,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 l||(window.addEventListener(s,c),l=!0),u+=1,()=>{u-=1,u<=0&&l&&(window.removeEventListener(s,c),l=!1)}},[]);const x=t.useMemo(()=>"string"==typeof w?w.trim():"",[w]),z=p(m),O=p(v),P=t.useMemo(()=>g(A),[A]),V="eager"===i,T=t.useMemo(()=>({src:x,eager:V,width:z,height:O,rootMargin:y??"200px",threshold:E??.1,optixFlowConfig:P}),[x,V,z,O,y,E,P]),{ref:R,src:C,srcset:D,sizes:N,loading:q,isInView:_,size:k}=function(e){const{src:i,eager:n=!1,threshold:s=.1,rootMargin:c="50px",width:u,height:l,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:u??p.width,height:l??p.height}),[u,l,p.width,p.height]);o(()=>{if(!m.current)return;if(void 0!==u&&void 0!==l)return;const e=()=>{const e=m.current;if(!e)return;const t=u??(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()}},[u,l]);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]),A=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]),I=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]),S=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:c}),v.current.observe(m.current),()=>{var e;null==(e=v.current)||e.disconnect()};h({isLoaded:!1,isInView:!0})}},[n,s,c]),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?A:i,srcset:g.isInView||n?I:{avif:"",webp:"",jpeg:""},sizes:g.isInView||n?S:"",isLoaded:g.isLoaded,isInView:g.isInView,loading:n?"eager":"lazy",size:b}}(T),H=function(e,i,n){return t.useCallback(t=>{e(t),n.current=t,"function"==typeof i?i(t):i&&"object"==typeof i&&(i.current=t)},[e,i,n])}(R,S,L),$=b??(V?"high":void 0);t.useEffect(()=>{const e=L.current;e&&($?e.setAttribute("fetchpriority",$):e.removeAttribute("fetchpriority"))},[$]);const B=C||x||a,G=Boolean(D.avif||D.webp||D.jpeg),K=!G||D.avif||D.webp?void 0:D.jpeg,U=e??N??void 0;!function({enabled:e,eagerLoad:i,isInView:n,imgSrc:r,transparentPixel:o,srcset:s,sizesAttr:c}){const u=t.useRef(null);t.useEffect(()=>{if(!e)return;if("undefined"==typeof window)return;if(!i&&!n)return;if(r===o)return;const t=[r,s.avif,s.webp,s.jpeg,c??""].join("|");u.current!==t&&(u.current=t,"undefined"!=typeof console&&console.info&&console.info("[PageSpeedImg] image request",{src:r,srcset:s,sizes:c}))},[e,i,r,n,c,s.avif,s.webp,s.jpeg,o])}({enabled:I??!1,eagerLoad:V,isInView:_,imgSrc:B,transparentPixel:a,srcset:D,sizesAttr:U});const W={...M,ref:H,src:B,loading:i??q??"lazy",decoding:n??"async",alt:f,title:h,width:z??k.width??void 0,height:O??k.height??void 0};return G?t.createElement("picture",{ref:j},D.avif&&t.createElement("source",{type:"image/avif",srcSet:D.avif,sizes:U}),D.webp&&t.createElement("source",{type:"image/webp",srcSet:D.webp,sizes:U}),t.createElement("img",{...W,srcSet:K,sizes:K?U:void 0})):t.createElement("img",{...W})},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","../../src/core/OptixFlowConfig.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' ? (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// Re-export specific items for clarity and CDN usage\nexport { Img, setDefaultOptixFlowConfig, OptixFlowConfig } from './core/index.js';\nexport type { ImgProps, OptixFlowConfigProps } 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 OptixFlowConfig component\n */\nexport interface OptixFlowConfigProps {\n /**\n * OptixFlow configuration to set as default for all images\n * @example { apiKey: 'your-api-key', compressionLevel: 80 }\n */\n config: UseOptimizedImageOptions[\"optixFlowConfig\"];\n /**\n * Optional children (component returns null regardless)\n */\n children?: React.ReactNode;\n}\n\n/**\n * A component that sets the default OptixFlow configuration for all Img components\n * in an SSR-safe way. This component should be rendered once at the app root level.\n *\n * The config is applied inside a useEffect, which means it only runs on the client\n * and is never executed during server-side rendering.\n *\n * @example\n * ```tsx\n * // In your app root\n * <OptixFlowConfig config={{ apiKey: 'your-api-key' }}>\n * <App />\n * </OptixFlowConfig>\n * ```\n *\n * Or without children:\n * ```tsx\n * <OptixFlowConfig config={{ apiKey: 'your-api-key' }} />\n * ```\n */\nexport function OptixFlowConfig({\n config,\n children,\n}: OptixFlowConfigProps): 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]}
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, useEffect, 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\" | \"fetchPriority\"\n> & {\n src?: string;\n};\n\nexport type ImgProps = NativeImgProps & {\n /** Explicit sizes attribute (otherwise derived from useOptimizedImage) */\n sizes?: string;\n /** Fetch priority hint – maps to the HTML `fetchpriority` attribute */\n fetchPriority?: \"high\" | \"low\" | \"auto\";\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\n/** Merges the hook ref, the forwarded ref, and a local ref into a single callback ref. */\nfunction useComposeRefs(\n hookRef: (node: HTMLImageElement | null) => void,\n forwardedRef: React.Ref<HTMLImageElement | null>,\n localRef: React.MutableRefObject<HTMLImageElement | null>,\n) {\n return useCallback(\n (node: HTMLImageElement | null) => {\n hookRef(node);\n localRef.current = node;\n if (typeof forwardedRef === \"function\") {\n forwardedRef(node);\n } else if (forwardedRef && typeof forwardedRef === \"object\") {\n (forwardedRef as React.MutableRefObject<HTMLImageElement | null>).current = node;\n }\n },\n [hookRef, forwardedRef, localRef],\n );\n}\n\nconst ModernImg: React.FC<ForwardedImgProps> = ({\n sizes,\n loading,\n decoding,\n alt,\n title,\n src: directSrc,\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\n const numericWidth = parseDimension(width);\n const numericHeight = parseDimension(height);\n\n const resolvedOptixConfig = useMemo(\n () => resolveOptixFlowConfig(optixFlowConfig),\n [optixFlowConfig],\n );\n\n const isEagerLoad = loading === \"eager\";\n\n const hookOptions = useMemo(\n () => ({\n src: normalizedSrc,\n eager: isEagerLoad,\n width: numericWidth,\n height: numericHeight,\n rootMargin: intersectionMargin ?? \"200px\",\n threshold: intersectionThreshold ?? 0.1,\n optixFlowConfig: resolvedOptixConfig,\n }),\n [\n normalizedSrc,\n isEagerLoad,\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 = useComposeRefs(hookRef, forwardedRef, imgRef);\n\n // Compute fetchpriority: explicit prop wins, otherwise \"high\" for eager loads\n const resolvedFetchPriority = fetchPriority ?? (isEagerLoad ? \"high\" : undefined);\n\n // Apply fetchpriority via DOM API for React 18 compatibility\n useEffect(() => {\n const el = imgRef.current;\n if (!el) return;\n if (resolvedFetchPriority) {\n el.setAttribute(\"fetchpriority\", resolvedFetchPriority);\n } else {\n el.removeAttribute(\"fetchpriority\");\n }\n }, [resolvedFetchPriority]);\n\n // Derived values\n const imgSrc = src || normalizedSrc || TRANSPARENT_PIXEL;\n const hasSrcSet = Boolean(srcset.avif || srcset.webp || srcset.jpeg);\n const inlineSrcSet = hasSrcSet && !srcset.avif && !srcset.webp ? srcset.jpeg : undefined;\n const sizesAttr = sizes ?? computedSizes ?? undefined;\n\n useImgDebugLog({\n enabled: useDebugMode ?? false,\n eagerLoad: isEagerLoad,\n isInView,\n imgSrc,\n transparentPixel: TRANSPARENT_PIXEL,\n srcset,\n sizesAttr,\n });\n\n // Shared img props for both render paths\n const imgProps = {\n ...restProps,\n ref: mergedRef,\n src: imgSrc,\n loading: loading ?? hookLoading ?? \"lazy\",\n decoding: decoding ?? \"async\",\n alt,\n title,\n width: numericWidth ?? size.width ?? undefined,\n height: numericHeight ?? size.height ?? undefined,\n } as const;\n\n if (!hasSrcSet) {\n return <img {...imgProps} />;\n }\n\n return (\n <picture ref={pictureRef}>\n {srcset.avif && <source type=\"image/avif\" srcSet={srcset.avif} sizes={sizesAttr} />}\n {srcset.webp && <source type=\"image/webp\" srcSet={srcset.webp} sizes={sizesAttr} />}\n <img {...imgProps} srcSet={inlineSrcSet} sizes={inlineSrcSet ? sizesAttr : undefined} />\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","width","height","fetchPriority","intersectionMargin","intersectionThreshold","useDebugMode","forwardedRef","restProps","imgRef","useRef","pictureRef","ref","current","HTMLPictureElement","parentElement","addEventListener","removeEventListener","normalizedSrc","useMemo","trim","numericWidth","numericHeight","resolvedOptixConfig","isEagerLoad","hookOptions","eager","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","useComposeRefs","resolvedFetchPriority","el","imgSrc","hasSrcSet","Boolean","inlineSrcSet","sizesAttr","enabled","eagerLoad","transparentPixel","logKeyRef","logKey","console","info","useImgDebugLog","imgProps","React","createElement","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]}
package/dist/core/Img.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { forwardRef, memo, useCallback, useMemo, useRef } from "react";
3
+ import { forwardRef, memo, useCallback, useEffect, useMemo, useRef } from "react";
4
4
  import { useOptimizedImage } from "@page-speed/hooks/media";
5
5
  import { useImgDebugLog } from "./useImgDebugLog.js";
6
6
  import { useMediaSelectionEffect } from "./useMediaSelectionEffect.js";
@@ -33,32 +33,32 @@ const parseDimension = (value) => {
33
33
  }
34
34
  return undefined;
35
35
  };
36
- const composeRefs = (hookRef, forwardedRef, localRef) => useCallback((node) => {
37
- hookRef(node);
38
- // eslint-disable-next-line no-param-reassign
39
- localRef.current = node;
40
- if (typeof forwardedRef === "function") {
41
- forwardedRef(node);
42
- }
43
- else if (forwardedRef && typeof forwardedRef === "object") {
44
- forwardedRef.current = node;
45
- }
46
- }, [hookRef, forwardedRef, localRef]);
47
- const ModernImg = ({ sizes, loading, decoding, alt, title, src: directSrc, eager, width, height, fetchPriority, intersectionMargin, intersectionThreshold, optixFlowConfig, useDebugMode, forwardedRef, ...restProps }) => {
36
+ /** Merges the hook ref, the forwarded ref, and a local ref into a single callback ref. */
37
+ function useComposeRefs(hookRef, forwardedRef, localRef) {
38
+ return useCallback((node) => {
39
+ hookRef(node);
40
+ localRef.current = node;
41
+ if (typeof forwardedRef === "function") {
42
+ forwardedRef(node);
43
+ }
44
+ else if (forwardedRef && typeof forwardedRef === "object") {
45
+ forwardedRef.current = node;
46
+ }
47
+ }, [hookRef, forwardedRef, localRef]);
48
+ }
49
+ const ModernImg = ({ sizes, loading, decoding, alt, title, src: directSrc, width, height, fetchPriority, intersectionMargin, intersectionThreshold, optixFlowConfig, useDebugMode, forwardedRef, ...restProps }) => {
48
50
  const imgRef = useRef(null);
49
51
  const pictureRef = useRef(null);
50
52
  useResponsiveReset(pictureRef);
51
53
  useMediaSelectionEffect();
52
54
  const normalizedSrc = useMemo(() => (typeof directSrc === "string" ? directSrc.trim() : ""), [directSrc]);
53
- const numericWidth = useMemo(() => parseDimension(width), [width]);
54
- const numericHeight = useMemo(() => parseDimension(height), [height]);
55
+ const numericWidth = parseDimension(width);
56
+ const numericHeight = parseDimension(height);
55
57
  const resolvedOptixConfig = useMemo(() => resolveOptixFlowConfig(optixFlowConfig), [optixFlowConfig]);
56
- const eagerLoad = useMemo(() => {
57
- return eager ?? loading === "eager";
58
- }, [eager, loading]);
58
+ const isEagerLoad = loading === "eager";
59
59
  const hookOptions = useMemo(() => ({
60
60
  src: normalizedSrc,
61
- eager: eagerLoad,
61
+ eager: isEagerLoad,
62
62
  width: numericWidth,
63
63
  height: numericHeight,
64
64
  rootMargin: intersectionMargin ?? "200px",
@@ -66,7 +66,7 @@ const ModernImg = ({ sizes, loading, decoding, alt, title, src: directSrc, eager
66
66
  optixFlowConfig: resolvedOptixConfig,
67
67
  }), [
68
68
  normalizedSrc,
69
- eagerLoad,
69
+ isEagerLoad,
70
70
  numericWidth,
71
71
  numericHeight,
72
72
  intersectionMargin,
@@ -74,47 +74,51 @@ const ModernImg = ({ sizes, loading, decoding, alt, title, src: directSrc, eager
74
74
  resolvedOptixConfig,
75
75
  ]);
76
76
  const { ref: hookRef, src, srcset, sizes: computedSizes, loading: hookLoading, isInView, size, } = useOptimizedImage(hookOptions);
77
- const mergedRef = composeRefs(hookRef, forwardedRef, imgRef);
78
- const sizesAttr = useMemo(() => {
79
- return sizes ?? (computedSizes || undefined);
80
- }, [sizes, computedSizes]);
81
- const loadingAttr = useMemo(() => {
82
- return loading ?? hookLoading ?? "lazy";
83
- }, [loading, hookLoading]);
84
- const decodingAttr = useMemo(() => {
85
- return decoding ?? "async";
86
- }, [decoding]);
87
- const fetchPriorityAttr = useMemo(() => {
88
- return fetchPriority ?? (eagerLoad ? "high" : undefined);
89
- }, [fetchPriority, eagerLoad]);
90
- const hasSrcSet = useMemo(() => {
91
- return Boolean(srcset.avif || srcset.webp || srcset.jpeg);
92
- }, [srcset.avif, srcset.webp, srcset.jpeg]);
93
- const imgSrc = useMemo(() => {
94
- return src || normalizedSrc || TRANSPARENT_PIXEL;
95
- }, [src, normalizedSrc]);
96
- const inlineSrcSet = useMemo(() => {
97
- return hasSrcSet && !srcset.avif && !srcset.webp ? srcset.jpeg : "";
98
- }, [hasSrcSet, srcset.avif, srcset.webp, srcset.jpeg]);
99
- const widthAttr = useMemo(() => {
100
- return numericWidth ?? (size.width || undefined);
101
- }, [numericWidth, size?.width]);
102
- const heightAttr = useMemo(() => {
103
- return numericHeight ?? (size.height || undefined);
104
- }, [numericHeight, size?.height]);
77
+ const mergedRef = useComposeRefs(hookRef, forwardedRef, imgRef);
78
+ // Compute fetchpriority: explicit prop wins, otherwise "high" for eager loads
79
+ const resolvedFetchPriority = fetchPriority ?? (isEagerLoad ? "high" : undefined);
80
+ // Apply fetchpriority via DOM API for React 18 compatibility
81
+ useEffect(() => {
82
+ const el = imgRef.current;
83
+ if (!el)
84
+ return;
85
+ if (resolvedFetchPriority) {
86
+ el.setAttribute("fetchpriority", resolvedFetchPriority);
87
+ }
88
+ else {
89
+ el.removeAttribute("fetchpriority");
90
+ }
91
+ }, [resolvedFetchPriority]);
92
+ // Derived values
93
+ const imgSrc = src || normalizedSrc || TRANSPARENT_PIXEL;
94
+ const hasSrcSet = Boolean(srcset.avif || srcset.webp || srcset.jpeg);
95
+ const inlineSrcSet = hasSrcSet && !srcset.avif && !srcset.webp ? srcset.jpeg : undefined;
96
+ const sizesAttr = sizes ?? computedSizes ?? undefined;
105
97
  useImgDebugLog({
106
98
  enabled: useDebugMode ?? false,
107
- eagerLoad,
99
+ eagerLoad: isEagerLoad,
108
100
  isInView,
109
101
  imgSrc,
110
102
  transparentPixel: TRANSPARENT_PIXEL,
111
103
  srcset,
112
104
  sizesAttr,
113
105
  });
106
+ // Shared img props for both render paths
107
+ const imgProps = {
108
+ ...restProps,
109
+ ref: mergedRef,
110
+ src: imgSrc,
111
+ loading: loading ?? hookLoading ?? "lazy",
112
+ decoding: decoding ?? "async",
113
+ alt,
114
+ title,
115
+ width: numericWidth ?? size.width ?? undefined,
116
+ height: numericHeight ?? size.height ?? undefined,
117
+ };
114
118
  if (!hasSrcSet) {
115
- return (_jsx("img", { ref: mergedRef, src: imgSrc, loading: loadingAttr, decoding: decodingAttr, fetchPriority: fetchPriorityAttr, alt: alt, title: title, width: widthAttr, height: heightAttr, ...restProps }));
119
+ return _jsx("img", { ...imgProps });
116
120
  }
117
- return (_jsxs("picture", { ref: pictureRef, children: [srcset.avif ? (_jsx("source", { type: "image/avif", srcSet: srcset.avif, sizes: sizesAttr })) : null, srcset.webp ? (_jsx("source", { type: "image/webp", srcSet: srcset.webp, sizes: sizesAttr })) : null, _jsx("img", { ref: mergedRef, src: imgSrc, srcSet: inlineSrcSet || undefined, sizes: inlineSrcSet ? sizesAttr : undefined, loading: loadingAttr, decoding: decodingAttr, fetchPriority: fetchPriorityAttr, alt: alt, title: title, width: widthAttr, height: heightAttr, ...restProps })] }));
121
+ return (_jsxs("picture", { ref: pictureRef, children: [srcset.avif && _jsx("source", { type: "image/avif", srcSet: srcset.avif, sizes: sizesAttr }), srcset.webp && _jsx("source", { type: "image/webp", srcSet: srcset.webp, sizes: sizesAttr }), _jsx("img", { ...imgProps, srcSet: inlineSrcSet, sizes: inlineSrcSet ? sizesAttr : undefined })] }));
118
122
  };
119
123
  const ImgBase = forwardRef(function Img(props, ref) {
120
124
  const hasSrc = typeof props.src === "string" && props.src.trim().length > 0;
@@ -1,13 +1,13 @@
1
1
  import React from "react";
2
2
  import type { UseOptimizedImageOptions } from "@page-speed/hooks/media";
3
- type NativeImgProps = Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src" | "srcSet" | "sizes"> & {
3
+ type NativeImgProps = Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src" | "srcSet" | "sizes" | "fetchPriority"> & {
4
4
  src?: string;
5
5
  };
6
6
  export type ImgProps = NativeImgProps & {
7
7
  /** Explicit sizes attribute (otherwise derived from useOptimizedImage) */
8
8
  sizes?: string;
9
- /** Force eager load (alias for loading="eager") */
10
- eager?: boolean;
9
+ /** Fetch priority hint maps to the HTML `fetchpriority` attribute */
10
+ fetchPriority?: "high" | "low" | "auto";
11
11
  /** Intersection observer threshold for lazy loading */
12
12
  intersectionThreshold?: number;
13
13
  /** Intersection observer root margin for lazy loading */
@@ -18,13 +18,13 @@ export type ImgProps = NativeImgProps & {
18
18
  useDebugMode?: boolean;
19
19
  };
20
20
  export declare const setDefaultOptixFlowConfig: (config?: UseOptimizedImageOptions["optixFlowConfig"] | null) => void;
21
- export declare const Img: React.MemoExoticComponent<React.ForwardRefExoticComponent<Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src" | "srcSet" | "sizes"> & {
21
+ export declare const Img: React.MemoExoticComponent<React.ForwardRefExoticComponent<Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src" | "srcSet" | "sizes" | "fetchPriority"> & {
22
22
  src?: string;
23
23
  } & {
24
24
  /** Explicit sizes attribute (otherwise derived from useOptimizedImage) */
25
25
  sizes?: string;
26
- /** Force eager load (alias for loading="eager") */
27
- eager?: boolean;
26
+ /** Fetch priority hint maps to the HTML `fetchpriority` attribute */
27
+ fetchPriority?: "high" | "low" | "auto";
28
28
  /** Intersection observer threshold for lazy loading */
29
29
  intersectionThreshold?: number;
30
30
  /** Intersection observer root margin for lazy loading */
package/dist/core/Img.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { forwardRef, memo, useCallback, useMemo, useRef } from "react";
3
+ import { forwardRef, memo, useCallback, useEffect, useMemo, useRef } from "react";
4
4
  import { useOptimizedImage } from "@page-speed/hooks/media";
5
5
  import { useImgDebugLog } from "./useImgDebugLog.js";
6
6
  import { useMediaSelectionEffect } from "./useMediaSelectionEffect.js";
@@ -33,32 +33,32 @@ const parseDimension = (value) => {
33
33
  }
34
34
  return undefined;
35
35
  };
36
- const composeRefs = (hookRef, forwardedRef, localRef) => useCallback((node) => {
37
- hookRef(node);
38
- // eslint-disable-next-line no-param-reassign
39
- localRef.current = node;
40
- if (typeof forwardedRef === "function") {
41
- forwardedRef(node);
42
- }
43
- else if (forwardedRef && typeof forwardedRef === "object") {
44
- forwardedRef.current = node;
45
- }
46
- }, [hookRef, forwardedRef, localRef]);
47
- const ModernImg = ({ sizes, loading, decoding, alt, title, src: directSrc, eager, width, height, fetchPriority, intersectionMargin, intersectionThreshold, optixFlowConfig, useDebugMode, forwardedRef, ...restProps }) => {
36
+ /** Merges the hook ref, the forwarded ref, and a local ref into a single callback ref. */
37
+ function useComposeRefs(hookRef, forwardedRef, localRef) {
38
+ return useCallback((node) => {
39
+ hookRef(node);
40
+ localRef.current = node;
41
+ if (typeof forwardedRef === "function") {
42
+ forwardedRef(node);
43
+ }
44
+ else if (forwardedRef && typeof forwardedRef === "object") {
45
+ forwardedRef.current = node;
46
+ }
47
+ }, [hookRef, forwardedRef, localRef]);
48
+ }
49
+ const ModernImg = ({ sizes, loading, decoding, alt, title, src: directSrc, width, height, fetchPriority, intersectionMargin, intersectionThreshold, optixFlowConfig, useDebugMode, forwardedRef, ...restProps }) => {
48
50
  const imgRef = useRef(null);
49
51
  const pictureRef = useRef(null);
50
52
  useResponsiveReset(pictureRef);
51
53
  useMediaSelectionEffect();
52
54
  const normalizedSrc = useMemo(() => (typeof directSrc === "string" ? directSrc.trim() : ""), [directSrc]);
53
- const numericWidth = useMemo(() => parseDimension(width), [width]);
54
- const numericHeight = useMemo(() => parseDimension(height), [height]);
55
+ const numericWidth = parseDimension(width);
56
+ const numericHeight = parseDimension(height);
55
57
  const resolvedOptixConfig = useMemo(() => resolveOptixFlowConfig(optixFlowConfig), [optixFlowConfig]);
56
- const eagerLoad = useMemo(() => {
57
- return eager ?? loading === "eager";
58
- }, [eager, loading]);
58
+ const isEagerLoad = loading === "eager";
59
59
  const hookOptions = useMemo(() => ({
60
60
  src: normalizedSrc,
61
- eager: eagerLoad,
61
+ eager: isEagerLoad,
62
62
  width: numericWidth,
63
63
  height: numericHeight,
64
64
  rootMargin: intersectionMargin ?? "200px",
@@ -66,7 +66,7 @@ const ModernImg = ({ sizes, loading, decoding, alt, title, src: directSrc, eager
66
66
  optixFlowConfig: resolvedOptixConfig,
67
67
  }), [
68
68
  normalizedSrc,
69
- eagerLoad,
69
+ isEagerLoad,
70
70
  numericWidth,
71
71
  numericHeight,
72
72
  intersectionMargin,
@@ -74,47 +74,51 @@ const ModernImg = ({ sizes, loading, decoding, alt, title, src: directSrc, eager
74
74
  resolvedOptixConfig,
75
75
  ]);
76
76
  const { ref: hookRef, src, srcset, sizes: computedSizes, loading: hookLoading, isInView, size, } = useOptimizedImage(hookOptions);
77
- const mergedRef = composeRefs(hookRef, forwardedRef, imgRef);
78
- const sizesAttr = useMemo(() => {
79
- return sizes ?? (computedSizes || undefined);
80
- }, [sizes, computedSizes]);
81
- const loadingAttr = useMemo(() => {
82
- return loading ?? hookLoading ?? "lazy";
83
- }, [loading, hookLoading]);
84
- const decodingAttr = useMemo(() => {
85
- return decoding ?? "async";
86
- }, [decoding]);
87
- const fetchPriorityAttr = useMemo(() => {
88
- return fetchPriority ?? (eagerLoad ? "high" : undefined);
89
- }, [fetchPriority, eagerLoad]);
90
- const hasSrcSet = useMemo(() => {
91
- return Boolean(srcset.avif || srcset.webp || srcset.jpeg);
92
- }, [srcset.avif, srcset.webp, srcset.jpeg]);
93
- const imgSrc = useMemo(() => {
94
- return src || normalizedSrc || TRANSPARENT_PIXEL;
95
- }, [src, normalizedSrc]);
96
- const inlineSrcSet = useMemo(() => {
97
- return hasSrcSet && !srcset.avif && !srcset.webp ? srcset.jpeg : "";
98
- }, [hasSrcSet, srcset.avif, srcset.webp, srcset.jpeg]);
99
- const widthAttr = useMemo(() => {
100
- return numericWidth ?? (size.width || undefined);
101
- }, [numericWidth, size?.width]);
102
- const heightAttr = useMemo(() => {
103
- return numericHeight ?? (size.height || undefined);
104
- }, [numericHeight, size?.height]);
77
+ const mergedRef = useComposeRefs(hookRef, forwardedRef, imgRef);
78
+ // Compute fetchpriority: explicit prop wins, otherwise "high" for eager loads
79
+ const resolvedFetchPriority = fetchPriority ?? (isEagerLoad ? "high" : undefined);
80
+ // Apply fetchpriority via DOM API for React 18 compatibility
81
+ useEffect(() => {
82
+ const el = imgRef.current;
83
+ if (!el)
84
+ return;
85
+ if (resolvedFetchPriority) {
86
+ el.setAttribute("fetchpriority", resolvedFetchPriority);
87
+ }
88
+ else {
89
+ el.removeAttribute("fetchpriority");
90
+ }
91
+ }, [resolvedFetchPriority]);
92
+ // Derived values
93
+ const imgSrc = src || normalizedSrc || TRANSPARENT_PIXEL;
94
+ const hasSrcSet = Boolean(srcset.avif || srcset.webp || srcset.jpeg);
95
+ const inlineSrcSet = hasSrcSet && !srcset.avif && !srcset.webp ? srcset.jpeg : undefined;
96
+ const sizesAttr = sizes ?? computedSizes ?? undefined;
105
97
  useImgDebugLog({
106
98
  enabled: useDebugMode ?? false,
107
- eagerLoad,
99
+ eagerLoad: isEagerLoad,
108
100
  isInView,
109
101
  imgSrc,
110
102
  transparentPixel: TRANSPARENT_PIXEL,
111
103
  srcset,
112
104
  sizesAttr,
113
105
  });
106
+ // Shared img props for both render paths
107
+ const imgProps = {
108
+ ...restProps,
109
+ ref: mergedRef,
110
+ src: imgSrc,
111
+ loading: loading ?? hookLoading ?? "lazy",
112
+ decoding: decoding ?? "async",
113
+ alt,
114
+ title,
115
+ width: numericWidth ?? size.width ?? undefined,
116
+ height: numericHeight ?? size.height ?? undefined,
117
+ };
114
118
  if (!hasSrcSet) {
115
- return (_jsx("img", { ref: mergedRef, src: imgSrc, loading: loadingAttr, decoding: decodingAttr, fetchPriority: fetchPriorityAttr, alt: alt, title: title, width: widthAttr, height: heightAttr, ...restProps }));
119
+ return _jsx("img", { ...imgProps });
116
120
  }
117
- return (_jsxs("picture", { ref: pictureRef, children: [srcset.avif ? (_jsx("source", { type: "image/avif", srcSet: srcset.avif, sizes: sizesAttr })) : null, srcset.webp ? (_jsx("source", { type: "image/webp", srcSet: srcset.webp, sizes: sizesAttr })) : null, _jsx("img", { ref: mergedRef, src: imgSrc, srcSet: inlineSrcSet || undefined, sizes: inlineSrcSet ? sizesAttr : undefined, loading: loadingAttr, decoding: decodingAttr, fetchPriority: fetchPriorityAttr, alt: alt, title: title, width: widthAttr, height: heightAttr, ...restProps })] }));
121
+ return (_jsxs("picture", { ref: pictureRef, children: [srcset.avif && _jsx("source", { type: "image/avif", srcSet: srcset.avif, sizes: sizesAttr }), srcset.webp && _jsx("source", { type: "image/webp", srcSet: srcset.webp, sizes: sizesAttr }), _jsx("img", { ...imgProps, srcSet: inlineSrcSet, sizes: inlineSrcSet ? sizesAttr : undefined })] }));
118
122
  };
119
123
  const ImgBase = forwardRef(function Img(props, ref) {
120
124
  const hasSrc = typeof props.src === "string" && props.src.trim().length > 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
+ }
@@ -1,2 +1,2 @@
1
1
  export { Img, setDefaultOptixFlowConfig } from "./Img.js";
2
- export { OptixFlowConfig } from "./OptixFlowConfig.js";
2
+ export { ImgDefaults } from "./ImgDefaults.js";
@@ -1,4 +1,4 @@
1
1
  export { Img, setDefaultOptixFlowConfig } from "./Img.js";
2
2
  export type { ImgProps } from "./Img.js";
3
- export { OptixFlowConfig } from "./OptixFlowConfig.js";
4
- export type { OptixFlowConfigProps } from "./OptixFlowConfig.js";
3
+ export { ImgDefaults } from "./ImgDefaults.js";
4
+ export type { ImgDefaultsProps } from "./ImgDefaults.js";
@@ -1,2 +1,2 @@
1
1
  export { Img, setDefaultOptixFlowConfig } from "./Img.js";
2
- export { OptixFlowConfig } from "./OptixFlowConfig.js";
2
+ export { ImgDefaults } from "./ImgDefaults.js";
package/dist/index.cjs CHANGED
@@ -1,17 +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";
16
19
  // Re-export specific items for clarity and CDN usage
17
- export { Img, setDefaultOptixFlowConfig, OptixFlowConfig } from './core/index.js';
20
+ export { Img, setDefaultOptixFlowConfig, ImgDefaults } from "./core/index.js";
package/dist/index.d.ts CHANGED
@@ -1,6 +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'];
5
- export { Img, setDefaultOptixFlowConfig, OptixFlowConfig } from './core/index.js';
6
- export type { ImgProps, OptixFlowConfigProps } from './core/index.js';
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,17 +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";
16
19
  // Re-export specific items for clarity and CDN usage
17
- export { Img, setDefaultOptixFlowConfig, OptixFlowConfig } from './core/index.js';
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.7",
3
+ "version": "0.4.9",
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",