chore: rename api folder to composition
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { Ref } from 'vue';
|
||||
import { useGlobalEvent } from './use-global-event';
|
||||
|
||||
export type UseClickOutsideOpitons = {
|
||||
event: string;
|
||||
callback: EventListener;
|
||||
element: Ref<Element>;
|
||||
flag?: Ref<boolean>;
|
||||
};
|
||||
|
||||
export function useClickOutside(options: UseClickOutsideOpitons) {
|
||||
const { event = 'click', callback, element, flag } = options;
|
||||
|
||||
function onClick(event: Event) {
|
||||
if (!element.value.contains(event.target as Node)) {
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
|
||||
useGlobalEvent(document, event, onClick, false, flag);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { on, off } from '../utils/dom/event';
|
||||
import {
|
||||
Ref,
|
||||
watch,
|
||||
onMounted,
|
||||
onActivated,
|
||||
onUnmounted,
|
||||
onDeactivated,
|
||||
} from 'vue';
|
||||
|
||||
export function useGlobalEvent(
|
||||
target: EventTarget,
|
||||
event: string,
|
||||
handler: EventListener,
|
||||
passive = false,
|
||||
flag?: Ref<boolean>
|
||||
) {
|
||||
let binded: boolean;
|
||||
|
||||
function add() {
|
||||
if (binded || (flag && !flag.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
on(target, event, handler, passive);
|
||||
binded = true;
|
||||
}
|
||||
|
||||
function remove() {
|
||||
if (binded) {
|
||||
off(target, event, handler);
|
||||
binded = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
watch(() => {
|
||||
flag.value ? add() : remove();
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(add);
|
||||
onActivated(add);
|
||||
onUnmounted(remove);
|
||||
onDeactivated(remove);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ref, Ref, watch } from 'vue';
|
||||
|
||||
export function useLazyRender(show: Ref<boolean>) {
|
||||
const inited = ref(show.value);
|
||||
|
||||
watch(show, (value) => {
|
||||
if (value) {
|
||||
inited.value = value;
|
||||
}
|
||||
});
|
||||
|
||||
return inited;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { useTouch } from './use-touch';
|
||||
import { getScroller } from '../utils/dom/scroll';
|
||||
import { on, off, preventDefault } from '../utils/dom/event';
|
||||
|
||||
let count = 0;
|
||||
const CLASSNAME = 'van-overflow-hidden';
|
||||
|
||||
export function useLockScroll(element: HTMLElement) {
|
||||
const { start, move, deltaY, direction } = useTouch();
|
||||
|
||||
function onTouchMove(event: TouchEvent) {
|
||||
move(event);
|
||||
|
||||
if (direction.value !== 'vertical') {
|
||||
return;
|
||||
}
|
||||
|
||||
let prevent = false;
|
||||
const up = deltaY.value < 0;
|
||||
const scroller = getScroller(event.target as HTMLElement, element);
|
||||
const { scrollTop, scrollHeight, offsetHeight } = scroller as HTMLElement;
|
||||
|
||||
if (scrollTop === 0) {
|
||||
prevent = up && offsetHeight < scrollHeight;
|
||||
} else if (scrollTop + offsetHeight >= scrollHeight) {
|
||||
prevent = !up;
|
||||
}
|
||||
|
||||
if (prevent) {
|
||||
preventDefault(event, true);
|
||||
}
|
||||
}
|
||||
|
||||
function lock() {
|
||||
if (!count) {
|
||||
document.body.classList.add(CLASSNAME);
|
||||
}
|
||||
|
||||
count++;
|
||||
on(document, 'touchstart', start);
|
||||
on(document, 'touchmove', onTouchMove);
|
||||
}
|
||||
|
||||
lock();
|
||||
|
||||
return function unlock() {
|
||||
count--;
|
||||
off(document, 'touchstart', start);
|
||||
off(document, 'touchmove', onTouchMove);
|
||||
|
||||
if (!count) {
|
||||
document.body.classList.remove(CLASSNAME);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
export const useRect = (el: Ref<Element>) => el.value.getBoundingClientRect();
|
||||
|
||||
export const useWidth = (el: Ref<Element>) => useRect(el).width;
|
||||
|
||||
export const useHeight = (el: Ref<Element>) => useRect(el).height;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { inject, computed, onUnmounted } from 'vue';
|
||||
|
||||
export type Parent<T = unknown> = null | {
|
||||
children: T[];
|
||||
};
|
||||
|
||||
export function useParent<T = unknown>(key: string, child: T = {} as T) {
|
||||
const parent = inject<Parent<T>>(key, null);
|
||||
|
||||
if (parent) {
|
||||
const { children } = parent;
|
||||
const index = computed(() => children.indexOf(child));
|
||||
|
||||
children.push(child);
|
||||
|
||||
onUnmounted(() => {
|
||||
children.splice(index.value, 1);
|
||||
});
|
||||
|
||||
return {
|
||||
index,
|
||||
parent,
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Vue Router support
|
||||
*/
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import type { Router, RouteLocation } from 'vue-router';
|
||||
|
||||
export type RouteConfig = {
|
||||
url?: string;
|
||||
to?: RouteLocation;
|
||||
replace?: boolean;
|
||||
};
|
||||
|
||||
function isRedundantNavigation(err: Error) {
|
||||
return (
|
||||
err.name === 'NavigationDuplicated' ||
|
||||
// compatible with vue-router@3.3
|
||||
(err.message && err.message.indexOf('redundant navigation') !== -1)
|
||||
);
|
||||
}
|
||||
|
||||
export function route(router: Router, props: RouteProps) {
|
||||
const { to, url, replace } = props;
|
||||
if (to && router) {
|
||||
const promise = router[replace ? 'replace' : 'push'](to);
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (promise && promise.catch) {
|
||||
promise.catch((err) => {
|
||||
if (err && !isRedundantNavigation(err)) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (url) {
|
||||
replace ? location.replace(url) : (location.href = url);
|
||||
}
|
||||
}
|
||||
|
||||
export function useRoute() {
|
||||
const vm = getCurrentInstance()!.proxy!;
|
||||
return () => {
|
||||
route(vm.$router, vm as RouteProps);
|
||||
};
|
||||
}
|
||||
|
||||
export type RouteProps = {
|
||||
url?: string;
|
||||
replace?: boolean;
|
||||
to?: RouteLocation;
|
||||
};
|
||||
|
||||
export const routeProps = {
|
||||
url: String,
|
||||
replace: Boolean,
|
||||
to: [String, Object],
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
export function useToggle(defaultValue = false) {
|
||||
const state = ref(defaultValue);
|
||||
const setState = (value: boolean) => {
|
||||
state.value = value;
|
||||
};
|
||||
|
||||
return [state, setState];
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
const MIN_DISTANCE = 10;
|
||||
|
||||
function getDirection(x: number, y: number) {
|
||||
if (x > y && x > MIN_DISTANCE) {
|
||||
return 'horizontal';
|
||||
}
|
||||
|
||||
if (y > x && y > MIN_DISTANCE) {
|
||||
return 'vertical';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
export function useTouch() {
|
||||
const startX = ref(0);
|
||||
const startY = ref(0);
|
||||
const deltaX = ref(0);
|
||||
const deltaY = ref(0);
|
||||
const offsetX = ref(0);
|
||||
const offsetY = ref(0);
|
||||
const direction = ref('');
|
||||
|
||||
function reset() {
|
||||
direction.value = '';
|
||||
deltaX.value = 0;
|
||||
deltaY.value = 0;
|
||||
offsetX.value = 0;
|
||||
offsetY.value = 0;
|
||||
}
|
||||
|
||||
function start(event: TouchEvent) {
|
||||
reset();
|
||||
startX.value = event.touches[0].clientX;
|
||||
startY.value = event.touches[0].clientY;
|
||||
}
|
||||
|
||||
function move(event: TouchEvent) {
|
||||
const touch = event.touches[0];
|
||||
deltaX.value = touch.clientX - startX.value;
|
||||
deltaY.value = touch.clientY - startY.value;
|
||||
offsetX.value = Math.abs(deltaX.value);
|
||||
offsetY.value = Math.abs(deltaY.value);
|
||||
|
||||
if (!direction.value) {
|
||||
direction.value = getDirection(offsetX.value, offsetY.value);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
move,
|
||||
start,
|
||||
startX,
|
||||
startY,
|
||||
deltaX,
|
||||
deltaY,
|
||||
offsetX,
|
||||
offsetY,
|
||||
direction,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user