Configure elements

This step by step guide is aimed at getting visible content in your instance of Fonto editor.

What you'll need

  • A basic instance of the Fonto editor
  • An example content file which adheres to the rules in the schema used by the instance of the Fonto editor

Step 1: Choose an element to configure

For completely new editors, it helps to configure from the outermost nodes, inwards. When the Fonto editor instance was initialized by the fdt editor init command, configuration and some metadata was added for each element in the schema(s). In order to get a full list of elements in a schema, or more details for a specific element, you can use the fdt elements and fdt element <elementName> commands respectively.  For the recipe schema, this list should consist of the following elements, and the generated configuration can be found in configureSxModule.js of the recipe-sx-module package:

  1. <recipe>

  2. <title>

  3. <description>

  4. <p>

  5. <b>

  6. <i>

  7. <u>

  8. <allergy-warning>

  9. <ingrediënt>

  10. <ul>

  11. <li>

  12. <steps>

  13. <step>

It is important to start with the root element of the xml-file, otherwise it is not possible to visualize the other elements in the editor. In this training <recipe> is the root element.

Step 2: Pick an element family

The element family determines the element visualization, cursor behaviour, keypresses, and so on. You can find a list of all families in the API  documentation. 

The following flowchart is a helper tool to select an element family for a given node. The yellow boxes are element names that you should look up in our API documentation. 

Repeat this step for every element that you want to configure. In this guide we'll go with the elements found in step 1 and we will start configuring the first element we encounter starting from the outermost nodes, inwards. 

  1. In our xml file the first element we encounter is <recipe>. For this element, we select the configureAsSheetFrame family. Sheet frames are meant for elements that represent a document or parts of several documents and they are displayed as a sheet of 'paper'.

  2. The second element we will consider is the <title> element. It only contains text and has a clear role as defined by the element name. The configureAsTitleFrame family seems like a good fit.

  3. The third element is <description>. This element only serves as a container for other elements. For elements which only provide grouping and are themselves semantically irrelevant we provide the configureAsStructure family.

  4. The fourth element, <p>, represents a paragraph, so we'll use the configureAsBlock family.

  5. The <b>, <i> and <u> elements are inline elements which can be split. Thus for these elements we have the configureAsInlineFormatting family. We can give it additional parameters in step 4 to format these elements as you would expect.

  6. The <allergy-warning> element contains information which calls attention to a particular point. Keep in mind that this element cannot be split in two, otherwise you will have two <allergy-warning> elements and that would defeat the purpose of semantics. As this element needs to be visually distinct we will use the configureAsFrame family.

  7. The next element is the <ul> element. This is a list-like structure which represents an unordered list. The <steps> element also represents a list. For list-like structures we use the configureAsListElements family.

Our last element is the <ingredient> element. This is an inline element that cannot be split. Therefore we use the configureAsInlineFrame family. This family can apply meaning to a text range and are visualised through a colored border and a background.

Step 3: Apply the element family configuration

The element configuration is registered in the packages/recipe-sx-module/src/configureSxModule.js file or any of the packages depended on by your schema package.

This step is the continuation of the Getting started guide. In this file some configuration and metadata was added for each element, using the configureAsRemoved(sxModule, 'self::<elementName>') to ensure we do get an error when loading a document for which an element is found with no configuration.


You can use the // TODOs that are left in the code as a checklist to make sure you configured all elements. In order to finish the element configuration, all elements should be checked and have the correct configuration assigned to them.

For the first element, <recipe>, we need to import the sheet family from its documented location. It is a function that we can call with three or four arguments: the schema representation, an element selector, the markup label, and additional options. Our file becomes:

import configureAsRemoved from 'fontoxml-families/src/configureAsRemoved.js'; 
import configureAsSheetFrame from 'fontoxml-families/src/configureAsSheetFrame.js';

export default function configureSxModule(sxModule) {
	...


	// <recipe>
	// ...
	configureAsSheetFrame(sxModule, 'self::recipe', 'recipe');


	...
}

And similarly, for the other elements you should change them from configureAsRemoved to the correct family as chosen in step 2:

