瀏覽代碼

Merge branch 'main' into release

zhangyumei.0319 2 年之前
父節點
當前提交
00f66ed5ad
共有 54 個文件被更改,包括 1133 次插入322 次删除
  1. 7 2
      .github/workflows/publish.yml
  2. 4 0
      content/start/changelog/index-en-US.md
  3. 4 0
      content/start/changelog/index.md
  4. 2 2
      cypress/integration/overflowList.spec.js
  5. 7 0
      cypress/integration/select.spec.js
  6. 1 1
      lerna.json
  7. 1 1
      packages/semi-animation-react/package.json
  8. 1 1
      packages/semi-animation-styled/package.json
  9. 1 1
      packages/semi-animation/package.json
  10. 1 1
      packages/semi-eslint-plugin/package.json
  11. 8 20
      packages/semi-foundation/cascader/cascader.scss
  12. 4 2
      packages/semi-foundation/overflowList/constants.ts
  13. 46 30
      packages/semi-foundation/overflowList/foundation.ts
  14. 1 1
      packages/semi-foundation/package.json
  15. 11 0
      packages/semi-foundation/tagInput/tagInput.scss
  16. 1 1
      packages/semi-foundation/tooltip/foundation.ts
  17. 14 2
      packages/semi-foundation/tree/treeUtil.ts
  18. 38 6
      packages/semi-foundation/treeSelect/foundation.ts
  19. 9 19
      packages/semi-foundation/treeSelect/treeSelect.scss
  20. 1 1
      packages/semi-icons/package.json
  21. 2 2
      packages/semi-icons/src/icons/IconLoading.tsx
  22. 2 2
      packages/semi-icons/src/icons/IconSpin.tsx
  23. 2 2
      packages/semi-icons/src/icons/IconTriangleArrow.tsx
  24. 2 2
      packages/semi-icons/src/icons/IconUserAdd.tsx
  25. 0 0
      packages/semi-icons/src/svgs/loading.svg
  26. 1 1
      packages/semi-icons/src/svgs/spin.svg
  27. 1 1
      packages/semi-icons/src/svgs/triangle_arrow.svg
  28. 1 1
      packages/semi-icons/src/svgs/user_add.svg
  29. 1 1
      packages/semi-illustrations/package.json
  30. 4 4
      packages/semi-illustrations/src/illustrations/IllustrationFailure.tsx
  31. 4 4
      packages/semi-illustrations/src/illustrations/IllustrationFailureDark.tsx
  32. 4 4
      packages/semi-illustrations/src/svgs/failure-dark.svg
  33. 4 4
      packages/semi-illustrations/src/svgs/failure.svg
  34. 1 1
      packages/semi-next/package.json
  35. 1 1
      packages/semi-scss-compile/package.json
  36. 1 1
      packages/semi-theme-default/package.json
  37. 9 10
      packages/semi-ui/cascader/_story/cascader.stories.jsx
  38. 11 11
      packages/semi-ui/overflowList/__test__/overflowList.test.js
  39. 48 5
      packages/semi-ui/overflowList/_story/overflowList.stories.jsx
  40. 137 58
      packages/semi-ui/overflowList/index.tsx
  41. 7 7
      packages/semi-ui/package.json
  42. 8 0
      packages/semi-ui/select/_story/select.stories.jsx
  43. 6 1
      packages/semi-ui/select/index.tsx
  44. 19 1
      packages/semi-ui/tag/_story/tag.stories.jsx
  45. 1 1
      packages/semi-ui/tag/index.tsx
  46. 13 10
      packages/semi-ui/tagInput/index.tsx
  47. 235 2
      packages/semi-ui/treeSelect/__test__/treeMultiple.test.js
  48. 121 2
      packages/semi-ui/treeSelect/__test__/treeSelect.test.js
  49. 298 1
      packages/semi-ui/treeSelect/_story/treeSelect.stories.jsx
  50. 18 14
      packages/semi-ui/treeSelect/index.tsx
  51. 4 1
      packages/semi-ui/typography/util.tsx
  52. 1 1
      packages/semi-webpack/package.json
  53. 4 1
      packages/semi-webpack/src/semi-webpack-plugin.ts
  54. 0 74
      yarn.lock

+ 7 - 2
.github/workflows/publish.yml

@@ -33,8 +33,13 @@ jobs:
               run: |
                   git config --global user.name 'semi-bot'
                   git config --global user.email '[email protected]'
-                  git add .
-                  git commit --no-verify -m "chore: publish ${{ env.RELEASE_VERSION }}"
+                  if [ -n "$(git status --porcelain)" ]; then
+                    echo "there are changes";
+                    git add .
+                    git commit --no-verify -m "chore: publish ${{ env.RELEASE_VERSION }}"
+                  else
+                    echo "no changes";
+                  fi
                   npm config set registry=https://registry.npmjs.org/
                   npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}
                   npm whoami

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

@@ -15,6 +15,10 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
 -   **Patch version**: Only include bug fix, the release time is not limited
 
 ---
+#### 🎉 2.25.2 (2022-12-19)
+- 【Fix】
+    - fix the problem that the automatic adjustment position is incorrect when the Tooltip position is topLeft
+    - fix the wrong style of TagInput when it is draggable
 
 #### 🎉 2.25.0 (2022-12-09)
 - 【Fix】

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

