Browse Source

feat: cascader add focus/blur methods (#1553)

Co-authored-by: 方俊 <[email protected]>
方阿森 2 years ago
parent
commit
625cd8010a

+ 18 - 13
packages/semi-foundation/cascader/foundation.ts

@@ -92,9 +92,9 @@ export interface BasicTriggerRenderProps {
      * customized by triggerRender is updated to synchronize the state
      * with Cascader. */
     onSearch: (inputValue: string) => void;
-    /* This function is the same as onSearch (supported since v2.32.0), 
-     * because this function was used before, and to align with TreeSelect, 
-     * use onSearch instead of onChange is more suitable, 
+    /* This function is the same as onSearch (supported since v2.32.0),
+     * because this function was used before, and to align with TreeSelect,
+     * use onSearch instead of onChange is more suitable,
      * onChange needs to be deleted in the next Major
     */
     onChange: (inputValue: string) => void;
@@ -203,6 +203,7 @@ export interface CascaderAdapter extends DefaultAdapter<BasicCascaderProps, Basi
     updateInputValue: (value: string) => void;
     updateInputPlaceHolder: (value: string) => void;
     focusInput: () => void;
+    blurInput: () => void;
     registerClickOutsideHandler: (cb: (e: any) => void) => void;
     unregisterClickOutsideHandler: () => void;
     rePositionDropdown: () => void;
@@ -259,7 +260,7 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
         if (multiple) {
             const valuePath: BasicValue = [];
             // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-            // @ts-ignore 
+            // @ts-ignore
             item.forEach((checkedKey: string) => {
                 const valuePathItem = this.getItemPropPath(checkedKey, valueProp);
                 valuePath.push(valuePathItem as any);
@@ -449,7 +450,7 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
                 if (filterable && !multiple) {
                     const displayText = this.renderDisplayText(selectedKey, keyEntities);
                     updateStates.inputPlaceHolder = displayText;
-                    /* 
+                    /*
                      *  displayText should not be assign to inputValue,
                      *  cause inputValue should only change by user enter
                      */
@@ -484,7 +485,7 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
             if (filterable && !multiple) {
                 const displayText = this._defaultRenderText(valuePath);
                 updateStates.inputPlaceHolder = displayText;
-                /* 
+                /*
                  *  displayText should not be assign to inputValue,
                  *  cause inputValue should only change by user enter
                  */
@@ -555,20 +556,24 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
         this._notifyBlur(e);
     }
 
+    focus() {
+        this._adapter.focusInput();
+        this._adapter.updateFocusState(true);
+    }
+
+    blur() {
+        this._adapter.blurInput();
+        this._adapter.updateFocusState(false);
+    }
+
     toggle2SearchInput(isShow: boolean) {
         if (isShow) {
-            this._adapter.toggleInputShow(isShow, () => this.focusInput());
+            this._adapter.toggleInputShow(isShow, () => this.focus());
         } else {
             this._adapter.toggleInputShow(isShow, () => undefined);
         }
     }
 
-    focusInput() {
-        this._adapter.focusInput();
-        this._adapter.updateFocusState(true);
-    }
-
-
     updateSearching = (isSearching: boolean)=>{
         this._adapter.updateStates({ isSearching: false });
     }

+ 35 - 21
packages/semi-ui/cascader/_story/cascader.stories.jsx

@@ -334,7 +334,7 @@ export const issue703 = () => {
         },
     ];
     const [data, setData] = useState(initialData);
-    
+
     const updateTreeData = (list, value, children) => {
         return list.map(node => {
             if (node.value === value) {
@@ -380,7 +380,7 @@ export const issue703 = () => {
     useEffect(()=>{
       console.log('data change');
         setTimeout(()=>setV([['0-0'], ['0-1', 'Node2-2', 'Node2-2-2']]),0);
-    },[data]) 
+    },[data])
 
     return (
       <>
@@ -391,7 +391,7 @@ export const issue703 = () => {
             value={v}
             style={{ width: 300 }}
             treeData={data}
-            loadData={onLoadData} 
+            loadData={onLoadData}
             placeholder="Please select"
         />
         <div>非受控,动态更新treeData</div>
@@ -400,7 +400,7 @@ export const issue703 = () => {
             onChange={(a)=>console.log(a)}
             style={{ width: 300 }}
             treeData={data}
-            loadData={onLoadData} 
+            loadData={onLoadData}
             placeholder="Please select"
         />
       </>
@@ -1444,7 +1444,7 @@ export const OnChangeWithObject = () => (
 
 export const undefinedValueWhileMutipleAndOnChangeWithObject = () => {
   const [value, setValue] = useState(undefined);
-  
+
   return (
     <>
       <div>多选 + onChangeWithObject + value 为 undefined</div>
@@ -1565,7 +1565,7 @@ export const DynamicTreeData = () => {
           <Cascader
             style={{ width: 300 }}
             treeData={treeDataDemo1}
-            multiple 
+            multiple
             placeholder="请选择所在地区"
           />
           <Button onClick={()=>{setTreeData1(treeData3)}}>改变treeData</Button>
@@ -1671,17 +1671,17 @@ export const filterSorter = () => {
               {
                   label: 'Semi-Material',
                   value: 'Semi-Material',
-                  
+
               },
               {
                   label: 'Semi-DSM',
                   value: 'Semi-DSM',
-                  
+
               },
               {
                   label: 'Semi',
                   value: 'Semi',
-                  
+
               },
               {
                   label: 'Semi-C2D',
@@ -1726,17 +1726,17 @@ export const filterRender = () => {
             {
                 label: 'Semi-Material Semi-Material Semi-Material Semi-Material',
                 value: 'Semi-Material',
-                
+
             },
             {
                 label: 'Semi-DSM Semi-DSM Semi-DSM Semi-DSM',
                 value: 'Semi-DSM',
-                
+
             },
             {
                 label: 'Semi Design Semi Design Semi Design Semi Design',
                 value: 'Semi',
-                
+
             },
             {
                 label: 'Semi-C2D Semi-C2D Semi-C2D Semi-C2D Semi-C2D',
@@ -1764,9 +1764,9 @@ export const filterRender = () => {
           style={{justifyContent: 'flex-start'}}
           role="treeitem"
           onClick={onClick}
-      > 
-        <Text 
-          ellipsis={{ showTooltip: { opts: { style: { wordBreak: 'break-all'} }}}} 
+      >
+        <Text
+          ellipsis={{ showTooltip: { opts: { style: { wordBreak: 'break-all'} }}}}
           style={{ width: 270, color: selected ? 'var(--semi-color-primary)': undefined }}
         >
             {data.map(item => item.label ).join(' / ')}
@@ -1789,15 +1789,15 @@ export const filterRender = () => {
           style={{justifyContent: 'flex-start'}}
           role="treeitem"
           onClick={onCheck}
-      > 
+      >
         <Checkbox
             onClick={onCheck}
             indeterminate={checkStatus.halfChecked}
             checked={checkStatus.checked}
             style={{ marginRight: 8 }}
         />
-        <Text 
-          ellipsis={{ showTooltip: { opts: { style: { wordBreak: 'break-all'} }}}} 
+        <Text
+          ellipsis={{ showTooltip: { opts: { style: { wordBreak: 'break-all'} }}}}
           style={{ width: 270 }}
         >
             {data.map(item => item.label).join(' / ')}
@@ -1805,7 +1805,7 @@ export const filterRender = () => {
       </li>
     )
   }
-  
+
   return (
       <div>
           <p>鼠标 hover 到选项可查看被省略文本完整内容</p>
@@ -1841,6 +1841,14 @@ export const RefMethods = () => {
     cRef.current.close();
   }, [cRef]);
 
+  const onClickFocus = useCallback(() => {
+    cRef.current.focus();
+  }, [cRef]);
+
+  const onClickBlur = useCallback(() => {
+    cRef.current.blur();
+  }, [cRef]);
+
   const treeData = [
     {
         label: '浙江省',
@@ -1870,7 +1878,13 @@ export const RefMethods = () => {
   return (
       <div>
           <Button onClick={onClickOpen}> cascader visible</Button>
-          <br />
+          <br /><br />
+          <Button onClick={onClickClose}> cascader hidden</Button>
+          <br /><br />
+          <Button onClick={onClickFocus}> cascader focusable</Button>
+          <br /><br />
+          <Button onClick={onClickBlur}> cascader blur</Button>
+          <br /><br />
           <Cascader
               multiple
               ref={cRef}
@@ -2056,7 +2070,7 @@ export const TriggerAddMethods = () => {
           const label = getLabelFromValue(value);
           return <Tag tagKey={value} key={value} closable onClose={onCloseTag} style={{ marginLeft: 2 }}>{label}</Tag>
       };
-      
+
       return (
           <TagInput
               value={Array.from(value)}

+ 15 - 1
packages/semi-ui/cascader/index.tsx

@@ -272,7 +272,7 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
     }
 
     get adapter(): CascaderAdapter {
-        const filterAdapter: Pick<CascaderAdapter, 'updateInputValue' | 'updateInputPlaceHolder' | 'focusInput'> = {
+        const filterAdapter: Pick<CascaderAdapter, 'updateInputValue' | 'updateInputPlaceHolder' | 'focusInput' | 'blurInput'> = {
             updateInputValue: value => {
                 this.setState({ inputValue: value });
             },
@@ -286,6 +286,11 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
                     (this.inputRef.current as any).focus({ preventScroll });
                 }
             },
+            blurInput: () => {
+                if (this.inputRef && this.inputRef.current) {
+                    (this.inputRef.current as any).blur();
+                }
+            },
         };
         const cascaderAdapter: Pick<
         CascaderAdapter,
@@ -629,6 +634,14 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
         this.foundation.open();
     }
 
+    focus() {
+        this.foundation.focus();
+    }
+
+    blur() {
+        this.foundation.blur();
+    }
+
     renderContent = () => {
         const {
             inputValue,
@@ -761,6 +774,7 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
         const { placeholder, filterTreeNode, multiple } = this.props;
         const { checkedKeys } = this.state;
         const searchable = Boolean(filterTreeNode);
+
         if (!searchable) {
             if (multiple) {
                 if (isEmpty(checkedKeys)) {

+ 0 - 74
yarn.lock

@@ -1465,15 +1465,6 @@
     "@douyinfe/semi-animation-styled" "2.23.2"
     classnames "^2.2.6"
 
-"@douyinfe/[email protected]":
-  version "2.32.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.32.2.tgz#c388ed32d1713c13d0400d7c209d8816bc6579cf"
-  integrity sha512-ALi+M7fF5IPQaf2fjLmykMjV/NFiRl9IvaoQsDv4uNFlVLANrZXeuHjqxQb3en6RM2NCcrtq4KwWnvZ8y7V1jw==
-  dependencies:
-    "@douyinfe/semi-animation" "2.12.0"
-    "@douyinfe/semi-animation-styled" "2.23.2"
-    classnames "^2.2.6"
-
 "@douyinfe/[email protected]":
   version "2.23.2"
   resolved "https://registry.npmjs.org/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.23.2.tgz"
@@ -1494,13 +1485,6 @@
   dependencies:
     bezier-easing "^2.1.0"
 
-"@douyinfe/[email protected]":
-  version "2.32.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.32.2.tgz#f0b1164941671fa335d4e54027a5a52e6c09ba2c"
-  integrity sha512-dW/1SpcgBWeEnPDshNysFeAu6L6FDcxiOeYHd7u0C6+Oet+4rtDXe/9HqxhFkVuLbjyy0MCLFI+izTgycUvH3w==
-  dependencies:
-    bezier-easing "^2.1.0"
-
 "@douyinfe/[email protected]":
   version "2.30.2"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.30.2.tgz#f7a3f983da02a2843c8e95604f10c08d5c8e922c"
@@ -1515,20 +1499,6 @@
     memoize-one "^5.2.1"
     scroll-into-view-if-needed "^2.2.24"
 
-"@douyinfe/[email protected]":
-  version "2.32.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.32.2.tgz#c61a621e900fdc9a5605532012808a3722f97ac2"
-  integrity sha512-F+b3u1XvqlufKZilyM0eepk/COfBsnLED/XtEimt2uGWWk+Ftv9Th+CNcxs1mAW3fhM8hqw4EkVA5RYEogHq3g==
-  dependencies:
-    "@douyinfe/semi-animation" "2.12.0"
-    async-validator "^3.5.0"
-    classnames "^2.2.6"
-    date-fns "^2.29.3"
-    date-fns-tz "^1.3.8"
-    lodash "^4.17.21"
-    memoize-one "^5.2.1"
-    scroll-into-view-if-needed "^2.2.24"
-
 "@douyinfe/[email protected]":
   version "2.30.2"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.30.2.tgz#03bf9a5ef99bf3b77557379d631908889767b8aa"
@@ -1536,13 +1506,6 @@
   dependencies:
     classnames "^2.2.6"
 
-"@douyinfe/[email protected]", "@douyinfe/semi-icons@^2.0.0":
-  version "2.32.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.32.2.tgz#ba713d95ae4365bf7d05c1dbb78976ba29c7cf04"
-  integrity sha512-Bo085OU8xhqenTiFepKrEXjY63GI1u8310ZAynHQsg2vRNdSVmQH5RRV2Tgqj27Uq6Q3rtEtN0f/lGxE+MNYQw==
-  dependencies:
-    classnames "^2.2.6"
-
 "@douyinfe/semi-icons@latest":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.9.1.tgz#7a04e1a77070220b04f63e6f65aac30155ed8ddd"
@@ -1556,11 +1519,6 @@
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.30.2.tgz#e31007086d75c98c6b87789945bffcdb72cf195e"
   integrity sha512-cjJ02F4Fg6wfWgVCpGSEUU/MDg/fc6zy2eAxMJ2yMqbERYI99y3Qk4RlckPcUnXx5LzfFAeFtM6E/ri+xMtIKQ==
 
-"@douyinfe/[email protected]":
-  version "2.32.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.32.2.tgz#a580339eea5e526bfc33196f6b5b1a5f07d005b6"
-  integrity sha512-DmDo1/DdjJiFgq0vs2se5zrtM8mFdne5F8dL0g96sHcPFAG2tzVYzlcpgQ11gVRiiCpBJKOcem1UwKe32jWv7w==
-
 "@douyinfe/[email protected]":
   version "2.23.2"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-scss-compile/-/semi-scss-compile-2.23.2.tgz#30884bb194ee9ae1e81877985e5663c3297c1ced"
@@ -1634,38 +1592,6 @@
   dependencies:
     glob "^7.1.6"
 
-"@douyinfe/[email protected]":
-  version "2.32.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.32.2.tgz#5ad25f274b66508f9167e05f154db4fada921162"
-  integrity sha512-qjepAmNQ+1JY8dQVfaXAng0jD+n4dWsz33xWnbIRQ93lgmqBsvP7JRKKcNkOsWe5nbhxP3R0HZMQ5KeJoNm0Bg==
-  dependencies:
-    glob "^7.1.6"
-
-"@douyinfe/semi-ui@^2.0.0":
-  version "2.32.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.32.2.tgz#fb21c2ba6a6a6f650d460dcb35d274d608796dec"
-  integrity sha512-AVOLhJ16FIHZuL6izmlM5AYVyLiPcsUtFxbMdm47d6dgCMA2OevvwrkUZX/uMBvprnM62IP12SB3pfpOUoI0Fw==
-  dependencies:
-    "@douyinfe/semi-animation" "2.32.2"
-    "@douyinfe/semi-animation-react" "2.32.2"
-    "@douyinfe/semi-foundation" "2.32.2"
-    "@douyinfe/semi-icons" "2.32.2"
-    "@douyinfe/semi-illustrations" "2.32.2"
-    "@douyinfe/semi-theme-default" "2.32.2"
-    async-validator "^3.5.0"
-    classnames "^2.2.6"
-    copy-text-to-clipboard "^2.1.1"
-    date-fns "^2.29.3"
-    date-fns-tz "^1.3.8"
-    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.30.2"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.30.2.tgz#75c2ad58ead2f96845381d8af94d32a58dec3d45"