forked from tsai/budibase
8 changed files with 339 additions and 174 deletions
@ -0,0 +1,32 @@ |
|||||
|
<script> |
||||
|
import Slider from "./Slider.svelte"; |
||||
|
import Palette from "./Palette.svelte"; |
||||
|
|
||||
|
export let value = "#00000000"; |
||||
|
|
||||
|
let h = 0; |
||||
|
let s = 0; |
||||
|
let v = 0; |
||||
|
let a = 1; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.colorpicker-container { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
height: 300px; |
||||
|
width: 250px; |
||||
|
background: #ffffff; |
||||
|
border: 1px solid #e8e8ef; |
||||
|
border-radius: 2px; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<div class="colorpicker-container"> |
||||
|
|
||||
|
<Palette {h} {a} /> |
||||
|
|
||||
|
<Slider type="hue" on:change={hue => (h = hue.detail)} /> |
||||
|
|
||||
|
<Slider type="alpha" on:change={alpha => (a = alpha.detail)} /> |
||||
|
</div> |
||||
@ -0,0 +1,49 @@ |
|||||
|
<script> |
||||
|
import { onMount } from "svelte"; |
||||
|
export let h, |
||||
|
a = 0; |
||||
|
|
||||
|
let palette; |
||||
|
let dimensions; |
||||
|
|
||||
|
onMount(() => { |
||||
|
if (palette) { |
||||
|
dimensions = palette.getBoundingClientRect(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
function handleClick(event) { |
||||
|
const { left, top, width, height } = dimensions; |
||||
|
let pickerX = (event.clientX - left) / width; |
||||
|
let pickerY = (event.clientY - top) / height; |
||||
|
//Saturation - adds white: pickerX * 100 |
||||
|
//value - adds black: 100 - pickerY * 100 |
||||
|
console.log("X", pickerX, "Y", pickerY); |
||||
|
} |
||||
|
|
||||
|
$: paletteGradient = `linear-gradient(to top, rgba(0, 0, 0, 1), transparent), |
||||
|
linear-gradient(to left, hsla(${h}, 100%, 50%, ${a}), rgba(255, 255, 255, ${a})) |
||||
|
`; |
||||
|
$: style = `background: ${paletteGradient};`; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.palette { |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
height: 175px; |
||||
|
} |
||||
|
|
||||
|
.picker { |
||||
|
position: absolute; |
||||
|
width: 16px; |
||||
|
height: 16px; |
||||
|
background: transparent; |
||||
|
border: 2px solid white; |
||||
|
border-radius: 50%; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<div bind:this={palette} on:click={handleClick} class="palette" {style}> |
||||
|
<div class="picker" /> |
||||
|
</div> |
||||
@ -0,0 +1,87 @@ |
|||||
|
<script> |
||||
|
import { onMount, createEventDispatcher } from "svelte"; |
||||
|
import dragable from "./drag.js"; |
||||
|
|
||||
|
export let type = "hue"; |
||||
|
|
||||
|
const dispatch = createEventDispatcher(); |
||||
|
|
||||
|
let slider; |
||||
|
let dimensions = {}; |
||||
|
let thumbPosition = 0; |
||||
|
|
||||
|
onMount(() => { |
||||
|
if (slider) { |
||||
|
dimensions = slider.getBoundingClientRect(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
function handleClick(mouseX) { |
||||
|
const { left, width } = dimensions; |
||||
|
let clickPosition = mouseX - left; |
||||
|
debugger; |
||||
|
if (clickPosition >= 0 && clickPosition <= width) { |
||||
|
thumbPosition = clickPosition; |
||||
|
let percentageClick = thumbPosition / width; |
||||
|
let value = |
||||
|
type === "hue" |
||||
|
? Math.round(360 * percentageClick).toString() |
||||
|
: percentageClick.toFixed(2); |
||||
|
dispatch("change", value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$: style = `transform: translateX(${thumbPosition - 6}px);`; |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.color-format-slider { |
||||
|
position: relative; |
||||
|
align-self: center; |
||||
|
height: 8px; |
||||
|
width: 220px; |
||||
|
border-radius: 10px; |
||||
|
margin: 10px 0px; |
||||
|
border: 1px solid #e8e8ef; |
||||
|
} |
||||
|
|
||||
|
.hue { |
||||
|
background: linear-gradient( |
||||
|
to right, |
||||
|
hsl(0, 100%, 50%), |
||||
|
hsl(60, 100%, 50%), |
||||
|
hsl(120, 100%, 50%), |
||||
|
hsl(180, 100%, 50%), |
||||
|
hsl(240, 100%, 50%), |
||||
|
hsl(300, 100%, 50%), |
||||
|
hsl(360, 100%, 50%) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
.alpha { |
||||
|
background: linear-gradient(to right, transparent, rgb(0 0 0)); |
||||
|
} |
||||
|
|
||||
|
.slider-thumb { |
||||
|
position: absolute; |
||||
|
bottom: -3px; |
||||
|
height: 12px; |
||||
|
width: 12px; |
||||
|
border: 1px solid black; |
||||
|
border-radius: 50%; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<div |
||||
|
bind:this={slider} |
||||
|
on:click={event => handleClick(event.clientX)} |
||||
|
class="color-format-slider" |
||||
|
class:hue={type === 'hue'} |
||||
|
class:alpha={type === 'alpha'}> |
||||
|
<div |
||||
|
use:dragable |
||||
|
on:drag={e => handleClick(e.detail)} |
||||
|
class="slider-thumb" |
||||
|
{style} /> |
||||
|
</div> |
||||
@ -0,0 +1,23 @@ |
|||||
|
export default function(node) { |
||||
|
function handleMouseDown() { |
||||
|
window.addEventListener('mousemove', handleMouseMove); |
||||
|
window.addEventListener('mouseup', handleMouseUp); |
||||
|
} |
||||
|
|
||||
|
function handleMouseMove(event) { |
||||
|
let mouseX = event.clientX; |
||||
|
console.log(mouseX); |
||||
|
node.dispatchEvent( |
||||
|
new CustomEvent('drag', { |
||||
|
detail: mouseX |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
function handleMouseUp() { |
||||
|
window.removeEventListener('mousedown', handleMouseDown); |
||||
|
window.removeEventListener('mousemove', handleMouseMove); |
||||
|
} |
||||
|
|
||||
|
node.addEventListener('mousedown', handleMouseDown); |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
import Colorpicker from './Colorpicker.svelte'; |
||||
|
export default Colorpicker; |
||||
@ -0,0 +1,146 @@ |
|||||
|
export const getRgbaValues = (rgbaString) => rgbaString.replace(/[a-z()\s]/gi, '').split(','); |
||||
|
|
||||
|
export const getHexaValues = (hexString) => hexString.match(/[A-F]{2}|[A-F]\d{1}|\d{2}|\d[A-F]/gi); |
||||
|
|
||||
|
export const isValidRgba = (rgba) => { |
||||
|
let [ r, g, b, a = 1 ] = rgba; |
||||
|
|
||||
|
let isValidLengthRange = rgba.length === 3 || rgba.length === 4; |
||||
|
let isValidColorRange = [ r, g, b ].every((v) => v >= 0 && v <= 255); |
||||
|
let isValidAlphaRange = rgba.length === 3 || (a >= 0 && a <= 1); |
||||
|
|
||||
|
return isValidLengthRange && isValidColorRange && isValidAlphaRange; |
||||
|
}; |
||||
|
|
||||
|
export const determineColorType = (color) => { |
||||
|
let hsva = []; |
||||
|
if (color.startsWith('#')) { |
||||
|
let [ rHex, gHex, bHex, aHex ] = getHexaValues(color); |
||||
|
hsva = hexaToHSVA([ rHex, gHex, bHex ], aHex); |
||||
|
} else if (color.startsWith('rgb')) { |
||||
|
let rgba = getRgbaValues(color); |
||||
|
hsva = rgbaToHSVA(rgba); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export const getHSLA = (hsv, a) => { |
||||
|
const [ h, s, l ] = hsvToHSL(hsv); |
||||
|
return `hsla(${h}, ${s}, ${l}, ${a})`; |
||||
|
}; |
||||
|
|
||||
|
export const hexaToHSVA = (hex, alpha = 'FF') => { |
||||
|
const rgba = hex.map((v) => parseInt(v, 16)).concat((parseInt(alpha, 16) / 255).toFixed(1)); |
||||
|
return rgbaToHSVA(rgba); |
||||
|
}; |
||||
|
|
||||
|
export const rgbaToHSVA = (rgba) => { |
||||
|
if (isValidRgba(rgba)) { |
||||
|
const [ r, g, b, a = '1' ] = rgba; |
||||
|
let hsv = rgbToHSV([ r, g, b ]); |
||||
|
return [ ...hsv, a ]; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export const hsvaToHexa = () => { |
||||
|
const [ r, g, b, a ] = hsvaToRgba(); |
||||
|
const hexa = [ r, g, b ].map((v) => v.toString(16)).concat((a * 255).toFixed(1).toString(16)); |
||||
|
return hexa; |
||||
|
}; |
||||
|
|
||||
|
export const hsvaToRgba = (h, s, v, a) => { |
||||
|
let rgb = _hsvToRgb([ h, s, v ]); |
||||
|
return [ ...rgb, a ]; |
||||
|
}; |
||||
|
|
||||
|
//Credit : https://github.com/Qix-/color-convert
|
||||
|
export const _rgbToHSV = (rgb) => { |
||||
|
let rdif; |
||||
|
let gdif; |
||||
|
let bdif; |
||||
|
let h; |
||||
|
let s; |
||||
|
|
||||
|
const r = rgb[0] / 255; |
||||
|
const g = rgb[1] / 255; |
||||
|
const b = rgb[2] / 255; |
||||
|
const v = Math.max(r, g, b); |
||||
|
const diff = v - Math.min(r, g, b); |
||||
|
const diffc = function(c) { |
||||
|
return (v - c) / 6 / diff + 1 / 2; |
||||
|
}; |
||||
|
|
||||
|
if (diff === 0) { |
||||
|
h = 0; |
||||
|
s = 0; |
||||
|
} else { |
||||
|
s = diff / v; |
||||
|
rdif = diffc(r); |
||||
|
gdif = diffc(g); |
||||
|
bdif = diffc(b); |
||||
|
|
||||
|
if (r === v) { |
||||
|
h = bdif - gdif; |
||||
|
} else if (g === v) { |
||||
|
h = 1 / 3 + rdif - bdif; |
||||
|
} else if (b === v) { |
||||
|
h = 2 / 3 + gdif - rdif; |
||||
|
} |
||||
|
|
||||
|
if (h < 0) { |
||||
|
h += 1; |
||||
|
} else if (h > 1) { |
||||
|
h -= 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const hsvResult = [ h * 360, s * 100, v * 100 ].map((v) => v.toFixed(0)); |
||||
|
return hsvResult; |
||||
|
}; |
||||
|
|
||||
|
//Credit : https://github.com/Qix-/color-convert
|
||||
|
export const _hsvToRgb = (hsv) => { |
||||
|
const h = hsv[0] / 60; |
||||
|
const s = hsv[1] / 100; |
||||
|
let v = hsv[2] / 100; |
||||
|
const hi = Math.floor(h) % 6; |
||||
|
|
||||
|
const f = h - Math.floor(h); |
||||
|
const p = 255 * v * (1 - s); |
||||
|
const q = 255 * v * (1 - s * f); |
||||
|
const t = 255 * v * (1 - s * (1 - f)); |
||||
|
v *= 255; |
||||
|
|
||||
|
switch (hi) { |
||||
|
case 0: |
||||
|
return [ v, t, p ]; |
||||
|
case 1: |
||||
|
return [ q, v, p ]; |
||||
|
case 2: |
||||
|
return [ p, v, t ]; |
||||
|
case 3: |
||||
|
return [ p, q, v ]; |
||||
|
case 4: |
||||
|
return [ t, p, v ]; |
||||
|
case 5: |
||||
|
return [ v, p, q ]; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
//Credit : https://github.com/Qix-/color-convert
|
||||
|
export const _hsvToHSL = (hsv) => { |
||||
|
const h = hsv[0]; |
||||
|
const s = hsv[1] / 100; |
||||
|
const v = hsv[2] / 100; |
||||
|
const vmin = Math.max(v, 0.01); |
||||
|
let sl; |
||||
|
let l; |
||||
|
|
||||
|
l = (2 - s) * v; |
||||
|
const lmin = (2 - s) * vmin; |
||||
|
sl = s * vmin; |
||||
|
sl /= lmin <= 1 ? lmin : 2 - lmin; |
||||
|
sl = sl || 0; |
||||
|
l /= 2; |
||||
|
|
||||
|
return [ h, sl * 100, l * 100 ]; |
||||
|
}; |
||||
@ -1,66 +0,0 @@ |
|||||
import { |
|
||||
getHexaValues, |
|
||||
getRgbaValues, |
|
||||
hsvToHSL, |
|
||||
isValidRgba, |
|
||||
rgbToHSV, |
|
||||
hsvToRgb, |
|
||||
} from "./utils" |
|
||||
|
|
||||
export class HSVAColor { |
|
||||
h = 0 |
|
||||
s = 0 |
|
||||
v = 0 |
|
||||
a = 1 |
|
||||
|
|
||||
constructor(color) { |
|
||||
if (color.startsWith("#")) { |
|
||||
let [rHex, gHex, bHex, aHex] = getHexaValues(color) |
|
||||
this.hexaToHSVA([rHex, gHex, bHex], aHex) |
|
||||
} else if (color.startsWith("rgb")) { |
|
||||
let rgba = getRgbaValues(color) |
|
||||
this.rgbaToHSVA(rgba) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
setHSVA([h, s, v, a]) { |
|
||||
this.h = h |
|
||||
this.s = s |
|
||||
this.v = v |
|
||||
this.a = a |
|
||||
} |
|
||||
|
|
||||
getHSLA() { |
|
||||
const [h, s, l] = hsvToHSL([this.h, this.s, this.v]) |
|
||||
return `hsla(${h}, ${s}, ${l}, ${this.a})` |
|
||||
} |
|
||||
|
|
||||
hexaToHSVA(hex, alpha = "FF") { |
|
||||
const rgba = hex |
|
||||
.map(v => parseInt(v, 16)) |
|
||||
.concat((parseInt(alpha, 16) / 255).toFixed(1)) |
|
||||
this.rgbaToHSVA(rgba) |
|
||||
} |
|
||||
|
|
||||
rgbaToHSVA(rgba) { |
|
||||
if (isValidRgba(rgba)) { |
|
||||
const [r, g, b, a = "1"] = rgba |
|
||||
let hsv = rgbToHSV([r, g, b]) |
|
||||
this.setHSVA([...hsv, a]) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
hsvaToHexa() { |
|
||||
const [r, g, b, a] = this.hsvaToRgba() |
|
||||
const hexa = [r, g, b] |
|
||||
.map(v => v.toString(16)) |
|
||||
.concat((a * 255).toFixed(1).toString(16)) |
|
||||
return hexa |
|
||||
} |
|
||||
|
|
||||
hsvaToRgba() { |
|
||||
const { h, s, v, a } = this |
|
||||
let rgb = hsvToRgb([h, s, v]) |
|
||||
return [...rgb, a] |
|
||||
} |
|
||||
} |
|
||||
@ -1,108 +0,0 @@ |
|||||
export const getRgbaValues = rgbaString => |
|
||||
rgbaString.replace(/[a-z\(\)\s]/gi, "").split(",") |
|
||||
|
|
||||
export const getHexaValues = hexString => |
|
||||
hexString.match(/[A-F]{2}|[A-F]\d{1}|\d{2}|\d[A-F]/gi) |
|
||||
|
|
||||
export const isValidRgba = rgba => { |
|
||||
let [r, g, b, a = 1] = rgba |
|
||||
|
|
||||
let isValidLengthRange = rgba.length === 3 || rgba.length === 4 |
|
||||
let isValidColorRange = [r, g, b].every(v => v >= 0 && v <= 255) |
|
||||
let isValidAlphaRange = rgba.length === 3 || (a >= 0 && a <= 1) |
|
||||
|
|
||||
return isValidLengthRange && isValidColorRange && isValidAlphaRange |
|
||||
} |
|
||||
|
|
||||
//Credit : https://github.com/Qix-/color-convert
|
|
||||
export const rgbToHSV = rgb => { |
|
||||
let rdif |
|
||||
let gdif |
|
||||
let bdif |
|
||||
let h |
|
||||
let s |
|
||||
|
|
||||
const r = rgb[0] / 255 |
|
||||
const g = rgb[1] / 255 |
|
||||
const b = rgb[2] / 255 |
|
||||
const v = Math.max(r, g, b) |
|
||||
const diff = v - Math.min(r, g, b) |
|
||||
const diffc = function(c) { |
|
||||
return (v - c) / 6 / diff + 1 / 2 |
|
||||
} |
|
||||
|
|
||||
if (diff === 0) { |
|
||||
h = 0 |
|
||||
s = 0 |
|
||||
} else { |
|
||||
s = diff / v |
|
||||
rdif = diffc(r) |
|
||||
gdif = diffc(g) |
|
||||
bdif = diffc(b) |
|
||||
|
|
||||
if (r === v) { |
|
||||
h = bdif - gdif |
|
||||
} else if (g === v) { |
|
||||
h = 1 / 3 + rdif - bdif |
|
||||
} else if (b === v) { |
|
||||
h = 2 / 3 + gdif - rdif |
|
||||
} |
|
||||
|
|
||||
if (h < 0) { |
|
||||
h += 1 |
|
||||
} else if (h > 1) { |
|
||||
h -= 1 |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const hsvResult = [h * 360, s * 100, v * 100].map(v => v.toFixed(0)) |
|
||||
return hsvResult |
|
||||
} |
|
||||
|
|
||||
//Credit : https://github.com/Qix-/color-convert
|
|
||||
export const hsvToRgb = hsv => { |
|
||||
const h = hsv[0] / 60 |
|
||||
const s = hsv[1] / 100 |
|
||||
let v = hsv[2] / 100 |
|
||||
const hi = Math.floor(h) % 6 |
|
||||
|
|
||||
const f = h - Math.floor(h) |
|
||||
const p = 255 * v * (1 - s) |
|
||||
const q = 255 * v * (1 - s * f) |
|
||||
const t = 255 * v * (1 - s * (1 - f)) |
|
||||
v *= 255 |
|
||||
|
|
||||
switch (hi) { |
|
||||
case 0: |
|
||||
return [v, t, p] |
|
||||
case 1: |
|
||||
return [q, v, p] |
|
||||
case 2: |
|
||||
return [p, v, t] |
|
||||
case 3: |
|
||||
return [p, q, v] |
|
||||
case 4: |
|
||||
return [t, p, v] |
|
||||
case 5: |
|
||||
return [v, p, q] |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
//Credit : https://github.com/Qix-/color-convert
|
|
||||
export const hsvToHSL = hsv => { |
|
||||
const h = hsv[0] |
|
||||
const s = hsv[1] / 100 |
|
||||
const v = hsv[2] / 100 |
|
||||
const vmin = Math.max(v, 0.01) |
|
||||
let sl |
|
||||
let l |
|
||||
|
|
||||
l = (2 - s) * v |
|
||||
const lmin = (2 - s) * vmin |
|
||||
sl = s * vmin |
|
||||
sl /= lmin <= 1 ? lmin : 2 - lmin |
|
||||
sl = sl || 0 |
|
||||
l /= 2 |
|
||||
|
|
||||
return [h, sl * 100, l * 100] |
|
||||
} |
|
||||
Loading…
Reference in new issue