Add stepper component to bbui for numeric values
This commit is contained in:
parent
7eafb9125a
commit
c945fc650d
|
@ -65,6 +65,7 @@
|
||||||
"@spectrum-css/search": "^3.0.2",
|
"@spectrum-css/search": "^3.0.2",
|
||||||
"@spectrum-css/sidenav": "^3.0.2",
|
"@spectrum-css/sidenav": "^3.0.2",
|
||||||
"@spectrum-css/statuslight": "^3.0.2",
|
"@spectrum-css/statuslight": "^3.0.2",
|
||||||
|
"@spectrum-css/stepper": "^3.0.3",
|
||||||
"@spectrum-css/switch": "^1.0.2",
|
"@spectrum-css/switch": "^1.0.2",
|
||||||
"@spectrum-css/table": "^3.0.1",
|
"@spectrum-css/table": "^3.0.1",
|
||||||
"@spectrum-css/tabs": "^3.0.1",
|
"@spectrum-css/tabs": "^3.0.1",
|
||||||
|
|
|
@ -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>
|
|
@ -9,3 +9,4 @@ export { default as CoreSwitch } from "./Switch.svelte"
|
||||||
export { default as CoreSearch } from "./Search.svelte"
|
export { default as CoreSearch } from "./Search.svelte"
|
||||||
export { default as CoreDatePicker } from "./DatePicker.svelte"
|
export { default as CoreDatePicker } from "./DatePicker.svelte"
|
||||||
export { default as CoreDropzone } from "./Dropzone.svelte"
|
export { default as CoreDropzone } from "./Dropzone.svelte"
|
||||||
|
export { default as CoreStepper } from "./Stepper.svelte"
|
||||||
|
|
|
@ -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>
|
|
@ -5,6 +5,7 @@ import "@spectrum-css/icon/dist/index-vars.css"
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
export { default as Input } from "./Form/Input.svelte"
|
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 TextArea } from "./Form/TextArea.svelte"
|
||||||
export { default as Select } from "./Form/Select.svelte"
|
export { default as Select } from "./Form/Select.svelte"
|
||||||
export { default as Combobox } from "./Form/Combobox.svelte"
|
export { default as Combobox } from "./Form/Combobox.svelte"
|
||||||
|
|
|
@ -206,6 +206,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@spectrum-css/statuslight/-/statuslight-3.0.2.tgz#dc54b6cd113413dcdb909c486b5d7bae60db65c5"
|
resolved "https://registry.yarnpkg.com/@spectrum-css/statuslight/-/statuslight-3.0.2.tgz#dc54b6cd113413dcdb909c486b5d7bae60db65c5"
|
||||||
integrity sha512-xodB8g8vGJH20XmUj9ZsPlM1jHrGeRbvmVXkz0q7YvQrYAhim8pP3W+XKKZAletPFAuu8cmUOc6SWn6i4X4z6w==
|
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":
|
"@spectrum-css/switch@^1.0.2":
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@spectrum-css/switch/-/switch-1.0.2.tgz#f0b4c69271964573e02b08e90998096e49e1de44"
|
resolved "https://registry.yarnpkg.com/@spectrum-css/switch/-/switch-1.0.2.tgz#f0b4c69271964573e02b08e90998096e49e1de44"
|
||||||
|
|
|
@ -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 DataSourceSelect from "./DataSourceSelect.svelte"
|
||||||
import DataProviderSelect from "./DataProviderSelect.svelte"
|
import DataProviderSelect from "./DataProviderSelect.svelte"
|
||||||
import EventsEditor from "./EventsEditor"
|
import EventsEditor from "./EventsEditor"
|
||||||
|
@ -22,7 +22,7 @@ const componentMap = {
|
||||||
dataSource: DataSourceSelect,
|
dataSource: DataSourceSelect,
|
||||||
dataProvider: DataProviderSelect,
|
dataProvider: DataProviderSelect,
|
||||||
boolean: Checkbox,
|
boolean: Checkbox,
|
||||||
number: Input,
|
number: Stepper,
|
||||||
event: EventsEditor,
|
event: EventsEditor,
|
||||||
table: TableSelect,
|
table: TableSelect,
|
||||||
color: ColorPicker,
|
color: ColorPicker,
|
||||||
|
|
|
@ -1725,6 +1725,12 @@
|
||||||
"label": "Custom"
|
"label": "Custom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"label": "Number of steps",
|
||||||
|
"key": "steps",
|
||||||
|
"defaultValue": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
|
|
Loading…
Reference in New Issue