# Layouts API

Source: https://docs.interop.io/browser/capabilities/windows/layouts/layouts-api/index.html

## Overview

The Layouts API is accessible via the [`io.layouts`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md) object.

## Layout Operations

### Current Global Layout

Available since io.Connect Browser 4.0

To retrieve the currently restored Global Layout, use the [`getCurrentLayout()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-getCurrentLayout) method:

```javascript
const currentLayout = await io.layouts.getCurrentLayout();
```

### All Layouts by Type

To retrieve all Layouts by type, use the [`getAll()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-getAll) method and specify the Layout type. It resolves with a collection of [`LayoutSummary`](https://docs.interop.io/browser/reference/javascript/layouts/layoutsummary/index.md) objects, which don't contain the extensive objects describing the actual [`Layout`](https://docs.interop.io/browser/reference/javascript/layouts/layout/index.md) components:

```javascript
const allGlobalLayouts = await io.layouts.getAll("Global");
```

### Specific Layout

To retrieve a specific Layout, use the [`get()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-get) method and provide the name of the Layout and the Layout type as arguments. It resolves with the requested [`Layout`](https://docs.interop.io/browser/reference/javascript/layouts/layout/index.md) object or `undefined` if a Layout with the specified name and type doesn't exist:

```javascript
const name = "My Layout";
const type = "Global";

const myLayout = await io.layouts.get(name, type);
```

### Save

> ⚠️ *Note that if you haven't already handled programmatically the process of [requesting a permission from the user for the Window Management browser functionality](#requesting_window_management_permission), the first time either the [`save()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-save) or the [`restore()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-restore) method is invoked, the Global Layouts library will automatically ask the user for permission. This default behavior isn't recommended, as it isn't ideal for the user experience.*

To save a Layout, use the [`save()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-save) method and pass a [`NewLayoutOptions`](https://docs.interop.io/browser/reference/javascript/layouts/newlayoutoptions/index.md) object with a required `name` property. If a Layout with that name already exists, it will be replaced. The method resolves with the saved [`Layout`](https://docs.interop.io/browser/reference/javascript/layouts/layout/index.md) object:

```javascript
const layoutConfig = {
    name: "My Layout"
};

const savedLayout = await io.layouts.save(layoutConfig);
```

Available since io.Connect Browser 4.2

When saving a Layout, you can specify whether to set it as the currently active Global Layout by using the `setAsCurrent` property of the [`NewLayoutOptions`](https://docs.interop.io/browser/reference/javascript/layouts/newlayoutoptions/index.md) object:

```javascript
const options = {
    name: "My Layout",
    setAsCurrent: false
};

const myLayout = await io.layouts.save(options);
```

Available since io.Connect Browser 4.3

To skip saving any window or Workspace contexts when saving a Layout, use the `ignoreContexts` property of the `NewLayoutOptions` object:

```javascript
const options = {
    name: "My Layout",
    ignoreContexts: true
};

const savedLayout = await io.layouts.save(options);
```

Available since io.Connect Browser 4.4

To instruct the platform to persist the context objects of individual io.Connect Windows (set via the [`setContext()`](https://docs.interop.io/browser/reference/javascript/windows/webwindow/index.md#WebWindow-setContext) method of a [`WebWindow`](https://docs.interop.io/browser/reference/javascript/windows/webwindow/index.md) instance), use the `saveWindowContexts` property of the `NewLayoutOptions` object:

```javascript
const options = {
    name: "My Layout",
    saveWindowContexts: true
};

const myLayout = await io.layouts.save(options);
```

If a Browser Client has [subscribed for Layout save requests](#saving_context_data) via the [`onSaveRequested()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-onSaveRequested) method and the event handler returns a context object, it will take precedence over the context set via the `setContext()` method. If the `ignoreContexts` property of the `NewLayoutOptions` object is set to `true`, the `saveWindowContexts` property will be ignored.

### Restore

To restore a Layout, use the [`restore()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-restore) method and pass a [`RestoreOptions`](https://docs.interop.io/browser/reference/javascript/layouts/restoreoptions/index.md) object specifying the name of the Layout (required) and other restore options:

```javascript
const restoreOptions = {
    name: "My Layout",
    // Specify whether to close all running apps before restoring the Layout. Defaults to `true`.
    // The Main app is an exception and will never be closed when restoring a Layout.
    closeRunningInstance: false
};

await io.layouts.restore(restoreOptions);
```

### Retrieving Layout Contents

Available since io.Connect Browser 4.5

To retrieve the contents of a Global Layout or a Workspace Layout, use the [`getLayoutContents()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-getLayoutContents) method. Pass a [`GetContentsOptions`](https://docs.interop.io/browser/reference/javascript/layouts/getcontentsoptions/index.md) object as a required argument and specify the `name` and `type` of the Layout whose contents to retrieve:

```javascript
const options = { name: "My Layout", type: "Global" };

const {
    appComponents,
    workspaceComponents,
    workspaceWindows
} = await io.layouts.getLayoutContents(options);

// List all apps in the Layout.
console.log("Apps:", appComponents);

// List all Workspaces App instances in the Layout.
console.log("Workspaces App instances:", workspaceComponents);

// List all windows participating in all Workspaces.
console.log("Workspace windows:", workspaceWindows);
```

The `getLayoutContents()` method resolves with a [`LayoutContents`](https://docs.interop.io/browser/reference/javascript/layouts/layoutcontents/index.md) object. When used for a Global Layout, the returned object contains all components participating in the Layout: individual app instances (including all Workspaces App instances), all Workspace instances hosted in all Workspaces App instances, and all windows participating in all Workspaces. When used for a Workspace Layout, the returned object contains only the windows participating in the Workspace.

### Rename

Available since io.Connect Browser 3.3

To rename a Layout, use the [`rename()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-rename) method. You must pass the [`Layout`](https://docs.interop.io/browser/reference/javascript/layouts/layout/index.md) object to rename as a first argument and the new name for the Layout as a second argument:

```javascript
await io.layouts.rename(myLayout, "My New Layout");
```

### Remove

To remove a Layout, use the [`remove()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-remove) method. You must pass the Layout type and the name of the Layout as arguments:

```javascript
await io.layouts.remove("Global", "My Layout");
```

### Export & Import

You can export all currently available [`Layout`](https://docs.interop.io/browser/reference/javascript/layouts/layout/index.md) objects with the [`export()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-export) method. Exported Layouts can be stored to a database and then be used as restore points, or can be sent to another user and imported on their machine.

```javascript
const layouts = await io.layouts.export();
```

To import exported Layouts, use the [`import()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-import) method. Pass the collection of [`Layout`](https://docs.interop.io/browser/reference/javascript/layouts/layout/index.md) objects to import and specify an [`ImportMode`](https://docs.interop.io/browser/reference/javascript/layouts/importmode/index.md):

```javascript
const mode = "merge";

await io.layouts.import(layouts, mode);
```

The [`ImportMode`](https://docs.interop.io/browser/reference/javascript/layouts/importmode/index.md) controls the import behavior. If set to `"replace"` (default), all existing Layouts will be removed. If set to `"merge"`, the imported Layouts will be added to the existing ones, replacing any Layouts with conflicting names.

### Update Metadata

Available since io.Connect Browser 3.3

To persist any changes made to the metadata of a [`Layout`](https://docs.interop.io/browser/reference/javascript/layouts/layout/index.md) object, use the [`updateMetadata()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-updateMetadata) method and provide the Layout as an argument:

```javascript
// Changing the metadata of a Layout.
myLayout.metadata = { io: 42 };

// Updating the metadata of a Layout so that it will be persisted.
await io.layouts.updateMetadata(myLayout);
```

## Save & Update Context

When a Layout is saved, apps can store context data in it. When the Layout is restored, the context data is also restored and returned to the apps. Context data can be saved in all Layout types.

> ⚠️ *Note that saving large volumes of custom data as window context (e.g., thousands of lines of table data) can lead to significant delays when saving a Layout. A Layout usually contains several (in some cases - many) apps and/or Workspaces (which can also contain many apps) and if one or more of the apps saves large amounts of context data each time a Layout is saved, this will significantly slow down the saving process. The methods for saving custom context work best with smaller amounts of data. If your app needs to save large amounts of data, you have to think about how to design this process better - for instance, you may store IDs, indices, etc., as context data, save the actual data to a database and when you restore the Layout, fetch the data using the data IDs saved as window context.*

### Saving Context Data

To save context data, apps can subscribe for Layout save requests using the [`onSaveRequested()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-onSaveRequested) method. A Layout save request event is fired when the user attempts to save a Layout or close a window, Workspace, etc. The on `onSaveRequested()` method accepts a callback which will be invoked when a Layout save request is triggered. The callback will receive as an argument a [`SaveRequestContext`](https://docs.interop.io/browser/reference/javascript/layouts/saverequestcontext/index.md) object containing the Layout name, type and context. Use it to determine the type of the Layout and instruct your app to react accordingly:

```javascript
const saveRequestHandler = (requestInfo) => {
    // Get the Layout type.
    const layoutType = requestInfo.layoutType;

    // Check the Layout type and return data.
    if (layoutType === "Global") {
        return { windowContext: { gridWidth: 42 } };

    } else {
        // Return if not interested in other Layout types.
        return;
    };
};

io.layouts.onSaveRequested(saveRequestHandler);
```

The callback must return a [`SaveRequestResponse`](https://docs.interop.io/browser/reference/javascript/layouts/saverequestresponse/index.md) object that has a `windowContext` property.

After the Layout has been restored, the saved context data will be available in the window context:

```javascript
// Extracting previously saved data from the window context.
const windowContext = await io.windows.my().getContext();
const gridWidth = windowContext.gridWidth;
```

## Default Global Layout

**io.Connect Browser** allows you to specify a default Global Layout that will be automatically loaded on start. You can get, set and clear the default Global Layout programmatically.

To retrieve the current default Global Layout, use the [`getDefaultGlobal()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-getDefaultGlobal) method:

```javascript
// May return `undefined` if no default Global Layout has been set.
const defaultLayout = await io.layouts.getDefaultGlobal();
```

To set a default Global Layout, use the [`setDefaultGlobal()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-setDefaultGlobal) method:

```javascript
io.layouts.setDefaultGlobal("My Layout");
```

To clear the default Global Layout, use the [`clearDefaultGlobal()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-clearDefaultGlobal) method:

```javascript
io.layouts.clearDefaultGlobal();
```

## Checking the Global Layouts State

To check whether the [Main app](https://docs.interop.io/browser/developers/browser-platform/overview/index.md) has loaded and initialized the Global Layouts library that enables **io.Connect Browser** to save and restore Layouts of type `"Global"`, use the [`getGlobalTypeState()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-getGlobalTypeState) method. It returns an object with an `activated` property holding a Boolean value:

```javascript
const state = await io.layouts.getGlobalTypeState();

if (state.activated) {
    // Global Layouts are available.
} else {
    // Global Layouts aren't available.
};
```

## Requesting Window Management Permission

The Layouts API provides methods with which you can ask the user to allow the Window Management browser functionality, which is mandatory for the Global Layouts to work properly. This allows you to handle the permission request process at the right moment, in a well-designed manner.

To check whether the user has already granted or denied permission for the Window Management browser functionality, or this permission is yet to be requested, use the [`getMultiScreenPermissionState()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-getMultiScreenPermissionState) method and extract the value describing the current permission state from the `state` property of the returned object:

```javascript
const permission = await io.layouts.getMultiScreenPermissionState();

switch (permission.state) {
    case "prompt":
        // Permission hasn't been requested yet.
        break;
    case "granted":
        // Permission has already been granted.
        break;
    case "denied":
        // Permission has already been denied.
        break;
    default:
        break;
};
```

To request a permission from the user for the Window Management browser functionality, use the [`requestMultiScreenPermission()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-requestMultiScreenPermission) method. It returns an object with a `permissionGranted` Boolean property:

```javascript
const result = await io.layouts.requestMultiScreenPermission();

if (result.permissionGranted) {
    // The user has granted permission.
} else {
    // The user has denied permission.
};
```

> ⚠️ *Note that `requestMultiScreenPermission()` should be called only by the [Main app](https://docs.interop.io/browser/developers/browser-platform/overview/index.md) in order to guarantee that the browser will indeed display the appropriate prompt. If any other [Browser Client](https://docs.interop.io/browser/developers/browser-client/overview/index.md) invokes `requestMultiScreenPermission()`, then the corresponding Main app will try to request the permission, but will almost certainly fail to display the prompt, because of the transient activation requirement for displaying permission prompts employed by most major browsers.*

## Events

The Layouts API allows your app to react to Layout events like adding, removing, updating or renaming a Layout. Use the returned unsubscribe function to stop receiving notifications about the respective event.

### Added

To subscribe for the event which fires when a Layout is added, use the [`onAdded()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-onAdded) method:

```javascript
io.layouts.onAdded(console.log);
```

### Removed

To subscribe for the event which fires when a Layout is removed, use the [`onRemoved()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-onRemoved) method:

```javascript
io.layouts.onRemoved(console.log);
```

### Changed

To subscribe for the event which fires when a Layout is changed, use the [`onChanged()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-onChanged) method:

```javascript
io.layouts.onChanged(console.log);
```

### Renamed

Available since io.Connect Browser 3.3

To get notified when a Layout is renamed, use the [`onRenamed()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-onRenamed) method:

```javascript
io.layouts.onRenamed(console.log);
```

### Restored

Available since io.Connect Browser 4.0

To get notified when a Layout is restored, use the [`onRestored()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-onRestored) method:

```javascript
io.layouts.onRestored(console.log);
```

### Default Global Layout Changed

Available since io.Connect Browser 4.0

To get notified when the [default Global Layout](#default_global_layout) has been changed, use the [`onDefaultGlobalChanged()`](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md#API-onDefaultGlobalChanged) method:

```javascript
io.layouts.onDefaultGlobalChanged(console.log);
```

## API Reference

For a complete list of the available Layouts API methods and properties, see the [Layouts API Reference Documentation](https://docs.interop.io/browser/reference/javascript/layouts/api/index.md).
