浏览代码

Merge branch 'release' into js2css

代强 3 年之前
父节点
当前提交
557fd13490

+ 45 - 10
content/input/datepicker/index-en-US.md

@@ -608,6 +608,40 @@ class App extends React.Component {
 }
 ```
 
+When `type` contains `range`, dates can be disabled based on the focus state. The focus state is passed through the `rangeInputFocus` parameter in `options`.
+
+```jsx live=true
+import React from 'react';
+import { DatePicker } from '@douyinfe/semi-ui';
+import * as dateFns from 'date-fns';
+
+function App() {
+    const today = new Date();
+    const disabledDate = (date, options) => {
+        const { rangeInputFocus } = options;
+        const baseDate = dateFns.set(today, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 });
+        if (rangeInputFocus === 'rangeStart') {
+            const disabledStart = dateFns.subDays(baseDate, 2);
+            const disabledEnd = dateFns.addDays(baseDate, 2);
+            return disabledStart <= date && date <= disabledEnd;
+        } else if (rangeInputFocus === 'rangeEnd') {
+            const disabledStart = dateFns.subDays(baseDate, 3);
+            const disabledEnd = dateFns.addDays(baseDate, 3);
+            return disabledStart <= date && date <= disabledEnd;
+        } else {
+            return false;
+        }
+    };
+
+    return (
+        <div>
+            <h4>{`Start date disables 2 days before and 2 days after today, end date disables 3 days before and 3 days after today`}</h4>
+            <DatePicker motion={false} type='dateRange' disabledDate={disabledDate} defaultPickerValue={today} />
+        </div>
+    );
+}
+```
+
 ### Custom Display Format
 
 Pass parameter `format` to custom display format.
@@ -831,19 +865,19 @@ function Demo() {
 
 ## API Reference
 
-| Properties         | Instructions                                                                                                                                                                              | Type                                             | Default | Version    |
-|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|---------|------------|
-| autoAdjustOverflow | Whether the floating layer automatically adjusts its direction when it is blocked                                                                                                         | boolean                                          | true    | **0.34.0** |
-| autoFocus          | Automatic access to focus                                                                                                                                                                 | boolean                                          | false   | **1.10.0** |
-| autoSwitchDate     | When the year and month are changed through the left and right buttons and the drop-down menu at the top of the panel, the date is automatically switched. Only valid for `date` type. | boolean                                          | true    | **1.13.0** |
-| bottomSlot         | Render the bottom extra area                                                                                                                                                              | ReactNode                                        |         | **1.22.0** |
-| className          | Class name                                                                                                                                                                                | string                                           | -       |            |
-| defaultOpen        | Panel displays or hides by default                                                                                                                                                        | boolean                                          | false   |            |
-| defaultPickerValue | Default panel date                                                                                                                                                                        | ValueType |         |            |
+| Properties         | Instructions                                                                                                                                                                           | Type      | Default | Version    |
+|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|---------|------------|
+| autoAdjustOverflow | Whether the floating layer automatically adjusts its direction when it is blocked                                                                                                      | boolean   | true    | **0.34.0** |
+| autoFocus          | Automatic access to focus                                                                                                                                                              | boolean   | false   | **1.10.0** |
+| autoSwitchDate     | When the year and month are changed through the left and right buttons and the drop-down menu at the top of the panel, the date is automatically switched. Only valid for `date` type. | boolean   | true    | **1.13.0** |
+| bottomSlot         | Render the bottom extra area                                                                                                                                                           | ReactNode |         | **1.22.0** |
+| className          | Class name                                                                                                                                                                             | string    | -       |            |
+| defaultOpen        | Panel displays or hides by default                                                                                                                                                     | boolean   | false   |            |
+| defaultPickerValue | Default panel date                                                                                                                                                                     | ValueType |         |            |
 | defaultValue       | Default value                                                                                                                                                                            | ValueType                                                                                                                                                                                                    |                                             |                           |  |
 | density            | Density of picker panel, one of `default`, `compact`                                                                 | string                                                                                                                                                                                                    | default                                        | **1.17.0**              |
 | disabled           | Is it disabled?                                                                                                                                                                           | boolean                                                                                                                                                                                                   | false                                                                                 |                           |
-| disabledDate       | The date is prohibited from the judgment method, and the date is prohibited when returned to true. Options parameter supported after 1.9.0 and rangeEnd supported after 1.29            | <ApiType detail='(date: Date, options: { rangeStart: string, rangeEnd: string }) => boolean'>(date, options) => boolean</ApiType>     | () = > false                                                                          |                           |
+| disabledDate       | The date is prohibited from the judgment method, and the date is prohibited when returned to true. Options parameter supported after 1.9.0, rangeEnd supported after 1.29 and rangeInputFocus is supported since 2.22            | <ApiType detail='(date: Date, options: { rangeStart: string, rangeEnd: string, rangeInputFocus: "rangeStart" \| "rangeEnd" \| false }) => boolean'>(date, options) => boolean</ApiType>     | () = > false                                                                          |                           |
 | disabledTime       | Time prohibition configuration, the return value will be transparently passed to [`TimePicker`](/en-US/input/timepicker#API_Reference) as a parameter    | <ApiType detail='(date: Date \| Date[], panelType?: string) => ({ disabledHours:() => number[], disabledMinutes: (hour: number) => number[], disabledSeconds: (hour: number, minute: number) => number[] })'>(date, panelType) => object</ApiType> | () => false        | **0.36.0**                |
 | disabledTimePicker | Disable time selection or not.                                                                                                                                                            | boolean                                                                                                                                                                                                   |                                                                                       | **0.32.0**                |
 | dropdownClassName  | CSS classname for drop-down menu                                                                                                                                                          | string                                                               |                                 | **1.13.0** |
@@ -888,6 +922,7 @@ function Demo() {
 | onOpenChange       | Callback when popup open or close                                                                                                                                 | (isOpen) => void                                                                                                                                                                                 |                                                                                       |                           |
 | onPanelChange      | Callback when the year or date of the panel is switched|  <ApiType detail='(date: DateType \| DateType[], dateStr: StringType \| StringType[])=>void'>(date, dateStr) => void</ApiType>  |  |**1.28.0**|
 | onPresetClick      | Callback when click preset button                                                                          | <ApiType detail='(item: Object, e: Event) => void'>(item, e) => void</ApiType>       |   **1.24.0**                           |
+| yearAndMonthOpts | Other parameters that can be transparently passed to the year-month selector, see details in [ScrollList#API](/zh-CN/show/scrolllist#ScrollItem)|  | object | **2.22.0** |
 
 
 ## Interface Define

+ 48 - 13
content/input/datepicker/index.md

@@ -578,6 +578,40 @@ import * as dateFns from 'date-fns';
 );
 ```
 
