(ref = el)} />
{location()?.latitude}, {location()?.longitude}
diff --git a/packages/intersection-observer/README.md b/packages/intersection-observer/README.md
index e76f47484..cb70a970e 100644
--- a/packages/intersection-observer/README.md
+++ b/packages/intersection-observer/README.md
@@ -10,6 +10,7 @@
A range of IntersectionObserver API utilities great for different types of use cases:
+- [`makeIntersectionObserver`](#makeintersectionobserver) - A non-reactive, imperative wrapper around the IntersectionObserver API.
- [`createIntersectionObserver`](#createintersectionobserver) - A reactive observer primitive.
- [`createViewportObserver`](#createviewportobserver) - More advanced tracker that creates a store of element signals.
- [`createVisibilityObserver`](#createvisibilityobserver) - Basic visibility observer using a signal.
@@ -24,44 +25,100 @@ pnpm add @solid-primitives/intersection-observer
yarn add @solid-primitives/intersection-observer
```
+## `makeIntersectionObserver`
+
+A non-reactive, imperative wrapper around the native IntersectionObserver API. Useful when you need full manual control over observation lifecycle without integrating into a Solid reactive scope.
+
+```ts
+import { makeIntersectionObserver } from "@solid-primitives/intersection-observer";
+
+const { add, remove, start, stop, reset, instance } = makeIntersectionObserver(
+ [el1, el2],
+ entries => {
+ entries.forEach(e => console.log(e.isIntersecting));
+ },
+ { threshold: 0.5 },
+);
+
+add(el3);
+remove(el1);
+stop(); // disconnects the observer
+```
+
+### Definition
+
+```ts
+function makeIntersectionObserver(
+ elements: Element[],
+ onChange: IntersectionObserverCallback,
+ options?: IntersectionObserverInit,
+): {
+ add: (el: Element) => void;
+ remove: (el: Element) => void;
+ start: () => void;
+ reset: () => void;
+ stop: () => void;
+ instance: IntersectionObserver;
+};
+```
+
## `createIntersectionObserver`
+Returns a tuple of:
+
+- A **store array** of `IntersectionObserverEntry` objects — one slot per element, updated in place when that element's intersection state changes. Reading `entries[i].isIntersecting` only re-runs the computation that reads slot `i`.
+- **`isVisible(el)`** — a pending-aware helper that throws `NotReadyError` until the first observation fires for that element, then returns `entry.isIntersecting` reactively. Integrates with `
` for a natural loading fallback.
+
```tsx
import { createIntersectionObserver } from "@solid-primitives/intersection-observer";
-const [targets, setTargets] = createSignal([some_element]);
+const [targets, setTargets] = createSignal([]);
+
+const [entries, isVisible] = createIntersectionObserver(targets, { threshold: 0.5 });
-createIntersectionObserver(els, entries => {
+// entries — reactive store, fine-grained per-element tracking:
+createEffect(() => {
entries.forEach(e => console.log(e.isIntersecting));
});
+// isVisible — integrates with for pending state:
+Checking…}>
+ Visible!
+
+
setTargets(p => [...p, el])} />;
```
+`options` may be a reactive accessor — if the options object changes, the observer is disconnected and recreated with the new options, and all currently tracked elements are re-observed.
+
### Definition
```ts
function createIntersectionObserver(
elements: Accessor
,
- onChange: IntersectionObserverCallback,
- options?: IntersectionObserverInit,
-): void;
+ options?: MaybeAccessor,
+): readonly [entries: readonly IntersectionObserverEntry[], isVisible: (el: Element) => boolean];
```
## `createViewportObserver`
This primitive comes with a number of flexible options. You can specify a callback at the root with an array of elements or individual callbacks for individual elements.
+The `add` function has two forms:
+
+- `add(el, callback)` — imperative: register an element with its callback directly.
+- `add(callback)` — curried ref form: returns a `(el) => void` ref callback for use as `ref={add(e => ...)}` in JSX.
+
```tsx
import { createViewportObserver } from '@solid-primitives/intersection-observer';
// Basic usage:
const [add, { remove, start, stop, instance }] = createViewportObserver(els, e => {...});
-add(el, e => console.log(e.isIntersecting))
+add(el, e => console.log(e.isIntersecting));
-// Directive usage:
-const [intersectionObserver] = createViewportObserver()
- console.log(e.isIntersecting)}>
+// Ref usage (replaces old use: directive):
+const [add] = createViewportObserver();
+ console.log(e.isIntersecting))}>
```
### Definition
@@ -83,63 +140,69 @@ function createViewportObserver(
## `createVisibilityObserver`
-Creates reactive signal that changes when a single element's visibility changes.
-
-### How to use it
-
-`createVisibilityObserver` takes a `IntersectionObserverInit` object as the first argument. Use it to set thresholds, margins, and other options.
+Creates a reactive signal that changes when a single element's visibility changes. Takes the element to observe directly — the previous curried factory pattern has been removed.
-- `root` — The Element or Document whose bounds are used as the bounding box when testing for intersection.
-- `rootMargin` — A string which specifies a set of offsets to add to the root's bounding_box when calculating intersections, effectively shrinking or growing the root for calculation purposes.
-- `threshold` — Either a single number or an array of numbers between 0.0 and 1.0, specifying a ratio of intersection area to total bounding box area for the observed target.
-- `initialValue` — Initial value of the signal _(default: false)_
+The element may be a reactive accessor (`() => el`) or a plain DOM element. Passing a falsy accessor value removes the element from the observer.
-It returns a configured _"use"_ function for creating a visibility signal for a single element. The passed element can be a **reactive signal** or a DOM element. Returning a falsy value will remove the element from the observer.
+When `initialValue` is omitted, `visible()` throws `NotReadyError` until the first `IntersectionObserver` callback fires — integrating naturally with `` for a loading fallback:
```tsx
import { createVisibilityObserver } from "@solid-primitives/intersection-observer";
let el: HTMLDivElement | undefined;
-const useVisibilityObserver = createVisibilityObserver({ threshold: 0.8 });
+const visible = createVisibilityObserver(() => el, { threshold: 0.8 });
-// make sure that you pass the element reference in a thunk if it is undefined initially
-const visible = useVisibilityObserver(() => el);
-
-{visible() ? "Visible" : "Hidden"}
;
+// Pending until first IO fires — shows fallback in the meantime:
+Checking…}>
+ Hidden}>
+ Visible!
+
+;
```
-You can use this shorthand when creating a visibility signal for a single element:
+Provide `initialValue` to opt out of the pending state and start with a known value:
```tsx
-let el: HTMLDivElement | undefined;
+const visible = createVisibilityObserver(() => el, { initialValue: false });
+// visible() === false immediately, no pending state
+{visible() ? "Visible" : "Hidden"}
;
+```
-const visible = createVisibilityObserver({ threshold: 0.8 })(() => el);
+Options accepted in addition to `IntersectionObserverInit`:
-{visible() ? "Visible" : "Hidden"}
;
-```
+- `initialValue` — Opt-in initial value; when omitted, `visible()` throws `NotReadyError` until the first observation.
### Setter callback
-`createVisibilityObserver` takes a setter callback as the second argument. It is called when the element's intersection changes. The callback should return a boolean value indicating whether the element is visible — it'll be assigned to the signal.
+`createVisibilityObserver` accepts an optional setter callback as the third argument. It is called when the element's intersection changes and should return a boolean indicating whether the element is visible.
```ts
-const useVisibilityObserver = createVisibilityObserver({ threshold: 0.8 }, entry => {
- // do some calculations on the intersection entry
- return entry.isIntersecting;
-});
+let el: HTMLDivElement | undefined;
+
+const visible = createVisibilityObserver(
+ () => el,
+ { threshold: 0.8 },
+ entry => {
+ // do some calculations on the intersection entry
+ return entry.isIntersecting;
+ },
+);
```
**Exported modifiers**
#### `withOccurrence`
-It provides information about element occurrence in the viewport — `"Entering"`, `"Leaving"`, `"Inside"` or `"Outside"`.
+Provides information about element occurrence in the viewport — `"Entering"`, `"Leaving"`, `"Inside"` or `"Outside"`.
```tsx
import { createVisibilityObserver, withOccurrence } from "@solid-primitives/intersection-observer";
-const useVisibilityObserver = createVisibilityObserver(
+let el: HTMLDivElement | undefined;
+
+const visible = createVisibilityObserver(
+ () => el,
{ threshold: 0.8 },
withOccurrence((entry, { occurrence }) => {
console.log(occurrence); // => "Entering" | "Leaving" | "Inside" | "Outside"
@@ -150,12 +213,15 @@ const useVisibilityObserver = createVisibilityObserver(
#### `withDirection`
-It provides information about element direction on the screen — `"Left"`, `"Right"`, `"Top"`, `"Bottom"` or `"None"`.
+Provides information about element direction on the screen — `"Left"`, `"Right"`, `"Top"`, `"Bottom"` or `"None"`.
```ts
import { createVisibilityObserver, withDirection } from "@solid-primitives/intersection-observer";
-const useVisibilityObserver = createVisibilityObserver(
+let el: HTMLDivElement | undefined;
+
+const visible = createVisibilityObserver(
+ () => el,
{ threshold: 0.8 },
withDirection((entry, { directionY, directionX, visible }) => {
if (!entry.isIntersecting && directionY === "Top" && visible) {
@@ -169,11 +235,11 @@ const useVisibilityObserver = createVisibilityObserver(
### Definition
```ts
-function createViewportObserver(
- elements: MaybeAccessor,
- callback: EntryCallback,
- options?: IntersectionObserverInit,
-): CreateViewportObserverReturnValue;
+function createVisibilityObserver(
+ element: Accessor | Element,
+ options?: IntersectionObserverInit & { initialValue?: boolean },
+ setter?: MaybeAccessor,
+): Accessor;
```
## Demo
diff --git a/packages/intersection-observer/package.json b/packages/intersection-observer/package.json
index 746525e23..3041a6167 100644
--- a/packages/intersection-observer/package.json
+++ b/packages/intersection-observer/package.json
@@ -42,7 +42,7 @@
"scripts": {
"dev": "node --import=@nothing-but/node-resolve-ts --experimental-transform-types ../../scripts/dev.ts",
"build": "node --import=@nothing-but/node-resolve-ts --experimental-transform-types ../../scripts/build.ts",
- "vitest": "vitest -c ../../configs/vitest.config.ts",
+ "vitest": "vitest -c vitest.config.ts",
"test": "pnpm run vitest",
"test:ssr": "pnpm run vitest --mode ssr"
},
@@ -54,12 +54,14 @@
],
"devDependencies": {
"@solid-primitives/range": "workspace:^",
- "solid-js": "^1.9.7"
+ "@solidjs/web": "2.0.0-beta.7",
+ "solid-js": "2.0.0-beta.7"
},
"dependencies": {
"@solid-primitives/utils": "workspace:^"
},
"peerDependencies": {
- "solid-js": "^1.6.12"
+ "@solidjs/web": "2.0.0-beta.7",
+ "solid-js": "2.0.0-beta.7"
}
}
diff --git a/packages/intersection-observer/src/index.ts b/packages/intersection-observer/src/index.ts
index fbf571980..14fb967ce 100644
--- a/packages/intersection-observer/src/index.ts
+++ b/packages/intersection-observer/src/index.ts
@@ -1,13 +1,13 @@
import {
- onMount,
onCleanup,
createSignal,
createEffect,
+ createStore,
untrack,
- type Setter,
+ NotReadyError,
DEV,
} from "solid-js";
-import type { JSX, Accessor } from "solid-js";
+import type { Accessor } from "solid-js";
import { isServer } from "solid-js/web";
import {
access,
@@ -16,6 +16,9 @@ import {
handleDiffArray,
} from "@solid-primitives/utils";
+// Sentinel for the "not yet observed" pending state.
+const NOT_SET: unique symbol = Symbol();
+
export type AddIntersectionObserverEntry = (el: Element) => void;
export type RemoveIntersectionObserverEntry = (el: Element) => void;
@@ -23,10 +26,15 @@ export type EntryCallback = (
entry: IntersectionObserverEntry,
instance: IntersectionObserver,
) => void;
-export type AddViewportObserverEntry = (
- el: Element,
- callback: MaybeAccessor,
-) => void;
+
+/**
+ * Curried ref-callback form: `add(callback)` returns `(el) => void` for use as
+ * a Solid `ref`. Direct imperative form: `add(el, callback)`.
+ */
+export type AddViewportObserverEntry = {
+ (el: Element, callback: MaybeAccessor): void;
+ (callback: MaybeAccessor): (el: Element) => void;
+};
export type RemoveViewportObserverEntry = (el: Element) => void;
export type CreateViewportObserverReturnValue = [
@@ -39,21 +47,7 @@ export type CreateViewportObserverReturnValue = [
},
];
-declare module "solid-js" {
- namespace JSX {
- interface Directives {
- intersectionObserver: true | EntryCallback;
- }
- }
-}
-
-// This ensures the `JSX` import won't fall victim to tree shaking before
-// TypesScript can use it
-export type E = JSX.Element;
-
function observe(el: Element, instance: IntersectionObserver): void {
- // Elements with 'display: "contents"' don't work with IO, even if they are visible by users
- // (https://github.com/solidjs-community/solid-primitives/issues/116)
if (DEV && el instanceof HTMLElement && el.style.display === "contents") {
// eslint-disable-next-line no-console
console.warn(
@@ -64,9 +58,6 @@ function observe(el: Element, instance: IntersectionObserver): void {
instance.observe(el);
}
-/**
- * @deprecated Please use native {@link IntersectionObserver}, or {@link createIntersectionObserver} instead.
- */
export function makeIntersectionObserver(
elements: Element[],
onChange: IntersectionObserverCallback,
@@ -99,62 +90,107 @@ export function makeIntersectionObserver(
}
/**
- * Creates a reactive Intersection Observer primitive.
- *
- * @param elements - A list of elements to watch
- * @param onChange - An event handler that returns an array of observer entires
- * @param options - IntersectionObserver constructor options:
- * - `root` — The Element or Document whose bounds are used as the bounding box when testing for intersection.
- * - `rootMargin` — A string which specifies a set of offsets to add to the root's bounding_box when calculating intersections, effectively shrinking or growing the root for calculation purposes.
- * - `threshold` — Either a single number or an array of numbers between 0.0 and 1.0, specifying a ratio of intersection area to total bounding box area for the observed target.
+ * Creates a reactive Intersection Observer primitive. Returns a tuple of:
+ * - A store array of {@link IntersectionObserverEntry} objects, one slot per
+ * element, updated in place whenever that element's intersection state changes.
+ * - `isVisible(el)` — a pending-aware accessor that throws `NotReadyError` until
+ * the first observation fires for that element (integrates with ``),
+ * then returns `entry.isIntersecting` reactively.
*
* @example
* ```tsx
- * const createIntersectionObserver(els, entries =>
- * console.log(entries)
- * );
+ * const [entries, isVisible] = createIntersectionObserver(elements);
+ *
+ * // In JSX — Loading shows fallback until first observation:
+ * Checking…}>
+ * Visible!
+ *
* ```
*/
export function createIntersectionObserver(
elements: Accessor,
- onChange: IntersectionObserverCallback,
- options?: IntersectionObserverInit,
-): void {
- if (isServer) return;
+ options?: MaybeAccessor,
+): readonly [
+ entries: readonly IntersectionObserverEntry[],
+ isVisible: (el: Element) => boolean,
+] {
+ if (isServer) {
+ const isVisible = (_el: Element): boolean => {
+ throw new NotReadyError("IntersectionObserver not available on server");
+ };
+ return [[], isVisible] as const;
+ }
- const io = new IntersectionObserver(onChange, options);
+ const [entries, setEntries] = createStore([]);
+ const indexMap = new WeakMap();
+ let nextIdx = 0;
+ let trackedEls: Element[] = [];
+
+ const ioCallback: IntersectionObserverCallback = newEntries => {
+ for (const entry of newEntries) {
+ let idx = indexMap.get(entry.target);
+ if (idx === undefined) {
+ idx = nextIdx++;
+ indexMap.set(entry.target, idx);
+ }
+ const frozen = Object.freeze({ ...entry });
+ setEntries(draft => {
+ draft[idx] = frozen as any;
+ if (idx >= draft.length) draft.length = idx + 1;
+ });
+ }
+ };
+
+ let io = new IntersectionObserver(ioCallback, untrack(() => access(options)));
onCleanup(() => io.disconnect());
- createEffect((p: Element[]) => {
- const list = elements();
- handleDiffArray(
- list,
- p,
- el => observe(el, io),
- el => io.unobserve(el),
- );
- return list;
- }, []);
+ if (typeof options === "function") {
+ createEffect(options, (opts: IntersectionObserverInit) => {
+ io.disconnect();
+ io = new IntersectionObserver(ioCallback, opts);
+ trackedEls.forEach(el => observe(el, io));
+ });
+ }
+
+ createEffect(
+ () => elements(),
+ (list: Element[], prev: Element[] = []) => {
+ handleDiffArray(list, prev, el => observe(el, io), el => io.unobserve(el));
+ trackedEls = list;
+ },
+ [] as Element[],
+ );
+
+ // Reads the entry for the given element from the store. Throws NotReadyError
+ // until the IO has fired for that element — integrates with .
+ // When called inside a reactive scope, tracks the store slot reactively.
+ const isVisible = (el: Element): boolean => {
+ const idx = indexMap.get(el);
+ if (idx === undefined || !entries[idx])
+ throw new NotReadyError("Element has not yet been observed");
+ return entries[idx]!.isIntersecting;
+ };
+
+ return [entries, isVisible] as const;
}
/**
- * Creates a more advanced viewport observer for complex tracking with multiple objects in a single IntersectionObserver instance.
+ * Creates a more advanced viewport observer for complex tracking with multiple
+ * objects in a single IntersectionObserver instance.
*
- * @param elements - A list of elements to watch
- * @param callback - Element intersection change event handler
- * @param options - IntersectionObserver constructor options:
- * - `root` — The Element or Document whose bounds are used as the bounding box when testing for intersection.
- * - `rootMargin` — A string which specifies a set of offsets to add to the root's bounding_box when calculating intersections, effectively shrinking or growing the root for calculation purposes.
- * - `threshold` — Either a single number or an array of numbers between 0.0 and 1.0, specifying a ratio of intersection area to total bounding box area for the observed target.
+ * The `add` function has two forms:
+ * - `add(el, callback)` — imperative: register element directly.
+ * - `add(callback)` — returns a ref callback `(el) => void` for use as
+ * `ref={add(e => ...)}` in JSX. Replaces the old `use:intersectionObserver` directive.
*
* @example
* ```tsx
* const [add, { remove, start, stop, instance }] = createViewportObserver(els, (e) => {...});
* add(el, e => console.log(e.isIntersecting))
*
- * // directive usage:
- * const [intersectionObserver] = createViewportObserver()
- * console.log(e.isIntersecting)}>
+ * // ref usage (replaces old use: directive):
+ * const [add] = createViewportObserver()
+ * console.log(e.isIntersecting))}>
* ```
*/
export function createViewportObserver(
@@ -188,25 +224,38 @@ export function createViewportObserver(...a: any) {
options = a[1];
}
} else options = a[0];
+
const callbacks = new WeakMap>();
const onChange: IntersectionObserverCallback = (entries, instance) =>
entries.forEach(entry => {
const cb = callbacks.get(entry.target)?.(entry, instance);
- // Additional check to prevent errors when the user
- // use "observe" directive without providing a callback
cb instanceof Function && cb(entry, instance);
});
+
const { add, remove, stop, instance } = makeIntersectionObserver([], onChange, options);
- const addEntry: AddViewportObserverEntry = (el, callback) => {
- add(el);
- callbacks.set(el, callback);
+
+ const addEntry: AddViewportObserverEntry = (
+ elOrCallback: Element | MaybeAccessor,
+ callback?: MaybeAccessor,
+ ): any => {
+ if (elOrCallback instanceof Element) {
+ add(elOrCallback);
+ callbacks.set(elOrCallback, callback!);
+ } else {
+ // Curried ref form: add(callback) → ref callback (el) => void
+ return (el: Element) => {
+ add(el);
+ callbacks.set(el, elOrCallback);
+ };
+ }
};
+
const removeEntry: RemoveViewportObserverEntry = el => {
callbacks.delete(el);
remove(el);
};
const start = () => initial.forEach(([el, cb]) => addEntry(el, cb));
- onMount(start);
+ createEffect(() => {}, () => { start(); });
return [addEntry, { remove: removeEntry, start, stop, instance }];
}
@@ -216,87 +265,108 @@ export type VisibilitySetter = (
) => boolean;
/**
- * Creates reactive signal that changes when a single element's visibility changes.
+ * Creates a reactive signal that changes when a single element's visibility changes.
*
- * @param options - A Primitive and IntersectionObserver constructor options:
- * - `root` — The Element or Document whose bounds are used as the bounding box when testing for intersection.
- * - `rootMargin` — A string which specifies a set of offsets to add to the root's bounding_box when calculating intersections, effectively shrinking or growing the root for calculation purposes.
- * - `threshold` — Either a single number or an array of numbers between 0.0 and 1.0, specifying a ratio of intersection area to total bounding box area for the observed target.
- * - `initialValue` — Initial value of the signal *(default: false)*
+ * When `initialValue` is omitted, `visible()` throws `NotReadyError` until the
+ * first IntersectionObserver callback fires — integrates with `` for a
+ * natural loading fallback:
*
- * @returns A configured *"use"* function for creating a visibility signal for a single element. The passed element can be a **reactive signal** or a DOM element. Returning a falsy value will remove the element from the observer.
- * ```ts
- * (element: Accessor | Element) => Accessor
+ * ```tsx
+ * const visible = createVisibilityObserver(() => el)
+ *
+ * Checking…}>
+ * Hidden}>
+ * Visible!
+ *
+ *
* ```
*
- * @example
+ * Provide `initialValue` to opt out of the pending state and start with a known value:
+ *
* ```tsx
- * let el: HTMLDivElement | undefined
- * const useVisibilityObserver = createVisibilityObserver({ threshold: 0.8 })
- * const visible = useVisibilityObserver(() => el)
- * { visible() ? "Visible" : "Hidden" }
+ * const visible = createVisibilityObserver(() => el, { initialValue: false })
+ * // visible() === false immediately
* ```
+ *
+ * @param element - The element to observe; may be `Accessor` or a plain `Element`.
+ * @param options - IntersectionObserver options plus optional `initialValue`.
+ * @param setter - Optional custom setter controlling the signal value.
*/
export function createVisibilityObserver(
- options?: IntersectionObserverInit & {
- initialValue?: boolean;
- },
+ element: Accessor | Element,
+ options?: IntersectionObserverInit & { initialValue?: boolean },
setter?: MaybeAccessor,
-): (element: Accessor | Element) => Accessor {
+): Accessor {
if (isServer) {
- return () => () => false;
+ if (options?.initialValue !== undefined) return () => options.initialValue!;
+ return () => {
+ throw new NotReadyError("Visibility not yet observed");
+ };
}
- const callbacks = new WeakMap();
+ // rawVisible tracks the actual observed value; NOT_SET means "first IO hasn't fired yet".
+ const [rawVisible, setRawVisible] = createSignal(
+ options?.initialValue !== undefined ? options.initialValue : NOT_SET,
+ { ownedWrite: true },
+ );
+
+ // Plain accessor — reading rawVisible() inside a reactive scope is tracked normally.
+ // Throwing from a plain function (not a computed signal) avoids caching issues
+ // when called outside a reactive scope between state transitions.
+ const visible = (): boolean => {
+ const val = rawVisible();
+ if (val === NOT_SET) throw new NotReadyError("Visibility not yet observed");
+ return val;
+ };
- const io = new IntersectionObserver((entries, instance) => {
- for (const entry of entries) callbacks.get(entry.target)?.(entry, instance);
+ // access(setter) called once so factory setters (withOccurrence, withDirection)
+ // create their per-element closure exactly once.
+ const setterFn = setter ? access(setter) : null;
+ const entryCallback: EntryCallback = setterFn
+ ? entry => {
+ const prev = untrack(rawVisible);
+ setRawVisible(setterFn(entry, { visible: prev === NOT_SET ? false : prev }));
+ }
+ : entry => setRawVisible(entry.isIntersecting);
+
+ const io = new IntersectionObserver((newEntries, instance) => {
+ for (const entry of newEntries) entryCallback(entry, instance);
}, options);
onCleanup(() => io.disconnect());
- function removeEntry(el: Element) {
- io.unobserve(el);
- callbacks.delete(el);
- }
- function addEntry(el: Element, callback: EntryCallback) {
- observe(el, io);
- callbacks.set(el, callback);
- }
-
- const getCallback: (get: Accessor, set: Setter) => EntryCallback = setter
- ? (get, set) => {
- const setterRef = access(setter);
- return entry => set(setterRef(entry, { visible: untrack(get) }));
- }
- : (_, set) => entry => set(entry.isIntersecting);
-
- return element => {
- const [isVisible, setVisible] = createSignal(options?.initialValue ?? false);
- const callback = getCallback(isVisible, setVisible);
- let prevEl: Element | FalsyValue;
+ let prevEl: Element | FalsyValue;
- if (!(element instanceof Element)) {
- createEffect(() => {
- const el = element();
+ if (!(element instanceof Element)) {
+ createEffect(
+ () => element(),
+ (el: Element | FalsyValue) => {
if (el === prevEl) return;
- if (prevEl) removeEntry(prevEl);
- if (el) addEntry(el, callback);
+ if (prevEl) io.unobserve(prevEl);
+ if (el) observe(el, io);
prevEl = el;
- });
- } else addEntry(element, callback);
+ },
+ );
+ } else {
+ observe(element, io);
+ prevEl = element;
+ }
- onCleanup(() => prevEl && removeEntry(prevEl));
+ onCleanup(() => {
+ if (prevEl) io.unobserve(prevEl);
+ });
- return isVisible;
- };
+ return visible;
}
-export enum Occurrence {
- Entering = "Entering",
- Leaving = "Leaving",
- Inside = "Inside",
- Outside = "Outside",
-}
+// ─── Occurrence ───────────────────────────────────────────────────────────────
+
+export const Occurrence = {
+ Entering: "Entering",
+ Leaving: "Leaving",
+ Inside: "Inside",
+ Outside: "Outside",
+} as const;
+export type Occurrence = (typeof Occurrence)[keyof typeof Occurrence];
/**
* Calculates the occurrence of an element in the viewport.
@@ -305,9 +375,6 @@ export function getOccurrence(
isIntersecting: boolean,
prevIsIntersecting: boolean | undefined,
): Occurrence {
- if (isServer) {
- return Occurrence.Outside;
- }
return isIntersecting
? prevIsIntersecting
? Occurrence.Inside
@@ -318,30 +385,26 @@ export function getOccurrence(
}
/**
- * A visibility setter factory function. It provides information about element occurrence in the viewport — `"Entering"`, `"Leaving"`, `"Inside"` or `"Outside"`.
- * @param setter - A function that sets the occurrence of an element in the viewport.
- * @returns A visibility setter function.
+ * A visibility setter factory providing occurrence context — `"Entering"`,
+ * `"Leaving"`, `"Inside"`, or `"Outside"`.
+ *
* @example
* ```ts
- * const useVisibilityObserver = createVisibilityObserver(
- * { threshold: 0.8 },
- * withOccurrence((entry, { occurrence }) => {
- * console.log(occurrence);
- * return entry.isIntersecting;
- * })
+ * const visible = createVisibilityObserver(el, { threshold: 0.8 },
+ * withOccurrence((entry, { occurrence }) => {
+ * console.log(occurrence);
+ * return entry.isIntersecting;
+ * })
* );
* ```
*/
export function withOccurrence(
setter: MaybeAccessor>,
): () => VisibilitySetter {
- if (isServer) {
- return () => () => false;
- }
+ if (isServer) return () => () => false;
return () => {
let prevIntersecting: boolean | undefined;
const cb = access(setter);
-
return (entry, ctx) => {
const { isIntersecting } = entry;
const occurrence = getOccurrence(isIntersecting, prevIntersecting);
@@ -351,35 +414,32 @@ export function withOccurrence(
};
}
-export enum DirectionX {
- Left = "Left",
- Right = "Right",
- None = "None",
-}
+// ─── Direction ────────────────────────────────────────────────────────────────
-export enum DirectionY {
- Top = "Top",
- Bottom = "Bottom",
- None = "None",
-}
+export const DirectionX = {
+ Left: "Left",
+ Right: "Right",
+ None: "None",
+} as const;
+export type DirectionX = (typeof DirectionX)[keyof typeof DirectionX];
+
+export const DirectionY = {
+ Top: "Top",
+ Bottom: "Bottom",
+ None: "None",
+} as const;
+export type DirectionY = (typeof DirectionY)[keyof typeof DirectionY];
/**
- * Calculates the direction of an element in the viewport. The direction is calculated based on the element's rect, it's previous rect and the `isIntersecting` flag.
- * @returns A direction string: `"Left"`, `"Right"`, `"Top"`, `"Bottom"` or `"None"`.
+ * Calculates the scroll direction of an element based on bounding rect changes.
*/
export function getDirection(
rect: DOMRectReadOnly,
prevRect: DOMRectReadOnly | undefined,
intersecting: boolean,
): { directionX: DirectionX; directionY: DirectionY } {
- if (isServer) {
- return {
- directionX: DirectionX.None,
- directionY: DirectionY.None,
- };
- }
- let directionX = DirectionX.None;
- let directionY = DirectionY.None;
+ let directionX: DirectionX = DirectionX.None;
+ let directionY: DirectionY = DirectionY.None;
if (!prevRect) return { directionX, directionY };
if (rect.top < prevRect.top) directionY = intersecting ? DirectionY.Bottom : DirectionY.Top;
else if (rect.top > prevRect.top) directionY = intersecting ? DirectionY.Top : DirectionY.Bottom;
@@ -390,19 +450,16 @@ export function getDirection(
}
/**
- * A visibility setter factory function. It provides information about element direction on the screen — `"Left"`, `"Right"`, `"Top"`, `"Bottom"` or `"None"`.
- * @param setter - A function that sets the occurrence of an element in the viewport.
- * @returns A visibility setter function.
+ * A visibility setter factory providing scroll direction context — `"Left"`,
+ * `"Right"`, `"Top"`, `"Bottom"`, or `"None"`.
+ *
* @example
* ```ts
- * const useVisibilityObserver = createVisibilityObserver(
- * { threshold: 0.8 },
- * withDirection((entry, { directionY, directionX, visible }) => {
- * if (!entry.isIntersecting && directionY === "Top" && visible) {
- * return true;
- * }
- * return entry.isIntersecting;
- * })
+ * const visible = createVisibilityObserver(el, { threshold: 0.8 },
+ * withDirection((entry, { directionY, directionX, visible }) => {
+ * if (!entry.isIntersecting && directionY === "Top" && visible) return true;
+ * return entry.isIntersecting;
+ * })
* );
* ```
*/
@@ -411,13 +468,10 @@ export function withDirection(
VisibilitySetter
>,
): () => VisibilitySetter {
- if (isServer) {
- return () => () => false;
- }
+ if (isServer) return () => () => false;
return () => {
let prevBounds: DOMRectReadOnly | undefined;
const cb = access(callback);
-
return (entry, ctx) => {
const { boundingClientRect } = entry;
const direction = getDirection(boundingClientRect, prevBounds, entry.isIntersecting);
diff --git a/packages/intersection-observer/test/index.test.ts b/packages/intersection-observer/test/index.test.ts
index fd7baded6..e3d8574de 100644
--- a/packages/intersection-observer/test/index.test.ts
+++ b/packages/intersection-observer/test/index.test.ts
@@ -1,8 +1,9 @@
-import { createRoot, createSignal } from "solid-js";
+import { createRoot, createSignal, createEffect, flush } from "solid-js";
import { describe, test, expect, beforeEach } from "vitest";
import {
makeIntersectionObserver,
+ createIntersectionObserver,
createViewportObserver,
createVisibilityObserver,
withOccurrence,
@@ -229,6 +230,111 @@ describe("makeIntersectionObserver", () => {
});
});
+describe("createIntersectionObserver", () => {
+ let div!: HTMLDivElement;
+ let img!: HTMLImageElement;
+
+ beforeEach(() => {
+ div = document.createElement("div");
+ img = document.createElement("img");
+ });
+
+ test("creates a new IntersectionObserver instance", () => {
+ const previousInstanceCount = intersectionObserverInstances.length;
+ createRoot(dispose => {
+ createIntersectionObserver(() => [div]);
+ dispose();
+ });
+ expect(intersectionObserverInstances.length).toBe(previousInstanceCount + 1);
+ });
+
+ test("returns a store array of entries", () => {
+ createRoot(dispose => {
+ const [entries] = createIntersectionObserver(() => [div, img]);
+ expect(Array.isArray(entries)).toBe(true);
+ dispose();
+ });
+ });
+
+ test("store is updated when IO fires", () => {
+ createRoot(dispose => {
+ const [els] = createSignal([div]);
+ const [entries] = createIntersectionObserver(els);
+ const instance = _getLastIOInstance();
+
+ // flush() lets the deferred element effect run so div is actually observed
+ flush();
+
+ instance.__TEST__onChange({ isIntersecting: true });
+ flush();
+
+ expect(entries.length).toBe(1);
+ expect(entries[0]?.isIntersecting).toBe(true);
+
+ instance.__TEST__onChange({ isIntersecting: false });
+ flush();
+
+ expect(entries[0]?.isIntersecting).toBe(false);
+
+ dispose();
+ });
+ });
+
+ test("each element occupies its own store slot", () => {
+ createRoot(dispose => {
+ const [entries] = createIntersectionObserver(() => [div, img]);
+ const instance = _getLastIOInstance();
+
+ flush(); // let the element effect observe both elements
+
+ instance.__TEST__onChange({ isIntersecting: true });
+ flush();
+
+ expect(entries.length).toBe(2);
+ expect(entries[0]?.target).toBe(div);
+ expect(entries[1]?.target).toBe(img);
+ // Each slot tracks its own element independently
+ expect(entries[0]?.isIntersecting).toBe(true);
+ expect(entries[1]?.isIntersecting).toBe(true);
+
+ dispose();
+ });
+ });
+
+ test("options are passed to IntersectionObserver", () => {
+ createRoot(dispose => {
+ const options: IntersectionObserverInit = { threshold: 0.5 };
+ createIntersectionObserver(() => [div], options);
+ const instance = _getLastIOInstance();
+ expect(instance.options).toBe(options);
+ dispose();
+ });
+ });
+
+ test("isVisible throws before first observation, returns boolean after", () => {
+ createRoot(dispose => {
+ const [, isVisible] = createIntersectionObserver(() => [div]);
+ const instance = _getLastIOInstance();
+
+ flush(); // let the element effect observe div
+
+ expect(() => isVisible(div), "should throw NotReadyError before first IO").toThrow();
+
+ instance.__TEST__onChange({ isIntersecting: true });
+ flush();
+
+ expect(isVisible(div), "should return true after intersecting").toBe(true);
+
+ instance.__TEST__onChange({ isIntersecting: false });
+ flush();
+
+ expect(isVisible(div), "should return false when not intersecting").toBe(false);
+
+ dispose();
+ });
+ });
+});
+
describe("createViewportObserver", () => {
let div!: HTMLDivElement;
let img!: HTMLImageElement;
@@ -434,8 +540,9 @@ describe("createViewportObserver", () => {
let cbEntry: any;
- // the correct usage
- const [props] = createSignal((e: any) => (cbEntry = e));
+ // the correct usage (plain accessor, since createSignal(fn) is the compute overload in Solid 2.0)
+ const callback = (e: any) => (cbEntry = e);
+ const props = () => callback;
observe(el, props);
// the incorrect usage (just shouldn't cause an error)
@@ -449,6 +556,31 @@ describe("createViewportObserver", () => {
dispose();
});
});
+
+ test("add(callback) returns a ref callback for JSX ref usage", () => {
+ createRoot(dispose => {
+ const [add, { instance }] = createViewportObserver();
+ let cbEntry: IntersectionObserverEntry | undefined;
+
+ // curried form: add(callback) → (el) => void — used as ref={add(e => ...)}
+ const refCb = add((e: IntersectionObserverEntry) => {
+ cbEntry = e;
+ });
+ refCb(div); // simulate JSX ref binding
+
+ expect(
+ (instance as IntersectionObserver).elements[0],
+ "element wasn't observed via ref callback",
+ ).toBe(div);
+
+ (instance as IntersectionObserver).__TEST__onChange({ isIntersecting: true });
+
+ expect(cbEntry?.target, "callback should fire with correct target").toBe(div);
+ expect(cbEntry?.isIntersecting).toBe(true);
+
+ dispose();
+ });
+ });
});
describe("createVisibilityObserver", () => {
@@ -461,8 +593,7 @@ describe("createVisibilityObserver", () => {
test("creates a new IntersectionObserver instance", () => {
const previousInstanceCount = intersectionObserverInstances.length;
createRoot(dispose => {
- const useVisibilityObserver = createVisibilityObserver();
- useVisibilityObserver(div);
+ createVisibilityObserver(div);
dispose();
});
const newInstanceCount = intersectionObserverInstances.length;
@@ -476,8 +607,7 @@ describe("createVisibilityObserver", () => {
root: div,
rootMargin: "10px 10px 10px 10px",
};
- const useVisibilityObserver = createVisibilityObserver(options);
- useVisibilityObserver(div);
+ createVisibilityObserver(div, options);
const instance = _getLastIOInstance();
expect(instance.options, "options in IntersectionObserver don't match").toBe(options);
@@ -486,20 +616,20 @@ describe("createVisibilityObserver", () => {
});
});
- test("returns signal", () => {
+ test("returns signal — pending until first observation", () => {
createRoot(dispose => {
- const useVisibilityObserver = createVisibilityObserver();
- const isVisible = useVisibilityObserver(div);
+ const isVisible = createVisibilityObserver(div);
- expect(isVisible(), "signal doesn't return default initialValue").toBe(false);
+ expect(
+ () => isVisible(),
+ "should throw NotReadyError before first observation",
+ ).toThrow();
- const options = {
- initialValue: true,
- };
- const useVisibilityObserver2 = createVisibilityObserver(options);
- const isVisible2 = useVisibilityObserver2(div);
+ const isVisibleFalse = createVisibilityObserver(div, { initialValue: false });
+ expect(isVisibleFalse(), "should return false with initialValue: false").toBe(false);
- expect(isVisible2(), "signal doesn't return custom initialValue").toBe(true);
+ const isVisibleTrue = createVisibilityObserver(div, { initialValue: true });
+ expect(isVisibleTrue(), "should return true with initialValue: true").toBe(true);
dispose();
});
@@ -507,15 +637,16 @@ describe("createVisibilityObserver", () => {
test("signal changes state when intersection changes", () => {
createRoot(dispose => {
- const useVisibilityObserver = createVisibilityObserver();
- const isVisible = useVisibilityObserver(div);
+ const isVisible = createVisibilityObserver(div);
const instance = _getLastIOInstance();
instance.__TEST__onChange({ isIntersecting: true });
+ flush();
expect(isVisible(), "signal returns incorrect value").toBe(true);
instance.__TEST__onChange({ isIntersecting: false });
+ flush();
expect(isVisible(), "signal returns incorrect value").toBe(false);
@@ -526,19 +657,21 @@ describe("createVisibilityObserver", () => {
test("setter callback dictates the signal value", () =>
createRoot(dispose => {
let goalValue = true;
- const useVisibilityObserver = createVisibilityObserver({}, _ => goalValue);
- const isVisible = useVisibilityObserver(div);
+ const isVisible = createVisibilityObserver(div, {}, _ => goalValue);
const instance = _getLastIOInstance();
instance.__TEST__onChange();
+ flush();
expect(isVisible()).toBe(true);
instance.__TEST__onChange();
+ flush();
expect(isVisible()).toBe(true);
goalValue = false;
instance.__TEST__onChange();
+ flush();
expect(isVisible()).toBe(false);
dispose();
@@ -555,14 +688,14 @@ describe("withOccurrence", () => {
test("returns correct occurrence value", () =>
createRoot(dispose => {
let lastOccurrence: any;
- const useVisibilityObserver = createVisibilityObserver(
+ createVisibilityObserver(
+ div,
{},
withOccurrence((e, { occurrence }) => {
lastOccurrence = occurrence;
return e.isIntersecting;
}),
);
- useVisibilityObserver(div);
const instance = _getLastIOInstance();
instance.__TEST__onChange({ isIntersecting: false });
@@ -593,7 +726,8 @@ describe("withDirection", () => {
let lastDirectionX: any;
let lastDirectionY: any;
- const useVisibilityObserver = createVisibilityObserver(
+ createVisibilityObserver(
+ div,
{},
withDirection((e, { directionX, directionY }) => {
lastDirectionX = directionX;
@@ -601,7 +735,6 @@ describe("withDirection", () => {
return e.isIntersecting;
}),
);
- useVisibilityObserver(div);
const instance = _getLastIOInstance();
instance.__TEST__onChange({
diff --git a/packages/intersection-observer/test/server.test.ts b/packages/intersection-observer/test/server.test.ts
index 5fc1e2dd5..6688e2dd4 100644
--- a/packages/intersection-observer/test/server.test.ts
+++ b/packages/intersection-observer/test/server.test.ts
@@ -13,17 +13,35 @@ describe("API works in SSR", () => {
test("createIntersectionObserver() - SSR", () => {
const el = vi.fn(() => []);
- const cb = vi.fn(() => {});
- expect(() => createIntersectionObserver(el, cb)).not.toThrow();
+ const options = vi.fn(() => ({}));
+ expect(() => createIntersectionObserver(el, options)).not.toThrow();
+ // elements accessor and options accessor are not called on the server
expect(el).not.toBeCalled();
- expect(cb).not.toBeCalled();
+ expect(options).not.toBeCalled();
+ });
+
+ test("createIntersectionObserver() - SSR returns tuple with throwing isVisible", () => {
+ const [entries, isVisible] = createIntersectionObserver(() => []);
+ expect(entries).toEqual([]);
+ expect(() => isVisible({} as Element)).toThrow();
});
test("createViewportObserver() - SSR", () => {
expect(() => createViewportObserver()).not.toThrow();
});
- test("createVisibilityObserver() - SSR", () => {
- expect(() => createVisibilityObserver()).not.toThrow();
+ test("createVisibilityObserver() - SSR throws before first observation without initialValue", () => {
+ const div = {} as Element;
+ const isVisible = createVisibilityObserver(div);
+ expect(() => isVisible()).toThrow();
+ });
+
+ test("createVisibilityObserver() - SSR returns initialValue when provided", () => {
+ const div = {} as Element;
+ const isVisible = createVisibilityObserver(div, { initialValue: false });
+ expect(isVisible()).toBe(false);
+
+ const isVisibleTrue = createVisibilityObserver(div, { initialValue: true });
+ expect(isVisibleTrue()).toBe(true);
});
});
diff --git a/packages/intersection-observer/vitest.config.ts b/packages/intersection-observer/vitest.config.ts
new file mode 100644
index 000000000..b1802d6eb
--- /dev/null
+++ b/packages/intersection-observer/vitest.config.ts
@@ -0,0 +1,46 @@
+import { defineConfig } from "vitest/config";
+import solidPlugin from "vite-plugin-solid";
+
+// solid-js 2.0 removed the "solid-js/web" sub-package in favour of "@solidjs/web".
+// @solid-primitives/utils still imports from "solid-js/web", so alias it here until
+// that package is upgraded.
+export default defineConfig(({ mode }) => {
+ const testSSR = mode === "test:ssr" || mode === "ssr";
+
+ return {
+ plugins: [
+ solidPlugin({
+ hot: false,
+ solid: { generate: testSSR ? "ssr" : "dom", omitNestedClosingTags: false },
+ }),
+ ],
+ resolve: {
+ conditions: testSSR
+ ? ["@solid-primitives/source", "node"]
+ : ["@solid-primitives/source", "browser", "development"],
+ alias: {
+ "solid-js/web": new URL(
+ testSSR
+ ? "./node_modules/@solidjs/web/dist/server.js"
+ : "./node_modules/@solidjs/web/dist/web.js",
+ import.meta.url,
+ ).pathname,
+ },
+ },
+ test: {
+ watch: false,
+ isolate: false,
+ passWithNoTests: true,
+ environment: testSSR ? "node" : "jsdom",
+ transformMode: {
+ web: [/\.[jt]sx$/],
+ },
+ ...(testSSR
+ ? { include: ["test/server.test.{ts,tsx}"] }
+ : {
+ include: ["test/*.test.{ts,tsx}"],
+ exclude: ["test/server.test.{ts,tsx}"],
+ }),
+ },
+ };
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ecadfdb95..8fe0f78d0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -464,9 +464,12 @@ importers:
'@solid-primitives/range':
specifier: workspace:^
version: link:../range
+ '@solidjs/web':
+ specifier: 2.0.0-beta.7
+ version: 2.0.0-beta.7(@solidjs/signals@2.0.0-beta.7)(solid-js@2.0.0-beta.7)
solid-js:
- specifier: ^1.9.7
- version: 1.9.7
+ specifier: 2.0.0-beta.7
+ version: 2.0.0-beta.7
packages/jsx-tokenizer:
dependencies:
@@ -1048,10 +1051,10 @@ importers:
version: link:../packages/utils
'@solidjs/meta':
specifier: ^0.29.3
- version: 0.29.4(solid-js@1.9.7)
+ version: 0.29.4(solid-js@2.0.0-experimental.16)
'@solidjs/router':
specifier: ^0.13.1
- version: 0.13.6(solid-js@1.9.7)
+ version: 0.13.6(solid-js@2.0.0-experimental.16)
clsx:
specifier: ^2.0.0
version: 2.1.1
@@ -1078,13 +1081,13 @@ importers:
version: 1.77.8
solid-dismiss:
specifier: ^1.7.121
- version: 1.8.2(solid-js@1.9.7)
+ version: 1.8.2(solid-js@2.0.0-experimental.16)
solid-icons:
specifier: ^1.1.0
- version: 1.1.0(solid-js@1.9.7)
+ version: 1.1.0(solid-js@2.0.0-experimental.16)
solid-tippy:
specifier: ^0.2.1
- version: 0.2.1(solid-js@1.9.7)(tippy.js@6.3.7)
+ version: 0.2.1(solid-js@2.0.0-experimental.16)(tippy.js@6.3.7)
tippy.js:
specifier: ^6.3.7
version: 6.3.7
@@ -2042,6 +2045,7 @@ packages:
'@graphql-tools/prisma-loader@8.0.4':
resolution: {integrity: sha512-hqKPlw8bOu/GRqtYr0+dINAI13HinTVYBDqhwGAPIFmLr5s+qKskzgCiwbsckdrb5LWVFmVZc+UXn80OGiyBzg==}
engines: {node: '>=16.0.0'}
+ deprecated: 'This package was intended to be used with an older versions of Prisma.\nThe newer versions of Prisma has a different approach to GraphQL integration.\nTherefore, this package is no longer needed and has been deprecated and removed.\nLearn more: https://www.prisma.io/graphql'
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
@@ -2587,11 +2591,23 @@ packages:
peerDependencies:
solid-js: ^1.5.3
+ '@solidjs/signals@0.11.3':
+ resolution: {integrity: sha512-udMfutYPOlcxKUmc5+n1QtarsxOiAlC6LJY2TqFyaMwdXgo+reiYUcYGDlOiAPXfCLE0lavZHQ/6GT5pJbXKBA==}
+
+ '@solidjs/signals@2.0.0-beta.7':
+ resolution: {integrity: sha512-SgK6oQlQZofz82LiEJ2RzT3sbs1lWTqFEtLoWjLsUo/dk1v9EoIFpJJlmvgkXvNugASWG+l1yOHa1a8lPamxug==}
+
'@solidjs/start@1.1.4':
resolution: {integrity: sha512-ma1TBYqoTju87tkqrHExMReM5Z/+DTXSmi30CCTavtwuR73Bsn4rVGqm528p4sL2koRMfAuBMkrhuttjzhL68g==}
peerDependencies:
vinxi: ^0.5.3
+ '@solidjs/web@2.0.0-beta.7':
+ resolution: {integrity: sha512-m5VjmDBufrOX0ZKGbhvwkT0CPK0TbMxDbxVPDB1PH2evGbWXQZcUlrpFM1N8RBO5md3aR/T1PgMfnOjleJbrRg==}
+ peerDependencies:
+ '@solidjs/signals': ^2.0.0-beta.7
+ solid-js: ^2.0.0-beta.7
+
'@speed-highlight/core@1.2.7':
resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==}
@@ -3513,6 +3529,7 @@ packages:
dax-sh@0.43.2:
resolution: {integrity: sha512-uULa1sSIHgXKGCqJ/pA0zsnzbHlVnuq7g8O2fkHokWFNwEGIhh5lAJlxZa1POG5En5ba7AU4KcBAvGQWMMf8rg==}
+ deprecated: This package has moved to simply be 'dax' instead of 'dax-sh'
db0@0.3.2:
resolution: {integrity: sha512-xzWNQ6jk/+NtdfLyXEipbX55dmDSeteLFt/ayF+wZUU5bzKgmrDOxmInUTbyVRp46YwnJdkDA1KhB7WIXFofJw==}
@@ -4164,11 +4181,12 @@ packages:
glob@10.4.5:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
- deprecated: Glob versions prior to v9 are no longer supported
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
globals@11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
@@ -5892,10 +5910,20 @@ packages:
peerDependencies:
seroval: ^1.0
+ seroval-plugins@1.5.2:
+ resolution: {integrity: sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ seroval: ^1.0
+
seroval@1.3.2:
resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==}
engines: {node: '>=10'}
+ seroval@1.5.2:
+ resolution: {integrity: sha512-xcRN39BdsnO9Tf+VzsE7b3JyTJASItIV1FVFewJKCFcW4s4haIKS3e6vj8PGB9qBwC7tnuOywQMdv5N4qkzi7Q==}
+ engines: {node: '>=10'}
+
serve-placeholder@2.0.2:
resolution: {integrity: sha512-/TMG8SboeiQbZJWRlfTCqMs2DD3SZgWp0kDQePz9yUuCnDfDh/92gf7/PxGhzXTKBIPASIHxFcZndoNbp6QOLQ==}
@@ -6001,6 +6029,12 @@ packages:
solid-js@1.9.7:
resolution: {integrity: sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==}
+ solid-js@2.0.0-beta.7:
+ resolution: {integrity: sha512-7JHs+BhLeZXoU+u9dG+eKnyxxfZyGpOuJEBbN/1XbHKO/WhxecdplOAurlg/YDllNWPhsbXqmLR1H2paqSu62g==}
+
+ solid-js@2.0.0-experimental.16:
+ resolution: {integrity: sha512-zZ1dU7cR0EnvLnrYiRLQbCFiDw5blLdlqmofgLzKUYE1TCMWDcisBlSwz0Ez8l4yXB4adbdhtaYCuynH4xSq9A==}
+
solid-refresh@0.6.3:
resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==}
peerDependencies:
@@ -6195,6 +6229,7 @@ packages:
tar@7.4.3:
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
engines: {node: '>=18'}
+ deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
term-size@2.2.1:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
@@ -6710,6 +6745,7 @@ packages:
whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
+ deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
@@ -8576,18 +8612,22 @@ snapshots:
dependencies:
solid-js: 1.9.7
- '@solidjs/meta@0.29.4(solid-js@1.9.7)':
+ '@solidjs/meta@0.29.4(solid-js@2.0.0-experimental.16)':
dependencies:
- solid-js: 1.9.7
+ solid-js: 2.0.0-experimental.16
- '@solidjs/router@0.13.6(solid-js@1.9.7)':
+ '@solidjs/router@0.13.6(solid-js@2.0.0-experimental.16)':
dependencies:
- solid-js: 1.9.7
+ solid-js: 2.0.0-experimental.16
'@solidjs/router@0.8.4(solid-js@1.9.7)':
dependencies:
solid-js: 1.9.7
+ '@solidjs/signals@0.11.3': {}
+
+ '@solidjs/signals@2.0.0-beta.7': {}
+
'@solidjs/start@1.1.4(solid-js@1.9.7)(vinxi@0.5.7(@types/node@22.15.31)(db0@0.3.2)(ioredis@5.6.1)(jiti@2.4.2)(sass@1.77.8)(terser@5.42.0)(tsx@4.20.2)(yaml@2.5.0))(vite@6.3.5(@types/node@22.15.31)(jiti@2.4.2)(sass@1.77.8)(terser@5.42.0)(tsx@4.20.2)(yaml@2.5.0))':
dependencies:
'@tanstack/server-functions-plugin': 1.121.0(vite@6.3.5(@types/node@22.15.31)(jiti@2.4.2)(sass@1.77.8)(terser@5.42.0)(tsx@4.20.2)(yaml@2.5.0))
@@ -8611,6 +8651,13 @@ snapshots:
- supports-color
- vite
+ '@solidjs/web@2.0.0-beta.7(@solidjs/signals@2.0.0-beta.7)(solid-js@2.0.0-beta.7)':
+ dependencies:
+ '@solidjs/signals': 2.0.0-beta.7
+ seroval: 1.5.2
+ seroval-plugins: 1.5.2(seroval@1.5.2)
+ solid-js: 2.0.0-beta.7
+
'@speed-highlight/core@1.2.7': {}
'@supabase/auth-js@2.67.3':
@@ -12441,8 +12488,14 @@ snapshots:
dependencies:
seroval: 1.3.2
+ seroval-plugins@1.5.2(seroval@1.5.2):
+ dependencies:
+ seroval: 1.5.2
+
seroval@1.3.2: {}
+ seroval@1.5.2: {}
+
serve-placeholder@2.0.2:
dependencies:
defu: 6.1.4
@@ -12557,13 +12610,13 @@ snapshots:
dot-case: 3.0.4
tslib: 2.8.1
- solid-dismiss@1.8.2(solid-js@1.9.7):
+ solid-dismiss@1.8.2(solid-js@2.0.0-experimental.16):
dependencies:
- solid-js: 1.9.7
+ solid-js: 2.0.0-experimental.16
- solid-icons@1.1.0(solid-js@1.9.7):
+ solid-icons@1.1.0(solid-js@2.0.0-experimental.16):
dependencies:
- solid-js: 1.9.7
+ solid-js: 2.0.0-experimental.16
solid-js@1.9.7:
dependencies:
@@ -12571,6 +12624,20 @@ snapshots:
seroval: 1.3.2
seroval-plugins: 1.3.2(seroval@1.3.2)
+ solid-js@2.0.0-beta.7:
+ dependencies:
+ '@solidjs/signals': 2.0.0-beta.7
+ csstype: 3.1.3
+ seroval: 1.5.2
+ seroval-plugins: 1.5.2(seroval@1.5.2)
+
+ solid-js@2.0.0-experimental.16:
+ dependencies:
+ '@solidjs/signals': 0.11.3
+ csstype: 3.1.3
+ seroval: 1.5.2
+ seroval-plugins: 1.5.2(seroval@1.5.2)
+
solid-refresh@0.6.3(solid-js@1.9.7):
dependencies:
'@babel/generator': 7.27.5
@@ -12580,9 +12647,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- solid-tippy@0.2.1(solid-js@1.9.7)(tippy.js@6.3.7):
+ solid-tippy@0.2.1(solid-js@2.0.0-experimental.16)(tippy.js@6.3.7):
dependencies:
- solid-js: 1.9.7
+ solid-js: 2.0.0-experimental.16
tippy.js: 6.3.7
solid-transition-group@0.2.3(solid-js@1.9.7):