On this page ...

Editor Customization

You can customize the editor by adding projections, actions, and/or external Svelte components. Before you dive into all this, we suggest that you become familiar with the Box Model, as explained in the Freon Editor Framework.

Generated Box Providers

The generated editor is implemented by a set of box providers. Every box provider is capable of returning the box for a specific type of AST node. For instance, the generated box provider for the EuroLiteral concept contains the following two methods:

// Insurance/src/freon/editor/gen/EuroLiteralBoxProvider.ts#L23-L62

The first method shows that any custom projection is always handled first. The second method returns a Box based on the current content of this._node. Note that the implementation relies heavily on the predefined box types, as well as on the predefined helper createDefaultExpressionBox, which returns the correct box for a (non-binary) expression concept.

Writing a Custom Projection

Writing a custom projection does not require the implementation of a complete box provider. Instead, a custom projection (set) is a series of methods, each returning a box object for an AST node. Every custom method needs to be registered in nodeTypeToBoxMethod.

Custom table projections are not yet supported.

In the example below, we copy the getDefault method and adjust it to display an SVG Euro symbol instead of the string ‘EUR’. Note that the method checks whether it is used for concepts of type EuroLiteral.

// Insurance/src/freon/editor/CustomInsuranceModelProjection.ts#L24-L85

    // add your custom methods here

    // BOX_FOR_CONCEPT(node: NAME_OF_CONCEPT) : Box { ... }

    // TABLE_DEFINITION_FOR_CONCEPT() : FreTableDefinition { ... }
}
Use another filename and/or location
You can rename the file ~/freon/editor/CustomYourLanguageNameProjection.ts, and/or place it in another location. In that case, you need to adjust the file ~/freon/config/FreonConfiguration. See Adding TypeScript Files.

Writing Custom Actions

When you have created your own custom projection, this projection is exactly that: a projection. Other than the built-in default behavior, there are no actions defined yet. Actions are necessary to enable the user to change the model and add elements to it. The projections only describe what you will see in the editor. Actions determine what you can do, how you interact with the editor.

As a convenience, the file ~/freon/editor/CustomYourLanguageNameActions.ts is generated, which is a placeholder for your own actions written in TypeScript. This file defines two constants for different kinds of actions. The constant MANUAL_CUSTOM_ACTIONS is an array that contains all actions to be performed on normal concepts. Actions on binary expressions should be added to the array MANUAL_BINARY_EXPRESSION_ACTIONS. When you define these constants, Freon merges your actions with the default actions.

Any CustomAction must implement the FreCustomAction interface. An easy way to create one is to use the predefined method FreCustomAction.create(), which takes a partial CustomAction object as a parameter. The most important properties of the CustomAction object are the action itself, and the box roles that indicate the action could be activated.

In the example below two actions are added, one for the button that was added to the BaseProduct projection in Buttons, and one for the buttons that were added to the table projection of InsurancePart concepts in Icon Buttons. The first was defined with the box role “MyButton-role”, the second with “MyTableButton-role”. In this example, the action implementation is a simple alert shown to the user.

// Insurance/src/custom/editor/CustomInsuranceModelActions.ts#L19-L82

export class CustomInsuranceModelActions implements FreCombinedActions {
    binaryExpressionActions: FreCreateBinaryExpressionAction[] = MANUAL_BINARY_EXPRESSION_ACTIONS;
    customActions: FreCustomAction[] = MANUAL_CUSTOM_ACTIONS;
}

export const MANUAL_BINARY_EXPRESSION_ACTIONS: FreCreateBinaryExpressionAction[] = [
    // Add your own custom binary expression actions here
];

export const MANUAL_CUSTOM_ACTIONS: FreCustomAction[] = [
    // Add your own custom behavior here
    FreCustomAction.create({
        activeInBoxRoles: ["MyButton-role"],
        action: (box: Box, trigger: FreTriggerType, ed: FreEditor): FreNode | null => {
            // do something
            const thisNode: FreNode = box.node;
            // const thisParent: FreNode = box.element.freOwner();
            alert("You shouldn't have pushed the button with role 'MyButton-role' on element " + thisNode.freId() + ".\nPunishment awaits !!!!!!!!!!");
            return null;
        },
    }),
    FreCustomAction.create({
        activeInBoxRoles: ["MyTableButton-role"],
        action: (box: Box, trigger: FreTriggerType, ed: FreEditor): FreNode | null => {
            // do something
            const thisNode: FreNode = box.node;
            // const thisParent: FreNode = box.element.freOwner();
            alert("You shouldn't have pushed the button with role 'MyTableButton-role' on element " + thisNode.freId() + ".\nPunishment awaits !!!!!!!!!!");
            return null;
        },
    }),
];
© 2018 - 2025 Freon contributors - Freon is open source under the MIT License.