Part of #145. Twenty-three files across the task list zone: the list and card views, sorting and grouping, multi-select and its toolbar and help sheet, archived tasks, the assignee card, the chore action menu, the nudge/NFC/photo modals, the rich text editor, the scan panel, the notification templates and the keyboard-shortcut toasts. Extends the existing `chores` namespace, so `src/i18n/config.js` is untouched. English only — no translations, no behaviour change. Every t() value is checked against this branch's base: the string must appear character-for-character in the code it replaces (247 call sites). Two values are matched loosely and worth naming: archived.closeMultiSelect in both the archived view and the toolbar. The base builds that tooltip as `${size === 0 ? 'Close' : 'Clear'} multi-select (Esc)`, so only one branch of the ternary exists contiguously in the source. Both keys hold exactly what each branch renders; the tooltip is kept whole so a translator can reorder it. Rebased on current `develop` again after #215 and #216 landed — the dictionary conflict was theirs, not the code's. No code file in this PR was touched upstream in the meantime.
133 lines
3.3 KiB
JavaScript
133 lines
3.3 KiB
JavaScript
import { Button, Chip, Menu, MenuItem, Typography } from '@mui/joy'
|
|
import { useTranslation } from 'react-i18next'
|
|
import IconButton from '@mui/joy/IconButton'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
import { getTextColorFromBackgroundColor } from '../../utils/Colors.jsx'
|
|
|
|
const IconButtonWithMenu = ({
|
|
label,
|
|
k,
|
|
icon,
|
|
options,
|
|
onItemSelect,
|
|
selectedItem,
|
|
setSelectedItem,
|
|
isActive,
|
|
useChips,
|
|
title,
|
|
}) => {
|
|
const { t } = useTranslation('chores')
|
|
const [anchorEl, setAnchorEl] = useState(null)
|
|
const menuRef = useRef(null)
|
|
const menuOptions = Array.isArray(options) ? options : []
|
|
|
|
const handleMenuOpen = event => {
|
|
setAnchorEl(event.currentTarget)
|
|
}
|
|
|
|
const handleMenuClose = () => {
|
|
setAnchorEl(null)
|
|
}
|
|
useEffect(() => {
|
|
document.addEventListener('mousedown', handleMenuOutsideClick)
|
|
return () => {
|
|
document.removeEventListener('mousedown', handleMenuOutsideClick)
|
|
}
|
|
}, [anchorEl])
|
|
|
|
const handleMenuOutsideClick = event => {
|
|
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
|
handleMenuClose()
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{!label && (
|
|
<IconButton
|
|
onClick={handleMenuOpen}
|
|
variant='outlined'
|
|
color={isActive ? 'primary' : 'neutral'}
|
|
size='sm'
|
|
sx={{
|
|
height: 24,
|
|
borderRadius: 24,
|
|
}}
|
|
>
|
|
{icon}
|
|
{label ? label : null}
|
|
</IconButton>
|
|
)}
|
|
{label && (
|
|
<Button
|
|
onClick={handleMenuOpen}
|
|
variant='outlined'
|
|
color={isActive ? 'primary' : 'neutral'}
|
|
size='sm'
|
|
startDecorator={icon}
|
|
sx={{
|
|
height: 24,
|
|
borderRadius: 24,
|
|
}}
|
|
>
|
|
{label}
|
|
</Button>
|
|
)}
|
|
|
|
<Menu
|
|
key={k}
|
|
ref={menuRef}
|
|
anchorEl={anchorEl}
|
|
open={Boolean(anchorEl)}
|
|
onClose={handleMenuClose}
|
|
>
|
|
{title && (
|
|
<MenuItem key={`${k}-title`} disabled>
|
|
<Typography level='body-sm' sx={{ fontWeight: 'bold' }}>
|
|
{title}
|
|
</Typography>
|
|
</MenuItem>
|
|
)}
|
|
{menuOptions.map(item => (
|
|
<MenuItem
|
|
key={`${k}-${item?.id}`}
|
|
onClick={() => {
|
|
onItemSelect(item)
|
|
setSelectedItem?.selectedItem(item.name)
|
|
handleMenuClose()
|
|
}}
|
|
>
|
|
{useChips ? (
|
|
<Chip
|
|
sx={{
|
|
backgroundColor: item.color ? item.color : null,
|
|
color: getTextColorFromBackgroundColor(item.color),
|
|
}}
|
|
>
|
|
{item.name}
|
|
</Chip>
|
|
) : (
|
|
<>
|
|
{item?.icon}
|
|
{item.name}
|
|
</>
|
|
)}
|
|
</MenuItem>
|
|
))}
|
|
{/* {selectedItem && selectedItem !== 'All' && (
|
|
<MenuItem
|
|
id={`${id}cancel-all-filters`}
|
|
onClick={() => {
|
|
onItemSelect(null)
|
|
setSelectedItem?.setSelectedItem('All')
|
|
}}
|
|
>
|
|
{t('filterChip.cancelAll')}
|
|
</MenuItem>
|
|
)} */}
|
|
</Menu>
|
|
</>
|
|
)
|
|
}
|
|
export default IconButtonWithMenu
|