Skip to content

Commit f1318fe

Browse files
committed
Mobile improvements
1 parent 2463107 commit f1318fe

4 files changed

Lines changed: 125 additions & 22 deletions

File tree

app/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import CollectionLoader from "@/components/collection-loader";
88

99
export default function Home() {
1010
return (
11-
<main className="flex flex-col h-screen overflow-hidden">
11+
<main className="flex h-dvh flex-col overflow-hidden">
1212
<Suspense fallback={null}>
1313
<CollectionLoader />
1414
</Suspense>

components/iso-canvas.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { useCallback, useEffect, useRef } from "react";
3+
import { useCallback, useEffect, useRef, useState } from "react";
44
import { useMapStore } from "@/lib/store";
55
import { SPRITE_TILE_H, SPRITE_TILE_W } from "@/lib/tiles";
66
import {
@@ -10,10 +10,12 @@ import {
1010
} from "@/lib/textures";
1111

1212
export default function IsoCanvas() {
13+
const wrapperRef = useRef<HTMLDivElement>(null);
1314
const bgRef = useRef<HTMLCanvasElement>(null);
1415
const fgRef = useRef<HTMLCanvasElement>(null);
1516
const textureCacheRef = useRef<Map<string, HTMLImageElement>>(new Map());
1617
const isPlacingRef = useRef(false);
18+
const [displayScale, setDisplayScale] = useState(1);
1719

1820
const { map, gridSize, activeTool, setTile, clearTile, location } =
1921
useMapStore();
@@ -27,6 +29,25 @@ export default function IsoCanvas() {
2729
const originX = canvasWidth / 2;
2830
const originY = tileHeight * 2;
2931

32+
useEffect(() => {
33+
const wrapper = wrapperRef.current;
34+
if (!wrapper) return;
35+
36+
const updateScale = () => {
37+
const availableWidth = Math.max(wrapper.clientWidth - 16, 1);
38+
const availableHeight = Math.max(wrapper.clientHeight - 16, 1);
39+
const widthScale = availableWidth / canvasWidth;
40+
const heightScale = availableHeight / canvasHeight;
41+
const nextScale = Math.min(1, widthScale, heightScale);
42+
setDisplayScale(Number.isFinite(nextScale) && nextScale > 0 ? nextScale : 1);
43+
};
44+
45+
updateScale();
46+
const observer = new ResizeObserver(updateScale);
47+
observer.observe(wrapper);
48+
return () => observer.disconnect();
49+
}, [canvasWidth, canvasHeight]);
50+
3051
const getPosition = useCallback(
3152
(e: React.MouseEvent<HTMLCanvasElement>) => {
3253
const rect = e.currentTarget.getBoundingClientRect();
@@ -195,24 +216,28 @@ export default function IsoCanvas() {
195216

196217
return (
197218
<div
219+
ref={wrapperRef}
198220
id="iso-canvas-wrapper"
199-
className="relative flex-1 overflow-auto flex items-center justify-center bg-muted/30"
221+
className="relative flex flex-1 items-center justify-center overflow-hidden bg-muted/30 p-2"
200222
>
201223
<div
202224
className="relative"
203-
style={{ width: canvasWidth, height: canvasHeight }}
225+
style={{
226+
width: Math.round(canvasWidth * displayScale),
227+
height: Math.round(canvasHeight * displayScale),
228+
}}
204229
>
205230
<canvas
206231
ref={bgRef}
207232
width={canvasWidth}
208233
height={canvasHeight}
209-
className="absolute inset-0"
234+
className="absolute inset-0 h-full w-full touch-none"
210235
/>
211236
<canvas
212237
ref={fgRef}
213238
width={canvasWidth}
214239
height={canvasHeight}
215-
className="absolute inset-0"
240+
className="absolute inset-0 h-full w-full touch-none"
216241
onMouseDown={handleClick}
217242
onMouseMove={handleMouseMove}
218243
onMouseUp={handleMouseUp}

components/tile-picker.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,25 @@ export default function TilePicker() {
2525
: TEXTURE_PLACES.filter((place) => place.id === location);
2626

2727
return (
28-
<div className="border-t bg-background">
28+
<div className="shrink-0 border-t bg-background">
2929
<ScrollArea className="w-full">
30-
<div className="flex gap-4 p-3">
30+
<div className="flex gap-3 p-2 sm:gap-4 sm:p-3">
3131
<TooltipProvider delayDuration={200}>
3232
{visibleRealms.map((realm, realmIndex) => (
3333
<div key={realm.id} className="flex gap-2 items-end">
3434
{realmIndex > 0 && (
35-
<Separator orientation="vertical" className="h-40" />
35+
<Separator orientation="vertical" className="h-24 sm:h-40" />
3636
)}
3737
<div className="flex flex-col gap-2">
3838
{isMixed && (
39-
<span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground px-1 whitespace-nowrap">
39+
<span className="hidden px-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground whitespace-nowrap sm:inline">
4040
{realm.label}
4141
</span>
4242
)}
4343
<div className="flex gap-2">
4444
{TILE_GROUPS.map((group) => (
4545
<div key={`${realm.id}-${group.name}`} className="flex flex-col gap-1">
46-
<span className="text-xs font-semibold text-muted-foreground px-1 whitespace-nowrap">
46+
<span className="px-1 text-[11px] font-semibold text-muted-foreground whitespace-nowrap sm:text-xs">
4747
{group.name}
4848
</span>
4949
<div className="flex gap-1">
@@ -59,14 +59,12 @@ export default function TilePicker() {
5959
<TooltipTrigger asChild>
6060
<button
6161
className={cn(
62-
"block border-2 rounded-md overflow-hidden transition-colors shrink-0",
62+
"block h-[96px] w-[54px] shrink-0 overflow-hidden rounded-md border-2 transition-colors sm:h-[115px] sm:w-[65px]",
6363
isActive
6464
? "border-primary ring-2 ring-primary/30"
6565
: "border-transparent hover:border-muted-foreground/30",
6666
)}
6767
style={{
68-
width: 65,
69-
height: 115,
7068
backgroundImage: `url('${getTexturePath(realm.id)}')`,
7169
backgroundRepeat: "no-repeat",
7270
backgroundPosition: `-${(tile.col * SPRITE_TILE_W) / 2 + 1}px -${(group.row * SPRITE_TILE_H) / 2}px`,

components/toolbar.tsx

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useEffect, useRef, useState } from "react";
44
import Link from "next/link";
5-
import { Coffee, Github, Heart, Twitter } from "lucide-react";
5+
import { Coffee, Github, Heart, Menu, Twitter } from "lucide-react";
66
import { HugeiconsIcon } from "@hugeicons/react";
77
import {
88
GridTableIcon,
@@ -299,10 +299,12 @@ export default function Toolbar() {
299299
}, [undo, canUndo]);
300300

301301
return (
302-
<div className="flex items-center gap-2 border-b bg-background px-4 py-2">
303-
<Link href="/" className="flex items-center gap-2 mr-4">
302+
<div className="flex items-center gap-1 border-b bg-background px-2 py-2 sm:gap-2 sm:px-4">
303+
<Link href="/" className="mr-1 flex shrink-0 items-center gap-2 sm:mr-4">
304304
<img src="/logo.png" alt="Isoshire" className="w-10 object-contain" />
305-
<h1 className={`text-3xl font-bold ${bilboSwashCaps.className}`}>
305+
<h1
306+
className={`hidden text-3xl font-bold md:block ${bilboSwashCaps.className}`}
307+
>
306308
Iso Middle Earth
307309
</h1>
308310
</Link>
@@ -356,7 +358,7 @@ export default function Toolbar() {
356358
>
357359
<SelectTrigger
358360
aria-label="Choose location"
359-
className="border-zinc-300 bg-linear-to-t from-muted to-background shadow-xs shadow-zinc-950/10 hover:to-muted dark:from-muted/50 dark:border-border duration-200"
361+
className="w-[118px] border-zinc-300 bg-linear-to-t from-muted to-background shadow-xs shadow-zinc-950/10 duration-200 hover:to-muted sm:w-[156px] dark:border-border dark:from-muted/50"
360362
>
361363
<SelectValue placeholder="Choose location" />
362364
</SelectTrigger>
@@ -390,10 +392,10 @@ export default function Toolbar() {
390392
<TooltipTrigger asChild>
391393
<Button
392394
variant="outline"
395+
size="icon"
393396
onClick={undo}
394397
disabled={!canUndo}
395398
aria-label="Undo"
396-
className="gap-2"
397399
>
398400
<HugeiconsIcon icon={Undo02Icon} />
399401
</Button>
@@ -414,6 +416,7 @@ export default function Toolbar() {
414416
size="icon"
415417
onClick={handleExport}
416418
aria-label="Export PNG"
419+
className="hidden sm:inline-flex"
417420
>
418421
<HugeiconsIcon icon={Download01Icon} className="h-4 w-4" />
419422
</Button>
@@ -425,6 +428,7 @@ export default function Toolbar() {
425428
variant="outline"
426429
size="icon"
427430
aria-label="Save and load states"
431+
className="hidden sm:inline-flex"
428432
>
429433
<HugeiconsIcon icon={FloppyDiskIcon} className="h-4 w-4" />
430434
</Button>
@@ -475,13 +479,89 @@ export default function Toolbar() {
475479
</DialogContent>
476480
</Dialog>
477481

478-
<Button variant="outline" size="icon" asChild aria-label="Collections">
482+
<Button
483+
variant="outline"
484+
size="icon"
485+
asChild
486+
aria-label="Collections"
487+
className="hidden sm:inline-flex"
488+
>
479489
<Link href="/collections">
480490
<HugeiconsIcon icon={AdventureIcon} />
481491
</Link>
482492
</Button>
483493

484-
<div className="ml-auto flex items-center gap-1">
494+
<Dialog>
495+
<DialogTrigger asChild>
496+
<Button
497+
variant="outline"
498+
size="icon"
499+
className="ml-auto sm:hidden"
500+
aria-label="Open more options"
501+
>
502+
<Menu className="h-4 w-4" />
503+
</Button>
504+
</DialogTrigger>
505+
<DialogContent className="max-w-xs">
506+
<DialogHeader>
507+
<DialogTitle>More</DialogTitle>
508+
</DialogHeader>
509+
<div className="grid gap-2">
510+
<Button
511+
variant="outline"
512+
className="justify-start gap-2"
513+
onClick={handleExport}
514+
>
515+
<HugeiconsIcon icon={Download01Icon} className="h-4 w-4" />
516+
Export PNG
517+
</Button>
518+
<Button variant="outline" asChild className="justify-start gap-2">
519+
<Link href="/collections">
520+
<HugeiconsIcon icon={AdventureIcon} className="h-4 w-4" />
521+
Collections
522+
</Link>
523+
</Button>
524+
<Button variant="outline" asChild className="justify-start gap-2">
525+
<a href="https://x.com/strad3r" target="_blank" rel="noreferrer">
526+
<Twitter className="h-4 w-4" />
527+
X / Twitter
528+
</a>
529+
</Button>
530+
<Button variant="outline" asChild className="justify-start gap-2">
531+
<a
532+
href="https://github.com/hasanharman/isoshire"
533+
target="_blank"
534+
rel="noreferrer"
535+
>
536+
<Github className="h-4 w-4" />
537+
GitHub
538+
</a>
539+
</Button>
540+
<Button variant="outline" asChild className="justify-start gap-2">
541+
<a
542+
href="https://github.com/sponsors/hasanharman"
543+
target="_blank"
544+
rel="noreferrer"
545+
>
546+
<Heart className="h-4 w-4" />
547+
Sponsor
548+
</a>
549+
</Button>
550+
<Button variant="outline" asChild className="justify-start gap-2">
551+
<a
552+
href="https://buymeacoffee.com/hasanharman"
553+
target="_blank"
554+
rel="noreferrer"
555+
>
556+
<Coffee className="h-4 w-4" />
557+
Buy Me a Coffee
558+
</a>
559+
</Button>
560+
</div>
561+
</DialogContent>
562+
</Dialog>
563+
564+
<div className="ml-auto hidden items-center gap-1 sm:flex">
485565
<Button variant="ghost" size="icon" asChild aria-label="X @strad3r">
486566
<a href="https://x.com/strad3r" target="_blank" rel="noreferrer">
487567
<Twitter className="h-4 w-4" />

0 commit comments

Comments
 (0)