elements-kit 0.0.9 → 0.0.11
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 +3 -4
- package/dist/builder/dom.d.mts +1 -1
- package/dist/builder/index.d.mts +87 -1
- package/dist/builder/index.mjs +1 -1
- package/dist/{element-Ddk9YaoE.mjs → element-MXzFk4G2.mjs} +1 -1
- package/dist/{index-BtqiEEfc.d.mts → index-Cvdhuy6Y.d.mts} +8 -4
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +3 -3
- package/dist/{signals/lib → integrations}/react.d.mts +2 -2
- package/dist/{signals/lib → integrations}/react.mjs +2 -2
- package/dist/jsx-runtime/index.d.mts +2 -2
- package/dist/jsx-runtime/index.mjs +3 -3
- package/dist/lib-BYJ5jPTG.d.mts +4 -0
- package/dist/polyfill-DR5XVnh_.d.mts +9 -0
- package/dist/signals/index.d.mts +2 -2
- package/dist/signals/index.mjs +2 -2
- package/dist/{signals-CLAPw8kk.mjs → signals-DcgXhkU2.mjs} +41 -9
- package/dist/slot.d.mts +1 -2
- package/dist/{test.BmQO5GaM-CR2qjV1t.mjs → test.BmQO5GaM-ANkhHvbr.mjs} +1 -1
- package/dist/utilities/active-element.d.mts +6 -0
- package/dist/utilities/active-element.mjs +13 -0
- package/dist/utilities/active-element.test.mjs +24 -0
- package/dist/{signals/lib → utilities}/debounced.d.mts +3 -3
- package/dist/{signals/lib → utilities}/debounced.mjs +7 -6
- package/dist/{signals/lib → utilities}/debounced.test.mjs +3 -3
- package/dist/{signals/lib → utilities}/element-rect.d.mts +3 -3
- package/dist/{signals/lib → utilities}/element-rect.mjs +7 -7
- package/dist/{signals/lib → utilities}/element-rect.test.mjs +3 -3
- package/dist/utilities/element-scroll.d.mts +16 -0
- package/dist/utilities/element-scroll.mjs +52 -0
- package/dist/utilities/element-scroll.test.mjs +25 -0
- package/dist/utilities/event-driven.d.mts +47 -0
- package/dist/utilities/event-driven.mjs +38 -0
- package/dist/utilities/event-listener.d.mts +16 -0
- package/dist/{signals/lib → utilities}/event-listener.mjs +4 -4
- package/dist/{signals/lib → utilities}/event-listener.test.mjs +8 -8
- package/dist/utilities/focus-within.d.mts +10 -0
- package/dist/utilities/focus-within.mjs +20 -0
- package/dist/{signals/lib/is-focus-within.test.mjs → utilities/focus-within.test.mjs} +10 -11
- package/dist/utilities/hover.d.mts +11 -0
- package/dist/utilities/hover.mjs +20 -0
- package/dist/{signals/lib → utilities}/hover.test.mjs +5 -16
- package/dist/utilities/intersection-observer.d.mts +8 -0
- package/dist/{signals/lib → utilities}/intersection-observer.mjs +3 -4
- package/dist/{signals/lib → utilities}/intersection-observer.test.mjs +3 -3
- package/dist/{signals/lib → utilities}/interval.d.mts +2 -2
- package/dist/{signals/lib → utilities}/interval.mjs +6 -5
- package/dist/{signals/lib → utilities}/interval.test.mjs +3 -3
- package/dist/utilities/location.d.mts +24 -0
- package/dist/utilities/location.mjs +54 -0
- package/dist/utilities/location.test.mjs +60 -0
- package/dist/utilities/long-press.d.mts +10 -0
- package/dist/utilities/long-press.mjs +27 -0
- package/dist/{signals/lib → utilities}/long-press.test.mjs +3 -3
- package/dist/{signals/lib → utilities}/media-devices.d.mts +3 -3
- package/dist/{signals/lib → utilities}/media-devices.mjs +5 -5
- package/dist/{signals/lib → utilities}/media-devices.test.mjs +6 -8
- package/dist/utilities/media-player.d.mts +22 -0
- package/dist/utilities/media-player.mjs +36 -0
- package/dist/utilities/media-player.test.mjs +100 -0
- package/dist/{signals/lib/media.d.mts → utilities/media-query.d.mts} +4 -4
- package/dist/utilities/media-query.mjs +19 -0
- package/dist/utilities/mutation-observer.d.mts +8 -0
- package/dist/{signals/lib → utilities}/mutation-observer.mjs +3 -3
- package/dist/{signals/lib → utilities}/mutation-observer.test.mjs +3 -3
- package/dist/{signals/lib → utilities}/on-click-outside.d.mts +3 -3
- package/dist/utilities/on-click-outside.mjs +17 -0
- package/dist/{signals/lib → utilities}/on-click-outside.test.mjs +7 -18
- package/dist/utilities/orientation.d.mts +10 -0
- package/dist/utilities/orientation.mjs +21 -0
- package/dist/utilities/previous.d.mts +12 -0
- package/dist/utilities/previous.mjs +24 -0
- package/dist/utilities/previous.test.mjs +88 -0
- package/dist/utilities/resize-observer.d.mts +8 -0
- package/dist/{signals/lib → utilities}/resize-observer.mjs +4 -5
- package/dist/utilities/routing.d.mts +59 -0
- package/dist/utilities/routing.mjs +89 -0
- package/dist/utilities/routing.test.mjs +128 -0
- package/dist/utilities/search-params.d.mts +13 -0
- package/dist/utilities/search-params.mjs +23 -0
- package/dist/utilities/search-params.test.mjs +35 -0
- package/dist/utilities/storage.d.mts +22 -0
- package/dist/utilities/storage.mjs +65 -0
- package/dist/utilities/storage.test.mjs +137 -0
- package/dist/{signals/lib → utilities}/throttled.d.mts +2 -2
- package/dist/utilities/throttled.mjs +36 -0
- package/dist/{signals/lib → utilities}/throttled.test.mjs +3 -3
- package/dist/{signals/lib → utilities}/timeout.d.mts +6 -5
- package/dist/utilities/timeout.mjs +42 -0
- package/dist/{signals/lib → utilities}/timeout.test.mjs +7 -7
- package/dist/utilities/window-size.d.mts +10 -0
- package/dist/utilities/window-size.mjs +22 -0
- package/dist/utilities/window-size.test.mjs +42 -0
- package/package.json +11 -6
- package/dist/index-CKjDUp1B.d.mts +0 -89
- package/dist/polyfill-AFIi9kAN.d.mts +0 -14
- package/dist/signals/lib/active-element.d.mts +0 -10
- package/dist/signals/lib/active-element.mjs +0 -20
- package/dist/signals/lib/active-element.test.mjs +0 -39
- package/dist/signals/lib/animation-frames.d.mts +0 -18
- package/dist/signals/lib/animation-frames.mjs +0 -48
- package/dist/signals/lib/animation-frames.test.mjs +0 -52
- package/dist/signals/lib/async-retry.d.mts +0 -21
- package/dist/signals/lib/async-retry.mjs +0 -57
- package/dist/signals/lib/async-retry.test.mjs +0 -57
- package/dist/signals/lib/audio.d.mts +0 -26
- package/dist/signals/lib/audio.mjs +0 -60
- package/dist/signals/lib/audio.test.mjs +0 -54
- package/dist/signals/lib/battery.d.mts +0 -17
- package/dist/signals/lib/battery.mjs +0 -45
- package/dist/signals/lib/battery.test.mjs +0 -38
- package/dist/signals/lib/before-unload.d.mts +0 -11
- package/dist/signals/lib/before-unload.mjs +0 -20
- package/dist/signals/lib/before-unload.test.mjs +0 -29
- package/dist/signals/lib/clipboard.d.mts +0 -15
- package/dist/signals/lib/clipboard.mjs +0 -25
- package/dist/signals/lib/clipboard.test.mjs +0 -53
- package/dist/signals/lib/counter.d.mts +0 -18
- package/dist/signals/lib/counter.mjs +0 -21
- package/dist/signals/lib/counter.test.d.mts +0 -1
- package/dist/signals/lib/counter.test.mjs +0 -74
- package/dist/signals/lib/document-title.d.mts +0 -10
- package/dist/signals/lib/document-title.mjs +0 -15
- package/dist/signals/lib/document-title.test.d.mts +0 -1
- package/dist/signals/lib/document-title.test.mjs +0 -33
- package/dist/signals/lib/element-size.d.mts +0 -16
- package/dist/signals/lib/element-size.mjs +0 -32
- package/dist/signals/lib/element-size.test.d.mts +0 -1
- package/dist/signals/lib/element-size.test.mjs +0 -86
- package/dist/signals/lib/event-listener.d.mts +0 -14
- package/dist/signals/lib/favicon.d.mts +0 -10
- package/dist/signals/lib/favicon.mjs +0 -24
- package/dist/signals/lib/favicon.test.d.mts +0 -1
- package/dist/signals/lib/favicon.test.mjs +0 -28
- package/dist/signals/lib/finite-state-machine.d.mts +0 -22
- package/dist/signals/lib/finite-state-machine.mjs +0 -26
- package/dist/signals/lib/finite-state-machine.test.d.mts +0 -1
- package/dist/signals/lib/finite-state-machine.test.mjs +0 -66
- package/dist/signals/lib/fullscreen.d.mts +0 -15
- package/dist/signals/lib/fullscreen.mjs +0 -26
- package/dist/signals/lib/fullscreen.test.d.mts +0 -1
- package/dist/signals/lib/fullscreen.test.mjs +0 -56
- package/dist/signals/lib/geolocation.d.mts +0 -15
- package/dist/signals/lib/geolocation.mjs +0 -30
- package/dist/signals/lib/geolocation.test.d.mts +0 -1
- package/dist/signals/lib/geolocation.test.mjs +0 -37
- package/dist/signals/lib/hash.d.mts +0 -10
- package/dist/signals/lib/hash.mjs +0 -20
- package/dist/signals/lib/hash.test.d.mts +0 -1
- package/dist/signals/lib/hash.test.mjs +0 -47
- package/dist/signals/lib/hover.d.mts +0 -11
- package/dist/signals/lib/hover.mjs +0 -23
- package/dist/signals/lib/intersection-observer.d.mts +0 -8
- package/dist/signals/lib/is-document-visible.d.mts +0 -11
- package/dist/signals/lib/is-document-visible.mjs +0 -19
- package/dist/signals/lib/is-document-visible.test.d.mts +0 -1
- package/dist/signals/lib/is-document-visible.test.mjs +0 -58
- package/dist/signals/lib/is-focus-within.d.mts +0 -10
- package/dist/signals/lib/is-focus-within.mjs +0 -28
- package/dist/signals/lib/is-focus-within.test.d.mts +0 -1
- package/dist/signals/lib/is-idle.d.mts +0 -10
- package/dist/signals/lib/is-idle.mjs +0 -37
- package/dist/signals/lib/is-idle.test.d.mts +0 -1
- package/dist/signals/lib/is-idle.test.mjs +0 -50
- package/dist/signals/lib/is-in-viewport.d.mts +0 -10
- package/dist/signals/lib/is-in-viewport.mjs +0 -16
- package/dist/signals/lib/is-in-viewport.test.d.mts +0 -1
- package/dist/signals/lib/is-in-viewport.test.mjs +0 -74
- package/dist/signals/lib/key-press.d.mts +0 -13
- package/dist/signals/lib/key-press.mjs +0 -25
- package/dist/signals/lib/key-press.test.d.mts +0 -1
- package/dist/signals/lib/key-press.test.mjs +0 -52
- package/dist/signals/lib/list.d.mts +0 -19
- package/dist/signals/lib/list.mjs +0 -36
- package/dist/signals/lib/list.test.d.mts +0 -1
- package/dist/signals/lib/list.test.mjs +0 -104
- package/dist/signals/lib/lock-body-scroll.d.mts +0 -8
- package/dist/signals/lib/lock-body-scroll.mjs +0 -17
- package/dist/signals/lib/lock-body-scroll.test.d.mts +0 -1
- package/dist/signals/lib/lock-body-scroll.test.mjs +0 -37
- package/dist/signals/lib/long-press.d.mts +0 -10
- package/dist/signals/lib/long-press.mjs +0 -29
- package/dist/signals/lib/map.d.mts +0 -18
- package/dist/signals/lib/map.mjs +0 -33
- package/dist/signals/lib/map.test.d.mts +0 -1
- package/dist/signals/lib/map.test.mjs +0 -60
- package/dist/signals/lib/media.mjs +0 -26
- package/dist/signals/lib/motion.d.mts +0 -15
- package/dist/signals/lib/motion.mjs +0 -27
- package/dist/signals/lib/motion.test.d.mts +0 -1
- package/dist/signals/lib/motion.test.mjs +0 -51
- package/dist/signals/lib/mouse-position.d.mts +0 -14
- package/dist/signals/lib/mouse-position.mjs +0 -22
- package/dist/signals/lib/mouse-position.test.d.mts +0 -1
- package/dist/signals/lib/mouse-position.test.mjs +0 -44
- package/dist/signals/lib/mouse-wheel.d.mts +0 -10
- package/dist/signals/lib/mouse-wheel.mjs +0 -17
- package/dist/signals/lib/mouse-wheel.test.d.mts +0 -1
- package/dist/signals/lib/mouse-wheel.test.mjs +0 -38
- package/dist/signals/lib/mutation-observer.d.mts +0 -8
- package/dist/signals/lib/network-state.d.mts +0 -17
- package/dist/signals/lib/network-state.mjs +0 -34
- package/dist/signals/lib/network-state.test.d.mts +0 -1
- package/dist/signals/lib/network-state.test.mjs +0 -61
- package/dist/signals/lib/on-click-outside.mjs +0 -20
- package/dist/signals/lib/orientation.d.mts +0 -13
- package/dist/signals/lib/orientation.mjs +0 -21
- package/dist/signals/lib/orientation.test.d.mts +0 -1
- package/dist/signals/lib/orientation.test.mjs +0 -43
- package/dist/signals/lib/page-leave.d.mts +0 -8
- package/dist/signals/lib/page-leave.mjs +0 -12
- package/dist/signals/lib/page-leave.test.d.mts +0 -1
- package/dist/signals/lib/page-leave.test.mjs +0 -29
- package/dist/signals/lib/permission.d.mts +0 -14
- package/dist/signals/lib/permission.mjs +0 -26
- package/dist/signals/lib/permission.test.d.mts +0 -1
- package/dist/signals/lib/permission.test.mjs +0 -50
- package/dist/signals/lib/persisted-state.d.mts +0 -11
- package/dist/signals/lib/persisted-state.mjs +0 -25
- package/dist/signals/lib/persisted-state.test.d.mts +0 -1
- package/dist/signals/lib/persisted-state.test.mjs +0 -70
- package/dist/signals/lib/pressed-keys.d.mts +0 -10
- package/dist/signals/lib/pressed-keys.mjs +0 -32
- package/dist/signals/lib/pressed-keys.test.d.mts +0 -1
- package/dist/signals/lib/pressed-keys.test.mjs +0 -54
- package/dist/signals/lib/previous-distinct.d.mts +0 -10
- package/dist/signals/lib/previous-distinct.mjs +0 -22
- package/dist/signals/lib/previous-distinct.test.d.mts +0 -1
- package/dist/signals/lib/previous-distinct.test.mjs +0 -50
- package/dist/signals/lib/previous.d.mts +0 -10
- package/dist/signals/lib/previous.mjs +0 -18
- package/dist/signals/lib/previous.test.mjs +0 -47
- package/dist/signals/lib/queue.d.mts +0 -17
- package/dist/signals/lib/queue.mjs +0 -28
- package/dist/signals/lib/queue.test.d.mts +0 -1
- package/dist/signals/lib/queue.test.mjs +0 -61
- package/dist/signals/lib/raf.d.mts +0 -17
- package/dist/signals/lib/raf.mjs +0 -38
- package/dist/signals/lib/raf.test.d.mts +0 -1
- package/dist/signals/lib/raf.test.mjs +0 -58
- package/dist/signals/lib/resize-observer.d.mts +0 -8
- package/dist/signals/lib/resize-observer.test.d.mts +0 -1
- package/dist/signals/lib/resize-observer.test.mjs +0 -44
- package/dist/signals/lib/resource.d.mts +0 -23
- package/dist/signals/lib/resource.mjs +0 -43
- package/dist/signals/lib/resource.test.d.mts +0 -1
- package/dist/signals/lib/resource.test.mjs +0 -56
- package/dist/signals/lib/scroll-state.d.mts +0 -19
- package/dist/signals/lib/scroll-state.mjs +0 -46
- package/dist/signals/lib/scroll-state.test.d.mts +0 -1
- package/dist/signals/lib/scroll-state.test.mjs +0 -94
- package/dist/signals/lib/scrolling.d.mts +0 -12
- package/dist/signals/lib/scrolling.mjs +0 -26
- package/dist/signals/lib/scrolling.test.d.mts +0 -1
- package/dist/signals/lib/scrolling.test.mjs +0 -57
- package/dist/signals/lib/search-params.d.mts +0 -16
- package/dist/signals/lib/search-params.mjs +0 -37
- package/dist/signals/lib/search-params.test.mjs +0 -56
- package/dist/signals/lib/set.d.mts +0 -18
- package/dist/signals/lib/set.mjs +0 -38
- package/dist/signals/lib/set.test.d.mts +0 -1
- package/dist/signals/lib/set.test.mjs +0 -70
- package/dist/signals/lib/start-typing.d.mts +0 -9
- package/dist/signals/lib/start-typing.mjs +0 -39
- package/dist/signals/lib/start-typing.test.d.mts +0 -1
- package/dist/signals/lib/start-typing.test.mjs +0 -64
- package/dist/signals/lib/state-history.d.mts +0 -21
- package/dist/signals/lib/state-history.mjs +0 -61
- package/dist/signals/lib/state-history.test.d.mts +0 -1
- package/dist/signals/lib/state-history.test.mjs +0 -106
- package/dist/signals/lib/state-validator.d.mts +0 -16
- package/dist/signals/lib/state-validator.mjs +0 -21
- package/dist/signals/lib/state-validator.test.d.mts +0 -1
- package/dist/signals/lib/state-validator.test.mjs +0 -41
- package/dist/signals/lib/throttled.mjs +0 -45
- package/dist/signals/lib/timeout.mjs +0 -39
- package/dist/signals/lib/toggle.d.mts +0 -12
- package/dist/signals/lib/toggle.mjs +0 -12
- package/dist/signals/lib/toggle.test.d.mts +0 -1
- package/dist/signals/lib/toggle.test.mjs +0 -43
- package/dist/signals/lib/video.d.mts +0 -25
- package/dist/signals/lib/video.mjs +0 -59
- package/dist/signals/lib/video.test.d.mts +0 -1
- package/dist/signals/lib/video.test.mjs +0 -51
- package/dist/signals/lib/watch.d.mts +0 -16
- package/dist/signals/lib/watch.mjs +0 -31
- package/dist/signals/lib/watch.test.d.mts +0 -1
- package/dist/signals/lib/watch.test.mjs +0 -51
- package/dist/signals/lib/window-size.d.mts +0 -14
- package/dist/signals/lib/window-size.mjs +0 -22
- package/dist/signals/lib/window-size.test.mjs +0 -50
- /package/dist/{signals/lib → utilities}/active-element.test.d.mts +0 -0
- /package/dist/{signals/lib → utilities}/debounced.test.d.mts +0 -0
- /package/dist/{signals/lib → utilities}/element-rect.test.d.mts +0 -0
- /package/dist/{signals/lib/animation-frames.test.d.mts → utilities/element-scroll.test.d.mts} +0 -0
- /package/dist/{signals/lib → utilities}/event-listener.test.d.mts +0 -0
- /package/dist/{signals/lib/async-retry.test.d.mts → utilities/focus-within.test.d.mts} +0 -0
- /package/dist/{signals/lib → utilities}/hover.test.d.mts +0 -0
- /package/dist/{signals/lib → utilities}/intersection-observer.test.d.mts +0 -0
- /package/dist/{signals/lib → utilities}/interval.test.d.mts +0 -0
- /package/dist/{signals/lib/audio.test.d.mts → utilities/location.test.d.mts} +0 -0
- /package/dist/{signals/lib → utilities}/long-press.test.d.mts +0 -0
- /package/dist/{signals/lib → utilities}/media-devices.test.d.mts +0 -0
- /package/dist/{signals/lib/battery.test.d.mts → utilities/media-player.test.d.mts} +0 -0
- /package/dist/{signals/lib → utilities}/mutation-observer.test.d.mts +0 -0
- /package/dist/{signals/lib → utilities}/on-click-outside.test.d.mts +0 -0
- /package/dist/{signals/lib → utilities}/previous.test.d.mts +0 -0
- /package/dist/{signals/lib/before-unload.test.d.mts → utilities/routing.test.d.mts} +0 -0
- /package/dist/{signals/lib → utilities}/search-params.test.d.mts +0 -0
- /package/dist/{signals/lib/clipboard.test.d.mts → utilities/storage.test.d.mts} +0 -0
- /package/dist/{signals/lib → utilities}/throttled.test.d.mts +0 -0
- /package/dist/{signals/lib → utilities}/timeout.test.d.mts +0 -0
- /package/dist/{signals/lib → utilities}/window-size.test.d.mts +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { a as computed } from "../signals-DcgXhkU2.mjs";
|
|
2
|
+
import { currentLocation } from "./location.mjs";
|
|
3
|
+
//#region src/utilities/routing.ts
|
|
4
|
+
const patchHistoryMethod = (method) => {
|
|
5
|
+
const original = history[method];
|
|
6
|
+
history[method] = function(data, unused, url) {
|
|
7
|
+
const result = original.apply(this, [
|
|
8
|
+
data,
|
|
9
|
+
unused,
|
|
10
|
+
url
|
|
11
|
+
]);
|
|
12
|
+
const event = new Event(method.toLowerCase());
|
|
13
|
+
event.state = data;
|
|
14
|
+
window.dispatchEvent(event);
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
function patchHistory() {
|
|
19
|
+
if (typeof window !== "undefined" && typeof history !== "undefined") {
|
|
20
|
+
patchHistoryMethod("pushState");
|
|
21
|
+
patchHistoryMethod("replaceState");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Navigates to `url` via `history.pushState` (or `replaceState` when
|
|
26
|
+
* `replace` is `true`).
|
|
27
|
+
*
|
|
28
|
+
* `history.pushState` / `replaceState` are patched once at module load so
|
|
29
|
+
* all navigation — including third-party router calls — dispatches the
|
|
30
|
+
* `pushstate` / `replacestate` events that `currentLocation` signals react to.
|
|
31
|
+
*/
|
|
32
|
+
function navigate(url, { replace = false, state = null } = {}) {
|
|
33
|
+
if (replace) history.replaceState(state, "", url);
|
|
34
|
+
else history.pushState(state, "", url);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns `true` when a click event on an `<a>` element should be handled
|
|
38
|
+
* client-side — same origin, primary button, no modifier keys, no download,
|
|
39
|
+
* no `_blank` target. Walks up to the nearest anchor via `closest("a")`.
|
|
40
|
+
*
|
|
41
|
+
* Use alongside `navigate()` to intercept anchor clicks:
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* el.addEventListener("click", (e) => {
|
|
45
|
+
* if (isLocalNavigationEvent(e)) { e.preventDefault(); navigate(el.href); }
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
function isLocalNavigationEvent(e) {
|
|
50
|
+
if (e.button !== 0) return false;
|
|
51
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return false;
|
|
52
|
+
const el = e.currentTarget?.closest("a") ?? e.target?.closest("a");
|
|
53
|
+
if (!el) return false;
|
|
54
|
+
if (el.hasAttribute("download")) return false;
|
|
55
|
+
if (el.getAttribute("target") === "_blank") return false;
|
|
56
|
+
if (new URL(el.href, location.href).origin !== location.origin) return false;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Returns `Computed<boolean>` — `true` when the current URL matches `input`.
|
|
61
|
+
* Uses `URLPattern.test()` — faster than `match` when you don't need
|
|
62
|
+
* captured groups.
|
|
63
|
+
*
|
|
64
|
+
* Use object form: `{ pathname: "/users/:id" }`. Relative string patterns
|
|
65
|
+
* require a base URL and will throw.
|
|
66
|
+
*
|
|
67
|
+
* Requires `urlpattern-polyfill` for Safari < 26 and Firefox < 142.
|
|
68
|
+
*/
|
|
69
|
+
function matches(input, options) {
|
|
70
|
+
const pattern = options ? new URLPattern(input, options) : new URLPattern(input);
|
|
71
|
+
return computed(() => pattern.test(currentLocation.href()));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Returns `Computed<URLPatternResult | null>` — the full match result when
|
|
75
|
+
* the current URL matches `input`, `null` when it does not.
|
|
76
|
+
* Use when you need captured groups (params). For a boolean gate,
|
|
77
|
+
* prefer `matches()` which is faster.
|
|
78
|
+
*
|
|
79
|
+
* Use object form: `{ pathname: "/users/:id" }`. Relative string patterns
|
|
80
|
+
* require a base URL and will throw.
|
|
81
|
+
*
|
|
82
|
+
* Requires `urlpattern-polyfill` for Safari < 26 and Firefox < 142.
|
|
83
|
+
*/
|
|
84
|
+
function match(input, options) {
|
|
85
|
+
const pattern = options ? new URLPattern(input, options) : new URLPattern(input);
|
|
86
|
+
return computed(() => pattern.exec(currentLocation.href()));
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { isLocalNavigationEvent, match, matches, navigate, patchHistory };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { i as beforeAll, n as vi, o as describe, r as afterEach, s as it, t as globalExpect } from "../test.BmQO5GaM-ANkhHvbr.mjs";
|
|
2
|
+
import { isLocalNavigationEvent, match, matches, navigate, patchHistory } from "./routing.mjs";
|
|
3
|
+
//#region src/utilities/routing.test.ts
|
|
4
|
+
beforeAll(() => {
|
|
5
|
+
patchHistory();
|
|
6
|
+
});
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
history.replaceState(null, "", "/");
|
|
9
|
+
});
|
|
10
|
+
describe("navigate", () => {
|
|
11
|
+
it("pushes a new history entry", () => {
|
|
12
|
+
navigate("/about");
|
|
13
|
+
globalExpect(location.pathname).toBe("/about");
|
|
14
|
+
});
|
|
15
|
+
it("replaces the current entry when replace is true", () => {
|
|
16
|
+
const before = history.length;
|
|
17
|
+
navigate("/replaced", { replace: true });
|
|
18
|
+
globalExpect(location.pathname).toBe("/replaced");
|
|
19
|
+
globalExpect(history.length).toBe(before);
|
|
20
|
+
});
|
|
21
|
+
it("dispatches pushstate event so currentLocation updates", () => {
|
|
22
|
+
const handler = vi.fn();
|
|
23
|
+
window.addEventListener("pushstate", handler);
|
|
24
|
+
navigate("/dispatched");
|
|
25
|
+
window.removeEventListener("pushstate", handler);
|
|
26
|
+
globalExpect(handler).toHaveBeenCalledOnce();
|
|
27
|
+
});
|
|
28
|
+
it("dispatches replacestate event on replace", () => {
|
|
29
|
+
const handler = vi.fn();
|
|
30
|
+
window.addEventListener("replacestate", handler);
|
|
31
|
+
navigate("/dispatched", { replace: true });
|
|
32
|
+
window.removeEventListener("replacestate", handler);
|
|
33
|
+
globalExpect(handler).toHaveBeenCalledOnce();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("isLocalNavigationEvent", () => {
|
|
37
|
+
function makeClick(el, init = {}) {
|
|
38
|
+
const e = new MouseEvent("click", {
|
|
39
|
+
bubbles: true,
|
|
40
|
+
cancelable: true,
|
|
41
|
+
...init
|
|
42
|
+
});
|
|
43
|
+
Object.defineProperty(e, "currentTarget", {
|
|
44
|
+
value: el,
|
|
45
|
+
configurable: true
|
|
46
|
+
});
|
|
47
|
+
return e;
|
|
48
|
+
}
|
|
49
|
+
it("returns true for a same-origin link", () => {
|
|
50
|
+
const el = document.createElement("a");
|
|
51
|
+
el.href = "/linked";
|
|
52
|
+
globalExpect(isLocalNavigationEvent(makeClick(el))).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it("returns false for an external link", () => {
|
|
55
|
+
const el = document.createElement("a");
|
|
56
|
+
el.href = "https://example.com/page";
|
|
57
|
+
globalExpect(isLocalNavigationEvent(makeClick(el))).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
it("returns false when metaKey is held", () => {
|
|
60
|
+
const el = document.createElement("a");
|
|
61
|
+
el.href = "/about";
|
|
62
|
+
globalExpect(isLocalNavigationEvent(makeClick(el, { metaKey: true }))).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
it("returns false when ctrlKey is held", () => {
|
|
65
|
+
const el = document.createElement("a");
|
|
66
|
+
el.href = "/about";
|
|
67
|
+
globalExpect(isLocalNavigationEvent(makeClick(el, { ctrlKey: true }))).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
it("returns false for download links", () => {
|
|
70
|
+
const el = document.createElement("a");
|
|
71
|
+
el.href = "/file.pdf";
|
|
72
|
+
el.setAttribute("download", "");
|
|
73
|
+
globalExpect(isLocalNavigationEvent(makeClick(el))).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
it("returns false for _blank links", () => {
|
|
76
|
+
const el = document.createElement("a");
|
|
77
|
+
el.href = "/page";
|
|
78
|
+
el.setAttribute("target", "_blank");
|
|
79
|
+
globalExpect(isLocalNavigationEvent(makeClick(el))).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
it("returns false for non-primary button clicks", () => {
|
|
82
|
+
const el = document.createElement("a");
|
|
83
|
+
el.href = "/page";
|
|
84
|
+
globalExpect(isLocalNavigationEvent(makeClick(el, { button: 1 }))).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("matches", () => {
|
|
88
|
+
it("returns true when current URL matches", () => {
|
|
89
|
+
navigate("/users/42");
|
|
90
|
+
globalExpect(matches({ pathname: "/users/:id" })()).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
it("returns false when current URL does not match", () => {
|
|
93
|
+
navigate("/other");
|
|
94
|
+
globalExpect(matches({ pathname: "/users/:id" })()).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
it("updates reactively on navigation", () => {
|
|
97
|
+
const result = matches({ pathname: "/home" });
|
|
98
|
+
navigate("/other");
|
|
99
|
+
globalExpect(result()).toBe(false);
|
|
100
|
+
navigate("/home");
|
|
101
|
+
globalExpect(result()).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe("match", () => {
|
|
105
|
+
it("returns null when current URL does not match", () => {
|
|
106
|
+
navigate("/other");
|
|
107
|
+
globalExpect(match({ pathname: "/users/:id" })()).toBeNull();
|
|
108
|
+
});
|
|
109
|
+
it("returns URLPatternResult with groups when matched", () => {
|
|
110
|
+
navigate("/users/42");
|
|
111
|
+
globalExpect(match({ pathname: "/users/:id" })()?.pathname.groups.id).toBe("42");
|
|
112
|
+
});
|
|
113
|
+
it("updates reactively on navigation", () => {
|
|
114
|
+
const result = match({ pathname: "/posts/:slug" });
|
|
115
|
+
navigate("/posts/hello");
|
|
116
|
+
globalExpect(result()?.pathname.groups.slug).toBe("hello");
|
|
117
|
+
navigate("/other");
|
|
118
|
+
globalExpect(result()).toBeNull();
|
|
119
|
+
});
|
|
120
|
+
it("extracts multiple params", () => {
|
|
121
|
+
navigate("/posts/2024/my-slug");
|
|
122
|
+
const result = match({ pathname: "/posts/:year/:slug" });
|
|
123
|
+
globalExpect(result()?.pathname.groups.year).toBe("2024");
|
|
124
|
+
globalExpect(result()?.pathname.groups.slug).toBe("my-slug");
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
//#endregion
|
|
128
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { t as Computed } from "../index-Cvdhuy6Y.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/utilities/search-params.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Returns a read-only `Computed<string | null>` bound to a single URL search
|
|
6
|
+
* parameter.
|
|
7
|
+
*
|
|
8
|
+
* Returns the raw string value, or `null` when absent.
|
|
9
|
+
* Reacts to `popstate` so back/forward navigation is reflected.
|
|
10
|
+
*/
|
|
11
|
+
declare function createSearchParam(key: string): Computed<string | null>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { createSearchParam };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { fromEvent, sync } from "./event-driven.mjs";
|
|
2
|
+
//#region src/utilities/search-params.ts
|
|
3
|
+
/**
|
|
4
|
+
* Returns a read-only `Computed<string | null>` bound to a single URL search
|
|
5
|
+
* parameter.
|
|
6
|
+
*
|
|
7
|
+
* Returns the raw string value, or `null` when absent.
|
|
8
|
+
* Reacts to `popstate` so back/forward navigation is reflected.
|
|
9
|
+
*/
|
|
10
|
+
function createSearchParam(key) {
|
|
11
|
+
const read = () => {
|
|
12
|
+
if (typeof location === "undefined") return null;
|
|
13
|
+
return new URLSearchParams(location.search).get(key);
|
|
14
|
+
};
|
|
15
|
+
const [s] = sync(fromEvent(window, [
|
|
16
|
+
"popstate",
|
|
17
|
+
"pushstate",
|
|
18
|
+
"replacestate"
|
|
19
|
+
]), read);
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { createSearchParam };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { s as effectScope } from "../signals-DcgXhkU2.mjs";
|
|
2
|
+
import { o as describe, r as afterEach, s as it, t as globalExpect } from "../test.BmQO5GaM-ANkhHvbr.mjs";
|
|
3
|
+
import { createSearchParam } from "./search-params.mjs";
|
|
4
|
+
//#region src/utilities/search-params.test.ts
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
history.replaceState(null, "", location.pathname);
|
|
7
|
+
});
|
|
8
|
+
describe("createSearchParam", () => {
|
|
9
|
+
it("returns null when param is absent", () => {
|
|
10
|
+
let s;
|
|
11
|
+
effectScope(() => {
|
|
12
|
+
s = createSearchParam("q");
|
|
13
|
+
});
|
|
14
|
+
globalExpect(s()).toBeNull();
|
|
15
|
+
});
|
|
16
|
+
it("reads an existing param from the URL", () => {
|
|
17
|
+
history.replaceState(null, "", "?q=hello");
|
|
18
|
+
let s;
|
|
19
|
+
effectScope(() => {
|
|
20
|
+
s = createSearchParam("q");
|
|
21
|
+
});
|
|
22
|
+
globalExpect(s()).toBe("hello");
|
|
23
|
+
});
|
|
24
|
+
it("reacts to popstate events", () => {
|
|
25
|
+
let s;
|
|
26
|
+
effectScope(() => {
|
|
27
|
+
s = createSearchParam("q");
|
|
28
|
+
});
|
|
29
|
+
history.replaceState(null, "", "?q=changed");
|
|
30
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
31
|
+
globalExpect(s()).toBe("changed");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//#endregion
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { r as Signal } from "../index-Cvdhuy6Y.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/utilities/storage.d.ts
|
|
4
|
+
type StorageOptions<T> = {
|
|
5
|
+
/** Custom serialiser (default `JSON.stringify`). */serialise?: (value: T) => string; /** Custom deserialiser (default `JSON.parse`). */
|
|
6
|
+
deserialise?: (raw: string) => T;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Returns a `Signal` persisted to `localStorage`.
|
|
10
|
+
*
|
|
11
|
+
* Changes made in other tabs/windows are synchronised automatically via
|
|
12
|
+
* the `StorageEvent`.
|
|
13
|
+
*/
|
|
14
|
+
declare function createLocalStorage<T>(key: string, initialValue: T, options?: StorageOptions<T>): Signal<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Returns a `Signal` persisted to `sessionStorage`.
|
|
17
|
+
*
|
|
18
|
+
* Session storage is scoped to the current tab — no cross-tab sync.
|
|
19
|
+
*/
|
|
20
|
+
declare function createSessionStorage<T>(key: string, initialValue: T, options?: StorageOptions<T>): Signal<T>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { createLocalStorage, createSessionStorage };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { sync } from "./event-driven.mjs";
|
|
2
|
+
//#region src/utilities/storage.ts
|
|
3
|
+
function readOrDefault(storage, key, initialValue, deserialise) {
|
|
4
|
+
try {
|
|
5
|
+
const item = storage.getItem(key);
|
|
6
|
+
if (item !== null) return deserialise(item);
|
|
7
|
+
} catch {}
|
|
8
|
+
return initialValue;
|
|
9
|
+
}
|
|
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
|
+
function createLocalStorage(key, initialValue, options) {
|
|
17
|
+
const serialise = options?.serialise ?? ((v) => JSON.stringify(v));
|
|
18
|
+
const deserialise = options?.deserialise ?? ((raw) => JSON.parse(raw));
|
|
19
|
+
const storage = localStorage;
|
|
20
|
+
let notify;
|
|
21
|
+
const subscribe = (cb) => {
|
|
22
|
+
notify = cb;
|
|
23
|
+
const handler = (e) => {
|
|
24
|
+
if (e.key === key && e.storageArea === storage) cb();
|
|
25
|
+
};
|
|
26
|
+
window.addEventListener("storage", handler);
|
|
27
|
+
return () => {
|
|
28
|
+
window.removeEventListener("storage", handler);
|
|
29
|
+
notify = void 0;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const [s] = sync(subscribe, () => readOrDefault(storage, key, initialValue, deserialise), (v) => {
|
|
33
|
+
try {
|
|
34
|
+
storage.setItem(key, serialise(v));
|
|
35
|
+
} catch {}
|
|
36
|
+
notify?.();
|
|
37
|
+
});
|
|
38
|
+
return s;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns a `Signal` persisted to `sessionStorage`.
|
|
42
|
+
*
|
|
43
|
+
* Session storage is scoped to the current tab — no cross-tab sync.
|
|
44
|
+
*/
|
|
45
|
+
function createSessionStorage(key, initialValue, options) {
|
|
46
|
+
const serialise = options?.serialise ?? ((v) => JSON.stringify(v));
|
|
47
|
+
const deserialise = options?.deserialise ?? ((raw) => JSON.parse(raw));
|
|
48
|
+
const storage = sessionStorage;
|
|
49
|
+
let notify;
|
|
50
|
+
const subscribe = (cb) => {
|
|
51
|
+
notify = cb;
|
|
52
|
+
return () => {
|
|
53
|
+
notify = void 0;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
const [s] = sync(subscribe, () => readOrDefault(storage, key, initialValue, deserialise), (v) => {
|
|
57
|
+
try {
|
|
58
|
+
storage.setItem(key, serialise(v));
|
|
59
|
+
} catch {}
|
|
60
|
+
notify?.();
|
|
61
|
+
});
|
|
62
|
+
return s;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { createLocalStorage, createSessionStorage };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { s as effectScope } from "../signals-DcgXhkU2.mjs";
|
|
2
|
+
import { o as describe, r as afterEach, s as it, t as globalExpect } from "../test.BmQO5GaM-ANkhHvbr.mjs";
|
|
3
|
+
import { createLocalStorage, createSessionStorage } from "./storage.mjs";
|
|
4
|
+
//#region src/utilities/storage.test.ts
|
|
5
|
+
describe("createLocalStorage", () => {
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
localStorage.clear();
|
|
8
|
+
});
|
|
9
|
+
it("uses initialValue when storage is empty", () => {
|
|
10
|
+
let s;
|
|
11
|
+
effectScope(() => {
|
|
12
|
+
s = createLocalStorage("count", 0);
|
|
13
|
+
});
|
|
14
|
+
globalExpect(s()).toBe(0);
|
|
15
|
+
});
|
|
16
|
+
it("reads an existing value from localStorage", () => {
|
|
17
|
+
localStorage.setItem("count", "42");
|
|
18
|
+
let s;
|
|
19
|
+
effectScope(() => {
|
|
20
|
+
s = createLocalStorage("count", 0);
|
|
21
|
+
});
|
|
22
|
+
globalExpect(s()).toBe(42);
|
|
23
|
+
});
|
|
24
|
+
it("persists writes to localStorage", () => {
|
|
25
|
+
let s;
|
|
26
|
+
effectScope(() => {
|
|
27
|
+
s = createLocalStorage("key", "a");
|
|
28
|
+
});
|
|
29
|
+
s("b");
|
|
30
|
+
globalExpect(JSON.parse(localStorage.getItem("key"))).toBe("b");
|
|
31
|
+
});
|
|
32
|
+
it("persists complex values as JSON", () => {
|
|
33
|
+
let s;
|
|
34
|
+
effectScope(() => {
|
|
35
|
+
s = createLocalStorage("obj", { x: 1 });
|
|
36
|
+
});
|
|
37
|
+
s({ x: 99 });
|
|
38
|
+
globalExpect(JSON.parse(localStorage.getItem("obj"))).toEqual({ x: 99 });
|
|
39
|
+
});
|
|
40
|
+
it("falls back to initialValue on invalid JSON", () => {
|
|
41
|
+
localStorage.setItem("broken", "{not json}");
|
|
42
|
+
let s;
|
|
43
|
+
effectScope(() => {
|
|
44
|
+
s = createLocalStorage("broken", 7);
|
|
45
|
+
});
|
|
46
|
+
globalExpect(s()).toBe(7);
|
|
47
|
+
});
|
|
48
|
+
it("supports custom serialise / deserialise", () => {
|
|
49
|
+
let s;
|
|
50
|
+
effectScope(() => {
|
|
51
|
+
s = createLocalStorage("date", /* @__PURE__ */ new Date("2024-01-01"), {
|
|
52
|
+
serialise: (d) => d.toISOString(),
|
|
53
|
+
deserialise: (raw) => new Date(raw)
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
const d = /* @__PURE__ */ new Date("2025-06-15");
|
|
57
|
+
s(d);
|
|
58
|
+
globalExpect(localStorage.getItem("date")).toBe(d.toISOString());
|
|
59
|
+
});
|
|
60
|
+
it("reacts to storage events from other tabs", () => {
|
|
61
|
+
let s;
|
|
62
|
+
effectScope(() => {
|
|
63
|
+
s = createLocalStorage("sync-key", "init");
|
|
64
|
+
});
|
|
65
|
+
localStorage.setItem("sync-key", JSON.stringify("from-other-tab"));
|
|
66
|
+
window.dispatchEvent(new StorageEvent("storage", {
|
|
67
|
+
key: "sync-key",
|
|
68
|
+
newValue: JSON.stringify("from-other-tab"),
|
|
69
|
+
storageArea: localStorage
|
|
70
|
+
}));
|
|
71
|
+
globalExpect(s()).toBe("from-other-tab");
|
|
72
|
+
});
|
|
73
|
+
it("ignores storage events for different keys", () => {
|
|
74
|
+
let s;
|
|
75
|
+
effectScope(() => {
|
|
76
|
+
s = createLocalStorage("my-key", "init");
|
|
77
|
+
});
|
|
78
|
+
window.dispatchEvent(new StorageEvent("storage", {
|
|
79
|
+
key: "other-key",
|
|
80
|
+
newValue: JSON.stringify("nope"),
|
|
81
|
+
storageArea: localStorage
|
|
82
|
+
}));
|
|
83
|
+
globalExpect(s()).toBe("init");
|
|
84
|
+
});
|
|
85
|
+
it("resets to initialValue when key is removed", () => {
|
|
86
|
+
let s;
|
|
87
|
+
effectScope(() => {
|
|
88
|
+
s = createLocalStorage("rm-key", "default");
|
|
89
|
+
});
|
|
90
|
+
s("changed");
|
|
91
|
+
localStorage.removeItem("rm-key");
|
|
92
|
+
window.dispatchEvent(new StorageEvent("storage", {
|
|
93
|
+
key: "rm-key",
|
|
94
|
+
newValue: null,
|
|
95
|
+
storageArea: localStorage
|
|
96
|
+
}));
|
|
97
|
+
globalExpect(s()).toBe("default");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe("createSessionStorage", () => {
|
|
101
|
+
afterEach(() => {
|
|
102
|
+
sessionStorage.clear();
|
|
103
|
+
});
|
|
104
|
+
it("uses initialValue when storage is empty", () => {
|
|
105
|
+
let s;
|
|
106
|
+
effectScope(() => {
|
|
107
|
+
s = createSessionStorage("count", 0);
|
|
108
|
+
});
|
|
109
|
+
globalExpect(s()).toBe(0);
|
|
110
|
+
});
|
|
111
|
+
it("reads an existing value from sessionStorage", () => {
|
|
112
|
+
sessionStorage.setItem("count", "42");
|
|
113
|
+
let s;
|
|
114
|
+
effectScope(() => {
|
|
115
|
+
s = createSessionStorage("count", 0);
|
|
116
|
+
});
|
|
117
|
+
globalExpect(s()).toBe(42);
|
|
118
|
+
});
|
|
119
|
+
it("persists writes to sessionStorage", () => {
|
|
120
|
+
let s;
|
|
121
|
+
effectScope(() => {
|
|
122
|
+
s = createSessionStorage("key", "a");
|
|
123
|
+
});
|
|
124
|
+
s("b");
|
|
125
|
+
globalExpect(JSON.parse(sessionStorage.getItem("key"))).toBe("b");
|
|
126
|
+
});
|
|
127
|
+
it("falls back to initialValue on invalid JSON", () => {
|
|
128
|
+
sessionStorage.setItem("broken", "{not json}");
|
|
129
|
+
let s;
|
|
130
|
+
effectScope(() => {
|
|
131
|
+
s = createSessionStorage("broken", 7);
|
|
132
|
+
});
|
|
133
|
+
globalExpect(s()).toBe(7);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
//#endregion
|
|
137
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as Computed } from "
|
|
1
|
+
import { t as Computed } from "../index-Cvdhuy6Y.mjs";
|
|
2
2
|
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/utilities/throttled.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Returns a `Computed` that mirrors `getter` but updates at most once per
|
|
6
6
|
* `interval` milliseconds. A trailing-edge update is scheduled so the final
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { o as effect, p as signal } from "../signals-DcgXhkU2.mjs";
|
|
2
|
+
import { createTimeout } from "./timeout.mjs";
|
|
3
|
+
//#region src/utilities/throttled.ts
|
|
4
|
+
/**
|
|
5
|
+
* Returns a `Computed` that mirrors `getter` but updates at most once per
|
|
6
|
+
* `interval` milliseconds. A trailing-edge update is scheduled so the final
|
|
7
|
+
* value is never lost.
|
|
8
|
+
*
|
|
9
|
+
* The initial value is read synchronously.
|
|
10
|
+
*/
|
|
11
|
+
function createThrottled(getter, interval) {
|
|
12
|
+
const s = signal(getter());
|
|
13
|
+
let lastRun = -Infinity;
|
|
14
|
+
let firstRun = true;
|
|
15
|
+
let latest;
|
|
16
|
+
const { stop, reset } = createTimeout(() => {
|
|
17
|
+
lastRun = Date.now();
|
|
18
|
+
s(latest);
|
|
19
|
+
}, () => interval - (Date.now() - lastRun), false);
|
|
20
|
+
effect(() => {
|
|
21
|
+
latest = getter();
|
|
22
|
+
if (firstRun) {
|
|
23
|
+
firstRun = false;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const elapsed = Date.now() - lastRun;
|
|
27
|
+
stop();
|
|
28
|
+
if (elapsed >= interval) {
|
|
29
|
+
lastRun = Date.now();
|
|
30
|
+
s(latest);
|
|
31
|
+
} else reset();
|
|
32
|
+
});
|
|
33
|
+
return s;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { createThrottled };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as
|
|
1
|
+
import { p as signal, s as effectScope } from "../signals-DcgXhkU2.mjs";
|
|
2
|
+
import { a as beforeEach, n as vi, o as describe, r as afterEach, s as it, t as globalExpect } from "../test.BmQO5GaM-ANkhHvbr.mjs";
|
|
3
3
|
import { createThrottled } from "./throttled.mjs";
|
|
4
|
-
//#region src/
|
|
4
|
+
//#region src/utilities/throttled.test.ts
|
|
5
5
|
beforeEach(() => {
|
|
6
6
|
vi.useFakeTimers();
|
|
7
7
|
});
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { t as Computed } from "
|
|
1
|
+
import { t as Computed } from "../index-Cvdhuy6Y.mjs";
|
|
2
2
|
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/utilities/timeout.d.ts
|
|
4
4
|
type TimeoutResult = {
|
|
5
|
-
|
|
5
|
+
isRunning: Computed<boolean>;
|
|
6
6
|
start(): void;
|
|
7
7
|
stop(): void;
|
|
8
8
|
reset(): void;
|
|
9
9
|
} & Disposable;
|
|
10
10
|
/**
|
|
11
11
|
* Reactive `setTimeout` wrapper with pause/resume/reset control.
|
|
12
|
-
* The callback fires once after `delay` ms. Starts running immediately
|
|
12
|
+
* The callback fires once after `delay` ms. Starts running immediately
|
|
13
|
+
* unless `immediate` is set to `false`.
|
|
13
14
|
*/
|
|
14
|
-
declare function createTimeout(callback: () => void, delay: number): TimeoutResult;
|
|
15
|
+
declare function createTimeout(callback: () => void, delay: number | (() => number), immediate?: boolean): TimeoutResult;
|
|
15
16
|
//#endregion
|
|
16
17
|
export { createTimeout };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { f as onCleanup, p as signal } from "../signals-DcgXhkU2.mjs";
|
|
2
|
+
//#region src/utilities/timeout.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reactive `setTimeout` wrapper with pause/resume/reset control.
|
|
5
|
+
* The callback fires once after `delay` ms. Starts running immediately
|
|
6
|
+
* unless `immediate` is set to `false`.
|
|
7
|
+
*/
|
|
8
|
+
function createTimeout(callback, delay, immediate = true) {
|
|
9
|
+
const isRunning = signal(false);
|
|
10
|
+
let id;
|
|
11
|
+
const getDelay = typeof delay === "function" ? delay : () => delay;
|
|
12
|
+
const stop = () => {
|
|
13
|
+
clearTimeout(id);
|
|
14
|
+
id = void 0;
|
|
15
|
+
isRunning(false);
|
|
16
|
+
};
|
|
17
|
+
const start = () => {
|
|
18
|
+
if (isRunning()) return;
|
|
19
|
+
isRunning(true);
|
|
20
|
+
id = setTimeout(() => {
|
|
21
|
+
isRunning(false);
|
|
22
|
+
id = void 0;
|
|
23
|
+
callback();
|
|
24
|
+
}, getDelay());
|
|
25
|
+
};
|
|
26
|
+
const reset = () => {
|
|
27
|
+
stop();
|
|
28
|
+
start();
|
|
29
|
+
};
|
|
30
|
+
if (immediate) start();
|
|
31
|
+
const cleanup = () => stop();
|
|
32
|
+
onCleanup(cleanup);
|
|
33
|
+
return {
|
|
34
|
+
isRunning,
|
|
35
|
+
start,
|
|
36
|
+
stop,
|
|
37
|
+
reset,
|
|
38
|
+
[Symbol.dispose]: cleanup
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { createTimeout };
|