Add more keyboard shortcuts for search focus and multi-select mode toggle in MyChores
This commit is contained in:
@@ -420,7 +420,13 @@ const CompactChoreCard = ({
|
||||
borderRadius: '16px',
|
||||
},
|
||||
}}
|
||||
onClick={() => navigate(`/chores/${chore.id}`)}
|
||||
onClick={() => {
|
||||
if (isMultiSelectMode) {
|
||||
onSelectionToggle()
|
||||
} else {
|
||||
navigate(`/chores/${chore.id}`)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* Multi-select checkbox */}
|
||||
{isMultiSelectMode && (
|
||||
@@ -471,8 +477,50 @@ const CompactChoreCard = ({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{/* Left side - Content */}
|
||||
{/* Complete Button - Left side like typical task apps */}
|
||||
<IconButton
|
||||
variant='soft'
|
||||
color='success'
|
||||
size='sm'
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
handleTaskCompletion()
|
||||
}}
|
||||
disabled={isPendingCompletion || notInCompletionWindow(chore)}
|
||||
sx={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: '50%',
|
||||
mr: 1.5,
|
||||
flexShrink: 0,
|
||||
bgcolor: 'success.softBg',
|
||||
color: 'success.600',
|
||||
border: '1px solid',
|
||||
borderColor: 'success.200',
|
||||
transition: 'all 0.2s ease',
|
||||
'&:hover': {
|
||||
bgcolor: 'success.solidBg',
|
||||
color: 'success.solidColor',
|
||||
borderColor: 'success.400',
|
||||
transform: 'scale(1.05)',
|
||||
},
|
||||
'&:active': {
|
||||
transform: 'scale(0.95)',
|
||||
},
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
transform: 'none',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{isPendingCompletion ? (
|
||||
<CircularProgress size='sm' />
|
||||
) : (
|
||||
<Check sx={{ fontSize: 16 }} />
|
||||
)}
|
||||
</IconButton>
|
||||
|
||||
{/* Content - Center */}
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
@@ -482,7 +530,7 @@ const CompactChoreCard = ({
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
{/* Line 1: Name + Due Date + Frequency */}
|
||||
{/* Line 1: Name + Due Date */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@@ -491,36 +539,35 @@ const CompactChoreCard = ({
|
||||
mb: 0.25,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
{/* Chore Name */}
|
||||
<Typography
|
||||
level='title-sm'
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
minWidth: 0,
|
||||
fontWeight: 600,
|
||||
fontSize: 14,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
mr: 1,
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
{/* Chore Name */}
|
||||
<Typography
|
||||
level='title-sm'
|
||||
sx={{
|
||||
fontWeight: 600,
|
||||
fontSize: 14,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
mr: 1,
|
||||
}}
|
||||
>
|
||||
{chore.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
{chore.name}
|
||||
</Typography>
|
||||
|
||||
{/* Due Date */}
|
||||
{/* Due Date - Inline with name */}
|
||||
<Chip
|
||||
variant='soft'
|
||||
size='sm'
|
||||
color={getDueDateColor(chore.nextDueDate)}
|
||||
sx={{ fontSize: 10, height: 20, flexShrink: 0 }}
|
||||
sx={{
|
||||
fontSize: 10,
|
||||
height: 18,
|
||||
px: 0.75,
|
||||
flexShrink: 0,
|
||||
ml: 1,
|
||||
}}
|
||||
>
|
||||
{getDueDateText(chore.nextDueDate)}
|
||||
</Chip>
|
||||
@@ -586,63 +633,31 @@ const CompactChoreCard = ({
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Right side - Actions */}
|
||||
<Box
|
||||
{/* Right side - Action Menu only */}
|
||||
<ChoreActionMenu
|
||||
variant='plain'
|
||||
chore={chore}
|
||||
onChoreUpdate={onChoreUpdate}
|
||||
onChoreRemove={onChoreRemove}
|
||||
onCompleteWithNote={() => setIsCompleteWithNoteModalOpen(true)}
|
||||
onCompleteWithPastDate={() =>
|
||||
setIsCompleteWithPastDateModalOpen(true)
|
||||
}
|
||||
onChangeAssignee={() => setIsChangeAssigneeModalOpen(true)}
|
||||
onChangeDueDate={() => setIsChangeDueDateModalOpen(true)}
|
||||
onWriteNFC={() => setIsNFCModalOpen(true)}
|
||||
onDelete={handleDelete}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.25,
|
||||
width: 32,
|
||||
height: 32,
|
||||
color: 'text.tertiary',
|
||||
flexShrink: 0,
|
||||
'&:hover': {
|
||||
color: 'text.secondary',
|
||||
bgcolor: 'background.level1',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* Complete Button */}
|
||||
<IconButton
|
||||
variant='solid'
|
||||
color='success'
|
||||
size='sm'
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
handleTaskCompletion()
|
||||
}}
|
||||
disabled={isPendingCompletion || notInCompletionWindow(chore)}
|
||||
sx={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: '50%',
|
||||
}}
|
||||
>
|
||||
{isPendingCompletion ? (
|
||||
<CircularProgress size='sm' color='success' />
|
||||
) : (
|
||||
<Check sx={{ fontSize: 16 }} />
|
||||
)}
|
||||
</IconButton>
|
||||
|
||||
{/* Chore Action Menu */}
|
||||
<ChoreActionMenu
|
||||
variant='plain'
|
||||
chore={chore}
|
||||
onChoreUpdate={onChoreUpdate}
|
||||
onChoreRemove={onChoreRemove}
|
||||
onCompleteWithNote={() => setIsCompleteWithNoteModalOpen(true)}
|
||||
onCompleteWithPastDate={() =>
|
||||
setIsCompleteWithPastDateModalOpen(true)
|
||||
}
|
||||
onChangeAssignee={() => setIsChangeAssigneeModalOpen(true)}
|
||||
onChangeDueDate={() => setIsChangeDueDateModalOpen(true)}
|
||||
onWriteNFC={() => setIsNFCModalOpen(true)}
|
||||
onDelete={handleDelete}
|
||||
sx={{
|
||||
width: 28,
|
||||
marginRight: -3,
|
||||
height: 28,
|
||||
// opacity: 0.6,
|
||||
'&:hover': {
|
||||
opacity: 0,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* All modals (same as original) */}
|
||||
|
||||
@@ -171,8 +171,26 @@ const MyChores = () => {
|
||||
return
|
||||
}
|
||||
|
||||
// Ctrl/Cmd + F to focus search input:
|
||||
else if ((event.ctrlKey || event.metaKey) && event.key === 'f') {
|
||||
event.preventDefault()
|
||||
searchInputRef.current?.focus()
|
||||
return
|
||||
}
|
||||
|
||||
// Ctrl/Cmd + S Toggle Multi-select mode
|
||||
else if ((event.ctrlKey || event.metaKey) && event.key === 's') {
|
||||
event.preventDefault()
|
||||
toggleMultiSelectMode()
|
||||
return
|
||||
}
|
||||
|
||||
// Ctrl/Cmd + A to select all - works both in and out of multi-select mode
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === 'a') {
|
||||
else if (
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.key === 'a' &&
|
||||
!['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)
|
||||
) {
|
||||
event.preventDefault()
|
||||
if (!isMultiSelectMode) {
|
||||
// Enable multi-select mode and select all visible tasks
|
||||
@@ -248,7 +266,6 @@ const MyChores = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Multi-select keyboard shortcuts (only when in multi-select mode)
|
||||
@@ -812,17 +829,6 @@ const MyChores = () => {
|
||||
performers.length === 0 ||
|
||||
choresLoading
|
||||
) {
|
||||
console.log(
|
||||
'userProfile:',
|
||||
userProfile,
|
||||
'userLabelsLoading:',
|
||||
userLabelsLoading,
|
||||
'performers:',
|
||||
performers.length,
|
||||
'choresLoading:',
|
||||
choresLoading,
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<LoadingComponent />
|
||||
@@ -848,7 +854,7 @@ const MyChores = () => {
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
ref={searchInputRef}
|
||||
slotProps={{ input: { ref: searchInputRef } }}
|
||||
placeholder='Search'
|
||||
value={searchTerm}
|
||||
onFocus={() => {
|
||||
|
||||
Reference in New Issue
Block a user