You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
5.5 KiB
174 lines
5.5 KiB
import type { ExtractPropTypes } from 'vue';
|
|
import { computed, defineComponent } from 'vue';
|
|
import RcDropdown from '../vc-dropdown';
|
|
import DropdownButton from './dropdown-button';
|
|
import { cloneElement } from '../_util/vnode';
|
|
import classNames from '../_util/classNames';
|
|
import { isValidElement, initDefaultProps } from '../_util/props-util';
|
|
import { dropdownProps } from './props';
|
|
import RightOutlined from '@ant-design/icons-vue/RightOutlined';
|
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
|
import devWarning from '../vc-util/devWarning';
|
|
import omit from '../_util/omit';
|
|
import getPlacements from '../tooltip/placements';
|
|
import type { CustomSlotsType } from '../_util/type';
|
|
|
|
export type DropdownProps = Partial<ExtractPropTypes<ReturnType<typeof dropdownProps>>>;
|
|
|
|
const Dropdown = defineComponent({
|
|
compatConfig: { MODE: 3 },
|
|
name: 'ADropdown',
|
|
inheritAttrs: false,
|
|
props: initDefaultProps(dropdownProps(), {
|
|
mouseEnterDelay: 0.15,
|
|
mouseLeaveDelay: 0.1,
|
|
placement: 'bottomLeft',
|
|
trigger: 'hover',
|
|
}),
|
|
// emits: ['visibleChange', 'update:visible'],
|
|
slots: Object as CustomSlotsType<{
|
|
default?: any;
|
|
overlay?: any;
|
|
}>,
|
|
setup(props, { slots, attrs, emit }) {
|
|
const { prefixCls, rootPrefixCls, direction, getPopupContainer } = useConfigInject(
|
|
'dropdown',
|
|
props,
|
|
);
|
|
|
|
const transitionName = computed(() => {
|
|
const { placement = '', transitionName } = props;
|
|
if (transitionName !== undefined) {
|
|
return transitionName;
|
|
}
|
|
if (placement.indexOf('top') >= 0) {
|
|
return `${rootPrefixCls.value}-slide-down`;
|
|
}
|
|
return `${rootPrefixCls.value}-slide-up`;
|
|
});
|
|
|
|
const renderOverlay = () => {
|
|
// rc-dropdown already can process the function of overlay, but we have check logic here.
|
|
// So we need render the element to check and pass back to rc-dropdown.
|
|
const overlay = props.overlay || slots.overlay?.();
|
|
const overlayNode = Array.isArray(overlay) ? overlay[0] : overlay;
|
|
|
|
if (!overlayNode) return null;
|
|
const overlayProps = overlayNode.props || {};
|
|
|
|
// Warning if use other mode
|
|
devWarning(
|
|
!overlayProps.mode || overlayProps.mode === 'vertical',
|
|
'Dropdown',
|
|
`mode="${overlayProps.mode}" is not supported for Dropdown's Menu.`,
|
|
);
|
|
|
|
// menu cannot be selectable in dropdown defaultly
|
|
const { selectable = false, expandIcon = (overlayNode.children as any)?.expandIcon?.() } =
|
|
overlayProps;
|
|
|
|
const overlayNodeExpandIcon =
|
|
typeof expandIcon !== 'undefined' && isValidElement(expandIcon) ? (
|
|
expandIcon
|
|
) : (
|
|
<span class={`${prefixCls.value}-menu-submenu-arrow`}>
|
|
<RightOutlined class={`${prefixCls.value}-menu-submenu-arrow-icon`} />
|
|
</span>
|
|
);
|
|
|
|
const fixedModeOverlay = isValidElement(overlayNode)
|
|
? cloneElement(overlayNode, {
|
|
mode: 'vertical',
|
|
selectable,
|
|
expandIcon: () => overlayNodeExpandIcon,
|
|
})
|
|
: overlayNode;
|
|
|
|
return fixedModeOverlay;
|
|
};
|
|
|
|
const placement = computed(() => {
|
|
const placement = props.placement;
|
|
if (!placement) {
|
|
return direction.value === 'rtl' ? 'bottomRight' : 'bottomLeft';
|
|
}
|
|
|
|
if (placement.includes('Center')) {
|
|
const newPlacement = placement.slice(0, placement.indexOf('Center'));
|
|
devWarning(
|
|
!placement.includes('Center'),
|
|
'Dropdown',
|
|
`You are using '${placement}' placement in Dropdown, which is deprecated. Try to use '${newPlacement}' instead.`,
|
|
);
|
|
return newPlacement;
|
|
}
|
|
return placement;
|
|
});
|
|
|
|
const handleVisibleChange = (val: boolean) => {
|
|
emit('update:visible', val);
|
|
emit('visibleChange', val);
|
|
};
|
|
|
|
return () => {
|
|
const { arrow, trigger, disabled, overlayClassName } = props;
|
|
const child = slots.default?.()[0];
|
|
const dropdownTrigger = cloneElement(
|
|
child,
|
|
Object.assign(
|
|
{
|
|
class: classNames(
|
|
child?.props?.class,
|
|
{
|
|
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
|
},
|
|
`${prefixCls.value}-trigger`,
|
|
),
|
|
},
|
|
disabled ? { disabled } : {},
|
|
),
|
|
);
|
|
|
|
const overlayClassNameCustomized = classNames(overlayClassName, {
|
|
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
|
});
|
|
|
|
const triggerActions = disabled ? [] : trigger;
|
|
let alignPoint: boolean;
|
|
if (triggerActions && triggerActions.indexOf('contextmenu') !== -1) {
|
|
alignPoint = true;
|
|
}
|
|
|
|
const builtinPlacements = getPlacements({
|
|
arrowPointAtCenter: typeof arrow === 'object' && arrow.pointAtCenter,
|
|
autoAdjustOverflow: true,
|
|
});
|
|
const dropdownProps = omit(
|
|
{
|
|
...props,
|
|
...attrs,
|
|
builtinPlacements,
|
|
overlayClassName: overlayClassNameCustomized,
|
|
arrow,
|
|
alignPoint,
|
|
prefixCls: prefixCls.value,
|
|
getPopupContainer: getPopupContainer.value,
|
|
transitionName: transitionName.value,
|
|
trigger: triggerActions,
|
|
onVisibleChange: handleVisibleChange,
|
|
placement: placement.value,
|
|
},
|
|
['overlay', 'onUpdate:visible'],
|
|
);
|
|
return (
|
|
<RcDropdown {...dropdownProps} v-slots={{ overlay: renderOverlay }}>
|
|
{dropdownTrigger}
|
|
</RcDropdown>
|
|
);
|
|
};
|
|
},
|
|
});
|
|
|
|
Dropdown.Button = DropdownButton;
|
|
export default Dropdown;
|