@preact/signals 2.0.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @preact/signals
2
2
 
3
+ ## 2.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#683](https://github.com/preactjs/signals/pull/683) [`4b585a5`](https://github.com/preactjs/signals/commit/4b585a5f15300b73dc66f45effd21342e3f6e5d5) Thanks [@JoviDeCroock](https://github.com/JoviDeCroock)! - Provide `@preact/signals/utils` package with some helpers to make working with signals easier in Preact
8
+
3
9
  ## 2.0.5
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -111,6 +111,84 @@ function Person() {
111
111
 
112
112
  This way we'll bypass checking the virtual-dom and update the DOM property directly.
113
113
 
114
+ ## Utility Components and Hooks
115
+
116
+ The `@preact/signals/utils` package provides additional utility components and hooks to make working with signals even easier.
117
+
118
+ ### Show Component
119
+
120
+ The `Show` component provides a declarative way to conditionally render content based on a signal's value.
121
+
122
+ ```js
123
+ import { Show } from "@preact/signals/utils";
124
+ import { signal } from "@preact/signals";
125
+
126
+ const isVisible = signal(false);
127
+
128
+ function App() {
129
+ return (
130
+ <Show when={isVisible} fallback={<p>Nothing to see here</p>}>
131
+ <p>Now you see me!</p>
132
+ </Show>
133
+ );
134
+ }
135
+
136
+ // You can also use a function to access the value
137
+ function App() {
138
+ return <Show when={isVisible}>{value => <p>The value is {value}</p>}</Show>;
139
+ }
140
+ ```
141
+
142
+ ### For Component
143
+
144
+ The `For` component helps you render lists from signal arrays with automatic caching of rendered items.
145
+
146
+ ```js
147
+ import { For } from "@preact/signals/utils";
148
+ import { signal } from "@preact/signals";
149
+
150
+ const items = signal(["A", "B", "C"]);
151
+
152
+ function App() {
153
+ return (
154
+ <For each={items} fallback={<p>No items</p>}>
155
+ {(item, index) => <div key={index}>Item: {item}</div>}
156
+ </For>
157
+ );
158
+ }
159
+ ```
160
+
161
+ ### Additional Hooks
162
+
163
+ #### useLiveSignal
164
+
165
+ The `useLiveSignal` hook allows you to create a local signal that stays synchronized with an external signal.
166
+
167
+ ```js
168
+ import { useLiveSignal } from "@preact/signals/utils";
169
+ import { signal } from "@preact/signals";
170
+
171
+ const external = signal(0);
172
+
173
+ function Component() {
174
+ const local = useLiveSignal(external);
175
+ // local will automatically update when external changes
176
+ }
177
+ ```
178
+
179
+ #### useSignalRef
180
+
181
+ The `useSignalRef` hook creates a signal that behaves like a React ref with a `.current` property.
182
+
183
+ ```js
184
+ import { useSignalRef } from "@preact/signals/utils";
185
+
186
+ function Component() {
187
+ const ref = useSignalRef(null);
188
+ return <div ref={ref}>The ref's value is {ref.current}</div>;
189
+ }
190
+ ```
191
+
114
192
  ## License
115
193
 
116
194
  `MIT`, see the [LICENSE](../../LICENSE) file.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preact/signals",
3
- "version": "2.0.5",
3
+ "version": "2.1.0",
4
4
  "license": "MIT",
5
5
  "description": "Manage state with style in Preact",
6
6
  "keywords": [],
@@ -30,6 +30,12 @@
30
30
  "browser": "./dist/signals.module.js",
31
31
  "import": "./dist/signals.mjs",
32
32
  "require": "./dist/signals.js"
33
+ },
34
+ "./utils": {
35
+ "types": "./utils/dist/index.d.ts",
36
+ "browser": "./utils/dist/utils.module.js",
37
+ "import": "./utils/dist/utils.mjs",
38
+ "require": "./utils/dist/utils.js"
33
39
  }
34
40
  },
35
41
  "mangle": "../../mangle.json",
