@boxcustodia/library 2.0.0-alpha.22 → 2.0.0-alpha.23
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/dist/components/calendar/calendar.cjs.js +1 -1
- package/dist/components/calendar/calendar.es.js +43 -44
- package/dist/components/date-picker/date-input.cjs.js +1 -1
- package/dist/components/date-picker/date-input.es.js +160 -140
- package/dist/components/pagination/pagination.cjs.js +1 -1
- package/dist/components/pagination/pagination.es.js +37 -35
- package/dist/components/scroll-area/scroll-area.cjs.js +1 -1
- package/dist/components/scroll-area/scroll-area.es.js +4 -4
- package/dist/components/select/select.cjs.js +1 -1
- package/dist/components/select/select.es.js +94 -90
- package/dist/hooks/use-action/use-action.cjs.js +1 -0
- package/dist/hooks/use-action/use-action.es.js +41 -0
- package/dist/hooks/use-pagination/use-pagination.cjs.js +1 -1
- package/dist/hooks/use-pagination/use-pagination.es.js +77 -32
- package/dist/hooks/use-range-pagination/use-range-pagination.cjs.js +1 -1
- package/dist/hooks/use-range-pagination/use-range-pagination.es.js +8 -5
- package/dist/hooks/use-selection/use-selection.cjs.js +1 -1
- package/dist/hooks/use-selection/use-selection.es.js +95 -33
- package/dist/hooks/use-session-storage/use-session-storage.cjs.js +1 -0
- package/dist/hooks/use-session-storage/use-session-storage.es.js +57 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +61 -63
- package/dist/src/components/select/select.d.ts +9 -2
- package/dist/src/hooks/index.d.ts +2 -3
- package/dist/src/hooks/internal/index.d.ts +1 -0
- package/dist/src/hooks/internal/serializer.d.ts +4 -0
- package/dist/src/hooks/use-action/index.d.ts +1 -0
- package/dist/src/hooks/use-action/use-action.d.ts +22 -0
- package/dist/src/hooks/use-local-storage/use-local-storage.d.ts +2 -4
- package/dist/src/hooks/use-pagination/use-pagination.d.ts +47 -32
- package/dist/src/hooks/use-range-pagination/use-range-pagination.d.ts +16 -10
- package/dist/src/hooks/use-selection/use-selection.d.ts +39 -45
- package/dist/src/hooks/use-session-storage/index.d.ts +1 -0
- package/dist/src/hooks/use-session-storage/use-session-storage.d.ts +11 -0
- package/package.json +1 -1
- package/src/components/calendar/calendar.tsx +10 -8
- package/src/components/combobox/combobox.stories.tsx +16 -0
- package/src/components/date-picker/date-input.tsx +23 -2
- package/src/components/form/form.tsx +3 -2
- package/src/components/pagination/pagination.tsx +5 -3
- package/src/components/scroll-area/scroll-area.tsx +2 -2
- package/src/components/select/select.tsx +14 -3
- package/src/hooks/index.ts +2 -3
- package/src/hooks/internal/index.ts +1 -0
- package/src/hooks/internal/serializer.ts +4 -0
- package/src/hooks/use-action/index.ts +1 -0
- package/src/hooks/{use-mutation/use-mutation.stories.tsx → use-action/use-action.stories.tsx} +34 -34
- package/src/hooks/{use-mutation/use-mutation.test.ts → use-action/use-action.test.ts} +53 -53
- package/src/hooks/{use-mutation/use-mutation.ts → use-action/use-action.ts} +20 -20
- package/src/hooks/use-click-outside/use-click-outside.stories.tsx +0 -1
- package/src/hooks/use-clipboard/use-clipboard.stories.tsx +0 -1
- package/src/hooks/use-document-title/use-document-title.stories.tsx +0 -1
- package/src/hooks/use-is-visible/use-is-visible.test.tsx +1 -1
- package/src/hooks/use-local-storage/use-local-storage.stories.tsx +0 -1
- package/src/hooks/use-local-storage/use-local-storage.ts +2 -5
- package/src/hooks/use-media-query/use-media-query.stories.tsx +0 -1
- package/src/hooks/use-pagination/use-pagination.stories.tsx +720 -57
- package/src/hooks/use-pagination/use-pagination.test.tsx +560 -48
- package/src/hooks/use-pagination/use-pagination.ts +266 -0
- package/src/hooks/use-prevent-page-close/use-prevent-page-close.stories.tsx +0 -1
- package/src/hooks/use-range-pagination/use-range-pagination.test.tsx +2 -2
- package/src/hooks/use-range-pagination/use-range-pagination.tsx +24 -21
- package/src/hooks/use-selection/use-selection.stories.tsx +339 -84
- package/src/hooks/use-selection/use-selection.test.tsx +417 -2
- package/src/hooks/use-selection/use-selection.ts +212 -102
- package/src/hooks/use-session-storage/index.ts +1 -0
- package/src/hooks/use-session-storage/use-session-storage.stories.tsx +122 -0
- package/src/hooks/use-session-storage/use-session-storage.test.ts +164 -0
- package/src/hooks/use-session-storage/use-session-storage.ts +115 -0
- package/dist/hooks/use-async/use-async.cjs.js +0 -1
- package/dist/hooks/use-async/use-async.es.js +0 -57
- package/dist/hooks/use-focus-trap/scope-tab.cjs.js +0 -1
- package/dist/hooks/use-focus-trap/scope-tab.es.js +0 -21
- package/dist/hooks/use-focus-trap/tabbable.cjs.js +0 -1
- package/dist/hooks/use-focus-trap/tabbable.es.js +0 -38
- package/dist/hooks/use-focus-trap/use-focus-trap.cjs.js +0 -1
- package/dist/hooks/use-focus-trap/use-focus-trap.es.js +0 -34
- package/dist/hooks/use-mutation/use-mutation.cjs.js +0 -1
- package/dist/hooks/use-mutation/use-mutation.es.js +0 -41
- package/dist/src/hooks/use-async/index.d.ts +0 -1
- package/dist/src/hooks/use-async/use-async.d.ts +0 -21
- package/dist/src/hooks/use-focus-trap/index.d.ts +0 -1
- package/dist/src/hooks/use-focus-trap/scope-tab.d.ts +0 -1
- package/dist/src/hooks/use-focus-trap/tabbable.d.ts +0 -4
- package/dist/src/hooks/use-focus-trap/use-focus-trap.d.ts +0 -1
- package/dist/src/hooks/use-mutation/index.d.ts +0 -1
- package/dist/src/hooks/use-mutation/use-mutation.d.ts +0 -22
- package/dist/src/hooks/use-mutation/use-mutation.test.d.ts +0 -1
- package/src/hooks/use-async/index.ts +0 -1
- package/src/hooks/use-async/use-async.stories.tsx +0 -272
- package/src/hooks/use-async/use-async.test.ts +0 -397
- package/src/hooks/use-async/use-async.ts +0 -135
- package/src/hooks/use-focus-trap/index.ts +0 -1
- package/src/hooks/use-focus-trap/scope-tab.ts +0 -38
- package/src/hooks/use-focus-trap/tabbable.ts +0 -70
- package/src/hooks/use-focus-trap/use-focus-trap.stories.tsx +0 -37
- package/src/hooks/use-focus-trap/use-focus-trap.test.ts +0 -355
- package/src/hooks/use-focus-trap/use-focus-trap.ts +0 -78
- package/src/hooks/use-mutation/index.ts +0 -1
- package/src/hooks/use-pagination/use-pagination.tsx +0 -84
- /package/dist/src/hooks/{use-async/use-async.test.d.ts → use-action/use-action.test.d.ts} +0 -0
- /package/dist/src/hooks/{use-focus-trap/use-focus-trap.test.d.ts → use-session-storage/use-session-storage.test.d.ts} +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useCallback, useMemo, useSyncExternalStore } from "react";
|
|
2
|
+
import { isBrowser, type Serializer } from "../internal";
|
|
3
|
+
|
|
4
|
+
export type { Serializer };
|
|
5
|
+
|
|
6
|
+
export interface UseSessionStorageOptions<T> {
|
|
7
|
+
serializer?: Serializer<T>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const defaultSerializer: Serializer<unknown> = {
|
|
11
|
+
read: (raw) => JSON.parse(raw) as unknown,
|
|
12
|
+
write: (value) => JSON.stringify(value),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type Listener = () => void;
|
|
16
|
+
const listeners = new Map<string, Set<Listener>>();
|
|
17
|
+
|
|
18
|
+
function notifyKey(key: string): void {
|
|
19
|
+
listeners.get(key)?.forEach((listener) => listener());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function subscribeKey(key: string, listener: Listener): () => void {
|
|
23
|
+
if (!isBrowser) return () => {};
|
|
24
|
+
|
|
25
|
+
let bucket = listeners.get(key);
|
|
26
|
+
if (!bucket) {
|
|
27
|
+
bucket = new Set();
|
|
28
|
+
listeners.set(key, bucket);
|
|
29
|
+
}
|
|
30
|
+
bucket.add(listener);
|
|
31
|
+
|
|
32
|
+
return () => {
|
|
33
|
+
bucket.delete(listener);
|
|
34
|
+
if (bucket.size === 0) listeners.delete(key);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function useSessionStorage<T>(
|
|
39
|
+
key: string,
|
|
40
|
+
): [
|
|
41
|
+
T | undefined,
|
|
42
|
+
(value: T | ((prev: T | undefined) => T)) => void,
|
|
43
|
+
() => void,
|
|
44
|
+
];
|
|
45
|
+
export function useSessionStorage<T>(
|
|
46
|
+
key: string,
|
|
47
|
+
initialValue: T,
|
|
48
|
+
options?: UseSessionStorageOptions<T>,
|
|
49
|
+
): [T, (value: T | ((prev: T) => T)) => void, () => void];
|
|
50
|
+
export function useSessionStorage<T>(
|
|
51
|
+
key: string,
|
|
52
|
+
initialValue?: T,
|
|
53
|
+
options: UseSessionStorageOptions<T> = {},
|
|
54
|
+
): [
|
|
55
|
+
T | undefined,
|
|
56
|
+
(value: T | ((prev: T | undefined) => T)) => void,
|
|
57
|
+
() => void,
|
|
58
|
+
] {
|
|
59
|
+
const serializer = (options.serializer ?? defaultSerializer) as Serializer<T>;
|
|
60
|
+
|
|
61
|
+
const subscribe = useCallback(
|
|
62
|
+
(listener: Listener) => subscribeKey(key, listener),
|
|
63
|
+
[key],
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const getSnapshot = useCallback((): string | null => {
|
|
67
|
+
if (!isBrowser) return null;
|
|
68
|
+
try {
|
|
69
|
+
return sessionStorage.getItem(key);
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}, [key]);
|
|
74
|
+
|
|
75
|
+
const getServerSnapshot = useCallback((): string | null => null, []);
|
|
76
|
+
|
|
77
|
+
const raw = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
78
|
+
|
|
79
|
+
const value = useMemo<T | undefined>(() => {
|
|
80
|
+
if (raw === null) return initialValue;
|
|
81
|
+
try {
|
|
82
|
+
return serializer.read(raw);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error(`Error parsing sessionStorage key "${key}":`, error);
|
|
85
|
+
return initialValue;
|
|
86
|
+
}
|
|
87
|
+
}, [raw, initialValue, key, serializer]);
|
|
88
|
+
|
|
89
|
+
const setValue = useCallback(
|
|
90
|
+
(next: T | ((prev: T | undefined) => T)) => {
|
|
91
|
+
try {
|
|
92
|
+
const resolved =
|
|
93
|
+
typeof next === "function"
|
|
94
|
+
? (next as (prev: T | undefined) => T)(value)
|
|
95
|
+
: next;
|
|
96
|
+
sessionStorage.setItem(key, serializer.write(resolved));
|
|
97
|
+
notifyKey(key);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(`Error setting sessionStorage key "${key}":`, error);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
[key, value, serializer],
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const remove = useCallback(() => {
|
|
106
|
+
try {
|
|
107
|
+
sessionStorage.removeItem(key);
|
|
108
|
+
notifyKey(key);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(`Error removing sessionStorage key "${key}":`, error);
|
|
111
|
+
}
|
|
112
|
+
}, [key]);
|
|
113
|
+
|
|
114
|
+
return [value, setValue, remove];
|
|
115
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react"),o=require("../internal/use-latest-ref.cjs.js");function m({fn:L,deps:h=[],enabled:l=!0,keepPreviousData:k=!1,onSuccess:p,onError:q,onSettled:w}){const[n,u]=t.useState(l?"pending":"idle"),[A,f]=t.useState(null),[C,i]=t.useState(null),d=o.useLatestRef(L),R=o.useLatestRef(p),S=o.useLatestRef(q),b=o.useLatestRef(w),c=t.useRef(0),[T,I]=t.useState(0),y=t.useCallback(async(e,s)=>{let E=null,g=null;try{const r=await d.current(e);if(s!==c.current||e.aborted)return;E=r,f(r),u("success"),R.current?.(r)}catch(r){if(e.aborted||s!==c.current)return;const a=r instanceof Error?r:new Error(String(r));g=a,i(a),u("error"),S.current?.(a)}finally{s===c.current&&!e.aborted&&b.current?.(E,g)}},[d,R,S,b]),j=t.useCallback(()=>{I(e=>e+1)},[]);return t.useEffect(()=>{if(!l){u("idle");return}c.current+=1;const e=c.current,s=new AbortController;return u("pending"),i(null),k||f(null),y(s.signal,e),()=>{s.abort()}},[...h,l,y,T]),{data:A,error:C,status:n,loading:n==="pending",isIdle:n==="idle",isSuccess:n==="success",isError:n==="error",refetch:j}}exports.useAsync=m;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { useState as o, useRef as L, useCallback as m, useEffect as j } from "react";
|
|
2
|
-
import { useLatestRef as u } from "../internal/use-latest-ref.es.js";
|
|
3
|
-
function B({
|
|
4
|
-
fn: y,
|
|
5
|
-
deps: g = [],
|
|
6
|
-
enabled: l = !0,
|
|
7
|
-
keepPreviousData: k = !1,
|
|
8
|
-
onSuccess: w,
|
|
9
|
-
onError: x,
|
|
10
|
-
onSettled: A
|
|
11
|
-
}) {
|
|
12
|
-
const [n, s] = o(
|
|
13
|
-
l ? "pending" : "idle"
|
|
14
|
-
), [C, i] = o(null), [I, a] = o(null), d = u(y), R = u(w), E = u(x), p = u(A), c = L(0), [T, q] = o(0), S = m(
|
|
15
|
-
async (r, t) => {
|
|
16
|
-
let b = null, h = null;
|
|
17
|
-
try {
|
|
18
|
-
const e = await d.current(r);
|
|
19
|
-
if (t !== c.current || r.aborted) return;
|
|
20
|
-
b = e, i(e), s("success"), R.current?.(e);
|
|
21
|
-
} catch (e) {
|
|
22
|
-
if (r.aborted || t !== c.current) return;
|
|
23
|
-
const f = e instanceof Error ? e : new Error(String(e));
|
|
24
|
-
h = f, a(f), s("error"), E.current?.(f);
|
|
25
|
-
} finally {
|
|
26
|
-
t === c.current && !r.aborted && p.current?.(b, h);
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
// All deps are refs — this callback is effectively stable
|
|
30
|
-
[d, R, E, p]
|
|
31
|
-
), D = m(() => {
|
|
32
|
-
q((r) => r + 1);
|
|
33
|
-
}, []);
|
|
34
|
-
return j(() => {
|
|
35
|
-
if (!l) {
|
|
36
|
-
s("idle");
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
c.current += 1;
|
|
40
|
-
const r = c.current, t = new AbortController();
|
|
41
|
-
return s("pending"), a(null), k || i(null), S(t.signal, r), () => {
|
|
42
|
-
t.abort();
|
|
43
|
-
};
|
|
44
|
-
}, [...g, l, S, T]), {
|
|
45
|
-
data: C,
|
|
46
|
-
error: I,
|
|
47
|
-
status: n,
|
|
48
|
-
loading: n === "pending",
|
|
49
|
-
isIdle: n === "idle",
|
|
50
|
-
isSuccess: n === "success",
|
|
51
|
-
isError: n === "error",
|
|
52
|
-
refetch: D
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
export {
|
|
56
|
-
B as useAsync
|
|
57
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./tabbable.cjs.js");function s(a,e){const t=r.findTabbableDescendants(a);if(!t.length){e.preventDefault();return}const o=t[e.shiftKey?0:t.length-1],i=a.getRootNode();let c=o===i.activeElement||a===i.activeElement;const n=i.activeElement;if(n.tagName==="INPUT"&&n.getAttribute("type")==="radio"&&(c=t.filter(b=>b.getAttribute("type")==="radio"&&b.getAttribute("name")===n.getAttribute("name")).includes(o)),!c)return;e.preventDefault();const l=t[e.shiftKey?t.length-1:0];l&&l.focus()}exports.scopeTab=s;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { findTabbableDescendants as b } from "./tabbable.es.js";
|
|
2
|
-
function g(a, e) {
|
|
3
|
-
const t = b(a);
|
|
4
|
-
if (!t.length) {
|
|
5
|
-
e.preventDefault();
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
const o = t[e.shiftKey ? 0 : t.length - 1], i = a.getRootNode();
|
|
9
|
-
let c = o === i.activeElement || a === i.activeElement;
|
|
10
|
-
const n = i.activeElement;
|
|
11
|
-
if (n.tagName === "INPUT" && n.getAttribute("type") === "radio" && (c = t.filter(
|
|
12
|
-
(r) => r.getAttribute("type") === "radio" && r.getAttribute("name") === n.getAttribute("name")
|
|
13
|
-
).includes(o)), !c)
|
|
14
|
-
return;
|
|
15
|
-
e.preventDefault();
|
|
16
|
-
const l = t[e.shiftKey ? t.length - 1 : 0];
|
|
17
|
-
l && l.focus();
|
|
18
|
-
}
|
|
19
|
-
export {
|
|
20
|
-
g as scopeTab
|
|
21
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=/input|select|textarea|button|object/,a="a, input, select, textarea, button, object, [tabindex]";function b(t){return process.env.NODE_ENV==="test"?!1:t.style.display==="none"}function d(t){if(t.getAttribute("aria-hidden")||t.getAttribute("hidden")||t.getAttribute("type")==="hidden")return!1;let e=t;for(;e&&!(e===document.body||e.nodeType===11);){if(b(e))return!1;e=e.parentNode}return!0}function r(t){let n=t.getAttribute("tabindex");return n===null&&(n=void 0),parseInt(n,10)}function i(t){const n=t.nodeName.toLowerCase(),e=!Number.isNaN(r(t));return(o.test(n)&&!t.disabled||t instanceof HTMLAnchorElement&&t.href||e)&&d(t)}function s(t){const n=r(t);return(Number.isNaN(n)||n>=0)&&i(t)}function u(t){return Array.from(t.querySelectorAll(a)).filter(s)}exports.FOCUS_SELECTOR=a;exports.findTabbableDescendants=u;exports.focusable=i;exports.tabbable=s;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const i = /input|select|textarea|button|object/, a = "a, input, select, textarea, button, object, [tabindex]";
|
|
2
|
-
function o(t) {
|
|
3
|
-
return process.env.NODE_ENV === "test" ? !1 : t.style.display === "none";
|
|
4
|
-
}
|
|
5
|
-
function s(t) {
|
|
6
|
-
if (t.getAttribute("aria-hidden") || t.getAttribute("hidden") || t.getAttribute("type") === "hidden")
|
|
7
|
-
return !1;
|
|
8
|
-
let e = t;
|
|
9
|
-
for (; e && !(e === document.body || e.nodeType === 11); ) {
|
|
10
|
-
if (o(e))
|
|
11
|
-
return !1;
|
|
12
|
-
e = e.parentNode;
|
|
13
|
-
}
|
|
14
|
-
return !0;
|
|
15
|
-
}
|
|
16
|
-
function r(t) {
|
|
17
|
-
let n = t.getAttribute("tabindex");
|
|
18
|
-
return n === null && (n = void 0), parseInt(n, 10);
|
|
19
|
-
}
|
|
20
|
-
function d(t) {
|
|
21
|
-
const n = t.nodeName.toLowerCase(), e = !Number.isNaN(r(t));
|
|
22
|
-
return /* @ts-expect-error function accepts any html element but if it is a button, it should not be disabled to trigger the condition */ (i.test(n) && !t.disabled || t instanceof HTMLAnchorElement && t.href || e) && s(t);
|
|
23
|
-
}
|
|
24
|
-
function u(t) {
|
|
25
|
-
const n = r(t);
|
|
26
|
-
return (Number.isNaN(n) || n >= 0) && d(t);
|
|
27
|
-
}
|
|
28
|
-
function c(t) {
|
|
29
|
-
return Array.from(
|
|
30
|
-
t.querySelectorAll(a)
|
|
31
|
-
).filter(u);
|
|
32
|
-
}
|
|
33
|
-
export {
|
|
34
|
-
a as FOCUS_SELECTOR,
|
|
35
|
-
c as findTabbableDescendants,
|
|
36
|
-
d as focusable,
|
|
37
|
-
u as tabbable
|
|
38
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("react"),f=require("./scope-tab.cjs.js"),n=require("./tabbable.cjs.js");function i(u=!0){const t=c.useRef(null),o=e=>{let r=e.querySelector("[data-autofocus]");if(!r){const s=Array.from(e.querySelectorAll(n.FOCUS_SELECTOR));r=s.find(n.tabbable)||s.find(n.focusable)||null,!r&&n.focusable(e)&&(r=e)}r&&r.focus({preventScroll:!0})},l=c.useCallback(e=>{u&&e!==null&&t.current!==e&&(e?(setTimeout(()=>{e.getRootNode()&&o(e)}),t.current=e):t.current=null)},[u]);return c.useEffect(()=>{if(!u)return;t.current&&setTimeout(()=>o(t.current));const e=r=>{r.key==="Tab"&&t.current&&f.scopeTab(t.current,r)};return document.addEventListener("keydown",e),()=>document.removeEventListener("keydown",e)},[u]),l}exports.useFocusTrap=i;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { useRef as l, useCallback as s, useEffect as i } from "react";
|
|
2
|
-
import { scopeTab as m } from "./scope-tab.es.js";
|
|
3
|
-
import { FOCUS_SELECTOR as a, tabbable as b, focusable as f } from "./tabbable.es.js";
|
|
4
|
-
function E(u = !0) {
|
|
5
|
-
const t = l(null), n = (e) => {
|
|
6
|
-
let r = e.querySelector("[data-autofocus]");
|
|
7
|
-
if (!r) {
|
|
8
|
-
const o = Array.from(
|
|
9
|
-
e.querySelectorAll(a)
|
|
10
|
-
);
|
|
11
|
-
r = o.find(b) || o.find(f) || null, !r && f(e) && (r = e);
|
|
12
|
-
}
|
|
13
|
-
r && r.focus({ preventScroll: !0 });
|
|
14
|
-
}, c = s(
|
|
15
|
-
(e) => {
|
|
16
|
-
u && e !== null && t.current !== e && (e ? (setTimeout(() => {
|
|
17
|
-
e.getRootNode() && n(e);
|
|
18
|
-
}), t.current = e) : t.current = null);
|
|
19
|
-
},
|
|
20
|
-
[u]
|
|
21
|
-
);
|
|
22
|
-
return i(() => {
|
|
23
|
-
if (!u)
|
|
24
|
-
return;
|
|
25
|
-
t.current && setTimeout(() => n(t.current));
|
|
26
|
-
const e = (r) => {
|
|
27
|
-
r.key === "Tab" && t.current && m(t.current, r);
|
|
28
|
-
};
|
|
29
|
-
return document.addEventListener("keydown", e), () => document.removeEventListener("keydown", e);
|
|
30
|
-
}, [u]), c;
|
|
31
|
-
}
|
|
32
|
-
export {
|
|
33
|
-
E as useFocusTrap
|
|
34
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react"),c=require("../internal/use-latest-ref.cjs.js"),k=s=>{const[u,l]=t.useState("idle"),[y,a]=t.useState(null),[L,o]=t.useState(null),d=c.useLatestRef(s.fn),R=c.useLatestRef(s.onMutate),i=c.useLatestRef(s.onSuccess),S=c.useLatestRef(s.onError),g=c.useLatestRef(s.onSettled),f=t.useCallback(async e=>{R.current?.(e),l("pending"),a(null),o(null);let n=null,r=null;try{return n=await d.current(e),a(n),l("success"),i.current?.(n,e),n}catch(E){throw r=E,o(r),l("error"),S.current?.(r,e),r}finally{g.current?.(n,r,e)}},[d,R,i,S,g]),M=t.useCallback(e=>{f(e).catch(()=>{})},[f]),h=t.useCallback(()=>{l("idle"),a(null),o(null)},[]);return{mutate:M,mutateAsync:f,reset:h,data:y,error:L,status:u,isIdle:u==="idle",isPending:u==="pending",isSuccess:u==="success",isError:u==="error"}};exports.useMutation=k;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { useState as f, useCallback as d } from "react";
|
|
2
|
-
import { useLatestRef as s } from "../internal/use-latest-ref.es.js";
|
|
3
|
-
const x = (e) => {
|
|
4
|
-
const [n, u] = f("idle"), [h, o] = f(null), [y, l] = f(null), i = s(e.fn), S = s(e.onMutate), m = s(e.onSuccess), R = s(e.onError), g = s(e.onSettled), a = d(
|
|
5
|
-
async (t) => {
|
|
6
|
-
S.current?.(t), u("pending"), o(null), l(null);
|
|
7
|
-
let r = null, c = null;
|
|
8
|
-
try {
|
|
9
|
-
return r = await i.current(t), o(r), u("success"), m.current?.(r, t), r;
|
|
10
|
-
} catch (w) {
|
|
11
|
-
throw c = w, l(c), u("error"), R.current?.(c, t), c;
|
|
12
|
-
} finally {
|
|
13
|
-
g.current?.(r, c, t);
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
[i, S, m, R, g]
|
|
17
|
-
), E = d(
|
|
18
|
-
(t) => {
|
|
19
|
-
a(t).catch(() => {
|
|
20
|
-
});
|
|
21
|
-
},
|
|
22
|
-
[a]
|
|
23
|
-
), M = d(() => {
|
|
24
|
-
u("idle"), o(null), l(null);
|
|
25
|
-
}, []);
|
|
26
|
-
return {
|
|
27
|
-
mutate: E,
|
|
28
|
-
mutateAsync: a,
|
|
29
|
-
reset: M,
|
|
30
|
-
data: h,
|
|
31
|
-
error: y,
|
|
32
|
-
status: n,
|
|
33
|
-
isIdle: n === "idle",
|
|
34
|
-
isPending: n === "pending",
|
|
35
|
-
isSuccess: n === "success",
|
|
36
|
-
isError: n === "error"
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
export {
|
|
40
|
-
x as useMutation
|
|
41
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './use-async';
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export type AsyncStatus = "idle" | "pending" | "success" | "error";
|
|
2
|
-
export interface UseAsyncOptions<T> {
|
|
3
|
-
fn: (signal: AbortSignal) => Promise<T>;
|
|
4
|
-
deps?: readonly unknown[];
|
|
5
|
-
enabled?: boolean;
|
|
6
|
-
keepPreviousData?: boolean;
|
|
7
|
-
onSuccess?: (data: T) => void;
|
|
8
|
-
onError?: (error: Error) => void;
|
|
9
|
-
onSettled?: (data: T | null, error: Error | null) => void;
|
|
10
|
-
}
|
|
11
|
-
export interface UseAsyncReturn<T> {
|
|
12
|
-
data: T | null;
|
|
13
|
-
error: Error | null;
|
|
14
|
-
status: AsyncStatus;
|
|
15
|
-
loading: boolean;
|
|
16
|
-
isIdle: boolean;
|
|
17
|
-
isSuccess: boolean;
|
|
18
|
-
isError: boolean;
|
|
19
|
-
refetch: () => void;
|
|
20
|
-
}
|
|
21
|
-
export declare function useAsync<T>({ fn, deps, enabled, keepPreviousData, onSuccess, onError, onSettled, }: UseAsyncOptions<T>): UseAsyncReturn<T>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './use-focus-trap';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function scopeTab(node: HTMLElement, event: KeyboardEvent): void;
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare const FOCUS_SELECTOR = "a, input, select, textarea, button, object, [tabindex]";
|
|
2
|
-
export declare function focusable(element: HTMLElement): boolean | "";
|
|
3
|
-
export declare function tabbable(element: HTMLElement): boolean | "";
|
|
4
|
-
export declare function findTabbableDescendants(element: HTMLElement): HTMLElement[];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function useFocusTrap(active?: boolean): (instance: HTMLElement | null) => void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './use-mutation';
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
type MutationStatus = "idle" | "pending" | "success" | "error";
|
|
2
|
-
export interface UseMutationOptions<TVariables = void, TData = unknown> {
|
|
3
|
-
fn: (variables: TVariables) => Promise<TData>;
|
|
4
|
-
onMutate?: (variables: TVariables) => void;
|
|
5
|
-
onSuccess?: (data: TData, variables: TVariables) => void;
|
|
6
|
-
onError?: (error: Error, variables: TVariables) => void;
|
|
7
|
-
onSettled?: (data: TData | null, error: Error | null, variables: TVariables) => void;
|
|
8
|
-
}
|
|
9
|
-
export interface UseMutationReturn<TVariables = void, TData = unknown> {
|
|
10
|
-
mutate: (variables: TVariables) => void;
|
|
11
|
-
mutateAsync: (variables: TVariables) => Promise<TData>;
|
|
12
|
-
reset: () => void;
|
|
13
|
-
data: TData | null;
|
|
14
|
-
error: Error | null;
|
|
15
|
-
status: MutationStatus;
|
|
16
|
-
isIdle: boolean;
|
|
17
|
-
isPending: boolean;
|
|
18
|
-
isSuccess: boolean;
|
|
19
|
-
isError: boolean;
|
|
20
|
-
}
|
|
21
|
-
export declare const useMutation: <TVariables = void, TData = unknown>(options: UseMutationOptions<TVariables, TData>) => UseMutationReturn<TVariables, TData>;
|
|
22
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./use-async";
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import { Button } from "../../components";
|
|
4
|
-
import { useAsync } from "./use-async";
|
|
5
|
-
|
|
6
|
-
// ─── Types ─────────────────────────────────────────────────────────────────────
|
|
7
|
-
|
|
8
|
-
interface DemoData {
|
|
9
|
-
message: string;
|
|
10
|
-
timestamp: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// ─── Demo component ────────────────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
type AsyncDemoProps = {
|
|
16
|
-
/** Simulated async delay in ms */
|
|
17
|
-
delay?: number;
|
|
18
|
-
/** When true the demo's fn rejects with a simulated error */
|
|
19
|
-
shouldFail?: boolean;
|
|
20
|
-
enabled?: boolean;
|
|
21
|
-
keepPreviousData?: boolean;
|
|
22
|
-
onSuccess?: (data: DemoData) => void;
|
|
23
|
-
onError?: (error: Error) => void;
|
|
24
|
-
onSettled?: (data: DemoData | null, error: Error | null) => void;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const AsyncDemo = ({
|
|
28
|
-
delay = 800,
|
|
29
|
-
shouldFail = false,
|
|
30
|
-
enabled = true,
|
|
31
|
-
keepPreviousData = false,
|
|
32
|
-
onSuccess,
|
|
33
|
-
onError,
|
|
34
|
-
onSettled,
|
|
35
|
-
}: AsyncDemoProps) => {
|
|
36
|
-
const [log, setLog] = useState<string[]>([]);
|
|
37
|
-
|
|
38
|
-
const addLog = (msg: string) =>
|
|
39
|
-
setLog((prev) => [
|
|
40
|
-
`[${new Date().toISOString().slice(11, 23)}] ${msg}`,
|
|
41
|
-
...prev,
|
|
42
|
-
]);
|
|
43
|
-
|
|
44
|
-
const { status, data, error, loading, refetch } = useAsync<DemoData>({
|
|
45
|
-
fn: async (signal) => {
|
|
46
|
-
await new Promise<void>((res, rej) => {
|
|
47
|
-
const timer = setTimeout(() => {
|
|
48
|
-
if (shouldFail) rej(new Error("Simulated failure"));
|
|
49
|
-
else res();
|
|
50
|
-
}, delay);
|
|
51
|
-
signal.addEventListener("abort", () => clearTimeout(timer));
|
|
52
|
-
});
|
|
53
|
-
return {
|
|
54
|
-
message: "Data loaded successfully",
|
|
55
|
-
timestamp: new Date().toLocaleTimeString(),
|
|
56
|
-
};
|
|
57
|
-
},
|
|
58
|
-
enabled,
|
|
59
|
-
keepPreviousData,
|
|
60
|
-
onSuccess: (d) => {
|
|
61
|
-
addLog(`onSuccess — ${d.message} at ${d.timestamp}`);
|
|
62
|
-
onSuccess?.(d);
|
|
63
|
-
},
|
|
64
|
-
onError: (e) => {
|
|
65
|
-
addLog(`onError — ${e.message}`);
|
|
66
|
-
onError?.(e);
|
|
67
|
-
},
|
|
68
|
-
onSettled: (d, e) => {
|
|
69
|
-
addLog(
|
|
70
|
-
`onSettled — data: ${d ? d.timestamp : "null"}, error: ${e?.message ?? "null"}`,
|
|
71
|
-
);
|
|
72
|
-
onSettled?.(d, e);
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<div className="flex flex-col gap-4 max-w-sm">
|
|
78
|
-
<Button variant="outline" onClick={refetch} disabled={loading}>
|
|
79
|
-
{loading ? "Loading…" : "Refetch"}
|
|
80
|
-
</Button>
|
|
81
|
-
|
|
82
|
-
<pre className="rounded-md bg-slate-950 p-3 text-xs text-white leading-relaxed">
|
|
83
|
-
{JSON.stringify(
|
|
84
|
-
{
|
|
85
|
-
status,
|
|
86
|
-
loading,
|
|
87
|
-
data,
|
|
88
|
-
error: error?.message ?? null,
|
|
89
|
-
},
|
|
90
|
-
null,
|
|
91
|
-
2,
|
|
92
|
-
)}
|
|
93
|
-
</pre>
|
|
94
|
-
|
|
95
|
-
{log.length > 0 && (
|
|
96
|
-
<div className="rounded-md border bg-muted p-3 text-xs font-mono space-y-0.5">
|
|
97
|
-
{log.map((entry, i) => (
|
|
98
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: log is append-only
|
|
99
|
-
<div key={i}>{entry}</div>
|
|
100
|
-
))}
|
|
101
|
-
</div>
|
|
102
|
-
)}
|
|
103
|
-
</div>
|
|
104
|
-
);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// ─── Meta ─────────────────────────────────────────────────────────────────────
|
|
108
|
-
|
|
109
|
-
const meta: Meta<typeof AsyncDemo> = {
|
|
110
|
-
title: "hooks/useAsync",
|
|
111
|
-
component: AsyncDemo,
|
|
112
|
-
parameters: {
|
|
113
|
-
docs: {
|
|
114
|
-
description: {
|
|
115
|
-
component: `
|
|
116
|
-
\`useAsync\` runs an async function automatically when its \`deps\` change.
|
|
117
|
-
|
|
118
|
-
**API summary**
|
|
119
|
-
|
|
120
|
-
\`\`\`ts
|
|
121
|
-
const { data, error, status, loading, isIdle, isSuccess, isError, refetch } =
|
|
122
|
-
useAsync({ fn, deps?, enabled?, keepPreviousData?, onSuccess?, onError?, onSettled? });
|
|
123
|
-
\`\`\`
|
|
124
|
-
|
|
125
|
-
**Key behaviors**
|
|
126
|
-
- \`fn(signal)\` receives an \`AbortSignal\` — pass it to \`fetch\` to cancel in-flight requests on cleanup.
|
|
127
|
-
- Runs on mount (when \`enabled: true\`) and re-runs whenever \`deps\` change.
|
|
128
|
-
- Race-safe: if \`deps\` change before \`fn\` resolves, the stale response is silently discarded.
|
|
129
|
-
- \`enabled: false\` keeps the hook in \`'idle'\` state — no request fires until \`enabled\` becomes \`true\`.
|
|
130
|
-
- \`keepPreviousData: true\` retains the last resolved \`data\` while a new request is in-flight.
|
|
131
|
-
- \`refetch()\` manually re-triggers without changing deps. Stable reference across renders.
|
|
132
|
-
- All callbacks read through \`useLatestRef\` — inline functions never cause re-subscriptions.
|
|
133
|
-
`.trim(),
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
argTypes: {
|
|
138
|
-
delay: {
|
|
139
|
-
control: { type: "range", min: 0, max: 3000, step: 100 },
|
|
140
|
-
description:
|
|
141
|
-
"Simulated async delay in ms. Demo control only — not a hook option.",
|
|
142
|
-
table: { category: "Demo controls" },
|
|
143
|
-
},
|
|
144
|
-
shouldFail: {
|
|
145
|
-
control: "boolean",
|
|
146
|
-
description:
|
|
147
|
-
"When true the demo's fn rejects. Toggle then click Refetch to see the error state.",
|
|
148
|
-
table: { category: "Demo controls" },
|
|
149
|
-
},
|
|
150
|
-
enabled: {
|
|
151
|
-
control: "boolean",
|
|
152
|
-
description:
|
|
153
|
-
"When false the hook stays idle and no request fires. Set to true to trigger the first run.",
|
|
154
|
-
table: {
|
|
155
|
-
category: "Hook options",
|
|
156
|
-
type: { summary: "boolean" },
|
|
157
|
-
defaultValue: { summary: "true" },
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
keepPreviousData: {
|
|
161
|
-
control: "boolean",
|
|
162
|
-
description:
|
|
163
|
-
"When true, `data` is preserved while a new request is in-flight instead of being reset to null.",
|
|
164
|
-
table: {
|
|
165
|
-
category: "Hook options",
|
|
166
|
-
type: { summary: "boolean" },
|
|
167
|
-
defaultValue: { summary: "false" },
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
onSuccess: {
|
|
171
|
-
action: "onSuccess",
|
|
172
|
-
description: "Called with `data` after `fn` resolves successfully.",
|
|
173
|
-
table: {
|
|
174
|
-
category: "Hook options",
|
|
175
|
-
type: { summary: "(data: T) => void" },
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
onError: {
|
|
179
|
-
action: "onError",
|
|
180
|
-
description:
|
|
181
|
-
"Called with the thrown `Error` when `fn` rejects. Abort errors are swallowed and never reach this callback.",
|
|
182
|
-
table: {
|
|
183
|
-
category: "Hook options",
|
|
184
|
-
type: { summary: "(error: Error) => void" },
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
onSettled: {
|
|
188
|
-
action: "onSettled",
|
|
189
|
-
description:
|
|
190
|
-
"Always called after `fn` settles — regardless of success or failure.",
|
|
191
|
-
table: {
|
|
192
|
-
category: "Hook options",
|
|
193
|
-
type: { summary: "(data: T | null, error: Error | null) => void" },
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
render: (args) => <AsyncDemo {...args} />,
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
export default meta;
|
|
201
|
-
type Story = StoryObj<typeof AsyncDemo>;
|
|
202
|
-
|
|
203
|
-
// ─── Stories ──────────────────────────────────────────────────────────────────
|
|
204
|
-
|
|
205
|
-
export const Default: Story = {
|
|
206
|
-
args: {
|
|
207
|
-
delay: 800,
|
|
208
|
-
shouldFail: false,
|
|
209
|
-
enabled: true,
|
|
210
|
-
keepPreviousData: false,
|
|
211
|
-
},
|
|
212
|
-
parameters: {
|
|
213
|
-
docs: {
|
|
214
|
-
description: {
|
|
215
|
-
story:
|
|
216
|
-
"Runs on mount, transitions through `pending` → `success`, then populates `data`. Click **Refetch** to re-trigger manually. Toggle **shouldFail** in controls then refetch to see the error path.",
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
export const ErrorState: Story = {
|
|
223
|
-
name: "Error",
|
|
224
|
-
args: {
|
|
225
|
-
delay: 600,
|
|
226
|
-
shouldFail: true,
|
|
227
|
-
enabled: true,
|
|
228
|
-
keepPreviousData: false,
|
|
229
|
-
},
|
|
230
|
-
parameters: {
|
|
231
|
-
docs: {
|
|
232
|
-
description: {
|
|
233
|
-
story:
|
|
234
|
-
"`fn` rejects immediately — `status` becomes `'error'`, `error` is populated, and `data` stays null. `onError` and `onSettled` both fire. Toggle **shouldFail** off then click Refetch to recover.",
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
export const WithEnabled: Story = {
|
|
241
|
-
args: {
|
|
242
|
-
delay: 800,
|
|
243
|
-
shouldFail: false,
|
|
244
|
-
enabled: false,
|
|
245
|
-
keepPreviousData: false,
|
|
246
|
-
},
|
|
247
|
-
parameters: {
|
|
248
|
-
docs: {
|
|
249
|
-
description: {
|
|
250
|
-
story:
|
|
251
|
-
"Starts with `enabled: false` — the hook is in `'idle'` state and no request fires. Flip **enabled** to true in controls to trigger the first run.",
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
export const WithKeepPreviousData: Story = {
|
|
258
|
-
args: {
|
|
259
|
-
delay: 1500,
|
|
260
|
-
shouldFail: false,
|
|
261
|
-
enabled: true,
|
|
262
|
-
keepPreviousData: true,
|
|
263
|
-
},
|
|
264
|
-
parameters: {
|
|
265
|
-
docs: {
|
|
266
|
-
description: {
|
|
267
|
-
story:
|
|
268
|
-
"With `keepPreviousData: true`, the previous `data` stays visible while the next request is in-flight (`status: 'pending'`). Click **Refetch** to see stale data persist during the 1.5 s delay.",
|
|
269
|
-
},
|
|
270
|
-
},
|
|
271
|
-
},
|
|
272
|
-
};
|