// <title>
// ...
configureAsTitleFrame(sxModule, 'self::title', 'title');


// <description>
// ...
configureAsStructure(sxModule, 'self::description', 'description');


// <p>
// ...
configureAsBlock(sxModule, 'self::p', 'paragraph');


// <b>
// ...
configureAsInlineFormatting(sxModule, 'self::b', 'bold');


// <i>
// ...
configureAsInlineFormatting(sxModule, 'self::i', 'italic');


// <u>
// ...
configureAsInlineFormatting(sxModule, 'self::u', 'underline');


// <allergy-warning>
// ...
configureAsFrame(sxModule, 'self::allergy-warning', 'allergy warning');


// <ingredient>
// ...
configureAsInlineFrame(sxModule, 'self::ingredient', 'ingredient');


// <ul>
// ...
configureAsListElements(sxModule, {
    list: {
        selector: 'self::ul',
        style: configureAsListElements.BULLETED_LIST_STYLE
    },
    item: {
        nodeName: 'li'
    },
    paragraph: {
        nodeName: 'p'
    }
});


The list family is configured differently than the other families. It only accepts 2 arguments;  the schema representation and an options object. By passing a static property, for example configureAsListElements.BULLETED_LIST_STYLE, Fonto will properly style the list. This property is required, Fonto will throw an error if it is not passed.

Also note that configureAsListElements should be imported from the fontoxml-list-flow package instead of the fontoxml-families package.


// <steps>
// ...
configureAsListElements(sxModule, {
    list: {
        selector: 'self::steps',
        style: configureAsListElements.NUMBERED_LIST_STYLE
    },
    item: {
        nodeName: 'step'
    },
    paragraph: {
        nodeName: 'p'
    }
});

If you have a development server running and refresh your editor, you should be seeing an increasing amount of elements being rendered. If it's not working and you are not seeing errors in your browser console, check that there is a fonto-manifest.json file in the correct schema package that points to your element configuration package.

Step 4: Pass additional options to the element family configuration

These additional options can improve cursor and selection behaviour significantly, but also provide styling and interactivity to the content view.

The options specific to a family are documented on the family API page, and in the options argument you can mix them with globally available options. All global options can also be registered using configureProperties.

A few of the options you can use for the elements in the training:

  • fontVariation: to set the font size of the title.
  • emptyElementPlaceholderText: if the title is removed this element shows a placeholder text so the user will notice where it is possible to type the title.
// <title>
configureAsTitleFrame(sxModule, 'self::title', 'title', {
    fontVariation: 'document-title',
    emptyElementPlaceholderText: 'Type your title here:'
});
  • weight: to visualize the <b> element as bold: 
// <b>
configureAsInlineFormatting(sxModule, 'self::b', 'bold', {
    weight: 'bold'
});
  • slant: to visualize the <i> element as italic:
// <i>
configureAsInlineFormatting(sxModule, 'self::i', 'italic', {
    slant: 'italic'
});
  • underlineStyle: to visualize the <u> element with a single underline:
// <u>
configureAsInlineFormatting(sxModule, 'self::u', 'underline', {
    underlineStyle: 'single'
});
  • blockHeaderLeft: to place the elements markup label in the upper left corner using a widget.
  • backgroundColor: to change the background color.

  • defaultTextContainer: if an element cannot contain text directly, but the author should be able to navigate into it and start typing, the 'default text container' is the element that the text will be wrapped into when the author does so.

// <allergy-warning>
configureAsFrame(sxModule, 'self::allergy-warning', 'allergy warning', {
    blockHeaderLeft: [
        createMarkupLabelWidget()
    ],
    backgroundColor: 'amber',
    defaultTextContainer: 'p'
});


Do not forget to import the createMarkupLabelWidget, otherwise Fonto will throw an error and will fail to load

If you click on a list item on the editor you will notice that on the breadcrumb the <ul> and <li> elements did not get proper names. If you want to give these elements better readable names use the configureMarkupLabel family to add additional configuration.

