React-draft-wysiwyg: Custom toolbar

Created on 20 Oct 2016  路  25Comments  路  Source: jpuri/react-draft-wysiwyg

Really promising project you have here! We've been looking to part ways with Summernote for a while now and this might just be it. Would you be open to having a way to configure the toolbar through props a bit like it's done in Summernote: http://summernote.org/deep-dive/#custom-toolbar-popover

Thanks!

Most helpful comment

@mathieumg: basic support for customizing toolbar is now in master branch. I will work to add more options to it later.

Thanks a lot for your nice suggestions 馃憤

All 25 comments

Thanks @mathieumg: yeh it very close to what you have in summer-note. I already plan to work on super-script, sub-script and remove-style.

Exactly what other configurations are you looking for ?

A way to indicate what toolbars and what buttons to display, something similar to Summernote, approach could be different I imagine:

<Editor
  toolbar={[
    // [groupName, [list of button]]
    ['style', ['bold', 'italic', 'underline', 'clear']],
    ['font', ['strikethrough', 'superscript', 'subscript']],
    ['fontsize', ['fontsize']],
    ['color', ['color']],
    ['para', ['ul', 'ol', 'paragraph']],
    ['height', ['height']]
  ]}
/>

That looks very useful, I will do it - soon after 1.0. My priority first is to release a well documented 1.0.

Awesome, no hurry of course! :grin:

This change will be very useful I think and thus I plan to include it in 1.0.

My plan for it is that toolbar will be divided into groups (division will not be visible in UI):

  1. Inline styles - BOLD, ITALIC, UNDERLINE, STRIKE-THROUGH, MONOSPACE
  2. Block type
  3. Font size
  4. Font Family
  5. List - ordered, un-ordered, indent, outdent
  6. Text Align - left, center, right, justify
  7. Color
  8. Link - add link, remove link
  9. Image
  10. History - undo, redo

It should be possible for user to:

  1. Show / Hide any of above group or any option inside the group.
  2. Show the group inside dropdown - this is already present for most of groups above.
  3. Change icon images.

@mathieumg , @theLordz: plz share your suggestion and feedbacks.

Prop to customize toolbar will be simple JS object. The object below list all the options but user needs to provide only those where he wants to override the defaults. Default value of visible will be true and inDropdown will be false. By default for icons current images (as shown in docs) will be used:

toolbarConfig = {
  inline: {
    visible: true,
    inDropdown: false,
    bold: { visible: true, icon: 'xxx.png', },
    italic: { visible: true, icon: 'xxx.png', },
    underline: { visible: true, icon: 'xxx.png', },
    strikeThrough: { visible: true, icon: 'xxx.png', }
    monospace: { visible: true, icon: 'xxx.png', }
  },
  blockType: { visible: true, },
  fontSize: { visible: true, icon: 'xxx.png', }
  fontFamily: { visible: true, }
  list: {
    visible: true,
    inDropdown: true,
    unordered: { visible: true, icon: 'xxx.png', },
    ordered: { visible: true, icon: 'xxx.png', },
    indent: { visible: true, icon: 'xxx.png', },
    outdent: { visible: true, icon: 'xxx.png', }
  },
  textAlign: {
    visible: true,
    inDropdown: true,
    left: { visible: true, icon: 'xxx.png', },
    center: { visible: true, icon: 'xxx.png', },
    right: { visible: true, icon: 'xxx.png', },
    justify: { visible: true, icon: 'xxx.png', }
  },
  colorPicker: { visible: true, icon: 'xxx.png', }
  link: {
    visible: true,
    inDropdown: true,
    addLink: { visible: true, icon: 'xxx.png', },
    removeLink: { visible: true, icon: 'xxx.png', },
  },
  image: { 
    visible: true, 
    icon: 'xxx.png',
    fileUpload: true,
    url: true,
  },
  history: {
    visible: true,
    inDropdown: true,
    undo: { visible: true, icon: 'xxx.png', },
    redo: { visible: true, icon: 'xxx.png', },
  }
}

Very good start! :tada: What I liked about the array approach is that the developers can also reorder the groups and the buttons within each group however they see fit. For instance:

