reduce the noise by making the color just a small badge
This commit is contained in:
@@ -601,7 +601,11 @@ const ChoreToolbar = ({
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* ── Row 2: pinned filters quick access ──────────────────────────────── */}
|
{/* ── Row 2: pinned filters quick access ──────────────────────────────── */}
|
||||||
{!hasAnyActive && (
|
{/* Stays visible even while a saved filter is active (it self-highlights)
|
||||||
|
so switching between pinned filters never requires clearing first.
|
||||||
|
Only steps aside for an ad-hoc/temp condition set, which has no
|
||||||
|
matching pinned identity to show. */}
|
||||||
|
{tempConditionCount === 0 && (
|
||||||
<CustomFilterChips
|
<CustomFilterChips
|
||||||
filters={savedFilters}
|
filters={savedFilters}
|
||||||
activeFilterId={activeFilterId}
|
activeFilterId={activeFilterId}
|
||||||
@@ -612,8 +616,8 @@ const ChoreToolbar = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Row 3: active filter chips ──────────────────────────────────────── */}
|
{/* ── Row 3: active (temp/ad-hoc) filter chips ──────────────────────────── */}
|
||||||
{hasAnyActive && (
|
{tempConditionCount > 0 && (
|
||||||
<ActiveFilterChips
|
<ActiveFilterChips
|
||||||
chips={inlineChips}
|
chips={inlineChips}
|
||||||
onOpen={openFilterSheet}
|
onOpen={openFilterSheet}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
Check,
|
Check,
|
||||||
Delete,
|
Delete,
|
||||||
Edit,
|
Edit,
|
||||||
|
MoreVert,
|
||||||
Settings,
|
Settings,
|
||||||
Star,
|
Star,
|
||||||
StarBorder,
|
StarBorder,
|
||||||
@@ -66,158 +67,163 @@ const CustomFilterChips = ({
|
|||||||
handleMenuClose()
|
handleMenuClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedFilters = [...filters].sort((a, b) => {
|
const pinnedFilters = filters
|
||||||
if (a.isPinned && !b.isPinned) return -1
|
.filter(f => f.isPinned)
|
||||||
if (!a.isPinned && b.isPinned) return 1
|
.sort((a, b) => (b.usageCount || 0) - (a.usageCount || 0))
|
||||||
return (b.usageCount || 0) - (a.usageCount || 0)
|
|
||||||
})
|
if (pinnedFilters.length === 0) return null
|
||||||
if (sortedFilters.filter(f => f.isPinned).length === 0) return null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box sx={{ position: 'relative' }}>
|
||||||
sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
gap: 1,
|
display: 'flex',
|
||||||
overflowX: 'auto',
|
gap: 0.75,
|
||||||
py: 1,
|
overflowX: 'auto',
|
||||||
'&::-webkit-scrollbar': {
|
py: 1,
|
||||||
display: 'none',
|
pr: 2,
|
||||||
},
|
'&::-webkit-scrollbar': {
|
||||||
scrollbarWidth: 'none', // Firefox
|
display: 'none',
|
||||||
msOverflowStyle: 'none', // IE and Edge
|
},
|
||||||
}}
|
scrollbarWidth: 'none', // Firefox
|
||||||
>
|
msOverflowStyle: 'none', // IE and Edge
|
||||||
{sortedFilters.map(filter => {
|
}}
|
||||||
const isActive = activeFilterId === filter.id
|
>
|
||||||
const hasWarning = !filter.isValid
|
{pinnedFilters.map(filter => {
|
||||||
const hasCustomColor = !!filter.color && !hasWarning
|
const isActive = activeFilterId === filter.id
|
||||||
const textColor = hasCustomColor
|
const hasWarning = !filter.isValid
|
||||||
? getTextColorFromBackgroundColor(filter.color)
|
const badgeColor = filter.color || 'var(--joy-palette-neutral-400)'
|
||||||
: undefined
|
const badgeTextColor = filter.color
|
||||||
|
? getTextColorFromBackgroundColor(filter.color)
|
||||||
|
: '#ffffff'
|
||||||
|
const displayCount =
|
||||||
|
filter.count > 99 ? '99+' : (filter.count ?? 0)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
key={filter.id}
|
key={filter.id}
|
||||||
title={
|
title={
|
||||||
hasWarning
|
hasWarning
|
||||||
? `Filter has issues: ${filter.validationIssues?.join(', ')}`
|
? `Filter has issues: ${filter.validationIssues?.join(', ')}`
|
||||||
: `${filter.description ? filter.description + ' - ' : ''}${filter.count} tasks${filter.overdueCount > 0 ? ` (${filter.overdueCount} overdue)` : ''}`
|
: `${filter.description ? filter.description + ' - ' : ''}${filter.count} tasks${filter.overdueCount > 0 ? ` (${filter.overdueCount} overdue)` : ''}`
|
||||||
}
|
}
|
||||||
placement='bottom'
|
placement='bottom'
|
||||||
>
|
>
|
||||||
<div onClick={() => !hasWarning && onFilterClick(filter.id)}>
|
|
||||||
<Chip
|
<Chip
|
||||||
variant='solid'
|
variant={isActive ? 'solid' : 'outlined'}
|
||||||
|
color={hasWarning ? 'warning' : isActive ? 'primary' : 'neutral'}
|
||||||
size='md'
|
size='md'
|
||||||
onContextMenu={e => handleContextMenu(e, filter)}
|
onClick={() => !hasWarning && onFilterClick(filter.id)}
|
||||||
sx={{
|
sx={{
|
||||||
cursor: hasWarning ? 'not-allowed' : 'pointer',
|
cursor: hasWarning ? 'not-allowed' : 'pointer',
|
||||||
transition: 'all 0.2s ease',
|
transition: 'background-color 0.15s ease, color 0.15s ease',
|
||||||
px: 1.0,
|
px: 1,
|
||||||
py: 0.5,
|
|
||||||
height: 32,
|
height: 32,
|
||||||
display: 'flex',
|
flexShrink: 0,
|
||||||
alignItems: 'center',
|
fontWeight: isActive ? 600 : 500,
|
||||||
|
'&:hover': {
|
||||||
opacity: hasWarning ? 0.7 : isActive ? 1 : 0.85,
|
backgroundColor: isActive ? undefined : 'neutral.softHoverBg',
|
||||||
...(hasCustomColor && {
|
},
|
||||||
backgroundColor: `${filter.color} !important`,
|
|
||||||
color: `${textColor} !important`,
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor: `${filter.color} !important`,
|
|
||||||
filter: 'brightness(0.95)',
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}}
|
}}
|
||||||
startDecorator={
|
startDecorator={
|
||||||
<Box sx={{ display: 'flex', gap: 0.5, alignItems: 'center' }}>
|
!hasWarning && (
|
||||||
{isActive ? (
|
<Box
|
||||||
<Check
|
sx={{
|
||||||
sx={{
|
width: 20,
|
||||||
fontSize: '1rem',
|
height: 20,
|
||||||
color: hasCustomColor ? textColor : 'primary.500',
|
borderRadius: '50%',
|
||||||
}}
|
bgcolor: badgeColor,
|
||||||
/>
|
display: 'flex',
|
||||||
) : (
|
alignItems: 'center',
|
||||||
<Chip
|
justifyContent: 'center',
|
||||||
size='sm'
|
flexShrink: 0,
|
||||||
variant='solid'
|
}}
|
||||||
sx={{
|
>
|
||||||
...(hasCustomColor
|
{isActive ? (
|
||||||
? {
|
<Check
|
||||||
bgcolor:
|
sx={{ fontSize: '0.85rem', color: badgeTextColor }}
|
||||||
textColor === '#FFFFFF'
|
/>
|
||||||
? '#00000040'
|
) : (
|
||||||
: '#FFFFFF40',
|
<Typography
|
||||||
color: textColor,
|
level='body-xs'
|
||||||
border: `1px solid ${textColor}30`,
|
sx={{
|
||||||
}
|
fontSize: '0.65rem',
|
||||||
: {}),
|
lineHeight: 1,
|
||||||
}}
|
color: badgeTextColor,
|
||||||
color={
|
}}
|
||||||
hasCustomColor
|
>
|
||||||
? undefined
|
{displayCount}
|
||||||
: hasWarning
|
</Typography>
|
||||||
? 'warning'
|
)}
|
||||||
: 'neutral'
|
</Box>
|
||||||
}
|
)
|
||||||
>
|
}
|
||||||
{filter.count}
|
endDecorator={
|
||||||
</Chip>
|
<IconButton
|
||||||
)}
|
size='sm'
|
||||||
</Box>
|
variant='plain'
|
||||||
|
color='neutral'
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleContextMenu(e, filter)
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
'--IconButton-size': '20px',
|
||||||
|
ml: 0.25,
|
||||||
|
opacity: 0.6,
|
||||||
|
'&:hover': { opacity: 1, backgroundColor: 'transparent' },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MoreVert sx={{ fontSize: '0.95rem' }} />
|
||||||
|
</IconButton>
|
||||||
}
|
}
|
||||||
// endDecorator={
|
|
||||||
// <Box sx={{ display: 'flex', gap: 0.5, alignItems: 'center' }}>
|
|
||||||
// {hasWarning && <Warning sx={{ fontSize: '1rem' }} />}
|
|
||||||
// {!hasWarning && filter.overdueCount > 0 && (
|
|
||||||
// <Chip size='sm' color='danger' variant='solid'>
|
|
||||||
// {filter.overdueCount}
|
|
||||||
// </Chip>
|
|
||||||
// )}
|
|
||||||
// </Box>
|
|
||||||
// }
|
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
level='body-sm'
|
level='body-sm'
|
||||||
fontWeight={isActive ? 'md' : 'normal'}
|
fontWeight='inherit'
|
||||||
sx={{
|
sx={{
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
maxWidth: 100,
|
maxWidth: 100,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
display: 'flex',
|
color: 'inherit',
|
||||||
alignItems: 'center',
|
|
||||||
height: '100%',
|
|
||||||
lineHeight: 1,
|
|
||||||
...(hasCustomColor && {
|
|
||||||
color: textColor,
|
|
||||||
}),
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{filter.name}
|
{filter.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Chip>
|
</Chip>
|
||||||
</div>
|
</Tooltip>
|
||||||
</Tooltip>
|
)
|
||||||
)
|
})}
|
||||||
})}
|
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
size='sm'
|
color='neutral'
|
||||||
|
size='sm'
|
||||||
|
sx={{ borderRadius: 24, flexShrink: 0 }}
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
navigate('/filters')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Settings sx={{ fontSize: '1.1rem' }} />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Fade hint that more chips are scrollable off the trailing edge */}
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 24,
|
pointerEvents: 'none',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
width: 24,
|
||||||
|
background:
|
||||||
|
'linear-gradient(to right, transparent, var(--joy-palette-background-surface, #fff))',
|
||||||
}}
|
}}
|
||||||
onClick={e => {
|
/>
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
navigate('/filters')
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Settings />
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<Menu
|
<Menu
|
||||||
anchorEl={menuAnchor}
|
anchorEl={menuAnchor}
|
||||||
|
|||||||
Reference in New Issue
Block a user