medium-editor icon indicating copy to clipboard operation
medium-editor copied to clipboard

medium-editor support for font color?

Open Yaphats opened this issue 9 years ago • 21 comments

Thank you for all medium-editor contributors,It is a really nice html5 editor. But for some reasons we need change font size and font color ,will medium-editor add this feature ?

Yaphats avatar Mar 30 '15 10:03 Yaphats

We are discussing the font-size feature as part of issue #376 and pull request #494

We can start a discussion on how we'd like to do font-color as part of this issue (I'll rename the issue).

nmielnik avatar Mar 30 '15 13:03 nmielnik

Similar to fontsize, the browsers have native font color support via document.execCommand('foreColor', false, '#ffffff'). Similar to font size, this will result in a font tag being created, but this will also created a color attribute:

<font color="#ffffff">

nmielnik avatar Mar 30 '15 13:03 nmielnik

slightly terrified at the cross-browser implications here :)

would be trivial to support foreColor, but would need to include a UX for a color picker, which could get large. Seems like some of these built-in extensions would better serve as "external", too? eg: not included in the medium-editor.js by default? (only in the interest of keeping core codebase smallish). Also if foreColor is using inline font tags with [color], the removeFormat will need to ensure that, and the #376 code wouldn't be able to just-remove the font element for clear. just thinking out loud.

phiggins42 avatar Mar 30 '15 13:03 phiggins42

additional outloud thinking: should go ahead and do foreColor backColor as near-identical things. Wouldn't be horrible to provide the 'default' color selection as just accepting a string hex value to use, but provide enough hook to allow someone to use any of the plethora of available colorpicker components available in the wild. just need to define the API properly as to how a hasForm type extension/button reports values/changes/etc and should be fairly straightforward to allow.

phiggins42 avatar Mar 30 '15 15:03 phiggins42

