Skip to content

Commit ad3bf86

Browse files
committed
fix: number field
1 parent ac60f26 commit ad3bf86

12 files changed

+378
-48
lines changed

packages/react/.ladle/components.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ThemeProviderProps,
99
createThemes,
1010
} from "../src";
11+
import "./reset.css";
1112
import "../src/styles/global.css";
1213

1314
export const Provider: GlobalProvider = ({

packages/react/.ladle/reset.css

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
3+
1. Use a more-intuitive box-sizing model.
4+
5+
*/
6+
7+
*,
8+
*::before,
9+
*::after {
10+
11+
box-sizing: border-box;
12+
13+
}
14+
15+
/*
16+
17+
2. Remove default margin
18+
19+
*/
20+
21+
* {
22+
23+
margin: 0;
24+
25+
}
26+
27+
/*
28+
29+
Typographic tweaks!
30+
31+
3. Add accessible line-height
32+
33+
4. Improve text rendering
34+
35+
*/
36+
37+
body {
38+
39+
line-height: 1.5;
40+
41+
-webkit-font-smoothing: antialiased;
42+
43+
}
44+
45+
/*
46+
47+
5. Improve media defaults
48+
49+
*/
50+
51+
img,
52+
picture,
53+
video,
54+
canvas,
55+
svg {
56+
57+
display: block;
58+
59+
max-width: 100%;
60+
61+
}
62+
63+
/*
64+
65+
6. Remove built-in form typography styles
66+
67+
*/
68+
69+
input,
70+
button,
71+
textarea,
72+
select {
73+
74+
font: inherit;
75+
76+
}
77+
78+
/*
79+
80+
7. Avoid text overflows
81+
82+
*/
83+
84+
p,
85+
h1,
86+
h2,
87+
h3,
88+
h4,
89+
h5,
90+
h6 {
91+
92+
overflow-wrap: break-word;
93+
94+
}
95+
96+
/*
97+
98+
8. Create a root stacking context
99+
100+
*/
101+
102+
#root,
103+
#__next {
104+
105+
isolation: isolate;
106+
107+
}

packages/react/scaffolds/number-field/number-field.tsx

+79-27
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,11 @@ import { useNumberField, useLocale, AriaNumberFieldProps } from "react-aria";
44
import { mergeRefs } from "@react-aria/utils";
55

66
import clx from "clsx";
7-
import {
8-
inputStyles,
9-
inputSizes,
10-
inputIntent,
11-
inputRootIntent,
12-
rootInput,
13-
rootInputFocused,
14-
} from "@/ui/text-field/text-field.css";
7+
import * as textFieldStyles from "@/ui/text-field/text-field.css";
158
import FieldLabel from "@/ui/field-label";
169
import Stack from "@/ui/stack";
1710
import Box from "@/ui/box";
11+
import TextFieldAddon from "@/ui/text-field-addon";
1812
import useTheme from "../hooks/use-theme";
1913
import * as styles from "./number-field.css";
2014
import type { NumberInputProps } from "./number-field.types";
@@ -39,12 +33,18 @@ const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
3933
id = useId(),
4034
label,
4135
isDisabled,
42-
size = "sm",
43-
intent = "default",
36+
disabled,
37+
readOnly,
38+
isReadOnly,
39+
size,
40+
intent,
4441
clampValueOnBlur = true,
4542
formatOptions = defaultFormatOptions,
4643
} = props;
4744

45+
const isFinalDisabled = isDisabled ?? disabled ?? false;
46+
const isFinalReadOnly = isReadOnly ?? readOnly ?? false;
47+
4848
const { theme } = useTheme();
4949
const { locale } = useLocale();
5050
const [internalValue, setInternalValue] = useState<number | null>(
@@ -135,6 +135,7 @@ const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
135135

136136
// Default mode, fallback to react-aria logic
137137
if (clampValueOnBlur) {
138+
console.log("old mode");
138139
newValue = state.numberValue;
139140
applyFormatting(newValue);
140141
inputProps.onBlur?.(e);
@@ -201,6 +202,9 @@ const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
201202
}
202203
}, [state.inputValue, formatValue, internalValue, strValue]);
203204

