@ehfuse/mui-form-controls 3.1.20 → 3.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,8 +16,9 @@ MUI 기반 폼 컨트롤과 텍스트 필드 컴포넌트 모음
16
16
  | **DateTimeTextField** | 날짜 + 시간 입력 필드 | [API](./docs/ko/api.md#datetimetextfield) |
17
17
  | **EmailTextField** | 도메인 자동완성, 유효성 검사 기능이 있는 이메일 필드 | [API](./docs/ko/api.md#emailtextfield) |
18
18
  | **JuminTextField** | 주민등록번호 입력 필드 (마스킹, 정보 추출) | [API](./docs/ko/api.md#jumintextfield) |
19
- | **NumberField** | 숫자 입력 (`default` 스피너 / `stepper` 가로 스테퍼, 길게 누르기 가속) | [API](./docs/ko/api.md#numberfield) |
20
- | **NumberSpinner** | 숫자 스피너 | [API](./docs/ko/api.md#numberspinner) |
19
+ | **NumberField** | 숫자 입력 (MUI OutlinedInput + 위·아래 스피너) | [API](./docs/ko/api.md#numberfield) |
20
+ | **NumberStepper** | 가로 스테퍼 (`[-]` 숫자 `[+]`, 길게 누르기 가속) | [API](./docs/ko/api.md#numberstepper) |
21
+ | **NumberSpinner** | 숫자 스피너 (좌우 버튼 + 라벨 드래그) | [API](./docs/ko/api.md#numberspinner) |
21
22
  | **NumberTextField** | 천 단위 구분자, 소수점, 음수 지원하는 숫자 필드 | [API](./docs/ko/api.md#numbertextfield) |
22
23
  | **PasswordTextField** | 비밀번호 보기/숨기기 토글, 유효성 검사 기능이 있는 비밀번호 필드 | [API](./docs/ko/api.md#passwordtextfield) |
23
24
  | **PhoneTextField** | 자동 포맷팅, prefix 고정 기능이 있는 전화번호 필드 | [API](./docs/ko/api.md#phonetextfield) |
@@ -102,6 +103,7 @@ import {
102
103
  ButtonGroup,
103
104
  Stepper,
104
105
  NumberField,
106
+ NumberStepper,
105
107
  NumberSpinner,
106
108
  LabelSelect,
107
109
  Autocomplete,
@@ -292,25 +294,32 @@ import { AddressTextField } from '@ehfuse/mui-form-controls/address';
292
294
 
293
295
  `draggable`이 `true`이면 짧은 클릭과 드래그 시작이 겹칠 수 있습니다(기본 포인터 센서는 약 5px 이동 후 드래그). 자세한 props는 [한국어 API](./docs/ko/api.md#tagstextfield)를 참고하세요.
294
296
 
295
- ## NumberField
297
+ ## NumberField · NumberStepper · NumberSpinner
296
298
 
297
- 기본은 MUI OutlinedInput + 위·아래 스피너입니다. `variant="stepper"`는 장바구니 수량처럼 `[-]` 숫자 `[+]` 가로 UI입니다.
299
+ 컴포넌트는 공용 `useNumberBasic`을 기반으로 동일한 동작 규칙을 공유합니다.
300
+
301
+ - 입력 중 `min`/`max` 초과 시 **즉시 경계값으로 고정** (clamp)
302
+ - 천 단위 구분은 기본 적용(Intl·로케일), `thousandSeparator={false}`로 끔
303
+ - `form`/`name` 지정 시 폼 연동
304
+ - `clearWhenZero`로 0을 빈칸 표시 (**기본 `false` — 0을 0으로 표시**)
305
+
306
+ 표시 형태만 다릅니다.
307
+
308
+ | 컴포넌트 | 형태 |
309
+ | -------- | ---- |
310
+ | **NumberField** | MUI OutlinedInput + 오른쪽 위·아래 스피너 |
311
+ | **NumberStepper** | `[-]` 숫자 `[+]` 가로 스테퍼 (길게 누르기 가속) |
312
+ | **NumberSpinner** | 좌우 `[-]`/`[+]` 버튼 + 라벨 드래그(scrub) 증감 |
298
313
 
299
314
  ```tsx
300
- // 기본 스피너
315
+ // NumberField — 기본 입력 + 위·아래 스피너
301
316
  <NumberField label="수량" defaultValue={2} min={0} max={99} />
302
317
 
303
- // 천 단위 구분은 기본 적용(Intl·로케일). 끄려면 thousandSeparator={false}
304
- <NumberField
305
- label="금액"
306
- defaultValue={1234567}
307
- min={0}
308
- max={99999999}
309
- />
318
+ // 천 단위 구분은 기본 적용. 끄려면 thousandSeparator={false}
319
+ <NumberField label="금액" defaultValue={1234567} min={0} max={99999999} />
310
320
 
311
- // 가로 스테퍼 + 길게 누르기 가속 (CPS = 초당 클릭/증감 횟수)
312
- <NumberField
313
- variant="stepper"
321
+ // NumberStepper — 가로 스테퍼 + 길게 누르기 가속 (CPS = 초당 증감 횟수)
322
+ <NumberStepper
314
323
  defaultValue={1}
315
324
  min={0}
316
325
  max={99}
@@ -320,16 +329,20 @@ import { AddressTextField } from '@ehfuse/mui-form-controls/address';
320
329
  stepperAccelerateCps={5}
321
330
  stepperAccelerateMaxCps={25}
322
331
  />
332
+
333
+ // NumberSpinner — 좌우 버튼 + 라벨 드래그
334
+ <NumberSpinner label="수량" defaultValue={2} min={0} max={99} />
323
335
  ```
324
336
 
325
337
  | prop | 설명 |
326
338
  | ---- | ---- |
327
- | `stepperAccelerateHoldDelay` | 누른 직후 1회 변경 후, 자동 반복까지 대기(ms) |
328
- | `stepperAccelerateRampDuration` | 시작 CPS→최대 CPS까지 걸리는 시간(ms) |
329
- | `stepperAccelerateCps` | 자동 반복 **시작** 속도 (초당 횟수) |
330
- | `thousandSeparator` | `boolean \| string` 기본 `true`. `false` 끔 · `string` 구분 문자 고정 · NumberField는 `true`일 때 Intl |
339
+ | `thousandSeparator` | `boolean \| string` 기본 `true`. `false` · `string` 구분 문자 고정 · `true`일 때 Intl |
340
+ | `clearWhenZero` | 0을 빈칸으로 표시. 기본 `false` |
341
+ | `stepperAccelerateHoldDelay` | (NumberStepper) 누른 직후 1회 변경 후, 자동 반복까지 대기(ms) |
342
+ | `stepperAccelerateRampDuration` | (NumberStepper) 시작 CPS→최대 CPS까지 걸리는 시간(ms) |
343
+ | `stepperAccelerateCps` | (NumberStepper) 자동 반복 **시작** 속도 (초당 횟수) |
331
344
 
332
- `stepperEditable`, `stepperButtonDivider`, `spinnerDivider` 등 전체 props는 [API](./docs/ko/api.md#numberfield)를 참고하세요.
345
+ `stepperEditable`, `stepperButtonDivider`(NumberStepper), `spinnerDivider`(NumberField) 등 전체 props는 [API](./docs/ko/api.md#numberfield)를 참고하세요.
333
346
 
334
347
  ## Boolean 필드 표준 패턴
335
348
 
@@ -6,5 +6,8 @@
6
6
  * @author 김영진 (ehfuse@gmail.com)
7
7
  */
8
8
  export { default as NumberField } from "./components/NumberField";
9
- export type { NumberFieldAlign, NumberFieldProps, NumberFieldVariant, } from "./components/NumberField";
9
+ export type { NumberFieldAlign, NumberFieldProps, } from "./components/NumberField";
10
+ export { default as NumberStepper } from "./components/NumberStepper";
11
+ export type { NumberStepperProps } from "./components/NumberStepper";
10
12
  export { default as NumberSpinner } from "./components/NumberSpinner";
13
+ export type { NumberSpinnerProps } from "./components/NumberSpinner";
@@ -1,101 +1,21 @@
1
- import * as React from "react";
2
- import { NumberField as BaseNumberField } from "@base-ui/react/number-field";
3
- import type { SxProps, Theme } from "@mui/material/styles";
4
- import type { FormLike } from "../types";
5
- import { type ThousandSeparatorProp } from "../utils/number";
6
- export type NumberFieldVariant = "default" | "stepper";
7
- export type NumberFieldAlign = "left" | "center" | "right";
8
- export type NumberFieldProps = BaseNumberField.Root.Props & {
9
- label?: React.ReactNode;
10
- size?: "small" | "medium";
11
- error?: boolean;
12
- fullWidth?: boolean;
13
- form?: FormLike | null;
14
- suffix?: React.ReactNode;
15
- /** 입력 숫자 텍스트 정렬 방향. */
16
- align?: NumberFieldAlign;
17
- /**
18
- * 천 단위 구분.
19
- * - `false`: 구분 없음
20
- * - `true` / `undefined`(기본): `Intl` 로케일 규칙 (`locale`·`format`과 함께)
21
- * - `string`: 구분 문자 직접 지정 (예: `","`, `"."`, `" "`)
22
- * @default true
23
- */
24
- thousandSeparator?: ThousandSeparatorProp;
25
- /** @default "default" — `stepper`는 [-] 값 [+] 가로 스테퍼 UI */
26
- variant?: NumberFieldVariant;
27
- /**
28
- * 값이 정확히 0일 때 입력칸을 빈칸으로 표시한다 (placeholder만 노출).
29
- *
30
- * 내부 값은 항상 숫자(0 포함)로 유지되므로 base-ui의 스피너 증감이 정상 동작한다
31
- * (빈칸=0에서 ▲ → 1, 1에서 ▼ → 0=빈칸). 표시(렌더링)만 비우며,
32
- * `onValueChange`로는 숫자 0이 그대로 전달된다. 포커스/입력 중에는 사용자가
33
- * 타이핑한 값을 그대로 보여주고, 비포커스 상태에서만 0을 빈칸으로 표시한다.
34
- *
35
- * `default`·`stepper` 두 variant 모두에 적용된다.
36
- * 0을 그대로 `0`으로 표시하려면 `clearWhenZero={false}`로 끈다.
37
- * @default true
38
- */
39
- clearWhenZero?: boolean;
40
- /**
41
- * `variant="stepper"`일 때 가운데를 키패드·키보드로 직접 입력할지 여부.
42
- * @default false — Typography로 숫자만 표시 (장바구니 수량 UI 등)
43
- */
44
- stepperEditable?: boolean;
45
- /** `variant="stepper"` 루트(Box)에 적용할 `sx` */
46
- stepperSx?: SxProps<Theme>;
47
- /** `variant="stepper"`일 때 − / + 버튼과 숫자 영역 사이 세로 구분선 */
48
- stepperButtonDivider?: boolean;
49
- /** `variant="stepper"` 감소(−) 버튼 `sx` */
50
- stepperDecrementButtonSx?: SxProps<Theme>;
51
- /** `variant="stepper"` 증가(+) 버튼 `sx` */
52
- stepperIncrementButtonSx?: SxProps<Theme>;
53
- /** `variant="stepper"` 가운데 숫자 영역 `sx` */
54
- stepperCenterSx?: SxProps<Theme>;
55
- /**
56
- * `variant="stepper"`일 때 화면에 라벨을 표시할지 여부.
57
- * @default false — 장바구니 수량처럼 컴팩트 UI에는 라벨 없음. `label`은 접근성용 aria-label로만 쓸 수 있음.
58
- */
59
- stepperShowLabel?: boolean;
60
- /** `variant="stepper"` 감소 버튼 아이콘. 미지정 시 Remove(−) 아이콘 */
61
- stepperDecrementIcon?: React.ReactNode;
62
- /** `variant="stepper"` 증가 버튼 아이콘. 미지정 시 Add(+) 아이콘 */
63
- stepperIncrementIcon?: React.ReactNode;
64
- /** `variant="stepper"` 감소 버튼 클릭 이벤트. `event.preventDefault()` 호출 시 기본 감소 동작을 막는다. */
65
- onStepperDecrementClick?: (event: React.MouseEvent<HTMLButtonElement>, value: number | null) => void;
66
- /** `variant="stepper"` 증가 버튼 클릭 이벤트. `event.preventDefault()` 호출 시 기본 증가 동작을 막는다. */
67
- onStepperIncrementClick?: (event: React.MouseEvent<HTMLButtonElement>, value: number | null) => void;
68
- /**
69
- * `variant="default"`일 때 위·아래 스피너 영역 왼쪽 구분선 표시 여부.
1
+ /**
2
+ * NumberField.tsx
3
+ *
4
+ * MUI OutlinedInput + Base UI NumberField 기반 숫자 입력 필드.
5
+ * 가로 스테퍼(−값+)는 `NumberStepper`, 드래그 스피너는 `NumberSpinner`로 분리됐다.
6
+ * 상태·clamp·grouping 공용 로직은 `useNumberBasic` 훅이 담당한다.
7
+ *
8
+ * @license MIT
9
+ * @copyright 2025 김영진 (Kim Young Jin)
10
+ * @author 김영진 (ehfuse@gmail.com)
11
+ */
12
+ import { type NumberBaseProps } from "./numberBasic";
13
+ export type { NumberFieldAlign } from "./numberBasic";
14
+ export type NumberFieldProps = NumberBaseProps & {
15
+ /**
16
+ * 위·아래 스피너 영역 왼쪽 구분선 표시 여부.
70
17
  * @default true
71
18
  */
72
19
  spinnerDivider?: boolean;
73
- /**
74
- * `variant="stepper"`에서 길게 누를 때 반복 간격이 점점 짧아지는 가속 효과.
75
- * @default true
76
- */
77
- stepperAccelerate?: boolean;
78
- /**
79
- * 길게 누른 뒤, 숫자가 **한 번** 바뀐 다음 자동 반복이 시작되기까지의 대기 시간(ms).
80
- * @default 300
81
- */
82
- stepperAccelerateHoldDelay?: number;
83
- /**
84
- * 자동 반복이 시작된 뒤, 시작 속도에서 최고 속도까지 빨라지는 데 걸리는 시간(ms).
85
- * @default 1800
86
- */
87
- stepperAccelerateRampDuration?: number;
88
- /**
89
- * 길게 눌렀을 때 **처음** 자동으로 숫자가 바뀌는 속도
90
- * 예) `5` → 1초에 5번(약 0.2초마다 1씩 증가)
91
- * @default 5
92
- */
93
- stepperAccelerateCps?: number;
94
- /**
95
- * 가속이 끝난 뒤 유지되는 **최고** 속도.
96
- * 예) `100` → 1초에 100번 반복 (약 0.01초마다 1씩 증가)
97
- * @default 100
98
- */
99
- stepperAccelerateMaxCps?: number;
100
20
  };
101
21
  export default function NumberField(props: NumberFieldProps): import("react/jsx-runtime").JSX.Element;
@@ -1,16 +1,14 @@
1
- import * as React from "react";
2
- import { NumberField as BaseNumberField } from "@base-ui/react/number-field";
3
- import { type ThousandSeparatorProp } from "../utils/number";
4
- type NumberSpinnerProps = BaseNumberField.Root.Props & {
5
- label?: React.ReactNode;
6
- size?: "small" | "medium";
7
- error?: boolean;
8
- suffix?: React.ReactNode;
9
- /**
10
- * 단위 구분자(표시·입력, `Intl.NumberFormat`). `true`이면 `format.useGrouping: true` 병합.
11
- * @default true — `string`이면 해당 문자로 그룹
12
- */
13
- thousandSeparator?: ThousandSeparatorProp;
14
- };
15
- export default function NumberSpinner({ id: idProp, label, error, size, suffix, thousandSeparator, locale, format, ...other }: NumberSpinnerProps): import("react/jsx-runtime").JSX.Element;
16
- export {};
1
+ /**
2
+ * NumberSpinner.tsx
3
+ *
4
+ * 좌우 [−][+] 버튼 + 라벨 드래그(ScrubArea) 증감형 숫자 입력.
5
+ * 값 상태·min/max 즉시 clamp·grouping·폼 연동 공용 로직은 `useNumberBasic` 훅이
6
+ * 담당한다(min/max·form이 주어졌을 때만 동작하므로 기존 사용과 호환).
7
+ *
8
+ * @license MIT
9
+ * @copyright 2025 김영진 (Kim Young Jin)
10
+ * @author 김영진 (ehfuse@gmail.com)
11
+ */
12
+ import { type NumberBaseProps } from "./numberBasic";
13
+ export type NumberSpinnerProps = NumberBaseProps;
14
+ export default function NumberSpinner(props: NumberSpinnerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * NumberStepper.tsx
3
+ *
4
+ * 가로 스테퍼([−] 값 [+]) 형태의 숫자 입력. 장바구니 수량 등 컴팩트 UI용.
5
+ * 값 상태·clamp·grouping 공용 로직은 `useNumberBasic` 훅이 담당한다.
6
+ *
7
+ * @license MIT
8
+ * @copyright 2025 김영진 (Kim Young Jin)
9
+ * @author 김영진 (ehfuse@gmail.com)
10
+ */
11
+ import * as React from "react";
12
+ import type { SxProps, Theme } from "@mui/material/styles";
13
+ import { type NumberBaseProps } from "./numberBasic";
14
+ export type { NumberFieldAlign } from "./numberBasic";
15
+ export type NumberStepperProps = NumberBaseProps & {
16
+ /**
17
+ * 가운데를 키패드·키보드로 직접 입력할지 여부.
18
+ * @default false — Typography로 숫자만 표시 (장바구니 수량 UI 등)
19
+ */
20
+ stepperEditable?: boolean;
21
+ /** 루트(Box)에 적용할 `sx` */
22
+ stepperSx?: SxProps<Theme>;
23
+ /** − / + 버튼과 숫자 영역 사이 세로 구분선 */
24
+ stepperButtonDivider?: boolean;
25
+ /** 감소(−) 버튼 `sx` */
26
+ stepperDecrementButtonSx?: SxProps<Theme>;
27
+ /** 증가(+) 버튼 `sx` */
28
+ stepperIncrementButtonSx?: SxProps<Theme>;
29
+ /** 가운데 숫자 영역 `sx` */
30
+ stepperCenterSx?: SxProps<Theme>;
31
+ /**
32
+ * 화면에 라벨을 표시할지 여부.
33
+ * @default false — 컴팩트 UI에는 라벨 없음. `label`은 aria-label로만 쓸 수 있음.
34
+ */
35
+ stepperShowLabel?: boolean;
36
+ /** 감소 버튼 아이콘. 미지정 시 Remove(−) 아이콘 */
37
+ stepperDecrementIcon?: React.ReactNode;
38
+ /** 증가 버튼 아이콘. 미지정 시 Add(+) 아이콘 */
39
+ stepperIncrementIcon?: React.ReactNode;
40
+ /** 감소 버튼 클릭 이벤트. `event.preventDefault()` 호출 시 기본 감소 동작을 막는다. */
41
+ onStepperDecrementClick?: (event: React.MouseEvent<HTMLButtonElement>, value: number | null) => void;
42
+ /** 증가 버튼 클릭 이벤트. `event.preventDefault()` 호출 시 기본 증가 동작을 막는다. */
43
+ onStepperIncrementClick?: (event: React.MouseEvent<HTMLButtonElement>, value: number | null) => void;
44
+ /**
45
+ * 길게 누를 때 반복 간격이 점점 짧아지는 가속 효과.
46
+ * @default true
47
+ */
48
+ stepperAccelerate?: boolean;
49
+ /**
50
+ * 길게 누른 뒤, 숫자가 **한 번** 바뀐 다음 자동 반복이 시작되기까지의 대기 시간(ms).
51
+ * @default 300
52
+ */
53
+ stepperAccelerateHoldDelay?: number;
54
+ /**
55
+ * 자동 반복이 시작된 뒤, 시작 속도에서 최고 속도까지 빨라지는 데 걸리는 시간(ms).
56
+ * @default 1800
57
+ */
58
+ stepperAccelerateRampDuration?: number;
59
+ /**
60
+ * 길게 눌렀을 때 **처음** 자동으로 숫자가 바뀌는 속도
61
+ * 예) `5` → 1초에 5번(약 0.2초마다 1씩 증가)
62
+ * @default 5
63
+ */
64
+ stepperAccelerateCps?: number;
65
+ /**
66
+ * 가속이 끝난 뒤 유지되는 **최고** 속도.
67
+ * 예) `100` → 1초에 100번 반복 (약 0.01초마다 1씩 증가)
68
+ * @default 100
69
+ */
70
+ stepperAccelerateMaxCps?: number;
71
+ };
72
+ export default function NumberStepper(props: NumberStepperProps): import("react/jsx-runtime").JSX.Element;
@@ -3,5 +3,8 @@ export type { GroupedInputWrapperProps } from "./GroupedInputWrapper";
3
3
  export { ColonBox } from "./ColonBox";
4
4
  export type { ColonBoxProps } from "./ColonBox";
5
5
  export { default as NumberField } from "./NumberField";
6
- export type { NumberFieldProps } from "./NumberField";
6
+ export type { NumberFieldProps, NumberFieldAlign } from "./NumberField";
7
+ export { default as NumberStepper } from "./NumberStepper";
8
+ export type { NumberStepperProps } from "./NumberStepper";
7
9
  export { default as NumberSpinner } from "./NumberSpinner";
10
+ export type { NumberSpinnerProps } from "./NumberSpinner";
@@ -0,0 +1,72 @@
1
+ /**
2
+ * numberBasic.tsx
3
+ *
4
+ * NumberField · NumberStepper · NumberSpinner 공용 타입/헬퍼/폼 바인딩.
5
+ * 값 상태·clamp·grouping 등 동작 로직은 `useNumberBasic` 훅이 담당하고,
6
+ * 이 파일은 세 컴포넌트가 공유하는 순수 헬퍼와 타입만 모은다.
7
+ *
8
+ * @license MIT
9
+ * @copyright 2025 김영진 (Kim Young Jin)
10
+ * @author 김영진 (ehfuse@gmail.com)
11
+ */
12
+ import * as React from "react";
13
+ import { NumberField as BaseNumberField } from "@base-ui/react/number-field";
14
+ import type { NumberFieldRoot } from "@base-ui/react/number-field";
15
+ import type { FormLike } from "../types";
16
+ import type { ThousandSeparatorProp } from "../utils/number";
17
+ export type NumberFieldAlign = "left" | "center" | "right";
18
+ /**
19
+ * 세 숫자 컴포넌트(NumberField·NumberStepper·NumberSpinner) 공통 props.
20
+ * 각 컴포넌트는 여기에 자기만의 표시 옵션을 더한다.
21
+ */
22
+ export type NumberBaseProps = BaseNumberField.Root.Props & {
23
+ label?: React.ReactNode;
24
+ size?: "small" | "medium";
25
+ error?: boolean;
26
+ fullWidth?: boolean;
27
+ form?: FormLike | null;
28
+ suffix?: React.ReactNode;
29
+ /** 입력 숫자 텍스트 정렬 방향. */
30
+ align?: NumberFieldAlign;
31
+ /**
32
+ * 천 단위 구분.
33
+ * - `false`: 구분 없음
34
+ * - `true` / `undefined`(기본): `Intl` 로케일 규칙 (`locale`·`format`과 함께)
35
+ * - `string`: 구분 문자 직접 지정 (예: `","`, `"."`, `" "`)
36
+ * @default true
37
+ */
38
+ thousandSeparator?: ThousandSeparatorProp;
39
+ /**
40
+ * 값이 정확히 0일 때 입력칸을 빈칸으로 표시한다 (placeholder만 노출).
41
+ *
42
+ * 내부 값은 항상 숫자(0 포함)로 유지되므로 base-ui의 스피너 증감이 정상 동작한다.
43
+ * 표시(렌더링)만 비우며, `onValueChange`로는 숫자 0이 그대로 전달된다.
44
+ * 0을 빈칸으로 표시하려면 `clearWhenZero={true}`로 켠다.
45
+ * @default false
46
+ */
47
+ clearWhenZero?: boolean;
48
+ };
49
+ /** 다양한 입력(문자열·null 포함)을 NumberField 내부 숫자 값으로 정규화 */
50
+ export declare function normalizeNumberFieldValue(value: unknown): number | null;
51
+ /** default OutlinedInput과 동일한 입력 영역 높이 */
52
+ export declare function getNumberFieldControlHeight(size: "small" | "medium"): 40 | 56;
53
+ /**
54
+ * FormControl이 SSR에서 shrink label 상태를 올바르게 잡도록 하는 placeholder.
55
+ */
56
+ export declare function SSRInitialFilled(_: BaseNumberField.Root.Props): null;
57
+ export declare namespace SSRInitialFilled {
58
+ var muiName: string;
59
+ }
60
+ /**
61
+ * `form`/`name`이 주어졌을 때 폼 값과 양방향으로 연결한다.
62
+ * form이 있는 경우에만 렌더되는 하위 컴포넌트에서 무조건 호출되어야 한다
63
+ * (조건부 호출 금지). NumberField·NumberStepper·NumberSpinner 공통.
64
+ */
65
+ export declare function useNumberFormBinding({ form, name, onValueChange, }: {
66
+ form: FormLike;
67
+ name: string;
68
+ onValueChange?: (value: number | null, eventDetails: NumberFieldRoot.ChangeEventDetails) => void;
69
+ }): {
70
+ value: number | null;
71
+ onValueChange: (next: number | null, eventDetails: NumberFieldRoot.ChangeEventDetails) => void;
72
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * useNumberBasic.ts
3
+ *
4
+ * NumberField · NumberStepper · NumberSpinner 공용 동작 훅.
5
+ * 값 상태(controlled/uncontrolled) · min/max 즉시 clamp · 천 단위 live grouping ·
6
+ * format 정규화 · Base UI Root 배선에 필요한 값들을 한곳에서 만들어 돌려준다.
7
+ * 각 컴포넌트는 이 훅을 호출하고 "가운데 표시/입력 UI"만 다르게 렌더한다.
8
+ *
9
+ * clamp는 min/max가 주어졌을 때만, 폼 연동은 form이 주어졌을 때만 동작하므로
10
+ * 세 컴포넌트에 동일하게 적용해도 순수한 상위호환이다.
11
+ *
12
+ * @license MIT
13
+ * @copyright 2025 김영진 (Kim Young Jin)
14
+ * @author 김영진 (ehfuse@gmail.com)
15
+ */
16
+ import * as React from "react";
17
+ import type { NumberFieldRoot } from "@base-ui/react/number-field";
18
+ import { type ThousandSeparatorProp } from "../utils/number";
19
+ export interface UseNumberBasicParams {
20
+ value?: number | null;
21
+ defaultValue?: number | string | null;
22
+ onValueChange?: (value: number | null, eventDetails: NumberFieldRoot.ChangeEventDetails) => void;
23
+ min?: number;
24
+ max?: number;
25
+ locale?: Intl.LocalesArgument;
26
+ format?: Intl.NumberFormatOptions;
27
+ thousandSeparator?: ThousandSeparatorProp;
28
+ clearWhenZero?: boolean;
29
+ /**
30
+ * 초기값(defaultValue)이 없을 때 min(또는 0)으로 채울지 여부.
31
+ * 스테퍼처럼 항상 숫자 값이 있어야 증감이 자연스러운 경우 true.
32
+ * @default false
33
+ */
34
+ initialFallbackToMin?: boolean;
35
+ }
36
+ export declare function useNumberBasic({ value, defaultValue, onValueChange, min, max, locale, format, thousandSeparator, clearWhenZero, initialFallbackToMin, }: UseNumberBasicParams): {
37
+ isControlled: boolean;
38
+ resolvedValue: number | null;
39
+ setInternalValue: React.Dispatch<React.SetStateAction<number | null>>;
40
+ inputRef: React.RefObject<HTMLInputElement | null>;
41
+ liveGrouping: {
42
+ getDisplayValue: (baseInputValue: string) => string;
43
+ syncBaseInputValue: (baseInputValue: string) => void;
44
+ setLiveDisplay: (text: string | null) => void;
45
+ wrapOnChange: (handler: React.ChangeEventHandler<HTMLInputElement> | undefined) => (event: React.ChangeEvent<HTMLInputElement>) => void;
46
+ wrapOnBlur: (handler: React.FocusEventHandler<HTMLInputElement> | undefined) => (event: React.FocusEvent<HTMLInputElement, Element>) => void;
47
+ wrapOnFocus: (handler: React.FocusEventHandler<HTMLInputElement> | undefined) => (event: React.FocusEvent<HTMLInputElement, Element>) => void;
48
+ };
49
+ groupingEnabled: boolean;
50
+ handleValueChange: (next: number | null, details: NumberFieldRoot.ChangeEventDetails) => void;
51
+ formatForRoot: Intl.NumberFormatOptions | undefined;
52
+ };
package/dist/index.d.ts CHANGED
@@ -21,8 +21,8 @@ export { ButtonGroup } from "./ButtonGroup";
21
21
  export { DateRange } from "./DateRange";
22
22
  export { Slider } from "./Slider";
23
23
  export { Stepper } from "./Stepper";
24
- export { NumberField, NumberSpinner } from "./NumberField";
25
- export type { NumberFieldProps, NumberFieldVariant } from "./NumberField";
24
+ export { NumberField, NumberStepper, NumberSpinner } from "./NumberField";
25
+ export type { NumberFieldProps, NumberFieldAlign, NumberStepperProps, NumberSpinnerProps, } from "./NumberField";
26
26
  export { Autocomplete } from "./Autocomplete";
27
27
  export { Select } from "./Select";
28
28
  export { LabelSelect } from "./LabelSelect";