Skip to content

Layout

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.

What are we learning here?

  • How to use a Layout component

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.

Let's start with a simple skeleton:

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { Application } from "@codecoupler/cc-ui";
import LayoutComponent from "./layout.js";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "1165 630",
        headerTitle: "Test Layout Features",
      }
    }
  };
  async start() {
    await this.stage.load(LayoutComponent);
  }
}

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

src/system.js

1
2
3
4
5
6
7
import { Component } from "@codecoupler/cc-ui";
import TestApp from "./demo/apps/layout-component";
export default class extends Component {
  async boot() {
    await this.stage.load(TestApp);
  }
}

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

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 application component WidgetBasics into this to demonstrate how to modify an application component to look like a canvas or widget component.

We want to load the application component in our first stage without the title and borders. So we will set the panel to the status "fullsize".To set this option we use so called namespaced options. These options will be evaluated by the underlying base classes. In our example the options described by { "@codecoupler": { application: {} } } will be processed by the base class Application which is be extended by WidgetBasics.

Each component intended to be extended has its own options in many cases. In order not to conflict with the options of the final class, namespaces are used. Best practice is to use the top level with the vendor name prefixed with an @-sign. In the level below, the name of the base class is used.

As we will use this option many times we will put it in an own variable:

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import { Application, Layout } from "@codecoupler/cc-ui";
import WidgetBasics from "../widget-basics";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "1165 630",
        headerTitle: "Test Layout Features",
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      root: { id: "first", type: "stage" }
    });
    let options = "@codecoupler": { application: { panel: { setStatus: "fullsize" } } };
    await this.getStage("first").load(WidgetBasics, options);
  }
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { Application, Layout } from "@codecoupler/cc-ui";
import WidgetBasics from "../widget-basics";
import SingleRoot from "../single-root";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "1165 630",
        headerTitle: "Test Layout Features",
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      root: {
        type: "row",
        content: [
          { id: "first", type: "stage" },
          { id: "second", type: "stage" }
        ]
      }
    });
    let options = "@codecoupler": { application: { panel: { setStatus: "fullsize" } } };
    await Promise.all([
      this.getStage("first").load(WidgetBasics, options),
      this.getStage("second").load(SingleRoot, options)
    ]);
  }
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { Application, Layout } from "@codecoupler/cc-ui";
import WidgetBasics from "../widget-basics";
import SingleRoot from "../single-root";
import WidgetBasicsVue from "../widget-basics-vue";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "1165 630",
        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%" }
            ]
          }
        ]
      }
    });
    let options = {
      "@codecoupler": { application: { panel: { setStatus: "fullsize" } } }
    };
    await Promise.all([
      this.getStage("first").load(WidgetBasics, options),
      this.getStage("second").load(SingleRoot, options),
      this.getStage("third").load(WidgetBasicsVue, options)
    ]);
  }
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { Application, Layout } from "@codecoupler/cc-ui";
import WidgetBasics from "../widget-basics";
import SingleRoot from "../single-root";
import WidgetBasicsVue from "../widget-basics-vue";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "1165 630",
        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%" }
            ]
          }
        ]
      }
    });
    let options = {
      "@codecoupler": { application: { panel: { setStatus: "fullsize" } } }
    };
    await Promise.all([
      this.getStage("first").load(WidgetBasics, options),
      this.getStage("second").load(SingleRoot, options),
      this.getStage("third").load(WidgetBasicsVue, options)
    ]);
  }
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import { Application, Layout } from "@codecoupler/cc-ui";
import WidgetBasics from "../widget-basics";
import SingleRoot from "../single-root";
import WidgetBasicsVue from "../widget-basics-vue";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "1165 630",
        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%" }
            ]
          }
        ]
      }
    });
    let options = {
      "@codecoupler": { application: { panel: { setStatus: "fullsize" } } }
    };
    await Promise.all([
      this.getStage("first").load(WidgetBasics, options),
      this.getStage("second").load(SingleRoot, options),
      this.getStage("third").load(WidgetBasicsVue, options)
    ]);
  }
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import { Application, Layout } from "@codecoupler/cc-ui";
import WidgetBasics from "../widget-basics";
import SingleRoot from "../single-root";
import WidgetBasicsVue from "../widget-basics-vue";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "1165 630",
        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: "Basics" },
          {
            type: "column",
            content: [
              { id: "second", type: "stage", title: "Single Root" },
              { id: "third", type: "stage", size: "60%", title: "Vue" }
            ]
          }
        ]
      }
    });
    let options = {
      "@codecoupler": { application: { panel: { setStatus: "fullsize" } } }
    };
    await Promise.all([
      this.getStage("first").load(WidgetBasics, options),
      this.getStage("second").load(SingleRoot, options),
      this.getStage("third").load(WidgetBasicsVue, options)
    ]);
  }
}

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import { Application, Layout } from "@codecoupler/cc-ui";
import WidgetBasics from "../widget-basics";
import SingleRoot from "../single-root";
import WidgetBasicsVue from "../widget-basics-vue";
import UnusableLayout from "../canvas-vs-widget-layout";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "1165 630",
        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: "Basics" },
          {
            type: "column",
            content: [
              { id: "second", type: "stage", title: "Single Root" },
              {
                type: "stack",
                size: "60%",
                content: [
                  { id: "third", type: "stage", title: "Vue" },
                  { id: "fourth", type: "stage", title: "No Height" }
                ]
              }
            ]
          }
        ]
      }
    });
    let options = {
      "@codecoupler": { application: { panel: { setStatus: "fullsize" } } }
    };
    await Promise.all([
      this.getStage("first").load(WidgetBasics, options),
      this.getStage("second").load(SingleRoot, options),
      this.getStage("third").load(WidgetBasicsVue, options),
      this.getStage("fourth").load(UnusableLayout, options)
    ]);
  }
}

