seitu 0.12.0 → 0.13.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/dist/core-BKImMfak.mjs +308 -0
- package/dist/core.d.mts +2 -0
- package/dist/core.mjs +2 -0
- package/dist/index-ChvHgGIa.d.mts +207 -0
- package/dist/{react/hooks.d.ts → react.d.mts} +73 -7
- package/dist/react.mjs +176 -0
- package/dist/{utils/validation.d.ts → utils.d.mts} +7 -3
- package/dist/utils.mjs +32 -0
- package/dist/validate-BzLxGOQt.d.mts +93 -0
- package/dist/{vue/composables.d.ts → vue.d.mts} +10 -6
- package/dist/vue.mjs +87 -0
- package/dist/web.d.mts +383 -0
- package/dist/web.mjs +445 -0
- package/package.json +24 -25
- package/dist/core/computed.d.ts +0 -33
- package/dist/core/debounce-fn.d.ts +0 -21
- package/dist/core/debounce.d.ts +0 -19
- package/dist/core/index.d.ts +0 -8
- package/dist/core/schema-store.d.ts +0 -35
- package/dist/core/store.d.ts +0 -20
- package/dist/core/subscription.d.ts +0 -35
- package/dist/core/throttle-fn.d.ts +0 -23
- package/dist/core/throttle.d.ts +0 -19
- package/dist/core-CIQELdxV.js +0 -158
- package/dist/core.js +0 -2
- package/dist/react/components.d.ts +0 -58
- package/dist/react/index.d.ts +0 -2
- package/dist/react.js +0 -19
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils.d.ts +0 -7
- package/dist/utils.js +0 -9
- package/dist/validate.d.ts +0 -19
- package/dist/vue/index.d.ts +0 -1
- package/dist/vue/test-utils.d.ts +0 -2
- package/dist/vue.js +0 -22
- package/dist/web/index.d.ts +0 -5
- package/dist/web/is-online.d.ts +0 -33
- package/dist/web/media-query.d.ts +0 -77
- package/dist/web/scroll-state.d.ts +0 -111
- package/dist/web/web-storage-value.d.ts +0 -74
- package/dist/web/web-storage.d.ts +0 -70
- package/dist/web.js +0 -179
package/dist/react.mjs
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { deepEqual } from "fast-equals";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
//#region src/react/hooks.ts
|
|
4
|
+
/**
|
|
5
|
+
* Use this hook to subscribe to a reactive value. Accepts a subscription object
|
|
6
|
+
* directly, or a factory function that is called only once on first render —
|
|
7
|
+
* subsequent renders reuse the cached subscription unless dependency array changes.
|
|
8
|
+
*
|
|
9
|
+
* @example Inline subscription
|
|
10
|
+
* ```tsx twoslash title="/app/page.tsx"
|
|
11
|
+
* 'use client'
|
|
12
|
+
*
|
|
13
|
+
* import { createWebStorageValue } from 'seitu/web'
|
|
14
|
+
* import { useSubscription } from 'seitu/react'
|
|
15
|
+
* import * as z from 'zod'
|
|
16
|
+
*
|
|
17
|
+
* export default function Page() {
|
|
18
|
+
* const value = useSubscription(() => createWebStorageValue({
|
|
19
|
+
* type: 'sessionStorage',
|
|
20
|
+
* key: 'test',
|
|
21
|
+
* defaultValue: 0,
|
|
22
|
+
* schema: z.number(),
|
|
23
|
+
* }))
|
|
24
|
+
*
|
|
25
|
+
* return <div>{value}</div>
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example Instance outside of component
|
|
30
|
+
* ```tsx twoslash title="/app/page.tsx"
|
|
31
|
+
* 'use client'
|
|
32
|
+
*
|
|
33
|
+
* import { createWebStorage } from 'seitu/web'
|
|
34
|
+
* import { useSubscription } from 'seitu/react'
|
|
35
|
+
* import * as z from 'zod'
|
|
36
|
+
*
|
|
37
|
+
* const sessionStorage = createWebStorage({
|
|
38
|
+
* type: 'sessionStorage',
|
|
39
|
+
* schemas: { count: z.number(), name: z.string() },
|
|
40
|
+
* defaultValues: { count: 0, name: '' },
|
|
41
|
+
* })
|
|
42
|
+
*
|
|
43
|
+
* export default function Page() {
|
|
44
|
+
* const value = useSubscription(sessionStorage)
|
|
45
|
+
* return <div>{value.count}</div>
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example Subscription with selector
|
|
50
|
+
* ```tsx twoslash title="/app/page.tsx"
|
|
51
|
+
* 'use client'
|
|
52
|
+
*
|
|
53
|
+
* import { createWebStorage } from 'seitu/web'
|
|
54
|
+
* import { useSubscription } from 'seitu/react'
|
|
55
|
+
* import * as z from 'zod'
|
|
56
|
+
*
|
|
57
|
+
* const sessionStorage = createWebStorage({
|
|
58
|
+
* type: 'sessionStorage',
|
|
59
|
+
* schemas: {
|
|
60
|
+
* count: z.number(),
|
|
61
|
+
* name: z.string(),
|
|
62
|
+
* },
|
|
63
|
+
* defaultValues: { count: 0, name: '' },
|
|
64
|
+
* })
|
|
65
|
+
*
|
|
66
|
+
* export default function Page() {
|
|
67
|
+
* // Usage with selector, re-renders only when count changes
|
|
68
|
+
* const count = useSubscription(sessionStorage, { selector: value => value.count })
|
|
69
|
+
*
|
|
70
|
+
* return <div>{count}</div>
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @example Ref example
|
|
75
|
+
* ```tsx twoslash title="/app/page.tsx"
|
|
76
|
+
* 'use client'
|
|
77
|
+
*
|
|
78
|
+
* import * as React from 'react'
|
|
79
|
+
* import { createScrollState } from 'seitu/web'
|
|
80
|
+
* import { useSubscription } from 'seitu/react'
|
|
81
|
+
*
|
|
82
|
+
* export default function Page() {
|
|
83
|
+
* const ref = React.useRef<HTMLDivElement>(null)
|
|
84
|
+
* const state = useSubscription(() => createScrollState({ element: () => ref.current, direction: 'vertical' }))
|
|
85
|
+
*
|
|
86
|
+
* return (
|
|
87
|
+
* <div ref={ref}>
|
|
88
|
+
* {String(state.top.reached)}
|
|
89
|
+
* </div>
|
|
90
|
+
* )
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
function useSubscription(source, options) {
|
|
95
|
+
const { selector, deps = [], isEqual = deepEqual } = options ?? {};
|
|
96
|
+
const isFactory = typeof source === "function";
|
|
97
|
+
const factoryFn = isFactory ? source : () => source;
|
|
98
|
+
const subscription = React.useMemo(() => factoryFn(), isFactory ? deps : [source, ...deps]);
|
|
99
|
+
const lastSnapshotRef = React.useRef(void 0);
|
|
100
|
+
const subscriptionRef = React.useRef(subscription);
|
|
101
|
+
if (subscriptionRef.current !== subscription) {
|
|
102
|
+
subscriptionRef.current = subscription;
|
|
103
|
+
lastSnapshotRef.current = void 0;
|
|
104
|
+
}
|
|
105
|
+
const selectorRef = React.useRef(selector);
|
|
106
|
+
const isEqualRef = React.useRef(isEqual);
|
|
107
|
+
selectorRef.current = selector;
|
|
108
|
+
isEqualRef.current = isEqual;
|
|
109
|
+
const getSnapshot = React.useCallback(() => {
|
|
110
|
+
const sel = selectorRef.current;
|
|
111
|
+
const next = sel ? sel(subscriptionRef.current.get()) : subscriptionRef.current.get();
|
|
112
|
+
const prev = lastSnapshotRef.current;
|
|
113
|
+
if (prev !== void 0 && isEqualRef.current(prev, next)) return prev;
|
|
114
|
+
lastSnapshotRef.current = next;
|
|
115
|
+
return next;
|
|
116
|
+
}, []);
|
|
117
|
+
return React.useSyncExternalStore(subscription.subscribe, getSnapshot, getSnapshot);
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/react/components.tsx
|
|
121
|
+
/**
|
|
122
|
+
* Declarative component that subscribes to a reactive value and passes it to a render function.
|
|
123
|
+
* Re-renders when the value (or selected value) changes. Use when you prefer a component API
|
|
124
|
+
* over the useSubscription hook.
|
|
125
|
+
*
|
|
126
|
+
* @example Basic usage
|
|
127
|
+
* ```tsx twoslash title="/app/page.tsx"
|
|
128
|
+
* 'use client'
|
|
129
|
+
*
|
|
130
|
+
* import { createWebStorage } from 'seitu/web'
|
|
131
|
+
* import { Subscription } from 'seitu/react'
|
|
132
|
+
* import * as z from 'zod'
|
|
133
|
+
*
|
|
134
|
+
* const sessionStorage = createWebStorage({
|
|
135
|
+
* type: 'sessionStorage',
|
|
136
|
+
* schemas: { count: z.number(), name: z.string() },
|
|
137
|
+
* defaultValues: { count: 0, name: '' },
|
|
138
|
+
* })
|
|
139
|
+
*
|
|
140
|
+
* export default function Page() {
|
|
141
|
+
* return (
|
|
142
|
+
* <Subscription value={sessionStorage}>
|
|
143
|
+
* {(value) => <div>{value.count}</div>}
|
|
144
|
+
* </Subscription>
|
|
145
|
+
* )
|
|
146
|
+
* }
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* @example With selector
|
|
150
|
+
* ```tsx twoslash title="/app/page.tsx"
|
|
151
|
+
* 'use client'
|
|
152
|
+
*
|
|
153
|
+
* import { createWebStorage } from 'seitu/web'
|
|
154
|
+
* import { Subscription } from 'seitu/react'
|
|
155
|
+
* import * as z from 'zod'
|
|
156
|
+
*
|
|
157
|
+
* const sessionStorage = createWebStorage({
|
|
158
|
+
* type: 'sessionStorage',
|
|
159
|
+
* schemas: { count: z.number(), name: z.string() },
|
|
160
|
+
* defaultValues: { count: 0, name: '' },
|
|
161
|
+
* })
|
|
162
|
+
*
|
|
163
|
+
* export default function Page() {
|
|
164
|
+
* return (
|
|
165
|
+
* <Subscription value={sessionStorage} selector={(v) => v.count}>
|
|
166
|
+
* {(count) => <div>{count}</div>}
|
|
167
|
+
* </Subscription>
|
|
168
|
+
* )
|
|
169
|
+
* }
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
function Subscription({ value, selector, children }) {
|
|
173
|
+
return children(useSubscription(value, { selector }));
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
export { Subscription, useSubscription };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { t as ValidationSchemaErrorProps } from "./validate-BzLxGOQt.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/validation.d.ts
|
|
2
4
|
/**
|
|
3
5
|
* Repair broken web storage value object if value was object but not all keys are in the default value object.
|
|
4
6
|
*
|
|
@@ -22,6 +24,8 @@ import type { ValidationSchemaErrorProps } from '../validate';
|
|
|
22
24
|
* value.get() // { a: 1, b: 'default' }
|
|
23
25
|
* ```
|
|
24
26
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
declare function repairValueObjectWithDefault<O extends Record<string, unknown>>(props: ValidationSchemaErrorProps<O>): O & {
|
|
28
|
+
[k: string]: any;
|
|
27
29
|
};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { repairValueObjectWithDefault };
|
package/dist/utils.mjs
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/utils/validation.ts
|
|
2
|
+
/**
|
|
3
|
+
* Repair broken web storage value object if value was object but not all keys are in the default value object.
|
|
4
|
+
*
|
|
5
|
+
* @returns A new object with the existing keys in the value object that are not in the default value object.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { createWebStorageValue } from 'seitu/web'
|
|
10
|
+
* import { repairValueObjectWithDefault } from 'seitu/utils'
|
|
11
|
+
* import * as z from 'zod'
|
|
12
|
+
*
|
|
13
|
+
* const value = createWebStorageValue({
|
|
14
|
+
* type: 'localStorage',
|
|
15
|
+
* schema: z.object({ a: z.number(), b: z.string() }),
|
|
16
|
+
* key: 'storage-key',
|
|
17
|
+
* defaultValue: { a: 0, b: 'default' },
|
|
18
|
+
* onValidationError: repairValueObjectWithDefault,
|
|
19
|
+
* })
|
|
20
|
+
* value.get() // { a: 0, b: 'default' }
|
|
21
|
+
* window.localStorage.setItem('storage-key', JSON.stringify({ a: 1 }))
|
|
22
|
+
* value.get() // { a: 1, b: 'default' }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function repairValueObjectWithDefault(props) {
|
|
26
|
+
return {
|
|
27
|
+
...props.defaultValue,
|
|
28
|
+
...typeof props.value === "object" && props.value !== null ? Object.fromEntries(Object.entries(props.value).filter(([key, val]) => key in props.defaultValue && typeof val === typeof props.defaultValue[key])) : {}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { repairValueObjectWithDefault };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//#region ../node_modules/.pnpm/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts
|
|
2
|
+
/** The Standard Typed interface. This is a base type extended by other specs. */
|
|
3
|
+
interface StandardTypedV1<Input = unknown, Output = Input> {
|
|
4
|
+
/** The Standard properties. */
|
|
5
|
+
readonly "~standard": StandardTypedV1.Props<Input, Output>;
|
|
6
|
+
}
|
|
7
|
+
declare namespace StandardTypedV1 {
|
|
8
|
+
/** The Standard Typed properties interface. */
|
|
9
|
+
interface Props<Input = unknown, Output = Input> {
|
|
10
|
+
/** The version number of the standard. */
|
|
11
|
+
readonly version: 1;
|
|
12
|
+
/** The vendor name of the schema library. */
|
|
13
|
+
readonly vendor: string;
|
|
14
|
+
/** Inferred types associated with the schema. */
|
|
15
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
16
|
+
}
|
|
17
|
+
/** The Standard Typed types interface. */
|
|
18
|
+
interface Types<Input = unknown, Output = Input> {
|
|
19
|
+
/** The input type of the schema. */
|
|
20
|
+
readonly input: Input;
|
|
21
|
+
/** The output type of the schema. */
|
|
22
|
+
readonly output: Output;
|
|
23
|
+
}
|
|
24
|
+
/** Infers the input type of a Standard Typed. */
|
|
25
|
+
type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
|
|
26
|
+
/** Infers the output type of a Standard Typed. */
|
|
27
|
+
type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
|
|
28
|
+
}
|
|
29
|
+
/** The Standard Schema interface. */
|
|
30
|
+
interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
31
|
+
/** The Standard Schema properties. */
|
|
32
|
+
readonly "~standard": StandardSchemaV1.Props<Input, Output>;
|
|
33
|
+
}
|
|
34
|
+
declare namespace StandardSchemaV1 {
|
|
35
|
+
/** The Standard Schema properties interface. */
|
|
36
|
+
interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
|
|
37
|
+
/** Validates unknown input values. */
|
|
38
|
+
readonly validate: (value: unknown, options?: StandardSchemaV1.Options | undefined) => Result<Output> | Promise<Result<Output>>;
|
|
39
|
+
}
|
|
40
|
+
/** The result interface of the validate function. */
|
|
41
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
42
|
+
/** The result interface if validation succeeds. */
|
|
43
|
+
interface SuccessResult<Output> {
|
|
44
|
+
/** The typed output value. */
|
|
45
|
+
readonly value: Output;
|
|
46
|
+
/** A falsy value for `issues` indicates success. */
|
|
47
|
+
readonly issues?: undefined;
|
|
48
|
+
}
|
|
49
|
+
interface Options {
|
|
50
|
+
/** Explicit support for additional vendor-specific parameters, if needed. */
|
|
51
|
+
readonly libraryOptions?: Record<string, unknown> | undefined;
|
|
52
|
+
}
|
|
53
|
+
/** The result interface if validation fails. */
|
|
54
|
+
interface FailureResult {
|
|
55
|
+
/** The issues of failed validation. */
|
|
56
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
57
|
+
}
|
|
58
|
+
/** The issue interface of the failure output. */
|
|
59
|
+
interface Issue {
|
|
60
|
+
/** The error message of the issue. */
|
|
61
|
+
readonly message: string;
|
|
62
|
+
/** The path of the issue, if any. */
|
|
63
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
64
|
+
}
|
|
65
|
+
/** The path segment interface of the issue. */
|
|
66
|
+
interface PathSegment {
|
|
67
|
+
/** The key representing a path segment. */
|
|
68
|
+
readonly key: PropertyKey;
|
|
69
|
+
}
|
|
70
|
+
/** The Standard types interface. */
|
|
71
|
+
interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> {}
|
|
72
|
+
/** Infers the input type of a Standard. */
|
|
73
|
+
type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
|
|
74
|
+
/** Infers the output type of a Standard. */
|
|
75
|
+
type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
|
|
76
|
+
}
|
|
77
|
+
/** The Standard JSON Schema interface. */
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/validate.d.ts
|
|
80
|
+
type ValidationSchemaOutput<S extends StandardSchemaV1<unknown>> = StandardSchemaV1.InferOutput<S>;
|
|
81
|
+
interface ValidationSchemaErrorProps<O> {
|
|
82
|
+
defaultValue: O;
|
|
83
|
+
issues: StandardSchemaV1.Issue[];
|
|
84
|
+
value: unknown;
|
|
85
|
+
}
|
|
86
|
+
interface ValidationSchemaObjectErrorProps<O extends Record<string, unknown>> {
|
|
87
|
+
defaultValue: O[keyof O];
|
|
88
|
+
issues: StandardSchemaV1.Issue[];
|
|
89
|
+
value: unknown;
|
|
90
|
+
key: keyof O;
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { StandardSchemaV1 as i, ValidationSchemaObjectErrorProps as n, ValidationSchemaOutput as r, ValidationSchemaErrorProps as t };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { _ as Readable, v as Subscribable } from "./index-ChvHgGIa.mjs";
|
|
2
|
+
import { MaybeRefOrGetter, ShallowRef } from "vue";
|
|
3
|
+
|
|
4
|
+
//#region src/vue/composables.d.ts
|
|
5
|
+
interface UseSubscriptionOptions<S extends Subscribable<any> & Readable<any>, R = S['~']['output']> {
|
|
6
|
+
selector?: (value: S['~']['output']) => R;
|
|
7
|
+
isEqual?: (prev: R, next: R) => boolean;
|
|
6
8
|
}
|
|
7
9
|
/**
|
|
8
10
|
* Use this composable to subscribe to a reactive value. Accepts a subscription object
|
|
@@ -67,4 +69,6 @@ export interface UseSubscriptionOptions<S extends Subscribable<any> & Readable<a
|
|
|
67
69
|
* </template>
|
|
68
70
|
* ```
|
|
69
71
|
*/
|
|
70
|
-
|
|
72
|
+
declare function useSubscription<S extends Subscribable<any> & Readable<any>, R = S['~']['output']>(source: MaybeRefOrGetter<S>, options?: UseSubscriptionOptions<S, R>): Readonly<ShallowRef<R>>;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { UseSubscriptionOptions, useSubscription };
|
package/dist/vue.mjs
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { deepEqual } from "fast-equals";
|
|
2
|
+
import { computed, onWatcherCleanup, readonly, shallowRef, toValue, watch } from "vue";
|
|
3
|
+
//#region src/vue/composables.ts
|
|
4
|
+
/**
|
|
5
|
+
* Use this composable to subscribe to a reactive value. Accepts a subscription object
|
|
6
|
+
* directly or a ref/getter that returns one.
|
|
7
|
+
*
|
|
8
|
+
* @example Inline subscription
|
|
9
|
+
* ```vue
|
|
10
|
+
* <script setup lang="ts">
|
|
11
|
+
* import { createWebStorageValue } from 'seitu/web'
|
|
12
|
+
* import { useSubscription } from 'seitu/vue'
|
|
13
|
+
* import * as z from 'zod'
|
|
14
|
+
*
|
|
15
|
+
* const value = useSubscription(
|
|
16
|
+
* createWebStorageValue({ type: 'sessionStorage', key: 'test', defaultValue: 0, schema: z.number() }),
|
|
17
|
+
* )
|
|
18
|
+
* <\/script>
|
|
19
|
+
*
|
|
20
|
+
* <template>
|
|
21
|
+
* <div>{{ value }}</div>
|
|
22
|
+
* </template>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example Instance outside of the subscription
|
|
26
|
+
* ```vue
|
|
27
|
+
* <script setup lang="ts">
|
|
28
|
+
* import { createWebStorage } from 'seitu/web'
|
|
29
|
+
* import { useSubscription } from 'seitu/vue'
|
|
30
|
+
* import * as z from 'zod'
|
|
31
|
+
*
|
|
32
|
+
* const sessionStorage = createWebStorage({
|
|
33
|
+
* type: 'sessionStorage',
|
|
34
|
+
* schemas: { count: z.number(), name: z.string() },
|
|
35
|
+
* defaultValues: { count: 0, name: '' },
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* const value = useSubscription(sessionStorage)
|
|
39
|
+
* <\/script>
|
|
40
|
+
*
|
|
41
|
+
* <template>
|
|
42
|
+
* <div>{{ value.count }}</div>
|
|
43
|
+
* </template>
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example With selector
|
|
47
|
+
* ```vue
|
|
48
|
+
* <script setup lang="ts">
|
|
49
|
+
* import { createWebStorage } from 'seitu/web'
|
|
50
|
+
* import { useSubscription } from 'seitu/vue'
|
|
51
|
+
* import * as z from 'zod'
|
|
52
|
+
*
|
|
53
|
+
* const sessionStorage = createWebStorage({
|
|
54
|
+
* type: 'sessionStorage',
|
|
55
|
+
* schemas: { count: z.number(), name: z.string() },
|
|
56
|
+
* defaultValues: { count: 0, name: '' },
|
|
57
|
+
* })
|
|
58
|
+
*
|
|
59
|
+
* const count = useSubscription(sessionStorage, { selector: v => v.count })
|
|
60
|
+
* <\/script>
|
|
61
|
+
*
|
|
62
|
+
* <template>
|
|
63
|
+
* <div>{{ count }}</div>
|
|
64
|
+
* </template>
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
function useSubscription(source, options) {
|
|
68
|
+
const { selector, isEqual = deepEqual } = options ?? {};
|
|
69
|
+
function getSnapshot(sub) {
|
|
70
|
+
return selector ? selector(sub.get()) : sub.get();
|
|
71
|
+
}
|
|
72
|
+
const sub = computed(() => toValue(source));
|
|
73
|
+
const state = shallowRef(getSnapshot(sub.value));
|
|
74
|
+
watch(sub, (sub) => {
|
|
75
|
+
state.value = getSnapshot(sub);
|
|
76
|
+
const unsubscribe = sub.subscribe(() => {
|
|
77
|
+
const next = getSnapshot(sub);
|
|
78
|
+
if (!isEqual(state.value, next)) state.value = next;
|
|
79
|
+
});
|
|
80
|
+
onWatcherCleanup(() => {
|
|
81
|
+
unsubscribe();
|
|
82
|
+
});
|
|
83
|
+
}, { immediate: true });
|
|
84
|
+
return readonly(state);
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { useSubscription };
|