@douyinfe/semi-ui 2.17.0-beta.1 → 2.18.0-beta.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.
Files changed (155) hide show
  1. package/anchor/index.tsx +1 -1
  2. package/anchor/link.tsx +3 -4
  3. package/autoComplete/__test__/autoComplete.test.js +6 -6
  4. package/autoComplete/index.tsx +3 -1
  5. package/autoComplete/option.tsx +164 -0
  6. package/calendar/__test__/calendar.test.js +21 -2
  7. package/calendar/_story/calendar.stories.js +31 -0
  8. package/calendar/index.tsx +3 -1
  9. package/calendar/interface.ts +2 -1
  10. package/carousel/index.tsx +5 -5
  11. package/checkbox/checkbox.tsx +4 -1
  12. package/dist/css/semi.css +161 -23
  13. package/dist/css/semi.min.css +1 -1
  14. package/dist/umd/semi-ui.js +359 -94
  15. package/dist/umd/semi-ui.js.map +1 -1
  16. package/dist/umd/semi-ui.min.js +1 -1
  17. package/dist/umd/semi-ui.min.js.map +1 -1
  18. package/form/_story/FieldProps/labelOptional.jsx +30 -0
  19. package/form/_story/form.stories.js +7 -0
  20. package/form/hoc/withField.tsx +1 -0
  21. package/form/label.tsx +21 -7
  22. package/gulpfile.js +3 -1
  23. package/lib/cjs/_base/base.css +35 -0
  24. package/lib/cjs/anchor/index.js +2 -1
  25. package/lib/cjs/anchor/link.d.ts +1 -1
  26. package/lib/cjs/anchor/link.js +9 -5
  27. package/lib/cjs/autoComplete/index.d.ts +1 -1
  28. package/lib/cjs/autoComplete/index.js +6 -3
  29. package/lib/cjs/autoComplete/option.d.ts +50 -0
  30. package/lib/cjs/autoComplete/option.js +218 -0
  31. package/lib/cjs/calendar/index.d.ts +2 -0
  32. package/lib/cjs/calendar/index.js +3 -1
  33. package/lib/cjs/calendar/interface.d.ts +2 -1
  34. package/lib/cjs/carousel/index.js +2 -2
  35. package/lib/cjs/checkbox/checkbox.js +4 -1
  36. package/lib/cjs/form/hoc/withField.js +2 -1
  37. package/lib/cjs/form/label.d.ts +8 -5
  38. package/lib/cjs/form/label.js +15 -4
  39. package/lib/cjs/locale/interface.d.ts +3 -0
  40. package/lib/cjs/locale/source/ar.js +3 -0
  41. package/lib/cjs/locale/source/de.js +3 -0
  42. package/lib/cjs/locale/source/en_GB.js +3 -0
  43. package/lib/cjs/locale/source/en_US.js +3 -0
  44. package/lib/cjs/locale/source/es.js +3 -0
  45. package/lib/cjs/locale/source/fr.js +3 -0
  46. package/lib/cjs/locale/source/id_ID.js +3 -0
  47. package/lib/cjs/locale/source/it.js +3 -0
  48. package/lib/cjs/locale/source/ja_JP.js +3 -0
  49. package/lib/cjs/locale/source/ko_KR.js +3 -0
  50. package/lib/cjs/locale/source/ms_MY.js +3 -0
  51. package/lib/cjs/locale/source/pt_BR.js +3 -0
  52. package/lib/cjs/locale/source/ru_RU.js +3 -0
  53. package/lib/cjs/locale/source/th_TH.js +3 -0
  54. package/lib/cjs/locale/source/tr_TR.js +3 -0
  55. package/lib/cjs/locale/source/vi_VN.js +3 -0
  56. package/lib/cjs/locale/source/zh_CN.js +3 -0
  57. package/lib/cjs/locale/source/zh_TW.js +3 -0
  58. package/lib/cjs/modal/Modal.js +0 -8
  59. package/lib/cjs/modal/ModalContent.js +4 -1
  60. package/lib/cjs/tag/group.d.ts +3 -0
  61. package/lib/cjs/tag/group.js +24 -6
  62. package/lib/cjs/tag/index.d.ts +2 -1
  63. package/lib/cjs/tag/index.js +7 -5
  64. package/lib/cjs/tag/interface.d.ts +2 -1
  65. package/lib/cjs/tree/index.d.ts +3 -1
  66. package/lib/cjs/tree/index.js +23 -0
  67. package/lib/cjs/tree/interface.d.ts +4 -0
  68. package/lib/es/_base/base.css +35 -0
  69. package/lib/es/anchor/index.js +2 -1
  70. package/lib/es/anchor/link.d.ts +1 -1
  71. package/lib/es/anchor/link.js +9 -5
  72. package/lib/es/autoComplete/index.d.ts +1 -1
  73. package/lib/es/autoComplete/index.js +6 -3
  74. package/lib/es/autoComplete/option.d.ts +50 -0
  75. package/lib/es/autoComplete/option.js +188 -0
  76. package/lib/es/calendar/index.d.ts +2 -0
  77. package/lib/es/calendar/index.js +3 -1
  78. package/lib/es/calendar/interface.d.ts +2 -1
  79. package/lib/es/carousel/index.js +2 -2
  80. package/lib/es/checkbox/checkbox.js +4 -1
  81. package/lib/es/form/hoc/withField.js +2 -1
  82. package/lib/es/form/label.d.ts +8 -5
  83. package/lib/es/form/label.js +13 -4
  84. package/lib/es/locale/interface.d.ts +3 -0
  85. package/lib/es/locale/source/ar.js +3 -0
  86. package/lib/es/locale/source/de.js +3 -0
  87. package/lib/es/locale/source/en_GB.js +3 -0
  88. package/lib/es/locale/source/en_US.js +3 -0
  89. package/lib/es/locale/source/es.js +3 -0
  90. package/lib/es/locale/source/fr.js +3 -0
  91. package/lib/es/locale/source/id_ID.js +3 -0
  92. package/lib/es/locale/source/it.js +3 -0
  93. package/lib/es/locale/source/ja_JP.js +3 -0
  94. package/lib/es/locale/source/ko_KR.js +3 -0
  95. package/lib/es/locale/source/ms_MY.js +3 -0
  96. package/lib/es/locale/source/pt_BR.js +3 -0
  97. package/lib/es/locale/source/ru_RU.js +3 -0
  98. package/lib/es/locale/source/th_TH.js +3 -0
  99. package/lib/es/locale/source/tr_TR.js +3 -0
  100. package/lib/es/locale/source/vi_VN.js +3 -0
  101. package/lib/es/locale/source/zh_CN.js +3 -0
  102. package/lib/es/locale/source/zh_TW.js +3 -0
  103. package/lib/es/modal/Modal.js +0 -8
  104. package/lib/es/modal/ModalContent.js +4 -1
  105. package/lib/es/tag/group.d.ts +3 -0
  106. package/lib/es/tag/group.js +24 -6
  107. package/lib/es/tag/index.d.ts +2 -1
  108. package/lib/es/tag/index.js +7 -5
  109. package/lib/es/tag/interface.d.ts +2 -1
  110. package/lib/es/tree/index.d.ts +3 -1
  111. package/lib/es/tree/index.js +22 -0
  112. package/lib/es/tree/interface.d.ts +4 -0
  113. package/locale/interface.ts +3 -0
  114. package/locale/source/ar.ts +3 -0
  115. package/locale/source/de.ts +3 -0
  116. package/locale/source/en_GB.ts +3 -0
  117. package/locale/source/en_US.ts +3 -0
  118. package/locale/source/es.ts +3 -0
  119. package/locale/source/fr.ts +3 -0
  120. package/locale/source/id_ID.ts +3 -0
  121. package/locale/source/it.ts +3 -0
  122. package/locale/source/ja_JP.ts +3 -0
  123. package/locale/source/ko_KR.ts +3 -0
  124. package/locale/source/ms_MY.ts +3 -0
  125. package/locale/source/pt_BR.ts +3 -0
  126. package/locale/source/ru_RU.ts +3 -0
  127. package/locale/source/th_TH.ts +3 -0
  128. package/locale/source/tr_TR.ts +4 -1
  129. package/locale/source/vi_VN.ts +3 -0
  130. package/locale/source/zh_CN.ts +3 -0
  131. package/locale/source/zh_TW.ts +3 -0
  132. package/modal/Modal.tsx +0 -6
  133. package/modal/ModalContent.tsx +4 -1
  134. package/modal/__test__/modal.test.js +1 -1
  135. package/modal/_story/__snapshots__/modal.stories.tsx.snap +203 -0
  136. package/package.json +7 -7
  137. package/radio/_story/radio.stories.js +2 -2
  138. package/rating/__test__/rating.test.js +1 -1
  139. package/select/__test__/select.test.js +11 -17
  140. package/select/_story/select.stories.js +6 -6
  141. package/steps/_story/steps.stories.js +3 -3
  142. package/switch/_story/switch.stories.js +4 -4
  143. package/switch/_story/switch.stories.tsx +4 -4
  144. package/table/_story/v2/FixedFilter/index.tsx +106 -0
  145. package/table/_story/v2/FixedSorter/index.tsx +102 -0
  146. package/table/_story/v2/index.js +4 -2
  147. package/tag/_story/tag.stories.js +57 -1
  148. package/tag/group.tsx +20 -3
  149. package/tag/index.tsx +6 -5
  150. package/tag/interface.ts +2 -1
  151. package/transfer/_story/transfer.stories.js +2 -2
  152. package/tree/_story/tree.stories.js +152 -3
  153. package/tree/index.tsx +16 -1
  154. package/tree/interface.ts +6 -0
  155. package/upload/_story/upload.stories.js +2 -2
