Integrating Colorpicker with Builder

This commit is contained in:
Conor_Mack 2020-06-17 16:46:03 +01:00
parent ab15bd479c
commit ab5b7ccb39
9 changed files with 121 additions and 51 deletions

View File

@ -22,14 +22,11 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
onMount(() => { onMount(() => {
format = getColorFormat(value);
if (format) { if (format) {
convertAndSetHSVA() convertAndSetHSVA()
} }
}); });
const hsvaIsNull = () => [h, s, v, a].every(c => c === null);
function convertAndSetHSVA() { function convertAndSetHSVA() {
let hsva = convertToHSVA(value, format); let hsva = convertToHSVA(value, format);
setHSVA(hsva); setHSVA(hsva);
@ -53,17 +50,16 @@
function setHue(hue) { function setHue(hue) {
h = hue; h = hue;
value = convertHsvaToFormat([h, s, v, a], format); value = convertHsvaToFormat([h, s, v, a], format);
dispatch("change", value)
} }
function setAlpha(alpha) { function setAlpha(alpha) {
a = alpha === "1.00" ? "1" :alpha; a = alpha === "1.00" ? "1" :alpha;
value = convertHsvaToFormat([h, s, v, a], format); value = convertHsvaToFormat([h, s, v, a], format);
dispatch("change", value)
} }
function changeFormatAndConvert(f) { function changeFormatAndConvert(f) {
format = f; format = f;
console.log(f)
value = convertHsvaToFormat([h, s, v, a], format); value = convertHsvaToFormat([h, s, v, a], format);
} }
@ -73,14 +69,16 @@
format = f; format = f;
value = text value = text
convertAndSetHSVA() convertAndSetHSVA()
dispatch("change", value)
} }
} }
</script> </script>
<style> <style>
.colorpicker-container { .colorpicker-container {
display: flex; display: flex;
font-size: 12px; font-size: 11px;
font-weight: 400; font-weight: 400;
flex-direction: column; flex-direction: column;
height: 300px; height: 300px;
@ -104,7 +102,6 @@
grid-gap: 15px; grid-gap: 15px;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
/* padding: 8px; */
} }
.selected-color { .selected-color {

View File

@ -1,37 +1,84 @@
<script> <script>
import Colorpicker from "./Colorpicker.svelte" import Colorpicker from "./Colorpicker.svelte"
import {createEventDispatcher} 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"
export let value = "#3ec1d3ff" export let value = "#3ec1d3ff"
export let format = "hexa"; export let open = false;
export let width = "25px" export let width = "25px"
export let open = true; export let height = "25px"
export let fullwidth = false;
let format = "hexa";
let dimensions = {top: 0, left: 0}
let colorPreview = null let colorPreview = null
let positionSide = "top"
$: width = fullwidth ? "100%" : width let previewHeight = null
$: style = buildStyle({width, background: value}) let previewWidth = null
$: colorStyle = buildStyle({[positionSide]: `28px`}) let pickerWidth = 250
let pickerHeight = 300
let anchorEl = null
let parentNodes = [];
let errorMsg = null
$: previewStyle = buildStyle({width, height, background: value})
$: errorPreviewStyle = buildStyle({width, height})
$: pickerStyle = buildStyle({top: `${dimensions.top}px`, left: `${dimensions.left}px`})
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
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
}
})
afterUpdate(() => {
if(colorPreview && colorPreview.offsetParent && !anchorEl) {
//Anchor relative to closest positioned ancestor element. If none, then anchor to body
anchorEl = colorPreview.offsetParent
let curEl = colorPreview
let els = []
//Travel up dom tree from preview element to find parent elements that scroll
while(!anchorEl.isSameNode(curEl)) {
curEl = curEl.parentNode
let elOverflow = window.getComputedStyle(curEl).getPropertyValue("overflow")
if(/scroll|auto/.test(elOverflow)) {
els.push(curEl)
}
}
parentNodes = els
}
})
function openColorpicker(event) { function openColorpicker(event) {
if(colorPreview) { if(colorPreview) {
const {top: spaceAbove, width, bottom} = colorPreview.getBoundingClientRect() const {top: spaceAbove, width, bottom, right, left: spaceLeft} = colorPreview.getBoundingClientRect()
const spaceBelow = window.innerHeight - bottom; 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) + pickerWidth : offsetLeft - pickerWidth
dimensions = {top, left}
if (spaceAbove > spaceBelow) {
positionSide = "bottom"
} else {
positionSide = "top"
}
}
open = true; open = true;
} }
}
function onColorChange(color) { function onColorChange(color) {
value = color.detail; value = color.detail;
@ -40,31 +87,48 @@
</script> </script>
<div class="color-preview-container"> <div class="color-preview-container">
<div bind:this={colorPreview} class="color-preview" {style} on:click={openColorpicker}></div> {#if !errorMsg}
<div bind:this={colorPreview} bind:clientHeight={previewHeight} bind:clientWidth={previewWidth} class="color-preview" style={previewStyle} on:click={openColorpicker}></div>
{#if open} {#if open}
<div class="color-preview" style={colorStyle}> <div class="picker-container" bind:clientHeight={pickerHeight} bind:clientWidth={pickerWidth} style={pickerStyle}>
<Colorpicker on:change={onColorChange} {format} {value} /> <Colorpicker on:change={onColorChange} {format} {value} />
</div> </div>
<div on:click|self={() => open = false} class="overlay"></div>
{/if}
{:else}
<div class="color-preview preview-error" style={errorPreviewStyle}>
<span>&times;</span>
</div>
{/if} {/if}
</div> </div>
{#if open}
<div on:click|self={() => open = false} class="overlay"></div>
{/if}
<style> <style>
.color-preview-container{ .color-preview-container{
position: relative;
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
height: fit-content; height: fit-content;
} }
.color-preview { .color-preview {
position: absolute;
flex: 1; flex: 1;
height: 25px;
z-index: 2;
border-radius: 3px; border-radius: 3px;
border: 1px solid #dedada;
}
.preview-error {
background: #cccccc;
color: #808080;
text-align: center;
font-size: 18px;
cursor: not-allowed;
}
.picker-container {
position: absolute;
z-index: 3;
width: fit-content;
height: fit-content;
} }
.overlay{ .overlay{
@ -73,6 +137,7 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
z-index: 1; z-index: 2;
} }
</style> </style>

View File

@ -1,3 +1,2 @@
import Colorpreview from './Colorpreview.svelte'; import Colorpreview from "./Colorpreview.svelte"
export default Colorpreview
export default Colorpreview;

View File

@ -127,9 +127,11 @@ export const rgbaToHSVA = rgba => {
}; };
export const hslaToHSVA = ([h, s, l, a = 1]) => { export const hslaToHSVA = ([h, s, l, a = 1]) => {
let hsv = _hslToHSV([h, s, l]); let sat = s.replace(/%/, "")
return [...hsv, a]; let lum = l.replace(/%/, "")
}; let hsv = _hslToHSV([h, sat, lum])
return [...hsv, a]
}
export const hsvaToHexa = (hsva, asString = false) => { export const hsvaToHexa = (hsva, asString = false) => {
const [r, g, b, a] = hsvaToRgba(hsva); const [r, g, b, a] = hsvaToRgba(hsva);

View File

@ -102,8 +102,6 @@
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-x: hidden;
overflow-y: hidden;
padding: 20px; padding: 20px;
box-sizing: border-box; box-sizing: border-box;
} }
@ -121,6 +119,5 @@
margin-top: 20px; margin-top: 20px;
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0; min-height: 0;
overflow-y: auto;
} }
</style> </style>

View File

@ -31,6 +31,7 @@
<FlatButtonGroup value={selectedCategory} {buttonProps} {onChange} /> <FlatButtonGroup value={selectedCategory} {buttonProps} {onChange} />
</div> </div>
<div class="positioned-wrapper">
<div class="design-view-property-groups"> <div class="design-view-property-groups">
{#if propertyGroupNames.length > 0} {#if propertyGroupNames.length > 0}
{#each propertyGroupNames as groupName} {#each propertyGroupNames as groupName}
@ -49,6 +50,7 @@
{/if} {/if}
</div> </div>
</div> </div>
</div>
<style> <style>
.design-view-container { .design-view-container {
@ -62,10 +64,15 @@
flex: 0 0 50px; flex: 0 0 50px;
} }
.positioned-wrapper{
position: relative;
display: flex;
min-height: 0;
}
.design-view-property-groups { .design-view-property-groups {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden;
min-height: 0; min-height: 0;
} }

View File

@ -12,6 +12,8 @@
if (v.target) { if (v.target) {
let val = props.valueKey ? v.target[props.valueKey] : v.target.value let val = props.valueKey ? v.target[props.valueKey] : v.target.value
onChange(key, val) onChange(key, val)
}else if(v.detail) {
onChange(key, v.detail)
} else { } else {
onChange(key, v) onChange(key, v)
} }

View File

@ -2,7 +2,7 @@ import Input from "../common/Input.svelte"
import OptionSelect from "./OptionSelect.svelte" import OptionSelect from "./OptionSelect.svelte"
import InputGroup from "../common/Inputs/InputGroup.svelte" import InputGroup from "../common/Inputs/InputGroup.svelte"
import FlatButtonGroup from "./FlatButtonGroup.svelte" import FlatButtonGroup from "./FlatButtonGroup.svelte"
// import Colorpicker from "../common/Colorpicker.svelte" import Colorpicker from "./Colorpicker"
/* /*
TODO: Allow for default values for all properties TODO: Allow for default values for all properties
*/ */
@ -256,8 +256,8 @@ export const typography = [
{ {
label: "Color", label: "Color",
key: "color", key: "color",
control: Input, control: Colorpicker,
placeholder: "hex", defaultValue: "#000",
}, },
{ {
label: "align", label: "align",
@ -305,7 +305,8 @@ export const background = [
{ {
label: "Color", label: "Color",
key: "background", key: "background",
control: Input, control: Colorpicker,
defaultValue: "#000",
}, },
{ {
label: "Image", label: "Image",
@ -347,7 +348,8 @@ export const border = [
{ {
label: "Color", label: "Color",
key: "border-color", key: "border-color",
control: Input, control: Colorpicker,
defaultValue: "#000",
}, },
{ {
label: "Style", label: "Style",

View File

@ -103,7 +103,6 @@
background-color: var(--white); background-color: var(--white);
min-height: 0px; min-height: 0px;
height: calc(100vh - 69px); height: calc(100vh - 69px);
overflow-y: hidden;
} }
.nav-group-header > div:nth-child(1) { .nav-group-header > div:nth-child(1) {