| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- import state, { useSelector } from "state"
- import { motion } from "framer-motion"
- import styled from "styles"
- import inputs from "state/inputs"
- import { useRef } from "react"
-
- export default function Bounds() {
- const zoom = useSelector((state) => state.data.camera.zoom)
- const bounds = useSelector((state) => state.values.selectedBounds)
- const isBrushing = useSelector((state) => state.isIn("brushSelecting"))
-
- if (!bounds) return null
-
- const { minX, minY, maxX, maxY, width, height } = bounds
-
- const p = 4 / zoom
- const cp = p * 2
-
- return (
- <g pointerEvents={isBrushing ? "none" : "all"}>
- <StyledBounds
- x={minX}
- y={minY}
- width={width}
- height={height}
- pointerEvents="none"
- />
- {width * zoom > 8 && (
- <>
- <Corner
- x={minX}
- y={minY}
- width={cp}
- height={cp}
- corner="top_left_corner"
- />
- <Corner
- x={maxX}
- y={minY}
- width={cp}
- height={cp}
- corner="top_right_corner"
- />
- <Corner
- x={maxX}
- y={maxY}
- width={cp}
- height={cp}
- corner="bottom_right_corner"
- />
- <Corner
- x={minX}
- y={maxY}
- width={cp}
- height={cp}
- corner="bottom_left_corner"
- />
- <EdgeHorizontal
- x={minX + p}
- y={minY}
- width={Math.max(0, width - p * 2)}
- height={p}
- edge="top_edge"
- />
- <EdgeVertical
- x={maxX}
- y={minY + p}
- width={p}
- height={Math.max(0, height - p * 2)}
- edge="right_edge"
- />
- <EdgeHorizontal
- x={minX + p}
- y={maxY}
- width={Math.max(0, width - p * 2)}
- height={p}
- edge="bottom_edge"
- />
- <EdgeVertical
- x={minX}
- y={minY + p}
- width={p}
- height={Math.max(0, height - p * 2)}
- edge="left_edge"
- />
- </>
- )}
- </g>
- )
- }
-
- function Corner({
- x,
- y,
- width,
- height,
- corner,
- }: {
- x: number
- y: number
- width: number
- height: number
- corner:
- | "top_left_corner"
- | "top_right_corner"
- | "bottom_right_corner"
- | "bottom_left_corner"
- }) {
- const rRotateCorner = useRef<SVGRectElement>(null)
- const rCorner = useRef<SVGRectElement>(null)
-
- const isTop = corner.includes("top")
- const isLeft = corner.includes("bottom")
-
- return (
- <g>
- <StyledRotateCorner
- ref={rRotateCorner}
- x={x + width * (isLeft ? -1.25 : -0.5)}
- y={y + width * (isTop ? -1.25 : -0.5)}
- width={width * 1.75}
- height={height * 1.75}
- onPointerDown={(e) => {
- e.stopPropagation()
- rRotateCorner.current.setPointerCapture(e.pointerId)
- state.send("POINTED_ROTATE_CORNER", inputs.pointerDown(e, corner))
- }}
- onPointerUp={(e) => {
- e.stopPropagation()
- rRotateCorner.current.releasePointerCapture(e.pointerId)
- rRotateCorner.current.replaceWith(rRotateCorner.current)
- state.send("STOPPED_POINTING", inputs.pointerDown(e, corner))
- }}
- />
- <StyledCorner
- ref={rCorner}
- x={x + width * -0.5}
- y={y + height * -0.5}
- width={width}
- height={height}
- corner={corner}
- onPointerDown={(e) => {
- e.stopPropagation()
- rCorner.current.setPointerCapture(e.pointerId)
- state.send("POINTED_BOUNDS_CORNER", inputs.pointerDown(e, corner))
- }}
- onPointerUp={(e) => {
- e.stopPropagation()
- rCorner.current.releasePointerCapture(e.pointerId)
- rCorner.current.replaceWith(rCorner.current)
- state.send("STOPPED_POINTING", inputs.pointerDown(e, corner))
- }}
- />
- </g>
- )
- }
-
- function EdgeHorizontal({
- x,
- y,
- width,
- height,
- edge,
- }: {
- x: number
- y: number
- width: number
- height: number
- edge: "top_edge" | "bottom_edge"
- }) {
- const rEdge = useRef<SVGRectElement>(null)
-
- return (
- <StyledEdge
- ref={rEdge}
- x={x}
- y={y - height / 2}
- width={width}
- height={height}
- onPointerDown={(e) => {
- e.stopPropagation()
- rEdge.current.setPointerCapture(e.pointerId)
- state.send("POINTED_BOUNDS_EDGE", inputs.pointerDown(e, edge))
- }}
- onPointerUp={(e) => {
- e.stopPropagation()
- e.preventDefault()
- state.send("STOPPED_POINTING", inputs.pointerUp(e))
- rEdge.current.releasePointerCapture(e.pointerId)
- rEdge.current.replaceWith(rEdge.current)
- }}
- edge={edge}
- />
- )
- }
-
- function EdgeVertical({
- x,
- y,
- width,
- height,
- edge,
- }: {
- x: number
- y: number
- width: number
- height: number
- edge: "right_edge" | "left_edge"
- }) {
- const rEdge = useRef<SVGRectElement>(null)
-
- return (
- <StyledEdge
- ref={rEdge}
- x={x - width / 2}
- y={y}
- width={width}
- height={height}
- onPointerDown={(e) => {
- e.stopPropagation()
- state.send("POINTED_BOUNDS_EDGE", inputs.pointerDown(e, edge))
- rEdge.current.setPointerCapture(e.pointerId)
- }}
- onPointerUp={(e) => {
- e.stopPropagation()
- state.send("STOPPED_POINTING", inputs.pointerUp(e))
- rEdge.current.releasePointerCapture(e.pointerId)
- rEdge.current.replaceWith(rEdge.current)
- }}
- edge={edge}
- />
- )
- }
-
- function restoreCursor(e: PointerEvent) {
- state.send("STOPPED_POINTING", { id: "bounds", ...inputs.pointerUp(e) })
- document.body.style.cursor = "default"
- }
-
- const StyledEdge = styled("rect", {
- stroke: "none",
- fill: "none",
- variants: {
- edge: {
- bottom_edge: { cursor: "ns-resize" },
- right_edge: { cursor: "ew-resize" },
- top_edge: { cursor: "ns-resize" },
- left_edge: { cursor: "ew-resize" },
- },
- },
- })
-
- const StyledCorner = styled("rect", {
- stroke: "$bounds",
- fill: "#fff",
- zStrokeWidth: 2,
- variants: {
- corner: {
- top_left_corner: { cursor: "nwse-resize" },
- top_right_corner: { cursor: "nesw-resize" },
- bottom_right_corner: { cursor: "nwse-resize" },
- bottom_left_corner: { cursor: "nesw-resize" },
- },
- },
- })
-
- const StyledBounds = styled("rect", {
- fill: "none",
- stroke: "$bounds",
- zStrokeWidth: 2,
- })
-
- const StyledRotateCorner = styled("rect", {
- cursor: "grab",
- fill: "transparent",
- })
|