From 3c634a90d0b381a5de864f34ef6d47c4b7c09538 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sat, 1 Aug 2026 00:05:10 +0100 Subject: [PATCH] Refactor ObjectCard and ObjectTable components for improved layout and responsiveness - Introduced a new method to dynamically calculate card column span based on container width in ObjectTable, enhancing responsiveness. - Updated ObjectCard to simplify rendering logic by extracting name and state properties into a separate component. - Added min-width: 0 to relevant CSS classes in App.css to ensure proper layout handling and prevent overflow issues. --- assets/stylesheets/App.css | 2 + .../Dashboard/common/ObjectCard.jsx | 49 ++++---- .../Dashboard/common/ObjectTable.jsx | 105 ++++++++++++++---- src/database/models/User.js | 26 +++-- 4 files changed, 128 insertions(+), 54 deletions(-) diff --git a/assets/stylesheets/App.css b/assets/stylesheets/App.css index 93e745d..2d2b8be 100644 --- a/assets/stylesheets/App.css +++ b/assets/stylesheets/App.css @@ -811,6 +811,7 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton { .farmcontrol-splitter.ant-splitter-horizontal .ant-splitter-panel { width: 100%; + min-width: 0; } .farmcontrol-splitter @@ -829,6 +830,7 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton { .objectTableCardsContainer { width: 100%; height: 100%; + min-width: 0; } .objectTableCardsContainer .ant-spin-nested-loading { diff --git a/src/components/Dashboard/common/ObjectCard.jsx b/src/components/Dashboard/common/ObjectCard.jsx index 35af1ef..8ebdb47 100644 --- a/src/components/Dashboard/common/ObjectCard.jsx +++ b/src/components/Dashboard/common/ObjectCard.jsx @@ -66,6 +66,31 @@ const ObjectCard = ({ ? descriptionItems.slice(2) : descriptionItems + const nameContent = ( + <> + + {modelIcon} + p.name === 'name')} + objectData={record} + isEditing={isEditing} + style={{ + fontSize: 20, + fontWeight: '600', + lineHeight: 1.2, + minWidth: 0 + }} + /> + {visibleColumns?.state == true && ( + p.name === 'state')} + objectData={record} + /> + )} + + + ) + return ( - {visibleColumns?.name == true && ( - - {modelIcon} - p.name === 'name')} - objectData={record} - isEditing={isEditing} - style={{ - fontSize: 20, - fontWeight: '600', - lineHeight: 1.2, - minWidth: 0 - }} - /> - {visibleColumns?.state == true && ( - p.name === 'state')} - objectData={record} - /> - )} - - )} + {visibleColumns?.name == true && nameContent} {primaryDescriptionItems.length > 0 && ( )} + {visibleColumns?.name == true && !thumbnailPresent && nameContent} {remainingDescriptionItems.length > 0 && ( { + if (containerWidth >= 2980) return 2 + if (containerWidth >= 1780) return 4 + if (containerWidth >= 1080) return 8 + if (containerWidth >= 680) return 12 + return 24 +} + const RowForm = ({ record, isEditing, onRegister, children }) => { const [form] = Form.useForm() useEffect(() => { @@ -1038,13 +1046,68 @@ const ObjectTable = forwardRef( } // Card view rendering - const cardsContainerRef = useRef(null) + const [cardColSpan, setCardColSpan] = useState(24) + const cardsContainerNodeRef = useRef(null) + const cardsResizeObserverRef = useRef(null) + + const measureCardColSpan = useCallback(() => { + const node = cardsContainerNodeRef.current + if (!node) return + const panel = node.closest('.ant-splitter-panel') + const width = (panel ?? node).getBoundingClientRect().width + if (width <= 0) return + const nextSpan = getCardColSpan(width) + setCardColSpan((prev) => (prev === nextSpan ? prev : nextSpan)) + }, []) + + const objectTableCardsContainerRef = useCallback( + (node) => { + cardsResizeObserverRef.current?.disconnect() + cardsResizeObserverRef.current = null + cardsContainerNodeRef.current = node + + if (!node) return + + let rafId = null + const scheduleMeasure = () => { + if (rafId != null) cancelAnimationFrame(rafId) + rafId = requestAnimationFrame(() => { + rafId = null + measureCardColSpan() + }) + } + + scheduleMeasure() + + const observer = new ResizeObserver(scheduleMeasure) + const panel = node.closest('.ant-splitter-panel') + if (panel) { + observer.observe(panel) + } else { + observer.observe(node) + } + + cardsResizeObserverRef.current = observer + }, + [measureCardColSpan] + ) + + useLayoutEffect(() => { + if (!cards) return + measureCardColSpan() + }, [cards, showFilterSidebar, measureCardColSpan]) + + useEffect(() => { + return () => cardsResizeObserverRef.current?.disconnect() + }, []) // Card view scroll handler useEffect(() => { if (!cards) return - const container = cardsContainerRef.current + const container = cardsContainerNodeRef.current if (!container) return + const scrollEl = container.querySelector('.simplebar-content-wrapper') + if (!scrollEl) return const handleCardsScroll = (e) => { const { scrollTop, scrollHeight, clientHeight } = e.target @@ -1056,18 +1119,18 @@ const ObjectTable = forwardRef( scrollHeight - scrollTop - clientHeight < 100 && !lazyLoading ) { - loadNextPage() + loadNextPage(scrollEl) } else if ( firstPage?.isSkeletonPage && scrollTop < 100 && !lazyLoading ) { - loadPreviousPage() + loadPreviousPage(scrollEl) } } - container.addEventListener('scroll', handleCardsScroll) - return () => container.removeEventListener('scroll', handleCardsScroll) + scrollEl.addEventListener('scroll', handleCardsScroll) + return () => scrollEl.removeEventListener('scroll', handleCardsScroll) }, [cards, pages, lazyLoading, loadNextPage, loadPreviousPage]) const renderCards = () => { @@ -1078,22 +1141,13 @@ const ObjectTable = forwardRef( }} >
- + {tableData.map((record) => { if (record?._id == undefined) { return null } return ( - +
- + + {cards ? ( -
+
} spinning={loading} diff --git a/src/database/models/User.js b/src/database/models/User.js index 99fc29c..208f290 100644 --- a/src/database/models/User.js +++ b/src/database/models/User.js @@ -68,6 +68,13 @@ export const User = { readOnly: true, columnWidth: 180 }, + { + name: 'updatedAt', + label: 'Updated At', + type: 'dateTime', + readOnly: true, + columnWidth: 175 + }, { name: 'name', label: 'Name', @@ -76,12 +83,13 @@ export const User = { type: 'text', columnWidth: 200 }, + { - name: 'updatedAt', - label: 'Updated At', - type: 'dateTime', - readOnly: true, - columnWidth: 175 + name: 'username', + label: 'Username', + required: true, + type: 'text', + columnWidth: 150 }, { @@ -90,13 +98,7 @@ export const User = { type: 'text', columnWidth: 130 }, - { - name: 'username', - label: 'Username', - required: true, - type: 'text', - columnWidth: 150 - }, + { name: 'lastName', label: 'Last Name',