Add component for ShapeIndicators
(#4083)
This PR adds a component for `ShapeIndicators` to the UI component overrides. It moves the "select tool" state logic up to the new `TldrawShapeIndicators` component. ### Change type - [ ] `bugfix` - [x] `improvement` - [ ] `feature` - [x] `api` - [ ] `other` ### Release notes - Added new `ShapeIndicators` component to `components` object. - Added new `TldrawShapeIndicators` component.
This commit is contained in:
parent
a466ffe92a
commit
1bf2820a3f
15 changed files with 146 additions and 126 deletions
|
@ -9,6 +9,7 @@ import {
|
||||||
TldrawScribble,
|
TldrawScribble,
|
||||||
TldrawSelectionBackground,
|
TldrawSelectionBackground,
|
||||||
TldrawSelectionForeground,
|
TldrawSelectionForeground,
|
||||||
|
TldrawShapeIndicators,
|
||||||
TldrawUi,
|
TldrawUi,
|
||||||
defaultBindingUtils,
|
defaultBindingUtils,
|
||||||
defaultEditorAssetUrls,
|
defaultEditorAssetUrls,
|
||||||
|
@ -24,6 +25,7 @@ import 'tldraw/tldraw.css'
|
||||||
// [1]
|
// [1]
|
||||||
const defaultComponents = {
|
const defaultComponents = {
|
||||||
Scribble: TldrawScribble,
|
Scribble: TldrawScribble,
|
||||||
|
ShapeIndicators: TldrawShapeIndicators,
|
||||||
CollaboratorScribble: TldrawScribble,
|
CollaboratorScribble: TldrawScribble,
|
||||||
SelectionForeground: TldrawSelectionForeground,
|
SelectionForeground: TldrawSelectionForeground,
|
||||||
SelectionBackground: TldrawSelectionBackground,
|
SelectionBackground: TldrawSelectionBackground,
|
||||||
|
|
|
@ -653,6 +653,9 @@ export function DefaultSelectionForeground({ bounds, rotation }: TLSelectionFore
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const DefaultShapeIndicator: NamedExoticComponent<TLShapeIndicatorProps>;
|
export const DefaultShapeIndicator: NamedExoticComponent<TLShapeIndicatorProps>;
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
export const DefaultShapeIndicators: NamedExoticComponent<object>;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function DefaultSnapIndicator({ className, line, zoom }: TLSnapIndicatorProps): JSX_2.Element;
|
export function DefaultSnapIndicator({ className, line, zoom }: TLSnapIndicatorProps): JSX_2.Element;
|
||||||
|
|
||||||
|
@ -1772,7 +1775,7 @@ export function openWindow(url: string, target?: string): void;
|
||||||
// @internal (undocumented)
|
// @internal (undocumented)
|
||||||
export function OptionalErrorBoundary({ children, fallback, ...props }: Omit<TLErrorBoundaryProps, 'fallback'> & {
|
export function OptionalErrorBoundary({ children, fallback, ...props }: Omit<TLErrorBoundaryProps, 'fallback'> & {
|
||||||
fallback: TLErrorFallbackComponent;
|
fallback: TLErrorFallbackComponent;
|
||||||
}): JSX_2.Element;
|
}): boolean | JSX_2.Element | Iterable<React_3.ReactNode> | null | number | string | undefined;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type OptionalKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
export type OptionalKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||||
|
@ -2669,6 +2672,8 @@ export interface TLEditorComponents {
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
ShapeIndicatorErrorFallback?: TLShapeIndicatorErrorFallbackComponent;
|
ShapeIndicatorErrorFallback?: TLShapeIndicatorErrorFallbackComponent;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
|
ShapeIndicators?: ComponentType | null;
|
||||||
|
// (undocumented)
|
||||||
SnapIndicator?: ComponentType<TLSnapIndicatorProps> | null;
|
SnapIndicator?: ComponentType<TLSnapIndicatorProps> | null;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
Spinner?: ComponentType | null;
|
Spinner?: ComponentType | null;
|
||||||
|
|
|
@ -96,6 +96,7 @@ export {
|
||||||
type TLShapeIndicatorProps,
|
type TLShapeIndicatorProps,
|
||||||
} from './lib/components/default-components/DefaultShapeIndicator'
|
} from './lib/components/default-components/DefaultShapeIndicator'
|
||||||
export { type TLShapeIndicatorErrorFallbackComponent } from './lib/components/default-components/DefaultShapeIndicatorErrorFallback'
|
export { type TLShapeIndicatorErrorFallbackComponent } from './lib/components/default-components/DefaultShapeIndicatorErrorFallback'
|
||||||
|
export { DefaultShapeIndicators } from './lib/components/default-components/DefaultShapeIndicators'
|
||||||
export {
|
export {
|
||||||
DefaultSnapIndicator,
|
DefaultSnapIndicator,
|
||||||
type TLSnapIndicatorProps,
|
type TLSnapIndicatorProps,
|
||||||
|
|
|
@ -453,7 +453,7 @@ function Layout({ children, onMount }: { children: ReactNode; onMount?: TLOnMoun
|
||||||
useForceUpdate()
|
useForceUpdate()
|
||||||
useOnMount(onMount)
|
useOnMount(onMount)
|
||||||
|
|
||||||
return <>{children}</>
|
return children
|
||||||
}
|
}
|
||||||
|
|
||||||
function Crash({ crashingError }: { crashingError: unknown }): null {
|
function Crash({ crashingError }: { crashingError: unknown }): null {
|
||||||
|
|
|
@ -46,7 +46,7 @@ export function OptionalErrorBoundary({
|
||||||
fallback: TLErrorFallbackComponent
|
fallback: TLErrorFallbackComponent
|
||||||
}) {
|
}) {
|
||||||
if (fallback === null) {
|
if (fallback === null) {
|
||||||
return <>{children}</>
|
return children
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -9,13 +9,7 @@ import { usePresence } from '../hooks/usePresence'
|
||||||
|
|
||||||
export const LiveCollaborators = track(function Collaborators() {
|
export const LiveCollaborators = track(function Collaborators() {
|
||||||
const peerIds = usePeerIds()
|
const peerIds = usePeerIds()
|
||||||
return (
|
return peerIds.map((id) => <CollaboratorGuard key={id} collaboratorId={id} />)
|
||||||
<>
|
|
||||||
{peerIds.map((id) => (
|
|
||||||
<CollaboratorGuard key={id} collaboratorId={id} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const CollaboratorGuard = track(function CollaboratorGuard({
|
const CollaboratorGuard = track(function CollaboratorGuard({
|
||||||
|
|
|
@ -33,7 +33,7 @@ export interface TLCanvasComponentProps {
|
||||||
export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
|
|
||||||
const { Background, SvgDefs } = useEditorComponents()
|
const { Background, SvgDefs, ShapeIndicators } = useEditorComponents()
|
||||||
|
|
||||||
const rCanvas = useRef<HTMLDivElement>(null)
|
const rCanvas = useRef<HTMLDivElement>(null)
|
||||||
const rHtmlLayer = useRef<HTMLDivElement>(null)
|
const rHtmlLayer = useRef<HTMLDivElement>(null)
|
||||||
|
@ -161,7 +161,7 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
||||||
<BrushWrapper />
|
<BrushWrapper />
|
||||||
<ScribbleWrapper />
|
<ScribbleWrapper />
|
||||||
<ZoomBrushWrapper />
|
<ZoomBrushWrapper />
|
||||||
<ShapeIndicators />
|
{ShapeIndicators && <ShapeIndicators />}
|
||||||
<HintedShapeIndicator />
|
<HintedShapeIndicator />
|
||||||
<SnapIndicatorWrapper />
|
<SnapIndicatorWrapper />
|
||||||
<SelectionForegroundWrapper />
|
<SelectionForegroundWrapper />
|
||||||
|
@ -201,18 +201,9 @@ function ScribbleWrapper() {
|
||||||
|
|
||||||
if (!(Scribble && scribbles.length)) return null
|
if (!(Scribble && scribbles.length)) return null
|
||||||
|
|
||||||
return (
|
return scribbles.map((scribble) => (
|
||||||
<>
|
<Scribble key={scribble.id} className="tl-user-scribble" scribble={scribble} zoom={zoomLevel} />
|
||||||
{scribbles.map((scribble) => (
|
))
|
||||||
<Scribble
|
|
||||||
key={scribble.id}
|
|
||||||
className="tl-user-scribble"
|
|
||||||
scribble={scribble}
|
|
||||||
zoom={zoomLevel}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function BrushWrapper() {
|
function BrushWrapper() {
|
||||||
|
@ -243,13 +234,9 @@ function SnapIndicatorWrapper() {
|
||||||
|
|
||||||
if (!(SnapIndicator && lines.length > 0)) return null
|
if (!(SnapIndicator && lines.length > 0)) return null
|
||||||
|
|
||||||
return (
|
return lines.map((line) => (
|
||||||
<>
|
<SnapIndicator key={line.id} className="tl-user-snapline" line={line} zoom={zoomLevel} />
|
||||||
{lines.map((line) => (
|
))
|
||||||
<SnapIndicator key={line.id} className="tl-user-snapline" line={line} zoom={zoomLevel} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function HandlesWrapper() {
|
function HandlesWrapper() {
|
||||||
|
@ -388,16 +375,12 @@ function ShapesWithSVGs() {
|
||||||
[editor]
|
[editor]
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return renderingShapes.map((result) => (
|
||||||
<>
|
<Fragment key={result.id + '_fragment'}>
|
||||||
{renderingShapes.map((result) => (
|
<Shape {...result} dprMultiple={dprMultiple} />
|
||||||
<Fragment key={result.id + '_fragment'}>
|
<DebugSvgCopy id={result.id} />
|
||||||
<Shape {...result} dprMultiple={dprMultiple} />
|
</Fragment>
|
||||||
<DebugSvgCopy id={result.id} />
|
))
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
function ReflowIfNeeded() {
|
function ReflowIfNeeded() {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
|
@ -448,70 +431,6 @@ function ShapesToDisplay() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ShapeIndicators() {
|
|
||||||
const editor = useEditor()
|
|
||||||
const renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])
|
|
||||||
const rPreviousSelectedShapeIds = useRef<Set<TLShapeId>>(new Set())
|
|
||||||
const idsToDisplay = useValue(
|
|
||||||
'should display selected ids',
|
|
||||||
() => {
|
|
||||||
// todo: move to tldraw selected ids wrappe
|
|
||||||
const prev = rPreviousSelectedShapeIds.current
|
|
||||||
const next = new Set<TLShapeId>()
|
|
||||||
if (
|
|
||||||
editor.isInAny(
|
|
||||||
'select.idle',
|
|
||||||
'select.brushing',
|
|
||||||
'select.scribble_brushing',
|
|
||||||
'select.editing_shape',
|
|
||||||
'select.pointing_shape',
|
|
||||||
'select.pointing_selection',
|
|
||||||
'select.pointing_handle'
|
|
||||||
) &&
|
|
||||||
!editor.getInstanceState().isChangingStyle
|
|
||||||
) {
|
|
||||||
const selected = editor.getSelectedShapeIds()
|
|
||||||
for (const id of selected) {
|
|
||||||
next.add(id)
|
|
||||||
}
|
|
||||||
if (editor.isInAny('select.idle', 'select.editing_shape')) {
|
|
||||||
const instanceState = editor.getInstanceState()
|
|
||||||
if (instanceState.isHoveringCanvas && !instanceState.isCoarsePointer) {
|
|
||||||
const hovered = editor.getHoveredShapeId()
|
|
||||||
if (hovered) next.add(hovered)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev.size !== next.size) {
|
|
||||||
rPreviousSelectedShapeIds.current = next
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const id of next) {
|
|
||||||
if (!prev.has(id)) {
|
|
||||||
rPreviousSelectedShapeIds.current = next
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prev
|
|
||||||
},
|
|
||||||
[editor]
|
|
||||||
)
|
|
||||||
|
|
||||||
const { ShapeIndicator } = useEditorComponents()
|
|
||||||
if (!ShapeIndicator) return null
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{renderingShapes.map(({ id }) => (
|
|
||||||
<ShapeIndicator key={id + '_indicator'} shapeId={id} hidden={!idsToDisplay.has(id)} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function HintedShapeIndicator() {
|
function HintedShapeIndicator() {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
const { ShapeIndicator } = useEditorComponents()
|
const { ShapeIndicator } = useEditorComponents()
|
||||||
|
@ -521,13 +440,9 @@ function HintedShapeIndicator() {
|
||||||
if (!ids.length) return null
|
if (!ids.length) return null
|
||||||
if (!ShapeIndicator) return null
|
if (!ShapeIndicator) return null
|
||||||
|
|
||||||
return (
|
return ids.map((id) => (
|
||||||
<>
|
<ShapeIndicator className="tl-user-indicator__hint" shapeId={id} key={id + '_hinting'} />
|
||||||
{ids.map((id) => (
|
))
|
||||||
<ShapeIndicator className="tl-user-indicator__hint" shapeId={id} key={id + '_hinting'} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function CursorDef() {
|
function CursorDef() {
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { useValue } from '@tldraw/state'
|
||||||
|
import { TLShapeId } from '@tldraw/tlschema'
|
||||||
|
import { memo, useRef } from 'react'
|
||||||
|
import { useEditor } from '../../hooks/useEditor'
|
||||||
|
import { useEditorComponents } from '../../hooks/useEditorComponents'
|
||||||
|
|
||||||
|
/** @public @react */
|
||||||
|
export const DefaultShapeIndicators = memo(function DefaultShapeIndicators() {
|
||||||
|
const editor = useEditor()
|
||||||
|
|
||||||
|
const rPreviousSelectedShapeIds = useRef<Set<TLShapeId>>(new Set())
|
||||||
|
|
||||||
|
const idsToDisplay = useValue(
|
||||||
|
'should display selected ids',
|
||||||
|
() => {
|
||||||
|
const prev = rPreviousSelectedShapeIds.current
|
||||||
|
const next = new Set<TLShapeId>()
|
||||||
|
|
||||||
|
if (
|
||||||
|
// We only show indicators when in the following states...
|
||||||
|
editor.isInAny(
|
||||||
|
'select.idle',
|
||||||
|
'select.brushing',
|
||||||
|
'select.scribble_brushing',
|
||||||
|
'select.editing_shape',
|
||||||
|
'select.pointing_shape',
|
||||||
|
'select.pointing_selection',
|
||||||
|
'select.pointing_handle'
|
||||||
|
) &&
|
||||||
|
// ...but we hide indicators when we've just changed a style (so that the user can see the change)
|
||||||
|
!editor.getInstanceState().isChangingStyle
|
||||||
|
) {
|
||||||
|
// We always want to show indicators for the selected shapes, if any
|
||||||
|
const selected = editor.getSelectedShapeIds()
|
||||||
|
for (const id of selected) {
|
||||||
|
next.add(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're idle or editing a shape, we want to also show an indicator for the hovered shape, if any
|
||||||
|
if (editor.isInAny('select.idle', 'select.editing_shape')) {
|
||||||
|
const instanceState = editor.getInstanceState()
|
||||||
|
if (instanceState.isHoveringCanvas && !instanceState.isCoarsePointer) {
|
||||||
|
const hovered = editor.getHoveredShapeId()
|
||||||
|
if (hovered) next.add(hovered)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, has anything changed?
|
||||||
|
|
||||||
|
// If the number of items in the set is different, then the selection has changed. This catches most changes.
|
||||||
|
if (prev.size !== next.size) {
|
||||||
|
rPreviousSelectedShapeIds.current = next
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any of the new ids are not in the previous set, then the selection has changed
|
||||||
|
for (const id of next) {
|
||||||
|
if (!prev.has(id)) {
|
||||||
|
rPreviousSelectedShapeIds.current = next
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If nothing has changed, then return the previous value
|
||||||
|
return prev
|
||||||
|
},
|
||||||
|
[editor]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Show indicators only for the shapes that are currently being rendered (ie that are on screen)
|
||||||
|
const renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])
|
||||||
|
|
||||||
|
const { ShapeIndicator } = useEditorComponents()
|
||||||
|
if (!ShapeIndicator) return null
|
||||||
|
|
||||||
|
return renderingShapes.map(({ id }) => (
|
||||||
|
<ShapeIndicator key={id + '_indicator'} shapeId={id} hidden={!idsToDisplay.has(id)} />
|
||||||
|
))
|
||||||
|
})
|
|
@ -39,6 +39,7 @@ import {
|
||||||
DefaultShapeIndicatorErrorFallback,
|
DefaultShapeIndicatorErrorFallback,
|
||||||
TLShapeIndicatorErrorFallbackComponent,
|
TLShapeIndicatorErrorFallbackComponent,
|
||||||
} from '../components/default-components/DefaultShapeIndicatorErrorFallback'
|
} from '../components/default-components/DefaultShapeIndicatorErrorFallback'
|
||||||
|
import { DefaultShapeIndicators } from '../components/default-components/DefaultShapeIndicators'
|
||||||
import {
|
import {
|
||||||
DefaultSnapIndicator,
|
DefaultSnapIndicator,
|
||||||
TLSnapIndicatorProps,
|
TLSnapIndicatorProps,
|
||||||
|
@ -53,6 +54,7 @@ export interface TLEditorComponents {
|
||||||
SvgDefs?: ComponentType | null
|
SvgDefs?: ComponentType | null
|
||||||
Brush?: ComponentType<TLBrushProps> | null
|
Brush?: ComponentType<TLBrushProps> | null
|
||||||
ZoomBrush?: ComponentType<TLBrushProps> | null
|
ZoomBrush?: ComponentType<TLBrushProps> | null
|
||||||
|
ShapeIndicators?: ComponentType | null
|
||||||
ShapeIndicator?: ComponentType<TLShapeIndicatorProps> | null
|
ShapeIndicator?: ComponentType<TLShapeIndicatorProps> | null
|
||||||
Cursor?: ComponentType<TLCursorProps> | null
|
Cursor?: ComponentType<TLCursorProps> | null
|
||||||
Canvas?: ComponentType<TLCanvasComponentProps> | null
|
Canvas?: ComponentType<TLCanvasComponentProps> | null
|
||||||
|
@ -114,6 +116,7 @@ export function EditorComponentsProvider({
|
||||||
Spinner: DefaultSpinner,
|
Spinner: DefaultSpinner,
|
||||||
SelectionBackground: DefaultSelectionBackground,
|
SelectionBackground: DefaultSelectionBackground,
|
||||||
SelectionForeground: DefaultSelectionForeground,
|
SelectionForeground: DefaultSelectionForeground,
|
||||||
|
ShapeIndicators: DefaultShapeIndicators,
|
||||||
ShapeIndicator: DefaultShapeIndicator,
|
ShapeIndicator: DefaultShapeIndicator,
|
||||||
OnTheCanvas: null,
|
OnTheCanvas: null,
|
||||||
InFrontOfTheCanvas: null,
|
InFrontOfTheCanvas: null,
|
||||||
|
|
|
@ -1686,6 +1686,9 @@ export const TldrawSelectionBackground: ({ bounds, rotation }: TLSelectionBackgr
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const TldrawSelectionForeground: MemoExoticComponent<({ bounds, rotation, }: TLSelectionForegroundProps) => JSX_2.Element | null>;
|
export const TldrawSelectionForeground: MemoExoticComponent<({ bounds, rotation, }: TLSelectionForegroundProps) => JSX_2.Element | null>;
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
export function TldrawShapeIndicators(): JSX_2.Element | null;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const TldrawUi: React_3.NamedExoticComponent<TldrawUiProps>;
|
export const TldrawUi: React_3.NamedExoticComponent<TldrawUiProps>;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ export { TldrawHandles } from './lib/canvas/TldrawHandles'
|
||||||
export { TldrawScribble } from './lib/canvas/TldrawScribble'
|
export { TldrawScribble } from './lib/canvas/TldrawScribble'
|
||||||
export { TldrawSelectionBackground } from './lib/canvas/TldrawSelectionBackground'
|
export { TldrawSelectionBackground } from './lib/canvas/TldrawSelectionBackground'
|
||||||
export { TldrawSelectionForeground } from './lib/canvas/TldrawSelectionForeground'
|
export { TldrawSelectionForeground } from './lib/canvas/TldrawSelectionForeground'
|
||||||
|
export { TldrawShapeIndicators } from './lib/canvas/TldrawShapeIndicators'
|
||||||
export { defaultBindingUtils } from './lib/defaultBindingUtils'
|
export { defaultBindingUtils } from './lib/defaultBindingUtils'
|
||||||
export { type TLExternalContentProps } from './lib/defaultExternalContentHandlers'
|
export { type TLExternalContentProps } from './lib/defaultExternalContentHandlers'
|
||||||
export { defaultShapeTools } from './lib/defaultShapeTools'
|
export { defaultShapeTools } from './lib/defaultShapeTools'
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { TldrawHandles } from './canvas/TldrawHandles'
|
||||||
import { TldrawScribble } from './canvas/TldrawScribble'
|
import { TldrawScribble } from './canvas/TldrawScribble'
|
||||||
import { TldrawSelectionBackground } from './canvas/TldrawSelectionBackground'
|
import { TldrawSelectionBackground } from './canvas/TldrawSelectionBackground'
|
||||||
import { TldrawSelectionForeground } from './canvas/TldrawSelectionForeground'
|
import { TldrawSelectionForeground } from './canvas/TldrawSelectionForeground'
|
||||||
|
import { TldrawShapeIndicators } from './canvas/TldrawShapeIndicators'
|
||||||
import { defaultBindingUtils } from './defaultBindingUtils'
|
import { defaultBindingUtils } from './defaultBindingUtils'
|
||||||
import {
|
import {
|
||||||
TLExternalContentProps,
|
TLExternalContentProps,
|
||||||
|
@ -72,6 +73,7 @@ export function Tldraw(props: TldrawProps) {
|
||||||
const componentsWithDefault = useMemo(
|
const componentsWithDefault = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
Scribble: TldrawScribble,
|
Scribble: TldrawScribble,
|
||||||
|
ShapeIndicators: TldrawShapeIndicators,
|
||||||
CollaboratorScribble: TldrawScribble,
|
CollaboratorScribble: TldrawScribble,
|
||||||
SelectionForeground: TldrawSelectionForeground,
|
SelectionForeground: TldrawSelectionForeground,
|
||||||
SelectionBackground: TldrawSelectionBackground,
|
SelectionBackground: TldrawSelectionBackground,
|
||||||
|
|
26
packages/tldraw/src/lib/canvas/TldrawShapeIndicators.tsx
Normal file
26
packages/tldraw/src/lib/canvas/TldrawShapeIndicators.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { DefaultShapeIndicators, useEditor, useValue } from '@tldraw/editor'
|
||||||
|
|
||||||
|
/** @public @react */
|
||||||
|
export function TldrawShapeIndicators() {
|
||||||
|
const editor = useEditor()
|
||||||
|
|
||||||
|
const isInSelectState = useValue(
|
||||||
|
'is in a valid select state',
|
||||||
|
() => {
|
||||||
|
return editor.isInAny(
|
||||||
|
'select.idle',
|
||||||
|
'select.brushing',
|
||||||
|
'select.scribble_brushing',
|
||||||
|
'select.editing_shape',
|
||||||
|
'select.pointing_shape',
|
||||||
|
'select.pointing_selection',
|
||||||
|
'select.pointing_handle'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[editor]
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!isInSelectState) return null
|
||||||
|
|
||||||
|
return <DefaultShapeIndicators />
|
||||||
|
}
|
|
@ -47,13 +47,7 @@ const Dialog = ({ id, component: ModalContent, onClose }: TLUiDialog) => {
|
||||||
function _Dialogs() {
|
function _Dialogs() {
|
||||||
const { dialogs } = useDialogs()
|
const { dialogs } = useDialogs()
|
||||||
|
|
||||||
return (
|
return dialogs.map((dialog: TLUiDialog) => <Dialog key={dialog.id} {...dialog} />)
|
||||||
<>
|
|
||||||
{dialogs.map((dialog: TLUiDialog) => (
|
|
||||||
<Dialog key={dialog.id} {...dialog} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Dialogs = React.memo(_Dialogs)
|
export const Dialogs = React.memo(_Dialogs)
|
||||||
|
|
|
@ -83,13 +83,7 @@ function Toast({ toast }: { toast: TLUiToast }) {
|
||||||
function _Toasts() {
|
function _Toasts() {
|
||||||
const { toasts } = useToasts()
|
const { toasts } = useToasts()
|
||||||
|
|
||||||
return (
|
return toasts.map((toast) => <Toast key={toast.id} toast={toast} />)
|
||||||
<>
|
|
||||||
{toasts.map((toast) => (
|
|
||||||
<Toast key={toast.id} toast={toast} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Toasts = React.memo(_Toasts)
|
export const Toasts = React.memo(_Toasts)
|
||||||
|
|
Loading…
Reference in a new issue