@fjyueke/arco-mcp 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/README.md +85 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +244 -0
- package/metaData/components/action-sheet/doc.md +102 -0
- package/metaData/components/action-sheet/example/index.tsx +199 -0
- package/metaData/components/action-sheet/example/methods.tsx +6 -0
- package/metaData/components/avatar/doc.md +57 -0
- package/metaData/components/avatar/example/group.tsx +69 -0
- package/metaData/components/avatar/example/index.tsx +196 -0
- package/metaData/components/badge/doc.md +28 -0
- package/metaData/components/badge/example/index.tsx +128 -0
- package/metaData/components/button/doc.md +45 -0
- package/metaData/components/button/example/index.tsx +264 -0
- package/metaData/components/carousel/doc.md +89 -0
- package/metaData/components/carousel/example/index.tsx +1481 -0
- package/metaData/components/cell/doc.md +68 -0
- package/metaData/components/cell/example/arrow.tsx +13 -0
- package/metaData/components/cell/example/cell.tsx +71 -0
- package/metaData/components/cell/example/group.tsx +63 -0
- package/metaData/components/cell/example/index.tsx +19 -0
- package/metaData/components/checkbox/doc.md +91 -0
- package/metaData/components/checkbox/example/checkbox.tsx +158 -0
- package/metaData/components/checkbox/example/group.tsx +103 -0
- package/metaData/components/checkbox/example/index.tsx +25 -0
- package/metaData/components/circle-progress/doc.md +40 -0
- package/metaData/components/circle-progress/example/index.tsx +323 -0
- package/metaData/components/collapse/doc.md +74 -0
- package/metaData/components/collapse/example/collapse.tsx +183 -0
- package/metaData/components/collapse/example/group.tsx +136 -0
- package/metaData/components/collapse/example/index.tsx +16 -0
- package/metaData/components/collapse/example/utils.tsx +20 -0
- package/metaData/components/context-provider/doc.md +44 -0
- package/metaData/components/context-provider/example/index.tsx +208 -0
- package/metaData/components/count-down/doc.md +40 -0
- package/metaData/components/count-down/example/hooks.tsx +118 -0
- package/metaData/components/count-down/example/index.tsx +63 -0
- package/metaData/components/date-picker/doc.md +102 -0
- package/metaData/components/date-picker/example/index.tsx +545 -0
- package/metaData/components/dialog/doc.md +200 -0
- package/metaData/components/dialog/example/index.tsx +328 -0
- package/metaData/components/dialog/example/methods.tsx +56 -0
- package/metaData/components/divider/doc.md +25 -0
- package/metaData/components/divider/example/index.tsx +87 -0
- package/metaData/components/dropdown/doc.md +80 -0
- package/metaData/components/dropdown/example/dropdown.tsx +308 -0
- package/metaData/components/dropdown/example/index.tsx +19 -0
- package/metaData/components/dropdown/example/options.tsx +103 -0
- package/metaData/components/dropdown-menu/doc.md +91 -0
- package/metaData/components/dropdown-menu/example/dropdown-menu.tsx +270 -0
- package/metaData/components/dropdown-menu/example/index.tsx +16 -0
- package/metaData/components/ellipsis/doc.md +33 -0
- package/metaData/components/ellipsis/example/index.tsx +93 -0
- package/metaData/components/ellipsis/example/js-ellipsis.tsx +229 -0
- package/metaData/components/ellipsis/example/native-ellipsis.tsx +48 -0
- package/metaData/components/form/doc.md +90 -0
- package/metaData/components/form/example/form-item-context.tsx +8 -0
- package/metaData/components/form/example/form-item.tsx +378 -0
- package/metaData/components/form/example/index.tsx +87 -0
- package/metaData/components/form/example/linked-container.tsx +66 -0
- package/metaData/components/grid/doc.md +39 -0
- package/metaData/components/grid/example/index.tsx +252 -0
- package/metaData/components/icon/doc.md +15 -0
- package/metaData/components/icon/example/index.tsx +32 -0
- package/metaData/components/image/doc.md +62 -0
- package/metaData/components/image/example/index.tsx +528 -0
- package/metaData/components/image-picker/doc.md +116 -0
- package/metaData/components/image-picker/example/add-icon.tsx +14 -0
- package/metaData/components/image-picker/example/index.tsx +175 -0
- package/metaData/components/image-preview/doc.md +119 -0
- package/metaData/components/image-preview/example/index.tsx +1290 -0
- package/metaData/components/image-preview/example/methods.tsx +72 -0
- package/metaData/components/index-bar/doc.md +83 -0
- package/metaData/components/index-bar/example/group.tsx +93 -0
- package/metaData/components/index-bar/example/index.tsx +271 -0
- package/metaData/components/index-bar/example/side-bar.tsx +82 -0
- package/metaData/components/input/doc.md +60 -0
- package/metaData/components/input/example/hooks.tsx +259 -0
- package/metaData/components/input/example/index.tsx +134 -0
- package/metaData/components/input/example/props.tsx +183 -0
- package/metaData/components/keyboard/doc.md +73 -0
- package/metaData/components/keyboard/example/index.tsx +238 -0
- package/metaData/components/load-more/doc.md +48 -0
- package/metaData/components/load-more/example/index.tsx +395 -0
- package/metaData/components/loading/doc.md +34 -0
- package/metaData/components/loading/example/index.tsx +312 -0
- package/metaData/components/masking/doc.md +93 -0
- package/metaData/components/masking/example/index.tsx +410 -0
- package/metaData/components/masking/example/methods.tsx +88 -0
- package/metaData/components/nav-bar/doc.md +40 -0
- package/metaData/components/nav-bar/example/back-icon.tsx +28 -0
- package/metaData/components/nav-bar/example/index.tsx +305 -0
- package/metaData/components/notice-bar/doc.md +34 -0
- package/metaData/components/notice-bar/example/index.tsx +294 -0
- package/metaData/components/notify/doc.md +81 -0
- package/metaData/components/notify/example/index.tsx +171 -0
- package/metaData/components/notify/example/methods.tsx +83 -0
- package/metaData/components/pagination/doc.md +41 -0
- package/metaData/components/pagination/example/arrow.tsx +14 -0
- package/metaData/components/pagination/example/index.tsx +292 -0
- package/metaData/components/picker/doc.md +89 -0
- package/metaData/components/picker/example/index.tsx +217 -0
- package/metaData/components/picker-view/doc.md +57 -0
- package/metaData/components/picker-view/example/cascader.tsx +141 -0
- package/metaData/components/picker-view/example/index.tsx +297 -0
- package/metaData/components/picker-view/example/multi-picker.tsx +66 -0
- package/metaData/components/picker-view/example/picker-cell.tsx +416 -0
- package/metaData/components/popover/doc.md +144 -0
- package/metaData/components/popover/example/index.tsx +23 -0
- package/metaData/components/popover/example/menu.tsx +145 -0
- package/metaData/components/popover/example/popover-inner.tsx +147 -0
- package/metaData/components/popover/example/popover.tsx +268 -0
- package/metaData/components/popup/doc.md +101 -0
- package/metaData/components/popup/example/index.tsx +125 -0
- package/metaData/components/popup/example/methods.tsx +6 -0
- package/metaData/components/popup-swiper/doc.md +108 -0
- package/metaData/components/popup-swiper/example/index.tsx +332 -0
- package/metaData/components/popup-swiper/example/methods.tsx +6 -0
- package/metaData/components/portal/doc.md +14 -0
- package/metaData/components/portal/example/index.tsx +35 -0
- package/metaData/components/progress/doc.md +42 -0
- package/metaData/components/progress/example/index.tsx +256 -0
- package/metaData/components/pull-refresh/doc.md +37 -0
- package/metaData/components/pull-refresh/example/android-pull-refresh.tsx +260 -0
- package/metaData/components/pull-refresh/example/index.tsx +44 -0
- package/metaData/components/pull-refresh/example/ios-pull-refresh.tsx +216 -0
- package/metaData/components/radio/doc.md +89 -0
- package/metaData/components/radio/example/group.tsx +92 -0
- package/metaData/components/radio/example/index.tsx +25 -0
- package/metaData/components/radio/example/radio.tsx +22 -0
- package/metaData/components/rate/doc.md +33 -0
- package/metaData/components/rate/example/index.tsx +281 -0
- package/metaData/components/search-bar/doc.md +97 -0
- package/metaData/components/search-bar/example/association.tsx +89 -0
- package/metaData/components/search-bar/example/cancel-button.tsx +34 -0
- package/metaData/components/search-bar/example/highlight.tsx +66 -0
- package/metaData/components/search-bar/example/index.tsx +251 -0
- package/metaData/components/show-monitor/doc.md +34 -0
- package/metaData/components/show-monitor/example/index.tsx +646 -0
- package/metaData/components/skeleton/doc.md +101 -0
- package/metaData/components/skeleton/example/elements.tsx +290 -0
- package/metaData/components/skeleton/example/index.tsx +96 -0
- package/metaData/components/skeleton/example/skeleton-context.tsx +7 -0
- package/metaData/components/slider/doc.md +42 -0
- package/metaData/components/slider/example/index.tsx +326 -0
- package/metaData/components/slider/example/marks.tsx +50 -0
- package/metaData/components/slider/example/popover.tsx +38 -0
- package/metaData/components/slider/example/thumb.tsx +48 -0
- package/metaData/components/slider/example/useSliderIcon.tsx +38 -0
- package/metaData/components/slider/example/useSliderInit.tsx +170 -0
- package/metaData/components/stepper/doc.md +46 -0
- package/metaData/components/stepper/example/index.tsx +166 -0
- package/metaData/components/stepper/example/useButtonClick.tsx +74 -0
- package/metaData/components/stepper/example/useInputEvent.tsx +65 -0
- package/metaData/components/stepper/example/useValue.tsx +43 -0
- package/metaData/components/steps/doc.md +57 -0
- package/metaData/components/steps/example/index.tsx +123 -0
- package/metaData/components/steps/example/step.tsx +126 -0
- package/metaData/components/sticky/doc.md +45 -0
- package/metaData/components/sticky/example/index.tsx +430 -0
- package/metaData/components/swipe-action/doc.md +44 -0
- package/metaData/components/swipe-action/example/index.tsx +338 -0
- package/metaData/components/swipe-action/example/item.tsx +40 -0
- package/metaData/components/swipe-load/doc.md +43 -0
- package/metaData/components/swipe-load/example/index.tsx +339 -0
- package/metaData/components/swipe-load/example/type.tsx +153 -0
- package/metaData/components/switch/doc.md +38 -0
- package/metaData/components/switch/example/index.tsx +237 -0
- package/metaData/components/tab-bar/doc.md +57 -0
- package/metaData/components/tab-bar/example/index.tsx +18 -0
- package/metaData/components/tab-bar/example/item.tsx +74 -0
- package/metaData/components/tab-bar/example/tab-bar.tsx +105 -0
- package/metaData/components/tabs/doc.md +187 -0
- package/metaData/components/tabs/example/index.tsx +514 -0
- package/metaData/components/tabs/example/tab-cell-underline.tsx +320 -0
- package/metaData/components/tabs/example/tab-cell.tsx +433 -0
- package/metaData/components/tabs/example/tab-pane.tsx +349 -0
- package/metaData/components/tag/doc.md +80 -0
- package/metaData/components/tag/example/index.tsx +23 -0
- package/metaData/components/tag/example/list.tsx +97 -0
- package/metaData/components/tag/example/tag.tsx +85 -0
- package/metaData/components/textarea/doc.md +67 -0
- package/metaData/components/textarea/example/index.tsx +258 -0
- package/metaData/components/toast/doc.md +90 -0
- package/metaData/components/toast/example/index.tsx +335 -0
- package/metaData/components/toast/example/methods.tsx +87 -0
- package/metaData/components/transition/doc.md +19 -0
- package/metaData/components/transition/example/index.tsx +113 -0
- package/metaData/components/uploader/doc.md +82 -0
- package/metaData/components/uploader/example/index.tsx +208 -0
- package/metaData/components-index.json +242 -0
- package/package.json +56 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
ReactNode,
|
|
3
|
+
forwardRef,
|
|
4
|
+
Ref,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useRef,
|
|
7
|
+
useEffect,
|
|
8
|
+
CSSProperties,
|
|
9
|
+
useMemo,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import { cls, defaultLocale, nextTick } from '@arco-design/mobile-utils';
|
|
12
|
+
import { ContextLayout } from '../context-provider';
|
|
13
|
+
import Loading from '../loading';
|
|
14
|
+
import { useSystem, useWindowSize, getStyleWithVendor, useMountedState } from '../_helpers';
|
|
15
|
+
|
|
16
|
+
export type ImageStatus = 'loading' | 'loaded' | 'init' | 'error';
|
|
17
|
+
|
|
18
|
+
export interface ImageProps {
|
|
19
|
+
/**
|
|
20
|
+
* 自定义样式
|
|
21
|
+
* @en Custom stylesheet
|
|
22
|
+
*/
|
|
23
|
+
style?: CSSProperties;
|
|
24
|
+
/**
|
|
25
|
+
* 自定义类名
|
|
26
|
+
* @en Custom classname
|
|
27
|
+
*/
|
|
28
|
+
className?: string;
|
|
29
|
+
/**
|
|
30
|
+
* 指定图片状态,staticLabel=false时有效
|
|
31
|
+
* @en The specified image state, valid when staticLabel=false
|
|
32
|
+
*/
|
|
33
|
+
status?: ImageStatus;
|
|
34
|
+
/**
|
|
35
|
+
* 图片链接
|
|
36
|
+
* @@en Image resource
|
|
37
|
+
* */
|
|
38
|
+
src: string;
|
|
39
|
+
/**
|
|
40
|
+
* 容器宽度,传数值,默认单位为px,传字符串则接受传入的单位
|
|
41
|
+
* @en Container width, when number is input, the default unit is px, if a string is input, the unit is accepted
|
|
42
|
+
* */
|
|
43
|
+
width?: string | number;
|
|
44
|
+
/**
|
|
45
|
+
* 容器高度,传数值,默认单位为px,传字符串则接受传入的单位
|
|
46
|
+
* @en Container height, when number is input, the default unit is px, if a string is input, the unit is accepted
|
|
47
|
+
* */
|
|
48
|
+
height?: string | number;
|
|
49
|
+
/**
|
|
50
|
+
* 替代文本
|
|
51
|
+
* @en Alternative text
|
|
52
|
+
* @default ""
|
|
53
|
+
*/
|
|
54
|
+
alt?: string;
|
|
55
|
+
/**
|
|
56
|
+
* 图片填充模式(object-fit),传preview-*为预览模式,预览模式仅staticLabel=false时有效
|
|
57
|
+
* @en Image fill mode (object-fit), preview-* is preview mode, preview mode is only valid when staticLabel=false
|
|
58
|
+
* @default "fill"
|
|
59
|
+
*/
|
|
60
|
+
fit?: CSSProperties['objectFit'] | 'preview-y' | 'preview-x';
|
|
61
|
+
/**
|
|
62
|
+
* 图片填充位置(object-position)
|
|
63
|
+
* @en Image fill position(object-position)
|
|
64
|
+
* @default "center"
|
|
65
|
+
*/
|
|
66
|
+
position?: CSSProperties['objectPosition'];
|
|
67
|
+
/**
|
|
68
|
+
* 图片圆角
|
|
69
|
+
* @en Image border radius
|
|
70
|
+
* */
|
|
71
|
+
radius?: string | number;
|
|
72
|
+
/**
|
|
73
|
+
* 是否加边框
|
|
74
|
+
* @en Whether to add a border
|
|
75
|
+
* */
|
|
76
|
+
bordered?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* 自定义展示加载中内容
|
|
79
|
+
* @en Custom display loading content, valid when staticLabel=false
|
|
80
|
+
* */
|
|
81
|
+
loadingArea?: ReactNode;
|
|
82
|
+
/**
|
|
83
|
+
* 自定义展示加载失败内容
|
|
84
|
+
* @en Custom display failed to load content, valid when staticLabel=false
|
|
85
|
+
* */
|
|
86
|
+
errorArea?: ReactNode;
|
|
87
|
+
/**
|
|
88
|
+
* 是否展示图片加载中提示
|
|
89
|
+
* @en Whether to display the image loading prompt, valid when staticLabel=false
|
|
90
|
+
* */
|
|
91
|
+
showLoading?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* 是否展示图片加载失败提示
|
|
94
|
+
* @en Whether to display the image loading failure prompt, valid when staticLabel=false
|
|
95
|
+
* */
|
|
96
|
+
showError?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* 加载完时展现动画时长,staticLabel=false时有效
|
|
99
|
+
* @en Display animation duration when loading is complete, valid when staticLabel=false
|
|
100
|
+
* @default 200
|
|
101
|
+
*/
|
|
102
|
+
animateDuration?: number;
|
|
103
|
+
/**
|
|
104
|
+
* 失败时自动重试次数
|
|
105
|
+
* @en Number of automatic retries on failure
|
|
106
|
+
* @default 0
|
|
107
|
+
*/
|
|
108
|
+
retryTime?: number;
|
|
109
|
+
/**
|
|
110
|
+
* 是否强制使用https
|
|
111
|
+
* @en Whether to force the use of https
|
|
112
|
+
* */
|
|
113
|
+
forceHttps?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* 预览模式下,父容器宽度
|
|
116
|
+
* @en In preview mode, the width of the parent container
|
|
117
|
+
* */
|
|
118
|
+
boxWidth?: number;
|
|
119
|
+
/**
|
|
120
|
+
* 预览模式下,父容器高度
|
|
121
|
+
* @en In preview mode, the height of the parent container
|
|
122
|
+
* */
|
|
123
|
+
boxHeight?: number;
|
|
124
|
+
/**
|
|
125
|
+
* 图片顶层内容
|
|
126
|
+
* @en Top-level content of the image
|
|
127
|
+
*/
|
|
128
|
+
topOverlap?: ReactNode;
|
|
129
|
+
/**
|
|
130
|
+
* 图片底层内容(placeholder),默认是灰色兜底,传null可移除
|
|
131
|
+
* @en The bottom content of the image (placeholder), the default is gray bottom, input null to remove
|
|
132
|
+
* */
|
|
133
|
+
bottomOverlap?: ReactNode;
|
|
134
|
+
/**
|
|
135
|
+
* 手动控制是否加载图片
|
|
136
|
+
* @en Manually control whether to load images
|
|
137
|
+
* */
|
|
138
|
+
showImage?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* 是否直接渲染<img>标签,不走加载图片流程
|
|
141
|
+
* @en Whether to render the <img> tag directly without going through the image loading process
|
|
142
|
+
* */
|
|
143
|
+
staticLabel?: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* img标签原生属性,优先级低于单独设置
|
|
146
|
+
* @en Img tag native attributes, the priority is lower than the separate setting
|
|
147
|
+
*/
|
|
148
|
+
nativeProps?: React.DetailedHTMLProps<
|
|
149
|
+
React.ImgHTMLAttributes<HTMLImageElement>,
|
|
150
|
+
HTMLImageElement
|
|
151
|
+
>;
|
|
152
|
+
/**
|
|
153
|
+
* 切换status时触发的回调
|
|
154
|
+
* @en Callback triggered when switching status
|
|
155
|
+
*/
|
|
156
|
+
onChange?: (status: string) => void;
|
|
157
|
+
/**
|
|
158
|
+
* 点击图片时触发的回调
|
|
159
|
+
* @en Callback when clicking image
|
|
160
|
+
*/
|
|
161
|
+
onClick?: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
|
162
|
+
/**
|
|
163
|
+
* 图片加载完毕时触发的回调
|
|
164
|
+
* @en Callback when the image is loaded
|
|
165
|
+
* */
|
|
166
|
+
onLoad?: (e: Event | null, image: HTMLImageElement) => void;
|
|
167
|
+
/**
|
|
168
|
+
* 图片加载失败时触发的回调,如果有自动重试则在重试最终失败后触发
|
|
169
|
+
* @en Callback when the image fails to load, triggered after the retry finally fails if there is an automatic retry
|
|
170
|
+
*/
|
|
171
|
+
onError?: (e: string | Event | null) => void;
|
|
172
|
+
/**
|
|
173
|
+
* 图片加载失败时自动重试触发的回调
|
|
174
|
+
* @en Callback triggered by automatic retry when image loading fails
|
|
175
|
+
*/
|
|
176
|
+
onAutoRetry?: (e: string | Event | null) => void;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface ImageRef {
|
|
180
|
+
/**
|
|
181
|
+
* 最外层元素 DOM
|
|
182
|
+
* @en The outermost element DOM
|
|
183
|
+
*/
|
|
184
|
+
dom: HTMLDivElement | null;
|
|
185
|
+
/**
|
|
186
|
+
* 原生图片元素 DOM
|
|
187
|
+
* @en Native image element DOM
|
|
188
|
+
*/
|
|
189
|
+
image: HTMLImageElement | null;
|
|
190
|
+
/**
|
|
191
|
+
* 手动重试图片加载
|
|
192
|
+
* @en Manually retry image loading
|
|
193
|
+
*/
|
|
194
|
+
retry: () => void;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 增强版的 img 标签,提供多种图片填充模式,支持图片加载中提示、加载失败提示。
|
|
199
|
+
* @en Enhanced img tag, provides a variety of image filling modes, and supports image loading prompts and loading failure prompts.
|
|
200
|
+
* @type 信息展示
|
|
201
|
+
* @type_en Data Display
|
|
202
|
+
* @name 图片
|
|
203
|
+
* @name_en Image
|
|
204
|
+
*/
|
|
205
|
+
export const BaseImage = forwardRef((props: ImageProps, ref: Ref<ImageRef>) => {
|
|
206
|
+
const {
|
|
207
|
+
style,
|
|
208
|
+
className,
|
|
209
|
+
status,
|
|
210
|
+
src,
|
|
211
|
+
width,
|
|
212
|
+
height,
|
|
213
|
+
alt = '',
|
|
214
|
+
fit = 'cover',
|
|
215
|
+
position = 'center',
|
|
216
|
+
radius,
|
|
217
|
+
bordered,
|
|
218
|
+
loadingArea,
|
|
219
|
+
errorArea,
|
|
220
|
+
showLoading,
|
|
221
|
+
showError,
|
|
222
|
+
animateDuration = 200,
|
|
223
|
+
retryTime = 0,
|
|
224
|
+
forceHttps,
|
|
225
|
+
boxWidth = 0,
|
|
226
|
+
boxHeight = 0,
|
|
227
|
+
topOverlap,
|
|
228
|
+
bottomOverlap,
|
|
229
|
+
showImage = true,
|
|
230
|
+
staticLabel,
|
|
231
|
+
nativeProps = {},
|
|
232
|
+
onChange,
|
|
233
|
+
onClick,
|
|
234
|
+
onLoad,
|
|
235
|
+
onError,
|
|
236
|
+
onAutoRetry,
|
|
237
|
+
} = props;
|
|
238
|
+
const system = useSystem();
|
|
239
|
+
const { windowWidth, windowHeight } = useWindowSize();
|
|
240
|
+
const [imageStatus, setImageStatus] = useMountedState<ImageStatus>(
|
|
241
|
+
staticLabel && showLoading ? 'loading' : 'init',
|
|
242
|
+
);
|
|
243
|
+
const [wrapClass, setWrapClass] = useMountedState('');
|
|
244
|
+
const [staticRetrying, setStaticRetrying] = useMountedState(false);
|
|
245
|
+
const imageRef = useRef<HTMLDivElement | null>(null);
|
|
246
|
+
const imageDomRef = useRef<HTMLImageElement | null>(null);
|
|
247
|
+
const wrapRef = useRef<HTMLDivElement | null>(null);
|
|
248
|
+
const retryCountRef = useRef(0);
|
|
249
|
+
const loadingImageRef = useRef<HTMLImageElement | null>(null);
|
|
250
|
+
const hasLoadedRef = useRef(false);
|
|
251
|
+
const isPreview = Boolean(fit.indexOf('preview') >= 0);
|
|
252
|
+
const actualBoxWidth = boxWidth || windowWidth;
|
|
253
|
+
const actualBoxHeight = boxHeight || windowHeight;
|
|
254
|
+
const validStatus = status === undefined ? imageStatus : status;
|
|
255
|
+
|
|
256
|
+
const attrs = useMemo(() => {
|
|
257
|
+
const imageStyle: CSSProperties = {};
|
|
258
|
+
if (!isPreview) {
|
|
259
|
+
imageStyle.objectFit = fit as CSSProperties['objectFit'];
|
|
260
|
+
imageStyle.objectPosition = position;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
alt,
|
|
264
|
+
src: forceHttps ? src.replace('http://', 'https://') : src,
|
|
265
|
+
className: `image-content${bordered ? ' bordered' : ''} ${system}`,
|
|
266
|
+
style: imageStyle,
|
|
267
|
+
};
|
|
268
|
+
}, [alt, bordered, fit, forceHttps, isPreview, position, radius, src, system]);
|
|
269
|
+
|
|
270
|
+
function retry() {
|
|
271
|
+
loadImage(true);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
useImperativeHandle(ref, () => ({
|
|
275
|
+
dom: wrapRef.current,
|
|
276
|
+
image: imageDomRef.current,
|
|
277
|
+
retry,
|
|
278
|
+
}));
|
|
279
|
+
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
retryCountRef.current = 0;
|
|
282
|
+
loadImage();
|
|
283
|
+
}, [attrs, width, height, showImage, staticLabel]);
|
|
284
|
+
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
// 当使用img标签时,onLoad可能加载完成前已经执行完,此时手动触发一次
|
|
287
|
+
// @en When using the img tag, onLoad may have been executed before loading is complete, and it needs to be triggered manually
|
|
288
|
+
if (staticLabel && !hasLoadedRef.current && imageDomRef.current?.complete) {
|
|
289
|
+
// 图片有宽高认为正常加载,否则认为加载错误
|
|
290
|
+
// @en If the image has width and height, it is considered to be loaded normally, otherwise it is considered to be a loading error
|
|
291
|
+
if (imageDomRef.current.naturalWidth || imageDomRef.current.naturalHeight) {
|
|
292
|
+
handleStaticImageLoaded(null, imageDomRef.current);
|
|
293
|
+
} else {
|
|
294
|
+
handleStaticImageError(null);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}, [staticLabel]);
|
|
298
|
+
|
|
299
|
+
function changeStatus(newStatus: ImageStatus) {
|
|
300
|
+
setImageStatus(newStatus);
|
|
301
|
+
onChange && onChange(newStatus);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function replaceChild(newChild: HTMLElement) {
|
|
305
|
+
const dom = imageRef.current;
|
|
306
|
+
if (!dom) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (dom.children && dom.children.length) {
|
|
310
|
+
dom.replaceChild(newChild, dom.children[0]);
|
|
311
|
+
} else {
|
|
312
|
+
dom.appendChild(newChild);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function handleImageLoaded(image: HTMLImageElement) {
|
|
317
|
+
hasLoadedRef.current = true;
|
|
318
|
+
changeStatus('loaded');
|
|
319
|
+
const { width: imageWidth = 0, height: imageHeight = 0 } = image;
|
|
320
|
+
let extraClass = '';
|
|
321
|
+
if (isPreview) {
|
|
322
|
+
const scale = imageWidth / imageHeight;
|
|
323
|
+
const windowScale = actualBoxWidth / actualBoxHeight;
|
|
324
|
+
if (fit === 'preview-y') {
|
|
325
|
+
if (scale < windowScale) {
|
|
326
|
+
image.style.width = `${actualBoxWidth}px`;
|
|
327
|
+
image.style.height = `${actualBoxWidth / scale}px`;
|
|
328
|
+
extraClass = 'preview-overflow-y';
|
|
329
|
+
} else {
|
|
330
|
+
extraClass = 'preview-fit-contain-y';
|
|
331
|
+
}
|
|
332
|
+
} else if (fit === 'preview-x') {
|
|
333
|
+
if (scale > windowScale) {
|
|
334
|
+
image.style.width = `${actualBoxHeight * scale}px`;
|
|
335
|
+
image.style.height = `${actualBoxHeight}px`;
|
|
336
|
+
extraClass = 'preview-overflow-x';
|
|
337
|
+
} else {
|
|
338
|
+
extraClass = 'preview-fit-contain-x';
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
extraClass && image.classList.add(extraClass);
|
|
343
|
+
setWrapClass(extraClass ? `${extraClass}-container` : '');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function loadImage(isFromRetry?: boolean) {
|
|
347
|
+
// 如果在加载图片前发现上一个图片还没加载完,则抛弃上一个图片的加载
|
|
348
|
+
// @en Abort last image before starting loading new image
|
|
349
|
+
if (loadingImageRef.current) {
|
|
350
|
+
loadingImageRef.current.src = '';
|
|
351
|
+
loadingImageRef.current = null;
|
|
352
|
+
}
|
|
353
|
+
if (!showImage || staticLabel) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
changeStatus('loading');
|
|
357
|
+
const image = new Image();
|
|
358
|
+
Object.keys(nativeProps).forEach(key => {
|
|
359
|
+
image[key] = nativeProps[key];
|
|
360
|
+
});
|
|
361
|
+
image.className = attrs.className;
|
|
362
|
+
Object.keys(attrs.style).forEach((key: string) => {
|
|
363
|
+
image.style[key] = attrs.style[key];
|
|
364
|
+
});
|
|
365
|
+
image.onload = evt => {
|
|
366
|
+
loadingImageRef.current = null;
|
|
367
|
+
imageDomRef.current = image;
|
|
368
|
+
handleImageLoaded(image);
|
|
369
|
+
replaceChild(image);
|
|
370
|
+
onLoad && onLoad(evt, image);
|
|
371
|
+
};
|
|
372
|
+
image.onerror = evt => {
|
|
373
|
+
loadingImageRef.current = null;
|
|
374
|
+
if (isFromRetry || retryCountRef.current >= retryTime) {
|
|
375
|
+
changeStatus('error');
|
|
376
|
+
const placeholder = document.createElement('div');
|
|
377
|
+
replaceChild(placeholder);
|
|
378
|
+
onError && onError(evt);
|
|
379
|
+
} else {
|
|
380
|
+
retryCountRef.current += 1;
|
|
381
|
+
onAutoRetry && onAutoRetry(evt);
|
|
382
|
+
loadImage();
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
nextTick(() => {
|
|
386
|
+
image.src = attrs.src;
|
|
387
|
+
image.alt = attrs.alt;
|
|
388
|
+
loadingImageRef.current = image;
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function handleStaticImageLoaded(evt: Event | null, image: HTMLImageElement) {
|
|
393
|
+
handleImageLoaded(image);
|
|
394
|
+
onLoad && onLoad(evt, image);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function handleStaticImageError(e: React.SyntheticEvent<HTMLImageElement, Event> | null) {
|
|
398
|
+
const evt = e ? e.nativeEvent : null;
|
|
399
|
+
if (retryCountRef.current >= retryTime) {
|
|
400
|
+
changeStatus('error');
|
|
401
|
+
onError && onError(evt);
|
|
402
|
+
} else {
|
|
403
|
+
retryCountRef.current += 1;
|
|
404
|
+
onAutoRetry && onAutoRetry(evt);
|
|
405
|
+
// 强制刷新一下<img>使其重新加载
|
|
406
|
+
// @en Force refresh <img> to reload
|
|
407
|
+
setStaticRetrying(true);
|
|
408
|
+
nextTick(() => {
|
|
409
|
+
setStaticRetrying(false);
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function getImageStyle(): CSSProperties {
|
|
415
|
+
if (isPreview) {
|
|
416
|
+
const overflow: CSSProperties =
|
|
417
|
+
fit === 'preview-x'
|
|
418
|
+
? {
|
|
419
|
+
overflowX: 'auto',
|
|
420
|
+
}
|
|
421
|
+
: {
|
|
422
|
+
overflowY: 'auto',
|
|
423
|
+
};
|
|
424
|
+
return {
|
|
425
|
+
width: actualBoxWidth,
|
|
426
|
+
height: actualBoxHeight,
|
|
427
|
+
...overflow,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
width: Number(width) || width,
|
|
432
|
+
height: Number(height) || height,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return (
|
|
437
|
+
<ContextLayout>
|
|
438
|
+
{({ prefixCls, locale = defaultLocale }) => (
|
|
439
|
+
<div
|
|
440
|
+
className={cls(
|
|
441
|
+
`${prefixCls}-image all-border-box`,
|
|
442
|
+
{ preview: isPreview },
|
|
443
|
+
system,
|
|
444
|
+
wrapClass,
|
|
445
|
+
className,
|
|
446
|
+
)}
|
|
447
|
+
style={{ ...getImageStyle(), ...(style || {}) }}
|
|
448
|
+
ref={wrapRef}
|
|
449
|
+
>
|
|
450
|
+
{bottomOverlap !== null ? (
|
|
451
|
+
<div
|
|
452
|
+
className="image-content image-bottom-overlap"
|
|
453
|
+
style={{ borderRadius: radius }}
|
|
454
|
+
>
|
|
455
|
+
{bottomOverlap || <div className="image-placeholder" />}
|
|
456
|
+
</div>
|
|
457
|
+
) : null}
|
|
458
|
+
<div
|
|
459
|
+
className={cls('image-container', validStatus, {
|
|
460
|
+
animate: Boolean(animateDuration),
|
|
461
|
+
'static-label': staticLabel,
|
|
462
|
+
'has-loaded': hasLoadedRef.current,
|
|
463
|
+
})}
|
|
464
|
+
style={getStyleWithVendor({
|
|
465
|
+
borderRadius: radius,
|
|
466
|
+
transitionDuration: `${animateDuration}ms`,
|
|
467
|
+
})}
|
|
468
|
+
onClick={onClick}
|
|
469
|
+
ref={imageRef}
|
|
470
|
+
>
|
|
471
|
+
{staticLabel && showImage && !staticRetrying ? (
|
|
472
|
+
<img
|
|
473
|
+
{...nativeProps}
|
|
474
|
+
{...attrs}
|
|
475
|
+
ref={imageDomRef}
|
|
476
|
+
onLoad={e =>
|
|
477
|
+
handleStaticImageLoaded(e.nativeEvent, imageDomRef.current!)
|
|
478
|
+
}
|
|
479
|
+
onError={handleStaticImageError}
|
|
480
|
+
/>
|
|
481
|
+
) : null}
|
|
482
|
+
</div>
|
|
483
|
+
{showLoading && validStatus === 'loading' ? (
|
|
484
|
+
<div
|
|
485
|
+
className="image-content image-loading-container"
|
|
486
|
+
style={{ borderRadius: radius }}
|
|
487
|
+
>
|
|
488
|
+
{loadingArea || (
|
|
489
|
+
<div className="image-loading">
|
|
490
|
+
<Loading
|
|
491
|
+
type="circle"
|
|
492
|
+
className="loading-icon"
|
|
493
|
+
radius={8.5}
|
|
494
|
+
stroke={3}
|
|
495
|
+
/>
|
|
496
|
+
</div>
|
|
497
|
+
)}
|
|
498
|
+
</div>
|
|
499
|
+
) : null}
|
|
500
|
+
{showError && validStatus === 'error' ? (
|
|
501
|
+
<div
|
|
502
|
+
className="image-content image-error-container"
|
|
503
|
+
onClick={e => {
|
|
504
|
+
e.stopPropagation();
|
|
505
|
+
retry();
|
|
506
|
+
}}
|
|
507
|
+
style={{ borderRadius: radius }}
|
|
508
|
+
>
|
|
509
|
+
{errorArea || (
|
|
510
|
+
<div className="image-retry-load">{locale.Image.loadError}</div>
|
|
511
|
+
)}
|
|
512
|
+
</div>
|
|
513
|
+
) : null}
|
|
514
|
+
{topOverlap ? (
|
|
515
|
+
<div
|
|
516
|
+
className="image-content image-top-overlap"
|
|
517
|
+
style={{ borderRadius: radius }}
|
|
518
|
+
>
|
|
519
|
+
{topOverlap}
|
|
520
|
+
</div>
|
|
521
|
+
) : null}
|
|
522
|
+
</div>
|
|
523
|
+
)}
|
|
524
|
+
</ContextLayout>
|
|
525
|
+
);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
export default BaseImage;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
### 数据录入
|
|
2
|
+
|
|
3
|
+
# 图片选择器 ImagePicker
|
|
4
|
+
|
|
5
|
+
图片选择器组件
|
|
6
|
+
|
|
7
|
+
======
|
|
8
|
+
|
|
9
|
+
> 属性/Props
|
|
10
|
+
|
|
11
|
+
|参数|描述|类型|默认值|
|
|
12
|
+
|----------|-------------|------|------|
|
|
13
|
+
|className|自定义类名|string|-|
|
|
14
|
+
|style|自定义样式|CSSProperties|-|
|
|
15
|
+
|images|已选择图片列表|ImagePickItem\[\]|必填|
|
|
16
|
+
|accept|可以选择的文件类型|string|'image/*'|
|
|
17
|
+
|multiple|是否支持多选|boolean|-|
|
|
18
|
+
|capture|图片选取模式 Image selection mode \[capture MDN\](https://developer\.mozilla\.org/en\-US/docs/Web/HTML/Attributes/capture)|string \| boolean|-|
|
|
19
|
+
|columns|一行展示图片张数|number|3|
|
|
20
|
+
|gutter|格子间的间距|number|8|
|
|
21
|
+
|hideDelete|是否隐藏删除Icon|boolean|false|
|
|
22
|
+
|hideSelect|是否隐藏选择Icon|boolean|false|
|
|
23
|
+
|alwaysShowSelect|是否总是展示选择Icon,默认情况下当图片数量超出limit值时会自动隐藏选择Icon|boolean|false|
|
|
24
|
+
|disabled|禁用选择和删除图片|boolean|-|
|
|
25
|
+
|deleteIcon|自定义删除图标|ReactNode|-|
|
|
26
|
+
|selectIcon|自定义选择图标|ReactNode|-|
|
|
27
|
+
|imageProps|透传给图片的属性|Partial\<ImageProps\>|-|
|
|
28
|
+
|renderError|自定义上传失败展示|(index?: number) =\> ReactNode|-|
|
|
29
|
+
|renderLoading|自定义上传中展示|(index?: number) =\> ReactNode|-|
|
|
30
|
+
|onSelectClick|选图点击事件(同onUploadClick)|() =\> void|-|
|
|
31
|
+
|onUploadClick|选图点击事件|() =\> void|-|
|
|
32
|
+
|upload|上传方法|(file: ImagePickItem) =\> Promise\<ImagePickItem\>|-|
|
|
33
|
+
|onChange|已选文件列表发生变化|(fileList: ImagePickItem\[\]) =\> void|-|
|
|
34
|
+
|maxSize|文件大小限制,单位为K|number|-|
|
|
35
|
+
|onMaxSizeExceed|文件超过限制大小|(file: File) =\> void|-|
|
|
36
|
+
|limit|最多选择文件数,超出数量自动隐藏上传按钮,0表示不做限制|number|0|
|
|
37
|
+
|onLimitExceed|选择文件数超过限制|(files: File\[\]) =\> void|-|
|
|
38
|
+
|onDeleteClick|删除点击事件|(index: number) =\> void|-|
|
|
39
|
+
|selectAdapter|文件选择适配器|() =\> Promise\<SelectCallback\>|-|
|
|
40
|
+
|onClick|文件点击|(e: MouseEvent\<HTMLElement, MouseEvent\>, file: ImagePickItem, index: number) =\> void|-|
|
|
41
|
+
|onLongPress|文件长按事件|(e: TouchEvent\<HTMLElement\>, file: ImagePickItem, index: number) =\> void|-|
|
|
42
|
+
|
|
43
|
+
> 引用/Refs
|
|
44
|
+
|
|
45
|
+
|参数|描述|类型|
|
|
46
|
+
|----------|-------------|------|
|
|
47
|
+
|dom|最外层 DOM 元素|HTMLDivElement|
|
|
48
|
+
|
|
49
|
+
> ImagePickItem
|
|
50
|
+
|
|
51
|
+
|参数|描述|类型|默认值|
|
|
52
|
+
|----------|-------------|------|------|
|
|
53
|
+
|url|图片地址|string|必填|
|
|
54
|
+
|file|文件|File|-|
|
|
55
|
+
|status|文件状态|"loaded" \| "loading" \| "error"|以文件自身加载状态而定|
|
|
56
|
+
|
|
57
|
+
> ImageProps
|
|
58
|
+
|
|
59
|
+
|参数|描述|类型|默认值|
|
|
60
|
+
|----------|-------------|------|------|
|
|
61
|
+
|style|自定义样式|CSSProperties|-|
|
|
62
|
+
|className|自定义类名|string|-|
|
|
63
|
+
|status|指定图片状态,staticLabel=false时有效|"loaded" \| "loading" \| "error" \| "init"|-|
|
|
64
|
+
|src|图片链接|string|必填|
|
|
65
|
+
|width|容器宽度,传数值,默认单位为px,传字符串则接受传入的单位|ReactText|-|
|
|
66
|
+
|height|容器高度,传数值,默认单位为px,传字符串则接受传入的单位|ReactText|-|
|
|
67
|
+
|alt|替代文本|string|""|
|
|
68
|
+
|fit|图片填充模式(object\-fit),传preview\-\*为预览模式,预览模式仅staticLabel=false时有效|"\-moz\-initial" \| "inherit" \| "initial" \| "revert" \| "revert\-layer" \| "unset" \| "contain" \| "cover" \| "fill" \| "none" \| "scale\-down" \| "preview\-y" \| "preview\-x"|"fill"|
|
|
69
|
+
|position|图片填充位置(object\-position)|ObjectPosition\<ReactText\>|"center"|
|
|
70
|
+
|radius|图片圆角|ReactText|-|
|
|
71
|
+
|bordered|是否加边框|boolean|-|
|
|
72
|
+
|loadingArea|自定义展示加载中内容|ReactNode|-|
|
|
73
|
+
|errorArea|自定义展示加载失败内容|ReactNode|-|
|
|
74
|
+
|showLoading|是否展示图片加载中提示|boolean|-|
|
|
75
|
+
|showError|是否展示图片加载失败提示|boolean|-|
|
|
76
|
+
|animateDuration|加载完时展现动画时长,staticLabel=false时有效|number|200|
|
|
77
|
+
|retryTime|失败时自动重试次数|number|0|
|
|
78
|
+
|forceHttps|是否强制使用https|boolean|-|
|
|
79
|
+
|boxWidth|预览模式下,父容器宽度|number|-|
|
|
80
|
+
|boxHeight|预览模式下,父容器高度|number|-|
|
|
81
|
+
|topOverlap|图片顶层内容|ReactNode|-|
|
|
82
|
+
|bottomOverlap|图片底层内容(placeholder),默认是灰色兜底,传null可移除|ReactNode|-|
|
|
83
|
+
|showImage|手动控制是否加载图片|boolean|-|
|
|
84
|
+
|staticLabel|是否直接渲染\<img\>标签,不走加载图片流程|boolean|-|
|
|
85
|
+
|nativeProps|img标签原生属性,优先级低于单独设置|DetailedHTMLProps\<ImgHTMLAttributes\<HTMLImageElement\>, HTMLImageElement\>|-|
|
|
86
|
+
|onChange|切换status时触发的回调|(status: string) =\> void|-|
|
|
87
|
+
|onClick|点击图片时触发的回调|(e: MouseEvent\<HTMLElement, MouseEvent\>) =\> void|-|
|
|
88
|
+
|onLoad|图片加载完毕时触发的回调|(e: Event, image: HTMLImageElement) =\> void|-|
|
|
89
|
+
|onError|图片加载失败时触发的回调,如果有自动重试则在重试最终失败后触发|(e: string \| Event) =\> void|-|
|
|
90
|
+
|onAutoRetry|图片加载失败时自动重试触发的回调|(e: string \| Event) =\> void|-|
|
|
91
|
+
|
|
92
|
+
> ImageStatus
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
"loaded" | "loading" | "error" | "init"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
> ObjectPosition
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
string | number | string & {}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
> SelectCallback
|
|
105
|
+
|
|
106
|
+
|参数|描述|类型|
|
|
107
|
+
|----------|-------------|------|
|
|
108
|
+
|files|文件列表|AdapterFile\[\]|
|
|
109
|
+
|
|
110
|
+
> AdapterFile
|
|
111
|
+
|
|
112
|
+
|参数|描述|类型|
|
|
113
|
+
|----------|-------------|------|
|
|
114
|
+
|url|文件 url|string|
|
|
115
|
+
|size|文件大小|number|
|
|
116
|
+
|name|文件名|string|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export default function AddIcon() {
|
|
4
|
+
return (
|
|
5
|
+
<svg width="30" height="30" viewBox="0 0 30 30" fill="none">
|
|
6
|
+
<path
|
|
7
|
+
fillRule="evenodd"
|
|
8
|
+
clipRule="evenodd"
|
|
9
|
+
d="M14.5 0C14.2239 0 14 0.223857 14 0.5V14H0.5C0.223858 14 0 14.2239 0 14.5V15.5C0 15.7761 0.223857 16 0.5 16H14V29.5C14 29.7761 14.2239 30 14.5 30H15.5C15.7761 30 16 29.7761 16 29.5V16H29.5C29.7761 16 30 15.7761 30 15.5V14.5C30 14.2239 29.7761 14 29.5 14H16V0.5C16 0.223858 15.7761 0 15.5 0H14.5Z"
|
|
10
|
+
fill="#C9CDD4"
|
|
11
|
+
/>
|
|
12
|
+
</svg>
|
|
13
|
+
);
|
|
14
|
+
}
|