implemented basic board text search function, added double click to zoom

This commit is contained in:
Jeff-Emmett 2025-01-03 10:52:04 +07:00
parent 7b1fe2b803
commit e3e2c474ac
3 changed files with 128 additions and 0 deletions

View File

@ -18,6 +18,7 @@ import {
import { useState, useEffect } from "react"
import { saveToPdf } from "../utils/pdfUtils"
import { TLFrameShape } from "tldraw"
import { searchText } from "../utils/searchUtils"
const getAllFrames = (editor: Editor) => {
return editor
@ -217,6 +218,16 @@ export function CustomContextMenu(props: TLUiContextMenuProps) {
}}
/>
</TldrawUiMenuGroup>
<TldrawUiMenuGroup id="search-controls">
<TldrawUiMenuItem
id="search-text"
label="Search Text"
icon="search"
kbd="s"
onSelect={() => searchText(editor)}
/>
</TldrawUiMenuGroup>
</DefaultContextMenu>
)
}

View File

@ -7,6 +7,7 @@ import {
zoomToSelection,
} from "./cameraUtils"
import { saveToPdf } from "../utils/pdfUtils"
import { searchText } from "../utils/searchUtils"
export const overrides: TLUiOverrides = {
tools(editor, tools) {
@ -38,6 +39,22 @@ export const overrides: TLUiOverrides = {
// Otherwise, use default select tool behavior
;(tools.select as any).onPointerDown?.(info)
},
//TODO: Fix double click to zoom on selector tool later...
onDoubleClick: (info: any) => {
// Prevent default double-click behavior (which would start text editing)
info.preventDefault?.()
// Handle all pointer types (mouse, touch, pen)
const point = info.point || (info.touches && info.touches[0]) || info
// Zoom in at the clicked/touched point
editor.zoomIn(point, { animation: { duration: 200 } })
// Stop event propagation and prevent default handling
info.stopPropagation?.()
return false
},
},
VideoChat: {
id: "VideoChat",
@ -81,6 +98,12 @@ export const overrides: TLUiOverrides = {
onSelect: () => editor.setCurrentTool("MycrozineTemplate"),
},
*/
hand: {
...tools.hand,
onDoubleClick: (info: any) => {
editor.zoomIn(info.point, { animation: { duration: 200 } })
},
},
}
},
actions(editor, actions) {
@ -317,6 +340,13 @@ export const overrides: TLUiOverrides = {
editor.stopFollowingUser()
},
},
searchShapes: {
id: "search-shapes",
label: "Search Shapes",
kbd: "s",
readonlyOk: true,
onSelect: () => searchText(editor),
},
}
},
}

87
src/utils/searchUtils.ts Normal file
View File

@ -0,0 +1,87 @@
import { Editor } from "tldraw"
export const searchText = (editor: Editor) => {
// Switch to select tool first
editor.setCurrentTool('select')
const searchTerm = prompt("Enter search text:")
if (!searchTerm) return
const shapes = editor.getCurrentPageShapes()
const matchingShapes = shapes.filter(shape => {
if (!shape.props) return false
const textProperties = [
(shape.props as any).text,
(shape.props as any).name,
(shape.props as any).value,
(shape.props as any).url,
(shape.props as any).description,
(shape.props as any).content,
]
const termLower = searchTerm.toLowerCase()
return textProperties.some(prop =>
typeof prop === 'string' &&
prop.toLowerCase().includes(termLower)
)
})
if (matchingShapes.length > 0) {
editor.selectNone()
editor.setSelectedShapes(matchingShapes)
const commonBounds = editor.getSelectionPageBounds()
if (!commonBounds) return
// Calculate viewport dimensions
const viewportPageBounds = editor.getViewportPageBounds()
// Calculate the ratio of selection size to viewport size
const widthRatio = commonBounds.width / viewportPageBounds.width
const heightRatio = commonBounds.height / viewportPageBounds.height
// Calculate target zoom based on selection size
let targetZoom
if (widthRatio < 0.1 || heightRatio < 0.1) {
targetZoom = Math.min(
(viewportPageBounds.width * 0.8) / commonBounds.width,
(viewportPageBounds.height * 0.8) / commonBounds.height,
40
)
} else if (widthRatio > 1 || heightRatio > 1) {
targetZoom = Math.min(
(viewportPageBounds.width * 0.7) / commonBounds.width,
(viewportPageBounds.height * 0.7) / commonBounds.height,
0.125
)
} else {
targetZoom = Math.min(
(viewportPageBounds.width * 0.8) / commonBounds.width,
(viewportPageBounds.height * 0.8) / commonBounds.height,
20
)
}
// Zoom to the common bounds
editor.zoomToBounds(commonBounds, {
targetZoom,
inset: widthRatio > 1 || heightRatio > 1 ? 20 : 50,
animation: {
duration: 400,
easing: (t) => t * (2 - t),
},
})
// Update URL with new camera position and first selected shape ID
const newCamera = editor.getCamera()
const url = new URL(window.location.href)
url.searchParams.set("shapeId", matchingShapes[0].id)
url.searchParams.set("x", newCamera.x.toString())
url.searchParams.set("y", newCamera.y.toString())
url.searchParams.set("zoom", newCamera.z.toString())
window.history.replaceState(null, "", url.toString())
} else {
alert("No matches found")
}
}