|
|
|
@ -1,5 +1,6 @@ |
|
|
|
<script> |
|
|
|
import { onMount, beforeUpdate } from "svelte" |
|
|
|
import { onMount, beforeUpdate, afterUpdate } from "svelte" |
|
|
|
import Portal from "svelte-portal" |
|
|
|
import { buildStyle } from "../../helpers.js" |
|
|
|
export let options = [] |
|
|
|
export let value = "" |
|
|
|
@ -12,12 +13,13 @@ |
|
|
|
let selectMenu |
|
|
|
let icon |
|
|
|
|
|
|
|
let selectYPosition = null |
|
|
|
let availableSpace = 0 |
|
|
|
let selectAnchor = null; |
|
|
|
let dimensions = {top: 0, bottom: 0, left: 0} |
|
|
|
|
|
|
|
let positionSide = "top" |
|
|
|
let maxHeight = null |
|
|
|
let menuHeight |
|
|
|
let maxHeight = 0 |
|
|
|
let scrollTop = 0; |
|
|
|
let containerEl = null; |
|
|
|
|
|
|
|
const handleStyleBind = value => |
|
|
|
!!styleBindingProperty ? { style: `${styleBindingProperty}: ${value}` } : {} |
|
|
|
@ -26,22 +28,37 @@ |
|
|
|
if (select) { |
|
|
|
select.addEventListener("keydown", addSelectKeyEvents) |
|
|
|
} |
|
|
|
|
|
|
|
containerEl = document.querySelector(".design-view-property-groups") |
|
|
|
|
|
|
|
return () => { |
|
|
|
select.removeEventListener("keydown", addSelectKeyEvents) |
|
|
|
containerEl.removeEventListener("scroll", disableScroll) |
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
function checkPosition() { |
|
|
|
const { bottom, top: spaceAbove } = select.getBoundingClientRect() |
|
|
|
function disableScroll(e) { |
|
|
|
containerEl.scrollTop = scrollTop |
|
|
|
} |
|
|
|
|
|
|
|
function getDimensions() { |
|
|
|
const { bottom, top: spaceAbove, left } = selectAnchor.getBoundingClientRect() |
|
|
|
const spaceBelow = window.innerHeight - bottom |
|
|
|
|
|
|
|
let y; |
|
|
|
|
|
|
|
if (spaceAbove > spaceBelow) { |
|
|
|
positionSide = "bottom" |
|
|
|
maxHeight = `${spaceAbove.toFixed(0) - 20}px` |
|
|
|
maxHeight = spaceAbove - 20 |
|
|
|
y = (window.innerHeight - spaceAbove) |
|
|
|
} else { |
|
|
|
positionSide = "top" |
|
|
|
maxHeight = `${spaceBelow.toFixed(0) - 20}px` |
|
|
|
y = bottom |
|
|
|
maxHeight = spaceBelow - 20 |
|
|
|
} |
|
|
|
|
|
|
|
dimensions = {[positionSide]: y, left} |
|
|
|
} |
|
|
|
|
|
|
|
function addSelectKeyEvents(e) { |
|
|
|
@ -57,24 +74,30 @@ |
|
|
|
} |
|
|
|
|
|
|
|
function toggleSelect(isOpen) { |
|
|
|
checkPosition() |
|
|
|
getDimensions() |
|
|
|
if (isOpen) { |
|
|
|
icon.style.transform = "rotate(180deg)" |
|
|
|
let containerWidth = containerEl.offsetWidth |
|
|
|
scrollTop = containerEl.scrollTop |
|
|
|
containerEl.addEventListener("scroll", disableScroll) |
|
|
|
} else { |
|
|
|
icon.style.transform = "rotate(0deg)" |
|
|
|
containerEl.removeEventListener("scroll", disableScroll) |
|
|
|
} |
|
|
|
open = isOpen |
|
|
|
open = isOpen |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function handleClick(val) { |
|
|
|
value = val |
|
|
|
onChange(value) |
|
|
|
} |
|
|
|
|
|
|
|
$: menuStyle = buildStyle({ |
|
|
|
"max-height": maxHeight, |
|
|
|
"max-height": `${maxHeight.toFixed(0)}px`, |
|
|
|
"transform-origin": `center ${positionSide}`, |
|
|
|
[positionSide]: "32px", |
|
|
|
[positionSide]: `${dimensions[positionSide]}px`, |
|
|
|
"left": `${dimensions.left.toFixed(0)}px`, |
|
|
|
}) |
|
|
|
|
|
|
|
$: isOptionsObject = options.every(o => typeof o === "object") |
|
|
|
@ -92,37 +115,39 @@ |
|
|
|
bind:this={select} |
|
|
|
class="bb-select-container" |
|
|
|
on:click={() => toggleSelect(!open)}> |
|
|
|
<div class="bb-select-anchor selected"> |
|
|
|
<div bind:this={selectAnchor} class="bb-select-anchor selected"> |
|
|
|
<span>{displayLabel}</span> |
|
|
|
<i bind:this={icon} class="ri-arrow-down-s-fill" /> |
|
|
|
</div> |
|
|
|
<div |
|
|
|
bind:this={selectMenu} |
|
|
|
style={menuStyle} |
|
|
|
class="bb-select-menu" |
|
|
|
class:open> |
|
|
|
<ul> |
|
|
|
{#if isOptionsObject} |
|
|
|
{#each options as { value: v, label }} |
|
|
|
<li |
|
|
|
{...handleStyleBind(v)} |
|
|
|
on:click|self={handleClick(v)} |
|
|
|
class:selected={value === v}> |
|
|
|
{label} |
|
|
|
</li> |
|
|
|
{/each} |
|
|
|
{:else} |
|
|
|
{#each options as v} |
|
|
|
<li |
|
|
|
{...handleStyleBind(v)} |
|
|
|
on:click|self={handleClick(v)} |
|
|
|
class:selected={value === v}> |
|
|
|
{v} |
|
|
|
</li> |
|
|
|
{/each} |
|
|
|
{/if} |
|
|
|
</ul> |
|
|
|
</div> |
|
|
|
<Portal> |
|
|
|
<div |
|
|
|
bind:this={selectMenu} |
|
|
|
style={menuStyle} |
|
|
|
class="bb-select-menu" |
|
|
|
class:open> |
|
|
|
<ul> |
|
|
|
{#if isOptionsObject} |
|
|
|
{#each options as { value: v, label }} |
|
|
|
<li |
|
|
|
{...handleStyleBind(v)} |
|
|
|
on:click|self={handleClick(v)} |
|
|
|
class:selected={value === v}> |
|
|
|
{label} |
|
|
|
</li> |
|
|
|
{/each} |
|
|
|
{:else} |
|
|
|
{#each options as v} |
|
|
|
<li |
|
|
|
{...handleStyleBind(v)} |
|
|
|
on:click|self={handleClick(v)} |
|
|
|
class:selected={value === v}> |
|
|
|
{v} |
|
|
|
</li> |
|
|
|
{/each} |
|
|
|
{/if} |
|
|
|
</ul> |
|
|
|
</div> |
|
|
|
</Portal> |
|
|
|
</div> |
|
|
|
{#if open} |
|
|
|
<div on:click|self={() => toggleSelect(false)} class="overlay" /> |
|
|
|
@ -130,7 +155,7 @@ |
|
|
|
|
|
|
|
<style> |
|
|
|
.overlay { |
|
|
|
position: absolute; |
|
|
|
position: fixed; |
|
|
|
top: 0; |
|
|
|
bottom: 0; |
|
|
|
right: 0; |
|
|
|
@ -139,7 +164,6 @@ |
|
|
|
} |
|
|
|
|
|
|
|
.bb-select-container { |
|
|
|
position: relative; |
|
|
|
outline: none; |
|
|
|
width: 160px; |
|
|
|
height: 36px; |
|
|
|
@ -190,9 +214,6 @@ |
|
|
|
height: fit-content !important; |
|
|
|
border-bottom-left-radius: 2px; |
|
|
|
border-bottom-right-radius: 2px; |
|
|
|
border-right: 1px solid var(--grey-4); |
|
|
|
border-left: 1px solid var(--grey-4); |
|
|
|
border-bottom: 1px solid var(--grey-4); |
|
|
|
background-color: var(--grey-2); |
|
|
|
transform: scale(0); |
|
|
|
transition: opacity 0.13s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1); |
|
|
|
|