import { useQuickReactor, useStateTracking, useValue } from '@tldraw/state-react' import { TLShape, TLShapeId } from '@tldraw/tlschema' import classNames from 'classnames' import { memo, useLayoutEffect, useRef } from 'react' import type { Editor } from '../../editor/Editor' import { ShapeUtil } from '../../editor/shapes/ShapeUtil' import { useEditorComponents } from '../../hooks/EditorComponentsContext' import { useEditor } from '../../hooks/useEditor' import { OptionalErrorBoundary } from '../ErrorBoundary' // need an extra layer of indirection here to allow hooks to be used inside the indicator render const EvenInnererIndicator = memo( ({ shape, util }: { shape: TLShape; util: ShapeUtil }) => { return useStateTracking('Indicator: ' + shape.type, () => // always fetch the latest shape from the store even if the props/meta have not changed, to avoid // calling the render method with stale data. util.indicator(util.editor.store.unsafeGetWithoutCapture(shape.id) as TLShape) ) }, (prevProps, nextProps) => { return ( prevProps.shape.props === nextProps.shape.props && prevProps.shape.meta === nextProps.shape.meta ) } ) const InnerIndicator = memo(({ editor, id }: { editor: Editor; id: TLShapeId }) => { const shape = useValue('shape for indicator', () => editor.store.get(id), [editor, id]) const { ShapeIndicatorErrorFallback } = useEditorComponents() if (!shape || shape.isLocked) return null const util = editor.getShapeUtil(shape) // If the shape uses canvas indicators, it will be rendered by CanvasShapeIndicators if (!util.useLegacyIndicator()) return null return ( editor.annotateError(error, { origin: 'react.shapeIndicator', willCrashApp: false }) } > ) }) /** @public */ export interface TLShapeIndicatorProps { userId?: string shapeId: TLShapeId color?: string | undefined opacity?: number className?: string hidden?: boolean } /** @public @react */ export const DefaultShapeIndicator = memo(function DefaultShapeIndicator({ shapeId, className, color, hidden, opacity, }: TLShapeIndicatorProps) { const editor = useEditor() const rIndicator = useRef(null) useQuickReactor( 'indicator transform', () => { if (hidden) return const elm = rIndicator.current if (!elm) return const pageTransform = editor.getShapePageTransform(shapeId) if (!pageTransform) return elm.style.setProperty('transform', pageTransform.toCssString()) }, [editor, shapeId, hidden] ) useLayoutEffect(() => { const elm = rIndicator.current if (!elm) return elm.style.setProperty('display', hidden ? 'none' : 'block') }, [hidden]) return ( ) })