Update all design panel settings and styles to use new spectrum components

This commit is contained in:
Andrew Kingston 2021-04-19 14:04:07 +01:00
parent eeac45d3e7
commit 90bcd87d24
15 changed files with 143 additions and 384 deletions

View File

@ -43,7 +43,8 @@
use:clickOutside={hide}
style={menuStyle}
on:keydown={handleEscape}
class="spectrum-Popover is-open" role="presentation">
class="spectrum-Popover is-open"
role="presentation">
<slot />
</div>
</Portal>

View File

@ -3,6 +3,7 @@
import "@spectrum-css/popover/dist/index-vars.css"
import "@spectrum-css/menu/dist/index-vars.css"
import { fly } from "svelte/transition"
import { createEventDispatcher } from "svelte"
export let id = null
export let disabled = false
@ -16,6 +17,16 @@
export let getOptionLabel = option => option
export let getOptionValue = option => option
export let open = false
export let readonly = false
const dispatch = createEventDispatcher()
const onClick = () => {
dispatch("click")
if (readonly) {
return
}
open = true
}
</script>
<button
@ -25,7 +36,7 @@
class:is-invalid={!!error}
class:is-open={open}
aria-haspopup="listbox"
on:click={() => (open = true)}>
on:click={onClick}>
<span class="spectrum-Picker-label" class:is-placeholder={isPlaceholder}>
{fieldText}
</span>

View File

@ -10,6 +10,7 @@
export let options = []
export let getOptionLabel = option => option
export let getOptionValue = option => option
export let readonly = false
const dispatch = createEventDispatcher()
let open = false
@ -38,10 +39,12 @@
</script>
<Picker
on:click
bind:open
{id}
{error}
{disabled}
{readonly}
{fieldText}
{options}
{getOptionLabel}

View File

