123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import { IDisposable } from '@lumino/disposable';
- import { ISignal, Signal } from '@lumino/signaling';
- import { IObservable } from './modeldb';
- /**
- * A map which can be observed for changes.
- */
- export interface IObservableMap<T> extends IDisposable, IObservable {
- /**
- * The type of the Observable.
- */
- type: 'Map';
- /**
- * A signal emitted when the map has changed.
- */
- readonly changed: ISignal<this, IObservableMap.IChangedArgs<T>>;
- /**
- * The number of key-value pairs in the map.
- */
- readonly size: number;
- /**
- * Set a key-value pair in the map
- *
- * @param key - The key to set.
- *
- * @param value - The value for the key.
- *
- * @returns the old value for the key, or undefined
- * if that did not exist.
- */
- set(key: string, value: T): T | undefined;
- /**
- * Get a value for a given key.
- *
- * @param key - the key.
- *
- * @returns the value for that key.
- */
- get(key: string): T | undefined;
- /**
- * Check whether the map has a key.
- *
- * @param key - the key to check.
- *
- * @returns `true` if the map has the key, `false` otherwise.
- */
- has(key: string): boolean;
- /**
- * Get a list of the keys in the map.
- *
- * @returns - a list of keys.
- */
- keys(): string[];
- /**
- * Get a list of the values in the map.
- *
- * @returns - a list of values.
- */
- values(): T[];
- /**
- * Remove a key from the map
- *
- * @param key - the key to remove.
- *
- * @returns the value of the given key,
- * or undefined if that does not exist.
- */
- delete(key: string): T | undefined;
- /**
- * Set the ObservableMap to an empty map.
- */
- clear(): void;
- /**
- * Dispose of the resources held by the map.
- */
- dispose(): void;
- }
- /**
- * The interfaces associated with an IObservableMap.
- */
- export namespace IObservableMap {
- /**
- * The change types which occur on an observable map.
- */
- export type ChangeType =
- /**
- * An entry was added.
- */
- | 'add'
- /**
- * An entry was removed.
- */
- | 'remove'
- /**
- * An entry was changed.
- */
- | 'change';
- /**
- * The changed args object which is emitted by an observable map.
- */
- export interface IChangedArgs<T> {
- /**
- * The type of change undergone by the map.
- */
- type: ChangeType;
- /**
- * The key of the change.
- */
- key: string;
- /**
- * The old value of the change.
- */
- oldValue: T | undefined;
- /**
- * The new value of the change.
- */
- newValue: T | undefined;
- }
- }
- /**
- * A concrete implementation of IObservbleMap<T>.
- */
- export class ObservableMap<T> implements IObservableMap<T> {
- /**
- * Construct a new observable map.
- */
- constructor(options: ObservableMap.IOptions<T> = {}) {
- this._itemCmp = options.itemCmp || Private.itemCmp;
- if (options.values) {
- for (const key in options.values) {
- this._map.set(key, options.values[key]);
- }
- }
- }
- /**
- * The type of the Observable.
- */
- get type(): 'Map' {
- return 'Map';
- }
- /**
- * A signal emitted when the map has changed.
- */
- get changed(): ISignal<this, IObservableMap.IChangedArgs<T>> {
- return this._changed;
- }
- /**
- * Whether this map has been disposed.
- */
- get isDisposed(): boolean {
- return this._isDisposed;
- }
- /**
- * The number of key-value pairs in the map.
- */
- get size(): number {
- return this._map.size;
- }
- /**
- * Set a key-value pair in the map
- *
- * @param key - The key to set.
- *
- * @param value - The value for the key.
- *
- * @returns the old value for the key, or undefined
- * if that did not exist.
- *
- * @throws if the new value is undefined.
- *
- * #### Notes
- * This is a no-op if the value does not change.
- */
- set(key: string, value: T): T | undefined {
- const oldVal = this._map.get(key);
- if (value === undefined) {
- throw Error('Cannot set an undefined value, use remove');
- }
- // Bail if the value does not change.
- const itemCmp = this._itemCmp;
- if (oldVal !== undefined && itemCmp(oldVal, value)) {
- return oldVal;
- }
- this._map.set(key, value);
- this._changed.emit({
- type: oldVal ? 'change' : 'add',
- key: key,
- oldValue: oldVal,
- newValue: value
- });
- return oldVal;
- }
- /**
- * Get a value for a given key.
- *
- * @param key - the key.
- *
- * @returns the value for that key.
- */
- get(key: string): T | undefined {
- return this._map.get(key);
- }
- /**
- * Check whether the map has a key.
- *
- * @param key - the key to check.
- *
- * @returns `true` if the map has the key, `false` otherwise.
- */
- has(key: string): boolean {
- return this._map.has(key);
- }
- /**
- * Get a list of the keys in the map.
- *
- * @returns - a list of keys.
- */
- keys(): string[] {
- const keyList: string[] = [];
- this._map.forEach((v: T, k: string) => {
- keyList.push(k);
- });
- return keyList;
- }
- /**
- * Get a list of the values in the map.
- *
- * @returns - a list of values.
- */
- values(): T[] {
- const valList: T[] = [];
- this._map.forEach((v: T, k: string) => {
- valList.push(v);
- });
- return valList;
- }
- /**
- * Remove a key from the map
- *
- * @param key - the key to remove.
- *
- * @returns the value of the given key,
- * or undefined if that does not exist.
- *
- * #### Notes
- * This is a no-op if the value does not change.
- */
- delete(key: string): T | undefined {
- const oldVal = this._map.get(key);
- const removed = this._map.delete(key);
- if (removed) {
- this._changed.emit({
- type: 'remove',
- key: key,
- oldValue: oldVal,
- newValue: undefined
- });
- }
- return oldVal;
- }
- /**
- * Set the ObservableMap to an empty map.
- */
- clear(): void {
- // Delete one by one to emit the correct signals.
- const keyList = this.keys();
- for (let i = 0; i < keyList.length; i++) {
- this.delete(keyList[i]);
- }
- }
- /**
- * Dispose of the resources held by the map.
- */
- dispose(): void {
- if (this.isDisposed) {
- return;
- }
- this._isDisposed = true;
- Signal.clearData(this);
- this._map.clear();
- }
- private _map: Map<string, T> = new Map<string, T>();
- private _itemCmp: (first: T, second: T) => boolean;
- private _changed = new Signal<this, IObservableMap.IChangedArgs<T>>(this);
- private _isDisposed = false;
- }
- /**
- * The namespace for `ObservableMap` class statics.
- */
- export namespace ObservableMap {
- /**
- * The options used to initialize an observable map.
- */
- export interface IOptions<T> {
- /**
- * An optional initial set of values.
- */
- values?: { [key: string]: T };
- /**
- * The item comparison function for change detection on `set`.
- *
- * If not given, strict `===` equality will be used.
- */
- itemCmp?: (first: T, second: T) => boolean;
- }
- }
- /**
- * The namespace for module private data.
- */
- namespace Private {
- /**
- * The default strict equality item comparator.
- */
- export function itemCmp(first: any, second: any): boolean {
- return first === second;
- }
- }
|