Skip to content

Commit 7fa1fe4

Browse files
committed
feat: add vueuse feature
1 parent ac9d3e4 commit 7fa1fe4

11 files changed

+462
-0
lines changed

LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,25 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
4444
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
4545
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
4646
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
47+
48+
MIT License
49+
50+
Copyright (c) 2019-PRESENT Anthony Fu<https://github.com/antfu>
51+
52+
Permission is hereby granted, free of charge, to any person obtaining a copy
53+
of this software and associated documentation files (the "Software"), to deal
54+
in the Software without restriction, including without limitation the rights
55+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
56+
copies of the Software, and to permit persons to whom the Software is
57+
furnished to do so, subject to the following conditions:
58+
59+
The above copyright notice and this permission notice shall be included in all
60+
copies or substantial portions of the Software.
61+
62+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
63+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
64+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
65+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
66+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
67+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
68+
SOFTWARE.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { isClient } from './is';
2+
3+
export interface ConfigurableWindow {
4+
/*
5+
* Specify a custom `window` instance, e.g. working with iframes or in testing environments.
6+
*/
7+
window?: Window;
8+
}
9+
10+
export interface ConfigurableDocument {
11+
/*
12+
* Specify a custom `document` instance, e.g. working with iframes or in testing environments.
13+
*/
14+
document?: Document;
15+
}
16+
17+
export interface ConfigurableNavigator {
18+
/*
19+
* Specify a custom `navigator` instance, e.g. working with iframes or in testing environments.
20+
*/
21+
navigator?: Navigator;
22+
}
23+
24+
export interface ConfigurableLocation {
25+
/*
26+
* Specify a custom `location` instance, e.g. working with iframes or in testing environments.
27+
*/
28+
location?: Location;
29+
}
30+
31+
export const defaultWindow = /* #__PURE__ */ isClient ? window : undefined;
32+
export const defaultDocument = /* #__PURE__ */ isClient ? window.document : undefined;
33+
export const defaultNavigator = /* #__PURE__ */ isClient ? window.navigator : undefined;
34+
export const defaultLocation = /* #__PURE__ */ isClient ? window.location : undefined;

