Set Trailing Cmma to (#525)

This commit is contained in:
Lipis 2020-01-24 12:04:54 +02:00 committed by GitHub
parent 25202aec11
commit ee68af0fd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 352 additions and 350 deletions

View File

@ -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"],
};

View File

@ -1 +1,3 @@
{}
{
"trailingComma": "all"
}

View File

@ -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();

View File

@ -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 = {
}
}}
/>
)
),
};

View File

@ -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,
};

View File

@ -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));
}}
/>
)
),
};

View File

@ -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)}
/>
</>
)
),
};

View File

@ -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",
};

View File

@ -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,
};

View File

@ -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",
};

View File

@ -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";

View File

@ -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];

View File

@ -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;
}

View File

@ -19,6 +19,6 @@ export function getDefaultAppState(): AppState {
scrollY: 0,
cursorX: 0,
cursorY: 0,
name: DEFAULT_PROJECT_NAME
name: DEFAULT_PROJECT_NAME,
};
}

View File

@ -3,7 +3,7 @@ import React from "react";
export function ButtonSelect<T>({
options,
value,
onChange
onChange,
}: {
options: { value: T; text: string }[];
value: T | null;

View File

@ -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",
],
};

View File

@ -83,8 +83,8 @@ export default {
options={options}
onCloseRequest={handleClose}
/>,
getContextMenuNode()
getContextMenuNode(),
);
}
}
},
};

View File

@ -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>

View File

@ -9,7 +9,7 @@ type FixedSideContainerProps = {
export function FixedSideContainer({
children,
side
side,
}: FixedSideContainerProps) {
return (
<div className={"FixedSideContainer FixedSideContainer_side_" + side}>

View File

@ -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;

View File

@ -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) {

View File

@ -16,7 +16,7 @@ export function Modal(props: {
{props.children}
</div>
</div>,
modalRoot
modalRoot,
);
}

View File

@ -14,7 +14,7 @@ export function Popover({
left,
top,
onCloseRequest,
fitInViewport = false
fitInViewport = false,
}: Props) {
const popoverRef = useRef<HTMLDivElement>(null);

View File

@ -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,
};

View File

@ -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);
});

View File

@ -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
];
}

View File

@ -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)) {

View File

@ -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;
}

View File

@ -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";

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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 => {

View File

@ -1,7 +1,7 @@
import { ExcalidrawElement, ExcalidrawTextElement } from "./types";
export function isTextElement(
element: ExcalidrawElement
element: ExcalidrawElement,
): element is ExcalidrawTextElement {
return element.type === "text";
}

View File

@ -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,
})),
});
}

View File

@ -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;

View File

@ -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");

View File

@ -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,
);

View File

@ -11,7 +11,7 @@ export const KEYS = {
return /Mac|iPod|iPhone|iPad/.test(window.navigator.platform)
? "metaKey"
: "ctrlKey";
}
},
};
export function isArrowKey(keyCode: string) {

View File

@ -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,
];
}

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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));

View File

@ -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;
}

View File

@ -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";

View File

@ -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,
};
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;

View File

@ -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"],
);
});

View File

@ -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