Skip to content

Extend Image Editor

With the Image Editor, users can do basic image modifications. You can configure the Image Editor's default appearance or behavior. You can also extend it by adding custom features.

The following example shows how to extend the Image Editor by adding a button that draws a dot at a random location on the image.

Create the JavaScript component file

In assets/random_dot/, create the random-dot.js file with the following code of the React component:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, { useContext } from 'react';
import PropTypes from 'prop-types';

const { ibexa } = window;

const IDENTIFIER = 'dot';

const Dot = () => {
    return (
        <div className="c-image-editor-dot">
            <button type="button" className="btn btn-secondary">
                Add dot
            </button>
        </div>
    );
};

Dot.propTypes = {};

Dot.defaultProps = {};

export default Dot;

ibexa.addConfig(
    'imageEditor.actions.dot', // The ID ("dot") must match the one from the configuration yaml file
    {
        label: 'Dot',
        component: Dot,
        icon: ibexa.helpers.icon.getIconPath('radio-button'), // Path to an icon that will be displayed in the UI
        identifier: IDENTIFIER, // The identifier must match the one from the configuration yaml file
    },
    true
);

The code does not perform any action yet, you add the action in the following steps.

Add configuration

Configure the new Image Editor action under the ibexa.system.<scope>.image_editor configuration key:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
ibexa:
    system:
        default:
            image_editor:
                action_groups:
                    default:
                        id: default
                        label: Default
                        actions:
                            dot:
                                id: dot
                                priority: 50

Add entry to the Webpack configuration

Once you create and configure the React component, you must add an entry to the Webpack configuration. In the root directory of your project, modify the webpack.config.js file by adding the following code:

1
2
3
4
5
6
7
const ibexaConfigManager = require('./ibexa.webpack.config.manager.js');
//...
ibexaConfigManager.add({
    ibexaConfig,
    entryName: 'ibexa-admin-ui-layout-js',
    newItems: [ path.resolve(__dirname, './assets/random_dot/random-dot.js'), ],
});
At this point you should be able to see a new button in the Image Editor's UI.

Tip

Before you restart Ibexa DXP, run php bin/console cache:clear and yarn encore <dev|prod> to regenerate the assets.

Expand the React component

The button that you created above does not initiate any action yet. You must modify the JavaScript component to add a function to the button.

Contexts

When you create a React-based extension of the Image Editor, you can use a number of contexts that have the following functions:

  • CanvasContext - stores a canvas that displays the image, on which you can modify the image
  • ImageHistoryContext - stores the image history used by the Undo/Redo feature
  • AdditionalDataContext - stores additional data that is attached to the image, for example, focal point coordinates
  • TechnicalCanvasContext - stores a canvas, which you can use to draw elements that help modify the image, for example, a crop area or a grid, without interrupting with the actual image

The last context is not used in this example.

Draw a dot

Modify the random-dot.js file by creating a function that uses the canvas context to draw a random dot on the image:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    const drawDot = () => {
        const ctx = canvas.current.getContext('2d');
        const positionX = Math.random() * canvas.current.width;
        const positionY = Math.random() * canvas.current.height;

        ctx.save();

        ctx.fillStyle = '#ae1164';

        ctx.beginPath();
        ctx.arc(positionX, positionY, 20, 0, Math.PI * 2, true);
        ctx.fill();

        ctx.restore();

        saveInHistory();
    };

Store changes in history

Create another function that uses the history context to store changes, so that users can undo their edits:

1
2
3
4
5
6
7
8
9
    const saveInHistory = () => {
        const newImage = new Image();

        newImage.onload = () => {
            dispatchImageHistoryAction({ type: 'ADD_TO_HISTORY', image: newImage, additionalData });
        };

        newImage.src = canvas.current.toDataURL();
    };
Complete component code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import React, { useContext } from 'react';
import PropTypes from 'prop-types';

import {
    CanvasContext,
    ImageHistoryContext,
    AdditionalDataContext,
} from '../../vendor/ibexa/image-editor/src/bundle/ui-dev/src/modules/image-editor/image.editor.modules';

const { ibexa } = window;

const IDENTIFIER = 'dot';

const Dot = () => {
    const [canvas, setCanvas] = useContext(CanvasContext);
    const [imageHistory, dispatchImageHistoryAction] = useContext(ImageHistoryContext);
    const [additionalData, setAdditionalData] = useContext(AdditionalDataContext);
    const saveInHistory = () => {
        const newImage = new Image();

        newImage.onload = () => {
            dispatchImageHistoryAction({ type: 'ADD_TO_HISTORY', image: newImage, additionalData });
        };

        newImage.src = canvas.current.toDataURL();
    };
    const drawDot = () => {
        const ctx = canvas.current.getContext('2d');
        const positionX = Math.random() * canvas.current.width;
        const positionY = Math.random() * canvas.current.height;

        ctx.save();

        ctx.fillStyle = '#ae1164';

        ctx.beginPath();
        ctx.arc(positionX, positionY, 20, 0, Math.PI * 2, true);
        ctx.fill();

        ctx.restore();

        saveInHistory();
    };

    return (
        <div className="c-image-editor-dot">
            <button type="button" onClick={drawDot} className="btn btn-secondary">
                Add dot
            </button>
        </div>
    );
};

Dot.propTypes = {};

Dot.defaultProps = {};

export default Dot;

ibexa.addConfig(
    'imageEditor.actions.dot',
    {
        label: 'Dot',
        component: Dot,
        icon: ibexa.helpers.icon.getIconPath('radio-button'),
        identifier: IDENTIFIER,
    },
    true
);

Clear the cache and rebuild assets with the following commands:

1
2
php bin/console cache:clear
yarn encore dev

At this point you should be able to draw a random dot by clicking a button in the Image Editor's UI.