Skip to content

Commit 9e660d1

Browse files
committed
Keyboard navigation, other small improvements
1 parent 311402e commit 9e660d1

File tree

18 files changed

+460
-199
lines changed

18 files changed

+460
-199
lines changed

packages/excalidraw/actions/actionProperties.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -811,12 +811,12 @@ export const actionChangeFontFamily = register({
811811
"currentItemFontFamily"
812812
>);
813813
}}
814-
onPopupChange={(isOpened) =>
814+
onPopupChange={(isOpened) => {
815815
updateData({ openPopup: isOpened ? "fontFamily" : null } as Pick<
816816
AppState,
817817
"openPopup"
818-
>)
819-
}
818+
>);
819+
}}
820820
/>
821821
</fieldset>
822822
);

packages/excalidraw/components/App.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4043,6 +4043,35 @@ class App extends React.Component<AppProps, AppState> {
40434043
}
40444044
}
40454045

4046+
if (
4047+
!event[KEYS.CTRL_OR_CMD] &&
4048+
event.shiftKey &&
4049+
event.key.toLowerCase() === KEYS.F
4050+
) {
4051+
const selectedElements = this.scene.getSelectedElements(this.state);
4052+
4053+
if (
4054+
this.state.activeTool.type === "selection" &&
4055+
!selectedElements.length
4056+
) {
4057+
return;
4058+
}
4059+
4060+
if (
4061+
selectedElements.find(
4062+
(element) =>
4063+
isTextElement(element) ||
4064+
getBoundTextElement(
4065+
element,
4066+
this.scene.getNonDeletedElementsMap(),
4067+
),
4068+
)
4069+
) {
4070+
event.preventDefault();
4071+
this.setState({ openPopup: "fontFamily" });
4072+
}
4073+
}
4074+
40464075
if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
40474076
if (this.state.activeTool.type === "laser") {
40484077
this.setActiveTool({ type: "selection" });

packages/excalidraw/components/ButtonIcon.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,10 @@
33
.excalidraw {
44
button.standalone {
55
@include outlineButtonIconStyles;
6+
7+
& > * {
8+
// dissalow pointer events on chidlren, so we always have event.target on the button itself
9+
pointer-events: none;
10+
}
611
}
712
}

packages/excalidraw/components/ButtonIcon.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,26 @@ import "./ButtonIcon.scss";
66
interface ButtonIconProps {
77
icon: JSX.Element;
88
title: string;
9+
className?: string;
910
testId?: string;
1011
/** if not supplied, defaults to value identity check */
1112
active?: boolean;
12-
/** if not supplied, styles won't be included to not intefere with parent styles */
13+
/** include standalone style (could interfere with parent styles) */
1314
standalone?: boolean;
1415
onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
1516
}
1617

1718
export const ButtonIcon = forwardRef<HTMLButtonElement, ButtonIconProps>(
1819
(props, ref) => {
19-
const { title, testId, active, standalone, icon, onClick } = props;
20+
const { title, className, testId, active, standalone, icon, onClick } =
21+
props;
2022
return (
2123
<button
2224
ref={ref}
2325
key={title}
2426
title={title}
2527
data-testid={testId}
26-
className={clsx({ active, standalone })}
28+
className={clsx(className, { standalone, active })}
2729
onClick={onClick}
2830
>
2931
{icon}

packages/excalidraw/components/ColorPicker/ColorPicker.tsx

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isInteractive, isTransparent, isWritableElement } from "../../utils";
1+
import { isTransparent } from "../../utils";
22
import { ExcalidrawElement } from "../../element/types";
33
import { AppState } from "../../types";
44
import { TopPicks } from "./TopPicks";
@@ -14,9 +14,9 @@ import { ColorTuple, COLOR_PALETTE, ColorPaletteCustom } from "../../colors";
1414
import PickerHeading from "./PickerHeading";
1515
import { t } from "../../i18n";
1616
import clsx from "clsx";
17+
import { useRef } from "react";
1718
import { jotaiScope } from "../../jotai";
1819
import { ColorInput } from "./ColorInput";
19-
import { useRef } from "react";
2020
import { activeEyeDropperAtom } from "../EyeDropper";
2121
import { PropertiesPopover } from "../PropertiesPopover";
2222
import { useExcalidrawContainer } from "../App";
@@ -95,6 +95,7 @@ const ColorPickerPopupContent = ({
9595
/>
9696
</div>
9797
);
98+
9899
const popoverRef = useRef<HTMLDivElement>(null);
99100

100101
const focusPickerContent = () => {
@@ -108,6 +109,7 @@ const ColorPickerPopupContent = ({
108109
container={container}
109110
style={{ maxWidth: "208px" }}
110111
onFocusOutside={(event) => {
112+
// refocus due to eye dropper
111113
focusPickerContent();
112114
event.preventDefault();
113115
}}
@@ -119,18 +121,7 @@ const ColorPickerPopupContent = ({
119121
event.preventDefault();
120122
}
121123
}}
122-
onCloseAutoFocus={(e) => {
123-
e.stopPropagation();
124-
// prevents focusing the trigger
125-
e.preventDefault();
126-
127-
// return focus to excalidraw container unless
128-
// user focuses an interactive element, such as a button, or
129-
// enters the text editor by clicking on canvas with the text tool
130-
if (container && !isInteractive(document.activeElement)) {
131-
container.focus();
132-
}
133-
124+
onClose={() => {
134125
updateData({ openPopup: null });
135126
setActiveColorPickerSection(null);
136127
}}
@@ -166,8 +157,6 @@ const ColorPickerPopupContent = ({
166157
onEscape={(event) => {
167158
if (eyeDropperState) {
168159
setEyeDropperState(null);
169-
} else if (isWritableElement(event.target)) {
170-
focusPickerContent();
171160
} else {
172161
updateData({ openPopup: null });
173162
}
@@ -198,7 +187,7 @@ const ColorPickerTrigger = ({
198187
return (
199188
<Popover.Trigger
200189
type="button"
201-
className={clsx("color-picker__button active-color", {
190+
className={clsx("color-picker__button active-color properties-trigger", {
202191
"is-transparent": color === "transparent" || !color,
203192
})}
204193
aria-label={label}

packages/excalidraw/components/ColorPicker/Picker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export const Picker = ({
138138
event.stopPropagation();
139139
}
140140
}}
141-
className="color-picker-content"
141+
className="color-picker-content properties-content"
142142
// to allow focusing by clicking but not by tabbing
143143
tabIndex={-1}
144144
>

packages/excalidraw/components/FontPicker/FontPicker.scss

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
@import "../../css/variables.module.scss";
22

3-
// FIXME_FONTS: revisit for meaningful & pixel perfect styling
4-
// - magic values of rem / max-width
5-
// - width of the sidebar is not already 200px
6-
// - nothing is really pixel perfect according to the desing
7-
// - buttons width / height, label paddings, sidebar width, divers height, text align icons, etc.
8-
// - mixing div.h3 and fieldset.label for labels
9-
103
.excalidraw {
114
.FontPicker__container {
125
display: grid;

0 commit comments

Comments
 (0)