Blocks Edit

Taking hardcore charge

Perhaps one of the best things about Tripetto is that you decide which form building blocks (e.g. question types) you want to use in the editor and collector. We offer a default set to choose from, but you can also develop your own building blocks.

In one single package a block typically both:

  • Holds the properties of a particular building block (e.g. dropdown, checkbox, etc.);
  • And facilitates the management of those properties through the editor.

For building blocks we recommend using TypeScript. We supply typings to enable optimal IntelliSense support.

This step-by-step guide for building blocks assumes a good understanding of TypeScript, object-oriented programming and webpack.

Concepts Edit

This is a good time to highlight again that we built a 2D drawing board because we think that’s the best way to visualize and edit an advanced form or survey; especially if it supports logic and conditional flow to smartly adapt to respondents’ inputs. This generally shortens forms and enhances their responsiveness. So instead of being a WYSIWYG editor, it presents the structural layout of a form’s flow and lets you easily move around building blocks on a self-organizing grid.

This is where blocks also come in. These node blocks and condition blocks essentially define building block behaviors in a form and dictate what properties and feature cards to unlock in the editor for their configuration. A block instructs the editor. And when instructed correctly by properly formatted block interfaces, the editor will know everything it needs to know to handle the pertaining building block on the drawing board and the collector can collect respondent inputs in so-called slots.

Overview

The following diagram shows the root structure of a form in the editor. This scheme is actually a pattern that can occur recursively. Each cluster can be a repeat of the shown structure, with varying numbers of branches and clusters per branch of course. You’ll recognize this recursive pattern quite easily when you start using the editor.

You’ll probably notice that the following diagram looks a lot like the diagram we used in the chapter about the collector to explain the core concepts for the structure of Tripetto. That’s because we’re talking about the exact same concepts here. Yet, we’re now taking you a step deeper into how exactly behaviors are coupled with nodes; by injecting blocks.

Editor structure

Entities

Before starting your development of blocks you’ll want to familiarize yourself with the following entities:

Nodes

These are the containers for the actual form building blocks (i.e. element types like text input, dropdown, checkbox etc.). A node is basically a placeholder for a block. The node behavior itself is defined in a block.

Clusters

One or more nodes can be placed in a so-called cluster. It is simply a collection of nodes.

Branches

One or more clusters can form a branch. A branch can be conditional. You can define certain conditions for the branch to be taken or skipped.

Conditions

A branch can contain one or more conditions, which are used to direct flow into the branch. They are evaluated when a cluster ends. Only subsequent branches with matching condition(s) are taken by the collector. Just like nodes, the conditions are actually placeholders for condition behaviors. The condition behavior itself is defined in a condition block.

Blocks

So, blocks supply a certain behavior to nodes and conditions that are used in the editor to build smart forms. As mentioned before blocks come in two flavours:

  • Node blocks: Provide building blocks for nodes (for example text input, dropdown, checkbox etc.);
  • Condition blocks: Provide building blocks for conditions (for example evaluate a certain given answer).

Slots

All data collected through the collector needs to be stored somewhere. In Tripetto we use Slots to do this. Slots are also defined by blocks. For example, if you build a text-input block, you’re probably going to retrieve a text value from an input control in your collector implementation. In that case your block should create a slot to store this text value. There are different types of slots, such as a StringSlot, BooleanSlot or NumberSlot.

Boilerplate Edit

We’ve created a boilerplate to help you start building blocks. It contains the recommended packages, boilerplate code and tasks to get things up and running smoothly. We suggest using the boilerplate as a starting point when you are going to develop a block. To do so, start by downloading and extracting the boilerplate from the following URL to any local folder you like:

https://gitlab.com/tripetto/blocks/boilerplate/repository/master/archive.zip

Next, open your terminal/command prompt and go to your newly created folder. Execute the following command (make sure you have npm and Node.js installed) to install all the required packages:

$ npm install

To start developing and testing your block run the following command:

