ivue-material-plus
Version:
A high quality UI components Library with Vue.js
130 lines (115 loc) • 3.51 kB
text/typescript
import { isRef, ref } from 'vue';
import { hyphenate, isObject, isString } from '@vue/shared';
import Loading from './loading';
// ts
import { LoadingInstance } from './createLoadingComponent';
import { LoadingOptions } from './types/loading';
import type { Directive, DirectiveBinding, UnwrapRef } from 'vue';
const INSTANCE_KEY = Symbol('IvueLoading');
export type LoadingBinding = boolean | UnwrapRef<LoadingOptions>;
export interface ElementLoading extends HTMLElement {
INSTANCE_KEY: {
instance: LoadingInstance;
options: LoadingOptions;
};
}
const createInstance = (
el: ElementLoading,
binding: DirectiveBinding<LoadingBinding>
) => {
// 当前实例
const vm = binding.instance;
// 获取绑定的props
const getBindingProp = <K extends keyof LoadingOptions>(
key: K
): LoadingOptions[K] =>
isObject(binding.value) ? binding.value[key] : undefined;
const resolveExpression = (key: any) => {
const data = (isString(key) && vm?.[key]) || key;
if (data) return ref(data);
else return data;
};
// 获取prop
const getProp = <K extends keyof LoadingOptions>(name: K) =>
resolveExpression(
getBindingProp(name) || el.getAttribute(`ivue-loading-${hyphenate(name)}`)
);
// 开启全屏
const fullscreen =
getBindingProp('fullscreen') ?? binding.modifiers.fullscreen;
// 设置参数
const options: LoadingOptions = {
// 显示在加载图标下方的加载文案
text: getProp('text'),
// 自定义加载图标类名
iconClass: getProp('iconClass'),
// 图标 slot
iconText: getProp('iconText'),
// 遮罩背景色
background: getProp('background'),
// Loading 的自定义类名
customClass: getProp('customClass'),
// 开启全屏
fullscreen,
// 需要覆盖的 DOM 节点
target: getBindingProp('target') ?? (fullscreen ? undefined : el),
// 使遮罩插入至 DOM 中的 body 上
body: getBindingProp('body') ?? binding.modifiers.body,
// 锁定屏幕的滚动
lock: getBindingProp('lock') ?? binding.modifiers.lock,
};
// 重置实例
el[INSTANCE_KEY] = {
options,
instance: Loading(options),
};
};
// 更新选项
const updateOptions = (
// 新的选项
newOptions: UnwrapRef<LoadingOptions>,
// 原始选项
originalOptions: LoadingOptions
) => {
for (const key of Object.keys(originalOptions)) {
// 替换选项是否是 isRef
if (isRef(originalOptions[key])) {
originalOptions[key].value = newOptions[key];
}
}
};
const vLoading: Directive<ElementLoading, LoadingBinding> = {
// mounted
mounted(el, binding) {
if (binding.value) {
createInstance(el, binding);
}
},
// 更新
updated(el, binding) {
const instance = el[INSTANCE_KEY];
// 是否需要更新
if (binding.oldValue !== binding.value) {
// 初始化实例
if (binding.value && !binding.oldValue) {
createInstance(el, binding);
}
// 更新选项
else if (binding.value && binding.oldValue) {
if (isObject(binding.value)) {
updateOptions(binding.value, instance.options);
}
}
// 清除是实例
else {
instance?.instance.close();
}
}
},
// 销毁
unmounted(el) {
// 关闭实例
el[INSTANCE_KEY]?.instance.close();
},
};
export default vLoading;