Implement Excel Color Picker

My collegues are emotionally attachetd to Excel color picker (hexagon). I wish I was joking. Is there any way to implement such solution into Dash?

link1
link2

So, far I am using dbc client-side option which is great but apperantly doesn’t suit everybody.

Edit:
Adding more info:

HTML

Demo

Hi @ davzup89. Did you try ColorPicker from dash-daq?

Best start of a topic ever. :rofl:

Color swatches from dmc are not an option neither, right?

2 Likes

Tried all of these. Nothing really wrong with any of them. It’s probably just a matter of habit in corporate world.

.jsscript from demo above is a good starting point I reckon. I hope i get some time to look into it.

		<script>
			const allColors=['#003366','#336699','#3366CC','#003399','#000099','#0000CC','#000066','#006666','#006699','#0099CC','#0066CC','#0033CC','#0000FF','#3333FF','#333399','#669999','#009999','#33CCCC','#00CCFF','#0099FF','#0066FF','#3366FF','#3333CC','#666699','#339966','#00CC99','#00FFCC','#00FFFF','#33CCFF','#3399FF','#6699FF','#6666FF','#6600FF','#6600CC','#339933','#00CC66','#00FF99','#66FFCC','#66FFFF','#66CCFF','#99CCFF','#9999FF','#9966FF','#9933FF','#9900FF','#006600','#00CC00','#00FF00','#66FF99','#99FFCC','#CCFFFF','#CCCCFF','#CC99FF','#CC66FF','#CC33FF','#CC00FF','#9900CC','#003300','#009933','#33CC33','#66FF66','#99FF99','#CCFFCC','#FFFFFF','#FFCCFF','#FF99FF','#FF66FF','#FF00FF','#CC00CC','#660066','#336600','#009900','#66FF33','#99FF66','#CCFF99','#FFFFCC','#FFCCCC','#FF99CC','#FF66CC','#FF33CC','#CC0099','#993399','#333300','#669900','#99FF33','#CCFF66','#FFFF99','#FFCC99','#FF9999','#FF6699','#FF3399','#CC3399','#990099','#666633','#99CC00','#CCFF33','#FFFF66','#FFCC66','#FF9966','#FF6666','#FF0066','#CC6699','#993366','#999966','#CCCC00','#FFFF00','#FFCC00','#FF9933','#FF6600','#FF5050','#CC0066','#660033','#996633','#CC9900','#FF9900','#CC6600','#FF3300','#FF0000','#CC0000','#990033','#663300','#996600','#CC3300','#993300','#990000','#800000','#993333'];
			const hexUnicode='⬢';
			var n=0;
			var hexNums=[7, 8, 9, 10, 11, 12];
			var hexArrs=[];
			var hexNum;
			for(hexNum of hexNums) {
				hexArr=[];
				for(n=0;n<hexNum;n++) hexArr.push(hexUnicode);
				hexArrs.push(hexArr);
			}
			hexArr=[];
			for(n=0;n<13;n++) hexArr.push(hexUnicode);
			hexArrs.push(hexArr);
			hexArr=[];
			var hexNums2=JSON.parse(JSON.stringify(hexNums));
			hexNums2.reverse();
			for(hexNum of hexNums2) {
				hexArr=[];
				for(n=0;n<hexNum;n++) hexArr.push(hexUnicode);
				hexArrs.push(hexArr);
			}
			var output=document.getElementById("output");
			for(var hexArrLine of hexArrs) {
				var hexLine='<span class="colorHex">'+hexArrLine.join('</span><span class="colorHex">') + '</span>';
				output.insertAdjacentHTML('beforeend', '<span class="condensed-line">'+hexLine+'</span>');
			}

			// Taken from: https://www.30secondsofcode.org/js/s/hex-to-rgb
			const hexToRGB = (hex) => {
			  let h = hex.slice(hex.startsWith('#') ? 1 : 0);
			  if (h.length === 3) h = [...h].map(x => x + x).join('');
			  h = parseInt(h, 16);
			  return {
			  	'r': (h >>> 16),
			  	'g': ((h & 0x00ff00) >>> 8),
			  	'b': ((h & 0x0000ff) >>> 0)
			  };
			};


			var selectedHexColor=document.getElementById('selectedHexColor');
			var selectedRgbColor=document.getElementById('selectedRgbColor');

			var colorHexs=document.getElementsByClassName('colorHex');
			var h=0;
			for(var colorHex of colorHexs) {
				colorHex['style']['webkit-text-fill-color']=allColors[h];
				colorHex['style']['color']=allColors[h];
				colorHex['style']['-webkit-text-stroke']='3.75px '+allColors[h];

				colorHex.setAttribute('data-hex',allColors[h]);
				let rgbObj=hexToRGB(allColors[h]);
				colorHex.setAttribute('data-r',  rgbObj['r']);
				colorHex.setAttribute('data-g',  rgbObj['g']);
				colorHex.setAttribute('data-b',  rgbObj['b']);

				colorHex.addEventListener('click', (evt) => {
					let hexSrcElement=evt.srcElement;

					let selectedColorHex=document.getElementsByClassName('color-selected');
					if(selectedColorHex.length>0) {
						selectedColorHex[0].classList.remove('color-selected');
					}
					hexSrcElement.classList.add('color-selected');

					let dataset=hexSrcElement.dataset;
					let hexVal=dataset.hex;
					hexInputVal.value=hexVal.toLowerCase();
					selectedHexColor['style']['background']=hexVal;

					let rVal=dataset['r'];
					let gVal=dataset['g'];
					let bVal=dataset['b'];
					rInputVal.value=rVal;
					gInputVal.value=gVal;
					bInputVal.value=bVal;
					let rgbVal=`rgb(${rVal}, ${gVal}, ${bVal})`;
					rgbInputVal.value=rgbVal;
					selectedRgbColor['style']['background']=rgbVal;
				}, false);
				h++;
			}
			colorHexs[Math.floor(Math.random() * 127)].click();
		</script>