@@ -0,0 +1,102 @@
1
+ import React, { useState } from 'react';
2
+ // eslint-disable-next-line semi-design/no-import
3
+ import { Table } from '@douyinfe/semi-ui';
4
+ // eslint-disable-next-line semi-design/no-import
5
+ import { ChangeInfo } from '@douyinfe/semi-ui/table';
6
+
7
+ const data = [
8
+ {
9
+ key: 'a',
10
+ group: 'yes',
11
+ count: 3,
12
+ },
13
+ {
14
+ key: 'b',
15
+ group: 'no',
16
+ count: 3,
17
+ },
18
+ {
19
+ key: 'c',
20
+ group: 'no',
21
+ count: 1,
22
+ },
23
+ {
24
+ key: 'd',
25
+ group: 'yes',
26
+ count: 1,
27
+ },
28
+ {
29
+ key: 'e',
30
+ group: 'no',
31
+ count: 2,
32
+ },
33
+ {
34
+ key: 'f',
35
+ group: 'yes',
36
+ count: 2,
37
+ }
38
+ ];
39
+
40
+ Demo.storyName = 'fixed sorter';
41
+ /**
42
+ * 保持分组顺序不变的排序方式
43
+ */
44
+ function Demo() {
45
+ const [filtered, setFiltered] = useState([...data]);
46
+ console.log(filtered);
47
+ const columns = [
48
+ {
49
+ title: 'ID',
50
+ dataIndex: 'key',
51
+ },
52
+ {
53
+ title: 'group',
54
+ dataIndex: 'Group',
55
+ sorter: (a, b) => a.group === 'yes' ? -1 : 1,
56
+ // sortOrder: 'ascend'
57
+ },
58
+ {
59
+ title: 'Count',
60
+ dataIndex: 'count',
61
+ // sorter: true
62
+ sorter: (a, b) => a.count - b.count > 0 ? 1 : -1,
63
+ },
64
+ ];
65
+
66
+ const onTableChange = ({ sorter }: ChangeInfo<any>) => {
67
+ if (sorter) {
68
+ const { dataIndex, sortOrder } = sorter;
69
+ setFiltered(prev => [...prev].sort((a, b) => {
70
+ if (a.group !== b.group) {
71
+ return a.group === 'yes' ? -1 : 1;
72
+ }
73
+
74
+ let ascendValue = -1;
75
+ if (dataIndex === 'count') {
76
+ ascendValue = a.count - b.count > 0 ? 1 : -1;
77
+ }
78
+
79
+ return sortOrder === 'ascend' ? ascendValue : -ascendValue;
80
+ }));
81
+ }
82
+ };
83
+
84
+
85
+ return (
86
+ <div style={{ padding: '20px 0px' }}>
87
+ <Table
88
+ dataSource={filtered}
89
+ onChange={onTableChange}
90
+ rowKey="key"
91
+ groupBy="group"
92
+ columns={columns}
93
+ renderGroupSection={groupKey => <strong>分组 {groupKey}</strong>}
94
+ expandAllGroupRows
95
+ scroll={{ y: 480 }}
96
+ pagination={{ pageSize: 4 }}
97
+ />
98
+ </div>
99
+ );
100
+ }
101
+
102
+ export default Demo;
@@ -4,7 +4,9 @@ export { default as FixedZIndex } from './FixedZIndex';
4
4
  export { default as FixedHeaderMerge } from './FixedHeaderMerge';
