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

fix(core): fix scroll for masked narrow textfields #1645

Merged
merged 1 commit into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 39 additions & 27 deletions projects/core/src/lib/mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export class Maskito extends MaskHistory {
...this.maskitoOptions,
};

private upcomingElementState: ElementState | null = null;

private readonly preprocessor = maskitoPipe(this.options.preprocessors);

private readonly postprocessor = maskitoPipe(this.options.postprocessors);
Expand Down Expand Up @@ -132,6 +134,17 @@ export class Maskito extends MaskHistory {
}
});

this.eventListener.listen(
'input',
() => {
if (this.upcomingElementState) {
this.updateElementState(this.upcomingElementState);
this.upcomingElementState = null;
}
},
{capture: true},
);

this.eventListener.listen('input', ({inputType}) => {
if (inputType === 'insertCompositionText') {
return; // will be handled inside `compositionend` event
Expand All @@ -154,17 +167,14 @@ export class Maskito extends MaskHistory {

protected updateElementState(
{value, selection}: ElementState,
eventInit: Pick<TypedInputEvent, 'data' | 'inputType'> = {
inputType: 'insertText',
data: null,
},
eventInit?: Pick<TypedInputEvent, 'data' | 'inputType'>,
): void {
const initialValue = this.elementState.value;

this.updateValue(value);
this.updateSelectionRange(selection);

if (initialValue !== value) {
if (eventInit && initialValue !== value) {
this.dispatchInputEvent(eventInit);
}
}
Expand Down Expand Up @@ -200,7 +210,10 @@ export class Maskito extends MaskHistory {
}

private ensureValueFitsMask(): void {
this.updateElementState(maskitoTransform(this.elementState, this.options));
this.updateElementState(maskitoTransform(this.elementState, this.options), {
inputType: 'insertText',
data: null,
});
}

private dispatchInputEvent(
Expand Down Expand Up @@ -261,32 +274,28 @@ export class Maskito extends MaskHistory {
return;
}

event.preventDefault();

if (
areElementValuesEqual(initialState, elementState, maskModel, newElementState)
) {
event.preventDefault();

// User presses Backspace/Delete for the fixed value
return this.updateSelectionRange(isForward ? [to, to] : [from, from]);
}

this.updateElementState(newElementState, {
inputType: event.inputType,
data: null,
});
this.updateHistory(newElementState);
this.upcomingElementState = newElementState;
}

private handleInsert(event: TypedInputEvent, data: string): void {
const initialElementState = this.elementState;
const {options, maxLength, element, elementState: initialElementState} = this;
const {elementState, data: insertedText = data} = this.preprocessor(
{
data,
elementState: initialElementState,
},
'insert',
);
const maskModel = new MaskModel(elementState, this.options);
const maskModel = new MaskModel(elementState, options);

try {
maskModel.addCharacters(elementState.selection, insertedText);
Expand All @@ -301,21 +310,24 @@ export class Maskito extends MaskHistory {
initialElementState.value.slice(to);
const newElementState = this.postprocessor(maskModel, initialElementState);

if (newElementState.value.length > this.maxLength) {
if (newElementState.value.length > maxLength) {
return event.preventDefault();
}

if (
newPossibleValue !== newElementState.value ||
this.element.isContentEditable
) {
event.preventDefault();

this.updateElementState(newElementState, {
data,
inputType: event.inputType,
});
this.updateHistory(newElementState);
if (newPossibleValue !== newElementState.value || element.isContentEditable) {
this.upcomingElementState = newElementState;

if (
options.overwriteMode === 'replace' &&
newPossibleValue.length > maxLength
) {
/**
* Browsers know nothing about Maskito and its `overwriteMode`.
* When textfield value length is already equal to attribute `maxlength`,
* pressing any key (even with valid value) does not emit `input` event.
*/
this.dispatchInputEvent({inputType: 'insertText', data});
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion projects/core/src/lib/utils/dom/event-listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export class EventListener {
const untypedFn = fn as (event: HTMLElementEventMap[E]) => unknown;

this.element.addEventListener<E>(eventType, untypedFn, options);
this.listeners.push(() => this.element.removeEventListener(eventType, untypedFn));
this.listeners.push(() =>
this.element.removeEventListener(eventType, untypedFn, options),
);
}

public destroy(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ describe('DateRange | Basic', () => {
it('Type `deleteSoftLineBackward` of `InputEvent` works', () => {
cy.get('@input')
.trigger('beforeinput', {inputType: 'deleteSoftLineBackward'})
.trigger('input', {inputType: 'deleteSoftLineBackward'})
.should('have.value', '')
.should('have.prop', 'selectionStart', ''.length)
.should('have.prop', 'selectionEnd', ''.length);
Expand All @@ -161,6 +162,7 @@ describe('DateRange | Basic', () => {
cy.get('@input')
.type('{moveToStart}')
.trigger('beforeinput', {inputType: 'deleteSoftLineForward'})
.trigger('input', {inputType: 'deleteSoftLineForward'})
.should('have.value', '')
.should('have.prop', 'selectionStart', ''.length)
.should('have.prop', 'selectionEnd', ''.length);
Expand All @@ -170,6 +172,7 @@ describe('DateRange | Basic', () => {
cy.get('@input')
.type('{leftArrow}'.repeat(' 31.12.2022'.length))
.trigger('beforeinput', {inputType: 'deleteSoftLineBackward'})
.trigger('input', {inputType: 'deleteSoftLineBackward'})
.should('have.value', '01.01.0001 – 31.12.2022')
.should('have.prop', 'selectionStart', ''.length)
.should('have.prop', 'selectionEnd', ''.length);
Expand All @@ -179,6 +182,7 @@ describe('DateRange | Basic', () => {
cy.get('@input')
.type('{leftArrow}'.repeat(' 31.12.2022'.length))
.trigger('beforeinput', {inputType: 'deleteSoftLineForward'})
.trigger('input', {inputType: 'deleteSoftLineForward'})
.should('have.value', '20.01.1990')
.should('have.prop', 'selectionStart', '20.01.1990'.length)
.should('have.prop', 'selectionEnd', '20.01.1990'.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ describe('Date', () => {
cy.get('@input')
.type('{moveToStart}')
.trigger('beforeinput', {inputType: 'deleteSoftLineForward'})
.trigger('input', {inputType: 'deleteSoftLineForward'})
.should('have.value', '')
.should('have.prop', 'selectionStart', ''.length)
.should('have.prop', 'selectionEnd', ''.length);
Expand All @@ -135,6 +136,7 @@ describe('Date', () => {
it('Type `deleteSoftLineBackward` of `InputEvent` works', () => {
cy.get('@input')
.trigger('beforeinput', {inputType: 'deleteSoftLineBackward'})
.trigger('input', {inputType: 'deleteSoftLineBackward'})
.should('have.value', '')
.should('have.prop', 'selectionStart', ''.length)
.should('have.prop', 'selectionEnd', ''.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('Textarea (mask latin letters + digits)', () => {
.type('{enter}')
.type('UI and Maskito')
.trigger('beforeinput', {inputType: 'deleteSoftLineBackward'})
.trigger('input', {inputType: 'deleteSoftLineBackward'})
.should('have.value', 'Taiga\n');
});

Expand All @@ -58,6 +59,7 @@ describe('Textarea (mask latin letters + digits)', () => {
.type('UI and Maskito')
.type('{moveToStart}')
.trigger('beforeinput', {inputType: 'deleteSoftLineForward'})
.trigger('input', {inputType: 'deleteSoftLineForward'})
.should('have.value', 'UI and Maskito');
});
});
Expand Down
Loading