+范围选择时,可以根据 focus 状态禁用日期。focus 状态通过 options 中的 rangeInputFocus 参数传递。
+
+```jsx live=true
+import React from 'react';
+import { DatePicker } from '@douyinfe/semi-ui';
+import * as dateFns from 'date-fns';
+
+function App() {
+    const today = new Date();
+    const disabledDate = (date, options) => {
+        const { rangeInputFocus } = options;
+        const baseDate = dateFns.set(today, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 });
+        if (rangeInputFocus === 'rangeStart') {
+            const disabledStart = dateFns.subDays(baseDate, 2);
+            const disabledEnd = dateFns.addDays(baseDate, 2);
+            return disabledStart <= date && date <= disabledEnd;
+        } else if (rangeInputFocus === 'rangeEnd') {
+            const disabledStart = dateFns.subDays(baseDate, 3);
+            const disabledEnd = dateFns.addDays(baseDate, 3);
+            return disabledStart <= date && date <= disabledEnd;
+        } else {
+            return false;
+        }
+    };
+
+    return (
+        <div>
+            <h4>{`开始日期禁用今天前2日和后2日,结束日期禁用今天前3天和后3天`}</h4>
+            <DatePicker motion={false} type='dateRange' disabledDate={disabledDate} defaultPickerValue={today} />
+        </div>
+    );
+}
+```
+
 ### 自定义显示格式
 
 可以通过 `format` 自定义显示格式
