golden-layout

A multi-screen javascript Layout manager

Usage no npm install needed!

<script type="module">
  import goldenLayout from 'https://cdn.skypack.dev/golden-layout';
</script>

README

Golden Layout

NPM version License

Table of Contents

Features

  • Native popup windows
  • Full touch support
  • Virtual components
  • Comprehensive API
  • Powerful persistence
  • Completely themeable
  • Works in modern browsers (Firefox, Chrome)
  • Reponsive design

Installation / Usage

Library

Golden Layout is shipped via NPM. Use the following commands to install it into an application package:
npm i golden-layout

Source

The source can be installed by cloning the repository at:
https://github.com/golden-layout/golden-layout

To build the distribution locally, open a shell at the golden-layout directory/folder and run the following commands:

  1. npm install or npm ci (recommended) to install required dependencies
  2. npm run build to generate the distribution (dist subfolder). This script will:
    • delete the existing lib and dist folders
    • compile the TypeScript code
    • generate the rolled up TypeScript definition files (index.d.ts and golden-layout-untrimmed.d.ts)
    • generate source map
    • copy the style files to the dist folder

Note that the lib subfolder only holds the TypeScript declaration files generated by the compiler. Generally this subfolder can be ignored. It is used during the build process to generate the rolled up TypeScript definition files.

Build and run demo/test app

After installing the source and building the distribution, you can build and start the apitest (demo) app to view the library in action. Use the following commands:

  • npm run apitest:build to just build it
  • npm run apitest:serve to both build and start the development server.
    You can then view it in your browser using the following link:
    http://localhost:3000/

Building single-file bundles

We provide different types of single file bundles for easier consumption without toolchain in-place. To do this, run npm run build:bundles, afterwards find your bundled files in dist/bundle/. Bundles are not built by default and are not included in the NPM package, we recommend everyone to consume the library through NPM and webpack.

Debugging Golden Layout library

The apitest app can be used to debug the Golden Layout library. Its webpack configuration will import the Golden Layout library source map, allowing debuggers to step through the library source code and place break points.

If you wish to test the library with other applications, you can link to the Golden Layout repository without having to install it into the application from NPM. This is done with the npm link command. Use the following steps:

  1. Run the npm link from a shell in the golden-layout source repository top level folder.
  2. Run npm link golden-layout from a shell in your application's top level folder.

Your application will then use the distribution in the Golden Layout repository dist subfolder. If you wish to make changes to the Golden Layout library, you will need to run the build:api to regenerate the dist folder.

Run npm install to remove the npm link.

Code Examples

Angular

An example Angular application using Golden Layout is available. The source can be installed by cloning the repository:
https://github.com/golden-layout/golden-layout-ng-app

After installing the source, the app can be built and started with the standard build and start scripts.

Vue

The following snippets of code demonstrate how Golden Layout can be used in Vue.

These snippets use embedding via events binding. It may be preferable to use virtual via events binding (ie Virtual Components). A Pull Request from a Vue user demonstrating using Vue with virtual components would be appreciated.

Composable Hook

import { GoldenLayout, LayoutConfig } from 'golden-layout';
import { onMounted, ref, shallowRef } from 'vue';

export const isClient = typeof window !== 'undefined';
export const isDocumentReady = () => isClient && document.readyState === 'complete' && document.body != null;

export function useDocumentReady(func: () => void) {
    onMounted(() => {
        console.log(isDocumentReady());
        if (isDocumentReady()) func();
        else
            document.addEventListener('readystatechange', () => isDocumentReady() && func(), {
                passive: true,
            });
    });
}

export function useGoldenLayout(
    createComponent: (type: string, container: HTMLElement) => ComponentContainer.Component,
    destroyComponent: (container: HTMLElement) => void,
    config?: LayoutConfig
) {
    const element = shallowRef<HTMLElement | null>(null);
    const layout = shallowRef<GoldenLayout | null>(null);
    const initialized = ref(false);

    useDocumentReady(() => {
        if (element.value == null) throw new Error('Element must be set.');
        const goldenLayout = new GoldenLayout(element.value);

        goldenLayout.bindComponentEvent = (container, itemConfig) => {
            const { componentType } = itemConfig;
            if (typeof componentType !== 'string') throw new Error('Invalid component type.');
            const component = createComponent(componentType, container.element);
            return {
                component,
                virtual: false,
            }
        }
        goldenLayout.unbindComponentEvent = container => {
            destroyComponent(container.element);
        }

        if (config != null) goldenLayout.loadLayout(config);

        // https://github.com/microsoft/TypeScript/issues/34933
        layout.value = goldenLayout as any;

        initialized.value = true;
    });

    return { element, initialized, layout };
}

