Merge pull request #447 from Budibase/colorpicker/post-publish-tidyup
Tidyup and Deletion of old files
This commit is contained in:
commit
61a31a7867
|
@ -57,6 +57,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.15.8",
|
"@budibase/bbui": "^1.15.8",
|
||||||
"@budibase/client": "^0.1.1",
|
"@budibase/client": "^0.1.1",
|
||||||
|
"@budibase/colorpicker": "^1.0.1",
|
||||||
"@nx-js/compiler-util": "^2.0.0",
|
"@nx-js/compiler-util": "^2.0.0",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"codemirror": "^5.51.0",
|
"codemirror": "^5.51.0",
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
// import { HsvPicker } from "svelte-color-picker"
|
|
||||||
|
|
||||||
// export let initialValue = "#ffffff"
|
|
||||||
export let onChange = color => {}
|
|
||||||
export let open = false
|
|
||||||
let value = "#ffffff"
|
|
||||||
|
|
||||||
let _justMounted = true //see onColorChange
|
|
||||||
let pickerHeight = 275
|
|
||||||
let colorPreview
|
|
||||||
let pickerTopPosition = null
|
|
||||||
|
|
||||||
function rbgaToHexa({ r, g, b, a }) {
|
|
||||||
r = r.toString(16)
|
|
||||||
g = g.toString(16)
|
|
||||||
b = b.toString(16)
|
|
||||||
a = Math.round(a * 255).toString(16)
|
|
||||||
|
|
||||||
if (r.length == 1) r = "0" + r
|
|
||||||
if (g.length == 1) g = "0" + g
|
|
||||||
if (b.length == 1) b = "0" + b
|
|
||||||
if (a.length == 1) a = "0" + a
|
|
||||||
|
|
||||||
return "#" + r + g + b + a
|
|
||||||
}
|
|
||||||
|
|
||||||
function onColourChange(rgba) {
|
|
||||||
value = rbgaToHexa(rgba.detail)
|
|
||||||
|
|
||||||
//Hack: so that color change doesn't fire onMount
|
|
||||||
if (!_justMounted) {
|
|
||||||
// onChange(value)
|
|
||||||
}
|
|
||||||
_justMounted = false
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleColorpicker(isOpen) {
|
|
||||||
if (isOpen) {
|
|
||||||
const {
|
|
||||||
y: previewYPosition,
|
|
||||||
height: previewHeight,
|
|
||||||
} = colorPreview.getBoundingClientRect()
|
|
||||||
|
|
||||||
let wiggleRoom = window.innerHeight - previewYPosition
|
|
||||||
let displayTop = wiggleRoom < pickerHeight
|
|
||||||
|
|
||||||
if (displayTop) {
|
|
||||||
pickerTopPosition = previewYPosition - (pickerHeight - window.scrollY)
|
|
||||||
} else {
|
|
||||||
pickerTopPosition = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
open = isOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
$: style = open ? "display: block;" : "display: none;"
|
|
||||||
$: pickerStyle = pickerTopPosition ? `top: ${pickerTopPosition}px;` : ""
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={colorPreview}
|
|
||||||
on:click={() => toggleColorpicker(true)}
|
|
||||||
class="color-preview"
|
|
||||||
style={`background: ${value}`} />
|
|
||||||
|
|
||||||
<div class="colorpicker" {style}>
|
|
||||||
<div class="overlay" on:click|self={() => toggleColorpicker(false)} />
|
|
||||||
<div class="cp" style={pickerStyle}>
|
|
||||||
<!-- <HsvPicker on:colorChange={onColourChange} startColor={value} /> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--
|
|
||||||
OLD LOCAL STORAGE OPTIONS. INCLUDING FOR ADDING LATER
|
|
||||||
function getRecentColors() {
|
|
||||||
let colorStore = localStorage.getItem("bb:recentColors")
|
|
||||||
if (!!colorStore) {
|
|
||||||
swatches = JSON.parse(colorStore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setRecentColor(color) {
|
|
||||||
if (swatches.length >= 15) {
|
|
||||||
swatches.splice(0, 1)
|
|
||||||
picker.removeSwatch(0)
|
|
||||||
}
|
|
||||||
if (!swatches.includes(color)) {
|
|
||||||
swatches = [...swatches, color]
|
|
||||||
picker.addSwatch(color)
|
|
||||||
localStorage.setItem("bb:recentColors", JSON.stringify(swatches))
|
|
||||||
}
|
|
||||||
} -->
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.overlay {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
/* background: rgba(5, 5, 5, 0.25); */
|
|
||||||
}
|
|
||||||
|
|
||||||
.cp {
|
|
||||||
position: absolute;
|
|
||||||
right: 25px;
|
|
||||||
}
|
|
||||||
.color-preview {
|
|
||||||
height: 30px;
|
|
||||||
width: 100%;
|
|
||||||
margin: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid gainsboro;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,23 +0,0 @@
|
||||||
export default function(node) {
|
|
||||||
function handleMouseDown() {
|
|
||||||
window.addEventListener("mousemove", handleMouseMove)
|
|
||||||
window.addEventListener("mouseup", handleMouseUp)
|
|
||||||
node.dispatchEvent(new CustomEvent("dragstart"))
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMouseMove(event) {
|
|
||||||
node.dispatchEvent(
|
|
||||||
new CustomEvent("drag", {
|
|
||||||
detail: { mouseX: event.clientX, mouseY: event.clientY },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMouseUp() {
|
|
||||||
window.removeEventListener("mousedown", handleMouseDown)
|
|
||||||
window.removeEventListener("mousemove", handleMouseMove)
|
|
||||||
node.dispatchEvent(new CustomEvent("dragend"))
|
|
||||||
}
|
|
||||||
|
|
||||||
node.addEventListener("mousedown", handleMouseDown)
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
export { default as drag } from "./drag.js"
|
|
||||||
export { default as keyevents } from "./key-events.js"
|
|
|
@ -1,41 +0,0 @@
|
||||||
//events: Array<{trigger: fn}>
|
|
||||||
export default function(node, events = []) {
|
|
||||||
const ev = Object.entries(events)
|
|
||||||
let fns = []
|
|
||||||
|
|
||||||
for (let [trigger, fn] of ev) {
|
|
||||||
let f = addEvent(trigger, fn)
|
|
||||||
fns = [...fns, f]
|
|
||||||
}
|
|
||||||
|
|
||||||
function _scaffold(trigger, fn) {
|
|
||||||
return () => {
|
|
||||||
let trig = parseInt(trigger)
|
|
||||||
if (trig) {
|
|
||||||
if (event.keyCode === trig) {
|
|
||||||
fn(event)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (event.key === trigger) {
|
|
||||||
fn(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addEvent(trigger, fn) {
|
|
||||||
let f = _scaffold(trigger, fn)
|
|
||||||
node.addEventListener("keydown", f)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeEvents() {
|
|
||||||
fns.forEach(f => node.removeEventListener("keypress", f))
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
destroy() {
|
|
||||||
removeEvents()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
<script>
|
|
||||||
import FlatButton from "./FlatButton.svelte"
|
|
||||||
import { createEventDispatcher } from "svelte"
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
export let format = "hex"
|
|
||||||
|
|
||||||
let colorFormats = ["hex", "rgb", "hsl"]
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flatbutton-group">
|
|
||||||
{#each colorFormats as text}
|
|
||||||
<FlatButton
|
|
||||||
selected={format === text}
|
|
||||||
{text}
|
|
||||||
on:click={() => dispatch('click', text)} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.flatbutton-group {
|
|
||||||
font-weight: 500;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
justify-content: center;
|
|
||||||
width: 170px;
|
|
||||||
height: 30px;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,26 +0,0 @@
|
||||||
<script>
|
|
||||||
import { buildStyle } from "../helpers.js"
|
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
|
|
||||||
export let backgroundSize = "10px"
|
|
||||||
export let borderRadius = ""
|
|
||||||
export let height = ""
|
|
||||||
export let width = ""
|
|
||||||
export let margin = ""
|
|
||||||
|
|
||||||
$: style = buildStyle({ backgroundSize, borderRadius, height, width, margin })
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div in:fade {style}>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
background-image: url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');
|
|
||||||
height: fit-content;
|
|
||||||
width: fit-content;
|
|
||||||
width: -moz-fit-content;
|
|
||||||
height: -moz-fit-content;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,349 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount, createEventDispatcher } from "svelte"
|
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
import Swatch from "./Swatch.svelte"
|
|
||||||
import CheckedBackground from "./CheckedBackground.svelte"
|
|
||||||
import { buildStyle } from "../helpers.js"
|
|
||||||
import {
|
|
||||||
getColorFormat,
|
|
||||||
convertToHSVA,
|
|
||||||
convertHsvaToFormat,
|
|
||||||
} from "../utils.js"
|
|
||||||
import Slider from "./Slider.svelte"
|
|
||||||
import Palette from "./Palette.svelte"
|
|
||||||
import ButtonGroup from "./ButtonGroup.svelte"
|
|
||||||
import Input from "./Input.svelte"
|
|
||||||
import Portal from "./Portal.svelte"
|
|
||||||
import { keyevents } from "../actions"
|
|
||||||
|
|
||||||
export let value = "#3ec1d3ff"
|
|
||||||
export let open = false
|
|
||||||
export let swatches = []
|
|
||||||
|
|
||||||
export let disableSwatches = false
|
|
||||||
export let format = "hexa"
|
|
||||||
export let style = ""
|
|
||||||
export let pickerHeight = 0
|
|
||||||
export let pickerWidth = 0
|
|
||||||
|
|
||||||
let colorPicker = null
|
|
||||||
let adder = null
|
|
||||||
let swatchesSetFromLocalStore = false
|
|
||||||
|
|
||||||
let h = 0
|
|
||||||
let s = 0
|
|
||||||
let v = 0
|
|
||||||
let a = 0
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (!swatches.length > 0) {
|
|
||||||
//Don't use locally stored recent colors if swatches have been passed as props
|
|
||||||
swatchesSetFromLocalStore = true
|
|
||||||
swatches = getRecentColors() || []
|
|
||||||
}
|
|
||||||
|
|
||||||
if (swatches.length > 12) {
|
|
||||||
console.warn(
|
|
||||||
`Colorpicker - ${swatches.length} swatches were provided. Only the first 12 swatches will be displayed.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colorPicker) {
|
|
||||||
colorPicker.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format) {
|
|
||||||
convertAndSetHSVA()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function getRecentColors() {
|
|
||||||
let colorStore = localStorage.getItem("cp:recent-colors")
|
|
||||||
if (colorStore) {
|
|
||||||
return JSON.parse(colorStore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleEscape() {
|
|
||||||
if (open) {
|
|
||||||
open = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setRecentColors(color) {
|
|
||||||
const s = swatchesSetFromLocalStore
|
|
||||||
? swatches
|
|
||||||
: [...getRecentColors(), color]
|
|
||||||
localStorage.setItem("cp:recent-colors", JSON.stringify(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertAndSetHSVA() {
|
|
||||||
let hsva = convertToHSVA(value, format)
|
|
||||||
setHSVA(hsva)
|
|
||||||
}
|
|
||||||
|
|
||||||
function setHSVA([hue, sat, val, alpha]) {
|
|
||||||
h = hue
|
|
||||||
s = sat
|
|
||||||
v = val
|
|
||||||
a = alpha
|
|
||||||
}
|
|
||||||
|
|
||||||
//fired by choosing a color from the palette
|
|
||||||
function setSaturationAndValue({ detail }) {
|
|
||||||
s = detail.s
|
|
||||||
v = detail.v
|
|
||||||
value = convertHsvaToFormat([h, s, v, a], format)
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setHue({ color, isDrag }) {
|
|
||||||
h = color
|
|
||||||
value = convertHsvaToFormat([h, s, v, a], format)
|
|
||||||
if (!isDrag) {
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAlpha({ color, isDrag }) {
|
|
||||||
a = color === "1.00" ? "1" : color
|
|
||||||
value = convertHsvaToFormat([h, s, v, a], format)
|
|
||||||
if (!isDrag) {
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchValue() {
|
|
||||||
dispatch("change", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeFormatAndConvert(f) {
|
|
||||||
format = f.detail
|
|
||||||
value = convertHsvaToFormat([h, s, v, a], format)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleColorInput(text) {
|
|
||||||
format = getColorFormat(text)
|
|
||||||
if (format) {
|
|
||||||
value = text
|
|
||||||
convertAndSetHSVA()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchInputChange() {
|
|
||||||
if (format) {
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addSwatch() {
|
|
||||||
if (format) {
|
|
||||||
if (swatches.length === 12) {
|
|
||||||
swatches.splice(0, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!swatches.includes(value)) {
|
|
||||||
swatches = [...swatches, value]
|
|
||||||
setRecentColors(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch("addswatch", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeSwatch(idx) {
|
|
||||||
let [removedSwatch] = swatches.splice(idx, 1)
|
|
||||||
swatches = swatches
|
|
||||||
dispatch("removeswatch", removedSwatch)
|
|
||||||
if (swatchesSetFromLocalStore) {
|
|
||||||
//as could be a swatch not present in local storage
|
|
||||||
setRecentColors()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function applySwatch(color) {
|
|
||||||
if (value !== color) {
|
|
||||||
format = getColorFormat(color)
|
|
||||||
if (format) {
|
|
||||||
value = color
|
|
||||||
convertAndSetHSVA()
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: border = v > 90 && s < 5 ? "1px dashed #dedada" : ""
|
|
||||||
$: selectedColorStyle = buildStyle({ background: value, border })
|
|
||||||
$: hasSwatches = swatches.length > 0
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Portal>
|
|
||||||
<div
|
|
||||||
class="colorpicker-container"
|
|
||||||
use:keyevents={{ Escape: handleEscape }}
|
|
||||||
transition:fade
|
|
||||||
bind:this={colorPicker}
|
|
||||||
{style}
|
|
||||||
tabindex="0"
|
|
||||||
bind:clientHeight={pickerHeight}
|
|
||||||
bind:clientWidth={pickerWidth}>
|
|
||||||
|
|
||||||
<div class="palette-panel">
|
|
||||||
<Palette on:change={setSaturationAndValue} {h} {s} {v} {a} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-panel">
|
|
||||||
<div class="alpha-hue-panel">
|
|
||||||
<div>
|
|
||||||
<CheckedBackground borderRadius="50%" backgroundSize="8px">
|
|
||||||
<div
|
|
||||||
class="selected-color"
|
|
||||||
title={value}
|
|
||||||
style={selectedColorStyle} />
|
|
||||||
</CheckedBackground>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Slider
|
|
||||||
type="hue"
|
|
||||||
value={h}
|
|
||||||
on:change={hue => setHue(hue.detail)}
|
|
||||||
on:dragend={dispatchValue} />
|
|
||||||
|
|
||||||
<CheckedBackground borderRadius="10px" backgroundSize="7px">
|
|
||||||
<Slider
|
|
||||||
type="alpha"
|
|
||||||
value={a}
|
|
||||||
on:change={(alpha, isDrag) => setAlpha(alpha.detail, isDrag)}
|
|
||||||
on:dragend={dispatchValue} />
|
|
||||||
</CheckedBackground>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if !disableSwatches}
|
|
||||||
<div transition:fade class="swatch-panel">
|
|
||||||
{#if hasSwatches}
|
|
||||||
{#each swatches as color, idx}
|
|
||||||
{#if idx < 12}
|
|
||||||
<Swatch
|
|
||||||
{color}
|
|
||||||
on:click={() => applySwatch(color)}
|
|
||||||
on:removeswatch={() => removeSwatch(idx)} />
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
{#if swatches.length < 12}
|
|
||||||
<div
|
|
||||||
tabindex="0"
|
|
||||||
title="Add Swatch"
|
|
||||||
use:keyevents={{ Enter: addSwatch }}
|
|
||||||
bind:this={adder}
|
|
||||||
transition:fade
|
|
||||||
class="adder"
|
|
||||||
class:shrink={hasSwatches}
|
|
||||||
on:click={addSwatch}>
|
|
||||||
<span>+</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="format-input-panel">
|
|
||||||
<ButtonGroup {format} on:click={changeFormatAndConvert} />
|
|
||||||
<Input
|
|
||||||
{value}
|
|
||||||
on:input={event => handleColorInput(event.target.value)}
|
|
||||||
on:change={dispatchInputChange} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</Portal>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.colorpicker-container {
|
|
||||||
position: absolute;
|
|
||||||
outline: none;
|
|
||||||
z-index: 3;
|
|
||||||
display: flex;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 400;
|
|
||||||
transition: top 0.1s, left 0.1s;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 5px 0px;
|
|
||||||
height: auto;
|
|
||||||
width: 220px;
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 2px;
|
|
||||||
box-shadow: 0 0.15em 1.5em 0 rgba(0, 0, 0, 0.1),
|
|
||||||
0 0 1em 0 rgba(0, 0, 0, 0.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-panel {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-panel {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 8px;
|
|
||||||
background: white;
|
|
||||||
border: 1px solid #d2d2d2;
|
|
||||||
color: #777373;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alpha-hue-panel {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 25px 1fr;
|
|
||||||
grid-gap: 15px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-color {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swatch-panel {
|
|
||||||
flex: 0 0 15px;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
justify-content: flex-start;
|
|
||||||
padding: 0 5px;
|
|
||||||
max-height: 56px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.adder {
|
|
||||||
flex: 1;
|
|
||||||
height: 20px;
|
|
||||||
display: flex;
|
|
||||||
transition: flex 0.3s;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background: #f1f3f4;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid #d4d4d4;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-left: 5px;
|
|
||||||
margin-top: 3px;
|
|
||||||
font-weight: 500;
|
|
||||||
outline-color: #003cb0;
|
|
||||||
outline-width: thin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shrink {
|
|
||||||
flex: 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.format-input-panel {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
padding-top: 3px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,177 +0,0 @@
|
||||||
<script>
|
|
||||||
import Colorpicker from "./Colorpicker.svelte"
|
|
||||||
import CheckedBackground from "./CheckedBackground.svelte"
|
|
||||||
import { createEventDispatcher, beforeUpdate, onMount } from "svelte"
|
|
||||||
|
|
||||||
import { buildStyle, debounce } from "../helpers.js"
|
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
import { getColorFormat } from "../utils.js"
|
|
||||||
|
|
||||||
export let value = "#3ec1d3ff"
|
|
||||||
export let swatches = []
|
|
||||||
export let disableSwatches = false
|
|
||||||
export let open = false
|
|
||||||
export let width = "25px"
|
|
||||||
export let height = "25px"
|
|
||||||
|
|
||||||
let format = "hexa"
|
|
||||||
let dimensions = { top: 0, bottom: 0, right: 0, left: 0 }
|
|
||||||
let positionY = "top"
|
|
||||||
let positionX = "left"
|
|
||||||
let colorPreview = null
|
|
||||||
|
|
||||||
let previewHeight = null
|
|
||||||
let previewWidth = null
|
|
||||||
let pickerWidth = 0
|
|
||||||
let pickerHeight = 0
|
|
||||||
|
|
||||||
let errorMsg = null
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
const dispatchValue = value =>
|
|
||||||
debounce(() => dispatch("change", value), 300, true)
|
|
||||||
|
|
||||||
beforeUpdate(() => {
|
|
||||||
format = getColorFormat(value)
|
|
||||||
if (!format) {
|
|
||||||
errorMsg = `Colorpicker - ${value} is an unknown color format. Please use a hex, rgb or hsl value`
|
|
||||||
console.error(errorMsg)
|
|
||||||
} else {
|
|
||||||
errorMsg = null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function openColorpicker(event) {
|
|
||||||
if (colorPreview) {
|
|
||||||
open = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onColorChange(color) {
|
|
||||||
value = color.detail
|
|
||||||
dispatchValue(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateDimensions() {
|
|
||||||
if (open) {
|
|
||||||
const {
|
|
||||||
top: spaceAbove,
|
|
||||||
width,
|
|
||||||
bottom,
|
|
||||||
right,
|
|
||||||
left,
|
|
||||||
} = colorPreview.getBoundingClientRect()
|
|
||||||
|
|
||||||
const spaceBelow = window.innerHeight - bottom
|
|
||||||
const previewCenter = previewWidth / 2
|
|
||||||
|
|
||||||
let y, x
|
|
||||||
|
|
||||||
if (spaceAbove > spaceBelow) {
|
|
||||||
positionY = "bottom"
|
|
||||||
y = window.innerHeight - spaceAbove
|
|
||||||
} else {
|
|
||||||
positionY = "top"
|
|
||||||
y = bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
// Centre picker by default
|
|
||||||
x = left + previewCenter - 220 / 2
|
|
||||||
|
|
||||||
const spaceRight = window.innerWidth - right
|
|
||||||
|
|
||||||
//Position picker left or right if space not available for centering
|
|
||||||
if (left < 110 && spaceRight > 220) {
|
|
||||||
positionX = "left"
|
|
||||||
x = right
|
|
||||||
} else if (spaceRight < 100 && left > 220) {
|
|
||||||
positionX = "right"
|
|
||||||
x = document.body.clientWidth - left
|
|
||||||
}
|
|
||||||
|
|
||||||
dimensions = { [positionY]: y.toFixed(1), [positionX]: x.toFixed(1) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: if (open && colorPreview) {
|
|
||||||
calculateDimensions()
|
|
||||||
}
|
|
||||||
|
|
||||||
$: previewStyle = buildStyle({ width, height, background: value })
|
|
||||||
$: errorPreviewStyle = buildStyle({ width, height })
|
|
||||||
$: pickerStyle = buildStyle({
|
|
||||||
[positionY]: `${dimensions[positionY]}px`,
|
|
||||||
[positionX]: `${dimensions[positionX]}px`,
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:window on:resize={debounce(calculateDimensions, 200)} />
|
|
||||||
|
|
||||||
<div class="color-preview-container">
|
|
||||||
{#if !errorMsg}
|
|
||||||
<CheckedBackground borderRadius="3px" backgroundSize="8px">
|
|
||||||
<div
|
|
||||||
bind:this={colorPreview}
|
|
||||||
bind:clientHeight={previewHeight}
|
|
||||||
bind:clientWidth={previewWidth}
|
|
||||||
title={value}
|
|
||||||
class="color-preview"
|
|
||||||
style={previewStyle}
|
|
||||||
on:click={openColorpicker} />
|
|
||||||
</CheckedBackground>
|
|
||||||
|
|
||||||
{#if open}
|
|
||||||
<Colorpicker
|
|
||||||
style={pickerStyle}
|
|
||||||
on:change={onColorChange}
|
|
||||||
on:addswatch
|
|
||||||
on:removeswatch
|
|
||||||
bind:format
|
|
||||||
bind:value
|
|
||||||
bind:pickerHeight
|
|
||||||
bind:pickerWidth
|
|
||||||
bind:open
|
|
||||||
{swatches}
|
|
||||||
{disableSwatches} />
|
|
||||||
<div on:click|self={() => (open = false)} class="overlay" />
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<div
|
|
||||||
class="color-preview preview-error"
|
|
||||||
title="Invalid Color"
|
|
||||||
style={errorPreviewStyle}>
|
|
||||||
<span>×</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.color-preview-container {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
height: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-preview {
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid #dedada;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-error {
|
|
||||||
background: #cccccc;
|
|
||||||
color: #808080;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 18px;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,44 +0,0 @@
|
||||||
<script>
|
|
||||||
import { createEventDispatcher } from "svelte"
|
|
||||||
import { keyevents } from "../actions"
|
|
||||||
|
|
||||||
export let text = ""
|
|
||||||
export let selected = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="flatbutton"
|
|
||||||
tabindex="0"
|
|
||||||
use:keyevents={{ Enter: () => dispatch('click') }}
|
|
||||||
class:selected
|
|
||||||
on:click>
|
|
||||||
{text}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.flatbutton {
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid #d4d4d4;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
margin: 5px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
font-size: 10px;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background: #f1f3f4;
|
|
||||||
outline-color: #003cb0;
|
|
||||||
outline-width: thin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #003cb0;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<script>
|
|
||||||
export let value = ""
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input on:input on:change type="text" {value} maxlength="25" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 5px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 175px;
|
|
||||||
font-size: 14px;
|
|
||||||
background: #f1f3f4;
|
|
||||||
border-radius: 8px;
|
|
||||||
height: 20px;
|
|
||||||
outline-color: #003cb0;
|
|
||||||
color: inherit;
|
|
||||||
text-align: center;
|
|
||||||
border: 1px solid #dadada;
|
|
||||||
font-weight: 550;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,81 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount, createEventDispatcher } from "svelte"
|
|
||||||
import { drag } from "../actions"
|
|
||||||
import CheckedBackground from "./CheckedBackground.svelte"
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
export let h = 0
|
|
||||||
export let s = 0
|
|
||||||
export let v = 0
|
|
||||||
export let a = 1
|
|
||||||
|
|
||||||
let palette
|
|
||||||
|
|
||||||
let paletteHeight,
|
|
||||||
paletteWidth = 0
|
|
||||||
|
|
||||||
function handePaletteChange({ mouseX, mouseY }) {
|
|
||||||
const { left, top } = palette.getBoundingClientRect()
|
|
||||||
let x = mouseX - left
|
|
||||||
let y = mouseY - top
|
|
||||||
if (x > 0 && y > 0 && x < paletteWidth && y < paletteHeight) {
|
|
||||||
let s = (x / paletteWidth) * 100
|
|
||||||
let v = 100 - (y / paletteHeight) * 100
|
|
||||||
dispatch("change", { s, v })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: pickerX = (s * paletteWidth) / 100
|
|
||||||
$: pickerY = paletteHeight * ((100 - v) / 100)
|
|
||||||
|
|
||||||
$: 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};`
|
|
||||||
|
|
||||||
$: pickerStyle = `transform: translate(${pickerX - 8}px, ${pickerY - 8}px);`
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CheckedBackground width="100%">
|
|
||||||
<div
|
|
||||||
bind:this={palette}
|
|
||||||
bind:clientHeight={paletteHeight}
|
|
||||||
bind:clientWidth={paletteWidth}
|
|
||||||
on:click={event => handePaletteChange({
|
|
||||||
mouseX: event.clientX,
|
|
||||||
mouseY: event.clientY,
|
|
||||||
})}
|
|
||||||
class="palette"
|
|
||||||
{style}>
|
|
||||||
<div
|
|
||||||
use:drag
|
|
||||||
on:drag={event => handePaletteChange(event.detail)}
|
|
||||||
class="picker"
|
|
||||||
style={pickerStyle} />
|
|
||||||
</div>
|
|
||||||
</CheckedBackground>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.palette {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 140px;
|
|
||||||
cursor: crosshair;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker {
|
|
||||||
position: absolute;
|
|
||||||
cursor: grab;
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
background: transparent;
|
|
||||||
border: 2px solid white;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.picker:active {
|
|
||||||
cursor: grabbing;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,37 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
|
|
||||||
export let target = document.body
|
|
||||||
|
|
||||||
let targetEl
|
|
||||||
let portal
|
|
||||||
let componentInstance
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (typeof target === "string") {
|
|
||||||
targetEl = document.querySelector(target)
|
|
||||||
// Force exit
|
|
||||||
if (targetEl === null) {
|
|
||||||
return () => {}
|
|
||||||
}
|
|
||||||
} else if (target instanceof HTMLElement) {
|
|
||||||
targetEl = target
|
|
||||||
} else {
|
|
||||||
throw new TypeError(
|
|
||||||
`Unknown target type: ${typeof target}. Allowed types: String (CSS selector), HTMLElement.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
portal = document.createElement("div")
|
|
||||||
targetEl.appendChild(portal)
|
|
||||||
portal.appendChild(componentInstance)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
targetEl.removeChild(portal)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={componentInstance}>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
|
@ -1,118 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount, createEventDispatcher } from "svelte"
|
|
||||||
import { drag, keyevents } from "../actions"
|
|
||||||
|
|
||||||
export let value = 1
|
|
||||||
export let type = "hue"
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
let slider
|
|
||||||
let sliderWidth = 0
|
|
||||||
let upperLimit = type === "hue" ? 360 : 1
|
|
||||||
let incrementFactor = type === "hue" ? 1 : 0.01
|
|
||||||
|
|
||||||
const isWithinLimit = value => value >= 0 && value <= upperLimit
|
|
||||||
|
|
||||||
function onSliderChange({ mouseX }, isDrag = false) {
|
|
||||||
const { left, width } = slider.getBoundingClientRect()
|
|
||||||
|
|
||||||
let clickPosition = mouseX - left
|
|
||||||
|
|
||||||
let percentageClick = (clickPosition / sliderWidth).toFixed(2)
|
|
||||||
|
|
||||||
if (percentageClick >= 0 && percentageClick <= 1) {
|
|
||||||
let value = type === "hue" ? 360 * percentageClick : percentageClick
|
|
||||||
dispatch("change", { color: value, isDrag })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleLeftKey() {
|
|
||||||
let v = value - incrementFactor
|
|
||||||
if (isWithinLimit(v)) {
|
|
||||||
value = v
|
|
||||||
dispatch("change", { color: value })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleRightKey() {
|
|
||||||
let v = value + incrementFactor
|
|
||||||
if (isWithinLimit(v)) {
|
|
||||||
value = v
|
|
||||||
dispatch("change", { color: value })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragEnd() {
|
|
||||||
dispatch("dragend")
|
|
||||||
}
|
|
||||||
|
|
||||||
$: thumbPosition =
|
|
||||||
type === "hue" ? sliderWidth * (value / 360) : sliderWidth * value
|
|
||||||
|
|
||||||
$: style = `transform: translateX(${thumbPosition - 6}px);`
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
tabindex="0"
|
|
||||||
bind:this={slider}
|
|
||||||
use:keyevents={{ 37: handleLeftKey, 39: handleRightKey }}
|
|
||||||
bind:clientWidth={sliderWidth}
|
|
||||||
on:click={event => onSliderChange({ mouseX: event.clientX })}
|
|
||||||
class="color-format-slider"
|
|
||||||
class:hue={type === 'hue'}
|
|
||||||
class:alpha={type === 'alpha'}>
|
|
||||||
<div
|
|
||||||
use:drag
|
|
||||||
on:drag={e => onSliderChange(e.detail, true)}
|
|
||||||
on:dragend={handleDragEnd}
|
|
||||||
class="slider-thumb"
|
|
||||||
{style} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.color-format-slider {
|
|
||||||
position: relative;
|
|
||||||
align-self: center;
|
|
||||||
height: 8px;
|
|
||||||
width: 150px;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 10px 0px;
|
|
||||||
border: 1px solid #e8e8ef;
|
|
||||||
cursor: pointer;
|
|
||||||
outline-color: #003cb0;
|
|
||||||
outline-width: thin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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 #777676;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #ffffff;
|
|
||||||
cursor: grab;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-thumb:active {
|
|
||||||
cursor: grabbing;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,68 +0,0 @@
|
||||||
<script>
|
|
||||||
import { createEventDispatcher } from "svelte"
|
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
import CheckedBackground from "./CheckedBackground.svelte"
|
|
||||||
import { keyevents } from "../actions"
|
|
||||||
|
|
||||||
export let hovered = false
|
|
||||||
export let color = "#fff"
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="space">
|
|
||||||
<CheckedBackground borderRadius="6px">
|
|
||||||
<div
|
|
||||||
tabindex="0"
|
|
||||||
use:keyevents={{ Enter: () => dispatch('click') }}
|
|
||||||
in:fade
|
|
||||||
title={color}
|
|
||||||
class="swatch"
|
|
||||||
style={`background: ${color};`}
|
|
||||||
on:mouseover={() => (hovered = true)}
|
|
||||||
on:mouseleave={() => (hovered = false)}
|
|
||||||
on:click|self>
|
|
||||||
{#if hovered}
|
|
||||||
<span in:fade on:click={() => dispatch('removeswatch')} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</CheckedBackground>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.swatch {
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid #dedada;
|
|
||||||
height: 20px;
|
|
||||||
width: 20px;
|
|
||||||
outline-color: #003cb0;
|
|
||||||
outline-width: thin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.space {
|
|
||||||
padding: 3px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
cursor: pointer;
|
|
||||||
position: absolute;
|
|
||||||
top: -5px;
|
|
||||||
right: -4px;
|
|
||||||
background: #800000;
|
|
||||||
color: white;
|
|
||||||
font-size: 12px;
|
|
||||||
border-radius: 50%;
|
|
||||||
text-align: center;
|
|
||||||
width: 13px;
|
|
||||||
height: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span:after {
|
|
||||||
content: "\00d7";
|
|
||||||
position: relative;
|
|
||||||
left: 0;
|
|
||||||
bottom: 3px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,24 +0,0 @@
|
||||||
export function buildStyle(styles) {
|
|
||||||
let str = ""
|
|
||||||
for (let s in styles) {
|
|
||||||
if (styles[s]) {
|
|
||||||
let key = convertCamel(s)
|
|
||||||
str += `${key}: ${styles[s]}; `
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
export const convertCamel = str => {
|
|
||||||
return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const debounce = (fn, milliseconds, callImmediately) => {
|
|
||||||
const debouncedFn = () => {
|
|
||||||
setTimeout(fn, milliseconds)
|
|
||||||
}
|
|
||||||
if (callImmediately) {
|
|
||||||
debouncedFn()
|
|
||||||
}
|
|
||||||
return debouncedFn
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
import Colorpreview from "./components/Colorpreview.svelte"
|
|
||||||
export default Colorpreview
|
|
|
@ -1,281 +0,0 @@
|
||||||
export const isValidHex = str =>
|
|
||||||
/^#(?:[A-F0-9]{3}$|[A-F0-9]{4}$|[A-F0-9]{6}$|[A-F0-9]{8})$/gi.test(str)
|
|
||||||
|
|
||||||
const getHexaValues = hexString => {
|
|
||||||
if (hexString.length <= 5) {
|
|
||||||
let hexArr = hexString.match(/[A-F0-9]/gi)
|
|
||||||
let t = hexArr.map(c => (c += c))
|
|
||||||
return t
|
|
||||||
} else {
|
|
||||||
return hexString.match(/[A-F0-9]{2}/gi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isValidRgb = str => {
|
|
||||||
const hasValidStructure = /^(?:rgba\(|rgb\()(?:[0-9,\s]|\.(?=\d))*\)$/gi.test(
|
|
||||||
str
|
|
||||||
)
|
|
||||||
if (hasValidStructure) {
|
|
||||||
return testRgbaValues(str.toLowerCase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const findNonNumericChars = /[a-z()\s]/gi
|
|
||||||
|
|
||||||
export const getNumericValues = str =>
|
|
||||||
str
|
|
||||||
.replace(findNonNumericChars, "")
|
|
||||||
.split(",")
|
|
||||||
.map(v => (v !== "" ? v : undefined))
|
|
||||||
|
|
||||||
export const testRgbaValues = str => {
|
|
||||||
const rgba = getNumericValues(str)
|
|
||||||
const [r, g, b, a] = rgba
|
|
||||||
|
|
||||||
let isValidLengthRange =
|
|
||||||
(str.startsWith("rgb(") && rgba.length === 3) ||
|
|
||||||
(str.startsWith("rgba(") && rgba.length === 4)
|
|
||||||
let isValidColorRange = [r, g, b].every(v => v >= 0 && v <= 255)
|
|
||||||
let isValidAlphaRange = str.startsWith("rgba(")
|
|
||||||
? `${a}`.length <= 4 && a >= 0 && a <= 1
|
|
||||||
: true
|
|
||||||
|
|
||||||
return isValidLengthRange && isValidColorRange && isValidAlphaRange
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isValidHsl = str => {
|
|
||||||
const hasValidStructure = /^(?:hsl\(|hsla\()(?:[0-9,%\s]|\.(?=\d))*\)$/gi.test(
|
|
||||||
str
|
|
||||||
)
|
|
||||||
if (hasValidStructure) {
|
|
||||||
return testHslaValues(str.toLowerCase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const testHslaValues = str => {
|
|
||||||
const hsla = getNumericValues(str)
|
|
||||||
const [h, s, l, a] = hsla
|
|
||||||
const isUndefined = [h, s, l].some(v => v === undefined)
|
|
||||||
|
|
||||||
if (isUndefined) return false
|
|
||||||
|
|
||||||
let isValidLengthRange =
|
|
||||||
(str.startsWith("hsl(") && hsla.length === 3) ||
|
|
||||||
(str.startsWith("hsla(") && hsla.length === 4)
|
|
||||||
let isValidHue = h >= 0 && h <= 360
|
|
||||||
let isValidSatLum = [s, l].every(
|
|
||||||
v => v.endsWith("%") && parseInt(v) >= 0 && parseInt(v) <= 100
|
|
||||||
)
|
|
||||||
let isValidAlphaRange = str.startsWith("hsla(")
|
|
||||||
? `${a}`.length <= 4 && a >= 0 && a <= 1
|
|
||||||
: true
|
|
||||||
|
|
||||||
return isValidLengthRange && isValidHue && isValidSatLum && isValidAlphaRange
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getColorFormat = color => {
|
|
||||||
if (typeof color === "string") {
|
|
||||||
if (isValidHex(color)) {
|
|
||||||
return "hex"
|
|
||||||
} else if (isValidRgb(color)) {
|
|
||||||
return "rgb"
|
|
||||||
} else if (isValidHsl(color)) {
|
|
||||||
return "hsl"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const convertToHSVA = (value, format) => {
|
|
||||||
switch (format) {
|
|
||||||
case "hex":
|
|
||||||
return getAndConvertHexa(value)
|
|
||||||
case "rgb":
|
|
||||||
return getAndConvertRgba(value)
|
|
||||||
case "hsl":
|
|
||||||
return getAndConvertHsla(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const convertHsvaToFormat = (hsva, format) => {
|
|
||||||
switch (format) {
|
|
||||||
case "hex":
|
|
||||||
return hsvaToHexa(hsva, true)
|
|
||||||
case "rgb":
|
|
||||||
return hsvaToRgba(hsva, true)
|
|
||||||
case "hsl":
|
|
||||||
return hsvaToHsla(hsva)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getAndConvertHexa = color => {
|
|
||||||
let [rHex, gHex, bHex, aHex] = getHexaValues(color)
|
|
||||||
return hexaToHSVA([rHex, gHex, bHex], aHex)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getAndConvertRgba = color => {
|
|
||||||
let rgba = getNumericValues(color)
|
|
||||||
return rgbaToHSVA(rgba)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getAndConvertHsla = color => {
|
|
||||||
let hsla = getNumericValues(color)
|
|
||||||
return hslaToHSVA(hsla)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const hexaToHSVA = (hex, alpha = "FF") => {
|
|
||||||
const rgba = hex
|
|
||||||
.map(v => parseInt(v, 16))
|
|
||||||
.concat(Number((parseInt(alpha, 16) / 255).toFixed(2)))
|
|
||||||
return rgbaToHSVA(rgba)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const rgbaToHSVA = rgba => {
|
|
||||||
const [r, g, b, a = 1] = rgba
|
|
||||||
let hsv = _rgbToHSV([r, g, b])
|
|
||||||
return [...hsv, a].map(x => parseFloat(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const hslaToHSVA = ([h, s, l, a = 1]) => {
|
|
||||||
let sat = s.replace(/%/, "")
|
|
||||||
let lum = l.replace(/%/, "")
|
|
||||||
let hsv = _hslToHSV([h, sat, lum])
|
|
||||||
return [...hsv, a].map(x => parseFloat(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const hsvaToHexa = (hsva, asString = false) => {
|
|
||||||
const [r, g, b, a] = hsvaToRgba(hsva)
|
|
||||||
const padSingle = hex => (hex.length === 1 ? `0${hex}` : hex)
|
|
||||||
|
|
||||||
let hexa = [r, g, b].map(v => {
|
|
||||||
let hex = Math.round(v).toString(16)
|
|
||||||
return padSingle(hex)
|
|
||||||
})
|
|
||||||
|
|
||||||
let alpha = padSingle(Math.round(a * 255).toString(16))
|
|
||||||
hexa = [...hexa, alpha]
|
|
||||||
return asString ? `#${hexa.join("")}` : hexa
|
|
||||||
}
|
|
||||||
|
|
||||||
export const hsvaToRgba = ([h, s, v, a = 1], asString = false) => {
|
|
||||||
let rgb = _hsvToRgb([h, s, v]).map(x => Math.round(x))
|
|
||||||
let rgba = [...rgb, a < 1 ? _fixNum(a, 2) : a]
|
|
||||||
return asString ? `rgba(${rgba.join(",")})` : rgba
|
|
||||||
}
|
|
||||||
|
|
||||||
export const hsvaToHsla = ([h, s, v, a = 1]) => {
|
|
||||||
let [hue, sat, lum] = _hsvToHSL([h, s, v])
|
|
||||||
let hsla = [hue, sat + "%", lum + "%", a < 1 ? _fixNum(a, 2) : a]
|
|
||||||
return `hsla(${hsla.join(",")})`
|
|
||||||
}
|
|
||||||
|
|
||||||
export const _hslToHSV = hsl => {
|
|
||||||
const h = hsl[0]
|
|
||||||
let s = hsl[1] / 100
|
|
||||||
let l = hsl[2] / 100
|
|
||||||
let smin = s
|
|
||||||
const lmin = Math.max(l, 0.01)
|
|
||||||
|
|
||||||
l *= 2
|
|
||||||
s *= l <= 1 ? l : 2 - l
|
|
||||||
smin *= lmin <= 1 ? lmin : 2 - lmin
|
|
||||||
const v = (l + s) / 2
|
|
||||||
const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s)
|
|
||||||
|
|
||||||
return [h, sv * 100, v * 100]
|
|
||||||
}
|
|
||||||
|
|
||||||
//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 => Math.round(v))
|
|
||||||
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 [_fixNum(h, 0), _fixNum(sl * 100, 0), _fixNum(l * 100, 0)]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const _fixNum = (value, decimalPlaces) =>
|
|
||||||
Number(parseFloat(value).toFixed(decimalPlaces))
|
|
|
@ -1,106 +0,0 @@
|
||||||
import { getColorFormat, convertToHSVA, convertHsvaToFormat } from "./utils"
|
|
||||||
|
|
||||||
describe("convertToHSVA - convert to hsva from format", () => {
|
|
||||||
test("convert from hexa", () => {
|
|
||||||
expect(convertToHSVA("#f222d382", "hex")).toEqual([309, 86, 95, 0.51])
|
|
||||||
})
|
|
||||||
|
|
||||||
test("convert from hex", () => {
|
|
||||||
expect(convertToHSVA("#f222d3", "hex")).toEqual([309, 86, 95, 1])
|
|
||||||
})
|
|
||||||
|
|
||||||
test("convert from rgba", () => {
|
|
||||||
expect(convertToHSVA("rgba(242, 34, 211, 1)", "rgb")).toEqual([
|
|
||||||
309,
|
|
||||||
86,
|
|
||||||
95,
|
|
||||||
1,
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
test("convert from rgb", () => {
|
|
||||||
expect(convertToHSVA("rgb(150, 80, 255)", "rgb")).toEqual([264, 69, 100, 1])
|
|
||||||
})
|
|
||||||
|
|
||||||
test("convert from from hsl", () => {
|
|
||||||
expect(convertToHSVA("hsl(264, 100%, 65.7%)", "hsl")).toEqual([
|
|
||||||
264,
|
|
||||||
68.6,
|
|
||||||
100,
|
|
||||||
1,
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
test("convert from from hsla", () => {
|
|
||||||
expect(convertToHSVA("hsla(264, 100%, 65.7%, 0.51)", "hsl")).toEqual([
|
|
||||||
264,
|
|
||||||
68.6,
|
|
||||||
100,
|
|
||||||
0.51,
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("convertHsvaToFormat - convert from hsva to format", () => {
|
|
||||||
test("Convert to hexa", () => {
|
|
||||||
expect(convertHsvaToFormat([264, 68.63, 100, 0.5], "hex")).toBe("#9650ff80")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Convert to rgba", () => {
|
|
||||||
expect(convertHsvaToFormat([264, 68.63, 100, 0.75], "rgb")).toBe(
|
|
||||||
"rgba(150,80,255,0.75)"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Convert to hsla", () => {
|
|
||||||
expect(convertHsvaToFormat([264, 68.63, 100, 1], "hsl")).toBe(
|
|
||||||
"hsla(264,100%,66%,1)"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("Get Color Format", () => {
|
|
||||||
test("Testing valid hex string", () => {
|
|
||||||
expect(getColorFormat("#FFF")).toBe("hex")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Testing invalid hex string", () => {
|
|
||||||
expect(getColorFormat("#FFZ")).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Testing valid hex with alpha", () => {
|
|
||||||
expect(getColorFormat("#FF00BB80")).toBe("hex")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Test valid rgb value", () => {
|
|
||||||
expect(getColorFormat("RGB(255, 20, 50)")).toBe("rgb")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Testing invalid rgb value", () => {
|
|
||||||
expect(getColorFormat("rgb(255, 0)")).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Testing rgb value with alpha", () => {
|
|
||||||
expect(getColorFormat("rgba(255, 0, 50, 0.5)")).toBe("rgb")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Testing rgb value with incorrectly provided alpha", () => {
|
|
||||||
expect(getColorFormat("rgb(255, 0, 50, 0.5)")).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Testing invalid hsl value", () => {
|
|
||||||
expect(getColorFormat("hsla(255, 0)")).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Testing hsla value with alpha", () => {
|
|
||||||
expect(getColorFormat("hsla(150, 60%, 50%, 0.5)")).toBe("hsl")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Testing hsl value with incorrectly provided alpha", () => {
|
|
||||||
expect(getColorFormat("hsl(150, 0, 50, 0.5)")).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test("Testing out of bounds hsl", () => {
|
|
||||||
expect(getColorFormat("hsl(375, 0, 50)")).toBeUndefined()
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -2,7 +2,6 @@
|
||||||
import PropertyControl from "./PropertyControl.svelte"
|
import PropertyControl from "./PropertyControl.svelte"
|
||||||
import InputGroup from "../common/Inputs/InputGroup.svelte"
|
import InputGroup from "../common/Inputs/InputGroup.svelte"
|
||||||
import Input from "../common/Input.svelte"
|
import Input from "../common/Input.svelte"
|
||||||
import Colorpicker from "../common/Colorpicker.svelte"
|
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { excludeProps } from "./propertyCategories.js"
|
import { excludeProps } from "./propertyCategories.js"
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import IconButton from "../common/IconButton.svelte"
|
import IconButton from "../common/IconButton.svelte"
|
||||||
import Input from "../common/Input.svelte"
|
import Input from "../common/Input.svelte"
|
||||||
import Colorpicker from "../common/Colorpicker.svelte"
|
|
||||||
|
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let onChanged = () => {}
|
export let onChanged = () => {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Input from "../common/Input.svelte"
|
import Input from "../common/Input.svelte"
|
||||||
import OptionSelect from "./OptionSelect.svelte"
|
import OptionSelect from "./OptionSelect.svelte"
|
||||||
import FlatButtonGroup from "./FlatButtonGroup.svelte"
|
import FlatButtonGroup from "./FlatButtonGroup.svelte"
|
||||||
import Colorpicker from "./Colorpicker"
|
import Colorpicker from "@budibase/colorpicker"
|
||||||
/*
|
/*
|
||||||
TODO: Allow for default values for all properties
|
TODO: Allow for default values for all properties
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue