@douyinfe/semi-ui 2.5.1 → 2.7.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 (111) hide show
  1. package/button/__test__/button.test.js +7 -0
  2. package/button/buttonGroup.tsx +5 -2
  3. package/calendar/monthCalendar.tsx +14 -13
  4. package/cascader/__test__/cascader.test.js +159 -81
  5. package/cascader/_story/cascader.stories.js +36 -23
  6. package/cascader/index.tsx +47 -8
  7. package/cascader/item.tsx +25 -5
  8. package/datePicker/_story/v2/InsetInput.jsx +104 -0
  9. package/datePicker/_story/v2/InsetInputE2E.jsx +69 -0
  10. package/datePicker/_story/v2/index.js +2 -0
  11. package/datePicker/dateInput.tsx +102 -13
  12. package/datePicker/datePicker.tsx +95 -16
  13. package/datePicker/index.tsx +15 -0
  14. package/datePicker/insetInput.tsx +76 -0
  15. package/datePicker/month.tsx +14 -7
  16. package/datePicker/monthsGrid.tsx +31 -12
  17. package/datePicker/navigation.tsx +8 -4
  18. package/datePicker/quickControl.tsx +1 -0
  19. package/datePicker/yearAndMonth.tsx +1 -1
  20. package/dist/css/semi.css +120 -8
  21. package/dist/css/semi.min.css +1 -1
  22. package/dist/umd/semi-ui.js +1100 -193
  23. package/dist/umd/semi-ui.js.map +1 -1
  24. package/dist/umd/semi-ui.min.js +1 -1
  25. package/dist/umd/semi-ui.min.js.map +1 -1
  26. package/form/hoc/withField.tsx +1 -1
  27. package/input/_story/input.stories.js +13 -0
  28. package/lib/cjs/_base/base.css +5 -5
  29. package/lib/cjs/button/buttonGroup.d.ts +1 -0
  30. package/lib/cjs/button/buttonGroup.js +6 -2
  31. package/lib/cjs/calendar/monthCalendar.js +21 -5
  32. package/lib/cjs/cascader/index.d.ts +10 -2
  33. package/lib/cjs/cascader/index.js +52 -10
  34. package/lib/cjs/cascader/item.d.ts +6 -2
  35. package/lib/cjs/cascader/item.js +33 -4
  36. package/lib/cjs/datePicker/dateInput.d.ts +9 -4
  37. package/lib/cjs/datePicker/dateInput.js +107 -13
  38. package/lib/cjs/datePicker/datePicker.d.ts +7 -2
  39. package/lib/cjs/datePicker/datePicker.js +138 -30
  40. package/lib/cjs/datePicker/index.js +24 -2
  41. package/lib/cjs/datePicker/insetInput.d.ts +21 -0
  42. package/lib/cjs/datePicker/insetInput.js +80 -0
  43. package/lib/cjs/datePicker/month.d.ts +1 -0
  44. package/lib/cjs/datePicker/month.js +18 -2
  45. package/lib/cjs/datePicker/monthsGrid.js +35 -11
  46. package/lib/cjs/datePicker/navigation.js +8 -0
  47. package/lib/cjs/datePicker/quickControl.js +1 -0
  48. package/lib/cjs/datePicker/yearAndMonth.js +1 -0
  49. package/lib/cjs/form/hoc/withField.js +1 -1
  50. package/lib/cjs/navigation/Item.d.ts +2 -2
  51. package/lib/cjs/navigation/Item.js +8 -6
  52. package/lib/cjs/navigation/SubNav.js +2 -2
  53. package/lib/cjs/scrollList/scrollItem.d.ts +2 -1
  54. package/lib/cjs/scrollList/scrollItem.js +13 -3
  55. package/lib/cjs/table/Body/index.d.ts +2 -0
  56. package/lib/cjs/table/Body/index.js +13 -4
  57. package/lib/cjs/tree/index.js +5 -3
  58. package/lib/cjs/tree/interface.d.ts +1 -0
  59. package/lib/cjs/tree/nodeList.js +2 -1
  60. package/lib/cjs/treeSelect/index.js +7 -3
  61. package/lib/es/_base/base.css +5 -5
  62. package/lib/es/button/buttonGroup.d.ts +1 -0
  63. package/lib/es/button/buttonGroup.js +5 -2
  64. package/lib/es/calendar/monthCalendar.js +22 -5
  65. package/lib/es/cascader/index.d.ts +10 -2
  66. package/lib/es/cascader/index.js +49 -7
  67. package/lib/es/cascader/item.d.ts +6 -2
  68. package/lib/es/cascader/item.js +31 -4
  69. package/lib/es/datePicker/dateInput.d.ts +9 -4
  70. package/lib/es/datePicker/dateInput.js +106 -13
  71. package/lib/es/datePicker/datePicker.d.ts +7 -2
  72. package/lib/es/datePicker/datePicker.js +139 -30
  73. package/lib/es/datePicker/index.js +20 -0
  74. package/lib/es/datePicker/insetInput.d.ts +21 -0
  75. package/lib/es/datePicker/insetInput.js +65 -0
  76. package/lib/es/datePicker/month.d.ts +1 -0
  77. package/lib/es/datePicker/month.js +18 -2
  78. package/lib/es/datePicker/monthsGrid.js +35 -11
  79. package/lib/es/datePicker/navigation.js +8 -0
  80. package/lib/es/datePicker/quickControl.js +2 -0
  81. package/lib/es/datePicker/yearAndMonth.js +1 -0
  82. package/lib/es/form/hoc/withField.js +1 -1
  83. package/lib/es/navigation/Item.d.ts +2 -2
  84. package/lib/es/navigation/Item.js +8 -6
  85. package/lib/es/navigation/SubNav.js +2 -2
  86. package/lib/es/scrollList/scrollItem.d.ts +2 -1
  87. package/lib/es/scrollList/scrollItem.js +13 -3
  88. package/lib/es/table/Body/index.d.ts +2 -0
  89. package/lib/es/table/Body/index.js +13 -4
  90. package/lib/es/tree/index.js +5 -3
  91. package/lib/es/tree/interface.d.ts +1 -0
  92. package/lib/es/tree/nodeList.js +2 -1
  93. package/lib/es/treeSelect/index.js +7 -3
  94. package/navigation/Item.tsx +15 -12
  95. package/navigation/SubNav.tsx +4 -4
  96. package/package.json +9 -9
  97. package/scrollList/_story/ScrollList/index.js +3 -0
  98. package/scrollList/_story/WheelList/index.js +3 -0
  99. package/scrollList/scrollItem.tsx +30 -9
  100. package/table/Body/index.tsx +15 -4
  101. package/table/__test__/table.test.js +18 -0
  102. package/table/_story/v2/FixedExpandedRow/index.jsx +95 -0
  103. package/table/_story/v2/index.js +2 -1
  104. package/tree/__test__/tree.test.js +87 -2
  105. package/tree/_story/tree.stories.js +65 -5
  106. package/tree/index.tsx +4 -2
  107. package/tree/interface.ts +1 -0
  108. package/tree/nodeList.tsx +2 -2
  109. package/treeSelect/__test__/treeSelect.test.js +28 -0
  110. package/treeSelect/_story/treeSelect.stories.js +55 -2
  111. package/treeSelect/index.tsx +11 -3
