fix: fixed formgenerator layout and design

This commit is contained in:
Ida Dittrich 2026-01-14 07:54:06 +01:00
parent 2b96ab7b66
commit 54ba020c45
4 changed files with 316 additions and 111 deletions

View file

@ -24,6 +24,8 @@
align-items: center;
gap: 10px;
flex-shrink: 0;
flex-wrap: wrap;
flex: 1;
}
.activeFiltersCount {
@ -72,7 +74,7 @@
.floatingLabelInput {
position: relative;
width: 250px;
width: 400px;
}
.label {
@ -280,3 +282,167 @@
}
}
/* Pagination Controls */
.paginationControls {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
margin-left: auto;
flex-wrap: wrap;
}
.pageSizeSelector {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--color-text);
}
.pageSizeSelector label {
white-space: nowrap;
font-family: var(--font-family);
}
.pageSizeSelect {
height: 32px;
padding: 4px 8px;
border: 1px solid var(--color-primary);
border-radius: 4px;
font-size: 14px;
font-family: var(--font-family);
background: var(--color-bg);
color: var(--color-text);
cursor: pointer;
min-width: 60px;
}
.pageSizeSelect:focus {
outline: none;
border-color: var(--color-secondary);
}
.paginationButton {
width: 36px;
height: 36px;
padding: 0;
border: none;
background: var(--color-secondary);
color: white;
border-radius: 50%;
cursor: pointer;
font-family: var(--font-family);
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: 500;
flex-shrink: 0;
}
.paginationButton:hover:not(:disabled) {
background: var(--color-secondary-hover);
}
.paginationButton:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.paginationInfo {
font-size: 14px;
color: var(--color-text);
white-space: nowrap;
flex-shrink: 0;
}
/* Page numbers container */
.pageNumbers {
display: flex;
flex-wrap: wrap;
gap: 2px;
align-items: center;
justify-content: flex-start;
max-width: 40vw;
max-height: 120px;
overflow-y: auto;
padding: 4px;
}
/* Individual page number button */
.pageNumber {
min-width: 28px;
height: 28px;
padding: 0 6px;
border: 1px solid var(--color-border, #ddd);
background: var(--color-bg, #fff);
color: var(--color-text);
border-radius: 4px;
cursor: pointer;
font-family: var(--font-family);
font-size: 12px;
transition: all 0.15s ease;
display: flex;
align-items: center;
justify-content: center;
}
.pageNumber:hover:not(:disabled) {
background: var(--color-secondary);
color: white;
border-color: var(--color-secondary);
}
.pageNumber:disabled {
cursor: default;
}
/* Active/current page number */
.pageNumberActive {
background: var(--color-secondary);
color: white;
border-color: var(--color-secondary);
font-weight: 600;
}
/* Ellipsis indicator */
.pageEllipsis {
padding: 0 8px;
color: var(--color-text-secondary, #666);
font-size: 14px;
}
/* Responsive Design for Pagination */
@media (max-width: 768px) {
.paginationControls {
flex-direction: column;
align-items: stretch;
gap: 10px;
margin-left: 0;
width: 100%;
}
.pageSizeSelector {
order: -1;
justify-content: center;
}
.paginationInfo {
text-align: center;
font-size: 13px;
}
.pageNumbers {
max-width: 100%;
justify-content: center;
}
.pageNumber {
min-width: 24px;
height: 24px;
font-size: 11px;
}
}

View file

@ -1,3 +1,4 @@
import React from 'react';
import { useLanguage } from '../../../providers/language/LanguageContext';
import styles from './FormGeneratorControls.module.css';
import { Button } from '../../UiComponents/Button';
@ -49,6 +50,18 @@ export interface FormGeneratorControlsProps {
// Active filters count for display
activeFiltersCount?: number;
// Pagination props
pagination?: boolean;
currentPage?: number;
totalPages?: number;
currentPageSize?: number;
pageSizeOptions?: number[];
showPageSizeSelector?: boolean;
onPageChange?: (page: number) => void;
onPageSizeChange?: (pageSize: number) => void;
supportsBackendPagination?: boolean;
hookData?: any;
}
export function FormGeneratorControls({
@ -64,7 +77,17 @@ export function FormGeneratorControls({
searchable = true,
selectable = true,
loading = false,
activeFiltersCount = 0
activeFiltersCount = 0,
pagination = false,
currentPage = 1,
totalPages = 1,
currentPageSize = 10,
pageSizeOptions = [10, 25, 50, 100, 500],
showPageSizeSelector = true,
onPageChange,
onPageSizeChange,
supportsBackendPagination = false,
hookData
}: FormGeneratorControlsProps) {
const { t } = useLanguage();
@ -101,7 +124,7 @@ export function FormGeneratorControls({
</div>
)}
{/* Search Controls - Hide when items are selected */}
{/* Search Controls with Pagination - Hide when items are selected */}
{searchable && selectedCount === 0 && (
<div className={styles.searchContainer}>
<div className={styles.floatingLabelInput}>
@ -133,6 +156,109 @@ export function FormGeneratorControls({
<span className={styles.refreshIcon}><IoIosRefresh /></span>
</button>
)}
{/* Pagination Controls */}
{pagination && supportsBackendPagination && onPageChange && (
<div className={styles.paginationControls}>
{showPageSizeSelector && onPageSizeChange && (
<div className={styles.pageSizeSelector}>
<label htmlFor="pageSize">{t('formgen.pagination.pageSize', 'Items per page:')}</label>
<select
id="pageSize"
value={currentPageSize}
onChange={(e) => onPageSizeChange(Number(e.target.value))}
className={styles.pageSizeSelect}
>
{pageSizeOptions.map(size => (
<option key={size} value={size}>{size}</option>
))}
</select>
</div>
)}
<button
onClick={() => onPageChange(1)}
disabled={currentPage === 1}
className={styles.paginationButton}
title={t('formgen.pagination.first')}
>
««
</button>
<button
onClick={() => onPageChange(currentPage - 1)}
disabled={currentPage === 1}
className={styles.paginationButton}
title={t('formgen.pagination.prev')}
>
«
</button>
{/* Page number buttons - show up to 100 pages before and after current */}
<div className={styles.pageNumbers}>
{(() => {
const maxPagesVisible = 100; // Max pages to show on each side
const startPage = Math.max(1, currentPage - maxPagesVisible);
const endPage = Math.min(totalPages, currentPage + maxPagesVisible);
const pages: React.ReactNode[] = [];
// Show ellipsis at start if we're not showing page 1
if (startPage > 1) {
pages.push(
<span key="start-ellipsis" className={styles.pageEllipsis}>...</span>
);
}
// Generate page buttons
for (let i = startPage; i <= endPage; i++) {
pages.push(
<button
key={i}
onClick={() => onPageChange(i)}
disabled={i === currentPage}
className={`${styles.pageNumber} ${i === currentPage ? styles.pageNumberActive : ''}`}
title={`${t('formgen.pagination.page', 'Page')} ${i}`}
>
{i}
</button>
);
}
// Show ellipsis at end if we're not showing last page
if (endPage < totalPages) {
pages.push(
<span key="end-ellipsis" className={styles.pageEllipsis}>...</span>
);
}
return pages;
})()}
</div>
<button
onClick={() => onPageChange(currentPage + 1)}
disabled={currentPage >= totalPages}
className={styles.paginationButton}
title={t('formgen.pagination.next')}
>
»
</button>
<button
onClick={() => onPageChange(totalPages)}
disabled={currentPage >= totalPages}
className={styles.paginationButton}
title={t('formgen.pagination.last')}
>
»»
</button>
{/* Total items count */}
<span className={styles.paginationInfo}>
({hookData?.pagination?.totalItems != null
? hookData.pagination.totalItems.toString()
: (loading ? '...' : displayData.length.toString())} {t('formgen.pagination.items', 'items')})
</span>
</div>
)}
</div>
)}
</div>

View file

@ -74,6 +74,7 @@
font-size: 14px;
background: var(--color-bg);
table-layout: fixed;
word-wrap: break-word;
}
/* Disabled user row styling */
@ -104,9 +105,13 @@
text-align: left;
font-weight: 400;
color: var(--color-text);
white-space: nowrap;
white-space: normal;
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-word;
user-select: none;
z-index: 10;
overflow: visible;
}
.th.actionsColumn {
@ -286,6 +291,11 @@
border-top: 1px solid var(--color-primary);
color: var(--color-text);
vertical-align: middle;
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-word;
white-space: normal;
overflow: visible;
}

View file

@ -1150,116 +1150,19 @@ export function FormGeneratorTable<T extends Record<string, any>>({
selectable={selectable}
loading={loading}
activeFiltersCount={activeFiltersCount}
pagination={pagination}
currentPage={currentPage}
totalPages={totalPages}
currentPageSize={currentPageSize}
pageSizeOptions={pageSizeOptions}
showPageSizeSelector={showPageSizeSelector}
onPageChange={setCurrentPage}
onPageSizeChange={handlePageSizeChange}
supportsBackendPagination={supportsBackendPagination}
hookData={hookData}
/>
)}
{/* Pagination - Above Table */}
{pagination && (
<div className={styles.pagination}>
{showPageSizeSelector && (
<div className={styles.pageSizeSelector}>
<label htmlFor="pageSize">{t('formgen.pagination.pageSize', 'Items per page:')}</label>
<select
id="pageSize"
value={currentPageSize}
onChange={(e) => handlePageSizeChange(Number(e.target.value))}
className={styles.pageSizeSelect}
>
{pageSizeOptions.map(size => (
<option key={size} value={size}>{size}</option>
))}
</select>
</div>
)}
{pagination && supportsBackendPagination && (
<>
<button
onClick={() => setCurrentPage(1)}
disabled={currentPage === 1}
className={styles.paginationButton}
title={t('formgen.pagination.first')}
>
««
</button>
<button
onClick={() => setCurrentPage(currentPage - 1)}
disabled={currentPage === 1}
className={styles.paginationButton}
title={t('formgen.pagination.prev')}
>
«
</button>
{/* Page number buttons - show up to 100 pages before and after current */}
<div className={styles.pageNumbers}>
{(() => {
const maxPagesVisible = 100; // Max pages to show on each side
const startPage = Math.max(1, currentPage - maxPagesVisible);
const endPage = Math.min(totalPages, currentPage + maxPagesVisible);
const pages: React.ReactNode[] = [];
// Show ellipsis at start if we're not showing page 1
if (startPage > 1) {
pages.push(
<span key="start-ellipsis" className={styles.pageEllipsis}>...</span>
);
}
// Generate page buttons
for (let i = startPage; i <= endPage; i++) {
pages.push(
<button
key={i}
onClick={() => setCurrentPage(i)}
disabled={i === currentPage}
className={`${styles.pageNumber} ${i === currentPage ? styles.pageNumberActive : ''}`}
title={`${t('formgen.pagination.page', 'Page')} ${i}`}
>
{i}
</button>
);
}
// Show ellipsis at end if we're not showing last page
if (endPage < totalPages) {
pages.push(
<span key="end-ellipsis" className={styles.pageEllipsis}>...</span>
);
}
return pages;
})()}
</div>
<button
onClick={() => setCurrentPage(currentPage + 1)}
disabled={currentPage >= totalPages}
className={styles.paginationButton}
title={t('formgen.pagination.next')}
>
»
</button>
<button
onClick={() => setCurrentPage(totalPages)}
disabled={currentPage >= totalPages}
className={styles.paginationButton}
title={t('formgen.pagination.last')}
>
»»
</button>
{/* Total items count */}
<span className={styles.paginationInfo}>
({hookData?.pagination?.totalItems != null
? hookData.pagination.totalItems.toString()
: (loading ? '...' : displayData.length.toString())} {t('formgen.pagination.items', 'items')})
</span>
</>
)}
</div>
)}
{/* Table */}
<div className={`${styles.tableContainer} ${displayData.length === 0 && !loading ? styles.emptyTable : ''}`}>
{/* Loading overlay - shown while loading */}