Create a metadata sidebar

This recipe covers a very basic metadata form for use in a sidebar. It's mostly meant to serve as an example for an editing roundtrip between an opened XML document and a custom form within a sidebar.

This is by no means an example for a be-all and end-all metadata sidebar. Assumptions have been made, and functionality has been omitted, in favour of brevity. It should be carefully evaluated and refined when building a production worthy metadata sidebar.

Recommended reading

  • The 'Create a sidebar' guide for instructions on creating and setting up a sidebar.
  • The 'Create a form' guide for detailed information about creating forms.
MetadataSidebar.jsx
import React, { useCallback, useMemo } from 'react';

// More information about each of the following imports can be found in the FontoXML Editor API
// section of the documentation (http://documentation.fontoxml.com/api/).
import { Flex, Form, FormRow, SidebarHeader, SidebarInlay, TextInput } from 'fds/components';

import documentsManager from 'fontoxml-documents/src/documentsManager.js';
import getNodeId from 'fontoxml-dom-identification/src/getNodeId.js';
import useManagerState from 'fontoxml-fx/src/useManagerState.js';
import useXPath, { XPATH_RETURN_TYPES } from 'fontoxml-fx/src/useXPath.js';
import t from 'fontoxml-localization/src/t.js';
import operationsManager from 'fontoxml-operations/src/operationsManager.js';
import selectionManager from 'fontoxml-selection/src/selectionManager.js';

export default function MetadataPanel() {
    const focusedDocumentNode = useManagerState(selectionManager.selectionChangeNotifier, () =>
        documentsManager.getNodeById(selectionManager.focusedDocumentId)
    );
    // This assumes an editor with a schema where in each loaded document file has xml like this:
    // <document><metadata>Actual metadata content</metadata><content>...</content></document>
    const metadataElement = useXPath('document/metadata', focusedDocumentNode, {
        expectedResultType: XPATH_RETURN_TYPES.FIRST_NODE_TYPE
    });
    const metadataNodeId = useMemo(() => getNodeId(metadataElement), [metadataElement]);
    const metadataTextContent = useXPath('string(.)', metadataElement, {
        expectedResultType: XPATH_RETURN_TYPES.STRING_TYPE
    });

    const updateMetadataTextContentInXML = useCallback(
        updatedMetadataContent => {
            // Details about the 'replace-node' operation, and potential alternatives, can be found in
            // the FontoXML Editor API section of the documentation. It's recommended to read through
            // them to find out which operation is best suited for your usecase.
            //
            // Tip: make sure to only execute a single operation for an intuitive undo/redo stack.
            //
            // 'operationsManager.executeOperation' returns a Promise. It's recommended to implement
            // 'then' and 'catch' handlers, and present users with some form of feedback when saving.
            operationsManager.executeOperation('replace-node', {
                contextNodeId: metadataNodeId,
                replacementNodeStructure: [updatedMetadataContent]
            });
        },
        [metadataNodeId]
    );

    return (
        <Flex flex="1" flexDirection="column">
            <SidebarHeader subtitle={t('Example metadata sidebar')} title={t('Metadata')} />

            <SidebarInlay>
                <Form labelPosition="before">
                    <FormRow key="text" label={t('Example field')}>
                        <TextInput
                            name="text"
                            onChange={updateMetadataTextContentInXML}
                            placeholder={t('type something here')}
                            tooltipContent={t('This field serves as an example.')}
                            value={metadataTextContent}
                        />
                    </FormRow>
                </Form>
            </SidebarInlay>
        </Flex>
    );
}