Skip to content

Commit

Permalink
Merge pull request #77 from Eureka-Shoulders/feat/negative-numeric-input
Browse files Browse the repository at this point in the history
feat: negative numeric input
  • Loading branch information
Sampaio Leal committed Jun 9, 2022
2 parents 66224ca + d386e38 commit 8c95d63
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
node-version: ${{ matrix.node }}

- name: Install
run: yarn install && yarn add @mui/material @mui/icons-material @mui/lab @emotion/react @emotion/styled mobx mobx-react-lite
run: yarn install

- name: Lint
run: yarn lint
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@euk-labs/componentz",
"version": "0.5.3",
"version": "0.5.4",
"main": "./cjs/index.js",
"module": "./index.js",
"types": "./index.d.ts",
Expand Down
9 changes: 8 additions & 1 deletion src/components/Inputs/NumericField/NumericField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ import NumericField from '.';
export default {
title: 'NumericField',
component: NumericField,
args: {
decimalChar: ',',
thousandChar: '.',
precision: 1,
negative: false,
},
} as ComponentMeta<typeof NumericField>;

const Template: ComponentStory<typeof NumericField> = (args) => {
const [value, setValue] = useState<number | string>('');
const [value, setValue] = useState<number | string>(0);

return (
<NumericField
{...args}
Expand Down
89 changes: 63 additions & 26 deletions src/components/Inputs/NumericField/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { TextFieldProps } from '@mui/material';
import { TextField } from '@mui/material';
import { useMemo } from 'react';
import { useEffect, useMemo, useState } from 'react';

export interface HTMLNumericElement
extends Omit<HTMLInputElement, 'value' | 'name'> {
Expand All @@ -11,6 +11,7 @@ export interface HTMLNumericElement
export type NumericInputProps = Omit<TextFieldProps, 'onChange'> & {
value?: number | string;
onChange?(e: React.ChangeEvent<HTMLNumericElement>): void;
negative?: boolean;

precision: number;
thousandChar: string;
Expand All @@ -22,17 +23,23 @@ function verifyNumber(string: string) {

return {
isNumber: !isNaN(Number(numericRepresentation)),
numberFomart: !isNaN(Number(numericRepresentation))
numberFormat: !isNaN(Number(numericRepresentation))
? Number(numericRepresentation)
: null,
};
}

// TODO: support negative numbers
function NumericField(props: NumericInputProps) {
const { value, precision, thousandChar, decimalChar, ...inputProps } = props;
const {
value,
precision = 1,
thousandChar = '.',
decimalChar = ',',
negative,
...inputProps
} = props;
const defaultValue = value === '' ? NaN : Number(value);

const [isNegative, setIsNegative] = useState(false);
const formatter = useMemo(
() =>
new Intl.NumberFormat('pt-BR', {
Expand All @@ -50,6 +57,31 @@ function NumericField(props: NumericInputProps) {
throw new Error('Thousand char should not be an empty string!');
}

const hasValue = value !== undefined;
let inputDefaultValue;
let inputValue;

if (hasValue) {
if (isNaN(defaultValue) || value === '') {
inputValue = '';
} else {
inputValue = format(defaultValue);
}
}

if (!hasValue && !isNaN(defaultValue)) {
inputDefaultValue = format(defaultValue);
}

if (isNegative) {
if (inputDefaultValue && !inputDefaultValue.includes('-')) {
inputDefaultValue = `-${inputDefaultValue}`;
}
if (inputValue && !inputValue.includes('-')) {
inputValue = `-${inputValue}`;
}
}

function format(number: number) {
const result = formatter
.format(number)
Expand All @@ -60,8 +92,6 @@ function NumericField(props: NumericInputProps) {
}

function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>): void {
if (e.key === ' ') e.preventDefault();

if (
e.ctrlKey ||
e.shiftKey ||
Expand All @@ -71,9 +101,22 @@ function NumericField(props: NumericInputProps) {
e.key === 'ArrowRight' ||
e.key === 'ArrowLeft' ||
e.key === 'Delete'
)
) {
return;
if (!verifyNumber(e.key).isNumber) e.preventDefault();
}

if (e.key === ' ') {
return e.preventDefault();
}

if (e.key === '-' && negative) {
setIsNegative((prev) => !prev);
return e.preventDefault();
}

if (!verifyNumber(e.key).isNumber) {
return e.preventDefault();
}
}

function handleChange(e: React.ChangeEvent<HTMLInputElement>): void {
Expand Down Expand Up @@ -102,12 +145,16 @@ function NumericField(props: NumericInputProps) {
return props.onChange && props.onChange(newEvent);
}

const { isNumber, numberFomart } = verifyNumber(numericRepresentation);
const { isNumber, numberFormat } = verifyNumber(numericRepresentation);

if (isNumber && numberFomart !== null && numberFomart >= 0) {
const withPrecision = numberFomart / 10 ** precision;
if (isNumber && numberFormat !== null) {
let withPrecision = numberFormat / 10 ** precision;
const formattedNumber = format(withPrecision);

if (withPrecision > 0 && isNegative) {
withPrecision = withPrecision * -1;
}

newEvent.target.value = withPrecision;
newEvent.currentTarget.value = withPrecision;

Expand All @@ -117,21 +164,11 @@ function NumericField(props: NumericInputProps) {
}
}

const hasValue = value !== undefined;
let inputDefaultValue;
let inputValue;

if (hasValue) {
if (isNaN(defaultValue) || value === '') {
inputValue = '';
} else {
inputValue = format(defaultValue);
useEffect(() => {
if (!negative) {
setIsNegative(false);
}
}

if (!hasValue && !isNaN(defaultValue)) {
inputDefaultValue = format(defaultValue);
}
}, [negative]);

return (
<TextField
Expand Down

0 comments on commit 8c95d63

Please sign in to comment.