material-motion-runtime
Version:
The core architecture for Material Motion.
105 lines • 4.45 kB
JavaScript
/** @license
* Copyright 2016 - present The Material Motion Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
;
const TokenGenerator_1 = require('./TokenGenerator');
const makeCompoundKeySelector_1 = require('./internal/makeCompoundKeySelector');
/**
* A runtime is responsible for fulfilling Plans by delegating them to the
* correct Performer.
*/
class Runtime {
constructor() {
this._performerMapSelector = makeCompoundKeySelector_1.default('PerformerType', 'target');
this._performerMap = new Map();
this._activityListeners = new Set();
this._isActive = false;
this._isActiveTokenGenerator = new TokenGenerator_1.default({
// Using arrow function because TypeScript doesn't support bind
// https://github.com/Microsoft/TypeScript/issues/212/
onTokenCountChange: kwargs => this._onTokenCountChange(kwargs)
});
}
/**
* If any of this runtime's performers aren't at rest, this will be true.
*/
get isActive() {
return this._isActive;
}
/**
* The runtime ensures the given plan is immediately applied to the given
* target.
*/
addPlan({ plan, target }) {
if (!plan) {
throw new Error(`runtime.addPlan requires a plan`);
}
if (!target) {
throw new Error(`runtime.addPlan requires a target`);
}
const isActiveTokenGenerator = this._isActiveTokenGenerator;
const PerformerType = plan._PerformerType;
const performerMapKey = this._performerMapSelector({ PerformerType, target });
let performer;
if (this._performerMap.has(performerMapKey)) {
performer = this._performerMap.get(performerMapKey);
}
else {
// There are a bunch of optional features that a performer might support.
// We give them all the tools we have and let them decide whether or not
// they want to use them.
//
// To express this in TypeScript, we cast PerformerType to an imaginary
// constructor that supports every feature. Of course, whatever
// performer we're actually instantiating will ignore any features it
// doesn't care about. By telling TypeScript it could support all of
// them, it should ensure we get type errors if a feature isn't threaded
// through correctly.
const PerformerOfAllFeatures = PerformerType;
performer = new PerformerOfAllFeatures({ target, isActiveTokenGenerator });
this._performerMap.set(performerMapKey, performer);
}
performer.addPlan({ plan });
}
// For now, we're using add${ propertyName }Listener to handle observation:
// - It's simple to implement.
// - It's simple to deprecate/upgrade from. When/if we have a more
// comprehensive observation story, we just have these log a warning and
// delegate to the new thing.
// - It's easy to attach to existing libraries, e.g. RxJS's fromEventPattern.
/**
* Any function passed here will be called every time runtime.isActive
* changes.
*/
addActivityListener({ listener }) {
this._activityListeners.add(listener);
}
/**
* Stops notifying the given listener of changes to runtime.isActive.
*/
removeActivityListener({ listener }) {
this._activityListeners.delete(listener);
}
_onTokenCountChange({ count }) {
const wasActive = this._isActive;
this._isActive = count !== 0;
if (this._isActive !== wasActive) {
this._activityListeners.forEach(listener => listener({ isActive: this._isActive }));
}
}
}
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Runtime;
//# sourceMappingURL=Runtime.js.map