Exploring Outerbase: A walkthrough for creating a Plugin.

A tutorial on building plugin to view data in a user-defined way.

What is Outerbase?

Outerbase is an interface for a database. It's a new and fun way to interact with a database as well as use it in your next project. Users can connect to their database and add functionality on top of it, make outerbase itself a server to serve resources to your front-end application, create dashboards from the database on the go and many more features that you can explore on its official page.

One of its great features includes the plugin ecosystem that enables the users to tailor the way they want their data to be seen or interacted with.

Plugin Ecosystem

A plugin enables users to enhance the database with greater flexibility and enriched experiences.

There are two types of plugins that it currently:

  1. Table Plugin: Allows users to customize the view from grid-based data to the way they want it.

  2. Column Plugin: Having control over each cell of the table and how it renders the data in it within a specific table column.

I like to think of the plugin as a UI to interact with data. And plugin allows it in two ways - by creating UI for the whole table itself and also in a column manner.

An example of table UI would be viewing a todo data table in a Kanban-style board or a location-oriented table data in Map view. For column-based UI a previewer for columns having url based data or adding a highlighter for null values cell. The possibilities are endless.

Creating Plugin

Prerequisite

A basic knowledge of HTML, CSS and JS is much appreciated.

For better understanding a little bit of research and practical knowledge of DOM manipulation API like getElementById(), querySelector(), document.createElement(), addEventListener() and of shadowDOM will also help in the future.

Theory - Tools to build

Privileges

Before starting to create a plugin we need to let the database know the privileges we'll accessing beforehand likecellValues, rowValues, tableValues etc just like social login. All the values are explained below just go through it once as we'll come back again in the code part.

  • cellValue: The value of the cell that the plugin is being rendered in. //

  • rowValue: The value of the row that the plugin is being rendered in.

  • tableValue: The value of the table that the plugin is being rendered in.

  • tableSchemaValue: The schema of the table that the plugin is being rendered in.

  • databaseSchemaValue: The schema of the database that the plugin is being rendered in.

  • configuration: The configuration object that the user specified when installing the plugin.

These privileges let the users know beforehand that this plugin will need to access this much information for it to work similar to a social media application like Instagram or YouTube accessing your camera and microphone for using a feature of going live.

Outerbase provides classes for working on a plugin be it a table or column. They have different names but work similar.

Plugin Classes:

Here is the list of classes used in Table/Cell based on the plugin you created. I have grouped them according to their usage according to my understanding.

  • Config class: Provides extra information to the plugin to help render data. And it's compulsory to declare in the plugin for getting the data attribute as data in a table.

      class OuterbasePluginConfig_$PLUGIN_ID{
      //common class declared in both column and table plugin
    
      //initalize custom properties here
      constructor() {
    
          }
      }
    
  • Table/Cell Specific Configuration: Helps to set variables/information for the Cell/Table specific plugin. Examples would be - having options to change the background color of a column or specific value in cell config. And it gets its power to render from the HTMLElement class.

      class OuterbasePlugin{Cell/Table}Configuration_$PLUGIN_ID extends HTMLElement {
      constructor() {
          super();
          }
      }
    
  • RenderClass: Handles rendering of the table/cell itself and its interaction(click, focus, etc.) with the user by extending the HTMLElementClass.

      class OuterbasePlugin{Cell/Table}_$PLUGIN_ID extends HTMLElement{
      /** It has a constructor and different methods to render 
      the view of the data itself like background, render tags inside cell 
      or view itself
      */
      constructor() {
          super();
          }
      //NOTE: IT DOES NOT HANDLE THE EDITING PART. 
      }
    
  • EditorClass: It handles UI and interaction for the editing of data.

      class OuterbasePlugin{Cell/Table}Editor_$PLUGIN_ID extends HTMLElement{
      /** It has a constructor and different methods to render 
      the view of the data itself like background, render tags inside cell 
      or view itself
      */
      constructor() {
          super();
          }
      }
    

Coding: Let's dive in

So we are going to build a simple image preview plugin. It's going to look something like this.

Requirements:

  • Shows URL link as it is in the column data, on a single click it opens up a modal that loads an image and an input tag with that URL and a save button to edit or update with a new URL.

Features:

  1. Shows URL column data as it is.

  2. Should open up a modal on single click event.

  3. Modal should have 3 things image, input and a button with background blur or something to pop out

  4. Should update the URL and preview it on the save button. (Try it yourself)

FEATURE 1: Showing URL column data as it is.

For this feature plugin has provided a class called OuterbasePluginCell_$PLUGIN_ID which you can relearn in the Theory part above.

We'll keep it empty as we are not going to change anything on how it shows the data.

class OuterbasePluginCell_$PLUGIN_ID extends HTMLElement{
constructor() {
    super();
    }
}

FEATURE 2: Should open up a modal on single click event

Let's divide it into three parts creating a modal (HTML/CSS), handling the click event (JS) part and making it work in the plugin.

I have confidence you know HTML and CSS so can you create the modal for me? Make it creative and share it in the comment, or even better if you are lazy generate it with AI.

Here's my modal UI:

const templateEditor_$PLUGIN_ID= document.createElement("template");
templateEditor_$PLUGIN_ID.innerHTML = `
<div class="modal-overlay">
    <div class="modal">
        <img id="modal-image" alt="Image" class="modal-image">
        <input type="url" id="url-input" class="url-input" placeholder="Don't tell anyone its AI generated ;)">
        <button id="save-button"   class="save-button">Save URL</button>
    </div>
</div>
`

