gnim 1.3.6 → 1.4.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.
Binary file
package/dist/jsx/state.ts CHANGED
@@ -96,28 +96,71 @@ export function createState<T>(init: T): State<T> {
96
96
  const value: T = typeof newValue === "function" ? newValue(currentValue) : newValue
97
97
  if (currentValue !== value) {
98
98
  currentValue = value
99
- subscribers.forEach((cb) => cb())
99
+ // running callbacks might mutate subscribers
100
+ Array.from(subscribers).forEach((cb) => cb())
100
101
  }
101
102
  }
102
103
 
103
104
  return [new Accessor(() => currentValue, subscribe), set as Setter<T>]
104
105
  }
105
106
 
106
- /**
107
- * Create an `Accessor` which is computed from a list of `Accessor`s.
108
- *
109
- * ```ts Example
110
- * let a: Accessor<number>
111
- * let b: Accessor<string>
112
- * const c: Accessor<[number, string]> = createComputed([a, b])
113
- * const d: Accessor<string> = createComputed([a, b], (a: number, b: string) => `${a} ${b}`)
114
- * ```
115
- *
116
- * @param deps List of `Accessors`.
117
- * @param transform An optional transform function.
118
- * @returns The computed `Accessor`.
119
- */
120
- export function createComputed<
107
+ const empty = Symbol("empty computed value")
108
+
109
+ function createComputedProducer<T>(fn: (track: <V>(signal: Accessor<V>) => V) => T): Accessor<T> {
110
+ const subscribers = new Set<SubscribeCallback>()
111
+ let value: typeof empty | T
112
+ let prevDeps = new Map<Accessor, DisposeFunction>()
113
+
114
+ const effect = () => {
115
+ const deps = new Set<Accessor>()
116
+ value = fn((v) => (deps.add(v), v.get()))
117
+
118
+ const newDeps = new Map<Accessor, DisposeFunction>()
119
+
120
+ for (const [dep, unsub] of prevDeps) {
121
+ if (!deps.has(dep)) {
122
+ unsub()
123
+ } else {
124
+ newDeps.set(dep, unsub)
125
+ }
126
+ }
127
+
128
+ for (const dep of deps) {
129
+ if (!newDeps.has(dep)) {
130
+ newDeps.set(dep, dep.subscribe(effect))
131
+ }
132
+ }
133
+
134
+ prevDeps = newDeps
135
+ Array.from(subscribers).forEach((cb) => cb())
136
+ }
137
+
138
+ const subscribe: SubscribeFunction = (callback) => {
139
+ if (subscribers.size === 0) {
140
+ effect()
141
+ }
142
+
143
+ subscribers.add(callback)
144
+
145
+ return () => {
146
+ subscribers.delete(callback)
147
+ if (subscribers.size === 0) {
148
+ value = empty
149
+ for (const [, unsub] of prevDeps) {
150
+ unsub()
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ const get = (): T => {
157
+ return value === empty ? fn((v) => v.get()) : value
158
+ }
159
+
160
+ return new Accessor(get, subscribe)
161
+ }
162
+
163
+ function createComputedArgs<
121
164
  const Deps extends Array<Accessor<any>>,
122
165
  Args extends { [K in keyof Deps]: Accessed<Deps[K]> },
123
166
  V = Args,
@@ -133,7 +176,7 @@ export function createComputed<
133
176
  const value = dep.get()
134
177
  if (cache[i] !== value) {
135
178
  cache[i] = dep.get()
136
- subscribers.forEach((cb) => cb())
179
+ Array.from(subscribers).forEach((cb) => cb())
137
180
  }
138
181
  }),
139
182
  )
@@ -166,6 +209,56 @@ export function createComputed<
166
209
  return new Accessor(get, subscribe)
167
210
  }
168
211
 
212
+ /**
213
+ * Create an `Accessor` from a producer function that tracks its dependencies.
214
+ *
215
+ * ```ts Example
216
+ * let a: Accessor<number>
217
+ * let b: Accessor<number>
218
+ * const c: Accessor<number> = createComputed((get) => get(a) + get(b))
219
+ * ```
220
+ *
221
+ * @experimental
222
+ * @param producer The producer function which let's you track dependencies
223
+ * @returns The computed `Accessor`.
224
+ */
225
+ export function createComputed<T>(
226
+ producer: (track: <V>(signal: Accessor<V>) => V) => T,
227
+ ): Accessor<T>
228
+
229
+ /**
230
+ * Create an `Accessor` which is computed from a list of given `Accessor`s.
231
+ *
232
+ * ```ts Example
233
+ * let a: Accessor<number>
234
+ * let b: Accessor<string>
235
+ * const c: Accessor<[number, string]> = createComputed([a, b])
236
+ * const d: Accessor<string> = createComputed([a, b], (a: number, b: string) => `${a} ${b}`)
237
+ * ```
238
+ *
239
+ * @param deps List of `Accessors`.
240
+ * @param transform An optional transform function.
241
+ * @returns The computed `Accessor`.
242
+ */
243
+ export function createComputed<
244
+ const Deps extends Array<Accessor<any>>,
245
+ Args extends { [K in keyof Deps]: Accessed<Deps[K]> },
246
+ T = Args,
247
+ >(deps: Deps, transform?: (...args: Args) => T): Accessor<T>
248
+
249
+ export function createComputed(
250
+ ...args:
251
+ | [producer: (track: <V>(signal: Accessor<V>) => V) => unknown]
252
+ | [deps: Array<Accessor>, transform?: (...args: unknown[]) => unknown]
253
+ ) {
254
+ const [depsOrProducer, transform] = args
255
+ if (typeof depsOrProducer === "function") {
256
+ return createComputedProducer(depsOrProducer)
257
+ } else {
258
+ return createComputedArgs(depsOrProducer, transform)
259
+ }
260
+ }
261
+
169
262
  /**
170
263
  * Create an `Accessor` on a `GObject.Object`'s `property`.
171
264
  *
@@ -192,6 +285,7 @@ export function createBinding<T extends GObject.Object, P extends keyof T>(
192
285
  * Create an `Accessor` on a `Gio.Settings`'s `key`.
193
286
  * Values are recursively unpacked.
194
287
  *
288
+ * @deprecated prefer using {@link createSettings}.
195
289
  * @param object The `Gio.Settings` to create the `Accessor` on.
196
290
  * @param key The settings key
197
291
  */
@@ -297,7 +391,7 @@ export function createConnection<
297
391
  const newValue = callback(...args, value)
298
392
  if (value !== newValue) {
299
393
  value = newValue
300
- subscribers.forEach((cb) => cb())
394
+ Array.from(subscribers).forEach((cb) => cb())
301
395
  }
302
396
  },
303
397
  )
@@ -351,7 +445,7 @@ export function createExternal<T>(
351
445
  const newValue: T = typeof v === "function" ? v(currentValue) : v
352
446
  if (newValue !== currentValue) {
353
447
  currentValue = newValue
354
- subscribers.forEach((cb) => cb())
448
+ Array.from(subscribers).forEach((cb) => cb())
355
449
  }
356
450
  })
357
451
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnim",
3
- "version": "1.3.6",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "author": "Aylur",
6
6
  "license": "MIT",