|
|
@ -1,36 +1,83 @@ |
|
|
<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 |
|
|
|
|
|
|
|
|
if (spaceAbove > spaceBelow) { |
|
|
const {offsetLeft, offsetTop} = colorPreview |
|
|
positionSide = "bottom" |
|
|
//get the scrollTop value for all scrollable parent elements |
|
|
} else { |
|
|
let scrollTop = parentNodes.reduce((scrollAcc, el) => scrollAcc += el.scrollTop, 0); |
|
|
positionSide = "top" |
|
|
|
|
|
} |
|
|
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} |
|
|
|
|
|
|
|
|
|
|
|
open = true; |
|
|
} |
|
|
} |
|
|
open = true; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function onColorChange(color) { |
|
|
function onColorChange(color) { |
|
|
@ -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} |
|
|
{#if open} |
|
|
<div bind:this={colorPreview} bind:clientHeight={previewHeight} bind:clientWidth={previewWidth} class="color-preview" style={previewStyle} on:click={openColorpicker}></div> |
|
|
<div class="color-preview" style={colorStyle}> |
|
|
{#if open} |
|
|
|
|
|
<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>×</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> |
|
|
|
|
|
|
|
|
|