Set Trailing Cmma to (#525)
This commit is contained in:
parent
25202aec11
commit
ee68af0fd3
@ -11,5 +11,5 @@ module.exports = {
|
||||
files.filter(file => !cli.isPathIgnored(file)).join(" ")
|
||||
);
|
||||
},
|
||||
"*.{js,css,scss,json,md,ts,tsx,html,yml}": ["prettier --write"]
|
||||
"*.{js,css,scss,json,md,ts,tsx,html,yml}": ["prettier --write"],
|
||||
};
|
||||
|
@ -1 +1,3 @@
|
||||
{}
|
||||
{
|
||||
"trailingComma": "all"
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ var config = defaults.__get__("config");
|
||||
config.optimization.runtimeChunk = false;
|
||||
config.optimization.splitChunks = {
|
||||
cacheGroups: {
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
// Set the filename to be deterministic
|
||||
config.output.filename = "static/js/build-node.js";
|
||||
@ -33,7 +33,7 @@ config.externals = function(context, request, callback) {
|
||||
if (/\.node$/.test(request)) {
|
||||
return callback(
|
||||
null,
|
||||
"commonjs ../../../node_modules/canvas/build/Release/canvas.node"
|
||||
"commonjs ../../../node_modules/canvas/build/Release/canvas.node",
|
||||
);
|
||||
}
|
||||
callback();
|
||||
|
@ -20,7 +20,7 @@ export const actionChangeViewBackgroundColor: Action = {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const actionClearCanvas: Action = {
|
||||
@ -28,7 +28,7 @@ export const actionClearCanvas: Action = {
|
||||
perform: () => {
|
||||
return {
|
||||
elements: [],
|
||||
appState: getDefaultAppState()
|
||||
appState: getDefaultAppState(),
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ updateData, t }) => (
|
||||
@ -47,5 +47,5 @@ export const actionClearCanvas: Action = {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
@ -6,10 +6,10 @@ export const actionDeleteSelected: Action = {
|
||||
name: "deleteSelectedElements",
|
||||
perform: elements => {
|
||||
return {
|
||||
elements: deleteSelectedElements(elements)
|
||||
elements: deleteSelectedElements(elements),
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.delete",
|
||||
contextMenuOrder: 3,
|
||||
keyTest: event => event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE
|
||||
keyTest: event => event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE,
|
||||
};
|
||||
|
@ -15,7 +15,7 @@ export const actionChangeProjectName: Action = {
|
||||
value={appState.name || "Unnamed"}
|
||||
onChange={(name: string) => updateData(name)}
|
||||
/>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionChangeExportBackground: Action = {
|
||||
@ -34,7 +34,7 @@ export const actionChangeExportBackground: Action = {
|
||||
/>{" "}
|
||||
{t("labels.withBackground")}
|
||||
</label>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionSaveScene: Action = {
|
||||
@ -51,7 +51,7 @@ export const actionSaveScene: Action = {
|
||||
aria-label={t("buttons.save")}
|
||||
onClick={() => updateData(null)}
|
||||
/>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionLoadScene: Action = {
|
||||
@ -59,7 +59,7 @@ export const actionLoadScene: Action = {
|
||||
perform: (
|
||||
elements,
|
||||
appState,
|
||||
{ elements: loadedElements, appState: loadedAppState }
|
||||
{ elements: loadedElements, appState: loadedAppState },
|
||||
) => {
|
||||
return { elements: loadedElements, appState: loadedAppState };
|
||||
},
|
||||
@ -77,5 +77,5 @@ export const actionLoadScene: Action = {
|
||||
.catch(err => console.error(err));
|
||||
}}
|
||||
/>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
@ -9,7 +9,7 @@ import { AppState } from "../../src/types";
|
||||
|
||||
const changeProperty = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
callback: (element: ExcalidrawElement) => ExcalidrawElement
|
||||
callback: (element: ExcalidrawElement) => ExcalidrawElement,
|
||||
) => {
|
||||
return elements.map(element => {
|
||||
if (element.isSelected) {
|
||||
@ -23,7 +23,7 @@ const getFormValue = function<T>(
|
||||
editingElement: AppState["editingElement"],
|
||||
elements: readonly ExcalidrawElement[],
|
||||
getAttribute: (element: ExcalidrawElement) => T,
|
||||
defaultValue?: T
|
||||
defaultValue?: T,
|
||||
): T | null {
|
||||
return (
|
||||
(editingElement && getAttribute(editingElement)) ??
|
||||
@ -40,9 +40,9 @@ export const actionChangeStrokeColor: Action = {
|
||||
elements: changeProperty(elements, el => ({
|
||||
...el,
|
||||
shape: null,
|
||||
strokeColor: value
|
||||
strokeColor: value,
|
||||
})),
|
||||
appState: { ...appState, currentItemStrokeColor: value }
|
||||
appState: { ...appState, currentItemStrokeColor: value },
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData, t }) => (
|
||||
@ -54,12 +54,12 @@ export const actionChangeStrokeColor: Action = {
|
||||
appState.editingElement,
|
||||
elements,
|
||||
element => element.strokeColor,
|
||||
appState.currentItemStrokeColor
|
||||
appState.currentItemStrokeColor,
|
||||
)}
|
||||
onChange={updateData}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionChangeBackgroundColor: Action = {
|
||||
@ -69,9 +69,9 @@ export const actionChangeBackgroundColor: Action = {
|
||||
elements: changeProperty(elements, el => ({
|
||||
...el,
|
||||
shape: null,
|
||||
backgroundColor: value
|
||||
backgroundColor: value,
|
||||
})),
|
||||
appState: { ...appState, currentItemBackgroundColor: value }
|
||||
appState: { ...appState, currentItemBackgroundColor: value },
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData, t }) => (
|
||||
@ -83,12 +83,12 @@ export const actionChangeBackgroundColor: Action = {
|
||||
appState.editingElement,
|
||||
elements,
|
||||
element => element.backgroundColor,
|
||||
appState.currentItemBackgroundColor
|
||||
appState.currentItemBackgroundColor,
|
||||
)}
|
||||
onChange={updateData}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionChangeFillStyle: Action = {
|
||||
@ -98,8 +98,8 @@ export const actionChangeFillStyle: Action = {
|
||||
elements: changeProperty(elements, el => ({
|
||||
...el,
|
||||
shape: null,
|
||||
fillStyle: value
|
||||
}))
|
||||
fillStyle: value,
|
||||
})),
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData, t }) => (
|
||||
@ -109,19 +109,19 @@ export const actionChangeFillStyle: Action = {
|
||||
options={[
|
||||
{ value: "solid", text: t("labels.solid") },
|
||||
{ value: "hachure", text: t("labels.hachure") },
|
||||
{ value: "cross-hatch", text: t("labels.crossHatch") }
|
||||
{ value: "cross-hatch", text: t("labels.crossHatch") },
|
||||
]}
|
||||
value={getFormValue(
|
||||
appState.editingElement,
|
||||
elements,
|
||||
element => element.fillStyle
|
||||
element => element.fillStyle,
|
||||
)}
|
||||
onChange={value => {
|
||||
updateData(value);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionChangeStrokeWidth: Action = {
|
||||
@ -131,8 +131,8 @@ export const actionChangeStrokeWidth: Action = {
|
||||
elements: changeProperty(elements, el => ({
|
||||
...el,
|
||||
shape: null,
|
||||
strokeWidth: value
|
||||
}))
|
||||
strokeWidth: value,
|
||||
})),
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData, t }) => (
|
||||
@ -142,17 +142,17 @@ export const actionChangeStrokeWidth: Action = {
|
||||
options={[
|
||||
{ value: 1, text: t("labels.thin") },
|
||||
{ value: 2, text: t("labels.bold") },
|
||||
{ value: 4, text: t("labels.extraBold") }
|
||||
{ value: 4, text: t("labels.extraBold") },
|
||||
]}
|
||||
value={getFormValue(
|
||||
appState.editingElement,
|
||||
elements,
|
||||
element => element.strokeWidth
|
||||
element => element.strokeWidth,
|
||||
)}
|
||||
onChange={value => updateData(value)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionChangeSloppiness: Action = {
|
||||
@ -162,8 +162,8 @@ export const actionChangeSloppiness: Action = {
|
||||
elements: changeProperty(elements, el => ({
|
||||
...el,
|
||||
shape: null,
|
||||
roughness: value
|
||||
}))
|
||||
roughness: value,
|
||||
})),
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData, t }) => (
|
||||
@ -173,17 +173,17 @@ export const actionChangeSloppiness: Action = {
|
||||
options={[
|
||||
{ value: 0, text: t("labels.architect") },
|
||||
{ value: 1, text: t("labels.artist") },
|
||||
{ value: 3, text: t("labels.cartoonist") }
|
||||
{ value: 3, text: t("labels.cartoonist") },
|
||||
]}
|
||||
value={getFormValue(
|
||||
appState.editingElement,
|
||||
elements,
|
||||
element => element.roughness
|
||||
element => element.roughness,
|
||||
)}
|
||||
onChange={value => updateData(value)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionChangeOpacity: Action = {
|
||||
@ -193,8 +193,8 @@ export const actionChangeOpacity: Action = {
|
||||
elements: changeProperty(elements, el => ({
|
||||
...el,
|
||||
shape: null,
|
||||
opacity: value
|
||||
}))
|
||||
opacity: value,
|
||||
})),
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData, t }) => (
|
||||
@ -210,12 +210,12 @@ export const actionChangeOpacity: Action = {
|
||||
appState.editingElement,
|
||||
elements,
|
||||
element => element.opacity,
|
||||
100 /* default opacity */
|
||||
100 /* default opacity */,
|
||||
) ?? undefined
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionChangeFontSize: Action = {
|
||||
@ -227,14 +227,14 @@ export const actionChangeFontSize: Action = {
|
||||
const element: ExcalidrawTextElement = {
|
||||
...el,
|
||||
shape: null,
|
||||
font: `${value}px ${el.font.split("px ")[1]}`
|
||||
font: `${value}px ${el.font.split("px ")[1]}`,
|
||||
};
|
||||
redrawTextBoundingBox(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
return el;
|
||||
})
|
||||
}),
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData, t }) => (
|
||||
@ -245,17 +245,17 @@ export const actionChangeFontSize: Action = {
|
||||
{ value: 16, text: t("labels.small") },
|
||||
{ value: 20, text: t("labels.medium") },
|
||||
{ value: 28, text: t("labels.large") },
|
||||
{ value: 36, text: t("labels.veryLarge") }
|
||||
{ value: 36, text: t("labels.veryLarge") },
|
||||
]}
|
||||
value={getFormValue(
|
||||
appState.editingElement,
|
||||
elements,
|
||||
element => isTextElement(element) && +element.font.split("px ")[0]
|
||||
element => isTextElement(element) && +element.font.split("px ")[0],
|
||||
)}
|
||||
onChange={value => updateData(value)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const actionChangeFontFamily: Action = {
|
||||
@ -267,14 +267,14 @@ export const actionChangeFontFamily: Action = {
|
||||
const element: ExcalidrawTextElement = {
|
||||
...el,
|
||||
shape: null,
|
||||
font: `${el.font.split("px ")[0]}px ${value}`
|
||||
font: `${el.font.split("px ")[0]}px ${value}`,
|
||||
};
|
||||
redrawTextBoundingBox(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
return el;
|
||||
})
|
||||
}),
|
||||
};
|
||||
},
|
||||
PanelComponent: ({ elements, appState, updateData, t }) => (
|
||||
@ -284,15 +284,15 @@ export const actionChangeFontFamily: Action = {
|
||||
options={[
|
||||
{ value: "Virgil", text: t("labels.handDrawn") },
|
||||
{ value: "Helvetica", text: t("labels.normal") },
|
||||
{ value: "Cascadia", text: t("labels.code") }
|
||||
{ value: "Cascadia", text: t("labels.code") },
|
||||
]}
|
||||
value={getFormValue(
|
||||
appState.editingElement,
|
||||
elements,
|
||||
element => isTextElement(element) && element.font.split("px ")[1]
|
||||
element => isTextElement(element) && element.font.split("px ")[1],
|
||||
)}
|
||||
onChange={value => updateData(value)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
@ -5,9 +5,9 @@ export const actionSelectAll: Action = {
|
||||
name: "selectAll",
|
||||
perform: elements => {
|
||||
return {
|
||||
elements: elements.map(elem => ({ ...elem, isSelected: true }))
|
||||
elements: elements.map(elem => ({ ...elem, isSelected: true })),
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.selectAll",
|
||||
keyTest: event => event[KEYS.META] && event.code === "KeyA"
|
||||
keyTest: event => event[KEYS.META] && event.code === "KeyA",
|
||||
};
|
||||
|
@ -15,7 +15,7 @@ export const actionCopyStyles: Action = {
|
||||
},
|
||||
contextItemLabel: "labels.copyStyles",
|
||||
keyTest: event => event[KEYS.META] && event.shiftKey && event.code === "KeyC",
|
||||
contextMenuOrder: 0
|
||||
contextMenuOrder: 0,
|
||||
};
|
||||
|
||||
export const actionPasteStyles: Action = {
|
||||
@ -33,7 +33,7 @@ export const actionPasteStyles: Action = {
|
||||
strokeColor: pastedElement?.strokeColor,
|
||||
fillStyle: pastedElement?.fillStyle,
|
||||
opacity: pastedElement?.opacity,
|
||||
roughness: pastedElement?.roughness
|
||||
roughness: pastedElement?.roughness,
|
||||
};
|
||||
if (isTextElement(newElement)) {
|
||||
newElement.font = pastedElement?.font;
|
||||
@ -42,10 +42,10 @@ export const actionPasteStyles: Action = {
|
||||
return newElement;
|
||||
}
|
||||
return element;
|
||||
})
|
||||
}),
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.pasteStyles",
|
||||
keyTest: event => event[KEYS.META] && event.shiftKey && event.code === "KeyV",
|
||||
contextMenuOrder: 1
|
||||
contextMenuOrder: 1,
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
moveOneLeft,
|
||||
moveOneRight,
|
||||
moveAllLeft,
|
||||
moveAllRight
|
||||
moveAllRight,
|
||||
} from "../zindex";
|
||||
import { getSelectedIndices } from "../scene";
|
||||
import { KEYS } from "../keys";
|
||||
@ -13,13 +13,13 @@ export const actionSendBackward: Action = {
|
||||
perform: (elements, appState) => {
|
||||
return {
|
||||
elements: moveOneLeft([...elements], getSelectedIndices(elements)),
|
||||
appState
|
||||
appState,
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.sendBackward",
|
||||
keyPriority: 40,
|
||||
keyTest: event =>
|
||||
event[KEYS.META] && event.shiftKey && event.altKey && event.code === "KeyB"
|
||||
event[KEYS.META] && event.shiftKey && event.altKey && event.code === "KeyB",
|
||||
};
|
||||
|
||||
export const actionBringForward: Action = {
|
||||
@ -27,13 +27,13 @@ export const actionBringForward: Action = {
|
||||
perform: (elements, appState) => {
|
||||
return {
|
||||
elements: moveOneRight([...elements], getSelectedIndices(elements)),
|
||||
appState
|
||||
appState,
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.bringForward",
|
||||
keyPriority: 40,
|
||||
keyTest: event =>
|
||||
event[KEYS.META] && event.shiftKey && event.altKey && event.code === "KeyF"
|
||||
event[KEYS.META] && event.shiftKey && event.altKey && event.code === "KeyF",
|
||||
};
|
||||
|
||||
export const actionSendToBack: Action = {
|
||||
@ -41,11 +41,11 @@ export const actionSendToBack: Action = {
|
||||
perform: (elements, appState) => {
|
||||
return {
|
||||
elements: moveAllLeft([...elements], getSelectedIndices(elements)),
|
||||
appState
|
||||
appState,
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.sendToBack",
|
||||
keyTest: event => event[KEYS.META] && event.shiftKey && event.code === "KeyB"
|
||||
keyTest: event => event[KEYS.META] && event.shiftKey && event.code === "KeyB",
|
||||
};
|
||||
|
||||
export const actionBringToFront: Action = {
|
||||
@ -53,9 +53,9 @@ export const actionBringToFront: Action = {
|
||||
perform: (elements, appState) => {
|
||||
return {
|
||||
elements: moveAllRight([...elements], getSelectedIndices(elements)),
|
||||
appState
|
||||
appState,
|
||||
};
|
||||
},
|
||||
contextItemLabel: "labels.bringToFront",
|
||||
keyTest: event => event[KEYS.META] && event.shiftKey && event.code === "KeyF"
|
||||
keyTest: event => event[KEYS.META] && event.shiftKey && event.code === "KeyF",
|
||||
};
|
||||
|
@ -4,7 +4,7 @@ export {
|
||||
actionBringForward,
|
||||
actionBringToFront,
|
||||
actionSendBackward,
|
||||
actionSendToBack
|
||||
actionSendToBack,
|
||||
} from "./actionZindex";
|
||||
export { actionSelectAll } from "./actionSelectAll";
|
||||
export {
|
||||
@ -15,19 +15,19 @@ export {
|
||||
actionChangeSloppiness,
|
||||
actionChangeOpacity,
|
||||
actionChangeFontSize,
|
||||
actionChangeFontFamily
|
||||
actionChangeFontFamily,
|
||||
} from "./actionProperties";
|
||||
|
||||
export {
|
||||
actionChangeViewBackgroundColor,
|
||||
actionClearCanvas
|
||||
actionClearCanvas,
|
||||
} from "./actionCanvas";
|
||||
|
||||
export {
|
||||
actionChangeProjectName,
|
||||
actionChangeExportBackground,
|
||||
actionSaveScene,
|
||||
actionLoadScene
|
||||
actionLoadScene,
|
||||
} from "./actionExport";
|
||||
|
||||
export { actionCopyStyles, actionPasteStyles } from "./actionStyles";
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
Action,
|
||||
ActionsManagerInterface,
|
||||
UpdaterFn,
|
||||
ActionFilterFn
|
||||
ActionFilterFn,
|
||||
} from "./types";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppState } from "../types";
|
||||
@ -17,7 +17,7 @@ export class ActionManager implements ActionsManagerInterface {
|
||||
| null = null;
|
||||
|
||||
setUpdater(
|
||||
updater: (elements: ExcalidrawElement[], appState: AppState) => void
|
||||
updater: (elements: ExcalidrawElement[], appState: AppState) => void,
|
||||
) {
|
||||
this.updater = updater;
|
||||
}
|
||||
@ -29,12 +29,12 @@ export class ActionManager implements ActionsManagerInterface {
|
||||
handleKeyDown(
|
||||
event: KeyboardEvent,
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState
|
||||
appState: AppState,
|
||||
) {
|
||||
const data = Object.values(this.actions)
|
||||
.sort((a, b) => (b.keyPriority || 0) - (a.keyPriority || 0))
|
||||
.filter(
|
||||
action => action.keyTest && action.keyTest(event, elements, appState)
|
||||
action => action.keyTest && action.keyTest(event, elements, appState),
|
||||
);
|
||||
|
||||
if (data.length === 0) return {};
|
||||
@ -48,7 +48,7 @@ export class ActionManager implements ActionsManagerInterface {
|
||||
appState: AppState,
|
||||
updater: UpdaterFn,
|
||||
actionFilter: ActionFilterFn = action => action,
|
||||
t?: TFunction
|
||||
t?: TFunction,
|
||||
) {
|
||||
return Object.values(this.actions)
|
||||
.filter(actionFilter)
|
||||
@ -56,7 +56,7 @@ export class ActionManager implements ActionsManagerInterface {
|
||||
.sort(
|
||||
(a, b) =>
|
||||
(a.contextMenuOrder !== undefined ? a.contextMenuOrder : 999) -
|
||||
(b.contextMenuOrder !== undefined ? b.contextMenuOrder : 999)
|
||||
(b.contextMenuOrder !== undefined ? b.contextMenuOrder : 999),
|
||||
)
|
||||
.map(action => ({
|
||||
label:
|
||||
@ -65,7 +65,7 @@ export class ActionManager implements ActionsManagerInterface {
|
||||
: action.contextItemLabel!,
|
||||
action: () => {
|
||||
updater(action.perform(elements, appState, null));
|
||||
}
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ export class ActionManager implements ActionsManagerInterface {
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
updater: UpdaterFn,
|
||||
t: TFunction
|
||||
t: TFunction,
|
||||
) {
|
||||
if (this.actions[name] && "PanelComponent" in this.actions[name]) {
|
||||
const action = this.actions[name];
|
||||
|
@ -11,7 +11,7 @@ export type ActionResult = {
|
||||
type ActionFn = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
formData: any
|
||||
formData: any,
|
||||
) => ActionResult;
|
||||
|
||||
export type UpdaterFn = (res: ActionResult) => void;
|
||||
@ -30,7 +30,7 @@ export interface Action {
|
||||
keyTest?: (
|
||||
event: KeyboardEvent,
|
||||
elements?: readonly ExcalidrawElement[],
|
||||
appState?: AppState
|
||||
appState?: AppState,
|
||||
) => boolean;
|
||||
contextItemLabel?: string;
|
||||
contextMenuOrder?: number;
|
||||
@ -44,19 +44,19 @@ export interface ActionsManagerInterface {
|
||||
handleKeyDown: (
|
||||
event: KeyboardEvent,
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState
|
||||
appState: AppState,
|
||||
) => ActionResult | {};
|
||||
getContextMenuItems: (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
updater: UpdaterFn,
|
||||
actionFilter: ActionFilterFn
|
||||
actionFilter: ActionFilterFn,
|
||||
) => { label: string; action: () => void }[];
|
||||
renderAction: (
|
||||
name: string,
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
updater: UpdaterFn,
|
||||
t: TFunction
|
||||
t: TFunction,
|
||||
) => React.ReactElement | null;
|
||||
}
|
||||
|
@ -19,6 +19,6 @@ export function getDefaultAppState(): AppState {
|
||||
scrollY: 0,
|
||||
cursorX: 0,
|
||||
cursorY: 0,
|
||||
name: DEFAULT_PROJECT_NAME
|
||||
name: DEFAULT_PROJECT_NAME,
|
||||
};
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import React from "react";
|
||||
export function ButtonSelect<T>({
|
||||
options,
|
||||
value,
|
||||
onChange
|
||||
onChange,
|
||||
}: {
|
||||
options: { value: T; text: string }[];
|
||||
value: T | null;
|
||||
|
@ -9,7 +9,7 @@ import "./ColorPicker.css";
|
||||
const Picker = function({
|
||||
colors,
|
||||
color,
|
||||
onChange
|
||||
onChange,
|
||||
}: {
|
||||
colors: string[];
|
||||
color: string | null;
|
||||
@ -53,7 +53,7 @@ const Picker = function({
|
||||
|
||||
function ColorInput({
|
||||
color,
|
||||
onChange
|
||||
onChange,
|
||||
}: {
|
||||
color: string | null;
|
||||
onChange: (color: string) => void;
|
||||
@ -90,7 +90,7 @@ function ColorInput({
|
||||
export function ColorPicker({
|
||||
type,
|
||||
color,
|
||||
onChange
|
||||
onChange,
|
||||
}: {
|
||||
type: "canvasBackground" | "elementBackground" | "elementStroke";
|
||||
color: string | null;
|
||||
@ -149,7 +149,7 @@ const colors = {
|
||||
"#ebfbee",
|
||||
"#f4fce3",
|
||||
"#fff9db",
|
||||
"#fff4e6"
|
||||
"#fff4e6",
|
||||
],
|
||||
// Shade 6
|
||||
elementBackground: [
|
||||
@ -167,7 +167,7 @@ const colors = {
|
||||
"#40c057",
|
||||
"#82c91e",
|
||||
"#fab005",
|
||||
"#fd7e14"
|
||||
"#fd7e14",
|
||||
],
|
||||
// Shade 9
|
||||
elementStroke: [
|
||||
@ -185,6 +185,6 @@ const colors = {
|
||||
"#2b8a3e",
|
||||
"#5c940d",
|
||||
"#e67700",
|
||||
"#d9480f"
|
||||
]
|
||||
"#d9480f",
|
||||
],
|
||||
};
|
||||
|
@ -83,8 +83,8 @@ export default {
|
||||
options={options}
|
||||
onCloseRequest={handleClose}
|
||||
/>,
|
||||
getContextMenuNode()
|
||||
getContextMenuNode(),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ const defaultScale = scales.includes(devicePixelRatio) ? devicePixelRatio : 1;
|
||||
|
||||
type ExportCB = (
|
||||
elements: readonly ExcalidrawElement[],
|
||||
scale?: number
|
||||
scale?: number,
|
||||
) => void;
|
||||
|
||||
export function ExportDialog({
|
||||
@ -36,7 +36,7 @@ export function ExportDialog({
|
||||
syncActionResult,
|
||||
onExportToPng,
|
||||
onExportToClipboard,
|
||||
onExportToBackend
|
||||
onExportToBackend,
|
||||
}: {
|
||||
appState: AppState;
|
||||
elements: readonly ExcalidrawElement[];
|
||||
@ -69,7 +69,7 @@ export function ExportDialog({
|
||||
exportBackground,
|
||||
viewBackgroundColor,
|
||||
exportPadding,
|
||||
scale
|
||||
scale,
|
||||
});
|
||||
previewNode?.appendChild(canvas);
|
||||
return () => {
|
||||
@ -81,7 +81,7 @@ export function ExportDialog({
|
||||
exportBackground,
|
||||
exportPadding,
|
||||
viewBackgroundColor,
|
||||
scale
|
||||
scale,
|
||||
]);
|
||||
|
||||
function handleClose() {
|
||||
@ -141,7 +141,7 @@ export function ExportDialog({
|
||||
elements,
|
||||
appState,
|
||||
syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
<Stack.Col gap={1}>
|
||||
<div className="ExportDialog__scales">
|
||||
@ -165,7 +165,7 @@ export function ExportDialog({
|
||||
elements,
|
||||
appState,
|
||||
syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
{someElementIsSelected && (
|
||||
<div>
|
||||
|
@ -9,7 +9,7 @@ type FixedSideContainerProps = {
|
||||
|
||||
export function FixedSideContainer({
|
||||
children,
|
||||
side
|
||||
side,
|
||||
}: FixedSideContainerProps) {
|
||||
return (
|
||||
<div className={"FixedSideContainer FixedSideContainer_side_" + side}>
|
||||
|
@ -3,7 +3,7 @@ import React from "react";
|
||||
export function LanguageList<T>({
|
||||
onClick,
|
||||
languages,
|
||||
currentLanguage
|
||||
currentLanguage,
|
||||
}: {
|
||||
languages: { lng: string; label: string }[];
|
||||
onClick: (value: string) => void;
|
||||
|
@ -35,7 +35,7 @@ const ICONS = {
|
||||
>
|
||||
<path d="M1728 576v256q0 26-19 45t-45 19h-64q-26 0-45-19t-19-45v-256q0-106-75-181t-181-75-181 75-75 181v192h96q40 0 68 28t28 68v576q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-576q0-40 28-68t68-28h672v-192q0-185 131.5-316.5t316.5-131.5 316.5 131.5 131.5 316.5z" />
|
||||
</svg>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export function LockIcon(props: LockIconProps) {
|
||||
|
@ -16,7 +16,7 @@ export function Modal(props: {
|
||||
{props.children}
|
||||
</div>
|
||||
</div>,
|
||||
modalRoot
|
||||
modalRoot,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ export function Popover({
|
||||
left,
|
||||
top,
|
||||
onCloseRequest,
|
||||
fitInViewport = false
|
||||
fitInViewport = false,
|
||||
}: Props) {
|
||||
const popoverRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
@ -17,7 +17,7 @@ function RowStack({ children, gap, align, justifyContent }: StackProps) {
|
||||
{
|
||||
"--gap": gap,
|
||||
alignItems: align,
|
||||
justifyContent
|
||||
justifyContent,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
@ -34,7 +34,7 @@ function ColStack({ children, gap, align, justifyContent }: StackProps) {
|
||||
{
|
||||
"--gap": gap,
|
||||
justifyItems: align,
|
||||
justifyContent
|
||||
justifyContent,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
@ -45,5 +45,5 @@ function ColStack({ children, gap, align, justifyContent }: StackProps) {
|
||||
|
||||
export default {
|
||||
Row: RowStack,
|
||||
Col: ColStack
|
||||
Col: ColStack,
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ const _ce = ({ x, y, w, h }: { x: number; y: number; w: number; h: number }) =>
|
||||
x,
|
||||
y,
|
||||
width: w,
|
||||
height: h
|
||||
height: h,
|
||||
} as ExcalidrawElement);
|
||||
|
||||
describe("getElementAbsoluteCoords", () => {
|
||||
@ -29,14 +29,14 @@ describe("getElementAbsoluteCoords", () => {
|
||||
|
||||
it("test x2 coordinate if width is positive or zero", () => {
|
||||
const [, , x2] = getElementAbsoluteCoords(
|
||||
_ce({ x: 10, y: 0, w: 10, h: 0 })
|
||||
_ce({ x: 10, y: 0, w: 10, h: 0 }),
|
||||
);
|
||||
expect(x2).toEqual(20);
|
||||
});
|
||||
|
||||
it("test x2 coordinate if width is negative", () => {
|
||||
const [, , x2] = getElementAbsoluteCoords(
|
||||
_ce({ x: 10, y: 0, w: -10, h: 0 })
|
||||
_ce({ x: 10, y: 0, w: -10, h: 0 }),
|
||||
);
|
||||
expect(x2).toEqual(10);
|
||||
});
|
||||
@ -53,14 +53,14 @@ describe("getElementAbsoluteCoords", () => {
|
||||
|
||||
it("test y2 coordinate if height is positive or zero", () => {
|
||||
const [, , , y2] = getElementAbsoluteCoords(
|
||||
_ce({ x: 0, y: 10, w: 0, h: 10 })
|
||||
_ce({ x: 0, y: 10, w: 0, h: 10 }),
|
||||
);
|
||||
expect(y2).toEqual(20);
|
||||
});
|
||||
|
||||
it("test y2 coordinate if height is negative", () => {
|
||||
const [, , , y2] = getElementAbsoluteCoords(
|
||||
_ce({ x: 0, y: 10, w: 0, h: -10 })
|
||||
_ce({ x: 0, y: 10, w: 0, h: -10 }),
|
||||
);
|
||||
expect(y2).toEqual(10);
|
||||
});
|
||||
|
@ -10,7 +10,7 @@ export function getElementAbsoluteCoords(element: ExcalidrawElement) {
|
||||
element.width >= 0 ? element.x : element.x + element.width, // x1
|
||||
element.height >= 0 ? element.y : element.y + element.height, // y1
|
||||
element.width >= 0 ? element.x + element.width : element.x, // x2
|
||||
element.height >= 0 ? element.y + element.height : element.y // y2
|
||||
element.height >= 0 ? element.y + element.height : element.y, // y2
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
getArrowPoints,
|
||||
getDiamondPoints,
|
||||
getElementAbsoluteCoords,
|
||||
getLinePoints
|
||||
getLinePoints,
|
||||
} from "./bounds";
|
||||
|
||||
function isElementDraggableFromInside(element: ExcalidrawElement): boolean {
|
||||
@ -15,7 +15,7 @@ function isElementDraggableFromInside(element: ExcalidrawElement): boolean {
|
||||
export function hitTest(
|
||||
element: ExcalidrawElement,
|
||||
x: number,
|
||||
y: number
|
||||
y: number,
|
||||
): boolean {
|
||||
// For shapes that are composed of lines, we only enable point-selection when the distance
|
||||
// of the click is less than x pixels of any of the lines that the shape is composed of
|
||||
@ -95,7 +95,7 @@ export function hitTest(
|
||||
bottomX,
|
||||
bottomY,
|
||||
leftX,
|
||||
leftY
|
||||
leftY,
|
||||
] = getDiamondPoints(element);
|
||||
|
||||
if (isElementDraggableFromInside(element)) {
|
||||
|
@ -5,7 +5,7 @@ type Sides = "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se";
|
||||
|
||||
export function handlerRectangles(
|
||||
element: ExcalidrawElement,
|
||||
{ scrollX, scrollY }: SceneScroll
|
||||
{ scrollX, scrollY }: SceneScroll,
|
||||
) {
|
||||
const elementX1 = element.x;
|
||||
const elementX2 = element.x + element.width;
|
||||
@ -24,14 +24,14 @@ export function handlerRectangles(
|
||||
elementX1 + (elementX2 - elementX1) / 2 + scrollX - 4,
|
||||
elementY1 - margin + scrollY + marginY,
|
||||
8,
|
||||
8
|
||||
8,
|
||||
];
|
||||
|
||||
handlers["s"] = [
|
||||
elementX1 + (elementX2 - elementX1) / 2 + scrollX - 4,
|
||||
elementY2 - margin + scrollY - marginY,
|
||||
8,
|
||||
8
|
||||
8,
|
||||
];
|
||||
}
|
||||
|
||||
@ -40,14 +40,14 @@ export function handlerRectangles(
|
||||
elementX1 - margin + scrollX + marginX,
|
||||
elementY1 + (elementY2 - elementY1) / 2 + scrollY - 4,
|
||||
8,
|
||||
8
|
||||
8,
|
||||
];
|
||||
|
||||
handlers["e"] = [
|
||||
elementX2 - margin + scrollX - marginX,
|
||||
elementY1 + (elementY2 - elementY1) / 2 + scrollY - 4,
|
||||
8,
|
||||
8
|
||||
8,
|
||||
];
|
||||
}
|
||||
|
||||
@ -55,31 +55,31 @@ export function handlerRectangles(
|
||||
elementX1 - margin + scrollX + marginX,
|
||||
elementY1 - margin + scrollY + marginY,
|
||||
8,
|
||||
8
|
||||
8,
|
||||
]; // nw
|
||||
handlers["ne"] = [
|
||||
elementX2 - margin + scrollX - marginX,
|
||||
elementY1 - margin + scrollY + marginY,
|
||||
8,
|
||||
8
|
||||
8,
|
||||
]; // ne
|
||||
handlers["sw"] = [
|
||||
elementX1 - margin + scrollX + marginX,
|
||||
elementY2 - margin + scrollY - marginY,
|
||||
8,
|
||||
8
|
||||
8,
|
||||
]; // sw
|
||||
handlers["se"] = [
|
||||
elementX2 - margin + scrollX - marginX,
|
||||
elementY2 - margin + scrollY - marginY,
|
||||
8,
|
||||
8
|
||||
8,
|
||||
]; // se
|
||||
|
||||
if (element.type === "arrow" || element.type === "line") {
|
||||
return {
|
||||
nw: handlers.nw,
|
||||
se: handlers.se
|
||||
se: handlers.se,
|
||||
} as typeof handlers;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ export {
|
||||
getElementAbsoluteCoords,
|
||||
getDiamondPoints,
|
||||
getArrowPoints,
|
||||
getLinePoints
|
||||
getLinePoints,
|
||||
} from "./bounds";
|
||||
|
||||
export { handlerRectangles } from "./handlerRectangles";
|
||||
@ -15,5 +15,5 @@ export { redrawTextBoundingBox } from "./textElement";
|
||||
export {
|
||||
getPerfectElementSize,
|
||||
isInvisiblySmallElement,
|
||||
resizePerfectLineForNWHandler
|
||||
resizePerfectLineForNWHandler,
|
||||
} from "./sizeHelpers";
|
||||
|
@ -16,7 +16,7 @@ export function newElement(
|
||||
roughness: number,
|
||||
opacity: number,
|
||||
width = 0,
|
||||
height = 0
|
||||
height = 0,
|
||||
) {
|
||||
const element = {
|
||||
id: nanoid(),
|
||||
@ -33,7 +33,7 @@ export function newElement(
|
||||
opacity,
|
||||
isSelected: false,
|
||||
seed: randomSeed(),
|
||||
shape: null as Drawable | Drawable[] | null
|
||||
shape: null as Drawable | Drawable[] | null,
|
||||
};
|
||||
return element;
|
||||
}
|
||||
@ -41,7 +41,7 @@ export function newElement(
|
||||
export function newTextElement(
|
||||
element: ExcalidrawElement,
|
||||
text: string,
|
||||
font: string
|
||||
font: string,
|
||||
) {
|
||||
const metrics = measureText(text, font);
|
||||
const textElement: ExcalidrawTextElement = {
|
||||
@ -54,7 +54,7 @@ export function newTextElement(
|
||||
y: element.y - metrics.height / 2,
|
||||
width: metrics.width,
|
||||
height: metrics.height,
|
||||
baseline: metrics.baseline
|
||||
baseline: metrics.baseline,
|
||||
};
|
||||
|
||||
return textElement;
|
||||
|
@ -9,7 +9,7 @@ export function resizeTest(
|
||||
element: ExcalidrawElement,
|
||||
x: number,
|
||||
y: number,
|
||||
{ scrollX, scrollY }: SceneScroll
|
||||
{ scrollX, scrollY }: SceneScroll,
|
||||
): HandlerRectanglesRet | false {
|
||||
if (!element.isSelected || element.type === "text") return false;
|
||||
|
||||
@ -36,7 +36,7 @@ export function resizeTest(
|
||||
export function getElementWithResizeHandler(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
{ x, y }: { x: number; y: number },
|
||||
{ scrollX, scrollY }: SceneScroll
|
||||
{ scrollX, scrollY }: SceneScroll,
|
||||
) {
|
||||
return elements.reduce((result, element) => {
|
||||
if (result) {
|
||||
@ -44,7 +44,7 @@ export function getElementWithResizeHandler(
|
||||
}
|
||||
const resizeHandle = resizeTest(element, x, y, {
|
||||
scrollX,
|
||||
scrollY
|
||||
scrollY,
|
||||
});
|
||||
return resizeHandle ? { element, resizeHandle } : null;
|
||||
}, null as { element: ExcalidrawElement; resizeHandle: ReturnType<typeof resizeTest> } | null);
|
||||
|
@ -10,7 +10,7 @@ export function isInvisiblySmallElement(element: ExcalidrawElement): boolean {
|
||||
export function getPerfectElementSize(
|
||||
elementType: string,
|
||||
width: number,
|
||||
height: number
|
||||
height: number,
|
||||
): { width: number; height: number } {
|
||||
const absWidth = Math.abs(width);
|
||||
const absHeight = Math.abs(height);
|
||||
@ -33,7 +33,7 @@ export function getPerfectElementSize(
|
||||
export function resizePerfectLineForNWHandler(
|
||||
element: ExcalidrawElement,
|
||||
x: number,
|
||||
y: number
|
||||
y: number,
|
||||
) {
|
||||
const anchorX = element.x + element.width;
|
||||
const anchorY = element.y + element.height;
|
||||
|
@ -22,7 +22,7 @@ export function textWysiwyg({
|
||||
y,
|
||||
strokeColor,
|
||||
font,
|
||||
onSubmit
|
||||
onSubmit,
|
||||
}: TextWysiwygParams) {
|
||||
// Using contenteditable here as it has dynamic width.
|
||||
// But this solution has an issue — it allows to paste
|
||||
@ -45,7 +45,7 @@ export function textWysiwyg({
|
||||
padding: "4px",
|
||||
outline: "transparent",
|
||||
whiteSpace: "nowrap",
|
||||
minHeight: "1em"
|
||||
minHeight: "1em",
|
||||
});
|
||||
|
||||
editable.onkeydown = ev => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ExcalidrawElement, ExcalidrawTextElement } from "./types";
|
||||
|
||||
export function isTextElement(
|
||||
element: ExcalidrawElement
|
||||
element: ExcalidrawElement,
|
||||
): element is ExcalidrawTextElement {
|
||||
return element.type === "text";
|
||||
}
|
||||
|
@ -8,14 +8,14 @@ class SceneHistory {
|
||||
|
||||
generateCurrentEntry(
|
||||
appState: Partial<AppState>,
|
||||
elements: readonly ExcalidrawElement[]
|
||||
elements: readonly ExcalidrawElement[],
|
||||
) {
|
||||
return JSON.stringify({
|
||||
appState,
|
||||
elements: elements.map(({ shape, ...element }) => ({
|
||||
...element,
|
||||
isSelected: false
|
||||
}))
|
||||
isSelected: false,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ export const languages = [
|
||||
{ lng: "en", label: "English" },
|
||||
{ lng: "es", label: "Español" },
|
||||
{ lng: "fr", label: "Français" },
|
||||
{ lng: "pt", label: "Português" }
|
||||
{ lng: "pt", label: "Português" },
|
||||
];
|
||||
|
||||
i18n
|
||||
@ -28,7 +28,7 @@ i18n
|
||||
.init({
|
||||
fallbackLng,
|
||||
react: { useSuspense: false },
|
||||
load: "languageOnly"
|
||||
load: "languageOnly",
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
@ -17,7 +17,7 @@ const elements = [
|
||||
roughness: 1,
|
||||
opacity: 100,
|
||||
isSelected: false,
|
||||
seed: 749612521
|
||||
seed: 749612521,
|
||||
},
|
||||
{
|
||||
id: "7W-iw5pEBPTU3eaCaLtFo",
|
||||
@ -33,7 +33,7 @@ const elements = [
|
||||
roughness: 1,
|
||||
opacity: 100,
|
||||
isSelected: false,
|
||||
seed: 952056308
|
||||
seed: 952056308,
|
||||
},
|
||||
{
|
||||
id: "kqKI231mvTrcsYo2DkUsR",
|
||||
@ -52,8 +52,8 @@ const elements = [
|
||||
seed: 1683771448,
|
||||
text: "test",
|
||||
font: "20px Virgil",
|
||||
baseline: 22
|
||||
}
|
||||
baseline: 22,
|
||||
},
|
||||
];
|
||||
|
||||
registerFont("./public/FG_Virgil.ttf", { family: "Virgil" });
|
||||
@ -62,9 +62,9 @@ const canvas = getExportCanvasPreview(
|
||||
{
|
||||
exportBackground: true,
|
||||
viewBackgroundColor: "#ffffff",
|
||||
scale: 1
|
||||
scale: 1,
|
||||
},
|
||||
createCanvas
|
||||
createCanvas,
|
||||
);
|
||||
|
||||
const fs = require("fs");
|
||||
|
170
src/index.tsx
170
src/index.tsx
@ -15,7 +15,7 @@ import {
|
||||
getElementAbsoluteCoords,
|
||||
getCursorForResizingElement,
|
||||
getPerfectElementSize,
|
||||
resizePerfectLineForNWHandler
|
||||
resizePerfectLineForNWHandler,
|
||||
} from "./element";
|
||||
import {
|
||||
clearSelection,
|
||||
@ -31,7 +31,7 @@ import {
|
||||
hasStroke,
|
||||
hasText,
|
||||
exportCanvas,
|
||||
importFromBackend
|
||||
importFromBackend,
|
||||
} from "./scene";
|
||||
|
||||
import { renderScene } from "./renderer";
|
||||
@ -71,7 +71,7 @@ import {
|
||||
actionLoadScene,
|
||||
actionSaveScene,
|
||||
actionCopyStyles,
|
||||
actionPasteStyles
|
||||
actionPasteStyles,
|
||||
} from "./actions";
|
||||
import { Action, ActionResult } from "./actions/types";
|
||||
import { getDefaultAppState } from "./appState";
|
||||
@ -100,7 +100,7 @@ const ELEMENT_TRANSLATE_AMOUNT = 1;
|
||||
const TEXT_TO_CENTER_SNAP_THRESHOLD = 30;
|
||||
const CURSOR_TYPE = {
|
||||
TEXT: "text",
|
||||
CROSSHAIR: "crosshair"
|
||||
CROSSHAIR: "crosshair",
|
||||
};
|
||||
|
||||
let lastCanvasWidth = -1;
|
||||
@ -110,7 +110,7 @@ let lastMouseUp: ((e: any) => void) | null = null;
|
||||
|
||||
export function viewportCoordsToSceneCoords(
|
||||
{ clientX, clientY }: { clientX: number; clientY: number },
|
||||
{ scrollX, scrollY }: { scrollX: number; scrollY: number }
|
||||
{ scrollX, scrollY }: { scrollX: number; scrollY: number },
|
||||
) {
|
||||
const x = clientX - CANVAS_WINDOW_OFFSET_LEFT - scrollX;
|
||||
const y = clientY - CANVAS_WINDOW_OFFSET_TOP - scrollY;
|
||||
@ -118,7 +118,7 @@ export function viewportCoordsToSceneCoords(
|
||||
}
|
||||
|
||||
function pickAppStatePropertiesForHistory(
|
||||
appState: AppState
|
||||
appState: AppState,
|
||||
): Partial<AppState> {
|
||||
return {
|
||||
exportBackground: appState.exportBackground,
|
||||
@ -126,7 +126,7 @@ function pickAppStatePropertiesForHistory(
|
||||
currentItemBackgroundColor: appState.currentItemBackgroundColor,
|
||||
currentItemFont: appState.currentItemFont,
|
||||
viewBackgroundColor: appState.viewBackgroundColor,
|
||||
name: appState.name
|
||||
name: appState.name,
|
||||
};
|
||||
}
|
||||
|
||||
@ -186,8 +186,8 @@ export class App extends React.Component<any, AppState> {
|
||||
JSON.stringify(
|
||||
elements
|
||||
.filter(element => element.isSelected)
|
||||
.map(({ shape, ...el }) => el)
|
||||
)
|
||||
.map(({ shape, ...el }) => el),
|
||||
),
|
||||
);
|
||||
elements = deleteSelectedElements(elements);
|
||||
this.forceUpdate();
|
||||
@ -200,8 +200,8 @@ export class App extends React.Component<any, AppState> {
|
||||
JSON.stringify(
|
||||
elements
|
||||
.filter(element => element.isSelected)
|
||||
.map(({ shape, ...el }) => el)
|
||||
)
|
||||
.map(({ shape, ...el }) => el),
|
||||
),
|
||||
);
|
||||
e.preventDefault();
|
||||
};
|
||||
@ -257,7 +257,7 @@ export class App extends React.Component<any, AppState> {
|
||||
document.removeEventListener(
|
||||
"mousemove",
|
||||
this.getCurrentCursorPosition,
|
||||
false
|
||||
false,
|
||||
);
|
||||
window.removeEventListener("resize", this.onResize, false);
|
||||
window.removeEventListener("unload", this.onUnload, false);
|
||||
@ -354,7 +354,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const text = JSON.stringify(
|
||||
elements
|
||||
.filter(element => element.isSelected)
|
||||
.map(({ shape, ...el }) => el)
|
||||
.map(({ shape, ...el }) => el),
|
||||
);
|
||||
navigator.clipboard.writeText(text);
|
||||
}
|
||||
@ -393,7 +393,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
|
||||
{(hasBackground(elements) ||
|
||||
@ -404,7 +404,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
|
||||
{this.actionManager.renderAction(
|
||||
@ -412,7 +412,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
<hr />
|
||||
</>
|
||||
@ -426,7 +426,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
|
||||
{this.actionManager.renderAction(
|
||||
@ -434,7 +434,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
<hr />
|
||||
</>
|
||||
@ -447,7 +447,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
|
||||
{this.actionManager.renderAction(
|
||||
@ -455,7 +455,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
<hr />
|
||||
</>
|
||||
@ -466,7 +466,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
|
||||
{this.actionManager.renderAction(
|
||||
@ -474,7 +474,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
</div>
|
||||
</Island>
|
||||
@ -533,14 +533,14 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
{this.actionManager.renderAction(
|
||||
"saveScene",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
<ExportDialog
|
||||
elements={elements}
|
||||
@ -553,7 +553,7 @@ export class App extends React.Component<any, AppState> {
|
||||
exportBackground: this.state.exportBackground,
|
||||
name: this.state.name,
|
||||
viewBackgroundColor: this.state.viewBackgroundColor,
|
||||
scale
|
||||
scale,
|
||||
});
|
||||
}}
|
||||
onExportToClipboard={(exportedElements, scale) => {
|
||||
@ -562,7 +562,7 @@ export class App extends React.Component<any, AppState> {
|
||||
exportBackground: this.state.exportBackground,
|
||||
name: this.state.name,
|
||||
viewBackgroundColor: this.state.viewBackgroundColor,
|
||||
scale
|
||||
scale,
|
||||
});
|
||||
}}
|
||||
onExportToBackend={exportedElements => {
|
||||
@ -571,10 +571,10 @@ export class App extends React.Component<any, AppState> {
|
||||
"backend",
|
||||
exportedElements.map(element => ({
|
||||
...element,
|
||||
isSelected: false
|
||||
isSelected: false,
|
||||
})),
|
||||
this.canvas,
|
||||
this.state
|
||||
this.state,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@ -583,7 +583,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
</Stack.Row>
|
||||
{this.actionManager.renderAction(
|
||||
@ -591,7 +591,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
t
|
||||
t,
|
||||
)}
|
||||
</Stack.Col>
|
||||
);
|
||||
@ -626,7 +626,7 @@ export class App extends React.Component<any, AppState> {
|
||||
id="canvas"
|
||||
style={{
|
||||
width: canvasWidth,
|
||||
height: canvasHeight
|
||||
height: canvasHeight,
|
||||
}}
|
||||
width={canvasWidth * window.devicePixelRatio}
|
||||
height={canvasHeight * window.devicePixelRatio}
|
||||
@ -641,7 +641,7 @@ export class App extends React.Component<any, AppState> {
|
||||
}
|
||||
if (canvas) {
|
||||
canvas.addEventListener("wheel", this.handleWheel, {
|
||||
passive: false
|
||||
passive: false,
|
||||
});
|
||||
this.removeWheelEventListener = () =>
|
||||
canvas.removeEventListener("wheel", this.handleWheel);
|
||||
@ -670,18 +670,18 @@ export class App extends React.Component<any, AppState> {
|
||||
options: [
|
||||
navigator.clipboard && {
|
||||
label: t("labels.paste"),
|
||||
action: () => this.pasteFromClipboard()
|
||||
action: () => this.pasteFromClipboard(),
|
||||
},
|
||||
...this.actionManager.getContextMenuItems(
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
action => this.canvasOnlyActions.includes(action),
|
||||
t
|
||||
)
|
||||
t,
|
||||
),
|
||||
],
|
||||
top: e.clientY,
|
||||
left: e.clientX
|
||||
left: e.clientX,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -696,22 +696,22 @@ export class App extends React.Component<any, AppState> {
|
||||
options: [
|
||||
navigator.clipboard && {
|
||||
label: t("labels.copy"),
|
||||
action: this.copyToClipboard
|
||||
action: this.copyToClipboard,
|
||||
},
|
||||
navigator.clipboard && {
|
||||
label: t("labels.paste"),
|
||||
action: () => this.pasteFromClipboard()
|
||||
action: () => this.pasteFromClipboard(),
|
||||
},
|
||||
...this.actionManager.getContextMenuItems(
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
action => !this.canvasOnlyActions.includes(action),
|
||||
t
|
||||
)
|
||||
t,
|
||||
),
|
||||
],
|
||||
top: e.clientY,
|
||||
left: e.clientX
|
||||
left: e.clientX,
|
||||
});
|
||||
}}
|
||||
onMouseDown={e => {
|
||||
@ -733,7 +733,7 @@ export class App extends React.Component<any, AppState> {
|
||||
lastY = e.clientY;
|
||||
this.setState(state => ({
|
||||
scrollX: state.scrollX - deltaX,
|
||||
scrollY: state.scrollY - deltaY
|
||||
scrollY: state.scrollY - deltaY,
|
||||
}));
|
||||
};
|
||||
const onMouseUp = (lastMouseUp = (e: MouseEvent) => {
|
||||
@ -743,7 +743,7 @@ export class App extends React.Component<any, AppState> {
|
||||
window.removeEventListener("mouseup", onMouseUp);
|
||||
});
|
||||
window.addEventListener("mousemove", onMouseMove, {
|
||||
passive: true
|
||||
passive: true,
|
||||
});
|
||||
window.addEventListener("mouseup", onMouseUp);
|
||||
return;
|
||||
@ -763,7 +763,7 @@ export class App extends React.Component<any, AppState> {
|
||||
// Handle scrollbars dragging
|
||||
const {
|
||||
isOverHorizontalScrollBar,
|
||||
isOverVerticalScrollBar
|
||||
isOverVerticalScrollBar,
|
||||
} = isOverScrollBars(
|
||||
elements,
|
||||
e.clientX - CANVAS_WINDOW_OFFSET_LEFT,
|
||||
@ -771,7 +771,7 @@ export class App extends React.Component<any, AppState> {
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
this.state.scrollX,
|
||||
this.state.scrollY
|
||||
this.state.scrollY,
|
||||
);
|
||||
|
||||
const { x, y } = viewportCoordsToSceneCoords(e, this.state);
|
||||
@ -785,7 +785,7 @@ export class App extends React.Component<any, AppState> {
|
||||
"hachure",
|
||||
1,
|
||||
1,
|
||||
100
|
||||
100,
|
||||
);
|
||||
|
||||
if (isTextElement(element)) {
|
||||
@ -802,16 +802,16 @@ export class App extends React.Component<any, AppState> {
|
||||
const resizeElement = getElementWithResizeHandler(
|
||||
elements,
|
||||
{ x, y },
|
||||
this.state
|
||||
this.state,
|
||||
);
|
||||
this.setState({
|
||||
resizingElement: resizeElement ? resizeElement.element : null
|
||||
resizingElement: resizeElement ? resizeElement.element : null,
|
||||
});
|
||||
|
||||
if (resizeElement) {
|
||||
resizeHandle = resizeElement.resizeHandle;
|
||||
document.documentElement.style.cursor = getCursorForResizingElement(
|
||||
resizeElement
|
||||
resizeElement,
|
||||
);
|
||||
isResizingElements = true;
|
||||
} else {
|
||||
@ -837,7 +837,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements = [
|
||||
...elements.map(element => ({
|
||||
...element,
|
||||
isSelected: false
|
||||
isSelected: false,
|
||||
})),
|
||||
...elements
|
||||
.filter(element => element.isSelected)
|
||||
@ -845,7 +845,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const newElement = duplicateElement(element);
|
||||
newElement.isSelected = true;
|
||||
return newElement;
|
||||
})
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -860,7 +860,7 @@ export class App extends React.Component<any, AppState> {
|
||||
if (!e.altKey) {
|
||||
const snappedToCenterPosition = this.getTextWysiwygSnappedToCenterPosition(
|
||||
x,
|
||||
y
|
||||
y,
|
||||
);
|
||||
if (snappedToCenterPosition) {
|
||||
element.x = snappedToCenterPosition.elementCenterX;
|
||||
@ -884,22 +884,22 @@ export class App extends React.Component<any, AppState> {
|
||||
...newTextElement(
|
||||
element,
|
||||
text,
|
||||
this.state.currentItemFont
|
||||
this.state.currentItemFont,
|
||||
),
|
||||
isSelected: true
|
||||
}
|
||||
isSelected: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
this.setState({
|
||||
draggingElement: null,
|
||||
editingElement: null,
|
||||
elementType: "selection"
|
||||
elementType: "selection",
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
this.setState({
|
||||
elementType: "selection",
|
||||
editingElement: element
|
||||
editingElement: element,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -993,7 +993,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const { width, height } = getPerfectElementSize(
|
||||
element.type,
|
||||
x - element.x,
|
||||
y - element.y
|
||||
y - element.y,
|
||||
);
|
||||
element.width = width;
|
||||
element.height = height;
|
||||
@ -1023,7 +1023,7 @@ export class App extends React.Component<any, AppState> {
|
||||
}
|
||||
|
||||
document.documentElement.style.cursor = getCursorForResizingElement(
|
||||
{ element, resizeHandle }
|
||||
{ element, resizeHandle },
|
||||
);
|
||||
el.x = element.x;
|
||||
el.y = element.y;
|
||||
@ -1078,11 +1078,11 @@ export class App extends React.Component<any, AppState> {
|
||||
if (e.shiftKey) {
|
||||
let {
|
||||
width: newWidth,
|
||||
height: newHeight
|
||||
height: newHeight,
|
||||
} = getPerfectElementSize(
|
||||
this.state.elementType,
|
||||
width,
|
||||
height
|
||||
height,
|
||||
);
|
||||
draggingElement.width = newWidth;
|
||||
draggingElement.height = newHeight;
|
||||
@ -1099,7 +1099,7 @@ export class App extends React.Component<any, AppState> {
|
||||
}
|
||||
const elementsWithinSelection = getElementsWithinSelection(
|
||||
elements,
|
||||
draggingElement
|
||||
draggingElement,
|
||||
);
|
||||
elementsWithinSelection.forEach(element => {
|
||||
element.isSelected = true;
|
||||
@ -1115,7 +1115,7 @@ export class App extends React.Component<any, AppState> {
|
||||
draggingElement,
|
||||
resizingElement,
|
||||
elementType,
|
||||
elementLocked
|
||||
elementLocked,
|
||||
} = this.state;
|
||||
|
||||
lastMouseUp = null;
|
||||
@ -1130,7 +1130,7 @@ export class App extends React.Component<any, AppState> {
|
||||
// remove invisible element which was added in onMouseDown
|
||||
elements = elements.slice(0, -1);
|
||||
this.setState({
|
||||
draggingElement: null
|
||||
draggingElement: null,
|
||||
});
|
||||
this.forceUpdate();
|
||||
return;
|
||||
@ -1179,7 +1179,7 @@ export class App extends React.Component<any, AppState> {
|
||||
|
||||
this.setState({
|
||||
draggingElement: null,
|
||||
elementType: "selection"
|
||||
elementType: "selection",
|
||||
});
|
||||
}
|
||||
|
||||
@ -1214,10 +1214,10 @@ export class App extends React.Component<any, AppState> {
|
||||
"hachure",
|
||||
1,
|
||||
1,
|
||||
100
|
||||
100,
|
||||
),
|
||||
"", // default text
|
||||
this.state.currentItemFont // default font
|
||||
this.state.currentItemFont, // default font
|
||||
);
|
||||
|
||||
this.setState({ editingElement: element });
|
||||
@ -1227,7 +1227,7 @@ export class App extends React.Component<any, AppState> {
|
||||
|
||||
if (elementAtPosition && isTextElement(elementAtPosition)) {
|
||||
elements = elements.filter(
|
||||
element => element.id !== elementAtPosition.id
|
||||
element => element.id !== elementAtPosition.id,
|
||||
);
|
||||
this.forceUpdate();
|
||||
|
||||
@ -1248,7 +1248,7 @@ export class App extends React.Component<any, AppState> {
|
||||
} else if (!e.altKey) {
|
||||
const snappedToCenterPosition = this.getTextWysiwygSnappedToCenterPosition(
|
||||
x,
|
||||
y
|
||||
y,
|
||||
);
|
||||
|
||||
if (snappedToCenterPosition) {
|
||||
@ -1273,16 +1273,16 @@ export class App extends React.Component<any, AppState> {
|
||||
// we need to recreate the element to update dimensions &
|
||||
// position
|
||||
...newTextElement(element, text, element.font),
|
||||
isSelected: true
|
||||
}
|
||||
isSelected: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
this.setState({
|
||||
draggingElement: null,
|
||||
editingElement: null,
|
||||
elementType: "selection"
|
||||
elementType: "selection",
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
onMouseMove={e => {
|
||||
@ -1296,11 +1296,11 @@ export class App extends React.Component<any, AppState> {
|
||||
const resizeElement = getElementWithResizeHandler(
|
||||
elements,
|
||||
{ x, y },
|
||||
this.state
|
||||
this.state,
|
||||
);
|
||||
if (resizeElement && resizeElement.resizeHandle) {
|
||||
document.documentElement.style.cursor = getCursorForResizingElement(
|
||||
resizeElement
|
||||
resizeElement,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -1327,7 +1327,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const { deltaX, deltaY } = e;
|
||||
this.setState(state => ({
|
||||
scrollX: state.scrollX - deltaX,
|
||||
scrollY: state.scrollY - deltaY
|
||||
scrollY: state.scrollY - deltaY,
|
||||
}));
|
||||
};
|
||||
|
||||
@ -1384,7 +1384,7 @@ export class App extends React.Component<any, AppState> {
|
||||
duplicate.x += dx - minX;
|
||||
duplicate.y += dy - minY;
|
||||
return duplicate;
|
||||
})
|
||||
}),
|
||||
];
|
||||
this.forceUpdate();
|
||||
}
|
||||
@ -1399,7 +1399,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elementClickedInside.y + elementClickedInside.height / 2;
|
||||
const distanceToCenter = Math.hypot(
|
||||
x - elementCenterX,
|
||||
y - elementCenterY
|
||||
y - elementCenterY,
|
||||
);
|
||||
const isSnappedToCenter =
|
||||
distanceToCenter < TEXT_TO_CENTER_SNAP_THRESHOLD;
|
||||
@ -1427,15 +1427,15 @@ export class App extends React.Component<any, AppState> {
|
||||
renderScene(elements, this.rc!, this.canvas!, {
|
||||
scrollX: this.state.scrollX,
|
||||
scrollY: this.state.scrollY,
|
||||
viewBackgroundColor: this.state.viewBackgroundColor
|
||||
viewBackgroundColor: this.state.viewBackgroundColor,
|
||||
});
|
||||
this.saveDebounced();
|
||||
if (history.isRecording()) {
|
||||
history.pushEntry(
|
||||
history.generateCurrentEntry(
|
||||
pickAppStatePropertiesForHistory(this.state),
|
||||
elements
|
||||
)
|
||||
elements,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1453,7 +1453,7 @@ class TopErrorBoundary extends React.Component {
|
||||
return {
|
||||
hasError: true,
|
||||
localStorage: JSON.stringify({ ...localStorage }),
|
||||
stack: error.stack
|
||||
stack: error.stack,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1471,7 +1471,7 @@ class TopErrorBoundary extends React.Component {
|
||||
} catch {}
|
||||
|
||||
window.open(
|
||||
`https://github.com/excalidraw/excalidraw/issues/new?body=${body}`
|
||||
`https://github.com/excalidraw/excalidraw/issues/new?body=${body}`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1539,5 +1539,5 @@ ReactDOM.render(
|
||||
<TopErrorBoundary>
|
||||
<AppWithTrans />
|
||||
</TopErrorBoundary>,
|
||||
rootElement
|
||||
rootElement,
|
||||
);
|
||||
|
@ -11,7 +11,7 @@ export const KEYS = {
|
||||
return /Mac|iPod|iPhone|iPad/.test(window.navigator.platform)
|
||||
? "metaKey"
|
||||
: "ctrlKey";
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export function isArrowKey(keyCode: string) {
|
||||
|
@ -5,7 +5,7 @@ export function distanceBetweenPointAndSegment(
|
||||
x1: number,
|
||||
y1: number,
|
||||
x2: number,
|
||||
y2: number
|
||||
y2: number,
|
||||
) {
|
||||
const A = x - x1;
|
||||
const B = y - y1;
|
||||
@ -42,13 +42,13 @@ export function rotate(
|
||||
y1: number,
|
||||
x2: number,
|
||||
y2: number,
|
||||
angle: number
|
||||
angle: number,
|
||||
) {
|
||||
// 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
|
||||
// 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
|
||||
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
||||
return [
|
||||
(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2,
|
||||
(x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2
|
||||
(x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2,
|
||||
];
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { isTextElement } from "../element/typeChecks";
|
||||
import {
|
||||
getDiamondPoints,
|
||||
getArrowPoints,
|
||||
getLinePoints
|
||||
getLinePoints,
|
||||
} from "../element/bounds";
|
||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||
import { Drawable } from "roughjs/bin/core";
|
||||
@ -11,7 +11,7 @@ import { Drawable } from "roughjs/bin/core";
|
||||
export function renderElement(
|
||||
element: ExcalidrawElement,
|
||||
rc: RoughCanvas,
|
||||
context: CanvasRenderingContext2D
|
||||
context: CanvasRenderingContext2D,
|
||||
) {
|
||||
const generator = rc.generator;
|
||||
if (element.type === "selection") {
|
||||
@ -30,7 +30,7 @@ export function renderElement(
|
||||
fillStyle: element.fillStyle,
|
||||
strokeWidth: element.strokeWidth,
|
||||
roughness: element.roughness,
|
||||
seed: element.seed
|
||||
seed: element.seed,
|
||||
});
|
||||
}
|
||||
|
||||
@ -47,14 +47,14 @@ export function renderElement(
|
||||
bottomX,
|
||||
bottomY,
|
||||
leftX,
|
||||
leftY
|
||||
leftY,
|
||||
] = getDiamondPoints(element);
|
||||
element.shape = generator.polygon(
|
||||
[
|
||||
[topX, topY],
|
||||
[rightX, rightY],
|
||||
[bottomX, bottomY],
|
||||
[leftX, leftY]
|
||||
[leftX, leftY],
|
||||
],
|
||||
{
|
||||
stroke: element.strokeColor,
|
||||
@ -65,8 +65,8 @@ export function renderElement(
|
||||
fillStyle: element.fillStyle,
|
||||
strokeWidth: element.strokeWidth,
|
||||
roughness: element.roughness,
|
||||
seed: element.seed
|
||||
}
|
||||
seed: element.seed,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -90,8 +90,8 @@ export function renderElement(
|
||||
strokeWidth: element.strokeWidth,
|
||||
roughness: element.roughness,
|
||||
seed: element.seed,
|
||||
curveFitting: 1
|
||||
}
|
||||
curveFitting: 1,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ export function renderElement(
|
||||
stroke: element.strokeColor,
|
||||
strokeWidth: element.strokeWidth,
|
||||
roughness: element.roughness,
|
||||
seed: element.seed
|
||||
seed: element.seed,
|
||||
};
|
||||
|
||||
if (!element.shape) {
|
||||
@ -114,7 +114,7 @@ export function renderElement(
|
||||
// -----
|
||||
generator.line(x1, y1, x2, y2, options),
|
||||
// /
|
||||
generator.line(x4, y4, x2, y2, options)
|
||||
generator.line(x4, y4, x2, y2, options),
|
||||
];
|
||||
}
|
||||
|
||||
@ -128,7 +128,7 @@ export function renderElement(
|
||||
stroke: element.strokeColor,
|
||||
strokeWidth: element.strokeWidth,
|
||||
roughness: element.roughness,
|
||||
seed: element.seed
|
||||
seed: element.seed,
|
||||
};
|
||||
|
||||
if (!element.shape) {
|
||||
@ -147,7 +147,7 @@ export function renderElement(
|
||||
context.fillText(
|
||||
element.text,
|
||||
0,
|
||||
element.baseline || element.actualBoundingBoxAscent || 0
|
||||
element.baseline || element.actualBoundingBoxAscent || 0,
|
||||
);
|
||||
context.fillStyle = fillStyle;
|
||||
context.font = font;
|
||||
|
@ -8,7 +8,7 @@ import { SceneState } from "../scene/types";
|
||||
import {
|
||||
getScrollBars,
|
||||
SCROLLBAR_COLOR,
|
||||
SCROLLBAR_WIDTH
|
||||
SCROLLBAR_WIDTH,
|
||||
} from "../scene/scrollbars";
|
||||
|
||||
import { renderElement } from "./renderElement";
|
||||
@ -23,13 +23,13 @@ export function renderScene(
|
||||
offsetX,
|
||||
offsetY,
|
||||
renderScrollbars = true,
|
||||
renderSelection = true
|
||||
renderSelection = true,
|
||||
}: {
|
||||
offsetX?: number;
|
||||
offsetY?: number;
|
||||
renderScrollbars?: boolean;
|
||||
renderSelection?: boolean;
|
||||
} = {}
|
||||
} = {},
|
||||
) {
|
||||
if (!canvas) return;
|
||||
const context = canvas.getContext("2d")!;
|
||||
@ -53,7 +53,7 @@ export function renderScene(
|
||||
sceneState = {
|
||||
...sceneState,
|
||||
scrollX: typeof offsetX === "number" ? offsetX : sceneState.scrollX,
|
||||
scrollY: typeof offsetY === "number" ? offsetY : sceneState.scrollY
|
||||
scrollY: typeof offsetY === "number" ? offsetY : sceneState.scrollY,
|
||||
};
|
||||
|
||||
elements.forEach(element => {
|
||||
@ -65,19 +65,19 @@ export function renderScene(
|
||||
// If canvas is scaled for high pixelDeviceRatio width and height
|
||||
// setted in the `style` attribute
|
||||
parseInt(canvas.style.width) || canvas.width,
|
||||
parseInt(canvas.style.height) || canvas.height
|
||||
parseInt(canvas.style.height) || canvas.height,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
context.translate(
|
||||
element.x + sceneState.scrollX,
|
||||
element.y + sceneState.scrollY
|
||||
element.y + sceneState.scrollY,
|
||||
);
|
||||
renderElement(element, rc, context);
|
||||
context.translate(
|
||||
-element.x - sceneState.scrollX,
|
||||
-element.y - sceneState.scrollY
|
||||
-element.y - sceneState.scrollY,
|
||||
);
|
||||
});
|
||||
|
||||
@ -91,7 +91,7 @@ export function renderScene(
|
||||
elementX1,
|
||||
elementY1,
|
||||
elementX2,
|
||||
elementY2
|
||||
elementY2,
|
||||
] = getElementAbsoluteCoords(element);
|
||||
const lineDash = context.getLineDash();
|
||||
context.setLineDash([8, 4]);
|
||||
@ -99,7 +99,7 @@ export function renderScene(
|
||||
elementX1 - margin + sceneState.scrollX,
|
||||
elementY1 - margin + sceneState.scrollY,
|
||||
elementX2 - elementX1 + margin * 2,
|
||||
elementY2 - elementY1 + margin * 2
|
||||
elementY2 - elementY1 + margin * 2,
|
||||
);
|
||||
context.setLineDash(lineDash);
|
||||
});
|
||||
@ -118,7 +118,7 @@ export function renderScene(
|
||||
context.canvas.width / window.devicePixelRatio,
|
||||
context.canvas.height / window.devicePixelRatio,
|
||||
sceneState.scrollX,
|
||||
sceneState.scrollY
|
||||
sceneState.scrollY,
|
||||
);
|
||||
|
||||
const strokeStyle = context.strokeStyle;
|
||||
@ -132,7 +132,7 @@ export function renderScene(
|
||||
scrollBar.y,
|
||||
scrollBar.width,
|
||||
scrollBar.height,
|
||||
SCROLLBAR_WIDTH / 2
|
||||
SCROLLBAR_WIDTH / 2,
|
||||
);
|
||||
});
|
||||
context.strokeStyle = strokeStyle;
|
||||
@ -145,7 +145,7 @@ function isVisibleElement(
|
||||
scrollX: number,
|
||||
scrollY: number,
|
||||
canvasWidth: number,
|
||||
canvasHeight: number
|
||||
canvasHeight: number,
|
||||
) {
|
||||
let [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
||||
x1 += scrollX;
|
||||
|
@ -14,7 +14,7 @@ export function roundRect(
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
radius: number
|
||||
radius: number,
|
||||
) {
|
||||
context.beginPath();
|
||||
context.moveTo(x + radius, y);
|
||||
@ -25,7 +25,7 @@ export function roundRect(
|
||||
x + width,
|
||||
y + height,
|
||||
x + width - radius,
|
||||
y + height
|
||||
y + height,
|
||||
);
|
||||
context.lineTo(x + radius, y + height);
|
||||
context.quadraticCurveTo(x, y + height, x, y + height - radius);
|
||||
|
@ -8,7 +8,7 @@ export const hasBackground = (elements: readonly ExcalidrawElement[]) =>
|
||||
element.isSelected &&
|
||||
(element.type === "rectangle" ||
|
||||
element.type === "ellipse" ||
|
||||
element.type === "diamond")
|
||||
element.type === "diamond"),
|
||||
);
|
||||
|
||||
export const hasStroke = (elements: readonly ExcalidrawElement[]) =>
|
||||
@ -19,7 +19,7 @@ export const hasStroke = (elements: readonly ExcalidrawElement[]) =>
|
||||
element.type === "ellipse" ||
|
||||
element.type === "diamond" ||
|
||||
element.type === "arrow" ||
|
||||
element.type === "line")
|
||||
element.type === "line"),
|
||||
);
|
||||
|
||||
export const hasText = (elements: readonly ExcalidrawElement[]) =>
|
||||
@ -28,7 +28,7 @@ export const hasText = (elements: readonly ExcalidrawElement[]) =>
|
||||
export function getElementAtPosition(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
x: number,
|
||||
y: number
|
||||
y: number,
|
||||
) {
|
||||
let hitElement = null;
|
||||
// We need to to hit testing from front (end of the array) to back (beginning of the array)
|
||||
@ -45,7 +45,7 @@ export function getElementAtPosition(
|
||||
export function getElementContainingPosition(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
x: number,
|
||||
y: number
|
||||
y: number,
|
||||
) {
|
||||
let hitElement = null;
|
||||
// We need to to hit testing from front (end of the array) to back (beginning of the array)
|
||||
|
@ -27,19 +27,19 @@ interface DataState {
|
||||
|
||||
export function serializeAsJSON(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState?: AppState
|
||||
appState?: AppState,
|
||||
): string {
|
||||
return JSON.stringify({
|
||||
version: 1,
|
||||
source: window.location.origin,
|
||||
elements: elements.map(({ shape, ...el }) => el),
|
||||
appState: appState || getDefaultAppState()
|
||||
appState: appState || getDefaultAppState(),
|
||||
});
|
||||
}
|
||||
|
||||
export async function saveAsJSON(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState
|
||||
appState: AppState,
|
||||
) {
|
||||
const serialized = serializeAsJSON(elements, appState);
|
||||
|
||||
@ -48,9 +48,9 @@ export async function saveAsJSON(
|
||||
new Blob([serialized], { type: "application/json" }),
|
||||
{
|
||||
fileName: name,
|
||||
description: "Excalidraw file"
|
||||
description: "Excalidraw file",
|
||||
},
|
||||
(window as any).handle
|
||||
(window as any).handle,
|
||||
);
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ export async function loadFromJSON() {
|
||||
const blob = await fileOpen({
|
||||
description: "Excalidraw files",
|
||||
extensions: ["json"],
|
||||
mimeTypes: ["application/json"]
|
||||
mimeTypes: ["application/json"],
|
||||
});
|
||||
if (blob.handle) {
|
||||
(window as any).handle = blob.handle;
|
||||
@ -101,12 +101,12 @@ export async function loadFromJSON() {
|
||||
|
||||
export async function exportToBackend(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState
|
||||
appState: AppState,
|
||||
) {
|
||||
const response = await fetch(BACKEND_POST, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: serializeAsJSON(elements, appState)
|
||||
body: serializeAsJSON(elements, appState),
|
||||
});
|
||||
const json = await response.json();
|
||||
if (json.id) {
|
||||
@ -117,8 +117,8 @@ export async function exportToBackend(
|
||||
window.alert(
|
||||
i18n.t("alerts.copiedToClipboard", {
|
||||
url: url.toString(),
|
||||
interpolation: { escapeValue: false }
|
||||
})
|
||||
interpolation: { escapeValue: false },
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
window.alert(i18n.t("alerts.couldNotCreateShareableLink"));
|
||||
@ -129,7 +129,7 @@ export async function importFromBackend(id: string | null) {
|
||||
let elements: readonly ExcalidrawElement[] = [];
|
||||
let appState: AppState = getDefaultAppState();
|
||||
const response = await fetch(`${BACKEND_GET}${id}.json`).then(data =>
|
||||
data.clone().json()
|
||||
data.clone().json(),
|
||||
);
|
||||
if (response != null) {
|
||||
try {
|
||||
@ -152,14 +152,14 @@ export async function exportCanvas(
|
||||
exportPadding = 10,
|
||||
viewBackgroundColor,
|
||||
name,
|
||||
scale = 1
|
||||
scale = 1,
|
||||
}: {
|
||||
exportBackground: boolean;
|
||||
exportPadding?: number;
|
||||
viewBackgroundColor: string;
|
||||
name: string;
|
||||
scale?: number;
|
||||
}
|
||||
},
|
||||
) {
|
||||
if (!elements.length)
|
||||
return window.alert(i18n.t("alerts.cannotExportEmptyCanvas"));
|
||||
@ -169,7 +169,7 @@ export async function exportCanvas(
|
||||
exportBackground,
|
||||
viewBackgroundColor,
|
||||
exportPadding,
|
||||
scale
|
||||
scale,
|
||||
});
|
||||
tempCanvas.style.display = "none";
|
||||
document.body.appendChild(tempCanvas);
|
||||
@ -180,7 +180,7 @@ export async function exportCanvas(
|
||||
if (blob) {
|
||||
await fileSave(blob, {
|
||||
fileName: fileName,
|
||||
description: "Excalidraw image"
|
||||
description: "Excalidraw image",
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -190,7 +190,7 @@ export async function exportCanvas(
|
||||
tempCanvas.toBlob(async function(blob: any) {
|
||||
try {
|
||||
await navigator.clipboard.write([
|
||||
new window.ClipboardItem({ "image/png": blob })
|
||||
new window.ClipboardItem({ "image/png": blob }),
|
||||
]);
|
||||
} catch (err) {
|
||||
window.alert(errorMsg);
|
||||
@ -213,7 +213,7 @@ export async function exportCanvas(
|
||||
|
||||
function restore(
|
||||
savedElements: readonly ExcalidrawElement[],
|
||||
savedState: AppState
|
||||
savedState: AppState,
|
||||
): DataState {
|
||||
return {
|
||||
elements: savedElements.map(element => ({
|
||||
@ -225,9 +225,9 @@ function restore(
|
||||
opacity:
|
||||
element.opacity === null || element.opacity === undefined
|
||||
? 100
|
||||
: element.opacity
|
||||
: element.opacity,
|
||||
})),
|
||||
appState: savedState
|
||||
appState: savedState,
|
||||
};
|
||||
}
|
||||
|
||||
@ -239,7 +239,7 @@ export function restoreFromLocalStorage() {
|
||||
if (savedElements) {
|
||||
try {
|
||||
elements = JSON.parse(savedElements).map(
|
||||
({ shape, ...element }: ExcalidrawElement) => element
|
||||
({ shape, ...element }: ExcalidrawElement) => element,
|
||||
);
|
||||
} catch (e) {
|
||||
// Do nothing because elements array is already empty
|
||||
@ -260,7 +260,7 @@ export function restoreFromLocalStorage() {
|
||||
|
||||
export function saveToLocalStorage(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
state: AppState
|
||||
state: AppState,
|
||||
) {
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(elements));
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY_STATE, JSON.stringify(state));
|
||||
|
@ -9,7 +9,7 @@ export function getExportCanvasPreview(
|
||||
exportBackground,
|
||||
exportPadding = 10,
|
||||
viewBackgroundColor,
|
||||
scale = 1
|
||||
scale = 1,
|
||||
}: {
|
||||
exportBackground: boolean;
|
||||
exportPadding?: number;
|
||||
@ -18,13 +18,13 @@ export function getExportCanvasPreview(
|
||||
},
|
||||
createCanvas: (width: number, height: number) => any = function(
|
||||
width,
|
||||
height
|
||||
height,
|
||||
) {
|
||||
const tempCanvas = document.createElement("canvas");
|
||||
tempCanvas.width = width * scale;
|
||||
tempCanvas.height = height * scale;
|
||||
return tempCanvas;
|
||||
}
|
||||
},
|
||||
) {
|
||||
// calculate smallest area to fit the contents in
|
||||
let subCanvasX1 = Infinity;
|
||||
@ -56,14 +56,14 @@ export function getExportCanvasPreview(
|
||||
{
|
||||
viewBackgroundColor: exportBackground ? viewBackgroundColor : null,
|
||||
scrollX: 0,
|
||||
scrollY: 0
|
||||
scrollY: 0,
|
||||
},
|
||||
{
|
||||
offsetX: -subCanvasX1 + exportPadding,
|
||||
offsetY: -subCanvasY1 + exportPadding,
|
||||
renderScrollbars: false,
|
||||
renderSelection: false
|
||||
}
|
||||
renderSelection: false,
|
||||
},
|
||||
);
|
||||
return tempCanvas;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ export {
|
||||
deleteSelectedElements,
|
||||
someElementIsSelected,
|
||||
getElementsWithinSelection,
|
||||
getCommonAttributeOfSelectedElements
|
||||
getCommonAttributeOfSelectedElements,
|
||||
} from "./selection";
|
||||
export {
|
||||
exportCanvas,
|
||||
@ -14,13 +14,13 @@ export {
|
||||
restoreFromLocalStorage,
|
||||
saveToLocalStorage,
|
||||
exportToBackend,
|
||||
importFromBackend
|
||||
importFromBackend,
|
||||
} from "./data";
|
||||
export {
|
||||
hasBackground,
|
||||
hasStroke,
|
||||
getElementAtPosition,
|
||||
getElementContainingPosition,
|
||||
hasText
|
||||
hasText,
|
||||
} from "./comparisons";
|
||||
export { createScene } from "./createScene";
|
||||
|
@ -11,7 +11,7 @@ export function getScrollBars(
|
||||
canvasWidth: number,
|
||||
canvasHeight: number,
|
||||
scrollX: number,
|
||||
scrollY: number
|
||||
scrollY: number,
|
||||
) {
|
||||
let minX = Infinity;
|
||||
let maxX = 0;
|
||||
@ -41,14 +41,14 @@ export function getScrollBars(
|
||||
horizontalScrollBar = {
|
||||
x: Math.min(
|
||||
leftOverflow + SCROLLBAR_MARGIN,
|
||||
canvasWidth - SCROLLBAR_MIN_SIZE - SCROLLBAR_MARGIN
|
||||
canvasWidth - SCROLLBAR_MIN_SIZE - SCROLLBAR_MARGIN,
|
||||
),
|
||||
y: canvasHeight - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
||||
width: Math.max(
|
||||
canvasWidth - rightOverflow - leftOverflow - SCROLLBAR_MARGIN * 2,
|
||||
SCROLLBAR_MIN_SIZE
|
||||
SCROLLBAR_MIN_SIZE,
|
||||
),
|
||||
height: SCROLLBAR_WIDTH
|
||||
height: SCROLLBAR_WIDTH,
|
||||
};
|
||||
}
|
||||
|
||||
@ -59,19 +59,19 @@ export function getScrollBars(
|
||||
x: canvasWidth - SCROLLBAR_WIDTH - SCROLLBAR_MARGIN,
|
||||
y: Math.min(
|
||||
topOverflow + SCROLLBAR_MARGIN,
|
||||
canvasHeight - SCROLLBAR_MIN_SIZE - SCROLLBAR_MARGIN
|
||||
canvasHeight - SCROLLBAR_MIN_SIZE - SCROLLBAR_MARGIN,
|
||||
),
|
||||
width: SCROLLBAR_WIDTH,
|
||||
height: Math.max(
|
||||
canvasHeight - bottomOverflow - topOverflow - SCROLLBAR_WIDTH * 2,
|
||||
SCROLLBAR_MIN_SIZE
|
||||
)
|
||||
SCROLLBAR_MIN_SIZE,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
horizontal: horizontalScrollBar,
|
||||
vertical: verticalScrollBar
|
||||
vertical: verticalScrollBar,
|
||||
};
|
||||
}
|
||||
|
||||
@ -82,30 +82,30 @@ export function isOverScrollBars(
|
||||
canvasWidth: number,
|
||||
canvasHeight: number,
|
||||
scrollX: number,
|
||||
scrollY: number
|
||||
scrollY: number,
|
||||
) {
|
||||
const scrollBars = getScrollBars(
|
||||
elements,
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
scrollX,
|
||||
scrollY
|
||||
scrollY,
|
||||
);
|
||||
|
||||
const [isOverHorizontalScrollBar, isOverVerticalScrollBar] = [
|
||||
scrollBars.horizontal,
|
||||
scrollBars.vertical
|
||||
scrollBars.vertical,
|
||||
].map(
|
||||
scrollBar =>
|
||||
scrollBar &&
|
||||
scrollBar.x <= x &&
|
||||
x <= scrollBar.x + scrollBar.width &&
|
||||
scrollBar.y <= y &&
|
||||
y <= scrollBar.y + scrollBar.height
|
||||
y <= scrollBar.y + scrollBar.height,
|
||||
);
|
||||
|
||||
return {
|
||||
isOverHorizontalScrollBar,
|
||||
isOverVerticalScrollBar
|
||||
isOverVerticalScrollBar,
|
||||
};
|
||||
}
|
||||
|
@ -3,20 +3,20 @@ import { getElementAbsoluteCoords } from "../element";
|
||||
|
||||
export function getElementsWithinSelection(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
selection: ExcalidrawElement
|
||||
selection: ExcalidrawElement,
|
||||
) {
|
||||
const [
|
||||
selectionX1,
|
||||
selectionY1,
|
||||
selectionX2,
|
||||
selectionY2
|
||||
selectionY2,
|
||||
] = getElementAbsoluteCoords(selection);
|
||||
return elements.filter(element => {
|
||||
const [
|
||||
elementX1,
|
||||
elementY1,
|
||||
elementX2,
|
||||
elementY2
|
||||
elementY2,
|
||||
] = getElementAbsoluteCoords(element);
|
||||
|
||||
return (
|
||||
@ -62,14 +62,14 @@ export const someElementIsSelected = (elements: readonly ExcalidrawElement[]) =>
|
||||
*/
|
||||
export function getCommonAttributeOfSelectedElements<T>(
|
||||
elements: readonly ExcalidrawElement[],
|
||||
getAttribute: (element: ExcalidrawElement) => T
|
||||
getAttribute: (element: ExcalidrawElement) => T,
|
||||
): T | null {
|
||||
const attributes = Array.from(
|
||||
new Set(
|
||||
elements
|
||||
.filter(element => element.isSelected)
|
||||
.map(element => getAttribute(element))
|
||||
)
|
||||
.map(element => getAttribute(element)),
|
||||
),
|
||||
);
|
||||
return attributes.length === 1 ? attributes[0] : null;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ export const SHAPES = [
|
||||
<path d="M302.189 329.126H196.105l55.831 135.993c3.889 9.428-.555 19.999-9.444 23.999l-49.165 21.427c-9.165 4-19.443-.571-23.332-9.714l-53.053-129.136-86.664 89.138C18.729 472.71 0 463.554 0 447.977V18.299C0 1.899 19.921-6.096 30.277 5.443l284.412 292.542c11.472 11.179 3.007 31.141-12.5 31.141z" />
|
||||
</svg>
|
||||
),
|
||||
value: "selection"
|
||||
value: "selection",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
@ -18,7 +18,7 @@ export const SHAPES = [
|
||||
<path d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z" />
|
||||
</svg>
|
||||
),
|
||||
value: "rectangle"
|
||||
value: "rectangle",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
@ -27,7 +27,7 @@ export const SHAPES = [
|
||||
<path d="M111.823 0L16.622 111.823 111.823 223.646 207.025 111.823z" />
|
||||
</svg>
|
||||
),
|
||||
value: "diamond"
|
||||
value: "diamond",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
@ -36,7 +36,7 @@ export const SHAPES = [
|
||||
<path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8z" />
|
||||
</svg>
|
||||
),
|
||||
value: "ellipse"
|
||||
value: "ellipse",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
@ -45,7 +45,7 @@ export const SHAPES = [
|
||||
<path d="M313.941 216H12c-6.627 0-12 5.373-12 12v56c0 6.627 5.373 12 12 12h301.941v46.059c0 21.382 25.851 32.09 40.971 16.971l86.059-86.059c9.373-9.373 9.373-24.569 0-33.941l-86.059-86.059c-15.119-15.119-40.971-4.411-40.971 16.971V216z" />
|
||||
</svg>
|
||||
),
|
||||
value: "arrow"
|
||||
value: "arrow",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
@ -54,7 +54,7 @@ export const SHAPES = [
|
||||
<line x1="0" y1="3" x2="6" y2="3" stroke="#000" strokeLinecap="round" />
|
||||
</svg>
|
||||
),
|
||||
value: "line"
|
||||
value: "line",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
@ -63,13 +63,13 @@ export const SHAPES = [
|
||||
<path d="M432 416h-23.41L277.88 53.69A32 32 0 0 0 247.58 32h-47.16a32 32 0 0 0-30.3 21.69L39.41 416H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16h-19.58l23.3-64h152.56l23.3 64H304a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM176.85 272L224 142.51 271.15 272z" />
|
||||
</svg>
|
||||
),
|
||||
value: "text"
|
||||
}
|
||||
value: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const shapesShortcutKeys = SHAPES.map((shape, index) => [
|
||||
shape.value[0],
|
||||
(index + 1).toString()
|
||||
(index + 1).toString(),
|
||||
]).flat(1);
|
||||
|
||||
export function findShapeByKey(key: string) {
|
||||
|
@ -15,7 +15,7 @@ export function capitalizeString(str: string) {
|
||||
}
|
||||
|
||||
export function isInputLike(
|
||||
target: Element | EventTarget | null
|
||||
target: Element | EventTarget | null,
|
||||
): target is HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement {
|
||||
return (
|
||||
(target instanceof HTMLElement && target.dataset.type === "wysiwyg") ||
|
||||
@ -54,7 +54,7 @@ export function measureText(text: string, font: string) {
|
||||
|
||||
export function debounce<T extends any[]>(
|
||||
fn: (...args: T) => void,
|
||||
timeout: number
|
||||
timeout: number,
|
||||
) {
|
||||
let handle = 0;
|
||||
let lastArgs: T;
|
||||
|
@ -4,7 +4,7 @@ function expectMove<T>(
|
||||
fn: (elements: T[], indicesToMove: number[]) => void,
|
||||
elems: T[],
|
||||
indices: number[],
|
||||
equal: T[]
|
||||
equal: T[],
|
||||
) {
|
||||
fn(elems, indices);
|
||||
expect(elems).toEqual(equal);
|
||||
@ -17,7 +17,7 @@ it("should moveOneLeft", () => {
|
||||
moveOneLeft,
|
||||
["a", "b", "c", "d"],
|
||||
[0, 1, 2, 3],
|
||||
["a", "b", "c", "d"]
|
||||
["a", "b", "c", "d"],
|
||||
);
|
||||
expectMove(moveOneLeft, ["a", "b", "c", "d"], [1, 3], ["b", "a", "d", "c"]);
|
||||
});
|
||||
@ -29,7 +29,7 @@ it("should moveOneRight", () => {
|
||||
moveOneRight,
|
||||
["a", "b", "c", "d"],
|
||||
[0, 1, 2, 3],
|
||||
["a", "b", "c", "d"]
|
||||
["a", "b", "c", "d"],
|
||||
);
|
||||
expectMove(moveOneRight, ["a", "b", "c", "d"], [0, 2], ["b", "a", "d", "c"]);
|
||||
});
|
||||
@ -39,31 +39,31 @@ it("should moveAllLeft", () => {
|
||||
moveAllLeft,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[2, 5],
|
||||
["c", "f", "a", "b", "d", "e", "g"]
|
||||
["c", "f", "a", "b", "d", "e", "g"],
|
||||
);
|
||||
expectMove(
|
||||
moveAllLeft,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[5],
|
||||
["f", "a", "b", "c", "d", "e", "g"]
|
||||
["f", "a", "b", "c", "d", "e", "g"],
|
||||
);
|
||||
expectMove(
|
||||
moveAllLeft,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[0, 1, 2, 3, 4, 5, 6],
|
||||
["a", "b", "c", "d", "e", "f", "g"]
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
);
|
||||
expectMove(
|
||||
moveAllLeft,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[0, 1, 2],
|
||||
["a", "b", "c", "d", "e", "f", "g"]
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
);
|
||||
expectMove(
|
||||
moveAllLeft,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[4, 5, 6],
|
||||
["e", "f", "g", "a", "b", "c", "d"]
|
||||
["e", "f", "g", "a", "b", "c", "d"],
|
||||
);
|
||||
});
|
||||
|
||||
@ -72,30 +72,30 @@ it("should moveAllRight", () => {
|
||||
moveAllRight,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[2, 5],
|
||||
["a", "b", "d", "e", "g", "c", "f"]
|
||||
["a", "b", "d", "e", "g", "c", "f"],
|
||||
);
|
||||
expectMove(
|
||||
moveAllRight,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[5],
|
||||
["a", "b", "c", "d", "e", "g", "f"]
|
||||
["a", "b", "c", "d", "e", "g", "f"],
|
||||
);
|
||||
expectMove(
|
||||
moveAllRight,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[0, 1, 2, 3, 4, 5, 6],
|
||||
["a", "b", "c", "d", "e", "f", "g"]
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
);
|
||||
expectMove(
|
||||
moveAllRight,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[0, 1, 2],
|
||||
["d", "e", "f", "g", "a", "b", "c"]
|
||||
["d", "e", "f", "g", "a", "b", "c"],
|
||||
);
|
||||
expectMove(
|
||||
moveAllRight,
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
[4, 5, 6],
|
||||
["a", "b", "c", "d", "e", "f", "g"]
|
||||
["a", "b", "c", "d", "e", "f", "g"],
|
||||
);
|
||||
});
|
||||
|
@ -23,7 +23,7 @@ export function moveOneLeft<T>(elements: T[], indicesToMove: number[]) {
|
||||
|
||||
export function moveOneRight<T>(elements: T[], indicesToMove: number[]) {
|
||||
const reversedIndicesToMove = indicesToMove.sort(
|
||||
(a: number, b: number) => b - a
|
||||
(a: number, b: number) => b - a,
|
||||
);
|
||||
let isSorted = true;
|
||||
|
||||
@ -166,7 +166,7 @@ export function moveAllLeft<T>(elements: T[], indicesToMove: number[]) {
|
||||
// And we are done!
|
||||
export function moveAllRight<T>(elements: T[], indicesToMove: number[]) {
|
||||
const reversedIndicesToMove = indicesToMove.sort(
|
||||
(a: number, b: number) => b - a
|
||||
(a: number, b: number) => b - a,
|
||||
);
|
||||
|
||||
// Copy the elements to move
|
||||
|
Loading…
x
Reference in New Issue
Block a user