|
@@ -11,6 +11,7 @@ import { Transform } from './tree/transform';
|
|
|
|
|
|
export interface Transformer<A extends StateObject = StateObject, B extends StateObject = StateObject, P = unknown> {
|
|
|
apply(params?: P, props?: Partial<Transform.Props>): Transform<A, B, P>,
|
|
|
+ readonly namespace: string,
|
|
|
readonly id: Transformer.Id,
|
|
|
readonly definition: Transformer.Definition<A, B, P>
|
|
|
}
|
|
@@ -21,9 +22,22 @@ export namespace Transformer {
|
|
|
export type To<T extends Transformer<any, any, any>> = T extends Transformer<any, infer B, any> ? B : unknown;
|
|
|
export type ControlsFor<Props> = { [P in keyof Props]?: any }
|
|
|
|
|
|
+ export interface ApplyParams<A extends StateObject = StateObject, P = unknown> {
|
|
|
+ a: A,
|
|
|
+ params: P
|
|
|
+ }
|
|
|
+
|
|
|
+ export interface UpdateParams<A extends StateObject = StateObject, B extends StateObject = StateObject, P = unknown> {
|
|
|
+ a: A,
|
|
|
+ b: B,
|
|
|
+ oldParams: P,
|
|
|
+ newParams: P
|
|
|
+ }
|
|
|
+
|
|
|
+ export enum UpdateResult { Unchanged, Updated, Recreate }
|
|
|
+
|
|
|
export interface Definition<A extends StateObject = StateObject, B extends StateObject = StateObject, P = unknown> {
|
|
|
readonly name: string,
|
|
|
- readonly namespace?: string,
|
|
|
readonly from: { type: StateObject.Type }[],
|
|
|
readonly to: { type: StateObject.Type }[],
|
|
|
|
|
@@ -31,16 +45,14 @@ export namespace Transformer {
|
|
|
* Apply the actual transformation. It must be pure (i.e. with no side effects).
|
|
|
* Returns a task that produces the result of the result directly.
|
|
|
*/
|
|
|
- apply(a: A, params: P, context: TransformContext): Task<B> | B,
|
|
|
+ apply(params: ApplyParams<A, P>): Task<B> | B,
|
|
|
|
|
|
/**
|
|
|
* Attempts to update the entity in a non-destructive way.
|
|
|
* For example changing a color scheme of a visual does not require computing new geometry.
|
|
|
* Return/resolve to undefined if the update is not possible.
|
|
|
- *
|
|
|
- * The ability to resolve the task to undefined is present for "async updates" (i.e. containing an ajax call).
|
|
|
*/
|
|
|
- update?(a: A, oldParams: P, b: B, newParams: P, context: TransformContext): Task<B | undefined> | B | undefined,
|
|
|
+ update?(params: UpdateParams<A, B, P>): Task<UpdateResult> | UpdateResult,
|
|
|
|
|
|
/** Check the parameters and return a list of errors if the are not valid. */
|
|
|
defaultParams?(a: A, context: TransformContext): P,
|
|
@@ -66,12 +78,6 @@ export namespace Transformer {
|
|
|
|
|
|
const registry = new Map<Id, Transformer>();
|
|
|
|
|
|
- function typeToString(a: { type: StateObject.Type }[]) {
|
|
|
- if (!a.length) return '()';
|
|
|
- if (a.length === 1) return a[0].type.kind;
|
|
|
- return `(${a.map(t => t.type.kind).join(' | ')})`;
|
|
|
- }
|
|
|
-
|
|
|
export function get(id: string): Transformer {
|
|
|
const t = registry.get(id as Id);
|
|
|
if (!t) {
|
|
@@ -81,8 +87,8 @@ export namespace Transformer {
|
|
|
}
|
|
|
|
|
|
export function create<A extends StateObject, B extends StateObject, P>(namespace: string, definition: Definition<A, B, P>) {
|
|
|
- const { from, to, name } = definition;
|
|
|
- const id = `${namespace}.${name} :: ${typeToString(from)} -> ${typeToString(to)}` as Id;
|
|
|
+ const { name } = definition;
|
|
|
+ const id = `${namespace}.${name}` as Id;
|
|
|
|
|
|
if (registry.has(id)) {
|
|
|
throw new Error(`A transform with id '${name}' is already registered. Please pick a unique identifier for your transforms and/or register them only once. This is to ensure that transforms can be serialized and replayed.`);
|
|
@@ -90,6 +96,7 @@ export namespace Transformer {
|
|
|
|
|
|
const t: Transformer<A, B, P> = {
|
|
|
apply(params, props) { return Transform.create<A, B, P>(t as any, params, props); },
|
|
|
+ namespace,
|
|
|
id,
|
|
|
definition
|
|
|
};
|