Merge pull request #397 from Budibase/bugfix/select-overflow-clipping
OptionSelect - Fix for Overflow Clipping
This commit is contained in:
commit
b565c940f2
|
@ -70,6 +70,7 @@
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"shortid": "^2.2.8",
|
"shortid": "^2.2.8",
|
||||||
"string_decoder": "^1.2.0",
|
"string_decoder": "^1.2.0",
|
||||||
|
"svelte-portal": "^0.1.0",
|
||||||
"svelte-simple-modal": "^0.4.2",
|
"svelte-simple-modal": "^0.4.2",
|
||||||
"uikit": "^3.1.7"
|
"uikit": "^3.1.7"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,16 +1,27 @@
|
||||||
<script>
|
<script>
|
||||||
|
import {onMount} from "svelte"
|
||||||
import PropertyGroup from "./PropertyGroup.svelte"
|
import PropertyGroup from "./PropertyGroup.svelte"
|
||||||
import FlatButtonGroup from "./FlatButtonGroup.svelte"
|
import FlatButtonGroup from "./FlatButtonGroup.svelte"
|
||||||
|
|
||||||
|
|
||||||
export let panelDefinition = {}
|
export let panelDefinition = {}
|
||||||
export let componentInstance = {}
|
export let componentInstance = {}
|
||||||
export let componentDefinition = {}
|
export let componentDefinition = {}
|
||||||
export let onStyleChanged = () => {}
|
export let onStyleChanged = () => {}
|
||||||
|
|
||||||
let selectedCategory = "normal"
|
let selectedCategory = "normal"
|
||||||
|
let propGroup = null
|
||||||
|
|
||||||
const getProperties = name => panelDefinition[name]
|
const getProperties = name => panelDefinition[name]
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// if(propGroup) {
|
||||||
|
// propGroup.addEventListener("scroll", function(e){
|
||||||
|
// console.log("I SCROLLED", e.target.scrollTop)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
|
||||||
function onChange(category) {
|
function onChange(category) {
|
||||||
selectedCategory = category
|
selectedCategory = category
|
||||||
}
|
}
|
||||||
|
@ -31,7 +42,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="positioned-wrapper">
|
<div class="positioned-wrapper">
|
||||||
<div class="design-view-property-groups">
|
<div bind:this={propGroup} class="design-view-property-groups">
|
||||||
{#if propertyGroupNames.length > 0}
|
{#if propertyGroupNames.length > 0}
|
||||||
{#each propertyGroupNames as groupName}
|
{#each propertyGroupNames as groupName}
|
||||||
<PropertyGroup
|
<PropertyGroup
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount, beforeUpdate } from "svelte"
|
import { onMount, beforeUpdate, afterUpdate } from "svelte"
|
||||||
|
import Portal from "svelte-portal"
|
||||||
import { buildStyle } from "../../helpers.js"
|
import { buildStyle } from "../../helpers.js"
|
||||||
export let options = []
|
export let options = []
|
||||||
export let value = ""
|
export let value = ""
|
||||||
|
@ -12,52 +13,61 @@
|
||||||
let selectMenu
|
let selectMenu
|
||||||
let icon
|
let icon
|
||||||
|
|
||||||
let selectYPosition = null
|
let selectAnchor = null;
|
||||||
let availableSpace = 0
|
let dimensions = {top: 0, bottom: 0, left: 0}
|
||||||
|
|
||||||
let positionSide = "top"
|
let positionSide = "top"
|
||||||
let maxHeight = null
|
let maxHeight = 0
|
||||||
let menuHeight
|
let scrollTop = 0;
|
||||||
|
let containerEl = null;
|
||||||
|
|
||||||
const handleStyleBind = value =>
|
const handleStyleBind = value =>
|
||||||
!!styleBindingProperty ? { style: `${styleBindingProperty}: ${value}` } : {}
|
!!styleBindingProperty ? { style: `${styleBindingProperty}: ${value}` } : {}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (select) {
|
if (select) {
|
||||||
select.addEventListener("keydown", addSelectKeyEvents)
|
select.addEventListener("keydown", handleEnter)
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
select.removeEventListener("keydown", addSelectKeyEvents)
|
select.removeEventListener("keydown", handleEnter)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function checkPosition() {
|
function handleEscape(e) {
|
||||||
const { bottom, top: spaceAbove } = select.getBoundingClientRect()
|
if(open && e.key === "Escape") {
|
||||||
const spaceBelow = window.innerHeight - bottom
|
toggleSelect(false)
|
||||||
|
|
||||||
if (spaceAbove > spaceBelow) {
|
|
||||||
positionSide = "bottom"
|
|
||||||
maxHeight = `${spaceAbove.toFixed(0) - 20}px`
|
|
||||||
} else {
|
|
||||||
positionSide = "top"
|
|
||||||
maxHeight = `${spaceBelow.toFixed(0) - 20}px`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addSelectKeyEvents(e) {
|
function getDimensions() {
|
||||||
if (e.key === "Enter") {
|
const { bottom, top: spaceAbove, left } = selectAnchor.getBoundingClientRect()
|
||||||
if (!open) {
|
const spaceBelow = window.innerHeight - bottom
|
||||||
|
|
||||||
|
let y;
|
||||||
|
|
||||||
|
if (spaceAbove > spaceBelow) {
|
||||||
|
positionSide = "bottom"
|
||||||
|
maxHeight = spaceAbove - 20
|
||||||
|
y = (window.innerHeight - spaceAbove)
|
||||||
|
} else {
|
||||||
|
positionSide = "top"
|
||||||
|
y = bottom
|
||||||
|
maxHeight = spaceBelow - 20
|
||||||
|
}
|
||||||
|
|
||||||
|
dimensions = {[positionSide]: y, left}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEnter(e) {
|
||||||
|
if (!open && e.key === "Enter") {
|
||||||
toggleSelect(true)
|
toggleSelect(true)
|
||||||
}
|
|
||||||
} else if (e.key === "Escape") {
|
|
||||||
if (open) {
|
|
||||||
toggleSelect(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSelect(isOpen) {
|
function toggleSelect(isOpen) {
|
||||||
checkPosition()
|
getDimensions()
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
icon.style.transform = "rotate(180deg)"
|
icon.style.transform = "rotate(180deg)"
|
||||||
} else {
|
} else {
|
||||||
|
@ -66,15 +76,17 @@
|
||||||
open = isOpen
|
open = isOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function handleClick(val) {
|
function handleClick(val) {
|
||||||
value = val
|
value = val
|
||||||
onChange(value)
|
onChange(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
$: menuStyle = buildStyle({
|
$: menuStyle = buildStyle({
|
||||||
"max-height": maxHeight,
|
"max-height": `${maxHeight.toFixed(0)}px`,
|
||||||
"transform-origin": `center ${positionSide}`,
|
"transform-origin": `center ${positionSide}`,
|
||||||
[positionSide]: "32px",
|
[positionSide]: `${dimensions[positionSide]}px`,
|
||||||
|
"left": `${dimensions.left.toFixed(0)}px`,
|
||||||
})
|
})
|
||||||
|
|
||||||
$: isOptionsObject = options.every(o => typeof o === "object")
|
$: isOptionsObject = options.every(o => typeof o === "object")
|
||||||
|
@ -83,6 +95,10 @@
|
||||||
? options.find(o => o.value === value)
|
? options.find(o => o.value === value)
|
||||||
: {}
|
: {}
|
||||||
|
|
||||||
|
$: if(open && selectMenu) {
|
||||||
|
selectMenu.focus()
|
||||||
|
}
|
||||||
|
|
||||||
$: displayLabel =
|
$: displayLabel =
|
||||||
selectedOption && selectedOption.label ? selectedOption.label : value || ""
|
selectedOption && selectedOption.label ? selectedOption.label : value || ""
|
||||||
</script>
|
</script>
|
||||||
|
@ -92,45 +108,49 @@
|
||||||
bind:this={select}
|
bind:this={select}
|
||||||
class="bb-select-container"
|
class="bb-select-container"
|
||||||
on:click={() => toggleSelect(!open)}>
|
on:click={() => toggleSelect(!open)}>
|
||||||
<div class="bb-select-anchor selected">
|
<div bind:this={selectAnchor} class="bb-select-anchor selected">
|
||||||
<span>{displayLabel}</span>
|
<span>{displayLabel}</span>
|
||||||
<i bind:this={icon} class="ri-arrow-down-s-fill" />
|
<i bind:this={icon} class="ri-arrow-down-s-fill" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
{#if open}
|
||||||
bind:this={selectMenu}
|
<Portal>
|
||||||
style={menuStyle}
|
<div
|
||||||
class="bb-select-menu"
|
tabindex="0"
|
||||||
class:open>
|
class:open
|
||||||
<ul>
|
bind:this={selectMenu}
|
||||||
{#if isOptionsObject}
|
style={menuStyle}
|
||||||
{#each options as { value: v, label }}
|
on:keydown={handleEscape}
|
||||||
<li
|
class="bb-select-menu">
|
||||||
{...handleStyleBind(v)}
|
<ul>
|
||||||
on:click|self={handleClick(v)}
|
{#if isOptionsObject}
|
||||||
class:selected={value === v}>
|
{#each options as { value: v, label }}
|
||||||
{label}
|
<li
|
||||||
</li>
|
{...handleStyleBind(v)}
|
||||||
{/each}
|
on:click|self={handleClick(v)}
|
||||||
{:else}
|
class:selected={value === v}>
|
||||||
{#each options as v}
|
{label}
|
||||||
<li
|
</li>
|
||||||
{...handleStyleBind(v)}
|
{/each}
|
||||||
on:click|self={handleClick(v)}
|
{:else}
|
||||||
class:selected={value === v}>
|
{#each options as v}
|
||||||
{v}
|
<li
|
||||||
</li>
|
{...handleStyleBind(v)}
|
||||||
{/each}
|
on:click|self={handleClick(v)}
|
||||||
{/if}
|
class:selected={value === v}>
|
||||||
</ul>
|
{v}
|
||||||
</div>
|
</li>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div on:click|self={() => toggleSelect(false)} class="overlay" />
|
||||||
|
</Portal>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if open}
|
|
||||||
<div on:click|self={() => toggleSelect(false)} class="overlay" />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.overlay {
|
.overlay {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -139,7 +159,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.bb-select-container {
|
.bb-select-container {
|
||||||
position: relative;
|
|
||||||
outline: none;
|
outline: none;
|
||||||
width: 160px;
|
width: 160px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
|
@ -180,6 +199,7 @@
|
||||||
.bb-select-menu {
|
.bb-select-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
outline: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@ -190,9 +210,6 @@
|
||||||
height: fit-content !important;
|
height: fit-content !important;
|
||||||
border-bottom-left-radius: 2px;
|
border-bottom-left-radius: 2px;
|
||||||
border-bottom-right-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);
|
background-color: var(--grey-2);
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
transition: opacity 0.13s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1);
|
transition: opacity 0.13s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1);
|
||||||
|
|
Loading…
Reference in New Issue