menu_book Navigation menu

JS Hooks (UI & Editor)

Use the global window.CMS object to register tools and extensions in the Admin Panel.

info
Note
Always enqueue your JS files via the PHP hook Hook::ADMIN_ENQUEUE_JS first.

1. Visual Builder (Editor.js)

Register custom blocks for the drag-and-drop editor.

Creating a Custom Editor.js Tool

Example: A Google Maps block (components/editorjs/editor-map.js).

JAVASCRIPT
(function() { 
    class MapTool {
        static get toolbox() {
            return {
                title: 'Google Map',
                icon: '<svg width="20" height="20" viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>'
            };
        }

        constructor({data, config, api}) {
            this.data = {
                lat: data.lat || '',
                lng: data.lng || '',
                zoom: data.zoom || 12
            };
            this.apiKey = config.apiKey || '';
            this.wrapper = undefined;
        }

        render() {
            this.wrapper = document.createElement('div');
            this.wrapper.classList.add('custom-map-tool');
            this.wrapper.style.padding = '15px';
            this.wrapper.style.border = '1px solid #eee';
            this.wrapper.style.backgroundColor = '#f9f9f9';

            this.wrapper.innerHTML = `
                <div style="font-weight:bold; margin-bottom:10px; color:#555;">📍 Map Settings</div>
                <div style="display:flex; gap:10px;">
                    <input type="text" class="cdx-input map-lat" placeholder="Latitude (e.g. 48.8566)" value="${this.data.lat}">
                    <input type="text" class="cdx-input map-lng" placeholder="Longitude (e.g. 2.3522)" value="${this.data.lng}">
                </div>
                <div style="margin-top:10px; font-size:0.8em; color:#888;">
                    API Key: ${this.apiKey ? 'Yes (Hidden)' : 'Not defined'}
                </div>
            `;
            return this.wrapper;
        }

        save(blockContent) {
            return {
                lat: blockContent.querySelector('.map-lat').value,
                lng: blockContent.querySelector('.map-lng').value,
                zoom: this.data.zoom
            };
        }
    }

    // Register tool in CMS
    window.CMS = window.CMS || {};
    window.CMS.editorTools = window.CMS.editorTools || {};

    window.CMS.editorTools.map = {
        class: MapTool,
        tunes: ['containerTune'],
        inlineToolbar: true,
        config: {
            apiKey: 'AIzaSyD...'
        }
    };
})();

2. Admin UI Form Field Renderers

The JS constructor exposes a global registry to let plugins render their own configuration UI for custom fields inside the Content Type / Form builders.

Entry point: window.CMS.fieldRenderers

JAVASCRIPT
// Register a renderer for a custom field type 'color_picker'
window.CMS.fieldRenderers['color_picker'] = function(fieldId, data) {
    // Return HTML string or DOM element
    return `<input type="color" id="${fieldId}" value="${data.value}">`;
};
JAVASCRIPT
document.addEventListener('DOMContentLoaded', () => {
    window.CMS.fieldRenderers['rating'] = function(fieldId, data) {
        // Return HTML string or DOM element
        return `
            <div class="form-container">
                <label>Maximum number of stars</label>
                <input type="number" class="form-control val-max-input" value="${data.validation_rules?.match(/max:(\d+)/)?.[1] || 5}">
                <input type="hidden" name="fields[${fieldId}][validation_rules]" class="validation-rules-final">
            </div>
        `;
    };
});

3. Media & Icons API

Interact dynamically with the built-in CMS managers.

MethodDescription
window.CMS.media.open(options)Open the Media Library modal. options: { multiple: bool, onSelect: func, trigger: HTMLElement }.
window.CMS.media.load(page)Force reload the media grid via AJAX.
window.CMS.icons.open(options)Open the Icon Picker modal. options: { onSelect: func }.
window.CMS.icons.registerProvider(type, name, provider)Register a new icon set (e.g. FontAwesome). provider must implement load() and render().

Registering an Icon Provider

Here is the implementation of fontawesome-provider.js to complement the PHP logic shown in Page 1:

JAVASCRIPT
window.CMS.icons.registerProvider('fontawesome', 'Font Awesome 7', {
    // Path to the JSON catalog of icons
    path: window.CMS.baseUrl + "assets/themes/mon-theme/assets/vendors/fontawesome/icons.json",

    // Load the icon list
    async load() {
        const response = await fetch(this.path);
        const jsonData = await response.json();
        
        return Object.keys(jsonData).map(iconName => ({
            name: iconName,
            styles: jsonData[iconName].styles 
        }));
    },

    // Render the icon in the selection grid
    render(iconData, callback) {
        const item = document.createElement('div');
        item.className = 'grid-icon-item';
    
        const style = iconData.styles.includes('solid') ? 'solid' : iconData.styles[0];
        const libraryKey = `fa-${style}`;          
        const iconClassName = `fa-${style} fa-${iconData.name}`;
    
        item.innerHTML = `
            <i class="${iconClassName}"></i>
            <div class="icon-name">${iconData.name}</div>
        `;
    
        // Click action: Send data to the CMS
        item.addEventListener('click', () => {
            const iconDataForForm = {
                html: `<i class="${iconClassName}"></i>`, 
                library: libraryKey,                      
                name: iconData.name                             
            };
            callback(iconDataForForm);
        });
    
        return item;
    }
});

4. Global Event Listeners

The CMS broadcasts specific events across the document when actions occur within the managers. You can listen to these from any JS extension.

JAVASCRIPT
// Triggered when a media is selected (Legacy Mode / no specific callback provided)
document.addEventListener('cms:media:selected', (e) => {
    const mediaData = e.detail;
    console.log('User selected an image:', mediaData.url);
});

// Triggered to force the media tool to reload its grid
document.addEventListener('cms:media:reload', (e) => {
    if (window.CMS.media) { window.CMS.media.load(1); }
});

// Triggered when an icon is selected (Legacy Mode)
document.addEventListener('cms:icon:selected', (e) => {
    const iconData = e.detail; // { library, name, html }
    console.log('User selected an icon:', iconData.name);
});

// Triggered specifically by the Editor.js Gallery Tool when media array is confirmed
document.addEventListener('cms:gallery:confirmed', (e) => {
    const mediaDataArray = e.detail;
    console.log('User confirmed a gallery selection containing:', mediaDataArray.length, 'items');
});