Usage

<template>
  <div ref="element" style="width: 100%; height: 75vh">
    <teleport
      v-for="{ id, type, element } in componentInstances"
      :key="id"
      :to="element"
    >
      <component :is="type"></component>
    </teleport>
  </div>
</template>
<script lang="ts">
import { useGoldenLayout } from "@/use-golden-layout";
import { defineComponent, h, shallowRef } from "vue";
import "golden-layout/dist/css/goldenlayout-base.css";
import "golden-layout/dist/css/themes/goldenlayout-dark-theme.css";

const Test = defineComponent({ render: () => h('span', 'It works!') });

const components = { Test, /* other components */ };

export default defineComponent({
  components,
  setup() {
    interface ComponentInstance {
      id: number;
      type: string;
      element: HTMLElement;
    }
    let instanceId = 0;
    const componentTypes = new Set(Object.keys(components));
    const componentInstances = shallowRef<ComponentInstance[]>([]);

    const createComponent = (type: string, element: HTMLElement) => {
      if (!componentTypes.has(type)) {
        throw new Error(`Component not found: '${type}'`);
      }
      ++instanceId;
      componentInstances.value = componentInstances.value.concat({
        id: instanceId,
        type,
        element,
      });
    };
    const destroyComponent = (toBeRemoved: HTMLElement) => {
      componentInstances.value = componentInstances.value.filter(
        ({ element }) => element !== toBeRemoved
      );
    };

    const { element } = useGoldenLayout(createComponent, destroyComponent, {
      root: {
        type: "column",
        content: [
          {
            type: "component",
            componentType: "Test",
          },
          {
            type: "component",
            componentType: "Test",
          },
        ],
      },
    });

    return { element, componentInstances };
  },
});
</script>

Other Frameworks

Other frameworks can use use the bindComponentEvent and unbindComponentEvent events to manage components bindings with Golden Layout. The VirtualLayout.bindComponentEvent event will be fired whenever a component is needed. The VirtualLayout.unbindComponentEvent event will be fired when a component is no longer needed and gives the application a chance to tear-down the component.

These events can be set up in an application's start up code as shown in the example code below.

    this._goldenLayout = new GoldenLayout(goldenLayoutHostElement);

    this._goldenLayout.bindComponentEvent = (container, itemConfig) => {
        const component = this.createFrameworkComponent(itemConfig);
        // component.rootHtmlElement is the top most HTML element in the component
        container.element.appendChild(component.rootHtmlElement);
        this._containerMap.set(container, component);
        return {
            component,
            virtual: false,
        }
    }

    this._goldenLayout.unbindComponentEvent = (container, component) => {
        const component = this._containerMap.get(container);
        container.element.removeChild(component.rootHtmlElement);
        this._containerMap.delete(container);
        this.disposeFrameworkComponent(component);
    }

Once the VirtualLayout.bindComponentEvent and VirtualLayout.unbindComponentEvent events have been set up, functions that create components can be used. For example:

  • LayoutManager.loadLayout()
  • LayoutManager.addComponent()

The above example uses 'embedding via events' binding. It is also possible to use these events with 'virtual via events' binding. For more information on binding, see the Binding Components section below.

Notes

Using Popouts

Popouts are supported, although the scope is more limited than in the original v1. Popouts are enabled by default for all content items. Popouts are disabled by either setting { popout: false } in the header configuration or when a component is not closable. Also, as a popout user, make sure to register all component types before initializing the golden-layout instance in your child windows.

Popout examples are available in the standard and tabDropdown layouts within the apitest application.

EventHub can be used to broadcast messages and events to all windows. The LayoutManager.eventHub.emitUserBroadcast() function is used to broadcast messages. Messages can be received by listening to “userBroadcast” events. For example:

layoutManager.eventHub.on('userBroadcast',  (...ev: EventEmitter.UnknownParams) => {
  // respond to user broadcast event
});

See event-component.ts in apitest for a complete example of broadcasting user messages.

Limitations

  • The EventHub is restricted to userBroadcast events, other event types will not be broadcasted between windows.
  • This means the you have to take care of propagating state changes between windows yourself.

Binding Components