$ npm start

This will start the editor with a (initial empty) form (stored in ./test/example.json). The editor will restart automatically with every code change.

Start with a clean installation

If you want to start from scratch or develop blocks in an existing project, you can install the editor package as a dependency using the following command:

$ npm install tripetto --save-dev

It contains the editor application as well as the TypeScript declaration files (typings) necessary for block development. The typings should be discovered automatically by your IDE when importing symbols from the tripetto module.

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

Implementing blocks Edit

Blocks for Tripetto are actually packages. A package is a directory that is described by a package.json and so is a block. The most important part of the package configuration is the entry point of the block. Normally defined by the main-field. The entry point is used when the Tripetto editor wants to load your block. If you want you can include multiple blocks in a single package. But we’ll assume for now that you create a package for each block.

The minimal package file for a block should look like this:

{
  "name": "your-block",
  "version": "1.0.0",
  "main": "index.js"
}

This example assumes the entry point of your block implementation is at index.js.

Publishing and consuming your block

When you have a block with a valid package.json, you can publish it to any registry you like. To use your block in the editor, read the instructions on how to use them in the cli tool or in the library.

Testing during development

You’ll want an easy and quick way to test your block while you are building your block. This is achieved by adding a tripetto-field with the following content to the package.json of your block:

{
  "name": "your-block",
  "version": "1.0.0",
  "main": "index.js",
  "tripetto": {
    "blocks": [
      "."
    ]
  }
}

With this the editor will load the block when you start the editor from the block folder. In the boilerplate we combined this feature with the webpack live reload plugin. This automatically restarts the editor with each change in the block code.

An example package.json with corresponding webpack configuration is displayed to the right (or a bit further down, if you’re reading this on a device with limited screen width) to get you up and running fast.

Alternative method for the entry point

Instead of supplying the entry point in the main-field you may also use the entry field of the tripetto section to do this. For example:

{
  "name": "your-block",
  "version": "1.0.0",
  "tripetto": {
    "entry": "index.js",
    "blocks": [
      "."
    ]
  }
}

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

{
  "name": "your-block",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "webpack -d && concurrently -n \"tripetto,webpack\" -c \"blue.bold,green\" -k -s \"first\" \"tripetto ./test/example.json\" \"webpack -d --watch\""
  },
  "devDependencies": {
    "tripetto": "*",
    "concurrently": "*",
    "ts-loader": "*",
    "typescript": "*",
    "webpack": "*",
    "webpack-livereload-plugin": "*"
  },
  "tripetto": {
    "blocks": [
      "."
    ]
  }
}
const webpack = require("webpack");
const webpackLiveReload = require("webpack-livereload-plugin");

module.exports = {
  entry: "./index.ts",
  output: {
    filename: "./index.js"
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: "ts-loader"
      }
    ]
  },
  resolve: {
    extensions: [".ts", ".js"]
  },
  externals: {
    "tripetto": "Tripetto"
  },
  plugins: [
    new webpackLiveReload({
      appendScriptTag: true
    })
  ]
};

Implementing a node block Edit

Node blocks are used to create node building blocks for the editor. The minimal implementation should look like this:

import {
  ConditionCreateTemplates, NodeBlock, PropertiesEditor, node
} from "tripetto";

@node("ExampleNodeBlock", "Example node block")
export class Example extends NodeBlock {
  public static readonly Icon = "icon.svg";
  public readonly Type = Example;

  public OnSlots(): void {
    // If your block needs slots, specify them here
  }

  public OnProperties(properties: PropertiesEditor): void {
    // Invoked when the user wants to edit the properties of the block.
  }

  // If your blocks supports conditions it should return the condition create templates here.
  public OnConditions(): ConditionCreateTemplates {
    return [];
  }
}

So, let’s walk through the code. We define our node block by implementing the NodeBlock abstract class, prefixed with the @node decorator to supply (meta) information about the block. The decorators, properties and methods that you can use are listed below. Some of them are required.

