Skip to content

Commit

Permalink
Handle press & long-press events on current pentomino window (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
RheingoldRiver authored Nov 3, 2023
1 parent 918df74 commit 74fcca7
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 24 deletions.
17 changes: 16 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-router-dom": "^6.16.0"
"react-router-dom": "^6.16.0",
"use-long-press": "^3.2.0"
},
"devDependencies": {
"@savvywombat/tailwindcss-grid-areas": "^3.1.0",
Expand Down
64 changes: 57 additions & 7 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import clsx from "clsx";
import { useContext } from "react";
import { useContext, useRef } from "react";
import { GameStateContext } from "../GameStateProvider/GameStateProvider";
import { PENTOMINOES } from "../../pentominoes";
import { PentominoDisplay } from "../PentominoDisplay/PentominoDisplay";
import { ALL_PENTOMINO_NAMES } from "../../constants";
import { AppStateContext } from "../AppStateProvider/AppStateProvider";
import {
OrientationActionType,
ReflectionDirection,
RotationDirection,
} from "../GameStateProvider/currentPentominoReducer";
import { useLongPress } from "use-long-press";

export const Header = ({ ...rest }) => {
const { currentPentomino, updateCurrentPentomino, currentOrientation, pentominoColors, showKeyboardIndicators } =
const { currentPentomino, updateCurrentPentomino, pentominoColors, showKeyboardIndicators } =
useContext(GameStateContext);

const { appPreferences } = useContext(AppStateContext);
Expand Down Expand Up @@ -49,12 +55,56 @@ export const Header = ({ ...rest }) => {
"border-black dark:border-slate-50"
)}
>
<PentominoDisplay
pentomino={currentPentomino}
color={appPreferences.displayColors[pentominoColors[currentPentomino.name]]}
orientation={currentOrientation}
></PentominoDisplay>
<CurrentPentominoDisplay />
</div>
</div>
);
};

