From 48d2b0d15aef08f67242942a20bef85f395044ba Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Fri, 18 Sep 2026 15:34:56 +0100 Subject: [PATCH] Refine Dashboard Styles and Tab Functionality for Enhanced User Experience - Adjusted CSS styles for dashboard elements, including margin and positioning tweaks for improved layout and visual consistency. - Updated tab masking logic to enhance scrolling behavior and visual integration. - Removed unnecessary divider components to streamline the dashboard navigation structure. - Implemented new logic for handling tab scrolling, ensuring better responsiveness and user interaction. --- assets/stylesheets/App.css | 44 +++++++++----- .../Dashboard/common/DashboardNavigation.jsx | 17 +----- .../Dashboard/common/DashboardTabs.jsx | 57 ++++++++++++++++--- 3 files changed, 82 insertions(+), 36 deletions(-) diff --git a/assets/stylesheets/App.css b/assets/stylesheets/App.css index 73a10ebe..4172a1c5 100644 --- a/assets/stylesheets/App.css +++ b/assets/stylesheets/App.css @@ -175,13 +175,14 @@ position: relative; height: 42px; padding-top: 2px; + margin-left: 8px; } .dashboard-menu-container:after { border-bottom: 1px solid var(--color-header-border); content: ''; position: absolute; - left: 0; - right: -2px; + left: -8px; + right: 0px; bottom: 0; } @@ -3483,6 +3484,10 @@ body.objectKanbanColumnResizing * { min-width: 0; height: 100%; position: relative; + --tab-mask-left: 4px; + --tab-mask-right: 8px; + --tab-mask-fade-left: var(--tab-mask-fade); + --tab-mask-fade-right: var(--tab-mask-fade); } .dashboard-tabs-wrap .simplebar-content-wrapper { @@ -3490,10 +3495,6 @@ body.objectKanbanColumnResizing * { } .dashboard-tabs-wrap .simplebar-mask { - --tab-mask-left: 4px; - --tab-mask-right: 4px; - --tab-mask-fade-left: var(--tab-mask-fade); - --tab-mask-fade-right: var(--tab-mask-fade); -webkit-clip-path: inset(0 var(--tab-mask-right) 0 var(--tab-mask-left)); clip-path: inset(0 var(--tab-mask-right) 0 var(--tab-mask-left)); -webkit-mask-image: linear-gradient( @@ -3512,12 +3513,12 @@ body.objectKanbanColumnResizing * { ); } -.dashboard-tabs[data-at-start] .dashboard-tabs-wrap .simplebar-mask { +.dashboard-tabs[data-at-start] .dashboard-tabs-wrap { --tab-mask-left: 0px; --tab-mask-fade-left: 0px; } -.dashboard-tabs[data-at-end] .dashboard-tabs-wrap .simplebar-mask { +.dashboard-tabs[data-at-end] .dashboard-tabs-wrap { --tab-mask-right: 0px; --tab-mask-fade-right: 0px; } @@ -3527,7 +3528,6 @@ body.objectKanbanColumnResizing * { content: ''; position: absolute; bottom: 0; - width: var(--tab-mask-fade); height: 1px; pointer-events: none; z-index: 2; @@ -3536,14 +3536,32 @@ body.objectKanbanColumnResizing * { .dashboard-tabs-wrap::before { left: 0; - -webkit-mask-image: linear-gradient(to right, #000, transparent); - mask-image: linear-gradient(to right, #000, transparent); + width: calc(var(--tab-mask-left) + var(--tab-mask-fade-left)); + -webkit-mask-image: linear-gradient( + to right, + #000 var(--tab-mask-left), + transparent calc(var(--tab-mask-left) + var(--tab-mask-fade-left)) + ); + mask-image: linear-gradient( + to right, + #000 var(--tab-mask-left), + transparent calc(var(--tab-mask-left) + var(--tab-mask-fade-left)) + ); } .dashboard-tabs-wrap::after { right: 0.2px; - -webkit-mask-image: linear-gradient(to left, #000, transparent); - mask-image: linear-gradient(to left, #000, transparent); + width: calc(var(--tab-mask-right) + var(--tab-mask-fade-right)); + -webkit-mask-image: linear-gradient( + to left, + #000 var(--tab-mask-right), + transparent calc(var(--tab-mask-right) + var(--tab-mask-fade-right)) + ); + mask-image: linear-gradient( + to left, + #000 var(--tab-mask-right), + transparent calc(var(--tab-mask-right) + var(--tab-mask-fade-right)) + ); } .dashboard-tabs-wrap .simplebar-track.simplebar-vertical { diff --git a/src/components/Dashboard/common/DashboardNavigation.jsx b/src/components/Dashboard/common/DashboardNavigation.jsx index 1398afd4..7c3dcda9 100644 --- a/src/components/Dashboard/common/DashboardNavigation.jsx +++ b/src/components/Dashboard/common/DashboardNavigation.jsx @@ -78,14 +78,6 @@ const NAV_ELECTRON_CENTER_FADE_STYLE = { alignItems: 'center' } const NAV_ELECTRON_TABS_STYLE = { flex: 1, minWidth: 0, display: 'flex' } -const NAV_ELECTRON_DIVIDER_STYLE_WITH_LABELS = { - margin: '3px 1px 0 4px', - height: '14px' -} -const NAV_ELECTRON_DIVIDER_STYLE = { - margin: '3px 1px 0 0', - height: '14px' -} const NAV_TRAILING_FLEX_STYLE = { marginTop: '-2px', marginRight: '6px' } const NAV_TRAILING_SPACE_STYLE = { paddingTop: '2px', marginRight: '8px' } const NAV_SEARCH_BUTTON_STYLE = { marginTop: '4px' } @@ -237,14 +229,7 @@ const DashboardElectronCenterItems = memo( > {menu} - +
diff --git a/src/components/Dashboard/common/DashboardTabs.jsx b/src/components/Dashboard/common/DashboardTabs.jsx index 2ab1fcc8..fadabad2 100644 --- a/src/components/Dashboard/common/DashboardTabs.jsx +++ b/src/components/Dashboard/common/DashboardTabs.jsx @@ -1,5 +1,5 @@ import PropTypes from 'prop-types' -import { useCallback, useLayoutEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' import { Button, Flex, Typography } from 'antd' import classNames from 'classnames' import ScrollBox from './ScrollBox' @@ -177,17 +177,18 @@ const hideStickyClone = (cloneEl) => { const setSimplebarMaskInset = (maskEl, edge, inset) => { if (!maskEl) return + const wrapEl = maskEl.closest('.dashboard-tabs-wrap') || maskEl if (!edge) { - maskEl.style.removeProperty('--tab-mask-left') - maskEl.style.removeProperty('--tab-mask-right') + wrapEl.style.removeProperty('--tab-mask-left') + wrapEl.style.removeProperty('--tab-mask-right') return } if (edge === 'left') { - maskEl.style.setProperty('--tab-mask-left', `${inset}px`) - maskEl.style.removeProperty('--tab-mask-right') + wrapEl.style.setProperty('--tab-mask-left', `${inset}px`) + wrapEl.style.removeProperty('--tab-mask-right') } else { - maskEl.style.removeProperty('--tab-mask-left') - maskEl.style.setProperty('--tab-mask-right', `${inset}px`) + wrapEl.style.removeProperty('--tab-mask-left') + wrapEl.style.setProperty('--tab-mask-right', `${inset}px`) } } @@ -391,6 +392,8 @@ const useActiveTabStickyScroll = (activeTabId, tabs) => { const listRef = useRef(null) const cloneRef = useRef(null) const lineRef = useRef(null) + const prevTabCountRef = useRef(tabs.length) + const pendingScrollTabIdRef = useRef(null) const tabLayoutKey = `${activeTabId}:${tabs.map((tab) => `${tab.id}:${tab.title || ''}`).join('|')}` useLayoutEffect(() => { @@ -429,6 +432,46 @@ const useActiveTabStickyScroll = (activeTabId, tabs) => { } }, [tabLayoutKey]) + useEffect(() => { + const lastTab = tabs[tabs.length - 1] + if ( + tabs.length === prevTabCountRef.current + 1 && + lastTab?.id === activeTabId + ) { + pendingScrollTabIdRef.current = lastTab.id + } + prevTabCountRef.current = tabs.length + + if (!lastTab || pendingScrollTabIdRef.current !== lastTab.id) { + return undefined + } + + const list = listRef.current + const scrollEl = rootRef.current?.querySelector( + '.simplebar-content-wrapper' + ) + const outlineEl = list?.lastElementChild?.querySelector( + '.dashboard-tab-item-outline-container' + ) + if (!scrollEl || !outlineEl) return undefined + + const scrollToEndIfReady = () => { + if (outlineEl.getBoundingClientRect().width <= 36) return false + scrollEl.scrollLeft = scrollEl.scrollWidth + pendingScrollTabIdRef.current = null + return true + } + + if (scrollToEndIfReady()) return undefined + + const observer = new ResizeObserver(() => { + if (scrollToEndIfReady()) observer.disconnect() + }) + observer.observe(outlineEl) + + return () => observer.disconnect() + }, [activeTabId, tabs]) + return { rootRef, listRef, cloneRef, lineRef } }