Skip to content

Layout

What are we learning here?

  • How to use a Layout component

Now let's use another component with which you can define more complex layouts without using flexbox or any other HTML structures like before. We will use a component called Layout and we will extend this.

Let's start with a simple skeleton:

src/apps/demo/layout-component/index.js

import { Application, Layout } from "@codecoupler/cc-ui";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "900 500",
        headerTitle: "Test Layout Features"
      }
    }
  };
  async start() {
    await this.stage.load(Layout);
  }
}
A Layout need an outer element with specified height

An Layout component will always adapt its dimensions to the outer element. A Layout is not suitable for loading into an empty stage that has no height. In addition, a Layout cannot adapt to the content of its own stages.

We load the new application in our root.js file (just by replacing the previous one):

src/root.js

import { Component, Flexbox } from "@codecoupler/cc-ui";
import LoadingComponents from "./demo/components/loading-components";
import TestApp from "./demo/apps/layout-component";
export default class extends Component {
  async start() {
    await this.stage.load(Flexbox, {
      root: {
        type: "column",
        content: [
          { type: "stage", id: "top", class: "card-header" },
          { type: "stage", id: "$", grow: true }
        ]
      }
    });
    await this.getStage("top").load(LoadingComponents);
    await this.stage.load(TestApp);
  }
}

Ok, we have an empty application window. Not very spectacular. Let's start building a layout.

Layout components can be loaded with an option object without namespaced options. The passed options will be mapped automatically into the correct namespace.

Now we pass options to the component starting with one property root to define stages. The structure is very similar to the options of the Flexbox component, but there are significant differences in the details.

In the following step we define first only one stage and load the simple component Message.

src/apps/demo/layout-component/index.js

import { Application, Layout, Message } from "@codecoupler/cc-ui";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "900 500",
        headerTitle: "Test Layout Features"
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      root: { id: "first", type: "stage" }
    });
    await this.getStage("first").load(Message, { text: "First" });
  }
}

Still not very spectacular, so lets replace the first stage with the type row and split this row into two parts. Sublayouts in a row will be defined in the array content. Every part is now an own stage in which we can load a component.

Furthermore, we combine two awaits into one. The components can be loaded in parallel instead of sequentially.

src/apps/demo/layout-component/index.js

import { Application, Layout, Message } from "@codecoupler/cc-ui";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "900 500",
        headerTitle: "Test Layout Features"
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      root: {
        type: "row",
        content: [
          { id: "first", type: "stage" },
          { id: "second", type: "stage" }
        ]
      }
    });
    await Promise.all([
      this.getStage("first").load(Message, { text: "First" }),
      this.getStage("second").load(Message, { text: "Second" })
    ]);
  }
}

Now let's go one step further to illustrate how you can use the types row and column, how to use size and how they can be nested:

src/apps/demo/layout-component/index.js

import { Application, Layout, Message } from "@codecoupler/cc-ui";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "900 500",
        headerTitle: "Test Layout Features"
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      root: {
        type: "row",
        content: [
          { id: "first", type: "stage", size: "60%" },
          {
            type: "column",
            content: [
              { id: "second", type: "stage" },
              { id: "third", type: "stage", size: "60%" }
            ]
          }
        ]
      }
    });
    await Promise.all([
      this.getStage("first").load(Message, { text: "First" }),
      this.getStage("second").load(Message, { text: "Second" }),
      this.getStage("third").load(Message, { text: "Third" })
    ]);
  }
}

Ok, now we have created a basic layout which we could have also done with a flexbox layout. Now let's add the following property resizable: true to show borders between the stages and and make them resizable:

src/apps/demo/layout-component/index.js

import { Application, Layout, Message } from "@codecoupler/cc-ui";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "900 500",
        headerTitle: "Test Layout Features"
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      resizable: true,
      root: {
        type: "row",
        content: [
          { id: "first", type: "stage", size: "60%" },
          {
            type: "column",
            content: [
              { id: "second", type: "stage" },
              { id: "third", type: "stage", size: "60%" }
            ]
          }
        ]
      }
    });
    await Promise.all([
      this.getStage("first").load(Message, { text: "First" }),
      this.getStage("second").load(Message, { text: "Second" }),
      this.getStage("third").load(Message, { text: "Third" })
    ]);
  }
}

Info

resizable: true is just a shortcut for dimensions: { borderWidth: 5, borderGrabWidth: 15 }. You can set of course only borderWidth to have just borders that are not resizable.

Resizing the stages is nice, but what about draging them around and rearange them? Let's insert a new property reordable: true.

src/apps/demo/layout-component/index.js

import { Application, Layout, Message } from "@codecoupler/cc-ui";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "900 500",
        headerTitle: "Test Layout Features"
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      reordable: true,
      resizable: true,
      root: {
        type: "row",
        content: [
          { id: "first", type: "stage", size: "60%" },
          {
            type: "column",
            content: [
              { id: "second", type: "stage" },
              { id: "third", type: "stage", size: "60%" }
            ]
          }
        ]
      }
    });
    await Promise.all([
      this.getStage("first").load(Message, { text: "First" }),
      this.getStage("second").load(Message, { text: "Second" }),
      this.getStage("third").load(Message, { text: "Third" })
    ]);
  }
}

Info

reordable: true is just a shortcut for header: { show: true }

Now you can drag the areas around, rearange the layout and even maximize single areas. Let's set an title for every tab:

src/apps/demo/layout-component/index.js

import { Application, Layout, Message } from "@codecoupler/cc-ui";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "900 500",
        headerTitle: "Test Layout Features"
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      reordable: true,
      resizable: true,
      root: {
        type: "row",
        content: [
          { id: "first", type: "stage", size: "60%", title: "First" },
          {
            type: "column",
            content: [
              { id: "second", type: "stage", title: "Second" },
              { id: "third", type: "stage", size: "60%", title: "Third" }
            ]
          }
        ]
      }
    });
    await Promise.all([
      this.getStage("first").load(Message, { text: "First" }),
      this.getStage("second").load(Message, { text: "Second" }),
      this.getStage("third").load(Message, { text: "Third" })
    ]);
  }
}

Let's demonstrate one last type. Beside row and column you can define a stack:

src/apps/demo/layout-component/index.js

import { Application, Layout, Message } from "@codecoupler/cc-ui";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "900 500",
        headerTitle: "Test Layout Features"
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      reordable: true,
      resizable: true,
      root: {
        type: "row",
        content: [
          { id: "first", type: "stage", size: "60%", title: "First" },
          {
            type: "column",
            content: [
              { id: "second", type: "stage", title: "Second" },
              {
                type: "stack",
                size: "60%",
                content: [
                  { id: "third", type: "stage", title: "Third" },
                  { id: "fourth", type: "stage", title: "Fourth" }
                ]
              }
            ]
          }
        ]
      }
    });
    await Promise.all([
      this.getStage("first").load(Message, { text: "First" }),
      this.getStage("second").load(Message, { text: "Second" }),
      this.getStage("third").load(Message, { text: "Third" }),
      this.getStage("fourth").load(Message, { text: "Fourth" })
    ]);
  }
}