smart-glide-react 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +333 -0
- package/dist/index.d.mts +241 -0
- package/dist/index.d.ts +241 -0
- package/dist/index.js +436 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +421 -0
- package/dist/index.mjs.map +1 -0
- package/dist/next.d.mts +21 -0
- package/dist/next.d.ts +21 -0
- package/dist/next.js +21 -0
- package/dist/next.js.map +1 -0
- package/dist/next.mjs +16 -0
- package/dist/next.mjs.map +1 -0
- package/dist/server.d.mts +112 -0
- package/dist/server.d.ts +112 -0
- package/dist/server.js +83 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +79 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +81 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
/** Named responsive presets available on the Laravel backend. */
|
|
5
|
+
type ResponsivePreset = 'hero' | 'thumbnails' | 'square' | 'portrait' | 'hd' | 'fhd' | 'retina' | (string & {});
|
|
6
|
+
/** Named image profiles available on the Laravel backend. */
|
|
7
|
+
type ImageProfile = 'default' | 'thumbnail' | 'hero' | 'portrait' | 'square' | 'profile_photo' | 'cover' | 'background' | (string & {});
|
|
8
|
+
/** Response shape returned by the `/img-data` API. */
|
|
9
|
+
interface SmartGlideData {
|
|
10
|
+
src: string;
|
|
11
|
+
srcset?: string | null;
|
|
12
|
+
sizes?: string | null;
|
|
13
|
+
widths?: number[];
|
|
14
|
+
blurDataUrl?: string | null;
|
|
15
|
+
schema?: Record<string, unknown> | null;
|
|
16
|
+
}
|
|
17
|
+
/** Options passed to `useSmartGlide` and lower-level helpers. */
|
|
18
|
+
interface SmartGlideOptions {
|
|
19
|
+
/** Relative path of the image (e.g. `"products/phone.jpg"`). */
|
|
20
|
+
path: string;
|
|
21
|
+
/**
|
|
22
|
+
* Base URL of your Laravel app (e.g. `"https://api.example.com"`).
|
|
23
|
+
* Defaults to the `NEXT_PUBLIC_SMART_GLIDE_URL` / `VITE_SMART_GLIDE_URL`
|
|
24
|
+
* environment variable when omitted.
|
|
25
|
+
*/
|
|
26
|
+
baseUrl?: string;
|
|
27
|
+
/** Named or custom compression profile (default: `"default"`). */
|
|
28
|
+
profile?: ImageProfile;
|
|
29
|
+
/**
|
|
30
|
+
* Responsive breakpoints:
|
|
31
|
+
* - `"hero"` — named preset
|
|
32
|
+
* - `[640, 960, 1280]` — custom widths
|
|
33
|
+
* - `false` — disable srcset
|
|
34
|
+
*/
|
|
35
|
+
responsive?: ResponsivePreset | number[] | false;
|
|
36
|
+
/** Fetch the blurred Base64 LQIP from the server. Enables `blurDataUrl`. */
|
|
37
|
+
blurPlaceholder?: boolean;
|
|
38
|
+
/** Request JSON-LD `ImageObject` data from the server. */
|
|
39
|
+
schema?: boolean;
|
|
40
|
+
/** Extra Glide transformation params (w, h, fit, focus, q…). */
|
|
41
|
+
params?: Record<string, string | number>;
|
|
42
|
+
/**
|
|
43
|
+
* Additional fetch options forwarded to the underlying `fetch()` call.
|
|
44
|
+
* (e.g. `{ next: { revalidate: 3600 } }` for Next.js ISR)
|
|
45
|
+
*/
|
|
46
|
+
fetchOptions?: RequestInit;
|
|
47
|
+
}
|
|
48
|
+
/** Props accepted by the `<SmartGlideImage>` component. */
|
|
49
|
+
interface SmartGlideImageProps extends SmartGlideOptions {
|
|
50
|
+
/** Alt text — required for accessibility and SEO. */
|
|
51
|
+
alt: string;
|
|
52
|
+
/**
|
|
53
|
+
* Mark this image as a Largest Contentful Paint candidate.
|
|
54
|
+
* Sets `fetchpriority="high"` + `loading="eager"`.
|
|
55
|
+
*/
|
|
56
|
+
priority?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Show a blurred low-quality placeholder while the full image loads.
|
|
59
|
+
*/
|
|
60
|
+
placeholder?: 'blur' | 'empty';
|
|
61
|
+
/** Inline aspect-ratio hint (e.g. `"16 / 9"`, `"1"`, `"4 / 3"`). */
|
|
62
|
+
aspectRatio?: string;
|
|
63
|
+
/** Width attribute for layout stability. */
|
|
64
|
+
width?: number;
|
|
65
|
+
/** Height attribute for layout stability. */
|
|
66
|
+
height?: number;
|
|
67
|
+
/** CSS class name. */
|
|
68
|
+
className?: string;
|
|
69
|
+
/** Inline styles. */
|
|
70
|
+
style?: React.CSSProperties;
|
|
71
|
+
/** Callback fired when the image fully loads. */
|
|
72
|
+
onLoad?: React.ReactEventHandler<HTMLImageElement>;
|
|
73
|
+
/** Callback fired on image error. */
|
|
74
|
+
onError?: React.ReactEventHandler<HTMLImageElement>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A full-featured, SEO-optimised `<img>` component backed by Laravel Smart Glide.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* <SmartGlideImage
|
|
82
|
+
* path="products/phone.jpg"
|
|
83
|
+
* alt="Awesome Phone"
|
|
84
|
+
* profile="hero"
|
|
85
|
+
* responsive="retina"
|
|
86
|
+
* />
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* <SmartGlideImage
|
|
90
|
+
* path="home/hero.jpg"
|
|
91
|
+
* alt="Hero banner"
|
|
92
|
+
* profile="hero"
|
|
93
|
+
* priority
|
|
94
|
+
* placeholder="blur"
|
|
95
|
+
* blurPlaceholder
|
|
96
|
+
* schema
|
|
97
|
+
* width={1600}
|
|
98
|
+
* height={900}
|
|
99
|
+
* />
|
|
100
|
+
*/
|
|
101
|
+
declare const SmartGlideImage: React.ForwardRefExoticComponent<SmartGlideImageProps & React.RefAttributes<HTMLImageElement>>;
|
|
102
|
+
interface SmartGlidePictureSource {
|
|
103
|
+
media?: string;
|
|
104
|
+
widths?: number[];
|
|
105
|
+
responsive?: string | number[];
|
|
106
|
+
params?: Record<string, string | number>;
|
|
107
|
+
type?: string;
|
|
108
|
+
}
|
|
109
|
+
interface SmartGlidePictureProps extends SmartGlideImageProps {
|
|
110
|
+
sources?: SmartGlidePictureSource[];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `<picture>` component with multiple `<source>` breakpoints
|
|
114
|
+
* plus a Smart Glide powered `<img>` fallback.
|
|
115
|
+
*/
|
|
116
|
+
declare const SmartGlidePicture: React.ForwardRefExoticComponent<SmartGlidePictureProps & React.RefAttributes<HTMLImageElement>>;
|
|
117
|
+
interface SmartGlideBackgroundProps extends SmartGlideOptions {
|
|
118
|
+
children?: React.ReactNode;
|
|
119
|
+
className?: string;
|
|
120
|
+
style?: React.CSSProperties;
|
|
121
|
+
'aria-label'?: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* CSS background-image container backed by Smart Glide.
|
|
125
|
+
*/
|
|
126
|
+
declare const SmartGlideBackground: React.FC<SmartGlideBackgroundProps>;
|
|
127
|
+
|
|
128
|
+
interface SmartGlideConfig {
|
|
129
|
+
/** Base URL of your Laravel application (e.g. `"https://api.example.com"`). */
|
|
130
|
+
baseUrl?: string;
|
|
131
|
+
/** Delivery path prefix (default: `"/img"`). */
|
|
132
|
+
deliveryPath?: string;
|
|
133
|
+
/** Data API path prefix (default: `"/img-data"`). */
|
|
134
|
+
dataPath?: string;
|
|
135
|
+
/** Default image profile to apply when none is specified. */
|
|
136
|
+
defaultProfile?: string;
|
|
137
|
+
/** Default responsive set name or widths. */
|
|
138
|
+
defaultResponsive?: string | number[];
|
|
139
|
+
/** Emit JSON-LD `ImageObject` by default for all images. */
|
|
140
|
+
defaultSchema?: boolean;
|
|
141
|
+
}
|
|
142
|
+
declare function useSmartGlideConfig(): SmartGlideConfig;
|
|
143
|
+
interface SmartGlideProviderProps {
|
|
144
|
+
config: SmartGlideConfig;
|
|
145
|
+
children: React.ReactNode;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Wrap your application (or part of it) with `<SmartGlideProvider>` to
|
|
149
|
+
* configure Smart Glide globally. Individual components/hooks can still
|
|
150
|
+
* override any option locally.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* // app/layout.tsx (Next.js App Router)
|
|
154
|
+
* import { SmartGlideProvider } from 'smart-glide-react';
|
|
155
|
+
*
|
|
156
|
+
* export default function RootLayout({ children }) {
|
|
157
|
+
* return (
|
|
158
|
+
* <SmartGlideProvider config={{ baseUrl: process.env.NEXT_PUBLIC_API_URL }}>
|
|
159
|
+
* {children}
|
|
160
|
+
* </SmartGlideProvider>
|
|
161
|
+
* );
|
|
162
|
+
* }
|
|
163
|
+
*/
|
|
164
|
+
declare function SmartGlideProvider({ config, children }: SmartGlideProviderProps): react_jsx_runtime.JSX.Element;
|
|
165
|
+
|
|
166
|
+
interface UseSmartGlideResult {
|
|
167
|
+
/** Final signed delivery URL (ready for `<img src>`). */
|
|
168
|
+
src: string;
|
|
169
|
+
/** Responsive srcset string. */
|
|
170
|
+
srcset: string | null;
|
|
171
|
+
/** Sizes attribute. */
|
|
172
|
+
sizes: string | null;
|
|
173
|
+
/** Base64 LQIP blur data URI for placeholder. */
|
|
174
|
+
blurDataUrl: string | null;
|
|
175
|
+
/** JSON-LD ImageObject schema data. */
|
|
176
|
+
schema: Record<string, unknown> | null;
|
|
177
|
+
/** True while fetching from the API. */
|
|
178
|
+
isLoading: boolean;
|
|
179
|
+
/** Error if the fetch failed. */
|
|
180
|
+
error: Error | null;
|
|
181
|
+
/** Manually re-fetch the data. */
|
|
182
|
+
refetch: () => void;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Fetch Smart Glide image data from the Laravel API.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* const { src, srcset, sizes, blurDataUrl } = useSmartGlide({
|
|
189
|
+
* path: 'products/phone.jpg',
|
|
190
|
+
* profile: 'hero',
|
|
191
|
+
* responsive: 'retina',
|
|
192
|
+
* blurPlaceholder: true,
|
|
193
|
+
* baseUrl: process.env.NEXT_PUBLIC_API_URL,
|
|
194
|
+
* });
|
|
195
|
+
*/
|
|
196
|
+
declare function useSmartGlide(options: SmartGlideOptions): UseSmartGlideResult;
|
|
197
|
+
interface UseSmartGlideStaticResult {
|
|
198
|
+
src: string;
|
|
199
|
+
srcset: string;
|
|
200
|
+
sizes: string;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Build image URLs entirely client-side — no API fetch required.
|
|
204
|
+
* Ideal for Next.js App Router with `generateStaticParams` or ISR.
|
|
205
|
+
*
|
|
206
|
+
* NOTE: URLs generated here are **unsigned**. Use this only in development
|
|
207
|
+
* or when `SMART_GLIDE_SECURE=false` on your Laravel backend.
|
|
208
|
+
* For signed URLs, use `useSmartGlide` or server-side data fetching.
|
|
209
|
+
*/
|
|
210
|
+
declare function useSmartGlideStatic(options: SmartGlideOptions): UseSmartGlideStaticResult;
|
|
211
|
+
|
|
212
|
+
/** Resolve the base URL from options or environment variables. */
|
|
213
|
+
declare function resolveBaseUrl(baseUrl?: string): string;
|
|
214
|
+
/** Resolve the delivery path prefix (default: `/img`). */
|
|
215
|
+
declare function resolveDeliveryPath(deliveryPath?: string): string;
|
|
216
|
+
/** Resolve the data API path prefix (default: `/img-data`). */
|
|
217
|
+
declare function resolveDataPath(dataPath?: string): string;
|
|
218
|
+
/** Build a single Smart Glide delivery URL (unsigned — for preview/dev). */
|
|
219
|
+
declare function buildDeliveryUrl(path: string, params?: Record<string, string | number>, options?: {
|
|
220
|
+
baseUrl?: string;
|
|
221
|
+
deliveryPath?: string;
|
|
222
|
+
}): string;
|
|
223
|
+
/** Build the JSON data API URL for a given path. */
|
|
224
|
+
declare function buildDataUrl(path: string, options: SmartGlideOptions & {
|
|
225
|
+
deliveryPath?: string;
|
|
226
|
+
dataPath?: string;
|
|
227
|
+
}): string;
|
|
228
|
+
declare function resolveWidths(responsive?: string | number[] | false | null): number[];
|
|
229
|
+
/**
|
|
230
|
+
* Build srcset and sizes strings entirely client-side
|
|
231
|
+
* (no API call — for use with Next.js custom loader or static generation).
|
|
232
|
+
*/
|
|
233
|
+
declare function buildSrcSet(path: string, params?: Record<string, string | number>, widths?: number[], options?: {
|
|
234
|
+
baseUrl?: string;
|
|
235
|
+
deliveryPath?: string;
|
|
236
|
+
}): {
|
|
237
|
+
srcset: string;
|
|
238
|
+
sizes: string;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export { type ImageProfile, type ResponsivePreset, SmartGlideBackground, type SmartGlideBackgroundProps, type SmartGlideConfig, type SmartGlideData, SmartGlideImage, type SmartGlideImageProps, type SmartGlideOptions, SmartGlidePicture, type SmartGlidePictureProps, type SmartGlidePictureSource, SmartGlideProvider, type SmartGlideProviderProps, type UseSmartGlideResult, type UseSmartGlideStaticResult, buildDataUrl, buildDeliveryUrl, buildSrcSet, resolveBaseUrl, resolveDataPath, resolveDeliveryPath, resolveWidths, useSmartGlide, useSmartGlideConfig, useSmartGlideStatic };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
// src/components.tsx
|
|
7
|
+
|
|
8
|
+
// src/url-builder.ts
|
|
9
|
+
function resolveBaseUrl(baseUrl) {
|
|
10
|
+
if (baseUrl) return baseUrl.replace(/\/$/, "");
|
|
11
|
+
if (typeof process !== "undefined") {
|
|
12
|
+
const env = process.env.NEXT_PUBLIC_SMART_GLIDE_URL ?? process.env.VITE_SMART_GLIDE_URL ?? process.env.SMART_GLIDE_URL ?? "";
|
|
13
|
+
if (env) return env.replace(/\/$/, "");
|
|
14
|
+
}
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
function resolveDeliveryPath(deliveryPath) {
|
|
18
|
+
if (deliveryPath) return "/" + deliveryPath.replace(/^\/|\/$/g, "");
|
|
19
|
+
if (typeof process !== "undefined") {
|
|
20
|
+
const env = process.env.NEXT_PUBLIC_SMART_GLIDE_DELIVERY_PATH ?? "/img";
|
|
21
|
+
return "/" + env.replace(/^\/|\/$/g, "");
|
|
22
|
+
}
|
|
23
|
+
return "/img";
|
|
24
|
+
}
|
|
25
|
+
function resolveDataPath(dataPath) {
|
|
26
|
+
if (dataPath) return "/" + dataPath.replace(/^\/|\/$/g, "");
|
|
27
|
+
if (typeof process !== "undefined") {
|
|
28
|
+
const env = process.env.NEXT_PUBLIC_SMART_GLIDE_DATA_PATH ?? "/img-data";
|
|
29
|
+
return "/" + env.replace(/^\/|\/$/g, "");
|
|
30
|
+
}
|
|
31
|
+
return "/img-data";
|
|
32
|
+
}
|
|
33
|
+
function buildDeliveryUrl(path, params = {}, options = {}) {
|
|
34
|
+
const base = resolveBaseUrl(options.baseUrl);
|
|
35
|
+
const prefix = resolveDeliveryPath(options.deliveryPath);
|
|
36
|
+
const cleanPath = path.replace(/^\//, "");
|
|
37
|
+
const queryParams = Object.entries(params).filter(([, v]) => v !== void 0 && v !== "").map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
38
|
+
return `${base}${prefix}/${cleanPath}${queryParams ? "?" + queryParams : ""}`;
|
|
39
|
+
}
|
|
40
|
+
function buildDataUrl(path, options) {
|
|
41
|
+
const base = resolveBaseUrl(options.baseUrl);
|
|
42
|
+
const prefix = resolveDataPath(options.dataPath);
|
|
43
|
+
const cleanPath = path.replace(/^\//, "");
|
|
44
|
+
const params = {};
|
|
45
|
+
if (options.profile) params.profile = options.profile;
|
|
46
|
+
if (options.blurPlaceholder) params.blur_placeholder = "1";
|
|
47
|
+
if (options.schema) params.schema = "1";
|
|
48
|
+
if (options.responsive === false) {
|
|
49
|
+
params.responsive = "0";
|
|
50
|
+
} else if (Array.isArray(options.responsive)) {
|
|
51
|
+
params.responsive = options.responsive.join(",");
|
|
52
|
+
} else if (options.responsive) {
|
|
53
|
+
params.responsive = options.responsive;
|
|
54
|
+
}
|
|
55
|
+
if (options.params) {
|
|
56
|
+
Object.entries(options.params).forEach(([k, v]) => {
|
|
57
|
+
params[k] = String(v);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const query = new URLSearchParams(params).toString();
|
|
61
|
+
return `${base}${prefix}/${cleanPath}${query ? "?" + query : ""}`;
|
|
62
|
+
}
|
|
63
|
+
var NAMED_SETS = {
|
|
64
|
+
hero: [640, 960, 1280, 1600, 1920],
|
|
65
|
+
thumbnails: [240, 320, 480],
|
|
66
|
+
square: [320, 480, 640],
|
|
67
|
+
portrait: [480, 768, 1024],
|
|
68
|
+
hd: [960, 1280, 1600],
|
|
69
|
+
fhd: [1280, 1600, 1920, 2560],
|
|
70
|
+
retina: [640, 960, 1280, 1920, 2560]
|
|
71
|
+
};
|
|
72
|
+
function resolveWidths(responsive) {
|
|
73
|
+
if (responsive === false) return [];
|
|
74
|
+
if (responsive === null || responsive === void 0) {
|
|
75
|
+
return NAMED_SETS.retina;
|
|
76
|
+
}
|
|
77
|
+
if (Array.isArray(responsive)) return responsive;
|
|
78
|
+
return NAMED_SETS[responsive] ?? NAMED_SETS.retina;
|
|
79
|
+
}
|
|
80
|
+
function buildSrcSet(path, params = {}, widths = NAMED_SETS.retina, options = {}) {
|
|
81
|
+
const entries = widths.map(
|
|
82
|
+
(w) => `${buildDeliveryUrl(path, { ...params, w }, options)} ${w}w`
|
|
83
|
+
);
|
|
84
|
+
const sizes = widths.map((w) => `(max-width: ${w}px) 100vw`).concat("100vw").join(", ");
|
|
85
|
+
return {
|
|
86
|
+
srcset: entries.join(", "),
|
|
87
|
+
sizes
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/hooks.ts
|
|
92
|
+
function useSmartGlide(options) {
|
|
93
|
+
const {
|
|
94
|
+
path,
|
|
95
|
+
baseUrl,
|
|
96
|
+
profile,
|
|
97
|
+
responsive,
|
|
98
|
+
blurPlaceholder,
|
|
99
|
+
schema,
|
|
100
|
+
params,
|
|
101
|
+
fetchOptions
|
|
102
|
+
} = options;
|
|
103
|
+
const [data, setData] = react.useState(null);
|
|
104
|
+
const [isLoading, setIsLoading] = react.useState(true);
|
|
105
|
+
const [error, setError] = react.useState(null);
|
|
106
|
+
const fetchCounter = react.useRef(0);
|
|
107
|
+
const fetchData = react.useCallback(async () => {
|
|
108
|
+
const id = ++fetchCounter.current;
|
|
109
|
+
setIsLoading(true);
|
|
110
|
+
setError(null);
|
|
111
|
+
try {
|
|
112
|
+
const url = buildDataUrl(path, { path, baseUrl, profile, responsive, blurPlaceholder, schema, params });
|
|
113
|
+
const response = await fetch(url, {
|
|
114
|
+
headers: { Accept: "application/json" },
|
|
115
|
+
...fetchOptions
|
|
116
|
+
});
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
throw new Error(`Smart Glide API error: ${response.status} ${response.statusText}`);
|
|
119
|
+
}
|
|
120
|
+
const json = await response.json();
|
|
121
|
+
if (id === fetchCounter.current) {
|
|
122
|
+
setData(json);
|
|
123
|
+
}
|
|
124
|
+
} catch (err) {
|
|
125
|
+
if (id === fetchCounter.current) {
|
|
126
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
127
|
+
}
|
|
128
|
+
} finally {
|
|
129
|
+
if (id === fetchCounter.current) {
|
|
130
|
+
setIsLoading(false);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}, [path, baseUrl, profile, JSON.stringify(responsive), blurPlaceholder, schema, JSON.stringify(params)]);
|
|
134
|
+
react.useEffect(() => {
|
|
135
|
+
fetchData();
|
|
136
|
+
}, [fetchData]);
|
|
137
|
+
return {
|
|
138
|
+
src: data?.src ?? buildDeliveryUrl(path, params ?? {}, { baseUrl }),
|
|
139
|
+
srcset: data?.srcset ?? null,
|
|
140
|
+
sizes: data?.sizes ?? null,
|
|
141
|
+
blurDataUrl: data?.blurDataUrl ?? null,
|
|
142
|
+
schema: data?.schema ?? null,
|
|
143
|
+
isLoading,
|
|
144
|
+
error,
|
|
145
|
+
refetch: fetchData
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function useSmartGlideStatic(options) {
|
|
149
|
+
const { path, baseUrl, profile, responsive, params } = options;
|
|
150
|
+
const glideParams = { ...params };
|
|
151
|
+
if (profile) glideParams.profile = profile;
|
|
152
|
+
const widths = resolveWidths(responsive);
|
|
153
|
+
const { srcset, sizes } = buildSrcSet(path, glideParams, widths, { baseUrl });
|
|
154
|
+
const src = buildDeliveryUrl(path, glideParams, { baseUrl });
|
|
155
|
+
return { src, srcset, sizes };
|
|
156
|
+
}
|
|
157
|
+
var BlurOverlay = ({ blurDataUrl, isVisible }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
158
|
+
"span",
|
|
159
|
+
{
|
|
160
|
+
"aria-hidden": "true",
|
|
161
|
+
style: {
|
|
162
|
+
position: "absolute",
|
|
163
|
+
inset: 0,
|
|
164
|
+
backgroundImage: `url(${blurDataUrl})`,
|
|
165
|
+
backgroundSize: "cover",
|
|
166
|
+
backgroundPosition: "center",
|
|
167
|
+
filter: "blur(20px)",
|
|
168
|
+
transform: "scale(1.05)",
|
|
169
|
+
transition: "opacity 0.4s ease",
|
|
170
|
+
opacity: isVisible ? 1 : 0,
|
|
171
|
+
pointerEvents: "none"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
var SchemaScript = ({ schema }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
176
|
+
"script",
|
|
177
|
+
{
|
|
178
|
+
type: "application/ld+json",
|
|
179
|
+
dangerouslySetInnerHTML: { __html: JSON.stringify(schema) }
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
var SmartGlideImage = react.forwardRef(
|
|
183
|
+
function SmartGlideImage2(props, ref) {
|
|
184
|
+
const {
|
|
185
|
+
path,
|
|
186
|
+
baseUrl,
|
|
187
|
+
profile,
|
|
188
|
+
responsive,
|
|
189
|
+
blurPlaceholder: fetchBlur = false,
|
|
190
|
+
schema: emitSchema = false,
|
|
191
|
+
params,
|
|
192
|
+
fetchOptions,
|
|
193
|
+
priority = false,
|
|
194
|
+
placeholder = "empty",
|
|
195
|
+
aspectRatio,
|
|
196
|
+
alt,
|
|
197
|
+
width,
|
|
198
|
+
height,
|
|
199
|
+
className,
|
|
200
|
+
style,
|
|
201
|
+
onLoad,
|
|
202
|
+
onError
|
|
203
|
+
} = props;
|
|
204
|
+
const shouldFetchBlur = placeholder === "blur" || fetchBlur;
|
|
205
|
+
const { src, srcset, sizes, blurDataUrl, schema } = useSmartGlide({
|
|
206
|
+
path,
|
|
207
|
+
baseUrl,
|
|
208
|
+
profile,
|
|
209
|
+
responsive,
|
|
210
|
+
blurPlaceholder: shouldFetchBlur,
|
|
211
|
+
schema: emitSchema,
|
|
212
|
+
params,
|
|
213
|
+
fetchOptions
|
|
214
|
+
});
|
|
215
|
+
const [imgLoaded, setImgLoaded] = react.useState(false);
|
|
216
|
+
const showBlur = shouldFetchBlur && !!blurDataUrl && !imgLoaded;
|
|
217
|
+
const handleLoad = react.useCallback(
|
|
218
|
+
(e) => {
|
|
219
|
+
setImgLoaded(true);
|
|
220
|
+
onLoad?.(e);
|
|
221
|
+
},
|
|
222
|
+
[onLoad]
|
|
223
|
+
);
|
|
224
|
+
const handleError = react.useCallback(
|
|
225
|
+
(e) => {
|
|
226
|
+
setImgLoaded(true);
|
|
227
|
+
onError?.(e);
|
|
228
|
+
},
|
|
229
|
+
[onError]
|
|
230
|
+
);
|
|
231
|
+
const composedStyle = {
|
|
232
|
+
...style,
|
|
233
|
+
...aspectRatio ? { aspectRatio } : {}
|
|
234
|
+
};
|
|
235
|
+
if (shouldFetchBlur && blurDataUrl) {
|
|
236
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
237
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
238
|
+
"span",
|
|
239
|
+
{
|
|
240
|
+
style: {
|
|
241
|
+
position: "relative",
|
|
242
|
+
display: "inline-block",
|
|
243
|
+
overflow: "hidden",
|
|
244
|
+
width: width ? `${width}px` : void 0,
|
|
245
|
+
height: height ? `${height}px` : void 0,
|
|
246
|
+
aspectRatio
|
|
247
|
+
},
|
|
248
|
+
children: [
|
|
249
|
+
/* @__PURE__ */ jsxRuntime.jsx(BlurOverlay, { blurDataUrl, isVisible: showBlur }),
|
|
250
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
251
|
+
"img",
|
|
252
|
+
{
|
|
253
|
+
ref,
|
|
254
|
+
src,
|
|
255
|
+
srcSet: srcset ?? void 0,
|
|
256
|
+
sizes: sizes ?? void 0,
|
|
257
|
+
alt,
|
|
258
|
+
width,
|
|
259
|
+
height,
|
|
260
|
+
className,
|
|
261
|
+
style: { ...composedStyle, display: "block", width: "100%", height: "100%", objectFit: "cover" },
|
|
262
|
+
loading: priority ? "eager" : "lazy",
|
|
263
|
+
fetchPriority: priority ? "high" : "auto",
|
|
264
|
+
decoding: "async",
|
|
265
|
+
onLoad: handleLoad,
|
|
266
|
+
onError: handleError
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
),
|
|
272
|
+
schema && /* @__PURE__ */ jsxRuntime.jsx(SchemaScript, { schema })
|
|
273
|
+
] });
|
|
274
|
+
}
|
|
275
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
276
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
277
|
+
"img",
|
|
278
|
+
{
|
|
279
|
+
ref,
|
|
280
|
+
src,
|
|
281
|
+
srcSet: srcset ?? void 0,
|
|
282
|
+
sizes: sizes ?? void 0,
|
|
283
|
+
alt,
|
|
284
|
+
width,
|
|
285
|
+
height,
|
|
286
|
+
className,
|
|
287
|
+
style: composedStyle,
|
|
288
|
+
loading: priority ? "eager" : "lazy",
|
|
289
|
+
fetchPriority: priority ? "high" : "auto",
|
|
290
|
+
decoding: "async",
|
|
291
|
+
onLoad,
|
|
292
|
+
onError
|
|
293
|
+
}
|
|
294
|
+
),
|
|
295
|
+
emitSchema && schema && /* @__PURE__ */ jsxRuntime.jsx(SchemaScript, { schema })
|
|
296
|
+
] });
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
SmartGlideImage.displayName = "SmartGlideImage";
|
|
300
|
+
var SmartGlidePicture = react.forwardRef(
|
|
301
|
+
function SmartGlidePicture2(props, ref) {
|
|
302
|
+
const {
|
|
303
|
+
sources = [],
|
|
304
|
+
path,
|
|
305
|
+
baseUrl,
|
|
306
|
+
params,
|
|
307
|
+
alt,
|
|
308
|
+
profile,
|
|
309
|
+
blurPlaceholder,
|
|
310
|
+
schema,
|
|
311
|
+
fetchOptions,
|
|
312
|
+
priority,
|
|
313
|
+
placeholder,
|
|
314
|
+
aspectRatio,
|
|
315
|
+
width,
|
|
316
|
+
height,
|
|
317
|
+
className,
|
|
318
|
+
style,
|
|
319
|
+
onLoad,
|
|
320
|
+
onError
|
|
321
|
+
} = props;
|
|
322
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("picture", { children: [
|
|
323
|
+
sources.map((source, i) => {
|
|
324
|
+
const widths = source.widths ?? resolveWidths(source.responsive);
|
|
325
|
+
const mergedParams = { ...params ?? {}, ...source.params ?? {} };
|
|
326
|
+
const { srcset } = buildSrcSet(path, mergedParams, widths, { baseUrl });
|
|
327
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
328
|
+
"source",
|
|
329
|
+
{
|
|
330
|
+
media: source.media,
|
|
331
|
+
srcSet: srcset,
|
|
332
|
+
type: source.type
|
|
333
|
+
},
|
|
334
|
+
i
|
|
335
|
+
);
|
|
336
|
+
}),
|
|
337
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
338
|
+
SmartGlideImage,
|
|
339
|
+
{
|
|
340
|
+
ref,
|
|
341
|
+
path,
|
|
342
|
+
baseUrl,
|
|
343
|
+
params,
|
|
344
|
+
alt,
|
|
345
|
+
profile,
|
|
346
|
+
blurPlaceholder,
|
|
347
|
+
schema,
|
|
348
|
+
fetchOptions,
|
|
349
|
+
priority,
|
|
350
|
+
placeholder,
|
|
351
|
+
aspectRatio,
|
|
352
|
+
width,
|
|
353
|
+
height,
|
|
354
|
+
className,
|
|
355
|
+
style,
|
|
356
|
+
onLoad,
|
|
357
|
+
onError
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
] });
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
SmartGlidePicture.displayName = "SmartGlidePicture";
|
|
364
|
+
var SmartGlideBackground = ({
|
|
365
|
+
path,
|
|
366
|
+
baseUrl,
|
|
367
|
+
profile,
|
|
368
|
+
responsive,
|
|
369
|
+
params,
|
|
370
|
+
blurPlaceholder: fetchBlur,
|
|
371
|
+
children,
|
|
372
|
+
className,
|
|
373
|
+
style,
|
|
374
|
+
"aria-label": ariaLabel
|
|
375
|
+
}) => {
|
|
376
|
+
const glideParams = { ...params ?? {} };
|
|
377
|
+
if (profile) glideParams.profile = profile;
|
|
378
|
+
const widths = resolveWidths(responsive);
|
|
379
|
+
const primaryWidth = widths[widths.length - 1] ?? 1920;
|
|
380
|
+
const primarySrc = buildDeliveryUrl(path, { ...glideParams, w: primaryWidth }, { baseUrl });
|
|
381
|
+
const { blurDataUrl } = useSmartGlide({
|
|
382
|
+
path,
|
|
383
|
+
baseUrl,
|
|
384
|
+
profile,
|
|
385
|
+
responsive,
|
|
386
|
+
blurPlaceholder: !!fetchBlur,
|
|
387
|
+
params
|
|
388
|
+
});
|
|
389
|
+
const mediaStyles = widths.slice().reverse().map((w) => {
|
|
390
|
+
const url = buildDeliveryUrl(path, { ...glideParams, w }, { baseUrl });
|
|
391
|
+
return `@media (max-width: ${w}px) { .sg-bg-${w} { background-image: url('${url}'); } }`;
|
|
392
|
+
}).join("\n");
|
|
393
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
394
|
+
mediaStyles && /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: mediaStyles } }),
|
|
395
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
396
|
+
"div",
|
|
397
|
+
{
|
|
398
|
+
role: "img",
|
|
399
|
+
"aria-label": ariaLabel,
|
|
400
|
+
className,
|
|
401
|
+
style: {
|
|
402
|
+
backgroundImage: blurDataUrl ? `url('${primarySrc}'), url('${blurDataUrl}')` : `url('${primarySrc}')`,
|
|
403
|
+
backgroundSize: "cover",
|
|
404
|
+
backgroundPosition: "center",
|
|
405
|
+
backgroundRepeat: "no-repeat",
|
|
406
|
+
...style
|
|
407
|
+
},
|
|
408
|
+
children
|
|
409
|
+
}
|
|
410
|
+
)
|
|
411
|
+
] });
|
|
412
|
+
};
|
|
413
|
+
var SmartGlideContext = react.createContext({});
|
|
414
|
+
function useSmartGlideConfig() {
|
|
415
|
+
return react.useContext(SmartGlideContext);
|
|
416
|
+
}
|
|
417
|
+
function SmartGlideProvider({ config, children }) {
|
|
418
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SmartGlideContext.Provider, { value: config, children });
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
exports.SmartGlideBackground = SmartGlideBackground;
|
|
422
|
+
exports.SmartGlideImage = SmartGlideImage;
|
|
423
|
+
exports.SmartGlidePicture = SmartGlidePicture;
|
|
424
|
+
exports.SmartGlideProvider = SmartGlideProvider;
|
|
425
|
+
exports.buildDataUrl = buildDataUrl;
|
|
426
|
+
exports.buildDeliveryUrl = buildDeliveryUrl;
|
|
427
|
+
exports.buildSrcSet = buildSrcSet;
|
|
428
|
+
exports.resolveBaseUrl = resolveBaseUrl;
|
|
429
|
+
exports.resolveDataPath = resolveDataPath;
|
|
430
|
+
exports.resolveDeliveryPath = resolveDeliveryPath;
|
|
431
|
+
exports.resolveWidths = resolveWidths;
|
|
432
|
+
exports.useSmartGlide = useSmartGlide;
|
|
433
|
+
exports.useSmartGlideConfig = useSmartGlideConfig;
|
|
434
|
+
exports.useSmartGlideStatic = useSmartGlideStatic;
|
|
435
|
+
//# sourceMappingURL=index.js.map
|
|
436
|
+
//# sourceMappingURL=index.js.map
|