mirror of
https://github.com/stan-smith/FossFLOW.git
synced 2025-12-25 07:28:55 -05:00
feat: add Help dialog and shortcut key support
This commit is contained in:
228
src/components/HelpDialog/HelpDialog.tsx
Normal file
228
src/components/HelpDialog/HelpDialog.tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper,
|
||||
Typography,
|
||||
Box,
|
||||
Divider
|
||||
} from '@mui/material';
|
||||
import { Close as CloseIcon } from '@mui/icons-material';
|
||||
import { useUiStateStore } from 'src/stores/uiStateStore';
|
||||
|
||||
interface ShortcutItem {
|
||||
action: string;
|
||||
shortcut: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const keyboardShortcuts: ShortcutItem[] = [
|
||||
{
|
||||
action: 'Undo',
|
||||
shortcut: 'Ctrl+Z',
|
||||
description: 'Undo the last action'
|
||||
},
|
||||
{
|
||||
action: 'Redo',
|
||||
shortcut: 'Ctrl+Y',
|
||||
description: 'Redo the last undone action'
|
||||
},
|
||||
{
|
||||
action: 'Redo (Alternative)',
|
||||
shortcut: 'Ctrl+Shift+Z',
|
||||
description: 'Alternative redo shortcut'
|
||||
},
|
||||
{
|
||||
action: 'Help',
|
||||
shortcut: 'F1',
|
||||
description: 'Open help dialog with keyboard shortcuts'
|
||||
},
|
||||
{
|
||||
action: 'Zoom In',
|
||||
shortcut: 'Mouse Wheel Up',
|
||||
description: 'Zoom in on the canvas'
|
||||
},
|
||||
{
|
||||
action: 'Zoom Out',
|
||||
shortcut: 'Mouse Wheel Down',
|
||||
description: 'Zoom out from the canvas'
|
||||
},
|
||||
{
|
||||
action: 'Pan Canvas',
|
||||
shortcut: 'Left-click + Drag',
|
||||
description: 'Pan the canvas when in Pan mode'
|
||||
},
|
||||
{
|
||||
action: 'Context Menu',
|
||||
shortcut: 'Right-click',
|
||||
description: 'Open context menu for items or empty space'
|
||||
}
|
||||
];
|
||||
|
||||
const mouseInteractions: ShortcutItem[] = [
|
||||
{
|
||||
action: 'Select Tool',
|
||||
shortcut: 'Click Select button',
|
||||
description: 'Switch to selection mode'
|
||||
},
|
||||
{
|
||||
action: 'Pan Tool',
|
||||
shortcut: 'Click Pan button',
|
||||
description: 'Switch to pan mode for moving canvas'
|
||||
},
|
||||
{
|
||||
action: 'Add Item',
|
||||
shortcut: 'Click Add item button',
|
||||
description: 'Open icon picker to add new items'
|
||||
},
|
||||
{
|
||||
action: 'Draw Rectangle',
|
||||
shortcut: 'Click Rectangle button',
|
||||
description: 'Switch to rectangle drawing mode'
|
||||
},
|
||||
{
|
||||
action: 'Create Connector',
|
||||
shortcut: 'Click Connector button',
|
||||
description: 'Switch to connector mode'
|
||||
},
|
||||
{
|
||||
action: 'Add Text',
|
||||
shortcut: 'Click Text button',
|
||||
description: 'Create a new text box'
|
||||
}
|
||||
];
|
||||
|
||||
export const HelpDialog = () => {
|
||||
const dialog = useUiStateStore((state) => state.dialog);
|
||||
const setDialog = useUiStateStore((state) => state.actions.setDialog);
|
||||
|
||||
const isOpen = dialog === 'HELP';
|
||||
|
||||
const handleClose = () => {
|
||||
setDialog(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onClose={handleClose}
|
||||
maxWidth="md"
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
sx: {
|
||||
minHeight: '60vh'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Typography variant="h6" component="div">
|
||||
Keyboard Shortcuts & Help
|
||||
</Typography>
|
||||
<Button
|
||||
onClick={handleClose}
|
||||
sx={{ minWidth: 'auto', p: 1, bgcolor: 'transparent', boxShadow: 'none', '&:hover': { bgcolor: 'transparent' }, '&:focus': { bgcolor: 'transparent' }, '&:active': { bgcolor: 'transparent' } }}
|
||||
>
|
||||
<CloseIcon />
|
||||
</Button>
|
||||
</Box>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Keyboard Shortcuts
|
||||
</Typography>
|
||||
<TableContainer component={Paper} variant="outlined">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Action</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Shortcut</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Description</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{keyboardShortcuts.map((shortcut, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>{shortcut.action}</TableCell>
|
||||
<TableCell>
|
||||
<code style={{
|
||||
backgroundColor: '#f5f5f5',
|
||||
padding: '2px 6px',
|
||||
borderRadius: '4px',
|
||||
fontFamily: 'monospace'
|
||||
}}>
|
||||
{shortcut.shortcut}
|
||||
</code>
|
||||
</TableCell>
|
||||
<TableCell>{shortcut.description}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Mouse Interactions
|
||||
</Typography>
|
||||
<TableContainer component={Paper} variant="outlined">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Action</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Method</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Description</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{mouseInteractions.map((interaction, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>{interaction.action}</TableCell>
|
||||
<TableCell>
|
||||
<code style={{
|
||||
backgroundColor: '#f5f5f5',
|
||||
padding: '2px 6px',
|
||||
borderRadius: '4px',
|
||||
fontFamily: 'monospace'
|
||||
}}>
|
||||
{interaction.shortcut}
|
||||
</code>
|
||||
</TableCell>
|
||||
<TableCell>{interaction.description}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mt: 3, p: 2, bgcolor: 'info.light', borderRadius: 1 }}>
|
||||
<Typography variant="body2" color="info.contrastText">
|
||||
<strong>Note:</strong> Keyboard shortcuts are disabled when typing in input fields,
|
||||
text areas, or content-editable elements to prevent conflicts.
|
||||
</Typography>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} variant="contained">
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
CropSquareOutlined as CropSquareIcon,
|
||||
Title as TitleIcon,
|
||||
Undo as UndoIcon,
|
||||
Redo as RedoIcon
|
||||
Redo as RedoIcon,
|
||||
Help as HelpIcon
|
||||
} from '@mui/icons-material';
|
||||
import { useUiStateStore } from 'src/stores/uiStateStore';
|
||||
import { IconButton } from 'src/components/IconButton/IconButton';
|
||||
@@ -57,7 +58,7 @@ export const ToolMenu = () => {
|
||||
|
||||
return (
|
||||
<UiElement>
|
||||
<Stack direction="row">
|
||||
<Stack direction="row" spacing={0.5} alignItems="center">
|
||||
{/* Undo/Redo Section */}
|
||||
<IconButton
|
||||
name="Undo (Ctrl+Z)"
|
||||
|
||||
@@ -16,6 +16,7 @@ import { ContextMenuManager } from 'src/components/ContextMenu/ContextMenuManage
|
||||
import { useScene } from 'src/hooks/useScene';
|
||||
import { useModelStore } from 'src/stores/modelStore';
|
||||
import { ExportImageDialog } from '../ExportImageDialog/ExportImageDialog';
|
||||
import { HelpDialog } from '../HelpDialog/HelpDialog';
|
||||
|
||||
const ToolsEnum = {
|
||||
MAIN_MENU: 'MAIN_MENU',
|
||||
@@ -234,6 +235,8 @@ export const UiOverlay = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{dialog === 'HELP' && <HelpDialog />}
|
||||
|
||||
<SceneLayer>
|
||||
<Box ref={contextMenuAnchorRef} />
|
||||
<ContextMenuManager anchorEl={contextMenuAnchorRef.current} />
|
||||
|
||||
@@ -2,7 +2,8 @@ import React from 'react';
|
||||
import {
|
||||
Add as ZoomInIcon,
|
||||
Remove as ZoomOutIcon,
|
||||
CropFreeOutlined as FitToScreenIcon
|
||||
CropFreeOutlined as FitToScreenIcon,
|
||||
Help as HelpIcon
|
||||
} from '@mui/icons-material';
|
||||
import { Stack, Box, Typography, Divider } from '@mui/material';
|
||||
import { toPx } from 'src/utils';
|
||||
@@ -22,7 +23,7 @@ export const ZoomControls = () => {
|
||||
const { fitToView } = useDiagramUtils();
|
||||
|
||||
return (
|
||||
<Stack direction="row" spacing={1}>
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<UiElement>
|
||||
<Stack direction="row">
|
||||
<IconButton
|
||||
@@ -60,6 +61,13 @@ export const ZoomControls = () => {
|
||||
onClick={fitToView}
|
||||
/>
|
||||
</UiElement>
|
||||
<UiElement>
|
||||
<IconButton
|
||||
name="Help (F1)"
|
||||
Icon={<HelpIcon />}
|
||||
onClick={() => uiStateStoreActions.setDialog('HELP')}
|
||||
/>
|
||||
</UiElement>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -85,6 +85,12 @@ export const useInteractionManager = () => {
|
||||
redo();
|
||||
}
|
||||
}
|
||||
|
||||
// Help dialog shortcut
|
||||
if (e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
uiState.actions.setDialog('HELP');
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
@@ -114,7 +114,8 @@ export type IconCollectionStateWithIcons = IconCollectionState & {
|
||||
};
|
||||
|
||||
export const DialogTypeEnum = {
|
||||
EXPORT_IMAGE: 'EXPORT_IMAGE'
|
||||
EXPORT_IMAGE: 'EXPORT_IMAGE',
|
||||
HELP: 'HELP'
|
||||
} as const;
|
||||
|
||||
export interface ContextMenu {
|
||||
|
||||
Reference in New Issue
Block a user