From d6c72c21e8e1813dce30e65cf073aa3f4bab1178 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Thu, 17 Sep 2026 23:36:44 +0100 Subject: [PATCH] Refactor Dashboard Navigation and Tabs for Enhanced User Experience - Restructured the DashboardNavigation component to separate leading and trailing navigation elements, improving layout and clarity. - Updated CSS styles for navigation and dashboard tabs to enhance responsiveness and visual consistency. - Implemented drag-and-drop functionality for dashboard tabs, allowing users to reorder tabs seamlessly. - Introduced new styles for tab items and improved handling of drag states, enhancing the overall interaction experience. --- assets/stylesheets/App.css | 236 +++++++++++++++- .../Dashboard/common/DashboardNavigation.jsx | 175 +++++++----- .../Dashboard/common/DashboardTabs.jsx | 262 +++++++++++++++--- .../Dashboard/common/SegmentedNav.jsx | 10 +- .../Dashboard/context/ThemeContext.jsx | 4 + 5 files changed, 567 insertions(+), 120 deletions(-) diff --git a/assets/stylesheets/App.css b/assets/stylesheets/App.css index 1ec414f2..b02c2f20 100644 --- a/assets/stylesheets/App.css +++ b/assets/stylesheets/App.css @@ -112,6 +112,18 @@ --webkit-user-select: none; } +.electron-navigation-leading, +.electron-navigation-trailing { + align-self: stretch; + height: 42px; + border-bottom: 1px solid var(--color-header-border); +} + +.electron-navigation-trailing { + flex: 0 1 auto; + justify-content: flex-end; +} + .electron-body * { -webkit-user-select: none; -khtml-user-select: none; @@ -145,12 +157,20 @@ html.macos-vibrancy .main-layout { background: transparent !important; } +html.macos-vibrancy .light-mode { + --color-layout-background: rgba(255, 255, 255, 0.88); +} + +html.macos-vibrancy .dark-mode { + --color-layout-background: rgba(0, 0, 0, 0.88); +} + html.macos-vibrancy .light-mode .ant-layout.main-content-layout { - background: rgba(255, 255, 255, 0.88) !important; + background: var(--color-layout-background) !important; } html.macos-vibrancy .dark-mode .ant-layout.main-content-layout { - background: rgba(0, 0, 0, 0.88) !important; + background: var(--color-layout-background) !important; } html.macos-vibrancy .light-mode .ant-layout-sider { @@ -367,6 +387,7 @@ code { :root, .light-mode, html:has(.light-mode) { + --color-layout-background: #ffffff; --kbd-color: #363636; --kbd-bg: #efefef; --kbd-inset: #e8e8e8; @@ -378,6 +399,7 @@ html:has(.light-mode) { .dark-mode, html:has(.dark-mode) { + --color-layout-background: #000000; --kbd-color: #ececec; --kbd-bg: #3a3a3a; --kbd-inset: #323232; @@ -3349,25 +3371,188 @@ body.objectKanbanColumnResizing * { .dashboard-tabs { flex: 1; min-width: 0; + align-self: stretch; } -.dashboard-tabs .segmented-nav-item-label { - padding-inline: 0px; - padding-left: 8px; -} - -.dashboard-tabs-segmented-wrap { +.dashboard-tabs-wrap { flex: 1; min-width: 0; + height: 100%; } -.dashboard-tabs-segmented-wrap .simplebar-track.simplebar-vertical { +.dashboard-tabs-wrap .simplebar-track.simplebar-vertical { display: none; } -.dashboard-tabs-segmented-inner { +.dashboard-tabs-inner { width: max-content; + height: 100%; + align-items: flex-start; + box-sizing: border-box; +} + +.dashboard-tabs-list { + position: relative; + display: flex; + align-items: flex-start; + gap: 4px; + padding-inline: 12px; + height: 100%; + max-height: 42px; +} + +.dashboard-tab-item-container { + height: 43px; + position: relative; +} + +.dashboard-tab-item-container-active { + border-bottom: none; + height: 43px; +} + +.dashboard-tab-item-container:before { + content: ''; + position: absolute; + top: 0; + left: -2px; + right: -2px; + bottom: 0; + border-bottom: 1px solid var(--color-header-border); +} + +.dashboard-tab-item-container-active + .dashboard-tab-item-container:before { + left: 16px; +} + +.dashboard-tabs-list:has(+ .dashboard-tab-item-container-active):before { + right: 9.5px; +} + +.dashboard-tab-item-container-active:before { + content: none; + display: none; +} + +.dashboard-tab-item { + position: relative; + z-index: 1; + display: inline-flex; align-items: center; + justify-content: center; + overflow: visible; + margin: 5px 0 0 0; + padding: 0 8px 0 8px; + border: 1px solid var(--color-list-view-tabs-border); + border-radius: 12px; + background: transparent; + cursor: pointer; + font-family: inherit; + font-size: inherit; + line-height: inherit; + color: inherit; + box-shadow: none; + outline: none; + + transition: + background 0.3s ease, + border-color 0.3s ease; +} + +.dashboard-tab-item-icon { + display: flex; + align-items: center; + min-height: 30px; + line-height: 30px; + margin-right: 6px; +} + +.dashboard-tab-item:focus-visible { + outline: 2px solid + color-mix(in srgb, var(--color-primary, #1677ff) 45%, transparent); + outline-offset: 2px; +} + +.dashboard-tab-item:not(.dashboard-tab-item-active):hover { + background: var(--color-button-background); + border: 1px solid + color-mix( + in srgb, + var(--color-button-border) 40%, + var(--color-button-background) + ); +} + +.dashboard-tab-item-active { + z-index: 2; + align-self: stretch; + margin-bottom: -1px; + padding-bottom: 5px; + border-color: transparent; + border-radius: 12px 12px 0 0; + background: transparent; +} + +.dashboard-tab-item-outline-container { + position: absolute; + top: -1px; + left: -1px; + right: -1px; + bottom: 0; +} + +.dashboard-tab-item-outline { + position: absolute; + inset: 0 0 20px 0; + border: 1px solid var(--color-header-border); + border-bottom: none; + border-radius: 12px 12px 0 0; + pointer-events: none; + opacity: 0; + transition: opacity 0.3s ease; +} + +.dashboard-tab-item-corner { + position: absolute; + bottom: -2px; + width: 22px; + height: 22px; + overflow: hidden; + pointer-events: none; + opacity: 0; + transition: opacity 0.3s ease; +} + +.dashboard-tab-item-corner-left { + left: -21px; +} + +.dashboard-tab-item-corner-right { + right: -21px; +} + +.dashboard-tab-item-corner-inner { + position: absolute; + width: 48px; + height: 48px; + box-sizing: border-box; + border: 1px solid var(--color-header-border); + border-radius: 15.5px; +} + +.dashboard-tab-item-corner-left .dashboard-tab-item-corner-inner { + bottom: 1px; + right: 1px; +} + +.dashboard-tab-item-corner-right .dashboard-tab-item-corner-inner { + bottom: 1px; + left: 1px; +} + +.dashboard-tab-item-active .dashboard-tab-item-outline, +.dashboard-tab-item-active .dashboard-tab-item-corner { + opacity: 1; } .dashboard-tabs-label { @@ -3395,7 +3580,36 @@ body.objectKanbanColumnResizing * { .dashboard-tabs-add-button { padding-inline: 4px; min-width: 20px; - margin-bottom: 2px; + margin-top: 2px; +} + +.dashboard-tab-item-dragging { + opacity: 0.4; +} + +.dashboard-tab-item-drop-before::before, +.dashboard-tab-item-drop-after::after { + content: ''; + position: absolute; + top: 4px; + bottom: 4px; + width: 2px; + border-radius: 1px; + background: var(--color-primary, #1677ff); + pointer-events: none; +} + +.dashboard-tab-item-drop-before::before { + left: -5px; +} + +.dashboard-tab-item-drop-after::after { + right: -5px; +} + +.dashboard-tabs-reordering + .dashboard-tab-item:not(.dashboard-tab-item-dragging) { + cursor: default; } .dashboard-tab-panes { diff --git a/src/components/Dashboard/common/DashboardNavigation.jsx b/src/components/Dashboard/common/DashboardNavigation.jsx index bd497c73..60155357 100644 --- a/src/components/Dashboard/common/DashboardNavigation.jsx +++ b/src/components/Dashboard/common/DashboardNavigation.jsx @@ -280,75 +280,8 @@ const DashboardNavigation = () => { /> ) - const navigationContents = ( - - {isMacOSApp ? : null} - {isOtherApp ? ( - <> - {' '} - - - ) : null} - {showControls && isMobile && menu} - {showDesktopLogo == true ? ( - - ) : showMobileLogo == true ? ( - - ) : null} -
- {showControls && !isMobile ? ( - - {menu} - {isElectron ? ( - <> - - - - ) : null} - - ) : null} - {!showControls && ( - - Farm Control - - )} -
- + const navigationTrailing = ( + <>
{
{isOtherApp ? : null} + + ) + + const navigationLeading = ( + <> + {isMacOSApp ? : null} + {isOtherApp ? ( + <> + {' '} + + + ) : null} + {showControls && isMobile && menu} + {showDesktopLogo == true ? ( + + ) : showMobileLogo == true ? ( + + ) : null} + + ) + + const navigationContents = ( + + {navigationLeading} +
+ {showControls && !isMobile ? ( + + {menu} + + ) : null} + {!showControls && ( + + Farm Control + + )} +
+ {navigationTrailing}
) + const electronDragClass = + isFullScreen || (isMaximized && isOtherApp) + ? 'electrobun-webkit-app-region-no-drag' + : 'electrobun-webkit-app-region-drag' + return ( <> {isElectron ? ( - {navigationContents} + + {navigationLeading} + {showControls && !isMobile ? menu : null} + {showControls && !isMobile ? ( + + ) : null} + {!showControls ? ( + + Farm Control + + ) : null} + + {showControls && !isMobile ? : null} + + {navigationTrailing} + ) : ( diff --git a/src/components/Dashboard/common/DashboardTabs.jsx b/src/components/Dashboard/common/DashboardTabs.jsx index d8164c1b..4eff3515 100644 --- a/src/components/Dashboard/common/DashboardTabs.jsx +++ b/src/components/Dashboard/common/DashboardTabs.jsx @@ -1,8 +1,7 @@ import PropTypes from 'prop-types' -import { useMemo } from 'react' +import { useCallback, useState } from 'react' import { Button, Flex, Typography } from 'antd' import classNames from 'classnames' -import SegmentedNav from './SegmentedNav' import ScrollBox from './ScrollBox' import PlusIcon from '../../Icons/PlusIcon' import XMarkIcon from '../../Icons/XMarkIcon' @@ -14,6 +13,26 @@ import { hasExternalTabDrag, writeTabDragData } from './tabDrag' const { Text } = Typography +const DashboardTabChrome = () => ( + <> +