value-enhancer
Advanced tools
Comparing version 5.1.2 to 5.1.3
@@ -1,2 +0,2 @@ | ||
import { R as ReadonlyVal } from './typings-2bae36c2.js'; | ||
import { R as ReadonlyVal } from './typings-e10eb4db.js'; | ||
@@ -3,0 +3,0 @@ /** |
'use strict'; | ||
var chunkLU5CXO64_js = require('./chunk-LU5CXO64.js'); | ||
var chunkJNRMXROO_js = require('./chunk-JNRMXROO.js'); | ||
@@ -8,3 +8,3 @@ // src/collections/list.ts | ||
constructor(items) { | ||
const [$, set$] = chunkLU5CXO64_js.readonlyVal( | ||
const [$, set$] = chunkJNRMXROO_js.readonlyVal( | ||
items ? [...items] : [], | ||
@@ -92,3 +92,3 @@ { equal: false } | ||
set(index, item) { | ||
if (index >= 0 && !chunkLU5CXO64_js.strictEqual(this.array[index], item)) { | ||
if (index >= 0 && !chunkJNRMXROO_js.strictEqual(this.array[index], item)) { | ||
this.array[index] = item; | ||
@@ -102,3 +102,3 @@ this.#notify(); | ||
for (const [index, item] of entries) { | ||
if (index >= 0 && !chunkLU5CXO64_js.strictEqual(this.array[index], item)) { | ||
if (index >= 0 && !chunkJNRMXROO_js.strictEqual(this.array[index], item)) { | ||
isDirty = true; | ||
@@ -183,3 +183,3 @@ this.array[index] = item; | ||
} | ||
const [$, set$] = chunkLU5CXO64_js.readonlyVal(this, { equal: false }); | ||
const [$, set$] = chunkJNRMXROO_js.readonlyVal(this, { equal: false }); | ||
this.$ = $; | ||
@@ -242,3 +242,3 @@ this.#notify = () => set$(this); | ||
const oldValue = this.get(key); | ||
if (chunkLU5CXO64_js.strictEqual(oldValue, value)) { | ||
if (chunkJNRMXROO_js.strictEqual(oldValue, value)) { | ||
return false; | ||
@@ -316,3 +316,3 @@ } | ||
} | ||
const [$, set$] = chunkLU5CXO64_js.readonlyVal(this, { equal: false }); | ||
const [$, set$] = chunkJNRMXROO_js.readonlyVal(this, { equal: false }); | ||
this.$ = $; | ||
@@ -319,0 +319,0 @@ this.#notify = () => set$(this); |
@@ -1,35 +0,4 @@ | ||
import { R as ReadonlyVal, V as ValInputsValueTuple, a as ValConfig, U as UnwrapVal, b as ValDisposer, N as NoInfer, c as ValSetValue, d as Val, e as ValSubscriber } from './typings-2bae36c2.js'; | ||
export { F as FlattenVal, f as ValEqual, g as ValVersion } from './typings-2bae36c2.js'; | ||
import { R as ReadonlyVal, V as ValInputsValueTuple, a as ValConfig, U as UnwrapVal, b as ValDisposer, c as ValSubscriber, d as Val, N as NoInfer, e as ValSetValue } from './typings-e10eb4db.js'; | ||
export { F as FlattenVal, f as ValEqual, g as ValVersion } from './typings-e10eb4db.js'; | ||
declare const nextTick: () => Promise<void>; | ||
/** Returns the value passed in. */ | ||
declare const identity: <TValue>(value: TValue) => TValue; | ||
/** | ||
* `Object.is` | ||
*/ | ||
declare const strictEqual: (value1: any, value2: any) => boolean; | ||
/** | ||
* Shallow compare two arrays. | ||
* @param arrA - any value | ||
* @param arrB - any value | ||
* @returns `false` if any of: | ||
* 1. one of arrA or arrB is an array and the other is not | ||
* 2. arrA and arrB have different lengths | ||
* 3. arrA and arrB have different values at any index | ||
*/ | ||
declare const arrayShallowEqual: (arrA: any, arrB: any) => boolean; | ||
/** | ||
* Checks if `val` is `ReadonlyVal` or `Val`. | ||
* | ||
* @returns `true` if `val` is `ReadonlyVal` or `Val`. | ||
*/ | ||
declare function isVal<T extends ReadonlyVal>(val: T): val is T extends ReadonlyVal ? T : never; | ||
/** | ||
* Checks if `val` is `ReadonlyVal` or `Val`. | ||
* | ||
* @returns `true` if `val` is `ReadonlyVal` or `Val`. | ||
*/ | ||
declare function isVal(val: unknown): val is ReadonlyVal; | ||
type CombineValTransform<TCombinedValue = any, TValues extends readonly any[] = any[]> = (newValues: TValues) => TCombinedValue; | ||
@@ -116,2 +85,13 @@ /** | ||
*/ | ||
/** | ||
* Creates a readonly val from a getter function and a listener function. | ||
* If the value is a val, it will be auto-flattened. | ||
* | ||
* @param getValue A function that returns the current value. | ||
* If the value is a val, it will be auto-flattened. | ||
* @param listen A function that takes a notify function and returns a disposer. | ||
* The notify function should be called when the value changes. | ||
* @param config custom config for the val. | ||
* @returns A readonly val with value of inner val. | ||
*/ | ||
declare const flattenFrom: <TValOrValue = any>(getValue: () => TValOrValue, listen: (notify: () => void) => ValDisposer | void | undefined, config?: ValConfig<UnwrapVal<TValOrValue>> | undefined) => ReadonlyVal<UnwrapVal<TValOrValue>>; | ||
@@ -151,3 +131,71 @@ | ||
declare const nextTick: () => Promise<void>; | ||
/** | ||
* @deprecated | ||
* Set the value of a val. | ||
* It works for both `Val` and `ReadonlyVal` type (if the `ReadonlyVal` is actually a `Val`). | ||
* Do nothing if the val is really `ReadonlyVal`. | ||
*/ | ||
declare const setValue: <TValue>(val: ReadonlyVal<TValue>, value: TValue) => void; | ||
/** | ||
* Subscribe to value changes with immediate emission. | ||
* @param val | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
declare const subscribe: <TValue>(val: ReadonlyVal<TValue>, subscriber: ValSubscriber<TValue>, eager?: boolean) => ValDisposer; | ||
/** | ||
* Subscribe to value changes without immediate emission. | ||
* @param val | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
declare const reaction: <TValue>(val: ReadonlyVal<TValue>, subscriber: ValSubscriber<TValue>, eager?: boolean) => ValDisposer; | ||
/** | ||
* Remove the given subscriber. | ||
* Remove all if no subscriber provided. | ||
* @param val | ||
* @param subscriber | ||
*/ | ||
declare const unsubscribe: <TValue>(val: ReadonlyVal<TValue>, subscriber?: ((...args: any[]) => any) | undefined) => void; | ||
/** Returns the value passed in. */ | ||
declare const identity: <TValue>(value: TValue) => TValue; | ||
/** | ||
* `Object.is` | ||
*/ | ||
declare const strictEqual: (value1: any, value2: any) => boolean; | ||
/** | ||
* Shallow compare two arrays. | ||
* @param arrA - any value | ||
* @param arrB - any value | ||
* @returns `false` if any of: | ||
* 1. one of arrA or arrB is an array and the other is not | ||
* 2. arrA and arrB have different lengths | ||
* 3. arrA and arrB have different values at any index | ||
*/ | ||
declare const arrayShallowEqual: (arrA: any, arrB: any) => boolean; | ||
/** | ||
* Attach a new setter to a val. | ||
* @param val$ a readonly Val | ||
* @param set a function that sets the value of val$ | ||
* @returns The same val$ with the new setter. | ||
*/ | ||
declare const attachSetter: <TValue>(val$: ReadonlyVal<TValue>, set: (this: void, value: TValue) => void) => Val<TValue>; | ||
/** | ||
* Checks if `val` is `ReadonlyVal` or `Val`. | ||
* | ||
* @returns `true` if `val` is `ReadonlyVal` or `Val`. | ||
*/ | ||
declare function isVal<T extends ReadonlyVal>(val: T): val is T extends ReadonlyVal ? T : never; | ||
/** | ||
* Checks if `val` is `ReadonlyVal` or `Val`. | ||
* | ||
* @returns `true` if `val` is `ReadonlyVal` or `Val`. | ||
*/ | ||
declare function isVal(val: unknown): val is ReadonlyVal; | ||
/** | ||
* Creates a readonly val with the given value. | ||
@@ -164,2 +212,4 @@ * | ||
* | ||
* @param value Value for the val | ||
* @param config Optional custom config for the val. | ||
* @returns A tuple with the readonly val and a function to set the value. | ||
@@ -172,3 +222,3 @@ */ | ||
* @param value Value for the val | ||
* @param config Custom config for the val. | ||
* @param config Optional custom config for the val. | ||
* @returns A tuple with the readonly val and a function to set the value. | ||
@@ -178,2 +228,36 @@ */ | ||
/** | ||
* Creates a readonly val with the given value. | ||
* | ||
* @param value Optional value for the val | ||
* @param config Optional custom config for the val. | ||
* @returns A tuple with the readonly val and a function to set the value. | ||
*/ | ||
declare function readonlyVal<TValue = any>(value?: TValue, config?: ValConfig<TValue>): [ | ||
ReadonlyVal<NoInfer<TValue | undefined>>, | ||
ValSetValue<NoInfer<TValue | undefined>> | ||
]; | ||
/** | ||
* Creates a writable val. | ||
* @returns A val with undefined value. | ||
*/ | ||
declare function val<TValue = any>(): Val<NoInfer<TValue> | undefined>; | ||
/** | ||
* Creates a writable val. | ||
* @param value Initial value. | ||
* @param config Optional custom config. | ||
*/ | ||
declare function val(value: [], config?: ValConfig<any[]>): Val<any[]>; | ||
/** | ||
* Creates a writable val. | ||
* @param value Initial value. | ||
* @param config Optional custom config. | ||
*/ | ||
declare function val<TValue = any>(value: TValue, config?: ValConfig<TValue>): Val<NoInfer<TValue>>; | ||
/** | ||
* Creates a writable val. | ||
* @param value Initial value. | ||
* @param config Optional custom config. | ||
*/ | ||
declare function val<TValue = any>(value?: TValue, config?: ValConfig<TValue | undefined>): Val<NoInfer<TValue>>; | ||
/** | ||
* Takes an object of key-value pairs containing `ReadonlyVal` instances and their corresponding `ValSetValue` functions, | ||
@@ -222,50 +306,2 @@ * and returns a tuple containing an array of the `ReadonlyVal` instances and a function to set their values. | ||
/** | ||
* Creates a writable val. | ||
* @returns A val with undefined value. | ||
*/ | ||
declare function val<TValue = any>(): Val<NoInfer<TValue> | undefined>; | ||
/** | ||
* Creates a writable val. | ||
* @param value Initial value. | ||
* @param config Custom config. | ||
*/ | ||
declare function val(value: [], config?: ValConfig<any[]>): Val<any[]>; | ||
/** | ||
* Creates a writable val. | ||
* @param value Initial value. | ||
* @param config Custom config. | ||
*/ | ||
declare function val<TValue = any>(value: TValue, config?: ValConfig<TValue>): Val<NoInfer<TValue>>; | ||
/** | ||
* Set the value of a val. | ||
* It works for both `Val` and `ReadonlyVal` type (if the `ReadonlyVal` is actually a `Val`). | ||
* Does nothing if the val is really `ReadonlyVal`. | ||
*/ | ||
declare const setValue: <TValue>(val: ReadonlyVal<TValue>, value: TValue) => void; | ||
/** | ||
* Subscribe to value changes with immediate emission. | ||
* @param val | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
declare const subscribe: <TValue>(val: ReadonlyVal<TValue>, subscriber: ValSubscriber<TValue>, eager?: boolean) => ValDisposer; | ||
/** | ||
* Subscribe to value changes without immediate emission. | ||
* @param val | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
declare const reaction: <TValue>(val: ReadonlyVal<TValue>, subscriber: ValSubscriber<TValue>, eager?: boolean) => ValDisposer; | ||
/** | ||
* Remove the given subscriber. | ||
* Remove all if no subscriber provided. | ||
* @param val | ||
* @param subscriber | ||
*/ | ||
declare const unsubscribe: <TValue>(val: ReadonlyVal<TValue>, subscriber?: ((...args: any[]) => any) | undefined) => void; | ||
export { CombineValTransform, DerivedValTransform, ReadonlyVal, UnwrapVal, Val, ValConfig, ValDisposer, ValSetValue, ValSubscriber, arrayShallowEqual, combine, derive, flatten, flattenFrom, from, groupVals, identity, isVal, nextTick, reaction, readonlyVal, setValue, strictEqual, subscribe, unsubscribe, val }; | ||
export { CombineValTransform, DerivedValTransform, ReadonlyVal, UnwrapVal, Val, ValConfig, ValDisposer, ValSetValue, ValSubscriber, arrayShallowEqual, attachSetter, combine, derive, flatten, flattenFrom, from, groupVals, identity, isVal, nextTick, reaction, readonlyVal, setValue, strictEqual, subscribe, unsubscribe, val }; |
'use strict'; | ||
var chunkLU5CXO64_js = require('./chunk-LU5CXO64.js'); | ||
var chunkJNRMXROO_js = require('./chunk-JNRMXROO.js'); | ||
// src/from.ts | ||
var FromImpl = class extends chunkLU5CXO64_js.ReadonlyValImpl { | ||
constructor(getValue, listen, config) { | ||
let currentValue = chunkLU5CXO64_js.INIT_VALUE; | ||
let dirty = false; | ||
let notified = false; | ||
const get = () => { | ||
if (currentValue === chunkLU5CXO64_js.INIT_VALUE || subs.s <= 0) { | ||
currentValue = getValue(); | ||
subs.w(config, currentValue); | ||
} else if (dirty) { | ||
const value = getValue(); | ||
if (!this.$equal?.(value, currentValue)) { | ||
subs.d = true; | ||
subs.w(config, value, currentValue); | ||
currentValue = value; | ||
} | ||
} | ||
dirty = notified = false; | ||
return currentValue; | ||
}; | ||
const notify = () => { | ||
dirty = true; | ||
if (!notified) { | ||
notified = true; | ||
subs.n(); | ||
} | ||
}; | ||
const subs = new chunkLU5CXO64_js.Subscribers(get, (subs2) => { | ||
const disposer = listen(notify); | ||
currentValue = getValue(); | ||
subs2.w(config, currentValue); | ||
dirty = notified = false; | ||
return disposer; | ||
}); | ||
super(subs, config); | ||
} | ||
get $version() { | ||
this.get(); | ||
return super.$version; | ||
} | ||
}; | ||
var from = (getValue, listen, config) => new FromImpl(getValue, listen, config); | ||
// src/combine.ts | ||
function combine(valInputs, transform = chunkLU5CXO64_js.identity, config) { | ||
let cachedValue; | ||
let cachedSrcVersions; | ||
return from( | ||
() => { | ||
const versions = valInputs.map(chunkLU5CXO64_js.getValVersion); | ||
if (!cachedSrcVersions || !chunkLU5CXO64_js.arrayShallowEqual(versions, cachedSrcVersions)) { | ||
cachedSrcVersions = versions; | ||
cachedValue = transform(chunkLU5CXO64_js.getValues(valInputs)); | ||
} | ||
return cachedValue; | ||
}, | ||
(notify) => { | ||
const disposers = valInputs.map((val2) => val2.$valCompute(notify)); | ||
return () => disposers.forEach(chunkLU5CXO64_js.invoke); | ||
}, | ||
config | ||
); | ||
} | ||
// src/derive.ts | ||
function derive(val2, transform = chunkLU5CXO64_js.identity, config) { | ||
let cachedValue; | ||
let cachedSrcVersion = chunkLU5CXO64_js.INIT_VALUE; | ||
return from( | ||
() => { | ||
const version = val2.$version; | ||
if (!chunkLU5CXO64_js.strictEqual(version, cachedSrcVersion)) { | ||
cachedSrcVersion = version; | ||
cachedValue = transform(val2.value); | ||
} | ||
return cachedValue; | ||
}, | ||
(notify) => val2.$valCompute(notify), | ||
config | ||
); | ||
} | ||
// src/flatten-from.ts | ||
var FlattenFromImpl = class extends chunkLU5CXO64_js.ReadonlyValImpl { | ||
constructor(getValue, listen, config) { | ||
const initialEqual = config?.equal; | ||
let currentValue = chunkLU5CXO64_js.INIT_VALUE; | ||
let dirty = false; | ||
let notified = false; | ||
let innerMaybeVal; | ||
let innerVal; | ||
let innerDisposer; | ||
const computeValue = () => { | ||
if (subs.s <= 0) { | ||
updateInnerVal(); | ||
} | ||
return innerVal ? innerVal.value : innerMaybeVal; | ||
}; | ||
const get = () => { | ||
if (currentValue === chunkLU5CXO64_js.INIT_VALUE || subs.s <= 0) { | ||
currentValue = computeValue(); | ||
subs.w(config, currentValue); | ||
} else if (dirty) { | ||
const value = computeValue(); | ||
if (!this.$equal?.(value, currentValue)) { | ||
subs.d = true; | ||
subs.w(config, value, currentValue); | ||
currentValue = value; | ||
} | ||
} | ||
dirty = notified = false; | ||
return currentValue; | ||
}; | ||
const updateInnerVal = () => { | ||
const maybeVal = getValue(); | ||
if (!chunkLU5CXO64_js.strictEqual(maybeVal, innerMaybeVal)) { | ||
innerMaybeVal = maybeVal; | ||
innerVal = chunkLU5CXO64_js.isVal(maybeVal) ? maybeVal : null; | ||
innerDisposer?.(); | ||
innerDisposer = innerVal && innerVal.$valCompute(notify); | ||
this.$equal = initialEqual || (initialEqual === false ? void 0 : innerVal ? innerVal.$equal : chunkLU5CXO64_js.strictEqual); | ||
} | ||
}; | ||
const notify = () => { | ||
dirty = true; | ||
if (!notified) { | ||
notified = true; | ||
subs.n(); | ||
} | ||
}; | ||
const subs = new chunkLU5CXO64_js.Subscribers(get, (subs2) => { | ||
const outerDisposer = listen(() => { | ||
updateInnerVal(); | ||
notify(); | ||
}); | ||
updateInnerVal(); | ||
currentValue = innerVal ? innerVal.value : innerMaybeVal; | ||
subs2.w(config, currentValue); | ||
dirty = notified = false; | ||
return () => { | ||
innerDisposer?.(); | ||
outerDisposer?.(); | ||
}; | ||
}); | ||
super(subs, config); | ||
} | ||
get $version() { | ||
this.get(); | ||
return super.$version; | ||
} | ||
}; | ||
var flattenFrom = (getValue, listen, config) => new FlattenFromImpl(getValue, listen, config); | ||
// src/flatten.ts | ||
function flatten(val2, get = chunkLU5CXO64_js.identity, config) { | ||
return flattenFrom( | ||
() => get(val2.value), | ||
(notify) => val2.$valCompute(notify), | ||
config | ||
); | ||
} | ||
// src/val.ts | ||
var ValImpl = class extends chunkLU5CXO64_js.ReadonlyValImpl { | ||
#config; | ||
constructor(currentValue, config) { | ||
const get = () => currentValue; | ||
const subs = new chunkLU5CXO64_js.Subscribers(get); | ||
super(subs, config); | ||
this.#config = config; | ||
this.set = (value) => { | ||
if (!this.$equal?.(value, currentValue)) { | ||
subs.d = true; | ||
subs.w(config, value, currentValue); | ||
currentValue = value; | ||
subs.n(); | ||
} | ||
}; | ||
} | ||
set; | ||
get value() { | ||
return this.get(); | ||
} | ||
set value(value) { | ||
this.set(value); | ||
} | ||
ref(writable) { | ||
return writable ? new ValRefImpl(this, this.#config) : new chunkLU5CXO64_js.ReadonlyValRefImpl(this, this.#config); | ||
} | ||
}; | ||
var ValRefImpl = class extends chunkLU5CXO64_js.ReadonlyValRefImpl { | ||
#source$; | ||
#config; | ||
constructor(source$, config) { | ||
super(source$); | ||
this.#source$ = source$; | ||
this.#config = config; | ||
this.set = source$.set; | ||
} | ||
set; | ||
get value() { | ||
return this.get(); | ||
} | ||
set value(value) { | ||
this.set(value); | ||
} | ||
ref(writable) { | ||
return writable ? new ValRefImpl(this.#source$, this.#config) : new chunkLU5CXO64_js.ReadonlyValRefImpl(this.#source$, this.#config); | ||
} | ||
}; | ||
function val(value, config) { | ||
return new ValImpl(value, config); | ||
} | ||
// src/value-enhancer.ts | ||
var setValue = (val2, value) => val2.set?.(value); | ||
var subscribe = (val2, subscriber, eager) => val2.subscribe(subscriber, eager); | ||
var reaction = (val2, subscriber, eager) => val2.reaction(subscriber, eager); | ||
var unsubscribe = (val2, subscriber) => val2.unsubscribe(subscriber); | ||
Object.defineProperty(exports, 'arrayShallowEqual', { | ||
enumerable: true, | ||
get: function () { return chunkLU5CXO64_js.arrayShallowEqual; } | ||
get: function () { return chunkJNRMXROO_js.arrayShallowEqual; } | ||
}); | ||
Object.defineProperty(exports, 'attachSetter', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.attachSetter; } | ||
}); | ||
Object.defineProperty(exports, 'combine', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.combine; } | ||
}); | ||
Object.defineProperty(exports, 'derive', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.derive; } | ||
}); | ||
Object.defineProperty(exports, 'flatten', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.flatten; } | ||
}); | ||
Object.defineProperty(exports, 'flattenFrom', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.flattenFrom; } | ||
}); | ||
Object.defineProperty(exports, 'from', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.from; } | ||
}); | ||
Object.defineProperty(exports, 'groupVals', { | ||
enumerable: true, | ||
get: function () { return chunkLU5CXO64_js.groupVals; } | ||
get: function () { return chunkJNRMXROO_js.groupVals; } | ||
}); | ||
Object.defineProperty(exports, 'identity', { | ||
enumerable: true, | ||
get: function () { return chunkLU5CXO64_js.identity; } | ||
get: function () { return chunkJNRMXROO_js.identity; } | ||
}); | ||
Object.defineProperty(exports, 'isVal', { | ||
enumerable: true, | ||
get: function () { return chunkLU5CXO64_js.isVal; } | ||
get: function () { return chunkJNRMXROO_js.isVal; } | ||
}); | ||
Object.defineProperty(exports, 'nextTick', { | ||
enumerable: true, | ||
get: function () { return chunkLU5CXO64_js.nextTick; } | ||
get: function () { return chunkJNRMXROO_js.nextTick; } | ||
}); | ||
Object.defineProperty(exports, 'reaction', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.reaction; } | ||
}); | ||
Object.defineProperty(exports, 'readonlyVal', { | ||
enumerable: true, | ||
get: function () { return chunkLU5CXO64_js.readonlyVal; } | ||
get: function () { return chunkJNRMXROO_js.readonlyVal; } | ||
}); | ||
Object.defineProperty(exports, 'setValue', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.setValue; } | ||
}); | ||
Object.defineProperty(exports, 'strictEqual', { | ||
enumerable: true, | ||
get: function () { return chunkLU5CXO64_js.strictEqual; } | ||
get: function () { return chunkJNRMXROO_js.strictEqual; } | ||
}); | ||
exports.combine = combine; | ||
exports.derive = derive; | ||
exports.flatten = flatten; | ||
exports.flattenFrom = flattenFrom; | ||
exports.from = from; | ||
exports.reaction = reaction; | ||
exports.setValue = setValue; | ||
exports.subscribe = subscribe; | ||
exports.unsubscribe = unsubscribe; | ||
exports.val = val; | ||
Object.defineProperty(exports, 'subscribe', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.subscribe; } | ||
}); | ||
Object.defineProperty(exports, 'unsubscribe', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.unsubscribe; } | ||
}); | ||
Object.defineProperty(exports, 'val', { | ||
enumerable: true, | ||
get: function () { return chunkJNRMXROO_js.val; } | ||
}); |
{ | ||
"name": "value-enhancer", | ||
"version": "5.1.2", | ||
"version": "5.1.3", | ||
"private": false, | ||
@@ -44,2 +44,13 @@ "description": "A tiny library to enhance value with reactive wrapper.", | ||
], | ||
"scripts": { | ||
"prepublishOnly": "pnpm run build", | ||
"lint": "eslint --ext .ts,.tsx . && prettier --check . && tsc --noEmit", | ||
"test": "tsc --noEmit -p ./test/tsconfig.json && jest", | ||
"docs": "typedoc --options typedoc.json", | ||
"types": "cross-env NODE_ENV=production tsc --declaration --emitDeclarationOnly --jsx react --esModuleInterop --outDir dist", | ||
"build": "cross-env NODE_ENV=production tsup-node", | ||
"build:min": "cross-env NODE_ENV=production MINIFY=true tsup-node && node scripts/gzip.mjs", | ||
"build:dev": "cross-env NODE_ENV=development tsup-node src/index.ts", | ||
"release": "standard-version" | ||
}, | ||
"devDependencies": { | ||
@@ -63,13 +74,3 @@ "@jest/globals": "^29.5.0", | ||
"yoctocolors": "^1.0.0" | ||
}, | ||
"scripts": { | ||
"lint": "eslint --ext .ts,.tsx . && prettier --check . && tsc --noEmit", | ||
"test": "tsc --noEmit -p ./test/tsconfig.json && jest", | ||
"docs": "typedoc --options typedoc.json", | ||
"types": "cross-env NODE_ENV=production tsc --declaration --emitDeclarationOnly --jsx react --esModuleInterop --outDir dist", | ||
"build": "cross-env NODE_ENV=production tsup-node", | ||
"build:min": "cross-env NODE_ENV=production MINIFY=true tsup-node && node scripts/gzip.mjs", | ||
"build:dev": "cross-env NODE_ENV=development tsup-node src/index.ts", | ||
"release": "standard-version" | ||
} | ||
} | ||
} |
@@ -1,4 +0,3 @@ | ||
import { readonlyVal } from "../readonly-val"; | ||
import type { ReadonlyVal } from "../typings"; | ||
import { strictEqual } from "../utils"; | ||
import type { ReadonlyVal } from ".."; | ||
import { readonlyVal, strictEqual } from ".."; | ||
@@ -5,0 +4,0 @@ /** |
@@ -1,4 +0,3 @@ | ||
import { readonlyVal } from "../readonly-val"; | ||
import type { ReadonlyVal } from "../typings"; | ||
import { strictEqual } from "../utils"; | ||
import type { ReadonlyVal } from ".."; | ||
import { readonlyVal, strictEqual } from ".."; | ||
@@ -5,0 +4,0 @@ /** |
@@ -1,3 +0,3 @@ | ||
import { readonlyVal } from "../readonly-val"; | ||
import type { ReadonlyVal } from "../typings"; | ||
import type { ReadonlyVal } from ".."; | ||
import { readonlyVal } from ".."; | ||
@@ -4,0 +4,0 @@ /** |
@@ -1,126 +0,101 @@ | ||
import type { | ||
ReadonlyVal, | ||
UnwrapVal, | ||
ValConfig, | ||
ValDisposer, | ||
ValVersion, | ||
} from "./typings"; | ||
import type { ReadonlyVal, UnwrapVal, ValConfig, ValDisposer } from "./typings"; | ||
import { ReadonlyValImpl } from "./readonly-val"; | ||
import { Subscribers } from "./subscribers"; | ||
import { INIT_VALUE, isVal, strictEqual } from "./utils"; | ||
import { from } from "./from"; | ||
import { isVal, strictEqual } from "./utils"; | ||
import type { ValImpl } from "./val"; | ||
class FlattenFromImpl< | ||
TValOrValue = any, | ||
TValue = UnwrapVal<TValOrValue> | ||
> extends ReadonlyValImpl<TValue> { | ||
public constructor( | ||
getValue: () => TValOrValue, | ||
listen: (handler: () => void) => ValDisposer | void | undefined, | ||
config?: ValConfig<TValue> | ||
) { | ||
const initialEqual = config?.equal; | ||
/** | ||
* Creates a readonly val from a getter function and a listener function. | ||
* If the value is a val, it will be auto-flattened. | ||
* | ||
* @param getValue A function that returns the current value. | ||
* If the value is a val, it will be auto-flattened. | ||
* @param listen A function that takes a notify function and returns a disposer. | ||
* The notify function should be called when the value changes. | ||
* @param config custom config for the val. | ||
* @returns A readonly val with value of inner val. | ||
*/ | ||
// export const flattenFrom2 = <TValOrValue = any>( | ||
// getValue: () => TValOrValue, | ||
// listen: (notify: () => void) => ValDisposer | void | undefined, | ||
// config?: ValConfig<UnwrapVal<TValOrValue>> | ||
// ): ReadonlyVal<UnwrapVal<TValOrValue>> => | ||
// new FlattenFromImpl(getValue, listen, config); | ||
let currentValue = INIT_VALUE as TValue; | ||
let dirty = false; | ||
let notified = false; | ||
/** | ||
* Creates a readonly val from a getter function and a listener function. | ||
* If the value is a val, it will be auto-flattened. | ||
* | ||
* @param getValue A function that returns the current value. | ||
* If the value is a val, it will be auto-flattened. | ||
* @param listen A function that takes a notify function and returns a disposer. | ||
* The notify function should be called when the value changes. | ||
* @param config custom config for the val. | ||
* @returns A readonly val with value of inner val. | ||
*/ | ||
export const flattenFrom = <TValOrValue = any>( | ||
getValue: () => TValOrValue, | ||
listen: (notify: () => void) => ValDisposer | void | undefined, | ||
config?: ValConfig<UnwrapVal<TValOrValue>> | ||
): ReadonlyVal<UnwrapVal<TValOrValue>> => { | ||
const initialEqual = config?.equal; | ||
let innerMaybeVal: TValOrValue | undefined; | ||
let innerVal: ValImpl<UnwrapVal<TValOrValue>> | undefined | null; | ||
let innerDisposer: ValDisposer | undefined | null; | ||
let withSubscriber = false; | ||
let innerMaybeVal: TValOrValue | undefined; | ||
let innerVal: ReadonlyValImpl<TValue> | undefined | null; | ||
let innerDisposer: ValDisposer | undefined | null; | ||
const computeValue = (): UnwrapVal<TValOrValue> => { | ||
if (!withSubscriber) { | ||
updateInnerVal(); | ||
} | ||
return innerVal | ||
? innerVal.value | ||
: (innerMaybeVal as UnwrapVal<TValOrValue>); | ||
}; | ||
const computeValue = (): TValue => { | ||
if (subs.size_ <= 0) { | ||
updateInnerVal(); | ||
} | ||
return innerVal ? innerVal.value : (innerMaybeVal as TValue); | ||
}; | ||
const updateInnerVal = () => { | ||
const maybeVal = getValue(); | ||
if (!strictEqual(maybeVal, innerMaybeVal)) { | ||
innerMaybeVal = maybeVal; | ||
innerVal = isVal(maybeVal) | ||
? (maybeVal as unknown as ValImpl<UnwrapVal<TValOrValue>>) | ||
: null; | ||
(val$ as ValImpl).$equal = | ||
initialEqual || | ||
(initialEqual === false | ||
? void 0 | ||
: innerVal | ||
? innerVal.$equal | ||
: strictEqual); | ||
} | ||
}; | ||
const get = () => { | ||
if (currentValue === INIT_VALUE || subs.size_ <= 0) { | ||
currentValue = computeValue(); | ||
subs.newVersion_(config, currentValue); | ||
} else if (dirty) { | ||
const value = computeValue(); | ||
if (!this.$equal?.(value, currentValue)) { | ||
subs.dirty_ = true; | ||
subs.newVersion_(config, value, currentValue); | ||
currentValue = value; | ||
} | ||
} | ||
dirty = notified = false; | ||
return currentValue; | ||
}; | ||
const updateInnerValCompute = (notify: () => void) => { | ||
innerDisposer?.(); | ||
innerDisposer = innerVal && innerVal.$valCompute(notify); | ||
}; | ||
const updateInnerVal = () => { | ||
const maybeVal = getValue(); | ||
if (!strictEqual(maybeVal, innerMaybeVal)) { | ||
innerMaybeVal = maybeVal; | ||
innerVal = isVal(maybeVal) | ||
? (maybeVal as unknown as ReadonlyValImpl<TValue>) | ||
: null; | ||
innerDisposer?.(); | ||
innerDisposer = innerVal && innerVal.$valCompute(notify); | ||
this.$equal = | ||
initialEqual || | ||
(initialEqual === false | ||
? void 0 | ||
: innerVal | ||
? innerVal.$equal | ||
: strictEqual); | ||
} | ||
}; | ||
const val$ = from( | ||
computeValue, | ||
notify => { | ||
withSubscriber = true; | ||
updateInnerVal(); | ||
updateInnerValCompute(notify); | ||
const notify = () => { | ||
dirty = true; | ||
if (!notified) { | ||
notified = true; | ||
subs.notify_(); | ||
} | ||
}; | ||
const subs = new Subscribers(get, subs => { | ||
// attach listener first so that upstream value is resolved | ||
const outerDisposer = listen(() => { | ||
updateInnerVal(); | ||
updateInnerValCompute(notify); | ||
notify(); | ||
}); | ||
updateInnerVal(); | ||
currentValue = innerVal ? innerVal.value : (innerMaybeVal as TValue); | ||
subs.newVersion_(config, currentValue); | ||
dirty = notified = false; | ||
return () => { | ||
withSubscriber = false; | ||
innerDisposer?.(); | ||
outerDisposer?.(); | ||
}; | ||
}); | ||
}, | ||
config | ||
); | ||
super(subs, config); | ||
} | ||
override get $version(): ValVersion { | ||
// resolve current value for the latest version | ||
this.get(); | ||
return super.$version; | ||
} | ||
} | ||
/** | ||
* Creates a readonly val from a getter function and a listener function. | ||
* If the value is a val, it will be auto-flattened. | ||
* | ||
* @param getValue A function that returns the current value. | ||
* If the value is a val, it will be auto-flattened. | ||
* @param listen A function that takes a notify function and returns a disposer. | ||
* The notify function should be called when the value changes. | ||
* @param config custom config for the val. | ||
* @returns A readonly val with value of inner val. | ||
*/ | ||
export const flattenFrom = <TValOrValue = any>( | ||
getValue: () => TValOrValue, | ||
listen: (notify: () => void) => ValDisposer | void | undefined, | ||
config?: ValConfig<UnwrapVal<TValOrValue>> | ||
): ReadonlyVal<UnwrapVal<TValOrValue>> => | ||
new FlattenFromImpl(getValue, listen, config); | ||
return val$; | ||
}; |
@@ -1,3 +0,3 @@ | ||
import type { ReadonlyValImpl } from "./readonly-val"; | ||
import type { ReadonlyVal, UnwrapVal, ValConfig } from "./typings"; | ||
import type { ValImpl } from "./val"; | ||
@@ -53,3 +53,3 @@ import { flattenFrom } from "./flatten-from"; | ||
TValOrValue = any, | ||
TSrcVal extends ReadonlyValImpl = ReadonlyValImpl<TSrcValue> | ||
TSrcVal extends ValImpl = ValImpl<TSrcValue> | ||
>( | ||
@@ -56,0 +56,0 @@ val: TSrcVal, |
@@ -1,2 +0,1 @@ | ||
import { ReadonlyValImpl } from "./readonly-val"; | ||
import { Subscribers } from "./subscribers"; | ||
@@ -10,4 +9,5 @@ import type { | ||
import { INIT_VALUE } from "./utils"; | ||
import { ValImpl } from "./val"; | ||
class FromImpl<TValue = any> extends ReadonlyValImpl<TValue> { | ||
class FromImpl<TValue = any> extends ValImpl<TValue> { | ||
public constructor( | ||
@@ -14,0 +14,0 @@ getValue: () => TValue, |
@@ -0,1 +1,7 @@ | ||
export { combine, type CombineValTransform } from "./combine"; | ||
export { derive, type DerivedValTransform } from "./derive"; | ||
export { flatten } from "./flatten"; | ||
export { flattenFrom } from "./flatten-from"; | ||
export { from } from "./from"; | ||
export { nextTick } from "./scheduler"; | ||
export type { | ||
@@ -13,14 +19,13 @@ FlattenVal, | ||
} from "./typings"; | ||
export { nextTick } from "./scheduler"; | ||
export { arrayShallowEqual, identity, isVal, strictEqual } from "./utils"; | ||
export { combine, type CombineValTransform } from "./combine"; | ||
export { derive, type DerivedValTransform } from "./derive"; | ||
export { flatten } from "./flatten"; | ||
export { flattenFrom } from "./flatten-from"; | ||
export { from } from "./from"; | ||
export { groupVals, readonlyVal } from "./readonly-val"; | ||
export { val } from "./val"; | ||
export { reaction, setValue, subscribe, unsubscribe } from "./value-enhancer"; | ||
export { | ||
arrayShallowEqual, | ||
attachSetter, | ||
identity, | ||
isVal, | ||
reaction, | ||
setValue, | ||
strictEqual, | ||
subscribe, | ||
unsubscribe, | ||
} from "./utils"; | ||
export { groupVals, readonlyVal, val } from "./val"; |
@@ -1,3 +0,58 @@ | ||
import type { ReadonlyVal, ValInputsValueTuple, ValVersion } from "./typings"; | ||
import type { | ||
ReadonlyVal, | ||
Val, | ||
ValDisposer, | ||
ValInputsValueTuple, | ||
ValSubscriber, | ||
ValVersion, | ||
} from "./typings"; | ||
/** | ||
* @deprecated | ||
* Set the value of a val. | ||
* It works for both `Val` and `ReadonlyVal` type (if the `ReadonlyVal` is actually a `Val`). | ||
* Do nothing if the val is really `ReadonlyVal`. | ||
*/ | ||
export const setValue = <TValue>( | ||
val: ReadonlyVal<TValue>, | ||
value: TValue | ||
): void => (val as Val<TValue>).set?.(value); | ||
/** | ||
* Subscribe to value changes with immediate emission. | ||
* @param val | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
export const subscribe = <TValue>( | ||
val: ReadonlyVal<TValue>, | ||
subscriber: ValSubscriber<TValue>, | ||
eager?: boolean | ||
): ValDisposer => val.subscribe(subscriber, eager); | ||
/** | ||
* Subscribe to value changes without immediate emission. | ||
* @param val | ||
* @param subscriber | ||
* @param eager by default subscribers will be notified on next tick. set `true` to notify subscribers of value changes synchronously. | ||
* @returns a disposer function that cancels the subscription | ||
*/ | ||
export const reaction = <TValue>( | ||
val: ReadonlyVal<TValue>, | ||
subscriber: ValSubscriber<TValue>, | ||
eager?: boolean | ||
): ValDisposer => val.reaction(subscriber, eager); | ||
/** | ||
* Remove the given subscriber. | ||
* Remove all if no subscriber provided. | ||
* @param val | ||
* @param subscriber | ||
*/ | ||
export const unsubscribe = <TValue>( | ||
val: ReadonlyVal<TValue>, | ||
subscriber?: (...args: any[]) => any | ||
): void => val.unsubscribe(subscriber); | ||
/** Returns the value passed in. */ | ||
@@ -59,2 +114,13 @@ export const identity = <TValue>(value: TValue): TValue => value; | ||
/** | ||
* Attach a new setter to a val. | ||
* @param val$ a readonly Val | ||
* @param set a function that sets the value of val$ | ||
* @returns The same val$ with the new setter. | ||
*/ | ||
export const attachSetter = <TValue>( | ||
val$: ReadonlyVal<TValue>, | ||
set: (this: void, value: TValue) => void | ||
): Val<TValue> => (((val$ as Val<TValue>).set = set), val$ as Val<TValue>); | ||
export const INIT_VALUE: any = {}; | ||
@@ -61,0 +127,0 @@ |
320
src/val.ts
@@ -1,83 +0,221 @@ | ||
import type { NoInfer, ReadonlyVal, Val, ValConfig } from "./typings"; | ||
import type { | ||
NoInfer, | ||
ReadonlyVal, | ||
Val, | ||
ValConfig, | ||
ValDisposer, | ||
ValSetValue, | ||
ValSubscriber, | ||
ValVersion, | ||
} from "./typings"; | ||
import { ReadonlyValImpl, ReadonlyValRefImpl } from "./readonly-val"; | ||
import { Subscribers } from "./subscribers"; | ||
import { SubscriberMode, Subscribers } from "./subscribers"; | ||
import { attachSetter, invoke, strictEqual } from "./utils"; | ||
export type { ValImpl }; | ||
/** | ||
* Bare minimum implementation of a readonly val. | ||
* Generally, you should use `readonlyVal` and `ReadonlyVal` instead of this class. | ||
*/ | ||
export class ValImpl<TValue = any> implements ReadonlyVal<TValue> { | ||
/** | ||
* Manage subscribers for a val. | ||
*/ | ||
readonly #subs: Subscribers<TValue>; | ||
class ValImpl<TValue = any> extends ReadonlyValImpl<TValue> { | ||
#config?: ValConfig<TValue>; | ||
readonly #config?: ValConfig; | ||
public constructor(currentValue: TValue, config?: ValConfig<TValue>) { | ||
const get = () => currentValue; | ||
readonly #eager?: boolean; | ||
const subs = new Subscribers(get); | ||
super(subs, config); | ||
/** | ||
* @param get A pure function that returns the current value of the val. | ||
* @param config Custom config for the val. | ||
* @param start A function that is called when a val get its first subscriber. | ||
* The returned disposer will be called when the last subscriber unsubscribed from the val. | ||
*/ | ||
public constructor(subs: Subscribers<TValue>, config?: ValConfig<TValue>) { | ||
this.#subs = subs; | ||
this.get = subs.getValue_; | ||
this.#config = config; | ||
this.set = (value: TValue) => { | ||
if (!this.$equal?.(value, currentValue)) { | ||
subs.dirty_ = true; | ||
subs.newVersion_(config, value, currentValue); | ||
currentValue = value; | ||
subs.notify_(); | ||
} | ||
}; | ||
this.$equal = (config?.equal ?? strictEqual) || void 0; | ||
this.#eager = config?.eager; | ||
} | ||
public set: (this: void, value: TValue) => void; | ||
public get $version(): ValVersion { | ||
return this.#subs.version_; | ||
} | ||
public override get value() { | ||
public get value(): TValue { | ||
return this.get(); | ||
} | ||
public override set value(value: TValue) { | ||
public set value(value: TValue) { | ||
this.set(value); | ||
} | ||
public override ref(): ReadonlyVal<TValue>; | ||
public override ref(writable?: false): ReadonlyVal<TValue>; | ||
public override ref(writable: true): Val<TValue>; | ||
public override ref(writable?: boolean): ReadonlyVal<TValue> | Val<TValue>; | ||
public override ref(writable?: boolean): ReadonlyVal<TValue> | Val<TValue> { | ||
return writable | ||
? new ValRefImpl(this, this.#config) | ||
: new ReadonlyValRefImpl(this, this.#config); | ||
public set(this: void, _value: TValue): void { | ||
// do nothing | ||
} | ||
} | ||
export class ValRefImpl<TValue = any> extends ReadonlyValRefImpl<TValue> { | ||
readonly #source$: ValImpl<TValue>; | ||
readonly #config?: ValConfig<TValue>; | ||
public get: (this: void) => TValue; | ||
public constructor(source$: ValImpl<TValue>, config?: ValConfig<TValue>) { | ||
super(source$); | ||
this.#source$ = source$; | ||
this.#config = config; | ||
this.set = source$.set; | ||
public $equal?: (this: void, newValue: TValue, oldValue: TValue) => boolean; | ||
public ref(writable?: boolean): ReadonlyVal<TValue> { | ||
const val$ = new ValRefImpl(this, this.#config); | ||
return writable ? attachSetter(val$, this.set) : val$; | ||
} | ||
public set: (this: void, value: TValue) => void; | ||
public reaction( | ||
subscriber: ValSubscriber<TValue>, | ||
eager = this.#eager | ||
): ValDisposer { | ||
return this.#subs.add_( | ||
subscriber, | ||
eager ? SubscriberMode.Eager : SubscriberMode.Async | ||
); | ||
} | ||
public override get value() { | ||
return this.get(); | ||
public subscribe( | ||
subscriber: ValSubscriber<TValue>, | ||
eager = this.#eager | ||
): ValDisposer { | ||
const disposer = this.reaction(subscriber, eager); | ||
invoke(subscriber, this.value); | ||
this.#subs.dirty_ = false; | ||
return disposer; | ||
} | ||
public override set value(value: TValue) { | ||
this.set(value); | ||
public $valCompute(subscriber: ValSubscriber<void>): ValDisposer { | ||
return this.#subs.add_(subscriber, SubscriberMode.Computed); | ||
} | ||
public override ref(): ReadonlyVal<TValue>; | ||
public override ref(writable?: false): ReadonlyVal<TValue>; | ||
public override ref(writable: true): ReadonlyVal<TValue>; | ||
public override ref(writable?: boolean): ReadonlyVal<TValue> | Val<TValue>; | ||
public override ref(writable?: boolean): ReadonlyVal<TValue> | Val<TValue> { | ||
return writable | ||
? new ValRefImpl(this.#source$, this.#config) | ||
: new ReadonlyValRefImpl(this.#source$, this.#config); | ||
public unsubscribe(subscriber?: (...args: any[]) => any): void { | ||
if (subscriber) { | ||
this.#subs.remove_(subscriber); | ||
} else { | ||
this.#subs.clear_(); | ||
} | ||
} | ||
public dispose(): void { | ||
this.#subs.clear_(); | ||
} | ||
/** | ||
* @returns the string representation of `this.value`. | ||
* | ||
* @example | ||
* ```js | ||
* const v$ = val(val(val(1))); | ||
* console.log(`${v$}`); // "1" | ||
* ``` | ||
*/ | ||
public toString(): string { | ||
return String(this.value); | ||
} | ||
/** | ||
* @returns the JSON representation of `this.value`. | ||
* | ||
* @example | ||
* ```js | ||
* const v$ = val(val(val({ a: 1 }))); | ||
* JSON.stringify(v$); // '{"a":1}' | ||
* ``` | ||
*/ | ||
public toJSON(key: string): unknown { | ||
const value = this.value as | ||
| undefined | ||
| null | ||
| { toJSON?: (key: string) => unknown }; | ||
return value && value.toJSON ? value.toJSON(key) : value; | ||
} | ||
} | ||
export class ValRefImpl<TValue = any> extends ValImpl<TValue> { | ||
public constructor(source$: ValImpl<TValue>, config?: ValConfig<TValue>) { | ||
const subs = new Subscribers(source$.get, () => | ||
source$.$valCompute(() => { | ||
subs.dirty_ = true; | ||
subs.newVersion_(config); | ||
subs.notify_(); | ||
}) | ||
); | ||
super(subs, config); | ||
} | ||
} | ||
/** | ||
* Creates a readonly val with the given value. | ||
* | ||
* @returns A tuple with the readonly val and a function to set the value. | ||
*/ | ||
export function readonlyVal<TValue = any>(): [ | ||
ReadonlyVal<NoInfer<TValue> | undefined>, | ||
ValSetValue<NoInfer<TValue> | undefined> | ||
]; | ||
/** | ||
* Creates a readonly val with the given value. | ||
* | ||
* @param value Value for the val | ||
* @param config Optional custom config for the val. | ||
* @returns A tuple with the readonly val and a function to set the value. | ||
*/ | ||
export function readonlyVal( | ||
value: [], | ||
config?: ValConfig<any[]> | ||
): [ReadonlyVal<any[]>, ValSetValue<any[]>]; | ||
/** | ||
* Creates a readonly val with the given value. | ||
* | ||
* @param value Value for the val | ||
* @param config Optional custom config for the val. | ||
* @returns A tuple with the readonly val and a function to set the value. | ||
*/ | ||
export function readonlyVal<TValue = any>( | ||
value: TValue, | ||
config?: ValConfig<TValue> | ||
): [ReadonlyVal<NoInfer<TValue>>, ValSetValue<NoInfer<TValue>>]; | ||
/** | ||
* Creates a readonly val with the given value. | ||
* | ||
* @param value Optional value for the val | ||
* @param config Optional custom config for the val. | ||
* @returns A tuple with the readonly val and a function to set the value. | ||
*/ | ||
export function readonlyVal<TValue = any>( | ||
value?: TValue, | ||
config?: ValConfig<TValue> | ||
): [ | ||
ReadonlyVal<NoInfer<TValue | undefined>>, | ||
ValSetValue<NoInfer<TValue | undefined>> | ||
]; | ||
export function readonlyVal<TValue = any>( | ||
value?: TValue, | ||
config?: ValConfig<TValue | undefined> | ||
): [ | ||
ReadonlyVal<NoInfer<TValue> | undefined>, | ||
ValSetValue<NoInfer<TValue> | undefined> | ||
] { | ||
let currentValue = value; | ||
const get = () => currentValue; | ||
const subs = new Subscribers(get); | ||
const set = (value: TValue | undefined): void => { | ||
if (!val.$equal?.(value, currentValue)) { | ||
subs.dirty_ = true; | ||
subs.newVersion_(config, value, currentValue); | ||
currentValue = value; | ||
subs.notify_(); | ||
} | ||
}; | ||
const val = new ValImpl(subs, config); | ||
return [val, set]; | ||
} | ||
/** | ||
* Creates a writable val. | ||
@@ -90,3 +228,3 @@ * @returns A val with undefined value. | ||
* @param value Initial value. | ||
* @param config Custom config. | ||
* @param config Optional custom config. | ||
*/ | ||
@@ -97,3 +235,3 @@ export function val(value: [], config?: ValConfig<any[]>): Val<any[]>; | ||
* @param value Initial value. | ||
* @param config Custom config. | ||
* @param config Optional custom config. | ||
*/ | ||
@@ -104,7 +242,75 @@ export function val<TValue = any>( | ||
): Val<NoInfer<TValue>>; | ||
/** | ||
* Creates a writable val. | ||
* @param value Initial value. | ||
* @param config Optional custom config. | ||
*/ | ||
export function val<TValue = any>( | ||
value?: TValue, | ||
config?: ValConfig<TValue | undefined> | ||
): Val<NoInfer<TValue>>; | ||
export function val<TValue = any>( | ||
value?: TValue, | ||
config?: ValConfig<TValue> | ||
): Val<NoInfer<TValue>> { | ||
return new ValImpl(value as TValue, config); | ||
): Val<NoInfer<TValue | undefined>> { | ||
const [val$, set] = readonlyVal(value, config); | ||
return attachSetter(val$, set); | ||
} | ||
/** | ||
* Takes an object of key-value pairs containing `ReadonlyVal` instances and their corresponding `ValSetValue` functions, | ||
* and returns a tuple containing an array of the `ReadonlyVal` instances and a function to set their values. | ||
* | ||
* @example | ||
* ```ts | ||
* const [vals, setVals] = groupVals({ | ||
* a: readonlyVal(1), | ||
* b: readonlyVal(2), | ||
* c: readonlyVal(3), | ||
* }); | ||
* | ||
* vals.a.value; // 1 | ||
* | ||
* setVals.a(2); | ||
* ``` | ||
* | ||
* This is useful for classes that have multiple `ReadonlyVal` instances as properties. | ||
* | ||
* ```ts | ||
* export interface Foo$ { | ||
* a: ReadonlyVal<number>; | ||
* b: ReadonlyVal<number>; | ||
* c: ReadonlyVal<number>; | ||
* } | ||
* | ||
* export class Foo { | ||
* public $: Foo$; | ||
* private setVals: { [K in keyof Foo$]: ValSetValue<UnwrapVal<Foo$[K]>> }; | ||
* | ||
* public constructor() { | ||
* const [vals, setVals] = groupVals({ | ||
* a: readonlyVal(1), | ||
* b: readonlyVal(2), | ||
* c: readonlyVal(3), | ||
* }); | ||
* | ||
* this.$ = vals; | ||
* this.setVals = setVals; | ||
* } | ||
* ``` | ||
*/ | ||
export const groupVals = <TValues extends {}>(valPairs: { | ||
[K in keyof TValues]: [ReadonlyVal<TValues[K]>, ValSetValue<TValues[K]>]; | ||
}): [ | ||
{ [K in keyof TValues]: ReadonlyVal<TValues[K]> }, | ||
{ [K in keyof TValues]: ValSetValue<TValues[K]> } | ||
] => { | ||
const vals = {} as { [K in keyof TValues]: ReadonlyVal<TValues[K]> }; | ||
const setters = {} as { [K in keyof TValues]: ValSetValue<TValues[K]> }; | ||
for (const key of Object.keys(valPairs) as (keyof TValues)[]) { | ||
const [val, set] = valPairs[key]; | ||
vals[key] = val; | ||
setters[key] = set; | ||
} | ||
return [vals, setters]; | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
152974
28
4413