diff --git a/src/components/UserProfile/UserProfile.css b/src/components/UserProfile/UserProfile.css index c99d78e..3e088bc 100644 --- a/src/components/UserProfile/UserProfile.css +++ b/src/components/UserProfile/UserProfile.css @@ -2,627 +2,707 @@ /* Container for the profile page */ .user-profile { - margin: auto; - max-width: 1200px; - margin-top: 40px; - } + margin: auto; + max-width: 1200px; + margin-top: 40px; +} - .user-profile h1 { - margin: 0px; - } - - /* Media query for responsiveness (optional, handled by react-grid-layout) */ - @media (max-width: 1200px) { - .layout { - width: 100%; - } - } +.user-profile h1 { + margin: 0px; +} - .user-profile { - margin: auto; - max-width: 1200px; - opacity: 0; /* Initially hidden */ - transform: translateY(20px); - transition: opacity 1s ease, transform 1s ease; - } - - .user-profile.fade-in { - opacity: 1; - transform: translateY(0); - } - - .user-profile.hidden { - display: none; - } - - .user-profile.loading-container { - opacity: 1; - transition: opacity 1s ease; - } - - .user-profile.loading-container.fade-out { - opacity: 0; - pointer-events: none; - } - - .user-profile-header { - text-align: center; +/* Media query for responsiveness (optional, handled by react-grid-layout) */ +@media (max-width: 1200px) { + .layout { + width: 100%; } +} - .react-grid-item.react-grid-placeholder { - background: #3B9AF8 !important; - border-radius: 8px; - opacity: 0.2; - transition-duration: 100ms; - z-index: 2; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -o-user-select: none; - user-select: none; - } +.user-profile { + margin: auto; + max-width: 1200px; + opacity: 0; /* Initially hidden */ + transform: translateY(20px); + transition: opacity 1s ease, transform 1s ease; +} - .react-grid-item.react-grid-placeholder.placeholder-resizing { - transition: none; - } +.user-profile.fade-in { + opacity: 1; + transform: translateY(0); +} - .react-grid-layout { - margin-bottom: 40px; - } +.user-profile.hidden { + display: none; +} - /* src/components/UserProfile/UserProfile.css */ +.user-profile.loading-container { + opacity: 1; + transition: opacity 1s ease; +} - .user-profile-header { - display: inline-grid; - column-gap: 50px; - row-gap: 22px; - justify-content: center; - text-align: center; - grid-auto-flow: column; - margin-left: 36.5px; - } +.user-profile.loading-container.fade-out { + opacity: 0; + pointer-events: none; +} + +.user-profile-header { + text-align: center; +} + +.react-grid-item.react-grid-placeholder { + background: #3B9AF8 !important; + border-radius: 8px; + opacity: 0.2; + transition-duration: 100ms; + z-index: 2; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} + +.react-grid-item.react-grid-placeholder.placeholder-resizing { + transition: none; +} + +.react-grid-layout { + margin-bottom: 40px; +} + +/* src/components/UserProfile/UserProfile.css */ + +.user-profile-header { + display: inline-grid; + column-gap: 50px; + row-gap: 22px; + justify-content: center; + text-align: center; + grid-auto-flow: column; + margin-left: 36.5px; +} .toggle-button { - padding: 10px 20px; - margin: 0 5px; - border: none; - background-color: #a9a9a9; - cursor: pointer; - border-radius: 6px; - transition: background-color 0.3s; +padding: 10px 20px; +margin: 0 5px; +border: none; +background-color: #a9a9a9; +cursor: pointer; +border-radius: 6px; +transition: background-color 0.3s; } .toggle-button.active { - background-color: #3B9AF8; - color: white; +background-color: #3B9AF8; +color: white; } .toggle-button:hover { - background-color: #ccc; +background-color: #ccc; } .user-profile-header p { - margin-bottom: 0px; - margin-top: 0px; +margin-bottom: 0px; +margin-top: 0px; } .user-profile-header h2 { - font-size: 1.2em; - margin: 0px; +font-size: 1.2em; +margin: 0px; } .user-profile-header h1 { - margin-bottom: 0px; - line-height: 0.9em; +margin-bottom: 0px; +line-height: 0.9em; } .text-center.font-semibold.mt-2 { - font-size: 1.2em; - font-weight: 600; - color: var(--text); - margin-top: -10.5px; - margin-left: 11.5px; - display: flex; - gap: 8.5px; +font-size: 1.2em; +font-weight: 600; +color: var(--text); +margin-top: -10.5px; +margin-left: 11.5px; +display: flex; +gap: 8.5px; } .user-profile-header-main { - display: flex; - flex-direction: column; - gap: 20px; - align-content: center; - justify-content: center; +display: flex; +flex-direction: column; +gap: 20px; +align-content: center; +justify-content: center; } .user-profile-data { - display: flex; - flex-direction: column; +display: flex; +flex-direction: column; } .user-profile-data { - justify-content: center; - gap: 35px; +justify-content: center; +gap: 35px; } .circular-badge { - margin-bottom: -10px; - margin-top: -10px; +margin-bottom: -10px; +margin-top: -10px; } .user-profile-name { - gap: 7px; - display: flex; - flex-direction: column; +gap: 7px; +display: flex; +flex-direction: column; } .loading-container-1 { - min-height: 0 !important; - margin-bottom: 50px; - margin-top: 30px; +min-height: 0 !important; +margin-bottom: 50px; +margin-top: 30px; } +/* Make grid items responsive and flexible */ .grid-item { - overflow: clip; +overflow: hidden; +transition: all 0.3s ease; +display: flex; +flex-direction: column; } .react-grid-item { - transition: all 200ms ease; - transition-property: left, top; +transition: all 200ms ease; +transition-property: left, top, width, height; } .narrative-card p:first-letter { - text-transform: capitalize; +text-transform: capitalize; } .user-profile-badges h3 { - margin: 0px; - font-size: 0.8em; - font-weight: 500; - margin-bottom: 4px; +margin: 0px; +font-size: 0.8em; +font-weight: 500; +margin-bottom: 4px; } .circular-badge { - position: relative; - display: inline-block; - grid-row: 1; - grid-column: 1; - margin-left: -9px; - margin-right: -18px; +position: relative; +display: inline-block; +grid-row: 1; +grid-column: 1; +margin-left: -9px; +margin-right: -18px; } /* Light mode styles */ .circular-logo-text { - fill: var(--circular-logo-text, #004f84); - transition: fill 0.3s ease; +fill: var(--circular-logo-text, #004f84); +transition: fill 0.3s ease; } .user-profile .recharts-responsive-container { - max-width: 100%; +max-width: 100%; } .share-button-container { - align-content: center; - grid-column: 3; - grid-row: 2; - display: flex; - flex-direction: column; - gap: 5px; - font-size: 0.8em; +align-content: center; +grid-column: 3; +grid-row: 2; +display: flex; +flex-direction: column; +gap: 5px; +font-size: 0.8em; } .toggle-switch { - display: none; +display: none; } .user-profile-activity { - text-transform: capitalize; - align-content: center; - justify-content: center; - display: flex; - flex-direction: column; - padding: 8px 0px; +text-transform: capitalize; +align-content: center; +justify-content: center; +display: flex; +flex-direction: column; +padding: 8px 0px; } tspan { - font-family: articulat-cf; +font-family: articulat-cf; } .dark-mode tspan { - fill: white; - opacity: 0.5; +fill: white; +opacity: 0.5; } .user-profile-score { - align-content: center; - justify-content: center; - display: flex; - flex-direction: column; +align-content: center; +justify-content: center; +display: flex; +flex-direction: column; } .user-profile-data-group { - display: flex; - flex-direction: column; - justify-content: center; - grid-row: 2; - grid-column: 2; - font-size: 0.8em; +display: flex; +flex-direction: column; +justify-content: center; +grid-row: 2; +grid-column: 2; +font-size: 0.8em; } .dark-mode rect { - stroke: #2c2c2c !important; - stroke-width: 5px !important; +stroke: #2c2c2c !important; +stroke-width: 5px !important; } .recharts-surface { - stroke-width: 0.3px; +stroke-width: 0.3px; } .recharts-cartesian-axis-ticks { - font-size: 0.7em; +font-size: 0.7em; } .text-sm.text-gray-500.text-center.mt-4 { - text-align: center; - padding-top: 10px; - font-size: 0.8em; - opacity: 0.5; +text-align: center; +padding-top: 10px; +font-size: 0.8em; +opacity: 0.5; } .user-profile-header-rechart { - align-items: center; - display: flex; - grid-row: 1; - justify-content: center; - margin: 0 auto; - max-height: 130px; - max-width: 200px; - padding: 1rem; - width: 100%; - grid-column: 3; - scale: 0.9; +align-items: center; +display: flex; +grid-row: 1; +justify-content: center; +margin: 0 auto; +max-height: 130px; +max-width: 200px; +padding: 1rem; +width: 100%; +grid-column: 3; +scale: 0.9; } .score-gauge { - margin-right: 10px !important; +margin-right: 10px !important; } .user-profile-main { - display: flex; - flex-direction: column; - grid-row: 1; - justify-content: space-evenly; - padding-top: 5px; - grid-column: 2; - max-width: 240px; +display: flex; +flex-direction: column; +grid-row: 1; +justify-content: space-evenly; +padding-top: 5px; +grid-column: 2; +max-width: 240px; } .user-profile-container { - text-align: center; - margin: 55px 20px 40px; +text-align: center; +margin: 55px 20px 40px; } .user-profile-age h2 { - font-size: 0.8em; +font-size: 0.8em; } .compare-button-container { - align-content: center; - grid-row: 3; +align-content: center; +grid-row: 3; } .max-score { - opacity: 0.5; +opacity: 0.5; } .comparea-button-profile { - background: gray; +background: gray; } .user-profile-emoji-group { - display: inline-grid; - font-size: 1.8em; - align-content: center; - justify-content: center; - row-gap: 10px; - column-gap: 20px; - margin-left: 10px; - grid-column: 1; - grid-row: 2; +display: inline-grid; +font-size: 1.8em; +align-content: center; +justify-content: center; +row-gap: 10px; +column-gap: 20px; +margin-left: 10px; +grid-column: 1; +grid-row: 2; } .emoji-1 { - grid-column: 1; - grid-row: 1; +grid-column: 1; +grid-row: 1; } .emoji-2 { - grid-column: 2; - grid-row: 1; +grid-column: 2; +grid-row: 1; } .emoji-3 { - grid-column: 1; - grid-row: 2; +grid-column: 1; +grid-row: 2; } .emoji-4 { - grid-column: 2; - grid-row: 2; +grid-column: 2; +grid-row: 2; } .narrative-card { - font-size: 0.9em; +font-size: 0.9em; } .narrative-disclaimer { - font-size: 0.8em; - margin-top: 13.5px; - opacity: 0.5; +font-size: 0.8em; +margin-top: 13.5px; +opacity: 0.5; } .post-type-card .recharts-surface { - margin-left: -15px; +margin-left: -15px; } .score-breakdown-card .disclaimer { - margin-left: 3px; +margin-left: 3px; } .user-profile-container { - margin: auto; - margin-top: auto; - margin-bottom: auto; - width: 100%; - margin-top: 60px; - margin-bottom: 60px; +margin: auto; + margin-top: auto; + margin-bottom: auto; +width: 100%; +margin-top: 60px; +margin-bottom: 60px; } .profile-sections-wrapper { - display: grid; - grid-template-columns: 1fr 2fr 1fr; - gap: 20px; - align-items: start; - max-width: 1200px; - margin: 0 auto; +display: grid; +grid-template-columns: 1fr 2fr 1fr; +gap: 20px; +align-items: start; +max-width: 1200px; +margin: 0 auto; } /* Left Section */ .profile-section.left-section { - justify-self: start; - margin-right: 31.1px; - margin-top: 15.3px; +justify-self: start; +margin-right: 31.1px; +margin-top: 15.3px; } /* Middle Section */ .profile-section.middle-section { - display: flex; - flex-direction: column; - align-items: center; - gap: 17px; - justify-self: center; - margin-right: -31.3px; - margin-top: 11.5px; +display: flex; +flex-direction: column; +align-items: center; +gap: 17px; +justify-self: center; +margin-right: -31.3px; +margin-top: 11.5px; } .user-profile-header-rechart { - margin: 0 auto; - max-width: 200px; - width: 100%; - scale: 1.07; - margin-top: 4px; +margin: 0 auto; +max-width: 200px; +width: 100%; +scale: 1.07; +margin-top: 4px; } .user-profile-data-group { - text-align: center; - width: 100%; - max-width: 300px; - gap: 10px; +text-align: center; +width: 100%; +max-width: 300px; +gap: 10px; } .share-button-container { - width: 100%; - text-align: center; - display: block; +width: 100%; +text-align: center; +display: block; } .comparea-button-profile { - margin-top: 10px; +margin-top: 10px; } /* Right Section */ .profile-section.right-section { - justify-self: end; - padding-left: 32.6px; - scale: 0.9; +justify-self: end; +padding-left: 32.6px; +scale: 0.9; } .user-profile-main { - text-align: center; - gap: 22.5px; +text-align: center; +gap: 22.5px; } .user-profile-activity { - align-content: center; - display: flex; - flex-direction: column; - justify-content: center; - padding: 0px; - text-transform: capitalize; +align-content: center; +display: flex; +flex-direction: column; +justify-content: center; +padding: 0px; +text-transform: capitalize; } .user-profile-badges { - font-size: 1.2em; - line-height: 1.2em; +font-size: 1.2em; +line-height: 1.2em; } .context-line p { - margin: 0px; - font-size: 0.7em; - opacity: 0.5; +margin: 0px; +font-size: 0.7em; +opacity: 0.5; } .bluesky-link { - font-size: 0.8em; - text-decoration: underline; +font-size: 0.8em; +text-decoration: underline; } .user-profile-data-group p { - margin: 2px; +margin: 2px; } .user-profile-name h2 { - font-size: 1em; - margin: 0px; +font-size: 1em; +margin: 0px; } .user-profile-age h2 { - font-size: .8em; - margin: 0px; +font-size: .8em; +margin: 0px; } .circular-badge { - margin: 0px +margin: 0px } .card .disclaimer { - margin: 0px; - margin-top: 0px; - margin-top: 12.3px; - max-width: 400px; +margin: 0px; + margin-top: 0px; +margin-top: 12.3px; +max-width: 400px; } .learn-more-text { - font-size: 0.9em +font-size: 0.9em } .score-breakdown-container .disclaimer { - margin-left: 3px; +margin-left: 3px; +} + +/* Make card component fill grid item */ +.grid-item > div { +height: 100%; +width: 100%; +display: flex; +flex-direction: column; +} + +/* Make card content expand to fill available space */ +.card-content { +flex: 1; +overflow: auto; +} + +/* Style for resize handle */ +.react-resizable-handle { +background-color: rgba(0, 0, 0, 0.1); +border-radius: 0 0 5px 0; +z-index: 10; +} + +.react-resizable-handle:hover { +background-color: rgba(0, 0, 0, 0.2); +} + +/* Custom styles for the Card component */ +.card { +display: flex; +flex-direction: column; +height: 100%; +box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +border-radius: 8px; +background-color: white; +overflow: hidden; +} + +.card-header { +padding: 12px 16px; +background-color: #f8f9fa; +border-bottom: 1px solid #eaeaea; +cursor: move; +display: flex; +justify-content: space-between; +align-items: center; +} + +.card-content { +padding: 16px; +flex: 1; +overflow: auto; } /* Responsive Adjustments for Cards */ @media (max-width: 835px) { - .user-profile-container { - margin: 30px auto; - margin-top: 30px; - width: 100%; - margin-top: 40px; - } +.user-profile-container { + margin: 30px auto; + margin-top: 30px; + width: 100%; + margin-top: 40px; +} - .profile-sections-wrapper { - grid-template-columns: 1fr; - grid-template-rows: auto auto auto; - gap: 27px; - } +.profile-sections-wrapper { + grid-template-columns: 1fr; + grid-template-rows: auto auto auto; + gap: 27px; +} - .profile-section.left-section { - justify-self: center; - padding-left: 0; - scale: .7; - grid-column: 1; - grid-row: 1; - } +.profile-section.left-section { + justify-self: center; + padding-left: 0; + scale: .7; + grid-column: 1; + grid-row: 1; +} - .profile-section.left-section { - margin-right: 0px; - } +.profile-section.left-section { + margin-right: 0px; +} - .profile-section.right-section { - justify-self: center; - padding-left: 0; - grid-column: 1; - grid-row: 3; - margin-top: -10px; - } +.profile-section.right-section { + justify-self: center; + padding-left: 0; + grid-column: 1; + grid-row: 3; + margin-top: -10px; +} - .user-profile-main { - text-align: center; - } +.user-profile-main { + text-align: center; +} - .circular-badge { - grid-column: 1; - grid-row: 1; - margin-left: 0; - margin-right: 1.5px; - } - .user-profile-emoji-group { - grid-row: 1; - grid-column: 1; - display: none; - } - .user-profile-main { - grid-row: 2; - grid-column: 1; - gap: 20.8px; - } - .user-profile-header-rechart { - grid-row: 3; - grid-column: 1; - scale: 0.9; - margin-bottom: 5px; - } +.circular-badge { + grid-column: 1; + grid-row: 1; + margin-left: 0; + margin-right: 1.5px; +} +.user-profile-emoji-group { + grid-row: 1; + grid-column: 1; + display: none; +} +.user-profile-main { + grid-row: 2; + grid-column: 1; + gap: 20.8px; +} +.user-profile-header-rechart { + grid-row: 3; + grid-column: 1; + scale: 0.9; + margin-bottom: 5px; +} - .user-profile-data-group { - grid-row: 4; - grid-column: 1; - } +.user-profile-data-group { + grid-row: 4; + grid-column: 1; +} - .share-button-container { - gap: 10px; - } +.share-button-container { + gap: 10px; +} - .user-profile-score { - padding: 0px; - } +.user-profile-score { + padding: 0px; +} - .share-button-container { - grid-row: 5; - grid-column: 1; - text-align: center; - } +.share-button-container { + grid-row: 5; + grid-column: 1; + text-align: center; +} - .user-profile-header { - margin-left: 0px; - row-gap: 10px; - } - .user-profile-data-group { - text-align: center; - } +.user-profile-header { + margin-left: 0px; + row-gap: 10px; +} +.user-profile-data-group { + text-align: center; +} - .share-button-container { - text-align: center; - } +.share-button-container { + text-align: center; +} - .profile-section.left-section { - scale: 0.6; - margin-top: -64.5px; - } +.profile-section.left-section { + scale: 0.6; + margin-top: -64.5px; +} - .profile-section.middle-section { - margin-top: -85.7px; - gap: 13.5px; - grid-column: 1; - grid-row: 2; - margin-right: 0px; - } +.profile-section.middle-section { + margin-top: -85.7px; + gap: 13.5px; + grid-column: 1; + grid-row: 2; + margin-right: 0px; +} - .user-profile-badges { - margin-top: 0px; - } +.user-profile-badges { + margin-top: 0px; +} - .text-center.font-semibold.mt-2 { - font-size: 1.5em; - } +.text-center.font-semibold.mt-2 { + font-size: 1.5em; +} +.react-grid-layout { + margin: 10px; +} + +.react-grid-item { + margin-bottom: 15px; +} } @media (max-width: 440px) { - .score-breakdown-card .disclaimer { - font-size: .7em !important; - margin: 0px; - margin-top: 0px; - max-width: 300px; - } +.score-breakdown-card .disclaimer { + font-size: .7em !important; + margin: 0px; + margin-top: 0px; + max-width: 300px; +} + +.react-resizable-handle { + width: 20px; + height: 20px; +} + +/* Make the resizing handle more touch-friendly on small screens */ +.react-resizable-handle::after { + content: ''; + position: absolute; + width: 20px; + height: 20px; + bottom: 0; + right: 0; + background-color: rgba(0, 0, 0, 0.1); + border-radius: 0 0 5px 0; +} } \ No newline at end of file diff --git a/src/components/UserProfile/UserProfile.js b/src/components/UserProfile/UserProfile.js index fcecb3a..c82200d 100644 --- a/src/components/UserProfile/UserProfile.js +++ b/src/components/UserProfile/UserProfile.js @@ -58,26 +58,23 @@ const createLayouts = () => ({ ], xs: [ { i: "ScoreBreakdownCard", x: 0, y: 8, w: 1, h: 6, static: false }, - { i: "NarrativeCard", x: 1, y: 0, w: 1, h: 6, static: false }, + { i: "NarrativeCard", x: 0, y: 0, w: 1, h: 6, static: false }, { i: "ProfileCard", x: 0, y: 14, w: 1, h: 6, static: false }, - { i: "PostTypeCard", x: 1, y: 22, w: 1, h: 6, static: false }, + { i: "PostTypeCard", x: 0, y: 22, w: 1, h: 6, static: false }, { i: "AltTextCard", x: 0, y: 26, w: 1, h: 6, static: false }, - { i: "ActivityCard", x: 1, y: 30, w: 1, h: 6, static: false } + { i: "ActivityCard", x: 0, y: 30, w: 1, h: 6, static: false } ], xxs: [ { i: "ScoreBreakdownCard", x: 0, y: 8, w: 1, h: 6, static: false }, - { i: "NarrativeCard", x: 1, y: 0, w: 1, h: 8, static: false }, + { i: "NarrativeCard", x: 0, y: 0, w: 1, h: 8, static: false }, { i: "ProfileCard", x: 0, y: 14, w: 1, h: 7, static: false }, - { i: "PostTypeCard", x: 1, y: 22, w: 1, h: 6, static: false }, + { i: "PostTypeCard", x: 0, y: 22, w: 1, h: 6, static: false }, { i: "AltTextCard", x: 0, y: 26, w: 1, h: 6, static: false }, - { i: "ActivityCard", x: 1, y: 30, w: 1, h: 6, static: false } + { i: "ActivityCard", x: 0, y: 30, w: 1, h: 6, static: false } ] }); -const layouts = createLayouts(); - // Memoized save function with debouncing -// Update createDebouncedSave function in UserProfile.js const createDebouncedSave = () => { let timeout; return async (userData) => { @@ -201,25 +198,116 @@ const UserProfile = () => { const [showContent, setShowContent] = useState(false); const cardRefs = useRef({}); const [cardHeights, setCardHeights] = useState({}); + + // Add state for saving and restoring layouts + const [currentLayouts, setCurrentLayouts] = useState(() => { + // Try to load saved layouts from localStorage + const savedLayouts = localStorage.getItem('userProfileLayouts'); + return savedLayouts ? JSON.parse(savedLayouts) : createLayouts(); + }); + + // Function to handle layout changes + const handleLayoutChange = (layout, allLayouts) => { + if (allLayouts) { + // Save to state + setCurrentLayouts(allLayouts); + + // Save to localStorage + localStorage.setItem('userProfileLayouts', JSON.stringify(allLayouts)); + } + + // Update card heights after layout changes + setTimeout(updateCardHeights, 100); + }; - // Memoize layout-related calculations + // Improved card height calculation const updateCardHeights = useMemo(() => { return () => { - const rowHeight = 50; + const rowHeight = 50; // Same as your rowHeight prop + const margin = 20; // Same as your margin prop const newHeights = {}; + const currentWidth = window.innerWidth; + + // Determine current breakpoint + let currentBreakpoint = 'lg'; + for (const [bp, width] of Object.entries(breakpoints)) { + if (currentWidth <= width) { + currentBreakpoint = bp; + } + } + + // Get the number of columns for the current breakpoint + const numCols = cols[currentBreakpoint]; + + // Adjust calculations based on available width + const containerWidth = document.querySelector('.user-profile')?.clientWidth || window.innerWidth; + const availableWidth = (containerWidth - (margin * (numCols + 1))) / numCols; Object.keys(cardRefs.current).forEach(key => { const element = cardRefs.current[key]; if (element) { - const contentHeight = element.scrollHeight; - const gridHeight = Math.ceil(contentHeight / rowHeight); - newHeights[key] = Math.max(gridHeight, 6); + // Get the content element (assuming each card has a .card-content) + const contentElement = element.querySelector('.card-content') || element; + + // Clone the element to measure its natural height without constraints + const clone = contentElement.cloneNode(true); + clone.style.position = 'absolute'; + clone.style.visibility = 'hidden'; + clone.style.width = `${availableWidth}px`; + clone.style.height = 'auto'; + document.body.appendChild(clone); + + // Measure natural height + const contentHeight = clone.scrollHeight; + + // Remove the clone + document.body.removeChild(clone); + + // Calculate grid height based on content + const gridHeight = Math.max(Math.ceil(contentHeight / rowHeight), 6); + + // Store the new height + newHeights[key] = gridHeight; } }); + // Update layout with new heights + const updatedLayouts = {}; + + for (const [bp, layout] of Object.entries(currentLayouts)) { + updatedLayouts[bp] = layout.map(item => { + if (newHeights[item.i]) { + return { ...item, h: newHeights[item.i] }; + } + return item; + }); + } + + // Only update if there are actual changes + if (!_.isEqual(updatedLayouts, currentLayouts)) { + setCurrentLayouts(updatedLayouts); + localStorage.setItem('userProfileLayouts', JSON.stringify(updatedLayouts)); + } + + // Update state with new heights setCardHeights(newHeights); }; - }, []); + }, [currentLayouts]); + + // Add a function to handle window resize events more effectively + const handleWindowResize = useMemo(() => { + return _.debounce(() => { + // Update card heights and force layout refresh + updateCardHeights(); + + // Force a layout recalculation + const gridLayoutElement = document.querySelector('.react-grid-layout'); + if (gridLayoutElement) { + // This forces a reflow + const temp = gridLayoutElement.offsetHeight; + } + }, 200); + }, [updateCardHeights]); // Optimized data fetching with DID support useEffect(() => { @@ -283,19 +371,36 @@ const UserProfile = () => { fetchAccountData(); }, [username, navigate, updateCardHeights]); - // Memoized resize handler + // Add effect to initialize the layout on first render useEffect(() => { - const handleResize = _.debounce(() => { + // Small delay to ensure DOM is ready + setTimeout(updateCardHeights, 300); + + // Add a resize observer to each card to handle content changes + const resizeObserver = new ResizeObserver(_.debounce(() => { updateCardHeights(); - }, 250); - - window.addEventListener('resize', handleResize); + }, 300)); + + Object.values(cardRefs.current).forEach(ref => { + if (ref) { + resizeObserver.observe(ref); + } + }); + return () => { - window.removeEventListener('resize', handleResize); - handleResize.cancel(); + resizeObserver.disconnect(); }; }, [updateCardHeights]); + // Memoized resize handler + useEffect(() => { + window.addEventListener('resize', handleWindowResize); + return () => { + window.removeEventListener('resize', handleWindowResize); + handleWindowResize.cancel(); + }; + }, [handleWindowResize]); + if (loading) { return (
@@ -396,16 +501,28 @@ const UserProfile = () => { updateCardHeights()} + onLayoutChange={handleLayoutChange} draggableHandle=".card-header" + resizeHandles={['se']} + compactType="vertical" + preventCollision={false} + autoSize={true} + onBreakpointChange={(newBreakpoint) => { + // Recalculate heights when breakpoint changes + setTimeout(updateCardHeights, 100); + }} + onWidthChange={(width, margin, cols) => { + // Recalculate heights when width changes + setTimeout(updateCardHeights, 100); + }} >
cardRefs.current.ScoreBreakdownCard = el}>