{
>
{
{
diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss
index 95d2ab56e0..23d1c7f7cf 100644
--- a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss
+++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/DateTimePicker/styles.scss
@@ -214,4 +214,9 @@
.input-value-padding {
box-sizing: border-box;
padding-right: 30px !important;
+}
+
+.react-datepicker__navigation{
+ overflow: visible !important;
+ height: inherit !important;
}
\ No newline at end of file
diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx
index e3f31c974d..e873228888 100644
--- a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx
+++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx
@@ -677,6 +677,7 @@ const ToolJetDbOperations = ({ optionchanged, options, darkMode, isHorizontalLay
}}
componentName="TooljetDatabase"
delayOnChange={false}
+ className="w-100"
/>
)}
diff --git a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/ComponentsManagerTab.jsx b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/ComponentsManagerTab.jsx
index ef70343140..2ad7977496 100644
--- a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/ComponentsManagerTab.jsx
+++ b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/ComponentsManagerTab.jsx
@@ -133,7 +133,7 @@ export const ComponentsManagerTab = ({ darkMode }) => {
'StarRating',
];
const integrationItems = ['Map'];
- const layoutItems = ['Container', 'Listview', 'Tabs', 'Modal'];
+ const layoutItems = ['Container', 'Listview', 'Tabs', 'ModalV2'];
filteredComponents.forEach((f) => {
if (commonItems.includes(f)) commonSection.items.push(f);
diff --git a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/constants.js b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/constants.js
index cc66756eff..fda26656ca 100644
--- a/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/constants.js
+++ b/frontend/src/AppBuilder/RightSideBar/ComponentsManagerTab/constants.js
@@ -1 +1 @@
-export const LEGACY_ITEMS = ['ToggleSwitch', 'DropDown', 'Multiselect', 'RadioButton', 'Datepicker'];
+export const LEGACY_ITEMS = ['ToggleSwitch', 'DropDown', 'Multiselect', 'RadioButton', 'Datepicker', 'Modal'];
diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/DefaultComponent.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/DefaultComponent.jsx
index acd1908bba..d680f21d1e 100644
--- a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/DefaultComponent.jsx
+++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/DefaultComponent.jsx
@@ -23,6 +23,7 @@ const SHOW_ADDITIONAL_ACTIONS = [
'Button',
'RichTextEditor',
'Image',
+ 'ModalV2',
];
const PROPERTIES_VS_ACCORDION_TITLE = {
Text: 'Data',
@@ -34,6 +35,7 @@ const PROPERTIES_VS_ACCORDION_TITLE = {
Button: 'Data',
Image: 'Data',
Container: 'Data',
+ ModalV2: 'Data',
};
export const DefaultComponent = ({ componentMeta, darkMode, ...restProps }) => {
@@ -151,7 +153,8 @@ export const baseComponentProperties = (
'properties',
currentState,
allComponents,
- darkMode
+ darkMode,
+ ''
)
),
});
diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Components/ModalV2.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/ModalV2.jsx
new file mode 100644
index 0000000000..4131217386
--- /dev/null
+++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Components/ModalV2.jsx
@@ -0,0 +1,110 @@
+import React from 'react';
+import Accordion from '@/_ui/Accordion';
+import { renderElement } from '../Utils';
+import { baseComponentProperties } from './DefaultComponent';
+import { resolveReferences } from '@/_helpers/utils';
+
+const INDEX_OF_TRIGGER = 2;
+
+export const ModalV2 = ({ componentMeta, darkMode, ...restProps }) => {
+ const {
+ layoutPropertyChanged,
+ component,
+ paramUpdated,
+ dataQueries,
+ currentState,
+ eventsChanged,
+ apps,
+ allComponents,
+ } = restProps;
+
+ let properties = [];
+ let additionalActions = [];
+ let dataProperties = [];
+
+ const events = Object.keys(componentMeta.events);
+ const validations = Object.keys(componentMeta.validation || {});
+
+ for (const [key] of Object.entries(componentMeta?.properties)) {
+ if (componentMeta?.properties[key]?.section === 'additionalActions') {
+ additionalActions.push(key);
+ } else if (componentMeta?.properties[key]?.accordian === 'Data') {
+ dataProperties.push(key);
+ } else {
+ properties.push(key);
+ }
+ }
+
+ const renderCustomElement = (param, paramType = 'properties') => {
+ return renderElement(component, componentMeta, paramUpdated, dataQueries, param, paramType, currentState);
+ };
+ const conditionalAccordionItems = (component) => {
+ const useDefaultButton = resolveReferences(
+ component.component.definition.properties.useDefaultButton?.value ?? false
+ );
+ const accordionItems = [];
+ let renderOptions = [];
+ const options = ['visibility', 'disabledTrigger', 'useDefaultButton'];
+
+ options.map((option) => renderOptions.push(renderCustomElement(option)));
+
+ const conditionalOptions = [{ name: 'triggerButtonLabel', condition: useDefaultButton }];
+
+ conditionalOptions.map(({ name, condition }) => {
+ if (condition) renderOptions.push(renderCustomElement(name));
+ });
+
+ accordionItems.push({
+ title: 'Trigger',
+ children: renderOptions,
+ });
+
+ return accordionItems;
+ };
+
+ if (component.component.definition.properties.size.value === 'fullscreen') {
+ component.component.properties.modalHeight = {
+ ...component.component.properties.modalHeight,
+ isHidden: true,
+ };
+ }
+
+ if (component.component.definition.properties.showHeader.value === '{{false}}') {
+ component.component.properties.headerHeight = {
+ ...component.component.properties.headerHeight,
+ isHidden: true,
+ };
+ }
+
+ if (component.component.definition.properties.showFooter.value === '{{false}}') {
+ component.component.properties.footerHeight = {
+ ...component.component.properties.footerHeight,
+ isHidden: true,
+ };
+ }
+
+ const accordionItems = baseComponentProperties(
+ dataProperties,
+ events,
+ component,
+ componentMeta,
+ layoutPropertyChanged,
+ paramUpdated,
+ dataQueries,
+ currentState,
+ eventsChanged,
+ apps,
+ allComponents,
+ validations,
+ darkMode,
+ [],
+ additionalActions
+ );
+
+ const [optionsItems] = conditionalAccordionItems(component);
+
+ // Insert the Trigger option as the third item
+ accordionItems.splice(INDEX_OF_TRIGGER, 0, optionsItems);
+
+ return ;
+};
diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Elements/Code.jsx b/frontend/src/AppBuilder/RightSideBar/Inspector/Elements/Code.jsx
index 7ee512ee20..c74e023c0b 100644
--- a/frontend/src/AppBuilder/RightSideBar/Inspector/Elements/Code.jsx
+++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Elements/Code.jsx
@@ -19,6 +19,7 @@ export const Code = ({
accordian,
placeholder,
validationFn,
+ isHidden = false,
}) => {
const currentState = useCurrentState();
@@ -43,6 +44,7 @@ export const Code = ({
onChange({ name: 'iconVisibility' }, 'value', value, 'styles');
}
+ if (isHidden) return null;
return (
{
@@ -703,6 +705,9 @@ const GetAccordion = React.memo(
case 'FilePicker':
return ;
+ case 'ModalV2':
+ return ;
+
case 'Modal':
return ;
diff --git a/frontend/src/AppBuilder/RightSideBar/Inspector/Utils.js b/frontend/src/AppBuilder/RightSideBar/Inspector/Utils.js
index 0ddda9f572..62ee032172 100644
--- a/frontend/src/AppBuilder/RightSideBar/Inspector/Utils.js
+++ b/frontend/src/AppBuilder/RightSideBar/Inspector/Utils.js
@@ -50,7 +50,8 @@ export function renderCustomStyles(
componentConfig.component == 'MultiselectV2' ||
componentConfig.component == 'RadioButtonV2' ||
componentConfig.component == 'Button' ||
- componentConfig.component == 'Image'
+ componentConfig.component == 'Image' ||
+ componentConfig.component == 'ModalV2'
) {
const paramTypeConfig = componentMeta[paramType] || {};
const paramConfig = paramTypeConfig[param] || {};
@@ -131,6 +132,7 @@ export function renderElement(
const paramTypeDefinition = componentDefinition[paramType] || {};
const definition = paramTypeDefinition[param] || {};
const meta = componentMeta[paramType][param];
+ const isHidden = component.component.properties[param]?.isHidden ?? false;
if (
componentConfig.component == 'DropDown' ||
@@ -170,6 +172,7 @@ export function renderElement(
component={component}
placeholder={placeholder}
validationFn={validationFn}
+ isHidden={isHidden}
/>
);
}
diff --git a/frontend/src/AppBuilder/RightSideBar/WidgetBox/WidgetBox.jsx b/frontend/src/AppBuilder/RightSideBar/WidgetBox/WidgetBox.jsx
index 1226bfba3e..69ded14971 100644
--- a/frontend/src/AppBuilder/RightSideBar/WidgetBox/WidgetBox.jsx
+++ b/frontend/src/AppBuilder/RightSideBar/WidgetBox/WidgetBox.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import WidgetIcon from '@/../assets/images/icons/widgets';
import { useTranslation } from 'react-i18next';
-const LEGACY_WIDGETS = ['ToggleSwitch', 'DropDown', 'Multiselect', 'RadioButton', 'Datepicker'];
+const LEGACY_WIDGETS = ['ToggleSwitch', 'DropDown', 'Multiselect', 'RadioButton', 'Datepicker', 'Modal'];
const NEW_WIDGETS = [
'ToggleSwitchV2',
'DropdownV2',
@@ -12,6 +12,7 @@ const NEW_WIDGETS = [
'DaterangePicker',
'DatePickerV2',
'TimePicker',
+ 'ModalV2',
];
export const WidgetBox = ({ component, darkMode }) => {
diff --git a/frontend/src/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig.js b/frontend/src/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig.js
index 4ad52da372..6933f4a5a5 100644
--- a/frontend/src/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig.js
+++ b/frontend/src/AppBuilder/WidgetManager/configs/restrictedWidgetsConfig.js
@@ -4,7 +4,14 @@ export const RESTRICTED_WIDGETS_CONFIG = {
Calendar: ['Calendar', 'Kanban'],
Container: ['Calendar', 'Kanban'],
Modal: ['Calendar', 'Kanban'],
+ ModalV2: ['Calendar', 'Kanban'],
+ ModalSlot: ['Calendar', 'Kanban', 'Table', 'Listview', 'Container'],
Tabs: ['Calendar', 'Kanban'],
Kanban_popout: ['Calendar', 'Kanban'],
Listview: ['Calendar', 'Kanban'],
};
+
+export const RESTRICTED_WIDGET_SLOTS_CONFIG = {
+ header: ['Calendar', 'Kanban', 'Table', 'Listview', 'Container'],
+ footer: ['Calendar', 'Kanban', 'Table', 'Listview', 'Container'],
+};
diff --git a/frontend/src/AppBuilder/WidgetManager/configs/widgetConfig.js b/frontend/src/AppBuilder/WidgetManager/configs/widgetConfig.js
index 13832857fb..be2855c476 100644
--- a/frontend/src/AppBuilder/WidgetManager/configs/widgetConfig.js
+++ b/frontend/src/AppBuilder/WidgetManager/configs/widgetConfig.js
@@ -3,6 +3,7 @@ import {
tableConfig,
chartConfig,
modalConfig,
+ modalV2Config,
formConfig,
textinputConfig,
numberinputConfig,
@@ -64,6 +65,7 @@ export const widgets = [
buttonConfig,
chartConfig,
modalConfig,
+ modalV2Config,
formConfig,
textinputConfig,
numberinputConfig,
diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/index.js b/frontend/src/AppBuilder/WidgetManager/widgets/index.js
index 2540cdeeef..ce0e73fdf5 100644
--- a/frontend/src/AppBuilder/WidgetManager/widgets/index.js
+++ b/frontend/src/AppBuilder/WidgetManager/widgets/index.js
@@ -2,6 +2,7 @@ import { buttonConfig } from './button';
import { tableConfig } from './table';
import { chartConfig } from './chart';
import { modalConfig } from './modal';
+import { modalV2Config } from './modalV2';
import { formConfig } from './form';
import { textinputConfig } from './textinput';
import { numberinputConfig } from './numberinput';
@@ -62,7 +63,8 @@ export {
buttonConfig,
tableConfig,
chartConfig,
- modalConfig,
+ modalConfig, //Deprecated
+ modalV2Config,
formConfig,
textinputConfig,
numberinputConfig,
diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/modal.js b/frontend/src/AppBuilder/WidgetManager/widgets/modal.js
index 42740ad9c1..8f0c34b566 100644
--- a/frontend/src/AppBuilder/WidgetManager/widgets/modal.js
+++ b/frontend/src/AppBuilder/WidgetManager/widgets/modal.js
@@ -1,6 +1,6 @@
export const modalConfig = {
- name: 'Modal',
- displayName: 'Modal',
+ name: 'ModalLegacy',
+ displayName: 'Modal (Legacy)',
description: 'Show pop-up windows',
component: 'Modal',
defaultSize: {
diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/modalV2.js b/frontend/src/AppBuilder/WidgetManager/widgets/modalV2.js
new file mode 100644
index 0000000000..e7e96c4398
--- /dev/null
+++ b/frontend/src/AppBuilder/WidgetManager/widgets/modalV2.js
@@ -0,0 +1,277 @@
+export const modalV2Config = {
+ name: 'Modal',
+ displayName: 'Modal',
+ description: 'Show pop-up windows',
+ component: 'ModalV2',
+ defaultSize: {
+ width: 10,
+ height: 34,
+ },
+ others: {
+ showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
+ showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
+ },
+ properties: {
+ loadingState: {
+ type: 'toggle',
+ displayName: 'Loading state',
+ validation: {
+ schema: { type: 'boolean' },
+ defaultValue: false,
+ },
+ section: 'additionalActions',
+ },
+ visibility: {
+ type: 'toggle',
+ displayName: 'Modal trigger visibility',
+ validation: {
+ schema: { type: 'boolean' },
+ defaultValue: true,
+ },
+ },
+ disabledTrigger: {
+ type: 'toggle',
+ displayName: 'Disable modal trigger',
+ validation: {
+ schema: { type: 'boolean' },
+ defaultValue: false,
+ },
+ },
+ disabledModal: {
+ type: 'toggle',
+ displayName: 'Disable modal window',
+ validation: {
+ schema: { type: 'boolean' },
+ defaultValue: false,
+ },
+ section: 'additionalActions',
+ },
+ useDefaultButton: {
+ type: 'toggle',
+ displayName: 'Use default trigger button',
+ validation: {
+ schema: {
+ type: 'boolean',
+ },
+ defaultValue: true,
+ },
+ },
+ triggerButtonLabel: {
+ type: 'code',
+ displayName: 'Trigger button label',
+ validation: {
+ schema: {
+ type: 'string',
+ },
+ defaultValue: 'Launch Modal',
+ },
+ },
+
+ // Data Accordion
+ showHeader: { type: 'toggle', displayName: 'Header', accordian: 'Data' },
+ showFooter: { type: 'toggle', displayName: 'Footer', accordian: 'Data' },
+
+ size: {
+ type: 'select',
+ displayName: 'Width',
+ accordian: 'Data',
+ options: [
+ { name: 'small', value: 'sm' },
+ { name: 'medium', value: 'lg' },
+ { name: 'large', value: 'xl' },
+ { name: 'fullscreen', value: 'fullscreen' },
+ ],
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: 'lg',
+ },
+ },
+ modalHeight: {
+ type: 'numberInput',
+ displayName: 'Height',
+ accordian: 'Data',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 400 },
+ },
+ headerHeight: {
+ type: 'numberInput',
+ displayName: 'Header height',
+ accordian: 'Data',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
+ },
+ footerHeight: {
+ type: 'numberInput',
+ displayName: 'Footer height',
+ accordian: 'Data',
+ validation: { schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] }, defaultValue: 80 },
+ },
+ hideOnEsc: { type: 'toggle', displayName: 'Close on escape key', section: 'additionalActions' },
+ closeOnClickingOutside: { type: 'toggle', displayName: 'Close on clicking outside', section: 'additionalActions' },
+ hideCloseButton: { type: 'toggle', displayName: 'Hide close button', section: 'additionalActions' },
+ },
+ events: {
+ onOpen: { displayName: 'On open' },
+ onClose: { displayName: 'On close' },
+ },
+ defaultChildren: [
+ {
+ componentName: 'Text',
+ slotName: 'header',
+ layout: {
+ top: 21,
+ left: 1,
+ height: 40,
+ },
+ displayName: 'ModalHeaderTitle',
+ properties: ['text'],
+ accessorKey: 'text',
+ styles: ['fontWeight', 'textSize', 'textColor'],
+ defaultValue: {
+ text: 'Modal title',
+ textSize: 20,
+ textColor: '#000',
+ },
+ },
+ {
+ componentName: 'Button',
+ slotName: 'footer',
+ layout: {
+ top: 24,
+ left: 22,
+ height: 36,
+ },
+ displayName: 'ModalFooterCancel',
+ properties: ['text'],
+ styles: ['type', 'borderColor', 'padding'],
+ defaultValue: {
+ text: 'Button1',
+ type: 'outline',
+ borderColor: '#CCD1D5',
+ },
+ },
+ {
+ componentName: 'Button',
+ slotName: 'footer',
+ layout: {
+ top: 24,
+ left: 32,
+ height: 36,
+ },
+ displayName: 'ModalFooterConfirm',
+ properties: ['text'],
+ defaultValue: {
+ text: 'Button2',
+ padding: 'none',
+ },
+ },
+ ],
+ styles: {
+ headerBackgroundColor: {
+ type: 'color',
+ displayName: 'Header background color',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: '#ffffffff',
+ },
+ },
+ footerBackgroundColor: {
+ type: 'color',
+ displayName: 'Footer background color',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: '#ffffffff',
+ },
+ },
+ bodyBackgroundColor: {
+ type: 'color',
+ displayName: 'Body background color',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: '#ffffffff',
+ },
+ },
+ triggerButtonBackgroundColor: {
+ type: 'color',
+ displayName: 'Trigger button background color',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: false,
+ },
+ },
+ triggerButtonTextColor: {
+ type: 'color',
+ displayName: 'Trigger button text color',
+ validation: {
+ schema: { type: 'string' },
+ defaultValue: false,
+ },
+ },
+ },
+ exposedVariables: {
+ show: false,
+ isDisabledModal: false,
+ isDisabledTrigger: false,
+ isVisible: true,
+ isLoading: false,
+ },
+ actions: [
+ {
+ handle: 'open',
+ displayName: 'Open',
+ },
+ {
+ handle: 'close',
+ displayName: 'Close',
+ },
+ {
+ handle: 'setVisibility',
+ displayName: 'Set visibility',
+ params: [{ handle: 'setVisibility', displayName: 'Value', defaultValue: '{{true}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisableTrigger',
+ displayName: 'Set disable trigger',
+ params: [{ handle: 'setDisableTrigger', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setDisableModal',
+ displayName: 'Set disable modal',
+ params: [{ handle: 'setDisableModal', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ {
+ handle: 'setLoading',
+ displayName: 'Set loading',
+ params: [{ handle: 'setLoading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
+ },
+ ],
+ definition: {
+ others: {
+ showOnDesktop: { value: '{{true}}' },
+ showOnMobile: { value: '{{false}}' },
+ },
+ properties: {
+ loadingState: { value: `{{false}}` },
+ visibility: { value: '{{true}}' },
+ disabledTrigger: { value: '{{false}}' },
+ disabledModal: { value: '{{false}}' },
+ useDefaultButton: { value: `{{true}}` },
+ triggerButtonLabel: { value: `Launch Modal` },
+ size: { value: 'lg' },
+ showHeader: { value: '{{true}}' },
+ showFooter: { value: '{{true}}' },
+ hideCloseButton: { value: '{{false}}' },
+ hideOnEsc: { value: '{{true}}' },
+ closeOnClickingOutside: { value: '{{false}}' },
+ modalHeight: { value: 400 },
+ headerHeight: { value: 80 },
+ footerHeight: { value: 80 },
+ },
+ events: [],
+ styles: {
+ headerBackgroundColor: { value: '#ffffffff' },
+ footerBackgroundColor: { value: '#ffffffff' },
+ bodyBackgroundColor: { value: '#ffffffff' },
+ triggerButtonBackgroundColor: { value: '#4D72FA' },
+ triggerButtonTextColor: { value: '#ffffffff' },
+ },
+ },
+};
diff --git a/frontend/src/AppBuilder/Widgets/Container.jsx b/frontend/src/AppBuilder/Widgets/Container.jsx
index 16623280a5..1334098423 100644
--- a/frontend/src/AppBuilder/Widgets/Container.jsx
+++ b/frontend/src/AppBuilder/Widgets/Container.jsx
@@ -45,6 +45,7 @@ export const Container = ({
border: `1px solid ${borderColor}`,
height,
display: isVisible ? 'flex' : 'none',
+ flexDirection: 'column',
position: 'relative',
boxShadow,
};
diff --git a/frontend/src/AppBuilder/Widgets/ModalV2/Components/Footer.jsx b/frontend/src/AppBuilder/Widgets/ModalV2/Components/Footer.jsx
new file mode 100644
index 0000000000..e25027ce33
--- /dev/null
+++ b/frontend/src/AppBuilder/Widgets/ModalV2/Components/Footer.jsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { default as BootstrapModal } from 'react-bootstrap/Modal';
+import { Container as SubContainer } from '@/AppBuilder/AppCanvas/Container';
+import { getCanvasHeight } from '@/AppBuilder/Widgets/ModalV2/helpers/utils';
+
+export const ModalFooter = React.memo(({ id, isDisabled, customStyles, darkMode, width, footerHeight, onClick }) => {
+ const canvasFooterHeight = getCanvasHeight(footerHeight);
+ return (
+
+
+ {isDisabled && (
+