Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/docs/docs/02-Usage/01-PhoneInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ description="Disable phone value mask formatting. All formatting characters will
defaultValue="false"
/>

### `allowMaskOverflow`

<PropDescription
type="boolean"
description={<span>Allow input to exceed the mask length. When set to <code>true</code>, formatting mask will apply to the part that fits within the mask capacity, and overflow digits will be appended at the end unformatted.<br/>For example: <code>"+123456789012"</code>, will be formatted like <code>"+1 (234) 567-89012"</code></span>}
defaultValue="false"
/>

### `flags`

<PropDescription
Expand Down
8 changes: 8 additions & 0 deletions packages/docs/docs/04-Advanced-Usage/01-usePhoneInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ description="Disable phone value mask formatting. All formatting characters will
defaultValue="false"
/>

### `allowMaskOverflow`

<PropDescription
type="boolean"
description={<span>Allow input to exceed the mask length. When set to <code>true</code>, formatting mask will apply to the part that fits within the mask capacity, and overflow digits will be appended at the end unformatted.<br/>For example: <code>"+123456789012"</code>, will be formatted like <code>"+1 (234) 567-89012"</code></span>}
defaultValue="false"
/>

### `inputRef`

<PropDescription
Expand Down
9 changes: 9 additions & 0 deletions src/hooks/usePhoneInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ export interface UsePhoneInputConfig {
*/
disableFormatting?: boolean;

/**
* @description Allow input to exceed the mask length. When set to true, formatting mask will not restrict input length and will not apply when input exceeds mask length.
* @default false
*/
allowMaskOverflow?: boolean;

/**
* @description Callback that calls on phone change
* @param data - New phone data.
Expand Down Expand Up @@ -134,6 +140,7 @@ export const defaultConfig: Required<
forceDialCode: false,
disableDialCodeAndPrefix: false,
disableFormatting: false,
allowMaskOverflow: false,
countries: defaultCountries,
preferredCountries: [],
};
Expand All @@ -151,6 +158,7 @@ export const usePhoneInput = ({
forceDialCode: forceDialCodeConfig = defaultConfig.forceDialCode,
disableDialCodeAndPrefix = defaultConfig.disableDialCodeAndPrefix,
disableFormatting = defaultConfig.disableFormatting,
allowMaskOverflow = defaultConfig.allowMaskOverflow,
onChange,
inputRef: inputRefProp,
}: UsePhoneInputConfig) => {
Expand All @@ -166,6 +174,7 @@ export const usePhoneInput = ({
defaultMask,
countryGuessingEnabled,
disableFormatting,
allowMaskOverflow,
};

const ref = useRef<HTMLInputElement | null>(null);
Expand Down
2 changes: 2 additions & 0 deletions src/stories/PhoneInput/PhoneInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { WithAutofocus } from './stories/WithAutofocus.story';
import { DisableFormatting } from './stories/DisableFormatting.story';
import { ControlledMode } from './stories/ControlledMode.story';
import { CustomFlags } from './stories/CustomFlags.story';
import { AllowMaskOverflow } from './stories/AllowMaskOverflow.story';

export const _Default = Default;
export const _WithInitialValue = WithInitialValue;
Expand All @@ -42,3 +43,4 @@ export const _WithAutofocus = WithAutofocus;
export const _DisableFormatting = DisableFormatting;
export const _ControlledMode = ControlledMode;
export const _CustomFlags = CustomFlags;
export const _AllowMaskOverflow = AllowMaskOverflow;
14 changes: 14 additions & 0 deletions src/stories/PhoneInput/stories/AllowMaskOverflow.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

import { PhoneInput } from '../../../index';
import { PhoneInputStory } from '../PhoneInput.stories';

export const AllowMaskOverflow: PhoneInputStory = {
name: 'Allow Mask Overflow',
render: (args) => <PhoneInput {...args} />,
args: {
defaultCountry: 'us',
allowMaskOverflow: true,
placeholder: 'Enter your phone number',
},
};
70 changes: 70 additions & 0 deletions src/utils/common/__tests__/applyMask.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,74 @@ describe('applyMask', () => {
'(1234',
);
});

test('should handle allowMaskOverflow option', () => {
// format the part that fits, append overflow at the end
expect(
applyMask({
value: '1234567890123',
mask: '.. .. ....',
maskSymbol: '.',
allowMaskOverflow: true,
}),
).toBe('12 34 567890123');

expect(
applyMask({
value: '123456789',
mask: '.... ....',
maskSymbol: '.',
allowMaskOverflow: true,
}),
).toBe('1234 56789');

expect(
applyMask({
value: '23456789012',
mask: '(...) ...-....',
maskSymbol: '.',
allowMaskOverflow: true,
}),
).toBe('(234) 567-89012');

// with offset
expect(
applyMask({
value: '+1234567890123',
mask: '.. .. ....',
maskSymbol: '.',
offset: 2,
allowMaskOverflow: true,
}),
).toBe('+123 45 67890123');

// apply formatting on mask not overflow (normal behavior)
expect(
applyMask({
value: '1234567',
mask: '.. .. ....',
maskSymbol: '.',
allowMaskOverflow: true,
}),
).toBe('12 34 567');

expect(
applyMask({
value: '1234',
mask: '.... ....',
maskSymbol: '.',
allowMaskOverflow: true,
}),
).toBe('1234 ');

expect(
applyMask({
value: '+1234567',
mask: '.. .. ....',
maskSymbol: '.',
offset: 2,
allowMaskOverflow: true,
}),
).toBe('+123 45 67');
});
});
25 changes: 21 additions & 4 deletions src/utils/common/applyMask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ interface ApplyMaskArgs {
* if false -> "(1234) "
*/
trimNonMaskCharsLeftover?: boolean;

/**
* @description Allow input to exceed the mask length. When set to true, formatting mask will apply to the part that fits, and overflow digits will be appended at the end.
* @example
* value: "12345678"
* mask: "(....)"
* if true -> "(1234)5678" (formatted part + overflow)
* if false -> "(1234" (formatted but truncated)
*/
allowMaskOverflow?: boolean;
}

