|
1 | 1 | <script lang="ts"> |
2 | | - import { createEventDispatcher, onMount } from 'svelte'; |
| 2 | + import { createEventDispatcher, onMount, tick } from 'svelte'; |
3 | 3 | import { parsePhoneNumberWithError, ParseError } from 'libphonenumber-js/max'; |
4 | 4 | import { |
5 | 5 | normalizeTelInput, |
6 | 6 | getCountryForPartialE164Number, |
7 | 7 | generatePlaceholder, |
8 | | - telInputAction |
| 8 | + telInputAction, |
| 9 | +
|
| 10 | + allowedCharacters |
| 11 | +
|
9 | 12 | } from '$lib/utils/index.js'; |
10 | 13 | import type { DetailedValue, CountryCode, E164Number, TelInputOptions } from '$lib/types'; |
11 | 14 |
|
|
80 | 83 | return country; |
81 | 84 | }; |
82 | 85 |
|
83 | | - const handleParsePhoneNumber = ( |
| 86 | + const findNewCursorPosition = ( |
| 87 | + newValue: string, |
| 88 | + formattedValue: string, |
| 89 | + initialCursorPosition: number |
| 90 | + ) => { |
| 91 | + let fvIndex = 0; |
| 92 | + for(let nvIndex = 0; nvIndex < initialCursorPosition; nvIndex++) { |
| 93 | +
|
| 94 | + // Since newValue has not been normalized yet, we need to map any non standard digits. |
| 95 | + const nvChar = allowedCharacters(newValue[nvIndex], {spaces: false}); |
| 96 | +
|
| 97 | + // For each non-formatting character encountered in the value entered by the user, |
| 98 | + // find the corresponding digit in the formatted value. |
| 99 | + if(nvChar >= '0' && nvChar <= '9') { |
| 100 | + while(!(formattedValue[fvIndex] >= '0' && formattedValue[fvIndex] <= '9') && fvIndex < formattedValue.length) { |
| 101 | + fvIndex++; |
| 102 | + } |
| 103 | + fvIndex++; |
| 104 | + } |
| 105 | + } |
| 106 | +
|
| 107 | + return fvIndex; |
| 108 | + } |
| 109 | +
|
| 110 | + const handleParsePhoneNumber = async ( |
84 | 111 | rawInput: string | null, |
85 | 112 | currCountry: CountryCode | null = null |
86 | 113 | ) => { |
|
111 | 138 | const formatOption = combinedOptions.format === 'national' ? 'nationalNumber' : 'e164'; |
112 | 139 | const formattedValue = |
113 | 140 | combinedOptions.format === 'national' ? 'formatOriginal' : 'formatInternational'; |
| 141 | + const initialCursorPosition = el?.selectionStart || 0; |
114 | 142 | if (combinedOptions.spaces && detailedValue?.[formattedValue]) { |
115 | 143 | inputValue = detailedValue[formattedValue] ?? null; |
| 144 | +
|
| 145 | + // Need to wait for input element to update before cursor position can be restored |
| 146 | + await tick(); |
| 147 | + if(el) { |
| 148 | + const newCursorPosition = findNewCursorPosition(input, inputValue, initialCursorPosition) |
| 149 | + el.selectionStart = newCursorPosition; |
| 150 | + el.selectionEnd = newCursorPosition; |
| 151 | + } |
116 | 152 | } else if (detailedValue?.[formatOption]) { |
117 | 153 | inputValue = detailedValue[formatOption] ?? null; |
| 154 | +
|
| 155 | + // Need to wait for input element to update before cursor position can be restored |
| 156 | + await tick(); |
| 157 | + if(el) { |
| 158 | + const newCursorPosition = findNewCursorPosition(input, inputValue, initialCursorPosition) |
| 159 | + el.selectionStart = newCursorPosition; |
| 160 | + el.selectionEnd = newCursorPosition; |
| 161 | + } |
118 | 162 | } |
119 | 163 |
|
120 | 164 | // keep the input value as value |
|
0 commit comments