Decorators, properties and methods

node(id, name, version)

This decorator supplies (meta) information about your block.

Parameters
id
Specifies a unique identifier for the block.
name
Specifies a localized name for the block. We recommend using _(...) to provide the localized name (this is a convention from the gettext tools). It prepares your block package to support translations.
version
Specifies an optional version number (in semver format) for the block (defaults to 0.0.0 if omitted).
definition(mode)

Specifies whether a property needs to be read from or written to the form definition.

Parameters
mode
Specifies the mode (optional). Can be one of the following values:
  • rw: Reads and writes the property from/to the definition (default);
  • r: Only read the property from the definition;
  • w: Only write the property to the definition.

Examples:

export class Example extends NodeBlock {
  ...

  // Write and read
  @definition Description = "";

  // Only write
  @definition("w") Revision = 1;

  ...
}
name

Specifies whether a property influences the name of the block as displayed in the editor. When you apply this decorator to a property the block name will be updated when the property value is changed. This also requires you to override the Name property, because by default the name is always equal to the fixed block name specified by the @node decorator.

icon

Specifies whether a property influences the icon of the block as displayed in the editor. When you apply this decorator to a property the block icon will be updated when the property value is changed. This also requires you to override the Icon property, because by default the icon is always equal to the fixed block icon.

Name

Normally the name of the block is specified by the @node decorator. If you want to specify another name – or make your name more dynamic – you can override it in your implementation.

Icon

Specifies an icon for the block. This icon is used for block identification inside the editor. Can be an URL or base64 encoded data (data:image/svg+xml;base64,...). We recommend using an SVG-image for optimal scaling. When an SVG-image with paths is supplied, those paths are automatically colorized for the different use cases in the editor.

Type

Should always be a reference to the block constructor. Just always implement it as shown in the example.

IsRequired

Should return true when your block implements a required node. It tells the collector the node cannot be skipped.

OnSlots()

Invoked when the slots for the block are initialized. If you need slots, this is the place to define them. More details about slots and how to us them can be found here.

OnProperties(properties)

Invoked when the user wants to edit the properties of the block. Tripetto uses a special mechanism called feature cards to edit the properties of blocks. It is explained in detail later on.

Parameters
properties
Reference to the properties object which is used to supply feature cards.
OnConditions(): ConditionCreateTemplates

Invoked when the editor wants to know which conditions could be created for this block. Should return an array with condition create templates.

OnInit()

Invoked when the block is initialized. This is right after the block instance is created and before de-serialization occurs.

OnAssign(previous)

Invoked when the block is assigned to a node. If there was another block attached to the node it is supplied as parameter. You could use it to derive data from the previous block.

Parameters
previous
Reference to the previous attached block (or undefined if there was none).
OnUnassign()

Invoked when a block is unassigned from a node.

OnUpgrade(oldVersion)

Invoked when a block version stored in the form definition is older then the current block version. You can use this event to perform upgrades.

Parameters
oldVersion
Specifies the (semver) version-number stored in the form definition.
OnLabelChange()

Invoked when the label of the node is changed.

Block assignment

When a block is assigned to a node these events are generated in the following order:

  1. OnInit: The new block is initialized;
  2. OnSlots: The slots are prepared;
  3. If there is a previous block attached, this previous block receives the OnUnassign event.
  4. OnAssign: The block is assigned to the node (if there was a previous block attached it is supplied as parameter);
  5. OnUpgrade: Is invoked when the block was previously attached to the node, but with an older version of it.

Next steps

Now that you have a working simple node block, let’s add more functionality to it by:

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

import {
  ConditionCreateTemplates, Features, NodeBlock, PropertiesEditor, Slots, _, node
} from "tripetto";

import * as ICON from "./text.svg";

@node("Text", _("Text (single line)"))
export class Text extends NodeBlock {
  public static readonly Icon = ICON;
  public readonly Type = Text;
  public Value: Slots.TextSlot;

