keyboard shortcuts baby
This commit is contained in:
parent
850a4b68cf
commit
2d36c04a49
10
package-lock.json
generated
10
package-lock.json
generated
@ -27,6 +27,7 @@
|
|||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-hotkeys-hook": "^4.4.1",
|
||||||
"react-router-dom": "^6.11.2",
|
"react-router-dom": "^6.11.2",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"url": "^0.11.1",
|
"url": "^0.11.1",
|
||||||
@ -15225,6 +15226,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
|
||||||
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
|
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-hotkeys-hook": {
|
||||||
|
"version": "4.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.4.1.tgz",
|
||||||
|
"integrity": "sha512-sClBMBioFEgFGYLTWWRKvhxcCx1DRznd+wkFHwQZspnRBkHTgruKIHptlK/U/2DPX8BhHoRGzpMVWUXMmdZlmw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.1",
|
||||||
|
"react-dom": ">=16.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "17.0.2",
|
"version": "17.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-hotkeys-hook": "^4.4.1",
|
||||||
"react-router-dom": "^6.11.2",
|
"react-router-dom": "^6.11.2",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"url": "^0.11.1",
|
"url": "^0.11.1",
|
||||||
|
@ -49,6 +49,7 @@ import { ObjectType, Action } from "../data/data";
|
|||||||
import CodeMirror from "@uiw/react-codemirror";
|
import CodeMirror from "@uiw/react-codemirror";
|
||||||
import { json } from "@codemirror/lang-json";
|
import { json } from "@codemirror/lang-json";
|
||||||
import jsPDF from "jspdf";
|
import jsPDF from "jspdf";
|
||||||
|
import { useHotkeys } from "react-hotkeys-hook";
|
||||||
|
|
||||||
export default function ControlPanel(props) {
|
export default function ControlPanel(props) {
|
||||||
const MODAL = {
|
const MODAL = {
|
||||||
@ -368,37 +369,65 @@ export default function ControlPanel(props) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fileImport = () => setVisible(MODAL.IMPORT);
|
||||||
|
const viewGrid = () =>
|
||||||
|
setSettings((prev) => ({ ...prev, showGrid: !prev.showGrid }));
|
||||||
|
const zoomIn = () =>
|
||||||
|
setSettings((prev) => ({ ...prev, zoom: prev.zoom * 1.2 }));
|
||||||
|
const zoomOut = () =>
|
||||||
|
setSettings((prev) => ({ ...prev, zoom: prev.zoom / 1.2 }));
|
||||||
|
const viewStrictMode = () => {
|
||||||
|
setSettings((prev) => ({ ...prev, strictMode: !prev.strictMode }));
|
||||||
|
Toast.success(`Stict mode is ${settings.strictMode ? "on" : "off"}.`);
|
||||||
|
};
|
||||||
|
const viewFieldSummary = () => {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
showFieldSummary: !prev.showFieldSummary,
|
||||||
|
}));
|
||||||
|
Toast.success(
|
||||||
|
`Field summary is ${settings.showFieldSummary ? "off" : "on"}.`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const copyAsImage = () => {
|
||||||
|
toPng(document.getElementById("canvas")).then(function (dataUrl) {
|
||||||
|
const blob = dataURItoBlob(dataUrl);
|
||||||
|
navigator.clipboard
|
||||||
|
.write([new ClipboardItem({ "image/png": blob })])
|
||||||
|
.then(() => {
|
||||||
|
Toast.success("Copied to clipboard.");
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
Toast.error("Could not copy to clipboard.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const resetView = () =>
|
||||||
|
setSettings((prev) => ({ ...prev, zoom: 1, pan: { x: 0, y: 0 } }));
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
File: {
|
File: {
|
||||||
New: {
|
New: {
|
||||||
children: [],
|
function: () => {},
|
||||||
function: () => console.log("New"),
|
|
||||||
},
|
},
|
||||||
"New window": {
|
"New window": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Save: {
|
Save: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
"Save as": {
|
"Save as": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Share: {
|
Share: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Rename: {
|
Rename: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Import: {
|
Import: {
|
||||||
children: [],
|
function: fileImport,
|
||||||
function: () => {
|
shortcut: "Ctrl+I",
|
||||||
setVisible(MODAL.IMPORT);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"Export as": {
|
"Export as": {
|
||||||
children: [
|
children: [
|
||||||
@ -518,183 +547,151 @@ export default function ControlPanel(props) {
|
|||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Properties: {
|
Properties: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Close: {
|
Close: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Edit: {
|
Edit: {
|
||||||
Undo: {
|
Undo: {
|
||||||
children: [],
|
|
||||||
function: undo,
|
function: undo,
|
||||||
|
shortcut: "Ctrl+Z",
|
||||||
},
|
},
|
||||||
Redo: {
|
Redo: {
|
||||||
children: [],
|
|
||||||
function: redo,
|
function: redo,
|
||||||
|
shortcut: "Ctrl+Y",
|
||||||
},
|
},
|
||||||
Clear: {
|
Clear: {
|
||||||
children: [],
|
|
||||||
function: () => {
|
function: () => {
|
||||||
setTables([]);
|
setTables([]);
|
||||||
setRelationships([]);
|
setRelationships([]);
|
||||||
setAreas([]);
|
setAreas([]);
|
||||||
setNotes([]);
|
setNotes([]);
|
||||||
|
setUndoStack([]);
|
||||||
|
setRedoStack([]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Edit: {
|
Edit: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Cut: {
|
Cut: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Paste: {
|
Paste: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Duplicate: {
|
Duplicate: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Delete: {
|
Delete: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
"Copy as image": {
|
"Copy as image": {
|
||||||
children: [],
|
function: copyAsImage,
|
||||||
function: () => {
|
shortcut: "Ctrl+Alt+C",
|
||||||
toPng(document.getElementById("canvas")).then(function (dataUrl) {
|
|
||||||
const blob = dataURItoBlob(dataUrl);
|
|
||||||
navigator.clipboard
|
|
||||||
.write([new ClipboardItem({ "image/png": blob })])
|
|
||||||
.then(() => {
|
|
||||||
Toast.success("Copied to clipboard.");
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
Toast.error("Could not copy to clipboard.");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
View: {
|
View: {
|
||||||
Header: {
|
Header: {
|
||||||
children: [],
|
|
||||||
function: () =>
|
function: () =>
|
||||||
setLayout((prev) => ({ ...prev, header: !prev.header })),
|
setLayout((prev) => ({ ...prev, header: !prev.header })),
|
||||||
},
|
},
|
||||||
Sidebar: {
|
Sidebar: {
|
||||||
children: [],
|
|
||||||
function: () =>
|
function: () =>
|
||||||
setLayout((prev) => ({ ...prev, sidebar: !prev.sidebar })),
|
setLayout((prev) => ({ ...prev, sidebar: !prev.sidebar })),
|
||||||
},
|
},
|
||||||
Issues: {
|
Issues: {
|
||||||
children: [],
|
|
||||||
function: () =>
|
function: () =>
|
||||||
setLayout((prev) => ({ ...prev, issues: !prev.issues })),
|
setLayout((prev) => ({ ...prev, issues: !prev.issues })),
|
||||||
},
|
},
|
||||||
Services: {
|
Services: {
|
||||||
children: [],
|
|
||||||
function: () =>
|
function: () =>
|
||||||
setLayout((prev) => ({ ...prev, services: !prev.services })),
|
setLayout((prev) => ({ ...prev, services: !prev.services })),
|
||||||
},
|
},
|
||||||
"Strict mode": {
|
"Strict mode": {
|
||||||
children: [],
|
function: viewStrictMode,
|
||||||
function: () => {
|
shortcut: "Ctrl+Shift+M",
|
||||||
setSettings((prev) => ({ ...prev, strictMode: !prev.strictMode }));
|
|
||||||
Toast.success(`Stict mode is ${settings.strictMode ? "on" : "off"}.`);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"Field summary": {
|
"Field summary": {
|
||||||
children: [],
|
function: viewFieldSummary,
|
||||||
function: () => {
|
shortcut: "Ctrl+Shift+F",
|
||||||
setSettings((prev) => ({
|
|
||||||
...prev,
|
|
||||||
showFieldSummary: !prev.showFieldSummary,
|
|
||||||
}));
|
|
||||||
Toast.success(
|
|
||||||
`Field summary is ${settings.showFieldSummary ? "off" : "on"}.`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"Reset view": {
|
"Reset view": {
|
||||||
children: [],
|
function: resetView,
|
||||||
function: () => {},
|
shortcut: "Ctrl+R",
|
||||||
},
|
},
|
||||||
"View schema": {
|
"View schema": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
Grid: {
|
Grid: {
|
||||||
children: [],
|
function: viewGrid,
|
||||||
function: () =>
|
shortcut: "Ctrl+Shift+G",
|
||||||
setSettings((prev) => ({ ...prev, showGrid: !prev.showGrid })),
|
|
||||||
},
|
},
|
||||||
Theme: {
|
Theme: {
|
||||||
children: [{ Light: () => {} }, { Dark: () => {} }],
|
children: [{ Light: () => {} }, { Dark: () => {} }],
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
"Zoom in": {
|
"Zoom in": {
|
||||||
children: [],
|
function: zoomIn,
|
||||||
function: () =>
|
shortcut: "Ctrl+Up/Wheel",
|
||||||
setSettings((prev) => ({ ...prev, zoom: prev.zoom * 1.2 })),
|
|
||||||
},
|
},
|
||||||
"Zoom out": {
|
"Zoom out": {
|
||||||
children: [],
|
function: zoomOut,
|
||||||
function: () =>
|
shortcut: "Ctrl+Down/Wheel",
|
||||||
setSettings((prev) => ({ ...prev, zoom: prev.zoom / 1.2 })),
|
|
||||||
},
|
},
|
||||||
Fullscreen: {
|
Fullscreen: {
|
||||||
children: [],
|
|
||||||
function: enterFullscreen,
|
function: enterFullscreen,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Logs: {
|
Logs: {
|
||||||
"Open logs": {
|
"Open logs": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
"Commit changes": {
|
"Commit changes": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
"Revert changes": {
|
"Revert changes": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
"View commits": {
|
"View commits": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Help: {
|
Help: {
|
||||||
Shortcuts: {
|
Shortcuts: {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
"Ask us on discord": {
|
"Ask us on discord": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
"Tweet us": {
|
"Tweet us": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
"Found a bug": {
|
"Found a bug": {
|
||||||
children: [],
|
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useHotkeys("ctrl+i, meta+i", fileImport, { preventDefault: true });
|
||||||
|
useHotkeys("ctrl+z, meta+z", undo, { preventDefault: true });
|
||||||
|
useHotkeys("ctrl+y, meta+y", redo, { preventDefault: true });
|
||||||
|
useHotkeys("ctrl+shift+g, meta+shift+g", viewGrid, { preventDefault: true });
|
||||||
|
useHotkeys("ctrl+up, meta+up", zoomIn, { preventDefault: true });
|
||||||
|
useHotkeys("ctrl+down, meta+down", zoomOut, { preventDefault: true });
|
||||||
|
useHotkeys("ctrl+shift+m, meta+shift+m", viewStrictMode, {
|
||||||
|
preventDefault: true,
|
||||||
|
});
|
||||||
|
useHotkeys("ctrl+shift+f, meta+shift+f", viewFieldSummary, {
|
||||||
|
preventDefault: true,
|
||||||
|
});
|
||||||
|
useHotkeys("ctrl+alt+c, meta+alt+c", copyAsImage, { preventDefault: true });
|
||||||
|
useHotkeys("ctrl+r, meta+r", resetView, { preventDefault: true });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{layout.header && header()}
|
{layout.header && header()}
|
||||||
@ -1040,11 +1037,11 @@ export default function ControlPanel(props) {
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
key={category}
|
key={category}
|
||||||
position="bottomLeft"
|
position="bottomLeft"
|
||||||
style={{ width: "200px" }}
|
style={{ width: "220px" }}
|
||||||
render={
|
render={
|
||||||
<Dropdown.Menu>
|
<Dropdown.Menu>
|
||||||
{Object.keys(menu[category]).map((item, index) => {
|
{Object.keys(menu[category]).map((item, index) => {
|
||||||
if (menu[category][item].children.length > 0) {
|
if (menu[category][item].children) {
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
style={{ width: "120px" }}
|
style={{ width: "120px" }}
|
||||||
@ -1083,8 +1080,24 @@ export default function ControlPanel(props) {
|
|||||||
<Dropdown.Item
|
<Dropdown.Item
|
||||||
key={index}
|
key={index}
|
||||||
onClick={menu[category][item].function}
|
onClick={menu[category][item].function}
|
||||||
|
style={
|
||||||
|
menu[category][item].shortcut && {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{item}
|
{menu[category][item].shortcut ? (
|
||||||
|
<>
|
||||||
|
<div>{item}</div>
|
||||||
|
<div className="text-gray-400">
|
||||||
|
{menu[category][item].shortcut}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
item
|
||||||
|
)}
|
||||||
</Dropdown.Item>
|
</Dropdown.Item>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
Loading…
Reference in New Issue
Block a user