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