  public OnSlots(): void {
    this.Value = this.Slots.SelectOrCreate<Slots.TextSlot>("static", Slots.TextSlot, "value");
  }

  public OnProperties(properties: PropertiesEditor): void {
    Features.Static.General(properties);
    Features.Name(this, properties);
    Features.Description(this, properties);

    Features.Static.Options(properties);
    Features.Required(this.Value, properties);
    Features.Transformations(this.Value, properties);
    Features.Visibility(this, properties);
    Features.Alias(this.Value, properties);
  }

  public OnConditions(): ConditionCreateTemplates {
    return [];
  }
}

Implementing a condition block Edit

Condition blocks are used to create condition building blocks for the editor. The minimal implementation should look like this:

import {
  ConditionBlock, PropertiesEditor, condition
} from "tripetto";

@condition("ExampleConditionBlock", "Example condition block", "*")
export class Example extends ConditionBlock {
  public static readonly Icon = "icon.svg";
  public readonly Type = Example;

  public OnProperties(properties: PropertiesEditor): void {
    // Invoked when the user wants to edit the properties of the block.
  }
}

Let’s have a closer look at this. We define our condition block by implementing the ConditionBlock abstract class, prefixed with the @condition decorator to supply (meta) information about the block. The decorators, properties and methods that you can use are listed below. Some of them are required.

Decorators, properties and methods

condition(id, name, context, version)

This decorator supplies (meta) information about your block.

Parameters
id
Specifies a unique identifier for the block.
name
Specifies a localized name for the block. We recommend using _(...) to provide the localized name (this is a convention from the gettext tools). It prepares your block package to support translations.
context
Specifies the context for the condition block. Can be one of the following values:
  • *: Condition relates to nothing and can be placed anywhere;
  • cluster: Condition relates to a cluster;
  • branch: Condition relates to a branch;
  • node: Condition relates to a node;
  • Reference to a certain NodeBlock block implementation (condition relates to nodes of the specified block);
  • Identifier string of a certain NodeBlock block implementation (condition relates to nodes of the specified block).
version
Specifies an optional version number (in semver format) for the block (defaults to 0.0.0 if omitted).
definition(mode)

Specifies whether a property needs to be read from or written to the form definition.

Parameters
mode
Specifies the mode (optional). Can be one of the following values:
  • rw: Reads and writes the property from/to the definition (default);
  • r: Only read the property from the definition;
  • w: Only write the property to the definition.

Examples:

export class Example extends ConditionBlock {
  // Write and read
  @definition Description = "";

  // Only write
  @definition("w") Revision = 1;
}
name

Specifies whether a property influences the name of the block as displayed in the editor. When you apply this decorator to a property the block name will be updated when the property value is changed. This requires you to override the Name property, because by default the name is always equal to the fixed block name specified by the @condition decorator.

icon

Specifies whether a property influences the icon of the block as displayed in the editor. When you apply this decorator to a property the block icon will be updated when the property value is changed. This requires you to override the Icon property, because by default the icon is always equal to the fixed block icon.

Name

Normally the name of the block is specified by the @condition decorator. If you want to specify another name – or make your name more dynamic – you can override it in your implementation.

Icon

Specifies an icon for the block. This icon is used for block identification inside the editor. Can be an URL or base64 encoded data (data:image/svg+xml;base64,...). We recommend using an SVG-image for optimal scaling. When an SVG-image with paths is supplied, those paths are automatically colorized for the different use cases in the editor.

Type

Should always be a reference to the block constructor. Just always implement it as shown in the example.

Node

Contains the node reference if the condition is attached to a node or undefined if not.

Cluster

Contains the cluster reference if the condition is attached to a cluster or undefined if not.

Branch

Contains the branch reference if the condition is attached to a branch or undefined if not.

Slot

Contains the slot reference if the condition is attached to a slot or undefined if not.

