Skip to content

Commit

Permalink
refactor(ui): remove dCloseOnClick in dropdown
Browse files Browse the repository at this point in the history
BREAKING CHANGE: onItemClick support control close.
  • Loading branch information
xiejay97 committed Mar 15, 2023
1 parent 291d28d commit adde61a
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 19 deletions.
2 changes: 1 addition & 1 deletion packages/ui/src/components/cascader/Cascader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,10 @@ function Cascader<V extends DId, T extends DCascaderItem<V>>(
node,
};
})}
dCloseOnClick={false}
onItemClick={(id, item) => {
const checkeds = (item.node as MultipleTreeNode<V, T>).changeStatus('UNCHECKED', select as Set<V>);
changeSelect(Array.from(checkeds.keys()));
return false;
}}
>
<DTag className={`${dPrefix}cascader__multiple-count`} tabIndex={-1} dSize={size}>
Expand Down
22 changes: 16 additions & 6 deletions packages/ui/src/components/dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { isNull, isNumber, isUndefined, nth } from 'lodash';
import React, { useImperativeHandle, useRef, useState } from 'react';
import ReactDOM from 'react-dom';

import { useEvent, useEventCallback, useId, useRefExtra } from '@react-devui/hooks';
import { useEvent, useEventCallback, useId, useImmer, useRefExtra } from '@react-devui/hooks';
import { getClassName, getVerticalSidePosition, scrollToView } from '@react-devui/utils';

import { useMaxIndex, useDValue } from '../../hooks';
Expand Down Expand Up @@ -42,10 +42,9 @@ export interface DDropdownProps<ID extends DId, T extends DDropdownItem<ID>>
dPlacement?: 'top' | 'top-left' | 'top-right' | 'bottom' | 'bottom-left' | 'bottom-right';
dTrigger?: 'hover' | 'click';
dArrow?: boolean;
dCloseOnClick?: boolean;
dZIndex?: number | string;
onVisibleChange?: (visible: boolean) => void;
onItemClick?: (id: T['id'], item: T) => void;
onItemClick?: (id: T['id'], item: T) => void | boolean | Promise<void>;
afterVisibleChange?: (visible: boolean) => void;
}