export const applyMask = ({
Expand All @@ -30,14 +40,21 @@ export const applyMask = ({
maskSymbol,
offset = 0,
trimNonMaskCharsLeftover = false,
allowMaskOverflow = false,
}: ApplyMaskArgs): string => {
if (value.length < offset) return value;

const savedValuePart = value.slice(0, offset);
const valueToMask = value.slice(offset);
const prefix = value.slice(0, offset);
const valueToFormat = value.slice(offset);

const maskLength = mask
.split('')
.filter((char) => char === maskSymbol).length;

let result = savedValuePart;
const valueToMask = valueToFormat.slice(0, maskLength);
const overflow = allowMaskOverflow ? valueToFormat.slice(maskLength) : '';

let result = prefix;
let charsPlaced = 0;

for (const maskChar of mask.split('')) {
Expand All @@ -56,5 +73,5 @@ export const applyMask = ({
}
}

return result;
return result + overflow;
};
3 changes: 3 additions & 0 deletions src/utils/handlePhoneChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface PhoneFormattingConfig {
defaultMask: string;
countryGuessingEnabled: boolean;
disableFormatting: boolean;
allowMaskOverflow: boolean;
}

interface HandlePhoneChangeProps extends PhoneFormattingConfig {
Expand All @@ -38,6 +39,7 @@ export function handlePhoneChange({
defaultMask,
countryGuessingEnabled,
disableFormatting,
allowMaskOverflow,
}: HandlePhoneChangeProps): {
phone: string;
inputValue: string;
Expand Down Expand Up @@ -77,6 +79,7 @@ export function handlePhoneChange({
forceDialCode,
insertDialCodeOnEmpty,
disableDialCodeAndPrefix,
allowMaskOverflow,
});

const resultCountry =
Expand Down
2 changes: 2 additions & 0 deletions src/utils/handleUserInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const handleUserInput = (
defaultMask,
disableFormatting,
countries,
allowMaskOverflow,
}: HandleUserInputOptions,
): {
phone: string;
Expand Down Expand Up @@ -131,6 +132,7 @@ export const handleUserInput = (
disableDialCodeAndPrefix,
disableFormatting,
defaultMask,
allowMaskOverflow,
});

const newCursorPosition = getCursorPosition({
Expand Down
8 changes: 8 additions & 0 deletions src/utils/phoneUtils/formatPhone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ export interface FormatPhoneConfig {
* Trim all non-digit values from the end of the result
*/
trimNonDigitsEnd?: boolean;
/**
* Allow input to exceed the mask length. When set to true, formatting mask will apply to the part that fits, and overflow digits will be appended at the end.
* @example
* phone: "+1 23456789012", mask: "(...) ...-...."
* result: "+1 (234) 567-89012"
*/
allowMaskOverflow?: boolean;
}

export const formatPhone = (
Expand Down Expand Up @@ -137,6 +144,7 @@ export const formatPhone = (
trimNonMaskCharsLeftover:
config.trimNonDigitsEnd ||
(config.disableDialCodeAndPrefix && phoneRightSide.length === 0),
allowMaskOverflow: config.allowMaskOverflow,
});

if (config.disableDialCodeAndPrefix) {
Expand Down