Move constants to options prop (#3799)
Another go at #3628 & #3783. This moves (most) constants into `editor.options`, configurable by the `options` prop on the tldraw component. ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `feature` — New feature ### Release Notes You can now override many options which were previously hard-coded constants. Pass an `options` prop into the tldraw component to change the maximum number of pages, grid steps, or other previously hard-coded values. See `TldrawOptions` for more
This commit is contained in:
parent
19f8d4248c
commit
a457a39081
37 changed files with 347 additions and 267 deletions
|
@ -0,0 +1,15 @@
|
||||||
|
import { Tldraw, TldrawOptions } from 'tldraw'
|
||||||
|
import 'tldraw/tldraw.css'
|
||||||
|
|
||||||
|
const options: Partial<TldrawOptions> = {
|
||||||
|
maxPages: 3,
|
||||||
|
animationMediumMs: 5000,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CustomOptionsExample() {
|
||||||
|
return (
|
||||||
|
<div className="tldraw__editor">
|
||||||
|
<Tldraw persistenceKey="example" options={options} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
11
apps/examples/src/examples/custom-options/README.md
Normal file
11
apps/examples/src/examples/custom-options/README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
title: Custom options
|
||||||
|
component: ./CustomOptionsExample.tsx
|
||||||
|
category: basic
|
||||||
|
priority: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
Use the `options` property to override tldraw's options. In this example, we limit the maximum
|
||||||
|
number of pages to 3, and slow down camera animations like zoom in and zoom out.
|
||||||
|
|
||||||
|
---
|
|
@ -89,12 +89,6 @@ import { whyAmIRunning } from '@tldraw/state';
|
||||||
// @public
|
// @public
|
||||||
export function angleDistance(fromAngle: number, toAngle: number, direction: number): number;
|
export function angleDistance(fromAngle: number, toAngle: number, direction: number): number;
|
||||||
|
|
||||||
// @internal (undocumented)
|
|
||||||
export const ANIMATION_MEDIUM_MS = 320;
|
|
||||||
|
|
||||||
// @internal (undocumented)
|
|
||||||
export const ANIMATION_SHORT_MS = 80;
|
|
||||||
|
|
||||||
// @internal (undocumented)
|
// @internal (undocumented)
|
||||||
export function applyRotationToSnapshotShapes({ delta, editor, snapshot, stage, }: {
|
export function applyRotationToSnapshotShapes({ delta, editor, snapshot, stage, }: {
|
||||||
delta: number;
|
delta: number;
|
||||||
|
@ -391,9 +385,6 @@ export class Box {
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type BoxLike = Box | BoxModel;
|
export type BoxLike = Box | BoxModel;
|
||||||
|
|
||||||
// @internal (undocumented)
|
|
||||||
export const CAMERA_SLIDE_FRICTION = 0.09;
|
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function canonicalizeRotation(a: number): number;
|
export function canonicalizeRotation(a: number): number;
|
||||||
|
|
||||||
|
@ -607,6 +598,50 @@ export function DefaultSpinner(): JSX_2.Element;
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const DefaultSvgDefs: () => null;
|
export const DefaultSvgDefs: () => null;
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
export const defaultTldrawOptions: {
|
||||||
|
readonly adjacentShapeMargin: 10;
|
||||||
|
readonly animationMediumMs: 320;
|
||||||
|
readonly cameraMovingTimoutMs: 64;
|
||||||
|
readonly cameraSlideFriction: 0.09;
|
||||||
|
readonly coarseDragDistanceSquared: 36;
|
||||||
|
readonly coarseHandleRadius: 20;
|
||||||
|
readonly coarsePointerWidth: 12;
|
||||||
|
readonly collaboratorCheckIntervalMs: 1200;
|
||||||
|
readonly collaboratorIdleTimeoutMs: 3000;
|
||||||
|
readonly collaboratorInactiveTimeoutMs: 60000;
|
||||||
|
readonly defaultSvgPadding: 32;
|
||||||
|
readonly doubleClickDurationMs: 450;
|
||||||
|
readonly dragDistanceSquared: 16;
|
||||||
|
readonly edgeScrollDistance: 8;
|
||||||
|
readonly edgeScrollSpeed: 20;
|
||||||
|
readonly followChaseViewportSnap: 2;
|
||||||
|
readonly gridSteps: readonly [{
|
||||||
|
readonly mid: 0.15;
|
||||||
|
readonly min: -1;
|
||||||
|
readonly step: 64;
|
||||||
|
}, {
|
||||||
|
readonly mid: 0.375;
|
||||||
|
readonly min: 0.05;
|
||||||
|
readonly step: 16;
|
||||||
|
}, {
|
||||||
|
readonly mid: 1;
|
||||||
|
readonly min: 0.15;
|
||||||
|
readonly step: 4;
|
||||||
|
}, {
|
||||||
|
readonly mid: 2.5;
|
||||||
|
readonly min: 0.7;
|
||||||
|
readonly step: 1;
|
||||||
|
}];
|
||||||
|
readonly handleRadius: 12;
|
||||||
|
readonly hitTestMargin: 8;
|
||||||
|
readonly longPressDurationMs: 500;
|
||||||
|
readonly maxPages: 40;
|
||||||
|
readonly maxShapesPerPage: 4000;
|
||||||
|
readonly multiClickDurationMs: 200;
|
||||||
|
readonly textShadowLod: 0.35;
|
||||||
|
};
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const defaultUserPreferences: Readonly<{
|
export const defaultUserPreferences: Readonly<{
|
||||||
animationSpeed: 0 | 1;
|
animationSpeed: 0 | 1;
|
||||||
|
@ -622,12 +657,6 @@ export const defaultUserPreferences: Readonly<{
|
||||||
// @public
|
// @public
|
||||||
export function degreesToRadians(d: number): number;
|
export function degreesToRadians(d: number): number;
|
||||||
|
|
||||||
// @internal (undocumented)
|
|
||||||
export const DOUBLE_CLICK_DURATION = 450;
|
|
||||||
|
|
||||||
// @internal (undocumented)
|
|
||||||
export const DRAG_DISTANCE = 16;
|
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const EASINGS: {
|
export const EASINGS: {
|
||||||
readonly easeInCubic: (t: number) => number;
|
readonly easeInCubic: (t: number) => number;
|
||||||
|
@ -683,7 +712,7 @@ export class Edge2d extends Geometry2d {
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export class Editor extends EventEmitter<TLEventMap> {
|
export class Editor extends EventEmitter<TLEventMap> {
|
||||||
constructor({ store, user, shapeUtils, bindingUtils, tools, getContainer, cameraOptions, initialState, autoFocus, inferDarkMode, }: TLEditorOptions);
|
constructor({ store, user, shapeUtils, bindingUtils, tools, getContainer, cameraOptions, initialState, autoFocus, inferDarkMode, options, }: TLEditorOptions);
|
||||||
addOpenMenu(id: string): this;
|
addOpenMenu(id: string): this;
|
||||||
alignShapes(shapes: TLShape[] | TLShapeId[], operation: 'bottom' | 'center-horizontal' | 'center-vertical' | 'left' | 'right' | 'top'): this;
|
alignShapes(shapes: TLShape[] | TLShapeId[], operation: 'bottom' | 'center-horizontal' | 'center-vertical' | 'left' | 'right' | 'top'): this;
|
||||||
animateShape(partial: null | TLShapePartial | undefined, opts?: Partial<{
|
animateShape(partial: null | TLShapePartial | undefined, opts?: Partial<{
|
||||||
|
@ -998,6 +1027,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
mark(markId?: string): this;
|
mark(markId?: string): this;
|
||||||
moveShapesToPage(shapes: TLShape[] | TLShapeId[], pageId: TLPageId): this;
|
moveShapesToPage(shapes: TLShape[] | TLShapeId[], pageId: TLPageId): this;
|
||||||
nudgeShapes(shapes: TLShape[] | TLShapeId[], offset: VecLike): this;
|
nudgeShapes(shapes: TLShape[] | TLShapeId[], offset: VecLike): this;
|
||||||
|
// (undocumented)
|
||||||
|
readonly options: TldrawOptions;
|
||||||
packShapes(shapes: TLShape[] | TLShapeId[], gap: number): this;
|
packShapes(shapes: TLShape[] | TLShapeId[], gap: number): this;
|
||||||
pageToScreen(point: VecLike): Vec;
|
pageToScreen(point: VecLike): Vec;
|
||||||
pageToViewport(point: VecLike): Vec;
|
pageToViewport(point: VecLike): Vec;
|
||||||
|
@ -1051,7 +1082,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
readonly sideEffects: StoreSideEffects<TLRecord>;
|
readonly sideEffects: StoreSideEffects<TLRecord>;
|
||||||
slideCamera(opts?: {
|
slideCamera(opts?: {
|
||||||
direction: VecLike;
|
direction: VecLike;
|
||||||
friction: number;
|
friction?: number | undefined;
|
||||||
speed: number;
|
speed: number;
|
||||||
speedThreshold?: number | undefined;
|
speedThreshold?: number | undefined;
|
||||||
}): this;
|
}): this;
|
||||||
|
@ -1281,13 +1312,6 @@ export function getSvgPathFromPoints(points: VecLike[], closed?: boolean): strin
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function getUserPreferences(): TLUserPreferences;
|
export function getUserPreferences(): TLUserPreferences;
|
||||||
|
|
||||||
// @public (undocumented)
|
|
||||||
export const GRID_STEPS: {
|
|
||||||
mid: number;
|
|
||||||
min: number;
|
|
||||||
step: number;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export class Group2d extends Geometry2d {
|
export class Group2d extends Geometry2d {
|
||||||
constructor(config: Omit<Geometry2dOptions, 'isClosed' | 'isFilled'> & {
|
constructor(config: Omit<Geometry2dOptions, 'isClosed' | 'isFilled'> & {
|
||||||
|
@ -1409,9 +1433,6 @@ export class HistoryManager<R extends UnknownRecord> {
|
||||||
undo: () => this;
|
undo: () => this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @public (undocumented)
|
|
||||||
export const HIT_TEST_MARGIN = 8;
|
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function HTMLContainer({ children, className, ...rest }: HTMLContainerProps): JSX_2.Element;
|
export function HTMLContainer({ children, className, ...rest }: HTMLContainerProps): JSX_2.Element;
|
||||||
|
|
||||||
|
@ -1589,18 +1610,9 @@ export interface MatModel {
|
||||||
f: number;
|
f: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @internal (undocumented)
|
|
||||||
export const MAX_PAGES = 40;
|
|
||||||
|
|
||||||
// @internal (undocumented)
|
|
||||||
export const MAX_SHAPES_PER_PAGE = 4000;
|
|
||||||
|
|
||||||
// @public
|
// @public
|
||||||
export function moveCameraWhenCloseToEdge(editor: Editor): void;
|
export function moveCameraWhenCloseToEdge(editor: Editor): void;
|
||||||
|
|
||||||
// @internal (undocumented)
|
|
||||||
export const MULTI_CLICK_DURATION = 200;
|
|
||||||
|
|
||||||
// @internal (undocumented)
|
// @internal (undocumented)
|
||||||
export function normalizeWheel(event: React.WheelEvent<HTMLElement> | WheelEvent): {
|
export function normalizeWheel(event: React.WheelEvent<HTMLElement> | WheelEvent): {
|
||||||
x: number;
|
x: number;
|
||||||
|
@ -2075,9 +2087,6 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const stopEventPropagation: (e: any) => any;
|
export const stopEventPropagation: (e: any) => any;
|
||||||
|
|
||||||
// @internal (undocumented)
|
|
||||||
export const SVG_PADDING = 32;
|
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function SVGContainer({ children, className, ...rest }: SVGContainerProps): JSX_2.Element;
|
export function SVGContainer({ children, className, ...rest }: SVGContainerProps): JSX_2.Element;
|
||||||
|
|
||||||
|
@ -2281,6 +2290,7 @@ export interface TldrawEditorBaseProps {
|
||||||
inferDarkMode?: boolean;
|
inferDarkMode?: boolean;
|
||||||
initialState?: string;
|
initialState?: string;
|
||||||
onMount?: TLOnMountHandler;
|
onMount?: TLOnMountHandler;
|
||||||
|
options?: Partial<TldrawOptions>;
|
||||||
shapeUtils?: readonly TLAnyShapeUtilConstructor[];
|
shapeUtils?: readonly TLAnyShapeUtilConstructor[];
|
||||||
tools?: readonly TLStateNodeConstructor[];
|
tools?: readonly TLStateNodeConstructor[];
|
||||||
user?: TLUser;
|
user?: TLUser;
|
||||||
|
@ -2299,6 +2309,62 @@ export type TldrawEditorProps = Expand<TldrawEditorBaseProps & ({
|
||||||
store: TLStore | TLStoreWithStatus;
|
store: TLStore | TLStoreWithStatus;
|
||||||
})>;
|
})>;
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export interface TldrawOptions {
|
||||||
|
// (undocumented)
|
||||||
|
readonly adjacentShapeMargin: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly animationMediumMs: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly cameraMovingTimoutMs: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly cameraSlideFriction: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly coarseDragDistanceSquared: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly coarseHandleRadius: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly coarsePointerWidth: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly collaboratorCheckIntervalMs: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly collaboratorIdleTimeoutMs: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly collaboratorInactiveTimeoutMs: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly defaultSvgPadding: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly doubleClickDurationMs: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly dragDistanceSquared: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly edgeScrollDistance: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly edgeScrollSpeed: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly followChaseViewportSnap: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly gridSteps: readonly {
|
||||||
|
readonly mid: number;
|
||||||
|
readonly min: number;
|
||||||
|
readonly step: number;
|
||||||
|
}[];
|
||||||
|
// (undocumented)
|
||||||
|
readonly handleRadius: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly hitTestMargin: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly longPressDurationMs: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly maxPages: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly maxShapesPerPage: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly multiClickDurationMs: number;
|
||||||
|
// (undocumented)
|
||||||
|
readonly textShadowLod: number;
|
||||||
|
}
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type TLEditorComponents = Partial<{
|
export type TLEditorComponents = Partial<{
|
||||||
[K in keyof BaseEditorComponents]: BaseEditorComponents[K] | null;
|
[K in keyof BaseEditorComponents]: BaseEditorComponents[K] | null;
|
||||||
|
@ -2312,6 +2378,8 @@ export interface TLEditorOptions {
|
||||||
getContainer: () => HTMLElement;
|
getContainer: () => HTMLElement;
|
||||||
inferDarkMode?: boolean;
|
inferDarkMode?: boolean;
|
||||||
initialState?: string;
|
initialState?: string;
|
||||||
|
// (undocumented)
|
||||||
|
options?: Partial<TldrawOptions>;
|
||||||
shapeUtils: readonly TLShapeUtilConstructor<TLUnknownShape>[];
|
shapeUtils: readonly TLShapeUtilConstructor<TLUnknownShape>[];
|
||||||
store: TLStore;
|
store: TLStore;
|
||||||
tools: readonly TLStateNodeConstructor[];
|
tools: readonly TLStateNodeConstructor[];
|
||||||
|
|
|
@ -106,22 +106,7 @@ export {
|
||||||
export { createTLUser } from './lib/config/createTLUser'
|
export { createTLUser } from './lib/config/createTLUser'
|
||||||
export { type TLAnyBindingUtilConstructor } from './lib/config/defaultBindings'
|
export { type TLAnyBindingUtilConstructor } from './lib/config/defaultBindings'
|
||||||
export { coreShapes, type TLAnyShapeUtilConstructor } from './lib/config/defaultShapes'
|
export { coreShapes, type TLAnyShapeUtilConstructor } from './lib/config/defaultShapes'
|
||||||
export {
|
export { DEFAULT_ANIMATION_OPTIONS, DEFAULT_CAMERA_OPTIONS, SIDES } from './lib/constants'
|
||||||
ANIMATION_MEDIUM_MS,
|
|
||||||
ANIMATION_SHORT_MS,
|
|
||||||
CAMERA_SLIDE_FRICTION,
|
|
||||||
DEFAULT_ANIMATION_OPTIONS,
|
|
||||||
DEFAULT_CAMERA_OPTIONS,
|
|
||||||
DOUBLE_CLICK_DURATION,
|
|
||||||
DRAG_DISTANCE,
|
|
||||||
GRID_STEPS,
|
|
||||||
HIT_TEST_MARGIN,
|
|
||||||
MAX_PAGES,
|
|
||||||
MAX_SHAPES_PER_PAGE,
|
|
||||||
MULTI_CLICK_DURATION,
|
|
||||||
SIDES,
|
|
||||||
SVG_PADDING,
|
|
||||||
} from './lib/constants'
|
|
||||||
export { Editor, type TLEditorOptions, type TLResizeShapeOptions } from './lib/editor/Editor'
|
export { Editor, type TLEditorOptions, type TLResizeShapeOptions } from './lib/editor/Editor'
|
||||||
export {
|
export {
|
||||||
BindingUnbindReason,
|
BindingUnbindReason,
|
||||||
|
@ -247,6 +232,7 @@ export { useSafeId } from './lib/hooks/useSafeId'
|
||||||
export { useSelectionEvents } from './lib/hooks/useSelectionEvents'
|
export { useSelectionEvents } from './lib/hooks/useSelectionEvents'
|
||||||
export { useTLStore } from './lib/hooks/useTLStore'
|
export { useTLStore } from './lib/hooks/useTLStore'
|
||||||
export { useTransform } from './lib/hooks/useTransform'
|
export { useTransform } from './lib/hooks/useTransform'
|
||||||
|
export { defaultTldrawOptions, type TldrawOptions } from './lib/options'
|
||||||
export {
|
export {
|
||||||
Box,
|
Box,
|
||||||
ROTATE_CORNER_TO_SELECTION_CORNER,
|
ROTATE_CORNER_TO_SELECTION_CORNER,
|
||||||
|
|
|
@ -33,6 +33,7 @@ import { useEvent } from './hooks/useEvent'
|
||||||
import { useForceUpdate } from './hooks/useForceUpdate'
|
import { useForceUpdate } from './hooks/useForceUpdate'
|
||||||
import { useLocalStore } from './hooks/useLocalStore'
|
import { useLocalStore } from './hooks/useLocalStore'
|
||||||
import { useZoomCss } from './hooks/useZoomCss'
|
import { useZoomCss } from './hooks/useZoomCss'
|
||||||
|
import { TldrawOptions } from './options'
|
||||||
import { stopEventPropagation } from './utils/dom'
|
import { stopEventPropagation } from './utils/dom'
|
||||||
import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
|
import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
|
||||||
|
|
||||||
|
@ -124,6 +125,11 @@ export interface TldrawEditorBaseProps {
|
||||||
* Camera options for the editor.
|
* Camera options for the editor.
|
||||||
*/
|
*/
|
||||||
cameraOptions?: Partial<TLCameraOptions>
|
cameraOptions?: Partial<TLCameraOptions>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the editor.
|
||||||
|
*/
|
||||||
|
options?: Partial<TldrawOptions>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -293,6 +299,7 @@ function TldrawEditorWithReadyStore({
|
||||||
autoFocus = true,
|
autoFocus = true,
|
||||||
inferDarkMode,
|
inferDarkMode,
|
||||||
cameraOptions,
|
cameraOptions,
|
||||||
|
options,
|
||||||
}: Required<
|
}: Required<
|
||||||
TldrawEditorProps & {
|
TldrawEditorProps & {
|
||||||
store: TLStore
|
store: TLStore
|
||||||
|
@ -317,6 +324,7 @@ function TldrawEditorWithReadyStore({
|
||||||
autoFocus: initialAutoFocus,
|
autoFocus: initialAutoFocus,
|
||||||
inferDarkMode,
|
inferDarkMode,
|
||||||
cameraOptions,
|
cameraOptions,
|
||||||
|
options,
|
||||||
})
|
})
|
||||||
setEditor(editor)
|
setEditor(editor)
|
||||||
|
|
||||||
|
@ -334,6 +342,7 @@ function TldrawEditorWithReadyStore({
|
||||||
initialAutoFocus,
|
initialAutoFocus,
|
||||||
inferDarkMode,
|
inferDarkMode,
|
||||||
cameraOptions,
|
cameraOptions,
|
||||||
|
options,
|
||||||
])
|
])
|
||||||
|
|
||||||
const crashingError = useSyncExternalStore(
|
const crashingError = useSyncExternalStore(
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import { track } from '@tldraw/state'
|
import { track } from '@tldraw/state'
|
||||||
import { TLInstancePresence } from '@tldraw/tlschema'
|
import { TLInstancePresence } from '@tldraw/tlschema'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import {
|
import { Editor } from '../editor/Editor'
|
||||||
COLLABORATOR_CHECK_INTERVAL,
|
|
||||||
COLLABORATOR_IDLE_TIMEOUT,
|
|
||||||
COLLABORATOR_INACTIVE_TIMEOUT,
|
|
||||||
} from '../constants'
|
|
||||||
import { useEditor } from '../hooks/useEditor'
|
import { useEditor } from '../hooks/useEditor'
|
||||||
import { useEditorComponents } from '../hooks/useEditorComponents'
|
import { useEditorComponents } from '../hooks/useEditorComponents'
|
||||||
import { usePeerIds } from '../hooks/usePeerIds'
|
import { usePeerIds } from '../hooks/usePeerIds'
|
||||||
|
@ -29,7 +25,7 @@ const CollaboratorGuard = track(function CollaboratorGuard({
|
||||||
}) {
|
}) {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
const presence = usePresence(collaboratorId)
|
const presence = usePresence(collaboratorId)
|
||||||
const collaboratorState = useCollaboratorState(presence)
|
const collaboratorState = useCollaboratorState(editor, presence)
|
||||||
|
|
||||||
if (!(presence && presence.currentPageId === editor.getCurrentPageId())) {
|
if (!(presence && presence.currentPageId === editor.getCurrentPageId())) {
|
||||||
// No need to render if we don't have a presence or if they're on a different page
|
// No need to render if we don't have a presence or if they're on a different page
|
||||||
|
@ -153,28 +149,28 @@ const Collaborator = track(function Collaborator({
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
function getStateFromElapsedTime(elapsed: number) {
|
function getStateFromElapsedTime(editor: Editor, elapsed: number) {
|
||||||
return elapsed > COLLABORATOR_INACTIVE_TIMEOUT
|
return elapsed > editor.options.collaboratorInactiveTimeoutMs
|
||||||
? 'inactive'
|
? 'inactive'
|
||||||
: elapsed > COLLABORATOR_IDLE_TIMEOUT
|
: elapsed > editor.options.collaboratorIdleTimeoutMs
|
||||||
? 'idle'
|
? 'idle'
|
||||||
: 'active'
|
: 'active'
|
||||||
}
|
}
|
||||||
|
|
||||||
function useCollaboratorState(latestPresence: TLInstancePresence | null) {
|
function useCollaboratorState(editor: Editor, latestPresence: TLInstancePresence | null) {
|
||||||
const rLastActivityTimestamp = useRef(latestPresence?.lastActivityTimestamp ?? -1)
|
const rLastActivityTimestamp = useRef(latestPresence?.lastActivityTimestamp ?? -1)
|
||||||
|
|
||||||
const [state, setState] = useState<'active' | 'idle' | 'inactive'>(() =>
|
const [state, setState] = useState<'active' | 'idle' | 'inactive'>(() =>
|
||||||
getStateFromElapsedTime(Date.now() - rLastActivityTimestamp.current)
|
getStateFromElapsedTime(editor, Date.now() - rLastActivityTimestamp.current)
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setState(getStateFromElapsedTime(Date.now() - rLastActivityTimestamp.current))
|
setState(getStateFromElapsedTime(editor, Date.now() - rLastActivityTimestamp.current))
|
||||||
}, COLLABORATOR_CHECK_INTERVAL)
|
}, editor.options.collaboratorCheckIntervalMs)
|
||||||
|
|
||||||
return () => clearInterval(interval)
|
return () => clearInterval(interval)
|
||||||
}, [])
|
}, [editor])
|
||||||
|
|
||||||
if (latestPresence) {
|
if (latestPresence) {
|
||||||
// We can do this on every render, it's free and cheaper than an effect
|
// We can do this on every render, it's free and cheaper than an effect
|
||||||
|
|
|
@ -3,7 +3,6 @@ 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 { Fragment, JSX, useEffect, useRef, useState } from 'react'
|
import { Fragment, JSX, useEffect, useRef, useState } from 'react'
|
||||||
import { COARSE_HANDLE_RADIUS, HANDLE_RADIUS, TEXT_SHADOW_LOD } from '../../constants'
|
|
||||||
import { useCanvasEvents } from '../../hooks/useCanvasEvents'
|
import { useCanvasEvents } from '../../hooks/useCanvasEvents'
|
||||||
import { useCoarsePointer } from '../../hooks/useCoarsePointer'
|
import { useCoarsePointer } from '../../hooks/useCoarsePointer'
|
||||||
import { useContainer } from '../../hooks/useContainer'
|
import { useContainer } from '../../hooks/useContainer'
|
||||||
|
@ -65,9 +64,9 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
||||||
// If we're below the lod distance for text shadows, turn them off
|
// If we're below the lod distance for text shadows, turn them off
|
||||||
if (
|
if (
|
||||||
rMemoizedStuff.current.allowTextOutline &&
|
rMemoizedStuff.current.allowTextOutline &&
|
||||||
z < TEXT_SHADOW_LOD !== rMemoizedStuff.current.lodDisableTextOutline
|
z < editor.options.textShadowLod !== rMemoizedStuff.current.lodDisableTextOutline
|
||||||
) {
|
) {
|
||||||
const lodDisableTextOutline = z < TEXT_SHADOW_LOD
|
const lodDisableTextOutline = z < editor.options.textShadowLod
|
||||||
container.style.setProperty(
|
container.style.setProperty(
|
||||||
'--tl-text-outline',
|
'--tl-text-outline',
|
||||||
lodDisableTextOutline
|
lodDisableTextOutline
|
||||||
|
@ -297,7 +296,8 @@ function HandlesWrapperInner({ shapeId }: { shapeId: TLShapeId }) {
|
||||||
if (!handles) return null
|
if (!handles) return null
|
||||||
|
|
||||||
const minDistBetweenVirtualHandlesAndRegularHandles =
|
const minDistBetweenVirtualHandlesAndRegularHandles =
|
||||||
((isCoarse ? COARSE_HANDLE_RADIUS : HANDLE_RADIUS) / zoomLevel) * 2
|
((isCoarse ? editor.options.coarseHandleRadius : editor.options.handleRadius) / zoomLevel) *
|
||||||
|
2
|
||||||
|
|
||||||
return (
|
return (
|
||||||
handles
|
handles
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { modulate } from '@tldraw/utils'
|
import { modulate } from '@tldraw/utils'
|
||||||
import { GRID_STEPS } from '../../constants'
|
import { useEditor } from '../../hooks/useEditor'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export interface TLGridProps {
|
export interface TLGridProps {
|
||||||
|
@ -11,10 +11,12 @@ export interface TLGridProps {
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export function DefaultGrid({ x, y, z, size }: TLGridProps) {
|
export function DefaultGrid({ x, y, z, size }: TLGridProps) {
|
||||||
|
const editor = useEditor()
|
||||||
|
const { gridSteps } = editor.options
|
||||||
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>
|
||||||
{GRID_STEPS.map(({ min, mid, step }, i) => {
|
{gridSteps.map(({ min, mid, step }, i) => {
|
||||||
const s = step * size * z
|
const s = step * size * z
|
||||||
const xo = 0.5 + x * z
|
const xo = 0.5 + x * z
|
||||||
const yo = 0.5 + y * z
|
const yo = 0.5 + y * z
|
||||||
|
@ -35,7 +37,7 @@ export function DefaultGrid({ x, y, z, size }: TLGridProps) {
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</defs>
|
</defs>
|
||||||
{GRID_STEPS.map(({ step }, i) => (
|
{gridSteps.map(({ step }, i) => (
|
||||||
<rect key={`grid-rect-${i}`} width="100%" height="100%" fill={`url(#grid-${step})`} />
|
<rect key={`grid-rect-${i}`} width="100%" height="100%" fill={`url(#grid-${step})`} />
|
||||||
))}
|
))}
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { TLHandle, TLShapeId } from '@tldraw/tlschema'
|
import { TLHandle, TLShapeId } from '@tldraw/tlschema'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { COARSE_HANDLE_RADIUS, HANDLE_RADIUS, SIDES } from '../../constants'
|
import { SIDES } from '../../constants'
|
||||||
|
import { useEditor } from '../../hooks/useEditor'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export interface TLHandleProps {
|
export interface TLHandleProps {
|
||||||
|
@ -13,7 +14,8 @@ export interface TLHandleProps {
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export function DefaultHandle({ handle, isCoarse, className, zoom }: TLHandleProps) {
|
export function DefaultHandle({ handle, isCoarse, className, zoom }: TLHandleProps) {
|
||||||
const br = (isCoarse ? COARSE_HANDLE_RADIUS : HANDLE_RADIUS) / zoom
|
const editor = useEditor()
|
||||||
|
const br = (isCoarse ? editor.options.coarseHandleRadius : editor.options.handleRadius) / zoom
|
||||||
|
|
||||||
if (handle.type === 'clone') {
|
if (handle.type === 'clone') {
|
||||||
// bouba
|
// bouba
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
import { TLCameraOptions } from './editor/types/misc-types'
|
import { TLCameraOptions } from './editor/types/misc-types'
|
||||||
import { EASINGS } from './primitives/easings'
|
import { EASINGS } from './primitives/easings'
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const MAX_SHAPES_PER_PAGE = 4000
|
|
||||||
/** @internal */
|
|
||||||
export const MAX_PAGES = 40
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const ANIMATION_SHORT_MS = 80
|
|
||||||
/** @internal */
|
|
||||||
export const ANIMATION_MEDIUM_MS = 320
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const DEFAULT_CAMERA_OPTIONS: TLCameraOptions = {
|
export const DEFAULT_CAMERA_OPTIONS: TLCameraOptions = {
|
||||||
isLocked: false,
|
isLocked: false,
|
||||||
|
@ -20,49 +10,12 @@ export const DEFAULT_CAMERA_OPTIONS: TLCameraOptions = {
|
||||||
zoomSteps: [0.1, 0.25, 0.5, 1, 2, 4, 8],
|
zoomSteps: [0.1, 0.25, 0.5, 1, 2, 4, 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const FOLLOW_CHASE_VIEWPORT_SNAP = 2
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const DOUBLE_CLICK_DURATION = 450
|
|
||||||
/** @internal */
|
|
||||||
export const MULTI_CLICK_DURATION = 200
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const COARSE_DRAG_DISTANCE = 36 // 6 squared
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const DRAG_DISTANCE = 16 // 4 squared
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const SVG_PADDING = 32
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const DEFAULT_ANIMATION_OPTIONS = {
|
export const DEFAULT_ANIMATION_OPTIONS = {
|
||||||
duration: 0,
|
duration: 0,
|
||||||
easing: EASINGS.easeInOutCubic,
|
easing: EASINGS.easeInOutCubic,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const CAMERA_SLIDE_FRICTION = 0.09
|
|
||||||
|
|
||||||
/** @public */
|
|
||||||
export const GRID_STEPS = [
|
|
||||||
{ min: -1, mid: 0.15, step: 64 },
|
|
||||||
{ min: 0.05, mid: 0.375, step: 16 },
|
|
||||||
{ min: 0.15, mid: 1, step: 4 },
|
|
||||||
{ min: 0.7, mid: 2.5, step: 1 },
|
|
||||||
]
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const COLLABORATOR_INACTIVE_TIMEOUT = 60000
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const COLLABORATOR_IDLE_TIMEOUT = 3000
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const COLLABORATOR_CHECK_INTERVAL = 1200
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Negative pointer ids are reserved for internal use.
|
* Negative pointer ids are reserved for internal use.
|
||||||
*
|
*
|
||||||
|
@ -71,36 +24,9 @@ export const INTERNAL_POINTER_IDS = {
|
||||||
CAMERA_MOVE: -10,
|
CAMERA_MOVE: -10,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const CAMERA_MOVING_TIMEOUT = 64
|
|
||||||
|
|
||||||
/** @public */
|
|
||||||
export const HIT_TEST_MARGIN = 8
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const EDGE_SCROLL_SPEED = 20
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const EDGE_SCROLL_DISTANCE = 8
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const COARSE_POINTER_WIDTH = 12
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const COARSE_HANDLE_RADIUS = 20
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const HANDLE_RADIUS = 12
|
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export const SIDES = ['top', 'right', 'bottom', 'left'] as const
|
export const SIDES = ['top', 'right', 'bottom', 'left'] as const
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const LONG_PRESS_DURATION = 500
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export const TEXT_SHADOW_LOD = 0.35
|
|
||||||
|
|
||||||
export const LEFT_MOUSE_BUTTON = 0
|
export const LEFT_MOUSE_BUTTON = 0
|
||||||
export const RIGHT_MOUSE_BUTTON = 2
|
export const RIGHT_MOUSE_BUTTON = 2
|
||||||
export const MIDDLE_MOUSE_BUTTON = 1
|
export const MIDDLE_MOUSE_BUTTON = 1
|
||||||
|
|
|
@ -83,26 +83,16 @@ import { TLUser, createTLUser } from '../config/createTLUser'
|
||||||
import { checkBindings } from '../config/defaultBindings'
|
import { checkBindings } from '../config/defaultBindings'
|
||||||
import { checkShapesAndAddCore } from '../config/defaultShapes'
|
import { checkShapesAndAddCore } from '../config/defaultShapes'
|
||||||
import {
|
import {
|
||||||
ANIMATION_MEDIUM_MS,
|
|
||||||
CAMERA_MOVING_TIMEOUT,
|
|
||||||
CAMERA_SLIDE_FRICTION,
|
|
||||||
COARSE_DRAG_DISTANCE,
|
|
||||||
COLLABORATOR_IDLE_TIMEOUT,
|
|
||||||
DEFAULT_ANIMATION_OPTIONS,
|
DEFAULT_ANIMATION_OPTIONS,
|
||||||
DEFAULT_CAMERA_OPTIONS,
|
DEFAULT_CAMERA_OPTIONS,
|
||||||
DRAG_DISTANCE,
|
|
||||||
FOLLOW_CHASE_VIEWPORT_SNAP,
|
|
||||||
HIT_TEST_MARGIN,
|
|
||||||
INTERNAL_POINTER_IDS,
|
INTERNAL_POINTER_IDS,
|
||||||
LEFT_MOUSE_BUTTON,
|
LEFT_MOUSE_BUTTON,
|
||||||
LONG_PRESS_DURATION,
|
|
||||||
MAX_PAGES,
|
|
||||||
MAX_SHAPES_PER_PAGE,
|
|
||||||
MIDDLE_MOUSE_BUTTON,
|
MIDDLE_MOUSE_BUTTON,
|
||||||
RIGHT_MOUSE_BUTTON,
|
RIGHT_MOUSE_BUTTON,
|
||||||
STYLUS_ERASER_BUTTON,
|
STYLUS_ERASER_BUTTON,
|
||||||
ZOOM_TO_FIT_PADDING,
|
ZOOM_TO_FIT_PADDING,
|
||||||
} from '../constants'
|
} from '../constants'
|
||||||
|
import { TldrawOptions, defaultTldrawOptions } from '../options'
|
||||||
import { Box, BoxLike } from '../primitives/Box'
|
import { Box, BoxLike } from '../primitives/Box'
|
||||||
import { Mat, MatLike } from '../primitives/Mat'
|
import { Mat, MatLike } from '../primitives/Mat'
|
||||||
import { Vec, VecLike } from '../primitives/Vec'
|
import { Vec, VecLike } from '../primitives/Vec'
|
||||||
|
@ -217,6 +207,8 @@ export interface TLEditorOptions {
|
||||||
* Options for the editor's camera.
|
* Options for the editor's camera.
|
||||||
*/
|
*/
|
||||||
cameraOptions?: Partial<TLCameraOptions>
|
cameraOptions?: Partial<TLCameraOptions>
|
||||||
|
|
||||||
|
options?: Partial<TldrawOptions>
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
|
@ -232,9 +224,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
initialState,
|
initialState,
|
||||||
autoFocus,
|
autoFocus,
|
||||||
inferDarkMode,
|
inferDarkMode,
|
||||||
|
options,
|
||||||
}: TLEditorOptions) {
|
}: TLEditorOptions) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
this.options = { ...defaultTldrawOptions, ...options }
|
||||||
this.store = store
|
this.store = store
|
||||||
this.history = new HistoryManager<TLRecord>({
|
this.history = new HistoryManager<TLRecord>({
|
||||||
store,
|
store,
|
||||||
|
@ -700,6 +694,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
this.performanceTracker = new PerformanceTracker()
|
this.performanceTracker = new PerformanceTracker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly options: TldrawOptions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor's store
|
* The editor's store
|
||||||
*
|
*
|
||||||
|
@ -2876,7 +2872,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
opts = {} as {
|
opts = {} as {
|
||||||
speed: number
|
speed: number
|
||||||
direction: VecLike
|
direction: VecLike
|
||||||
friction: number
|
friction?: number
|
||||||
speedThreshold?: number
|
speedThreshold?: number
|
||||||
}
|
}
|
||||||
): this {
|
): this {
|
||||||
|
@ -2887,7 +2883,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
|
|
||||||
this.stopCameraAnimation()
|
this.stopCameraAnimation()
|
||||||
|
|
||||||
const { speed, friction, direction, speedThreshold = 0.01 } = opts
|
const {
|
||||||
|
speed,
|
||||||
|
friction = this.options.cameraSlideFriction,
|
||||||
|
direction,
|
||||||
|
speedThreshold = 0.01,
|
||||||
|
} = opts
|
||||||
let currentSpeed = Math.min(speed, 1)
|
let currentSpeed = Math.min(speed, 1)
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
|
@ -2963,7 +2964,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
if (index < 0) return
|
if (index < 0) return
|
||||||
highlightedUserIds.splice(index, 1)
|
highlightedUserIds.splice(index, 1)
|
||||||
this.updateInstanceState({ highlightedUserIds })
|
this.updateInstanceState({ highlightedUserIds })
|
||||||
}, COLLABORATOR_IDLE_TIMEOUT)
|
}, this.options.collaboratorIdleTimeoutMs)
|
||||||
})
|
})
|
||||||
|
|
||||||
return this
|
return this
|
||||||
|
@ -3279,7 +3280,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
Math.abs(targetViewport.maxY - currentViewport.maxY)
|
Math.abs(targetViewport.maxY - currentViewport.maxY)
|
||||||
|
|
||||||
// Stop chasing if we're close enough!
|
// Stop chasing if we're close enough!
|
||||||
if (diffX < FOLLOW_CHASE_VIEWPORT_SNAP && diffY < FOLLOW_CHASE_VIEWPORT_SNAP) {
|
if (
|
||||||
|
diffX < this.options.followChaseViewportSnap &&
|
||||||
|
diffY < this.options.followChaseViewportSnap
|
||||||
|
) {
|
||||||
this._isLockedOnFollowingUser.set(true)
|
this._isLockedOnFollowingUser.set(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -3364,8 +3368,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
opacity: number
|
opacity: number
|
||||||
}[] = []
|
}[] = []
|
||||||
|
|
||||||
let nextIndex = MAX_SHAPES_PER_PAGE * 2
|
let nextIndex = this.options.maxShapesPerPage * 2
|
||||||
let nextBackgroundIndex = MAX_SHAPES_PER_PAGE
|
let nextBackgroundIndex = this.options.maxShapesPerPage
|
||||||
|
|
||||||
const erasingShapeIds = this.getErasingShapeIds()
|
const erasingShapeIds = this.getErasingShapeIds()
|
||||||
|
|
||||||
|
@ -3403,7 +3407,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
if (util.providesBackgroundForChildren(shape)) {
|
if (util.providesBackgroundForChildren(shape)) {
|
||||||
backgroundIndexToRestore = nextBackgroundIndex
|
backgroundIndexToRestore = nextBackgroundIndex
|
||||||
nextBackgroundIndex = nextIndex
|
nextBackgroundIndex = nextIndex
|
||||||
nextIndex += MAX_SHAPES_PER_PAGE
|
nextIndex += this.options.maxShapesPerPage
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const childId of childIds) {
|
for (const childId of childIds) {
|
||||||
|
@ -3444,7 +3448,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
}
|
}
|
||||||
private _tickCameraState = () => {
|
private _tickCameraState = () => {
|
||||||
// always reset the timeout
|
// always reset the timeout
|
||||||
this._cameraStateTimeoutRemaining = CAMERA_MOVING_TIMEOUT
|
this._cameraStateTimeoutRemaining = this.options.cameraMovingTimoutMs
|
||||||
// If the state is idle, then start the tick
|
// If the state is idle, then start the tick
|
||||||
if (this._cameraState.__unsafe__getWithoutCapture() !== 'idle') return
|
if (this._cameraState.__unsafe__getWithoutCapture() !== 'idle') return
|
||||||
this._cameraState.set('moving')
|
this._cameraState.set('moving')
|
||||||
|
@ -3667,7 +3671,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
createPage(page: Partial<TLPage>): this {
|
createPage(page: Partial<TLPage>): this {
|
||||||
this.history.batch(() => {
|
this.history.batch(() => {
|
||||||
if (this.getInstanceState().isReadonly) return
|
if (this.getInstanceState().isReadonly) return
|
||||||
if (this.getPages().length >= MAX_PAGES) return
|
if (this.getPages().length >= this.options.maxPages) return
|
||||||
const pages = this.getPages()
|
const pages = this.getPages()
|
||||||
|
|
||||||
const name = getIncrementedName(
|
const name = getIncrementedName(
|
||||||
|
@ -3734,7 +3738,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
duplicatePage(page: TLPageId | TLPage, createId: TLPageId = PageRecordType.createId()): this {
|
duplicatePage(page: TLPageId | TLPage, createId: TLPageId = PageRecordType.createId()): this {
|
||||||
if (this.getPages().length >= MAX_PAGES) return this
|
if (this.getPages().length >= this.options.maxPages) return this
|
||||||
const id = typeof page === 'string' ? page : page.id
|
const id = typeof page === 'string' ? page : page.id
|
||||||
const freshPage = this.getPage(id) // get the most recent version of the page anyway
|
const freshPage = this.getPage(id) // get the most recent version of the page anyway
|
||||||
if (!freshPage) return this
|
if (!freshPage) return this
|
||||||
|
@ -4543,7 +4547,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
} else {
|
} else {
|
||||||
// For open shapes (e.g. lines or draw shapes) always use the margin.
|
// For open shapes (e.g. lines or draw shapes) always use the margin.
|
||||||
// If the distance is less than the margin, return the shape as the hit.
|
// If the distance is less than the margin, return the shape as the hit.
|
||||||
if (distance < HIT_TEST_MARGIN / zoomLevel) {
|
if (distance < this.options.hitTestMargin / zoomLevel) {
|
||||||
return shape
|
return shape
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5445,7 +5449,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxShapesReached =
|
const maxShapesReached =
|
||||||
shapesToCreate.length + this.getCurrentPageShapeIds().size > MAX_SHAPES_PER_PAGE
|
shapesToCreate.length + this.getCurrentPageShapeIds().size > this.options.maxShapesPerPage
|
||||||
|
|
||||||
if (maxShapesReached) {
|
if (maxShapesReached) {
|
||||||
alertMaxShapes(this)
|
alertMaxShapes(this)
|
||||||
|
@ -5464,7 +5468,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
const viewportPageBounds = this.getViewportPageBounds()
|
const viewportPageBounds = this.getViewportPageBounds()
|
||||||
if (selectionPageBounds && !viewportPageBounds.contains(selectionPageBounds)) {
|
if (selectionPageBounds && !viewportPageBounds.contains(selectionPageBounds)) {
|
||||||
this.centerOnPoint(selectionPageBounds.center, {
|
this.centerOnPoint(selectionPageBounds.center, {
|
||||||
animation: { duration: ANIMATION_MEDIUM_MS },
|
animation: { duration: this.options.animationMediumMs },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5508,7 +5512,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
|
|
||||||
// If there is no space on pageId, or if the selected shapes
|
// If there is no space on pageId, or if the selected shapes
|
||||||
// would take the new page above the limit, don't move the shapes
|
// would take the new page above the limit, don't move the shapes
|
||||||
if (this.getPageShapeIds(pageId).size + content.shapes.length > MAX_SHAPES_PER_PAGE) {
|
if (this.getPageShapeIds(pageId).size + content.shapes.length > this.options.maxShapesPerPage) {
|
||||||
alertMaxShapes(this, pageId)
|
alertMaxShapes(this, pageId)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -6611,7 +6615,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
|
|
||||||
const currentPageShapeIds = this.getCurrentPageShapeIds()
|
const currentPageShapeIds = this.getCurrentPageShapeIds()
|
||||||
|
|
||||||
const maxShapesReached = shapes.length + currentPageShapeIds.size > MAX_SHAPES_PER_PAGE
|
const maxShapesReached =
|
||||||
|
shapes.length + currentPageShapeIds.size > this.options.maxShapesPerPage
|
||||||
|
|
||||||
if (maxShapesReached) {
|
if (maxShapesReached) {
|
||||||
// can't create more shapes than fit on the page
|
// can't create more shapes than fit on the page
|
||||||
|
@ -7855,7 +7860,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
return newShape
|
return newShape
|
||||||
})
|
})
|
||||||
|
|
||||||
if (newShapes.length + this.getCurrentPageShapeIds().size > MAX_SHAPES_PER_PAGE) {
|
if (newShapes.length + this.getCurrentPageShapeIds().size > this.options.maxShapesPerPage) {
|
||||||
// There's some complexity here involving children
|
// There's some complexity here involving children
|
||||||
// that might be created without their parents, so
|
// that might be created without their parents, so
|
||||||
// if we're going over the limit then just don't paste.
|
// if we're going over the limit then just don't paste.
|
||||||
|
@ -8613,7 +8618,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
point: this.inputs.currentScreenPoint,
|
point: this.inputs.currentScreenPoint,
|
||||||
name: 'long_press',
|
name: 'long_press',
|
||||||
})
|
})
|
||||||
}, LONG_PRESS_DURATION)
|
}, this.options.longPressDurationMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the selected ids at pointer down
|
// Save the selected ids at pointer down
|
||||||
|
@ -8681,7 +8686,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
inputs.isPointing &&
|
inputs.isPointing &&
|
||||||
!inputs.isDragging &&
|
!inputs.isDragging &&
|
||||||
Vec.Dist2(originPagePoint, currentPagePoint) >
|
Vec.Dist2(originPagePoint, currentPagePoint) >
|
||||||
(instanceState.isCoarsePointer ? COARSE_DRAG_DISTANCE : DRAG_DISTANCE) / cz
|
(instanceState.isCoarsePointer
|
||||||
|
? this.options.coarseDragDistanceSquared
|
||||||
|
: this.options.dragDistanceSquared) /
|
||||||
|
cz
|
||||||
) {
|
) {
|
||||||
// Start dragging
|
// Start dragging
|
||||||
inputs.isDragging = true
|
inputs.isDragging = true
|
||||||
|
@ -8734,11 +8742,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slideSpeed > 0) {
|
if (slideSpeed > 0) {
|
||||||
this.slideCamera({
|
this.slideCamera({ speed: slideSpeed, direction: slideDirection })
|
||||||
speed: slideSpeed,
|
|
||||||
direction: slideDirection,
|
|
||||||
friction: CAMERA_SLIDE_FRICTION,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (info.button === STYLUS_ERASER_BUTTON) {
|
if (info.button === STYLUS_ERASER_BUTTON) {
|
||||||
|
@ -8851,7 +8855,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
|
|
||||||
function alertMaxShapes(editor: Editor, pageId = editor.getCurrentPageId()) {
|
function alertMaxShapes(editor: Editor, pageId = editor.getCurrentPageId()) {
|
||||||
const name = editor.getPage(pageId)!.name
|
const name = editor.getPage(pageId)!.name
|
||||||
editor.emit('max-shapes', { name, pageId, count: MAX_SHAPES_PER_PAGE })
|
editor.emit('max-shapes', { name, pageId, count: editor.options.maxShapesPerPage })
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyPartialToRecordWithProps<
|
function applyPartialToRecordWithProps<
|
||||||
|
|
|
@ -6,7 +6,6 @@ import {
|
||||||
getDefaultColorTheme,
|
getDefaultColorTheme,
|
||||||
} from '@tldraw/tlschema'
|
} from '@tldraw/tlschema'
|
||||||
import { Fragment, ReactElement } from 'react'
|
import { Fragment, ReactElement } from 'react'
|
||||||
import { SVG_PADDING } from '../constants'
|
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
import { SvgExportContext, SvgExportContextProvider, SvgExportDef } from './types/SvgExportContext'
|
import { SvgExportContext, SvgExportContextProvider, SvgExportDef } from './types/SvgExportContext'
|
||||||
import { TLSvgOptions } from './types/misc-types'
|
import { TLSvgOptions } from './types/misc-types'
|
||||||
|
@ -22,7 +21,12 @@ export async function getSvgJsx(
|
||||||
if (ids.length === 0) return
|
if (ids.length === 0) return
|
||||||
if (!window.document) throw Error('No document')
|
if (!window.document) throw Error('No document')
|
||||||
|
|
||||||
const { scale = 1, background = false, padding = SVG_PADDING, preserveAspectRatio = false } = opts
|
const {
|
||||||
|
scale = 1,
|
||||||
|
background = false,
|
||||||
|
padding = editor.options.defaultSvgPadding,
|
||||||
|
preserveAspectRatio = false,
|
||||||
|
} = opts
|
||||||
|
|
||||||
const isDarkMode = opts.darkMode ?? editor.user.getIsDarkMode()
|
const isDarkMode = opts.darkMode ?? editor.user.getIsDarkMode()
|
||||||
const theme = getDefaultColorTheme({ isDarkMode })
|
const theme = getDefaultColorTheme({ isDarkMode })
|
||||||
|
|
|
@ -1,9 +1,3 @@
|
||||||
import {
|
|
||||||
COARSE_DRAG_DISTANCE,
|
|
||||||
DOUBLE_CLICK_DURATION,
|
|
||||||
DRAG_DISTANCE,
|
|
||||||
MULTI_CLICK_DURATION,
|
|
||||||
} from '../../constants'
|
|
||||||
import { Vec } from '../../primitives/Vec'
|
import { Vec } from '../../primitives/Vec'
|
||||||
import { uniqueId } from '../../utils/uniqueId'
|
import { uniqueId } from '../../utils/uniqueId'
|
||||||
import type { Editor } from '../Editor'
|
import type { Editor } from '../Editor'
|
||||||
|
@ -72,7 +66,9 @@ export class ClickManager {
|
||||||
this._clickState = 'idle'
|
this._clickState = 'idle'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
state === 'idle' || state === 'pendingDouble' ? DOUBLE_CLICK_DURATION : MULTI_CLICK_DURATION
|
state === 'idle' || state === 'pendingDouble'
|
||||||
|
? this.editor.options.doubleClickDurationMs
|
||||||
|
: this.editor.options.multiClickDurationMs
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +195,9 @@ export class ClickManager {
|
||||||
this._clickState !== 'idle' &&
|
this._clickState !== 'idle' &&
|
||||||
this._clickScreenPoint &&
|
this._clickScreenPoint &&
|
||||||
Vec.Dist2(this._clickScreenPoint, this.editor.inputs.currentScreenPoint) >
|
Vec.Dist2(this._clickScreenPoint, this.editor.inputs.currentScreenPoint) >
|
||||||
(this.editor.getInstanceState().isCoarsePointer ? COARSE_DRAG_DISTANCE : DRAG_DISTANCE)
|
(this.editor.getInstanceState().isCoarsePointer
|
||||||
|
? this.editor.options.coarseDragDistanceSquared
|
||||||
|
: this.editor.options.dragDistanceSquared)
|
||||||
) {
|
) {
|
||||||
this.cancelDoubleClickTimeout()
|
this.cancelDoubleClickTimeout()
|
||||||
}
|
}
|
||||||
|
|
80
packages/editor/src/lib/options.ts
Normal file
80
packages/editor/src/lib/options.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
* Options for configuring tldraw. For defaults, see {@link defaultTldrawOptions}.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```tsx
|
||||||
|
* const options: Partial<TldrawOptions> = {
|
||||||
|
* maxPages: 3,
|
||||||
|
* maxShapesPerPage: 1000,
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* function MyTldrawComponent() {
|
||||||
|
* return <Tldraw options={options} />
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface TldrawOptions {
|
||||||
|
readonly maxShapesPerPage: number
|
||||||
|
readonly maxPages: number
|
||||||
|
readonly animationMediumMs: number
|
||||||
|
readonly followChaseViewportSnap: number
|
||||||
|
readonly doubleClickDurationMs: number
|
||||||
|
readonly multiClickDurationMs: number
|
||||||
|
readonly coarseDragDistanceSquared: number
|
||||||
|
readonly dragDistanceSquared: number
|
||||||
|
readonly defaultSvgPadding: number
|
||||||
|
readonly cameraSlideFriction: number
|
||||||
|
readonly gridSteps: readonly {
|
||||||
|
readonly min: number
|
||||||
|
readonly mid: number
|
||||||
|
readonly step: number
|
||||||
|
}[]
|
||||||
|
readonly collaboratorInactiveTimeoutMs: number
|
||||||
|
readonly collaboratorIdleTimeoutMs: number
|
||||||
|
readonly collaboratorCheckIntervalMs: number
|
||||||
|
readonly cameraMovingTimoutMs: number
|
||||||
|
readonly hitTestMargin: number
|
||||||
|
readonly edgeScrollSpeed: number
|
||||||
|
readonly edgeScrollDistance: number
|
||||||
|
readonly coarsePointerWidth: number
|
||||||
|
readonly coarseHandleRadius: number
|
||||||
|
readonly handleRadius: number
|
||||||
|
readonly longPressDurationMs: number
|
||||||
|
readonly textShadowLod: number
|
||||||
|
readonly adjacentShapeMargin: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @public */
|
||||||
|
export const defaultTldrawOptions = {
|
||||||
|
maxShapesPerPage: 4000,
|
||||||
|
maxPages: 40,
|
||||||
|
animationMediumMs: 320,
|
||||||
|
followChaseViewportSnap: 2,
|
||||||
|
doubleClickDurationMs: 450,
|
||||||
|
multiClickDurationMs: 200,
|
||||||
|
coarseDragDistanceSquared: 36, // 6 squared
|
||||||
|
dragDistanceSquared: 16, // 4 squared
|
||||||
|
defaultSvgPadding: 32,
|
||||||
|
cameraSlideFriction: 0.09,
|
||||||
|
gridSteps: [
|
||||||
|
{ min: -1, mid: 0.15, step: 64 },
|
||||||
|
{ min: 0.05, mid: 0.375, step: 16 },
|
||||||
|
{ min: 0.15, mid: 1, step: 4 },
|
||||||
|
{ min: 0.7, mid: 2.5, step: 1 },
|
||||||
|
],
|
||||||
|
collaboratorInactiveTimeoutMs: 60000,
|
||||||
|
collaboratorIdleTimeoutMs: 3000,
|
||||||
|
collaboratorCheckIntervalMs: 1200,
|
||||||
|
cameraMovingTimoutMs: 64,
|
||||||
|
hitTestMargin: 8,
|
||||||
|
edgeScrollSpeed: 20,
|
||||||
|
edgeScrollDistance: 8,
|
||||||
|
coarsePointerWidth: 12,
|
||||||
|
coarseHandleRadius: 20,
|
||||||
|
handleRadius: 12,
|
||||||
|
longPressDurationMs: 500,
|
||||||
|
textShadowLod: 0.35,
|
||||||
|
adjacentShapeMargin: 10,
|
||||||
|
} as const satisfies TldrawOptions
|
|
@ -1,4 +1,3 @@
|
||||||
import { COARSE_POINTER_WIDTH, EDGE_SCROLL_DISTANCE, EDGE_SCROLL_SPEED } from '../constants'
|
|
||||||
import { Editor } from '../editor/Editor'
|
import { Editor } from '../editor/Editor'
|
||||||
import { Vec } from '../primitives/Vec'
|
import { Vec } from '../primitives/Vec'
|
||||||
|
|
||||||
|
@ -9,14 +8,15 @@ import { Vec } from '../primitives/Vec'
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
function getEdgeProximityFactor(
|
function getEdgeProximityFactor(
|
||||||
|
editor: Editor,
|
||||||
position: number,
|
position: number,
|
||||||
dimension: number,
|
dimension: number,
|
||||||
isCoarse: boolean,
|
isCoarse: boolean,
|
||||||
insetStart: boolean,
|
insetStart: boolean,
|
||||||
insetEnd: boolean
|
insetEnd: boolean
|
||||||
) {
|
) {
|
||||||
const dist = EDGE_SCROLL_DISTANCE
|
const dist = editor.options.edgeScrollDistance
|
||||||
const pw = isCoarse ? COARSE_POINTER_WIDTH : 0 // pointer width
|
const pw = isCoarse ? editor.options.coarsePointerWidth : 0 // pointer width
|
||||||
const pMin = position - pw
|
const pMin = position - pw
|
||||||
const pMax = position + pw
|
const pMax = position + pw
|
||||||
const min = insetStart ? 0 : dist
|
const min = insetStart ? 0 : dist
|
||||||
|
@ -53,13 +53,13 @@ export function moveCameraWhenCloseToEdge(editor: Editor) {
|
||||||
isCoarsePointer,
|
isCoarsePointer,
|
||||||
insets: [t, r, b, l],
|
insets: [t, r, b, l],
|
||||||
} = editor.getInstanceState()
|
} = editor.getInstanceState()
|
||||||
const proximityFactorX = getEdgeProximityFactor(x, screenBounds.w, isCoarsePointer, l, r)
|
const proximityFactorX = getEdgeProximityFactor(editor, x, screenBounds.w, isCoarsePointer, l, r)
|
||||||
const proximityFactorY = getEdgeProximityFactor(y, screenBounds.h, isCoarsePointer, t, b)
|
const proximityFactorY = getEdgeProximityFactor(editor, y, screenBounds.h, isCoarsePointer, t, b)
|
||||||
|
|
||||||
if (proximityFactorX === 0 && proximityFactorY === 0) return
|
if (proximityFactorX === 0 && proximityFactorY === 0) return
|
||||||
|
|
||||||
// Determines the base speed of the scroll
|
// Determines the base speed of the scroll
|
||||||
const pxSpeed = editor.user.getEdgeScrollSpeed() * EDGE_SCROLL_SPEED
|
const pxSpeed = editor.user.getEdgeScrollSpeed() * editor.options.edgeScrollSpeed
|
||||||
const scrollDeltaX = (pxSpeed * proximityFactorX * screenSizeFactorX) / zoomLevel
|
const scrollDeltaX = (pxSpeed * proximityFactorX * screenSizeFactorX) / zoomLevel
|
||||||
const scrollDeltaY = (pxSpeed * proximityFactorY * screenSizeFactorY) / zoomLevel
|
const scrollDeltaY = (pxSpeed * proximityFactorY * screenSizeFactorY) / zoomLevel
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
DRAG_DISTANCE,
|
|
||||||
Mat,
|
Mat,
|
||||||
StateNode,
|
StateNode,
|
||||||
TLDefaultSizeStyle,
|
TLDefaultSizeStyle,
|
||||||
|
@ -317,7 +316,8 @@ export class Drawing extends StateNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasMovedFarEnough =
|
const hasMovedFarEnough =
|
||||||
Vec.Dist2(pagePointWhereNextSegmentChanged, inputs.currentPagePoint) > DRAG_DISTANCE
|
Vec.Dist2(pagePointWhereNextSegmentChanged, inputs.currentPagePoint) >
|
||||||
|
this.editor.options.dragDistanceSquared
|
||||||
|
|
||||||
// Find the distance from where the pointer was when shift was released and
|
// Find the distance from where the pointer was when shift was released and
|
||||||
// where it is now; if it's far enough away, then update the page point where
|
// where it is now; if it's far enough away, then update the page point where
|
||||||
|
@ -388,7 +388,8 @@ export class Drawing extends StateNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasMovedFarEnough =
|
const hasMovedFarEnough =
|
||||||
Vec.Dist2(pagePointWhereNextSegmentChanged, inputs.currentPagePoint) > DRAG_DISTANCE
|
Vec.Dist2(pagePointWhereNextSegmentChanged, inputs.currentPagePoint) >
|
||||||
|
this.editor.options.dragDistanceSquared
|
||||||
|
|
||||||
// Find the distance from where the pointer was when shift was released and
|
// Find the distance from where the pointer was when shift was released and
|
||||||
// where it is now; if it's far enough away, then update the page point where
|
// where it is now; if it's far enough away, then update the page point where
|
||||||
|
|
|
@ -36,7 +36,6 @@ import {
|
||||||
import { getFontDefForExport } from '../shared/defaultStyleDefs'
|
import { getFontDefForExport } from '../shared/defaultStyleDefs'
|
||||||
|
|
||||||
import { startEditingShapeWithLabel } from '../../tools/SelectTool/selectHelpers'
|
import { startEditingShapeWithLabel } from '../../tools/SelectTool/selectHelpers'
|
||||||
import { ADJACENT_SHAPE_MARGIN } from '../../ui/constants'
|
|
||||||
import { useForceSolid } from '../shared/useForceSolid'
|
import { useForceSolid } from '../shared/useForceSolid'
|
||||||
import {
|
import {
|
||||||
CLONE_HANDLE_MARGIN,
|
CLONE_HANDLE_MARGIN,
|
||||||
|
@ -402,7 +401,7 @@ function useNoteKeydownHandler(id: TLShapeId) {
|
||||||
|
|
||||||
const offsetLength =
|
const offsetLength =
|
||||||
NOTE_SIZE +
|
NOTE_SIZE +
|
||||||
ADJACENT_SHAPE_MARGIN +
|
editor.options.adjacentShapeMargin +
|
||||||
// If we're growing down, we need to account for the current shape's growY
|
// If we're growing down, we need to account for the current shape's growY
|
||||||
(isCmdEnter && !e.shiftKey ? shape.props.growY : 0)
|
(isCmdEnter && !e.shiftKey ? shape.props.growY : 0)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
HIT_TEST_MARGIN,
|
|
||||||
StateNode,
|
StateNode,
|
||||||
TLEventHandlers,
|
TLEventHandlers,
|
||||||
TLFrameShape,
|
TLFrameShape,
|
||||||
|
@ -90,7 +89,7 @@ export class Erasing extends StateNode {
|
||||||
this.pushPointToScribble()
|
this.pushPointToScribble()
|
||||||
|
|
||||||
const erasing = new Set<TLShapeId>(erasingShapeIds)
|
const erasing = new Set<TLShapeId>(erasingShapeIds)
|
||||||
const minDist = HIT_TEST_MARGIN / zoomLevel
|
const minDist = this.editor.options.hitTestMargin / zoomLevel
|
||||||
|
|
||||||
for (const shape of currentPageShapes) {
|
for (const shape of currentPageShapes) {
|
||||||
if (editor.isShapeOfType<TLGroupShape>(shape, 'group')) continue
|
if (editor.isShapeOfType<TLGroupShape>(shape, 'group')) continue
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
HIT_TEST_MARGIN,
|
|
||||||
StateNode,
|
StateNode,
|
||||||
TLEventHandlers,
|
TLEventHandlers,
|
||||||
TLFrameShape,
|
TLFrameShape,
|
||||||
|
@ -34,7 +33,7 @@ export class Pointing extends StateNode {
|
||||||
if (
|
if (
|
||||||
this.editor.isPointInShape(shape, currentPagePoint, {
|
this.editor.isPointInShape(shape, currentPagePoint, {
|
||||||
hitInside: false,
|
hitInside: false,
|
||||||
margin: HIT_TEST_MARGIN / zoomLevel,
|
margin: this.editor.options.hitTestMargin / zoomLevel,
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
const hitShape = this.editor.getOutermostSelectableShape(shape)
|
const hitShape = this.editor.getOutermostSelectableShape(shape)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { CAMERA_SLIDE_FRICTION, StateNode, TLEventHandlers, Vec } from '@tldraw/editor'
|
import { StateNode, TLEventHandlers, Vec } from '@tldraw/editor'
|
||||||
|
|
||||||
export class Dragging extends StateNode {
|
export class Dragging extends StateNode {
|
||||||
static override id = 'dragging'
|
static override id = 'dragging'
|
||||||
|
@ -42,11 +42,7 @@ export class Dragging extends StateNode {
|
||||||
const velocityAtPointerUp = Math.min(pointerVelocity.len(), 2)
|
const velocityAtPointerUp = Math.min(pointerVelocity.len(), 2)
|
||||||
|
|
||||||
if (velocityAtPointerUp > 0.1) {
|
if (velocityAtPointerUp > 0.1) {
|
||||||
this.editor.slideCamera({
|
this.editor.slideCamera({ speed: velocityAtPointerUp, direction: pointerVelocity })
|
||||||
speed: velocityAtPointerUp,
|
|
||||||
direction: pointerVelocity,
|
|
||||||
friction: CAMERA_SLIDE_FRICTION,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.parent.transition('idle')
|
this.parent.transition('idle')
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import {
|
import {
|
||||||
Editor,
|
Editor,
|
||||||
Group2d,
|
Group2d,
|
||||||
HIT_TEST_MARGIN,
|
|
||||||
StateNode,
|
StateNode,
|
||||||
TLArrowShape,
|
TLArrowShape,
|
||||||
TLClickEventInfo,
|
TLClickEventInfo,
|
||||||
|
@ -199,7 +198,7 @@ export class Idle extends StateNode {
|
||||||
? hoveredShape
|
? hoveredShape
|
||||||
: this.editor.getSelectedShapeAtPoint(this.editor.inputs.currentPagePoint) ??
|
: this.editor.getSelectedShapeAtPoint(this.editor.inputs.currentPagePoint) ??
|
||||||
this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
|
this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
|
||||||
margin: HIT_TEST_MARGIN / this.editor.getZoomLevel(),
|
margin: this.editor.options.hitTestMargin / this.editor.getZoomLevel(),
|
||||||
hitInside: false,
|
hitInside: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -353,7 +352,7 @@ export class Idle extends StateNode {
|
||||||
hoveredShape && !this.editor.isShapeOfType<TLGroupShape>(hoveredShape, 'group')
|
hoveredShape && !this.editor.isShapeOfType<TLGroupShape>(hoveredShape, 'group')
|
||||||
? hoveredShape
|
? hoveredShape
|
||||||
: this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
|
: this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
|
||||||
margin: HIT_TEST_MARGIN / this.editor.getZoomLevel(),
|
margin: this.editor.options.hitTestMargin / this.editor.getZoomLevel(),
|
||||||
hitInside: false,
|
hitInside: false,
|
||||||
hitLabels: true,
|
hitLabels: true,
|
||||||
hitLocked: true,
|
hitLocked: true,
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
import {
|
import { StateNode, TLEventHandlers, TLPointerEventInfo, TLShape } from '@tldraw/editor'
|
||||||
HIT_TEST_MARGIN,
|
|
||||||
StateNode,
|
|
||||||
TLEventHandlers,
|
|
||||||
TLPointerEventInfo,
|
|
||||||
TLShape,
|
|
||||||
} from '@tldraw/editor'
|
|
||||||
import { getTextLabels } from '../../../utils/shapes/shapes'
|
import { getTextLabels } from '../../../utils/shapes/shapes'
|
||||||
|
|
||||||
export class PointingShape extends StateNode {
|
export class PointingShape extends StateNode {
|
||||||
|
@ -73,7 +67,7 @@ export class PointingShape extends StateNode {
|
||||||
|
|
||||||
const hitShape =
|
const hitShape =
|
||||||
this.editor.getShapeAtPoint(currentPagePoint, {
|
this.editor.getShapeAtPoint(currentPagePoint, {
|
||||||
margin: HIT_TEST_MARGIN / zoomLevel,
|
margin: this.editor.options.hitTestMargin / zoomLevel,
|
||||||
hitInside: true,
|
hitInside: true,
|
||||||
renderingOnly: true,
|
renderingOnly: true,
|
||||||
}) ?? this.hitShape
|
}) ?? this.hitShape
|
||||||
|
|
|
@ -98,7 +98,7 @@ export class ScribbleBrushing extends StateNode {
|
||||||
const shapes = currentPageShapes
|
const shapes = currentPageShapes
|
||||||
let shape: TLShape, geometry: Geometry2d, A: Vec, B: Vec
|
let shape: TLShape, geometry: Geometry2d, A: Vec, B: Vec
|
||||||
|
|
||||||
const minDist = 0 // HIT_TEST_MARGIN / zoomLevel
|
const minDist = 0 // this.editor.options.hitTestMargin / zoomLevel
|
||||||
|
|
||||||
for (let i = 0, n = shapes.length; i < n; i++) {
|
for (let i = 0, n = shapes.length; i < n; i++) {
|
||||||
shape = shapes[i]
|
shape = shapes[i]
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
ANIMATION_MEDIUM_MS,
|
|
||||||
Editor,
|
Editor,
|
||||||
Geometry2d,
|
Geometry2d,
|
||||||
Mat,
|
Mat,
|
||||||
|
@ -149,7 +148,7 @@ export function zoomToShapeIfOffscreen(editor: Editor) {
|
||||||
})
|
})
|
||||||
editor.zoomToBounds(nextBounds, {
|
editor.zoomToBounds(nextBounds, {
|
||||||
animation: {
|
animation: {
|
||||||
duration: ANIMATION_MEDIUM_MS,
|
duration: editor.options.animationMediumMs,
|
||||||
},
|
},
|
||||||
inset: 0,
|
inset: 0,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Editor, HIT_TEST_MARGIN, TLShape } from '@tldraw/editor'
|
import { Editor, TLShape } from '@tldraw/editor'
|
||||||
|
|
||||||
export function getHitShapeOnCanvasPointerDown(
|
export function getHitShapeOnCanvasPointerDown(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
|
@ -14,7 +14,7 @@ export function getHitShapeOnCanvasPointerDown(
|
||||||
editor.getShapeAtPoint(currentPagePoint, {
|
editor.getShapeAtPoint(currentPagePoint, {
|
||||||
hitInside: false,
|
hitInside: false,
|
||||||
hitLabels,
|
hitLabels,
|
||||||
margin: HIT_TEST_MARGIN / zoomLevel,
|
margin: editor.options.hitTestMargin / zoomLevel,
|
||||||
renderingOnly: true,
|
renderingOnly: true,
|
||||||
}) ??
|
}) ??
|
||||||
// selected shape at point
|
// selected shape at point
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Editor, HIT_TEST_MARGIN, TLShape, isShapeId } from '@tldraw/editor'
|
import { Editor, TLShape, isShapeId } from '@tldraw/editor'
|
||||||
|
|
||||||
export function selectOnCanvasPointerUp(editor: Editor) {
|
export function selectOnCanvasPointerUp(editor: Editor) {
|
||||||
const selectedShapeIds = editor.getSelectedShapeIds()
|
const selectedShapeIds = editor.getSelectedShapeIds()
|
||||||
|
@ -6,7 +6,7 @@ export function selectOnCanvasPointerUp(editor: Editor) {
|
||||||
|
|
||||||
const hitShape = editor.getShapeAtPoint(currentPagePoint, {
|
const hitShape = editor.getShapeAtPoint(currentPagePoint, {
|
||||||
hitInside: false,
|
hitInside: false,
|
||||||
margin: HIT_TEST_MARGIN / editor.getZoomLevel(),
|
margin: editor.options.hitTestMargin / editor.getZoomLevel(),
|
||||||
hitLabels: true,
|
hitLabels: true,
|
||||||
renderingOnly: true,
|
renderingOnly: true,
|
||||||
filter: (shape) => !shape.isLocked,
|
filter: (shape) => !shape.isLocked,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Editor, HIT_TEST_MARGIN, TLShape, throttle } from '@tldraw/editor'
|
import { Editor, TLShape, throttle } from '@tldraw/editor'
|
||||||
|
|
||||||
function _updateHoveredShapeId(editor: Editor) {
|
function _updateHoveredShapeId(editor: Editor) {
|
||||||
// todo: consider replacing `get hoveredShapeId` with this; it would mean keeping hoveredShapeId in memory rather than in the store and possibly re-computing it more often than necessary
|
// todo: consider replacing `get hoveredShapeId` with this; it would mean keeping hoveredShapeId in memory rather than in the store and possibly re-computing it more often than necessary
|
||||||
const hitShape = editor.getShapeAtPoint(editor.inputs.currentPagePoint, {
|
const hitShape = editor.getShapeAtPoint(editor.inputs.currentPagePoint, {
|
||||||
hitInside: false,
|
hitInside: false,
|
||||||
hitLabels: false,
|
hitLabels: false,
|
||||||
margin: HIT_TEST_MARGIN / editor.getZoomLevel(),
|
margin: editor.options.hitTestMargin / editor.getZoomLevel(),
|
||||||
renderingOnly: true,
|
renderingOnly: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
ANIMATION_MEDIUM_MS,
|
|
||||||
Box,
|
Box,
|
||||||
TLPointerEventInfo,
|
TLPointerEventInfo,
|
||||||
Vec,
|
Vec,
|
||||||
|
@ -62,7 +61,7 @@ export function DefaultMinimap() {
|
||||||
minimapRef.current.originPagePoint.setTo(clampedPoint)
|
minimapRef.current.originPagePoint.setTo(clampedPoint)
|
||||||
minimapRef.current.originPageCenter.setTo(editor.getViewportPageBounds().center)
|
minimapRef.current.originPageCenter.setTo(editor.getViewportPageBounds().center)
|
||||||
|
|
||||||
editor.centerOnPoint(point, { animation: { duration: ANIMATION_MEDIUM_MS } })
|
editor.centerOnPoint(point, { animation: { duration: editor.options.animationMediumMs } })
|
||||||
},
|
},
|
||||||
[editor]
|
[editor]
|
||||||
)
|
)
|
||||||
|
@ -101,7 +100,7 @@ export function DefaultMinimap() {
|
||||||
const pagePoint = Vec.Add(point, delta)
|
const pagePoint = Vec.Add(point, delta)
|
||||||
minimapRef.current.originPagePoint.setTo(pagePoint)
|
minimapRef.current.originPagePoint.setTo(pagePoint)
|
||||||
minimapRef.current.originPageCenter.setTo(point)
|
minimapRef.current.originPageCenter.setTo(point)
|
||||||
editor.centerOnPoint(point, { animation: { duration: ANIMATION_MEDIUM_MS } })
|
editor.centerOnPoint(point, { animation: { duration: editor.options.animationMediumMs } })
|
||||||
} else {
|
} else {
|
||||||
const clampedPoint = minimapRef.current.minimapScreenPointToPagePoint(
|
const clampedPoint = minimapRef.current.minimapScreenPointToPagePoint(
|
||||||
e.clientX,
|
e.clientX,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
MAX_PAGES,
|
|
||||||
PageRecordType,
|
PageRecordType,
|
||||||
TLPageId,
|
TLPageId,
|
||||||
releasePointerCapture,
|
releasePointerCapture,
|
||||||
|
@ -50,7 +49,7 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
|
||||||
// If the user has reached the max page count, we disable the "add page" button
|
// If the user has reached the max page count, we disable the "add page" button
|
||||||
const maxPageCountReached = useValue(
|
const maxPageCountReached = useValue(
|
||||||
'maxPageCountReached',
|
'maxPageCountReached',
|
||||||
() => editor.getPages().length >= MAX_PAGES,
|
() => editor.getPages().length >= editor.options.maxPages,
|
||||||
[editor]
|
[editor]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MAX_PAGES, PageRecordType, TLPageId, track, useEditor } from '@tldraw/editor'
|
import { PageRecordType, TLPageId, track, useEditor } from '@tldraw/editor'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||||
|
@ -66,7 +66,7 @@ export const PageItemSubmenu = track(function PageItemSubmenu({
|
||||||
id="duplicate"
|
id="duplicate"
|
||||||
label="page-menu.submenu.duplicate-page"
|
label="page-menu.submenu.duplicate-page"
|
||||||
onSelect={onDuplicate}
|
onSelect={onDuplicate}
|
||||||
disabled={pages.length >= MAX_PAGES}
|
disabled={pages.length >= editor.options.maxPages}
|
||||||
/>
|
/>
|
||||||
{index > 0 && (
|
{index > 0 && (
|
||||||
<TldrawUiMenuItem
|
<TldrawUiMenuItem
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as _Dropdown from '@radix-ui/react-dropdown-menu'
|
import * as _Dropdown from '@radix-ui/react-dropdown-menu'
|
||||||
import { ANIMATION_MEDIUM_MS, useContainer, useEditor, useValue } from '@tldraw/editor'
|
import { useContainer, useEditor, useValue } from '@tldraw/editor'
|
||||||
import { ReactNode, forwardRef, memo, useCallback } from 'react'
|
import { ReactNode, forwardRef, memo, useCallback } from 'react'
|
||||||
import { PORTRAIT_BREAKPOINT } from '../../constants'
|
import { PORTRAIT_BREAKPOINT } from '../../constants'
|
||||||
import { useBreakpoint } from '../../context/breakpoints'
|
import { useBreakpoint } from '../../context/breakpoints'
|
||||||
|
@ -56,7 +56,7 @@ const ZoomTriggerButton = forwardRef<HTMLButtonElement, any>(
|
||||||
|
|
||||||
const handleDoubleClick = useCallback(() => {
|
const handleDoubleClick = useCallback(() => {
|
||||||
editor.resetZoom(editor.getViewportScreenCenter(), {
|
editor.resetZoom(editor.getViewportScreenCenter(), {
|
||||||
animation: { duration: ANIMATION_MEDIUM_MS },
|
animation: { duration: editor.options.animationMediumMs },
|
||||||
})
|
})
|
||||||
}, [editor])
|
}, [editor])
|
||||||
|
|
||||||
|
|
|
@ -13,5 +13,3 @@ export enum PORTRAIT_BREAKPOINT {
|
||||||
TABLET = 6,
|
TABLET = 6,
|
||||||
DESKTOP = 7,
|
DESKTOP = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ADJACENT_SHAPE_MARGIN = 20
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
ANIMATION_MEDIUM_MS,
|
|
||||||
Box,
|
Box,
|
||||||
DefaultColorStyle,
|
DefaultColorStyle,
|
||||||
Editor,
|
Editor,
|
||||||
|
@ -25,7 +24,6 @@ import { getEmbedInfo } from '../../utils/embeds/embeds'
|
||||||
import { fitFrameToContent, removeFrame } from '../../utils/frames/frames'
|
import { fitFrameToContent, removeFrame } from '../../utils/frames/frames'
|
||||||
import { EditLinkDialog } from '../components/EditLinkDialog'
|
import { EditLinkDialog } from '../components/EditLinkDialog'
|
||||||
import { EmbedDialog } from '../components/EmbedDialog'
|
import { EmbedDialog } from '../components/EmbedDialog'
|
||||||
import { ADJACENT_SHAPE_MARGIN } from '../constants'
|
|
||||||
import { useMenuClipboardEvents } from '../hooks/useClipboardEvents'
|
import { useMenuClipboardEvents } from '../hooks/useClipboardEvents'
|
||||||
import { useCopyAs } from '../hooks/useCopyAs'
|
import { useCopyAs } from '../hooks/useCopyAs'
|
||||||
import { useExportAs } from '../hooks/useExportAs'
|
import { useExportAs } from '../hooks/useExportAs'
|
||||||
|
@ -819,7 +817,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
||||||
trackEvent('pack-shapes', { source })
|
trackEvent('pack-shapes', { source })
|
||||||
editor.mark('pack')
|
editor.mark('pack')
|
||||||
const selectedShapeIds = editor.getSelectedShapeIds()
|
const selectedShapeIds = editor.getSelectedShapeIds()
|
||||||
editor.packShapes(selectedShapeIds, ADJACENT_SHAPE_MARGIN)
|
editor.packShapes(selectedShapeIds, editor.options.adjacentShapeMargin)
|
||||||
kickoutOccludedShapes(editor, selectedShapeIds)
|
kickoutOccludedShapes(editor, selectedShapeIds)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1038,7 +1036,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
||||||
onSelect(source) {
|
onSelect(source) {
|
||||||
trackEvent('zoom-in', { source })
|
trackEvent('zoom-in', { source })
|
||||||
editor.zoomIn(undefined, {
|
editor.zoomIn(undefined, {
|
||||||
animation: { duration: ANIMATION_MEDIUM_MS },
|
animation: { duration: editor.options.animationMediumMs },
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1050,7 +1048,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
||||||
onSelect(source) {
|
onSelect(source) {
|
||||||
trackEvent('zoom-out', { source })
|
trackEvent('zoom-out', { source })
|
||||||
editor.zoomOut(undefined, {
|
editor.zoomOut(undefined, {
|
||||||
animation: { duration: ANIMATION_MEDIUM_MS },
|
animation: { duration: editor.options.animationMediumMs },
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1063,7 +1061,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
||||||
onSelect(source) {
|
onSelect(source) {
|
||||||
trackEvent('reset-zoom', { source })
|
trackEvent('reset-zoom', { source })
|
||||||
editor.resetZoom(undefined, {
|
editor.resetZoom(undefined, {
|
||||||
animation: { duration: ANIMATION_MEDIUM_MS },
|
animation: { duration: editor.options.animationMediumMs },
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1074,7 +1072,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
||||||
readonlyOk: true,
|
readonlyOk: true,
|
||||||
onSelect(source) {
|
onSelect(source) {
|
||||||
trackEvent('zoom-to-fit', { source })
|
trackEvent('zoom-to-fit', { source })
|
||||||
editor.zoomToFit({ animation: { duration: ANIMATION_MEDIUM_MS } })
|
editor.zoomToFit({ animation: { duration: editor.options.animationMediumMs } })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1087,7 +1085,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
||||||
if (mustGoBackToSelectToolFirst()) return
|
if (mustGoBackToSelectToolFirst()) return
|
||||||
|
|
||||||
trackEvent('zoom-to-selection', { source })
|
trackEvent('zoom-to-selection', { source })
|
||||||
editor.zoomToSelection({ animation: { duration: ANIMATION_MEDIUM_MS } })
|
editor.zoomToSelection({ animation: { duration: editor.options.animationMediumMs } })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import {
|
import {
|
||||||
AssetRecordType,
|
AssetRecordType,
|
||||||
Editor,
|
Editor,
|
||||||
MAX_SHAPES_PER_PAGE,
|
|
||||||
PageRecordType,
|
PageRecordType,
|
||||||
TLArrowShape,
|
TLArrowShape,
|
||||||
TLArrowShapeArrowheadStyle,
|
TLArrowShapeArrowheadStyle,
|
||||||
|
@ -130,7 +129,7 @@ export function buildFromV1Document(editor: Editor, document: LegacyTldrawDocume
|
||||||
|
|
||||||
const v1Shapes = Object.values(v1Page.shapes ?? {})
|
const v1Shapes = Object.values(v1Page.shapes ?? {})
|
||||||
.sort((a, b) => (a.childIndex < b.childIndex ? -1 : 1))
|
.sort((a, b) => (a.childIndex < b.childIndex ? -1 : 1))
|
||||||
.slice(0, MAX_SHAPES_PER_PAGE)
|
.slice(0, editor.options.maxShapesPerPage)
|
||||||
|
|
||||||
// Groups only
|
// Groups only
|
||||||
v1Shapes.forEach((v1Shape) => {
|
v1Shapes.forEach((v1Shape) => {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MAX_PAGES, PageRecordType } from '@tldraw/editor'
|
import { PageRecordType } from '@tldraw/editor'
|
||||||
import { TestEditor } from '../TestEditor'
|
import { TestEditor } from '../TestEditor'
|
||||||
|
|
||||||
let editor: TestEditor
|
let editor: TestEditor
|
||||||
|
@ -31,10 +31,10 @@ it('Creates a page', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Doesn't create a page if max pages is reached", () => {
|
it("Doesn't create a page if max pages is reached", () => {
|
||||||
for (let i = 0; i < MAX_PAGES + 1; i++) {
|
for (let i = 0; i < editor.options.maxPages + 1; i++) {
|
||||||
editor.createPage({ name: `Test Page ${i}` })
|
editor.createPage({ name: `Test Page ${i}` })
|
||||||
}
|
}
|
||||||
expect(editor.getPages().length).toBe(MAX_PAGES)
|
expect(editor.getPages().length).toBe(editor.options.maxPages)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('[regression] does not die if every page has the same index', () => {
|
it('[regression] does not die if every page has the same index', () => {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MAX_PAGES, createShapeId } from '@tldraw/editor'
|
import { createShapeId } from '@tldraw/editor'
|
||||||
import { TestEditor } from '../TestEditor'
|
import { TestEditor } from '../TestEditor'
|
||||||
|
|
||||||
let editor: TestEditor
|
let editor: TestEditor
|
||||||
|
@ -48,8 +48,8 @@ it('Duplicates a page', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Doesn't duplicate the page if max pages is reached", () => {
|
it("Doesn't duplicate the page if max pages is reached", () => {
|
||||||
for (let i = 0; i < MAX_PAGES; i++) {
|
for (let i = 0; i < editor.options.maxPages; i++) {
|
||||||
editor.duplicatePage(editor.getCurrentPageId())
|
editor.duplicatePage(editor.getCurrentPageId())
|
||||||
}
|
}
|
||||||
expect(editor.getPages().length).toBe(MAX_PAGES)
|
expect(editor.getPages().length).toBe(editor.options.maxPages)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { DefaultDashStyle, SVG_PADDING, createShapeId } from '@tldraw/editor'
|
import { DefaultDashStyle, createShapeId } from '@tldraw/editor'
|
||||||
import { TestEditor } from '../TestEditor'
|
import { TestEditor } from '../TestEditor'
|
||||||
|
|
||||||
let editor: TestEditor
|
let editor: TestEditor
|
||||||
|
@ -73,7 +73,7 @@ it('Gets the bounding box at the correct size', async () => {
|
||||||
const svg = await editor.getSvgString(editor.getSelectedShapeIds())
|
const svg = await editor.getSvgString(editor.getSelectedShapeIds())
|
||||||
const parsed = parseSvg(svg!)
|
const parsed = parseSvg(svg!)
|
||||||
const bbox = editor.getSelectionRotatedPageBounds()!
|
const bbox = editor.getSelectionRotatedPageBounds()!
|
||||||
const expanded = bbox.expandBy(SVG_PADDING) // adds 32px padding
|
const expanded = bbox.expandBy(editor.options.defaultSvgPadding) // adds 32px padding
|
||||||
|
|
||||||
expect(parsed.getAttribute('width')).toMatch(expanded.width + '')
|
expect(parsed.getAttribute('width')).toMatch(expanded.width + '')
|
||||||
expect(parsed.getAttribute('height')).toMatch(expanded.height + '')
|
expect(parsed.getAttribute('height')).toMatch(expanded.height + '')
|
||||||
|
|
Loading…
Reference in a new issue