@gravityforms/components
Version:
UI components for use in Gravity Forms development. Both React and vanilla js flavors.
170 lines (153 loc) • 5.05 kB
JavaScript
import { React } from '@gravityforms/libraries';
import { getValidLocale, sanitizeLocale } from '@gravityforms/utils';
import { getCountryCallingCode } from 'libphonenumber-js';
const NEEDS_I18N_LABEL = 'Needs i18n';
/**
* @function getUnicodeFlag
* @description Returns a Unicode flag emoji for a given ISO country code.
*
* @since 5.6.2
*
* @param {string} isoCode The ISO country code.
*
* @return {string} The Unicode flag emoji.
*/
export const getUnicodeFlag = ( isoCode ) => {
if ( ! isoCode || isoCode.length !== 2 ) {
return '';
}
// Convert ISO code to regional indicator symbols
// e.g., 'U' (char code 85) becomes 0x1F1FA (regional indicator U)
// The offset is 0x1F1E6 (base for regional indicators) - 0x41 ('A').
return String.fromCodePoint(
...isoCode.toUpperCase()
.split( '' )
.map( ( char ) => char.charCodeAt( 0 ) + ( 0x1F1E6 - 0x41 ) )
);
};
/**
* @function getCountriesListItemsFromIsos
* @description Returns a list of countries for a given ISO country code.
*
* @since 5.6.2
*
* @param {Array} isoCodes The ISO country codes.
* @param {string} language The language to use for the country names.
* @param {object} i18n The i18n object.
* @param {object} options Options for the country list item.
*
* @return {Array} The list of countries.
*/
export const getCountriesListItemsFromIsos = ( isoCodes = [], language = 'en-US', i18n = {}, options = {} ) => {
const { showFlag = true, showCallingCode = false, showPlaceholder = false } = options;
const listItems = [];
const locale = getValidLocale( sanitizeLocale( language ) );
const countryNameProvider = new Intl.DisplayNames( [ locale ], { type: 'region' } );
if ( showPlaceholder ) {
listItems.push( {
label: i18n?.placeholder || NEEDS_I18N_LABEL,
value: '',
} );
}
for ( const iso of isoCodes ) {
try {
const name = countryNameProvider.of( iso.toUpperCase() );
if ( name === iso.toUpperCase() ) {
// Intl.DisplayNames returns the code itself if it can't find a name.
console.warn( `Could not find display name for ISO code: ${ iso }` );
}
let fullCallingCode = '';
if ( showCallingCode ) {
const callingCode = getCountryCallingCode( iso.toUpperCase() );
fullCallingCode = `+${ callingCode }`;
}
listItems.push( {
searchValue: `${ name } ${ fullCallingCode } ${ iso.toUpperCase() }`,
label: name,
value: iso.toUpperCase(),
beforeLabel: showFlag ? <span className="gform-phone__flag-icon">{ getUnicodeFlag( iso.toUpperCase() ) }</span> : undefined,
afterLabel: showCallingCode ? <span className="gform-phone__country-code">{ fullCallingCode }</span> : undefined,
} );
} catch ( error ) {
// Handle cases where ISO code is invalid for getCountryCallingCode or other errors.
console.error( `Error processing ISO code ${ iso }:`, error );
}
}
return listItems;
};
/**
* @function sortPreferredCountries
* @description Sort the countries list items to set preferred countries first.
* If preferredCountries is empty, return the list as is.
* If preferredCountries is not empty, split the list into preferred and remaining countries.
*
* @since 5.5.0
*
* @param {Array} listItems List of countries.
* @param {Array} preferredCountries List of preferred countries.
* @param {object} i18n i18n object.
*
* @return {Array} The sorted countries list.
*/
export const sortPreferredCountries = ( listItems, preferredCountries, i18n ) => {
if ( preferredCountries.length === 0 ) {
return listItems;
}
let placeholder = null;
const filteredListItems = listItems.filter( ( item ) => {
if ( ! item.value ) {
// Item is placeholder, remove it.
placeholder = item;
return false;
}
return true;
} );
const countryMap = new Map();
filteredListItems.forEach( ( item ) => {
countryMap.set( item.value, item );
} );
// Process the preferred countries in their specified order.
const preferred = [];
const usedISOs = new Set();
preferredCountries.forEach( ( country ) => {
const iso = typeof country === 'string' ? country : country?.value || country?.iso;
if ( iso && countryMap.has( iso ) && ! usedISOs.has( iso ) ) {
preferred.push( countryMap.get( iso ) );
usedISOs.add( iso );
}
} );
// If no preferred countries were found, return the list as is.
if ( preferred.length === 0 ) {
return listItems;
}
const remaining = filteredListItems.filter( ( item ) => ! usedISOs.has( item.value ) );
if ( placeholder ) {
preferred.unshift( placeholder );
}
// If no remaining countries were found, return the preferred list.
if ( remaining.length === 0 ) {
return preferred;
}
return [
{
type: 'group',
items: preferred,
label: i18n?.preferredCountries
? {
type: 'groupLabel',
label: i18n.preferredCountries,
}
: undefined,
},
{
type: 'group',
items: remaining,
label: i18n?.allCountries
? {
type: 'groupLabel',
label: i18n.allCountries,
}
: undefined,
},
];
};