ASB
ASB

Reputation: 3

How to implement a multi-level dropdown in Froala Editor?

I am using Froala Editor in my project, and I have successfully implemented a custom simple dropdown using the editor's API. However, I need to create a multi-level dropdown (accordion-style) where each top-level option can have its own sub-options.

Here's the code I currently have for a simple custom dropdown:

FroalaEditor.DefineIcon('my_dropdown', { NAME: 'cog', SVG_KEY: 'cogs' });
FroalaEditor.RegisterCommand('my_dropdown', {
  title: 'Advanced options',
  type: 'dropdown',
  focus: false,
  undo: false,
  refreshAfterCallback: true,
  options: {
    'v1': 'Option 1',
    'v2': 'Option 2'
  },
  callback: function (cmd, val) {
    console.log(val);
  },
  refresh: function ($btn) {
    console.log('do refresh');
  },
  refreshOnShow: function ($btn, $dropdown) {
    console.log('do refresh when show');
  }
});

new FroalaEditor('div#froala-editor', {
  toolbarButtons: ['bold', 'italic', 'formatBlock', 'my_dropdown']
})`;

I want to extend this functionality to support a multi-level dropdown. For example:

Level 1 - Option 1
Level 1 - Option 2
    Level 2 - Option 1
    Level 2 - Option 2
Level 1 - Option 3

I have tried modifying the refreshOnShow function to include nested HTML for the multi-level dropdown and adding event handlers to manage the showing and hiding of sub-options. Here is my attempt:

FroalaEditor.DefineIcon('my_multilevel_dropdown', { NAME: 'list', SVG_KEY: 'list' });

FroalaEditor.RegisterCommand('my_multilevel_dropdown', {
  title: 'Advanced options',
  type: 'dropdown',
  focus: false,
  undo: false,
  refreshAfterCallback: true,
  options: {
    'level1_option1': 'Level 1 - Option 1',
    'level1_option2': 'Level 1 - Option 2',
    'level1_option3': 'Level 1 - Option 3'
  },
  callback: function (cmd, val) {
    console.log('Selected:', val);
  },
  refresh: function ($btn) {
    console.log('do refresh');
  },
  refreshOnShow: function ($btn, $dropdown) {
    console.log('do refresh when show');

    // Add custom HTML for multi-level dropdown
    $dropdown.html(`
      <div class="fr-dropdown-wrapper">
        <div class="fr-command fr-dropdown-item" data-cmd="level1_option1">Level 1 - Option 1</div>
        <div class="fr-command fr-dropdown-item" data-cmd="level1_option2">Level 1 - Option 2</div>
        <div class="fr-command fr-dropdown-item" data-cmd="level1_option3">Level 1 - Option 3
          <div class="fr-submenu">
            <div class="fr-command" data-cmd="level2_option1">Level 2 - Option 1</div>
            <div class="fr-command" data-cmd="level2_option2">Level 2 - Option 2</div>
          </div>
        </div>
      </div>
    `);

    // Debug: Check if elements are found
    var $commands = $dropdown.find('.fr-command');
    console.log($commands.length + ' commands found.');

    // Add accordion behavior
    $commands.off('click').on('click', function (event) {
      var $this = $(this);
      var $submenu = $this.children('.fr-submenu');

      if ($submenu.length) {
        event.stopPropagation();
        $submenu.slideToggle();
        $this.siblings().find('.fr-submenu').slideUp();
      } else {
        var cmd = $this.data('cmd');
        $this.closest('.fr-dropdown-wrapper').trigger('click.command', [cmd]);
      }
    });
  }
});

new FroalaEditor('#froala-editor', {
  toolbarButtons: ['bold', 'italic', 'formatBlock', 'my_multilevel_dropdown']
});

Upvotes: 0

Views: 144

Answers (1)

Carl Cruz
Carl Cruz

Reputation: 11

You're on the right track!

The approach you've taken to create a multi-level dropdown in Froala Editor is a good one. Here's a breakdown of your code and some refinements to make it work smoothly: Understanding the Code

DefineIcon: This registers a custom icon for your dropdown button.

RegisterCommand: This is where the magic happens. You define:

  • title: The name displayed on the button.

  • type: Set to 'dropdown' for a dropdown menu.

  • options: These are the top-level options of your dropdown.

  • callback: This function is executed when a top-level option is selected.

  • refreshOnShow: This is crucial for dynamically generating the multi-level structure.

  • refreshOnShow Function: This is where you build the HTML for your multi-level dropdown.

Dynamic Submenu Generation:

Instead of hardcoding the submenu structure within refreshOnShow, it's better to make it more dynamic based on your data structure.

refreshOnShow: function ($btn, $dropdown) {
    // Assuming you have a data structure like this:
    const menuData = {
      'level1_option1': 'Level 1 - Option 1',
      'level1_option2': {
        title: 'Level 1 - Option 2',
        suboptions: {
          'level2_option1': 'Level 2 - Option 1',
          'level2_option2': 'Level 2 - Option 2'
        }
      },
      'level1_option3': 'Level 1 - Option 3'
    };
  
    // Build the dropdown HTML
    let html = '';
    for (const key in menuData) {
      const item = menuData[key];
      html += `<div class="fr-command fr-dropdown-item" data-cmd="${key}">${item.title || item}</div>`;
      if (item.suboptions) {
        html += `<div class="fr-submenu">`;
        for (const subkey in item.suboptions) {
          html += `<div class="fr-command" data-cmd="${subkey}">${item.suboptions[subkey]}</div>`;
        }
        html += `</div>`;
      }
    }
  
    $dropdown.html(html);
  
    // ... (rest of your accordion behavior code)
  }

Accordion Behavior: Your accordion logic is good. Here's a slightly refined version:

$commands.off('click').on('click', function (event) {
  var $this = $(this);
  var $submenu = $this.children('.fr-submenu');

  if ($submenu.length) {
    event.stopPropagation();
    $submenu.slideToggle();
    $this.siblings().find('.fr-submenu').slideUp();
  } else {
    // Handle top-level option selection
    var cmd = $this.data('cmd');
    $this.closest('.fr-dropdown-wrapper').trigger('click.command', [cmd]);
  }
});

Styling:

You'll likely want to add some CSS to style your multi-level dropdown to match your Froala Editor theme.

You can check out more options in the Froala docs.

Upvotes: 0

Related Questions