Golden Layout binds to components and then controls their position, size and visibility (positioning) so that they fit within a layout. There are 4 ways Golden Layout can bind to a component:

  1. Embedding via Registration (classic)
    A component's constructor/factory is registered with Golden Layout. Golden Layout will instantiate an instance when required. The constructor/factory will add the component's root HTML element within Golden Layout's own DOM hierarchy sub-tree. This is the classic Golden Layout binding method. With this binding method an ancestor of the component's root HTML element could be reparented if the layout changes.
  2. Embedding via Events
    Components are obtained on demand by events. An event handler will construct or otherwise fetch the component and return it. The event handler will also add the component's root HTML element within Golden Layout's own DOM hierarchy sub-tree. This is the binding method introduced in version 2.
  3. Virtual via Registration
    A component's constructor/factory is registered with Golden Layout. Golden Layout will instantiate an instance when required. The component will use the same positioning as virtual components however Golden Layout will handle all the events internally.
  4. Virtual via Events (Virtual Components)
    With virtual components, Golden Layout never interacts directly with components. The application controls the construction/allocation, destruction/deallocation and positioning of components. Golden Layout will advise the application when components are needed and no longer needed via events. It will also advise about components' positioning via events. This allows an application to control the placement of components in the DOM hierarchy and components' root HTML element's ancestors are not reparented when the layout is changed.

Embedding via Registration

Registering a component and specifying static positioning is the classic GoldenLayout approach to binding components. The components are registered with GoldenLayout and specify a constructor or callback function used to create a component whenever a new instance is needed in the layout. When the constructor or callback is invoked, it is passed a container object which includes a HTML element. The constructor or callback will create the object and make its top level HTML element a child of the container's HTML element. The component is then part of the Golden Layout's DOM hierarchy. Whenever the layout is re-arranged, the GoldenLayout DOM is adjusted to reflect the new layout hierarchy. Effectively this involves the ancestors of components' root HTML elements being reparented when a layout is changed.

The following functions can be used to register components.

  • GoldenLayout.registerComponent()
  • GoldenLayout.registerComponentConstructor()
  • GoldenLayout.registerComponentFactoryFunction()
  • GoldenLayout.registerComponentFunction()
  • GoldenLayout.registerGetComponentConstructorCallback()

Embedding via Events

To give applications more control over the allocation of components, you can bind components with events instead of registration. If a handler is assigned to the event VirtualLayout.bindComponentEvent it will be fired whenever a new component is needed. The handler should:

  • create or fetch the component,
  • make sure its top level HTML elements are made children of container.element,
  • return the component inside a BindableComponent interface with virtual: false.

When a component is removed from Golden Layout it will be necessary to remove the component's top level HTML elements as children of container.element. Other component 'tear-down' actions may also be required. These actions can be carried out in either the VirtualLayout.unbindComponentEvent event or the component container's beforeComponentRelease event (or both). Both these events will be fired (if handlers are assigned) when a component is no longer needed in Golden Layout.

Virtual via Events

With virtual components, Golden Layout knows nothing about components and does not include the component's HTML elements in its own DOM hierarchy. Instead, whenever a component needs its position, size or visibility changed, Golden Layout will fire events which allow the application to change a component's position, size or visibility. This is analogous to virtual grids where strings and other content to be displayed in a grid, are not included within the grid. Instead the grid fires events whenever it needs to display content. The application will return the required content.

Virtual Components has the following advantages:

  • Components and their ancestors are not reparented when a layout is changed. This avoids breaking iframe, sockets, etc.
  • It is no longer necessary to extract the top level HTML element from a component.
  • Applications using frameworks with their own component hierarchy, such as Angular and Vue, no longer have to break their component hierarchy to insert Golden Layout. The framework's methodology for handling parent/child relationships can be maintained even with the components which Golden Layout is positioning. (Teleporting component's HTML elements is no longer necessary)
  • Applications typically bind a component's top level HTML element to the Golden Layout root element. Debugging becomes easier as the DOM hierarchy relevant to your application is a lot shallower.

