| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- import state, { useSelector } from "state"
- import { motion } from "framer-motion"
- import styled from "styles"
- import inputs from "state/inputs"
-
- export default function Bounds() {
- const bounds = useSelector((state) => state.values.selectedBounds)
- const isBrushing = useSelector((state) => state.isIn("brushSelecting"))
- const zoom = useSelector((state) => state.data.camera.zoom)
-
- 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}
- corner={0}
- width={cp}
- height={cp}
- cursor="nwse-resize"
- />
- <Corner
- x={maxX}
- y={minY}
- corner={1}
- width={cp}
- height={cp}
- cursor="nesw-resize"
- />
- <Corner
- x={maxX}
- y={maxY}
- corner={2}
- width={cp}
- height={cp}
- cursor="nwse-resize"
- />
- <Corner
- x={minX}
- y={maxY}
- corner={3}
- width={cp}
- height={cp}
- cursor="nesw-resize"
- />
- </>
- )}
- <EdgeHorizontal
- x={minX + p}
- y={minY}
- width={Math.max(0, width - p * 2)}
- height={p}
- onSelect={(e) => {
- e.stopPropagation()
- if (e.buttons !== 1) return
- state.send("POINTED_BOUNDS_EDGE", {
- edge: 0,
- ...inputs.pointerDown(e),
- })
- document.body.style.cursor = "ns-resize"
- }}
- />
- <EdgeVertical
- x={maxX}
- y={minY + p}
- width={p}
- height={Math.max(0, height - p * 2)}
- onSelect={(e) => {
- e.stopPropagation()
- if (e.buttons !== 1) return
- state.send("POINTED_BOUNDS_EDGE", {
- edge: 1,
- ...inputs.pointerDown(e),
- })
- document.body.style.cursor = "ew-resize"
- }}
- />
- <EdgeHorizontal
- x={minX + p}
- y={maxY}
- width={Math.max(0, width - p * 2)}
- height={p}
- onSelect={(e) => {
- e.stopPropagation()
- if (e.buttons !== 1) return
- state.send("POINTED_BOUNDS_EDGE", {
- edge: 2,
- ...inputs.pointerDown(e),
- })
- document.body.style.cursor = "ns-resize"
- }}
- />
- <EdgeVertical
- x={minX}
- y={minY + p}
- width={p}
- height={Math.max(0, height - p * 2)}
- onSelect={(e) => {
- e.stopPropagation()
- if (e.buttons !== 1) return
- state.send("POINTED_BOUNDS_EDGE", {
- edge: 3,
- ...inputs.pointerDown(e),
- })
- document.body.style.cursor = "ew-resize"
- }}
- />
- </g>
- )
- }
-
- function Corner({
- x,
- y,
- width,
- height,
- cursor,
- onHover,
- corner,
- }: {
- x: number
- y: number
- width: number
- height: number
- cursor: string
- corner: number
- onHover?: () => void
- }) {
- const isTop = corner === 0 || corner === 1
- const isLeft = corner === 0 || corner === 3
- return (
- <g>
- <motion.rect
- x={x + width * (isLeft ? -1.25 : -0.5)} // + width * 2 * transformOffset[0]}
- y={y + width * (isTop ? -1.25 : -0.5)} // + height * 2 * transformOffset[1]}
- width={width * 1.75}
- height={height * 1.75}
- onPanEnd={restoreCursor}
- onTap={restoreCursor}
- onPointerDown={(e) => {
- e.stopPropagation()
- if (e.buttons !== 1) return
- state.send("POINTED_ROTATE_CORNER", {
- corner,
- ...inputs.pointerDown(e),
- })
- document.body.style.cursor = "grabbing"
- }}
- style={{ cursor: "grab" }}
- fill="transparent"
- />
- <StyledCorner
- x={x + width * -0.5}
- y={y + height * -0.5}
- width={width}
- height={height}
- onPointerEnter={onHover}
- onPointerDown={(e) => {
- e.stopPropagation()
- if (e.buttons !== 1) return
- state.send("POINTED_BOUNDS_CORNER", {
- corner,
- ...inputs.pointerDown(e),
- })
- document.body.style.cursor = "nesw-resize"
- }}
- onPanEnd={restoreCursor}
- onTap={restoreCursor}
- style={{ cursor }}
- />
- </g>
- )
- }
-
- function EdgeHorizontal({
- x,
- y,
- width,
- height,
- onHover,
- onSelect,
- }: {
- x: number
- y: number
- width: number
- height: number
- onHover?: () => void
- onSelect?: (e: React.PointerEvent) => void
- }) {
- return (
- <StyledEdge
- x={x}
- y={y - height / 2}
- width={width}
- height={height}
- onPointerEnter={onHover}
- onPointerDown={onSelect}
- onPanEnd={restoreCursor}
- onTap={restoreCursor}
- style={{ cursor: "ns-resize" }}
- direction="horizontal"
- />
- )
- }
-
- function EdgeVertical({
- x,
- y,
- width,
- height,
- onHover,
- onSelect,
- }: {
- x: number
- y: number
- width: number
- height: number
- onHover?: () => void
- onSelect?: (e: React.PointerEvent) => void
- }) {
- return (
- <StyledEdge
- x={x - width / 2}
- y={y}
- width={width}
- height={height}
- onPointerEnter={onHover}
- onPointerDown={onSelect}
- onPanEnd={restoreCursor}
- onTap={restoreCursor}
- direction="vertical"
- />
- )
- }
-
- function restoreCursor(e: PointerEvent) {
- state.send("STOPPED_POINTING", { id: "bounds", ...inputs.pointerUp(e) })
- document.body.style.cursor = "default"
- }
-
- const StyledEdge = styled(motion.rect, {
- stroke: "none",
- fill: "none",
- variant: {
- direction: {
- horizontal: { cursor: "ns-resize" },
- vertical: { cursor: "ew-resize" },
- },
- },
- })
-
- const StyledCorner = styled(motion.rect, {
- stroke: "$bounds",
- fill: "#fff",
- zStrokeWidth: 2,
- })
-
- const StyledBounds = styled("rect", {
- fill: "none",
- stroke: "$bounds",
- zStrokeWidth: 2,
- })
|