@varlet/use
Version:
composable utils of varlet
465 lines (464 loc) • 12.8 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
let _varlet_shared = require("@varlet/shared");
let vue = require("vue");
//#region src/onSmartMounted.ts
function onSmartMounted(hook) {
let isMounted = false;
(0, vue.onMounted)(() => {
hook();
(0, vue.nextTick)(() => {
isMounted = true;
});
});
(0, vue.onActivated)(() => {
if (!isMounted) return;
hook();
});
}
//#endregion
//#region src/useEventListener.ts
function useEventListener(target, type, listener, options = {}) {
if (!(0, _varlet_shared.inBrowser)()) return;
const { passive = false, capture = false } = options;
let listening = false;
let cleaned = false;
const getElement = (target) => (0, _varlet_shared.isFunction)(target) ? target() : (0, vue.unref)(target);
const add = (target) => {
if (listening || cleaned) return;
const element = getElement(target);
if (element) {
element.addEventListener(type, listener, {
passive,
capture
});
listening = true;
}
};
const remove = (target) => {
if (!listening || cleaned) return;
const element = getElement(target);
if (element) {
element.removeEventListener(type, listener, { capture });
listening = false;
}
};
let watchStopHandle;
if ((0, vue.isRef)(target)) watchStopHandle = (0, vue.watch)(() => target.value, (newValue, oldValue) => {
remove(oldValue);
add(newValue);
});
const cleanup = () => {
watchStopHandle?.();
remove(target);
cleaned = true;
};
onSmartMounted(() => {
add(target);
});
(0, vue.onBeforeUnmount)(() => {
remove(target);
});
(0, vue.onDeactivated)(() => {
remove(target);
});
return cleanup;
}
//#endregion
//#region src/useClickOutside.ts
function useClickOutside(target, type, listener) {
if (!(0, _varlet_shared.inBrowser)()) return;
const handler = (event) => {
const element = (0, _varlet_shared.isFunction)(target) ? target() : (0, vue.unref)(target);
if (element && !element.contains(event.target)) listener(event);
};
useEventListener(document, type, handler);
}
//#endregion
//#region src/onSmartUnmounted.ts
function onSmartUnmounted(hook) {
let keepalive = false;
(0, vue.onDeactivated)(() => {
keepalive = true;
hook();
});
(0, vue.onUnmounted)(() => {
if (keepalive) return;
hook();
});
}
//#endregion
//#region src/useParent.ts
function keyInProvides(key) {
return key in (0, vue.getCurrentInstance)().provides;
}
function useParent(key) {
if (!keyInProvides(key)) return {
index: null,
parentProvider: null,
bindParent: null
};
const { childInstances, collect, clear, ...parentProvider } = (0, vue.inject)(key);
const childInstance = (0, vue.getCurrentInstance)();
const index = (0, vue.computed)(() => childInstances.indexOf(childInstance));
const bindParent = (childProvider) => {
(0, vue.onMounted)(() => {
(0, vue.nextTick)().then(() => {
collect(childInstance, childProvider);
});
});
(0, vue.onBeforeUnmount)(() => {
(0, vue.nextTick)().then(() => {
clear(childInstance, childProvider);
});
});
};
return {
index,
parentProvider,
bindParent
};
}
//#endregion
//#region src/useChildren.ts
function flatVNodes(subTree) {
const vNodes = [];
const flat = (subTree) => {
if (subTree?.component) {
flat(subTree?.component.subTree);
return;
}
if ((0, _varlet_shared.isArray)(subTree?.children)) subTree.children.forEach((child) => {
if ((0, vue.isVNode)(child)) {
vNodes.push(child);
flat(child);
}
});
};
flat(subTree);
return vNodes;
}
function useChildren(key) {
const parentInstance = (0, vue.getCurrentInstance)();
const childInstances = (0, vue.reactive)([]);
const childProviders = [];
const length = (0, vue.computed)(() => childInstances.length);
const sortInstances = () => {
const vNodes = flatVNodes(parentInstance.subTree);
const pairs = childInstances.map((instance, index) => ({
instance,
provider: childProviders[index]
}));
const getVNodeIndex = (instance) => {
const index = vNodes.indexOf(instance.vnode);
return index === -1 ? Number.MAX_SAFE_INTEGER : index;
};
pairs.sort((a, b) => getVNodeIndex(a.instance) - getVNodeIndex(b.instance));
childInstances.splice(0, childInstances.length, ...pairs.map(({ instance }) => instance));
childProviders.splice(0, childProviders.length, ...pairs.map(({ provider }) => provider));
};
const collect = (childInstance, childProvider) => {
childInstances.push(childInstance);
childProviders.push(childProvider);
sortInstances();
};
const clear = (childInstance, childProvider) => {
(0, _varlet_shared.removeItem)(childInstances, childInstance);
(0, _varlet_shared.removeItem)(childProviders, childProvider);
};
const bindChildren = (parentProvider) => {
(0, vue.provide)(key, {
childInstances,
collect,
clear,
...parentProvider
});
};
(0, vue.onUpdated)(sortInstances);
return {
length,
childInstances,
childProviders,
bindChildren
};
}
//#endregion
//#region src/onWindowResize.ts
function onWindowResize(listener) {
useEventListener(() => window, "resize", listener, { passive: true });
useEventListener(() => window, "orientationchange", listener, { passive: true });
}
//#endregion
//#region src/useInitialized.ts
function useInitialized(source, value) {
const initialized = (0, vue.ref)(false);
(0, vue.watch)(source, (newValue) => {
if (value === newValue) initialized.value = true;
}, { immediate: true });
return initialized;
}
//#endregion
//#region src/useTouch.ts
function getDirection(x, y) {
if (x > y) return "horizontal";
if (y > x) return "vertical";
}
function useTouch() {
const startX = (0, vue.ref)(0);
const startY = (0, vue.ref)(0);
const deltaX = (0, vue.ref)(0);
const deltaY = (0, vue.ref)(0);
const offsetX = (0, vue.ref)(0);
const offsetY = (0, vue.ref)(0);
const prevX = (0, vue.ref)(0);
const prevY = (0, vue.ref)(0);
const moveX = (0, vue.ref)(0);
const moveY = (0, vue.ref)(0);
const direction = (0, vue.ref)();
const touching = (0, vue.ref)(false);
const dragging = (0, vue.ref)(false);
const startTime = (0, vue.ref)(0);
const distance = (0, vue.ref)(0);
let draggingAnimationFrame = null;
const resetTouch = () => {
startX.value = 0;
startY.value = 0;
deltaX.value = 0;
deltaY.value = 0;
offsetX.value = 0;
offsetY.value = 0;
prevX.value = 0;
prevY.value = 0;
moveX.value = 0;
moveY.value = 0;
direction.value = void 0;
touching.value = false;
dragging.value = false;
startTime.value = 0;
distance.value = 0;
};
const startTouch = (event) => {
resetTouch();
const { clientX: x, clientY: y } = event.touches[0];
startX.value = x;
startY.value = y;
prevX.value = x;
prevY.value = y;
touching.value = true;
startTime.value = performance.now();
dragging.value = false;
if (draggingAnimationFrame) window.cancelAnimationFrame(draggingAnimationFrame);
};
const moveTouch = (event) => {
const { clientX: x, clientY: y } = event.touches[0];
dragging.value = true;
deltaX.value = x - startX.value;
deltaY.value = y - startY.value;
offsetX.value = Math.abs(deltaX.value);
offsetY.value = Math.abs(deltaY.value);
distance.value = Math.sqrt(offsetX.value ** 2 + offsetY.value ** 2);
moveX.value = x - prevX.value;
moveY.value = y - prevY.value;
if (!direction.value) direction.value = getDirection(offsetX.value, offsetY.value);
prevX.value = x;
prevY.value = y;
};
const endTouch = () => {
touching.value = false;
draggingAnimationFrame = window.requestAnimationFrame(() => {
dragging.value = false;
});
};
const isReachTop = (element) => {
return (0, _varlet_shared.getScrollTop)(element) === 0 && deltaY.value > 0;
};
const isReachBottom = (element, offset = 1) => {
const { scrollHeight, clientHeight, scrollTop } = element;
const offsetBottom = Math.abs(scrollHeight - scrollTop - clientHeight);
return deltaY.value < 0 && offsetBottom <= offset;
};
return {
startX,
startY,
deltaX,
deltaY,
offsetX,
offsetY,
prevX,
prevY,
moveX,
moveY,
direction,
touching,
dragging,
startTime,
distance,
resetTouch,
startTouch,
moveTouch,
endTouch,
isReachTop,
isReachBottom
};
}
//#endregion
//#region src/useId.ts
function useId() {
const id = (0, vue.ref)();
const instance = (0, vue.getCurrentInstance)();
const name = (0, _varlet_shared.kebabCase)(instance.type.name);
id.value = process.env.NODE_ENV === "test" ? `${name}-mock-id` : `${name}-${instance.uid}`;
return id;
}
//#endregion
//#region src/useClientId.ts
function useClientId() {
const instance = (0, vue.getCurrentInstance)();
const name = (0, _varlet_shared.kebabCase)(instance.type.name);
const id = (0, vue.ref)(process.env.NODE_ENV === "test" ? `${name}-mock-id` : void 0);
(0, vue.onMounted)(() => {
if (process.env.NODE_ENV !== "test") id.value = `${name}-${instance.uid}`;
});
return id;
}
//#endregion
//#region src/useWindowSize.ts
function useWindowSize(options = {}) {
const { initialWidth = 0, initialHeight = 0 } = options;
const width = (0, vue.ref)(initialWidth);
const height = (0, vue.ref)(initialHeight);
const update = () => {
if (!(0, _varlet_shared.inBrowser)()) return;
width.value = window.innerWidth;
height.value = window.innerHeight;
};
onSmartMounted(update);
onWindowResize(update);
return {
width,
height
};
}
//#endregion
//#region src/useVModel.ts
function useVModel(props, key, options = {}) {
const { passive = true, eventName, defaultValue, emit } = options;
const event = eventName ?? `onUpdate:${key.toString()}`;
const getValue = () => props[key] ?? defaultValue;
if (!passive) return (0, vue.computed)({
get() {
return getValue();
},
set(value) {
emit ? emit(event, value) : (0, _varlet_shared.call)(props[event], value);
}
});
const proxy = (0, vue.ref)(getValue());
let shouldEmit = true;
(0, vue.watch)(() => props[key], () => {
shouldEmit = false;
proxy.value = getValue();
(0, vue.nextTick)(() => {
shouldEmit = true;
});
});
(0, vue.watch)(() => proxy.value, (newValue) => {
if (!shouldEmit) return;
emit ? emit(event, newValue) : (0, _varlet_shared.call)(props[event], newValue);
});
return proxy;
}
//#endregion
//#region src/useMotion.ts
function useMotion(options) {
const value = (0, vue.ref)(getter(options.from));
const state = (0, vue.ref)("pending");
let ctx = createMotionContext();
function getter(value) {
return (0, _varlet_shared.isFunction)(value) ? value() : value;
}
function reset() {
ctx.reset();
value.value = getter(options.from);
state.value = "pending";
ctx = createMotionContext();
}
function start() {
ctx.start();
}
function pause() {
ctx.pause();
}
function createMotionContext() {
return (0, _varlet_shared.motion)({
from: getter(options.from),
to: getter(options.to),
duration: options.duration ? getter(options.duration) : 300,
timingFunction: options.timingFunction,
onStateChange(newState) {
state.value = newState;
},
frame({ value: newValue, done }) {
value.value = newValue;
if (done) options.onFinished?.(value.value);
}
});
}
return {
value,
state,
start,
pause,
reset
};
}
//#endregion
//#region src/useResizeObserver.ts
function useResizeObserver(target, callback) {
if (!(0, _varlet_shared.inBrowser)() || typeof ResizeObserver === "undefined") return;
let observer;
let observedElement;
let cleaned = false;
const getElement = (target) => (0, _varlet_shared.isFunction)(target) ? target() : (0, vue.unref)(target);
const observe = (target) => {
if (cleaned) return;
const element = getElement(target);
if (!element || element === observedElement) return;
unobserve();
observer = new ResizeObserver(callback);
observer.observe(element);
observedElement = element;
};
const unobserve = () => {
observer?.disconnect();
observer = void 0;
observedElement = void 0;
};
let watchStopHandle;
if ((0, vue.isRef)(target)) watchStopHandle = (0, vue.watch)(() => target.value, () => observe(target));
const cleanup = () => {
watchStopHandle?.();
unobserve();
cleaned = true;
};
onSmartMounted(() => observe(target));
(0, vue.onBeforeUnmount)(unobserve);
(0, vue.onDeactivated)(unobserve);
return cleanup;
}
//#endregion
exports.keyInProvides = keyInProvides;
exports.onSmartMounted = onSmartMounted;
exports.onSmartUnmounted = onSmartUnmounted;
exports.onWindowResize = onWindowResize;
exports.useChildren = useChildren;
exports.useClickOutside = useClickOutside;
exports.useClientId = useClientId;
exports.useEventListener = useEventListener;
exports.useId = useId;
exports.useInitialized = useInitialized;
exports.useMotion = useMotion;
exports.useParent = useParent;
exports.useResizeObserver = useResizeObserver;
exports.useTouch = useTouch;
exports.useVModel = useVModel;
exports.useWindowSize = useWindowSize;