With Virtual Components the following events need to be handled:

  • VirtualLayout.bindComponentEvent: (container, itemConfig) => ComponentContainer.BindableComponent
    Fired whenever a GoldenLayout wants to bind to a new component. The handler is passed the container and the item's resolved config. Typically, the handler would:

    • create or fetch the component using itemConfig,
    • get the the component's top level HTML component,
    • ensure this element has absolute position,
    • make the element a child of Golden Layout's root HTML element,
    • store the component in a map using container as the key,
    • add handlers to the container's virtualRectingRequiredEvent and virtualVisibilityChangeRequiredEvent events,
    • return the component in an BindableComponent interface with virtual: true.

    Example:

    private handleBindComponentEvent(container: ComponentContainer, itemConfig: ResolvedComponentItemConfig) {
        // Use ResolvedComponentItemConfig.resolveComponentTypeNamecan to resolve component types to a unique name
        const componentTypeName = ResolvedComponentItemConfig.resolveComponentTypeName(itemConfig);
        if (componentTypeName === undefined) {
            throw new Error('handleBindComponentEvent: Undefined componentTypeName');
        }
        const component = this.createVirtualComponent(container, componentTypeName, itemConfig.componentState);
        const componentRootElement = component.rootHtmlElement;
        this._layoutElement.appendChild(componentRootElement);
        this._boundComponentMap.set(container, component);
        container.virtualRectingRequiredEvent = (container, width, height) => this.handleContainerVirtualRectingRequiredEvent(container, width, height);
        container.virtualVisibilityChangeRequiredEvent = (container, visible) => this.handleContainerVisibilityChangeRequiredEvent(container, visible);
        return {
            component,
            virtual: true,
        };
    }
    
  • VirtualLayout.unbindComponentEvent: (container) => void
    Fired when a component is removed from Golden Layout. The handler is passed the container. Typically, the handler would:

    • find the component in the map using container as the key,
    • remove it as a child from Golden Layout's root HTML element,
    • remove it from the map.

    Example:

    private handleUnbindComponentEvent(container: ComponentContainer) {
        const component = this._boundComponentMap.get(container);
        if (component === undefined) {
            throw new Error('handleUnbindComponentEvent: Component not found');
        }
    
        const componentRootElement = component.rootHtmlElement;
        if (componentRootElement === undefined) {
            throw new Error('handleUnbindComponentEvent: Component does not have a root HTML element');
        }
    
        this._layoutElement.removeChild(componentRootElement);
        this._boundComponentMap.delete(container);
    }
    
  • LayoutManager.beforeVirtualRectingEvent: () => void
    This event does not need to be handled. However it can be used to optimise positioning of components. Whenever a layout is changed, it may be that several components need to be repositioned. This event will be fired whenever one or more components need to be positioned as the result of one layout change. Typically it is used to get the position of Golden Layout's root HTML element, using getBoundingClientRect(). This can then be cached for use when each component's position needs to be calculated.

    Example:

    private handleBeforeVirtualRectingEvent(count: number) {
        this._goldenLayoutBoundingClientRect = this._layoutElement.getBoundingClientRect();
    }
    
  • ComponentContainer.virtualRectingRequiredEvent: (container, width, height) => void;
    Fired when a component's position and/or size need to be changed. The handler is passed the container and the component's required width and height. Typically, the handler would:

    • find the component in the map using container as the key,
    • get the Golden Layout's root HTML element's position using getBoundingClientRect(), (Alternatively, it can used the position calculated by the handler for the virtualRectingRequiredEvent event.)
    • get the container's position using getBoundingClientRect(),
    • calculate the container's position relative to Golden Layout's root HTML element position.
    • accordingly, update the following properties in the component's top level HTML element:
      • left
      • top
      • width
      • height

    Example:

    private handleContainerVirtualRectingRequiredEvent(container: ComponentContainer, width: number, height: number) {
        const component = this._boundComponentMap.get(container);
        if (component === undefined) {
            throw new Error('handleContainerVirtualRectingRequiredEvent: Component not found');
        }
    
        const rootElement = component.rootHtmlElement;
        if (rootElement === undefined) {
            throw new Error('handleContainerVirtualRectingRequiredEvent: Component does not have a root HTML element');
        }
    
        const containerBoundingClientRect = container.element.getBoundingClientRect();
        const left = containerBoundingClientRect.left - this._goldenLayoutBoundingClientRect.left;
        rootElement.style.left = this.numberToPixels(left);
        const top = containerBoundingClientRect.top - this._goldenLayoutBoundingClientRect.top;
        rootElement.style.top = this.numberToPixels(top);
        rootElement.style.width = this.numberToPixels(width);
        rootElement.style.height = this.numberToPixels(height);
    }
    
  • ComponentContainer.virtualVisibilityChangeRequiredEvent: (container, visible) => void;
    Fired when a component's visibility needs to be changed. The handler is passed the container and a boolean specifying visibility. Typically, the handler would:

    • find the component in the map using container as the key,
    • change its visibility using the display property in the component's top level HTML element.

    Example:

    private handleContainerVisibilityChangeRequiredEvent(container: ComponentContainer, visible: boolean) {
        const component = this._boundComponentMap.get(container);
        if (component === undefined) {
            throw new Error('handleContainerVisibilityChangeRequiredEvent: Component not found');
        }
    
        const componentRootElement = component.rootHtmlElement;
        if (componentRootElement === undefined) {
            throw new Error('handleContainerVisibilityChangeRequiredEvent: Component does not have a root HTML element');
        }
    
        if (visible) {
            componentRootElement.style.display = '';
        } else {
            componentRootElement.style.display = 'none';
        }
    }
    
  • ComponentContainer.virtualZIndexChangeRequiredEvent: (container, logicalZIndex, defaultZIndex) => void
    Fired when a component's z-index needs to be changed. The handler is passed the container and a logical z-index and the default style z-index. Typically, the handler would:

    • find the component in the map using container as the key,
    • change its z-index to the default style z-index specified in defaultZIndex.

    Example:

    private handleContainerVirtualZIndexChangeRequiredEvent(container: ComponentContainer, logicalZIndex: LogicalZIndex, defaultZIndex: string) {
        const component = this._boundComponentMap.get(container);
        if (component === undefined) {
            throw new Error('handleContainerVirtualZIndexChangeRequiredEvent: Component not found');
        }
    
        const componentRootElement = component.rootHtmlElement;
        if (componentRootElement === undefined) {
            throw new Error('handleContainerVirtualZIndexChangeRequiredEvent: Component does not have a root HTML element');
        }
    
        componentRootElement.style.zIndex = defaultZIndex;
    }
    

