131 lines
3.2 KiB
TypeScript
131 lines
3.2 KiB
TypeScript
import { useState, useCallback, useRef, useEffect } from 'react'
|
|
import { Editor, TLShapeId } from 'tldraw'
|
|
|
|
interface OriginalDimensions {
|
|
x: number
|
|
y: number
|
|
w: number
|
|
h: number
|
|
}
|
|
|
|
interface UseMaximizeOptions {
|
|
/** Editor instance */
|
|
editor: Editor
|
|
/** Shape ID to maximize */
|
|
shapeId: TLShapeId
|
|
/** Current width of the shape */
|
|
currentW: number
|
|
/** Current height of the shape */
|
|
currentH: number
|
|
/** Shape type for updateShape call */
|
|
shapeType: string
|
|
/** Padding from viewport edges in pixels */
|
|
padding?: number
|
|
}
|
|
|
|
interface UseMaximizeReturn {
|
|
/** Whether the shape is currently maximized */
|
|
isMaximized: boolean
|
|
/** Toggle maximize state */
|
|
toggleMaximize: () => void
|
|
}
|
|
|
|
/**
|
|
* Hook to enable maximize/fullscreen functionality for shapes.
|
|
* When maximized, the shape fills the viewport.
|
|
* Press Esc or click maximize again to restore original size.
|
|
*/
|
|
export function useMaximize({
|
|
editor,
|
|
shapeId,
|
|
currentW,
|
|
currentH,
|
|
shapeType,
|
|
padding = 40,
|
|
}: UseMaximizeOptions): UseMaximizeReturn {
|
|
const [isMaximized, setIsMaximized] = useState(false)
|
|
const originalDimensionsRef = useRef<OriginalDimensions | null>(null)
|
|
|
|
const toggleMaximize = useCallback(() => {
|
|
if (!editor || !shapeId) return
|
|
|
|
const shape = editor.getShape(shapeId)
|
|
if (!shape) return
|
|
|
|
if (isMaximized) {
|
|
// Restore original dimensions
|
|
const original = originalDimensionsRef.current
|
|
if (original) {
|
|
editor.updateShape({
|
|
id: shapeId,
|
|
type: shapeType,
|
|
x: original.x,
|
|
y: original.y,
|
|
props: {
|
|
w: original.w,
|
|
h: original.h,
|
|
},
|
|
})
|
|
}
|
|
originalDimensionsRef.current = null
|
|
setIsMaximized(false)
|
|
} else {
|
|
// Store current dimensions before maximizing
|
|
originalDimensionsRef.current = {
|
|
x: shape.x,
|
|
y: shape.y,
|
|
w: currentW,
|
|
h: currentH,
|
|
}
|
|
|
|
// Get viewport bounds in page coordinates
|
|
const viewportBounds = editor.getViewportPageBounds()
|
|
|
|
// Calculate new dimensions to fill viewport with padding
|
|
const newX = viewportBounds.x + padding
|
|
const newY = viewportBounds.y + padding
|
|
const newW = viewportBounds.width - (padding * 2)
|
|
const newH = viewportBounds.height - (padding * 2)
|
|
|
|
editor.updateShape({
|
|
id: shapeId,
|
|
type: shapeType,
|
|
x: newX,
|
|
y: newY,
|
|
props: {
|
|
w: newW,
|
|
h: newH,
|
|
},
|
|
})
|
|
|
|
// Center the view on the maximized shape
|
|
editor.centerOnPoint({ x: newX + newW / 2, y: newY + newH / 2 })
|
|
|
|
setIsMaximized(true)
|
|
}
|
|
}, [editor, shapeId, shapeType, currentW, currentH, padding, isMaximized])
|
|
|
|
// Clean up when shape is deleted or unmounted
|
|
useEffect(() => {
|
|
return () => {
|
|
originalDimensionsRef.current = null
|
|
}
|
|
}, [])
|
|
|
|
// Reset maximize state if shape dimensions change externally while maximized
|
|
useEffect(() => {
|
|
if (isMaximized && originalDimensionsRef.current) {
|
|
const shape = editor.getShape(shapeId)
|
|
if (!shape) {
|
|
setIsMaximized(false)
|
|
originalDimensionsRef.current = null
|
|
}
|
|
}
|
|
}, [editor, shapeId, isMaximized])
|
|
|
|
return {
|
|
isMaximized,
|
|
toggleMaximize,
|
|
}
|
|
}
|