Kaynağa Gözat

Merge pull request #237 from DouyinFE/main

merge main into release
zwlafk 4 yıl önce
ebeveyn
işleme
36789e090d

+ 1 - 1
CONTRIBUTING-en-US.md

@@ -42,7 +42,7 @@ We may ask or help you to modify the PR according to the situation. For inapprop
 ## Help Improve the Docs
 The documentation site is based on [gatsby](https://www.gatsbyjs.com/), and the code is in the `src` directory.
 
-**The component documentation is located in the md file under the `semi-ui` component folder. ** Take tooltip as an example:
+**The component documentation is located in the md file under the `semi-ui` component folder.** Take tooltip as an example:
 
 * Chinese document `packages/semi-ui/tooltip/index.md` 
 * English document `packages/semi-ui/tooltip/index-en-US.md`

+ 9 - 0
content/start/changelog/index-en-US.md

@@ -16,6 +16,15 @@ Version:Major.Minor.Patch
 
 ---
 
+#### 🎉 2.0.4 (2021-11-08)
+- 【Fix】
+   - Fixed Cascader single selection, the problem that the `defaultValue` is disabled when the node is selected will be filtered [#183](https://github.com/DouyinFE/semi-design/issues/183)
+   - Fixed Cascader `multiple` and `onChangeWithObject` enabled, the `defaultValue` is `object[]` does not take effect [#184](https://github.com/DouyinFE/semi-design/issues/184)
+   - Fixed the problem that the Select support cannot automatically scroll to the selected item after opening the drop-down box [#169](https://github.com/DouyinFE/semi-design/issues/169)
+   - Fixed Table `resizable` table issue [#154](https://github.com/DouyinFE/semi-design/issues/154)
+- 【Docs】
+  - Optimized the contributing document [#224](https://github.com/DouyinFE/semi-design/issues/224) [@btea](https://github.com/btea)
+
 #### 🎉 2.0.3 (2021-11-06)
 - 【Fix】
   - Fix the problem that the item height of Tree / TreeSelect becomes larger in the loading state [#181](https://github.com/DouyinFE/semi-design/issues/181)

+ 9 - 0
content/start/changelog/index.md

@@ -15,6 +15,15 @@ Semi 版本号遵循**Semver**规范(主版本号-次版本号-修订版本号
 
 ---
 
+#### 🎉 2.0.4 (2021-11-08)
+- 【Fix】
+  - 修复 Cascader 单选时,defaultValue 为 disabled 节点时选中会被过滤的问题 [#183](https://github.com/DouyinFE/semi-design/issues/183)
+  - 修复 Cascader 多选且开启 onChangeWithObject,defaultValue 为 object[] 没有生效的问题 [#184](https://github.com/DouyinFE/semi-design/issues/184) 
+  - 修复 Select 支持打开下拉框后,无法自动滚动到已选中的项目的问题 [#169](https://github.com/DouyinFE/semi-design/issues/169) 
+  - 修复 Table resizable 表格问题 [#154](https://github.com/DouyinFE/semi-design/issues/154)
+- 【Docs】
+  - 优化了共建文档 [#224](https://github.com/DouyinFE/semi-design/issues/224) [@btea](https://github.com/btea)
+
 #### 🎉 2.0.3 (2021-11-06)
 - 【Fix】
   - 修复 Tree / TreeSelect 在 loading 状态下 item 高度变大的问题 [#181](https://github.com/DouyinFE/semi-design/issues/181)

+ 1 - 1
lerna.json

@@ -1,5 +1,5 @@
 {
     "useWorkspaces": true,
     "npmClient": "yarn",
-    "version": "2.0.3"
+    "version": "2.0.4"
 }

+ 1 - 1
package.json

@@ -37,7 +37,7 @@
   "dependencies": {
     "@douyinfe/semi-site-banner": "0.0.1",
     "@douyinfe/semi-site-doc-style": "0.0.1",
-    "@douyinfe/semi-site-header": "0.0.1",
+    "@douyinfe/semi-site-header": "0.0.2",
     "@douyinfe/semi-site-markdown-blocks": "0.0.1",
     "@mdx-js/react": "^1.6.22",
     "@svgr/core": "^5.5.0",

+ 3 - 3
packages/semi-animation-react/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation-react",
-  "version": "2.0.3",
+  "version": "2.0.4",
   "description": "motion library for semi-ui-react",
   "keywords": [
     "motion",
@@ -26,8 +26,8 @@
   },
   "dependencies": {
     "@babel/runtime-corejs3": "^7.15.4",
-    "@douyinfe/semi-animation": "2.0.3",
-    "@douyinfe/semi-animation-styled": "2.0.3",
+    "@douyinfe/semi-animation": "2.0.4",
+    "@douyinfe/semi-animation-styled": "2.0.4",
     "classnames": "^2.2.6"
   },
   "peerDependencies": {

+ 1 - 1
packages/semi-animation-styled/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation-styled",
-  "version": "2.0.3",
+  "version": "2.0.4",
   "description": "semi styled animation",
   "keywords": [
     "semi",

+ 1 - 1
packages/semi-animation/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation",
-  "version": "2.0.3",
+  "version": "2.0.4",
   "description": "animation base library for semi-ui",
   "keywords": [
     "animation",

+ 5 - 2
packages/semi-foundation/cascader/foundation.ts

@@ -414,9 +414,12 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
 
         if (selectedKeys.length) {
             const selectedKey = selectedKeys[0];
-            const isOptionDisabled = this._isOptionDisabled(selectedKey, keyEntities);
             const selectedItem = keyEntities[selectedKey];
-            if (!isOptionDisabled && (changeOnSelect || this._isLeaf(selectedItem.data))) {
+            /**
+             * When changeOnSelect is turned on, or the target option is a leaf option,
+             * the option is considered to be selected, even if the option is disabled
+             */
+            if (changeOnSelect || this._isLeaf(selectedItem.data)) {
                 updateStates.selectedKeys = new Set([selectedKey]);
                 if (!loadingActive.length) {
                     updateStates.activeKeys = new Set(selectedItem.path);

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

@@ -60,6 +60,7 @@ $module: #{$prefix}-input;
 
     &:hover {
         background-color: $color-input_default-bg-hover;
+        border: $color-input_default-border-hover;
     }
 
     // &:active {

+ 2 - 2
packages/semi-foundation/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-foundation",
-    "version": "2.0.3",
+    "version": "2.0.4",
     "description": "",
     "files": [
         "lib",
@@ -12,7 +12,7 @@
     },
     "dependencies": {
         "@babel/runtime-corejs3": "^7.15.4",
-        "@douyinfe/semi-animation": "2.0.3",
+        "@douyinfe/semi-animation": "2.0.4",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "date-fns": "^2.9.0",

+ 5 - 0
packages/semi-foundation/select/foundation.ts

@@ -43,6 +43,7 @@ export interface SelectAdapter<P = Record<string, any>, S = Record<string, any>>
     notifyMouseLeave(event: any): void;
     notifyMouseEnter(event: any): void;
     updateHovering(isHover: boolean): void;
+    updateScrollTop(): void;
 }
 type PropValue = string | number | Record<string, any>;
 
@@ -926,4 +927,8 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
             }
         }
     }
+
+    updateScrollTop() {
+        this._adapter.updateScrollTop();
+    }
 }

+ 11 - 4
packages/semi-foundation/table/foundation.ts

@@ -102,7 +102,14 @@ export interface TableAdapter<RecordType> extends DefaultAdapter {
 }
 
 class TableFoundation<RecordType> extends BaseFoundation<TableAdapter<RecordType>> {
-    memoizedWithFnsColumns: (queries: BaseColumnProps<RecordType>[], cachedColumns: BaseColumnProps<RecordType>[]) => BaseColumnProps<RecordType>[];
+    memoizedWithFnsColumns: (
+        queries: BaseColumnProps<RecordType>[], 
+        cachedColumns: BaseColumnProps<RecordType>[], 
+        rowSelectionUpdate: boolean, 
+        hideExpandedColumn: boolean,
+        scrollbarColumnUpdate: boolean | string | number,
+        bodyHasScrollBar: boolean,
+    ) => BaseColumnProps<RecordType>[];
     memoizedFilterColumns: (columns: BaseColumnProps<RecordType>[], ignoreKeys?: string[]) => BaseColumnProps<RecordType>[];
     memoizedFlattenFnsColumns: (columns: BaseColumnProps<RecordType>[], childrenColumnName?: string) => BaseColumnProps<RecordType>[];
     memoizedPagination: (pagination: BasePagination) => BasePagination;
@@ -117,8 +124,8 @@ class TableFoundation<RecordType> extends BaseFoundation<TableAdapter<RecordType
         const mergePagination: (pagination: BasePagination) => BasePagination = this._adapter.getMergePagination();
 
         this.memoizedWithFnsColumns = memoizeOne(handleColumns, isEqual);
-        this.memoizedFilterColumns = memoizeOne(filterColumns, isEqual);
-        this.memoizedFlattenFnsColumns = memoizeOne(flattenColumns, isEqual);
+        this.memoizedFilterColumns = memoizeOne(filterColumns);
+        this.memoizedFlattenFnsColumns = memoizeOne(flattenColumns);
         this.memoizedPagination = memoizeOne(mergePagination, isEqual);
     }
 
@@ -214,7 +221,7 @@ class TableFoundation<RecordType> extends BaseFoundation<TableAdapter<RecordType
      * @param {object} queries
      * @returns {{dataSource: RecordType[], groups: Map<string, Set<string>>, pagination: object, disabledRowKeys: string[], queries: BaseColumnProps[], allRowKeys: string[]}}
      */
-    getCurrentPageData(dataSource: RecordType[], pagination?: BasePagination, queries?: BaseColumnProps<RecordType>[]) {
+    getCurrentPageData(dataSource?: RecordType[], pagination?: BasePagination, queries?: BaseColumnProps<RecordType>[]) {
         const filteredSortedDataSource = this._adapter.getCachedFilteredSortedDataSource();
         dataSource = dataSource == null ? [...filteredSortedDataSource] : dataSource;
         pagination =

+ 1 - 1
packages/semi-icons/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-icons",
-  "version": "2.0.3",
+  "version": "2.0.4",
   "description": "semi icons",
   "keywords": [
     "semi",

+ 1 - 1
packages/semi-illustrations/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-illustrations",
-  "version": "2.0.3",
+  "version": "2.0.4",
   "description": "semi illustrations",
   "keywords": [
     "semi",

+ 1 - 1
packages/semi-scss-compile/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-scss-compile",
-  "version": "2.0.3",
+  "version": "2.0.4",
   "description": "compile semi scss to css",
   "author": "[email protected]",
   "license": "MIT",

+ 1 - 1
packages/semi-theme-default/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-theme-default",
-    "version": "2.0.3",
+    "version": "2.0.4",
     "description": "semi-theme-default",
     "keywords": [
         "semi-theme",

+ 123 - 1
packages/semi-ui/cascader/__test__/cascader.test.js

@@ -302,6 +302,89 @@ describe('Cascader', () => {
         // done();
     });
 
+    it('disabled + defaultValue', () => {
+        const cascaderWithSingle = render({
+            treeData: treeDataWithDisabled,
+            changeOnSelect: true,
+            defaultValue:['Yazhou', 'Zhongguo']
+        });
+        expect(
+            cascaderWithSingle
+            .find(`.${BASE_CLASS_PREFIX}-cascader-selection span`)
+            .at(0)
+            .getDOMNode()
+            .textContent
+        ).toEqual('亚洲 / 中国');
+        cascaderWithSingle.unmount();
+
+        const cascaderWithSingleFilter = render({
+            treeData: treeDataWithDisabled,
+            changeOnSelect: true,
+            filterTreeNode: true,
+            defaultValue:['Yazhou', 'Zhongguo']
+        });
+        expect(
+            cascaderWithSingleFilter
+            .find(`.${BASE_CLASS_PREFIX}-cascader-search-wrapper .${BASE_CLASS_PREFIX}-input`)
+            .getDOMNode()
+            .getAttribute('value')
+        ).toEqual('亚洲 / 中国');
+        cascaderWithSingleFilter.unmount();
+
+        const cascaderWithSingleControlled = render({
+            treeData: treeDataWithDisabled,
+            changeOnSelect: true,
+            value: ['Yazhou', 'Zhongguo'],
+        });
+        expect(cascaderWithSingleControlled.find(`.${BASE_CLASS_PREFIX}-cascader-selection span`).getDOMNode().textContent).toEqual(
+            '亚洲 / 中国'
+        );
+        cascaderWithSingleControlled.unmount();
+
+        const cascaderWithMultiple = render({
+            treeData: treeDataWithDisabled,
+            multiple: true,
+            defaultValue:['Yazhou', 'Zhongguo']
+        });
+        expect(
+            cascaderWithMultiple
+            .find(`.${BASE_CLASS_PREFIX}-tag .${BASE_CLASS_PREFIX}-tag-content`)
+            .at(0)
+            .getDOMNode()
+            .textContent
+        ).toEqual('中国');
+        cascaderWithMultiple.unmount();
+
+        const cascaderWithMultipleFilter = render({
+            treeData: treeDataWithDisabled,
+            multiple: true,
+            filterTreeNode: true,
+            defaultValue:['Yazhou', 'Zhongguo']
+        });
+        expect(
+            cascaderWithMultipleFilter
+            .find(`.${BASE_CLASS_PREFIX}-tag .${BASE_CLASS_PREFIX}-tag-content`)
+            .at(0)
+            .getDOMNode()
+            .textContent
+        ).toEqual('中国');
+        cascaderWithMultipleFilter.unmount();
+
+        const cascaderWithMultipleControlled = render({
+            treeData: treeDataWithDisabled,
+            multiple: true,
+            value:['Yazhou', 'Zhongguo']
+        });
+        expect(
+            cascaderWithMultipleControlled
+            .find(`.${BASE_CLASS_PREFIX}-tag .${BASE_CLASS_PREFIX}-tag-content`)
+            .at(0)
+            .getDOMNode()
+            .textContent
+        ).toEqual('中国');
+        cascaderWithMultipleControlled.unmount();
+    });
+
     it('select item / onSelect / onChange', () => {
         let spyOnSelect = sinon.spy(() => {});
         let spyOnChange = sinon.spy(() => {});
@@ -873,5 +956,44 @@ describe('Cascader', () => {
         // check checkedKeys
         expect(cascaderWithDisable.state().checkedKeys.size).toEqual(5); 
         cascaderWithDisable.unmount();
-    })
+    });
+
+    it('multiple + onChangeWithObject', () => {
+        const cascader = render({
+            multiple: true,
+            onChangeWithObject: true,
+            defaultValue: [
+                {
+                    label: '北美洲',
+                    value: 'Beimeizhou',
+                    key: 'beimeizhou',
+                    children: [
+                        {
+                            label: '美国',
+                            value: 'Meiguo',
+                            key: 'meiguo',
+                        },
+                        {
+                            label: '加拿大',
+                            value: 'Jianada',
+                            key: 'jianada',
+                        },
+                    ],
+                },
+                {
+                    label: '美国',
+                    value: 'Meiguo',
+                    key: 'meiguo',
+                }
+            ]
+        });
+        const tags = cascader.find(`.${BASE_CLASS_PREFIX}-cascader-selection .${BASE_CLASS_PREFIX}-tag`)
+        expect(tags.length).toEqual(1);
+        expect(
+            tags
+            .find(`.${BASE_CLASS_PREFIX}-tag-content`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('美国');
+    });
 });

+ 106 - 2
packages/semi-ui/cascader/_story/cascader.stories.js

@@ -361,7 +361,41 @@ stories.add('disabled', () => {
 stories.add('disabled option', () => {
     return (
         <div>
-            <Cascader style={{ width: 300 }} treeData={treeData2} placeholder="Please select" filterTreeNode disabled />
+            <div>common disabled option</div>
+            <Cascader
+                style={{ width: 300 }}
+                treeData={treeData2}
+                placeholder="Japan node is disabled"
+            />
+            <br /><br />
+            <div>single selection + defaultValue is disabled option + changeOnSelect</div>
+            <Cascader
+                style={{ width: 300 }}
+                treeData={treeData2}
+                changeOnSelect
+                defaultValue={['yazhou', 'riben']}
+                placeholder="Japan node is disabled"
+            />
+            <br /><br />
+            <div>single selection + defaultValue is disabled option + changeOnSelect + filterTreeNode</div>
+            <Cascader
+                style={{ width: 300 }}
+                treeData={treeData2}
+                changeOnSelect
+                filterTreeNode
+                defaultValue={['yazhou', 'riben']}
+                placeholder="Japan node is disabled"
+            />
+            <br /><br />
+            <div>multiple selection + defaultValue is disabled option</div>
+            <Cascader
+                multiple
+                filterTreeNode
+                style={{ width: 300 }}
+                treeData={treeData2}
+                defaultValue={['yazhou', 'riben']}
+                placeholder="Japan node is disabled"
+            />
         </div>
     );
 });
@@ -1061,4 +1095,74 @@ stories.add('loadData with defaultValue', () => {
             <LoadDataWithDefaultValue/>
         </div>
     );
-});
+});
+
+stories.add('onChangeWithObject', () => (
+    <>
+        <div>单选 + onChangeWithObject + defaultValue 为 string []</div>
+        <Cascader
+            onChangeWithObject
+            style={{ width: 300 }}
+            treeData={treeData4}
+            placeholder="请选择所在地区"
+            defaultValue={['zhejiang', 'hangzhou', 'xihu']}
+        />
+        <br /><br />
+        <div>多选 + onChangeWithObject + defaultValue 为 string []</div>
+        <Cascader
+            multiple
+            changeOnSelect
+            onChangeWithObject
+            style={{ width: 300 }}
+            treeData={treeData4}
+            placeholder="请选择所在地区"
+            defaultValue={'zhejiang'}
+        />
+        <br /><br />
+        <div>单选 + onChangeWithObject + defaultValue 为 object []</div>
+        <Cascader
+            onChangeWithObject
+            changeOnSelect
+            style={{ width: 300 }}
+            treeData={treeData2}
+            placeholder="请选择所在地区"
+            defaultValue={{
+                label: '北美洲',
+                value: 'beimeizhou',
+                children: [
+                    {
+                        label: '美国',
+                        value: 'meiguo',
+                    },
+                    {
+                        label: '加拿大',
+                        value: 'jianada',
+                    },
+                ],
+            }}
+        />
+        <br /><br />
+        <div>多选 + onChangeWithObject + defaultValue 为 object []</div>
+        <Cascader
+            multiple
+            onChangeWithObject
+            style={{ width: 300 }}
+            treeData={treeData2}
+            placeholder="请选择所在地区"
+            defaultValue={{
+                label: '北美洲',
+                value: 'beimeizhou',
+                children: [
+                    {
+                        label: '美国',
+                        value: 'meiguo',
+                    },
+                    {
+                        label: '加拿大',
+                        value: 'jianada',
+                    },
+                ],
+            }}
+        />
+    </>
+));

+ 4 - 2
packages/semi-ui/cascader/index.tsx

@@ -378,15 +378,17 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
                     let normallizedValue: SimpleValueType[][] = [];
                     const realValue = needUpdate('value') ? value : defaultValue;
                     // eslint-disable-next-line max-depth
-                    if (!isEmpty(realValue)) {
+                    if (Array.isArray(realValue)) {
                         normallizedValue = Array.isArray(realValue[0]) ?
                             realValue as SimpleValueType[][] :
                             [realValue] as SimpleValueType[][];
+                    } else {
+                        normallizedValue = [[realValue]];
                     }
                     // formatValuePath is used to save value of valuePath
                     const formatValuePath: (string | number)[][] = [];
                     normallizedValue.forEach((valueItem: SimpleValueType[]) => {
-                        const formatItem: (string | number)[] = onChangeWithObject && !isEmpty(prevProps) ?
+                        const formatItem: (string | number)[] = onChangeWithObject ?
                             valueItem.map((i: CascaderData) => i.value) :
                             valueItem as (string | number)[];
                         formatValuePath.push(formatItem);

+ 11 - 1
packages/semi-ui/form/arrayField.tsx

@@ -9,7 +9,17 @@ import { ArrayFieldStaff, FormUpdaterContextType } from '@douyinfe/semi-foundati
 export interface ArrayFieldProps {
     initValue?: any[];
     field?: string;
-    children?: ({ }) => React.ReactNode;
+    children?: (props: ArrayFieldChildrenProps) => React.ReactNode;
+}
+
+export interface ArrayFieldChildrenProps {
+    arrayFields: {
+        key: string;
+        field: string;
+        remove: () => void;
+    }[];
+    add: () => void;
+    addWithInitValue: (lineObject: Record<string, any>) => void;
 }
 
 export interface ArrayFieldState {

+ 7 - 7
packages/semi-ui/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-ui",
-    "version": "2.0.3",
+    "version": "2.0.4",
     "description": "",
     "main": "lib/es/index.js",
     "module": "lib/es/index.js",
@@ -19,11 +19,11 @@
     },
     "dependencies": {
         "@babel/runtime-corejs3": "^7.15.4",
-        "@douyinfe/semi-animation-react": "2.0.3",
-        "@douyinfe/semi-foundation": "2.0.3",
-        "@douyinfe/semi-icons": "2.0.3",
-        "@douyinfe/semi-illustrations": "2.0.3",
-        "@douyinfe/semi-theme-default": "2.0.3",
+        "@douyinfe/semi-animation-react": "2.0.4",
+        "@douyinfe/semi-foundation": "2.0.4",
+        "@douyinfe/semi-icons": "2.0.4",
+        "@douyinfe/semi-illustrations": "2.0.4",
+        "@douyinfe/semi-theme-default": "2.0.4",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "copy-text-to-clipboard": "^2.1.1",
@@ -70,7 +70,7 @@
         "@babel/plugin-transform-runtime": "^7.15.8",
         "@babel/preset-env": "^7.15.8",
         "@babel/preset-react": "^7.14.5",
-        "@douyinfe/semi-scss-compile": "2.0.3",
+        "@douyinfe/semi-scss-compile": "2.0.4",
         "@storybook/addon-knobs": "^6.3.1",
         "babel-loader": "^8.2.2",
         "case-sensitive-paths-webpack-plugin": "^2.4.0",

+ 76 - 31
packages/semi-ui/select/_story/select.stories.js

@@ -2269,37 +2269,39 @@ const SelectRefDemo = () => {
 };
 
 stories.add('ref', () => <SelectRefDemo />);
-stories.add(`custom trigger`, () => <CustomTrigger />); // class VirtualizeDemo extends React.Component {
-//     constructor(props) {
-//         super(props);
-//         // this.handleSearch = this.handleSearch.bind(this);
-//         let newOptions = Array.from({ length: 1000 }, (v, i) => ({ label: `option-${i}`, value: i }));
-//         this.state = {
-//             optionList: newOptions,
-//         };
-//     }
-//     render() {
-//         let { groups, optionList } = this.state;
-//         let virtualize = {
-//             height: 300,
-//             widht: '100%',
-//             itemSize: 36,
-//         };
-//         return (
-//             <>
-//                 <Select
-//                     placeholder=""
-//                     style={{ width: 180 }}
-//                     filter
-//                     onSearch={this.handleSearch}
-//                     virtualize={virtualize}
-//                     optionList={optionList}
-//                 ></Select>
-//             </>
-//         );
-//     }
-// }
-// stories.add(`virtualize select`, () => <VirtualizeDemo />);
+stories.add(`custom trigger`, () => <CustomTrigger />); 
+
+class VirtualizeDemo extends React.Component {
+    constructor(props) {
+        super(props);
+        // this.handleSearch = this.handleSearch.bind(this);
+        let newOptions = Array.from({ length: 1000 }, (v, i) => ({ label: `option-${i}`, value: i }));
+        this.state = {
+            optionList: newOptions,
+        };
+    }
+    render() {
+        let { groups, optionList } = this.state;
+        let virtualize = {
+            height: 300,
+            widht: '100%',
+            itemSize: 36,
+        };
+        return (
+            <>
+                <Select
+                    placeholder=""
+                    style={{ width: 180 }}
+                    filter
+                    onSearch={this.handleSearch}
+                    virtualize={virtualize}
+                    optionList={optionList}
+                ></Select>
+            </>
+        );
+    }
+}
+stories.add(`virtualize select`, () => <VirtualizeDemo />);
 
 const SelectPosition = () => {
     return (
@@ -2609,3 +2611,46 @@ const Highlight = () => {
 };
 
 stories.add('highlight', () => <Highlight />);
+
+stories.add('scroll into view', () => (
+    <>
+        <div>
+            <p>single selection</p>
+            <Select defaultValue='option-11' defaultOpen style={{ marginBottom: 300, width: 120 }}>
+                {new Array(50).fill(null).map((item, idx) => (
+                    <Option value={`${idx}`} key={idx}>{`option-${idx}`}</Option>
+                ))}
+            </Select>
+            <p>single selection with no selected item</p>
+            <Select style={{ marginBottom: 300, width: 120 }}>
+                {new Array(50).fill(null).map((item, idx) => (
+                    <Option value={`${idx}`} key={idx}>{`option-${idx}`}</Option>
+                ))}
+            </Select>
+            <p>The selected node is the last</p>
+            <Select defaultValue='option-49' defaultOpen style={{ marginBottom: 300, width: 120 }}>
+                {new Array(50).fill(null).map((item, idx) => (
+                    <Option value={`${idx}`} key={idx}>{`option-${idx}`}</Option>
+                ))}
+            </Select>
+            <p>The selected node is the first</p>
+            <Select defaultValue='option-0' style={{ marginBottom: 300, width: 120 }}>
+                {new Array(50).fill(null).map((item, idx) => (
+                    <Option value={`${idx}`} key={idx}>{`option-${idx}`}</Option>
+                ))}
+            </Select>
+            <p>multiple selection</p>
+            <Select defaultValue={['option-25', 'option-9']} multiple style={{ marginBottom: 300, width: 220 }}>
+                {new Array(30).fill(null).map((item, idx) => (
+                    <Option value={`${idx}`} key={idx}>{`option-${idx}`}</Option>
+                ))}
+            </Select>
+            <p>multiple selection with no selected item</p>
+            <Select multiple style={{ marginBottom: 300, width: 220 }}>
+                {new Array(30).fill(null).map((item, idx) => (
+                    <Option value={`${idx}`} key={idx}>{`option-${idx}`}</Option>
+                ))}
+            </Select>
+        </div>
+    </>
+));

+ 49 - 2
packages/semi-ui/select/index.tsx

@@ -293,6 +293,8 @@ class Select extends BaseComponent<SelectProps, SelectState> {
     inputRef: React.RefObject<HTMLInputElement>;
     triggerRef: React.RefObject<HTMLDivElement>;
     optionsRef: React.RefObject<any>;
+    virtualizeListRef: React.RefObject<any>;
+    selectOptionListID: string;
     clickOutsideHandler: (e: MouseEvent) => void;
     foundation: SelectFoundation;
 
@@ -312,6 +314,9 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             optionGroups: [],
             isHovering: false,
         };
+        /* Generate random string */
+        this.selectOptionListID = Math.random().toString(36).slice(2);
+        this.virtualizeListRef = React.createRef();
         this.inputRef = React.createRef();
         this.triggerRef = React.createRef();
         this.optionsRef = React.createRef();
@@ -493,7 +498,26 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                 } catch (error) {
 
                 }
-            }
+            },
+            updateScrollTop: () => {
+                // eslint-disable-next-line max-len
+                let destNode = document.querySelector(`#${prefixcls}-${this.selectOptionListID} .${prefixcls}-option-selected`) as HTMLDivElement;
+                if (Array.isArray(destNode)) {
+                    // eslint-disable-next-line prefer-destructuring
+                    destNode = destNode[0];
+                }
+                if (destNode) {
+                    /**
+                     * Scroll the first selected item into view.
+                     * The reason why ScrollIntoView is not used here is that it may cause page to move.
+                     */
+                    const destParent = destNode.parentNode as HTMLDivElement;
+                    destParent.scrollTop = destNode.offsetTop -
+                        destParent.offsetTop -
+                        (destParent.clientHeight / 2) +
+                        (destNode.clientHeight / 2);
+                }
+            },
         };
     }
 
@@ -718,6 +742,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
 
         return (
             <List
+                ref={this.virtualizeListRef}
                 height={height || numbers.LIST_HEIGHT}
                 itemCount={visibileOptions.length}
                 itemSize={itemSize}
@@ -761,7 +786,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
 
         const isEmpty = !options.length || !options.some(item => item._show);
         return (
-            <div className={dropdownClassName} style={style}>
+            <div id={`${prefixcls}-${this.selectOptionListID}`} className={dropdownClassName} style={style}>
                 {outerTopSlot}
                 <div
                     style={{ maxHeight: `${maxHeight}px` }}
@@ -894,6 +919,27 @@ class Select extends BaseComponent<SelectProps, SelectState> {
         this.foundation.handleMouseLeave(e as any);
     }
 
+    /* Processing logic when popover visible changes */
+    handlePopoverVisibleChange(status) {
+        const { virtualize } = this.props;
+        const { selections } = this.state;
+        if (!status) {
+            return;
+        }
+        if (virtualize) {
+            let minKey;
+            selections.forEach((v, k) => {
+                const tempKey = Number(String(k).match(/option-(.*)/)[1]);
+                minKey = (typeof minKey === 'number' && minKey < tempKey) ? minKey : tempKey;
+            });
+            if (minKey) {
+                this.virtualizeListRef.current.scrollToItem(minKey, 'center');
+            }
+        } else {
+            this.foundation.updateScrollTop();
+        }
+    }
+
     renderSuffix() {
         const { suffix } = this.props;
         const suffixWrapperCls = cls({
@@ -1053,6 +1099,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                 position={position}
                 spacing={spacing}
                 stopPropagation={stopPropagation}
+                onVisibleChange={status => this.handlePopoverVisibleChange(status)}
             >
                 {selection}
             </Popover>

+ 2 - 0
packages/semi-ui/table/ColumnFilter.tsx

@@ -137,6 +137,8 @@ export interface ColumnFilterProps {
     filterDropdown?: React.ReactElement;
     renderFilterDropdown?: (props: RenderDropdownProps, options: { iconElem: React.ReactNode }) => React.ReactElement;
     filterDropdownProps?: DropdownProps;
+    onFilterDropdownVisibleChange?: OnFilterDropdownVisibleChange;
+    onSelect?: (data: OnSelectData) => void;
 }
 
 export default function ColumnFilter(props: ColumnFilterProps = {}): React.ReactElement {

+ 10 - 3
packages/semi-ui/table/Table.tsx

@@ -351,6 +351,7 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
     lastScrollLeft!: number;
     scrollPosition!: BodyScrollPosition;
     position!: BodyScrollPosition;
+    foundation: TableFoundation<RecordType>;
     constructor(props: NormalTableProps<RecordType>, context: TableContextProps) {
         super(props);
         this.foundation = new TableFoundation<RecordType>(this.adapter);
@@ -554,7 +555,7 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
                 pagination: statePagination = null,
                 dataSource: stateDataSource = null,
             } = states;
-            const handledProps: Partial<NormalTableState<RecordType>> = this.foundation.getCurrentPageData(stateDataSource, statePagination, stateQueries);
+            const handledProps: Partial<NormalTableState<RecordType>> = this.foundation.getCurrentPageData(stateDataSource, statePagination as TablePaginationProps, stateQueries);
 
             // After the pager is updated, reset allRowKeys of the current page
             this.adapter.setAllRowKeys(handledProps.allRowKeys);
@@ -613,7 +614,7 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
         }
     };
 
-    _invokeColumnFn = (key: string | number, funcName: string, ...args: any[]) => {
+    _invokeColumnFn = (key: string, funcName: string, ...args: any[]) => {
         if (key && funcName) {
             const column = this.foundation.getQuery(key);
             const func = get(column, funcName, null);
@@ -642,10 +643,15 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
 
     getColumns = (columns: ColumnProps<RecordType>[], children: ReactNode) => (!Array.isArray(columns) || !columns || !columns.length ? getColumns(children) : columns);
 
+    // @ts-ignore
     getCellWidths = (...args: any[]) => this.foundation.getCellWidths(...args);
+    // @ts-ignore
     setHeadWidths = (...args: any[]) => this.foundation.setHeadWidths(...args);
+    // @ts-ignore
     getHeadWidths = (...args: any[]) => this.foundation.getHeadWidths(...args);
+    // @ts-ignore
     mergedRowExpandable = (...args: any[]) => this.foundation.mergedRowExpandable(...args);
+    // @ts-ignore
     setBodyHasScrollbar = (...args: any[]) => this.foundation.setBodyHasScrollbar(...args);
 
     handleWheel = (event: React.WheelEvent<HTMLDivElement>) => {
@@ -857,7 +863,7 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
         const key =
             typeof groupKey === 'string' || typeof groupKey === 'number' ?
                 groupKey :
-                this.foundation.getRecordKey(record);
+                this.foundation.getRecordKey(record as RecordType);
 
         return (
             <ExpandedIcon
@@ -870,6 +876,7 @@ class Table<RecordType extends Record<string, any>> extends BaseComponent<Normal
         );
     };
 
+    // @ts-ignore
     handleRowExpanded = (...args: any[]) => this.foundation.handleRowExpanded(...args);
 
     normalizeExpandColumn = (props: { prefixCls?: string; expandCellFixed?: ArrayElement<typeof strings.FIXED_SET>; expandIcon?: ExpandIcon } = {}) => {

+ 1 - 1
packages/semi-webpack/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-webpack-plugin",
-    "version": "2.0.3",
+    "version": "2.0.4",
     "description": "",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",

+ 24 - 7
src/components/side-nav.js

@@ -1,6 +1,6 @@
 import React, { useState, useEffect, useMemo, Suspense } from 'react';
 import { Nav, Icon } from '@douyinfe/semi-ui';
-import { navigate, withPrefix } from 'gatsby';
+import { withPrefix, Link } from 'gatsby';
 import { getLocale } from 'utils/locale';
 import IconMap from '../images/docIcons';
 
@@ -34,13 +34,31 @@ const SideNav = ({ location = null, type = null, itemsArr, edges, style, hasBann
                     text: node.frontmatter.title,
                     icon: <Icon svg={<IconNode />} size={'extra-large'} /> || '',
                     order: node.frontmatter.order || 0,
+                    // link: `/${node.fields.slug}`,
+                    // linkOptions: {
+                    //     onClick: (e) => {
+                    //         e.preventDefault();
+                    //     }
+                    // },
                     category: navData[categoryIndex].text || navData[categoryIndex].textUs,
                 });
                 navData[categoryIndex].items.sort((a, b) => a.order - b.order);
             }
         });
-
-        return navData;
+        return navData.map(category=>{
+            const { items, ...rest } = category
+            return (
+                <Nav.Sub {...rest} key={rest.itemKey}>
+                    {items.map(item=>{
+                        return (
+                            <Link to={item.itemKey} key={item.itemKey} >
+                                <Nav.Item {...item} />
+                            </Link>
+                        )
+                    })}
+                </Nav.Sub>
+            )
+        })
     }
 
     const computedNavData = useMemo(() => getNavData(itemsArr), [itemsArr, localeCode]);
@@ -69,8 +87,6 @@ const SideNav = ({ location = null, type = null, itemsArr, edges, style, hasBann
 
 
     const onNavSelect = ({ itemKey, selectedKeys }) => {
-        setSelectedKeys([...selectedKeys]);
-        navigate(itemKey);
         window?.__semi__?.Tea?.eventHappened('mainSiteNavClicked', itemKey);
     };
 
@@ -90,12 +106,13 @@ const SideNav = ({ location = null, type = null, itemsArr, edges, style, hasBann
                 subNavMotion={subNavMotion}
                 bodyStyle={{ height: '100%' }}
                 onSelect={onNavSelect}
-                items={computedNavData}
                 selectedKeys={selectedKeys}
                 onOpenChange={onOpenChange}
                 defaultOpenKeys={computedNavData.map(item => item.itemKey)}
                 openKeys={openKeys}
-            />
+            >
+                {computedNavData}
+            </Nav>
         </div>
     );
 };

+ 1 - 1
src/sitePages/newHome/components/comments/comments.jsx

@@ -21,7 +21,7 @@ function Comments(props) {
                 <div className={styles.group3737}>
                     <Button onClick={goStart} size="large" theme="solid" className={styles.extraLarge}>{_t("start_using", { }, "开始使用")}</Button>
                     <div onClick={goGithub} className={styles.buttonSecondarySolid_4427b030}>
-                        <IconGithubLogo size="extra-large" /><p className={styles.text_bff7eaeb}>Github</p>
+                        <IconGithubLogo size="extra-large" /><p className={styles.text_bff7eaeb}>GitHub</p>
                     </div>
                 </div>
             </div>