Compare commits
4 Commits
c39051a891
...
d52eea3a41
| Author | SHA1 | Date | |
|---|---|---|---|
| d52eea3a41 | |||
| f3af883bd2 | |||
| 5cc74bca18 | |||
| 65417f75b8 |
@ -112,6 +112,79 @@
|
|||||||
--webkit-user-select: none;
|
--webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-navigation-center {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
align-self: stretch;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-navigation-fade {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
transition:
|
||||||
|
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 {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
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,
|
.electron-navigation-leading,
|
||||||
.electron-navigation-trailing {
|
.electron-navigation-trailing {
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
@ -190,6 +263,17 @@ html.macos-vibrancy .light-mode .ant-layout-sider {
|
|||||||
background: rgba(244, 244, 244, 0.82) !important;
|
background: rgba(244, 244, 244, 0.82) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html.macos-vibrancy .light-mode .ant-layout.main-content-layout:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(255, 255, 255, 0.85) !important;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
html.macos-vibrancy .dark-mode .ant-layout-sider {
|
html.macos-vibrancy .dark-mode .ant-layout-sider {
|
||||||
background: rgba(10, 10, 10, 0.82) !important;
|
background: rgba(10, 10, 10, 0.82) !important;
|
||||||
}
|
}
|
||||||
@ -3413,7 +3497,7 @@ body.objectKanbanColumnResizing * {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
padding-left: 12px;
|
padding-left: 14px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: 42px;
|
max-height: 42px;
|
||||||
}
|
}
|
||||||
@ -3439,7 +3523,7 @@ body.objectKanbanColumnResizing * {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-tab-item-container:first-child:before {
|
.dashboard-tab-item-container:first-child:before {
|
||||||
left: -12px;
|
left: -14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-tab-item-container:last-child:before {
|
.dashboard-tab-item-container:last-child:before {
|
||||||
@ -3518,7 +3602,19 @@ body.objectKanbanColumnResizing * {
|
|||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
position: relative;
|
||||||
border-radius: 12px 12px 0 0;
|
border-radius: 12px 12px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-tab-item-active:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
left: 1px;
|
||||||
|
right: 1px;
|
||||||
|
bottom: -1px;
|
||||||
|
border-radius: 9px 9px 0 0;
|
||||||
|
z-index: -2;
|
||||||
background: var(--color-layout-background);
|
background: var(--color-layout-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3542,7 +3638,7 @@ body.objectKanbanColumnResizing * {
|
|||||||
|
|
||||||
.dashboard-tab-item-corner {
|
.dashboard-tab-item-corner {
|
||||||
--tab-corner-size: 22px;
|
--tab-corner-size: 22px;
|
||||||
--tab-corner-radius: 15.5px;
|
--tab-corner-radius: 16px;
|
||||||
--tab-corner-inset: 1px;
|
--tab-corner-inset: 1px;
|
||||||
--tab-corner-center: calc(
|
--tab-corner-center: calc(
|
||||||
var(--tab-corner-size) - var(--tab-corner-inset) - var(--tab-corner-radius)
|
var(--tab-corner-size) - var(--tab-corner-inset) - var(--tab-corner-radius)
|
||||||
@ -3566,10 +3662,7 @@ body.objectKanbanColumnResizing * {
|
|||||||
|
|
||||||
.dashboard-tab-item-corner-fill {
|
.dashboard-tab-item-corner-fill {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1px;
|
inset: 0;
|
||||||
left: 2px;
|
|
||||||
right: 2px;
|
|
||||||
bottom: -1px;
|
|
||||||
background: var(--color-layout-background);
|
background: var(--color-layout-background);
|
||||||
-webkit-mask-repeat: no-repeat;
|
-webkit-mask-repeat: no-repeat;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
@ -3577,33 +3670,27 @@ body.objectKanbanColumnResizing * {
|
|||||||
|
|
||||||
.dashboard-tab-item-corner-left .dashboard-tab-item-corner-fill {
|
.dashboard-tab-item-corner-left .dashboard-tab-item-corner-fill {
|
||||||
-webkit-mask-image: radial-gradient(
|
-webkit-mask-image: radial-gradient(
|
||||||
circle var(--tab-corner-radius) at var(--tab-corner-center)
|
circle at var(--tab-corner-center) var(--tab-corner-center),
|
||||||
var(--tab-corner-center),
|
transparent calc(var(--tab-corner-radius) - 0.5px),
|
||||||
transparent var(--tab-corner-radius),
|
#000 calc(var(--tab-corner-radius) - 0.5px)
|
||||||
#000 var(--tab-corner-radius)
|
|
||||||
);
|
);
|
||||||
mask-image: radial-gradient(
|
mask-image: radial-gradient(
|
||||||
circle var(--tab-corner-radius) at var(--tab-corner-center)
|
circle at var(--tab-corner-center) var(--tab-corner-center),
|
||||||
var(--tab-corner-center),
|
transparent calc(var(--tab-corner-radius) - 0.5px),
|
||||||
transparent var(--tab-corner-radius),
|
#000 calc(var(--tab-corner-radius) - 0.5px)
|
||||||
#000 var(--tab-corner-radius)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-tab-item-corner-right .dashboard-tab-item-corner-fill {
|
.dashboard-tab-item-corner-right .dashboard-tab-item-corner-fill {
|
||||||
-webkit-mask-image: radial-gradient(
|
-webkit-mask-image: radial-gradient(
|
||||||
circle var(--tab-corner-radius) at
|
circle at calc(100% - var(--tab-corner-center)) var(--tab-corner-center),
|
||||||
calc(var(--tab-corner-radius) + var(--tab-corner-inset))
|
transparent calc(var(--tab-corner-radius) - 0.5px),
|
||||||
var(--tab-corner-center),
|
#000 calc(var(--tab-corner-radius) - 0.5px)
|
||||||
transparent var(--tab-corner-radius),
|
|
||||||
#000 var(--tab-corner-radius)
|
|
||||||
);
|
);
|
||||||
mask-image: radial-gradient(
|
mask-image: radial-gradient(
|
||||||
circle var(--tab-corner-radius) at
|
circle at calc(100% - var(--tab-corner-center)) var(--tab-corner-center),
|
||||||
calc(var(--tab-corner-radius) + var(--tab-corner-inset))
|
transparent calc(var(--tab-corner-radius) - 0.5px),
|
||||||
var(--tab-corner-center),
|
#000 calc(var(--tab-corner-radius) - 0.5px)
|
||||||
transparent var(--tab-corner-radius),
|
|
||||||
#000 var(--tab-corner-radius)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3652,7 +3739,7 @@ body.objectKanbanColumnResizing * {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
margin-left: 2px;
|
margin-left: 1px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--color-text-secondary, inherit);
|
color: var(--color-text-secondary, inherit);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
@ -3666,11 +3753,6 @@ body.objectKanbanColumnResizing * {
|
|||||||
inset: -8px -8px -8px -6px;
|
inset: -8px -8px -8px -6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-tabs-close:hover {
|
|
||||||
opacity: 1;
|
|
||||||
background: var(--color-button-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-tabs-add-button {
|
.dashboard-tabs-add-button {
|
||||||
padding-inline: 4px;
|
padding-inline: 4px;
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
|
|||||||
70
src/components/Dashboard/common/DashboardDevControls.jsx
Normal file
70
src/components/Dashboard/common/DashboardDevControls.jsx
Normal file
@ -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 (
|
||||||
|
<Space>
|
||||||
|
{apiServerState === 'connected' ? (
|
||||||
|
<Tooltip content='Connected to api server'>
|
||||||
|
<Tag
|
||||||
|
color='success'
|
||||||
|
style={{ marginRight: 0 }}
|
||||||
|
icon={<CloudIcon />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
{apiServerState === 'connecting' ? (
|
||||||
|
<Tooltip content='Connecting to api server...'>
|
||||||
|
<Tag
|
||||||
|
color='warning'
|
||||||
|
style={{ marginRight: 0 }}
|
||||||
|
icon={<LoadingOutlined />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
{apiServerState === 'disconnected' ? (
|
||||||
|
<Tooltip content='Disconnected from api server'>
|
||||||
|
<Tag
|
||||||
|
color='error'
|
||||||
|
style={{ marginRight: 0 }}
|
||||||
|
icon={<CloudIcon />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
<Tooltip content='Developer'>
|
||||||
|
<Tag
|
||||||
|
color='yellow'
|
||||||
|
style={{ marginRight: 0 }}
|
||||||
|
icon={<DeveloperIcon />}
|
||||||
|
onClick={() => {
|
||||||
|
navigate('/dashboard/developer/sessionstorage')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DashboardDevControls
|
||||||
@ -1,5 +1,6 @@
|
|||||||
// DashboardNavigation.js
|
// DashboardNavigation.js
|
||||||
import { useContext, useEffect, useState, useMemo } from 'react'
|
import { useContext, useEffect, useState, useMemo, useCallback, memo } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import {
|
import {
|
||||||
Menu,
|
Menu,
|
||||||
Flex,
|
Flex,
|
||||||
@ -11,7 +12,6 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
Popover
|
Popover
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
|
||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
import { SpotlightContext } from '../context/SpotlightContext'
|
import { SpotlightContext } from '../context/SpotlightContext'
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
@ -35,9 +35,9 @@ import CloudIcon from '../../Icons/CloudIcon'
|
|||||||
import BellIcon from '../../Icons/BellIcon'
|
import BellIcon from '../../Icons/BellIcon'
|
||||||
import SearchIcon from '../../Icons/SearchIcon'
|
import SearchIcon from '../../Icons/SearchIcon'
|
||||||
import SettingsIcon from '../../Icons/SettingsIcon'
|
import SettingsIcon from '../../Icons/SettingsIcon'
|
||||||
import DeveloperIcon from '../../Icons/DeveloperIcon'
|
|
||||||
import { ElectronContext } from '../context/ElectronContext'
|
import { ElectronContext } from '../context/ElectronContext'
|
||||||
import DashboardWindowButtons from './DashboardWindowButtons'
|
import DashboardWindowButtons from './DashboardWindowButtons'
|
||||||
|
import DashboardDevControls from './DashboardDevControls'
|
||||||
import WindowAppMenu from './WindowAppMenu'
|
import WindowAppMenu from './WindowAppMenu'
|
||||||
import WebAppSwitcher from './WebAppSwitcher'
|
import WebAppSwitcher from './WebAppSwitcher'
|
||||||
import DashboardTabs from './DashboardTabs'
|
import DashboardTabs from './DashboardTabs'
|
||||||
@ -57,6 +57,225 @@ import { getEffectiveAppearance } from '../../../database/Settings'
|
|||||||
|
|
||||||
const { Text } = Typography
|
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 (
|
||||||
|
<div
|
||||||
|
className={navigationFadeClass(visible)}
|
||||||
|
inert={visible ? undefined : true}
|
||||||
|
aria-hidden={visible ? undefined : true}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Flex gap='small' align='center' style={NAV_TRAILING_FLEX_STYLE}>
|
||||||
|
{controlsReady ? (
|
||||||
|
<Space style={NAV_TRAILING_SPACE_STYLE}>
|
||||||
|
<WebAppSwitcher />
|
||||||
|
<KeyboardShortcut
|
||||||
|
shortcut='alt+q'
|
||||||
|
hint='ALT Q'
|
||||||
|
onTrigger={onSearch}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon={<SearchIcon />}
|
||||||
|
type='text'
|
||||||
|
style={NAV_SEARCH_BUTTON_STYLE}
|
||||||
|
onClick={onSearch}
|
||||||
|
/>
|
||||||
|
</KeyboardShortcut>
|
||||||
|
<Badge
|
||||||
|
count={unreadCount}
|
||||||
|
size='small'
|
||||||
|
offset={[-5, 8]}
|
||||||
|
style={NAV_BADGE_STYLE}
|
||||||
|
>
|
||||||
|
<KeyboardShortcut
|
||||||
|
shortcut='alt+n'
|
||||||
|
hint='ALT N'
|
||||||
|
onTrigger={onNotifications}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon={<BellIcon />}
|
||||||
|
type='text'
|
||||||
|
style={NAV_BELL_BUTTON_STYLE}
|
||||||
|
onClick={onNotifications}
|
||||||
|
/>
|
||||||
|
</KeyboardShortcut>
|
||||||
|
</Badge>
|
||||||
|
</Space>
|
||||||
|
) : null}
|
||||||
|
<DashboardDevControls />
|
||||||
|
{controlsReady && userProfile ? (
|
||||||
|
<Space>
|
||||||
|
<Popover
|
||||||
|
content={
|
||||||
|
<UserProfilePopover
|
||||||
|
onClose={() => onUserPopoverOpenChange(false)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placement='bottomRight'
|
||||||
|
trigger='hover'
|
||||||
|
open={userPopoverOpen}
|
||||||
|
onOpenChange={onUserPopoverOpenChange}
|
||||||
|
arrow={false}
|
||||||
|
>
|
||||||
|
<Tag style={{ marginRight: 0 }} icon={<PersonIcon />}>
|
||||||
|
{!isMobile && (userProfile?.name || userProfile.username)}
|
||||||
|
</Tag>
|
||||||
|
</Popover>
|
||||||
|
</Space>
|
||||||
|
) : null}
|
||||||
|
{controlsReady && isElectron && availableUpdate ? (
|
||||||
|
<Tag
|
||||||
|
icon={<CloudIcon />}
|
||||||
|
style={NAV_UPDATE_TAG_STYLE}
|
||||||
|
color='cyan'
|
||||||
|
onClick={onCheckUpdates}
|
||||||
|
>
|
||||||
|
Update Available
|
||||||
|
</Tag>
|
||||||
|
) : null}
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
DashboardNavigationTrailingItems.propTypes = {
|
||||||
|
controlsReady: PropTypes.bool,
|
||||||
|
isMobile: PropTypes.bool,
|
||||||
|
isElectron: PropTypes.bool,
|
||||||
|
unreadCount: PropTypes.number,
|
||||||
|
userProfile: PropTypes.shape({
|
||||||
|
name: PropTypes.string,
|
||||||
|
username: PropTypes.string
|
||||||
|
}),
|
||||||
|
availableUpdate: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
|
||||||
|
userPopoverOpen: PropTypes.bool,
|
||||||
|
onUserPopoverOpenChange: PropTypes.func,
|
||||||
|
onSearch: PropTypes.func,
|
||||||
|
onNotifications: PropTypes.func,
|
||||||
|
onCheckUpdates: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
const DashboardElectronCenterItems = memo(function DashboardElectronCenterItems({
|
||||||
|
menu,
|
||||||
|
showNavigationLabels
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='dashboard-menu-container'>{menu}</div>
|
||||||
|
<Divider
|
||||||
|
type='vertical'
|
||||||
|
style={
|
||||||
|
showNavigationLabels
|
||||||
|
? NAV_ELECTRON_DIVIDER_STYLE_WITH_LABELS
|
||||||
|
: NAV_ELECTRON_DIVIDER_STYLE
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div style={NAV_ELECTRON_TABS_STYLE}>
|
||||||
|
<DashboardTabs />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
DashboardElectronCenterItems.propTypes = {
|
||||||
|
menu: PropTypes.node,
|
||||||
|
showNavigationLabels: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const DashboardNavigationTrailing = memo(function DashboardNavigationTrailing({
|
||||||
|
showControls,
|
||||||
|
isOtherApp,
|
||||||
|
...itemProps
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='electrobun-webkit-app-region-no-drag'>
|
||||||
|
<NavigationFade visible={showControls}>
|
||||||
|
<DashboardNavigationTrailingItems {...itemProps} />
|
||||||
|
</NavigationFade>
|
||||||
|
</div>
|
||||||
|
{isOtherApp ? <DashboardWindowButtons /> : null}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
DashboardNavigationTrailing.propTypes = {
|
||||||
|
showControls: PropTypes.bool,
|
||||||
|
isOtherApp: PropTypes.bool,
|
||||||
|
controlsReady: PropTypes.bool,
|
||||||
|
isMobile: PropTypes.bool,
|
||||||
|
isElectron: PropTypes.bool,
|
||||||
|
unreadCount: PropTypes.number,
|
||||||
|
userProfile: PropTypes.shape({
|
||||||
|
name: PropTypes.string,
|
||||||
|
username: PropTypes.string
|
||||||
|
}),
|
||||||
|
availableUpdate: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
|
||||||
|
userPopoverOpen: PropTypes.bool,
|
||||||
|
onUserPopoverOpenChange: PropTypes.func,
|
||||||
|
onSearch: PropTypes.func,
|
||||||
|
onNotifications: PropTypes.func,
|
||||||
|
onCheckUpdates: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
const mapSidebarItemsToMenu = (items, navigate) =>
|
const mapSidebarItemsToMenu = (items, navigate) =>
|
||||||
items.map((item) => {
|
items.map((item) => {
|
||||||
if (item?.type === 'divider') {
|
if (item?.type === 'divider') {
|
||||||
@ -83,8 +302,7 @@ const mapSidebarItemsToMenu = (items, navigate) =>
|
|||||||
const DashboardNavigation = () => {
|
const DashboardNavigation = () => {
|
||||||
const { userProfile } = useContext(AuthContext)
|
const { userProfile } = useContext(AuthContext)
|
||||||
const { showSpotlight } = useContext(SpotlightContext)
|
const { showSpotlight } = useContext(SpotlightContext)
|
||||||
const { connecting, connected, userSettings, userSettingsLoaded } =
|
const { userSettings, userSettingsLoaded } = useContext(ApiServerContext)
|
||||||
useContext(ApiServerContext)
|
|
||||||
const { authenticated } = useContext(AuthContext)
|
const { authenticated } = useContext(AuthContext)
|
||||||
const {
|
const {
|
||||||
showNavigationLabels,
|
showNavigationLabels,
|
||||||
@ -94,7 +312,6 @@ const DashboardNavigation = () => {
|
|||||||
} = useThemeContext()
|
} = useThemeContext()
|
||||||
const { toggleNotificationCenter, unreadCount } =
|
const { toggleNotificationCenter, unreadCount } =
|
||||||
useContext(NotificationContext)
|
useContext(NotificationContext)
|
||||||
const [apiServerState, setApiServerState] = useState('disconnected')
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const [selectedKey, setSelectedKey] = useState('production')
|
const [selectedKey, setSelectedKey] = useState('production')
|
||||||
@ -209,17 +426,18 @@ const DashboardNavigation = () => {
|
|||||||
[includeDevItems, location.pathname, selectedKey, userProfile]
|
[includeDevItems, location.pathname, selectedKey, userProfile]
|
||||||
)
|
)
|
||||||
|
|
||||||
const headerMenuItems = isMobile
|
const headerMenuItems = useMemo(
|
||||||
|
() =>
|
||||||
|
isMobile
|
||||||
? sidebarMenuItems
|
? sidebarMenuItems
|
||||||
: showNavigationLabels
|
: showNavigationLabels
|
||||||
? mainMenuItems
|
? mainMenuItems
|
||||||
: mainMenuItems.map((item) => ({ ...item, label: undefined }))
|
: mainMenuItems.map((item) => ({ ...item, label: undefined })),
|
||||||
|
[isMobile, mainMenuItems, showNavigationLabels, sidebarMenuItems]
|
||||||
|
)
|
||||||
|
|
||||||
const [userPopoverOpen, setUserPopoverOpen] = useState(false)
|
const [userPopoverOpen, setUserPopoverOpen] = useState(false)
|
||||||
|
const [anyModalVisible, setAnyModalVisible] = useState(false)
|
||||||
const userPopoverContent = (
|
|
||||||
<UserProfilePopover onClose={() => setUserPopoverOpen(false)} />
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const pathParts = location.pathname.split('/').filter(Boolean)
|
const pathParts = location.pathname.split('/').filter(Boolean)
|
||||||
@ -229,19 +447,12 @@ const DashboardNavigation = () => {
|
|||||||
setSelectedKey(nextItem?.key || sectionKey)
|
setSelectedKey(nextItem?.key || sectionKey)
|
||||||
}, [location.pathname, mainMenuItems])
|
}, [location.pathname, mainMenuItems])
|
||||||
|
|
||||||
useEffect(() => {
|
const handleMainMenuClick = useCallback(
|
||||||
if (connecting == true) {
|
({ key }) => {
|
||||||
setApiServerState('connecting')
|
|
||||||
} else if (connected == true) {
|
|
||||||
setApiServerState('connected')
|
|
||||||
} else {
|
|
||||||
setApiServerState('disconnected')
|
|
||||||
}
|
|
||||||
}, [connecting, connected])
|
|
||||||
|
|
||||||
const handleMainMenuClick = ({ key }) => {
|
|
||||||
navigate(getSidebarDefaultPath(key, { includeDev, userProfile }))
|
navigate(getSidebarDefaultPath(key, { includeDev, userProfile }))
|
||||||
}
|
},
|
||||||
|
[includeDev, navigate, userProfile]
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isElectron || !setSidebarViewMenu) return
|
if (!isElectron || !setSidebarViewMenu) return
|
||||||
@ -255,9 +466,67 @@ const DashboardNavigation = () => {
|
|||||||
const showDesktopLogo = !isElectron && !isMobile
|
const showDesktopLogo = !isElectron && !isMobile
|
||||||
const showMobileLogo = !isElectron && isMobile
|
const showMobileLogo = !isElectron && isMobile
|
||||||
|
|
||||||
const showControls = !isElectron || authenticated
|
useEffect(() => {
|
||||||
|
const isModalWrapVisible = (node) => {
|
||||||
|
if (!(node instanceof Element)) return false
|
||||||
|
if (node.getAttribute('aria-hidden') === 'true') return false
|
||||||
|
const style = window.getComputedStyle(node)
|
||||||
|
return (
|
||||||
|
style.display !== 'none' &&
|
||||||
|
style.visibility !== 'hidden' &&
|
||||||
|
style.pointerEvents !== 'none'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const menu = (
|
const updateModalVisibility = () => {
|
||||||
|
const wraps = document.querySelectorAll('.ant-modal-wrap')
|
||||||
|
setAnyModalVisible(Array.from(wraps).some(isModalWrapVisible))
|
||||||
|
}
|
||||||
|
|
||||||
|
let frameId = 0
|
||||||
|
const scheduleUpdate = () => {
|
||||||
|
if (frameId) return
|
||||||
|
frameId = window.requestAnimationFrame(() => {
|
||||||
|
frameId = 0
|
||||||
|
updateModalVisibility()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateModalVisibility()
|
||||||
|
const observer = new MutationObserver(scheduleUpdate)
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['style', 'class', 'aria-hidden']
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.disconnect()
|
||||||
|
if (frameId) window.cancelAnimationFrame(frameId)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const controlsReady = !isElectron || authenticated
|
||||||
|
const showControls = controlsReady && !anyModalVisible
|
||||||
|
const handleSearch = useCallback(() => {
|
||||||
|
showSpotlight()
|
||||||
|
}, [showSpotlight])
|
||||||
|
const handleNotifications = useCallback(() => {
|
||||||
|
toggleNotificationCenter()
|
||||||
|
}, [toggleNotificationCenter])
|
||||||
|
const handleCheckUpdates = useCallback(() => {
|
||||||
|
checkForUpdates()
|
||||||
|
}, [checkForUpdates])
|
||||||
|
const overflowedIndicator = useMemo(
|
||||||
|
() => (
|
||||||
|
<Button type='text' icon={<MenuIcon />} style={NAV_OVERFLOW_BUTTON_STYLE} />
|
||||||
|
),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const menu = useMemo(
|
||||||
|
() => (
|
||||||
<Menu
|
<Menu
|
||||||
mode='horizontal'
|
mode='horizontal'
|
||||||
className={`${isElectron ? 'electron-navigation' : null} ${!showNavigationLabels && !isMobile ? 'no-navigation-labels' : ''}${isMobile ? 'mobile-menu' : ''}`}
|
className={`${isElectron ? 'electron-navigation' : null} ${!showNavigationLabels && !isMobile ? 'no-navigation-labels' : ''}${isMobile ? 'mobile-menu' : ''}`}
|
||||||
@ -270,131 +539,37 @@ const DashboardNavigation = () => {
|
|||||||
}}
|
}}
|
||||||
onClick={isMobile ? undefined : handleMainMenuClick}
|
onClick={isMobile ? undefined : handleMainMenuClick}
|
||||||
selectedKeys={[isMobile ? selectedSidebarItemKey : selectedKey]}
|
selectedKeys={[isMobile ? selectedSidebarItemKey : selectedKey]}
|
||||||
overflowedIndicator={
|
overflowedIndicator={overflowedIndicator}
|
||||||
<Button
|
|
||||||
type='text'
|
|
||||||
icon={<MenuIcon />}
|
|
||||||
style={{ marginBottom: '4px' }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
),
|
||||||
|
[
|
||||||
|
handleMainMenuClick,
|
||||||
|
headerMenuItems,
|
||||||
|
isElectron,
|
||||||
|
isMobile,
|
||||||
|
overflowedIndicator,
|
||||||
|
selectedKey,
|
||||||
|
selectedSidebarItemKey,
|
||||||
|
showNavigationLabels
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
const navigationTrailing = (
|
const navigationTrailing = (
|
||||||
<>
|
<DashboardNavigationTrailing
|
||||||
<div className='electrobun-webkit-app-region-no-drag'>
|
showControls={showControls}
|
||||||
<Flex
|
isOtherApp={isOtherApp}
|
||||||
gap={'small'}
|
controlsReady={controlsReady}
|
||||||
align='center'
|
isMobile={isMobile}
|
||||||
style={{ marginTop: '-2px', marginRight: '6px' }}
|
isElectron={isElectron}
|
||||||
>
|
unreadCount={unreadCount}
|
||||||
{showControls && (
|
userProfile={userProfile}
|
||||||
<Space style={{ paddingTop: '2px', marginRight: '8px' }}>
|
availableUpdate={availableUpdate}
|
||||||
<WebAppSwitcher />
|
userPopoverOpen={userPopoverOpen}
|
||||||
<KeyboardShortcut
|
onUserPopoverOpenChange={setUserPopoverOpen}
|
||||||
shortcut='alt+q'
|
onSearch={handleSearch}
|
||||||
hint='ALT Q'
|
onNotifications={handleNotifications}
|
||||||
onTrigger={() => showSpotlight()}
|
onCheckUpdates={handleCheckUpdates}
|
||||||
>
|
|
||||||
<Button
|
|
||||||
icon={<SearchIcon />}
|
|
||||||
type='text'
|
|
||||||
style={{ marginTop: '4px' }}
|
|
||||||
onClick={() => showSpotlight()}
|
|
||||||
/>
|
/>
|
||||||
</KeyboardShortcut>
|
|
||||||
<Badge
|
|
||||||
count={unreadCount}
|
|
||||||
size='small'
|
|
||||||
offset={[-5, 8]}
|
|
||||||
style={{ padding: 0, fontWeight: 600 }}
|
|
||||||
>
|
|
||||||
<KeyboardShortcut
|
|
||||||
shortcut='alt+n'
|
|
||||||
hint='ALT N'
|
|
||||||
onTrigger={() => toggleNotificationCenter()}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
icon={<BellIcon />}
|
|
||||||
type='text'
|
|
||||||
style={{ marginTop: '2px' }}
|
|
||||||
onClick={() => toggleNotificationCenter()}
|
|
||||||
/>
|
|
||||||
</KeyboardShortcut>
|
|
||||||
</Badge>
|
|
||||||
</Space>
|
|
||||||
)}
|
|
||||||
{import.meta.env.MODE === 'development' && (
|
|
||||||
<Space>
|
|
||||||
{apiServerState === 'connected' ? (
|
|
||||||
<Tooltip content='Connected to api server'>
|
|
||||||
<Tag
|
|
||||||
color='success'
|
|
||||||
style={{ marginRight: 0 }}
|
|
||||||
icon={<CloudIcon />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
{apiServerState === 'connecting' ? (
|
|
||||||
<Tooltip content='Connecting to api erver...'>
|
|
||||||
<Tag
|
|
||||||
color='warning'
|
|
||||||
style={{ marginRight: 0 }}
|
|
||||||
icon={<LoadingOutlined />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
{apiServerState === 'disconnected' ? (
|
|
||||||
<Tooltip content='Disconnected from api server'>
|
|
||||||
<Tag
|
|
||||||
color='error'
|
|
||||||
style={{ marginRight: 0 }}
|
|
||||||
icon={<CloudIcon />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
<Tooltip content='Developer'>
|
|
||||||
<Tag
|
|
||||||
color='yellow'
|
|
||||||
style={{ marginRight: 0 }}
|
|
||||||
icon={<DeveloperIcon />}
|
|
||||||
onClick={() => {
|
|
||||||
navigate('/dashboard/developer/sessionstorage')
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Space>
|
|
||||||
)}
|
|
||||||
{showControls && userProfile ? (
|
|
||||||
<Space>
|
|
||||||
<Popover
|
|
||||||
content={userPopoverContent}
|
|
||||||
placement='bottomRight'
|
|
||||||
trigger='hover'
|
|
||||||
open={userPopoverOpen}
|
|
||||||
onOpenChange={setUserPopoverOpen}
|
|
||||||
arrow={false}
|
|
||||||
>
|
|
||||||
<Tag style={{ marginRight: 0 }} icon={<PersonIcon />}>
|
|
||||||
{!isMobile && (userProfile?.name || userProfile.username)}
|
|
||||||
</Tag>
|
|
||||||
</Popover>
|
|
||||||
</Space>
|
|
||||||
) : null}
|
|
||||||
{showControls && isElectron && availableUpdate ? (
|
|
||||||
<Tag
|
|
||||||
icon={<CloudIcon />}
|
|
||||||
style={{ cursor: 'pointer', margin: '2px 0 0 0' }}
|
|
||||||
color='cyan'
|
|
||||||
onClick={() => checkForUpdates()}
|
|
||||||
>
|
|
||||||
Update Available
|
|
||||||
</Tag>
|
|
||||||
) : null}
|
|
||||||
</Flex>
|
|
||||||
</div>
|
|
||||||
{isOtherApp ? <DashboardWindowButtons /> : null}
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const navigationLeading = (
|
const navigationLeading = (
|
||||||
@ -414,7 +589,9 @@ const DashboardNavigation = () => {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{showControls && isMobile && menu}
|
{isMobile ? (
|
||||||
|
<NavigationFade visible={showControls}>{menu}</NavigationFade>
|
||||||
|
) : null}
|
||||||
{showDesktopLogo == true ? (
|
{showDesktopLogo == true ? (
|
||||||
<FarmControlLogo
|
<FarmControlLogo
|
||||||
style={{
|
style={{
|
||||||
@ -434,28 +611,38 @@ const DashboardNavigation = () => {
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const windowTitle = (
|
||||||
|
<div
|
||||||
|
className={`dashboard-navigation-window-title ${navigationFadeClass(
|
||||||
|
!showControls
|
||||||
|
)}`}
|
||||||
|
aria-hidden={showControls}
|
||||||
|
inert={showControls ? true : undefined}
|
||||||
|
>
|
||||||
|
<Text type='secondary'>Farm Control</Text>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const navigationCenterBorder = (
|
||||||
|
<div
|
||||||
|
className={`dashboard-navigation-center-border${
|
||||||
|
showControls ? ' dashboard-navigation-center-border-hidden' : ''
|
||||||
|
}`}
|
||||||
|
aria-hidden='true'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
const navigationContents = (
|
const navigationContents = (
|
||||||
<Flex style={{ width: '100%' }} align='center'>
|
<Flex style={{ width: '100%' }} align='center'>
|
||||||
{navigationLeading}
|
{navigationLeading}
|
||||||
<div style={{ flexGrow: 1, minWidth: 0 }}>
|
<div className='dashboard-navigation-center'>
|
||||||
{showControls && !isMobile ? (
|
{!isMobile ? (
|
||||||
<Flex align='center' style={{ width: '100%', minWidth: 0 }}>
|
<NavigationFade visible={showControls} style={NAV_CENTER_FADE_STYLE}>
|
||||||
{menu}
|
{menu}
|
||||||
</Flex>
|
</NavigationFade>
|
||||||
) : null}
|
) : null}
|
||||||
{!showControls && (
|
{windowTitle}
|
||||||
<Text
|
{navigationCenterBorder}
|
||||||
type='secondary'
|
|
||||||
style={{
|
|
||||||
fontSize: '14px',
|
|
||||||
marginLeft: '8px',
|
|
||||||
userSelect: 'none',
|
|
||||||
'--webkit-user-select': 'none'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Farm Control
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{navigationTrailing}
|
{navigationTrailing}
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -473,39 +660,24 @@ const DashboardNavigation = () => {
|
|||||||
className={`electron-navigation-wrapper ${electronDragClass}`}
|
className={`electron-navigation-wrapper ${electronDragClass}`}
|
||||||
style={{ lineHeight: '40px', padding: '0 2px 0 2px' }}
|
style={{ lineHeight: '40px', padding: '0 2px 0 2px' }}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex align='center' className='electron-navigation-leading'>
|
||||||
align='center'
|
|
||||||
className='electron-navigation-leading'
|
|
||||||
style={{ flexGrow: !showControls ? 1 : 0 }}
|
|
||||||
>
|
|
||||||
{navigationLeading}
|
{navigationLeading}
|
||||||
{showControls && !isMobile ? menu : null}
|
|
||||||
{showControls && !isMobile ? (
|
|
||||||
<Divider
|
|
||||||
type='vertical'
|
|
||||||
style={{
|
|
||||||
margin: showNavigationLabels
|
|
||||||
? '3px 1px 0 4px'
|
|
||||||
: '3px 1px 0 0',
|
|
||||||
height: '14px'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{!showControls ? (
|
|
||||||
<Text
|
|
||||||
type='secondary'
|
|
||||||
style={{
|
|
||||||
fontSize: '14px',
|
|
||||||
marginLeft: '8px',
|
|
||||||
userSelect: 'none',
|
|
||||||
'--webkit-user-select': 'none'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Farm Control
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
{showControls && !isMobile ? <DashboardTabs /> : null}
|
<div className='dashboard-navigation-center'>
|
||||||
|
{!isMobile ? (
|
||||||
|
<NavigationFade
|
||||||
|
visible={showControls}
|
||||||
|
style={NAV_ELECTRON_CENTER_FADE_STYLE}
|
||||||
|
>
|
||||||
|
<DashboardElectronCenterItems
|
||||||
|
menu={menu}
|
||||||
|
showNavigationLabels={showNavigationLabels}
|
||||||
|
/>
|
||||||
|
</NavigationFade>
|
||||||
|
) : null}
|
||||||
|
{windowTitle}
|
||||||
|
{navigationCenterBorder}
|
||||||
|
</div>
|
||||||
<Flex align='center' className='electron-navigation-trailing'>
|
<Flex align='center' className='electron-navigation-trailing'>
|
||||||
{navigationTrailing}
|
{navigationTrailing}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user