UNPKG

naive-ui

Version:

A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast

265 lines 9.34 kB
import { Transition, computed, defineComponent, h, inject, mergeProps, onBeforeUnmount, provide, ref, vShow, watch, watchEffect, withDirectives } from 'vue'; import { VFocusTrap } from 'vueuc'; import { clickoutside } from 'vdirs'; import { useConfig, useRtl } from "../../_mixins/index.mjs"; import { popoverBodyInjectionKey } from "../../popover/src/interface.mjs"; import { modalBodyInjectionKey } from "../../modal/src/interface.mjs"; import { NScrollbar } from "../../_internal/index.mjs"; import { useLockHtmlScroll } from "../../_utils/index.mjs"; import { drawerBodyInjectionKey, drawerInjectionKey } from "./interface.mjs"; export default defineComponent({ name: 'NDrawerContent', inheritAttrs: false, props: { blockScroll: Boolean, show: { type: Boolean, default: undefined }, displayDirective: { type: String, required: true }, placement: { type: String, required: true }, contentClass: String, contentStyle: [Object, String], nativeScrollbar: { type: Boolean, required: true }, scrollbarProps: Object, trapFocus: { type: Boolean, default: true }, autoFocus: { type: Boolean, default: true }, showMask: { type: [Boolean, String], required: true }, maxWidth: Number, maxHeight: Number, minWidth: Number, minHeight: Number, resizable: Boolean, onClickoutside: Function, onAfterLeave: Function, onAfterEnter: Function, onEsc: Function }, setup(props) { const displayedRef = ref(!!props.show); const bodyRef = ref(null); // used for detached content const NDrawer = inject(drawerInjectionKey); let startPosition = 0; let memoizedBodyStyleCursor = ''; let hoverTimerId = null; const isHoverOnResizeTriggerRef = ref(false); const isDraggingRef = ref(false); const isVertical = computed(() => { return props.placement === 'top' || props.placement === 'bottom'; }); const { mergedClsPrefixRef, mergedRtlRef } = useConfig(props); const rtlEnabledRef = useRtl('Drawer', mergedRtlRef, mergedClsPrefixRef); const handleBodyMouseleave = handleBodyMouseup; const handleMousedownResizeTrigger = e => { isDraggingRef.value = true; startPosition = isVertical.value ? e.clientY : e.clientX; memoizedBodyStyleCursor = document.body.style.cursor; document.body.style.cursor = isVertical.value ? 'ns-resize' : 'ew-resize'; document.body.addEventListener('mousemove', handleBodyMousemove); document.body.addEventListener('mouseleave', handleBodyMouseleave); document.body.addEventListener('mouseup', handleBodyMouseup); }; const handleMouseenterResizeTrigger = () => { if (hoverTimerId !== null) { window.clearTimeout(hoverTimerId); hoverTimerId = null; } if (isDraggingRef.value) { isHoverOnResizeTriggerRef.value = true; } else { hoverTimerId = window.setTimeout(() => { isHoverOnResizeTriggerRef.value = true; }, 300); } }; const handleMouseleaveResizeTrigger = () => { if (hoverTimerId !== null) { window.clearTimeout(hoverTimerId); hoverTimerId = null; } isHoverOnResizeTriggerRef.value = false; }; const { doUpdateHeight, doUpdateWidth } = NDrawer; const regulateWidth = size => { const { maxWidth } = props; if (maxWidth && size > maxWidth) return maxWidth; const { minWidth } = props; if (minWidth && size < minWidth) return minWidth; return size; }; const regulateHeight = size => { const { maxHeight } = props; if (maxHeight && size > maxHeight) return maxHeight; const { minHeight } = props; if (minHeight && size < minHeight) return minHeight; return size; }; function handleBodyMousemove(e) { var _a, _b; if (isDraggingRef.value) { if (isVertical.value) { let height = ((_a = bodyRef.value) === null || _a === void 0 ? void 0 : _a.offsetHeight) || 0; const increment = startPosition - e.clientY; height += props.placement === 'bottom' ? increment : -increment; height = regulateHeight(height); doUpdateHeight(height); startPosition = e.clientY; } else { let width = ((_b = bodyRef.value) === null || _b === void 0 ? void 0 : _b.offsetWidth) || 0; const increment = startPosition - e.clientX; width += props.placement === 'right' ? increment : -increment; width = regulateWidth(width); doUpdateWidth(width); startPosition = e.clientX; } } } function handleBodyMouseup() { if (isDraggingRef.value) { startPosition = 0; isDraggingRef.value = false; document.body.style.cursor = memoizedBodyStyleCursor; document.body.removeEventListener('mousemove', handleBodyMousemove); document.body.removeEventListener('mouseup', handleBodyMouseup); document.body.removeEventListener('mouseleave', handleBodyMouseleave); } } watchEffect(() => { if (props.show) displayedRef.value = true; }); watch(() => props.show, value => { if (!value) { handleBodyMouseup(); } }); onBeforeUnmount(() => { handleBodyMouseup(); }); const bodyDirectivesRef = computed(() => { const { show } = props; const directives = [[vShow, show]]; if (!props.showMask) { directives.push([clickoutside, props.onClickoutside, undefined, { capture: true }]); } return directives; }); function handleAfterLeave() { var _a; displayedRef.value = false; (_a = props.onAfterLeave) === null || _a === void 0 ? void 0 : _a.call(props); } useLockHtmlScroll(computed(() => props.blockScroll && displayedRef.value)); provide(drawerBodyInjectionKey, bodyRef); provide(popoverBodyInjectionKey, null); provide(modalBodyInjectionKey, null); return { bodyRef, rtlEnabled: rtlEnabledRef, mergedClsPrefix: NDrawer.mergedClsPrefixRef, isMounted: NDrawer.isMountedRef, mergedTheme: NDrawer.mergedThemeRef, displayed: displayedRef, transitionName: computed(() => { return { right: 'slide-in-from-right-transition', left: 'slide-in-from-left-transition', top: 'slide-in-from-top-transition', bottom: 'slide-in-from-bottom-transition' }[props.placement]; }), handleAfterLeave, bodyDirectives: bodyDirectivesRef, handleMousedownResizeTrigger, handleMouseenterResizeTrigger, handleMouseleaveResizeTrigger, isDragging: isDraggingRef, isHoverOnResizeTrigger: isHoverOnResizeTriggerRef }; }, render() { const { $slots, mergedClsPrefix } = this; return this.displayDirective === 'show' || this.displayed || this.show ? withDirectives( /* Keep the wrapper dom. Make sure the drawer has a host. Nor the detached content will disappear without transition */ h("div", { role: "none" }, h(VFocusTrap, { disabled: !this.showMask || !this.trapFocus, active: this.show, autoFocus: this.autoFocus, onEsc: this.onEsc }, { default: () => h(Transition, { name: this.transitionName, appear: this.isMounted, onAfterEnter: this.onAfterEnter, onAfterLeave: this.handleAfterLeave }, { default: () => withDirectives(h('div', mergeProps(this.$attrs, { role: 'dialog', ref: 'bodyRef', 'aria-modal': 'true', class: [`${mergedClsPrefix}-drawer`, this.rtlEnabled && `${mergedClsPrefix}-drawer--rtl`, `${mergedClsPrefix}-drawer--${this.placement}-placement`, /** * When the mouse is pressed to resize the drawer, * disable text selection */ this.isDragging && `${mergedClsPrefix}-drawer--unselectable`, this.nativeScrollbar && `${mergedClsPrefix}-drawer--native-scrollbar`] }), [this.resizable ? h("div", { class: [`${mergedClsPrefix}-drawer__resize-trigger`, (this.isDragging || this.isHoverOnResizeTrigger) && `${mergedClsPrefix}-drawer__resize-trigger--hover`], onMouseenter: this.handleMouseenterResizeTrigger, onMouseleave: this.handleMouseleaveResizeTrigger, onMousedown: this.handleMousedownResizeTrigger }) : null, this.nativeScrollbar ? h("div", { class: [`${mergedClsPrefix}-drawer-content-wrapper`, this.contentClass], style: this.contentStyle, role: "none" }, $slots) : h(NScrollbar, Object.assign({}, this.scrollbarProps, { contentStyle: this.contentStyle, contentClass: [`${mergedClsPrefix}-drawer-content-wrapper`, this.contentClass], theme: this.mergedTheme.peers.Scrollbar, themeOverrides: this.mergedTheme.peerOverrides.Scrollbar }), $slots)]), this.bodyDirectives) }) })), [[vShow, this.displayDirective === 'if' || this.displayed || this.show]]) : null; } });