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
2 changes: 1 addition & 1 deletion semcore/inline-input/src/InlineInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type ConfirmControlAsProps = ControlAsProps & {
type CancelControlAsProps = ControlAsProps & {
onCancel?: OnCancel;
};
type NumberValueAsProps = NSInputNumber.Value.Props & {
type NumberValueAsProps = NSInputNumber.Value.Props<any> & {
increment?: (event: WheelEvent) => void;
decrement?: (event: WheelEvent) => void;
};
Expand Down
59 changes: 44 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,7 +324,7 @@ 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;
}
}
Expand All @@ -317,7 +333,7 @@ class Value extends Component<

if (digits || value === '') {
const newValue = this.limitDecimals(value);
this.handlers.value(newValue, event);
this.handlers.value(this.processedValue(newValue), event);
if (newValue === prevValue) {
const { displayValue } = this.valueParser(
newValue ?? '',
Expand Down Expand Up @@ -366,6 +382,7 @@ class Value extends Component<
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes(event.key))
) {
const afterSelection = value.slice(element.selectionEnd ?? undefined);

requestAnimationFrame(() => {
const newValue = this.state.displayValue;
const index = newValue.lastIndexOf(afterSelection);
Expand Down Expand Up @@ -500,7 +517,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 @@ -518,9 +535,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 = Value,
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 v19. */
export type InputNumberProps = NSInputNumber.Props;
/** @deprecated It will be removed in v19. */
export type InputNumberValueProps = NSInputNumber.Value.Props;
export type InputNumberValueProps = NSInputNumber.Value.Props<string>;
/** @deprecated It will be removed in v19. */
export type InputNumberControlsProps = NSInputNumber.Controls.Props;
/** @deprecated It will be removed in v19. */
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