import type { EventEmitter } from '../../stencil-public-runtime'; import type { NumericInputChangeDetail, NumericInputErrorDetail, NumericInputSize, NumericInputStepDetail, NumericInputVariant } from './mud-numeric-input.types'; import type { LocaleProp } from '../../utils/locale'; /** * Numeric Input — numeric-entry control with stacked step buttons. * * Pattern B (atom-interactive, form-associated): renders its own `` * inside shadow DOM and pairs it with a trailing stepper stack (chevron-up * over chevron-bottom). Shares the visual primitives of `mud-text-input` (border, * focus ring, label, helper / error, sizes, states) and adds a * `--numeric-input-stepper-*` token namespace for the increment / decrement * affordance. * * Why `` instead of * ``: native `type="number"` mixes parsing, locale, and * UI affordances in ways that interact poorly with `precision` rounding and * `min`/`max` clamping. The component delegates parsing + clamping to its own * logic and exposes `inputmode="decimal"` so mobile devices still surface the * numeric keypad. * * @element mud-numeric-input * * @slot label - Rich label content, replaces the `label` prop when present. * @slot helper - Rich helper / hint content, replaces the `helper-text` prop. Hidden when invalid + error-text is shown. * @slot icon-start - Leading icon (a `mud-icon`, icon-leading variant) rendered before the prefix / value. Sized to the square icon box. * @slot prefix - Leading unit / currency symbol rendered before the value (e.g. `€`, `$`, `MDL`). Shares the suffix's text styling — auto-width rather than the fixed icon box, so multi-character symbols don't clip. Distinct from `icon-start`, mirroring the Figma master's separate `prefix` and `leadingIcon` properties. * @slot suffix - Trailing unit text rendered after the value (e.g. `lei`, `kg`). Sits before the stepper stack. */ export declare class MudNumericInput { /** * Color treatment. `destructive` is forced when `invalid` is set. * Numeric inputs ship 3 styles per Figma (no Warning) — invalid numeric * values are typically out-of-range (Destructive) or confirmed-valid * (Success); there is no in-between state worth a Warning tone. * @default 'default' */ variant: NumericInputVariant; /** * Loading state. When true the control becomes uninteractive and a * brand `mud-spinner` replaces the trailing stepper stack. The host * carries `aria-busy="true"` for assistive technologies. * @default false */ loading: boolean; /** * Visual size rung. * @default 'md' */ size: NumericInputSize; /** * Disables interactivity. The internal control receives `aria-disabled` and * the native `disabled` attribute. Stepper buttons are also disabled. * @default false */ disabled: boolean; /** * Marks the field as mandatory. Adds a red asterisk to the label and sets * `aria-required` on the internal control. * @default false */ required: boolean; /** * Renders the field read-only. The control remains focusable; steppers are * suppressed. * @default false */ readonly: boolean; /** * Forces destructive visuals regardless of `variant`. Sets `aria-invalid`. * Use together with `errorText` to surface the message. * @default false */ invalid: boolean; /** * Show the trailing stacked stepper (chevron-up / chevron-bottom) buttons. * Off by default per Figma master, which renders the canonical numeric input * without steppers (suffix-only). Opt in via `show-steppers` for compact * quantity / rating fields where stepper affordance is valuable. * @default false */ showSteppers: boolean; /** * Current numeric value. `undefined` represents an empty field. Reflects to * the host attribute when set. */ value?: number; /** Inclusive lower bound. Stepper-down disables at this value; manual entries below clamp on blur. */ min?: number; /** Inclusive upper bound. Stepper-up disables at this value; manual entries above clamp on blur. */ max?: number; /** * Increment / decrement amount applied by the stepper buttons and arrow keys. * @default 1 */ step: number; /** * Decimal precision applied on blur (number of decimal places). When unset * the value is preserved as typed (subject to clamping). */ precision?: number; /** Form-control `name`. Used during form submission. */ name?: string; /** Placeholder shown when the control is empty. */ placeholder?: string; /** Plain-text label. Use the `label` slot for richer content. */ label?: string; /** Plain-text helper / hint shown below the control. */ helperText?: string; /** * Plain-text error message shown below the control when `invalid` is set. * When present it replaces `helperText` and pairs with the error icon. */ errorText?: string; /** * Accessible label for the increment button. Overrides the `locale`'s copy when set to a * non-empty string. * @default 'Crește' (ro-MD) */ incrementLabel?: string; /** * Accessible label for the decrement button. Overrides the `locale`'s copy when set to a * non-empty string. * @default 'Scade' (ro-MD) */ decrementLabel?: string; /** * Validation message reported when the field is `required` and empty. Overrides the * `locale`'s copy when set to a non-empty string. * @default 'Acest câmp este obligatoriu.' (ro-MD) */ requiredMessage?: string; /** * Validation message reported when the value is below `min`. Carries a `{min}` placeholder. * Overrides the `locale`'s copy when set to a non-empty string. * @default 'Valoarea minimă este {min}.' (ro-MD) */ minMessage?: string; /** * Validation message reported when the value is above `max`. Carries a `{max}` placeholder. * Overrides the `locale`'s copy when set to a non-empty string. * @default 'Valoarea maximă este {max}.' (ro-MD) */ maxMessage?: string; /** * Text of the `mudError` (`reason: 'ambiguous'`) raised for an entry that could be a thousands * group or a decimal, such as `1.234` under `ro-MD`. `{decimal}` is replaced by the locale's * decimal separator. Overrides the `locale`'s copy when set to a non-empty string. * @default 'Valoarea este ambiguă. Scrieți numărul fără separator de mii și folosiți „{decimal}” pentru zecimale.' (ro-MD) */ ambiguousMessage?: string; /** * Human-readable value announcement for screen readers (e.g. `"5 lei"`). * Maps to the native `aria-valuetext` on the spinbutton. An `aria-valuetext` * attribute on the host is read once on load and stripped; later updates go * through this prop, and setting it empty does not clear the value. */ ariaValuetext?: string; /** * Allow fractional input. When `false` the field is integer-only: typing a * decimal separator is blocked and any fractional part is truncated on commit. * @default true */ allowDecimal: boolean; /** * Allow negative input. When `false` the field is positive-only: typing `-` * is blocked and negative entries are rejected on commit. * @default true */ allowNegative: boolean; /** * BCP-47 locale used to group the displayed value with thousands separators * (e.g. `ro-MD` → `1.250,00`). When unset the value displays ungrouped. Grouping * is applied while the field is not being edited; on focus the number is shown * with the locale's decimal separator and no grouping (`1250,00`) so the caret * stays sane. Typed input is read the same way under every locale: spaces and `'` * are ignored, and when both `.` and `,` occur the last one is the decimal. A * single separator is a decimal, except the locale's own grouping character * followed by exactly three digits (`1.234` under `ro-MD`), which raises `mudError` * with `reason: 'ambiguous'` instead of guessing. * * Also selects the language of the built-in copy (steppers, clear button, validation * messages): unset, the copy follows the closest ancestor `lang` (`` * included), else `ro-MD`. Number grouping is unaffected by that fallback — it stays off * unless `locale` itself is set. */ locale?: LocaleProp; /** * When `true`, renders a trailing clear (×) button while the field holds a * value. Activating it clears the value and emits `mudChange` with `null`. * @default false */ clearable: boolean; /** * Accessible label for the clear button. Overrides the `locale`'s copy when set to a * non-empty string. * @default 'Șterge' (ro-MD) */ clearLabel?: string; /** * Maximum number of characters accepted by the field (native `maxlength`). * When set, a character counter renders in the assistive row unless * `show-counter` is `false`. */ maxLength?: number; /** * Force the character counter to show or hide. Auto-shows when `maxlength` * is set; pass `false` to suppress it. * @default true */ showCounter: boolean; private hasLabelSlot; private hasHelperSlot; private hasIconStart; private hasPrefix; private hasSuffix; private isFocused; private fieldsetDisabled; private displayValue; /** * The host's `aria-label` (attribute or native `ariaLabel` property), moved onto the * internal control when no visible label is present. */ private resolvedAriaLabel?; private resolvedAriaValuetext?; /** * Whether the current value falls outside `min` / `max`. Kept as state so the * control can carry `aria-invalid` for a value that is actually wrong, the * way `internals.validity` already reports it to the form. */ private outOfRange; host: HTMLMudNumericInputElement; internals: ElementInternals; /** Fires on every keystroke. `detail.value` is the parsed current value or `null`. */ mudInput: EventEmitter; /** Fires when the value is committed (blur / Enter / stepper). `detail.value` is the clamped, precision-rounded value or `null`. */ mudChange: EventEmitter; /** Fires when a stepper button (or arrow key) bumps the value. */ mudStep: EventEmitter; /** Fires when validation rejects the current input (out-of-range, NaN). */ mudError: EventEmitter; /** Fires when the internal control gains focus. */ mudFocus: EventEmitter; /** Fires when the internal control loses focus. */ mudBlur: EventEmitter; /** Fires when the clear button empties the field. `detail.value` is `null`. */ mudClear: EventEmitter; private readonly instanceId; private readonly labelId; private readonly helperId; private readonly errorId; private readonly counterId; private initialValue; private nativeEl?; private stopAriaLabel?; private stopLang?; syncAriaValuetextProp(next?: string): void; onRequiredChange(): void; onMinChange(): void; onMaxChange(): void; onLocaleChange(): void; validateVariant(next: NumericInputVariant): void; validateSize(next: NumericInputSize): void; handleValueChange(next: number | undefined): void; connectedCallback(): void; disconnectedCallback(): void; /** Built-in strings in the resolved locale, with the override props on top. */ private messages; componentWillLoad(): void; formDisabledCallback(disabled: boolean): void; formResetCallback(): void; formStateRestoreCallback(state: string | File | FormData | null): void; private captureAriaValuetext; private syncValidity; private syncFormValue; /** * Resolve the active locale's grouping + decimal separators. Empty `locale` * → no grouping and a dot decimal (legacy behaviour; comma is still accepted). */ private localeSeparators; /** * Read a typed or pasted entry, the same way under every locale. Every Unicode space and `'` * is dropped and the Unicode minus (U+2212, which `ru-*` writes) counts as `-`. Then: * - `.` and `,` both present: the last one is the decimal, the other groups thousands; * - one of them repeated: it groups thousands (`1.234.567`); * - one of them once: it is the decimal (`1.5`, `1,5`, `1234.5`) — except the locale's own * grouping character followed by exactly three digits (`1.234` under `ro-MD`), which could * be either, so the entry is `ambiguous` rather than guessed. * Stays lenient about sign and fractions — `allow-negative` / `allow-decimal` are enforced in * `commit`. */ private parseRaw; /** Clamp a number to `[min, max]`. */ private clamp; /** Round to the configured `precision` decimal places (no-op when unset). */ private round; /** Apply sign / integer policy, then clamp + round, in one step. */ private commit; /** Render a number for display. Grouped per `locale` while not being edited. */ private formatForDisplay; /** * The field's own number rule, also used for the `{min}` / `{max}` of its messages so they * read like the field. With a `locale`: its decimal separator, grouped unless `focused` * (typing needs a plain number, and the parser never sees a group separator this component * wrote). Without one: `String(value)`, or `toFixed(precision)`. */ private formatNumber; private canStep; private performStep; /** * Choose the seed value when the field is empty and the user presses a * stepper or arrow key: snap into `[min, max]` when defined, else 0. */ private startValueForStep; private onLabelSlotChange; private onHelperSlotChange; private onIconStartSlotChange; private onPrefixSlotChange; private onSuffixSlotChange; private slotHasContent; private handleInput; /** Ambiguous entries yield no value; the raw text stays so the user can fix it. */ private emitAmbiguous; private ambiguousText; private handleChange; private handleFocus; private handleClearClick; private canClear; private hasCharacterCounter; private handleBlur; private commitFromDisplay; private handleKeyDown; private handleStepClick; private isInert; private resolvedVariant; private hasVisibleLabel; private hasErrorMessage; private hasHelperMessage; private describedBy; private showSteppersStack; render(): any; }