-
-
Notifications
You must be signed in to change notification settings - Fork 155
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: NumberField (Spinbutton) (#903)
* feat: basic feature * feat: clamp, parsing modelValue * feat: add locale, a11y, form control * fix: currencySign was undefined * chore: allow isNan * fix: steps, min value, empty initial value * test: add test * test: add test case * feat: expose NumberField * docs: populate numberfield * feat: use Label primitive, and make sure a11y * fix: formatOptions and locale not reactive * docs: number field example * docs: add installation section
- Loading branch information
Showing
34 changed files
with
1,650 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<script setup lang="ts"> | ||
import { NumberFieldDecrement, NumberFieldIncrement, NumberFieldInput, NumberFieldLabel, NumberFieldRoot } from 'radix-vue' | ||
import { Icon } from '@iconify/vue' | ||
</script> | ||
|
||
<template> | ||
<NumberFieldRoot | ||
class="text-sm text-white" | ||
:min="0" | ||
:default-value="18" | ||
> | ||
<NumberFieldLabel>Age</NumberFieldLabel> | ||
|
||
<div class="mt-1 flex items-center border bg-blackA7 border-blackA9 rounded-md"> | ||
<NumberFieldDecrement class="p-2 disabled:opacity-20"> | ||
<Icon icon="radix-icons:minus" /> | ||
</NumberFieldDecrement> | ||
<NumberFieldInput class="bg-transparent w-20 tabular-nums focus:outline-0 p-1" /> | ||
<NumberFieldIncrement class="p-2 disabled:opacity-20"> | ||
<Icon icon="radix-icons:plus" /> | ||
</NumberFieldIncrement> | ||
</div> | ||
</NumberFieldRoot> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
@import '@radix-ui/colors/black-alpha.css'; | ||
@import '@radix-ui/colors/grass.css'; | ||
|
||
/* reset */ | ||
button { | ||
all: unset; | ||
} | ||
|
||
.CheckboxRoot { | ||
background-color: white; | ||
width: 25px; | ||
height: 25px; | ||
border-radius: 4px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
box-shadow: 0 2px 10px var(--black-a7); | ||
} | ||
.CheckboxRoot:hover { | ||
background-color: var(--grass-3); | ||
} | ||
.CheckboxRoot:focus { | ||
box-shadow: 0 0 0 2px black; | ||
} | ||
|
||
.CheckboxIndicator { | ||
color: var(--grass-11); | ||
} | ||
|
||
.Label { | ||
color: white; | ||
padding-left: 15px; | ||
font-size: 15px; | ||
line-height: 1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<script setup lang="ts"> | ||
import { NumberFieldDecrement, NumberFieldIncrement, NumberFieldInput, NumberFieldLabel, NumberFieldRoot } from 'radix-vue' | ||
import { Icon } from '@iconify/vue' | ||
</script> | ||
|
||
<template> | ||
<NumberFieldRoot | ||
class="text-sm text-white" | ||
:min="0" | ||
:default-value="18" | ||
> | ||
<NumberFieldLabel>Age</NumberFieldLabel> | ||
|
||
<div class="mt-1 flex items-center border bg-blackA7 border-blackA9 rounded-md"> | ||
<NumberFieldDecrement class="p-2 disabled:opacity-20"> | ||
<Icon icon="radix-icons:minus" /> | ||
</NumberFieldDecrement> | ||
<NumberFieldInput class="bg-transparent w-20 tabular-nums focus:outline-0 p-1" /> | ||
<NumberFieldIncrement class="p-2 disabled:opacity-20"> | ||
<Icon icon="radix-icons:plus" /> | ||
</NumberFieldIncrement> | ||
</div> | ||
</NumberFieldRoot> | ||
</template> |
14 changes: 14 additions & 0 deletions
14
docs/components/demo/NumberField/tailwind/tailwind.config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
const { blackA } = require('@radix-ui/colors') | ||
|
||
/** @type {import('tailwindcss').Config} */ | ||
module.exports = { | ||
content: ['./**/*.vue'], | ||
theme: { | ||
extend: { | ||
colors: { | ||
...blackA, | ||
}, | ||
}, | ||
}, | ||
plugins: [], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
--- | ||
|
||
title: Number Field | ||
description: A number field allows a user to enter a number, and increment or decrement the value using stepper buttons. | ||
name: number field | ||
aria: https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton | ||
--- | ||
|
||
# Number Field | ||
|
||
<Description> | ||
A number field allows a user to enter a number, and increment or decrement the value using stepper buttons. | ||
</Description> | ||
|
||
<ComponentPreview name="NumberField" /> | ||
|
||
|
||
## Features | ||
|
||
<Highlights | ||
:features="[ | ||
'Full keyboard navigation.', | ||
'Can be controlled or uncontrolled.', | ||
'Support button hold and wheel event.', | ||
'Support numbering systems in different locale.', | ||
'Customizable formatting.' | ||
]" | ||
/> | ||
|
||
## Installation | ||
|
||
|
||
Install the number package. | ||
|
||
<InstallationTabs value="@internationalized/number" /> | ||
|
||
Install the component from your command line. | ||
|
||
<InstallationTabs value="radix-vue" /> | ||
|
||
## Anatomy | ||
|
||
Import all parts and piece them together. | ||
|
||
```vue | ||
<script setup> | ||
import { NumberFieldDecrement, NumberFieldIncrement, NumberFieldInput, NumberFieldLabel, NumberFieldRoot } from 'radix-vue' | ||
</script> | ||
<template> | ||
<NumberFieldRoot> | ||
<NumberFieldLabel /> | ||
<NumberFieldDecrement /> | ||
<NumberFieldInput /> | ||
<NumberFieldIncrement /> | ||
</NumberFieldRoot> | ||
</template> | ||
``` | ||
|
||
## API Reference | ||
|
||
### Root | ||
|
||
Contains all the parts of a number field. An `input` will also render when used within a `form` to ensure events propagate correctly. | ||
|
||
|
||
<!-- @include: @/meta/NumberFieldRoot.md --> | ||
|
||
<DataAttributesTable | ||
:data="[ | ||
{ | ||
attribute: '[data-disabled]', | ||
values: 'Present when disabled', | ||
}, | ||
]" | ||
/> | ||
|
||
### Label | ||
|
||
Label for the input. | ||
|
||
<!-- @include: @/meta/NumberFieldLabel.md --> | ||
|
||
### Input | ||
|
||
Input | ||
|
||
The input component that render the textValue based on value and format options. | ||
|
||
<!-- @include: @/meta/NumberFieldInput.md --> | ||
|
||
|
||
<DataAttributesTable | ||
:data="[ | ||
{ | ||
attribute: '[data-disabled]', | ||
values: 'Present when disabled', | ||
}, | ||
]" | ||
/> | ||
|
||
|
||
### Increment | ||
|
||
The button that increase the value. | ||
|
||
<!-- @include: @/meta/NumberFieldIncrement.md --> | ||
|
||
|
||
<DataAttributesTable | ||
:data="[ | ||
{ | ||
attribute: '[data-pressed]', | ||
values: 'Present when pressed', | ||
}, | ||
{ | ||
attribute: '[data-disabled]', | ||
values: 'Present when disabled', | ||
}, | ||
]" | ||
/> | ||
|
||
|
||
### Decrement | ||
|
||
The button that decrease the value. | ||
|
||
<!-- @include: @/meta/NumberFieldDecrement.md --> | ||
|
||
|
||
<DataAttributesTable | ||
:data="[ | ||
{ | ||
attribute: '[data-pressed]', | ||
values: 'Present when pressed', | ||
}, | ||
{ | ||
attribute: '[data-disabled]', | ||
values: 'Present when disabled', | ||
}, | ||
]" | ||
/> | ||
|
||
|
||
## Example | ||
|
||
|
||
### Decimal | ||
|
||
All options supported by [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat) are supported, including configuration of minimum and maximum fraction digits, sign display, grouping separators, etc. | ||
|
||
|
||
```vue line=3-7 | ||
<template> | ||
<NumberFieldRoot | ||
:default-value="5" | ||
:format-options="{ | ||
signDisplay: 'exceptZero', | ||
minimumFractionDigits: 1, | ||
}" | ||
> | ||
… | ||
</NumberFieldRoot> | ||
</template> | ||
``` | ||
|
||
### Percentage | ||
|
||
You can set `formatOptions.style` to `percent` to treat the value as percentage. You need to set the `step` to `0.01` manually to allow appriopriate step size in this mode. | ||
|
||
|
||
```vue line=3-7 | ||
<template> | ||
<NumberFieldRoot | ||
:default-value="0.05" | ||
:step="0.01" | ||
:format-options="{ | ||
style: 'percent', | ||
}" | ||
> | ||
… | ||
</NumberFieldRoot> | ||
</template> | ||
``` | ||
|
||
### Currency | ||
|
||
You can set `formatOptions.style` to `currency` to treat the value as currency value. The `currency` option must also be passed to set the currency code (e.g. USD). | ||
|
||
If you need to allow the user to change the currency, you should include a separate dropdown next to the number field. The number field itself will not determine the currency from the user input. | ||
|
||
```vue line=4-9 | ||
<template> | ||
<NumberFieldRoot | ||
:default-value="5" | ||
:format-options="{ | ||
style: 'currency', | ||
currency: 'EUR', | ||
currencyDisplay: 'code', | ||
currencySign: 'accounting', | ||
}" | ||
> | ||
… | ||
</NumberFieldRoot> | ||
</template> | ||
``` | ||
|
||
|
||
|
||
## Accessibility | ||
|
||
Adheres to the [Spinbutton WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton). | ||
|
||
### Keyboard Interactions | ||
|
||
<KeyboardTable | ||
:data="[ | ||
{ | ||
keys: ['Arrow Up'], | ||
description: 'Increase the value', | ||
}, | ||
{ | ||
keys: ['Arrow Down'], | ||
description: 'Decrease the value', | ||
}, | ||
{ | ||
keys: ['Page Up'], | ||
description: 'Increase the value by scale of 10', | ||
}, | ||
{ | ||
keys: ['Page Down'], | ||
description: 'Decrease the value by scale of 10', | ||
}, | ||
{ | ||
keys: ['Home'], | ||
description: 'Set value to minimum (if <code>min</code> is provided)', | ||
}, | ||
{ | ||
keys: ['End'], | ||
description: 'Set value to maximum (if <code>max</code> is provided)', | ||
}, | ||
]" | ||
/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<!-- This file was automatic generated. Do not edit it manually --> | ||
|
||
<PropsTable :data="[ | ||
{ | ||
'name': 'as', | ||
'description': '<p>The element or component this component should render as. Can be overwrite by <code>asChild</code></p>\n', | ||
'type': 'AsTag | Component', | ||
'required': false, | ||
'default': '\'button\'' | ||
}, | ||
{ | ||
'name': 'asChild', | ||
'description': '<p>Change the default rendered element for the one passed as a child, merging their props and behavior.</p>\n<p>Read our <a href=\'https://www.radix-vue.com/guides/composition.html\'>Composition</a> guide for more details.</p>\n', | ||
'type': 'boolean', | ||
'required': false | ||
}, | ||
{ | ||
'name': 'disabled', | ||
'description': '', | ||
'type': 'boolean', | ||
'required': false | ||
} | ||
]" /> |
Oops, something went wrong.