FieldStepper

Числовое поле для выбора значения шагами через кнопки / + и/или прямой ввод. Базируется на FieldDecorator поверх <input type="number"> без нативных стрелочек. Кнопки и + — buttonField-слоты по краям поля, отделённые от значения вертикальными разделителями; фон (acrylic) общий для кнопок и инпута.

Когда использовать

  • Количество товара в корзине, кол-во гостей, штучные параметры.
  • Числовые настройки с понятным шагом (вес, объём, время в минутах).
  • Когда дискретные значения уместнее ползунка Slider (есть осмысленный «один тик»).

Анатомия

Size (default m)

ЗначениеКогда
sInline-формы и таблицы
mСтандартные формы (по умолчанию)
lЛендинги, primary-формы

ValidationState (default default)

Через проп error форсится error.

ЗначениеКогда
defaultНет валидации — нейтральный baseline без подсветки и иконки; зелёный сигнал даёт только success
errorНе прошло валидацию
warningПредупреждение
successПодтверждение

Кнопки −/+

Кнопки шага — buttonField-слоты по краям поля, а не отдельные элементы вне инпута.

  • — слот слева, отделён от значения вертикальным Divider.
  • + — слот справа, отделён от значения вертикальным Divider.

Кнопки не участвуют в табуляции (tabIndex={-1}) — фокус принимает сам инпут. Фон (acrylic) общий для кнопок, разделителей и инпута: на hover/focus подсвечивается единым слоем.

Поведение

ПропПоведение
min / maxОграничивают кнопки / +; кнопки становятся disabled на границе
stepШаг изменения (default 1); поддерживает дробные значения через fixed-precision
allowMoreThanLimitsfalse — клампит значение к min/max на blur после ручного ввода
prefix / postfixПроизвольная нода рядом со значением (валюта, единица измерения)

Тултипы

  • minusButtonTooltip / plusButtonTooltipTooltipProps для подсказки над соответствующей кнопкой шага.
  • clampTooltipText — тексты тултипа клампа. Показывается на 2 секунды после blur, когда вручную введённое значение вышло за min (clampTooltipText.min(value)) или max (clampTooltipText.max(value)) при allowMoreThanLimits={false}. По умолчанию — Значение должно быть больше либо равно {value} для нижней границы и Значение должно быть меньше либо равно {value} для верхней.

Установка

pnpm add @cloud-ru/ds-fields
import { FieldStepper } from '@cloud-ru/ds-fields'

Controlled vs uncontrolled

FieldStepper работает в двух режимах:

  • Controlled — передаётся value + onChange. Состояние держит потребитель, компонент только отображает текущее значение.
  • Uncontrolled — передаётся defaultValue (или ничего; тогда начальное значение выводится из min/max). Компонент держит состояние внутри, onChange остаётся опциональным для наблюдения.

Не смешивайте режимы: либо value, либо defaultValue.

Примеры использования

Базовый stepper

Базовый stepperControlled FieldStepper с шагом 1, без ограничений.
tsx
import { FieldStepper } from '@cloud-ru/ds-fields';
import { useState } from 'react';

export function Stepper() {
  const [value, setValue] = useState(1);
  return <FieldStepper label='Количество' value={value} onChange={setValue} />;
}

Uncontrolled

UncontrolleddefaultValue задаёт начальное значение; состояние держит сам компонент.
шт
tsx
import { FieldStepper } from '@cloud-ru/ds-fields';

export function StepperUncontrolled() {
  return <FieldStepper label='Количество' postfix='шт' defaultValue={3} min={0} max={10} />;
}

С единицей измерения

С единицей измеренияpostfix отображается прямо у значения.
шт
tsx
import { FieldStepper } from '@cloud-ru/ds-fields';
import { useState } from 'react';

export function StepperWithPostfix() {
  const [value, setValue] = useState(12);
  return <FieldStepper label='Количество' postfix='шт' value={value} onChange={setValue} />;
}

Min/Max с клампом

Min/Max с клампомallowMoreThanLimits=false клампит вручную введённое значение на blur.
От 0 до 120
tsx
import { FieldStepper } from '@cloud-ru/ds-fields';
import { useState } from 'react';

export function StepperLimits() {
  const [value, setValue] = useState(0);
  return (
    <FieldStepper
      label='Возраст'
      hint='От 0 до 120'
      min={0}
      max={120}
      allowMoreThanLimits={false}
      value={value}
      onChange={setValue}
    />
  );
}

Дробный шаг

