Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 43 additions & 15 deletions semcore/input-number/src/InputNumber.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Intergalactic } from '@semcore/core';
import { Component, createComponent, Root, sstyled } from '@semcore/core';
import propsObserver from '@semcore/core/lib/decorators/propsObserver';
import { callAllEventHandlers } from '@semcore/core/lib/utils/assignProps';
import type { WithI18nEnhanceProps } from '@semcore/core/lib/utils/enhances/i18nEnhance';
import i18nEnhance from '@semcore/core/lib/utils/enhances/i18nEnhance';
Expand Down Expand Up @@ -91,21 +92,24 @@ class InputNumber extends Component<
}
}

@propsObserver(['value'])
class Value extends Component<
Intergalactic.InternalTypings.InferChildComponentProps<NSInputNumber.Value.Component, typeof InputNumber, 'Value'>,
[],
NSInputNumber.Value.Handlers,
{},
NSInputNumber.Value.State,
NSInputNumber.Value.DefaultProps
> {
Intergalactic.InternalTypings.InferChildComponentProps<NSInputNumber.Value.Component, typeof InputNumber, 'Value'>,
[],
NSInputNumber.Value.Handlers,
{},
NSInputNumber.Value.State,
NSInputNumber.Value.DefaultProps
> {
static style = style;
static defaultProps = {
defaultValue: '',
defaultDisplayValue: '',
step: 1,
} as const;

valueType: 'string' | 'number' = typeof this.props.value === 'string' ? 'string' : 'number';

state: NSInputNumber.Value.State = {
displayValue: '',
};
Expand Down Expand Up @@ -148,15 +152,27 @@ class Value extends Component<
return numberFormatter.format(1111).replace(/\d/g, '');
}

onPropsChange(changedProps: { value?: string | number | null }) {
const { value } = changedProps;

if (value !== undefined) {
if (typeof value === 'string') {
this.valueType = 'string';
} else {
this.valueType = 'number';
}
}
}

getFormattedValue = (value: string) => {
return value
.replace(new RegExp(`[${this.separatorThousands}]`, 'g'), '')
.replace(this.separatorDecimal, '.');
};

valueParser = (
value: typeof this.asProps['value'],
prevValue: typeof this.asProps['value'],
value: string | number,
prevValue: string,
prevDisplayValue: NSInputNumber.Value.State['displayValue'],
) => {
const { numberFormatter } = this.props;
Expand Down Expand Up @@ -221,7 +237,7 @@ class Value extends Component<

if (Number.isNaN(value) || Number.isNaN(Number.parseFloat(parsedValue))) {
event.currentTarget.value = '';
this.handlers.value('', event);
this.handlers.value(this.processedValue(''), event);
} else {
let numberValue = parseValueWithMinMax(Number.parseFloat(parsedValue), min, max);
const rounded = this.round(numberValue % step);
Expand All @@ -233,7 +249,7 @@ class Value extends Component<
}
}

this.handlers.value(this.getDisplayValue(numberValue), event);
this.handlers.value(this.processedValue(this.getDisplayValue(numberValue)), event);
}
};

Expand Down Expand Up @@ -308,15 +324,15 @@ class Value extends Component<
this.setState({ displayValue: numberFormatter.format(value as `${number}`) + this.separatorDecimal });
return false;
} else {
this.handlers.value(value.slice(0, -1), event);
this.handlers.value(this.processedValue(value.slice(0, -1)), event);
return false;
}
}

const digits = /^[0-9.-]+$/.test(value);

if (digits || value === '') {
this.handlers.value(this.limitDecimals(value), event);
this.handlers.value(this.processedValue(this.limitDecimals(value)), event);
}
};

Expand Down Expand Up @@ -492,7 +508,7 @@ class Value extends Component<

const nextValue = Math.min(numberValue + step, max);

this.handlers.value(this.getDisplayValue(nextValue), event);
this.handlers.value(this.processedValue(this.getDisplayValue(nextValue)), event);
};

stepDown = (event: StepEvent) => {
Expand All @@ -510,9 +526,21 @@ class Value extends Component<

const nextValue = Math.max(numberValue - step, min);

this.handlers.value(this.getDisplayValue(nextValue), event);
this.handlers.value(this.processedValue(this.getDisplayValue(nextValue)), event);
};

