feat: moved to shared-components and implemented focal point picker in RTE

This commit is contained in:
Erik Tiekstra
2024-10-15 11:15:58 +02:00
parent b1493bcd3d
commit 4c1ee66542
15 changed files with 246 additions and 27 deletions

View File

@@ -0,0 +1,47 @@
.container {
display: grid;
gap: 1rem;
border-right: 1px solid #eee;
padding-right: 1rem;
}
.focalPointWrapper {
position: relative;
user-select: none;
justify-self: center;
}
.focalPointButton {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 0, 0, 0.4);
border: 3px solid red;
display: block;
width: 25px;
height: 25px;
border-radius: 50%;
}
.focalPointImage {
height: 100%;
max-height: 350px;
}
.examples {
width: 100%;
max-width: 600px;
max-height: 400px;
display: grid;
grid-template-columns: 3fr 1fr;
grid-template-rows: 2fr 1fr;
gap: 0.25rem;
}
.examples img {
width: 100%;
height: 100%;
object-fit: cover;
overflow: hidden;
}

View File

@@ -0,0 +1,53 @@
import React from "react"
import useFocalPoint from "./useFocalPoint"
import type { FocalPoint } from "~/types/imagevault"
import "./focalPointPicker.css"
export interface FocalPointPickerProps {
focalPoint?: FocalPoint
imageSrc: string
onChange: (focalPoint: FocalPoint) => void
}
export default function FocalPointPicker({
focalPoint,
imageSrc,
onChange,
}: FocalPointPickerProps) {
const { ref, x, y, onMove, canMove, setCanMove } = useFocalPoint({
focalPoint,
onChange,
})
const imagesArray = Array.from({ length: 4 })
return (
<div className="container">
<div className="focalPointWrapper" ref={ref} onMouseMove={onMove}>
<button
className="focalPointButton"
style={{
left: `${x}%`,
top: `${y}%`,
cursor: canMove ? "grabbing" : "grab",
}}
onMouseDown={() => setCanMove(true)}
onMouseUp={() => setCanMove(false)}
></button>
<img className="focalPointImage" src={imageSrc} alt="" />
</div>
<div className="examples">
{imagesArray.map((_, idx) => (
<img
key={idx}
src={imageSrc}
alt=""
style={{ objectPosition: `${x}% ${y}%` }}
/>
))}
</div>
</div>
)
}

View File

@@ -0,0 +1,59 @@
import { useCallback, useRef, useState, MouseEvent, useEffect } from "react"
import { FocalPoint } from "~/types/imagevault"
interface UseFocalPointProps {
focalPoint?: FocalPoint
onChange: (focalPoint: FocalPoint) => void
}
const DEFAULT_PERCENTAGE = 50
export default function useFocalPoint({
focalPoint,
onChange,
}: UseFocalPointProps) {
const ref = useRef<HTMLDivElement>(null)
const [x, setX] = useState<number>(DEFAULT_PERCENTAGE)
const [y, setY] = useState<number>(DEFAULT_PERCENTAGE)
const [canMove, setCanMove] = useState(false)
useEffect(() => {
if (focalPoint) {
setX(focalPoint.x)
setY(focalPoint.y)
}
}, [focalPoint])
const onMove = useCallback(
(e: MouseEvent) => {
if (canMove) {
const containerBoundingRectangle = ref.current!.getBoundingClientRect()
const xPixels = e.clientX - containerBoundingRectangle.left
const yPixels = e.clientY - containerBoundingRectangle.top
let x = Math.min(
Math.max((xPixels * 100) / ref.current!.clientWidth, 0),
100
)
let y = Math.min(
Math.max((yPixels * 100) / ref.current!.clientHeight, 0),
100
)
x = parseFloat(x.toFixed(2))
y = parseFloat(y.toFixed(2))
setX(x)
setY(y)
onChange({ x, y })
}
},
[canMove, onChange]
)
return {
ref,
x,
y,
onMove,
canMove,
setCanMove,
}
}