import { Card, Col, Row } from "antd"; import { Children, isValidElement } from "react"; import { INLINE_FORM_ROW_WRAP_TITLE_STYLES } from "./inline-form-row-title.utils.js"; import "./layout-form-row.styles.scss"; export default function LayoutFormRow({ header, children, grow = false, noDivider = false, titleOnly = false, wrapTitle = false, gutter, rowProps, // Optional overrides if you ever need per-section customization surface = true, surfaceBg, surfaceHeaderBg, surfaceBorderColor, ...cardProps }) { const items = Children.toArray(children).filter(Boolean); const isCompactRow = noDivider; const title = !noDivider && header ? header : undefined; const resolvedTitle = cardProps.title ?? title; const isHeaderOnly = titleOnly || items.length === 0; const hideBody = isHeaderOnly; if (items.length === 0 && !resolvedTitle) return null; const resolvedGutter = gutter ?? [16, isCompactRow ? 8 : 16]; const bg = surfaceBg ?? (surface ? "var(--imex-form-surface)" : undefined); const headBg = surfaceHeaderBg ?? (surface ? "var(--imex-form-surface-head)" : undefined); const borderColor = surfaceBorderColor ?? (surface ? "var(--imex-form-surface-border)" : undefined); const mergedStyles = mergeSemanticStyles( { ...(wrapTitle ? INLINE_FORM_ROW_WRAP_TITLE_STYLES : null), header: { paddingInline: isHeaderOnly ? 8 : isCompactRow ? 12 : 16, background: headBg, borderBottomColor: borderColor }, body: { padding: hideBody ? 0 : isCompactRow ? 12 : 16, display: hideBody ? "none" : undefined, background: bg } }, cardProps.styles ); const baseCardStyle = { marginBottom: isHeaderOnly ? "0" : isCompactRow ? "8px" : ".8rem", ...(bg ? { background: bg } : null), // ensures the “circled area” is tinted ...(borderColor ? { borderColor } : null), ...cardProps.style }; const count = items.length; // Modern responsive strategy leveraging Ant Design 6: // - xs (phone <576px): Always stack vertically // - sm (tablet 576-768px): 2 columns for better readability // - md (tablet 768-992px): 3 columns for tablets // - lg+ (desktop >992px): Dynamic flex-based columns that adapt to screen width // Target: 1366px → 4 cols, 1920px → 6 cols, 2560px → 8 cols, 4K → capped at 8-9 cols // Note: xxl uses higher min-width to naturally cap maximum columns const baseCol = (() => { if (grow) { // Grow mode: use flex with reasonable min-widths return { xs: 24, sm: 12, // Fixed 2 cols on small tablets md: 8, // Fixed 3 cols on large tablets lg: { flex: `1 1 320px` }, // Dynamic: ~3 cols at 1200px xl: { flex: `1 1 280px` }, // Dynamic: ~4 cols at 1366px, ~6 cols at 1920px xxl: { flex: `1 1 380px` } // Dynamic: ~6 cols at 2560px, ~9 cols at 4K (capped) }; } else { // Fixed mode: Use flex without grow to maintain uniform widths across rows // xxl uses larger min-width to cap maximum columns on 4K/ultrawide const minWidthLg = count <= 2 ? 500 : count === 3 ? 380 : 320; // 3 cols at 1200px const minWidthXl = count <= 2 ? 480 : count === 3 ? 360 : 280; // 4 cols at 1366px, ~6 at 1920px const minWidthXxl = count <= 2 ? 500 : count === 3 ? 400 : 380; // ~6 cols at 2560px, ~9 at 4K (natural cap) return { xs: 24, sm: 12, // Fixed 2 columns on tablet md: count === 1 ? 24 : count === 2 ? 12 : 8, // Fixed 1-3 cols on large tablet lg: count === 1 ? 24 : { flex: `0 0 ${minWidthLg}px` }, // Fixed width on desktop xl: count === 1 ? 24 : { flex: `0 0 ${minWidthXl}px` }, // Fixed width on large desktop xxl: count === 1 ? 24 : { flex: `0 0 ${minWidthXxl}px` } // Fixed width on ultrawide }; } })(); const getColPropsForChild = (child) => { if (!isValidElement(child)) return baseCol; // Back-compat: child.props.span can override Col sizing const spanOverride = child.props?.span; if (typeof spanOverride === "number") return { span: spanOverride }; if (spanOverride && typeof spanOverride === "object") return spanOverride; // Explicit override: const colOverride = child.props?.col; if (colOverride && typeof colOverride === "object") { // Deep merge: allow partial overrides while keeping baseCol defaults const merged = { ...baseCol }; Object.keys(colOverride).forEach((bp) => { merged[bp] = typeof colOverride[bp] === "object" ? { ...baseCol[bp], ...colOverride[bp] } : colOverride[bp]; }); return merged; } return baseCol; }; return ( {!isHeaderOnly && (items.length === 1 ? ( items[0] ) : ( {items.map((child, idx) => ( {child} ))} ))} ); } function mergeSemanticStyles(defaults, userStyles) { if (!userStyles) return defaults; if (typeof userStyles === "function") { return (info) => { const computed = userStyles(info) || {}; return { ...defaults, ...computed, title: { ...(defaults.title || {}), ...(computed.title || {}) }, header: { ...defaults.header, ...(computed.header || {}) }, body: { ...defaults.body, ...(computed.body || {}) } }; }; } return { ...defaults, ...userStyles, title: { ...(defaults.title || {}), ...(userStyles.title || {}) }, header: { ...defaults.header, ...(userStyles.header || {}) }, body: { ...defaults.body, ...(userStyles.body || {}) } }; }