refactor(mobile): drawing mouse to pointer events, field colors and actions feedback, touch action
This commit is contained in:
parent
757012399a
commit
b20ffe6e87
@ -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">
|
||||||
|
Loading…
Reference in New Issue
Block a user