mirror of https://github.com/Budibase/budibase.git
15 changed files with 143 additions and 384 deletions
@ -1,25 +1,41 @@ |
|||
<script> |
|||
import OptionSelect from "./OptionSelect.svelte" |
|||
import MultiOptionSelect from "./MultiOptionSelect.svelte" |
|||
import { Select } from "@budibase/bbui" |
|||
import { |
|||
getDatasourceForProvider, |
|||
getSchemaForDatasource, |
|||
} from "builderStore/dataBinding" |
|||
import { currentAsset } from "builderStore" |
|||
import { createEventDispatcher } from "svelte" |
|||
|
|||
export let componentInstance = {} |
|||
export let value = "" |
|||
export let onChange = () => {} |
|||
export let multiselect = false |
|||
export let placeholder |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) |
|||
$: schema = getSchemaForDatasource(datasource).schema |
|||
$: options = Object.keys(schema || {}) |
|||
$: boundValue = getValidValue(value, options) |
|||
|
|||
const getValidValue = (value, options) => { |
|||
// Reset value if there aren't any options |
|||
if (!Array.isArray(options)) { |
|||
return null |
|||
} |
|||
|
|||
// Reset value if the value isn't found in the options |
|||
if (options.indexOf(value) === -1) { |
|||
return null |
|||
} |
|||
|
|||
return value |
|||
} |
|||
|
|||
const onChange = value => { |
|||
boundValue = getValidValue(value.detail, options) |
|||
dispatch("change", boundValue) |
|||
} |
|||
</script> |
|||
|
|||
{#if multiselect} |
|||
<MultiOptionSelect {value} {onChange} {options} {placeholder} /> |
|||
{:else} |
|||
<OptionSelect {value} {onChange} {options} {placeholder} /> |
|||
{/if} |
|||
<Select {placeholder} value={boundValue} on:change={onChange} {options} /> |
|||
|
|||
@ -1,5 +1,35 @@ |
|||
<script> |
|||
import FieldSelect from "./FieldSelect.svelte" |
|||
import { Multiselect } from "@budibase/bbui" |
|||
import { |
|||
getDatasourceForProvider, |
|||
getSchemaForDatasource, |
|||
} from "builderStore/dataBinding" |
|||
import { currentAsset } from "builderStore" |
|||
import { createEventDispatcher } from "svelte" |
|||
|
|||
export let componentInstance = {} |
|||
export let value = "" |
|||
export let multiselect = false |
|||
export let placeholder |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) |
|||
$: schema = getSchemaForDatasource(datasource).schema |
|||
$: options = Object.keys(schema || {}) |
|||
$: boundValue = getValidOptions(value, options) |
|||
|
|||
const getValidOptions = (selectedOptions, allOptions) => { |
|||
// Fix the hardcoded default string value |
|||
if (!Array.isArray(selectedOptions)) { |
|||
selectedOptions = [] |
|||
} |
|||
return selectedOptions.filter(val => allOptions.indexOf(val) !== -1) |
|||
} |
|||
|
|||
const setValue = value => { |
|||
boundValue = getValidOptions(value.detail, options) |
|||
dispatch("change", boundValue) |
|||
} |
|||
</script> |
|||
|
|||
<FieldSelect {...$$props} multiselect /> |
|||
<Multiselect {placeholder} value={boundValue} on:change={setValue} {options} /> |
|||
|
|||
@ -1,43 +0,0 @@ |
|||
<script> |
|||
import { Multiselect } from "@budibase/bbui" |
|||
|
|||
export let options = [] |
|||
export let value = [] |
|||
export let onChange = () => {} |
|||
export let placeholder |
|||
|
|||
let boundValue = getValidOptions(value, options) |
|||
|
|||
function getValidOptions(selectedOptions, allOptions) { |
|||
// Fix the hardcoded default string value |
|||
if (!Array.isArray(selectedOptions)) { |
|||
selectedOptions = [] |
|||
} |
|||
return selectedOptions.filter(val => allOptions.indexOf(val) !== -1) |
|||
} |
|||
|
|||
function setValue(value) { |
|||
boundValue = getValidOptions(value.detail, options) |
|||
onChange(boundValue) |
|||
} |
|||
</script> |
|||
|
|||
<div> |
|||
<Multiselect |
|||
align="right" |
|||
extraThin |
|||
secondary |
|||
{placeholder} |
|||
value={boundValue} |
|||
on:change={setValue}> |
|||
{#each options as option} |
|||
<option value={option}>{option}</option> |
|||
{/each} |
|||
</Multiselect> |
|||
</div> |
|||
|
|||
<style> |
|||
div { |
|||
flex: 1 1 auto; |
|||
} |
|||
</style> |
|||
@ -1,257 +0,0 @@ |
|||
<script> |
|||
import { onMount } from "svelte" |
|||
import Portal from "svelte-portal" |
|||
import { buildStyle } from "../../../../helpers.js" |
|||
|
|||
export let options = [] |
|||
export let value = "" |
|||
export let styleBindingProperty |
|||
export let onChange = () => {} |
|||
export let placeholder |
|||
|
|||
let open = null |
|||
let rotate = "" |
|||
let select |
|||
let selectMenu |
|||
let icon |
|||
let width = 0 |
|||
|
|||
let selectAnchor = null |
|||
let dimensions = { top: 0, bottom: 0, left: 0 } |
|||
|
|||
let positionSide = "top" |
|||
let maxHeight = 0 |
|||
let scrollTop = 0 |
|||
let containerEl = null |
|||
|
|||
const handleStyleBind = value => |
|||
!!styleBindingProperty ? { style: `${styleBindingProperty}: ${value}` } : {} |
|||
|
|||
onMount(() => { |
|||
if (select) { |
|||
select.addEventListener("keydown", handleEnter) |
|||
} |
|||
|
|||
return () => { |
|||
select.removeEventListener("keydown", handleEnter) |
|||
} |
|||
}) |
|||
|
|||
function handleEscape(e) { |
|||
if (open && e.key === "Escape") { |
|||
toggleSelect(false) |
|||
} |
|||
} |
|||
|
|||
function getDimensions() { |
|||
const { |
|||
bottom, |
|||
top: spaceAbove, |
|||
left, |
|||
} = selectAnchor.getBoundingClientRect() |
|||
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) |
|||
} |
|||
} |
|||
|
|||
function toggleSelect(isOpen) { |
|||
getDimensions() |
|||
if (isOpen) { |
|||
icon.style.transform = "rotate(180deg)" |
|||
} else { |
|||
icon.style.transform = "rotate(0deg)" |
|||
} |
|||
open = isOpen |
|||
} |
|||
|
|||
function handleClick(val) { |
|||
value = val |
|||
onChange(value) |
|||
toggleSelect(false) |
|||
} |
|||
|
|||
$: menuStyle = buildStyle({ |
|||
"max-height": `${maxHeight.toFixed(0)}px`, |
|||
"transform-origin": `center ${positionSide}`, |
|||
[positionSide]: `${dimensions[positionSide]}px`, |
|||
left: `${dimensions.left.toFixed(0)}px`, |
|||
width: `${width}px`, |
|||
}) |
|||
|
|||
$: isOptionsObject = options.every(o => typeof o === "object") |
|||
|
|||
$: selectedOption = isOptionsObject |
|||
? options.find(o => o.value === value || (o.value === "" && value == null)) |
|||
: {} |
|||
|
|||
$: if (open && selectMenu) { |
|||
selectMenu.focus() |
|||
} |
|||
|
|||
$: displayLabel = |
|||
selectedOption && selectedOption.label |
|||
? selectedOption.label |
|||
: value || placeholder || "Choose option" |
|||
</script> |
|||
|
|||
<div |
|||
bind:clientWidth={width} |
|||
tabindex="0" |
|||
bind:this={select} |
|||
class="bb-select-container" |
|||
on:click={() => toggleSelect(!open)}> |
|||
<div bind:this={selectAnchor} title={value} class="bb-select-anchor selected"> |
|||
<span>{displayLabel}</span> |
|||
<i bind:this={icon} class="ri-arrow-down-s-fill" /> |
|||
</div> |
|||
{#if open} |
|||
<Portal> |
|||
<div |
|||
tabindex="0" |
|||
class:open |
|||
bind:this={selectMenu} |
|||
style={menuStyle} |
|||
on:keydown={handleEscape} |
|||
class="bb-select-menu"> |
|||
<ul> |
|||
<li |
|||
on:click|self={() => handleClick(null)} |
|||
class:selected={value == null || value === ''}> |
|||
Choose option |
|||
</li> |
|||
{#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> |
|||
<div on:click|self={() => toggleSelect(false)} class="overlay" /> |
|||
</Portal> |
|||
{/if} |
|||
</div> |
|||
|
|||
<style> |
|||
.overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
bottom: 0; |
|||
right: 0; |
|||
left: 0; |
|||
z-index: 1; |
|||
} |
|||
|
|||
.bb-select-container { |
|||
outline: none; |
|||
cursor: pointer; |
|||
overflow: hidden; |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
.bb-select-anchor { |
|||
cursor: pointer; |
|||
display: flex; |
|||
padding: var(--spacing-s) var(--spacing-m); |
|||
background-color: var(--grey-2); |
|||
border-radius: var(--border-radius-m); |
|||
align-items: center; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.bb-select-anchor > span { |
|||
color: var(--ink); |
|||
font-weight: 400; |
|||
overflow-x: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
font-size: var(--font-size-xs); |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
.bb-select-anchor > i { |
|||
transition: transform 0.13s ease; |
|||
transform-origin: center; |
|||
width: 20px; |
|||
height: 20px; |
|||
text-align: center; |
|||
} |
|||
|
|||
.selected { |
|||
color: var(--ink); |
|||
font-weight: 400; |
|||
} |
|||
|
|||
.bb-select-menu { |
|||
position: absolute; |
|||
display: flex; |
|||
outline: none; |
|||
box-sizing: border-box; |
|||
flex-direction: column; |
|||
opacity: 0; |
|||
z-index: 2; |
|||
color: var(--ink); |
|||
font-weight: 400; |
|||
height: fit-content !important; |
|||
border-bottom-left-radius: 2px; |
|||
border-bottom-right-radius: 2px; |
|||
background-color: var(--grey-2); |
|||
transform: scale(0); |
|||
transition: opacity 0.13s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1); |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.open { |
|||
transform: scale(1); |
|||
opacity: 1; |
|||
} |
|||
|
|||
ul { |
|||
list-style-type: none; |
|||
margin: 0; |
|||
padding: 5px 0px; |
|||
} |
|||
|
|||
li { |
|||
height: auto; |
|||
padding: 5px 0px; |
|||
cursor: pointer; |
|||
padding-left: 10px; |
|||
font-size: var(--font-size-xs); |
|||
} |
|||
|
|||
li:hover { |
|||
background-color: var(--grey-3); |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue