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