Дробный шагstep=0.5 для веса/объёма; внутри используется fixed-precision.
кг
tsx
import { FieldStepper } from '@cloud-ru/ds-fields';
import { useState } from 'react';

export function StepperFractional() {
  const [value, setValue] = useState(1.5);
  return <FieldStepper label='Вес' postfix='кг' step={0.5} min={0} max={20} value={value} onChange={setValue} />;
}

Тултипы кнопок и клампа

Тултипы кнопок и клампаminusButtonTooltip/plusButtonTooltip над кнопками; clampTooltipText показывается на blur при выходе за границы.
Наведите на кнопки −/+; превышение границ показывает тултип на blur
tsx
import { FieldStepper } from '@cloud-ru/ds-fields';
import { useState } from 'react';

export function StepperTooltips() {
  const [value, setValue] = useState(3);
  return (
    <FieldStepper
      label='Количество'
      hint='Наведите на кнопки −/+; превышение границ показывает тултип на blur'
      min={0}
      max={10}
      allowMoreThanLimits={false}
      value={value}
      onChange={setValue}
      minusButtonTooltip={{ tip: 'Уменьшить' }}
      plusButtonTooltip={{ tip: 'Увеличить' }}
      clampTooltipText={{
        min: limit => `Не меньше ${limit}`,
        max: limit => `Не больше ${limit}`,
      }}
    />
  );
}

Props

Types

PropsFieldStepperProps
PropTypeDefaultRequiredDescription
allowMoreThanLimitsbooleantruenoРазрешить ввод значений вне `min`/`max`. Если `false`, на blur значение клампится.
autoFocusbooleannoАвтофокус. На mobile выключается адаптивно (см. `layoutPresets`)
backgroundbooleantruenoФон поля (acrylic)
captionstringnoВторичная подпись справа
clampTooltipText{ min?: ((value: number) => string); max?: ((value: number) => string); } | undefined{ min: 'Значение должно быть больше либо равно {value}', max: 'Значение должно быть меньше либо равно {value}' }noТексты тултипа клампа (показывается на 2с после blur с выходом за `min`/`max`).
classNamestringnoCSS-класс CSS-класс корня `FieldDecorator`
data-test-idstringno
defaultValuenumbernoНачальное значение (uncontrolled-режим). По умолчанию выводится из `min`/`max`.
disabledbooleannoПоле выключено
errorstringnoОшибка (приоритетнее `hint`; форсит `validationState=error`)
fieldClassNamestringnoCSS-класс оболочки поля
hintstringnoПодсказка
idstringnoHTML id
innerRefRef<HTMLDivElement>noRef на корневой DOM-элемент
labelstringnoЗаголовок
labelForstringnoHTML-атрибут `for` для `<label>`
labelTooltipQuestionTooltipPropsnoПодсказка (question-tooltip) у заголовка
layoutPresetsPartial<Record<LayoutType, Partial<{ autoFocus: boolean; }>>>noПереопределение адаптивных дефолтов по раскладке. Участвует `autoFocus`: на mobile он выключен (открывает клавиатуру без действия). Вернуть на mobile — `layoutPresets={{ mobile: { autoFocus: true } }}`.
lengthFieldLengthnoСчётчик длины `current/max`
maxnumbernoМаксимум
minnumbernoМинимум
minusButtonTooltipTooltipPropsnoТултип над кнопкой `−`
namestringnoHTML name
onBlur((event: FocusEvent<HTMLInputElement, Element>) => void)noКолбек блюра
onChange((value: number, event?: ChangeEvent<HTMLInputElement>) => void)noКолбек смены значения. Второй аргумент — событие, если изменение пришло из ручного ввода.
onCopyButtonClick(() => void)noКолбек после успешного копирования значения.
onFocus((event: FocusEvent<HTMLInputElement, Element>) => void)noКолбек фокуса
plusButtonTooltipTooltipPropsnoТултип над кнопкой `+`
postfixReactNodenoПостфикс — текст или иконка справа от значения (например, единица измерения)
prefixReactNodenoПрефикс — текст или иконка слева от значения
readonlybooleannoТолько для чтения
requiredbooleannoПоказать знак обязательности `*`
showCopyButtonbooleantruenoПоказывать кнопку копирования значения (видна в readonly, при `!disabled`).
showHintIconbooleannoОтображение статус-иконки у подсказки (по умолчанию `true`)
size"l" | "m" | "s"mnoРазмер
stepnumber1noШаг приращения
validationState"default" | "error" | "success" | "warning"defaultnoСостояние валидации
valuenumbernoЗначение (controlled-режим)

Types

FieldStepperProps

Storybook

Figma