mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 21:00:31 +00:00
re-structure to monorepo.
This commit is contained in:
27
packages/webapp/src/components/Forms/BlueprintFormik.tsx
Normal file
27
packages/webapp/src/components/Forms/BlueprintFormik.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
// @ts-nocheck
|
||||
import {
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
NumericInput,
|
||||
Checkbox,
|
||||
RadioGroup,
|
||||
Switch,
|
||||
EditableText,
|
||||
TextArea,
|
||||
} from '@blueprintjs-formik/core';
|
||||
import { Select, MultiSelect } from '@blueprintjs-formik/select';
|
||||
import { DateInput } from '@blueprintjs-formik/datetime';
|
||||
|
||||
export {
|
||||
FormGroup as FFormGroup,
|
||||
InputGroup as FInputGroup,
|
||||
NumericInput as FNumericInput,
|
||||
Checkbox as FCheckbox,
|
||||
RadioGroup as FRadioGroup,
|
||||
Switch as FSwitch,
|
||||
Select as FSelect,
|
||||
MultiSelect as FMultiSelect,
|
||||
EditableText as FEditableText,
|
||||
TextArea as FTextArea,
|
||||
DateInput as FDateInput,
|
||||
};
|
||||
28
packages/webapp/src/components/Forms/Checkbox.tsx
Normal file
28
packages/webapp/src/components/Forms/Checkbox.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
// @ts-nocheck
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Checkbox as BPCheckbox,
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
export default function CheckboxComponent(props: any) {
|
||||
const { field, form, ...rest } = props;
|
||||
const [value, setValue] = useState(field.value || false);
|
||||
|
||||
const handleChange = () => {
|
||||
const checked = !value;
|
||||
form.setFieldValue(field.name, checked);
|
||||
setValue(checked);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
form.setFieldTouched(field.name);
|
||||
};
|
||||
|
||||
const checkboxProps = {
|
||||
...rest,
|
||||
onChange: handleChange,
|
||||
onBlur: handleBlur,
|
||||
checked: value,
|
||||
}
|
||||
return <BPCheckbox {...checkboxProps} />;
|
||||
}
|
||||
37
packages/webapp/src/components/Forms/FMoneyInputGroup.tsx
Normal file
37
packages/webapp/src/components/Forms/FMoneyInputGroup.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { Field, getIn } from 'formik';
|
||||
import { CurrencyInput } from './MoneyInputGroup';
|
||||
|
||||
const fieldToMoneyInputGroup = ({
|
||||
field: { onBlur: onFieldBlur, ...field },
|
||||
form: { setFieldValue, touched, errors },
|
||||
onBlur,
|
||||
...props
|
||||
}) => {
|
||||
const fieldError = getIn(errors, field.name);
|
||||
const showError = getIn(touched, field.name) && !!fieldError;
|
||||
|
||||
return {
|
||||
intent: showError ? Intent.DANGER : Intent.NONE,
|
||||
onBlurValue:
|
||||
onBlur ??
|
||||
function (e) {
|
||||
onFieldBlur(e ?? field.name);
|
||||
},
|
||||
...field,
|
||||
onChange: (value) => {
|
||||
setFieldValue(field.name, value);
|
||||
},
|
||||
...props,
|
||||
};
|
||||
};
|
||||
|
||||
function FieldToMoneyInputGroup({ ...props }) {
|
||||
return <CurrencyInput {...fieldToMoneyInputGroup(props)} />;
|
||||
}
|
||||
|
||||
export function FMoneyInputGroup({ ...props }) {
|
||||
return <Field {...props} component={FieldToMoneyInputGroup} />;
|
||||
}
|
||||
15
packages/webapp/src/components/Forms/FormObserver.tsx
Normal file
15
packages/webapp/src/components/Forms/FormObserver.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export function FormObserver({ onChange, values }) {
|
||||
useEffect(() => {
|
||||
onChange(values);
|
||||
}, [Object.values(values).join(', ')]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
FormObserver.defaultProps = {
|
||||
onChange: () => null,
|
||||
};
|
||||
17
packages/webapp/src/components/Forms/FormikObserver.tsx
Normal file
17
packages/webapp/src/components/Forms/FormikObserver.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
// @ts-nocheck
|
||||
import { useFormikContext } from 'formik';
|
||||
import { useDeepCompareEffect } from '@/hooks/utils';
|
||||
|
||||
export function FormikObserver({ onChange }) {
|
||||
const { values } = useFormikContext();
|
||||
|
||||
useDeepCompareEffect(() => {
|
||||
onChange(values);
|
||||
}, [values]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
FormikObserver.defaultProps = {
|
||||
onChange: () => null,
|
||||
};
|
||||
10
packages/webapp/src/components/Forms/InputPrepend.tsx
Normal file
10
packages/webapp/src/components/Forms/InputPrepend.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
export default function InputPrepend({ children }) {
|
||||
return (
|
||||
<div class="input-prepend">
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
31
packages/webapp/src/components/Forms/InputPrependButton.tsx
Normal file
31
packages/webapp/src/components/Forms/InputPrependButton.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
// @ts-nocheck
|
||||
import React, { useMemo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Button, Tooltip, Classes } from '@blueprintjs/core';
|
||||
|
||||
export function InputPrependButton({
|
||||
buttonProps = {},
|
||||
tooltip = false,
|
||||
tooltipProps = {},
|
||||
}) {
|
||||
const appendButton = useMemo(
|
||||
() => (
|
||||
<Button
|
||||
className={classNames('input-prepend__button', Classes.SMALL)}
|
||||
{...buttonProps}
|
||||
/>
|
||||
),
|
||||
[buttonProps],
|
||||
);
|
||||
|
||||
const appendButtonWithTooltip = useMemo(
|
||||
() => <Tooltip {...tooltipProps}>{appendButton}</Tooltip>,
|
||||
[tooltipProps, appendButton],
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="input-prepend">
|
||||
{tooltip ? appendButtonWithTooltip : appendButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
10
packages/webapp/src/components/Forms/InputPrependText.tsx
Normal file
10
packages/webapp/src/components/Forms/InputPrependText.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
export function InputPrependText({ text, children }) {
|
||||
return (
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">{text}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// @ts-nocheck
|
||||
type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;
|
||||
|
||||
export type Separator = ',' | '.';
|
||||
|
||||
export type CurrencyInputProps = Overwrite<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
{
|
||||
/**
|
||||
* Allow decimals
|
||||
*
|
||||
* Default = true
|
||||
*/
|
||||
allowDecimals?: boolean;
|
||||
|
||||
/**
|
||||
* Allow user to enter negative value
|
||||
*
|
||||
* Default = true
|
||||
*/
|
||||
allowNegativeValue?: boolean;
|
||||
|
||||
/**
|
||||
* Component id
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* Maximum characters the user can enter
|
||||
*/
|
||||
maxLength?: number;
|
||||
|
||||
/**
|
||||
* Class names
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* Limit length of decimals allowed
|
||||
*
|
||||
* Default = 2
|
||||
*/
|
||||
decimalsLimit?: number;
|
||||
|
||||
/**
|
||||
* Default value
|
||||
*/
|
||||
defaultValue?: number | string;
|
||||
|
||||
/**
|
||||
* Disabled
|
||||
*
|
||||
* Default = false
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Value will always have the specified length of decimals
|
||||
*/
|
||||
fixedDecimalLength?: number;
|
||||
|
||||
/**
|
||||
* Handle change in value
|
||||
*/
|
||||
onChange?: (value: string | undefined, name?: string) => void;
|
||||
|
||||
/**
|
||||
* Handle value onBlur
|
||||
*
|
||||
*/
|
||||
onBlurValue?: (value: string | undefined, name?: string) => void;
|
||||
|
||||
/**
|
||||
* Placeholder
|
||||
*/
|
||||
placeholder?: string;
|
||||
|
||||
/**
|
||||
* Specify decimal precision for padding/trimming
|
||||
*/
|
||||
precision?: number;
|
||||
|
||||
/**
|
||||
* Include a prefix eg. £
|
||||
*/
|
||||
prefix?: string;
|
||||
|
||||
/**
|
||||
* Incremental value change on arrow down and arrow up key press
|
||||
*/
|
||||
step?: number;
|
||||
|
||||
/**
|
||||
* Separator between integer part and fractional part of value. Cannot be a number
|
||||
*
|
||||
* Default = "."
|
||||
*/
|
||||
decimalSeparator?: string;
|
||||
|
||||
/**
|
||||
* Separator between thousand, million and billion. Cannot be a number
|
||||
*
|
||||
* Default = ","
|
||||
*/
|
||||
groupSeparator?: string;
|
||||
|
||||
/**
|
||||
* Disable auto adding separator between values eg. 1000 > 1,000
|
||||
*
|
||||
* Default = false
|
||||
*/
|
||||
turnOffSeparators?: boolean;
|
||||
|
||||
/**
|
||||
* Disable abbreviations eg. 1k > 1,000, 2m > 2,000,000
|
||||
*
|
||||
* Default = false
|
||||
*/
|
||||
turnOffAbbreviations?: boolean;
|
||||
}
|
||||
>;
|
||||
213
packages/webapp/src/components/Forms/MoneyInputGroup/index.tsx
Normal file
213
packages/webapp/src/components/Forms/MoneyInputGroup/index.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
// @ts-nocheck
|
||||
import React, { FC, useState, useEffect, useRef } from 'react';
|
||||
import { InputGroup } from '@blueprintjs/core';
|
||||
import { CurrencyInputProps } from './CurrencyInputProps';
|
||||
import {
|
||||
isNumber,
|
||||
cleanValue,
|
||||
fixedDecimalValue,
|
||||
formatValue,
|
||||
padTrimValue,
|
||||
CleanValueOptions,
|
||||
} from './utils';
|
||||
|
||||
export const CurrencyInput: FC<CurrencyInputProps> = ({
|
||||
allowDecimals = true,
|
||||
allowNegativeValue = true,
|
||||
id,
|
||||
name,
|
||||
className,
|
||||
decimalsLimit,
|
||||
defaultValue,
|
||||
disabled = false,
|
||||
maxLength: userMaxLength,
|
||||
value: userValue,
|
||||
onChange,
|
||||
onBlurValue,
|
||||
fixedDecimalLength,
|
||||
placeholder,
|
||||
precision,
|
||||
prefix,
|
||||
step,
|
||||
decimalSeparator = '.',
|
||||
groupSeparator = ',',
|
||||
turnOffSeparators = false,
|
||||
turnOffAbbreviations = false,
|
||||
...props
|
||||
}: CurrencyInputProps) => {
|
||||
if (decimalSeparator === groupSeparator) {
|
||||
throw new Error('decimalSeparator cannot be the same as groupSeparator');
|
||||
}
|
||||
|
||||
if (isNumber(decimalSeparator)) {
|
||||
throw new Error('decimalSeparator cannot be a number');
|
||||
}
|
||||
|
||||
if (isNumber(groupSeparator)) {
|
||||
throw new Error('groupSeparator cannot be a number');
|
||||
}
|
||||
|
||||
const formatValueOptions = {
|
||||
decimalSeparator,
|
||||
groupSeparator,
|
||||
turnOffSeparators,
|
||||
prefix,
|
||||
};
|
||||
|
||||
const cleanValueOptions: Partial<CleanValueOptions> = {
|
||||
decimalSeparator,
|
||||
groupSeparator,
|
||||
allowDecimals,
|
||||
decimalsLimit: decimalsLimit || fixedDecimalLength || 2,
|
||||
allowNegativeValue,
|
||||
turnOffAbbreviations,
|
||||
prefix,
|
||||
};
|
||||
|
||||
const _defaultValue =
|
||||
defaultValue !== undefined
|
||||
? formatValue({ value: String(defaultValue), ...formatValueOptions })
|
||||
: '';
|
||||
const [stateValue, setStateValue] = useState(_defaultValue);
|
||||
const [cursor, setCursor] = useState(0);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const onFocus = (): number => (stateValue ? stateValue.length : 0);
|
||||
|
||||
const processChange = (
|
||||
value: string,
|
||||
selectionStart?: number | null,
|
||||
): void => {
|
||||
const valueOnly = cleanValue({ value, ...cleanValueOptions });
|
||||
|
||||
if (!valueOnly) {
|
||||
onChange && onChange(undefined, name);
|
||||
setStateValue('');
|
||||
return;
|
||||
}
|
||||
|
||||
if (userMaxLength && valueOnly.replace(/-/g, '').length > userMaxLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (valueOnly === '-') {
|
||||
onChange && onChange(undefined, name);
|
||||
setStateValue(value);
|
||||
return;
|
||||
}
|
||||
|
||||
const formattedValue = formatValue({
|
||||
value: valueOnly,
|
||||
...formatValueOptions,
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (selectionStart !== undefined && selectionStart !== null) {
|
||||
const cursor =
|
||||
selectionStart + (formattedValue.length - value.length) || 1;
|
||||
setCursor(cursor);
|
||||
}
|
||||
|
||||
setStateValue(formattedValue);
|
||||
|
||||
onChange && onChange(valueOnly, name);
|
||||
};
|
||||
|
||||
const handleOnChange = ({
|
||||
target: { value, selectionStart },
|
||||
}: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
processChange(value, selectionStart);
|
||||
};
|
||||
|
||||
const handleOnBlur = ({
|
||||
target: { value },
|
||||
}: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const valueOnly = cleanValue({ value, ...cleanValueOptions });
|
||||
|
||||
if (valueOnly === '-' || !valueOnly) {
|
||||
onBlurValue && onBlurValue(undefined, name);
|
||||
setStateValue('');
|
||||
return;
|
||||
}
|
||||
|
||||
const fixedDecimals = fixedDecimalValue(
|
||||
valueOnly,
|
||||
decimalSeparator,
|
||||
fixedDecimalLength,
|
||||
);
|
||||
|
||||
// Add padding or trim value to precision
|
||||
const newValue = padTrimValue(
|
||||
fixedDecimals,
|
||||
decimalSeparator,
|
||||
precision || fixedDecimalLength,
|
||||
);
|
||||
onChange && onChange(newValue, name);
|
||||
onBlurValue && onBlurValue(newValue, name);
|
||||
|
||||
const formattedValue = formatValue({
|
||||
value: newValue,
|
||||
...formatValueOptions,
|
||||
});
|
||||
setStateValue(formattedValue);
|
||||
};
|
||||
|
||||
const handleOnKeyDown = ({ key }: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (step && (key === 'ArrowUp' || key === 'ArrowDown')) {
|
||||
const currentValue =
|
||||
Number(
|
||||
userValue !== undefined
|
||||
? userValue
|
||||
: cleanValue({ value: stateValue, ...cleanValueOptions }),
|
||||
) || 0;
|
||||
const newValue =
|
||||
key === 'ArrowUp'
|
||||
? String(currentValue + Number(step))
|
||||
: String(currentValue - Number(step));
|
||||
|
||||
processChange(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
useEffect(() => {
|
||||
if (inputRef && inputRef.current) {
|
||||
inputRef.current.setSelectionRange(cursor, cursor);
|
||||
}
|
||||
}, [cursor, inputRef]);
|
||||
|
||||
const formattedPropsValue =
|
||||
userValue !== undefined
|
||||
? formatValue({ value: String(userValue), ...formatValueOptions })
|
||||
: undefined;
|
||||
|
||||
const handleInputRef = (ref: HTMLInputElement | null) => {
|
||||
inputRef.current = ref;
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<InputGroup
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
id={id}
|
||||
name={name}
|
||||
className={className}
|
||||
onChange={handleOnChange}
|
||||
onBlur={handleOnBlur}
|
||||
onFocus={onFocus}
|
||||
onKeyDown={handleOnKeyDown}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
value={
|
||||
formattedPropsValue !== undefined && stateValue !== '-'
|
||||
? formattedPropsValue
|
||||
: stateValue
|
||||
}
|
||||
inputRef={handleInputRef}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export { CurrencyInput as MoneyInputGroup };
|
||||
@@ -0,0 +1,12 @@
|
||||
// @ts-nocheck
|
||||
import { addSeparators } from '../addSeparators';
|
||||
|
||||
describe('Separators', () => {
|
||||
it('should add separator in string', () => {
|
||||
expect(addSeparators('1000000')).toEqual('1,000,000');
|
||||
});
|
||||
|
||||
it('should use custom separator when provided', () => {
|
||||
expect(addSeparators('1000000', '.')).toEqual('1.000.000');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,221 @@
|
||||
// @ts-nocheck
|
||||
import { cleanValue } from '../cleanValue';
|
||||
|
||||
describe('cleanValue', () => {
|
||||
it('should remove group separator in string', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '1,000,000',
|
||||
})
|
||||
).toEqual('1000000');
|
||||
});
|
||||
|
||||
it('should handle period decimal separator in string', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '1.000.000,12',
|
||||
decimalSeparator: ',',
|
||||
groupSeparator: '.',
|
||||
})
|
||||
).toEqual('1000000,12');
|
||||
});
|
||||
|
||||
it('should remove prefix', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '£1000000',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('1000000');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '$5.5',
|
||||
prefix: '$',
|
||||
})
|
||||
).toEqual('5.5');
|
||||
});
|
||||
|
||||
it('should remove extra decimals', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '100.0000',
|
||||
})
|
||||
).toEqual('100.00');
|
||||
});
|
||||
|
||||
it('should remove decimals if not allowed', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '100.0000',
|
||||
allowDecimals: false,
|
||||
decimalsLimit: 0,
|
||||
})
|
||||
).toEqual('100');
|
||||
});
|
||||
|
||||
it('should include decimals if allowed', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '100.123',
|
||||
allowDecimals: true,
|
||||
decimalsLimit: 0,
|
||||
})
|
||||
).toEqual('100.123');
|
||||
});
|
||||
|
||||
it('should format value', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '£1,234,567.89',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('1234567.89');
|
||||
});
|
||||
|
||||
describe('negative values', () => {
|
||||
it('should handle negative value', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '-£1,000',
|
||||
decimalSeparator: '.',
|
||||
groupSeparator: ',',
|
||||
allowDecimals: true,
|
||||
decimalsLimit: 2,
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('-1000');
|
||||
});
|
||||
|
||||
it('should handle negative value with decimal', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '-£99,999.99',
|
||||
decimalSeparator: '.',
|
||||
groupSeparator: ',',
|
||||
allowDecimals: true,
|
||||
decimalsLimit: 2,
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('-99999.99');
|
||||
});
|
||||
|
||||
it('should handle not allow negative value if allowNegativeValue is false', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '-£1,000',
|
||||
decimalSeparator: '.',
|
||||
groupSeparator: ',',
|
||||
allowDecimals: true,
|
||||
decimalsLimit: 2,
|
||||
allowNegativeValue: false,
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('1000');
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle values placed before prefix', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '2£1',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('12');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '-2£1',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('-12');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '2-£1',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('-12');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '2-£1.99',
|
||||
prefix: '£',
|
||||
decimalsLimit: 5,
|
||||
})
|
||||
).toEqual('-1.992');
|
||||
});
|
||||
|
||||
describe('abbreviations', () => {
|
||||
it('should return empty string if abbreviation only', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: 'k',
|
||||
turnOffAbbreviations: true,
|
||||
})
|
||||
).toEqual('');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: 'm',
|
||||
turnOffAbbreviations: true,
|
||||
})
|
||||
).toEqual('');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: 'b',
|
||||
turnOffAbbreviations: true,
|
||||
})
|
||||
).toEqual('');
|
||||
});
|
||||
|
||||
it('should return empty string if prefix and abbreviation only', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '$k',
|
||||
prefix: '$',
|
||||
turnOffAbbreviations: true,
|
||||
})
|
||||
).toEqual('');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '£m',
|
||||
prefix: '£',
|
||||
turnOffAbbreviations: true,
|
||||
})
|
||||
).toEqual('');
|
||||
});
|
||||
|
||||
it('should ignore abbreviations if turnOffAbbreviations is true', () => {
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '1k',
|
||||
turnOffAbbreviations: true,
|
||||
})
|
||||
).toEqual('1');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '-2k',
|
||||
turnOffAbbreviations: true,
|
||||
})
|
||||
).toEqual('-2');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '25.6m',
|
||||
turnOffAbbreviations: true,
|
||||
})
|
||||
).toEqual('25.6');
|
||||
|
||||
expect(
|
||||
cleanValue({
|
||||
value: '9b',
|
||||
turnOffAbbreviations: true,
|
||||
})
|
||||
).toEqual('9');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
// @ts-nocheck
|
||||
import { fixedDecimalValue } from '../fixedDecimalValue';
|
||||
|
||||
describe('fixedDecimalValue', () => {
|
||||
it('should return original value if no match', () => {
|
||||
expect(fixedDecimalValue('abc', '.', 2)).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should work with 2 fixed decimal length', () => {
|
||||
expect(fixedDecimalValue('1', '.', 2)).toEqual('1');
|
||||
expect(fixedDecimalValue('12', '.', 2)).toEqual('1.2');
|
||||
expect(fixedDecimalValue('123', '.', 2)).toEqual('1.23');
|
||||
expect(fixedDecimalValue('12345', '.', 2)).toEqual('123.45');
|
||||
expect(fixedDecimalValue('123.4567', '.', 2)).toEqual('123.45');
|
||||
});
|
||||
|
||||
it('should work with 4 fixed decimal length', () => {
|
||||
expect(fixedDecimalValue('12', ',', 4)).toEqual('1,2');
|
||||
expect(fixedDecimalValue('123', ',', 4)).toEqual('1,23');
|
||||
expect(fixedDecimalValue('1234', ',', 4)).toEqual('1,234');
|
||||
expect(fixedDecimalValue('12345', ',', 4)).toEqual('1,2345');
|
||||
});
|
||||
|
||||
it('should trim decimals if too long', () => {
|
||||
expect(fixedDecimalValue('1.23', '.', 2)).toEqual('1.23');
|
||||
expect(fixedDecimalValue('1.2345', '.', 2)).toEqual('1.23');
|
||||
expect(fixedDecimalValue('1,2345678', ',', 3)).toEqual('1,234');
|
||||
expect(fixedDecimalValue('123,45678', ',', 3)).toEqual('123,456');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,173 @@
|
||||
// @ts-nocheck
|
||||
import { formatValue } from '../formatValue';
|
||||
|
||||
describe('formatValue', () => {
|
||||
it('should return empty if blank value', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '',
|
||||
})
|
||||
).toEqual('');
|
||||
});
|
||||
|
||||
it('should add separator', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234567',
|
||||
})
|
||||
).toEqual('1,234,567');
|
||||
});
|
||||
|
||||
it('should handle period separator', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234567',
|
||||
decimalSeparator: '.',
|
||||
groupSeparator: '.',
|
||||
})
|
||||
).toEqual('1.234.567');
|
||||
});
|
||||
|
||||
it('should handle comma separator for decimals', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234567,89',
|
||||
decimalSeparator: '.',
|
||||
groupSeparator: '.',
|
||||
})
|
||||
).toEqual('1.234.567,89');
|
||||
});
|
||||
|
||||
it('should handle - as separator for decimals', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234567-89',
|
||||
decimalSeparator: '-',
|
||||
groupSeparator: '.',
|
||||
})
|
||||
).toEqual('1.234.567-89');
|
||||
});
|
||||
|
||||
it('should handle empty decimal separator', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234567-89',
|
||||
decimalSeparator: '',
|
||||
groupSeparator: '.',
|
||||
})
|
||||
).toEqual('1.234.567-89');
|
||||
});
|
||||
|
||||
it('should NOT add separator if "turnOffSeparators" is true', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234567',
|
||||
turnOffSeparators: true,
|
||||
})
|
||||
).toEqual('1234567');
|
||||
});
|
||||
|
||||
it('should NOT add separator if "turnOffSeparators" is true even if decimal and group separators specified', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234567',
|
||||
decimalSeparator: '.',
|
||||
groupSeparator: ',',
|
||||
turnOffSeparators: true,
|
||||
})
|
||||
).toEqual('1234567');
|
||||
});
|
||||
|
||||
it('should add prefix', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '123',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('£123');
|
||||
});
|
||||
|
||||
it('should include "."', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234567.',
|
||||
})
|
||||
).toEqual('1,234,567.');
|
||||
});
|
||||
|
||||
it('should include decimals', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234.567',
|
||||
})
|
||||
).toEqual('1,234.567');
|
||||
});
|
||||
|
||||
it('should format value', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '1234567.89',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('£1,234,567.89');
|
||||
});
|
||||
|
||||
it('should handle 0 value', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '0',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('£0');
|
||||
});
|
||||
|
||||
describe('negative values', () => {
|
||||
it('should handle negative values', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '-1234',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('-£1,234');
|
||||
});
|
||||
|
||||
it('should return negative sign if only negative sign', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '-',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('-');
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle negative value and "-" as groupSeparator', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '-1234',
|
||||
groupSeparator: '-',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('-£1-234');
|
||||
});
|
||||
|
||||
it('should handle negative value and "-" as decimalSeparator', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '-12-34',
|
||||
decimalSeparator: '-',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('-£12-34');
|
||||
});
|
||||
|
||||
it('should handle negative value and "-" as groupSeparator', () => {
|
||||
expect(
|
||||
formatValue({
|
||||
value: '-123456',
|
||||
groupSeparator: '-',
|
||||
prefix: '£',
|
||||
})
|
||||
).toEqual('-£123-456');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
// @ts-nocheck
|
||||
import { isNumber } from '../isNumber';
|
||||
|
||||
describe('isNumber', () => {
|
||||
it('should return true for 0', () => {
|
||||
expect(isNumber('0')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for -3', () => {
|
||||
expect(isNumber('-3')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for 9', () => {
|
||||
expect(isNumber('9')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for abc1', () => {
|
||||
expect(isNumber('abc1')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for a.1', () => {
|
||||
expect(isNumber('a.1')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for space', () => {
|
||||
expect(isNumber(' ')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for comma', () => {
|
||||
expect(isNumber(',')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for period', () => {
|
||||
expect(isNumber('.')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for -', () => {
|
||||
expect(isNumber('-')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for +', () => {
|
||||
expect(isNumber('+')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
// @ts-nocheck
|
||||
import { padTrimValue } from '../padTrimValue';
|
||||
|
||||
describe('padTrimValue', () => {
|
||||
it('should return original value if no precision', () => {
|
||||
const value = padTrimValue('1000000');
|
||||
expect(value).toEqual('1000000');
|
||||
});
|
||||
|
||||
it('should return blank value if no value', () => {
|
||||
const value = padTrimValue('', '.', 2);
|
||||
expect(value).toEqual('');
|
||||
});
|
||||
|
||||
it('should return blank value if no only negative', () => {
|
||||
const value = padTrimValue('-', '.', 2);
|
||||
expect(value).toEqual('');
|
||||
});
|
||||
|
||||
it('should pad with 0 if no decimals', () => {
|
||||
const value = padTrimValue('99', '.', 3);
|
||||
expect(value).toEqual('99.000');
|
||||
});
|
||||
|
||||
it('should pad with 0 if decimal length is less than precision', () => {
|
||||
const value = padTrimValue('10.5', '.', 5);
|
||||
expect(value).toEqual('10.50000');
|
||||
});
|
||||
|
||||
it('should trim if decimal length is larger than precision', () => {
|
||||
const value = padTrimValue('10.599', '.', 2);
|
||||
expect(value).toEqual('10.59');
|
||||
});
|
||||
|
||||
it('should trim handle comma as decimal separator', () => {
|
||||
const value = padTrimValue('9,9', ',', 3);
|
||||
expect(value).toEqual('9,900');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
// @ts-nocheck
|
||||
import { abbrValue, parseAbbrValue } from '../parseAbbrValue';
|
||||
|
||||
describe('abbrValue', () => {
|
||||
it('should not convert value under 1000', () => {
|
||||
expect(abbrValue(999)).toEqual('999');
|
||||
});
|
||||
|
||||
it('should convert thousand to k', () => {
|
||||
expect(abbrValue(1000)).toEqual('1k');
|
||||
expect(abbrValue(1500)).toEqual('1.5k');
|
||||
expect(abbrValue(10000)).toEqual('10k');
|
||||
});
|
||||
|
||||
it('should work with comma as decimal separator', () => {
|
||||
expect(abbrValue(1500, ',')).toEqual('1,5k');
|
||||
});
|
||||
|
||||
it('should work with decimal places option', () => {
|
||||
expect(abbrValue(123456, '.')).toEqual('0.123456M');
|
||||
expect(abbrValue(123456, '.', 2)).toEqual('0.12M');
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseAbbrValue', () => {
|
||||
it('should return undefined if cannot parse', () => {
|
||||
expect(parseAbbrValue('1km')).toEqual(undefined);
|
||||
expect(parseAbbrValue('2mb')).toEqual(undefined);
|
||||
expect(parseAbbrValue('3a')).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined if no abbreviation', () => {
|
||||
expect(parseAbbrValue('1.23')).toEqual(undefined);
|
||||
expect(parseAbbrValue('100')).toEqual(undefined);
|
||||
expect(parseAbbrValue('20000')).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined for only letter', () => {
|
||||
expect(parseAbbrValue('k')).toBeUndefined();
|
||||
expect(parseAbbrValue('m')).toBeUndefined();
|
||||
expect(parseAbbrValue('b')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return 0 for 0', () => {
|
||||
expect(parseAbbrValue('0k')).toEqual(0);
|
||||
expect(parseAbbrValue('0m')).toEqual(0);
|
||||
expect(parseAbbrValue('0b')).toEqual(0);
|
||||
});
|
||||
|
||||
it('should parse k', () => {
|
||||
expect(parseAbbrValue('1k')).toEqual(1000);
|
||||
expect(parseAbbrValue('2K')).toEqual(2000);
|
||||
expect(parseAbbrValue('1.1239999k')).toEqual(1123.9999);
|
||||
expect(parseAbbrValue('1.5k')).toEqual(1500);
|
||||
expect(parseAbbrValue('50.12K')).toEqual(50120);
|
||||
expect(parseAbbrValue('100K')).toEqual(100000);
|
||||
});
|
||||
|
||||
it('should parse m', () => {
|
||||
expect(parseAbbrValue('1m')).toEqual(1000000);
|
||||
expect(parseAbbrValue('1.5m')).toEqual(1500000);
|
||||
expect(parseAbbrValue('45.123456m')).toEqual(45123456);
|
||||
expect(parseAbbrValue('83.5m')).toEqual(83500000);
|
||||
expect(parseAbbrValue('100M')).toEqual(100000000);
|
||||
});
|
||||
|
||||
it('should parse b', () => {
|
||||
expect(parseAbbrValue('1b')).toEqual(1000000000);
|
||||
expect(parseAbbrValue('1.5b')).toEqual(1500000000);
|
||||
expect(parseAbbrValue('65.5513b')).toEqual(65551300000);
|
||||
expect(parseAbbrValue('100B')).toEqual(100000000000);
|
||||
});
|
||||
|
||||
it('should work with comma as decimal separator', () => {
|
||||
expect(parseAbbrValue('1,2k', ',')).toEqual(1200);
|
||||
expect(parseAbbrValue('2,3m', ',')).toEqual(2300000);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
// @ts-nocheck
|
||||
import { removeInvalidChars } from '../removeInvalidChars';
|
||||
|
||||
describe('removeInvalidChars', () => {
|
||||
it('should remove letters in string', () => {
|
||||
expect(removeInvalidChars('1,000ab,0cd00.99', [',', '.'])).toEqual('1,000,000.99');
|
||||
});
|
||||
|
||||
it('should remove special characters in string', () => {
|
||||
expect(removeInvalidChars('1.00ji0.0*&0^0', ['.'])).toEqual('1.000.000');
|
||||
});
|
||||
|
||||
it('should keep abbreviations', () => {
|
||||
expect(removeInvalidChars('9k', ['k'])).toEqual('9k');
|
||||
expect(removeInvalidChars('1m', ['m'])).toEqual('1m');
|
||||
expect(removeInvalidChars('5b', ['b'])).toEqual('5b');
|
||||
});
|
||||
|
||||
it('should keep abbreviations (case insensitive)', () => {
|
||||
expect(removeInvalidChars('9K', ['k'])).toEqual('9K');
|
||||
expect(removeInvalidChars('1M', ['m'])).toEqual('1M');
|
||||
expect(removeInvalidChars('5B', ['b'])).toEqual('5B');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
// @ts-nocheck
|
||||
import { removeSeparators } from '../removeSeparators';
|
||||
|
||||
describe('removeSeparators', () => {
|
||||
it('should remove separators in string', () => {
|
||||
expect(removeSeparators('1,000,000')).toEqual('1000000');
|
||||
});
|
||||
|
||||
it('should use custom separator when provided', () => {
|
||||
expect(removeSeparators('1.000.000', '.')).toEqual('1000000');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* Add group separator to value eg. 1000 > 1,000
|
||||
*/
|
||||
export const addSeparators = (value: string, separator = ','): string => {
|
||||
return value.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
// @ts-nocheck
|
||||
import { parseAbbrValue } from './parseAbbrValue';
|
||||
import { removeSeparators } from './removeSeparators';
|
||||
import { removeInvalidChars } from './removeInvalidChars';
|
||||
import { escapeRegExp } from './escapeRegExp';
|
||||
|
||||
export type CleanValueOptions = {
|
||||
value: string;
|
||||
decimalSeparator?: string;
|
||||
groupSeparator?: string;
|
||||
allowDecimals?: boolean;
|
||||
decimalsLimit?: number;
|
||||
allowNegativeValue?: boolean;
|
||||
turnOffAbbreviations?: boolean;
|
||||
prefix?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove prefix, separators and extra decimals from value
|
||||
*/
|
||||
export const cleanValue = ({
|
||||
value,
|
||||
groupSeparator = ',',
|
||||
decimalSeparator = '.',
|
||||
allowDecimals = true,
|
||||
decimalsLimit = 2,
|
||||
allowNegativeValue = true,
|
||||
turnOffAbbreviations = false,
|
||||
prefix = '',
|
||||
}: CleanValueOptions): string => {
|
||||
const abbreviations = turnOffAbbreviations ? [] : ['k', 'm', 'b'];
|
||||
const isNegative = value.includes('-');
|
||||
|
||||
const [prefixWithValue, preValue] = RegExp(`(\\d+)-?${escapeRegExp(prefix)}`).exec(value) || [];
|
||||
const withoutPrefix = prefix ? value.replace(prefixWithValue, '').concat(preValue) : value;
|
||||
const withoutSeparators = removeSeparators(withoutPrefix, groupSeparator);
|
||||
const withoutInvalidChars = removeInvalidChars(withoutSeparators, [
|
||||
groupSeparator,
|
||||
decimalSeparator,
|
||||
...abbreviations,
|
||||
]);
|
||||
|
||||
let valueOnly = withoutInvalidChars;
|
||||
|
||||
if (!turnOffAbbreviations) {
|
||||
// disallow letter without number
|
||||
if (abbreviations.some((letter) => letter === withoutInvalidChars.toLowerCase())) {
|
||||
return '';
|
||||
}
|
||||
const parsed = parseAbbrValue(withoutInvalidChars, decimalSeparator);
|
||||
if (parsed) {
|
||||
valueOnly = String(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
const includeNegative = isNegative && allowNegativeValue ? '-' : '';
|
||||
|
||||
if (String(valueOnly).includes(decimalSeparator)) {
|
||||
const [int, decimals] = withoutInvalidChars.split(decimalSeparator);
|
||||
const trimmedDecimals = decimalsLimit ? decimals.slice(0, decimalsLimit) : decimals;
|
||||
const includeDecimals = allowDecimals ? `${decimalSeparator}${trimmedDecimals}` : '';
|
||||
|
||||
return `${includeNegative}${int}${includeDecimals}`;
|
||||
}
|
||||
|
||||
return `${includeNegative}${valueOnly}`;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* Escape regex char
|
||||
*
|
||||
* See: https://stackoverflow.com/questions/17885855/use-dynamic-variable-string-as-regex-pattern-in-javascript
|
||||
*/
|
||||
export const escapeRegExp = (stringToGoIntoTheRegex: string): string => {
|
||||
return stringToGoIntoTheRegex.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// @ts-nocheck
|
||||
export const fixedDecimalValue = (
|
||||
value: string,
|
||||
decimalSeparator: string,
|
||||
fixedDecimalLength?: number
|
||||
): string => {
|
||||
if (fixedDecimalLength && value.length > 1) {
|
||||
if (value.includes(decimalSeparator)) {
|
||||
const [int, decimals] = value.split(decimalSeparator);
|
||||
if (decimals.length > fixedDecimalLength) {
|
||||
return `${int}${decimalSeparator}${decimals.slice(0, fixedDecimalLength)}`;
|
||||
}
|
||||
}
|
||||
|
||||
const reg =
|
||||
value.length > fixedDecimalLength
|
||||
? new RegExp(`(\\d+)(\\d{${fixedDecimalLength}})`)
|
||||
: new RegExp(`(\\d)(\\d+)`);
|
||||
|
||||
const match = value.match(reg);
|
||||
if (match) {
|
||||
const [, int, decimals] = match;
|
||||
return `${int}${decimalSeparator}${decimals}`;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
// @ts-nocheck
|
||||
import { addSeparators } from './addSeparators';
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
* Value to format
|
||||
*/
|
||||
value: number | string | undefined;
|
||||
|
||||
/**
|
||||
* Decimal separator
|
||||
*
|
||||
* Default = '.'
|
||||
*/
|
||||
decimalSeparator?: string;
|
||||
|
||||
/**
|
||||
* Group separator
|
||||
*
|
||||
* Default = ','
|
||||
*/
|
||||
groupSeparator?: string;
|
||||
|
||||
/**
|
||||
* Turn off separators
|
||||
*
|
||||
* This will override Group separators
|
||||
*
|
||||
* Default = false
|
||||
*/
|
||||
turnOffSeparators?: boolean;
|
||||
|
||||
/**
|
||||
* Prefix
|
||||
*/
|
||||
prefix?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format value with decimal separator, group separator and prefix
|
||||
*/
|
||||
export const formatValue = (props: Props): string => {
|
||||
const {
|
||||
value: _value,
|
||||
groupSeparator = ',',
|
||||
decimalSeparator = '.',
|
||||
turnOffSeparators = false,
|
||||
prefix,
|
||||
} = props;
|
||||
|
||||
if (_value === '' || _value === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const value = String(_value);
|
||||
|
||||
if (value === '-') {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const isNegative = RegExp('^-\\d+').test(value);
|
||||
const hasDecimalSeparator = decimalSeparator && value.includes(decimalSeparator);
|
||||
|
||||
const valueOnly = isNegative ? value.replace('-', '') : value;
|
||||
const [int, decimals] = hasDecimalSeparator ? valueOnly.split(decimalSeparator) : [valueOnly];
|
||||
|
||||
const formattedInt = turnOffSeparators ? int : addSeparators(int, groupSeparator);
|
||||
|
||||
const includePrefix = prefix ? prefix : '';
|
||||
const includeNegative = isNegative ? '-' : '';
|
||||
const includeDecimals =
|
||||
hasDecimalSeparator && decimals
|
||||
? `${decimalSeparator}${decimals}`
|
||||
: hasDecimalSeparator
|
||||
? `${decimalSeparator}`
|
||||
: '';
|
||||
|
||||
return `${includeNegative}${includePrefix}${formattedInt}${includeDecimals}`;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
// @ts-nocheck
|
||||
export * from './cleanValue';
|
||||
export * from './fixedDecimalValue';
|
||||
export * from './formatValue';
|
||||
export * from './isNumber';
|
||||
export * from './padTrimValue';
|
||||
@@ -0,0 +1,2 @@
|
||||
// @ts-nocheck
|
||||
export const isNumber = (input: string): boolean => RegExp(/\d/, 'gi').test(input);
|
||||
@@ -0,0 +1,23 @@
|
||||
// @ts-nocheck
|
||||
export const padTrimValue = (value: string, decimalSeparator = '.', precision?: number): string => {
|
||||
if (!precision || value === '' || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (!value.match(/\d/g)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const [int, decimals] = value.split(decimalSeparator);
|
||||
let newValue = decimals || '';
|
||||
|
||||
if (newValue.length < precision) {
|
||||
while (newValue.length < precision) {
|
||||
newValue += '0';
|
||||
}
|
||||
} else {
|
||||
newValue = newValue.slice(0, precision);
|
||||
}
|
||||
|
||||
return `${int}${decimalSeparator}${newValue}`;
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
// @ts-nocheck
|
||||
import { escapeRegExp } from './escapeRegExp';
|
||||
|
||||
/**
|
||||
* Abbreviate number eg. 1000 = 1k
|
||||
*
|
||||
* Source: https://stackoverflow.com/a/9345181
|
||||
*/
|
||||
export const abbrValue = (value: number, decimalSeparator = '.', _decimalPlaces = 10): string => {
|
||||
if (value > 999) {
|
||||
let valueLength = ('' + value).length;
|
||||
const p = Math.pow;
|
||||
const d = p(10, _decimalPlaces);
|
||||
valueLength -= valueLength % 3;
|
||||
|
||||
const abbrValue = Math.round((value * d) / p(10, valueLength)) / d + ' kMGTPE'[valueLength / 3];
|
||||
return abbrValue.replace('.', decimalSeparator);
|
||||
}
|
||||
|
||||
return String(value);
|
||||
};
|
||||
|
||||
type AbbrMap = { [key: string]: number };
|
||||
|
||||
const abbrMap: AbbrMap = { k: 1000, m: 1000000, b: 1000000000 };
|
||||
|
||||
/**
|
||||
* Parse a value with abbreviation e.g 1k = 1000
|
||||
*/
|
||||
export const parseAbbrValue = (value: string, decimalSeparator = '.'): number | undefined => {
|
||||
const reg = new RegExp(`(\\d+(${escapeRegExp(decimalSeparator)}\\d+)?)([kmb])$`, 'i');
|
||||
const match = value.match(reg);
|
||||
|
||||
if (match) {
|
||||
const [, digits, , abbr] = match;
|
||||
const multiplier = abbrMap[abbr.toLowerCase()];
|
||||
if (digits && multiplier) {
|
||||
return Number(digits.replace(decimalSeparator, '.')) * multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
import { escapeRegExp } from './escapeRegExp';
|
||||
|
||||
/**
|
||||
* Remove invalid characters
|
||||
*/
|
||||
export const removeInvalidChars = (value: string, validChars: ReadonlyArray<string>): string => {
|
||||
const chars = escapeRegExp(validChars.join(''));
|
||||
const reg = new RegExp(`[^\\d${chars}]`, 'gi');
|
||||
return value.replace(reg, '');
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
import { escapeRegExp } from './escapeRegExp';
|
||||
|
||||
/**
|
||||
* Remove group separator from value eg. 1,000 > 1000
|
||||
*/
|
||||
export const removeSeparators = (value: string, separator = ','): string => {
|
||||
const reg = new RegExp(escapeRegExp(separator), 'g');
|
||||
return value.replace(reg, '');
|
||||
};
|
||||
7
packages/webapp/src/components/Forms/index.tsx
Normal file
7
packages/webapp/src/components/Forms/index.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from './FormObserver';
|
||||
export * from './FormikObserver';
|
||||
export * from './FMoneyInputGroup';
|
||||
export * from './BlueprintFormik';
|
||||
export * from './InputPrependText';
|
||||
export * from './InputPrependButton';
|
||||
export * from './MoneyInputGroup';
|
||||
Reference in New Issue
Block a user