components/_util/hooks/_vueuse/is.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export const isClient = typeof window !== 'undefined';
2+
export const isDef = <T = any>(val?: T): val is T => typeof val !== 'undefined';
3+
export const assert = (condition: boolean, ...infos: any[]) => {
4+
if (!condition) console.warn(...infos);
5+
};
6+
const toString = Object.prototype.toString;
7+
export const isBoolean = (val: any): val is boolean => typeof val === 'boolean';
8+
export const isFunction = <T extends Function>(val: any): val is T => typeof val === 'function';
9+
export const isNumber = (val: any): val is number => typeof val === 'number';
10+
export const isString = (val: unknown): val is string => typeof val === 'string';
11+
export const isObject = (val: any): val is object => toString.call(val) === '[object Object]';
12+
export const isWindow = (val: any): val is Window =>
13+
typeof window !== 'undefined' && toString.call(val) === '[object Window]';
14+
export const now = () => Date.now();
15+
export const timestamp = () => +Date.now();
16+
export const clamp = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n));
17+
export const noop = () => {};
18+
export const rand = (min: number, max: number) => {
19+
min = Math.ceil(min);
20+
max = Math.floor(max);
21+
return Math.floor(Math.random() * (max - min + 1)) + min;
22+
};
23+
export const isIOS =
24+
/* #__PURE__ */ isClient &&
25+
window?.navigator?.userAgent &&
26+
/iP(ad|hone|od)/.test(window.navigator.userAgent);
27+
export const hasOwn = <T extends object, K extends keyof T>(val: T, key: K): key is K =>
28+
Object.prototype.hasOwnProperty.call(val, key);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { unref } from 'vue';
2+
import type { MaybeComputedRef } from './types';
3+
4+
/**
5+
* Get the value of value/ref/getter.
6+
*/
7+
export function resolveUnref<T>(r: MaybeComputedRef<T>): T {
8+
return typeof r === 'function' ? (r as any)() : unref(r);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// eslint-disable-next-line no-restricted-imports
2+
import { getCurrentInstance, nextTick, onMounted } from 'vue';
3+
import type { Fn } from './types';
4+
5+
/**
6+
* Call onMounted() if it's inside a component lifecycle, if not, just call the function
7+
*
8+
* @param fn
9+
* @param sync if set to false, it will run in the nextTick() of Vue
10+
*/
11+
export function tryOnMounted(fn: Fn, sync = true) {
12+
if (getCurrentInstance()) onMounted(fn);
13+
else if (sync) fn();
14+
else nextTick(fn);
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { getCurrentScope, onScopeDispose } from 'vue';
2+
import type { Fn } from './types';
3+
4+
/**
5+
* Call onScopeDispose() if it's inside a effect scope lifecycle, if not, do nothing
6+
*
7+
* @param fn
8+
*/
9+
export function tryOnScopeDispose(fn: Fn) {
10+
if (getCurrentScope()) {
11+
onScopeDispose(fn);
12+
return true;
13+
}
14+
return false;
15+
}
+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import type { ComputedRef, Ref, WatchOptions, WatchSource } from 'vue';
2+
3+
/**
4+
* Any function
5+
*/
6+
export type Fn = () => void;
7+
8+
/**
9+
* A ref that allow to set null or undefined
10+
*/
11+
export type RemovableRef<T> = Omit<Ref<T>, 'value'> & {
12+
get value(): T;
13+
set value(value: T | null | undefined);
14+
};
15+
16+
/**
17+
* @deprecated Use `RemovableRef`
18+
*/
19+
export type RemoveableRef<T> = RemovableRef<T>;
20+
21+
/**
22+
* Maybe it's a ref, or a plain value
23+
*
24+
* ```ts
25+
* type MaybeRef<T> = T | Ref<T>
26+
* ```
27+
*/
28+
export type MaybeRef<T> = T | Ref<T>;
29+
30+
/**
31+
* Maybe it's a ref, or a plain value, or a getter function
32+
*
33+
* ```ts
34+
* type MaybeComputedRef<T> = (() => T) | T | Ref<T> | ComputedRef<T>
35+
* ```
36+
*/
37+
export type MaybeComputedRef<T> = MaybeReadonlyRef<T> | MaybeRef<T>;
38+
39+
/**
40+
* Maybe it's a computed ref, or a getter function
41+
*
42+
* ```ts
43+
* type MaybeReadonlyRef<T> = (() => T) | ComputedRef<T>
44+
* ```
45+
*/
46+
export type MaybeReadonlyRef<T> = (() => T) | ComputedRef<T>;
47+
48+
/**
49+
* Make all the nested attributes of an object or array to MaybeRef<T>
50+
*
51+
* Good for accepting options that will be wrapped with `reactive` or `ref`
52+
*
53+
* ```ts
54+
* UnwrapRef<DeepMaybeRef<T>> === T
55+
* ```
56+
*/
57+
export type DeepMaybeRef<T> = T extends Ref<infer V>
58+
? MaybeRef<V>
59+
: T extends Array<any> | object
60+
? { [K in keyof T]: DeepMaybeRef<T[K]> }
61+
: MaybeRef<T>;
62+
63+
/**
64+
* Infers the element type of an array
65+
*/
66+
export type ElementOf<T> = T extends (infer E)[] ? E : never;
67+
68+
export type ShallowUnwrapRef<T> = T extends Ref<infer P> ? P : T;
69+
70+
export type Awaitable<T> = Promise<T> | T;
71+
72+
export type ArgumentsType<T> = T extends (...args: infer U) => any ? U : never;
73+
74+
export interface Pausable {
75+
/**
76+
* A ref indicate whether a pausable instance is active
77+
*/
78+
isActive: Ref<boolean>;
79+
80+
/**
81+
* Temporary pause the effect from executing
82+
*/
83+
pause: Fn;
84+
85+
/**
86+
* Resume the effects
87+
*/
88+
resume: Fn;
89+
}
90+
91+
export interface Stoppable {
92+
/**
93+
* A ref indicate whether a stoppable instance is executing
94+
*/
95+
isPending: Ref<boolean>;
96+
97+
/**
98+
* Stop the effect from executing
99+
*/
100+
stop: Fn;
101+
102+
/**
103+
* Start the effects
104+
*/
105+
start: Fn;
106+
}
107+
108+
/**
109+
* @deprecated Use `Stoppable`
110+
*/
111+
export type Stopable = Stoppable;
112+
113+
export interface ConfigurableFlush {
114+
/**
115+
* Timing for monitoring changes, refer to WatchOptions for more details
116+
*
117+
* @default 'pre'
118+
*/
119+
flush?: WatchOptions['flush'];
120+
}
121+
122+
export interface ConfigurableFlushSync {
123+
/**
124+
* Timing for monitoring changes, refer to WatchOptions for more details.
125+
* Unlike `watch()`, the default is set to `sync`
126+
*
127+
* @default 'sync'
128+
*/
129+
flush?: WatchOptions['flush'];
130+
}
131+
132+
// Internal Types
133+
export type MapSources<T> = {
134+
[K in keyof T]: T[K] extends WatchSource<infer V> ? V : never;
135+
};
136+
export type MapOldSources<T, Immediate> = {
137+
[K in keyof T]: T[K] extends WatchSource<infer V>
138+
? Immediate extends true
139+
? V | undefined
140+
: V
141+
: never;
142+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { ComponentPublicInstance } from 'vue';
2+
import type { MaybeComputedRef, MaybeRef } from './types';
3+
import { resolveUnref } from './resolveUnref';
4+
5+
export type VueInstance = ComponentPublicInstance;
6+
export type MaybeElementRef<T extends MaybeElement = MaybeElement> = MaybeRef<T>;
7+
export type MaybeComputedElementRef<T extends MaybeElement = MaybeElement> = MaybeComputedRef<T>;
8+
export type MaybeElement = HTMLElement | SVGElement | VueInstance | undefined | null;
9+
10+
export type UnRefElementReturn<T extends MaybeElement = MaybeElement> = T extends VueInstance
11+
? Exclude<MaybeElement, VueInstance>
12+
: T | undefined;
13+
14+
/**
15+
* Get the dom element of a ref of element or Vue component instance
16+
*
17+
* @param elRef
18+
*/
19+
export function unrefElement<T extends MaybeElement>(
20+
elRef: MaybeComputedElementRef<T>,
21+
): UnRefElementReturn<T> {
22+
const plain = resolveUnref(elRef);
23+
return (plain as VueInstance)?.$el ?? plain;
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { ref, watch } from 'vue';
2+
import type { MaybeComputedElementRef } from './unrefElement';
3+
import type { UseResizeObserverOptions } from './useResizeObserver';
4+
import { useResizeObserver } from './useResizeObserver';
5+
import { unrefElement } from './unrefElement';
6+
7+
export interface ElementSize {
8+
width: number;
9+
height: number;
10+
}
11+
12+
/**
13+
* Reactive size of an HTML element.
14+
*
15+
* @see https://vueuse.org/useElementSize
16+
* @param target
17+
* @param callback
18+
* @param options
19+
*/
20+
export function useElementSize(
21+
target: MaybeComputedElementRef,
22+
initialSize: ElementSize = { width: 0, height: 0 },
23+
options: UseResizeObserverOptions = {},
24+
) {
25+
const { box = 'content-box' } = options;
26+
const width = ref(initialSize.width);
27+
const height = ref(initialSize.height);
28+
29+
useResizeObserver(
30+
target,
31+
([entry]) => {
32+
const boxSize =
33+
box === 'border-box'
34+
? entry.borderBoxSize
35+
: box === 'content-box'
36+
? entry.contentBoxSize
37+
: entry.devicePixelContentBoxSize;
38+
39+
if (boxSize) {
40+
width.value = boxSize.reduce((acc, { inlineSize }) => acc + inlineSize, 0);
41+
height.value = boxSize.reduce((acc, { blockSize }) => acc + blockSize, 0);
42+
} else {
43+
// fallback
44+
width.value = entry.contentRect.width;
45+
height.value = entry.contentRect.height;
46+
}
47+
},
48+
options,
49+
);
50+
51+
watch(
52+
() => unrefElement(target),
53+
ele => {
54+
width.value = ele ? initialSize.width : 0;
55+
height.value = ele ? initialSize.height : 0;
56+
},
57+
);
58+
59+
return {
60+
width,
61+
height,
62+
};
63+
}
64+
65+
export type UseElementSizeReturn = ReturnType<typeof useElementSize>;

0 commit comments

Comments
 (0)