Add stepper component to bbui for numeric values

This commit is contained in:
Andrew Kingston 2021-08-18 11:32:39 +01:00
parent 7eafb9125a
commit c945fc650d
8 changed files with 233 additions and 2 deletions

View File

@ -65,6 +65,7 @@
"@spectrum-css/search": "^3.0.2",
"@spectrum-css/sidenav": "^3.0.2",
"@spectrum-css/statuslight": "^3.0.2",
"@spectrum-css/stepper": "^3.0.3",
"@spectrum-css/switch": "^1.0.2",
"@spectrum-css/table": "^3.0.1",
"@spectrum-css/tabs": "^3.0.1",

View File

@ -0,0 +1,172 @@
<script>
import "@spectrum-css/textfield/dist/index-vars.css"
import "@spectrum-css/actionbutton/dist/index-vars.css"
import "@spectrum-css/stepper/dist/index-vars.css"
import { createEventDispatcher } from "svelte"
export let value = null
export let placeholder = null
export let disabled = false
export let error = null
export let id = null
export let readonly = false
export let updateOnChange = true
export let quiet = false
export let min
export let max
export let step
const dispatch = createEventDispatcher()
let focus = false
// We need to keep the field value bound to a different variable in order
// to properly handle erroneous values. If we don't do this then it is
// possible for the field to show stale text which does not represent the
// real value. The reactive statement is to ensure that external changes to
// the value prop are reflected.
let fieldValue = value
$: fieldValue = value
// Ensure step is always a numeric value defaulting to 1
$: step = step == null || isNaN(step) ? 1 : step
const updateValue = value => {
if (readonly) {
return
}
const float = parseFloat(value)
value = isNaN(float) ? null : float
if (value != null) {
if (min != null && value < min) {
value = min
} else if (max != null && value > max) {
value = max
}
}
dispatch("change", value)
fieldValue = value
}
const onFocus = () => {
if (readonly) {
return
}
focus = true
}
const onBlur = event => {
if (readonly) {
return
}
focus = false
updateValue(event.target.value)
}
const onInput = event => {
if (readonly || !updateOnChange) {
return
}
updateValue(event.target.value)
}
const updateValueOnEnter = event => {
if (readonly) {
return
}
if (event.key === "Enter") {
updateValue(event.target.value)
}
}
const stepUp = () => {
if (value == null || isNaN(value)) {
updateValue(step)
} else {
updateValue(value + step)
}
}
const stepDown = () => {
if (value == null || isNaN(value)) {
updateValue(step)
} else {
updateValue(value - step)
}
}
</script>
<div
class="spectrum-Stepper"
class:spectrum-Stepper--quiet={quiet}
class:is-invalid={!!error}
class:is-disabled={disabled}
class:is-focused={focus}
>
{#if error}
<svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
focusable="false"
aria-hidden="true"
>
<use xlink:href="#spectrum-icon-18-Alert" />
</svg>
{/if}
<div class="spectrum-Textfield spectrum-Stepper-textfield">
<input
{disabled}
{readonly}
{id}
bind:value={fieldValue}
placeholder={placeholder || ""}
type="number"
class="spectrum-Textfield-input spectrum-Stepper-input"
on:click
on:blur
on:focus
on:input
on:keyup
on:blur={onBlur}
on:focus={onFocus}
on:input={onInput}
on:keyup={updateValueOnEnter}
/>
</div>
<span class="spectrum-Stepper-buttons">
<button
class="spectrum-ActionButton spectrum-ActionButton--sizeM spectrum-Stepper-stepUp"
tabindex="-1"
on:click={stepUp}
>
<svg
class="spectrum-Icon spectrum-UIIcon-ChevronUp75"
focusable="false"
aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Chevron75" />
</svg>
</button>
<button
class="spectrum-ActionButton spectrum-ActionButton--sizeM spectrum-Stepper-stepDown"
tabindex="-1"
on:click={stepDown}
>
<svg
class="spectrum-Icon spectrum-UIIcon-ChevronDown75"
focusable="false"
aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Chevron75" />
</svg>
</button>
</span>
</div>
<style>
.spectrum-Stepper {
width: 100%;
}
.spectrum-Stepper::before {
display: none;
}
</style>

View File

@ -9,3 +9,4 @@ export { default as CoreSwitch } from "./Switch.svelte"
export { default as CoreSearch } from "./Search.svelte"
export { default as CoreDatePicker } from "./DatePicker.svelte"
export { default as CoreDropzone } from "./Dropzone.svelte"
export { default as CoreStepper } from "./Stepper.svelte"

View File

@ -0,0 +1,45 @@
<script>
import Field from "./Field.svelte"
import Stepper from "./Core/Stepper.svelte"
import { createEventDispatcher } from "svelte"
export let value = null
export let label = null
export let labelPosition = "above"
export let placeholder = null
export let disabled = false
export let readonly = false
export let error = null
export let updateOnChange = true
export let quiet = false
export let min = null
export let max = null
export let step = 1
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
</script>
<Field {label} {labelPosition} {error}>
<Stepper
{updateOnChange}
{error}
{disabled}
{readonly}
{value}
{placeholder}
{quiet}
{min}
{max}
{step}
on:change={onChange}
on:click
on:input
on:blur
on:focus
on:keyup
/>
</Field>

View File

@ -5,6 +5,7 @@ import "@spectrum-css/icon/dist/index-vars.css"
// Components
export { default as Input } from "./Form/Input.svelte"
export { default as Stepper } from "./Form/Stepper.svelte"
export { default as TextArea } from "./Form/TextArea.svelte"
export { default as Select } from "./Form/Select.svelte"
export { default as Combobox } from "./Form/Combobox.svelte"

View File

@ -206,6 +206,11 @@
resolved "https://registry.yarnpkg.com/@spectrum-css/statuslight/-/statuslight-3.0.2.tgz#dc54b6cd113413dcdb909c486b5d7bae60db65c5"
integrity sha512-xodB8g8vGJH20XmUj9ZsPlM1jHrGeRbvmVXkz0q7YvQrYAhim8pP3W+XKKZAletPFAuu8cmUOc6SWn6i4X4z6w==
"@spectrum-css/stepper@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/stepper/-/stepper-3.0.3.tgz#ae89846886431e3edeee060207b8f81540f73a34"
integrity sha512-prAD61ImlOTs9b6PfB3cB08x4lAfxtvnW+RZiTYky0E8GgZdrc/MfCkL5/oqQaIQUtyQv/3Lb7ELAf/0K8QTXw==
"@spectrum-css/switch@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/switch/-/switch-1.0.2.tgz#f0b4c69271964573e02b08e90998096e49e1de44"

View File

@ -1,4 +1,4 @@
import { Checkbox, Input, Select } from "@budibase/bbui"
import { Checkbox, Input, Select, Stepper } from "@budibase/bbui"
import DataSourceSelect from "./DataSourceSelect.svelte"
import DataProviderSelect from "./DataProviderSelect.svelte"
import EventsEditor from "./EventsEditor"
@ -22,7 +22,7 @@ const componentMap = {
dataSource: DataSourceSelect,
dataProvider: DataProviderSelect,
boolean: Checkbox,
number: Input,
number: Stepper,
event: EventsEditor,
table: TableSelect,
color: ColorPicker,

View File

@ -1725,6 +1725,12 @@
"label": "Custom"
}
},
{
"type": "number",
"label": "Number of steps",
"key": "steps",
"defaultValue": 1
},
{
"type": "boolean",
"label": "Disabled",