processedValue(value: string): NSInputNumber.Value | NSInputNumber.ValueNumber {
if (this.valueType === 'string') {
return value;
}

if (value === '') {
return null;
}

return Number(value);
}

render() {
const SValue = Root;
const { styles, min, max, step, forwardRef, inputRef } = this.asProps;
Expand Down
23 changes: 17 additions & 6 deletions semcore/input-number/src/InputNumber.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { LocalizedMessages } from './translations/__intergalactic-dynamic-l

declare namespace NSInputNumber {
type Value = string;
type ValueNumber = number | null;
type CalculatedValue<V> = V extends Value ? Value : ValueNumber;
type Size = 'm' | 'l';
type Props = NSInput.Props & {
/** Input size
Expand All @@ -29,7 +31,7 @@ declare namespace NSInputNumber {
};

namespace Value {
type Props = NSInput.Value.Props & {
type Props<V extends Value | ValueNumber> = Intergalactic.InternalTypings.EfficientOmit<NSInput.Value.Props, 'value' | 'onChange'> & {
/** Minimum value
* @default Number.MIN_SAFE_INTEGER
*/
Expand All @@ -43,9 +45,9 @@ declare namespace NSInputNumber {
*/
step?: number;
/** Numeric value */
value?: NSInputNumber.Value;
value?: CalculatedValue<V>;
/** Called when the input value changes, it returns its current value in numeric format */
onChange?: (value: NSInputNumber.Value, event?: React.SyntheticEvent<HTMLInputElement>) => void;
onChange?: (value: CalculatedValue<V>, event?: React.SyntheticEvent<HTMLInputElement>) => void;
};
type DefaultProps = {
defaultValue: '';
Expand All @@ -57,11 +59,20 @@ declare namespace NSInputNumber {
type Handlers = {
value: [
null,
(value: Props['value'], event: React.SyntheticEvent | WheelEvent) => void,
(value: Value | ValueNumber | undefined, event: React.SyntheticEvent | WheelEvent) => void,
];
};

type Component = Intergalactic.Component<'input', Props>;
type Component = (<
V extends Value | ValueNumber,
Tag extends Intergalactic.Tag = 'input',
>(
props: Intergalactic.InternalTypings.EfficientOmit<
Intergalactic.InternalTypings.ComponentProps<Tag, 'input', Props<V>>,
'tag' | 'children'
>
) => Intergalactic.InternalTypings.ComponentRenderingResults) &
Intergalactic.InternalTypings.ComponentAdditive<'input', 'input', Props<any>>;
}

namespace Controls {
Expand Down Expand Up @@ -93,7 +104,7 @@ export type InputNumberSize = NSInputNumber.Size;
/** @deprecated It will be removed in v18. */
export type InputNumberProps = NSInputNumber.Props;
/** @deprecated It will be removed in v18. */
export type InputNumberValueProps = NSInputNumber.Value.Props;
export type InputNumberValueProps = NSInputNumber.Value.Props<string>;
/** @deprecated It will be removed in v18. */
export type InputNumberControlsProps = NSInputNumber.Controls.Props;
/** @deprecated It will be removed in v18. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from 'react';

type BaseExampleType =
& NSInputNumber.Props
& NSInputNumber.Value.Props
& NSInputNumber.Value.Props<NSInputNumber.ValueNumber>
& NSInputNumber.Controls.Props
& { disabledValue?: boolean; logChanges?: boolean };

Expand All @@ -17,9 +17,9 @@ const getStepPrecision = (step?: number) => {

const Demo = (props: BaseExampleType) => {
const stepPrecision = getStepPrecision(props.step);
const [lastValue, setLastValue] = React.useState<string | null>(null);
const [lastValue, setLastValue] = React.useState<NSInputNumber.ValueNumber>(null);

const handleChange = (value: string, _event?: React.SyntheticEvent<HTMLInputElement>) => {
const handleChange = (value: NSInputNumber.ValueNumber, _event?: React.SyntheticEvent<HTMLInputElement>) => {
setLastValue(value);

if (props.logChanges) {
Expand All @@ -38,7 +38,7 @@ const Demo = (props: BaseExampleType) => {
disabled={props.disabled}
locale={props.locale}
>
<InputNumber.Value
<InputNumber.Value<NSInputNumber.ValueNumber>
disabled={props.disabledValue}
max={props.max}
min={props.min}
Expand Down
Loading