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.
ant-design-vue/components/float-button/FloatButtonGroup.tsx

138 lines
4.4 KiB

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;