Now to the second part, add it to the editor class to clone the content of the node and render it in shadow dom using OuterbasePluginEditor_$PLUGIN_ID class.

class OuterbasePluginEditor_$PLUGIN_ID extends HTMLElement {
  constructor() {
    //to use HTMLElemement Properties (Specifically shadowDOM)
    super();

    this.shadow = this.attachShadow({ mode: "open" });
    //attach our Modal UI to shadow dom
    this.shadow.appendChild(templateEditor_$PLUGIN_ID.content.cloneNode(true));
    //initalize config class for getting values of the cell
    this.config = new OuterbasePluginConfig_$PLUGIN_ID(
      JSON.parse(this.getAttribute("configuration")),
    );
  }

  connectedCallback() {
    var modalImage = this.shadow.getElementById("modal-image");
    var urlInputEl = this.shadow.getElementById("url-input");
    //add source and values to image and input tag if we get the element
    if (modalImage && urlInputEl) {
      modalImage.src = this.getAttribute("cellValue");
      urlInputEl.value = this.getAttribute("cellValue");
    }
  }
}

I guess FEATURE 3 is automatically fulfilled. But we haven't handled click event for opening the modal. Let's do it.

A way to click on the individual cell that should trigger an event that should open the modal.

So the way I found is by updating the cell view to render the URL on the cell view using the previous OuterbasePluginCell_$PLUGIN_ID class. Same way as we did earlier an HTML/CSS for UI, JS for interaction and a Plugin class for making it work together.

HTML/CSS

  •       //creating a template 
          const templateCell_$PLUGIN_ID = document.createElement("template");
          //adding styles and tags inside the template tags innerHTML
          templateCell_$PLUGIN_ID.innerHTML = `
              <style>
                  #container-image-url{
                      margin: 0;
                      padding: 0 12px;
                  }
              </style>
    
              <div id="container">
                  <p id="container-image-url"></p>
              </div>
          `;
    

JS and Plugin Class

class OuterbasePluginCell_$PLUGIN_ID extends HTMLElement{
    //smiliar to previours editor view class constructor except the child 
    //that is getting appended to the nodoe
    constructor() {
        //to use HTMLElemement Properties (Specifically shadowDOM)
        super();

        this.shadow = this.attachShadow({ mode: "open" });
        //attach our Modal UI to shadow dom
        this.shadow.appendChild(templateCell_$PLUGIN_ID.content.cloneNode(true));
        //initalize config class for getting values of the cell
        this.config = new OuterbasePluginConfig_$PLUGIN_ID(
          JSON.parse(this.getAttribute("configuration"))
        );
      }

    connectedCallback() {
        const containerImageUrl = this.shadow.getElementById("container-image-url");
        //assign value as innerHTML or as innerTextContent
        containerImageUrl.innerHTML = this.getAttribute("cellValue");

        if (containerImageUrl) {
        //IMPORTANT on click event trigger a callCustomEvent property
        containerImageUrl.addEventListener("click", () => {
                this.callCustomEvent({
                action: "onedit",
                value: containerImageUrl.innerText,
            });
        });
        }
      }
    //IMPORTANT property to create the custom event and dispatch it.
  callCustomEvent(data) {
    const event = new CustomEvent("custom-change", {
      detail: data,
      bubbles: true,
      composed: true,
    });

    this.dispatchEvent(event);
  }
}

It's completed let's deploy it.

Deployment & Testing: Making it live !!

Deployment

Outerbase is working on providing a marketplace to share and download plugins, but for now, we can use it for ourselves by following the steps below:

  • Workspace -> Marketplace section -> Custom Plugin -> Create plugin

A dialog box will open up that will ask for the plugin information (name, code, table or column, icon and description) and configuration (privileges that we learned in theory).

So fill in all the details and copy and paste the whole code in the Plugin code text area. It will look similar when filling in details.

Testing & Fixing

Steps to add column in table

  • Go to that column -> click on the dropdown arrow -> Add Plugin -> Select the one to apply

Does it work? It won't as there are errors and one of them I found after some time was

I forgot to let the document (HTML page) know about the custom element. So we can fix it by adding two lines at the end.

//IMPORTANT TO LET THE DOCUMENT KNOW ABOUT CUSTOMELEMENTS 
  window.customElements.define(
    "outerbase-plugin-cell-$PLUGIN_ID",
    OuterbasePluginCell_$PLUGIN_ID
  );
  window.customElements.define(
    "outerbase-plugin-editor-$PLUGIN_ID",
    OuterbasePluginEditor_$PLUGIN_ID
  );

Let's try it again updating the code of the plugin.

But this time we got

It seems that the connectedCallback method on `this.getAttriubte(cellValue)` is null.

We have not added OuterbasePluginConfig_$PLUGIN_ID which is important. And by adding it the plugin will work.

How will it look? That's a mystery for you to solve. I'll share the repo here.

Further Improvements

I have added a few more features like better UI, loader, changing url in the input and saving it will load the new image in the modal. One can check that out in the same repository to see how it's working.

Still, it needs work on other features like updating and fixing CSS on viewing the first and last column as it flows out of view. Feel free to contribute to the repository.

Resources

Feel free to create a plugin yourself and share it on the Outerbase discord.

Did you find this article valuable?

Support Anuj Mistry by becoming a sponsor. Any amount is appreciated!