Expand All @@ -61,7 +60,6 @@ function Dropdown<ID extends DId, T extends DDropdownItem<ID>>(
dPlacement = 'bottom-right',
dTrigger = 'hover',
dArrow = false,
dCloseOnClick = true,
dZIndex,
onVisibleChange,
onItemClick,
Expand Down Expand Up @@ -103,6 +101,8 @@ function Dropdown<ID extends DId, T extends DDropdownItem<ID>>(
const triggerId = children.props.id ?? `${dPrefix}dropdown-trigger-${uniqueId}`;
const getItemId = (id: ID) => `${dPrefix}dropdown-item-${id}-${uniqueId}`;

const [loadingIds, setLoadingIds] = useImmer(new Set<ID>());

const { popupIds, setPopupIds, addPopupId, removePopupId } = useNestedPopup<ID>();
const [focusIds, setFocusIds] = useState<ID[]>([]);
const [isFocus, setIsFocus] = useState(false);
Expand Down Expand Up @@ -237,10 +237,19 @@ function Dropdown<ID extends DId, T extends DDropdownItem<ID>>(
const popupState = popupIds.find((v) => v.id === itemId);

const handleItemClick = () => {
onItemClick?.(itemId, item);
const shouldClose = onItemClick?.(itemId, item);

setFocusIds(subParents.map((parentItem) => parentItem.id).concat([itemId]));
if (dCloseOnClick) {
if (shouldClose instanceof Promise) {
setLoadingIds((draft) => {
draft.add(itemId);
});
shouldClose.then(() => {
setLoadingIds((draft) => {
draft.delete(itemId);
});
});
} else if (shouldClose !== false) {
changeVisible(false);
}
};
Expand Down Expand Up @@ -350,6 +359,7 @@ function Dropdown<ID extends DId, T extends DDropdownItem<ID>>(
dLevel={level}
dIcon={itemIcon}
dFocusVisible={focusVisible && isFocus}
dLoading={loadingIds.has(itemId)}
dDisabled={itemDisabled}
onItemClick={handleItemClick}
>
Expand Down
44 changes: 40 additions & 4 deletions packages/ui/src/components/dropdown/Item.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { LoadingOutlined } from '@react-devui/icons';
import { checkNodeExist, getClassName } from '@react-devui/utils';

import { TTANSITION_DURING_SLOW } from '../../utils';
import { DCollapseTransition } from '../_transition';
import { usePrefixConfig } from '../root';

export interface DItemProps {
Expand All @@ -8,30 +11,63 @@ export interface DItemProps {
dLevel: number;
dIcon: React.ReactNode | undefined;
dFocusVisible: boolean;
dLoading: boolean;
dDisabled: boolean;
onItemClick: () => void;
}

export function DItem(props: DItemProps): JSX.Element | null {
const { children, dId, dLevel, dIcon, dFocusVisible, dDisabled, onItemClick } = props;
const { children, dId, dLevel, dIcon, dFocusVisible, dLoading, dDisabled, onItemClick } = props;

//#region Context
const dPrefix = usePrefixConfig();
//#endregion

const itemIcon = (loading: boolean, iconRef?: React.RefObject<HTMLDivElement>, style?: React.CSSProperties) => (
<div ref={iconRef} className={`${dPrefix}dropdown__item-icon`} style={style}>
{loading ? <LoadingOutlined dSpin /> : dIcon}
</div>
);

return (
<li
id={dId}
className={getClassName(`${dPrefix}dropdown__item`, `${dPrefix}dropdown__item--basic`, {
'is-disabled': dDisabled,
'is-disabled': dDisabled || dLoading,
})}
style={{ paddingLeft: 12 + dLevel * 16 }}
role="menuitem"
aria-disabled={dDisabled}
aria-disabled={dDisabled || dLoading}
onClick={onItemClick}
>
{dFocusVisible && <div className={`${dPrefix}focus-outline`}></div>}
{checkNodeExist(dIcon) && <div className={`${dPrefix}dropdown__item-icon`}>{dIcon}</div>}
{checkNodeExist(dIcon) ? (
itemIcon(dLoading)
) : (
<DCollapseTransition
dOriginalSize={{
width: '',
}}
dCollapsedStyle={{
width: 0,
}}
dIn={dLoading}
dDuring={TTANSITION_DURING_SLOW}
dHorizontal
dStyles={{
entering: {
transition: ['width', 'padding', 'margin'].map((attr) => `${attr} ${TTANSITION_DURING_SLOW}ms linear`).join(', '),
},
leaving: {
transition: ['width', 'padding', 'margin'].map((attr) => `${attr} ${TTANSITION_DURING_SLOW}ms linear`).join(', '),
},
leaved: { display: 'none' },
}}
dDestroyWhenLeaved
>
{(collapseRef, collapseStyle) => itemIcon(true, collapseRef, collapseStyle)}
</DCollapseTransition>
)}
<div className={`${dPrefix}dropdown__item-content`}>{children}</div>
</li>
);
Expand Down
2 changes: 0 additions & 2 deletions packages/ui/src/components/dropdown/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ interface DDropdownProps<ID extends DId, T extends DDropdownItem<ID>> extends Om
dPlacement?: 'top' | 'top-left' | 'top-right' | 'bottom' | 'bottom-left' | 'bottom-right';
dTrigger?: 'hover' | 'click';
dArrow?: boolean;
dCloseOnClick?: boolean;
dZIndex?: number | string;
onVisibleChange?: (visible: boolean) => void;
onItemClick?: (id: T['id'], item: T) => void;
Expand All @@ -42,7 +41,6 @@ interface DDropdownProps<ID extends DId, T extends DDropdownItem<ID>> extends Om
| dPlacement | Set dropdown position | `'bottom-right'` | |
| dTrigger | Set trigger behavior | `'hover'` | |
| dArrow | Whether to show arrows | `false` | |
| dCloseOnClick | Close the dropdown when the menu item is clicked | `true` | |
| dZIndex | Set the `z-index` of the dropdown | - | |
| onVisibleChange | Callback for visible change | - | |
| onItemClick | Callback for menu item click | - | |
Expand Down
2 changes: 0 additions & 2 deletions packages/ui/src/components/dropdown/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ interface DDropdownProps<ID extends DId, T extends DDropdownItem<ID>> extends Om
dPlacement?: 'top' | 'top-left' | 'top-right' | 'bottom' | 'bottom-left' | 'bottom-right';
dTrigger?: 'hover' | 'click';
dArrow?: boolean;
dCloseOnClick?: boolean;
dZIndex?: number | string;
onVisibleChange?: (visible: boolean) => void;
onItemClick?: (id: T['id'], item: T) => void;
Expand All @@ -40,7 +39,6 @@ interface DDropdownProps<ID extends DId, T extends DDropdownItem<ID>> extends Om
| dPlacement | 设置下拉菜单位置 | `'bottom-right'` | |
| dTrigger | 设置触发行为 | `'hover'` | |
| dArrow | 是否展示箭头 | `false` | |
| dCloseOnClick | 当点击菜单项时关闭下拉菜单 | `true` | |
| dZIndex | 设置下拉菜单的 `z-index` | - | |
| onVisibleChange | 显/隐的回调 | - | |
| onItemClick | 点击菜单项的回调 | - | |
Expand Down
14 changes: 13 additions & 1 deletion packages/ui/src/components/dropdown/demos/1.Basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ The simplest usage.
最简单的用法。

```tsx
import { useAsync } from '@react-devui/hooks';
import { DownOutlined, ExperimentOutlined } from '@react-devui/icons';
import { DDropdown, DButton } from '@react-devui/ui';

export default function Demo() {
const async = useAsync();

return (
<>
<DDropdown
Expand All @@ -33,7 +36,7 @@ export default function Demo() {
<DDropdown
dList={[
{ id: 'Item1', label: 'Item 1', type: 'item' },
{ id: 'Item2', label: 'Item 2', type: 'item' },
{ id: 'Item2', label: 'Item 2', type: 'item', icon: <ExperimentOutlined /> },
{
id: 'Group3',
label: 'Group 3',
Expand All @@ -46,6 +49,15 @@ export default function Demo() {
{ id: 'Item4', label: 'Item 4', type: 'item' },
]}
dTrigger="click"
onItemClick={(id) => {
return ['Item1', 'Item2'].includes(id)
? new Promise((r) => {
async.setTimeout(() => {
r(true);
}, 3000);
})
: true;
}}
>
<DButton dType="secondary" dIcon={<DownOutlined />} dIconRight>
Click me
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,9 @@ function Select<V extends DId, T extends DSelectItem<V>>(
disabled: itemDisabled,
};
})}
dCloseOnClick={false}
onItemClick={(id: V) => {
changeSelectByClick(id);
return false;
}}
>
<DTag className={`${dPrefix}select__multiple-count`} tabIndex={-1} dSize={size}>
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,9 @@ function Tabs<ID extends DId, T extends DTabItem<ID>>(props: DTabsProps<ID, T>,
};
})}
dPlacement={dPlacement === 'left' ? 'bottom-left' : 'bottom-right'}
dCloseOnClick={false}
onItemClick={(id: ID) => {
changeActiveId(id);
return false;
}}
>
<div
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/tree-select/TreeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -355,10 +355,10 @@ function TreeSelect<V extends DId, T extends DTreeItem<V>>(
node,
};
})}
dCloseOnClick={false}
onItemClick={(id, item) => {
const checkeds = (item.node as MultipleTreeNode<V, T>).changeStatus('UNCHECKED', select as Set<V>);
changeSelect(Array.from(checkeds.keys()));
return false;
}}
>
<DTag className={`${dPrefix}tree-select__multiple-count`} tabIndex={-1} dSize={size}>
Expand Down

0 comments on commit adde61a

Please sign in to comment.