From fd45ea21a63d7bcb0efa2b2c31d995938e9ce90c Mon Sep 17 00:00:00 2001 From: Ryan Conceicao Date: Sun, 24 Sep 2023 16:54:59 +0800 Subject: [PATCH 1/4] Add selectors --- packages/selectors/LICENSE | 21 ++++++++++ packages/selectors/README.md | 44 ++++++++++++++++++++ packages/selectors/dev/index.tsx | 37 +++++++++++++++++ packages/selectors/package.json | 57 ++++++++++++++++++++++++++ packages/selectors/src/index.ts | 33 +++++++++++++++ packages/selectors/test/index.test.ts | 44 ++++++++++++++++++++ packages/selectors/test/server.test.ts | 19 +++++++++ packages/selectors/tsconfig.json | 3 ++ pnpm-lock.yaml | 6 +++ 9 files changed, 264 insertions(+) create mode 100644 packages/selectors/LICENSE create mode 100644 packages/selectors/README.md create mode 100644 packages/selectors/dev/index.tsx create mode 100644 packages/selectors/package.json create mode 100644 packages/selectors/src/index.ts create mode 100644 packages/selectors/test/index.test.ts create mode 100644 packages/selectors/test/server.test.ts create mode 100644 packages/selectors/tsconfig.json 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..2feff52ef --- /dev/null +++ b/packages/selectors/src/index.ts @@ -0,0 +1,33 @@ +import { createSelector } from "solid-js"; + +/** + * Solid's `createReaction` that is based on pure computation *(runs before render, and is non-batching)* + * + * @param onInvalidate callback that runs when the tracked sources trigger update + * @param options set computation name for debugging pourposes + * - `options.initial` — an array of functions to be run initially and tracked. *(useful for runing code before other pure computations)* + * @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': From 5f610e8ace0db31af9f8eee447aff3a7665e0f15 Mon Sep 17 00:00:00 2001 From: Ryan Conceicao Date: Sun, 24 Sep 2023 17:27:53 +0800 Subject: [PATCH 2/4] fix: jsdoc --- packages/selectors/src/index.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/selectors/src/index.ts b/packages/selectors/src/index.ts index 2feff52ef..091212a57 100644 --- a/packages/selectors/src/index.ts +++ b/packages/selectors/src/index.ts @@ -1,11 +1,10 @@ import { createSelector } from "solid-js"; /** - * Solid's `createReaction` that is based on pure computation *(runs before render, and is non-batching)* + * Wrapper around `createSelector` to create a selector for an array. * - * @param onInvalidate callback that runs when the tracked sources trigger update + * @param source source array signal to create the selector from * @param options set computation name for debugging pourposes - * - `options.initial` — an array of functions to be run initially and tracked. *(useful for runing code before other pure computations)* * @returns selector for the array * * @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/selectors#createArraySelector From b4294233348739f12f3176f08c0d67e22fe9b3c6 Mon Sep 17 00:00:00 2001 From: Ryan Conceicao Date: Sun, 24 Sep 2023 17:29:15 +0800 Subject: [PATCH 3/4] fix: jsdoc --- packages/selectors/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/selectors/src/index.ts b/packages/selectors/src/index.ts index 091212a57..aaa1a6ce6 100644 --- a/packages/selectors/src/index.ts +++ b/packages/selectors/src/index.ts @@ -3,8 +3,8 @@ 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 pourposes + * @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 From 0e5a5bdee91665b4c43448c57d22e04248d4c90d Mon Sep 17 00:00:00 2001 From: Ryan Conceicao Date: Sun, 24 Sep 2023 17:30:14 +0800 Subject: [PATCH 4/4] fix: jsdoc example --- packages/selectors/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/selectors/src/index.ts b/packages/selectors/src/index.ts index aaa1a6ce6..00fa8e3eb 100644 --- a/packages/selectors/src/index.ts +++ b/packages/selectors/src/index.ts @@ -14,7 +14,10 @@ import { createSelector } from "solid-js"; * const [selectedItems] = createSignal([]) * const isSelected = createArraySelector(selectedItems) * - * {(item) =>
  • {item}
  • } + * {(item) => + *
  • + * {item} + *
  • } *
    */ export function createArraySelector(