const CurrentPentominoDisplay = () => {
const { currentPentomino, currentOrientation, orientationDispatch, pentominoColors } = useContext(GameStateContext);

// ensure that the normal onclick doesn't trigger when releasing the long-press hook
// by monitoring status of the mouse button
// 1. set to false on mouse down (don't know what to handle yet)
// 2. if mouse up before it's changed, fire normal event
// 3. set to true when long-press happens
// 4. if mouse up after this, do nothing
// 5. will be set back to false on next mouse-down event
const longPressTriggered = useRef(false);

const { appPreferences } = useContext(AppStateContext);
const bind = useLongPress((e) => {
e.preventDefault();
e.stopPropagation();
orientationDispatch({
type: OrientationActionType.reflect,
direction: ReflectionDirection.Y,
});
longPressTriggered.current = true;
});

return (
<button
className="cursor-pointer"
onMouseDown={() => (longPressTriggered.current = false)}
onMouseUp={(e) => {
e.preventDefault();
e.stopPropagation();
if (longPressTriggered.current === true) return;
orientationDispatch({
type: OrientationActionType.rotate,
direction: RotationDirection.Right,
});
}}
onContextMenu={(e) => e.preventDefault()}
{...bind()}
>
<PentominoDisplay
pentomino={currentPentomino}
color={appPreferences.displayColors[pentominoColors[currentPentomino.name]]}
orientation={currentOrientation}
></PentominoDisplay>
</button>
);
};
38 changes: 26 additions & 12 deletions src/components/Information/Information.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,37 +111,51 @@ export const Information = () => {
>
<Dialog.Title className="text-center font-bold text-md mb-2">About Pentominoes</Dialog.Title>
<div className="px-4 pb-4">
<div className="mb-2">
<p className="mb-2">
Pentominoes are tiles of area 5. There are 12 distinct pentominoes, up to rotation & reflection, with each
tile having somewhere between 2 (the {<InformationPentominoDisplay p="I" />} tile) and 8 (
{<InformationPentominoDisplay p="F" />} {<InformationPentominoDisplay p="L" />}{" "}
{<InformationPentominoDisplay p="N" />} {<InformationPentominoDisplay p="P" />}{" "}
{<InformationPentominoDisplay p="Y" />}) distinct orientations.
</div>
<div className="mb-2">
</p>
<p className="mb-2">
This puzzle game also provides a one-square-unit-area tile that you can use as terrain (the{" "}
<InformationPentominoDisplay p="R"></InformationPentominoDisplay> tile).
</div>
<div className="mb-2">
</p>
<p className="mb-2">
There are several different ways to enjoy Pentominoes, but the common theme is that you will try to fully tile
a grid of total area 60 (5x12=60) such that no pentominoes overlap or fall off the edge, and no empty squares
remain (other than whatever terrain you choose to place before starting to solve the puzzle).
</div>
<div className="mb-2">
</p>
<p className="mb-2">
Generally, you want to use one of each pentomino to tile the board, but you're welcome to use this app however
you like, and there are no prohibitions against using a tile more than once unless you want there to be. One
suggestion is to attempt to tile an area with just the {<InformationPentominoDisplay p="P" />} tile.
</div>
<div className="mb-2">
</p>
<p className="mb-2">
For an added challenge, you can also choose to apply "colorways" to your tiles. Then, constrain yourself to
make a solve where the 4 tiles of some color must be pairwise non-adjacent; or must be adjacent; or must be
adjacent and form a line spanning the grid area (this last one is especially fun in 8x8 grids with 4 squares
of terrain). Setting these colorways is available in the Settings dialog.
</div>
<div>
</p>
<p>
If you're new to pentominoes, feel free to "cheat" in your first few solve attempts and move terrain around,
or use one piece twice - this is a single-player puzzle game, so the rules are whatever you make them to be!
</div>
</p>
<Dialog.Title className="text-center font-bold text-md mb-2">Controls</Dialog.Title>
<p className="mb-2">
Select a tile to select it. Once selected, you can reorient it in one of the following ways:
</p>
<ul className="list-disc ml-8 mb-2">
<li>Use the rotation & reflection buttons</li>
<li>Click or tap on the highlighted tile to rotate clockwise; long-press to reflect horizontally</li>
<li>Use hotkeys (see below)</li>
</ul>
<p className="mb-2">
Once you've oriented it as desired, click the board to place it. You can also click a placed tile to remove
it. Removing a tile also sets this tile as the current removed tile, in the same orientation it was when
placed.
</p>
<Dialog.Title className="text-center font-bold text-md mb-2">Hotkeys</Dialog.Title>
<span className="italic mb-2">
You can{" "}
Expand Down
11 changes: 8 additions & 3 deletions src/components/PentominoDisplay/PentominoDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const PentominoDisplay = ({
return { class: "", style: "" };
}

// Do not return any divs because these appear inside of <p> elements in the Information modal.
return (
<span
{...rest}
Expand All @@ -65,9 +66,13 @@ export const PentominoDisplay = ({
row.map((cell, y) => {
const bg = bgColor(cell, { x, y });
return (
<div
<span
key={`${pentomino.name}_${x}_${y}`}
className={clsx(PENTOMINO_SIZES[size], bg.class, "text-white flex flex-row items-center justify-center")}
className={clsx(
PENTOMINO_SIZES[size],
bg.class,
"text-white flex flex-row items-center justify-center display-block"
)}
style={{
fontSize: "1rem",
lineHeight: "0.5rem",
Expand All @@ -77,7 +82,7 @@ export const PentominoDisplay = ({
{x === p.center.x && y === p.center.y && showCenter && pentomino.name !== PENTOMINOES.R.name && (
<DotFilledIcon />
)}
</div>
</span>
);
})
)}
Expand Down

0 comments on commit 74fcca7

Please sign in to comment.