Home Manual Reference Source Repository

Getting Started

Getting Started

We always start by creating a RootLayout.

import { RootLayout } from 'ug-layout';

const root = RootLayout.create({
  container: document.body // This can be any HTMLElement
});

Not that we have a root layout all we need to do is configure the renderable tree. For configuring renderables refer to the configuration section of the docs.

import { RootLayout, Layout, Row, View } from 'ug-layout';

const root = RootLayout.create({
  container: document.body
});

root.load(
  RootLayout.configure({
    child: Layout.configure({
      child: Row
    })
  })
)

The above will create a layout with a single row. We haven't configured any renderables to occupy that row yet. This is where Views come into play. Views are Renderables that render a component that we create. We can use factories, classes, or already existing instances. Read the configuration for the {@link View} Renderable for more information.

Let's create a basic component we want to render.

export class MyComponent {
  // This hook gets fired when the component is initialized.
  ugOnInit(container: ViewContainer): void {
    container.mountHTML('<div>Hello World!</div>');
  }
}

The ViewContainer is an object that contains the component instance. It's the link between your component and the layout framework. This seperation allows for the framework to be transparent to the component. You only need to expose hooks on your component.

So no that our component is created lets hook it up.

import { RootLayout, Layout, Row, View } from 'ug-layout';

import { MyComponent } from './MyComponent';

const root = RootLayout.create({
  container: document.body
});

root.load(
  RootLayout.configure({
    child: Layout.configure({
      child: Row.configure({
        children: [{
          use: View.configure({
            useClass: MyComponent
          })
        }, {
          use: View.configure({
            useClass: MyComponent
          })
        }]
      })
    })
  })
)

So, from this example we have a layout with a row that will render two instances of MyComponet. This all the configuration needed. This may seem a little verbose at first but this allows for any number of combinations of Renderables. Since this framework is completely extensible we can create adapters into any frameworks rendering and component engine. For example ug-layout-angular will render Angular 2 and Angular 1 components.

Views

Views

Views are Renderables that know how to render a specific component. This is where we can integrate it into different frameworks. Let's use the following as our base example. I would read the Getting Started guide before diving in here.

Component

import { ViewComponent } from 'ug-layout';

@ViewComponent()
export class MyComponent {
  ugOnInit(container: ViewContainer): void {
    container.mountHTML('<div>Hello World!</div>');
  }
}

Layout

import { RootLayout, Layout, Row, View } from 'ug-layout';

import { MyComponent } from './MyComponent';

const root = RootLayout.create({
  container: document.body
});

root.load(
  RootLayout.configure({
    child: Layout.configure({
      child: Row.configure({
        children: [{
          use: View.configure({
            useClass: MyComponent
          })
        }]
      })
    })
  })
)

View Container

The view container is a special object that manages how the component integrates with the view. It invokes hooks on the component, contains notification observables, and APIs for interacting with the layout framework. In the above example we are setting the content of the view with our components template. The view container also manages when the component is 'ready' allowing for components to resolve asynchronously.

Async View Resolution

Views can have two async resolution phases. We can use a factory that returns a promise or we can use the ugOnResolve hook and return a promise.

Factory

View.configure({
  useFactory: async () => {
    const data = await service.fetchData();

    return myViewFactory(data); // Returns our component instance.
  }
});

ugOnResolve Hook

@ViewComponent()
class MyComponent {
  async ugOnResolve(): Promise<void> {
    this.data = await this.getData();    
  }

  ugOnInit(container: ViewContainer): void {
    // This won't be invoked until the data is fetched.
    container.mountHTML('<div>Hello World!</div>');
  }
}

View.configure({
  useClass: MyComponent
});

Component Lifecycle Hooks

These are hooks that get invoked during different points in the lifecycle of the view.

  • ugOnResolve(container: ViewContainer) -> Invoked when the component is created, but before the ugOnInit hook. Can resolve async operations.
  • ugOnInit(container: ViewContainer) -> Invoked when the component is ready and resolved.
  • ugOnAttach(container: ViewContainer) -> Invoked when the ViewContainer is attached to it's View Renderable.
  • ugOnDetach() -> Invoked when the ViewContainer is detached from it's View Renderable. This is useful for component caching.
  • ugOnResize(dimensions: { width: number, height: number }) -> Invoked when the dimensions of the container change.
  • ugOnVisibilityChange(isVisible: boolean) -> Invoked whenever this view becomes visible or hidden.
  • ugOnBeforeDestroy(event: BeforeDestroyEvent) -> Invoked whenever the view is about to be destroyed. The event for this hook is cancellable or can wait on an async operation. This is useful for confirming before closing or halting the closing all together. Note, this is only fired during some destroy events (ex: Tab closing), not every time the view is destroyed.

View Component

The ViewComponent decorator is a special decorator used to define metadata for the view component. Many of the options for a view can be configured when configuring the view renderable as well.

@ViewComponent({
  lazy: true,
  cacheable: true
})
export class MyComponent {}

// These can be overriden upon each use of the component.

View.configure({
  useClass: MyComponent,
  lazy: false,
  cacheable: false
});

View Configuration

A view config has the following properties:

  • lazy: boolean -> The view container won't be initialized until the view is visible for the first time. This also won't create the component until it is first shown or intialize is explicitly invoked on the ViewContainer.
  • cacheable: boolean -> Whether the view container and it's component should NOT be destroyed when the View renderable is destroyed. This means you can use the same component instance across different layout configurations.
  • resolution: ResolverStrategy -> How the view should be resolved with the ViewManager. See the section on Resolver Strategies.
  • token: any -> The token to use to store this view with the {@link ViewManager}. If using useClass the constructor will be used as the token. If using a factory a token must be provided. This token is used to resolve configuration for the component as well.
  • useFactory: Function -> Use a factory to generate the component.
  • deps: any[] -> Depenedencies to inject into the factory function. Only applies when using useFactory option.
  • useClass: Type<any> -> A class that will be instantiated as the component.
  • useValue: any -> An already create component instance.
  • ref: string -> A reference identifier. This is used to identify specific component references programmatically.

Resolver Strategies

We can determine how a view gets resolved when it is created or asked for.

  • ResolverStrategy.TRANSIENT -> Each time a view is requested to create a component, it will always create a new one.
  • ResolverStrategy.SINGLETON -> Each time a view is requested to create a component, it will either return an existing instance of that component or create one that will be the single instance for that component.
  • ResolverStrategy.REF -> Each time a view is requested to create a component, it will either return an existing instance of that component that matches the same ref identifier given or create one that will be registered under the given ref. Note, ref must be provided or else an error will be thrown.

Custom View Renderables

A {@link View} can be extended like any {@link Renderable} and can be integrated into any framework. Checkout the ug-layout-angular package for a View that renders Angular 1 and 2 components.

Serialization

Serialization

ugLayout supports serializing a layout's state as JSON that can be stored and read back into a layout. Usually each Renderable has a Serializer associated with it. Let's dive in to serialization basic usage.

Usage

We need to create a SerializerContainer from our root layout. We can use the RootSerializer class which is a SerializerContainer bootstrapped with all the built in Renderables registered.

const rootLayout = RootLayout.create({ container: document.body });

class MyComponent {}

rootLayout.load(
  RootLayout.configure({
    child: Layout.configure({
      child: View.configure({ useClass: MyComponent })
    })
  })
);

Let's assume we have the root layout instance above. Now we can create out serializer.

import { RootSerializer } from 'ug-layout';

const serializer = RootSerializer.fromRoot(rootLayout);
const layoutJSON = serializer.serialize(rootLayout);

Not we can store this JSON into LocalStorage or any other storage. Now we'll load the root layout from the JSON.

rootLayout.load(serializer.deserialize(layoutJSON));

