feat: enhance KanbanPageClient and KeyboardShortcuts with new functionality
- Added `toggleFontSize` and `handleToggleDueDateFilter` to `KanbanPageClient` for improved user control over font size and due date visibility. - Replaced `useKeyboardShortcuts` with `useGlobalKeyboardShortcuts` for better shortcut management across components. - Updated keyboard shortcuts in `KeyboardShortcutsContext` to include new actions for toggling objectives, due date filters, and font size. - Refined `KeyboardShortcutsModal` layout for better usability and consistency. - Removed deprecated `useKeyboardShortcuts` hook to streamline codebase.
This commit is contained in:
@@ -21,7 +21,7 @@ interface KanbanPageClientProps {
|
|||||||
|
|
||||||
function KanbanPageContent() {
|
function KanbanPageContent() {
|
||||||
const { syncing, createTask, activeFiltersCount, kanbanFilters, setKanbanFilters } = useTasksContext();
|
const { syncing, createTask, activeFiltersCount, kanbanFilters, setKanbanFilters } = useTasksContext();
|
||||||
const { preferences, updateViewPreferences } = useUserPreferences();
|
const { preferences, updateViewPreferences, toggleFontSize } = useUserPreferences();
|
||||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||||
const isMobile = useIsMobile(768); // Tailwind md breakpoint
|
const isMobile = useIsMobile(768); // Tailwind md breakpoint
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
@@ -50,6 +50,13 @@ function KanbanPageContent() {
|
|||||||
updateViewPreferences({ swimlanesByTags: !swimlanesByTags });
|
updateViewPreferences({ swimlanesByTags: !swimlanesByTags });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleDueDateFilter = () => {
|
||||||
|
setKanbanFilters({
|
||||||
|
...kanbanFilters,
|
||||||
|
showWithDueDate: !kanbanFilters.showWithDueDate
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Handler pour la création de tâche depuis la barre de contrôles
|
// Handler pour la création de tâche depuis la barre de contrôles
|
||||||
const handleCreateTask = async (data: CreateTaskData) => {
|
const handleCreateTask = async (data: CreateTaskData) => {
|
||||||
await createTask(data);
|
await createTask(data);
|
||||||
@@ -62,6 +69,9 @@ function KanbanPageContent() {
|
|||||||
onToggleFilters: handleToggleFilters,
|
onToggleFilters: handleToggleFilters,
|
||||||
onToggleCompactView: handleToggleCompactView,
|
onToggleCompactView: handleToggleCompactView,
|
||||||
onToggleSwimlanes: handleToggleSwimlanes,
|
onToggleSwimlanes: handleToggleSwimlanes,
|
||||||
|
onToggleObjectives: handleToggleObjectives,
|
||||||
|
onToggleDueDateFilter: handleToggleDueDateFilter,
|
||||||
|
onToggleFontSize: toggleFontSize,
|
||||||
onOpenSearch: () => {
|
onOpenSearch: () => {
|
||||||
// Focus sur le champ de recherche dans les contrôles desktop
|
// Focus sur le champ de recherche dans les contrôles desktop
|
||||||
const searchInput = document.querySelector('input[placeholder*="Rechercher"]') as HTMLInputElement;
|
const searchInput = document.querySelector('input[placeholder*="Rechercher"]') as HTMLInputElement;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useKeyboardShortcuts } from '@/hooks/useKeyboardShortcuts';
|
import { useGlobalKeyboardShortcuts } from '@/hooks/useGlobalKeyboardShortcuts';
|
||||||
import { useKeyboardShortcutsModal } from '@/contexts/KeyboardShortcutsContext';
|
import { useKeyboardShortcutsModal } from '@/contexts/KeyboardShortcutsContext';
|
||||||
import { KeyboardShortcutsModal } from '@/components/ui/KeyboardShortcutsModal';
|
import { KeyboardShortcutsModal } from '@/components/ui/KeyboardShortcutsModal';
|
||||||
|
|
||||||
export function KeyboardShortcuts() {
|
export function KeyboardShortcuts() {
|
||||||
useKeyboardShortcuts();
|
useGlobalKeyboardShortcuts();
|
||||||
const { isOpen, shortcuts, closeModal } = useKeyboardShortcutsModal();
|
const { isOpen, shortcuts, closeModal } = useKeyboardShortcutsModal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ interface KeyboardShortcutsModalProps {
|
|||||||
|
|
||||||
function KeyBadge({ keyText }: { keyText: string }) {
|
function KeyBadge({ keyText }: { keyText: string }) {
|
||||||
return (
|
return (
|
||||||
<kbd className="inline-flex items-center px-2 py-1 text-xs font-mono font-medium text-[var(--foreground)] bg-[var(--card)] border border-[var(--border)] rounded-md shadow-sm">
|
<kbd className="inline-flex items-center px-1.5 py-0.5 text-xs font-mono font-medium text-[var(--foreground)] bg-[var(--card)] border border-[var(--border)] rounded shadow-sm">
|
||||||
{keyText}
|
{keyText}
|
||||||
</kbd>
|
</kbd>
|
||||||
);
|
);
|
||||||
@@ -26,10 +26,10 @@ function KeyBadge({ keyText }: { keyText: string }) {
|
|||||||
|
|
||||||
function ShortcutRow({ shortcut }: { shortcut: KeyboardShortcut }) {
|
function ShortcutRow({ shortcut }: { shortcut: KeyboardShortcut }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between py-2 px-3 rounded-md hover:bg-[var(--card-hover)] transition-colors">
|
<div className="flex items-center justify-between py-1 px-2 rounded hover:bg-[var(--card-hover)] transition-colors">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1">
|
||||||
{shortcut.keys.map((key, index) => (
|
{shortcut.keys.map((key, index) => (
|
||||||
<div key={index} className="flex items-center gap-1">
|
<div key={index} className="flex items-center gap-0.5">
|
||||||
<KeyBadge keyText={key} />
|
<KeyBadge keyText={key} />
|
||||||
{index < shortcut.keys.length - 1 && (
|
{index < shortcut.keys.length - 1 && (
|
||||||
<span className="text-[var(--muted-foreground)] text-xs">+</span>
|
<span className="text-[var(--muted-foreground)] text-xs">+</span>
|
||||||
@@ -37,7 +37,7 @@ function ShortcutRow({ shortcut }: { shortcut: KeyboardShortcut }) {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm text-[var(--foreground)] font-mono">
|
<span className="text-xs text-[var(--foreground)] font-mono">
|
||||||
{shortcut.description}
|
{shortcut.description}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -61,14 +61,14 @@ export function KeyboardShortcutsModal({
|
|||||||
}, {} as Record<string, KeyboardShortcut[]>);
|
}, {} as Record<string, KeyboardShortcut[]>);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onClose={onClose} title={title} size="lg">
|
<Modal isOpen={isOpen} onClose={onClose} title={title} size="md">
|
||||||
<div className="space-y-6">
|
<div className="space-y-4">
|
||||||
{Object.entries(groupedShortcuts).map(([category, categoryShortcuts]) => (
|
{Object.entries(groupedShortcuts).map(([category, categoryShortcuts]) => (
|
||||||
<div key={category}>
|
<div key={category}>
|
||||||
<h3 className="text-sm font-mono font-semibold text-[var(--primary)] uppercase tracking-wider mb-3">
|
<h3 className="text-xs font-mono font-semibold text-[var(--primary)] uppercase tracking-wider mb-2">
|
||||||
{category}
|
{category}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-1">
|
<div className="space-y-0.5">
|
||||||
{categoryShortcuts.map((shortcut, index) => (
|
{categoryShortcuts.map((shortcut, index) => (
|
||||||
<ShortcutRow key={index} shortcut={shortcut} />
|
<ShortcutRow key={index} shortcut={shortcut} />
|
||||||
))}
|
))}
|
||||||
@@ -77,8 +77,8 @@ export function KeyboardShortcutsModal({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{shortcuts.length === 0 && (
|
{shortcuts.length === 0 && (
|
||||||
<div className="text-center py-8">
|
<div className="text-center py-6">
|
||||||
<p className="text-[var(--muted-foreground)] font-mono">
|
<p className="text-xs text-[var(--muted-foreground)] font-mono">
|
||||||
Aucun raccourci disponible sur cette page
|
Aucun raccourci disponible sur cette page
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,9 +86,9 @@ export function KeyboardShortcutsModal({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer avec info */}
|
{/* Footer avec info */}
|
||||||
<div className="mt-6 pt-4 border-t border-[var(--border)]/50">
|
<div className="mt-4 pt-3 border-t border-[var(--border)]/50">
|
||||||
<p className="text-xs text-[var(--muted-foreground)] font-mono text-center">
|
<p className="text-xs text-[var(--muted-foreground)] font-mono text-center">
|
||||||
Appuyez sur <KeyBadge keyText="Esc" /> pour fermer cette fenêtre
|
<KeyBadge keyText="Esc" /> pour fermer
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -23,13 +23,13 @@ const PAGE_SHORTCUTS: PageShortcuts = {
|
|||||||
category: 'Navigation'
|
category: 'Navigation'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keys: ['Cmd', 'Ctrl', 'D'],
|
keys: ['Shift', 'Q'],
|
||||||
description: 'Basculer le thème',
|
description: 'Basculer le thème',
|
||||||
category: 'Apparence'
|
category: 'Apparence'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keys: ['Cmd', 'Ctrl', 'T'],
|
keys: ['Shift', 'W'],
|
||||||
description: 'Changer de thème sombre',
|
description: 'Faire tourner les thèmes dark',
|
||||||
category: 'Apparence'
|
category: 'Apparence'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -42,7 +42,7 @@ const PAGE_SHORTCUTS: PageShortcuts = {
|
|||||||
// Dashboard
|
// Dashboard
|
||||||
'/': [
|
'/': [
|
||||||
{
|
{
|
||||||
keys: ['Cmd', 'Ctrl', 'K'],
|
keys: ['Shift', 'K'],
|
||||||
description: 'Vers Kanban',
|
description: 'Vers Kanban',
|
||||||
category: 'Navigation'
|
category: 'Navigation'
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ const PAGE_SHORTCUTS: PageShortcuts = {
|
|||||||
// Kanban
|
// Kanban
|
||||||
'/kanban': [
|
'/kanban': [
|
||||||
{
|
{
|
||||||
keys: ['Cmd', 'Ctrl', 'N'],
|
keys: ['Shift', 'N'],
|
||||||
description: 'Créer une nouvelle tâche',
|
description: 'Créer une nouvelle tâche',
|
||||||
category: 'Actions'
|
category: 'Actions'
|
||||||
},
|
},
|
||||||
@@ -61,15 +61,30 @@ const PAGE_SHORTCUTS: PageShortcuts = {
|
|||||||
category: 'Vue'
|
category: 'Vue'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keys: ['Cmd', 'Ctrl', 'F'],
|
keys: ['Shift', 'F'],
|
||||||
description: 'Ouvrir les filtres',
|
description: 'Ouvrir les filtres',
|
||||||
category: 'Filtres'
|
category: 'Filtres'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keys: ['Cmd', 'Ctrl', 'S'],
|
keys: ['Shift', 'S'],
|
||||||
description: 'Basculer les swimlanes',
|
description: 'Basculer les swimlanes',
|
||||||
category: 'Vue'
|
category: 'Vue'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
keys: ['Shift', 'O'],
|
||||||
|
description: 'Basculer les objectifs',
|
||||||
|
category: 'Vue'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keys: ['Shift', 'D'],
|
||||||
|
description: 'Filtrer par date de fin',
|
||||||
|
category: 'Filtres'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keys: ['Shift', 'Z'],
|
||||||
|
description: 'Basculer la taille de police',
|
||||||
|
category: 'Vue'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
keys: ['Tab'],
|
keys: ['Tab'],
|
||||||
description: 'Navigation entre colonnes',
|
description: 'Navigation entre colonnes',
|
||||||
@@ -95,7 +110,7 @@ const PAGE_SHORTCUTS: PageShortcuts = {
|
|||||||
category: 'Navigation'
|
category: 'Navigation'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keys: ['Cmd', 'Ctrl', 'T'],
|
keys: ['Shift', 'H'],
|
||||||
description: 'Aller à aujourd\'hui',
|
description: 'Aller à aujourd\'hui',
|
||||||
category: 'Navigation'
|
category: 'Navigation'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,12 +2,16 @@
|
|||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
|
import { useTheme } from '@/contexts/ThemeContext';
|
||||||
|
|
||||||
interface KeyboardShortcutsActions {
|
interface KeyboardShortcutsActions {
|
||||||
onCreateTask?: () => void;
|
onCreateTask?: () => void;
|
||||||
onToggleFilters?: () => void;
|
onToggleFilters?: () => void;
|
||||||
onToggleCompactView?: () => void;
|
onToggleCompactView?: () => void;
|
||||||
onToggleSwimlanes?: () => void;
|
onToggleSwimlanes?: () => void;
|
||||||
|
onToggleObjectives?: () => void;
|
||||||
|
onToggleDueDateFilter?: () => void;
|
||||||
|
onToggleFontSize?: () => void;
|
||||||
onNavigatePrevious?: () => void;
|
onNavigatePrevious?: () => void;
|
||||||
onNavigateNext?: () => void;
|
onNavigateNext?: () => void;
|
||||||
onGoToToday?: () => void;
|
onGoToToday?: () => void;
|
||||||
@@ -21,6 +25,7 @@ interface KeyboardShortcutsActions {
|
|||||||
|
|
||||||
export function useGlobalKeyboardShortcuts(actions: KeyboardShortcutsActions = {}) {
|
export function useGlobalKeyboardShortcuts(actions: KeyboardShortcutsActions = {}) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
const { toggleTheme, cycleDarkThemes } = useTheme();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
@@ -36,61 +41,91 @@ export function useGlobalKeyboardShortcuts(actions: KeyboardShortcutsActions = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cmd+K - Ouvrir la recherche rapide
|
// Raccourcis globaux (toutes les pages)
|
||||||
if ((event.metaKey || event.ctrlKey) && event.key === 'k') {
|
if (event.shiftKey) {
|
||||||
event.preventDefault();
|
switch (event.key) {
|
||||||
actions.onOpenSearch?.();
|
case 'Q':
|
||||||
return;
|
event.preventDefault();
|
||||||
|
toggleTheme();
|
||||||
|
return;
|
||||||
|
case 'W':
|
||||||
|
event.preventDefault();
|
||||||
|
cycleDarkThemes();
|
||||||
|
return;
|
||||||
|
case 'K':
|
||||||
|
event.preventDefault();
|
||||||
|
actions.onOpenSearch?.();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cmd+N - Créer une nouvelle tâche/élément
|
// Raccourcis spécifiques par page
|
||||||
if ((event.metaKey || event.ctrlKey) && event.key === 'n') {
|
switch (pathname) {
|
||||||
event.preventDefault();
|
case '/kanban':
|
||||||
actions.onCreateTask?.();
|
handleKanbanShortcuts(event, actions);
|
||||||
return;
|
break;
|
||||||
|
case '/daily':
|
||||||
|
handleDailyShortcuts(event, actions);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
handleDefaultShortcuts(event, actions);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Cmd+F - Ouvrir les filtres (Kanban)
|
// Fonctions de gestion des raccourcis par page
|
||||||
if ((event.metaKey || event.ctrlKey) && event.key === 'f') {
|
const handleKanbanShortcuts = (event: KeyboardEvent, actions: KeyboardShortcutsActions) => {
|
||||||
event.preventDefault();
|
if (event.shiftKey) {
|
||||||
actions.onToggleFilters?.();
|
switch (event.key) {
|
||||||
return;
|
case 'N':
|
||||||
}
|
event.preventDefault();
|
||||||
|
actions.onCreateTask?.();
|
||||||
// Cmd+S - Basculer les swimlanes (Kanban)
|
break;
|
||||||
if ((event.metaKey || event.ctrlKey) && event.key === 's') {
|
case 'F':
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
actions.onToggleSwimlanes?.();
|
actions.onToggleFilters?.();
|
||||||
return;
|
break;
|
||||||
}
|
case 'S':
|
||||||
|
event.preventDefault();
|
||||||
// Space - Basculer la vue compacte (Kanban)
|
actions.onToggleSwimlanes?.();
|
||||||
if (event.key === ' ' && pathname === '/kanban') {
|
break;
|
||||||
|
case 'O':
|
||||||
|
event.preventDefault();
|
||||||
|
actions.onToggleObjectives?.();
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
event.preventDefault();
|
||||||
|
actions.onToggleDueDateFilter?.();
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
event.preventDefault();
|
||||||
|
actions.onToggleFontSize?.();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (event.key === ' ') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
actions.onToggleCompactView?.();
|
actions.onToggleCompactView?.();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Cmd+T - Aller à aujourd'hui/cette semaine
|
const handleDailyShortcuts = (event: KeyboardEvent, actions: KeyboardShortcutsActions) => {
|
||||||
if ((event.metaKey || event.ctrlKey) && event.key === 't') {
|
if (event.shiftKey && event.key === 'H') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
actions.onGoToToday?.();
|
actions.onGoToToday?.();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Flèches gauche/droite - Navigation
|
const handleDefaultShortcuts = (event: KeyboardEvent, actions: KeyboardShortcutsActions) => {
|
||||||
if (event.key === 'ArrowLeft') {
|
switch (event.key) {
|
||||||
event.preventDefault();
|
case 'ArrowLeft':
|
||||||
actions.onNavigatePrevious?.();
|
event.preventDefault();
|
||||||
return;
|
actions.onNavigatePrevious?.();
|
||||||
|
break;
|
||||||
|
case 'ArrowRight':
|
||||||
|
event.preventDefault();
|
||||||
|
actions.onNavigateNext?.();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'ArrowRight') {
|
|
||||||
event.preventDefault();
|
|
||||||
actions.onNavigateNext?.();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('keydown', handleKeyDown);
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
@@ -98,5 +133,5 @@ export function useGlobalKeyboardShortcuts(actions: KeyboardShortcutsActions = {
|
|||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('keydown', handleKeyDown);
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
};
|
};
|
||||||
}, [pathname, actions]);
|
}, [pathname, actions, toggleTheme, cycleDarkThemes]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useTheme } from '@/contexts/ThemeContext';
|
|
||||||
|
|
||||||
export function useKeyboardShortcuts() {
|
|
||||||
const { toggleTheme, cycleDarkThemes } = useTheme();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
|
||||||
// Cmd + Shift + D pour basculer entre light et le thème dark préféré
|
|
||||||
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'D') {
|
|
||||||
event.preventDefault();
|
|
||||||
toggleTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cmd + Shift + T pour faire tourner les thèmes dark
|
|
||||||
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'T') {
|
|
||||||
event.preventDefault();
|
|
||||||
cycleDarkThemes();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('keydown', handleKeyDown);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('keydown', handleKeyDown);
|
|
||||||
};
|
|
||||||
}, [toggleTheme, cycleDarkThemes]);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user