Richeditor/Froala - custom plugin with popup

Froala WYSIWYG looks to be hidden behind oc.RichEditor interface. There is an example for custom button in docs, but I need to register custom popup (but not AJAX!). Something like insertLink button looks like:
insert-link
Unfortunately all Froala plugins are distributed with OctoberCMS only as minified build.

Does anybody know, how to create Froala popup plugin for current version of OctoberCMS? Adding this example to docs would be keen!

jo o nieco podobne som sa snazil viac krat :smiley: hehe. moc user friendly to nie je…


i tried something similiar for extended media finder plugin (aka Image Databanks plugin - October CMS) , without any working example.

Hello,

waiting for @daft answering you, i give you something i tried but it’s always a WIP test.
Maybe you could give it a try :slight_smile:

WARNING : it is not official documentation, use it at your own risks

First you need to extend Backend\FormWidgets\RichEditor class in your plugin’s boot function.

RichEditor::extend(function($widget) {
    $widget->addJs('/plugins/author/plugin_name/assets/js/your_javascript.js');
});

then create this /plugins/author/plugin_name/assets/js/your_javascript.js
and make something like this :

/*
|-------------------------
| Froala test Dropdown button Plugin
|-------------------------
*/
+function ($) {

  oc.richEditorRegisterButton('paste', {
        title: 'paste',
        type: 'dropdown',
        icon: '<i class="icon-clipboard"></i>',
        options: {
            'opt1': 'Option 1',
            'opt2': 'Option 2'
        },
        undo: true,
        focus: true,
        refreshAfterCallback: true,
        callback: function (cmd, val, params) {
        console.log (this.html.get());
        },
        refresh: function ($btn) {
        
        console.log (this.selection.element());
        },
        refreshOnShow: function ($btn, $dropdown) {
        
        console.log (this.selection.element());
        }
  });


}(jQuery)

then, go to your backend, in editor configuration, add your command in the toolbar buttons.

you should see your new button in all your richeditor now.

Next step would be to add what you want, like a request which opens a popup, or whatever you want.

if you want you can follow the froala documentation, https://froala.com/wysiwyg-editor/docs/concepts/custom/dropdown/

About the October 3.0 Popup, i don’t find anything on how to use it on my own, but think you could use the old one until the new version is publicly released…

EDIT : i have found something, but don’t know all :slight_smile:

you can create some new popup with something like this :

 $.oc.vueComponentHelpers.inspector.host
                    .showModal(
                        'test',
                        ['test'],
                        [],
                        'test-modal',
                        {
                            buttonText: 'apply',
                            resizableWidth: true
                        }
                    )

for more info about the configuration, you could take a look at modules\backend\vuecomponents\inspector\assets\js\host.js

i’ve tried to add this JS in the callback function and it opens the new modal , enjoy it :slight_smile:

Let me know if you find some tips, i’m very interested about this feature :slight_smile:

good luck

1 Like

I’ve edited with some research on core :slight_smile: enjoy

@Rike-cz , here is a full example that work on my side !

/*
|-------------------------
| Froala test Dropdown button Plugin
|-------------------------
*/
+function ($) {

  oc.richEditorRegisterButton('paste', {
        title: 'paste',
        type: 'button',
        icon: '<i class="icon-clipboard"></i>',
        undo: true,
        focus: true,
        refreshAfterCallback: true,
        callback: function (cmd, val, params) {
            $.oc.vueComponentHelpers.inspector.host
                    .showModal(
                        // Modal Title
                        'Paste', 
                        // Object of modal (maybe modal config, dont know...)
                        {
                            title: 'Paste',
                        },
                        // dataSchema (seems like fields)
                        [
                            {
                                placeholder: 'Paste here...',
                                property: 'paste',
                                title: 'Paste',
                                type: 'string',
                            }
                        ],
                        // Unique ID of Modal
                        'paste-modal',
                        // Froala Options...
                        {
                            buttonText: 'Appliquer',
                            resizableWidth: true,
                            beforeApplyCallback: (updatedData) => {
                                console.log('updatedData', updatedData);
                                this.selection.element().append(updatedData.paste);
                                return Promise.resolve();
                            }
                        }
                    )
        },
        refresh: function ($btn) {
        
        console.log (this.selection.element());
        },
        refreshOnShow: function ($btn, $dropdown) {
        
        console.log (this.selection.element());
        }
  });


}(jQuery)

@Ladylain Nice example! You are using OC Vue modal.
I tried to read uglified code of Froala insertLink solution (because I need something very similar), and they are using kind of predefined inner popups (this.popups) and more than one command to deal with creating, editing and removing custom element. I cannot figure out how it works. Your solution is very helpful with simple pasting, but creating custom HTML tag and editing looks like different and difficult approach :confused:

ok, i see, so maybe this can help you ( done this quickly on my lunch :slight_smile: )

it’s based on Froala Docs

