Add shortcuts (#85)
* Add yarn.lock to .gitignore * Extract available shapes to one place * Add event listeners for shapes shortcuts * fixup! Add event listeners for shapes shortcuts * Underline first letter of shapes to indicate interactivity * fixup! Extract available shapes to one place * fixup! Add event listeners for shapes shortcuts
This commit is contained in:
parent
8c974411ce
commit
58d81280c9
3
.gitignore
vendored
3
.gitignore
vendored
@ -11,6 +11,9 @@ build
|
|||||||
# Dependency directories
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
# lock file
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
# Editors
|
# Editors
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
@ -118,6 +118,9 @@ function hitTest(element: ExcalidrawElement, x: number, y: number): boolean {
|
|||||||
const y2 = getElementAbsoluteY2(element);
|
const y2 = getElementAbsoluteY2(element);
|
||||||
|
|
||||||
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
|
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
|
||||||
|
} else if (element.type === "selection") {
|
||||||
|
console.warn("This should not happen, we need to investigate why it does.");
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Unimplemented type " + element.type);
|
throw new Error("Unimplemented type " + element.type);
|
||||||
}
|
}
|
||||||
@ -571,6 +574,40 @@ const KEYS = {
|
|||||||
BACKSPACE: "Backspace"
|
BACKSPACE: "Backspace"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SHAPES = [
|
||||||
|
{
|
||||||
|
label: "Rectange",
|
||||||
|
value: "rectangle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Ellipse",
|
||||||
|
value: "ellipse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Arrow",
|
||||||
|
value: "arrow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Text",
|
||||||
|
value: "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Selection",
|
||||||
|
value: "selection"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const shapesShortcutKeys = SHAPES.map(shape => shape.label[0].toLowerCase());
|
||||||
|
|
||||||
|
function findElementByKey(key: string) {
|
||||||
|
const defaultElement = "selection";
|
||||||
|
return SHAPES.reduce((element, shape) => {
|
||||||
|
if (shape.value[0] !== key) return element;
|
||||||
|
|
||||||
|
return shape.value;
|
||||||
|
}, defaultElement);
|
||||||
|
}
|
||||||
|
|
||||||
function isArrowKey(keyCode: string) {
|
function isArrowKey(keyCode: string) {
|
||||||
return (
|
return (
|
||||||
keyCode === KEYS.ARROW_LEFT ||
|
keyCode === KEYS.ARROW_LEFT ||
|
||||||
@ -643,32 +680,11 @@ class App extends React.Component<{}, AppState> {
|
|||||||
});
|
});
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
} else if (shapesShortcutKeys.includes(event.key.toLowerCase())) {
|
||||||
|
this.setState({ elementType: findElementByKey(event.key) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderOption({
|
|
||||||
type,
|
|
||||||
children
|
|
||||||
}: {
|
|
||||||
type: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
checked={this.state.elementType === type}
|
|
||||||
onChange={() => {
|
|
||||||
this.setState({ elementType: type });
|
|
||||||
clearSelection();
|
|
||||||
this.forceUpdate();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{children}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -713,11 +729,20 @@ class App extends React.Component<{}, AppState> {
|
|||||||
>
|
>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Shapes</legend>
|
<legend>Shapes</legend>
|
||||||
{this.renderOption({ type: "rectangle", children: "Rectangle" })}
|
{SHAPES.map(({ value, label }) => (
|
||||||
{this.renderOption({ type: "ellipse", children: "Ellipse" })}
|
<label>
|
||||||
{this.renderOption({ type: "arrow", children: "Arrow" })}
|
<input
|
||||||
{this.renderOption({ type: "text", children: "Text" })}
|
type="radio"
|
||||||
{this.renderOption({ type: "selection", children: "Selection" })}
|
checked={this.state.elementType === value}
|
||||||
|
onChange={() => {
|
||||||
|
this.setState({ elementType: value });
|
||||||
|
clearSelection();
|
||||||
|
this.forceUpdate();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span>{label}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<canvas
|
<canvas
|
||||||
|
@ -18,6 +18,14 @@ label {
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label span {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
label span::first-letter {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user