Recent Colors and UI Updates

This commit is contained in:
Conor_Mack 2020-06-19 17:07:11 +01:00
parent d8ac7296c3
commit c80f9f8819
6 changed files with 139 additions and 46 deletions

View File

@ -1,5 +1,6 @@
<script> <script>
import {buildStyle} from "./helpers.js" import {buildStyle} from "./helpers.js"
import {fade} from "svelte/transition"
export let backgroundSize = "10px" export let backgroundSize = "10px"
export let borderRadius = "" export let borderRadius = ""
@ -18,6 +19,6 @@
} }
</style> </style>
<div {style}> <div transition:fade {style}>
<slot /> <slot />
</div> </div>

View File

@ -1,5 +1,6 @@
<script> <script>
import { onMount, createEventDispatcher } from "svelte"; import { onMount, createEventDispatcher } from "svelte";
import { fade } from 'svelte/transition';
import CheckedBackground from "./CheckedBackground.svelte" import CheckedBackground from "./CheckedBackground.svelte"
import {buildStyle} from "./helpers.js" import {buildStyle} from "./helpers.js"
import { import {
@ -14,6 +15,11 @@
export let value = "#3ec1d3ff"; export let value = "#3ec1d3ff";
export let format = "hexa"; export let format = "hexa";
let recentColors = []
export let pickerHeight = 0;
export let pickerWidth = 0;
let h = null; let h = null;
let s = null; let s = null;
@ -23,11 +29,30 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
onMount(() => { onMount(() => {
getRecentColors()
if (format) { if (format) {
convertAndSetHSVA() convertAndSetHSVA()
} }
}); });
function getRecentColors() {
let colorStore = localStorage.getItem("cp:recent-colors")
if (colorStore) {
recentColors = JSON.parse(colorStore)
}
}
function setRecentColor(color) {
if (recentColors.length === 6) {
recentColors.splice(0, 1)
}
if (!recentColors.includes(color)) {
recentColors = [...recentColors, color]
localStorage.setItem("cp:recent-colors", JSON.stringify(recentColors))
}
}
function convertAndSetHSVA() { function convertAndSetHSVA() {
let hsva = convertToHSVA(value, format); let hsva = convertToHSVA(value, format);
setHSVA(hsva); setHSVA(hsva);
@ -45,35 +70,64 @@
s = detail.s; s = detail.s;
v = detail.v; v = detail.v;
value = convertHsvaToFormat([h, s, v, a], format); value = convertHsvaToFormat([h, s, v, a], format);
setRecentColor(value)
dispatchValue()
}
function setHue({color, isDrag}) {
h = color;
value = convertHsvaToFormat([h, s, v, a], format);
if(!isDrag) {
setRecentColor(value)
dispatchValue()
}
}
function setAlpha({color, isDrag}) {
a = color === "1.00" ? "1" : color;
value = convertHsvaToFormat([h, s, v, a], format);
if(!isDrag) {
setRecentColor(value)
dispatchValue()
}
}
function dispatchValue() {
dispatch("change", value) dispatch("change", value)
} }
function setHue(hue) {
h = hue;
value = convertHsvaToFormat([h, s, v, a], format);
}
function setAlpha(alpha) {
a = alpha === "1.00" ? "1" :alpha;
value = convertHsvaToFormat([h, s, v, a], format);
}
function changeFormatAndConvert(f) { function changeFormatAndConvert(f) {
format = f; format = f;
value = convertHsvaToFormat([h, s, v, a], format); value = convertHsvaToFormat([h, s, v, a], format);
} }
function handleColorInput(text) { function handleColorInput(text) {
let f = getColorFormat(text) let format = getColorFormat(text)
if(f) { if(format) {
format = f;
value = text value = text
convertAndSetHSVA() convertAndSetHSVA()
dispatch("change", value)
} }
} }
$: border = s < 10 ? "1px dashed #dedada" : "" function dispatchInputChange() {
if(format) {
setRecentColor(value)
dispatchValue()
}
}
function applyRecentColor(color) {
if(value !== color) {
format = getColorFormat(color)
if(format) {
value = color
convertAndSetHSVA()
dispatchValue()
}
}
}
$: border = (v > 90 && s < 5) ? "1px dashed #dedada" : ""
$: style = buildStyle({background: value, border}) $: style = buildStyle({background: value, border})
</script> </script>
@ -83,7 +137,8 @@
font-size: 11px; font-size: 11px;
font-weight: 400; font-weight: 400;
flex-direction: column; flex-direction: column;
height: 265px; /* height: 265px; */
height: auto;
width: 220px; width: 220px;
background: #ffffff; background: #ffffff;
border-radius: 2px; border-radius: 2px;
@ -118,6 +173,24 @@
border-radius: 50%; border-radius: 50%;
} }
.recent-color-panel {
flex: 0 0 15px;
display: grid;
grid-gap: 10px;
justify-content: flex-start;
grid-auto-flow: column;
padding: 5px;
margin-left: 6px;
}
.recent-color {
cursor: pointer;
border-radius: 6px;
border: 1px solid #dedada;
height: 20px;
width: 20px;
}
.format-input-panel { .format-input-panel {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -125,7 +198,7 @@
} }
</style> </style>
<div class="colorpicker-container"> <div class="colorpicker-container" bind:clientHeight={pickerHeight} bind:clientWidth={pickerWidth}>
<div class="palette-panel"> <div class="palette-panel">
<Palette on:change={setSaturationAndValue} {h} {s} {v} {a} /> <Palette on:change={setSaturationAndValue} {h} {s} {v} {a} />
@ -139,21 +212,33 @@
</CheckedBackground> </CheckedBackground>
</div> </div>
<div> <div>
<Slider type="hue" value={h} on:change={hue => setHue(hue.detail)} /> <Slider type="hue" value={h} on:change={(hue) => setHue(hue.detail)} on:dragend={dispatchValue} />
<CheckedBackground borderRadius="10px" backgroundSize="7px"> <CheckedBackground borderRadius="10px" backgroundSize="7px">
<Slider <Slider
type="alpha" type="alpha"
value={a} value={a}
on:change={alpha => setAlpha(alpha.detail)} /> on:change={(alpha, isDrag) => setAlpha(alpha.detail, isDrag)}
on:dragend={dispatchValue}
/>
</CheckedBackground> </CheckedBackground>
</div> </div>
</div> </div>
{#if recentColors.length > 0}
<div transition:fade class="recent-color-panel">
{#each recentColors as color}
<CheckedBackground borderRadius="6px">
<div transition:fade class="recent-color" style={`background: ${color};`} on:click={() => applyRecentColor(color)} />
</CheckedBackground>
{/each}
</div>
{/if}
<div class="format-input-panel"> <div class="format-input-panel">
<ButtonGroup {format} onclick={changeFormatAndConvert} /> <ButtonGroup {format} onclick={changeFormatAndConvert} />
<Input {value} on:input={event => handleColorInput(event.target.value)} /> <Input {value} on:input={event => handleColorInput(event.target.value)} on:change={dispatchInputChange} />
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@
import Colorpicker from "./Colorpicker.svelte" import Colorpicker from "./Colorpicker.svelte"
import CheckedBackground from "./CheckedBackground.svelte" import CheckedBackground from "./CheckedBackground.svelte"
import {createEventDispatcher, afterUpdate, beforeUpdate} from "svelte" import {createEventDispatcher, afterUpdate, beforeUpdate} from "svelte"
import {buildStyle} from "./helpers.js" import {buildStyle} from "./helpers.js"
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import {getColorFormat} from "./utils.js" import {getColorFormat} from "./utils.js"
@ -17,8 +18,8 @@
let previewHeight = null let previewHeight = null
let previewWidth = null let previewWidth = null
let pickerWidth = 250 let pickerWidth = 0
let pickerHeight = 300 let pickerHeight = 0
let anchorEl = null let anchorEl = null
let parentNodes = []; let parentNodes = [];
@ -61,30 +62,33 @@
function openColorpicker(event) { function openColorpicker(event) {
if(colorPreview) { if(colorPreview) {
const {top: spaceAbove, width, bottom, right, left: spaceLeft} = colorPreview.getBoundingClientRect()
const {innerHeight, innerWidth} = window
const {offsetLeft, offsetTop} = colorPreview
//get the scrollTop value for all scrollable parent elements
let scrollTop = parentNodes.reduce((scrollAcc, el) => scrollAcc += el.scrollTop, 0);
const spaceBelow = (innerHeight - spaceAbove) - previewHeight
const top = spaceAbove > spaceBelow ? (offsetTop - pickerHeight) - scrollTop : (offsetTop + previewHeight) - scrollTop
//TOO: Testing and Scroll Awareness for x Scroll
const spaceRight = (innerWidth - spaceLeft) + previewWidth
const left = spaceRight > spaceLeft ? (offsetLeft + previewWidth) : offsetLeft - pickerWidth
dimensions = {top, left}
open = true; open = true;
} }
} }
$: if(open && colorPreview) {
const {top: spaceAbove, width, bottom, right, left: spaceLeft} = colorPreview.getBoundingClientRect()
const {innerHeight, innerWidth} = window
const {offsetLeft, offsetTop} = colorPreview
//get the scrollTop value for all scrollable parent elements
let scrollTop = parentNodes.reduce((scrollAcc, el) => scrollAcc += el.scrollTop, 0);
const spaceBelow = (innerHeight - spaceAbove) - previewHeight
const top = spaceAbove > spaceBelow ? (offsetTop - pickerHeight) - scrollTop : (offsetTop + previewHeight) - scrollTop
//TOO: Testing and Scroll Awareness for x Scroll
const spaceRight = (innerWidth - spaceLeft) + previewWidth
const left = spaceRight > spaceLeft ? (offsetLeft + previewWidth) : offsetLeft - pickerWidth
dimensions = {top, left}
}
function onColorChange(color) { function onColorChange(color) {
value = color.detail; value = color.detail;
dispatch("change", color.detail) dispatch("change", color.detail)
} }
</script> </script>
<div class="color-preview-container"> <div class="color-preview-container">
@ -94,8 +98,8 @@
</CheckedBackground> </CheckedBackground>
{#if open} {#if open}
<div class="picker-container" bind:clientHeight={pickerHeight} bind:clientWidth={pickerWidth} style={pickerStyle}> <div transition:fade class="picker-container" style={pickerStyle}>
<Colorpicker on:change={onColorChange} {format} {value} /> <Colorpicker on:change={onColorChange} bind:format bind:value bind:pickerHeight bind:pickerWidth />
</div> </div>
<div on:click|self={() => open = false} class="overlay"></div> <div on:click|self={() => open = false} class="overlay"></div>
{/if} {/if}

View File

@ -24,5 +24,5 @@
</style> </style>
<div> <div>
<input on:input type="text" {value} maxlength="25" /> <input on:input on:change type="text" {value} maxlength="25" />
</div> </div>

View File

@ -10,7 +10,7 @@
let slider; let slider;
let sliderWidth = 0; let sliderWidth = 0;
function handleClick(mouseX) { function onSliderChange(mouseX, isDrag = false) {
const { left, width } = slider.getBoundingClientRect(); const { left, width } = slider.getBoundingClientRect();
let clickPosition = mouseX - left; let clickPosition = mouseX - left;
@ -21,7 +21,8 @@
type === "hue" type === "hue"
? 360 * percentageClick ? 360 * percentageClick
: percentageClick; : percentageClick;
dispatch("change", value);
dispatch("change", {color: value, isDrag});
} }
} }
@ -75,13 +76,14 @@
<div <div
bind:this={slider} bind:this={slider}
bind:clientWidth={sliderWidth} bind:clientWidth={sliderWidth}
on:click={event => handleClick(event.clientX)} on:click={event => onSliderChange(event.clientX)}
class="color-format-slider" class="color-format-slider"
class:hue={type === 'hue'} class:hue={type === 'hue'}
class:alpha={type === 'alpha'}> class:alpha={type === 'alpha'}>
<div <div
use:dragable use:dragable
on:drag={e => handleClick(e.detail)} on:drag={e => onSliderChange(e.detail, true)}
on:dragend
class="slider-thumb" class="slider-thumb"
{style} /> {style} />
</div> </div>

View File

@ -16,6 +16,7 @@ export default function(node) {
function handleMouseUp() { function handleMouseUp() {
window.removeEventListener("mousedown", handleMouseDown) window.removeEventListener("mousedown", handleMouseDown)
window.removeEventListener("mousemove", handleMouseMove) window.removeEventListener("mousemove", handleMouseMove)
node.dispatchEvent(new CustomEvent("dragend"))
} }
node.addEventListener("mousedown", handleMouseDown) node.addEventListener("mousedown", handleMouseDown)