@inoo-ch/payload-image-optimizer 1.4.6 → 1.4.7
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/AGENT_DOCS.md +40 -5
- package/README.md +12 -4
- package/dist/components/FadeImage.d.ts +23 -0
- package/dist/components/FadeImage.js +34 -0
- package/dist/components/FadeImage.js.map +1 -0
- package/dist/components/ImageBox.d.ts +4 -0
- package/dist/components/ImageBox.js +13 -4
- package/dist/components/ImageBox.js.map +1 -1
- package/dist/exports/client.d.ts +2 -0
- package/dist/exports/client.js +1 -0
- package/dist/exports/client.js.map +1 -1
- package/package.json +1 -1
- package/src/components/FadeImage.tsx +51 -0
- package/src/components/ImageBox.tsx +19 -3
- package/src/exports/client.ts +2 -0
package/AGENT_DOCS.md
CHANGED
|
@@ -219,7 +219,7 @@ Import from `@inoo-ch/payload-image-optimizer/client`:
|
|
|
219
219
|
|
|
220
220
|
### `ImageBox` Component
|
|
221
221
|
|
|
222
|
-
Drop-in Next.js `<Image>` wrapper with automatic ThumbHash blur placeholders
|
|
222
|
+
Drop-in Next.js `<Image>` wrapper with automatic ThumbHash blur placeholders, focal point support, and smooth fade-in transition.
|
|
223
223
|
|
|
224
224
|
```tsx
|
|
225
225
|
import { ImageBox } from '@inoo-ch/payload-image-optimizer/client'
|
|
@@ -229,21 +229,55 @@ import { ImageBox } from '@inoo-ch/payload-image-optimizer/client'
|
|
|
229
229
|
|
|
230
230
|
// With a plain URL string
|
|
231
231
|
<ImageBox media="/images/fallback.jpg" alt="Fallback" width={800} height={600} />
|
|
232
|
+
|
|
233
|
+
// Disable fade animation
|
|
234
|
+
<ImageBox media={doc.image} alt="Photo" fade={false} />
|
|
235
|
+
|
|
236
|
+
// Custom fade duration
|
|
237
|
+
<ImageBox media={doc.image} alt="Photo" fadeDuration={300} />
|
|
232
238
|
```
|
|
233
239
|
|
|
234
240
|
**Props:** Extends all Next.js `ImageProps` (except `src`), plus:
|
|
235
241
|
|
|
236
|
-
| Prop | Type | Description |
|
|
237
|
-
|
|
238
|
-
| `media` | `MediaResource \| string` | Payload media document or URL string |
|
|
239
|
-
| `alt` | `string` | Alt text (overrides `media.alt`) |
|
|
242
|
+
| Prop | Type | Default | Description |
|
|
243
|
+
|------|------|---------|-------------|
|
|
244
|
+
| `media` | `MediaResource \| string` | — | Payload media document or URL string |
|
|
245
|
+
| `alt` | `string` | — | Alt text (overrides `media.alt`) |
|
|
246
|
+
| `fade` | `boolean` | `true` | Enable smooth blur-to-sharp fade transition on load |
|
|
247
|
+
| `fadeDuration` | `number` | `500` | Duration of the fade animation in milliseconds |
|
|
240
248
|
|
|
241
249
|
Automatically applies:
|
|
242
250
|
- ThumbHash blur placeholder (if available on the media resource)
|
|
251
|
+
- Smooth blur-to-sharp fade transition on image load (disable with `fade={false}`)
|
|
243
252
|
- Focal point positioning via `objectPosition` (using `focalX`/`focalY`)
|
|
244
253
|
- Cache-busting via `updatedAt` query parameter
|
|
245
254
|
- `objectFit: 'cover'` by default (overridable via `style`)
|
|
246
255
|
|
|
256
|
+
### `FadeImage` Component
|
|
257
|
+
|
|
258
|
+
Standalone Next.js `<Image>` wrapper with fade-in transition for use with `getImageOptimizerProps()`. Use this when you have a custom image component and want the fade effect without `ImageBox`.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
import { FadeImage, getImageOptimizerProps } from '@inoo-ch/payload-image-optimizer/client'
|
|
262
|
+
|
|
263
|
+
const optimizerProps = getImageOptimizerProps(resource)
|
|
264
|
+
|
|
265
|
+
<FadeImage
|
|
266
|
+
src={resource.url}
|
|
267
|
+
alt=""
|
|
268
|
+
width={800}
|
|
269
|
+
height={600}
|
|
270
|
+
optimizerProps={optimizerProps}
|
|
271
|
+
/>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Props:** Extends all Next.js `ImageProps` (except `placeholder`, `blurDataURL`, `onLoad`), plus:
|
|
275
|
+
|
|
276
|
+
| Prop | Type | Default | Description |
|
|
277
|
+
|------|------|---------|-------------|
|
|
278
|
+
| `optimizerProps` | `ImageOptimizerProps` | — | Props returned by `getImageOptimizerProps()` |
|
|
279
|
+
| `fadeDuration` | `number` | `500` | Duration of the fade animation in milliseconds |
|
|
280
|
+
|
|
247
281
|
### `getImageOptimizerProps()` Utility
|
|
248
282
|
|
|
249
283
|
For integrating with existing image components (e.g., the Payload website template's `ImageMedia`):
|
|
@@ -360,6 +394,7 @@ import type {
|
|
|
360
394
|
|
|
361
395
|
import type {
|
|
362
396
|
ImageBoxProps,
|
|
397
|
+
FadeImageProps,
|
|
363
398
|
ImageOptimizerProps, // return type of getImageOptimizerProps
|
|
364
399
|
} from '@inoo-ch/payload-image-optimizer/client'
|
|
365
400
|
```
|
package/README.md
CHANGED
|
@@ -17,7 +17,8 @@ Built and maintained by [inoo.ch](https://inoo.ch) — a Swiss digital agency cr
|
|
|
17
17
|
- **Bulk regeneration** — Re-process existing images from the admin UI with progress tracking
|
|
18
18
|
- **Per-collection config** — Override formats, quality, and dimensions per collection
|
|
19
19
|
- **Admin UI** — Status badges, file size savings, and blur previews in the sidebar
|
|
20
|
-
- **ImageBox component** — Drop-in Next.js `<Image>` wrapper with automatic ThumbHash blur
|
|
20
|
+
- **ImageBox component** — Drop-in Next.js `<Image>` wrapper with automatic ThumbHash blur and smooth fade-in
|
|
21
|
+
- **FadeImage component** — Standalone fade-in image for custom setups using `getImageOptimizerProps()`
|
|
21
22
|
|
|
22
23
|
## Requirements
|
|
23
24
|
|
|
@@ -146,7 +147,7 @@ Payload CMS ships with [sharp](https://sharp.pixelplumbing.com/) built-in and ca
|
|
|
146
147
|
| Blur hash placeholders | Requires custom hooks | ThumbHash generated automatically |
|
|
147
148
|
| Optimization status & savings | Not available | Admin sidebar panel per image |
|
|
148
149
|
| Bulk re-process existing images | Not available | One-click regeneration with progress tracking |
|
|
149
|
-
| Next.js `<Image>` with blur placeholder | Manual wiring | Drop-in `<ImageBox>`
|
|
150
|
+
| Next.js `<Image>` with blur placeholder | Manual wiring | Drop-in `<ImageBox>` / `<FadeImage>` components |
|
|
150
151
|
| Per-collection format/quality overrides | N/A | Supported |
|
|
151
152
|
|
|
152
153
|
### CPU & Resource Impact
|
|
@@ -171,7 +172,7 @@ A **Regenerate Images** button appears in collection list views, allowing you to
|
|
|
171
172
|
|
|
172
173
|
## ImageBox Component
|
|
173
174
|
|
|
174
|
-
The plugin exports an `ImageBox` component — a Next.js `<Image>` wrapper that automatically applies ThumbHash blur placeholders:
|
|
175
|
+
The plugin exports an `ImageBox` component — a Next.js `<Image>` wrapper that automatically applies ThumbHash blur placeholders with a smooth blur-to-sharp fade transition:
|
|
175
176
|
|
|
176
177
|
```tsx
|
|
177
178
|
import { ImageBox } from '@inoo-ch/payload-image-optimizer/client'
|
|
@@ -181,10 +182,17 @@ import { ImageBox } from '@inoo-ch/payload-image-optimizer/client'
|
|
|
181
182
|
|
|
182
183
|
// Or use a plain URL string
|
|
183
184
|
<ImageBox media="/images/photo.jpg" alt="Photo" width={800} height={600} />
|
|
185
|
+
|
|
186
|
+
// Disable fade animation
|
|
187
|
+
<ImageBox media={doc.image} alt="Photo" fade={false} />
|
|
188
|
+
|
|
189
|
+
// Custom fade duration (default: 500ms)
|
|
190
|
+
<ImageBox media={doc.image} alt="Photo" fadeDuration={300} />
|
|
184
191
|
```
|
|
185
192
|
|
|
186
193
|
**Features:**
|
|
187
194
|
- Automatic ThumbHash `blurDataURL` from the media document
|
|
195
|
+
- Smooth blur-to-sharp fade transition on load (enabled by default)
|
|
188
196
|
- Respects Payload focal point (`focalX` / `focalY`) for `objectPosition`
|
|
189
197
|
- Lazy loading by default, with `priority` prop for above-the-fold images
|
|
190
198
|
- Cache busting via `updatedAt` timestamp
|
|
@@ -254,7 +262,7 @@ Copy-paste this instruction to your AI coding agent to have it autonomously inte
|
|
|
254
262
|
>
|
|
255
263
|
> 1. Which upload collections should be optimized and with what settings
|
|
256
264
|
> 2. Whether to use `replaceOriginal` or keep originals alongside variants
|
|
257
|
-
> 3. Where to add `<ImageBox
|
|
265
|
+
> 3. Where to add `<ImageBox>`, `<FadeImage>`, or `getImageOptimizerProps()` in the frontend for ThumbHash blur placeholders with smooth fade-in and focal point support
|
|
258
266
|
> 4. Whether any existing image rendering code should use the optimized variants
|
|
259
267
|
>
|
|
260
268
|
> Use the zero-config default (`collections: { <slug>: true }`) unless the project has specific requirements that call for custom settings.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type ImageProps } from 'next/image';
|
|
3
|
+
import type { ImageOptimizerProps } from '../utilities/getImageOptimizerProps.js';
|
|
4
|
+
export interface FadeImageProps extends Omit<ImageProps, 'placeholder' | 'blurDataURL' | 'onLoad'> {
|
|
5
|
+
/** Props returned by `getImageOptimizerProps()`. */
|
|
6
|
+
optimizerProps: ImageOptimizerProps;
|
|
7
|
+
/** Duration of the fade animation in milliseconds. Defaults to `500`. */
|
|
8
|
+
fadeDuration?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A Next.js `<Image>` wrapper that applies ThumbHash blur placeholders with a
|
|
12
|
+
* smooth blur-to-sharp fade transition on load.
|
|
13
|
+
*
|
|
14
|
+
* Use this when you call `getImageOptimizerProps()` manually instead of using `ImageBox`:
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* import { FadeImage, getImageOptimizerProps } from '@inoo-ch/payload-image-optimizer/client'
|
|
18
|
+
*
|
|
19
|
+
* const optimizerProps = getImageOptimizerProps(resource)
|
|
20
|
+
* <FadeImage src={src} alt="" optimizerProps={optimizerProps} width={800} height={600} />
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare const FadeImage: React.FC<FadeImageProps>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import NextImage from 'next/image';
|
|
5
|
+
/**
|
|
6
|
+
* A Next.js `<Image>` wrapper that applies ThumbHash blur placeholders with a
|
|
7
|
+
* smooth blur-to-sharp fade transition on load.
|
|
8
|
+
*
|
|
9
|
+
* Use this when you call `getImageOptimizerProps()` manually instead of using `ImageBox`:
|
|
10
|
+
*
|
|
11
|
+
* ```tsx
|
|
12
|
+
* import { FadeImage, getImageOptimizerProps } from '@inoo-ch/payload-image-optimizer/client'
|
|
13
|
+
*
|
|
14
|
+
* const optimizerProps = getImageOptimizerProps(resource)
|
|
15
|
+
* <FadeImage src={src} alt="" optimizerProps={optimizerProps} width={800} height={600} />
|
|
16
|
+
* ```
|
|
17
|
+
*/ export const FadeImage = ({ optimizerProps, style, fadeDuration = 500, ...props })=>{
|
|
18
|
+
const [loaded, setLoaded] = useState(false);
|
|
19
|
+
const { blurDataURL, style: optimizerStyle } = optimizerProps;
|
|
20
|
+
return /*#__PURE__*/ _jsx(NextImage, {
|
|
21
|
+
...props,
|
|
22
|
+
placeholder: blurDataURL ? 'blur' : 'empty',
|
|
23
|
+
blurDataURL: blurDataURL,
|
|
24
|
+
style: {
|
|
25
|
+
...optimizerStyle,
|
|
26
|
+
...style,
|
|
27
|
+
filter: loaded ? 'blur(0px)' : 'blur(20px)',
|
|
28
|
+
transition: loaded ? `filter ${fadeDuration}ms ease-in-out` : undefined
|
|
29
|
+
},
|
|
30
|
+
onLoad: ()=>setLoaded(true)
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=FadeImage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/FadeImage.tsx"],"sourcesContent":["'use client'\n\nimport React, { useState } from 'react'\nimport NextImage, { type ImageProps } from 'next/image'\nimport type { ImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'\n\nexport interface FadeImageProps extends Omit<ImageProps, 'placeholder' | 'blurDataURL' | 'onLoad'> {\n /** Props returned by `getImageOptimizerProps()`. */\n optimizerProps: ImageOptimizerProps\n /** Duration of the fade animation in milliseconds. Defaults to `500`. */\n fadeDuration?: number\n}\n\n/**\n * A Next.js `<Image>` wrapper that applies ThumbHash blur placeholders with a\n * smooth blur-to-sharp fade transition on load.\n *\n * Use this when you call `getImageOptimizerProps()` manually instead of using `ImageBox`:\n *\n * ```tsx\n * import { FadeImage, getImageOptimizerProps } from '@inoo-ch/payload-image-optimizer/client'\n *\n * const optimizerProps = getImageOptimizerProps(resource)\n * <FadeImage src={src} alt=\"\" optimizerProps={optimizerProps} width={800} height={600} />\n * ```\n */\nexport const FadeImage: React.FC<FadeImageProps> = ({\n optimizerProps,\n style,\n fadeDuration = 500,\n ...props\n}) => {\n const [loaded, setLoaded] = useState(false)\n\n const { blurDataURL, style: optimizerStyle } = optimizerProps\n\n return (\n <NextImage\n {...props}\n placeholder={blurDataURL ? 'blur' : 'empty'}\n blurDataURL={blurDataURL}\n style={{\n ...optimizerStyle,\n ...style,\n filter: loaded ? 'blur(0px)' : 'blur(20px)',\n transition: loaded ? `filter ${fadeDuration}ms ease-in-out` : undefined,\n }}\n onLoad={() => setLoaded(true)}\n />\n )\n}\n"],"names":["React","useState","NextImage","FadeImage","optimizerProps","style","fadeDuration","props","loaded","setLoaded","blurDataURL","optimizerStyle","placeholder","filter","transition","undefined","onLoad"],"mappings":"AAAA;;AAEA,OAAOA,SAASC,QAAQ,QAAQ,QAAO;AACvC,OAAOC,eAAoC,aAAY;AAUvD;;;;;;;;;;;;CAYC,GACD,OAAO,MAAMC,YAAsC,CAAC,EAClDC,cAAc,EACdC,KAAK,EACLC,eAAe,GAAG,EAClB,GAAGC,OACJ;IACC,MAAM,CAACC,QAAQC,UAAU,GAAGR,SAAS;IAErC,MAAM,EAAES,WAAW,EAAEL,OAAOM,cAAc,EAAE,GAAGP;IAE/C,qBACE,KAACF;QACE,GAAGK,KAAK;QACTK,aAAaF,cAAc,SAAS;QACpCA,aAAaA;QACbL,OAAO;YACL,GAAGM,cAAc;YACjB,GAAGN,KAAK;YACRQ,QAAQL,SAAS,cAAc;YAC/BM,YAAYN,SAAS,CAAC,OAAO,EAAEF,aAAa,cAAc,CAAC,GAAGS;QAChE;QACAC,QAAQ,IAAMP,UAAU;;AAG9B,EAAC"}
|
|
@@ -4,5 +4,9 @@ import type { MediaResource } from '../types.js';
|
|
|
4
4
|
export interface ImageBoxProps extends Omit<ImageProps, 'src' | 'alt'> {
|
|
5
5
|
media: MediaResource | string;
|
|
6
6
|
alt?: string;
|
|
7
|
+
/** Enable smooth blur-to-sharp fade transition on load. Defaults to `true`. */
|
|
8
|
+
fade?: boolean;
|
|
9
|
+
/** Duration of the fade animation in milliseconds. Defaults to `500`. */
|
|
10
|
+
fadeDuration?: number;
|
|
7
11
|
}
|
|
8
12
|
export declare const ImageBox: React.FC<ImageBoxProps>;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
4
|
import NextImage from 'next/image';
|
|
5
5
|
import { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js';
|
|
6
|
-
export const ImageBox = ({ media, alt: altFromProps, fill, sizes, priority, loading: loadingFromProps, style: styleFromProps, ...props })=>{
|
|
6
|
+
export const ImageBox = ({ media, alt: altFromProps, fill, sizes, priority, loading: loadingFromProps, style: styleFromProps, fade = true, fadeDuration = 500, ...props })=>{
|
|
7
|
+
const [loaded, setLoaded] = useState(false);
|
|
7
8
|
const loading = priority ? undefined : loadingFromProps ?? 'lazy';
|
|
9
|
+
const fadeStyle = fade ? {
|
|
10
|
+
filter: loaded ? 'blur(0px)' : 'blur(20px)',
|
|
11
|
+
transition: loaded ? `filter ${fadeDuration}ms ease-in-out` : undefined
|
|
12
|
+
} : undefined;
|
|
8
13
|
if (typeof media === 'string') {
|
|
9
14
|
return /*#__PURE__*/ _jsx(NextImage, {
|
|
10
15
|
...props,
|
|
@@ -16,10 +21,12 @@ export const ImageBox = ({ media, alt: altFromProps, fill, sizes, priority, load
|
|
|
16
21
|
style: {
|
|
17
22
|
objectFit: 'cover',
|
|
18
23
|
objectPosition: 'center',
|
|
24
|
+
...fadeStyle,
|
|
19
25
|
...styleFromProps
|
|
20
26
|
},
|
|
21
27
|
priority: priority,
|
|
22
|
-
loading: loading
|
|
28
|
+
loading: loading,
|
|
29
|
+
onLoad: fade ? ()=>setLoaded(true) : undefined
|
|
23
30
|
});
|
|
24
31
|
}
|
|
25
32
|
const width = media.width ?? undefined;
|
|
@@ -39,12 +46,14 @@ export const ImageBox = ({ media, alt: altFromProps, fill, sizes, priority, load
|
|
|
39
46
|
style: {
|
|
40
47
|
objectFit: 'cover',
|
|
41
48
|
...optimizerProps.style,
|
|
49
|
+
...fadeStyle,
|
|
42
50
|
...styleFromProps
|
|
43
51
|
},
|
|
44
52
|
placeholder: optimizerProps.placeholder,
|
|
45
53
|
blurDataURL: optimizerProps.blurDataURL,
|
|
46
54
|
priority: priority,
|
|
47
|
-
loading: loading
|
|
55
|
+
loading: loading,
|
|
56
|
+
onLoad: fade ? ()=>setLoaded(true) : undefined
|
|
48
57
|
});
|
|
49
58
|
};
|
|
50
59
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/ImageBox.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\nimport NextImage, { type ImageProps } from 'next/image'\nimport type { MediaResource } from '../types.js'\nimport { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'\n\nexport interface ImageBoxProps extends Omit<ImageProps, 'src' | 'alt'> {\n media: MediaResource | string\n alt?: string\n}\n\nexport const ImageBox: React.FC<ImageBoxProps> = ({\n media,\n alt: altFromProps,\n fill,\n sizes,\n priority,\n loading: loadingFromProps,\n style: styleFromProps,\n ...props\n}) => {\n const loading = priority ? undefined : (loadingFromProps ?? 'lazy')\n\n if (typeof media === 'string') {\n return (\n <NextImage\n {...props}\n src={media}\n alt={altFromProps || ''}\n quality={80}\n fill={fill}\n sizes={sizes}\n style={{ objectFit: 'cover', objectPosition: 'center', ...styleFromProps }}\n priority={priority}\n loading={loading}\n />\n )\n }\n\n const width = media.width ?? undefined\n const height = media.height ?? undefined\n const alt = altFromProps || (media as any).alt || media.filename || ''\n const src = media.url ? `${media.url}${media.updatedAt ? `?${media.updatedAt}` : ''}` : ''\n\n const optimizerProps = getImageOptimizerProps(media)\n\n return (\n <NextImage\n {...props}\n src={src}\n alt={alt}\n quality={80}\n fill={fill}\n width={!fill ? width : undefined}\n height={!fill ? height : undefined}\n sizes={sizes}\n style={{ objectFit: 'cover', ...optimizerProps.style, ...styleFromProps }}\n placeholder={optimizerProps.placeholder}\n blurDataURL={optimizerProps.blurDataURL}\n priority={priority}\n loading={loading}\n />\n )\n}\n"],"names":["React","NextImage","getImageOptimizerProps","ImageBox","media","alt","altFromProps","fill","sizes","priority","loading","loadingFromProps","style","styleFromProps","props","undefined","src","quality","objectFit","objectPosition","width","height","filename","url","updatedAt","optimizerProps","placeholder","blurDataURL"],"mappings":"AAAA;;AAEA,OAAOA,
|
|
1
|
+
{"version":3,"sources":["../../src/components/ImageBox.tsx"],"sourcesContent":["'use client'\n\nimport React, { useState } from 'react'\nimport NextImage, { type ImageProps } from 'next/image'\nimport type { MediaResource } from '../types.js'\nimport { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'\n\nexport interface ImageBoxProps extends Omit<ImageProps, 'src' | 'alt'> {\n media: MediaResource | string\n alt?: string\n /** Enable smooth blur-to-sharp fade transition on load. Defaults to `true`. */\n fade?: boolean\n /** Duration of the fade animation in milliseconds. Defaults to `500`. */\n fadeDuration?: number\n}\n\nexport const ImageBox: React.FC<ImageBoxProps> = ({\n media,\n alt: altFromProps,\n fill,\n sizes,\n priority,\n loading: loadingFromProps,\n style: styleFromProps,\n fade = true,\n fadeDuration = 500,\n ...props\n}) => {\n const [loaded, setLoaded] = useState(false)\n const loading = priority ? undefined : (loadingFromProps ?? 'lazy')\n\n const fadeStyle = fade\n ? {\n filter: loaded ? 'blur(0px)' : 'blur(20px)',\n transition: loaded ? `filter ${fadeDuration}ms ease-in-out` : undefined,\n }\n : undefined\n\n if (typeof media === 'string') {\n return (\n <NextImage\n {...props}\n src={media}\n alt={altFromProps || ''}\n quality={80}\n fill={fill}\n sizes={sizes}\n style={{ objectFit: 'cover', objectPosition: 'center', ...fadeStyle, ...styleFromProps }}\n priority={priority}\n loading={loading}\n onLoad={fade ? () => setLoaded(true) : undefined}\n />\n )\n }\n\n const width = media.width ?? undefined\n const height = media.height ?? undefined\n const alt = altFromProps || (media as any).alt || media.filename || ''\n const src = media.url ? `${media.url}${media.updatedAt ? `?${media.updatedAt}` : ''}` : ''\n\n const optimizerProps = getImageOptimizerProps(media)\n\n return (\n <NextImage\n {...props}\n src={src}\n alt={alt}\n quality={80}\n fill={fill}\n width={!fill ? width : undefined}\n height={!fill ? height : undefined}\n sizes={sizes}\n style={{ objectFit: 'cover', ...optimizerProps.style, ...fadeStyle, ...styleFromProps }}\n placeholder={optimizerProps.placeholder}\n blurDataURL={optimizerProps.blurDataURL}\n priority={priority}\n loading={loading}\n onLoad={fade ? () => setLoaded(true) : undefined}\n />\n )\n}\n"],"names":["React","useState","NextImage","getImageOptimizerProps","ImageBox","media","alt","altFromProps","fill","sizes","priority","loading","loadingFromProps","style","styleFromProps","fade","fadeDuration","props","loaded","setLoaded","undefined","fadeStyle","filter","transition","src","quality","objectFit","objectPosition","onLoad","width","height","filename","url","updatedAt","optimizerProps","placeholder","blurDataURL"],"mappings":"AAAA;;AAEA,OAAOA,SAASC,QAAQ,QAAQ,QAAO;AACvC,OAAOC,eAAoC,aAAY;AAEvD,SAASC,sBAAsB,QAAQ,yCAAwC;AAW/E,OAAO,MAAMC,WAAoC,CAAC,EAChDC,KAAK,EACLC,KAAKC,YAAY,EACjBC,IAAI,EACJC,KAAK,EACLC,QAAQ,EACRC,SAASC,gBAAgB,EACzBC,OAAOC,cAAc,EACrBC,OAAO,IAAI,EACXC,eAAe,GAAG,EAClB,GAAGC,OACJ;IACC,MAAM,CAACC,QAAQC,UAAU,GAAGlB,SAAS;IACrC,MAAMU,UAAUD,WAAWU,YAAaR,oBAAoB;IAE5D,MAAMS,YAAYN,OACd;QACEO,QAAQJ,SAAS,cAAc;QAC/BK,YAAYL,SAAS,CAAC,OAAO,EAAEF,aAAa,cAAc,CAAC,GAAGI;IAChE,IACAA;IAEJ,IAAI,OAAOf,UAAU,UAAU;QAC7B,qBACE,KAACH;YACE,GAAGe,KAAK;YACTO,KAAKnB;YACLC,KAAKC,gBAAgB;YACrBkB,SAAS;YACTjB,MAAMA;YACNC,OAAOA;YACPI,OAAO;gBAAEa,WAAW;gBAASC,gBAAgB;gBAAU,GAAGN,SAAS;gBAAE,GAAGP,cAAc;YAAC;YACvFJ,UAAUA;YACVC,SAASA;YACTiB,QAAQb,OAAO,IAAMI,UAAU,QAAQC;;IAG7C;IAEA,MAAMS,QAAQxB,MAAMwB,KAAK,IAAIT;IAC7B,MAAMU,SAASzB,MAAMyB,MAAM,IAAIV;IAC/B,MAAMd,MAAMC,gBAAgB,AAACF,MAAcC,GAAG,IAAID,MAAM0B,QAAQ,IAAI;IACpE,MAAMP,MAAMnB,MAAM2B,GAAG,GAAG,GAAG3B,MAAM2B,GAAG,GAAG3B,MAAM4B,SAAS,GAAG,CAAC,CAAC,EAAE5B,MAAM4B,SAAS,EAAE,GAAG,IAAI,GAAG;IAExF,MAAMC,iBAAiB/B,uBAAuBE;IAE9C,qBACE,KAACH;QACE,GAAGe,KAAK;QACTO,KAAKA;QACLlB,KAAKA;QACLmB,SAAS;QACTjB,MAAMA;QACNqB,OAAO,CAACrB,OAAOqB,QAAQT;QACvBU,QAAQ,CAACtB,OAAOsB,SAASV;QACzBX,OAAOA;QACPI,OAAO;YAAEa,WAAW;YAAS,GAAGQ,eAAerB,KAAK;YAAE,GAAGQ,SAAS;YAAE,GAAGP,cAAc;QAAC;QACtFqB,aAAaD,eAAeC,WAAW;QACvCC,aAAaF,eAAeE,WAAW;QACvC1B,UAAUA;QACVC,SAASA;QACTiB,QAAQb,OAAO,IAAMI,UAAU,QAAQC;;AAG7C,EAAC"}
|
package/dist/exports/client.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { OptimizationStatus } from '../components/OptimizationStatus.js';
|
|
2
2
|
export { ImageBox } from '../components/ImageBox.js';
|
|
3
3
|
export type { ImageBoxProps } from '../components/ImageBox.js';
|
|
4
|
+
export { FadeImage } from '../components/FadeImage.js';
|
|
5
|
+
export type { FadeImageProps } from '../components/FadeImage.js';
|
|
4
6
|
export { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js';
|
|
5
7
|
export type { ImageOptimizerProps } from '../utilities/getImageOptimizerProps.js';
|
|
6
8
|
export { RegenerationButton } from '../components/RegenerationButton.js';
|
package/dist/exports/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { OptimizationStatus } from '../components/OptimizationStatus.js';
|
|
2
2
|
export { ImageBox } from '../components/ImageBox.js';
|
|
3
|
+
export { FadeImage } from '../components/FadeImage.js';
|
|
3
4
|
export { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js';
|
|
4
5
|
export { RegenerationButton } from '../components/RegenerationButton.js';
|
|
5
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { OptimizationStatus } from '../components/OptimizationStatus.js'\nexport { ImageBox } from '../components/ImageBox.js'\nexport type { ImageBoxProps } from '../components/ImageBox.js'\nexport { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'\nexport type { ImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'\nexport { RegenerationButton } from '../components/RegenerationButton.js'\n"],"names":["OptimizationStatus","ImageBox","getImageOptimizerProps","RegenerationButton"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,sCAAqC;AACxE,SAASC,QAAQ,QAAQ,4BAA2B;AAEpD,SAASC,sBAAsB,QAAQ,yCAAwC;AAE/E,SAASC,kBAAkB,QAAQ,sCAAqC"}
|
|
1
|
+
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { OptimizationStatus } from '../components/OptimizationStatus.js'\nexport { ImageBox } from '../components/ImageBox.js'\nexport type { ImageBoxProps } from '../components/ImageBox.js'\nexport { FadeImage } from '../components/FadeImage.js'\nexport type { FadeImageProps } from '../components/FadeImage.js'\nexport { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'\nexport type { ImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'\nexport { RegenerationButton } from '../components/RegenerationButton.js'\n"],"names":["OptimizationStatus","ImageBox","FadeImage","getImageOptimizerProps","RegenerationButton"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,sCAAqC;AACxE,SAASC,QAAQ,QAAQ,4BAA2B;AAEpD,SAASC,SAAS,QAAQ,6BAA4B;AAEtD,SAASC,sBAAsB,QAAQ,yCAAwC;AAE/E,SAASC,kBAAkB,QAAQ,sCAAqC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inoo-ch/payload-image-optimizer",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.7",
|
|
4
4
|
"description": "Payload CMS plugin for automatic image optimization — WebP/AVIF conversion, resize, EXIF strip, ThumbHash placeholders, and bulk regeneration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
import NextImage, { type ImageProps } from 'next/image'
|
|
5
|
+
import type { ImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'
|
|
6
|
+
|
|
7
|
+
export interface FadeImageProps extends Omit<ImageProps, 'placeholder' | 'blurDataURL' | 'onLoad'> {
|
|
8
|
+
/** Props returned by `getImageOptimizerProps()`. */
|
|
9
|
+
optimizerProps: ImageOptimizerProps
|
|
10
|
+
/** Duration of the fade animation in milliseconds. Defaults to `500`. */
|
|
11
|
+
fadeDuration?: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A Next.js `<Image>` wrapper that applies ThumbHash blur placeholders with a
|
|
16
|
+
* smooth blur-to-sharp fade transition on load.
|
|
17
|
+
*
|
|
18
|
+
* Use this when you call `getImageOptimizerProps()` manually instead of using `ImageBox`:
|
|
19
|
+
*
|
|
20
|
+
* ```tsx
|
|
21
|
+
* import { FadeImage, getImageOptimizerProps } from '@inoo-ch/payload-image-optimizer/client'
|
|
22
|
+
*
|
|
23
|
+
* const optimizerProps = getImageOptimizerProps(resource)
|
|
24
|
+
* <FadeImage src={src} alt="" optimizerProps={optimizerProps} width={800} height={600} />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export const FadeImage: React.FC<FadeImageProps> = ({
|
|
28
|
+
optimizerProps,
|
|
29
|
+
style,
|
|
30
|
+
fadeDuration = 500,
|
|
31
|
+
...props
|
|
32
|
+
}) => {
|
|
33
|
+
const [loaded, setLoaded] = useState(false)
|
|
34
|
+
|
|
35
|
+
const { blurDataURL, style: optimizerStyle } = optimizerProps
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<NextImage
|
|
39
|
+
{...props}
|
|
40
|
+
placeholder={blurDataURL ? 'blur' : 'empty'}
|
|
41
|
+
blurDataURL={blurDataURL}
|
|
42
|
+
style={{
|
|
43
|
+
...optimizerStyle,
|
|
44
|
+
...style,
|
|
45
|
+
filter: loaded ? 'blur(0px)' : 'blur(20px)',
|
|
46
|
+
transition: loaded ? `filter ${fadeDuration}ms ease-in-out` : undefined,
|
|
47
|
+
}}
|
|
48
|
+
onLoad={() => setLoaded(true)}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import React from 'react'
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
4
|
import NextImage, { type ImageProps } from 'next/image'
|
|
5
5
|
import type { MediaResource } from '../types.js'
|
|
6
6
|
import { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'
|
|
@@ -8,6 +8,10 @@ import { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'
|
|
|
8
8
|
export interface ImageBoxProps extends Omit<ImageProps, 'src' | 'alt'> {
|
|
9
9
|
media: MediaResource | string
|
|
10
10
|
alt?: string
|
|
11
|
+
/** Enable smooth blur-to-sharp fade transition on load. Defaults to `true`. */
|
|
12
|
+
fade?: boolean
|
|
13
|
+
/** Duration of the fade animation in milliseconds. Defaults to `500`. */
|
|
14
|
+
fadeDuration?: number
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
export const ImageBox: React.FC<ImageBoxProps> = ({
|
|
@@ -18,10 +22,20 @@ export const ImageBox: React.FC<ImageBoxProps> = ({
|
|
|
18
22
|
priority,
|
|
19
23
|
loading: loadingFromProps,
|
|
20
24
|
style: styleFromProps,
|
|
25
|
+
fade = true,
|
|
26
|
+
fadeDuration = 500,
|
|
21
27
|
...props
|
|
22
28
|
}) => {
|
|
29
|
+
const [loaded, setLoaded] = useState(false)
|
|
23
30
|
const loading = priority ? undefined : (loadingFromProps ?? 'lazy')
|
|
24
31
|
|
|
32
|
+
const fadeStyle = fade
|
|
33
|
+
? {
|
|
34
|
+
filter: loaded ? 'blur(0px)' : 'blur(20px)',
|
|
35
|
+
transition: loaded ? `filter ${fadeDuration}ms ease-in-out` : undefined,
|
|
36
|
+
}
|
|
37
|
+
: undefined
|
|
38
|
+
|
|
25
39
|
if (typeof media === 'string') {
|
|
26
40
|
return (
|
|
27
41
|
<NextImage
|
|
@@ -31,9 +45,10 @@ export const ImageBox: React.FC<ImageBoxProps> = ({
|
|
|
31
45
|
quality={80}
|
|
32
46
|
fill={fill}
|
|
33
47
|
sizes={sizes}
|
|
34
|
-
style={{ objectFit: 'cover', objectPosition: 'center', ...styleFromProps }}
|
|
48
|
+
style={{ objectFit: 'cover', objectPosition: 'center', ...fadeStyle, ...styleFromProps }}
|
|
35
49
|
priority={priority}
|
|
36
50
|
loading={loading}
|
|
51
|
+
onLoad={fade ? () => setLoaded(true) : undefined}
|
|
37
52
|
/>
|
|
38
53
|
)
|
|
39
54
|
}
|
|
@@ -55,11 +70,12 @@ export const ImageBox: React.FC<ImageBoxProps> = ({
|
|
|
55
70
|
width={!fill ? width : undefined}
|
|
56
71
|
height={!fill ? height : undefined}
|
|
57
72
|
sizes={sizes}
|
|
58
|
-
style={{ objectFit: 'cover', ...optimizerProps.style, ...styleFromProps }}
|
|
73
|
+
style={{ objectFit: 'cover', ...optimizerProps.style, ...fadeStyle, ...styleFromProps }}
|
|
59
74
|
placeholder={optimizerProps.placeholder}
|
|
60
75
|
blurDataURL={optimizerProps.blurDataURL}
|
|
61
76
|
priority={priority}
|
|
62
77
|
loading={loading}
|
|
78
|
+
onLoad={fade ? () => setLoaded(true) : undefined}
|
|
63
79
|
/>
|
|
64
80
|
)
|
|
65
81
|
}
|
package/src/exports/client.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { OptimizationStatus } from '../components/OptimizationStatus.js'
|
|
2
2
|
export { ImageBox } from '../components/ImageBox.js'
|
|
3
3
|
export type { ImageBoxProps } from '../components/ImageBox.js'
|
|
4
|
+
export { FadeImage } from '../components/FadeImage.js'
|
|
5
|
+
export type { FadeImageProps } from '../components/FadeImage.js'
|
|
4
6
|
export { getImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'
|
|
5
7
|
export type { ImageOptimizerProps } from '../utilities/getImageOptimizerProps.js'
|
|
6
8
|
export { RegenerationButton } from '../components/RegenerationButton.js'
|