Skip to content

Commit

Permalink
feat(core): new utility maskitoTransform(value, maskitoOptions) (#177)
Browse files Browse the repository at this point in the history
  • Loading branch information
nsbarsukov authored Mar 7, 2023
1 parent cd52fad commit 20316f1
Show file tree
Hide file tree
Showing 15 changed files with 277 additions and 16 deletions.
2 changes: 1 addition & 1 deletion projects/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export {MASKITO_DEFAULT_OPTIONS} from './lib/constants';
export {Maskito} from './lib/mask';
export {MaskitoOptions} from './lib/types';
export {maskitoPipe} from './lib/utils';
export {maskitoPipe, maskitoTransform} from './lib/utils';
15 changes: 2 additions & 13 deletions projects/core/src/lib/mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
getNotEmptySelection,
isBeforeInputEventSupported,
isEventProducingCharacter,
maskitoTransform,
} from './utils';

export class Maskito extends MaskHistory {
Expand Down Expand Up @@ -162,19 +163,7 @@ export class Maskito extends MaskHistory {
}

private conformValueToMask(): void {
const initialElementState = this.elementState;
const {elementState} = this.options.preprocessor(
{
elementState: initialElementState,
data: '',
},
'validation',
);
const maskModel = new MaskModel(elementState, this.options);
const {value, selection} = this.options.postprocessor(
maskModel,
initialElementState,
);
const {value, selection} = maskitoTransform(this.elementState, this.options);

this.updateValue(value);
this.updateSelectionRange(selection);
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 @@ -5,3 +5,4 @@ export * from './element-states-equality';
export * from './get-not-empty-selection';
export * from './identity';
export * from './pipe';
export * from './transform';
76 changes: 76 additions & 0 deletions projects/core/src/lib/utils/test/transform.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {MaskitoOptions} from '../../types';
import {maskitoTransform} from '../transform';

describe('maskitoTransform', () => {
const maskitoOptions: MaskitoOptions = {
mask: /^\d+(,\d*)?$/,
preprocessor: ({elementState, data}) => {
const {value, selection} = elementState;

return {
elementState: {
selection,
value: value.replace('.', ','),
},
data: data.replace('.', ','),
};
},
postprocessor: ({value, selection}) => {
const newValue = value.replace(/^0+/, '0');
const deletedChars = value.length - newValue.length;
const [from, to] = selection;

return {value: newValue, selection: [from - deletedChars, to - deletedChars]};
},
};

it('returns string if the first argument is a string', () => {
expect(typeof maskitoTransform('100', maskitoOptions)).toBe('string');
});

it('returns ElementState if the first argument is a ElementState', () => {
const res = maskitoTransform({value: '', selection: [0, 0]}, maskitoOptions);

expect(res).toBeTruthy();
expect(typeof res).toBe('object');
});

it('formats value using preprocessor', () => {
expect(maskitoTransform('100.42', maskitoOptions)).toBe('100,42');
expect(
maskitoTransform(
{
value: '100.42',
selection: [2, 2],
},
maskitoOptions,
),
).toEqual({value: '100,42', selection: [2, 2]});
});

it('formats value using postprocessor', () => {
expect(maskitoTransform('0000,1234', maskitoOptions)).toBe('0,1234');
expect(
maskitoTransform(
{
value: '0000,1234',
selection: [6, 6],
},
maskitoOptions,
),
).toEqual({value: '0,1234', selection: [3, 3]});
});

it('drops invalid characters (mask expression works)', () => {
expect(maskitoTransform('42Taiga UI42', maskitoOptions)).toBe('4242');
expect(
maskitoTransform(
{
value: '42Taiga UI42',
selection: [11, 11],
},
maskitoOptions,
),
).toEqual({value: '4242', selection: [3, 3]});
});
});
32 changes: 32 additions & 0 deletions projects/core/src/lib/utils/transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {MaskModel} from '../classes';
import {MASKITO_DEFAULT_OPTIONS} from '../constants';
import {ElementState, MaskitoOptions} from '../types';

export function maskitoTransform(value: string, maskitoOptions: MaskitoOptions): string;
export function maskitoTransform(
state: ElementState,
maskitoOptions: MaskitoOptions,
): ElementState;

export function maskitoTransform(
valueOrState: ElementState | string,
maskitoOptions: MaskitoOptions,
): ElementState | string {
const options: Required<MaskitoOptions> = {
...MASKITO_DEFAULT_OPTIONS,
...maskitoOptions,
};
const initialElementState: ElementState =
typeof valueOrState === 'string'
? {value: valueOrState, selection: [0, 0]}
: valueOrState;

const {elementState} = options.preprocessor(
{elementState: initialElementState, data: ''},
'validation',
);
const maskModel = new MaskModel(elementState, options);
const {value, selection} = options.postprocessor(maskModel, initialElementState);

return typeof valueOrState === 'string' ? value : {value, selection};
}
10 changes: 10 additions & 0 deletions projects/demo/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ export const appRoutes: Routes = [
title: `Overwrite mode`,
},
},
{
path: DemoPath.Transformer,
loadChildren: async () =>
import(`../pages/documentation/transformer/transformer.module`).then(
m => m.TransformerDocPageModule,
),
data: {
title: `Transformer`,
},
},
// Kit
{
path: DemoPath.Number,
Expand Down
1 change: 1 addition & 0 deletions projects/demo/src/app/demo-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const enum DemoPath {
MaskExpression = 'core-concepts/mask-expression',
Processors = 'core-concepts/processors',
OverwriteMode = 'core-concepts/overwrite-mode',
Transformer = 'core-concepts/transformer',
Number = 'kit/number',
Time = 'kit/time',
Date = 'kit/date',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export class CoreConceptsOverviewDocPageComponent {
readonly maskExpressionDocPage = `/${DemoPath.MaskExpression}`;
readonly processorsDocPage = `/${DemoPath.Processors}`;
readonly overwriteModeDocPage = `/${DemoPath.OverwriteMode}`;
readonly transformerDocPage = `/${DemoPath.Transformer}`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
.islands-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
justify-content: center;
gap: 2rem;

@media @tui-mobile {
Expand All @@ -13,5 +13,9 @@

.island {
flex: 1;
min-width: 10rem;
min-width: 14rem;

@media @tui-desktop-min {
max-width: 40%;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ <h3 class="tui-island__title">Overwrite mode</h3>
parameter.
</p>
</a>

<a
tuiIsland
class="island"
[hoverable]="true"
[routerLink]="transformerDocPage"
>
<h3 class="tui-island__title">Transformer</h3>
<p class="tui-island__paragraph">
Learn how to correctly programatically update
element's value via
<code>maskitoTransform</code>
.
</p>
</a>
</div>
</section>
</tui-doc-page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
```ts
import {maskitoTransform} from '@maskito/core';
import {maskitoNumberOptionsGenerator} from '@maskito/kit';

const maskitoOptions = maskitoNumberOptionsGenerator({
thousandSeparator: ' ',
});

const definitelyValidValue = maskitoTransform('1000000', maskitoOptions);

console.info(definitelyValidValue); // '1 000 000'

// Framework agnostic way | index.ts
inputElement.value = definitelyValidValue;

// Angular way | app.component.ts
this.formControl.patchValue(definitelyValidValue);
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {DemoPath} from '@demo/path';

@Component({
selector: 'transformer-doc-page',
templateUrl: './transformer.template.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TransformerDocPageComponent {
readonly maskExpressionDocPage = `/${DemoPath.MaskExpression}`;
readonly processorsDocPage = `/${DemoPath.Processors}`;
readonly overwriteModeDocPage = `/${DemoPath.OverwriteMode}`;

readonly utilityInActionDemo = import('./examples/utility-in-action-demo.md?raw');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {TuiAddonDocModule, tuiGenerateRoutes} from '@taiga-ui/addon-doc';
import {TuiLinkModule, TuiNotificationModule} from '@taiga-ui/core';

import {TransformerDocPageComponent} from './transformer.component';

@NgModule({
imports: [
CommonModule,
TuiAddonDocModule,
TuiLinkModule,
TuiNotificationModule,
RouterModule.forChild(tuiGenerateRoutes(TransformerDocPageComponent)),
],
declarations: [TransformerDocPageComponent],
exports: [TransformerDocPageComponent],
})
export class TransformerDocPageModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<tui-doc-page
header="Transformer"
package="CORE"
>
<section>
<p class="tui-space_top-0">
<strong>Maskito</strong>
libraries were created to prevent user from typing invalid value.
<br />
<strong>Maskito</strong>
listens
<code>beforeinput</code>
and
<code>input</code>
events. Programatic (by developer) changes of input's value don't
trigger these events!
</p>

<tui-notification>
<strong>Maskito</strong>
is based on the assumption that developer is capable to
programatically patch input with
<u>valid</u>
value!
</tui-notification>

<p>
If you need to programatically patch input's value but you are not
sure that your value is valid (for example, you get it from the
server), you should use
<code>maskitoTransform</code>
utility .
</p>
</section>

<tui-doc-code [code]="utilityInActionDemo"></tui-doc-code>

<section class="tui-space_top-12">
<h2>Next steps</h2>

<p>
The following sections are recommended to explore core concepts
further:
</p>

<ul class="tui-list">
<li class="tui-list__item">
<a
tuiLink
[routerLink]="maskExpressionDocPage"
>
Mask expression
</a>
</li>
<li class="tui-list__item">
<a
tuiLink
[routerLink]="processorsDocPage"
>
Processors
</a>
</li>
<li class="tui-list__item">
<a
tuiLink
[routerLink]="overwriteModeDocPage"
>
Overwrite mode
</a>
</li>
</ul>
</section>
</tui-doc-page>
6 changes: 6 additions & 0 deletions projects/demo/src/pages/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export const DEMO_PAGES: TuiDocPages = [
route: DemoPath.OverwriteMode,
keywords: 'core, concepts, overwrite, mode, shift, replace',
},
{
section: 'Core concepts',
title: 'Transformer',
route: DemoPath.Transformer,
keywords: 'core, concepts, programmatic, patch, set, update, value',
},
{
section: 'Kit',
title: 'Number',
Expand Down

0 comments on commit 20316f1

Please sign in to comment.