@douyinfe/semi-ui 2.1.6-alpha.0 → 2.2.1
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/_base/_story/a11y.jsx +1302 -0
- package/_base/_story/a11y.scss +49 -0
- package/_base/_story/index.stories.js +3 -1
- package/_utils/index.ts +9 -4
- package/cascader/__test__/cascader.test.js +221 -0
- package/cascader/_story/cascader.stories.js +138 -0
- package/cascader/index.tsx +37 -21
- package/cascader/item.tsx +4 -2
- package/datePicker/__test__/datePicker.test.js +100 -2
- package/datePicker/_story/datePicker.stories.js +90 -1
- package/datePicker/_story/v2/YearButton.jsx +17 -0
- package/datePicker/_story/v2/index.js +1 -0
- package/datePicker/datePicker.tsx +3 -0
- package/datePicker/monthsGrid.tsx +12 -1
- package/datePicker/navigation.tsx +55 -29
- package/descriptions/__test__/descriptions.test.js +27 -1
- package/descriptions/_story/descriptions.stories.js +52 -2
- package/descriptions/item.tsx +1 -1
- package/dist/css/semi.css +72 -28
- package/dist/css/semi.min.css +1 -1
- package/dist/umd/semi-ui.js +1086 -468
- package/dist/umd/semi-ui.js.map +1 -1
- package/dist/umd/semi-ui.min.js +1 -1
- package/dist/umd/semi-ui.min.js.map +1 -1
- package/empty/index.tsx +2 -2
- package/gulpfile.js +2 -1
- package/lib/cjs/_utils/index.d.ts +1 -0
- package/lib/cjs/_utils/index.js +12 -5
- package/lib/cjs/cascader/index.d.ts +7 -0
- package/lib/cjs/cascader/index.js +35 -22
- package/lib/cjs/cascader/item.d.ts +2 -0
- package/lib/cjs/cascader/item.js +4 -2
- package/lib/cjs/datePicker/datePicker.js +4 -0
- package/lib/cjs/datePicker/monthsGrid.d.ts +1 -0
- package/lib/cjs/datePicker/monthsGrid.js +6 -0
- package/lib/cjs/datePicker/navigation.js +47 -7
- package/lib/cjs/descriptions/item.js +1 -1
- package/lib/cjs/empty/index.d.ts +2 -2
- package/lib/cjs/empty/index.js +19 -18
- package/lib/cjs/form/baseForm.d.ts +6 -1
- package/lib/cjs/form/field.d.ts +6 -1
- package/lib/cjs/locale/source/es.d.ts +7 -0
- package/lib/cjs/locale/source/es.js +168 -0
- package/lib/cjs/rating/item.js +1 -1
- package/lib/cjs/select/index.d.ts +9 -0
- package/lib/cjs/select/index.js +10 -8
- package/lib/cjs/tabs/index.js +3 -7
- package/lib/cjs/timeline/item.d.ts +3 -0
- package/lib/cjs/timeline/item.js +10 -4
- package/lib/cjs/typography/title.d.ts +1 -1
- package/lib/cjs/upload/fileCard.d.ts +2 -0
- package/lib/cjs/upload/fileCard.js +70 -45
- package/lib/cjs/upload/index.d.ts +23 -2
- package/lib/cjs/upload/index.js +133 -25
- package/lib/cjs/upload/interface.d.ts +3 -0
- package/lib/es/_utils/index.d.ts +1 -0
- package/lib/es/_utils/index.js +12 -5
- package/lib/es/cascader/index.d.ts +7 -0
- package/lib/es/cascader/index.js +34 -25
- package/lib/es/cascader/item.d.ts +2 -0
- package/lib/es/cascader/item.js +4 -2
- package/lib/es/datePicker/datePicker.js +4 -0
- package/lib/es/datePicker/monthsGrid.d.ts +1 -0
- package/lib/es/datePicker/monthsGrid.js +6 -0
- package/lib/es/datePicker/navigation.js +48 -8
- package/lib/es/descriptions/item.js +1 -1
- package/lib/es/empty/index.d.ts +2 -2
- package/lib/es/empty/index.js +19 -18
- package/lib/es/form/baseForm.d.ts +6 -1
- package/lib/es/form/field.d.ts +6 -1
- package/lib/es/locale/source/es.d.ts +7 -0
- package/lib/es/locale/source/es.js +157 -0
- package/lib/es/rating/item.js +1 -1
- package/lib/es/select/index.d.ts +9 -0
- package/lib/es/select/index.js +14 -8
- package/lib/es/tabs/index.js +1 -5
- package/lib/es/timeline/item.d.ts +3 -0
- package/lib/es/timeline/item.js +9 -4
- package/lib/es/typography/title.d.ts +1 -1
- package/lib/es/upload/fileCard.d.ts +2 -0
- package/lib/es/upload/fileCard.js +69 -44
- package/lib/es/upload/index.d.ts +23 -2
- package/lib/es/upload/index.js +133 -24
- package/lib/es/upload/interface.d.ts +3 -0
- package/locale/source/es.ts +160 -0
- package/package.json +9 -9
- package/popover/Arrow.tsx +1 -1
- package/rating/item.tsx +1 -1
- package/select/_story/select.stories.js +25 -0
- package/select/index.tsx +17 -6
- package/tabs/index.tsx +1 -1
- package/timeline/_story/timeline.stories.js +50 -0
- package/timeline/item.tsx +7 -2
- package/upload/__test__/upload.test.js +50 -1
- package/upload/fileCard.tsx +110 -95
- package/upload/index.tsx +147 -53
- package/upload/interface.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@douyinfe/semi-ui",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"module": "lib/es/index.js",
|
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@babel/runtime-corejs3": "^7.15.4",
|
|
17
|
-
"@douyinfe/semi-animation-react": "2.1
|
|
18
|
-
"@douyinfe/semi-foundation": "2.1
|
|
19
|
-
"@douyinfe/semi-icons": "2.1
|
|
20
|
-
"@douyinfe/semi-illustrations": "2.1
|
|
21
|
-
"@douyinfe/semi-theme-default": "2.1
|
|
17
|
+
"@douyinfe/semi-animation-react": "2.2.1",
|
|
18
|
+
"@douyinfe/semi-foundation": "2.2.1",
|
|
19
|
+
"@douyinfe/semi-icons": "2.2.1",
|
|
20
|
+
"@douyinfe/semi-illustrations": "2.2.1",
|
|
21
|
+
"@douyinfe/semi-theme-default": "2.2.1",
|
|
22
22
|
"@types/react-window": "^1.8.2",
|
|
23
23
|
"async-validator": "^3.5.0",
|
|
24
24
|
"classnames": "^2.2.6",
|
|
@@ -68,13 +68,13 @@
|
|
|
68
68
|
],
|
|
69
69
|
"author": "",
|
|
70
70
|
"license": "MIT",
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "6d8bb9dd22bf199225fa2a0dda4f8524a4b97f6f",
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@babel/plugin-proposal-decorators": "^7.15.8",
|
|
74
74
|
"@babel/plugin-transform-runtime": "^7.15.8",
|
|
75
75
|
"@babel/preset-env": "^7.15.8",
|
|
76
76
|
"@babel/preset-react": "^7.14.5",
|
|
77
|
-
"@douyinfe/semi-scss-compile": "2.1
|
|
77
|
+
"@douyinfe/semi-scss-compile": "2.2.1",
|
|
78
78
|
"@storybook/addon-knobs": "^6.3.1",
|
|
79
79
|
"@types/lodash": "^4.14.176",
|
|
80
80
|
"babel-loader": "^8.2.2",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"react-storybook-addon-props-combinations": "^1.1.0",
|
|
100
100
|
"react-virtualized": "^9.22.3",
|
|
101
101
|
"rimraf": "^3.0.2",
|
|
102
|
-
"sass": "1.
|
|
102
|
+
"sass": "1.45.0",
|
|
103
103
|
"sinon": "^6.3.5",
|
|
104
104
|
"terser-webpack-plugin": "^4.2.3",
|
|
105
105
|
"through2": "^4.0.2",
|
package/popover/Arrow.tsx
CHANGED
package/rating/item.tsx
CHANGED
|
@@ -88,7 +88,7 @@ export default class Item extends PureComponent<RatingItemProps> {
|
|
|
88
88
|
height: size,
|
|
89
89
|
fontSize: size
|
|
90
90
|
} : {};
|
|
91
|
-
const iconSize = size === 'small' ? 'default' : 'extra-large';
|
|
91
|
+
const iconSize = isCustomSize ? 'inherit' : (size === 'small' ? 'default' : 'extra-large');
|
|
92
92
|
const content = character ? character : <IconStar size={iconSize} />;
|
|
93
93
|
return (
|
|
94
94
|
<li className={starCls} style={{ ...sizeStyle }}>
|
|
@@ -2850,3 +2850,28 @@ export const ScrollIntoView = () => (
|
|
|
2850
2850
|
ScrollIntoView.story = {
|
|
2851
2851
|
name: 'scroll into view',
|
|
2852
2852
|
};
|
|
2853
|
+
|
|
2854
|
+
|
|
2855
|
+
export const SelectInputPropsDemo = () => {
|
|
2856
|
+
|
|
2857
|
+
const inputProps = {
|
|
2858
|
+
className: 'ttt',
|
|
2859
|
+
onCompositionEnd: (v) => console.log(v.target.value)
|
|
2860
|
+
};
|
|
2861
|
+
|
|
2862
|
+
return (
|
|
2863
|
+
<Select
|
|
2864
|
+
// onSearch={(v) => console.log(v)}
|
|
2865
|
+
optionList={list}
|
|
2866
|
+
inputProps={inputProps}
|
|
2867
|
+
multiple
|
|
2868
|
+
filter
|
|
2869
|
+
style={{ width: 200 }}
|
|
2870
|
+
>
|
|
2871
|
+
</Select>
|
|
2872
|
+
)
|
|
2873
|
+
};
|
|
2874
|
+
SelectInputPropsDemo.story = {
|
|
2875
|
+
name: 'inputProps',
|
|
2876
|
+
};
|
|
2877
|
+
|
package/select/index.tsx
CHANGED
|
@@ -18,13 +18,14 @@ import { FixedSizeList as List } from 'react-window';
|
|
|
18
18
|
import { getOptionsFromGroup } from './utils';
|
|
19
19
|
import VirtualRow from './virtualRow';
|
|
20
20
|
|
|
21
|
-
import Input from '../input/index';
|
|
21
|
+
import Input, { InputProps } from '../input/index';
|
|
22
22
|
import Option, { OptionProps } from './option';
|
|
23
23
|
import OptionGroup from './optionGroup';
|
|
24
24
|
import Spin from '../spin';
|
|
25
25
|
import Trigger from '../trigger';
|
|
26
26
|
import { IconChevronDown, IconClear } from '@douyinfe/semi-icons';
|
|
27
27
|
import { isSemiIcon } from '../_utils';
|
|
28
|
+
import { Subtract } from 'utility-types';
|
|
28
29
|
|
|
29
30
|
import warning from '@douyinfe/semi-foundation/utils/warning';
|
|
30
31
|
|
|
@@ -40,6 +41,12 @@ const prefixcls = cssClasses.PREFIX;
|
|
|
40
41
|
|
|
41
42
|
const key = 0;
|
|
42
43
|
|
|
44
|
+
type ExcludeInputType = {
|
|
45
|
+
value?: InputProps['value'];
|
|
46
|
+
onFocus?: InputProps['onFocus'];
|
|
47
|
+
onChange?: InputProps['onChange'];
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
type OnChangeValueType = string | number | Record<string, any>;
|
|
44
51
|
export interface optionRenderProps {
|
|
45
52
|
key?: any;
|
|
@@ -115,6 +122,7 @@ export type SelectProps = {
|
|
|
115
122
|
suffix?: React.ReactNode;
|
|
116
123
|
prefix?: React.ReactNode;
|
|
117
124
|
insetLabel?: React.ReactNode;
|
|
125
|
+
inputProps?: Subtract<InputProps, ExcludeInputType>;
|
|
118
126
|
showClear?: boolean;
|
|
119
127
|
showArrow?: boolean;
|
|
120
128
|
renderSelectedItem?: RenderSelectedItemFn;
|
|
@@ -200,6 +208,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
|
|
|
200
208
|
dropdownStyle: PropTypes.object,
|
|
201
209
|
outerTopSlot: PropTypes.node,
|
|
202
210
|
innerTopSlot: PropTypes.node,
|
|
211
|
+
inputProps: PropTypes.object,
|
|
203
212
|
outerBottomSlot: PropTypes.node,
|
|
204
213
|
innerBottomSlot: PropTypes.node, // Options slot
|
|
205
214
|
optionList: PropTypes.array,
|
|
@@ -558,18 +567,20 @@ class Select extends BaseComponent<SelectProps, SelectState> {
|
|
|
558
567
|
handleInputChange = (value: string) => this.foundation.handleInputChange(value);
|
|
559
568
|
|
|
560
569
|
renderInput() {
|
|
561
|
-
const { size, multiple, disabled } = this.props;
|
|
570
|
+
const { size, multiple, disabled, inputProps } = this.props;
|
|
571
|
+
const inputPropsCls = get(inputProps, 'className');
|
|
562
572
|
const inputcls = cls(`${prefixcls}-input`, {
|
|
563
573
|
[`${prefixcls}-input-single`]: !multiple,
|
|
564
574
|
[`${prefixcls}-input-multiple`]: multiple,
|
|
565
|
-
});
|
|
575
|
+
}, inputPropsCls);
|
|
566
576
|
const { inputValue } = this.state;
|
|
567
577
|
|
|
568
|
-
const
|
|
578
|
+
const selectInputProps: Record<string, any> = {
|
|
569
579
|
value: inputValue,
|
|
570
580
|
disabled,
|
|
571
581
|
className: inputcls,
|
|
572
582
|
onChange: this.handleInputChange,
|
|
583
|
+
...inputProps,
|
|
573
584
|
};
|
|
574
585
|
|
|
575
586
|
let style = {};
|
|
@@ -578,18 +589,18 @@ class Select extends BaseComponent<SelectProps, SelectState> {
|
|
|
578
589
|
style = {
|
|
579
590
|
width: inputValue ? `${inputValue.length * 16}px` : '2px',
|
|
580
591
|
};
|
|
581
|
-
|
|
592
|
+
selectInputProps.style = style;
|
|
582
593
|
}
|
|
583
594
|
return (
|
|
584
595
|
<Input
|
|
585
596
|
ref={this.inputRef as any}
|
|
586
597
|
size={size}
|
|
587
|
-
{...inputProps}
|
|
588
598
|
onFocus={(e: React.FocusEvent<HTMLInputElement>) => {
|
|
589
599
|
// prevent event bubbling which will fire trigger onFocus event
|
|
590
600
|
e.stopPropagation();
|
|
591
601
|
// e.nativeEvent.stopImmediatePropagation();
|
|
592
602
|
}}
|
|
603
|
+
{...selectInputProps}
|
|
593
604
|
/>
|
|
594
605
|
);
|
|
595
606
|
}
|
package/tabs/index.tsx
CHANGED
|
@@ -15,7 +15,7 @@ import TabPane from './TabPane';
|
|
|
15
15
|
import TabsContext from './tabs-context';
|
|
16
16
|
import { TabsProps, PlainTab, TabBarProps } from './interface';
|
|
17
17
|
|
|
18
|
-
const panePickKeys =
|
|
18
|
+
const panePickKeys = ['className', 'style', 'disabled', 'itemKey', 'tab', 'icon'];
|
|
19
19
|
|
|
20
20
|
export * from './interface';
|
|
21
21
|
|
|
@@ -183,3 +183,53 @@ export const DataSource = () => (
|
|
|
183
183
|
DataSource.story = {
|
|
184
184
|
name: 'dataSource',
|
|
185
185
|
};
|
|
186
|
+
|
|
187
|
+
const dataWithOnClick = [
|
|
188
|
+
{
|
|
189
|
+
time: '2019-07-14 10:35',
|
|
190
|
+
extra: '节点辅助说明信息',
|
|
191
|
+
content: '创建服务现场',
|
|
192
|
+
type: 'ongoing',
|
|
193
|
+
onClick: e => console.log(e, '创建服务现场'),
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
time: '2019-06-13 16:17',
|
|
197
|
+
extra: '节点辅助说明信息',
|
|
198
|
+
content: <span style={{ fontSize: '18px' }}>初步排除网络异常</span>,
|
|
199
|
+
color: 'pink',
|
|
200
|
+
onClick: e => console.log(e, '初步排除网络异常'),
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
time: '2019-05-14 18:34',
|
|
204
|
+
extra: '节点辅助说明信息',
|
|
205
|
+
dot: <Icon type="alert_triangle" />,
|
|
206
|
+
content: '技术测试异常',
|
|
207
|
+
type: 'warning',
|
|
208
|
+
onClick: e => console.log(e, '技术测试异常'),
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
time: '2019-05-09 09:12',
|
|
212
|
+
extra: '节点辅助说明信息',
|
|
213
|
+
content: '网络异常正在修复',
|
|
214
|
+
type: 'success',
|
|
215
|
+
onClick: e => console.log(e, '网络异常正在修复'),
|
|
216
|
+
}
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
export const OnClickDemo = () => (
|
|
220
|
+
<div style={{ width: '400px' }}>
|
|
221
|
+
<div style={{ width: '300px' }}>
|
|
222
|
+
<Timeline mode='center'>
|
|
223
|
+
<Timeline.Item time='2015-09-01' onClick={e=>console.log(e, '创建服务现场')}>创建服务现场</Timeline.Item>
|
|
224
|
+
<Timeline.Item time='2015-09-01' onClick={e=>console.log(e, '初步排除网络异常')}>初步排除网络异常</Timeline.Item>
|
|
225
|
+
<Timeline.Item time='2015-09-01' onClick={e=>console.log(e, '技术测试异常')}>技术测试异常</Timeline.Item>
|
|
226
|
+
<Timeline.Item time='2015-09-01' onClick={e=>console.log(e, '网络异常正在修复')}>网络异常正在修复</Timeline.Item>
|
|
227
|
+
</Timeline>
|
|
228
|
+
<Timeline mode='alternate' dataSource={dataWithOnClick} />
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
OnClickDemo.story = {
|
|
234
|
+
name: 'onClick',
|
|
235
|
+
};
|
package/timeline/item.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { PureComponent } from 'react';
|
|
2
2
|
import cls from 'classnames';
|
|
3
|
+
import { noop } from 'lodash';
|
|
3
4
|
import PropTypes from 'prop-types';
|
|
4
5
|
import { cssClasses, strings } from '@douyinfe/semi-foundation/timeline/constants';
|
|
5
6
|
import '@douyinfe/semi-foundation/timeline/timeline.scss';
|
|
@@ -13,6 +14,7 @@ export interface TimelineItemProps {
|
|
|
13
14
|
position?: 'left' | 'right';
|
|
14
15
|
className?: string;
|
|
15
16
|
style?: React.CSSProperties;
|
|
17
|
+
onClick?: React.MouseEventHandler<HTMLLIElement>;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
const prefixCls = cssClasses.ITEM;
|
|
@@ -27,11 +29,13 @@ export default class Item extends PureComponent<TimelineItemProps> {
|
|
|
27
29
|
position: PropTypes.oneOf(strings.ITEM_POS),
|
|
28
30
|
className: PropTypes.string,
|
|
29
31
|
style: PropTypes.object,
|
|
32
|
+
onClick: PropTypes.func,
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
static defaultProps = {
|
|
33
36
|
type: 'default',
|
|
34
37
|
time: '',
|
|
38
|
+
onClick: noop,
|
|
35
39
|
};
|
|
36
40
|
|
|
37
41
|
render() {
|
|
@@ -43,7 +47,8 @@ export default class Item extends PureComponent<TimelineItemProps> {
|
|
|
43
47
|
type,
|
|
44
48
|
style,
|
|
45
49
|
time,
|
|
46
|
-
extra
|
|
50
|
+
extra,
|
|
51
|
+
onClick,
|
|
47
52
|
} = this.props;
|
|
48
53
|
|
|
49
54
|
const itemCls = cls(prefixCls,
|
|
@@ -57,7 +62,7 @@ export default class Item extends PureComponent<TimelineItemProps> {
|
|
|
57
62
|
});
|
|
58
63
|
const dotStyle = color ? { style: { backgroundColor: color } } : null;
|
|
59
64
|
return (
|
|
60
|
-
<li className={itemCls} style={style}>
|
|
65
|
+
<li className={itemCls} style={style} onClick={onClick}>
|
|
61
66
|
<div className={`${prefixCls}-tail`} />
|
|
62
67
|
<div
|
|
63
68
|
className={dotCls}
|
|
@@ -869,7 +869,7 @@ describe('Upload', () => {
|
|
|
869
869
|
beforeClear: () => Promise.reject(),
|
|
870
870
|
onChange: spyOnChangeReject,
|
|
871
871
|
onClear: spyOnClearReject,
|
|
872
|
-
})
|
|
872
|
+
});
|
|
873
873
|
|
|
874
874
|
const clearBtn = upload.find(`.${BASE_CLASS_PREFIX}-upload-file-list-title-clear`).at(0);
|
|
875
875
|
const clearBtnPass = uploadPass.find(`.${BASE_CLASS_PREFIX}-upload-file-list-title-clear`).at(0);
|
|
@@ -893,4 +893,53 @@ describe('Upload', () => {
|
|
|
893
893
|
expect(spyOnClearReject.callCount).toEqual(0);
|
|
894
894
|
});
|
|
895
895
|
});
|
|
896
|
+
|
|
897
|
+
it('insert method', () => {
|
|
898
|
+
const props = {
|
|
899
|
+
defaultFileList: [],
|
|
900
|
+
};
|
|
901
|
+
const upload = getUpload(props);
|
|
902
|
+
const uploadInstance = upload.instance();
|
|
903
|
+
|
|
904
|
+
const file_0 = new File([new ArrayBuffer(1024)], 'chucknorris_0.png', { type: 'image/png' });
|
|
905
|
+
const file_1 = new File([new ArrayBuffer(1024)], 'chucknorris_1.png', { type: 'image/png' });
|
|
906
|
+
const file_2 = new File([new ArrayBuffer(1024)], 'chucknorris_2.png', { type: 'image/png' });
|
|
907
|
+
|
|
908
|
+
expect(uploadInstance instanceof Upload).toEqual(true);
|
|
909
|
+
expect(Object.prototype.hasOwnProperty.call(uploadInstance, 'insert')).toEqual(true);
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* test fileList state should be [] => [file_0] => [file_1, file_0] => [file_1, file_2, file_0]
|
|
913
|
+
*/
|
|
914
|
+
upload.instance().insert([file_0]);
|
|
915
|
+
upload.instance().insert([file_1], 0);
|
|
916
|
+
upload.instance().insert([file_2], 1);
|
|
917
|
+
|
|
918
|
+
expect(Array.isArray(upload.state('fileList'))).toEqual(true);
|
|
919
|
+
expect(upload.state('fileList').length).toEqual(3);
|
|
920
|
+
expect(upload.state('fileList')[0].name).toEqual('chucknorris_1.png');
|
|
921
|
+
expect(upload.state('fileList')[1].name).toEqual('chucknorris_2.png');
|
|
922
|
+
expect(upload.state('fileList')[2].name).toEqual('chucknorris_0.png');
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
it('showPicInfo works', () => {
|
|
926
|
+
const props = {
|
|
927
|
+
listType: 'picture',
|
|
928
|
+
defaultFileList: [
|
|
929
|
+
{
|
|
930
|
+
uid: '1',
|
|
931
|
+
name: 'jiafang1.jpeg',
|
|
932
|
+
status: 'success',
|
|
933
|
+
size: '130kb',
|
|
934
|
+
url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg',
|
|
935
|
+
},
|
|
936
|
+
],
|
|
937
|
+
showPicInfo: true,
|
|
938
|
+
};
|
|
939
|
+
const upload = getUpload(props);
|
|
940
|
+
|
|
941
|
+
expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload`)).toEqual(true);
|
|
942
|
+
expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload-file-list-main`)).toEqual(true);
|
|
943
|
+
expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload-picture-file-card-pic-info`)).toEqual(true);
|
|
944
|
+
});
|
|
896
945
|
});
|
package/upload/fileCard.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import cls from 'classnames';
|
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import { cssClasses, strings } from '@douyinfe/semi-foundation/upload/constants';
|
|
5
5
|
import { getFileSize } from '@douyinfe/semi-foundation/upload/utils';
|
|
6
|
-
import {
|
|
6
|
+
import { IconAlertCircle, IconClose, IconFile, IconRefresh } from '@douyinfe/semi-icons';
|
|
7
7
|
import LocaleConsumer from '../locale/localeConsumer';
|
|
8
8
|
import { Locale } from '../locale/interface';
|
|
9
9
|
|
|
@@ -13,7 +13,6 @@ import Tooltip from '../tooltip/index';
|
|
|
13
13
|
import Spin from '../spin/index';
|
|
14
14
|
import { isElement } from '../_base/reactUtils';
|
|
15
15
|
import { RenderFileItemProps } from './interface';
|
|
16
|
-
import { IconAlertCircle, IconClose, IconFile, IconRefresh } from '@douyinfe/semi-icons';
|
|
17
16
|
|
|
18
17
|
const prefixCls = cssClasses.PREFIX;
|
|
19
18
|
|
|
@@ -69,6 +68,7 @@ class FileCard extends PureComponent<FileCardProps> {
|
|
|
69
68
|
style: PropTypes.object,
|
|
70
69
|
url: PropTypes.string,
|
|
71
70
|
validateMessage: PropTypes.node,
|
|
71
|
+
index: PropTypes.number
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
static defaultProps = {
|
|
@@ -92,10 +92,10 @@ class FileCard extends PureComponent<FileCardProps> {
|
|
|
92
92
|
let content = null;
|
|
93
93
|
switch (true) {
|
|
94
94
|
case typeof validateMessage === 'string' && status === strings.FILE_STATUS_VALIDATING:
|
|
95
|
-
content = (<><Spin size="small" wrapperClassName={`${prefixCls
|
|
95
|
+
content = (<><Spin size="small" wrapperClassName={`${prefixCls}-file-card-icon-loading`} />{validateMessage}</>);
|
|
96
96
|
break;
|
|
97
97
|
case typeof validateMessage === 'string':
|
|
98
|
-
content = (<><IconAlertCircle className={`${prefixCls
|
|
98
|
+
content = (<><IconAlertCircle className={`${prefixCls}-file-card-icon-error`} />{validateMessage}</>);
|
|
99
99
|
break;
|
|
100
100
|
case isElement(validateMessage):
|
|
101
101
|
content = validateMessage;
|
|
@@ -111,10 +111,10 @@ class FileCard extends PureComponent<FileCardProps> {
|
|
|
111
111
|
let icon = null;
|
|
112
112
|
switch (true) {
|
|
113
113
|
case validateMessage && status === strings.FILE_STATUS_VALIDATING:
|
|
114
|
-
icon = (<Spin size="small" wrapperClassName={`${prefixCls
|
|
114
|
+
icon = (<Spin size="small" wrapperClassName={`${prefixCls}-picture-file-card-icon-loading`} />);
|
|
115
115
|
break;
|
|
116
116
|
case validateMessage && (status === strings.FILE_STATUS_VALID_FAIL || status === strings.FILE_STATUS_UPLOAD_FAIL):
|
|
117
|
-
icon = (<div className={`${prefixCls
|
|
117
|
+
icon = (<div className={`${prefixCls}-picture-file-card-icon-error`}><ErrorSvg /></div>);
|
|
118
118
|
break;
|
|
119
119
|
default:
|
|
120
120
|
break;
|
|
@@ -123,41 +123,50 @@ class FileCard extends PureComponent<FileCardProps> {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
renderPic(locale: Locale['Upload']): ReactNode {
|
|
126
|
-
const { url, percent, status, disabled, style, onPreviewClick } = this.props;
|
|
127
|
-
const filePicCardCls = cls({
|
|
128
|
-
[`${prefixCls }-picture-file-card`]: true,
|
|
129
|
-
[`${prefixCls }-picture-file-card-disabled`]: disabled,
|
|
130
|
-
[`${prefixCls }-picture-file-card-show-pointer`]: typeof onPreviewClick !== 'undefined',
|
|
131
|
-
});
|
|
126
|
+
const { url, percent, status, disabled, style, onPreviewClick, showPicInfo, renderPicInfo, renderThumbnail, name, index } = this.props;
|
|
132
127
|
const showProgress = status === strings.FILE_STATUS_UPLOADING && percent !== 100;
|
|
133
128
|
const showRetry = status === strings.FILE_STATUS_UPLOAD_FAIL && this.props.showRetry;
|
|
134
129
|
const showReplace = status === strings.FILE_STATUS_SUCCESS && this.props.showReplace;
|
|
135
|
-
const
|
|
130
|
+
const filePicCardCls = cls({
|
|
131
|
+
[`${prefixCls}-picture-file-card`]: true,
|
|
132
|
+
[`${prefixCls}-picture-file-card-disabled`]: disabled,
|
|
133
|
+
[`${prefixCls}-picture-file-card-show-pointer`]: typeof onPreviewClick !== 'undefined',
|
|
134
|
+
[`${prefixCls}-picture-file-card-error`]: status === strings.FILE_STATUS_UPLOAD_FAIL,
|
|
135
|
+
[`${prefixCls}-picture-file-card-uploading`]: showProgress
|
|
136
|
+
});
|
|
137
|
+
const closeCls = `${prefixCls}-picture-file-card-close`;
|
|
136
138
|
const retry = (
|
|
137
139
|
<div
|
|
138
|
-
className={`${prefixCls
|
|
139
|
-
<IconRefresh className={`${prefixCls
|
|
140
|
+
className={`${prefixCls}-picture-file-card-retry`} onClick={e => this.onRetry(e)}>
|
|
141
|
+
<IconRefresh className={`${prefixCls}-picture-file-card-icon-retry`} />
|
|
140
142
|
</div>
|
|
141
143
|
);
|
|
142
144
|
const replace = (
|
|
143
145
|
<Tooltip trigger="hover" position="top" content={locale.replace} showArrow={false} spacing={4}>
|
|
144
146
|
<div
|
|
145
|
-
className={`${prefixCls
|
|
146
|
-
<ReplaceSvg className={`${prefixCls
|
|
147
|
+
className={`${prefixCls}-picture-file-card-replace`} onClick={(e): void => this.onReplace(e)}>
|
|
148
|
+
<ReplaceSvg className={`${prefixCls}-picture-file-card-icon-replace`} />
|
|
147
149
|
</div>
|
|
148
150
|
</Tooltip>
|
|
149
151
|
|
|
150
152
|
);
|
|
151
153
|
|
|
154
|
+
const picInfo = typeof renderPicInfo === 'function' ? renderPicInfo(this.props) : (
|
|
155
|
+
<div className={`${prefixCls }-picture-file-card-pic-info`}>{index + 1}</div>
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const thumbnail = typeof renderThumbnail === 'function' ? renderThumbnail(this.props) : <img src={url} alt={`picture of ${name}`} />;
|
|
159
|
+
|
|
152
160
|
return (
|
|
153
161
|
<div className={filePicCardCls} style={style} onClick={onPreviewClick}>
|
|
154
|
-
|
|
162
|
+
{thumbnail}
|
|
155
163
|
{showProgress ? <Progress percent={percent} type="circle" size="small" orbitStroke={'#FFF'} /> : null}
|
|
156
164
|
{showRetry ? retry : null}
|
|
157
165
|
{showReplace && replace}
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
{showPicInfo && picInfo}
|
|
167
|
+
{!disabled && (
|
|
168
|
+
<div className={closeCls}>
|
|
169
|
+
<IconClose size="extra-small" onClick={e => this.onRemove(e)} />
|
|
161
170
|
</div>
|
|
162
171
|
)}
|
|
163
172
|
{this.renderPicValidateMsg()}
|
|
@@ -165,6 +174,77 @@ class FileCard extends PureComponent<FileCardProps> {
|
|
|
165
174
|
);
|
|
166
175
|
}
|
|
167
176
|
|
|
177
|
+
renderFile(locale: Locale["Upload"]) {
|
|
178
|
+
const { name, size, percent, url, showRetry: propsShowRetry, showReplace: propsShowReplace, preview, previewFile, status, style, onPreviewClick } = this.props;
|
|
179
|
+
const fileCardCls = cls({
|
|
180
|
+
[`${prefixCls}-file-card`]: true,
|
|
181
|
+
[`${prefixCls}-file-card-fail`]: status === strings.FILE_STATUS_VALID_FAIL || status === strings.FILE_STATUS_UPLOAD_FAIL,
|
|
182
|
+
[`${prefixCls}-file-card-show-pointer`]: typeof onPreviewClick !== 'undefined',
|
|
183
|
+
});
|
|
184
|
+
const previewCls = cls({
|
|
185
|
+
[`${prefixCls}-file-card-preview`]: true,
|
|
186
|
+
[`${prefixCls}-file-card-preview-placeholder`]: !preview || previewFile
|
|
187
|
+
});
|
|
188
|
+
const infoCls = `${prefixCls}-file-card-info`;
|
|
189
|
+
const closeCls = `${prefixCls}-file-card-close`;
|
|
190
|
+
const replaceCls = `${prefixCls}-file-card-replace`;
|
|
191
|
+
const showProgress = !(percent === 100 || typeof percent === 'undefined') && status === strings.FILE_STATUS_UPLOADING;
|
|
192
|
+
// only show retry when upload fail & showRetry is true, no need to show during validate fail
|
|
193
|
+
const showRetry = status === strings.FILE_STATUS_UPLOAD_FAIL && propsShowRetry;
|
|
194
|
+
const showReplace = status === strings.FILE_STATUS_SUCCESS && propsShowReplace;
|
|
195
|
+
const fileSize = this.transSize(size);
|
|
196
|
+
let previewContent: ReactNode = preview ? (<img src={url} />) : (<IconFile size="large" />);
|
|
197
|
+
if (previewFile) {
|
|
198
|
+
previewContent = previewFile(this.props);
|
|
199
|
+
}
|
|
200
|
+
return (
|
|
201
|
+
<div className={fileCardCls} style={style} onClick={onPreviewClick}>
|
|
202
|
+
<div className={previewCls}>
|
|
203
|
+
{previewContent}
|
|
204
|
+
</div>
|
|
205
|
+
<div className={`${infoCls}-main`}>
|
|
206
|
+
<div className={`${infoCls}-main-text`}>
|
|
207
|
+
<span className={`${infoCls}-name`}>
|
|
208
|
+
{name}
|
|
209
|
+
</span>
|
|
210
|
+
<span>
|
|
211
|
+
<span className={`${infoCls}-size`}>{fileSize}</span>
|
|
212
|
+
{showReplace && (
|
|
213
|
+
<Tooltip trigger="hover" position="top" showArrow={false} content={locale.replace}>
|
|
214
|
+
<IconButton
|
|
215
|
+
onClick={e => this.onReplace(e)}
|
|
216
|
+
type="tertiary"
|
|
217
|
+
theme="borderless"
|
|
218
|
+
size="small"
|
|
219
|
+
icon={<DirectorySvg />}
|
|
220
|
+
className={replaceCls}
|
|
221
|
+
/>
|
|
222
|
+
</Tooltip>
|
|
223
|
+
)}
|
|
224
|
+
|
|
225
|
+
</span>
|
|
226
|
+
|
|
227
|
+
</div>
|
|
228
|
+
{showProgress ? (<Progress percent={percent} style={{ width: '100%' }} />) : null}
|
|
229
|
+
<div className={`${infoCls}-main-control`}>
|
|
230
|
+
<span className={`${infoCls}-validate-message`}>
|
|
231
|
+
{this.renderValidateMessage()}
|
|
232
|
+
</span>
|
|
233
|
+
{showRetry ? <span className={`${infoCls}-retry`} onClick={e => this.onRetry(e)}>{locale.retry}</span> : null}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
<IconButton
|
|
237
|
+
onClick={e => this.onRemove(e)}
|
|
238
|
+
type="tertiary"
|
|
239
|
+
icon={<IconClose />}
|
|
240
|
+
theme="borderless"
|
|
241
|
+
size="small"
|
|
242
|
+
className={closeCls}
|
|
243
|
+
/>
|
|
244
|
+
</div>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
168
248
|
onRemove(e: MouseEvent): void {
|
|
169
249
|
e.stopPropagation();
|
|
170
250
|
this.props.onRemove(this.props, e);
|
|
@@ -181,89 +261,24 @@ class FileCard extends PureComponent<FileCardProps> {
|
|
|
181
261
|
}
|
|
182
262
|
|
|
183
263
|
render() {
|
|
184
|
-
const {
|
|
185
|
-
const fileCardCls = cls({
|
|
186
|
-
[`${prefixCls}-file-card`]: true,
|
|
187
|
-
[`${prefixCls}-file-card-fail`]: status === strings.FILE_STATUS_VALID_FAIL || status === strings.FILE_STATUS_UPLOAD_FAIL,
|
|
188
|
-
[`${prefixCls}-file-card-show-pointer`]: typeof onPreviewClick !== 'undefined',
|
|
189
|
-
});
|
|
190
|
-
const previewCls = cls({
|
|
191
|
-
[`${prefixCls}-file-card-preview`]: true,
|
|
192
|
-
[`${prefixCls}-file-card-preview-placeholder`]: !preview || previewFile
|
|
193
|
-
});
|
|
194
|
-
const infoCls = `${prefixCls}-file-card-info`;
|
|
195
|
-
const closeCls = `${prefixCls}-file-card-close`;
|
|
196
|
-
const replaceCls = `${prefixCls}-file-card-replace`;
|
|
197
|
-
const showProgress = !(percent === 100 || typeof percent === 'undefined') && status === strings.FILE_STATUS_UPLOADING;
|
|
198
|
-
// only show retry when upload fail & showRetry is true, no need to show during validate fail
|
|
199
|
-
const showRetry = status === strings.FILE_STATUS_UPLOAD_FAIL && this.props.showRetry;
|
|
200
|
-
const showReplace = status === strings.FILE_STATUS_SUCCESS && this.props.showReplace;
|
|
201
|
-
|
|
264
|
+
const { listType } = this.props;
|
|
202
265
|
if (listType === strings.FILE_LIST_PIC) {
|
|
203
266
|
return (
|
|
204
267
|
<LocaleConsumer componentName="Upload">
|
|
205
|
-
{(locale: Locale[
|
|
268
|
+
{(locale: Locale["Upload"]) => (this.renderPic(locale))}
|
|
206
269
|
</LocaleConsumer>
|
|
207
270
|
);
|
|
208
271
|
}
|
|
209
272
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
273
|
+
if (listType === strings.FILE_LIST_DEFAULT) {
|
|
274
|
+
return (
|
|
275
|
+
<LocaleConsumer componentName="Upload">
|
|
276
|
+
{(locale: Locale["Upload"]) => (this.renderFile(locale))}
|
|
277
|
+
</LocaleConsumer>
|
|
278
|
+
);
|
|
214
279
|
}
|
|
215
|
-
return (
|
|
216
|
-
<LocaleConsumer componentName="Upload">
|
|
217
|
-
{(locale: Locale['Upload']): ReactNode => (
|
|
218
|
-
<div className={fileCardCls} style={style} onClick={onPreviewClick}>
|
|
219
|
-
{/* <a target='_blank' href={url} className={infoCls} rel="noopener noreferrer"> */}
|
|
220
|
-
<div className={previewCls}>
|
|
221
|
-
{previewContent}
|
|
222
|
-
</div>
|
|
223
|
-
<div className={`${infoCls}-main`}>
|
|
224
|
-
<div className={`${infoCls}-main-text`}>
|
|
225
|
-
<span className={`${infoCls}-name`}>
|
|
226
|
-
{name}
|
|
227
|
-
</span>
|
|
228
|
-
<span>
|
|
229
|
-
<span className={`${infoCls}-size`}>{fileSize}</span>
|
|
230
|
-
{showReplace && (
|
|
231
|
-
<Tooltip trigger="hover" position="top" showArrow={false} content={locale.replace}>
|
|
232
|
-
<IconButton
|
|
233
|
-
onClick={(e): void => this.onReplace(e)}
|
|
234
|
-
type="tertiary"
|
|
235
|
-
theme="borderless"
|
|
236
|
-
size="small"
|
|
237
|
-
icon={<DirectorySvg />}
|
|
238
|
-
className={replaceCls}
|
|
239
|
-
/>
|
|
240
|
-
</Tooltip>
|
|
241
|
-
)}
|
|
242
280
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
</div>
|
|
246
|
-
{showProgress ? (<Progress percent={percent} style={{ width: '100%' }} />) : null}
|
|
247
|
-
<div className={`${infoCls}-main-control`}>
|
|
248
|
-
<span className={`${infoCls}-validate-message`}>
|
|
249
|
-
{this.renderValidateMessage()}
|
|
250
|
-
</span>
|
|
251
|
-
{showRetry ? <span className={`${infoCls}-retry`} onClick={e => this.onRetry(e)}>{locale.retry}</span> : null}
|
|
252
|
-
</div>
|
|
253
|
-
</div>
|
|
254
|
-
{/* </a> */}
|
|
255
|
-
<IconButton
|
|
256
|
-
onClick={(e): void => this.onRemove(e)}
|
|
257
|
-
type="tertiary"
|
|
258
|
-
icon={<IconClose />}
|
|
259
|
-
theme="borderless"
|
|
260
|
-
size="small"
|
|
261
|
-
className={closeCls}
|
|
262
|
-
/>
|
|
263
|
-
</div>
|
|
264
|
-
)}
|
|
265
|
-
</LocaleConsumer>
|
|
266
|
-
);
|
|
281
|
+
return null;
|
|
267
282
|
}
|
|
268
283
|
}
|
|
269
284
|
|