I can actually add most of the hex codes from excel hexagon into swatches. Do you think there’s a way to highlight the choosen color with swatches and also use it as dbc.input (type=“color”) where certain swatch get highlighet when the same color is chosen on plot?

Hi @davzup89 I’m not sure, if I understand you completely.

with some kind of a different border color/thickness?

What plot? How could that be selected and how would you use the dbc.Input()?

It could be black border.

When you click on data series which is designated by green line on a given plot, a callback is triggered that shows green swatch with black border. This works well with dbc.Input(type=‘color’) where the callback can update the Input based on chosen color.

In my head this should be possible. Could you provide some sample code for that? And even if this wasn’t possible with the swatches, we could just create small html.Div() with an ID to change the style.

Excel demo from first post has such black border.

Regarding the color update, I achieve it this with ‘clickData’ and then in callback function I do:

    current_color = fig['data'][clicked_series].get('line', {}).get('color', '#000000')  # Default to black

This obviously updates dbc.input in a way that it shows the color of the chosen line. With mantine I imagine the chosen color would get black border or smth.

Works well for updating/chaging color on plots where there a lot of data series.

I recreated some sort of excel (basic) color panel with mantine swatches. The top colors are also the same as in ‘Dark24’ colorscale:

image

Here is the code if anybody finds it suitable:

    swatches = [
    '#FFFFFF',
    '#222A2A',
    '#FB0D0D',
    '#0D2A63',
    '#511CFB',
    '#EB663B',
    '#E15F99',
    '#FFC000',
    '#2E91E5',
    '#1CA71C',
    '#F2F2F2',
    '#808080',
    '#FFB9B9',
    '#D6DCE4',
    '#D9E1F2',
    '#FCE4D6',
    '#FFC5FB',
    '#FFF2CC',
    '#DDEBF7',
    '#E2EFDA',
    '#D9D9D9',
    '#595959',
    '#FD5F5F',
    '#ACB9CA',
    '#B4C6E7',
    '#F8CBAD',
    '#FF89FC',
    '#FFE699',
    '#BDD7EE',
    '#C6E0B4',
    '#BFBFBF',
    '#404040',
    '#F03838',
    '#8497B0',
    '#8EA9DB',
    '#F4B084',
    '#FE1EDE',
    '#FFD966',
    '#9BC2E6',
    '#A9D08E',
    '#A6A6A6',
    '#262626',
    '#E60000',
    '#333F4F',
    '#305496',
    '#C65911',
    '#CE04C0',
    '#BF8F00',
    '#2F75B5',
    '#548235',
    '#808080',
    '#0D0D0D',
    '#A20000',
    '#222B35',
    '#203764',
    '#833C0C',
    '#820479',
    '#806000',
    '#1F4E78',
    '#375623'
]


    color_picker = html.Div(
        [
            dmc.ColorPicker(id="color-picker", swatches=swatches, swatchesPerRow=10, withPicker=False)
        ]
    )