@ -8,11 +8,15 @@
export let disabled = false
export let error = null
export let id = null
export let readonly = false
const dispatch = createEventDispatcher()
let focus = false
const updateValue = value => {
if (readonly) {
return
}
if (type === "number") {
const float = parseFloat(value)
value = isNaN(float) ? null : float
@ -20,12 +24,25 @@
dispatch("change", value)
}
const onFocus = () => {
if (readonly) {
return
}
focus = true
}
const onBlur = event => {
if (readonly) {
return
}
focus = false
updateValue(event.target.value)
}
const updateValueOnEnter = event => {
if (readonly) {
return
}
if (event.key === "Enter") {
updateValue(event.target.value)
}
@ -46,13 +63,15 @@
</svg>
{/if}
<input
on:click
on:keyup={updateValueOnEnter}
{disabled}
{readonly}
{id}
value={value || ''}
placeholder={placeholder || ''}
on:blur={onBlur}
on:focus={() => (focus = true)}
on:focus={onFocus}
{type}
class="spectrum-Textfield-input" />
</div>

View File

@ -9,6 +9,7 @@
export let placeholder = null
export let type = "text"
export let disabled = false
export let readonly = false
export let error = null
const dispatch = createEventDispatcher()
@ -22,8 +23,10 @@
<TextField
{error}
{disabled}
{readonly}
{value}
{placeholder}
{type}
on:change={onChange} />
on:change={onChange}
on:click />
</Field>

View File

@ -3,9 +3,10 @@
import Multiselect from "./Core/Multiselect.svelte"
import Field from "./Field.svelte"
export let value = null
export let label = []
export let value = []
export let label = null
export let disabled = false
export let readonly = false
export let labelPosition = "above"
export let error = null
export let placeholder = null
@ -29,5 +30,6 @@
{placeholder}
{getOptionLabel}
{getOptionValue}
on:change={onChange} />
on:change={onChange}
on:click />
</Field>

View File

@ -6,6 +6,7 @@
export let value = null
export let label = undefined
export let disabled = false
export let readonly = false
export let labelPosition = "above"
export let error = null
export let placeholder = "Choose an option"
@ -30,10 +31,12 @@
<Select
{error}
{disabled}
{readonly}
{value}
{options}
{placeholder}
{getOptionLabel}
{getOptionValue}
on:change={onChange} />
on:change={onChange}
on:click />
</Field>

View File

@ -4,6 +4,8 @@
Button,
Icon,
DropdownMenu,
Divider,
Select,
Spacer,
Heading,
Drawer,
@ -27,6 +29,7 @@
export let otherSources
export let showAllQueries
$: text = value?.label ?? "Choose an option"
$: tables = $tablesStore.list.map(m => ({
label: m.name,
tableId: m._id,
@ -92,14 +95,12 @@
}
</script>
<div class="container">
<div
class="dropdownbutton"
bind:this={anchorRight}
on:click={dropdownRight.show}>
<span>{value?.label ?? 'Choose option'}</span>
<Icon name="arrowdown" />
</div>
<div class="container" bind:this={anchorRight}>
<Select
readonly
value={text}
options={[text]}
on:click={dropdownRight.show} />
{#if value?.type === 'query'}
<i class="ri-settings-5-line" on:click={drawer.show} />
<Drawer title={'Query Parameters'} bind:this={drawer}>
@ -148,7 +149,7 @@
</li>
{/each}
</ul>
<hr />
<Divider s />
<div class="title">
<Heading xs h3>Views</Heading>
</div>
@ -161,9 +162,9 @@
</li>
{/each}
</ul>
<hr />
<Divider s />
<div class="title">
<Heading extraSmall>Relationships</Heading>
<Heading xs h3>Relationships</Heading>
</div>
<ul>
{#each links as link}
@ -174,10 +175,9 @@
</li>
{/each}
</ul>
<hr />
<Divider s />
<div class="title">
<Heading extraSmall>Queries</Heading>
<Heading xs h3>Queries</Heading>
</div>
<ul>
{#each queries as query}
@ -190,7 +190,7 @@
</ul>
{#if otherSources?.length}
<hr />
<Divider s />
<div class="title">
<Heading extraSmall>Other</Heading>
</div>
@ -214,45 +214,16 @@
justify-content: flex-start;
align-items: center;
}
.dropdownbutton {
background-color: var(--grey-2);
border: var(--border-transparent);
padding: var(--spacing-s) var(--spacing-m);
border-radius: var(--border-radius-m);
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
overflow: hidden;
.container :global(:first-child) {
flex: 1 1 auto;
}
.dropdownbutton:hover {
cursor: pointer;
background-color: var(--grey-3);
}
.dropdownbutton span {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
flex: 1 1 auto;
text-align: left;
font-size: var(--font-size-xs);
}
.dropdownbutton :global(svg) {
margin: -4px 0;
}
.dropdown {
padding: var(--spacing-m) 0;
z-index: 99999999;
}
.title {
padding: 0 var(--spacing-m) var(--spacing-xs) var(--spacing-m);
}
hr {
margin: var(--spacing-m) 0 var(--spacing-xl) 0;
padding: 0 var(--spacing-m) var(--spacing-s) var(--spacing-m);
}
ul {

View File

@ -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} />

View File

@ -5,9 +5,9 @@
export let value
</script>
<Select bind:value extraThin secondary on:change>
<option value="">Choose an option</option>
{#each $store.layouts as layout}
<option value={layout._id}>{layout.name}</option>
{/each}
</Select>
<Select
bind:value
on:change
options={$store.layouts}
getOptionLabel={layout => layout.name}
getOptionValue={layout => layout._id} />

View File

@ -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} />

View File

@ -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>

View File

@ -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>

View File

@ -5,9 +5,9 @@
export let value
</script>
<Select bind:value extraThin secondary on:change>
<option value="">Choose an option</option>
{#each $roles as role}
<option value={role._id}>{role.name}</option>
{/each}
</Select>
<Select
bind:value
on:change
options={$roles}
getOptionLabel={role => role.name}
getOptionValue={role => role._id} />

View File

@ -46,7 +46,7 @@
"settings": [
{
"type": "dataProvider",
"label": "Data",
"label": "Provider",
"key": "dataProvider"
},
{
@ -529,7 +529,7 @@
},
{
"type": "dataProvider",
"label": "Data",
"label": "Provider",
"key": "dataProvider"
},
{
@ -629,7 +629,7 @@
},
{
"type": "dataProvider",
"label": "Data",
"label": "Provider",
"key": "dataProvider"
},
{
@ -730,7 +730,7 @@
},
{
"type": "dataProvider",
"label": "Data",
"label": "Provider",
"key": "dataProvider"
},
{
@ -843,7 +843,7 @@
},
{
"type": "dataProvider",
"label": "Data",
"label": "Provider",
"key": "dataProvider"
},
{
@ -920,7 +920,7 @@
},
{
"type": "dataProvider",
"label": "Data",
"label": "Provider",
"key": "dataProvider"
},
{
@ -997,7 +997,7 @@
},
{
"type": "dataProvider",
"label": "Data",
"label": "Provider",
"key": "dataProvider"
},
{
@ -1482,7 +1482,7 @@
"settings": [
{
"type": "dataProvider",
"label": "Data Provider",
"label": "Provider",
"key": "dataProvider"
},
{