seitu 0.0.1 → 0.1.0
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/README.md +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/store.d.ts +107 -0
- package/dist/core/store.test.d.ts +1 -0
- package/dist/core/subscription.d.ts +7 -9
- package/dist/core-CuCUF5Aj.js +48 -0
- package/dist/core.js +2 -14
- package/dist/react/hooks.d.ts +8 -28
- package/dist/react/hooks.test.d.ts +1 -0
- package/dist/react.js +8 -5
- package/dist/utils.d.ts +1 -1
- package/dist/web/index.d.ts +2 -0
- package/dist/web/media-query.d.ts +75 -0
- package/dist/web/media-query.test.d.ts +1 -0
- package/dist/web/scroll-state.d.ts +70 -0
- package/dist/web/scroll-state.test.d.ts +1 -0
- package/dist/web/session-storage-value.d.ts +4 -5
- package/dist/web/session-storage-value.test.d.ts +1 -0
- package/dist/web/session-storage.d.ts +3 -49
- package/dist/web/session-storage.test.d.ts +1 -0
- package/dist/web.js +107 -63
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/core/index.d.ts
CHANGED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
import type { Simplify } from '../utils';
|
|
3
|
+
import type { Readable, Subscribable, Writable } from './index';
|
|
4
|
+
export type StoreSchema = Record<string, StandardSchemaV1<unknown, unknown>>;
|
|
5
|
+
export type StoreOutput<S extends StoreSchema> = Simplify<{
|
|
6
|
+
[K in keyof S]: StandardSchemaV1.InferOutput<S[K]>;
|
|
7
|
+
}>;
|
|
8
|
+
export interface Store<O extends Record<string, unknown>> extends Subscribable<O>, Readable<O>, Writable<O> {
|
|
9
|
+
getDefaultValue: <K extends keyof O>(key: K) => O[K];
|
|
10
|
+
}
|
|
11
|
+
export interface StoreOptions<S extends Record<string, StandardSchemaV1>> {
|
|
12
|
+
/**
|
|
13
|
+
* Schemas for each storage key (one validator per key).
|
|
14
|
+
* Use this when each key has its own type; for a single compound schema use a wrapper with one key.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const store = createStore({
|
|
19
|
+
* schemas: {
|
|
20
|
+
* token: z.string().nullable(),
|
|
21
|
+
* preferences: z.object({ theme: z.enum(['light', 'dark']) }),
|
|
22
|
+
* },
|
|
23
|
+
* defaultValues: { token: null, preferences: { theme: 'light' } },
|
|
24
|
+
* provider: {
|
|
25
|
+
* get: () => ({
|
|
26
|
+
* token: window.localStorage.getItem('token'),
|
|
27
|
+
* preferences: window.localStorage.getItem('preferences'),
|
|
28
|
+
* }),
|
|
29
|
+
* set: (value) => {
|
|
30
|
+
* window.localStorage.setItem('token', value.token ?? '')
|
|
31
|
+
* window.localStorage.setItem('preferences', JSON.stringify(value.preferences))
|
|
32
|
+
* },
|
|
33
|
+
* },
|
|
34
|
+
* })
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
schemas: S;
|
|
38
|
+
/**
|
|
39
|
+
* The default values to use for the storage.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const store = createStore({
|
|
44
|
+
* schemas: {
|
|
45
|
+
* token: z.string().nullable(),
|
|
46
|
+
* preferences: z.object({ theme: z.enum(['light', 'dark']) }),
|
|
47
|
+
* },
|
|
48
|
+
* defaultValues: {
|
|
49
|
+
* token: null,
|
|
50
|
+
* preferences: { theme: 'light' },
|
|
51
|
+
* },
|
|
52
|
+
* provider: {
|
|
53
|
+
* get: () => ({
|
|
54
|
+
* token: window.localStorage.getItem('token'),
|
|
55
|
+
* preferences: window.localStorage.getItem('preferences'),
|
|
56
|
+
* }),
|
|
57
|
+
* set: (value) => {
|
|
58
|
+
* window.localStorage.setItem('token', value.token ?? '')
|
|
59
|
+
* window.localStorage.setItem('preferences', JSON.stringify(value.preferences))
|
|
60
|
+
* },
|
|
61
|
+
* },
|
|
62
|
+
* })
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
defaultValues: StoreOutput<S>;
|
|
66
|
+
provider: {
|
|
67
|
+
get: () => StoreOutput<S>;
|
|
68
|
+
set: (value: StoreOutput<S>) => void;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Creates a reactive handle for a storage instance.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts twoslash
|
|
76
|
+
* import { createStore } from 'seitu'
|
|
77
|
+
* import * as z from 'zod'
|
|
78
|
+
*
|
|
79
|
+
* const store = createStore({
|
|
80
|
+
* schemas: {
|
|
81
|
+
* token: z.string().nullable(),
|
|
82
|
+
* preferences: z.object({ theme: z.enum(['light', 'dark']) }),
|
|
83
|
+
* },
|
|
84
|
+
* defaultValues: { token: null, preferences: { theme: 'light' } },
|
|
85
|
+
* provider: {
|
|
86
|
+
* get: () => ({
|
|
87
|
+
* token: window.localStorage.getItem('token'),
|
|
88
|
+
* preferences: JSON.parse(window.localStorage.getItem('preferences') ?? '{"theme":"light"}'),
|
|
89
|
+
* }),
|
|
90
|
+
* set: (value) => {
|
|
91
|
+
* window.localStorage.setItem('token', value.token ?? '')
|
|
92
|
+
* window.localStorage.setItem('preferences', JSON.stringify(value.preferences))
|
|
93
|
+
* },
|
|
94
|
+
* },
|
|
95
|
+
* })
|
|
96
|
+
*
|
|
97
|
+
* store.get().token // null
|
|
98
|
+
* store.get().preferences // { theme: 'light' }
|
|
99
|
+
* store.set({ token: '123', preferences: { theme: 'dark' } })
|
|
100
|
+
* store.get().token // '123'
|
|
101
|
+
* store.get().preferences // { theme: 'dark' }
|
|
102
|
+
* store.subscribe(value => console.log(value))
|
|
103
|
+
* store.set({ token: '456', preferences: { theme: 'light' } })
|
|
104
|
+
* // { token: '456', preferences: { theme: 'light' } }
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare function createStore<S extends Record<string, StandardSchemaV1>>(options: StoreOptions<S>): Store<StoreOutput<S>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
export interface Subscribable<V> {
|
|
2
2
|
'subscribe': (callback: (value: V) => any) => () => void;
|
|
3
|
-
'~
|
|
3
|
+
'~': {
|
|
4
4
|
output: V;
|
|
5
|
+
notify: () => void;
|
|
5
6
|
};
|
|
6
7
|
}
|
|
7
8
|
export interface Readable<T> {
|
|
8
9
|
get: () => T;
|
|
9
10
|
}
|
|
10
|
-
export interface Writable<T
|
|
11
|
-
set: (value: T | ((prev:
|
|
11
|
+
export interface Writable<T> {
|
|
12
|
+
set: (value: T | ((prev: T) => T)) => any;
|
|
12
13
|
}
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export declare function createSubscription<V>(): {
|
|
17
|
-
subscribe: (callback: (value: V) => any) => () => void;
|
|
18
|
-
notify: (value: V) => void;
|
|
14
|
+
export declare function createSubscription<T = void>(): {
|
|
15
|
+
subscribe: (callback: (payload: T) => any) => () => void;
|
|
16
|
+
notify: (payload: T) => void;
|
|
19
17
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
function e(e) {
|
|
2
|
+
if (typeof e != "string") return e;
|
|
3
|
+
try {
|
|
4
|
+
return JSON.parse(e);
|
|
5
|
+
} catch {
|
|
6
|
+
return e;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function t(t) {
|
|
10
|
+
let { subscribe: r, notify: i } = n(), a = { ...t.defaultValues }, o = () => {
|
|
11
|
+
let n = { ...a };
|
|
12
|
+
for (let [r, i] of Object.entries(t.schemas)) {
|
|
13
|
+
let o = t.provider.get()[r], s = i["~standard"].validate(e(o));
|
|
14
|
+
if (s instanceof Promise) throw TypeError("[createStorage] Validation schema should not return a Promise.");
|
|
15
|
+
s.issues && console.warn(JSON.stringify(s.issues, null, 2), { cause: s.issues }), n[r] = s.issues ? a[r] : s.value;
|
|
16
|
+
}
|
|
17
|
+
return n;
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
get: o,
|
|
21
|
+
set: (e) => {
|
|
22
|
+
let n = typeof e == "function" ? e(o()) : e;
|
|
23
|
+
t.provider.set(n), i();
|
|
24
|
+
},
|
|
25
|
+
getDefaultValue: (e) => a[e],
|
|
26
|
+
subscribe: (e) => r(() => {
|
|
27
|
+
e(o());
|
|
28
|
+
}),
|
|
29
|
+
"~": {
|
|
30
|
+
output: null,
|
|
31
|
+
notify: i
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function n() {
|
|
36
|
+
let e = new Set();
|
|
37
|
+
return {
|
|
38
|
+
subscribe(t) {
|
|
39
|
+
return e.add(t), () => {
|
|
40
|
+
e.delete(t);
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
notify(t) {
|
|
44
|
+
e.forEach((e) => e(t));
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export { t as n, e as r, n as t };
|
package/dist/core.js
CHANGED
|
@@ -1,14 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return {
|
|
4
|
-
subscribe(t) {
|
|
5
|
-
return e.add(t), () => {
|
|
6
|
-
e.delete(t);
|
|
7
|
-
};
|
|
8
|
-
},
|
|
9
|
-
notify(t) {
|
|
10
|
-
e.forEach((e) => e(t));
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
export { e as createSubscription };
|
|
1
|
+
import { n as e, t } from "./core-CuCUF5Aj.js";
|
|
2
|
+
export { e as createStore, t as createSubscription };
|
package/dist/react/hooks.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { Readable, Subscribable } from '../core/
|
|
1
|
+
import type { Readable, Subscribable } from '../core/index';
|
|
2
2
|
/**
|
|
3
|
-
* Use this hook to
|
|
3
|
+
* Use this hook to subscribe to a reactive value. The factory function is called only
|
|
4
|
+
* once on first render — subsequent renders reuse the cached subscription.
|
|
4
5
|
*
|
|
5
6
|
* @kind hook
|
|
6
7
|
*
|
|
@@ -13,7 +14,7 @@ import type { Readable, Subscribable } from '../core/subscription';
|
|
|
13
14
|
* import * as z from 'zod'
|
|
14
15
|
*
|
|
15
16
|
* export default function Page() {
|
|
16
|
-
* const value = useSubscription(sessionStorageValue({
|
|
17
|
+
* const value = useSubscription(() => sessionStorageValue({
|
|
17
18
|
* key: 'test',
|
|
18
19
|
* defaultValue: 0,
|
|
19
20
|
* schema: z.number(),
|
|
@@ -27,28 +28,7 @@ import type { Readable, Subscribable } from '../core/subscription';
|
|
|
27
28
|
* ```tsx twoslash title="/app/page.tsx"
|
|
28
29
|
* 'use client'
|
|
29
30
|
*
|
|
30
|
-
* import {
|
|
31
|
-
* import { useSubscription } from 'seitu/react'
|
|
32
|
-
* import * as z from 'zod'
|
|
33
|
-
*
|
|
34
|
-
* const sessionStorage = sessionStorageValue({
|
|
35
|
-
* key: 'test',
|
|
36
|
-
* defaultValue: 0,
|
|
37
|
-
* schema: z.number(),
|
|
38
|
-
* })
|
|
39
|
-
*
|
|
40
|
-
* export default function Page() {
|
|
41
|
-
* const value = useSubscription(sessionStorage)
|
|
42
|
-
*
|
|
43
|
-
* return <div>{value}</div>
|
|
44
|
-
* }
|
|
45
|
-
* ```
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```tsx twoslash title="/app/page.tsx"
|
|
49
|
-
* 'use client'
|
|
50
|
-
*
|
|
51
|
-
* import { createSessionStorage, sessionStorageValue } from 'seitu/web'
|
|
31
|
+
* import { createSessionStorage } from 'seitu/web'
|
|
52
32
|
* import { useSubscription } from 'seitu/react'
|
|
53
33
|
* import * as z from 'zod'
|
|
54
34
|
*
|
|
@@ -60,12 +40,12 @@ import type { Readable, Subscribable } from '../core/subscription';
|
|
|
60
40
|
* defaultValues: { count: 0, name: '' },
|
|
61
41
|
* })
|
|
62
42
|
*
|
|
63
|
-
* // Usage with selector, re-renders only when count changes
|
|
64
43
|
* export default function Page() {
|
|
65
|
-
*
|
|
44
|
+
* // Usage with selector, re-renders only when count changes
|
|
45
|
+
* const count = useSubscription(() => sessionStorage, value => value.count)
|
|
66
46
|
*
|
|
67
47
|
* return <div>{count}</div>
|
|
68
48
|
* }
|
|
69
49
|
* ```
|
|
70
50
|
*/
|
|
71
|
-
export declare function useSubscription<S extends Subscribable<any> & Readable<any>, R = S['~
|
|
51
|
+
export declare function useSubscription<S extends Subscribable<any> & Readable<any>, R = S['~']['output']>(factory: () => S, selector?: (value: S['~']['output']) => R): R;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom/vitest';
|
package/dist/react.js
CHANGED
|
@@ -1778,11 +1778,14 @@ var t = Object.create, n = Object.defineProperty, r = Object.getOwnPropertyDescr
|
|
|
1778
1778
|
};
|
|
1779
1779
|
}))(), 1);
|
|
1780
1780
|
function Ze(t, n) {
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1781
|
+
let r = e.useRef(void 0);
|
|
1782
|
+
r.current === void 0 && (r.current = t());
|
|
1783
|
+
let i = r.current;
|
|
1784
|
+
if (!i.get || !i.subscribe) throw Error("Subscription is not valid. It must have a get and subscribe method.");
|
|
1785
|
+
let [a, o] = e.useState(() => n ? n(i.get()) : i.get()), s = e.useEffectEvent(() => a);
|
|
1786
|
+
return e.useEffect(() => i.subscribe((e) => {
|
|
1784
1787
|
let t = n ? n(e) : e;
|
|
1785
|
-
(0, Xe.default)(t,
|
|
1786
|
-
}), [
|
|
1788
|
+
(0, Xe.default)(t, s()) || o(t);
|
|
1789
|
+
}), [i, n]), a;
|
|
1787
1790
|
}
|
|
1788
1791
|
export { Ze as useSubscription };
|
package/dist/utils.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export type Simplify<T> = {
|
|
|
4
4
|
} & {};
|
|
5
5
|
export type Satisfies<T extends U, U> = T;
|
|
6
6
|
export type PartialForKeys<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>;
|
|
7
|
-
export declare function tryParseJson(value:
|
|
7
|
+
export declare function tryParseJson(value: unknown): unknown;
|
package/dist/web/index.d.ts
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Readable, Subscribable } from '../core/index';
|
|
2
|
+
export interface MediaQuery extends Subscribable<boolean>, Readable<boolean> {
|
|
3
|
+
}
|
|
4
|
+
type MinMaxPrefix = 'min-' | 'max-' | '';
|
|
5
|
+
type CSSUnitSuffix = 'px' | 'em' | 'rem' | 'vw' | 'vh' | 'dvw' | 'dvh' | 'svw' | 'svh' | 'lvw' | 'lvh' | 'cqw' | 'cqh' | 'vmin' | 'vmax' | 'cm' | 'mm' | 'in' | 'pt' | 'pc';
|
|
6
|
+
type CSSUnit = `${number}${CSSUnitSuffix}`;
|
|
7
|
+
type CSSResolution = `${number}${'dpi' | 'dpcm' | 'dppx' | 'x'}`;
|
|
8
|
+
type Ratio = `${number}/${number}`;
|
|
9
|
+
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
|
|
10
|
+
type ParseNum<S extends string, Acc extends string = ''> = S extends `${infer D extends Digit | '.'}${infer Rest}` ? ParseNum<Rest, `${Acc}${D}`> : Acc extends `${infer N extends number}` ? N : false;
|
|
11
|
+
type MQEnum<F extends string, V extends string> = `(${F}: ${V})`;
|
|
12
|
+
type MQRange<F extends string, V extends string = CSSUnit> = `(${MinMaxPrefix}${F}: ${V})`;
|
|
13
|
+
type MQOptNum<F extends string> = `(${MinMaxPrefix}${F}${'' | `: ${number}`})`;
|
|
14
|
+
export type MediaQueryType = `${'only ' | 'not ' | ''}${'screen' | 'print' | 'all'}` | MQEnum<'any-hover' | 'hover', 'hover' | 'none'> | MQEnum<'any-pointer' | 'pointer', 'none' | 'coarse' | 'fine'> | MQRange<'width' | 'height' | 'device-width' | 'device-height' | 'inline-size' | 'block-size'> | MQRange<'aspect-ratio' | 'device-aspect-ratio', Ratio> | MQOptNum<'color' | 'color-index' | 'monochrome'> | MQEnum<'color-gamut', 'srgb' | 'p3' | 'rec2020'> | MQEnum<'dynamic-range' | 'video-dynamic-range', 'standard' | 'high'> | MQEnum<'device-posture', 'continuous' | 'folded'> | MQEnum<'display-mode', 'browser' | 'fullscreen' | 'minimal-ui' | 'picture-in-picture' | 'standalone' | 'window-controls-overlay'> | MQEnum<'orientation', 'portrait' | 'landscape'> | MQEnum<'scan', 'interlace' | 'progressive'> | MQEnum<'grid', '0' | '1'> | MQEnum<'update', 'none' | 'slow' | 'fast'> | MQEnum<'overflow-block', 'none' | 'scroll' | 'optional-paged' | 'paged'> | MQEnum<'overflow-inline', 'none' | 'scroll'> | MQEnum<'environment-blending', 'opaque' | 'additive' | 'subtractive'> | MQRange<'resolution', 'infinite' | CSSResolution> | MQEnum<'prefers-color-scheme', 'light' | 'dark'> | MQEnum<'prefers-contrast', 'no-preference' | 'more' | 'less' | 'forced'> | MQEnum<'prefers-reduced-motion' | 'prefers-reduced-transparency' | 'prefers-reduced-data', 'no-preference' | 'reduce'> | MQEnum<'forced-colors', 'none' | 'active'> | MQEnum<'inverted-colors', 'none' | 'inverted'> | MQEnum<'prefers-online', 'online' | 'offline'> | MQEnum<'scripting', 'none' | 'initial-only' | 'enabled'>;
|
|
15
|
+
type Suggestion = MediaQueryType | `${MediaQueryType} and ` | `${MediaQueryType}, `;
|
|
16
|
+
type IsValid<T extends string> = string extends T ? true : T extends `${infer L}, ${infer R}` ? (IsValid<L> extends true ? IsValid<R> : false) : T extends `${infer L} and ${infer R}` ? (IsValid<L> extends true ? IsValid<R> : false) : T extends `not ${infer Rest}` ? IsValid<Rest> : T extends MediaQueryType ? true : false;
|
|
17
|
+
type ValuesOf<F extends string, MQ = MediaQueryType> = MQ extends `(${F}: ${infer V})` ? V : never;
|
|
18
|
+
type SuggestFeature<F extends string, V extends string> = ParseNum<V> extends infer N extends number ? `(${F}: ${N}${CSSUnitSuffix})` : [ValuesOf<F>] extends [never] ? MediaQueryType : `(${F}: ${ValuesOf<F>})`;
|
|
19
|
+
type Suggest<T extends string> = string extends T ? Suggestion : T extends `${infer L}, ${infer R}` ? (IsValid<L> extends true ? `${L}, ${Suggest<R>}` : Suggestion) : T extends `${infer L} and ${infer R}` ? (IsValid<L> extends true ? `${L} and ${Suggest<R>}` : Suggestion) : T extends `not ${infer Rest}` ? `not ${Suggest<Rest>}` : T extends `(${infer F}: ${infer Rest}` ? SuggestFeature<F, Rest extends `${infer V})` ? V : Rest> : MediaQueryType;
|
|
20
|
+
export interface MediaQueryOptions<T extends string> {
|
|
21
|
+
/**
|
|
22
|
+
* A media query string (e.g. `(min-width: 768px)` or `(prefers-color-scheme: dark)`).
|
|
23
|
+
*/
|
|
24
|
+
query: IsValid<T> extends true ? T : Suggest<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Value returned from `get()` during SSR.
|
|
27
|
+
* @default false
|
|
28
|
+
*/
|
|
29
|
+
defaultMatches?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates a handle for a media query.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts twoslash title="media-query.ts"
|
|
36
|
+
* import { mediaQuery } from 'seitu/web'
|
|
37
|
+
* import { useSubscription } from 'seitu/react'
|
|
38
|
+
*
|
|
39
|
+
* const isDesktop = mediaQuery({ query: '(min-width: 768px)' })
|
|
40
|
+
*
|
|
41
|
+
* // Usage with subscribe
|
|
42
|
+
* isDesktop.subscribe(matches => {
|
|
43
|
+
* console.log(matches)
|
|
44
|
+
* })
|
|
45
|
+
*
|
|
46
|
+
* const state = isDesktop.get()
|
|
47
|
+
* console.log(state)
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* ```tsx twoslash title="page.tsx"
|
|
51
|
+
* import { mediaQuery } from 'seitu/web'
|
|
52
|
+
* import { useSubscription } from 'seitu/react'
|
|
53
|
+
*
|
|
54
|
+
* const isDesktop = mediaQuery({ query: '(min-width: 768px)' })
|
|
55
|
+
*
|
|
56
|
+
* // Usage with some function component
|
|
57
|
+
* function Layout() {
|
|
58
|
+
* const matches = useSubscription(() => isDesktop)
|
|
59
|
+
* return matches ? 'i am desktop' : 'i am mobile'
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* ```tsx twoslash
|
|
64
|
+
* import { mediaQuery } from 'seitu/web'
|
|
65
|
+
* import { useSubscription } from 'seitu/react'
|
|
66
|
+
*
|
|
67
|
+
* // @errors: 2362 2322 1109
|
|
68
|
+
* mediaQuery({ query: '(min-width: ' })
|
|
69
|
+
*
|
|
70
|
+
* // @errors: 2362 2322 2820
|
|
71
|
+
* mediaQuery({ query: '(min-width: 768' })
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function mediaQuery<T extends string>(options: MediaQueryOptions<T>): MediaQuery;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Readable, Subscribable } from '../core/index';
|
|
2
|
+
export type ScrollDirection = 'vertical' | 'horizontal' | 'both';
|
|
3
|
+
export interface ScrollStateEdge {
|
|
4
|
+
value: boolean;
|
|
5
|
+
remaining: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ScrollStateValue {
|
|
8
|
+
top: ScrollStateEdge;
|
|
9
|
+
bottom: ScrollStateEdge;
|
|
10
|
+
left: ScrollStateEdge;
|
|
11
|
+
right: ScrollStateEdge;
|
|
12
|
+
}
|
|
13
|
+
export interface ScrollState extends Subscribable<ScrollStateValue>, Readable<ScrollStateValue> {
|
|
14
|
+
}
|
|
15
|
+
export interface ScrollStateOptions {
|
|
16
|
+
/**
|
|
17
|
+
* The element to observe scroll position on.
|
|
18
|
+
*/
|
|
19
|
+
element: Element | null;
|
|
20
|
+
/**
|
|
21
|
+
* Which scroll axis to track.
|
|
22
|
+
* @default 'both'
|
|
23
|
+
*/
|
|
24
|
+
direction?: ScrollDirection;
|
|
25
|
+
/**
|
|
26
|
+
* Number of pixels from each edge before it counts as "scrolled".
|
|
27
|
+
* @default 0
|
|
28
|
+
*/
|
|
29
|
+
threshold?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates a reactive handle that tracks scroll position of an element relative to each edge.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts twoslash
|
|
36
|
+
* import { scrollState } from 'seitu/web'
|
|
37
|
+
*
|
|
38
|
+
* const scroll = scrollState({
|
|
39
|
+
* element: document.querySelector('.container'),
|
|
40
|
+
* direction: 'vertical',
|
|
41
|
+
* threshold: 10,
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* scroll.subscribe(state => {
|
|
45
|
+
* console.log(state.top.value) // scrolled away from top
|
|
46
|
+
* console.log(state.top.remaining) // px left to scroll back to top
|
|
47
|
+
* console.log(state.bottom.value) // reached the bottom
|
|
48
|
+
* console.log(state.bottom.remaining) // px left to reach bottom
|
|
49
|
+
* })
|
|
50
|
+
*
|
|
51
|
+
* const state = scroll.get()
|
|
52
|
+
* console.log(state)
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* ```tsx twoslash
|
|
56
|
+
* import { scrollState } from 'seitu/web'
|
|
57
|
+
* import { useSubscription } from 'seitu/react'
|
|
58
|
+
*
|
|
59
|
+
* const scroll = scrollState({
|
|
60
|
+
* element: document.querySelector('.container'),
|
|
61
|
+
* threshold: 10,
|
|
62
|
+
* })
|
|
63
|
+
*
|
|
64
|
+
* function Layout() {
|
|
65
|
+
* const state = useSubscription(() => scroll)
|
|
66
|
+
* return state.top.value ? 'scrolled' : 'at the top'
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function scrollState(options: ScrollStateOptions): ScrollState;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
-
import type { Readable,
|
|
2
|
+
import type { Readable, Subscribable, Writable } from '../core/index';
|
|
3
3
|
import type { SessionStorage } from './session-storage';
|
|
4
|
-
export interface SessionStorageValue<V> extends Subscribable<V>, Readable<V>, Writable<V
|
|
4
|
+
export interface SessionStorageValue<V> extends Subscribable<V>, Readable<V>, Writable<V> {
|
|
5
5
|
}
|
|
6
|
-
export interface SessionStorageValueOptionsWithStorage<Storage extends SessionStorage<any>, K extends keyof Storage['~
|
|
6
|
+
export interface SessionStorageValueOptionsWithStorage<Storage extends SessionStorage<any>, K extends keyof Storage['~']['output']> {
|
|
7
7
|
storage: Storage;
|
|
8
8
|
key: K;
|
|
9
9
|
}
|
|
@@ -35,7 +35,6 @@ export type SessionStorageValueOptions = SessionStorageValueOptionsWithStorage<S
|
|
|
35
35
|
* value.get() // 0
|
|
36
36
|
* value.set(1)
|
|
37
37
|
* value.set(v => v + 1)
|
|
38
|
-
* value.remove()
|
|
39
38
|
* value.subscribe(v => console.log(v))
|
|
40
39
|
* ```
|
|
41
40
|
*
|
|
@@ -53,5 +52,5 @@ export type SessionStorageValueOptions = SessionStorageValueOptionsWithStorage<S
|
|
|
53
52
|
* storage.get().count === 5 // true
|
|
54
53
|
* ```
|
|
55
54
|
*/
|
|
56
|
-
export declare function sessionStorageValue<Storage extends SessionStorage<any>, K extends keyof Storage['~
|
|
55
|
+
export declare function sessionStorageValue<Storage extends SessionStorage<any>, K extends keyof Storage['~']['output']>(options: SessionStorageValueOptionsWithStorage<Storage, K>): SessionStorageValue<Storage['~']['output'][K]>;
|
|
57
56
|
export declare function sessionStorageValue<S extends StandardSchemaV1<unknown>>(options: SessionStorageValueOptionsWithSchema<S>): SessionStorageValue<StandardSchemaV1.InferOutput<S>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,45 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
import type { Simplify } from '../utils';
|
|
4
|
-
type Output<S extends Record<string, StandardSchemaV1>> = Simplify<{
|
|
5
|
-
[K in keyof S]: StandardSchemaV1.InferOutput<S[K]>;
|
|
6
|
-
}>;
|
|
7
|
-
export interface SessionStorage<O extends Record<string, unknown>> extends Subscribable<O>, Readable<O>, Writable<Partial<O>, O> {
|
|
8
|
-
clear: () => void;
|
|
9
|
-
defaultValues: Readonly<O>;
|
|
10
|
-
}
|
|
11
|
-
export interface SessionStorageOptions<S extends Record<string, StandardSchemaV1>> {
|
|
12
|
-
/**
|
|
13
|
-
* Schemas for each session storage key (one validator per key).
|
|
14
|
-
* Use this when each key has its own type; for a single compound schema use a wrapper with one key.
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* const sessionStorage = createSessionStorage({
|
|
19
|
-
* schemas: {
|
|
20
|
-
* token: z.string().nullable(),
|
|
21
|
-
* preferences: z.object({ theme: z.enum(['light', 'dark']) }),
|
|
22
|
-
* },
|
|
23
|
-
* defaultValues: { token: null, preferences: { theme: 'light' } },
|
|
24
|
-
* })
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
schemas: S;
|
|
28
|
-
/**
|
|
29
|
-
* The default values to use for the session storage.
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```ts
|
|
33
|
-
* const sessionStorage = createSessionStorage({
|
|
34
|
-
* defaultValues: {
|
|
35
|
-
* token: null,
|
|
36
|
-
* preferences: { theme: 'light' },
|
|
37
|
-
* },
|
|
38
|
-
* })
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
defaultValues: Output<S>;
|
|
42
|
-
}
|
|
1
|
+
import type { Store, StoreOptions, StoreOutput, StoreSchema } from '../core/index';
|
|
2
|
+
export type SessionStorage<O extends Record<string, unknown>> = Store<O>;
|
|
43
3
|
/**
|
|
44
4
|
* Creates a reactive handle for a sessionStorage instance.
|
|
45
5
|
*
|
|
@@ -61,15 +21,9 @@ export interface SessionStorageOptions<S extends Record<string, StandardSchemaV1
|
|
|
61
21
|
* sessionStorage.set({ token: '123', preferences: { theme: 'dark' } })
|
|
62
22
|
* sessionStorage.get().token // '123'
|
|
63
23
|
* sessionStorage.get().preferences // { theme: 'dark' }
|
|
64
|
-
* sessionStorage.clear()
|
|
65
|
-
* sessionStorage.get().token // null
|
|
66
|
-
* sessionStorage.get().preferences // { theme: 'light' }
|
|
67
24
|
* sessionStorage.subscribe(value => console.log(value))
|
|
68
25
|
* sessionStorage.set({ token: '456', preferences: { theme: 'light' } })
|
|
69
26
|
* // { token: '456', preferences: { theme: 'light' } }
|
|
70
|
-
* sessionStorage.clear()
|
|
71
|
-
* // { token: null, preferences: { theme: 'light' } }
|
|
72
27
|
* ```
|
|
73
28
|
*/
|
|
74
|
-
export declare function createSessionStorage<S extends
|
|
75
|
-
export {};
|
|
29
|
+
export declare function createSessionStorage<S extends StoreSchema>(options: Omit<StoreOptions<S>, 'provider'>): SessionStorage<StoreOutput<S>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/web.js
CHANGED
|
@@ -1,87 +1,131 @@
|
|
|
1
|
-
import {
|
|
2
|
-
function
|
|
3
|
-
|
|
4
|
-
return
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function n(n) {
|
|
10
|
-
let { subscribe: r, notify: i } = e(), a = { ...n.defaultValues }, o = () => {
|
|
11
|
-
let e = { ...a };
|
|
12
|
-
for (let [r, i] of Object.entries(n.schemas)) {
|
|
13
|
-
if (typeof window > "u") return a;
|
|
14
|
-
let n = window.sessionStorage.getItem(r);
|
|
15
|
-
if (n === null) continue;
|
|
16
|
-
let o = i["~standard"].validate(t(n));
|
|
17
|
-
if (o instanceof Promise) throw TypeError("[createSessionStorage] Validation schema should not return a Promise.");
|
|
18
|
-
e[r] = o.issues ? a[r] : o.value;
|
|
1
|
+
import { n as e, r as t, t as n } from "./core-CuCUF5Aj.js";
|
|
2
|
+
function r(e) {
|
|
3
|
+
let { subscribe: t, notify: r } = n(), i = () => {
|
|
4
|
+
if (typeof window > "u") return e.defaultMatches ?? !1;
|
|
5
|
+
try {
|
|
6
|
+
return window.matchMedia(e.query).matches;
|
|
7
|
+
} catch {
|
|
8
|
+
return e.defaultMatches ?? !1;
|
|
19
9
|
}
|
|
20
|
-
return e;
|
|
21
10
|
};
|
|
22
11
|
return {
|
|
23
|
-
get:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
...t
|
|
30
|
-
});
|
|
31
|
-
},
|
|
32
|
-
subscribe: (e) => {
|
|
33
|
-
let t = r(() => {
|
|
34
|
-
e(o());
|
|
35
|
-
});
|
|
36
|
-
return () => {
|
|
37
|
-
t();
|
|
12
|
+
get: i,
|
|
13
|
+
subscribe: (n) => {
|
|
14
|
+
if (typeof window > "u") return n(i()), () => {};
|
|
15
|
+
let r = t(() => n(i())), a = window.matchMedia(e.query), o = () => n(i());
|
|
16
|
+
return a.addEventListener("change", o), () => {
|
|
17
|
+
r(), a.removeEventListener("change", o);
|
|
38
18
|
};
|
|
39
19
|
},
|
|
40
|
-
|
|
41
|
-
|
|
20
|
+
"~": {
|
|
21
|
+
output: null,
|
|
22
|
+
notify: r
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
var i = {
|
|
27
|
+
value: !1,
|
|
28
|
+
remaining: 0
|
|
29
|
+
};
|
|
30
|
+
function a(e) {
|
|
31
|
+
let { direction: t = "both", threshold: r = 0 } = e, { subscribe: a, notify: o } = n(), s = () => {
|
|
32
|
+
let n = e.element, a = (e, t) => ({
|
|
33
|
+
value: e,
|
|
34
|
+
remaining: Math.max(0, t)
|
|
35
|
+
});
|
|
36
|
+
if (!n) return {
|
|
37
|
+
top: i,
|
|
38
|
+
bottom: i,
|
|
39
|
+
left: i,
|
|
40
|
+
right: i
|
|
41
|
+
};
|
|
42
|
+
let o = n.scrollTop, s = n.scrollHeight - n.scrollTop - n.clientHeight, c = n.scrollLeft, l = n.scrollWidth - n.scrollLeft - n.clientWidth;
|
|
43
|
+
return {
|
|
44
|
+
top: t === "horizontal" ? i : a(o > r, o),
|
|
45
|
+
bottom: t === "horizontal" ? i : a(s <= r, s),
|
|
46
|
+
left: t === "vertical" ? i : a(c > r, c),
|
|
47
|
+
right: t === "vertical" ? i : a(l <= r, l)
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
get: s,
|
|
52
|
+
subscribe: (t) => {
|
|
53
|
+
let n = e.element;
|
|
54
|
+
if (!n) return t(s()), () => {};
|
|
55
|
+
let r = a(() => t(s())), i = () => t(s());
|
|
56
|
+
return n.addEventListener("scroll", i, { passive: !0 }), () => {
|
|
57
|
+
r(), n.removeEventListener("scroll", i);
|
|
58
|
+
};
|
|
42
59
|
},
|
|
43
|
-
|
|
44
|
-
|
|
60
|
+
"~": {
|
|
61
|
+
output: null,
|
|
62
|
+
notify: o
|
|
63
|
+
}
|
|
45
64
|
};
|
|
46
65
|
}
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
66
|
+
function o(n) {
|
|
67
|
+
let r = e({
|
|
68
|
+
...n,
|
|
69
|
+
provider: {
|
|
70
|
+
get: () => {
|
|
71
|
+
if (typeof window > "u") return n.defaultValues;
|
|
72
|
+
let e = { ...n.defaultValues };
|
|
73
|
+
for (let r in e) {
|
|
74
|
+
let i = t(window.sessionStorage.getItem(r)), a = n.schemas[r]["~standard"].validate(i);
|
|
75
|
+
if (a instanceof Promise) throw TypeError("[createSessionStorage] Validation schema should not return a Promise.");
|
|
76
|
+
a.issues && console.warn(JSON.stringify(a.issues, null, 2), { cause: a.issues }), e[r] = a.issues ? n.defaultValues[r] : a.value;
|
|
77
|
+
}
|
|
78
|
+
return e;
|
|
79
|
+
},
|
|
80
|
+
set: (e) => {
|
|
81
|
+
typeof window > "u" || Object.entries(e).forEach(([e, t]) => {
|
|
82
|
+
window.sessionStorage.setItem(e, typeof t == "string" ? t : JSON.stringify(t));
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return typeof window < "u" && window.addEventListener("storage", () => {
|
|
88
|
+
r["~"].notify();
|
|
89
|
+
}), r;
|
|
90
|
+
}
|
|
91
|
+
function s(e) {
|
|
92
|
+
if ("schema" in e && e.defaultValue === void 0) throw Error("[sessionStorageValue] Default value is required");
|
|
93
|
+
if (e.key === void 0) throw Error("[sessionStorageValue] Key is required");
|
|
94
|
+
if (!("schema" in e || "storage" in e)) throw Error("[sessionStorageValue] Either schema or storage must be provided");
|
|
95
|
+
let r = ("schema" in e ? e.defaultValue : e.storage.getDefaultValue(e.key)) ?? null, { subscribe: i, notify: a } = n(), o = () => {
|
|
52
96
|
if (typeof window > "u") return r;
|
|
53
|
-
let
|
|
54
|
-
if (
|
|
55
|
-
let i = t(
|
|
97
|
+
let n = window.sessionStorage.getItem(e.key);
|
|
98
|
+
if (n === null) return r;
|
|
99
|
+
let i = t(n);
|
|
56
100
|
try {
|
|
57
|
-
if ("schema" in
|
|
58
|
-
let
|
|
59
|
-
if (
|
|
60
|
-
return
|
|
61
|
-
} else return
|
|
101
|
+
if ("schema" in e) {
|
|
102
|
+
let t = e.schema["~standard"].validate(i);
|
|
103
|
+
if (t instanceof Promise) throw TypeError("Validation schema should not return a Promise.");
|
|
104
|
+
return t.issues ? (console.error(JSON.stringify(t.issues, null, 2), { cause: t.issues }), r) : t.value;
|
|
105
|
+
} else return i;
|
|
62
106
|
} catch {
|
|
63
107
|
return r !== void 0 && typeof r != "string" ? r : i;
|
|
64
108
|
}
|
|
65
109
|
};
|
|
66
110
|
return {
|
|
67
111
|
get: o,
|
|
68
|
-
set: (
|
|
112
|
+
set: (t) => {
|
|
69
113
|
if (typeof window > "u") return;
|
|
70
|
-
let
|
|
71
|
-
window.sessionStorage.setItem(
|
|
114
|
+
let n = typeof t == "function" ? t(o()) : t;
|
|
115
|
+
window.sessionStorage.setItem(e.key, typeof n == "string" ? n : JSON.stringify(n)), window.dispatchEvent(new Event("storage")), a();
|
|
72
116
|
},
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
subscribe: (e) => {
|
|
77
|
-
let t = i(e), r = (t) => {
|
|
78
|
-
t.key === n.key && e(o());
|
|
117
|
+
subscribe: (t) => {
|
|
118
|
+
let n = i(() => t(o())), r = (n) => {
|
|
119
|
+
n.key === e.key && t(o());
|
|
79
120
|
};
|
|
80
121
|
return typeof window < "u" && window.addEventListener("storage", r), () => {
|
|
81
|
-
|
|
122
|
+
n(), typeof window < "u" && window.removeEventListener("storage", r);
|
|
82
123
|
};
|
|
83
124
|
},
|
|
84
|
-
"~
|
|
125
|
+
"~": {
|
|
126
|
+
output: null,
|
|
127
|
+
notify: a
|
|
128
|
+
}
|
|
85
129
|
};
|
|
86
130
|
}
|
|
87
|
-
export {
|
|
131
|
+
export { o as createSessionStorage, r as mediaQuery, a as scrollState, s as sessionStorageValue };
|