/*
|-------------------------
| Froala test Dropdown button Plugin
|-------------------------
*/
+function ($) {
  // Define popup template.
  Object.assign($.FroalaEditor.POPUP_TEMPLATES, {
    'customPlugin.popup': '[_BUTTONS_][_CUSTOM_LAYER_]'
  });

  // Define popup buttons.
Object.assign($.FroalaEditor.DEFAULTS, {
    popupButtons: ['popupClose', '|', 'popupButton1', 'popupButton2'],
  });


  $.FroalaEditor.PLUGINS.customPlugin = function (editor) {
    // Create custom popup.
    function initPopup () {
      // Load popup template.
      var template = $.FroalaEditor.POPUP_TEMPLATES.customPopup;
      if (typeof template == 'function') template = template.apply(editor);
  
      // Popup buttons.
      var popup_buttons = '';
  
      // Create the list of buttons.
      if (editor.opts.popupButtons.length > 1) {
        popup_buttons += '<div class="fr-buttons">';
        popup_buttons += editor.button.buildList(editor.opts.popupButtons);
        popup_buttons += '</div>';
      }
  
      // Load popup template.
      var template = {
        buttons: popup_buttons,
        custom_layer: '<div class="custom-layer">Hello World!</div>'
      };
  
      // Create popup.
      var $popup = editor.popups.create('customPlugin.popup', template);
  
      return $popup;
    }
  
    // Show the popup
    function showPopup () {
      // Get the popup object defined above.
      var $popup = editor.popups.get('customPlugin.popup');
  
      // If popup doesn't exist then create it.
      // To improve performance it is best to create the popup when it is first needed
      // and not when the editor is initialized.
      if (!$popup) $popup = initPopup();
  
      // Set the editor toolbar as the popup's container.
      editor.popups.setContainer('customPlugin.popup', editor.$tb);
  
      // If the editor is not displayed when a toolbar button is pressed, then set BODY as the popup's container.
      // editor.popups.setContainer('customPlugin.popup', $('body'));
  
      // Trigger refresh for the popup.
      // editor.popups.refresh('customPlugin.popup');
  
      // This custom popup is opened by pressing a button from the editor's toolbar.
      // Get the button's object in order to place the popup relative to it.
      var $btn = editor.$tb.find('.fr-command[data-cmd="paste"]');
  
      // Compute the popup's position.
      var left = $btn.offset().left + $btn.outerWidth() / 2;
      var top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
  
      // Show the custom popup.
      // The button's outerHeight is required in case the popup needs to be displayed above it.
      editor.popups.show('customPlugin.popup', left, top, $btn.outerHeight());
    }
  
    // Hide the custom popup.
    function hidePopup () {
      editor.popups.hide('customPlugin.popup');
    }
  
    // Methods visible outside the plugin.
    return {
      showPopup: showPopup,
      hidePopup: hidePopup
    }
  }

  // Define custom popup close button icon and command.

  oc.richEditorRegisterButton('popupClose', {
  title: 'Close',
  icon: '<i class="fa fa-times"></i>',
  undo: false,
  focus: false,
  callback: function () {
    this.customPlugin.hidePopup();
  }
});

// Define custom popup 1.

oc.richEditorRegisterButton('popupButton1', {
  title: 'Button 1',
  icon: '<i class="fa fa-bell-o"></i>',
  undo: false,
  focus: false,
  callback: function () {
    alert("popupButton1 was pressed");
  }
});

// Define custom popup 2.
oc.richEditorRegisterButton('popupButton2', {
  title: 'Button 2',
  icon: '<i class="fa fa-bullhorn"></i>',
  undo: false,
  focus: false,
  callback: function () {
    alert("popupButton2");
  }
});

  oc.richEditorRegisterButton('paste', {
        title: 'paste',
        type: 'button',
        icon: '<i class="icon-clipboard"></i>',
        undo: true,
        focus: true,
        refreshAfterCallback: true,
        callback: function (cmd, val, params) {     
            this.customPlugin.showPopup();
        },
        refresh: function ($btn) {
        
        console.log (this.selection.element());
        },
        refreshOnShow: function ($btn, $dropdown) {
        
        console.log (this.selection.element());
        }
  });
  

}(jQuery)

you just need to make your own template for your popup :slight_smile:

“oc.” didn’t have exactly what I needed in my case before and it didn’t work in richeditor fields that are nested in a repeater so I’m using something like this:

+function ($) {
	const myCoolObject = {
		init: function () {
            /*

            use $.FroalaEditor to add or edit anything

            */

			$.FroalaEditor.DEFAULTS = $.extend($.FroalaEditor.DEFAULTS, {
				fontFamily: {
					'Inter, sans-serif;': 'Inter',
					'Rift, sans-serif;': 'Rift',
				}
			});

            $.FroalaEditor.DefineIcon('myCoolIcon', { NAME: 'code-snippet', template: 'octobercms'});
            $.FroalaEditor.RegisterCommand('myCoolCommand', {
                title: 'My Cool Command',
                icon: 'myCoolIcon'
            });
		}
	};

    $(document).on('render', () => {
		myCoolObject.init();
	});
}(jQuery);

The js file is added as @Ladylain explained and defined all the custom features using the official Froala docs. A lot of trying and testing, but I hope this helps too!

1 Like