refactor(Navbar): improve WindowControls visibility and clean up event listener management (#10066)

* refactor(Navbar): improve WindowControls visibility and clean up event listener management

- Updated Navbar component to conditionally render WindowControls based on minappShow state.
- Refactored IPC event listener management in preload script for better clarity and performance.

* feat(WindowControls): replace custom restore icon with a new SVG component

- Introduced a new `WindowRestoreIcon` component with enhanced SVG structure and styling.
- Updated `WindowControls` to use the new `WindowRestoreIcon` for better visual consistency and scalability.

* feat(WindowControls): update WindowRestoreIcon SVG for improved design

- Enhanced the SVG structure of the `WindowRestoreIcon` component with updated dimensions and styling for better visual appeal.
- Adjusted the path and rectangle properties to refine the icon's appearance and maintain consistency across the application.

* lint error
This commit is contained in:
beyondkmp 2025-09-09 23:18:15 +08:00 committed by GitHub
parent 87bdfbeeeb
commit 2962ef79dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 44 additions and 22 deletions

View File

@ -455,9 +455,10 @@ const api = {
isMaximized: (): Promise<boolean> => ipcRenderer.invoke(IpcChannel.Windows_IsMaximized),
onMaximizedChange: (callback: (isMaximized: boolean) => void): (() => void) => {
const channel = IpcChannel.Windows_MaximizedChanged
ipcRenderer.on(channel, (_, isMaximized: boolean) => callback(isMaximized))
const listener = (_: Electron.IpcRendererEvent, isMaximized: boolean) => callback(isMaximized)
ipcRenderer.on(channel, listener)
return () => {
ipcRenderer.removeAllListeners(channel)
ipcRenderer.removeListener(channel, listener)
}
}
}

View File

@ -2,17 +2,45 @@ import { isLinux, isWin } from '@renderer/config/constant'
import { Tooltip } from 'antd'
import { Minus, Square, X } from 'lucide-react'
import { useEffect, useState } from 'react'
import { SVGProps } from 'react'
import { useTranslation } from 'react-i18next'
import { ControlButton, WindowControlsContainer } from './WindowControls.styled'
// Custom restore icon - two overlapping squares like Windows
const RestoreIcon: React.FC<{ size?: number }> = ({ size = 14 }) => (
<svg width={size} height={size} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1">
{/* Back square (top-right) */}
<path d="M 4 2 H 11 V 9 H 9 V 4 H 4 V 2" />
{/* Front square (bottom-left) */}
<rect x="2" y="4" width="7" height="7" />
interface WindowRestoreIconProps extends SVGProps<SVGSVGElement> {
size?: string | number
}
export const WindowRestoreIcon = ({ size = '1.1em', ...props }: WindowRestoreIconProps) => (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-square-icon lucide-square"
version="1.1"
id="svg4"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<defs id="defs1" />
<rect
width="14.165795"
height="14.165795"
x="2.7646871"
y="7.0695167"
rx="1.2377932"
id="rect2"
style={{ strokeWidth: '1.57397' }}
/>
<path
d="m 8.8907777,2.8269172 c -0.5045461,0 -0.9490675,0.2424833 -1.2285866,0.6160739 H 18.993677 c 0.866756,0 1.563332,0.696576 1.563332,1.5633319 v 11.331486 c 0.37359,-0.279519 0.616074,-0.72404 0.616074,-1.228587 V 4.3635407 c 0,-0.8505156 -0.686108,-1.5366235 -1.536624,-1.5366235 z"
style={{ strokeWidth: '0.911647', strokeDasharray: 'none' }}
id="path5"
/>
</svg>
)
@ -67,7 +95,7 @@ const WindowControls: React.FC = () => {
placement="bottom"
mouseEnterDelay={DEFAULT_DELAY}>
<ControlButton onClick={handleMaximize} aria-label={isMaximized ? 'Restore' : 'Maximize'}>
{isMaximized ? <RestoreIcon size={14} /> : <Square size={14} />}
{isMaximized ? <WindowRestoreIcon size={14} /> : <Square size={14} />}
</ControlButton>
</Tooltip>
<Tooltip title={t('navbar.window.close')} placement="bottom" mouseEnterDelay={DEFAULT_DELAY}>

View File

@ -1,6 +1,7 @@
import { isLinux, isMac, isWin } from '@renderer/config/constant'
import { useFullscreen } from '@renderer/hooks/useFullscreen'
import useNavBackgroundColor from '@renderer/hooks/useNavBackgroundColor'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useNavbarPosition } from '@renderer/hooks/useSettings'
import type { FC, PropsWithChildren } from 'react'
import type { HTMLAttributes } from 'react'
@ -13,6 +14,7 @@ type Props = PropsWithChildren & HTMLAttributes<HTMLDivElement>
export const Navbar: FC<Props> = ({ children, ...props }) => {
const backgroundColor = useNavBackgroundColor()
const { isTopNavbar } = useNavbarPosition()
const { minappShow } = useRuntime()
if (isTopNavbar) {
return null
@ -23,7 +25,7 @@ export const Navbar: FC<Props> = ({ children, ...props }) => {
<NavbarContainer {...props} style={{ backgroundColor }}>
{children}
</NavbarContainer>
{(isWin || isLinux) && <WindowControls />}
{!isTopNavbar && !minappShow && <WindowControls />}
</>
)
}

View File

@ -1,7 +1,6 @@
import { Navbar, NavbarMain, NavbarRight } from '@renderer/components/app/Navbar'
import { Navbar, NavbarMain } from '@renderer/components/app/Navbar'
import App from '@renderer/components/MinApp/MinApp'
import Scrollbar from '@renderer/components/Scrollbar'
import { isLinux, isWin } from '@renderer/config/constant'
import { useMinapps } from '@renderer/hooks/useMinapps'
import { useNavbarPosition } from '@renderer/hooks/useSettings'
import { Button, Input } from 'antd'
@ -58,21 +57,13 @@ const AppsPage: FC = () => {
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</NavbarMain>
<NavbarRight
style={{
justifyContent: 'flex-end',
flex: 1,
position: 'relative',
paddingRight: isWin || isLinux ? '144px' : '6px'
}}>
<Button
type="text"
className="nodrag"
icon={<SettingsIcon size={18} color="var(--color-text-2)" />}
onClick={MinappSettingsPopup.show}
/>
</NavbarRight>
</NavbarMain>
</Navbar>
<ContentContainer id="content-container">
<MainContainer>