xxf_react 0.7.7 → 0.8.2
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/dist/layout/image/XImage.d.ts +38 -15
- package/dist/layout/image/XImage.d.ts.map +1 -1
- package/dist/layout/image/XImage.js +243 -32
- package/dist/layout/image/XImage.types.d.ts +110 -0
- package/dist/layout/image/XImage.types.d.ts.map +1 -0
- package/dist/layout/image/XImage.types.js +2 -0
- package/dist/layout/image/index.d.ts +3 -0
- package/dist/layout/image/index.d.ts.map +1 -0
- package/dist/layout/image/index.js +3 -0
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +1 -1
- package/package.json +3 -2
|
@@ -1,19 +1,42 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
export interface XImageProps extends ImageProps {
|
|
4
|
-
/** 图片加载失败时显示的备用图片 URL 或自定义组件
|
|
5
|
-
* 可选:urlPlaceholder函数来生成失败的占位图
|
|
6
|
-
*/
|
|
7
|
-
fallback?: string | ReactNode;
|
|
8
|
-
}
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { XImageProps } from "./XImage.types";
|
|
9
3
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
4
|
+
* 生产级图片组件
|
|
5
|
+
*
|
|
6
|
+
* 基于 next/image 实现,支持:
|
|
7
|
+
* - 双层渲染:placeholder 先加载,主图加载完成后 blur 过渡
|
|
8
|
+
* - 自动图片优化(Next.js 内置)
|
|
9
|
+
* - 错误处理 + 自动重试
|
|
10
|
+
* - fallback 备用图/组件
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* // 基础用法
|
|
15
|
+
* <XImage src="/photo.jpg" alt="Photo" width={400} height={300} />
|
|
16
|
+
*
|
|
17
|
+
* // 带 placeholder 模糊过渡(推荐使用小尺寸缩略图,如 w=20)
|
|
18
|
+
* <XImage
|
|
19
|
+
* src="/photo.jpg?w=800"
|
|
20
|
+
* placeholder="/photo.jpg?w=20"
|
|
21
|
+
* width={400}
|
|
22
|
+
* height={300}
|
|
23
|
+
* />
|
|
24
|
+
*
|
|
25
|
+
* // 带错误处理和重试
|
|
26
|
+
* <XImage
|
|
27
|
+
* src="/photo.jpg"
|
|
28
|
+
* fallback="/fallback.jpg"
|
|
29
|
+
* retryCount={2}
|
|
30
|
+
* retryDelay={1000}
|
|
31
|
+
* />
|
|
32
|
+
*
|
|
33
|
+
* // 自定义占位组件
|
|
34
|
+
* <XImage
|
|
35
|
+
* src="/photo.jpg"
|
|
36
|
+
* placeholder={<Skeleton className="w-full h-full" />}
|
|
37
|
+
* />
|
|
38
|
+
* ```
|
|
16
39
|
*/
|
|
17
|
-
export declare
|
|
40
|
+
export declare const XImage: React.NamedExoticComponent<XImageProps & React.RefAttributes<HTMLDivElement | HTMLImageElement>>;
|
|
18
41
|
export default XImage;
|
|
19
42
|
//# sourceMappingURL=XImage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"XImage.d.ts","sourceRoot":"","sources":["../../../src/layout/image/XImage.tsx"],"names":[],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"XImage.d.ts","sourceRoot":"","sources":["../../../src/layout/image/XImage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,gBAAgB,CAAC;AAmBhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,MAAM,kGA4TlB,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -1,45 +1,256 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import React, { useState, useEffect, useCallback, useRef, forwardRef, memo, useMemo, } from "react";
|
|
2
3
|
import Image from "next/image";
|
|
3
|
-
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// 常量定义
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/** 过渡动画时长 */
|
|
8
|
+
const TRANSITION_DURATION = "0.3s";
|
|
9
|
+
/** 占位图模糊程度 */
|
|
10
|
+
const PLACEHOLDER_BLUR = "20px";
|
|
11
|
+
/** 占位图放大比例(防止模糊边缘露出) */
|
|
12
|
+
const PLACEHOLDER_SCALE = 1.1;
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// 组件实现
|
|
15
|
+
// ============================================================================
|
|
4
16
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
17
|
+
* 生产级图片组件
|
|
18
|
+
*
|
|
19
|
+
* 基于 next/image 实现,支持:
|
|
20
|
+
* - 双层渲染:placeholder 先加载,主图加载完成后 blur 过渡
|
|
21
|
+
* - 自动图片优化(Next.js 内置)
|
|
22
|
+
* - 错误处理 + 自动重试
|
|
23
|
+
* - fallback 备用图/组件
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* // 基础用法
|
|
28
|
+
* <XImage src="/photo.jpg" alt="Photo" width={400} height={300} />
|
|
29
|
+
*
|
|
30
|
+
* // 带 placeholder 模糊过渡(推荐使用小尺寸缩略图,如 w=20)
|
|
31
|
+
* <XImage
|
|
32
|
+
* src="/photo.jpg?w=800"
|
|
33
|
+
* placeholder="/photo.jpg?w=20"
|
|
34
|
+
* width={400}
|
|
35
|
+
* height={300}
|
|
36
|
+
* />
|
|
37
|
+
*
|
|
38
|
+
* // 带错误处理和重试
|
|
39
|
+
* <XImage
|
|
40
|
+
* src="/photo.jpg"
|
|
41
|
+
* fallback="/fallback.jpg"
|
|
42
|
+
* retryCount={2}
|
|
43
|
+
* retryDelay={1000}
|
|
44
|
+
* />
|
|
45
|
+
*
|
|
46
|
+
* // 自定义占位组件
|
|
47
|
+
* <XImage
|
|
48
|
+
* src="/photo.jpg"
|
|
49
|
+
* placeholder={<Skeleton className="w-full h-full" />}
|
|
50
|
+
* />
|
|
51
|
+
* ```
|
|
11
52
|
*/
|
|
12
|
-
export function XImage({
|
|
13
|
-
|
|
53
|
+
export const XImage = memo(forwardRef(function XImage({
|
|
54
|
+
// -------- XImage 扩展属性 --------
|
|
55
|
+
placeholder, fallback, retryCount = 0, retryDelay = 1000, onStatusChange, wrapperProps, wrapperClassName,
|
|
56
|
+
// -------- next/image 常用属性(需要特殊处理) --------
|
|
57
|
+
src, alt = "", fill, style, className, onLoad, onError,
|
|
58
|
+
// -------- next/image 其余属性(透传) --------
|
|
59
|
+
...imageProps }, ref) {
|
|
60
|
+
// ========================================================================
|
|
61
|
+
// 解析 placeholder 类型
|
|
62
|
+
// ========================================================================
|
|
63
|
+
const placeholderSrc = typeof placeholder === "string" ? placeholder : undefined;
|
|
64
|
+
const placeholderElement = typeof placeholder !== "string" ? placeholder : undefined;
|
|
65
|
+
const hasPlaceholder = Boolean(placeholderSrc || placeholderElement);
|
|
66
|
+
// ========================================================================
|
|
67
|
+
// 状态管理
|
|
68
|
+
// ========================================================================
|
|
69
|
+
const [currentSrc, setCurrentSrc] = useState(src);
|
|
70
|
+
const [status, setStatus] = useState("idle");
|
|
14
71
|
const [hasError, setHasError] = useState(false);
|
|
72
|
+
const [retryAttempt, setRetryAttempt] = useState(0);
|
|
73
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
74
|
+
// ========================================================================
|
|
75
|
+
// Refs
|
|
76
|
+
// ========================================================================
|
|
77
|
+
const wrapperRef = useRef(null);
|
|
78
|
+
const mainImageRef = useRef(null);
|
|
79
|
+
const retryTimerRef = useRef(null);
|
|
80
|
+
// 使用 ref 存储回调,避免 useCallback 依赖问题
|
|
81
|
+
const callbacksRef = useRef({ onStatusChange, onLoad, onError });
|
|
82
|
+
callbacksRef.current = { onStatusChange, onLoad, onError };
|
|
83
|
+
// ========================================================================
|
|
84
|
+
// 状态更新辅助函数
|
|
85
|
+
// ========================================================================
|
|
86
|
+
const updateStatus = useCallback((newStatus) => {
|
|
87
|
+
var _a, _b;
|
|
88
|
+
setStatus(newStatus);
|
|
89
|
+
(_b = (_a = callbacksRef.current).onStatusChange) === null || _b === void 0 ? void 0 : _b.call(_a, newStatus);
|
|
90
|
+
}, []);
|
|
91
|
+
// ========================================================================
|
|
15
92
|
// src 变化时重置状态
|
|
93
|
+
// ========================================================================
|
|
16
94
|
useEffect(() => {
|
|
17
|
-
|
|
95
|
+
setCurrentSrc(src);
|
|
18
96
|
setHasError(false);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
97
|
+
setRetryAttempt(0);
|
|
98
|
+
setIsLoaded(false);
|
|
99
|
+
updateStatus("loading");
|
|
100
|
+
// 清理重试定时器
|
|
101
|
+
if (retryTimerRef.current) {
|
|
102
|
+
clearTimeout(retryTimerRef.current);
|
|
103
|
+
retryTimerRef.current = null;
|
|
104
|
+
}
|
|
105
|
+
}, [src, updateStatus]);
|
|
106
|
+
// 组件卸载时清理定时器
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
return () => {
|
|
109
|
+
if (retryTimerRef.current) {
|
|
110
|
+
clearTimeout(retryTimerRef.current);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}, []);
|
|
114
|
+
// ========================================================================
|
|
115
|
+
// ref 转发:将内部 img 元素暴露给外部
|
|
116
|
+
// ========================================================================
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (!ref || !wrapperRef.current)
|
|
119
|
+
return;
|
|
120
|
+
// 获取主图 img 元素(双层渲染时取第二个,单层取第一个)
|
|
121
|
+
const imgs = wrapperRef.current.querySelectorAll("img");
|
|
122
|
+
const targetImg = imgs.length > 1 ? imgs[1] : imgs[0];
|
|
123
|
+
if (targetImg) {
|
|
124
|
+
if (typeof ref === "function") {
|
|
125
|
+
ref(targetImg);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
ref.current = targetImg;
|
|
27
129
|
}
|
|
28
130
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
131
|
+
}, [ref, isLoaded]); // isLoaded 变化时重新获取,确保 img 已渲染
|
|
132
|
+
// ========================================================================
|
|
133
|
+
// 事件处理
|
|
134
|
+
// ========================================================================
|
|
135
|
+
/**
|
|
136
|
+
* 主图加载成功
|
|
137
|
+
*/
|
|
138
|
+
const handleLoad = useCallback((e) => {
|
|
139
|
+
var _a, _b;
|
|
140
|
+
setIsLoaded(true);
|
|
141
|
+
updateStatus("loaded");
|
|
142
|
+
(_b = (_a = callbacksRef.current).onLoad) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
143
|
+
}, [updateStatus]);
|
|
144
|
+
/**
|
|
145
|
+
* 主图加载失败 - 处理重试和 fallback 逻辑
|
|
146
|
+
*/
|
|
147
|
+
const handleError = useCallback((e) => {
|
|
148
|
+
setRetryAttempt((currentRetry) => {
|
|
149
|
+
// 还有重试次数,延迟后重试
|
|
150
|
+
if (currentRetry < retryCount) {
|
|
151
|
+
retryTimerRef.current = setTimeout(() => {
|
|
152
|
+
// 添加时间戳绕过缓存
|
|
153
|
+
const srcStr = typeof src === "string" ? src : "";
|
|
154
|
+
const separator = srcStr.includes("?") ? "&" : "?";
|
|
155
|
+
setCurrentSrc(`${srcStr}${separator}_retry=${Date.now()}`);
|
|
156
|
+
}, retryDelay);
|
|
157
|
+
return currentRetry + 1;
|
|
158
|
+
}
|
|
159
|
+
// 重试耗尽,尝试 fallback
|
|
160
|
+
setHasError((currentHasError) => {
|
|
161
|
+
var _a, _b;
|
|
162
|
+
if (fallback && !currentHasError) {
|
|
163
|
+
if (typeof fallback === "string") {
|
|
164
|
+
setCurrentSrc(fallback);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
// fallback 是 ReactNode,标记错误状态
|
|
168
|
+
updateStatus("error");
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
// 无 fallback 或已经在 fallback 状态,触发错误回调
|
|
172
|
+
updateStatus("error");
|
|
173
|
+
(_b = (_a = callbacksRef.current).onError) === null || _b === void 0 ? void 0 : _b.call(_a, e);
|
|
174
|
+
return currentHasError;
|
|
175
|
+
});
|
|
176
|
+
return currentRetry;
|
|
177
|
+
});
|
|
178
|
+
}, [retryCount, retryDelay, src, fallback, updateStatus]);
|
|
179
|
+
// ========================================================================
|
|
180
|
+
// 合并 wrapperProps 的 ref
|
|
181
|
+
// ========================================================================
|
|
182
|
+
const mergedWrapperRef = useCallback((node) => {
|
|
183
|
+
wrapperRef.current = node;
|
|
184
|
+
// 调用用户传入的 ref
|
|
185
|
+
const userRef = wrapperProps === null || wrapperProps === void 0 ? void 0 : wrapperProps.ref;
|
|
186
|
+
if (userRef) {
|
|
187
|
+
if (typeof userRef === "function") {
|
|
188
|
+
userRef(node);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
userRef.current = node;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}, [wrapperProps]);
|
|
195
|
+
// ========================================================================
|
|
196
|
+
// 计算样式(使用 useMemo 避免重复创建对象)
|
|
197
|
+
// ========================================================================
|
|
198
|
+
/** 容器样式 */
|
|
199
|
+
const wrapperStyle = useMemo(() => ({
|
|
200
|
+
position: "relative",
|
|
201
|
+
display: "inline-block",
|
|
202
|
+
overflow: "hidden",
|
|
203
|
+
...wrapperProps === null || wrapperProps === void 0 ? void 0 : wrapperProps.style,
|
|
204
|
+
}), [wrapperProps === null || wrapperProps === void 0 ? void 0 : wrapperProps.style]);
|
|
205
|
+
/** 占位图样式(带 blur 效果) */
|
|
206
|
+
const placeholderStyle = useMemo(() => ({
|
|
207
|
+
...style,
|
|
208
|
+
position: "absolute",
|
|
209
|
+
inset: 0,
|
|
210
|
+
width: "100%",
|
|
211
|
+
height: "100%",
|
|
212
|
+
transition: `opacity ${TRANSITION_DURATION} ease-in-out, filter ${TRANSITION_DURATION} ease-in-out`,
|
|
213
|
+
opacity: isLoaded ? 0 : 1,
|
|
214
|
+
filter: isLoaded ? "blur(0px)" : `blur(${PLACEHOLDER_BLUR})`,
|
|
215
|
+
transform: `scale(${PLACEHOLDER_SCALE})`,
|
|
216
|
+
pointerEvents: "none", // 防止占位图拦截事件
|
|
217
|
+
}), [style, isLoaded]);
|
|
218
|
+
/** 主图样式(加载完成后显示) */
|
|
219
|
+
const mainImageStyle = useMemo(() => ({
|
|
220
|
+
...style,
|
|
221
|
+
position: "absolute",
|
|
222
|
+
inset: 0,
|
|
223
|
+
width: "100%",
|
|
224
|
+
height: "100%",
|
|
225
|
+
transition: `opacity ${TRANSITION_DURATION} ease-in-out`,
|
|
226
|
+
opacity: isLoaded ? 1 : 0,
|
|
227
|
+
}), [style, isLoaded]);
|
|
228
|
+
// ========================================================================
|
|
229
|
+
// 渲染
|
|
230
|
+
// ========================================================================
|
|
231
|
+
// 错误状态 + ReactNode fallback
|
|
32
232
|
if (hasError && fallback && typeof fallback !== "string") {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
233
|
+
return (React.createElement("div", { ref: ref, className: wrapperClassName || className, style: { ...wrapperStyle, ...style }, ...wrapperProps }, fallback));
|
|
234
|
+
}
|
|
235
|
+
// 有 placeholder(URL 或 ReactElement):双层渲染
|
|
236
|
+
if (hasPlaceholder) {
|
|
237
|
+
return (React.createElement("div", { ...wrapperProps, ref: mergedWrapperRef, className: wrapperClassName, style: wrapperStyle },
|
|
238
|
+
placeholderSrc ? (
|
|
239
|
+
// URL 占位图:使用 next/image,priority 立即加载
|
|
240
|
+
React.createElement(Image, { src: placeholderSrc, alt: "" // 占位图不需要 alt(屏幕阅读器跳过)
|
|
241
|
+
, fill: true, priority: true, unoptimized: true, className: className, style: placeholderStyle, sizes: imageProps.sizes })) : (
|
|
242
|
+
// ReactElement 占位组件(如 Skeleton)
|
|
243
|
+
React.createElement("div", { style: {
|
|
244
|
+
position: "absolute",
|
|
245
|
+
inset: 0,
|
|
246
|
+
transition: `opacity ${TRANSITION_DURATION} ease-in-out`,
|
|
247
|
+
opacity: isLoaded ? 0 : 1,
|
|
248
|
+
pointerEvents: "none",
|
|
249
|
+
} }, placeholderElement)),
|
|
250
|
+
React.createElement(Image, { ...imageProps, ref: mainImageRef, src: currentSrc, alt: alt, fill: true, className: className, style: mainImageStyle, onLoad: handleLoad, onError: handleError })));
|
|
42
251
|
}
|
|
43
|
-
|
|
44
|
-
}
|
|
252
|
+
// 无 placeholder:单层渲染
|
|
253
|
+
return (React.createElement("div", { ...wrapperProps, ref: mergedWrapperRef, className: wrapperClassName, style: wrapperStyle },
|
|
254
|
+
React.createElement(Image, { ...imageProps, ref: mainImageRef, src: currentSrc, alt: alt, fill: fill, className: className, style: style, onLoad: handleLoad, onError: handleError })));
|
|
255
|
+
}));
|
|
45
256
|
export default XImage;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { ImageProps } from "next/image";
|
|
2
|
+
import type { ReactElement, ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* 图片加载状态
|
|
5
|
+
*
|
|
6
|
+
* - `idle`: 初始状态(未开始加载)
|
|
7
|
+
* - `loading`: 加载中
|
|
8
|
+
* - `loaded`: 加载成功
|
|
9
|
+
* - `error`: 加载失败(重试耗尽且无 fallback)
|
|
10
|
+
*/
|
|
11
|
+
export type XImageStatus = "idle" | "loading" | "loaded" | "error";
|
|
12
|
+
/**
|
|
13
|
+
* XImage 组件 Props
|
|
14
|
+
*
|
|
15
|
+
* 继承 next/image 的所有属性,扩展双层渲染和错误处理能力。
|
|
16
|
+
*
|
|
17
|
+
* ## 性能建议
|
|
18
|
+
*
|
|
19
|
+
* 1. **placeholder 使用小尺寸缩略图**:推荐 20-40px 宽度,体积 < 1KB
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <XImage
|
|
22
|
+
* src="/photo.jpg?w=800"
|
|
23
|
+
* placeholder="/photo.jpg?w=20"
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* 2. **首屏图片使用 priority**:跳过懒加载
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <XImage src="/hero.jpg" priority />
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* 3. **设置正确的 sizes**:避免加载过大图片
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <XImage
|
|
35
|
+
* src="/photo.jpg"
|
|
36
|
+
* sizes="(max-width: 768px) 100vw, 50vw"
|
|
37
|
+
* />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export interface XImageProps extends Omit<ImageProps, "placeholder" | "ref"> {
|
|
41
|
+
/**
|
|
42
|
+
* 占位内容(用于双层渲染 + blur 过渡)
|
|
43
|
+
*
|
|
44
|
+
* - `string`: 占位图片 URL(推荐使用小尺寸缩略图)
|
|
45
|
+
* - `ReactElement`: 自定义占位组件(如 Skeleton)
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* // URL 占位图(推荐)
|
|
50
|
+
* <XImage src="/photo.jpg" placeholder="/photo-tiny.jpg" />
|
|
51
|
+
*
|
|
52
|
+
* // 自定义 Skeleton
|
|
53
|
+
* <XImage src="/photo.jpg" placeholder={<Skeleton />} />
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
placeholder?: string | ReactElement | null;
|
|
57
|
+
/**
|
|
58
|
+
* 加载失败时显示的备用内容
|
|
59
|
+
*
|
|
60
|
+
* - `string`: 备用图片 URL(会尝试加载)
|
|
61
|
+
* - `ReactNode`: 自定义错误展示组件
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* // 备用图片
|
|
66
|
+
* <XImage src="/photo.jpg" fallback="/fallback.jpg" />
|
|
67
|
+
*
|
|
68
|
+
* // 自定义错误组件
|
|
69
|
+
* <XImage src="/photo.jpg" fallback={<ErrorPlaceholder />} />
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
fallback?: string | ReactNode;
|
|
73
|
+
/**
|
|
74
|
+
* 加载失败后的重试次数
|
|
75
|
+
*
|
|
76
|
+
* @default 0
|
|
77
|
+
*/
|
|
78
|
+
retryCount?: number;
|
|
79
|
+
/**
|
|
80
|
+
* 重试间隔(毫秒)
|
|
81
|
+
*
|
|
82
|
+
* @default 1000
|
|
83
|
+
*/
|
|
84
|
+
retryDelay?: number;
|
|
85
|
+
/**
|
|
86
|
+
* 加载状态变化回调
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```tsx
|
|
90
|
+
* <XImage
|
|
91
|
+
* src="/photo.jpg"
|
|
92
|
+
* onStatusChange={(status) => console.log('Status:', status)}
|
|
93
|
+
* />
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
onStatusChange?: (status: XImageStatus) => void;
|
|
97
|
+
/**
|
|
98
|
+
* 传递给包裹 div 元素的属性
|
|
99
|
+
*
|
|
100
|
+
* 用于自定义容器的事件处理、数据属性等
|
|
101
|
+
*/
|
|
102
|
+
wrapperProps?: React.HTMLAttributes<HTMLDivElement>;
|
|
103
|
+
/**
|
|
104
|
+
* 包裹元素的类名
|
|
105
|
+
*
|
|
106
|
+
* 与 className 区分:className 应用于 img,wrapperClassName 应用于外层 div
|
|
107
|
+
*/
|
|
108
|
+
wrapperClassName?: string;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=XImage.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XImage.types.d.ts","sourceRoot":"","sources":["../../../src/layout/image/XImage.types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,KAAK,CAAC;IACxE;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAAC;IAE3C;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAEhD;;;;OAIG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;IAEpD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/layout/image/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,UAAU,CAAC;AAG5D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/layout/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export * from './resize/core/SizedLayoutContext';
|
|
|
3
3
|
export * from './resize/core/ResizeObserverHook';
|
|
4
4
|
export * from './resize/impl/SizedContainer';
|
|
5
5
|
export * from './resize/impl/SizedLayout';
|
|
6
|
-
export * from './image
|
|
6
|
+
export * from './image';
|
|
7
7
|
export * from './visibility/ElementVisibilityHooks';
|
|
8
8
|
export * from './virtualized/VirtualizedConfig';
|
|
9
9
|
export * from './hover/XHover';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/layout/index.ts"],"names":[],"mappings":"AAAA,cAAc,gCAAgC,CAAA;AAC9C,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAA;AAChD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2BAA2B,CAAA;AACzC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/layout/index.ts"],"names":[],"mappings":"AAAA,cAAc,gCAAgC,CAAA;AAC9C,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAA;AAChD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2BAA2B,CAAA;AACzC,cAAc,SAAS,CAAA;AACvB,cAAc,qCAAqC,CAAA;AACnD,cAAc,iCAAiC,CAAA;AAC/C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA"}
|
package/dist/layout/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export * from './resize/core/SizedLayoutContext';
|
|
|
3
3
|
export * from './resize/core/ResizeObserverHook';
|
|
4
4
|
export * from './resize/impl/SizedContainer';
|
|
5
5
|
export * from './resize/impl/SizedLayout';
|
|
6
|
-
export * from './image
|
|
6
|
+
export * from './image';
|
|
7
7
|
export * from './visibility/ElementVisibilityHooks';
|
|
8
8
|
export * from './virtualized/VirtualizedConfig';
|
|
9
9
|
export * from './hover/XHover';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xxf_react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -70,7 +70,8 @@
|
|
|
70
70
|
"build": "tsc"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"react": ">=18"
|
|
73
|
+
"react": ">=18",
|
|
74
|
+
"next": ">=13"
|
|
74
75
|
},
|
|
75
76
|
"dependencies": {
|
|
76
77
|
"@microsoft/fetch-event-source": "^2.0.1",
|