Skip to content

Commit 1db7b5f

Browse files
authored
add SetValues (#731)
2 parents ec5775b + e82cb70 commit 1db7b5f

File tree

8 files changed

+336
-5
lines changed

8 files changed

+336
-5
lines changed

.changeset/perfect-dryers-tan.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@solid-primitives/keyed": minor
3+
---
4+
5+
Add `SetValues` control flow component.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ The goal of Solid Primitives is to wrap client and server side functionality to
6565
|<h4>*Control Flow*</h4>|
6666
|[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)|
6767
|[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)|
68-
|[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)|
68+
|[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)|
6969
|[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)|
7070
|[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)|
7171
|[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)|

packages/keyed/README.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Control Flow primitives and components that require specifying explicit keys to
1515
- [`Key`](#key) - Creates a list of elements by mapping items by provided key.
1616
- [`Entries`](#entries) - Creates a list of elements by mapping object entries.
1717
- [`MapEntries`](#mapentries) - Creates a list of elements by mapping Map entries.
18+
- [`SetValues`](#setvalues) - Creates a list of elements by mapping Set values.
1819
- [`Rerun`](#rerun) - Causes the children to rerender when the `on` changes.
1920

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

178179
Third argument of the map function is an index signal.
179180

180-
`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.
181+
`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.
181182

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

193+
## `<SetValues>`
194+
195+
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.
196+
197+
### How to use it
198+
199+
```tsx
200+
import { SetValues } from "@solid-primitives/keyed";
201+
202+
const [set, setSet] = createSignal(new Set());
203+
204+
<SetValues of={set()} fallback={<div>No items</div>}>
205+
{(value) => (
206+
<div>
207+
{value}
208+
</div>
209+
)}
210+
</SetValues>;
211+
```
212+
213+
### Index argument
214+
215+
Second argument of the map function is an index signal.
216+
217+
`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.
218+
219+
```tsx
220+
<SetValues of={set()} fallback={<div>No items</div>}>
221+
{(value, index) => (
222+
<div data-index={index()}>
223+
{value}
224+
</div>
225+
)}
226+
</SetValues>
227+
```
228+
192229
## `<Rerun>`
193230

194231
Causes the children to rerender when the `on` key changes. Equivalent of `v-key` in vue, and `{#key}` in svelte.

packages/keyed/dev/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Component } from "solid-js";
33
import Key from "./key.js";
44
import Entries from "./entries.js";
55
import MapEntries from "./mapEntries.js";
6+
import SetValues from "./setValues.js";
67

78
const App: Component = () => {
89
return (
@@ -19,6 +20,10 @@ const App: Component = () => {
1920
<h4>MapEntries</h4>
2021
<MapEntries />
2122
</div>
23+
<div class="wrapper-v">
24+
<h4>SetValues</h4>
25+
<SetValues />
26+
</div>
2227
</div>
2328
);
2429
};

packages/keyed/dev/setValues.tsx

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// changes to this file might be applicable to similar files - grep 95DB7339-BB2A-4F06-A34A-25DDF8BF7AF7
2+
3+
import { createEffect, createSignal } from "solid-js";
4+
import { SetValues } from "../src/index.js";
5+
import { TransitionGroup } from "solid-transition-group";
6+
7+
const foods = [
8+
"oatmeal",
9+
"plantains",
10+
"cranberries",
11+
"chickpeas",
12+
"tofu",
13+
"Parmesan cheese",
14+
"amaretto",
15+
"sunflower seeds",
16+
"grapes",
17+
"vegemite",
18+
"pasta",
19+
"cider",
20+
"chicken",
21+
"pinto beans",
22+
"bok choy",
23+
"sweet peppers",
24+
"Cappuccino Latte",
25+
"corn",
26+
"broccoli",
27+
"brussels sprouts",
28+
"bread",
29+
"milk",
30+
"honey",
31+
"chips",
32+
"cookie",
33+
];
34+
const randomIndex = (list: readonly any[]): number => Math.floor(Math.random() * list.length);
35+
const getRandomFood = () => foods[randomIndex(foods)]!;
36+
const randomValue = (map: Set<string>): string => {
37+
const values = Array.from(map.values());
38+
return values[randomIndex(values)]!;
39+
};
40+
41+
export default function App() {
42+
const [set, setSet] = createSignal(new Set(["bread", "milk", "honey", "chips", "cookie"]));
43+
44+
const addRandom = () => {
45+
setSet(p => {
46+
p.add(getRandomFood());
47+
return new Set(p);
48+
});
49+
};
50+
const removeRandom = () =>
51+
setSet(p => {
52+
p.delete(randomValue(p));
53+
return new Set(p);
54+
});
55+
const clone = () => setSet(p => new Set(p));
56+
57+
return (
58+
<>
59+
<div class="wrapper-h">
60+
<button class="btn" onclick={addRandom}>
61+
Add
62+
</button>
63+
<button class="btn" onclick={removeRandom}>
64+
Remove
65+
</button>
66+
<button class="btn" onclick={clone}>
67+
Clone
68+
</button>
69+
</div>
70+
<div class="wrapper-h flex-wrap">
71+
<TransitionGroup name="fade">
72+
<SetValues
73+
of={set()}
74+
fallback={<p class="bg-yellow-500 p-1 transition-all">No items.</p>}
75+
>
76+
{(value, index) => {
77+
createEffect(() => {
78+
console.log("Effect:", value);
79+
});
80+
return (
81+
<div class="node relative transition-all duration-500">
82+
{index()}. {value}
83+
<div class="bg-dark-500 text-light-900 absolute -bottom-2 left-2 px-1 text-[9px]">
84+
{value}
85+
</div>
86+
</div>
87+
);
88+
}}
89+
</SetValues>
90+
</TransitionGroup>
91+
</div>
92+
</>
93+
);
94+
}

packages/keyed/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"keyArray",
1717
"Key",
1818
"Entries",
19-
"MapEntries"
19+
"MapEntries",
20+
"SetValues"
2021
],
2122
"category": "Control Flow"
2223
},

packages/keyed/src/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,37 @@ export function MapEntries<K, V>(props: {
239239
) as unknown as JSX.Element;
240240
}
241241

242+
/**
243+
* Creates a list of elements from the values of provided Set
244+
*
245+
* @param props
246+
* @param props.of set to iterate values of (`mySet.values()`)
247+
* @param props.children
248+
* 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:
249+
* ```tsx
250+
* <SetValues of={set()} fallback={<div>No items</div>}>
251+
* {(value, index) => <div data-index={index()}>{value)}</div>}
252+
* </SetValues>
253+
* ```
254+
*
255+
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#setvalues
256+
*/
257+
export function SetValues<T>(props: {
258+
of: Set<T> | undefined | null | false;
259+
fallback?: JSX.Element;
260+
children: (value: T, i: Accessor<number>) => JSX.Element;
261+
}): JSX.Element {
262+
// changes to this function may be applicable to similar functions - grep 4A29BECD-767A-4CC0-AEBB-3543D7B444C6
263+
const mapFn = props.children;
264+
return createMemo(
265+
mapArray(
266+
() => props.of && Array.from(props.of.values()),
267+
mapFn.length < 2 ? value => (mapFn as (value: T) => JSX.Element)(value) : mapFn,
268+
"fallback" in props ? { fallback: () => props.fallback } : undefined,
269+
),
270+
) as unknown as JSX.Element;
271+
}
272+
242273
export type RerunChildren<T> = ((input: T, prevInput: T | undefined) => JSX.Element) | JSX.Element;
243274

244275
/**

0 commit comments

Comments
 (0)