diff --git a/packages/selectors/LICENSE b/packages/selectors/LICENSE new file mode 100644 index 000000000..38b41d975 --- /dev/null +++ b/packages/selectors/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Solid Primitives Working Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/selectors/README.md b/packages/selectors/README.md new file mode 100644 index 000000000..c0ccb2f4b --- /dev/null +++ b/packages/selectors/README.md @@ -0,0 +1,44 @@ +

+ Solid Primitives selectors +

+ +# @solid-primitives/selectors + +[![turborepo](https://img.shields.io/badge/built%20with-turborepo-cc00ff.svg?style=for-the-badge&logo=turborepo)](https://turborepo.org/) +[![size](https://img.shields.io/bundlephobia/minzip/@solid-primitives/selectors?style=for-the-badge&label=size)](https://bundlephobia.com/package/@solid-primitives/selectors) +[![version](https://img.shields.io/npm/v/@solid-primitives/selectors?style=for-the-badge)](https://www.npmjs.com/package/@solid-primitives/selectors) +[![stage](https://img.shields.io/endpoint?style=for-the-badge&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process) + +A sample primitive that is made up for templating with the following options: + +`createArraySelcetor` - Provides a getter and setter for the primitive. + +## Installation + +```bash +npm install @solid-primitives/selectors +# or +yarn add @solid-primitives/selectors +# or +pnpm add @solid-primitives/selectors +``` + +## How to use it + +```tsx +const list: string[] = ["apple", "pear", "orange"] +const [selectedItems] = createSignal(["apple"]) +const isSelected = createArraySelector(selectedItems) + + + {(item) =>
  • {item}
  • } +
    +``` + +## Demo + +You can use this template for publishing your demo on CodeSandbox: https://codesandbox.io/s/solid-primitives-demo-template-sz95h + +## Changelog + +See [CHANGELOG.md](./CHANGELOG.md) diff --git a/packages/selectors/dev/index.tsx b/packages/selectors/dev/index.tsx new file mode 100644 index 000000000..753e8ce71 --- /dev/null +++ b/packages/selectors/dev/index.tsx @@ -0,0 +1,37 @@ +import { Component, For, createSignal } from "solid-js"; +import { createArraySelector } from "../src/index.js"; + +const items = [1, 2, 3]; + +const App: Component = () => { + const [selectedItems, setSelectedItems] = createSignal([]); + const isSelected = createArraySelector(selectedItems); + + return ( + <> + + {item => ( +
    + { + const filtered = selectedItems().filter(i => i === item); + if (e.currentTarget.checked) { + if (filtered.length === 0) { + setSelectedItems([...selectedItems(), item]); + } + } else { + setSelectedItems(filtered); + } + }} + /> + {item} +
    + )} +
    + + ); +}; + +export default App; diff --git a/packages/selectors/package.json b/packages/selectors/package.json new file mode 100644 index 000000000..d08a822c9 --- /dev/null +++ b/packages/selectors/package.json @@ -0,0 +1,57 @@ +{ + "name": "@solid-primitives/selectors", + "version": "0.0.1", + "description": "Primitives that support creating selectors.", + "author": "Ryan Conceicao ", + "contributors": [], + "license": "MIT", + "homepage": "https://github.com/solidjs-community/solid-primitives/tree/main/packages/selectors#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/solidjs-community/solid-primitives.git" + }, + "bugs": { + "url": "https://github.com/solidjs-community/solid-primitives/issues" + }, + "primitive": { + "name": "selectors", + "stage": 0, + "list": [ + "createArraySelector" + ], + "category": "Reactivity" + }, + "keywords": [ + "solid", + "primitives", + "selector" + ], + "private": false, + "sideEffects": false, + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "browser": {}, + "exports": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": "./dist/index.cjs" + }, + "typesVersions": {}, + "scripts": { + "dev": "tsx ../../scripts/dev.ts", + "build": "tsx ../../scripts/build.ts", + "vitest": "vitest -c ../../configs/vitest.config.ts", + "test": "pnpm run vitest", + "test:ssr": "pnpm run vitest --mode ssr" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } +} diff --git a/packages/selectors/src/index.ts b/packages/selectors/src/index.ts new file mode 100644 index 000000000..00fa8e3eb --- /dev/null +++ b/packages/selectors/src/index.ts @@ -0,0 +1,35 @@ +import { createSelector } from "solid-js"; + +/** + * Wrapper around `createSelector` to create a selector for an array. + * + * @param source - source array signal to create the selector from + * @param options - set computation name for debugging purposes + * @returns selector for the array + * + * @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/selectors#createArraySelector + * + * @example + * const list: string[] = [...] + * const [selectedItems] = createSignal([]) + * const isSelected = createArraySelector(selectedItems) + * + * {(item) => + *
  • + * {item} + *
  • } + *
    + */ +export function createArraySelector( + source: () => Array, + options?: { name?: string }, +): (k: T) => boolean { + const selector = createSelector, T>( + source, + (a, b) => { + return b.includes(a); + }, + options, + ); + return selector; +} diff --git a/packages/selectors/test/index.test.ts b/packages/selectors/test/index.test.ts new file mode 100644 index 000000000..aab25f2c6 --- /dev/null +++ b/packages/selectors/test/index.test.ts @@ -0,0 +1,44 @@ +import { describe, test, expect } from "vitest"; +import { createRoot, createSignal } from "solid-js"; +import { createArraySelector } from "../src/index.js"; + +describe("createArraySelector", () => { + test("createArraySelector select single item", () => + createRoot(dispose => { + const [selected, setSelected] = createSignal(["a"]); + const isSelected = createArraySelector(selected); + + expect(isSelected("a"), "initial value selected for 'a' should be true").toBe(true); + expect(isSelected("b"), "initial value selected for 'b' should be false").toBe(false); + setSelected(["c"]); + expect(isSelected("a"), "value after change for 'a' should be false").toBe(false); + expect(isSelected("b"), "value after change for 'b' should be false").toBe(false); + setSelected(["b"]); + expect(isSelected("a"), "value after change for 'a' should be false").toBe(false); + expect(isSelected("b"), "value after change for 'b' should be true").toBe(true); + dispose(); + })); + + test("createArraySelector select multiple items", () => + createRoot(dispose => { + const [selected, setSelected] = createSignal(["a", "b"]); + const isSelected = createArraySelector(selected); + + expect(isSelected("a"), "initial value selected for 'a' should be true").toBe(true); + expect(isSelected("b"), "initial value selected for 'b' should be true").toBe(true); + expect(isSelected("c"), "initial value selected for 'c' should be false").toBe(false); + setSelected(["c", "b"]); + expect(isSelected("a"), "value after change for 'a' should be false").toBe(false); + expect(isSelected("b"), "value after change for 'b' should be true").toBe(true); + expect(isSelected("c"), "value after change for 'c' should be true").toBe(true); + setSelected(["c", "a"]); + expect(isSelected("a"), "value after change for 'a' should be true").toBe(true); + expect(isSelected("b"), "value after change for 'b' should be false").toBe(false); + expect(isSelected("c"), "value after change for 'c' should be true").toBe(true); + setSelected([]); + expect(isSelected("a"), "value after change empty for 'a' should be false").toBe(false); + expect(isSelected("b"), "value after change empty for 'b' should be false").toBe(false); + expect(isSelected("c"), "value after change empty for 'c' should be false").toBe(false); + dispose(); + })); +}); diff --git a/packages/selectors/test/server.test.ts b/packages/selectors/test/server.test.ts new file mode 100644 index 000000000..1d1bbd2f6 --- /dev/null +++ b/packages/selectors/test/server.test.ts @@ -0,0 +1,19 @@ +import { describe, test, expect } from "vitest"; +import { createSignal } from "solid-js"; +import { createArraySelector } from "../src/index.js"; + +describe("createArraySelector", () => { + test("doesn't break in SSR", () => { + const [selected, setSelected] = createSignal(["a"]); + const isSelected = createArraySelector(selected); + + expect(isSelected("a"), "initial value selected for 'a' should be true").toBe(true); + expect(isSelected("b"), "initial value selected for 'b' should be false").toBe(false); + setSelected(["c"]); + expect(isSelected("a"), "value after change for 'a' should be false").toBe(false); + expect(isSelected("b"), "value after change for 'b' should be false").toBe(false); + setSelected(["b"]); + expect(isSelected("a"), "value after change for 'a' should be false").toBe(false); + expect(isSelected("b"), "value after change for 'b' should be true").toBe(true); + }); +}); diff --git a/packages/selectors/tsconfig.json b/packages/selectors/tsconfig.json new file mode 100644 index 000000000..4082f16a5 --- /dev/null +++ b/packages/selectors/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d4fba1b1..5ca87dc6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -834,6 +834,12 @@ importers: specifier: ^1.7.11 version: 1.7.11 + packages/selectors: + devDependencies: + solid-js: + specifier: ^1.7.11 + version: 1.7.11 + packages/set: dependencies: '@solid-primitives/trigger':