mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-10 02:49:57 +02:00
Compare commits
1 Commits
dwelle/pro
...
dwelle/dis
Author | SHA1 | Date | |
---|---|---|---|
![]() |
77b8f5afb6 |
@@ -30,7 +30,6 @@ All `props` are *optional*.
|
||||
| [`generateIdForFile`](#generateidforfile) | `function` | _ | Allows you to override `id` generation for files added on canvas |
|
||||
| [`validateEmbeddable`](#validateEmbeddable) | string[] | `boolean | RegExp | RegExp[] | ((link: string) => boolean | undefined)` | \_ | use for custom src url validation |
|
||||
| [`renderEmbeddable`](/docs/@excalidraw/excalidraw/api/props/render-props#renderEmbeddable) | `function` | \_ | Render function that can override the built-in `<iframe>` |
|
||||
| [`activeTool`](#activeTool) | `object` | - | Set the active editor tool (forced) |
|
||||
|
||||
### Storing custom data on Excalidraw elements
|
||||
|
||||
@@ -237,19 +236,4 @@ validateEmbeddable?: boolean | string[] | RegExp | RegExp[] | ((link: string) =>
|
||||
|
||||
This is an optional property. By default we support a handful of well-known sites. You may allow additional sites or disallow the default ones by supplying a custom validator. If you pass `true`, all URLs will be allowed. You can also supply a list of hostnames, RegExp (or list of RegExp objects), or a function. If the function returns `undefined`, the built-in validator will be used.
|
||||
|
||||
Supplying a list of hostnames (with or without `www.`) is the preferred way to allow a specific list of domains.
|
||||
|
||||
### activeTool
|
||||
|
||||
```ts
|
||||
activeTool?:
|
||||
| {
|
||||
type: ToolType;
|
||||
}
|
||||
| {
|
||||
type: "custom";
|
||||
customType: string;
|
||||
};
|
||||
```
|
||||
|
||||
Tool to be force-set as active. As long as the prop is set, the editor active tool will be set to this. Useful only in specific circumstances such as when UI is disabled and the editor should be limited to just one tool (such as a laser pointer).
|
||||
Supplying a list of hostnames (with or without `www.`) is the preferred way to allow a specific list of domains.
|
@@ -1211,6 +1211,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
app={this}
|
||||
isCollaborating={this.props.isCollaborating}
|
||||
uiDisabled={this.props.ui === false}
|
||||
>
|
||||
{this.props.children}
|
||||
</LayerUI>
|
||||
@@ -1237,14 +1238,16 @@ class App extends React.Component<AppProps, AppState> {
|
||||
closable={this.state.toast.closable}
|
||||
/>
|
||||
)}
|
||||
{this.state.contextMenu && (
|
||||
<ContextMenu
|
||||
items={this.state.contextMenu.items}
|
||||
top={this.state.contextMenu.top}
|
||||
left={this.state.contextMenu.left}
|
||||
actionManager={this.actionManager}
|
||||
/>
|
||||
)}
|
||||
{this.state.contextMenu &&
|
||||
this.props.interactive !== false &&
|
||||
this.props.ui !== false && (
|
||||
<ContextMenu
|
||||
items={this.state.contextMenu.items}
|
||||
top={this.state.contextMenu.top}
|
||||
left={this.state.contextMenu.left}
|
||||
actionManager={this.actionManager}
|
||||
/>
|
||||
)}
|
||||
<StaticCanvas
|
||||
canvas={this.canvas}
|
||||
rc={this.rc}
|
||||
@@ -1903,13 +1906,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.refreshDeviceState(this.excalidrawContainerRef.current);
|
||||
}
|
||||
|
||||
if (
|
||||
this.props.activeTool &&
|
||||
this.props.activeTool.type !== this.state.activeTool.type
|
||||
) {
|
||||
this.setActiveTool(this.props.activeTool);
|
||||
}
|
||||
|
||||
if (
|
||||
prevState.scrollX !== this.state.scrollX ||
|
||||
prevState.scrollY !== this.state.scrollY
|
||||
@@ -2115,6 +2111,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if (this.props.interactive === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!didTapTwice) {
|
||||
didTapTwice = true;
|
||||
clearTimeout(tappedTwiceTimer);
|
||||
@@ -2149,6 +2149,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
};
|
||||
|
||||
private onTouchEnd = (event: TouchEvent) => {
|
||||
if (this.props.interactive === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.resetContextMenuTimer();
|
||||
if (event.touches.length > 0) {
|
||||
this.setState({
|
||||
@@ -2165,6 +2169,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
public pasteFromClipboard = withBatchedUpdates(
|
||||
async (event: ClipboardEvent | null) => {
|
||||
if (this.props.interactive === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isPlainPaste = !!(IS_PLAIN_PASTE && event);
|
||||
|
||||
// #686
|
||||
@@ -3143,7 +3151,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||
| { type: "custom"; customType: string },
|
||||
) => {
|
||||
const nextActiveTool = updateActiveTool(this.state, tool);
|
||||
|
||||
if (nextActiveTool.type === "hand") {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.GRAB);
|
||||
} else if (!isHoldingSpace) {
|
||||
setCursorForShape(this.interactiveCanvas, this.state);
|
||||
}
|
||||
if (isToolIcon(document.activeElement)) {
|
||||
this.focusContainer();
|
||||
}
|
||||
@@ -3160,10 +3172,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
originSnapOffset: null,
|
||||
activeEmbeddable: null,
|
||||
} as const;
|
||||
let nextState: AppState;
|
||||
|
||||
if (nextActiveTool.type !== "selection") {
|
||||
nextState = {
|
||||
return {
|
||||
...prevState,
|
||||
activeTool: nextActiveTool,
|
||||
selectedElementIds: makeNextSelectedElementIds({}, prevState),
|
||||
@@ -3172,21 +3182,12 @@ class App extends React.Component<AppProps, AppState> {
|
||||
multiElement: null,
|
||||
...commonResets,
|
||||
};
|
||||
} else {
|
||||
nextState = {
|
||||
...prevState,
|
||||
activeTool: nextActiveTool,
|
||||
...commonResets,
|
||||
};
|
||||
}
|
||||
|
||||
if (nextActiveTool.type === "hand") {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.GRAB);
|
||||
} else if (!isHoldingSpace) {
|
||||
setCursorForShape(this.interactiveCanvas, nextState);
|
||||
}
|
||||
|
||||
return nextState;
|
||||
return {
|
||||
...prevState,
|
||||
activeTool: nextActiveTool,
|
||||
...commonResets,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3214,6 +3215,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
private onGestureStart = withBatchedUpdates((event: GestureEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.props.interactive === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we only want to deselect on touch screens because user may have selected
|
||||
// elements by mistake while zooming
|
||||
if (this.isTouchScreenMultiTouchGesture()) {
|
||||
@@ -3229,6 +3234,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
private onGestureChange = withBatchedUpdates((event: GestureEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.props.interactive === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// onGestureChange only has zoom factor but not the center.
|
||||
// If we're on iPad or iPhone, then we recognize multi-touch and will
|
||||
// zoom in at the right location in the touchmove handler
|
||||
@@ -3260,6 +3269,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||
// fires only on Safari
|
||||
private onGestureEnd = withBatchedUpdates((event: GestureEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.props.interactive === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// reselect elements only on touch screens (see onGestureStart)
|
||||
if (this.isTouchScreenMultiTouchGesture()) {
|
||||
this.setState({
|
||||
@@ -3841,6 +3855,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
private handleCanvasPointerMove = (
|
||||
event: React.PointerEvent<HTMLCanvasElement>,
|
||||
) => {
|
||||
if (this.props.interactive === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.savePointer(event.clientX, event.clientY, this.state.cursorButton);
|
||||
|
||||
if (gesture.pointers.has(event.pointerId)) {
|
||||
@@ -4493,8 +4511,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.handleCanvasPanUsingWheelOrSpaceDrag(event)) {
|
||||
return;
|
||||
if (this.props.interactive !== false) {
|
||||
if (this.handleCanvasPanUsingWheelOrSpaceDrag(event)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.lastPointerDownEvent = event;
|
||||
@@ -4526,14 +4546,20 @@ class App extends React.Component<AppProps, AppState> {
|
||||
selectedElementsAreBeingDragged: false,
|
||||
});
|
||||
|
||||
if (this.handleDraggingScrollBar(event, pointerDownState)) {
|
||||
if (
|
||||
this.props.interactive !== false &&
|
||||
this.handleDraggingScrollBar(event, pointerDownState)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearSelectionIfNotUsingSelection();
|
||||
this.updateBindingEnabledOnPointerMove(event);
|
||||
|
||||
if (this.handleSelectionOnPointerDown(event, pointerDownState)) {
|
||||
if (
|
||||
this.props.interactive !== false &&
|
||||
this.handleSelectionOnPointerDown(event, pointerDownState)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4615,15 +4641,15 @@ class App extends React.Component<AppProps, AppState> {
|
||||
const onPointerMove =
|
||||
this.onPointerMoveFromPointerDownHandler(pointerDownState);
|
||||
|
||||
const onPointerUp =
|
||||
this.onPointerUpFromPointerDownHandler(pointerDownState);
|
||||
|
||||
const onKeyDown = this.onKeyDownFromPointerDownHandler(pointerDownState);
|
||||
const onKeyUp = this.onKeyUpFromPointerDownHandler(pointerDownState);
|
||||
|
||||
lastPointerUp = onPointerUp;
|
||||
|
||||
if (!this.state.viewModeEnabled || this.state.activeTool.type === "laser") {
|
||||
const onPointerUp =
|
||||
this.onPointerUpFromPointerDownHandler(pointerDownState);
|
||||
|
||||
const onKeyDown = this.onKeyDownFromPointerDownHandler(pointerDownState);
|
||||
const onKeyUp = this.onKeyUpFromPointerDownHandler(pointerDownState);
|
||||
|
||||
lastPointerUp = onPointerUp;
|
||||
|
||||
window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
|
||||
window.addEventListener(EVENT.POINTER_UP, onPointerUp);
|
||||
window.addEventListener(EVENT.KEYDOWN, onKeyDown);
|
||||
@@ -7818,6 +7844,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.props.interactive === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(("pointerType" in event.nativeEvent &&
|
||||
event.nativeEvent.pointerType === "touch") ||
|
||||
@@ -8229,7 +8259,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
event: WheelEvent | React.WheelEvent<HTMLDivElement | HTMLCanvasElement>,
|
||||
) => {
|
||||
event.preventDefault();
|
||||
if (isPanning) {
|
||||
if (isPanning || this.props.interactive === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -79,6 +79,7 @@ interface LayerUIProps {
|
||||
children?: React.ReactNode;
|
||||
app: AppClassProperties;
|
||||
isCollaborating: boolean;
|
||||
uiDisabled: boolean;
|
||||
}
|
||||
|
||||
const DefaultMainMenu: React.FC<{
|
||||
@@ -137,6 +138,7 @@ const LayerUI = ({
|
||||
children,
|
||||
app,
|
||||
isCollaborating,
|
||||
uiDisabled,
|
||||
}: LayerUIProps) => {
|
||||
const device = useDevice();
|
||||
const tunnels = useInitializeTunnels();
|
||||
@@ -354,6 +356,10 @@ const LayerUI = ({
|
||||
|
||||
const isSidebarDocked = useAtomValue(isSidebarDockedAtom, jotaiScope);
|
||||
|
||||
if (uiDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const layerUIJSX = (
|
||||
<>
|
||||
{/* ------------------------- tunneled UI ---------------------------- */}
|
||||
|
@@ -44,7 +44,8 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
|
||||
children,
|
||||
validateEmbeddable,
|
||||
renderEmbeddable,
|
||||
activeTool,
|
||||
ui,
|
||||
interactive,
|
||||
} = props;
|
||||
|
||||
const canvasActions = props.UIOptions?.canvasActions;
|
||||
@@ -101,7 +102,7 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
|
||||
onPointerUpdate={onPointerUpdate}
|
||||
renderTopRightUI={renderTopRightUI}
|
||||
langCode={langCode}
|
||||
viewModeEnabled={viewModeEnabled}
|
||||
viewModeEnabled={interactive === false ? true : viewModeEnabled}
|
||||
zenModeEnabled={zenModeEnabled}
|
||||
gridModeEnabled={gridModeEnabled}
|
||||
libraryReturnUrl={libraryReturnUrl}
|
||||
@@ -120,7 +121,8 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
|
||||
onScrollChange={onScrollChange}
|
||||
validateEmbeddable={validateEmbeddable}
|
||||
renderEmbeddable={renderEmbeddable}
|
||||
activeTool={activeTool}
|
||||
ui={ui}
|
||||
interactive={interactive}
|
||||
>
|
||||
{children}
|
||||
</App>
|
||||
|
10
src/types.ts
10
src/types.ts
@@ -445,14 +445,8 @@ export interface ExcalidrawProps {
|
||||
element: NonDeleted<ExcalidrawEmbeddableElement>,
|
||||
appState: AppState,
|
||||
) => JSX.Element | null;
|
||||
activeTool?:
|
||||
| {
|
||||
type: ToolType;
|
||||
}
|
||||
| {
|
||||
type: "custom";
|
||||
customType: string;
|
||||
};
|
||||
interactive?: boolean;
|
||||
ui?: boolean;
|
||||
}
|
||||
|
||||
export type SceneData = {
|
||||
|
Reference in New Issue
Block a user