205+
const hasStartAddon = props.canDecrement && props.decrementButton != null;
206+
const hasEndAddon = props.canIncrement && props.incrementButton != null;
207+
204208
return (
205209
<Box className={props.className} {...props.attributes}>
206210
<Stack direction="vertical" space="$4">
@@ -209,22 +213,51 @@ const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
209213
<div
210214
{...groupProps}
211215
className={clx(
212-
rootInput,
213-
isFocused ? rootInputFocused : null,
214-
props.isDisabled
215-
? inputRootIntent.disabled
216-
: inputRootIntent[props.intent],
216+
textFieldStyles.textField({
217+
intent: intent,
218+
size: size,
219+
theme: theme,
220+
}),
217221
props.inputContainer,
218222
)}
223+
tabIndex={props.disabled ? undefined : 0}
224+
data-element-type="input"
225+
data-intent={props.intent ?? "none"}
226+
data-state={
227+
isFocused ? "focused" : props.disabled ? "disabled" : "default"
228+
}
219229
>
220-
{props.canDecrement && React.isValidElement(props.decrementButton)
221-
? React.cloneElement(props.decrementButton, decrementButtonProps)
222-
: props?.decrementButton}
230+
{hasStartAddon && (
231+
<TextFieldAddon
232+
position="start"
233+
divider={props.addonDivider ?? true}
234+
intent={props.intent}
235+
disabled={props.disabled}
236+
>
237+
{hasStartAddon && React.isValidElement(props.decrementButton)
238+
? React.cloneElement(
239+
props.decrementButton,
240+
decrementButtonProps,
241+
)
242+
: props?.decrementButton}
243+
</TextFieldAddon>
244+
)}
223245

224246
<Box
225247
as="input"
226248
attributes={{
227249
...inputProps,
250+
"data-state": isFinalReadOnly
251+
? "readonly"
252+
: isFocused
253+
? "focused"
254+
: isFinalDisabled
255+
? "disabled"
256+
: "default",
257+
"data-intent": props.intent ?? "none",
258+
"data-theme": theme,
259+
readOnly: isFinalReadOnly,
260+
disabled: isFinalDisabled,
228261
value: inputValue,
229262
onChange: clampValueOnBlur ? inputProps.onChange : handleChange,
230263
onBlur: clampValueOnBlur ? inputProps.onBlur : handleBlur,
@@ -233,23 +266,42 @@ const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
233266
textAlign={props.textAlign}
234267
fontSize={props.fontSize}
235268
className={clx(
236-
inputStyles[theme],
237-
inputSizes[size],
238-
props.isDisabled ? inputIntent.disabled : inputIntent[intent],
239-
props.inputClassName,
240-
isDisabled && props.decrementButton
269+
textFieldStyles.input,
270+
isFinalDisabled && props.decrementButton
241271
? styles.withDecrementButton
242272
: null,
243-
isDisabled && props.incrementButton
273+
isFinalDisabled && props.incrementButton
244274
? styles.withIncrementButton
245275
: null,
246276
props.borderless ? styles.borderless : null,
277+
props.inputClassName,
247278
)}
248279
/>
249280

250-
{props.canIncrement && React.isValidElement(props.incrementButton)
251-
? React.cloneElement(props.incrementButton, incrementButtonProps)
252-
: props?.incrementButton}
281+
<div
282+
className={textFieldStyles.borderElement}
283+
data-theme={theme}
284+
data-intent={props.intent ?? "none"}
285+
data-state={
286+
isFocused ? "focused" : props.disabled ? "disabled" : "default"
287+
}
288+
/>
289+
290+
{hasEndAddon && (
291+
<TextFieldAddon
292+
position="end"
293+
divider={props.addonDivider ?? true}
294+
intent={props.intent}
295+
disabled={props.disabled}
296+
>
297+
{hasEndAddon && React.isValidElement(props.incrementButton)
298+
? React.cloneElement(
299+
props.incrementButton,
300+
incrementButtonProps,
301+
)
302+
: props?.incrementButton}
303+
</TextFieldAddon>
304+
)}
253305
</div>
254306
</Stack>
255307
</Box>

packages/react/scaffolds/number-field/number-field.types.tsx

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { ReactNode } from "react";
22
import type { Sprinkles } from "@/styles/rainbow-sprinkles.css";
33
import type { AriaNumberFieldProps } from "react-aria";
4+
import { TextFieldProps } from "@/ui/text-field/text-field.types";
5+
6+
export type NumberFieldSize = TextFieldProps["size"];
7+
export type NumberFieldIntent = TextFieldProps["intent"];
8+
export type NumberFieldTextAlignment = Sprinkles["textAlign"];
49

510
export interface NumberInputProps {
611
// ==== Core logic props
@@ -16,7 +21,9 @@ export interface NumberInputProps {
1621
// ==== Form field props
1722
id?: string;
1823
isDisabled?: boolean;
24+
disabled?: boolean;
1925
isReadOnly?: boolean;
26+
readOnly?: boolean;
2027
autoFocus?: boolean;
2128
formatOptions?: AriaNumberFieldProps["formatOptions"];
2229
name?: string;
@@ -29,14 +36,15 @@ export interface NumberInputProps {
2936
onKeyDown?: AriaNumberFieldProps["onKeyDown"];
3037
onKeyUp?: AriaNumberFieldProps["onKeyUp"];
3138
// ==== Style props
32-
textAlign?: Sprinkles["textAlign"];
39+
textAlign?: NumberFieldTextAlignment;
3340
fontSize?: Sprinkles["fontSize"];
3441
attributes?: Sprinkles & React.HTMLAttributes<HTMLDivElement>;
35-
size?: "sm" | "md" | "lg";
42+
size?: NumberFieldSize;
3643
placeholder?: string | undefined;
37-
intent?: "default" | "error";
44+
intent?: NumberFieldIntent;
3845
className?: string;
3946
inputContainer?: string;
4047
inputClassName?: string;
4148
borderless?: boolean;
49+
addonDivider?: boolean;
4250
}

0 commit comments

Comments
 (0)