OnProperties(properties)

Invoked when the user wants to edit the properties of the block. Tripetto uses a special mechanism called feature cards to edit the properties of blocks. It is explained in detail later on.

Parameters
properties
Reference to the properties object which is used to supply feature cards.
OnInit()

Invoked when the block is initialized. This is right after the block instance is created and before de-serialization occurs.

OnAssign(previous)

Invoked when the block is assigned to a condition. If there was another block attached to the condition it is supplied as parameter. You could use it to derive data from the previous block.

Parameters
previous
Reference to the previous attached block (or undefined if there was none).
OnUnassign()

Invoked when a block is unassigned from a condition.

OnUpgrade(oldVersion)

Invoked when a block version stored in the form definition is older then the current block version. You can use this event to perform upgrades.

Parameters
oldVersion
Specifies the (semver) version-number stored in the form definition.
OnNameChange()

Invoked when the name of the condition block is changed.

Block assignment

When a block is assigned to a condition these events are generated in the following order:

  1. OnInit: The new block is initialized;
  2. If there is a previous block attached, this previous block receives the OnUnassign-event.
  3. OnAssign: The block is assigned to the condition (if there was a previous block attached it is supplied as parameter);
  4. OnUpgrade: Is invoked when the block was previously attached to the condition, but with an older version of it.

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

Working with interfaces Edit

If you have read the collector documentation, you probably already noticed that Tripetto requires you to implement each block in two domains:

  • Editor domain: The part of the block that will allow the use of it for smart form building in the editor;
  • Collector domain: The part of the block that renders it to a visual component inside an actual form in a website or application. If you have different collectors with different visual styles, you probably need multiple implementations for each block.

There is no technical overlap between the two implementation domains, except for the block’s properties. These properties describe the behavior of your block (they are set with the editor and consumed by the collector) and act as the block contract between the implementation domains.

To avoid duplicate interface declarations or mismatches between the properties, we suggest creating a single interface inside your editor block implementation. This interface then acts as the single point of truth and should be exposed by your editor block package, so you can use it inside each collector implementation. This way the block properties are declared in a single place, making it easier to maintain and more consistent.

Blocks diagram

Editor domain

The following example shows how to achieve this in the editor domain. First off we define the interface in a separate type declaration file, for example interface.d.ts. We consume the required symbols from the module @tripetto/map. This module is included in both the tripetto and the tripetto-collector package. So it works in both domains.

import { INodeBlock } from "@tripetto/map";

export interface IExampleProperties extends INodeBlock {
  ...

  // Define a property
  Color: string;

  ...
}

Next, implement a block (in this example index.ts) and feed the properties interface as type. BTW, this is optional and only necessary if you implement a custom (de)serialization function.

import { IExampleProperties } from "./interface";

@node("ExampleBlock", "Example block")
export class Example extends NodeBlock<IExampleProperties> {
  ...

  // Make the property part of the form definition
  @definition Color = "red";

  ...
}

And this is all you need to do in the editor domain. Make sure to reference your type definition inside your package.json as follows.

{
  "name": "example-block",
  "version": "1.0.0",
  "main": "index.js",
  "types": "interface.d.ts"
}

It is important to only import symbols from the @tripetto/map module as this module is shared between the editor and collector domain. You cannot import symbols from the tripetto package as this would lead to compilation errors when consuming block interfaces in the collector domain which depend on the tripetto package.

Collector domain

Now you can use the interface that is declared inside the editor block package in your collector implementation. Also make sure to add the block package to your collector. Then you can import the interface type and feed it into your collector block.

import { IExampleProperties } from "example-block";

@node("ExampleBlock")
export class ExampleBlock extends NodeBlock<void, IExampleProperties> {
  ...

  public OnRender(): void {
    // Use the property
    console.log(this.Props.Color); // Outputs `red`
  }

  ...
}

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

Slots Edit

