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 CHANGED
@@ -30,7 +30,7 @@ value.remove()
30
30
  value.subscribe(v => console.log(v))
31
31
 
32
32
  function Counter() {
33
- const count = useSubscription(value)
33
+ const count = useSubscription(() => value)
34
34
 
35
35
  return (
36
36
  <div>
@@ -1 +1,2 @@
1
+ export * from './store';
1
2
  export * from './subscription';
@@ -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
- '~types': {
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, P extends Partial<T> = T> {
11
- set: (value: T | ((prev: P) => T)) => any;
11
+ export interface Writable<T> {
12
+ set: (value: T | ((prev: T) => T)) => any;
12
13
  }
13
- export interface Removable {
14
- remove: () => void;
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
- function e() {
2
- let e = new Set();
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 };
@@ -1,6 +1,7 @@
1
- import type { Readable, Subscribable } from '../core/subscription';
1
+ import type { Readable, Subscribable } from '../core/index';
2
2
  /**
3
- * Use this hook to access a subscription for any function that implements the Subscription interface.
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 { sessionStorageValue } from 'seitu/web'
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
- * const count = useSubscription(sessionStorage, value => value.count)
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['~types']['output']>(subscription: S, selector?: (value: S['~types']['output']) => R): R;
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
- if (!t.get || !t.subscribe) throw Error("Subscription is not valid. It must have a get and subscribe method.");
1782
- let [r, i] = e.useState(() => n ? n(t.get()) : t.get()), a = e.useEffectEvent(() => r);
1783
- return e.useEffect(() => t.subscribe((e) => {
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, a()) || i(t);
1786
- }), [t, n]), r;
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: string): unknown;
7
+ export declare function tryParseJson(value: unknown): unknown;
@@ -1,2 +1,4 @@
1
+ export * from './media-query';
2
+ export * from './scroll-state';
1
3
  export * from './session-storage';
2
4
  export * from './session-storage-value';
@@ -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, Removable, Subscribable, Writable } from '../core/index';
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>, Removable {
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['~types']['output']> {
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['~types']['output']>(options: SessionStorageValueOptionsWithStorage<Storage, K>): SessionStorageValue<Storage['~types']['output'][K]>;
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 { StandardSchemaV1 } from '@standard-schema/spec';
2
- import type { Readable, Subscribable, Writable } from '../core/index';
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 Record<string, StandardSchemaV1>>(options: SessionStorageOptions<S>): SessionStorage<Output<S>>;
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 { createSubscription as e } from "./core.js";
2
- function t(e) {
3
- try {
4
- return JSON.parse(e);
5
- } catch {
6
- return e;
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: o,
24
- set: (e) => {
25
- let t = typeof e == "function" ? e(o()) : e;
26
- for (let [e, n] of Object.entries(t)) window.sessionStorage.setItem(e, typeof n == "string" ? n : JSON.stringify(n));
27
- i({
28
- ...o(),
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
- clear: () => {
41
- window.sessionStorage.clear(), i({ ...n.defaultValues });
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
- defaultValues: a,
44
- "~types": { output: null }
60
+ "~": {
61
+ output: null,
62
+ notify: o
63
+ }
45
64
  };
46
65
  }
47
- function r(n) {
48
- if ("schema" in n && n.defaultValue === void 0) throw Error("[sessionStorageValue] Default value is required");
49
- if (n.key === void 0) throw Error("[sessionStorageValue] Key is required");
50
- if (!("schema" in n || "storage" in n)) throw Error("[sessionStorageValue] Either schema or storage must be provided");
51
- let r = ("schema" in n ? n.defaultValue : n.storage.defaultValues[n.key]) ?? null, { subscribe: i, notify: a } = e(), o = () => {
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 e = window.sessionStorage.getItem(n.key);
54
- if (e === null) return r;
55
- let i = t(e);
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 n) {
58
- let e = n.schema["~standard"].validate(i);
59
- if (e instanceof Promise) throw TypeError("Validation schema should not return a Promise.");
60
- return e.issues ? (console.error(JSON.stringify(e.issues, null, 2), { cause: e.issues }), r) : e.value;
61
- } else return n.storage.get()[n.key];
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: (e) => {
112
+ set: (t) => {
69
113
  if (typeof window > "u") return;
70
- let t = typeof e == "function" ? e(o()) : e;
71
- window.sessionStorage.setItem(n.key, typeof t == "string" ? t : JSON.stringify(t)), window.dispatchEvent(new Event("storage")), a(t);
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
- remove: () => {
74
- typeof window > "u" || (window.sessionStorage.removeItem(n.key), window.dispatchEvent(new Event("storage")), a(o()));
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
- t(), typeof window < "u" && window.removeEventListener("storage", r);
122
+ n(), typeof window < "u" && window.removeEventListener("storage", r);
82
123
  };
83
124
  },
84
- "~types": { output: null }
125
+ "~": {
126
+ output: null,
127
+ notify: a
128
+ }
85
129
  };
86
130
  }
87
- export { n as createSessionStorage, r as sessionStorageValue };
131
+ export { o as createSessionStorage, r as mediaQuery, a as scrollState, s as sessionStorageValue };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "seitu",
3
3
  "displayName": "Seitu",
4
4
  "type": "module",
5
- "version": "0.0.1",
5
+ "version": "0.1.0",
6
6
  "private": false,
7
7
  "author": "Valerii Strilets",
8
8
  "license": "MIT",