Files
itflow/libs/intl-tel-input/js/constants.ts
2026-08-26 13:14:32 -04:00

214 lines
6.7 KiB
TypeScript

// Shared constants extracted from intl-tel-input logic to avoid magic strings/numbers
import type { Iso2 } from "./data.js";
export const EVENTS = {
OPEN_COUNTRY_SELECTOR: "open:countryselector",
CLOSE_COUNTRY_SELECTOR: "close:countryselector",
COUNTRY_CHANGE: "countrychange",
INPUT: "input", // used for synthetic input trigger
STRICT_REJECT: "strict:reject",
} as const;
//* The named "slots" (i.e. the elements we generate) that consumers can add their own classes
//* to, via the classNames option. Deliberately decoupled from our internal iti__* class names,
//* so the markup remains an implementation detail. Used to validate the option at runtime, and
//* to derive the ItiClassNames type. Keep in sync with the ItiClassNames interface.
export const ITI_SLOTS = [
"container",
"input",
"countryContainer",
"selectedCountry",
"selectedCountryPrimary",
"selectedFlag",
"arrow",
"selectedDialCode",
"countrySelector",
"countrySelectorContainer",
"searchWrapper",
"searchIcon",
"searchInput",
"searchClear",
"countryList",
"countryListItem",
"countryListItemFlag",
"countryName",
"dialCode",
"countryCheck",
"noResults",
] as const;
export const CLASSES = {
HIDE: "iti__hide",
V_HIDE: "iti__v-hide",
ARROW_UP: "iti__arrow--up",
GLOBE: "iti__globe",
FLAG: "iti__flag",
LOADING: "iti__loading",
COUNTRY_ITEM: "iti__country",
HIGHLIGHT: "iti__highlight",
STRICT_REJECT_ANIMATION: "iti__strict-reject-animation",
} as const;
export const KEYS = {
ARROW_UP: "ArrowUp",
ARROW_DOWN: "ArrowDown",
SPACE: " ",
ENTER: "Enter",
ESC: "Escape",
TAB: "Tab",
} as const;
export const INPUT_TYPES = {
PASTE: "insertFromPaste",
DELETE_FORWARD: "deleteContentForward",
} as const;
export const REGEX = {
ALPHA_UNICODE: /\p{L}/u, // any kind of letter from any language
NON_PLUS_NUMERIC: /[^+0-9]/, // chars that are NOT + or digit
NON_PLUS_NUMERIC_GLOBAL: /[^+0-9]/g, // chars that are NOT + or digit (global)
HIDDEN_SEARCH_CHAR: /^[a-zA-ZÀ-ÿа-яА-Я ]$/, // single acceptable hidden-search char
} as const;
export const TIMINGS = {
SEARCH_DEBOUNCE_MS: 100,
HIDDEN_SEARCH_RESET_MS: 1000,
NEXT_TICK: 0,
} as const;
// Layout / sizing fallbacks (used when measuring elements fails e.g., hidden containers)
// TODO: find a way to pull these out of the CSS, so there's a single source of truth.
export const LAYOUT = {
NARROW_VIEWPORT_WIDTH: 500, // keep in sync with .iti__country-list CSS media query
FALLBACK_SELECTED_COUNTRY_WITH_DIAL_WIDTH: 78, // px width fallback when separateDialCode enabled
FALLBACK_SELECTED_COUNTRY_NO_DIAL_WIDTH: 42, // px width fallback when no separate dial code
INPUT_PADDING_EXTRA_LEFT: 6, // px gap between selected country container and input text
DROPDOWN_MARGIN: 3, // px margin between dropdown and tel input
FALLBACK_DROPDOWN_HEIGHT: 200, // px height fallback for dropdown
} as const;
// Helpful grouping for dial code logic (kept lean; expand only if reused widely)
export const DIAL_CODE = {
PLUS: "+",
NANP: "1", // North American Numbering Plan
} as const;
// E.164 caps a full international number (dial code + national number) at 15
// digits. Used as a fallback max length in strictMode when there is no selected
// country, and so no country-specific max to enforce.
export const E164_MAX_DIGITS = 15;
// Country-specific telephone rules
export const UK = {
ISO2: "gb" as Iso2,
DIAL_CODE: "44", // +44 United Kingdom
MOBILE_PREFIX: "7", // UK mobile numbers start with 7 after national trunk (0) or core section
MOBILE_CORE_LENGTH: 10, // core number length (excluding dial code / national prefix) for mobiles
} as const;
export const US = {
ISO2: "us" as Iso2,
DIAL_CODE: "1", // +1 United States
};
// Public-facing enum constant for placeholderNumberPolicy. Singular to match
// NUMBER_FORMAT / NUMBER_TYPE / VALIDATION_ERROR. Hand-defined (rather than
// derived via toEnumObject) because there's no parallel array to be the source
// of truth.
export const PLACEHOLDER_POLICY = {
AGGRESSIVE: "AGGRESSIVE",
POLITE: "POLITE",
OFF: "OFF",
} as const;
// Valid values for the countrySelectorMode option. The COUNTRY_SELECTOR_MODE
// enum object is derived from this array (below, alongside NUMBER_FORMAT etc).
export const COUNTRY_SELECTOR_MODES = [
"OFF",
"DROPDOWN",
"FULLSCREEN",
"AUTO",
] as const;
// libphonenumber enums - sole source of truth for both the core library (option
// validation, public types) and utils.js (which builds its int maps from
// these arrays at build time via scripts/build-utils.js).
//
// Order matters - the array index is the integer value libphonenumber uses,
// EXCEPT for NUMBER_TYPES.UNKNOWN which libphonenumber maps to -1 (handled
// explicitly in utils.js).
//
// Mirrors:
// - i18n.phonenumbers.PhoneNumberFormat
// - i18n.phonenumbers.PhoneNumberType
// - i18n.phonenumbers.PhoneNumberUtil.ValidationResult
// in third_party/libphonenumber/javascript/i18n/phonenumbers/phonenumberutil.js
export const NUMBER_FORMATS = [
"E164",
"INTERNATIONAL",
"NATIONAL",
"RFC3966",
] as const;
export const NUMBER_TYPES = [
"FIXED_LINE",
"MOBILE",
"FIXED_LINE_OR_MOBILE",
"TOLL_FREE",
"PREMIUM_RATE",
"SHARED_COST",
"VOIP",
"PERSONAL_NUMBER",
"PAGER",
"UAN",
"VOICEMAIL",
"UNKNOWN",
] as const;
export const VALIDATION_ERRORS = [
"IS_POSSIBLE",
"INVALID_COUNTRY_CODE",
"TOO_SHORT",
"TOO_LONG",
"IS_POSSIBLE_LOCAL_ONLY",
"INVALID_LENGTH",
] as const;
// Convenience constants so consumers can write NUMBER_TYPE.MOBILE instead of
// the bare string "MOBILE" - typo-safe and matches the PLACEHOLDER_POLICY style.
// Derived from the arrays above so there's no second list to keep in sync.
const toEnumObject = <T extends readonly string[]>(
arr: T,
): Readonly<{ [K in T[number]]: K }> =>
Object.fromEntries(arr.map((v) => [v, v])) as Readonly<{
[K in T[number]]: K;
}>;
export const NUMBER_FORMAT = toEnumObject(NUMBER_FORMATS);
export const NUMBER_TYPE = toEnumObject(NUMBER_TYPES);
export const VALIDATION_ERROR = toEnumObject(VALIDATION_ERRORS);
export const COUNTRY_SELECTOR_MODE = toEnumObject(COUNTRY_SELECTOR_MODES);
// Data-* keys used on DOM nodes
export const DATA_KEYS = {
// e.g. <li data-iso2="us"> for country items in the country list
ISO2: "iso2",
DIAL_CODE: "dialCode",
// e.g. <input data-intl-tel-input-id="0"> on the input element
INSTANCE_ID: "intlTelInputId",
} as const;
// ARIA attribute names (avoid typos & for potential future refactors)
export const ARIA = {
EXPANDED: "aria-expanded",
LABEL: "aria-label",
SELECTED: "aria-selected",
ACTIVE_DESCENDANT: "aria-activedescendant",
HASPOPUP: "aria-haspopup",
CONTROLS: "aria-controls",
HIDDEN: "aria-hidden",
AUTOCOMPLETE: "aria-autocomplete",
MODAL: "aria-modal",
} as const;