The apitest application demonstrates how virtual components are implemented.

When using virtual components, think of Golden Layout as more of an engine calculating position rather than actually positioning components. This binding method requires more work to set up than other binding methods. However it offers more flexibility and opens up more design opportunities. For example with virtual components, any HTML element could be the parent for your components (not just the Golden Layout container). You can even have different parents for different components. This allows, for example, some of your components have one parent, and the others a different parent. They could then inherit different CSS or handle event propagation differently.

Virtual via Registration

These events give applications a lot of flexibility with positioning components in Golden Layout - but at the expense of more effort of integrating into Golden Layout. It is however, possible to get the same benefits of Virtual Components with just registering a component. In this case, a component will be registered as in classic approach to Golden Layout binding, however, within Golden Layout, the component will be handled like a virtual component. Golden Layout will internally handle the necessary events.

Existing applications using register functions in Golden Layout can easily be updated to use virtual binding by:

  1. The register functions have a new parameter virtual. By default, this is false, specifying the classic binding in Golden Layout. Set this to true to specify that components of that type should be implemented internally as virtual components.
  2. Components need to have a getter: rootHtmlElement which returns the component's root HTML element. Components written in TypeScript should implement the GoldenLayout.VirtuableComponent interface.
  3. Components' rootHtmlElement element need to have its overflow CSS property set to hidden.
  4. Ensure that the Golden Layout container HTML element is positioned (ie. its position property is not static).

With these changes, applications can continue to use Golden Layout as they are now however Golden Layout will internally use virtual component binding.

Please note there will be a couple of minor behaviour changes:

  • Golden Layout will ensure a component's root HTML element has position type absolute.
  • Golden Layout will modify the height and width of the root HTML element. In embedding bindings, Golden Layout modified the height and width of the container element - not the component's root HTML Element. If your application also sets the height or width of a components root HTML element, you will need to modify your design. This can easily be done by giving the current root HTML element a new parent element and making this parent the new root HTML element for the component. Your component logic can continue to use the existing element while Golden Layout uses the new root HTML element.
  • Golden Layout will modify the z-index of the component's root HTML element.

Also note that 'virtual via registration' binding is not supported by the GoldenLayout.registerGetComponentConstructorCallback() registration function.

Multiple binding methods

An application can use multiple methods of binding components for different component types. Whenever a component needs to be bound, Golden Layout will try to bind in the following order:

  1. First check if its type has been registered. If so, it will bind using that registration.
  2. Check whether there is a bindComponentEvent handler. If so, this event will be used to bind it as a virtual component.
  3. Check whether there is a getComponentEvent handler. If so, this event will be used to bind the component statically within the Golden Layout DOM. This method is deprecated.
  4. If none of the above, then an exception will be raised.

If you use both 'Virtual via Events' and 'Embedding via Events', then the unbindComponentEvent handler can use the ComponentContainer.virtual field to determine which of these binding methods was used for a component.

VirtualLayout class

The inheritance hierarchy for the Golden Layout class is: LayoutManager -> VirtualLayout -> GoldenLayout.

The VirtualLayout class implements all the Golden Layout functionality except for the register functions. If you only intend to use virtual components using the bindComponentEvent, you can create an instance of VirtualLayout instead of GoldenLayout.

Usage Scenarios

  • Quick and easy
    Use 'Embedding via Registration'. The classic way of using Golden Layout.
  • Backwards compatibility
    If your existing application uses the Golden Layout registration functions, then it will automatically use 'Embedding via Registration' without any changes.
  • Deprecated getComponentEvent
    To quickly get rid of this deprecation, use 'Embedding via Events'.
  • Easy virtual component bindings Use 'Virtual via Registration' to get the advantages of Virtual Component binding with minimal changes to applications.
  • Maximum design flexibility
    Use 'Virtual via Events' (Virtual Components).

Understanding Focus

