Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/range-solid2-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@solid-primitives/range": major
---

Migrate to Solid.js v2.0 (beta.13)

## Breaking Changes

**Peer dependencies**: `solid-js@^2.0.0-beta.13` and `@solidjs/web@^2.0.0-beta.13` are now required.

- `isServer` is now imported from `@solidjs/web` (was `solid-js/web`)
- `JSX` types are now imported from `@solidjs/web` (was `solid-js`)
- `indexRange` internal signals now use `ownedWrite: true` so they can be updated from inside reactive scopes (e.g. when `IndexRange` wraps in `createMemo`)
- `createEffect` usage in user code must use the split compute/apply form required by Solid 2.0 — see updated README examples
- Signal writes are now batched by default; call `flush()` after writing signals before reading the resulting mapped array
- `<Repeat>` is **deprecated** — Solid 2.0 ships an equivalent built-in `<Repeat count={n}>` in `@solidjs/web`. See the README for migration guidance. The `repeat` primitive continues to be supported for cases that require incremental child creation/disposal.
81 changes: 68 additions & 13 deletions packages/range/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@

Control Flow Primitives for displaying a number range or given number of elements.

- [`createNumericRange`](#createnumericrange) - Reactively generates a number array for a given range. Convenient companion to the built-in `<For>`.
- [`repeat`](#repeat) - Primitive for mapping a number of elements. Underlying helper for the [`<Repeat>`](#repeat-1) control flow.
- [`<Repeat>`](#repeat-1) - Control Flow Component for displaying a number of elements.
- [`<Repeat>`](#repeat-1) - Control Flow Component for displaying a number of elements. ⚠️ Deprecated — use Solid 2.0's built-in `<Repeat>`.
Comment thread
davedbase marked this conversation as resolved.
- [`mapRange`](#maprange) - Primitive for mapping a number range of given start, end, and step values. Underlying helper for the [`<Range>`](#range) control flow.
- [`<Range>`](#range) - Control Flow Component for displaying a number range of elements.
- [`indexRange`](#indexrange) - Primitive for mapping a number range while keeping previous elements of the same index. Underlying helper for the [`<IndexRange>`](#indexrange-1) control flow.
Expand All @@ -25,15 +26,50 @@ npm install @solid-primitives/range
yarn add @solid-primitives/range
```

Requires `solid-js` and `@solidjs/web` v2.0.0-beta.13 or later as peer dependencies.

## `createNumericRange`

Reactively generates an array of numbers for the given range. Mirrors the `range()` API from Python — one argument gives `[0, to)`, two or three give `[start, to)` with an optional step.

All arguments accept either a plain number or a reactive accessor. Pairs naturally with Solid 2.0's built-in `<For>`.

```ts
// static
createNumericRange(5) // [0, 1, 2, 3, 4]
createNumericRange(2, 7) // [2, 3, 4, 5, 6]
createNumericRange(0, 10, 2) // [0, 2, 4, 6, 8]
createNumericRange(5, 0) // [5, 4, 3, 2, 1] (descending)

// reactive
const [to, setTo] = createSignal(5);
const nums = createNumericRange(to); // updates when `to` changes

// compose with <For> — pass the signal, not the called value
const nums = createNumericRange(count);
<For each={nums()}>{n => <div>{n}</div>}</For>
```

#### Definition

```ts
function createNumericRange(to: MaybeAccessor<number>): Accessor<number[]>;
function createNumericRange(
start: MaybeAccessor<number>,
to: MaybeAccessor<number>,
step?: MaybeAccessor<number>,
): Accessor<number[]>;
```

## `repeat`

Reactively maps a number range of specified length with a callback function - underlying helper for the [`<Repeat>`](#repeat-1) control flow.
Reactively maps a number range of specified length with a callback function - underlying helper for the `<Repeat>` control flow.

```ts
const [length, setLength] = createSignal(10)
const mapped = repeat(length, index => {
const [value, setValue] = createSignal(index);
createEffect(() => {...})
createEffect(() => value(), v => { /* react to value */ })
return value
})
```
Expand All @@ -52,6 +88,25 @@ function repeat<T>(

## `<Repeat>`

> **Deprecated** — Solid 2.0 ships a built-in `<Repeat>` in `@solidjs/web`. Migrate to that:
>
> ```tsx
> import { Repeat } from "@solidjs/web";
>
> // basic — children receive a plain index number
> <Repeat count={10}>{(i) => <div>{i}</div>}</Repeat>
>
> // with offset (replaces a start > 0 use-case)
> <Repeat count={8} from={2}>{(i) => <div>{i}</div>}</Repeat>
>
> // with fallback — wrap in <Show> since built-in <Repeat> has no fallback prop
> <Show when={count() > 0} fallback={<p>no items</p>}>
> <Repeat count={count()}>{(i) => <div>{i}</div>}</Repeat>
> </Show>
> ```
>
> **Note:** The built-in `<Repeat>` does not diff — it re-renders all children when `count` changes. If incremental creation/disposal of children is important for your use case, keep using this primitive.

Control Flow component for displaying a specified number of elements.

The `times` prop is reactive – changing it will only create new elements for added numbers.
Expand Down Expand Up @@ -84,17 +139,17 @@ function Repeat<T>(props: {

## `mapRange`

Reactively maps a number range of specified `stop`, `to` and `step`, with a callback function - underlying helper for the [`<Range>`](#range) control flow.
Reactively maps a number range of specified `start`, `to` and `step`, with a callback function - underlying helper for the [`<Range>`](#range) control flow.

All `stop`, `to` and `step` arguments are accessors, and changing them will cause the mapped array to be recalculated, mapping new items for numbers added to the range.
All `start`, `to` and `step` arguments are accessors, and changing them will cause the mapped array to be recalculated, mapping new items for numbers added to the range.

`step` will become negative _(the range will be descending)_ if `to` is smaller than `start`. Range stops at `to`, it is not included in the range.

```ts
const [to, setTo] = createSignal(5)
const mapped = mapRange(() => 0, to, () => 0.5, number => {
const [value, setValue] = createSignal(number);
createEffect(() => {...})
createEffect(() => value(), v => { /* react to value */ })
return value
})
mapped() // => [0, 0.5, 1, 1.5, 2...]
Expand All @@ -119,7 +174,7 @@ function mapRange<T>(

Creates a list of elements by mapping a number range of specified `start`, `to`, and `step`.

All `stop`, `to` and `step` props are reactive, and changing them will cause the elements array to be recalculated, creating new elements for numbers added to the range.
All `start`, `to` and `step` props are reactive, and changing them will cause the elements array to be recalculated, creating new elements for numbers added to the range.

- `start` defaults to 0.

Expand Down Expand Up @@ -157,7 +212,7 @@ const [step, setStep] = createSignal(2);

#### Definition

`RangeProps` is an interface of `stop`, `to` and `step` props, OR `0`, `1` and `2` indexes of a spread array.
`RangeProps` is an interface of `start`, `to` and `step` props, OR `0`, `1` and `2` indexes of a spread array.

```ts
function Range<T>(
Expand All @@ -170,9 +225,9 @@ function Range<T>(

## `indexRange`

Primitive for mapping a number range of specified `stop`, `to` and `step`, while keeping previous elements of the same index. Underlying helper for the [`<IndexRange>`](#indexrange-1) control flow.
Primitive for mapping a number range of specified `start`, `to` and `step`, while keeping previous elements of the same index. Underlying helper for the [`<IndexRange>`](#indexrange-1) control flow.

All `stop`, `to` and `step` arguments are accessors, and changing them will cause the mapped array to be recalculated, mapping new items appended at the end of the range.
All `start`, `to` and `step` arguments are accessors, and changing them will cause the mapped array to be recalculated, mapping new items appended at the end of the range.

`step` will become negative _(the range will be descending)_ if `to` is smaller than `start`. Range stops at `to`, it is not included in the range.

Expand All @@ -184,7 +239,7 @@ const mapped = indexRange(
() => 0.5,
number => {
const [value, setValue] = createSignal(number());
createEffect(() => handleNewNumber(number()));
createEffect(() => number(), n => handleNewNumber(n));
return value;
},
);
Expand All @@ -210,7 +265,7 @@ function indexRange<T>(

Control Flow Component for displaying a number range of elements, where elements receive a number value as signal, by mapping a number range of specified `start`, `to`, and `step`.

All `stop`, `to` and `step` props are reactive, and changing them will cause the elements array to be recalculated, creating new elements for numbers added to the range.
All `start`, `to` and `step` props are reactive, and changing them will cause the elements array to be recalculated, creating new elements for numbers added to the range.

- `start` defaults to 0.

Expand Down Expand Up @@ -248,7 +303,7 @@ const [step, setStep] = createSignal(2);

#### Definition

`RangeProps` is an interface of `stop`, `to` and `step` props, OR `0`, `1` and `2` indexes of a spread array.
`RangeProps` is an interface of `start`, `to` and `step` props, OR `0`, `1` and `2` indexes of a spread array.

```ts
function IndexRange<T>(
Expand Down
11 changes: 6 additions & 5 deletions packages/range/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@solid-primitives/range",
"version": "0.2.4",
"description": "Control Flow Primitives for displaying given number or a number range of elements.",
"description": "Control Flow Primitives for number ranges: createNumericRange, mapRange, indexRange, repeat, and their JSX component counterparts.",
"author": "Damian Tarnawski @thetarnav <gthetarnav@gmail.com>",
"contributors": [],
"license": "MIT",
Expand All @@ -17,11 +17,11 @@
"name": "range",
"stage": 1,
"list": [
"createNumericRange",
"repeat",
"mapRange",
"indexRange",
"Repeat",
"Range",
"IndexRange"
],
"category": "Control Flow"
Expand Down Expand Up @@ -57,14 +57,15 @@
"test:ssr": "pnpm run vitest --mode ssr"
},
"devDependencies": {
"solid-js": "^1.9.7",
"solid-transition-group": "^0.2.3"
"@solidjs/web": "2.0.0-beta.13",
"solid-js": "2.0.0-beta.13"
},
"dependencies": {
"@solid-primitives/utils": "workspace:^"
},
"peerDependencies": {
"solid-js": "^1.6.12"
"@solidjs/web": "^2.0.0-beta.13",
"solid-js": "^2.0.0-beta.13"
},
"typesVersions": {}
}
44 changes: 44 additions & 0 deletions packages/range/src/createNumericRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Accessor } from "solid-js";
import type { MaybeAccessor } from "@solid-primitives/utils";
import { access } from "@solid-primitives/utils";
import { mapRange } from "./mapRange.js";

/**
* Reactively generates an array of numbers for the given range.
*
* When called with one argument, generates `[0, 1, ..., to - 1]`.
* When called with two or three arguments, generates `[start, start + step, ..., to - 1]`.
*
* Step direction is inferred from the relationship between `start` and `to` — a negative
* `to` relative to `start` produces a descending range regardless of the sign of `step`.
*
* @param startOrTo start of the range, or — when `to` is omitted — the end (start defaults to 0)
* @param to end of the range (not included)
* @param step difference between consecutive values (defaults to 1)
* @returns accessor returning the current number array
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#createnumericrange
* @example
* ```ts
* const nums = createNumericRange(5); // [0, 1, 2, 3, 4]
* const nums = createNumericRange(2, 7); // [2, 3, 4, 5, 6]
* const nums = createNumericRange(0, 10, 2); // [0, 2, 4, 6, 8]
*
* // reactive
* const [to, setTo] = createSignal(5);
* const nums = createNumericRange(to); // updates when `to` changes
*
* // use with <For>
* const nums = createNumericRange(count);
* <For each={nums()}>{n => <div>{n}</div>}</For>
* ```
*/
export function createNumericRange(
startOrTo: MaybeAccessor<number>,
to?: MaybeAccessor<number>,
step: MaybeAccessor<number> = 1,
): Accessor<number[]> {
const getStart = to !== undefined ? () => access(startOrTo) : () => 0;
const getTo = to !== undefined ? () => access(to) : () => access(startOrTo);
const getStep = () => access(step);
return mapRange(getStart, getTo, getStep, n => n);
}
1 change: 1 addition & 0 deletions packages/range/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { type RangeProps } from "./common.js";
export * from "./repeat.js";
export * from "./mapRange.js";
export * from "./indexRange.js";
export * from "./createNumericRange.js";
6 changes: 3 additions & 3 deletions packages/range/src/indexRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import {
type Setter,
untrack,
DEV,
type JSX,
createMemo,
} from "solid-js";
import { isServer } from "solid-js/web";
import { isServer, type JSX } from "@solidjs/web";
import { INTERNAL_OPTIONS } from "@solid-primitives/utils";
import { abs, ceil, min, type RangeProps, sign, toFunction, accessor } from "./common.js";

/**
Expand Down Expand Up @@ -49,7 +49,7 @@ export function indexRange<T>(

const mapper = (i: number, n: number): void =>
createRoot(dispose => {
const [number, setNumber] = createSignal(n);
const [number, setNumber] = createSignal(n, INTERNAL_OPTIONS);
disposers[i] = dispose;
items[i] = mapFn(number);
setters[i] = setNumber;
Expand Down
4 changes: 2 additions & 2 deletions packages/range/src/mapRange.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Accessor, createRoot, onCleanup, untrack, DEV, type JSX, createMemo } from "solid-js";
import { isServer } from "solid-js/web";
import { type Accessor, createRoot, onCleanup, untrack, DEV, createMemo } from "solid-js";
import { isServer, type JSX } from "@solidjs/web";
import { abs, accessor, ceil, floor, min, type RangeProps, toFunction } from "./common.js";

/**
Expand Down
33 changes: 17 additions & 16 deletions packages/range/src/repeat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type Accessor, type JSX, createMemo, createRoot, onCleanup } from "solid-js";
import { type Accessor, createMemo, createRoot, onCleanup } from "solid-js";
import { type JSX } from "@solidjs/web";
import { toFunction } from "./common.js";

/**
Expand All @@ -8,15 +9,6 @@ import { toFunction } from "./common.js";
* @param options a fallback for when the input list is empty or missing
* @returns mapped input array signal
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#repeat
* @example
* ```tsx
* const [length, setLength] = createSignal(10)
* const mapped = repeat(length, index => {
* const [value, setValue] = createSignal(index);
* createEffect(() => {...})
* return value
* })
* ```
*/
export function repeat<T>(
times: Accessor<number>,
Expand Down Expand Up @@ -89,16 +81,25 @@ export function repeat<T>(
}

/**
* Creates a range of elements `of` specified size.
* Creates a range of elements of specified size.
* @param times number of elements
* @param fallback element returned when `of` equals 0
* @param fallback element returned when `times` equals 0
* @param children render function
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#Repeat-1
* @example
* @deprecated Use the built-in `<Repeat>` from `@solidjs/web` instead:
* ```tsx
* <Repeat times={10}>
* {n => <div>{n}</div>}
* </Repeat>
* import { Repeat } from "@solidjs/web";
*
* // basic
* <Repeat count={10}>{(i) => <div>{i}</div>}</Repeat>
*
* // with offset
* <Repeat count={10} from={2}>{(i) => <div>{i}</div>}</Repeat>
*
* // with fallback
* <Show when={count() > 0} fallback={<p>no items</p>}>
* <Repeat count={count()}>{(i) => <div>{i}</div>}</Repeat>
* </Show>
* ```
*/
export function Repeat<T>(props: {
Expand Down
Loading