dev
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import { h, render } from "vue";
|
||||
import type { App, Directive, DirectiveBinding } from "vue";
|
||||
import LoadingOverlay from "@/components/LoadingOverlay.vue";
|
||||
|
||||
type LoadingValue =
|
||||
| boolean
|
||||
| string
|
||||
| {
|
||||
loading?: boolean;
|
||||
show?: boolean;
|
||||
visible?: boolean;
|
||||
text?: string;
|
||||
};
|
||||
|
||||
type LoadingState = {
|
||||
container: HTMLDivElement;
|
||||
props: {
|
||||
visible: boolean;
|
||||
text: string;
|
||||
};
|
||||
hadRelativePosition: boolean;
|
||||
};
|
||||
|
||||
type LoadingElement = HTMLElement & {
|
||||
__vLoadingState__?: LoadingState;
|
||||
};
|
||||
|
||||
function normalizeBinding(value: LoadingValue): { loading: boolean; text: string } {
|
||||
if (typeof value === "string") {
|
||||
return { loading: true, text: value };
|
||||
}
|
||||
|
||||
if (typeof value === "boolean") {
|
||||
return { loading: value, text: "" };
|
||||
}
|
||||
|
||||
if (value && typeof value === "object") {
|
||||
const loading = value.loading ?? value.show ?? value.visible ?? true;
|
||||
return { loading: Boolean(loading), text: value.text ?? "" };
|
||||
}
|
||||
|
||||
return { loading: false, text: "" };
|
||||
}
|
||||
|
||||
function ensureState(el: LoadingElement): LoadingState {
|
||||
if (el.__vLoadingState__) {
|
||||
return el.__vLoadingState__;
|
||||
}
|
||||
|
||||
const computedPosition = window.getComputedStyle(el).position;
|
||||
const hadRelativePosition = computedPosition !== "static";
|
||||
|
||||
if (!hadRelativePosition) {
|
||||
el.style.position = "relative";
|
||||
}
|
||||
|
||||
const container = document.createElement("div");
|
||||
el.appendChild(container);
|
||||
|
||||
const props = {
|
||||
visible: false,
|
||||
text: "",
|
||||
};
|
||||
|
||||
render(h(LoadingOverlay, props), container);
|
||||
|
||||
const state: LoadingState = {
|
||||
container,
|
||||
props,
|
||||
hadRelativePosition,
|
||||
};
|
||||
|
||||
el.__vLoadingState__ = state;
|
||||
return state;
|
||||
}
|
||||
|
||||
function applyState(el: LoadingElement, binding: DirectiveBinding<LoadingValue>): void {
|
||||
const state = ensureState(el);
|
||||
const { loading, text } = normalizeBinding(binding.value);
|
||||
|
||||
state.props.visible = loading;
|
||||
state.props.text = text;
|
||||
render(h(LoadingOverlay, state.props), state.container);
|
||||
}
|
||||
|
||||
const loadingDirective: Directive<HTMLElement, LoadingValue> = {
|
||||
mounted(el, binding) {
|
||||
applyState(el as LoadingElement, binding);
|
||||
},
|
||||
updated(el, binding) {
|
||||
applyState(el as LoadingElement, binding);
|
||||
},
|
||||
unmounted(el) {
|
||||
const loadingElement = el as LoadingElement;
|
||||
const state = loadingElement.__vLoadingState__;
|
||||
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
render(null, state.container);
|
||||
state.container.remove();
|
||||
|
||||
if (!state.hadRelativePosition) {
|
||||
loadingElement.style.position = "";
|
||||
}
|
||||
|
||||
delete loadingElement.__vLoadingState__;
|
||||
},
|
||||
};
|
||||
|
||||
export const loadingPlugin = {
|
||||
install(app: App): void {
|
||||
app.directive("loading", loadingDirective);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user