For anyone interested, I have completed an integration of the Spectrum color picker (https://github.com/bgrins/spectrum). My implementation is based partly on the code from Issue #257 and from the examples to create your own plugin.

My Extension Javascript file:

var colorPickerExtension;
(function (window, document) {
    'use strict';
    function colorPickerDerived() {// we set our options
        this.parent = true;
        this.options = {
            name: 'colorPicker',
            action: 'applyForeColor',
            aria: 'color picker',
            tagnames: ['p', 'h3', 'h4'],
            contentDefault: '<input type="text" class="MediumEditorColorPicker" />'
        };
        this.name = 'colorPicker';
    }

    colorPickerDerived.prototype = {
        handleClick: function (evt) {
            evt.preventDefault();
            evt.stopPropagation();

            var self = this.base;

            self.saveSelection();

            // If no text selected, stop here.
            if( self.selectionState.end - self.selectionState.start === 0 ) {
                return;
            }

            var colorpicker = $('.MediumEditorColorPicker');
            var this_color = document.queryCommandValue('foreColor');
            colorpicker.spectrum('set', this_color);

            //handle the change event
            colorpicker.on('change.spectrum', function (e, color) {
                self.restoreSelection();//make sure we still have a selection
                var new_color = color.toHexString();
                self.options.ownerDocument.execCommand('styleWithCSS', false, true);//we want css color
                self.options.ownerDocument.execCommand('foreColor', false, new_color);
            });
            colorpicker.spectrum('toggle');//open spectrum

            return false;
        }
    };
    colorPickerExtension = Util.derives(MediumEditor.statics.DefaultButton, colorPickerDerived);
}(window, document));

Using this in the Medium Editor, I include the above file after including Medium Editor (as well as the 'util.js' file from the 'src' folder).

In my script to initiate Medium Editor, I have the following related code:

$.fn.spectrum.load = false; // I want to initialize Spectrum myself
//in your list of buttons we need: 'colorPicker'
//in your list of extensions we need: 'colorPicker': new colorPickerExtension
//my initialization for Spectrum:
jQuery('.MediumEditorColorPicker').spectrum({
    //set your options here. I found it best to provide a palette for users to select
});
//after Spectrum initialization
jQuery('.medium-editor-action-colorPicker *').unbind();//if the original bind events remain on Spectrum, it fails in Chrome, Safari, Opera and Internet Explorer (works in Firefox)

I also had to adjust the padding of the button around the color picker (padding top and bottom a little bit less to vertical center the button).

This was tested in Firefox, Chrome, Opera, Internet Explorer 11, and should work in a little bit older versions as long as both Medium Editor and Spectrum supports them.

I made use of jQuery in my code since it is a requirement for some other plugins and extensions I need (since I already need it, I might as well just use it).

Jacotheron avatar Apr 21 '15 15:04 Jacotheron

Jacotheron - thank you for posting! This is exactly what I was looking for - an easy integration with medium-editor and spectrum. I updated the CSS for the color picker to more closely resemble the default medium-editor theme, and added a small snippet to insert the colorpicker into the medium-editor dom node so that the medium-editor does not close when selecting a color. Looks great thanks again tremendous amount of time you saved me!

trockets avatar May 28 '15 23:05 trockets

It seems like there might be something we could do inside the editor to help support extensions like this. It's worth thinking about.

Worst case, we should consider adding this example in the documentation perhaps?

nmielnik avatar Jun 24 '15 22:06 nmielnik

If somebody needs really simple, vanilla javascript picker, this is my implementation. I forked vanilla-color-picker to make some adjustments for medium-editor styling.

    /**
     * Custom `color picker` extension
     */
    var ColorPickerExtension;

    function ColorPickerDerived() {
      this.parent = true;
      this.options = {
        name: "colorPicker",
        action: "applyForeColor",
        aria: "color picker",
        tagname: ["p", "h3", "h4"],
        contentDefault: "<span class='editor-color-picker'>Text Color<span>"
      };
      this.name = "colorPicker";
    }

    ColorPickerDerived.prototype = {
      handleClick: function(e) {
        e.preventDefault();
        e.stopPropagation();

        var self = this.base;
        self.saveSelection();

        // If no text selected, stop here.
        if( self.selectionState && (self.selectionState.end - self.selectionState.start === 0) ) {
          return;
        }

        // colors for picker
        var pickerColors = [ 
          "#1abc9c",
          "#2ecc71",
          "#3498db",
          "#9b59b6",
          "#34495e",
          "#16a085",
          "#27ae60",
          "#2980b9",
          "#8e44ad",
          "#2c3e50",
          "#f1c40f",
          "#e67e22",
          "#e74c3c",
          "#bdc3c7",
          "#95a5a6",
          "#f39c12"
        ];

        var picker = vanillaColorPicker(document.querySelector(".medium-editor-toolbar-active .editor-color-picker"));
        picker.set("customColors", pickerColors);
        picker.set("positionOnTop");
        picker.openPicker();
        picker.on("colorChosen", function(color) {
          self.restoreSelection();
          self.options.ownerDocument.execCommand("styleWithCSS", false, true);
          self.options.ownerDocument.execCommand("foreColor", false, color);
        });
      }
    };

    ColorPickerExtension = MediumEditor.util.derives(MediumEditor.statics.DefaultButton, ColorPickerDerived);

The final result looks like this:

twipe_publishing_tools

Olgagr avatar Jul 10 '15 07:07 Olgagr

@Olgagr thanks, but it seems to me this is for v4. Cause util.derives is gone in v5

benjamin79 avatar Jul 20 '15 10:07 benjamin79

@benjamin79 Yes, this is for v4. I'm using version4 right now.

Olgagr avatar Jul 20 '15 16:07 Olgagr

An updated version of that code that would work with v5 would be something like this:

    /**
     * Custom `color picker` extension
     */
    var ColorPickerExtension = MediumEditor.extensions.button.extend({
        name: "colorPicker",
        action: "applyForeColor",
        aria: "color picker",
        contentDefault: "<span class='editor-color-picker'>Text Color<span>",

        handleClick: function(e) {
            e.preventDefault();
            e.stopPropagation();

            this.selectionState = this.base.exportSelection();

            // If no text selected, stop here.
            if(this.selectionState && (this.selectionState.end - this.selectionState.start === 0) ) {
              return;
            }

            // colors for picker
            var pickerColors = [ 
              "#1abc9c",
              "#2ecc71",
              "#3498db",
              "#9b59b6",
              "#34495e",
              "#16a085",
              "#27ae60",
              "#2980b9",
              "#8e44ad",
              "#2c3e50",
              "#f1c40f",
              "#e67e22",
              "#e74c3c",
              "#bdc3c7",
              "#95a5a6",
              "#f39c12"
            ];

            var picker = vanillaColorPicker(this.document.querySelector(".medium-editor-toolbar-active .editor-color-picker"));
            picker.set("customColors", pickerColors);
            picker.set("positionOnTop");
            picker.openPicker();
            picker.on("colorChosen", function(color) {
              this.base.importSelection(this.selectionState);
              this.document.execCommand("styleWithCSS", false, true);
              this.document.execCommand("foreColor", false, color);
            }.bind(this));
        }
    });

nmielnik avatar Jul 20 '15 16:07 nmielnik

+1 A color picker option would be ideal, if not integrated directly, by providing a way to integrate it. It can be a quite important factor that can determine which editor to choose.

alvarotrigo avatar Apr 15 '16 21:04 alvarotrigo

It would be great if you could add some codepen or jsfiddle with the suggestions. I almost got it working with spectrum but I would like to keep the editor toollbar visible while I change from one color to another.

alvarotrigo avatar Apr 15 '16 22:04 alvarotrigo

Well, here's what I got: https://jsfiddle.net/ay9cLgab/ Not 100% perfect, as the medium editor toolbar gets hidden when clicking outside the colors' area of the colorpicker, but mostly functional.

Is there anyway to tell medium editor not to hide the toolbar when for example, clicking over the hexadecimal color input box?

alvarotrigo avatar Apr 15 '16 23:04 alvarotrigo

@alvarotrigo thanks for sharing that fiddle, pretty sweet to see a working color picking example for others to model after...have you considered offering what you made as an external npm package or repo so others can use it and/or improve on it? We can call it out in our Extensions documentation and on the landing page.

One way to prevent the toolbar from hiding when displaying is to have the element that you're displaying be a descendant of the toolbar itself. There is code within the event handling that checks if the element receiving focus is a child of the toolbar, and if it is it won't hide the toolbar. This might be problematic for what you're trying to do, but it's one way that things will just work as they are.

There was also a discussion in #830 about trying to better expose a way for extensions to prevent the toolbar from hiding when they're being interacted with, so it anyone wanted to take a crack at implementing the approach called out in #830 then that would be an even better solution for your issue.

nmielnik avatar Apr 16 '16 20:04 nmielnik

@nmielnik I ended up modifying medium-editor a bit as a quick solution to the problem.

I basically added a new check on the hideToolbar function:

hideToolbar: function() {
    if (this.isDisplayed() && this.isColorPickerHidden()) {  //added here
        this.getToolbarElement().classList.remove('medium-editor-toolbar-active');
        this.trigger('hideToolbar', {}, this.base.getFocusedElement());
    }
},
isColorPickerHidden: function(){
    var picker = document.querySelector('.sp-container');
    return picker? this.hasClass(picker, 'sp-hidden') : true;
},

//This method might not be necessary. It just implements the equivalent to jQuery hasClass. 
//but I didn't spent time to check if medium-editor already has an equivalent. 
hasClass: function(ele, cls){
    return !!ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
},

But to make it more generic, a method like keepOpen(true) to set a flag could be an easy way to solve it.

alvarotrigo avatar Apr 17 '16 01:04 alvarotrigo

Regarding the input box with text, I ended up finding another solution. I basically kept record of the selection just before opening the color picker. Then I imported it again just before applying the color.

Here's the complete jsfiddle solution with the medium-editor modification + this last improvements regarding the input box.

alvarotrigo avatar Apr 17 '16 01:04 alvarotrigo

@alvarotrigo thanks for sharing your solution on this, it works great.

nmielnik avatar Apr 25 '16 13:04 nmielnik

but the active state for color picker button was missing.. how to achieve that??

ramthenmala avatar Dec 13 '16 06:12 ramthenmala

Simple implementation work with 5.23.1 based by https://github.com/yabwe/medium-editor/issues/523#issuecomment-120263143 and https://github.com/yabwe/medium-editor/issues/523#issuecomment-122942628 and https://github.com/yabwe/medium-editor/issues/844

You need include https://github.com/miroshko/vanilla-color-picker.

    /**
     * Custom `color picker` extension
     */
    var ColorPickerExtension = MediumEditor.Extension.extend({
      name: "colorPicker",

      init: function () {
        this.button = this.document.createElement('button');
        this.button.classList.add('medium-editor-action');
        this.button.classList.add('editor-color-picker');
        this.button.title = 'Text color'
        this.button.innerHTML = '<i class="fa fa-paint-brush"></i>';

        this.on(this.button, 'click', this.handleClick.bind(this));
      },

      getButton: function () {
        return this.button;
      },

      handleClick: function (e) {
        e.preventDefault();
        e.stopPropagation();

        this.selectionState = this.base.exportSelection();

        // If no text selected, stop here.
        if (this.selectionState && (this.selectionState.end - this.selectionState.start === 0)) {
          return;
        }

        // colors for picker
        var pickerColors = [
          "#1abc9c",
          "#2ecc71",
          "#3498db",
          "#9b59b6",
          "#34495e",
          "#16a085",
          "#27ae60",
          "#2980b9",
          "#8e44ad",
          "#2c3e50",
          "#f1c40f",
          "#e67e22",
          "#e74c3c",
          "#bdc3c7",
          "#95a5a6",
          "#f39c12"
        ];

        var picker = vanillaColorPicker(this.document.querySelector(".medium-editor-toolbar-active .editor-color-picker").parentNode);
        picker.set("customColors", pickerColors);
        picker.set("positionOnTop");
        picker.openPicker();
        picker.on("colorChosen", function (color) {
          this.base.importSelection(this.selectionState);
          this.document.execCommand("styleWithCSS", false, true);
          this.document.execCommand("foreColor", false, color);
        }.bind(this));
      }
    });

Then in MediumEditor initialization

var editor = new MediumEditor('.editable', {
  toolbar: {
    buttons: ['bold', 'italic', 'underline', 'colorPicker']
  },
  extensions: {
    'colorPicker': new ColorPickerExtension()
  }
});

havran avatar Jun 30 '17 08:06 havran

@havran I Love U

rageshS avatar Apr 24 '19 08:04 rageshS