5
5
  export { default as FixedResizable } from './FixedResizable';
6
6
  export { default as FixedExpandedRow } from './FixedExpandedRow';
7
- export { default as FixedMemoryLeak } from './FixedMemoryLeak';
7
+ export { default as FixedMemoryLeak } from './FixedMemoryLeak';
8
8
  export { default as FixedOnHeaderRow } from './FixedOnHeaderRow';
9
9
  export { default as RadioRowSelection } from './radioRowSelection';
10
- export { default as FixedVirtualizedEmpty } from './FixedVirtualizedEmpty';
10
+ export { default as FixedVirtualizedEmpty } from './FixedVirtualizedEmpty';
11
+ export { default as FixedFilter } from './FixedFilter';
12
+ export { default as FixedSorter } from './FixedSorter';
@@ -1,5 +1,5 @@
1
1
  /* argus-disable unPkgSensitiveInfo */
2
- import React from 'react';
2
+ import React, { useCallback, useState } from 'react';
3
3
  import withPropsCombinations from 'react-storybook-addon-props-combinations';
4
4
  import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants';
5
5
 
@@ -232,3 +232,59 @@ export const AvatarTagGroup = () => <AvatarTagGroupDemo />;
232
232
  AvatarTagGroup.story = {
233
233
  name: 'avatar tagGroup',
234
234
  };
