Phone input done

This commit is contained in:
Shaurya Sharma 2025-03-20 03:08:38 +05:30
parent ccc14a030c
commit e9dc0f5710
9 changed files with 1464 additions and 70 deletions

View file

@ -0,0 +1,96 @@
import React, { useState } from 'react';
import Accordion from '@/_ui/Accordion';
import { baseComponentProperties } from '../DefaultComponent';
import Select from '@/_ui/Select';
import useStore from '@/AppBuilder/_stores/store';
import { countries } from './en';
export const PhoneInput = ({ componentMeta, darkMode, ...restProps }) => {
const {
layoutPropertyChanged,
component,
paramUpdated,
dataQueries,
currentState,
eventsChanged,
apps,
allComponents,
} = restProps;
const properties = Object.keys(componentMeta.properties);
const events = Object.keys(componentMeta.events);
const validations = Object.keys(componentMeta.validation || {});
const resolvedProperties = useStore((state) => state.getResolvedComponent(component.id)?.properties);
const defaultCountry = resolvedProperties?.defaultCountry || 'None';
const renderCustomOption = ({ label, value: optionValue }) => {
const optionStyle = {
display: 'flex',
alignItems: 'center',
justifyContent: 'start',
height: '18px',
gap: '6px',
cursor: 'pointer',
fontFamily: 'IBM Plex Sans',
fontSize: '12px',
lineHeight: '18px',
fontWeight: '400',
color: darkMode ? '#fff' : '#1B1F24',
};
return (
<div style={optionStyle} className={`selectedOption ${optionValue !== 'none' && 'custom-phone-input-options'}`}>
<div style={{ width: '25px', height: '16px' }} className={`flag ${optionValue}`}></div>
{label}
</div>
);
};
const getCountryDropdown = () => {
return (
<div className="mb-2">
<label class="tj-text-xsm color-slate12 mb-2 false">Default country</label>
<Select
width="100%"
options={countries}
value={defaultCountry}
customOption={renderCustomOption}
onChange={(value) => {
console.log('value', value);
paramUpdated({ name: 'defaultCountry' }, 'value', value, 'properties');
}}
/>
</div>
);
};
const filteredProperties = properties.filter(
(property) => componentMeta.properties[property].section !== 'additionalActions'
);
const additionalActions = properties.filter(
(property) => componentMeta.properties[property].section === 'additionalActions'
);
const accordionItems = baseComponentProperties(
filteredProperties,
events,
component,
componentMeta,
layoutPropertyChanged,
paramUpdated,
dataQueries,
currentState,
eventsChanged,
apps,
allComponents,
validations,
darkMode,
null,
additionalActions
);
accordionItems[0].children.splice(3, 0, getCountryDropdown());
return <Accordion items={accordionItems} />;
};

View file