@@ -38,7 +44,10 @@
38
44
  "dist",
39
45
  "CHANGELOG.md",
40
46
  "LICENSE",
41
- "README.md"
47
+ "README.md",
48
+ "utils/dist",
49
+ "utils/package.json",
50
+ "utils/src"
42
51
  ],
43
52
  "dependencies": {
44
53
  "@preact/signals-core": "^1.7.0"
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@preact/signals-utils",
3
+ "description": "Sub package for @preact/signals that contains some useful utilities",
4
+ "private": true,
5
+ "amdName": "preactSignalsutils",
6
+ "main": "dist/utils.js",
7
+ "module": "dist/utils.module.js",
8
+ "unpkg": "dist/utils.min.js",
9
+ "types": "dist/index.d.ts",
10
+ "source": "src/index.ts",
11
+ "mangle": "../../../mangle.json",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "browser": "./dist/utils.module.js",
16
+ "import": "./dist/utils.mjs",
17
+ "require": "./dist/utils.js"
18
+ }
19
+ },
20
+ "dependencies": {
21
+ "@preact/signals-core": "workspace:^1.3.0"
22
+ },
23
+ "peerDependencies": {
24
+ "@preact/signals": "workspace:*",
25
+ "preact": ">= 10.25.0"
26
+ }
27
+ }
@@ -0,0 +1,71 @@
1
+ import { ReadonlySignal, Signal } from "@preact/signals-core";
2
+ import { useSignal } from "@preact/signals";
3
+ import { Fragment, createElement, JSX } from "preact";
4
+ import { useMemo } from "preact/hooks";
5
+
6
+ interface ShowProps<T = boolean> {
7
+ when: Signal<T> | ReadonlySignal<T>;
8
+ fallback?: JSX.Element;
9
+ children: JSX.Element | ((value: T) => JSX.Element);
10
+ }
11
+
12
+ export function Show<T = boolean>(props: ShowProps<T>): JSX.Element | null {
13
+ const value = props.when.value;
14
+ if (!value) return props.fallback || null;
15
+ return typeof props.children === "function"
16
+ ? props.children(value)
17
+ : props.children;
18
+ }
19
+
20
+ interface ForProps<T> {
21
+ each:
22
+ | Signal<Array<T>>
23
+ | ReadonlySignal<Array<T>>
24
+ | (() => Signal<Array<T>> | ReadonlySignal<Array<T>>);
25
+ fallback?: JSX.Element;
26
+ children: (value: T, index: number) => JSX.Element;
27
+ }
28
+
29
+ export function For<T>(props: ForProps<T>): JSX.Element | null {
30
+ const cache = useMemo(() => new Map(), []);
31
+ let list = (
32
+ (typeof props.each === "function" ? props.each() : props.each) as Signal<
33
+ Array<T>
34
+ >
35
+ ).value;
36
+
37
+ if (!list.length) return props.fallback || null;
38
+
39
+ const items = list.map((value, key) => {
40
+ if (!cache.has(value)) {
41
+ cache.set(value, props.children(value, key));
42
+ }
43
+ return cache.get(value);
44
+ });
45
+
46
+ return createElement(Fragment, null, items);
47
+ }
48
+
49
+ export function useLiveSignal<T>(
50
+ value: Signal<T> | ReadonlySignal<T>
51
+ ): Signal<Signal<T> | ReadonlySignal<T>> {
52
+ const s = useSignal(value);
53
+ if (s.peek() !== value) s.value = value;
54
+ return s;
55
+ }
56
+
57
+ export function useSignalRef<T>(value: T): Signal<T> & { current: T } {
58
+ const ref = useSignal(value) as Signal<T> & { current: T };
59
+ if (!("current" in ref))
60
+ Object.defineProperty(ref, "current", refSignalProto);
61
+ return ref;
62
+ }
63
+ const refSignalProto = {
64
+ configurable: true,
65
+ get(this: Signal) {
66
+ return this.value;
67
+ },
68
+ set(this: Signal, v: any) {
69
+ this.value = v;
70
+ },
71
+ };