vite-smart-img 1.1.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.
@@ -0,0 +1,40 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ /**
4
+ * Результат импорта локального ассета из `.../assets/*` при подключённых плагинах SmartImg.
5
+ */
6
+ type SmartImgImportedAsset = {
7
+ src: string;
8
+ originalW: number;
9
+ originalH: number;
10
+ };
11
+
12
+ type SmartImgCommon = {
13
+ alt: string;
14
+ /**
15
+ * Передай `opacity`, чтобы картинка появлялась плавно (fade-in).
16
+ * Без этого пропа — появляется мгновенно.
17
+ * При перемонтировании с закешированной картинкой fade не запускается.
18
+ */
19
+ opacity?: boolean;
20
+ /** Доп. класс на обёртку. */
21
+ className?: string;
22
+ };
23
+ type SmartImgProps = SmartImgCommon & ({
24
+ /** Импорт из `.../assets/*` через плагины пакета (`{ src, originalW, originalH }`). */
25
+ asset: SmartImgImportedAsset;
26
+ } | {
27
+ /** Прямой URL (часто внешний) — размеры задаёшь вручную. */
28
+ dataSrc: string;
29
+ originalW: number;
30
+ originalH: number;
31
+ });
32
+ /**
33
+ * «Умная» ленивая загрузка изображений (SmartImg).
34
+ *
35
+ * - Локальные ассеты: проп **`asset`** из импорта с метаданными из Vite.
36
+ * - Внешние URL: **`dataSrc`** + **`originalW`** / **`originalH`**.
37
+ */
38
+ declare const SmartImg: (props: SmartImgProps) => react_jsx_runtime.JSX.Element;
39
+
40
+ export { SmartImg, type SmartImgImportedAsset, type SmartImgProps };
package/dist/index.js ADDED
@@ -0,0 +1,384 @@
1
+ // src/component/SmartImg.tsx
2
+ import { useEffect, useLayoutEffect, useRef, useState } from "react";
3
+
4
+ // src/component/smartImgManager.ts
5
+ var IS_DEV = typeof import.meta !== "undefined" && typeof import.meta.env === "object" && import.meta.env["DEV"] === true;
6
+ var SIZES = [400, 600, 800, 1e3, 1200, 1400, 1600, 1800, 2e3, 2500, 3e3, 3500, 4e3, 5e3, 6e3, 7e3, 8e3];
7
+ var _webpResult = null;
8
+ function supportsWebP() {
9
+ if (_webpResult !== null) return _webpResult;
10
+ try {
11
+ const c = document.createElement("canvas");
12
+ c.width = 1;
13
+ c.height = 1;
14
+ _webpResult = c.toDataURL("image/webp").startsWith("data:image/webp");
15
+ } catch {
16
+ _webpResult = false;
17
+ }
18
+ return _webpResult;
19
+ }
20
+ function pickSize(cssWidth, dpr, originalW) {
21
+ const target = Math.ceil(cssWidth * dpr);
22
+ return SIZES.find((s) => s >= target && s <= originalW) ?? null;
23
+ }
24
+ function buildUrl(dataSrc, size, webp) {
25
+ const base = dataSrc.replace(/\.[^./]+$/, "");
26
+ const originalExt = dataSrc.match(/\.([^./]+)$/)?.[1] ?? "jpg";
27
+ return `${base}-${size}.${webp ? "webp" : originalExt}`;
28
+ }
29
+ function isSvgDataSrc(url) {
30
+ if (url == null || typeof url !== "string") return false;
31
+ if (url.startsWith("data:image/svg")) return true;
32
+ const base = url.replace(/[?#].*$/, "");
33
+ return base.toLowerCase().endsWith(".svg");
34
+ }
35
+ function isExternalDataSrc(url) {
36
+ if (!url || url.startsWith("data:") || url.startsWith("blob:")) return false;
37
+ if (url.startsWith("/") || url.startsWith("./") || url.startsWith("../")) return false;
38
+ try {
39
+ const resolved = new URL(url, window.location.href);
40
+ return resolved.origin !== window.location.origin;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+ function directLoadKind(dataSrc) {
46
+ if (isSvgDataSrc(dataSrc)) return "svg";
47
+ if (isExternalDataSrc(dataSrc)) return "external";
48
+ return null;
49
+ }
50
+ function loadDirectFinal(img, state, kind) {
51
+ const url = state.dataSrc;
52
+ const tempImg = new Image();
53
+ tempImg.onload = () => {
54
+ state.loadedSize = Infinity;
55
+ loadedCache.set(state.dataSrc, { url, size: Infinity });
56
+ state.onLoad(url);
57
+ img.setAttribute("data-max-size-load", kind);
58
+ };
59
+ tempImg.onerror = () => {
60
+ state.onError();
61
+ img.setAttribute("data-max-size-load", `${kind}-error`);
62
+ };
63
+ tempImg.src = url;
64
+ }
65
+ function needsIntersectionObserver(dataSrc, originalW, wrapper, cached) {
66
+ if (!cached) return true;
67
+ if (directLoadKind(dataSrc)) return false;
68
+ if (IS_DEV) return false;
69
+ const cssWidth = wrapper.clientWidth || wrapper.offsetWidth;
70
+ if (cssWidth === 0) return true;
71
+ const neededSize = pickSize(cssWidth, window.devicePixelRatio ?? 1, originalW);
72
+ const loaded = cached.size;
73
+ if (neededSize === null) {
74
+ return loaded !== Infinity;
75
+ }
76
+ return neededSize > loaded;
77
+ }
78
+ function applyCachedCompleteMarker(img, dataSrc, cached) {
79
+ if (img.hasAttribute("data-max-size-load")) return;
80
+ const direct = directLoadKind(dataSrc);
81
+ if (direct) {
82
+ img.setAttribute("data-max-size-load", direct);
83
+ return;
84
+ }
85
+ if (IS_DEV) {
86
+ img.setAttribute("data-max-size-load", "dev");
87
+ return;
88
+ }
89
+ const s = cached.size;
90
+ img.setAttribute("data-max-size-load", s === Infinity ? "Infinity" : String(s));
91
+ }
92
+ var loadedCache = /* @__PURE__ */ new Map();
93
+ function getLoadedUrl(dataSrc) {
94
+ return loadedCache.get(dataSrc) ?? null;
95
+ }
96
+ var registry = /* @__PURE__ */ new Map();
97
+ var observing = /* @__PURE__ */ new Set();
98
+ var ioInstance = null;
99
+ var resizeTimer = null;
100
+ var resizeAttached = false;
101
+ function getIO() {
102
+ if (!ioInstance) {
103
+ ioInstance = new IntersectionObserver(handleIntersect, { threshold: 0.05 });
104
+ }
105
+ return ioInstance;
106
+ }
107
+ function safeObserve(img) {
108
+ if (observing.has(img)) return;
109
+ observing.add(img);
110
+ getIO().observe(img);
111
+ }
112
+ function safeUnobserve(img) {
113
+ if (!observing.has(img)) return;
114
+ observing.delete(img);
115
+ getIO().unobserve(img);
116
+ }
117
+ function handleIntersect(entries) {
118
+ for (const ioEntry of entries) {
119
+ if (!ioEntry.isIntersecting) continue;
120
+ const img = ioEntry.target;
121
+ const state = registry.get(img);
122
+ if (!state) continue;
123
+ safeUnobserve(img);
124
+ const direct = directLoadKind(state.dataSrc);
125
+ if (direct) {
126
+ loadDirectFinal(img, state, direct);
127
+ continue;
128
+ }
129
+ if (IS_DEV) {
130
+ const hit = loadedCache.get(state.dataSrc);
131
+ if (hit) {
132
+ state.loadedSize = hit.size;
133
+ if (!img.hasAttribute("data-max-size-load")) {
134
+ img.setAttribute("data-max-size-load", "dev");
135
+ }
136
+ continue;
137
+ }
138
+ const url2 = state.dataSrc;
139
+ const tempImg2 = new Image();
140
+ tempImg2.onload = () => {
141
+ state.loadedSize = Infinity;
142
+ loadedCache.set(state.dataSrc, { url: url2, size: Infinity });
143
+ state.onLoad(url2);
144
+ img.setAttribute("data-max-size-load", "dev");
145
+ };
146
+ tempImg2.onerror = () => state.onError();
147
+ tempImg2.src = url2;
148
+ continue;
149
+ }
150
+ const cssWidth = state.wrapper.clientWidth || state.wrapper.offsetWidth;
151
+ if (cssWidth === 0) {
152
+ safeObserve(img);
153
+ continue;
154
+ }
155
+ const neededSize = pickSize(cssWidth, window.devicePixelRatio ?? 1, state.originalW);
156
+ const isResizeCheck = state.pendingResizeCheck;
157
+ state.pendingResizeCheck = false;
158
+ const url = neededSize === null ? state.dataSrc : buildUrl(state.dataSrc, neededSize, supportsWebP());
159
+ if (isResizeCheck && neededSize !== null && state.loadedSize !== null && neededSize <= state.loadedSize) {
160
+ img.setAttribute("data-max-size-load", String(state.loadedSize));
161
+ return;
162
+ }
163
+ const tempImg = new Image();
164
+ tempImg.onload = () => {
165
+ state.loadedSize = neededSize ?? Infinity;
166
+ loadedCache.set(state.dataSrc, { url, size: state.loadedSize });
167
+ state.onLoad(url);
168
+ if (isResizeCheck || neededSize === null) {
169
+ img.setAttribute("data-max-size-load", String(state.loadedSize));
170
+ }
171
+ };
172
+ tempImg.onerror = () => {
173
+ state.onError();
174
+ if (isResizeCheck || neededSize === null) {
175
+ img.setAttribute("data-max-size-load", String(state.loadedSize ?? 0));
176
+ }
177
+ };
178
+ tempImg.src = url;
179
+ }
180
+ }
181
+ function handleResize() {
182
+ if (IS_DEV) return;
183
+ for (const [img, state] of registry) {
184
+ if (state.loadedSize === null) continue;
185
+ if (img.hasAttribute("data-max-size-load")) continue;
186
+ state.pendingResizeCheck = true;
187
+ safeObserve(img);
188
+ }
189
+ }
190
+ function attachResizeListener() {
191
+ if (resizeAttached) return;
192
+ resizeAttached = true;
193
+ window.addEventListener(
194
+ "resize",
195
+ () => {
196
+ if (resizeTimer) clearTimeout(resizeTimer);
197
+ resizeTimer = setTimeout(handleResize, 200);
198
+ },
199
+ { passive: true }
200
+ );
201
+ }
202
+ function register(img, wrapper, dataSrc, originalW, onLoad, onError) {
203
+ if (dataSrc == null || dataSrc === "" || typeof dataSrc !== "string") {
204
+ return () => {
205
+ safeUnobserve(img);
206
+ registry.delete(img);
207
+ };
208
+ }
209
+ attachResizeListener();
210
+ const cached = loadedCache.get(dataSrc);
211
+ const observe = needsIntersectionObserver(dataSrc, originalW, wrapper, cached);
212
+ const pendingResizeCheck = Boolean(cached) && observe && !IS_DEV && !directLoadKind(dataSrc);
213
+ registry.set(img, {
214
+ wrapper,
215
+ dataSrc,
216
+ originalW,
217
+ loadedSize: cached?.size ?? null,
218
+ pendingResizeCheck,
219
+ onLoad,
220
+ onError
221
+ });
222
+ const cleanup = () => {
223
+ safeUnobserve(img);
224
+ registry.delete(img);
225
+ };
226
+ if (cached && !observe) {
227
+ applyCachedCompleteMarker(img, dataSrc, cached);
228
+ return cleanup;
229
+ }
230
+ safeObserve(img);
231
+ return cleanup;
232
+ }
233
+
234
+ // src/component/styles.ts
235
+ var CSS = `
236
+ .smi-wrapper {
237
+ position: relative;
238
+ width: 100%;
239
+ height: 0;
240
+ overflow: hidden;
241
+ background-color: var(--smi-bg, #f0f0f0);
242
+ }
243
+ .smi-image {
244
+ position: absolute;
245
+ inset: 0;
246
+ width: 100%;
247
+ height: 100%;
248
+ object-fit: cover;
249
+ display: block;
250
+ }
251
+ .smi-fade {
252
+ opacity: 0;
253
+ transition: opacity var(--smi-fade-duration, 400ms) ease;
254
+ }
255
+ .smi-fade.smi-visible {
256
+ opacity: 1;
257
+ }
258
+ .smi-error {
259
+ object-fit: contain;
260
+ padding: 15%;
261
+ }
262
+ `;
263
+ var injected = false;
264
+ function injectStyles() {
265
+ if (typeof document === "undefined" || injected) return;
266
+ injected = true;
267
+ const style = document.createElement("style");
268
+ style.setAttribute("data-smi", "");
269
+ style.textContent = CSS;
270
+ document.head.appendChild(style);
271
+ }
272
+ var cls = {
273
+ wrapper: "smi-wrapper",
274
+ image: "smi-image",
275
+ fade: "smi-fade",
276
+ visible: "smi-visible",
277
+ error: "smi-error"
278
+ };
279
+
280
+ // src/component/SmartImg.tsx
281
+ import { jsx } from "react/jsx-runtime";
282
+ var BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
283
+ var ERROR_PLACEHOLDER = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect width='24' height='24' fill='%2312121c'/%3E%3Cpath fill='%23444466' d='M21 5v6.59l-3-3.01-4 4.01-4-4-4 3.99-3-2.99V5c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2zm-3 6.42 3 3.01V19c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2v-6.58l3 2.99 4-4 4 4 4-4.99z'/%3E%3C/svg%3E";
284
+ var SmartImg = (props) => {
285
+ injectStyles();
286
+ const { alt, opacity, className } = props;
287
+ const { dataSrc, originalW, originalH } = "asset" in props ? {
288
+ dataSrc: props.asset.src,
289
+ originalW: props.asset.originalW,
290
+ originalH: props.asset.originalH
291
+ } : {
292
+ dataSrc: props.dataSrc,
293
+ originalW: props.originalW,
294
+ originalH: props.originalH
295
+ };
296
+ const wrapperRef = useRef(null);
297
+ const imgRef = useRef(null);
298
+ const [imgSrc, setImgSrc] = useState(() => {
299
+ if (typeof window === "undefined") return BLANK;
300
+ return getLoadedUrl(dataSrc)?.url ?? BLANK;
301
+ });
302
+ const [isVisible, setIsVisible] = useState(() => {
303
+ if (typeof window === "undefined") return false;
304
+ return getLoadedUrl(dataSrc) !== null;
305
+ });
306
+ const [isError, setIsError] = useState(false);
307
+ const hasRealSrc = useRef(
308
+ typeof window !== "undefined" && getLoadedUrl(dataSrc) !== null
309
+ );
310
+ const paddingTop = `${(originalH / originalW * 100).toFixed(4)}%`;
311
+ useLayoutEffect(() => {
312
+ const hit = getLoadedUrl(dataSrc);
313
+ if (!hit) return;
314
+ hasRealSrc.current = true;
315
+ setImgSrc(hit.url);
316
+ setIsVisible(true);
317
+ setIsError(false);
318
+ }, [dataSrc]);
319
+ useEffect(() => {
320
+ const img = imgRef.current;
321
+ const wrapper = wrapperRef.current;
322
+ if (!img || !wrapper) return;
323
+ if (!getLoadedUrl(dataSrc)) {
324
+ hasRealSrc.current = false;
325
+ setImgSrc(BLANK);
326
+ setIsVisible(false);
327
+ setIsError(false);
328
+ }
329
+ return register(
330
+ img,
331
+ wrapper,
332
+ dataSrc,
333
+ originalW,
334
+ (url) => {
335
+ hasRealSrc.current = true;
336
+ setIsError(false);
337
+ setImgSrc(url);
338
+ if (!opacity) setIsVisible(true);
339
+ },
340
+ () => {
341
+ hasRealSrc.current = true;
342
+ setIsError(true);
343
+ setImgSrc(ERROR_PLACEHOLDER);
344
+ setIsVisible(true);
345
+ }
346
+ );
347
+ }, [dataSrc, opacity, originalW, originalH]);
348
+ const imageClassName = [
349
+ cls.image,
350
+ opacity ? cls.fade : "",
351
+ isVisible ? cls.visible : "",
352
+ isError ? cls.error : ""
353
+ ].filter(Boolean).join(" ");
354
+ return /* @__PURE__ */ jsx(
355
+ "div",
356
+ {
357
+ ref: wrapperRef,
358
+ className: `${cls.wrapper}${className ? ` ${className}` : ""}`,
359
+ style: { paddingTop },
360
+ children: /* @__PURE__ */ jsx(
361
+ "img",
362
+ {
363
+ ref: imgRef,
364
+ src: imgSrc,
365
+ alt,
366
+ "data-src": dataSrc,
367
+ "data-original-w": originalW,
368
+ "data-original-h": originalH,
369
+ className: imageClassName,
370
+ onLoad: () => {
371
+ if (opacity && hasRealSrc.current) {
372
+ setIsVisible(true);
373
+ }
374
+ },
375
+ decoding: "async"
376
+ }
377
+ )
378
+ }
379
+ );
380
+ };
381
+ export {
382
+ SmartImg
383
+ };
384
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/component/SmartImg.tsx","../src/component/smartImgManager.ts","../src/component/styles.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef, useState } from 'react'\r\nimport type { SmartImgImportedAsset } from '../smartImgAsset'\r\nimport { register, getLoadedUrl } from './smartImgManager'\r\nimport { injectStyles, cls } from './styles'\r\n\r\n/** 1×1 прозрачный GIF — начальный src пока картинка не загружена */\r\nconst BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'\r\n\r\n/** SVG-заглушка при ошибке загрузки */\r\nconst ERROR_PLACEHOLDER =\r\n \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect width='24' height='24' fill='%2312121c'/%3E%3Cpath fill='%23444466' d='M21 5v6.59l-3-3.01-4 4.01-4-4-4 3.99-3-2.99V5c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2zm-3 6.42 3 3.01V19c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2v-6.58l3 2.99 4-4 4 4 4-4.99z'/%3E%3C/svg%3E\"\r\n\r\ntype SmartImgCommon = {\r\n alt: string\r\n /**\r\n * Передай `opacity`, чтобы картинка появлялась плавно (fade-in).\r\n * Без этого пропа — появляется мгновенно.\r\n * При перемонтировании с закешированной картинкой fade не запускается.\r\n */\r\n opacity?: boolean\r\n /** Доп. класс на обёртку. */\r\n className?: string\r\n}\r\n\r\nexport type SmartImgProps = SmartImgCommon &\r\n (\r\n | {\r\n /** Импорт из `.../assets/*` через плагины пакета (`{ src, originalW, originalH }`). */\r\n asset: SmartImgImportedAsset\r\n }\r\n | {\r\n /** Прямой URL (часто внешний) — размеры задаёшь вручную. */\r\n dataSrc: string\r\n originalW: number\r\n originalH: number\r\n }\r\n )\r\n\r\n/**\r\n * «Умная» ленивая загрузка изображений (SmartImg).\r\n *\r\n * - Локальные ассеты: проп **`asset`** из импорта с метаданными из Vite.\r\n * - Внешние URL: **`dataSrc`** + **`originalW`** / **`originalH`**.\r\n */\r\nexport const SmartImg = (props: SmartImgProps) => {\r\n injectStyles()\r\n\r\n const { alt, opacity, className } = props\r\n const { dataSrc, originalW, originalH } =\r\n 'asset' in props\r\n ? {\r\n dataSrc: props.asset.src,\r\n originalW: props.asset.originalW,\r\n originalH: props.asset.originalH,\r\n }\r\n : {\r\n dataSrc: props.dataSrc,\r\n originalW: props.originalW,\r\n originalH: props.originalH,\r\n }\r\n\r\n const wrapperRef = useRef<HTMLDivElement>(null)\r\n const imgRef = useRef<HTMLImageElement>(null)\r\n\r\n const [imgSrc, setImgSrc] = useState<string>(() => {\r\n if (typeof window === 'undefined') return BLANK\r\n return getLoadedUrl(dataSrc)?.url ?? BLANK\r\n })\r\n const [isVisible, setIsVisible] = useState<boolean>(() => {\r\n if (typeof window === 'undefined') return false\r\n return getLoadedUrl(dataSrc) !== null\r\n })\r\n const [isError, setIsError] = useState(false)\r\n\r\n const hasRealSrc = useRef(\r\n typeof window !== 'undefined' && getLoadedUrl(dataSrc) !== null,\r\n )\r\n\r\n const paddingTop = `${((originalH / originalW) * 100).toFixed(4)}%`\r\n\r\n useLayoutEffect(() => {\r\n const hit = getLoadedUrl(dataSrc)\r\n if (!hit) return\r\n hasRealSrc.current = true\r\n setImgSrc(hit.url)\r\n setIsVisible(true)\r\n setIsError(false)\r\n }, [dataSrc])\r\n\r\n useEffect(() => {\r\n const img = imgRef.current\r\n const wrapper = wrapperRef.current\r\n if (!img || !wrapper) return\r\n\r\n if (!getLoadedUrl(dataSrc)) {\r\n hasRealSrc.current = false\r\n setImgSrc(BLANK)\r\n setIsVisible(false)\r\n setIsError(false)\r\n }\r\n\r\n return register(\r\n img,\r\n wrapper,\r\n dataSrc,\r\n originalW,\r\n (url) => {\r\n hasRealSrc.current = true\r\n setIsError(false)\r\n setImgSrc(url)\r\n if (!opacity) setIsVisible(true)\r\n },\r\n () => {\r\n hasRealSrc.current = true\r\n setIsError(true)\r\n setImgSrc(ERROR_PLACEHOLDER)\r\n setIsVisible(true)\r\n },\r\n )\r\n }, [dataSrc, opacity, originalW, originalH])\r\n\r\n const imageClassName = [\r\n cls.image,\r\n opacity ? cls.fade : '',\r\n isVisible ? cls.visible : '',\r\n isError ? cls.error : '',\r\n ]\r\n .filter(Boolean)\r\n .join(' ')\r\n\r\n return (\r\n <div\r\n ref={wrapperRef}\r\n className={`${cls.wrapper}${className ? ` ${className}` : ''}`}\r\n style={{ paddingTop }}\r\n >\r\n <img\r\n ref={imgRef}\r\n src={imgSrc}\r\n alt={alt}\r\n data-src={dataSrc}\r\n data-original-w={originalW}\r\n data-original-h={originalH}\r\n className={imageClassName}\r\n onLoad={() => {\r\n if (opacity && hasRealSrc.current) {\r\n setIsVisible(true)\r\n }\r\n }}\r\n decoding=\"async\"\r\n />\r\n </div>\r\n )\r\n}\r\n","/**\r\n * Singleton-менеджер ленивой загрузки изображений (SmartImg).\r\n *\r\n * Один IntersectionObserver и один resize-listener на всю страницу.\r\n * Все компоненты SmartImg регистрируются здесь через register().\r\n */\r\n\r\n// ─── Режим сборки ─────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * В dev-режиме миниатюр нет — грузим оригинал напрямую.\r\n * Вся логика выбора размера, WebP и resize-апгрейдов пропускается.\r\n * Значение подставляется Vite из import.meta.env.DEV в проекте-потребителе.\r\n */\r\nconst IS_DEV: boolean =\r\n typeof import.meta !== 'undefined' &&\r\n typeof (import.meta as Record<string, unknown>).env === 'object' &&\r\n ((import.meta as { env: Record<string, unknown> }).env['DEV'] as boolean | undefined) === true\r\n\r\n// ─── Доступные размеры миниатюр (ширина в пикселях) ──────────────────────────\r\n\r\nconst SIZES = [400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000, 3500, 4000, 5000, 6000, 7000, 8000] as const\r\n\r\n// ─── WebP detection (однократная синхронная проверка через canvas) ────────────\r\n\r\nlet _webpResult: boolean | null = null\r\n\r\nfunction supportsWebP(): boolean {\r\n if (_webpResult !== null) return _webpResult\r\n try {\r\n const c = document.createElement('canvas')\r\n c.width = 1\r\n c.height = 1\r\n _webpResult = c.toDataURL('image/webp').startsWith('data:image/webp')\r\n } catch {\r\n _webpResult = false\r\n }\r\n return _webpResult\r\n}\r\n\r\n// ─── URL helpers ──────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Выбирает размер миниатюры с учётом нужного размера И оригинальных размеров.\r\n *\r\n * Правило: берём первый size из SIZES такой что:\r\n * size >= target (покрывает нужное разрешение)\r\n * size <= originalW (миниатюра с таким размером физически существует)\r\n *\r\n * null означает: грузить оригинальный dataSrc напрямую.\r\n */\r\nfunction pickSize(cssWidth: number, dpr: number, originalW: number): number | null {\r\n const target = Math.ceil(cssWidth * dpr)\r\n return SIZES.find((s) => s >= target && s <= originalW) ?? null\r\n}\r\n\r\n/**\r\n * Строит URL миниатюры из оригинального URL.\r\n * photo.jpg + 1200 + webp → photo-1200.webp\r\n * photo.jpg + 1200 + false → photo-1200.jpg\r\n */\r\nfunction buildUrl(dataSrc: string, size: number, webp: boolean): string {\r\n const base = dataSrc.replace(/\\.[^./]+$/, '')\r\n const originalExt = dataSrc.match(/\\.([^./]+)$/)?.[1] ?? 'jpg'\r\n return `${base}-${size}.${webp ? 'webp' : originalExt}`\r\n}\r\n\r\n/**\r\n * SVG: миниатюры и плагин нарезки не применяются — только оригинал.\r\n * data:... и путь с `.svg` до query/hash.\r\n */\r\nfunction isSvgDataSrc(url: string): boolean {\r\n if (url == null || typeof url !== 'string') return false\r\n if (url.startsWith('data:image/svg')) return true\r\n const base = url.replace(/[?#].*$/, '')\r\n return base.toLowerCase().endsWith('.svg')\r\n}\r\n\r\n/**\r\n * Абсолютный URL с origin, отличным от страницы: нет наших прегенерённых миниатюр.\r\n * Относительные (`/`, `./`, `../`), `data:` и `blob:` не считаем внешними.\r\n */\r\nfunction isExternalDataSrc(url: string): boolean {\r\n if (!url || url.startsWith('data:') || url.startsWith('blob:')) return false\r\n if (url.startsWith('/') || url.startsWith('./') || url.startsWith('../')) return false\r\n try {\r\n const resolved = new URL(url, window.location.href)\r\n return resolved.origin !== window.location.origin\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\n/** Картинка без цепочки миниатюр SmartImg: сразу оригинал и финальная пометка. */\r\nfunction directLoadKind(dataSrc: string): 'svg' | 'external' | null {\r\n if (isSvgDataSrc(dataSrc)) return 'svg'\r\n if (isExternalDataSrc(dataSrc)) return 'external'\r\n return null\r\n}\r\n\r\n/**\r\n * Загрузка только `dataSrc`, без buildUrl / выбора размера / WebP.\r\n * Вызывается из handleIntersect (после пересечения viewport, threshold 5%).\r\n * На успех: кэш, onLoad, `data-max-size-load` = svg | external.\r\n */\r\nfunction loadDirectFinal(img: HTMLImageElement, state: Entry, kind: 'svg' | 'external'): void {\r\n const url = state.dataSrc\r\n const tempImg = new Image()\r\n tempImg.onload = () => {\r\n state.loadedSize = Infinity\r\n loadedCache.set(state.dataSrc, { url, size: Infinity })\r\n state.onLoad(url)\r\n img.setAttribute('data-max-size-load', kind)\r\n }\r\n tempImg.onerror = () => {\r\n state.onError()\r\n img.setAttribute('data-max-size-load', `${kind}-error`)\r\n }\r\n tempImg.src = url\r\n}\r\n\r\n/**\r\n * Нужен ли IntersectionObserver для этого dataSrc: false = уже есть подходящая\r\n * версия в loadedCache, повторный `new Image()` и ожидание не требуются.\r\n */\r\nfunction needsIntersectionObserver(\r\n dataSrc: string,\r\n originalW: number,\r\n wrapper: HTMLElement,\r\n cached: CacheEntry | undefined,\r\n): boolean {\r\n if (!cached) return true\r\n if (directLoadKind(dataSrc)) return false\r\n if (IS_DEV) return false\r\n const cssWidth = wrapper.clientWidth || wrapper.offsetWidth\r\n if (cssWidth === 0) return true\r\n const neededSize = pickSize(cssWidth, window.devicePixelRatio ?? 1, originalW)\r\n const loaded = cached.size\r\n if (neededSize === null) {\r\n return loaded !== Infinity\r\n }\r\n return neededSize > loaded\r\n}\r\n\r\n/** Пометить завершённую загрузку по данным кэша (без повторного IO). */\r\nfunction applyCachedCompleteMarker(img: HTMLImageElement, dataSrc: string, cached: CacheEntry): void {\r\n if (img.hasAttribute('data-max-size-load')) return\r\n const direct = directLoadKind(dataSrc)\r\n if (direct) {\r\n img.setAttribute('data-max-size-load', direct)\r\n return\r\n }\r\n if (IS_DEV) {\r\n img.setAttribute('data-max-size-load', 'dev')\r\n return\r\n }\r\n const s = cached.size\r\n img.setAttribute('data-max-size-load', s === Infinity ? 'Infinity' : String(s))\r\n}\r\n\r\n// ─── Кэш загруженных URL ──────────────────────────────────────────────────────\r\n\r\ntype CacheEntry = {\r\n /** Финальный URL который был подставлен в src (миниатюра или оригинал) */\r\n url: string\r\n /** Размер загруженной версии. Infinity = оригинал */\r\n size: number\r\n}\r\n\r\n/**\r\n * Модульный кэш: dataSrc → последний успешно загруженный url + size.\r\n * Живёт в рамках сессии страницы. При перемонтировании компонента\r\n * позволяет сразу показать картинку без fade, не дожидаясь IO.\r\n */\r\nconst loadedCache = new Map<string, CacheEntry>()\r\n\r\n/** Возвращает кэшированный результат для dataSrc, или null если ещё не грузили. */\r\nexport function getLoadedUrl(dataSrc: string): CacheEntry | null {\r\n return loadedCache.get(dataSrc) ?? null\r\n}\r\n\r\n// ─── Типы ─────────────────────────────────────────────────────────────────────\r\n\r\ntype Entry = {\r\n wrapper: HTMLElement\r\n dataSrc: string\r\n /** Оригинальная ширина картинки — ограничивает максимально доступный размер миниатюры */\r\n originalW: number\r\n /** Размер уже загруженной версии. null — картинка ещё не загружалась. */\r\n loadedSize: number | null\r\n /**\r\n * true — картинка была повторно добавлена в observer после resize,\r\n * ждём подтверждения видимости чтобы проверить нужен ли апгрейд.\r\n */\r\n pendingResizeCheck: boolean\r\n onLoad: (url: string) => void\r\n onError: () => void\r\n}\r\n\r\n// ─── Singleton state ──────────────────────────────────────────────────────────\r\n\r\nconst registry = new Map<HTMLImageElement, Entry>()\r\n\r\n/** Элементы, которые сейчас отслеживает IntersectionObserver. */\r\nconst observing = new Set<HTMLImageElement>()\r\n\r\nlet ioInstance: IntersectionObserver | null = null\r\nlet resizeTimer: ReturnType<typeof setTimeout> | null = null\r\nlet resizeAttached = false\r\n\r\n// ─── IntersectionObserver ─────────────────────────────────────────────────────\r\n\r\nfunction getIO(): IntersectionObserver {\r\n if (!ioInstance) {\r\n ioInstance = new IntersectionObserver(handleIntersect, { threshold: 0.05 })\r\n }\r\n return ioInstance\r\n}\r\n\r\n/** Добавляет в observer без дублей. */\r\nfunction safeObserve(img: HTMLImageElement): void {\r\n if (observing.has(img)) return\r\n observing.add(img)\r\n getIO().observe(img)\r\n}\r\n\r\nfunction safeUnobserve(img: HTMLImageElement): void {\r\n if (!observing.has(img)) return\r\n observing.delete(img)\r\n getIO().unobserve(img)\r\n}\r\n\r\nfunction handleIntersect(entries: IntersectionObserverEntry[]): void {\r\n for (const ioEntry of entries) {\r\n if (!ioEntry.isIntersecting) continue\r\n\r\n const img = ioEntry.target as HTMLImageElement\r\n const state = registry.get(img)\r\n if (!state) continue\r\n\r\n safeUnobserve(img)\r\n\r\n const direct = directLoadKind(state.dataSrc)\r\n if (direct) {\r\n loadDirectFinal(img, state, direct)\r\n continue\r\n }\r\n\r\n // ── Dev mode: миниатюр нет, грузим оригинал ──────────────────────────────\r\n if (IS_DEV) {\r\n const hit = loadedCache.get(state.dataSrc)\r\n if (hit) {\r\n // Уже в кэше (повторный mount / ре-рендер грида): не дергаем onLoad и\r\n // повторный decode — иначе мигает fade и лишняя перерисовка <img>.\r\n state.loadedSize = hit.size\r\n if (!img.hasAttribute('data-max-size-load')) {\r\n img.setAttribute('data-max-size-load', 'dev')\r\n }\r\n continue\r\n }\r\n const url = state.dataSrc\r\n const tempImg = new Image()\r\n tempImg.onload = () => {\r\n state.loadedSize = Infinity\r\n loadedCache.set(state.dataSrc, { url, size: Infinity })\r\n state.onLoad(url)\r\n img.setAttribute('data-max-size-load', 'dev')\r\n }\r\n tempImg.onerror = () => state.onError()\r\n tempImg.src = url\r\n continue\r\n }\r\n\r\n const cssWidth = state.wrapper.clientWidth || state.wrapper.offsetWidth\r\n if (cssWidth === 0) {\r\n safeObserve(img)\r\n continue\r\n }\r\n\r\n const neededSize = pickSize(cssWidth, window.devicePixelRatio ?? 1, state.originalW)\r\n const isResizeCheck = state.pendingResizeCheck\r\n state.pendingResizeCheck = false\r\n\r\n const url = neededSize === null\r\n ? state.dataSrc\r\n : buildUrl(state.dataSrc, neededSize, supportsWebP())\r\n\r\n if (isResizeCheck && neededSize !== null && state.loadedSize !== null && neededSize <= state.loadedSize) {\r\n img.setAttribute('data-max-size-load', String(state.loadedSize))\r\n return\r\n }\r\n\r\n const tempImg = new Image()\r\n\r\n tempImg.onload = () => {\r\n state.loadedSize = neededSize ?? Infinity\r\n loadedCache.set(state.dataSrc, { url, size: state.loadedSize })\r\n state.onLoad(url)\r\n if (isResizeCheck || neededSize === null) {\r\n img.setAttribute('data-max-size-load', String(state.loadedSize))\r\n }\r\n }\r\n\r\n tempImg.onerror = () => {\r\n state.onError()\r\n if (isResizeCheck || neededSize === null) {\r\n img.setAttribute('data-max-size-load', String(state.loadedSize ?? 0))\r\n }\r\n }\r\n\r\n tempImg.src = url\r\n }\r\n}\r\n\r\n// ─── Resize handler ───────────────────────────────────────────────────────────\r\n\r\nfunction handleResize(): void {\r\n if (IS_DEV) return\r\n\r\n for (const [img, state] of registry) {\r\n if (state.loadedSize === null) continue\r\n if (img.hasAttribute('data-max-size-load')) continue\r\n\r\n state.pendingResizeCheck = true\r\n safeObserve(img)\r\n }\r\n}\r\n\r\nfunction attachResizeListener(): void {\r\n if (resizeAttached) return\r\n resizeAttached = true\r\n window.addEventListener(\r\n 'resize',\r\n () => {\r\n if (resizeTimer) clearTimeout(resizeTimer)\r\n resizeTimer = setTimeout(handleResize, 200)\r\n },\r\n { passive: true },\r\n )\r\n}\r\n\r\n// ─── Public API ───────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Регистрирует img-элемент в менеджере ленивой загрузки.\r\n * Возвращает функцию очистки — вызвать при размонтировании компонента.\r\n */\r\nexport function register(\r\n img: HTMLImageElement,\r\n wrapper: HTMLElement,\r\n dataSrc: string,\r\n originalW: number,\r\n onLoad: (url: string) => void,\r\n onError: () => void,\r\n): () => void {\r\n if (dataSrc == null || dataSrc === '' || typeof dataSrc !== 'string') {\r\n return () => {\r\n safeUnobserve(img)\r\n registry.delete(img)\r\n }\r\n }\r\n\r\n attachResizeListener()\r\n\r\n const cached = loadedCache.get(dataSrc)\r\n const observe = needsIntersectionObserver(dataSrc, originalW, wrapper, cached)\r\n const pendingResizeCheck = Boolean(cached) && observe && !IS_DEV && !directLoadKind(dataSrc)\r\n\r\n registry.set(img, {\r\n wrapper,\r\n dataSrc,\r\n originalW,\r\n loadedSize: cached?.size ?? null,\r\n pendingResizeCheck,\r\n onLoad,\r\n onError,\r\n })\r\n\r\n const cleanup = () => {\r\n safeUnobserve(img)\r\n registry.delete(img)\r\n }\r\n\r\n if (cached && !observe) {\r\n applyCachedCompleteMarker(img, dataSrc, cached)\r\n return cleanup\r\n }\r\n\r\n safeObserve(img)\r\n\r\n return cleanup\r\n}\r\n","/**\n * Runtime CSS injection for SmartImg.\n * Injects styles once into <head> on first component use.\n * Uses `smi-` prefix to avoid class name collisions.\n */\n\nconst CSS = `\n.smi-wrapper {\n position: relative;\n width: 100%;\n height: 0;\n overflow: hidden;\n background-color: var(--smi-bg, #f0f0f0);\n}\n.smi-image {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.smi-fade {\n opacity: 0;\n transition: opacity var(--smi-fade-duration, 400ms) ease;\n}\n.smi-fade.smi-visible {\n opacity: 1;\n}\n.smi-error {\n object-fit: contain;\n padding: 15%;\n}\n`\n\nlet injected = false\n\nexport function injectStyles(): void {\n if (typeof document === 'undefined' || injected) return\n injected = true\n const style = document.createElement('style')\n style.setAttribute('data-smi', '')\n style.textContent = CSS\n document.head.appendChild(style)\n}\n\nexport const cls = {\n wrapper: 'smi-wrapper',\n image: 'smi-image',\n fade: 'smi-fade',\n visible: 'smi-visible',\n error: 'smi-error',\n} as const\n"],"mappings":"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","names":["url","tempImg"]}