UNPKG

@v4fire/client

Version:

V4Fire client core library

155 lines (124 loc) 3.86 kB
/*! * V4Fire Client Core * https://github.com/V4Fire/Client * * Released under the MIT license * https://github.com/V4Fire/Client/blob/master/LICENSE */ import type iInputText from 'super/i-input-text/i-input-text'; import { fitForText, convertCursorPositionToRaw } from 'super/i-input-text/modules/mask/helpers'; import type { SyncMaskWithTextOptions } from 'super/i-input-text/modules/mask/interface'; /** * Synchronizes the component mask with the specified text value. * * The component can already have some masked text as a value (by default, the value is taken from the DOM node, * but you can specify it directly). So we need to apply a new text to the old value with or without limiting bounds to * update. If only a part of the mask is modified, the rest symbols from the old masked text will be preserved. * * The resulting text is saved to the input. The cursor position is updated too. * * @param component * @param text - text to synchronize or a list of Unicode symbols * @param [opts] - additional options */ export function syncWithText<C extends iInputText>( component: C, text: CanArray<string>, opts: SyncMaskWithTextOptions = {} ): void { const { unsafe, unsafe: {maskPlaceholder} } = component; const originalMask = unsafe.compiledMask; if (originalMask == null) { return; } const originalText = opts.inputText ?? originalMask.text, originalTextChunks = Object.isArray(originalText) ? originalText.slice() : [...originalText.letters()]; const from = opts.from ?? 0, to = opts.to ?? originalMask.symbols.length; text = originalTextChunks .slice(0, from) .concat(text, originalTextChunks.slice(to + 1)) .join(''); const mask = opts.fitMask !== false ? fitForText(component, text) : originalMask; if (mask == null) { return; } const textChunks = Object.isArray(text) ? text.slice() : [...text.letters()]; if (!Object.isArray(textChunks)) { return; } const {symbols: maskSymbols} = mask; const isFocused = unsafe.mods.focused === 'true', isEmptyText = !Object.isTruly(opts.from) && textChunks.length === 0; let newMaskedText = '', cursorPos = opts.cursorPos ?? from; if (isEmptyText) { newMaskedText = mask.placeholder; } else { for (let i = 0; i < maskSymbols.length; i++) { const maskEl = maskSymbols[i]; if (Object.isRegExp(maskEl)) { if (textChunks.length > 0) { // Skip all symbols that don't match the non-terminal grammar while ( textChunks.length > 0 && ( (!opts.preservePlaceholders || textChunks[0] !== maskPlaceholder) && !maskEl.test(textChunks[0]) ) ) { textChunks.shift(); } if (textChunks.length > 0) { newMaskedText += textChunks[0]; if (!opts.preserveCursor) { cursorPos++; } } } // There are no symbols from the raw input that match the non-terminal grammar if (textChunks.length === 0) { newMaskedText += maskPlaceholder; } else { textChunks.shift(); } // This is a static symbol from the mask } else { newMaskedText += maskEl; if (!opts.preserveCursor && textChunks.length > 0) { cursorPos++; } } } } mask.text = newMaskedText; unsafe.updateTextStore(newMaskedText); // If the component is focused, we need to correct the cursor position if (isFocused) { const needRewindCursorPos = !opts.preserveCursor && newMaskedText[cursorPos] !== mask.placeholder && cursorPos < (opts.to ?? maskSymbols.length) - 1; if (needRewindCursorPos) { do { cursorPos++; } while (cursorPos < to && !Object.isRegExp(maskSymbols[cursorPos])); } mask.selectionStart = cursorPos; mask.selectionEnd = cursorPos; cursorPos = convertCursorPositionToRaw(component, cursorPos); unsafe.$refs.input.setSelectionRange(cursorPos, cursorPos); } }