mirror of https://github.com/Budibase/budibase.git
21 changed files with 193 additions and 253 deletions
@ -0,0 +1,39 @@ |
|||||
|
<script> |
||||
|
import Field from "./Field.svelte" |
||||
|
import Combobox from "./Core/Combobox.svelte" |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
|
||||
|
export let value = null |
||||
|
export let label = undefined |
||||
|
export let disabled = false |
||||
|
export let labelPosition = "above" |
||||
|
export let error = null |
||||
|
export let placeholder = "Choose an option" |
||||
|
export let options = [] |
||||
|
export let getOptionLabel = option => extractProperty(option, "label") |
||||
|
export let getOptionValue = option => extractProperty(option, "value") |
||||
|
|
||||
|
const dispatch = createEventDispatcher() |
||||
|
const onChange = e => { |
||||
|
dispatch("change", e.detail) |
||||
|
value = e.detail |
||||
|
} |
||||
|
const extractProperty = (value, property) => { |
||||
|
if (value && typeof value === "object") { |
||||
|
return value[property] |
||||
|
} |
||||
|
return value |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<Field {label} {labelPosition} {disabled} {error}> |
||||
|
<Combobox |
||||
|
{error} |
||||
|
{disabled} |
||||
|
{value} |
||||
|
{options} |
||||
|
{placeholder} |
||||
|
{getOptionLabel} |
||||
|
{getOptionValue} |
||||
|
on:change={onChange} /> |
||||
|
</Field> |
||||
@ -0,0 +1,128 @@ |
|||||
|
<script> |
||||
|
import "@spectrum-css/inputgroup/dist/index-vars.css" |
||||
|
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 value = null |
||||
|
export let id = null |
||||
|
export let placeholder = "Choose an option" |
||||
|
export let disabled = false |
||||
|
export let error = null |
||||
|
export let options = [] |
||||
|
export let getOptionLabel = option => option |
||||
|
export let getOptionValue = option => option |
||||
|
|
||||
|
const dispatch = createEventDispatcher() |
||||
|
let open = false |
||||
|
let focus = false |
||||
|
$: fieldText = getFieldText(value, options, placeholder) |
||||
|
|
||||
|
const getFieldText = (value, options, placeholder) => { |
||||
|
// Always use placeholder if no value |
||||
|
if (value == null || value === "") { |
||||
|
return placeholder || "Choose an option" |
||||
|
} |
||||
|
|
||||
|
// Wait for options to load if there is a value but no options |
||||
|
if (!options?.length) { |
||||
|
return "" |
||||
|
} |
||||
|
|
||||
|
// Render the label if the selected option is found, otherwise raw value |
||||
|
const selected = options.find(option => getOptionValue(option) === value) |
||||
|
return selected ? getOptionLabel(selected) : value |
||||
|
} |
||||
|
|
||||
|
const selectOption = value => { |
||||
|
dispatch("change", value) |
||||
|
open = false |
||||
|
} |
||||
|
|
||||
|
const onChange = e => { |
||||
|
selectOption(e.target.value) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="spectrum-InputGroup" class:is-focused={open || focus}> |
||||
|
<div |
||||
|
class="spectrum-Textfield spectrum-InputGroup-textfield" |
||||
|
class:is-disabled={!!error} |
||||
|
class:is-focused={open || focus}> |
||||
|
<input |
||||
|
type="text" |
||||
|
on:focus={() => (focus = true)} |
||||
|
on:blur={() => (focus = false)} |
||||
|
on:change={onChange} |
||||
|
{value} |
||||
|
{placeholder} |
||||
|
class="spectrum-Textfield-input spectrum-InputGroup-input" /> |
||||
|
</div> |
||||
|
<button |
||||
|
class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button" |
||||
|
tabindex="-1" |
||||
|
aria-haspopup="true" |
||||
|
disabled={!!error} |
||||
|
on:click={() => (open = true)}> |
||||
|
<svg |
||||
|
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon spectrum-InputGroup-icon" |
||||
|
focusable="false" |
||||
|
aria-hidden="true"> |
||||
|
<use xlink:href="#spectrum-css-icon-Chevron100" /> |
||||
|
</svg> |
||||
|
</button> |
||||
|
{#if open} |
||||
|
<div class="overlay" on:mousedown|self={() => (open = false)} /> |
||||
|
<div |
||||
|
transition:fly={{ y: -20, duration: 200 }} |
||||
|
class="spectrum-Popover spectrum-Popover--bottom is-open"> |
||||
|
<ul class="spectrum-Menu" role="listbox"> |
||||
|
{#if options && Array.isArray(options)} |
||||
|
{#each options as option} |
||||
|
<li |
||||
|
class="spectrum-Menu-item" |
||||
|
class:is-selected={getOptionValue(option) === value} |
||||
|
role="option" |
||||
|
aria-selected="true" |
||||
|
tabindex="0" |
||||
|
on:click={() => selectOption(getOptionValue(option))}> |
||||
|
<span |
||||
|
class="spectrum-Menu-itemLabel">{getOptionLabel(option)}</span> |
||||
|
<svg |
||||
|
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon" |
||||
|
focusable="false" |
||||
|
aria-hidden="true"> |
||||
|
<use xlink:href="#spectrum-css-icon-Checkmark100" /> |
||||
|
</svg> |
||||
|
</li> |
||||
|
{/each} |
||||
|
{/if} |
||||
|
</ul> |
||||
|
</div> |
||||
|
{/if} |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.spectrum-InputGroup { |
||||
|
min-width: 0; |
||||
|
width: 100%; |
||||
|
} |
||||
|
.spectrum-Textfield-input { |
||||
|
width: 0; |
||||
|
} |
||||
|
.overlay { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
z-index: 999; |
||||
|
} |
||||
|
.spectrum-Popover { |
||||
|
max-height: 240px; |
||||
|
width: 100%; |
||||
|
z-index: 999; |
||||
|
top: 100%; |
||||
|
} |
||||
|
</style> |
||||
@ -1,158 +0,0 @@ |
|||||
<script> |
|
||||
import Icon from "../Icons/Icon.svelte" |
|
||||
import Label from "../Styleguide/Label.svelte" |
|
||||
import { createEventDispatcher } from "svelte" |
|
||||
|
|
||||
export let label = undefined |
|
||||
export let value = "" |
|
||||
export let name = undefined |
|
||||
export let thin = false |
|
||||
export let extraThin = false |
|
||||
export let secondary = false |
|
||||
export let outline = false |
|
||||
export let disabled = false |
|
||||
|
|
||||
const dispatch = createEventDispatcher() |
|
||||
let focus = false |
|
||||
|
|
||||
const updateValue = e => { |
|
||||
value = e.target.value |
|
||||
} |
|
||||
|
|
||||
function handleFocus(e) { |
|
||||
focus = true |
|
||||
dispatch("focus", e) |
|
||||
} |
|
||||
|
|
||||
function handleBlur(e) { |
|
||||
focus = false |
|
||||
dispatch("blur", e) |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
{#if label} |
|
||||
<Label extraSmall grey forAttr={name}>{label}</Label> |
|
||||
{/if} |
|
||||
<div class="container" class:disabled class:secondary class:outline class:focus> |
|
||||
<select |
|
||||
{name} |
|
||||
class:thin |
|
||||
class:extraThin |
|
||||
class:secondary |
|
||||
{disabled} |
|
||||
on:change |
|
||||
on:focus={handleFocus} |
|
||||
on:blur={handleBlur} |
|
||||
bind:value> |
|
||||
<slot /> |
|
||||
</select> |
|
||||
<slot name="custom-input" /> |
|
||||
<input |
|
||||
class:thin |
|
||||
class:extraThin |
|
||||
class:secondary |
|
||||
class:disabled |
|
||||
{disabled} |
|
||||
on:change={updateValue} |
|
||||
on:input={updateValue} |
|
||||
on:focus={handleFocus} |
|
||||
on:blur={e => { |
|
||||
updateValue(e) |
|
||||
handleBlur(e) |
|
||||
}} |
|
||||
value={value || ''} |
|
||||
type="text" /> |
|
||||
<div class="pointer editable-pointer"> |
|
||||
<Icon name="arrowdown" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.container { |
|
||||
position: relative !important; |
|
||||
display: block; |
|
||||
border-radius: var(--border-radius-s); |
|
||||
border: var(--border-transparent); |
|
||||
background-color: var(--background); |
|
||||
} |
|
||||
.container.outline { |
|
||||
border: var(--border-dark); |
|
||||
} |
|
||||
.container.focus { |
|
||||
border: var(--border-blue); |
|
||||
} |
|
||||
|
|
||||
input, |
|
||||
select { |
|
||||
border-radius: var(--border-radius-s); |
|
||||
font-size: var(--font-size-m); |
|
||||
outline: none; |
|
||||
border: none; |
|
||||
color: var(--ink); |
|
||||
text-align: left; |
|
||||
background-color: transparent; |
|
||||
} |
|
||||
select { |
|
||||
display: block !important; |
|
||||
width: 100% !important; |
|
||||
padding: var(--spacing-m) 2rem var(--spacing-m) var(--spacing-m); |
|
||||
appearance: none !important; |
|
||||
-webkit-appearance: none !important; |
|
||||
-moz-appearance: none !important; |
|
||||
align-items: center; |
|
||||
white-space: pre; |
|
||||
opacity: 0; |
|
||||
} |
|
||||
input { |
|
||||
position: absolute; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
width: calc(100% - 30px); |
|
||||
height: 100%; |
|
||||
border: none; |
|
||||
box-sizing: border-box; |
|
||||
padding: var(--spacing-m) 0 var(--spacing-m) var(--spacing-m); |
|
||||
} |
|
||||
|
|
||||
select.thin, |
|
||||
input.thin { |
|
||||
font-size: var(--font-size-xs); |
|
||||
} |
|
||||
select.extraThin, |
|
||||
input.extraThin { |
|
||||
font-size: var(--font-size-xs); |
|
||||
padding: var(--spacing-s) 0 var(--spacing-s) var(--spacing-m); |
|
||||
} |
|
||||
.secondary { |
|
||||
background: var(--grey-2); |
|
||||
} |
|
||||
|
|
||||
select:disabled, |
|
||||
input:disabled, |
|
||||
.disabled { |
|
||||
background: var(--grey-4); |
|
||||
color: var(--grey-6); |
|
||||
} |
|
||||
|
|
||||
.pointer { |
|
||||
right: 0 !important; |
|
||||
top: 0 !important; |
|
||||
bottom: 0 !important; |
|
||||
position: absolute !important; |
|
||||
pointer-events: none !important; |
|
||||
align-items: center !important; |
|
||||
display: flex !important; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.editable-pointer { |
|
||||
border-style: solid; |
|
||||
border-width: 0 0 0 1px; |
|
||||
border-color: var(--grey-4); |
|
||||
padding-left: var(--spacing-xs); |
|
||||
} |
|
||||
.editable-pointer :global(svg) { |
|
||||
margin-right: var(--spacing-xs); |
|
||||
fill: var(--ink); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,55 +0,0 @@ |
|||||
<script> |
|
||||
import { DataList } from "@budibase/bbui" |
|
||||
import { createEventDispatcher } from "svelte" |
|
||||
import { allScreens } from "builderStore" |
|
||||
|
|
||||
const dispatch = createEventDispatcher() |
|
||||
|
|
||||
export let value = "" |
|
||||
|
|
||||
$: urls = getUrls() |
|
||||
|
|
||||
const handleBlur = () => dispatch("change", value) |
|
||||
|
|
||||
const getUrls = () => { |
|
||||
return [ |
|
||||
...$allScreens |
|
||||
.filter( |
|
||||
screen => |
|
||||
screen.props._component.endsWith("/rowdetail") || |
|
||||
screen.routing.route.endsWith(":id") |
|
||||
) |
|
||||
.map(screen => ({ |
|
||||
name: screen.props._instanceName, |
|
||||
url: screen.routing.route, |
|
||||
sort: screen.props._component, |
|
||||
})), |
|
||||
] |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<div> |
|
||||
<DataList |
|
||||
editable |
|
||||
secondary |
|
||||
extraThin |
|
||||
on:blur={handleBlur} |
|
||||
on:change |
|
||||
bind:value> |
|
||||
<option value="" /> |
|
||||
{#each urls as url} |
|
||||
<option value={url.url}>{url.name}</option> |
|
||||
{/each} |
|
||||
</DataList> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
div { |
|
||||
flex: 1 1 auto; |
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
} |
|
||||
div :global(> div) { |
|
||||
flex: 1 1 auto; |
|
||||
} |
|
||||
</style> |
|
||||
Loading…
Reference in new issue