Components can have focus. This is analagous to HTML Elements having focus.

Only one component in a layout can have focus at any time (or alternatively, no component has focus). Similarly to HTML elements, a component will be focused when you click on its tab. You can programatically give a component focus by calling the focus() method on its container. Likewise, you can remove focus from a container by calling ComponentContainer.blur().

Clicking on HTML within a component will not automatically give a Golden Layout component focus. However this can be achieved by listening to the bubbling click and/or focusin events and calling ComponentContainer.focus() in these events' handlers. The apitest demonstrates this technique.

A focused component's tab and header HTML elements will contain the class lm_focused. This can be used to highlight the focused tab and or header. The goldenlayout-dark-theme.less theme that ships with Golden Layout (and is used by apitest) will set the background color of a focused tab to a different color from other tabs. If you do NOT want focused tabs to be highlighted, ensure that the lm_focused selector is removed from the relevant css/less/scss used by your application.

Understanding LocationSelectors

LocationSelectors specify the location of a component in terms of a parent and a index. LocationSelectors are useful for specifying where a new ContentItem should be placed.

A LocationSelector does not specify the parent directly. Instead it specifies how the parent is to be searched for. It has the type:

export interface LocationSelector {
    typeId: LocationSelector.TypeId;
    index?: number;
}

typeId specifies the algorithm used to search for a parent. index is used by the algorithm to work out the preferred child position under the parent.

Some LocationSelector.TypeId will always find a location. Eg: LocationSelector.TypeId.Root is guaranteed to find a location. Others may not find a location. Eg: LocationSelector.TypeId.FirstStack will not find a location if a layout is empty.

The LayoutManager.addComponentAtLocation() and LayoutManager.newComponentAtLocation() use an array of LocationSelectors to determine the location at which a new/added component will be installed. These functions will attempt to find a valid location starting with the first element in the LocationSelectors array. When a valid location is found, that location will be used for the new component. If no valid location is found from the LocationSelectors in the array, then the component will not be added.

The LayoutManager.addComponent() and LayoutManager.newComponent() use a default LocationSelectors array. The last element in this default array is a LocationSelector of type LocationSelector.TypeId.Root. So this array is guaranteed to find a location. Accordingly, LayoutManager.addComponent() and LayoutManager.newComponent() will always succeed in adding a component.

This default LocationSelectors array is available at LayoutManager.defaultLocationSelectors. An alternative predefined array is available at LayoutManager.afterFocusedItemIfPossibleLocationSelectors.

Version 2

This version is a substantial change from the previous (1.5.9) version. The change can be summarised as:

  1. The code has been ported to TypeScript
  2. The primary focus of maintenance will be on reliability.

Before migrating from version 1, it is important to review the following:

Dropped Features

As part of the port, the code base was significantly refactored. A number of features have been dropped from the version 1.0 as their implementation was not robust enough to meet the reliability requirements of version 2. The dropped features are:

  • React Support - The FlexLayout library has been designed for React components. We recommend developers using React to use this library instead of Golden Layout.
  • Nested Stacks - While it was possible to create layouts with Nested Stacks in version 1, the implementation was incomplete. Due to the large amount of work that would have been necessary to fix the implementation, it was decided instead to drop this feature. Version 2 explicitly does not allow nested stacks.
  • Internal and Public API - All classes, interfaces, functions and properties are marked as either internal or public. Only public APIs are generally available to applications.
  • Legacy Browsers - The library will now only target modern browsers (see package.json for browserlist configuration)
  • No JQuery - JQuery is no longer used in Golden Layout (many would consider this as an added feature)

Migration to v2

Version 2 has been re-written in TypeScript. A general code cleanup has been carried out as part of this re-write.

Also, some changes have been made to the GoldenLayout API. Where possible, backwards compatibility has been retained,however functions and properties kept for backwards compatibility have been marked as deprecated. It is strongly recommend applications be migrated to the new API.

Config

Configs are now strongly typed. In addition, GoldenLayout now has "Configs" and "Resolved Configs"

  1. Configs
    Application developers will mainly work with "Configs". A "Config" supports optional properties. If a property is not specified, a default will be used. In addition, "Config" also will handle backwards compatibility. It will migrate deprecated properties to their new values.
    Config parameters in GoldenLayout API methods will be of type "Config". The one exception is LayoutConfig.saveLayout() which returns a "Resolved Config".
  2. Resolved Configs
    Golden-Layout internally uses "Resolved Config"s. Whenever an API function is passed a "Config", GoldenLayout will resolve it to its corresponding "Resolved Config". This resolving process will set default values where an optional value has not been specified. It will also handle backwards compatibility. This allows the GoldenLayout library to always work with fully configured Configs.

