diff --git a/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx b/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx index 8d2dcb302d..559ef0446d 100644 --- a/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx +++ b/frontend/src/AppBuilder/AppCanvas/AppCanvas.jsx @@ -257,6 +257,7 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => { { isModuleMode={isModuleMode} isMobileLayout={isMobileLayout} showCanvasHeader={showCanvasHeader} + showCanvasFooter={showCanvasFooter} position={position} isPagesSidebarHidden={isPagesSidebarHidden} appType={appType} diff --git a/frontend/src/AppBuilder/AppCanvas/Grid/DesktopLayout.jsx b/frontend/src/AppBuilder/AppCanvas/Grid/DesktopLayout.jsx index 55b5a220a3..680616bfee 100644 --- a/frontend/src/AppBuilder/AppCanvas/Grid/DesktopLayout.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Grid/DesktopLayout.jsx @@ -3,6 +3,7 @@ import cx from 'classnames'; import { PAGE_CANVAS_HEADER_HEIGHT } from '../appCanvasConstants'; import { PageCanvasHeader } from './PageCanvasHeader'; +import { PageCanvasFooter } from './PageCanvasFooter'; import PagesSidebarNavigation from '../../RightSideBar/PageSettingsTab/PageMenu/PagesSidebarNavigation'; import { CanvasContentTail } from './CanvasContentTail'; @@ -11,6 +12,7 @@ export const DesktopLayout = ({ isModuleMode, isMobileLayout, showCanvasHeader, + showCanvasFooter, position, isPagesSidebarHidden, appType, @@ -67,5 +69,6 @@ export const DesktopLayout = ({ {mainCanvasContainer} + ); diff --git a/frontend/src/AppBuilder/AppCanvas/Grid/MobileLayout.jsx b/frontend/src/AppBuilder/AppCanvas/Grid/MobileLayout.jsx index 7b27f810bb..8d76bc09ad 100644 --- a/frontend/src/AppBuilder/AppCanvas/Grid/MobileLayout.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Grid/MobileLayout.jsx @@ -3,6 +3,7 @@ import cx from 'classnames'; import { PAGE_CANVAS_HEADER_HEIGHT } from '../appCanvasConstants'; import { PageCanvasHeader } from './PageCanvasHeader'; +import { PageCanvasFooter } from './PageCanvasFooter'; import MobileNavigationHeader from '../../RightSideBar/PageSettingsTab/PageMenu/MobileNavigationHeader'; import { CanvasContentTail } from './CanvasContentTail'; @@ -11,6 +12,7 @@ export const MobileLayout = ({ // mobileCanvasFrameRef, // mobileNavSheetContainerRef, showCanvasHeader, + showCanvasFooter, isMobileLayout, currentMode, appType, @@ -62,6 +64,7 @@ export const MobileLayout = ({ {mainCanvasContainer} + ); }; diff --git a/frontend/src/AppBuilder/AppCanvas/Grid/PageCanvasFooter.jsx b/frontend/src/AppBuilder/AppCanvas/Grid/PageCanvasFooter.jsx new file mode 100644 index 0000000000..384d286e4c --- /dev/null +++ b/frontend/src/AppBuilder/AppCanvas/Grid/PageCanvasFooter.jsx @@ -0,0 +1,96 @@ +import React from 'react'; +import cx from 'classnames'; +import { shallow } from 'zustand/shallow'; + +import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext'; +import useStore from '@/AppBuilder/_stores/store'; +import useAppDarkMode from '@/_hooks/useAppDarkMode'; +import { CONTAINER_FORM_CANVAS_PADDING, PAGE_CANVAS_FOOTER_HEIGHT } from '../appCanvasConstants'; +import { Container } from '../Container'; +import ConfigHandleButton from '@/_components/ConfigHandleButton'; + +export const PageCanvasFooter = ({ showCanvasFooter, isMobileLayout, currentMode }) => { + const { moduleId } = useModuleContext(); + const currentPageId = useStore((state) => state.modules[moduleId].currentPageId); + const selectedVersion = useStore((state) => state.selectedVersion, shallow); + const isMobilePreviewMode = selectedVersion?.id && isMobileLayout && currentMode === 'view'; + + const footerBackgroundColor = useStore( + (state) => state.modules[moduleId].pages.find((p) => p.id === currentPageId)?.pageFooter?.backgroundColor, + shallow + ); + const footerBorderColor = useStore( + (state) => state.modules[moduleId].pages.find((p) => p.id === currentPageId)?.pageFooter?.borderColor, + shallow + ); + + const setCanvasFooterSelected = useStore((state) => state.setCanvasFooterSelected, shallow); + const isCanvasFooterSelected = useStore((state) => state.isCanvasFooterSelected, shallow); + const clearSelectedComponents = useStore((state) => state.clearSelectedComponents, shallow); + + const { isAppDarkMode } = useAppDarkMode(); + + if (!showCanvasFooter) return null; + + return ( +
{ + if (currentMode === 'edit') { + e.stopPropagation(); + clearSelectedComponents(); + setCanvasFooterSelected(true); + } + }} + style={{ + position: 'sticky', + bottom: 0, + zIndex: 10, + flexShrink: 0, + padding: `${CONTAINER_FORM_CANVAS_PADDING}px`, + height: `${PAGE_CANVAS_FOOTER_HEIGHT}px`, + border: `1px solid ${ + isCanvasFooterSelected ? 'var(--border-accent-strong)' : footerBorderColor ?? 'var(--cc-default-border)' + }`, + backgroundColor: footerBackgroundColor ?? (isAppDarkMode ? '#232E3C' : '#fff'), + width: '100%', + }} + > + {currentMode === 'edit' && ( +
+ + App footer + +
+ )} + +
+ ); +}; diff --git a/frontend/src/AppBuilder/AppCanvas/Selecto.jsx b/frontend/src/AppBuilder/AppCanvas/Selecto.jsx index 52dc0acd79..83a510c4fd 100644 --- a/frontend/src/AppBuilder/AppCanvas/Selecto.jsx +++ b/frontend/src/AppBuilder/AppCanvas/Selecto.jsx @@ -34,8 +34,8 @@ const EditorSelecto = () => { const target = e.inputEvent.target; const componentId = target.getAttribute('component-id'); - // For canvas header, we don't have a specific canvasStartId to track - if (componentId === 'canvas-header') { + // For canvas header/footer, we don't have a specific canvasStartId to track + if (componentId === 'canvas-header' || componentId === 'canvas-footer') { canvasStartId.current = null; return; } @@ -124,8 +124,10 @@ const EditorSelecto = () => { const isAppCanvas = target.getAttribute('component-id') === 'canvas'; const isSubContainer = target.getAttribute('component-id') !== 'canvas' || target.getAttribute('data-parentId'); const isShiftKeyPressed = e.inputEvent.shiftKey; - const isPageCanvasHeader = target.getAttribute('component-id') === 'canvas-header'; - if (isAppCanvas || (isShiftKeyPressed && isSubContainer) || isPageCanvasHeader) { + const isPageCanvasHeaderOrFooter = + target.getAttribute('component-id') === 'canvas-header' || + target.getAttribute('component-id') === 'canvas-footer'; + if (isAppCanvas || (isShiftKeyPressed && isSubContainer) || isPageCanvasHeaderOrFooter) { return true; } diff --git a/frontend/src/AppBuilder/AppCanvas/appCanvas.scss b/frontend/src/AppBuilder/AppCanvas/appCanvas.scss index f19cb03091..a49444f364 100644 --- a/frontend/src/AppBuilder/AppCanvas/appCanvas.scss +++ b/frontend/src/AppBuilder/AppCanvas/appCanvas.scss @@ -42,7 +42,6 @@ } &.canvas-footer-slot--edit { - &:hover, &.canvas-footer-slot--selected { .canvas-footer-tooltip { display: flex !important; @@ -142,10 +141,15 @@ overflow: visible !important; } -#main-editor-canvas.disable-moveable-line .canvas-header-slot + .canvas-wrapper { - position: relative; - z-index: 11; -} +// #main-editor-canvas.disable-moveable-line .canvas-header-slot + .canvas-wrapper { +// position: relative; +// z-index: 11; +// } + +// #main-editor-canvas.disable-moveable-line .canvas-wrapper + .canvas-footer-slot { +// position: relative; +// z-index: 11; +// } // This is required to maintain the height of the subcontainer when dragging a widget inside it diff --git a/frontend/src/AppBuilder/RightSideBar/PageSettingsTab/PageMenu/AddNewPagePopup.jsx b/frontend/src/AppBuilder/RightSideBar/PageSettingsTab/PageMenu/AddNewPagePopup.jsx index 3d3c82d8b4..03021adda0 100644 --- a/frontend/src/AppBuilder/RightSideBar/PageSettingsTab/PageMenu/AddNewPagePopup.jsx +++ b/frontend/src/AppBuilder/RightSideBar/PageSettingsTab/PageMenu/AddNewPagePopup.jsx @@ -97,6 +97,8 @@ export const AddEditPagePopup = forwardRef(({ darkMode, ...props }, ref) => { ); const hasCanvasPageHeaderEnabled = useStore((state) => state.license?.featureAccess?.canvasPageHeaderEnabled); + const hasCanvasPageFooterEnabled = useStore((state) => state.license?.featureAccess?.canvasPageFooterEnabled); + const [page, setPage] = useState(editingPage || props?.page); const [pageName, setPageName] = useState(''); const [handle, setHandle] = useState(''); @@ -449,12 +451,24 @@ export const AddEditPagePopup = forwardRef(({ darkMode, ...props }, ref) => {
Page footer
- +
- +