refactor: dropdown
parent
f71a72208b
commit
38258d33ba
|
@ -4,8 +4,6 @@ exports[`DropdownButton should support href like Button 1`] = `
|
|||
<div class="ant-btn-group ant-dropdown-button"><a class="ant-btn ant-btn-default" href="https://ant.design">
|
||||
<!---->
|
||||
</a>
|
||||
<!----><button class="ant-btn ant-btn-default ant-dropdown-trigger" type="button">
|
||||
<!----><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg focusable="false" class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span>
|
||||
</button>
|
||||
<!----><button class="ant-btn ant-btn-default ant-dropdown-trigger ant-btn-icon-only" type="button"><span role="img" aria-label="ellipsis" class="anticon anticon-ellipsis"><svg focusable="false" class="" data-icon="ellipsis" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"></path></svg></span></button>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -24,8 +24,8 @@ describe('DropdownButton', () => {
|
|||
|
||||
it("don't pass visible to Dropdown if it's not exits", () => {
|
||||
const wrapper = mount(Dropdown.Button, {
|
||||
props: {
|
||||
overlay: (
|
||||
slots: {
|
||||
overlay: () => (
|
||||
<Menu>
|
||||
<Menu.Item>foo</Menu.Item>
|
||||
</Menu>
|
||||
|
@ -43,11 +43,13 @@ describe('DropdownButton', () => {
|
|||
return (
|
||||
<Dropdown.Button
|
||||
href="https://ant.design"
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item>foo</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
v-slots={{
|
||||
overlay: () => (
|
||||
<Menu>
|
||||
<Menu.Item>foo</Menu.Item>
|
||||
</Menu>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,117 +1,92 @@
|
|||
import type { VNode, ExtractPropTypes } from 'vue';
|
||||
import { provide, inject, defineComponent } from 'vue';
|
||||
import { ExtractPropTypes } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import Button from '../button';
|
||||
import classNames from '../_util/classNames';
|
||||
import buttonTypes from '../button/buttonTypes';
|
||||
import Dropdown from './dropdown';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { hasProp, getComponent, getSlot } from '../_util/props-util';
|
||||
import getDropdownProps from './getDropdownProps';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import { initDefaultProps } from '../_util/props-util';
|
||||
import { dropdownButtonProps } from './props';
|
||||
import EllipsisOutlined from '@ant-design/icons-vue/EllipsisOutlined';
|
||||
import { tuple } from '../_util/type';
|
||||
|
||||
const ButtonTypesProps = buttonTypes();
|
||||
const DropdownProps = getDropdownProps();
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
const ButtonGroup = Button.Group;
|
||||
const dropdownButtonProps = {
|
||||
...DropdownProps,
|
||||
type: PropTypes.oneOf(tuple('primary', 'ghost', 'dashed', 'danger', 'default')).def('default'),
|
||||
size: PropTypes.oneOf(tuple('small', 'large', 'default')).def('default'),
|
||||
htmlType: ButtonTypesProps.htmlType,
|
||||
href: PropTypes.string,
|
||||
disabled: PropTypes.looseBool,
|
||||
prefixCls: PropTypes.string,
|
||||
placement: DropdownProps.placement.def('bottomRight'),
|
||||
icon: PropTypes.any,
|
||||
title: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
onVisibleChange: PropTypes.func,
|
||||
'onUpdate:visible': PropTypes.func,
|
||||
};
|
||||
export type DropdownButtonProps = Partial<ExtractPropTypes<typeof dropdownButtonProps>>;
|
||||
|
||||
export type DropdownButtonProps = Partial<ExtractPropTypes<ReturnType<typeof dropdownButtonProps>>>;
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ADropdownButton',
|
||||
inheritAttrs: false,
|
||||
props: dropdownButtonProps,
|
||||
__ANT_BUTTON: true,
|
||||
props: initDefaultProps(dropdownButtonProps(), {
|
||||
trigger: 'hover',
|
||||
placement: 'bottomRight',
|
||||
type: 'default',
|
||||
}),
|
||||
emits: ['click', 'visibleChange', 'update:visible'],
|
||||
setup() {
|
||||
return {
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
popupRef: null,
|
||||
slots: ['icon', 'leftButton', 'rightButton', 'overlay'],
|
||||
setup(props, { slots, attrs, emit }) {
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
emit('click', e);
|
||||
};
|
||||
},
|
||||
created() {
|
||||
provide('savePopupRef', this.savePopupRef);
|
||||
},
|
||||
methods: {
|
||||
savePopupRef(ref: VNode) {
|
||||
this.popupRef = ref;
|
||||
},
|
||||
handleClick(e: Event) {
|
||||
this.$emit('click', e);
|
||||
},
|
||||
handleVisibleChange(val: boolean) {
|
||||
this.$emit('update:visible', val);
|
||||
this.$emit('visibleChange', val);
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const {
|
||||
type,
|
||||
disabled,
|
||||
onClick,
|
||||
htmlType,
|
||||
class: className,
|
||||
prefixCls: customizePrefixCls,
|
||||
overlay,
|
||||
trigger,
|
||||
align,
|
||||
visible,
|
||||
onVisibleChange,
|
||||
placement,
|
||||
getPopupContainer,
|
||||
href,
|
||||
title,
|
||||
...restProps
|
||||
} = { ...this.$props, ...this.$attrs } as any;
|
||||
const icon = getComponent(this, 'icon') || <EllipsisOutlined />;
|
||||
const { getPopupContainer: getContextPopupContainer } = this.configProvider;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('dropdown-button', customizePrefixCls);
|
||||
const dropdownProps: any = {
|
||||
align,
|
||||
disabled,
|
||||
trigger: disabled ? [] : trigger,
|
||||
placement,
|
||||
getPopupContainer: getPopupContainer || getContextPopupContainer,
|
||||
onVisibleChange: this.handleVisibleChange,
|
||||
};
|
||||
if (hasProp(this, 'visible')) {
|
||||
dropdownProps.visible = visible;
|
||||
}
|
||||
|
||||
const buttonGroupProps = {
|
||||
...restProps,
|
||||
class: classNames(prefixCls, className),
|
||||
const handleVisibleChange = (val: boolean) => {
|
||||
emit('update:visible', val);
|
||||
emit('visibleChange', val);
|
||||
};
|
||||
|
||||
return (
|
||||
<ButtonGroup {...buttonGroupProps}>
|
||||
const { prefixCls, direction, getPopupContainer } = useConfigInject('dropdown-button', props);
|
||||
|
||||
return () => {
|
||||
const {
|
||||
type,
|
||||
disabled,
|
||||
htmlType,
|
||||
class: className = '',
|
||||
overlay = slots.overlay?.(),
|
||||
trigger,
|
||||
align,
|
||||
visible,
|
||||
onVisibleChange,
|
||||
placement = direction.value === 'rtl' ? 'bottomLeft' : 'bottomRight',
|
||||
href,
|
||||
title,
|
||||
icon = slots.icon?.() || <EllipsisOutlined />,
|
||||
mouseEnterDelay,
|
||||
mouseLeaveDelay,
|
||||
...restProps
|
||||
} = { ...props, ...attrs };
|
||||
|
||||
const dropdownProps = {
|
||||
align,
|
||||
disabled,
|
||||
trigger: disabled ? [] : trigger,
|
||||
placement,
|
||||
getPopupContainer: getPopupContainer.value,
|
||||
onVisibleChange: handleVisibleChange,
|
||||
mouseEnterDelay,
|
||||
mouseLeaveDelay,
|
||||
visible,
|
||||
};
|
||||
|
||||
const leftButton = (
|
||||
<Button
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
onClick={this.handleClick}
|
||||
onClick={handleClick}
|
||||
htmlType={htmlType}
|
||||
href={href}
|
||||
title={title}
|
||||
>
|
||||
{getSlot(this)}
|
||||
</Button>
|
||||
<Dropdown {...dropdownProps} overlay={getComponent(this, 'overlay')}>
|
||||
<Button type={type}>{icon}</Button>
|
||||
</Dropdown>
|
||||
</ButtonGroup>
|
||||
);
|
||||
v-slots={{ default: slots.default }}
|
||||
></Button>
|
||||
);
|
||||
|
||||
const rightButton = <Button type={type} icon={icon} />;
|
||||
|
||||
return (
|
||||
<ButtonGroup {...restProps} class={classNames(prefixCls.value, className)}>
|
||||
{slots.leftButton ? slots.leftButton({ button: leftButton }) : leftButton}
|
||||
<Dropdown {...dropdownProps} v-slots={{ overlay: slots.overlay }}>
|
||||
{slots.rightButton ? slots.rightButton({ button: rightButton }) : rightButton}
|
||||
</Dropdown>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,123 +1,148 @@
|
|||
import type { VNode, ExtractPropTypes } from 'vue';
|
||||
import { provide, inject, cloneVNode, defineComponent } from 'vue';
|
||||
import RcDropdown from '../vc-dropdown/src/index';
|
||||
import { ExtractPropTypes, computed } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import RcDropdown from '../vc-dropdown';
|
||||
import DropdownButton from './dropdown-button';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import classNames from '../_util/classNames';
|
||||
import {
|
||||
getOptionProps,
|
||||
getPropsData,
|
||||
getComponent,
|
||||
isValidElement,
|
||||
getSlot,
|
||||
} from '../_util/props-util';
|
||||
import getDropdownProps from './getDropdownProps';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
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';
|
||||
|
||||
const dropdownProps = getDropdownProps();
|
||||
|
||||
export type DropdownProps = Partial<ExtractPropTypes<typeof dropdownProps>>;
|
||||
export type DropdownProps = Partial<ExtractPropTypes<ReturnType<typeof dropdownProps>>>;
|
||||
|
||||
const Dropdown = defineComponent({
|
||||
name: 'ADropdown',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
...dropdownProps,
|
||||
prefixCls: PropTypes.string,
|
||||
mouseEnterDelay: PropTypes.number.def(0.15),
|
||||
mouseLeaveDelay: PropTypes.number.def(0.1),
|
||||
placement: dropdownProps.placement.def('bottomLeft'),
|
||||
onVisibleChange: PropTypes.func,
|
||||
'onUpdate:visible': PropTypes.func,
|
||||
},
|
||||
props: initDefaultProps(dropdownProps(), {
|
||||
mouseEnterDelay: 0.15,
|
||||
mouseLeaveDelay: 0.1,
|
||||
placement: 'bottomLeft',
|
||||
trigger: 'hover',
|
||||
}),
|
||||
emits: ['visibleChange', 'update:visible'],
|
||||
setup() {
|
||||
return {
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
popupRef: null,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
provide('savePopupRef', this.savePopupRef);
|
||||
},
|
||||
methods: {
|
||||
savePopupRef(ref: VNode) {
|
||||
this.popupRef = ref;
|
||||
},
|
||||
getTransitionName() {
|
||||
const { placement = '', transitionName } = this.$props;
|
||||
slots: ['overlay'],
|
||||
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 'slide-down';
|
||||
return `${rootPrefixCls.value}-slide-down`;
|
||||
}
|
||||
return 'slide-up';
|
||||
},
|
||||
renderOverlay(prefixCls: string) {
|
||||
const overlay = getComponent(this, 'overlay');
|
||||
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;
|
||||
// menu cannot be selectable in dropdown defaultly
|
||||
// menu should be focusable in dropdown defaultly
|
||||
const overlayProps = overlayNode && getPropsData(overlayNode);
|
||||
const { selectable = false, focusable = true } = (overlayProps || {}) as any;
|
||||
const expandIcon = () => (
|
||||
<span class={`${prefixCls}-menu-submenu-arrow`}>
|
||||
<RightOutlined class={`${prefixCls}-menu-submenu-arrow-icon`} />
|
||||
</span>
|
||||
|
||||
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)
|
||||
? cloneVNode(overlayNode, {
|
||||
? cloneElement(overlayNode, {
|
||||
mode: 'vertical',
|
||||
selectable,
|
||||
focusable,
|
||||
expandIcon,
|
||||
expandIcon: () => overlayNodeExpandIcon,
|
||||
})
|
||||
: overlay;
|
||||
return fixedModeOverlay;
|
||||
},
|
||||
handleVisibleChange(val: boolean) {
|
||||
this.$emit('update:visible', val);
|
||||
this.$emit('visibleChange', val);
|
||||
},
|
||||
},
|
||||
: overlayNode;
|
||||
|
||||
render() {
|
||||
const props = getOptionProps(this);
|
||||
const { prefixCls: customizePrefixCls, trigger, disabled, getPopupContainer } = props;
|
||||
const { getPopupContainer: getContextPopupContainer } = this.configProvider;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('dropdown', customizePrefixCls);
|
||||
const child = getSlot(this)[0];
|
||||
const dropdownTrigger = cloneElement(
|
||||
child,
|
||||
Object.assign(
|
||||
{
|
||||
class: classNames(child?.props?.class, `${prefixCls}-trigger`),
|
||||
},
|
||||
disabled ? { disabled } : {},
|
||||
),
|
||||
);
|
||||
const triggerActions = disabled ? [] : typeof trigger === 'string' ? [trigger] : trigger;
|
||||
let alignPoint;
|
||||
if (triggerActions && triggerActions.indexOf('contextmenu') !== -1) {
|
||||
alignPoint = true;
|
||||
}
|
||||
const dropdownProps = {
|
||||
alignPoint,
|
||||
...props,
|
||||
...this.$attrs,
|
||||
prefixCls,
|
||||
getPopupContainer: getPopupContainer || getContextPopupContainer,
|
||||
transitionName: this.getTransitionName(),
|
||||
trigger: triggerActions,
|
||||
overlay: this.renderOverlay(prefixCls),
|
||||
onVisibleChange: this.handleVisibleChange,
|
||||
return fixedModeOverlay;
|
||||
};
|
||||
|
||||
const placement = computed(() => {
|
||||
if (props.placement !== undefined) {
|
||||
return props.placement;
|
||||
}
|
||||
return direction.value === 'rtl' ? 'bottomRight' : 'bottomLeft';
|
||||
});
|
||||
|
||||
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 dropdownProps = omit(
|
||||
{
|
||||
...props,
|
||||
...attrs,
|
||||
overlayClassName: overlayClassNameCustomized,
|
||||
arrow,
|
||||
alignPoint,
|
||||
prefixCls: prefixCls.value,
|
||||
getPopupContainer: getPopupContainer.value,
|
||||
transitionName: transitionName.value,
|
||||
trigger: triggerActions,
|
||||
onVisibleChange: handleVisibleChange,
|
||||
placement: placement.value,
|
||||
},
|
||||
['overlay'],
|
||||
);
|
||||
return (
|
||||
<RcDropdown {...dropdownProps} v-slots={{ overlay: renderOverlay }}>
|
||||
{dropdownTrigger}
|
||||
</RcDropdown>
|
||||
);
|
||||
};
|
||||
return <RcDropdown {...dropdownProps}>{dropdownTrigger}</RcDropdown>;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import { tuple } from '../_util/type';
|
||||
import type { PropType } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
export default () => ({
|
||||
trigger: {
|
||||
type: [Array, String] as PropType<
|
||||
('click' | 'hover' | 'contextmenu')[] | 'click' | 'hover' | 'contextmenu'
|
||||
>,
|
||||
default: 'hover',
|
||||
},
|
||||
overlay: PropTypes.any,
|
||||
visible: PropTypes.looseBool,
|
||||
disabled: PropTypes.looseBool,
|
||||
align: PropTypes.object,
|
||||
getPopupContainer: PropTypes.func,
|
||||
prefixCls: PropTypes.string,
|
||||
transitionName: PropTypes.string,
|
||||
placement: PropTypes.oneOf(
|
||||
tuple('topLeft', 'topCenter', 'topRight', 'bottomLeft', 'bottomCenter', 'bottomRight'),
|
||||
),
|
||||
overlayClassName: PropTypes.string,
|
||||
overlayStyle: PropTypes.style,
|
||||
forceRender: PropTypes.looseBool,
|
||||
mouseEnterDelay: PropTypes.number,
|
||||
mouseLeaveDelay: PropTypes.number,
|
||||
openClassName: PropTypes.string,
|
||||
minOverlayWidthMatchTrigger: PropTypes.looseBool,
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import type { App, Plugin } from 'vue';
|
||||
import Dropdown from './dropdown';
|
||||
import DropdownButton from './dropdown-button';
|
||||
|
||||
import { dropdownProps, dropdownButtonProps } from './props';
|
||||
export type { DropdownProps } from './dropdown';
|
||||
export type { DropdownButtonProps } from './dropdown-button';
|
||||
|
||||
|
@ -14,7 +14,7 @@ Dropdown.install = function (app: App) {
|
|||
return app;
|
||||
};
|
||||
|
||||
export { DropdownButton };
|
||||
export { DropdownButton, dropdownProps, dropdownButtonProps };
|
||||
|
||||
export default Dropdown as typeof Dropdown &
|
||||
Plugin & {
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import { tuple } from '../_util/type';
|
||||
import type { PropType } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
|
||||
import buttonTypes from '../button/buttonTypes';
|
||||
|
||||
type Align = {
|
||||
points?: [string, string];
|
||||
offset?: [number, number];
|
||||
targetOffset?: [number, number];
|
||||
overflow?: {
|
||||
adjustX?: boolean;
|
||||
adjustY?: boolean;
|
||||
};
|
||||
useCssRight?: boolean;
|
||||
useCssBottom?: boolean;
|
||||
useCssTransform?: boolean;
|
||||
};
|
||||
const dropdownProps = () => ({
|
||||
arrow: PropTypes.looseBool,
|
||||
trigger: {
|
||||
type: [Array, String] as PropType<
|
||||
('click' | 'hover' | 'contextmenu')[] | 'click' | 'hover' | 'contextmenu'
|
||||
>,
|
||||
},
|
||||
overlay: PropTypes.any,
|
||||
visible: PropTypes.looseBool,
|
||||
disabled: PropTypes.looseBool,
|
||||
align: { type: Object as PropType<Align> },
|
||||
getPopupContainer: PropTypes.func,
|
||||
prefixCls: PropTypes.string,
|
||||
transitionName: PropTypes.string,
|
||||
placement: PropTypes.oneOf(
|
||||
tuple('topLeft', 'topCenter', 'topRight', 'bottomLeft', 'bottomCenter', 'bottomRight'),
|
||||
),
|
||||
overlayClassName: PropTypes.string,
|
||||
overlayStyle: PropTypes.style,
|
||||
forceRender: PropTypes.looseBool,
|
||||
mouseEnterDelay: PropTypes.number,
|
||||
mouseLeaveDelay: PropTypes.number,
|
||||
openClassName: PropTypes.string,
|
||||
minOverlayWidthMatchTrigger: PropTypes.looseBool,
|
||||
});
|
||||
|
||||
const ButtonTypesProps = buttonTypes();
|
||||
const dropdownButtonProps = () => ({
|
||||
...dropdownProps(),
|
||||
type: PropTypes.oneOf(tuple('primary', 'ghost', 'dashed', 'default')),
|
||||
size: PropTypes.oneOf(tuple('small', 'large')),
|
||||
htmlType: ButtonTypesProps.htmlType,
|
||||
href: PropTypes.string,
|
||||
disabled: PropTypes.looseBool,
|
||||
prefixCls: PropTypes.string,
|
||||
icon: PropTypes.any,
|
||||
title: PropTypes.string,
|
||||
});
|
||||
|
||||
export { dropdownProps, dropdownButtonProps };
|
||||
|
||||
export default dropdownProps;
|
|
@ -1,5 +1,6 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import './status';
|
||||
|
||||
@dropdown-prefix-cls: ~'@{ant-prefix}-dropdown';
|
||||
|
||||
|
@ -27,11 +28,11 @@
|
|||
position: relative;
|
||||
|
||||
.@{ant-prefix}-btn > .@{iconfont-css-prefix}-down {
|
||||
.iconfont-size-under-12px(10px);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.@{iconfont-css-prefix}-down::before {
|
||||
transition: transform 0.2s;
|
||||
transition: transform @animation-duration-base;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,10 +43,81 @@
|
|||
}
|
||||
|
||||
&-hidden,
|
||||
&-menu-hidden {
|
||||
&-menu-hidden,
|
||||
&-menu-submenu-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Offset the popover to account for the dropdown arrow
|
||||
&-show-arrow&-placement-topCenter,
|
||||
&-show-arrow&-placement-topLeft,
|
||||
&-show-arrow&-placement-topRight {
|
||||
padding-bottom: @popover-distance;
|
||||
}
|
||||
|
||||
&-show-arrow&-placement-bottomCenter,
|
||||
&-show-arrow&-placement-bottomLeft,
|
||||
&-show-arrow&-placement-bottomRight {
|
||||
padding-top: @popover-distance;
|
||||
}
|
||||
|
||||
// Arrows
|
||||
// .popover-arrow is outer, .popover-arrow:after is inner
|
||||
|
||||
&-arrow {
|
||||
position: absolute;
|
||||
z-index: 1; // lift it up so the menu wouldn't cask shadow on it
|
||||
display: block;
|
||||
width: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
|
||||
height: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
|
||||
background: transparent;
|
||||
border-style: solid;
|
||||
border-width: (sqrt(@popover-arrow-width * @popover-arrow-width * 2) / 2);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
&-placement-topCenter > &-arrow,
|
||||
&-placement-topLeft > &-arrow,
|
||||
&-placement-topRight > &-arrow {
|
||||
bottom: @popover-distance - @popover-arrow-width + 2.2px;
|
||||
border-top-color: transparent;
|
||||
border-right-color: @popover-bg;
|
||||
border-bottom-color: @popover-bg;
|
||||
border-left-color: transparent;
|
||||
box-shadow: 3px 3px 7px fade(@black, 7%);
|
||||
}
|
||||
&-placement-topCenter > &-arrow {
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
}
|
||||
&-placement-topLeft > &-arrow {
|
||||
left: 16px;
|
||||
}
|
||||
&-placement-topRight > &-arrow {
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
&-placement-bottomCenter > &-arrow,
|
||||
&-placement-bottomLeft > &-arrow,
|
||||
&-placement-bottomRight > &-arrow {
|
||||
top: @popover-distance - @popover-arrow-width + 2px;
|
||||
border-top-color: @popover-bg;
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: @popover-bg;
|
||||
box-shadow: -2px -2px 5px fade(@black, 6%);
|
||||
}
|
||||
&-placement-bottomCenter > &-arrow {
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
}
|
||||
&-placement-bottomLeft > &-arrow {
|
||||
left: 16px;
|
||||
}
|
||||
&-placement-bottomRight > &-arrow {
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
&-menu {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
|
@ -57,12 +129,11 @@
|
|||
border-radius: @border-radius-base;
|
||||
outline: none;
|
||||
box-shadow: @box-shadow-base;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
|
||||
&-item-group-title {
|
||||
padding: 5px @control-padding-horizontal;
|
||||
color: @text-color-secondary;
|
||||
transition: all 0.3s;
|
||||
transition: all @animation-duration-slow;
|
||||
}
|
||||
|
||||
&-submenu-popup {
|
||||
|
@ -72,10 +143,6 @@
|
|||
box-shadow: none;
|
||||
transform-origin: 0 0;
|
||||
|
||||
> .@{dropdown-prefix-cls}-menu {
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
ul,
|
||||
li {
|
||||
list-style: none;
|
||||
|
@ -87,6 +154,42 @@
|
|||
}
|
||||
}
|
||||
|
||||
// ======================= Item Content =======================
|
||||
&-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-item-icon {
|
||||
min-width: 12px;
|
||||
margin-right: 8px;
|
||||
font-size: @font-size-sm;
|
||||
}
|
||||
|
||||
&-title-content {
|
||||
flex: auto;
|
||||
|
||||
> a {
|
||||
color: inherit;
|
||||
transition: all @animation-duration-slow;
|
||||
|
||||
&:hover {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================== Item ===========================
|
||||
&-item,
|
||||
&-submenu-title {
|
||||
clear: both;
|
||||
|
@ -98,25 +201,7 @@
|
|||
line-height: @dropdown-line-height;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
> .anticon:first-child,
|
||||
> span > .anticon:first-child {
|
||||
min-width: 12px;
|
||||
margin-right: 8px;
|
||||
font-size: @font-size-sm;
|
||||
}
|
||||
|
||||
> a {
|
||||
display: block;
|
||||
margin: -@dropdown-vertical-padding -@control-padding-horizontal;
|
||||
padding: @dropdown-vertical-padding @control-padding-horizontal;
|
||||
color: @text-color;
|
||||
transition: all 0.3s;
|
||||
&:hover {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
transition: all @animation-duration-slow;
|
||||
|
||||
&:first-child {
|
||||
& when (@dropdown-edge-child-vertical-padding = 0) {
|
||||
|
@ -130,8 +215,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-selected,
|
||||
&-selected > a {
|
||||
&-selected {
|
||||
color: @dropdown-selected-color;
|
||||
background-color: @item-active-bg;
|
||||
}
|
||||
|
@ -146,9 +230,13 @@
|
|||
|
||||
&:hover {
|
||||
color: @disabled-color;
|
||||
background-color: @component-background;
|
||||
background-color: @dropdown-menu-submenu-disabled-bg;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
a {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-divider {
|
||||
|
@ -159,13 +247,15 @@
|
|||
background-color: @border-color-split;
|
||||
}
|
||||
|
||||
.@{dropdown-prefix-cls}-menu-submenu-arrow {
|
||||
.@{dropdown-prefix-cls}-menu-submenu-expand-icon {
|
||||
position: absolute;
|
||||
right: @padding-xs;
|
||||
&-icon {
|
||||
|
||||
.@{dropdown-prefix-cls}-menu-submenu-arrow-icon {
|
||||
margin-right: 0 !important;
|
||||
color: @text-color-secondary;
|
||||
font-size: 10px;
|
||||
font-style: normal;
|
||||
.iconfont-size-under-12px(10px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +267,7 @@
|
|||
}
|
||||
|
||||
&-submenu-title {
|
||||
padding-right: 26px;
|
||||
padding-right: @control-padding-horizontal + @font-size-sm;
|
||||
}
|
||||
|
||||
&-submenu-vertical {
|
||||
|
@ -208,54 +298,54 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.slide-down-enter.slide-down-enter-active&-placement-bottomLeft,
|
||||
&.slide-down-appear.slide-down-appear-active&-placement-bottomLeft,
|
||||
&.slide-down-enter.slide-down-enter-active&-placement-bottomCenter,
|
||||
&.slide-down-appear.slide-down-appear-active&-placement-bottomCenter,
|
||||
&.slide-down-enter.slide-down-enter-active&-placement-bottomRight,
|
||||
&.slide-down-appear.slide-down-appear-active&-placement-bottomRight {
|
||||
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottomLeft,
|
||||
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottomLeft,
|
||||
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottomCenter,
|
||||
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottomCenter,
|
||||
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottomRight,
|
||||
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottomRight {
|
||||
animation-name: antSlideUpIn;
|
||||
}
|
||||
|
||||
&.slide-up-enter.slide-up-enter-active&-placement-topLeft,
|
||||
&.slide-up-appear.slide-up-appear-active&-placement-topLeft,
|
||||
&.slide-up-enter.slide-up-enter-active&-placement-topCenter,
|
||||
&.slide-up-appear.slide-up-appear-active&-placement-topCenter,
|
||||
&.slide-up-enter.slide-up-enter-active&-placement-topRight,
|
||||
&.slide-up-appear.slide-up-appear-active&-placement-topRight {
|
||||
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-topLeft,
|
||||
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-topLeft,
|
||||
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-topCenter,
|
||||
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-topCenter,
|
||||
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-topRight,
|
||||
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-topRight {
|
||||
animation-name: antSlideDownIn;
|
||||
}
|
||||
|
||||
&.slide-down-leave.slide-down-leave-active&-placement-bottomLeft,
|
||||
&.slide-down-leave.slide-down-leave-active&-placement-bottomCenter,
|
||||
&.slide-down-leave.slide-down-leave-active&-placement-bottomRight {
|
||||
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottomLeft,
|
||||
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottomCenter,
|
||||
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottomRight {
|
||||
animation-name: antSlideUpOut;
|
||||
}
|
||||
|
||||
&.slide-up-leave.slide-up-leave-active&-placement-topLeft,
|
||||
&.slide-up-leave.slide-up-leave-active&-placement-topCenter,
|
||||
&.slide-up-leave.slide-up-leave-active&-placement-topRight {
|
||||
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-topLeft,
|
||||
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-topCenter,
|
||||
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-topRight {
|
||||
animation-name: antSlideDownOut;
|
||||
}
|
||||
}
|
||||
|
||||
.@{dropdown-prefix-cls}-trigger,
|
||||
.@{dropdown-prefix-cls}-link {
|
||||
.@{dropdown-prefix-cls}-link,
|
||||
.@{dropdown-prefix-cls}-button {
|
||||
> .@{iconfont-css-prefix}.@{iconfont-css-prefix}-down {
|
||||
.iconfont-size-under-12px(10px);
|
||||
font-size: 10px;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
.@{dropdown-prefix-cls}-button {
|
||||
white-space: nowrap;
|
||||
|
||||
&.@{ant-prefix}-btn-group > .@{ant-prefix}-btn:last-child:not(:first-child) {
|
||||
&.@{ant-prefix}-btn-group
|
||||
> .@{ant-prefix}-btn:last-child:not(:first-child):not(.@{ant-prefix}-btn-icon-only) {
|
||||
padding-right: @padding-xs;
|
||||
padding-left: @padding-xs;
|
||||
}
|
||||
.@{iconfont-css-prefix}.@{iconfont-css-prefix}-down {
|
||||
.iconfont-size-under-12px(10px);
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/4903
|
||||
|
@ -266,7 +356,8 @@
|
|||
}
|
||||
.@{dropdown-prefix-cls}-menu-item,
|
||||
.@{dropdown-prefix-cls}-menu-submenu-title,
|
||||
.@{dropdown-prefix-cls}-menu-item > a {
|
||||
.@{dropdown-prefix-cls}-menu-item > a,
|
||||
.@{dropdown-prefix-cls}-menu-item > .@{iconfont-css-prefix} + span > a {
|
||||
color: @text-color-secondary-dark;
|
||||
.@{dropdown-prefix-cls}-menu-submenu-arrow::after {
|
||||
color: @text-color-secondary-dark;
|
||||
|
@ -285,3 +376,5 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@import './rtl';
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@dropdown-prefix-cls: ~'@{ant-prefix}-dropdown';
|
||||
|
||||
.@{dropdown-prefix-cls} {
|
||||
&-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
&::before {
|
||||
.@{dropdown-prefix-cls}-rtl& {
|
||||
right: -7px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-menu {
|
||||
&&-rtl {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&-item-group-title {
|
||||
.@{dropdown-prefix-cls}-rtl & {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
&-submenu-popup {
|
||||
&.@{dropdown-prefix-cls}-menu-submenu-rtl {
|
||||
transform-origin: 100% 0;
|
||||
}
|
||||
|
||||
ul,
|
||||
li {
|
||||
.@{dropdown-prefix-cls}-rtl & {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-item,
|
||||
&-submenu-title {
|
||||
.@{dropdown-prefix-cls}-rtl & {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
> .@{iconfont-css-prefix}:first-child,
|
||||
> span > .@{iconfont-css-prefix}:first-child {
|
||||
.@{dropdown-prefix-cls}-rtl & {
|
||||
margin-right: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{dropdown-prefix-cls}-menu-submenu-arrow {
|
||||
.@{dropdown-prefix-cls}-rtl & {
|
||||
right: auto;
|
||||
left: @padding-xs;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
.@{dropdown-prefix-cls}-rtl & {
|
||||
margin-left: 0 !important;
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-submenu-title {
|
||||
.@{dropdown-prefix-cls}-rtl & {
|
||||
padding-right: @control-padding-horizontal;
|
||||
padding-left: @control-padding-horizontal + @font-size-sm;
|
||||
}
|
||||
}
|
||||
|
||||
&-submenu-vertical > & {
|
||||
.@{dropdown-prefix-cls}-rtl & {
|
||||
right: 100%;
|
||||
left: 0;
|
||||
margin-right: 4px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
@import './index';
|
||||
|
||||
.@{dropdown-prefix-cls}-menu-item {
|
||||
&&-danger {
|
||||
color: @error-color;
|
||||
|
||||
&:hover {
|
||||
color: @text-color-inverse;
|
||||
background-color: @error-color;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import Overflow from '../../vc-overflow';
|
|||
import MenuItem from './MenuItem';
|
||||
import SubMenu from './SubMenu';
|
||||
import EllipsisOutlined from '@ant-design/icons-vue/EllipsisOutlined';
|
||||
import { cloneElement } from '../../_util/vnode';
|
||||
|
||||
export const menuProps = {
|
||||
prefixCls: String,
|
||||
|
@ -345,7 +346,17 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
const lastVisibleIndex = ref(0);
|
||||
|
||||
const expandIcon: MenuProps['expandIcon'] = opt => {
|
||||
let icon = props.expandIcon || slots.expandIcon;
|
||||
icon = typeof icon === 'function' ? icon(opt) : icon;
|
||||
return cloneElement(
|
||||
icon,
|
||||
{
|
||||
class: `${prefixCls.value}-submenu-expand-icon`,
|
||||
},
|
||||
false,
|
||||
);
|
||||
};
|
||||
useProvideMenu({
|
||||
store,
|
||||
prefixCls,
|
||||
|
@ -374,7 +385,7 @@ export default defineComponent({
|
|||
unRegisterMenuInfo,
|
||||
selectedSubMenuEventKeys,
|
||||
isRootMenu: ref(true),
|
||||
expandIcon: props.expandIcon || slots.expandIcon,
|
||||
expandIcon,
|
||||
});
|
||||
return () => {
|
||||
const childList = flattenChildren(slots.default?.());
|
||||
|
|
|
@ -5,7 +5,6 @@ import Tooltip from '../tooltip';
|
|||
import abstractTooltipProps from '../tooltip/abstractTooltipProps';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { initDefaultProps } from '../_util/props-util';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import type { ButtonProps, LegacyButtonType } from '../button/buttonTypes';
|
||||
import { convertLegacyProps } from '../button/buttonTypes';
|
||||
import ExclamationCircleFilled from '@ant-design/icons-vue/ExclamationCircleFilled';
|
||||
|
@ -47,7 +46,6 @@ export interface PopconfirmLocale {
|
|||
|
||||
const Popconfirm = defineComponent({
|
||||
name: 'APopconfirm',
|
||||
mixins: [BaseMixin],
|
||||
props: initDefaultProps(popconfirmProps(), {
|
||||
trigger: 'click',
|
||||
transitionName: 'zoom-big',
|
||||
|
|
|
@ -7,7 +7,7 @@ import raf from '../_util/raf';
|
|||
export default defineComponent({
|
||||
name: 'SliderTooltip',
|
||||
inheritAttrs: false,
|
||||
props: tooltipProps,
|
||||
props: tooltipProps(),
|
||||
setup(props, { attrs, slots }) {
|
||||
const innerRef = ref<any>(null);
|
||||
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
import { computed, defineComponent, ref, watch } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import Trigger from '../vc-trigger';
|
||||
import placements from './placements';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import classNames from '../_util/classNames';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
minOverlayWidthMatchTrigger: PropTypes.looseBool,
|
||||
arrow: PropTypes.looseBool.def(false),
|
||||
prefixCls: PropTypes.string.def('rc-dropdown'),
|
||||
transitionName: PropTypes.string,
|
||||
overlayClassName: PropTypes.string.def(''),
|
||||
openClassName: PropTypes.string,
|
||||
animation: PropTypes.any,
|
||||
align: PropTypes.object,
|
||||
overlayStyle: PropTypes.style,
|
||||
placement: PropTypes.string.def('bottomLeft'),
|
||||
overlay: PropTypes.any,
|
||||
trigger: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]).def(
|
||||
'hover',
|
||||
),
|
||||
alignPoint: PropTypes.looseBool,
|
||||
showAction: PropTypes.array,
|
||||
hideAction: PropTypes.array,
|
||||
getPopupContainer: PropTypes.func,
|
||||
visible: PropTypes.looseBool,
|
||||
defaultVisible: PropTypes.looseBool.def(false),
|
||||
mouseEnterDelay: PropTypes.number.def(0.15),
|
||||
mouseLeaveDelay: PropTypes.number.def(0.1),
|
||||
},
|
||||
emits: ['visibleChange', 'overlayClick'],
|
||||
slots: ['overlay'],
|
||||
setup(props, { slots, emit, expose }) {
|
||||
const triggerVisible = ref(!!props.visible);
|
||||
watch(
|
||||
() => props.visible,
|
||||
val => {
|
||||
if (val !== undefined) {
|
||||
triggerVisible.value = val;
|
||||
}
|
||||
},
|
||||
);
|
||||
const triggerRef = ref();
|
||||
|
||||
expose({
|
||||
triggerRef,
|
||||
});
|
||||
|
||||
const onClick = (e: MouseEvent) => {
|
||||
if (props.visible === undefined) {
|
||||
triggerVisible.value = false;
|
||||
}
|
||||
|
||||
emit('overlayClick', e);
|
||||
};
|
||||
|
||||
const onVisibleChange = (visible: boolean) => {
|
||||
if (props.visible === undefined) {
|
||||
triggerVisible.value = visible;
|
||||
}
|
||||
emit('visibleChange', visible);
|
||||
};
|
||||
|
||||
const getMenuElement = () => {
|
||||
const overlayElement = slots.overlay?.();
|
||||
const extraOverlayProps = {
|
||||
prefixCls: `${props.prefixCls}-menu`,
|
||||
onClick,
|
||||
getPopupContainer: () => triggerRef.value.getPopupDomNode(),
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{props.arrow && <div class={`${props.prefixCls}-arrow`} />}
|
||||
{cloneElement(overlayElement, extraOverlayProps, false)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const minOverlayWidthMatchTrigger = computed(() => {
|
||||
const { minOverlayWidthMatchTrigger: matchTrigger = !props.alignPoint } = props;
|
||||
return matchTrigger;
|
||||
});
|
||||
|
||||
const renderChildren = () => {
|
||||
const children = slots.default?.();
|
||||
return triggerVisible.value && children
|
||||
? cloneElement(
|
||||
children[0],
|
||||
{ class: props.openClassName || `${props.prefixCls}-open` },
|
||||
false,
|
||||
)
|
||||
: children;
|
||||
};
|
||||
|
||||
const triggerHideAction = computed(() => {
|
||||
if (!props.hideAction && props.trigger.indexOf('contextmenu') !== -1) {
|
||||
return ['click'];
|
||||
}
|
||||
return props.hideAction;
|
||||
});
|
||||
return () => {
|
||||
const {
|
||||
prefixCls,
|
||||
arrow,
|
||||
showAction,
|
||||
overlayStyle,
|
||||
trigger,
|
||||
placement,
|
||||
align,
|
||||
getPopupContainer,
|
||||
transitionName,
|
||||
animation,
|
||||
overlayClassName,
|
||||
...otherProps
|
||||
} = props;
|
||||
return (
|
||||
<Trigger
|
||||
{...otherProps}
|
||||
prefixCls={prefixCls}
|
||||
ref={triggerRef}
|
||||
popupClassName={classNames(overlayClassName, {
|
||||
[`${prefixCls}-show-arrow`]: arrow,
|
||||
})}
|
||||
popupStyle={overlayStyle}
|
||||
builtinPlacements={placements}
|
||||
action={trigger}
|
||||
showAction={showAction}
|
||||
hideAction={triggerHideAction.value || []}
|
||||
popupPlacement={placement}
|
||||
popupAlign={align}
|
||||
popupTransitionName={transitionName}
|
||||
popupAnimation={animation}
|
||||
popupVisible={triggerVisible.value}
|
||||
stretch={minOverlayWidthMatchTrigger.value ? 'minWidth' : ''}
|
||||
onPopupVisibleChange={onVisibleChange}
|
||||
getPopupContainer={getPopupContainer}
|
||||
v-slots={{ popup: getMenuElement, default: renderChildren }}
|
||||
></Trigger>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -1,225 +0,0 @@
|
|||
@dropdownPrefixCls: rc-dropdown;
|
||||
|
||||
@font-face {
|
||||
font-family: 'anticon';
|
||||
src: url('//at.alicdn.com/t/font_1434092639_4910953.eot');
|
||||
/* IE9*/
|
||||
src: url('//at.alicdn.com/t/font_1434092639_4910953.eot?#iefix') format('embedded-opentype'),
|
||||
/* IE6-IE8 */ url('//at.alicdn.com/t/font_1434092639_4910953.woff') format('woff'),
|
||||
/* chrome、firefox */ url('//at.alicdn.com/t/font_1434092639_4910953.ttf') format('truetype'),
|
||||
/* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
|
||||
url('//at.alicdn.com/t/font_1434092639_4910953.svg#iconfont') format('svg');
|
||||
/* iOS 4.1- */
|
||||
}
|
||||
|
||||
.@{dropdownPrefixCls} {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
top: -9999px;
|
||||
z-index: 1070;
|
||||
display: block;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
&-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-menu {
|
||||
outline: none;
|
||||
position: relative;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 2px 0 0 0;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 5px #ccc;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ccc;
|
||||
|
||||
> li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: rgb(255, 255, 255);
|
||||
background: rgba(255, 255, 255, 0.01);
|
||||
}
|
||||
|
||||
& > &-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 7px 10px;
|
||||
clear: both;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
color: #666666;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover,
|
||||
&-active,
|
||||
&-selected {
|
||||
background-color: #ebfaff;
|
||||
}
|
||||
|
||||
&-selected {
|
||||
position: relative;
|
||||
&:after {
|
||||
content: '\e613';
|
||||
font-family: 'anticon';
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 16px;
|
||||
color: #3cb8f0;
|
||||
}
|
||||
}
|
||||
|
||||
&-disabled {
|
||||
color: #ccc;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
|
||||
&:hover {
|
||||
color: #ccc;
|
||||
background-color: #fff;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
|
||||
&-divider {
|
||||
height: 1px;
|
||||
margin: 1px 0;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5;
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.effect() {
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
transform-origin: 0 0;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
&-slide-up-enter,
|
||||
&-slide-up-appear {
|
||||
.effect();
|
||||
opacity: 0;
|
||||
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
&-slide-up-leave {
|
||||
.effect();
|
||||
opacity: 1;
|
||||
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-bottomLeft,
|
||||
&-slide-up-appear&-slide-up-appear-active&-placement-bottomLeft,
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-bottomCenter,
|
||||
&-slide-up-appear&-slide-up-appear-active&-placement-bottomCenter,
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-bottomRight,
|
||||
&-slide-up-appear&-slide-up-appear-active&-placement-bottomRight {
|
||||
animation-name: rcDropdownSlideUpIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-topLeft,
|
||||
&-slide-up-appear&-slide-up-appear-active&-placement-topLeft,
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-topCenter,
|
||||
&-slide-up-appear&-slide-up-appear-active&-placement-topCenter,
|
||||
&-slide-up-enter&-slide-up-enter-active&-placement-topRight,
|
||||
&-slide-up-appear&-slide-up-appear-active&-placement-topRight {
|
||||
animation-name: rcDropdownSlideDownIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
&-slide-up-leave&-slide-up-leave-active&-placement-bottomLeft,
|
||||
&-slide-up-leave&-slide-up-leave-active&-placement-bottomCenter,
|
||||
&-slide-up-leave&-slide-up-leave-active&-placement-bottomRight {
|
||||
animation-name: rcDropdownSlideUpOut;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
&-slide-up-leave&-slide-up-leave-active&-placement-topLeft,
|
||||
&-slide-up-leave&-slide-up-leave-active&-placement-topCenter,
|
||||
&-slide-up-leave&-slide-up-leave-active&-placement-topRight {
|
||||
animation-name: rcDropdownSlideDownOut;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
@keyframes rcDropdownSlideUpIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform-origin: 0% 0%;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform-origin: 0% 0%;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
@keyframes rcDropdownSlideUpOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform-origin: 0% 0%;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform-origin: 0% 0%;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rcDropdownSlideDownIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform-origin: 0% 100%;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform-origin: 0% 100%;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
@keyframes rcDropdownSlideDownOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform-origin: 0% 100%;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform-origin: 0% 100%;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
// base in 2.4.1
|
||||
// base in 3.2.0
|
||||
import Dropdown from './Dropdown';
|
||||
export default Dropdown;
|
|
@ -5,7 +5,7 @@ const autoAdjustOverflow = {
|
|||
|
||||
const targetOffset = [0, 0];
|
||||
|
||||
export const placements = {
|
||||
const placements = {
|
||||
topLeft: {
|
||||
points: ['bl', 'tl'],
|
||||
overflow: autoAdjustOverflow,
|
|
@ -1,202 +0,0 @@
|
|||
import { defineComponent, Text } from 'vue';
|
||||
import PropTypes from '../../_util/vue-types';
|
||||
import Trigger from '../../vc-trigger';
|
||||
import placements from './placements';
|
||||
import {
|
||||
hasProp,
|
||||
getComponent,
|
||||
getOptionProps,
|
||||
getSlot,
|
||||
findDOMNode,
|
||||
} from '../../_util/props-util';
|
||||
import BaseMixin from '../../_util/BaseMixin';
|
||||
import { cloneElement } from '../../_util/vnode';
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [BaseMixin],
|
||||
props: {
|
||||
minOverlayWidthMatchTrigger: PropTypes.looseBool,
|
||||
prefixCls: PropTypes.string.def('rc-dropdown'),
|
||||
transitionName: PropTypes.string,
|
||||
overlayClassName: PropTypes.string.def(''),
|
||||
openClassName: PropTypes.string,
|
||||
animation: PropTypes.any,
|
||||
align: PropTypes.object,
|
||||
overlayStyle: PropTypes.style,
|
||||
placement: PropTypes.string.def('bottomLeft'),
|
||||
overlay: PropTypes.any,
|
||||
trigger: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]).def(
|
||||
'hover',
|
||||
),
|
||||
alignPoint: PropTypes.looseBool,
|
||||
showAction: PropTypes.array,
|
||||
hideAction: PropTypes.array,
|
||||
getPopupContainer: PropTypes.func,
|
||||
visible: PropTypes.looseBool,
|
||||
defaultVisible: PropTypes.looseBool.def(false),
|
||||
mouseEnterDelay: PropTypes.number.def(0.15),
|
||||
mouseLeaveDelay: PropTypes.number.def(0.1),
|
||||
},
|
||||
data() {
|
||||
let sVisible = this.defaultVisible;
|
||||
if (hasProp(this, 'visible')) {
|
||||
sVisible = this.visible;
|
||||
}
|
||||
return {
|
||||
sVisible,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val !== undefined) {
|
||||
this.setState({
|
||||
sVisible: val,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onClick(e) {
|
||||
const overlayProps = this.getOverlayElement().props;
|
||||
// do no call onVisibleChange, if you need click to hide, use onClick and control visible
|
||||
if (!hasProp(this, 'visible')) {
|
||||
this.setState({
|
||||
sVisible: false,
|
||||
});
|
||||
}
|
||||
this.__emit('overlayClick', e);
|
||||
if (overlayProps.onClick) {
|
||||
overlayProps.onClick(e);
|
||||
}
|
||||
},
|
||||
|
||||
onVisibleChange(visible) {
|
||||
if (!hasProp(this, 'visible')) {
|
||||
this.setState({
|
||||
sVisible: visible,
|
||||
});
|
||||
}
|
||||
this.__emit('update:visible', visible);
|
||||
this.__emit('visibleChange', visible);
|
||||
},
|
||||
|
||||
getMinOverlayWidthMatchTrigger() {
|
||||
const props = getOptionProps(this);
|
||||
const { minOverlayWidthMatchTrigger, alignPoint } = props;
|
||||
if ('minOverlayWidthMatchTrigger' in props) {
|
||||
return minOverlayWidthMatchTrigger;
|
||||
}
|
||||
|
||||
return !alignPoint;
|
||||
},
|
||||
|
||||
getOverlayElement() {
|
||||
const overlay = getComponent(this, 'overlay');
|
||||
return Array.isArray(overlay) ? overlay[0] : overlay;
|
||||
},
|
||||
|
||||
getMenuElement() {
|
||||
const { onClick, prefixCls } = this;
|
||||
const overlayElement = this.getOverlayElement();
|
||||
const extraOverlayProps = {
|
||||
prefixCls: `${prefixCls}-menu`,
|
||||
getPopupContainer: () => this.getPopupDomNode(),
|
||||
onClick,
|
||||
};
|
||||
if (overlayElement && overlayElement.type === Text) {
|
||||
delete extraOverlayProps.prefixCls;
|
||||
}
|
||||
return cloneElement(overlayElement, extraOverlayProps);
|
||||
},
|
||||
|
||||
getMenuElementOrLambda() {
|
||||
const overlay = this.overlay || this.$slots.overlay;
|
||||
if (typeof overlay === 'function') {
|
||||
return this.getMenuElement;
|
||||
}
|
||||
return this.getMenuElement();
|
||||
},
|
||||
|
||||
getPopupDomNode() {
|
||||
return this.triggerRef.getPopupDomNode();
|
||||
},
|
||||
|
||||
getOpenClassName() {
|
||||
const { openClassName, prefixCls } = this.$props;
|
||||
if (openClassName !== undefined) {
|
||||
return openClassName;
|
||||
}
|
||||
return `${prefixCls}-open`;
|
||||
},
|
||||
|
||||
afterVisibleChange(visible) {
|
||||
if (visible && this.getMinOverlayWidthMatchTrigger()) {
|
||||
const overlayNode = this.getPopupDomNode();
|
||||
const rootNode = findDOMNode(this);
|
||||
if (rootNode && overlayNode && rootNode.offsetWidth > overlayNode.offsetWidth) {
|
||||
overlayNode.style.minWidth = `${rootNode.offsetWidth}px`;
|
||||
if (
|
||||
this.triggerRef &&
|
||||
this.triggerRef._component &&
|
||||
this.triggerRef._component.alignInstance
|
||||
) {
|
||||
this.triggerRef._component.alignInstance.forceAlign();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderChildren() {
|
||||
const children = getSlot(this);
|
||||
const { sVisible } = this;
|
||||
return sVisible && children
|
||||
? cloneElement(children[0], { class: this.getOpenClassName() }, false)
|
||||
: children;
|
||||
},
|
||||
saveTrigger(node) {
|
||||
this.triggerRef = node;
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls,
|
||||
transitionName,
|
||||
animation,
|
||||
align,
|
||||
placement,
|
||||
getPopupContainer,
|
||||
showAction,
|
||||
hideAction,
|
||||
overlayClassName,
|
||||
overlayStyle,
|
||||
trigger,
|
||||
...otherProps
|
||||
} = this.$props;
|
||||
let triggerHideAction = hideAction;
|
||||
if (!triggerHideAction && trigger.indexOf('contextmenu') !== -1) {
|
||||
triggerHideAction = ['click'];
|
||||
}
|
||||
const triggerProps = {
|
||||
...otherProps,
|
||||
prefixCls,
|
||||
popupClassName: overlayClassName,
|
||||
popupStyle: overlayStyle,
|
||||
builtinPlacements: placements,
|
||||
action: trigger,
|
||||
showAction,
|
||||
hideAction: triggerHideAction || [],
|
||||
popupPlacement: placement,
|
||||
popupAlign: align,
|
||||
popupTransitionName: transitionName,
|
||||
popupAnimation: animation,
|
||||
popupVisible: this.sVisible,
|
||||
afterPopupVisibleChange: this.afterVisibleChange,
|
||||
getPopupContainer,
|
||||
onPopupVisibleChange: this.onVisibleChange,
|
||||
popup: this.getMenuElementOrLambda(),
|
||||
ref: this.saveTrigger,
|
||||
};
|
||||
return <Trigger {...triggerProps}>{this.renderChildren()}</Trigger>;
|
||||
},
|
||||
});
|
|
@ -21,13 +21,11 @@ export default function Mask(props: MaskProps) {
|
|||
let motion = {};
|
||||
|
||||
if (maskTransitionName || maskAnimation) {
|
||||
motion = {
|
||||
...getMotion({
|
||||
prefixCls,
|
||||
transitionName: maskTransitionName,
|
||||
animation: maskAnimation,
|
||||
}),
|
||||
};
|
||||
motion = getMotion({
|
||||
prefixCls,
|
||||
transitionName: maskTransitionName,
|
||||
animation: maskAnimation,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -83,7 +83,7 @@ export default defineComponent({
|
|||
|
||||
// ======================== Motion ========================
|
||||
const motion = computed(() => {
|
||||
const m = { ...getMotion(props) };
|
||||
const m = getMotion(props);
|
||||
['onAfterEnter', 'onAfterLeave'].forEach(eventName => {
|
||||
m[eventName] = () => {
|
||||
goNextStatus();
|
||||
|
|
|
@ -18,6 +18,5 @@ export function getMotion({ prefixCls, animation, transitionName }: GetMotionPro
|
|||
name: transitionName,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
return {};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue