typescriptifying MappedMap

This commit is contained in:
Isaiah Becker-Mayer 2022-07-08 22:07:36 -04:00
parent d060d337b6
commit 95c65280ef
8 changed files with 62 additions and 21 deletions

View File

@ -47,7 +47,7 @@ export class MemberListViewModel extends ViewModel {
this.nameDisambiguator.disambiguate(vm); this.nameDisambiguator.disambiguate(vm);
return vm; return vm;
} }
const updater = (vm, params, newMember) => { const updater = (params, vm, newMember) => {
vm.updateFrom(newMember); vm.updateFrom(newMember);
this.nameDisambiguator.disambiguate(vm); this.nameDisambiguator.disambiguate(vm);
}; };

View File

@ -29,7 +29,7 @@ export interface IMapObserver<K, V> {
export type BaseObservableMapConfig<K, V> = { export type BaseObservableMapConfig<K, V> = {
join(_this: BaseObservableMap<K, V>, ...otherMaps: Array<BaseObservableMap<K, V>>): JoinedMap<K, V>; join(_this: BaseObservableMap<K, V>, ...otherMaps: Array<BaseObservableMap<K, V>>): JoinedMap<K, V>;
mapValues(_this: BaseObservableMap<K, V>, mapper: any, updater?: (params: any) => void): MappedMap; mapValues(_this: BaseObservableMap<K, V>, mapper: any, updater?: (params: any) => void): MappedMap<K, V>;
sortValues(_this: BaseObservableMap<K, V>, comparator?: (a: any, b: any) => number): SortedMapList; sortValues(_this: BaseObservableMap<K, V>, comparator?: (a: any, b: any) => number): SortedMapList;
filterValues(_this: BaseObservableMap<K, V>, filter: (v: V, k: K) => boolean): FilteredMap<K, V>; filterValues(_this: BaseObservableMap<K, V>, filter: (v: V, k: K) => boolean): FilteredMap<K, V>;
} }
@ -67,7 +67,7 @@ export abstract class BaseObservableMap<K, V> extends BaseObservable<IMapObserve
// to easily use the default implementation in a class that extends // to easily use the default implementation in a class that extends
// this one (which is most likely what you want to do). // this one (which is most likely what you want to do).
abstract join(...otherMaps: Array<typeof this>): JoinedMap<K, V>; abstract join(...otherMaps: Array<typeof this>): JoinedMap<K, V>;
abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap; abstract mapValues(mapper: any, updater?: (params: any) => void): MappedMap<K, V>;
abstract sortValues(comparator?: (a: any, b: any) => number): SortedMapList; abstract sortValues(comparator?: (a: any, b: any) => number): SortedMapList;
abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap<K, V>; abstract filterValues(filter: (v: V, k: K) => boolean): FilteredMap<K, V>;

View File

@ -39,7 +39,7 @@ export class FilteredMap<K, V> extends BaseObservableMap<K, V> {
return this._config.join(this, ...otherMaps); return this._config.join(this, ...otherMaps);
} }
mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ mapValues(mapper: any, updater?: (params: any) => void): MappedMap<K, V>{
return this._config.mapValues(this, mapper, updater); return this._config.mapValues(this, mapper, updater);
} }

View File

@ -24,7 +24,7 @@ import {SubscriptionHandle} from "../BaseObservable"
export class JoinedMap<K, V> extends BaseObservableMap<K, V> { export class JoinedMap<K, V> extends BaseObservableMap<K, V> {
protected _sources: BaseObservableMap<K, V>[]; protected _sources: BaseObservableMap<K, V>[];
private _config: BaseObservableMapConfig<K, V> private _config: BaseObservableMapConfig<K, V>;
private _subscriptions?: SourceSubscriptionHandler<K, V>[]; private _subscriptions?: SourceSubscriptionHandler<K, V>[];
constructor(sources: BaseObservableMap<K, V>[]) { constructor(sources: BaseObservableMap<K, V>[]) {
@ -37,7 +37,7 @@ export class JoinedMap<K, V> extends BaseObservableMap<K, V> {
return this._config.join(this, ...otherMaps); return this._config.join(this, ...otherMaps);
} }
mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ mapValues(mapper: any, updater?: (params: any) => void): MappedMap<K, V> {
return this._config.mapValues(this, mapper, updater); return this._config.mapValues(this, mapper, updater);
} }

View File

@ -42,7 +42,7 @@ export class LogMap<K, V> extends BaseObservableMap<K, V> {
return this._config.join(this, ...otherMaps); return this._config.join(this, ...otherMaps);
} }
mapValues(mapper: any, updater?: (params: any) => void): MappedMap { mapValues(mapper: any, updater?: (params: any) => void): MappedMap<K, V> {
return this._config.mapValues(this, mapper, updater); return this._config.mapValues(this, mapper, updater);
} }

View File

@ -14,49 +14,83 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseObservableMap} from "./BaseObservableMap"; import {BaseObservableMap, BaseObservableMapConfig} from "./BaseObservableMap";
import {config} from "./config";
import {JoinedMap} from "./JoinedMap.js";
import {FilteredMap} from "./FilteredMap.js";
import {SortedMapList} from "../list/SortedMapList.js";
import {SubscriptionHandle} from "../BaseObservable";
/* /*
so a mapped value can emit updates on it's own with this._emitSpontaneousUpdate that is passed in the mapping function so a mapped value can emit updates on it's own with this._emitSpontaneousUpdate that is passed in the mapping function
how should the mapped value be notified of an update though? and can it then decide to not propagate the update? how should the mapped value be notified of an update though? and can it then decide to not propagate the update?
*/ */
export class MappedMap extends BaseObservableMap { export class MappedMap<K, V> extends BaseObservableMap<K, V> {
constructor(source, mapper, updater) { private _source: BaseObservableMap<K, V>;
private _mapper: Mapper<V>;
private _updater?: Updater<V>;
private _mappedValues: Map<K, V>;
private _subscription?: SubscriptionHandle;
private _config: BaseObservableMapConfig<K, V>
constructor(
source: BaseObservableMap<K, V>,
mapper: Mapper<V>,
updater?: Updater<V>
) {
super(); super();
this._source = source; this._source = source;
this._mapper = mapper; this._mapper = mapper;
this._updater = updater; this._updater = updater;
this._mappedValues = new Map(); this._mappedValues = new Map<K, V>();
this._config = config<K, V>();
} }
_emitSpontaneousUpdate(key, params) { join(...otherMaps: Array<typeof this>): JoinedMap<K, V> {
return this._config.join(this, ...otherMaps);
}
mapValues(mapper: any, updater?: (params: any) => void): MappedMap<K, V>{
return this._config.mapValues(this, mapper, updater);
}
sortValues(comparator?: (a: any, b: any) => number): SortedMapList {
return this._config.sortValues(this, comparator);
}
filterValues(filter: (v: V, k: K) => boolean): FilteredMap<K, V> {
return this._config.filterValues(this, filter);
}
_emitSpontaneousUpdate(key: K, params: any) {
const value = this._mappedValues.get(key); const value = this._mappedValues.get(key);
if (value) { if (value) {
this.emitUpdate(key, value, params); this.emitUpdate(key, value, params);
} }
} }
onAdd(key, value) { onAdd(key: K, value: V) {
const emitSpontaneousUpdate = this._emitSpontaneousUpdate.bind(this, key); const emitSpontaneousUpdate = this._emitSpontaneousUpdate.bind(this, key);
const mappedValue = this._mapper(value, emitSpontaneousUpdate); const mappedValue = this._mapper(value, emitSpontaneousUpdate);
this._mappedValues.set(key, mappedValue); this._mappedValues.set(key, mappedValue);
this.emitAdd(key, mappedValue); this.emitAdd(key, mappedValue);
} }
onRemove(key/*, _value*/) { onRemove(key: K/*, _value*/) {
const mappedValue = this._mappedValues.get(key); const mappedValue = this._mappedValues.get(key);
if (this._mappedValues.delete(key)) { if (this._mappedValues.delete(key)) {
this.emitRemove(key, mappedValue); if (mappedValue) this.emitRemove(key, mappedValue);
} }
} }
onUpdate(key, value, params) { onUpdate(key: K, value: V, params: any) {
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it // if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
if (!this._mappedValues) { if (!this._mappedValues) {
return; return;
} }
const mappedValue = this._mappedValues.get(key); const mappedValue = this._mappedValues.get(key);
if (mappedValue !== undefined) { if (mappedValue !== undefined) {
this._updater?.(mappedValue, params, value); this._updater?.(params, mappedValue, value);
// TODO: map params somehow if needed? // TODO: map params somehow if needed?
this.emitUpdate(key, mappedValue, params); this.emitUpdate(key, mappedValue, params);
} }
@ -74,7 +108,7 @@ export class MappedMap extends BaseObservableMap {
onUnsubscribeLast() { onUnsubscribeLast() {
super.onUnsubscribeLast(); super.onUnsubscribeLast();
this._subscription = this._subscription(); if (this._subscription) this._subscription = this._subscription();
this._mappedValues.clear(); this._mappedValues.clear();
} }
@ -91,7 +125,14 @@ export class MappedMap extends BaseObservableMap {
return this._mappedValues.size; return this._mappedValues.size;
} }
get(key) { get(key: K): V | undefined {
return this._mappedValues.get(key); return this._mappedValues.get(key);
} }
} }
type Mapper<V> = (
value: V,
emitSpontaneousUpdate: any,
) => V;
type Updater<V> = (params: any, mappedValue?: V, value?: V) => void;

View File

@ -36,7 +36,7 @@ export class ObservableMap<K, V> extends BaseObservableMap<K, V> {
return this._config.join(this, ...otherMaps); return this._config.join(this, ...otherMaps);
} }
mapValues(mapper: any, updater?: (params: any) => void): MappedMap{ mapValues(mapper: any, updater?: (params: any) => void): MappedMap<K, V> {
return this._config.mapValues(this, mapper, updater); return this._config.mapValues(this, mapper, updater);
} }

View File

@ -30,7 +30,7 @@ export function config<K, V>(): BaseObservableMapConfig<K, V> {
join: (_this: BaseObservableMap<K, V>, ...otherMaps: Array<BaseObservableMap<K, V>>): JoinedMap<K, V> => { join: (_this: BaseObservableMap<K, V>, ...otherMaps: Array<BaseObservableMap<K, V>>): JoinedMap<K, V> => {
return new JoinedMap([_this].concat(otherMaps)); return new JoinedMap([_this].concat(otherMaps));
}, },
mapValues: (_this: BaseObservableMap<K, V>, mapper: any, updater?: (params: any) => void): MappedMap => { mapValues: (_this: BaseObservableMap<K, V>, mapper: any, updater: (params: any) => void): MappedMap<K, V> => {
return new MappedMap(_this, mapper, updater); return new MappedMap(_this, mapper, updater);
}, },
sortValues: (_this: BaseObservableMap<K, V>, comparator?: (a: any, b: any) => number): SortedMapList => { sortValues: (_this: BaseObservableMap<K, V>, comparator?: (a: any, b: any) => number): SortedMapList => {