farmcontrol-ui/src/components/Dashboard/common/MacOSTrafficLights.jsx
Tom Butcher 8e3ce20bb6
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
Add macOS traffic light controls and enhance window management
- Introduced a new MacOSTrafficLights component to manage window controls (close, minimize, fullscreen) with custom SVG icons.
- Updated DashboardWindowButtons to integrate the new traffic light controls for macOS.
- Enhanced window control functionality in mainWindow.js to toggle fullscreen mode.
- Added CSS styles for traffic light indicators to improve visual representation.
- Refactored DashboardNavigation to streamline the handling of fullscreen state.
2026-08-02 15:57:54 +01:00

213 lines
6.0 KiB
JavaScript

import { useContext, useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import { Flex } from 'antd'
import { ElectronContext } from '../context/ElectronContext'
import CloseColored from '../../../../assets/trafficlights/closecolored.svg?react'
import CloseHoverColored from '../../../../assets/trafficlights/closehovercolored.svg?react'
import CloseDownColored from '../../../../assets/trafficlights/closedowncolored.svg?react'
import MinimizeColored from '../../../../assets/trafficlights/minimizecolored.svg?react'
import MinimizeHoverColored from '../../../../assets/trafficlights/minimizehovercolored.svg?react'
import MinimizeDownColored from '../../../../assets/trafficlights/minimizedowncolored.svg?react'
import FullscreenColored from '../../../../assets/trafficlights/fullscreencolored.svg?react'
import FullscreenHoverColored from '../../../../assets/trafficlights/fullscreenhovercolored.svg?react'
import FullscreenDownColored from '../../../../assets/trafficlights/fullscreendowncolored.svg?react'
import ExitFullscreenHoverColored from '../../../../assets/trafficlights/exitfullscreenhovercolored.svg?react'
import ExitFullscreenDownColored from '../../../../assets/trafficlights/exitfullscreendowncolored.svg?react'
import NoFocus from '../../../../assets/trafficlights/nofocus.svg?react'
const TRAFFIC_LIGHT_SIZE = 12
const TRAFFIC_LIGHT_BUTTON_STYLE = {
width: TRAFFIC_LIGHT_SIZE,
height: TRAFFIC_LIGHT_SIZE,
padding: 0,
border: 'none',
background: 'transparent',
cursor: 'default',
lineHeight: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}
const TRAFFIC_LIGHT_ICON_STYLE = {
width: TRAFFIC_LIGHT_SIZE,
height: TRAFFIC_LIGHT_SIZE,
display: 'block'
}
function getTrafficLightIcon({ isEnabled, isFocused, isActive, isHovered, icons }) {
if (!isEnabled || !isFocused) {
return icons.noFocus
}
if (isActive) {
return icons.down
}
if (isHovered) {
return icons.hover
}
return icons.colored
}
const TrafficLightButton = ({
action,
icons,
isGroupHovered,
activeAction,
isEnabled,
isWindowFocused,
onActive,
onAction
}) => {
const Icon = getTrafficLightIcon({
isEnabled,
isFocused: isWindowFocused,
isActive: activeAction === action,
isHovered: isGroupHovered,
icons
})
const handleMouseDown = (event) => {
if (!isEnabled) return
event.preventDefault()
event.stopPropagation()
onActive(action)
onAction(action)
}
return (
<button
type='button'
aria-label={action}
aria-disabled={!isEnabled}
disabled={!isEnabled}
className='electrobun-webkit-app-region-no-drag'
style={TRAFFIC_LIGHT_BUTTON_STYLE}
onMouseDown={handleMouseDown}
onMouseUp={isEnabled ? () => onActive(null) : undefined}
>
<Icon style={TRAFFIC_LIGHT_ICON_STYLE} />
</button>
)
}
TrafficLightButton.propTypes = {
action: PropTypes.string.isRequired,
icons: PropTypes.shape({
colored: PropTypes.elementType.isRequired,
hover: PropTypes.elementType.isRequired,
down: PropTypes.elementType.isRequired,
noFocus: PropTypes.elementType.isRequired
}).isRequired,
isGroupHovered: PropTypes.bool.isRequired,
activeAction: PropTypes.string,
isEnabled: PropTypes.bool.isRequired,
isWindowFocused: PropTypes.bool.isRequired,
onActive: PropTypes.func.isRequired,
onAction: PropTypes.func.isRequired
}
const MacOSTrafficLights = () => {
const { handleWindowControl, isFullScreen } = useContext(ElectronContext)
const [isGroupHovered, setIsGroupHovered] = useState(false)
const [activeAction, setActiveAction] = useState(null)
const [isWindowFocused, setIsWindowFocused] = useState(
() => document.hasFocus?.() ?? true
)
useEffect(() => {
const handleFocus = () => setIsWindowFocused(true)
const handleBlur = () => setIsWindowFocused(false)
window.addEventListener('focus', handleFocus)
window.addEventListener('blur', handleBlur)
return () => {
window.removeEventListener('focus', handleFocus)
window.removeEventListener('blur', handleBlur)
}
}, [])
const fullscreenIcons = isFullScreen
? {
colored: FullscreenColored,
hover: ExitFullscreenHoverColored,
down: ExitFullscreenDownColored,
noFocus: NoFocus
}
: {
colored: FullscreenColored,
hover: FullscreenHoverColored,
down: FullscreenDownColored,
noFocus: NoFocus
}
const handleAction = (action) => {
handleWindowControl(action)
}
return (
<Flex
className='electrobun-webkit-app-region-no-drag'
style={{
width: '56.6px',
marginLeft: '12.5px',
marginRight: '12.5px',
position: 'relative',
zIndex: 9999
}}
gap={8}
onMouseEnter={() => setIsGroupHovered(true)}
onMouseLeave={() => {
setIsGroupHovered(false)
setActiveAction(null)
}}
>
<TrafficLightButton
action='close'
icons={{
colored: CloseColored,
hover: CloseHoverColored,
down: CloseDownColored,
noFocus: NoFocus
}}
isGroupHovered={isGroupHovered}
activeAction={activeAction}
isEnabled
isWindowFocused={isWindowFocused}
onActive={setActiveAction}
onAction={handleAction}
/>
<TrafficLightButton
action='minimize'
icons={{
colored: MinimizeColored,
hover: MinimizeHoverColored,
down: MinimizeDownColored,
noFocus: NoFocus
}}
isGroupHovered={isGroupHovered}
activeAction={activeAction}
isEnabled={!isFullScreen}
isWindowFocused={isWindowFocused}
onActive={setActiveAction}
onAction={handleAction}
/>
<TrafficLightButton
action='fullscreen'
icons={fullscreenIcons}
isGroupHovered={isGroupHovered}
activeAction={activeAction}
isEnabled
isWindowFocused={isWindowFocused}
onActive={setActiveAction}
onAction={handleAction}
/>
</Flex>
)
}
export default MacOSTrafficLights