Skip to content

Commit

Permalink
fix #876: use strict type checking
Browse files Browse the repository at this point in the history
  • Loading branch information
uNmAnNeR committed Jun 22, 2023
1 parent 27b8807 commit 37716e9
Show file tree
Hide file tree
Showing 43 changed files with 242 additions and 201 deletions.
4 changes: 4 additions & 0 deletions .eslintrc.cjs
@@ -1,5 +1,9 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
Expand Down
1 change: 0 additions & 1 deletion README.md
@@ -1,7 +1,6 @@
# imaskjs
vanilla javascript input mask

[![Build Status](https://travis-ci.com/uNmAnNeR/imaskjs.svg?branch=master)](https://travis-ci.com/uNmAnNeR/imaskjs)
[![Financial Contributors on Open Collective](https://opencollective.com/imask/all/badge.svg?label=financial+contributors)](https://opencollective.com/imask) [![Coverage Status](https://coveralls.io/repos/github/uNmAnNeR/imaskjs/badge.svg?branch=master)](https://coveralls.io/github/uNmAnNeR/imaskjs?branch=master)
[![npm version](https://badge.fury.io/js/imask.svg)](https://badge.fury.io/jas/imask)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -47,11 +47,12 @@
"lerna": "lerna",
"clean": "lerna clean",
"build": "npm run build:core && npm run build:plugins",
"lint": "lerna run lint",
"lint": "lerna run lint --stream",
"build:core": "lerna run build --scope=imask",
"build:react": "lerna run build --scope=react-imask",
"build:angular": "lerna run build --scope=angular-imask",
"build:solid": "lerna run build --scope=solid-imask",
"build:vue": "lerna run build --scope=vue-imask",
"build:svelte": "lerna run build --scope=@imask/svelte",
"build:plugins": "lerna run build --ignore=imask",
"test": "lerna run test --parallel",
Expand Down
4 changes: 2 additions & 2 deletions packages/angular-imask/package.json
Expand Up @@ -6,8 +6,8 @@
"description": "Angular IMask Plugin",
"repository": "https://github.com/uNmAnNeR/imaskjs/tree/master/packages/angular-imask",
"scripts": {
"lint": "eslint --quiet",
"prebuild": "npm run lint && rimraf --glob \"dist\"",
"lint": "eslint src",
"prebuild": "npm run lint -- --quiet && rimraf --glob \"dist\"",
"build": "ng-packagr --config tsconfig.json",
"dev": "ng build angular-imask --watch",
"example": "ng serve",
Expand Down
8 changes: 4 additions & 4 deletions packages/angular-imask/src/imask.directive.ts
@@ -1,9 +1,9 @@
import IMask, { type InputMask, type InputMaskElement, type FactoryArg } from 'imask';
import { type InputMask, type InputMaskElement, type FactoryArg, type NormalizedOpts } from 'imask';
import { isPlatformBrowser } from '@angular/common';
import {
Directive, ElementRef, Input, Output, forwardRef, Provider, Renderer2,
EventEmitter, OnDestroy, OnChanges, AfterViewInit,
SimpleChanges, PLATFORM_ID, inject, InjectionToken
SimpleChanges, PLATFORM_ID, inject
} from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, COMPOSITION_BUFFER_MODE } from '@angular/forms';
import { IMASK_FACTORY } from './imask-factory-token';
Expand Down Expand Up @@ -97,7 +97,7 @@ export class IMaskDirective<
if (!changes['imask'] || !this._viewInitialized) return;

if (this.imask) {
if (this.maskRef) this.maskRef.updateOptions(this.imask);
if (this.maskRef) this.maskRef.updateOptions(this.imask as Partial<NormalizedOpts<Opts>>);
else {
this.initMask();
this.onChange(this.maskValue);
Expand Down Expand Up @@ -157,7 +157,7 @@ export class IMaskDirective<
}

private initMask () {
this.maskRef = this._factory.create(this.element, this.imask)
this.maskRef = this._factory.create(this.element, this.imask as Opts)
.on('accept', this._onAccept.bind(this))
.on('complete', this._onComplete.bind(this));

Expand Down
4 changes: 2 additions & 2 deletions packages/imask/package.json
Expand Up @@ -16,8 +16,8 @@
"scripts": {
"test": "cross-env NODE_ENV=test c8 node --test-reporter=spec -r ts-node/register --test test/**/*",
"watch": "rollup -c -w",
"lint": "eslint --quiet",
"prebuild": "npm run lint && rimraf --glob \"{dist,esm}\"",
"lint": "eslint src",
"prebuild": "npm run lint -- --quiet && rimraf --glob \"{dist,esm}\"",
"build": "tsc --emitDeclarationOnly && rollup -c",
"coverage": "c8 report --reporter=lcov"
},
Expand Down
Expand Up @@ -6,7 +6,7 @@ export default
class HTMLContenteditableMaskElement extends HTMLMaskElement {
declare input: HTMLElement;
/** Returns HTMLElement selection start */
override get _unsafeSelectionStart (): number {
override get _unsafeSelectionStart (): number | null {
const root = this.rootElement;
const selection = root.getSelection && root.getSelection();
const anchorOffset = selection && selection.anchorOffset;
Expand All @@ -18,7 +18,7 @@ class HTMLContenteditableMaskElement extends HTMLMaskElement {
}

/** Returns HTMLElement selection end */
override get _unsafeSelectionEnd (): number {
override get _unsafeSelectionEnd (): number | null {
const root = this.rootElement;
const selection = root.getSelection && root.getSelection();
const anchorOffset = selection && selection.anchorOffset;
Expand Down Expand Up @@ -46,7 +46,7 @@ class HTMLContenteditableMaskElement extends HTMLMaskElement {

/** HTMLElement value */
override get value (): string {
return this.input.textContent;
return this.input.textContent || '';
}
override set value (value: string) {
this.input.textContent = value;
Expand Down
6 changes: 3 additions & 3 deletions packages/imask/src/controls/html-input-mask-element.ts
Expand Up @@ -17,12 +17,12 @@ class HTMLInputMaskElement extends HTMLMaskElement {
}

/** Returns InputElement selection start */
override get _unsafeSelectionStart (): number {
return this.input.selectionStart;
override get _unsafeSelectionStart (): number | null {
return this.input.selectionStart != null ? this.input.selectionStart : this.value.length;
}

/** Returns InputElement selection end */
override get _unsafeSelectionEnd (): number {
override get _unsafeSelectionEnd (): number | null {
return this.input.selectionEnd;
}

Expand Down
23 changes: 13 additions & 10 deletions packages/imask/src/controls/input.ts
@@ -1,6 +1,6 @@
import { objectIncludes, DIRECTION, type Selection } from '../core/utils';
import ActionDetails from '../core/action-details';
import createMask, { FactoryOpts, maskedClass, type FactoryArg, type FactoryReturnMasked } from '../masked/factory';
import createMask, { type NormalizedOpts, maskedClass, type FactoryArg, type FactoryReturnMasked } from '../masked/factory';
import Masked from '../masked/base';
import MaskElement from './mask-element';
import HTMLInputMaskElement, { type InputElement } from './html-input-mask-element';
Expand All @@ -11,6 +11,9 @@ import IMask from '../core/holder';
export
type InputMaskElement = MaskElement | InputElement | HTMLElement;

export
type InputMaskEventListener = (e?: InputEvent) => void;

/** Listens to element events and controls changes between element and {@link Masked} */
export default
class InputMask<Opts extends FactoryArg> {
Expand All @@ -22,7 +25,7 @@ class InputMask<Opts extends FactoryArg> {
/** Internal {@link Masked} model */
declare masked: FactoryReturnMasked<Opts>;

declare _listeners: Record<string, Array<EventListener>>;
declare _listeners: Record<string, Array<InputMaskEventListener>>;
declare _value: string;
declare _changingCursorPos: number;
declare _unmaskedValue: string;
Expand Down Expand Up @@ -62,7 +65,7 @@ class InputMask<Opts extends FactoryArg> {
return mask == null || this.masked?.maskEquals(mask);
}

/** Read or update mask */
/** Masked */
get mask (): FactoryReturnMasked<Opts>['mask'] {
return this.masked.mask;
}
Expand Down Expand Up @@ -142,7 +145,7 @@ class InputMask<Opts extends FactoryArg> {
}

/** Fires custom event */
_fireEvent (ev: string, e: InputEvent) {
_fireEvent (ev: string, e?: InputEvent) {
const listeners = this._listeners[ev];
if (!listeners) return;

Expand Down Expand Up @@ -203,15 +206,15 @@ class InputMask<Opts extends FactoryArg> {
if (isChanged) this._fireChangeEvents();
}

/** Updates options with deep equal check, recreates @{link Masked} model if mask type changes */
updateOptions<UpdateOpts extends Partial<Opts>> (opts: UpdateOpts extends FactoryOpts ? UpdateOpts : never) {
/** Updates options with deep equal check, recreates {@link Masked} model if mask type changes */
updateOptions(opts: Partial<NormalizedOpts<Opts>>) {
const { mask, ...restOpts } = opts;

const updateMask = !this.maskEquals(mask);
const updateOpts = !objectIncludes(this.masked, restOpts);

if (updateMask) this.mask = mask;
if (updateOpts) this.masked.updateOptions(restOpts as any); // TODO "any" no idea
if (updateOpts) this.masked.updateOptions(restOpts);

if (updateMask || updateOpts) this.updateControl();
}
Expand Down Expand Up @@ -262,14 +265,14 @@ class InputMask<Opts extends FactoryArg> {
}

/** Adds listener on custom event */
on (ev: string, handler: EventListener): this {
on (ev: string, handler: InputMaskEventListener): this {
if (!this._listeners[ev]) this._listeners[ev] = [];
this._listeners[ev].push(handler);
return this;
}

/** Removes custom event listener */
off (ev: string, handler: EventListener): this {
off (ev: string, handler: InputMaskEventListener): this {
if (!this._listeners[ev]) return this;
if (!handler) {
delete this._listeners[ev];
Expand Down Expand Up @@ -355,7 +358,7 @@ class InputMask<Opts extends FactoryArg> {
destroy () {
this._unbindEvents();
(this._listeners as any).length = 0;
delete this.el;
delete (this as any).el;
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/imask/src/controls/mask-element.ts
Expand Up @@ -14,9 +14,9 @@ type ElementEvent =
export default
abstract class MaskElement {
/** */
abstract _unsafeSelectionStart: number;
abstract _unsafeSelectionStart: number | null;
/** */
abstract _unsafeSelectionEnd: number;
abstract _unsafeSelectionEnd: number | null;
/** */
abstract value: string;

Expand Down
2 changes: 1 addition & 1 deletion packages/imask/src/core/utils.ts
Expand Up @@ -11,7 +11,7 @@ function isObject (obj: unknown): obj is object {
}

export
function pick<T, K extends keyof T, V> (
function pick<T extends object, K extends keyof T, V extends T[keyof T]> (
obj: T,
keys: K[] | ((v: V, k: K) => boolean),
): Pick<T, K> {
Expand Down
18 changes: 9 additions & 9 deletions packages/imask/src/masked/base.ts
Expand Up @@ -48,27 +48,27 @@ type MaskedOptions<M extends Masked=Masked, Props extends keyof M=never> = Parti
/** Provides common masking stuff */
export default
abstract class Masked<Value=any> {
static DEFAULTS: Partial<MaskedOptions> = {
static DEFAULTS: Record<string, any> = {
skipInvalid: true,
};
} satisfies Partial<MaskedOptions>;
static EMPTY_VALUES: Array<any> = [undefined, null, ''];

/** */
declare mask: unknown;
/** */
declare parent?: Masked;
/** Transforms value before mask processing */
declare prepare?: (chars: string, masked: this, flags: AppendFlags) => string | [string, ChangeDetails];
declare prepare?: (chars: string, masked: Masked, flags: AppendFlags) => string | [string, ChangeDetails];
/** Transforms each char before mask processing */
declare prepareChar?: (chars: string, masked: this, flags: AppendFlags) => string | [string, ChangeDetails];
declare prepareChar?: (chars: string, masked: Masked, flags: AppendFlags) => string | [string, ChangeDetails];
/** Validates if value is acceptable */
declare validate?: (value: string, masked: this, flags: AppendFlags) => boolean;
declare validate?: (value: string, masked: Masked, flags: AppendFlags) => boolean;
/** Does additional processing at the end of editing */
declare commit?: (value: string, masked: this) => void;
declare commit?: (value: string, masked: Masked) => void;
/** Format typed value to string */
declare format?: (value: Value, masked: this) => string;
declare format?: (value: Value, masked: Masked) => string;
/** Parse string to get typed value */
declare parse?: (str: string, masked: this) => Value;
declare parse?: (str: string, masked: Masked) => Value;
/** Enable characters overwriting */
abstract overwrite?: boolean | 'shift' | undefined;
/** */
Expand Down Expand Up @@ -422,7 +422,7 @@ abstract class Masked<Value=any> {

return value === tval ||
Masked.EMPTY_VALUES.includes(value) && Masked.EMPTY_VALUES.includes(tval) ||
this.format && this.format(value, this) === this.format(this.typedValue, this);
(this.format ? this.format(value, this) === this.format(this.typedValue, this) : false);
}
}

Expand Down
44 changes: 26 additions & 18 deletions packages/imask/src/masked/date.ts
Expand Up @@ -2,6 +2,7 @@ import MaskedPattern, { type MaskedPatternOptions } from './pattern';
import { type MaskedRangeOptions } from './range';
import MaskedRange from './range';
import IMask from '../core/holder';
import type Masked from './base';
import { type AppendFlags } from './base';
import { isString } from '../core/utils';

Expand All @@ -17,15 +18,18 @@ type DateOptionsKeys =
;

export
type MaskedDateOptions<Value=Date | null> =
Omit<MaskedPatternOptions<Value>, 'mask'> &
Partial<Pick<MaskedDate<Value>, DateOptionsKeys>> &
type DateValue = Date | null;

export
type MaskedDateOptions =
Omit<MaskedPatternOptions<DateValue>, 'mask'> &
Partial<Pick<MaskedDate, DateOptionsKeys>> &
{ mask?: string | DateMaskType }
;

/** Date mask */
export default
class MaskedDate<Value=Date | null> extends MaskedPattern<Value> {
class MaskedDate extends MaskedPattern<DateValue> {
static GET_DEFAULT_BLOCKS: () => { [k: string]: MaskedRangeOptions } = () => ({
d: {
mask: MaskedRange,
Expand All @@ -45,10 +49,10 @@ class MaskedDate<Value=Date | null> extends MaskedPattern<Value> {
to: 9999,
}
});
static DEFAULTS: Partial<MaskedPatternOptions<any, MaskedDate<any>, DateOptionsKeys>> = {
mask: Date as any,
static DEFAULTS: Record<string, any> = {
mask: Date,
pattern: 'd{.}`m{.}`Y',
format: (date: Date) => {
format: (date: DateValue, masked: Masked): string => {
if (!date) return '';

const day = String(date.getDate()).padStart(2, '0');
Expand All @@ -57,11 +61,11 @@ class MaskedDate<Value=Date | null> extends MaskedPattern<Value> {

return [day, month, year].join('.');
},
parse: (str: string) => {
parse: (str: string, masked: Masked): DateValue => {
const [day, month, year] = str.split('.').map(Number);
return new Date(year, month - 1, day);
},
};
} satisfies Partial<MaskedDateOptions>;

/** Pattern mask for date according to {@link MaskedDate#format} */
declare pattern: string;
Expand All @@ -71,10 +75,14 @@ class MaskedDate<Value=Date | null> extends MaskedPattern<Value> {
declare max?: Date;
/** */
declare autofix?: boolean | 'pad' | undefined;
/** Format typed value to string */
declare format: (value: DateValue, masked: Masked) => string;
/** Parse string to get typed value */
declare parse: (str: string, masked: Masked) => DateValue;

constructor (opts?: MaskedDateOptions<Value>) {
constructor (opts?: MaskedDateOptions) {
const { mask, pattern, ...patternOpts } = {
...(MaskedDate.DEFAULTS as MaskedDateOptions<Value>),
...(MaskedDate.DEFAULTS as MaskedDateOptions),
...opts,
};

Expand All @@ -84,11 +92,11 @@ class MaskedDate<Value=Date | null> extends MaskedPattern<Value> {
});
}

override updateOptions (opts: Partial<MaskedDateOptions<Value>>) {
super.updateOptions(opts as Partial<MaskedPatternOptions<Value>>);
override updateOptions (opts: Partial<MaskedDateOptions>) {
super.updateOptions(opts as Partial<MaskedPatternOptions<DateValue>>);
}

override _update (opts: Partial<MaskedDateOptions<Value>>) {
override _update (opts: Partial<MaskedDateOptions>) {
const { mask, pattern, blocks, ...patternOpts } = {
...MaskedDate.DEFAULTS,
...opts,
Expand Down Expand Up @@ -139,17 +147,17 @@ class MaskedDate<Value=Date | null> extends MaskedPattern<Value> {
}

/** Parsed Date */
get date (): Value {
get date (): DateValue {
return this.typedValue;
}
set date (date: Value) {
set date (date: DateValue) {
this.typedValue = date;
}

override get typedValue (): Value {
override get typedValue (): DateValue {
return this.isComplete ? super.typedValue : null;
}
override set typedValue (value: Value) {
override set typedValue (value: DateValue) {
super.typedValue = value;
}

Expand Down

0 comments on commit 37716e9

Please sign in to comment.