Let’s talk about slots now! Because they are a fundamental part of Tripetto. Slots are used for the actual storage of all data retrieved from respondents through the collector. A slot contains all the settings related to the data retrieval; whether data input for it is required, the different data formatting options and much more.

Blocks each hold their own definition of any number of slots and their characteristics in the block implementation in the editor. So if, for example, you create a block for retrieving a string value with a text input control, you would define a single slot of a type StringSlot.

All slots combined make up the complete slot collection of a form. And this slot collection ultimately contains all collected data. Slots are automatically stored inside the form definition. In the collector domain the slot collection is available to store collected values.

Slots diagram

Slot types

The following slot types are available:

  • BooleanSlot: Stores boolean values;
  • NumberSlot: Stores numeric values;
  • StringSlot: Stores string values;
  • DateSlot: Stores dates;
  • TextSlot: Stores string values with optional transformations and formatting;
  • NumericSlot: Stores numeric values with formatting like number of decimals, prefix, suffix, etc.

Slot kinds

There are four kind of slots available:

  • static: Indicates a static slot;
  • dynamic: Indicates a slot with a dynamic origin (for example when you want to generate a slot for a editable list of values);
  • feature: Indicates a slot for a certain feature (for example a slot for an optional comment);
  • meta: Indicates a slot which contains meta data.

Creating slots

Slots should be created by your block when the OnSlots event is invoked. Inside your node block slots are available through a collection named this.Slots. You can select, create and delete slots. For example:

import { Slots } from "tripetto";

@node("TextInput", "Text input")
export class TextInput extends NodeBlock {
  ...

  public OnSlots(): void {
    // Create a static slot to store a string with name `value`
    this.Slots.SelectOrCreate("static", Slots.StringSlot, "value");
  }

  ...
}

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

Feature cards Edit

Feature cards are used inside the editor to manage the properties of blocks. They are designed specifically to just show the settings needed in average scenarios and add only the desired additional settings options dynamically on the go. The idea behind this approach is that users only select the block features in the editor they actually want to configure and prevent the typical clutter. Hence the name feature cards.

To speed up block development for you, we also supply a list of common features. Those features implement common configuration options, such as the name of the block, description, explanation, etc.

The optional features are listed to the left side of the screen. The configuration options for activated features are presented inside so-called Cards, which appear to the right of the feature list

Creating features and cards

Features are implemented inside the OnProperties method of your block. This method receives a properties argument which holds the features. Take a look at the following example. It shows you how to add a feature with an empty form card (Forms.Form).

...
public OnProperties(properties: PropertiesEditor): void {
  properties.Features.Feature(
    "Feature example",
    new Forms.Form({
      Title: "Example",
      Controls: []
    }));
}
...

This will show the feature in the feature list. When a feature is selected, the appropriate form card will be displayed. Next step is to add actual controls to the form card.

Building feature cards

Tripetto contains a collection of form controls that can be used to build form cards for the feature cards in the editor. The table below shows the available controls. These controls can be used in the Controls array of the form card.

Form control Description
Forms.Button Button form control.
Forms.Checkbox Checkbox form control.
Forms.Date Date/time form control.
Forms.Dropdown Dropdown form control.
Forms.Email Email form control.
Forms.HTML HTML form control.
Forms.Notification Notification form control.
Forms.Numeric Numeric form control.
Forms.Radiobutton Radiobutton form control.
Forms.Spacer Spacer form control.
Forms.Static Static form control.
Forms.Text Text form control.
Forms.Upload File upload control.
Forms.Group Form group.

Example

The following example shows how to use the controls.

properties.Features.Feature(
  "Feature example",
  new Forms.Form({
    Title: "Example",
    Controls: [
      new Forms.Text("single").Label("This is a text input"),
      new Forms.Checkbox("This is a checkbox")
    ]
  }));

Automatically retrieving data from controls

