value-enhancer
Advanced tools
Comparing version 4.1.2 to 4.2.0
@@ -1,2 +0,2 @@ | ||
import { R as ReadonlyVal } from './typings-dd45eb69.js'; | ||
import { R as ReadonlyVal } from './typings-7269679d.js'; | ||
@@ -3,0 +3,0 @@ /** |
@@ -1,3 +0,3 @@ | ||
import { R as ReadonlyVal, V as ValSetValue, a as ValConfig, b as ValInputsValueTuple, F as FlattenVal, c as ValDisposer, d as Val, e as ValSubscriber } from './typings-dd45eb69.js'; | ||
export { f as ValEqual } from './typings-dd45eb69.js'; | ||
import { R as ReadonlyVal, V as ValSetValue, a as ValConfig, b as ValInputsValueTuple, U as UnwrapVal, c as ValDisposer, d as Val, e as ValSubscriber } from './typings-7269679d.js'; | ||
export { F as FlattenVal, f as ValEqual } from './typings-7269679d.js'; | ||
@@ -49,3 +49,3 @@ /** | ||
* public $: Foo$; | ||
* private setVals: { [K in keyof Foo$]: ValSetValue<FlattenVal<Foo$[K]>> }; | ||
* private setVals: { [K in keyof Foo$]: ValSetValue<UnwrapVal<Foo$[K]>> }; | ||
* | ||
@@ -130,3 +130,3 @@ * public constructor() { | ||
*/ | ||
declare function flatten<TValOrValue = any>(val: ReadonlyVal<TValOrValue>): ReadonlyVal<FlattenVal<TValOrValue>>; | ||
declare function flatten<TValOrValue = any>(val: ReadonlyVal<TValOrValue>): ReadonlyVal<UnwrapVal<TValOrValue>>; | ||
/** | ||
@@ -150,3 +150,3 @@ * Flatten an inner val extracted from a source val to a val of the inner val value. | ||
*/ | ||
declare function flatten<TSrcValue = any, TValOrValue = any>(val: ReadonlyVal<TSrcValue>, get: (value: TSrcValue) => TValOrValue, config?: ValConfig<FlattenVal<TValOrValue>>): ReadonlyVal<FlattenVal<TValOrValue>>; | ||
declare function flatten<TSrcValue = any, TValOrValue = any>(val: ReadonlyVal<TSrcValue>, get: (value: TSrcValue) => TValOrValue, config?: ValConfig<UnwrapVal<TValOrValue>>): ReadonlyVal<UnwrapVal<TValOrValue>>; | ||
@@ -164,3 +164,3 @@ /** | ||
*/ | ||
declare const flattenFrom: <TValOrValue = any>(getValue: () => TValOrValue, listen: (notify: () => void) => ValDisposer | void | undefined, config?: ValConfig<FlattenVal<TValOrValue>> | undefined) => ReadonlyVal<FlattenVal<TValOrValue>>; | ||
declare const flattenFrom: <TValOrValue = any>(getValue: () => TValOrValue, listen: (notify: () => void) => ValDisposer | void | undefined, config?: ValConfig<UnwrapVal<TValOrValue>> | undefined) => ReadonlyVal<UnwrapVal<TValOrValue>>; | ||
@@ -241,2 +241,2 @@ /** | ||
export { CombineValTransform, DerivedValTransform, FlattenVal, ReadonlyVal, Val, ValConfig, ValDisposer, ValSetValue, ValSubscriber, combine, derive, flatten, flattenFrom, from, groupVals, identity, isVal, reaction, readonlyVal, setValue, subscribe, unsubscribe, val }; | ||
export { CombineValTransform, DerivedValTransform, ReadonlyVal, UnwrapVal, Val, ValConfig, ValDisposer, ValSetValue, ValSubscriber, combine, derive, flatten, flattenFrom, from, groupVals, identity, isVal, reaction, readonlyVal, setValue, subscribe, unsubscribe, val }; |
@@ -383,5 +383,7 @@ 'use strict'; | ||
var ValImpl = class extends ReadonlyValImpl { | ||
#config; | ||
constructor(currentValue, config) { | ||
const get = () => currentValue; | ||
super(get, config); | ||
this.#config = config; | ||
this.set = (value) => { | ||
@@ -402,3 +404,33 @@ if (!this.$equal?.(value, currentValue)) { | ||
} | ||
ref() { | ||
return new RefValImpl(this, this.#config); | ||
} | ||
}; | ||
var RefValImpl = class extends ReadonlyValImpl { | ||
#config; | ||
#source$; | ||
constructor(source$, config) { | ||
super( | ||
source$.get, | ||
config, | ||
() => source$.$valCompute(() => { | ||
this._subs.dirty = true; | ||
this._subs.notify(); | ||
}) | ||
); | ||
this.#source$ = source$; | ||
this.#config = config; | ||
this.set = source$.set; | ||
} | ||
set; | ||
get value() { | ||
return this.get(); | ||
} | ||
set value(value) { | ||
this.set(value); | ||
} | ||
ref() { | ||
return new RefValImpl(this.#source$, this.#config); | ||
} | ||
}; | ||
function val(value, config) { | ||
@@ -405,0 +437,0 @@ return new ValImpl(value, config); |
{ | ||
"name": "value-enhancer", | ||
"version": "4.1.2", | ||
"version": "4.2.0", | ||
"private": false, | ||
@@ -5,0 +5,0 @@ "description": "A tiny library to enhance value with reactive wrapper.", |
196
README.md
@@ -377,10 +377,131 @@ # [value-enhancer](https://github.com/crimx/value-enhancer) | ||
## Use in Class | ||
## Reactive Collections | ||
With `groupVals` you can easily create a group of ReadonlyVals and hide the setters. | ||
The Reactive Collections are a group of classes that expand on the built-in JavaScript collections, allowing changes to the collections to be observed. See [docs](https://value-enhancer.js.org/modules/collections.html) for API details. | ||
```ts | ||
import { derive } from "value-enhancer"; | ||
import { ReactiveList } from "value-enhancer/collections"; | ||
const list = new ReactiveList(["a", "b", "c"]); | ||
const item$ = derive(list.$, list => list[2]); // watch the item at index 2 | ||
console.log(item$.value); // "c" | ||
list.set(2, "d"); | ||
console.log(item$.value); // "d" | ||
``` | ||
```ts | ||
import { val, flatten } from "value-enhancer"; | ||
import { ReactiveMap } from "value-enhancer/collections"; | ||
const map = new ReactiveMap(); | ||
const v = val("someValue"); | ||
const item$ = flatten(map.$, map => map.get("someKey")); // watch the item at "someKey" | ||
console.log(item$.value); // undefined | ||
map.set("someKey", v); // set a val, the value inside the val is subscribed and flatten to `item$` | ||
console.log(item$.value); // "someValue" | ||
v.set("someValue2"); // you can also set a non-val value, which is passed to `item$`` directly | ||
console.log(item$.value); // "someValue2" | ||
``` | ||
## Patterns | ||
### Use in Class with different types. | ||
With this pattern, Writable `Val` properties are exposed as `$` and `ReadonlyVal` properties are exposed as `$$`. | ||
Note that they are all Writable `Val` under the hood. The difference is just the type. | ||
```ts | ||
import { val, type ReadonlyVal, type Val } from "value-enhancer"; | ||
interface MyClassVals { | ||
a: number; | ||
b: string; | ||
} | ||
export type MyClass$ = { | ||
[K in keyof MyClassVals]: ReadonlyVal<MyClassVals[K]>; | ||
}; | ||
export type MyClass$$ = { | ||
[K in keyof MyClassVals]: Val<MyClassVals[K]>; | ||
}; | ||
export class MyClass { | ||
public readonly $: MyClass$; | ||
public readonly $$: MyClassSet$; | ||
public constructor() { | ||
this.$ = this.$$ = { | ||
a: val(1), | ||
b: val("2"), | ||
}; | ||
} | ||
} | ||
const myClass = new MyClass(); | ||
console.log(myClass.$.a.value); | ||
myClass.$$.a.set(3); | ||
``` | ||
### Use in Class with ReadonlyVal and setter | ||
If you want to ensure maximum safety and prevent others from modifying the value accidentally, you can use a real `ReadonlyVal`. | ||
```ts | ||
import { | ||
readonlyVal, | ||
type ReadonlyVal, | ||
type ValSetValue, | ||
} from "value-enhancer"; | ||
interface MyClassVals { | ||
a: number; | ||
b: string; | ||
} | ||
export type MyClass$ = { | ||
[K in keyof MyClassVals]: ReadonlyVal<MyClassVals[K]>; | ||
}; | ||
export type MyClassSet$ = { | ||
[K in keyof MyClassVals]: ValSetValue<MyClassVals[K]>; | ||
}; | ||
export class MyClass { | ||
public readonly $: MyClass$; | ||
public readonly set$: MyClass$$; | ||
public constructor() { | ||
const [a$, setA] = readonlyVal(1); | ||
const [b$, setB] = readonlyVal("2"); | ||
this.$ = { a: a$, b: b$ }; | ||
this.set$ = { a: setA, b: setB }; | ||
} | ||
} | ||
const myClass = new MyClass(); | ||
console.log(myClass.$.a.value); | ||
myClass.set$.a(3); | ||
``` | ||
### Use in Class with GroupVals | ||
Writing all these ReadonlyVals and setters could be cumbersome. With `groupVals` you can easily create a group of ReadonlyVals and setters. | ||
```ts | ||
import { | ||
type ReadonlyVal, | ||
type ValSetValue, | ||
type FlattenVal, | ||
@@ -399,3 +520,3 @@ readonlyVal, | ||
public readonly $: Foo$; | ||
private setVals: { [K in keyof Foo$]: ValSetValue<FlattenVal<Foo$[K]>> }; | ||
private setVals: { [K in keyof Foo$]: ValSetValue<UnwrapVal<Foo$[K]>> }; | ||
@@ -425,39 +546,70 @@ public constructor() { | ||
## Reactive Collections | ||
### Sharing vals to other Classes | ||
The Reactive Collections are a group of classes that expand on the built-in JavaScript collections, allowing changes to the collections to be observed. See [docs](https://value-enhancer.js.org/modules/collections.html) for API details. | ||
Sharing vals to other classes directly should be careful. Other classes may `dispose` the vals and cause unexpected behavior. | ||
To share ReadonlyVals to other classes, use a derived val. | ||
```ts | ||
import { derive } from "value-enhancer"; | ||
import { ReactiveList } from "value-enhancer/collections"; | ||
import { val, derive, type ReadonlyVal } from "value-enhancer"; | ||
const list = new ReactiveList(["a", "b", "c"]); | ||
interface AProps { | ||
a$: ReadonlyVal<number>; | ||
} | ||
const item$ = derive(list.$, list => list[2]); // watch the item at index 2 | ||
class A { | ||
a$: ReadonlyVal<number>; | ||
console.log(item$.value); // "c" | ||
constructor(props: AProps) { | ||
this.a$ = props.a$; | ||
} | ||
list.set(2, "d"); | ||
dispose() { | ||
this.a$.dispose(); | ||
} | ||
} | ||
console.log(item$.value); // "d" | ||
const a$ = val(1); | ||
const a = new A({ a$: derive(a$) }); | ||
a.dispose(); // will not affect a$ | ||
``` | ||
To share writable vals to other classes, use a ref val. | ||
`Val.ref` creates a new Val referencing the value of the current Val as source. | ||
All ref Vals share the same value from the source Val. | ||
The act of setting a value on the ref Val is essentially setting the value on the source Val. | ||
The ref Vals can be safely disposed without affecting the source Val and other ref Vals. | ||
```ts | ||
import { val, flatten } from "value-enhancer"; | ||
import { ReactiveMap } from "value-enhancer/collections"; | ||
import { val, type Val } from "value-enhancer"; | ||
const map = new ReactiveMap(); | ||
const v = val("someValue"); | ||
interface AProps { | ||
a$: Val<number>; | ||
} | ||
const item$ = flatten(map.$, map => map.get("someKey")); // watch the item at "someKey" | ||
class A { | ||
a$: Val<number>; | ||
console.log(item$.value); // undefined | ||
constructor(props: AProps) { | ||
this.a$ = props.a$; | ||
} | ||
map.set("someKey", v); // set a val, the value inside the val is subscribed and flatten to `item$` | ||
dispose() { | ||
this.a$.dispose(); | ||
} | ||
} | ||
console.log(item$.value); // "someValue" | ||
const a$ = val(1); | ||
const a1 = new A({ a$: a$.ref() }); | ||
const a2 = new A({ a$: a$.ref() }); | ||
v.set("someValue2"); // you can also set a non-val value, which is passed to `item$`` directly | ||
a2.a$.set(2); | ||
console.log(item$.value); // "someValue2" | ||
console.log(a$.value); // 2 | ||
console.log(a1.a$.value); // 2 | ||
console.log(a2.a$.value); // 2 | ||
a1.dispose(); // will not affect a$ and a2.a$ | ||
``` |
@@ -1,7 +0,2 @@ | ||
import type { | ||
FlattenVal, | ||
ReadonlyVal, | ||
ValConfig, | ||
ValDisposer, | ||
} from "./typings"; | ||
import type { ReadonlyVal, UnwrapVal, ValConfig, ValDisposer } from "./typings"; | ||
@@ -13,3 +8,3 @@ import { ReadonlyValImpl } from "./readonly-val"; | ||
TValOrValue = any, | ||
TValue = FlattenVal<TValOrValue> | ||
TValue = UnwrapVal<TValOrValue> | ||
> extends ReadonlyValImpl<TValue> { | ||
@@ -112,4 +107,4 @@ public constructor( | ||
listen: (notify: () => void) => ValDisposer | void | undefined, | ||
config?: ValConfig<FlattenVal<TValOrValue>> | ||
): ReadonlyVal<FlattenVal<TValOrValue>> => | ||
config?: ValConfig<UnwrapVal<TValOrValue>> | ||
): ReadonlyVal<UnwrapVal<TValOrValue>> => | ||
new FlattenFromImpl(getValue, listen, config); |
import type { ReadonlyValImpl } from "./readonly-val"; | ||
import type { FlattenVal, ReadonlyVal, ValConfig } from "./typings"; | ||
import type { ReadonlyVal, UnwrapVal, ValConfig } from "./typings"; | ||
@@ -26,3 +26,3 @@ import { flattenFrom } from "./flatten-from"; | ||
val: ReadonlyVal<TValOrValue> | ||
): ReadonlyVal<FlattenVal<TValOrValue>>; | ||
): ReadonlyVal<UnwrapVal<TValOrValue>>; | ||
/** | ||
@@ -49,4 +49,4 @@ * Flatten an inner val extracted from a source val to a val of the inner val value. | ||
get: (value: TSrcValue) => TValOrValue, | ||
config?: ValConfig<FlattenVal<TValOrValue>> | ||
): ReadonlyVal<FlattenVal<TValOrValue>>; | ||
config?: ValConfig<UnwrapVal<TValOrValue>> | ||
): ReadonlyVal<UnwrapVal<TValOrValue>>; | ||
export function flatten< | ||
@@ -59,4 +59,4 @@ TSrcValue = any, | ||
get: (value: TSrcValue) => TValOrValue = identity as any, | ||
config?: ValConfig<FlattenVal<TValOrValue>> | ||
): ReadonlyVal<FlattenVal<TValOrValue>> { | ||
config?: ValConfig<UnwrapVal<TValOrValue>> | ||
): ReadonlyVal<UnwrapVal<TValOrValue>> { | ||
return flattenFrom( | ||
@@ -63,0 +63,0 @@ () => get(val.value), |
export type { | ||
FlattenVal, | ||
ReadonlyVal, | ||
UnwrapVal, | ||
Val, | ||
@@ -5,0 +6,0 @@ ValConfig, |
@@ -196,3 +196,3 @@ import type { ValOnStart } from "./subscribers"; | ||
* public $: Foo$; | ||
* private setVals: { [K in keyof Foo$]: ValSetValue<FlattenVal<Foo$[K]>> }; | ||
* private setVals: { [K in keyof Foo$]: ValSetValue<UnwrapVal<Foo$[K]>> }; | ||
* | ||
@@ -199,0 +199,0 @@ * public constructor() { |
@@ -43,2 +43,11 @@ /** | ||
set(this: void, value: TValue): void; | ||
/** | ||
* Create a new Val referencing the value of the current Val as source. | ||
* All ref Vals share the same value from the source Val. | ||
* The act of setting a value on the ref Val is essentially setting the value on the source Val. | ||
* | ||
* With this pattern you can pass a ref Val as a writable Val to downstream. | ||
* The ref Vals can be safely disposed without affecting the source Val and other ref Vals. | ||
*/ | ||
ref(): Val<TValue>; | ||
} | ||
@@ -73,4 +82,6 @@ | ||
export type FlattenVal<T> = T extends ReadonlyVal<infer TValue> ? TValue : T; | ||
export type UnwrapVal<T> = T extends ReadonlyVal<infer TValue> ? TValue : T; | ||
export type FlattenVal<T> = ReadonlyVal<UnwrapVal<UnwrapVal<T>>>; | ||
/** @internal */ | ||
@@ -77,0 +88,0 @@ export type ValInputsValueTuple<TValInputs extends readonly ReadonlyVal[]> = |
@@ -6,2 +6,4 @@ import type { Val, ValConfig } from "./typings"; | ||
class ValImpl<TValue = any> extends ReadonlyValImpl<TValue> { | ||
#config?: ValConfig<TValue>; | ||
public constructor(currentValue: TValue, config?: ValConfig<TValue>) { | ||
@@ -12,2 +14,3 @@ const get = () => currentValue; | ||
this.#config = config; | ||
this.set = (value: TValue) => { | ||
@@ -31,4 +34,40 @@ if (!this.$equal?.(value, currentValue)) { | ||
} | ||
public ref(): Val<TValue> { | ||
return new RefValImpl(this, this.#config); | ||
} | ||
} | ||
class RefValImpl<TValue = any> extends ReadonlyValImpl<TValue> { | ||
#config?: ValConfig<TValue>; | ||
#source$: ValImpl<TValue>; | ||
public constructor(source$: ValImpl<TValue>, config?: ValConfig<TValue>) { | ||
super(source$.get, config, () => | ||
source$.$valCompute(() => { | ||
this._subs.dirty = true; | ||
this._subs.notify(); | ||
}) | ||
); | ||
this.#source$ = source$; | ||
this.#config = config; | ||
this.set = source$.set; | ||
} | ||
public set: (this: void, value: TValue) => void; | ||
public override get value() { | ||
return this.get(); | ||
} | ||
public override set value(value: TValue) { | ||
this.set(value); | ||
} | ||
public ref(): Val<TValue> { | ||
return new RefValImpl(this.#source$, this.#config); | ||
} | ||
} | ||
/** | ||
@@ -35,0 +74,0 @@ * Creates a writable val. |
Sorry, the diff of this file is not supported yet
147947
4369
613