From 2ded1f3f3bf3a8b19a9aa34e3c0a19414e0993a7 Mon Sep 17 00:00:00 2001 From: Bahati308 Date: Wed, 10 Dec 2025 15:32:19 +0300 Subject: [PATCH] fix(formplayer): fix navigation button overlap and keyboard layout issues --- formulus-formplayer/src/App.tsx | 6 +- formulus-formplayer/src/FormLayout.tsx | 184 ++++++++++++++++++ .../src/SwipeLayoutRenderer.tsx | 70 ++++--- formulus-formplayer/src/index.css | 9 +- 4 files changed, 225 insertions(+), 44 deletions(-) create mode 100644 formulus-formplayer/src/FormLayout.tsx diff --git a/formulus-formplayer/src/App.tsx b/formulus-formplayer/src/App.tsx index 1c739ec16..e81813b99 100644 --- a/formulus-formplayer/src/App.tsx +++ b/formulus-formplayer/src/App.tsx @@ -514,7 +514,7 @@ function App() { // Render loading state or error if needed if (isLoading) { return ( - + Loading form... @@ -528,7 +528,7 @@ function App() { if (loadError || !schema || !uischema) { return ( - + {loadError || 'Failed to load form'} @@ -562,7 +562,7 @@ function App() { className="App" style={{ display: 'flex', - height: '100vh', + height: '100dvh', // Use dynamic viewport height for mobile keyboard support width: '100%' }} > diff --git a/formulus-formplayer/src/FormLayout.tsx b/formulus-formplayer/src/FormLayout.tsx new file mode 100644 index 000000000..6eefdba9b --- /dev/null +++ b/formulus-formplayer/src/FormLayout.tsx @@ -0,0 +1,184 @@ +import React, { ReactNode } from 'react'; +import { Box, Paper, Stack, Button } from '@mui/material'; + +interface FormLayoutProps { + /** + * The main form content to display + */ + children: ReactNode; + + /** + * Previous button configuration + */ + previousButton?: { + label?: string; + onClick: () => void; + disabled?: boolean; + }; + + /** + * Next button configuration + */ + nextButton?: { + label?: string; + onClick: () => void; + disabled?: boolean; + }; + + /** + * Optional header content (e.g., progress bar) + */ + header?: ReactNode; + + /** + * Additional padding at the bottom of content area (in pixels) + * Default: 120px to ensure content is never hidden behind navigation + */ + contentBottomPadding?: number; + + /** + * Whether to show navigation buttons + * Default: true + */ + showNavigation?: boolean; +} + +/** + * FormLayout Component + * + * A robust, responsive layout component for forms that: + * - Prevents navigation buttons from overlapping form content + * - Handles mobile keyboard appearance correctly + * - Ensures all form fields are scrollable and accessible + * - Uses dynamic viewport height (100dvh) for proper mobile support + * + * Layout Structure: + * - Header area (sticky at top, optional) + * - Scrollable content area (flexible, with bottom padding) + * - Navigation bar (sticky at bottom, non-overlapping) + */ +const FormLayout: React.FC = ({ + children, + previousButton, + nextButton, + header, + contentBottomPadding = 120, + showNavigation = true +}) => { + return ( + + {/* Header Area - Sticky at top (optional) */} + {header && ( + + {header} + + )} + + {/* Scrollable Content Area */} + + + {/* Navigation Bar - Sticky at bottom, non-overlapping */} + {showNavigation && (previousButton || nextButton) && ( + + *': { + flex: { xs: 1, sm: '0 1 auto' }, + minWidth: { xs: 'auto', sm: '120px' }, + }, + }} + > + {previousButton && ( + + )} + {nextButton && ( + + )} + + + )} + + ); +}; + +export default FormLayout; + diff --git a/formulus-formplayer/src/SwipeLayoutRenderer.tsx b/formulus-formplayer/src/SwipeLayoutRenderer.tsx index f4e534a67..bf3adacc1 100644 --- a/formulus-formplayer/src/SwipeLayoutRenderer.tsx +++ b/formulus-formplayer/src/SwipeLayoutRenderer.tsx @@ -2,10 +2,10 @@ import React, { useCallback, useState, useEffect, useMemo } from "react"; import { JsonFormsDispatch, withJsonFormsControlProps } from "@jsonforms/react"; import { ControlProps, rankWith, uiTypeIs, RankedTester } from "@jsonforms/core"; import { useSwipeable } from "react-swipeable"; -import { Button, Box } from "@mui/material"; import { useFormContext } from "./App"; import { draftService } from './DraftService'; import FormProgressBar from './FormProgressBar'; +import FormLayout from './FormLayout'; interface SwipeLayoutProps extends ControlProps { currentPage: number; @@ -89,17 +89,37 @@ const SwipeLayoutRenderer = ({ }, [layouts, currentPage]); return ( - - {/* Progress Bar */} - + + } + previousButton={ + currentPage > 0 + ? { + onClick: () => navigateToPage(Math.max(currentPage - 1, 0)), + disabled: isNavigating, + } + : undefined + } + nextButton={ + currentPage < layouts.length - 1 + ? { + onClick: () => navigateToPage(Math.min(currentPage + 1, layouts.length - 1)), + disabled: isNavigating, + } + : undefined + } + contentBottomPadding={120} + showNavigation={true} + >
{(uischema as any)?.label &&

{(uischema as any).label}

} {layouts.length > 0 && ( @@ -113,31 +133,7 @@ const SwipeLayoutRenderer = ({ /> )}
- - - - -
+ ); }; diff --git a/formulus-formplayer/src/index.css b/formulus-formplayer/src/index.css index 05541afad..9fff6f7fb 100644 --- a/formulus-formplayer/src/index.css +++ b/formulus-formplayer/src/index.css @@ -9,13 +9,14 @@ body { .swipelayout_screen { margin: 0; - padding-top: 4vh; + padding-top: 2vh; padding-left: 2%; padding-right: 2%; - padding-bottom: 0; + padding-bottom: 2vh; width: 96%; - height: 96vh; - overflow: hidden; + /* Remove fixed height and overflow hidden - let FormLayout handle scrolling */ + min-height: 100%; + box-sizing: border-box; } .swipelayout_screen > div > div {