refactor(mobile): drawing mouse to pointer events, field colors and actions feedback, touch action

This commit is contained in:
enes 2024-09-03 10:14:43 +02:00
parent 757012399a
commit b20ffe6e87

View File

@ -9,7 +9,6 @@ import {
} from '@mui/material' } from '@mui/material'
import styles from './style.module.scss' import styles from './style.module.scss'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import * as PDFJS from 'pdfjs-dist'
import { ProfileMetadata, User, UserRole } from '../../types' import { ProfileMetadata, User, UserRole } from '../../types'
import { MouseState, PdfPage, DrawnField, DrawTool } from '../../types/drawing' import { MouseState, PdfPage, DrawnField, DrawTool } from '../../types/drawing'
import { truncate } from 'lodash' import { truncate } from 'lodash'
@ -22,10 +21,10 @@ import { useScale } from '../../hooks/useScale'
import { AvatarIconButton } from '../UserAvatarIconButton' import { AvatarIconButton } from '../UserAvatarIconButton'
import { LoadingSpinner } from '../LoadingSpinner' import { LoadingSpinner } from '../LoadingSpinner'
PDFJS.GlobalWorkerOptions.workerSrc = new URL( const DEFAULT_START_SIZE = {
'pdfjs-dist/build/pdf.worker.min.mjs', width: 140,
import.meta.url height: 40
).toString() } as const
interface Props { interface Props {
selectedFiles: File[] selectedFiles: File[]
@ -46,6 +45,8 @@ export const DrawPDFFields = (props: Props) => {
clicked: false clicked: false
}) })
const [activeDrawField, setActiveDrawField] = useState<number>()
useEffect(() => { useEffect(() => {
if (selectedFiles) { if (selectedFiles) {
/** /**
@ -77,10 +78,12 @@ export const DrawPDFFields = (props: Props) => {
* Drawing events * Drawing events
*/ */
useEffect(() => { useEffect(() => {
window.addEventListener('mouseup', onMouseUp) window.addEventListener('pointerup', handlePointerUp)
window.addEventListener('pointercancel', handlePointerUp)
return () => { return () => {
window.removeEventListener('mouseup', onMouseUp) window.removeEventListener('pointerup', handlePointerUp)
window.removeEventListener('pointercancel', handlePointerUp)
} }
}, []) }, [])
@ -96,10 +99,7 @@ export const DrawPDFFields = (props: Props) => {
* @param event Mouse event * @param event Mouse event
* @param page PdfPage where press happened * @param page PdfPage where press happened
*/ */
const onMouseDown = ( const handlePointerDown = (event: React.PointerEvent, page: PdfPage) => {
event: React.MouseEvent<HTMLDivElement>,
page: PdfPage
) => {
// Proceed only if left click // Proceed only if left click
if (event.button !== 0) return if (event.button !== 0) return
@ -112,8 +112,8 @@ export const DrawPDFFields = (props: Props) => {
const newField: DrawnField = { const newField: DrawnField = {
left: to(page.width, mouseX), left: to(page.width, mouseX),
top: to(page.width, mouseY), top: to(page.width, mouseY),
width: 0, width: event.pointerType === 'mouse' ? 0 : DEFAULT_START_SIZE.width,
height: 0, height: event.pointerType === 'mouse' ? 0 : DEFAULT_START_SIZE.height,
counterpart: '', counterpart: '',
type: selectedTool.identifier type: selectedTool.identifier
} }
@ -132,7 +132,7 @@ export const DrawPDFFields = (props: Props) => {
* Drawing is finished, resets all the variables used to draw * Drawing is finished, resets all the variables used to draw
* @param event Mouse event * @param event Mouse event
*/ */
const onMouseUp = () => { const handlePointerUp = () => {
setMouseState((prev) => { setMouseState((prev) => {
return { return {
...prev, ...prev,
@ -144,16 +144,13 @@ export const DrawPDFFields = (props: Props) => {
} }
/** /**
* After {@link onMouseDown} create an drawing element, this function gets called on every pixel moved * After {@link handlePointerDown} create an drawing element, this function gets called on every pixel moved
* which alters the newly created drawing element, resizing it while mouse move * which alters the newly created drawing element, resizing it while mouse move
* @param event Mouse event * @param event Mouse event
* @param page PdfPage where moving is happening * @param page PdfPage where moving is happening
*/ */
const onMouseMove = ( const handlePointerMove = (event: React.PointerEvent, page: PdfPage) => {
event: React.MouseEvent<HTMLDivElement>, if (mouseState.clicked && selectedTool && event.pointerType === 'mouse') {
page: PdfPage
) => {
if (mouseState.clicked && selectedTool) {
const lastElementIndex = page.drawnFields.length - 1 const lastElementIndex = page.drawnFields.length - 1
const lastDrawnField = page.drawnFields[lastElementIndex] const lastDrawnField = page.drawnFields[lastElementIndex]
@ -187,9 +184,12 @@ export const DrawPDFFields = (props: Props) => {
* mouseY - offsetY * mouseY - offsetY
* *
* @param event Mouse event * @param event Mouse event
* @param drawnField Which we are moving * @param drawnFieldIndex Which we are moving
*/ */
const onDrawnFieldMouseDown = (event: React.MouseEvent<HTMLDivElement>) => { const handleDrawnFieldPointerDown = (
event: React.PointerEvent,
drawnFieldIndex: number
) => {
event.stopPropagation() event.stopPropagation()
// Proceed only if left click // Proceed only if left click
@ -197,6 +197,7 @@ export const DrawPDFFields = (props: Props) => {
const drawingRectangleCoords = getMouseCoordinates(event) const drawingRectangleCoords = getMouseCoordinates(event)
setActiveDrawField(drawnFieldIndex)
setMouseState({ setMouseState({
dragging: true, dragging: true,
clicked: false, clicked: false,
@ -212,8 +213,8 @@ export const DrawPDFFields = (props: Props) => {
* @param event Mouse event * @param event Mouse event
* @param drawnField which we are moving * @param drawnField which we are moving
*/ */
const onDrawnFieldMouseMove = ( const handleDrawnFieldPointerMove = (
event: React.MouseEvent<HTMLDivElement>, event: React.PointerEvent,
drawnField: DrawnField, drawnField: DrawnField,
pageWidth: number pageWidth: number
) => { ) => {
@ -228,8 +229,8 @@ export const DrawPDFFields = (props: Props) => {
let left = to(pageWidth, mouseX - coordsOffset.mouseX) let left = to(pageWidth, mouseX - coordsOffset.mouseX)
let top = to(pageWidth, mouseY - coordsOffset.mouseY) let top = to(pageWidth, mouseY - coordsOffset.mouseY)
const rightLimit = to(pageWidth, rect.width) - drawnField.width - 3 const rightLimit = to(pageWidth, rect.width) - drawnField.width
const bottomLimit = to(pageWidth, rect.height) - drawnField.height - 3 const bottomLimit = to(pageWidth, rect.height) - drawnField.height
if (left < 0) left = 0 if (left < 0) left = 0
if (top < 0) top = 0 if (top < 0) top = 0
@ -247,16 +248,17 @@ export const DrawPDFFields = (props: Props) => {
/** /**
* Fired when clicked on the resize handle, sets the state for a resize action * Fired when clicked on the resize handle, sets the state for a resize action
* @param event Mouse event * @param event Mouse event
* @param drawnField which we are resizing * @param drawnFieldIndex which we are resizing
*/ */
const onResizeHandleMouseDown = ( const handleResizePointerDown = (
event: React.MouseEvent<HTMLSpanElement> event: React.PointerEvent,
drawnFieldIndex: number
) => { ) => {
// Proceed only if left click // Proceed only if left click
if (event.button !== 0) return if (event.button !== 0) return
event.stopPropagation() event.stopPropagation()
setActiveDrawField(drawnFieldIndex)
setMouseState({ setMouseState({
resizing: true resizing: true
}) })
@ -267,8 +269,8 @@ export const DrawPDFFields = (props: Props) => {
* @param event Mouse event * @param event Mouse event
* @param drawnField which we are resizing * @param drawnField which we are resizing
*/ */
const onResizeHandleMouseMove = ( const handleResizePointerMove = (
event: React.MouseEvent<HTMLSpanElement>, event: React.PointerEvent,
drawnField: DrawnField, drawnField: DrawnField,
pageWidth: number pageWidth: number
) => { ) => {
@ -298,8 +300,8 @@ export const DrawPDFFields = (props: Props) => {
* @param pdfPageIndex pdf page index * @param pdfPageIndex pdf page index
* @param drawnFileIndex drawn file index * @param drawnFileIndex drawn file index
*/ */
const onRemoveHandleMouseDown = ( const handleRemovePointerDown = (
event: React.MouseEvent<HTMLSpanElement>, event: React.PointerEvent,
pdfFileIndex: number, pdfFileIndex: number,
pdfPageIndex: number, pdfPageIndex: number,
drawnFileIndex: number drawnFileIndex: number
@ -317,20 +319,18 @@ export const DrawPDFFields = (props: Props) => {
* so select can work properly * so select can work properly
* @param event Mouse event * @param event Mouse event
*/ */
const onUserSelectHandleMouseDown = ( const handleUserSelectPointerDown = (event: React.PointerEvent) => {
event: React.MouseEvent<HTMLDivElement>
) => {
event.stopPropagation() event.stopPropagation()
} }
/** /**
* Gets the mouse coordinates relative to a element in the `event` param * Gets the mouse coordinates relative to a element in the `event` param
* @param event MouseEvent * @param event PointerEvent
* @param customTarget mouse coordinates relative to this element, if not provided * @param customTarget mouse coordinates relative to this element, if not provided
* event.target will be used * event.target will be used
*/ */
const getMouseCoordinates = ( const getMouseCoordinates = (
event: React.MouseEvent<HTMLElement>, event: React.PointerEvent,
customTarget?: HTMLElement | null customTarget?: HTMLElement | null
) => { ) => {
const target = customTarget ? customTarget : event.currentTarget const target = customTarget ? customTarget : event.currentTarget
@ -346,7 +346,6 @@ export const DrawPDFFields = (props: Props) => {
rect rect
} }
} }
/** /**
* Renders the pdf pages and drawing elements * Renders the pdf pages and drawing elements
*/ */
@ -363,11 +362,11 @@ export const DrawPDFFields = (props: Props) => {
className={`image-wrapper ${styles.pdfImageWrapper} ${selectedTool ? styles.drawing : ''}`} className={`image-wrapper ${styles.pdfImageWrapper} ${selectedTool ? styles.drawing : ''}`}
> >
<img <img
onMouseMove={(event) => { onPointerMove={(event) => {
onMouseMove(event, page) handlePointerMove(event, page)
}} }}
onMouseDown={(event) => { onPointerDown={(event) => {
onMouseDown(event, page) handlePointerDown(event, page)
}} }}
draggable="false" draggable="false"
src={page.image} src={page.image}
@ -378,29 +377,52 @@ export const DrawPDFFields = (props: Props) => {
return ( return (
<div <div
key={drawnFieldIndex} key={drawnFieldIndex}
onMouseDown={onDrawnFieldMouseDown} onPointerDown={(event) =>
onMouseMove={(event) => { handleDrawnFieldPointerDown(event, drawnFieldIndex)
onDrawnFieldMouseMove(event, drawnField, page.width) }
onPointerMove={(event) => {
handleDrawnFieldPointerMove(event, drawnField, page.width)
}} }}
className={styles.drawingRectangle} className={styles.drawingRectangle}
style={{ style={{
backgroundColor: drawnField.counterpart
? `#${npubToHex(drawnField.counterpart)?.substring(0, 6)}4b`
: undefined,
borderColor: drawnField.counterpart
? `#${npubToHex(drawnField.counterpart)?.substring(0, 6)}`
: undefined,
left: inPx(from(page.width, drawnField.left)), left: inPx(from(page.width, drawnField.left)),
top: inPx(from(page.width, drawnField.top)), top: inPx(from(page.width, drawnField.top)),
width: inPx(from(page.width, drawnField.width)), width: inPx(from(page.width, drawnField.width)),
height: inPx(from(page.width, drawnField.height)), height: inPx(from(page.width, drawnField.height)),
pointerEvents: mouseState.clicked ? 'none' : 'all' pointerEvents: mouseState.clicked ? 'none' : 'all',
touchAction: 'none',
opacity:
mouseState.dragging &&
activeDrawField === drawnFieldIndex
? 0.8
: undefined
}} }}
> >
<span <span
onMouseDown={onResizeHandleMouseDown} onPointerDown={(event) =>
onMouseMove={(event) => { handleResizePointerDown(event, drawnFieldIndex)
onResizeHandleMouseMove(event, drawnField, page.width) }
onPointerMove={(event) => {
handleResizePointerMove(event, drawnField, page.width)
}} }}
className={styles.resizeHandle} className={styles.resizeHandle}
style={{
background:
mouseState.resizing &&
activeDrawField === drawnFieldIndex
? 'var(--primary-main)'
: undefined
}}
></span> ></span>
<span <span
onMouseDown={(event) => { onPointerDown={(event) => {
onRemoveHandleMouseDown( handleRemovePointerDown(
event, event,
fileIndex, fileIndex,
pageIndex, pageIndex,
@ -412,7 +434,7 @@ export const DrawPDFFields = (props: Props) => {
<Close fontSize="small" /> <Close fontSize="small" />
</span> </span>
<div <div
onMouseDown={onUserSelectHandleMouseDown} onPointerDown={handleUserSelectPointerDown}
className={styles.userSelect} className={styles.userSelect}
> >
<FormControl fullWidth size="small"> <FormControl fullWidth size="small">