From abea8dcd150dfe2f25e3d9599325a26ac735c3e1 Mon Sep 17 00:00:00 2001 From: enes Date: Sat, 21 Dec 2024 17:15:59 +0100 Subject: [PATCH] refactor(create): optimize the events and fix responsiveness of the drawn fields with proper re-renders --- src/components/DrawPDFFields/index.tsx | 671 ++++++++++-------- .../DrawPDFFields/style.module.scss | 19 +- 2 files changed, 384 insertions(+), 306 deletions(-) diff --git a/src/components/DrawPDFFields/index.tsx b/src/components/DrawPDFFields/index.tsx index b9fd162..25e4b6a 100644 --- a/src/components/DrawPDFFields/index.tsx +++ b/src/components/DrawPDFFields/index.tsx @@ -8,19 +8,20 @@ import { Select } from '@mui/material' import styles from './style.module.scss' -import React, { useEffect, useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { ProfileMetadata, User, UserRole, KeyboardCode } from '../../types' -import { MouseState, PdfPage, DrawnField, DrawTool } from '../../types/drawing' +import { MouseState, DrawnField, DrawTool } from '../../types/drawing' import { hexToNpub, npubToHex, getProfileUsername } from '../../utils' import { SigitFile } from '../../utils/file' import { getToolboxLabelByMarkType } from '../../utils/mark' -import { FileDivider } from '../FileDivider' -import { ExtensionFileBox } from '../ExtensionFileBox' import { FONT_SIZE, FONT_TYPE, inPx } from '../../utils/pdf' import { useScale } from '../../hooks/useScale' import { AvatarIconButton } from '../UserAvatarIconButton' import { UserAvatar } from '../UserAvatar' -import _ from 'lodash' +import { Updater } from 'use-immer' +import { FileItem } from './internal/FileItem' +import { FileDivider } from '../FileDivider' +import { Counterpart } from './internal/Counterpart' const MINIMUM_RECT_SIZE = { width: 21, @@ -36,16 +37,25 @@ interface HideSignersForDrawnField { [key: number]: boolean } -interface Props { +type PageIndexer = [file: number, page: number] +type FieldIndexer = [...PageIndexer, field: number] + +interface DrawPdfFieldsProps { users: User[] metadata: { [key: string]: ProfileMetadata } sigitFiles: SigitFile[] - setSigitFiles: React.Dispatch> + updateSigitFiles: Updater selectedTool?: DrawTool } -export const DrawPDFFields = (props: Props) => { - const { selectedTool, sigitFiles, setSigitFiles, users } = props +export const DrawPDFFields = ({ + selectedTool, + metadata, + sigitFiles, + updateSigitFiles, + users +}: DrawPdfFieldsProps) => { + const { to, from } = useScale() const signers = users.filter((u) => u.role === UserRole.signer) const defaultSignerNpub = signers.length ? hexToNpub(signers[0].pubkey) : '' @@ -58,158 +68,124 @@ export const DrawPDFFields = (props: Props) => { * @param pubkeys * @returns available pubkey or empty string */ - const getAvailableSigner = (...pubkeys: string[]) => { - const availableSigner: string | undefined = pubkeys.find((pubkey) => - signers.some((s) => s.pubkey === npubToHex(pubkey)) - ) - return availableSigner || '' - } + const getAvailableSigner = useCallback( + (...pubkeys: string[]) => { + const availableSigner: string | undefined = pubkeys.find((pubkey) => + signers.some((s) => s.pubkey === npubToHex(pubkey)) + ) + return availableSigner || '' + }, + [signers] + ) - const { to, from } = useScale() - - const [mouseState, setMouseState] = useState({ - clicked: false - }) - - const [activeDrawnField, setActiveDrawnField] = useState<{ - fileIndex: number - pageIndex: number - drawnFieldIndex: number - }>() + const [mouseState, setMouseState] = useState({}) + const [indexer, setIndexer] = useState() + const [field, setField] = useState< + DrawnField & { + x: number + y: number + } + >() + const [lastIndexer, setLastIndexer] = useState() const isActiveDrawnField = ( fileIndex: number, pageIndex: number, drawnFieldIndex: number ) => - activeDrawnField?.fileIndex === fileIndex && - activeDrawnField?.pageIndex === pageIndex && - activeDrawnField?.drawnFieldIndex === drawnFieldIndex + lastIndexer && + lastIndexer[0] === fileIndex && + lastIndexer[1] === pageIndex && + lastIndexer[2] === drawnFieldIndex /** - * Drawing events + * Gets the pointer coordinates relative to a element in the `event` param + * @param event PointerEvent + * @param customTarget coordinates relative to this element, if not provided + * event.target will be used */ - useEffect(() => { - window.addEventListener('pointerup', handlePointerUp) - window.addEventListener('pointercancel', handlePointerUp) + const getPointerCoordinates = ( + event: React.PointerEvent, + customTarget?: HTMLElement | null + ) => { + const target = customTarget ? customTarget : event.currentTarget + const rect = target.getBoundingClientRect() - return () => { - window.removeEventListener('pointerup', handlePointerUp) - window.removeEventListener('pointercancel', handlePointerUp) + // Clamp X Y within the target + const x = Math.max(0, Math.min(event.clientX, rect.right) - rect.left) //x position within the element. + const y = Math.max(0, Math.min(event.clientY, rect.bottom) - rect.top) //y position within the element. + + return { + x, + y, + rect } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - const refreshPdfFiles = () => { - setSigitFiles([...sigitFiles]) } /** * Fired only on when left (primary pointer interaction) clicking page image - * Creates new drawnElement and pushes in the array - * It is re rendered and visible right away + * Creates new drawnElement * * @param event Pointer event - * @param page PdfPage where press happened + * @param pageIndexer File and page index + * @param pageWidth pdf value used to scale pointer coordinates */ - const handlePointerDown = ( - event: React.PointerEvent, - page: PdfPage, - fileIndex: number, - pageIndex: number - ) => { - // Proceed only if left click - if (event.button !== 0) return - - if (!selectedTool) { - return - } - - const { x, y } = getPointerCoordinates(event) - - const newField: DrawnField = { - left: to(page.width, x), - top: to(page.width, y), - width: event.pointerType === 'mouse' ? 0 : DEFAULT_START_SIZE.width, - height: event.pointerType === 'mouse' ? 0 : DEFAULT_START_SIZE.height, - counterpart: getAvailableSigner(lastSigner, defaultSignerNpub), - type: selectedTool.identifier - } - - page.drawnFields.push(newField) - - setActiveDrawnField({ - fileIndex, - pageIndex, - drawnFieldIndex: page.drawnFields.length - 1 - }) - setMouseState((prev) => { - return { - ...prev, - clicked: true - } - }) - } - - /** - * Drawing is finished, resets all the variables used to draw - * @param event Pointer event - */ - const handlePointerUp = () => { - sigitFiles.forEach((s) => { - s.pages?.forEach((p) => { - // Remove drawn fields below the MINIMUM_RECT_SIZE threshhold - p.drawnFields = p.drawnFields.filter( - (f) => - !( - f.width < MINIMUM_RECT_SIZE.width || - f.height < MINIMUM_RECT_SIZE.height - ) - ) - }) - }) - setMouseState((prev) => { - return { - ...prev, - clicked: false, - dragging: false, - resizing: false - } - }) - refreshPdfFiles() - } - - /** - * 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 pointer moves - * @param event Pointer event - * @param page PdfPage where moving is happening - */ - const handlePointerMove = (event: React.PointerEvent, page: PdfPage) => { - if (mouseState.clicked && selectedTool && event.pointerType === 'mouse') { - const lastElementIndex = page.drawnFields.length - 1 - - const lastDrawnField = page.drawnFields[lastElementIndex] - - // Return early if we don't have lastDrawnField - // Issue noticed in the console when dragging out of bounds - // to the page below (without releaseing mouse click) - if (!lastDrawnField) return - + const handlePointerDown = useCallback( + ( + event: React.PointerEvent, + pageIndexer: PageIndexer, + pageWidth: number + ) => { + // Proceed only if left click + if (event.button !== 0) return + if (!selectedTool) return + event.currentTarget.setPointerCapture(event.pointerId) + const counterpart = getAvailableSigner(lastSigner, defaultSignerNpub) const { x, y } = getPointerCoordinates(event) - const width = to(page.width, x) - lastDrawnField.left - const height = to(page.width, y) - lastDrawnField.top + setIndexer(pageIndexer) + setField({ + x: to(pageWidth, x), + y: to(pageWidth, y), + left: to(pageWidth, x), + top: to(pageWidth, y), + width: event.pointerType === 'mouse' ? 0 : DEFAULT_START_SIZE.width, + height: event.pointerType === 'mouse' ? 0 : DEFAULT_START_SIZE.height, + type: selectedTool.identifier, + counterpart + }) + setMouseState({ + clicked: true + }) + }, + [defaultSignerNpub, getAvailableSigner, lastSigner, selectedTool, to] + ) - lastDrawnField.width = width - lastDrawnField.height = height + /** + * After {@link handlePointerDown} creates an drawing element, this function + * alters the newly created drawing element, resizing it while pointer moves + * @param event Pointer event + * @param pageWidth pdf value used to scale pointer coordinates + */ + const handlePointerMove = useCallback( + (event: React.PointerEvent, pageWidth: number) => { + if (mouseState.clicked && selectedTool && event.pointerType === 'mouse') { + const { x, y } = getPointerCoordinates(event) + const pageX = to(pageWidth, x) + const pageY = to(pageWidth, y) - const currentDrawnFields = page.drawnFields + // Calculate left, top, width, and height based on direction + setField((prev) => { + const left = pageX < prev!.x ? pageX : prev!.x + const top = pageY < prev!.y ? pageY : prev!.y - currentDrawnFields[lastElementIndex] = lastDrawnField - - refreshPdfFiles() - } - } + const width = Math.abs(pageX - prev!.x) + const height = Math.abs(pageY - prev!.y) + return { ...prev!, left, top, width, height } + }) + } + }, + [mouseState.clicked, selectedTool, to] + ) /** * Fired when event happens on the drawn element which will be moved @@ -219,22 +195,30 @@ export const DrawPDFFields = (props: Props) => { * y - offsetY * * @param event Pointer event - * @param drawnFieldIndex Which we are moving + * @param fieldIndexer Which field we are moving */ const handleDrawnFieldPointerDown = ( event: React.PointerEvent, - fileIndex: number, - pageIndex: number, - drawnFieldIndex: number + fieldIndexer: FieldIndexer ) => { event.stopPropagation() // Proceed only if left click if (event.button !== 0) return + event.currentTarget.setPointerCapture(event.pointerId) const drawingRectangleCoords = getPointerCoordinates(event) + const [fileIndex, pageIndex, drawnFieldIndex] = fieldIndexer + const page = sigitFiles[fileIndex].pages![pageIndex] + const drawnField = page.drawnFields[drawnFieldIndex] - setActiveDrawnField({ fileIndex, pageIndex, drawnFieldIndex }) + setIndexer(fieldIndexer) + setField({ + ...drawnField, + x: to(page.width, drawingRectangleCoords.x), + y: to(page.width, drawingRectangleCoords.y) + }) + setLastIndexer(fieldIndexer) setMouseState({ dragging: true, clicked: false, @@ -244,7 +228,7 @@ export const DrawPDFFields = (props: Props) => { } }) - // make signers dropdown visible + // Make signers dropdown visible setHideSignersForDrawnField((prev) => ({ ...prev, [drawnFieldIndex]: false @@ -254,12 +238,10 @@ export const DrawPDFFields = (props: Props) => { /** * Moves the drawnElement by the pointer position (pointer can grab anywhere on the drawn element) * @param event Pointer event - * @param drawnField which we are moving - * @param pageWidth pdf value which is used to calculate scaled offset + * @param pageWidth pdf value used to scale pointer coordinates */ const handleDrawnFieldPointerMove = ( event: React.PointerEvent, - drawnField: DrawnField, pageWidth: number ) => { if (mouseState.dragging) { @@ -273,18 +255,21 @@ export const DrawPDFFields = (props: Props) => { let left = to(pageWidth, x - coordsOffset.x) let top = to(pageWidth, y - coordsOffset.y) - const rightLimit = to(pageWidth, rect.width) - drawnField.width - const bottomLimit = to(pageWidth, rect.height) - drawnField.height + setField((prev) => { + const rightLimit = to(pageWidth, rect.width) - prev!.width + const bottomLimit = to(pageWidth, rect.height) - prev!.height - if (left < 0) left = 0 - if (top < 0) top = 0 - if (left > rightLimit) left = rightLimit - if (top > bottomLimit) top = bottomLimit + if (left < 0) left = 0 + if (top < 0) top = 0 + if (left > rightLimit) left = rightLimit + if (top > bottomLimit) top = bottomLimit - drawnField.left = left - drawnField.top = top - - refreshPdfFiles() + return { + ...prev!, + left, + top + } + }) } } } @@ -292,73 +277,85 @@ export const DrawPDFFields = (props: Props) => { /** * Fired when clicked on the resize handle, sets the state for a resize action * @param event Pointer event - * @param drawnFieldIndex which we are resizing + * @param fieldIndexer which field we are resizing */ - const handleResizePointerDown = ( - event: React.PointerEvent, - fileIndex: number, - pageIndex: number, - drawnFieldIndex: number - ) => { - // Proceed only if left click - if (event.button !== 0) return - event.stopPropagation() + const handleResizePointerDown = useCallback( + (event: React.PointerEvent, fieldIndexer: FieldIndexer) => { + // Proceed only if left click + if (event.button !== 0) return + event.stopPropagation() - setActiveDrawnField({ fileIndex, pageIndex, drawnFieldIndex }) - setMouseState({ - resizing: true - }) - } + event.currentTarget.setPointerCapture(event.pointerId) + const [fileIndex, pageIndex, drawnFieldIndex] = fieldIndexer + const page = sigitFiles[fileIndex].pages![pageIndex] + const drawnField = page.drawnFields[drawnFieldIndex] + setIndexer(fieldIndexer) + setField({ + ...drawnField, + x: drawnField.left, + y: drawnField.top + }) + setLastIndexer(fieldIndexer) + setMouseState({ + resizing: true + }) + }, + [sigitFiles] + ) /** * Resizes the drawn element by the mouse position * @param event Pointer event - * @param drawnField which we are resizing - * @param pageWidth pdf value which is used to calculate scaled offset + * @param pageWidth pdf value used to scale pointer coordinates */ - const handleResizePointerMove = ( - event: React.PointerEvent, - drawnField: DrawnField, - pageWidth: number - ) => { - if (mouseState.resizing) { - const { x, y } = getPointerCoordinates( - event, + const handleResizePointerMove = useCallback( + (event: React.PointerEvent, pageWidth: number) => { + if (mouseState.resizing) { // currentTarget = span handle // 1st parent = drawnField // 2nd parent = img - event.currentTarget.parentElement?.parentElement - ) + const { x, y } = getPointerCoordinates( + event, + event.currentTarget.parentElement?.parentElement + ) - const width = to(pageWidth, x) - drawnField.left - const height = to(pageWidth, y) - drawnField.top + const pageX = to(pageWidth, x) + const pageY = to(pageWidth, y) - drawnField.width = width - drawnField.height = height + setField((prev) => { + const left = pageX < prev!.x ? pageX : prev!.x + const top = pageY < prev!.y ? pageY : prev!.y - refreshPdfFiles() - } - } + const width = Math.abs(pageX - prev!.x) + const height = Math.abs(pageY - prev!.y) + return { ...prev!, left, top, width, height } + }) + } + }, + [mouseState.resizing, to] + ) + + const handlePointerUpReleaseCapture = useCallback( + (event: React.PointerEvent) => { + event.currentTarget.releasePointerCapture(event.pointerId) + }, + [] + ) /** * Removes the drawn element using the indexes in the params * @param event Pointer event - * @param pdfFileIndex pdf file index - * @param pdfPageIndex pdf page index - * @param drawnFileIndex drawn file index + * @param fieldIIndexer [file index, page index, field index] */ const handleRemovePointerDown = ( event: React.PointerEvent, - pdfFileIndex: number, - pdfPageIndex: number, - drawnFileIndex: number + [fileIndex, pageIndex, fieldIndex]: FieldIndexer ) => { event.stopPropagation() - const pages = sigitFiles[pdfFileIndex]?.pages - if (pages) { - pages[pdfPageIndex]?.drawnFields?.splice(drawnFileIndex, 1) - } + updateSigitFiles((draft) => { + draft[fileIndex]?.pages![pageIndex]?.drawnFields?.splice(fieldIndex, 1) + }) } /** @@ -397,28 +394,72 @@ export const DrawPDFFields = (props: Props) => { } /** - * Gets the pointer coordinates relative to a element in the `event` param - * @param event PointerEvent - * @param customTarget coordinates relative to this element, if not provided - * event.target will be used + * Drawing is finished, resets all the variables used to draw */ - const getPointerCoordinates = ( - event: React.PointerEvent, - customTarget?: HTMLElement | null - ) => { - const target = customTarget ? customTarget : event.currentTarget - const rect = target.getBoundingClientRect() + const handlePointerUp = useCallback(() => { + // Proceed if we have selected something + if (indexer) { + // Check if we have the "preview" field state + if (field) { + // Cancel update if preview field is below the MINIMUM_RECT_SIZE threshhold + if ( + field.width < MINIMUM_RECT_SIZE.width || + field.height < MINIMUM_RECT_SIZE.height + ) { + setIndexer(undefined) + setMouseState({}) + return + } - // Clamp X Y within the target - const x = Math.min(event.clientX, rect.right) - rect.left //x position within the element. - const y = Math.min(event.clientY, rect.bottom) - rect.top //y position within the element. + const [fileIndex, pageIndex, fieldIndex] = indexer - return { - x, - y, - rect + // Add new drawn field to the files + if (mouseState.clicked) { + updateSigitFiles((draft) => { + draft[fileIndex].pages![pageIndex].drawnFields.push(field) + }) + } + + // Move + if (mouseState.dragging) { + updateSigitFiles((draft) => { + draft[fileIndex].pages![pageIndex].drawnFields[fieldIndex!] = field + }) + } + + // Resize + if (mouseState.resizing) { + updateSigitFiles((draft) => { + draft[fileIndex].pages![pageIndex].drawnFields[fieldIndex!] = field + }) + } + + // Clear indexer after applying the update + setIndexer(undefined) + } } - } + setMouseState({}) + }, [ + field, + indexer, + mouseState.clicked, + mouseState.dragging, + mouseState.resizing, + updateSigitFiles + ]) + + /** + * Drawing events + */ + useEffect(() => { + window.addEventListener('pointerup', handlePointerUp) + window.addEventListener('pointercancel', handlePointerUp) + + return () => { + window.removeEventListener('pointerup', handlePointerUp) + window.removeEventListener('pointercancel', handlePointerUp) + } + }, [handlePointerUp]) /** * Renders the pdf pages and drawing elements @@ -430,6 +471,13 @@ export const DrawPDFFields = (props: Props) => { return ( <> {file.pages?.map((page, pageIndex: number) => { + let isPageIndexerActive = false + if (indexer) { + const [fi, pi, di] = indexer + isPageIndexerActive = + fi === fileIndex && pi === pageIndex && typeof di === 'undefined' + } + return (
{ onKeyDown={(event) => handleEscapeButtonDown(event)} > { - handlePointerMove(event, page) - }} onPointerDown={(event) => { - handlePointerDown(event, page, fileIndex, pageIndex) + handlePointerDown(event, [fileIndex, pageIndex], page.width) }} + onPointerMove={(event) => { + handlePointerMove(event, page.width) + }} + onPointerUp={handlePointerUpReleaseCapture} draggable="false" src={page.image} alt={`page ${pageIndex + 1} of ${file.name}`} /> - + {isPageIndexerActive && field && ( +
+
+ {getToolboxLabelByMarkType(field.type) || 'placeholder'} +
+
+ )} {page.drawnFields.map((drawnField, drawnFieldIndex: number) => { + let isFieldIndexerActive = false + if (indexer) { + const [fi, pi, di] = indexer + isFieldIndexerActive = + fi === fileIndex && + pi === pageIndex && + di === drawnFieldIndex + } + return (
- handleDrawnFieldPointerDown( - event, + handleDrawnFieldPointerDown(event, [ fileIndex, pageIndex, drawnFieldIndex - ) + ]) } onPointerMove={(event) => { - handleDrawnFieldPointerMove(event, drawnField, page.width) + handleDrawnFieldPointerMove(event, page.width) }} + onPointerUp={handlePointerUpReleaseCapture} className={styles.drawingRectangle} style={{ backgroundColor: drawnField.counterpart @@ -472,12 +556,29 @@ export const DrawPDFFields = (props: Props) => { outlineColor: drawnField.counterpart ? `#${npubToHex(drawnField.counterpart)?.substring(0, 6)}` : undefined, - left: inPx(from(page.width, drawnField.left)), - top: inPx(from(page.width, drawnField.top)), - width: inPx(from(page.width, drawnField.width)), - height: inPx(from(page.width, drawnField.height)), + ...(isFieldIndexerActive && field + ? { + left: inPx(from(page.width, field.left)), + top: inPx(from(page.width, field.top)), + width: inPx(from(page.width, field.width)), + height: inPx(from(page.width, field.height)) + } + : { + left: inPx(from(page.width, drawnField.left)), + top: inPx(from(page.width, drawnField.top)), + width: inPx(from(page.width, drawnField.width)), + height: inPx(from(page.width, drawnField.height)) + }), + pointerEvents: mouseState.clicked ? 'none' : 'all', touchAction: 'none', + zIndex: isActiveDrawnField( + fileIndex, + pageIndex, + drawnFieldIndex + ) + ? 60 + : undefined, opacity: mouseState.dragging && isActiveDrawnField( @@ -501,37 +602,36 @@ export const DrawPDFFields = (props: Props) => {
- handleResizePointerDown( - event, + handleResizePointerDown(event, [ fileIndex, pageIndex, drawnFieldIndex - ) + ]) } onPointerMove={(event) => { - handleResizePointerMove(event, drawnField, page.width) + handleResizePointerMove(event, page.width) }} + onPointerUp={handlePointerUpReleaseCapture} className={styles.resizeHandle} style={{ - background: - mouseState.resizing && + ...(mouseState.resizing && isActiveDrawnField( fileIndex, pageIndex, drawnFieldIndex - ) - ? 'var(--primary-main)' - : undefined + ) && { + cursor: 'grabbing', + opacity: 0.1 + }) }} > { - handleRemovePointerDown( - event, + handleRemovePointerDown(event, [ fileIndex, pageIndex, drawnFieldIndex - ) + ]) }} className={styles.removeHandle} > @@ -569,23 +669,26 @@ export const DrawPDFFields = (props: Props) => { onChange={(event) => { drawnField.counterpart = event.target.value setLastSigner(event.target.value) - refreshPdfFiles() }} labelId="counterparts" label="Counterparts" sx={{ background: 'white' }} - renderValue={(value) => - renderCounterpartValue(value) - } + renderValue={(value) => ( + + )} > {signers.map((signer, index) => { const npub = hexToNpub(signer.pubkey) - const metadata = props.metadata[signer.pubkey] + const profileMetadata = metadata[signer.pubkey] const displayValue = getProfileUsername( npub, - metadata + profileMetadata ) // make current signers dropdown visible if ( @@ -604,7 +707,7 @@ export const DrawPDFFields = (props: Props) => { { ) } - const renderCounterpartValue = (npub: string) => { - let displayValue = _.truncate(npub, { length: 16 }) - - const signer = signers.find((u) => u.pubkey === npubToHex(npub)) - if (signer) { - const metadata = props.metadata[signer.pubkey] - displayValue = getProfileUsername(npub, metadata) - - return ( -
- img': { - width: '21px', - height: '21px' - } - }} - /> - {displayValue} -
- ) - } - - return displayValue - } - return (
- {sigitFiles.map((file, i) => { - return ( - -
- {file.isPdf && getPdfPages(file, i)} - {file.isImage && ( - {file.name} - )} - {!(file.isPdf || file.isImage) && ( - - )} -
- {i < sigitFiles.length - 1 && } -
- ) - })} + {sigitFiles.length > 0 && + sigitFiles + .map((file, i) => + file.isPdf ? ( + + {getPdfPages(file, i)} + + ) : ( + + ) + ) + .reduce((prev, curr, i) => [ + prev, + , + curr + ])}
) } diff --git a/src/components/DrawPDFFields/style.module.scss b/src/components/DrawPDFFields/style.module.scss index 7ae0de3..5eb5307 100644 --- a/src/components/DrawPDFFields/style.module.scss +++ b/src/components/DrawPDFFields/style.module.scss @@ -47,7 +47,7 @@ background-color: #fff; border: 1px solid rgb(160, 160, 160); border-radius: 50%; - cursor: nwse-resize; + cursor: grab; // Increase the area a bit so it's easier to click &::after { @@ -85,10 +85,6 @@ } } -.counterpartSelectValue { - display: flex; -} - .counterpartAvatar { img { width: 21px; @@ -107,3 +103,16 @@ outline: 1px dotted #01aaad; } } + +.drawingRectanglePreview { + position: absolute; + outline: 1px solid; + z-index: 50; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + pointer-events: none; + touch-action: none; + opacity: 0.8; +}