diff --git a/src/mami.css/colpick.css b/src/mami.css/colpick.css index 8223512..6f960b5 100644 --- a/src/mami.css/colpick.css +++ b/src/mami.css/colpick.css @@ -157,11 +157,13 @@ } .colpick-presets-option { display: block; + border: 0; width: 40px; height: 40px; border-radius: 5px; text-decoration: none; color: #fff; + cursor: pointer; } .colpick-presets-option:focus { box-shadow: 0 0 0 1px #fff, 0 0 0 2px #000, inset 0 0 0 1px #000; @@ -175,6 +177,8 @@ grid-template-columns: repeat(12, minmax(0, 1fr)); } .colpick-grid-option { + display: block; + border: 0; width: 23px; height: 23px; z-index: 1; diff --git a/src/mami.js/colour.js b/src/mami.js/colour.js new file mode 100644 index 0000000..fdc5284 --- /dev/null +++ b/src/mami.js/colour.js @@ -0,0 +1,69 @@ +const MamiColour = (() => { + const readThres = 168, + lumiRed = .299, + lumiGreen = .587, + lumiBlue = .114; + + const pub = {}; + + const extractRGB = raw => [ + (raw >> 16) & 0xFF, + (raw >> 8) & 0xFF, + raw & 0xFF, + ]; + + const luminance = raw => { + const rgb = extractRGB(raw); + return rgb[0] * lumiRed + + rgb[1] * lumiGreen + + rgb[2] * lumiBlue; + }; + + const weightedNumber = (num1, num2, weight) => { + weight = Math.min(1, Math.max(0, weight)); + return Math.round((num1 * weight) + (num2 * (1 - weight))); + }; + + const weighted = (raw1, raw2, weight) => { + const rgb1 = extractRGB(raw1), + rgb2 = extractRGB(raw2); + return (weightedNumber(rgb1[0], rgb2[0], weight) << 16) + | (weightedNumber(rgb1[1], rgb2[1], weight) << 8) + | weightedNumber(rgb1[2], rgb2[2], weight); + }; + + pub.extractRGB = extractRGB; + pub.weightedNumber = weightedNumber; + pub.luminance = luminance; + pub.weighted = weighted; + + pub.text = raw => luminance(raw) > readThres ? 0 : 0xFFFFFF; + + pub.shaded = (raw, offset) => { + if(offset == 0) + return raw; + + let dir = 0xFFFFFF; + if(offset < 0) { + dir = 0; + offset *= -1; + } + + return weighted(dir, raw, offset); + }; + + pub.hexByte = raw => { + let str = raw.toString(16).substring(0, 2); + return str.length < 2 + ? `0${str}` : str; + }; + + pub.hex = raw => { + let str = raw.toString(16).substring(0, 6); + if(str.length < 6) + str = '000000'.substring(str.length) + str; + return `#${str}`; + }; + + return pub; +})(); diff --git a/src/mami.js/colpick/picker.jsx b/src/mami.js/colpick/picker.jsx index a3730d5..193f42c 100644 --- a/src/mami.js/colpick/picker.jsx +++ b/src/mami.js/colpick/picker.jsx @@ -1,3 +1,8 @@ +#include colour.js +#include colpick/tgrid.jsx +#include colpick/tpresets.jsx +#include colpick/tsliders.jsx + const MamiColourPicker = function(callback, options, colour, onClose) { if(typeof callback !== 'function') return; @@ -6,59 +11,6 @@ const MamiColourPicker = function(callback, options, colour, onClose) { if(typeof colour !== 'number') colour = parseInt(colour || 0); - const readThres = 168, - lumiRed = .299, - lumiGreen = .587, - lumiBlue = .114; - - const hexFormat = raw => { - let str = raw.toString(16).substring(0, 6); - if(str.length < 6) - str = '000000'.substring(str.length) + str; - return `#${str}`; - }; - - const extractRGB = raw => [ - (raw >> 16) & 0xFF, - (raw >> 8) & 0xFF, - raw & 0xFF, - ]; - - const calcLumi = raw => { - const rgb = extractRGB(raw); - return rgb[0] * lumiRed - + rgb[1] * lumiGreen - + rgb[2] * lumiBlue; - }; - - const textColour = raw => calcLumi(raw) > readThres ? 0 : 0xFFFFFF; - - const weightNum = (n1, n2, w) => { - w = Math.min(1, Math.max(0, w)); - return Math.round((n1 * w) + (n2 * (1 - w))); - }; - - const weightColour = (c1, c2, w) => { - c1 = extractRGB(c1); - c2 = extractRGB(c2); - return (weightNum(c1[0], c2[0], w) << 16) - | (weightNum(c1[1], c2[1], w) << 8) - | weightNum(c1[2], c2[2], w); - }; - - const shadeColour = (raw, offset) => { - if(offset == 0) - return raw; - - let dir = 0xFFFFFF; - if(offset < 0) { - dir = 0; - offset *= -1; - } - - return weightColour(dir, raw, offset); - }; - const verifyOption = (name, type, def) => { if(typeof options[name] !== type) options[name] = def; @@ -76,34 +28,27 @@ const MamiColourPicker = function(callback, options, colour, onClose) { const onColourChange = []; const runOnColourChange = () => { - const text = textColour(colour); + const text = MamiColour.text(colour); for(const handler of onColourChange) handler(colour, text); }; - const setColour = raw => { - colour = parseInt(raw || 0) & 0xFFFFFF; + const setColour = (raw, mask) => { + raw = raw === undefined ? 0 : parseInt(raw); + mask = mask === undefined ? ~0 : parseInt(mask); + + colour = (colour & ~mask) | (raw & mask); runOnColourChange(); }; - const runCallback = apply => { - const result = Object.defineProperties({}, { - apply: { value: apply }, - colour: { value: colour }, - hex: { get() { return hexFormat(colour); } }, - }); - - callback(result); - }; - const apply = () => { - runCallback(true); + callback(colour); if(options.autoClose) close(); }; const cancel = () => { - runCallback(false); + callback(null); if(options.autoClose) close(); }; @@ -118,8 +63,8 @@ const MamiColourPicker = function(callback, options, colour, onClose) { const container =
; onColourChange.push((colour, text) => { - container.style.setProperty('--colpick-colour', hexFormat(colour)); - container.style.setProperty('--colpick-text', hexFormat(text)); + container.style.setProperty('--colpick-colour', MamiColour.hex(colour)); + container.style.setProperty('--colpick-text', MamiColour.hex(text)); }); const form =
{ @@ -155,8 +100,11 @@ const MamiColourPicker = function(callback, options, colour, onClose) { } }; - const createTab = (id, name, construct) => { - const tabContainer = construct(); + const createTab = (id, name, tabInfo) => { + tabInfo.onUpdate(setColour); + onColourChange.push(value => tabInfo.updateColour(value)); + + const tabContainer = tabInfo.getElement(); tabContainer.className = `colpick-tab-container colpick-tab-${id}-container colpick-tab-container-inactive`; tabbedContainer.appendChild(tabContainer); @@ -173,88 +121,13 @@ const MamiColourPicker = function(callback, options, colour, onClose) { }; if(options.showPresetsTab) - createTab('presets', 'Presets', () => { - const presets = options.presets; - const cont =
; - - for(const preset of presets) { - const option = setColour(preset.c)}/>; - onColourChange.push(value => option.classList.toggle('colpick-presets-option-active', value === preset.c)); - cont.appendChild(option); - } - - return cont; - }); + createTab('presets', 'Presets', new MamiColourPickerPresetsTab(options.presets)); if(options.showGridTab) - createTab('grid', 'Grid', () => { - const greys = [0xFFFFFF, 0xEBEBEB, 0xD6D6D6, 0xC2C2C2, 0xADADAD, 0x999999, 0x858585, 0x707070, 0x5C5C5C, 0x474747, 0x333333, 0]; - const colours = [0x00A1D8, 0x0061FE, 0x4D22B2, 0x982ABC, 0xB92D5D, 0xFF4015, 0xFF6A00, 0xFFAB01, 0xFDC700, 0xFEFB41, 0xD9EC37, 0x76BB40]; - const shades = [-.675, -.499, -.345, -.134, 0, .134, .345, .499, .675]; - - const cont =
; - - for(const grey of greys) { - const option = setColour(grey)}/>; - onColourChange.push(value => option.classList.toggle('colpick-grid-option-active', value === grey)); - cont.appendChild(option); - } - - for(const shade of shades) - for(const colour of colours) { - const shaded = shadeColour(colour, shade); - const option = setColour(shaded)}/>; - onColourChange.push(value => option.classList.toggle('colpick-grid-option-active', value === shaded)); - cont.appendChild(option); - } - - return cont; - }); + createTab('grid', 'Grid', new MamiColourPickerGridTab()); if(options.showSlidersTab) - createTab('sliders', 'Sliders', () => { - const cont =
; - - const addSlider = (id, name, update, apply) => { - let sGradient, sValueSlider, sValueInput; - - const sCont =
-
{name}
-
-
- {sGradient =
{ if(ev.button === 0) setColour(apply(colour, Math.floor(255 * (Math.max(0, Math.min(200, (ev.layerX - 5))) / 200)))) }}/>} - {sValueSlider = setColour(apply(colour, sValueSlider.value))}/>} -
-
- {sValueInput = setColour(apply(colour, sValueInput.value))}/>} -
-
-
; - cont.appendChild(sCont); - - onColourChange.push(colour => { - sCont.style.setProperty('--colpick-slider-colour', hexFormat(apply(0, update(colour)))); - sValueSlider.value = update(colour); - sValueInput.value = update(colour); - sGradient.style.background = `linear-gradient(to right, ${hexFormat(apply(colour, 0))}, ${hexFormat(apply(colour, 0xFF))})`; - }); - }; - - addSlider('red', 'Red', - value => (value >> 16) & 0xFF, - (colour, value) => (colour & 0xFFFF) | (Math.min(255, Math.max(0, value)) << 16) - ); - addSlider('green', 'Green', - value => (value >> 8) & 0xFF, - (colour, value) => (colour & 0xFF00FF) | (Math.min(255, Math.max(0, value)) << 8) - ); - addSlider('blue', 'Blue', - value => value & 0xFF, - (colour, value) => (colour & 0xFFFF00) | Math.min(255, Math.max(0, value)) - ); - - return cont; - }); + createTab('sliders', 'Sliders', new MamiColourPickerSlidersTab()); if(activeTab) { height += 261; @@ -285,7 +158,7 @@ const MamiColourPicker = function(callback, options, colour, onClose) { }; if(options.showHexValue) - addValue('hex', 'Hex', 'text', value => hexFormat(value), value => { + addValue('hex', 'Hex', 'text', value => MamiColour.hex(value), value => { while(value.substring(0, 1) === '#') value = value.substring(1); value = value.substring(0, 6); diff --git a/src/mami.js/colpick/tgrid.jsx b/src/mami.js/colpick/tgrid.jsx new file mode 100644 index 0000000..80b808a --- /dev/null +++ b/src/mami.js/colpick/tgrid.jsx @@ -0,0 +1,38 @@ +#include colour.js + +const MamiColourPickerGridTab = function() { + let onUpdate = () => {}; + + const greys = [0xFFFFFF, 0xEBEBEB, 0xD6D6D6, 0xC2C2C2, 0xADADAD, 0x999999, 0x858585, 0x707070, 0x5C5C5C, 0x474747, 0x333333, 0]; + const colours = [0x00A1D8, 0x0061FE, 0x4D22B2, 0x982ABC, 0xB92D5D, 0xFF4015, 0xFF6A00, 0xFFAB01, 0xFDC700, 0xFEFB41, 0xD9EC37, 0x76BB40]; + const shades = [-.675, -.499, -.345, -.134, 0, .134, .345, .499, .675]; + + const html =
; + + for(const grey of greys) + html.appendChild(