Skip to content

Commit

Permalink
react: fix types
Browse files Browse the repository at this point in the history
  • Loading branch information
uNmAnNeR committed Dec 13, 2023
1 parent 1c197fa commit bab8604
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 37 deletions.
2 changes: 1 addition & 1 deletion packages/imask/src/controls/input.ts
Expand Up @@ -16,7 +16,7 @@ type InputMaskEventListener = (e?: InputEvent) => void;

/** Listens to element events and controls changes between element and {@link Masked} */
export default
class InputMask<Opts extends FactoryArg = FactoryArg> {
class InputMask<Opts extends FactoryArg=Record<string, unknown>> {
/**
View element
*/
Expand Down
17 changes: 13 additions & 4 deletions packages/imask/src/masked/factory.ts
Expand Up @@ -69,7 +69,17 @@ type FactoryStaticMaskReturnMasked<Mask extends FactoryStaticOpts['mask']> =


export
type FactoryInstanceOpts = MaskedOptions & { mask: Masked };
type FactoryInstanceOpts =
| { mask: MaskedDate } & Omit<MaskedDateFactoryOptions, 'mask'>
| { mask: MaskedNumber } & Omit<MaskedNumberOptions, 'mask'>
| { mask: MaskedEnum } & Omit<MaskedEnumOptions, 'mask'>
| { mask: MaskedRange } & Omit<MaskedRangeOptions, 'mask'>
| { mask: MaskedRegExp } & Omit<MaskedRegExpOptions, 'mask'>
| { mask: MaskedFunction } & Omit<MaskedFunctionOptions, 'mask'>
| { mask: MaskedPattern } & Omit<MaskedPatternOptions, 'mask'>
| { mask: MaskedDynamic } & Omit<MaskedDynamicOptions, 'mask'>
| { mask: Masked } & Omit<MaskedOptions, 'mask'>
;

export
type FactoryInstanceReturnMasked<Opts extends FactoryInstanceOpts> = Opts extends { mask: infer M } ? M : never;
Expand Down Expand Up @@ -133,8 +143,8 @@ type UpdateInstanceOpts<M extends Masked> =
M extends MaskedDate ? MaskedDateOptions :
M extends MaskedNumber ? MaskedNumberOptions :
M extends MaskedDynamic ? MaskedDynamicOptions :
M extends MaskedRange ? MaskedRangeOptions :
M extends MaskedEnum ? MaskedEnumOptions :
M extends MaskedRange ? MaskedRangeOptions & { mask: MaskedRange } :
M extends MaskedEnum ? MaskedEnumOptions & { mask: MaskedEnum } :
M extends MaskedPattern ? MaskedPatternOptions :
AnyOpts
;
Expand Down Expand Up @@ -184,7 +194,6 @@ type FactoryReturnMasked<Opts extends FactoryArg> =
;



// TODO can't use overloads here because of https://github.com/microsoft/TypeScript/issues/50754
// export function maskedClass(mask: string): typeof MaskedPattern;
// export function maskedClass(mask: DateConstructor): typeof MaskedDate;
Expand Down
5 changes: 4 additions & 1 deletion packages/react-imask/src/hook.ts
Expand Up @@ -10,7 +10,10 @@ function useIMask<
Opts extends FactoryOpts=FactoryOpts,
>(
opts: Opts,
{ onAccept, onComplete }: Partial<Pick<ReactMaskProps<MaskElement, Opts>, 'onAccept' | 'onComplete'>> = {}
{ onAccept, onComplete }: {
onAccept?: (value: InputMask<Opts>['value'], maskRef: InputMask<Opts>, e?: InputEvent) => void;
onComplete?: (value: InputMask<Opts>['value'], maskRef: InputMask<Opts>, e?: InputEvent) => void;
} = {}
): {
ref: MutableRefObject<MaskElement | null>,
maskRef: MutableRefObject<InputMask<Opts> | null>,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-imask/src/index.ts
Expand Up @@ -4,7 +4,7 @@ export { default as IMaskInput } from './input';
export { default as useIMask } from './hook';
export { default as IMaskMixin,
type MaskPropsKeys,
type MaskOpts,
type ExtractMaskOpts,
type ReactElementProps,
type ReactMaskProps,
type ReactMixinComponent,
Expand Down
64 changes: 34 additions & 30 deletions packages/react-imask/src/mixin.ts
Expand Up @@ -3,11 +3,13 @@ import PropTypes from 'prop-types';
import IMask, { type InputMask, type InputMaskElement, type FactoryOpts, type AllFactoryStaticOpts } from 'imask';


type AnyProps = Record<string, unknown>;

export
type Falsy = false | 0 | "" | null | undefined;

export
type ReactMaskOpts<Opts extends FactoryOpts=FactoryOpts> = Opts & { unmask?: 'typed' | boolean };
type ReactMaskOpts = FactoryOpts & { unmask?: 'typed' | boolean };

export
type UnmaskValue<Opts extends ReactMaskOpts> =
Expand All @@ -16,17 +18,23 @@ type UnmaskValue<Opts extends ReactMaskOpts> =
InputMask<Opts>['unmaskedValue']
;

export
type ExtractReactMaskOpts<
MaskElement extends InputMaskElement,
Props extends IMaskInputProps<MaskElement>,
> = Extract<Props, ReactMaskOpts>;

export
type ReactMaskProps<
MaskElement extends InputMaskElement,
Opts extends ReactMaskOpts=ReactMaskOpts,
Props extends IMaskInputProps<MaskElement>=AnyProps,
> = {
onAccept?: (value: UnmaskValue<Opts>, maskRef: InputMask<Opts>, e?: InputEvent) => void;
onComplete?: (value: UnmaskValue<Opts>, maskRef: InputMask<Opts>, e?: InputEvent) => void;
unmask?: Opts['unmask'];
value?: UnmaskValue<Opts>;
onAccept?: (value: UnmaskValue<ExtractReactMaskOpts<MaskElement, Props>>, maskRef: InputMask<ExtractMaskOpts<MaskElement, Props>>, e?: InputEvent) => void;
onComplete?: (value: UnmaskValue<ExtractReactMaskOpts<MaskElement, Props>>, maskRef: InputMask<ExtractMaskOpts<MaskElement, Props>>, e?: InputEvent) => void;
unmask?: ExtractReactMaskOpts<MaskElement, Props>['unmask'];
value?: UnmaskValue<ExtractReactMaskOpts<MaskElement, Props>>;
inputRef?: React.Ref<MaskElement>;
ref?: React.Ref<React.ComponentType<IMaskInputProps<MaskElement>>>;
ref?: React.Ref<React.ComponentType<Props>>;
}

const MASK_PROPS: { [key in keyof (AllFactoryStaticOpts & ReactMaskProps<InputMaskElement, AllFactoryStaticOpts>)]: any } = {
Expand Down Expand Up @@ -116,16 +124,18 @@ const MASK_PROPS_NAMES = (Object.keys(MASK_PROPS) as Array<keyof typeof MASK_PRO
const NON_MASK_OPTIONS_PROPS_NAMES = ['value', 'unmask', 'onAccept', 'onComplete', 'inputRef'] as const;

export
type ReactElementProps<MaskElement extends InputMaskElement> = Omit<Omit<React.HTMLProps<MaskElement>, keyof typeof MASK_PROPS>, typeof NON_MASK_OPTIONS_PROPS_NAMES[number]>;
type ReactElementProps<MaskElement extends InputMaskElement> =
Omit<Omit<React.HTMLProps<MaskElement>, keyof typeof MASK_PROPS>, typeof NON_MASK_OPTIONS_PROPS_NAMES[number]>;

type NonMaskProps<
MaskElement extends InputMaskElement,
Props extends IMaskMixinProps<MaskElement>=IMaskMixinProps<MaskElement>
Props extends IMaskMixinProps<MaskElement>=AnyProps
> = Omit<Props, keyof FactoryOpts>;

export
type ReactMixinComponent<
MaskElement extends InputMaskElement,
Props extends IMaskInputProps<MaskElement>,
Props extends IMaskMixinProps<MaskElement>=AnyProps,
> = React.ComponentType<
& ReactElementProps<MaskElement>
& { inputRef: React.Ref<MaskElement> }
Expand All @@ -139,25 +149,19 @@ const MASK_OPTIONS_PROPS_NAMES = MASK_PROPS_NAMES.filter(pName =>
) as Array<MaskPropsKeys>;

export
type MaskOpts<
type ExtractMaskOpts<
MaskElement extends InputMaskElement,
Props extends IMaskInputProps<MaskElement>=IMaskInputProps<MaskElement>
Props extends IMaskInputProps<MaskElement>,
> = Extract<Props, FactoryOpts>;

export
type IMaskMixinProps<
MaskElement extends InputMaskElement,
Props extends (ReactMaskOpts & ReactMaskProps<MaskElement>)=ReactMaskOpts & ReactMaskProps<MaskElement>,
> = Omit<ReactMaskProps<MaskElement, Props>, 'ref'> & MaskOpts<MaskElement, Props>;
type IMaskMixinProps<MaskElement extends InputMaskElement> =
Omit<ReactMaskProps<MaskElement>, 'ref'> & FactoryOpts;

export
type IMaskInputProps<
MaskElement extends InputMaskElement,
Props extends IMaskMixinProps<MaskElement>=IMaskMixinProps<MaskElement>,
> = ReactElementProps<MaskElement> & IMaskMixinProps<MaskElement, Props>;

type IMaskInputProps< MaskElement extends InputMaskElement> =
ReactElementProps<MaskElement> & IMaskMixinProps<MaskElement>;

type AnyProps = Record<string, unknown>;

export default
function IMaskMixin<
Expand All @@ -169,7 +173,7 @@ function IMaskMixin<
static propTypes: typeof MASK_PROPS;

declare element: MaskElement;
declare maskRef?: InputMask<MaskOpts<MaskElement, Props>>;
declare maskRef?: InputMask<ExtractMaskOpts<MaskElement, Props>>;

constructor (props: Props) {
super(props);
Expand Down Expand Up @@ -215,7 +219,7 @@ function IMaskMixin<
}
}

initMask (maskOptions: MaskOpts<MaskElement, Props> = this._extractMaskOptionsFromProps(this.props)) {
initMask (maskOptions: ExtractMaskOpts<MaskElement, Props> = this._extractMaskOptionsFromProps(this.props)) {
this.maskRef = IMask(this.element, maskOptions)
.on('accept', this._onAccept.bind(this))
.on('complete', this._onComplete.bind(this));
Expand All @@ -230,7 +234,7 @@ function IMaskMixin<
}
}

_extractMaskOptionsFromProps (props: Readonly<Props>): MaskOpts<MaskElement, Props> {
_extractMaskOptionsFromProps (props: Readonly<Props>): ExtractMaskOpts<MaskElement, Props> {
const { ...cloneProps }: Readonly<Props> = props;

// keep only mask options
Expand All @@ -240,7 +244,7 @@ function IMaskMixin<
delete cloneProps[nonMaskProp];
});

return cloneProps as MaskOpts<MaskElement, Props>;
return cloneProps as ExtractMaskOpts<MaskElement, Props>;
}

_extractNonMaskProps (props: Readonly<Props>): NonMaskProps<MaskElement, Props> {
Expand All @@ -255,18 +259,18 @@ function IMaskMixin<
return cloneProps as NonMaskProps<MaskElement, Props>;
}

get maskValue (): UnmaskValue<Props> {
if (!this.maskRef) return '' as UnmaskValue<Props>;
get maskValue (): UnmaskValue<ExtractReactMaskOpts<MaskElement, Props>> {
if (!this.maskRef) return '' as UnmaskValue<ExtractReactMaskOpts<MaskElement, Props>>;

if (this.props.unmask === 'typed') return this.maskRef.typedValue;
if (this.props.unmask) return this.maskRef.unmaskedValue;
return this.maskRef.value;
}

set maskValue (value: UnmaskValue<Props>) {
set maskValue (value: UnmaskValue<ExtractReactMaskOpts<MaskElement, Props>>) {
if (!this.maskRef) return;

value = (value == null && this.props.unmask !== 'typed' ? '' : value) as UnmaskValue<Props>;
value = (value == null && this.props.unmask !== 'typed' ? '' : value) as UnmaskValue<ExtractReactMaskOpts<MaskElement, Props>>;
if (this.props.unmask === 'typed') this.maskRef.typedValue = value;
else if (this.props.unmask) this.maskRef.unmaskedValue = value;
else this.maskRef.value = value;
Expand Down

0 comments on commit bab8604

Please sign in to comment.