2 Likes

Hey @davzup89 I had something like this in mind:

import dash
from dash import html, dcc, Input, Output, ALL, State
from dash.exceptions import PreventUpdate
import plotly.graph_objects as go
import numpy as np

swatches = [
    '#FFFFFF',
    '#222A2A',
    '#FB0D0D',
    '#0D2A63',
    '#511CFB',
    '#EB663B',
    '#E15F99',
    '#FFC000',
    '#2E91E5',
    '#1CA71C',
    '#F2F2F2',
    '#808080',
    '#FFB9B9',
    '#D6DCE4',
    '#D9E1F2',
    '#FCE4D6',
    '#FFC5FB',
    '#FFF2CC',
    '#DDEBF7',
    '#E2EFDA',
    '#D9D9D9',
    '#595959',
    '#FD5F5F',
    '#ACB9CA',
    '#B4C6E7',
    '#F8CBAD',
    '#FF89FC',
    '#FFE699',
    '#BDD7EE',
    '#C6E0B4',
    '#BFBFBF',
    '#404040',
    '#F03838',
    '#8497B0',
    '#8EA9DB',
    '#F4B084',
    '#FE1EDE',
    '#FFD966',
    '#9BC2E6',
    '#A9D08E',
    '#A6A6A6',
    '#262626',
    '#E60000',
    '#333F4F',
    '#305496',
    '#C65911',
    '#CE04C0',
    '#BF8F00',
    '#2F75B5',
    '#548235',
    '#808080',
    '#0D0D0D',
    '#A20000',
    '#222B35',
    '#203764',
    '#833C0C',
    '#820479',
    '#806000',
    '#1F4E78',
    '#375623'
]

fig = go.Figure()
for c in swatches:
    fig.add_scatter(
        x=np.arange(len(swatches)),
        y=np.random.randint(0, 2000, len(swatches)),
        mode='lines',
        name=c,
        line_color=c
    )

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        dcc.Graph(
            id='graph',
            figure=fig
        ),
        html.Div(
            [
                html.Div(
                    id={'type': 'swatch', 'index': idx},
                    className='swatch',
                    style={'background-color': color}
                )
                for idx, color in enumerate(swatches)
            ],
            className='container'
        )
    ]
)


@app.callback(
    Output({'type': 'swatch', 'index': ALL}, 'style'),
    Input('graph', 'clickData'),
    State({'type': 'swatch', 'index': ALL}, 'style'),
    prevent_initil_call=True
)
def do(click_data, current_styles):
    if not click_data:
        raise PreventUpdate

    i = click_data['points'][0]['curveNumber']

    for counter, style_dict in enumerate(current_styles):
        if counter == i:
            current_styles[counter].update({'outline-color': 'red', 'outline-width': '2px'})
        else:
            current_styles[counter].update({'outline-color': 'black', 'outline-width': '1px'})

    return current_styles


if __name__ == "__main__":
    app.run(debug=True)

custom.css

.container {
    width: 1000px;
    height: 600px;
}

.swatch {
    width: 92px;
    height: 92px;
    margin: 4px;
    outline: 1px solid;
    float: left;
}

You could style the swatches as you like, hexagons, for example.

3 Likes

This is great :slight_smile:

1 Like

Just a teaser…
image

2 Likes

Hey @davzup89, here’s a new version of the color picker. There might be some tweaking necessary concerning css, styling or number of colors. This works for exactly 61 colors :-).

demo

3 Likes

Marvellous job! Thank you :slight_smile: