js-draw
Version:
Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.
185 lines (184 loc) • 7.63 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _ReactiveValueImpl_value, _ReactiveValueImpl_onUpdateListeners;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MutableReactiveValue = exports.ReactiveValue = void 0;
const noOpUpdateListenerResult = {
remove() { },
};
/**
* An update listener that does nothing. Useful for reactive values
* that will never change.
*/
const noOpSetUpdateListener = () => {
return noOpUpdateListenerResult;
};
/**
* A `ReactiveValue` is a value that
* - updates periodically,
* - can fire listeners when it updates,
* - and can be chanined together with other `ReactiveValue`s.
*
* A `ReactiveValue` is a read-only view. See {@link MutableReactiveValue} for a
* read-write view.
*
* Static methods in the `ReactiveValue` and `MutableReactiveValue` classes are
* constructors (e.g. `fromImmutable`).
*
* Avoid extending this class from an external library, as that may not be stable.
*/
class ReactiveValue {
/** Returns a promise that resolves when this value is next changed. */
waitForNextUpdate() {
return new Promise((resolve) => {
const listener = this.onUpdate((value) => {
listener.remove();
resolve(value);
});
});
}
/** Creates a `ReactiveValue` with an initial value, `initialValue`. */
static fromInitialValue(initialValue) {
return new ReactiveValueImpl(initialValue);
}
/** Returns a `ReactiveValue` that is **known** will never change. */
static fromImmutable(value) {
return {
get: () => value,
onUpdate: noOpSetUpdateListener,
onUpdateAndNow: (callback) => {
callback(value);
return noOpUpdateListenerResult;
},
// Never resolves -- immutable.
waitForNextUpdate: () => new Promise(() => { }),
};
}
/**
* Creates a `ReactiveValue` whose values come from `callback`.
*
* `callback` is called whenever any of `sourceValues` are updated and initially to
* set the initial value of the result.
*/
static fromCallback(callback, sourceValues) {
const result = new ReactiveValueImpl(callback());
const resultRef = typeof WeakRef !== 'undefined' ? new WeakRef(result) : { deref: () => result };
for (const value of sourceValues) {
const listener = value.onUpdate(() => {
// Use resultRef to allow `result` to be garbage collected
// despite this listener.
const value = resultRef.deref();
if (value) {
value.set(callback());
}
else {
listener.remove();
}
});
}
return result;
}
static map(source, map, inverseMap) {
const result = ReactiveValue.fromInitialValue(map(source.get()));
let expectedResultValue = result.get();
source.onUpdate((newValue) => {
expectedResultValue = map(newValue);
result.set(expectedResultValue);
});
if (inverseMap) {
result.onUpdate((newValue) => {
// Prevent infinite loops if inverseMap is not a true
// inverse.
if (newValue !== expectedResultValue) {
source.set(inverseMap(newValue));
}
});
}
return result;
}
static union(values) {
return ReactiveValue.fromCallback(() => {
return values.map((value) => value.get());
}, values);
}
}
exports.ReactiveValue = ReactiveValue;
class MutableReactiveValue extends ReactiveValue {
static fromProperty(sourceValue, propertyName) {
const child = ReactiveValue.fromInitialValue(sourceValue.get()[propertyName]);
const childRef = typeof WeakRef !== 'undefined' ? new WeakRef(child) : { deref: () => child };
// When the source is updated...
const sourceListener = sourceValue.onUpdate((newValue) => {
const childValue = childRef.deref();
if (childValue) {
childValue.set(newValue[propertyName]);
}
else {
// TODO: What if `sourceValue` would be dropped before
// the child value?
sourceListener.remove();
}
});
// When the child is updated, also apply the update to the
// parent.
child.onUpdate((newValue) => {
sourceValue.set({
...sourceValue.get(),
[propertyName]: newValue,
});
});
return child;
}
}
exports.MutableReactiveValue = MutableReactiveValue;
// @internal
class ReactiveValueImpl extends MutableReactiveValue {
constructor(initialValue) {
super();
_ReactiveValueImpl_value.set(this, void 0);
_ReactiveValueImpl_onUpdateListeners.set(this, void 0);
__classPrivateFieldSet(this, _ReactiveValueImpl_value, initialValue, "f");
__classPrivateFieldSet(this, _ReactiveValueImpl_onUpdateListeners, [], "f");
}
set(newValue) {
if (__classPrivateFieldGet(this, _ReactiveValueImpl_value, "f") === newValue) {
return;
}
__classPrivateFieldSet(this, _ReactiveValueImpl_value, newValue, "f");
for (const listener of __classPrivateFieldGet(this, _ReactiveValueImpl_onUpdateListeners, "f")) {
listener(newValue);
}
}
get() {
return __classPrivateFieldGet(this, _ReactiveValueImpl_value, "f");
}
onUpdate(listener) {
// **Note**: If memory is a concern, listeners should avoid referencing this
// reactive value directly. Doing so allows the value to be garbage collected when
// no longer referenced.
__classPrivateFieldGet(this, _ReactiveValueImpl_onUpdateListeners, "f").push(listener);
return {
remove: () => {
__classPrivateFieldSet(this, _ReactiveValueImpl_onUpdateListeners, __classPrivateFieldGet(this, _ReactiveValueImpl_onUpdateListeners, "f").filter((otherListener) => {
return otherListener !== listener;
}), "f");
},
};
}
onUpdateAndNow(callback) {
callback(this.get());
return this.onUpdate(callback);
}
}
_ReactiveValueImpl_value = new WeakMap(), _ReactiveValueImpl_onUpdateListeners = new WeakMap();
exports.default = ReactiveValue;
;