diff --git a/frontend/src/components/Kbd.jsx b/frontend/src/components/Kbd.jsx new file mode 100644 index 0000000..90251ed --- /dev/null +++ b/frontend/src/components/Kbd.jsx @@ -0,0 +1,10 @@ +import { shortcutKeys } from '../utils/shortcuts.js' + +// Raccourci clavier affiché à droite d'une entrée de menu ; caché sur écran tactile (styles/pz.css, .pz-kbd) +export default function Kbd({ mod, k }) { + return ( + + ) +} diff --git a/frontend/src/components/navbar/ContextualNavbar.jsx b/frontend/src/components/navbar/ContextualNavbar.jsx index a824f39..7dec10b 100644 --- a/frontend/src/components/navbar/ContextualNavbar.jsx +++ b/frontend/src/components/navbar/ContextualNavbar.jsx @@ -12,6 +12,7 @@ import { t } from '../../i18n/index.js' import { PHONE_QUERY } from '../../utils/sheetDrag' import './ContextualNavbar.css' import { initialOf } from '../../utils/initial.js' +import Kbd from '../Kbd.jsx' const ANNOT_TOOLS = [ { id: 'pointer', label: t('Sélection'), Icon: MousePointer2 }, @@ -211,9 +212,9 @@ function ContextualNavbar({ } const fixItems = [] if (isFamily) { - fixItems.push({ key: 'edit', icon: , label: editLabel, sub: editModeActive ? null : t('dates, noms, liens, le propriétaire relit'), onSelect: onEditClick }) + fixItems.push({ key: 'edit', icon: , label: editLabel, sub: editModeActive ? null : t('dates, noms, liens, le propriétaire relit'), onSelect: onEditClick, shortcut: 'e' }) } else if (isOwner || isDemo) { - fixItems.push({ key: 'edit', icon: , label: editLabel, onSelect: onEditClick }) + fixItems.push({ key: 'edit', icon: , label: editLabel, onSelect: onEditClick, shortcut: 'e' }) } if (addItems.length) addGroups.push({ label: t('Ajouter'), items: addItems }) if (fixItems.length) addGroups.push({ label: t('Corriger'), items: fixItems }) @@ -235,6 +236,7 @@ function ContextualNavbar({ label={item.label} sub={item.sub} onSelect={run(item.onSelect)} + right={item.shortcut ? : null} /> ))} diff --git a/frontend/src/components/search/ExpandableSearch.jsx b/frontend/src/components/search/ExpandableSearch.jsx index b9c4012..9912c85 100644 --- a/frontend/src/components/search/ExpandableSearch.jsx +++ b/frontend/src/components/search/ExpandableSearch.jsx @@ -3,6 +3,7 @@ import { Search, X } from 'lucide-react' import SearchResultsDropdown from './SearchResultsDropdown' import './ExpandableSearch.css' import { t } from '../../i18n/index.js' +import { isCommandK, withShortcut } from '../../utils/shortcuts.js' /** * Recherche expandable haut-droite @@ -80,6 +81,22 @@ function ExpandableSearch({ setExpanded(true) } + // Ctrl+K (⌘K sur Mac), de n'importe où : ouvre la recherche, ou y remet le curseur si elle est déjà ouverte + useEffect(() => { + function handleCommandK(event) { + if (!isCommandK(event)) return + event.preventDefault() + if (!expanded) setExpanded(true) + else { + inputRef.current?.focus() + inputRef.current?.select() + } + } + + document.addEventListener('keydown', handleCommandK) + return () => document.removeEventListener('keydown', handleCommandK) + }, [expanded]) + const handleInputChange = (e) => { const value = e.target.value setQuery(value) @@ -106,6 +123,7 @@ function ExpandableSearch({ className="search-icon-button" onClick={handleExpand} aria-label={t('Rechercher une personne')} + title={withShortcut(t('Rechercher une personne'), 'cmd', 'k')} > diff --git a/frontend/src/styles/pz.css b/frontend/src/styles/pz.css index ea9ce45..c65ee73 100644 --- a/frontend/src/styles/pz.css +++ b/frontend/src/styles/pz.css @@ -277,3 +277,8 @@ button:disabled:has(.pz-busy.is-busy) { opacity: 1; cursor: progress; } inset: min(0px, calc((100% - 44px) / 2)) min(0px, calc((100% - 44px) / 2)); } } + +/* Raccourci clavier à droite d'une entrée de menu (components/Kbd.jsx) ; rien sur écran tactile */ +.pz-kbd { display: inline-flex; gap: 3px; margin-left: auto; padding-left: 12px; } +.pz-kbd kbd { min-width: 20px; padding: 1px 5px; border: 1px solid var(--pz-line); border-radius: 5px; background: var(--pz-bg); font: 500 11px/16px var(--pz-sans); color: var(--pz-muted); text-align: center; } +@media (pointer: coarse) { .pz-kbd { display: none; } } diff --git a/frontend/src/utils/shortcuts.js b/frontend/src/utils/shortcuts.js new file mode 100644 index 0000000..77d585f --- /dev/null +++ b/frontend/src/utils/shortcuts.js @@ -0,0 +1,19 @@ +// Raccourcis clavier affichés dans l'interface (infobulles, menus). Sur Mac, Ctrl s'écrit ⌃ et Cmd ⌘. +const isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || '') + +// mod : 'ctrl' (Ctrl partout) ou 'cmd' (⌘ sur Mac, Ctrl ailleurs) +export function shortcutKeys(mod, key) { + const modifier = mod === 'cmd' ? (isMac ? '⌘' : 'Ctrl') : (isMac ? '⌃' : 'Ctrl') + return [modifier, key.toUpperCase()] +} + +// Texte d'une infobulle : « Rechercher une personne (Ctrl K) », « … (⌘K) » sur Mac +export function withShortcut(label, mod, key) { + const keys = shortcutKeys(mod, key) + return `${label} (${isMac ? keys.join('') : keys.join(' ')})` +} + +// Ctrl+K, ou ⌘K sur Mac +export function isCommandK(event) { + return (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey && event.key.toLowerCase() === 'k' +}