@@ -15,6 +15,10 @@ Semi 版本号遵循 **Semver** 规范(主版本号-次版本号-修订版本
 
 
 ---
+#### 🎉 2.25.2 (2022-12-19)
+- 【Fix】
+    - 修复 Tooltip position 为 topLeft 时,自动调整位置不正确问题。
+    - 修复 TagInput 可拖动时样式错误问题
 
 #### 🎉 2.25.0 (2022-12-09)
 - 【Fix】

+ 2 - 2
cypress/integration/overflowList.spec.js

@@ -3,9 +3,9 @@ describe('overflowList', () => {
         cy.visit('http://127.0.0.1:6006/iframe.html?id=overflowlist--overlap-overflow-list&args=&viewMode=story');
         cy.viewport(500, 500);
         cy.get('.semi-tag').eq(0).contains('0');
-        cy.get('.semi-tag').eq(1).contains('3');
+        cy.get('.semi-tag').eq(1).contains('9');
         cy.get('.semi-overflow-list-scroll-wrapper').scrollTo('right');
-        cy.get('.semi-tag').eq(0).contains('4');
+        cy.get('.semi-tag').eq(0).contains('10');
     });
 
     it('resize', () => {

+ 7 - 0
cypress/integration/select.spec.js

@@ -47,6 +47,13 @@ describe('Select', () => {
         cy.get('@consoleLog').should('be.calledWith', 'onBlur');
     });
 
+    it('emptyContent=null', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?path=/story/select--empty-content');
+        // when emptyContent = null, The dropdown list will not be displayed
+        // so element(which class has semi-popover-wrapper) show have 0px height;
+        cy.get('.semi-popover-wrapper').eq(0).should('have.css', 'height', '0px');
+    });
+
     // it('should trigger onSearch when click x icon', () => {
     //     cy.visit('http://127.0.0.1:6006/iframe.html?path=/story/select--select-filter-single');
     //     cy.get('.semi-select').eq(0).click();

+ 1 - 1
lerna.json

@@ -1,5 +1,5 @@
 {
     "useWorkspaces": true,
     "npmClient": "yarn",
-    "version": "2.25.0-beta.0"
+    "version": "2.25.2"
 }

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation-react",
-  "version": "2.25.0-beta.0",
+  "version": "2.25.2",
   "description": "motion library for semi-ui-react",
   "keywords": [
     "motion",

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation-styled",
-  "version": "2.25.0-beta.0",
+  "version": "2.25.2",
   "description": "semi styled animation",
   "keywords": [
     "semi",

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation",
-  "version": "2.25.0-beta.0",
+  "version": "2.25.2",
   "description": "animation base library for semi-ui",
   "keywords": [
     "animation",

+ 1 - 1
packages/semi-eslint-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-semi-design",
-  "version": "2.25.0-beta.0",
+  "version": "2.25.2",
   "description": "semi ui eslint plugin",
   "keywords": [
     "semi",

+ 8 - 20
packages/semi-foundation/cascader/cascader.scss

@@ -161,22 +161,17 @@ $module: #{$prefix}-cascader;
         }
         
         .#{$prefix}-tagInput {
-            &-wrapper {
-                &-default {
-                    min-height: $height-cascader_selection_tagInput_wrapper_default;
-                }
-    
-                &-small {
-                    min-height: $height-cascader_selection_tagInput_wrapper_small;
-                }
-    
-                &-large {
-                    min-height: $height-cascader_selection_tagInput_wrapper_large;
-                }
+            min-height: $height-cascader_selection_tagInput_wrapper_default;
+
+            &-small {
+                min-height: $height-cascader_selection_tagInput_wrapper_small;
+            }
+
+            &-large {
+                min-height: $height-cascader_selection_tagInput_wrapper_large;
             }
 
             .#{$prefix}-input-wrapper {
-                // height: $height-cascader_selection_tagInput_input_default;
 
                 margin-left: $spacing-cascader_selection_input-marginLeft;
                 .#{$prefix}-input {
@@ -193,13 +188,6 @@ $module: #{$prefix}-cascader;
                     margin-bottom: $spacing-cascader_selection_tag-marginY;
                 }
             }
-
-            // .#{$prefix}-input-wrapper-small {
-            //     height: $height-cascader_selection_tagInput_input_small;
-            // }
-            // .#{$prefix}-input-wrapper-large {
-            //     height: $height-cascader_selection_tagInput_input_large;
-            // }
         }
 
         &-text {

+ 4 - 2
packages/semi-foundation/overflowList/constants.ts

@@ -14,7 +14,7 @@ const MODE_MAP = {
 const BOUNDARY_MAP = {
     START: 'start',
     END: 'end',
-};
+} as const;
 
 const OVERFLOW_DIR = {
     NONE: 0,
@@ -31,7 +31,9 @@ const strings = {
     OVERFLOW_DIR
 };
 
-const numbers = {};
+const numbers = {
+    MINIMUM_HTML_ELEMENT_WIDTH: 4
+};
 
 export {
     cssClasses,

+ 46 - 30
packages/semi-foundation/overflowList/foundation.ts

@@ -7,7 +7,8 @@ const OverflowDirection = strings.OVERFLOW_DIR;
 export interface OverflowListAdapter extends DefaultAdapter {
     updateStates: (state: any) => void;
     updateVisibleState: (visible: Map<string, boolean>) => void;
-    notifyIntersect: (res: any) => void
+    notifyIntersect: (res: any) => void;
+    getItemSizeMap: ()=>Map<string, number>
 }
 
 class OverflowListFoundation extends BaseFoundation<OverflowListAdapter> {
@@ -29,6 +30,7 @@ class OverflowListFoundation extends BaseFoundation<OverflowListAdapter> {
         if (!this.isScrollMode()) {
             return overflow;
         }
+
         const visibleStateArr = items.map(({ key }: { key: string }) => Boolean(visibleState.get(key)));
         const visibleStart = visibleStateArr.indexOf(true);
         const visibleEnd = visibleStateArr.lastIndexOf(true);
@@ -71,38 +73,52 @@ class OverflowListFoundation extends BaseFoundation<OverflowListAdapter> {
         this._adapter.notifyIntersect(res);
     }
 
-    handlePartition(growing: number): void {
-        const { direction, overflow, lastOverflowCount, visible } = this.getStates();
-        const { minVisibleItems, collapseFrom, items } = this.getProps();
-        let updateState = {};
-        if (growing === OverflowDirection.NONE) {
-            updateState = { direction: OverflowDirection.NONE };
-        }
-        if (growing === OverflowDirection.GROW) {
-            const updatedOverflowCount = direction === OverflowDirection.NONE ? overflow.length : lastOverflowCount;
-            updateState = {
-                direction: OverflowDirection.GROW,
-                lastOverflowCount: updatedOverflowCount,
-                overflow: [],
-                visible: items,
-            };
+    getReversedItems = ()=>{
+        const { items } = this.getProps();
+        return cloneDeep(items).reverse();
+    }
+    handleCollapseOverflow(){
+        const { minVisibleItems, collapseFrom } = this.getProps();
+        const { overflowWidth, containerWidth, pivot: statePivot, overflowStatus } = this.getStates();
+        const { items, onOverflow } = this.getProps();
+        let itemWidths = overflowWidth, _pivot = 0;
+        let overflowed = false;
+        for (const size of this._adapter.getItemSizeMap().values()) {
+            itemWidths += size;
+            // 触发overflow
+            if (itemWidths > containerWidth) {
+                overflowed = true;
+                break;
+            }
+            // 顺利遍历完整个列表,说明不存在overflow,直接渲染全部
+            if (_pivot === items.length - 1) {
+                this._adapter.updateStates({
+                    overflowStatus: "normal",
+                    pivot: items.length - 1,
+                    visible: items,
+                    overflow: []
+                });
+                break;
+            }
+            _pivot++;
         }
-        if (growing === OverflowDirection.SHRINK && visible.length > minVisibleItems) {
-            const collapseFromStart = collapseFrom === Boundary.START;
-            const newVisible = visible.slice();
-            const next = collapseFromStart ? newVisible.shift() : newVisible.pop();
-            if (next !== undefined) {
-                updateState = {
-                    // set SHRINK mode unless a GROW is already in progress.
-                    // GROW shows all items then shrinks until it settles, so we
-                    // preserve the fact that the original trigger was a GROW.
-                    direction: direction !== OverflowDirection.GROW ? OverflowDirection.SHRINK : direction,
-                    overflow: collapseFromStart ? [...overflow, next] : [next, ...overflow],
-                    visible: newVisible
-                };
+        if (overflowed) {
+            const pivot = Math.max(minVisibleItems, _pivot);
+            const isCollapseFromStart = collapseFrom === Boundary.START;
+            const visible = isCollapseFromStart ? this.getReversedItems().slice(0, pivot).reverse() : items.slice(0, pivot);
+            const overflow = isCollapseFromStart ? this.getReversedItems().slice(pivot).reverse() : items.slice(pivot);
+            this._adapter.updateStates({
+                overflowStatus: "overflowed",
+                pivot: pivot,
+                visible,
+                overflow,
+            });
+            // trigger onOverflow
+            if (statePivot !== pivot){
+                onOverflow(overflow);
             }
+            return;
         }
-        this._adapter.updateStates(updateState);
     }
 
 }

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-foundation",
-    "version": "2.25.0-beta.0",
+    "version": "2.25.2",
     "description": "",
     "scripts": {
         "build:lib": "node ./scripts/compileLib.js",

+ 11 - 0
packages/semi-foundation/tagInput/tagInput.scss

@@ -259,6 +259,17 @@ $module: #{$prefix}-tagInput;
             color: $color-tagInput_suffix-default;
         }
     }
+
+    &-tag-content-wrapper {
+        display: flex;
+        align-items: center;
+    }
+
+    &-sortable-list {
+        display: flex;
+        flex-flow: row wrap;
+        width: 100%;
+    }
 }
 
 @import "./rtl.scss";

+ 1 - 1
packages/semi-foundation/tooltip/foundation.ts

@@ -855,7 +855,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
                         position = this._adjustPos(position, true);
                     }
                     if (shouldReverseLeftSide && widthIsBigger) {
-                        position = this._adjustPos(position, true);
+                        position = this._adjustPos(position);
                     }
                     if (isWidthOverFlow && (isViewXEnoughSideHalf || isContainerXEnoughSideHalf)) {
                         position = this._adjustPos(position, true, 'reduce');

+ 14 - 2
packages/semi-foundation/tree/treeUtil.ts

@@ -187,6 +187,9 @@ export function findKeysForValues(valueList: any, valueEntities: any, isMultiple
     valueList.forEach((val: string) => {
         if (val in valueEntities) {
             res.push(valueEntities[val]);
+        } else {
+            // if val not in valueEntities, then value push to keys array
+            val && res.push(val);
         }
     });
 
@@ -427,12 +430,17 @@ export function normalizedArr(val: any) {
     }
 }
 
-export function normalizeKeyList(keyList: any, keyEntities: KeyEntities, leafOnly = false) {
+// flag is used to determine whether to return when the key does not belong to the keys in keyEntities
+// export function normalizeKeyList(keyList: any, keyEntities: KeyEntities, leafOnly = false) {
+export function normalizeKeyList(keyList: any, keyEntities: KeyEntities, leafOnly = false, flag?: boolean) {
     const res: string[] = [];
     const keyListSet = new Set(keyList);
     if (!leafOnly) {
         keyList.forEach((key: string) => {
             if (!keyEntities[key]) {
+                if (flag) {
+                    res.push(key);
+                }
                 return;
             }
             const { parent } = keyEntities[key];
@@ -446,6 +454,10 @@ export function normalizeKeyList(keyList: any, keyEntities: KeyEntities, leafOnl
             if (keyEntities[key] && !isValid(keyEntities[key].children)) {
                 res.push(key);
             }
+            // when key is not in keyEntities, if flag is true, key should be push in res
+            if (!keyEntities[key] && flag) {
+                res.push(key);
+            }
         });
     }
     return res;
@@ -546,7 +558,7 @@ export function calcCheckedKeysForUnchecked(key: string, keyEntities: KeyEntitie
             calcCurrLevel(par);
         }
     };
-    calcCurrLevel(nodeItem);
+    nodeItem && calcCurrLevel(nodeItem);
     return {
         checkedKeys,
         halfCheckedKeys,

+ 38 - 6
packages/semi-foundation/treeSelect/foundation.ts

@@ -262,6 +262,36 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
         return Boolean(inputValue) && showFilteredOnly;
     }
 
+    findDataForValue(findValue: string) {
+        const { value, defaultValue } = this.getProps();
+        let valueArr = [];
+        if (value) {
+            valueArr = Array.isArray(value) ? value : [value];
+        } else if (defaultValue) {
+            valueArr = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
+        }
+        return valueArr.find(item => {
+            return item.value === findValue || item.key === findValue;
+        });
+    }
+
+    constructDataForValue(value: string) {
+        const { treeNodeLabelProp } = this.getProps();
+        return {
+            key: value,
+            [treeNodeLabelProp]: value
+        };    
+    }
+
+    getDataForKeyNotInKeyEntities(value: string) {
+        const { onChangeWithObject } = this.getProps();
+        if (onChangeWithObject) {
+            return this.findDataForValue(value);
+        } else {
+            return this.constructDataForValue(value);
+        }
+    }
+
     getCopyFromState(items: string | string[]) {
         const res = {};
         normalizedArr(items).forEach(key => {
@@ -366,11 +396,11 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
         const { leafOnly, checkRelation } = this.getProps();
         let keyList = [];
         if (checkRelation === 'related') {
-            keyList = normalizeKeyList(key, keyEntities, leafOnly);
+            keyList = normalizeKeyList(key, keyEntities, leafOnly, true);
         } else if (checkRelation === 'unRelated') {
             keyList = key as string[];
         }
-        const nodes = keyList.map(i => keyEntities[i].data);
+        const nodes = keyList.map(key => (keyEntities[key] && keyEntities[key].data.key === key) ? keyEntities[key].data : this.getDataForKeyNotInKeyEntities(key));
         if (this.getProp('onChangeWithObject')) {
             this._adapter.notifyChangeWithObject(nodes, e);
         } else {
@@ -498,7 +528,7 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
     removeTag(eventKey: BasicTreeNodeData['key']) {
         const { disableStrictly, checkRelation } = this.getProps();
         const { keyEntities, disabledKeys, realCheckedKeys } = this.getStates();
-        const item = keyEntities[eventKey].data;
+        const item = (keyEntities[eventKey] && keyEntities[eventKey].data.key === eventKey) ? keyEntities[eventKey].data : this.getDataForKeyNotInKeyEntities(eventKey);
         if (item.disabled || (disableStrictly && disabledKeys.has(eventKey))) {
             return;
         }
@@ -789,9 +819,11 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
         const renderSelectedItem = isFunction(propRenderSelectedItem) ?
             propRenderSelectedItem :
             (item: BasicTreeNodeData) => get(item, treeNodeLabelProp, null);
-        const item = selectedKeys.length && keyEntities[selectedKeys[0]] ?
-            keyEntities[selectedKeys[0]].data :
-            undefined;
+        let item;
+        if (selectedKeys.length) {
+            const key = selectedKeys[0];
+            item = (keyEntities[key] && keyEntities[key].data.key === key) ? keyEntities[key].data : this.getDataForKeyNotInKeyEntities(key);
+        }
         const renderText = item && treeNodeLabelProp in item ? renderSelectedItem(item) : null;
         return renderText;
     }

+ 9 - 19
packages/semi-foundation/treeSelect/treeSelect.scss

@@ -171,23 +171,20 @@ $module: #{$prefix}-tree-select;
         .#{$prefix}-tagInput {
             border: hidden;
             background: transparent;
+            min-height: $height-treeSelect_selection_tagInput_wrapper_default;
+
+            &-small {
+                min-height: $height-treeSelect_selection_tagInput_wrapper_small;
+            }
+
+            &-large {
+                min-height: $height-treeSelect_selection_tagInput_wrapper_large;
+            }
 
             .#{$prefix}-tagInput-wrapper {
                 padding-left: $spacing-treeSelect_selection_tagInput_wrapper-paddingX;
                 padding-right: $spacing-treeSelect_selection_tagInput_wrapper-paddingX;
 
-                &-default {
-                    min-height: $height-treeSelect_selection_tagInput_wrapper_default;
-                }
-    
-                &-small {
-                    min-height: $height-treeSelect_selection_tagInput_wrapper_small;
-                }
-
-                &-large {
-                    min-height: $height-treeSelect_selection_tagInput_wrapper_large;
-                }
-
                 .#{$prefix}-input-wrapper {
                     // height: $height-treeSelect_selection_tagInput_input_default;
 
@@ -205,13 +202,6 @@ $module: #{$prefix}-tree-select;
                         margin-bottom: $spacing-treeSelect_tag-marginY;
                     }
                 }
-    
-                // .#{$prefix}-input-wrapper-small {
-                //     height: $height-treeSelect_selection_tagInput_input_small;
-                // }
-                // .#{$prefix}-input-wrapper-large {
-                //     height: $height-treeSelect_selection_tagInput_input_large;
-                // }
             }
         }
     }

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-icons",
-  "version": "2.25.0-beta.0",
+  "version": "2.25.2",
   "description": "semi icons",
   "keywords": [
     "semi",

+ 2 - 2
packages/semi-icons/src/icons/IconLoading.tsx

@@ -13,7 +13,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
             aria-hidden={true}
             {...props}
         >
-            <g clipPath="url(#clip0)">
+            <g clipPath="url(#clip_loading)">
                 <path
                     fillRule="evenodd"
                     clipRule="evenodd"
@@ -22,7 +22,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
                 />
             </g>
             <defs>
-                <clipPath id="clip0">
+                <clipPath id="clip_loading">
                     <rect width={24} height={24} fill="currentColor" />
                 </clipPath>
             </defs>

+ 2 - 2
packages/semi-icons/src/icons/IconSpin.tsx

@@ -13,7 +13,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
             aria-hidden={true}
             {...props}
         >
-            <g clipPath="url(#clip0)">
+            <g clipPath="url(#clip_spin)">
                 <path
                     fillRule="evenodd"
                     clipRule="evenodd"
@@ -34,7 +34,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
                     <stop offset={0.301257} stopOpacity={0} stopColor="currentColor" />
                     <stop offset={0.466753} stopOpacity={1} stopColor="currentColor" />
                 </radialGradient>
-                <clipPath id="clip0">
+                <clipPath id="clip_spin">
                     <rect width={24} height={24} fill="currentColor" />
                 </clipPath>
             </defs>

+ 2 - 2
packages/semi-icons/src/icons/IconTriangleArrow.tsx

@@ -13,14 +13,14 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
             aria-hidden={true}
             {...props}
         >
-            <g clipPath="url(#clip0)">
+            <g clipPath="url(#clip_triangle_arrow)">
                 <path
                     d="M24 9L24 10C20 10 18.5 11 16.5 13C14.5 15 14 16 12 16C10 16 9.5 15 7.5 13C5.5 11 4 10 -4.37115e-08 10L0 9L24 9Z"
                     fill="currentColor"
                 />
             </g>
             <defs>
-                <clipPath id="clip0">
+                <clipPath id="clip_triangle_arrow">
                     <rect width={24} height={24} fill="currentColor" transform="translate(24) rotate(90)" />
                 </clipPath>
             </defs>

+ 2 - 2
packages/semi-icons/src/icons/IconUserAdd.tsx

@@ -13,7 +13,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
             aria-hidden={true}
             {...props}
         >
-            <g clipPath="url(#clip0)">
+            <g clipPath="url(#clip_user_add)">
                 <path
                     fillRule="evenodd"
                     clipRule="evenodd"
@@ -34,7 +34,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
                 />
             </g>
             <defs>
-                <clipPath id="clip0">
+                <clipPath id="clip_user_add">
                     <rect width={24} height={24} fill="currentColor" />
                 </clipPath>
             </defs>

文件差異過大導致無法顯示
+ 0 - 0
packages/semi-icons/src/svgs/loading.svg


+ 1 - 1
packages/semi-icons/src/svgs/spin.svg

@@ -1 +1 @@
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path fill-rule="evenodd" clip-rule="evenodd" d="M14.2 3.78966C9.66551 2.57466 5.00465 5.26561 3.78964 9.80007C3.12066 12.2967 3.63433 14.8301 4.99177 16.8102C5.46019 17.4935 5.28601 18.4271 4.60273 18.8955C3.91945 19.364 2.98581 19.1898 2.51739 18.5065C0.685557 15.8344 -0.0134454 12.4023 0.891867 9.02361C2.5357 2.88875 8.84157 -0.751945 14.9764 0.891885C21.1113 2.53572 24.752 8.84159 23.1082 14.9765C22.8937 15.7767 22.0712 16.2515 21.271 16.0371C20.4708 15.8227 19.996 15.0002 20.2104 14.2C21.4254 9.66553 18.7344 5.00467 14.2 3.78966Z" fill="url(#paint0_angular)"/></g><defs><radialGradient id="paint0_angular" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(12 12) rotate(15) scale(9.5 9.51825)"><stop/><stop offset="0.301257" stop-opacity="0" stop-color="#000" /><stop offset="0.466753" stop-opacity="1" stop-color="#000"/></radialGradient><clipPath id="clip0"><rect width="24" height="24" fill="white"/></clipPath></defs></svg>
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip_spin)"><path fill-rule="evenodd" clip-rule="evenodd" d="M14.2 3.78966C9.66551 2.57466 5.00465 5.26561 3.78964 9.80007C3.12066 12.2967 3.63433 14.8301 4.99177 16.8102C5.46019 17.4935 5.28601 18.4271 4.60273 18.8955C3.91945 19.364 2.98581 19.1898 2.51739 18.5065C0.685557 15.8344 -0.0134454 12.4023 0.891867 9.02361C2.5357 2.88875 8.84157 -0.751945 14.9764 0.891885C21.1113 2.53572 24.752 8.84159 23.1082 14.9765C22.8937 15.7767 22.0712 16.2515 21.271 16.0371C20.4708 15.8227 19.996 15.0002 20.2104 14.2C21.4254 9.66553 18.7344 5.00467 14.2 3.78966Z" fill="url(#paint0_angular)"/></g><defs><radialGradient id="paint0_angular" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(12 12) rotate(15) scale(9.5 9.51825)"><stop/><stop offset="0.301257" stop-opacity="0" stop-color="#000" /><stop offset="0.466753" stop-opacity="1" stop-color="#000"/></radialGradient><clipPath id="clip_spin"><rect width="24" height="24" fill="white"/></clipPath></defs></svg>

+ 1 - 1
packages/semi-icons/src/svgs/triangle_arrow.svg

@@ -1 +1 @@
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path d="M24 9L24 10C20 10 18.5 11 16.5 13C14.5 15 14 16 12 16C10 16 9.5 15 7.5 13C5.5 11 4 10 -4.37115e-08 10L0 9L24 9Z" fill="black"/></g><defs><clipPath id="clip0"><rect width="24" height="24" fill="white" transform="translate(24) rotate(90)"/></clipPath></defs></svg>
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip_triangle_arrow)"><path d="M24 9L24 10C20 10 18.5 11 16.5 13C14.5 15 14 16 12 16C10 16 9.5 15 7.5 13C5.5 11 4 10 -4.37115e-08 10L0 9L24 9Z" fill="black"/></g><defs><clipPath id="clip_triangle_arrow"><rect width="24" height="24" fill="white" transform="translate(24) rotate(90)"/></clipPath></defs></svg>

+ 1 - 1
packages/semi-icons/src/svgs/user_add.svg

@@ -1 +1 @@
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0)"><path fill-rule="evenodd" clip-rule="evenodd" d="M19.0796 19.8369C17.7027 17.6014 14.7559 15.5 10.4593 15.5C6.16267 15.5 3.21588 17.6014 1.83892 19.8369C1.19533 20.8817 2.10818 22 3.33535 22H17.5832C18.8104 22 19.7232 20.8817 19.0796 19.8369Z" fill="black"/><path fill-rule="evenodd" clip-rule="evenodd" d="M14.0499 10.4204C14.5723 10.2065 15.0693 9.55774 15.2962 8.71085C15.5913 7.60959 15.5327 6.62629 14.7285 6.3203C14.6504 2.48444 13.1369 1 9.96023 1C6.7838 1 5.27021 2.48424 5.19199 6.31952C4.38589 6.62467 4.32701 7.60866 4.62233 8.7108C4.84958 9.55892 5.34768 10.2083 5.87087 10.4213C6.71146 12.5727 8.24123 14.013 9.96023 14.013C11.6795 14.013 13.2094 12.5723 14.0499 10.4204Z" fill="black"/><path fill-rule="evenodd" clip-rule="evenodd" d="M20 1C19.4478 1 19 1.44772 19 2V4H17C16.4478 4 16 4.44772 16 5C16 5.55228 16.4478 6 17 6H19V8C19 8.55228 19.4478 9 20 9C20.5523 9 21 8.55228 21 8V6H23C23.5523 6 24 5.55228 24 5C24 4.44772 23.5523 4 23 4H21V2C21 1.44772 20.5523 1 20 1Z" fill="black"/></g><defs><clipPath id="clip0"><rect width="24" height="24" fill="white"/></clipPath></defs></svg>
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip_user_add)"><path fill-rule="evenodd" clip-rule="evenodd" d="M19.0796 19.8369C17.7027 17.6014 14.7559 15.5 10.4593 15.5C6.16267 15.5 3.21588 17.6014 1.83892 19.8369C1.19533 20.8817 2.10818 22 3.33535 22H17.5832C18.8104 22 19.7232 20.8817 19.0796 19.8369Z" fill="black"/><path fill-rule="evenodd" clip-rule="evenodd" d="M14.0499 10.4204C14.5723 10.2065 15.0693 9.55774 15.2962 8.71085C15.5913 7.60959 15.5327 6.62629 14.7285 6.3203C14.6504 2.48444 13.1369 1 9.96023 1C6.7838 1 5.27021 2.48424 5.19199 6.31952C4.38589 6.62467 4.32701 7.60866 4.62233 8.7108C4.84958 9.55892 5.34768 10.2083 5.87087 10.4213C6.71146 12.5727 8.24123 14.013 9.96023 14.013C11.6795 14.013 13.2094 12.5723 14.0499 10.4204Z" fill="black"/><path fill-rule="evenodd" clip-rule="evenodd" d="M20 1C19.4478 1 19 1.44772 19 2V4H17C16.4478 4 16 4.44772 16 5C16 5.55228 16.4478 6 17 6H19V8C19 8.55228 19.4478 9 20 9C20.5523 9 21 8.55228 21 8V6H23C23.5523 6 24 5.55228 24 5C24 4.44772 23.5523 4 23 4H21V2C21 1.44772 20.5523 1 20 1Z" fill="black"/></g><defs><clipPath id="clip_user_add"><rect width="24" height="24" fill="white"/></clipPath></defs></svg>

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-illustrations",
-  "version": "2.25.0-beta.0",
+  "version": "2.25.2",
   "description": "semi illustrations",
   "keywords": [
     "semi",

+ 4 - 4
packages/semi-illustrations/src/illustrations/IllustrationFailure.tsx

@@ -163,7 +163,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
                 d="M43.4007 78.8699C43.461 78.108 44.1276 77.5392 44.8895 77.5995C45.1407 77.6194 45.3966 77.6371 45.6572 77.6525C45.9083 77.6673 46.1551 77.6804 46.3977 77.6918C47.1612 77.7276 47.7511 78.3755 47.7153 79.1389C47.6795 79.9024 47.0316 80.4923 46.2681 80.4565C46.014 80.4446 45.756 80.4309 45.4941 80.4154C45.2155 80.399 44.9411 80.38 44.671 80.3586C43.9091 80.2983 43.3404 79.6318 43.4007 78.8699Z"
                 fill="var(--semi-color-primary)"
             />
-            <g clipPath="url(#clip0)">
+            <g clipPath="url(#clip_failure_96_39_65)">
                 <path
                     fillRule="evenodd"
                     clipRule="evenodd"
@@ -186,13 +186,13 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
                 fill="white"
                 stroke="#515151"
             />
-            <mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x={98} y={44} width={36} height={27}>
+            <mask id="mask_failure_130_526" mask-type="alpha" maskUnits="userSpaceOnUse" x={98} y={44} width={36} height={27}>
                 <path
                     d="M130.526 62.1048C130.034 58.8 121.013 47.3537 114.448 47.0738L101.772 51.595L108.238 65.7517L125.493 68.8189C127.335 67.6825 130.92 64.7486 130.526 62.1048Z"
                     fill="#C4C4C4"
                 />
             </mask>
-            <g mask="url(#mask0)">
+            <g mask="url(#mask_failure_130_526)">
                 <path
                     d="M127.206 63.618C123.49 67.5606 117.328 67.774 113.443 64.1127C109.559 60.4514 109.408 54.2878 113.124 50.3452C116.84 46.4026 123.002 46.1893 126.886 49.8505C130.771 53.5118 130.922 59.6755 127.206 63.618Z"
                     fill="white"
@@ -297,7 +297,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
                 fill="white"
             />
             <defs>
-                <clipPath id="clip0">
+                <clipPath id="clip_failure_96_39_65">
                     <rect
                         width={44.4004}
                         height={44.4627}

+ 4 - 4
packages/semi-illustrations/src/illustrations/IllustrationFailureDark.tsx

@@ -163,7 +163,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
                 d="M43.4007 78.8694C43.461 78.1075 44.1276 77.5387 44.8895 77.5991C45.1407 77.619 45.3966 77.6366 45.6572 77.652C45.9083 77.6668 46.1551 77.6799 46.3977 77.6913C47.1612 77.7271 47.7511 78.375 47.7153 79.1384C47.6795 79.9019 47.0316 80.4918 46.2681 80.456C46.014 80.4441 45.756 80.4304 45.4941 80.4149C45.2155 80.3985 44.9411 80.3795 44.671 80.3582C43.9091 80.2978 43.3404 79.6313 43.4007 78.8694Z"
                 fill="var(--semi-color-primary)"
             />
-            <g clipPath="url(#clip0)">
+            <g clipPath="url(#clip_failure_dark_46_27)">
                 <path
                     fillRule="evenodd"
                     clipRule="evenodd"
@@ -186,13 +186,13 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
                 fill="#C6CACD"
                 stroke="#1C1F23"
             />
-            <mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x={101} y={47} width={30} height={22}>
+            <mask id="mask_failure_dark_130_527" mask-type="alpha" maskUnits="userSpaceOnUse" x={101} y={47} width={30} height={22}>
                 <path
                     d="M130.527 62.1043C130.034 58.7995 121.014 47.3532 114.449 47.0733L101.773 51.5945L108.238 65.7512L125.493 68.8184C127.335 67.682 130.921 64.7481 130.527 62.1043Z"
                     fill="#C4C4C4"
                 />
             </mask>
-            <g mask="url(#mask0)">
+            <g mask="url(#mask_failure_dark_130_527)">
                 <path
                     d="M127.206 63.6176C123.49 67.5601 117.328 67.7735 113.443 64.1122C109.559 60.4509 109.408 54.2873 113.124 50.3447C116.84 46.4022 123.002 46.1888 126.887 49.8501C130.771 53.5113 130.922 59.675 127.206 63.6176Z"
                     fill="#C6CACD"
@@ -297,7 +297,7 @@ function SvgComponent(props: React.SVGProps<SVGSVGElement>) {
                 fill="#C6CACD"
             />
             <defs>
-                <clipPath id="clip0">
+                <clipPath id="clip_failure_dark_46_27">
                     <rect
                         width={44.4004}
                         height={44.4627}

+ 4 - 4
packages/semi-illustrations/src/svgs/failure-dark.svg

@@ -36,16 +36,16 @@
 <path d="M37.6691 75.9706C38.0867 76.1589 38.5348 76.3348 39.0157 76.4968C39.7399 76.7409 40.1292 77.5259 39.8852 78.2502C39.6411 78.9744 38.8561 79.3637 38.1318 79.1197C37.5698 78.9303 37.0365 78.7215 36.5311 78.4935C36.5767 78.3355 37.0282 76.6692 37.2482 75.8557C37.3901 75.871 37.532 75.9087 37.6691 75.9706Z" fill="var(--semi-color-primary)"/>
 <path d="M55.0501 78.5666C55.1708 79.3213 54.6569 80.0309 53.9022 80.1516C53.4091 80.2305 52.8957 80.2968 52.3599 80.3504C51.5994 80.4264 50.9213 79.8715 50.8453 79.111C50.7692 78.3505 51.3241 77.6724 52.0846 77.5964C52.5696 77.5479 53.0289 77.4884 53.465 77.4186C54.2197 77.2979 54.9294 77.8119 55.0501 78.5666Z" fill="var(--semi-color-primary)"/>
 <path d="M43.4007 78.8694C43.461 78.1075 44.1276 77.5387 44.8895 77.5991C45.1407 77.619 45.3966 77.6366 45.6572 77.652C45.9083 77.6668 46.1551 77.6799 46.3977 77.6913C47.1612 77.7271 47.7511 78.375 47.7153 79.1384C47.6795 79.9019 47.0316 80.4918 46.2681 80.456C46.014 80.4441 45.756 80.4304 45.4941 80.4149C45.2155 80.3985 44.9411 80.3795 44.671 80.3582C43.9091 80.2978 43.3404 79.6313 43.4007 78.8694Z" fill="var(--semi-color-primary)"/>
-<g clip-path="url(#clip0)">
+<g clip-path="url(#clip_failure_dark_46_27)">
 <path fill-rule="evenodd" clip-rule="evenodd" d="M30.9045 46.8694C28.9309 39.7229 33.1312 32.3059 40.2688 30.3361C47.4063 28.3663 54.8182 32.5787 56.7918 39.7252C59.4325 49.2872 52.025 69.0037 51.649 69.6235C51.5459 69.792 51.387 69.9063 51.21 69.9551C51.0331 70.0039 50.838 69.9874 50.6631 69.8956C50.0222 69.5564 33.5451 56.4314 30.9045 46.8694ZM45.2597 48.4085C42.0243 49.3014 38.6639 47.3918 37.7692 44.1521C36.8745 40.9124 38.7791 37.5498 42.0143 36.657C45.2496 35.7641 48.6101 37.6737 49.5048 40.9134C50.3995 44.153 48.495 47.5156 45.2597 48.4085Z" fill="var(--semi-color-primary)"/>
 </g>
 <path d="M102.315 100.689C99.5168 102.244 96.6807 103.536 92.95 105.268L94.4346 109.189C96.5664 108.23 100.589 103.162 102.315 100.689Z" fill="#1C1F23"/>
 <path fill-rule="evenodd" clip-rule="evenodd" d="M120.997 63.0355C129.853 65.1334 133.432 56.5347 134.117 54.2696C134.801 52.0046 128.52 42.4111 126.122 43.0773C125.987 43.115 125.829 43.157 125.652 43.2038C125.593 43.2193 125.532 43.2355 125.469 43.2521L125.469 43.2521C125.081 43.3543 124.616 43.4769 124.097 43.623C120.463 44.6457 114.172 46.8222 113.124 51.2465C111.829 56.7149 115.529 61.7401 120.997 63.0355Z" fill="#1C1F23"/>
 <path d="M134.496 64.7273C134.066 65.1978 133.296 65.4733 132.167 65.4633C131.049 65.4534 129.661 65.1618 128.113 64.6007C125.022 63.4797 121.389 61.3163 118.175 58.3814C114.923 55.4111 112.198 52.5779 110.583 49.9642C108.965 47.3474 108.529 45.0749 109.601 43.1176C110.147 42.1201 110.88 41.5274 111.71 41.2601C112.548 40.9902 113.536 41.0356 114.61 41.4129C116.767 42.1707 119.176 44.2399 121.092 47.4249C124.482 53.0614 127.976 55.6582 130.796 57.7547C131.276 58.1116 131.737 58.4539 132.174 58.7943C133.624 59.9228 134.451 61.1773 134.784 62.2682C135.12 63.3716 134.939 64.242 134.496 64.7273Z" fill="#C6CACD" stroke="#1C1F23"/>
-<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="101" y="47" width="30" height="22">
+<mask id="mask_failure_dark_130_527" mask-type="alpha" maskUnits="userSpaceOnUse" x="101" y="47" width="30" height="22">
 <path d="M130.527 62.1043C130.034 58.7995 121.014 47.3532 114.449 47.0733L101.773 51.5945L108.238 65.7512L125.493 68.8184C127.335 67.682 130.921 64.7481 130.527 62.1043Z" fill="#C4C4C4"/>
 </mask>
-<g mask="url(#mask0)">
+<g mask="url(#mask_failure_dark_130_527)">
 <path d="M127.206 63.6176C123.49 67.5601 117.328 67.7735 113.443 64.1122C109.559 60.4509 109.408 54.2873 113.124 50.3447C116.84 46.4022 123.002 46.1888 126.887 49.8501C130.771 53.5113 130.922 59.675 127.206 63.6176Z" fill="#C6CACD" stroke="#1C1F23"/>
 <path fill-rule="evenodd" clip-rule="evenodd" d="M116.79 47.3517L116.774 47.7162C119.629 48.2226 125.342 50.307 125.347 54.593C125.353 59.9505 117.907 63.9254 110.882 58.0528C110.54 57.7671 110.224 57.5209 109.933 57.3112C109.986 59.9423 111.047 62.5402 113.1 64.4759C117.19 68.3305 123.668 68.0997 127.57 63.9603C131.471 59.821 131.319 53.3406 127.229 49.486C124.355 46.7768 120.301 46.0858 116.79 47.3517Z" fill="#1C1F23"/>
 <path d="M119.341 55.001C119.649 54.6958 119.652 54.1984 119.347 53.89C119.042 53.5816 118.544 53.5789 118.236 53.8841C117.928 54.1892 117.925 54.6865 118.23 54.995C118.535 55.3034 119.032 55.3061 119.341 55.001Z" fill="#1C1F23"/>
@@ -71,7 +71,7 @@
 <path fill-rule="evenodd" clip-rule="evenodd" d="M133.433 123.46L142.64 121.651L142.958 123.273L133.752 125.082L133.433 123.46Z" fill="#C6CACD"/>
 <path fill-rule="evenodd" clip-rule="evenodd" d="M145.504 133.113L154.711 131.305L155.029 132.927L145.823 134.735L145.504 133.113Z" fill="#C6CACD"/>
 <defs>
-<clipPath id="clip0">
+<clipPath id="clip_failure_dark_46_27">
 <rect width="44.4004" height="44.4627" fill="white" transform="matrix(0.963965 -0.266028 0.266198 0.963918 18.4214 34.6221)"/>
 </clipPath>
 </defs>

+ 4 - 4
packages/semi-illustrations/src/svgs/failure.svg

@@ -37,16 +37,16 @@
 <path d="M37.6691 75.9711C38.0867 76.1594 38.5348 76.3353 39.0157 76.4973C39.7399 76.7414 40.1292 77.5264 39.8852 78.2507C39.6411 78.9749 38.8561 79.3642 38.1318 79.1201C37.5698 78.9307 37.0365 78.722 36.5311 78.494C36.5767 78.336 37.0282 76.6697 37.2482 75.8562C37.3901 75.8715 37.532 75.9092 37.6691 75.9711Z" fill="var(--semi-color-primary)"/>
 <path d="M55.0501 78.5671C55.1708 79.3217 54.6569 80.0314 53.9022 80.1521C53.4091 80.231 52.8957 80.2973 52.3599 80.3508C51.5994 80.4269 50.9213 79.872 50.8453 79.1115C50.7692 78.351 51.3241 77.6728 52.0846 77.5968C52.5696 77.5484 53.0289 77.4889 53.465 77.4191C54.2197 77.2984 54.9294 77.8124 55.0501 78.5671Z" fill="var(--semi-color-primary)"/>
 <path d="M43.4007 78.8699C43.461 78.108 44.1276 77.5392 44.8895 77.5995C45.1407 77.6194 45.3966 77.6371 45.6572 77.6525C45.9083 77.6673 46.1551 77.6804 46.3977 77.6918C47.1612 77.7276 47.7511 78.3755 47.7153 79.1389C47.6795 79.9024 47.0316 80.4923 46.2681 80.4565C46.014 80.4446 45.756 80.4309 45.4941 80.4154C45.2155 80.399 44.9411 80.38 44.671 80.3586C43.9091 80.2983 43.3404 79.6318 43.4007 78.8699Z" fill="var(--semi-color-primary)"/>
-<g clip-path="url(#clip0)">
+<g clip-path="url(#clip_failure_96_39_65)">
 <path fill-rule="evenodd" clip-rule="evenodd" d="M30.9046 46.8694C28.931 39.7229 33.1313 32.3059 40.2689 30.3361C47.4064 28.3663 54.8184 32.5787 56.792 39.7252C59.4326 49.2872 52.0251 69.0037 51.6492 69.6235C51.546 69.792 51.3871 69.9063 51.2102 69.9551C51.0332 70.0039 50.8382 69.9874 50.6632 69.8956C50.0223 69.5564 33.5453 56.4314 30.9046 46.8694ZM45.2598 48.4085C42.0245 49.3014 38.664 47.3918 37.7693 44.1521C36.8746 40.9124 38.7792 37.5498 42.0145 36.657C45.2497 35.7641 48.6103 37.6737 49.5049 40.9134C50.3996 44.153 48.4951 47.5156 45.2598 48.4085Z" fill="var(--semi-color-primary)"/>
 </g>
 <path d="M102.315 100.69C99.5168 102.244 96.6807 103.536 92.95 105.268L94.4346 109.189C96.5664 108.23 100.589 103.162 102.315 100.69Z" fill="#515151"/>
 <path fill-rule="evenodd" clip-rule="evenodd" d="M120.997 63.0355C129.853 65.1334 133.432 56.5347 134.117 54.2696C134.801 52.0046 128.52 42.4111 126.122 43.0773C125.998 43.1118 125.855 43.15 125.696 43.1922C125.623 43.2113 125.548 43.2313 125.469 43.2521C122.367 44.0697 114.322 46.1902 113.124 51.2465C111.829 56.7149 115.529 61.7401 120.997 63.0355Z" fill="#515151"/>
 <path d="M134.495 64.7278C134.066 65.1983 133.296 65.4738 132.167 65.4638C131.049 65.4538 129.661 65.1623 128.113 64.6012C125.022 63.4801 121.389 61.3167 118.175 58.3818C114.923 55.4115 112.198 52.5784 110.582 49.9647C108.965 47.3479 108.529 45.0754 109.601 43.1181C110.147 42.1205 110.88 41.5279 111.71 41.2605C112.548 40.9907 113.536 41.0361 114.61 41.4134C116.767 42.1712 119.176 44.2404 121.092 47.4254C124.482 53.0618 127.976 55.6587 130.796 57.7552C131.276 58.112 131.737 58.4544 132.174 58.7948C133.623 59.9233 134.451 61.1778 134.783 62.2687C135.12 63.372 134.939 64.2425 134.495 64.7278Z" fill="white" stroke="#515151"/>
-<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="98" y="44" width="36" height="27">
+<mask id="mask_failure_130_526" mask-type="alpha" maskUnits="userSpaceOnUse" x="98" y="44" width="36" height="27">
 <path d="M130.526 62.1048C130.034 58.8 121.013 47.3537 114.448 47.0738L101.772 51.595L108.238 65.7517L125.493 68.8189C127.335 67.6825 130.92 64.7486 130.526 62.1048Z" fill="#C4C4C4"/>
 </mask>
-<g mask="url(#mask0)">
+<g mask="url(#mask_failure_130_526)">
 <path d="M127.206 63.618C123.49 67.5606 117.328 67.774 113.443 64.1127C109.559 60.4514 109.408 54.2878 113.124 50.3452C116.84 46.4026 123.002 46.1893 126.886 49.8505C130.771 53.5118 130.922 59.6755 127.206 63.618Z" fill="white" stroke="#515151"/>
 <path fill-rule="evenodd" clip-rule="evenodd" d="M116.79 47.3522L116.774 47.7167C119.629 48.2231 125.342 50.3075 125.347 54.5935C125.353 59.951 117.907 63.9259 110.882 58.0533C110.54 57.7676 110.224 57.5214 109.933 57.3117C109.986 59.9427 111.047 62.5407 113.1 64.4764C117.19 68.331 123.668 68.1002 127.57 63.9608C131.471 59.8215 131.319 53.3411 127.229 49.4865C124.355 46.7773 120.301 46.0863 116.79 47.3522Z" fill="#515151"/>
 <path d="M119.341 55.001C119.649 54.6958 119.652 54.1984 119.347 53.89C119.042 53.5816 118.544 53.5789 118.236 53.8841C117.928 54.1892 117.925 54.6865 118.23 54.995C118.535 55.3034 119.032 55.3061 119.341 55.001Z" fill="#515151"/>
@@ -72,7 +72,7 @@
 <path fill-rule="evenodd" clip-rule="evenodd" d="M133.433 123.46L142.64 121.651L142.958 123.273L133.752 125.082L133.433 123.46Z" fill="white"/>
 <path fill-rule="evenodd" clip-rule="evenodd" d="M145.504 133.113L154.711 131.304L155.029 132.926L145.823 134.735L145.504 133.113Z" fill="white"/>
 <defs>
-<clipPath id="clip0">
+<clipPath id="clip_failure_96_39_65">
 <rect width="44.4004" height="44.4627" transform="matrix(0.963965 -0.266028 0.266198 0.963918 18.4214 34.6221)" fill="white"/>
 </clipPath>
 </defs>

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-next",
-    "version": "2.25.0-beta.0",
+    "version": "2.25.2",
     "description": "Plugin that support Semi Design in Next.js",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-scss-compile",
-  "version": "2.25.0-beta.0",
+  "version": "2.25.2",
   "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.25.0-beta.0",
+    "version": "2.25.2",
     "description": "semi-theme-default",
     "keywords": [
         "semi-theme",

+ 9 - 10
packages/semi-ui/cascader/_story/cascader.stories.jsx

@@ -523,17 +523,16 @@ export const Searchable = () => {
       <br />
       <br />
       <div>
-          v2.5 起,filterTreeNode=false,且 label 为 ReactNode
-          时,无配合displayRender 使用,回显到input的内容也是符合预期
-        </div>
-        <Cascader
-          style={{ width: 300 }}
-          treeData={treedataWithNodeLabel}
-          placeholder="宁波为 ReactNode"
-          
-          defaultValue={['zhejiang', 'ningbo', 'jiangbei']}
-        />
+        v2.5 起,filterTreeNode=false,且 label 为 ReactNode
+        时,无配合displayRender 使用,回显到input的内容也是符合预期
       </div>
+      <Cascader
+        style={{ width: 300 }}
+        treeData={treedataWithNodeLabel}
+        placeholder="宁波为 ReactNode"
+        defaultValue={['zhejiang', 'ningbo', 'jiangbei']}
+      />
+    </div>
   );
 };
 Searchable.parameters = {

+ 11 - 11
packages/semi-ui/overflowList/__test__/overflowList.test.js

@@ -4,17 +4,17 @@ import { BASE_CLASS_PREFIX } from '@douyinfe/semi-foundation/base/constants';
 import OverflowList from '../index';
 
 describe('OverflowList', () => {
-    it('render basic', () => {
-        const node = mount(
-            <OverflowList
-                items={[{ key: 'alarm' }, { key: 'bookmark' }, { key: 'camera' }, { key: 'duration' }]}
-                visibleItemRenderer={item => <div>{item.key}</div>}
-                overflowRenderer={() => null}
-            />
-        );
-        expect(node.find(`.${BASE_CLASS_PREFIX}-overflow-list`).exists()).toEqual(true);
-        expect(node.find(`.${BASE_CLASS_PREFIX}-overflow-list-spacer`).exists()).toEqual(true);
-    });
+    // it('render basic', () => {
+    //     const node = mount(
+    //         <OverflowList
+    //             items={[{ key: 'alarm' }, { key: 'bookmark' }, { key: 'camera' }, { key: 'duration' }]}
+    //             visibleItemRenderer={item => <div>{item.key}</div>}
+    //             overflowRenderer={() => null}
+    //         />
+    //     );
+    //     expect(node.find(`.${BASE_CLASS_PREFIX}-overflow-list`).exists()).toEqual(true);
+    //     // expect(node.find(`.${BASE_CLASS_PREFIX}-overflow-list-spacer`).exists()).toEqual(true);
+    // });
     it('render scroll', () => {
         const node = mount(
             <OverflowList

+ 48 - 5
packages/semi-ui/overflowList/_story/overflowList.stories.jsx

@@ -23,8 +23,16 @@ const ITEMS = [
   { href: '#', icon: <IconFolderOpen />, key: 'Photos' },
   { href: '#', icon: <IconFolderOpen />, key: 'Wednesday' },
   { icon: <IconBolt />, key: 'image', current: true },
+  { icon: <IconFolderOpen />, key: 'Users2' },
+  { icon: <IconFolderOpen />, key: 'Users3' },
+  { icon: <IconFolderOpen />, key: 'Users4' },
+  { icon: <IconFolderOpen />, key: 'Users5' },
+  { icon: <IconFolderOpen />, key: 'Users6' },
+  { icon: <IconFolderOpen />, key: 'Users7' },
 ];
-
+const createItems = (length = 10) => {
+  return Array(length).fill().map(()=>({ key: Math.random() }))
+}
 class Demo extends React.Component {
   renderOverflow = items => {
     // console.log('overflow items: ', items);
@@ -40,9 +48,9 @@ class Demo extends React.Component {
   };
   render() {
     return (
-      <div style={{ width: '30%' }}>
+      <div style={{ width: '500px' }}>
         <OverflowList
-          items={ITEMS}
+          items={createItems(80)}
           overflowRenderer={this.renderOverflow}
           visibleItemRenderer={this.renderItem}
         />
@@ -50,8 +58,43 @@ class Demo extends React.Component {
     );
   }
 }
-
-export const ASimpleOverflowList = () => <Demo />;
+export const ASimpleOverflowList = () => {
+  const [width, setWidth] = useState(100)
+  const renderOverflow = (items) => {
+      // console.log('overflow items: ', items);
+      return (items.length ? <Tag style={{ flex: '0 0 auto' }}>+{items.length}</Tag> : null)
+  }
+  const renderItem = (item, ind) => {
+      // console.log('visible item: ', item);
+      return (
+          <Tag color='blue' key={item.key} style={{  flex: '0 0 auto' }}>
+              <Icon type={item.icon} style={{ }}/>
+              {item.key}
+          </Tag>
+      )
+  }
+  const items =  Array.from(new Array(100)).map((i, ind) => ({ icon: "alarm", key: `${ind}-alarm` }))
+      
+  return (
+      <div style={{width:'800px'}}>
+          <Slider step={1} value={width} onChange={(value) => setWidth(value)} />
+          <div><span>.</span></div>
+          <br/>
+          <br/>
+          <div style={{ maxWidth: `${width}%` }}>
+              <OverflowList
+                  items={items}
+                  onOverflow={e=>{
+                    console.log('🚀 ~~~~~~ ASimpleOverflowList ~~~~~~ object', e)
+                  }}
+                  // minVisibleItems={3}
+                  overflowRenderer={renderOverflow}
+                  visibleItemRenderer={renderItem}
+              />
+          </div>
+      </div>
+  );
+};
 
 ASimpleOverflowList.story = {
   name: 'a simple semi overflow list',

+ 137 - 58
packages/semi-ui/overflowList/index.tsx

@@ -3,8 +3,8 @@ import React, { CSSProperties, ReactNode, MutableRefObject, RefCallback, Key, Re
 import cls from 'classnames';
 import BaseComponent from '../_base/baseComponent';
 import PropTypes from 'prop-types';
-import { isEqual, omit, isNull, isUndefined } from 'lodash';
-import { cssClasses, strings } from '@douyinfe/semi-foundation/overflowList/constants';
+import { isEqual, omit, isNull, isUndefined, isFunction, get } from 'lodash';
+import { cssClasses, strings, numbers } from '@douyinfe/semi-foundation/overflowList/constants';
 import ResizeObserver, { ResizeEntry } from '../resizeObserver';
 import IntersectionObserver from './intersectionObserver';
 
@@ -33,7 +33,8 @@ export interface OverflowListProps {
     threshold?: number;
     visibleItemRenderer?: (item: OverflowItem, index: number) => ReactElement;
     wrapperClassName?: string;
-    wrapperStyle?: CSSProperties
+    wrapperStyle?: CSSProperties;
+    itemKey?: Key | ((item: OverflowItem) => Key)
 }
 
 export interface OverflowListState {
@@ -42,7 +43,13 @@ export interface OverflowListState {
     overflow?: Array<OverflowItem>;
     visible?: Array<OverflowItem>;
     visibleState?: Map<string, boolean>;
-    prevProps?: OverflowListProps
+    prevProps?: OverflowListProps;
+    itemSizeMap?: Map<Key, number>;
+    containerWidth?: number;
+    maxCount?: number;
+    overflowStatus?: 'calculating' | 'overflowed' | 'normal';
+    pivot?: number;
+    overflowWidth?: number
 }
 
 // reference to https://github.com/palantir/blueprint/blob/1aa71605/packages/core/src/components/overflow-list/overflowList.tsx#L34
@@ -54,6 +61,7 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
         renderMode: 'collapse',
         threshold: 0.75,
         visibleItemRenderer: (): ReactElement => null,
+        onOverflow: () => null,
     };
     static propTypes = {
         // if render in scroll mode, key is required in items
@@ -79,8 +87,14 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
             direction: OverflowDirection.GROW,
             lastOverflowCount: 0,
             overflow: [],
-            visible: props.items,
+            visible: [],
+            containerWidth: 0,
             visibleState: new Map(),
+            itemSizeMap: new Map(),
+            overflowStatus: "calculating",
+            pivot: 0,
+            overflowWidth: 0,
+            maxCount: 0,
         };
         this.foundation = new OverflowListFoundation(this.adapter);
         this.previousWidths = new Map();
@@ -100,8 +114,16 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
             // reset visible state if the above props change.
             newState.direction = OverflowDirection.GROW;
             newState.lastOverflowCount = 0;
-            newState.overflow = [];
-            newState.visible = props.items;
+            if (props.renderMode === RenderMode.SCROLL) {
+                newState.visible = props.items;
+                newState.overflow = [];
+            } else {
+                newState.visible = [];
+                newState.overflow = [];
+            }
+            newState.pivot = 0;
+            newState.maxCount = 0;
+            newState.overflowStatus = "calculating";
         }
         return newState;
     }
@@ -117,7 +139,8 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
             },
             notifyIntersect: (res): void => {
                 this.props.onIntersect && this.props.onIntersect(res);
-            }
+            },
+            getItemSizeMap: () => this.itemSizeMap
         };
     }
 
@@ -128,28 +151,13 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
 
     previousWidths: Map<Element, number>;
 
-    itemSizeMap: Map<string, any>;
+    itemSizeMap: Map<string, number>;
 
     isScrollMode = (): boolean => {
         const { renderMode } = this.props;
         return renderMode === RenderMode.SCROLL;
     };
 
-    componentDidMount(): void {
-        this.repartition(false);
-    }
-
-    shouldComponentUpdate(_nextProps: OverflowListProps, nextState: OverflowListState): boolean {
-        // We want this component to always re-render, even when props haven't changed, so that
-        // changes in the renderers' behavior can be reflected.
-        // The following statement prevents re-rendering only in the case where the state changes
-        // identity (i.e. setState was called), but the state is still the same when
-        // shallow-compared to the previous state.
-        const currState = omit(this.state, 'prevProps');
-        const comingState = omit(nextState, 'prevProps');
-        return !(currState !== comingState && isEqual(currState, comingState));
-    }
-
     componentDidUpdate(prevProps: OverflowListProps, prevState: OverflowListState): void {
 
 
@@ -157,41 +165,38 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
             this.itemRefs = {};
         }
 
+        const { overflow, containerWidth, visible, overflowStatus } = this.state;
 
-        if (!isEqual(omit(prevState, 'prevProps'), omit(this.state, 'prevProps'))) {
-            this.repartition(false);
+        if (this.isScrollMode() || overflowStatus !== "calculating") {
+            return;
         }
-        const { direction, overflow, lastOverflowCount } = this.state;
-        if (
-            // if a resize operation has just completed (transition to NONE)
-            direction === OverflowDirection.NONE &&
-            direction !== prevState.direction &&
-            overflow.length !== lastOverflowCount
-        ) {
-            this.props.onOverflow && this.props.onOverflow(overflow);
+        if (visible.length === 0 && overflow.length === 0 && this.props.items.length !== 0) {
+            // 推测container最多能渲染的数量
+            // Figure out the maximum number of items in this container
+            const maxCount = Math.min(this.props.items.length, Math.floor(containerWidth / numbers.MINIMUM_HTML_ELEMENT_WIDTH));
+            // 如果collapseFrom是start, 第一次用来计算容量时,倒转列表顺序渲染
+            // If collapseFrom === start, render item from end to start. Figuring out how many items in the end could fit in container.
+            const isCollapseFromStart = this.props.collapseFrom === Boundary.START;
+            const visible = isCollapseFromStart ? this.foundation.getReversedItems().slice(0, maxCount) : this.props.items.slice(0, maxCount);
+            const overflow = isCollapseFromStart ? this.foundation.getReversedItems().slice(maxCount) : this.props.items.slice(maxCount);
+            this.setState({
+                overflowStatus: 'calculating',
+                visible,
+                overflow,
+                maxCount: maxCount,
+            });
+            this.itemSizeMap.clear();
+        } else {
+            this.foundation.handleCollapseOverflow();
         }
     }
 
     resize = (entries: Array<ResizeEntry> = []): void => {
-        // if any parent is growing, assume we have more room than before
-        const growing = entries.some(entry => {
-            const previousWidth = this.previousWidths.get(entry.target) || 0;
-            return entry.contentRect.width > previousWidth;
+        const containerWidth = entries[0]?.target.clientWidth;
+        this.setState({
+            containerWidth,
+            overflowStatus: 'calculating',
         });
-        this.repartition(growing);
-        entries.forEach(entry => this.previousWidths.set(entry.target, entry.contentRect.width));
-    };
-
-    repartition = (growing: boolean): void => {
-        // if not mounted or scroll mode, we do not
-        if (isNull(this.spacer) || isUndefined(this.spacer) || this.isScrollMode()) {
-            return;
-        }
-        // spacer has flex-shrink and width 1px so if it's much smaller then we know to shrink
-        const state = growing ?
-            OverflowDirection.GROW :
-            this.spacer.getBoundingClientRect().width < 0.9 ? OverflowDirection.SHRINK : OverflowDirection.NONE;
-        this.foundation.handlePartition(state);
     };
 
     reintersect = (entries: Array<IntersectionObserverEntry>): void => {
@@ -212,11 +217,43 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
         return this.props.overflowRenderer(overflow);
     };
 
+    getItemKey = (item, defalutKey?: Key) => {
+        const { itemKey } = this.props;
+        if (isFunction(itemKey)) {
+            return itemKey(item);
+        }
+        return get(item, itemKey || 'key', defalutKey);
+    }
+
     renderItemList = () => {
         const { className, wrapperClassName, wrapperStyle, style, visibleItemRenderer, renderMode, collapseFrom } = this.props;
 
-        const { visible } = this.state;
-        const overflow = this.renderOverflow();
+        const { visible, overflowStatus } = this.state;
+        let overflow = this.renderOverflow();
+        if (!this.isScrollMode()) {
+            if (Array.isArray(overflow)) {
+                overflow = (
+                    <>
+                        {overflow}
+                    </>
+                );
+            }
+            if (React.isValidElement(overflow)) {
+                const child = React.cloneElement(overflow);
+                overflow = (<ResizeObserver
+                    onResize={([entry]) => {
+                        this.setState({
+                            overflowWidth: entry.target.clientWidth,
+                            overflowStatus: 'calculating'
+                        });
+                    }}
+                >
+                    <div className={`${prefixCls}-overflow`}>
+                        {child}
+                    </div>
+                </ResizeObserver>);
+            }
+        }
         const inner =
             renderMode === RenderMode.SCROLL ?
                 [
@@ -229,7 +266,7 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
                         style={{ ...wrapperStyle }}
                         key={`${prefixCls}-scroll-wrapper`}
                     >
-                        {visible.map(visibleItemRenderer).map((item: ReactElement, ind) => {
+                        {visible.map(visibleItemRenderer).map((item: ReactElement) => {
                             const { forwardRef, key } = item as any;
                             return React.cloneElement(item, {
                                 ref: (node: any) => this.mergeRef(forwardRef, node, key),
@@ -242,21 +279,63 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
                 ] :
                 [
                     collapseFrom === Boundary.START ? overflow : null,
-                    visible.map(visibleItemRenderer),
+                    visible.map((item, idx) => {
+                        const { key } = item;
+                        const element = visibleItemRenderer(item, idx);
+                        const child = React.cloneElement(element);
+                        return (
+                            <ResizeObserver
+                                key={key}
+                                onResize={([entry]) => this.onItemResize(entry, item, idx)}
+                            >
+                                {/* 用div包起来,可以直接在resize回调中拿到宽度,不用通过获取元素的padding, margin, border-width求和计算宽度*/}
+                                {/* This div wrap can get width directly rather than do the math of padding, margin, border-width*/}
+                                <div key={key} className={`${prefixCls}-item`}>
+                                    {child}
+                                </div>
+                            </ResizeObserver>);
+                    }),
                     collapseFrom === Boundary.END ? overflow : null,
-                    <div className={`${prefixCls}-spacer`} ref={ref => (this.spacer = ref)} key={`${prefixCls}-spacer`} />,
                 ];
         const list = React.createElement(
             'div',
             {
                 className: cls(`${prefixCls}`, className),
-                style,
+                style: {
+                    ...style,
+                    ...(renderMode === RenderMode.COLLAPSE ? {
+                        maxWidth: '100%',
+                        visibility: overflowStatus === "calculating" ? "hidden" : "visible",
+                    } : null)
+                },
             },
             ...inner
         );
         return list;
     };
 
+    onItemResize = (entry: ResizeEntry, item: OverflowItem, idx: number) => {
+        const key = this.getItemKey(item, idx);
+        const width = this.itemSizeMap.get(key);
+        if (!width) {
+            this.itemSizeMap.set(key, entry.target.clientWidth);
+        } else if (width !== entry.target.clientWidth) {
+            // 某个item发生resize后,重新计算
+            this.itemSizeMap.set(key, entry.target.clientWidth);
+            this.setState({
+                overflowStatus: 'calculating'
+            });
+        }
+        const { maxCount } = this.state;
+        // 已经按照最大值maxCount渲染完毕,触发真正的渲染。(-1 是overflow部分会占1)
+        // Already rendered maxCount items, trigger the real rendering. (-1 for the overflow part)
+        if (this.itemSizeMap.size === maxCount - 1) {
+            this.setState({
+                overflowStatus: 'calculating'
+            });
+        }
+    }
+
     render(): ReactNode {
         const list = this.renderItemList();
         const { renderMode } = this.props;

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-ui",
-    "version": "2.25.0-beta.0",
+    "version": "2.25.2",
     "description": "",
     "main": "lib/cjs/index.js",
     "module": "lib/es/index.js",
@@ -17,12 +17,12 @@
         "lib/*"
     ],
     "dependencies": {
-        "@douyinfe/semi-animation": "2.25.0-beta.0",
-        "@douyinfe/semi-animation-react": "2.25.0-beta.0",
-        "@douyinfe/semi-foundation": "2.25.0-beta.0",
-        "@douyinfe/semi-icons": "2.25.0-beta.0",
-        "@douyinfe/semi-illustrations": "2.25.0-beta.0",
-        "@douyinfe/semi-theme-default": "2.25.0-beta.0",
+        "@douyinfe/semi-animation": "2.25.2",
+        "@douyinfe/semi-animation-react": "2.25.2",
+        "@douyinfe/semi-foundation": "2.25.2",
+        "@douyinfe/semi-icons": "2.25.2",
+        "@douyinfe/semi-illustrations": "2.25.2",
+        "@douyinfe/semi-theme-default": "2.25.2",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "copy-text-to-clipboard": "^2.1.1",

+ 8 - 0
packages/semi-ui/select/_story/select.stories.jsx

@@ -2994,3 +2994,11 @@ export const RenderSelectedItemCallCount = () => {
 RenderSelectedItemCallCount.story = {
   name: 'RenderSelectedItemCallCount',
 };
+
+
+export const emptyContent = () => {
+  const list = null;
+  return (
+    <Select placeholder='请选择业务线' emptyContent={null} style={{ width: 180 }} optionList={list} defaultOpen={true}/>
+  )
+}

+ 6 - 1
packages/semi-ui/select/index.tsx

@@ -862,6 +862,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             loading,
             virtualize,
             multiple,
+            emptyContent
         } = this.props;
 
         // Do a filter first, instead of directly judging in forEach, so that the focusIndex can correspond to
@@ -884,7 +885,11 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             // eslint-disable-next-line jsx-a11y/no-static-element-interactions
             <div 
                 id={`${prefixcls}-${this.selectOptionListID}`} 
-                className={cls(`${prefixcls}-option-list-wrapper`, dropdownClassName)} 
+                className={cls({
+                    // When emptyContent is null and the option is empty, there is no need for the drop-down option for the user,
+                    // so there is no need to set padding through this className
+                    [`${prefixcls}-option-list-wrapper`]: !(isEmpty && emptyContent === null),
+                }, dropdownClassName)} 
                 style={style}
                 ref={this.setOptionContainerEl} 
                 onKeyDown={e => this.foundation.handleContainerKeyDown(e)}

+ 19 - 1
packages/semi-ui/tag/_story/tag.stories.jsx

@@ -365,4 +365,22 @@ export const TagShape = () => {
 
 TagShape.story = {
   name: 'tag shape',
-};
+};
+
+export const maxWidth = () => {
+  const src = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dy.png';
+  return (
+  <>
+    <Tag style={{maxWidth: 100}} >
+      李白李白李白李白李白
+    </Tag>
+    <br /><br />
+    <Tag style={{maxWidth: 100}} closable={true}>
+      李白李白李白李白李白
+    </Tag>
+    <br /><br />
+    <Tag style={{maxWidth: 100}} avatarSrc={src} avatarShape={'square'} onClick={()=>{console.log('如果能重来,我要做李白')}}>
+      李白李白李白李白李白
+    </Tag>
+  </>
+)}

+ 1 - 1
packages/semi-ui/tag/index.tsx

@@ -156,8 +156,8 @@ export default class Tag extends Component<TagProps, TagState> {
         ) : null;
         return (
             <div aria-label={this.props['aria-label'] || isString(children) ? `${closable ? 'Closable ' : ''}Tag: ${children}` : '' } {...wrapProps}>
+                {avatarSrc ? this.renderAvatar() : null}
                 <div className={`${prefixCls}-content`}>
-                    {avatarSrc ? this.renderAvatar() : null}
                     {children}
                 </div>
                 {closeIcon}

+ 13 - 10
packages/semi-ui/tagInput/index.tsx

@@ -22,6 +22,8 @@ import Paragraph from '../typography/paragraph';
 import { IconClear, IconHandle } from '@douyinfe/semi-icons';
 import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
 
+const prefixCls = cssClasses.PREFIX;
+
 export type Size = ArrayElement<typeof strings.SIZE_SET>;
 export type RestTagsPopoverProps = PopoverProps;
 type ValidateStatus = "default" | "error" | "warning";
@@ -31,7 +33,7 @@ const SortableItem = SortableElement(props => props.item);
 const SortableList = SortableContainer(
     ({ items }) => {
         return (
-            <div style={{ display: 'flex', flexFlow: 'row wrap', }}>
+            <div className={`${prefixCls}-sortable-list`}>
                 {items.map((item, index) => (
                     // @ts-ignore skip SortableItem type check
                     <SortableItem key={item.key} index={index} item={item.item}></SortableItem>
@@ -90,8 +92,6 @@ export interface TagInputState {
     active?: boolean
 }
 
-const prefixCls = cssClasses.PREFIX;
-
 class TagInput extends BaseComponent<TagInputProps, TagInputState> {
     static propTypes = {
         children: PropTypes.node,
@@ -439,13 +439,16 @@ class TagInput extends BaseComponent<TagInputProps, TagInputState> {
                         visible
                         aria-label={`${!disabled ? 'Closable ' : ''}Tag: ${value}`}
                     >
-                        {showIconHandler && <DragHandle />}
-                        <Paragraph
-                            className={typoCls}
-                            ellipsis={{ showTooltip: showContentTooltip, rows: 1 }}
-                        >
-                            {value}
-                        </Paragraph>
+                        {/* Wrap a layer of div outside IconHandler and Value to ensure that the two are aligned */}
+                        <div className={`${prefixCls}-tag-content-wrapper`}>
+                            {showIconHandler && <DragHandle />}
+                            <Paragraph
+                                className={typoCls}
+                                ellipsis={{ showTooltip: showContentTooltip, rows: 1 }}
+                            >
+                                {value}
+                            </Paragraph>
+                        </div>
                     </Tag>
                 );
             }

+ 235 - 2
packages/semi-ui/treeSelect/__test__/treeMultiple.test.js

@@ -15,6 +15,19 @@ const treeChildren = [
     },
 ];
 
+const treeChildrenWithFakeObj = [
+    {
+        label: '北京',
+        value: 'Beijing',
+        key: 'beijing',
+    },
+    {
+        label: '鱼',
+        value: 'Fish',
+        key: 'fish',
+    },
+]
+
 const treeData = [
     {
         label: '亚洲',
@@ -103,6 +116,59 @@ const treeDataWithDisabled = [
     }
 ];
 
+const treeChildrenWithoutValue = [
+    {
+        label: '北京',
+        key: 'beijing',
+    },
+    {
+        label: '上海',
+        key: 'shanghai',
+    },
+];
+
+const treeDataWithoutValue = [
+    {
+        label: '亚洲',
+        key: 'yazhou',
+        children: [
+            {
+                label: '中国',
+                key: 'zhongguo',
+                children: treeChildrenWithoutValue,
+            },
+            {
+                label: '日本',
+                key: 'riben',
+                children: [
+                    {
+                        label: '东京',
+                        key: 'dongjing'
+                    },
+                    {
+                        label: '大阪',
+                        key: 'daban'
+                    }
+                ]
+            },
+        ],
+    },
+    {
+        label: '北美洲',
+        key: 'beimeizhou',
+        children: [
+            {
+                label: '美国',
+                key: 'meiguo'
+            },
+            {
+                label: '加拿大',
+                key: 'jianada'
+            }
+        ]
+    },
+];
+
 let commonProps = {
     motion: false,
     motionExpand: false,
@@ -597,7 +663,6 @@ describe('TreeSelect', () => {
                 defaultOpen
                 defaultExpandAll
                 disableStrictly
-                multiple
                 leafOnly
                 treeData={treeData}
                 {...commonProps}
@@ -616,7 +681,6 @@ describe('TreeSelect', () => {
                 defaultOpen
                 defaultExpandAll
                 disableStrictly
-                multiple
                 treeData={treeDataWithDisabled}
                 {...commonProps}
             />
@@ -772,4 +836,173 @@ describe('TreeSelect', () => {
         // onSelect first args is key, not value
         expect(spyOnSelect.calledWithMatch('zhongguo')).toEqual(true);
     });
+
+    it('option not in treeData + treeData item with value', () => {
+        const spyOnSelect = sinon.spy(() => { });
+        const spyOnChange = sinon.spy(() => { });
+        const treeSelect = getTreeSelect({
+            defaultValue: ['Beijing', 'fish'],
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+        });
+
+        // Nodes that do not exist in treeData also appear in the tag input box
+        let tagGroup = treeSelect.find(`.${BASE_CLASS_PREFIX}-tag`);
+        expect(tagGroup.length).toEqual(2);
+        expect(tagGroup.at(0).instance().textContent).toEqual('北京');
+        expect(tagGroup.at(1).instance().textContent).toEqual('fish');
+
+        // Only one item is selected in the panel
+        let selectedNodes = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`);
+        let selectedNode = selectedNodes.at(0);
+        expect(selectedNode.find(`.${BASE_CLASS_PREFIX}-checkbox-inner-checked`).exists()).toEqual(true);
+        expect(selectedNode.find(`.${BASE_CLASS_PREFIX}-tree-option-label-text`).instance().textContent).toEqual('北京');
+       
+        // Check for fish in onSelect and onChange
+        let closeBtn = tagGroup.at(0).find(`.${BASE_CLASS_PREFIX}-tag-close`);
+        const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => { } } }
+        closeBtn.simulate('click', nativeEvent);
+        tagGroup = treeSelect.find(`.${BASE_CLASS_PREFIX}-tag`);
+        expect(tagGroup.length).toEqual(1);
+        expect(tagGroup.at(0).instance().textContent).toEqual('fish');
+        expect(spyOnChange.calledOnce).toBe(true);
+        expect(spyOnChange.calledWithMatch(['fish'])).toEqual(true);
+        
+        let nodeChina = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`).at(0);
+        // select China
+        nodeChina.simulate('click');
+        expect(spyOnSelect.calledWithMatch('zhongguo')).toEqual(true);
+        expect(spyOnChange.calledWithMatch(['fish', 'Zhongguo'])).toEqual(true);
+
+    });
+
+    it('option not in treeData + treeData item has value + onChangeWithObject', () => {
+        const spyOnSelect = sinon.spy(() => { });
+        const spyOnChange = sinon.spy(() => { });
+        const treeSelect = getTreeSelect({
+            defaultValue: treeChildrenWithFakeObj,
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+            defaultExpandAll: true,
+            onChangeWithObject: true
+        });
+
+        // Nodes that do not exist in treeData also appear in the tag input box
+        let tagGroup = treeSelect.find(`.${BASE_CLASS_PREFIX}-tag`);
+        expect(tagGroup.length).toEqual(2);
+        expect(tagGroup.at(0).instance().textContent).toEqual('北京');
+        expect(tagGroup.at(1).instance().textContent).toEqual('鱼');
+
+        // Only one item is selected in the panel
+        let selectedNodes = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`);
+        let selectedNode = selectedNodes.at(0);
+        expect(selectedNode.find(`.${BASE_CLASS_PREFIX}-checkbox-inner-checked`).exists()).toEqual(true);
+        expect(selectedNode.find(`.${BASE_CLASS_PREFIX}-tree-option-label-text`).instance().textContent).toEqual('北京');
+    
+        // Check for fish in onSelect and onChange
+        let closeBtn = tagGroup.at(0).find(`.${BASE_CLASS_PREFIX}-tag-close`);
+        const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => { } } }
+        closeBtn.simulate('click', nativeEvent);
+        tagGroup = treeSelect.find(`.${BASE_CLASS_PREFIX}-tag`);
+        expect(tagGroup.length).toEqual(1);
+        expect(tagGroup.at(0).instance().textContent).toEqual('鱼');
+        expect(spyOnChange.calledOnce).toBe(true);
+        expect(spyOnChange.calledWithMatch([{ label: '鱼', value: 'Fish',  key: 'fish' }])).toEqual(true);
+        
+        let nodeChina = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`).at(0);
+        // select China
+        nodeChina.simulate('click');
+        expect(spyOnSelect.calledWithMatch('beijing')).toEqual(true);
+        expect(spyOnChange.calledWithMatch([
+            { label: '鱼', value: 'Fish',  key: 'fish' }, 
+            { label: '北京', value: 'Beijing', key: 'beijing' }
+        ])).toEqual(true);
+    });
+
+    it('option not in treeData + treeData item without value ', () => {
+        const spyOnSelect = sinon.spy(() => { });
+        const spyOnChange = sinon.spy(() => { });
+        const treeSelect = getTreeSelect({
+            treeData: treeDataWithoutValue,
+            defaultValue: ['beijing', 'fish'],
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+        });
+
+        // Nodes that do not exist in treeData also appear in the tag input box
+        let tagGroup = treeSelect.find(`.${BASE_CLASS_PREFIX}-tag`);
+        expect(tagGroup.length).toEqual(2);
+        expect(tagGroup.at(0).instance().textContent).toEqual('北京');
+        expect(tagGroup.at(1).instance().textContent).toEqual('fish');
+
+        // Only one item is selected in the panel
+        let selectedNodes = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`);
+        let selectedNode = selectedNodes.at(0);
+        expect(selectedNode.find(`.${BASE_CLASS_PREFIX}-checkbox-inner-checked`).exists()).toEqual(true);
+        expect(selectedNode.find(`.${BASE_CLASS_PREFIX}-tree-option-label-text`).instance().textContent).toEqual('北京');
+       
+        // Check for fish in onSelect and onChange
+        let closeBtn = tagGroup.at(0).find(`.${BASE_CLASS_PREFIX}-tag-close`);
+        const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => { } } }
+        closeBtn.simulate('click', nativeEvent);
+        tagGroup = treeSelect.find(`.${BASE_CLASS_PREFIX}-tag`);
+        expect(tagGroup.length).toEqual(1);
+        expect(tagGroup.at(0).instance().textContent).toEqual('fish');
+        expect(spyOnChange.calledOnce).toBe(true);
+        expect(spyOnChange.calledWithMatch(['fish'])).toEqual(true);
+        
+        let nodeChina = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`).at(0);
+        // select China
+        nodeChina.simulate('click');
+        expect(spyOnSelect.calledWithMatch('zhongguo')).toEqual(true);
+        expect(spyOnChange.calledWithMatch(['fish', 'zhongguo'])).toEqual(true);
+
+    });
+
+    it('option not in treeData + treeData item without value + onChangeWithObject', () => {
+        const spyOnSelect = sinon.spy(() => { });
+        const spyOnChange = sinon.spy(() => { });
+        const treeSelect = getTreeSelect({
+            treeData: treeDataWithoutValue,
+            defaultValue: [
+                { label: '北京',  key: 'beijing' },
+                { label: '鱼', key: 'fish' }
+            ],
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+            defaultExpandAll: true,
+            onChangeWithObject: true
+        });
+
+        // Nodes that do not exist in treeData also appear in the tag input box
+        let tagGroup = treeSelect.find(`.${BASE_CLASS_PREFIX}-tag`);
+        expect(tagGroup.length).toEqual(2);
+        expect(tagGroup.at(0).instance().textContent).toEqual('北京');
+        expect(tagGroup.at(1).instance().textContent).toEqual('鱼');
+
+        // Only one item is selected in the panel
+        let selectedNodes = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`);
+        let selectedNode = selectedNodes.at(0);
+        expect(selectedNode.find(`.${BASE_CLASS_PREFIX}-checkbox-inner-checked`).exists()).toEqual(true);
+        expect(selectedNode.find(`.${BASE_CLASS_PREFIX}-tree-option-label-text`).instance().textContent).toEqual('北京');
+    
+        // Check for fish in onSelect and onChange
+        let closeBtn = tagGroup.at(0).find(`.${BASE_CLASS_PREFIX}-tag-close`);
+        const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => { } } }
+        closeBtn.simulate('click', nativeEvent);
+        tagGroup = treeSelect.find(`.${BASE_CLASS_PREFIX}-tag`);
+        expect(tagGroup.length).toEqual(1);
+        expect(tagGroup.at(0).instance().textContent).toEqual('鱼');
+        expect(spyOnChange.calledOnce).toBe(true);
+        expect(spyOnChange.calledWithMatch([{ label: '鱼', key: 'fish' }])).toEqual(true);
+        
+        let nodeChina = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`).at(0);
+        // select China
+        nodeChina.simulate('click');
+        expect(spyOnSelect.calledWithMatch('beijing')).toEqual(true);
+        expect(spyOnChange.calledWithMatch([
+            { label: '鱼',  key: 'fish' }, 
+            { label: '北京', key: 'beijing' }
+        ])).toEqual(true);
+    });
 })

+ 121 - 2
packages/semi-ui/treeSelect/__test__/treeSelect.test.js

@@ -117,6 +117,59 @@ const treeData3 = [
     }
 ];
 
+const treeChildrenWithoutValue = [
+    {
+        label: '北京',
+        key: 'beijing',
+    },
+    {
+        label: '上海',
+        key: 'shanghai',
+    },
+];
+
+const treeDataWithoutValue = [
+    {
+        label: '亚洲',
+        key: 'yazhou',
+        children: [
+            {
+                label: '中国',
+                key: 'zhongguo',
+                children: treeChildrenWithoutValue,
+            },
+            {
+                label: '日本',
+                key: 'riben',
+                children: [
+                    {
+                        label: '东京',
+                        key: 'dongjing'
+                    },
+                    {
+                        label: '大阪',
+                        key: 'daban'
+                    }
+                ]
+            },
+        ],
+    },
+    {
+        label: '北美洲',
+        key: 'beimeizhou',
+        children: [
+            {
+                label: '美国',
+                key: 'meiguo'
+            },
+            {
+                label: '加拿大',
+                key: 'jianada'
+            }
+        ]
+    },
+];
+
 let commonProps = {
     motion: false,
     motionExpand: false,
@@ -905,12 +958,13 @@ describe('TreeSelect', () => {
         ).toEqual('北京');
         treeSelect.setProps({ treeData: treeData3});
         treeSelect.update();
+        // If the value exists, but not in the treeData, the value will be displayed in the trigger
         expect(
             treeSelect
             .find(`.${BASE_CLASS_PREFIX}-tree-select .${BASE_CLASS_PREFIX}-tree-select-selection span`)
             .getDOMNode()
             .textContent
-        ).toEqual('');
+        ).toEqual('Beijing');
     });
 
     it('treeData is updated should not clear value when controlled mode and multiple selection', () => {
@@ -945,7 +999,7 @@ describe('TreeSelect', () => {
             .at(0)
             .find(`.${BASE_CLASS_PREFIX}-tag-content`)
             .length
-        ).toEqual(0);
+        ).toEqual(1);
     });
 
     it('expandedKeys controlled + filterTreeNode', () => {
@@ -965,4 +1019,69 @@ describe('TreeSelect', () => {
         expect(topNode.at(0).hasClass(`${BASE_CLASS_PREFIX}-tree-option-collapsed`)).toEqual(true);
         expect(topNode.at(1).hasClass(`${BASE_CLASS_PREFIX}-tree-option-collapsed`)).toEqual(true);
     });
+    
+    it('option not in treeData + treeData item with value', () => {
+        const spyOnSelect = sinon.spy(() => { });
+        const spyOnChange = sinon.spy(() => { });
+        let treeSelect = getTreeSelect({
+            defaultValue: 'fish',
+            defaultExpandAll: true,
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+        });
+        expect(treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-select-selection`).getDOMNode().textContent).toEqual('fish');
+        // beijing
+        let topNodeBeijing = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`).at(0);
+        topNodeBeijing.simulate('click');
+        expect(spyOnChange.calledWithMatch("Beijing")).toEqual(true);
+        treeSelect.unmount(); 
+
+        // onChangeWithObject
+        treeSelect = getTreeSelect({
+            defaultValue: { label: '鱼', value: 'Fish', key: 'fish' },
+            onChangeWithObject: true,
+            defaultExpandAll: true,
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+        });
+        expect(treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-select-selection`).getDOMNode().textContent).toEqual('鱼');
+        // beijing
+        topNodeBeijing = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`).at(0);
+        topNodeBeijing.simulate('click');
+        expect(spyOnChange.calledWithMatch({ label: '北京', value: 'Beijing', key: 'beijing' })).toEqual(true);
+        treeSelect.unmount(); 
+    })
+
+    it('option not in treeData + treeData item without value', () => {
+        const spyOnSelect = sinon.spy(() => { });
+        const spyOnChange = sinon.spy(() => { });
+        let treeSelect = getTreeSelect({
+            treeData: treeDataWithoutValue,
+            defaultValue: 'fish',
+            defaultExpandAll: true,
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+        });
+        expect(treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-select-selection`).getDOMNode().textContent).toEqual('fish');
+        // beijing
+        let topNodeBeijing = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`).at(0);
+        topNodeBeijing.simulate('click');
+        expect(spyOnChange.calledWithMatch("beijing")).toEqual(true);
+        treeSelect.unmount(); 
+
+        // onChangeWithObject
+        treeSelect = getTreeSelect({
+            defaultValue: { label: '鱼', key: 'fish' },
+            onChangeWithObject: true,
+            defaultExpandAll: true,
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+        });
+        expect(treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-select-selection`).getDOMNode().textContent).toEqual('鱼');
+        // beijing
+        topNodeBeijing = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-3`).at(0);
+        topNodeBeijing.simulate('click');
+        expect(spyOnChange.calledWithMatch({ label: '北京', key: 'beijing' })).toEqual(true);
+        treeSelect.unmount(); 
+    })
 })

+ 298 - 1
packages/semi-ui/treeSelect/_story/treeSelect.stories.jsx

@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useMemo } from 'react';
 import { Icon, Button, Form, Popover, Tag, Typography, CheckboxGroup } from '../../index';
 import TreeSelect from '../index';
 import { flattenDeep } from 'lodash';
@@ -1745,3 +1745,300 @@ export const size = () => {
     <TreeSelect {...props} size={'large'} placeholder={'large'} />
   </>);
 }
+
+export const valueNotInTreeDataIssue = () => {
+  const treeData = [
+      {
+          key: "test",
+          label: "测试标签",
+          children: [
+              {
+                  key: "test_2",
+                  label: "测试二级标签"
+              },
+              {
+                  key: "jzr_test",
+                  label: "之睿测试"
+              }
+          ]
+      },
+  
+      {
+          key: "create",
+          label: "创作构思",
+          children: [
+              {
+                  key: "material",
+                  label: "素材积累"
+              },
+              {
+                  key: "lens_script",
+                  label: "分镜脚本"
+              }
+          ]
+      }
+  ];
+
+  const treeDataWithValue = [
+    {
+        value: "test",
+        key: "0",
+        label: "测试标签",
+        children: [
+            {
+                value: "test_2",
+                key: "0-1",
+                label: "测试二级标签"
+            },
+            {
+              value: "jzr_test",
+                key: "0-2",
+                label: "之睿测试"
+            }
+        ]
+    },
+
+    {
+        value: "create",
+        key: "1",
+        label: "创作构思",
+        children: [
+            {
+                value: "material",
+                key: "1-1",
+                label: "素材积累"
+            },
+            {
+                value: "lens_script",
+                key: "1-2",
+                label: "分镜脚本"
+            }
+        ]
+    }
+  ];
+
+  const commonProps = useMemo(() => {
+    return {
+      multiple: true,
+      style: { width: 300 },
+      dropdownStyle: { maxHeight: 400, overflow: 'auto' },
+      onChange: (value) => {
+        console.log('onChange', value);
+      },
+      onSelect: (value) => {
+        console.log('onSelect', value); 
+      },
+    };
+  }, []);
+  
+  return (
+    <>
+      <p style={{ backgroundColor: 'yellowgreen', width: 'fit-content' }}>多选,无 value</p>
+      <p>checkRelation='related'</p>
+        <TreeSelect
+          defaultExpandAll
+          defaultValue={["test_2", 0]}
+          treeData={treeData}
+          {...commonProps}
+        />
+        <p>checkRelation='unRelated'</p>
+        <TreeSelect
+          defaultExpandAll
+          defaultValue={["test_2", "fish"]}
+          checkRelation='unRelated'
+          treeData={treeData}
+          {...commonProps}
+        />
+        <p>onChangeWithObject, checkRelation='related'</p>
+        <TreeSelect
+          defaultExpandAll
+          onChangeWithObject
+          defaultValue={[
+            {
+              key: "test_2",
+              label: "测试二级标签"
+            },
+            {
+              key: "fish",
+              label: "鱼"
+            }
+          ]}
+          treeData={treeData}
+          {...commonProps}
+        />
+        <p>onChangeWithObject, checkRelation='unRelated'</p>
+        <TreeSelect
+          defaultExpandAll
+          onChangeWithObject
+          defaultValue={[
+            {
+              key: "test_2",
+              label: "测试二级标签"
+            },
+            {
+              key: "fish",
+              label: "鱼"
+            }
+          ]}
+          treeData={treeData}
+          {...commonProps}
+        />
+        <p style={{ backgroundColor: 'yellowgreen', width: 'fit-content' }}>多选,有 value</p>
+        <p>checkRelation='related'</p>
+        <TreeSelect
+          defaultExpandAll
+          defaultValue={["test", "fish"]}
+          treeData={treeDataWithValue}
+          {...commonProps}
+        />
+        <p>checkRelation='unRelated'</p>
+        <TreeSelect
+          defaultExpandAll
+          defaultValue={["test", "fish"]}
+          checkRelation='unRelated'
+          treeData={treeDataWithValue}
+          {...commonProps}
+        />
+        <p>onChangeWithObject, checkRelation='unRelated'</p>
+        <TreeSelect
+          defaultExpandAll
+          onChangeWithObject
+          defaultValue={[
+            {
+              value: "test_2",
+              key: "0-1",
+              label: "测试二级标签"
+            },
+            {
+              key: "fish",
+              value: "Fish",
+              label: "鱼"
+            }
+          ]}
+          treeData={treeDataWithValue}
+          {...commonProps}
+        />
+        <p>onChangeWithObject, checkRelation='unRelated'</p>
+        <TreeSelect
+          defaultExpandAll
+          onChangeWithObject
+          defaultValue={[
+            {
+              value: "test_2",
+              key: "0-1",
+              label: "测试二级标签"
+            },
+            {
+              key: "fish",
+              value: "Fish",
+              label: "鱼"
+            }
+          ]}
+          treeData={treeDataWithValue}
+          {...commonProps}
+        />
+        <p style={{ backgroundColor: 'yellowgreen', width: 'fit-content' }}>单选,无 value</p>
+        <TreeSelect
+          defaultExpandAll
+          defaultValue={"fish"}
+          treeData={treeData}
+          {...commonProps}
+          multiple={false}
+        />
+        <p>onChangeWithObject</p>
+        <TreeSelect
+          defaultExpandAll
+          defaultValue={{
+            key: "fish",
+            value: "Fish",
+            label: "鱼"
+          }}
+          treeData={treeData}
+          {...commonProps}
+          multiple={false}
+          onChangeWithObject
+        />
+        <p style={{ backgroundColor: 'yellowgreen', width: 'fit-content' }}>单选,有 value</p>
+        <TreeSelect
+          defaultExpandAll
+          defaultValue={"fish"}
+          treeData={treeDataWithValue}
+          {...commonProps}
+          multiple={false}
+        />
+        <p>onChangeWithObject</p>
+        <TreeSelect
+          defaultExpandAll
+          defaultValue={{
+            key: "fish",
+            value: "Fish",
+            label: "鱼"
+          }}
+          treeData={treeDataWithValue}
+          {...commonProps}
+          multiple={false}
+          onChangeWithObject
+        />
+    </>
+  );
+};
+
+class ValueTypeIsNumber extends React.Component {
+  constructor() {
+      super();
+      this.state = {
+          value: 1
+      };
+  }
+  onChange(value) {
+      console.log('onChange', value);
+      this.setState({ value });
+  }
+  render() {
+      const treeData = [
+           {
+              label: '北美洲',
+              value: 'North America',
+              key: '1',
+          },
+          {
+              label: '亚洲',
+              value: 'Asia',
+              key: '0',
+              children: [
+                  {
+                      label: '中国',
+                      value: 'China',
+                      key: '0-0',
+                      children: [
+                          {
+                              label: '北京',
+                              value: 'Beijing',
+                              key: '0-0-0',
+                          },
+                          {
+                              label: '上海',
+                              value: 'Shanghai',
+                              key: '0-0-1',
+                          },
+                      ],
+                  },
+              ],
+          },
+         
+      ];
+      return (
+          <TreeSelect
+              style={{ width: 300 }}
+              dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+              treeData={treeData}
+              value={this.state.value}
+              placeholder="请选择"
+              multiple
+              onChange={e => this.onChange(e)}
+          />
+      );
+  }
+}
+
+export const valueIsNumber = () => <ValueTypeIsNumber />

+ 18 - 14
packages/semi-ui/treeSelect/index.tsx

@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
 import ReactDOM from 'react-dom';
 import cls from 'classnames';
 import PropTypes from 'prop-types';
-import { isEqual, isString, isEmpty, noop, get, isFunction } from 'lodash';
+import { isEqual, isString, isEmpty, noop, get, isFunction, isUndefined, isNull } from 'lodash';
 import TreeSelectFoundation, {
     Size,
     BasicTriggerRenderProps,
@@ -186,7 +186,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         arrowIcon: PropTypes.node,
         clearIcon: PropTypes.node,
         defaultOpen: PropTypes.bool,
-        defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
+        defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]),
         defaultExpandAll: PropTypes.bool,
         defaultExpandedKeys: PropTypes.array,
         expandAll: PropTypes.bool,
@@ -716,6 +716,10 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         this.foundation.handleClick(e);
     };
 
+    getDataForKeyNotInKeyEntities = (key: string) => {
+        return this.foundation.getDataForKeyNotInKeyEntities(key);
+    }
+
     /* istanbul ignore next */
     handleSelectionEnterPress = (e: React.KeyboardEvent<HTMLDivElement>) => {
         this.foundation.handleSelectionEnterPress(e);
@@ -764,14 +768,14 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
             });
         let renderKeys = [];
         if (checkRelation === 'related') {
-            renderKeys = normalizeKeyList([...checkedKeys], keyEntities, leafOnly);
+            renderKeys = normalizeKeyList([...checkedKeys], keyEntities, leafOnly, true);
         } else if (checkRelation === 'unRelated' && Object.keys(keyEntities).length > 0) {
             renderKeys = [...realCheckedKeys];
         }
         const tagList: Array<React.ReactNode> = [];
         // eslint-disable-next-line @typescript-eslint/no-shadow
-        renderKeys.forEach((key: TreeNodeData['key']) => {
-            const item = keyEntities[key].data;
+        renderKeys.forEach((key: TreeNodeData['key'], index) => {
+            const item = (keyEntities[key] && keyEntities[key].data.key === key) ? keyEntities[key].data : this.getDataForKeyNotInKeyEntities(key);
             const onClose = (tagContent: any, e: React.MouseEvent) => {
                 if (e && typeof e.preventDefault === 'function') {
                     // make sure that tag will not hidden immediately in controlled mode
@@ -779,10 +783,10 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 }
                 this.removeTag(key);
             };
-            const { content, isRenderInTag } = (treeNodeLabelProp in item && item) ?
-                (renderSelectedItem as RenderSelectedItemInMultiple)(item, { index: key, onClose }) :
+            const { content, isRenderInTag } = (item && treeNodeLabelProp in item) ?
+                (renderSelectedItem as RenderSelectedItemInMultiple)(item, { index, onClose }) :
                 null;
-            if (!content) {
+            if (isNull(content) || isUndefined(content)) {
                 return;
             }
             const isDisabled = disabled || item.disabled || (disableStrictly && disabledKeys.has(item.key));
@@ -791,7 +795,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 color: 'white',
                 visible: true,
                 onClose,
-                key,
+                key: `tag-${key}-${index}`,
                 size: size === 'small' ? 'small' : 'large'
             };
             if (isRenderInTag) {
@@ -990,7 +994,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 },
                 className
             );
-        const triggerRenderKeys = multiple ? normalizeKeyList([...checkedKeys], keyEntities, leafOnly) : selectedKeys;
+        const triggerRenderKeys = multiple ? normalizeKeyList([...checkedKeys], keyEntities, leafOnly, true) : selectedKeys;
         const inner = useCustomTrigger ? (
             <Trigger
                 inputValue={inputValue}
@@ -1061,8 +1065,8 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
             renderSelectedItem: propRenderSelectedItem,
             treeNodeLabelProp
         } = this.props;
-        const keyList = normalizeKeyList([key], keyEntities, leafOnly);
-        const nodes = keyList.map(i => keyEntities[i].data);
+        const keyList = normalizeKeyList([key], keyEntities, leafOnly, true);
+        const nodes = keyList.map(i => (keyEntities[key] && keyEntities[key].data.key === key) ? keyEntities[key].data : this.getDataForKeyNotInKeyEntities(key));
         const value = getValueOrKey(nodes);
         const tagCls = cls(`${prefixcls}-selection-tag`, {
             [`${prefixcls}-selection-tag-disabled`]: disabled,
@@ -1090,7 +1094,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 content: get(selectedItem, treeNodeLabelProp, null)
             });
         if (isFunction(renderSelectedItem)) {
-            const { content, isRenderInTag } = treeNodeLabelProp in item && item ?
+            const { content, isRenderInTag } = item && treeNodeLabelProp in item ?
                 (renderSelectedItem as RenderSelectedItemInMultiple)(item, { index: idx, onClose }) :
                 null;
             if (isRenderInTag) {
@@ -1128,7 +1132,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         } = this.state;
         let keyList = [];
         if (checkRelation === 'related') {
-            keyList = normalizeKeyList(checkedKeys, keyEntities, leafOnly);
+            keyList = normalizeKeyList(checkedKeys, keyEntities, leafOnly, true);
         } else if (checkRelation === 'unRelated') {
             keyList = [...realCheckedKeys];
         }

+ 4 - 1
packages/semi-ui/typography/util.tsx

@@ -35,8 +35,11 @@ const getRenderText = (
     ellipsisStr: string,
     suffix: string,
     ellipsisPos: string
-// eslint-disable-next-line max-params
+    // eslint-disable-next-line max-params
 ) => {
+    if (content.length === 0) {
+        return '';
+    }
     if (!ellipsisContainer) {
         ellipsisContainer = document.createElement('div');
         ellipsisContainer.setAttribute('aria-hidden', 'true');

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

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

+ 4 - 1
packages/semi-webpack/src/semi-webpack-plugin.ts

@@ -97,7 +97,10 @@ export default class SemiWebpackPlugin {
                 module.loaders = [
                     lastLoader,
                     {
-                        loader: cssLoader
+                        loader: cssLoader,
+                        options: {
+                            sourceMap: false,
+                        }
                     }, {
                         loader: scssLoader
                     },

+ 0 - 74
yarn.lock

@@ -1456,15 +1456,6 @@
   resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
   integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
 
-"@douyinfe/[email protected]":
-  version "2.24.3"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-animation-react/-/semi-animation-react-2.24.3.tgz#6006e32037aa2f819d167f0db85d3bb985b572eb"
-  integrity sha512-b8mps1dw9vUqqsLJrZvm2ocBf+5Htfeo57eKskiDqNExu1hrt3azWV75NgPUUPVCdXaFhiB7YHMsNiqcSkv0aw==
-  dependencies:
-    "@douyinfe/semi-animation" "2.12.0"
-    "@douyinfe/semi-animation-styled" "2.23.2"
-    classnames "^2.2.6"
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.9.1.tgz#f2e4c6ef7899729ee6145edf0579598ba195bfdd"
@@ -1495,13 +1486,6 @@
     "@babel/runtime-corejs3" "^7.15.4"
     bezier-easing "^2.1.0"
 
-"@douyinfe/[email protected]":
-  version "2.24.3"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-animation/-/semi-animation-2.24.3.tgz#90fbb6d8df16689c136288422da2a76b609ff0e8"
-  integrity sha512-ED1/w5REPbQ5wsxzUsQfMeivwB07OQrsdZlRRHlmlb+oDuOBJr1FmIJ0Ukt5EYrwFWbQ03phiKTBGYu1JzRCaQ==
-  dependencies:
-    bezier-easing "^2.1.0"
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.9.1.tgz#4345fd86823b51e7c6fb5e9079d8f5c3ffe608f8"
@@ -1510,20 +1494,6 @@
     "@babel/runtime-corejs3" "^7.15.4"
     bezier-easing "^2.1.0"
 
-"@douyinfe/[email protected]":
-  version "2.24.3"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-foundation/-/semi-foundation-2.24.3.tgz#2071cac67751edd960f07014eaa869a4bd15e5f2"
-  integrity sha512-i/7bNg2CvMPfgIVN4myIUgad1pE1uDQJ8MRTd67Ntfzhsd3cvaa6N1h9YSWD3iGIC+yKi+B75I5DqmV20qRgTw==
-  dependencies:
-    "@douyinfe/semi-animation" "2.12.0"
-    async-validator "^3.5.0"
-    classnames "^2.2.6"
-    date-fns "^2.9.0"
-    date-fns-tz "^1.0.10"
-    lodash "^4.17.21"
-    memoize-one "^5.2.1"
-    scroll-into-view-if-needed "^2.2.24"
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.9.1.tgz#1300bb97d6ceb92274ca4c9e6c66c5c16dc284ea"
@@ -1539,13 +1509,6 @@
     memoize-one "^5.2.1"
     scroll-into-view-if-needed "^2.2.24"
 
-"@douyinfe/[email protected]", "@douyinfe/semi-icons@^2.0.0":
-  version "2.24.3"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-icons/-/semi-icons-2.24.3.tgz#25d02f843797905e6949b08c6d1e99b5af28f144"
-  integrity sha512-Kj8VZewhXVic1SO9jVuGqz0QxWmhkf5LikRHfCGEt1Uh+EevSI4U2iWeqkFxW8+xThs6aThmcYhd9CRiF6VG5A==
-  dependencies:
-    classnames "^2.2.6"
-
 "@douyinfe/[email protected]", "@douyinfe/semi-icons@latest":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.9.1.tgz#7a04e1a77070220b04f63e6f65aac30155ed8ddd"
@@ -1554,11 +1517,6 @@
     "@babel/runtime-corejs3" "^7.15.4"
     classnames "^2.2.6"
 
-"@douyinfe/[email protected]":
-  version "2.24.3"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-illustrations/-/semi-illustrations-2.24.3.tgz#697c9fb18c109365d1411e06bf4fd78925296fb2"
-  integrity sha512-F/GCY2yvZadB2k3CR9v8gxJA8BblE7X1HpZkpmYmsewAAvzsa1c5RyrOtlnufGQGe2c5gSsHVMDKnWCZtbwOug==
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.9.1.tgz#1a448d1854ee1beeba57ea612da052b549ea105f"
@@ -1632,13 +1590,6 @@
     monaco-themes "^0.3.3"
     react-live "^2.2.2"
 
-"@douyinfe/[email protected]":
-  version "2.24.3"
-  resolved "https://registry.npmjs.org/@douyinfe/semi-theme-default/-/semi-theme-default-2.24.3.tgz#c2bc489b6a9d604b7ebab71f6fb9e2bc7150a42b"
-  integrity sha512-KP7Wf0K6epwA19K366hE3z/y43Zdfujz2lOCC40UuE/2yfD2umXzC6YB9uGsHNaBctmYEnuXSWMS+fCTZH9arA==
-  dependencies:
-    glob "^7.1.6"
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.9.1.tgz#734113e9783ca58b69afe1769005e7e57e5a4da7"
@@ -1646,31 +1597,6 @@
   dependencies:
     glob "^7.1.6"
 
-"@douyinfe/semi-ui@^2.0.0":
-  version "2.24.3"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.24.3.tgz#e4101d3ffd9730fa019fcc0e7a5fa9f868ab8147"
-  integrity sha512-RwFomK5ZlImoFRJHGf5GD/tIDc4aGK0POurO3P2wwperKyO+SO4/TEpxSI933Rv+PysBLM7nUjbu45ApGZYU3A==
-  dependencies:
-    "@douyinfe/semi-animation" "2.24.3"
-    "@douyinfe/semi-animation-react" "2.24.3"
-    "@douyinfe/semi-foundation" "2.24.3"
-    "@douyinfe/semi-icons" "2.24.3"
-    "@douyinfe/semi-illustrations" "2.24.3"
-    "@douyinfe/semi-theme-default" "2.24.3"
-    async-validator "^3.5.0"
-    classnames "^2.2.6"
-    copy-text-to-clipboard "^2.1.1"
-    date-fns "^2.9.0"
-    date-fns-tz "^1.0.10"
-    lodash "^4.17.21"
-    prop-types "^15.7.2"
-    react-resizable "^1.8.0"
-    react-sortable-hoc "^2.0.0"
-    react-window "^1.8.2"
-    resize-observer-polyfill "^1.5.1"
-    scroll-into-view-if-needed "^2.2.24"
-    utility-types "^3.10.0"
-
 "@douyinfe/semi-ui@latest":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.9.1.tgz#505d4783ea1fa73d307b75f62091030f1fee9332"

部分文件因文件數量過多而無法顯示