Tom Butcher 7d79e0d206
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
Enhance MenuButton component in WindowAppMenu
- Added a new `logo` prop to the `MenuButton` component to conditionally adjust padding and margin based on the presence of a logo.
- Updated the `MenuButton` prop types to include the new `logo` boolean, improving flexibility in rendering menu items with logos.
2026-08-09 11:06:02 +01:00

260 lines
6.3 KiB
JavaScript

import { useContext, useMemo } from 'react'
import PropTypes from 'prop-types'
import { Button, Dropdown, Flex } from 'antd'
import { useNavigate } from 'react-router-dom'
import { ElectronContext } from '../context/ElectronContext'
import { useAppUpdateContext } from '../context/AppUpdateContext'
import { getSidebarMenuSections } from '../../../database/Sidebars'
import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall'
const runEditCommand = (command) => {
try {
document.execCommand(command)
} catch (error) {
console.warn(
`[WindowAppMenu] Failed to run edit command: ${command}`,
error
)
}
}
const mapSidebarItemsToMenuItems = (items = [], navigate) =>
items
.map((item, index) => {
if (item?.type === 'divider') {
return { type: 'divider', key: `divider-${index}` }
}
if (item?.children?.length) {
return {
key: item.key || `group-${item.label}-${index}`,
label: item.label,
children: mapSidebarItemsToMenuItems(item.children, navigate)
}
}
if (item?.path) {
return {
key: item.path,
label: item.label,
onClick: () => navigate(item.path)
}
}
return {
key: item.key || `disabled-${item.label}-${index}`,
label: item.label,
disabled: true
}
})
.filter(Boolean)
const MenuButton = ({ label, items, logo }) => (
<Dropdown menu={{ items }} trigger={['click']} placement='bottomLeft'>
<Button
type='text'
size='small'
className='electrobun-webkit-app-region-no-drag'
style={{
height: '28px',
paddingInline: logo ? '6px' : '8px',
marginRight: logo ? '1px' : '0',
fontWeight: 500
}}
>
{label}
</Button>
</Dropdown>
)
MenuButton.propTypes = {
label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
items: PropTypes.array.isRequired,
logo: PropTypes.bool
}
const WindowAppMenu = () => {
const navigate = useNavigate()
const { handleWindowControl } = useContext(ElectronContext)
const { checkForUpdates } = useAppUpdateContext()
const includeDev = import.meta.env.DEV
const viewSections = useMemo(
() => getSidebarMenuSections({ includeDev }),
[includeDev]
)
const appMenuItems = useMemo(
() => [
{
key: 'about',
label: 'About Farm Control',
onClick: () => navigate('/dashboard/management/about')
},
{ type: 'divider' },
{
key: 'check-for-updates',
label: 'Check For Updates...',
onClick: () => checkForUpdates()
},
{ type: 'divider' },
{
key: 'quit',
label: 'Quit Farm Control',
onClick: () => handleWindowControl('quit')
}
],
[checkForUpdates, handleWindowControl, navigate]
)
const fileMenuItems = useMemo(
() => [
{
key: 'close',
label: 'Close Window',
onClick: () => handleWindowControl('close')
}
],
[handleWindowControl]
)
const editMenuItems = useMemo(
() => [
{
key: 'undo',
label: 'Undo',
onClick: () => runEditCommand('undo')
},
{
key: 'redo',
label: 'Redo',
onClick: () => runEditCommand('redo')
},
{ type: 'divider' },
{
key: 'cut',
label: 'Cut',
onClick: () => runEditCommand('cut')
},
{
key: 'copy',
label: 'Copy',
onClick: () => runEditCommand('copy')
},
{
key: 'paste',
label: 'Paste',
onClick: () => runEditCommand('paste')
},
{
key: 'pasteAndMatchStyle',
label: 'Paste and Match Style',
onClick: () => runEditCommand('paste')
},
{
key: 'delete',
label: 'Delete',
onClick: () => runEditCommand('delete')
},
{ type: 'divider' },
{
key: 'selectAll',
label: 'Select All',
onClick: () => runEditCommand('selectAll')
}
],
[]
)
const viewMenuItems = useMemo(() => {
const sectionItems =
viewSections.length > 0
? viewSections.map((section) => ({
key: `view-section-${section.key}`,
label: section.label,
children: mapSidebarItemsToMenuItems(section.items || [], navigate)
}))
: [
{
key: 'no-sidebar-items',
label: 'No sidebar items available',
disabled: true
}
]
if (!includeDev) {
return sectionItems
}
return [
...sectionItems,
{ type: 'divider' },
{
key: 'toggle-devtools',
label: 'Toggle Developer Tools',
onClick: () => handleWindowControl('toggle-devtools')
}
]
}, [handleWindowControl, includeDev, navigate, viewSections])
const windowMenuItems = useMemo(
() => [
{
key: 'minimize',
label: 'Minimize',
onClick: () => handleWindowControl('minimize')
},
{
key: 'zoom',
label: 'Zoom',
onClick: () => handleWindowControl('maximize')
}
],
[handleWindowControl]
)
const menus = useMemo(
() => [
{ key: 'app', label: 'Farm Control', items: appMenuItems },
{ key: 'file', label: 'File', items: fileMenuItems },
{ key: 'edit', label: 'Edit', items: editMenuItems },
{ key: 'view', label: 'View', items: viewMenuItems },
{ key: 'window', label: 'Window', items: windowMenuItems }
],
[appMenuItems, editMenuItems, fileMenuItems, viewMenuItems, windowMenuItems]
)
return (
<Flex
align='center'
className='electrobun-webkit-app-region-no-drag'
style={{ marginRight: '8px', flexShrink: 0, marginLeft: '4px' }}
>
{menus.map((menu) => {
if (menu.key === 'app') {
return (
<MenuButton
key={menu.key}
logo={true}
label={
<FarmControlLogoSmall
style={{
fontSize: '46px',
height: '16px'
}}
/>
}
items={menu.items}
/>
)
}
return (
<MenuButton key={menu.key} label={menu.label} items={menu.items} />
)
})}
</Flex>
)
}
export default WindowAppMenu