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.
138 lines
4.4 KiB
138 lines
4.4 KiB
1 year ago
|
import { defineComponent, ref, computed, watch, onBeforeUnmount } from 'vue';
|
||
|
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
|
||
|
import FileTextOutlined from '@ant-design/icons-vue/FileTextOutlined';
|
||
|
import classNames from '../_util/classNames';
|
||
|
import { getTransitionProps, Transition } from '../_util/transition';
|
||
|
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
|
||
|
import useConfigInject from '../config-provider/hooks/useConfigInject';
|
||
|
import { useProvideFloatButtonGroupContext } from './context';
|
||
|
import { findDOMNode, initDefaultProps } from '../_util/props-util';
|
||
|
import { floatButtonGroupProps } from './interface';
|
||
|
import type { FloatButtonGroupProps } from './interface';
|
||
|
|
||
|
// CSSINJS
|
||
|
import useStyle from './style';
|
||
|
import useMergedState from '../_util/hooks/useMergedState';
|
||
|
|
||
|
const FloatButtonGroup = defineComponent({
|
||
|
compatConfig: { MODE: 3 },
|
||
|
name: 'AFloatButtonGroup',
|
||
|
inheritAttrs: false,
|
||
|
props: initDefaultProps(floatButtonGroupProps(), {
|
||
|
type: 'default',
|
||
|
shape: 'circle',
|
||
|
} as FloatButtonGroupProps),
|
||
|
setup(props, { attrs, slots, emit }) {
|
||
|
const { prefixCls, direction } = useConfigInject(floatButtonPrefixCls, props);
|
||
|
|
||
|
// style
|
||
|
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||
|
|
||
|
const [open, setOpen] = useMergedState(false, { value: computed(() => props.open) });
|
||
|
|
||
|
const floatButtonGroupRef = ref<HTMLDivElement>(null);
|
||
|
const floatButtonRef = ref<HTMLButtonElement | HTMLAnchorElement>(null);
|
||
|
|
||
|
useProvideFloatButtonGroupContext({
|
||
|
shape: computed(() => props.shape),
|
||
|
});
|
||
|
const hoverTypeAction = {
|
||
|
onMouseenter() {
|
||
|
setOpen(true);
|
||
|
emit('update:open', true);
|
||
|
props.onOpenChange?.(true);
|
||
|
},
|
||
|
onMouseleave() {
|
||
|
setOpen(false);
|
||
|
emit('update:open', false);
|
||
|
props.onOpenChange?.(false);
|
||
|
},
|
||
|
};
|
||
|
const hoverAction = computed(() => {
|
||
|
return props.trigger === 'hover' ? hoverTypeAction : {};
|
||
|
});
|
||
|
|
||
|
const handleOpenChange = () => {
|
||
|
const nextOpen = !open.value;
|
||
|
emit('update:open', nextOpen);
|
||
|
props.onOpenChange?.(nextOpen);
|
||
|
setOpen(nextOpen);
|
||
|
};
|
||
|
|
||
|
const onClick = (e: MouseEvent) => {
|
||
|
if (floatButtonGroupRef.value?.contains(e.target as Node)) {
|
||
|
if (findDOMNode(floatButtonRef.value)?.contains(e.target as Node)) {
|
||
|
handleOpenChange();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
setOpen(false);
|
||
|
emit('update:open', false);
|
||
|
props.onOpenChange?.(false);
|
||
|
};
|
||
|
|
||
|
watch(
|
||
|
computed(() => props.trigger),
|
||
|
value => {
|
||
|
document.removeEventListener('click', onClick);
|
||
|
if (value === 'click') {
|
||
|
document.addEventListener('click', onClick);
|
||
|
}
|
||
|
},
|
||
|
{ immediate: true },
|
||
|
);
|
||
|
onBeforeUnmount(() => {
|
||
|
document.removeEventListener('click', onClick);
|
||
|
});
|
||
|
|
||
|
return () => {
|
||
|
const { shape = 'circle', type = 'default', tooltip, description, trigger } = props;
|
||
|
|
||
|
const groupPrefixCls = `${prefixCls.value}-group`;
|
||
|
|
||
|
const groupCls = classNames(groupPrefixCls, hashId.value, attrs.class, {
|
||
|
[`${groupPrefixCls}-rtl`]: direction.value === 'rtl',
|
||
|
[`${groupPrefixCls}-${shape}`]: shape,
|
||
|
[`${groupPrefixCls}-${shape}-shadow`]: !trigger,
|
||
|
});
|
||
|
|
||
|
const wrapperCls = classNames(hashId.value, `${groupPrefixCls}-wrap`);
|
||
|
|
||
|
const transitionProps = getTransitionProps(`${groupPrefixCls}-wrap`);
|
||
|
|
||
|
return wrapSSR(
|
||
|
<div ref={floatButtonGroupRef} {...attrs} class={groupCls} {...hoverAction.value}>
|
||
|
{trigger && ['click', 'hover'].includes(trigger) ? (
|
||
|
<>
|
||
|
<Transition {...transitionProps}>
|
||
|
<div v-show={open.value} class={wrapperCls}>
|
||
|
{slots.default && slots.default()}
|
||
|
</div>
|
||
|
</Transition>
|
||
|
<FloatButton
|
||
|
ref={floatButtonRef}
|
||
|
type={type}
|
||
|
shape={shape}
|
||
|
tooltip={tooltip}
|
||
|
description={description}
|
||
|
v-slots={{
|
||
|
icon: () =>
|
||
|
open.value
|
||
|
? slots.closeIcon?.() || <CloseOutlined />
|
||
|
: slots.icon?.() || <FileTextOutlined />,
|
||
|
tooltip: slots.tooltip,
|
||
|
description: slots.description,
|
||
|
}}
|
||
|
></FloatButton>
|
||
|
</>
|
||
|
) : (
|
||
|
slots.default?.()
|
||
|
)}
|
||
|
</div>,
|
||
|
);
|
||
|
};
|
||
|
},
|
||
|
});
|
||
|
|
||
|
export default FloatButtonGroup;
|