@@ -794,19 +828,19 @@ function Demo() {
 
 ## API 参考
 
-| 属性 | 说明 | 类型 | 默认值 | 版本 |
-| --- | --- | --- | --- | --- |
-| autoAdjustOverflow | 浮层被遮挡时是否自动调整方向 | boolean | true | **0.34.0** |
-| autoFocus | 自动获取焦点 | boolean | false | **1.10.0** |
-| autoSwitchDate | 通过面板上方左右按钮、下拉菜单更改年月时,自动切换日期。仅对 date type 生效。 | boolean | true | **1.13.0** |
-| bottomSlot | 渲染底部额外区域 | ReactNode |  | **1.22.0** |
-| className | 类名 | string | - |  |
-| defaultOpen | 面板默认显示或隐藏 | boolean | false |  |
-| defaultPickerValue | 默认面板日期 | ValueType |  |  |
-| defaultValue | 默认值 | ValueType |  |  |
-| density | 面板的尺寸,可选值:`default`, `compact` | string | default | **1.17.0** |
-| disabled | 是否禁用 | boolean | false |  |
-| disabledDate | 日期禁止判断方法,返回为 true 时禁止该日期,options 参数 1.9.0 后支持,rangeEnd 1.29 后支持 | <ApiType detail='(date: Date, options: { rangeStart: string, rangeEnd: string }) => boolean'>(date, options) => boolean</ApiType> | () => false   |
+| 属性               | 说明                                                                      | 类型      | 默认值  | 版本       |
+|--------------------|-------------------------------------------------------------------------|-----------|---------|------------|
+| autoAdjustOverflow | 浮层被遮挡时是否自动调整方向                                              | boolean   | true    | **0.34.0** |
+| autoFocus          | 自动获取焦点                                                              | boolean   | false   | **1.10.0** |
+| autoSwitchDate     | 通过面板上方左右按钮、下拉菜单更改年月时,自动切换日期。仅对 date type 生效。 | boolean   | true    | **1.13.0** |
+| bottomSlot         | 渲染底部额外区域                                                          | ReactNode |         | **1.22.0** |
+| className          | 类名                                                                      | string    | -       |            |
+| defaultOpen        | 面板默认显示或隐藏                                                        | boolean   | false   |            |
+| defaultPickerValue | 默认面板日期                                                              | ValueType |         |            |
+| defaultValue       | 默认值                                                                    | ValueType |         |            |
+| density            | 面板的尺寸,可选值:`default`, `compact`                                    | string    | default | **1.17.0** |
+| disabled           | 是否禁用                                                                  | boolean   | false   |            |
+| disabledDate | 日期禁止判断方法,返回为 true 时禁止该日期,options 参数 1.9.0 后支持,其中 rangeEnd 1.29 后支持,rangeInputFocus 2.22 后支持 | <ApiType detail='(date: Date, options: { rangeStart: string, rangeEnd: string, rangeInputFocus: "rangeStart" \| "rangeEnd" \| false }) => boolean'>(date, options) => boolean</ApiType> | () => false   |
 | disabledTime | 时间禁止配置,返回值将会作为参数透传给 [`TimePicker`](/zh-CN/input/timepicker#API_参考) | <ApiType detail='(date: Date \| Date[], panelType?: string) => ({ disabledHours:() => number[], disabledMinutes: (hour: number) => number[], disabledSeconds: (hour: number, minute: number) => number[] })'>(date, panelType) => object</ApiType> | () => false | **0.36.0** |
 | disabledTimePicker | 是否禁止时间选择 | boolean |  | **0.32.0** |
 | dropdownClassName | 下拉列表的 CSS 类名 | string |  | **1.13.0** |
@@ -858,6 +892,7 @@ function Demo() {
 | onOpenChange | 面板显示或隐藏状态切换的回调 | <ApiType detail='(isOpen: boolean) => void'>(isOpen) => void</ApiType> |  |  |
 | onPanelChange | 切换面板的年份或者日期时的回调 | <ApiType detail='(date: DateType \| DateType[], dateStr: StringType \| StringType[])=>void'>(date, dateStr) => void</ApiType> | function | **1.28.0** |
 | onPresetClick | 点击快捷选择按钮的回调 | <ApiType detail='(item: Object, e: Event) => void'>(item, e) => void</ApiType> | () => {}  | **1.24.0** |
+| yearAndMonthOpts | 其他可以透传给年月选择器的参数,详见 [ScrollList#API](/zh-CN/show/scrolllist#ScrollItem)|  | object | **2.20.0** |
 
 ## 类型定义
 

+ 14 - 0
content/input/timepicker/index-en-US.md

@@ -29,6 +29,20 @@ function Demo() {
 }
 ```
 
+### Infinite Scroll
+
+Starting from version V2.22.0, we changed the default mode of ScrollItem in TimePicker from `wheel` to `normal`. If you want to apply the effect of infinite scrolling back, please refer to the following example.
+
+```jsx live=true
+import React from 'react';
+import { TimePicker } from '@douyinfe/semi-ui';
+
+function Demo() {
+    return <TimePicker scrollItemProps={{ mode: "wheel", cycled: true }}/>;
+}
+```
+
+
 ### With an Embedded Label
 
 ```jsx live=true

+ 13 - 0
content/input/timepicker/index.md

@@ -28,6 +28,19 @@ function Demo() {
 }
 ```
 
+### 无限滚动
+
+版本V2.22.0开始,我们将 TimePicker 内的 ScrollItem 的默认模式从 `wheel` 变更为了 `normal`, 若想应用回无限滚动的效果,可参考以下示例。
+
+```jsx live=true
+import React from 'react';
+import { TimePicker } from '@douyinfe/semi-ui';
+
+function Demo() {
+    return <TimePicker scrollItemProps={{ mode: "wheel", cycled: true }}/>;
+}
+```
+
 ### 带内嵌标签
 
 ```jsx live=true

+ 11 - 10
content/show/scrolllist/index-en-US.md

@@ -123,16 +123,17 @@ class ScrollListDemo extends React.Component {
 
 ### ScrollItem
 
-| Properties  | Instructions                                                                         | type                                  | Default |
-| ----------- | ------------------------------------------------------------------------------------ |---------------------------------------| ------- |
-| cycled      | Whether it is an infinite loop, effective only if the mode is "wheel"                | boolean                               | false   |
-| className   | classname of scroll item                | string                                | ''   |
-| list        | List content                                                                         | [Item Data](#ItemData)[]              | []      |
-| motion      | Whether to start the scroll animation                                                | boolean                               | true    |
-| onSelect    | Select callback                                                                      | (data: [ItemData](#ItemData)) => void | NOOP    |
-| selectIndex | Index of selected items                                                              | number                                | 0       |
-| style | Inline style                                                              | CSSProperties                         | {}       |
-| transform   | For the Transformation of the selected item, the return value is displayed as a copy | (value: any, text: string) => string  | v = > v |
+| Properties  | Instructions                                                                         | type                     | Default |
+| ----------- | ------------------------------------------------------------------------------------ | ------------------------ | ------- |
+| cycled      | Whether it is an infinite loop, effective only if the mode is "wheel"                | boolean                  | false   |
+| className   | classname of scroll item                | string                  | ''   |
+| list        | List content                                                                         | [Item Data](#ItemData)[] | []      |
+| mode        | mode selection                                                                       | "normal" \| "wheel"      | "wheel"|
+| motion      | Whether to start the scroll animation                                                | Motion                  | true    |
+| onSelect    | Select callback                                                                      | (data: [ItemData](#ItemData)) => void                 | NOOP    |
+| selectIndex | Index of selected items                                                              | number                   | 0       |
+| style | Inline style                                                              | CSSProperties                   | {}       |
+| transform   | For the Transformation of the selected item, the return value is displayed as a copy | (value: any, text: string) => string                 | v = > v |
 
 #### ItemData
 

+ 11 - 10
content/show/scrolllist/index.md

@@ -145,16 +145,17 @@ class ScrollListDemo extends React.Component {
 
 ### ScrollItem
 
-| 属性        | 说明                                        | 类型                                    | 默认值 |
-| ----------- | ------------------------------------------- |---------------------------------------| ------ |
-| className   | 样式类名 | string                                | ''  |
-| cycled      | 是否为无限循环,仅在 mode 为 "wheel" 时生效 | boolean                               | false  |
-| list        | 列表内容                                    | [ItemData](#ItemData)[]               | []     |
-| motion      | 是否开启滚动动画                            | boolean                               | true   |
-| onSelect    | 选中回调                                    | (data: [ItemData](#ItemData)) => void | NOOP   |
-| selectIndex | 选中项的索引                                | number                                | 0      |
-| style       | 内联样式                                   | CSSProperties                         | {}      |
-| transform   | 对选中项的变换,返回值会作为文案进行显示    | (value: any, text: string) => string  | v => v |
+| 属性        | 说明                                                | 类型                                | 默认值 |
+| ----------- | -------------------------------------------------- | ----------------------------------- | ------ |
+| className   | 样式类名 | string                                   | ''                                  |
+| cycled      | 是否为无限循环,仅在 mode 为 "wheel" 时生效 | boolean  | false                                |
+| list        | 列表内容                                            | [ItemData](#ItemData)[]              | []     |
+| mode        | 模式选择                                            | "normal" \| "wheel"                  | "wheel"|
+| motion      | 是否开启滚动动画                                     | Motion                                | true   |
+| onSelect    | 选中回调                                            | (data: [ItemData](#ItemData)) => void| NOOP   |
+| selectIndex | 选中项的索引                                         | number                               | 0      |
+| style       | 内联样式                                            | CSSProperties                        | {}      |
+| transform   | 对选中项的变换,返回值会作为文案进行显示                  | (value: any, text: string) => string | v => v |
 
 #### ItemData
 

+ 12 - 0
cypress/integration/datePicker.spec.js

@@ -572,4 +572,16 @@ describe('DatePicker', () => {
         cy.get('.semi-popover .semi-datepicker-day-selected-start').contains('8');
         cy.get('.semi-popover .semi-datepicker-day-selected-end').contains('9');
     });
+
+    it('disabledDate by focus state', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--disabled-range&args=&viewMode=story');
+        cy.get('.semi-input').eq(0).click();
+        cy.get('.semi-datepicker-month-grid-left .semi-datepicker-day.semi-datepicker-day-disabled').should('have.length', 5);
+        cy.get('.semi-datepicker-month-grid-left .semi-datepicker-day.semi-datepicker-day-disabled').eq(0).contains('18');
+        cy.get('.semi-datepicker-month-grid-left .semi-datepicker-day.semi-datepicker-day-disabled').eq(4).contains('22');
+        cy.get('.semi-input').eq(1).click();
+        cy.get('.semi-datepicker-month-grid-left .semi-datepicker-day.semi-datepicker-day-disabled').should('have.length', 7);
+        cy.get('.semi-datepicker-month-grid-left .semi-datepicker-day.semi-datepicker-day-disabled').eq(0).contains('17');
+        cy.get('.semi-datepicker-month-grid-left .semi-datepicker-day.semi-datepicker-day-disabled').eq(6).contains('23');
+    });
 });

+ 10 - 0
packages/semi-foundation/datePicker/datePicker.scss

@@ -2,12 +2,22 @@
 @import "./variables.scss";
 
 $module: #{$prefix}-datepicker;
+$module-list: #{$prefix}-scrolllist;
 
 .#{$module} {
     box-sizing: border-box;
     display: inline-block;
     vertical-align: top;
 
+    .#{$module-list}-body {
+
+        .#{$module-list}-item {
+            &::-webkit-scrollbar {
+                display: none;
+            }
+        }
+    }
+
     // 双月网格
 
     &-month-grid {

+ 9 - 4
packages/semi-foundation/datePicker/foundation.ts

@@ -36,11 +36,15 @@ export type DayStatusType = {
     isHover?: boolean; // Date between selection and hover date
     isOffsetRangeStart?: boolean; // Week selection start
     isOffsetRangeEnd?: boolean; // End of week selection
-    isHoverInOffsetRange?: boolean; // Hover in the week selection
+    isHoverInOffsetRange?: boolean // Hover in the week selection
 };
 export type DisabledDateOptions = {
     rangeStart?: string;
-    rangeEnd?: string
+    rangeEnd?: string;
+    /**
+     * current select of range type
+     */
+    rangeInputFocus?: 'rangeStart' | 'rangeEnd' | false
 };
 export type PresetType = {
     start?: string | Date | number;
@@ -208,7 +212,7 @@ export interface DatePickerAdapter extends DefaultAdapter<DatePickerFoundationPr
     setInsetInputFocus: () => void;
     setTriggerDisabled: (disabled: boolean) => void
 }
-
+ 
 
 /**
  * The datePicker foundation.js is responsible for maintaining the date value and the input box value, as well as the callback of both
@@ -1185,7 +1189,8 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
      */
     _someDateDisabled(value: Date[]) {
         const stateValue = this.getState('value');
-        const disabledOptions = { rangeStart: '', rangeEnd: '' };
+        const { rangeInputFocus } = this.getStates();
+        const disabledOptions = { rangeStart: '', rangeEnd: '', rangeInputFocus };
 
         // DisabledDate needs to pass the second parameter
         if (this._isRangeType() && Array.isArray(stateValue)) {

+ 2 - 1
packages/semi-foundation/datePicker/yearAndMonthFoundation.ts

@@ -15,7 +15,8 @@ export interface YearAndMonthFoundationProps {
     density?: string;
     presetPosition?: PresetPosition;
     renderQuickControls?: any;
-    renderDateInput?: any
+    renderDateInput?: any;
+    yearAndMonthOpts?: any
 }
 
 export interface YearAndMonthFoundationState {

+ 1 - 0
packages/semi-foundation/scrollList/scrollList.scss

@@ -126,6 +126,7 @@ $module: #{$prefix}-scrolllist;
 
             .#{$module}-item-selected {
                 font-weight: $font-scrollList_item_wheel_item_selected-fontWeight;
+                color: var(--semi-color-primary) !important;
             }
 
             .#{$module}-list-outer {

+ 6 - 0
packages/semi-foundation/timePicker/timePicker.scss

@@ -24,6 +24,12 @@ $module-list: #{$prefix}-scrolllist;
                     padding-bottom: ($height-timePicker_panel_body - $height-scrollList_item) * 0.5;
                 }
             }
+            .#{$module-list}-item {
+                &::-webkit-scrollbar {
+                    display: none;
+                }
+            }
+
         }
 
         .#{$module-list}-item,

+ 36 - 0
packages/semi-ui/datePicker/_story/v2/DisabledRange.tsx

@@ -0,0 +1,36 @@
+import React from 'react';
+import { DatePicker } from '../../../index';
+import { DisabledDateType } from '../../index';
+import * as dateFns from 'date-fns';
+
+App.storyName = '根据 focus 状态禁用日期';
+/**
+ * test with cy
+ */
+function App() {
+    const today = new Date('2021-10-20');
+    const disabledDate: DisabledDateType = (date, options) => {
+        const { rangeInputFocus } = options;
+        const baseDate = dateFns.set(today, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 });
+        if (rangeInputFocus === 'rangeStart') {
+            const disabledStart = dateFns.subDays(baseDate, 2);
+            const disabledEnd = dateFns.addDays(baseDate, 2);
+            return disabledStart <= date && date <= disabledEnd;
+        } else if (rangeInputFocus === 'rangeEnd') {
+            const disabledStart = dateFns.subDays(baseDate, 3);
+            const disabledEnd = dateFns.addDays(baseDate, 3);
+            return disabledStart <= date && date <= disabledEnd;
+        } else {
+            return false;
+        }
+    };
+
+    return (
+        <div>
+            <h4>{`开始禁用 ${dateFns.format(today, 'yyyy-MM-dd')} 的前 2 日和后 2 日,结束日期禁用当前日期的前 3 天和后 3 天`}</h4>
+            <DatePicker motion={false} type='dateRange' disabledDate={disabledDate} defaultPickerValue={today} />
+        </div>
+    );
+}
+
+export default App;

+ 1 - 0
packages/semi-ui/datePicker/_story/v2/index.js

@@ -9,3 +9,4 @@ export { default as InputFormatDisabled } from './InputFormatDisabled';
 export { default as AutoFillTime } from './AutoFillTime';
 export { default as InputFormatConfirm } from './InputFormatConfirm';
 export { default as FixedTriggerRender } from './FixTriggerRender';
+export { default as DisabledRange } from './DisabledRange';

+ 9 - 3
packages/semi-ui/datePicker/datePicker.tsx

@@ -21,6 +21,7 @@ import YearAndMonth, { YearAndMonthProps } from './yearAndMonth';
 import '@douyinfe/semi-foundation/datePicker/datePicker.scss';
 import { Locale } from '../locale/interface';
 import { TimePickerProps } from '../timePicker/TimePicker';
+import { ScrollItemProps } from '../scrollList/scrollItem';
 import { InsetInputValue, InsetInputChangeProps } from '@douyinfe/semi-foundation/datePicker/inputFoundation';
 
 export interface DatePickerProps extends DatePickerFoundationProps {
@@ -43,7 +44,8 @@ export interface DatePickerProps extends DatePickerFoundationProps {
     onFocus?: (e: React.MouseEvent, rangeType: RangeType) => void;
     onPresetClick?: (item: PresetType, e: React.MouseEvent<HTMLDivElement>) => void;
     locale?: Locale['DatePicker'];
-    dateFnsLocale?: Locale['dateFnsLocale']
+    dateFnsLocale?: Locale['dateFnsLocale'];
+    yearAndMonthOpts?: ScrollItemProps<any>
 }
 
 export type DatePickerState = DatePickerFoundationState;
@@ -127,6 +129,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
         onPanelChange: PropTypes.func,
         rangeSeparator: PropTypes.string,
         preventScroll: PropTypes.bool,
+        yearAndMonthOpts: PropTypes.object
     };
 
     static defaultProps = {
@@ -416,7 +419,8 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
             timeZone,
             triggerRender,
             insetInput,
-            presetPosition
+            presetPosition,
+            yearAndMonthOpts
         } = this.props;
         const { cachedSelectedValue, rangeInputFocus } = this.state;
 
@@ -459,6 +463,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
                 presetPosition={presetPosition}
                 renderQuickControls={this.renderQuickControls()}
                 renderDateInput={this.renderDateInput()}
+                yearAndMonthOpts={yearAndMonthOpts}
             />
         );
     }
@@ -705,7 +710,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
     };
 
     renderYearMonthPanel = (locale: Locale['DatePicker'], localeCode: string) => {
-        const { density, presetPosition } = this.props;
+        const { density, presetPosition, yearAndMonthOpts } = this.props;
 
         const date = this.state.value[0];
         let year = 0;
@@ -730,6 +735,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
                 presetPosition={presetPosition}
                 renderQuickControls={this.renderQuickControls()}
                 renderDateInput={this.renderDateInput()}
+                yearAndMonthOpts={yearAndMonthOpts}
             />
         );
     };

+ 3 - 2
packages/semi-ui/datePicker/month.tsx

@@ -16,7 +16,7 @@ const prefixCls = cssClasses.PREFIX;
 export interface MonthProps extends MonthFoundationProps, BaseProps {
     forwardRef: React.Ref<any>;
     locale: Locale['DatePicker'];
-    focusRecordsRef: React.RefObject<{ rangeStart: boolean; rangeEnd: boolean }>;
+    focusRecordsRef: React.RefObject<{ rangeStart: boolean; rangeEnd: boolean }>
 }
 
 export type MonthState = MonthFoundationState;
@@ -100,8 +100,9 @@ export default class Month extends BaseComponent<MonthProps, MonthState> {
     }
 
     getSingleDayStatus(options: Partial<MonthProps> & { fullDate: string; todayText: string }) {
+        const { rangeInputFocus } = this.props;
         const { fullDate, todayText, selected, disabledDate, rangeStart, rangeEnd } = options;
-        const disabledOptions = { rangeStart, rangeEnd };
+        const disabledOptions = { rangeStart, rangeEnd, rangeInputFocus };
         const isToday = fullDate === todayText;
         const isSelected = selected.has(fullDate);
 

+ 9 - 6
packages/semi-ui/datePicker/monthsGrid.tsx

@@ -20,6 +20,7 @@ import YearAndMonth from './yearAndMonth';
 import { IconClock, IconCalendar } from '@douyinfe/semi-icons';
 import { getDefaultFormatTokenByType } from '@douyinfe/semi-foundation/datePicker/_utils/getDefaultFormatToken';
 import getDefaultPickerDate from '@douyinfe/semi-foundation/datePicker/_utils/getDefaultPickerDate';
+import { ScrollItemProps } from '../scrollList/scrollItem';
 
 const prefixCls = cssClasses.PREFIX;
 
@@ -29,6 +30,7 @@ export interface MonthsGridProps extends MonthsGridFoundationProps, BaseProps {
     renderDate?: () => React.ReactNode;
     renderFullDate?: () => React.ReactNode;
     focusRecordsRef?: React.RefObject<{ rangeStart: boolean; rangeEnd: boolean }>;
+    yearAndMonthOpts?: ScrollItemProps<any>
 }
 
 export type MonthsGridState = MonthsGridFoundationState;
@@ -497,7 +499,7 @@ export default class MonthsGrid extends BaseComponent<MonthsGridProps, MonthsGri
 
     renderYearAndMonth(panelType: PanelType, panelDetail: MonthInfo) {
         const { pickerDate } = panelDetail;
-        const { locale, localeCode, density } = this.props;
+        const { locale, localeCode, density, yearAndMonthOpts } = this.props;
         const y = pickerDate.getFullYear();
         const m = pickerDate.getMonth() + 1;
         return (
@@ -518,6 +520,7 @@ export default class MonthsGrid extends BaseComponent<MonthsGridProps, MonthsGri
                     }
                 }}
                 density={density}
+                yearAndMonthOpts={yearAndMonthOpts}
             />
         );
     }
@@ -558,13 +561,13 @@ export default class MonthsGrid extends BaseComponent<MonthsGridProps, MonthsGri
 
         const switchCls = classnames(`${prefixCls}-switch`);
         const dateCls = classnames({
-            [`${prefixCls }-switch-date`]: true,
-            [`${prefixCls }-switch-date-active`]: !isTimePickerOpen,
+            [`${prefixCls}-switch-date`]: true,
+            [`${prefixCls}-switch-date-active`]: !isTimePickerOpen,
         });
         const timeCls = classnames({
-            [`${prefixCls }-switch-time`]: true,
+            [`${prefixCls}-switch-time`]: true,
             [`${prefixCls}-switch-time-disabled`]: disabledTimePicker,
-            [`${prefixCls }-switch-date-active`]: isTimePickerOpen,
+            [`${prefixCls}-switch-date-active`]: isTimePickerOpen,
         });
         const textCls = classnames(`${prefixCls}-switch-text`);
 
@@ -597,7 +600,7 @@ export default class MonthsGrid extends BaseComponent<MonthsGridProps, MonthsGri
         const { monthLeft, monthRight } = this.state;
         const { type, insetInput, presetPosition, renderQuickControls, renderDateInput } = this.props;
         const monthGridCls = classnames({
-            [`${prefixCls }-month-grid`]: true,
+            [`${prefixCls}-month-grid`]: true,
         });
         const panelTypeLeft = strings.PANEL_TYPE_LEFT;
         const panelTypeRight = strings.PANEL_TYPE_RIGHT;

+ 11 - 9
packages/semi-ui/datePicker/yearAndMonth.tsx

@@ -120,7 +120,7 @@ class YearAndMonth extends BaseComponent<YearAndMonthProps, YearAndMonthState> {
 
     renderColYear() {
         const { years, currentYear, currentMonth } = this.state;
-        const { disabledDate, localeCode, yearCycled } = this.props;
+        const { disabledDate, localeCode, yearCycled, yearAndMonthOpts } = this.props;
         const currentDate = setMonth(Date.now(), currentMonth - 1);
         const list: any[] = years.map(({ value, year }) => ({
             year,
@@ -130,7 +130,7 @@ class YearAndMonth extends BaseComponent<YearAndMonthProps, YearAndMonthState> {
         let transform = (val: string) => val;
         if (localeCode === 'zh-CN' || localeCode === 'zh-TW') {
             // Only Chinese needs to add [year] after the selected year
-            transform = val => `${val }年`;
+            transform = val => `${val}年`;
         }
         return (
             <ScrollItem
@@ -141,6 +141,8 @@ class YearAndMonth extends BaseComponent<YearAndMonthProps, YearAndMonthState> {
                 selectedIndex={years.findIndex(item => item.value === currentYear)}
                 type="year"
                 onSelect={this.selectYear}
+                mode="normal"
+                {...yearAndMonthOpts}
             />
         );
     }
@@ -167,12 +169,13 @@ class YearAndMonth extends BaseComponent<YearAndMonthProps, YearAndMonthState> {
 
     renderColMonth() {
         const { months, currentMonth, currentYear } = this.state;
-        const { locale, localeCode, monthCycled, disabledDate } = this.props;
+        const { locale, localeCode, monthCycled, disabledDate, yearAndMonthOpts } = this.props;
+        console.log('yearAndMonthOpts', yearAndMonthOpts);
         let transform = (val: string) => val;
         const currentDate = setYear(Date.now(), currentYear);
         if (localeCode === 'zh-CN' || localeCode === 'zh-TW') {
             // Only Chinese needs to add [month] after the selected month
-            transform = val => `${val }月`;
+            transform = val => `${val}月`;
         }
         // i18n
         const list: MonthScrollItem[] = months.map(({ value, month }) => ({
@@ -190,6 +193,8 @@ class YearAndMonth extends BaseComponent<YearAndMonthProps, YearAndMonthState> {
                 selectedIndex={selectedIndex}
                 type="month"
                 onSelect={this.selectMonth}
+                mode='normal'
+                {...yearAndMonthOpts}
             />
         );
     }
@@ -223,7 +228,7 @@ class YearAndMonth extends BaseComponent<YearAndMonthProps, YearAndMonthState> {
                 )}
                 {
                     presetPosition ? (
-                        <div style={{ display: 'flex' }}> 
+                        <div style={{ display: 'flex' }}>
                             {presetPosition === "left" && renderQuickControls}
                             <div>
                                 {renderDateInput}
@@ -234,7 +239,7 @@ class YearAndMonth extends BaseComponent<YearAndMonthProps, YearAndMonthState> {
                             </div>
                             {presetPosition === "right" && renderQuickControls}
                         </div>
-                    ) : 
+                    ) :
                         <>
                             {renderDateInput}
                             <ScrollList>
@@ -242,10 +247,7 @@ class YearAndMonth extends BaseComponent<YearAndMonthProps, YearAndMonthState> {
                                 {this.renderColMonth()}
                             </ScrollList>
                         </>
-
-                    
                 }
-             
             </React.Fragment>
         );
     }

+ 1 - 1
packages/semi-ui/scrollList/_story/WheelList/index.jsx

@@ -81,7 +81,7 @@ class ScrollListDemo extends React.Component {
             // mode: 'normal',
             mode: 'wheel',
             cycled: false,
-            motion: false,
+            motion: true,
         };
         return (
             <div>

+ 5 - 5
packages/semi-ui/scrollList/scrollItem.tsx

@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
 import classnames from 'classnames';
 import { noop, debounce, throttle, find, map, findIndex, times } from 'lodash';
 
-import { cssClasses, numbers } from '@douyinfe/semi-foundation/scrollList/constants';
+import { cssClasses, numbers, strings } from '@douyinfe/semi-foundation/scrollList/constants';
 import ItemFoundation, { Item, ScrollItemAdapter } from '@douyinfe/semi-foundation/scrollList/itemFoundation';
 import animatedScrollTo from '@douyinfe/semi-foundation/scrollList/scrollTo';
 import isElement from '@douyinfe/semi-foundation/utils/isElement';
@@ -37,7 +37,7 @@ export interface ScrollItemState {
 }
 export default class ScrollItem<T extends Item> extends BaseComponent<ScrollItemProps<T>, ScrollItemState> {
     static propTypes = {
-        mode: PropTypes.string,
+        mode: PropTypes.oneOf(strings.MODE),
         cycled: PropTypes.bool,
         list: PropTypes.array,
         selectedIndex: PropTypes.number,
@@ -98,7 +98,7 @@ export default class ScrollItem<T extends Item> extends BaseComponent<ScrollItem
         this.debouncedSelect = debounce((e, nearestNode) => {
             this._cacheSelectedNode(nearestNode);
             this.foundation.selectNode(nearestNode, this.list);
-        }, msPerFrame * 5);
+        }, msPerFrame * 2);
     }
 
     get adapter(): ScrollItemAdapter<ScrollItemProps<T>, ScrollItemState, T> {
@@ -113,7 +113,7 @@ export default class ScrollItem<T extends Item> extends BaseComponent<ScrollItem
             scrollToCenter: this.scrollToCenter,
         };
     }
-    componentWillUnmount(){
+    componentWillUnmount() {
         if (this.props.cycled) {
             this.throttledAdjustList.cancel();
             this.debouncedSelect.cancel();
@@ -329,7 +329,7 @@ export default class ScrollItem<T extends Item> extends BaseComponent<ScrollItem
         const { wrapper } = this;
         const wrapperHeight = wrapper.offsetHeight;
         const itemHeight = this.getItmHeight(node);
-        const targetTop = (node.offsetTop || this.list.children.length * itemHeight / 2 ) - (wrapperHeight - itemHeight) / 2;
+        const targetTop = (node.offsetTop || this.list.children.length * itemHeight / 2) - (wrapperHeight - itemHeight) / 2;
 
         this.scrollToPos(targetTop, duration);
     };

+ 5 - 9
packages/semi-ui/timePicker/Combobox.tsx

@@ -195,9 +195,8 @@ class Combobox extends BaseComponent<ComboboxProps, ComboboxState> {
         return (
             <ScrollItem<FormatOptionReturn>
                 ref={current => this.cacheRefCurrent('hour', current)}
-                mode={'wheel'}
+                mode={'normal'}
                 transform={transformHour}
-                cycled={true}
                 className={className}
                 list={hourOptionsAdj.map(option => formatOption(option, disabledOptions))}
                 selectedIndex={hourOptionsAdj.indexOf(hourAdj)}
@@ -226,9 +225,8 @@ class Combobox extends BaseComponent<ComboboxProps, ComboboxState> {
         return (
             <ScrollItem<FormatOptionReturn>
                 ref={current => this.cacheRefCurrent('minute', current)}
-                mode={'wheel'}
+                mode={'normal'}
                 transform={transformMinute}
-                cycled={true}
                 list={minuteOptions.map(option => formatOption(option, disabledOptions))}
                 selectedIndex={minuteOptions.indexOf(minute)}
                 type="minute"
@@ -258,9 +256,8 @@ class Combobox extends BaseComponent<ComboboxProps, ComboboxState> {
         return (
             <ScrollItem<FormatOptionReturn>
                 ref={current => this.cacheRefCurrent('second', current)}
-                mode={'wheel'}
+                mode={'normal'}
                 transform={transformSecond}
-                cycled={true}
                 list={secondOptions.map(option => formatOption(option, disabledOptions))}
                 selectedIndex={secondOptions.indexOf(second)}
                 className={className}
@@ -295,9 +292,8 @@ class Combobox extends BaseComponent<ComboboxProps, ComboboxState> {
         return (
             <ScrollItem<AMPMOptionItem>
                 ref={current => this.cacheRefCurrent('ampm', current)}
-                mode={'wheel'}
+                mode={'normal'}
                 className={className}
-                cycled={false}
                 list={AMPMOptions}
                 selectedIndex={selected}
                 type="ampm"
@@ -319,7 +315,7 @@ class Combobox extends BaseComponent<ComboboxProps, ComboboxState> {
                 {(locale: Locale['TimePicker'], localeCode: Locale['code']) => (
                     <ScrollList
                         header={panelHeader}
-                        footer={panelFooter} 
+                        footer={panelFooter}
                         x-semi-header-alias="panelHeader"
                         x-semi-footer-alias="panelFooter"
                     >