For persistence of configs, always save the "Resolved Config" returned by LayoutManager.saveLayout(). When reloading a saved Layout, first convert the saved "Resolved Config" to a "Config" by calling LayoutConfig.fromResolved().

Both "Resolved Config" and "Config" have 2 types of interface hierarchies:

  1. ItemConfig
    This specifies the config for a content item.
  2. LayoutConfig (previously the Config interface)
    This specifies the config for a layout.

The (optional) ItemConfig.id property now has type string (instead of its previous string | string[] type). For backwards compatibility, when ItemConfig.id is resolved, it will still accept an id with of type string array. This will allow handling of legacy saved configs in which id contains an array of strings (including possibly the legacy maximise indicator). When such an id is resolved, the array is first checked for the legacy maximise indicator and then the first element becomes the id string value. The remaining elements are discarded.

The ComponentItemConfig.componentName property has now been replaced by property ComponentItemConfig.componentType. componentType is of type JsonValue. While a component type can now be specified by values that can be serialised by JSON, componentType must be of type string if it is registered with one of the following functions:

  1. GoldenLayout.registerComponent() (deprecated)
  2. GoldenLayout.registerComponentConstructor()
  3. GoldenLayout.registerComponentFactoryFunction()

A LayoutConfig has a root property which specifies the ItemConfig of root content item of the layout. root is not optional and must always be specified.

The LayoutConfig selectionEnabled property has been removed. Clicking of Stack Headers can now be handled with the new stackHeaderClick event (which is always enabled).

ResolvedLayoutConfig now has functions to minify and unminify configurations:

  1. minifyConfig() Replaces LayoutManager.minifyConfig()
  2. unminifyConfig() Replaces LayoutManager.unminifyConfig()

For examples of how to create LayoutConfigs, please refer to the apitest program in the repository.

Many of the Config properties have been deprecated as they overlapped or were moved to more appropriate locations. Please refer to the config.ts source file for more information about these deprecations.

GoldenLayout class and VirtualLayout class

GoldenLayout is now a distinct class which is a descendant of the VirtualLayout class, which in turn is a descendant of the LayoutManager class. Your application should always create an instance of either GoldenLayout or VirtualLayout.

The GoldenLayout and VirtualLayout constructors takes 3 optionals parameters:

  1. The HTML element which contains the GoldenLayout instance. If this is not specified, GoldenLayout will be placed under body.
  2. The bindComponentEvent event handler.
  3. The unbindComponentEvent event handler.

Note that the initial Layout is no longer specified in this constructor. Instead it is loaded with LayoutManager.loadLayout() (see below).

The GoldenLayout class now handles component registration. LayoutManager no longer includes any component registration functions. The following changes to registration functions have been made:

  1. registerComponentConstructor() (new function)
    Same as previous registerComponent() however only used when registering a component constructor.
  2. registerComponentFactoryFunction (new function)
    Same as previous LayoutManager.registerComponent() however only used when registering a call back function (closure) for creating components.
  3. Do not use registerComponent(). Use the new registerComponentConstructor() or registerComponentFactoryFunction() instead.

LayoutManager changes

  1. Do not construct an instance of LayoutManager. Construct an instance of GoldenLayout (see above).
  2. Do not call init(). Call LayoutManager.loadLayout() instead.
  3. loadLayout() (new function)
    Will load the new layout specified in its LayoutConfig parameter. This can also be subsequently called whenever the GoldenLayout layout is to be replaced.
  4. saveLayout() (new function)
    Saves the current layout as a LayoutConfig. Replaces the existing toConfig() function.
  5. Do not uses minifyConfig() of unminifyConfig() functions. Use the respective functions in ResolvedLayoutConfig.
  6. Do not call toConfig(). Call LayoutManager.saveLayout() instead.
  7. setSize() (new function)
    Sets the size of the GoldenLayout instance in pixels. Replaces the existing updateSize() function.
  8. Do not use updateSize(). Use the new LayoutManager.setSize() instead.
  9. rootItem (new property) Specifies the root content item of the layout (not the Ground content item).
  10. Do not use root. This has been replaced with the internal property groundItem. You probably want to use the new rootItem instead.
  11. focusComponent() will focus the specified component item. Only one component item can have focus. If previously, another component item had focus, then it will lose focus (become blurred). focus or blur events will be emitted as appropriate unless the suppressEvent parameter is set to true.
  12. clearComponentFocus() which removes any existing component item focus. If focus is removed, a blur event will be emitted unless the suppressEvent parameter is set to true.