toolbar = [
  ['inline', [
    'visible',
    // omit strings instead of {visible: false}
    {type: 'bold', icon: 'xxx.png'},
    // etc.
  ],
  'fontFamily', // different order than in yours
  'blockType',
  {type: 'link', inDropdown: true},
  // etc.
],

I also kept toolbar as the prop name, it's pretty descriptive already as props are always a form of "config" when we think about it. Just my two cents, tell me what you think! :)

@mathieumg: I am not sure if I would prefer implicit ordering using array or rather explicit ordering using a field position. I think approach is different in using array toolbar is kind-of created by the array. But I am using these configurations to override the defaults.

I have created a new issue for ordering https://github.com/jpuri/react-draft-wysiwyg/issues/19, I actually want to release a stable 1.0 soon. But if time permits I will include it in 1.0.

Please share your opinions.

I am thinking it might actually be better to get rid of {visible: false} and show only those options which user asks for.

I think the approach I would prefer is:

  • by default all toolbar options will be shown if user passes toolbar property only those options will be shown which are present in configuration property
  • toolbar options will not be show in drop-down unless explicitly specified
  • default icons will be used unless over-ridden

Thus configuration will look like:

toolbar = {
  inline: {
    inDropdown: true,
    options: {
      bold: { icon: 'xxx.png' },
      italic: { icon: 'xxx.png' },
      underline: { icon: 'xxx.png' },
      strikeThrough: { icon: 'xxx.png' },
      monospace: { icon: 'xxx.png' }
    }
  },
  blockType: { },
  fontSize: { icon: 'xxx.png' }
  fontFamily: { }
  list: {
    inDropdown: true,
    options: {
      unordered: { icon: 'xxx.png' },
      ordered: { icon: 'xxx.png' },
      indent: { icon: 'xxx.png' },
      outdent: { icon: 'xxx.png' }
    }
  },
  textAlign: {
    inDropdown: true,
    options: {
      left: { icon: 'xxx.png' },
      center: { icon: 'xxx.png' },
      right: { icon: 'xxx.png' },
      justify: { icon: 'xxx.png' }
    }
  },
  colorPicker: { icon: 'xxx.png' }
  link: {
    inDropdown: true,
    options: {
      addLink: { icon: 'xxx.png' },
      removeLink: { icon: 'xxx.png' },
    }
  },
  image: { 
    icon: 'xxx.png',
    fileUpload: true,
    url: true,
  },
  history: {
    inDropdown: true,
    options: {
      undo: { icon: 'xxx.png' },
      redo: { icon: 'xxx.png' },
    }
  }
}

I also like the idea of using toolbar rather than toolbarConfig. I also see some advantages of using array but I think simple object gives more flexibility. later I will add more configuration parameters like position for ordering, etc.

Thanks a ton of your useful input - please share your feedbacks.

Finally I have arrived on this configuration:
https://github.com/jpuri/react-draft-wysiwyg/blob/toolbar_customization/js/src/config/defaultToolbar.js

It looks much generic and also extensible to me. It will be immutable data-structure and there will be deep merge with what user provides. DeepMerge will merge only objects and not arrays.

Cool! So options can be used to change the order if need be. Looks good!

I actually want to avoid implicit ordering based on position, later I plan to add additional fields position, ... like icon right now.

Ordering will not be supported right now.

@mathieumg: basic support for customizing toolbar is now in master branch. I will work to add more options to it later.

Thanks a lot for your nice suggestions 馃憤

Awesome, thanks! Did you want me to close this issue?

I think it can be closed. I added any other issue for allowing ordering of options in toolbar.

Please share more suggestions for improving the editor.

At this moment i can't create own toolbar with custom blocks, for example it will cause error:

{
  options: ['textIndent', 'list'],

  textIndent: {
    inDropdown: false,
    className: undefined,
    component: undefined,
    dropdownClassName: undefined,
    options: ['indent', 'outdent'],
    indent: { className: undefined },
    outdent: { className: undefined },
  },

  list: {
    inDropdown: false,
    className: undefined,
    component: undefined,
    dropdownClassName: undefined,
    options: ['unordered', 'ordered'],
    unordered: { className: undefined },
    ordered: { className: undefined },
  },
}

because 'textIndent' not supported. How can we make fully custom toolbar like this:
screen
?

@AndreyMiloserdov : can you plz share exactly what error / result do you get for above code snippet.

screen shot 2017-10-25 at 17 39 42

Another problem in src/controls/TextAlign/Component/index.js
<div className={classNames('rdw-text-align-wrapper', className)} aria-label="rdw-textalign-control">
no any currentState in the className property: can't set custom icon for selected style, only default image as icon, no any css possibility for this

Same for the list buttons.

@jpuri how does one disable something like fontFamily in the current configuration model?

edit: nevermind, can explicitly include them under toolbar.options array. thanks!

Yes @ericnkatz , toolbar property can be used to remove toolbar options.

First of all, really love the library!
@jpuri what if we want to show "B(bold)" "I(italics)" and the rest of the inline functionalities in a dropdown instead of having the whole inline as dropdown

@jpuri also, why can't we customise the drop-down icon for toolbar icons it seems to take the first option of the subarray (i.e) If I wan to but inline in a drop-down shouldn't I have an option to add an icon for the drop-down instead of using "Bold" as the icon just because it is the first function

@jpuri Can we insert link to custom toolbar values like we do in summernote?

ui.dropdown({
        //items: ['thing', 'thing2'],
        contents: "<li><a href='#' data-name='test' data-url='www.example.com'>example</a></li>",
        callback: function (items) {
          $(items).find('li a').on('click', function(){
            context.invoke("editor.createLink", {text: $(this).data('name'), url: $(this).data('url'), isNewWindow: true });
          })
        }
      })
Was this page helpful?
0 / 5 - 0 ratings

Related issues

rajasekar-ideas2it picture rajasekar-ideas2it  路  4Comments

Pixelatex picture Pixelatex  路  3Comments

ananddodia picture ananddodia  路  4Comments

FriOne picture FriOne  路  3Comments

gyarasu picture gyarasu  路  4Comments