dist/decoration.js
/**
* InputKinds are passed into the @Input() decorator to define what data
* type the inputs takes. If you're using TypeScript and you define the
* types of your properties, we can automatically infer some of these
* except for the ones marked "not inferrable".
*
* For example:
*
* ```
* @Control({ name: 'button' })
* class Button {
* @Input()
* public dimensions: Mixer.IDimensions;
*
* @Input({ kind: Mixer.InputKind.Color })
* public background: string;
* }
* ```
*/
export var InputKind;
(function (InputKind) {
InputKind[InputKind["Dimensions"] = 0] = "Dimensions";
InputKind[InputKind["Number"] = 1] = "Number";
InputKind[InputKind["String"] = 2] = "String";
InputKind[InputKind["Boolean"] = 3] = "Boolean";
InputKind[InputKind["Color"] = 4] = "Color";
InputKind[InputKind["Duration"] = 5] = "Duration";
InputKind[InputKind["Url"] = 6] = "Url";
InputKind[InputKind["JSON"] = 7] = "JSON";
})(InputKind || (InputKind = {}));
const sceneMetaKey = '__mix_scene';
const controlMetaKey = '__miix_control';
const descriptorMetaKey = '__miix_descriptor';
/**
* The Registry is a simple class that maintains a list of available
* controls and scenes, and can return them given control kinds or scene IDs.
*/
export class Registry {
constructor() {
this.scenes = Object.create(null);
this.controls = Object.create(null);
this.config = window.mixerPackageConfig;
}
/**
* Adds a collection of controls and scenes to the registry. This will throw
* if anything given is not a scene or control.
* @param {...*} things
* @returns {Registry}
*/
register(...things) {
things.forEach(thing => {
if (thing[sceneMetaKey]) {
this.registerScene(thing, thing[sceneMetaKey]);
}
else if (thing[controlMetaKey]) {
this.registerControl(thing, thing[controlMetaKey]);
}
else {
throw new Error(`Passed ${thing.name} to the miix registry, but it wasn't decorated with ` +
'@Control or @Scene!');
}
});
return this;
}
/**
* Returns the Control descriptor for a control of the given kind, or
* thows if it's not found.
* @param {string} kind
* @returns {IControlDescriptor}
* @throws {Error} if the control kind is not found
*/
getControl(kind) {
const control = this.controls[kind];
if (!control) {
throw new Error(`No control was found for kind "${kind}"! If you have a class already, ` +
`make sure you're passing it to the Registry.register() function.`);
}
return control;
}
/**
* Returns the Scene descriptor for the given scene ID, returning the
* default scene if a specific handler wasn't found. Throws if no default
* scene is present and the specific ID is not registered.
* @param {string} id
* @returns {ISceneDescriptor}
* @throws {Error} if an appropriate scene implementation is not found
*/
getScene(id) {
const scene = this.scenes[id] || this.defaultScene;
if (!scene) {
throw new Error(`No scene class was found for scene ID "${id}" and no default scene was ` +
`registered! If you have a default handler, make sure you pass it to the ` +
`Registry.register() function, or create a specific class for this scene.`);
}
return scene;
}
/**
* Returns inputs defined on the given control instance.
* @param {object} control
* @returns {IInputDescriptor[]}
* @throws {Error} if the passed object is not decorated with @{@link Scene}
* or @{@link Control}.
*/
getInputs(control) {
const descriptor = control.constructor[descriptorMetaKey];
if (!descriptor) {
throw new Error(`Tried to get inputs on ${control.constructor.name}, but it isn't a ` +
`@Scene or @Control object!`);
}
return descriptor.inputs || [];
}
registerScene(scene, options) {
const existing = options.id && this.scenes[options.id];
const id = options.id || 'default';
if (existing) {
throw new Error(`Duplicate scene IDs registered! Both ${existing.ctor.name} and ` +
`${scene.name} registered themselves for scene ID ${id}`);
}
const descriptor = Object.assign({}, options, this.config.scenes[id], { ctor: scene });
Object.defineProperty(scene, descriptorMetaKey, { value: descriptor });
this.scenes[id] = descriptor;
if (options.default) {
this.defaultScene = descriptor;
}
}
registerControl(control, options) {
const existing = options.kind && this.controls[options.kind];
if (existing) {
throw new Error(`Duplicate controls registered! Both ${existing.ctor.name} and ` +
`${control.name} registered themselves for control kind ${options.kind}`);
}
const descriptor = Object.assign({}, options, this.config.controls[options.kind], { ctor: control });
Object.defineProperty(control, descriptorMetaKey, { value: descriptor });
this.controls[options.kind] = descriptor;
}
}
/**
* Scene is a decorator you can use to designate a class as a Scene. See
* documentation on {@link ISceneOptions} for more info.
* @param {ISceneOptions} [options]
*/
export function Scene(options = { default: true }) {
return (ctor) => {
Object.defineProperty(ctor, sceneMetaKey, { value: options });
};
}
/**
* Scene is a decorator you can use to designate a class as a Scene. See
* documentation on {@link IControlOptions} for more info.
* @param {IControlOptions} [options]
*/
export function Control(options) {
return (ctor) => {
Object.defineProperty(ctor, controlMetaKey, { value: options });
};
}
/**
* @Input decorates a property on a control. It makes it configurable in the
* Interactive studio and settable for Preact components. See the
* {@link IInputOptions} for more info.
* @param {IInputOptions} [_options]
*/
export function Input(_options = {}) {
return (_host, _propertyName) => {
// noop, this is handled by static analysis
};
}