@agentscope-ai/chat 1.1.53 → 1.1.54-beta.1773217735669
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/components/Attachments/FileList/FileListCard.tsx +57 -97
- package/components/Attachments/FileList/ImageCard.tsx +197 -0
- package/components/Attachments/FileList/index.tsx +7 -0
- package/components/Attachments/index.tsx +29 -0
- package/components/Attachments/style/fileCard.ts +42 -3
- package/components/ChatAnywhere/Input/index.tsx +1 -0
- package/lib/Attachments/FileList/FileListCard.d.ts +2 -1
- package/lib/Attachments/FileList/FileListCard.js +27 -61
- package/lib/Attachments/FileList/ImageCard.d.ts +31 -0
- package/lib/Attachments/FileList/ImageCard.js +162 -0
- package/lib/Attachments/FileList/index.d.ts +5 -0
- package/lib/Attachments/FileList/index.js +2 -0
- package/lib/Attachments/index.d.ts +7 -0
- package/lib/Attachments/index.js +23 -1
- package/lib/Attachments/style/fileCard.js +11 -1
- package/lib/ChatAnywhere/Input/index.js +1 -0
- package/package.json +1 -1
|
@@ -2,16 +2,14 @@ import classnames from 'classnames';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import type { Attachment } from '..';
|
|
4
4
|
import { AttachmentContext } from '../context';
|
|
5
|
-
import { previewImage } from '../util';
|
|
6
|
-
import AudioIcon from './AudioIcon';
|
|
7
|
-
import Progress from './Progress';
|
|
8
|
-
import VideoIcon from './VideoIcon';
|
|
9
5
|
import Style from '../style/fileCard';
|
|
10
6
|
import { useProviderContext } from '@agentscope-ai/chat';
|
|
11
7
|
import { SparkFalseLine } from '@agentscope-ai/icons';
|
|
8
|
+
import ImageCard from './ImageCard';
|
|
9
|
+
import type { ImageCardProps } from './ImageCard';
|
|
12
10
|
|
|
13
11
|
|
|
14
|
-
export interface FileListCardProps {
|
|
12
|
+
export interface FileListCardProps extends Pick<ImageCardProps, 'onReplace'> {
|
|
15
13
|
/**
|
|
16
14
|
* @description 自定义CSS类名前缀,用于样式隔离和主题定制
|
|
17
15
|
* @descriptionEn Custom CSS class name prefix for style isolation and theme customization
|
|
@@ -42,7 +40,6 @@ export interface FileListCardProps {
|
|
|
42
40
|
* @descriptionEn Render type, currently only supports default render mode
|
|
43
41
|
*/
|
|
44
42
|
renderType?: 'default',
|
|
45
|
-
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
const EMPTY = '\u00A0';
|
|
@@ -126,7 +123,7 @@ function getSize(size: number) {
|
|
|
126
123
|
|
|
127
124
|
function FileListCard(props: FileListCardProps, ref: React.Ref<HTMLDivElement>) {
|
|
128
125
|
const { getPrefixCls } = useProviderContext();
|
|
129
|
-
const { item, onRemove, className, style } = props;
|
|
126
|
+
const { item, onRemove, onReplace, className, style } = props;
|
|
130
127
|
const context = React.useContext(AttachmentContext);
|
|
131
128
|
const { disabled } = context || {};
|
|
132
129
|
const { name, size, percent, status = 'done', description } = item;
|
|
@@ -141,7 +138,23 @@ function FileListCard(props: FileListCardProps, ref: React.Ref<HTMLDivElement>)
|
|
|
141
138
|
|
|
142
139
|
const isImg = React.useMemo(() => matchExt(nameSuffix, IMG_EXTS), [nameSuffix]);
|
|
143
140
|
|
|
144
|
-
const
|
|
141
|
+
const renderType = props.renderType || 'default';
|
|
142
|
+
const isImgPreview = isImg && (item.originFileObj || item.thumbUrl || item.url) && renderType === 'default';
|
|
143
|
+
|
|
144
|
+
if (isImgPreview) {
|
|
145
|
+
return (
|
|
146
|
+
<ImageCard
|
|
147
|
+
ref={ref}
|
|
148
|
+
item={item}
|
|
149
|
+
onRemove={onRemove}
|
|
150
|
+
onReplace={onReplace}
|
|
151
|
+
className={className}
|
|
152
|
+
style={style}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const desc = (() => {
|
|
145
158
|
if (description) {
|
|
146
159
|
return description;
|
|
147
160
|
}
|
|
@@ -155,9 +168,9 @@ function FileListCard(props: FileListCardProps, ref: React.Ref<HTMLDivElement>)
|
|
|
155
168
|
}
|
|
156
169
|
|
|
157
170
|
return size ? getSize(size) : EMPTY;
|
|
158
|
-
}
|
|
171
|
+
})();
|
|
159
172
|
|
|
160
|
-
const [icon, iconColor] =
|
|
173
|
+
const [icon, iconColor] = (() => {
|
|
161
174
|
for (const { ext, icon, color } of PRESET_FILE_ICONS) {
|
|
162
175
|
if (matchExt(nameSuffix, ext)) {
|
|
163
176
|
return [icon, color];
|
|
@@ -165,57 +178,25 @@ function FileListCard(props: FileListCardProps, ref: React.Ref<HTMLDivElement>)
|
|
|
165
178
|
}
|
|
166
179
|
|
|
167
180
|
return [<IconImage url="https://gw.alicdn.com/imgextra/i1/O1CN01K7jgEj1sywWTkPSGY_!!6000000005836-55-tps-40-40.svg" key="defaultIcon" />, DEFAULT_ICON_COLOR];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
setPreviewImg(undefined);
|
|
186
|
-
}, [item.originFileObj]);
|
|
187
|
-
|
|
188
|
-
let content: React.ReactNode = null;
|
|
189
|
-
const previewUrl = item.thumbUrl || item.url || previewImg;
|
|
190
|
-
const renderType = props.renderType || 'default';
|
|
191
|
-
const isImgPreview = isImg && (item.originFileObj || previewUrl) && renderType === 'default';
|
|
192
|
-
|
|
193
|
-
if (isImgPreview) {
|
|
194
|
-
content = (
|
|
195
|
-
<>
|
|
196
|
-
{
|
|
197
|
-
previewUrl && (
|
|
198
|
-
<img alt="preview" src={previewUrl} />
|
|
199
|
-
)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
{status !== 'done' && (
|
|
203
|
-
<div className={`${cardCls}-img-mask`}>
|
|
204
|
-
{status === 'uploading' && percent !== undefined && (
|
|
205
|
-
<Progress percent={percent} prefixCls={cardCls} />
|
|
206
|
-
)}
|
|
207
|
-
{status === 'error' && (
|
|
208
|
-
<div className={`${cardCls}-desc`}>
|
|
209
|
-
<div className={`${cardCls}-ellipsis-prefix`}>{desc}</div>
|
|
210
|
-
</div>
|
|
211
|
-
)}
|
|
212
|
-
</div>
|
|
181
|
+
})();
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<>
|
|
185
|
+
<Style />
|
|
186
|
+
<div
|
|
187
|
+
className={classnames(
|
|
188
|
+
cardCls,
|
|
189
|
+
{
|
|
190
|
+
[`${cardCls}-status-${status}`]: status,
|
|
191
|
+
[`${cardCls}-type-overview`]: true,
|
|
192
|
+
[`${cardCls}-type-${renderType}`]: true,
|
|
193
|
+
[`${cardCls}-hoverable`]: !disabled && onRemove,
|
|
194
|
+
},
|
|
195
|
+
className,
|
|
213
196
|
)}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
content = (
|
|
218
|
-
<>
|
|
197
|
+
style={style}
|
|
198
|
+
ref={ref}
|
|
199
|
+
>
|
|
219
200
|
<div className={`${cardCls}-icon`} style={{ color: iconColor }}>
|
|
220
201
|
{icon}
|
|
221
202
|
</div>
|
|
@@ -227,44 +208,23 @@ function FileListCard(props: FileListCardProps, ref: React.Ref<HTMLDivElement>)
|
|
|
227
208
|
<div className={`${cardCls}-ellipsis-prefix`}>{desc}</div>
|
|
228
209
|
</div>
|
|
229
210
|
</div>
|
|
230
|
-
</>
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return <>
|
|
235
|
-
<Style />
|
|
236
|
-
<div
|
|
237
|
-
className={classnames(
|
|
238
|
-
cardCls,
|
|
239
|
-
{
|
|
240
|
-
[`${cardCls}-status-${status}`]: status,
|
|
241
|
-
[`${cardCls}-type-preview`]: isImgPreview,
|
|
242
|
-
[`${cardCls}-type-overview`]: !isImgPreview,
|
|
243
|
-
[`${cardCls}-type-${renderType}`]: true,
|
|
244
|
-
[`${cardCls}-hoverable`]: !disabled && onRemove,
|
|
245
|
-
},
|
|
246
|
-
className,
|
|
247
|
-
)}
|
|
248
|
-
style={style}
|
|
249
|
-
ref={ref}
|
|
250
|
-
>
|
|
251
|
-
{content}
|
|
252
|
-
|
|
253
|
-
<button
|
|
254
|
-
style={{
|
|
255
|
-
opacity: !disabled && onRemove ? 1 : 0,
|
|
256
|
-
}}
|
|
257
|
-
className={`${cardCls}-remove`}
|
|
258
|
-
onClick={() => {
|
|
259
|
-
if (!disabled && onRemove) {
|
|
260
|
-
onRemove(item);
|
|
261
|
-
}
|
|
262
|
-
}}
|
|
263
|
-
>
|
|
264
|
-
<SparkFalseLine />
|
|
265
211
|
|
|
266
|
-
|
|
267
|
-
|
|
212
|
+
<button
|
|
213
|
+
style={{
|
|
214
|
+
opacity: !disabled && onRemove ? 1 : 0,
|
|
215
|
+
}}
|
|
216
|
+
className={`${cardCls}-remove`}
|
|
217
|
+
onClick={() => {
|
|
218
|
+
if (!disabled && onRemove) {
|
|
219
|
+
onRemove(item);
|
|
220
|
+
}
|
|
221
|
+
}}
|
|
222
|
+
>
|
|
223
|
+
<SparkFalseLine />
|
|
224
|
+
</button>
|
|
225
|
+
</div>
|
|
226
|
+
</>
|
|
227
|
+
);
|
|
268
228
|
}
|
|
269
229
|
|
|
270
230
|
export default React.forwardRef(FileListCard);
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import classnames from 'classnames';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Image } from 'antd';
|
|
4
|
+
import type { Attachment } from '..';
|
|
5
|
+
import { AttachmentContext } from '../context';
|
|
6
|
+
import { previewImage } from '../util';
|
|
7
|
+
import Progress from './Progress';
|
|
8
|
+
import Style from '../style/fileCard';
|
|
9
|
+
import { useProviderContext } from '@agentscope-ai/chat';
|
|
10
|
+
import { SparkFalseLine, SparkVisibleLine, SparkRefreshLine, SparkReplaceLine } from '@agentscope-ai/icons';
|
|
11
|
+
|
|
12
|
+
export interface ImageCardProps {
|
|
13
|
+
/**
|
|
14
|
+
* @description 文件附件数据对象,包含文件的基本信息
|
|
15
|
+
* @descriptionEn File attachment data object containing basic file information
|
|
16
|
+
*/
|
|
17
|
+
item: Attachment;
|
|
18
|
+
/**
|
|
19
|
+
* @description 文件移除时的回调函数,用于处理文件删除操作
|
|
20
|
+
* @descriptionEn Callback function when file is removed for handling file deletion operations
|
|
21
|
+
*/
|
|
22
|
+
onRemove?: (item: Attachment) => void;
|
|
23
|
+
/**
|
|
24
|
+
* @description 替换当前图片的回调,传入原始附件和新选择的文件
|
|
25
|
+
* @descriptionEn Callback to replace current image, receives the original attachment and newly selected file
|
|
26
|
+
*/
|
|
27
|
+
onReplace?: (oldItem: Attachment, file: File) => void;
|
|
28
|
+
/**
|
|
29
|
+
* @description 组件的CSS类名
|
|
30
|
+
* @descriptionEn CSS class name for the component
|
|
31
|
+
*/
|
|
32
|
+
className?: string;
|
|
33
|
+
/**
|
|
34
|
+
* @description 组件的内联样式对象
|
|
35
|
+
* @descriptionEn Inline style object for the component
|
|
36
|
+
*/
|
|
37
|
+
style?: React.CSSProperties;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const EMPTY = '\u00A0';
|
|
41
|
+
|
|
42
|
+
const IMG_ACCEPT = 'image/png,image/jpeg,image/jpg,image/gif,image/bmp,image/webp,image/svg+xml';
|
|
43
|
+
|
|
44
|
+
function ImageCard(props: ImageCardProps, ref: React.Ref<HTMLDivElement>) {
|
|
45
|
+
const { getPrefixCls } = useProviderContext();
|
|
46
|
+
const { item, onRemove, onReplace, className, style } = props;
|
|
47
|
+
const context = React.useContext(AttachmentContext);
|
|
48
|
+
const { disabled } = context || {};
|
|
49
|
+
const { percent, status = 'done', description } = item;
|
|
50
|
+
const prefixCls = getPrefixCls('attachment');
|
|
51
|
+
const cardCls = `${prefixCls}-list-card`;
|
|
52
|
+
|
|
53
|
+
const [previewVisible, setPreviewVisible] = React.useState(false);
|
|
54
|
+
const fileInputRef = React.useRef<HTMLInputElement>(null);
|
|
55
|
+
|
|
56
|
+
const previewConfig = React.useMemo(() => ({
|
|
57
|
+
visible: previewVisible,
|
|
58
|
+
onVisibleChange: setPreviewVisible,
|
|
59
|
+
}), [previewVisible]);
|
|
60
|
+
|
|
61
|
+
const desc = React.useMemo(() => {
|
|
62
|
+
if (description) {
|
|
63
|
+
return description;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (status === 'uploading') {
|
|
67
|
+
return `${percent || 0}%`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (status === 'error') {
|
|
71
|
+
return item.response || EMPTY;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return EMPTY;
|
|
75
|
+
}, [description, status, percent, item.response]);
|
|
76
|
+
|
|
77
|
+
const [previewImg, setPreviewImg] = React.useState<string>();
|
|
78
|
+
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
if (item.originFileObj) {
|
|
81
|
+
let synced = true;
|
|
82
|
+
previewImage(item.originFileObj).then((url) => {
|
|
83
|
+
if (synced) {
|
|
84
|
+
setPreviewImg(url);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return () => {
|
|
89
|
+
synced = false;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
setPreviewImg(undefined);
|
|
93
|
+
}, [item.originFileObj]);
|
|
94
|
+
|
|
95
|
+
const previewUrl = item.thumbUrl || item.url || previewImg;
|
|
96
|
+
|
|
97
|
+
const handleRefreshClick = (e: React.MouseEvent) => {
|
|
98
|
+
e.stopPropagation();
|
|
99
|
+
fileInputRef.current?.click();
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
103
|
+
const file = e.target.files?.[0];
|
|
104
|
+
if (file && onReplace) {
|
|
105
|
+
onReplace(item, file);
|
|
106
|
+
}
|
|
107
|
+
if (fileInputRef.current) {
|
|
108
|
+
fileInputRef.current.value = '';
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<Style />
|
|
115
|
+
<div
|
|
116
|
+
className={classnames(
|
|
117
|
+
cardCls,
|
|
118
|
+
{
|
|
119
|
+
[`${cardCls}-status-${status}`]: status,
|
|
120
|
+
[`${cardCls}-type-preview`]: true,
|
|
121
|
+
[`${cardCls}-hoverable`]: !disabled && onRemove,
|
|
122
|
+
},
|
|
123
|
+
className,
|
|
124
|
+
)}
|
|
125
|
+
style={style}
|
|
126
|
+
ref={ref}
|
|
127
|
+
>
|
|
128
|
+
{previewUrl && <img alt="preview" src={previewUrl} />}
|
|
129
|
+
|
|
130
|
+
<Image
|
|
131
|
+
src={previewUrl}
|
|
132
|
+
style={{ display: 'none' }}
|
|
133
|
+
preview={previewConfig}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
{status !== 'done' && (
|
|
137
|
+
<div className={`${cardCls}-img-mask`}>
|
|
138
|
+
{status === 'uploading' && percent !== undefined && (
|
|
139
|
+
<Progress percent={percent} prefixCls={cardCls} />
|
|
140
|
+
)}
|
|
141
|
+
{status === 'error' && (
|
|
142
|
+
<div className={`${cardCls}-desc`}>
|
|
143
|
+
<div className={`${cardCls}-ellipsis-prefix`}>{desc}</div>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
</div>
|
|
147
|
+
)}
|
|
148
|
+
|
|
149
|
+
{status === 'done' && (
|
|
150
|
+
<div className={`${cardCls}-img-hover-mask`}>
|
|
151
|
+
<button
|
|
152
|
+
className={`${cardCls}-img-action`}
|
|
153
|
+
onClick={(e) => {
|
|
154
|
+
e.stopPropagation();
|
|
155
|
+
setPreviewVisible(true);
|
|
156
|
+
}}
|
|
157
|
+
>
|
|
158
|
+
<SparkVisibleLine />
|
|
159
|
+
</button>
|
|
160
|
+
{onReplace && (
|
|
161
|
+
<button
|
|
162
|
+
className={`${cardCls}-img-action`}
|
|
163
|
+
onClick={handleRefreshClick}
|
|
164
|
+
>
|
|
165
|
+
<SparkReplaceLine />
|
|
166
|
+
</button>
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
)}
|
|
170
|
+
|
|
171
|
+
<input
|
|
172
|
+
ref={fileInputRef}
|
|
173
|
+
type="file"
|
|
174
|
+
accept={IMG_ACCEPT}
|
|
175
|
+
style={{ display: 'none' }}
|
|
176
|
+
onChange={handleFileChange}
|
|
177
|
+
/>
|
|
178
|
+
|
|
179
|
+
<button
|
|
180
|
+
style={{
|
|
181
|
+
opacity: !disabled && onRemove ? 1 : 0,
|
|
182
|
+
}}
|
|
183
|
+
className={`${cardCls}-remove`}
|
|
184
|
+
onClick={() => {
|
|
185
|
+
if (!disabled && onRemove) {
|
|
186
|
+
onRemove(item);
|
|
187
|
+
}
|
|
188
|
+
}}
|
|
189
|
+
>
|
|
190
|
+
<SparkFalseLine />
|
|
191
|
+
</button>
|
|
192
|
+
</div>
|
|
193
|
+
</>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export default React.forwardRef(ImageCard);
|
|
@@ -22,6 +22,11 @@ export interface FileListProps {
|
|
|
22
22
|
* @descriptionEn Callback function when file is removed for handling file deletion operations
|
|
23
23
|
*/
|
|
24
24
|
onRemove: (item: Attachment) => void;
|
|
25
|
+
/**
|
|
26
|
+
* @description 替换图片文件的回调,传入原始附件和新选择的文件
|
|
27
|
+
* @descriptionEn Callback to replace an image file, receives the original attachment and newly selected file
|
|
28
|
+
*/
|
|
29
|
+
onReplace?: (oldItem: Attachment, file: File) => void;
|
|
25
30
|
/**
|
|
26
31
|
* @description 文件列表的溢出处理方式,影响滚动和布局行为
|
|
27
32
|
* @descriptionEn Overflow handling method for file list, affects scrolling and layout behavior
|
|
@@ -68,6 +73,7 @@ export default function FileList(props: FileListProps) {
|
|
|
68
73
|
prefixCls,
|
|
69
74
|
items,
|
|
70
75
|
onRemove,
|
|
76
|
+
onReplace,
|
|
71
77
|
overflow,
|
|
72
78
|
listClassName,
|
|
73
79
|
listStyle,
|
|
@@ -167,6 +173,7 @@ export default function FileList(props: FileListProps) {
|
|
|
167
173
|
prefixCls={prefixCls}
|
|
168
174
|
item={item}
|
|
169
175
|
onRemove={onRemove}
|
|
176
|
+
onReplace={onReplace}
|
|
170
177
|
className={classnames(motionCls, itemClassName)}
|
|
171
178
|
style={{
|
|
172
179
|
...motionStyle,
|
|
@@ -7,6 +7,7 @@ import { useEvent, useMergedState } from 'rc-util';
|
|
|
7
7
|
import DropArea from './DropArea';
|
|
8
8
|
import FileList, { type FileListProps } from './FileList';
|
|
9
9
|
import FileListCard from './FileList/FileListCard';
|
|
10
|
+
import ImageCard from './FileList/ImageCard';
|
|
10
11
|
import PlaceholderUploader, {
|
|
11
12
|
type PlaceholderProps,
|
|
12
13
|
type PlaceholderType,
|
|
@@ -92,6 +93,11 @@ export interface AttachmentsProps extends Omit<UploadProps, 'fileList'> {
|
|
|
92
93
|
* @descriptionEn Render type, currently only supports default render mode
|
|
93
94
|
*/
|
|
94
95
|
renderType?: 'default',
|
|
96
|
+
/**
|
|
97
|
+
* @description 图片类型文件是否支持点击刷新按钮直接替换上传
|
|
98
|
+
* @descriptionEn Whether image files support direct replacement upload via the refresh button
|
|
99
|
+
*/
|
|
100
|
+
replaceable?: boolean;
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
export interface AttachmentsRef {
|
|
@@ -121,6 +127,7 @@ function Attachments(props: AttachmentsProps, ref: React.Ref<AttachmentsRef>) {
|
|
|
121
127
|
onChange,
|
|
122
128
|
overflow,
|
|
123
129
|
disabled,
|
|
130
|
+
replaceable,
|
|
124
131
|
classNames = {},
|
|
125
132
|
styles = {},
|
|
126
133
|
...uploadProps
|
|
@@ -170,6 +177,25 @@ function Attachments(props: AttachmentsProps, ref: React.Ref<AttachmentsRef>) {
|
|
|
170
177
|
});
|
|
171
178
|
};
|
|
172
179
|
|
|
180
|
+
const onItemReplace = useEvent((oldItem: Attachment, file: File) => {
|
|
181
|
+
const newAttachment: Attachment = {
|
|
182
|
+
uid: oldItem.uid,
|
|
183
|
+
name: file.name,
|
|
184
|
+
size: file.size,
|
|
185
|
+
type: file.type,
|
|
186
|
+
originFileObj: file as any,
|
|
187
|
+
status: 'done',
|
|
188
|
+
percent: 100,
|
|
189
|
+
};
|
|
190
|
+
const newFileList = fileList.map((fileItem) =>
|
|
191
|
+
fileItem.uid === oldItem.uid ? newAttachment : fileItem,
|
|
192
|
+
);
|
|
193
|
+
triggerChange({
|
|
194
|
+
file: newAttachment,
|
|
195
|
+
fileList: newFileList,
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
173
199
|
let renderChildren: React.ReactElement;
|
|
174
200
|
|
|
175
201
|
const getPlaceholderNode = (
|
|
@@ -233,6 +259,7 @@ function Attachments(props: AttachmentsProps, ref: React.Ref<AttachmentsRef>) {
|
|
|
233
259
|
prefixCls={prefixCls}
|
|
234
260
|
items={fileList}
|
|
235
261
|
onRemove={onItemRemove}
|
|
262
|
+
onReplace={replaceable ? onItemReplace : undefined}
|
|
236
263
|
overflow={overflow}
|
|
237
264
|
upload={mergedUploadProps}
|
|
238
265
|
listClassName={classnames(classNames.list)}
|
|
@@ -267,8 +294,10 @@ const ForwardAttachments = React.forwardRef(Attachments) as React.ForwardRefExot
|
|
|
267
294
|
AttachmentsProps & React.RefAttributes<AttachmentsRef>
|
|
268
295
|
> & {
|
|
269
296
|
FileCard: typeof FileListCard;
|
|
297
|
+
ImageCard: typeof ImageCard;
|
|
270
298
|
};
|
|
271
299
|
|
|
272
300
|
ForwardAttachments.FileCard = FileListCard;
|
|
301
|
+
ForwardAttachments.ImageCard = ImageCard;
|
|
273
302
|
|
|
274
303
|
export default ForwardAttachments;
|
|
@@ -61,7 +61,7 @@ export default createGlobalStyle`
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
&-type-preview {
|
|
64
|
-
width:
|
|
64
|
+
width: 100px;
|
|
65
65
|
height: 56px;
|
|
66
66
|
line-height: 1;
|
|
67
67
|
|
|
@@ -70,7 +70,7 @@ export default createGlobalStyle`
|
|
|
70
70
|
height: 100%;
|
|
71
71
|
vertical-align: top;
|
|
72
72
|
object-fit: cover;
|
|
73
|
-
border-radius:
|
|
73
|
+
border-radius: 6px;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
.${(p) => p.theme.prefixCls}-attachment-list-card-img-mask {
|
|
@@ -83,6 +83,44 @@ export default createGlobalStyle`
|
|
|
83
83
|
border-radius: inherit;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
.${(p) => p.theme.prefixCls}-attachment-list-card-img-hover-mask {
|
|
87
|
+
position: absolute;
|
|
88
|
+
inset: 0;
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-direction: row;
|
|
91
|
+
justify-content: center;
|
|
92
|
+
align-items: center;
|
|
93
|
+
gap: 16px;
|
|
94
|
+
background: rgba(20, 19, 39, 0.45);
|
|
95
|
+
border-radius: 6px;
|
|
96
|
+
opacity: 0;
|
|
97
|
+
transition: opacity 0.2s;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&:hover .${(p) => p.theme.prefixCls}-attachment-list-card-img-hover-mask {
|
|
101
|
+
opacity: 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.${(p) => p.theme.prefixCls}-attachment-list-card-img-action {
|
|
105
|
+
display: flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
justify-content: center;
|
|
108
|
+
width: 20px;
|
|
109
|
+
height: 20px;
|
|
110
|
+
padding: 0;
|
|
111
|
+
border: none;
|
|
112
|
+
background: transparent;
|
|
113
|
+
color: ${(p) => p.theme.colorWhite};
|
|
114
|
+
font-size: 20px;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
line-height: 1;
|
|
117
|
+
transition: opacity 0.2s;
|
|
118
|
+
|
|
119
|
+
&:hover {
|
|
120
|
+
opacity: 0.8;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
86
124
|
&.${(p) => p.theme.prefixCls}-attachment-list-card-status-error {
|
|
87
125
|
|
|
88
126
|
img,
|
|
@@ -161,7 +199,9 @@ export default createGlobalStyle`
|
|
|
161
199
|
|
|
162
200
|
&:hover {
|
|
163
201
|
border-color: ${(p) => p.theme.colorPrimary};
|
|
202
|
+
}
|
|
164
203
|
|
|
204
|
+
&.${(p) => p.theme.prefixCls}-attachment-list-card-type-overview:hover {
|
|
165
205
|
&::after {
|
|
166
206
|
content: '';
|
|
167
207
|
position: absolute;
|
|
@@ -173,6 +213,5 @@ export default createGlobalStyle`
|
|
|
173
213
|
background-color: rgba(0, 0, 0, 0.45);
|
|
174
214
|
}
|
|
175
215
|
}
|
|
176
|
-
|
|
177
216
|
}
|
|
178
217
|
`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Attachment } from '..';
|
|
3
|
-
|
|
3
|
+
import type { ImageCardProps } from './ImageCard';
|
|
4
|
+
export interface FileListCardProps extends Pick<ImageCardProps, 'onReplace'> {
|
|
4
5
|
/**
|
|
5
6
|
* @description 自定义CSS类名前缀,用于样式隔离和主题定制
|
|
6
7
|
* @descriptionEn Custom CSS class name prefix for style isolation and theme customization
|
|
@@ -12,11 +12,10 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
|
12
12
|
import classnames from 'classnames';
|
|
13
13
|
import React from 'react';
|
|
14
14
|
import { AttachmentContext } from "../context";
|
|
15
|
-
import { previewImage } from "../util";
|
|
16
|
-
import Progress from "./Progress";
|
|
17
15
|
import Style from "../style/fileCard";
|
|
18
16
|
import { useProviderContext } from "../..";
|
|
19
17
|
import { SparkFalseLine } from '@agentscope-ai/icons';
|
|
18
|
+
import ImageCard from "./ImageCard";
|
|
20
19
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
21
20
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
21
|
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
@@ -106,6 +105,7 @@ function FileListCard(props, ref) {
|
|
|
106
105
|
getPrefixCls = _useProviderContext.getPrefixCls;
|
|
107
106
|
var item = props.item,
|
|
108
107
|
onRemove = props.onRemove,
|
|
108
|
+
onReplace = props.onReplace,
|
|
109
109
|
className = props.className,
|
|
110
110
|
style = props.style;
|
|
111
111
|
var context = React.useContext(AttachmentContext);
|
|
@@ -130,7 +130,19 @@ function FileListCard(props, ref) {
|
|
|
130
130
|
var isImg = React.useMemo(function () {
|
|
131
131
|
return matchExt(nameSuffix, IMG_EXTS);
|
|
132
132
|
}, [nameSuffix]);
|
|
133
|
-
var
|
|
133
|
+
var renderType = props.renderType || 'default';
|
|
134
|
+
var isImgPreview = isImg && (item.originFileObj || item.thumbUrl || item.url) && renderType === 'default';
|
|
135
|
+
if (isImgPreview) {
|
|
136
|
+
return /*#__PURE__*/_jsx(ImageCard, {
|
|
137
|
+
ref: ref,
|
|
138
|
+
item: item,
|
|
139
|
+
onRemove: onRemove,
|
|
140
|
+
onReplace: onReplace,
|
|
141
|
+
className: className,
|
|
142
|
+
style: style
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
var desc = function () {
|
|
134
146
|
if (description) {
|
|
135
147
|
return description;
|
|
136
148
|
}
|
|
@@ -141,8 +153,8 @@ function FileListCard(props, ref) {
|
|
|
141
153
|
return item.response || EMPTY;
|
|
142
154
|
}
|
|
143
155
|
return size ? getSize(size) : EMPTY;
|
|
144
|
-
}
|
|
145
|
-
var
|
|
156
|
+
}();
|
|
157
|
+
var _ref3 = function () {
|
|
146
158
|
var _iterator = _createForOfIteratorHelper(PRESET_FILE_ICONS),
|
|
147
159
|
_step;
|
|
148
160
|
try {
|
|
@@ -163,53 +175,15 @@ function FileListCard(props, ref) {
|
|
|
163
175
|
return [/*#__PURE__*/_jsx(IconImage, {
|
|
164
176
|
url: "https://gw.alicdn.com/imgextra/i1/O1CN01K7jgEj1sywWTkPSGY_!!6000000005836-55-tps-40-40.svg"
|
|
165
177
|
}, "defaultIcon"), DEFAULT_ICON_COLOR];
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
icon =
|
|
169
|
-
iconColor =
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (item.originFileObj) {
|
|
176
|
-
var synced = true;
|
|
177
|
-
previewImage(item.originFileObj).then(function (url) {
|
|
178
|
-
if (synced) {
|
|
179
|
-
setPreviewImg(url);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
return function () {
|
|
183
|
-
synced = false;
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
setPreviewImg(undefined);
|
|
187
|
-
}, [item.originFileObj]);
|
|
188
|
-
var content = null;
|
|
189
|
-
var previewUrl = item.thumbUrl || item.url || previewImg;
|
|
190
|
-
var renderType = props.renderType || 'default';
|
|
191
|
-
var isImgPreview = isImg && (item.originFileObj || previewUrl) && renderType === 'default';
|
|
192
|
-
if (isImgPreview) {
|
|
193
|
-
content = /*#__PURE__*/_jsxs(_Fragment, {
|
|
194
|
-
children: [previewUrl && /*#__PURE__*/_jsx("img", {
|
|
195
|
-
alt: "preview",
|
|
196
|
-
src: previewUrl
|
|
197
|
-
}), status !== 'done' && /*#__PURE__*/_jsxs("div", {
|
|
198
|
-
className: "".concat(cardCls, "-img-mask"),
|
|
199
|
-
children: [status === 'uploading' && percent !== undefined && /*#__PURE__*/_jsx(Progress, {
|
|
200
|
-
percent: percent,
|
|
201
|
-
prefixCls: cardCls
|
|
202
|
-
}), status === 'error' && /*#__PURE__*/_jsx("div", {
|
|
203
|
-
className: "".concat(cardCls, "-desc"),
|
|
204
|
-
children: /*#__PURE__*/_jsx("div", {
|
|
205
|
-
className: "".concat(cardCls, "-ellipsis-prefix"),
|
|
206
|
-
children: desc
|
|
207
|
-
})
|
|
208
|
-
})]
|
|
209
|
-
})]
|
|
210
|
-
});
|
|
211
|
-
} else {
|
|
212
|
-
content = /*#__PURE__*/_jsxs(_Fragment, {
|
|
178
|
+
}(),
|
|
179
|
+
_ref4 = _slicedToArray(_ref3, 2),
|
|
180
|
+
icon = _ref4[0],
|
|
181
|
+
iconColor = _ref4[1];
|
|
182
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
183
|
+
children: [/*#__PURE__*/_jsx(Style, {}), /*#__PURE__*/_jsxs("div", {
|
|
184
|
+
className: classnames(cardCls, _defineProperty(_defineProperty(_defineProperty(_defineProperty({}, "".concat(cardCls, "-status-").concat(status), status), "".concat(cardCls, "-type-overview"), true), "".concat(cardCls, "-type-").concat(renderType), true), "".concat(cardCls, "-hoverable"), !disabled && onRemove), className),
|
|
185
|
+
style: style,
|
|
186
|
+
ref: ref,
|
|
213
187
|
children: [/*#__PURE__*/_jsx("div", {
|
|
214
188
|
className: "".concat(cardCls, "-icon"),
|
|
215
189
|
style: {
|
|
@@ -228,15 +202,7 @@ function FileListCard(props, ref) {
|
|
|
228
202
|
children: desc
|
|
229
203
|
})
|
|
230
204
|
})]
|
|
231
|
-
})
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
235
|
-
children: [/*#__PURE__*/_jsx(Style, {}), /*#__PURE__*/_jsxs("div", {
|
|
236
|
-
className: classnames(cardCls, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, "".concat(cardCls, "-status-").concat(status), status), "".concat(cardCls, "-type-preview"), isImgPreview), "".concat(cardCls, "-type-overview"), !isImgPreview), "".concat(cardCls, "-type-").concat(renderType), true), "".concat(cardCls, "-hoverable"), !disabled && onRemove), className),
|
|
237
|
-
style: style,
|
|
238
|
-
ref: ref,
|
|
239
|
-
children: [content, /*#__PURE__*/_jsx("button", {
|
|
205
|
+
}), /*#__PURE__*/_jsx("button", {
|
|
240
206
|
style: {
|
|
241
207
|
opacity: !disabled && onRemove ? 1 : 0
|
|
242
208
|
},
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Attachment } from '..';
|
|
3
|
+
export interface ImageCardProps {
|
|
4
|
+
/**
|
|
5
|
+
* @description 文件附件数据对象,包含文件的基本信息
|
|
6
|
+
* @descriptionEn File attachment data object containing basic file information
|
|
7
|
+
*/
|
|
8
|
+
item: Attachment;
|
|
9
|
+
/**
|
|
10
|
+
* @description 文件移除时的回调函数,用于处理文件删除操作
|
|
11
|
+
* @descriptionEn Callback function when file is removed for handling file deletion operations
|
|
12
|
+
*/
|
|
13
|
+
onRemove?: (item: Attachment) => void;
|
|
14
|
+
/**
|
|
15
|
+
* @description 替换当前图片的回调,传入原始附件和新选择的文件
|
|
16
|
+
* @descriptionEn Callback to replace current image, receives the original attachment and newly selected file
|
|
17
|
+
*/
|
|
18
|
+
onReplace?: (oldItem: Attachment, file: File) => void;
|
|
19
|
+
/**
|
|
20
|
+
* @description 组件的CSS类名
|
|
21
|
+
* @descriptionEn CSS class name for the component
|
|
22
|
+
*/
|
|
23
|
+
className?: string;
|
|
24
|
+
/**
|
|
25
|
+
* @description 组件的内联样式对象
|
|
26
|
+
* @descriptionEn Inline style object for the component
|
|
27
|
+
*/
|
|
28
|
+
style?: React.CSSProperties;
|
|
29
|
+
}
|
|
30
|
+
declare const _default: React.ForwardRefExoticComponent<ImageCardProps & React.RefAttributes<HTMLDivElement>>;
|
|
31
|
+
export default _default;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
3
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
4
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
5
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
6
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
7
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
8
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
9
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
10
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
11
|
+
import classnames from 'classnames';
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { Image } from 'antd';
|
|
14
|
+
import { AttachmentContext } from "../context";
|
|
15
|
+
import { previewImage } from "../util";
|
|
16
|
+
import Progress from "./Progress";
|
|
17
|
+
import Style from "../style/fileCard";
|
|
18
|
+
import { useProviderContext } from "../..";
|
|
19
|
+
import { SparkFalseLine, SparkVisibleLine, SparkReplaceLine } from '@agentscope-ai/icons';
|
|
20
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
21
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
23
|
+
var EMPTY = "\xA0";
|
|
24
|
+
var IMG_ACCEPT = 'image/png,image/jpeg,image/jpg,image/gif,image/bmp,image/webp,image/svg+xml';
|
|
25
|
+
function ImageCard(props, ref) {
|
|
26
|
+
var _useProviderContext = useProviderContext(),
|
|
27
|
+
getPrefixCls = _useProviderContext.getPrefixCls;
|
|
28
|
+
var item = props.item,
|
|
29
|
+
onRemove = props.onRemove,
|
|
30
|
+
onReplace = props.onReplace,
|
|
31
|
+
className = props.className,
|
|
32
|
+
style = props.style;
|
|
33
|
+
var context = React.useContext(AttachmentContext);
|
|
34
|
+
var _ref = context || {},
|
|
35
|
+
disabled = _ref.disabled;
|
|
36
|
+
var percent = item.percent,
|
|
37
|
+
_item$status = item.status,
|
|
38
|
+
status = _item$status === void 0 ? 'done' : _item$status,
|
|
39
|
+
description = item.description;
|
|
40
|
+
var prefixCls = getPrefixCls('attachment');
|
|
41
|
+
var cardCls = "".concat(prefixCls, "-list-card");
|
|
42
|
+
var _React$useState = React.useState(false),
|
|
43
|
+
_React$useState2 = _slicedToArray(_React$useState, 2),
|
|
44
|
+
previewVisible = _React$useState2[0],
|
|
45
|
+
setPreviewVisible = _React$useState2[1];
|
|
46
|
+
var fileInputRef = React.useRef(null);
|
|
47
|
+
var previewConfig = React.useMemo(function () {
|
|
48
|
+
return {
|
|
49
|
+
visible: previewVisible,
|
|
50
|
+
onVisibleChange: setPreviewVisible
|
|
51
|
+
};
|
|
52
|
+
}, [previewVisible]);
|
|
53
|
+
var desc = React.useMemo(function () {
|
|
54
|
+
if (description) {
|
|
55
|
+
return description;
|
|
56
|
+
}
|
|
57
|
+
if (status === 'uploading') {
|
|
58
|
+
return "".concat(percent || 0, "%");
|
|
59
|
+
}
|
|
60
|
+
if (status === 'error') {
|
|
61
|
+
return item.response || EMPTY;
|
|
62
|
+
}
|
|
63
|
+
return EMPTY;
|
|
64
|
+
}, [description, status, percent, item.response]);
|
|
65
|
+
var _React$useState3 = React.useState(),
|
|
66
|
+
_React$useState4 = _slicedToArray(_React$useState3, 2),
|
|
67
|
+
previewImg = _React$useState4[0],
|
|
68
|
+
setPreviewImg = _React$useState4[1];
|
|
69
|
+
React.useEffect(function () {
|
|
70
|
+
if (item.originFileObj) {
|
|
71
|
+
var synced = true;
|
|
72
|
+
previewImage(item.originFileObj).then(function (url) {
|
|
73
|
+
if (synced) {
|
|
74
|
+
setPreviewImg(url);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return function () {
|
|
78
|
+
synced = false;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
setPreviewImg(undefined);
|
|
82
|
+
}, [item.originFileObj]);
|
|
83
|
+
var previewUrl = item.thumbUrl || item.url || previewImg;
|
|
84
|
+
var handleRefreshClick = function handleRefreshClick(e) {
|
|
85
|
+
var _fileInputRef$current;
|
|
86
|
+
e.stopPropagation();
|
|
87
|
+
(_fileInputRef$current = fileInputRef.current) === null || _fileInputRef$current === void 0 || _fileInputRef$current.click();
|
|
88
|
+
};
|
|
89
|
+
var handleFileChange = function handleFileChange(e) {
|
|
90
|
+
var _e$target$files;
|
|
91
|
+
var file = (_e$target$files = e.target.files) === null || _e$target$files === void 0 ? void 0 : _e$target$files[0];
|
|
92
|
+
if (file && onReplace) {
|
|
93
|
+
onReplace(item, file);
|
|
94
|
+
}
|
|
95
|
+
if (fileInputRef.current) {
|
|
96
|
+
fileInputRef.current.value = '';
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
100
|
+
children: [/*#__PURE__*/_jsx(Style, {}), /*#__PURE__*/_jsxs("div", {
|
|
101
|
+
className: classnames(cardCls, _defineProperty(_defineProperty(_defineProperty({}, "".concat(cardCls, "-status-").concat(status), status), "".concat(cardCls, "-type-preview"), true), "".concat(cardCls, "-hoverable"), !disabled && onRemove), className),
|
|
102
|
+
style: style,
|
|
103
|
+
ref: ref,
|
|
104
|
+
children: [previewUrl && /*#__PURE__*/_jsx("img", {
|
|
105
|
+
alt: "preview",
|
|
106
|
+
src: previewUrl
|
|
107
|
+
}), /*#__PURE__*/_jsx(Image, {
|
|
108
|
+
src: previewUrl,
|
|
109
|
+
style: {
|
|
110
|
+
display: 'none'
|
|
111
|
+
},
|
|
112
|
+
preview: previewConfig
|
|
113
|
+
}), status !== 'done' && /*#__PURE__*/_jsxs("div", {
|
|
114
|
+
className: "".concat(cardCls, "-img-mask"),
|
|
115
|
+
children: [status === 'uploading' && percent !== undefined && /*#__PURE__*/_jsx(Progress, {
|
|
116
|
+
percent: percent,
|
|
117
|
+
prefixCls: cardCls
|
|
118
|
+
}), status === 'error' && /*#__PURE__*/_jsx("div", {
|
|
119
|
+
className: "".concat(cardCls, "-desc"),
|
|
120
|
+
children: /*#__PURE__*/_jsx("div", {
|
|
121
|
+
className: "".concat(cardCls, "-ellipsis-prefix"),
|
|
122
|
+
children: desc
|
|
123
|
+
})
|
|
124
|
+
})]
|
|
125
|
+
}), status === 'done' && /*#__PURE__*/_jsxs("div", {
|
|
126
|
+
className: "".concat(cardCls, "-img-hover-mask"),
|
|
127
|
+
children: [/*#__PURE__*/_jsx("button", {
|
|
128
|
+
className: "".concat(cardCls, "-img-action"),
|
|
129
|
+
onClick: function onClick(e) {
|
|
130
|
+
e.stopPropagation();
|
|
131
|
+
setPreviewVisible(true);
|
|
132
|
+
},
|
|
133
|
+
children: /*#__PURE__*/_jsx(SparkVisibleLine, {})
|
|
134
|
+
}), onReplace && /*#__PURE__*/_jsx("button", {
|
|
135
|
+
className: "".concat(cardCls, "-img-action"),
|
|
136
|
+
onClick: handleRefreshClick,
|
|
137
|
+
children: /*#__PURE__*/_jsx(SparkReplaceLine, {})
|
|
138
|
+
})]
|
|
139
|
+
}), /*#__PURE__*/_jsx("input", {
|
|
140
|
+
ref: fileInputRef,
|
|
141
|
+
type: "file",
|
|
142
|
+
accept: IMG_ACCEPT,
|
|
143
|
+
style: {
|
|
144
|
+
display: 'none'
|
|
145
|
+
},
|
|
146
|
+
onChange: handleFileChange
|
|
147
|
+
}), /*#__PURE__*/_jsx("button", {
|
|
148
|
+
style: {
|
|
149
|
+
opacity: !disabled && onRemove ? 1 : 0
|
|
150
|
+
},
|
|
151
|
+
className: "".concat(cardCls, "-remove"),
|
|
152
|
+
onClick: function onClick() {
|
|
153
|
+
if (!disabled && onRemove) {
|
|
154
|
+
onRemove(item);
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
children: /*#__PURE__*/_jsx(SparkFalseLine, {})
|
|
158
|
+
})]
|
|
159
|
+
})]
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
export default /*#__PURE__*/React.forwardRef(ImageCard);
|
|
@@ -17,6 +17,11 @@ export interface FileListProps {
|
|
|
17
17
|
* @descriptionEn Callback function when file is removed for handling file deletion operations
|
|
18
18
|
*/
|
|
19
19
|
onRemove: (item: Attachment) => void;
|
|
20
|
+
/**
|
|
21
|
+
* @description 替换图片文件的回调,传入原始附件和新选择的文件
|
|
22
|
+
* @descriptionEn Callback to replace an image file, receives the original attachment and newly selected file
|
|
23
|
+
*/
|
|
24
|
+
onReplace?: (oldItem: Attachment, file: File) => void;
|
|
20
25
|
/**
|
|
21
26
|
* @description 文件列表的溢出处理方式,影响滚动和布局行为
|
|
22
27
|
* @descriptionEn Overflow handling method for file list, affects scrolling and layout behavior
|
|
@@ -24,6 +24,7 @@ export default function FileList(props) {
|
|
|
24
24
|
var prefixCls = props.prefixCls,
|
|
25
25
|
items = props.items,
|
|
26
26
|
onRemove = props.onRemove,
|
|
27
|
+
onReplace = props.onReplace,
|
|
27
28
|
overflow = props.overflow,
|
|
28
29
|
listClassName = props.listClassName,
|
|
29
30
|
listStyle = props.listStyle,
|
|
@@ -108,6 +109,7 @@ export default function FileList(props) {
|
|
|
108
109
|
prefixCls: prefixCls,
|
|
109
110
|
item: item,
|
|
110
111
|
onRemove: onRemove,
|
|
112
|
+
onReplace: onReplace,
|
|
111
113
|
className: classnames(motionCls, itemClassName),
|
|
112
114
|
style: _objectSpread(_objectSpread({}, motionStyle), itemStyle),
|
|
113
115
|
renderType: props.renderType
|
|
@@ -2,6 +2,7 @@ import { type GetProp, type UploadProps } from 'antd';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { type FileListProps } from './FileList';
|
|
4
4
|
import FileListCard from './FileList/FileListCard';
|
|
5
|
+
import ImageCard from './FileList/ImageCard';
|
|
5
6
|
import { type PlaceholderType } from './PlaceholderUploader';
|
|
6
7
|
export type SemanticType = 'list' | 'item' | 'placeholder';
|
|
7
8
|
export type Attachment = GetProp<UploadProps, 'fileList'>[number] & {
|
|
@@ -78,6 +79,11 @@ export interface AttachmentsProps extends Omit<UploadProps, 'fileList'> {
|
|
|
78
79
|
* @descriptionEn Render type, currently only supports default render mode
|
|
79
80
|
*/
|
|
80
81
|
renderType?: 'default';
|
|
82
|
+
/**
|
|
83
|
+
* @description 图片类型文件是否支持点击刷新按钮直接替换上传
|
|
84
|
+
* @descriptionEn Whether image files support direct replacement upload via the refresh button
|
|
85
|
+
*/
|
|
86
|
+
replaceable?: boolean;
|
|
81
87
|
}
|
|
82
88
|
export interface AttachmentsRef {
|
|
83
89
|
/**
|
|
@@ -93,5 +99,6 @@ export interface AttachmentsRef {
|
|
|
93
99
|
}
|
|
94
100
|
declare const ForwardAttachments: React.ForwardRefExoticComponent<AttachmentsProps & React.RefAttributes<AttachmentsRef>> & {
|
|
95
101
|
FileCard: typeof FileListCard;
|
|
102
|
+
ImageCard: typeof ImageCard;
|
|
96
103
|
};
|
|
97
104
|
export default ForwardAttachments;
|
package/lib/Attachments/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
-
var _excluded = ["prefixCls", "rootClassName", "rootStyle", "className", "style", "items", "children", "getDropContainer", "placeholder", "onChange", "overflow", "disabled", "classNames", "styles"];
|
|
2
|
+
var _excluded = ["prefixCls", "rootClassName", "rootStyle", "className", "style", "items", "children", "getDropContainer", "placeholder", "onChange", "overflow", "disabled", "replaceable", "classNames", "styles"];
|
|
3
3
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
4
4
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
5
5
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
@@ -20,6 +20,7 @@ import { useEvent, useMergedState } from 'rc-util';
|
|
|
20
20
|
import DropArea from "./DropArea";
|
|
21
21
|
import FileList from "./FileList";
|
|
22
22
|
import FileListCard from "./FileList/FileListCard";
|
|
23
|
+
import ImageCard from "./FileList/ImageCard";
|
|
23
24
|
import PlaceholderUploader from "./PlaceholderUploader";
|
|
24
25
|
import SilentUploader from "./SilentUploader";
|
|
25
26
|
import { AttachmentContext } from "./context";
|
|
@@ -40,6 +41,7 @@ function Attachments(props, ref) {
|
|
|
40
41
|
onChange = props.onChange,
|
|
41
42
|
overflow = props.overflow,
|
|
42
43
|
disabled = props.disabled,
|
|
44
|
+
replaceable = props.replaceable,
|
|
43
45
|
_props$classNames = props.classNames,
|
|
44
46
|
classNames = _props$classNames === void 0 ? {} : _props$classNames,
|
|
45
47
|
_props$styles = props.styles,
|
|
@@ -94,6 +96,24 @@ function Attachments(props, ref) {
|
|
|
94
96
|
fileList: newFileList
|
|
95
97
|
});
|
|
96
98
|
};
|
|
99
|
+
var onItemReplace = useEvent(function (oldItem, file) {
|
|
100
|
+
var newAttachment = {
|
|
101
|
+
uid: oldItem.uid,
|
|
102
|
+
name: file.name,
|
|
103
|
+
size: file.size,
|
|
104
|
+
type: file.type,
|
|
105
|
+
originFileObj: file,
|
|
106
|
+
status: 'done',
|
|
107
|
+
percent: 100
|
|
108
|
+
};
|
|
109
|
+
var newFileList = fileList.map(function (fileItem) {
|
|
110
|
+
return fileItem.uid === oldItem.uid ? newAttachment : fileItem;
|
|
111
|
+
});
|
|
112
|
+
triggerChange({
|
|
113
|
+
file: newAttachment,
|
|
114
|
+
fileList: newFileList
|
|
115
|
+
});
|
|
116
|
+
});
|
|
97
117
|
var renderChildren;
|
|
98
118
|
var getPlaceholderNode = function getPlaceholderNode(type, props, ref) {
|
|
99
119
|
var placeholderContent = typeof placeholder === 'function' ? placeholder(type) : placeholder;
|
|
@@ -131,6 +151,7 @@ function Attachments(props, ref) {
|
|
|
131
151
|
prefixCls: prefixCls,
|
|
132
152
|
items: fileList,
|
|
133
153
|
onRemove: onItemRemove,
|
|
154
|
+
onReplace: replaceable ? onItemReplace : undefined,
|
|
134
155
|
overflow: overflow,
|
|
135
156
|
upload: mergedUploadProps,
|
|
136
157
|
listClassName: classnames(classNames.list),
|
|
@@ -158,4 +179,5 @@ function Attachments(props, ref) {
|
|
|
158
179
|
}
|
|
159
180
|
var ForwardAttachments = /*#__PURE__*/React.forwardRef(Attachments);
|
|
160
181
|
ForwardAttachments.FileCard = FileListCard;
|
|
182
|
+
ForwardAttachments.ImageCard = ImageCard;
|
|
161
183
|
export default ForwardAttachments;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var _templateObject;
|
|
2
2
|
function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
|
|
3
3
|
import { createGlobalStyle } from 'antd-style';
|
|
4
|
-
export default createGlobalStyle(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n.", "-attachment-list-card {\n border-radius: ", "px;\n position: relative;\n background: ", ";\n border-width: ", "px;\n border-style: solid;\n border-color: ", ";\n flex: none;\n transition: all 0.3s;\n\n &-name,\n &-desc {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n }\n\n &-ellipsis-prefix {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &-ellipsis-suffix {\n flex: none;\n }\n\n &-type-overview {\n padding: 0 8px;\n display: flex;\n height: 56px;\n gap: ", "px;\n align-items: center;\n width: 140px;\n\n .", "-attachment-list-card-icon {\n display: flex;\n align-items: center;\n }\n\n .", "-attachment-list-card-content {\n flex: auto;\n min-width: 0;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n font-size: ", "px;\n color: ", ";\n }\n\n .", "-attachment-list-card-desc {\n color: ", ";\n font-size: ", "px;\n }\n }\n\n &-type-preview {\n width:
|
|
4
|
+
export default createGlobalStyle(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n.", "-attachment-list-card {\n border-radius: ", "px;\n position: relative;\n background: ", ";\n border-width: ", "px;\n border-style: solid;\n border-color: ", ";\n flex: none;\n transition: all 0.3s;\n\n &-name,\n &-desc {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n }\n\n &-ellipsis-prefix {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &-ellipsis-suffix {\n flex: none;\n }\n\n &-type-overview {\n padding: 0 8px;\n display: flex;\n height: 56px;\n gap: ", "px;\n align-items: center;\n width: 140px;\n\n .", "-attachment-list-card-icon {\n display: flex;\n align-items: center;\n }\n\n .", "-attachment-list-card-content {\n flex: auto;\n min-width: 0;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n font-size: ", "px;\n color: ", ";\n }\n\n .", "-attachment-list-card-desc {\n color: ", ";\n font-size: ", "px;\n }\n }\n\n &-type-preview {\n width: 100px;\n height: 56px;\n line-height: 1;\n\n img {\n width: 100%;\n height: 100%;\n vertical-align: top;\n object-fit: cover;\n border-radius: 6px;\n }\n\n .", "-attachment-list-card-img-mask {\n position: absolute;\n inset: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n background: rgba(0, 0, 0, ", ");\n border-radius: inherit;\n }\n\n .", "-attachment-list-card-img-hover-mask {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n gap: 16px;\n background: rgba(20, 19, 39, 0.45);\n border-radius: 6px;\n opacity: 0;\n transition: opacity 0.2s;\n }\n\n &:hover .", "-attachment-list-card-img-hover-mask {\n opacity: 1;\n }\n\n .", "-attachment-list-card-img-action {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: none;\n background: transparent;\n color: ", ";\n font-size: 20px;\n cursor: pointer;\n line-height: 1;\n transition: opacity 0.2s;\n\n &:hover {\n opacity: 0.8;\n }\n }\n\n &.", "-attachment-list-card-status-error {\n\n img,\n .", "-attachment-list-card-img-mask {\n border-radius: calc(", "px - ", "px);\n }\n\n .", "-attachment-list-card-desc {\n padding-inline: ", "px;\n }\n }\n\n .", "-attachment-list-card-progress {\n }\n }\n\n &-remove {\n position: absolute;\n top: -6px;\n right: -6px;\n width: 16px;\n height: 16px;\n line-height: 1;\n font-size: 10px;\n cursor: pointer;\n display: none;\n color: ", ";\n background-color: ", ";\n border-width: ", "px;\n border-style: solid;\n border-color: ", ";\n border-radius: 50%;\n align-items: center;\n justify-content: center;\n transition: all 0.3s;\n z-index: 1;\n\n &:dir(rtl) {\n transform: translate(-50%, -50%);\n }\n }\n\n &:hover &-remove {\n display: flex;\n \n }\n\n &-status-error {\n border-color: ", ";\n\n .", "-attachment-list-card-desc {\n color: ", ";\n }\n }\n\n &-motion {\n\n &-appear-start {\n width: 0;\n transition: none;\n }\n\n &-leave-active {\n opacity: 0;\n width: 0;\n padding-inline: 0;\n border-inline-width: 0;\n margin-inline-end: calc(-1 * ", "px);\n }\n }\n}\n\n.", "-attachment-list-card-hoverable {\n position: relative;\n\n &:hover {\n border-color: ", ";\n }\n\n &.", "-attachment-list-card-type-overview:hover {\n &::after {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n width: 100%;\n border-radius: 5px;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.45);\n }\n }\n}\n"])), function (p) {
|
|
5
5
|
return p.theme.prefixCls;
|
|
6
6
|
}, function (p) {
|
|
7
7
|
return p.theme.borderRadius;
|
|
@@ -35,6 +35,14 @@ export default createGlobalStyle(_templateObject || (_templateObject = _taggedTe
|
|
|
35
35
|
return p.theme.prefixCls;
|
|
36
36
|
}, function (p) {
|
|
37
37
|
return p.theme.prefixCls;
|
|
38
|
+
}, function (p) {
|
|
39
|
+
return p.theme.prefixCls;
|
|
40
|
+
}, function (p) {
|
|
41
|
+
return p.theme.colorWhite;
|
|
42
|
+
}, function (p) {
|
|
43
|
+
return p.theme.prefixCls;
|
|
44
|
+
}, function (p) {
|
|
45
|
+
return p.theme.prefixCls;
|
|
38
46
|
}, function (p) {
|
|
39
47
|
return p.theme.borderRadius;
|
|
40
48
|
}, function (p) {
|
|
@@ -65,4 +73,6 @@ export default createGlobalStyle(_templateObject || (_templateObject = _taggedTe
|
|
|
65
73
|
return p.theme.prefixCls;
|
|
66
74
|
}, function (p) {
|
|
67
75
|
return p.theme.colorPrimary;
|
|
76
|
+
}, function (p) {
|
|
77
|
+
return p.theme.prefixCls;
|
|
68
78
|
});
|
|
@@ -192,6 +192,7 @@ export default /*#__PURE__*/forwardRef(function (_, ref) {
|
|
|
192
192
|
if (!files.length) return null;
|
|
193
193
|
return /*#__PURE__*/_jsx(Attachments, {
|
|
194
194
|
items: files,
|
|
195
|
+
replaceable: true,
|
|
195
196
|
onChange: function onChange(info) {
|
|
196
197
|
return handleFileChange(index, info.fileList);
|
|
197
198
|
}
|
package/package.json
CHANGED