diff --git a/assets/stylesheets/App.css b/assets/stylesheets/App.css index 576d4c6f..d32f01ed 100644 --- a/assets/stylesheets/App.css +++ b/assets/stylesheets/App.css @@ -125,14 +125,17 @@ opacity: 1; visibility: visible; transition: - opacity 0.2s ease, - visibility 0.2s ease; + opacity 0.2s ease 0.2s, + visibility 0s linear 0.2s; } .dashboard-navigation-fade-hidden { opacity: 0; visibility: hidden; pointer-events: none; + transition: + opacity 0.2s ease 0s, + visibility 0s linear 0.2s; } .dashboard-navigation-window-title { @@ -140,12 +143,46 @@ inset: 0; display: flex; align-items: center; - padding-left: 14px; + justify-content: space-between; + padding-left: 16px; + padding-right: 16px; font-size: 14px; pointer-events: none; user-select: none; -webkit-user-select: none; +} + +.dashboard-navigation-center-border { + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 1px; + background: var(--color-header-border); + pointer-events: none; + opacity: 1; + /* Wait for outgoing chrome to finish fading before this line appears. */ + transition: opacity 0.2s ease 0.2s; +} + +.dashboard-navigation-center-border-hidden { + opacity: 0; + /* Stay through the gap, then fade out as incoming chrome fades in. */ + transition: opacity 0.2s ease 0.2s; +} + +.dashboard-menu-container { + position: relative; + height: 42px; + padding-top: 2px; +} +.dashboard-menu-container:after { border-bottom: 1px solid var(--color-header-border); + content: ''; + position: absolute; + left: 0; + right: -2px; + bottom: 0; } .electron-navigation-leading, @@ -3460,7 +3497,7 @@ body.objectKanbanColumnResizing * { display: flex; align-items: flex-start; gap: 4px; - padding-left: 12px; + padding-left: 14px; height: 100%; max-height: 42px; } @@ -3486,7 +3523,7 @@ body.objectKanbanColumnResizing * { } .dashboard-tab-item-container:first-child:before { - left: -12px; + left: -14px; } .dashboard-tab-item-container:last-child:before { diff --git a/src/components/Dashboard/common/DashboardDevControls.jsx b/src/components/Dashboard/common/DashboardDevControls.jsx new file mode 100644 index 00000000..b0590300 --- /dev/null +++ b/src/components/Dashboard/common/DashboardDevControls.jsx @@ -0,0 +1,70 @@ +import { useContext, useEffect, useState } from 'react' +import { Space, Tag } from 'antd' +import { LoadingOutlined } from '@ant-design/icons' +import { useNavigate } from 'react-router-dom' +import { ApiServerContext } from '../context/ApiServerContext' +import Tooltip from './Tooltip' +import CloudIcon from '../../Icons/CloudIcon' +import DeveloperIcon from '../../Icons/DeveloperIcon' + +const DashboardDevControls = () => { + const { connecting, connected } = useContext(ApiServerContext) + const navigate = useNavigate() + const [apiServerState, setApiServerState] = useState('disconnected') + + useEffect(() => { + if (connecting == true) { + setApiServerState('connecting') + } else if (connected == true) { + setApiServerState('connected') + } else { + setApiServerState('disconnected') + } + }, [connecting, connected]) + + if (import.meta.env.MODE !== 'development') return null + + return ( + + {apiServerState === 'connected' ? ( + + } + /> + + ) : null} + {apiServerState === 'connecting' ? ( + + } + /> + + ) : null} + {apiServerState === 'disconnected' ? ( + + } + /> + + ) : null} + + } + onClick={() => { + navigate('/dashboard/developer/sessionstorage') + }} + /> + + + ) +} + +export default DashboardDevControls diff --git a/src/components/Dashboard/common/DashboardNavigation.jsx b/src/components/Dashboard/common/DashboardNavigation.jsx index 6168ca28..93e4d1a1 100644 --- a/src/components/Dashboard/common/DashboardNavigation.jsx +++ b/src/components/Dashboard/common/DashboardNavigation.jsx @@ -1,5 +1,6 @@ // DashboardNavigation.js -import { useContext, useEffect, useState, useMemo } from 'react' +import { useContext, useEffect, useState, useMemo, useCallback, memo } from 'react' +import PropTypes from 'prop-types' import { Menu, Flex, @@ -11,7 +12,6 @@ import { Typography, Popover } from 'antd' -import { LoadingOutlined } from '@ant-design/icons' import { AuthContext } from '../context/AuthContext' import { SpotlightContext } from '../context/SpotlightContext' import { ApiServerContext } from '../context/ApiServerContext' @@ -35,9 +35,9 @@ import CloudIcon from '../../Icons/CloudIcon' import BellIcon from '../../Icons/BellIcon' import SearchIcon from '../../Icons/SearchIcon' import SettingsIcon from '../../Icons/SettingsIcon' -import DeveloperIcon from '../../Icons/DeveloperIcon' import { ElectronContext } from '../context/ElectronContext' import DashboardWindowButtons from './DashboardWindowButtons' +import DashboardDevControls from './DashboardDevControls' import WindowAppMenu from './WindowAppMenu' import WebAppSwitcher from './WebAppSwitcher' import DashboardTabs from './DashboardTabs' @@ -57,6 +57,225 @@ import { getEffectiveAppearance } from '../../../database/Settings' const { Text } = Typography +const NAV_CENTER_FADE_STYLE = { + width: '100%', + minWidth: 0, + display: 'flex', + alignItems: 'center' +} +const NAV_ELECTRON_CENTER_FADE_STYLE = { + flex: 1, + minWidth: 0, + display: 'flex', + 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' } +const NAV_BELL_BUTTON_STYLE = { marginTop: '2px' } +const NAV_BADGE_STYLE = { padding: 0, fontWeight: 600 } +const NAV_UPDATE_TAG_STYLE = { cursor: 'pointer', margin: '2px 0 0 0' } +const NAV_OVERFLOW_BUTTON_STYLE = { marginBottom: '4px' } + +const navigationFadeClass = (visible) => + `dashboard-navigation-fade${visible ? '' : ' dashboard-navigation-fade-hidden'}` + +const NavigationFade = memo(function NavigationFade({ visible, children, style }) { + return ( +
+ {children} +
+ ) +}) + +NavigationFade.propTypes = { + visible: PropTypes.bool, + children: PropTypes.node, + style: PropTypes.object +} + +const DashboardNavigationTrailingItems = memo( + function DashboardNavigationTrailingItems({ + controlsReady, + isMobile, + isElectron, + unreadCount, + userProfile, + availableUpdate, + userPopoverOpen, + onUserPopoverOpenChange, + onSearch, + onNotifications, + onCheckUpdates + }) { + return ( + + {controlsReady ? ( + + + +