@@ -1,4 +1,4 @@
1
- import React, { Fragment, ReactNode, CSSProperties, MouseEvent } from 'react';
1
+ import React, { Fragment, ReactNode, CSSProperties, MouseEvent, KeyboardEvent } from 'react';
2
2
  import ReactDOM from 'react-dom';
3
3
  import cls from 'classnames';
4
4
  import PropTypes from 'prop-types';
@@ -54,6 +54,7 @@ export interface CascaderProps extends BasicCascaderProps {
54
54
  'aria-invalid'?: React.AriaAttributes['aria-invalid'];
55
55
  'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
56
56
  'aria-required'?: React.AriaAttributes['aria-required'];
57
+ 'aria-label'?: React.AriaAttributes['aria-label'];
57
58
  arrowIcon?: ReactNode;
58
59
  defaultValue?: Value;
59
60
  dropdownStyle?: CSSProperties;
@@ -100,6 +101,7 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
100
101
  'aria-errormessage': PropTypes.string,
101
102
  'aria-describedby': PropTypes.string,
102
103
  'aria-required': PropTypes.bool,
104
+ 'aria-label': PropTypes.string,
103
105
  arrowIcon: PropTypes.node,
104
106
  changeOnSelect: PropTypes.bool,
105
107
  defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
@@ -197,6 +199,7 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
197
199
  onDropdownVisibleChange: noop,
198
200
  onListScroll: noop,
199
201
  enableLeafClick: false,
202
+ 'aria-label': 'Cascader'
200
203
  };
201
204
 
202
205
  options: any;
