Skip to content
Snippets Groups Projects
Commit d918eee7 authored by David Sehnal's avatar David Sehnal
Browse files

mol-state: wip

parent c5fff9d3
Branches
Tags
No related merge requests found
......@@ -31,11 +31,16 @@ namespace PluginBehavior {
name: string,
ctor: Ctor<P>,
label?: (params: P) => { label: string, description?: string },
display: { name: string, description?: string },
params?: Transformer.Definition<Root, Behavior, P>['params']
display: {
name: string,
group: string,
description?: string
},
params?: Transformer.Definition<Root, Behavior, P>['params'],
}
export function create<P>(params: CreateParams<P>) {
// TODO: cache groups etc
return PluginStateTransform.Create<Root, Behavior, P>({
name: params.name,
display: params.display,
......
......@@ -11,13 +11,13 @@ import { StateTree } from 'mol-state';
export const SetCurrentObject = PluginBehavior.create({
name: 'set-current-data-object-behavior',
ctor: PluginBehavior.simpleCommandHandler(PluginCommands.Data.SetCurrentObject, ({ ref }, ctx) => ctx.state.data.setCurrent(ref)),
display: { name: 'Set Current Handler' }
display: { name: 'Set Current Handler', group: 'Data' }
});
export const Update = PluginBehavior.create({
name: 'update-data-behavior',
ctor: PluginBehavior.simpleCommandHandler(PluginCommands.Data.Update, ({ tree }, ctx) => ctx.state.updateData(tree)),
display: { name: 'Update Data Handler' }
display: { name: 'Update Data Handler', group: 'Data' }
});
export const RemoveObject = PluginBehavior.create({
......@@ -26,5 +26,5 @@ export const RemoveObject = PluginBehavior.create({
const tree = StateTree.build(ctx.state.data.tree).delete(ref).getTree();
ctx.state.updateData(tree);
}),
display: { name: 'Remove Object Handler' }
display: { name: 'Remove Object Handler', group: 'Data' }
});
\ No newline at end of file
......@@ -44,7 +44,7 @@ export const AddRepresentationToCanvas = PluginBehavior.create({
});
}
},
display: { name: 'Add Representation To Canvas' }
display: { name: 'Add Representation To Canvas', group: 'Data' }
});
export const HighlightLoci = PluginBehavior.create({
......@@ -64,7 +64,7 @@ export const HighlightLoci = PluginBehavior.create({
});
}
},
display: { name: 'Highlight Loci on Canvas' }
display: { name: 'Highlight Loci on Canvas', group: 'Data' }
});
export const SelectLoci = PluginBehavior.create({
......@@ -77,5 +77,5 @@ export const SelectLoci = PluginBehavior.create({
});
}
},
display: { name: 'Select Loci on Canvas' }
display: { name: 'Select Loci on Canvas', group: 'Data' }
});
\ No newline at end of file
......@@ -6,7 +6,7 @@
import * as React from 'react';
import { PluginContext } from '../context';
import { Transform, Transformer, StateObject } from 'mol-state';
import { Transform, Transformer } from 'mol-state';
import { ParametersComponent } from 'mol-app/component/parameters';
export class Controls extends React.Component<{ plugin: PluginContext }, { id: string }> {
......@@ -73,7 +73,7 @@ export class _test_CreateTransform extends React.Component<{ plugin: PluginConte
render() {
const obj = this.getObj();
if (obj.state !== StateObject.StateType.Ok) {
if (obj.status !== 'ok') {
// TODO filter this elsewhere
return <div />;
}
......
......@@ -7,7 +7,7 @@
import * as React from 'react';
import { PluginContext } from '../context';
import { PluginStateObject } from 'mol-plugin/state/base';
import { StateObject, State } from 'mol-state'
import { State } from 'mol-state'
import { PluginCommands } from 'mol-plugin/command';
export class StateTree extends React.Component<{ plugin: PluginContext, state: State }, { }> {
......@@ -35,9 +35,9 @@ export class StateTreeNode extends React.Component<{ plugin: PluginContext, node
PluginCommands.Data.RemoveObject.dispatch(this.props.plugin, { ref: this.props.nodeRef });
}}>X</a>]</>
if (!obj.obj) {
if (obj.status !== 'ok' || !obj.obj) {
return <div style={{ borderLeft: '1px solid black', paddingLeft: '7px' }}>
{remove} {StateObject.StateType[obj.state]} {obj.errorText}
{remove} {obj.status} {obj.errorText}
</div>;
}
const props = obj.obj!.props as PluginStateObject.Props;
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
// TODO manage snapshots etc
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Transform } from './transform';
import { UUID } from 'mol-util';
import { Transform } from './transform';
/** A mutable state object */
export interface StateObject<P = any, D = any> {
export { StateObject, StateObjectBox }
interface StateObject<P = any, D = any, Type = { }> {
readonly id: UUID,
readonly type: StateObject.Type,
readonly props: P,
readonly data: D
}
export namespace StateObject {
export enum StateType {
// The object has been successfully created
Ok,
// An error occured during the creation of the object
Error,
// The object is queued to be created
Pending,
// The object is currently being created
Processing
}
namespace StateObject {
export interface Type<Info = any> {
info: Info
}
......@@ -40,7 +29,7 @@ export namespace StateObject {
export function create<Props, Data, TypeInfo>(typeInfo: TypeInfo) {
const dataType: Type<TypeInfo> = { info: typeInfo };
return class implements StateObject<Props, Data> {
return class implements StateObject<Props, Data, Type<TypeInfo>> {
static type = dataType;
static is(obj?: StateObject): obj is StateObject<Props, Data> { return !!obj && dataType === obj.type; }
id = UUID.create();
......@@ -48,13 +37,25 @@ export namespace StateObject {
constructor(public props: Props, public data: Data) { }
}
}
}
export interface Node {
interface StateObjectBox {
ref: Transform.Ref,
state: StateType,
props: unknown,
status: StateObjectBox.Status,
errorText?: string,
obj?: StateObject,
version: string
}
namespace StateObjectBox {
export type Status = 'ok' | 'error' | 'pending' | 'processing'
export interface Props {
isVisible: boolean,
isHidden: boolean,
isBound: boolean,
isExpanded: boolean
}
}
\ No newline at end of file
......@@ -4,13 +4,13 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StateObject } from './object';
import { StateObject, StateObjectBox } from './object';
import { State } from './state';
import { ImmutableTree } from './util/immutable-tree';
import { ImmutableTree } from './immutable-tree';
namespace StateSelection {
export type Selector = Query | Builder | string | StateObject.Node;
export type NodeSeq = StateObject.Node[]
export type Selector = Query | Builder | string | StateObjectBox;
export type NodeSeq = StateObjectBox[]
export type Query = (state: State) => NodeSeq;
export function select(s: Selector, state: State) {
......@@ -27,8 +27,8 @@ namespace StateSelection {
return query;
}
function isObj(arg: any): arg is StateObject.Node {
return (arg as StateObject.Node).version !== void 0;
function isObj(arg: any): arg is StateObjectBox {
return (arg as StateObjectBox).version !== void 0;
}
function isBuilder(arg: any): arg is Builder {
......@@ -40,13 +40,13 @@ namespace StateSelection {
}
export interface Builder {
flatMap(f: (n: StateObject.Node) => StateObject.Node[]): Builder;
mapEntity(f: (n: StateObject.Node) => StateObject.Node): Builder;
flatMap(f: (n: StateObjectBox) => StateObjectBox[]): Builder;
mapEntity(f: (n: StateObjectBox) => StateObjectBox): Builder;
unique(): Builder;
parent(): Builder;
first(): Builder;
filter(p: (n: StateObject.Node) => boolean): Builder;
filter(p: (n: StateObjectBox) => boolean): Builder;
subtree(): Builder;
children(): Builder;
ofType(t: StateObject.Type): Builder;
......@@ -68,7 +68,7 @@ namespace StateSelection {
export function byRef(...refs: string[]) {
return build(() => (state: State) => {
const ret: StateObject.Node[] = [];
const ret: StateObjectBox[] = [];
for (const ref of refs) {
const n = state.objects.get(ref);
if (!n) continue;
......@@ -78,13 +78,13 @@ namespace StateSelection {
});
}
export function byValue(...objects: StateObject.Node[]) { return build(() => (state: State) => objects); }
export function byValue(...objects: StateObjectBox[]) { return build(() => (state: State) => objects); }
registerModifier('flatMap', flatMap);
export function flatMap(b: Selector, f: (obj: StateObject.Node, state: State) => NodeSeq) {
export function flatMap(b: Selector, f: (obj: StateObjectBox, state: State) => NodeSeq) {
const q = compile(b);
return build(() => (state: State) => {
const ret: StateObject.Node[] = [];
const ret: StateObjectBox[] = [];
for (const n of q(state)) {
for (const m of f(n, state)) {
ret.push(m);
......@@ -95,10 +95,10 @@ namespace StateSelection {
}
registerModifier('mapEntity', mapEntity);
export function mapEntity(b: Selector, f: (n: StateObject.Node, state: State) => StateObject.Node | undefined) {
export function mapEntity(b: Selector, f: (n: StateObjectBox, state: State) => StateObjectBox | undefined) {
const q = compile(b);
return build(() => (state: State) => {
const ret: StateObject.Node[] = [];
const ret: StateObjectBox[] = [];
for (const n of q(state)) {
const x = f(n, state);
if (x) ret.push(x);
......@@ -112,7 +112,7 @@ namespace StateSelection {
const q = compile(b);
return build(() => (state: State) => {
const set = new Set<string>();
const ret: StateObject.Node[] = [];
const ret: StateObjectBox[] = [];
for (const n of q(state)) {
if (!set.has(n.ref)) {
set.add(n.ref);
......@@ -133,7 +133,7 @@ namespace StateSelection {
}
registerModifier('filter', filter);
export function filter(b: Selector, p: (n: StateObject.Node) => boolean) { return flatMap(b, n => p(n) ? [n] : []); }
export function filter(b: Selector, p: (n: StateObjectBox) => boolean) { return flatMap(b, n => p(n) ? [n] : []); }
registerModifier('subtree', subtree);
export function subtree(b: Selector) {
......@@ -147,7 +147,7 @@ namespace StateSelection {
registerModifier('children', children);
export function children(b: Selector) {
return flatMap(b, (n, s) => {
const nodes: StateObject.Node[] = [];
const nodes: StateObjectBox[] = [];
s.tree.nodes.get(n.ref)!.children.forEach(c => nodes.push(s.objects.get(c!)!));
return nodes;
});
......@@ -162,7 +162,7 @@ namespace StateSelection {
registerModifier('parent', parent);
export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.objects.get(s.tree.nodes.get(n.ref)!.parent))); }
function findAncestorOfType({ tree, objects }: State, root: string, type: StateObject.Type): StateObject.Node | undefined {
function findAncestorOfType({ tree, objects }: State, root: string, type: StateObject.Type): StateObjectBox | undefined {
let current = tree.nodes.get(root)!;
while (true) {
current = tree.nodes.get(current.parent)!;
......
......@@ -4,10 +4,10 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StateObject } from './object';
import { StateObject, StateObjectBox } from './object';
import { StateTree } from './tree';
import { Transform } from './transform';
import { ImmutableTree } from './util/immutable-tree';
import { ImmutableTree } from './immutable-tree';
import { Transformer } from './transformer';
import { StateContext } from './context';
import { UUID } from 'mol-util';
......@@ -87,7 +87,7 @@ class State {
this.objects.set(tree.rootRef, {
ref: tree.rootRef,
obj: rootObject,
state: StateObject.StateType.Ok,
status: 'ok',
version: root.version,
props: { ...defaultObjectProps }
});
......@@ -101,7 +101,7 @@ class State {
}
namespace State {
export type Objects = Map<Transform.Ref, StateObject.Node>
export type Objects = Map<Transform.Ref, StateObjectBox>
export interface Snapshot {
readonly tree: StateTree.Serialized,
......@@ -177,15 +177,15 @@ namespace State {
return deletes;
}
function setObjectState(ctx: UpdateContext, ref: Ref, state: StateObject.StateType, errorText?: string) {
function setObjectState(ctx: UpdateContext, ref: Ref, status: StateObjectBox.Status, errorText?: string) {
let changed = false;
if (ctx.objects.has(ref)) {
const obj = ctx.objects.get(ref)!;
changed = obj.state !== state;
obj.state = state;
changed = obj.status !== status;
obj.status = status;
obj.errorText = errorText;
} else {
const obj: StateObject.Node = { ref, state, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } };
const obj: StateObjectBox = { ref, status, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } };
ctx.objects.set(ref, obj);
changed = true;
}
......@@ -193,7 +193,7 @@ namespace State {
}
function _initVisitor(t: ImmutableTree.Node<Transform>, _: any, ctx: UpdateContext) {
setObjectState(ctx, t.ref, StateObject.StateType.Pending);
setObjectState(ctx, t.ref, 'pending');
}
/** Return "resolve set" */
function initObjectState(ctx: UpdateContext, roots: Ref[]) {
......@@ -203,7 +203,7 @@ namespace State {
}
function doError(ctx: UpdateContext, ref: Ref, errorText: string) {
setObjectState(ctx, ref, StateObject.StateType.Error, errorText);
setObjectState(ctx, ref, 'error', errorText);
const wrap = ctx.objects.get(ref)!;
if (wrap.obj) {
ctx.stateCtx.events.object.removed.next({ ref });
......@@ -232,11 +232,11 @@ namespace State {
}
async function updateSubtree(ctx: UpdateContext, root: Ref) {
setObjectState(ctx, root, StateObject.StateType.Processing);
setObjectState(ctx, root, 'processing');
try {
const update = await updateNode(ctx, root);
setObjectState(ctx, root, StateObject.StateType.Ok);
setObjectState(ctx, root, 'ok');
if (update.action === 'created') {
ctx.stateCtx.events.object.created.next({ ref: root, obj: update.obj! });
} else if (update.action === 'updated') {
......@@ -268,7 +268,7 @@ namespace State {
objects.set(currentRef, {
ref: currentRef,
obj,
state: StateObject.StateType.Ok,
status: 'ok',
version: transform.version,
props: { ...ctx.stateCtx.defaultObjectProps, ...transform.defaultProps }
});
......@@ -283,7 +283,7 @@ namespace State {
objects.set(currentRef, {
ref: currentRef,
obj,
state: StateObject.StateType.Ok,
status: 'ok',
version: transform.version,
props: { ...ctx.stateCtx.defaultObjectProps, ...current.props, ...transform.defaultProps }
});
......
......@@ -59,7 +59,7 @@ export namespace Transformer {
*/
update?(params: UpdateParams<A, B, P>, globalCtx: unknown): Task<UpdateResult> | UpdateResult,
params?: {
readonly params?: {
/** Check the parameters and return a list of errors if the are not valid. */
default?(a: A, globalCtx: unknown): P,
/** Specify default control descriptors for the parameters */
......@@ -68,7 +68,7 @@ export namespace Transformer {
validate?(a: A, params: P, globalCtx: unknown): string[] | undefined,
/** Optional custom parameter equality. Use deep structural equal by default. */
areEqual?(oldParams: P, newParams: P): boolean
}
},
/** Test if the transform can be applied to a given node */
isApplicable?(a: A, globalCtx: unknown): boolean,
......@@ -77,7 +77,7 @@ export namespace Transformer {
isSerializable?(params: P): { isSerializable: true } | { isSerializable: false; reason: string },
/** Custom conversion to and from JSON */
customSerialization?: { toJSON(params: P, obj?: B): any, fromJSON(data: any): P }
readonly customSerialization?: { toJSON(params: P, obj?: B): any, fromJSON(data: any): P }
}
const registry = new Map<Id, Transformer<any, any>>();
......
......@@ -5,7 +5,7 @@
*/
import { Transform } from './transform';
import { ImmutableTree } from './util/immutable-tree';
import { ImmutableTree } from './immutable-tree';
import { Transformer } from './transformer';
import { StateObject } from './object';
......
......@@ -68,8 +68,7 @@ function hookEvents(state: State) {
state.context.events.object.created.subscribe(e => console.log('created:', e.ref));
state.context.events.object.removed.subscribe(e => console.log('removed:', e.ref));
state.context.events.object.replaced.subscribe(e => console.log('replaced:', e.ref));
state.context.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref,
StateObject.StateType[state.objects.get(e.ref)!.state]));
state.context.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref, state.objects.get(e.ref)!.status));
state.context.events.object.updated.subscribe(e => console.log('updated:', e.ref));
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment