Roundup fixes (#2862)
This one is a roundup of superficial changes, apologies for having them in a single PR. This PR: - does some chair re-arranging for one of our hotter paths related to updating shapes - changes our type exports for editor components - adds shape indicator to editor components - moves canvas to be an editor component - fixes a CSS bug with hinted buttons - fixes CSS bugs with the menus - fixes bad imports in examples ### Change Type - [x] `major`
This commit is contained in:
parent
b3d6af4454
commit
9fc5f4459f
57 changed files with 2191 additions and 1825 deletions
|
@ -8,6 +8,7 @@ import {
|
|||
TldrawUiButton,
|
||||
TldrawUiButtonLabel,
|
||||
useEditor,
|
||||
useRelevantStyles,
|
||||
} from '@tldraw/tldraw'
|
||||
import '@tldraw/tldraw/tldraw.css'
|
||||
|
||||
|
@ -16,6 +17,8 @@ function CustomStylePanel(props: TLUiStylePanelProps) {
|
|||
|
||||
// Styles are complex, sorry. Check our DefaultStylePanel for an example.
|
||||
|
||||
const styles = useRelevantStyles()
|
||||
|
||||
return (
|
||||
<DefaultStylePanel {...props}>
|
||||
<TldrawUiButton
|
||||
|
@ -34,7 +37,7 @@ function CustomStylePanel(props: TLUiStylePanelProps) {
|
|||
>
|
||||
<TldrawUiButtonLabel>Green</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<DefaultStylePanelContent relevantStyles={props.relevantStyles} />
|
||||
<DefaultStylePanelContent styles={styles} />
|
||||
</DefaultStylePanel>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { TLComponents, Tldraw } from '@tldraw/tldraw'
|
||||
import { DefaultToolbar } from '@tldraw/tldraw/src/lib/ui/components/Toolbar/DefaultToolbar'
|
||||
import { DefaultToolbar, TLComponents, Tldraw } from '@tldraw/tldraw'
|
||||
import '@tldraw/tldraw/tldraw.css'
|
||||
|
||||
function CustomToolbar() {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {
|
||||
Canvas,
|
||||
ContextMenu,
|
||||
DefaultContextMenuContent,
|
||||
TldrawEditor,
|
||||
|
@ -39,7 +38,7 @@ export default function ExplodedExample() {
|
|||
persistenceKey="exploded-example"
|
||||
>
|
||||
<TldrawUi>
|
||||
<ContextMenu canvas={<Canvas />}>
|
||||
<ContextMenu>
|
||||
<DefaultContextMenuContent />
|
||||
</ContextMenu>
|
||||
</TldrawUi>
|
||||
|
|
|
@ -21,7 +21,7 @@ setDefaultEditorAssetUrls(assetUrls)
|
|||
setDefaultUiAssetUrls(assetUrls)
|
||||
const gettingStartedExamples = examples.find((e) => e.id === 'Getting Started')
|
||||
if (!gettingStartedExamples) throw new Error('Could not find getting started exmaples')
|
||||
const basicExample = gettingStartedExamples.value.find((e) => e.priority === 1)
|
||||
const basicExample = gettingStartedExamples.value.find((e) => e.title === 'Persistence key')
|
||||
if (!basicExample) throw new Error('Could not find initial example')
|
||||
|
||||
const router = createBrowserRouter([
|
||||
|
|
|
@ -306,11 +306,6 @@ export const CAMERA_SLIDE_FRICTION = 0.09;
|
|||
// @public (undocumented)
|
||||
export function canonicalizeRotation(a: number): number;
|
||||
|
||||
// @public (undocumented)
|
||||
export function Canvas({ className }: {
|
||||
className?: string;
|
||||
}): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export class Circle2d extends Geometry2d {
|
||||
constructor(config: Omit<Geometry2dOptions, 'isClosed'> & {
|
||||
|
@ -449,50 +444,49 @@ export const DEFAULT_ANIMATION_OPTIONS: {
|
|||
export function DefaultBackground(): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultBrush: TLBrushComponent;
|
||||
export const DefaultBrush: ({ brush, color, opacity, className }: TLBrushProps) => JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultCollaboratorHint: TLCollaboratorHintComponent;
|
||||
export function DefaultCanvas({ className }: TLCanvasComponentProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultCursor: NamedExoticComponent< {
|
||||
className?: string | undefined;
|
||||
point: null | VecModel;
|
||||
zoom: number;
|
||||
color?: string | undefined;
|
||||
name: null | string;
|
||||
chatMessage: string;
|
||||
}>;
|
||||
export function DefaultCollaboratorHint({ className, zoom, point, color, viewport, opacity, }: TLCollaboratorHintProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultCursor: NamedExoticComponent<TLCursorProps>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultErrorFallback: TLErrorFallbackComponent;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultGrid: TLGridComponent;
|
||||
export function DefaultGrid({ x, y, z, size }: TLGridProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultHandle: TLHandleComponent;
|
||||
export function DefaultHandle({ handle, isCoarse, className, zoom }: TLHandleProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultHandles: TLHandlesComponent;
|
||||
export const DefaultHandles: ({ children }: TLHandlesProps) => JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultHoveredShapeIndicator: TLHoveredShapeIndicatorComponent;
|
||||
export function DefaultHoveredShapeIndicator({ shapeId }: TLHoveredShapeIndicatorProps): JSX_2.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultScribble: TLScribbleComponent;
|
||||
export function DefaultScribble({ scribble, zoom, color, opacity, className }: TLScribbleProps): JSX_2.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultSelectionBackground: TLSelectionBackgroundComponent;
|
||||
export function DefaultSelectionBackground({ bounds, rotation }: TLSelectionBackgroundProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultSelectionForeground: TLSelectionForegroundComponent;
|
||||
export function DefaultSelectionForeground({ bounds, rotation }: TLSelectionForegroundProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultSnapIndicator: TLSnapIndicatorComponent;
|
||||
export const DefaultShapeIndicator: NamedExoticComponent<TLShapeIndicatorProps>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultSpinner: TLSpinnerComponent;
|
||||
export function DefaultSnapIndicator({ className, line, zoom }: TLSnapIndicatorProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DefaultSpinner(): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultSvgDefs: () => null;
|
||||
|
@ -1587,14 +1581,6 @@ export function setRuntimeOverrides(input: Partial<typeof runtime>): void;
|
|||
// @public (undocumented)
|
||||
export function setUserPreferences(user: TLUserPreferences): void;
|
||||
|
||||
// @public (undocumented)
|
||||
export const ShapeIndicator: React_3.NamedExoticComponent<{
|
||||
id: TLShapeId;
|
||||
color?: string | undefined;
|
||||
opacity?: number | undefined;
|
||||
className?: string | undefined;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
||||
constructor(editor: Editor);
|
||||
|
@ -1898,9 +1884,6 @@ export type TLArrowPoint = {
|
|||
arrowhead: TLArrowShapeArrowheadStyle;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLBackgroundComponent = ComponentType;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLBaseBoxShape = TLBaseShape<string, {
|
||||
w: number;
|
||||
|
@ -1932,12 +1915,12 @@ export type TLBeforeCreateHandler<R extends TLRecord> = (record: R, source: 'rem
|
|||
export type TLBeforeDeleteHandler<R extends TLRecord> = (record: R, source: 'remote' | 'user') => false | void;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLBrushComponent = ComponentType<{
|
||||
export type TLBrushProps = {
|
||||
brush: BoxModel;
|
||||
color?: string;
|
||||
opacity?: number;
|
||||
className?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLCancelEvent = (info: TLCancelEventInfo) => void;
|
||||
|
@ -1965,14 +1948,14 @@ export type TLClickEventInfo = TLBaseEventInfo & {
|
|||
export type TLCLickEventName = 'double_click' | 'quadruple_click' | 'triple_click';
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLCollaboratorHintComponent = ComponentType<{
|
||||
export type TLCollaboratorHintProps = {
|
||||
className?: string;
|
||||
point: VecModel;
|
||||
viewport: Box;
|
||||
zoom: number;
|
||||
opacity?: number;
|
||||
color: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLCommand<Name extends string = any, Data = any> = {
|
||||
|
@ -2020,14 +2003,14 @@ export interface TLContent {
|
|||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLCursorComponent = ComponentType<{
|
||||
export type TLCursorProps = {
|
||||
className?: string;
|
||||
point: null | VecModel;
|
||||
zoom: number;
|
||||
color?: string;
|
||||
name: null | string;
|
||||
chatMessage: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawEditor: React_2.NamedExoticComponent<TldrawEditorProps>;
|
||||
|
@ -2224,27 +2207,26 @@ export type TLExternalContentSource = {
|
|||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLGridComponent = ComponentType<{
|
||||
export type TLGridProps = {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
size: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLHandleComponent = ComponentType<{
|
||||
export type TLHandleProps = {
|
||||
shapeId: TLShapeId;
|
||||
handle: TLHandle;
|
||||
zoom: number;
|
||||
isCoarse: boolean;
|
||||
className?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLHandlesComponent = ComponentType<{
|
||||
className?: string;
|
||||
export type TLHandlesProps = {
|
||||
children: any;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLHistoryEntry = TLCommand | TLHistoryMark;
|
||||
|
@ -2258,12 +2240,9 @@ export type TLHistoryMark = {
|
|||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLHoveredShapeIndicatorComponent = ComponentType<{
|
||||
export type TLHoveredShapeIndicatorProps = {
|
||||
shapeId: TLShapeId;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLInFrontOfTheCanvas = ComponentType<object>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLInterruptEvent = (info: TLInterruptEventInfo) => void;
|
||||
|
@ -2343,9 +2322,6 @@ export type TLOnRotateHandler<T extends TLShape> = TLEventChangeHandler<T>;
|
|||
// @public (undocumented)
|
||||
export type TLOnRotateStartHandler<T extends TLShape> = TLEventStartHandler<T>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLOnTheCanvas = ComponentType<object>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLOnTranslateEndHandler<T extends TLShape> = TLEventChangeHandler<T>;
|
||||
|
||||
|
@ -2442,25 +2418,25 @@ export type TLRotationSnapshot = {
|
|||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLScribbleComponent = ComponentType<{
|
||||
export type TLScribbleProps = {
|
||||
scribble: TLScribble;
|
||||
zoom: number;
|
||||
color?: string;
|
||||
opacity?: number;
|
||||
className?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLSelectionBackgroundComponent = React_3.ComponentType<{
|
||||
export type TLSelectionBackgroundProps = {
|
||||
bounds: Box;
|
||||
rotation: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLSelectionForegroundComponent = ComponentType<{
|
||||
export type TLSelectionForegroundProps = {
|
||||
bounds: Box;
|
||||
rotation: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLSelectionHandle = RotateCorner | SelectionCorner | SelectionEdge;
|
||||
|
@ -2495,12 +2471,12 @@ export interface TLSessionStateSnapshot {
|
|||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLShapeIndicatorComponent = React_3.ComponentType<{
|
||||
id: TLShapeId;
|
||||
export type TLShapeIndicatorProps = {
|
||||
shapeId: TLShapeId;
|
||||
color?: string | undefined;
|
||||
opacity?: number;
|
||||
className?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLShapeUtilCanvasSvgDef {
|
||||
|
@ -2526,14 +2502,11 @@ export interface TLShapeUtilConstructor<T extends TLUnknownShape, U extends Shap
|
|||
export type TLShapeUtilFlag<T> = (shape: T) => boolean;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLSnapIndicatorComponent = React_3.ComponentType<{
|
||||
export type TLSnapIndicatorProps = {
|
||||
className?: string;
|
||||
line: SnapIndicator;
|
||||
zoom: number;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLSpinnerComponent = ComponentType<object>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLStateNodeConstructor {
|
||||
|
@ -2584,9 +2557,6 @@ export type TLStoreWithStatus = {
|
|||
readonly error?: undefined;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLSvgDefsComponent = React.ComponentType;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLSvgOptions = {
|
||||
bounds: Box;
|
||||
|
@ -2672,6 +2642,34 @@ export function useContainer(): HTMLDivElement;
|
|||
// @public (undocumented)
|
||||
export function useEditor(): Editor;
|
||||
|
||||
// @public (undocumented)
|
||||
export function useEditorComponents(): Partial<{
|
||||
Background: ComponentType | null;
|
||||
SvgDefs: ComponentType | null;
|
||||
Brush: ComponentType<TLBrushProps> | null;
|
||||
ZoomBrush: ComponentType<TLBrushProps> | null;
|
||||
ShapeIndicator: ComponentType<TLShapeIndicatorProps> | null;
|
||||
Cursor: ComponentType<TLCursorProps> | null;
|
||||
Canvas: ComponentType<TLCanvasComponentProps> | null;
|
||||
CollaboratorBrush: ComponentType<TLBrushProps> | null;
|
||||
CollaboratorCursor: ComponentType<TLCursorProps> | null;
|
||||
CollaboratorHint: ComponentType<TLCollaboratorHintProps> | null;
|
||||
CollaboratorShapeIndicator: ComponentType<TLShapeIndicatorProps> | null;
|
||||
Grid: ComponentType<TLGridProps> | null;
|
||||
Scribble: ComponentType<TLScribbleProps> | null;
|
||||
CollaboratorScribble: ComponentType<TLScribbleProps> | null;
|
||||
SnapIndicator: ComponentType<TLSnapIndicatorProps> | null;
|
||||
Handles: ComponentType<TLHandlesProps> | null;
|
||||
Handle: ComponentType<TLHandleProps> | null;
|
||||
Spinner: ComponentType | null;
|
||||
SelectionForeground: ComponentType<TLSelectionForegroundProps> | null;
|
||||
SelectionBackground: ComponentType<TLSelectionBackgroundProps> | null;
|
||||
HoveredShapeIndicator: ComponentType<TLHoveredShapeIndicatorProps> | null;
|
||||
OnTheCanvas: ComponentType | null;
|
||||
InFrontOfTheCanvas: ComponentType | null;
|
||||
LoadingScreen: ComponentType | null;
|
||||
} & ErrorComponents> & ErrorComponents;
|
||||
|
||||
// @public (undocumented)
|
||||
export function useIsCropping(shapeId: TLShapeId): boolean;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -34,7 +34,6 @@ export {
|
|||
type TldrawEditorBaseProps,
|
||||
type TldrawEditorProps,
|
||||
} from './lib/TldrawEditor'
|
||||
export { Canvas } from './lib/components/Canvas'
|
||||
export {
|
||||
ErrorBoundary,
|
||||
OptionalErrorBoundary,
|
||||
|
@ -42,63 +41,53 @@ export {
|
|||
} from './lib/components/ErrorBoundary'
|
||||
export { HTMLContainer, type HTMLContainerProps } from './lib/components/HTMLContainer'
|
||||
export { SVGContainer, type SVGContainerProps } from './lib/components/SVGContainer'
|
||||
export { ShapeIndicator, type TLShapeIndicatorComponent } from './lib/components/ShapeIndicator'
|
||||
export {
|
||||
DefaultBackground,
|
||||
type TLBackgroundComponent,
|
||||
} from './lib/components/default-components/DefaultBackground'
|
||||
export {
|
||||
DefaultBrush,
|
||||
type TLBrushComponent,
|
||||
} from './lib/components/default-components/DefaultBrush'
|
||||
export { DefaultBackground } from './lib/components/default-components/DefaultBackground'
|
||||
export { DefaultBrush, type TLBrushProps } from './lib/components/default-components/DefaultBrush'
|
||||
export { DefaultCanvas } from './lib/components/default-components/DefaultCanvas'
|
||||
export {
|
||||
DefaultCollaboratorHint,
|
||||
type TLCollaboratorHintComponent,
|
||||
type TLCollaboratorHintProps,
|
||||
} from './lib/components/default-components/DefaultCollaboratorHint'
|
||||
export {
|
||||
DefaultCursor,
|
||||
type TLCursorComponent,
|
||||
type TLCursorProps,
|
||||
} from './lib/components/default-components/DefaultCursor'
|
||||
export { DefaultErrorFallback } from './lib/components/default-components/DefaultErrorFallback'
|
||||
export { DefaultGrid, type TLGridComponent } from './lib/components/default-components/DefaultGrid'
|
||||
export { DefaultGrid, type TLGridProps } from './lib/components/default-components/DefaultGrid'
|
||||
export {
|
||||
DefaultHandle,
|
||||
type TLHandleComponent,
|
||||
type TLHandleProps,
|
||||
} from './lib/components/default-components/DefaultHandle'
|
||||
export {
|
||||
DefaultHandles,
|
||||
type TLHandlesComponent,
|
||||
type TLHandlesProps,
|
||||
} from './lib/components/default-components/DefaultHandles'
|
||||
export {
|
||||
DefaultHoveredShapeIndicator,
|
||||
type TLHoveredShapeIndicatorComponent,
|
||||
type TLHoveredShapeIndicatorProps,
|
||||
} from './lib/components/default-components/DefaultHoveredShapeIndicator'
|
||||
export { type TLInFrontOfTheCanvas } from './lib/components/default-components/DefaultInFrontOfTheCanvas'
|
||||
export { type TLOnTheCanvas } from './lib/components/default-components/DefaultOnTheCanvas'
|
||||
export {
|
||||
DefaultScribble,
|
||||
type TLScribbleComponent,
|
||||
type TLScribbleProps,
|
||||
} from './lib/components/default-components/DefaultScribble'
|
||||
export {
|
||||
DefaultSelectionBackground,
|
||||
type TLSelectionBackgroundComponent,
|
||||
type TLSelectionBackgroundProps,
|
||||
} from './lib/components/default-components/DefaultSelectionBackground'
|
||||
export {
|
||||
DefaultSelectionForeground,
|
||||
type TLSelectionForegroundComponent,
|
||||
type TLSelectionForegroundProps,
|
||||
} from './lib/components/default-components/DefaultSelectionForeground'
|
||||
export {
|
||||
DefaultShapeIndicator,
|
||||
type TLShapeIndicatorProps,
|
||||
} from './lib/components/default-components/DefaultShapeIndicator'
|
||||
export {
|
||||
DefaultSnapIndicator,
|
||||
type TLSnapIndicatorComponent,
|
||||
type TLSnapIndicatorProps,
|
||||
} from './lib/components/default-components/DefaultSnapIndictor'
|
||||
export {
|
||||
DefaultSpinner,
|
||||
type TLSpinnerComponent,
|
||||
} from './lib/components/default-components/DefaultSpinner'
|
||||
export {
|
||||
DefaultSvgDefs,
|
||||
type TLSvgDefsComponent,
|
||||
} from './lib/components/default-components/DefaultSvgDefs'
|
||||
export { DefaultSpinner } from './lib/components/default-components/DefaultSpinner'
|
||||
export { DefaultSvgDefs } from './lib/components/default-components/DefaultSvgDefs'
|
||||
export {
|
||||
TAB_ID,
|
||||
createSessionStateSnapshotSignal,
|
||||
|
@ -256,6 +245,7 @@ export { type TLResizeHandle, type TLSelectionHandle } from './lib/editor/types/
|
|||
export { ContainerProvider, useContainer } from './lib/hooks/useContainer'
|
||||
export { getCursor } from './lib/hooks/useCursor'
|
||||
export { EditorContext, useEditor } from './lib/hooks/useEditor'
|
||||
export { useEditorComponents } from './lib/hooks/useEditorComponents'
|
||||
export type { TLEditorComponents } from './lib/hooks/useEditorComponents'
|
||||
export { useShallowArrayIdentity, useShallowObjectIdentity } from './lib/hooks/useIdentity'
|
||||
export { useIsCropping } from './lib/hooks/useIsCropping'
|
||||
|
|
|
@ -11,7 +11,6 @@ import React, {
|
|||
} from 'react'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { Canvas } from './components/Canvas'
|
||||
import { OptionalErrorBoundary } from './components/ErrorBoundary'
|
||||
import { DefaultErrorFallback } from './components/default-components/DefaultErrorFallback'
|
||||
import { DefaultLoadingScreen } from './components/default-components/DefaultLoadingScreen'
|
||||
|
@ -309,6 +308,8 @@ function TldrawEditorWithReadyStore({
|
|||
() => editor?.getCrashingError() ?? null
|
||||
)
|
||||
|
||||
const { Canvas } = useEditorComponents()
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
|
@ -331,7 +332,7 @@ function TldrawEditorWithReadyStore({
|
|||
) : (
|
||||
<EditorContext.Provider value={editor}>
|
||||
<Layout autoFocus={autoFocus} onMount={onMount}>
|
||||
{children}
|
||||
{children ?? (Canvas ? <Canvas /> : null)}
|
||||
</Layout>
|
||||
</EditorContext.Provider>
|
||||
)}
|
||||
|
@ -356,7 +357,7 @@ function Layout({
|
|||
useFocusEvents(autoFocus)
|
||||
useOnMount(onMount)
|
||||
|
||||
return children ?? <Canvas />
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
function Crash({ crashingError }: { crashingError: unknown }): null {
|
||||
|
|
|
@ -144,7 +144,7 @@ const Collaborator = track(function Collaborator({
|
|||
<CollaboratorShapeIndicator
|
||||
className="tl-collaborator__shape-indicator"
|
||||
key={userId + '_' + shapeId}
|
||||
id={shapeId}
|
||||
shapeId={shapeId}
|
||||
color={color}
|
||||
opacity={0.5}
|
||||
/>
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
import { ComponentType } from 'react'
|
||||
|
||||
/** @public */
|
||||
export type TLBackgroundComponent = ComponentType
|
||||
|
||||
/** @public */
|
||||
export function DefaultBackground() {
|
||||
return <div className="tl-background" />
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import { BoxModel } from '@tldraw/tlschema'
|
||||
import { ComponentType, useRef } from 'react'
|
||||
import { useRef } from 'react'
|
||||
import { useTransform } from '../../hooks/useTransform'
|
||||
import { toDomPrecision } from '../../primitives/utils'
|
||||
|
||||
/** @public */
|
||||
export type TLBrushComponent = ComponentType<{
|
||||
export type TLBrushProps = {
|
||||
brush: BoxModel
|
||||
color?: string
|
||||
opacity?: number
|
||||
className?: string
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultBrush: TLBrushComponent = ({ brush, color, opacity, className }) => {
|
||||
export const DefaultBrush = ({ brush, color, opacity, className }: TLBrushProps) => {
|
||||
const rSvg = useRef<SVGSVGElement>(null)
|
||||
useTransform(rSvg, brush.x, brush.y)
|
||||
|
||||
|
|
|
@ -3,27 +3,29 @@ import { TLHandle, TLShapeId } from '@tldraw/tlschema'
|
|||
import { dedupe, modulate, objectMapValues } from '@tldraw/utils'
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
import { COARSE_HANDLE_RADIUS, HANDLE_RADIUS } from '../constants'
|
||||
import { useCanvasEvents } from '../hooks/useCanvasEvents'
|
||||
import { useCoarsePointer } from '../hooks/useCoarsePointer'
|
||||
import { useDocumentEvents } from '../hooks/useDocumentEvents'
|
||||
import { useEditor } from '../hooks/useEditor'
|
||||
import { useEditorComponents } from '../hooks/useEditorComponents'
|
||||
import { useFixSafariDoubleTapZoomPencilEvents } from '../hooks/useFixSafariDoubleTapZoomPencilEvents'
|
||||
import { useGestureEvents } from '../hooks/useGestureEvents'
|
||||
import { useHandleEvents } from '../hooks/useHandleEvents'
|
||||
import { useScreenBounds } from '../hooks/useScreenBounds'
|
||||
import { Mat } from '../primitives/Mat'
|
||||
import { Vec } from '../primitives/Vec'
|
||||
import { toDomPrecision } from '../primitives/utils'
|
||||
import { debugFlags } from '../utils/debug-flags'
|
||||
import { GeometryDebuggingView } from './GeometryDebuggingView'
|
||||
import { LiveCollaborators } from './LiveCollaborators'
|
||||
import { Shape } from './Shape'
|
||||
import { ShapeIndicator } from './ShapeIndicator'
|
||||
import { COARSE_HANDLE_RADIUS, HANDLE_RADIUS } from '../../constants'
|
||||
import { useCanvasEvents } from '../../hooks/useCanvasEvents'
|
||||
import { useCoarsePointer } from '../../hooks/useCoarsePointer'
|
||||
import { useDocumentEvents } from '../../hooks/useDocumentEvents'
|
||||
import { useEditor } from '../../hooks/useEditor'
|
||||
import { useEditorComponents } from '../../hooks/useEditorComponents'
|
||||
import { useFixSafariDoubleTapZoomPencilEvents } from '../../hooks/useFixSafariDoubleTapZoomPencilEvents'
|
||||
import { useGestureEvents } from '../../hooks/useGestureEvents'
|
||||
import { useHandleEvents } from '../../hooks/useHandleEvents'
|
||||
import { useScreenBounds } from '../../hooks/useScreenBounds'
|
||||
import { Mat } from '../../primitives/Mat'
|
||||
import { Vec } from '../../primitives/Vec'
|
||||
import { toDomPrecision } from '../../primitives/utils'
|
||||
import { debugFlags } from '../../utils/debug-flags'
|
||||
import { GeometryDebuggingView } from '../GeometryDebuggingView'
|
||||
import { LiveCollaborators } from '../LiveCollaborators'
|
||||
import { Shape } from '../Shape'
|
||||
|
||||
/** @public */
|
||||
export function Canvas({ className }: { className?: string }) {
|
||||
export type TLCanvasComponentProps = { className?: string }
|
||||
|
||||
/** @public */
|
||||
export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
||||
const editor = useEditor()
|
||||
|
||||
const { Background, SvgDefs } = useEditorComponents()
|
||||
|
@ -378,12 +380,19 @@ function SelectedIdIndicators() {
|
|||
[editor]
|
||||
)
|
||||
|
||||
const { ShapeIndicator } = useEditorComponents()
|
||||
|
||||
if (!ShapeIndicator) return null
|
||||
if (!shouldDisplay) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedShapeIds.map((id) => (
|
||||
<ShapeIndicator key={id + '_indicator'} className="tl-user-indicator__selected" id={id} />
|
||||
<ShapeIndicator
|
||||
key={id + '_indicator'}
|
||||
className="tl-user-indicator__selected"
|
||||
shapeId={id}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
|
@ -413,15 +422,17 @@ const HoveredShapeIndicator = function HoveredShapeIndicator() {
|
|||
|
||||
const HintedShapeIndicator = track(function HintedShapeIndicator() {
|
||||
const editor = useEditor()
|
||||
const { ShapeIndicator } = useEditorComponents()
|
||||
|
||||
const ids = dedupe(editor.getHintingShapeIds())
|
||||
|
||||
if (!ids.length) return null
|
||||
if (!ShapeIndicator) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
{ids.map((id) => (
|
||||
<ShapeIndicator className="tl-user-indicator__hint" id={id} key={id + '_hinting'} />
|
||||
<ShapeIndicator className="tl-user-indicator__hint" shapeId={id} key={id + '_hinting'} />
|
||||
))}
|
||||
</>
|
||||
)
|
|
@ -1,30 +1,30 @@
|
|||
import { VecModel } from '@tldraw/tlschema'
|
||||
import classNames from 'classnames'
|
||||
import { ComponentType, useRef } from 'react'
|
||||
import { useRef } from 'react'
|
||||
import { useTransform } from '../../hooks/useTransform'
|
||||
import { Box } from '../../primitives/Box'
|
||||
import { Vec } from '../../primitives/Vec'
|
||||
import { clamp } from '../../primitives/utils'
|
||||
|
||||
/** @public */
|
||||
export type TLCollaboratorHintComponent = ComponentType<{
|
||||
export type TLCollaboratorHintProps = {
|
||||
className?: string
|
||||
point: VecModel
|
||||
viewport: Box
|
||||
zoom: number
|
||||
opacity?: number
|
||||
color: string
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultCollaboratorHint: TLCollaboratorHintComponent = ({
|
||||
export function DefaultCollaboratorHint({
|
||||
className,
|
||||
zoom,
|
||||
point,
|
||||
color,
|
||||
viewport,
|
||||
opacity = 1,
|
||||
}) => {
|
||||
}: TLCollaboratorHintProps) {
|
||||
const rSvg = useRef<SVGSVGElement>(null)
|
||||
|
||||
useTransform(
|
||||
|
|
|
@ -1,19 +1,27 @@
|
|||
import { VecModel } from '@tldraw/tlschema'
|
||||
import classNames from 'classnames'
|
||||
import { ComponentType, memo, useRef } from 'react'
|
||||
import { memo, useRef } from 'react'
|
||||
import { useTransform } from '../../hooks/useTransform'
|
||||
|
||||
/** @public */
|
||||
export type TLCursorComponent = ComponentType<{
|
||||
export type TLCursorProps = {
|
||||
className?: string
|
||||
point: VecModel | null
|
||||
zoom: number
|
||||
color?: string
|
||||
name: string | null
|
||||
chatMessage: string
|
||||
}>
|
||||
}
|
||||
|
||||
const _Cursor: TLCursorComponent = ({ className, zoom, point, color, name, chatMessage }) => {
|
||||
/** @public */
|
||||
export const DefaultCursor = memo(function DefaultCursor({
|
||||
className,
|
||||
zoom,
|
||||
point,
|
||||
color,
|
||||
name,
|
||||
chatMessage,
|
||||
}: TLCursorProps) {
|
||||
const rCursor = useRef<HTMLDivElement>(null)
|
||||
useTransform(rCursor, point?.x, point?.y, 1 / zoom)
|
||||
|
||||
|
@ -44,7 +52,4 @@ const _Cursor: TLCursorComponent = ({ className, zoom, point, color, name, chatM
|
|||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultCursor = memo(_Cursor)
|
||||
})
|
||||
|
|
|
@ -3,9 +3,9 @@ import classNames from 'classnames'
|
|||
import { ComponentType, useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { Editor } from '../../editor/Editor'
|
||||
import { EditorContext } from '../../hooks/useEditor'
|
||||
import { useEditorComponents } from '../../hooks/useEditorComponents'
|
||||
import { hardResetEditor } from '../../utils/hardResetEditor'
|
||||
import { refreshPage } from '../../utils/refreshPage'
|
||||
import { Canvas } from '../Canvas'
|
||||
import { ErrorBoundary } from '../ErrorBoundary'
|
||||
|
||||
const BASE_ERROR_URL = 'https://github.com/tldraw/tldraw/issues/new'
|
||||
|
@ -23,6 +23,8 @@ export const DefaultErrorFallback: TLErrorFallbackComponent = ({ error, editor }
|
|||
const [didCopy, setDidCopy] = useState(false)
|
||||
const [shouldShowResetConfirmation, setShouldShowResetConfirmation] = useState(false)
|
||||
|
||||
const { Canvas } = useEditorComponents()
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
const errorStack = error instanceof Error ? error.stack : null
|
||||
|
||||
|
@ -135,9 +137,7 @@ My browser: ${navigator.userAgent}`
|
|||
// a plain grey background.
|
||||
<ErrorBoundary onError={noop} fallback={() => null}>
|
||||
<EditorContext.Provider value={editor}>
|
||||
<div className="tl-overlay tl-error-boundary__canvas">
|
||||
<Canvas />
|
||||
</div>
|
||||
<div className="tl-overlay tl-error-boundary__canvas">{Canvas ? <Canvas /> : null}</div>
|
||||
</EditorContext.Provider>
|
||||
</ErrorBoundary>
|
||||
)}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import { modulate } from '@tldraw/utils'
|
||||
import { ComponentType } from 'react'
|
||||
import { GRID_STEPS } from '../../constants'
|
||||
|
||||
/** @public */
|
||||
export type TLGridComponent = ComponentType<{
|
||||
export type TLGridProps = {
|
||||
x: number
|
||||
y: number
|
||||
z: number
|
||||
size: number
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultGrid: TLGridComponent = ({ x, y, z, size }) => {
|
||||
export function DefaultGrid({ x, y, z, size }: TLGridProps) {
|
||||
return (
|
||||
<svg className="tl-grid" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
import { TLHandle, TLShapeId } from '@tldraw/tlschema'
|
||||
import classNames from 'classnames'
|
||||
import { ComponentType } from 'react'
|
||||
import { COARSE_HANDLE_RADIUS, HANDLE_RADIUS } from '../../constants'
|
||||
|
||||
/** @public */
|
||||
export type TLHandleComponent = ComponentType<{
|
||||
export type TLHandleProps = {
|
||||
shapeId: TLShapeId
|
||||
handle: TLHandle
|
||||
zoom: number
|
||||
isCoarse: boolean
|
||||
className?: string
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultHandle: TLHandleComponent = ({ handle, isCoarse, className, zoom }) => {
|
||||
export function DefaultHandle({ handle, isCoarse, className, zoom }: TLHandleProps) {
|
||||
const bgRadius = (isCoarse ? COARSE_HANDLE_RADIUS : HANDLE_RADIUS) / zoom
|
||||
const fgRadius = (handle.type === 'create' && isCoarse ? 3 : 4) / zoom
|
||||
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
import { ComponentType } from 'react'
|
||||
|
||||
/** @public */
|
||||
export type TLHandlesComponent = ComponentType<{
|
||||
className?: string
|
||||
export type TLHandlesProps = {
|
||||
children: any
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultHandles: TLHandlesComponent = ({ children }) => {
|
||||
export const DefaultHandles = ({ children }: TLHandlesProps) => {
|
||||
return <svg className="tl-user-handles tl-overlays__item">{children}</svg>
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { TLShapeId } from '@tldraw/tlschema'
|
||||
import { ComponentType } from 'react'
|
||||
import { ShapeIndicator } from '../ShapeIndicator'
|
||||
import { useEditorComponents } from '../../hooks/useEditorComponents'
|
||||
|
||||
/** @public */
|
||||
export type TLHoveredShapeIndicatorComponent = ComponentType<{
|
||||
export type TLHoveredShapeIndicatorProps = {
|
||||
shapeId: TLShapeId
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultHoveredShapeIndicator: TLHoveredShapeIndicatorComponent = ({ shapeId }) => {
|
||||
return <ShapeIndicator className="tl-user-indicator__hovered" id={shapeId} />
|
||||
export function DefaultHoveredShapeIndicator({ shapeId }: TLHoveredShapeIndicatorProps) {
|
||||
const { ShapeIndicator } = useEditorComponents()
|
||||
if (!ShapeIndicator) return null
|
||||
return <ShapeIndicator className="tl-user-indicator__hovered" shapeId={shapeId} />
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
import { ComponentType } from 'react'
|
||||
|
||||
/** @public */
|
||||
export type TLInFrontOfTheCanvas = ComponentType<object>
|
|
@ -1,10 +1,6 @@
|
|||
import { ComponentType } from 'react'
|
||||
import { LoadingScreen } from '../../TldrawEditor'
|
||||
|
||||
/** @public */
|
||||
export type TLLoadingScreenComponent = ComponentType<object>
|
||||
|
||||
/** @public */
|
||||
export const DefaultLoadingScreen: TLLoadingScreenComponent = () => {
|
||||
export const DefaultLoadingScreen = () => {
|
||||
return <LoadingScreen>Connecting...</LoadingScreen>
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
import { ComponentType } from 'react'
|
||||
|
||||
/** @public */
|
||||
export type TLOnTheCanvas = ComponentType<object>
|
|
@ -1,25 +1,18 @@
|
|||
import { TLScribble } from '@tldraw/tlschema'
|
||||
import classNames from 'classnames'
|
||||
import { ComponentType } from 'react'
|
||||
import { getSvgPathFromPoints } from '../../utils/getSvgPathFromPoints'
|
||||
|
||||
/** @public */
|
||||
export type TLScribbleComponent = ComponentType<{
|
||||
export type TLScribbleProps = {
|
||||
scribble: TLScribble
|
||||
zoom: number
|
||||
color?: string
|
||||
opacity?: number
|
||||
className?: string
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultScribble: TLScribbleComponent = ({
|
||||
scribble,
|
||||
zoom,
|
||||
color,
|
||||
opacity,
|
||||
className,
|
||||
}) => {
|
||||
export function DefaultScribble({ scribble, zoom, color, opacity, className }: TLScribbleProps) {
|
||||
if (!scribble.points.length) return null
|
||||
|
||||
return (
|
||||
|
|
|
@ -4,16 +4,13 @@ import { Box } from '../../primitives/Box'
|
|||
import { toDomPrecision } from '../../primitives/utils'
|
||||
|
||||
/** @public */
|
||||
export type TLSelectionBackgroundComponent = React.ComponentType<{
|
||||
export type TLSelectionBackgroundProps = {
|
||||
bounds: Box
|
||||
rotation: number
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultSelectionBackground: TLSelectionBackgroundComponent = ({
|
||||
bounds,
|
||||
rotation,
|
||||
}) => {
|
||||
export function DefaultSelectionBackground({ bounds, rotation }: TLSelectionBackgroundProps) {
|
||||
const rDiv = React.useRef<HTMLDivElement>(null)
|
||||
useTransform(rDiv, bounds.x, bounds.y, 1, rotation)
|
||||
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
import { useValue } from '@tldraw/state'
|
||||
import classNames from 'classnames'
|
||||
import { ComponentType, useRef } from 'react'
|
||||
import { useRef } from 'react'
|
||||
import { useEditor } from '../../hooks/useEditor'
|
||||
import { useTransform } from '../../hooks/useTransform'
|
||||
import { Box } from '../../primitives/Box'
|
||||
import { toDomPrecision } from '../../primitives/utils'
|
||||
|
||||
/** @public */
|
||||
export type TLSelectionForegroundComponent = ComponentType<{
|
||||
export type TLSelectionForegroundProps = {
|
||||
bounds: Box
|
||||
rotation: number
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultSelectionForeground: TLSelectionForegroundComponent = ({
|
||||
bounds,
|
||||
rotation,
|
||||
}) => {
|
||||
export function DefaultSelectionForeground({ bounds, rotation }: TLSelectionForegroundProps) {
|
||||
const editor = useEditor()
|
||||
const rSvg = useRef<SVGSVGElement>(null)
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { useStateTracking, useValue } from '@tldraw/state'
|
||||
import { TLShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import classNames from 'classnames'
|
||||
import * as React from 'react'
|
||||
import type { Editor } from '../editor/Editor'
|
||||
import { ShapeUtil } from '../editor/shapes/ShapeUtil'
|
||||
import { useEditor } from '../hooks/useEditor'
|
||||
import { useEditorComponents } from '../hooks/useEditorComponents'
|
||||
import { OptionalErrorBoundary } from './ErrorBoundary'
|
||||
import { memo } from 'react'
|
||||
import type { Editor } from '../../editor/Editor'
|
||||
import { ShapeUtil } from '../../editor/shapes/ShapeUtil'
|
||||
import { useEditor } from '../../hooks/useEditor'
|
||||
import { useEditorComponents } from '../../hooks/useEditorComponents'
|
||||
import { OptionalErrorBoundary } from '../ErrorBoundary'
|
||||
|
||||
class ShapeWithPropsEquality {
|
||||
constructor(public shape: TLShape | undefined) {}
|
||||
|
@ -51,24 +51,30 @@ const InnerIndicator = ({ editor, id }: { editor: Editor; id: TLShapeId }) => {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export type TLShapeIndicatorComponent = React.ComponentType<{
|
||||
id: TLShapeId
|
||||
export type TLShapeIndicatorProps = {
|
||||
shapeId: TLShapeId
|
||||
color?: string | undefined
|
||||
opacity?: number
|
||||
className?: string
|
||||
}>
|
||||
}
|
||||
|
||||
const _ShapeIndicator: TLShapeIndicatorComponent = ({ id, className, color, opacity }) => {
|
||||
/** @public */
|
||||
export const DefaultShapeIndicator = memo(function DefaultShapeIndicator({
|
||||
shapeId,
|
||||
className,
|
||||
color,
|
||||
opacity,
|
||||
}: TLShapeIndicatorProps) {
|
||||
const editor = useEditor()
|
||||
|
||||
const transform = useValue(
|
||||
'transform',
|
||||
() => {
|
||||
const pageTransform = editor.getShapePageTransform(id)
|
||||
const pageTransform = editor.getShapePageTransform(shapeId)
|
||||
if (!pageTransform) return ''
|
||||
return pageTransform.toCssString()
|
||||
},
|
||||
[editor, id]
|
||||
[editor, shapeId]
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -79,11 +85,8 @@ const _ShapeIndicator: TLShapeIndicatorComponent = ({ id, className, color, opac
|
|||
stroke={color ?? 'var(--color-selected)'}
|
||||
opacity={opacity}
|
||||
>
|
||||
<InnerIndicator editor={editor} id={id} />
|
||||
<InnerIndicator editor={editor} id={shapeId} />
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const ShapeIndicator = React.memo(_ShapeIndicator)
|
||||
})
|
|
@ -154,14 +154,14 @@ function GapsSnapIndicator({ gaps, direction, zoom }: { zoom: number } & GapsSna
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export type TLSnapIndicatorComponent = React.ComponentType<{
|
||||
export type TLSnapIndicatorProps = {
|
||||
className?: string
|
||||
line: SnapIndicator
|
||||
zoom: number
|
||||
}>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultSnapIndicator: TLSnapIndicatorComponent = ({ className, line, zoom }) => {
|
||||
export function DefaultSnapIndicator({ className, line, zoom }: TLSnapIndicatorProps) {
|
||||
return (
|
||||
<svg className={classNames('tl-overlays__item', className)}>
|
||||
{line.type === 'points' ? (
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
import { ComponentType } from 'react'
|
||||
|
||||
/** @public */
|
||||
export type TLSpinnerComponent = ComponentType<object>
|
||||
|
||||
/** @public */
|
||||
export const DefaultSpinner: TLSpinnerComponent = () => {
|
||||
export function DefaultSpinner() {
|
||||
return (
|
||||
<svg width={16} height={16} viewBox="0 0 16 16">
|
||||
<g strokeWidth={2} fill="none" fillRule="evenodd">
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
/** @public */
|
||||
export type TLSvgDefsComponent = React.ComponentType
|
||||
|
||||
/** @public */
|
||||
export const DefaultSvgDefs = () => {
|
||||
return null
|
||||
|
|
|
@ -53,6 +53,7 @@ import {
|
|||
getIndicesBetween,
|
||||
getOwnProperty,
|
||||
hasOwnProperty,
|
||||
objectMapValues,
|
||||
sortById,
|
||||
sortByIndex,
|
||||
structuredClone,
|
||||
|
@ -6723,23 +6724,27 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
let remaining = duration
|
||||
let t: number
|
||||
|
||||
type FromTo = { prop: string; from: number; to: number }
|
||||
type ShapeAnimation = { partial: TLShapePartial; values: FromTo[] }
|
||||
type ShapeAnimation = {
|
||||
partial: TLShapePartial
|
||||
values: { prop: string; from: number; to: number }[]
|
||||
}
|
||||
|
||||
const animations: ShapeAnimation[] = []
|
||||
|
||||
partials.forEach((partial) => {
|
||||
if (!partial) return
|
||||
let partial: TLShapePartial | null | undefined, result: ShapeAnimation
|
||||
for (let i = 0, n = partials.length; i < n; i++) {
|
||||
partial = partials[i]
|
||||
if (!partial) continue
|
||||
|
||||
const result: ShapeAnimation = {
|
||||
result = {
|
||||
partial,
|
||||
values: [],
|
||||
}
|
||||
|
||||
const shape = this.getShape(partial.id)!
|
||||
if (!shape) continue
|
||||
|
||||
if (!shape) return
|
||||
|
||||
// We only support animations for certain props
|
||||
for (const key of ['x', 'y', 'rotation'] as const) {
|
||||
if (partial[key] !== undefined && shape[key] !== partial[key]) {
|
||||
result.values.push({ prop: key, from: shape[key], to: partial[key] as number })
|
||||
|
@ -6748,7 +6753,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
animations.push(result)
|
||||
this.animatingShapes.set(shape.id, animationId)
|
||||
})
|
||||
}
|
||||
|
||||
let value: ShapeAnimation
|
||||
|
||||
|
@ -6773,28 +6778,27 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
const { animatingShapes } = this
|
||||
|
||||
try {
|
||||
const tPartials: TLShapePartial[] = []
|
||||
const updates: TLShapePartial[] = []
|
||||
|
||||
for (let i = 0; i < animations.length; i++) {
|
||||
value = animations[i]
|
||||
let animationIdForShape: string | undefined
|
||||
for (let i = 0, n = animations.length; i < n; i++) {
|
||||
value = animations[i]
|
||||
// Is the animation for this shape still active?
|
||||
animationIdForShape = animatingShapes.get(value.partial.id)
|
||||
if (animationIdForShape !== animationId) continue
|
||||
|
||||
if (animatingShapes.get(value.partial.id) === animationId) {
|
||||
tPartials.push({
|
||||
id: value.partial.id,
|
||||
type: value.partial.type,
|
||||
...value.values.reduce((acc, { prop, from, to }) => {
|
||||
acc[prop] = from + (to - from) * t
|
||||
return acc
|
||||
}, {} as any),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this._updateShapes(tPartials, { squashing: true })
|
||||
} catch (e) {
|
||||
// noop
|
||||
// Create the update
|
||||
updates.push({
|
||||
id: value.partial.id,
|
||||
type: value.partial.type,
|
||||
...value.values.reduce((acc, { prop, from, to }) => {
|
||||
acc[prop] = from + (to - from) * t
|
||||
return acc
|
||||
}, {} as any),
|
||||
})
|
||||
}
|
||||
|
||||
this._updateShapes(updates, { squashing: true })
|
||||
}
|
||||
|
||||
this.addListener('tick', handleTick)
|
||||
|
@ -6968,20 +6972,24 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
partials: (TLShapePartial<T> | null | undefined)[],
|
||||
historyOptions?: TLCommandHistoryOptions
|
||||
) {
|
||||
let compactedPartials = compact(partials)
|
||||
if (this.animatingShapes.size > 0) {
|
||||
compactedPartials.forEach((p) => this.animatingShapes.delete(p.id))
|
||||
const compactedPartials: TLShapePartial<T>[] = Array(partials.length)
|
||||
|
||||
for (let i = 0, n = partials.length; i < n; i++) {
|
||||
const partial = partials[i]
|
||||
if (!partial) continue
|
||||
// Get the current shape referenced by the partial
|
||||
const shape = this.getShape(partial.id)
|
||||
if (!shape) continue
|
||||
|
||||
// If the shape is locked and we're not setting isLocked to true, continue
|
||||
if (this.isShapeOrAncestorLocked(shape) && !Object.hasOwn(partial, 'isLocked')) continue
|
||||
|
||||
// Remove any animating shapes from the list of partials
|
||||
this.animatingShapes.delete(partial.id)
|
||||
|
||||
compactedPartials.push(partial)
|
||||
}
|
||||
|
||||
compactedPartials = compactedPartials.filter((p) => {
|
||||
const shape = this.getShape(p.id)
|
||||
if (!shape) return false
|
||||
|
||||
// Only allow changes to unlocked shapes or changes to the isLocked property (otherwise we cannot unlock a shape)
|
||||
if (this.isShapeOrAncestorLocked(shape) && !Object.hasOwn(p, 'isLocked')) return false
|
||||
return true
|
||||
})
|
||||
|
||||
this._updateShapes(compactedPartials, historyOptions)
|
||||
return this
|
||||
}
|
||||
|
@ -6995,45 +7003,49 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
) => {
|
||||
if (this.getInstanceState().isReadonly) return null
|
||||
|
||||
const partials = compact(_partials)
|
||||
const snapshots: Record<string, TLShape> = {}
|
||||
const updates: Record<string, TLShape> = {}
|
||||
|
||||
const snapshots = Object.fromEntries(
|
||||
compact(partials.map(({ id }) => this.getShape(id))).map((shape) => {
|
||||
return [shape.id, shape]
|
||||
})
|
||||
)
|
||||
let shape: TLShape | undefined
|
||||
let updated: TLShape
|
||||
|
||||
if (partials.length <= 0) return null
|
||||
for (let i = 0, n = _partials.length; i < n; i++) {
|
||||
const partial = _partials[i]
|
||||
// Skip nullish partials (sometimes created by map fns returning undefined)
|
||||
if (!partial) continue
|
||||
|
||||
const updated = compact(
|
||||
partials.map((partial) => {
|
||||
const prev = snapshots[partial.id]
|
||||
if (!prev) return null
|
||||
return applyPartialToShape(prev, partial)
|
||||
})
|
||||
)
|
||||
// Get the current shape referenced by the partial
|
||||
// If there is no current shape, we'll skip this update
|
||||
shape = this.getShape(partial.id)
|
||||
if (!shape) continue
|
||||
|
||||
const updates = Object.fromEntries(updated.map((shape) => [shape.id, shape]))
|
||||
// Get the updated version of the shape
|
||||
// If the update had no effect, we'll skip this update
|
||||
updated = applyPartialToShape(shape, partial)
|
||||
if (updated === shape) continue
|
||||
|
||||
snapshots[shape.id] = shape
|
||||
updates[shape.id] = updated
|
||||
}
|
||||
|
||||
return { data: { snapshots, updates }, ...historyOptions }
|
||||
},
|
||||
{
|
||||
do: ({ updates }) => {
|
||||
// Iterate through array; if any shape has an onUpdate handler, call it
|
||||
// Iterate through array; if any shape has an onBeforeUpdate handler, call it
|
||||
// and, if the handler returns a new shape, replace the old shape with
|
||||
// the new one. This is used for example when repositioning a text shape
|
||||
// based on its new text content.
|
||||
const result = Object.values(updates)
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
const shape = result[i]
|
||||
const current = this.store.get(shape.id)
|
||||
if (!current) continue
|
||||
const next = this.getShapeUtil(shape).onBeforeUpdate?.(current, shape)
|
||||
if (next) {
|
||||
result[i] = next
|
||||
}
|
||||
}
|
||||
this.store.put(result)
|
||||
this.store.put(
|
||||
objectMapValues(updates).map((shape) => {
|
||||
const current = this.store.get(shape.id)
|
||||
if (current) {
|
||||
const next = this.getShapeUtil(shape).onBeforeUpdate?.(current, shape)
|
||||
if (next) return next
|
||||
}
|
||||
return shape
|
||||
})
|
||||
)
|
||||
},
|
||||
undo: ({ snapshots }) => {
|
||||
this.store.put(Object.values(snapshots))
|
||||
|
@ -8954,44 +8966,34 @@ function alertMaxShapes(editor: Editor, pageId = editor.getCurrentPageId()) {
|
|||
function applyPartialToShape<T extends TLShape>(prev: T, partial?: TLShapePartial<T>): T {
|
||||
if (!partial) return prev
|
||||
let next = null as null | T
|
||||
for (const [k, v] of Object.entries(partial)) {
|
||||
const entries = Object.entries(partial)
|
||||
for (let i = 0, n = entries.length; i < n; i++) {
|
||||
const [k, v] = entries[i]
|
||||
if (v === undefined) continue
|
||||
switch (k) {
|
||||
case 'id':
|
||||
case 'type':
|
||||
continue
|
||||
default: {
|
||||
if (v !== (prev as any)[k]) {
|
||||
if (!next) {
|
||||
next = { ...prev }
|
||||
}
|
||||
|
||||
if (k === 'props') {
|
||||
// props property
|
||||
const nextProps = { ...prev.props } as JsonObject
|
||||
for (const [propKey, propValue] of Object.entries(v as object)) {
|
||||
if (propValue !== undefined) {
|
||||
nextProps[propKey] = propValue
|
||||
}
|
||||
}
|
||||
next!.props = nextProps
|
||||
} else if (k === 'meta') {
|
||||
// meta property
|
||||
const nextMeta = { ...prev.meta } as JsonObject
|
||||
for (const [metaKey, metaValue] of Object.entries(v as object)) {
|
||||
if (metaValue !== undefined) {
|
||||
nextMeta[metaKey] = metaValue
|
||||
}
|
||||
}
|
||||
next!.meta = nextMeta
|
||||
} else {
|
||||
// base property
|
||||
;(next as any)[k] = v
|
||||
}
|
||||
// Is the key a special key? We don't update those
|
||||
if (k === 'id' || k === 'type' || k === 'typeName') continue
|
||||
|
||||
// Is the value the same as it was before?
|
||||
if (v === (prev as any)[k]) continue
|
||||
|
||||
// There's a new value, so create the new shape if we haven't already (should we be cloning this?)
|
||||
if (!next) next = { ...prev }
|
||||
|
||||
// for props / meta properties, we support updates with partials of this object
|
||||
if (k === 'props' || k === 'meta') {
|
||||
next[k] = { ...prev[k] } as JsonObject
|
||||
for (const [nextKey, nextValue] of Object.entries(v as object)) {
|
||||
if (nextValue !== undefined) {
|
||||
;(next[k] as JsonObject)[nextKey] = nextValue
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return next ?? prev
|
||||
// base property
|
||||
;(next as any)[k] = v
|
||||
}
|
||||
if (!next) return prev
|
||||
return next
|
||||
}
|
||||
|
|
|
@ -1,80 +1,80 @@
|
|||
import { createContext, useContext, useMemo } from 'react'
|
||||
import { ShapeIndicator, TLShapeIndicatorComponent } from '../components/ShapeIndicator'
|
||||
import { ComponentType, createContext, useContext, useMemo } from 'react'
|
||||
import { DefaultBackground } from '../components/default-components/DefaultBackground'
|
||||
import { DefaultBrush, TLBrushProps } from '../components/default-components/DefaultBrush'
|
||||
import {
|
||||
DefaultBackground,
|
||||
TLBackgroundComponent,
|
||||
} from '../components/default-components/DefaultBackground'
|
||||
import { DefaultBrush, TLBrushComponent } from '../components/default-components/DefaultBrush'
|
||||
DefaultCanvas,
|
||||
TLCanvasComponentProps,
|
||||
} from '../components/default-components/DefaultCanvas'
|
||||
import {
|
||||
DefaultCollaboratorHint,
|
||||
TLCollaboratorHintComponent,
|
||||
TLCollaboratorHintProps,
|
||||
} from '../components/default-components/DefaultCollaboratorHint'
|
||||
import { DefaultCursor, TLCursorComponent } from '../components/default-components/DefaultCursor'
|
||||
import { DefaultCursor, TLCursorProps } from '../components/default-components/DefaultCursor'
|
||||
import {
|
||||
DefaultErrorFallback,
|
||||
TLErrorFallbackComponent,
|
||||
} from '../components/default-components/DefaultErrorFallback'
|
||||
import { DefaultGrid, TLGridComponent } from '../components/default-components/DefaultGrid'
|
||||
import { DefaultHandle, TLHandleComponent } from '../components/default-components/DefaultHandle'
|
||||
import { DefaultHandles, TLHandlesComponent } from '../components/default-components/DefaultHandles'
|
||||
import { DefaultGrid, TLGridProps } from '../components/default-components/DefaultGrid'
|
||||
import { DefaultHandle, TLHandleProps } from '../components/default-components/DefaultHandle'
|
||||
import { DefaultHandles, TLHandlesProps } from '../components/default-components/DefaultHandles'
|
||||
import {
|
||||
DefaultHoveredShapeIndicator,
|
||||
TLHoveredShapeIndicatorComponent,
|
||||
TLHoveredShapeIndicatorProps,
|
||||
} from '../components/default-components/DefaultHoveredShapeIndicator'
|
||||
import { TLInFrontOfTheCanvas } from '../components/default-components/DefaultInFrontOfTheCanvas'
|
||||
import { TLLoadingScreenComponent } from '../components/default-components/DefaultLoadingScreen'
|
||||
import { TLOnTheCanvas } from '../components/default-components/DefaultOnTheCanvas'
|
||||
import {
|
||||
DefaultScribble,
|
||||
TLScribbleComponent,
|
||||
} from '../components/default-components/DefaultScribble'
|
||||
import { DefaultScribble, TLScribbleProps } from '../components/default-components/DefaultScribble'
|
||||
import {
|
||||
DefaultSelectionBackground,
|
||||
TLSelectionBackgroundComponent,
|
||||
TLSelectionBackgroundProps,
|
||||
} from '../components/default-components/DefaultSelectionBackground'
|
||||
import {
|
||||
DefaultSelectionForeground,
|
||||
TLSelectionForegroundComponent,
|
||||
TLSelectionForegroundProps,
|
||||
} from '../components/default-components/DefaultSelectionForeground'
|
||||
import {
|
||||
DefaultShapeErrorFallback,
|
||||
TLShapeErrorFallbackComponent,
|
||||
} from '../components/default-components/DefaultShapeErrorFallback'
|
||||
import {
|
||||
DefaultShapeIndicator,
|
||||
TLShapeIndicatorProps,
|
||||
} from '../components/default-components/DefaultShapeIndicator'
|
||||
import {
|
||||
DefaultShapeIndicatorErrorFallback,
|
||||
TLShapeIndicatorErrorFallbackComponent,
|
||||
} from '../components/default-components/DefaultShapeIndicatorErrorFallback'
|
||||
import {
|
||||
DefaultSnapIndicator,
|
||||
TLSnapIndicatorComponent,
|
||||
TLSnapIndicatorProps,
|
||||
} from '../components/default-components/DefaultSnapIndictor'
|
||||
import { DefaultSpinner, TLSpinnerComponent } from '../components/default-components/DefaultSpinner'
|
||||
import { DefaultSvgDefs, TLSvgDefsComponent } from '../components/default-components/DefaultSvgDefs'
|
||||
import { DefaultSpinner } from '../components/default-components/DefaultSpinner'
|
||||
import { DefaultSvgDefs } from '../components/default-components/DefaultSvgDefs'
|
||||
import { useShallowObjectIdentity } from './useIdentity'
|
||||
|
||||
export interface BaseEditorComponents {
|
||||
Background: TLBackgroundComponent
|
||||
SvgDefs: TLSvgDefsComponent
|
||||
Brush: TLBrushComponent
|
||||
ZoomBrush: TLBrushComponent
|
||||
Cursor: TLCursorComponent
|
||||
CollaboratorBrush: TLBrushComponent
|
||||
CollaboratorCursor: TLCursorComponent
|
||||
CollaboratorHint: TLCollaboratorHintComponent
|
||||
CollaboratorShapeIndicator: TLShapeIndicatorComponent
|
||||
Grid: TLGridComponent
|
||||
Scribble: TLScribbleComponent
|
||||
CollaboratorScribble: TLScribbleComponent
|
||||
SnapIndicator: TLSnapIndicatorComponent
|
||||
Handles: TLHandlesComponent
|
||||
Handle: TLHandleComponent
|
||||
Spinner: TLSpinnerComponent
|
||||
SelectionForeground: TLSelectionForegroundComponent
|
||||
SelectionBackground: TLSelectionBackgroundComponent
|
||||
HoveredShapeIndicator: TLHoveredShapeIndicatorComponent
|
||||
OnTheCanvas: TLOnTheCanvas
|
||||
InFrontOfTheCanvas: TLInFrontOfTheCanvas
|
||||
LoadingScreen: TLLoadingScreenComponent
|
||||
Background: ComponentType
|
||||
SvgDefs: ComponentType
|
||||
Brush: ComponentType<TLBrushProps>
|
||||
ZoomBrush: ComponentType<TLBrushProps>
|
||||
ShapeIndicator: ComponentType<TLShapeIndicatorProps>
|
||||
Cursor: ComponentType<TLCursorProps>
|
||||
Canvas: ComponentType<TLCanvasComponentProps>
|
||||
CollaboratorBrush: ComponentType<TLBrushProps>
|
||||
CollaboratorCursor: ComponentType<TLCursorProps>
|
||||
CollaboratorHint: ComponentType<TLCollaboratorHintProps>
|
||||
CollaboratorShapeIndicator: ComponentType<TLShapeIndicatorProps>
|
||||
Grid: ComponentType<TLGridProps>
|
||||
Scribble: ComponentType<TLScribbleProps>
|
||||
CollaboratorScribble: ComponentType<TLScribbleProps>
|
||||
SnapIndicator: ComponentType<TLSnapIndicatorProps>
|
||||
Handles: ComponentType<TLHandlesProps>
|
||||
Handle: ComponentType<TLHandleProps>
|
||||
Spinner: ComponentType
|
||||
SelectionForeground: ComponentType<TLSelectionForegroundProps>
|
||||
SelectionBackground: ComponentType<TLSelectionBackgroundProps>
|
||||
HoveredShapeIndicator: ComponentType<TLHoveredShapeIndicatorProps>
|
||||
OnTheCanvas: ComponentType
|
||||
InFrontOfTheCanvas: ComponentType
|
||||
LoadingScreen: ComponentType
|
||||
}
|
||||
|
||||
// These will always have defaults
|
||||
|
@ -116,7 +116,7 @@ export function EditorComponentsProvider({
|
|||
Cursor: DefaultCursor,
|
||||
CollaboratorCursor: DefaultCursor,
|
||||
CollaboratorHint: DefaultCollaboratorHint,
|
||||
CollaboratorShapeIndicator: ShapeIndicator,
|
||||
CollaboratorShapeIndicator: DefaultShapeIndicator,
|
||||
Grid: DefaultGrid,
|
||||
Scribble: DefaultScribble,
|
||||
SnapIndicator: DefaultSnapIndicator,
|
||||
|
@ -130,8 +130,10 @@ export function EditorComponentsProvider({
|
|||
SelectionBackground: DefaultSelectionBackground,
|
||||
SelectionForeground: DefaultSelectionForeground,
|
||||
HoveredShapeIndicator: DefaultHoveredShapeIndicator,
|
||||
ShapeIndicator: DefaultShapeIndicator,
|
||||
OnTheCanvas: null,
|
||||
InFrontOfTheCanvas: null,
|
||||
Canvas: DefaultCanvas,
|
||||
..._overrides,
|
||||
}),
|
||||
[_overrides]
|
||||
|
|
|
@ -74,9 +74,9 @@ import { TLExitEventHandler } from '@tldraw/editor';
|
|||
import { TLFrameShape } from '@tldraw/editor';
|
||||
import { TLGeoShape } from '@tldraw/editor';
|
||||
import { TLHandle } from '@tldraw/editor';
|
||||
import { TLHandlesComponent } from '@tldraw/editor';
|
||||
import { TLHandlesProps } from '@tldraw/editor';
|
||||
import { TLHighlightShape } from '@tldraw/editor';
|
||||
import { TLHoveredShapeIndicatorComponent } from '@tldraw/editor';
|
||||
import { TLHoveredShapeIndicatorProps } from '@tldraw/editor';
|
||||
import { TLImageShape } from '@tldraw/editor';
|
||||
import { TLInterruptEvent } from '@tldraw/editor';
|
||||
import { TLKeyboardEvent } from '@tldraw/editor';
|
||||
|
@ -100,9 +100,9 @@ import { TLPointerEventName } from '@tldraw/editor';
|
|||
import { TLRecord } from '@tldraw/editor';
|
||||
import { TLRotationSnapshot } from '@tldraw/editor';
|
||||
import { TLSchema } from '@tldraw/editor';
|
||||
import { TLScribbleComponent } from '@tldraw/editor';
|
||||
import { TLSelectionBackgroundComponent } from '@tldraw/editor';
|
||||
import { TLSelectionForegroundComponent } from '@tldraw/editor';
|
||||
import { TLScribbleProps } from '@tldraw/editor';
|
||||
import { TLSelectionBackgroundProps } from '@tldraw/editor';
|
||||
import { TLSelectionForegroundProps } from '@tldraw/editor';
|
||||
import { TLSelectionHandle } from '@tldraw/editor';
|
||||
import { TLShape } from '@tldraw/editor';
|
||||
import { TLShapeId } from '@tldraw/editor';
|
||||
|
@ -347,7 +347,7 @@ export const defaultShapeUtils: TLAnyShapeUtilConstructor[];
|
|||
export const DefaultStylePanel: NamedExoticComponent<TLUiStylePanelProps>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DefaultStylePanelContent({ relevantStyles }: TLUiStylePanelContentProps): JSX_2.Element | null;
|
||||
export function DefaultStylePanelContent({ styles }: TLUiStylePanelContentProps): JSX_2.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultToolbar: React_2.NamedExoticComponent<object>;
|
||||
|
@ -1173,21 +1173,6 @@ export function Tldraw(props: TldrawProps): JSX_2.Element;
|
|||
// @public (undocumented)
|
||||
export const TLDRAW_FILE_EXTENSION: ".tldr";
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawCropHandles({ size, width, height, hideAlternateHandles, }: TldrawCropHandlesProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TldrawCropHandlesProps {
|
||||
// (undocumented)
|
||||
height: number;
|
||||
// (undocumented)
|
||||
hideAlternateHandles: boolean;
|
||||
// (undocumented)
|
||||
size: number;
|
||||
// (undocumented)
|
||||
width: number;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TldrawFile {
|
||||
// (undocumented)
|
||||
|
@ -1199,10 +1184,10 @@ export interface TldrawFile {
|
|||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawHandles: TLHandlesComponent;
|
||||
export function TldrawHandles({ children }: TLHandlesProps): JSX_2.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawHoveredShapeIndicator: TLHoveredShapeIndicatorComponent;
|
||||
export function TldrawHoveredShapeIndicator({ shapeId }: TLHoveredShapeIndicatorProps): JSX_2.Element | null;
|
||||
|
||||
// @public
|
||||
export const TldrawImage: NamedExoticComponent< {
|
||||
|
@ -1240,13 +1225,13 @@ export type TldrawProps = (Omit<TldrawUiProps, 'components'> & Omit<TldrawEditor
|
|||
});
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawScribble: TLScribbleComponent;
|
||||
export function TldrawScribble({ scribble, zoom, color, opacity, className }: TLScribbleProps): JSX_2.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawSelectionBackground: TLSelectionBackgroundComponent;
|
||||
export const TldrawSelectionBackground: ({ bounds, rotation }: TLSelectionBackgroundProps) => JSX_2.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawSelectionForeground: TLSelectionForegroundComponent;
|
||||
export const TldrawSelectionForeground: MemoExoticComponent<({ bounds, rotation, }: TLSelectionForegroundProps) => JSX_2.Element | null>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawUi: React_2.NamedExoticComponent<TldrawUiProps>;
|
||||
|
@ -1313,7 +1298,7 @@ export function TldrawUiDropdownMenuCheckboxItem({ children, onSelect, ...rest }
|
|||
export function TldrawUiDropdownMenuContent({ side, align, sideOffset, alignOffset, children, }: TLUiDropdownMenuContentProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuGroup({ children, size, }: TLUiDropdownMenuGroupProps): JSX_2.Element;
|
||||
export function TldrawUiDropdownMenuGroup({ children }: TLUiDropdownMenuGroupProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuIndicator(): JSX_2.Element;
|
||||
|
@ -1349,7 +1334,7 @@ export function TldrawUiMenuCheckboxItem<TranslationKey extends string = string,
|
|||
export function TldrawUiMenuContextProvider({ type, sourceId, children, }: TLUiMenuContextProviderProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiMenuGroup({ id, label, small, children }: TLUiMenuGroupProps): any;
|
||||
export function TldrawUiMenuGroup({ id, label, children }: TLUiMenuGroupProps): any;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiMenuItem<TranslationKey extends string = string, IconType extends string = string>({ disabled, spinner, readonlyOk, id, kbd, label, icon, onSelect, noClose, }: TLUiMenuItemProps<TranslationKey, IconType>): JSX_2.Element | null;
|
||||
|
@ -1459,8 +1444,6 @@ export type TLUiComponentsProviderProps = {
|
|||
|
||||
// @public (undocumented)
|
||||
export interface TLUiContextMenuProps {
|
||||
// (undocumented)
|
||||
canvas: any;
|
||||
// (undocumented)
|
||||
children?: any;
|
||||
}
|
||||
|
@ -1549,7 +1532,6 @@ export type TLUiDropdownMenuContentProps = {
|
|||
// @public (undocumented)
|
||||
export type TLUiDropdownMenuGroupProps = {
|
||||
children: any;
|
||||
size?: 'medium' | 'small' | 'tiny' | 'wide';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -1866,7 +1848,6 @@ export type TLUiMenuGroupProps<TranslationKey extends string = string> = {
|
|||
label?: {
|
||||
[key: string]: TranslationKey;
|
||||
} | TranslationKey;
|
||||
small?: boolean;
|
||||
children?: any;
|
||||
};
|
||||
|
||||
|
@ -1894,7 +1875,7 @@ export type TLUiMenuSubmenuProps<Translation extends string = string> = {
|
|||
} | Translation;
|
||||
disabled?: boolean;
|
||||
children: any;
|
||||
size?: 'large' | 'medium' | 'small' | 'tiny';
|
||||
size?: 'medium' | 'small' | 'tiny' | 'wide';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -1951,7 +1932,7 @@ export interface TLUiSliderProps {
|
|||
|
||||
// @public (undocumented)
|
||||
export type TLUiStylePanelContentProps = {
|
||||
relevantStyles: ReturnType<typeof useRelevantStyles>;
|
||||
styles: ReturnType<typeof useRelevantStyles>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -1960,11 +1941,6 @@ export interface TLUiStylePanelProps {
|
|||
children?: any;
|
||||
// (undocumented)
|
||||
isMobile?: boolean;
|
||||
// (undocumented)
|
||||
relevantStyles: {
|
||||
styles: ReadonlySharedStyleMap;
|
||||
opacity: SharedStyle<number>;
|
||||
} | null;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -2136,6 +2112,9 @@ export function useNativeClipboardEvents(): void;
|
|||
// @public (undocumented)
|
||||
export function useReadonly(): boolean;
|
||||
|
||||
// @public (undocumented)
|
||||
export function useRelevantStyles(stylesToCheck?: readonly (EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow"> | EnumStyleProp<"dashed" | "dotted" | "draw" | "solid"> | EnumStyleProp<"l" | "m" | "s" | "xl"> | EnumStyleProp<"none" | "pattern" | "semi" | "solid">)[]): null | ReadonlySharedStyleMap;
|
||||
|
||||
// @public (undocumented)
|
||||
export function useTldrawUiComponents(): Partial<{
|
||||
ContextMenu: ComponentType<TLUiContextMenuProps> | null;
|
||||
|
|
|
@ -3185,7 +3185,7 @@
|
|||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function DefaultStylePanelContent({ relevantStyles }: "
|
||||
"text": "export declare function DefaultStylePanelContent({ styles }: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -3223,7 +3223,7 @@
|
|||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "{ relevantStyles }",
|
||||
"parameterName": "{ styles }",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
|
@ -13722,183 +13722,6 @@
|
|||
],
|
||||
"name": "Tldraw"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawCropHandles:function(1)",
|
||||
"docComment": "/**\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function TldrawCropHandles({ size, width, height, hideAlternateHandles, }: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TldrawCropHandlesProps",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawCropHandlesProps:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "import(\"react/jsx-runtime\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "JSX.Element",
|
||||
"canonicalReference": "@types/react!JSX.Element:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/canvas/TldrawCropHandles.tsx",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 5
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "{ size, width, height, hideAlternateHandles, }",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "TldrawCropHandles"
|
||||
},
|
||||
{
|
||||
"kind": "Interface",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawCropHandlesProps:interface",
|
||||
"docComment": "/**\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export interface TldrawCropHandlesProps "
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/canvas/TldrawCropHandles.tsx",
|
||||
"releaseTag": "Public",
|
||||
"name": "TldrawCropHandlesProps",
|
||||
"preserveMemberOrder": false,
|
||||
"members": [
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawCropHandlesProps#height:member",
|
||||
"docComment": "",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "height: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isReadonly": false,
|
||||
"isOptional": false,
|
||||
"releaseTag": "Public",
|
||||
"name": "height",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawCropHandlesProps#hideAlternateHandles:member",
|
||||
"docComment": "",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "hideAlternateHandles: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "boolean"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isReadonly": false,
|
||||
"isOptional": false,
|
||||
"releaseTag": "Public",
|
||||
"name": "hideAlternateHandles",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawCropHandlesProps#size:member",
|
||||
"docComment": "",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "size: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isReadonly": false,
|
||||
"isOptional": false,
|
||||
"releaseTag": "Public",
|
||||
"name": "size",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawCropHandlesProps#width:member",
|
||||
"docComment": "",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "width: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isReadonly": false,
|
||||
"isOptional": false,
|
||||
"releaseTag": "Public",
|
||||
"name": "width",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
}
|
||||
],
|
||||
"extendsTokenRanges": []
|
||||
},
|
||||
{
|
||||
"kind": "Interface",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawFile:interface",
|
||||
|
@ -14005,52 +13828,114 @@
|
|||
"extendsTokenRanges": []
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawHandles:var",
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawHandles:function(1)",
|
||||
"docComment": "/**\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "TldrawHandles: "
|
||||
"text": "export declare function TldrawHandles({ children }: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLHandlesComponent",
|
||||
"canonicalReference": "@tldraw/editor!TLHandlesComponent:type"
|
||||
"text": "TLHandlesProps",
|
||||
"canonicalReference": "@tldraw/editor!TLHandlesProps:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "import(\"react/jsx-runtime\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "JSX.Element",
|
||||
"canonicalReference": "@types/react!JSX.Element:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": " | null"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/canvas/TldrawHandles.tsx",
|
||||
"isReadonly": true,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"name": "TldrawHandles",
|
||||
"variableTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "{ children }",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "TldrawHandles"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawHoveredShapeIndicator:var",
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawHoveredShapeIndicator:function(1)",
|
||||
"docComment": "/**\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "TldrawHoveredShapeIndicator: "
|
||||
"text": "export declare function TldrawHoveredShapeIndicator({ shapeId }: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLHoveredShapeIndicatorComponent",
|
||||
"canonicalReference": "@tldraw/editor!TLHoveredShapeIndicatorComponent:type"
|
||||
"text": "TLHoveredShapeIndicatorProps",
|
||||
"canonicalReference": "@tldraw/editor!TLHoveredShapeIndicatorProps:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "import(\"react/jsx-runtime\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "JSX.Element",
|
||||
"canonicalReference": "@types/react!JSX.Element:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": " | null"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/canvas/TldrawHoveredShapeIndicator.tsx",
|
||||
"isReadonly": true,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"name": "TldrawHoveredShapeIndicator",
|
||||
"variableTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "{ shapeId }",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "TldrawHoveredShapeIndicator"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
|
@ -14340,28 +14225,59 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawScribble:var",
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/tldraw!TldrawScribble:function(1)",
|
||||
"docComment": "/**\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "TldrawScribble: "
|
||||
"text": "export declare function TldrawScribble({ scribble, zoom, color, opacity, className }: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLScribbleComponent",
|
||||
"canonicalReference": "@tldraw/editor!TLScribbleComponent:type"
|
||||
"text": "TLScribbleProps",
|
||||
"canonicalReference": "@tldraw/editor!TLScribbleProps:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "import(\"react/jsx-runtime\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "JSX.Element",
|
||||
"canonicalReference": "@types/react!JSX.Element:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": " | null"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/canvas/TldrawScribble.tsx",
|
||||
"isReadonly": true,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"name": "TldrawScribble",
|
||||
"variableTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "{ scribble, zoom, color, opacity, className }",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "TldrawScribble"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
|
@ -14372,10 +14288,27 @@
|
|||
"kind": "Content",
|
||||
"text": "TldrawSelectionBackground: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "({ bounds, rotation }: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLSelectionBackgroundComponent",
|
||||
"canonicalReference": "@tldraw/editor!TLSelectionBackgroundComponent:type"
|
||||
"text": "TLSelectionBackgroundProps",
|
||||
"canonicalReference": "@tldraw/editor!TLSelectionBackgroundProps:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ") => import(\"react/jsx-runtime\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "JSX.Element",
|
||||
"canonicalReference": "@types/react!JSX.Element:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": " | null"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/canvas/TldrawSelectionBackground.tsx",
|
||||
|
@ -14384,7 +14317,7 @@
|
|||
"name": "TldrawSelectionBackground",
|
||||
"variableTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
"endIndex": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -14396,10 +14329,36 @@
|
|||
"kind": "Content",
|
||||
"text": "TldrawSelectionForeground: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "import(\"react\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLSelectionForegroundComponent",
|
||||
"canonicalReference": "@tldraw/editor!TLSelectionForegroundComponent:type"
|
||||
"text": "MemoExoticComponent",
|
||||
"canonicalReference": "@types/react!React.MemoExoticComponent:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<({ bounds, rotation, }: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLSelectionForegroundProps",
|
||||
"canonicalReference": "@tldraw/editor!TLSelectionForegroundProps:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ") => import(\"react/jsx-runtime\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "JSX.Element",
|
||||
"canonicalReference": "@types/react!JSX.Element:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": " | null>"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/canvas/TldrawSelectionForeground.tsx",
|
||||
|
@ -14408,7 +14367,7 @@
|
|||
"name": "TldrawSelectionForeground",
|
||||
"variableTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
"endIndex": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -15525,7 +15484,7 @@
|
|||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function TldrawUiDropdownMenuGroup({ children, size, }: "
|
||||
"text": "export declare function TldrawUiDropdownMenuGroup({ children }: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -15559,7 +15518,7 @@
|
|||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "{ children, size, }",
|
||||
"parameterName": "{ children }",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
|
@ -16181,7 +16140,7 @@
|
|||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function TldrawUiMenuGroup({ id, label, small, children }: "
|
||||
"text": "export declare function TldrawUiMenuGroup({ id, label, children }: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -16210,7 +16169,7 @@
|
|||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "{ id, label, small, children }",
|
||||
"parameterName": "{ id, label, children }",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
|
@ -17518,34 +17477,6 @@
|
|||
"name": "TLUiContextMenuProps",
|
||||
"preserveMemberOrder": false,
|
||||
"members": [
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/tldraw!TLUiContextMenuProps#canvas:member",
|
||||
"docComment": "",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "canvas: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "any"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx",
|
||||
"isReadonly": false,
|
||||
"isOptional": false,
|
||||
"releaseTag": "Public",
|
||||
"name": "canvas",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/tldraw!TLUiContextMenuProps#children:member",
|
||||
|
@ -18144,7 +18075,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "{\n children: any;\n size?: 'medium' | 'small' | 'tiny' | 'wide';\n}"
|
||||
"text": "{\n children: any;\n}"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -21351,7 +21282,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "{\n id: string;\n label?: {\n [key: string]: TranslationKey;\n } | TranslationKey;\n small?: boolean;\n children?: any;\n}"
|
||||
"text": "{\n id: string;\n label?: {\n [key: string]: TranslationKey;\n } | TranslationKey;\n children?: any;\n}"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -21506,7 +21437,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "{\n id: string;\n label?: {\n [key: string]: Translation;\n } | Translation;\n disabled?: boolean;\n children: any;\n size?: 'large' | 'medium' | 'small' | 'tiny';\n}"
|
||||
"text": "{\n id: string;\n label?: {\n [key: string]: Translation;\n } | Translation;\n disabled?: boolean;\n children: any;\n size?: 'medium' | 'small' | 'tiny' | 'wide';\n}"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -21808,7 +21739,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "{\n relevantStyles: "
|
||||
"text": "{\n styles: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -21822,7 +21753,7 @@
|
|||
{
|
||||
"kind": "Reference",
|
||||
"text": "useRelevantStyles",
|
||||
"canonicalReference": "@tldraw/tldraw!~useRelevantStyles:function"
|
||||
"canonicalReference": "@tldraw/tldraw!useRelevantStyles:function"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -21851,7 +21782,7 @@
|
|||
"text": "export interface TLUiStylePanelProps "
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx",
|
||||
"fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/StylePanel/DefaultStylePanel.d.ts",
|
||||
"releaseTag": "Public",
|
||||
"name": "TLUiStylePanelProps",
|
||||
"preserveMemberOrder": false,
|
||||
|
@ -21874,6 +21805,7 @@
|
|||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx",
|
||||
"isReadonly": false,
|
||||
"isOptional": true,
|
||||
"releaseTag": "Public",
|
||||
|
@ -21901,6 +21833,7 @@
|
|||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx",
|
||||
"isReadonly": false,
|
||||
"isOptional": true,
|
||||
"releaseTag": "Public",
|
||||
|
@ -21909,51 +21842,6 @@
|
|||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/tldraw!TLUiStylePanelProps#relevantStyles:member",
|
||||
"docComment": "",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "relevantStyles: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "{\n styles: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "ReadonlySharedStyleMap",
|
||||
"canonicalReference": "@tldraw/editor!ReadonlySharedStyleMap:class"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n opacity: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "SharedStyle",
|
||||
"canonicalReference": "@tldraw/editor!SharedStyle:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<number>;\n } | null"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isReadonly": false,
|
||||
"isOptional": false,
|
||||
"releaseTag": "Public",
|
||||
"name": "relevantStyles",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 6
|
||||
}
|
||||
}
|
||||
],
|
||||
"extendsTokenRanges": []
|
||||
|
@ -23678,6 +23566,92 @@
|
|||
"parameters": [],
|
||||
"name": "useReadonly"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/tldraw!useRelevantStyles:function(1)",
|
||||
"docComment": "/**\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function useRelevantStyles(stylesToCheck?: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "readonly (import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "EnumStyleProp",
|
||||
"canonicalReference": "@tldraw/tlschema!EnumStyleProp:class"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\"> | import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "EnumStyleProp",
|
||||
"canonicalReference": "@tldraw/tlschema!EnumStyleProp:class"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"dashed\" | \"dotted\" | \"draw\" | \"solid\"> | import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "EnumStyleProp",
|
||||
"canonicalReference": "@tldraw/tlschema!EnumStyleProp:class"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"l\" | \"m\" | \"s\" | \"xl\"> | import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "EnumStyleProp",
|
||||
"canonicalReference": "@tldraw/tlschema!EnumStyleProp:class"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"none\" | \"pattern\" | \"semi\" | \"solid\">)[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "null | "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "ReadonlySharedStyleMap",
|
||||
"canonicalReference": "@tldraw/editor!ReadonlySharedStyleMap:class"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useRevelantStyles.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 11,
|
||||
"endIndex": 13
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "stylesToCheck",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 10
|
||||
},
|
||||
"isOptional": true
|
||||
}
|
||||
],
|
||||
"name": "useRelevantStyles"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/tldraw!useTldrawUiComponents:function(1)",
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
export * from '@tldraw/editor'
|
||||
export { Tldraw, type TldrawProps } from './lib/Tldraw'
|
||||
export { TldrawImage, type TldrawImageProps } from './lib/TldrawImage'
|
||||
export { TldrawCropHandles, type TldrawCropHandlesProps } from './lib/canvas/TldrawCropHandles'
|
||||
export { TldrawHandles } from './lib/canvas/TldrawHandles'
|
||||
export { TldrawHoveredShapeIndicator } from './lib/canvas/TldrawHoveredShapeIndicator'
|
||||
export { TldrawScribble } from './lib/canvas/TldrawScribble'
|
||||
|
@ -82,6 +81,7 @@ export { useKeyboardShortcuts } from './lib/ui/hooks/useKeyboardShortcuts'
|
|||
export { useLocalStorageState } from './lib/ui/hooks/useLocalStorageState'
|
||||
export { useMenuIsOpen } from './lib/ui/hooks/useMenuIsOpen'
|
||||
export { useReadonly } from './lib/ui/hooks/useReadonly'
|
||||
export { useRelevantStyles } from './lib/ui/hooks/useRevelantStyles'
|
||||
export {
|
||||
toolbarItem,
|
||||
useToolbarSchema,
|
||||
|
|
|
@ -9,7 +9,8 @@ describe('<Tldraw />', () => {
|
|||
await renderTldrawComponent(
|
||||
<Tldraw>
|
||||
<div data-testid="canvas-1" />
|
||||
</Tldraw>
|
||||
</Tldraw>,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
|
||||
await screen.findByTestId('canvas-1')
|
||||
|
@ -26,7 +27,7 @@ describe('<Tldraw />', () => {
|
|||
)
|
||||
}
|
||||
|
||||
await renderTldrawComponent(<TestComponent />)
|
||||
await renderTldrawComponent(<TestComponent />, { waitForPatterns: false })
|
||||
await screen.findByTestId('canvas-1')
|
||||
})
|
||||
|
||||
|
@ -59,7 +60,8 @@ describe('<Tldraw />', () => {
|
|||
const rendered = await renderTldrawComponent(
|
||||
<Tldraw shapeUtils={[FakeShapeUtil1]}>
|
||||
<div data-testid="canvas-1" />
|
||||
</Tldraw>
|
||||
</Tldraw>,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
|
||||
await screen.findByTestId('canvas-1')
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {
|
||||
Canvas,
|
||||
Editor,
|
||||
ErrorScreen,
|
||||
LoadingScreen,
|
||||
|
@ -13,6 +12,7 @@ import {
|
|||
TldrawEditorBaseProps,
|
||||
assert,
|
||||
useEditor,
|
||||
useEditorComponents,
|
||||
useShallowArrayIdentity,
|
||||
useShallowObjectIdentity,
|
||||
} from '@tldraw/editor'
|
||||
|
@ -125,7 +125,7 @@ export function Tldraw(props: TldrawProps) {
|
|||
tools={toolsWithDefaults}
|
||||
>
|
||||
<TldrawUi {...rest} components={componentsWithDefault}>
|
||||
<InsideOfEditorContext
|
||||
<InsideOfEditorAndUiContext
|
||||
maxImageDimension={maxImageDimension}
|
||||
maxAssetSize={maxAssetSize}
|
||||
acceptedImageMimeTypes={acceptedImageMimeTypes}
|
||||
|
@ -147,8 +147,8 @@ const defaultAcceptedImageMimeTypes = Object.freeze([
|
|||
|
||||
const defaultAcceptedVideoMimeTypes = Object.freeze(['video/mp4', 'video/quicktime'])
|
||||
|
||||
// We put these hooks into a component here so that they can run inside of the context provided by TldrawEditor.
|
||||
function InsideOfEditorContext({
|
||||
// We put these hooks into a component here so that they can run inside of the context provided by TldrawEditor and TldrawUi.
|
||||
function InsideOfEditorAndUiContext({
|
||||
maxImageDimension = 1000,
|
||||
maxAssetSize = 10 * 1024 * 1024, // 10mb
|
||||
acceptedImageMimeTypes = defaultAcceptedImageMimeTypes,
|
||||
|
@ -182,10 +182,19 @@ function InsideOfEditorContext({
|
|||
if (editor) return onMountEvent?.(editor)
|
||||
}, [editor, onMountEvent])
|
||||
|
||||
const { Canvas } = useEditorComponents()
|
||||
const { ContextMenu } = useTldrawUiComponents()
|
||||
if (!ContextMenu) return <Canvas />
|
||||
|
||||
return <ContextMenu canvas={<Canvas />} />
|
||||
if (ContextMenu) {
|
||||
// should wrap canvas
|
||||
return <ContextMenu />
|
||||
}
|
||||
|
||||
if (Canvas) {
|
||||
return <Canvas />
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// duped from tldraw editor
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { toDomPrecision } from '@tldraw/editor'
|
||||
import classNames from 'classnames'
|
||||
|
||||
/** @public */
|
||||
export interface TldrawCropHandlesProps {
|
||||
size: number
|
||||
width: number
|
||||
|
@ -9,7 +8,6 @@ export interface TldrawCropHandlesProps {
|
|||
hideAlternateHandles: boolean
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function TldrawCropHandles({
|
||||
size,
|
||||
width,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { TLHandlesComponent, useEditor, useValue } from '@tldraw/editor'
|
||||
import { TLHandlesProps, useEditor, useValue } from '@tldraw/editor'
|
||||
|
||||
/** @public */
|
||||
export const TldrawHandles: TLHandlesComponent = ({ children }) => {
|
||||
export function TldrawHandles({ children }: TLHandlesProps) {
|
||||
const editor = useEditor()
|
||||
const shouldDisplayHandles = useValue(
|
||||
'shouldDisplayHandles',
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import {
|
||||
ShapeIndicator,
|
||||
TLHoveredShapeIndicatorComponent,
|
||||
TLHoveredShapeIndicatorProps,
|
||||
useEditor,
|
||||
useEditorComponents,
|
||||
useValue,
|
||||
} from '@tldraw/editor'
|
||||
|
||||
/** @public */
|
||||
export const TldrawHoveredShapeIndicator: TLHoveredShapeIndicatorComponent = ({ shapeId }) => {
|
||||
export function TldrawHoveredShapeIndicator({ shapeId }: TLHoveredShapeIndicatorProps) {
|
||||
const editor = useEditor()
|
||||
const { ShapeIndicator } = useEditorComponents()
|
||||
const showHoveredShapeIndicator = useValue(
|
||||
'show hovered',
|
||||
() => {
|
||||
|
@ -23,8 +24,9 @@ export const TldrawHoveredShapeIndicator: TLHoveredShapeIndicatorComponent = ({
|
|||
},
|
||||
[editor]
|
||||
)
|
||||
if (!ShapeIndicator) return null
|
||||
if (!showHoveredShapeIndicator) return null
|
||||
return <ShapeIndicator className="tl-user-indicator__hovered" id={shapeId} />
|
||||
return <ShapeIndicator className="tl-user-indicator__hovered" shapeId={shapeId} />
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
import { EASINGS, TLScribbleComponent, getSvgPathFromPoints } from '@tldraw/editor'
|
||||
import { EASINGS, TLScribbleProps, getSvgPathFromPoints } from '@tldraw/editor'
|
||||
import classNames from 'classnames'
|
||||
import { getStroke } from '../shapes/shared/freehand/getStroke'
|
||||
|
||||
/** @public */
|
||||
export const TldrawScribble: TLScribbleComponent = ({
|
||||
scribble,
|
||||
zoom,
|
||||
color,
|
||||
opacity,
|
||||
className,
|
||||
}) => {
|
||||
export function TldrawScribble({ scribble, zoom, color, opacity, className }: TLScribbleProps) {
|
||||
if (!scribble.points.length) return null
|
||||
|
||||
const stroke = getStroke(scribble.points, {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import {
|
||||
DefaultSelectionBackground,
|
||||
TLSelectionBackgroundComponent,
|
||||
TLSelectionBackgroundProps,
|
||||
useEditor,
|
||||
useValue,
|
||||
} from '@tldraw/editor'
|
||||
|
||||
/** @public */
|
||||
export const TldrawSelectionBackground: TLSelectionBackgroundComponent = ({ bounds, rotation }) => {
|
||||
export const TldrawSelectionBackground = ({ bounds, rotation }: TLSelectionBackgroundProps) => {
|
||||
const editor = useEditor()
|
||||
|
||||
const shouldDisplay = useValue(
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import {
|
||||
Box,
|
||||
RotateCorner,
|
||||
TLEmbedShape,
|
||||
TLSelectionForegroundComponent,
|
||||
TLSelectionForegroundProps,
|
||||
TLTextShape,
|
||||
getCursor,
|
||||
toDomPrecision,
|
||||
|
@ -18,428 +17,428 @@ import { useReadonly } from '../ui/hooks/useReadonly'
|
|||
import { TldrawCropHandles } from './TldrawCropHandles'
|
||||
|
||||
/** @public */
|
||||
export const TldrawSelectionForeground: TLSelectionForegroundComponent = track(
|
||||
function TldrawSelectionForeground({ bounds, rotation }: { bounds: Box; rotation: number }) {
|
||||
const editor = useEditor()
|
||||
const rSvg = useRef<SVGSVGElement>(null)
|
||||
export const TldrawSelectionForeground = track(function TldrawSelectionForeground({
|
||||
bounds,
|
||||
rotation,
|
||||
}: TLSelectionForegroundProps) {
|
||||
const editor = useEditor()
|
||||
const rSvg = useRef<SVGSVGElement>(null)
|
||||
|
||||
const isReadonlyMode = useReadonly()
|
||||
const topEvents = useSelectionEvents('top')
|
||||
const rightEvents = useSelectionEvents('right')
|
||||
const bottomEvents = useSelectionEvents('bottom')
|
||||
const leftEvents = useSelectionEvents('left')
|
||||
const topLeftEvents = useSelectionEvents('top_left')
|
||||
const topRightEvents = useSelectionEvents('top_right')
|
||||
const bottomRightEvents = useSelectionEvents('bottom_right')
|
||||
const bottomLeftEvents = useSelectionEvents('bottom_left')
|
||||
const isReadonlyMode = useReadonly()
|
||||
const topEvents = useSelectionEvents('top')
|
||||
const rightEvents = useSelectionEvents('right')
|
||||
const bottomEvents = useSelectionEvents('bottom')
|
||||
const leftEvents = useSelectionEvents('left')
|
||||
const topLeftEvents = useSelectionEvents('top_left')
|
||||
const topRightEvents = useSelectionEvents('top_right')
|
||||
const bottomRightEvents = useSelectionEvents('bottom_right')
|
||||
const bottomLeftEvents = useSelectionEvents('bottom_left')
|
||||
|
||||
const isDefaultCursor =
|
||||
!editor.getIsMenuOpen() && editor.getInstanceState().cursor.type === 'default'
|
||||
const isCoarsePointer = editor.getInstanceState().isCoarsePointer
|
||||
const isDefaultCursor =
|
||||
!editor.getIsMenuOpen() && editor.getInstanceState().cursor.type === 'default'
|
||||
const isCoarsePointer = editor.getInstanceState().isCoarsePointer
|
||||
|
||||
const onlyShape = editor.getOnlySelectedShape()
|
||||
const isLockedShape = onlyShape && editor.isShapeOrAncestorLocked(onlyShape)
|
||||
const onlyShape = editor.getOnlySelectedShape()
|
||||
const isLockedShape = onlyShape && editor.isShapeOrAncestorLocked(onlyShape)
|
||||
|
||||
// if all shapes have an expandBy for the selection outline, we can expand by the l
|
||||
const expandOutlineBy = onlyShape
|
||||
? editor.getShapeUtil(onlyShape).expandSelectionOutlinePx(onlyShape)
|
||||
: 0
|
||||
// if all shapes have an expandBy for the selection outline, we can expand by the l
|
||||
const expandOutlineBy = onlyShape
|
||||
? editor.getShapeUtil(onlyShape).expandSelectionOutlinePx(onlyShape)
|
||||
: 0
|
||||
|
||||
useTransform(rSvg, bounds?.x, bounds?.y, 1, editor.getSelectionRotation(), {
|
||||
x: -expandOutlineBy,
|
||||
y: -expandOutlineBy,
|
||||
})
|
||||
useTransform(rSvg, bounds?.x, bounds?.y, 1, editor.getSelectionRotation(), {
|
||||
x: -expandOutlineBy,
|
||||
y: -expandOutlineBy,
|
||||
})
|
||||
|
||||
if (!bounds) return null
|
||||
bounds = bounds.clone().expandBy(expandOutlineBy).zeroFix()
|
||||
if (!bounds) return null
|
||||
bounds = bounds.clone().expandBy(expandOutlineBy).zeroFix()
|
||||
|
||||
const zoom = editor.getZoomLevel()
|
||||
const isChangingStyle = editor.getInstanceState().isChangingStyle
|
||||
const zoom = editor.getZoomLevel()
|
||||
const isChangingStyle = editor.getInstanceState().isChangingStyle
|
||||
|
||||
const width = bounds.width
|
||||
const height = bounds.height
|
||||
const width = bounds.width
|
||||
const height = bounds.height
|
||||
|
||||
const size = 8 / zoom
|
||||
const isTinyX = width < size * 2
|
||||
const isTinyY = height < size * 2
|
||||
const size = 8 / zoom
|
||||
const isTinyX = width < size * 2
|
||||
const isTinyY = height < size * 2
|
||||
|
||||
const isSmallX = width < size * 4
|
||||
const isSmallY = height < size * 4
|
||||
const isSmallCropX = width < size * 5
|
||||
const isSmallCropY = height < size * 5
|
||||
const isSmallX = width < size * 4
|
||||
const isSmallY = height < size * 4
|
||||
const isSmallCropX = width < size * 5
|
||||
const isSmallCropY = height < size * 5
|
||||
|
||||
const mobileHandleMultiplier = isCoarsePointer ? 1.75 : 1
|
||||
const targetSize = (6 / zoom) * mobileHandleMultiplier
|
||||
const mobileHandleMultiplier = isCoarsePointer ? 1.75 : 1
|
||||
const targetSize = (6 / zoom) * mobileHandleMultiplier
|
||||
|
||||
const targetSizeX = (isSmallX ? targetSize / 2 : targetSize) * (mobileHandleMultiplier * 0.75)
|
||||
const targetSizeY = (isSmallY ? targetSize / 2 : targetSize) * (mobileHandleMultiplier * 0.75)
|
||||
const targetSizeX = (isSmallX ? targetSize / 2 : targetSize) * (mobileHandleMultiplier * 0.75)
|
||||
const targetSizeY = (isSmallY ? targetSize / 2 : targetSize) * (mobileHandleMultiplier * 0.75)
|
||||
|
||||
const showSelectionBounds =
|
||||
(onlyShape ? !editor.getShapeUtil(onlyShape).hideSelectionBoundsFg(onlyShape) : true) &&
|
||||
!isChangingStyle
|
||||
const showSelectionBounds =
|
||||
(onlyShape ? !editor.getShapeUtil(onlyShape).hideSelectionBoundsFg(onlyShape) : true) &&
|
||||
!isChangingStyle
|
||||
|
||||
let shouldDisplayBox =
|
||||
(showSelectionBounds &&
|
||||
editor.isInAny(
|
||||
'select.idle',
|
||||
'select.brushing',
|
||||
'select.scribble_brushing',
|
||||
'select.pointing_canvas',
|
||||
'select.pointing_selection',
|
||||
'select.pointing_shape',
|
||||
'select.crop.idle',
|
||||
'select.crop.pointing_crop',
|
||||
'select.pointing_resize_handle',
|
||||
'select.pointing_crop_handle'
|
||||
)) ||
|
||||
(showSelectionBounds &&
|
||||
editor.isIn('select.resizing') &&
|
||||
onlyShape &&
|
||||
editor.isShapeOfType<TLTextShape>(onlyShape, 'text'))
|
||||
|
||||
if (onlyShape && shouldDisplayBox) {
|
||||
if (editor.environment.isFirefox && editor.isShapeOfType<TLEmbedShape>(onlyShape, 'embed')) {
|
||||
shouldDisplayBox = false
|
||||
}
|
||||
}
|
||||
|
||||
const showCropHandles =
|
||||
editor.isInAny(
|
||||
'select.pointing_crop_handle',
|
||||
'select.crop.idle',
|
||||
'select.crop.pointing_crop'
|
||||
) &&
|
||||
!isChangingStyle &&
|
||||
!isReadonlyMode
|
||||
|
||||
const shouldDisplayControls =
|
||||
let shouldDisplayBox =
|
||||
(showSelectionBounds &&
|
||||
editor.isInAny(
|
||||
'select.idle',
|
||||
'select.brushing',
|
||||
'select.scribble_brushing',
|
||||
'select.pointing_canvas',
|
||||
'select.pointing_selection',
|
||||
'select.pointing_shape',
|
||||
'select.crop.idle'
|
||||
) &&
|
||||
!isChangingStyle &&
|
||||
!isReadonlyMode
|
||||
|
||||
const showCornerRotateHandles =
|
||||
!isCoarsePointer &&
|
||||
!(isTinyX || isTinyY) &&
|
||||
(shouldDisplayControls || showCropHandles) &&
|
||||
(onlyShape ? !editor.getShapeUtil(onlyShape).hideRotateHandle(onlyShape) : true) &&
|
||||
!isLockedShape
|
||||
|
||||
const showMobileRotateHandle =
|
||||
isCoarsePointer &&
|
||||
(!isSmallX || !isSmallY) &&
|
||||
(shouldDisplayControls || showCropHandles) &&
|
||||
(onlyShape ? !editor.getShapeUtil(onlyShape).hideRotateHandle(onlyShape) : true) &&
|
||||
!isLockedShape
|
||||
|
||||
const showResizeHandles =
|
||||
shouldDisplayControls &&
|
||||
(onlyShape
|
||||
? editor.getShapeUtil(onlyShape).canResize(onlyShape) &&
|
||||
!editor.getShapeUtil(onlyShape).hideResizeHandles(onlyShape)
|
||||
: true) &&
|
||||
!showCropHandles &&
|
||||
!isLockedShape
|
||||
|
||||
const hideAlternateCornerHandles = isTinyX || isTinyY
|
||||
const showOnlyOneHandle = isTinyX && isTinyY
|
||||
const hideAlternateCropHandles = isSmallCropX || isSmallCropY
|
||||
|
||||
const showHandles = showResizeHandles || showCropHandles
|
||||
const hideRotateCornerHandles = !showCornerRotateHandles
|
||||
const hideMobileRotateHandle = !shouldDisplayControls || !showMobileRotateHandle
|
||||
const hideTopLeftCorner = !shouldDisplayControls || !showHandles
|
||||
const hideTopRightCorner = !shouldDisplayControls || !showHandles || hideAlternateCornerHandles
|
||||
const hideBottomLeftCorner =
|
||||
!shouldDisplayControls || !showHandles || hideAlternateCornerHandles
|
||||
const hideBottomRightCorner =
|
||||
!shouldDisplayControls || !showHandles || (showOnlyOneHandle && !showCropHandles)
|
||||
|
||||
// If we're showing crop handles, then show the edges too.
|
||||
// If we're showing resize handles, then show the edges only
|
||||
// if we're not hiding them for some other reason.
|
||||
let hideVerticalEdgeTargets = true
|
||||
// The same logic above applies here, except another nuance is that
|
||||
// we enable resizing for text on mobile (coarse).
|
||||
let hideHorizontalEdgeTargets = true
|
||||
|
||||
if (showCropHandles) {
|
||||
hideVerticalEdgeTargets = hideAlternateCropHandles
|
||||
hideHorizontalEdgeTargets = hideAlternateCropHandles
|
||||
} else if (showResizeHandles) {
|
||||
hideVerticalEdgeTargets = hideAlternateCornerHandles || showOnlyOneHandle || isCoarsePointer
|
||||
const isMobileAndTextShape = isCoarsePointer && onlyShape && onlyShape.type === 'text'
|
||||
hideHorizontalEdgeTargets = hideVerticalEdgeTargets && !isMobileAndTextShape
|
||||
}
|
||||
|
||||
const textHandleHeight = Math.min(24 / zoom, height - targetSizeY * 3)
|
||||
const showTextResizeHandles =
|
||||
shouldDisplayControls &&
|
||||
isCoarsePointer &&
|
||||
'select.crop.idle',
|
||||
'select.crop.pointing_crop',
|
||||
'select.pointing_resize_handle',
|
||||
'select.pointing_crop_handle'
|
||||
)) ||
|
||||
(showSelectionBounds &&
|
||||
editor.isIn('select.resizing') &&
|
||||
onlyShape &&
|
||||
editor.isShapeOfType<TLTextShape>(onlyShape, 'text') &&
|
||||
textHandleHeight * zoom >= 4
|
||||
editor.isShapeOfType<TLTextShape>(onlyShape, 'text'))
|
||||
|
||||
return (
|
||||
<svg className="tl-overlays__item tl-selection__fg" data-testid="selection-foreground">
|
||||
<g ref={rSvg}>
|
||||
{shouldDisplayBox && (
|
||||
<rect
|
||||
className="tl-selection__fg__outline"
|
||||
width={toDomPrecision(width)}
|
||||
height={toDomPrecision(height)}
|
||||
/>
|
||||
)}
|
||||
<RotateCornerHandle
|
||||
data-testid="selection.rotate.top-left"
|
||||
cx={0}
|
||||
cy={0}
|
||||
targetSize={targetSize}
|
||||
corner="top_left_rotate"
|
||||
cursor={isDefaultCursor ? getCursor('nwse-rotate', rotation) : undefined}
|
||||
isHidden={hideRotateCornerHandles}
|
||||
/>
|
||||
<RotateCornerHandle
|
||||
data-testid="selection.rotate.top-right"
|
||||
cx={width + targetSize * 3}
|
||||
cy={0}
|
||||
targetSize={targetSize}
|
||||
corner="top_right_rotate"
|
||||
cursor={isDefaultCursor ? getCursor('nesw-rotate', rotation) : undefined}
|
||||
isHidden={hideRotateCornerHandles}
|
||||
/>
|
||||
<RotateCornerHandle
|
||||
data-testid="selection.rotate.bottom-left"
|
||||
cx={0}
|
||||
cy={height + targetSize * 3}
|
||||
targetSize={targetSize}
|
||||
corner="bottom_left_rotate"
|
||||
cursor={isDefaultCursor ? getCursor('swne-rotate', rotation) : undefined}
|
||||
isHidden={hideRotateCornerHandles}
|
||||
/>
|
||||
<RotateCornerHandle
|
||||
data-testid="selection.rotate.bottom-right"
|
||||
cx={width + targetSize * 3}
|
||||
cy={height + targetSize * 3}
|
||||
targetSize={targetSize}
|
||||
corner="bottom_right_rotate"
|
||||
cursor={isDefaultCursor ? getCursor('senw-rotate', rotation) : undefined}
|
||||
isHidden={hideRotateCornerHandles}
|
||||
/>
|
||||
<MobileRotateHandle
|
||||
data-testid="selection.rotate.mobile"
|
||||
cx={isSmallX ? -targetSize * 1.5 : width / 2}
|
||||
cy={isSmallX ? height / 2 : -targetSize * 1.5}
|
||||
size={size}
|
||||
isHidden={hideMobileRotateHandle}
|
||||
/>
|
||||
{/* Targets */}
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideVerticalEdgeTargets,
|
||||
})}
|
||||
data-testid="selection.resize.top"
|
||||
aria-label="top target"
|
||||
pointerEvents="all"
|
||||
x={0}
|
||||
y={toDomPrecision(0 - (isSmallY ? targetSizeY * 2 : targetSizeY))}
|
||||
width={toDomPrecision(width)}
|
||||
height={toDomPrecision(Math.max(1, targetSizeY * 2))}
|
||||
style={isDefaultCursor ? { cursor: getCursor('ns-resize', rotation) } : undefined}
|
||||
{...topEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideHorizontalEdgeTargets,
|
||||
})}
|
||||
data-testid="selection.resize.right"
|
||||
aria-label="right target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(width - (isSmallX ? 0 : targetSizeX))}
|
||||
y={0}
|
||||
height={toDomPrecision(height)}
|
||||
width={toDomPrecision(Math.max(1, targetSizeX * 2))}
|
||||
style={isDefaultCursor ? { cursor: getCursor('ew-resize', rotation) } : undefined}
|
||||
{...rightEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideVerticalEdgeTargets,
|
||||
})}
|
||||
data-testid="selection.resize.bottom"
|
||||
aria-label="bottom target"
|
||||
pointerEvents="all"
|
||||
x={0}
|
||||
y={toDomPrecision(height - (isSmallY ? 0 : targetSizeY))}
|
||||
width={toDomPrecision(width)}
|
||||
height={toDomPrecision(Math.max(1, targetSizeY * 2))}
|
||||
style={isDefaultCursor ? { cursor: getCursor('ns-resize', rotation) } : undefined}
|
||||
{...bottomEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideHorizontalEdgeTargets,
|
||||
})}
|
||||
data-testid="selection.resize.left"
|
||||
aria-label="left target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(0 - (isSmallX ? targetSizeX * 2 : targetSizeX))}
|
||||
y={0}
|
||||
height={toDomPrecision(height)}
|
||||
width={toDomPrecision(Math.max(1, targetSizeX * 2))}
|
||||
style={isDefaultCursor ? { cursor: getCursor('ew-resize', rotation) } : undefined}
|
||||
{...leftEvents}
|
||||
/>
|
||||
{/* Corner Targets */}
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideTopLeftCorner,
|
||||
})}
|
||||
data-testid="selection.target.top-left"
|
||||
aria-label="top-left target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(0 - (isSmallX ? targetSizeX * 2 : targetSizeX * 1.5))}
|
||||
y={toDomPrecision(0 - (isSmallY ? targetSizeY * 2 : targetSizeY * 1.5))}
|
||||
width={toDomPrecision(targetSizeX * 3)}
|
||||
height={toDomPrecision(targetSizeY * 3)}
|
||||
style={isDefaultCursor ? { cursor: getCursor('nwse-resize', rotation) } : undefined}
|
||||
{...topLeftEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideTopRightCorner,
|
||||
})}
|
||||
data-testid="selection.target.top-right"
|
||||
aria-label="top-right target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(width - (isSmallX ? 0 : targetSizeX * 1.5))}
|
||||
y={toDomPrecision(0 - (isSmallY ? targetSizeY * 2 : targetSizeY * 1.5))}
|
||||
width={toDomPrecision(targetSizeX * 3)}
|
||||
height={toDomPrecision(targetSizeY * 3)}
|
||||
style={isDefaultCursor ? { cursor: getCursor('nesw-resize', rotation) } : undefined}
|
||||
{...topRightEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideBottomRightCorner,
|
||||
})}
|
||||
data-testid="selection.target.bottom-right"
|
||||
aria-label="bottom-right target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(width - (isSmallX ? targetSizeX : targetSizeX * 1.5))}
|
||||
y={toDomPrecision(height - (isSmallY ? targetSizeY : targetSizeY * 1.5))}
|
||||
width={toDomPrecision(targetSizeX * 3)}
|
||||
height={toDomPrecision(targetSizeY * 3)}
|
||||
style={isDefaultCursor ? { cursor: getCursor('nwse-resize', rotation) } : undefined}
|
||||
{...bottomRightEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideBottomLeftCorner,
|
||||
})}
|
||||
data-testid="selection.target.bottom-left"
|
||||
aria-label="bottom-left target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(0 - (isSmallX ? targetSizeX * 3 : targetSizeX * 1.5))}
|
||||
y={toDomPrecision(height - (isSmallY ? 0 : targetSizeY * 1.5))}
|
||||
width={toDomPrecision(targetSizeX * 3)}
|
||||
height={toDomPrecision(targetSizeY * 3)}
|
||||
style={isDefaultCursor ? { cursor: getCursor('nesw-resize', rotation) } : undefined}
|
||||
{...bottomLeftEvents}
|
||||
/>
|
||||
{/* Resize Handles */}
|
||||
{showResizeHandles && (
|
||||
<>
|
||||
<rect
|
||||
data-testid="selection.resize.top-left"
|
||||
className={classNames('tl-corner-handle', {
|
||||
'tl-hidden': hideTopLeftCorner,
|
||||
})}
|
||||
aria-label="top_left handle"
|
||||
x={toDomPrecision(0 - size / 2)}
|
||||
y={toDomPrecision(0 - size / 2)}
|
||||
width={toDomPrecision(size)}
|
||||
height={toDomPrecision(size)}
|
||||
/>
|
||||
<rect
|
||||
data-testid="selection.resize.top-right"
|
||||
className={classNames('tl-corner-handle', {
|
||||
'tl-hidden': hideTopRightCorner,
|
||||
})}
|
||||
aria-label="top_right handle"
|
||||
x={toDomPrecision(width - size / 2)}
|
||||
y={toDomPrecision(0 - size / 2)}
|
||||
width={toDomPrecision(size)}
|
||||
height={toDomPrecision(size)}
|
||||
/>
|
||||
<rect
|
||||
data-testid="selection.resize.bottom-right"
|
||||
className={classNames('tl-corner-handle', {
|
||||
'tl-hidden': hideBottomRightCorner,
|
||||
})}
|
||||
aria-label="bottom_right handle"
|
||||
x={toDomPrecision(width - size / 2)}
|
||||
y={toDomPrecision(height - size / 2)}
|
||||
width={toDomPrecision(size)}
|
||||
height={toDomPrecision(size)}
|
||||
/>
|
||||
<rect
|
||||
data-testid="selection.resize.bottom-left"
|
||||
className={classNames('tl-corner-handle', {
|
||||
'tl-hidden': hideBottomLeftCorner,
|
||||
})}
|
||||
aria-label="bottom_left handle"
|
||||
x={toDomPrecision(0 - size / 2)}
|
||||
y={toDomPrecision(height - size / 2)}
|
||||
width={toDomPrecision(size)}
|
||||
height={toDomPrecision(size)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{showTextResizeHandles && (
|
||||
<>
|
||||
<rect
|
||||
data-testid="selection.text-resize.left.handle"
|
||||
className="tl-text-handle"
|
||||
aria-label="bottom_left handle"
|
||||
x={toDomPrecision(0 - size / 4)}
|
||||
y={toDomPrecision(height / 2 - textHandleHeight / 2)}
|
||||
rx={size / 4}
|
||||
width={toDomPrecision(size / 2)}
|
||||
height={toDomPrecision(textHandleHeight)}
|
||||
/>
|
||||
<rect
|
||||
data-testid="selection.text-resize.right.handle"
|
||||
className="tl-text-handle"
|
||||
aria-label="bottom_left handle"
|
||||
rx={size / 4}
|
||||
x={toDomPrecision(width - size / 4)}
|
||||
y={toDomPrecision(height / 2 - textHandleHeight / 2)}
|
||||
width={toDomPrecision(size / 2)}
|
||||
height={toDomPrecision(textHandleHeight)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{/* Crop Handles */}
|
||||
{showCropHandles && (
|
||||
<TldrawCropHandles
|
||||
{...{
|
||||
size,
|
||||
width,
|
||||
height,
|
||||
hideAlternateHandles: hideAlternateCropHandles,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
if (onlyShape && shouldDisplayBox) {
|
||||
if (editor.environment.isFirefox && editor.isShapeOfType<TLEmbedShape>(onlyShape, 'embed')) {
|
||||
shouldDisplayBox = false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const showCropHandles =
|
||||
editor.isInAny(
|
||||
'select.pointing_crop_handle',
|
||||
'select.crop.idle',
|
||||
'select.crop.pointing_crop'
|
||||
) &&
|
||||
!isChangingStyle &&
|
||||
!isReadonlyMode
|
||||
|
||||
const shouldDisplayControls =
|
||||
editor.isInAny(
|
||||
'select.idle',
|
||||
'select.pointing_selection',
|
||||
'select.pointing_shape',
|
||||
'select.crop.idle'
|
||||
) &&
|
||||
!isChangingStyle &&
|
||||
!isReadonlyMode
|
||||
|
||||
const showCornerRotateHandles =
|
||||
!isCoarsePointer &&
|
||||
!(isTinyX || isTinyY) &&
|
||||
(shouldDisplayControls || showCropHandles) &&
|
||||
(onlyShape ? !editor.getShapeUtil(onlyShape).hideRotateHandle(onlyShape) : true) &&
|
||||
!isLockedShape
|
||||
|
||||
const showMobileRotateHandle =
|
||||
isCoarsePointer &&
|
||||
(!isSmallX || !isSmallY) &&
|
||||
(shouldDisplayControls || showCropHandles) &&
|
||||
(onlyShape ? !editor.getShapeUtil(onlyShape).hideRotateHandle(onlyShape) : true) &&
|
||||
!isLockedShape
|
||||
|
||||
const showResizeHandles =
|
||||
shouldDisplayControls &&
|
||||
(onlyShape
|
||||
? editor.getShapeUtil(onlyShape).canResize(onlyShape) &&
|
||||
!editor.getShapeUtil(onlyShape).hideResizeHandles(onlyShape)
|
||||
: true) &&
|
||||
!showCropHandles &&
|
||||
!isLockedShape
|
||||
|
||||
const hideAlternateCornerHandles = isTinyX || isTinyY
|
||||
const showOnlyOneHandle = isTinyX && isTinyY
|
||||
const hideAlternateCropHandles = isSmallCropX || isSmallCropY
|
||||
|
||||
const showHandles = showResizeHandles || showCropHandles
|
||||
const hideRotateCornerHandles = !showCornerRotateHandles
|
||||
const hideMobileRotateHandle = !shouldDisplayControls || !showMobileRotateHandle
|
||||
const hideTopLeftCorner = !shouldDisplayControls || !showHandles
|
||||
const hideTopRightCorner = !shouldDisplayControls || !showHandles || hideAlternateCornerHandles
|
||||
const hideBottomLeftCorner = !shouldDisplayControls || !showHandles || hideAlternateCornerHandles
|
||||
const hideBottomRightCorner =
|
||||
!shouldDisplayControls || !showHandles || (showOnlyOneHandle && !showCropHandles)
|
||||
|
||||
// If we're showing crop handles, then show the edges too.
|
||||
// If we're showing resize handles, then show the edges only
|
||||
// if we're not hiding them for some other reason.
|
||||
let hideVerticalEdgeTargets = true
|
||||
// The same logic above applies here, except another nuance is that
|
||||
// we enable resizing for text on mobile (coarse).
|
||||
let hideHorizontalEdgeTargets = true
|
||||
|
||||
if (showCropHandles) {
|
||||
hideVerticalEdgeTargets = hideAlternateCropHandles
|
||||
hideHorizontalEdgeTargets = hideAlternateCropHandles
|
||||
} else if (showResizeHandles) {
|
||||
hideVerticalEdgeTargets = hideAlternateCornerHandles || showOnlyOneHandle || isCoarsePointer
|
||||
const isMobileAndTextShape = isCoarsePointer && onlyShape && onlyShape.type === 'text'
|
||||
hideHorizontalEdgeTargets = hideVerticalEdgeTargets && !isMobileAndTextShape
|
||||
}
|
||||
|
||||
const textHandleHeight = Math.min(24 / zoom, height - targetSizeY * 3)
|
||||
const showTextResizeHandles =
|
||||
shouldDisplayControls &&
|
||||
isCoarsePointer &&
|
||||
onlyShape &&
|
||||
editor.isShapeOfType<TLTextShape>(onlyShape, 'text') &&
|
||||
textHandleHeight * zoom >= 4
|
||||
|
||||
return (
|
||||
<svg className="tl-overlays__item tl-selection__fg" data-testid="selection-foreground">
|
||||
<g ref={rSvg}>
|
||||
{shouldDisplayBox && (
|
||||
<rect
|
||||
className="tl-selection__fg__outline"
|
||||
width={toDomPrecision(width)}
|
||||
height={toDomPrecision(height)}
|
||||
/>
|
||||
)}
|
||||
<RotateCornerHandle
|
||||
data-testid="selection.rotate.top-left"
|
||||
cx={0}
|
||||
cy={0}
|
||||
targetSize={targetSize}
|
||||
corner="top_left_rotate"
|
||||
cursor={isDefaultCursor ? getCursor('nwse-rotate', rotation) : undefined}
|
||||
isHidden={hideRotateCornerHandles}
|
||||
/>
|
||||
<RotateCornerHandle
|
||||
data-testid="selection.rotate.top-right"
|
||||
cx={width + targetSize * 3}
|
||||
cy={0}
|
||||
targetSize={targetSize}
|
||||
corner="top_right_rotate"
|
||||
cursor={isDefaultCursor ? getCursor('nesw-rotate', rotation) : undefined}
|
||||
isHidden={hideRotateCornerHandles}
|
||||
/>
|
||||
<RotateCornerHandle
|
||||
data-testid="selection.rotate.bottom-left"
|
||||
cx={0}
|
||||
cy={height + targetSize * 3}
|
||||
targetSize={targetSize}
|
||||
corner="bottom_left_rotate"
|
||||
cursor={isDefaultCursor ? getCursor('swne-rotate', rotation) : undefined}
|
||||
isHidden={hideRotateCornerHandles}
|
||||
/>
|
||||
<RotateCornerHandle
|
||||
data-testid="selection.rotate.bottom-right"
|
||||
cx={width + targetSize * 3}
|
||||
cy={height + targetSize * 3}
|
||||
targetSize={targetSize}
|
||||
corner="bottom_right_rotate"
|
||||
cursor={isDefaultCursor ? getCursor('senw-rotate', rotation) : undefined}
|
||||
isHidden={hideRotateCornerHandles}
|
||||
/>
|
||||
<MobileRotateHandle
|
||||
data-testid="selection.rotate.mobile"
|
||||
cx={isSmallX ? -targetSize * 1.5 : width / 2}
|
||||
cy={isSmallX ? height / 2 : -targetSize * 1.5}
|
||||
size={size}
|
||||
isHidden={hideMobileRotateHandle}
|
||||
/>
|
||||
{/* Targets */}
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideVerticalEdgeTargets,
|
||||
})}
|
||||
data-testid="selection.resize.top"
|
||||
aria-label="top target"
|
||||
pointerEvents="all"
|
||||
x={0}
|
||||
y={toDomPrecision(0 - (isSmallY ? targetSizeY * 2 : targetSizeY))}
|
||||
width={toDomPrecision(width)}
|
||||
height={toDomPrecision(Math.max(1, targetSizeY * 2))}
|
||||
style={isDefaultCursor ? { cursor: getCursor('ns-resize', rotation) } : undefined}
|
||||
{...topEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideHorizontalEdgeTargets,
|
||||
})}
|
||||
data-testid="selection.resize.right"
|
||||
aria-label="right target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(width - (isSmallX ? 0 : targetSizeX))}
|
||||
y={0}
|
||||
height={toDomPrecision(height)}
|
||||
width={toDomPrecision(Math.max(1, targetSizeX * 2))}
|
||||
style={isDefaultCursor ? { cursor: getCursor('ew-resize', rotation) } : undefined}
|
||||
{...rightEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideVerticalEdgeTargets,
|
||||
})}
|
||||
data-testid="selection.resize.bottom"
|
||||
aria-label="bottom target"
|
||||
pointerEvents="all"
|
||||
x={0}
|
||||
y={toDomPrecision(height - (isSmallY ? 0 : targetSizeY))}
|
||||
width={toDomPrecision(width)}
|
||||
height={toDomPrecision(Math.max(1, targetSizeY * 2))}
|
||||
style={isDefaultCursor ? { cursor: getCursor('ns-resize', rotation) } : undefined}
|
||||
{...bottomEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideHorizontalEdgeTargets,
|
||||
})}
|
||||
data-testid="selection.resize.left"
|
||||
aria-label="left target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(0 - (isSmallX ? targetSizeX * 2 : targetSizeX))}
|
||||
y={0}
|
||||
height={toDomPrecision(height)}
|
||||
width={toDomPrecision(Math.max(1, targetSizeX * 2))}
|
||||
style={isDefaultCursor ? { cursor: getCursor('ew-resize', rotation) } : undefined}
|
||||
{...leftEvents}
|
||||
/>
|
||||
{/* Corner Targets */}
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideTopLeftCorner,
|
||||
})}
|
||||
data-testid="selection.target.top-left"
|
||||
aria-label="top-left target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(0 - (isSmallX ? targetSizeX * 2 : targetSizeX * 1.5))}
|
||||
y={toDomPrecision(0 - (isSmallY ? targetSizeY * 2 : targetSizeY * 1.5))}
|
||||
width={toDomPrecision(targetSizeX * 3)}
|
||||
height={toDomPrecision(targetSizeY * 3)}
|
||||
style={isDefaultCursor ? { cursor: getCursor('nwse-resize', rotation) } : undefined}
|
||||
{...topLeftEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideTopRightCorner,
|
||||
})}
|
||||
data-testid="selection.target.top-right"
|
||||
aria-label="top-right target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(width - (isSmallX ? 0 : targetSizeX * 1.5))}
|
||||
y={toDomPrecision(0 - (isSmallY ? targetSizeY * 2 : targetSizeY * 1.5))}
|
||||
width={toDomPrecision(targetSizeX * 3)}
|
||||
height={toDomPrecision(targetSizeY * 3)}
|
||||
style={isDefaultCursor ? { cursor: getCursor('nesw-resize', rotation) } : undefined}
|
||||
{...topRightEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideBottomRightCorner,
|
||||
})}
|
||||
data-testid="selection.target.bottom-right"
|
||||
aria-label="bottom-right target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(width - (isSmallX ? targetSizeX : targetSizeX * 1.5))}
|
||||
y={toDomPrecision(height - (isSmallY ? targetSizeY : targetSizeY * 1.5))}
|
||||
width={toDomPrecision(targetSizeX * 3)}
|
||||
height={toDomPrecision(targetSizeY * 3)}
|
||||
style={isDefaultCursor ? { cursor: getCursor('nwse-resize', rotation) } : undefined}
|
||||
{...bottomRightEvents}
|
||||
/>
|
||||
<rect
|
||||
className={classNames('tl-transparent', {
|
||||
'tl-hidden': hideBottomLeftCorner,
|
||||
})}
|
||||
data-testid="selection.target.bottom-left"
|
||||
aria-label="bottom-left target"
|
||||
pointerEvents="all"
|
||||
x={toDomPrecision(0 - (isSmallX ? targetSizeX * 3 : targetSizeX * 1.5))}
|
||||
y={toDomPrecision(height - (isSmallY ? 0 : targetSizeY * 1.5))}
|
||||
width={toDomPrecision(targetSizeX * 3)}
|
||||
height={toDomPrecision(targetSizeY * 3)}
|
||||
style={isDefaultCursor ? { cursor: getCursor('nesw-resize', rotation) } : undefined}
|
||||
{...bottomLeftEvents}
|
||||
/>
|
||||
{/* Resize Handles */}
|
||||
{showResizeHandles && (
|
||||
<>
|
||||
<rect
|
||||
data-testid="selection.resize.top-left"
|
||||
className={classNames('tl-corner-handle', {
|
||||
'tl-hidden': hideTopLeftCorner,
|
||||
})}
|
||||
aria-label="top_left handle"
|
||||
x={toDomPrecision(0 - size / 2)}
|
||||
y={toDomPrecision(0 - size / 2)}
|
||||
width={toDomPrecision(size)}
|
||||
height={toDomPrecision(size)}
|
||||
/>
|
||||
<rect
|
||||
data-testid="selection.resize.top-right"
|
||||
className={classNames('tl-corner-handle', {
|
||||
'tl-hidden': hideTopRightCorner,
|
||||
})}
|
||||
aria-label="top_right handle"
|
||||
x={toDomPrecision(width - size / 2)}
|
||||
y={toDomPrecision(0 - size / 2)}
|
||||
width={toDomPrecision(size)}
|
||||
height={toDomPrecision(size)}
|
||||
/>
|
||||
<rect
|
||||
data-testid="selection.resize.bottom-right"
|
||||
className={classNames('tl-corner-handle', {
|
||||
'tl-hidden': hideBottomRightCorner,
|
||||
})}
|
||||
aria-label="bottom_right handle"
|
||||
x={toDomPrecision(width - size / 2)}
|
||||
y={toDomPrecision(height - size / 2)}
|
||||
width={toDomPrecision(size)}
|
||||
height={toDomPrecision(size)}
|
||||
/>
|
||||
<rect
|
||||
data-testid="selection.resize.bottom-left"
|
||||
className={classNames('tl-corner-handle', {
|
||||
'tl-hidden': hideBottomLeftCorner,
|
||||
})}
|
||||
aria-label="bottom_left handle"
|
||||
x={toDomPrecision(0 - size / 2)}
|
||||
y={toDomPrecision(height - size / 2)}
|
||||
width={toDomPrecision(size)}
|
||||
height={toDomPrecision(size)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{showTextResizeHandles && (
|
||||
<>
|
||||
<rect
|
||||
data-testid="selection.text-resize.left.handle"
|
||||
className="tl-text-handle"
|
||||
aria-label="bottom_left handle"
|
||||
x={toDomPrecision(0 - size / 4)}
|
||||
y={toDomPrecision(height / 2 - textHandleHeight / 2)}
|
||||
rx={size / 4}
|
||||
width={toDomPrecision(size / 2)}
|
||||
height={toDomPrecision(textHandleHeight)}
|
||||
/>
|
||||
<rect
|
||||
data-testid="selection.text-resize.right.handle"
|
||||
className="tl-text-handle"
|
||||
aria-label="bottom_left handle"
|
||||
rx={size / 4}
|
||||
x={toDomPrecision(width - size / 4)}
|
||||
y={toDomPrecision(height / 2 - textHandleHeight / 2)}
|
||||
width={toDomPrecision(size / 2)}
|
||||
height={toDomPrecision(textHandleHeight)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{/* Crop Handles */}
|
||||
{showCropHandles && (
|
||||
<TldrawCropHandles
|
||||
{...{
|
||||
size,
|
||||
width,
|
||||
height,
|
||||
hideAlternateHandles: hideAlternateCropHandles,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
})
|
||||
|
||||
export const RotateCornerHandle = function RotateCornerHandle({
|
||||
cx,
|
||||
|
|
|
@ -79,6 +79,16 @@
|
|||
margin-left: var(--space-2);
|
||||
}
|
||||
|
||||
.tlui-button[data-state='hinted']::after {
|
||||
background-color: var(--color-hint);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tlui-button[data-state='hinted']:not(:disabled, :focus-visible):active:after {
|
||||
background: var(--color-hint);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.tlui-button::after {
|
||||
background-color: var(--color-muted-2);
|
||||
|
@ -136,7 +146,6 @@
|
|||
.tlui-button__menu {
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
min-width: 128px;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
margin: -4px 0px;
|
||||
|
@ -929,6 +938,10 @@
|
|||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.tlui-menu__group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tlui-menu__group:empty {
|
||||
display: none;
|
||||
}
|
||||
|
@ -952,25 +965,22 @@
|
|||
|
||||
/* Menu Sizes */
|
||||
|
||||
.tlui-menu[data-size='large'] > .tlui-menu__group,
|
||||
.tlui-menu__submenu__content[data-size='large'] > .tlui-menu__group {
|
||||
.tlui-menu[data-size='large'] > .tlui-menu__group {
|
||||
min-width: initial;
|
||||
}
|
||||
|
||||
.tlui-menu[data-size='medium'] > .tlui-menu__group,
|
||||
.tlui-menu__submenu__content[data-size='medium'] > .tlui-menu__group {
|
||||
.tlui-menu[data-size='medium'] > .tlui-menu__group {
|
||||
min-width: 144px;
|
||||
}
|
||||
|
||||
.tlui-menu[data-size='small'] > .tlui-menu__group,
|
||||
.tlui-menu__submenu__content[data-size='small'] > .tlui-menu__group {
|
||||
min-width: 96px;
|
||||
}
|
||||
|
||||
.tlui-menu[data-size='tiny'] > .tlui-menu__group,
|
||||
.tlui-menu__submenu__content[data-size='tiny'] > .tlui-menu__group {
|
||||
.tlui-menu[data-size='small'] > .tlui-menu__group {
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
.tlui-menu[data-size='tiny'] > .tlui-menu__group {
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
/* ------------------ Actions Menu ------------------ */
|
||||
|
||||
.tlui-actions-menu {
|
||||
|
|
|
@ -21,7 +21,6 @@ import { useNativeClipboardEvents } from './hooks/useClipboardEvents'
|
|||
import { useEditorEvents } from './hooks/useEditorEvents'
|
||||
import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
|
||||
import { useReadonly } from './hooks/useReadonly'
|
||||
import { useRelevantStyles } from './hooks/useRevelantStyles'
|
||||
import { useTranslation } from './hooks/useTranslation/useTranslation'
|
||||
|
||||
/**
|
||||
|
@ -159,7 +158,7 @@ const TldrawUiContent = React.memo(function TldrawUI() {
|
|||
<div className="tlui-layout__top__right">
|
||||
{SharePanel && <SharePanel />}
|
||||
{StylePanel && breakpoint >= PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && (
|
||||
<_StylePanel />
|
||||
<StylePanel />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -181,11 +180,3 @@ const TldrawUiContent = React.memo(function TldrawUI() {
|
|||
</ToastProvider>
|
||||
)
|
||||
})
|
||||
|
||||
function _StylePanel() {
|
||||
const { StylePanel } = useTldrawUiComponents()
|
||||
const relevantStyles = useRelevantStyles()
|
||||
|
||||
if (!StylePanel) return null
|
||||
return <StylePanel relevantStyles={relevantStyles} />
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as _ContextMenu from '@radix-ui/react-context-menu'
|
||||
import { preventDefault, useContainer, useEditor } from '@tldraw/editor'
|
||||
import { preventDefault, useContainer, useEditor, useEditorComponents } from '@tldraw/editor'
|
||||
import { memo, useCallback } from 'react'
|
||||
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
|
@ -7,17 +7,17 @@ import { DefaultContextMenuContent } from './DefaultContextMenuContent'
|
|||
|
||||
/** @public */
|
||||
export interface TLUiContextMenuProps {
|
||||
canvas: any
|
||||
children?: any
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultContextMenu = memo(function DefaultContextMenu({
|
||||
canvas,
|
||||
children,
|
||||
}: TLUiContextMenuProps) {
|
||||
const editor = useEditor()
|
||||
|
||||
const { Canvas } = useEditorComponents()
|
||||
|
||||
const cb = useCallback(
|
||||
(isOpen: boolean) => {
|
||||
if (!isOpen) {
|
||||
|
@ -68,7 +68,7 @@ export const DefaultContextMenu = memo(function DefaultContextMenu({
|
|||
return (
|
||||
<_ContextMenu.Root dir="ltr" onOpenChange={handleOpenChange} modal={false}>
|
||||
<_ContextMenu.Trigger onContextMenu={undefined} dir="ltr">
|
||||
{canvas}
|
||||
{Canvas ? <Canvas /> : null}
|
||||
</_ContextMenu.Trigger>
|
||||
{isOpen && (
|
||||
<_ContextMenu.Portal container={container}>
|
||||
|
|
|
@ -22,14 +22,14 @@ export function MobileStylePanel() {
|
|||
const msg = useTranslation()
|
||||
|
||||
const relevantStyles = useRelevantStyles()
|
||||
const color = relevantStyles?.styles.get(DefaultColorStyle)
|
||||
const color = relevantStyles?.get(DefaultColorStyle)
|
||||
const theme = getDefaultColorTheme({ isDarkMode: editor.user.getIsDarkMode() })
|
||||
const currentColor = (
|
||||
color?.type === 'shared' ? theme[color.value as TLDefaultColorStyle] : theme.black
|
||||
).solid
|
||||
|
||||
const disableStylePanel = useValue(
|
||||
'isHandOrEraserToolActive',
|
||||
'disable style panel',
|
||||
() => editor.isInAny('hand', 'zoom', 'eraser', 'laser'),
|
||||
[editor]
|
||||
)
|
||||
|
@ -64,16 +64,8 @@ export function MobileStylePanel() {
|
|||
</TldrawUiButton>
|
||||
</TldrawUiPopoverTrigger>
|
||||
<TldrawUiPopoverContent side="top" align="end">
|
||||
<_StylePanel />
|
||||
{StylePanel && <StylePanel isMobile />}
|
||||
</TldrawUiPopoverContent>
|
||||
</TldrawUiPopover>
|
||||
)
|
||||
}
|
||||
|
||||
function _StylePanel() {
|
||||
const { StylePanel } = useTldrawUiComponents()
|
||||
const relevantStyles = useRelevantStyles()
|
||||
|
||||
if (!StylePanel) return null
|
||||
return <StylePanel relevantStyles={relevantStyles} isMobile />
|
||||
}
|
||||
|
|
|
@ -1,35 +1,31 @@
|
|||
import { ReadonlySharedStyleMap, SharedStyle, useEditor } from '@tldraw/editor'
|
||||
import { useEditor } from '@tldraw/editor'
|
||||
import classNames from 'classnames'
|
||||
import { memo, useCallback } from 'react'
|
||||
import { useRelevantStyles } from '../../hooks/useRevelantStyles'
|
||||
import { DefaultStylePanelContent } from './DefaultStylePanelContent'
|
||||
|
||||
/** @public */
|
||||
export interface TLUiStylePanelProps {
|
||||
isMobile?: boolean
|
||||
children?: any
|
||||
relevantStyles: {
|
||||
styles: ReadonlySharedStyleMap
|
||||
opacity: SharedStyle<number>
|
||||
} | null
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const DefaultStylePanel = memo(function DefaultStylePanel({
|
||||
isMobile,
|
||||
children,
|
||||
relevantStyles,
|
||||
}: TLUiStylePanelProps) {
|
||||
const editor = useEditor()
|
||||
|
||||
const styles = useRelevantStyles()
|
||||
|
||||
const handlePointerOut = useCallback(() => {
|
||||
if (!isMobile) {
|
||||
editor.updateInstanceState({ isChangingStyle: false })
|
||||
}
|
||||
}, [editor, isMobile])
|
||||
|
||||
if (!relevantStyles) return null
|
||||
|
||||
const content = children ?? <DefaultStylePanelContent relevantStyles={relevantStyles} />
|
||||
const content = children ?? <DefaultStylePanelContent styles={styles} />
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -11,10 +11,10 @@ import {
|
|||
GeoShapeGeoStyle,
|
||||
LineShapeSplineStyle,
|
||||
ReadonlySharedStyleMap,
|
||||
SharedStyle,
|
||||
StyleProp,
|
||||
minBy,
|
||||
useEditor,
|
||||
useValue,
|
||||
} from '@tldraw/editor'
|
||||
import React from 'react'
|
||||
import { STYLES } from '../../../styles'
|
||||
|
@ -30,14 +30,13 @@ import { DropdownPicker } from './DropdownPicker'
|
|||
|
||||
/** @public */
|
||||
export type TLUiStylePanelContentProps = {
|
||||
relevantStyles: ReturnType<typeof useRelevantStyles>
|
||||
styles: ReturnType<typeof useRelevantStyles>
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function DefaultStylePanelContent({ relevantStyles }: TLUiStylePanelContentProps) {
|
||||
if (!relevantStyles) return null
|
||||
export function DefaultStylePanelContent({ styles }: TLUiStylePanelContentProps) {
|
||||
if (!styles) return null
|
||||
|
||||
const { styles, opacity } = relevantStyles
|
||||
const geo = styles.get(GeoShapeGeoStyle)
|
||||
const arrowheadEnd = styles.get(ArrowShapeArrowheadEndStyle)
|
||||
const arrowheadStart = styles.get(ArrowShapeArrowheadStartStyle)
|
||||
|
@ -51,7 +50,7 @@ export function DefaultStylePanelContent({ relevantStyles }: TLUiStylePanelConte
|
|||
|
||||
return (
|
||||
<>
|
||||
<CommonStylePickerSet styles={styles} opacity={opacity} />
|
||||
<CommonStylePickerSet styles={styles} />
|
||||
{!hideText && <TextStylePickerSet styles={styles} />}
|
||||
{!(hideGeo && hideArrowHeads && hideSpline) && (
|
||||
<div className="tlui-style-panel__section" aria-label="style panel styles">
|
||||
|
@ -68,52 +67,28 @@ function useStyleChangeCallback() {
|
|||
const editor = useEditor()
|
||||
const trackEvent = useUiEvents()
|
||||
|
||||
return React.useMemo(() => {
|
||||
return function handleStyleChange<T>(style: StyleProp<T>, value: T, squashing: boolean) {
|
||||
editor.batch(() => {
|
||||
if (editor.isIn('select')) {
|
||||
editor.setStyleForSelectedShapes(style, value, { squashing })
|
||||
}
|
||||
editor.setStyleForNextShapes(style, value, { squashing })
|
||||
editor.updateInstanceState({ isChangingStyle: true })
|
||||
})
|
||||
return React.useMemo(
|
||||
() =>
|
||||
function handleStyleChange<T>(style: StyleProp<T>, value: T, squashing: boolean) {
|
||||
editor.batch(() => {
|
||||
if (editor.isIn('select')) {
|
||||
editor.setStyleForSelectedShapes(style, value, { squashing })
|
||||
}
|
||||
editor.setStyleForNextShapes(style, value, { squashing })
|
||||
editor.updateInstanceState({ isChangingStyle: true })
|
||||
})
|
||||
|
||||
trackEvent('set-style', { source: 'style-panel', id: style.id, value: value as string })
|
||||
}
|
||||
}, [editor, trackEvent])
|
||||
trackEvent('set-style', { source: 'style-panel', id: style.id, value: value as string })
|
||||
},
|
||||
[editor, trackEvent]
|
||||
)
|
||||
}
|
||||
|
||||
const tldrawSupportedOpacities = [0.1, 0.25, 0.5, 0.75, 1] as const
|
||||
|
||||
function CommonStylePickerSet({
|
||||
styles,
|
||||
opacity,
|
||||
}: {
|
||||
styles: ReadonlySharedStyleMap
|
||||
opacity: SharedStyle<number>
|
||||
}) {
|
||||
const editor = useEditor()
|
||||
const trackEvent = useUiEvents()
|
||||
function CommonStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
|
||||
const msg = useTranslation()
|
||||
|
||||
const handleValueChange = useStyleChangeCallback()
|
||||
|
||||
const handleOpacityValueChange = React.useCallback(
|
||||
(value: number, ephemeral: boolean) => {
|
||||
const item = tldrawSupportedOpacities[value]
|
||||
editor.batch(() => {
|
||||
if (editor.isIn('select')) {
|
||||
editor.setOpacityForSelectedShapes(item, { ephemeral })
|
||||
}
|
||||
editor.setOpacityForNextShapes(item, { ephemeral })
|
||||
editor.updateInstanceState({ isChangingStyle: true })
|
||||
})
|
||||
|
||||
trackEvent('set-style', { source: 'style-panel', id: 'opacity', value })
|
||||
},
|
||||
[editor, trackEvent]
|
||||
)
|
||||
|
||||
const color = styles.get(DefaultColorStyle)
|
||||
const fill = styles.get(DefaultFillStyle)
|
||||
const dash = styles.get(DefaultDashStyle)
|
||||
|
@ -121,15 +96,6 @@ function CommonStylePickerSet({
|
|||
|
||||
const showPickers = fill !== undefined || dash !== undefined || size !== undefined
|
||||
|
||||
const opacityIndex =
|
||||
opacity.type === 'mixed'
|
||||
? -1
|
||||
: tldrawSupportedOpacities.indexOf(
|
||||
minBy(tldrawSupportedOpacities, (supportedOpacity) =>
|
||||
Math.abs(supportedOpacity - opacity.value)
|
||||
)!
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
@ -148,18 +114,7 @@ function CommonStylePickerSet({
|
|||
onValueChange={handleValueChange}
|
||||
/>
|
||||
)}
|
||||
{opacity === undefined ? null : (
|
||||
<TldrawUiSlider
|
||||
data-testid="style.opacity"
|
||||
value={opacityIndex >= 0 ? opacityIndex : tldrawSupportedOpacities.length - 1}
|
||||
label={
|
||||
opacity.type === 'mixed' ? 'style-panel.mixed' : `opacity-style.${opacity.value}`
|
||||
}
|
||||
onValueChange={handleOpacityValueChange}
|
||||
steps={tldrawSupportedOpacities.length - 1}
|
||||
title={msg('style-panel.opacity')}
|
||||
/>
|
||||
)}
|
||||
<OpacitySlider />
|
||||
</div>
|
||||
{showPickers && (
|
||||
<div className="tlui-style-panel__section" aria-label="style panel styles">
|
||||
|
@ -331,3 +286,50 @@ function ArrowheadStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap })
|
|||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const tldrawSupportedOpacities = [0.1, 0.25, 0.5, 0.75, 1] as const
|
||||
|
||||
function OpacitySlider() {
|
||||
const editor = useEditor()
|
||||
const opacity = useValue('opacity', () => editor.getSharedOpacity(), [editor])
|
||||
const trackEvent = useUiEvents()
|
||||
const msg = useTranslation()
|
||||
|
||||
const handleOpacityValueChange = React.useCallback(
|
||||
(value: number, ephemeral: boolean) => {
|
||||
const item = tldrawSupportedOpacities[value]
|
||||
editor.batch(() => {
|
||||
if (editor.isIn('select')) {
|
||||
editor.setOpacityForSelectedShapes(item, { ephemeral })
|
||||
}
|
||||
editor.setOpacityForNextShapes(item, { ephemeral })
|
||||
editor.updateInstanceState({ isChangingStyle: true })
|
||||
})
|
||||
|
||||
trackEvent('set-style', { source: 'style-panel', id: 'opacity', value })
|
||||
},
|
||||
[editor, trackEvent]
|
||||
)
|
||||
|
||||
if (opacity === undefined) return null
|
||||
|
||||
const opacityIndex =
|
||||
opacity.type === 'mixed'
|
||||
? -1
|
||||
: tldrawSupportedOpacities.indexOf(
|
||||
minBy(tldrawSupportedOpacities, (supportedOpacity) =>
|
||||
Math.abs(supportedOpacity - opacity.value)
|
||||
)!
|
||||
)
|
||||
|
||||
return (
|
||||
<TldrawUiSlider
|
||||
data-testid="style.opacity"
|
||||
value={opacityIndex >= 0 ? opacityIndex : tldrawSupportedOpacities.length - 1}
|
||||
label={opacity.type === 'mixed' ? 'style-panel.mixed' : `opacity-style.${opacity.value}`}
|
||||
onValueChange={handleOpacityValueChange}
|
||||
steps={tldrawSupportedOpacities.length - 1}
|
||||
title={msg('style-panel.opacity')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -282,7 +282,7 @@ export function ArrangeMenuSubmenu() {
|
|||
if (!(twoSelected || onlyFlippableShapeSelected)) return null
|
||||
|
||||
return (
|
||||
<TldrawUiMenuSubmenu id="arrange" label="context-menu.arrange">
|
||||
<TldrawUiMenuSubmenu id="arrange" label="context-menu.arrange" size="small">
|
||||
{twoSelected && (
|
||||
<TldrawUiMenuGroup id="align">
|
||||
<TldrawUiMenuItem {...actions['align-left']} />
|
||||
|
|
|
@ -139,6 +139,7 @@ export type TLUiDropdownMenuSubContentProps = {
|
|||
id?: string
|
||||
alignOffset?: number
|
||||
sideOffset?: number
|
||||
size?: 'tiny' | 'small' | 'medium' | 'wide'
|
||||
children: any
|
||||
}
|
||||
|
||||
|
@ -146,6 +147,7 @@ export type TLUiDropdownMenuSubContentProps = {
|
|||
export function TldrawUiDropdownMenuSubContent({
|
||||
alignOffset = -1,
|
||||
sideOffset = -4,
|
||||
size = 'small',
|
||||
children,
|
||||
}: TLUiDropdownMenuSubContentProps) {
|
||||
const container = useContainer()
|
||||
|
@ -156,6 +158,7 @@ export function TldrawUiDropdownMenuSubContent({
|
|||
alignOffset={alignOffset}
|
||||
sideOffset={sideOffset}
|
||||
collisionPadding={4}
|
||||
data-size={size}
|
||||
>
|
||||
{children}
|
||||
</_DropdownMenu.SubContent>
|
||||
|
@ -166,16 +169,12 @@ export function TldrawUiDropdownMenuSubContent({
|
|||
/** @public */
|
||||
export type TLUiDropdownMenuGroupProps = {
|
||||
children: any
|
||||
size?: 'tiny' | 'small' | 'medium' | 'wide'
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function TldrawUiDropdownMenuGroup({
|
||||
children,
|
||||
size = 'medium',
|
||||
}: TLUiDropdownMenuGroupProps) {
|
||||
export function TldrawUiDropdownMenuGroup({ children }: TLUiDropdownMenuGroupProps) {
|
||||
return (
|
||||
<_DropdownMenu.Group dir="ltr" className="tlui-menu__group" data-size={size}>
|
||||
<_DropdownMenu.Group dir="ltr" className="tlui-menu__group">
|
||||
{children}
|
||||
</_DropdownMenu.Group>
|
||||
)
|
||||
|
|
|
@ -12,12 +12,11 @@ export type TLUiMenuGroupProps<TranslationKey extends string = string> = {
|
|||
* The label to display on the item. If it's a string, it will be translated. If it's an object, the keys will be used as the language keys and the values will be translated.
|
||||
*/
|
||||
label?: TranslationKey | { [key: string]: TranslationKey }
|
||||
small?: boolean
|
||||
children?: any
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function TldrawUiMenuGroup({ id, label, small = false, children }: TLUiMenuGroupProps) {
|
||||
export function TldrawUiMenuGroup({ id, label, children }: TLUiMenuGroupProps) {
|
||||
const { type: menuType, sourceId } = useTldrawUiMenuContext()
|
||||
const msg = useTranslation()
|
||||
const labelToUse = unwrapLabel(label, menuType)
|
||||
|
@ -33,10 +32,7 @@ export function TldrawUiMenuGroup({ id, label, small = false, children }: TLUiMe
|
|||
}
|
||||
case 'menu': {
|
||||
return (
|
||||
<TldrawUiDropdownMenuGroup
|
||||
data-testid={`${sourceId}-group.${id}`}
|
||||
size={small ? 'tiny' : 'medium'}
|
||||
>
|
||||
<TldrawUiDropdownMenuGroup data-testid={`${sourceId}-group.${id}`}>
|
||||
{children}
|
||||
</TldrawUiDropdownMenuGroup>
|
||||
)
|
||||
|
@ -46,7 +42,6 @@ export function TldrawUiMenuGroup({ id, label, small = false, children }: TLUiMe
|
|||
<ContextMenuGroup
|
||||
dir="ltr"
|
||||
className="tlui-menu__group"
|
||||
data-size={small ? 'tiny' : 'medium'}
|
||||
data-testid={`${sourceId}-group.${id}`}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -24,7 +24,7 @@ export type TLUiMenuSubmenuProps<Translation extends string = string> = {
|
|||
label?: Translation | { [key: string]: Translation }
|
||||
disabled?: boolean
|
||||
children: any
|
||||
size?: 'tiny' | 'small' | 'medium' | 'large'
|
||||
size?: 'tiny' | 'small' | 'medium' | 'wide'
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
@ -32,7 +32,7 @@ export function TldrawUiMenuSubmenu<Translation extends string = string>({
|
|||
id,
|
||||
disabled = false,
|
||||
label,
|
||||
size,
|
||||
size = 'small',
|
||||
children,
|
||||
}: TLUiMenuSubmenuProps<Translation>) {
|
||||
const { type: menuType, sourceId } = useTldrawUiMenuContext()
|
||||
|
@ -55,7 +55,7 @@ export function TldrawUiMenuSubmenu<Translation extends string = string>({
|
|||
label={labelStr!}
|
||||
title={labelStr!}
|
||||
/>
|
||||
<TldrawUiDropdownMenuSubContent id={`${sourceId}-sub-content.${id}`} data-size={size}>
|
||||
<TldrawUiDropdownMenuSubContent id={`${sourceId}-sub-content.${id}`} size={size}>
|
||||
{children}
|
||||
</TldrawUiDropdownMenuSubContent>
|
||||
</TldrawUiDropdownMenuSub>
|
||||
|
|
|
@ -4,18 +4,20 @@ import {
|
|||
DefaultFillStyle,
|
||||
DefaultSizeStyle,
|
||||
ReadonlySharedStyleMap,
|
||||
SharedStyle,
|
||||
SharedStyleMap,
|
||||
useEditor,
|
||||
useValue,
|
||||
} from '@tldraw/editor'
|
||||
|
||||
const selectToolStyles = [DefaultColorStyle, DefaultDashStyle, DefaultFillStyle, DefaultSizeStyle]
|
||||
const selectToolStyles = Object.freeze([
|
||||
DefaultColorStyle,
|
||||
DefaultDashStyle,
|
||||
DefaultFillStyle,
|
||||
DefaultSizeStyle,
|
||||
])
|
||||
|
||||
export function useRelevantStyles(): {
|
||||
styles: ReadonlySharedStyleMap
|
||||
opacity: SharedStyle<number>
|
||||
} | null {
|
||||
/** @public */
|
||||
export function useRelevantStyles(stylesToCheck = selectToolStyles): ReadonlySharedStyleMap | null {
|
||||
const editor = useEditor()
|
||||
return useValue(
|
||||
'getRelevantStyles',
|
||||
|
@ -25,13 +27,13 @@ export function useRelevantStyles(): {
|
|||
editor.getSelectedShapeIds().length > 0 || !!editor.root.getCurrent()?.shapeType
|
||||
|
||||
if (styles.size === 0 && editor.isIn('select') && editor.getSelectedShapeIds().length === 0) {
|
||||
for (const style of selectToolStyles) {
|
||||
for (const style of stylesToCheck) {
|
||||
styles.applyValue(style, editor.getStyleForNextShape(style))
|
||||
}
|
||||
}
|
||||
|
||||
if (styles.size === 0 && !hasShape) return null
|
||||
return { styles, opacity: editor.getSharedOpacity() }
|
||||
return styles
|
||||
},
|
||||
[editor]
|
||||
)
|
||||
|
|
|
@ -2,7 +2,6 @@ import { act, render, screen } from '@testing-library/react'
|
|||
import {
|
||||
BaseBoxShapeTool,
|
||||
BaseBoxShapeUtil,
|
||||
Canvas,
|
||||
Editor,
|
||||
HTMLContainer,
|
||||
TLBaseShape,
|
||||
|
@ -26,13 +25,10 @@ function checkAllShapes(editor: Editor, shapes: string[]) {
|
|||
describe('<TldrawEditor />', () => {
|
||||
it('Renders without crashing', async () => {
|
||||
await renderTldrawComponent(
|
||||
<TldrawEditor tools={defaultTools} autoFocus initialState="select">
|
||||
<div data-testid="canvas-1" />
|
||||
<Canvas />
|
||||
</TldrawEditor>,
|
||||
<TldrawEditor tools={defaultTools} autoFocus initialState="select" />,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
await screen.findByTestId('canvas-1')
|
||||
await screen.findByTestId('canvas')
|
||||
})
|
||||
|
||||
it('Creates its own store with core shapes', async () => {
|
||||
|
@ -45,12 +41,9 @@ describe('<TldrawEditor />', () => {
|
|||
initialState="select"
|
||||
tools={defaultTools}
|
||||
autoFocus
|
||||
>
|
||||
<div data-testid="canvas-1" />
|
||||
</TldrawEditor>,
|
||||
/>,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
await screen.findByTestId('canvas-1')
|
||||
checkAllShapes(editor!, ['group'])
|
||||
})
|
||||
|
||||
|
@ -65,13 +58,9 @@ describe('<TldrawEditor />', () => {
|
|||
editor = e
|
||||
}}
|
||||
autoFocus
|
||||
>
|
||||
<div data-testid="canvas-1" />
|
||||
<Canvas />
|
||||
</TldrawEditor>,
|
||||
/>,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
await screen.findByTestId('canvas-1')
|
||||
expect(editor!).toBeTruthy()
|
||||
|
||||
checkAllShapes(editor!, ['group'])
|
||||
|
@ -88,13 +77,9 @@ describe('<TldrawEditor />', () => {
|
|||
expect(editor.store).toBe(store)
|
||||
}}
|
||||
autoFocus
|
||||
>
|
||||
<div data-testid="canvas-1" />
|
||||
<Canvas />
|
||||
</TldrawEditor>,
|
||||
/>,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
await screen.findByTestId('canvas-1')
|
||||
})
|
||||
|
||||
it('throws if the store has different shapes to the ones passed in', async () => {
|
||||
|
@ -148,11 +133,8 @@ describe('<TldrawEditor />', () => {
|
|||
store={initialStore}
|
||||
onMount={onMount}
|
||||
autoFocus
|
||||
>
|
||||
<div data-testid="canvas-1" />
|
||||
</TldrawEditor>
|
||||
/>
|
||||
)
|
||||
await screen.findByTestId('canvas-1')
|
||||
const initialEditor = onMount.mock.lastCall[0]
|
||||
jest.spyOn(initialEditor, 'dispose')
|
||||
expect(initialEditor.store).toBe(initialStore)
|
||||
|
@ -164,11 +146,8 @@ describe('<TldrawEditor />', () => {
|
|||
store={initialStore}
|
||||
onMount={onMount}
|
||||
autoFocus
|
||||
>
|
||||
<div data-testid="canvas-2" />
|
||||
</TldrawEditor>
|
||||
/>
|
||||
)
|
||||
await screen.findByTestId('canvas-2')
|
||||
// not called again:
|
||||
expect(onMount).toHaveBeenCalledTimes(1)
|
||||
// re-render with a new store:
|
||||
|
@ -180,11 +159,8 @@ describe('<TldrawEditor />', () => {
|
|||
store={newStore}
|
||||
onMount={onMount}
|
||||
autoFocus
|
||||
>
|
||||
<div data-testid="canvas-3" />
|
||||
</TldrawEditor>
|
||||
/>
|
||||
)
|
||||
await screen.findByTestId('canvas-3')
|
||||
expect(initialEditor.dispose).toHaveBeenCalledTimes(1)
|
||||
expect(onMount).toHaveBeenCalledTimes(2)
|
||||
expect(onMount.mock.lastCall[0].store).toBe(newStore)
|
||||
|
@ -201,12 +177,9 @@ describe('<TldrawEditor />', () => {
|
|||
onMount={(editorApp) => {
|
||||
editor = editorApp
|
||||
}}
|
||||
>
|
||||
<Canvas />
|
||||
<div data-testid="canvas-1" />
|
||||
</TldrawEditor>
|
||||
/>,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
await screen.findByTestId('canvas-1')
|
||||
|
||||
expect(editor).toBeTruthy()
|
||||
await act(async () => {
|
||||
|
@ -325,13 +298,9 @@ describe('Custom shapes', () => {
|
|||
onMount={(editorApp) => {
|
||||
editor = editorApp
|
||||
}}
|
||||
>
|
||||
<Canvas />
|
||||
<div data-testid="canvas-1" />
|
||||
</TldrawEditor>,
|
||||
/>,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
await screen.findByTestId('canvas-1')
|
||||
|
||||
expect(editor).toBeTruthy()
|
||||
await act(async () => {
|
||||
|
|
|
@ -165,12 +165,10 @@ describe('Locked shapes', () => {
|
|||
describe('Unlocking', () => {
|
||||
it('Can unlock shapes', () => {
|
||||
editor.setSelectedShapes([ids.lockedShapeA, ids.lockedShapeB])
|
||||
let lockedStatus = [ids.lockedShapeA, ids.lockedShapeB].map(
|
||||
(id) => editor.getShape(id)!.isLocked
|
||||
)
|
||||
expect(lockedStatus).toStrictEqual([true, true])
|
||||
const getLockedStatus = () =>
|
||||
[ids.lockedShapeA, ids.lockedShapeB].map((id) => editor.getShape(id)!.isLocked)
|
||||
expect(getLockedStatus()).toStrictEqual([true, true])
|
||||
editor.toggleLock(editor.getSelectedShapeIds())
|
||||
lockedStatus = [ids.lockedShapeA, ids.lockedShapeB].map((id) => editor.getShape(id)!.isLocked)
|
||||
expect(lockedStatus).toStrictEqual([false, false])
|
||||
expect(getLockedStatus()).toStrictEqual([false, false])
|
||||
})
|
||||
})
|
||||
|
|
|
@ -15,7 +15,7 @@ import { ReactElement } from 'react'
|
|||
*/
|
||||
export async function renderTldrawComponent(
|
||||
element: ReactElement,
|
||||
{ waitForPatterns = true } = {}
|
||||
{ waitForPatterns }: { waitForPatterns: boolean }
|
||||
) {
|
||||
const result = render(element)
|
||||
if (waitForPatterns) await result.findByTestId('ready-pattern-fill-defs')
|
||||
|
@ -24,7 +24,7 @@ export async function renderTldrawComponent(
|
|||
|
||||
export async function renderTldrawComponentWithEditor(
|
||||
cb: (onMount: (editor: Editor) => void) => ReactElement,
|
||||
opts?: { waitForPatterns?: boolean }
|
||||
opts: { waitForPatterns: boolean }
|
||||
) {
|
||||
const editorPromise = promiseWithResolve<Editor>()
|
||||
const element = cb((editor) => {
|
||||
|
|
|
@ -10,7 +10,8 @@ it('opens on right-click', async () => {
|
|||
onMount={(editor) => {
|
||||
editor.createShape({ id: createShapeId(), type: 'geo' })
|
||||
}}
|
||||
/>
|
||||
/>,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
const canvas = await screen.findByTestId('canvas')
|
||||
|
||||
|
@ -38,7 +39,8 @@ it('tunnels context menu', async () => {
|
|||
editor.createShape({ id: createShapeId(), type: 'geo' })
|
||||
}}
|
||||
components={components}
|
||||
/>
|
||||
/>,
|
||||
{ waitForPatterns: false }
|
||||
)
|
||||
|
||||
const canvas = await screen.findByTestId('canvas')
|
||||
|
|
Loading…
Reference in a new issue