Files
donetick/src/views/Chores/IconButtonWithMenu.jsx
everysingletear 7d36090e46 i18n: extract the chore list and its modals (chores namespace)
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.
2026-08-15 09:03:28 +08:00

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