diff --git a/app/react-components/devtools.module.css b/app/react-components/devtools.module.css
index 27f57dc..a671109 100644
--- a/app/react-components/devtools.module.css
+++ b/app/react-components/devtools.module.css
@@ -7,7 +7,17 @@
.devtools {
background-color: #444444;
color: white;
- max-height: 100%;
- overflow-y: auto;
+ height: 100%;
+ overflow: hidden;
padding: 16px;
+ width: 100%;
}
+
+.devtools > :global(.react-tabs) {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ > :global(.react-tabs__tab-panel) {
+ overflow-y: auto;
+ }
+}
\ No newline at end of file
diff --git a/app/react-components/devtools/tiles.jsx b/app/react-components/devtools/tiles.jsx
index 859b856..3cfaa7d 100644
--- a/app/react-components/devtools/tiles.jsx
+++ b/app/react-components/devtools/tiles.jsx
@@ -1,6 +1,7 @@
import {useRef, useState} from 'react';
import {useEcs} from '@/context/ecs.js';
+import useRect from '@/util/react-hooks/use-rect.js';
import styles from './tiles.module.css';
@@ -11,8 +12,10 @@ export default function Tiles({
setLayer,
setStamp,
}) {
- const offsetRef = useRef();
- const [selection, setSelection] = useState({x: 0, y: 0, w: 2, h: 2});
+ const wrapperRef = useRef();
+ const imageRef = useRef();
+ const imageRect = useRect(imageRef);
+ const [selection, setSelection] = useState({x: 0, y: 0, w: 1, h: 1});
const [moveStart, setMoveStart] = useState();
const [ecs] = useEcs();
if (!ecs) {
@@ -25,6 +28,7 @@ export default function Tiles({
const {TileLayers} = master;
const {sourceJson, tileSize} = TileLayers.layer(0);
const {w, h} = sourceJson.meta.size;
+ const factor = (imageRect?.width ?? 1) / w;
return (
-
+
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
{
- if (!offsetRef.current) {
- return;
- }
- const {left, top} = offsetRef.current.getBoundingClientRect();
+ const wrapperRect = wrapperRef.current.getBoundingClientRect();
+ const {left, top} = wrapperRect;
if (
- event.clientX - left >= w
- || event.clientY - top >= h
+ event.clientX - left >= (w * factor)
+ || event.clientY - top >= (h * factor)
) {
return;
}
- const x = Math.floor((event.clientX - left) / tileSize.x);
- const y = Math.floor((event.clientY - top) / tileSize.y);
+ const x = Math.floor((event.clientX - left) / (tileSize.x * factor));
+ const y = Math.floor((event.clientY - top) / (tileSize.y * factor));
setMoveStart({x, y});
setSelection({x, y, w: 1, h: 1});
}}
onMouseMove={(event) => {
- if (!offsetRef.current) {
- return;
- }
if (!moveStart) {
return;
}
const {x: sx, y: sy} = moveStart;
- const {left, top} = offsetRef.current.getBoundingClientRect();
+ const wrapperRect = wrapperRef.current.getBoundingClientRect();
+ const {left, top} = wrapperRect;
const x = Math.floor(
- Math.max(0, Math.min(w - 1, (event.clientX - left)) / tileSize.x),
+ Math.max(0, Math.min((w * factor) - 1, (event.clientX - left)) / (tileSize.x * factor)),
);
const y = Math.floor(
- Math.max(0, Math.min(h - 1, (event.clientY - top)) / tileSize.y),
+ Math.max(0, Math.min((h * factor) - 1, (event.clientY - top)) / (tileSize.y * factor)),
);
const mx = Math.min(sx, x);
const my = Math.min(sy, y);
@@ -119,19 +119,20 @@ export default function Tiles({
setStamp(stamp);
}}
className={styles.selectionWrapper}
- ref={offsetRef}
+ ref={wrapperRef}
>
diff --git a/app/react-components/devtools/tiles.module.css b/app/react-components/devtools/tiles.module.css
index 9a7db77..5ed1869 100644
--- a/app/react-components/devtools/tiles.module.css
+++ b/app/react-components/devtools/tiles.module.css
@@ -10,6 +10,7 @@
.selectionWrapper img {
user-select: none;
+ width: 100%;
}
.selection {
diff --git a/app/util/react-hooks/use-rect.js b/app/util/react-hooks/use-rect.js
new file mode 100644
index 0000000..b4d5bf6
--- /dev/null
+++ b/app/util/react-hooks/use-rect.js
@@ -0,0 +1,16 @@
+import {useLayoutEffect, useState} from 'react'
+import useResizeObserver from '@react-hook/resize-observer'
+
+export default function useRect(target) {
+ const [rect, setRect] = useState();
+ useLayoutEffect(() => {
+ setRect(target.current.getBoundingClientRect())
+ }, [target]);
+ useResizeObserver(
+ target,
+ (entry) => {
+ setRect(entry.target.getBoundingClientRect());
+ },
+ );
+ return rect;
+}
diff --git a/package-lock.json b/package-lock.json
index f29334b..3986b5d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"@pixi/react": "^7.1.2",
"@pixi/spritesheet": "^7.4.2",
"@pixi/tilemap": "^4.1.0",
+ "@react-hook/resize-observer": "^2.0.1",
"@remix-run/express": "^2.9.2",
"@remix-run/node": "^2.9.2",
"@remix-run/react": "^2.9.2",
@@ -3020,6 +3021,11 @@
"integrity": "sha512-Lg3PnLp0QXpxwLIAuuJboLeRaIhrgJjeuh797QADg3xz8wGLugQOS5DpsE8A6i6Adgzf+bacllkKZG3J0tGfDw==",
"dev": true
},
+ "node_modules/@juggle/resize-observer": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
+ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
+ },
"node_modules/@leodeslf/simplex-noise": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@leodeslf/simplex-noise/-/simplex-noise-1.0.0.tgz",
@@ -3971,6 +3977,35 @@
}
}
},
+ "node_modules/@react-hook/latest": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@react-hook/latest/-/latest-1.0.3.tgz",
+ "integrity": "sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==",
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/@react-hook/passive-layout-effect": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz",
+ "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==",
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/@react-hook/resize-observer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@react-hook/resize-observer/-/resize-observer-2.0.1.tgz",
+ "integrity": "sha512-9PCX9grWfxdPizY8ohr+X4IkV1JhGMWr2Nm4ngbg6IcAIv0WBs7YoJcNBqYl22OqPHr5eOMItGcStZrmj2mbmQ==",
+ "dependencies": {
+ "@juggle/resize-observer": "^3.3.1",
+ "@react-hook/latest": "^1.0.2",
+ "@react-hook/passive-layout-effect": "^1.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=18"
+ }
+ },
"node_modules/@remix-run/dev": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@remix-run/dev/-/dev-2.9.2.tgz",
diff --git a/package.json b/package.json
index d0aedbe..53f4ded 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"@pixi/react": "^7.1.2",
"@pixi/spritesheet": "^7.4.2",
"@pixi/tilemap": "^4.1.0",
+ "@react-hook/resize-observer": "^2.0.1",
"@remix-run/express": "^2.9.2",
"@remix-run/node": "^2.9.2",
"@remix-run/react": "^2.9.2",