@@ -584,7 +587,7 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
584
587
  );
585
588
  }
586
589
 
587
- handleItemClick = (e: MouseEvent, item: Entity | Data) => {
590
+ handleItemClick = (e: MouseEvent | KeyboardEvent, item: Entity | Data) => {
588
591
  this.foundation.handleItemClick(e, item);
589
592
  };
590
593
 
@@ -708,9 +711,32 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
708
711
  );
709
712
  };
710
713
 
714
+ renderDisplayText = (): ReactNode => {
715
+ const { displayProp, separator, displayRender } = this.props;
716
+ const { selectedKeys } = this.state;
717
+ let displayText: ReactNode = '';
718
+ if (selectedKeys.size) {
719
+ const displayPath = this.foundation.getItemPropPath([...selectedKeys][0], displayProp);
720
+ if (displayRender && typeof displayRender === 'function') {
721
+ displayText = displayRender(displayPath);
722
+ } else {
723
+ displayText = displayPath.map((path: ReactNode, index: number)=>(
724
+ <Fragment key={`${path}-${index}`}>
725
+ {
726
+ index<displayPath.length-1
727
+ ? <>{path}{separator}</>
728
+ : path
729
+ }
730
+ </Fragment>
731
+ ));
732
+ }
733
+ }
734
+ return displayText;
735
+ }
736
+
711
737
  renderSelectContent = () => {
712
738
  const { placeholder, filterTreeNode, multiple } = this.props;
713
- const { selectedKeys, checkedKeys } = this.state;
739
+ const { checkedKeys } = this.state;
714
740
  const searchable = Boolean(filterTreeNode);
715
741
  if (!searchable) {
716
742
  if (multiple) {
@@ -719,11 +745,9 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
719
745
  }
720
746
  return this.renderMultipleTags();
721
747
  } else {
722
- const displayText = selectedKeys.size ?
723
- this.foundation.renderDisplayText([...selectedKeys][0]) :
724
- '';
748
+ const displayText = this.renderDisplayText();
725
749
  const spanCls = cls({
726
- [`${prefixcls}-selection-placeholder`]: !displayText || !displayText.length,
750
+ [`${prefixcls}-selection-placeholder`]: !displayText,
727
751
  });
728
752
  return <span className={spanCls}>{displayText ? displayText : placeholder}</span>;
729
753
  }
@@ -798,6 +822,14 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
798
822
  this.foundation.handleClear();
799
823
  };
800
824
 
