@maskito/core
Advanced tools
Comparing version 2.3.1 to 2.3.2
358
index.cjs.js
@@ -65,171 +65,15 @@ 'use strict'; | ||
class EventListener { | ||
constructor(element) { | ||
this.element = element; | ||
this.listeners = []; | ||
} | ||
listen(eventType, fn, options) { | ||
const untypedFn = fn; | ||
this.element.addEventListener(eventType, untypedFn, options); | ||
this.listeners.push(() => this.element.removeEventListener(eventType, untypedFn)); | ||
} | ||
destroy() { | ||
this.listeners.forEach(stopListen => stopListen()); | ||
} | ||
} | ||
const MASKITO_DEFAULT_ELEMENT_PREDICATE = e => e.isContentEditable | ||
? maskitoAdaptContentEditable(e) | ||
: e.querySelector('input,textarea') || | ||
e; | ||
const HotkeyModifier = { | ||
CTRL: 1 << 0, | ||
ALT: 1 << 1, | ||
SHIFT: 1 << 2, | ||
META: 1 << 3, | ||
const MASKITO_DEFAULT_OPTIONS = { | ||
mask: /^.*$/, | ||
preprocessors: [], | ||
postprocessors: [], | ||
plugins: [], | ||
overwriteMode: 'shift', | ||
}; | ||
// TODO add variants that can be processed correctly | ||
const HotkeyCode = { | ||
Y: 89, | ||
Z: 90, | ||
}; | ||
/** | ||
* Checks if the passed keyboard event match the required hotkey. | ||
* | ||
* @example | ||
* input.addEventListener('keydown', (event) => { | ||
* if (isHotkey(event, HotkeyModifier.CTRL | HotkeyModifier.SHIFT, HotkeyCode.Z)) { | ||
* // redo hotkey pressed | ||
* } | ||
* }) | ||
* | ||
* @return will return `true` only if the {@link HotkeyCode} matches and only the necessary | ||
* {@link HotkeyModifier modifiers} have been pressed | ||
*/ | ||
function isHotkey(event, modifiers, hotkeyCode) { | ||
return (event.ctrlKey === !!(modifiers & HotkeyModifier.CTRL) && | ||
event.altKey === !!(modifiers & HotkeyModifier.ALT) && | ||
event.shiftKey === !!(modifiers & HotkeyModifier.SHIFT) && | ||
event.metaKey === !!(modifiers & HotkeyModifier.META) && | ||
/** | ||
* We intentionally use legacy {@link KeyboardEvent#keyCode `keyCode`} property. It is more | ||
* "keyboard-layout"-independent than {@link KeyboardEvent#key `key`} or {@link KeyboardEvent#code `code`} properties. | ||
* @see {@link https://github.com/taiga-family/maskito/issues/315 `KeyboardEvent#code` issue} | ||
*/ | ||
// eslint-disable-next-line sonar/deprecation | ||
event.keyCode === hotkeyCode); | ||
} | ||
function isRedo(event) { | ||
return (isHotkey(event, HotkeyModifier.CTRL, HotkeyCode.Y) || // Windows | ||
isHotkey(event, HotkeyModifier.CTRL | HotkeyModifier.SHIFT, HotkeyCode.Z) || // Windows & Android | ||
isHotkey(event, HotkeyModifier.META | HotkeyModifier.SHIFT, HotkeyCode.Z) // macOS & iOS | ||
); | ||
} | ||
function isUndo(event) { | ||
return (isHotkey(event, HotkeyModifier.CTRL, HotkeyCode.Z) || // Windows & Android | ||
isHotkey(event, HotkeyModifier.META, HotkeyCode.Z) // macOS & iOS | ||
); | ||
} | ||
/** | ||
* Sets value to element, and dispatches input event | ||
* if you passed ELementState, it also sets selection range | ||
* | ||
* @example | ||
* maskitoUpdateElement(input, newValue); | ||
* maskitoUpdateElement(input, elementState); | ||
* | ||
* @see {@link https://github.com/taiga-family/maskito/issues/804 issue} | ||
* | ||
* @return void | ||
*/ | ||
function maskitoUpdateElement(element, valueOrElementState) { | ||
var _a; | ||
const initialValue = element.value; | ||
if (typeof valueOrElementState === 'string') { | ||
element.value = valueOrElementState; | ||
} | ||
else { | ||
const [from, to] = valueOrElementState.selection; | ||
element.value = valueOrElementState.value; | ||
if (element.matches(':focus')) { | ||
(_a = element.setSelectionRange) === null || _a === void 0 ? void 0 : _a.call(element, from, to); | ||
} | ||
} | ||
if (element.value !== initialValue) { | ||
element.dispatchEvent(new Event('input', | ||
/** | ||
* React handles this event only on bubbling phase | ||
* | ||
* here is the list of events that are processed in the capture stage, others are processed in the bubbling stage | ||
* https://github.com/facebook/react/blob/cb2439624f43c510007f65aea5c50a8bb97917e4/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js#L222 | ||
*/ | ||
{ bubbles: true })); | ||
} | ||
} | ||
function areElementValuesEqual(sampleState, ...states) { | ||
return states.every(({ value }) => value === sampleState.value); | ||
} | ||
function areElementStatesEqual(sampleState, ...states) { | ||
return states.every(({ value, selection }) => value === sampleState.value && | ||
selection[0] === sampleState.selection[0] && | ||
selection[1] === sampleState.selection[1]); | ||
} | ||
function getLineSelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
const nearestBreak = isForward | ||
? value.slice(from).indexOf('\n') + 1 || value.length | ||
: value.slice(0, to).lastIndexOf('\n') + 1; | ||
const selectFrom = isForward ? from : nearestBreak; | ||
const selectTo = isForward ? nearestBreak : to; | ||
return [selectFrom, selectTo]; | ||
} | ||
function getNotEmptySelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
const notEmptySelection = isForward ? [from, to + 1] : [from - 1, to]; | ||
return notEmptySelection.map(x => Math.min(Math.max(x, 0), value.length)); | ||
} | ||
const TRAILING_SPACES_REG = /\s+$/g; | ||
const LEADING_SPACES_REG = /^\s+/g; | ||
const SPACE_REG = /\s/; | ||
function getWordSelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
if (isForward) { | ||
const valueAfterSelectionStart = value.slice(from); | ||
const [leadingSpaces] = valueAfterSelectionStart.match(LEADING_SPACES_REG) || [ | ||
'', | ||
]; | ||
const nearestWordEndIndex = valueAfterSelectionStart | ||
.trimStart() | ||
.search(SPACE_REG); | ||
return [ | ||
from, | ||
nearestWordEndIndex !== -1 | ||
? from + leadingSpaces.length + nearestWordEndIndex | ||
: value.length, | ||
]; | ||
} | ||
const valueBeforeSelectionEnd = value.slice(0, to); | ||
const [trailingSpaces] = valueBeforeSelectionEnd.match(TRAILING_SPACES_REG) || ['']; | ||
const selectedWordLength = valueBeforeSelectionEnd | ||
.trimEnd() | ||
.split('') | ||
.reverse() | ||
.findIndex(char => char.match(SPACE_REG)); | ||
return [ | ||
selectedWordLength !== -1 ? to - trailingSpaces.length - selectedWordLength : 0, | ||
to, | ||
]; | ||
} | ||
class MaskHistory { | ||
@@ -277,2 +121,11 @@ constructor() { | ||
function areElementValuesEqual(sampleState, ...states) { | ||
return states.every(({ value }) => value === sampleState.value); | ||
} | ||
function areElementStatesEqual(sampleState, ...states) { | ||
return states.every(({ value, selection }) => value === sampleState.value && | ||
selection[0] === sampleState.selection[0] && | ||
selection[1] === sampleState.selection[1]); | ||
} | ||
function applyOverwriteMode({ value, selection }, newCharacters, mode) { | ||
@@ -470,2 +323,162 @@ const [from, to] = selection; | ||
class EventListener { | ||
constructor(element) { | ||
this.element = element; | ||
this.listeners = []; | ||
} | ||
listen(eventType, fn, options) { | ||
const untypedFn = fn; | ||
this.element.addEventListener(eventType, untypedFn, options); | ||
this.listeners.push(() => this.element.removeEventListener(eventType, untypedFn)); | ||
} | ||
destroy() { | ||
this.listeners.forEach(stopListen => stopListen()); | ||
} | ||
} | ||
const HotkeyModifier = { | ||
CTRL: 1 << 0, | ||
ALT: 1 << 1, | ||
SHIFT: 1 << 2, | ||
META: 1 << 3, | ||
}; | ||
// TODO add variants that can be processed correctly | ||
const HotkeyCode = { | ||
Y: 89, | ||
Z: 90, | ||
}; | ||
/** | ||
* Checks if the passed keyboard event match the required hotkey. | ||
* | ||
* @example | ||
* input.addEventListener('keydown', (event) => { | ||
* if (isHotkey(event, HotkeyModifier.CTRL | HotkeyModifier.SHIFT, HotkeyCode.Z)) { | ||
* // redo hotkey pressed | ||
* } | ||
* }) | ||
* | ||
* @return will return `true` only if the {@link HotkeyCode} matches and only the necessary | ||
* {@link HotkeyModifier modifiers} have been pressed | ||
*/ | ||
function isHotkey(event, modifiers, hotkeyCode) { | ||
return (event.ctrlKey === !!(modifiers & HotkeyModifier.CTRL) && | ||
event.altKey === !!(modifiers & HotkeyModifier.ALT) && | ||
event.shiftKey === !!(modifiers & HotkeyModifier.SHIFT) && | ||
event.metaKey === !!(modifiers & HotkeyModifier.META) && | ||
/** | ||
* We intentionally use legacy {@link KeyboardEvent#keyCode `keyCode`} property. It is more | ||
* "keyboard-layout"-independent than {@link KeyboardEvent#key `key`} or {@link KeyboardEvent#code `code`} properties. | ||
* @see {@link https://github.com/taiga-family/maskito/issues/315 `KeyboardEvent#code` issue} | ||
*/ | ||
// eslint-disable-next-line sonar/deprecation | ||
event.keyCode === hotkeyCode); | ||
} | ||
function isRedo(event) { | ||
return (isHotkey(event, HotkeyModifier.CTRL, HotkeyCode.Y) || // Windows | ||
isHotkey(event, HotkeyModifier.CTRL | HotkeyModifier.SHIFT, HotkeyCode.Z) || // Windows & Android | ||
isHotkey(event, HotkeyModifier.META | HotkeyModifier.SHIFT, HotkeyCode.Z) // macOS & iOS | ||
); | ||
} | ||
function isUndo(event) { | ||
return (isHotkey(event, HotkeyModifier.CTRL, HotkeyCode.Z) || // Windows & Android | ||
isHotkey(event, HotkeyModifier.META, HotkeyCode.Z) // macOS & iOS | ||
); | ||
} | ||
/** | ||
* Sets value to element, and dispatches input event | ||
* if you passed ELementState, it also sets selection range | ||
* | ||
* @example | ||
* maskitoUpdateElement(input, newValue); | ||
* maskitoUpdateElement(input, elementState); | ||
* | ||
* @see {@link https://github.com/taiga-family/maskito/issues/804 issue} | ||
* | ||
* @return void | ||
*/ | ||
function maskitoUpdateElement(element, valueOrElementState) { | ||
var _a; | ||
const initialValue = element.value; | ||
if (typeof valueOrElementState === 'string') { | ||
element.value = valueOrElementState; | ||
} | ||
else { | ||
const [from, to] = valueOrElementState.selection; | ||
element.value = valueOrElementState.value; | ||
if (element.matches(':focus')) { | ||
(_a = element.setSelectionRange) === null || _a === void 0 ? void 0 : _a.call(element, from, to); | ||
} | ||
} | ||
if (element.value !== initialValue) { | ||
element.dispatchEvent(new Event('input', | ||
/** | ||
* React handles this event only on bubbling phase | ||
* | ||
* here is the list of events that are processed in the capture stage, others are processed in the bubbling stage | ||
* https://github.com/facebook/react/blob/cb2439624f43c510007f65aea5c50a8bb97917e4/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js#L222 | ||
*/ | ||
{ bubbles: true })); | ||
} | ||
} | ||
function getLineSelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
const nearestBreak = isForward | ||
? value.slice(from).indexOf('\n') + 1 || value.length | ||
: value.slice(0, to).lastIndexOf('\n') + 1; | ||
const selectFrom = isForward ? from : nearestBreak; | ||
const selectTo = isForward ? nearestBreak : to; | ||
return [selectFrom, selectTo]; | ||
} | ||
function getNotEmptySelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
const notEmptySelection = isForward ? [from, to + 1] : [from - 1, to]; | ||
return notEmptySelection.map(x => Math.min(Math.max(x, 0), value.length)); | ||
} | ||
const TRAILING_SPACES_REG = /\s+$/g; | ||
const LEADING_SPACES_REG = /^\s+/g; | ||
const SPACE_REG = /\s/; | ||
function getWordSelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
if (isForward) { | ||
const valueAfterSelectionStart = value.slice(from); | ||
const [leadingSpaces] = valueAfterSelectionStart.match(LEADING_SPACES_REG) || [ | ||
'', | ||
]; | ||
const nearestWordEndIndex = valueAfterSelectionStart | ||
.trimStart() | ||
.search(SPACE_REG); | ||
return [ | ||
from, | ||
nearestWordEndIndex !== -1 | ||
? from + leadingSpaces.length + nearestWordEndIndex | ||
: value.length, | ||
]; | ||
} | ||
const valueBeforeSelectionEnd = value.slice(0, to); | ||
const [trailingSpaces] = valueBeforeSelectionEnd.match(TRAILING_SPACES_REG) || ['']; | ||
const selectedWordLength = valueBeforeSelectionEnd | ||
.trimEnd() | ||
.split('') | ||
.reverse() | ||
.findIndex(char => char.match(SPACE_REG)); | ||
return [ | ||
selectedWordLength !== -1 ? to - trailingSpaces.length - selectedWordLength : 0, | ||
to, | ||
]; | ||
} | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
@@ -528,15 +541,2 @@ /** | ||
const MASKITO_DEFAULT_ELEMENT_PREDICATE = e => e.isContentEditable | ||
? maskitoAdaptContentEditable(e) | ||
: e.querySelector('input,textarea') || | ||
e; | ||
const MASKITO_DEFAULT_OPTIONS = { | ||
mask: /^.*$/, | ||
preprocessors: [], | ||
postprocessors: [], | ||
plugins: [], | ||
overwriteMode: 'shift', | ||
}; | ||
class Maskito extends MaskHistory { | ||
@@ -543,0 +543,0 @@ constructor(element, maskitoOptions) { |
358
index.esm.js
@@ -61,171 +61,15 @@ function getContentEditableSelection(element) { | ||
class EventListener { | ||
constructor(element) { | ||
this.element = element; | ||
this.listeners = []; | ||
} | ||
listen(eventType, fn, options) { | ||
const untypedFn = fn; | ||
this.element.addEventListener(eventType, untypedFn, options); | ||
this.listeners.push(() => this.element.removeEventListener(eventType, untypedFn)); | ||
} | ||
destroy() { | ||
this.listeners.forEach(stopListen => stopListen()); | ||
} | ||
} | ||
const MASKITO_DEFAULT_ELEMENT_PREDICATE = e => e.isContentEditable | ||
? maskitoAdaptContentEditable(e) | ||
: e.querySelector('input,textarea') || | ||
e; | ||
const HotkeyModifier = { | ||
CTRL: 1 << 0, | ||
ALT: 1 << 1, | ||
SHIFT: 1 << 2, | ||
META: 1 << 3, | ||
const MASKITO_DEFAULT_OPTIONS = { | ||
mask: /^.*$/, | ||
preprocessors: [], | ||
postprocessors: [], | ||
plugins: [], | ||
overwriteMode: 'shift', | ||
}; | ||
// TODO add variants that can be processed correctly | ||
const HotkeyCode = { | ||
Y: 89, | ||
Z: 90, | ||
}; | ||
/** | ||
* Checks if the passed keyboard event match the required hotkey. | ||
* | ||
* @example | ||
* input.addEventListener('keydown', (event) => { | ||
* if (isHotkey(event, HotkeyModifier.CTRL | HotkeyModifier.SHIFT, HotkeyCode.Z)) { | ||
* // redo hotkey pressed | ||
* } | ||
* }) | ||
* | ||
* @return will return `true` only if the {@link HotkeyCode} matches and only the necessary | ||
* {@link HotkeyModifier modifiers} have been pressed | ||
*/ | ||
function isHotkey(event, modifiers, hotkeyCode) { | ||
return (event.ctrlKey === !!(modifiers & HotkeyModifier.CTRL) && | ||
event.altKey === !!(modifiers & HotkeyModifier.ALT) && | ||
event.shiftKey === !!(modifiers & HotkeyModifier.SHIFT) && | ||
event.metaKey === !!(modifiers & HotkeyModifier.META) && | ||
/** | ||
* We intentionally use legacy {@link KeyboardEvent#keyCode `keyCode`} property. It is more | ||
* "keyboard-layout"-independent than {@link KeyboardEvent#key `key`} or {@link KeyboardEvent#code `code`} properties. | ||
* @see {@link https://github.com/taiga-family/maskito/issues/315 `KeyboardEvent#code` issue} | ||
*/ | ||
// eslint-disable-next-line sonar/deprecation | ||
event.keyCode === hotkeyCode); | ||
} | ||
function isRedo(event) { | ||
return (isHotkey(event, HotkeyModifier.CTRL, HotkeyCode.Y) || // Windows | ||
isHotkey(event, HotkeyModifier.CTRL | HotkeyModifier.SHIFT, HotkeyCode.Z) || // Windows & Android | ||
isHotkey(event, HotkeyModifier.META | HotkeyModifier.SHIFT, HotkeyCode.Z) // macOS & iOS | ||
); | ||
} | ||
function isUndo(event) { | ||
return (isHotkey(event, HotkeyModifier.CTRL, HotkeyCode.Z) || // Windows & Android | ||
isHotkey(event, HotkeyModifier.META, HotkeyCode.Z) // macOS & iOS | ||
); | ||
} | ||
/** | ||
* Sets value to element, and dispatches input event | ||
* if you passed ELementState, it also sets selection range | ||
* | ||
* @example | ||
* maskitoUpdateElement(input, newValue); | ||
* maskitoUpdateElement(input, elementState); | ||
* | ||
* @see {@link https://github.com/taiga-family/maskito/issues/804 issue} | ||
* | ||
* @return void | ||
*/ | ||
function maskitoUpdateElement(element, valueOrElementState) { | ||
var _a; | ||
const initialValue = element.value; | ||
if (typeof valueOrElementState === 'string') { | ||
element.value = valueOrElementState; | ||
} | ||
else { | ||
const [from, to] = valueOrElementState.selection; | ||
element.value = valueOrElementState.value; | ||
if (element.matches(':focus')) { | ||
(_a = element.setSelectionRange) === null || _a === void 0 ? void 0 : _a.call(element, from, to); | ||
} | ||
} | ||
if (element.value !== initialValue) { | ||
element.dispatchEvent(new Event('input', | ||
/** | ||
* React handles this event only on bubbling phase | ||
* | ||
* here is the list of events that are processed in the capture stage, others are processed in the bubbling stage | ||
* https://github.com/facebook/react/blob/cb2439624f43c510007f65aea5c50a8bb97917e4/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js#L222 | ||
*/ | ||
{ bubbles: true })); | ||
} | ||
} | ||
function areElementValuesEqual(sampleState, ...states) { | ||
return states.every(({ value }) => value === sampleState.value); | ||
} | ||
function areElementStatesEqual(sampleState, ...states) { | ||
return states.every(({ value, selection }) => value === sampleState.value && | ||
selection[0] === sampleState.selection[0] && | ||
selection[1] === sampleState.selection[1]); | ||
} | ||
function getLineSelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
const nearestBreak = isForward | ||
? value.slice(from).indexOf('\n') + 1 || value.length | ||
: value.slice(0, to).lastIndexOf('\n') + 1; | ||
const selectFrom = isForward ? from : nearestBreak; | ||
const selectTo = isForward ? nearestBreak : to; | ||
return [selectFrom, selectTo]; | ||
} | ||
function getNotEmptySelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
const notEmptySelection = isForward ? [from, to + 1] : [from - 1, to]; | ||
return notEmptySelection.map(x => Math.min(Math.max(x, 0), value.length)); | ||
} | ||
const TRAILING_SPACES_REG = /\s+$/g; | ||
const LEADING_SPACES_REG = /^\s+/g; | ||
const SPACE_REG = /\s/; | ||
function getWordSelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
if (isForward) { | ||
const valueAfterSelectionStart = value.slice(from); | ||
const [leadingSpaces] = valueAfterSelectionStart.match(LEADING_SPACES_REG) || [ | ||
'', | ||
]; | ||
const nearestWordEndIndex = valueAfterSelectionStart | ||
.trimStart() | ||
.search(SPACE_REG); | ||
return [ | ||
from, | ||
nearestWordEndIndex !== -1 | ||
? from + leadingSpaces.length + nearestWordEndIndex | ||
: value.length, | ||
]; | ||
} | ||
const valueBeforeSelectionEnd = value.slice(0, to); | ||
const [trailingSpaces] = valueBeforeSelectionEnd.match(TRAILING_SPACES_REG) || ['']; | ||
const selectedWordLength = valueBeforeSelectionEnd | ||
.trimEnd() | ||
.split('') | ||
.reverse() | ||
.findIndex(char => char.match(SPACE_REG)); | ||
return [ | ||
selectedWordLength !== -1 ? to - trailingSpaces.length - selectedWordLength : 0, | ||
to, | ||
]; | ||
} | ||
class MaskHistory { | ||
@@ -273,2 +117,11 @@ constructor() { | ||
function areElementValuesEqual(sampleState, ...states) { | ||
return states.every(({ value }) => value === sampleState.value); | ||
} | ||
function areElementStatesEqual(sampleState, ...states) { | ||
return states.every(({ value, selection }) => value === sampleState.value && | ||
selection[0] === sampleState.selection[0] && | ||
selection[1] === sampleState.selection[1]); | ||
} | ||
function applyOverwriteMode({ value, selection }, newCharacters, mode) { | ||
@@ -466,2 +319,162 @@ const [from, to] = selection; | ||
class EventListener { | ||
constructor(element) { | ||
this.element = element; | ||
this.listeners = []; | ||
} | ||
listen(eventType, fn, options) { | ||
const untypedFn = fn; | ||
this.element.addEventListener(eventType, untypedFn, options); | ||
this.listeners.push(() => this.element.removeEventListener(eventType, untypedFn)); | ||
} | ||
destroy() { | ||
this.listeners.forEach(stopListen => stopListen()); | ||
} | ||
} | ||
const HotkeyModifier = { | ||
CTRL: 1 << 0, | ||
ALT: 1 << 1, | ||
SHIFT: 1 << 2, | ||
META: 1 << 3, | ||
}; | ||
// TODO add variants that can be processed correctly | ||
const HotkeyCode = { | ||
Y: 89, | ||
Z: 90, | ||
}; | ||
/** | ||
* Checks if the passed keyboard event match the required hotkey. | ||
* | ||
* @example | ||
* input.addEventListener('keydown', (event) => { | ||
* if (isHotkey(event, HotkeyModifier.CTRL | HotkeyModifier.SHIFT, HotkeyCode.Z)) { | ||
* // redo hotkey pressed | ||
* } | ||
* }) | ||
* | ||
* @return will return `true` only if the {@link HotkeyCode} matches and only the necessary | ||
* {@link HotkeyModifier modifiers} have been pressed | ||
*/ | ||
function isHotkey(event, modifiers, hotkeyCode) { | ||
return (event.ctrlKey === !!(modifiers & HotkeyModifier.CTRL) && | ||
event.altKey === !!(modifiers & HotkeyModifier.ALT) && | ||
event.shiftKey === !!(modifiers & HotkeyModifier.SHIFT) && | ||
event.metaKey === !!(modifiers & HotkeyModifier.META) && | ||
/** | ||
* We intentionally use legacy {@link KeyboardEvent#keyCode `keyCode`} property. It is more | ||
* "keyboard-layout"-independent than {@link KeyboardEvent#key `key`} or {@link KeyboardEvent#code `code`} properties. | ||
* @see {@link https://github.com/taiga-family/maskito/issues/315 `KeyboardEvent#code` issue} | ||
*/ | ||
// eslint-disable-next-line sonar/deprecation | ||
event.keyCode === hotkeyCode); | ||
} | ||
function isRedo(event) { | ||
return (isHotkey(event, HotkeyModifier.CTRL, HotkeyCode.Y) || // Windows | ||
isHotkey(event, HotkeyModifier.CTRL | HotkeyModifier.SHIFT, HotkeyCode.Z) || // Windows & Android | ||
isHotkey(event, HotkeyModifier.META | HotkeyModifier.SHIFT, HotkeyCode.Z) // macOS & iOS | ||
); | ||
} | ||
function isUndo(event) { | ||
return (isHotkey(event, HotkeyModifier.CTRL, HotkeyCode.Z) || // Windows & Android | ||
isHotkey(event, HotkeyModifier.META, HotkeyCode.Z) // macOS & iOS | ||
); | ||
} | ||
/** | ||
* Sets value to element, and dispatches input event | ||
* if you passed ELementState, it also sets selection range | ||
* | ||
* @example | ||
* maskitoUpdateElement(input, newValue); | ||
* maskitoUpdateElement(input, elementState); | ||
* | ||
* @see {@link https://github.com/taiga-family/maskito/issues/804 issue} | ||
* | ||
* @return void | ||
*/ | ||
function maskitoUpdateElement(element, valueOrElementState) { | ||
var _a; | ||
const initialValue = element.value; | ||
if (typeof valueOrElementState === 'string') { | ||
element.value = valueOrElementState; | ||
} | ||
else { | ||
const [from, to] = valueOrElementState.selection; | ||
element.value = valueOrElementState.value; | ||
if (element.matches(':focus')) { | ||
(_a = element.setSelectionRange) === null || _a === void 0 ? void 0 : _a.call(element, from, to); | ||
} | ||
} | ||
if (element.value !== initialValue) { | ||
element.dispatchEvent(new Event('input', | ||
/** | ||
* React handles this event only on bubbling phase | ||
* | ||
* here is the list of events that are processed in the capture stage, others are processed in the bubbling stage | ||
* https://github.com/facebook/react/blob/cb2439624f43c510007f65aea5c50a8bb97917e4/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js#L222 | ||
*/ | ||
{ bubbles: true })); | ||
} | ||
} | ||
function getLineSelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
const nearestBreak = isForward | ||
? value.slice(from).indexOf('\n') + 1 || value.length | ||
: value.slice(0, to).lastIndexOf('\n') + 1; | ||
const selectFrom = isForward ? from : nearestBreak; | ||
const selectTo = isForward ? nearestBreak : to; | ||
return [selectFrom, selectTo]; | ||
} | ||
function getNotEmptySelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
const notEmptySelection = isForward ? [from, to + 1] : [from - 1, to]; | ||
return notEmptySelection.map(x => Math.min(Math.max(x, 0), value.length)); | ||
} | ||
const TRAILING_SPACES_REG = /\s+$/g; | ||
const LEADING_SPACES_REG = /^\s+/g; | ||
const SPACE_REG = /\s/; | ||
function getWordSelection({ value, selection }, isForward) { | ||
const [from, to] = selection; | ||
if (from !== to) { | ||
return [from, to]; | ||
} | ||
if (isForward) { | ||
const valueAfterSelectionStart = value.slice(from); | ||
const [leadingSpaces] = valueAfterSelectionStart.match(LEADING_SPACES_REG) || [ | ||
'', | ||
]; | ||
const nearestWordEndIndex = valueAfterSelectionStart | ||
.trimStart() | ||
.search(SPACE_REG); | ||
return [ | ||
from, | ||
nearestWordEndIndex !== -1 | ||
? from + leadingSpaces.length + nearestWordEndIndex | ||
: value.length, | ||
]; | ||
} | ||
const valueBeforeSelectionEnd = value.slice(0, to); | ||
const [trailingSpaces] = valueBeforeSelectionEnd.match(TRAILING_SPACES_REG) || ['']; | ||
const selectedWordLength = valueBeforeSelectionEnd | ||
.trimEnd() | ||
.split('') | ||
.reverse() | ||
.findIndex(char => char.match(SPACE_REG)); | ||
return [ | ||
selectedWordLength !== -1 ? to - trailingSpaces.length - selectedWordLength : 0, | ||
to, | ||
]; | ||
} | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
@@ -524,15 +537,2 @@ /** | ||
const MASKITO_DEFAULT_ELEMENT_PREDICATE = e => e.isContentEditable | ||
? maskitoAdaptContentEditable(e) | ||
: e.querySelector('input,textarea') || | ||
e; | ||
const MASKITO_DEFAULT_OPTIONS = { | ||
mask: /^.*$/, | ||
preprocessors: [], | ||
postprocessors: [], | ||
plugins: [], | ||
overwriteMode: 'shift', | ||
}; | ||
class Maskito extends MaskHistory { | ||
@@ -539,0 +539,0 @@ constructor(element, maskitoOptions) { |
{ | ||
"name": "@maskito/core", | ||
"version": "2.3.1", | ||
"version": "2.3.2", | ||
"description": "The main zero-dependency and framework-agnostic Maskito's package to create an input mask", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package