Skip to content

Commit

Permalink
fix: code review revisions
Browse files Browse the repository at this point in the history
  • Loading branch information
pongstr committed Dec 23, 2024
1 parent e617a01 commit f76a3a3
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 83 deletions.
114 changes: 51 additions & 63 deletions src/keyboard/KeyboardViewport.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,27 @@
import { FC, PropsWithChildren, useEffect, useRef } from "react";
import { useLocalStorageState } from "../misc/useLocalStorageState";
import { ExpandIcon, MaximizeIcon, ShrinkIcon } from "lucide-react";

type KeyboardViewportType = PropsWithChildren<{
className?: string;
}>;

const KEYMAP_SCALE = "keymap:scale";
const DEFAULT_SCALE = window.localStorage.getItem(KEYMAP_SCALE) ?? "1";
const KEYMAP_SCALE = "keymapScale";
const DEFAULT_SCALE = 1;

export const KeyboardViewport: FC<KeyboardViewportType> = ({
children,
className,
}) => {
const targetRef = useRef<HTMLDivElement>(null);
const scaleRef = useRef<HTMLInputElement>(null);

const setScale = (param: "increase" | "decrease") => {
if (!targetRef.current || !scaleRef.current) return;

const current = scaleRef.current.value;

if (param === "increase" && Number(current) < 2) {
scaleRef.current.value = String(Number(scaleRef.current.value) + 0.2);
}

if (param === "decrease" && Number(current) > 0.2) {
scaleRef.current.value = String(Number(scaleRef.current.value) - 0.2);
}

localStorage.setItem(KEYMAP_SCALE, scaleRef.current.value);
targetRef.current.style.setProperty(
"transform",
`scale(${scaleRef.current.value})`,
);
};
const [scale, setScale] = useLocalStorageState(KEYMAP_SCALE, DEFAULT_SCALE);

const resetScale = () => {
if (!targetRef.current || !scaleRef.current) return;
if (!targetRef.current) return;
targetRef.current.style.translate = "unset";
targetRef.current.style.setProperty("transform", "scale(1)");
scaleRef.current.value = "1";
localStorage.setItem(KEYMAP_SCALE, "1");
setScale(DEFAULT_SCALE);
};

useEffect(() => {
Expand All @@ -49,15 +31,23 @@ export const KeyboardViewport: FC<KeyboardViewportType> = ({
const offset = { x: 0, y: 0 };
let isPanningActive = false;

function panStart(e: KeyboardEvent) {
function keyDownPanStart(e: KeyboardEvent) {
if (e.key !== " ") return;
e.preventDefault();

target.style.cursor = "grab";
isPanningActive = true;
}

function panEnd(e: KeyboardEvent) {
function pointerDownPanStart(e: PointerEvent) {
if (e.button !== 0) return;
e.preventDefault();

target.style.cursor = "grab";
isPanningActive = true;
}

function keyUpPanEnd(e: KeyboardEvent) {
if (e.key !== " ") return;
isPanningActive = false;
target.style.cursor = "unset";
Expand All @@ -70,35 +60,27 @@ export const KeyboardViewport: FC<KeyboardViewportType> = ({
target.style.translate = `${offset.x}px ${offset.y}px`;
}

document.addEventListener("keydown", panStart);
document.addEventListener("keyup", panEnd);
target.addEventListener("pointermove", panMove);

return () => {
document.removeEventListener("keydown", panStart);
document.removeEventListener("keyup", panEnd);
target.removeEventListener("pointermove", panMove);
};
}, []);

useEffect(() => {
if (!scaleRef.current || !targetRef.current) return;

const input = scaleRef.current;
const target = targetRef.current;
function pointerUpPanEnd() {
isPanningActive = false;
target.style.cursor = "unset";
}

input.value = DEFAULT_SCALE;
target.style.setProperty("transform", `scale(${DEFAULT_SCALE})`);
document.addEventListener("keydown", keyDownPanStart);
document.addEventListener("keyup", keyUpPanEnd);

function onInputChange(e: Event) {
const value = (e.currentTarget as HTMLInputElement).value;
target.style.setProperty("transform", `scale(${value})`);
localStorage.setItem(KEYMAP_SCALE, value);
}
target.addEventListener("pointermove", panMove);
target.addEventListener("pointerdown", pointerDownPanStart);
target.addEventListener("pointerup", pointerUpPanEnd);
target.addEventListener("pointerleave", pointerUpPanEnd);

input.addEventListener("change", onInputChange);
return () => {
input.removeEventListener("change", onInputChange);
document.removeEventListener("keydown", keyDownPanStart);
document.removeEventListener("keyup", keyUpPanEnd);

target.removeEventListener("pointermove", panMove);
target.removeEventListener("pointerdown", pointerDownPanStart);
target.removeEventListener("pointerup", pointerUpPanEnd);
target.removeEventListener("pointerleave", pointerUpPanEnd);
};
}, []);

Expand All @@ -111,17 +93,20 @@ export const KeyboardViewport: FC<KeyboardViewportType> = ({
>
<div
ref={targetRef}
style={{ transform: `scale(${scale})` }}
className="flex size-full origin-center items-center justify-center transition-transform"
>
{children}
</div>

<div className="absolute bottom-[10px] left-1/2 ml-[-170px] flex justify-center items-center w-[298px] gap-1 rounded-xl bg-muted py-1 select-none bg-base-300">
<div className="absolute bottom-8 left-0 flex justify-center items-center w-full gap-1 rounded-xl bg-muted py-1 select-none bg-base-300">
<button
className="block px-4 py-1.5 bg-base-100 rounded-l-lg"
onClick={() => setScale("decrease")}
className="block h-9 px-4 py-1.5 bg-base-100 rounded-l-lg disabled:opacity-50 disabled:cursor-not-allowed"
disabled={scale <= 0.25}
onClick={() => setScale((prev: number) => prev - 0.05)}
>
-
<ShrinkIcon className="size-4" />
<span className="sr-only">Decrease scale</span>
</button>
<div className="flex h-9 px-2 justify-center items-center bg-base-100">
<input
Expand All @@ -130,22 +115,25 @@ export const KeyboardViewport: FC<KeyboardViewportType> = ({
min={0.25}
max={2}
step={0.01}
ref={scaleRef}
defaultValue={DEFAULT_SCALE}
className="mx-auto h-1 w-28 cursor-pointer appearance-none rounded-lg"
value={scale}
onChange={(e) => setScale(Number(e.target.value))}
/>
</div>
<button
className="block px-4 py-1.5 bg-base-100"
onClick={() => setScale("increase")}
className="block h-9 px-4 py-1.5 bg-base-100 disabled:opacity-50 disabled:cursor-not-allowed"
disabled={scale >= 2}
onClick={() => setScale((prev: number) => prev + 0.05)}
>
+
<ExpandIcon className="size-4" />
<span className="sr-only">Increase scale</span>
</button>
<button
className="block px-4 py-1.5 bg-base-100 rounded-r-lg"
className="block px-4 py-1.5 bg-base-100 rounded-r-lg h-9 disabled:opacity-50 disabled:cursor-not-allowed"
onClick={resetScale}
>
Auto
<MaximizeIcon className="size-4" />
<span className="sr-only">Reset scale</span>
</button>
</div>
</div>
Expand Down
42 changes: 22 additions & 20 deletions src/misc/useLocalStorageState.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { useEffect, useState } from "react";

function basicSerialize<T>(value: T): string {
if (typeof value === "object") {
return JSON.stringify(value);
return typeof value !== "string" ? JSON.stringify(value) : String(value);
}

function toJson<T>(value: string): T {
try {
return JSON.parse(value) as T;
} catch {
return value as T;
}
return String(value);
}

export function useLocalStorageState<T>(
Expand All @@ -14,25 +19,22 @@ export function useLocalStorageState<T>(
serialize?: (value: T) => string;
deserialize?: (value: string) => T;
},
) {
const reactState = useState<T>(() => {
const savedValue = localStorage.getItem(key);
if (savedValue !== null) {
if (options?.deserialize) {
return options.deserialize(savedValue);
}
return savedValue as T; // Assuming T is a string
}
return defaultValue;
});
): [T, React.Dispatch<React.SetStateAction<T>>] {
const [state, setState] = useState<T>(() => {
const saved = localStorage.getItem(key);

const [state] = reactState;
if (saved === null) return defaultValue;
return (
options?.deserialize?.(saved) ?? (toJson(saved) as T) ?? defaultValue
);
});

useEffect(() => {
const serializedState =
options?.serialize?.(state) || basicSerialize(state);
localStorage.setItem(key, serializedState);
}, [state, key, options]);
localStorage.setItem(
key,
options?.serialize?.(state) ?? basicSerialize(state),
);
}, [key, state, options]);

return reactState;
return [state, setState];
}

0 comments on commit f76a3a3

Please sign in to comment.