Skip to content

Latest commit

 

History

History
233 lines (190 loc) · 7.16 KB

how_to_create_a_new_component.md

File metadata and controls

233 lines (190 loc) · 7.16 KB

Create a new web component

This tutorial walks you through the process of building a new web component, whether it is an entirely new type of annotation interaction or an improvement of an already existing one.

Prerequisites

Complete beginner

A few key concepts before starting this tutorial:

Key concept What's for?
npm Tool to import external libraies required in the project and easily run build commands.
package.json Config settings for npm with the list of libraries the project needs to import.
package-lock.json File automatically generated by npm to keep track of exact version numbers. You will likely never need to go in here.
node_modules/ Folder where npm stores the external libraries.
Typescript Typed javascript language for easier coding.
tsconfig.json Config settings for Typescript and how the compiler should convert it into Javascript.
Webpack Tool to bundle the multiple libraries generated into one Javascript file.
webpack.config.js Config settings for WebPack.

Some reading material:

  • Article explaining the main Javascript concepts here.

Graphical libraries

In addition to classical javascript tools mentioned above, we use three amazing libraries in Pixano:

Library What's for?
Lit Element Easy building of Web Components. If you are not familiar with this tool, please follow their tutorial.
PixiJS Create rich, interactive 2d graphics elements. If you intend to edit the interactions on the canvas, please follow their tutorial.
ThreeJS Create 3d scenes. If you intend to edit the 3d scene, please follow their tutorial.

Create a component from scratch

Let's create a simple component that displays an image. A minimal setup to do so is as follows:

1. Create the component

Create a new file pxn-my-element.ts under the graphics-2d package:

import { LitElement, html, customElement, property } from 'lit-element';

@customElement('pxn-my-element' as any)
export class MyElement extends LitElement {

    // input image path
    @property({type: String})
    public path: string = '';

    /**
    * Render the element template.
    */
    render() {
        /**
        * `render` must return a lit-html `TemplateResult`.
        *
        * To create a `TemplateResult`, tag a JavaScript template literal
        * with the `html` helper function:
        */
        return html`
        <img src=${this.path}>
        `;
    }
}

2. Add it to the serverless-demo

  • In the file 'demo/plugins_index.js':
    • add the component name to the 'pluginsList'
    • add a default annotation schema for it in 'defaultLabelValues'
  • In the file 'demo/serverless-demo.js':
    • add the component call in the 'plugin' function
    • add the tools used in the component in the 'tools' function
    • add the component in an adapted case (or create a new case if needed) in the handlers: 'onCreate', 'onDelete', 'onSelection', 'onUpdate', 'onAttributeChanged'

3. Optionnal : create a isolated demo

Create a simple demo application that imports and instanciates the component to check that it behaves as expected. Create a my-element folder in demos with the following files:

demos
└── my-element
    ├── my-demo.js
    ├── image.jpg
    ├── index.html
    └── webpack.config.js
  • Create an entrypoint index.html file:
<!doctype html>
<html>
<head>
    <title>My Demo</title>
</head>
<body>
    <!-- Use Web Components in your HTML like regular built-in elements. -->
    <my-demo></my-demo>
    <script src="./my-demo-bundle.js"></script>
</body>
</html>
  • Add its javascript code in a my-demo.js:
import { html, LitElement} from 'lit-element';
import '@pixano/graphics-2d/lib/pxn-my-element';

class MyDemo extends LitElement {

  render() {
    return html`<pxn-my-element path="image.jpg"></pxn-my-element>`;
  }
}
customElements.define('my-demo', MyDemo);
  • Create a package.json to define the dependencies of the demo:
{
    "name": "demo-my-element",
    "version": "0.5.0",
    "private": true,
    "description": "Demo",
    "scripts": {
        "build": "webpack --config webpack.config.js",
        "watch": "webpack --config webpack.config.js --watch"
    },
    "devDependencies": {
        "source-map-loader": "^0.2.4",
        "webpack": "4.44.2",
        "webpack-cli": "3.3.12"
    },
    "dependencies": {
        "@pixano/graphics-2d": "0.5.0",
        "@webcomponents/webcomponentsjs": "^2.4.3",
        "lit-element": "^2.3.1"
    }
}
  • Create webpack.config.js to define the parameters of the app bundling:
const path = require('path');

module.exports = {
  mode: 'development',
  entry: path.resolve(__dirname, 'my-demo.js'),
  output: {
    path: path.resolve(__dirname),
    filename: 'my-demo-bundle.js'
  },
  devtool: 'eval-source-map',
  module: {
    rules: [
      {
        test: /\.(js|mjs|jsx|ts|tsx)$/,
        use: ["source-map-loader"],
        enforce: "pre"
      }
    ]
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js']
  }
};

Run the demo

# This assumes you already have installed
# the roots dependancies (npm i)
npm run bootstrap
# Watch src file change
npm run watch
# In another terminal, serve the demo app:
npx serve demo

Inherit from existing annotation element

Let's create a component that inherits pxn-polygon, adding a way to create a polygon from a click fed to a segmentation algorithm through a HTTP request.

1. Create the component

Create a new file pxn-http-polygon.ts under the graphics-2d package:

import { LitElement, html, customElement, property } from 'lit-element';
import { Polygon } from "./pxn-polygon";

@customElement('pxn-http-polygon' as any)
export class HttpPolygon extends Polygon {

    // segmentation url
    @property({type: String})
    public segurl: string = 'localhost:4000';

    constructor() {
      super();
      this.shManager.setController('smart-create', new SmartPolygonCreateController(this.renderer, this.shapes));
    }
}

/**
 * Inherit RectanglesManager to handle smart rectangle creation.
 */
class SmartPolygonCreateController extends ShapeCreateController {

  protected onRootDown(evt: any) {
    const click = this.renderer.getPosition(evt.data);
    fetch(this.segurl, {
      method: "POST",
      body: {click, image: this.renderer.imageBase64 }
    }).then((res) => res.json())
      .then((pts: number[]) => {
        this.shapes.add(observable({
          id: Math.random().toString(36),
          geometry: { type: "polygon", vertices: pts }
        }));
      });
  }
}

2. Create its demo

Go through the above from scratch demo. Edit url as you want.