Making a configurable color picker for Episerver: Part 1

When I first had to create a color picker for Episerver (which was some time ago now) there was an absolute dearth of information or options (I ended up creating one from scratch). However, things have moved on and now there are some good starting points, for example:

  • The custom property documented in this blog post (and GitHub repository) by Håkon Nordli
  • This blog post on using the default Dijit color palette in Episerver by Steve Celius
  • And finally, this blog post detailing how to customize the default Dijit color palette by Mark van Dijk

There are probably more, but these are all good places to start. All these examples beg the question: why another? I think there are a few good reasons:

  1. It would be great not to persist the chosen Hex code but instead an ID for the color, this would allow us to change out a color and all usages would update.
  2. It would be nice to be able to define a different palette for each site in a multi-site setup.
  3. Combining 1 and 2, would mean the color palette and editor selected color would all change when content is moved between sites.

The goal of this blog post is therefore to put together the Dojo part of the puzzle and persist an ID instead of a Hex code, the part 2 will wire the whole thing up — including the configuration and multi-site stuff.

I really like the approach that Mark van Dijk took, because I like the idea of using the out-of-the-box Dojo (or Dijit) functionality and the Dijit _PaletteMixin base class is intended exactly for this usage.

Step one is taking his ColorPickerEditorDescriptor and changing the colors we pass to the widget to include an ID (and name), this ends up looking like:

[EditorDescriptorRegistration(TargetType = typeof(int?), UIHint = "ColorPicker")]
public class ColorPickerEditorDescriptor : EditorDescriptor
{
    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
    {
        var colors = new[]
        {
            new { Value = "#fff", Name = "White", Id = 1 }, 
            new { Value = "#ff0000", Name = "Red", Id = 2 },
            new { Value = "#0000ff", Name = (string)null, Id = 3 },
            new { Value = "#008000", Name = "Green", Id = 4 }
        };

        ClientEditingClass = "alloy/editors/ColorPalette";
        EditorConfiguration["colors"] = colors;
        EditorConfiguration["maxColumns"] = 3;

        base.ModifyMetadata(metadata, attributes);
    }
}

As you can see our objects now include the actual color (as a Hex code), an ID and an optional name, ultimately all these values won't be hard-coded, but we'll address that in part 2. I also removed the use of a two-dimensional array to create the columns and rows in the editor descriptor, so now we can just pass the color objects in an array as well as a maxColumns value which will be used to split them into rows in the widget (again, we'll make this configurable).

This brings us on to the actual widget:

define([
    "dojo/text!./templates/ColorPalette.html",
    "dijit/_Widget",
    "dijit/_TemplatedMixin",
    "dijit/_WidgetsInTemplateMixin",
    "dijit/_PaletteMixin",
    "dojo/_base/declare",
    "dojo/dom-construct",
    "dojo/string"
], function (template, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _PaletteMixin, declare, domConstruct, string) {
 
    var ColorPalette = declare("alloy.editors.ColorPalette", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _PaletteMixin], {

        templateString: template,

        widgetsInTemplate: true,
 
        clearSelection: function() {
            this.set("value", null);
        },
 
        _dyeFactory: function (value, row, col, title) {
            return new this._dyeClass(value, row, col, title);
        },

        buildRendering: function () {
            this.inherited(arguments);

            // Extract all the colors
            var values = this.colors.map(a => a.value);

            var paletteValues = [];

            // Separate into rows, based on the user specified 'maxColumns' value
            while (values.length) {
                paletteValues.push(values.splice(0, this.maxColumns));
            }

            var tooltips = {};

            // Extract the tooltips, using the color name or Hex code
            // if no name is supplied.
            for (var i = 0; i < this.colors.length; i++) {
                tooltips[this.colors[i].value] = this.colors[i].name || this.colors[i].value;
            }

            this._dyeClass = declare(ColorPalette._Color, {
                colors: this.colors
            });

            this._preparePalette(paletteValues, tooltips);
        }
    });

    ColorPalette._Color = declare(null, {
        template:
            "<span class='dijitInline dijitPaletteImg' style='line-height:normal;padding-bottom:1px;'>" +
                "<img src='${blankGif}' alt='${alt}' title='${title}' class='dijitColorPaletteSwatch' style='width:20px;height:20px;background-color: ${color}'/>" +
            "</span>",

        constructor: function (alias, row, col, title) {
            // Find the color based on it's Hex
            var color = this.colors.find(o => { return o.value === alias; });

            this._color = alias;
            this._title = title;
            this._value = color.id;
        },

        getValue: function () {
            return this._value;
        },

        fillCell: function (cell, blankGif) {
            var html = string.substitute(this.template, {
                color: this._color,
                blankGif: blankGif,
                alt: this._title,
                title: this._title
            });

            domConstruct.place(html, cell);
        }
    });
 
    return ColorPalette;
});

I tried to simplify this as much as possible, but it still draws heavily on the aforementioned blog post.

Looking at the 'buildRendering' method you can see that the color objects coming from the editor descriptor are separated out into the colors (and then into rows and columns that make up the palette) and the tooltips are also extracted from the object.

The major difference, however, is that the ID is set in the _Color constructor and this is returned by the 'getValue' method, meaning that value of the widget is now a nullable integer (and not a string). This means it can be used like this:

[UIHint("ColorPicker")]
public virtual int? Color { get; set; }

In the editor UI it should look something like this (the tooltip value is replaced by name — if one is given):

Color palette widget

Next blog post we'll do the fun part (in my opinion) and wire the thing up, getting the values from a config file and resolving the correct ones based on the given site.

Comments

There are zero comments 😢