Skip to content

Commit 06cceda

Browse files
author
Kubit
committed
Improve hook for inputs
1 parent f1e0c08 commit 06cceda

File tree

2 files changed

+42
-29
lines changed

2 files changed

+42
-29
lines changed

src/hooks/useInput/__tests__/useInput.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { act, renderHook } from '@testing-library/react-hooks';
22
import React, { ChangeEvent } from 'react';
33

4-
import { FormatNumber } from '@/components';
54
import * as validationsProvider from '@/provider/validations/validationsProvider';
65

76
import { useInput } from '../useInput';
87

98
describe('useInput Hook', () => {
109
it('useInput - on internal change should call parent onChange', () => {
1110
const onChange = jest.fn();
12-
const formatNumber = { style: 'decimal' } as FormatNumber;
11+
const formatNumber = { style: 'decimal' };
1312
const ref = React.createRef<HTMLInputElement | undefined>();
1413
const currentValue = '123234';
1514
const regex = new RegExp('^[0-9]*$');
@@ -44,7 +43,7 @@ describe('useInput Hook', () => {
4443
});
4544
it('useInput - on internal blur should call parent onBlur', () => {
4645
const onBlur = jest.fn();
47-
const formatNumber = { style: 'decimal' } as FormatNumber;
46+
const formatNumber = { style: 'decimal' };
4847
const { result } = renderHook(() => useInput({ onBlur, formatNumber }));
4948

5049
act(() => {

src/hooks/useInput/useInput.ts

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -118,17 +118,25 @@ export const useInput = (props: ParamsTypeInputHook): ReturnTypeInputHook => {
118118

119119
useEffect(() => {
120120
if (eventKeyPressRef.current && props.mask && inputRef?.current && value) {
121+
const { start, end } = eventKeyPressRef.current.cursor;
122+
// if multiple digits are selected, recovery the selected area
123+
const area = Math.abs(start - end);
124+
// this the mask representation of the selected area
125+
const selected = props.mask.slice(start, end);
126+
// match the mask representation with the mask character
127+
const maskChart = selected.match(/#/g);
128+
// calculate the difference between the selected area and the mask representation
129+
const diffStart = maskChart ? Math.abs(maskChart.length - area) : 0;
130+
const diffEnd = maskChart ? maskChart.length : 0;
131+
121132
const position = getPosition(
122133
eventKeyPressRef.current.key,
123134
value,
124135
eventKeyPressRef.current.cursor,
125136
positionRef.current
126137
);
127138
inputRef.current.focus();
128-
inputRef.current.setSelectionRange(
129-
eventKeyPressRef.current.cursor.start + position,
130-
eventKeyPressRef.current.cursor.end + position
131-
);
139+
inputRef.current.setSelectionRange(start + diffStart + position, end - diffEnd + position);
132140
}
133141
}, [value]);
134142

@@ -290,47 +298,53 @@ export const useInput = (props: ParamsTypeInputHook): ReturnTypeInputHook => {
290298
key,
291299
currentTarget: { selectionStart, selectionEnd },
292300
} = event;
293-
let start = selectionStart;
294-
let end = selectionEnd;
301+
let start = selectionStart ?? 0;
302+
let end = selectionEnd ?? 0;
303+
304+
const isSelected = Math.abs(start - end) > 0;
295305

296306
// Check if the input has a mask
297307
if (props.mask && inputRef.current && start !== null && end !== null) {
298308
// Check if the key pressed is "Backspace" or "Delete"
299309
// Check if the character at the cursor position is a mask character
300310
if (key === BACKSPACE.key && props.mask[start - 1] && props.mask[start - 1] !== '#') {
301-
let count = 1;
302-
// Check if the character at the cursor position is a mask character
303-
for (let index = start - 1; index >= 0; index--) {
304-
if (props.mask[index] === '#') {
305-
count = start - 1 - index;
306-
break;
311+
if (!isSelected) {
312+
let count = 1;
313+
// Check if the character at the cursor position is a mask character
314+
for (let index = start - 1; index >= 0; index--) {
315+
if (props.mask[index] === '#') {
316+
count = start - 1 - index;
317+
break;
318+
}
307319
}
320+
// Calculate the new cursor position
321+
start = start - count;
322+
end = end - count;
308323
}
309-
// Calculate the new cursor position
310-
start = start - count;
311-
end = end - count;
312324
// Set the cursor to the new position
313325
inputRef.current.setSelectionRange(start, end);
314326
} else if (key === DELETE.key && props.mask[end] && props.mask[end] !== '#') {
315-
let count = 1;
316-
// Check if the character at the cursor position is a mask character
317-
for (let index = end; index < props.mask.length; index++) {
318-
if (props.mask[index] === '#') {
319-
count = index - end;
320-
break;
327+
if (!isSelected) {
328+
let count = 1;
329+
// Check if the character at the cursor position is a mask character
330+
for (let index = end; index < props.mask.length; index++) {
331+
if (props.mask[index] === '#') {
332+
count = index - end;
333+
break;
334+
}
321335
}
336+
// Calculate the new cursor position
337+
start = start + count;
338+
end = end + count;
322339
}
323-
// Calculate the new cursor position
324-
start = start + count;
325-
end = end + count;
326340
// Set the cursor to the new position
327341
inputRef.current.setSelectionRange(start, end);
328342
}
329343
}
330344

331345
const cursor = {
332-
start: start ?? 0,
333-
end: end ?? 0,
346+
start: start,
347+
end: end,
334348
};
335349
eventKeyPressRef.current = { key, cursor };
336350
props.onKeyDown?.(event);

0 commit comments

Comments
 (0)