InputNumber
Usage
Use the v-model directive to control the value of the InputNumber.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" />
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" />
</template>Use the default-value prop to set the initial value when you do not need to control its state.
<template>
<PInputNumber :default-value="5" />
</template>@internationalized/number package which provides utilities for formatting and parsing numbers across locales and numbering systems.Min / Max
Use the min and max props to set the minimum and maximum values of the InputNumber.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" :min="0" :max="10" />
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" :min="0" :max="10" />
</template>Step
Use the step prop to set the step value of the InputNumber.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" :step="2" />
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" :step="2" />
</template>Starting Value
Use the starting-value prop to set the value of the first increment or decrement when the InputNumber is empty. It is clamped to min / max but not snapped to step. Defaults to min, or 0 when min is not set.
<template>
<PInputNumber :starting-value="18" placeholder="Age" />
</template>Allow Invalid
Use the allow-invalid prop to keep a typed value that is out of the min / max range or off the step grid. Stepping towards the range still brings the value back in range and on the step grid.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" :min="0" :max="10" allow-invalid />
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" :min="0" :max="10" allow-invalid />
</template>Orientation
Use the orientation prop to change the orientation of the InputNumber.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" orientation="vertical" />
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" orientation="vertical" />
</template>Placeholder
Use the placeholder prop to set a placeholder text.
<template>
<PInputNumber placeholder="Enter a number" />
</template>Color
Use the color prop to change the ring color when the InputNumber is focused.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" color="neutral" highlight />
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" color="neutral" highlight />
</template>Variant
Use the variant prop to change the variant of the InputNumber.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" variant="subtle" color="neutral" />
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" variant="subtle" color="neutral" />
</template>Size
Use the size prop to change the size of the InputNumber.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" size="xl" />
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" size="xl" />
</template>Disabled
Use the disabled prop to disable the InputNumber.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" disabled />
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber v-model="value" disabled />
</template>Increment / Decrement
Use the increment and decrement props to customize the increment and decrement buttons with any Button props. Defaults to { variant: 'link' }.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber
v-model="value"
:increment="{
color: 'neutral',
variant: 'solid',
size: 'xs'
}"
:decrement="{
color: 'neutral',
variant: 'solid',
size: 'xs'
}"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber
v-model="value"
:increment="{
color: 'neutral',
variant: 'solid',
size: 'xs'
}"
:decrement="{
color: 'neutral',
variant: 'solid',
size: 'xs'
}"
/>
</template>Increment / Decrement Icons
Use the increment-icon and decrement-icon props to customize the buttons Icon. Defaults to i-lucide-plus / i-lucide-minus.
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<PInputNumber
v-model="value"
increment-icon="i-lucide-arrow-right"
decrement-icon="i-lucide-arrow-left"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
const value = ref(5)
</script>
<template>
<PInputNumber
v-model="value"
increment-icon="i-lucide-arrow-right"
decrement-icon="i-lucide-arrow-left"
/>
</template>Examples
With decimal format
Use the format-options prop to customize the format of the value.
<script setup lang="ts">
const value = ref(5);
</script>
<template>
<PInputNumber
v-model="value"
:format-options="{
signDisplay: 'exceptZero',
minimumFractionDigits: 1,
}"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref(5);
</script>
<template>
<PInputNumber
v-model="value"
:format-options="{
signDisplay: 'exceptZero',
minimumFractionDigits: 1,
}"
/>
</template>
With percentage format
Use the format-options prop with style: 'percent' to customize the format of the value.
<script setup lang="ts">
const value = ref(0.05);
</script>
<template>
<PInputNumber
v-model="value"
:step="0.01"
:format-options="{
style: 'percent',
}"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref(0.05);
</script>
<template>
<PInputNumber
v-model="value"
:step="0.01"
:format-options="{
style: 'percent',
}"
/>
</template>
With currency format
Use the format-options prop with style: 'currency' to customize the format of the value.
<script setup lang="ts">
const value = ref(1500);
</script>
<template>
<PInputNumber
v-model="value"
:format-options="{
style: 'currency',
currency: 'EUR',
currencyDisplay: 'code',
currencySign: 'accounting',
}"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref(1500);
</script>
<template>
<PInputNumber
v-model="value"
:format-options="{
style: 'currency',
currency: 'EUR',
currencyDisplay: 'code',
currencySign: 'accounting',
}"
/>
</template>
Without buttons
You can use the increment and decrement props to control visibility of the buttons.
<script setup lang="ts">
const value = ref(5);
</script>
<template>
<PInputNumber
v-model="value"
:increment="false"
:decrement="false"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref(5);
</script>
<template>
<PInputNumber
v-model="value"
:increment="false"
:decrement="false"
/>
</template>
Within a FormField
You can use the InputNumber within a FormField component to display a label, help text, required indicator, etc.
<script setup lang="ts">
const retries = ref(0);
</script>
<template>
<PFormField
label="Retries"
help="Specify number of attempts"
required
>
<PInputNumber
v-model="retries"
placeholder="Enter retries"
/>
</PFormField>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const retries = ref(0);
</script>
<template>
<PFormField
label="Retries"
help="Specify number of attempts"
required
>
<PInputNumber
v-model="retries"
placeholder="Enter retries"
/>
</PFormField>
</template>
With slots
Use the #increment and #decrement slots to customize the buttons.
<script setup lang="ts">
const value = ref(5);
</script>
<template>
<PInputNumber v-model="value">
<template #decrement>
<PButton
size="xs"
icon="i-lucide-minus"
/>
</template>
<template #increment>
<PButton
size="xs"
icon="i-lucide-plus"
/>
</template>
</PInputNumber>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref(5);
</script>
<template>
<PInputNumber v-model="value">
<template #decrement>
<PButton
size="xs"
icon="i-lucide-minus"
/>
</template>
<template #increment>
<PButton
size="xs"
icon="i-lucide-plus"
/>
</template>
</PInputNumber>
</template>
API
Props
| Prop | Default | Type |
|---|
Slots
| Slot | Type |
|---|
Emits
| Event | Type |
|---|
Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
|---|---|
inputRef | Ref<HTMLInputElement | null> |
Theme
export default defineAppConfig({
ui: {
inputNumber: {
slots: {
root: 'relative inline-flex items-center',
base: [
'placeholder:color-text-dimmed border-0 rounded-md w-full disabled:opacity-75 disabled:cursor-not-allowed',
'transition-colors'
],
increment: 'absolute flex items-center',
decrement: 'absolute flex items-center'
},
variants: {
fieldGroup: {
horizontal: {
root: 'group has-focus-visible:z-1',
base: 'group-not-[*:only-child]:group-first:rounded-e-none group-not-[*:only-child]:group-last:rounded-s-none group-not-last:group-not-first:rounded-none'
},
vertical: {
root: 'group has-focus-visible:z-1',
base: 'group-not-[*:only-child]:group-first:rounded-b-none group-not-[*:only-child]:group-last:rounded-t-none group-not-last:group-not-first:rounded-none'
}
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
size: {
xs: 'px-2 py-1 text-sm/4 gap-1',
sm: 'px-2.5 py-1.5 text-sm/4 gap-1.5',
md: 'px-2.5 py-1.5 text-base/5 gap-1.5',
lg: 'px-3 py-2 text-base/5 gap-2',
xl: 'px-3 py-2 text-base gap-2'
},
variant: {
outline: 'color-text-highlighted bg-background ring ring-inset ring-border-accented',
soft: 'color-text-highlighted bg-background-elevated/50 hover:bg-background-elevated focus:bg-background-elevated disabled:bg-background-elevated/50',
subtle: 'color-text-highlighted bg-background-elevated ring ring-inset ring-border-accented',
ghost: 'color-text-highlighted bg-transparent hover:bg-background-elevated focus:bg-background-elevated disabled:bg-transparent dark:disabled:bg-transparent',
none: 'color-text-highlighted bg-transparent focus:outline-none'
},
disabled: {
true: {
increment: 'opacity-75 cursor-not-allowed',
decrement: 'opacity-75 cursor-not-allowed'
}
},
orientation: {
horizontal: {
base: 'text-center',
increment: 'inset-y-0 end-0 pe-1',
decrement: 'inset-y-0 start-0 ps-1'
},
vertical: {
increment: 'top-0 end-0 pe-1 [&>button]:py-0 scale-80',
decrement: 'bottom-0 end-0 pe-1 [&>button]:py-0 scale-80'
}
},
highlight: {
true: ''
},
fixed: {
false: ''
},
increment: {
false: ''
},
decrement: {
false: ''
}
},
compoundVariants: [
{
color: 'primary',
variant: [
'outline',
'subtle'
],
class: 'outline-primary/25 focus-visible:outline-3 focus-visible:ring-primary'
},
{
color: 'primary',
variant: [
'soft',
'ghost'
],
class: 'outline-primary/25 focus-visible:outline-3'
},
{
color: 'primary',
highlight: true,
class: 'ring ring-inset pohon:ring-primary'
},
{
color: 'neutral',
variant: [
'outline',
'subtle'
],
class: 'outline-border-inverted/25 focus-visible:outline-3 focus-visible:ring-border-inverted'
},
{
color: 'neutral',
variant: [
'soft',
'ghost'
],
class: 'outline-border-inverted/25 focus-visible:outline-3'
},
{
color: 'neutral',
highlight: true,
class: 'ring ring-inset ring-border-inverted'
},
{
orientation: 'horizontal',
decrement: false,
class: 'text-start'
},
{
decrement: true,
size: 'xs',
class: 'ps-7'
},
{
decrement: true,
size: 'sm',
class: 'ps-8'
},
{
decrement: true,
size: 'md',
class: 'ps-9'
},
{
decrement: true,
size: 'lg',
class: 'ps-10'
},
{
decrement: true,
size: 'xl',
class: 'ps-11'
},
{
increment: true,
size: 'xs',
class: 'pe-7'
},
{
increment: true,
size: 'sm',
class: 'pe-8'
},
{
increment: true,
size: 'md',
class: 'pe-9'
},
{
increment: true,
size: 'lg',
class: 'pe-10'
},
{
increment: true,
size: 'xl',
class: 'pe-11'
},
{
fixed: false,
size: 'xs',
class: 'md:text-xs'
},
{
fixed: false,
size: 'sm',
class: 'md:text-xs'
},
{
fixed: false,
size: 'md',
class: 'md:text-sm'
},
{
fixed: false,
size: 'lg',
class: 'md:text-sm'
}
],
defaultVariants: {
size: 'md',
color: 'primary',
variant: 'outline'
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from 'pohon-ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
inputNumber: {
slots: {
root: 'relative inline-flex items-center',
base: [
'placeholder:color-text-dimmed border-0 rounded-md w-full disabled:opacity-75 disabled:cursor-not-allowed',
'transition-colors'
],
increment: 'absolute flex items-center',
decrement: 'absolute flex items-center'
},
variants: {
fieldGroup: {
horizontal: {
root: 'group has-focus-visible:z-1',
base: 'group-not-[*:only-child]:group-first:rounded-e-none group-not-[*:only-child]:group-last:rounded-s-none group-not-last:group-not-first:rounded-none'
},
vertical: {
root: 'group has-focus-visible:z-1',
base: 'group-not-[*:only-child]:group-first:rounded-b-none group-not-[*:only-child]:group-last:rounded-t-none group-not-last:group-not-first:rounded-none'
}
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
size: {
xs: 'px-2 py-1 text-sm/4 gap-1',
sm: 'px-2.5 py-1.5 text-sm/4 gap-1.5',
md: 'px-2.5 py-1.5 text-base/5 gap-1.5',
lg: 'px-3 py-2 text-base/5 gap-2',
xl: 'px-3 py-2 text-base gap-2'
},
variant: {
outline: 'color-text-highlighted bg-background ring ring-inset ring-border-accented',
soft: 'color-text-highlighted bg-background-elevated/50 hover:bg-background-elevated focus:bg-background-elevated disabled:bg-background-elevated/50',
subtle: 'color-text-highlighted bg-background-elevated ring ring-inset ring-border-accented',
ghost: 'color-text-highlighted bg-transparent hover:bg-background-elevated focus:bg-background-elevated disabled:bg-transparent dark:disabled:bg-transparent',
none: 'color-text-highlighted bg-transparent focus:outline-none'
},
disabled: {
true: {
increment: 'opacity-75 cursor-not-allowed',
decrement: 'opacity-75 cursor-not-allowed'
}
},
orientation: {
horizontal: {
base: 'text-center',
increment: 'inset-y-0 end-0 pe-1',
decrement: 'inset-y-0 start-0 ps-1'
},
vertical: {
increment: 'top-0 end-0 pe-1 [&>button]:py-0 scale-80',
decrement: 'bottom-0 end-0 pe-1 [&>button]:py-0 scale-80'
}
},
highlight: {
true: ''
},
fixed: {
false: ''
},
increment: {
false: ''
},
decrement: {
false: ''
}
},
compoundVariants: [
{
color: 'primary',
variant: [
'outline',
'subtle'
],
class: 'outline-primary/25 focus-visible:outline-3 focus-visible:ring-primary'
},
{
color: 'primary',
variant: [
'soft',
'ghost'
],
class: 'outline-primary/25 focus-visible:outline-3'
},
{
color: 'primary',
highlight: true,
class: 'ring ring-inset pohon:ring-primary'
},
{
color: 'neutral',
variant: [
'outline',
'subtle'
],
class: 'outline-border-inverted/25 focus-visible:outline-3 focus-visible:ring-border-inverted'
},
{
color: 'neutral',
variant: [
'soft',
'ghost'
],
class: 'outline-border-inverted/25 focus-visible:outline-3'
},
{
color: 'neutral',
highlight: true,
class: 'ring ring-inset ring-border-inverted'
},
{
orientation: 'horizontal',
decrement: false,
class: 'text-start'
},
{
decrement: true,
size: 'xs',
class: 'ps-7'
},
{
decrement: true,
size: 'sm',
class: 'ps-8'
},
{
decrement: true,
size: 'md',
class: 'ps-9'
},
{
decrement: true,
size: 'lg',
class: 'ps-10'
},
{
decrement: true,
size: 'xl',
class: 'ps-11'
},
{
increment: true,
size: 'xs',
class: 'pe-7'
},
{
increment: true,
size: 'sm',
class: 'pe-8'
},
{
increment: true,
size: 'md',
class: 'pe-9'
},
{
increment: true,
size: 'lg',
class: 'pe-10'
},
{
increment: true,
size: 'xl',
class: 'pe-11'
},
{
fixed: false,
size: 'xs',
class: 'md:text-xs'
},
{
fixed: false,
size: 'sm',
class: 'md:text-xs'
},
{
fixed: false,
size: 'md',
class: 'md:text-sm'
},
{
fixed: false,
size: 'lg',
class: 'md:text-sm'
}
],
defaultVariants: {
size: 'md',
color: 'primary',
variant: 'outline'
}
}
}
})
]
})