Ok. Now we're taking it to the extreme, using all the other components we created earlier and putting them all in the layout:

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import { Application, Layout } from "@codecoupler/cc-ui";
import WidgetBasics from "../widget-basics";
import SingleRoot from "../single-root";
import WidgetBasicsVue from "../widget-basics-vue";
import UnusableLayout from "../canvas-vs-widget-layout";
import UnusableScroll from "../canvas-vs-widget-scroll";
import StageLevels from "../stage-levels";
import StagesFlexbox from "../stages-flexbox";
import SimpleBox from "../simple-box";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "1165 630",
        headerTitle: "Test Layout Features",
      }
    }
  };
  async start() {
    await this.stage.load(Layout, {
      reordable: true,
      resizable: true,
      root: {
        type: "row",
        content: [
          {
            type: "stack",
            size: "60%",
            content: [
              { id: "first", type: "stage", title: "Basics" },
              { id: "sixth", type: "stage", title: "Layers" },
              { id: "seventh", type: "stage", title: "Flexbox" },
              { id: "eighth", type: "stage", title: "Box" }
            ]
          },
          {
            type: "column",
            content: [
              {
                type: "stack",
                content: [
                  { id: "second", type: "stage", title: "Single Root" },
                  { id: "fifth", type: "stage", title: "No Scroll" }
                ]
              },
              {
                type: "stack",
                size: "60%",
                content: [
                  { id: "third", type: "stage", title: "Vue" },
                  { id: "fourth", type: "stage", title: "No Height" }
                ]
              }
            ]
          }
        ]
      }
    });
    let options = {
      "@codecoupler": { panel: { setStatus: "fullsize" } }
    };
    await Promise.all([
      this.getStage("first").load(WidgetBasics, options),
      this.getStage("second").load(SingleRoot, options),
      this.getStage("third").load(WidgetBasicsVue, options),
      this.getStage("fourth").load(UnusableLayout, options),
      this.getStage("fifth").load(UnusableScroll, options),
      this.getStage("sixth").load(StageLevels, options),
      this.getStage("seventh").load(StagesFlexbox, options),
      this.getStage("eighth").load(SimpleBox, options)
    ]);
  }
}