🌈 An enterprise-class UI components based on Ant Design and Vue. 🐜
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.
 
 
 
 

154 lines
4.8 KiB

import type { CSSProperties, PropType } from 'vue';
import { computed, ref, defineComponent, nextTick } from 'vue';
import type { MouseEventHandler } from '../_util/EventInterface';
import Transition, { getTransitionProps } from '../_util/transition';
import dialogPropTypes from './IDialogPropTypes';
import { offset } from './util';
const sentinelStyle = { width: 0, height: 0, overflow: 'hidden', outline: 'none' };
export type ContentRef = {
focus: () => void;
changeActive: (next: boolean) => void;
};
export default defineComponent({
name: 'Content',
inheritAttrs: false,
props: {
...dialogPropTypes(),
motionName: String,
ariaId: String,
onVisibleChanged: Function as PropType<(visible: boolean) => void>,
onMousedown: Function as PropType<MouseEventHandler>,
onMouseup: Function as PropType<MouseEventHandler>,
},
setup(props, { expose, slots, attrs }) {
const sentinelStartRef = ref<HTMLDivElement>();
const sentinelEndRef = ref<HTMLDivElement>();
const dialogRef = ref<HTMLDivElement>();
expose({
focus: () => {
sentinelStartRef.value?.focus();
},
changeActive: next => {
const { activeElement } = document;
if (next && activeElement === sentinelEndRef.value) {
sentinelStartRef.value.focus();
} else if (!next && activeElement === sentinelStartRef.value) {
sentinelEndRef.value.focus();
}
},
});
const transformOrigin = ref<string>();
const contentStyleRef = computed(() => {
const { width, height } = props;
const contentStyle: CSSProperties = {};
if (width !== undefined) {
contentStyle.width = typeof width === 'number' ? `${width}px` : width;
}
if (height !== undefined) {
contentStyle.height = typeof height === 'number' ? `${height}px` : height;
}
if (transformOrigin.value) {
contentStyle.transformOrigin = transformOrigin.value;
}
return contentStyle;
});
const onPrepare = () => {
nextTick(() => {
if (dialogRef.value) {
const elementOffset = offset(dialogRef.value);
transformOrigin.value = props.mousePosition
? `${props.mousePosition.x - elementOffset.left}px ${
props.mousePosition.y - elementOffset.top
}px`
: '';
}
});
};
const onVisibleChanged = (visible: boolean) => {
props.onVisibleChanged(visible);
};
return () => {
const {
prefixCls,
footer = slots.footer?.(),
title = slots.title?.(),
ariaId,
closable,
closeIcon = slots.closeIcon?.(),
onClose,
bodyStyle,
bodyProps,
onMousedown,
onMouseup,
visible,
modalRender = slots.modalRender,
destroyOnClose,
motionName,
} = props;
let footerNode: any;
if (footer) {
footerNode = <div class={`${prefixCls}-footer`}>{footer}</div>;
}
let headerNode: any;
if (title) {
headerNode = (
<div class={`${prefixCls}-header`}>
<div class={`${prefixCls}-title`} id={ariaId}>
{title}
</div>
</div>
);
}
let closer: any;
if (closable) {
closer = (
<button type="button" onClick={onClose} aria-label="Close" class={`${prefixCls}-close`}>
{closeIcon || <span class={`${prefixCls}-close-x`} />}
</button>
);
}
const content = (
<div class={`${prefixCls}-content`}>
{closer}
{headerNode}
<div class={`${prefixCls}-body`} style={bodyStyle} {...bodyProps}>
{slots.default?.()}
</div>
{footerNode}
</div>
);
const transitionProps = getTransitionProps(motionName);
return (
<Transition
{...transitionProps}
onBeforeEnter={onPrepare}
onAfterEnter={() => onVisibleChanged(true)}
onAfterLeave={() => onVisibleChanged(false)}
>
{visible || !destroyOnClose ? (
<div
{...attrs}
ref={dialogRef}
v-show={visible}
key="dialog-element"
role="document"
style={[contentStyleRef.value, attrs.style]}
class={[prefixCls, attrs.class]}
onMousedown={onMousedown}
onMouseup={onMouseup}
>
<div tabindex={0} ref={sentinelStartRef} style={sentinelStyle} aria-hidden="true" />
{modalRender ? modalRender({ originVNode: content }) : content}
<div tabindex={0} ref={sentinelEndRef} style={sentinelStyle} aria-hidden="true" />
</div>
) : null}
</Transition>
);
};
},
});