elements-kit 0.27.2 → 0.27.4
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/await.d.mts +1 -1
- package/dist/{children-CB8W0ANY.d.mts → children-_cMdyX7Q.d.mts} +1 -1
- package/dist/for.d.mts +1 -1
- package/dist/{index-B1FHAd5T.d.mts → index-BrVMftR8.d.mts} +4 -0
- package/dist/integrations/react.d.mts +18 -2
- package/dist/integrations/react.mjs +18 -2
- package/dist/jsx-runtime/dev.d.mts +2 -2
- package/dist/jsx-runtime/index.d.mts +2 -2
- package/dist/signals/index.d.mts +1 -1
- package/dist/signals/index.mjs +4 -0
- package/dist/ui/marketing/marketing.css +0 -1
- package/dist/ui/overlay/index.d.mts +3 -3
- package/dist/ui/overlay/index.mjs +6 -1
- package/dist/utilities/active-element.d.mts +1 -1
- package/dist/utilities/async.d.mts +1 -1
- package/dist/utilities/debounced.d.mts +1 -1
- package/dist/utilities/dom-lifecycle.d.mts +3 -1
- package/dist/utilities/dom-lifecycle.mjs +2 -2
- package/dist/utilities/element-rect.d.mts +15 -15
- package/dist/utilities/element-rect.mjs +39 -36
- package/dist/utilities/element-scroll.d.mts +1 -1
- package/dist/utilities/event-driven.d.mts +1 -1
- package/dist/utilities/event-listener.d.mts +1 -1
- package/dist/utilities/focus-within.d.mts +1 -1
- package/dist/utilities/hover.d.mts +1 -1
- package/dist/utilities/interval.d.mts +1 -1
- package/dist/utilities/location.d.mts +1 -1
- package/dist/utilities/media-devices.d.mts +1 -1
- package/dist/utilities/media-player.d.mts +1 -1
- package/dist/utilities/media-query.d.mts +1 -1
- package/dist/utilities/network.d.mts +1 -1
- package/dist/utilities/orientation.d.mts +1 -1
- package/dist/utilities/previous.d.mts +1 -1
- package/dist/utilities/promise.d.mts +1 -1
- package/dist/utilities/routing.d.mts +1 -1
- package/dist/utilities/search-params.d.mts +1 -1
- package/dist/utilities/storage.d.mts +7 -1
- package/dist/utilities/storage.mjs +46 -34
- package/dist/utilities/throttled.d.mts +1 -1
- package/dist/utilities/timeout.d.mts +1 -1
- package/dist/utilities/window-focus.d.mts +1 -1
- package/dist/utilities/window-size.d.mts +1 -1
- package/package.json +5 -3
package/dist/await.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as AttrChangeHandler, t as ATTRIBUTES } from "./attributes-3r7Diua4.mjs";
|
|
2
2
|
import { i as PropertiesOf, n as CustomElementRegistry, r as EventsOf } from "./custom-elements-C7oiqw-y.mjs";
|
|
3
|
-
import { r as MaybeReactive } from "./index-
|
|
3
|
+
import { r as MaybeReactive } from "./index-BrVMftR8.mjs";
|
|
4
4
|
import * as CSS from "csstype";
|
|
5
5
|
import { JSX } from "dom-expressions/src/jsx";
|
|
6
6
|
|
package/dist/for.d.mts
CHANGED
|
@@ -333,6 +333,10 @@ type MaybeReactive<T> = T | Computed<T>;
|
|
|
333
333
|
* the runtime hands props over exactly as the caller wrote them, so the value
|
|
334
334
|
* may be either form. Reading inside an effect or a JSX getter subscribes.
|
|
335
335
|
*
|
|
336
|
+
* Only branded handles created by `signal`/`computed` are called — a
|
|
337
|
+
* plain function is treated as a static VALUE and returned as-is (so
|
|
338
|
+
* `T` itself may be a function type).
|
|
339
|
+
*
|
|
336
340
|
* @example
|
|
337
341
|
* ```ts
|
|
338
342
|
* resolve(5); // 5
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as Computed } from "../index-
|
|
1
|
+
import { t as Computed } from "../index-BrVMftR8.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/integrations/react.d.ts
|
|
4
4
|
/**
|
|
@@ -8,8 +8,17 @@ import { t as Computed } from "../index-B1FHAd5T.mjs";
|
|
|
8
8
|
* `Computed<T>`. Using `() => T` instead of `Computed<T>` prevents TypeScript from
|
|
9
9
|
* picking the write overload of `Signal<T>` during type inference.
|
|
10
10
|
*
|
|
11
|
+
* **Server rendering:** pass `getServerSnapshot` for any component React renders
|
|
12
|
+
* on the server — React requires it there and throws without one. It runs on
|
|
13
|
+
* the server *and* again for the hydration render on the client, so it must
|
|
14
|
+
* return the same value both times: whatever the server put in the HTML. A
|
|
15
|
+
* signal reading browser state (`matchMedia`, `localStorage`, `location`) does
|
|
16
|
+
* not qualify, since it answers differently in the browser.
|
|
17
|
+
*
|
|
11
18
|
* @template T - The type of the signal value.
|
|
12
19
|
* @param value - A writable `Signal<T>` or a derived `Computed<T>`.
|
|
20
|
+
* @param getServerSnapshot - Returns the value the server rendered. Required
|
|
21
|
+
* for server-rendered components; omit only for client-only ones.
|
|
13
22
|
* @returns The current value, updated on every signal change.
|
|
14
23
|
*
|
|
15
24
|
* @example
|
|
@@ -23,8 +32,15 @@ import { t as Computed } from "../index-B1FHAd5T.mjs";
|
|
|
23
32
|
* return <div>{countValue} × 2 = {doubleValue}</div>;
|
|
24
33
|
* }
|
|
25
34
|
* ```
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* Server-rendered component reading a browser-only signal:
|
|
38
|
+
* ```tsx
|
|
39
|
+
* // `theme` resolves to "light" on the server — say so, or hydration mismatches.
|
|
40
|
+
* const mode = useSignal(theme, () => "light");
|
|
41
|
+
* ```
|
|
26
42
|
*/
|
|
27
|
-
declare function useSignal<T>(value: () => T): T;
|
|
43
|
+
declare function useSignal<T>(value: () => T, getServerSnapshot?: () => T): T;
|
|
28
44
|
/**
|
|
29
45
|
* Create a signal effect scope tied to a React component's lifetime.
|
|
30
46
|
*
|
|
@@ -10,8 +10,17 @@ import { useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
|
10
10
|
* `Computed<T>`. Using `() => T` instead of `Computed<T>` prevents TypeScript from
|
|
11
11
|
* picking the write overload of `Signal<T>` during type inference.
|
|
12
12
|
*
|
|
13
|
+
* **Server rendering:** pass `getServerSnapshot` for any component React renders
|
|
14
|
+
* on the server — React requires it there and throws without one. It runs on
|
|
15
|
+
* the server *and* again for the hydration render on the client, so it must
|
|
16
|
+
* return the same value both times: whatever the server put in the HTML. A
|
|
17
|
+
* signal reading browser state (`matchMedia`, `localStorage`, `location`) does
|
|
18
|
+
* not qualify, since it answers differently in the browser.
|
|
19
|
+
*
|
|
13
20
|
* @template T - The type of the signal value.
|
|
14
21
|
* @param value - A writable `Signal<T>` or a derived `Computed<T>`.
|
|
22
|
+
* @param getServerSnapshot - Returns the value the server rendered. Required
|
|
23
|
+
* for server-rendered components; omit only for client-only ones.
|
|
15
24
|
* @returns The current value, updated on every signal change.
|
|
16
25
|
*
|
|
17
26
|
* @example
|
|
@@ -25,12 +34,19 @@ import { useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
|
25
34
|
* return <div>{countValue} × 2 = {doubleValue}</div>;
|
|
26
35
|
* }
|
|
27
36
|
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* Server-rendered component reading a browser-only signal:
|
|
40
|
+
* ```tsx
|
|
41
|
+
* // `theme` resolves to "light" on the server — say so, or hydration mismatches.
|
|
42
|
+
* const mode = useSignal(theme, () => "light");
|
|
43
|
+
* ```
|
|
28
44
|
*/
|
|
29
|
-
function useSignal(value) {
|
|
45
|
+
function useSignal(value, getServerSnapshot) {
|
|
30
46
|
return useSyncExternalStore((callback) => effect(() => {
|
|
31
47
|
value();
|
|
32
48
|
callback();
|
|
33
|
-
}),
|
|
49
|
+
}), value, getServerSnapshot);
|
|
34
50
|
}
|
|
35
51
|
/**
|
|
36
52
|
* Create a signal effect scope tied to a React component's lifetime.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as Fragment, c as PropsOf, i as JSX, l as Require, n as ComponentClass, o as MaybeReactiveProps, r as ComponentFn, s as Props, t as Children } from "../children-
|
|
2
|
-
import { n as ComputedProps, r as MaybeReactive } from "../index-
|
|
1
|
+
import { a as Fragment, c as PropsOf, i as JSX, l as Require, n as ComponentClass, o as MaybeReactiveProps, r as ComponentFn, s as Props, t as Children } from "../children-_cMdyX7Q.mjs";
|
|
2
|
+
import { n as ComputedProps, r as MaybeReactive } from "../index-BrVMftR8.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/jsx-runtime/hot.d.ts
|
|
5
5
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as Fragment, c as PropsOf, i as JSX, l as Require, n as ComponentClass, o as MaybeReactiveProps, r as ComponentFn, s as Props, t as Children, u as createElement } from "../children-
|
|
2
|
-
import { n as ComputedProps, r as MaybeReactive } from "../index-
|
|
1
|
+
import { a as Fragment, c as PropsOf, i as JSX, l as Require, n as ComponentClass, o as MaybeReactiveProps, r as ComponentFn, s as Props, t as Children, u as createElement } from "../children-_cMdyX7Q.mjs";
|
|
2
|
+
import { n as ComputedProps, r as MaybeReactive } from "../index-BrVMftR8.mjs";
|
|
3
3
|
export { type Children, ComponentClass, ComponentFn, type ComputedProps, Fragment, JSX, type MaybeReactive, type MaybeReactiveProps, type Props, type PropsOf, type Require, createElement as h, createElement as jsx, createElement as jsxDEV, createElement as jsxs };
|
package/dist/signals/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as isSignal, D as untracked, E as trigger, S as isEffectScope, T as signal, _ as computed, a as Updater, b as isComputed, c as reactive, d as COMPUTED, f as EFFECT, g as batch, h as SIGNAL, i as Signal, l as resolve, m as SEED, n as ComputedProps, o as computedProps, p as EFFECT_SCOPE, r as MaybeReactive, s as isReactive, t as Computed, u as CLAIM, v as effect, w as onCleanup, x as isEffect, y as effectScope } from "../index-
|
|
1
|
+
import { C as isSignal, D as untracked, E as trigger, S as isEffectScope, T as signal, _ as computed, a as Updater, b as isComputed, c as reactive, d as COMPUTED, f as EFFECT, g as batch, h as SIGNAL, i as Signal, l as resolve, m as SEED, n as ComputedProps, o as computedProps, p as EFFECT_SCOPE, r as MaybeReactive, s as isReactive, t as Computed, u as CLAIM, v as effect, w as onCleanup, x as isEffect, y as effectScope } from "../index-BrVMftR8.mjs";
|
|
2
2
|
export { CLAIM, COMPUTED, Computed, ComputedProps, EFFECT, EFFECT_SCOPE, MaybeReactive, SEED, SIGNAL, Signal, Updater, batch, computed, computedProps, effect, effectScope, isComputed, isEffect, isEffectScope, isReactive, isSignal, onCleanup, reactive, resolve, signal, trigger, untracked };
|
package/dist/signals/index.mjs
CHANGED
|
@@ -83,6 +83,10 @@ function reactive(source) {
|
|
|
83
83
|
* the runtime hands props over exactly as the caller wrote them, so the value
|
|
84
84
|
* may be either form. Reading inside an effect or a JSX getter subscribes.
|
|
85
85
|
*
|
|
86
|
+
* Only branded handles created by `signal`/`computed` are called — a
|
|
87
|
+
* plain function is treated as a static VALUE and returned as-is (so
|
|
88
|
+
* `T` itself may be a function type).
|
|
89
|
+
*
|
|
86
90
|
* @example
|
|
87
91
|
* ```ts
|
|
88
92
|
* resolve(5); // 5
|
|
@@ -11,9 +11,9 @@ import { Placement } from "@floating-ui/dom";
|
|
|
11
11
|
* viewport defaults). Derived values build on regions — `detents()`
|
|
12
12
|
* quantizes one, `rubber()` resists at its edges.
|
|
13
13
|
*/
|
|
14
|
-
/** A reactive rect
|
|
15
|
-
*
|
|
16
|
-
* area) can be supplied too. */
|
|
14
|
+
/** A reactive rect. `createElementRect` returns one reactive `DOMRect`,
|
|
15
|
+
* so the element branch projects its fields onto this shape; custom
|
|
16
|
+
* rects (a static region, a virtual area) can be supplied too. */
|
|
17
17
|
interface Region {
|
|
18
18
|
top(): number;
|
|
19
19
|
left(): number;
|
|
@@ -19,7 +19,12 @@ function constraint(source) {
|
|
|
19
19
|
if (source instanceof Element) {
|
|
20
20
|
const rect = createElementRect(source);
|
|
21
21
|
onCleanup(() => rect[Symbol.dispose]());
|
|
22
|
-
return
|
|
22
|
+
return {
|
|
23
|
+
top: () => rect().top,
|
|
24
|
+
left: () => rect().left,
|
|
25
|
+
width: () => rect().width,
|
|
26
|
+
height: () => rect().height
|
|
27
|
+
};
|
|
23
28
|
}
|
|
24
29
|
return {
|
|
25
30
|
top: () => source.top,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { t as HTMLElementBase } from "../environment-c2ftMYT0.mjs";
|
|
2
|
+
|
|
1
3
|
//#region src/utilities/dom-lifecycle.d.ts
|
|
2
4
|
type LifecycleCallback = (self: DomLifecycleElement) => void;
|
|
3
5
|
type AdoptedCallback = (oldDocument: Document, newDocument: Document) => void;
|
|
@@ -59,7 +61,7 @@ type AdoptedCallback = (oldDocument: Document, newDocument: Document) => void;
|
|
|
59
61
|
* </div>
|
|
60
62
|
* ```
|
|
61
63
|
*/
|
|
62
|
-
declare class DomLifecycleElement extends
|
|
64
|
+
declare class DomLifecycleElement extends HTMLElementBase {
|
|
63
65
|
#private;
|
|
64
66
|
set onConnect(fn: LifecycleCallback | null);
|
|
65
67
|
get onConnect(): LifecycleCallback | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { d as effectScope } from "../lib-DYypKhxk.mjs";
|
|
2
2
|
import "../signals/index.mjs";
|
|
3
|
-
import { isBrowser } from "./environment.mjs";
|
|
3
|
+
import { HTMLElementBase, isBrowser } from "./environment.mjs";
|
|
4
4
|
//#region src/utilities/dom-lifecycle.ts
|
|
5
5
|
/**
|
|
6
6
|
* Custom element backing the `<dom-lifecycle>` JSX tag. Place inside any
|
|
@@ -60,7 +60,7 @@ import { isBrowser } from "./environment.mjs";
|
|
|
60
60
|
* </div>
|
|
61
61
|
* ```
|
|
62
62
|
*/
|
|
63
|
-
var DomLifecycleElement = class extends
|
|
63
|
+
var DomLifecycleElement = class extends HTMLElementBase {
|
|
64
64
|
#onConnect = null;
|
|
65
65
|
#onDisconnect = null;
|
|
66
66
|
#onMove = null;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { t as Computed } from "../index-
|
|
1
|
+
import { r as MaybeReactive, t as Computed } from "../index-BrVMftR8.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/utilities/element-rect.d.ts
|
|
4
|
-
type RectResult = {
|
|
5
|
-
x: Computed<number>;
|
|
6
|
-
y: Computed<number>;
|
|
7
|
-
width: Computed<number>;
|
|
8
|
-
height: Computed<number>;
|
|
9
|
-
top: Computed<number>;
|
|
10
|
-
right: Computed<number>;
|
|
11
|
-
bottom: Computed<number>;
|
|
12
|
-
left: Computed<number>;
|
|
13
|
-
} & Disposable;
|
|
14
4
|
/**
|
|
15
|
-
* Observes the full bounding rect of `target`
|
|
16
|
-
*
|
|
17
|
-
*
|
|
5
|
+
* Observes the full bounding rect of `target` and returns it as ONE reactive
|
|
6
|
+
* `DOMRect`, so every field a reader sees was measured at the same instant.
|
|
7
|
+
* Change sources: a `ResizeObserver` for size, plus capture-phase `scroll` and
|
|
8
|
+
* window `resize` for position — a `ResizeObserver` stays silent when an
|
|
9
|
+
* element merely moves.
|
|
10
|
+
*
|
|
11
|
+
* `target` may be a getter, in which case the observer follows it: the previous
|
|
12
|
+
* element is unobserved before the new one is observed. The returned computed
|
|
13
|
+
* keeps its identity across a swap, so consumers never rebind.
|
|
14
|
+
*
|
|
15
|
+
* The value is CACHED, not measured per read: it refreshes when one of the
|
|
16
|
+
* sources above fires, not at the moment you read it. Dispose explicitly, or
|
|
17
|
+
* let the enclosing scope do it.
|
|
18
18
|
*/
|
|
19
|
-
declare function createElementRect(target: Element):
|
|
19
|
+
declare function createElementRect(target: MaybeReactive<Element>): Computed<DOMRect> & Disposable;
|
|
20
20
|
//#endregion
|
|
21
21
|
export { createElementRect };
|
|
@@ -1,47 +1,50 @@
|
|
|
1
|
-
import { x as signal } from "../lib-DYypKhxk.mjs";
|
|
2
|
-
import "../signals/index.mjs";
|
|
1
|
+
import { _ as onCleanup, d as effectScope, l as computed, u as effect, x as signal } from "../lib-DYypKhxk.mjs";
|
|
2
|
+
import { resolve } from "../signals/index.mjs";
|
|
3
3
|
import { createResizeObserver } from "./resize-observer.mjs";
|
|
4
4
|
//#region src/utilities/element-rect.ts
|
|
5
5
|
/**
|
|
6
|
-
* Observes the full bounding rect of `target`
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Observes the full bounding rect of `target` and returns it as ONE reactive
|
|
7
|
+
* `DOMRect`, so every field a reader sees was measured at the same instant.
|
|
8
|
+
* Change sources: a `ResizeObserver` for size, plus capture-phase `scroll` and
|
|
9
|
+
* window `resize` for position — a `ResizeObserver` stays silent when an
|
|
10
|
+
* element merely moves.
|
|
11
|
+
*
|
|
12
|
+
* `target` may be a getter, in which case the observer follows it: the previous
|
|
13
|
+
* element is unobserved before the new one is observed. The returned computed
|
|
14
|
+
* keeps its identity across a swap, so consumers never rebind.
|
|
15
|
+
*
|
|
16
|
+
* The value is CACHED, not measured per read: it refreshes when one of the
|
|
17
|
+
* sources above fires, not at the moment you read it. Dispose explicitly, or
|
|
18
|
+
* let the enclosing scope do it.
|
|
9
19
|
*/
|
|
10
20
|
function createElementRect(target) {
|
|
11
|
-
const
|
|
12
|
-
const y = signal(0);
|
|
13
|
-
const width = signal(0);
|
|
14
|
-
const height = signal(0);
|
|
15
|
-
const top = signal(0);
|
|
16
|
-
const right = signal(0);
|
|
17
|
-
const bottom = signal(0);
|
|
18
|
-
const left = signal(0);
|
|
21
|
+
const cache = signal(resolve(target).getBoundingClientRect());
|
|
19
22
|
const updateRect = (el) => {
|
|
20
|
-
|
|
21
|
-
x(rect.x);
|
|
22
|
-
y(rect.y);
|
|
23
|
-
width(rect.width);
|
|
24
|
-
height(rect.height);
|
|
25
|
-
top(rect.top);
|
|
26
|
-
right(rect.right);
|
|
27
|
-
bottom(rect.bottom);
|
|
28
|
-
left(rect.left);
|
|
23
|
+
cache(el.getBoundingClientRect());
|
|
29
24
|
};
|
|
30
|
-
const
|
|
31
|
-
|
|
25
|
+
const stop = effectScope(() => {
|
|
26
|
+
effect(() => {
|
|
27
|
+
const el = resolve(target);
|
|
28
|
+
createResizeObserver(el, (entries) => {
|
|
29
|
+
for (const entry of entries) updateRect(entry.target);
|
|
30
|
+
});
|
|
31
|
+
const remeasure = () => updateRect(el);
|
|
32
|
+
window.addEventListener("scroll", remeasure, {
|
|
33
|
+
capture: true,
|
|
34
|
+
passive: true
|
|
35
|
+
});
|
|
36
|
+
window.addEventListener("resize", remeasure, { passive: true });
|
|
37
|
+
onCleanup(() => {
|
|
38
|
+
window.removeEventListener("scroll", remeasure, { capture: true });
|
|
39
|
+
window.removeEventListener("resize", remeasure);
|
|
40
|
+
});
|
|
41
|
+
updateRect(el);
|
|
42
|
+
});
|
|
32
43
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
width,
|
|
38
|
-
height,
|
|
39
|
-
top,
|
|
40
|
-
right,
|
|
41
|
-
bottom,
|
|
42
|
-
left,
|
|
43
|
-
[Symbol.dispose]: observer[Symbol.dispose]
|
|
44
|
-
};
|
|
44
|
+
onCleanup(stop);
|
|
45
|
+
const rect = computed(() => cache());
|
|
46
|
+
rect[Symbol.dispose] = stop;
|
|
47
|
+
return rect;
|
|
45
48
|
}
|
|
46
49
|
//#endregion
|
|
47
50
|
export { createElementRect };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as Signal } from "../index-
|
|
1
|
+
import { i as Signal } from "../index-BrVMftR8.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/utilities/storage.d.ts
|
|
4
4
|
type StorageOptions<T> = {
|
|
@@ -11,6 +11,9 @@ type StorageOptions<T> = {
|
|
|
11
11
|
* Changes made in other tabs/windows are synchronised automatically via
|
|
12
12
|
* the `StorageEvent`.
|
|
13
13
|
*
|
|
14
|
+
* Outside a browser — or when `localStorage` is unavailable — the signal is
|
|
15
|
+
* plain in-memory state seeded with `initialValue`; nothing is persisted.
|
|
16
|
+
*
|
|
14
17
|
* @example
|
|
15
18
|
* ```ts
|
|
16
19
|
* import { createLocalStorage } from "elements-kit/utilities/storage";
|
|
@@ -26,6 +29,9 @@ declare function createLocalStorage<T>(key: string, initialValue: T, options?: S
|
|
|
26
29
|
*
|
|
27
30
|
* Session storage is scoped to the current tab — no cross-tab sync.
|
|
28
31
|
*
|
|
32
|
+
* Outside a browser — or when `sessionStorage` is unavailable — the signal is
|
|
33
|
+
* plain in-memory state seeded with `initialValue`; nothing is persisted.
|
|
34
|
+
*
|
|
29
35
|
* @example
|
|
30
36
|
* ```ts
|
|
31
37
|
* import { createSessionStorage } from "elements-kit/utilities/storage";
|
|
@@ -1,5 +1,20 @@
|
|
|
1
|
+
import { x as signal } from "../lib-DYypKhxk.mjs";
|
|
2
|
+
import "../signals/index.mjs";
|
|
1
3
|
import { sync } from "./event-driven.mjs";
|
|
4
|
+
import { isBrowser } from "./environment.mjs";
|
|
2
5
|
//#region src/utilities/storage.ts
|
|
6
|
+
/**
|
|
7
|
+
* The requested `Storage`, or `null` outside a browser and when access throws
|
|
8
|
+
* (sandboxed iframes, blocked site data).
|
|
9
|
+
*/
|
|
10
|
+
function getStorage(area) {
|
|
11
|
+
if (!isBrowser) return null;
|
|
12
|
+
try {
|
|
13
|
+
return area === "local" ? localStorage : sessionStorage;
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
3
18
|
function readOrDefault(storage, key, initialValue, deserialise) {
|
|
4
19
|
try {
|
|
5
20
|
const item = storage.getItem(key);
|
|
@@ -7,28 +22,17 @@ function readOrDefault(storage, key, initialValue, deserialise) {
|
|
|
7
22
|
} catch {}
|
|
8
23
|
return initialValue;
|
|
9
24
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* Changes made in other tabs/windows are synchronised automatically via
|
|
14
|
-
* the `StorageEvent`.
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* import { createLocalStorage } from "elements-kit/utilities/storage";
|
|
19
|
-
*
|
|
20
|
-
* const theme = createLocalStorage<"light" | "dark">("theme", "light");
|
|
21
|
-
* theme(); // read current
|
|
22
|
-
* theme("dark"); // write — persists and notifies
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
function createLocalStorage(key, initialValue, options) {
|
|
25
|
+
function createStorageSignal(area, key, initialValue, options, crossTab) {
|
|
26
|
+
const storage = getStorage(area);
|
|
27
|
+
if (!storage) return signal(initialValue);
|
|
26
28
|
const serialise = options?.serialise ?? ((v) => JSON.stringify(v));
|
|
27
29
|
const deserialise = options?.deserialise ?? ((raw) => JSON.parse(raw));
|
|
28
|
-
const storage = localStorage;
|
|
29
30
|
let notify;
|
|
30
31
|
const subscribe = (cb) => {
|
|
31
32
|
notify = cb;
|
|
33
|
+
if (!crossTab) return () => {
|
|
34
|
+
notify = void 0;
|
|
35
|
+
};
|
|
32
36
|
const handler = (e) => {
|
|
33
37
|
if (e.key === key && e.storageArea === storage) cb();
|
|
34
38
|
};
|
|
@@ -47,10 +51,34 @@ function createLocalStorage(key, initialValue, options) {
|
|
|
47
51
|
return s;
|
|
48
52
|
}
|
|
49
53
|
/**
|
|
54
|
+
* Returns a `Signal` persisted to `localStorage`.
|
|
55
|
+
*
|
|
56
|
+
* Changes made in other tabs/windows are synchronised automatically via
|
|
57
|
+
* the `StorageEvent`.
|
|
58
|
+
*
|
|
59
|
+
* Outside a browser — or when `localStorage` is unavailable — the signal is
|
|
60
|
+
* plain in-memory state seeded with `initialValue`; nothing is persisted.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { createLocalStorage } from "elements-kit/utilities/storage";
|
|
65
|
+
*
|
|
66
|
+
* const theme = createLocalStorage<"light" | "dark">("theme", "light");
|
|
67
|
+
* theme(); // read current
|
|
68
|
+
* theme("dark"); // write — persists and notifies
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
function createLocalStorage(key, initialValue, options) {
|
|
72
|
+
return createStorageSignal("local", key, initialValue, options, true);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
50
75
|
* Returns a `Signal` persisted to `sessionStorage`.
|
|
51
76
|
*
|
|
52
77
|
* Session storage is scoped to the current tab — no cross-tab sync.
|
|
53
78
|
*
|
|
79
|
+
* Outside a browser — or when `sessionStorage` is unavailable — the signal is
|
|
80
|
+
* plain in-memory state seeded with `initialValue`; nothing is persisted.
|
|
81
|
+
*
|
|
54
82
|
* @example
|
|
55
83
|
* ```ts
|
|
56
84
|
* import { createSessionStorage } from "elements-kit/utilities/storage";
|
|
@@ -60,23 +88,7 @@ function createLocalStorage(key, initialValue, options) {
|
|
|
60
88
|
* ```
|
|
61
89
|
*/
|
|
62
90
|
function createSessionStorage(key, initialValue, options) {
|
|
63
|
-
|
|
64
|
-
const deserialise = options?.deserialise ?? ((raw) => JSON.parse(raw));
|
|
65
|
-
const storage = sessionStorage;
|
|
66
|
-
let notify;
|
|
67
|
-
const subscribe = (cb) => {
|
|
68
|
-
notify = cb;
|
|
69
|
-
return () => {
|
|
70
|
-
notify = void 0;
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
const [s] = sync(subscribe, () => readOrDefault(storage, key, initialValue, deserialise), (v) => {
|
|
74
|
-
try {
|
|
75
|
-
storage.setItem(key, serialise(v));
|
|
76
|
-
} catch {}
|
|
77
|
-
notify?.();
|
|
78
|
-
});
|
|
79
|
-
return s;
|
|
91
|
+
return createStorageSignal("session", key, initialValue, options, false);
|
|
80
92
|
}
|
|
81
93
|
//#endregion
|
|
82
94
|
export { createLocalStorage, createSessionStorage };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elements-kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.27.
|
|
4
|
+
"version": "0.27.4",
|
|
5
5
|
"description": "A lightweight reactive UI library that transforms native HTMLElements into reactive components with signals. Ideal for framework-agnostic applications and web components.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"webcomponents",
|
|
@@ -124,7 +124,8 @@
|
|
|
124
124
|
"storybook": "pnpm --filter elements-kit-storybook dev",
|
|
125
125
|
"watch": "tsdown -w",
|
|
126
126
|
"test": "vitest run",
|
|
127
|
-
"test:watch": "vitest"
|
|
127
|
+
"test:watch": "vitest",
|
|
128
|
+
"test:engines": "node scripts/engine-matrix.mjs"
|
|
128
129
|
},
|
|
129
130
|
"packageManager": "pnpm@10.33.0",
|
|
130
131
|
"devDependencies": {
|
|
@@ -138,7 +139,8 @@
|
|
|
138
139
|
"react-dom": "^19.2.5",
|
|
139
140
|
"tsdown": "0.22.0",
|
|
140
141
|
"typescript": "^6.0.3",
|
|
141
|
-
"vitest": "^4.1.2"
|
|
142
|
+
"vitest": "^4.1.2",
|
|
143
|
+
"puppeteer-core": "^24.0.0"
|
|
142
144
|
},
|
|
143
145
|
"dependencies": {
|
|
144
146
|
"@floating-ui/dom": "^1.7.6",
|