@laser-ui/hooks 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
4
+
5
+ ## 0.0.1 (2023-09-07)
6
+
7
+ **Note:** Version bump only for package @laser-ui/hooks
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Xie Jay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @laser-ui/hooks
2
+
3
+ Laser UI: Fast and Powerful React Components.
4
+
5
+ Shared hooks used by Laser UI packages.
package/index.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ export { useStorage } from './storage';
2
+ export { useACL } from './useACL';
3
+ export { useAsync } from './useAsync';
4
+ export { useEvent } from './useEvent';
5
+ export { useEventCallback } from './useEventCallback';
6
+ export { useForceUpdate } from './useForceUpdate';
7
+ export { useForkRef } from './useForkRef';
8
+ export { useImmer } from './useImmer';
9
+ export { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
10
+ export { useMount } from './useMount';
11
+ export { useRefExtra } from './useRefExtra';
12
+ export { useResize } from './useResize';
13
+ export { useUnmount } from './useUnmount';
package/index.js ADDED
@@ -0,0 +1,13 @@
1
+ export { useStorage } from './storage';
2
+ export { useACL } from './useACL';
3
+ export { useAsync } from './useAsync';
4
+ export { useEvent } from './useEvent';
5
+ export { useEventCallback } from './useEventCallback';
6
+ export { useForceUpdate } from './useForceUpdate';
7
+ export { useForkRef } from './useForkRef';
8
+ export { useImmer } from './useImmer';
9
+ export { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
10
+ export { useMount } from './useMount';
11
+ export { useRefExtra } from './useRefExtra';
12
+ export { useResize } from './useResize';
13
+ export { useUnmount } from './useUnmount';
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@laser-ui/hooks",
3
+ "version": "0.0.1",
4
+ "description": "Shared hooks used by Laser UI packages.",
5
+ "keywords": [
6
+ "ui",
7
+ "laser-ui",
8
+ "hook",
9
+ "hooks",
10
+ "react",
11
+ "react-hook"
12
+ ],
13
+ "homepage": "https://github.com/laser-ui/laser-ui",
14
+ "bugs": {
15
+ "url": "https://github.com/laser-ui/laser-ui/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/laser-ui/laser-ui.git"
20
+ },
21
+ "license": "MIT",
22
+ "author": "Xie Jay <xiejay97@gmail.com>",
23
+ "sideEffects": false,
24
+ "main": "./index.js",
25
+ "types": "./index.d.ts",
26
+ "dependencies": {
27
+ "lodash": "^4.17.21",
28
+ "rxjs": "^7.8.1"
29
+ },
30
+ "peerDependencies": {
31
+ "immer": ">=2.0.0",
32
+ "react": "^18.0.0",
33
+ "react-dom": "^18.0.0"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public",
37
+ "directory": "../../dist/libs/hooks"
38
+ },
39
+ "gitHead": "d08f89d34ac56ff632db700f52c06931337c9ce8",
40
+ "module": "./src/index.js",
41
+ "type": "module"
42
+ }
@@ -0,0 +1,4 @@
1
+ export * from './useStorage';
2
+ export * from './parser';
3
+ export * from './storage';
4
+ export * from './localStorage';
@@ -0,0 +1,4 @@
1
+ export * from './useStorage';
2
+ export * from './parser';
3
+ export * from './storage';
4
+ export * from './localStorage';
@@ -0,0 +1,8 @@
1
+ import { AbstractStorage } from './storage';
2
+ export declare class LocalStorageService extends AbstractStorage<string, string> {
3
+ getItem(key: string): string | null;
4
+ getItem(key: string, defaultValue: string): string;
5
+ setItem(key: string, value: string): void;
6
+ removeItem(key: string): void;
7
+ clear(): void;
8
+ }
@@ -0,0 +1,16 @@
1
+ import { AbstractStorage } from './storage';
2
+ export class LocalStorageService extends AbstractStorage {
3
+ getItem(key, defaultValue) {
4
+ var _a, _b;
5
+ return (_b = (_a = localStorage.getItem(key)) !== null && _a !== void 0 ? _a : defaultValue) !== null && _b !== void 0 ? _b : null;
6
+ }
7
+ setItem(key, value) {
8
+ localStorage.setItem(key, value);
9
+ }
10
+ removeItem(key) {
11
+ localStorage.removeItem(key);
12
+ }
13
+ clear() {
14
+ localStorage.clear();
15
+ }
16
+ }
@@ -0,0 +1,10 @@
1
+ export interface Parser<V, O> {
2
+ deserializer: (value: V) => O;
3
+ serializer: (value: O) => V;
4
+ }
5
+ export interface AbstractParserOptions<V> {
6
+ plain: Parser<V, string>;
7
+ number: Parser<V, number>;
8
+ json: Parser<V, any>;
9
+ }
10
+ export declare const STRING_PARSER: AbstractParserOptions<string>;
@@ -0,0 +1,14 @@
1
+ export const STRING_PARSER = {
2
+ plain: {
3
+ serializer: (value) => value,
4
+ deserializer: (value) => value,
5
+ },
6
+ number: {
7
+ serializer: (value) => String(value),
8
+ deserializer: (value) => Number(value),
9
+ },
10
+ json: {
11
+ serializer: (value) => JSON.stringify(value),
12
+ deserializer: (value) => JSON.parse(value),
13
+ },
14
+ };
@@ -0,0 +1,7 @@
1
+ export declare abstract class AbstractStorage<K, V> {
2
+ abstract getItem(key: K): V | null;
3
+ abstract getItem(key: K, defaultValue: V): V;
4
+ abstract setItem(key: K, value: V): void;
5
+ abstract removeItem(key: K): void;
6
+ abstract clear(): void;
7
+ }
@@ -0,0 +1,2 @@
1
+ export class AbstractStorage {
2
+ }
@@ -0,0 +1,18 @@
1
+ import type { AbstractStorage } from './storage';
2
+ declare class Store {
3
+ private _listeners;
4
+ private _key;
5
+ private _storage;
6
+ constructor(key: any, storage: AbstractStorage<any, any>);
7
+ subscribe(onStoreChange: () => void): () => void;
8
+ getSnapshot(): any;
9
+ emitChange(): void;
10
+ }
11
+ export declare const stores: Map<any, Store>;
12
+ export declare function useStore(key: any, defaultValue: any, storage: AbstractStorage<any, any>): {
13
+ subscribe: (onStoreChange: () => void) => () => void;
14
+ getSnapshot: () => any;
15
+ getServerSnapshot: () => any;
16
+ emitChange: () => void;
17
+ };
18
+ export {};
@@ -0,0 +1,64 @@
1
+ import { useMemo } from 'react';
2
+ class Store {
3
+ constructor(key, storage) {
4
+ Object.defineProperty(this, "_listeners", {
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true,
8
+ value: []
9
+ });
10
+ Object.defineProperty(this, "_key", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: void 0
15
+ });
16
+ Object.defineProperty(this, "_storage", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: void 0
21
+ });
22
+ this._key = key;
23
+ this._storage = storage;
24
+ }
25
+ subscribe(onStoreChange) {
26
+ this._listeners = this._listeners.concat([onStoreChange]);
27
+ return () => {
28
+ this._listeners = this._listeners.filter((f) => f !== onStoreChange);
29
+ };
30
+ }
31
+ getSnapshot() {
32
+ return this._storage.getItem(this._key);
33
+ }
34
+ emitChange() {
35
+ for (const listener of this._listeners) {
36
+ listener();
37
+ }
38
+ }
39
+ }
40
+ export const stores = new Map();
41
+ export function useStore(key, defaultValue, storage) {
42
+ return useMemo(() => {
43
+ let store = stores.get(key);
44
+ if (!store) {
45
+ store = new Store(key, storage);
46
+ stores.set(key, store);
47
+ }
48
+ return {
49
+ subscribe: store.subscribe.bind(store),
50
+ getSnapshot: store.getSnapshot.bind(store),
51
+ getServerSnapshot: () => {
52
+ try {
53
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
54
+ return store.getSnapshot().bind(store);
55
+ }
56
+ catch (error) {
57
+ return defaultValue !== null && defaultValue !== void 0 ? defaultValue : null;
58
+ }
59
+ },
60
+ emitChange: store.emitChange.bind(store),
61
+ };
62
+ // eslint-disable-next-line react-hooks/exhaustive-deps
63
+ }, [key, storage]);
64
+ }
@@ -0,0 +1,18 @@
1
+ import type { AbstractParserOptions } from './parser';
2
+ import type { AbstractStorage } from './storage';
3
+ interface UseStorageMethod<V> {
4
+ set: (value: V) => void;
5
+ remove: () => void;
6
+ }
7
+ export declare function useStorage<V, K = string>(key: K | null, defaultValue?: undefined, parser?: keyof AbstractParserOptions<any>): {
8
+ readonly value: V | null;
9
+ } & UseStorageMethod<V>;
10
+ export declare function useStorage<V, K = string>(key: K, defaultValue: V, parser?: keyof AbstractParserOptions<any>): {
11
+ readonly value: V;
12
+ } & UseStorageMethod<V>;
13
+ export declare namespace useStorage {
14
+ var SERVICE: AbstractStorage<any, any>;
15
+ var PARSER: AbstractParserOptions<any>;
16
+ var clear: () => void;
17
+ }
18
+ export {};
@@ -0,0 +1,36 @@
1
+ import { isNull, isUndefined } from 'lodash';
2
+ import { useMemo, useSyncExternalStore } from 'react';
3
+ import { LocalStorageService } from './localStorage';
4
+ import { STRING_PARSER } from './parser';
5
+ import { useStore, stores } from './store';
6
+ export function useStorage(key, defaultValue, parser = 'plain') {
7
+ const { SERVICE, PARSER } = useStorage;
8
+ const { serializer, deserializer } = isUndefined(parser) ? PARSER.plain : PARSER[parser];
9
+ const store = useStore(key, defaultValue, SERVICE);
10
+ const value = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot);
11
+ return useMemo(() => ({
12
+ get value() {
13
+ if (isNull(value)) {
14
+ return defaultValue !== null && defaultValue !== void 0 ? defaultValue : null;
15
+ }
16
+ return deserializer(value);
17
+ },
18
+ set: (value) => {
19
+ const originValue = serializer(value);
20
+ SERVICE.setItem(key, originValue);
21
+ store.emitChange();
22
+ },
23
+ remove: () => {
24
+ SERVICE.removeItem(key);
25
+ store.emitChange();
26
+ },
27
+ }), [SERVICE, defaultValue, deserializer, key, serializer, store, value]);
28
+ }
29
+ useStorage.SERVICE = new LocalStorageService();
30
+ useStorage.PARSER = STRING_PARSER;
31
+ useStorage.clear = () => {
32
+ useStorage.SERVICE.clear();
33
+ for (const [, store] of stores) {
34
+ store.emitChange();
35
+ }
36
+ };
package/useACL.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export type Control = string | number;
2
+ export type ControlMode = 'one' | 'all';
3
+ export declare class ACL {
4
+ private _full;
5
+ private _controls;
6
+ get full(): boolean;
7
+ get controls(): Control[];
8
+ setFull(full: boolean): void;
9
+ set(control: Control[]): void;
10
+ add(control: Control | Control[]): void;
11
+ remove(control: Control | Control[]): void;
12
+ can(control: Control | Control[], mode?: ControlMode): boolean;
13
+ }
14
+ export declare function useACL(): ACL;
package/useACL.js ADDED
@@ -0,0 +1,79 @@
1
+ import { isArray } from 'lodash';
2
+ import { useSyncExternalStore } from 'react';
3
+ export class ACL {
4
+ constructor() {
5
+ Object.defineProperty(this, "_full", {
6
+ enumerable: true,
7
+ configurable: true,
8
+ writable: true,
9
+ value: false
10
+ });
11
+ Object.defineProperty(this, "_controls", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: new Set()
16
+ });
17
+ }
18
+ get full() {
19
+ return this._full;
20
+ }
21
+ get controls() {
22
+ return Array.from(this._controls);
23
+ }
24
+ setFull(full) {
25
+ this._full = full;
26
+ emitChange();
27
+ }
28
+ set(control) {
29
+ this._controls = new Set(control);
30
+ emitChange();
31
+ }
32
+ add(control) {
33
+ for (const v of isArray(control) ? control : [control]) {
34
+ this._controls.add(v);
35
+ }
36
+ emitChange();
37
+ }
38
+ remove(control) {
39
+ for (const v of isArray(control) ? control : [control]) {
40
+ this._controls.delete(v);
41
+ }
42
+ emitChange();
43
+ }
44
+ can(control, mode = 'one') {
45
+ if (this._full) {
46
+ return true;
47
+ }
48
+ const arr = isArray(control) ? control : [control];
49
+ let n = 0;
50
+ for (const v of arr) {
51
+ if (this._controls.has(v)) {
52
+ n += 1;
53
+ }
54
+ }
55
+ if (n > 0 && (mode === 'one' || (mode === 'all' && n === arr.length))) {
56
+ return true;
57
+ }
58
+ return false;
59
+ }
60
+ }
61
+ const acl = new ACL();
62
+ let listeners = [];
63
+ function subscribe(onStoreChange) {
64
+ listeners = listeners.concat([onStoreChange]);
65
+ return () => {
66
+ listeners = listeners.filter((f) => f !== onStoreChange);
67
+ };
68
+ }
69
+ function getSnapshot() {
70
+ return acl;
71
+ }
72
+ function emitChange() {
73
+ for (const listener of listeners) {
74
+ listener();
75
+ }
76
+ }
77
+ export function useACL() {
78
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
79
+ }
package/useAsync.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ declare class AsyncInstance {
2
+ protected clearFns: Set<() => void>;
3
+ clearAll(): void;
4
+ setTimeout(handler: TimerHandler, timeout?: number, clearFn?: () => void): () => void;
5
+ requestAnimationFrame(cb: FrameRequestCallback, clearFn?: () => void): () => void;
6
+ }
7
+ export declare class Async extends AsyncInstance {
8
+ private instances;
9
+ create(): AsyncInstance;
10
+ clearAll(): void;
11
+ }
12
+ export declare function useAsync(): Async;
13
+ export {};
package/useAsync.js ADDED
@@ -0,0 +1,66 @@
1
+ import { useMemo } from 'react';
2
+ import { useUnmount } from './useUnmount';
3
+ class AsyncInstance {
4
+ constructor() {
5
+ Object.defineProperty(this, "clearFns", {
6
+ enumerable: true,
7
+ configurable: true,
8
+ writable: true,
9
+ value: new Set()
10
+ });
11
+ }
12
+ clearAll() {
13
+ for (const clear of this.clearFns) {
14
+ clear();
15
+ }
16
+ }
17
+ setTimeout(handler, timeout, clearFn) {
18
+ const tid = window.setTimeout(handler, timeout);
19
+ const clear = () => {
20
+ clearFn === null || clearFn === void 0 ? void 0 : clearFn();
21
+ clearTimeout(tid);
22
+ };
23
+ this.clearFns.add(clear);
24
+ return clear;
25
+ }
26
+ requestAnimationFrame(cb, clearFn) {
27
+ const tid = requestAnimationFrame(cb);
28
+ const clear = () => {
29
+ clearFn === null || clearFn === void 0 ? void 0 : clearFn();
30
+ cancelAnimationFrame(tid);
31
+ };
32
+ this.clearFns.add(clear);
33
+ return clear;
34
+ }
35
+ }
36
+ export class Async extends AsyncInstance {
37
+ constructor() {
38
+ super(...arguments);
39
+ Object.defineProperty(this, "instances", {
40
+ enumerable: true,
41
+ configurable: true,
42
+ writable: true,
43
+ value: new Set()
44
+ });
45
+ }
46
+ create() {
47
+ const instance = new AsyncInstance();
48
+ this.instances.add(instance);
49
+ return instance;
50
+ }
51
+ clearAll() {
52
+ for (const clear of this.clearFns) {
53
+ clear();
54
+ }
55
+ for (const instance of this.instances) {
56
+ instance.clearAll();
57
+ }
58
+ }
59
+ }
60
+ export function useAsync() {
61
+ const async = useMemo(() => new Async(), []);
62
+ useUnmount(() => {
63
+ async.clearAll();
64
+ });
65
+ return async;
66
+ }
package/useEvent.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { HasEventTargetAddRemove } from 'rxjs/internal/observable/fromEvent';
2
+ export declare function useEvent<E = Event>(target: React.RefObject<HasEventTargetAddRemove<E> | null>, eventName: keyof HTMLElementEventMap, callback?: (e: E) => void, options?: AddEventListenerOptions, disabled?: boolean): void;
package/useEvent.js ADDED
@@ -0,0 +1,16 @@
1
+ import { useEffect } from 'react';
2
+ import { fromEvent } from 'rxjs';
3
+ export function useEvent(target, eventName, callback, options, disabled = false) {
4
+ useEffect(() => {
5
+ if (target.current && !disabled) {
6
+ const ob = fromEvent(target.current, eventName, options).subscribe({
7
+ next: (e) => {
8
+ callback === null || callback === void 0 ? void 0 : callback(e);
9
+ },
10
+ });
11
+ return () => {
12
+ ob.unsubscribe();
13
+ };
14
+ }
15
+ });
16
+ }
@@ -0,0 +1 @@
1
+ export declare function useEventCallback<T extends (...args: any[]) => any>(fn: T): T;
@@ -0,0 +1,6 @@
1
+ import { useCallback, useRef } from 'react';
2
+ export function useEventCallback(fn) {
3
+ const ref = useRef(fn);
4
+ ref.current = fn;
5
+ return useCallback((...args) => ref.current(...args), []);
6
+ }
@@ -0,0 +1 @@
1
+ export declare function useForceUpdate(): import("react").DispatchWithoutAction;
@@ -0,0 +1,5 @@
1
+ import { useReducer } from 'react';
2
+ export function useForceUpdate() {
3
+ const [, forceUpdate] = useReducer((n) => n + 1, 0);
4
+ return forceUpdate;
5
+ }
@@ -0,0 +1 @@
1
+ export declare function useForkRef<T>(...refs: (React.ForwardedRef<T> | undefined)[]): React.RefCallback<T>;
package/useForkRef.js ADDED
@@ -0,0 +1,19 @@
1
+ import { isFunction, isObject } from 'lodash';
2
+ import { useCallback } from 'react';
3
+ function setRef(ref, value) {
4
+ if (isFunction(ref)) {
5
+ ref(value);
6
+ }
7
+ else if (isObject(ref) && 'current' in ref) {
8
+ ref['current'] = value;
9
+ }
10
+ }
11
+ export function useForkRef(...refs) {
12
+ return useCallback((refValue) => {
13
+ refs.forEach((ref) => {
14
+ setRef(ref, refValue);
15
+ });
16
+ },
17
+ // eslint-disable-next-line react-hooks/exhaustive-deps
18
+ [...refs]);
19
+ }
package/useImmer.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export type DraftFunction<S> = (draft: S) => void;
2
+ export type Updater<S> = (value: S | DraftFunction<S>) => void;
3
+ export type ImmerHook<S> = [S, Updater<S>];
4
+ export declare function useImmer<S>(): ImmerHook<S | undefined>;
5
+ export declare function useImmer<S = any>(initialValue: S | (() => S)): ImmerHook<S>;
package/useImmer.js ADDED
@@ -0,0 +1,13 @@
1
+ import { enableMapSet, freeze, produce } from 'immer';
2
+ import { useState, useCallback } from 'react';
3
+ enableMapSet();
4
+ export function useImmer(initialValue) {
5
+ const [value, updateValue] = useState(() => freeze(typeof initialValue === 'function' ? initialValue() : initialValue, true));
6
+ const setValue = useCallback((updater) => {
7
+ if (typeof updater === 'function')
8
+ updateValue(produce(updater));
9
+ else
10
+ updateValue(freeze(updater));
11
+ }, []);
12
+ return [value, setValue];
13
+ }
@@ -0,0 +1,2 @@
1
+ import { useEffect } from 'react';
2
+ export declare const useIsomorphicLayoutEffect: typeof useEffect;
@@ -0,0 +1,2 @@
1
+ import { useLayoutEffect, useEffect } from 'react';
2
+ export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
package/useMount.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function useMount(fn: () => any): void;
package/useMount.js ADDED
@@ -0,0 +1,5 @@
1
+ import { useEffect } from 'react';
2
+ export function useMount(fn) {
3
+ // eslint-disable-next-line react-hooks/exhaustive-deps
4
+ useEffect(() => fn(), []);
5
+ }
@@ -0,0 +1,4 @@
1
+ export type RefExtra<T = HTMLElement> = (() => T | null) | string;
2
+ export declare function useRefExtra(refExtra?: string): React.RefObject<HTMLElement>;
3
+ export declare function useRefExtra<T = HTMLElement>(refExtra?: () => T | null): React.RefObject<T>;
4
+ export declare function useRefExtra<T = HTMLElement>(refExtra?: RefExtra<T>): React.RefObject<T | HTMLElement>;
package/useRefExtra.js ADDED
@@ -0,0 +1,13 @@
1
+ import { isString, isUndefined } from 'lodash';
2
+ import { useRef } from 'react';
3
+ import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
4
+ export function useRefExtra(refExtra) {
5
+ const ref = useRef(null);
6
+ // eslint-disable-next-line react-hooks/exhaustive-deps
7
+ useIsomorphicLayoutEffect(() => {
8
+ if (!isUndefined(refExtra)) {
9
+ ref.current = isString(refExtra) ? document.querySelector(refExtra) : refExtra();
10
+ }
11
+ });
12
+ return ref;
13
+ }
package/useResize.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function useResize(target: React.RefObject<Element | null>, cb?: ResizeObserverCallback, options?: {
2
+ skipEmpty?: boolean;
3
+ }, disabled?: boolean): void;
package/useResize.js ADDED
@@ -0,0 +1,50 @@
1
+ import { isUndefined } from 'lodash';
2
+ import { useEffect, useRef } from 'react';
3
+ import { flushSync } from 'react-dom';
4
+ let checkResize = (cb1, cb2, entry) => {
5
+ if ('borderBoxSize' in entry) {
6
+ checkResize = (cb1) => {
7
+ cb1();
8
+ };
9
+ }
10
+ else {
11
+ checkResize = (cb1, cb2) => {
12
+ cb2();
13
+ };
14
+ }
15
+ };
16
+ export function useResize(target, cb, options, disabled = false) {
17
+ const { skipEmpty = true } = options !== null && options !== void 0 ? options : {};
18
+ const dataRef = useRef({});
19
+ if (disabled) {
20
+ dataRef.current.prevContentRect = undefined;
21
+ }
22
+ useEffect(() => {
23
+ if (target.current && !disabled) {
24
+ const observer = new ResizeObserver((entries, observer) => {
25
+ const entry = entries[0];
26
+ checkResize(() => {
27
+ if (!isUndefined(dataRef.current.prevContentRect) &&
28
+ !(skipEmpty && entry.borderBoxSize[0].blockSize === 0 && entry.borderBoxSize[0].inlineSize === 0) &&
29
+ (dataRef.current.prevContentRect.width !== entry.borderBoxSize[0].inlineSize ||
30
+ dataRef.current.prevContentRect.height !== entry.borderBoxSize[0].blockSize)) {
31
+ flushSync(() => cb === null || cb === void 0 ? void 0 : cb(entries, observer));
32
+ }
33
+ dataRef.current.prevContentRect = { width: entry.borderBoxSize[0].inlineSize, height: entry.borderBoxSize[0].blockSize };
34
+ }, () => {
35
+ if (!isUndefined(dataRef.current.prevContentRect) &&
36
+ !(skipEmpty && entry.contentRect.width === 0 && entry.contentRect.height === 0) &&
37
+ (dataRef.current.prevContentRect.width !== entry.contentRect.width ||
38
+ dataRef.current.prevContentRect.height !== entry.contentRect.height)) {
39
+ flushSync(() => cb === null || cb === void 0 ? void 0 : cb(entries, observer));
40
+ }
41
+ dataRef.current.prevContentRect = { width: entry.contentRect.width, height: entry.contentRect.height };
42
+ }, entry);
43
+ });
44
+ observer.observe(target.current);
45
+ return () => {
46
+ observer.disconnect();
47
+ };
48
+ }
49
+ });
50
+ }
@@ -0,0 +1 @@
1
+ export declare function useUnmount(fn: () => any): void;
package/useUnmount.js ADDED
@@ -0,0 +1,6 @@
1
+ import { useEffect, useRef } from 'react';
2
+ export function useUnmount(fn) {
3
+ const ref = useRef(fn);
4
+ ref.current = fn;
5
+ useEffect(() => () => ref.current(), []);
6
+ }