235
+
236
+ class TagGroupCloseableDemo extends React.Component {
237
+ constructor(props){
238
+ super(props);
239
+ this.state = {
240
+ tagList: [
241
+ { tagKey: '1', color: 'white', children: '抖音', closable: true,},
242
+ { tagKey: '2',color: 'white', children: '火山小视频', closable: true,},
243
+ { tagKey: '3',color: 'white', children: '剪映', closable: true,},
244
+ { tagKey: '4',color: 'white', children: '皮皮虾', closable: true,},
245
+ ]
246
+ };
247
+ this.tagListClick = this.tagListClick.bind(this);
248
+ }
249
+
250
+ tagListClick(value, e, tagKey){
251
+ const newTagList = [...this.state.tagList];
252
+ const closeTagIndex = newTagList.findIndex(t => t.tagKey === tagKey);
253
+ newTagList.splice(closeTagIndex, 1);
254
+ this.setState({
255
+ tagList: newTagList,
256
+ });
257
+ }
258
+
259
+ render() {
260
+ return (
261
+ <div style={ {
262
+ backgroundColor: 'var(--semi-color-fill-0)',
263
+ height: 35,
264
+ width: 300,
265
+ display: 'flex',
266
+ alignItems: 'center',
267
+ padding: '0 10px',
268
+ marginBottom: 30,
269
+ }}>
270
+ <TagGroup
271
+ maxTagCount={3}
272
+ style={ {
273
+ display: 'flex',
274
+ alignItems: 'center',
275
+ width: 350,
276
+ }}
277
+ tagList={this.state.tagList}
278
+ size='large'
279
+ onTagClose={this.tagListClick}
280
+ />
281
+ </div>
282
+ );
283
+ }
284
+ }
285
+
286
+ export const TagGroupCloseable = () => <TagGroupCloseableDemo />;
287
+
288
+ TagGroupCloseable.story = {
289
+ name: 'tagGroup closable',
290
+ }
package/tag/group.tsx CHANGED
@@ -21,6 +21,7 @@ export interface TagGroupProps<T> {
21
21
  popoverProps?: PopoverProps;
22
22
  avatarShape?: AvatarShape;
23
23
  mode?: string;
24
+ onTagClose: (tagChildren: React.ReactNode, event: React.MouseEvent<HTMLElement>, tagKey: string | number) => void;
24
25
  }
