Skip to content

Commit fbdbbc6

Browse files
authored
Merge pull request #230 from autumn-canfield/issue-212-cursor-jumps
Prevent cursor from jumping to end of input (Issue 212)
2 parents e63d305 + ff574e1 commit fbdbbc6

2 files changed

Lines changed: 52 additions & 8 deletions

File tree

packages/svelte-tel-input/src/lib/components/input/TelInput.svelte

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
<script lang="ts">
2-
import { createEventDispatcher, onMount } from 'svelte';
2+
import { createEventDispatcher, onMount, tick } from 'svelte';
33
import { parsePhoneNumberWithError, ParseError } from 'libphonenumber-js/max';
44
import {
55
normalizeTelInput,
66
getCountryForPartialE164Number,
77
generatePlaceholder,
8-
telInputAction
8+
telInputAction,
9+
10+
allowedCharacters
11+
912
} from '$lib/utils/index.js';
1013
import type { DetailedValue, CountryCode, E164Number, TelInputOptions } from '$lib/types';
1114
@@ -80,7 +83,31 @@
8083
return country;
8184
};
8285
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 (
84111
rawInput: string | null,
85112
currCountry: CountryCode | null = null
86113
) => {
@@ -111,10 +138,27 @@
111138
const formatOption = combinedOptions.format === 'national' ? 'nationalNumber' : 'e164';
112139
const formattedValue =
113140
combinedOptions.format === 'national' ? 'formatOriginal' : 'formatInternational';
141+
const initialCursorPosition = el?.selectionStart || 0;
114142
if (combinedOptions.spaces && detailedValue?.[formattedValue]) {
115143
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+
}
116152
} else if (detailedValue?.[formatOption]) {
117153
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+
}
118162
}
119163
120164
// keep the input value as value

packages/svelte-tel-input/src/lib/utils/helpers.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ export const normalizeTelInput = (input?: PhoneNumber) => {
4848
isPossible: input ? input.isPossible() : false,
4949
phoneNumber: input ? input.number : null,
5050
countryCallingCode: input ? input.countryCallingCode : null,
51-
formattedNumber: input ? input.formatInternational() : null,
51+
formattedNumber: input ? (new AsYouType()).input(input.number) : null,
5252
nationalNumber: input ? input.nationalNumber : null,
53-
formatInternational: input ? input.formatInternational() : null,
53+
formatInternational: input ? (new AsYouType()).input(input.number) : null,
5454
formatOriginal: input
55-
? input
56-
.formatInternational()
55+
? (new AsYouType())
56+
.input(input.number)
5757
.slice(input.countryCallingCode.length + 1)
5858
.trim()
5959
: null,
60-
formatNational: input ? input.formatNational() : null,
60+
formatNational: input ? (new AsYouType(input.country)).input(input.number) : null,
6161
uri: input ? input.getURI() : null,
6262
e164: input ? input.number : null
6363
}).filter(([, value]) => value !== null)

0 commit comments

Comments
 (0)