825
+ /**
826
+ * A11y: simulate clear button click
827
+ */
828
+ handleClearEnterPress = (e: KeyboardEvent) => {
829
+ e && e.stopPropagation();
830
+ this.foundation.handleClearEnterPress();
831
+ };
832
+
801
833
  showClearBtn = () => {
802
834
  const { showClear, disabled, multiple } = this.props;
803
835
  const { selectedKeys, isOpen, isHovering, checkedKeys } = this.state;
@@ -811,7 +843,13 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
811
843
  const allowClear = this.showClearBtn();
812
844
  if (allowClear) {
813
845
  return (
814
- <div className={clearCls} onClick={this.handleClear} role='button' tabIndex={0}>
846
+ <div
847
+ className={clearCls}
848
+ onClick={this.handleClear}
849
+ onKeyPress={this.handleClearEnterPress}
850
+ role='button'
851
+ tabIndex={0}
852
+ >
815
853
  <IconClear />
816
854
  </div>
817
855
  );
@@ -891,6 +929,7 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
891
929
  style={style}
892
930
  ref={this.triggerRef}
893
931
  onClick={e => this.foundation.handleClick(e)}
932
+ onKeyPress={e => this.foundation.handleSelectionEnterPress(e)}
894
933
  aria-invalid={this.props['aria-invalid']}
895
934
  aria-errormessage={this.props['aria-errormessage']}
896
935
  aria-label={this.props['aria-label']}
package/cascader/item.tsx CHANGED
@@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
2
2
  import cls from 'classnames';
3
3
  import PropTypes from 'prop-types';
4
4
  import { cssClasses, strings } from '@douyinfe/semi-foundation/cascader/constants';
5
+ import isEnterPress from '@douyinfe/semi-foundation/utils/isEnterPress';
5
6
  import { includes } from 'lodash';
6
7
  import ConfigContext from '../configProvider/context';
7
8
  import LocaleConsumer from '../locale/localeConsumer';
@@ -43,7 +44,7 @@ export interface CascaderItemProps {
43
44
  selectedKeys: Set<string>;
44
45
  loadedKeys: Set<string>;
45
46
  loadingKeys: Set<string>;
46
- onItemClick: (e: React.MouseEvent, item: Entity | Data) => void;
47
+ onItemClick: (e: React.MouseEvent | React.KeyboardEvent, item: Entity | Data) => void;
47
48
  onItemHover: (e: React.MouseEvent, item: Entity) => void;
48
49
  showNext: ShowNextType;
49
50
  onItemCheckboxClick: (item: Entity | Data) => void;
@@ -84,7 +85,7 @@ export default class Item extends PureComponent<CascaderItemProps> {
84
85
  empty: false,
85
86
  };
86
87
 
87
- onClick = (e: React.MouseEvent, item: Entity | Data) => {
88
+ onClick = (e: React.MouseEvent | React.KeyboardEvent, item: Entity | Data) => {
88
89
  const { onItemClick } = this.props;
89
90
  if (item.data.disabled || ('disabled' in item && item.disabled)) {
90
91
  return;
@@ -92,6 +93,15 @@ export default class Item extends PureComponent<CascaderItemProps> {
92
93
  onItemClick(e, item);
93
94
  };
94
95
 
96
+ /**
97
+ * A11y: simulate item click
98
+ */
99
+ handleItemEnterPress = (keyboardEvent: React.KeyboardEvent, item: Entity | Data) => {
100
+ if (isEnterPress(keyboardEvent)) {
101
+ this.onClick(keyboardEvent, item);
102
+ }
103
+ }
104
+
95
105
  onHover = (e: React.MouseEvent, item: Entity) => {
96
106
  const { showNext, onItemHover } = this.props;
97
107
  if (item.data.disabled) {
@@ -136,7 +146,7 @@ export default class Item extends PureComponent<CascaderItemProps> {
136
146
  case 'loading':
137
147
  return <Spin wrapperClassName={`${prefixcls}-spin-icon`} />;
138
148
  case 'empty':
139
- return (<span className={`${prefixcls}-icon ${prefixcls}-icon-empty`} />);
149
+ return (<span aria-hidden={true} className={`${prefixcls}-icon ${prefixcls}-icon-empty`} />);
140
150
  default:
141
151
  return null;
142
152
  }
@@ -179,11 +189,13 @@ export default class Item extends PureComponent<CascaderItemProps> {
179
189
  });
180
190
  return (
181
191
  <li
192
+ role='menuitem'
182
193
  className={className}
183
194
  key={key}
184
195
  onClick={e => {
185
196
  this.onClick(e, item);
186
197
  }}
198
+ onKeyPress={e => this.handleItemEnterPress(e, item)}
187
199
  >
188
200
  <span className={`${prefixcls}-label`}>
189
201
  {!multiple && this.renderIcon('empty')}
@@ -211,9 +223,9 @@ export default class Item extends PureComponent<CascaderItemProps> {
211
223
  let showChildItem: Entity;
212
224
  const ind = content.length;
213
225
  content.push(
214
- <ul className={`${prefixcls}-list`} key={renderData[0].key} onScroll={e => this.props.onListScroll(e, ind)}>
226
+ <ul role='menu' className={`${prefixcls}-list`} key={renderData[0].key} onScroll={e => this.props.onListScroll(e, ind)}>
215
227
  {renderData.map(item => {
216
- const { data, key } = item;
228
+ const { data, key, parentKey } = item;
217
229
  const { children, label, disabled, isLeaf } = data;
218
230
  const { active, selected, loading } = this.getItemStatus(key);
219
231
  const hasChild = Boolean(children) && children.length;
@@ -226,13 +238,21 @@ export default class Item extends PureComponent<CascaderItemProps> {
226
238
  [`${prefixcls}-select`]: selected && !multiple,
227
239
  [`${prefixcls}-disabled`]: disabled
228
240
  });
241
+ const otherAriaProps = parentKey ? { ['aria-owns']: `cascaderItem-${parentKey}` } : {};
229
242
  return (
230
243
  <li
244
+ role='menuitem'
245
+ id={`cascaderItem-${key}`}
246
+ aria-expanded={active}
247
+ aria-haspopup={Boolean(showExpand)}
248
+ aria-disabled={disabled}
249
+ {...otherAriaProps}
231
250
  className={className}
232
251
  key={key}
233
252
  onClick={e => {
234
253
  this.onClick(e, item);
235
254
  }}
255
+ onKeyPress={e => this.handleItemEnterPress(e, item)}
236
256
  onMouseEnter={e => {
237
257
  this.onHover(e, item);
238
258
  }}
@@ -0,0 +1,104 @@
1
+ import React from 'react';
2
+ import { DatePicker, Space, Input, Button, Select } from '@douyinfe/semi-ui';
3
+ import { Position } from '@douyinfe/semi-foundation/tooltip/foundation';
4
+ import { strings } from '@douyinfe/semi-foundation/tooltip/constants';
5
+ import * as dateFns from 'date-fns';
6
+
7
+ /**
8
+ * Test with Chromatic
9
+ */
10
+ export default function App() {
11
+ const spacing = [200, 400];
12
+ const [date, setDate] = React.useState();
13
+ const [insetInput, setInsetInput] = React.useState(true);
14
+ const [position, setPosition] = React.useState('leftTopOver');
15
+ const [needConfirm, setNeedConfirm] = React.useState(false);
16
+ const [density, setDensity] = React.useState(true);
17
+
18
+ const props = {
19
+ defaultOpen: true,
20
+ motion: false,
21
+ insetInput,
22
+ defaultPickerValue: '2021-12-01',
23
+ position,
24
+ density: density ? 'compact' : 'default',
25
+ autoAdjustOverflow: false,
26
+ };
27
+
28
+ const positionOptionList = strings.POSITION_SET.map((position) => ({
29
+ value: position,
30
+ label: position,
31
+ key: position,
32
+ }));
33
+
34
+ const triggerRender = ({ placeholder }) => {
35
+ const format = 'Pp';
36
+ const value = (date && dateFns.format(date, format)) || placeholder;
37
+ return <Input value={value} readOnly />;
38
+ };
39
+
40
+ const handleDateChange = (date) => {
41
+ setDate(date);
42
+ };
43
+
44
+ const handleBtnClick = () => {
45
+ setInsetInput(!insetInput);
46
+ };
47
+
48
+ const handleReset = () => {
49
+ setInsetInput(true);
50
+ setPosition('leftTopOver');
51
+ };
52
+
53
+ const handleToggleNeedConfirm = () => {
54
+ setNeedConfirm(!needConfirm);
55
+ };
56
+
57
+ const handleToggleDensity = () => {
58
+ setDensity(!density);
59
+ };
60
+
61
+ return (
62
+ <div style={{ height: '200vh' }}>
63
+ <div style={{ marginBottom: 12 }}>
64
+ <Space>
65
+ <Button onClick={handleBtnClick}>
66
+ {`insetInput=${insetInput}`}
67
+ </Button>
68
+ <Button onClick={handleToggleNeedConfirm}>
69
+ {`needConfirm=${needConfirm}`}
70
+ </Button>
71
+ <Button onClick={handleToggleDensity}>
72
+ {`小尺寸=${density}`}
73
+ </Button>
74
+ <Select
75
+ style={{ width: 200 }}
76
+ optionList={positionOptionList}
77
+ placeholder='选择position'
78
+ value={position}
79
+ onChange={setPosition}
80
+ showClear
81
+ />
82
+ <Button onClick={handleReset} theme="solid">reset</Button>
83
+ </Space>
84
+ </div>
85
+ <Space wrap spacing={spacing}>
86
+ <DatePicker placeholder='选择单个日期' {...props} />
87
+ <DatePicker placeholder='选择月' {...props} type='month' />
88
+ <DatePicker placeholder='选择日期时间' {...props} type='dateTime' needConfirm={needConfirm} />
89
+ <DatePicker placeholder='选择日期范围' {...props} type='dateRange' />
90
+ <DatePicker placeholder='选择日期时间范围' {...props} type='dateTimeRange' needConfirm={needConfirm} />
91
+ <DatePicker placeholder='format=Pp' {...props} format='yyyy-MM-dd HH:mm:ss' type='dateTimeRange' />
92
+ </Space>
93
+ </div>
94
+ );
95
+ }
96
+
97
+ App.parameters = {
98
+ chromatic: {
99
+ disableSnapshot: false,
100
+ delay: 3000,
101
+ viewports: [1800]
102
+ },
103
+ };
104
+ App.storyName = 'inset input';
@@ -0,0 +1,69 @@
1
+ import React from 'react';
2
+ import { DatePicker, Space, Button } from '@douyinfe/semi-ui';
3
+
4
+ /**
5
+ * Test with Cypress
6
+ * Don't modify DOM structure
7
+ */
8
+ export default function App() {
9
+ const [needConfirm, setNeedConfirm] = React.useState(false);
10
+
11
+ const spacing = [200, 400];
12
+ const props = {
13
+ insetInput: true,
14
+ defaultPickerValue: '2021-12-01',
15
+ motion: false,
16
+ };
17
+
18
+ const handleToggleNeedConfirm = () => {
19
+ setNeedConfirm(!needConfirm);
20
+ };
21
+
22
+ return (
23
+ <div style={{ height: '300vh' }}>
24
+ <div style={{ marginBottom: 12 }}>
25
+ <Space>
26
+ <Button onClick={handleToggleNeedConfirm} data-cy="btn">
27
+ {`needConfirm=${needConfirm}`}
28
+ </Button>
29
+ </Space>
30
+ </div>
31
+ <Space wrap spacing={spacing}>
32
+ <div data-cy="date">
33
+ <DatePicker placeholder="选择单个日期" {...props} />
34
+ </div>
35
+ <div data-cy="month">
36
+ <DatePicker
37
+ {...props}
38
+ placeholder="选择月"
39
+ type="month"
40
+ defaultValue="2021-12"
41
+ timePickerOpts={{
42
+ scrollItemProps: { motion: false },
43
+ }}
44
+ />
45
+ </div>
46
+ <div data-cy="dateTime">
47
+ <DatePicker placeholder="选择日期时间" {...props} type="dateTime" needConfirm={needConfirm} />
48
+ </div>
49
+ <div data-cy="dateRange">
50
+ <DatePicker placeholder="选择日期范围" {...props} type="dateRange" />
51
+ </div>
52
+ <div data-cy="dateTimeRange">
53
+ <DatePicker
54
+ placeholder="选择日期时间范围"
55
+ {...props}
56
+ type="dateTimeRange"
57
+ needConfirm={needConfirm}
58
+ />
59
+ </div>
60
+ <div data-cy="customFormat">
61
+ <DatePicker placeholder="选择日期范围" {...props} type="dateRange" format="Pp" />
62
+ </div>
63
+ </Space>
64
+ </div>
65
+ );
66
+ }
67
+
68
+ App.parameters = { chromatic: { disableSnapshot: true } };
69
+ App.storyName = 'inset input e2e test';
@@ -1,3 +1,5 @@
1
1
  export { default as YearButton } from './YearButton';
2
2
  export { default as PanelOpen } from './PanelOpen';
3
3
  export { default as FixInputRangeFocus } from './FixInputRangeFocus';
4
+ export { default as InsetInput } from './InsetInput';
5
+ export { default as InsetInputE2E } from './InsetInputE2E';
@@ -1,21 +1,28 @@
1
+ /* eslint-disable jsx-a11y/click-events-have-key-events */
2
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
1
3
  /* eslint-disable max-lines-per-function */
2
4
  /* eslint-disable no-unused-vars */
3
5
  import React from 'react';
4
6
  import cls from 'classnames';
5
7
  import PropTypes from 'prop-types';
8
+
6
9
  import DateInputFoundation, {
7
10
  DateInputAdapter,
8
11
  DateInputFoundationProps,
9
- RangeType
12
+ RangeType,
13
+ InsetInputChangeProps,
14
+ InsetInputChangeFoundationProps,
10
15
  } from '@douyinfe/semi-foundation/datePicker/inputFoundation';
11
16
  import { cssClasses, strings } from '@douyinfe/semi-foundation/datePicker/constants';
12
17
  import { noop } from '@douyinfe/semi-foundation/utils/function';
13
18
  import isNullOrUndefined from '@douyinfe/semi-foundation/utils/isNullOrUndefined';
14
- import BaseComponent, { BaseProps } from '../_base/baseComponent';
15
- import Input from '../input/index';
16
19
  import { IconCalendar, IconCalendarClock, IconClear } from '@douyinfe/semi-icons';
17
20
  import { BaseValueType, ValueType } from '@douyinfe/semi-foundation/datePicker/foundation';
18
21
 
22
+ import BaseComponent, { BaseProps } from '../_base/baseComponent';
23
+ import Input from '../input/index';
24
+ import { InsetDateInput, InsetTimeInput } from './insetInput';
25
+
19
26
  export interface DateInputProps extends DateInputFoundationProps, BaseProps {
20
27
  insetLabel?: React.ReactNode;
21
28
  prefix?: React.ReactNode;
@@ -25,9 +32,10 @@ export interface DateInputProps extends DateInputFoundationProps, BaseProps {
25
32
  onBlur?: (e: React.MouseEvent<HTMLInputElement>) => void;
26
33
  onFocus?: (e: React.MouseEvent<HTMLInputElement>, rangeType?: RangeType) => void;
27
34
  onClear?: (e: React.MouseEvent<HTMLDivElement>) => void;
35
+ onInsetInputChange?: (options: InsetInputChangeProps) => void;
36
+ value?: Date[];
28
37
  }
29
38
 
30
-
31
39
  // eslint-disable-next-line @typescript-eslint/ban-types
32
40
  export default class DateInput extends BaseComponent<DateInputProps, {}> {
33
41
  static propTypes = {
@@ -40,7 +48,6 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
40
48
  value: PropTypes.array,
41
49
  disabled: PropTypes.bool,
42
50
  type: PropTypes.oneOf(strings.TYPE_SET),
43
- multiple: PropTypes.bool,
44
51
  showClear: PropTypes.bool,
45
52
  format: PropTypes.string, // Attributes not used
46
53
  inputStyle: PropTypes.object,
@@ -55,6 +62,8 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
55
62
  rangeInputStartRef: PropTypes.object,
56
63
  rangeInputEndRef: PropTypes.object,
57
64
  rangeSeparator: PropTypes.string,
65
+ insetInput: PropTypes.bool,
66
+ insetInputValue: PropTypes.object,
58
67
  };
59
68
 
60
69
  static defaultProps = {
@@ -65,7 +74,6 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
65
74
  onBlur: noop,
66
75
  onClear: noop,
67
76
  onFocus: noop,
68
- multiple: false,
69
77
  type: 'date',
70
78
  inputStyle: {},
71
79
  inputReadOnly: false,
@@ -92,6 +100,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
92
100
  notifyRangeInputClear: (...args) => this.props.onRangeClear(...args),
93
101
  notifyRangeInputFocus: (...args) => this.props.onFocus(...args),
94
102
  notifyTabPress: (...args) => this.props.onRangeEndTabPress(...args),
103
+ notifyInsetInputChange: options => this.props.onInsetInputChange(options),
95
104
  };
96
105
  }
97
106
 
@@ -140,6 +149,10 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
140
149
  this.handleRangeInputFocus(e, 'rangeStart');
141
150
  };
142
151
 
152
+ handleInsetInputChange = (options: InsetInputChangeFoundationProps) => {
153
+ this.foundation.handleInsetInputChange(options);
154
+ };
155
+
143
156
  getRangeInputValue = (rangeStart: string, rangeEnd: string) => {
144
157
  const { rangeSeparator } = this.props;
145
158
  const rangeInputValue = `${rangeStart}${rangeSeparator}${rangeEnd}`;
@@ -177,9 +190,12 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
177
190
  const allowClear = (rangeStart || rangeEnd) && showClear;
178
191
  return allowClear && !disabled ? (
179
192
  <div
193
+ role="button"
194
+ tabIndex={0}
195
+ aria-label="Clear range input value"
180
196
  className={`${prefixCls}-range-input-clearbtn`}
181
197
  onMouseDown={e => !disabled && this.handleRangeInputClear(e)}>
182
- <IconClear />
198
+ <IconClear aria-hidden />
183
199
  </div>
184
200
  ) : null;
185
201
  }
@@ -223,11 +239,11 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
223
239
  const rangePlaceholder = Array.isArray(placeholder) ? placeholder : [placeholder, placeholder];
224
240
  const [rangeStartPlaceholder, rangeEndPlaceholder] = rangePlaceholder;
225
241
  const inputLeftWrapperCls = cls(`${prefixCls}-range-input-wrapper-start`, `${prefixCls}-range-input-wrapper`, {
226
- [`${prefixCls}-range-input-wrapper-active`]: rangeInputFocus === 'rangeStart',
227
- [`${prefixCls}-range-input-wrapper-start-with-prefix`]: this.props.prefix || this.props.insetLabel
242
+ [`${prefixCls}-range-input-wrapper-active`]: rangeInputFocus === 'rangeStart' && !disabled,
243
+ [`${prefixCls}-range-input-wrapper-start-with-prefix`]: this.props.prefix || this.props.insetLabel,
228
244
  });
229
245
  const inputRightWrapperCls = cls(`${prefixCls}-range-input-wrapper-end`, `${prefixCls}-range-input-wrapper`, {
230
- [`${prefixCls}-range-input-wrapper-active`]: rangeInputFocus === 'rangeEnd'
246
+ [`${prefixCls}-range-input-wrapper-active`]: rangeInputFocus === 'rangeEnd' && !disabled,
231
247
  });
232
248
  return (
233
249
  <>
@@ -279,7 +295,72 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
279
295
  );
280
296
  }
281
297
 
282
- render() {
298
+ renderInputInset() {
299
+ const {
300
+ type,
301
+ handleInsetDateFocus,
302
+ handleInsetTimeFocus,
303
+ value,
304
+ insetInputValue,
305
+ prefixCls,
306
+ rangeInputStartRef,
307
+ rangeInputEndRef,
308
+ density,
309
+ } = this.props;
310
+
311
+ const _isRangeType = type.includes('Range');
312
+ const newInsetInputValue = this.foundation.getInsetInputValue({ value, insetInputValue });
313
+ const { datePlaceholder, timePlaceholder } = this.foundation.getInsetInputPlaceholder();
314
+
315
+ const insetInputWrapperCls = `${prefixCls}-inset-input-wrapper`;
316
+ const separatorCls = `${prefixCls}-inset-input-separator`;
317
+
318
+ return (
319
+ <div className={insetInputWrapperCls} x-type={type}>
320
+ <InsetDateInput
321
+ forwardRef={rangeInputStartRef}
322
+ insetInputValue={newInsetInputValue}
323
+ placeholder={datePlaceholder}
324
+ valuePath={'monthLeft.dateInput'}
325
+ onChange={this.handleInsetInputChange}
326
+ onFocus={e => handleInsetDateFocus(e, 'rangeStart')}
327
+ />
328
+ <InsetTimeInput
329
+ disabled={!newInsetInputValue.monthLeft.dateInput}
330
+ insetInputValue={newInsetInputValue}
331
+ placeholder={timePlaceholder}
332
+ type={type}
333
+ valuePath={'monthLeft.timeInput'}
334
+ onChange={this.handleInsetInputChange}
335
+ onFocus={handleInsetTimeFocus}
336
+ />
337
+ {_isRangeType && (
338
+ <>
339
+ <div className={separatorCls}>{density === 'compact' ? null : '-'}</div>
340
+ <InsetDateInput
341
+ forwardRef={rangeInputEndRef}
342
+ insetInputValue={newInsetInputValue}
343
+ placeholder={datePlaceholder}
344
+ valuePath={'monthRight.dateInput'}
345
+ onChange={this.handleInsetInputChange}
346
+ onFocus={e => handleInsetDateFocus(e, 'rangeEnd')}
347
+ />
348
+ <InsetTimeInput
349
+ disabled={!newInsetInputValue.monthRight.dateInput}
350
+ insetInputValue={newInsetInputValue}
351
+ placeholder={timePlaceholder}
352
+ type={type}
353
+ valuePath={'monthRight.timeInput'}
354
+ onChange={this.handleInsetInputChange}
355
+ onFocus={handleInsetTimeFocus}
356
+ />
357
+ </>
358
+ )}
359
+ </div>
360
+ );
361
+ }
362
+
363
+ renderTriggerInput() {
283
364
  const {
284
365
  placeholder,
285
366
  type,
@@ -293,6 +374,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
293
374
  validateStatus,
294
375
  block,
295
376
  prefixCls,
377
+ multiple, // Whether to allow multiple values for email and file types
296
378
  dateFnsLocale, // No need to pass to input
297
379
  onBlur,
298
380
  onClear,
@@ -308,10 +390,12 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
308
390
  onRangeEndTabPress,
309
391
  rangeInputFocus,
310
392
  rangeSeparator,
393
+ insetInput,
394
+ insetInputValue,
311
395
  ...rest
312
396
  } = this.props;
313
- const dateIcon = <IconCalendar />;
314
- const dateTimeIcon = <IconCalendarClock />;
397
+ const dateIcon = <IconCalendar aria-hidden />;
398
+ const dateTimeIcon = <IconCalendarClock aria-hidden />;
315
399
  const suffix = type.includes('Time') ? dateTimeIcon : dateIcon;
316
400
  let text = '';
317
401
 
@@ -355,4 +439,9 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
355
439
  />
356
440
  );
357
441
  }
442
+
443
+ render() {
444
+ const { insetInput } = this.props;
445
+ return insetInput ? this.renderInputInset() : this.renderTriggerInput();
446
+ }
358
447
  }