Skip to content

Commit

Permalink
feat(core): new built-in maskitoScrollPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
nsbarsukov committed Sep 18, 2024
1 parent 1c31456 commit ea7c22a
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 0 deletions.
1 change: 1 addition & 0 deletions projects/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export {Maskito} from './lib/mask';
export {
maskitoChangeEventPlugin,
maskitoInitialCalibrationPlugin,
maskitoScrollPlugin,
maskitoStrictCompositionPlugin,
} from './lib/plugins';
export type {
Expand Down
1 change: 1 addition & 0 deletions projects/core/src/lib/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './change-event-plugin';
export * from './initial-calibration-plugin';
export * from './scroll-plugin';
export * from './strict-composition-plugin';
49 changes: 49 additions & 0 deletions projects/core/src/lib/plugins/scroll-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type {MaskitoPlugin} from '../types';

/**
* Helper to scroll narrow textfield after programmatic updates of masked element.
* It is rather hacky solution – that's why it should be enabled manually at your own risk.
* ___
* There are 2 issues solved by this plugin:
*
* - Case 1. Programmatic update of `element.value`
* DOM contains already focused <input />.
* It is narrow (only 3 characters can be fit inside it without scroll).
* ```
* const element = document.querySelector('input');
*
* element.value = '12345';
* // caret is placed after the last digit, but it is not visible (no scroll happens)
* ```
*
* - Case 2. Programmatic execution of `element.setSelectionRange`
* DOM contains already focused <input value="12345" />.
* It is narrow (only 3 characters can be fit inside it without scroll).
* Caret is placed at the beginning.
* ```
* const element = document.querySelector('input');
*
* input.setSelectionRange(5, 5);
* // caret is placed after the last digit, but it is not visible (no scroll happens)
* ```
* ___
* - {@link https://issues.chromium.org/issues/41081857 Open Chromium bug from 2014}
*/
export function maskitoScrollPlugin(): MaskitoPlugin {
return (element) => {
const document = element.ownerDocument;

const listener = (): void => {
if (element.matches(':focus') && element.scrollWidth > element.clientWidth) {
element.blur();
element.focus();
}
};

document.addEventListener('selectionchange', listener, {passive: true});

return () => {
document.removeEventListener('selectionchange', listener);
};
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {MaskitoDirective} from '@maskito/angular';
import {TuiTextfield} from '@taiga-ui/core';

import mask from './mask';

@Component({
standalone: true,
selector: 'plugins-scroll-doc-example-5',
imports: [FormsModule, MaskitoDirective, TuiTextfield],
template: `
<tui-textfield
[style.max-width.rem]="5"
[tuiTextfieldCleaner]="false"
>
<label tuiLabel>Price</label>
<input
tuiTextfield
[maskito]="maskitoOptions"
[(ngModel)]="value"
/>
</tui-textfield>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PluginsDocExample5 {
protected readonly maskitoOptions = mask;
protected value = '$1 234';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type {MaskitoOptions} from '@maskito/core';
import {maskitoScrollPlugin} from '@maskito/core';
import {maskitoNumberOptionsGenerator} from '@maskito/kit';

const numberOptions = maskitoNumberOptionsGenerator({
precision: 2,
prefix: '$',
});

export default {
...numberOptions,
plugins: [
...numberOptions.plugins,
maskitoScrollPlugin(), // <--- Enable it
],
} satisfies MaskitoOptions;
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {PluginsDocExample1} from './examples/1-reject/component';
import {PluginsDocExample2} from './examples/2-initial-calibration/component';
import {PluginsDocExample3} from './examples/3-strict-composition/component';
import {PluginsDocExample4} from './examples/4-change-event/component';
import {PluginsDocExample5} from './examples/5-scroll/component';

@Component({
standalone: true,
Expand All @@ -20,6 +21,7 @@ import {PluginsDocExample4} from './examples/4-change-event/component';
PluginsDocExample2,
PluginsDocExample3,
PluginsDocExample4,
PluginsDocExample5,
RouterLink,
TuiAddonDoc,
TuiLink,
Expand Down Expand Up @@ -55,4 +57,8 @@ export default class PluginsDocPageComponent {
'./examples/4-change-event/mask.ts?raw'
),
};

protected readonly scrollExample: Record<string, TuiRawLoaderContent> = {
[DocExamplePrimaryTab.MaskitoOptions]: import('./examples/5-scroll/mask.ts?raw'),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,50 @@
<plugins-change-event-doc-example-4 />
</tui-doc-example>

<tui-doc-example
id="scroll"
heading="Built-in plugin for scroll narrow textfield"
[content]="scrollExample"
[description]="scrollDescription"
>
<ng-template #scrollDescription>
<p class="tui-space_top-0">
Chromium-based browsers has known
<a
href="https://issues.chromium.org/issues/41081857"
target="_blank"
tuiLink
>
issue
</a>
that they does not scroll textfield's element after programmatic changes of caret position. It causes
problems for narrow textfields when they are overflown by their value. Unfortunately, there are no
silver bullet for it – only some hacky solutions (we put them all inside
<code>maskitoScrollPlugin</code>
). Enable this plugin if you have narrow masked textfield value.
</p>

<tui-notification
appearance="warning"
size="m"
>
<div>
This plugin sometimes trigger
<strong>fake</strong>
<code>blur</code>
&
<code>focus</code>
events as workaround to mentioned browser restrictions.

<p class="tui-space_bottom-0">
<strong>Be careful</strong>
and double check everything especially if you listen to these events from on the textfield!
</p>
</div>
</tui-notification>
</ng-template>
<plugins-scroll-doc-example-5 />
</tui-doc-example>

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

0 comments on commit ea7c22a

Please sign in to comment.