25
26
 
26
27
  export default class TagGroup<T> extends PureComponent<TagGroupProps<T>> {
@@ -29,6 +30,7 @@ export default class TagGroup<T> extends PureComponent<TagGroupProps<T>> {
29
30
  className: '',
30
31
  size: tagSize[0],
31
32
  avatarShape: 'square',
33
+ onTagClose: () => undefined,
32
34
  };
33
35
 
34
36
  static propTypes = {
@@ -40,6 +42,7 @@ export default class TagGroup<T> extends PureComponent<TagGroupProps<T>> {
40
42
  tagList: PropTypes.array,
41
43
  size: PropTypes.oneOf(tagSize),
42
44
  mode: PropTypes.string,
45
+ onTagClose: PropTypes.func,
43
46
  showPopover: PropTypes.bool,
44
47
  popoverProps: PropTypes.object,
45
48
  avatarShape: PropTypes.oneOf(avatarShapeSet),
@@ -95,18 +98,32 @@ export default class TagGroup<T> extends PureComponent<TagGroupProps<T>> {
95
98
  }
96
99
 
97
100
  renderAllTags() {
98
- const { tagList, size, mode, avatarShape } = this.props;
99
- const renderTags = tagList.map((tag, index): (Tag | React.ReactNode) => {
101
+ const { tagList, size, mode, avatarShape, onTagClose } = this.props;
102
+ const renderTags = tagList.map((tag): (Tag | React.ReactNode) => {
100
103
  if (mode === 'custom') {
101
104
  return tag as React.ReactNode;
102
105
  }
103
106
  if (!(tag as TagProps).size) {
104
107
  (tag as TagProps).size = size;
105
108
  }
109
+
106
110
  if (!(tag as TagProps).avatarShape) {
107
111
  (tag as TagProps).avatarShape = avatarShape;
108
112
  }
109
- return <Tag key={`${index}-tag`} {...(tag as TagProps)} />;
113
+
114
+ if (!(tag as TagProps).tagKey) {
115
+ if (typeof (tag as TagProps).children === 'string' || typeof (tag as TagProps).children === 'number') {
116
+ (tag as TagProps).tagKey = (tag as TagProps).children as string | number;
117
+ } else {
118
+ (tag as TagProps).tagKey = Math.random();
119
+ }
120
+ }
121
+ return <Tag {...(tag as TagProps)} key={(tag as TagProps).tagKey} onClose={(tagChildren, e, tagKey) => {
122
+ if ((tag as TagProps).onClose) {
123
+ (tag as TagProps).onClose(tagChildren, e, tagKey);
124
+ }
125
+ onTagClose && onTagClose(tagChildren, e, tagKey);
126
+ }} />;
110
127
  });
111
128
  return renderTags;
112
129
  }
package/tag/index.tsx CHANGED
@@ -40,6 +40,7 @@ export default class Tag extends Component<TagProps, TagState> {
40
40
 
41
41
  static propTypes = {
42
42
  children: PropTypes.node,
43
+ tagKey: PropTypes.oneOf([PropTypes.string, PropTypes.number]),
43
44
  size: PropTypes.oneOf(tagSize),
44
45
  color: PropTypes.oneOf(tagColors),
45
46
  type: PropTypes.oneOf(tagType),
@@ -79,11 +80,11 @@ export default class Tag extends Component<TagProps, TagState> {
79
80
  }
80
81
  }
81
82
 
82
- close(e: React.MouseEvent<HTMLElement>, value: React.ReactNode) {
83
+ close(e: React.MouseEvent<HTMLElement>, value: React.ReactNode, tagKey: string | number) {
83
84
  const { onClose } = this.props;
84
85
  e.stopPropagation();
85
86
  e.nativeEvent.stopImmediatePropagation();
86
- onClose && onClose(value, e);
87
+ onClose && onClose(value, e, tagKey);
87
88
  // when user call e.preventDefault() in onClick callback, tag will not hidden
88
89
  if (e.defaultPrevented) {
89
90
  return;
@@ -96,7 +97,7 @@ export default class Tag extends Component<TagProps, TagState> {
96
97
  switch (event.key) {
97
98
  case "Backspace":
98
99
  case "Delete":
99
- closable && this.close(event, this.props.children);
100
+ closable && this.close(event, this.props.children, this.props.tagKey);
100
101
  handlePrevent(event);
101
102
  break;
102
103
  case "Enter":
@@ -119,7 +120,7 @@ export default class Tag extends Component<TagProps, TagState> {
119
120
  }
120
121
 
121
122
  render() {
122
- const { children, size, color, closable, visible, onClose, onClick, className, type, avatarSrc, avatarShape, tabIndex, ...attr } = this.props;
123
+ const { tagKey, children, size, color, closable, visible, onClose, onClick, className, type, avatarSrc, avatarShape, tabIndex, ...attr } = this.props;
123
124
  const { visible: isVisible } = this.state;
124
125
  const clickable = onClick !== Tag.defaultProps.onClick || closable;
125
126
  // only when the Tag is clickable or closable, the value of tabIndex is allowed to be passed in.
@@ -145,7 +146,7 @@ export default class Tag extends Component<TagProps, TagState> {
145
146
  const wrapProps = clickable ? ({ ...baseProps, ...a11yProps }) : baseProps;
146
147
  const closeIcon = closable ? (
147
148
  // eslint-disable-next-line jsx-a11y/click-events-have-key-events
148
- <div className={`${prefixCls}-close`} onClick={e => this.close(e, children)}>
149
+ <div className={`${prefixCls}-close`} onClick={e => this.close(e, children, tagKey)}>
149
150
  <IconClose size="small" />
150
151
  </div>
151
152
  ) : null;
package/tag/interface.ts CHANGED
@@ -22,12 +22,13 @@ export type AvatarShape = 'circle' | 'square';
22
22
 
23
23
  export interface TagProps {
24
24
  children?: React.ReactNode;
25
+ tagKey?: string | number;
25
26
  size?: TagSize;
26
27
  color?: TagColor;
27
28
  type?: TagType;
28
29
  closable?: boolean;
29
30
  visible?: boolean;
30
- onClose?: (tagChildren: React.ReactNode, event: React.MouseEvent<HTMLElement>) => void;
31
+ onClose?: (tagChildren: React.ReactNode, event: React.MouseEvent<HTMLElement>, tagKey: string | number) => void;
31
32
  onClick?: React.MouseEventHandler<HTMLDivElement>;
32
33
  style?: React.CSSProperties;
33
34
  className?: string;
@@ -195,7 +195,7 @@ TransferDraggableAndDisabled.story = {
195
195
  }
196
196
 
197
197
 
198
- const ControledTransfer = () => {
198
+ const ControlledTransfer = () => {
199
199
  const [value, setValue] = useState([2, 3]);
200
200
 
201
201
  const handleChange = value => {
@@ -209,7 +209,7 @@ const ControledTransfer = () => {
209
209
  );
210
210
  };
211
211
 
212
- export const ControlledTransfer = () => <ControledTransfer />;
212
+ export const ControlledTransfer = () => <ControlledTransfer />;
213
213
 
214
214
  ControlledTransfer.story = {
215
215
  name: '受控Transfer',
@@ -1,9 +1,9 @@
1
- import React, { useRef, useState } from 'react';
1
+ import React, { useRef, useState, useCallback } from 'react';
2
2
  import { cloneDeep, difference, isEqual } from 'lodash';
3
3
  import { IconEdit, IconMapPin, IconMore } from '@douyinfe/semi-icons';
4
4
  import Tree from '../index';
5
5
  import AutoSizer from '../autoSizer';
6
- import { Button, ButtonGroup, Input, Popover, Toast, Space } from '../../index';
6
+ import { Button, ButtonGroup, Input, Popover, Toast, Space, Select, Switch } from '../../index';
7
7
  import BigTree from './BigData';
8
8
  import testData from './data';
9
9
  const TreeNode = Tree.TreeNode;
@@ -2398,4 +2398,153 @@ export const ValueImpactExpansionWithDynamicTreeData = () => {
2398
2398
  </Space>
2399
2399
  </>
2400
2400
  )
2401
- }
2401
+ }
2402
+
2403
+ class DemoV extends React.Component {
2404
+ constructor() {
2405
+ super();
2406
+ this.state = {
2407
+ gData: [],
2408
+ total: 0,
2409
+ align: 'center',
2410
+ scrollKey: '',
2411
+ expandAll: false,
2412
+ };
2413
+ this.onGen = this.onGen.bind(this);
2414
+ this.onScroll = this.onScroll.bind(this);
2415
+ this.onInputChange = this.onInputChange.bind(this);
2416
+ this.onInputBlur = this.onInputBlur.bind(this);
2417
+ this.onSelectChange = this.onSelectChange.bind(this);
2418
+ this.treeRef = React.createRef();
2419
+ }
2420
+
2421
+ generateData(x = 5, y = 4, z = 3, gData = []) {
2422
+ // x:每一级下的节点总数。y:每级节点里有y个节点、存在子节点。z:树的level层级数(0表示一级)
2423
+ function _loop(_level, _preKey, _tns) {
2424
+ const preKey = _preKey || '0';
2425
+ const tns = _tns || gData;
2426
+
2427
+ const children = [];
2428
+ for (let i = 0; i < x; i++) {
2429
+ const key = `${preKey}-${i}`;
2430
+ tns.push({ label: `${key}-标签`, key: `${key}-key`, value: `${key}-value` });
2431
+ if (i < y) {
2432
+ children.push(key);
2433
+ }
2434
+ }
2435
+ if (_level < 0) {
2436
+ return tns;
2437
+ }
2438
+ const __level = _level - 1;
2439
+ children.forEach((key, index) => {
2440
+ tns[index].children = [];
2441
+ return _loop(__level, key, tns[index].children);
2442
+ });
2443
+
2444
+ return null;
2445
+ }
2446
+ _loop(z);
2447
+
2448
+ function calcTotal(x, y, z) {
2449
+ const rec = n => (n >= 0 ? x * y ** n-- + rec(n) : 0);
2450
+ return rec(z + 1);
2451
+ }
2452
+ return { gData, total: calcTotal(x, y, z) };
2453
+ }
2454
+
2455
+ onGen() {
2456
+ const { gData, total } = this.generateData();
2457
+ this.setState({
2458
+ gData,
2459
+ total
2460
+ });
2461
+ };
2462
+
2463
+ onScroll(scrollKey, align) {
2464
+ this.treeRef?.current.scrollTo({ key: scrollKey, align});
2465
+ }
2466
+
2467
+ onInputChange(value) {
2468
+ this.setState({
2469
+ scrollKey: value,
2470
+ })
2471
+ }
2472
+
2473
+ onInputBlur(e) {
2474
+ const { value } = e.target;
2475
+ this.onScroll(value, this.state.align);
2476
+ }
2477
+
2478
+ onSelectChange(align){
2479
+ this.setState({
2480
+ align: align,
2481
+ })
2482
+ this.onScroll(this.state.scrollKey, align);
2483
+ }
2484
+
2485
+ render() {
2486
+ const style = {
2487
+ width: 260,
2488
+ border: '1px solid var(--semi-color-border)'
2489
+ };
2490
+ return (
2491
+ <div style={{ padding: '0 20px' }}>
2492
+ <Button onClick={this.onGen}>生成数据: </Button>
2493
+ <span>共 {this.state.total} 个节点</span>
2494
+ <br/>
2495
+ <br/>
2496
+ <div style={{ display: 'flex', alignItems: 'center', }}>
2497
+ <span>defaultExpandAll</span>
2498
+ <Switch onChange={(value) => {
2499
+ this.setState({
2500
+ expandAll: value,
2501
+ })
2502
+ }}/>
2503
+ </div>
2504
+ <br/>
2505
+ <span>跳转的key:</span>
2506
+ <Input
2507
+ placeholder={'格式:x-x-key'}
2508
+ style={{ width: 180, marginRight: 20 }}
2509
+ onChange={this.onInputChange}
2510
+ onBlur={this.onInputBlur}
2511
+ ></Input>
2512
+ <span>scroll align:</span>
2513
+ <Select
2514
+ defaultValue='center'
2515
+ style={{ width: 180 }}
2516
+ optionList={['center', 'start', 'end', 'smart', 'auto'].map(item => ({
2517
+ value: item,
2518
+ label: item,
2519
+ }))}
2520
+ onChange={this.onSelectChange}
2521
+ >
2522
+ </Select>
2523
+ <br />
2524
+ <br />
2525
+ {this.state.gData.length ? (
2526
+ <Tree
2527
+ key={`key-${this.state.expandAll}`}
2528
+ ref={this.treeRef}
2529
+ defaultExpandAll={this.state.expandAll}
2530
+ treeData={this.state.gData}
2531
+ filterTreeNode
2532
+ showFilteredOnly
2533
+ style={style}
2534
+ virtualize={{
2535
+ // if set height for tree, it will fill 100%
2536
+ height: 300,
2537
+ itemSize: 28,
2538
+ }}
2539
+ />
2540
+ ) : null}
2541
+ </div>
2542
+ );
2543
+ }
2544
+ }
2545
+
2546
+ export const virtualizeTree = () => <DemoV />;
2547
+
2548
+ virtualizeTree.story = {
2549
+ name: 'virtualize tree',
2550
+ };
package/tree/index.tsx CHANGED
@@ -39,7 +39,8 @@ import {
39
39
  TreeNodeData,
40
40
  FlattenNode,
41
41
  KeyEntity,
42
- OptionProps
42
+ OptionProps,
43
+ ScrollData,
43
44
  } from './interface';
44
45
  import CheckboxGroup from '../checkbox/checkboxGroup';
45
46
 
@@ -146,6 +147,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
146
147
  onNodeClick: any;
147
148
  onMotionEnd: any;
148
149
  context: ContextValue;
150
+ virtualizedListRef: React.RefObject<any>;
149
151
 
150
152
  constructor(props: TreeProps) {
151
153
  super(props);
@@ -179,6 +181,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
179
181
  this.optionsRef = React.createRef();
180
182
  this.foundation = new TreeFoundation(this.adapter);
181
183
  this.dragNode = null;
184
+ this.virtualizedListRef = React.createRef();
182
185
  }
183
186
 
184
187
  /**
@@ -493,6 +496,17 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
493
496
  this.foundation.handleInputChange(value);
494
497
  };
495
498
 
499
+ scrollTo = (scrollData: ScrollData) => {
500
+ const { key, align = 'center' } = scrollData;
501
+ const { flattenNodes } = this.state;
502
+ if (key) {
503
+ const index = flattenNodes?.findIndex((node) => {
504
+ return node.key === key;
505
+ });
506
+ index >= 0 && (this.virtualizedListRef.current as any)?.scrollToItem(index, align);
507
+ }
508
+ }
509
+
496
510
  renderInput() {
497
511
  const {
498
512
  searchClassName,
@@ -664,6 +678,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
664
678
  <AutoSizer defaultHeight={virtualize.height} defaultWidth={virtualize.width}>
665
679
  {({ height, width }: { width: string | number; height: string | number }) => (
666
680
  <VirtualList
681
+ ref={this.virtualizedListRef}
667
682
  itemCount={flattenNodes.length}
668
683
  itemSize={virtualize.itemSize}
669
684
  height={height}
package/tree/interface.ts CHANGED
@@ -140,3 +140,9 @@ export interface NodeListState {
140
140
  cachedMotionKeys?: Set<string>;
141
141
  cachedData?: FlattenNode[];
142
142
  }
143
+
144
+ export interface ScrollData {
145
+ key: string;
146
+ // The align parameter is consistent with react-window
147
+ align?: 'center' | 'start' | 'end' | 'smart' | 'auto';
148
+ }
@@ -311,7 +311,7 @@ DefaultFileList.story = {
311
311
  name: 'defaultFileList',
312
312
  };
313
313
 
314
- class ControledUpload extends React.Component {
314
+ class ControlledUpload extends React.Component {
315
315
  constructor(props) {
316
316
  super(props);
317
317
  this.state = {
@@ -344,7 +344,7 @@ class ControledUpload extends React.Component {
344
344
  }
345
345
  }
346
346
 
347
- export const ControlledFileList = () => <ControledUpload></ControledUpload>;
347
+ export const ControlledFileList = () => <ControlledUpload></ControlledUpload>;
348
348
 
349
349
  ControlledFileList.story = {
350
350
  name: 'controlled fileList',