VirtualLayout

  1. getComponentEvent Now implemented in the VirtualLayout class but has been deprecated. Use VirtualLayout.bindComponentEvent instead.
  2. releaseComponentEvent (new event)
    Now implemented in the VirtualLayout class but has been deprecated. Use VirtualLayout.unbindComponentEvent instead.
  3. See Binding Components section for more information about new events related to binding components.

Content Items

  1. AbstractContentItem has been renamed to ContentItem
  2. config property has been removed. Use the toConfig() method instead (as recommended in the original GoldenLayout documentation).
  3. Some of the previous config properties such as id and type are now available as properties of ContentItem or its descendants (where appropriate).
  4. id now has type string. (It used to be string | string[].)
  5. ItemContainer has been renamed to ComponentContainer
  6. Component has been renamed to ComponentItem. "Component" now refers to the external component hosted inside GoldenLayout
  7. Root has been renamed to GroundItem and has been marked as internal only. Applications should never access GroundItem. Note that the layout's root ContentItem is GroundItem's only child. You can access this root ContentItem with LayoutManager.rootItem.
  8. Stack.getActiveContentItem() and Stack.setActiveContentItem() have been renamed to respective Stack.getActiveComponentItem() and Stack.setActiveComponentItem()
  9. ContentItem.select() and ContentItem.deselect() have been removed. Use the new ComponentItem.focus() and ComponentItem.blur() instead.
  10. ComponentItem.focus() (new function) will focus the specified ComponentItem. It will also remove focus from another component item which previously had focus. Only one component item can have focus at any time. If layout focus has changed, a focus event will be emitted (unless suppressEvent parameter is set to true).
  11. ComponentItem.blur() (new function) will remove focus from the specified ComponentItem. After this is called, no component item in the layout will have focus. If the component lost focus, a blur event will be emitted (unless suppressEvent parameter is set to true).

ComponentContainer

  1. element (new property - replaces getElement())
    Returns HTMLElement which hosts component
  2. Do not use getElement(). Use the new element property instead
  3. initialState (new getter)
    Gets the componentState of the ComponentItemConfig used to create the contained component.
  4. stateRequestEvent (new event)
    If set, stateRequestEvent is fired whenever GoldenLayout wants the latest state for a component. Calling LayoutManager.saveLayout() will cause this event to be fired (if it is defined). If it is not defined, then the initial state in the ItemConfig or the latest state set in setState() will be saved.
  5. beforeComponentRelease (new EventEmitter event)
    beforeComponentRelease is emitted on the container before a component is released. Components can use this event to dispose of resources.
  6. Do not use getState() unless you are using the deprecated setState(). Use ComponentContainer.initialState getter if you have migrated to the new ComponentContainer.stateRequestEvent.
  7. setState() has been marked as deprecated. If possible, use the new stateRequestEvent event instead.
  8. replaceComponent() allows you to replace a component in a container without otherwise affecting the layout.
  9. See Binding Components section for more information about new events related to binding components.

Header and Tab

Several properties and functions have been renamed in header.ts and tab.ts. Please search for "@deprecated" in these files for these changes.

Events

  1. All DOM events are now propagated so that they can be handled by parents or globally.
  2. preventDefault() is not called by any event listeners.
  3. Bubbling Events are now emitted with the parameter EventEmitter.BubblingEvent (or descendant)
  4. New EventEmitter events:
    • beforeComponentRelease
    • stackHeaderClick - Bubbling event. Fired when stack header is clicked - but not tab.
    • stackHeaderTouchStart - Bubbling event. Fired when stack header is touched - but not tab.
    • focus - Bubbling event. Fired when a component gets focus.
    • blur - Bubbling event. Fired when a component loses focus.

Other

  1. undefined is used instead of null for new properties, events etc. Some internals have also been switched to use undefined instead of null. Existing properties using null mostly have been left as is however it is possible that some of these internal changes have affected external properties/events/methods.

Deprecations

For most changes, the existing functions and properties have been left in place but marked as deprecated. It is strongly recommended that applications be reworked not to use these deprecations. Bugs associated with deprecations will be given low priority (or not fixed at all). Also, deprecated aliases, methods and properties may be removed in future releases.

Public and Internal APIs

All API elements (classes, interfaces, functions etc) have been labelled as either public or internal. Applications should only use public API elements. Internal API elements are subject to change and no consideration will be given to backwards compatibility when these are changed.

The library distribution includes 2 TypeScript declaration (typing) files:

  1. index.d.ts which contains only public API elements. Applications should use this declaration file to access the library.
  2. golden-layout-untrimmed.d.ts which contains all (public and internal) API elements. Use this declaration file if you wish to access any API element in the library however please take the above warning into account.

Note that the allocation of API elements to either public or internal has not been finalised. However any element used in either the apitest application or the example Angular application will remain labelled as public.