mirror of https://github.com/Budibase/budibase.git
18 changed files with 413 additions and 351 deletions
@ -1,151 +1,184 @@ |
|||||
<script> |
<script> |
||||
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" |
||||
|
|
||||
export let value = "#3ec1d3ff" |
export let value = "#3ec1d3ff" |
||||
export let swatches = [] |
export let swatches = [] |
||||
export let disableSwatches = false |
export let disableSwatches = false |
||||
export let open = false; |
export let open = false |
||||
export let width = "25px" |
export let width = "25px" |
||||
export let height = "25px" |
export let height = "25px" |
||||
|
|
||||
let format = "hexa"; |
let format = "hexa" |
||||
let dimensions = {top: 0, left: 0} |
let dimensions = { top: 0, left: 0 } |
||||
let colorPreview = null |
let colorPreview = null |
||||
|
|
||||
let previewHeight = null |
let previewHeight = null |
||||
let previewWidth = null |
let previewWidth = null |
||||
let pickerWidth = 0 |
let pickerWidth = 0 |
||||
let pickerHeight = 0 |
let pickerHeight = 0 |
||||
|
|
||||
let anchorEl = null |
let anchorEl = null |
||||
let parentNodes = []; |
let parentNodes = [] |
||||
let errorMsg = null |
let errorMsg = null |
||||
|
|
||||
$: previewStyle = buildStyle({width, height, background: value}) |
$: previewStyle = buildStyle({ width, height, background: value }) |
||||
$: errorPreviewStyle = buildStyle({width, height}) |
$: errorPreviewStyle = buildStyle({ width, height }) |
||||
$: pickerStyle = buildStyle({top: `${dimensions.top}px`, left: `${dimensions.left}px`}) |
$: pickerStyle = buildStyle({ |
||||
|
top: `${dimensions.top}px`, |
||||
const dispatch = createEventDispatcher() |
left: `${dimensions.left}px`, |
||||
|
}) |
||||
beforeUpdate(() => { |
|
||||
format = getColorFormat(value) |
const dispatch = createEventDispatcher() |
||||
if(!format) { |
|
||||
errorMsg = `Colorpicker - ${value} is an unknown color format. Please use a hex, rgb or hsl value` |
beforeUpdate(() => { |
||||
console.error(errorMsg) |
format = getColorFormat(value) |
||||
}else{ |
if (!format) { |
||||
errorMsg = null |
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) { |
|
||||
if(colorPreview) { |
|
||||
open = true; |
|
||||
} |
|
||||
} |
} |
||||
|
}) |
||||
$: if(open && colorPreview) { |
|
||||
const {top: spaceAbove, width, bottom, right, left: spaceLeft} = colorPreview.getBoundingClientRect() |
afterUpdate(() => { |
||||
const {innerHeight, innerWidth} = window |
if (colorPreview && colorPreview.offsetParent && !anchorEl) { |
||||
|
//Anchor relative to closest positioned ancestor element. If none, then anchor to body |
||||
const {offsetLeft, offsetTop} = colorPreview |
anchorEl = colorPreview.offsetParent |
||||
//get the scrollTop value for all scrollable parent elements |
let curEl = colorPreview |
||||
let scrollTop = parentNodes.reduce((scrollAcc, el) => scrollAcc += el.scrollTop, 0); |
let els = [] |
||||
|
//Travel up dom tree from preview element to find parent elements that scroll |
||||
const spaceBelow = (innerHeight - spaceAbove) - previewHeight |
while (!anchorEl.isSameNode(curEl)) { |
||||
const top = spaceAbove > spaceBelow ? (offsetTop - pickerHeight) - scrollTop : (offsetTop + previewHeight) - scrollTop |
curEl = curEl.parentNode |
||||
|
let elOverflow = window |
||||
//TOO: Testing and Scroll Awareness for x Scroll |
.getComputedStyle(curEl) |
||||
const spaceRight = (innerWidth - spaceLeft) + previewWidth |
.getPropertyValue("overflow") |
||||
const left = spaceRight > spaceLeft ? (offsetLeft + previewWidth) : offsetLeft - pickerWidth |
if (/scroll|auto/.test(elOverflow)) { |
||||
|
els.push(curEl) |
||||
dimensions = {top, left} |
} |
||||
|
} |
||||
|
parentNodes = els |
||||
} |
} |
||||
|
}) |
||||
|
|
||||
function onColorChange(color) { |
function openColorpicker(event) { |
||||
value = color.detail; |
if (colorPreview) { |
||||
dispatch("change", color.detail) |
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) { |
||||
|
value = color.detail |
||||
|
dispatch("change", color.detail) |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<div class="color-preview-container"> |
<div class="color-preview-container"> |
||||
{#if !errorMsg} |
{#if !errorMsg} |
||||
<CheckedBackground borderRadius="3px" backgroundSize="8px"> |
<CheckedBackground borderRadius="3px" backgroundSize="8px"> |
||||
<div bind:this={colorPreview} bind:clientHeight={previewHeight} bind:clientWidth={previewWidth} class="color-preview" style={previewStyle} on:click={openColorpicker} /> |
<div |
||||
</CheckedBackground> |
bind:this={colorPreview} |
||||
|
bind:clientHeight={previewHeight} |
||||
{#if open} |
bind:clientWidth={previewWidth} |
||||
<div transition:fade class="picker-container" style={pickerStyle}> |
class="color-preview" |
||||
<Colorpicker on:change={onColorChange} on:addswatch on:removeswatch bind:format bind:value bind:pickerHeight bind:pickerWidth {swatches} {disableSwatches} {open} /> |
style={previewStyle} |
||||
</div> |
on:click={openColorpicker} /> |
||||
<div on:click|self={() => open = false} class="overlay"></div> |
</CheckedBackground> |
||||
{/if} |
|
||||
{:else} |
{#if open} |
||||
<div class="color-preview preview-error" style={errorPreviewStyle}> |
<div transition:fade class="picker-container" style={pickerStyle}> |
||||
<span>×</span> |
<Colorpicker |
||||
</div> |
on:change={onColorChange} |
||||
|
on:addswatch |
||||
|
on:removeswatch |
||||
|
bind:format |
||||
|
bind:value |
||||
|
bind:pickerHeight |
||||
|
bind:pickerWidth |
||||
|
{swatches} |
||||
|
{disableSwatches} |
||||
|
{open} /> |
||||
|
</div> |
||||
|
<div on:click|self={() => (open = false)} class="overlay" /> |
||||
{/if} |
{/if} |
||||
|
{:else} |
||||
|
<div class="color-preview preview-error" style={errorPreviewStyle}> |
||||
|
<span>×</span> |
||||
|
</div> |
||||
|
{/if} |
||||
</div> |
</div> |
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
.color-preview-container{ |
.color-preview-container { |
||||
display: flex; |
display: flex; |
||||
flex-flow: row nowrap; |
flex-flow: row nowrap; |
||||
height: fit-content; |
height: fit-content; |
||||
} |
} |
||||
|
|
||||
.color-preview { |
.color-preview { |
||||
border-radius: 3px; |
border-radius: 3px; |
||||
border: 1px solid #dedada; |
border: 1px solid #dedada; |
||||
} |
} |
||||
|
|
||||
.preview-error { |
.preview-error { |
||||
background: #cccccc; |
background: #cccccc; |
||||
color: #808080; |
color: #808080; |
||||
text-align: center; |
text-align: center; |
||||
font-size: 18px; |
font-size: 18px; |
||||
cursor: not-allowed; |
cursor: not-allowed; |
||||
} |
} |
||||
|
|
||||
.picker-container { |
.picker-container { |
||||
position: absolute; |
position: absolute; |
||||
z-index: 3; |
z-index: 3; |
||||
width: fit-content; |
width: fit-content; |
||||
height: fit-content; |
height: fit-content; |
||||
} |
} |
||||
|
|
||||
.overlay{ |
.overlay { |
||||
position: fixed; |
position: fixed; |
||||
top: 0; |
top: 0; |
||||
bottom: 0; |
bottom: 0; |
||||
left: 0; |
left: 0; |
||||
right: 0; |
right: 0; |
||||
z-index: 2; |
z-index: 2; |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||
Loading…
Reference in new issue