Skip to content

Commit

Permalink
feat(core): new built-in maskitoStrictCompositionPlugin (#881)
Browse files Browse the repository at this point in the history
  • Loading branch information
nsbarsukov authored Jan 10, 2024
1 parent c56b4ea commit 74dddd5
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 1 deletion.
1 change: 1 addition & 0 deletions projects/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export {
export {
maskitoInitialCalibrationPlugin,
maskitoPipe,
maskitoStrictCompositionPlugin,
maskitoTransform,
maskitoUpdateElement,
} from './lib/utils';
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 @@ -7,4 +7,5 @@ export * from './get-not-empty-selection';
export * from './get-word-selection';
export * from './initial-calibration-plugin';
export * from './pipe';
export * from './strict-composition-plugin';
export * from './transform';
33 changes: 33 additions & 0 deletions projects/core/src/lib/utils/strict-composition-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {ElementState, MaskitoPlugin, TypedInputEvent} from '../types';
import {maskitoUpdateElement} from './dom/update-element';
import {areElementStatesEqual} from './element-states-equality';
import {maskitoTransform} from './transform';

export function maskitoStrictCompositionPlugin(): MaskitoPlugin {
return (element, maskitoOptions) => {
const listener = (event: TypedInputEvent): void => {
if (event.inputType !== 'insertCompositionText') {
return;
}

const selection = [
element.selectionStart || 0,
element.selectionEnd || 0,
] as const;
const elementState: ElementState = {
selection,
value: element.value,
};
const validatedState = maskitoTransform(elementState, maskitoOptions);

if (!areElementStatesEqual(elementState, validatedState)) {
event.preventDefault();
maskitoUpdateElement(element, validatedState);
}
};

element.addEventListener('input', listener as EventListener);

return () => element.removeEventListener('input', listener as EventListener);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';

import mask from './mask';

@Component({
selector: 'plugins-strict-composition-doc-example-3',
template: `
<tui-input
[maskito]="maskitoOptions"
[style.max-width.rem]="20"
[(ngModel)]="value"
>
Enter number
</tui-input>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PluginsDocExample3 {
readonly maskitoOptions = mask;
value = '';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {MaskitoOptions, maskitoStrictCompositionPlugin} from '@maskito/core';

export default {
mask: /^[0-90-9]*$/,
plugins: [maskitoStrictCompositionPlugin()],
} as MaskitoOptions;
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ export class PluginsDocPageComponent {
),
'index.ts': import('./examples/2-initial-calibration/index.ts?raw'),
};

readonly strictCompositionExample: TuiDocExample = {
[DocExamplePrimaryTab.MaskitoOptions]: import(
'./examples/3-strict-composition/mask.ts?raw'
),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {TuiInputModule} from '@taiga-ui/kit';
import {NextStepsModule} from '../next-steps/next-steps.module';
import {PluginsDocExample1} from './examples/1-reject/component';
import {PluginsDocExample2} from './examples/2-initial-calibration/component';
import {PluginsDocExample3} from './examples/3-strict-composition/component';
import {PluginsDocPageComponent} from './plugins.component';

@NgModule({
Expand All @@ -24,7 +25,12 @@ import {PluginsDocPageComponent} from './plugins.component';
TuiAddonDocModule,
RouterModule.forChild(tuiGenerateRoutes(PluginsDocPageComponent)),
],
declarations: [PluginsDocPageComponent, PluginsDocExample1, PluginsDocExample2],
declarations: [
PluginsDocPageComponent,
PluginsDocExample1,
PluginsDocExample2,
PluginsDocExample3,
],
exports: [PluginsDocPageComponent],
})
export class PluginsDocPageModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,42 @@
<plugins-initial-calibration-doc-example-2></plugins-initial-calibration-doc-example-2>
</tui-doc-example>

<tui-doc-example
id="strict-composition"
heading="Built-in plugin for composition event"
[content]="strictCompositionExample"
[description]="strictCompositionDescription"
>
<ng-template #strictCompositionDescription>
<p class="tui-space_top-0">
By default,
<strong>Maskito</strong>
does not break IME Composition and waits until
<a
href="https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionend_event"
rel="noreferrer"
target="_blank"
tuiLink
>
<code>compositionend</code>
</a>
fires to begin calibration of the textfield's value. It is especially important for East Asian languages
such as Chinese, Japanese, Korean, and other languages with complex characters.
</p>

<p>
However, sometimes this behaviour is not desired and you can want to enable mask validation on every
keystroke (to be like a classic not-composition input). For example, some Android devices with enabled
system autocomplete can interpret user's input as part of composition event – waiting for
<code>compositionend</code>
can be not required for some cases (e.g. entering of numbers or your application is not used by East
Asian clients). For this cases, you can use
<code>maskitoStrictCompositionPlugin</code>
. It applies mask's constraints on ANY intermediate value of IME composition.
</p>
</ng-template>
<plugins-strict-composition-doc-example-3></plugins-strict-composition-doc-example-3>
</tui-doc-example>

<next-steps></next-steps>
</tui-doc-page>

0 comments on commit 74dddd5

Please sign in to comment.