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