The deserialize method returns a ConfiguredRenderable which is the same as calling RootLayout.configure().

Creating Serializers

Each Renderable is not essential a 1 to 1 relation ship but in a lot of cases it is. For example the Layout Renderable has a LayoutSerializer which knows how to serialize and deserialize a Layout Renderable. A Stack has a single StackSerializer, but it also serializes and deserializes StackHeader, StackTab, and StackItemContainer since they are only applicable to Stacks.

A serializer need to implement the Serializer interface. Here is an example serializer.

import {
  Serializer,
  Serialized,
  ConfiguredRenderable,
  SerializerContainer
} from 'ug-layout';

import { MyRenderable } from './MyRenderable';

export interface SerializedMyRenderable extends Serialized {
  someMessage: string;
}

export class MySerializer implements Serializer<MyRenderable, SerializeMyRenderable> {
  serialize(renderable: MyRenderable): SerializedMyRenderable {
    return {
      name: 'MyRenderable',
      someMessage: renderable.someMessage
    };
  }

  deserialize(serialized: SerializedMyRenderable): ConfiguredRenderable<MyRenderable> {
    return new ConfiguiredRenderable(MyRenderable, {
      someMessage: serialized.someMessage
    });
  }

  static register(container: SerializerContainer): void {
    container.registerClass('MyRenderable', MyRenderable);
  }
}

Builtin Renderables

Builtin Renderables

Here is a list of builtin renderables.

Row

A row renders a list of Renderables horizontally.

Column

A column renders a list of Renderables vertically.

Stack

A stack renders a list of Renderables as a tabbable interface. If the tab is configured with headers the tabs can be dropped onto other stacks.

Layout

A layout is a grouping that contains Renderables underneath it. Visually it does nothing, but it contains it's own DragHost which restricts items from being dragged outside it's bounds. If you want keep a Stack item from being dragged out side a set of Renderables then you would use a Layout to contain it.

RootLayout

This is the root layout that is created and bootstraps all dependencies for child renderables.

View

Renders components and interfaces with a ViewContainer.

Configuring Renderables

Configuring Renderables

ugLayout uses the ConfiguredRenderable class as a way to associate configuration between a Renderable and it's configuration. Most built in renderables contain static method named configure. This method returns a ConfiguredRenderable instance that contains the provided config and Renderable class.

Layout.configure({
  child: Row
});

// Is equal to

new ConfiguredRenderable(Layout, {
  child: Row
})

Anywhere something requires a RenderableArg You can provide a Renderable, a ConfiguredRenderable or an instance of a Renderable.

Resolving Renderables and Configuration

If you are creating Renderables from a RenderableArg you can use the resolve and resolveConfiguration static methods on the ConfiguredRenderable class to resolve them.

const layoutConfig = Layout.configure({ child: Row });
const layoutInstance = new Layout();

ConfiguredRenderable.resolve(layoutConfig); // => Layout
ConfiguredRenderable.resolve(Layout); // => Layout
ConfiguredRenderable.resolve(layoutInstance); // => Layout

ConfiguredRenderable.resolveConfiguration(layoutConfig); // => { child: Row }

Creating Renderables

Creating Renderables

References

References

Class Summary

Static Public Class Summary
public

Event fired before an item is destroyed.

public

An event that can be dispatched from the event bus.

public

An class that is thrown when a Cancellable is cancelled.

public

An event that can be cancelled.

public
public

A control that triggers a tab to close.

public
public
public

A container that holds a Renderable class and a configuration to use when it is instanatiated.

public

Invokes a custom view hook on any views that receive this event.

public
public
public
public
public

Custom eventing bus that abstracts common patterns between events.

public

An instance of a forward ref function.

public

A serializer that can be used for renderables that don't container any special logic or configuration.

public

A dependency injector for resolving dependencies.

public
public

Serializes/deserializes a layout renderable.

public
public
public
public
public
public abstract

The base renderable that all other renderables extend from.

public

A class that defines an area in pixels.

public

An injector created for renderables.

public

Renders the layout.

public
public
public
public
public

Row

public
public

A container for running and determining serializers.

public
public

A Renderable that renders the Renderable items in a tabbable format.

public

Base stack control that all stack controls extend.

public
public
public
public

A renderable that is a direct child of a stack.

public
public

Renderable representing a stack tab.

public
public

Fired when a tab is closed.

public
public

Fired when a tab is dragged.

public

Event fired when a tab is selected.

public
public

A token used for dependency injection.

public
public

A renderable that renders a component.

public

A container that holds a 1 to 1 relationship with a component instance.

public

Responsible for creating view containers from view configurations.

public

Executes view hooks on a component instance.

public

This service wires up a class instance with custom behavior for dealing with communication between multiple views.

public
public
public
public
public
public

Interface Summary

Static Public Interface Summary
public
public

An event interface that is emitted from a Draggable.

public

An interface that signifies the renderable can handle drops.

public
public
public
public
public

Function Summary

Static Public Function Summary
public

Inject(token: *): ParameterDecorator

Marks a param to be injected with the registered provider registered with the token.

public

Injectable(config: InjectableConfigArgs): ClassDecorator

Marks a dependency as injectable.

public

Lazy(): ParameterDecorator

Marks a dependency as lazy.

public

MemoizeFrom(properties: ...string[]): MethodDecorator

A decorator that memoizes a functions computation based on the current and previous value of other parameters.

public

ObserveViewHook(hook: string): PropertyDecorator

Creates an Observable that emits when the provided hook is invoked.

public

Optional(): ParameterDecorator

Marks a dependency as optional.

public

Self(): ParameterDecorator

Will only check the injector creating the dependency.

public

ViewComponent(config: ViewComponentConfigArgs): ClassDecorator

Registers view metadata with a component class.

public

ViewInsert(config: ViewInsertConfigArgs): PropertyDecorator

Sets up a view insert method.

public

ViewLinkInit(injections: ...any[]): MethodDecorator

Sets up an init method that is invoked at the time a view is linked by the ViewLinker.

public

ViewQuery(config: ViewQueryConfigArgs): MethodDecorator

Sets up a view query with the decorated method as the callback to receive the query results.

public

ViewResolve(config: ViewResolveConfigArgs): PropertyDecorator

Sets up a view resolve method.

public

clamp(val: *, min: *, max: *): *

public

defaults(dest: *, src: *): *

public

eq(value: *): *

public

A factory that creates a ForwardRef.

public

get(obj: *, path: *, defaultValue: *, comparer: *): *

public

getDefaultMetadata(): {"queries": *, "inits": *, "inserts": *, "resolves": *}

public

isBoolean(val: *): *

public

isFunction(val: *): *

public

isNumber(val: *): *

public

isObject(val: *): *

public

isObserver(val: *): *

public

isPromise(value: *): *

public

isString(val: *): *

public

isUndefined(val: *): *

public

negate(fn: *): *

public

partition(list: *, predicate: *): undefined[]

public

propEq(prop: *, value: *): *

public

round(val: *, precision: number): *

Variable Summary

Static Public Variable Summary
public

Injection token for injecting the renderables configuration.

public

Injection token for injecting the container renderable.

public

ContextType: {"NONE": string, "LOAD": string}

public
public

Injection token for the Document API.

public

Injection token for injecting an HTML element associated with the renderable.

public
public

Key used to assign the injector to on Renderables.

public
public
public
public

Layout: *

A layout is a set of renderables scoped to a drag host.

public

LinkerMetatdataRef: docs/di.js~Token

public
public

Injection token for injecting the patch method generated by snabbdom.

public

Injection token for the root configuration used to create a RootLayout

public
public

Symbol that signifies that an XYItemContainer's ratio is unallocated.

public
public
public
public

ViewComponentRef: docs/di.js~Token

public

uid: *

External Summary

Static Public External Summary
public
public
public
public
public
public