@ -0,0 +1,239 @@
export const countries = Object.entries({
af: 'Afghanistan',
al: 'Albania',
dz: 'Algeria',
um: 'United States Minor Outlying Islands',
ad: 'Andorra',
ao: 'Angola',
ai: 'Anguilla',
aq: 'Antarctica',
ag: 'Antigua and Barbuda',
sa: 'Saudi Arabia',
ar: 'Argentina',
am: 'Armenia',
aw: 'Aruba',
au: 'Australia',
at: 'Austria',
az: 'Azerbaijan',
bs: 'Bahamas',
bh: 'Bahrain',
bd: 'Bangladesh',
bb: 'Barbados',
be: 'Belgium',
bz: 'Belize',
bj: 'Benin',
bm: 'Bermuda',
bt: 'Bhutan',
by: 'Belarus',
bo: 'Bolivia',
ba: 'Bosnia and Herzegovina',
bw: 'Botswana',
br: 'Brazil',
bn: 'Brunei',
bg: 'Bulgaria',
bf: 'Burkina Faso',
bi: 'Burundi',
kh: 'Cambodia',
cm: 'Cameroon',
ca: 'Canada',
cv: 'Cape Verde',
bq: 'Caribbean Netherlands',
cz: 'Czechia',
ea: 'Ceuta and Melilla',
td: 'Chad',
cl: 'Chile',
cn: 'China',
cy: 'Cyprus',
va: 'Vatican City',
co: 'Colombia',
km: 'Comoros',
cd: 'Congo - Kinshasa',
cg: 'Congo - Brazzaville',
kp: 'North Korea',
kr: 'South Korea',
ci: 'Ivory Coast',
cr: 'Costa Rica',
hr: 'Croatia',
cu: 'Cuba',
cw: 'Curaçao',
dk: 'Denmark',
dg: 'Diego Garcia',
dm: 'Dominica',
ec: 'Ecuador',
eg: 'Egypt',
sv: 'El Salvador',
ae: 'United Arab Emirates',
er: 'Eritrea',
ee: 'Estonia',
et: 'Ethiopia',
fj: 'Fiji',
ph: 'Philippines',
fi: 'Finland',
fr: 'France',
ga: 'Gabon',
gm: 'Gambia',
ge: 'Georgia',
gs: 'South Georgia and the South Sandwich Islands',
de: 'Germany',
gh: 'Ghana',
jm: 'Jamaica',
jp: 'Japan',
gi: 'Gibraltar',
dj: 'Djibouti',
jo: 'Jordan',
gr: 'Greece',
gd: 'Grenada',
gl: 'Greenland',
gp: 'Guadeloupe',
gu: 'Guam',
gt: 'Guatemala',
gg: 'Guernsey',
gn: 'Guinea',
gq: 'Equatorial Guinea',
gw: 'Guinea-Bissau',
gy: 'Guyana',
gf: 'French Guiana',
ht: 'Haiti',
hn: 'Honduras',
in: 'India',
id: 'Indonesia',
ir: 'Iran',
iq: 'Iraq',
ie: 'Ireland',
is: 'Iceland',
ac: 'Ascension Island',
cx: 'Christmas Island',
im: 'Isle of Man',
nf: 'Norfolk Island',
ax: 'Åland Islands',
ic: 'Canary Islands',
ky: 'Cayman Islands',
cc: 'Cocos (Keeling) Islands',
ck: 'Cook Islands',
fo: 'Faroe Islands',
fk: 'Falkland Islands',
mp: 'Northern Mariana Islands',
mh: 'Marshall Islands',
pn: 'Pitcairn Islands',
sb: 'Solomon Islands',
tc: 'Turks and Caicos Islands',
vi: 'U.S. Virgin Islands',
vg: 'British Virgin Islands',
il: 'Israel',
it: 'Italy',
je: 'Jersey',
kz: 'Kazakhstan',
ke: 'Kenya',
kg: 'Kyrgyzstan',
ki: 'Kiribati',
xk: 'Kosovo',
kw: 'Kuwait',
la: 'Laos',
ls: 'Lesotho',
lv: 'Latvia',
lb: 'Lebanon',
lr: 'Liberia',
ly: 'Libya',
li: 'Liechtenstein',
lt: 'Lithuania',
lu: 'Luxembourg',
mk: 'North Macedonia',
mg: 'Madagascar',
mw: 'Malawi',
my: 'Malaysia',
mv: 'Maldives',
ml: 'Mali',
mt: 'Malta',
ma: 'Morocco',
mq: 'Martinique',
mr: 'Mauritania',
mu: 'Mauritius',
yt: 'Mayotte',
mx: 'Mexico',
fm: 'Micronesia',
md: 'Moldova',
mc: 'Monaco',
mn: 'Mongolia',
me: 'Montenegro',
ms: 'Montserrat',
mz: 'Mozambique',
mm: 'Myanmar',
na: 'Namibia',
nr: 'Nauru',
np: 'Nepal',
ni: 'Nicaragua',
ne: 'Niger',
ng: 'Nigeria',
nu: 'Niue',
no: 'Norway',
nc: 'New Caledonia',
nz: 'New Zealand',
om: 'Oman',
nl: 'Netherlands',
pk: 'Pakistan',
pw: 'Palau',
pa: 'Panama',
pg: 'Papua New Guinea',
py: 'Paraguay',
pe: 'Peru',
pf: 'French Polynesia',
pl: 'Poland',
pt: 'Portugal',
pr: 'Puerto Rico',
qa: 'Qatar',
hk: 'Hong Kong SAR',
mo: 'Macau SAR',
gb: 'United Kingdom',
cf: 'Central African Republic',
do: 'Dominican Republic',
re: 'Réunion',
ro: 'Romania',
rw: 'Rwanda',
ru: 'Russia',
eh: 'Western Sahara',
kn: 'Saint Kitts and Nevis',
lc: 'Saint Lucia',
mf: 'Saint Martin',
vc: 'Saint Vincent and the Grenadines',
bl: 'Saint Barthélemy',
pm: 'Saint Pierre and Miquelon',
ws: 'Samoa',
as: 'American Samoa',
sm: 'San Marino',
sh: 'Saint Helena',
st: 'São Tomé and Príncipe',
sn: 'Senegal',
rs: 'Serbia',
sc: 'Seychelles',
sl: 'Sierra Leone',
sg: 'Singapore',
sx: 'Sint Maarten',
sy: 'Syria',
sk: 'Slovakia',
si: 'Slovenia',
so: 'Somalia',
es: 'Spain',
lk: 'Sri Lanka',
us: 'United States',
ss: 'South Sudan',
za: 'South Africa',
sd: 'Sudan',
sr: 'Suriname',
se: 'Sweden',
ch: 'Switzerland',
tj: 'Tajikistan',
th: 'Thailand',
tl: 'Timor-Leste',
tg: 'Togo',
tn: 'Tunisia',
tr: 'Turkey',
ua: 'Ukraine',
ug: 'Uganda',
hu: 'Hungary',
uy: 'Uruguay',
uz: 'Uzbekistan',
vn: 'Vietnam',
ye: 'Yemen',
zm: 'Zambia',
zw: 'Zimbabwe',
}).map(([value, label]) => ({ value, label }));