configureMarkupLabel(sxModule, 'self::ul', 'unordered list');
 
configureMarkupLabel(sxModule, 'self::li', 'list item');

For the recipe schema, configuring the elements chosen in step 1 will give you a reasonable feel of what the test documents will look like. Also, you're able to type text in the title and paragraphs, and add new paragraphs to <description> by pressing enter.

Final result

configureSxModule.js
import configureAsBlock from 'fontoxml-families/src/configureAsBlock.js';
import configureAsFrame from 'fontoxml-families/src/configureAsFrame.js';
import configureAsInlineFormatting from 'fontoxml-families/src/configureAsInlineFormatting.js';
import configureAsInlineFrame from 'fontoxml-families/src/configureAsInlineFrame.js';
import configureAsListElements from 'fontoxml-list-flow/src/configureAsListElements.js';
import configureAsSheetFrame from 'fontoxml-families/src/configureAsSheetFrame.js';
import configureAsStructure from 'fontoxml-families/src/configureAsStructure.js';
import configureAsTitleFrame from 'fontoxml-families/src/configureAsTitleFrame.js';
import configureMarkupLabel from 'fontoxml-families/src/configureMarkupLabel.js';
import createMarkupLabelWidget from 'fontoxml-families/src/createMarkupLabelWidget.js';

// configureSxModule is meant for registering configuration for one or more schemas, such as element
// configuration or clipboard transformations. The sxModule argument represents the schema
// experience module for this package, and can extend, or be extended by, other packages.
// For more information:
// https://documentation.fontoxml.com/editor/latest/application-structure-25591887.html
// https://documentation.fontoxml.com/editor/latest/configure-elements-30024973.html
// https://documentation.fontoxml.com/api/latest/family-33433881.html
export default function configureSxModule(sxModule) {
    // <allergy-warning>
    // ...
    configureAsFrame(sxModule, 'self::allergy-warning', 'allergy warning', {
        backgroundColor: 'amber',
        blockHeaderLeft: [createMarkupLabelWidget()]
    });

    // <b>
    // ...
    configureAsInlineFormatting(sxModule, 'self::b', 'bold', {
        visualization: {
            weight: 'bold'
        }
    });

    // <description>
    // ...
    configureAsStructure(sxModule, 'self::description', 'description');

    // <i>
    // ...
    configureAsInlineFormatting(sxModule, 'self::i', 'italic', {
        visualization: {
            slant: 'italic'
        }
    });

    // <ingredient>
    // ...
    configureAsInlineFrame(sxModule, 'self::ingredient', 'ingredient');

    // <li>
    // ...
    configureMarkupLabel(sxModule, 'self::li', 'list item');

    // <p>
    // ...
    configureAsBlock(sxModule, 'self::p', 'paragraph');

    // <recipe>
    // ...
    configureAsSheetFrame(sxModule, 'self::recipe', 'recipe');

    // <step>
    // ...
    configureMarkupLabel(sxModule, 'self::step', 'step');

    // <steps>
    // ...
    configureAsListElements(sxModule, {
        list: {
            selector: 'self::steps',
            style: configureAsListElements.NUMBERED_LIST_STYLE
        },
        item: {
            nodeName: 'step'
        },
        paragraph: {
            nodeName: 'p'
        }
    });
    configureMarkupLabel(sxModule, 'self::steps', 'steps');

    // <title>
    // ...
    configureAsTitleFrame(sxModule, 'self::title', 'title', {
        fontVariation: 'document-title'
    });

    // <u>
    // ...
    configureAsInlineFormatting(sxModule, 'self::u', 'underline', {
        visualization: {
            underlineStyle: 'single'
        }
    });

    // <ul>
    // ...
    configureAsListElements(sxModule, {
        list: {
            selector: 'self::ul',
            style: configureAsListElements.BULLETED_LIST_STYLE
        },
        item: {
            nodeName: 'li'
        },
        paragraph: {
            nodeName: 'p'
        }
    });
    configureMarkupLabel(sxModule, 'self::ul', 'unordered list');
}




Was this page helpful?