If you dive deeper into the controls, you will see each control has events you can bind to. One of these events is invoked when the data of the control changes. But there is an easier way to retrieve data from controls by using the stream option of supported controls. If a control has a Stream method it supports data streaming. The stream method takes 3 parameters:

  • A reference to the object that holds the property the control is going to set;
  • The name of the property;
  • The default value that’s applied as long as the control has no value.

Let’s extend our example with the control stream.

const Example = {
    Text: "",
    Checkbox: false
};

properties.Features.Feature(
  "Feature example",
  new Forms.Form({
    Title: "Example",
    Controls: [
      new Forms.Text(
          "single",
          Forms.Text.Stream(Example, "Text", "")),
      new Forms.Checkbox(
          "This is a checkbox",
          Forms.Checkbox.Stream(Example, "Checkbox", false)),
    ]
  }));

Now the supplied properties Text and Checked will be automatically updated when the data of the control changes.

Common features

Common features are out-of-the-box available sets of form controls to manage often used properties. The following common features are available.

Feature Explanation
Features.Static Contains common feature titles.
Features.Name Sets the name of a node.
Features.Description Sets the description of a node.
Features.Placeholder Sets the placeholder of a node.
Features.Explanation Sets the explanation of a node.
Features.Visibility Sets the visibility of a node.
Features.Required Sets the slot requirements.
Features.Alias Sets the slot alias.
Features.Transformations Sets the transformation for a TextSlot.
Features.Collection Collection editor for creating collections.

Example

Common features can be added directly to your OnProperties implementation.

...
public OnProperties(properties: PropertiesEditor): void {
    // Add the `General` title
    Features.Static.General(properties);

    // If you implement a `Name` and a `Placeholder`, you can entangle them by feeding the name feature to the placeholder feature.
    const pName = Features.Name(this, properties);

    Features.Description(this, properties);
    Features.Placeholder(this, properties, pName);
    Features.Explanation(this, properties);

    // Add the `Option` title
    Features.Static.Options(properties);
    Features.Visibility(this, properties);
}
...

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

Collections Edit

Let’s say you’re building some kind of dropdown block. Dropdowns typically hold multiple options to choose from. And so you need a way to add and edit those options in the editor. We’ve got you covered! You need collections and they are quite easy to use. First of all you need to define the type of items for your collection.

Screen recording - Collections

Have a look at the following example where we define the type DropdownOption for our dropdown example.

import { Collections, PropertiesEditor } from "tripetto";

export class DropdownOption extends Collections.Item {
    public Name = "";

    public OnProperties(properties: PropertiesEditor): void {
    }
}

As you can see a collection item should extend the abstract class Collections.Item and implement the 2 required members: Name and OnProperties. The details of these members are shown below.

Decorators, properties and methods

Name

This required property should contain the name of the item.

OnProperties(properties)

Invoked when the properties editor for the item is shown. Tripetto uses a special mechanism called feature cards to edit the properties of items. It works exactly the same as it does for blocks.

Parameters
properties
Reference to the properties object which is used to supply feature cards.

Implementing the collection

Next step is to add the actual collection to your block. To do so you need to add a member to your block class.

import { Collections } from "tripetto";

@node("Dropdown", "Dropdown")
export class Dropdown extends NodeBlock {
  ...

  // Add a member and initialize the collection
  @definition public Options = Collections.Of(DropdownOption);

  ...
}

When you prefix the collection member with the @definition decorator, the collection will be automatically stored in the form definition!

Showing the collection in the feature card

The final step is to show the collection editor in the feature card of your block. To do so, you should add a feature for the collection to your OnProperties implementation. Use the common Features.Collection card (a collection editor). It supports adding, editing and deleting items.

properties.Features.Card(
  new Features.Collection({
    // Supply our collection to the card
    Collection: this.Options,

    // Give the collection editor a nice title
    Title: _("Dropdown options")
  })
);

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

Condition create templates Edit

