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.
Files changed (43) hide show
  1. package/dist/await.d.mts +1 -1
  2. package/dist/{children-CB8W0ANY.d.mts → children-_cMdyX7Q.d.mts} +1 -1
  3. package/dist/for.d.mts +1 -1
  4. package/dist/{index-B1FHAd5T.d.mts → index-BrVMftR8.d.mts} +4 -0
  5. package/dist/integrations/react.d.mts +18 -2
  6. package/dist/integrations/react.mjs +18 -2
  7. package/dist/jsx-runtime/dev.d.mts +2 -2
  8. package/dist/jsx-runtime/index.d.mts +2 -2
  9. package/dist/signals/index.d.mts +1 -1
  10. package/dist/signals/index.mjs +4 -0
  11. package/dist/ui/marketing/marketing.css +0 -1
  12. package/dist/ui/overlay/index.d.mts +3 -3
  13. package/dist/ui/overlay/index.mjs +6 -1
  14. package/dist/utilities/active-element.d.mts +1 -1
  15. package/dist/utilities/async.d.mts +1 -1
  16. package/dist/utilities/debounced.d.mts +1 -1
  17. package/dist/utilities/dom-lifecycle.d.mts +3 -1
  18. package/dist/utilities/dom-lifecycle.mjs +2 -2
  19. package/dist/utilities/element-rect.d.mts +15 -15
  20. package/dist/utilities/element-rect.mjs +39 -36
  21. package/dist/utilities/element-scroll.d.mts +1 -1
  22. package/dist/utilities/event-driven.d.mts +1 -1
  23. package/dist/utilities/event-listener.d.mts +1 -1
  24. package/dist/utilities/focus-within.d.mts +1 -1
  25. package/dist/utilities/hover.d.mts +1 -1
  26. package/dist/utilities/interval.d.mts +1 -1
  27. package/dist/utilities/location.d.mts +1 -1
  28. package/dist/utilities/media-devices.d.mts +1 -1
  29. package/dist/utilities/media-player.d.mts +1 -1
  30. package/dist/utilities/media-query.d.mts +1 -1
  31. package/dist/utilities/network.d.mts +1 -1
  32. package/dist/utilities/orientation.d.mts +1 -1
  33. package/dist/utilities/previous.d.mts +1 -1
  34. package/dist/utilities/promise.d.mts +1 -1
  35. package/dist/utilities/routing.d.mts +1 -1
  36. package/dist/utilities/search-params.d.mts +1 -1
  37. package/dist/utilities/storage.d.mts +7 -1
  38. package/dist/utilities/storage.mjs +46 -34
  39. package/dist/utilities/throttled.d.mts +1 -1
  40. package/dist/utilities/timeout.d.mts +1 -1
  41. package/dist/utilities/window-focus.d.mts +1 -1
  42. package/dist/utilities/window-size.d.mts +1 -1
  43. package/package.json +5 -3
package/dist/await.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { s as Props, t as Children } from "./children-CB8W0ANY.mjs";
1
+ import { s as Props, t as Children } from "./children-_cMdyX7Q.mjs";
2
2
 
3
3
  //#region src/await.d.ts
4
4
  /** @internal Metadata stamped on an Await region getter (see claim walk). */
@@ -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-B1FHAd5T.mjs";
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
@@ -1,4 +1,4 @@
1
- import { o as MaybeReactiveProps } from "./children-CB8W0ANY.mjs";
1
+ import { o as MaybeReactiveProps } from "./children-_cMdyX7Q.mjs";
2
2
 
3
3
  //#region src/for.d.ts
4
4
  type KeyFn<T> = (item: T, index: number) => string | number;
@@ -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-B1FHAd5T.mjs";
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
- }), () => value(), () => value());
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-CB8W0ANY.mjs";
2
- import { n as ComputedProps, r as MaybeReactive } from "../index-B1FHAd5T.mjs";
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-CB8W0ANY.mjs";
2
- import { n as ComputedProps, r as MaybeReactive } from "../index-B1FHAd5T.mjs";
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 };
@@ -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-B1FHAd5T.mjs";
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 };
@@ -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
@@ -522,7 +522,6 @@
522
522
  flex-flow: wrap;
523
523
  justify-content: center;
524
524
  align-items: center;
525
- gap: 1rem 1.5rem;
526
525
  width: auto;
527
526
  }
528
527
  }
@@ -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 — the shape `createElementRect` returns, so an
15
- * `Element` works directly and custom rects (a static region, a virtual
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 rect;
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,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/active-element.d.ts
4
4
  declare const activeElement: Computed<Element | null>;
@@ -1,4 +1,4 @@
1
- import { m as SEED, r as MaybeReactive, u as CLAIM } from "../index-B1FHAd5T.mjs";
1
+ import { m as SEED, r as MaybeReactive, u as CLAIM } from "../index-BrVMftR8.mjs";
2
2
  import { ComputedPromise } from "./promise.mjs";
3
3
 
4
4
  //#region src/utilities/async.d.ts
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/debounced.d.ts
4
4
  /**
@@ -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 HTMLElement {
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 HTMLElement {
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-B1FHAd5T.mjs";
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` using a `ResizeObserver` (which
16
- * fires on size changes) and returns all eight DOMRect properties as reactive
17
- * computeds.
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): RectResult;
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` using a `ResizeObserver` (which
7
- * fires on size changes) and returns all eight DOMRect properties as reactive
8
- * computeds.
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 x = signal(0);
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
- const rect = el.getBoundingClientRect();
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 observer = createResizeObserver(target, (entries) => {
31
- for (const entry of entries) updateRect(entry.target);
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
- updateRect(target);
34
- return {
35
- x,
36
- y,
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-B1FHAd5T.mjs";
1
+ import { i as Signal } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/element-scroll.d.ts
4
4
  type ElementScrollResult = {
@@ -1,4 +1,4 @@
1
- import { i as Signal, t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { i as Signal, t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/event-driven.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/event-listener.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/focus-within.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/hover.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/interval.d.ts
4
4
  type IntervalResult = {
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/location.d.ts
4
4
  type LocationResult = {
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/media-devices.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { i as Signal, t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { i as Signal, t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/media-player.d.ts
4
4
  type MediaPlayerResult<T extends HTMLMediaElement> = {
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/media-query.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/network.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/orientation.d.ts
4
4
  type OrientationResult = {
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/previous.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { m as SEED, t as Computed, u as CLAIM } from "../index-B1FHAd5T.mjs";
1
+ import { m as SEED, t as Computed, u as CLAIM } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/promise.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/routing.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/search-params.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { i as Signal } from "../index-B1FHAd5T.mjs";
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
- * Returns a `Signal` persisted to `localStorage`.
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
- const serialise = options?.serialise ?? ((v) => JSON.stringify(v));
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 };
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/throttled.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/timeout.d.ts
4
4
  type TimeoutResult = {
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/window-focus.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Computed } from "../index-B1FHAd5T.mjs";
1
+ import { t as Computed } from "../index-BrVMftR8.mjs";
2
2
 
3
3
  //#region src/utilities/window-size.d.ts
4
4
  type WindowSizeResult = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "elements-kit",
3
3
  "type": "module",
4
- "version": "0.27.2",
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",