Skip to content

Commit

Permalink
add SetValues
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexErrant committed Jan 6, 2025
1 parent 71f6c5e commit e82cb70
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/perfect-dryers-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solid-primitives/keyed": minor
---

Add `SetValues` control flow component.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ The goal of Solid Primitives is to wrap client and server side functionality to
|<h4>*Control Flow*</h4>|
|[context](https://github.com/solidjs-community/solid-primitives/tree/main/packages/context#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-2.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createContextProvider](https://github.com/solidjs-community/solid-primitives/tree/main/packages/context#createcontextprovider)<br />[MultiProvider](https://github.com/solidjs-community/solid-primitives/tree/main/packages/context#multiprovider)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/context?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/context)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/context?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/context)|
|[jsx-tokenizer](https://github.com/solidjs-community/solid-primitives/tree/main/packages/jsx-tokenizer#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-2.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createTokenizer](https://github.com/solidjs-community/solid-primitives/tree/main/packages/jsx-tokenizer#createtokenizer)<br />[createToken](https://github.com/solidjs-community/solid-primitives/tree/main/packages/jsx-tokenizer#createtoken)<br />[resolveTokens](https://github.com/solidjs-community/solid-primitives/tree/main/packages/jsx-tokenizer#resolvetokens)<br />[isToken](https://github.com/solidjs-community/solid-primitives/tree/main/packages/jsx-tokenizer#istoken)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/jsx-tokenizer?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/jsx-tokenizer)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/jsx-tokenizer?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/jsx-tokenizer)|
|[keyed](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-3.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[keyArray](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#keyarray)<br />[Key](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#key)<br />[Entries](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#entries)<br />[MapEntries](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#mapentries)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/keyed?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/keyed)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/keyed?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/keyed)|
|[keyed](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-3.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[keyArray](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#keyarray)<br />[Key](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#key)<br />[Entries](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#entries)<br />[MapEntries](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#mapentries)<br />[SetValues](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#setvalues)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/keyed?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/keyed)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/keyed?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/keyed)|
|[list](https://github.com/solidjs-community/solid-primitives/tree/main/packages/list#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[listArray](https://github.com/solidjs-community/solid-primitives/tree/main/packages/list#listarray)<br />[List](https://github.com/solidjs-community/solid-primitives/tree/main/packages/list#list)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/list?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/list)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/list?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/list)|
|[range](https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-1.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[repeat](https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#repeat)<br />[mapRange](https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#maprange)<br />[indexRange](https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#indexrange)<br />[Repeat](https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#repeat)<br />[Range](https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#range)<br />[IndexRange](https://github.com/solidjs-community/solid-primitives/tree/main/packages/range#indexrange)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/range?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/range)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/range?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/range)|
|[refs](https://github.com/solidjs-community/solid-primitives/tree/main/packages/refs#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-2.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[mergeRefs](https://github.com/solidjs-community/solid-primitives/tree/main/packages/refs#mergerefs)<br />[resolveElements](https://github.com/solidjs-community/solid-primitives/tree/main/packages/refs#resolveelements)<br />[resolveFirst](https://github.com/solidjs-community/solid-primitives/tree/main/packages/refs#resolvefirst)<br />[Ref](https://github.com/solidjs-community/solid-primitives/tree/main/packages/refs#ref)<br />[Refs](https://github.com/solidjs-community/solid-primitives/tree/main/packages/refs#refs)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/refs?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/refs)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/refs?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/refs)|
Expand Down
39 changes: 38 additions & 1 deletion packages/keyed/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Control Flow primitives and components that require specifying explicit keys to
- [`Key`](#key) - Creates a list of elements by mapping items by provided key.
- [`Entries`](#entries) - Creates a list of elements by mapping object entries.
- [`MapEntries`](#mapentries) - Creates a list of elements by mapping Map entries.
- [`SetValues`](#setvalues) - Creates a list of elements by mapping Set values.
- [`Rerun`](#rerun) - Causes the children to rerender when the `on` changes.

## Installation
Expand Down Expand Up @@ -177,7 +178,7 @@ const [map, setMap] = createSignal(new Map());

Third argument of the map function is an index signal.

`MapEntries` is using [`Map#key()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys) so the index and resulting JSX will follow the insertion order.
`MapEntries` is using [`Map#keys()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys) so the index and resulting JSX will follow the insertion order.

```tsx
<MapEntries of={map()} fallback={<div>No items</div>}>
Expand All @@ -189,6 +190,42 @@ Third argument of the map function is an index signal.
</MapEntries>
```

## `<SetValues>`

Creates a list of elements by mapping Set values. Similar to Solid's `<For>` and `<Index>`, but here, render function takes two arguments, the value and the index argument as a signal.

### How to use it

```tsx
import { SetValues } from "@solid-primitives/keyed";

const [set, setSet] = createSignal(new Set());

<SetValues of={set()} fallback={<div>No items</div>}>
{(value) => (
<div>
{value}
</div>
)}
</SetValues>;
```

### Index argument

Second argument of the map function is an index signal.

`SetValues` is using [`Set#values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/values) so the index and resulting JSX will follow the insertion order.

```tsx
<SetValues of={set()} fallback={<div>No items</div>}>
{(value, index) => (
<div data-index={index()}>
{value}
</div>
)}
</SetValues>
```

## `<Rerun>`

Causes the children to rerender when the `on` key changes. Equivalent of `v-key` in vue, and `{#key}` in svelte.
Expand Down
5 changes: 5 additions & 0 deletions packages/keyed/dev/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Component } from "solid-js";
import Key from "./key.js";
import Entries from "./entries.js";
import MapEntries from "./mapEntries.js";
import SetValues from "./setValues.js";

const App: Component = () => {
return (
Expand All @@ -19,6 +20,10 @@ const App: Component = () => {
<h4>MapEntries</h4>
<MapEntries />
</div>
<div class="wrapper-v">
<h4>SetValues</h4>
<SetValues />
</div>
</div>
);
};
Expand Down
94 changes: 94 additions & 0 deletions packages/keyed/dev/setValues.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// changes to this file might be applicable to similar files - grep 95DB7339-BB2A-4F06-A34A-25DDF8BF7AF7

import { createEffect, createSignal } from "solid-js";
import { SetValues } from "../src/index.js";
import { TransitionGroup } from "solid-transition-group";

const foods = [
"oatmeal",
"plantains",
"cranberries",
"chickpeas",
"tofu",
"Parmesan cheese",
"amaretto",
"sunflower seeds",
"grapes",
"vegemite",
"pasta",
"cider",
"chicken",
"pinto beans",
"bok choy",
"sweet peppers",
"Cappuccino Latte",
"corn",
"broccoli",
"brussels sprouts",
"bread",
"milk",
"honey",
"chips",
"cookie",
];
const randomIndex = (list: readonly any[]): number => Math.floor(Math.random() * list.length);
const getRandomFood = () => foods[randomIndex(foods)]!;
const randomValue = (map: Set<string>): string => {
const values = Array.from(map.values());
return values[randomIndex(values)]!;
};

export default function App() {
const [set, setSet] = createSignal(new Set(["bread", "milk", "honey", "chips", "cookie"]));

const addRandom = () => {
setSet(p => {
p.add(getRandomFood());
return new Set(p);
});
};
const removeRandom = () =>
setSet(p => {
p.delete(randomValue(p));
return new Set(p);
});
const clone = () => setSet(p => new Set(p));

return (
<>
<div class="wrapper-h">
<button class="btn" onclick={addRandom}>
Add
</button>
<button class="btn" onclick={removeRandom}>
Remove
</button>
<button class="btn" onclick={clone}>
Clone
</button>
</div>
<div class="wrapper-h flex-wrap">
<TransitionGroup name="fade">
<SetValues
of={set()}
fallback={<p class="bg-yellow-500 p-1 transition-all">No items.</p>}
>
{(value, index) => {
createEffect(() => {
console.log("Effect:", value);
});
return (
<div class="node relative transition-all duration-500">
{index()}. {value}
<div class="bg-dark-500 text-light-900 absolute -bottom-2 left-2 px-1 text-[9px]">
{value}
</div>
</div>
);
}}
</SetValues>
</TransitionGroup>
</div>
</>
);
}
3 changes: 2 additions & 1 deletion packages/keyed/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"keyArray",
"Key",
"Entries",
"MapEntries"
"MapEntries",
"SetValues"
],
"category": "Control Flow"
},
Expand Down
31 changes: 31 additions & 0 deletions packages/keyed/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,37 @@ export function MapEntries<K, V>(props: {
) as unknown as JSX.Element;
}

/**
* Creates a list of elements from the values of provided Set
*
* @param props
* @param props.of set to iterate values of (`mySet.values()`)
* @param props.children
* a map render function that receives a Set value and **index signal** and returns a JSX-Element; if the list is empty, an optional fallback is returned:
* ```tsx
* <SetValues of={set()} fallback={<div>No items</div>}>
* {(value, index) => <div data-index={index()}>{value)}</div>}
* </SetValues>
* ```
*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#setvalues
*/
export function SetValues<T>(props: {
of: Set<T> | undefined | null | false;
fallback?: JSX.Element;
children: (value: T, i: Accessor<number>) => JSX.Element;
}): JSX.Element {
// changes to this function may be applicable to similar functions - grep 4A29BECD-767A-4CC0-AEBB-3543D7B444C6
const mapFn = props.children;
return createMemo(
mapArray(
() => props.of && Array.from(props.of.values()),
mapFn.length < 2 ? value => (mapFn as (value: T) => JSX.Element)(value) : mapFn,
"fallback" in props ? { fallback: () => props.fallback } : undefined,
),
) as unknown as JSX.Element;
}

export type RerunChildren<T> = ((input: T, prevInput: T | undefined) => JSX.Element) | JSX.Element;

/**
Expand Down
Loading

0 comments on commit e82cb70

Please sign in to comment.