View file

@ -8,6 +8,7 @@ import { validateQueryName, convertToKebabCase, resolveReferences } from '@/_hel
import { useHotkeys } from 'react-hotkeys-hook';
import { DefaultComponent } from './Components/DefaultComponent';
import { FilePicker } from './Components/FilePicker';
import { PhoneInput } from './Components/PhoneInput/PhoneInput.jsx';
import { Modal } from './Components/Modal';
import { ModalV2 } from './Components/ModalV2';
import { CustomComponent } from './Components/CustomComponent';
@ -734,6 +735,8 @@ const GetAccordion = React.memo(
case 'DatePickerV2':
case 'TimePicker':
return <DatetimePickerV2 {...restProps} componentName={componentName} />;
case 'PhoneInput':
return <PhoneInput {...restProps} />;
default: {
return <DefaultComponent {...restProps} />;

View file

@ -205,9 +205,9 @@ export const phoneinputConfig = {
},
actions: [
{
handle: 'setText',
displayName: 'Set text',
params: [{ handle: 'text', displayName: 'text', defaultValue: 'New text' }],
handle: 'setValue',
displayName: 'Set Value',
params: [{ handle: 'value', displayName: 'value', defaultValue: '00' }],
},
{
handle: 'clear',
@ -221,16 +221,6 @@ export const phoneinputConfig = {
handle: 'setBlur',
displayName: 'Set blur',
},
{
handle: 'disable',
displayName: 'Disable(deprecated)',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'visibility',
displayName: 'Visibility(deprecated)',
params: [{ handle: 'visibility', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',

View file

@ -10,6 +10,7 @@ export const useInput = ({
setExposedVariable,
setExposedVariables,
fireEvent,
inputType,
}) => {
const isInitialRender = useRef(true);
const inputRef = useRef();
@ -90,8 +91,9 @@ export const useInput = ({
}, [properties.value]);
useEffect(() => {
const setterName = inputType === 'phone' ? 'setValue' : 'setText';
const exposedVariables = {
setText: async function (text) {
[setterName]: async function (text) {
setInputValue(text);
fireEvent('onChange');
},

View file

@ -7,7 +7,11 @@ import Label from '@/_ui/Label';
export const PhoneInput = (props) => {
const { properties, styles, componentName, darkMode } = props;
const inputLogic = useInput(props);
const transformedProps = {
...props,
inputType: 'phone',
};
const inputLogic = useInput(transformedProps);
const {
inputRef,
labelRef,
@ -30,7 +34,7 @@ export const PhoneInput = (props) => {
handleFocus,
handleKeyUp,
} = inputLogic;
const { label, placeholder, isCountryChangeEnabled } = properties;
const { label, placeholder, isCountryChangeEnabled, defaultCountry = 'us' } = properties;
const {
textColor,
backgroundColor,
@ -42,6 +46,8 @@ export const PhoneInput = (props) => {
borderColor,
accentColor,
errTextColor,
boxShadow,
borderRadius,
} = styles;
const _width = (width / 100) * 70;
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
@ -57,14 +63,34 @@ export const PhoneInput = (props) => {
: 'var(--borders-default)';
const inputStyle = {
color: textColor,
backgroundColor,
color: darkMode && textColor === '#1B1F24' ? '#FFF' : textColor,
backgroundColor: disable ? '#e4e7eb' : darkMode && backgroundColor === '#fff' ? '#1c2025' : backgroundColor,
border: `${isFocused ? '1.5px' : '1px'} solid ${inputBorderColor}`,
boxShadow,
borderRadius: `${borderRadius}px`,
};
const dropdownStyle = {
backgroundColor: darkMode ? '#1B1F24' : '#fff',
color: darkMode ? '#fff' : '#1B1F24',
};
const searchStyle = {
backgroundColor: darkMode ? '#1B1F24' : '#fff',
color: darkMode ? '#fff' : '#1B1F24',
};
const containerStyle = {
backgroundColor: darkMode ? '#1B1F24' : '#fff',
color: darkMode ? '#fff' : '#1B1F24',
borderRadius: `${borderRadius}px`,
};
const buttonStyle = {
backgroundColor: backgroundColor,
backgroundColor: disable ? '#e4e7eb' : darkMode && backgroundColor === '#fff' ? '#1c2025' : backgroundColor,
border: `${isFocused ? '1.5px' : '1px'} solid ${inputBorderColor}`,
borderTopLeftRadius: `${borderRadius}px`,
borderBottomLeftRadius: `${borderRadius}px`,
};
const loaderStyle = {
@ -90,7 +116,7 @@ export const PhoneInput = (props) => {
<>
<div
data-cy={`label-${String(componentName).toLowerCase()}`}
className={`text-input d-flex ${
className={`text-input d-flex phone-input-widget ${
defaultAlignment === 'top' &&
((width != 0 && label?.length != 0) || (auto && width == 0 && label && label?.length != 0))
? 'flex-column'
@ -130,8 +156,16 @@ export const PhoneInput = (props) => {
disabled={disable || loading}
onBlur={handleBlur}
onFocus={handleFocus}
onKeyUp={handleKeyUp}
disableDropdown={isCountryChangeEnabled}
inputProps={{
autoFocus: true,
}}
onKeyDown={handleKeyUp}
disableDropdown={!isCountryChangeEnabled}
{...(defaultCountry !== 'none' && { country: defaultCountry })}
countryCodeEditable={isCountryChangeEnabled}
dropdownStyle={dropdownStyle}
searchStyle={searchStyle}
containerStyle={containerStyle}
/>
{loading && <Loader style={loaderStyle} width="16" />}
</div>

File diff suppressed because one or more lines are too long

View file

@ -297,10 +297,18 @@ input:checked+.slider:before {
}
}
.theme-dark {
.form-control {
background-color: unset !important;
}
.react-tel-input .form-control {
background-color: inherit !important; // Or any default value you prefer
}
}
.dark-theme {

View file

@ -205,9 +205,9 @@ export const phoneinputConfig = {
},
actions: [
{
handle: 'setText',
displayName: 'Set text',
params: [{ handle: 'text', displayName: 'text', defaultValue: 'New text' }],
handle: 'setValue',
displayName: 'Set Value',
params: [{ handle: 'value', displayName: 'value', defaultValue: '00' }],
},
{
handle: 'clear',
@ -221,16 +221,6 @@ export const phoneinputConfig = {
handle: 'setBlur',
displayName: 'Set blur',
},
{
handle: 'disable',
displayName: 'Disable(deprecated)',
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'visibility',
displayName: 'Visibility(deprecated)',
params: [{ handle: 'visibility', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',