Skip to content

Details of Widget Creation

Creating Widgets Manually

Now let us look at some details on how we have created applications and widgets for now. Our applications have roughly always the same following structure:

Application Structure

export default class extends Application {
  async init(env, options) {
    await super.init({
      panel: {
        content: "... Here we defined some basic structure ..."
      },
      widget: {
        //Here we define a widget that should be initialized
        widget: Layout,
        options: { /* Some Options of the Widget */ }
      }
    });
  }
}

As you can see we use the init() method only to call super.init() with a configuration object. In fact thes properties panel.content and widget are only shortcuts.

You can access and modify the content area of the application or create a widget manually. Let's create a new application:

apps/testing-content.js

import { Application, Calendar } from "@codecoupler/cc-ui";
import $ from "jquery";
export default class extends Application {
  static ui = {
    name: "Testing Content",
    iconHtml: '<i class="fas fa-fire-extinguisher fa-fw"></i>'
  };

  async init() {
    await super.init();

    //After this point the panel is created and you can
    //insert a HTML structure into the content area:
    $(this.panel.content).html(
      `
      <div class="container-fluid">
        <div class="row">
          <div class="col">
            <h2>First Widget:</h2>
            <div class="first-div m-2 border border-dark" style="width:300px;height:350px;"></div>
          </div>
        </div>
      </div>
      `
    );

    //Now you can initialize a widget inside of one
    //of the created <div> elements:
    this.widget({
      widget: Calendar,
      container: $(this.panel.content).find(".first-div"),
      options: {
        onlyBackgroundEvents: true,
        themeToolbar: "small",
        pointerOnDay: true
      }
    });
  }
}

Now let's integrate our new application into the apps.js:

apps.js

import { Calendar, Layout } from "@codecoupler/cc-ui";
import TestingButtonsApp from "./apps/testing-buttons";
import TestingContentApp from "./apps/testing-content";
export default function() {
  return [
    {
      ui: {
        iconHtml: '<i class="far fa-smile fa-fw"></i>',
        name: "Just an Alert"
      },
      app: () => {
        alert("Hello World");
      }
    },
    {
      app: TestingButtonsApp,
      id: "TestApp1",
      options: {
        text: "My First Application"
      }
    },
    {
      ui: {
        iconHtml: '<i class="fas fa-tree fa-fw"></i>'
      },
      app: TestingButtonsApp,
      id: "TestApp2",
      options: {
        text: "My Second Application"
      }
    },
    {
      ui: {
        iconHtml: '<i class="fas fa-hand-sparkles fa-fw"></i>',
        name: "Date Selection"
      },
      panel: {
        header: true,
        setStatus: "normalized"
      },
      id: "DateSelection",
      app: {
        panel: {
          panelSize: "300 350",
          headerControls: "closeonly xs",
          headerTitle: "Select..."
        },
        widget: {
          widget: Layout,
          options: {
            root: {
              type: "widget",
              widget: Calendar,
              options: {
                onlyBackgroundEvents: true,
                themeToolbar: "small"
              }
            }
          }
        }
      }
    },
    {
      app: TestingContentApp,
      id: "TestApp3"
    }
  ];
}

Now if you start the application you will see the calendar inside of the content area of the application.

Using the Generic Widget

Now imagine you want to create and use the widget Layout. This is a widget that defines widgets itself. For each widget defined there you would have to define a class and at best in an own file. Sometimes it makes sense, but in other cases it would be better to keep all the logic of an application in one class.

This can be done using a special widget named GenericWidget. This widget will initialize a widget with no content, like a placeholder. The class provide the arguments env and options as public members. Over the env member you get access the element in which the widget was initialized. Let's rework our new application:

apps/testing-content.js

import {
  Application,
  Calendar,
  Layout,
  GenericWidget
} from "@codecoupler/cc-ui";
import $ from "jquery";
export default class extends Application {
  static ui = {
    name: "Testing Content",
    iconHtml: '<i class="fas fa-fire-extinguisher fa-fw"></i>'
  };

  async init() {
    await super.init();

    //We start now creating the widget "Layout" with
    //two columns and an "EmptyWidget" in each one.
    //Pay attention to the "await" keyword. This will
    //ensure that the widget is initialized in the
    //next lines!
    await this.widget({
      widget: Layout,
      id: "layout",
      container: this.panel.content,
      options: {
        root: {
          type: "row",
          content: [
            {
              type: "widget",
              id: "widget1",
              widget: GenericWidget
            },
            {
              type: "widget",
              id: "widget2",
              widget: GenericWidget
            }
          ]
        }
      }
    });

    //Now we inject our HTML structure into the first
    //widget instead of the panel content area:
    $(this.widgets.layout.widgets.widget1.env.element).html(
      `
      <div class="container-fluid">
        <div class="row">
          <div class="col">
            <h2>First Widget:</h2>
            <div class="first-div m-2 border border-dark" style="width:300px;height:350px;"></div>
          </div>
        </div>
      </div>
      `
    );

    //Now you can initialize a widget inside of one
    //of the created <div> elements. Pay attention
    //that we use the widget creation method of the
    //widget itself "this.widgets.layout.widgets.widget1.widget()"
    //instead of the applictaion "this.widget()"!
    this.widgets.layout.widgets.widget1.widget({
      widget: Calendar,
      container: $(this.panel.content).find(".first-div"),
      options: {
        onlyBackgroundEvents: true,
        themeToolbar: "small",
        pointerOnDay: true
      }
    });
  }
}

Widgets in Stages

Now that we know how to create widgets manually we can also apply our knowledge to Stages. Inside a stage you can also initialize a widget. We will use for this our header stage which is a Vue application. We can access inside a Vue application the parent Stage class which have also an widget() method to initialize a new widget:

stage/header.vue

<template>
  <div class="header-stage d-flex flex-column h-100">
    <div class="header text-white">
      Header as Vue with option: {{ options.header }}
      --- And here a Widget:
      <div
        class="header-widget text-white border border-white d-inline-block"
      ></div>
    </div>
    <div class="flex-grow-1 position-relative" data-role="cc-stage"></div>
  </div>
</template>
<script>
import { GenericWidget } from "@codecoupler/cc-ui";
import $ from "jquery";
export default {
  props: {
    env: { type: Object, default: null },
    options: { type: Object, default: null }
  },
  mounted() {
    this.env.start.stage
      .widget({
        widget: GenericWidget,
        id: "headerWidget",
        container: $(this.$el).find(".header-widget")
      })
      .then(() => {
        this.env.start.stage.widgets.headerWidget.env.$element.text(
          "Widget with Text"
        );
      });
  }
};
</script>
<style lang="postcss" scoped>
.header-stage {
  & .header {
    background-color: var(--color-primary-2);
    min-height: 40px;
    padding: 10px;
  }
}
</style>