rabbit-design
Version:
A lightweight UI plugin library written in TypeScript and based on JavaScript
370 lines (318 loc) • 13.1 kB
text/typescript
import PREFIX from '../prefix';
import {
$el,
bind,
createElem,
getBooleanTypeAttr,
getNumTypeAttr,
getStrTypeAttr,
removeAttrs,
setCss,
setHtml
} from '../../dom-utils';
import { _newCreatePopper } from '../../mixins/tooltip';
import { CssTransition, clickOutside, _arrow, warn, _Popper } from '../../mixins';
import { type, validComps } from '../../utils';
interface PoptipAttrs {
width: number;
offset: number;
title: string;
okText: string;
content: string;
trigger: string;
padding: string;
placement: string;
cancelText: string;
isDisabled: boolean;
isWordWrap: boolean;
isConfirm: boolean;
}
interface PoptipEvents {
onShow?: () => void; // 提示框显示时触发
onHide?: () => void; // 提示框消失时触发
onOk?: () => void; // 点击确定的回调
onCancel?: () => void; // 点击取消的回调
}
interface Config {
config(
el: string
): {
title: string | number; // 显示的标题
content: string | number; // 显示的正文内容,只在非 confirm 模式下有效
events: (options: PoptipEvents) => void;
};
}
const DEFAULTDELAY = 100;
let SHOWTIMER: any, EVENTTIMER: any;
class Poptip implements Config {
readonly VERSION: string;
private COMPONENTS: NodeListOf<Element>;
private children: NodeListOf<HTMLElement>;
constructor() {
this.VERSION = 'v1.0';
this.COMPONENTS = $el('r-poptip', { all: true });
this._create(this.COMPONENTS);
this.children = $el(`.${PREFIX.poptip}-popper`, { all: true });
clickOutside(this.children, 'poptipShow', 'zoom-big-fast-leave');
_arrow.scrollUpdate();
}
public config(
el: string
): {
title: string | number;
content: string | number;
events: ({ onShow, onHide, onOk, onCancel }: PoptipEvents) => void;
} {
const target = $el(el);
validComps(target, 'poptip');
const { attrs } = Poptip.prototype;
const PoptipRef = target.querySelector(`.${PREFIX.poptip}-rel`);
const PoptipPopper = target.querySelector(`.${PREFIX.poptip}-popper`);
const PoptipContent = target.querySelector(`.${PREFIX.poptip}-body-content-inner`);
let PoptipTitle: any;
let OkBtn: any;
let CancelBtn: any;
// 判断要设置的提示框标题是否是确认对话框的标题
// 判断是否要获取确认对话框的确定和取消按钮
if (attrs(target).isConfirm) {
PoptipTitle = target.querySelector(`.${PREFIX.poptip}-body-message`);
OkBtn = target.querySelector(`.${PREFIX.button}-primary.${PREFIX.button}-sm`);
CancelBtn = target.querySelector(`.${PREFIX.button}-text.${PREFIX.button}-sm`);
} else {
PoptipTitle = target.querySelector(`.${PREFIX.poptip}-title-inner`);
}
return {
get title() {
return setHtml(PoptipTitle);
},
set title(newVal) {
if (type.isStr(newVal) || type.isNum(newVal)) setHtml(PoptipTitle, newVal);
},
get content() {
return setHtml(PoptipContent);
},
set content(newVal) {
if (type.isStr(newVal) || type.isNum(newVal)) setHtml(PoptipContent, newVal);
},
events({ onShow, onHide, onOk, onCancel }) {
const triggerMode = attrs(target).trigger;
const showEv = () => {
if (PoptipPopper.dataset.poptipShow === 'true') onShow && type.isFn(onShow);
};
const hideEv = () => {
if (PoptipPopper.dataset.poptipShow === 'false') onHide && type.isFn(onHide);
};
const clickEv = () => {
showEv();
hideEv();
};
if (triggerMode === 'click') {
bind(PoptipRef, 'click', clickEv);
} else if (triggerMode === 'focus') {
bind(target, 'mousedown', showEv);
bind(target, 'mouseup', hideEv);
} else if (triggerMode === 'hover') {
_Popper.handleHoverShowAndHideEvents({
reference: target,
popper: PoptipPopper,
datasetVal: 'poptipStatus',
showCb: onShow,
hideCb: onHide,
delay: DEFAULTDELAY,
timer: EVENTTIMER
});
}
// 确认对话框的确定和取消按钮都要触发提示框隐藏
if (OkBtn) {
bind(OkBtn, 'click', () => {
hideEv();
onOk && type.isFn(onOk);
});
}
if (CancelBtn) {
bind(OkBtn, 'click', () => {
hideEv();
onCancel && type.isFn(onCancel);
});
}
}
};
}
private _create(COMPONENTS: NodeListOf<Element>): void {
COMPONENTS.forEach((node, i) => {
this._createPoptipNodes(node, i);
removeAttrs(node, [
'width',
'title',
'content',
'ok-text',
'padding',
'disabled',
'placement',
'word-wrap',
'cancel-text'
]);
});
}
private _createPoptipNodes(node: Element, i: number): void {
const attrs = this.attrs(node);
if (attrs.isConfirm) node.className = `${PREFIX.poptip}-confirm`;
const uid = `poptip${i}`;
const referenceElem = node.firstElementChild!;
const PoptipRel = createElem('div');
PoptipRel.className = `${PREFIX.poptip}-rel`;
PoptipRel.appendChild(referenceElem);
const whatModel = attrs.isConfirm ? this._confirmTpl(attrs) : this._normalTpl(attrs);
const template = `
<div class="${PREFIX.poptip}-popper" x-placement=${attrs.placement} data-poptip-uid=${uid}>
<div class="${PREFIX.poptip}-content">
<div class="${PREFIX.poptip}-arrow" data-popper-arrow></div>
<div class="${PREFIX.poptip}-inner">${whatModel}</div>
</div>
</div>
`;
setHtml(node, template);
this._setWidth(attrs, uid);
const Popper = $el(`[data-poptip-uid=${uid}]`)!;
Popper?.before(PoptipRel);
// 初始化 display
setCss(Popper, 'display', 'none');
if (!attrs.isDisabled) {
// @ts-ignore
this._triggerDisplay(attrs.trigger, node, PoptipRel, Popper, attrs);
}
}
private _normalTpl(attrs: PoptipAttrs): string {
const setPadding = attrs.padding ? `padding:${attrs.padding}` : '';
const isShowTitle =
!attrs.isWordWrap && attrs.title
? `<div class="${PREFIX.poptip}-title" style="${setPadding}">
<div class="${PREFIX.poptip}-title-inner">${attrs.title}</div>
</div>`
: '';
const template = `
${isShowTitle}
<div class="${PREFIX.poptip}-body" style="${setPadding}">
<div class="${PREFIX.poptip}-body-content">
<div class="${PREFIX.poptip}-body-content-inner">${attrs.content}</div>
</div>
</div>
`;
return template;
}
private _confirmTpl(attrs: PoptipAttrs): string {
const template = `
<div class="${PREFIX.poptip}-body">
<i class="${PREFIX.icon} ${PREFIX.icon}-ios-help-circle"></i>
<div class="${PREFIX.poptip}-body-message">${attrs.title}</div>
</div>
<div class="${PREFIX.poptip}-footer">
<button class="${PREFIX.button} ${PREFIX.button}-text ${PREFIX.button}-sm">${attrs.cancelText}</button>
<button class="${PREFIX.button} ${PREFIX.button}-primary ${PREFIX.button}-sm">${attrs.okText}</button>
</div>
`;
return template;
}
private _setWidth(attrs: PoptipAttrs, uid: string): void {
const popper = document.querySelector(`[data-poptip-uid=${uid}]`);
if (attrs.width) {
setCss(popper, 'width', `${attrs.width}px`);
}
if (attrs.isWordWrap) {
const popperContent = popper?.querySelector(`.${PREFIX.poptip}-body-content`);
popperContent?.classList.add(`${PREFIX.poptip}-body-content-word-wrap`);
}
}
private _triggerDisplay(
trigger: string,
parent: HTMLElement,
referenceChild: HTMLElement,
popper: Element | any,
poptipAttrs: PoptipAttrs
): void {
if (trigger !== 'click' && trigger !== 'hover' && trigger !== 'focus') {
warn(`The Poptip attribute trigger got an invalid trigger mode --> '${trigger}'`);
return;
}
const { _initPoptip } = this;
const common = {
rmCls: true,
enterCls: 'zoom-big-fast-enter',
leaveCls: 'zoom-big-fast-leave',
timeout: 200
};
// 通过设置 popper.dataset.poptipShow 来判断是否隐藏或显示
const show = () => {
popper.dataset.poptipShow = 'true';
CssTransition(popper, {
inOrOut: 'in',
...common
});
_initPoptip(parent, popper, poptipAttrs);
};
const hide = () => {
popper.dataset.poptipShow = 'false';
CssTransition(popper, {
inOrOut: 'out',
...common
});
};
const judgmentIsVisible = () => (popper.dataset.poptipShow === 'true' ? hide() : show());
if (trigger === 'click' || trigger === 'focus') {
_initPoptip(parent, popper, poptipAttrs);
_arrow.toggleUpdate(popper, trigger, parent);
}
if (trigger === 'click') {
bind(referenceChild, 'click', judgmentIsVisible);
} else if (trigger === 'focus' && !poptipAttrs.isConfirm) {
bind(referenceChild, 'mousedown', judgmentIsVisible);
bind(referenceChild, 'mouseup', hide);
} else if (trigger === 'hover' && !poptipAttrs.isConfirm) {
bind(parent, 'mouseenter', () => {
SHOWTIMER = setTimeout(() => {
show();
}, DEFAULTDELAY);
});
bind(parent, 'mouseleave', () => {
clearTimeout(SHOWTIMER);
hide();
});
_arrow.toggleUpdate(popper, 'hover', parent, DEFAULTDELAY);
}
// 确认对话框的确定和取消按钮触发隐藏
if (poptipAttrs.isConfirm) {
const confirmOkBtn = popper.querySelector(
`.${PREFIX.button}-primary.${PREFIX.button}-sm`
);
const confirmCancelBtn = popper.querySelector(
`.${PREFIX.button}-text.${PREFIX.button}-sm`
);
confirmOkBtn.addEventListener('click', judgmentIsVisible);
confirmCancelBtn.addEventListener('click', judgmentIsVisible);
}
}
private _initPoptip(reference: Element, popper: Element | any, poptipAttrs: PoptipAttrs): any {
const NCP = _newCreatePopper(reference, popper, poptipAttrs.placement, poptipAttrs.offset);
return NCP;
}
private attrs(node: Element): PoptipAttrs {
return {
// number type
width: getNumTypeAttr(node, 'width', 0),
offset: getNumTypeAttr(node, 'offset', 0),
// string type
title: getStrTypeAttr(node, 'title', ''),
okText: getStrTypeAttr(node, 'ok-text', '确定'),
content: getStrTypeAttr(node, 'content', ''),
trigger: getStrTypeAttr(node, 'trigger', 'click'),
padding: getStrTypeAttr(node, 'padding', ''),
placement: getStrTypeAttr(node, 'placement', 'top'),
cancelText: getStrTypeAttr(node, 'cancel-text', '取消'),
// boolean type
isConfirm: getBooleanTypeAttr(node, 'confirm'),
isDisabled: getBooleanTypeAttr(node, 'disabled'),
isWordWrap: getBooleanTypeAttr(node, 'word-wrap')
};
}
}
export default Poptip;