From 4495942cf21a091c9a8db0190b9021b8d045066d Mon Sep 17 00:00:00 2001 From: Nikita Barsukov Date: Tue, 12 Dec 2023 15:50:26 +0300 Subject: [PATCH] feat!: drop legacy fallbacks for `Firefox` < 91 (#756) --- projects/core/src/lib/mask.ts | 165 ++++++------------ .../dom/is-before-input-event-supported.ts | 16 -- .../utils/dom/is-event-producing-character.ts | 11 -- .../core/src/lib/utils/get-word-selection.ts | 4 +- projects/core/src/lib/utils/index.ts | 2 - .../src/lib/utils/date/parse-date-string.ts | 3 +- .../utils/date/raise-segment-value-to-min.ts | 3 +- .../src/lib/utils/get-object-from-entries.ts | 16 -- projects/kit/src/lib/utils/index.ts | 1 - .../src/lib/utils/time/pad-time-segments.ts | 3 +- .../src/lib/utils/time/parse-time-string.ts | 3 +- tsconfig.json | 2 +- 12 files changed, 65 insertions(+), 164 deletions(-) delete mode 100644 projects/core/src/lib/utils/dom/is-before-input-event-supported.ts delete mode 100644 projects/core/src/lib/utils/dom/is-event-producing-character.ts delete mode 100644 projects/kit/src/lib/utils/get-object-from-entries.ts diff --git a/projects/core/src/lib/mask.ts b/projects/core/src/lib/mask.ts index ff26005f8..c5b160c26 100644 --- a/projects/core/src/lib/mask.ts +++ b/projects/core/src/lib/mask.ts @@ -7,8 +7,6 @@ import { getLineSelection, getNotEmptySelection, getWordSelection, - isBeforeInputEventSupported, - isEventProducingCharacter, isRedo, isUndo, maskitoPipe, @@ -53,73 +51,58 @@ export class Maskito extends MaskHistory { } }); - if (isBeforeInputEventSupported(element)) { - this.eventListener.listen('beforeinput', event => { - const isForward = event.inputType.includes('Forward'); - - this.updateHistory(this.elementState); - - switch (event.inputType) { - // historyUndo/historyRedo will not be triggered if value was modified programmatically - case 'historyUndo': - event.preventDefault(); - - return this.undo(); - case 'historyRedo': - event.preventDefault(); - - return this.redo(); - case 'deleteByCut': - case 'deleteContentBackward': - case 'deleteContentForward': - return this.handleDelete({ - event, - isForward, - selection: getNotEmptySelection(this.elementState, isForward), - }); - case 'deleteWordForward': - case 'deleteWordBackward': - return this.handleDelete({ - event, - isForward, - selection: getWordSelection(this.elementState, isForward), - force: true, - }); - case 'deleteSoftLineBackward': - case 'deleteSoftLineForward': - case 'deleteHardLineBackward': - case 'deleteHardLineForward': - return this.handleDelete({ - event, - isForward, - selection: getLineSelection(this.elementState, isForward), - force: true, - }); - case 'insertCompositionText': - return; // will be handled inside `compositionend` event - case 'insertLineBreak': - return this.handleEnter(event); - case 'insertFromPaste': - case 'insertText': - case 'insertFromDrop': - default: - return this.handleInsert(event, event.data || ''); - } - }); - } else { - /** TODO: drop it after browser support bump (Firefox 87+) - * Also, replace union types `Event | TypedInputEvent` with `TypedInputEvent` inside: - *** {@link handleDelete} - *** {@link handleInsert} - */ - this.eventListener.listen('keydown', event => this.handleKeydown(event)); - this.eventListener.listen('paste', event => - this.handleInsert( - event, - event.clipboardData?.getData('text/plain') || '', - ), - ); - } + this.eventListener.listen('beforeinput', event => { + const isForward = event.inputType.includes('Forward'); + + this.updateHistory(this.elementState); + + switch (event.inputType) { + // historyUndo/historyRedo will not be triggered if value was modified programmatically + case 'historyUndo': + event.preventDefault(); + + return this.undo(); + case 'historyRedo': + event.preventDefault(); + + return this.redo(); + case 'deleteByCut': + case 'deleteContentBackward': + case 'deleteContentForward': + return this.handleDelete({ + event, + isForward, + selection: getNotEmptySelection(this.elementState, isForward), + }); + case 'deleteWordForward': + case 'deleteWordBackward': + return this.handleDelete({ + event, + isForward, + selection: getWordSelection(this.elementState, isForward), + force: true, + }); + case 'deleteSoftLineBackward': + case 'deleteSoftLineForward': + case 'deleteHardLineBackward': + case 'deleteHardLineForward': + return this.handleDelete({ + event, + isForward, + selection: getLineSelection(this.elementState, isForward), + force: true, + }); + case 'insertCompositionText': + return; // will be handled inside `compositionend` event + case 'insertLineBreak': + return this.handleEnter(event); + case 'insertFromPaste': + case 'insertText': + case 'insertFromDrop': + default: + return this.handleInsert(event, event.data || ''); + } + }); this.eventListener.listen('input', ({inputType}) => { if (inputType === 'insertCompositionText') { @@ -193,11 +176,7 @@ export class Maskito extends MaskHistory { data: null, }, ): void { - const globalObject = typeof window !== 'undefined' ? window : globalThis; - - // TODO: replace `globalObject` with `globalThis` after bumping Firefox to 65+ - // @see https://caniuse.com/?search=globalThis - if (globalObject?.InputEvent) { + if (globalThis?.InputEvent) { this.element.dispatchEvent( new InputEvent('input', { ...eventInit, @@ -208,36 +187,13 @@ export class Maskito extends MaskHistory { } } - private handleKeydown(event: KeyboardEvent): void { - const pressedKey = event.key; - const isForward = pressedKey === 'Delete'; - - switch (pressedKey) { - case 'Backspace': - case 'Delete': - return this.handleDelete({ - event, - isForward, - selection: getNotEmptySelection(this.elementState, isForward), - }); - case 'Enter': - return this.handleEnter(event); - } - - if (!isEventProducingCharacter(event)) { - return; - } - - this.handleInsert(event, pressedKey); - } - private handleDelete({ event, selection, isForward, force = false, }: { - event: Event | TypedInputEvent; + event: TypedInputEvent; selection: SelectionRange; isForward: boolean; force?: boolean; @@ -277,19 +233,14 @@ export class Maskito extends MaskHistory { return this.updateSelectionRange(isForward ? [to, to] : [from, from]); } - // TODO: drop it when `event: Event | TypedInputEvent` => `event: TypedInputEvent` - const inputTypeFallback = isForward - ? 'deleteContentForward' - : 'deleteContentBackward'; - this.updateElementState(newElementState, { - inputType: 'inputType' in event ? event.inputType : inputTypeFallback, + inputType: event.inputType, data: null, }); this.updateHistory(newElementState); } - private handleInsert(event: Event | TypedInputEvent, data: string): void { + private handleInsert(event: TypedInputEvent, data: string): void { const initialElementState = this.elementState; const {elementState, data: insertedText = data} = this.preprocessor( { @@ -320,13 +271,13 @@ export class Maskito extends MaskHistory { this.updateElementState(newElementState, { data, - inputType: 'inputType' in event ? event.inputType : 'insertText', + inputType: event.inputType, }); this.updateHistory(newElementState); } } - private handleEnter(event: Event): void { + private handleEnter(event: TypedInputEvent): void { if (this.isTextArea) { this.handleInsert(event, '\n'); } diff --git a/projects/core/src/lib/utils/dom/is-before-input-event-supported.ts b/projects/core/src/lib/utils/dom/is-before-input-event-supported.ts deleted file mode 100644 index 9baaee3f6..000000000 --- a/projects/core/src/lib/utils/dom/is-before-input-event-supported.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * "beforeinput" is more appropriate event for preprocessing of the input masking (than `keydown`): - * - `keydown` is not triggered by predictive text from native mobile keyboards. - * - `keydown` is triggered by system key combinations (we don't need them, and they should be manually filtered). - * - Dropping text inside input triggers `beforeinput` (but not `keydown`). - * ___ - * "beforeinput" is not supported by Chrome 49+ (only from 60+) and by Firefox 52+ (only from 87+). - * - * @see https://caniuse.com/?search=beforeinput - * @see https://taiga-ui.dev/browser-support - */ -export function isBeforeInputEventSupported( - element: HTMLInputElement | HTMLTextAreaElement, -): boolean { - return 'onbeforeinput' in element; -} diff --git a/projects/core/src/lib/utils/dom/is-event-producing-character.ts b/projects/core/src/lib/utils/dom/is-event-producing-character.ts deleted file mode 100644 index 60b83f708..000000000 --- a/projects/core/src/lib/utils/dom/is-event-producing-character.ts +++ /dev/null @@ -1,11 +0,0 @@ -export function isEventProducingCharacter({ - key, - ctrlKey, - metaKey, - altKey, -}: KeyboardEvent): boolean { - const isSystemKeyCombinations = ctrlKey || metaKey || altKey; - const isSingleUnicodeChar = /^.$/u.test(key); // 4-byte characters case (e.g. smile) - - return !isSystemKeyCombinations && key !== 'Backspace' && isSingleUnicodeChar; -} diff --git a/projects/core/src/lib/utils/get-word-selection.ts b/projects/core/src/lib/utils/get-word-selection.ts index da6932a2a..22bee75e5 100644 --- a/projects/core/src/lib/utils/get-word-selection.ts +++ b/projects/core/src/lib/utils/get-word-selection.ts @@ -20,7 +20,7 @@ export function getWordSelection( '', ]; const nearestWordEndIndex = valueAfterSelectionStart - .replace(LEADING_SPACES_REG, '') // TODO replace with `String.trimStart` after bumping Firefox to 61+ + .trimStart() .search(SPACE_REG); return [ @@ -34,7 +34,7 @@ export function getWordSelection( const valueBeforeSelectionEnd = value.slice(0, to); const [trailingSpaces] = valueBeforeSelectionEnd.match(TRAILING_SPACES_REG) || ['']; const selectedWordLength = valueBeforeSelectionEnd - .replace(TRAILING_SPACES_REG, '') // TODO replace with `String.trimEnd` after bumping Firefox to 61+ + .trimEnd() .split('') .reverse() .findIndex(char => char.match(SPACE_REG)); diff --git a/projects/core/src/lib/utils/index.ts b/projects/core/src/lib/utils/index.ts index 2e52e0ba4..23ba4ada5 100644 --- a/projects/core/src/lib/utils/index.ts +++ b/projects/core/src/lib/utils/index.ts @@ -1,7 +1,5 @@ export * from './dom/event-listener'; export * from './dom/history-events'; -export * from './dom/is-before-input-event-supported'; -export * from './dom/is-event-producing-character'; export * from './element-states-equality'; export * from './get-line-selection'; export * from './get-not-empty-selection'; diff --git a/projects/kit/src/lib/utils/date/parse-date-string.ts b/projects/kit/src/lib/utils/date/parse-date-string.ts index 8969d6291..30c7cd658 100644 --- a/projects/kit/src/lib/utils/date/parse-date-string.ts +++ b/projects/kit/src/lib/utils/date/parse-date-string.ts @@ -1,5 +1,4 @@ import {MaskitoDateSegments} from '../../types'; -import {getObjectFromEntries} from '../get-object-from-entries'; export function parseDateString( dateString: string, @@ -20,7 +19,7 @@ export function parseDateString( ), }; - return getObjectFromEntries( + return Object.fromEntries( Object.entries(dateSegments) .filter(([_, value]) => Boolean(value)) .sort(([a], [b]) => diff --git a/projects/kit/src/lib/utils/date/raise-segment-value-to-min.ts b/projects/kit/src/lib/utils/date/raise-segment-value-to-min.ts index 7d7a66d63..33d973a19 100644 --- a/projects/kit/src/lib/utils/date/raise-segment-value-to-min.ts +++ b/projects/kit/src/lib/utils/date/raise-segment-value-to-min.ts @@ -1,5 +1,4 @@ import {MaskitoDateSegments} from '../../types'; -import {getObjectFromEntries} from '../get-object-from-entries'; import {getDateSegmentValueLength} from './date-segment-value-length'; export function raiseSegmentValueToMin( @@ -8,7 +7,7 @@ export function raiseSegmentValueToMin( ): Partial { const segmentsLength = getDateSegmentValueLength(fullMode); - return getObjectFromEntries( + return Object.fromEntries( Object.entries(segments).map(([key, value]: [string, string]) => { const segmentLength = segmentsLength[key as keyof Partial]; diff --git a/projects/kit/src/lib/utils/get-object-from-entries.ts b/projects/kit/src/lib/utils/get-object-from-entries.ts deleted file mode 100644 index a8aad0dd7..000000000 --- a/projects/kit/src/lib/utils/get-object-from-entries.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @deprecated use `Object.fromEntries` instead - * (check browser support first https://caniuse.com/mdn-javascript_builtins_object_fromentries) - * ___ - * TODO: after we bump Firefox to 63+ replace this function with `Object.fromEntries`. - * TODO: Add `es2019.object` to `tsconfig.json` => `compilerOptions.lib`. - * - */ -export function getObjectFromEntries( - keyValuePairs: Array<[K, V]>, -): Record { - return keyValuePairs.reduce( - (obj, [key, val]) => ({...obj, [key]: val}), - {} as Record, - ); -} diff --git a/projects/kit/src/lib/utils/index.ts b/projects/kit/src/lib/utils/index.ts index 93838d748..25994b95b 100644 --- a/projects/kit/src/lib/utils/index.ts +++ b/projects/kit/src/lib/utils/index.ts @@ -11,7 +11,6 @@ export * from './date/validate-date-string'; export * from './escape-reg-exp'; export * from './find-common-beginning-substr'; export * from './get-focused'; -export * from './get-object-from-entries'; export * from './identity'; export * from './is-empty'; export * from './pad-with-zeroes-until-valid'; diff --git a/projects/kit/src/lib/utils/time/pad-time-segments.ts b/projects/kit/src/lib/utils/time/pad-time-segments.ts index 5d0b8ada4..c64afea18 100644 --- a/projects/kit/src/lib/utils/time/pad-time-segments.ts +++ b/projects/kit/src/lib/utils/time/pad-time-segments.ts @@ -1,6 +1,5 @@ import {TIME_SEGMENT_VALUE_LENGTHS} from '../../constants'; import {MaskitoTimeSegments} from '../../types'; -import {getObjectFromEntries} from '../get-object-from-entries'; export function padTimeSegments( timeSegments: MaskitoTimeSegments, @@ -13,7 +12,7 @@ export function padTimeSegments( export function padTimeSegments( timeSegments: Partial>, ): Partial { - return getObjectFromEntries( + return Object.fromEntries( Object.entries(timeSegments).map(([segmentName, segmentValue]) => [ segmentName, `${segmentValue}`.padEnd( diff --git a/projects/kit/src/lib/utils/time/parse-time-string.ts b/projects/kit/src/lib/utils/time/parse-time-string.ts index f6e3364db..ae1c597f1 100644 --- a/projects/kit/src/lib/utils/time/parse-time-string.ts +++ b/projects/kit/src/lib/utils/time/parse-time-string.ts @@ -1,5 +1,4 @@ import {MaskitoTimeSegments} from '../../types'; -import {getObjectFromEntries} from '../get-object-from-entries'; /** * @param timeString can be with/without fixed characters @@ -14,7 +13,7 @@ export function parseTimeString(timeString: string): Partial Boolean(value)), ); } diff --git a/tsconfig.json b/tsconfig.json index c800b1cbc..5e9842b46 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ "module": "es2020", "jsx": "react-jsx", "target": "es2015", - "lib": ["es2017", "es2018.asynciterable", "dom"], + "lib": ["es2017", "es2018.asynciterable", "es2019.object", "es2019.string", "dom"], "checkJs": false, "paths": { "@demo/constants": ["projects/demo/src/app/constants/index.ts"],