fix: add ARIA roles, labels, and states to all interactive components
This commit is contained in:
100
src/App.tsx
100
src/App.tsx
@@ -273,7 +273,7 @@ function App() {
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const sidebarRef = useRef<HTMLDivElement>(null);
|
||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||
const menuItemRefs = useRef<{ [key: string]: HTMLDivElement | null }>({});
|
||||
const menuItemRefs = useRef<{ [key: string]: HTMLButtonElement | null }>({});
|
||||
const kineticDragActiveRef = useRef(false);
|
||||
const contextMenuRef = useRef<HTMLDivElement>(null);
|
||||
const tabScrollRef = useRef<HTMLDivElement>(null);
|
||||
@@ -753,7 +753,7 @@ function App() {
|
||||
<a href="#main-content" className="skip-link">Skip to content</a>
|
||||
<AnimatePresence>
|
||||
{isDraggingOver && (
|
||||
<motion.div className="drop-zone" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15, ease: materialEase }}>
|
||||
<motion.div className="drop-zone" role="region" aria-label="Drop zone for markdown files" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15, ease: materialEase }}>
|
||||
<motion.div className="drop-zone-content" initial={{ opacity: 0, scale: 0.92, y: 8 }} animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.92, y: 8 }} transition={{ duration: 0.2, ease: materialEase }}>
|
||||
<motion.div className="drop-zone-icon-wrapper" animate={{ y: [0, -6, 0] }} transition={{ duration: 1.5, repeat: Infinity, ease: 'easeInOut' }}>
|
||||
<FileDown size={40} strokeWidth={1.5} />
|
||||
@@ -774,9 +774,9 @@ function App() {
|
||||
</div>
|
||||
<div className="title-bar-drag"></div>
|
||||
<div className="title-bar-controls">
|
||||
<button className="title-bar-button" onClick={() => appWindow?.minimize()}><Minus size={14} /></button>
|
||||
<button className="title-bar-button" onClick={() => appWindow?.toggleMaximize()}><Square size={12} /></button>
|
||||
<button className="title-bar-button close" onClick={closeWindow}><X size={14} /></button>
|
||||
<button className="title-bar-button" onClick={() => appWindow?.minimize()} aria-label="Minimize"><Minus size={14} /></button>
|
||||
<button className="title-bar-button" onClick={() => appWindow?.toggleMaximize()} aria-label="Maximize"><Square size={12} /></button>
|
||||
<button className="title-bar-button close" onClick={closeWindow} aria-label="Close"><X size={14} /></button>
|
||||
</div>
|
||||
</motion.header>
|
||||
)}
|
||||
@@ -784,39 +784,39 @@ function App() {
|
||||
|
||||
<AnimatePresence initial={false}>
|
||||
{!focusMode && (
|
||||
<motion.div key="menu-bar" className="menu-bar" initial={{ height: 0, opacity: 0 }} animate={{ height: 28, opacity: 1 }} exit={{ height: 0, opacity: 0 }} transition={smoothTransition} style={{ overflow: 'hidden' }}>
|
||||
<div className="menu-item" ref={(el) => { menuItemRefs.current['file'] = el; }} onClick={() => setMenuOpen(prev => prev === 'file' ? null : 'file')}>File</div>
|
||||
<div className="menu-item" ref={(el) => { menuItemRefs.current['view'] = el; }} onClick={() => setMenuOpen(prev => prev === 'view' ? null : 'view')}>View</div>
|
||||
<div className="menu-item" ref={(el) => { menuItemRefs.current['help'] = el; }} onClick={() => setMenuOpen(prev => prev === 'help' ? null : 'help')}>Help</div>
|
||||
<motion.div key="menu-bar" className="menu-bar" role="menubar" initial={{ height: 0, opacity: 0 }} animate={{ height: 28, opacity: 1 }} exit={{ height: 0, opacity: 0 }} transition={smoothTransition} style={{ overflow: 'hidden' }}>
|
||||
<button className="menu-item" role="menuitem" aria-haspopup="true" aria-expanded={menuOpen === 'file'} ref={(el) => { menuItemRefs.current['file'] = el; }} onClick={() => setMenuOpen(prev => prev === 'file' ? null : 'file')}>File</button>
|
||||
<button className="menu-item" role="menuitem" aria-haspopup="true" aria-expanded={menuOpen === 'view'} ref={(el) => { menuItemRefs.current['view'] = el; }} onClick={() => setMenuOpen(prev => prev === 'view' ? null : 'view')}>View</button>
|
||||
<button className="menu-item" role="menuitem" aria-haspopup="true" aria-expanded={menuOpen === 'help'} ref={(el) => { menuItemRefs.current['help'] = el; }} onClick={() => setMenuOpen(prev => prev === 'help' ? null : 'help')}>Help</button>
|
||||
<AnimatePresence>
|
||||
{menuOpen === 'file' && menuItemRefs.current['file'] && (
|
||||
<motion.div className="menu-dropdown" initial={{ opacity: 0, y: -5 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -5 }} transition={smoothTransition} style={{ position: 'fixed', left: menuItemRefs.current['file']!.getBoundingClientRect().left, top: menuItemRefs.current['file']!.getBoundingClientRect().bottom - 6 }}>
|
||||
<button className="menu-dropdown-item" onClick={() => { handleOpenDialog(); setMenuOpen(null); }}>Open File <span className="menu-shortcut">Ctrl+O</span></button>
|
||||
<button className="menu-dropdown-item" onClick={() => { if (activeTabId) closeTab(activeTabId); setMenuOpen(null); }}>Close Tab <span className="menu-shortcut">Ctrl+W</span></button>
|
||||
<motion.div className="menu-dropdown" role="menu" initial={{ opacity: 0, y: -5 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -5 }} transition={smoothTransition} style={{ position: 'fixed', left: menuItemRefs.current['file']!.getBoundingClientRect().left, top: menuItemRefs.current['file']!.getBoundingClientRect().bottom - 6 }}>
|
||||
<button className="menu-dropdown-item" role="menuitem" onClick={() => { handleOpenDialog(); setMenuOpen(null); }}>Open File <span className="menu-shortcut">Ctrl+O</span></button>
|
||||
<button className="menu-dropdown-item" role="menuitem" onClick={() => { if (activeTabId) closeTab(activeTabId); setMenuOpen(null); }}>Close Tab <span className="menu-shortcut">Ctrl+W</span></button>
|
||||
<div className="menu-separator"></div>
|
||||
<button className="menu-dropdown-item" onClick={() => { closeWindow(); setMenuOpen(null); }}>Exit <span className="menu-shortcut">Ctrl+Q</span></button>
|
||||
<button className="menu-dropdown-item" role="menuitem" onClick={() => { closeWindow(); setMenuOpen(null); }}>Exit <span className="menu-shortcut">Ctrl+Q</span></button>
|
||||
</motion.div>
|
||||
)}
|
||||
{menuOpen === 'view' && menuItemRefs.current['view'] && (
|
||||
<motion.div className="menu-dropdown" initial={{ opacity: 0, y: -5 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -5 }} transition={smoothTransition} style={{ position: 'fixed', left: menuItemRefs.current['view']!.getBoundingClientRect().left, top: menuItemRefs.current['view']!.getBoundingClientRect().bottom - 6 }}>
|
||||
<button className="menu-dropdown-item" onClick={() => { setShowSearch(s => !s); setMenuOpen(null); }}>Toggle Search <span className="menu-shortcut">Ctrl+F</span></button>
|
||||
<button className="menu-dropdown-item" onClick={() => { setShowSidebar(s => !s); setMenuOpen(null); }}>Toggle Sidebar <span className="menu-shortcut">Ctrl+Shift+S</span></button>
|
||||
<button className="menu-dropdown-item" onClick={() => { setFocusMode(f => !f); setMenuOpen(null); }}>Focus Mode <span className="menu-shortcut">F11</span></button>
|
||||
<motion.div className="menu-dropdown" role="menu" initial={{ opacity: 0, y: -5 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -5 }} transition={smoothTransition} style={{ position: 'fixed', left: menuItemRefs.current['view']!.getBoundingClientRect().left, top: menuItemRefs.current['view']!.getBoundingClientRect().bottom - 6 }}>
|
||||
<button className="menu-dropdown-item" role="menuitem" onClick={() => { setShowSearch(s => !s); setMenuOpen(null); }}>Toggle Search <span className="menu-shortcut">Ctrl+F</span></button>
|
||||
<button className="menu-dropdown-item" role="menuitem" onClick={() => { setShowSidebar(s => !s); setMenuOpen(null); }}>Toggle Sidebar <span className="menu-shortcut">Ctrl+Shift+S</span></button>
|
||||
<button className="menu-dropdown-item" role="menuitem" onClick={() => { setFocusMode(f => !f); setMenuOpen(null); }}>Focus Mode <span className="menu-shortcut">F11</span></button>
|
||||
<div className="menu-separator"></div>
|
||||
<div className="menu-dropdown-zoom" onClick={e => e.stopPropagation()}>
|
||||
<span>UI Scale</span>
|
||||
<div className="zoom-spinner">
|
||||
<button className="zoom-spinner-btn" onClick={() => setUiZoom(z => Math.max(50, z - 10))}><Minus size={12} /></button>
|
||||
<div className="zoom-spinner" aria-label="UI Scale">
|
||||
<button className="zoom-spinner-btn" aria-label="Decrease UI scale" onClick={() => setUiZoom(z => Math.max(50, z - 10))}><Minus size={12} /></button>
|
||||
<span className="zoom-spinner-value">{uiZoom}%</span>
|
||||
<button className="zoom-spinner-btn" onClick={() => setUiZoom(z => Math.min(200, z + 10))}><Plus size={12} /></button>
|
||||
<button className="zoom-spinner-btn" aria-label="Increase UI scale" onClick={() => setUiZoom(z => Math.min(200, z + 10))}><Plus size={12} /></button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
{menuOpen === 'help' && menuItemRefs.current['help'] && (
|
||||
<motion.div className="menu-dropdown" initial={{ opacity: 0, y: -5 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -5 }} transition={smoothTransition} style={{ position: 'fixed', left: menuItemRefs.current['help']!.getBoundingClientRect().left, top: menuItemRefs.current['help']!.getBoundingClientRect().bottom - 6 }}>
|
||||
<button className="menu-dropdown-item" onClick={() => { setShowShortcutsModal(true); setMenuOpen(null); }}>Keyboard Shortcuts</button>
|
||||
<button className="menu-dropdown-item" onClick={() => { setShowAboutModal(true); setMenuOpen(null); }}>About Vesper</button>
|
||||
<motion.div className="menu-dropdown" role="menu" initial={{ opacity: 0, y: -5 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -5 }} transition={smoothTransition} style={{ position: 'fixed', left: menuItemRefs.current['help']!.getBoundingClientRect().left, top: menuItemRefs.current['help']!.getBoundingClientRect().bottom - 6 }}>
|
||||
<button className="menu-dropdown-item" role="menuitem" onClick={() => { setShowShortcutsModal(true); setMenuOpen(null); }}>Keyboard Shortcuts</button>
|
||||
<button className="menu-dropdown-item" role="menuitem" onClick={() => { setShowAboutModal(true); setMenuOpen(null); }}>About Vesper</button>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
@@ -827,18 +827,18 @@ function App() {
|
||||
<AnimatePresence>
|
||||
{tabs.length > 1 && (
|
||||
<motion.div className="tab-bar" initial={{ height: 0, opacity: 0 }} animate={{ height: 38, opacity: 1 }} exit={{ height: 0, opacity: 0 }} transition={{ duration: 0.25, ease: materialEase }}>
|
||||
<button className={`tab-scroll-arrow ${tabScrollState.canLeft ? 'visible' : ''}`} onMouseDown={() => startScrollingTabs('left')} onMouseUp={stopScrollingTabs} onMouseLeave={stopScrollingTabs}><ChevronLeft size={14} /></button>
|
||||
<div className="tab-scroll-container" ref={tabScrollRef}>
|
||||
<button className={`tab-scroll-arrow ${tabScrollState.canLeft ? 'visible' : ''}`} aria-label="Scroll tabs left" onMouseDown={() => startScrollingTabs('left')} onMouseUp={stopScrollingTabs} onMouseLeave={stopScrollingTabs}><ChevronLeft size={14} /></button>
|
||||
<div className="tab-scroll-container" ref={tabScrollRef} role="tablist">
|
||||
<AnimatePresence initial={false}>
|
||||
{tabs.map(tab => (
|
||||
<motion.div layout key={tab.id} className={`tab ${tab.id === activeTabId ? 'active' : ''}`} initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.9 }} transition={smoothTransition} onClick={() => { setActiveTabId(tab.id); parseHeadings(tab.content); }}>
|
||||
<motion.div layout key={tab.id} id={`tab-${tab.id}`} role="tab" aria-selected={tab.id === activeTabId} className={`tab ${tab.id === activeTabId ? 'active' : ''}`} initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.9 }} transition={smoothTransition} onClick={() => { setActiveTabId(tab.id); parseHeadings(tab.content); }}>
|
||||
<span className="tab-title">{tab.title}</span>
|
||||
<button className="tab-close" onClick={(e) => { e.stopPropagation(); closeTab(tab.id); }}><X size={12} /></button>
|
||||
<button className="tab-close" aria-label="Close tab" onClick={(e) => { e.stopPropagation(); closeTab(tab.id); }}><X size={12} /></button>
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<button className={`tab-scroll-arrow ${tabScrollState.canRight ? 'visible' : ''}`} onMouseDown={() => startScrollingTabs('right')} onMouseUp={stopScrollingTabs} onMouseLeave={stopScrollingTabs}><ChevronRight size={14} /></button>
|
||||
<button className={`tab-scroll-arrow ${tabScrollState.canRight ? 'visible' : ''}`} aria-label="Scroll tabs right" onMouseDown={() => startScrollingTabs('right')} onMouseUp={stopScrollingTabs} onMouseLeave={stopScrollingTabs}><ChevronRight size={14} /></button>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
@@ -865,20 +865,20 @@ function App() {
|
||||
<AnimatePresence>
|
||||
{showSearch && activeTab && (
|
||||
<motion.search className="search-bar" initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -10 }} transition={smoothTransition}>
|
||||
<input ref={searchInputRef} type="text" className="search-input" placeholder="Search..." value={searchQuery} onChange={e => { setSearchQuery(e.target.value); }} onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); setCurrentMatchIndex(i => (i + 1) % Math.max(searchMatches.length, 1)); setSearchTriggerCount(c => c + 1); } }} autoFocus />
|
||||
<input ref={searchInputRef} type="text" className="search-input" placeholder="Search..." value={searchQuery} onChange={e => { setSearchQuery(e.target.value); }} onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); setCurrentMatchIndex(i => (i + 1) % Math.max(searchMatches.length, 1)); setSearchTriggerCount(c => c + 1); } }} aria-label="Search document" autoFocus />
|
||||
{searchMatches.length > 0 && (
|
||||
<>
|
||||
<button className="search-nav-btn" onClick={() => { setCurrentMatchIndex(i => (i - 1 + searchMatches.length) % searchMatches.length); setSearchTriggerCount(c => c + 1); }}><ChevronLeft size={16} /></button>
|
||||
<span className="search-results">{currentMatchIndex + 1} of {searchMatches.length}</span>
|
||||
<button className="search-nav-btn" onClick={() => { setCurrentMatchIndex(i => (i + 1) % searchMatches.length); setSearchTriggerCount(c => c + 1); }}><ChevronRight size={16} /></button>
|
||||
<button className="search-nav-btn" aria-label="Previous match" onClick={() => { setCurrentMatchIndex(i => (i - 1 + searchMatches.length) % searchMatches.length); setSearchTriggerCount(c => c + 1); }}><ChevronLeft size={16} /></button>
|
||||
<span className="search-results" role="status" aria-live="polite">{currentMatchIndex + 1} of {searchMatches.length}</span>
|
||||
<button className="search-nav-btn" aria-label="Next match" onClick={() => { setCurrentMatchIndex(i => (i + 1) % searchMatches.length); setSearchTriggerCount(c => c + 1); }}><ChevronRight size={16} /></button>
|
||||
</>
|
||||
)}
|
||||
{searchQuery && searchMatches.length === 0 && <span className="search-results">No results</span>}
|
||||
{searchQuery && searchMatches.length === 0 && <span className="search-results" role="status" aria-live="polite">No results</span>}
|
||||
</motion.search>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<motion.div className="content-area-scroll-wrapper" key={activeTabId} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={smoothTransition}>
|
||||
<motion.div className="content-area-scroll-wrapper" role="tabpanel" aria-labelledby={activeTabId ? `tab-${activeTabId}` : undefined} key={activeTabId} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={smoothTransition}>
|
||||
<OverlayScrollbarsComponent options={osScrollbarOptions} style={{ height: '100%' }}>
|
||||
<div className="content-area-scroll" ref={contentRef} style={{ zoom: `${(zoom / uiZoom) * 100}%`, ...(!activeTab ? { height: '100%' } : {}) }}>
|
||||
{activeTab ? (
|
||||
@@ -901,10 +901,10 @@ function App() {
|
||||
<AnimatePresence>
|
||||
{showShortcutsModal && (
|
||||
<motion.div className="modal-overlay" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={smoothTransition} onClick={() => setShowShortcutsModal(false)}>
|
||||
<motion.div className="shortcuts-dialog" initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} transition={smoothTransition} onClick={e => e.stopPropagation()}>
|
||||
<motion.div className="shortcuts-dialog" role="dialog" aria-modal="true" aria-labelledby="shortcuts-title" initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} transition={smoothTransition} onClick={e => e.stopPropagation()}>
|
||||
<div className="shortcuts-dialog-header">
|
||||
<h2 className="shortcuts-dialog-title">Keyboard Shortcuts</h2>
|
||||
<button className="shortcuts-dialog-close" onClick={() => setShowShortcutsModal(false)}><X size={16} /></button>
|
||||
<h2 className="shortcuts-dialog-title" id="shortcuts-title">Keyboard Shortcuts</h2>
|
||||
<button className="shortcuts-dialog-close" aria-label="Close dialog" onClick={() => setShowShortcutsModal(false)}><X size={16} /></button>
|
||||
</div>
|
||||
<div className="shortcuts-dialog-body">
|
||||
<div>
|
||||
@@ -933,10 +933,10 @@ function App() {
|
||||
)}
|
||||
{showAboutModal && (
|
||||
<motion.div className="modal-overlay" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={smoothTransition} onClick={() => setShowAboutModal(false)}>
|
||||
<motion.div className="modal" initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} transition={smoothTransition} onClick={e => e.stopPropagation()}>
|
||||
<motion.div className="modal" role="dialog" aria-modal="true" aria-labelledby="about-title" initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} transition={smoothTransition} onClick={e => e.stopPropagation()}>
|
||||
<div className="modal-header">
|
||||
<h2 className="modal-title">About Vesper</h2>
|
||||
<button className="modal-close" onClick={() => setShowAboutModal(false)}><X size={16} /></button>
|
||||
<h2 className="modal-title" id="about-title">About Vesper</h2>
|
||||
<button className="modal-close" aria-label="Close dialog" onClick={() => setShowAboutModal(false)}><X size={16} /></button>
|
||||
</div>
|
||||
<div className="modal-content about-content">
|
||||
<div className="about-icon"><FileText size={48} /></div>
|
||||
@@ -952,7 +952,7 @@ function App() {
|
||||
|
||||
<AnimatePresence>
|
||||
{menuOpen && (
|
||||
<div className="menu-backdrop" onClick={() => setMenuOpen(null)} />
|
||||
<div className="menu-backdrop" aria-hidden="true" onClick={() => setMenuOpen(null)} />
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
@@ -966,23 +966,23 @@ function App() {
|
||||
|
||||
<AnimatePresence>
|
||||
{contextMenu && (
|
||||
<motion.div ref={contextMenuRef} className="context-menu" style={{ left: contextMenu.x, top: contextMenu.y }} initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} transition={smoothTransition}>
|
||||
<motion.div ref={contextMenuRef} className="context-menu" role="menu" style={{ left: contextMenu.x, top: contextMenu.y }} initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} transition={smoothTransition}>
|
||||
{contextMenu.selectedText && (
|
||||
<>
|
||||
<button className="context-menu-item" onClick={() => { navigator.clipboard.writeText(contextMenu.selectedText); setContextMenu(null); }}>Copy</button>
|
||||
<button className="context-menu-item" role="menuitem" onClick={() => { navigator.clipboard.writeText(contextMenu.selectedText); setContextMenu(null); }}>Copy</button>
|
||||
<div className="context-menu-separator"></div>
|
||||
</>
|
||||
)}
|
||||
<button className="context-menu-item" onClick={() => { handleOpenDialog(); setContextMenu(null); }}>Open File</button>
|
||||
<button className="context-menu-item" role="menuitem" onClick={() => { handleOpenDialog(); setContextMenu(null); }}>Open File</button>
|
||||
<div className="context-menu-separator"></div>
|
||||
<button className="context-menu-item" onClick={() => { setShowSearch(s => !s); setContextMenu(null); }}>Toggle Search</button>
|
||||
<button className="context-menu-item" onClick={() => { setShowSidebar(s => !s); setContextMenu(null); }}>Toggle Sidebar</button>
|
||||
<button className="context-menu-item" onClick={() => { setFocusMode(f => !f); setContextMenu(null); }}>Focus Mode</button>
|
||||
<button className="context-menu-item" role="menuitem" onClick={() => { setShowSearch(s => !s); setContextMenu(null); }}>Toggle Search</button>
|
||||
<button className="context-menu-item" role="menuitem" onClick={() => { setShowSidebar(s => !s); setContextMenu(null); }}>Toggle Sidebar</button>
|
||||
<button className="context-menu-item" role="menuitem" onClick={() => { setFocusMode(f => !f); setContextMenu(null); }}>Focus Mode</button>
|
||||
<div className="context-menu-separator"></div>
|
||||
<button className="context-menu-item" onClick={() => { setShowShortcutsModal(true); setContextMenu(null); }}>Keyboard Shortcuts</button>
|
||||
<button className="context-menu-item" onClick={() => { setShowAboutModal(true); setContextMenu(null); }}>About</button>
|
||||
<button className="context-menu-item" role="menuitem" onClick={() => { setShowShortcutsModal(true); setContextMenu(null); }}>Keyboard Shortcuts</button>
|
||||
<button className="context-menu-item" role="menuitem" onClick={() => { setShowAboutModal(true); setContextMenu(null); }}>About</button>
|
||||
<div className="context-menu-separator"></div>
|
||||
<button className="context-menu-item" onClick={() => { closeWindow(); setContextMenu(null); }}>Exit <span className="menu-shortcut">Ctrl+Q</span></button>
|
||||
<button className="context-menu-item" role="menuitem" onClick={() => { closeWindow(); setContextMenu(null); }}>Exit <span className="menu-shortcut">Ctrl+Q</span></button>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
Reference in New Issue
Block a user