# or-dashboard-builder
### All-in-one bundle for creating dashboards with OpenRemote data.

This component is the core of the Insights page on our OpenRemote manager,<br />
where users can build dashboards with widgets, for monitoring their assets in their preferred way.<br />
It is quite an extensive piece of code, so some apps using this (such as or standalone Insights app)<br />
might not need the full package.<br />
<br />
It is structured to support the additions of custom widgets, whereof a short tutorial is shown below.


---

## Install
```bash
npm i @openremote/or-dashboard-builder
yarn add @openremote/or-dashboard-builder
```



## Terminology:

To be clear about what all functionalities are meant for, we created a list of terms used within `or-dashboard-builder`.
Here it is:

| Term          | Definition                                                                                                                                       |
|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| **Dashboard** | The board / area that contains widgets. (stored in database)                                                                                     | 
| **Widget**    | The object on the dashboard that contains information like content (stored in database)                                                          |
| *WidgetContainer* | Wrapper that displays widget content, header, and its actions                                                                                    |
| *WidgetSettings* | The available settings to customise WidgetConfig, 
| **Insights**  | 'The showcase term' we use for the page in our Manager App. We also have an "Insights app" that is a standalone app for viewing your dashboards. |

<br/>

Here is an overview of all elements used:

| HTML Tag                   | Description                                                                      |
|----------------------------|----------------------------------------------------------------------------------|
| or-dashboard-builder       | Contains the layout of the full dashboard builder. Keeps track of state as well. |
| or-dashboard-preview       | Manages grid, and the widgets loaded onto it.                                    |
| or-dashboard-browser       | Lists widget(s) in cards that can be dragged onto `or-dashboard-preview`         |
| or-dashboard-tree          | Lists dashboards                                                                 |
| or-dashboard-boardsettings | Managing dashboard settings                                                      |
| or-dashboard-widgetsettings | Wrapper that displays settings of the selected widget (`or-widget-settings`)     
| or-widget-container        | Manages the loaded widget in that container                                      |
| or-widget-settings         | Loads and saves widget settings based on the selected widget.                    |

*or-dashboard-engine is a logic class to inject in Gridstack for overriding grid behavior.*<br />
*or-dashboard-keyhandler is used to handle keystrokes from the user*
<br />
<br />

## Creating your own Widget

Add your widget manifest to the `registerWidgetTypes()` function in `index.ts`.<br />
This will register the widget, and handle all functions automatically.
```typescript
export function registerWidgetTypes() {
    widgetTypes.set("linechart", ChartWidget.getManifest());
    widgetTypes.set("gauge", GaugeWidget.getManifest());
    ...
    // add here
}
```

From there, you can add your custom class to the `/widgets` folder and build your HTMLElements.<br />
It is **required** to inherit from `or-widget`, *(or an extended class of it such as or-asset-widget)*<br />
and your custom config should extend on `WidgetConfig`<br />
<br />


### Example of a custom Widget

Here is a code example of how to create custom widgets.<br />
Feel free to copy, put it in separate files, and adjust it to your needs.<br />
Looking into our existing widgets also helps understanding the codebase.

```typescript
import {CustomWidgetConfig} from "./custom-widget";

export interface CustomWidgetConfig extends WidgetConfig {
    attributeRefs: AttributeRef[];
    customFieldOne: string;
    customFieldTwo: number;
}

function getDefaultWidgetConfig(): CustomWidgetConfig {
    return {
        attributeRefs: [],
        customFieldOne: "default text",
        customFieldTwo: 0
    };
}

@customElement("custom-widget")
export class CustomWidget extends OrWidget {

    // Override of widgetConfig with extended type
    protected readonly widgetConfig!: CustomWidgetConfig;

    static getManifest(): WidgetManifest {
        return {
            displayName: "Custom widget", // name to display in widget browser
            displayIcon: "gauge", // icon to display in widget browser. Uses <or-icon> and https://materialdesignicons.com
            minColumnWidth: 1,
            minColumnHeight: 1,
            getContentHtml(config: CustomWidgetConfig): OrWidget {
                return new CustomWidget(config);
            },
            getSettingsHtml(config: CustomWidgetConfig): WidgetSettings {
                return new CustomSettings(config);
            },
            getDefaultConfig(): CustomWidgetConfig {
                return getDefaultWidgetConfig();
            }
        }
    }

    public refreshContent(force: boolean) {
        // function that executes on refresh of the widget.
        // It's normally a 'silent' function that, for example, fetches the data of assets again.
    }

    protected render(): TemplateResult {
        return html`
            <span>Custom field one: </span>
            <span>${this.widgetConfig.customFieldOne}</span>
        `;
    }

}

// Settings element
// This can be placed in a seperate file if preferred.
@customElement("custom-settings")
export class CustomSettings extends WidgetSettings {

    // Override of widgetConfig with extended type
    protected readonly widgetConfig!: CustomWidgetConfig;

    protected render(): TemplateResult {
        return html`
            <span>Custom settings</span>
            <button @click="${() => this.onButtonClick()}">Click to customize text</button>
        `;
    }

    protected onButtonClick() {
        this.widgetConfig.customFieldOne = "custom text";
        this.notifyConfigUpdate();
    }
}

```