When you implement a node block, it is possible to supply condition create templates to the editor. These templates can be used to allow the creation of actual conditions for your block in the editor with one click. The templates will be shown when the user wants to add a branch to a certain cluster. To make this work you can return an array with condition create templates from your node block. This is done using the OnConditionTemplates method.

...
public OnConditions(): ConditionCreateTemplates {
    return [
        {
            Label: "Create example condition",
            Icon: "condition.svg",
            Command: (pCondition: Condition) => {
                // We assume you have a condition block named `ExampleCondition`
                const pExampleCondition = pCondition.Assign(ExampleCondition);

                if (pExampleCondition) {
                    // Attach the current node to the condition
                    pExampleCondition.Node = this.Node;
                }
            }
        }
    ];
}
...

Grouping condition templates

You can group condition templates by adding an item to the condition template array with just a Label property in it. For example:

...
public OnConditions(): ConditionCreateTemplates {
    return [
        {
            Label: "Example group"
        }
    ];
}
...

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

Localization Edit

We use the gettext convention for the localization of Tripetto and its blocks. You should enclose all your labels/texts that need translation using one of the following functions.

_(str, …arguments): string

The _ function is actually an alias for the gettext function. We use it because it is shorter and more distinctive. Supply your (to be translated) string as the first argument. Optional arguments can be referenced in your string using the percent sign followed by the argument index %n. The first argument is referenced with %1.

Parameters
str
Specifies the string that needs to be translated.
...arguments
Optional arguments that can be referenced using %n where n is the argument number (the first argument is %1).

Example:

const label = _("Lorem %1", "ipsum");

console.log(label); // Outputs `Lorem ipsum`
_n(single, plural, count, …arguments): string

Translates a plural string (short version for ngettext).

Parameters
single
Specifies the string containing the single message that needs to be translated.
plural
Specifies the string containing the plural message that needs to be translated.
count
Specifies the count for the plural.
...arguments
Optional string arguments that can be referenced in the translation string using the percent sign followed by the argument index %n. The count value count is automatically included as the first argument (%1).

Example:

const label = _n("1 car", "%1 cars", 2);

console.log(label); // Outputs `2 cars`

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

Translating your block Edit

If you have followed the localization guidelines from the previous chapter, your block is ready to be translated. Now you can follow these steps to get you going (we assume you’ve started your block implementation using the boilerplate):

  1. First, update the ./translations/sources file and add the source files that need to be translated (each file on a separate line);
  2. Next, generate the POT file by executing the following command: npm run translations:pot (this command uses the xgettext tool, make sure it is installed!);
  3. Verify the file ./translations/template.pot is generated. It contains the strings to be translated. Now use this template to create translations. You can use a tool for this (like Poedit) or send the file to a translation service. Each translation should be stored in a PO-file. The name of the PO-file should be the locale of that language, for example nl.PO;
  4. Add the PO-files to the ./translations folder of your block;
  5. Convert the PO-files to JSON-files by executing the command: npm run translations:po2json;
  6. Make sure to include the generated JSON files in the ./translations folder of your distributable block package.

Examples Edit

All of our blocks are open source and published under the MIT license. Have a blast!


Block boilerplate for creating new blocks

gitlab.com/tripetto/blocks/boilerplate


Checkbox

Standard checkbox block.


Checkboxes

List of checkboxes.


Dropdown

Standard dropdown block.


E-mail

Standard e-mail block.


Number

Standard number block


Password

Standard password block


Radiobuttons

Standard radiobutton block


Text

Standard text block


Textarea

Standard textarea block.


URL

Standard URL block.


This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.

Community Edit

We hope other enthusiasts will also start to develop blocks for Tripetto in the open source domain. We have a special repository where we collect a list of community driven blocks and collectors.

Add your own block to the list

If you have created a block yourself, create a PR and add yours to the list.

This documentation is updated as we continue our work on Tripetto and build a community. Please let us know if you run into issues or need additional information. We’re more than happy to keep you going and also improve the documentation.