Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5b406b055 | |||
| 7522285869 | |||
| 58567dd2b6 | |||
| a76999e9a2 | |||
| 5af532d229 | |||
| 96fc3ac5bb | |||
| 906652bac2 | |||
| 53a0428705 | |||
| 927e36c7b4 | |||
| e187faee77 | |||
| d12d97bfcb | |||
| 5acb5c9d91 | |||
| 23ee054025 | |||
| d91b234db1 | |||
| b2b03af1ec | |||
| f0876e3c03 | |||
| 6d0ee330b7 | |||
| e48eed6b21 | |||
| 549786a504 | |||
| 73c53a3c7c | |||
| 72a98da527 | |||
| 45ff9d1053 | |||
| 6a1477a55c |
@@ -14,4 +14,4 @@ jobs:
|
||||
- name: Install and test
|
||||
run: |
|
||||
yarn install
|
||||
yarn test:app
|
||||
yarn test
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build:workspace": "yarn workspace @excalidraw/excalidraw run build:esm",
|
||||
"dev": "yarn build:workspace && next dev -p 3005",
|
||||
"build": "yarn build:workspace && next build",
|
||||
"build:workspaces": "yarn workspace @excalidraw/utils run build:esm && yarn workspace @excalidraw/excalidraw run build:esm",
|
||||
"dev": "yarn build:workspaces && next dev -p 3005",
|
||||
"build": "yarn build:workspaces && next build",
|
||||
"start": "next start -p 3006",
|
||||
"lint": "next lint"
|
||||
},
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
"typescript": "^5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "yarn workspace @excalidraw/excalidraw run build:esm && vite",
|
||||
"build": "yarn workspace @excalidraw/excalidraw run build:esm && vite build",
|
||||
"build:workspaces": "yarn workspace @excalidraw/utils run build:esm && yarn workspace @excalidraw/excalidraw run build:esm",
|
||||
"start": "yarn build:workspaces && vite",
|
||||
"build": "yarn build:workspaces && vite build",
|
||||
"build:preview": "yarn build && vite preview --port 5002"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,38 +126,6 @@ polyfill();
|
||||
|
||||
window.EXCALIDRAW_THROTTLE_RENDER = true;
|
||||
|
||||
declare global {
|
||||
interface BeforeInstallPromptEventChoiceResult {
|
||||
outcome: "accepted" | "dismissed";
|
||||
}
|
||||
|
||||
interface BeforeInstallPromptEvent extends Event {
|
||||
prompt(): Promise<void>;
|
||||
userChoice: Promise<BeforeInstallPromptEventChoiceResult>;
|
||||
}
|
||||
|
||||
interface WindowEventMap {
|
||||
beforeinstallprompt: BeforeInstallPromptEvent;
|
||||
}
|
||||
}
|
||||
|
||||
let pwaEvent: BeforeInstallPromptEvent | null = null;
|
||||
|
||||
// Adding a listener outside of the component as it may (?) need to be
|
||||
// subscribed early to catch the event.
|
||||
//
|
||||
// Also note that it will fire only if certain heuristics are met (user has
|
||||
// used the app for some time, etc.)
|
||||
window.addEventListener(
|
||||
"beforeinstallprompt",
|
||||
(event: BeforeInstallPromptEvent) => {
|
||||
// prevent Chrome <= 67 from automatically showing the prompt
|
||||
event.preventDefault();
|
||||
// cache for later use
|
||||
pwaEvent = event;
|
||||
},
|
||||
);
|
||||
|
||||
let isSelfEmbedding = false;
|
||||
|
||||
if (window.self !== window.top) {
|
||||
@@ -1132,21 +1100,6 @@ const ExcalidrawWrapper = () => {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t("labels.installPWA"),
|
||||
category: DEFAULT_CATEGORIES.app,
|
||||
predicate: () => !!pwaEvent,
|
||||
perform: () => {
|
||||
if (pwaEvent) {
|
||||
pwaEvent.prompt();
|
||||
pwaEvent.userChoice.then(() => {
|
||||
// event cannot be reused, but we'll hopefully
|
||||
// grab new one as the event should be fired again
|
||||
pwaEvent = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Excalidraw>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { ImportedDataState } from "../../packages/excalidraw/data/types";
|
||||
import { STORAGE_KEYS } from "../app_constants";
|
||||
|
||||
export const initLocalStorage = (data: ImportedDataState) => {
|
||||
if (data.elements) {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS,
|
||||
JSON.stringify(data.elements),
|
||||
);
|
||||
}
|
||||
if (data.appState) {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEYS.LOCAL_STORAGE_APP_STATE,
|
||||
JSON.stringify(data.appState),
|
||||
);
|
||||
}
|
||||
};
|
||||
+5
-5
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "excalidraw-monorepo",
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"workspaces": [
|
||||
"excalidraw-app",
|
||||
"packages/excalidraw",
|
||||
@@ -64,7 +63,8 @@
|
||||
"build:app:docker": "yarn --cwd ./excalidraw-app build:app:docker",
|
||||
"build:app": "yarn --cwd ./excalidraw-app build:app",
|
||||
"build:version": "yarn --cwd ./excalidraw-app build:version",
|
||||
"build": "yarn --cwd ./excalidraw-app build",
|
||||
"build": "yarn workspace @excalidraw/utils build:esm && yarn --cwd ./excalidraw-app build",
|
||||
"clear:workspaces": "yarn workspace @excalidraw/utils run clear && yarn workspace @excalidraw/excalidraw run clear",
|
||||
"fix:code": "yarn test:code --fix",
|
||||
"fix:other": "yarn prettier --write",
|
||||
"fix": "yarn fix:other && yarn fix:code",
|
||||
@@ -72,7 +72,7 @@
|
||||
"locales-coverage:description": "node scripts/locales-coverage-description.js",
|
||||
"prepare": "husky install",
|
||||
"prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore",
|
||||
"start": "yarn --cwd ./excalidraw-app start",
|
||||
"start": "yarn clear:workspaces && yarn workspace @excalidraw/utils run build:esm && yarn --cwd ./excalidraw-app start & node scripts/watchUtils.js",
|
||||
"start:app:production": "npm run build && npx http-server build -a localhost -p 5001 -o",
|
||||
"test:all": "yarn test:typecheck && yarn test:code && yarn test:other && yarn test:app --watch=false",
|
||||
"test:app": "vitest",
|
||||
@@ -80,8 +80,8 @@
|
||||
"test:other": "yarn prettier --list-different",
|
||||
"test:typecheck": "tsc",
|
||||
"test:update": "yarn test:app --update --watch=false",
|
||||
"test": "yarn test:app",
|
||||
"test:coverage": "vitest --coverage",
|
||||
"test": "yarn clear:workspaces && yarn workspace @excalidraw/utils build:esm && yarn test:app",
|
||||
"test:coverage": "yarn clear:workspaces && yarn workspace @excalidraw/utils build:esm && yarn test:app --coverage",
|
||||
"test:coverage:watch": "vitest --coverage --watch",
|
||||
"test:ui": "yarn test --ui --coverage.enabled=true",
|
||||
"autorelease": "node scripts/autorelease.js",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
BOUND_TEXT_PADDING,
|
||||
ROUNDNESS,
|
||||
TEXT_ALIGN,
|
||||
VERTICAL_ALIGN,
|
||||
TEXT_ALIGN,
|
||||
} from "../constants";
|
||||
import { isTextElement, newElement } from "../element";
|
||||
import { mutateElement } from "../element/mutateElement";
|
||||
@@ -142,7 +142,6 @@ export const actionBindText = register({
|
||||
containerId: container.id,
|
||||
verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
||||
textAlign: TEXT_ALIGN.CENTER,
|
||||
autoResize: true,
|
||||
});
|
||||
mutateElement(container, {
|
||||
boundElements: (container.boundElements || []).concat({
|
||||
@@ -297,7 +296,6 @@ export const actionWrapTextInContainer = register({
|
||||
verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
||||
boundElements: null,
|
||||
textAlign: TEXT_ALIGN.CENTER,
|
||||
autoResize: true,
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -65,10 +65,7 @@ export const createUndoAction: ActionCreator = (history, store) => ({
|
||||
PanelComponent: ({ updateData, data }) => {
|
||||
const { isUndoStackEmpty } = useEmitter<HistoryChangedEvent>(
|
||||
history.onHistoryChangedEmitter,
|
||||
new HistoryChangedEvent(
|
||||
history.isUndoStackEmpty,
|
||||
history.isRedoStackEmpty,
|
||||
),
|
||||
new HistoryChangedEvent(),
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -79,7 +76,6 @@ export const createUndoAction: ActionCreator = (history, store) => ({
|
||||
onClick={updateData}
|
||||
size={data?.size || "medium"}
|
||||
disabled={isUndoStackEmpty}
|
||||
data-testid="button-undo"
|
||||
/>
|
||||
);
|
||||
},
|
||||
@@ -107,10 +103,7 @@ export const createRedoAction: ActionCreator = (history, store) => ({
|
||||
PanelComponent: ({ updateData, data }) => {
|
||||
const { isRedoStackEmpty } = useEmitter(
|
||||
history.onHistoryChangedEmitter,
|
||||
new HistoryChangedEvent(
|
||||
history.isUndoStackEmpty,
|
||||
history.isRedoStackEmpty,
|
||||
),
|
||||
new HistoryChangedEvent(),
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -121,7 +114,6 @@ export const createRedoAction: ActionCreator = (history, store) => ({
|
||||
onClick={updateData}
|
||||
size={data?.size || "medium"}
|
||||
disabled={isRedoStackEmpty}
|
||||
data-testid="button-redo"
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -167,7 +167,7 @@ const offsetElementAfterFontResize = (
|
||||
prevElement: ExcalidrawTextElement,
|
||||
nextElement: ExcalidrawTextElement,
|
||||
) => {
|
||||
if (isBoundToContainer(nextElement) || !nextElement.autoResize) {
|
||||
if (isBoundToContainer(nextElement)) {
|
||||
return nextElement;
|
||||
}
|
||||
return mutateElement(
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import { isTextElement } from "../element";
|
||||
import { newElementWith } from "../element/mutateElement";
|
||||
import { measureText } from "../element/textElement";
|
||||
import { getSelectedElements } from "../scene";
|
||||
import { StoreAction } from "../store";
|
||||
import type { AppClassProperties } from "../types";
|
||||
import { getFontString } from "../utils";
|
||||
import { register } from "./register";
|
||||
|
||||
export const actionTextAutoResize = register({
|
||||
name: "autoResize",
|
||||
label: "labels.autoResize",
|
||||
icon: null,
|
||||
trackEvent: { category: "element" },
|
||||
predicate: (elements, appState, _: unknown, app: AppClassProperties) => {
|
||||
const selectedElements = getSelectedElements(elements, appState);
|
||||
return (
|
||||
selectedElements.length === 1 &&
|
||||
isTextElement(selectedElements[0]) &&
|
||||
!selectedElements[0].autoResize
|
||||
);
|
||||
},
|
||||
perform: (elements, appState, _, app) => {
|
||||
const selectedElements = getSelectedElements(elements, appState);
|
||||
|
||||
return {
|
||||
appState,
|
||||
elements: elements.map((element) => {
|
||||
if (element.id === selectedElements[0].id && isTextElement(element)) {
|
||||
const metrics = measureText(
|
||||
element.originalText,
|
||||
getFontString(element),
|
||||
element.lineHeight,
|
||||
);
|
||||
|
||||
return newElementWith(element, {
|
||||
autoResize: true,
|
||||
width: metrics.width,
|
||||
height: metrics.height,
|
||||
text: element.originalText,
|
||||
});
|
||||
}
|
||||
return element;
|
||||
}),
|
||||
storeAction: StoreAction.CAPTURE,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -134,8 +134,7 @@ export type ActionName =
|
||||
| "setEmbeddableAsActiveTool"
|
||||
| "createContainerFromText"
|
||||
| "wrapTextInContainer"
|
||||
| "commandPalette"
|
||||
| "autoResize";
|
||||
| "commandPalette";
|
||||
|
||||
export type PanelComponentProps = {
|
||||
elements: readonly ExcalidrawElement[];
|
||||
|
||||
@@ -468,7 +468,6 @@ export const ExitZenModeAction = ({
|
||||
showExitZenModeBtn: boolean;
|
||||
}) => (
|
||||
<button
|
||||
type="button"
|
||||
className={clsx("disable-zen-mode", {
|
||||
"disable-zen-mode--visible": showExitZenModeBtn,
|
||||
})}
|
||||
|
||||
@@ -114,7 +114,7 @@ import {
|
||||
newTextElement,
|
||||
newImageElement,
|
||||
transformElements,
|
||||
refreshTextDimensions,
|
||||
updateTextElement,
|
||||
redrawTextBoundingBox,
|
||||
getElementAbsoluteCoords,
|
||||
} from "../element";
|
||||
@@ -224,7 +224,7 @@ import type {
|
||||
} from "../scene/types";
|
||||
import { getStateForZoom } from "../scene/zoom";
|
||||
import { findShapeByKey } from "../shapes";
|
||||
import type { GeometricShape } from "../../utils/geometry/shape";
|
||||
import type { GeometricShape } from "@excalidraw/utils";
|
||||
import {
|
||||
getClosedCurveShape,
|
||||
getCurveShape,
|
||||
@@ -232,8 +232,8 @@ import {
|
||||
getFreedrawShape,
|
||||
getPolygonShape,
|
||||
getSelectionBoxShape,
|
||||
} from "../../utils/geometry/shape";
|
||||
import { isPointInShape } from "../../utils/collision";
|
||||
isPointInShape,
|
||||
} from "@excalidraw/utils";
|
||||
import type {
|
||||
AppClassProperties,
|
||||
AppProps,
|
||||
@@ -403,7 +403,7 @@ import { Emitter } from "../emitter";
|
||||
import { ElementCanvasButtons } from "../element/ElementCanvasButtons";
|
||||
import type { MagicCacheData } from "../data/magic";
|
||||
import { diagramToHTML } from "../data/magic";
|
||||
import { exportToBlob } from "../../utils/export";
|
||||
import { exportToBlob } from "@excalidraw/utils";
|
||||
import { COLOR_PALETTE } from "../colors";
|
||||
import { ElementCanvasButton } from "./MagicButton";
|
||||
import { MagicIcon, copyIcon, fullscreenIcon } from "./icons";
|
||||
@@ -429,7 +429,6 @@ import {
|
||||
isPointHittingLinkIcon,
|
||||
} from "./hyperlink/helpers";
|
||||
import { getShortcutFromShortcutName } from "../actions/shortcuts";
|
||||
import { actionTextAutoResize } from "../actions/actionTextAutoResize";
|
||||
|
||||
const AppContext = React.createContext<AppClassProperties>(null!);
|
||||
const AppPropsContext = React.createContext<AppProps>(null!);
|
||||
@@ -715,7 +714,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
id: this.id,
|
||||
};
|
||||
|
||||
this.fonts = new Fonts({ scene: this.scene });
|
||||
this.fonts = new Fonts({
|
||||
scene: this.scene,
|
||||
onSceneUpdated: this.onSceneUpdated,
|
||||
});
|
||||
this.history = new History();
|
||||
|
||||
this.actionManager.registerAll(actions);
|
||||
@@ -938,7 +940,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
});
|
||||
|
||||
if (updated) {
|
||||
this.scene.triggerUpdate();
|
||||
this.scene.informMutation();
|
||||
}
|
||||
|
||||
// GC
|
||||
@@ -1450,10 +1452,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
const selectedElements = this.scene.getSelectedElements(this.state);
|
||||
const { renderTopRightUI, renderCustomStats } = this.props;
|
||||
|
||||
const sceneNonce = this.scene.getSceneNonce();
|
||||
const versionNonce = this.scene.getVersionNonce();
|
||||
const { elementsMap, visibleElements } =
|
||||
this.renderer.getRenderableElements({
|
||||
sceneNonce,
|
||||
versionNonce,
|
||||
zoom: this.state.zoom,
|
||||
offsetLeft: this.state.offsetLeft,
|
||||
offsetTop: this.state.offsetTop,
|
||||
@@ -1671,7 +1673,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
elementsMap={elementsMap}
|
||||
allElementsMap={allElementsMap}
|
||||
visibleElements={visibleElements}
|
||||
sceneNonce={sceneNonce}
|
||||
versionNonce={versionNonce}
|
||||
selectionNonce={
|
||||
this.state.selectionElement?.versionNonce
|
||||
}
|
||||
@@ -1693,7 +1695,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
elementsMap={elementsMap}
|
||||
visibleElements={visibleElements}
|
||||
selectedElements={selectedElements}
|
||||
sceneNonce={sceneNonce}
|
||||
versionNonce={versionNonce}
|
||||
selectionNonce={
|
||||
this.state.selectionElement?.versionNonce
|
||||
}
|
||||
@@ -1817,7 +1819,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
);
|
||||
}
|
||||
this.magicGenerations.set(frameElement.id, data);
|
||||
this.triggerRender();
|
||||
this.onSceneUpdated();
|
||||
};
|
||||
|
||||
private getTextFromElements(elements: readonly ExcalidrawElement[]) {
|
||||
@@ -2442,7 +2444,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.history.record(increment.elementsChange, increment.appStateChange);
|
||||
});
|
||||
|
||||
this.scene.onUpdate(this.triggerRender);
|
||||
this.scene.addCallback(this.onSceneUpdated);
|
||||
this.addEventListeners();
|
||||
|
||||
if (this.props.autoFocus && this.excalidrawContainerRef.current) {
|
||||
@@ -2487,7 +2489,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
public componentWillUnmount() {
|
||||
this.renderer.destroy();
|
||||
this.scene = new Scene();
|
||||
this.fonts = new Fonts({ scene: this.scene });
|
||||
this.renderer = new Renderer(this.scene);
|
||||
this.files = {};
|
||||
this.imageCache.clear();
|
||||
@@ -2594,9 +2595,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
),
|
||||
addEventListener(window, EVENT.FOCUS, () => {
|
||||
this.maybeCleanupAfterMissingPointerUp(null);
|
||||
// browsers (chrome?) tend to free up memory a lot, which results
|
||||
// in canvas context being cleared. Thus re-render on focus.
|
||||
this.triggerRender(true);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -3672,7 +3670,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
ShapeCache.delete(element);
|
||||
}
|
||||
});
|
||||
this.scene.triggerUpdate();
|
||||
this.scene.informMutation();
|
||||
|
||||
this.addNewImagesToImageCache();
|
||||
},
|
||||
@@ -3732,15 +3730,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
},
|
||||
);
|
||||
|
||||
private triggerRender = (
|
||||
/** force always re-renders canvas even if no change */
|
||||
force?: boolean,
|
||||
) => {
|
||||
if (force === true) {
|
||||
this.scene.triggerUpdate();
|
||||
} else {
|
||||
this.setState({});
|
||||
}
|
||||
private onSceneUpdated = () => {
|
||||
this.setState({});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -4309,22 +4300,25 @@ class App extends React.Component<AppProps, AppState> {
|
||||
) {
|
||||
const elementsMap = this.scene.getElementsMapIncludingDeleted();
|
||||
|
||||
const updateElement = (nextOriginalText: string, isDeleted: boolean) => {
|
||||
const updateElement = (
|
||||
text: string,
|
||||
originalText: string,
|
||||
isDeleted: boolean,
|
||||
) => {
|
||||
this.scene.replaceAllElements([
|
||||
// Not sure why we include deleted elements as well hence using deleted elements map
|
||||
...this.scene.getElementsIncludingDeleted().map((_element) => {
|
||||
if (_element.id === element.id && isTextElement(_element)) {
|
||||
return newElementWith(_element, {
|
||||
originalText: nextOriginalText,
|
||||
isDeleted: isDeleted ?? _element.isDeleted,
|
||||
// returns (wrapped) text and new dimensions
|
||||
...refreshTextDimensions(
|
||||
_element,
|
||||
getContainerElement(_element, elementsMap),
|
||||
elementsMap,
|
||||
nextOriginalText,
|
||||
),
|
||||
});
|
||||
return updateTextElement(
|
||||
_element,
|
||||
getContainerElement(_element, elementsMap),
|
||||
elementsMap,
|
||||
{
|
||||
text,
|
||||
isDeleted,
|
||||
originalText,
|
||||
},
|
||||
);
|
||||
}
|
||||
return _element;
|
||||
}),
|
||||
@@ -4347,15 +4341,15 @@ class App extends React.Component<AppProps, AppState> {
|
||||
viewportY - this.state.offsetTop,
|
||||
];
|
||||
},
|
||||
onChange: withBatchedUpdates((nextOriginalText) => {
|
||||
updateElement(nextOriginalText, false);
|
||||
onChange: withBatchedUpdates((text) => {
|
||||
updateElement(text, text, false);
|
||||
if (isNonDeletedElement(element)) {
|
||||
updateBoundElements(element, elementsMap);
|
||||
}
|
||||
}),
|
||||
onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
|
||||
const isDeleted = !nextOriginalText.trim();
|
||||
updateElement(nextOriginalText, isDeleted);
|
||||
onSubmit: withBatchedUpdates(({ text, viaKeyboard, originalText }) => {
|
||||
const isDeleted = !text.trim();
|
||||
updateElement(text, originalText, isDeleted);
|
||||
// select the created text element only if submitting via keyboard
|
||||
// (when submitting via click it should act as signal to deselect)
|
||||
if (!isDeleted && viaKeyboard) {
|
||||
@@ -4400,7 +4394,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
// do an initial update to re-initialize element position since we were
|
||||
// modifying element's x/y for sake of editor (case: syncing to remote)
|
||||
updateElement(element.originalText, false);
|
||||
updateElement(element.text, element.originalText, false);
|
||||
}
|
||||
|
||||
private deselectElements() {
|
||||
@@ -5107,11 +5101,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
this.translateCanvas({
|
||||
zoom: zoomState.zoom,
|
||||
// 2x multiplier is just a magic number that makes this work correctly
|
||||
// on touchscreen devices (note: if we get report that panning is slower/faster
|
||||
// than actual movement, consider swapping with devicePixelRatio)
|
||||
scrollX: zoomState.scrollX + 2 * (deltaX / nextZoom),
|
||||
scrollY: zoomState.scrollY + 2 * (deltaY / nextZoom),
|
||||
scrollX: zoomState.scrollX + deltaX / nextZoom,
|
||||
scrollY: zoomState.scrollY + deltaY / nextZoom,
|
||||
shouldCacheIgnoreZoom: true,
|
||||
});
|
||||
});
|
||||
@@ -5586,7 +5577,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
|
||||
this.elementsPendingErasure = new Set(this.elementsPendingErasure);
|
||||
this.triggerRender();
|
||||
this.onSceneUpdated();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8078,7 +8069,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
|
||||
this.scene.triggerUpdate();
|
||||
this.scene.informMutation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8573,7 +8564,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
private restoreReadyToEraseElements = () => {
|
||||
this.elementsPendingErasure = new Set();
|
||||
this.triggerRender();
|
||||
this.onSceneUpdated();
|
||||
};
|
||||
|
||||
private eraseElements = () => {
|
||||
@@ -8987,7 +8978,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
files,
|
||||
);
|
||||
if (updatedFiles.size) {
|
||||
this.scene.triggerUpdate();
|
||||
this.scene.informMutation();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -9642,7 +9633,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
|
||||
return [
|
||||
CONTEXT_MENU_SEPARATOR,
|
||||
actionCut,
|
||||
actionCopy,
|
||||
actionPaste,
|
||||
@@ -9655,7 +9645,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
actionPasteStyles,
|
||||
CONTEXT_MENU_SEPARATOR,
|
||||
actionGroup,
|
||||
actionTextAutoResize,
|
||||
actionUnbindText,
|
||||
actionBindText,
|
||||
actionWrapTextInContainer,
|
||||
|
||||
@@ -28,7 +28,6 @@ export const ButtonIconSelect = <T extends Object>(
|
||||
{props.options.map((option) =>
|
||||
props.type === "button" ? (
|
||||
<button
|
||||
type="button"
|
||||
key={option.text}
|
||||
onClick={(event) => props.onClick(option.value, event)}
|
||||
className={clsx({
|
||||
|
||||
@@ -22,12 +22,7 @@ export const CheckboxItem: React.FC<{
|
||||
).focus();
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="Checkbox-box"
|
||||
role="checkbox"
|
||||
aria-checked={checked}
|
||||
>
|
||||
<button className="Checkbox-box" role="checkbox" aria-checked={checked}>
|
||||
{checkIcon}
|
||||
</button>
|
||||
<div className="Checkbox-label">{children}</div>
|
||||
|
||||
@@ -540,7 +540,7 @@ function CommandPaletteInner({
|
||||
...command,
|
||||
icon: command.icon || boltIcon,
|
||||
order: command.order ?? getCategoryOrder(command.category),
|
||||
haystack: `${deburr(command.label.toLocaleLowerCase())} ${
|
||||
haystack: `${deburr(command.label)} ${
|
||||
command.keywords?.join(" ") || ""
|
||||
}`,
|
||||
};
|
||||
@@ -777,9 +777,7 @@ function CommandPaletteInner({
|
||||
return;
|
||||
}
|
||||
|
||||
const _query = deburr(
|
||||
commandSearch.toLocaleLowerCase().replace(/[<>_| -]/g, ""),
|
||||
);
|
||||
const _query = deburr(commandSearch.replace(/[<>-_| ]/g, ""));
|
||||
matchingCommands = fuzzy
|
||||
.filter(_query, matchingCommands, {
|
||||
extract: (command) => command.haystack,
|
||||
|
||||
@@ -105,7 +105,6 @@ export const ContextMenu = React.memo(
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={clsx("context-menu-item", {
|
||||
dangerous: actionName === "deleteSelectedElements",
|
||||
checkmark: item.checked?.(appState),
|
||||
|
||||
@@ -123,7 +123,6 @@ export const Dialog = (props: DialogProps) => {
|
||||
onClick={onClose}
|
||||
title={t("buttons.close")}
|
||||
aria-label={t("buttons.close")}
|
||||
type="button"
|
||||
>
|
||||
{CloseIcon}
|
||||
</button>
|
||||
|
||||
@@ -27,11 +27,7 @@ const FollowMode = ({
|
||||
{userToFollow.username}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onDisconnect}
|
||||
className="follow-mode__disconnect-btn"
|
||||
>
|
||||
<button onClick={onDisconnect} className="follow-mode__disconnect-btn">
|
||||
{CloseIcon}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -108,7 +108,6 @@ function Picker<T>({
|
||||
<div className="picker-content" ref={rGallery}>
|
||||
{options.map((option, i) => (
|
||||
<button
|
||||
type="button"
|
||||
className={clsx("picker-option", {
|
||||
active: value === option.value,
|
||||
})}
|
||||
@@ -172,7 +171,6 @@ export function IconPicker<T>({
|
||||
<div>
|
||||
<button
|
||||
name={group}
|
||||
type="button"
|
||||
className={isActive ? "active" : ""}
|
||||
aria-label={label}
|
||||
onClick={() => setActive(!isActive)}
|
||||
|
||||
@@ -23,7 +23,7 @@ import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
import type { NonDeletedExcalidrawElement } from "../element/types";
|
||||
import { t } from "../i18n";
|
||||
import { isSomeElementSelected } from "../scene";
|
||||
import { exportToCanvas } from "../../utils/export";
|
||||
import { exportToCanvas } from "@excalidraw/utils";
|
||||
|
||||
import { copyIcon, downloadIcon, helpIcon } from "./icons";
|
||||
import { Dialog } from "./Dialog";
|
||||
|
||||
@@ -444,7 +444,7 @@ const LayerUI = ({
|
||||
);
|
||||
ShapeCache.delete(element);
|
||||
}
|
||||
Scene.getScene(selectedElements[0])?.triggerUpdate();
|
||||
Scene.getScene(selectedElements[0])?.informMutation();
|
||||
} else if (colorPickerType === "elementBackground") {
|
||||
setAppState({
|
||||
currentItemBackgroundColor: color,
|
||||
@@ -555,7 +555,6 @@ const LayerUI = ({
|
||||
)}
|
||||
{appState.scrolledOutside && (
|
||||
<button
|
||||
type="button"
|
||||
className="scroll-back-to-content"
|
||||
onClick={() => {
|
||||
setAppState((appState) => ({
|
||||
|
||||
@@ -194,7 +194,6 @@ export const MobileMenu = ({
|
||||
!appState.openMenu &&
|
||||
!appState.openSidebar && (
|
||||
<button
|
||||
type="button"
|
||||
className="scroll-back-to-content"
|
||||
onClick={() => {
|
||||
setAppState((appState) => ({
|
||||
|
||||
@@ -65,7 +65,6 @@ const ChartPreviewBtn = (props: {
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="ChartPreview"
|
||||
onClick={() => {
|
||||
if (chartElements) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { t } from "../i18n";
|
||||
import Trans from "./Trans";
|
||||
|
||||
import type { LibraryItems, LibraryItem, UIAppState } from "../types";
|
||||
import { exportToCanvas, exportToSvg } from "../../utils/export";
|
||||
import { exportToCanvas, exportToSvg } from "@excalidraw/utils";
|
||||
import {
|
||||
EDITOR_LS_KEYS,
|
||||
EXPORT_DATA_TYPES,
|
||||
|
||||
@@ -19,7 +19,7 @@ type InteractiveCanvasProps = {
|
||||
elementsMap: RenderableElementsMap;
|
||||
visibleElements: readonly NonDeletedExcalidrawElement[];
|
||||
selectedElements: readonly NonDeletedExcalidrawElement[];
|
||||
sceneNonce: number | undefined;
|
||||
versionNonce: number | undefined;
|
||||
selectionNonce: number | undefined;
|
||||
scale: number;
|
||||
appState: InteractiveCanvasAppState;
|
||||
@@ -206,10 +206,10 @@ const areEqual = (
|
||||
// This could be further optimised if needed, as we don't have to render interactive canvas on each scene mutation
|
||||
if (
|
||||
prevProps.selectionNonce !== nextProps.selectionNonce ||
|
||||
prevProps.sceneNonce !== nextProps.sceneNonce ||
|
||||
prevProps.versionNonce !== nextProps.versionNonce ||
|
||||
prevProps.scale !== nextProps.scale ||
|
||||
// we need to memoize on elementsMap because they may have renewed
|
||||
// even if sceneNonce didn't change (e.g. we filter elements out based
|
||||
// even if versionNonce didn't change (e.g. we filter elements out based
|
||||
// on appState)
|
||||
prevProps.elementsMap !== nextProps.elementsMap ||
|
||||
prevProps.visibleElements !== nextProps.visibleElements ||
|
||||
|
||||
@@ -19,7 +19,7 @@ type StaticCanvasProps = {
|
||||
elementsMap: RenderableElementsMap;
|
||||
allElementsMap: NonDeletedSceneElementsMap;
|
||||
visibleElements: readonly NonDeletedExcalidrawElement[];
|
||||
sceneNonce: number | undefined;
|
||||
versionNonce: number | undefined;
|
||||
selectionNonce: number | undefined;
|
||||
scale: number;
|
||||
appState: StaticCanvasAppState;
|
||||
@@ -112,10 +112,10 @@ const areEqual = (
|
||||
nextProps: StaticCanvasProps,
|
||||
) => {
|
||||
if (
|
||||
prevProps.sceneNonce !== nextProps.sceneNonce ||
|
||||
prevProps.versionNonce !== nextProps.versionNonce ||
|
||||
prevProps.scale !== nextProps.scale ||
|
||||
// we need to memoize on elementsMap because they may have renewed
|
||||
// even if sceneNonce didn't change (e.g. we filter elements out based
|
||||
// even if versionNonce didn't change (e.g. we filter elements out based
|
||||
// on appState)
|
||||
prevProps.elementsMap !== nextProps.elementsMap ||
|
||||
prevProps.visibleElements !== nextProps.visibleElements
|
||||
|
||||
@@ -698,18 +698,14 @@ export const BringForwardIcon = createIcon(arrownNarrowUpJSX, tablerIconProps);
|
||||
|
||||
export const SendBackwardIcon = createIcon(arrownNarrowUpJSX, {
|
||||
...tablerIconProps,
|
||||
style: {
|
||||
transform: "rotate(180deg)",
|
||||
},
|
||||
transform: "rotate(180)",
|
||||
});
|
||||
|
||||
export const BringToFrontIcon = createIcon(arrowBarToTopJSX, tablerIconProps);
|
||||
|
||||
export const SendToBackIcon = createIcon(arrowBarToTopJSX, {
|
||||
...tablerIconProps,
|
||||
style: {
|
||||
transform: "rotate(180deg)",
|
||||
},
|
||||
transform: "rotate(180)",
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
@@ -228,7 +228,6 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to existing text elements when start / end provided with ids 1`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": [
|
||||
{
|
||||
@@ -274,7 +273,6 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to existing text elements when start / end provided with ids 2`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": [
|
||||
{
|
||||
@@ -380,7 +378,6 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to existing text elements when start / end provided with ids 4`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id48",
|
||||
@@ -481,7 +478,6 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to shapes when start / end provided without ids 2`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id37",
|
||||
@@ -656,7 +652,6 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to text when start / end provided without ids 2`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id41",
|
||||
@@ -697,7 +692,6 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to text when start / end provided without ids 3`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": [
|
||||
{
|
||||
@@ -743,7 +737,6 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
|
||||
exports[`Test Transform > Test arrow bindings > should bind arrows to text when start / end provided without ids 4`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": [
|
||||
{
|
||||
@@ -1201,7 +1194,6 @@ exports[`Test Transform > should transform regular shapes 6`] = `
|
||||
exports[`Test Transform > should transform text element 1`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": null,
|
||||
@@ -1242,7 +1234,6 @@ exports[`Test Transform > should transform text element 1`] = `
|
||||
exports[`Test Transform > should transform text element 2`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": null,
|
||||
@@ -1575,7 +1566,6 @@ exports[`Test Transform > should transform the elements correctly when linear el
|
||||
exports[`Test Transform > should transform the elements correctly when linear elements have single point 7`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "B",
|
||||
@@ -1618,7 +1608,6 @@ exports[`Test Transform > should transform the elements correctly when linear el
|
||||
exports[`Test Transform > should transform the elements correctly when linear elements have single point 8`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "A",
|
||||
@@ -1661,7 +1650,6 @@ exports[`Test Transform > should transform the elements correctly when linear el
|
||||
exports[`Test Transform > should transform the elements correctly when linear elements have single point 9`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "Alice",
|
||||
@@ -1704,7 +1692,6 @@ exports[`Test Transform > should transform the elements correctly when linear el
|
||||
exports[`Test Transform > should transform the elements correctly when linear elements have single point 10`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "Bob",
|
||||
@@ -1747,7 +1734,6 @@ exports[`Test Transform > should transform the elements correctly when linear el
|
||||
exports[`Test Transform > should transform the elements correctly when linear elements have single point 11`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "Bob_Alice",
|
||||
@@ -1788,7 +1774,6 @@ exports[`Test Transform > should transform the elements correctly when linear el
|
||||
exports[`Test Transform > should transform the elements correctly when linear elements have single point 12`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "Bob_B",
|
||||
@@ -2037,7 +2022,6 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
exports[`Test Transform > should transform to labelled arrows when label provided for arrows 5`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id25",
|
||||
@@ -2078,7 +2062,6 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
exports[`Test Transform > should transform to labelled arrows when label provided for arrows 6`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id26",
|
||||
@@ -2119,7 +2102,6 @@ exports[`Test Transform > should transform to labelled arrows when label provide
|
||||
exports[`Test Transform > should transform to labelled arrows when label provided for arrows 7`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id27",
|
||||
@@ -2161,7 +2143,6 @@ LABELLED ARROW",
|
||||
exports[`Test Transform > should transform to labelled arrows when label provided for arrows 8`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id28",
|
||||
@@ -2425,7 +2406,6 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
exports[`Test Transform > should transform to text containers when label provided 7`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id13",
|
||||
@@ -2466,7 +2446,6 @@ exports[`Test Transform > should transform to text containers when label provide
|
||||
exports[`Test Transform > should transform to text containers when label provided 8`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id14",
|
||||
@@ -2508,7 +2487,6 @@ CONTAINER",
|
||||
exports[`Test Transform > should transform to text containers when label provided 9`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id15",
|
||||
@@ -2552,7 +2530,6 @@ CONTAINER",
|
||||
exports[`Test Transform > should transform to text containers when label provided 10`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id16",
|
||||
@@ -2594,7 +2571,6 @@ TEXT CONTAINER",
|
||||
exports[`Test Transform > should transform to text containers when label provided 11`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id17",
|
||||
@@ -2637,7 +2613,6 @@ CONTAINER",
|
||||
exports[`Test Transform > should transform to text containers when label provided 12`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": null,
|
||||
"containerId": "id18",
|
||||
|
||||
@@ -140,7 +140,7 @@ const restoreElementWithProperties = <
|
||||
seed: element.seed ?? 1,
|
||||
groupIds: element.groupIds ?? [],
|
||||
frameId: element.frameId ?? null,
|
||||
roundness: typeof element.roundness !== "undefined"
|
||||
roundness: element.roundness
|
||||
? element.roundness
|
||||
: element.strokeSharpness === "round"
|
||||
? {
|
||||
@@ -208,7 +208,7 @@ const restoreElement = (
|
||||
verticalAlign: element.verticalAlign || DEFAULT_VERTICAL_ALIGN,
|
||||
containerId: element.containerId ?? null,
|
||||
originalText: element.originalText || text,
|
||||
autoResize: element.autoResize ?? true,
|
||||
|
||||
lineHeight,
|
||||
});
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import type {
|
||||
|
||||
import { getElementAbsoluteCoords } from "./bounds";
|
||||
import type { AppClassProperties, AppState, Point } from "../types";
|
||||
import { isPointOnShape } from "../../utils/collision";
|
||||
import { isPointOnShape } from "@excalidraw/utils";
|
||||
import { getElementAtPosition } from "../scene";
|
||||
import {
|
||||
isArrowElement,
|
||||
|
||||
@@ -8,9 +8,12 @@ import type {
|
||||
|
||||
import { getElementBounds } from "./bounds";
|
||||
import type { FrameNameBounds } from "../types";
|
||||
import type { Polygon, GeometricShape } from "../../utils/geometry/shape";
|
||||
import { getPolygonShape } from "../../utils/geometry/shape";
|
||||
import { isPointInShape, isPointOnShape } from "../../utils/collision";
|
||||
import type { Polygon, GeometricShape } from "@excalidraw/utils";
|
||||
import {
|
||||
getPolygonShape,
|
||||
isPointInShape,
|
||||
isPointOnShape,
|
||||
} from "@excalidraw/utils";
|
||||
import { isTransparent } from "../utils";
|
||||
import {
|
||||
hasBoundTextElement,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { isLinearElementType } from "./typeChecks";
|
||||
export {
|
||||
newElement,
|
||||
newTextElement,
|
||||
updateTextElement,
|
||||
refreshTextDimensions,
|
||||
newLinearElement,
|
||||
newImageElement,
|
||||
|
||||
@@ -98,7 +98,7 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
|
||||
element.updated = getUpdatedTimestamp();
|
||||
|
||||
if (informMutation) {
|
||||
Scene.getScene(element)?.triggerUpdate();
|
||||
Scene.getScene(element)?.informMutation();
|
||||
}
|
||||
|
||||
return element;
|
||||
@@ -107,8 +107,6 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
|
||||
export const newElementWith = <TElement extends ExcalidrawElement>(
|
||||
element: TElement,
|
||||
updates: ElementUpdate<TElement>,
|
||||
/** pass `true` to always regenerate */
|
||||
force = false,
|
||||
): TElement => {
|
||||
let didChange = false;
|
||||
for (const key in updates) {
|
||||
@@ -125,7 +123,7 @@ export const newElementWith = <TElement extends ExcalidrawElement>(
|
||||
}
|
||||
}
|
||||
|
||||
if (!didChange && !force) {
|
||||
if (!didChange) {
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
@@ -240,28 +240,24 @@ export const newTextElement = (
|
||||
metrics,
|
||||
);
|
||||
|
||||
const textElementProps: ExcalidrawTextElement = {
|
||||
..._newElementBase<ExcalidrawTextElement>("text", opts),
|
||||
text,
|
||||
fontSize,
|
||||
fontFamily,
|
||||
textAlign,
|
||||
verticalAlign,
|
||||
x: opts.x - offsets.x,
|
||||
y: opts.y - offsets.y,
|
||||
width: metrics.width,
|
||||
height: metrics.height,
|
||||
containerId: opts.containerId || null,
|
||||
originalText: text,
|
||||
autoResize: true,
|
||||
lineHeight,
|
||||
};
|
||||
|
||||
const textElement: ExcalidrawTextElement = newElementWith(
|
||||
textElementProps,
|
||||
const textElement = newElementWith(
|
||||
{
|
||||
..._newElementBase<ExcalidrawTextElement>("text", opts),
|
||||
text,
|
||||
fontSize,
|
||||
fontFamily,
|
||||
textAlign,
|
||||
verticalAlign,
|
||||
x: opts.x - offsets.x,
|
||||
y: opts.y - offsets.y,
|
||||
width: metrics.width,
|
||||
height: metrics.height,
|
||||
containerId: opts.containerId || null,
|
||||
originalText: text,
|
||||
lineHeight,
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
return textElement;
|
||||
};
|
||||
|
||||
@@ -275,25 +271,18 @@ const getAdjustedDimensions = (
|
||||
width: number;
|
||||
height: number;
|
||||
} => {
|
||||
let { width: nextWidth, height: nextHeight } = measureText(
|
||||
const { width: nextWidth, height: nextHeight } = measureText(
|
||||
nextText,
|
||||
getFontString(element),
|
||||
element.lineHeight,
|
||||
);
|
||||
|
||||
// wrapped text
|
||||
if (!element.autoResize) {
|
||||
nextWidth = element.width;
|
||||
}
|
||||
|
||||
const { textAlign, verticalAlign } = element;
|
||||
let x: number;
|
||||
let y: number;
|
||||
if (
|
||||
textAlign === "center" &&
|
||||
verticalAlign === VERTICAL_ALIGN.MIDDLE &&
|
||||
!element.containerId &&
|
||||
element.autoResize
|
||||
!element.containerId
|
||||
) {
|
||||
const prevMetrics = measureText(
|
||||
element.text,
|
||||
@@ -354,19 +343,38 @@ export const refreshTextDimensions = (
|
||||
if (textElement.isDeleted) {
|
||||
return;
|
||||
}
|
||||
if (container || !textElement.autoResize) {
|
||||
if (container) {
|
||||
text = wrapText(
|
||||
text,
|
||||
getFontString(textElement),
|
||||
container
|
||||
? getBoundTextMaxWidth(container, textElement)
|
||||
: textElement.width,
|
||||
getBoundTextMaxWidth(container, textElement),
|
||||
);
|
||||
}
|
||||
const dimensions = getAdjustedDimensions(textElement, elementsMap, text);
|
||||
return { text, ...dimensions };
|
||||
};
|
||||
|
||||
export const updateTextElement = (
|
||||
textElement: ExcalidrawTextElement,
|
||||
container: ExcalidrawTextContainer | null,
|
||||
elementsMap: ElementsMap,
|
||||
{
|
||||
text,
|
||||
isDeleted,
|
||||
originalText,
|
||||
}: {
|
||||
text: string;
|
||||
isDeleted?: boolean;
|
||||
originalText: string;
|
||||
},
|
||||
): ExcalidrawTextElement => {
|
||||
return newElementWith(textElement, {
|
||||
originalText,
|
||||
isDeleted: isDeleted ?? textElement.isDeleted,
|
||||
...refreshTextDimensions(textElement, container, elementsMap, originalText),
|
||||
});
|
||||
};
|
||||
|
||||
export const newFreeDrawElement = (
|
||||
opts: {
|
||||
type: "freedraw";
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
BOUND_TEXT_PADDING,
|
||||
MIN_FONT_SIZE,
|
||||
SHIFT_LOCKING_ANGLE,
|
||||
} from "../constants";
|
||||
import { MIN_FONT_SIZE, SHIFT_LOCKING_ANGLE } from "../constants";
|
||||
import { rescalePoints } from "../points";
|
||||
|
||||
import { rotate, centerPoint, rotatePoint } from "../math";
|
||||
@@ -49,9 +45,6 @@ import {
|
||||
handleBindTextResize,
|
||||
getBoundTextMaxWidth,
|
||||
getApproxMinLineHeight,
|
||||
wrapText,
|
||||
measureText,
|
||||
getMinCharWidth,
|
||||
} from "./textElement";
|
||||
import { LinearElementEditor } from "./linearElementEditor";
|
||||
import { isInGroup } from "../groups";
|
||||
@@ -91,9 +84,14 @@ export const transformElements = (
|
||||
shouldRotateWithDiscreteAngle,
|
||||
);
|
||||
updateBoundElements(element, elementsMap);
|
||||
} else if (isTextElement(element) && transformHandleType) {
|
||||
} else if (
|
||||
isTextElement(element) &&
|
||||
(transformHandleType === "nw" ||
|
||||
transformHandleType === "ne" ||
|
||||
transformHandleType === "sw" ||
|
||||
transformHandleType === "se")
|
||||
) {
|
||||
resizeSingleTextElement(
|
||||
originalElements,
|
||||
element,
|
||||
elementsMap,
|
||||
transformHandleType,
|
||||
@@ -225,10 +223,9 @@ const measureFontSizeFromWidth = (
|
||||
};
|
||||
|
||||
const resizeSingleTextElement = (
|
||||
originalElements: PointerDownState["originalElements"],
|
||||
element: NonDeleted<ExcalidrawTextElement>,
|
||||
elementsMap: ElementsMap,
|
||||
transformHandleType: TransformHandleDirection,
|
||||
transformHandleType: "nw" | "ne" | "sw" | "se",
|
||||
shouldResizeFromCenter: boolean,
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
@@ -248,19 +245,17 @@ const resizeSingleTextElement = (
|
||||
let scaleX = 0;
|
||||
let scaleY = 0;
|
||||
|
||||
if (transformHandleType !== "e" && transformHandleType !== "w") {
|
||||
if (transformHandleType.includes("e")) {
|
||||
scaleX = (rotatedX - x1) / (x2 - x1);
|
||||
}
|
||||
if (transformHandleType.includes("w")) {
|
||||
scaleX = (x2 - rotatedX) / (x2 - x1);
|
||||
}
|
||||
if (transformHandleType.includes("n")) {
|
||||
scaleY = (y2 - rotatedY) / (y2 - y1);
|
||||
}
|
||||
if (transformHandleType.includes("s")) {
|
||||
scaleY = (rotatedY - y1) / (y2 - y1);
|
||||
}
|
||||
if (transformHandleType.includes("e")) {
|
||||
scaleX = (rotatedX - x1) / (x2 - x1);
|
||||
}
|
||||
if (transformHandleType.includes("w")) {
|
||||
scaleX = (x2 - rotatedX) / (x2 - x1);
|
||||
}
|
||||
if (transformHandleType.includes("n")) {
|
||||
scaleY = (y2 - rotatedY) / (y2 - y1);
|
||||
}
|
||||
if (transformHandleType.includes("s")) {
|
||||
scaleY = (rotatedY - y1) / (y2 - y1);
|
||||
}
|
||||
|
||||
const scale = Math.max(scaleX, scaleY);
|
||||
@@ -323,102 +318,6 @@ const resizeSingleTextElement = (
|
||||
y: nextY,
|
||||
});
|
||||
}
|
||||
|
||||
if (transformHandleType === "e" || transformHandleType === "w") {
|
||||
const stateAtResizeStart = originalElements.get(element.id)!;
|
||||
const [x1, y1, x2, y2] = getResizedElementAbsoluteCoords(
|
||||
stateAtResizeStart,
|
||||
stateAtResizeStart.width,
|
||||
stateAtResizeStart.height,
|
||||
true,
|
||||
);
|
||||
const startTopLeft: Point = [x1, y1];
|
||||
const startBottomRight: Point = [x2, y2];
|
||||
const startCenter: Point = centerPoint(startTopLeft, startBottomRight);
|
||||
|
||||
const rotatedPointer = rotatePoint(
|
||||
[pointerX, pointerY],
|
||||
startCenter,
|
||||
-stateAtResizeStart.angle,
|
||||
);
|
||||
|
||||
const [esx1, , esx2] = getResizedElementAbsoluteCoords(
|
||||
element,
|
||||
element.width,
|
||||
element.height,
|
||||
true,
|
||||
);
|
||||
|
||||
const boundsCurrentWidth = esx2 - esx1;
|
||||
|
||||
const atStartBoundsWidth = startBottomRight[0] - startTopLeft[0];
|
||||
const minWidth =
|
||||
getMinCharWidth(getFontString(element)) + BOUND_TEXT_PADDING * 2;
|
||||
|
||||
let scaleX = atStartBoundsWidth / boundsCurrentWidth;
|
||||
|
||||
if (transformHandleType.includes("e")) {
|
||||
scaleX = (rotatedPointer[0] - startTopLeft[0]) / boundsCurrentWidth;
|
||||
}
|
||||
if (transformHandleType.includes("w")) {
|
||||
scaleX = (startBottomRight[0] - rotatedPointer[0]) / boundsCurrentWidth;
|
||||
}
|
||||
|
||||
const newWidth =
|
||||
element.width * scaleX < minWidth ? minWidth : element.width * scaleX;
|
||||
|
||||
const text = wrapText(
|
||||
element.originalText,
|
||||
getFontString(element),
|
||||
Math.abs(newWidth),
|
||||
);
|
||||
const metrics = measureText(
|
||||
text,
|
||||
getFontString(element),
|
||||
element.lineHeight,
|
||||
);
|
||||
|
||||
const eleNewHeight = metrics.height;
|
||||
|
||||
const [newBoundsX1, newBoundsY1, newBoundsX2, newBoundsY2] =
|
||||
getResizedElementAbsoluteCoords(
|
||||
stateAtResizeStart,
|
||||
newWidth,
|
||||
eleNewHeight,
|
||||
true,
|
||||
);
|
||||
const newBoundsWidth = newBoundsX2 - newBoundsX1;
|
||||
const newBoundsHeight = newBoundsY2 - newBoundsY1;
|
||||
|
||||
let newTopLeft = [...startTopLeft] as [number, number];
|
||||
if (["n", "w", "nw"].includes(transformHandleType)) {
|
||||
newTopLeft = [
|
||||
startBottomRight[0] - Math.abs(newBoundsWidth),
|
||||
startTopLeft[1],
|
||||
];
|
||||
}
|
||||
|
||||
// adjust topLeft to new rotation point
|
||||
const angle = stateAtResizeStart.angle;
|
||||
const rotatedTopLeft = rotatePoint(newTopLeft, startCenter, angle);
|
||||
const newCenter: Point = [
|
||||
newTopLeft[0] + Math.abs(newBoundsWidth) / 2,
|
||||
newTopLeft[1] + Math.abs(newBoundsHeight) / 2,
|
||||
];
|
||||
const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle);
|
||||
newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle);
|
||||
|
||||
const resizedElement: Partial<ExcalidrawTextElement> = {
|
||||
width: Math.abs(newWidth),
|
||||
height: Math.abs(metrics.height),
|
||||
x: newTopLeft[0],
|
||||
y: newTopLeft[1],
|
||||
text,
|
||||
autoResize: false,
|
||||
};
|
||||
|
||||
mutateElement(element, resizedElement);
|
||||
}
|
||||
};
|
||||
|
||||
export const resizeSingleElement = (
|
||||
@@ -977,7 +876,7 @@ export const resizeMultipleElements = (
|
||||
}
|
||||
}
|
||||
|
||||
Scene.getScene(elementsAndUpdates[0].element)?.triggerUpdate();
|
||||
Scene.getScene(elementsAndUpdates[0].element)?.informMutation();
|
||||
};
|
||||
|
||||
const rotateMultipleElements = (
|
||||
@@ -1039,7 +938,7 @@ const rotateMultipleElements = (
|
||||
}
|
||||
});
|
||||
|
||||
Scene.getScene(elements[0])?.triggerUpdate();
|
||||
Scene.getScene(elements[0])?.informMutation();
|
||||
};
|
||||
|
||||
export const getResizeOffsetXY = (
|
||||
|
||||
@@ -20,12 +20,8 @@ import type { AppState, Device, Zoom } from "../types";
|
||||
import type { Bounds } from "./bounds";
|
||||
import { getElementAbsoluteCoords } from "./bounds";
|
||||
import { SIDE_RESIZING_THRESHOLD } from "../constants";
|
||||
import {
|
||||
angleToDegrees,
|
||||
pointOnLine,
|
||||
pointRotate,
|
||||
} from "../../utils/geometry/geometry";
|
||||
import type { Line, Point } from "../../utils/geometry/shape";
|
||||
import { angleToDegrees, pointOnLine, pointRotate } from "@excalidraw/utils";
|
||||
import type { Line, Point } from "@excalidraw/utils";
|
||||
import { isLinearElement } from "./typeChecks";
|
||||
|
||||
const isInsideTransformHandle = (
|
||||
@@ -87,8 +83,12 @@ export const resizeTest = (
|
||||
elementsMap,
|
||||
);
|
||||
|
||||
// do not resize from the sides for linear elements with only two points
|
||||
if (!(isLinearElement(element) && element.points.length <= 2)) {
|
||||
// Note that for a text element, when "resized" from the side
|
||||
// we should make it wrap/unwrap
|
||||
if (
|
||||
element.type !== "text" &&
|
||||
!(isLinearElement(element) && element.points.length <= 2)
|
||||
) {
|
||||
const SPACING = SIDE_RESIZING_THRESHOLD / zoom.value;
|
||||
const sides = getSelectionBorders(
|
||||
[x1 - SPACING, y1 - SPACING],
|
||||
|
||||
@@ -48,7 +48,7 @@ export const redrawTextBoundingBox = (
|
||||
textElement: ExcalidrawTextElement,
|
||||
container: ExcalidrawElement | null,
|
||||
elementsMap: ElementsMap,
|
||||
informMutation = true,
|
||||
informMutation: boolean = true,
|
||||
) => {
|
||||
let maxWidth = undefined;
|
||||
const boundTextUpdates = {
|
||||
@@ -62,27 +62,21 @@ export const redrawTextBoundingBox = (
|
||||
|
||||
boundTextUpdates.text = textElement.text;
|
||||
|
||||
if (container || !textElement.autoResize) {
|
||||
maxWidth = container
|
||||
? getBoundTextMaxWidth(container, textElement)
|
||||
: textElement.width;
|
||||
if (container) {
|
||||
maxWidth = getBoundTextMaxWidth(container, textElement);
|
||||
boundTextUpdates.text = wrapText(
|
||||
textElement.originalText,
|
||||
getFontString(textElement),
|
||||
maxWidth,
|
||||
);
|
||||
}
|
||||
|
||||
const metrics = measureText(
|
||||
boundTextUpdates.text,
|
||||
getFontString(textElement),
|
||||
textElement.lineHeight,
|
||||
);
|
||||
|
||||
// Note: only update width for unwrapped text and bound texts (which always have autoResize set to true)
|
||||
if (textElement.autoResize) {
|
||||
boundTextUpdates.width = metrics.width;
|
||||
}
|
||||
boundTextUpdates.width = metrics.width;
|
||||
boundTextUpdates.height = metrics.height;
|
||||
|
||||
if (container) {
|
||||
|
||||
@@ -236,117 +236,6 @@ describe("textWysiwyg", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Test text wrapping", () => {
|
||||
const { h } = window;
|
||||
const dimensions = { height: 400, width: 800 };
|
||||
|
||||
beforeAll(() => {
|
||||
mockBoundingClientRect(dimensions);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await render(<Excalidraw handleKeyboardGlobally={true} />);
|
||||
// @ts-ignore
|
||||
h.app.refreshViewportBreakpoints();
|
||||
// @ts-ignore
|
||||
h.app.refreshEditorBreakpoints();
|
||||
|
||||
h.elements = [];
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
restoreOriginalGetBoundingClientRect();
|
||||
});
|
||||
|
||||
it("should keep width when editing a wrapped text", async () => {
|
||||
const text = API.createElement({
|
||||
type: "text",
|
||||
text: "Excalidraw\nEditor",
|
||||
});
|
||||
|
||||
h.elements = [text];
|
||||
|
||||
const prevWidth = text.width;
|
||||
const prevHeight = text.height;
|
||||
const prevText = text.text;
|
||||
|
||||
// text is wrapped
|
||||
UI.resize(text, "e", [-20, 0]);
|
||||
expect(text.width).not.toEqual(prevWidth);
|
||||
expect(text.height).not.toEqual(prevHeight);
|
||||
expect(text.text).not.toEqual(prevText);
|
||||
expect(text.autoResize).toBe(false);
|
||||
|
||||
const wrappedWidth = text.width;
|
||||
const wrappedHeight = text.height;
|
||||
const wrappedText = text.text;
|
||||
|
||||
// edit text
|
||||
UI.clickTool("selection");
|
||||
mouse.doubleClickAt(text.x + text.width / 2, text.y + text.height / 2);
|
||||
const editor = await getTextEditor(textEditorSelector);
|
||||
expect(editor).not.toBe(null);
|
||||
expect(h.state.editingElement?.id).toBe(text.id);
|
||||
expect(h.elements.length).toBe(1);
|
||||
|
||||
const nextText = `${wrappedText} is great!`;
|
||||
updateTextEditor(editor, nextText);
|
||||
await new Promise((cb) => setTimeout(cb, 0));
|
||||
editor.blur();
|
||||
|
||||
expect(h.elements[0].width).toEqual(wrappedWidth);
|
||||
expect(h.elements[0].height).toBeGreaterThan(wrappedHeight);
|
||||
|
||||
// remove all texts and then add it back editing
|
||||
updateTextEditor(editor, "");
|
||||
await new Promise((cb) => setTimeout(cb, 0));
|
||||
updateTextEditor(editor, nextText);
|
||||
await new Promise((cb) => setTimeout(cb, 0));
|
||||
editor.blur();
|
||||
|
||||
expect(h.elements[0].width).toEqual(wrappedWidth);
|
||||
});
|
||||
|
||||
it("should restore original text after unwrapping a wrapped text", async () => {
|
||||
const originalText = "Excalidraw\neditor\nis great!";
|
||||
const text = API.createElement({
|
||||
type: "text",
|
||||
text: originalText,
|
||||
});
|
||||
h.elements = [text];
|
||||
|
||||
// wrap
|
||||
UI.resize(text, "e", [-40, 0]);
|
||||
// enter text editing mode
|
||||
UI.clickTool("selection");
|
||||
mouse.doubleClickAt(text.x + text.width / 2, text.y + text.height / 2);
|
||||
const editor = await getTextEditor(textEditorSelector);
|
||||
editor.blur();
|
||||
// restore after unwrapping
|
||||
UI.resize(text, "e", [40, 0]);
|
||||
expect((h.elements[0] as ExcalidrawTextElement).text).toBe(originalText);
|
||||
|
||||
// wrap again and add a new line
|
||||
UI.resize(text, "e", [-30, 0]);
|
||||
const wrappedText = text.text;
|
||||
UI.clickTool("selection");
|
||||
mouse.doubleClickAt(text.x + text.width / 2, text.y + text.height / 2);
|
||||
updateTextEditor(editor, `${wrappedText}\nA new line!`);
|
||||
await new Promise((cb) => setTimeout(cb, 0));
|
||||
editor.blur();
|
||||
// remove the newly added line
|
||||
UI.clickTool("selection");
|
||||
mouse.doubleClickAt(text.x + text.width / 2, text.y + text.height / 2);
|
||||
updateTextEditor(editor, wrappedText);
|
||||
await new Promise((cb) => setTimeout(cb, 0));
|
||||
editor.blur();
|
||||
// unwrap
|
||||
UI.resize(text, "e", [30, 0]);
|
||||
// expect the text to be restored the same
|
||||
expect((h.elements[0] as ExcalidrawTextElement).text).toBe(originalText);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Test container-unbound text", () => {
|
||||
const { h } = window;
|
||||
const dimensions = { height: 400, width: 800 };
|
||||
@@ -911,15 +800,26 @@ describe("textWysiwyg", () => {
|
||||
mouse.down();
|
||||
|
||||
const text = h.elements[1] as ExcalidrawTextElementWithContainer;
|
||||
const editor = await getTextEditor(textEditorSelector, true);
|
||||
let editor = await getTextEditor(textEditorSelector, true);
|
||||
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
updateTextEditor(editor, "Hello World!");
|
||||
editor.blur();
|
||||
expect(text.fontFamily).toEqual(FONT_FAMILY.Virgil);
|
||||
UI.clickTool("text");
|
||||
|
||||
mouse.clickAt(
|
||||
rectangle.x + rectangle.width / 2,
|
||||
rectangle.y + rectangle.height / 2,
|
||||
);
|
||||
mouse.down();
|
||||
editor = await getTextEditor(textEditorSelector, true);
|
||||
|
||||
editor.select();
|
||||
fireEvent.click(screen.getByTitle(/code/i));
|
||||
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
editor.blur();
|
||||
expect(
|
||||
(h.elements[1] as ExcalidrawTextElementWithContainer).fontFamily,
|
||||
).toEqual(FONT_FAMILY.Cascadia);
|
||||
|
||||
@@ -79,14 +79,12 @@ export const textWysiwyg = ({
|
||||
app,
|
||||
}: {
|
||||
id: ExcalidrawElement["id"];
|
||||
/**
|
||||
* textWysiwyg only deals with `originalText`
|
||||
*
|
||||
* Note: `text`, which can be wrapped and therefore different from `originalText`,
|
||||
* is derived from `originalText`
|
||||
*/
|
||||
onChange?: (nextOriginalText: string) => void;
|
||||
onSubmit: (data: { viaKeyboard: boolean; nextOriginalText: string }) => void;
|
||||
onChange?: (text: string) => void;
|
||||
onSubmit: (data: {
|
||||
text: string;
|
||||
viaKeyboard: boolean;
|
||||
originalText: string;
|
||||
}) => void;
|
||||
getViewportCoords: (x: number, y: number) => [number, number];
|
||||
element: ExcalidrawTextElement;
|
||||
canvas: HTMLCanvasElement;
|
||||
@@ -131,8 +129,11 @@ export const textWysiwyg = ({
|
||||
app.scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
let maxWidth = updatedTextElement.width;
|
||||
|
||||
let maxHeight = updatedTextElement.height;
|
||||
let textElementWidth = updatedTextElement.width;
|
||||
// Set to element height by default since that's
|
||||
// what is going to be used for unbounded text
|
||||
const textElementHeight = updatedTextElement.height;
|
||||
|
||||
if (container && updatedTextElement.containerId) {
|
||||
@@ -225,8 +226,6 @@ export const textWysiwyg = ({
|
||||
if (!container) {
|
||||
maxWidth = (appState.width - 8 - viewportX) / appState.zoom.value;
|
||||
textElementWidth = Math.min(textElementWidth, maxWidth);
|
||||
} else {
|
||||
textElementWidth += 0.5;
|
||||
}
|
||||
|
||||
// Make sure text editor height doesn't go beyond viewport
|
||||
@@ -261,7 +260,6 @@ export const textWysiwyg = ({
|
||||
if (isTestEnv()) {
|
||||
editable.style.fontFamily = getFontFamilyString(updatedTextElement);
|
||||
}
|
||||
|
||||
mutateElement(updatedTextElement, { x: coordX, y: coordY });
|
||||
}
|
||||
};
|
||||
@@ -278,7 +276,7 @@ export const textWysiwyg = ({
|
||||
let whiteSpace = "pre";
|
||||
let wordBreak = "normal";
|
||||
|
||||
if (isBoundToContainer(element) || !element.autoResize) {
|
||||
if (isBoundToContainer(element)) {
|
||||
whiteSpace = "pre-wrap";
|
||||
wordBreak = "break-word";
|
||||
}
|
||||
@@ -501,12 +499,14 @@ export const textWysiwyg = ({
|
||||
if (!updateElement) {
|
||||
return;
|
||||
}
|
||||
let text = editable.value;
|
||||
const container = getContainerElement(
|
||||
updateElement,
|
||||
app.scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
|
||||
if (container) {
|
||||
text = updateElement.text;
|
||||
if (editable.value.trim()) {
|
||||
const boundTextElementId = getBoundTextElementId(container);
|
||||
if (!boundTextElementId || boundTextElementId !== element.id) {
|
||||
@@ -538,8 +538,9 @@ export const textWysiwyg = ({
|
||||
}
|
||||
|
||||
onSubmit({
|
||||
text,
|
||||
viaKeyboard: submittedViaKeyboard,
|
||||
nextOriginalText: editable.value,
|
||||
originalText: editable.value,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -643,7 +644,7 @@ export const textWysiwyg = ({
|
||||
};
|
||||
|
||||
// handle updates of textElement properties of editing element
|
||||
const unbindUpdate = Scene.getScene(element)!.onUpdate(() => {
|
||||
const unbindUpdate = Scene.getScene(element)!.addCallback(() => {
|
||||
updateWysiwygStyle();
|
||||
const isColorPickerActive = !!document.activeElement?.closest(
|
||||
".color-picker-content",
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { Bounds } from "./bounds";
|
||||
import { getElementAbsoluteCoords } from "./bounds";
|
||||
import { rotate } from "../math";
|
||||
import type { Device, InteractiveCanvasAppState, Zoom } from "../types";
|
||||
import { isTextElement } from ".";
|
||||
import { isFrameLikeElement, isLinearElement } from "./typeChecks";
|
||||
import {
|
||||
DEFAULT_TRANSFORM_HANDLE_SPACING,
|
||||
@@ -64,6 +65,13 @@ export const OMIT_SIDES_FOR_FRAME = {
|
||||
rotation: true,
|
||||
};
|
||||
|
||||
const OMIT_SIDES_FOR_TEXT_ELEMENT = {
|
||||
e: true,
|
||||
s: true,
|
||||
n: true,
|
||||
w: true,
|
||||
};
|
||||
|
||||
const OMIT_SIDES_FOR_LINE_SLASH = {
|
||||
e: true,
|
||||
s: true,
|
||||
@@ -282,6 +290,8 @@ export const getTransformHandles = (
|
||||
omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
|
||||
}
|
||||
}
|
||||
} else if (isTextElement(element)) {
|
||||
omitSides = OMIT_SIDES_FOR_TEXT_ELEMENT;
|
||||
} else if (isFrameLikeElement(element)) {
|
||||
omitSides = {
|
||||
...omitSides,
|
||||
|
||||
@@ -193,13 +193,6 @@ export type ExcalidrawTextElement = _ExcalidrawElementBase &
|
||||
verticalAlign: VerticalAlign;
|
||||
containerId: ExcalidrawGenericElement["id"] | null;
|
||||
originalText: string;
|
||||
/**
|
||||
* If `true` the width will fit the text. If `false`, the text will
|
||||
* wrap to fit the width.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
autoResize: boolean;
|
||||
/**
|
||||
* Unitless line height (aligned to W3C). To get line height in px, multiply
|
||||
* with font size (using `getLineHeightInPx` helper).
|
||||
|
||||
@@ -27,7 +27,10 @@ import { getElementsWithinSelection, getSelectedElements } from "./scene";
|
||||
import { getElementsInGroup, selectGroupsFromGivenElements } from "./groups";
|
||||
import type { ExcalidrawElementsIncludingDeleted } from "./scene/Scene";
|
||||
import { getElementLineSegments } from "./element/bounds";
|
||||
import { doLineSegmentsIntersect, elementsOverlappingBBox } from "../utils/";
|
||||
import {
|
||||
doLineSegmentsIntersect,
|
||||
elementsOverlappingBBox,
|
||||
} from "@excalidraw/utils";
|
||||
import { isFrameElement, isFrameLikeElement } from "./element/typeChecks";
|
||||
import type { ReadonlySetLike } from "./utility-types";
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { atom, useAtom } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
import { COLOR_PALETTE } from "../colors";
|
||||
import { jotaiScope } from "../jotai";
|
||||
import { exportToSvg } from "../../utils/export";
|
||||
import { exportToSvg } from "@excalidraw/utils";
|
||||
import type { LibraryItem } from "../types";
|
||||
|
||||
export type SvgCache = Map<LibraryItem["id"], SVGSVGElement>;
|
||||
|
||||
@@ -227,7 +227,7 @@ export {
|
||||
exportToBlob,
|
||||
exportToSvg,
|
||||
exportToClipboard,
|
||||
} from "../utils/export";
|
||||
} from "@excalidraw/utils";
|
||||
|
||||
export { serializeAsJSON, serializeLibraryAsJSON } from "./data/json";
|
||||
export {
|
||||
@@ -283,4 +283,4 @@ export {
|
||||
elementsOverlappingBBox,
|
||||
isElementInsideBBox,
|
||||
elementPartiallyOverlapsWithOrContainsBBox,
|
||||
} from "../utils/withinBounds";
|
||||
} from "@excalidraw/utils";
|
||||
|
||||
@@ -148,9 +148,7 @@
|
||||
"discordChat": "Discord chat",
|
||||
"zoomToFitViewport": "Zoom to fit in viewport",
|
||||
"zoomToFitSelection": "Zoom to fit selection",
|
||||
"zoomToFit": "Zoom to fit all elements",
|
||||
"installPWA": "Install Excalidraw locally (PWA)",
|
||||
"autoResize": "Enable text auto-resizing"
|
||||
"zoomToFit": "Zoom to fit all elements"
|
||||
},
|
||||
"library": {
|
||||
"noItems": "No items added yet...",
|
||||
|
||||
@@ -58,7 +58,8 @@
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.2",
|
||||
"@excalidraw/laser-pointer": "1.3.1",
|
||||
"@excalidraw/mermaid-to-excalidraw": "1.0.0",
|
||||
"@excalidraw/mermaid-to-excalidraw": "0.3.0",
|
||||
"@excalidraw/utils": "*",
|
||||
"@excalidraw/random-username": "1.1.0",
|
||||
"@radix-ui/react-popover": "1.0.3",
|
||||
"@radix-ui/react-tabs": "1.0.2",
|
||||
@@ -131,6 +132,7 @@
|
||||
"pack": "yarn build:umd && yarn pack",
|
||||
"start": "node ../../scripts/buildExample.mjs && vite",
|
||||
"build:example": "node ../../scripts/buildExample.mjs",
|
||||
"clear": "rm -rf dist",
|
||||
"size": "yarn build:umd && size-limit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { isTextElement } from "../element";
|
||||
import { isTextElement, refreshTextDimensions } from "../element";
|
||||
import { newElementWith } from "../element/mutateElement";
|
||||
import { getContainerElement } from "../element/textElement";
|
||||
import { isBoundToContainer } from "../element/typeChecks";
|
||||
import type {
|
||||
ExcalidrawElement,
|
||||
ExcalidrawTextElement,
|
||||
@@ -10,9 +12,17 @@ import { ShapeCache } from "./ShapeCache";
|
||||
|
||||
export class Fonts {
|
||||
private scene: Scene;
|
||||
private onSceneUpdated: () => void;
|
||||
|
||||
constructor({ scene }: { scene: Scene }) {
|
||||
constructor({
|
||||
scene,
|
||||
onSceneUpdated,
|
||||
}: {
|
||||
scene: Scene;
|
||||
onSceneUpdated: () => void;
|
||||
}) {
|
||||
this.scene = scene;
|
||||
this.onSceneUpdated = onSceneUpdated;
|
||||
}
|
||||
|
||||
// it's ok to track fonts across multiple instances only once, so let's use
|
||||
@@ -47,16 +57,22 @@ export class Fonts {
|
||||
let didUpdate = false;
|
||||
|
||||
this.scene.mapElements((element) => {
|
||||
if (isTextElement(element)) {
|
||||
didUpdate = true;
|
||||
if (isTextElement(element) && !isBoundToContainer(element)) {
|
||||
ShapeCache.delete(element);
|
||||
return newElementWith(element, {}, true);
|
||||
didUpdate = true;
|
||||
return newElementWith(element, {
|
||||
...refreshTextDimensions(
|
||||
element,
|
||||
getContainerElement(element, this.scene.getNonDeletedElementsMap()),
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
),
|
||||
});
|
||||
}
|
||||
return element;
|
||||
});
|
||||
|
||||
if (didUpdate) {
|
||||
this.scene.triggerUpdate();
|
||||
this.onSceneUpdated();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -107,8 +107,9 @@ export class Renderer {
|
||||
width,
|
||||
editingElement,
|
||||
pendingImageElementId,
|
||||
// cache-invalidation nonce
|
||||
sceneNonce: _sceneNonce,
|
||||
// unused but serves we cache on it to invalidate elements if they
|
||||
// get mutated
|
||||
versionNonce: _versionNonce,
|
||||
}: {
|
||||
zoom: AppState["zoom"];
|
||||
offsetLeft: AppState["offsetLeft"];
|
||||
@@ -119,7 +120,7 @@ export class Renderer {
|
||||
width: AppState["width"];
|
||||
editingElement: AppState["editingElement"];
|
||||
pendingImageElementId: AppState["pendingImageElementId"];
|
||||
sceneNonce: ReturnType<InstanceType<typeof Scene>["getSceneNonce"]>;
|
||||
versionNonce: ReturnType<InstanceType<typeof Scene>["getVersionNonce"]>;
|
||||
}) => {
|
||||
const elements = this.scene.getNonDeletedElements();
|
||||
|
||||
|
||||
@@ -138,17 +138,7 @@ class Scene {
|
||||
elements: null,
|
||||
cache: new Map(),
|
||||
};
|
||||
/**
|
||||
* Random integer regenerated each scene update.
|
||||
*
|
||||
* Does not relate to elements versions, it's only a renderer
|
||||
* cache-invalidation nonce at the moment.
|
||||
*/
|
||||
private sceneNonce: number | undefined;
|
||||
|
||||
getSceneNonce() {
|
||||
return this.sceneNonce;
|
||||
}
|
||||
private versionNonce: number | undefined;
|
||||
|
||||
getNonDeletedElementsMap() {
|
||||
return this.nonDeletedElementsMap;
|
||||
@@ -224,6 +214,10 @@ class Scene {
|
||||
return (this.elementsMap.get(id) as T | undefined) || null;
|
||||
}
|
||||
|
||||
getVersionNonce() {
|
||||
return this.versionNonce;
|
||||
}
|
||||
|
||||
getNonDeletedElement(
|
||||
id: ExcalidrawElement["id"],
|
||||
): NonDeleted<ExcalidrawElement> | null {
|
||||
@@ -292,18 +286,18 @@ class Scene {
|
||||
this.frames = nextFrameLikes;
|
||||
this.nonDeletedFramesLikes = getNonDeletedElements(this.frames).elements;
|
||||
|
||||
this.triggerUpdate();
|
||||
this.informMutation();
|
||||
}
|
||||
|
||||
triggerUpdate() {
|
||||
this.sceneNonce = randomInteger();
|
||||
informMutation() {
|
||||
this.versionNonce = randomInteger();
|
||||
|
||||
for (const callback of Array.from(this.callbacks)) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
onUpdate(cb: SceneStateCallback): SceneStateCallbackRemover {
|
||||
addCallback(cb: SceneStateCallback): SceneStateCallbackRemover {
|
||||
if (this.callbacks.has(cb)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ export const exportToSvg = async (
|
||||
assetPath =
|
||||
window.EXCALIDRAW_ASSET_PATH ||
|
||||
`https://unpkg.com/${import.meta.env.VITE_PKG_NAME}@${
|
||||
import.meta.env.VITE_PKG_VERSION
|
||||
import.meta.env.PKG_VERSION
|
||||
}`;
|
||||
|
||||
if (assetPath?.startsWith("/")) {
|
||||
|
||||
@@ -12,7 +12,6 @@ exports[`contextMenu element > right-clicking on a group should select whole gro
|
||||
"collaborators": Map {},
|
||||
"contextMenu": {
|
||||
"items": [
|
||||
"separator",
|
||||
{
|
||||
"icon": <svg
|
||||
aria-hidden="true"
|
||||
@@ -327,16 +326,6 @@ exports[`contextMenu element > right-clicking on a group should select whole gro
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"icon": null,
|
||||
"label": "labels.autoResize",
|
||||
"name": "autoResize",
|
||||
"perform": [Function],
|
||||
"predicate": [Function],
|
||||
"trackEvent": {
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "labels.unbindText",
|
||||
"name": "unbindText",
|
||||
@@ -398,11 +387,7 @@ exports[`contextMenu element > right-clicking on a group should select whole gro
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
@@ -497,11 +482,7 @@ exports[`contextMenu element > right-clicking on a group should select whole gro
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
@@ -4433,7 +4414,6 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
|
||||
"collaborators": Map {},
|
||||
"contextMenu": {
|
||||
"items": [
|
||||
"separator",
|
||||
{
|
||||
"icon": <svg
|
||||
aria-hidden="true"
|
||||
@@ -4748,16 +4728,6 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"icon": null,
|
||||
"label": "labels.autoResize",
|
||||
"name": "autoResize",
|
||||
"perform": [Function],
|
||||
"predicate": [Function],
|
||||
"trackEvent": {
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "labels.unbindText",
|
||||
"name": "unbindText",
|
||||
@@ -4819,11 +4789,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
@@ -4918,11 +4884,7 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
@@ -5552,7 +5514,6 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
|
||||
"collaborators": Map {},
|
||||
"contextMenu": {
|
||||
"items": [
|
||||
"separator",
|
||||
{
|
||||
"icon": <svg
|
||||
aria-hidden="true"
|
||||
@@ -5867,16 +5828,6 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"icon": null,
|
||||
"label": "labels.autoResize",
|
||||
"name": "autoResize",
|
||||
"perform": [Function],
|
||||
"predicate": [Function],
|
||||
"trackEvent": {
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "labels.unbindText",
|
||||
"name": "unbindText",
|
||||
@@ -5938,11 +5889,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
@@ -6037,11 +5984,7 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
@@ -7378,7 +7321,6 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap
|
||||
"collaborators": Map {},
|
||||
"contextMenu": {
|
||||
"items": [
|
||||
"separator",
|
||||
{
|
||||
"icon": <svg
|
||||
aria-hidden="true"
|
||||
@@ -7693,16 +7635,6 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"icon": null,
|
||||
"label": "labels.autoResize",
|
||||
"name": "autoResize",
|
||||
"perform": [Function],
|
||||
"predicate": [Function],
|
||||
"trackEvent": {
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "labels.unbindText",
|
||||
"name": "unbindText",
|
||||
@@ -7764,11 +7696,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
@@ -7863,11 +7791,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
@@ -8264,7 +8188,6 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap
|
||||
"collaborators": Map {},
|
||||
"contextMenu": {
|
||||
"items": [
|
||||
"separator",
|
||||
{
|
||||
"icon": <svg
|
||||
aria-hidden="true"
|
||||
@@ -8579,16 +8502,6 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"icon": null,
|
||||
"label": "labels.autoResize",
|
||||
"name": "autoResize",
|
||||
"perform": [Function],
|
||||
"predicate": [Function],
|
||||
"trackEvent": {
|
||||
"category": "element",
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "labels.unbindText",
|
||||
"name": "unbindText",
|
||||
@@ -8650,11 +8563,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
@@ -8749,11 +8658,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] ap
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
style={
|
||||
{
|
||||
"transform": "rotate(180deg)",
|
||||
}
|
||||
}
|
||||
transform="rotate(180)"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ exports[`Test Linear Elements > Test bound text element > should match styles fo
|
||||
class="excalidraw-wysiwyg"
|
||||
data-type="wysiwyg"
|
||||
dir="auto"
|
||||
style="position: absolute; display: inline-block; min-height: 1em; backface-visibility: hidden; margin: 0px; padding: 0px; border: 0px; outline: 0; resize: none; background: transparent; overflow: hidden; z-index: var(--zIndex-wysiwyg); word-break: break-word; white-space: pre-wrap; overflow-wrap: break-word; box-sizing: content-box; width: 10.5px; height: 25px; left: 35px; top: 7.5px; transform: translate(0px, 0px) scale(1) rotate(0deg); text-align: center; vertical-align: middle; color: rgb(30, 30, 30); opacity: 1; filter: var(--theme-filter); max-height: 992.5px; font: Emoji 20px 20px; line-height: 1.25; font-family: Virgil, Segoe UI Emoji;"
|
||||
style="position: absolute; display: inline-block; min-height: 1em; backface-visibility: hidden; margin: 0px; padding: 0px; border: 0px; outline: 0; resize: none; background: transparent; overflow: hidden; z-index: var(--zIndex-wysiwyg); word-break: break-word; white-space: pre-wrap; overflow-wrap: break-word; box-sizing: content-box; width: 10px; height: 25px; left: 35px; top: 7.5px; transform: translate(0px, 0px) scale(1) rotate(0deg); text-align: center; vertical-align: middle; color: rgb(30, 30, 30); opacity: 1; filter: var(--theme-filter); max-height: 992.5px; font: Emoji 20px 20px; line-height: 1.25; font-family: Virgil, Segoe UI Emoji;"
|
||||
tabindex="0"
|
||||
wrap="off"
|
||||
/>
|
||||
|
||||
@@ -10752,7 +10752,7 @@ exports[`regression tests > pinch-to-zoom works > [end of test] appState 1`] = `
|
||||
"pendingImageElementId": null,
|
||||
"previousSelectedElementIds": {},
|
||||
"resizingElement": null,
|
||||
"scrollX": -6.2500000000000036,
|
||||
"scrollX": -2.916666666666668,
|
||||
"scrollY": 0,
|
||||
"scrolledOutside": false,
|
||||
"selectedElementIds": {},
|
||||
@@ -13688,8 +13688,8 @@ exports[`regression tests > two-finger scroll works > [end of test] appState 1`]
|
||||
"pendingImageElementId": null,
|
||||
"previousSelectedElementIds": {},
|
||||
"resizingElement": null,
|
||||
"scrollX": 20,
|
||||
"scrollY": -18.535533905932738,
|
||||
"scrollX": 10,
|
||||
"scrollY": -10,
|
||||
"scrolledOutside": false,
|
||||
"selectedElementIds": {},
|
||||
"selectedElementsAreBeingDragged": false,
|
||||
|
||||
@@ -302,7 +302,6 @@ exports[`restoreElements > should restore line and draw elements correctly 2`] =
|
||||
exports[`restoreElements > should restore text element correctly passing value for each attribute 1`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": [],
|
||||
"containerId": null,
|
||||
@@ -345,7 +344,6 @@ exports[`restoreElements > should restore text element correctly passing value f
|
||||
exports[`restoreElements > should restore text element correctly with unknown font family, null text and undefined alignment 1`] = `
|
||||
{
|
||||
"angle": 0,
|
||||
"autoResize": true,
|
||||
"backgroundColor": "transparent",
|
||||
"boundElements": [],
|
||||
"containerId": null,
|
||||
|
||||
@@ -10,9 +10,8 @@ import { Excalidraw } from "../index";
|
||||
import { Keyboard, Pointer, UI } from "./helpers/ui";
|
||||
import { API } from "./helpers/api";
|
||||
import { getDefaultAppState } from "../appState";
|
||||
import { fireEvent, queryByTestId, waitFor } from "@testing-library/react";
|
||||
import { fireEvent, waitFor } from "@testing-library/react";
|
||||
import { createUndoAction, createRedoAction } from "../actions/actionHistory";
|
||||
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
|
||||
import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants";
|
||||
import type { AppState, ExcalidrawImperativeAPI } from "../types";
|
||||
import { arrayToMap, resolvablePromise } from "../utils";
|
||||
@@ -50,6 +49,7 @@ const checkpoint = (name: string) => {
|
||||
expect(renderStaticScene.mock.calls.length).toMatchSnapshot(
|
||||
`[${name}] number of renders`,
|
||||
);
|
||||
|
||||
// `scrolledOutside` does not appear to be stable between test runs
|
||||
// `selectedLinearElemnt` includes `startBindingElement` containing seed and versionNonce
|
||||
const {
|
||||
@@ -1688,129 +1688,6 @@ describe("history", () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it("should disable undo/redo buttons when stacks empty", async () => {
|
||||
const { container } = await render(
|
||||
<Excalidraw
|
||||
initialData={{
|
||||
elements: [API.createElement({ type: "rectangle", id: "A" })],
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const undoAction = createUndoAction(h.history, h.store);
|
||||
const redoAction = createRedoAction(h.history, h.store);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(h.elements).toEqual([expect.objectContaining({ id: "A" })]);
|
||||
expect(h.history.isUndoStackEmpty).toBeTruthy();
|
||||
expect(h.history.isRedoStackEmpty).toBeTruthy();
|
||||
});
|
||||
|
||||
const undoButton = queryByTestId(container, "button-undo");
|
||||
const redoButton = queryByTestId(container, "button-redo");
|
||||
|
||||
expect(undoButton).toBeDisabled();
|
||||
expect(redoButton).toBeDisabled();
|
||||
|
||||
const rectangle = UI.createElement("rectangle");
|
||||
expect(h.elements).toEqual([
|
||||
expect.objectContaining({ id: "A" }),
|
||||
expect.objectContaining({ id: rectangle.id }),
|
||||
]);
|
||||
|
||||
expect(h.history.isUndoStackEmpty).toBeFalsy();
|
||||
expect(h.history.isRedoStackEmpty).toBeTruthy();
|
||||
expect(undoButton).not.toBeDisabled();
|
||||
expect(redoButton).toBeDisabled();
|
||||
|
||||
act(() => h.app.actionManager.executeAction(undoAction));
|
||||
|
||||
expect(h.history.isUndoStackEmpty).toBeTruthy();
|
||||
expect(h.history.isRedoStackEmpty).toBeFalsy();
|
||||
expect(undoButton).toBeDisabled();
|
||||
expect(redoButton).not.toBeDisabled();
|
||||
|
||||
act(() => h.app.actionManager.executeAction(redoAction));
|
||||
|
||||
expect(h.history.isUndoStackEmpty).toBeFalsy();
|
||||
expect(h.history.isRedoStackEmpty).toBeTruthy();
|
||||
expect(undoButton).not.toBeDisabled();
|
||||
expect(redoButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("remounting undo/redo buttons should initialize undo/redo state correctly", async () => {
|
||||
const { container } = await render(
|
||||
<Excalidraw
|
||||
initialData={{
|
||||
elements: [API.createElement({ type: "rectangle", id: "A" })],
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const undoAction = createUndoAction(h.history, h.store);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(h.elements).toEqual([expect.objectContaining({ id: "A" })]);
|
||||
expect(h.history.isUndoStackEmpty).toBeTruthy();
|
||||
expect(h.history.isRedoStackEmpty).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(queryByTestId(container, "button-undo")).toBeDisabled();
|
||||
expect(queryByTestId(container, "button-redo")).toBeDisabled();
|
||||
|
||||
// testing undo button
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
const rectangle = UI.createElement("rectangle");
|
||||
expect(h.elements).toEqual([
|
||||
expect.objectContaining({ id: "A" }),
|
||||
expect.objectContaining({ id: rectangle.id }),
|
||||
]);
|
||||
|
||||
expect(h.history.isUndoStackEmpty).toBeFalsy();
|
||||
expect(h.history.isRedoStackEmpty).toBeTruthy();
|
||||
expect(queryByTestId(container, "button-undo")).not.toBeDisabled();
|
||||
expect(queryByTestId(container, "button-redo")).toBeDisabled();
|
||||
|
||||
act(() => h.app.actionManager.executeAction(actionToggleViewMode));
|
||||
expect(h.state.viewModeEnabled).toBe(true);
|
||||
|
||||
expect(queryByTestId(container, "button-undo")).toBeNull();
|
||||
expect(queryByTestId(container, "button-redo")).toBeNull();
|
||||
|
||||
act(() => h.app.actionManager.executeAction(actionToggleViewMode));
|
||||
expect(h.state.viewModeEnabled).toBe(false);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(queryByTestId(container, "button-undo")).not.toBeDisabled();
|
||||
expect(queryByTestId(container, "button-redo")).toBeDisabled();
|
||||
});
|
||||
|
||||
// testing redo button
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
act(() => h.app.actionManager.executeAction(undoAction));
|
||||
|
||||
expect(h.history.isUndoStackEmpty).toBeTruthy();
|
||||
expect(h.history.isRedoStackEmpty).toBeFalsy();
|
||||
expect(queryByTestId(container, "button-undo")).toBeDisabled();
|
||||
expect(queryByTestId(container, "button-redo")).not.toBeDisabled();
|
||||
|
||||
act(() => h.app.actionManager.executeAction(actionToggleViewMode));
|
||||
expect(h.state.viewModeEnabled).toBe(true);
|
||||
|
||||
expect(queryByTestId(container, "button-undo")).toBeNull();
|
||||
expect(queryByTestId(container, "button-redo")).toBeNull();
|
||||
|
||||
act(() => h.app.actionManager.executeAction(actionToggleViewMode));
|
||||
expect(h.state.viewModeEnabled).toBe(false);
|
||||
|
||||
expect(h.history.isUndoStackEmpty).toBeTruthy();
|
||||
expect(h.history.isRedoStackEmpty).toBeFalsy();
|
||||
expect(queryByTestId(container, "button-undo")).toBeDisabled();
|
||||
expect(queryByTestId(container, "button-redo")).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("multiplayer undo/redo", () => {
|
||||
|
||||
@@ -972,10 +972,10 @@ describe("Test Linear Elements", () => {
|
||||
]);
|
||||
expect((h.elements[1] as ExcalidrawTextElementWithContainer).text)
|
||||
.toMatchInlineSnapshot(`
|
||||
"Online whiteboard
|
||||
collaboration made
|
||||
easy"
|
||||
`);
|
||||
"Online whiteboard
|
||||
collaboration made
|
||||
easy"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should bind text to arrow when clicked on arrow and enter pressed", async () => {
|
||||
@@ -1006,10 +1006,10 @@ describe("Test Linear Elements", () => {
|
||||
]);
|
||||
expect((h.elements[1] as ExcalidrawTextElementWithContainer).text)
|
||||
.toMatchInlineSnapshot(`
|
||||
"Online whiteboard
|
||||
collaboration made
|
||||
easy"
|
||||
`);
|
||||
"Online whiteboard
|
||||
collaboration made
|
||||
easy"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should not bind text to line when double clicked", async () => {
|
||||
|
||||
@@ -426,112 +426,6 @@ describe("text element", () => {
|
||||
expect(text.fontSize).toBe(fontSize);
|
||||
});
|
||||
});
|
||||
|
||||
// text can be resized from sides
|
||||
it("can be resized from e", async () => {
|
||||
const text = UI.createElement("text");
|
||||
await UI.editText(text, "Excalidraw\nEditor");
|
||||
|
||||
const width = text.width;
|
||||
const height = text.height;
|
||||
|
||||
UI.resize(text, "e", [30, 0]);
|
||||
expect(text.width).toBe(width + 30);
|
||||
expect(text.height).toBe(height);
|
||||
|
||||
UI.resize(text, "e", [-30, 0]);
|
||||
expect(text.width).toBe(width);
|
||||
expect(text.height).toBe(height);
|
||||
});
|
||||
|
||||
it("can be resized from w", async () => {
|
||||
const text = UI.createElement("text");
|
||||
await UI.editText(text, "Excalidraw\nEditor");
|
||||
|
||||
const width = text.width;
|
||||
const height = text.height;
|
||||
|
||||
UI.resize(text, "w", [-50, 0]);
|
||||
expect(text.width).toBe(width + 50);
|
||||
expect(text.height).toBe(height);
|
||||
|
||||
UI.resize(text, "w", [50, 0]);
|
||||
expect(text.width).toBe(width);
|
||||
expect(text.height).toBe(height);
|
||||
});
|
||||
|
||||
it("wraps when width is narrower than texts inside", async () => {
|
||||
const text = UI.createElement("text");
|
||||
await UI.editText(text, "Excalidraw\nEditor");
|
||||
|
||||
const prevWidth = text.width;
|
||||
const prevHeight = text.height;
|
||||
const prevText = text.text;
|
||||
|
||||
UI.resize(text, "w", [50, 0]);
|
||||
expect(text.width).toBe(prevWidth - 50);
|
||||
expect(text.height).toBeGreaterThan(prevHeight);
|
||||
expect(text.text).not.toEqual(prevText);
|
||||
expect(text.autoResize).toBe(false);
|
||||
|
||||
UI.resize(text, "w", [-50, 0]);
|
||||
expect(text.width).toBe(prevWidth);
|
||||
expect(text.height).toEqual(prevHeight);
|
||||
expect(text.text).toEqual(prevText);
|
||||
expect(text.autoResize).toBe(false);
|
||||
|
||||
UI.resize(text, "e", [-20, 0]);
|
||||
expect(text.width).toBe(prevWidth - 20);
|
||||
expect(text.height).toBeGreaterThan(prevHeight);
|
||||
expect(text.text).not.toEqual(prevText);
|
||||
expect(text.autoResize).toBe(false);
|
||||
|
||||
UI.resize(text, "e", [20, 0]);
|
||||
expect(text.width).toBe(prevWidth);
|
||||
expect(text.height).toEqual(prevHeight);
|
||||
expect(text.text).toEqual(prevText);
|
||||
expect(text.autoResize).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps properties when wrapped", async () => {
|
||||
const text = UI.createElement("text");
|
||||
await UI.editText(text, "Excalidraw\nEditor");
|
||||
|
||||
const alignment = text.textAlign;
|
||||
const fontSize = text.fontSize;
|
||||
const fontFamily = text.fontFamily;
|
||||
|
||||
UI.resize(text, "e", [-60, 0]);
|
||||
expect(text.textAlign).toBe(alignment);
|
||||
expect(text.fontSize).toBe(fontSize);
|
||||
expect(text.fontFamily).toBe(fontFamily);
|
||||
expect(text.autoResize).toBe(false);
|
||||
|
||||
UI.resize(text, "e", [60, 0]);
|
||||
expect(text.textAlign).toBe(alignment);
|
||||
expect(text.fontSize).toBe(fontSize);
|
||||
expect(text.fontFamily).toBe(fontFamily);
|
||||
expect(text.autoResize).toBe(false);
|
||||
});
|
||||
|
||||
it("has a minimum width when wrapped", async () => {
|
||||
const text = UI.createElement("text");
|
||||
await UI.editText(text, "Excalidraw\nEditor");
|
||||
|
||||
const width = text.width;
|
||||
|
||||
UI.resize(text, "e", [-width, 0]);
|
||||
expect(text.width).not.toEqual(0);
|
||||
UI.resize(text, "e", [width - text.width, 0]);
|
||||
expect(text.width).toEqual(width);
|
||||
expect(text.autoResize).toBe(false);
|
||||
|
||||
UI.resize(text, "w", [width, 0]);
|
||||
expect(text.width).not.toEqual(0);
|
||||
UI.resize(text, "w", [text.width - width, 0]);
|
||||
expect(text.width).toEqual(width);
|
||||
expect(text.autoResize).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("image element", () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
rectangleWithLinkFixture,
|
||||
} from "../fixtures/elementFixture";
|
||||
import { API } from "../helpers/api";
|
||||
import { exportToCanvas, exportToSvg } from "../../../utils";
|
||||
import { exportToCanvas, exportToSvg } from "../../../utils/src/export";
|
||||
import { FRAME_STYLE } from "../../constants";
|
||||
import { prepareElementsForExport } from "../../data";
|
||||
|
||||
@@ -250,7 +250,6 @@ describe("exporting frames", () => {
|
||||
exportPadding: 0,
|
||||
exportingFrame: frame,
|
||||
});
|
||||
|
||||
// frame itself isn't exported
|
||||
expect(svg.querySelector(`[data-id="${frame.id}"]`)).toBeNull();
|
||||
// frame child is exported
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"exclude": ["**/*.test.*", "tests", "types", "examples", "dist"],
|
||||
"extends": "../../tsconfig",
|
||||
"exclude": ["**/*.test.*", "tests", "types", "examples", "packages/**/dist/**", "dist"],
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"strict": true,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export * from "./export";
|
||||
export * from "./withinBounds";
|
||||
export * from "./bbox";
|
||||
export * from "./src/export";
|
||||
export * from "./src/withinBounds";
|
||||
export * from "./src/bbox";
|
||||
export * from "./src/collision";
|
||||
export * from "./src/geometry/shape";
|
||||
export * from "./src/geometry/geometry";
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"babel-loader": "8.2.5",
|
||||
"babel-plugin-transform-class-properties": "6.24.1",
|
||||
"chokidar": "3.6.0",
|
||||
"cross-env": "7.0.3",
|
||||
"css-loader": "6.7.1",
|
||||
"file-loader": "6.2.0",
|
||||
@@ -79,8 +80,10 @@
|
||||
"scripts": {
|
||||
"gen:types": "rm -rf types && tsc",
|
||||
"build:umd": "cross-env NODE_ENV=production webpack --config webpack.prod.config.js",
|
||||
"build:src": "rm -rf dist && node ../../scripts/buildUtils.js",
|
||||
"build:esm": "rm -rf dist && node ../../scripts/buildUtils.js && yarn gen:types",
|
||||
"build:umd:withAnalyzer": "cross-env NODE_ENV=production ANALYZER=true webpack --config webpack.prod.config.js",
|
||||
"clear": "rm -rf dist",
|
||||
"pack": "yarn build:umd && yarn pack"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Bounds } from "../excalidraw/element/bounds";
|
||||
import type { Point } from "../excalidraw/types";
|
||||
import type { Bounds } from "../../excalidraw/element/bounds";
|
||||
import type { Point } from "../../excalidraw/types";
|
||||
|
||||
export type LineSegment = [Point, Point];
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import {
|
||||
exportToCanvas as _exportToCanvas,
|
||||
exportToSvg as _exportToSvg,
|
||||
} from "../excalidraw/scene/export";
|
||||
import { getDefaultAppState } from "../excalidraw/appState";
|
||||
import type { AppState, BinaryFiles } from "../excalidraw/types";
|
||||
} from "../../excalidraw/scene/export";
|
||||
import { getDefaultAppState } from "../../excalidraw/appState";
|
||||
import type { AppState, BinaryFiles } from "../../excalidraw/types";
|
||||
import type {
|
||||
ExcalidrawElement,
|
||||
ExcalidrawFrameLikeElement,
|
||||
NonDeleted,
|
||||
} from "../excalidraw/element/types";
|
||||
import { restore } from "../excalidraw/data/restore";
|
||||
import { MIME_TYPES } from "../excalidraw/constants";
|
||||
import { encodePngMetadata } from "../excalidraw/data/image";
|
||||
import { serializeAsJSON } from "../excalidraw/data/json";
|
||||
} from "../../excalidraw/element/types";
|
||||
import { restore } from "../../excalidraw/data/restore";
|
||||
import { MIME_TYPES } from "../../excalidraw/constants";
|
||||
import { encodePngMetadata } from "../../excalidraw/data/image";
|
||||
import { serializeAsJSON } from "../../excalidraw/data/json";
|
||||
import {
|
||||
copyBlobToClipboardAsPng,
|
||||
copyTextToSystemClipboard,
|
||||
copyToClipboard,
|
||||
} from "../excalidraw/clipboard";
|
||||
} from "../../excalidraw/clipboard";
|
||||
|
||||
export { MIME_TYPES };
|
||||
|
||||
@@ -170,6 +170,8 @@ export const exportToSvg = async ({
|
||||
exportPadding?: number;
|
||||
renderEmbeddables?: boolean;
|
||||
}): Promise<SVGSVGElement> => {
|
||||
console.info("Watching exportToSVG :)");
|
||||
|
||||
const { elements: restoredElements, appState: restoredAppState } = restore(
|
||||
{ elements, appState },
|
||||
null,
|
||||
@@ -1,4 +1,4 @@
|
||||
import { distance2d } from "../../excalidraw/math";
|
||||
import { distance2d } from "../../../excalidraw/math";
|
||||
import type {
|
||||
Point,
|
||||
Line,
|
||||
@@ -12,7 +12,7 @@
|
||||
* to pure shapes
|
||||
*/
|
||||
|
||||
import { getElementAbsoluteCoords } from "../../excalidraw/element";
|
||||
import { getElementAbsoluteCoords } from "../../../excalidraw/element";
|
||||
import type {
|
||||
ElementsMap,
|
||||
ExcalidrawDiamondElement,
|
||||
@@ -27,7 +27,7 @@ import type {
|
||||
ExcalidrawRectangleElement,
|
||||
ExcalidrawSelectionElement,
|
||||
ExcalidrawTextElement,
|
||||
} from "../../excalidraw/element/types";
|
||||
} from "../../../excalidraw/element/types";
|
||||
import { angleToDegrees, close, pointAdd, pointRotate } from "./geometry";
|
||||
import { pointsOnBezierCurves } from "points-on-curve";
|
||||
import type { Drawable, Op } from "roughjs/bin/core";
|
||||
@@ -3,19 +3,19 @@ import type {
|
||||
ExcalidrawFreeDrawElement,
|
||||
ExcalidrawLinearElement,
|
||||
NonDeletedExcalidrawElement,
|
||||
} from "../excalidraw/element/types";
|
||||
} from "../../excalidraw/element/types";
|
||||
import {
|
||||
isArrowElement,
|
||||
isExcalidrawElement,
|
||||
isFreeDrawElement,
|
||||
isLinearElement,
|
||||
isTextElement,
|
||||
} from "../excalidraw/element/typeChecks";
|
||||
import { isValueInRange, rotatePoint } from "../excalidraw/math";
|
||||
import type { Point } from "../excalidraw/types";
|
||||
import type { Bounds } from "../excalidraw/element/bounds";
|
||||
import { getElementBounds } from "../excalidraw/element/bounds";
|
||||
import { arrayToMap } from "../excalidraw/utils";
|
||||
} from "../../excalidraw/element/typeChecks";
|
||||
import { isValueInRange, rotatePoint } from "../../excalidraw/math";
|
||||
import type { Point } from "../../excalidraw/types";
|
||||
import type { Bounds } from "../../excalidraw/element/bounds";
|
||||
import { getElementBounds } from "../../excalidraw/element/bounds";
|
||||
import { arrayToMap } from "../../excalidraw/utils";
|
||||
|
||||
type Element = NonDeletedExcalidrawElement;
|
||||
type Elements = readonly NonDeletedExcalidrawElement[];
|
||||
@@ -0,0 +1,102 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`exportToSvg > with default arguments 1`] = `
|
||||
{
|
||||
"activeEmbeddable": null,
|
||||
"activeTool": {
|
||||
"customType": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
"collaborators": Map {},
|
||||
"contextMenu": null,
|
||||
"currentChartType": "bar",
|
||||
"currentItemBackgroundColor": "transparent",
|
||||
"currentItemEndArrowhead": "arrow",
|
||||
"currentItemFillStyle": "solid",
|
||||
"currentItemFontFamily": 1,
|
||||
"currentItemFontSize": 20,
|
||||
"currentItemOpacity": 100,
|
||||
"currentItemRoughness": 1,
|
||||
"currentItemRoundness": "round",
|
||||
"currentItemStartArrowhead": null,
|
||||
"currentItemStrokeColor": "#1e1e1e",
|
||||
"currentItemStrokeStyle": "solid",
|
||||
"currentItemStrokeWidth": 2,
|
||||
"currentItemTextAlign": "left",
|
||||
"cursorButton": "up",
|
||||
"defaultSidebarDockedPreference": false,
|
||||
"draggingElement": null,
|
||||
"editingElement": null,
|
||||
"editingFrame": null,
|
||||
"editingGroupId": null,
|
||||
"editingLinearElement": null,
|
||||
"elementsToHighlight": null,
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"exportEmbedScene": false,
|
||||
"exportPadding": undefined,
|
||||
"exportScale": 1,
|
||||
"exportWithDarkMode": false,
|
||||
"fileHandle": null,
|
||||
"followedBy": Set {},
|
||||
"frameRendering": {
|
||||
"clip": true,
|
||||
"enabled": true,
|
||||
"name": true,
|
||||
"outline": true,
|
||||
},
|
||||
"frameToHighlight": null,
|
||||
"gridSize": null,
|
||||
"isBindingEnabled": true,
|
||||
"isLoading": false,
|
||||
"isResizing": false,
|
||||
"isRotating": false,
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "name",
|
||||
"objectsSnapModeEnabled": false,
|
||||
"openDialog": null,
|
||||
"openMenu": null,
|
||||
"openPopup": null,
|
||||
"openSidebar": null,
|
||||
"originSnapOffset": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"pasteDialog": {
|
||||
"data": null,
|
||||
"shown": false,
|
||||
},
|
||||
"penDetected": false,
|
||||
"penMode": false,
|
||||
"pendingImageElementId": null,
|
||||
"previousSelectedElementIds": {},
|
||||
"resizingElement": null,
|
||||
"scrollX": 0,
|
||||
"scrollY": 0,
|
||||
"scrolledOutside": false,
|
||||
"selectedElementIds": {},
|
||||
"selectedElementsAreBeingDragged": false,
|
||||
"selectedGroupIds": {},
|
||||
"selectedLinearElement": null,
|
||||
"selectionElement": null,
|
||||
"shouldCacheIgnoreZoom": false,
|
||||
"showHyperlinkPopup": false,
|
||||
"showStats": false,
|
||||
"showWelcomeScreen": false,
|
||||
"snapLines": [],
|
||||
"startBoundElement": null,
|
||||
"suggestedBindings": [],
|
||||
"theme": "light",
|
||||
"toast": null,
|
||||
"userToFollow": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": {
|
||||
"value": 1,
|
||||
},
|
||||
}
|
||||
`;
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as utils from ".";
|
||||
import { diagramFactory } from "../excalidraw/tests/fixtures/diagramFixture";
|
||||
import * as utils from "../src/export";
|
||||
import { diagramFactory } from "../../excalidraw/tests/fixtures/diagramFixture";
|
||||
import { vi } from "vitest";
|
||||
import * as mockedSceneExportUtils from "../excalidraw/scene/export";
|
||||
import * as mockedSceneExportUtils from "../../excalidraw/scene/export";
|
||||
|
||||
import { MIME_TYPES } from "../excalidraw/constants";
|
||||
import { MIME_TYPES } from "../../excalidraw/constants";
|
||||
|
||||
const exportToSvgSpy = vi.spyOn(mockedSceneExportUtils, "exportToSvg");
|
||||
|
||||
@@ -11,8 +11,15 @@ import {
|
||||
pointOnPolyline,
|
||||
pointRightofLine,
|
||||
pointRotate,
|
||||
} from "./geometry";
|
||||
import type { Curve, Ellipse, Line, Point, Polygon, Polyline } from "./shape";
|
||||
} from "../src/geometry/geometry";
|
||||
import type {
|
||||
Curve,
|
||||
Ellipse,
|
||||
Line,
|
||||
Point,
|
||||
Polygon,
|
||||
Polyline,
|
||||
} from "../src/geometry/shape";
|
||||
|
||||
describe("point and line", () => {
|
||||
const line: Line = [
|
||||
@@ -1,7 +1,10 @@
|
||||
import { decodePngMetadata, decodeSvgMetadata } from "../excalidraw/data/image";
|
||||
import type { ImportedDataState } from "../excalidraw/data/types";
|
||||
import * as utils from "../utils";
|
||||
import { API } from "../excalidraw/tests/helpers/api";
|
||||
import {
|
||||
decodePngMetadata,
|
||||
decodeSvgMetadata,
|
||||
} from "../../excalidraw/data/image";
|
||||
import type { ImportedDataState } from "../../excalidraw/data/types";
|
||||
import * as utils from "../index";
|
||||
import { API } from "../../excalidraw/tests/helpers/api";
|
||||
|
||||
// NOTE this test file is using the actual API, unmocked. Hence splitting it
|
||||
// from the other test file, because I couldn't figure out how to test
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Bounds } from "../excalidraw/element/bounds";
|
||||
import { API } from "../excalidraw/tests/helpers/api";
|
||||
import type { Bounds } from "../../excalidraw/element/bounds";
|
||||
import { API } from "../../excalidraw/tests/helpers/api";
|
||||
import {
|
||||
elementPartiallyOverlapsWithOrContainsBBox,
|
||||
elementsOverlappingBBox,
|
||||
isElementInsideBBox,
|
||||
} from "./withinBounds";
|
||||
} from "../src/withinBounds";
|
||||
|
||||
const makeElement = (x: number, y: number, width: number, height: number) =>
|
||||
API.createElement({
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"exclude": ["**/*.test.*", "**/tests/*", "types", "examples", "packages/**/dist/**", "dist"],
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"strict": true,
|
||||
"outDir": "dist",
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"exclude": ["**/*.test.*", "**/tests/*", "types", "dist"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { execSync } from "child_process";
|
||||
|
||||
const createDevBuild = async () => {
|
||||
return await esbuild.build({
|
||||
entryPoints: ["../../examples/excalidraw/with-script-in-browser/index.tsx"],
|
||||
entryPoints: ["../../examples/excalidraw/with-script-in-browser/index.tsx"],
|
||||
outfile:
|
||||
"../../examples/excalidraw/with-script-in-browser/public/bundle.js",
|
||||
define: {
|
||||
@@ -13,6 +13,7 @@ const createDevBuild = async () => {
|
||||
bundle: true,
|
||||
format: "esm",
|
||||
plugins: [sassPlugin()],
|
||||
external: ["@excalidraw/utils"],
|
||||
loader: {
|
||||
".woff2": "dataurl",
|
||||
".html": "copy",
|
||||
|
||||
@@ -53,6 +53,7 @@ const browserConfig = {
|
||||
".woff2": "copy",
|
||||
".ttf": "copy",
|
||||
},
|
||||
external: ["@excalidraw/utils"],
|
||||
};
|
||||
const createESMBrowserBuild = async () => {
|
||||
// Development unminified build with source maps
|
||||
@@ -107,6 +108,7 @@ const rawConfig = {
|
||||
".json": "copy",
|
||||
},
|
||||
packages: "external",
|
||||
external: ["@excalidraw/utils"],
|
||||
};
|
||||
|
||||
const createESMRawBuild = async () => {
|
||||
|
||||
@@ -8,6 +8,7 @@ const browserConfig = {
|
||||
bundle: true,
|
||||
format: "esm",
|
||||
plugins: [sassPlugin()],
|
||||
external: ["@excalidraw/utils"],
|
||||
};
|
||||
|
||||
// Will be used later for treeshaking
|
||||
@@ -80,6 +81,7 @@ const rawConfig = {
|
||||
format: "esm",
|
||||
packages: "external",
|
||||
plugins: [sassPlugin()],
|
||||
external: ["@excalidraw/utils"],
|
||||
};
|
||||
|
||||
// const BASE_PATH = `${path.resolve(`${__dirname}/..`)}`;
|
||||
@@ -119,5 +121,7 @@ const createESMRawBuild = async () => {
|
||||
fs.writeFileSync("meta-raw-prod.json", JSON.stringify(rawProd.metafile));
|
||||
};
|
||||
|
||||
console.info("BUILDING UTILS STARTED");
|
||||
createESMRawBuild();
|
||||
createESMBrowserBuild();
|
||||
console.info("BUILDING UTILS COMPLETE");
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
const chokidar = require("chokidar");
|
||||
const path = require("path");
|
||||
const { execSync } = require("child_process");
|
||||
|
||||
const BASE_PATH = `${path.resolve(`${__dirname}/..`)}`;
|
||||
const utilsDir = `${BASE_PATH}/packages/utils/src`;
|
||||
// One-liner for current directory
|
||||
chokidar.watch(utilsDir).on("change", (event) => {
|
||||
console.info("Watching", event);
|
||||
try {
|
||||
execSync(`yarn workspace @excalidraw/utils run build:src`);
|
||||
} catch (err) {
|
||||
console.error("Error when building workspace", err);
|
||||
}
|
||||
console.info("BUILD DONE");
|
||||
});
|
||||
+1
-1
@@ -17,5 +17,5 @@
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["packages", "excalidraw-app"],
|
||||
"exclude": ["packages/excalidraw/types", "examples"]
|
||||
"exclude": ["packages/excalidraw/types", "examples", "packages/**/tests/**", "packages/**/dist/**", "excalidraw-app/tests"]
|
||||
}
|
||||
|
||||
@@ -234,6 +234,11 @@
|
||||
resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a"
|
||||
integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==
|
||||
|
||||
"@babel/helper-plugin-utils@^7.24.5":
|
||||
version "7.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz#a924607dd254a65695e5bd209b98b902b3b2f11a"
|
||||
integrity sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==
|
||||
|
||||
"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.20":
|
||||
version "7.22.20"
|
||||
resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0"
|
||||
@@ -273,7 +278,14 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.22.5"
|
||||
|
||||
"@babel/helper-string-parser@^7.23.4":
|
||||
"@babel/helper-split-export-declaration@^7.24.5":
|
||||
version "7.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz#b9a67f06a46b0b339323617c8c6213b9055a78b6"
|
||||
integrity sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==
|
||||
dependencies:
|
||||
"@babel/types" "^7.24.5"
|
||||
|
||||
"@babel/helper-string-parser@^7.23.4", "@babel/helper-string-parser@^7.24.1":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e"
|
||||
integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==
|
||||
@@ -283,6 +295,11 @@
|
||||
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
|
||||
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.24.5":
|
||||
version "7.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62"
|
||||
integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==
|
||||
|
||||
"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.23.5":
|
||||
version "7.23.5"
|
||||
resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
|
||||
@@ -700,13 +717,20 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.0"
|
||||
|
||||
"@babel/plugin-transform-block-scoping@^7.18.6", "@babel/plugin-transform-block-scoping@^7.18.9", "@babel/plugin-transform-block-scoping@^7.24.4":
|
||||
"@babel/plugin-transform-block-scoping@^7.18.6", "@babel/plugin-transform-block-scoping@^7.24.4":
|
||||
version "7.24.4"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz#28f5c010b66fbb8ccdeef853bef1935c434d7012"
|
||||
integrity sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.0"
|
||||
|
||||
"@babel/plugin-transform-block-scoping@^7.18.9":
|
||||
version "7.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.5.tgz#89574191397f85661d6f748d4b89ee4d9ee69a2a"
|
||||
integrity sha512-sMfBc3OxghjC95BkYrYocHL3NaOplrcaunblzwXhGmlPwpmfsxr4vK+mBBt49r+S240vahmv+kUxkeKgs+haCw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.5"
|
||||
|
||||
"@babel/plugin-transform-class-properties@^7.24.1":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz#bcbf1aef6ba6085cfddec9fc8d58871cf011fc29"
|
||||
@@ -724,7 +748,7 @@
|
||||
"@babel/helper-plugin-utils" "^7.24.0"
|
||||
"@babel/plugin-syntax-class-static-block" "^7.14.5"
|
||||
|
||||
"@babel/plugin-transform-classes@^7.18.6", "@babel/plugin-transform-classes@^7.18.9", "@babel/plugin-transform-classes@^7.24.1":
|
||||
"@babel/plugin-transform-classes@^7.18.6", "@babel/plugin-transform-classes@^7.24.1":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz#5bc8fc160ed96378184bc10042af47f50884dcb1"
|
||||
integrity sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==
|
||||
@@ -738,6 +762,20 @@
|
||||
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/plugin-transform-classes@^7.18.9":
|
||||
version "7.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.5.tgz#05e04a09df49a46348299a0e24bfd7e901129339"
|
||||
integrity sha512-gWkLP25DFj2dwe9Ck8uwMOpko4YsqyfZJrOmqqcegeDYEbp7rmn4U6UQZNj08UF6MaX39XenSpKRCvpDRBtZ7Q==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.22.5"
|
||||
"@babel/helper-compilation-targets" "^7.23.6"
|
||||
"@babel/helper-environment-visitor" "^7.22.20"
|
||||
"@babel/helper-function-name" "^7.23.0"
|
||||
"@babel/helper-plugin-utils" "^7.24.5"
|
||||
"@babel/helper-replace-supers" "^7.24.1"
|
||||
"@babel/helper-split-export-declaration" "^7.24.5"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/plugin-transform-computed-properties@^7.18.6", "@babel/plugin-transform-computed-properties@^7.18.9", "@babel/plugin-transform-computed-properties@^7.24.1":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz#bc7e787f8e021eccfb677af5f13c29a9934ed8a7"
|
||||
@@ -746,13 +784,20 @@
|
||||
"@babel/helper-plugin-utils" "^7.24.0"
|
||||
"@babel/template" "^7.24.0"
|
||||
|
||||
"@babel/plugin-transform-destructuring@^7.18.6", "@babel/plugin-transform-destructuring@^7.18.9", "@babel/plugin-transform-destructuring@^7.24.1":
|
||||
"@babel/plugin-transform-destructuring@^7.18.6", "@babel/plugin-transform-destructuring@^7.24.1":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz#b1e8243af4a0206841973786292b8c8dd8447345"
|
||||
integrity sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.0"
|
||||
|
||||
"@babel/plugin-transform-destructuring@^7.18.9":
|
||||
version "7.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.5.tgz#80843ee6a520f7362686d1a97a7b53544ede453c"
|
||||
integrity sha512-SZuuLyfxvsm+Ah57I/i1HVjveBENYK9ue8MJ7qkc7ndoNjqquJiElzA7f5yaAXjyW2hKojosOTAQQRX50bPSVg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.5"
|
||||
|
||||
"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.24.1", "@babel/plugin-transform-dotall-regex@^7.4.4":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz#d56913d2f12795cc9930801b84c6f8c47513ac13"
|
||||
@@ -948,13 +993,20 @@
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
|
||||
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
|
||||
|
||||
"@babel/plugin-transform-parameters@^7.18.6", "@babel/plugin-transform-parameters@^7.18.8", "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.24.1":
|
||||
"@babel/plugin-transform-parameters@^7.18.6", "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.24.1":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz#983c15d114da190506c75b616ceb0f817afcc510"
|
||||
integrity sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.0"
|
||||
|
||||
"@babel/plugin-transform-parameters@^7.18.8":
|
||||
version "7.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.5.tgz#5c3b23f3a6b8fed090f9b98f2926896d3153cc62"
|
||||
integrity sha512-9Co00MqZ2aoky+4j2jhofErthm6QVLKbpQrvz20c3CH9KQCLHyNB+t2ya4/UrRpQGR+Wrwjg9foopoeSdnHOkA==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.5"
|
||||
|
||||
"@babel/plugin-transform-private-methods@^7.24.1":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz#a0faa1ae87eff077e1e47a5ec81c3aef383dc15a"
|
||||
@@ -1044,7 +1096,7 @@
|
||||
|
||||
"@babel/plugin-transform-runtime@7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.6.tgz#77b14416015ea93367ca06979710f5000ff34ccb"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.6.tgz#77b14416015ea93367ca06979710f5000ff34ccb"
|
||||
integrity sha512-8uRHk9ZmRSnWqUgyae249EJZ94b0yAGLBIqzZzl+0iEdbno55Pmlt/32JZsHwXD9k/uZj18Aqqk35wBX4CBTXA==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.18.6"
|
||||
@@ -1107,13 +1159,20 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.0"
|
||||
|
||||
"@babel/plugin-transform-typeof-symbol@^7.18.6", "@babel/plugin-transform-typeof-symbol@^7.18.9", "@babel/plugin-transform-typeof-symbol@^7.24.1":
|
||||
"@babel/plugin-transform-typeof-symbol@^7.18.6", "@babel/plugin-transform-typeof-symbol@^7.24.1":
|
||||
version "7.24.1"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz#6831f78647080dec044f7e9f68003d99424f94c7"
|
||||
integrity sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.0"
|
||||
|
||||
"@babel/plugin-transform-typeof-symbol@^7.18.9":
|
||||
version "7.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.5.tgz#703cace5ef74155fb5eecab63cbfc39bdd25fe12"
|
||||
integrity sha512-UTGnhYVZtTAjdwOTzT+sCyXmTn8AhaxOS/MjG9REclZ6ULHWF9KoCZur0HSGU7hk8PdBFKKbYe6+gqdXWz84Jg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.24.5"
|
||||
|
||||
"@babel/plugin-transform-typescript@7.18.8":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0"
|
||||
@@ -1247,7 +1306,7 @@
|
||||
|
||||
"@babel/preset-env@7.18.9":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.9.tgz#9b3425140d724fbe590322017466580844c7eaff"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.9.tgz#9b3425140d724fbe590322017466580844c7eaff"
|
||||
integrity sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.18.8"
|
||||
@@ -1523,6 +1582,15 @@
|
||||
"@babel/helper-validator-identifier" "^7.22.20"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.24.5":
|
||||
version "7.24.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7"
|
||||
integrity sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.24.1"
|
||||
"@babel/helper-validator-identifier" "^7.24.5"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@bcoe/v8-coverage@^0.2.3":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
@@ -1540,7 +1608,7 @@
|
||||
|
||||
"@discoveryjs/json-ext@^0.5.0":
|
||||
version "0.5.7"
|
||||
resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
||||
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
|
||||
|
||||
"@esbuild/aix-ppc64@0.19.10":
|
||||
@@ -1930,10 +1998,10 @@
|
||||
resolved "https://registry.npmjs.org/@excalidraw/markdown-to-text/-/markdown-to-text-0.1.2.tgz#1703705e7da608cf478f17bfe96fb295f55a23eb"
|
||||
integrity sha512-1nDXBNAojfi3oSFwJswKREkFm5wrSjqay81QlyRv2pkITG/XYB5v+oChENVBQLcxQwX4IUATWvXM5BcaNhPiIg==
|
||||
|
||||
"@excalidraw/mermaid-to-excalidraw@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@excalidraw/mermaid-to-excalidraw/-/mermaid-to-excalidraw-1.0.0.tgz#8c058d2a43230425cba96d01e4a669a2d7c586a2"
|
||||
integrity sha512-RGSoJBY2gFag6mQOIwa3OakTrvAZYx0bwvnr5ojuCZInih8Fxhje4X1WZfsaQx+GATEH8Ioq3O3b1FPDg4nKjQ==
|
||||
"@excalidraw/mermaid-to-excalidraw@0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.npmjs.org/@excalidraw/mermaid-to-excalidraw/-/mermaid-to-excalidraw-0.3.0.tgz#94c438133fc66db6b920e237abda5152b62e6cb0"
|
||||
integrity sha512-eyFN8y2ES3HFtETZWZZBakkSB5ROfnHJeCLeBlMgrIk1fxbXpPtxlu2VwGNpqPjDiCfV5FYnx7FaZ4CRiVRVMg==
|
||||
dependencies:
|
||||
"@excalidraw/markdown-to-text" "0.1.2"
|
||||
mermaid "10.9.0"
|
||||
@@ -3145,7 +3213,7 @@
|
||||
|
||||
"@types/estree@^0.0.51":
|
||||
version "0.0.51"
|
||||
resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
|
||||
integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
|
||||
|
||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
|
||||
@@ -3543,7 +3611,7 @@
|
||||
|
||||
"@webassemblyjs/ast@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
|
||||
integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==
|
||||
dependencies:
|
||||
"@webassemblyjs/helper-numbers" "1.11.1"
|
||||
@@ -3559,7 +3627,7 @@
|
||||
|
||||
"@webassemblyjs/floating-point-hex-parser@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
|
||||
integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
|
||||
|
||||
"@webassemblyjs/floating-point-hex-parser@1.11.6":
|
||||
@@ -3569,7 +3637,7 @@
|
||||
|
||||
"@webassemblyjs/helper-api-error@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
|
||||
integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
|
||||
|
||||
"@webassemblyjs/helper-api-error@1.11.6":
|
||||
@@ -3579,7 +3647,7 @@
|
||||
|
||||
"@webassemblyjs/helper-buffer@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
|
||||
integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
|
||||
|
||||
"@webassemblyjs/helper-buffer@1.12.1":
|
||||
@@ -3589,7 +3657,7 @@
|
||||
|
||||
"@webassemblyjs/helper-numbers@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
|
||||
integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==
|
||||
dependencies:
|
||||
"@webassemblyjs/floating-point-hex-parser" "1.11.1"
|
||||
@@ -3607,7 +3675,7 @@
|
||||
|
||||
"@webassemblyjs/helper-wasm-bytecode@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
|
||||
integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
|
||||
|
||||
"@webassemblyjs/helper-wasm-bytecode@1.11.6":
|
||||
@@ -3617,7 +3685,7 @@
|
||||
|
||||
"@webassemblyjs/helper-wasm-section@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
|
||||
integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
@@ -3637,7 +3705,7 @@
|
||||
|
||||
"@webassemblyjs/ieee754@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
|
||||
integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==
|
||||
dependencies:
|
||||
"@xtuc/ieee754" "^1.2.0"
|
||||
@@ -3651,7 +3719,7 @@
|
||||
|
||||
"@webassemblyjs/leb128@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
|
||||
integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==
|
||||
dependencies:
|
||||
"@xtuc/long" "4.2.2"
|
||||
@@ -3665,7 +3733,7 @@
|
||||
|
||||
"@webassemblyjs/utf8@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
|
||||
integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
|
||||
|
||||
"@webassemblyjs/utf8@1.11.6":
|
||||
@@ -3675,7 +3743,7 @@
|
||||
|
||||
"@webassemblyjs/wasm-edit@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
|
||||
integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
@@ -3703,7 +3771,7 @@
|
||||
|
||||
"@webassemblyjs/wasm-gen@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
|
||||
integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
@@ -3725,7 +3793,7 @@
|
||||
|
||||
"@webassemblyjs/wasm-opt@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
|
||||
integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
@@ -3745,7 +3813,7 @@
|
||||
|
||||
"@webassemblyjs/wasm-parser@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
|
||||
integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
@@ -3769,7 +3837,7 @@
|
||||
|
||||
"@webassemblyjs/wast-printer@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
|
||||
integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
@@ -3785,19 +3853,19 @@
|
||||
|
||||
"@webpack-cli/configtest@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5"
|
||||
integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==
|
||||
|
||||
"@webpack-cli/info@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1"
|
||||
integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==
|
||||
dependencies:
|
||||
envinfo "^7.7.3"
|
||||
|
||||
"@webpack-cli/serve@^1.7.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1"
|
||||
integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==
|
||||
|
||||
"@xtuc/ieee754@^1.2.0":
|
||||
@@ -4592,7 +4660,7 @@ check-error@^1.0.2, check-error@^1.0.3:
|
||||
dependencies:
|
||||
get-func-name "^2.0.2"
|
||||
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1, chokidar@^3.5.3:
|
||||
chokidar@3.6.0, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1, chokidar@^3.5.3:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
|
||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
||||
@@ -4673,7 +4741,7 @@ cliui@^8.0.1:
|
||||
|
||||
clone-deep@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
|
||||
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
|
||||
integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
|
||||
dependencies:
|
||||
is-plain-object "^2.0.4"
|
||||
@@ -5516,7 +5584,7 @@ dotenv@^16.0.0:
|
||||
|
||||
duplexer@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
|
||||
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
|
||||
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
|
||||
|
||||
eastasianwidth@^0.2.0:
|
||||
@@ -5606,9 +5674,9 @@ entities@^4.4.0:
|
||||
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||
|
||||
envinfo@^7.7.3:
|
||||
version "7.11.1"
|
||||
resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz#2ffef77591057081b0129a8fd8cf6118da1b94e1"
|
||||
integrity sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==
|
||||
version "7.13.0"
|
||||
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31"
|
||||
integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==
|
||||
|
||||
error-ex@^1.3.1:
|
||||
version "1.3.2"
|
||||
@@ -5718,7 +5786,7 @@ es-iterator-helpers@^1.0.15, es-iterator-helpers@^1.0.17:
|
||||
|
||||
es-module-lexer@^0.9.0:
|
||||
version "0.9.3"
|
||||
resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
|
||||
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
|
||||
integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
|
||||
|
||||
es-module-lexer@^1.2.1:
|
||||
@@ -6292,7 +6360,7 @@ fast-levenshtein@^2.0.6:
|
||||
|
||||
fastest-levenshtein@^1.0.12:
|
||||
version "1.0.16"
|
||||
resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
|
||||
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
|
||||
integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
|
||||
|
||||
fastq@^1.6.0:
|
||||
@@ -6330,7 +6398,7 @@ file-entry-cache@^6.0.1:
|
||||
|
||||
file-loader@6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d"
|
||||
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d"
|
||||
integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
@@ -6403,7 +6471,7 @@ flat-cache@^3.0.4:
|
||||
|
||||
flat@^5.0.2:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
|
||||
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
|
||||
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
||||
|
||||
flatted@^3.2.7, flatted@^3.2.9:
|
||||
@@ -6661,7 +6729,7 @@ graphemer@^1.4.0:
|
||||
|
||||
gzip-size@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
|
||||
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
|
||||
integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==
|
||||
dependencies:
|
||||
duplexer "^0.1.2"
|
||||
@@ -6902,7 +6970,7 @@ import-fresh@^3.0.0, import-fresh@^3.2.1:
|
||||
|
||||
import-local@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
|
||||
resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
|
||||
integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
|
||||
dependencies:
|
||||
pkg-dir "^4.2.0"
|
||||
@@ -6957,7 +7025,7 @@ internmap@^1.0.0:
|
||||
|
||||
interpret@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
|
||||
integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
|
||||
|
||||
invariant@^2.2.2, invariant@^2.2.4:
|
||||
@@ -7113,7 +7181,7 @@ is-obj@^1.0.1:
|
||||
|
||||
is-plain-object@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||
integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
|
||||
dependencies:
|
||||
isobject "^3.0.1"
|
||||
@@ -7211,7 +7279,7 @@ isexe@^2.0.0:
|
||||
|
||||
isobject@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||
integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
|
||||
|
||||
istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
|
||||
@@ -7518,7 +7586,7 @@ khroma@^2.0.0:
|
||||
|
||||
kind-of@^6.0.2:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
||||
|
||||
kleur@^4.0.3:
|
||||
@@ -8150,7 +8218,7 @@ mri@^1.1.0:
|
||||
|
||||
mrmime@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
|
||||
resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
|
||||
integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==
|
||||
|
||||
mrmime@^2.0.0:
|
||||
@@ -9009,7 +9077,7 @@ realistic-structured-clone@^2.0.1:
|
||||
|
||||
rechoir@^0.7.0:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
|
||||
integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==
|
||||
dependencies:
|
||||
resolve "^1.9.0"
|
||||
@@ -9120,7 +9188,7 @@ requires-port@^1.0.0:
|
||||
|
||||
resolve-cwd@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||
integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
|
||||
dependencies:
|
||||
resolve-from "^5.0.0"
|
||||
@@ -9132,7 +9200,7 @@ resolve-from@^4.0.0:
|
||||
|
||||
resolve-from@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
|
||||
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
|
||||
|
||||
resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.4, resolve@^1.22.6, resolve@^1.9.0:
|
||||
@@ -9441,7 +9509,7 @@ set-function-name@^2.0.1, set-function-name@^2.0.2:
|
||||
|
||||
shallow-clone@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
||||
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
||||
integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
|
||||
dependencies:
|
||||
kind-of "^6.0.2"
|
||||
@@ -9485,7 +9553,7 @@ signal-exit@^4.1.0:
|
||||
|
||||
sirv@^1.0.7:
|
||||
version "1.0.19"
|
||||
resolved "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49"
|
||||
resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49"
|
||||
integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==
|
||||
dependencies:
|
||||
"@polka/url" "^1.0.0-next.20"
|
||||
@@ -9993,7 +10061,7 @@ to-regex-range@^5.0.1:
|
||||
|
||||
totalist@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
|
||||
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
|
||||
integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
|
||||
|
||||
totalist@^3.0.0:
|
||||
@@ -10574,7 +10642,7 @@ webidl-conversions@^7.0.0:
|
||||
|
||||
webpack-bundle-analyzer@4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
|
||||
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
|
||||
integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==
|
||||
dependencies:
|
||||
acorn "^8.0.4"
|
||||
@@ -10589,7 +10657,7 @@ webpack-bundle-analyzer@4.5.0:
|
||||
|
||||
webpack-cli@4.10.0:
|
||||
version "4.10.0"
|
||||
resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31"
|
||||
integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==
|
||||
dependencies:
|
||||
"@discoveryjs/json-ext" "^0.5.0"
|
||||
@@ -10607,7 +10675,7 @@ webpack-cli@4.10.0:
|
||||
|
||||
webpack-merge@^5.7.3:
|
||||
version "5.10.0"
|
||||
resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177"
|
||||
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177"
|
||||
integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==
|
||||
dependencies:
|
||||
clone-deep "^4.0.1"
|
||||
@@ -10621,7 +10689,7 @@ webpack-sources@^3.2.3:
|
||||
|
||||
webpack@5.76.0:
|
||||
version "5.76.0"
|
||||
resolved "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c"
|
||||
integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.3"
|
||||
@@ -10816,7 +10884,7 @@ why-is-node-running@^2.2.2:
|
||||
|
||||
wildcard@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
|
||||
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
|
||||
integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==
|
||||
|
||||
workbox-background-sync@7.0.0:
|
||||
@@ -11007,7 +11075,7 @@ ws@8.5.0:
|
||||
|
||||
ws@^7.3.1:
|
||||
version "7.5.9"
|
||||
resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
|
||||
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
|
||||
|
||||
ws@^8.13.0:
|
||||
|
||||
Reference in New Issue
Block a user