diff --git a/frontend/assets/images/icons/widgets/emailinput.jsx b/frontend/assets/images/icons/widgets/emailinput.jsx
new file mode 100644
index 0000000000..1394959b50
--- /dev/null
+++ b/frontend/assets/images/icons/widgets/emailinput.jsx
@@ -0,0 +1,39 @@
+import React from 'react';
+
+const EmailInput = ({ fill = '#D7DBDF', width = 24, className = '', viewBox = '0 0 49 48' }) => (
+
+);
+
+export default EmailInput;
diff --git a/frontend/assets/images/icons/widgets/index.jsx b/frontend/assets/images/icons/widgets/index.jsx
index 24705fdc6f..5f1715689c 100644
--- a/frontend/assets/images/icons/widgets/index.jsx
+++ b/frontend/assets/images/icons/widgets/index.jsx
@@ -59,6 +59,8 @@ import Upstatistics from './upstatistics.jsx';
import Verticaldivider from './verticaldivider.jsx';
import TimePicker from './timepicker.jsx';
import DatepickerV2 from './datepickerv2.jsx';
+import PhoneInput from './phoneinput.jsx';
+import EmailInput from './emailinput.jsx';
const WidgetIcon = (props) => {
switch (props.name) {
@@ -99,6 +101,10 @@ const WidgetIcon = (props) => {
return ;
case 'datetimepickerv2':
return ;
+ case 'emailinput':
+ return ;
+ case 'phoneinput':
+ return ;
case 'daterangepicker':
return ;
case 'horizontaldivider':
@@ -180,6 +186,7 @@ const WidgetIcon = (props) => {
case 'text':
return ;
case 'textarea':
+ case 'textarealegacy':
return ;
case 'textinput':
return ;
diff --git a/frontend/assets/images/icons/widgets/phoneinput.jsx b/frontend/assets/images/icons/widgets/phoneinput.jsx
new file mode 100644
index 0000000000..e3ad662143
--- /dev/null
+++ b/frontend/assets/images/icons/widgets/phoneinput.jsx
@@ -0,0 +1,36 @@
+import React from 'react';
+
+const PhoneInput = ({ fill = '#D7DBDF', width = 24, className = '', viewBox = '0 0 49 48' }) => (
+
+);
+
+export default PhoneInput;
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index a7b6357ad3..c268716fd5 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -96,6 +96,7 @@
"react-circular-progressbar": "^2.1.0",
"react-color": "^2.19.3",
"react-copy-to-clipboard": "^5.1.0",
+ "react-currency-input-field": "^3.10.0",
"react-datepicker": "^7.6.0",
"react-dates": "^21.8.0",
"react-datetime": "^3.2.0",
@@ -118,6 +119,7 @@
"react-multi-select-component": "^4.3.4",
"react-pdf": "^6.2.2",
"react-phone-input-2": "^2.15.1",
+ "react-phone-number-input": "^3.4.12",
"react-plotly.js": "^2.6.0",
"react-qr-reader": "^2.2.1",
"react-rnd": "^10.4.1",
@@ -29820,6 +29822,11 @@
"node": ">=10"
}
},
+ "node_modules/country-flag-icons": {
+ "version": "1.5.18",
+ "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.18.tgz",
+ "integrity": "sha512-z+Uzesi8u8IdkViqqbzzbkf3+a7WJpcET5B7sPwTg7GXqPYpVEgNlZ/FC3l8KO4mEf+mNkmzKLppKTN4PlCJEQ=="
+ },
"node_modules/country-regex": {
"version": "1.1.0",
"license": "MIT",
@@ -34649,6 +34656,26 @@
"version": "0.2.4",
"license": "MIT"
},
+ "node_modules/input-format": {
+ "version": "0.3.14",
+ "resolved": "https://registry.npmjs.org/input-format/-/input-format-0.3.14.tgz",
+ "integrity": "sha512-gHMrgrbCgmT4uK5Um5eVDUohuV9lcs95ZUUN9Px2Y0VIfjTzT2wF8Q3Z4fwLFm7c5Z2OXCm53FHoovj6SlOKdg==",
+ "dependencies": {
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.1.0",
+ "react-dom": ">=18.1.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/internal-slot": {
"version": "1.0.7",
"license": "MIT",
@@ -36786,6 +36813,11 @@
"url": "https://github.com/sponsors/dmonad"
}
},
+ "node_modules/libphonenumber-js": {
+ "version": "1.12.6",
+ "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.6.tgz",
+ "integrity": "sha512-PJiS4ETaUfCOFLpmtKzAbqZQjCCKVu2OhTV4SVNNE7c2nu/dACvtCqj4L0i/KWNnIgRv7yrILvBj5Lonv5Ncxw=="
+ },
"node_modules/lie": {
"version": "3.1.1",
"license": "MIT",
@@ -41248,6 +41280,14 @@
"framework-utils": "^1.1.0"
}
},
+ "node_modules/react-currency-input-field": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/react-currency-input-field/-/react-currency-input-field-3.10.0.tgz",
+ "integrity": "sha512-GRmZogHh1e1LrmgXg/fKHSuRLYUnj/c/AumfvfuDMA0UX1mDR6u2NR0fzDemRdq4tNHNLucJeJ2OKCr3ehqyDA==",
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-date-picker": {
"version": "10.6.0",
"license": "MIT",
@@ -42035,6 +42075,22 @@
"react-dom": "^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0"
}
},
+ "node_modules/react-phone-number-input": {
+ "version": "3.4.12",
+ "resolved": "https://registry.npmjs.org/react-phone-number-input/-/react-phone-number-input-3.4.12.tgz",
+ "integrity": "sha512-Raob77KdtLGm49iC6nuOX9qy6Mg16idkgC7Y1mHmvG2WBYoauHpzxYNlfmFskQKeiztrJIwPhPzBhjFwjenNCA==",
+ "dependencies": {
+ "classnames": "^2.5.1",
+ "country-flag-icons": "^1.5.17",
+ "input-format": "^0.3.10",
+ "libphonenumber-js": "^1.11.20",
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/react-plotly.js": {
"version": "2.6.0",
"license": "MIT",
diff --git a/frontend/package.json b/frontend/package.json
index fbab9f7145..776bae399e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -91,6 +91,7 @@
"react-circular-progressbar": "^2.1.0",
"react-color": "^2.19.3",
"react-copy-to-clipboard": "^5.1.0",
+ "react-currency-input-field": "^3.10.0",
"react-datepicker": "^7.6.0",
"react-dates": "^21.8.0",
"react-datetime": "^3.2.0",
@@ -113,6 +114,7 @@
"react-multi-select-component": "^4.3.4",
"react-pdf": "^6.2.2",
"react-phone-input-2": "^2.15.1",
+ "react-phone-number-input": "^3.4.12",
"react-plotly.js": "^2.6.0",
"react-qr-reader": "^2.2.1",
"react-rnd": "^10.4.1",
diff --git a/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx b/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx
index 44b5c38069..1116c3fb42 100644
--- a/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx
+++ b/frontend/src/AppBuilder/AppCanvas/RenderWidget.jsx
@@ -10,7 +10,11 @@ import { BOX_PADDING } from './appCanvasConstants';
const shouldAddBoxShadowAndVisibility = [
'Table',
'TextInput',
+ 'TextArea',
'PasswordInput',
+ 'EmailInput',
+ 'PhoneInput',
+ 'CurrencyInput',
'NumberInput',
'Text',
'Checkbox',
@@ -87,6 +91,7 @@ const RenderWidget = ({
...{ widgetValue: value },
...{ validationObject: unResolvedValidation },
customResolveObjects: customResolvables,
+ componentType,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[validateWidget, customResolvables, unResolvedValidation, resolvedValidation]
diff --git a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/ComponentsManagerTab.jsx b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/ComponentsManagerTab.jsx
index 2ad7977496..ae5aadfac6 100644
--- a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/ComponentsManagerTab.jsx
+++ b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/ComponentsManagerTab.jsx
@@ -118,7 +118,10 @@ export const ComponentsManagerTab = ({ darkMode }) => {
'TextInput',
'NumberInput',
'PasswordInput',
- 'Textarea',
+ 'TextArea',
+ 'EmailInput',
+ 'PhoneInput',
+ 'CurrencyInput',
'ToggleSwitchV2',
'DropdownV2',
'MultiselectV2',
diff --git a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/constants.js b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/constants.js
index fda26656ca..19cfdb7032 100644
--- a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/constants.js
+++ b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/constants.js
@@ -1 +1,9 @@
-export const LEGACY_ITEMS = ['ToggleSwitch', 'DropDown', 'Multiselect', 'RadioButton', 'Datepicker', 'Modal'];
+export const LEGACY_ITEMS = [
+ 'ToggleSwitch',
+ 'DropDown',
+ 'Multiselect',
+ 'RadioButton',
+ 'Datepicker',
+ 'Modal',
+ 'TextArea',
+];
diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/CurrencyInput/CurrencyInput.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/CurrencyInput/CurrencyInput.jsx
new file mode 100644
index 0000000000..9d2948c8aa
--- /dev/null
+++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/CurrencyInput/CurrencyInput.jsx
@@ -0,0 +1,132 @@
+import React, { useMemo, useState } from 'react';
+import Accordion from '@/_ui/Accordion';
+import { baseComponentProperties } from '../DefaultComponent';
+import Select from '@/_ui/Select';
+import useStore from '@/AppBuilder/_stores/store';
+import flags from 'react-phone-number-input/flags';
+import FxButton from '@/AppBuilder/CodeBuilder/Elements/FxButton';
+import CodeHinter from '@/AppBuilder/CodeEditor';
+import cx from 'classnames';
+import { CurrencyMap } from '@/AppBuilder/Widgets/PhoneCurrency/constants';
+
+export const CurrencyInput = ({ 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 || 'US';
+ const isDefaultCountryFxOn = componentMeta?.definition?.properties?.dateFormat?.fxActive || false;
+
+ const options = useMemo(() => {
+ return Object.keys(CurrencyMap).map((country) => ({
+ label: `${CurrencyMap[country].prefix} (${CurrencyMap[country].currency})`,
+ value: country,
+ }));
+ }, []);
+
+ 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',
+ };
+ const FlagIcon = flags[optionValue];
+
+ return (
+
+
{FlagIcon ? : null}
+ {label}
+
+ );
+ };
+
+ const getCountryDropdown = () => {
+ return (
+
+
+
+
+ {
+ paramUpdated({ name: 'dateFormat' }, 'fxActive', !isDefaultCountryFxOn, 'properties');
+ }}
+ />
+
+
+ {isDefaultCountryFxOn ? (
+
paramUpdated({ name: 'defaultCountry' }, 'value', value, 'properties')}
+ />
+ ) : (
+
+ );
+ };
+
+ 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(4, 0, getCountryDropdown());
+
+ return ;
+};
diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/DefaultComponent.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/DefaultComponent.jsx
index f5da78fc32..c7104edb40 100644
--- a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/DefaultComponent.jsx
+++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/DefaultComponent.jsx
@@ -14,8 +14,12 @@ const SHOW_ADDITIONAL_ACTIONS = [
'Text',
'Container',
'TextInput',
+ 'TextArea',
'NumberInput',
'PasswordInput',
+ 'EmailInput',
+ 'PhoneInput',
+ 'CurrencyInput',
'ToggleSwitchV2',
'Checkbox',
'DropdownV2',
@@ -33,6 +37,7 @@ const PROPERTIES_VS_ACCORDION_TITLE = {
NumberInput: 'Data',
ToggleSwitchV2: 'Data',
Checkbox: 'Data',
+ TextArea: 'Data',
Button: 'Data',
Image: 'Data',
Container: 'Data',
@@ -122,6 +127,10 @@ export const baseComponentProperties = (
'Modal',
'TextInput',
'PasswordInput',
+ 'TextArea',
+ 'EmailInput',
+ 'PhoneInput',
+ 'CurrencyInput',
'NumberInput',
'Text',
'Table',
diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/PhoneInput/PhoneInput.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/PhoneInput/PhoneInput.jsx
new file mode 100644
index 0000000000..f246514202
--- /dev/null
+++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/PhoneInput/PhoneInput.jsx
@@ -0,0 +1,135 @@
+import React, { useMemo, useState } from 'react';
+import Accordion from '@/_ui/Accordion';
+import { baseComponentProperties } from '../DefaultComponent';
+import Select from '@/_ui/Select';
+import useStore from '@/AppBuilder/_stores/store';
+import { getCountries } from 'react-phone-number-input/input';
+import en from 'react-phone-number-input/locale/en';
+import flags from 'react-phone-number-input/flags';
+import FxButton from '@/AppBuilder/CodeBuilder/Elements/FxButton';
+import CodeHinter from '@/AppBuilder/CodeEditor';
+import cx from 'classnames';
+
+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 || 'US';
+ const isDefaultCountryFxOn = componentMeta?.definition?.properties?.dateFormat?.fxActive || false;
+
+ const options = useMemo(
+ () =>
+ getCountries().map((country) => ({
+ label: `${en[country]}`,
+ value: country,
+ })),
+ []
+ );
+
+ 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',
+ };
+ const FlagIcon = flags[optionValue];
+
+ return (
+
+
{FlagIcon ? : null}
+ {label}
+
+ );
+ };
+
+ const getCountryDropdown = () => {
+ return (
+
+
+
+
+ {
+ paramUpdated({ name: 'dateFormat' }, 'fxActive', !isDefaultCountryFxOn, 'properties');
+ }}
+ />
+
+
+ {isDefaultCountryFxOn ? (
+
paramUpdated({ name: 'defaultCountry' }, 'value', value, 'properties')}
+ />
+ ) : (
+
+ );
+ };
+
+ 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 ;
+};
diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Inspector.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Inspector.jsx
index ec3aac4309..9e227b1e05 100644
--- a/frontend/src/AppBuilder/RightSideBar/Inspector/Inspector.jsx
+++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Inspector.jsx
@@ -8,6 +8,8 @@ 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 { CurrencyInput } from './Components/CurrencyInput/CurrencyInput.jsx';
import { Modal } from './Components/Modal';
import { ModalV2 } from './Components/ModalV2';
import { CustomComponent } from './Components/CustomComponent';
@@ -36,6 +38,7 @@ import { EMPTY_ARRAY } from '@/_stores/editorStore';
import { Select } from './Components/Select';
import { deepClone } from '@/_helpers/utilities/utils.helpers';
import useStore from '@/AppBuilder/_stores/store';
+// import { componentTypes } from '@/Editor/WidgetManager/components';
import { componentTypes } from '@/AppBuilder/WidgetManager/componentTypes';
import { copyComponents } from '@/AppBuilder/AppCanvas/appCanvasUtils.js';
import DatetimePickerV2 from './Components/DatetimePickerV2.jsx';
@@ -66,7 +69,11 @@ const INSPECTOR_HEADER_OPTIONS = [
const NEW_REVAMPED_COMPONENTS = [
'Text',
'TextInput',
+ 'TextArea',
'PasswordInput',
+ 'EmailInput',
+ 'PhoneInput',
+ 'CurrencyInput',
'NumberInput',
'Table',
'ToggleSwitchV2',
@@ -731,6 +738,10 @@ const GetAccordion = React.memo(
case 'DatePickerV2':
case 'TimePicker':
return ;
+ case 'PhoneInput':
+ return ;
+ case 'CurrencyInput':
+ return ;
default: {
return ;
diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Utils.js b/frontend/src/AppBuilder/RightSideBar/Inspector/Utils.js
index 62ee032172..7e47f7f6c0 100644
--- a/frontend/src/AppBuilder/RightSideBar/Inspector/Utils.js
+++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Utils.js
@@ -43,6 +43,9 @@ export function renderCustomStyles(
componentConfig.component == 'TextInput' ||
componentConfig.component == 'NumberInput' ||
componentConfig.component == 'PasswordInput' ||
+ componentConfig.component == 'EmailInput' ||
+ componentConfig.component == 'PhoneInput' ||
+ componentConfig.component == 'CurrencyInput' ||
componentConfig.component == 'ToggleSwitchV2' ||
componentConfig.component == 'Checkbox' ||
componentConfig.component == 'Table' ||
diff --git a/frontend/src/AppBuilder/RightSideBar/WidgetBox/WidgetBox.jsx b/frontend/src/AppBuilder/RightSideBar/WidgetBox/WidgetBox.jsx
index 69ded14971..9ce3bbd4ce 100644
--- a/frontend/src/AppBuilder/RightSideBar/WidgetBox/WidgetBox.jsx
+++ b/frontend/src/AppBuilder/RightSideBar/WidgetBox/WidgetBox.jsx
@@ -13,6 +13,7 @@ const NEW_WIDGETS = [
'DatePickerV2',
'TimePicker',
'ModalV2',
+ 'TextArea',
];
export const WidgetBox = ({ component, darkMode }) => {
diff --git a/frontend/src/AppBuilder/WidgetManager/configs/widgetConfig.js b/frontend/src/AppBuilder/WidgetManager/configs/widgetConfig.js
index be2855c476..136ef942c7 100644
--- a/frontend/src/AppBuilder/WidgetManager/configs/widgetConfig.js
+++ b/frontend/src/AppBuilder/WidgetManager/configs/widgetConfig.js
@@ -58,6 +58,9 @@ import {
linkConfig,
iconConfig,
boundedBoxConfig,
+ emailinputConfig,
+ phoneinputConfig,
+ currencyinputConfig,
} from '../widgets';
export const widgets = [
@@ -70,6 +73,9 @@ export const widgets = [
textinputConfig,
numberinputConfig,
passinputConfig,
+ emailinputConfig,
+ phoneinputConfig,
+ currencyinputConfig,
datepickerConfig,
datetimePickerV2Config,
datePickerV2Config,
diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/currencyinput.js b/frontend/src/AppBuilder/WidgetManager/widgets/currencyinput.js
new file mode 100644
index 0000000000..64a8f2df57
--- /dev/null
+++ b/frontend/src/AppBuilder/WidgetManager/widgets/currencyinput.js
@@ -0,0 +1,305 @@
+export const currencyinputConfig = {
+ name: 'CurrencyInput',
+ displayName: 'Currency Input',
+ description: 'Currency input field',
+ component: 'CurrencyInput',
+ defaultSize: {
+ width: 10,
+ height: 40,
+ },
+ others: {
+ showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
+ showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
+ },
+ properties: {
+ label: {
+ type: 'code',
+ displayName: 'Label',
+ validation: { schema: { type: 'string' }, defaultValue: 'Label' },
+ },
+ placeholder: {
+ type: 'code',
+ displayName: 'Placeholder',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: 'Enter your number',
+ },
+ },
+ value: {
+ type: 'code',
+ displayName: 'Default value',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 0,
+ },
+ },
+ decimalPlaces: {
+ type: 'code',
+ displayName: 'Decimal places',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: '2' },
+ },
+ isCountryChangeEnabled: {
+ type: 'toggle',
+ displayName: 'Enable currency change',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ },
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ visibility: {
+ type: 'toggle',
+ displayName: 'Visibility',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ section: 'additionalActions',
+ },
+ disabledState: {
+ type: 'toggle',
+ displayName: 'Disable',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ tooltip: {
+ type: 'code',
+ displayName: 'Tooltip',
+ validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
+ section: 'additionalActions',
+ placeholder: 'Enter tooltip text',
+ },
+ },
+ validation: {
+ mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
+ regex: { type: 'code', displayName: 'Regex', placeholder: '^[a-zA-Z0-9_ -]{3,16}$' },
+ minValue: { type: 'code', displayName: 'Min value', placeholder: 'Enter min value' },
+ maxValue: { type: 'code', displayName: 'Max value', placeholder: 'Enter max value' },
+ customRule: {
+ type: 'code',
+ displayName: 'Custom validation',
+ placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
+ },
+ },
+ events: {
+ onChange: { displayName: 'On change' },
+ onEnterPressed: { displayName: 'On enter pressed' },
+ onFocus: { displayName: 'On focus' },
+ onBlur: { displayName: 'On blur' },
+ },
+ styles: {
+ color: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'label',
+ },
+ alignment: {
+ type: 'switch',
+ displayName: 'Alignment',
+ validation: { schema: { type: 'string' }, defaultValue: 'side' },
+ options: [
+ { displayName: 'Side', value: 'side' },
+ { displayName: 'Top', value: 'top' },
+ ],
+ accordian: 'label',
+ },
+ direction: {
+ type: 'switch',
+ displayName: '',
+ validation: { schema: { type: 'string' }, defaultValue: 'left' },
+ showLabel: false,
+ isIcon: true,
+ options: [
+ { displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
+ { displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
+ ],
+ accordian: 'label',
+ isFxNotRequired: true,
+ },
+ width: {
+ type: 'slider',
+ displayName: 'Width',
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+ auto: {
+ type: 'checkbox',
+ displayName: 'auto',
+ showLabel: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+
+ backgroundColor: {
+ type: 'color',
+ displayName: 'Background',
+ validation: { schema: { type: 'string' }, defaultValue: '#fff' },
+ accordian: 'field',
+ },
+ borderColor: {
+ type: 'color',
+ displayName: 'Border',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ },
+ accentColor: {
+ type: 'color',
+ displayName: 'Accent',
+ validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
+ accordian: 'field',
+ },
+ textColor: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'field',
+ },
+ errTextColor: {
+ type: 'color',
+ displayName: 'Error text',
+ validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
+ accordian: 'field',
+ },
+ icon: {
+ type: 'icon',
+ displayName: 'Icon',
+ validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
+ accordian: 'field',
+ visibility: false,
+ },
+ iconColor: {
+ type: 'color',
+ displayName: 'Icon color',
+ validation: { schema: { type: 'string' }, defaultValue: '#CFD3D859' },
+ accordian: 'field',
+ visibility: false,
+ showLabel: false,
+ },
+ borderRadius: {
+ type: 'numberInput',
+ displayName: 'Border radius',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
+ accordian: 'field',
+ },
+ boxShadow: {
+ type: 'boxShadow',
+ displayName: 'Box Shadow',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: '0px 0px 0px 0px #00000040',
+ },
+ accordian: 'field',
+ },
+ padding: {
+ type: 'switch',
+ displayName: 'Padding',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 'default',
+ },
+ isFxNotRequired: true,
+ options: [
+ { displayName: 'Default', value: 'default' },
+ { displayName: 'None', value: 'none' },
+ ],
+ accordian: 'container',
+ },
+ },
+ exposedVariables: {
+ value: '',
+ isMandatory: false,
+ isVisible: true,
+ isDisabled: false,
+ isLoading: false,
+ },
+ actions: [
+ {
+ handle: 'setValue',
+ displayName: 'Set Value',
+ params: [
+ { handle: 'value', displayName: 'value', defaultValue: '' },
+ { handle: 'country', displayName: 'country', defaultValue: '' },
+ ],
+ },
+ {
+ handle: 'clear',
+ displayName: 'Clear',
+ },
+ {
+ handle: 'setFocus',
+ displayName: 'Set focus',
+ },
+ {
+ handle: 'setBlur',
+ displayName: 'Set blur',
+ },
+ {
+ handle: 'setVisibility',
+ displayName: 'Set visibility',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisable',
+ displayName: 'Set disable',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ ],
+ definition: {
+ validation: {
+ mandatory: { value: '{{false}}' },
+ regex: { value: '' },
+ minValue: { value: '' },
+ maxValue: { value: '' },
+ customRule: { value: '' },
+ },
+
+ others: {
+ showOnDesktop: { value: '{{true}}' },
+ showOnMobile: { value: '{{false}}' },
+ },
+ properties: {
+ value: { value: '0' },
+ label: { value: 'Label' },
+ placeholder: { value: 'Enter amount' },
+ visibility: { value: '{{true}}' },
+ disabledState: { value: '{{false}}' },
+ loadingState: { value: '{{false}}' },
+ tooltip: { value: '' },
+ isCountryChangeEnabled: { value: '{{true}}' },
+ decimalPlaces: { value: '2' },
+ },
+ events: [],
+ styles: {
+ textColor: { value: '#1B1F24' },
+ borderColor: { value: '#CCD1D5' },
+ accentColor: { value: '#4368E3' },
+ errTextColor: { value: '#D72D39' },
+ borderRadius: { value: '{{6}}' },
+ backgroundColor: { value: '#fff' },
+ iconColor: { value: '#CFD3D859' },
+ direction: { value: 'left' },
+ width: { value: '{{33}}' },
+ alignment: { value: 'side' },
+ color: { value: '#1B1F24' },
+ auto: { value: '{{true}}' },
+ padding: { value: 'default' },
+ boxShadow: { value: '0px 0px 0px 0px #00000040' },
+ icon: { value: 'IconHome2' },
+ iconVisibility: { value: false },
+ },
+ },
+};
diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/emailinput.js b/frontend/src/AppBuilder/WidgetManager/widgets/emailinput.js
new file mode 100644
index 0000000000..825340616f
--- /dev/null
+++ b/frontend/src/AppBuilder/WidgetManager/widgets/emailinput.js
@@ -0,0 +1,292 @@
+export const emailinputConfig = {
+ name: 'EmailInput',
+ displayName: 'Email Input',
+ description: 'Email input field',
+ component: 'EmailInput',
+ defaultSize: {
+ width: 10,
+ height: 40,
+ },
+ others: {
+ showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
+ showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
+ },
+ properties: {
+ label: {
+ type: 'code',
+ displayName: 'Label',
+ validation: { schema: { type: 'string' }, defaultValue: 'Label' },
+ },
+ placeholder: {
+ type: 'code',
+ displayName: 'Placeholder',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: 'Enter email',
+ },
+ },
+ value: {
+ type: 'code',
+ displayName: 'Default value',
+ validation: {
+ schema: {
+ type: 'string',
+ },
+ defaultValue: 'Default value',
+ },
+ },
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ visibility: {
+ type: 'toggle',
+ displayName: 'Visibility',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ section: 'additionalActions',
+ },
+ disabledState: {
+ type: 'toggle',
+ displayName: 'Disable',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ tooltip: {
+ type: 'code',
+ displayName: 'Tooltip',
+ validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
+ section: 'additionalActions',
+ placeholder: 'Enter tooltip text',
+ },
+ },
+ validation: {
+ mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
+ regex: { type: 'code', displayName: 'Regex', placeholder: '^[a-zA-Z0-9_ -]{3,16}$' },
+ minLength: { type: 'code', displayName: 'Min length', placeholder: 'Enter min length' },
+ maxLength: { type: 'code', displayName: 'Max length', placeholder: 'Enter max length' },
+ customRule: {
+ type: 'code',
+ displayName: 'Custom validation',
+ placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
+ },
+ },
+ events: {
+ onChange: { displayName: 'On change' },
+ onEnterPressed: { displayName: 'On enter pressed' },
+ onFocus: { displayName: 'On focus' },
+ onBlur: { displayName: 'On blur' },
+ },
+ styles: {
+ color: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'label',
+ },
+ alignment: {
+ type: 'switch',
+ displayName: 'Alignment',
+ validation: { schema: { type: 'string' }, defaultValue: 'side' },
+ options: [
+ { displayName: 'Side', value: 'side' },
+ { displayName: 'Top', value: 'top' },
+ ],
+ accordian: 'label',
+ },
+ direction: {
+ type: 'switch',
+ displayName: '',
+ validation: { schema: { type: 'string' }, defaultValue: 'left' },
+ showLabel: false,
+ isIcon: true,
+ options: [
+ { displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
+ { displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
+ ],
+ accordian: 'label',
+ isFxNotRequired: true,
+ },
+ width: {
+ type: 'slider',
+ displayName: 'Width',
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+ auto: {
+ type: 'checkbox',
+ displayName: 'auto',
+ showLabel: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+
+ backgroundColor: {
+ type: 'color',
+ displayName: 'Background',
+ validation: { schema: { type: 'string' }, defaultValue: '#fff' },
+ accordian: 'field',
+ },
+ borderColor: {
+ type: 'color',
+ displayName: 'Border',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ },
+ accentColor: {
+ type: 'color',
+ displayName: 'Accent',
+ validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
+ accordian: 'field',
+ },
+ textColor: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'field',
+ },
+ errTextColor: {
+ type: 'color',
+ displayName: 'Error text',
+ validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
+ accordian: 'field',
+ },
+ icon: {
+ type: 'icon',
+ displayName: 'Icon',
+ validation: { schema: { type: 'string' }, defaultValue: 'IconMailFilled' },
+ accordian: 'field',
+ visibility: true,
+ },
+ iconColor: {
+ type: 'color',
+ displayName: 'Icon color',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ visibility: false,
+ showLabel: false,
+ },
+ borderRadius: {
+ type: 'numberInput',
+ displayName: 'Border radius',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
+ accordian: 'field',
+ },
+ boxShadow: {
+ type: 'boxShadow',
+ displayName: 'Box Shadow',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: '0px 0px 0px 0px #00000040',
+ },
+ accordian: 'field',
+ },
+ padding: {
+ type: 'switch',
+ displayName: 'Padding',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 'default',
+ },
+ isFxNotRequired: true,
+ options: [
+ { displayName: 'Default', value: 'default' },
+ { displayName: 'None', value: 'none' },
+ ],
+ accordian: 'container',
+ },
+ },
+ exposedVariables: {
+ value: '',
+ isMandatory: false,
+ isVisible: true,
+ isDisabled: false,
+ isLoading: false,
+ },
+ actions: [
+ {
+ handle: 'setText',
+ displayName: 'Set text',
+ params: [{ handle: 'text', displayName: 'text', defaultValue: 'New text' }],
+ },
+ {
+ handle: 'clear',
+ displayName: 'Clear',
+ },
+ {
+ handle: 'setFocus',
+ displayName: 'Set focus',
+ },
+ {
+ handle: 'setBlur',
+ displayName: 'Set blur',
+ },
+ {
+ handle: 'setVisibility',
+ displayName: 'Set visibility',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisable',
+ displayName: 'Set disable',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ ],
+ definition: {
+ validation: {
+ mandatory: { value: '{{false}}' },
+ regex: { value: '' },
+ minLength: { value: '' },
+ maxLength: { value: '' },
+ customRule: { value: '' },
+ },
+
+ others: {
+ showOnDesktop: { value: '{{true}}' },
+ showOnMobile: { value: '{{false}}' },
+ },
+ properties: {
+ value: { value: '' },
+ label: { value: 'Label' },
+ placeholder: { value: 'Enter email' },
+ visibility: { value: '{{true}}' },
+ disabledState: { value: '{{false}}' },
+ loadingState: { value: '{{false}}' },
+ tooltip: { value: '' },
+ },
+ events: [],
+ styles: {
+ textColor: { value: '#1B1F24' },
+ borderColor: { value: '#CCD1D5' },
+ accentColor: { value: '#4368E3' },
+ errTextColor: { value: '#D72D39' },
+ borderRadius: { value: '{{6}}' },
+ backgroundColor: { value: '#fff' },
+ iconColor: { value: '#CCD1D5' },
+ direction: { value: 'left' },
+ width: { value: '{{33}}' },
+ alignment: { value: 'side' },
+ color: { value: '#1B1F24' },
+ auto: { value: '{{true}}' },
+ padding: { value: 'default' },
+ boxShadow: { value: '0px 0px 0px 0px #00000040' },
+ icon: { value: 'IconMailFilled' },
+ iconVisibility: { value: true },
+ },
+ },
+};
diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/index.js b/frontend/src/AppBuilder/WidgetManager/widgets/index.js
index ce0e73fdf5..e79bee68c2 100644
--- a/frontend/src/AppBuilder/WidgetManager/widgets/index.js
+++ b/frontend/src/AppBuilder/WidgetManager/widgets/index.js
@@ -58,6 +58,9 @@ import { kanbanBoardConfig } from './kanbanBoard';
import { datetimePickerV2Config } from './datetimepickerV2';
import { datePickerV2Config } from './datepickerV2';
import { timePickerConfig } from './timepicker';
+import { emailinputConfig } from './emailinput';
+import { phoneinputConfig } from './phoneinput';
+import { currencyinputConfig } from './currencyinput';
export {
buttonConfig,
@@ -73,6 +76,9 @@ export {
datetimePickerV2Config,
datePickerV2Config,
timePickerConfig,
+ emailinputConfig,
+ phoneinputConfig,
+ currencyinputConfig,
checkboxConfig,
radiobuttonConfig, //!Depreciated
radiobuttonV2Config,
diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/phoneinput.js b/frontend/src/AppBuilder/WidgetManager/widgets/phoneinput.js
new file mode 100644
index 0000000000..83e35ffc68
--- /dev/null
+++ b/frontend/src/AppBuilder/WidgetManager/widgets/phoneinput.js
@@ -0,0 +1,288 @@
+export const phoneinputConfig = {
+ name: 'PhoneInput',
+ displayName: 'Phone Input',
+ description: 'Phone input field',
+ component: 'PhoneInput',
+ defaultSize: {
+ width: 10,
+ height: 40,
+ },
+ others: {
+ showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
+ showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
+ },
+ properties: {
+ label: {
+ type: 'code',
+ displayName: 'Label',
+ validation: { schema: { type: 'string' }, defaultValue: 'Label' },
+ },
+ placeholder: {
+ type: 'code',
+ displayName: 'Placeholder',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: 'Enter your input',
+ },
+ },
+ value: {
+ type: 'code',
+ displayName: 'Default value',
+ validation: {
+ schema: {
+ type: 'string',
+ },
+ defaultValue: 'Default value',
+ },
+ },
+ isCountryChangeEnabled: {
+ type: 'toggle',
+ displayName: 'Enable country change',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ },
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ visibility: {
+ type: 'toggle',
+ displayName: 'Visibility',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ section: 'additionalActions',
+ },
+ disabledState: {
+ type: 'toggle',
+ displayName: 'Disable',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ tooltip: {
+ type: 'code',
+ displayName: 'Tooltip',
+ validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
+ section: 'additionalActions',
+ placeholder: 'Enter tooltip text',
+ },
+ },
+ validation: {
+ mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
+ regex: { type: 'code', displayName: 'Regex', placeholder: '^[a-zA-Z0-9_ -]{3,16}$' },
+ minLength: { type: 'code', displayName: 'Min length', placeholder: 'Enter min length' },
+ maxLength: { type: 'code', displayName: 'Max length', placeholder: 'Enter max length' },
+ customRule: {
+ type: 'code',
+ displayName: 'Custom validation',
+ placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
+ },
+ },
+ events: {
+ onChange: { displayName: 'On change' },
+ onEnterPressed: { displayName: 'On enter pressed' },
+ onFocus: { displayName: 'On focus' },
+ onBlur: { displayName: 'On blur' },
+ },
+ styles: {
+ color: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'label',
+ },
+ alignment: {
+ type: 'switch',
+ displayName: 'Alignment',
+ validation: { schema: { type: 'string' }, defaultValue: 'side' },
+ options: [
+ { displayName: 'Side', value: 'side' },
+ { displayName: 'Top', value: 'top' },
+ ],
+ accordian: 'label',
+ },
+ direction: {
+ type: 'switch',
+ displayName: '',
+ validation: { schema: { type: 'string' }, defaultValue: 'left' },
+ showLabel: false,
+ isIcon: true,
+ options: [
+ { displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
+ { displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
+ ],
+ accordian: 'label',
+ isFxNotRequired: true,
+ },
+ width: {
+ type: 'slider',
+ displayName: 'Width',
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+ auto: {
+ type: 'checkbox',
+ displayName: 'auto',
+ showLabel: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+
+ backgroundColor: {
+ type: 'color',
+ displayName: 'Background',
+ validation: { schema: { type: 'string' }, defaultValue: '#fff' },
+ accordian: 'field',
+ },
+ borderColor: {
+ type: 'color',
+ displayName: 'Border',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ },
+ accentColor: {
+ type: 'color',
+ displayName: 'Accent',
+ validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
+ accordian: 'field',
+ },
+ textColor: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'field',
+ },
+ errTextColor: {
+ type: 'color',
+ displayName: 'Error text',
+ validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
+ accordian: 'field',
+ },
+ borderRadius: {
+ type: 'numberInput',
+ displayName: 'Border radius',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
+ accordian: 'field',
+ },
+ boxShadow: {
+ type: 'boxShadow',
+ displayName: 'Box Shadow',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: '0px 0px 0px 0px #00000040',
+ },
+ accordian: 'field',
+ },
+ padding: {
+ type: 'switch',
+ displayName: 'Padding',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 'default',
+ },
+ isFxNotRequired: true,
+ options: [
+ { displayName: 'Default', value: 'default' },
+ { displayName: 'None', value: 'none' },
+ ],
+ accordian: 'container',
+ },
+ },
+ exposedVariables: {
+ value: '',
+ isMandatory: false,
+ isVisible: true,
+ isDisabled: false,
+ isLoading: false,
+ },
+ actions: [
+ {
+ handle: 'setValue',
+ displayName: 'Set Value',
+ params: [
+ { handle: 'value', displayName: 'value', defaultValue: '' },
+ { handle: 'country', displayName: 'country', defaultValue: '' },
+ ],
+ },
+ {
+ handle: 'setCountryCode',
+ displayName: 'Set country code',
+ params: [{ handle: 'countryCode', displayName: 'Country code', defaultValue: '' }],
+ },
+ {
+ handle: 'clear',
+ displayName: 'Clear',
+ },
+ {
+ handle: 'setFocus',
+ displayName: 'Set focus',
+ },
+ {
+ handle: 'setBlur',
+ displayName: 'Set blur',
+ },
+ {
+ handle: 'setVisibility',
+ displayName: 'Set visibility',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisable',
+ displayName: 'Set disable',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ ],
+ definition: {
+ validation: {
+ mandatory: { value: '{{false}}' },
+ regex: { value: '' },
+ minLength: { value: '' },
+ maxLength: { value: '' },
+ customRule: { value: '' },
+ },
+
+ others: {
+ showOnDesktop: { value: '{{true}}' },
+ showOnMobile: { value: '{{false}}' },
+ },
+ properties: {
+ value: { value: '' },
+ label: { value: 'Label' },
+ placeholder: { value: 'Enter your phone' },
+ visibility: { value: '{{true}}' },
+ disabledState: { value: '{{false}}' },
+ loadingState: { value: '{{false}}' },
+ tooltip: { value: '' },
+ isCountryChangeEnabled: { value: '{{true}}' },
+ },
+ events: [],
+ styles: {
+ textColor: { value: '#1B1F24' },
+ borderColor: { value: '#CCD1D5' },
+ accentColor: { value: '#4368E3' },
+ errTextColor: { value: '#D72D39' },
+ borderRadius: { value: '{{6}}' },
+ backgroundColor: { value: '#fff' },
+ direction: { value: 'left' },
+ width: { value: '{{33}}' },
+ alignment: { value: 'side' },
+ color: { value: '#1B1F24' },
+ auto: { value: '{{true}}' },
+ padding: { value: 'default' },
+ boxShadow: { value: '0px 0px 0px 0px #00000040' },
+ },
+ },
+};
diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/textarea.js b/frontend/src/AppBuilder/WidgetManager/widgets/textarea.js
index c5e71be77d..d0e0364334 100644
--- a/frontend/src/AppBuilder/WidgetManager/widgets/textarea.js
+++ b/frontend/src/AppBuilder/WidgetManager/widgets/textarea.js
@@ -4,7 +4,7 @@ export const textareaConfig = {
description: 'Multi-line text input',
component: 'TextArea',
defaultSize: {
- width: 6,
+ width: 10,
height: 100,
},
others: {
@@ -12,82 +12,291 @@ export const textareaConfig = {
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
- value: {
+ label: {
type: 'code',
- displayName: 'Default value',
- validation: {
- schema: { type: 'string' },
- defaultValue: 'default text',
- },
+ displayName: 'Label',
+ validation: { schema: { type: 'string' }, defaultValue: 'Label' },
},
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
- defaultValue: 'Placeholder text',
+ defaultValue: 'Enter your input',
},
},
- },
- events: {},
- styles: {
+ value: {
+ type: 'code',
+ displayName: 'Default value',
+ validation: {
+ schema: {
+ type: 'string',
+ },
+ defaultValue: 'Default value',
+ },
+ },
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
visibility: {
type: 'toggle',
displayName: 'Visibility',
- validation: {
- schema: { type: 'boolean' },
- defaultValue: true,
- },
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
- validation: {
- schema: { type: 'boolean' },
- defaultValue: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ tooltip: {
+ type: 'code',
+ displayName: 'Tooltip',
+ validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
+ section: 'additionalActions',
+ placeholder: 'Enter tooltip text',
+ },
+ },
+ validation: {
+ mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
+ regex: { type: 'code', displayName: 'Regex', placeholder: '^[a-zA-Z0-9_ -]{3,16}$' },
+ minLength: { type: 'code', displayName: 'Min length', placeholder: 'Enter min length' },
+ maxLength: { type: 'code', displayName: 'Max length', placeholder: 'Enter max length' },
+ customRule: {
+ type: 'code',
+ displayName: 'Custom validation',
+ placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
+ },
+ },
+ events: {
+ onChange: { displayName: 'On change' },
+ onEnterPressed: { displayName: 'On enter pressed' },
+ onFocus: { displayName: 'On focus' },
+ onBlur: { displayName: 'On blur' },
+ },
+ styles: {
+ color: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'label',
+ },
+ alignment: {
+ type: 'switch',
+ displayName: 'Alignment',
+ validation: { schema: { type: 'string' }, defaultValue: 'side' },
+ options: [
+ { displayName: 'Side', value: 'side' },
+ { displayName: 'Top', value: 'top' },
+ ],
+ accordian: 'label',
+ },
+ direction: {
+ type: 'switch',
+ displayName: '',
+ validation: { schema: { type: 'string' }, defaultValue: 'left' },
+ showLabel: false,
+ isIcon: true,
+ options: [
+ { displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
+ { displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
+ ],
+ accordian: 'label',
+ isFxNotRequired: true,
+ },
+ width: {
+ type: 'slider',
+ displayName: 'Width',
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
},
+ isFxNotRequired: true,
+ },
+ auto: {
+ type: 'checkbox',
+ displayName: 'auto',
+ showLabel: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+
+ backgroundColor: {
+ type: 'color',
+ displayName: 'Background',
+ validation: { schema: { type: 'string' }, defaultValue: '#fff' },
+ accordian: 'field',
+ },
+ borderColor: {
+ type: 'color',
+ displayName: 'Border',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ },
+ accentColor: {
+ type: 'color',
+ displayName: 'Accent',
+ validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
+ accordian: 'field',
+ },
+ textColor: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'field',
+ },
+ errTextColor: {
+ type: 'color',
+ displayName: 'Error text',
+ validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
+ accordian: 'field',
+ },
+ icon: {
+ type: 'icon',
+ displayName: 'Icon',
+ validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
+ accordian: 'field',
+ visibility: false,
+ },
+ iconColor: {
+ type: 'color',
+ displayName: 'Icon color',
+ validation: { schema: { type: 'string' }, defaultValue: '#CFD3D859' },
+ accordian: 'field',
+ visibility: false,
+ showLabel: false,
},
borderRadius: {
- type: 'code',
+ type: 'numberInput',
displayName: 'Border radius',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
+ accordian: 'field',
+ },
+ boxShadow: {
+ type: 'boxShadow',
+ displayName: 'Box Shadow',
validation: {
- schema: { type: 'number' },
- defaultValue: 4,
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: '0px 0px 0px 0px #00000040',
},
+ accordian: 'field',
+ },
+ padding: {
+ type: 'switch',
+ displayName: 'Padding',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 'default',
+ },
+ isFxNotRequired: true,
+ options: [
+ { displayName: 'Default', value: 'default' },
+ { displayName: 'None', value: 'none' },
+ ],
+ accordian: 'container',
},
},
exposedVariables: {
- value:
- 'ToolJet is an open-source low-code platform for building and deploying internal tools with minimal engineering efforts 🚀',
+ value: '',
+ isMandatory: false,
+ isVisible: true,
+ isDisabled: false,
+ isLoading: false,
},
actions: [
{
handle: 'setText',
- displayName: 'Set Text',
- params: [{ handle: 'text', displayName: 'text', defaultValue: 'New Text' }],
+ displayName: 'Set text',
+ params: [{ handle: 'text', displayName: 'text', defaultValue: 'New text' }],
},
{
handle: 'clear',
displayName: 'Clear',
},
+ {
+ handle: 'setFocus',
+ displayName: 'Set focus',
+ },
+ {
+ 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',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisable',
+ displayName: 'Set disable',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
],
definition: {
+ validation: {
+ mandatory: { value: '{{false}}' },
+ regex: { value: '' },
+ minLength: { value: '' },
+ maxLength: { value: '' },
+ customRule: { value: '' },
+ },
+
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
- value: {
- value:
- 'ToolJet is an open-source low-code platform for building and deploying internal tools with minimal engineering efforts 🚀',
- },
- placeholder: { value: 'Placeholder text' },
+ value: { value: '' },
+ label: { value: 'Label' },
+ placeholder: { value: 'Enter your input' },
+ visibility: { value: '{{true}}' },
+ disabledState: { value: '{{false}}' },
+ loadingState: { value: '{{false}}' },
+ tooltip: { value: '' },
},
events: [],
styles: {
- visibility: { value: '{{true}}' },
- disabledState: { value: '{{false}}' },
- borderRadius: { value: '{{4}}' },
+ textColor: { value: '#1B1F24' },
+ borderColor: { value: '#CCD1D5' },
+ accentColor: { value: '#4368E3' },
+ errTextColor: { value: '#D72D39' },
+ borderRadius: { value: '{{6}}' },
+ backgroundColor: { value: '#fff' },
+ iconColor: { value: '#CFD3D859' },
+ direction: { value: 'left' },
+ width: { value: '{{33}}' },
+ alignment: { value: 'side' },
+ color: { value: '#1B1F24' },
+ auto: { value: '{{true}}' },
+ padding: { value: 'default' },
+ boxShadow: { value: '0px 0px 0px 0px #00000040' },
+ icon: { value: 'IconHome2' },
+ iconVisibility: { value: false },
},
},
};
diff --git a/frontend/src/AppBuilder/Widgets/BaseComponents/BaseInput.jsx b/frontend/src/AppBuilder/Widgets/BaseComponents/BaseInput.jsx
new file mode 100644
index 0000000000..74ce215737
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/BaseComponents/BaseInput.jsx
@@ -0,0 +1,240 @@
+import React, { forwardRef } from 'react';
+import Label from '@/_ui/Label';
+import Loader from '@/ToolJetUI/Loader/Loader';
+import * as Icons from '@tabler/icons-react';
+const tinycolor = require('tinycolor2');
+
+const RenderInput = forwardRef((props, ref) => {
+ return props.inputType !== 'textarea' ? : ;
+});
+
+export const BaseInput = ({
+ height,
+ styles,
+ properties,
+ darkMode,
+ componentName,
+ dataCy,
+ // From useInput hook
+ inputRef,
+ labelRef,
+ visibility,
+ loading,
+ labelWidth,
+ validationError,
+ showValidationError,
+ isFocused,
+ isMandatory,
+ disable,
+ value,
+ handleChange,
+ handleBlur,
+ handleFocus,
+ handleKeyUp,
+ isValid,
+ // Input specific props
+ inputType = 'text',
+ additionalInputProps = {},
+ rightIcon,
+ getCustomStyles,
+}) => {
+ const {
+ padding,
+ borderRadius,
+ borderColor,
+ backgroundColor,
+ textColor,
+ boxShadow,
+ width,
+ alignment,
+ direction,
+ color,
+ auto,
+ errTextColor,
+ iconColor,
+ accentColor,
+ iconVisibility: showLeftIcon,
+ icon,
+ } = styles;
+
+ const { label, placeholder } = properties;
+ const _width = (width / 100) * 70;
+ const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
+
+ const computedStyles = {
+ height: height == 36 ? (padding == 'default' ? '36px' : '40px') : padding == 'default' ? height : height + 4,
+ borderRadius: `${borderRadius}px`,
+ color: !['#1B1F24', '#000', '#000000ff'].includes(textColor)
+ ? textColor
+ : disable || loading
+ ? 'var(--text-disabled)'
+ : 'var(--text-primary)',
+ borderColor: isFocused
+ ? accentColor != '4368E3'
+ ? accentColor
+ : 'var(--primary-accent-strong)'
+ : borderColor != '#CCD1D5'
+ ? borderColor
+ : disable || loading
+ ? '1px solid var(--borders-disabled-on-white)'
+ : 'var(--borders-default)',
+ '--tblr-input-border-color-darker': tinycolor(borderColor).darken(24).toString(),
+ backgroundColor:
+ backgroundColor != '#fff'
+ ? backgroundColor
+ : disable || loading
+ ? darkMode
+ ? 'var(--surfaces-app-bg-default)'
+ : 'var(--surfaces-surface-03)'
+ : 'var(--surfaces-surface-01)',
+ boxShadow,
+ padding: showLeftIcon ? '8px 10px 8px 29px' : '8px 10px',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ };
+
+ let loaderStyle;
+ // for textarea loader position is fixed on top right of input box.
+ if (inputType !== 'textarea') {
+ loaderStyle = {
+ right:
+ direction === 'right' &&
+ defaultAlignment === 'side' &&
+ ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
+ ? `${labelWidth + 11}px`
+ : '11px',
+ top:
+ defaultAlignment === 'top'
+ ? ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
+ 'calc(50% + 10px)'
+ : '',
+ transform:
+ defaultAlignment === 'top' &&
+ ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
+ ' translateY(-50%)',
+ zIndex: 3,
+ };
+ } else {
+ loaderStyle = {
+ right:
+ direction === 'right' &&
+ defaultAlignment === 'side' &&
+ ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
+ ? `${labelWidth + 11}px`
+ : '11px',
+ top: defaultAlignment === 'top' ? '30px' : '10px',
+ transform: 'none',
+ zIndex: 3,
+ };
+ }
+
+ // eslint-disable-next-line import/namespace
+ const IconElement = Icons[icon] ?? Icons['IconHome2'];
+
+ const finalStyles = getCustomStyles ? getCustomStyles(computedStyles) : computedStyles;
+
+ return (
+ <>
+
+
+
+ {showLeftIcon && (
+ 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
+ ? `${labelWidth + 11}px`
+ : '11px',
+ position: 'absolute',
+ top:
+ inputType === 'textarea'
+ ? defaultAlignment === 'top'
+ ? '38px'
+ : '18px'
+ : defaultAlignment === 'side'
+ ? '50%'
+ : (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
+ ? 'calc(50% + 10px)'
+ : '50%',
+ transform: 'translateY(-50%)',
+ color: iconColor !== '#CFD3D859' ? iconColor : 'var(--icons-weak-disabled)',
+ zIndex: 3,
+ }}
+ stroke={1.5}
+ />
+ )}
+
+
+ {rightIcon}
+ {loading && }
+
+
+ {showValidationError && visibility && (
+
+ {validationError}
+
+ )}
+ >
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/BaseComponents/hooks/useInput.js b/frontend/src/AppBuilder/Widgets/BaseComponents/hooks/useInput.js
new file mode 100644
index 0000000000..57260875a2
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/BaseComponents/hooks/useInput.js
@@ -0,0 +1,235 @@
+import { useState, useRef, useEffect } from 'react';
+import { useGridStore } from '@/_stores/gridStore';
+//eslint-disable-next-line import/no-unresolved
+import { getCountryCallingCode } from 'react-phone-number-input';
+
+export const useInput = ({
+ id,
+ properties,
+ styles,
+ validation,
+ validate,
+ setExposedVariable,
+ setExposedVariables,
+ fireEvent,
+ inputType,
+}) => {
+ const isInitialRender = useRef(true);
+ const inputRef = useRef();
+ const labelRef = useRef();
+
+ const { loadingState, disabledState, label, visibility: initialVisibility } = properties;
+ const isResizing = useGridStore((state) => state.resizingComponentId === id);
+
+ const [value, setValue] = useState(properties.value ?? '');
+ const [visibility, setVisibility] = useState(initialVisibility);
+ const [loading, setLoading] = useState(loadingState);
+ const [disable, setDisable] = useState(disabledState || loadingState);
+ const [validationStatus, setValidationStatus] = useState(validate(value));
+ const [showValidationError, setShowValidationError] = useState(false);
+ const [isFocused, setIsFocused] = useState(false);
+ const [labelWidth, setLabelWidth] = useState(0);
+ const [iconVisibility, setIconVisibility] = useState(false);
+ const [country, setCountry] = useState(properties.defaultCountry || 'US');
+
+ const { isValid, validationError } = validationStatus;
+ const isMandatory = validation?.mandatory ?? false;
+
+ const getCountryCallingCodeSafe = (country) => {
+ try {
+ return getCountryCallingCode(country);
+ } catch (error) {
+ return '';
+ }
+ };
+
+ useEffect(() => {
+ if (labelRef?.current) {
+ const absolutewidth = labelRef?.current?.getBoundingClientRect()?.width;
+ setLabelWidth(absolutewidth);
+ } else setLabelWidth(0);
+ }, [
+ isResizing,
+ styles.width,
+ styles.auto,
+ styles.alignment,
+ styles.iconVisibility,
+ label?.length,
+ isMandatory,
+ styles.padding,
+ styles.direction,
+ labelRef?.current?.getBoundingClientRect()?.width,
+ ]);
+
+ useEffect(() => {
+ if (isInitialRender.current) return;
+ setExposedVariable('label', label);
+ }, [label]);
+
+ useEffect(() => {
+ disable !== disabledState && setDisable(disabledState);
+ if (isInitialRender.current) return;
+ setExposedVariable('isDisabled', disabledState);
+ }, [disabledState]);
+
+ useEffect(() => {
+ visibility !== properties.visibility && setVisibility(properties.visibility);
+ if (isInitialRender.current) return;
+ setExposedVariable('isVisible', properties.visibility);
+ }, [properties.visibility]);
+
+ useEffect(() => {
+ loading !== loadingState && setLoading(loadingState);
+ if (isInitialRender.current) return;
+ setExposedVariable('isLoading', loadingState);
+ }, [loadingState]);
+
+ useEffect(() => {
+ if (isInitialRender.current) return;
+ setExposedVariable('isMandatory', isMandatory);
+ }, [isMandatory]);
+
+ useEffect(() => {
+ if (isInitialRender.current) return;
+ const validationStatus = validate(value);
+ setValidationStatus(validationStatus);
+ setExposedVariable('isValid', validationStatus?.isValid);
+ }, [validate]);
+
+ useEffect(() => {
+ if (inputType === 'phone') {
+ let code = getCountryCallingCodeSafe(country);
+ setInputValue(`+${code}${properties.value}`);
+ } else {
+ setInputValue(properties.value ?? '');
+ }
+ }, [properties.value]);
+
+ useEffect(() => {
+ if (inputType !== 'phone') return;
+ setExposedVariable('setValue', async function (value, countryCode = country) {
+ const code = getCountryCallingCodeSafe(country);
+ setInputValue(`+${code}${value}`);
+ setCountry(countryCode);
+ fireEvent('onChange');
+ });
+ }, [inputType, country]);
+
+ useEffect(() => {
+ if (inputType !== 'currency') return;
+ setExposedVariable('setValue', async function (value, countryCode = country) {
+ setInputValue(value);
+ setCountry(countryCode);
+ fireEvent('onChange');
+ });
+ }, [inputType, country]);
+
+ useEffect(() => {
+ const exposedVariables = {
+ ...(inputType !== 'phone' && {
+ setText: async function (text) {
+ setInputValue(text);
+ fireEvent('onChange');
+ },
+ }),
+ clear: async function () {
+ setInputValue('');
+ fireEvent('onChange');
+ },
+ setFocus: async function () {
+ inputRef.current.focus();
+ },
+ setBlur: async function () {
+ inputRef.current.blur();
+ },
+ setVisibility: async function (state) {
+ setVisibility(state);
+ setExposedVariable('isVisible', state);
+ },
+ setDisable: async function (disable) {
+ setDisable(disable);
+ setExposedVariable('isDisabled', disable);
+ },
+ setLoading: async function (loading) {
+ setLoading(loading);
+ setExposedVariable('isLoading', loading);
+ },
+ label,
+ isValid,
+ value: properties.value ?? '',
+ isMandatory,
+ isLoading: loading,
+ isVisible: visibility,
+ isDisabled: disable,
+ };
+
+ setExposedVariables(exposedVariables);
+ isInitialRender.current = false;
+ }, []);
+
+ const setInputValue = (value) => {
+ setValue(value);
+ setExposedVariable('value', value);
+ const validationStatus = validate(value);
+ setValidationStatus(validationStatus);
+ setExposedVariable('isValid', validationStatus?.isValid);
+ };
+
+ const handleChange = (e) => {
+ setInputValue(e.target.value);
+ fireEvent('onChange');
+ };
+
+ const handlePhoneCurrencyInputChange = (value) => {
+ setInputValue(value);
+ fireEvent('onChange');
+ };
+
+ const handleBlur = (e) => {
+ setShowValidationError(true);
+ setIsFocused(false);
+ e.stopPropagation();
+ fireEvent('onBlur');
+ };
+
+ const handleFocus = (e) => {
+ setIsFocused(true);
+ e.stopPropagation();
+ setTimeout(() => {
+ fireEvent('onFocus');
+ }, 0);
+ };
+
+ const handleKeyUp = (e) => {
+ if (e.key === 'Enter') {
+ setInputValue(e.target.value);
+ fireEvent('onEnterPressed');
+ }
+ };
+
+ return {
+ inputRef,
+ labelRef,
+ value,
+ visibility,
+ loading,
+ disable,
+ country,
+ setCountry,
+ validationStatus,
+ showValidationError,
+ isFocused,
+ labelWidth,
+ iconVisibility,
+ setIconVisibility,
+ isValid,
+ validationError,
+ isMandatory,
+ setInputValue,
+ handlePhoneCurrencyInputChange,
+ handleChange,
+ handleBlur,
+ handleFocus,
+ handleKeyUp,
+ };
+};
diff --git a/frontend/src/AppBuilder/Widgets/EmailInput.jsx b/frontend/src/AppBuilder/Widgets/EmailInput.jsx
new file mode 100644
index 0000000000..90061b0a66
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/EmailInput.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import { BaseInput } from './BaseComponents/BaseInput';
+import { useInput } from './BaseComponents/hooks/useInput';
+
+export const EmailInput = (props) => {
+ const inputLogic = useInput(props);
+ const additionalInputProps = {
+ autocomplete: 'email',
+ name: 'email',
+ };
+ return ;
+};
diff --git a/frontend/src/AppBuilder/Widgets/Form/FormUtils.js b/frontend/src/AppBuilder/Widgets/Form/FormUtils.js
index f8366e0b29..2dea50bdf9 100644
--- a/frontend/src/AppBuilder/Widgets/Form/FormUtils.js
+++ b/frontend/src/AppBuilder/Widgets/Form/FormUtils.js
@@ -97,6 +97,9 @@ export function generateUIComponents(JSONSchema, advanced, componentName = '') {
if (uiComponentsDraft?.length > 0 && uiComponentsDraft[index * 2 + 1]) {
switch (typeResolver(value?.type)) {
case 'TextInput':
+ case 'EmailInput':
+ case 'PhoneInput':
+ case 'CurrencyInput':
if (value?.styles?.backgroundColor)
uiComponentsDraft[index * 2 + 1]['definition']['styles']['backgroundColor'] =
value?.styles?.backgroundColor;
@@ -127,6 +130,15 @@ export function generateUIComponents(JSONSchema, advanced, componentName = '') {
if (value?.value) uiComponentsDraft[index * 2 + 1]['definition']['properties']['value'] = value?.value;
if (value?.placeholder)
uiComponentsDraft[index * 2 + 1]['definition']['properties']['placeholder'] = value?.placeholder;
+
+ if (value?.defaultCountry && ['PhoneInput', 'CurrencyInput'].includes(typeResolver(value?.type))) {
+ uiComponentsDraft[index * 2 + 1]['definition']['properties']['defaultCountry'] = value?.defaultCountry;
+ }
+
+ if (value?.defaultCountry && typeResolver(value?.type) === 'CurrencyInput') {
+ uiComponentsDraft[index * 2 + 1]['definition']['properties']['decimalPlaces'] = value?.decimalPlaces;
+ }
+
// prevent label from showing up in text input, because it is already shown in the text component. (Defaults to "Label" if not updated explicitly with an empty string)
uiComponentsDraft[index * 2 + 1]['definition']['properties']['label'] = '';
break;
@@ -482,6 +494,12 @@ const typeResolver = (type) => {
return 'DropDown';
case 'button':
return 'Button';
+ case 'emailinput':
+ return 'EmailInput';
+ case 'phoneinput':
+ return 'PhoneInput';
+ case 'currencyinput':
+ return 'CurrencyInput';
case 'text':
return 'Text';
case 'number':
diff --git a/frontend/src/AppBuilder/Widgets/Form/RenderSchema.jsx b/frontend/src/AppBuilder/Widgets/Form/RenderSchema.jsx
index b5bfa9e4c3..89d34b65c7 100644
--- a/frontend/src/AppBuilder/Widgets/Form/RenderSchema.jsx
+++ b/frontend/src/AppBuilder/Widgets/Form/RenderSchema.jsx
@@ -26,6 +26,7 @@ const RenderSchema = ({ component, parent, id, onOptionChange, onOptionsChange,
return validateWidget({
...{ widgetValue: value },
...{ validationObject: component.definition.validation },
+ componentType: component?.component,
});
},
[component.definition.validation]
diff --git a/frontend/src/AppBuilder/Widgets/NumberInput.jsx b/frontend/src/AppBuilder/Widgets/NumberInput.jsx
new file mode 100644
index 0000000000..f40288acd9
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/NumberInput.jsx
@@ -0,0 +1,169 @@
+import React, { useEffect } from 'react';
+import { BaseInput } from './BaseComponents/BaseInput';
+import { useInput } from './BaseComponents/hooks/useInput';
+import SolidIcon from '@/_ui/Icon/SolidIcons';
+
+export const NumberInput = (props) => {
+ const inputLogic = useInput({
+ ...props,
+ properties: {
+ ...props.properties,
+ value: Number(parseFloat(props.properties.value).toFixed(props.properties.decimalPlaces)),
+ },
+ });
+
+ const handleChange = (e) => {
+ if (e.target.value === '') {
+ inputLogic.setInputValue(null);
+ props.fireEvent('onChange');
+ } else {
+ const newValue = Number(parseFloat(e.target.value));
+ inputLogic.setInputValue(newValue);
+ if (!isNaN(newValue)) {
+ props.fireEvent('onChange');
+ }
+ }
+ };
+
+ const handleBlur = (e) => {
+ const value = Number(parseFloat(e.target.value).toFixed(props.properties.decimalPlaces));
+ inputLogic.setInputValue(value);
+ e.stopPropagation();
+ props.fireEvent('onBlur');
+ inputLogic.setIsFocused(false);
+ };
+
+ const handleIncrement = (e) => {
+ e.preventDefault();
+ const newValue = (inputLogic.value || 0) + 1;
+ inputLogic.setInputValue(newValue);
+ if (!isNaN(newValue)) {
+ props.fireEvent('onChange');
+ }
+ };
+
+ const handleDecrement = (e) => {
+ e.preventDefault();
+ const newValue = (inputLogic.value || 0) - 1;
+ inputLogic.setInputValue(newValue);
+ if (!isNaN(newValue)) {
+ props.fireEvent('onChange');
+ }
+ };
+
+ // Override the base input styles to account for number controls
+ const getCustomStyles = (baseStyles) => {
+ return {
+ ...baseStyles,
+ paddingRight: '20px', // Make room for number controls
+ };
+ };
+
+ const numberControls = !inputLogic.isResizing && (
+
+
+ 0 && props.styles.width > 0
+ ? '21px'
+ : '1px',
+ right: '1px',
+ borderLeft:
+ inputLogic.disable || inputLogic.loading
+ ? '1px solid var(--borders-weak-disabled)'
+ : '1px solid var(--borders-default)',
+ borderBottom:
+ inputLogic.disable || inputLogic.loading
+ ? '1px solid var(--borders-weak-disabled)'
+ : '.5px solid var(--borders-default)',
+ borderTopRightRadius: props.styles.borderRadius - 1,
+ backgroundColor: 'transparent',
+ }}
+ className="numberinput-up-arrow arrow number-input-arrow"
+ name="TriangleDownCenter"
+ />
+
+
+
+
+
+ );
+
+ useEffect(() => {
+ if (isNaN(inputLogic.value) || inputLogic.value === '') {
+ props.setExposedVariable('value', null);
+ }
+ }, [inputLogic.value]);
+
+ return (
+
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/PasswordInput.jsx b/frontend/src/AppBuilder/Widgets/PasswordInput.jsx
new file mode 100644
index 0000000000..6ca13f2048
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PasswordInput.jsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import { BaseInput } from './BaseComponents/BaseInput';
+import { useInput } from './BaseComponents/hooks/useInput';
+import SolidIcon from '@/_ui/Icon/SolidIcons';
+
+export const PasswordInput = (props) => {
+ const inputLogic = useInput(props);
+
+ const toggleVisibility = () => {
+ inputLogic.setIconVisibility(!inputLogic.iconVisibility);
+ };
+
+ const passwordIcon = (
+
+
+
+ );
+
+ return (
+
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/PhoneCurrency/CountrySelect.jsx b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CountrySelect.jsx
new file mode 100644
index 0000000000..5f4d1a6efc
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CountrySelect.jsx
@@ -0,0 +1,141 @@
+import React, { useEffect, useRef, useState } from 'react';
+import Select from 'react-select';
+import SolidIcon from '@/_ui/Icon/SolidIcons';
+import { CustomMenuList } from './CustomMenuList';
+import { CustomOption } from './CustomOption';
+import { CustomValueContainer } from './CustomValueContainer';
+
+export const CountrySelect = ({ value, onChange, options, ...rest }) => {
+ const {
+ isCountryChangeEnabled,
+ isCurrencyInput = false,
+ disabledState,
+ borderRadius,
+ isValid,
+ showValidationError,
+ computedStyles,
+ darkMode,
+ filterOption,
+ } = rest;
+ const [menuIsOpen, setMenuIsOpen] = useState(false);
+ const dropdownRef = useRef(null);
+
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
+ setMenuIsOpen(false);
+ }
+ };
+
+ if (menuIsOpen) {
+ document.addEventListener('mousedown', handleClickOutside);
+ }
+
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [menuIsOpen]);
+
+ const customStyles = {
+ container: (provided) => ({
+ ...provided,
+ minWidth: !isCountryChangeEnabled || disabledState ? '77px' : isCurrencyInput ? '87px' : '93px',
+ width: !isCountryChangeEnabled || disabledState ? '77px' : isCurrencyInput ? '87px' : '93px',
+ height: '100%',
+ }),
+ valueContainer: (provided) => ({
+ ...provided,
+ padding: '0px',
+ }),
+ control: (provided) => ({
+ ...provided,
+ minHeight: '0px',
+ height: '100%',
+ borderTopLeftRadius: `${borderRadius}px`,
+ borderBottomLeftRadius: `${borderRadius}px`,
+ borderTopRightRadius: '0px',
+ borderBottomRightRadius: '0px',
+ borderColor: `${
+ !isValid && showValidationError ? 'var(--status-error-strong)' : computedStyles?.borderColor
+ } !important`,
+ backgroundColor: `${computedStyles?.backgroundColor} !important`,
+ }),
+ menu: (provided) => ({
+ ...provided,
+ width: '208px',
+ height: '236px',
+ borderRadius: '8px',
+ marginTop: '2px',
+ boxShadow: 'var(--elevation-400-box-shadow)',
+ }),
+ menuList: (provided) => ({
+ ...provided,
+ maxHeight: '196px',
+ overflowY: 'auto',
+ scrollbarWidth: 'none',
+ gap: '1px',
+ padding: '8px',
+ borderRadius: '0px 0px 8px 8px',
+ display: 'flex',
+ flexDirection: 'column',
+ backgroundColor: 'var(--surfaces-surface-01)',
+ }),
+ option: (provided, state) => ({
+ ...provided,
+ backgroundColor: state.isSelected ? '#4368E31A' : 'var(--surfaces-surface-01)',
+ ...(state.isSelected && { borderRadius: '8px' }),
+ '&:hover': {
+ backgroundColor: 'var(--interactive-overlays-fill-hover)',
+ borderRadius: '8px',
+ },
+ display: 'flex',
+ cursor: 'pointer',
+ padding: '1px 14px',
+ }),
+ };
+ return (
+ {
+ if (disabledState || !isCountryChangeEnabled) return;
+ setMenuIsOpen((prev) => !prev);
+ }}
+ ref={dropdownRef}
+ >
+
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/PhoneCurrency/CurrencyInput.jsx b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CurrencyInput.jsx
new file mode 100644
index 0000000000..786299fc3a
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CurrencyInput.jsx
@@ -0,0 +1,266 @@
+import React, { useEffect, useMemo, useRef } from 'react';
+import { default as ReactCurrencyInput } from 'react-currency-input-field';
+import { useInput } from '../BaseComponents/hooks/useInput';
+import Loader from '@/ToolJetUI/Loader/Loader';
+import Label from '@/_ui/Label';
+import { CountrySelect } from './CountrySelect';
+import { CurrencyMap } from './constants';
+const tinycolor = require('tinycolor2');
+
+export const CurrencyInput = (props) => {
+ const { properties, styles, componentName, darkMode, setExposedVariables, fireEvent } = props;
+ const transformedProps = {
+ ...props,
+ inputType: 'currency',
+ };
+ const inputLogic = useInput(transformedProps);
+
+ const {
+ inputRef,
+ labelRef,
+ visibility,
+ loading,
+ disable,
+ showValidationError,
+ handlePhoneCurrencyInputChange,
+ isFocused,
+ labelWidth,
+ isValid,
+ validationError,
+ isMandatory,
+ handleBlur,
+ handleFocus,
+ value,
+ country,
+ setCountry,
+ } = inputLogic;
+ const { label, placeholder, decimalPlaces, isCountryChangeEnabled, defaultCountry = 'US' } = properties;
+
+ const handleKeyUp = (e) => {
+ if (e.key === 'Enter') {
+ fireEvent('onEnterPressed');
+ }
+ };
+
+ const options = useMemo(() => {
+ return Object.keys(CurrencyMap).map((ele) => ({
+ label: `${CurrencyMap[ele].prefix} (${CurrencyMap[ele].currency})`,
+ value: ele,
+ country: CurrencyMap[ele].country,
+ }));
+ }, []);
+
+ const onInputValueChange = (value) => {
+ handlePhoneCurrencyInputChange(value);
+ setExposedVariables({
+ country: country,
+ formattedValue: `${CurrencyMap[country]?.prefix} ${inputRef.current?.value}`,
+ });
+ };
+
+ const {
+ textColor,
+ backgroundColor,
+ alignment,
+ width,
+ direction,
+ auto,
+ color,
+ borderColor,
+ accentColor,
+ errTextColor,
+ boxShadow,
+ borderRadius,
+ } = styles;
+ const _width = (width / 100) * 70;
+ const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
+ const disabledState = disable || loading;
+ const isInitialRender = useRef(true);
+ const computedStyles = {
+ height: '100%',
+ borderRadius: `${borderRadius}px`,
+ color: !['#1B1F24', '#000', '#000000ff'].includes(textColor)
+ ? textColor
+ : disabledState
+ ? 'var(--text-disabled)'
+ : 'var(--text-primary)',
+ borderColor: isFocused
+ ? accentColor != '4368E3'
+ ? accentColor
+ : 'var(--primary-accent-strong)'
+ : borderColor != '#CCD1D5'
+ ? borderColor
+ : disabledState
+ ? '1px solid var(--borders-disabled-on-white)'
+ : 'var(--borders-default)',
+ '--tblr-input-border-color-darker': tinycolor(borderColor).darken(24).toString(),
+ backgroundColor:
+ backgroundColor != '#fff'
+ ? backgroundColor
+ : disabledState
+ ? darkMode
+ ? 'var(--surfaces-app-bg-default)'
+ : 'var(--surfaces-surface-03)'
+ : 'var(--surfaces-surface-01)',
+ padding: '8px 10px',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ borderBottomLeftRadius: '0px',
+ borderTopLeftRadius: '0px',
+ borderLeft: 'none',
+ };
+
+ const loaderStyle = {
+ right:
+ direction === 'right' &&
+ defaultAlignment === 'side' &&
+ ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
+ ? `${labelWidth + 11}px`
+ : '11px',
+ top:
+ defaultAlignment === 'top'
+ ? ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
+ 'calc(50% + 10px)'
+ : '',
+ transform:
+ defaultAlignment === 'top' &&
+ ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
+ ' translateY(-50%)',
+ zIndex: 3,
+ };
+
+ useEffect(() => {
+ if (!isInitialRender.current) {
+ setCountry(defaultCountry);
+ }
+ }, [defaultCountry]);
+
+ useEffect(() => {
+ if (!isInitialRender.current) {
+ setExposedVariables({
+ country: country,
+ formattedValue: `${CurrencyMap[country]?.prefix} ${value}`,
+ });
+ }
+ }, [country]);
+
+ useEffect(() => {
+ if (isInitialRender.current) {
+ setExposedVariables({
+ country: country,
+ formattedValue: `${CurrencyMap[country]?.prefix} ${value}`,
+ value: value,
+ setCountryCode: (code) => {
+ setCountry(code);
+ },
+ });
+ isInitialRender.current = false;
+ }
+ }, []);
+
+ return (
+ <>
+
+
+
+ {
+ return (
+ option.label.toLowerCase().includes(inputValue.toLowerCase()) ||
+ option.data.country.toLowerCase().includes(inputValue.toLowerCase())
+ );
+ }}
+ computedStyles={computedStyles}
+ showValidationError={showValidationError}
+ darkMode={darkMode}
+ isCurrencyInput={true}
+ onChange={(selectedOption) => {
+ if (selectedOption) {
+ setCountry(selectedOption.value);
+ fireEvent('onChange');
+ setExposedVariables({
+ country: selectedOption.value,
+ formattedValue: `${CurrencyMap[selectedOption.value]?.prefix} ${selectedOption.value}`,
+ });
+ }
+ }}
+ />
+ {
+ if (newVal === value) return;
+ onInputValueChange(newVal);
+ }}
+ // prefix={`${CurrencyMap?.[country]?.prefix || ''} `}
+ prefix={''}
+ disabled={disabledState}
+ onBlur={handleBlur}
+ onFocus={handleFocus}
+ onKeyUp={handleKeyUp}
+ />
+
+ {loading &&
}
+
+ {showValidationError && visibility && (
+
+ {validationError}
+
+ )}
+ >
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/PhoneCurrency/CustomMenuList.jsx b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CustomMenuList.jsx
new file mode 100644
index 0000000000..cff9f4fd9f
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CustomMenuList.jsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { components } from 'react-select';
+import SolidIcon from '@/_ui/Icon/SolidIcons';
+import cx from 'classnames';
+
+export const CustomMenuList = (props) => {
+ const { children, selectProps } = props;
+ const { onInputChange, inputValue } = selectProps;
+
+ return (
+ e.stopPropagation()}
+ >
+
+
+
+
+ {
+ onInputChange(e.currentTarget.value, {
+ action: 'input-change',
+ });
+ }}
+ onMouseDown={(e) => {
+ e.stopPropagation();
+ e.target.focus();
+ }}
+ onTouchEnd={(e) => {
+ e.stopPropagation();
+ e.target.focus();
+ }}
+ />
+
+
+
+ {children?.length > 0 ? children : No options
}
+
+
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/PhoneCurrency/CustomOption.jsx b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CustomOption.jsx
new file mode 100644
index 0000000000..a8dff94bed
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CustomOption.jsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { components } from 'react-select';
+// eslint-disable-next-line import/no-unresolved
+import flags from 'react-phone-number-input/flags';
+import TickV3 from '@/_ui/Icon/solidIcons/TickV3';
+
+export const CustomOption = (props) => {
+ const { label, value: optionValue, isSelected } = props;
+ const { darkMode } = props?.selectProps || {};
+
+ const optionStyle = {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'start',
+ minHeight: '32px',
+ gap: '6px',
+ cursor: 'pointer',
+ fontFamily: 'IBM Plex Sans',
+ fontSize: '12px',
+ lineHeight: '18px',
+ fontWeight: '400',
+ color: darkMode ? '#fff' : '#1B1F24',
+ width: '100%',
+ };
+ const FlagIcon = flags[optionValue];
+
+ return (
+
+
+
{FlagIcon ? : null}
+ {label}
+
+
+
+
+
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/PhoneCurrency/CustomValueContainer.jsx b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CustomValueContainer.jsx
new file mode 100644
index 0000000000..6cea56d47c
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PhoneCurrency/CustomValueContainer.jsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import { components } from 'react-select';
+// eslint-disable-next-line import/no-unresolved
+import flags from 'react-phone-number-input/flags';
+import Planet from '@/_ui/Icon/bulkIcons/Planet';
+import { getCountryCallingCodeSafe } from './utils';
+import { CurrencyMap } from './constants';
+
+export const CustomValueContainer = ({ getValue, ...props }) => {
+ const selectedValue = getValue()[0];
+ const country = selectedValue?.value;
+ const { isCurrencyInput, isCountryChangeEnabled } = props?.selectProps || {};
+ const FlagIcon = selectedValue ? flags[selectedValue.value] : null;
+ const countryCode = getCountryCallingCodeSafe(selectedValue.value);
+
+ return (
+
+ {FlagIcon ? (
+
+ <>
+ {' '}
+
+ {isCurrencyInput ? ` ${CurrencyMap?.[country]?.prefix}` : ` +${countryCode}`}
+
+ >
+
+ ) : (
+
+ )}
+
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/PhoneCurrency/PhoneInput.jsx b/frontend/src/AppBuilder/Widgets/PhoneCurrency/PhoneInput.jsx
new file mode 100644
index 0000000000..70fd463f94
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PhoneCurrency/PhoneInput.jsx
@@ -0,0 +1,254 @@
+import React, { useEffect, useMemo, useRef } from 'react';
+// eslint-disable-next-line import/no-unresolved
+import Input, { getCountries, getCountryCallingCode } from 'react-phone-number-input/input';
+import { getCountryCallingCodeSafe } from './utils';
+// eslint-disable-next-line import/no-unresolved
+import en from 'react-phone-number-input/locale/en';
+import 'react-phone-number-input/style.css';
+import { useInput } from '../BaseComponents/hooks/useInput';
+import Loader from '@/ToolJetUI/Loader/Loader';
+import Label from '@/_ui/Label';
+import { CountrySelect } from './CountrySelect';
+
+const tinycolor = require('tinycolor2');
+
+export const PhoneInput = (props) => {
+ const { properties, styles, componentName, darkMode, setExposedVariables, fireEvent } = props;
+ const transformedProps = {
+ ...props,
+ inputType: 'phone',
+ };
+ const inputLogic = useInput(transformedProps);
+ const {
+ inputRef,
+ labelRef,
+ visibility,
+ loading,
+ disable,
+ showValidationError,
+ isFocused,
+ labelWidth,
+ isValid,
+ validationError,
+ isMandatory,
+ handleBlur,
+ handleFocus,
+ value,
+ handlePhoneCurrencyInputChange,
+ country,
+ setCountry,
+ } = inputLogic;
+ const { label, placeholder, isCountryChangeEnabled, defaultCountry = 'US' } = properties;
+
+ const {
+ textColor,
+ backgroundColor,
+ alignment,
+ width,
+ direction,
+ auto,
+ color,
+ borderColor,
+ accentColor,
+ errTextColor,
+ boxShadow,
+ borderRadius,
+ } = styles;
+ const _width = (width / 100) * 70;
+ const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
+ const isInitialRender = useRef(true);
+
+ const options = useMemo(
+ () =>
+ getCountries()
+ .map((country) => ({
+ label: `${en[country]} +${getCountryCallingCodeSafe(country)}`,
+ value: country,
+ }))
+ .sort((a, b) => a.label.localeCompare(b.label)),
+ []
+ );
+
+ const onInputValueChange = (value) => {
+ setExposedVariables({
+ country: country,
+ countryCode: `+${getCountryCallingCodeSafe(country)}`,
+ formattedValue: `+${getCountryCallingCodeSafe(country)} ${inputRef.current?.value}`,
+ });
+ handlePhoneCurrencyInputChange(value);
+ };
+
+ const handleKeyUp = (e) => {
+ if (e.key === 'Enter') {
+ fireEvent('onEnterPressed');
+ }
+ };
+
+ useEffect(() => {
+ if (isInitialRender.current) {
+ setExposedVariables({
+ country: country,
+ countryCode: `+${getCountryCallingCodeSafe(country)}`,
+ formattedValue: `+${getCountryCallingCodeSafe(country)} ${inputRef.current?.value}`,
+ value: value,
+ setCountryCode: (code) => {
+ let value = getCountryCallingCodeSafe(code);
+ if (value) {
+ setCountry(code);
+ } else {
+ value = getCountries().find((country) => `+${getCountryCallingCode(country)}` === code);
+ setCountry(value ? value : '');
+ }
+ },
+ });
+ isInitialRender.current = false;
+ }
+ }, []);
+
+ useEffect(() => {
+ if (!isInitialRender.current) {
+ setCountry(defaultCountry);
+ }
+ }, [defaultCountry]);
+
+ const disabledState = disable || loading;
+
+ const loaderStyle = {
+ right:
+ direction === 'right' &&
+ defaultAlignment === 'side' &&
+ ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
+ ? `${labelWidth + 11}px`
+ : '11px',
+ top:
+ defaultAlignment === 'top'
+ ? ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
+ 'calc(50% + 10px)'
+ : '',
+ transform:
+ defaultAlignment === 'top' &&
+ ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
+ ' translateY(-50%)',
+ zIndex: 3,
+ };
+
+ const computedStyles = {
+ height: '100%',
+ borderRadius: `${borderRadius}px`,
+ color: !['#1B1F24', '#000', '#000000ff'].includes(textColor)
+ ? textColor
+ : disabledState
+ ? 'var(--text-disabled)'
+ : 'var(--text-primary)',
+ borderColor: isFocused
+ ? accentColor != '4368E3'
+ ? accentColor
+ : 'var(--primary-accent-strong)'
+ : borderColor != '#CCD1D5'
+ ? borderColor
+ : disabledState
+ ? '1px solid var(--borders-disabled-on-white)'
+ : 'var(--borders-default)',
+ '--tblr-input-border-color-darker': tinycolor(borderColor).darken(24).toString(),
+ backgroundColor:
+ backgroundColor != '#fff'
+ ? backgroundColor
+ : disabledState
+ ? darkMode
+ ? 'var(--surfaces-app-bg-default)'
+ : 'var(--surfaces-surface-03)'
+ : 'var(--surfaces-surface-01)',
+ padding: '8px 10px',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ borderBottomLeftRadius: '0px',
+ borderTopLeftRadius: '0px',
+ borderLeft: 'none',
+ };
+
+ return (
+ <>
+
+
+
+ {
+ if (selectedOption) {
+ setCountry(selectedOption.value);
+ }
+ }}
+ />
+
+
+ {loading &&
}
+
+ {showValidationError && visibility && (
+
+ {validationError}
+
+ )}
+ >
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/PhoneCurrency/constants.js b/frontend/src/AppBuilder/Widgets/PhoneCurrency/constants.js
new file mode 100644
index 0000000000..d27c7665a8
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PhoneCurrency/constants.js
@@ -0,0 +1,597 @@
+export const CurrencyMap = {
+ AE: {
+ currency: 'AED',
+ prefix: 'د.إ.',
+ country: 'United Arab Emirates',
+ },
+ AF: {
+ currency: 'AFN',
+ prefix: '؋',
+ country: 'Afghanistan',
+ },
+ AL: {
+ currency: 'ALL',
+ prefix: 'Lek',
+ country: 'Albania',
+ },
+ AM: {
+ currency: 'AMD',
+ prefix: 'դր.',
+ country: 'Armenia',
+ },
+ AR: {
+ currency: 'ARS',
+ prefix: '$',
+ country: 'Argentina',
+ },
+ AU: {
+ currency: 'AUD',
+ prefix: '$',
+ country: 'Australia',
+ },
+ AZ: {
+ currency: 'AZN',
+ prefix: 'ман.',
+ country: 'Azerbaijan',
+ },
+ BA: {
+ currency: 'BAM',
+ prefix: 'KM',
+ country: 'Bosnia and Herzegovina',
+ },
+ BD: {
+ currency: 'BDT',
+ prefix: '৳',
+ country: 'Bangladesh',
+ },
+ BG: {
+ currency: 'BGN',
+ prefix: 'лв.',
+ country: 'Bulgaria',
+ },
+ BH: {
+ currency: 'BHD',
+ prefix: 'د.ب.',
+ country: 'Bahrain',
+ },
+ BI: {
+ currency: 'BIF',
+ prefix: 'FBu',
+ country: 'Burundi',
+ },
+ BN: {
+ currency: 'BND',
+ prefix: '$',
+ country: 'Brunei Darussalam',
+ },
+ BO: {
+ currency: 'BOB',
+ prefix: 'Bs',
+ country: 'Bolivia',
+ },
+ BR: {
+ currency: 'BRL',
+ prefix: 'R$',
+ country: 'Brazil',
+ },
+ BV: {
+ currency: 'NOK',
+ prefix: 'kr',
+ country: 'Bouvet Island',
+ },
+ BW: {
+ currency: 'BWP',
+ prefix: 'P',
+ country: 'Botswana',
+ },
+ BY: {
+ currency: 'BYR',
+ prefix: 'BYR',
+ country: 'Belarus',
+ },
+ BZ: {
+ currency: 'BZD',
+ prefix: '$',
+ country: 'Belize',
+ },
+ CA: {
+ currency: 'CAD',
+ prefix: '$',
+ country: 'Canada',
+ },
+ CD: {
+ currency: 'CDF',
+ prefix: 'FrCD',
+ country: 'Congo, Democratic Republic of the',
+ },
+ CF: {
+ currency: 'XAF',
+ prefix: 'FCFA',
+ country: 'Central African Republic',
+ },
+ CH: {
+ currency: 'CHF',
+ prefix: 'CHF',
+ country: 'Switzerland',
+ },
+ CL: {
+ currency: 'CLP',
+ prefix: '$',
+ country: 'Chile',
+ },
+ CN: {
+ currency: 'CNY',
+ prefix: 'CN¥',
+ country: 'China',
+ },
+ CO: {
+ currency: 'COP',
+ prefix: '$',
+ country: 'Colombia',
+ },
+ CR: {
+ currency: 'CRC',
+ prefix: '₡',
+ country: 'Costa Rica',
+ },
+ CV: {
+ currency: 'CVE',
+ prefix: 'CV$',
+ country: 'Cape Verde',
+ },
+ CZ: {
+ currency: 'CZK',
+ prefix: 'Kč',
+ country: 'Czech Republic',
+ },
+ DJ: {
+ currency: 'DJF',
+ prefix: 'Fdj',
+ country: 'Djibouti',
+ },
+ DK: {
+ currency: 'DKK',
+ prefix: 'kr',
+ country: 'Denmark',
+ },
+ DO: {
+ currency: 'DOP',
+ prefix: 'RD$',
+ country: 'Dominican Republic',
+ },
+ DZ: {
+ currency: 'DZD',
+ prefix: 'د.ج.',
+ country: 'Algeria',
+ },
+ EG: {
+ currency: 'EGP',
+ prefix: 'ج.م.',
+ country: 'Egypt',
+ },
+ ER: {
+ currency: 'ERN',
+ prefix: 'Nfk',
+ country: 'Eritrea',
+ },
+ EU: {
+ currency: 'EUR',
+ prefix: '€',
+ country: 'European Union',
+ },
+ ET: {
+ currency: 'ETB',
+ prefix: 'Br',
+ country: 'Ethiopia',
+ },
+ GB: {
+ currency: 'GBP',
+ prefix: '£',
+ country: 'United Kingdom',
+ },
+ GE: {
+ currency: 'GEL',
+ prefix: 'GEL',
+ country: 'Georgia',
+ },
+ GH: {
+ currency: 'GHS',
+ prefix: 'GH₵',
+ country: 'Ghana',
+ },
+ GN: {
+ currency: 'GNF',
+ prefix: 'FG',
+ country: 'Guinea',
+ },
+ GT: {
+ currency: 'GTQ',
+ prefix: 'Q',
+ country: 'Guatemala',
+ },
+ HK: {
+ currency: 'HKD',
+ prefix: '$',
+ country: 'Hong Kong',
+ },
+ HN: {
+ currency: 'HNL',
+ prefix: 'L',
+ country: 'Honduras',
+ },
+ HR: {
+ currency: 'HRK',
+ prefix: 'kn',
+ country: 'Croatia',
+ },
+ HU: {
+ currency: 'HUF',
+ prefix: 'Ft',
+ country: 'Hungary',
+ },
+ ID: {
+ currency: 'IDR',
+ prefix: 'Rp',
+ country: 'Indonesia',
+ },
+ IL: {
+ currency: 'ILS',
+ prefix: '₪',
+ country: 'Israel',
+ },
+ IN: {
+ currency: 'INR',
+ prefix: '₹',
+ country: 'India',
+ },
+ IQ: {
+ currency: 'IQD',
+ prefix: 'د.ع.',
+ country: 'Iraq',
+ },
+ IR: {
+ currency: 'IRR',
+ prefix: '﷼',
+ country: 'Iran, Islamic Republic of',
+ },
+ IS: {
+ currency: 'ISK',
+ prefix: 'kr',
+ country: 'Iceland',
+ },
+ JM: {
+ currency: 'JMD',
+ prefix: '$',
+ country: 'Jamaica',
+ },
+ JO: {
+ currency: 'JOD',
+ prefix: 'د.أ.',
+ country: 'Jordan',
+ },
+ JP: {
+ currency: 'JPY',
+ prefix: '¥',
+ country: 'Japan',
+ },
+ KE: {
+ currency: 'KES',
+ prefix: 'Ksh',
+ country: 'Kenya',
+ },
+ KH: {
+ currency: 'KHR',
+ prefix: '៛',
+ country: 'Cambodia',
+ },
+ KM: {
+ currency: 'KMF',
+ prefix: 'FC',
+ country: 'Comoros',
+ },
+ KR: {
+ currency: 'KRW',
+ prefix: '₩',
+ country: 'Korea, Republic of',
+ },
+ KW: {
+ currency: 'KWD',
+ prefix: 'د.ك.',
+ country: 'Kuwait',
+ },
+ KZ: {
+ currency: 'KZT',
+ prefix: 'тңг.',
+ country: 'Kazakhstan',
+ },
+ LB: {
+ currency: 'LBP',
+ prefix: 'ل.ل.',
+ country: 'Lebanon',
+ },
+ LK: {
+ currency: 'LKR',
+ prefix: 'SL Re',
+ country: 'Sri Lanka',
+ },
+ LT: {
+ currency: 'LTL',
+ prefix: 'Lt',
+ country: 'Lithuania',
+ },
+ LY: {
+ currency: 'LYD',
+ prefix: 'د.ل.',
+ country: 'Libya',
+ },
+ MA: {
+ currency: 'MAD',
+ prefix: 'د.م.',
+ country: 'Morocco',
+ },
+ MD: {
+ currency: 'MDL',
+ prefix: 'MDL',
+ country: 'Moldova, Republic of',
+ },
+ MG: {
+ currency: 'MGA',
+ prefix: 'MGA',
+ country: 'Madagascar',
+ },
+ MK: {
+ currency: 'MKD',
+ prefix: 'MKD',
+ country: 'Macedonia, the Former Yugoslav Republic of',
+ },
+ MM: {
+ currency: 'MMK',
+ prefix: 'K',
+ country: 'Myanmar',
+ },
+ MO: {
+ currency: 'MOP',
+ prefix: 'MOP$',
+ country: 'Macao',
+ },
+ MU: {
+ currency: 'MUR',
+ prefix: 'MURs',
+ country: 'Mauritius',
+ },
+ MX: {
+ currency: 'MXN',
+ prefix: '$',
+ country: 'Mexico',
+ },
+ MY: {
+ currency: 'MYR',
+ prefix: 'RM',
+ country: 'Malaysia',
+ },
+ MZ: {
+ currency: 'MZN',
+ prefix: 'MTn',
+ country: 'Mozambique',
+ },
+ NA: {
+ currency: 'NAD',
+ prefix: 'N$',
+ country: 'Namibia',
+ },
+ NG: {
+ currency: 'NGN',
+ prefix: '₦',
+ country: 'Nigeria',
+ },
+ NI: {
+ currency: 'NIO',
+ prefix: 'C$',
+ country: 'Nicaragua',
+ },
+ NO: {
+ currency: 'NOK',
+ prefix: 'kr',
+ country: 'Norway',
+ },
+ NP: {
+ currency: 'NPR',
+ prefix: 'नेरू',
+ country: 'Nepal',
+ },
+ NZ: {
+ currency: 'NZD',
+ prefix: '$',
+ country: 'New Zealand',
+ },
+ OM: {
+ currency: 'OMR',
+ prefix: 'ر.ع.',
+ country: 'Oman',
+ },
+ PA: {
+ currency: 'PAB',
+ prefix: 'B/.',
+ country: 'Panama',
+ },
+ PE: {
+ currency: 'PEN',
+ prefix: 'S/.',
+ country: 'Peru',
+ },
+ PH: {
+ currency: 'PHP',
+ prefix: '₱',
+ country: 'Philippines',
+ },
+ PK: {
+ currency: 'PKR',
+ prefix: '₨',
+ country: 'Pakistan',
+ },
+ PL: {
+ currency: 'PLN',
+ prefix: 'zł',
+ country: 'Poland',
+ },
+ PY: {
+ currency: 'PYG',
+ prefix: '₲',
+ country: 'Paraguay',
+ },
+ QA: {
+ currency: 'QAR',
+ prefix: 'ر.ق.',
+ country: 'Qatar',
+ },
+ RO: {
+ currency: 'RON',
+ prefix: 'RON',
+ country: 'Romania',
+ },
+ RS: {
+ currency: 'RSD',
+ prefix: 'дин.',
+ country: 'Serbia',
+ },
+ RU: {
+ currency: 'RUB',
+ prefix: '₽',
+ country: 'Russian Federation',
+ },
+ RW: {
+ currency: 'RWF',
+ prefix: 'FR',
+ country: 'Rwanda',
+ },
+ SA: {
+ currency: 'SAR',
+ prefix: 'ر.س.',
+ country: 'Saudi Arabia',
+ },
+ SD: {
+ currency: 'SDG',
+ prefix: 'SDG',
+ country: 'Sudan',
+ },
+ SE: {
+ currency: 'SEK',
+ prefix: 'kr',
+ country: 'Sweden',
+ },
+ SG: {
+ currency: 'SGD',
+ prefix: '$',
+ country: 'Singapore',
+ },
+ SJ: {
+ currency: 'NOK',
+ prefix: 'kr',
+ country: 'Svalbard and Jan Mayen',
+ },
+ SN: {
+ currency: 'XOF',
+ prefix: 'CFA',
+ country: 'Senegal',
+ },
+ SO: {
+ currency: 'SOS',
+ prefix: 'Ssh',
+ country: 'Somalia',
+ },
+ SY: {
+ currency: 'SYP',
+ prefix: 'ل.س.',
+ country: 'Syrian Arab Republic',
+ },
+ TH: {
+ currency: 'THB',
+ prefix: '฿',
+ country: 'Thailand',
+ },
+ TN: {
+ currency: 'TND',
+ prefix: 'د.ت.',
+ country: 'Tunisia',
+ },
+ TO: {
+ currency: 'TOP',
+ prefix: 'T$',
+ country: 'Tonga',
+ },
+ TR: {
+ currency: 'TRY',
+ prefix: 'TL',
+ country: 'Turkey',
+ },
+ TT: {
+ currency: 'TTD',
+ prefix: '$',
+ country: 'Trinidad and Tobago',
+ },
+ TW: {
+ currency: 'TWD',
+ prefix: 'NT$',
+ country: 'Taiwan, Province of China',
+ },
+ TZ: {
+ currency: 'TZS',
+ prefix: 'TSh',
+ country: 'United Republic of Tanzania',
+ },
+ UA: {
+ currency: 'UAH',
+ prefix: '₴',
+ country: 'Ukraine',
+ },
+ UG: {
+ currency: 'UGX',
+ prefix: 'USh',
+ country: 'Uganda',
+ },
+ US: {
+ currency: 'USD',
+ prefix: '$',
+ country: 'United States',
+ },
+ UY: {
+ currency: 'UYU',
+ prefix: '$',
+ country: 'Uruguay',
+ },
+ UZ: {
+ currency: 'UZS',
+ prefix: 'UZS',
+ country: 'Uzbekistan',
+ },
+ VE: {
+ currency: 'VEF',
+ prefix: 'Bs.F.',
+ country: 'Venezuela',
+ },
+ VN: {
+ currency: 'VND',
+ prefix: '₫',
+ country: 'Vietnam',
+ },
+ VU: {
+ currency: 'VUV',
+ prefix: 'VT',
+ country: 'Vanuatu',
+ },
+ YE: {
+ currency: 'YER',
+ prefix: 'ر.ي.',
+ country: 'Yemen',
+ },
+ ZA: {
+ currency: 'ZAR',
+ prefix: 'R',
+ country: 'South Africa',
+ },
+ ZM: {
+ currency: 'ZMK',
+ prefix: 'ZK',
+ country: 'Zambia',
+ },
+};
diff --git a/frontend/src/AppBuilder/Widgets/PhoneCurrency/utils.js b/frontend/src/AppBuilder/Widgets/PhoneCurrency/utils.js
new file mode 100644
index 0000000000..9a23b45ca5
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PhoneCurrency/utils.js
@@ -0,0 +1,10 @@
+// eslint-disable-next-line import/no-unresolved
+import { getCountryCallingCode } from 'react-phone-number-input/input';
+
+export const getCountryCallingCodeSafe = (country) => {
+ try {
+ return getCountryCallingCode(country);
+ } catch (error) {
+ return '';
+ }
+};
diff --git a/frontend/src/AppBuilder/Widgets/PhoneInput/PhoneInput.jsx b/frontend/src/AppBuilder/Widgets/PhoneInput/PhoneInput.jsx
new file mode 100644
index 0000000000..6a7dafceea
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/PhoneInput/PhoneInput.jsx
@@ -0,0 +1,256 @@
+import React, { useEffect, useMemo, useRef } from 'react';
+// eslint-disable-next-line import/no-unresolved
+import Input, { getCountries, getCountryCallingCode } from 'react-phone-number-input/input';
+import { getCountryCallingCodeSafe } from './utils';
+// eslint-disable-next-line import/no-unresolved
+import en from 'react-phone-number-input/locale/en';
+import 'react-phone-number-input/style.css';
+import { useInput } from '../BaseComponents/hooks/useInput';
+import Loader from '@/ToolJetUI/Loader/Loader';
+import Label from '@/_ui/Label';
+import { CountrySelect } from './CountrySelect';
+
+const tinycolor = require('tinycolor2');
+
+export const PhoneInput = (props) => {
+ const { properties, styles, componentName, darkMode, setExposedVariables, fireEvent } = props;
+ const transformedProps = {
+ ...props,
+ inputType: 'phone',
+ };
+ const inputLogic = useInput(transformedProps);
+ const {
+ inputRef,
+ labelRef,
+ visibility,
+ loading,
+ disable,
+ showValidationError,
+ isFocused,
+ labelWidth,
+ isValid,
+ validationError,
+ isMandatory,
+ handleBlur,
+ handleFocus,
+ value,
+ handlePhoneInputChange,
+ country,
+ setCountry,
+ } = inputLogic;
+ const { label, placeholder, isCountryChangeEnabled, defaultCountry = 'US' } = properties;
+
+ const {
+ textColor,
+ backgroundColor,
+ alignment,
+ width,
+ direction,
+ auto,
+ color,
+ borderColor,
+ accentColor,
+ errTextColor,
+ boxShadow,
+ borderRadius,
+ } = styles;
+ const _width = (width / 100) * 70;
+ const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
+ const isInitialRender = useRef(true);
+
+ const options = useMemo(
+ () =>
+ getCountries()
+ .map((country) => ({
+ label: `${en[country]} +${getCountryCallingCodeSafe(country)}`,
+ value: country,
+ }))
+ .sort((a, b) => a.label.localeCompare(b.label)),
+ []
+ );
+
+ const onInputValueChange = (value) => {
+ setExposedVariables({
+ country: country,
+ countryCode: `+${getCountryCallingCodeSafe(country)}`,
+ formattedValue: `+${getCountryCallingCodeSafe(country)} ${inputRef.current?.value}`,
+ });
+ handlePhoneInputChange(value);
+ };
+
+ const handleKeyUp = (e) => {
+ if (e.key === 'Enter') {
+ fireEvent('onEnterPressed');
+ }
+ };
+
+ useEffect(() => {
+ if (isInitialRender.current) {
+ setExposedVariables({
+ country: country,
+ countryCode: `+${getCountryCallingCodeSafe(country)}`,
+ formattedValue: `+${getCountryCallingCodeSafe(country)} ${inputRef.current?.value}`,
+ value: value,
+ setCountryCode: (code) => {
+ let value = getCountryCallingCodeSafe(code);
+ if (value) {
+ setCountry(code);
+ } else {
+ value = getCountries().find((country) => `+${getCountryCallingCode(country)}` === code);
+ setCountry(value ? value : '');
+ }
+ },
+ });
+ isInitialRender.current = false;
+ }
+ }, []);
+
+ useEffect(() => {
+ if (!isInitialRender.current) {
+ setCountry(defaultCountry);
+ }
+ }, [defaultCountry]);
+
+ const disabledState = disable || loading;
+
+ const loaderStyle = {
+ right:
+ direction === 'right' &&
+ defaultAlignment === 'side' &&
+ ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
+ ? `${labelWidth + 11}px`
+ : '11px',
+ top:
+ defaultAlignment === 'top'
+ ? ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
+ 'calc(50% + 10px)'
+ : '',
+ transform:
+ defaultAlignment === 'top' &&
+ ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
+ ' translateY(-50%)',
+ zIndex: 3,
+ };
+
+ const computedStyles = {
+ height: '100%',
+ borderRadius: `0px ${borderRadius}px ${borderRadius}px 0px`,
+ color: !['#1B1F24', '#000', '#000000ff'].includes(textColor)
+ ? textColor
+ : disabledState
+ ? 'var(--text-disabled)'
+ : 'var(--text-primary)',
+ borderColor: isFocused
+ ? accentColor != '4368E3'
+ ? accentColor
+ : 'var(--primary-accent-strong)'
+ : borderColor != '#CCD1D5'
+ ? borderColor
+ : disabledState
+ ? '1px solid var(--borders-disabled-on-white)'
+ : 'var(--borders-default)',
+ '--tblr-input-border-color-darker': tinycolor(borderColor).darken(24).toString(),
+ backgroundColor:
+ backgroundColor != '#fff'
+ ? backgroundColor
+ : disabledState
+ ? darkMode
+ ? 'var(--surfaces-app-bg-default)'
+ : 'var(--surfaces-surface-03)'
+ : 'var(--surfaces-surface-01)',
+ padding: '8px 10px',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ borderBottomLeftRadius: '0px',
+ borderTopLeftRadius: '0px',
+ borderLeft: 'none',
+ };
+ const countryCode = getCountryCallingCodeSafe(country);
+ console.log(countryCode);
+ return (
+ <>
+
+
+
+ {
+ if (selectedOption) {
+ setCountry(selectedOption.value);
+ }
+ }}
+ />
+
+
+ {loading &&
}
+
+ {showValidationError && visibility && (
+
+ {validationError}
+
+ )}
+ >
+ );
+};
diff --git a/frontend/src/AppBuilder/Widgets/TextArea.jsx b/frontend/src/AppBuilder/Widgets/TextArea.jsx
new file mode 100644
index 0000000000..79a3125965
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/TextArea.jsx
@@ -0,0 +1,9 @@
+import React from 'react';
+import { BaseInput } from './BaseComponents/BaseInput';
+import { useInput } from './BaseComponents/hooks/useInput';
+
+export const TextArea = (props) => {
+ const inputLogic = useInput(props);
+
+ return ;
+};
diff --git a/frontend/src/AppBuilder/Widgets/TextInput.jsx b/frontend/src/AppBuilder/Widgets/TextInput.jsx
new file mode 100644
index 0000000000..119add300e
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/TextInput.jsx
@@ -0,0 +1,9 @@
+import React from 'react';
+import { BaseInput } from './BaseComponents/BaseInput';
+import { useInput } from './BaseComponents/hooks/useInput';
+
+export const TextInput = (props) => {
+ const inputLogic = useInput(props);
+
+ return ;
+};
diff --git a/frontend/src/AppBuilder/_helpers/editorHelpers.js b/frontend/src/AppBuilder/_helpers/editorHelpers.js
index a8fc3efca1..4f17c4c302 100644
--- a/frontend/src/AppBuilder/_helpers/editorHelpers.js
+++ b/frontend/src/AppBuilder/_helpers/editorHelpers.js
@@ -4,9 +4,9 @@ import { Text } from '@/Editor/Components/Text';
// import { Table } from '@/Editor/Components/Table/Table';
import { Table } from '@/AppBuilder/Widgets/Table/Table';
-import { TextInput } from '@/Editor/Components/TextInput';
-import { NumberInput } from '@/Editor/Components/NumberInput';
-import { TextArea } from '@/Editor/Components/TextArea';
+import { TextInput } from '@/AppBuilder/Widgets/TextInput';
+import { TextArea } from '@/AppBuilder/Widgets/TextArea';
+import { NumberInput } from '@/AppBuilder/Widgets/NumberInput';
import { RichTextEditor } from '@/Editor/Components/RichTextEditor';
import { DropDown } from '@/Editor/Components/DropDown';
import { DropdownV2 } from '@/Editor/Components/DropdownV2/DropdownV2';
@@ -29,7 +29,10 @@ import { RadioButtonV2 } from '@/Editor/Components/RadioButtonV2/RadioButtonV2';
import { StarRating } from '@/Editor/Components/StarRating';
import { Divider } from '@/Editor/Components/Divider';
import { FilePicker } from '@/Editor/Components/FilePicker';
-import { PasswordInput } from '@/Editor/Components/PasswordInput';
+import { PasswordInput } from '@/AppBuilder/Widgets/PasswordInput';
+import { EmailInput } from '@/AppBuilder/Widgets/EmailInput';
+import { PhoneInput } from '@/AppBuilder/Widgets/PhoneCurrency/PhoneInput';
+import { CurrencyInput } from '@/AppBuilder/Widgets/PhoneCurrency/CurrencyInput';
// import { Calendar } from '@/Editor/Components/Calendar';
// import { Listview } from '@/Editor/Components/Listview';
import { IFrame } from '@/Editor/Components/IFrame';
@@ -118,6 +121,9 @@ export const AllComponents = {
Divider,
FilePicker,
PasswordInput,
+ EmailInput,
+ PhoneInput,
+ CurrencyInput,
Calendar,
IFrame,
CodeEditor,
diff --git a/frontend/src/AppBuilder/_stores/slices/componentsSlice.js b/frontend/src/AppBuilder/_stores/slices/componentsSlice.js
index 8f325dd614..b746f1237b 100644
--- a/frontend/src/AppBuilder/_stores/slices/componentsSlice.js
+++ b/frontend/src/AppBuilder/_stores/slices/componentsSlice.js
@@ -439,7 +439,7 @@ export const createComponentsSlice = (set, get) => ({
}
},
- validateWidget: ({ validationObject, widgetValue, customResolveObjects }) => {
+ validateWidget: ({ validationObject, widgetValue, customResolveObjects, componentType }) => {
const { getResolvedValue } = get();
let isValid = true;
let validationError = null;
@@ -455,6 +455,17 @@ export const createComponentsSlice = (set, get) => ({
validationRegex = typeof validationRegex === 'string' ? validationRegex : '';
const re = new RegExp(validationRegex, 'g');
+ if (componentType === 'EmailInput' && widgetValue) {
+ const validationRegex = '^(?!.*\\.\\.)([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})$';
+ const emailRegex = new RegExp(validationRegex, 'g');
+ if (!emailRegex.test(widgetValue)) {
+ return {
+ isValid: false,
+ validationError: 'Input should be a valid email',
+ };
+ }
+ }
+
if (!re.test(widgetValue)) {
return {
isValid: false,
@@ -1833,6 +1844,9 @@ export const createComponentsSlice = (set, get) => ({
![
'TextInput',
'PasswordInput',
+ 'EmailInput',
+ 'PhoneInput',
+ 'CurrencyInput',
'NumberInput',
'DropdownV2',
'MultiselectV2',
@@ -1841,6 +1855,7 @@ export const createComponentsSlice = (set, get) => ({
'DaterangePicker',
'DatePickerV2',
'TimePicker',
+ 'TextArea',
].includes(componentType)
) {
return layoutData?.height;
diff --git a/frontend/src/Editor/Components/TextArea.jsx b/frontend/src/Editor/Components/TextArea.jsx
deleted file mode 100644
index cd7afc7e0d..0000000000
--- a/frontend/src/Editor/Components/TextArea.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import React, { useState, useEffect, useRef } from 'react';
-
-export const TextArea = function TextArea({
- height,
- properties,
- styles,
- setExposedVariable,
- setExposedVariables,
- dataCy,
-}) {
- const isInitialRender = useRef(true);
- const [value, setValue] = useState(properties.value);
-
- useEffect(() => {
- if (isInitialRender.current) return;
- setValue(properties.value);
- setExposedVariable('value', properties.value);
-
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [properties.value]);
-
- useEffect(() => {
- const exposedVariables = {
- setText: async function (text) {
- setValue(text);
- setExposedVariable('value', text);
- },
- clear: async function () {
- setValue('');
- setExposedVariable('value', '');
- },
- value: properties.value,
- };
- setExposedVariables(exposedVariables);
- setValue(properties.value);
- isInitialRender.current = false;
-
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- return (
-
- );
-};
diff --git a/frontend/src/Editor/WidgetManager/components.js b/frontend/src/Editor/WidgetManager/components.js
index d7aa4442f0..6b0c4beb53 100644
--- a/frontend/src/Editor/WidgetManager/components.js
+++ b/frontend/src/Editor/WidgetManager/components.js
@@ -1,4 +1,4 @@
-import { widgets } from './widgetConfig';
+import { widgets } from '../../AppBuilder/WidgetManager/configs/widgetConfig';
const universalProps = {
properties: {},
diff --git a/frontend/src/Editor/WidgetManager/configs/textarea.js b/frontend/src/Editor/WidgetManager/configs/textarea.js
index c5e71be77d..d0e0364334 100644
--- a/frontend/src/Editor/WidgetManager/configs/textarea.js
+++ b/frontend/src/Editor/WidgetManager/configs/textarea.js
@@ -4,7 +4,7 @@ export const textareaConfig = {
description: 'Multi-line text input',
component: 'TextArea',
defaultSize: {
- width: 6,
+ width: 10,
height: 100,
},
others: {
@@ -12,82 +12,291 @@ export const textareaConfig = {
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
- value: {
+ label: {
type: 'code',
- displayName: 'Default value',
- validation: {
- schema: { type: 'string' },
- defaultValue: 'default text',
- },
+ displayName: 'Label',
+ validation: { schema: { type: 'string' }, defaultValue: 'Label' },
},
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
- defaultValue: 'Placeholder text',
+ defaultValue: 'Enter your input',
},
},
- },
- events: {},
- styles: {
+ value: {
+ type: 'code',
+ displayName: 'Default value',
+ validation: {
+ schema: {
+ type: 'string',
+ },
+ defaultValue: 'Default value',
+ },
+ },
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
visibility: {
type: 'toggle',
displayName: 'Visibility',
- validation: {
- schema: { type: 'boolean' },
- defaultValue: true,
- },
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
- validation: {
- schema: { type: 'boolean' },
- defaultValue: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ tooltip: {
+ type: 'code',
+ displayName: 'Tooltip',
+ validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
+ section: 'additionalActions',
+ placeholder: 'Enter tooltip text',
+ },
+ },
+ validation: {
+ mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
+ regex: { type: 'code', displayName: 'Regex', placeholder: '^[a-zA-Z0-9_ -]{3,16}$' },
+ minLength: { type: 'code', displayName: 'Min length', placeholder: 'Enter min length' },
+ maxLength: { type: 'code', displayName: 'Max length', placeholder: 'Enter max length' },
+ customRule: {
+ type: 'code',
+ displayName: 'Custom validation',
+ placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
+ },
+ },
+ events: {
+ onChange: { displayName: 'On change' },
+ onEnterPressed: { displayName: 'On enter pressed' },
+ onFocus: { displayName: 'On focus' },
+ onBlur: { displayName: 'On blur' },
+ },
+ styles: {
+ color: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'label',
+ },
+ alignment: {
+ type: 'switch',
+ displayName: 'Alignment',
+ validation: { schema: { type: 'string' }, defaultValue: 'side' },
+ options: [
+ { displayName: 'Side', value: 'side' },
+ { displayName: 'Top', value: 'top' },
+ ],
+ accordian: 'label',
+ },
+ direction: {
+ type: 'switch',
+ displayName: '',
+ validation: { schema: { type: 'string' }, defaultValue: 'left' },
+ showLabel: false,
+ isIcon: true,
+ options: [
+ { displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
+ { displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
+ ],
+ accordian: 'label',
+ isFxNotRequired: true,
+ },
+ width: {
+ type: 'slider',
+ displayName: 'Width',
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
},
+ isFxNotRequired: true,
+ },
+ auto: {
+ type: 'checkbox',
+ displayName: 'auto',
+ showLabel: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+
+ backgroundColor: {
+ type: 'color',
+ displayName: 'Background',
+ validation: { schema: { type: 'string' }, defaultValue: '#fff' },
+ accordian: 'field',
+ },
+ borderColor: {
+ type: 'color',
+ displayName: 'Border',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ },
+ accentColor: {
+ type: 'color',
+ displayName: 'Accent',
+ validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
+ accordian: 'field',
+ },
+ textColor: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'field',
+ },
+ errTextColor: {
+ type: 'color',
+ displayName: 'Error text',
+ validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
+ accordian: 'field',
+ },
+ icon: {
+ type: 'icon',
+ displayName: 'Icon',
+ validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
+ accordian: 'field',
+ visibility: false,
+ },
+ iconColor: {
+ type: 'color',
+ displayName: 'Icon color',
+ validation: { schema: { type: 'string' }, defaultValue: '#CFD3D859' },
+ accordian: 'field',
+ visibility: false,
+ showLabel: false,
},
borderRadius: {
- type: 'code',
+ type: 'numberInput',
displayName: 'Border radius',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
+ accordian: 'field',
+ },
+ boxShadow: {
+ type: 'boxShadow',
+ displayName: 'Box Shadow',
validation: {
- schema: { type: 'number' },
- defaultValue: 4,
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: '0px 0px 0px 0px #00000040',
},
+ accordian: 'field',
+ },
+ padding: {
+ type: 'switch',
+ displayName: 'Padding',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 'default',
+ },
+ isFxNotRequired: true,
+ options: [
+ { displayName: 'Default', value: 'default' },
+ { displayName: 'None', value: 'none' },
+ ],
+ accordian: 'container',
},
},
exposedVariables: {
- value:
- 'ToolJet is an open-source low-code platform for building and deploying internal tools with minimal engineering efforts 🚀',
+ value: '',
+ isMandatory: false,
+ isVisible: true,
+ isDisabled: false,
+ isLoading: false,
},
actions: [
{
handle: 'setText',
- displayName: 'Set Text',
- params: [{ handle: 'text', displayName: 'text', defaultValue: 'New Text' }],
+ displayName: 'Set text',
+ params: [{ handle: 'text', displayName: 'text', defaultValue: 'New text' }],
},
{
handle: 'clear',
displayName: 'Clear',
},
+ {
+ handle: 'setFocus',
+ displayName: 'Set focus',
+ },
+ {
+ 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',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisable',
+ displayName: 'Set disable',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
],
definition: {
+ validation: {
+ mandatory: { value: '{{false}}' },
+ regex: { value: '' },
+ minLength: { value: '' },
+ maxLength: { value: '' },
+ customRule: { value: '' },
+ },
+
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
- value: {
- value:
- 'ToolJet is an open-source low-code platform for building and deploying internal tools with minimal engineering efforts 🚀',
- },
- placeholder: { value: 'Placeholder text' },
+ value: { value: '' },
+ label: { value: 'Label' },
+ placeholder: { value: 'Enter your input' },
+ visibility: { value: '{{true}}' },
+ disabledState: { value: '{{false}}' },
+ loadingState: { value: '{{false}}' },
+ tooltip: { value: '' },
},
events: [],
styles: {
- visibility: { value: '{{true}}' },
- disabledState: { value: '{{false}}' },
- borderRadius: { value: '{{4}}' },
+ textColor: { value: '#1B1F24' },
+ borderColor: { value: '#CCD1D5' },
+ accentColor: { value: '#4368E3' },
+ errTextColor: { value: '#D72D39' },
+ borderRadius: { value: '{{6}}' },
+ backgroundColor: { value: '#fff' },
+ iconColor: { value: '#CFD3D859' },
+ direction: { value: 'left' },
+ width: { value: '{{33}}' },
+ alignment: { value: 'side' },
+ color: { value: '#1B1F24' },
+ auto: { value: '{{true}}' },
+ padding: { value: 'default' },
+ boxShadow: { value: '0px 0px 0px 0px #00000040' },
+ icon: { value: 'IconHome2' },
+ iconVisibility: { value: false },
},
},
};
diff --git a/frontend/src/Editor/WidgetManager/constants.js b/frontend/src/Editor/WidgetManager/constants.js
index 8c593455e1..3e7144a412 100644
--- a/frontend/src/Editor/WidgetManager/constants.js
+++ b/frontend/src/Editor/WidgetManager/constants.js
@@ -4,4 +4,5 @@ export const LEGACY_ITEMS = [
'MultiselectLegacy',
'RadioButtonLegacy',
'ModalLegacy',
+ 'TextareaLegacy',
];
diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js
index f0c66a1fc8..9ce0e8527a 100644
--- a/frontend/src/_helpers/appUtils.js
+++ b/frontend/src/_helpers/appUtils.js
@@ -2486,7 +2486,9 @@ export const setMultipleComponentsSelected = (components) => {
export const calculateMoveableBoxHeight = (componentType, layoutData, stylesDefinition, label) => {
// Early return for non input components
- if (!['TextInput', 'PasswordInput', 'NumberInput', 'DropdownV2', 'MultiselectV2'].includes(componentType)) {
+ if (
+ !['TextInput', 'PasswordInput', 'NumberInput', 'DropdownV2', 'MultiselectV2', 'TextArea'].includes(componentType)
+ ) {
return layoutData?.height;
}
const { alignment = { value: null }, width = { value: null }, auto = { value: null } } = stylesDefinition ?? {};
diff --git a/frontend/src/_helpers/editorHelpers.js b/frontend/src/_helpers/editorHelpers.js
index a1fb6f953f..35335477d0 100644
--- a/frontend/src/_helpers/editorHelpers.js
+++ b/frontend/src/_helpers/editorHelpers.js
@@ -4,7 +4,6 @@ import { Text } from '@/Editor/Components/Text';
import { Table } from '@/Editor/Components/Table/Table';
import { TextInput } from '@/Editor/Components/TextInput';
import { NumberInput } from '@/Editor/Components/NumberInput';
-import { TextArea } from '@/Editor/Components/TextArea';
import { Container } from '@/Editor/Components/Container';
import { Tabs } from '@/Editor/Components/Tabs';
import { RichTextEditor } from '@/Editor/Components/RichTextEditor';
@@ -81,7 +80,6 @@ export const AllComponents = {
TextInput,
NumberInput,
Table,
- TextArea,
Container,
Tabs,
RichTextEditor,
diff --git a/frontend/src/_styles/theme.scss b/frontend/src/_styles/theme.scss
index 4e2d88ba13..ac89dbe702 100644
--- a/frontend/src/_styles/theme.scss
+++ b/frontend/src/_styles/theme.scss
@@ -15766,6 +15766,11 @@ tbody {
/* Set the desired width */
}
+textarea.tj-text-input-widget{
+ resize: none !important;
+ overflow-y: auto !important;
+}
+
.tj-text-input-widget {
border: 1px solid var(--borders-default);
background-color: var(--surfaces-surface-01);
@@ -15777,7 +15782,7 @@ tbody {
background-color: var(--surfaces-surface-03);
}
- &:hover:not(:focus) {
+ &:hover:not(:focus):not([data-ignore-hover="true"]) {
border: 1px solid var(--tblr-input-border-color-darker) !important;
}
@@ -18664,6 +18669,16 @@ section.ai-message-prompt-input-wrapper {
}
}
+.phone-input-widget {
+ .tj-text-input-widget.is-invalid {
+ border-left: none !important;
+ }
+
+ input[type="tel"] {
+ border-top-left-radius: '0px' !important;
+ border-bottom-left-radius: '0px' !important;
+ }
+}
.single-line-codehinter-input {
.cm-editor {
diff --git a/frontend/src/_ui/Icon/bulkIcons/Planet.jsx b/frontend/src/_ui/Icon/bulkIcons/Planet.jsx
new file mode 100644
index 0000000000..0606a7b3e1
--- /dev/null
+++ b/frontend/src/_ui/Icon/bulkIcons/Planet.jsx
@@ -0,0 +1,27 @@
+import React from 'react';
+
+const Globe = ({ fill = '#C1C8CD', width = '25', className = '', viewBox = '0 0 25 25' }) => (
+
+);
+
+export default Globe;
diff --git a/frontend/src/_ui/Icon/bulkIcons/index.js b/frontend/src/_ui/Icon/bulkIcons/index.js
index 5936461f90..0a0930c179 100644
--- a/frontend/src/_ui/Icon/bulkIcons/index.js
+++ b/frontend/src/_ui/Icon/bulkIcons/index.js
@@ -118,6 +118,7 @@ import Lock from './Lock.jsx';
import AddTemplate from './AddTemplate.jsx';
import InviteCollaborator from './InviteCollabarator.jsx';
import CloseIcon from './CloseIcon.jsx';
+import Planet from './Planet.jsx';
const Icon = (props) => {
switch (props.name) {
@@ -361,7 +362,8 @@ const Icon = (props) => {
return ;
case 'checkcircle':
return ;
-
+ case 'planet':
+ return ;
default:
return ;
}
diff --git a/frontend/src/modules/WorkspaceSettings/components/BaseSSOConfigurationList/Configuration.scss b/frontend/src/modules/WorkspaceSettings/components/BaseSSOConfigurationList/Configuration.scss
index 70d3b05ea2..666b828022 100644
--- a/frontend/src/modules/WorkspaceSettings/components/BaseSSOConfigurationList/Configuration.scss
+++ b/frontend/src/modules/WorkspaceSettings/components/BaseSSOConfigurationList/Configuration.scss
@@ -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 {
diff --git a/server/data-migrations/1736448327127-MoveVisibilityDisabledStatesToPropertiesTextarea.ts b/server/data-migrations/1736448327127-MoveVisibilityDisabledStatesToPropertiesTextarea.ts
new file mode 100644
index 0000000000..7fbbc29ad9
--- /dev/null
+++ b/server/data-migrations/1736448327127-MoveVisibilityDisabledStatesToPropertiesTextarea.ts
@@ -0,0 +1,64 @@
+import { Component } from 'src/entities/component.entity';
+import { processDataInBatches } from '@helpers/migration.helper';
+import { EntityManager, MigrationInterface, QueryRunner } from 'typeorm';
+
+export class MoveVisibilityDisabledStatesToPropertiesTextarea1736448327127 implements MigrationInterface {
+ public async up(queryRunner: QueryRunner): Promise {
+ const batchSize = 100;
+ const entityManager = queryRunner.manager;
+
+ await processDataInBatches(
+ entityManager,
+ async (entityManager: EntityManager) => {
+ return await entityManager.find(Component, {
+ where: { type: 'TextArea' },
+ order: { createdAt: 'ASC' },
+ });
+ },
+ async (entityManager: EntityManager, components: Component[]) => {
+ await this.processUpdates(entityManager, components);
+ },
+ batchSize
+ );
+ }
+
+ private async processUpdates(entityManager, components) {
+ for (const component of components) {
+ const properties = component.properties;
+ const styles = component.styles;
+ const general = component.general;
+ const generalStyles = component.generalStyles;
+ const validation = component.validation;
+
+ if (styles.visibility) {
+ properties.visibility = styles?.visibility;
+ delete styles?.visibility;
+ }
+
+ if (styles.disabledState) {
+ properties.disabledState = styles?.disabledState;
+ delete styles?.disabledState;
+ }
+
+ if (generalStyles?.boxShadow) {
+ styles.boxShadow = generalStyles?.boxShadow;
+ delete generalStyles?.boxShadow;
+ }
+
+ // Label and value
+ if (properties.label == undefined || null) {
+ properties.label = '';
+ }
+
+ await entityManager.update(Component, component.id, {
+ properties,
+ styles,
+ general,
+ generalStyles,
+ validation,
+ });
+ }
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {}
+}
diff --git a/server/src/modules/apps/services/widget-config/currencyinput.js b/server/src/modules/apps/services/widget-config/currencyinput.js
new file mode 100644
index 0000000000..64a8f2df57
--- /dev/null
+++ b/server/src/modules/apps/services/widget-config/currencyinput.js
@@ -0,0 +1,305 @@
+export const currencyinputConfig = {
+ name: 'CurrencyInput',
+ displayName: 'Currency Input',
+ description: 'Currency input field',
+ component: 'CurrencyInput',
+ defaultSize: {
+ width: 10,
+ height: 40,
+ },
+ others: {
+ showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
+ showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
+ },
+ properties: {
+ label: {
+ type: 'code',
+ displayName: 'Label',
+ validation: { schema: { type: 'string' }, defaultValue: 'Label' },
+ },
+ placeholder: {
+ type: 'code',
+ displayName: 'Placeholder',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: 'Enter your number',
+ },
+ },
+ value: {
+ type: 'code',
+ displayName: 'Default value',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 0,
+ },
+ },
+ decimalPlaces: {
+ type: 'code',
+ displayName: 'Decimal places',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: '2' },
+ },
+ isCountryChangeEnabled: {
+ type: 'toggle',
+ displayName: 'Enable currency change',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ },
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ visibility: {
+ type: 'toggle',
+ displayName: 'Visibility',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ section: 'additionalActions',
+ },
+ disabledState: {
+ type: 'toggle',
+ displayName: 'Disable',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ tooltip: {
+ type: 'code',
+ displayName: 'Tooltip',
+ validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
+ section: 'additionalActions',
+ placeholder: 'Enter tooltip text',
+ },
+ },
+ validation: {
+ mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
+ regex: { type: 'code', displayName: 'Regex', placeholder: '^[a-zA-Z0-9_ -]{3,16}$' },
+ minValue: { type: 'code', displayName: 'Min value', placeholder: 'Enter min value' },
+ maxValue: { type: 'code', displayName: 'Max value', placeholder: 'Enter max value' },
+ customRule: {
+ type: 'code',
+ displayName: 'Custom validation',
+ placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
+ },
+ },
+ events: {
+ onChange: { displayName: 'On change' },
+ onEnterPressed: { displayName: 'On enter pressed' },
+ onFocus: { displayName: 'On focus' },
+ onBlur: { displayName: 'On blur' },
+ },
+ styles: {
+ color: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'label',
+ },
+ alignment: {
+ type: 'switch',
+ displayName: 'Alignment',
+ validation: { schema: { type: 'string' }, defaultValue: 'side' },
+ options: [
+ { displayName: 'Side', value: 'side' },
+ { displayName: 'Top', value: 'top' },
+ ],
+ accordian: 'label',
+ },
+ direction: {
+ type: 'switch',
+ displayName: '',
+ validation: { schema: { type: 'string' }, defaultValue: 'left' },
+ showLabel: false,
+ isIcon: true,
+ options: [
+ { displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
+ { displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
+ ],
+ accordian: 'label',
+ isFxNotRequired: true,
+ },
+ width: {
+ type: 'slider',
+ displayName: 'Width',
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+ auto: {
+ type: 'checkbox',
+ displayName: 'auto',
+ showLabel: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+
+ backgroundColor: {
+ type: 'color',
+ displayName: 'Background',
+ validation: { schema: { type: 'string' }, defaultValue: '#fff' },
+ accordian: 'field',
+ },
+ borderColor: {
+ type: 'color',
+ displayName: 'Border',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ },
+ accentColor: {
+ type: 'color',
+ displayName: 'Accent',
+ validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
+ accordian: 'field',
+ },
+ textColor: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'field',
+ },
+ errTextColor: {
+ type: 'color',
+ displayName: 'Error text',
+ validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
+ accordian: 'field',
+ },
+ icon: {
+ type: 'icon',
+ displayName: 'Icon',
+ validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
+ accordian: 'field',
+ visibility: false,
+ },
+ iconColor: {
+ type: 'color',
+ displayName: 'Icon color',
+ validation: { schema: { type: 'string' }, defaultValue: '#CFD3D859' },
+ accordian: 'field',
+ visibility: false,
+ showLabel: false,
+ },
+ borderRadius: {
+ type: 'numberInput',
+ displayName: 'Border radius',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
+ accordian: 'field',
+ },
+ boxShadow: {
+ type: 'boxShadow',
+ displayName: 'Box Shadow',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: '0px 0px 0px 0px #00000040',
+ },
+ accordian: 'field',
+ },
+ padding: {
+ type: 'switch',
+ displayName: 'Padding',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 'default',
+ },
+ isFxNotRequired: true,
+ options: [
+ { displayName: 'Default', value: 'default' },
+ { displayName: 'None', value: 'none' },
+ ],
+ accordian: 'container',
+ },
+ },
+ exposedVariables: {
+ value: '',
+ isMandatory: false,
+ isVisible: true,
+ isDisabled: false,
+ isLoading: false,
+ },
+ actions: [
+ {
+ handle: 'setValue',
+ displayName: 'Set Value',
+ params: [
+ { handle: 'value', displayName: 'value', defaultValue: '' },
+ { handle: 'country', displayName: 'country', defaultValue: '' },
+ ],
+ },
+ {
+ handle: 'clear',
+ displayName: 'Clear',
+ },
+ {
+ handle: 'setFocus',
+ displayName: 'Set focus',
+ },
+ {
+ handle: 'setBlur',
+ displayName: 'Set blur',
+ },
+ {
+ handle: 'setVisibility',
+ displayName: 'Set visibility',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisable',
+ displayName: 'Set disable',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ ],
+ definition: {
+ validation: {
+ mandatory: { value: '{{false}}' },
+ regex: { value: '' },
+ minValue: { value: '' },
+ maxValue: { value: '' },
+ customRule: { value: '' },
+ },
+
+ others: {
+ showOnDesktop: { value: '{{true}}' },
+ showOnMobile: { value: '{{false}}' },
+ },
+ properties: {
+ value: { value: '0' },
+ label: { value: 'Label' },
+ placeholder: { value: 'Enter amount' },
+ visibility: { value: '{{true}}' },
+ disabledState: { value: '{{false}}' },
+ loadingState: { value: '{{false}}' },
+ tooltip: { value: '' },
+ isCountryChangeEnabled: { value: '{{true}}' },
+ decimalPlaces: { value: '2' },
+ },
+ events: [],
+ styles: {
+ textColor: { value: '#1B1F24' },
+ borderColor: { value: '#CCD1D5' },
+ accentColor: { value: '#4368E3' },
+ errTextColor: { value: '#D72D39' },
+ borderRadius: { value: '{{6}}' },
+ backgroundColor: { value: '#fff' },
+ iconColor: { value: '#CFD3D859' },
+ direction: { value: 'left' },
+ width: { value: '{{33}}' },
+ alignment: { value: 'side' },
+ color: { value: '#1B1F24' },
+ auto: { value: '{{true}}' },
+ padding: { value: 'default' },
+ boxShadow: { value: '0px 0px 0px 0px #00000040' },
+ icon: { value: 'IconHome2' },
+ iconVisibility: { value: false },
+ },
+ },
+};
diff --git a/server/src/modules/apps/services/widget-config/emailinput.js b/server/src/modules/apps/services/widget-config/emailinput.js
new file mode 100644
index 0000000000..825340616f
--- /dev/null
+++ b/server/src/modules/apps/services/widget-config/emailinput.js
@@ -0,0 +1,292 @@
+export const emailinputConfig = {
+ name: 'EmailInput',
+ displayName: 'Email Input',
+ description: 'Email input field',
+ component: 'EmailInput',
+ defaultSize: {
+ width: 10,
+ height: 40,
+ },
+ others: {
+ showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
+ showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
+ },
+ properties: {
+ label: {
+ type: 'code',
+ displayName: 'Label',
+ validation: { schema: { type: 'string' }, defaultValue: 'Label' },
+ },
+ placeholder: {
+ type: 'code',
+ displayName: 'Placeholder',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: 'Enter email',
+ },
+ },
+ value: {
+ type: 'code',
+ displayName: 'Default value',
+ validation: {
+ schema: {
+ type: 'string',
+ },
+ defaultValue: 'Default value',
+ },
+ },
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ visibility: {
+ type: 'toggle',
+ displayName: 'Visibility',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ section: 'additionalActions',
+ },
+ disabledState: {
+ type: 'toggle',
+ displayName: 'Disable',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ tooltip: {
+ type: 'code',
+ displayName: 'Tooltip',
+ validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
+ section: 'additionalActions',
+ placeholder: 'Enter tooltip text',
+ },
+ },
+ validation: {
+ mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
+ regex: { type: 'code', displayName: 'Regex', placeholder: '^[a-zA-Z0-9_ -]{3,16}$' },
+ minLength: { type: 'code', displayName: 'Min length', placeholder: 'Enter min length' },
+ maxLength: { type: 'code', displayName: 'Max length', placeholder: 'Enter max length' },
+ customRule: {
+ type: 'code',
+ displayName: 'Custom validation',
+ placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
+ },
+ },
+ events: {
+ onChange: { displayName: 'On change' },
+ onEnterPressed: { displayName: 'On enter pressed' },
+ onFocus: { displayName: 'On focus' },
+ onBlur: { displayName: 'On blur' },
+ },
+ styles: {
+ color: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'label',
+ },
+ alignment: {
+ type: 'switch',
+ displayName: 'Alignment',
+ validation: { schema: { type: 'string' }, defaultValue: 'side' },
+ options: [
+ { displayName: 'Side', value: 'side' },
+ { displayName: 'Top', value: 'top' },
+ ],
+ accordian: 'label',
+ },
+ direction: {
+ type: 'switch',
+ displayName: '',
+ validation: { schema: { type: 'string' }, defaultValue: 'left' },
+ showLabel: false,
+ isIcon: true,
+ options: [
+ { displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
+ { displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
+ ],
+ accordian: 'label',
+ isFxNotRequired: true,
+ },
+ width: {
+ type: 'slider',
+ displayName: 'Width',
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+ auto: {
+ type: 'checkbox',
+ displayName: 'auto',
+ showLabel: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+
+ backgroundColor: {
+ type: 'color',
+ displayName: 'Background',
+ validation: { schema: { type: 'string' }, defaultValue: '#fff' },
+ accordian: 'field',
+ },
+ borderColor: {
+ type: 'color',
+ displayName: 'Border',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ },
+ accentColor: {
+ type: 'color',
+ displayName: 'Accent',
+ validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
+ accordian: 'field',
+ },
+ textColor: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'field',
+ },
+ errTextColor: {
+ type: 'color',
+ displayName: 'Error text',
+ validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
+ accordian: 'field',
+ },
+ icon: {
+ type: 'icon',
+ displayName: 'Icon',
+ validation: { schema: { type: 'string' }, defaultValue: 'IconMailFilled' },
+ accordian: 'field',
+ visibility: true,
+ },
+ iconColor: {
+ type: 'color',
+ displayName: 'Icon color',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ visibility: false,
+ showLabel: false,
+ },
+ borderRadius: {
+ type: 'numberInput',
+ displayName: 'Border radius',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
+ accordian: 'field',
+ },
+ boxShadow: {
+ type: 'boxShadow',
+ displayName: 'Box Shadow',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: '0px 0px 0px 0px #00000040',
+ },
+ accordian: 'field',
+ },
+ padding: {
+ type: 'switch',
+ displayName: 'Padding',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 'default',
+ },
+ isFxNotRequired: true,
+ options: [
+ { displayName: 'Default', value: 'default' },
+ { displayName: 'None', value: 'none' },
+ ],
+ accordian: 'container',
+ },
+ },
+ exposedVariables: {
+ value: '',
+ isMandatory: false,
+ isVisible: true,
+ isDisabled: false,
+ isLoading: false,
+ },
+ actions: [
+ {
+ handle: 'setText',
+ displayName: 'Set text',
+ params: [{ handle: 'text', displayName: 'text', defaultValue: 'New text' }],
+ },
+ {
+ handle: 'clear',
+ displayName: 'Clear',
+ },
+ {
+ handle: 'setFocus',
+ displayName: 'Set focus',
+ },
+ {
+ handle: 'setBlur',
+ displayName: 'Set blur',
+ },
+ {
+ handle: 'setVisibility',
+ displayName: 'Set visibility',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisable',
+ displayName: 'Set disable',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ ],
+ definition: {
+ validation: {
+ mandatory: { value: '{{false}}' },
+ regex: { value: '' },
+ minLength: { value: '' },
+ maxLength: { value: '' },
+ customRule: { value: '' },
+ },
+
+ others: {
+ showOnDesktop: { value: '{{true}}' },
+ showOnMobile: { value: '{{false}}' },
+ },
+ properties: {
+ value: { value: '' },
+ label: { value: 'Label' },
+ placeholder: { value: 'Enter email' },
+ visibility: { value: '{{true}}' },
+ disabledState: { value: '{{false}}' },
+ loadingState: { value: '{{false}}' },
+ tooltip: { value: '' },
+ },
+ events: [],
+ styles: {
+ textColor: { value: '#1B1F24' },
+ borderColor: { value: '#CCD1D5' },
+ accentColor: { value: '#4368E3' },
+ errTextColor: { value: '#D72D39' },
+ borderRadius: { value: '{{6}}' },
+ backgroundColor: { value: '#fff' },
+ iconColor: { value: '#CCD1D5' },
+ direction: { value: 'left' },
+ width: { value: '{{33}}' },
+ alignment: { value: 'side' },
+ color: { value: '#1B1F24' },
+ auto: { value: '{{true}}' },
+ padding: { value: 'default' },
+ boxShadow: { value: '0px 0px 0px 0px #00000040' },
+ icon: { value: 'IconMailFilled' },
+ iconVisibility: { value: true },
+ },
+ },
+};
diff --git a/server/src/modules/apps/services/widget-config/index.js b/server/src/modules/apps/services/widget-config/index.js
index bef9a2ea99..fdb4cf26df 100644
--- a/server/src/modules/apps/services/widget-config/index.js
+++ b/server/src/modules/apps/services/widget-config/index.js
@@ -58,6 +58,9 @@ import { kanbanBoardConfig } from './kanbanBoard';
import { datetimePickerV2Config } from './datetimepickerV2';
import { datePickerV2Config } from './datepickerV2';
import { timePickerConfig } from './timepicker';
+import { emailinputConfig } from './emailinput';
+import { phoneinputConfig } from './phoneinput';
+import {currencyinputConfig} from './currencyinput';
const widgets = {
buttonConfig,
@@ -73,12 +76,15 @@ const widgets = {
datetimePickerV2Config,
datePickerV2Config,
timePickerConfig,
+ emailinputConfig,
+ phoneinputConfig,
+ currencyinputConfig,
checkboxConfig,
radiobuttonConfig, //!Depreciated
radiobuttonV2Config,
toggleswitchConfig, //!Depreciated
toggleSwitchV2Config,
- textareaConfig,
+ textareaConfig, //! Deprecated
daterangepickerConfig,
textConfig,
imageConfig,
diff --git a/server/src/modules/apps/services/widget-config/phoneinput.js b/server/src/modules/apps/services/widget-config/phoneinput.js
new file mode 100644
index 0000000000..83e35ffc68
--- /dev/null
+++ b/server/src/modules/apps/services/widget-config/phoneinput.js
@@ -0,0 +1,288 @@
+export const phoneinputConfig = {
+ name: 'PhoneInput',
+ displayName: 'Phone Input',
+ description: 'Phone input field',
+ component: 'PhoneInput',
+ defaultSize: {
+ width: 10,
+ height: 40,
+ },
+ others: {
+ showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
+ showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
+ },
+ properties: {
+ label: {
+ type: 'code',
+ displayName: 'Label',
+ validation: { schema: { type: 'string' }, defaultValue: 'Label' },
+ },
+ placeholder: {
+ type: 'code',
+ displayName: 'Placeholder',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: 'Enter your input',
+ },
+ },
+ value: {
+ type: 'code',
+ displayName: 'Default value',
+ validation: {
+ schema: {
+ type: 'string',
+ },
+ defaultValue: 'Default value',
+ },
+ },
+ isCountryChangeEnabled: {
+ type: 'toggle',
+ displayName: 'Enable country change',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ },
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ visibility: {
+ type: 'toggle',
+ displayName: 'Visibility',
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ section: 'additionalActions',
+ },
+ disabledState: {
+ type: 'toggle',
+ displayName: 'Disable',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ tooltip: {
+ type: 'code',
+ displayName: 'Tooltip',
+ validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
+ section: 'additionalActions',
+ placeholder: 'Enter tooltip text',
+ },
+ },
+ validation: {
+ mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
+ regex: { type: 'code', displayName: 'Regex', placeholder: '^[a-zA-Z0-9_ -]{3,16}$' },
+ minLength: { type: 'code', displayName: 'Min length', placeholder: 'Enter min length' },
+ maxLength: { type: 'code', displayName: 'Max length', placeholder: 'Enter max length' },
+ customRule: {
+ type: 'code',
+ displayName: 'Custom validation',
+ placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
+ },
+ },
+ events: {
+ onChange: { displayName: 'On change' },
+ onEnterPressed: { displayName: 'On enter pressed' },
+ onFocus: { displayName: 'On focus' },
+ onBlur: { displayName: 'On blur' },
+ },
+ styles: {
+ color: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'label',
+ },
+ alignment: {
+ type: 'switch',
+ displayName: 'Alignment',
+ validation: { schema: { type: 'string' }, defaultValue: 'side' },
+ options: [
+ { displayName: 'Side', value: 'side' },
+ { displayName: 'Top', value: 'top' },
+ ],
+ accordian: 'label',
+ },
+ direction: {
+ type: 'switch',
+ displayName: '',
+ validation: { schema: { type: 'string' }, defaultValue: 'left' },
+ showLabel: false,
+ isIcon: true,
+ options: [
+ { displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
+ { displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
+ ],
+ accordian: 'label',
+ isFxNotRequired: true,
+ },
+ width: {
+ type: 'slider',
+ displayName: 'Width',
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+ auto: {
+ type: 'checkbox',
+ displayName: 'auto',
+ showLabel: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+
+ backgroundColor: {
+ type: 'color',
+ displayName: 'Background',
+ validation: { schema: { type: 'string' }, defaultValue: '#fff' },
+ accordian: 'field',
+ },
+ borderColor: {
+ type: 'color',
+ displayName: 'Border',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ },
+ accentColor: {
+ type: 'color',
+ displayName: 'Accent',
+ validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
+ accordian: 'field',
+ },
+ textColor: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'field',
+ },
+ errTextColor: {
+ type: 'color',
+ displayName: 'Error text',
+ validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
+ accordian: 'field',
+ },
+ borderRadius: {
+ type: 'numberInput',
+ displayName: 'Border radius',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
+ accordian: 'field',
+ },
+ boxShadow: {
+ type: 'boxShadow',
+ displayName: 'Box Shadow',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: '0px 0px 0px 0px #00000040',
+ },
+ accordian: 'field',
+ },
+ padding: {
+ type: 'switch',
+ displayName: 'Padding',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 'default',
+ },
+ isFxNotRequired: true,
+ options: [
+ { displayName: 'Default', value: 'default' },
+ { displayName: 'None', value: 'none' },
+ ],
+ accordian: 'container',
+ },
+ },
+ exposedVariables: {
+ value: '',
+ isMandatory: false,
+ isVisible: true,
+ isDisabled: false,
+ isLoading: false,
+ },
+ actions: [
+ {
+ handle: 'setValue',
+ displayName: 'Set Value',
+ params: [
+ { handle: 'value', displayName: 'value', defaultValue: '' },
+ { handle: 'country', displayName: 'country', defaultValue: '' },
+ ],
+ },
+ {
+ handle: 'setCountryCode',
+ displayName: 'Set country code',
+ params: [{ handle: 'countryCode', displayName: 'Country code', defaultValue: '' }],
+ },
+ {
+ handle: 'clear',
+ displayName: 'Clear',
+ },
+ {
+ handle: 'setFocus',
+ displayName: 'Set focus',
+ },
+ {
+ handle: 'setBlur',
+ displayName: 'Set blur',
+ },
+ {
+ handle: 'setVisibility',
+ displayName: 'Set visibility',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisable',
+ displayName: 'Set disable',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ ],
+ definition: {
+ validation: {
+ mandatory: { value: '{{false}}' },
+ regex: { value: '' },
+ minLength: { value: '' },
+ maxLength: { value: '' },
+ customRule: { value: '' },
+ },
+
+ others: {
+ showOnDesktop: { value: '{{true}}' },
+ showOnMobile: { value: '{{false}}' },
+ },
+ properties: {
+ value: { value: '' },
+ label: { value: 'Label' },
+ placeholder: { value: 'Enter your phone' },
+ visibility: { value: '{{true}}' },
+ disabledState: { value: '{{false}}' },
+ loadingState: { value: '{{false}}' },
+ tooltip: { value: '' },
+ isCountryChangeEnabled: { value: '{{true}}' },
+ },
+ events: [],
+ styles: {
+ textColor: { value: '#1B1F24' },
+ borderColor: { value: '#CCD1D5' },
+ accentColor: { value: '#4368E3' },
+ errTextColor: { value: '#D72D39' },
+ borderRadius: { value: '{{6}}' },
+ backgroundColor: { value: '#fff' },
+ direction: { value: 'left' },
+ width: { value: '{{33}}' },
+ alignment: { value: 'side' },
+ color: { value: '#1B1F24' },
+ auto: { value: '{{true}}' },
+ padding: { value: 'default' },
+ boxShadow: { value: '0px 0px 0px 0px #00000040' },
+ },
+ },
+};
diff --git a/server/src/modules/apps/services/widget-config/textarea.js b/server/src/modules/apps/services/widget-config/textarea.js
index c5e71be77d..d0e0364334 100644
--- a/server/src/modules/apps/services/widget-config/textarea.js
+++ b/server/src/modules/apps/services/widget-config/textarea.js
@@ -4,7 +4,7 @@ export const textareaConfig = {
description: 'Multi-line text input',
component: 'TextArea',
defaultSize: {
- width: 6,
+ width: 10,
height: 100,
},
others: {
@@ -12,82 +12,291 @@ export const textareaConfig = {
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
- value: {
+ label: {
type: 'code',
- displayName: 'Default value',
- validation: {
- schema: { type: 'string' },
- defaultValue: 'default text',
- },
+ displayName: 'Label',
+ validation: { schema: { type: 'string' }, defaultValue: 'Label' },
},
placeholder: {
type: 'code',
displayName: 'Placeholder',
validation: {
schema: { type: 'string' },
- defaultValue: 'Placeholder text',
+ defaultValue: 'Enter your input',
},
},
- },
- events: {},
- styles: {
+ value: {
+ type: 'code',
+ displayName: 'Default value',
+ validation: {
+ schema: {
+ type: 'string',
+ },
+ defaultValue: 'Default value',
+ },
+ },
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
visibility: {
type: 'toggle',
displayName: 'Visibility',
- validation: {
- schema: { type: 'boolean' },
- defaultValue: true,
- },
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
- validation: {
- schema: { type: 'boolean' },
- defaultValue: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: false },
+ section: 'additionalActions',
+ },
+ tooltip: {
+ type: 'code',
+ displayName: 'Tooltip',
+ validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
+ section: 'additionalActions',
+ placeholder: 'Enter tooltip text',
+ },
+ },
+ validation: {
+ mandatory: { type: 'toggle', displayName: 'Make this field mandatory' },
+ regex: { type: 'code', displayName: 'Regex', placeholder: '^[a-zA-Z0-9_ -]{3,16}$' },
+ minLength: { type: 'code', displayName: 'Min length', placeholder: 'Enter min length' },
+ maxLength: { type: 'code', displayName: 'Max length', placeholder: 'Enter max length' },
+ customRule: {
+ type: 'code',
+ displayName: 'Custom validation',
+ placeholder: `{{components.text2.text=='yes'&&'valid'}}`,
+ },
+ },
+ events: {
+ onChange: { displayName: 'On change' },
+ onEnterPressed: { displayName: 'On enter pressed' },
+ onFocus: { displayName: 'On focus' },
+ onBlur: { displayName: 'On blur' },
+ },
+ styles: {
+ color: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'label',
+ },
+ alignment: {
+ type: 'switch',
+ displayName: 'Alignment',
+ validation: { schema: { type: 'string' }, defaultValue: 'side' },
+ options: [
+ { displayName: 'Side', value: 'side' },
+ { displayName: 'Top', value: 'top' },
+ ],
+ accordian: 'label',
+ },
+ direction: {
+ type: 'switch',
+ displayName: '',
+ validation: { schema: { type: 'string' }, defaultValue: 'left' },
+ showLabel: false,
+ isIcon: true,
+ options: [
+ { displayName: 'alignleftinspector', value: 'left', iconName: 'alignleftinspector' },
+ { displayName: 'alignrightinspector', value: 'right', iconName: 'alignrightinspector' },
+ ],
+ accordian: 'label',
+ isFxNotRequired: true,
+ },
+ width: {
+ type: 'slider',
+ displayName: 'Width',
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
},
+ isFxNotRequired: true,
+ },
+ auto: {
+ type: 'checkbox',
+ displayName: 'auto',
+ showLabel: false,
+ validation: { schema: { type: 'boolean' }, defaultValue: true },
+ accordian: 'label',
+ conditionallyRender: {
+ key: 'alignment',
+ value: 'side',
+ },
+ isFxNotRequired: true,
+ },
+
+ backgroundColor: {
+ type: 'color',
+ displayName: 'Background',
+ validation: { schema: { type: 'string' }, defaultValue: '#fff' },
+ accordian: 'field',
+ },
+ borderColor: {
+ type: 'color',
+ displayName: 'Border',
+ validation: { schema: { type: 'string' }, defaultValue: '#CCD1D5' },
+ accordian: 'field',
+ },
+ accentColor: {
+ type: 'color',
+ displayName: 'Accent',
+ validation: { schema: { type: 'string' }, defaultValue: '#4368E3' },
+ accordian: 'field',
+ },
+ textColor: {
+ type: 'color',
+ displayName: 'Text',
+ validation: { schema: { type: 'string' }, defaultValue: '#1B1F24' },
+ accordian: 'field',
+ },
+ errTextColor: {
+ type: 'color',
+ displayName: 'Error text',
+ validation: { schema: { type: 'string' }, defaultValue: '#D72D39' },
+ accordian: 'field',
+ },
+ icon: {
+ type: 'icon',
+ displayName: 'Icon',
+ validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
+ accordian: 'field',
+ visibility: false,
+ },
+ iconColor: {
+ type: 'color',
+ displayName: 'Icon color',
+ validation: { schema: { type: 'string' }, defaultValue: '#CFD3D859' },
+ accordian: 'field',
+ visibility: false,
+ showLabel: false,
},
borderRadius: {
- type: 'code',
+ type: 'numberInput',
displayName: 'Border radius',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 6 },
+ accordian: 'field',
+ },
+ boxShadow: {
+ type: 'boxShadow',
+ displayName: 'Box Shadow',
validation: {
- schema: { type: 'number' },
- defaultValue: 4,
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: '0px 0px 0px 0px #00000040',
},
+ accordian: 'field',
+ },
+ padding: {
+ type: 'switch',
+ displayName: 'Padding',
+ validation: {
+ schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
+ defaultValue: 'default',
+ },
+ isFxNotRequired: true,
+ options: [
+ { displayName: 'Default', value: 'default' },
+ { displayName: 'None', value: 'none' },
+ ],
+ accordian: 'container',
},
},
exposedVariables: {
- value:
- 'ToolJet is an open-source low-code platform for building and deploying internal tools with minimal engineering efforts 🚀',
+ value: '',
+ isMandatory: false,
+ isVisible: true,
+ isDisabled: false,
+ isLoading: false,
},
actions: [
{
handle: 'setText',
- displayName: 'Set Text',
- params: [{ handle: 'text', displayName: 'text', defaultValue: 'New Text' }],
+ displayName: 'Set text',
+ params: [{ handle: 'text', displayName: 'text', defaultValue: 'New text' }],
},
{
handle: 'clear',
displayName: 'Clear',
},
+ {
+ handle: 'setFocus',
+ displayName: 'Set focus',
+ },
+ {
+ 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',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisable',
+ displayName: 'Set disable',
+ params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'loading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
],
definition: {
+ validation: {
+ mandatory: { value: '{{false}}' },
+ regex: { value: '' },
+ minLength: { value: '' },
+ maxLength: { value: '' },
+ customRule: { value: '' },
+ },
+
others: {
showOnDesktop: { value: '{{true}}' },
showOnMobile: { value: '{{false}}' },
},
properties: {
- value: {
- value:
- 'ToolJet is an open-source low-code platform for building and deploying internal tools with minimal engineering efforts 🚀',
- },
- placeholder: { value: 'Placeholder text' },
+ value: { value: '' },
+ label: { value: 'Label' },
+ placeholder: { value: 'Enter your input' },
+ visibility: { value: '{{true}}' },
+ disabledState: { value: '{{false}}' },
+ loadingState: { value: '{{false}}' },
+ tooltip: { value: '' },
},
events: [],
styles: {
- visibility: { value: '{{true}}' },
- disabledState: { value: '{{false}}' },
- borderRadius: { value: '{{4}}' },
+ textColor: { value: '#1B1F24' },
+ borderColor: { value: '#CCD1D5' },
+ accentColor: { value: '#4368E3' },
+ errTextColor: { value: '#D72D39' },
+ borderRadius: { value: '{{6}}' },
+ backgroundColor: { value: '#fff' },
+ iconColor: { value: '#CFD3D859' },
+ direction: { value: 'left' },
+ width: { value: '{{33}}' },
+ alignment: { value: 'side' },
+ color: { value: '#1B1F24' },
+ auto: { value: '{{true}}' },
+ padding: { value: 'default' },
+ boxShadow: { value: '0px 0px 0px 0px #00000040' },
+ icon: { value: 'IconHome2' },
+ iconVisibility: { value: false },
},
},
};