diff --git a/client/src/components/layout-form-row/layout-form-row.component.jsx b/client/src/components/layout-form-row/layout-form-row.component.jsx
index 16e338d5e..e756d272c 100644
--- a/client/src/components/layout-form-row/layout-form-row.component.jsx
+++ b/client/src/components/layout-form-row/layout-form-row.component.jsx
@@ -1,59 +1,137 @@
-import { Col, Divider, Row } from "antd";
+import { Card, Col, Row } from "antd";
+import { Children, isValidElement } from "react";
import "./layout-form-row.styles.scss";
-export default function LayoutFormRow({ header, children, grow = false, noDivider = false, ...restProps }) {
- const DividerHeader = () =>
- !noDivider && (
-
- {header}
-
- );
+export default function LayoutFormRow({
+ header,
+ children,
+ grow = false,
+ noDivider = false,
+ gutter = [16, 16],
+ rowProps,
- if (!children.length) {
- //We have only one element. It's going to get the whole thing.
+ // Optional overrides if you ever need per-section customization
+ surface = true,
+ surfaceBg,
+ surfaceHeaderBg,
+
+ ...cardProps
+}) {
+ const items = Children.toArray(children).filter(Boolean);
+ if (items.length === 0) return null;
+
+ const title = !noDivider && header ? header : undefined;
+
+ const bg = surfaceBg ?? (surface ? "var(--imex-form-surface)" : undefined);
+ const headBg = surfaceHeaderBg ?? (surface ? "var(--imex-form-surface-head)" : undefined);
+
+ const mergedStyles = mergeSemanticStyles(
+ {
+ header: {
+ paddingInline: 16,
+ background: headBg
+ },
+ body: {
+ padding: 16,
+ background: bg
+ }
+ },
+ cardProps.styles
+ );
+
+ const baseCardStyle = {
+ marginBottom: ".8rem",
+ ...(bg ? { background: bg } : null), // ensures the “circled area” is tinted
+ ...cardProps.style
+ };
+
+ // single child => just render it
+ if (items.length === 1) {
return (
-
+
+ {items[0]}
+
);
}
- const rowGutter = { gutter: [16, 16] };
- const colSpan = (spanOverride) => {
- if (spanOverride) return { span: spanOverride };
- return {
- xs: {
- span: !grow ? 24 : Math.max(12, 24 / children.length)
- },
- sm: {
- span: !grow ? 12 : Math.max(12, 24 / children.length)
- },
- md: {
- span: !grow ? 8 : Math.max(8, 24 / children.length)
- },
- lg: {
- span: !grow ? 6 : Math.max(6, 24 / children.length)
- },
- xl: {
- span: !grow ? 4 : Math.max(4, 24 / children.length)
- }
- };
+ const count = items.length;
+ const spanFor = (min) => Math.max(min, Math.floor(24 / count));
+
+ // Mobile-first: stack on xs
+ const baseCol = {
+ xs: { span: 24 },
+ sm: { span: grow ? spanFor(12) : 12 },
+ md: { span: grow ? spanFor(8) : 8 },
+ lg: { span: grow ? spanFor(6) : 6 },
+ xl: { span: grow ? spanFor(4) : 4 },
+ xxl: { span: grow ? spanFor(4) : 4 }
};
- //{header ? {header} : null}
+
+ const getColPropsForChild = (child) => {
+ if (!isValidElement(child)) return baseCol;
+
+ // Back-compat with old pattern: 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;
+
+ // Optional explicit override:
+ const colOverride = child.props?.col;
+ if (colOverride && typeof colOverride === "object") {
+ return { ...baseCol, ...colOverride };
+ }
+
+ return baseCol;
+ };
+
return (
-
-
-
- {children.map(
- (c, idx) =>
- c && (
-
- {c}
-
- )
- )}
+
+
+ {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,
+ header: { ...defaults.header, ...(computed.header || {}) },
+ body: { ...defaults.body, ...(computed.body || {}) }
+ };
+ };
+ }
+
+ return {
+ ...defaults,
+ ...userStyles,
+ header: { ...defaults.header, ...(userStyles.header || {}) },
+ body: { ...defaults.body, ...(userStyles.body || {}) }
+ };
+}
diff --git a/client/src/components/layout-form-row/layout-form-row.styles.scss b/client/src/components/layout-form-row/layout-form-row.styles.scss
index faf24ccf1..4cedb9b79 100644
--- a/client/src/components/layout-form-row/layout-form-row.styles.scss
+++ b/client/src/components/layout-form-row/layout-form-row.styles.scss
@@ -1,20 +1,64 @@
-//Override Antd Row margin to save space on forms.
+/* layout-form-row.styles.scss */
+
+/**
+ * Surface tokens (light/dark)
+ * - --imex-form-surface: the main section background (circled area)
+ * - --imex-form-surface-head: slightly different strip for the Card header
+ * - --imex-form-surface-border: border color for the card
+ *
+ * Update the dark selector(s) to match whatever you use (body class, data-theme, etc).
+ */
+
+:root {
+ --imex-form-surface: #fafafa; /* subtle contrast vs white page */
+ --imex-form-surface-head: #f5f5f5; /* header strip */
+ --imex-form-surface-border: #d9d9d9; /* matches AntD-ish border */
+}
+
+/* Pick the selector that matches your app and remove the rest */
+html[data-theme="dark"] {
+ --imex-form-surface: rgba(255, 255, 255, 0.01); /* subtle lift off page bg */
+ --imex-form-surface-head: rgba(255, 255, 255, 0.06); /* slightly stronger for header strip */
+ --imex-form-surface-border: rgba(5, 5, 5, 0.12);
+}
+
.imex-form-row {
- .ant-row {
- margin-bottom: 0rem;
+ width: 100%;
+
+ /* Match old Divider title typography */
+ .ant-card-head-title {
+ font-weight: 500;
+ font-size: var(--ant-font-size-lg);
+ line-height: 1.2; /* optional, makes it feel like a section header */
+ }
- .ant-form-item-label {
- padding: 0rem;
+ /* Make the whole section read as its own surface */
+ &.ant-card {
+ background: var(--imex-form-surface);
+ border-color: var(--imex-form-surface-border);
+ }
+
+ .ant-card-head {
+ background: var(--imex-form-surface-head);
+ border-bottom-color: var(--imex-form-surface-border);
+ }
+
+ .ant-card-body {
+ background: var(--imex-form-surface);
+ }
+
+ /* Optional: slightly tighter on phones */
+ @media (max-width: 575px) {
+ .ant-card-head {
+ padding-inline: 12px;
}
-
- label {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- width: 100%;
- display: inline-block;
- padding: 0rem;
- margin: 0rem;
+ .ant-card-body {
+ padding: 12px;
}
}
+
+ /* Common form nicety */
+ .ant-col > * {
+ width: 100%;
+ }
}