Open m4theushw opened 4 years ago
It would need to support floating point numbers in addition to integers. With the ability to set precision ranges.
My project's current solution was to wrap TextField.
Is adding generic mask property for TextField sufficient? That is more generic....as it ensures userland can define the content for their needs...phone numbers, bank account numbers and of course floats, integers. As these are common...const regex patterns provide by mui would be nice.
It would need to support floating point numbers in addition to integers.
@dcworldwide I don't know, step buttons are not very useful with continuous quantities.
Is adding generic mask property for TextField sufficient?
For phone numbers, bank account numbers or currency, masks solve the problem. But to enforce a minimum/maximum value or a custom step (0, 2, 4, 6) regex patterns become huge.
Maybe I need redefine what kind of numeric input I am talking. I see an opportunity for an integer field and a floating-point field.
I'm thinking about starting a draft for NumericField
. To support currency values should we (1) use Intl.NumberFormat, (2) extend the Material-UI's localization API or (3) delegate all formating to user?
i would vote to start with (3)
Hey Why when I copy and paste exactly this from the TextField doc it does not work ? <TextField inputProps={{ inputMode: 'numeric', pattern: '[0-9]*' }} /> Daniel
@sscotth Your code sandbox also does not work Chrome Firefox
This is a little bit offtopic, but the documentation leads to this page. So, this information might be helpful for someone.
From the examples with the slider, I found the following way to add the Input element with the Number
type:
import MuiInput from '@mui/material/Input';
function NumberComponent(props){
// if you want to use state, uncomment this:
// let { numberValue } = props || 1;
// if you want to use state, uncomment this:
// const [numberValue, handleDurationChange] = useState(numberProperty);
return (
<MuiInput
value={numberValue}
size="small"
inputProps={{
step: 1,
min: 0,
max: 99999,
type: 'number',
}}
/>
)
}
I wanted to chime and say that, from a product designer's perspective, an incrementer would be very useful to have in Material UI. The software I use on a daily basis (Figma, Adobe products, Cinema 4D) uses this kind of field all the time, and because I work on apps at my company which involve similar visualization and manipulation of values, I want to be able to provide this kind of a control to our users. Pasting a small mockup of the sorts of things we would want it do in our case.
So, I'm mainly just chiming in here to say this would be a really valuable to provide in Material UI, and it could help product designers like me advocate more strongly for Material UI in our various spheres of influence. Cheers.
any update on this component?
Any update here?
Great, thank you!
@siriwatknp Could you link to the source for yours?
Demo: https://next.mui-treasury.com/?path=/story/component-numberinput--default Component src: https://github.com/siriwatknp/mui-treasury/blob/next/packages/component-numberinput/src/NumberInput.tsx useNumberInput hook: https://github.com/siriwatknp/mui-treasury/blob/next/packages/use-number-input/src/useNumberInput.ts
Ah, that's why I couldn't find it :)
Thanks!
Just to make sure: please don't overlook a step
parameter in the implementation. And please make it possible to define a set of valid fractions (static and by callback). I have number
fields implemented in a tool we use internally, where only a defined set of fractions are OK. Say you press the "up" button and you would only want to got from x.33
to x.5
, then there should be some kind of option to define those valid steps.
In case it helps, my Order ID is: :credit_card: 47016
Not sure why Sam is asking for interviews to ask what components users would like in v6 when we already have a concrete list based on issue upvotes; and even better, an existing implementation of the most popular request. Nor does it need to wait for v6 - an addition isn't a breaking change. Lab/refine/publish...
If possible, I would "urge" this to happen soon. Why?
Well, I have found myself several times accidentally changing the focused number input while scrolling through the touchpad towards the end of the page to hit the SAVE button. Due to the stepper...
See the result here, this was entered as 24500, I then scrolled down to hit Save and I saved the form, see what happened to the input (I re-entered to the form afterwards):
So, this can be done by many users too and they will not even notice! (I noticed this accidentally too)
Thanks!
If possible, I would "urge" this to happen soon. Why?
Well, I have found myself several times accidentally changing the focused number input while scrolling through the touchpad towards the end of the page to hit the SAVE button. Due to the stepper...
We recently had the same bug in our application which took a while to get noticed since it is very subtle. Our workaround for now is something like this:
export const blurTarget = (event: SyntheticEvent) => {
event.target instanceof HTMLElement && event.target.blur()
}
<TextField
size="small"
type="number"
onWheel={blurTarget}
My company would also like to see this implemented as soon as possible :)
Thanks
Any progress on this?
Any progress on this?
We plan to start working on this in this quarter.
@mj12albert I think that it could be great to spend your first couple of weeks on smaller changes than this issue. There are probably two different NumberInput
components to build here. My idea is that with smaller changes, you could maybe learn the codebase, workflows, and build confidence faster.
Hey guys!
I don't know if it has much to do with the topic of discussion, but I would like to ask a question! If I can't send it here, let me know and I'll remove it! And if possible, point me to the correct place to take this doubt.
In a system I'm building, I ask the user for the value in Brazilian Reais (R$ 00,00 format). This week, I was told that the input is not working on Samsung and OnePlus phones (I suspect it is on any phone with a Chromium-based browser). On my MacBook and Iphone, the input works perfectly.
Can anyone help me, please? Links: https://github.com/thiagorochatr/teste-input-value/blob/main/components/original/InputPriceOG.tsx https://teste-input-value.vercel.app/original
Hey guys!
I don't know if it has much to do with the topic of discussion, but I would like to ask a question! If I can't send it here, let me know and I'll remove it! And if possible, point me to the correct place to take this doubt.
In a system I'm building, I ask the user for the value in Brazilian Reais (R$ 00,00 format). This week, I was told that the input is not working on Samsung and OnePlus phones (I suspect it is on any phone with a Chromium-based browser). On my MacBook and Iphone, the input works perfectly.
Can anyone help me, please? Links: https://github.com/thiagorochatr/teste-input-value/blob/main/components/original/InputPriceOG.tsx https://teste-input-value.vercel.app/original
Update: fixed!
Demo: https://next.mui-treasury.com/?path=/story/component-numberinput--default Component src: https://github.com/siriwatknp/mui-treasury/blob/next/packages/component-numberinput/src/NumberInput.tsx useNumberInput hook: https://github.com/siriwatknp/mui-treasury/blob/next/packages/use-number-input/src/useNumberInput.ts
You can still use + and other math operators
Current example solution With Keydown preventing changes based on criteria.
// Solution with keydown
<TextField
value={state.value}
type="number"
onKeyDown={(e) => {
if (e.key === "e" || e.key === "E" || e.key === "-" || e.key === "+") {
e.preventDefault()
}
}}
onChange={(e) => {
handleChange();
}}
/>
found this stack over flow helpful https://stackoverflow.com/questions/31706611/why-does-the-html-input-with-type-number-allow-the-letter-e-to-be-entered-in
✨ We've released a first iteration on the Number Input for Base UI in v5.14.4
!
The docs: https://mui.com/base-ui/react-number-input/.
✨ We've released a first iteration on the Number Input for Base UI in
v5.14.4
!
Great to hear! Will this be ported to Material UI core itself at some point? Thanks!
Adding a reminder to update: https://mui.com/material-ui/react-text-field/#type-quot-number-quot when this is released.
Here is my implementation of NumberInput with support for decimal numbers: https://gist.github.com/nameer/664abb9d4e261a210de1f1ba814a0ad2
To use it for integers, simply set decimalScale
to 0.
One thing about existing number inputs I've always found annoying is the half-sized up
/down
arrows - they are often too small to be usable and I personally would much prefer something like 123
+
-
- normal text field followed by icon buttons.
This way the buttons have enough vertical space to get to usable size
Here is my implementation of NumberInput with support for decimal numbers: https://gist.github.com/nameer/664abb9d4e261a210de1f1ba814a0ad2
To use it for integers, simply set
decimalScale
to 0.
A live implementation is here: https://codesandbox.io/p/sandbox/numberinput-7fxlym
This component supports setting minimum, maximum, and step values, and also handles decimal numbers. It allows you to provide an initial number, and you can monitor changes via the onChange
prop. onChange
event will only be triggered if the value is a valid number (typing the dot of "5." will not trigger it).
Is there going to be any support for adding labels to number input fields?
It seems to not work with react-hook-form
.
https://codesandbox.io/p/sandbox/epic-monad-jrtnc2
Am I missing something?
Hello, is there any news about this new component?
Thank you 😄
Super duper excited for this component by the way can't wait 😉
Hi everyone,
I've been working on such a component for a little while now and I thought the MUI users could benefit from it. It is inspired from the integration with 3rd party input libraries section of the docs. It uses react-number-format under the hood and supports decimal numbers, increment and decrement with buttons or ArrowUp
/ArrowDown
, custom step, thousand and decimal separators based on the current locale.
That's how it looks:
And here is the code:
import * as React from "react";
import { NumericFormat, type NumericFormatProps } from "react-number-format";
import { useLocale, useTranslations } from "next-intl";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
import {
IconButton,
InputAdornment,
Stack,
TextField,
type IconButtonProps,
type TextFieldProps,
} from "@mui/material";
const NumericFormatCustom = React.forwardRef<
HTMLInputElement,
NumericFormatProps
>(function NumericFormatCustom(props, ref) {
const { decimalSeparator, thousandSeparator, value, ...rest } = props;
const locale = useLocale();
const defaultThousandSeparator = React.useMemo(
() => getThousandSeparator(locale),
[locale],
);
const defaultDecimalSeparator = React.useMemo(
() => getDecimalSeparator(locale),
[locale],
);
return (
<NumericFormat
{...rest}
value={value ?? ""} // We can't ever pass null to value because it breaks the shrink state of the label, so we pass empty string instead
getInputRef={ref}
thousandSeparator={thousandSeparator ?? defaultThousandSeparator}
decimalSeparator={decimalSeparator ?? defaultDecimalSeparator}
/>
);
});
type NumberInputProps = Omit<TextFieldProps, "onChange"> & {
hideActionButtons?: boolean;
max?: number;
min?: number;
numericFormatProps?: NumericFormatProps;
onChange?: (value: number | null) => void;
step?: number;
value?: number | null;
};
const NumberInput = React.forwardRef<HTMLDivElement, NumberInputProps>(
function NumberInput(props, ref) {
const t = useTranslations("NumberInput");
const {
disabled = false,
hideActionButtons = false,
max = Infinity,
min = -Infinity,
numericFormatProps: numericFormatPropsProp,
onChange,
size,
slotProps,
step = 1,
value: valueProp,
...rest
} = props;
const isControlled = valueProp !== undefined && onChange !== undefined;
// We use an internal state when the component is uncontrolled
const [fallbackValue, setFallbackValue] = React.useState(valueProp);
const value = isControlled ? valueProp : fallbackValue;
const setValue = isControlled ? onChange : setFallbackValue;
const increment = () => {
// If we increment when the input is empty, we consider the previous value to be 0
const newValue =
(value != null && !Number.isNaN(value) ? value : 0) + step;
if (newValue > max) {
return;
}
setValue(newValue);
};
const decrement = () => {
// If we decrement when the input is empty, we consider the previous value to be 0
const newValue =
(value != null && !Number.isNaN(value) ? value : 0) - step;
if (newValue < min) {
return;
}
setValue(newValue);
};
const numericFormatProps: NumericFormatProps = {
// We set a default to avoid displaying floating point errors when using a decimal step
decimalScale: 5,
// react-number-format doesn't provide min or max props so we do the checking here instead
isAllowed: ({ floatValue }) => {
if (floatValue == null) {
return true;
}
return floatValue >= min && floatValue <= max;
},
// Only add the min, max and step html attributes when the value isn't the default one
max: max !== Infinity ? max : undefined,
min: min !== -Infinity ? min : undefined,
step: step !== 1 ? step : undefined,
// Allow to increment with ArrowUp and decrement with ArrowDown
onKeyDown: (event) => {
if (event.key === "ArrowUp") {
increment();
} else if (event.key === "ArrowDown") {
decrement();
}
},
onValueChange: ({ floatValue }) => {
// When incrementing or decrementing, the value prop is already up to date
// so we make sure the value needs to be updated to prevent an unnecessary re-render
if (floatValue === value) {
return;
}
setValue(floatValue ?? null);
},
value,
...numericFormatPropsProp,
};
const commonAdornmentButtonProps: IconButtonProps = {
edge: "end",
sx: { p: size !== "small" ? "1px" : 0 },
};
return (
<TextField
{...rest}
ref={ref}
value={value ?? ""} // We can't ever pass null to value because it breaks the shrink state of the label, so we pass empty string instead
disabled={disabled}
size={size}
slotProps={{
...slotProps,
input: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
inputComponent: NumericFormatCustom as any,
endAdornment: !hideActionButtons && (
<InputAdornment position="end">
<Stack>
<IconButton
aria-label={t("incrementAriaLabel")}
disabled={disabled || (value ?? 0) + step > max}
onClick={increment}
{...commonAdornmentButtonProps}
>
<KeyboardArrowUpIcon fontSize={size} />
</IconButton>
<IconButton
aria-label={t("decrementAriaLabel")}
disabled={disabled || (value ?? 0) - step < min}
onClick={decrement}
{...commonAdornmentButtonProps}
>
<KeyboardArrowDownIcon fontSize={size} />
</IconButton>
</Stack>
</InputAdornment>
),
...slotProps?.input,
},
// @ts-expect-error The type should be React.ComponentProps<typeof inputComponent> but instead
// it is hard-coded to InputBaseComponentProps
htmlInput: { ...slotProps?.htmlInput, ...numericFormatProps },
}}
/>
);
},
);
// Taken from here:
// https://stackoverflow.com/questions/32054025/how-to-determine-thousands-separator-in-javascript#comments-77517574
function getThousandSeparator(locale: string) {
return (
new Intl.NumberFormat(locale)
.formatToParts(1234.5678)
.find((part) => part.type === "group")?.value ?? ""
);
}
function getDecimalSeparator(locale: string) {
return (
new Intl.NumberFormat(locale)
.formatToParts(1234.5678)
.find((part) => part.type === "decimal")?.value ?? "."
);
}
export default NumberInput;
You can hard code the aria-labels and just pass undefined
to getThousandSeparator
and getDecimalSeparator
if your app doesn't support internationalization.
Hope it helps!
Edit: Replaced InputProps
with slotProps.input
and inputProps
with slotProps.htmlInput
as they became deprecated in MUI v6.
Hello, is there any news about porting Number Input from Base IU to material UI? Thank you 🙇
I would love to see Number Input component implemented in MUI similar to Ant Design and BlueprintJs. It should support +, -
in the input, and floating point number. The step size should also support floating point number. There are few other properties that are missing. A combination of below would be awesome.
For me this is completely fundamental and I'm not sure i understand why it doesnt exist. the number of bugs we get due to numbers being strings due to being forced to use a textfield for entering numbers...
@ClementDreptin Thanks a lot, was struggling implementing this, by MUI recommended, NumberInput with React Hook Form from https://mui.com/base-ui/react-number-input/#hook
No success, yours works instantly and perfectly.
I would love to see Number Input component implemented in MUI similar to Ant Design and BlueprintJs. It should support
+, -
in the input, and floating point number. The step size should also support floating point number. There are few other properties that are missing. A combination of below would be awesome.
You can get the components from MUI Treasury:
https://github.com/user-attachments/assets/0f911c9e-670a-48a5-96b2-28aa72ed731e
I noticed in the Material-UI's roadmap a Numeric Input component to be built. If nobody has took the lead then I can help. Recently I had to develop a component like that for a personal project (first screenshot below).
Summary 💡
I didn't look deeply but I think about a
<NumericField />
wrapping a<TextField />
. This text field component would render the step buttons.Examples 🌈
Motivation 🔦
Sometimes we want to force an
input
to only accept numbers, but the support fortype="number"
has limitations:max
is set: https://github.com/mui-org/material-ui/issues/9313#issuecomment-731267593. It should likely round to the max when blurring the field.e
, #10582.TODO
Benchmarks
https://mui-org.notion.site/Input-Number-component-364825a7bec94381809ac11ff05b4cc0