shadcn.io is not affiliated with official shadcn/ui
Case Quantity Number Input Form Block for Vue
A quantity field that sells in cases of six: typing a number snaps it up to a whole case on leaving, the keys and buttons move one case or five, and the line total is worked out in whole cents.
Installation
Overview
Sell by the case instead of by the bottle, with this number input form for Vue with Ark UI. A compact header sits over a hairline frame with a plus mark on each corner, in two cells: a stepper (a Number Input with an outline Button on each side) and, behind a divider, the line total. A case is six bottles and an order is 6 to 120. Type any number and leaving the field snaps it up to the next whole case and says so ("Rounded up to 2 cases"); the arrow keys and the side buttons move one case, Page Up and Page Down five; past either end the field holds at it and the foot says why. The total reads "12 bottles in 2 cases × $27.00" over the amount, worked out in whole cents. Add to order adds the settled quantity, and an empty field puts focus back in it. It's built from Number Input, Field, Button, Shine Button and Guides, with a CSS entrance only. For wholesale ordering, packs, boxes and any quantity that moves in units.
FAQ
How do I change the case size, the limits and the price?
Edit the constants at the top: CASE (bottles in a case), MIN and MAX (the fewest and the most an order takes, in bottles) and CASE_CENTS (the price of a case in cents). settle is the rule for a typed number and stepFrom the rule for a button or key; both are pure functions, so the snapping, the notes and the total all follow from the same constants.
What happens when I type a quantity that is not a whole case?
Nothing happens while you type. Leaving the field rounds it up to the next whole case and says so in the foot ("Rounded up to 2 cases"); a number under 6 is raised to 6 and one over 120 is capped, each with its reason. The total under the divider follows the number as it will settle, so you see 12 bottles at once. Pressing Add to order with a number that still needs snapping settles it the same way and adds the settled quantity, which is the one the total already shows.
Which keys and buttons step the number, and is that the same everywhere?
Arrow Up and Arrow Down move one case and Page Up and Page Down five, in all four versions: the block takes those keys before the field sees them, so the library's own stepping never runs. The side buttons step one case and stay focusable at either end, where pressing one says the number is already at the limit.
How is the total worked out, and does it work with Arabic digits?
The price is held in whole cents (2700 a case), multiplied by the number of cases and formatted once with Intl.NumberFormat, so there is no floating point to round. A page in Arabic prints Arabic-Indic digits in the field on some versions; asciiDigits reads them back as ASCII before the number is parsed, and the amounts and the sentence keep their left-to-right order inside a right-to-left page.
What stays local, and how do I connect it for real?
Nothing is ordered: Add to order says so in the foot. To make it real, send the settled number of bottles from the submit handler, price it on your server (never trust the total on the page) and show the server's answer in the same status line. If stock or the case size depends on the customer, pass them in as props and change MAX and CASE_CENTS from them.
How do I connect it to react-hook-form or TanStack Form?
Store the settled number of bottles as the form's value and leave the typed text in the block, because the rounding happens on leaving the field. react-hook-form is a React library; use TanStack Form's adapter for your framework and set the field's value from the blur handler. With TanStack Form, validate with onBlur and set the value with field.handleChange after settle.