Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core)!: remove value's calibration on initialization + new maskitoInitialCalibrationPlugin #778

Merged
merged 7 commits into from
Dec 15, 2023
6 changes: 5 additions & 1 deletion projects/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ export {
MaskitoPostprocessor,
MaskitoPreprocessor,
} from './lib/types';
export {maskitoPipe, maskitoTransform} from './lib/utils';
export {
maskitoInitialCalibrationPlugin,
maskitoPipe,
maskitoTransform,
} from './lib/utils';
3 changes: 1 addition & 2 deletions projects/core/src/lib/mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export class Maskito extends MaskHistory {
private readonly maskitoOptions: MaskitoOptions,
) {
super();
this.ensureValueFitsMask();
this.updateHistory(this.elementState);

this.eventListener.listen('keydown', event => {
Expand Down Expand Up @@ -176,7 +175,7 @@ export class Maskito extends MaskHistory {
data: null,
},
): void {
if (globalThis?.InputEvent) {
if (globalThis.InputEvent) {
this.element.dispatchEvent(
new InputEvent('input', {
...eventInit,
Expand Down
1 change: 1 addition & 0 deletions projects/core/src/lib/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export * from './element-states-equality';
export * from './get-line-selection';
export * from './get-not-empty-selection';
export * from './get-word-selection';
export * from './initial-calibration-plugin';
export * from './pipe';
export * from './transform';
15 changes: 15 additions & 0 deletions projects/core/src/lib/utils/initial-calibration-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {MaskitoOptions, MaskitoPlugin} from '../types';
import {maskitoTransform} from './transform';

export function maskitoInitialCalibrationPlugin(
customOptions?: MaskitoOptions,
): MaskitoPlugin {
return (element, options) => {
const from = element.selectionStart || 0;
const to = element.selectionEnd || 0;

element.value = maskitoTransform(element.value, customOptions || options);
element.dispatchEvent(new Event('input'));
element.setSelectionRange?.(from, to);
};
}
5 changes: 2 additions & 3 deletions projects/demo/src/pages/cypress/cypress.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {CypressDocPageComponent} from './cypress.component';
import {TestDocExample1} from './examples/1-predicate/component';
import {TestDocExample2} from './examples/2-native-max-length/component';
import {TestDocExample3} from './examples/3-mirrored-prefix-postfix/component';
import {TestDocExample4, TestPipe4} from './examples/4-runtime-postfix-changes/component';
import {TestDocExample4} from './examples/4-runtime-postfix-changes/component';
import {TestDocExample5} from './examples/5-react-async-predicate/angular-wrapper';
import {TestDocExample6} from './examples/6-multi-character-prefix/component';

Expand All @@ -25,14 +25,13 @@ import {TestDocExample6} from './examples/6-multi-character-prefix/component';
TuiGroupModule,
TuiAddonDocModule,
RouterModule.forChild(tuiGenerateRoutes(CypressDocPageComponent)),
TestDocExample4,
],
declarations: [
CypressDocPageComponent,
TestDocExample1,
TestDocExample2,
TestDocExample3,
TestDocExample4,
TestPipe4,
TestDocExample5,
TestDocExample6,
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,47 @@
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Pipe,
PipeTransform,
ViewChild,
} from '@angular/core';
import {MaskitoOptions} from '@maskito/core';
import {I18nPluralPipe} from '@angular/common';
import {ChangeDetectionStrategy, Component, Pipe, PipeTransform} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {MaskitoDirective} from '@maskito/angular';
import {maskitoInitialCalibrationPlugin, MaskitoOptions} from '@maskito/core';
import {maskitoNumberOptionsGenerator, maskitoParseNumber} from '@maskito/kit';

@Pipe({
standalone: true,
name: 'calculateMask',
})
export class TestPipe4 implements PipeTransform {
transform(postfix: string): MaskitoOptions {
return maskitoNumberOptionsGenerator({
const options = maskitoNumberOptionsGenerator({
postfix,
precision: 2,
thousandSeparator: ' ',
});

return {
...options,
plugins: [...options.plugins, maskitoInitialCalibrationPlugin()],
};
}
}

@Component({
standalone: true,
selector: 'test-doc-example-4',
imports: [FormsModule, I18nPluralPipe, MaskitoDirective, TestPipe4],
template: `
<input
#inputRef
placeholder="Enter number"
value="1 year"
[maskito]="parsedValue | i18nPlural: pluralize | calculateMask"
[(ngModel)]="value"
/>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TestDocExample4 {
@ViewChild('inputRef', {read: ElementRef, static: true})
readonly inputRef!: ElementRef<HTMLInputElement>;
value = '1 year';

get parsedValue(): number {
return maskitoParseNumber(this.inputRef.nativeElement.value);
return maskitoParseNumber(this.value);
}

readonly pluralize = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// @ts-nocheck React & Vue Global JSX Types Conflicts
// TODO: Check if it still required after upgrade Vue to 3.4 (https://github.com/vuejs/core/pull/7958)
import {MaskitoElementPredicate} from '@maskito/core';
import {MaskitoElementPredicate, maskitoInitialCalibrationPlugin, MaskitoOptions} from '@maskito/core';
import {maskitoTimeOptionsGenerator} from '@maskito/kit';
import {useMaskito} from '@maskito/react';
import {forwardRef, useEffect, useState} from 'react';

const options = maskitoTimeOptionsGenerator({
const timeOptions = maskitoTimeOptionsGenerator({
mode: 'HH:MM',
});

const options: MaskitoOptions = {
...timeOptions,
plugins: [...timeOptions.plugins, maskitoInitialCalibrationPlugin()],
};

const correctPredicate: MaskitoElementPredicate = host => host.querySelector('.real-input')!;
const wrongPredicate: MaskitoElementPredicate = host => host.querySelector('input')!;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';

import mask from './mask';

@Component({
selector: 'plugins-initial-calibration-doc-example-2',
template: `
<tui-input
[style.max-width.rem]="20"
[(ngModel)]="value"
>
Enter number

<input
tuiTextfield
[maskito]="maskitoOptions"
/>
</tui-input>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PluginsDocExample2 {
maskitoOptions = mask;
value = '12345';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Maskito} from '@maskito/core';

import maskitoOptions from './mask';

const element = document.querySelector('input')!;

element.value = '12345'; // patch with invalid initial value

// enable mask
const maskedInput = new Maskito(element, maskitoOptions);

console.info(element.value); // 123

// Call this function when the element is detached from DOM
maskedInput.destroy();
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {maskitoInitialCalibrationPlugin, MaskitoOptions} from '@maskito/core';

const maskitoOptions: MaskitoOptions = {
mask: /^\d{0,3}$/,
plugins: [maskitoInitialCalibrationPlugin()],
};

export default maskitoOptions;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {DocExamplePrimaryTab} from '@demo/constants';
import {DemoPath, DocExamplePrimaryTab} from '@demo/constants';
import {TuiDocExample} from '@taiga-ui/addon-doc';

@Component({
Expand All @@ -8,9 +8,18 @@ import {TuiDocExample} from '@taiga-ui/addon-doc';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PluginsDocPageComponent {
readonly transformerDocPage = `/${DemoPath.Transformer}`;

readonly rejectExample: TuiDocExample = {
[DocExamplePrimaryTab.MaskitoOptions]: import('./examples/reject/mask.ts?raw'),
'index.less': import('./examples/reject/animation.less?raw'),
'index.ts': import('./examples/reject/index.ts?raw'),
[DocExamplePrimaryTab.MaskitoOptions]: import('./examples/1-reject/mask.ts?raw'),
'index.less': import('./examples/1-reject/animation.less?raw'),
'index.ts': import('./examples/1-reject/index.ts?raw'),
};

readonly initialCalibrationExample: TuiDocExample = {
[DocExamplePrimaryTab.MaskitoOptions]: import(
'./examples/2-initial-calibration/mask.ts?raw'
),
'index.ts': import('./examples/2-initial-calibration/index.ts?raw'),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {TuiLinkModule, TuiNotificationModule} from '@taiga-ui/core';
import {TuiInputModule} from '@taiga-ui/kit';

import {NextStepsModule} from '../next-steps/next-steps.module';
import {PluginsDocExample1} from './examples/reject/component';
import {PluginsDocExample1} from './examples/1-reject/component';
import {PluginsDocExample2} from './examples/2-initial-calibration/component';
import {PluginsDocPageComponent} from './plugins.component';

@NgModule({
Expand All @@ -23,7 +24,7 @@ import {PluginsDocPageComponent} from './plugins.component';
TuiAddonDocModule,
RouterModule.forChild(tuiGenerateRoutes(PluginsDocPageComponent)),
],
declarations: [PluginsDocPageComponent, PluginsDocExample1],
declarations: [PluginsDocPageComponent, PluginsDocExample1, PluginsDocExample2],
exports: [PluginsDocPageComponent],
})
export class PluginsDocPageModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,30 @@
<plugins-reject-doc-example-1></plugins-reject-doc-example-1>
</tui-doc-example>

<tui-doc-example
id="initial-calibration"
heading="Built-in initial calibration plugin"
[content]="initialCalibrationExample"
[description]="initialCalibrationDescription"
>
<ng-template #initialCalibrationDescription>
<strong>Maskito</strong>
libraries were created to prevent
<u>only user</u>
from typing invalid value. However, sometimes you (developer) need to enable mask but you not sure that you
programmatically patched textfield with valid value. In this case you can use
<a
tuiLink
[routerLink]="transformerDocPage"
>
<code>maskitoTransform</code>
</a>
or just add
<code>maskitoInitialCalibrationPlugin</code>
to mask options.
</ng-template>
<plugins-initial-calibration-doc-example-2></plugins-initial-calibration-doc-example-2>
</tui-doc-example>

<next-steps></next-steps>
</tui-doc-page>
8 changes: 7 additions & 1 deletion projects/react/src/lib/tests/elementPredicate.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import {MASKITO_DEFAULT_ELEMENT_PREDICATE, MaskitoElementPredicate, MaskitoOptions} from '@maskito/core';
import {
MASKITO_DEFAULT_ELEMENT_PREDICATE,
MaskitoElementPredicate,
maskitoInitialCalibrationPlugin,
MaskitoOptions,
} from '@maskito/core';
import {render, RenderResult, waitFor} from '@testing-library/react';
import userEvent from '@testing-library/user-event';

Expand All @@ -7,6 +12,7 @@ import {useMaskito} from '../useMaskito';
describe('@maskito/react | `elementPredicate` property', () => {
const options: MaskitoOptions = {
mask: /^\d+$/,
plugins: [maskitoInitialCalibrationPlugin()],
};
let predicate: MaskitoElementPredicate = MASKITO_DEFAULT_ELEMENT_PREDICATE;

Expand Down
2 changes: 2 additions & 0 deletions projects/vue/src/lib/maskito.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {maskitoInitialCalibrationPlugin} from '@maskito/core';
import {maskito} from '@maskito/vue';
import {mount} from '@vue/test-utils';

Expand All @@ -12,6 +13,7 @@ describe('Maskito Vue package', () => {
' ',
...new Array(4).fill(/\d/),
],
plugins: [maskitoInitialCalibrationPlugin()],
};
const component = {
template: '<input v-model="value" v-maskito="options" />',
Expand Down