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 { ... }
}
~/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;
},
}),
];