QuantitySelector
QuantitySelector allows the selection of a numeric value and the display of any additional information needed.
Basic usage
<template>
<div class="inline-flex flex-col items-center">
<div class="flex border border-neutral-300 rounded-md">
<SfButton
variant="tertiary"
:disabled="count <= min"
square
class="rounded-r-none"
:aria-controls="inputId"
aria-label="Decrease value"
@click="dec()"
>
<SfIconRemove />
</SfButton>
<input
:id="inputId"
v-model="count"
type="number"
class="appearance-none mx-2 w-8 text-center bg-transparent font-medium [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-inner-spin-button]:display-none [&::-webkit-inner-spin-button]:m-0 [&::-webkit-outer-spin-button]:display-none [&::-webkit-outer-spin-button]:m-0 [-moz-appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none disabled:placeholder-disabled-900 focus-visible:outline focus-visible:outline-offset focus-visible:rounded-sm"
:min="min"
:max="max"
@input="handleOnChange"
/>
<SfButton
variant="tertiary"
:disabled="count >= max"
square
class="rounded-l-none"
:aria-controls="inputId"
aria-label="Increase value"
@click="inc()"
>
<SfIconAdd />
</SfButton>
</div>
<p class="text-xs mt-2 text-neutral-500">
<strong class="text-neutral-900">{{ max }}</strong> in stock
</p>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { clamp } from '@storefront-ui/shared';
import { useCounter } from '@vueuse/core';
import { SfButton, SfIconAdd, SfIconRemove, useId } from '@storefront-ui/vue';
const min = ref(1);
const max = ref(999);
const inputId = useId();
const { count, inc, dec, set } = useCounter(1, { min: min.value, max: max.value });
function handleOnChange(event: Event) {
const currentValue = (event.target as HTMLInputElement)?.value;
const nextValue = parseFloat(currentValue);
set(clamp(nextValue, min.value, max.value));
}
</script>
With rounded buttons
<template>
<div class="flex">
<SfButton
square
class="!rounded-full"
:disabled="count <= min"
:aria-controls="inputId"
aria-label="Decrease value"
@click="dec()"
>
<SfIconRemove />
</SfButton>
<input
:id="inputId"
v-model="count"
type="number"
class="appearance-none px-2 mx-2 w-12 text-center bg-transparent font-medium [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-inner-spin-button]:display-none [&::-webkit-inner-spin-button]:m-0 [&::-webkit-outer-spin-button]:display-none [&::-webkit-outer-spin-button]:m-0 [-moz-appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none disabled:placeholder-disabled-900 focus-visible:outline focus-visible:outline-offset focus-visible:rounded-sm"
:min="min"
:max="max"
@input="handleOnChange"
/>
<SfButton
square
class="!rounded-full"
:disabled="count >= max"
:aria-controls="inputId"
aria-label="Increase value"
@click="inc()"
>
<SfIconAdd />
</SfButton>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useCounter } from '@vueuse/core';
import { SfButton, SfIconAdd, SfIconRemove, useId } from '@storefront-ui/vue';
import { clamp } from '@storefront-ui/shared';
const min = ref(1);
const max = ref(10);
const inputId = useId();
const { count, inc, dec, set } = useCounter(1, { min: min.value, max: max.value });
function handleOnChange(event: Event) {
const currentValue = (event.target as HTMLInputElement)?.value;
const nextValue = parseFloat(currentValue);
set(Number(clamp(nextValue, min.value, max.value)));
}
</script>
Out of stock
<template>
<div class="inline-flex flex-col items-center">
<div class="flex rounded-md border border-disabled-200 bg-disabled-100">
<SfButton variant="tertiary" square disabled :aria-controls="inputId" aria-label="Decrease value">
<SfIconRemove />
</SfButton>
<input
:id="inputId"
type="number"
disabled
class="appearance-none mx-2 w-8 text-center bg-transparent font-medium [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-inner-spin-button]:display-none [&::-webkit-inner-spin-button]:m-0 [&::-webkit-outer-spin-button]:display-none [&::-webkit-outer-spin-button]:m-0 [-moz-appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none disabled:placeholder-disabled-900 focus-visible:outline focus-visible:outline-offset focus-visible:rounded-sm"
placeholder="-"
:min="min"
:max="max"
/>
<SfButton variant="tertiary" square disabled :aria-controls="inputId" aria-label="Increase value">
<SfIconAdd />
</SfButton>
</div>
<p class="text-negative-700 font-medium text-xs mt-2">Out of stock</p>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { SfButton, SfIconAdd, SfIconRemove, useId } from '@storefront-ui/vue';
const min = ref(1);
const max = ref(10);
const inputId = useId();
</script>