feat: area hover text preview
This commit is contained in:
parent
2963dac8a6
commit
1f8ffc01b1
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.css": "tailwindcss"
|
"*.css": "tailwindcss"
|
||||||
}
|
},
|
||||||
|
"cSpell.words": [
|
||||||
|
"heroicons"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@ -7,19 +7,24 @@ import loadImage from '../../useCases/loadImage'
|
|||||||
import processImageArea from '../../useCases/processImageArea'
|
import processImageArea from '../../useCases/processImageArea'
|
||||||
import classNames from '../../utils/classNames'
|
import classNames from '../../utils/classNames'
|
||||||
import LanguageSelect from './LanguageSelect'
|
import LanguageSelect from './LanguageSelect'
|
||||||
|
import isInBounds from '../../utils/isInBounds'
|
||||||
|
import { ipc } from '../../wailsjs/wailsjs/go/models'
|
||||||
|
|
||||||
const zoomStep = 0.025
|
const zoomStep = 0.025
|
||||||
const maxZoomLevel = 4
|
const maxZoomLevel = 4
|
||||||
|
|
||||||
const DocumentRenderer = () => {
|
const DocumentRenderer = () => {
|
||||||
const { getSelectedDocument, requestAddArea, selectedAreaId, setSelectedAreaId } = useProject()
|
const { getSelectedDocument, requestAddArea, selectedAreaId, setSelectedAreaId, getProcessedAreasByDocumentId } = useProject()
|
||||||
const selectedDocument = getSelectedDocument()
|
const selectedDocument = getSelectedDocument()
|
||||||
const areas = selectedDocument?.areas
|
const areas = selectedDocument?.areas
|
||||||
const documentCanvas = useRef<HTMLCanvasElement>(null)
|
const documentCanvas = useRef<HTMLCanvasElement>(null)
|
||||||
const areaCanvas = useRef<HTMLCanvasElement>(null)
|
const areaCanvas = useRef<HTMLCanvasElement>(null)
|
||||||
|
const uiCanvas = useRef<HTMLCanvasElement>(null)
|
||||||
const drawingCanvas = useRef<HTMLCanvasElement>(null)
|
const drawingCanvas = useRef<HTMLCanvasElement>(null)
|
||||||
|
|
||||||
const [zoomLevel, setZoomLevel] = useState(1)
|
const [zoomLevel, setZoomLevel] = useState(1)
|
||||||
|
const [hoverOverAreaId, setHoverOverAreaId] = useState('')
|
||||||
|
const [hoveredProcessedArea, setHoveredProcessedArea] = useState<ipc.ProcessedArea | undefined>()
|
||||||
|
|
||||||
let downClickX = 0
|
let downClickX = 0
|
||||||
let downClickY = 0
|
let downClickY = 0
|
||||||
@ -36,6 +41,11 @@ const DocumentRenderer = () => {
|
|||||||
areaCanvasInstance.width = size.width
|
areaCanvasInstance.width = size.width
|
||||||
areaCanvasInstance.height = size.height
|
areaCanvasInstance.height = size.height
|
||||||
|
|
||||||
|
const uiCanvasInstance = uiCanvas.current
|
||||||
|
if (!uiCanvasInstance) return
|
||||||
|
uiCanvasInstance.width = size.width
|
||||||
|
uiCanvasInstance.height = size.height
|
||||||
|
|
||||||
const drawingCanvasInstance = drawingCanvas.current
|
const drawingCanvasInstance = drawingCanvas.current
|
||||||
if (!drawingCanvasInstance) return
|
if (!drawingCanvasInstance) return
|
||||||
drawingCanvasInstance.width = size.width
|
drawingCanvasInstance.width = size.width
|
||||||
@ -63,6 +73,7 @@ const DocumentRenderer = () => {
|
|||||||
context.drawImage(image, 0, 0, width, height)
|
context.drawImage(image, 0, 0, width, height)
|
||||||
|
|
||||||
if (areas) applyAreasToCanvas()
|
if (areas) applyAreasToCanvas()
|
||||||
|
applyUiCanvasUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
const applyAreasToCanvas = () => {
|
const applyAreasToCanvas = () => {
|
||||||
@ -96,6 +107,59 @@ const DocumentRenderer = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const applyUiCanvasUpdates = () => {
|
||||||
|
const uiCanvasInstance = uiCanvas.current
|
||||||
|
if (!uiCanvasInstance) return
|
||||||
|
const context = uiCanvasInstance.getContext('2d')!
|
||||||
|
if (!context) return
|
||||||
|
|
||||||
|
context.clearRect(0, 0, uiCanvasInstance.width, uiCanvasInstance.height)
|
||||||
|
|
||||||
|
if (!areas || !areas.length) return
|
||||||
|
|
||||||
|
const hoverArea = areas.find(a => a.id === hoverOverAreaId)
|
||||||
|
if (!hoverArea) return
|
||||||
|
|
||||||
|
context.beginPath()
|
||||||
|
context.setLineDash([])
|
||||||
|
context.lineWidth = 6
|
||||||
|
context.strokeStyle = '#dc8dec'
|
||||||
|
const width = (hoverArea.endX - hoverArea.startX) * zoomLevel
|
||||||
|
const height = (hoverArea.endY - hoverArea.startY) * zoomLevel
|
||||||
|
const x = hoverArea.startX * zoomLevel
|
||||||
|
const y = hoverArea.startY * zoomLevel
|
||||||
|
context.roundRect(x, y, width, height, 4)
|
||||||
|
context.stroke()
|
||||||
|
context.closePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProcessedAreaById = async (areaId: string) => {
|
||||||
|
if (!selectedDocument || !selectedDocument.id || !areaId) return
|
||||||
|
const processedAreas = await getProcessedAreasByDocumentId(selectedDocument.id)
|
||||||
|
const foundProcessedArea = processedAreas.find(a => a.id === areaId)
|
||||||
|
return foundProcessedArea
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleHoverOverArea = (e: React.MouseEvent) => {
|
||||||
|
const domRect = e.currentTarget.getBoundingClientRect()
|
||||||
|
const x = e.clientX - domRect.left
|
||||||
|
const y = e.clientY - domRect.top
|
||||||
|
const point = { x, y }
|
||||||
|
|
||||||
|
const areaContainingCoords = areas?.find(a => {
|
||||||
|
const bounds = { startX: a.startX, startY: a.startY, endX: a.endX, endY: a.endY }
|
||||||
|
return isInBounds(point, bounds, zoomLevel)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (areaContainingCoords?.id === hoverOverAreaId) return
|
||||||
|
|
||||||
|
setHoverOverAreaId(areaContainingCoords?.id || '')
|
||||||
|
getProcessedAreaById(areaContainingCoords?.id || '').then(response => {
|
||||||
|
console.log(response)
|
||||||
|
setHoveredProcessedArea(response)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const handleMouseDown = (e: React.MouseEvent) => {
|
const handleMouseDown = (e: React.MouseEvent) => {
|
||||||
const drawingCanvasInstance = drawingCanvas.current
|
const drawingCanvasInstance = drawingCanvas.current
|
||||||
if (!drawingCanvasInstance) return
|
if (!drawingCanvasInstance) return
|
||||||
@ -144,13 +208,13 @@ const DocumentRenderer = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseMove = (e: React.MouseEvent) => {
|
const handleMouseMove = (e: React.MouseEvent) => {
|
||||||
const drawingCanvasInstance = drawingCanvas.current
|
|
||||||
if (!drawingCanvasInstance) return
|
|
||||||
|
|
||||||
let mouseX = e.nativeEvent.offsetX
|
let mouseX = e.nativeEvent.offsetX
|
||||||
let mouseY = e.nativeEvent.offsetY
|
let mouseY = e.nativeEvent.offsetY
|
||||||
|
|
||||||
if (isMouseDown) {
|
if (!isMouseDown) handleHoverOverArea(e)
|
||||||
|
else {
|
||||||
|
const drawingCanvasInstance = drawingCanvas.current
|
||||||
|
if (!drawingCanvasInstance) return
|
||||||
const context = drawingCanvasInstance.getContext('2d')
|
const context = drawingCanvasInstance.getContext('2d')
|
||||||
if (!context) return
|
if (!context) return
|
||||||
|
|
||||||
@ -177,6 +241,39 @@ const DocumentRenderer = () => {
|
|||||||
if (selectedDocument?.path) applyDocumentToCanvas(selectedDocument.path)
|
if (selectedDocument?.path) applyDocumentToCanvas(selectedDocument.path)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
applyUiCanvasUpdates()
|
||||||
|
}, [hoverOverAreaId])
|
||||||
|
|
||||||
|
const renderAreaPreview = () => {
|
||||||
|
if (!areas || !areas.length || !hoveredProcessedArea) return <></>
|
||||||
|
|
||||||
|
const hoverArea = areas.find(a => a.id === hoverOverAreaId)
|
||||||
|
if (!hoverArea) return <></>
|
||||||
|
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
{
|
||||||
|
hoveredProcessedArea.lines?.map(l => l.words).flat().map((w, i) => {
|
||||||
|
const width = Math.floor((w.boundingBox.x1 - w.boundingBox.x0) * zoomLevel) + 2
|
||||||
|
const height = Math.floor((w.boundingBox.y1 - w.boundingBox.y0) * zoomLevel) + 2
|
||||||
|
return <span
|
||||||
|
key={i}
|
||||||
|
dir={w.direction === 'RIGHT_TO_LEFT' ? 'rtl' : 'ltr'}
|
||||||
|
className='absolute text-center inline-block p-1 bg-opacity-60 bg-black text-white rounded-md shadow-zinc-900 shadow-2xl'
|
||||||
|
style={{
|
||||||
|
fontSize: `${3.4 * zoomLevel}vmin`,
|
||||||
|
width,
|
||||||
|
top: Math.floor(w.boundingBox.y0 * zoomLevel) + height,
|
||||||
|
left: Math.floor(w.boundingBox.x0 * zoomLevel)
|
||||||
|
}}>
|
||||||
|
{w.fullText}
|
||||||
|
</span>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
return <div className='relative'>
|
return <div className='relative'>
|
||||||
<div className='flex justify-between align-top mb-2'>
|
<div className='flex justify-between align-top mb-2'>
|
||||||
<div className='flex align-top'>
|
<div className='flex align-top'>
|
||||||
@ -196,16 +293,20 @@ const DocumentRenderer = () => {
|
|||||||
<div
|
<div
|
||||||
onWheelCapture={handleWheelEvent}
|
onWheelCapture={handleWheelEvent}
|
||||||
className={classNames('relative mt-2 overflow-scroll',
|
className={classNames('relative mt-2 overflow-scroll',
|
||||||
'w-[calc(100vw-320px)] h-[calc(100vh-240px)] border-4',
|
'w-[calc(100vw-320px)] h-[calc(100vh-174px)] border-4',
|
||||||
'border-dashed border-gray-200')}>
|
'border-dashed border-gray-200')}>
|
||||||
<canvas
|
<canvas
|
||||||
className="absolute"
|
className="absolute"
|
||||||
ref={documentCanvas}
|
ref={documentCanvas}
|
||||||
/>
|
/>
|
||||||
<canvas
|
<canvas
|
||||||
className="absolute "
|
className="absolute"
|
||||||
ref={areaCanvas}
|
ref={areaCanvas}
|
||||||
/>
|
/>
|
||||||
|
<canvas
|
||||||
|
className="absolute"
|
||||||
|
ref={uiCanvas}
|
||||||
|
/>
|
||||||
<canvas
|
<canvas
|
||||||
className="absolute"
|
className="absolute"
|
||||||
ref={drawingCanvas}
|
ref={drawingCanvas}
|
||||||
@ -213,6 +314,7 @@ const DocumentRenderer = () => {
|
|||||||
onMouseUp={handleMouseUp}
|
onMouseUp={handleMouseUp}
|
||||||
onMouseMove={handleMouseMove}
|
onMouseMove={handleMouseMove}
|
||||||
/>
|
/>
|
||||||
|
{renderAreaPreview()}
|
||||||
</div>
|
</div>
|
||||||
</div >
|
</div >
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useRef, useState } from 'react'
|
import React, { useRef } from 'react'
|
||||||
import { useProject } from '../../../context/Project/provider'
|
import { useProject } from '../../../context/Project/provider'
|
||||||
import classNames from '../../../utils/classNames'
|
import classNames from '../../../utils/classNames'
|
||||||
import { ArrowPathIcon, XMarkIcon } from '@heroicons/react/24/outline'
|
import { ArrowPathIcon, XMarkIcon } from '@heroicons/react/24/outline'
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import React, { useRef } from 'react'
|
import React, { useRef } from 'react'
|
||||||
import { useProject } from '../../../context/Project/provider'
|
import { useProject } from '../../../context/Project/provider'
|
||||||
|
import { XMarkIcon } from '@heroicons/react/24/outline'
|
||||||
import classNames from '../../../utils/classNames'
|
import classNames from '../../../utils/classNames'
|
||||||
import onEnterHandler from '../../../utils/onEnterHandler'
|
import onEnterHandler from '../../../utils/onEnterHandler'
|
||||||
import AreaLineItem from './AreaLineItem'
|
import AreaLineItem from './AreaLineItem'
|
||||||
@ -11,7 +12,8 @@ import { SidebarDocument } from './types'
|
|||||||
const DocumentLineItem = (props: { document: SidebarDocument, groupId: string, index: number }) => {
|
const DocumentLineItem = (props: { document: SidebarDocument, groupId: string, index: number }) => {
|
||||||
const {
|
const {
|
||||||
getSelectedDocument,
|
getSelectedDocument,
|
||||||
requestUpdateDocument
|
requestUpdateDocument,
|
||||||
|
requestDeleteDocumentById,
|
||||||
} = useProject()
|
} = useProject()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -54,57 +56,17 @@ const DocumentLineItem = (props: { document: SidebarDocument, groupId: string, i
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<li className='p-0 m-0' key={props.document.id}>
|
<li className='p-0 m-0' key={props.document.id}>
|
||||||
{!props.document.areas.length
|
{!props.document.areas.length
|
||||||
?
|
?
|
||||||
<div
|
<div
|
||||||
onClick={() => onDocumentClickHandler(props.document.id)}
|
|
||||||
onDoubleClick={() => onDocumentDoubleClickHandler(props.document.id)}
|
|
||||||
className={classNames(
|
|
||||||
props.document.id === selectedDocumentId
|
|
||||||
? 'bg-gray-900 text-white'
|
|
||||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
|
||||||
'group items-center py-2 text-base font-medium rounded-b-md pl-10',
|
|
||||||
props.index !== 0 ? 'rounded-t-md' : '',
|
|
||||||
)}>
|
|
||||||
{selectedDocumentId === props.document.id && isEditDocumentNameInputShowing
|
|
||||||
? <input
|
|
||||||
type="text"
|
|
||||||
name="documentName"
|
|
||||||
id="documentName"
|
|
||||||
autoFocus
|
|
||||||
className="h-8 w-[calc(100%-18px)] text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 inline-block rounded-none rounded-l-md border-late-700 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
|
||||||
defaultValue={props.document.name}
|
|
||||||
onBlur={onDocumentInputBlur}
|
|
||||||
onKeyDown={(event) => {
|
|
||||||
onEnterHandler(event,
|
|
||||||
() => onConfirmDocumentNameChangeHandler(event.currentTarget.value))
|
|
||||||
}}
|
|
||||||
ref={editDocumentNameTextInput}
|
|
||||||
/>
|
|
||||||
: <a
|
|
||||||
role='button'
|
|
||||||
className={classNames(
|
|
||||||
props.document.id === selectedDocumentId
|
|
||||||
? 'bg-gray-900 text-white'
|
|
||||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
|
||||||
'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 '
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{props.document.name}
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
: <details>
|
|
||||||
<summary
|
|
||||||
onClick={() => onDocumentClickHandler(props.document.id)}
|
onClick={() => onDocumentClickHandler(props.document.id)}
|
||||||
onDoubleClick={() => onDocumentDoubleClickHandler(props.document.id)}
|
onDoubleClick={() => onDocumentDoubleClickHandler(props.document.id)}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
props.document.id === selectedDocumentId
|
props.document.id === selectedDocumentId
|
||||||
? 'bg-gray-900 text-white'
|
? 'bg-gray-900 text-white'
|
||||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||||
'group items-center py-2 text-base font-medium rounded-b-md pl-6',
|
'group items-center py-2 text-base font-medium rounded-b-md pl-10',
|
||||||
props.index !== 0 ? 'rounded-t-md' : '',
|
props.index !== 0 ? 'rounded-t-md' : '',
|
||||||
|
|
||||||
)}>
|
)}>
|
||||||
{selectedDocumentId === props.document.id && isEditDocumentNameInputShowing
|
{selectedDocumentId === props.document.id && isEditDocumentNameInputShowing
|
||||||
? <input
|
? <input
|
||||||
@ -121,73 +83,123 @@ const DocumentLineItem = (props: { document: SidebarDocument, groupId: string, i
|
|||||||
}}
|
}}
|
||||||
ref={editDocumentNameTextInput}
|
ref={editDocumentNameTextInput}
|
||||||
/>
|
/>
|
||||||
: <a
|
: <span className='flex justify-between items-center'>
|
||||||
role='button'
|
<a
|
||||||
className={classNames(
|
role='button'
|
||||||
props.document.id === selectedDocumentId
|
className={classNames(
|
||||||
? 'bg-gray-900 text-white'
|
props.document.id === selectedDocumentId
|
||||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
? 'bg-gray-900 text-white'
|
||||||
'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 '
|
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||||
)}
|
'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 '
|
||||||
>
|
)}
|
||||||
{props.document.name}
|
>
|
||||||
</a>
|
{props.document.name}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<XMarkIcon
|
||||||
|
className='w-5 h-5 mr-2 text-white hover:bg-red-400 hover:text-gray-100 rounded-full p-0.5'
|
||||||
|
onClick={() => requestDeleteDocumentById(props.document.id)} />
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
</summary>
|
</div>
|
||||||
<ul>
|
: <details>
|
||||||
{props.document.areas.map((a, index) => (
|
<summary
|
||||||
<AreaLineItem key={a.id} area={a} index={index} documentId={props.document.id} />
|
onClick={() => onDocumentClickHandler(props.document.id)}
|
||||||
// <li key={a.id}>
|
onDoubleClick={() => onDocumentDoubleClickHandler(props.document.id)}
|
||||||
// {selectedAreaId === a.id && isEditAreaNameInputShowing
|
className={classNames(
|
||||||
// ? <input
|
props.document.id === selectedDocumentId
|
||||||
// type="text"
|
? 'bg-gray-900 text-white'
|
||||||
// name="areaName"
|
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||||
// id="areaName"
|
'group items-center py-2 text-base font-medium rounded-b-md pl-6',
|
||||||
// autoFocus
|
props.index !== 0 ? 'rounded-t-md' : '',
|
||||||
// className="h-8 text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 block w-full rounded-none rounded-l-md border-late-700 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
|
||||||
// placeholder={a.name || `Area ${index}`}
|
)}>
|
||||||
// onBlur={onAreaInputBlur}
|
{selectedDocumentId === props.document.id && isEditDocumentNameInputShowing
|
||||||
// onKeyDown={(event) => {
|
? <input
|
||||||
// onEnterHandler(event,
|
type="text"
|
||||||
// () => onConfirmAreaNameChangeHandler({ areaId: a.id, areaName: event.currentTarget.value }))
|
name="documentName"
|
||||||
// }}
|
id="documentName"
|
||||||
// ref={editAreaNameTextInput}
|
autoFocus
|
||||||
// />
|
className="h-8 w-[calc(100%-18px)] text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 inline-block rounded-none rounded-l-md border-late-700 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
||||||
// : <div
|
defaultValue={props.document.name}
|
||||||
// draggable
|
onBlur={onDocumentInputBlur}
|
||||||
// onDragOver={() => onAreaDragOver(a.id)}
|
onKeyDown={(event) => {
|
||||||
// onDragStart={() => onAreaDragStart(a.id)}
|
onEnterHandler(event,
|
||||||
// onDragEnd={() => onAreaDropEnd(a.id)}
|
() => onConfirmDocumentNameChangeHandler(event.currentTarget.value))
|
||||||
// className={classNames('flex justify-between items-center cursor-pointer',
|
}}
|
||||||
// selectedAreaId === a.id ? 'bg-indigo-500 text-gray-200' : 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
ref={editDocumentNameTextInput}
|
||||||
// dragOverAreaId === a.id ? 'bg-gray-300 text-gray-700' : '',
|
/>
|
||||||
// selectedAreaId === a.id && dragOverAreaId === a.id ? 'bg-indigo-300' : '',
|
: <a
|
||||||
// )}>
|
role='button'
|
||||||
// <a
|
className={classNames(
|
||||||
// role='button'
|
props.document.id === selectedDocumentId
|
||||||
// onClick={() => onAreaClick(a.id)}
|
? 'bg-gray-900 text-white'
|
||||||
// onDoubleClick={() => onAreaDoubleClick(a.id)}
|
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||||
// className={classNames('group w-full pr-2 py-2 text-left font-medium pl-8 text-xs',
|
'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 '
|
||||||
// 'rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 py-2 select-none',
|
)}
|
||||||
// )}>
|
>
|
||||||
// {a.name || `Area ${a.order}`}
|
{props.document.name}
|
||||||
// </a>
|
</a>
|
||||||
// <ArrowPathIcon
|
}
|
||||||
// className='w-6 h-5 mr-2 text-white hover:bg-white hover:text-gray-700 rounded-full p-0.5'
|
|
||||||
// aria-hidden="true"
|
<XMarkIcon
|
||||||
// onClick={() => console.log('refresh')}
|
className='w-6 h-5 mr-2 text-white hover:bg-red-400 hover:text-gray-100 rounded-full p-0.5'
|
||||||
// />
|
onClick={() => requestDeleteDocumentById(props.document.id)} />
|
||||||
// <XMarkIcon
|
</summary>
|
||||||
// className='w-6 h-5 mr-2 text-white hover:bg-red-400 hover:text-gray-100 rounded-full p-0.5'
|
<ul>
|
||||||
// onClick={() => handleAreaDeleteButtonClick(a.id)} />
|
{props.document.areas.map((a, index) => (
|
||||||
// </div>
|
<AreaLineItem key={a.id} area={a} index={index} documentId={props.document.id} />
|
||||||
// }
|
// <li key={a.id}>
|
||||||
// </li>
|
// {selectedAreaId === a.id && isEditAreaNameInputShowing
|
||||||
))}
|
// ? <input
|
||||||
</ul>
|
// type="text"
|
||||||
</details>
|
// name="areaName"
|
||||||
}
|
// id="areaName"
|
||||||
</li>
|
// autoFocus
|
||||||
|
// className="h-8 text-white placeholder-gray-400 bg-gray-900 bg-opacity-5 block w-full rounded-none rounded-l-md border-late-700 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
||||||
|
// placeholder={a.name || `Area ${index}`}
|
||||||
|
// onBlur={onAreaInputBlur}
|
||||||
|
// onKeyDown={(event) => {
|
||||||
|
// onEnterHandler(event,
|
||||||
|
// () => onConfirmAreaNameChangeHandler({ areaId: a.id, areaName: event.currentTarget.value }))
|
||||||
|
// }}
|
||||||
|
// ref={editAreaNameTextInput}
|
||||||
|
// />
|
||||||
|
// : <div
|
||||||
|
// draggable
|
||||||
|
// onDragOver={() => onAreaDragOver(a.id)}
|
||||||
|
// onDragStart={() => onAreaDragStart(a.id)}
|
||||||
|
// onDragEnd={() => onAreaDropEnd(a.id)}
|
||||||
|
// className={classNames('flex justify-between items-center cursor-pointer',
|
||||||
|
// selectedAreaId === a.id ? 'bg-indigo-500 text-gray-200' : 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||||
|
// dragOverAreaId === a.id ? 'bg-gray-300 text-gray-700' : '',
|
||||||
|
// selectedAreaId === a.id && dragOverAreaId === a.id ? 'bg-indigo-300' : '',
|
||||||
|
// )}>
|
||||||
|
// <a
|
||||||
|
// role='button'
|
||||||
|
// onClick={() => onAreaClick(a.id)}
|
||||||
|
// onDoubleClick={() => onAreaDoubleClick(a.id)}
|
||||||
|
// className={classNames('group w-full pr-2 py-2 text-left font-medium pl-8 text-xs',
|
||||||
|
// 'rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 py-2 select-none',
|
||||||
|
// )}>
|
||||||
|
// {a.name || `Area ${a.order}`}
|
||||||
|
// </a>
|
||||||
|
// <ArrowPathIcon
|
||||||
|
// className='w-6 h-5 mr-2 text-white hover:bg-white hover:text-gray-700 rounded-full p-0.5'
|
||||||
|
// aria-hidden="true"
|
||||||
|
// onClick={() => console.log('refresh')}
|
||||||
|
// />
|
||||||
|
// <XMarkIcon
|
||||||
|
// className='w-6 h-5 mr-2 text-white hover:bg-red-400 hover:text-gray-100 rounded-full p-0.5'
|
||||||
|
// onClick={() => handleAreaDeleteButtonClick(a.id)} />
|
||||||
|
// </div>
|
||||||
|
// }
|
||||||
|
// </li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
}
|
||||||
|
</li>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useState } from 'react'
|
import React from 'react'
|
||||||
import { useProject } from '../../../context/Project/provider'
|
import { useProject } from '../../../context/Project/provider'
|
||||||
import AddGroupInput from './AddGroupInput'
|
import AddGroupInput from './AddGroupInput'
|
||||||
import GroupLineItem from './GroupLineItem'
|
import GroupLineItem from './GroupLineItem'
|
||||||
|
|||||||
@ -16,7 +16,7 @@ loader.config({
|
|||||||
})
|
})
|
||||||
|
|
||||||
let editorInteractions: ReturnType<typeof createDiffEditorInteractions>
|
let editorInteractions: ReturnType<typeof createDiffEditorInteractions>
|
||||||
const editorHeightOffset = 234
|
const editorHeightOffset = 174
|
||||||
|
|
||||||
const fontSizeStep = 1
|
const fontSizeStep = 1
|
||||||
const maxFontSize = 36
|
const maxFontSize = 36
|
||||||
|
|||||||
@ -15,6 +15,7 @@ const makeDefaultProject = (): ProjectContextType => ({
|
|||||||
requestUpdateArea: (updatedArea) => Promise.resolve(new ipc.Area()),
|
requestUpdateArea: (updatedArea) => Promise.resolve(new ipc.Area()),
|
||||||
requestDeleteAreaById: (areaId) => Promise.resolve(false),
|
requestDeleteAreaById: (areaId) => Promise.resolve(false),
|
||||||
requestAddDocument: (groupId, documentName) => Promise.resolve(new ipc.Document()),
|
requestAddDocument: (groupId, documentName) => Promise.resolve(new ipc.Document()),
|
||||||
|
requestDeleteDocumentById: (documentId) => Promise.resolve(false),
|
||||||
requestAddDocumentGroup: (groupName: string) => Promise.resolve(new ipc.Group()),
|
requestAddDocumentGroup: (groupName: string) => Promise.resolve(new ipc.Group()),
|
||||||
requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise.resolve(new ipc.UserMarkdown()),
|
requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise.resolve(new ipc.UserMarkdown()),
|
||||||
getUserMarkdownByDocumentId: (documentId) => Promise.resolve(new ipc.UserMarkdown),
|
getUserMarkdownByDocumentId: (documentId) => Promise.resolve(new ipc.UserMarkdown),
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
RequestDeleteAreaById,
|
RequestDeleteAreaById,
|
||||||
RequestChangeGroupOrder,
|
RequestChangeGroupOrder,
|
||||||
RequestChangeSessionProjectByName,
|
RequestChangeSessionProjectByName,
|
||||||
|
RequestDeleteDocumentAndChildren,
|
||||||
} from '../../wailsjs/wailsjs/go/ipc/Channel'
|
} from '../../wailsjs/wailsjs/go/ipc/Channel'
|
||||||
import { ipc } from '../../wailsjs/wailsjs/go/models'
|
import { ipc } from '../../wailsjs/wailsjs/go/models'
|
||||||
import { AddAreaProps, AreaProps, ProjectContextType, ProjectProps, UpdateDocumentRequest, UserProps } from './types'
|
import { AddAreaProps, AreaProps, ProjectContextType, ProjectProps, UpdateDocumentRequest, UserProps } from './types'
|
||||||
@ -35,8 +36,8 @@ export function ProjectProvider({ children, projectProps }: Props) {
|
|||||||
const updateDocuments = async () => {
|
const updateDocuments = async () => {
|
||||||
GetDocuments().then(response => {
|
GetDocuments().then(response => {
|
||||||
console.log(response)
|
console.log(response)
|
||||||
if (response.documents.length) setDocuments(response.documents)
|
setDocuments(response.documents)
|
||||||
if (response.groups.length) setGroups(response.groups)
|
setGroups(response.groups)
|
||||||
Promise.resolve(response)
|
Promise.resolve(response)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -48,6 +49,13 @@ export function ProjectProvider({ children, projectProps }: Props) {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const requestDeleteDocumentById = async (documentId: string): Promise<boolean> => {
|
||||||
|
const wasSuccessfulDeletion = await RequestDeleteDocumentAndChildren(documentId)
|
||||||
|
updateDocuments()
|
||||||
|
saveDocuments()
|
||||||
|
return wasSuccessfulDeletion
|
||||||
|
}
|
||||||
|
|
||||||
const requestAddDocumentGroup = async (groupName: string) => {
|
const requestAddDocumentGroup = async (groupName: string) => {
|
||||||
const response = await RequestAddDocumentGroup(groupName)
|
const response = await RequestAddDocumentGroup(groupName)
|
||||||
if (response.id) await updateDocuments()
|
if (response.id) await updateDocuments()
|
||||||
@ -195,6 +203,7 @@ export function ProjectProvider({ children, projectProps }: Props) {
|
|||||||
getAreaById,
|
getAreaById,
|
||||||
requestAddArea,
|
requestAddArea,
|
||||||
requestAddDocument,
|
requestAddDocument,
|
||||||
|
requestDeleteDocumentById,
|
||||||
requestAddDocumentGroup,
|
requestAddDocumentGroup,
|
||||||
requestUpdateArea,
|
requestUpdateArea,
|
||||||
requestDeleteAreaById,
|
requestDeleteAreaById,
|
||||||
|
|||||||
@ -45,6 +45,7 @@ export type ProjectContextType = {
|
|||||||
requestUpdateArea: (area: AreaProps) => Promise<ipc.Area>
|
requestUpdateArea: (area: AreaProps) => Promise<ipc.Area>
|
||||||
requestDeleteAreaById: (areaId: string) => Promise<boolean>
|
requestDeleteAreaById: (areaId: string) => Promise<boolean>
|
||||||
requestAddDocument: (groupId: string, documentName: string) => Promise<ipc.Document>
|
requestAddDocument: (groupId: string, documentName: string) => Promise<ipc.Document>
|
||||||
|
requestDeleteDocumentById: (documentId: string) => Promise<boolean>
|
||||||
requestAddDocumentGroup: (groupName: string) => Promise<ipc.Group>
|
requestAddDocumentGroup: (groupName: string) => Promise<ipc.Group>
|
||||||
requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise<ipc.UserMarkdown>
|
requestUpdateDocumentUserMarkdown: (documentId: string, markdown: string) => Promise<ipc.UserMarkdown>
|
||||||
getUserMarkdownByDocumentId: (documentId: string) => Promise<ipc.UserMarkdown>
|
getUserMarkdownByDocumentId: (documentId: string) => Promise<ipc.UserMarkdown>
|
||||||
|
|||||||
13
frontend/utils/isInBounds.ts
Normal file
13
frontend/utils/isInBounds.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
type Point = { x: number, y: number }
|
||||||
|
type Bounds = { startX: number, startY: number, endX: number, endY: number }
|
||||||
|
|
||||||
|
const isInBounds = (point: Point, bounds: Bounds, rectOffsetMultiplier: number = 1) => {
|
||||||
|
const { x, y } = point
|
||||||
|
const { startX, startY, endX, endY } = bounds
|
||||||
|
|
||||||
|
const isInBoundsX = (x >= startX * rectOffsetMultiplier) && x <= endX * rectOffsetMultiplier
|
||||||
|
const isInBoundsY = (y >= startY * rectOffsetMultiplier) && y <= endY * rectOffsetMultiplier
|
||||||
|
return isInBoundsX && isInBoundsY
|
||||||
|
}
|
||||||
|
|
||||||
|
export default isInBounds
|
||||||
2
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
2
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
@ -42,6 +42,8 @@ export function RequestChooseUserAvatar():Promise<string>;
|
|||||||
|
|
||||||
export function RequestDeleteAreaById(arg1:string):Promise<boolean>;
|
export function RequestDeleteAreaById(arg1:string):Promise<boolean>;
|
||||||
|
|
||||||
|
export function RequestDeleteDocumentAndChildren(arg1:string):Promise<boolean>;
|
||||||
|
|
||||||
export function RequestSaveDocumentCollection():Promise<boolean>;
|
export function RequestSaveDocumentCollection():Promise<boolean>;
|
||||||
|
|
||||||
export function RequestSaveGroupCollection():Promise<boolean>;
|
export function RequestSaveGroupCollection():Promise<boolean>;
|
||||||
|
|||||||
@ -82,6 +82,10 @@ export function RequestDeleteAreaById(arg1) {
|
|||||||
return window['go']['ipc']['Channel']['RequestDeleteAreaById'](arg1);
|
return window['go']['ipc']['Channel']['RequestDeleteAreaById'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function RequestDeleteDocumentAndChildren(arg1) {
|
||||||
|
return window['go']['ipc']['Channel']['RequestDeleteDocumentAndChildren'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function RequestSaveDocumentCollection() {
|
export function RequestSaveDocumentCollection() {
|
||||||
return window['go']['ipc']['Channel']['RequestSaveDocumentCollection']();
|
return window['go']['ipc']['Channel']['RequestSaveDocumentCollection']();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,6 +144,26 @@ func (c *Channel) RequestAddDocument(groupId string, documentName string) Docume
|
|||||||
return documentResponse
|
return documentResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Channel) deleteDocumentById(documentId string) bool {
|
||||||
|
collection := document.GetDocumentCollection()
|
||||||
|
|
||||||
|
documentToDeleteIndex := -1
|
||||||
|
for i, d := range collection.Documents {
|
||||||
|
if d.Id == documentId {
|
||||||
|
documentToDeleteIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if documentToDeleteIndex < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.Documents[documentToDeleteIndex] = collection.Documents[len(collection.Documents)-1]
|
||||||
|
collection.Documents = collection.Documents[:len(collection.Documents)-1]
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Channel) RequestUpdateDocumentUserMarkdown(documentId string, markdown string) UserMarkdown {
|
func (c *Channel) RequestUpdateDocumentUserMarkdown(documentId string, markdown string) UserMarkdown {
|
||||||
markdownCollection := document.GetUserMarkdownCollection()
|
markdownCollection := document.GetUserMarkdownCollection()
|
||||||
markdownToUpdate := markdownCollection.GetUserMarkdownByDocumentId(documentId)
|
markdownToUpdate := markdownCollection.GetUserMarkdownByDocumentId(documentId)
|
||||||
@ -166,6 +186,26 @@ func (c *Channel) RequestUpdateDocumentUserMarkdown(documentId string, markdown
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Channel) deleteDocumentUserMarkdown(documentId string) bool {
|
||||||
|
collection := document.GetUserMarkdownCollection()
|
||||||
|
|
||||||
|
markdownToDeleteIndex := -1
|
||||||
|
for i, d := range collection.Values {
|
||||||
|
if d.DocumentId == documentId {
|
||||||
|
markdownToDeleteIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if markdownToDeleteIndex < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.Values[markdownToDeleteIndex] = collection.Values[len(collection.Values)-1]
|
||||||
|
collection.Values = collection.Values[:len(collection.Values)-1]
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Channel) GetUserMarkdownByDocumentId(documentId string) UserMarkdown {
|
func (c *Channel) GetUserMarkdownByDocumentId(documentId string) UserMarkdown {
|
||||||
foundUserMarkdown := document.GetUserMarkdownCollection().GetUserMarkdownByDocumentId((documentId))
|
foundUserMarkdown := document.GetUserMarkdownCollection().GetUserMarkdownByDocumentId((documentId))
|
||||||
|
|
||||||
@ -548,3 +588,19 @@ func (c *Channel) RequestSaveLocalUserProcessedMarkdownCollection() bool {
|
|||||||
|
|
||||||
return successfulWrite
|
return successfulWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Channel) RequestDeleteDocumentAndChildren(documentId string) bool {
|
||||||
|
success := true
|
||||||
|
|
||||||
|
deletedDocument := c.deleteDocumentById(documentId)
|
||||||
|
if !deletedDocument {
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
|
||||||
|
deletedUserMarkDown := c.deleteDocumentUserMarkdown(documentId)
|
||||||
|
if !deletedUserMarkDown {
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|||||||
@ -146,25 +146,26 @@ func deserializeProcessedArea(area ProcessedArea) document.ProcessedArea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Channel) RequestAddProcessedArea(processedArea ProcessedArea) ProcessedArea {
|
func (c *Channel) RequestAddProcessedArea(processedArea ProcessedArea) ProcessedArea {
|
||||||
doesAreaAlreadyExist := false
|
// doesAreaAlreadyExist := false
|
||||||
processedAreasOfDocument := document.GetProcessedAreaCollection().GetAreasByDocumentId(processedArea.DocumentId)
|
// processedAreasOfDocuments := document.GetProcessedAreaCollection().GetAreasByDocumentId(processedArea.DocumentId)
|
||||||
for _, a := range processedAreasOfDocument {
|
|
||||||
if a.Order == processedArea.Order {
|
// for _, a := range processedAreasOfDocuments {
|
||||||
doesAreaAlreadyExist = true
|
// if a.Order == processedArea.Order {
|
||||||
break
|
// doesAreaAlreadyExist = true
|
||||||
}
|
// break
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
deserializedProcessedArea := deserializeProcessedArea(processedArea)
|
deserializedProcessedArea := deserializeProcessedArea(processedArea)
|
||||||
|
|
||||||
if doesAreaAlreadyExist {
|
// if doesAreaAlreadyExist {
|
||||||
storedProcessedArea := document.GetProcessedAreaCollection().GetAreaById(processedArea.Id)
|
// storedProcessedArea := document.GetProcessedAreaCollection().GetAreaById(processedArea.Id)
|
||||||
if storedProcessedArea.Id != "" {
|
// if storedProcessedArea.Id != "" {
|
||||||
storedProcessedArea = &deserializedProcessedArea
|
// storedProcessedArea = &deserializedProcessedArea
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
document.GetProcessedAreaCollection().AddProcessedArea((deserializedProcessedArea))
|
document.GetProcessedAreaCollection().AddProcessedArea((deserializedProcessedArea))
|
||||||
}
|
// }
|
||||||
|
|
||||||
return processedArea
|
return processedArea
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user