Inputs

Cash Register Input

Cash-register entry from the right: the amount lives in whole cents, each digit shifts it one place, and the separator never needs typing.

  • currency
  • money
  • mask
  • cents

Preview

1440pxHost theme

Use it with AI

  1. 1. Copy the link.
  2. 2. Write to your agent in your own words and drop the link into the sentence.
  3. 3. The agent opens the link and installs the component from the registry.

put this in the header: https://vibeui.ru/c/currency-003?lang=en

Точку вводить не нужно — копейки встают сами90 коп.

Show the full instructions

What the link says, spelled out. Use it when your agent cannot open links — paste this text instead.

# Install and place "currency-003" (Cash Register Input) from VibeUI

## 1. Install first — do not skip, do not recreate
Run this exact command before writing any code:
npx shadcn@latest add https://vibeui.ru/r/currency-003.json

Registry item: https://vibeui.ru/r/currency-003.json
Installs to: components/vibeui/currency-003.tsx (the exact path follows this project's components.json aliases).
npm dependencies: none.
Registry dependencies: none.

Install it from the registry. Do not recreate it from the description,
do not substitute a similar component from another library, and do not
rewrite it to match the project's existing style.

## 2. What it is
Cash-register entry from the right: the amount lives in whole cents, each digit shifts it one place, and the separator never needs typing.

An amount field typed right to left: cents and thousand separators appear on their own, the value is kept as whole cents. Zero dependencies, one file.

## 3. How to use it
import { Currency003 } from "@/components/vibeui/currency-003"

<Currency003 label="Receipt total" currency="₽" defaultValue={1499.9} />

Read the installed file for the full prop list.

## 4. Where to place it
This is a small inline component. Put it exactly where the user asked,
inside the existing markup. Do not create a new page, section or
wrapper for it. If a similar control already sits in that spot,
replace it instead of adding a second one.

Placement: ___
(The user fills this line in. If it is still blank, ask where to put it
instead of guessing.)

## 5. Keep exactly as installed
- the local --vibeui-currency-003-* palette — do not swap it for your theme tokens
- its own light surface: the field gets shown over any background
- storing whole cents: floating-point money eventually drifts by a cent
- the right-to-left entry: with no separator to place by hand there is nothing to mis-aim
- extracting digits only from the string: otherwise a user deletes the separator and breaks the format
- the right alignment with tabular-nums: right-to-left entry makes digit jitter especially visible
- inputMode="numeric": phones open the numeric keypad instead of the letters

## 6. You may change
- label — the field caption
- currency — the currency sign
- defaultValue — the starting amount in whole units
- hint — the explanation under the field
- the accent through the accent prop

## 7. Rules
- The input type is text on purpose: a number input cannot show a custom spaced format.
- Emit cents outwards: dividing by a hundred before sending brings the float problem back.
- Input is capped at eleven digits, which covers hundreds of millions.
- Do not lift the CSS variables into globals.css: the component has to stay a single file.

## 8. Verify
- it renders with no console errors;
- it looks like the preview on the VibeUI page you copied this from;
- if it does not, you changed something listed in section 5 — put it back.

For developers

npx shadcn@latest add https://vibeui.ru/r/currency-003.json
https://vibeui.ru/r/currency-003.json

Компонент самодостаточен: один файл, ноль зависимостей, палитра в локальных переменных --vibeui-currency-003-*. Клиентский: сумма в состоянии, целым числом копеек. Ввод идёт справа налево, как на кассовом терминале: из строки берутся только цифры, поэтому разделители не редактируются и промахнуться мимо запятой невозможно. Целые копейки заодно снимают главную беду денег в JS — дробную арифметику: складываются копейки, а не 0.1 и 0.2. Разряды расставляются на каждом нажатии, но курсор всегда в конце строки, поэтому ничего не прыгает.

Component source

The same file your agent installs. Here in case you would rather copy it by hand.

"use client"

import { useId, useState } from "react"
import type { ComponentPropsWithoutRef, CSSProperties } from "react"

export type Currency003Props = Omit<
  ComponentPropsWithoutRef<"div">,
  "children" | "defaultValue" | "onChange"
> & {
  label?: string
  currency?: string
  defaultValue?: number
  hint?: string
  accent?: string
}

// Идея компонента: кассовый ввод справа налево. Сумма хранится в копейках
// целым числом, а каждая набранная цифра сдвигает её на разряд: набрали «12345»
// — получили 123,45. Точку и запятую вводить не нужно вовсе, а значит некуда и
// промахнуться; заодно исчезает главная беда денег в JS — дробная арифметика,
// потому что складываются копейки, а не 0.1 + 0.2. Разряды расставляются на
// каждом нажатии, но курсор всегда в конце строки, поэтому ничего не прыгает.
const STYLES = `
:where([data-vibeui-block="currency-003"]){
--vibeui-currency-003-surface:oklch(1 0 0);
--vibeui-currency-003-field:oklch(0.985 0.002 265);
--vibeui-currency-003-shell:oklch(0.9 0.006 265);
--vibeui-currency-003-fg:oklch(0.22 0.014 265);
--vibeui-currency-003-muted:oklch(0.55 0.014 265);
--vibeui-currency-003-border:oklch(0.88 0.008 265);
--vibeui-currency-003-accent:oklch(0.5 0.15 150);
--vibeui-currency-003-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
/* Своя светлая подложка: поле показывают поверх любого фона. */
[data-vibeui-block="currency-003"]{
display:flex;flex-direction:column;gap:0.5rem;
width:100%;max-width:20rem;box-sizing:border-box;padding:0.875rem;
background:var(--vibeui-currency-003-surface);
border:1px solid var(--vibeui-currency-003-shell);border-radius:0.875rem;
font-family:var(--vibeui-currency-003-font);color:var(--vibeui-currency-003-fg);
}
[data-vibeui-block="currency-003"] label{font-size:0.8125rem;font-weight:650}
[data-vibeui-block="currency-003"] [data-part="row"]{
display:flex;align-items:baseline;gap:0.375rem;
padding:0.625rem 0.875rem;box-sizing:border-box;
border:1px solid var(--vibeui-currency-003-border);border-radius:0.75rem;
background:var(--vibeui-currency-003-field);
}
[data-vibeui-block="currency-003"] [data-part="row"]:focus-within{
border-color:var(--vibeui-currency-003-accent);
box-shadow:0 0 0 2px oklch(0.5 0.15 150 / 18%);
}
/* Числа моноширинные: при вводе справа налево дрожание разрядов заметно. */
[data-vibeui-block="currency-003"] input{
flex:1 1 auto;min-width:0;width:100%;
appearance:none;border:0;background:none;outline:none;
color:inherit;font:inherit;font-size:1.625rem;font-weight:750;
text-align:right;font-variant-numeric:tabular-nums;letter-spacing:-0.01em;
}
[data-vibeui-block="currency-003"] [data-part="sign"]{
flex:none;font-size:1.125rem;font-weight:700;color:var(--vibeui-currency-003-muted);
}
[data-vibeui-block="currency-003"] [data-part="foot"]{
display:flex;align-items:baseline;justify-content:space-between;gap:0.75rem;margin:0;
font-size:0.75rem;color:var(--vibeui-currency-003-muted);
}
[data-vibeui-block="currency-003"] [data-part="kopecks"]{
font-weight:650;color:var(--vibeui-currency-003-fg);font-variant-numeric:tabular-nums;
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="currency-003"] *{animation:none!important;transition:none!important}}
`

// Форматируется всегда целое число копеек: дробная арифметика к деньгам не
// подпускается вовсе.
function format(cents: number) {
  const whole = Math.floor(cents / 100)
  const rest = String(cents % 100).padStart(2, "0")
  return `${whole.toLocaleString("ru-RU")},${rest}`
}

/**
 * Кассовый ввод суммы: цифры набираются справа налево, копейки встают сами.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Currency003({
  label = "Сумма чека",
  currency = "₽",
  defaultValue = 1499.9,
  hint = "Точку вводить не нужно — копейки встают сами",
  accent,
  className,
  style,
  ...props
}: Currency003Props) {
  const id = useId()
  const [cents, setCents] = useState(Math.round(defaultValue * 100))

  const palette = {
    ...(accent ? { "--vibeui-currency-003-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-currency-003" precedence="medium">
        {STYLES}
      </style>
      <div
        {...props}
        data-vibeui-block="currency-003"
        className={className}
        style={palette}
      >
        <label htmlFor={id}>{label}</label>
        <div data-part="row">
          <input
            id={id}
            type="text"
            inputMode="numeric"
            value={format(cents)}
            aria-describedby={`${id}-foot`}
            onChange={(event) => {
              // Из строки берутся только цифры: разделители не редактируются,
              // а каждая новая цифра сдвигает сумму на разряд.
              const digits = event.target.value.replace(/\D/g, "").slice(0, 11)
              setCents(Number(digits) || 0)
            }}
          />
          <span data-part="sign" aria-hidden="true">
            {currency}
          </span>
        </div>
        <p id={`${id}-foot`} data-part="foot">
          <span>{hint}</span>
          <span data-part="kopecks">{cents % 100} коп.</span>
        </p>
      </div>
    </>
  )
}