Skip to content

Commit c98743e

Browse files
Nicellpetejohanson
authored andcommitted
feat: Auto-zoom keymap layout
1 parent 028c1c9 commit c98743e

File tree

3 files changed

+89
-11
lines changed

3 files changed

+89
-11
lines changed

src/keyboard/Keyboard.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { BehaviorBindingPicker } from "../behaviors/BehaviorBindingPicker";
2929
import { produce } from "immer";
3030
import { LockStateContext } from "../rpc/LockStateContext";
3131
import { LockState } from "@zmkfirmware/zmk-studio-ts-client/core";
32+
import { LayoutZoom } from "./PhysicalLayout";
3233

3334
type BehaviorMap = Record<number, GetBehaviorDetailsResponse>;
3435

@@ -172,6 +173,7 @@ export default function Keyboard() {
172173
true
173174
);
174175

176+
const [keymapScale, setKeymapScale] = useState<LayoutZoom>("auto");
175177
const [selectedLayerIndex, setSelectedLayerIndex] = useState<number>(0);
176178
const [selectedKeyPosition, setSelectedKeyPosition] = useState<
177179
number | undefined
@@ -514,15 +516,37 @@ export default function Keyboard() {
514516
)}
515517
</div>
516518
{layouts && keymap && behaviors && (
517-
<div className="col-start-2 row-start-1 grid items-center justify-center">
519+
<div className="col-start-2 row-start-1 grid items-center justify-center relative">
518520
<KeymapComp
519521
keymap={keymap}
520522
layout={layouts[selectedPhysicalLayoutIndex]}
521523
behaviors={behaviors}
524+
scale={keymapScale}
522525
selectedLayerIndex={selectedLayerIndex}
523526
selectedKeyPosition={selectedKeyPosition}
524527
onKeyPositionClicked={setSelectedKeyPosition}
525528
/>
529+
<select
530+
className="absolute top-2 right-2 h-8 rounded px-2"
531+
value={keymapScale}
532+
onChange={(e) => {
533+
const value = e.target.value;
534+
if (value === "auto") {
535+
setKeymapScale("auto");
536+
} else {
537+
setKeymapScale(parseFloat(value));
538+
}
539+
}}
540+
>
541+
<option value="auto">Auto</option>
542+
<option value={0.25}>25%</option>
543+
<option value={0.5}>50%</option>
544+
<option value={0.75}>75%</option>
545+
<option value={1}>100%</option>
546+
<option value={1.25}>125%</option>
547+
<option value={1.5}>150%</option>
548+
<option value={2}>200%</option>
549+
</select>
526550
</div>
527551
)}
528552
{keymap && selectedBinding && (

src/keyboard/Keymap.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import {
99
hid_usage_page_and_id_from_usage,
1010
} from "../hid-usages";
1111

12-
import { PhysicalLayout as PhysicalLayoutComp } from "./PhysicalLayout";
12+
import { LayoutZoom, PhysicalLayout as PhysicalLayoutComp } from "./PhysicalLayout";
1313

1414
type BehaviorMap = Record<number, GetBehaviorDetailsResponse>;
1515

1616
export interface KeymapProps {
1717
layout: PhysicalLayout;
1818
keymap: KeymapMsg;
1919
behaviors: BehaviorMap;
20+
scale: LayoutZoom;
2021
selectedLayerIndex: number;
2122
selectedKeyPosition: number | undefined;
2223
onKeyPositionClicked: (keyPosition: number) => void;
@@ -26,6 +27,7 @@ export const Keymap = ({
2627
layout,
2728
keymap,
2829
behaviors,
30+
scale,
2931
selectedLayerIndex,
3032
selectedKeyPosition,
3133
onKeyPositionClicked,
@@ -75,6 +77,7 @@ export const Keymap = ({
7577
positions={positions}
7678
oneU={48}
7779
hoverZoom={true}
80+
zoom={scale}
7881
selectedPosition={selectedKeyPosition}
7982
onPositionClicked={onKeyPositionClicked}
8083
/>

src/keyboard/PhysicalLayout.tsx

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { CSSProperties, PropsWithChildren } from "react";
1+
import {
2+
CSSProperties,
3+
PropsWithChildren,
4+
useEffect,
5+
useRef,
6+
useState,
7+
} from "react";
28
import { Key } from "./Key";
39

410
export type KeyPosition = PropsWithChildren<{
@@ -12,11 +18,14 @@ export type KeyPosition = PropsWithChildren<{
1218
ry?: number;
1319
}>;
1420

21+
export type LayoutZoom = number | "auto";
22+
1523
interface PhysicalLayoutProps {
1624
positions: Array<KeyPosition>;
1725
selectedPosition?: number;
1826
oneU?: number;
1927
hoverZoom?: boolean;
28+
zoom?: LayoutZoom;
2029
onPositionClicked?: (position: number) => void;
2130
}
2231

@@ -30,7 +39,7 @@ interface PhysicalLayoutPositionLocation {
3039

3140
function scalePosition(
3241
{ x, y, r, rx, ry }: PhysicalLayoutPositionLocation,
33-
oneU: number
42+
oneU: number,
3443
): CSSProperties {
3544
let left = x * oneU;
3645
let top = y * oneU;
@@ -61,6 +70,42 @@ export const PhysicalLayout = ({
6170
onPositionClicked,
6271
...props
6372
}: PhysicalLayoutProps) => {
73+
const ref = useRef<HTMLDivElement>(null);
74+
const [scale, setScale] = useState(1);
75+
76+
useEffect(() => {
77+
const element = ref.current;
78+
if (!element) return;
79+
80+
const parent = element.parentElement;
81+
if (!parent) return;
82+
83+
const calculateScale = () => {
84+
if (props.zoom === "auto") {
85+
const newScale = Math.min(
86+
parent.clientWidth / element.clientWidth,
87+
parent.clientHeight / element.clientHeight,
88+
);
89+
setScale(newScale);
90+
} else {
91+
setScale(props.zoom || 1);
92+
}
93+
};
94+
95+
calculateScale(); // Initial calculation
96+
97+
const resizeObserver = new ResizeObserver(() => {
98+
calculateScale();
99+
});
100+
101+
resizeObserver.observe(element);
102+
resizeObserver.observe(parent);
103+
104+
return () => {
105+
resizeObserver.disconnect();
106+
};
107+
}, [props.zoom]);
108+
64109
// TODO: Add a bit of padding for rotation when supported
65110
let rightMost = positions
66111
.map((k) => k.x + k.width)
@@ -88,14 +133,20 @@ export const PhysicalLayout = ({
88133

89134
return (
90135
<div
91-
className="relative"
92-
style={{
93-
height: bottomMost * oneU + "px",
94-
width: rightMost * oneU + "px",
95-
}}
96-
{...props}
136+
className="p-4 box-content"
137+
ref={ref}
138+
style={{ transform: `scale(${scale})` }}
97139
>
98-
{positionItems}
140+
<div
141+
className="relative"
142+
style={{
143+
height: bottomMost * oneU + "px",
144+
width: rightMost * oneU + "px",
145+
}}
146+
{...props}
147+
>
148+
{positionItems}
149+
</div>
99150
</div>
100151
);
101152
};

0 commit comments

Comments
 (0)