element-plus
Version:
> TODO: description
261 lines (260 loc) • 9.95 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePopperHook = exports.usePopperProps = exports.usePopperControlProps = exports.LIGHT_EFFECT = exports.DARK_EFFECT = void 0;
const vue_1 = require("vue");
const shared_1 = require("@vue/shared");
const core_1 = require("@popperjs/core");
const directives_1 = require("../../directives");
const util_1 = require("../../utils/util");
const vnode_1 = require("../../utils/vnode");
const dom_1 = require("../../utils/dom");
const popup_manager_1 = __importDefault(require("../../utils/popup-manager"));
const error_1 = __importDefault(require("../../utils/error"));
const use_teleport_1 = __importDefault(require("../use-teleport"));
const use_timeout_1 = __importDefault(require("../use-timeout"));
const use_model_toggle_1 = require("../use-model-toggle");
const use_transition_fallthrough_1 = require("../use-transition-fallthrough");
const use_popper_options_1 = require("./use-popper-options");
const use_target_events_1 = require("./use-target-events");
exports.DARK_EFFECT = 'dark';
exports.LIGHT_EFFECT = 'light';
exports.usePopperControlProps = {
appendToBody: {
type: Boolean,
default: true,
},
arrowOffset: {
type: Number,
},
popperOptions: use_popper_options_1.defaultPopperOptions,
popperClass: {
type: String,
default: '',
},
};
exports.usePopperProps = Object.assign(Object.assign({}, exports.usePopperControlProps), {
// the arrow size is an equailateral triangle with 10px side length, the 3rd side length ~ 14.1px
// adding a offset to the ceil of 4.1 should be 5 this resolves the problem of arrow overflowing out of popper.
autoClose: {
type: Number,
default: 0,
}, content: {
type: String,
default: '',
}, class: String, style: Object, hideAfter: {
type: Number,
default: 200,
}, disabled: {
type: Boolean,
default: false,
}, effect: {
type: String,
default: exports.DARK_EFFECT,
}, enterable: {
type: Boolean,
default: true,
}, manualMode: {
type: Boolean,
default: false,
}, showAfter: {
type: Number,
default: 0,
}, pure: {
type: Boolean,
default: false,
}, showArrow: {
type: Boolean,
default: true,
}, transition: {
type: String,
default: 'el-fade-in-linear',
}, trigger: {
type: [String, Array],
default: use_target_events_1.DEFAULT_TRIGGER,
}, visible: {
type: Boolean,
default: undefined,
}, stopPopperMouseEvent: {
type: Boolean,
default: true,
} });
const usePopperHook = () => {
const vm = vue_1.getCurrentInstance();
const props = vm.props;
const { slots } = vm;
const arrowRef = vue_1.ref(null);
const triggerRef = vue_1.ref(null);
const popperRef = vue_1.ref(null);
const popperStyle = vue_1.ref({ zIndex: popup_manager_1.default.nextZIndex() });
const visible = vue_1.ref(false);
const isManual = vue_1.computed(() => props.manualMode || props.trigger === 'manual');
const popperId = `el-popper-${util_1.generateId()}`;
let popperInstance = null;
const { renderTeleport, showTeleport, hideTeleport, } = use_teleport_1.default(popupRenderer, vue_1.toRef(props, 'appendToBody'));
const { show, hide } = use_model_toggle_1.useModelToggle({
indicator: visible,
onShow,
onHide,
});
const { registerTimeout, cancelTimeout } = use_timeout_1.default();
// event handlers
function onShow() {
popperStyle.value.zIndex = popup_manager_1.default.nextZIndex();
vue_1.nextTick(initializePopper);
}
function onHide() {
hideTeleport();
vue_1.nextTick(detachPopper);
}
/**
* The calling mechanism here is:
* when the visibility gets changed, let's say we change it to true
* the delayShow gets called which initializes a global root node for the popper content
* to insert in, then it register a timer for calling the show method, which changes the flag to
* true, then calls onShow method.
* So the order of invocation is: delayedShow -> timer(show) -> set the indicator to true -> onShow
*/
function delayShow() {
if (isManual.value || props.disabled)
return;
// renders out the teleport element root.
showTeleport();
registerTimeout(show, props.showAfter);
}
function delayHide() {
if (isManual.value)
return;
registerTimeout(hide, props.hideAfter);
}
function onToggle() {
if (visible.value) {
delayShow();
}
else {
delayHide();
}
}
function detachPopper() {
var _a;
(_a = popperInstance === null || popperInstance === void 0 ? void 0 : popperInstance.destroy) === null || _a === void 0 ? void 0 : _a.call(popperInstance);
popperInstance = null;
}
function onPopperMouseEnter() {
// if trigger is click, user won't be able to close popper when
// user tries to move the mouse over popper contents
if (props.enterable && props.trigger !== 'click') {
cancelTimeout();
}
}
function onPopperMouseLeave() {
const { trigger } = props;
const shouldPrevent = (util_1.isString(trigger) && (trigger === 'click' || trigger === 'focus')) ||
// we'd like to test array type trigger here, but the only case we need to cover is trigger === 'click' or
// trigger === 'focus', because that when trigger is string
// trigger.length === 1 and trigger[0] === 5 chars string is mutually exclusive.
// so there will be no need to test if trigger is array type.
(trigger.length === 1 &&
(trigger[0] === 'click' || trigger[0] === 'focus'));
if (shouldPrevent)
return;
delayHide();
}
function initializePopper() {
if (!visible.value || popperInstance !== null) {
return;
}
const unwrappedTrigger = triggerRef.value;
const $el = util_1.isHTMLElement(unwrappedTrigger)
? unwrappedTrigger
: unwrappedTrigger.$el;
popperInstance = core_1.createPopper($el, popperRef.value, buildPopperOptions());
popperInstance.update();
}
function buildPopperOptions() {
const modifiers = [
...use_popper_options_1.defaultModifiers,
...props.popperOptions.modifiers,
];
if (props.showArrow) {
modifiers.push({
name: 'arrow',
options: {
padding: props.arrowOffset || 5,
element: arrowRef.value,
},
});
}
return Object.assign(Object.assign({}, props.popperOptions), { modifiers });
}
const { onAfterEnter, onAfterLeave, onBeforeEnter, onBeforeLeave, } = use_transition_fallthrough_1.useTransitionFallthrough();
const events = use_target_events_1.useTargetEvents(delayShow, delayHide, onToggle);
const arrowRefAttacher = util_1.refAttacher(arrowRef);
const popperRefAttacher = util_1.refAttacher(popperRef);
const triggerRefAttacher = util_1.refAttacher(triggerRef);
// renderers
function popupRenderer() {
const mouseUpAndDown = props.stopPopperMouseEvent ? dom_1.stop : shared_1.NOOP;
return vue_1.h(vue_1.Transition, {
name: props.transition,
onAfterEnter,
onAfterLeave,
onBeforeEnter,
onBeforeLeave,
}, {
default: () => () => visible.value ? vue_1.h('div', {
'aria-hidden': false,
class: [
props.popperClass,
'el-popper',
`is-${props.effect}`,
props.pure ? 'is-pure' : '',
],
style: popperStyle.value,
id: popperId,
ref: popperRefAttacher,
role: 'tooltip',
onMouseenter: onPopperMouseEnter,
onMouseleave: onPopperMouseLeave,
onClick: dom_1.stop,
onMousedown: mouseUpAndDown,
onMouseup: mouseUpAndDown,
}, [
vue_1.renderSlot(slots, 'default', {}, () => [vue_1.toDisplayString(props.content)]),
arrowRenderer(),
]) : null,
});
}
function arrowRenderer() {
return props.showArrow
? vue_1.h('div', {
ref: arrowRefAttacher,
class: 'el-popper__arrow',
'data-popper-arrow': '',
}, null)
: null;
}
function triggerRenderer(triggerProps) {
var _a;
const trigger = (_a = slots.trigger) === null || _a === void 0 ? void 0 : _a.call(slots);
const firstElement = vnode_1.getFirstValidNode(trigger, 1);
if (!firstElement)
error_1.default('renderTrigger', 'trigger expects single rooted node');
return vue_1.cloneVNode(firstElement, triggerProps, true);
}
function render() {
const trigger = triggerRenderer(Object.assign({ 'aria-describedby': popperId, class: props.class, style: props.style, ref: triggerRefAttacher }, events));
return vue_1.h(vue_1.Fragment, null, [
isManual.value
? trigger
: vue_1.withDirectives(trigger, [[directives_1.ClickOutside, delayHide]]),
renderTeleport(),
]);
}
return {
render,
};
};
exports.usePopperHook = usePopperHook;