Files
excalidraw/src/element/transformHandles.ts
Barnabás Molnár 6334bd832f feat: editor redesign 🔥 (#5780)
* Placed eraser into shape switcher (top toolbar).
Redesigned top toolbar.

* Redesigned zoom and undo-redo buttons.

* Started redesigning left toolbar.

* Redesigned help dialog.

* Colour picker now somewhat in line with new design

* [WIP] Changed a bunch of icons.
TODO: organise new icons.

* [WIP] Organised a bunch of icons. Still some to do

* [WIP] Started working on hamburger menu.

* Fixed some bugs with hamburger menu.

* Menu and left toolbar positioning.

* Added some more items to hamburger menu.

* Changed some icons.

* Modal/dialog styling & bunch of fixes.

* Some more dialog improvements & fixes.

* Mobile menu changes.

* Menu can now be closed with outside click.

* Collab avatars and button changes.

* Icon sizing. Left toolbar positioning.

* Implemented welcome screen rendering logic.

* [WIP] Welcome screen content + design.

* Some more welcome screen content and design.

* Merge fixes.

* Tweaked icon set.

* Welcome screen darkmode fix.

* Content updates.

* Various small fixes & adjustments.
Moved language selection into menu.
Fixed some problematic icons.
Slightly moved encryption icon.

* Sidebar header redesign.

* Libraries content rendering logic + some styling.

* Somem more library sidebar styling.

* Publish library dialog styling.

* scroll-back-to-content btn styling

* ColorPicker positioning.

* Library button styling.

* ColorPicker positioning "fix".

* Misc adjustments.

* PenMode button changes.

* Trying to make mobile somewhat usable.

* Added a couple of icons.

* Added some shortcuts.

* Prevent welcome screen flickering.
Fix issue with welcome screen interactivity.
Don't show sidebar button when docked.

* Icon sizing on smaller screens.

* Sidebar styling changes.

* Alignment button... well... alignments.

* Fix inconsistent padding in left toolbar.

* HintViewer changes.

* Hamburger menu changes.

* Move encryption badge back to its original pos.

* Arrowhead changes.
Active state, colours + stronger shadow.

* Added new custom font.

* Fixed bug with library button not rendering.

* Fixed issue with lang selection colours.

* Add tooltips for undo, redo.

* Address some dark mode contrast issues.

* (Re)introduce counter for selectedItems in sidebar

* [WIP] Tweaked bounding box colour & padding.

* Dashed bounding box for remote clients.

* Some more bounding box tweaks.

* Removed docking animation for now...

* Address some RTL issues.

* Welcome screen responsiveness.

* use lighter selection color in dark mode & align naming

* use rounded corners for transform handles

* use lighter gray for welcomeScreen text in dark mode

* disable selection on dialog buttons

* change selection button icon

* fix library item width being flexible

* library: visually align spinner with first section heading

* lint

* fix scrollbar color in dark mode & make thinner

* adapt properties panel max-height

* add shrotcut label to save-to-current-file

* fix unrelated `useOutsideClick` firing for active modal

* add promo color to e+ menu item

* fix type

* lowered button size

* fix transform handles raidus not accounting for zoom

* attempt fix for excal logo on safari

* final fix for excal logo on safari

* fixing fhd resolution button sized

* remove TODO shortcut

* Collab related styling changes.
Expanding avatar list no longer offsets top toolbar.
Added active state & collaborator count badge for collab button.

* Tweaked collab button active colours.

* Added active style to collab btn in hamburger menu

* Remove unnecessary comment.

* Added back promo link for non (signed in) E+ users

* Go to E+ button added for signed in E+ users.

* Close menu & dropdown on modal close.

* tweak icons & fix rendering on smaller sizes [part one]

* align welcomeScreen icons with other UI

* switch icon resize mq to `device-width`

* disable welcomeScreen items `:hover` when selecting on canvas

* change selection box color and style

* reduce selection padding and fix group selection styling

* improve collab cursor styling

- make name borders round
- hide status when "active"
- remove black/gray colors

* add Twitter to hamburger menu

* align collab button

* add shortcut for image export dialog

* revert yarn.lock

* fix more tabler icons

* slightly better-looking penMode button

* change penMode button & tooltip

* revert hamburger menu icon

* align padding on lang picker & canvas bg

* updated robot txt to allow twitter bot and fb bot

* added new OG and tweaked the OG state

* add tooltip to collab button

* align style for scroll-to-content button

* fix pointer-events around toolbar

* fix decor arrow positioning and RTL

* fix welcomeScreen-item active state in dark mode

* change `load` button copy

* prevent shadow anim when opening a docked sidebar

* update E+ links ga params

* show redirect-to-eplus welcomeScreen subheading for signed-in users

* make more generic

* add ga for eplus redirect button

* change copy and icons for hamburger export buttons

* update snaps

* trim the username to account for trailing spaces

* tweaks around decor breakpoints

* fix linear element editor test

* remove .env change

* remove `it.only`

Co-authored-by: dwelle <luzar.david@gmail.com>
Co-authored-by: Maielo <maielo.mv@gmail.com>
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
2022-11-01 17:29:58 +01:00

285 lines
7.1 KiB
TypeScript

import {
ExcalidrawElement,
NonDeletedExcalidrawElement,
PointerType,
} from "./types";
import { getElementAbsoluteCoords, Bounds } from "./bounds";
import { rotate } from "../math";
import { AppState, Zoom } from "../types";
import { isTextElement } from ".";
import { isLinearElement } from "./typeChecks";
import { DEFAULT_SPACING } from "../renderer/renderScene";
export type TransformHandleDirection =
| "n"
| "s"
| "w"
| "e"
| "nw"
| "ne"
| "sw"
| "se";
export type TransformHandleType = TransformHandleDirection | "rotation";
export type TransformHandle = [number, number, number, number];
export type TransformHandles = Partial<{
[T in TransformHandleType]: TransformHandle;
}>;
export type MaybeTransformHandleType = TransformHandleType | false;
const transformHandleSizes: { [k in PointerType]: number } = {
mouse: 8,
pen: 16,
touch: 28,
};
const ROTATION_RESIZE_HANDLE_GAP = 16;
export const OMIT_SIDES_FOR_MULTIPLE_ELEMENTS = {
e: true,
s: true,
n: true,
w: true,
};
const OMIT_SIDES_FOR_TEXT_ELEMENT = {
e: true,
s: true,
n: true,
w: true,
};
const OMIT_SIDES_FOR_LINE_SLASH = {
e: true,
s: true,
n: true,
w: true,
nw: true,
se: true,
};
const OMIT_SIDES_FOR_LINE_BACKSLASH = {
e: true,
s: true,
n: true,
w: true,
};
const generateTransformHandle = (
x: number,
y: number,
width: number,
height: number,
cx: number,
cy: number,
angle: number,
): TransformHandle => {
const [xx, yy] = rotate(x + width / 2, y + height / 2, cx, cy, angle);
return [xx - width / 2, yy - height / 2, width, height];
};
export const getTransformHandlesFromCoords = (
[x1, y1, x2, y2]: Bounds,
angle: number,
zoom: Zoom,
pointerType: PointerType,
omitSides: { [T in TransformHandleType]?: boolean } = {},
margin = 4,
): TransformHandles => {
const size = transformHandleSizes[pointerType];
const handleWidth = size / zoom.value;
const handleHeight = size / zoom.value;
const handleMarginX = size / zoom.value;
const handleMarginY = size / zoom.value;
const width = x2 - x1;
const height = y2 - y1;
const cx = (x1 + x2) / 2;
const cy = (y1 + y2) / 2;
const dashedLineMargin = margin / zoom.value;
const centeringOffset = (size - DEFAULT_SPACING * 2) / (2 * zoom.value);
const transformHandles: TransformHandles = {
nw: omitSides.nw
? undefined
: generateTransformHandle(
x1 - dashedLineMargin - handleMarginX + centeringOffset,
y1 - dashedLineMargin - handleMarginY + centeringOffset,
handleWidth,
handleHeight,
cx,
cy,
angle,
),
ne: omitSides.ne
? undefined
: generateTransformHandle(
x2 + dashedLineMargin - centeringOffset,
y1 - dashedLineMargin - handleMarginY + centeringOffset,
handleWidth,
handleHeight,
cx,
cy,
angle,
),
sw: omitSides.sw
? undefined
: generateTransformHandle(
x1 - dashedLineMargin - handleMarginX + centeringOffset,
y2 + dashedLineMargin - centeringOffset,
handleWidth,
handleHeight,
cx,
cy,
angle,
),
se: omitSides.se
? undefined
: generateTransformHandle(
x2 + dashedLineMargin - centeringOffset,
y2 + dashedLineMargin - centeringOffset,
handleWidth,
handleHeight,
cx,
cy,
angle,
),
rotation: omitSides.rotation
? undefined
: generateTransformHandle(
x1 + width / 2 - handleWidth / 2,
y1 -
dashedLineMargin -
handleMarginY +
centeringOffset -
ROTATION_RESIZE_HANDLE_GAP / zoom.value,
handleWidth,
handleHeight,
cx,
cy,
angle,
),
};
// We only want to show height handles (all cardinal directions) above a certain size
// Note: we render using "mouse" size so we should also use "mouse" size for this check
const minimumSizeForEightHandles =
(5 * transformHandleSizes.mouse) / zoom.value;
if (Math.abs(width) > minimumSizeForEightHandles) {
if (!omitSides.n) {
transformHandles.n = generateTransformHandle(
x1 + width / 2 - handleWidth / 2,
y1 - dashedLineMargin - handleMarginY + centeringOffset,
handleWidth,
handleHeight,
cx,
cy,
angle,
);
}
if (!omitSides.s) {
transformHandles.s = generateTransformHandle(
x1 + width / 2 - handleWidth / 2,
y2 + dashedLineMargin - centeringOffset,
handleWidth,
handleHeight,
cx,
cy,
angle,
);
}
}
if (Math.abs(height) > minimumSizeForEightHandles) {
if (!omitSides.w) {
transformHandles.w = generateTransformHandle(
x1 - dashedLineMargin - handleMarginX + centeringOffset,
y1 + height / 2 - handleHeight / 2,
handleWidth,
handleHeight,
cx,
cy,
angle,
);
}
if (!omitSides.e) {
transformHandles.e = generateTransformHandle(
x2 + dashedLineMargin - centeringOffset,
y1 + height / 2 - handleHeight / 2,
handleWidth,
handleHeight,
cx,
cy,
angle,
);
}
}
return transformHandles;
};
export const getTransformHandles = (
element: ExcalidrawElement,
zoom: Zoom,
pointerType: PointerType = "mouse",
): TransformHandles => {
// so that when locked element is selected (especially when you toggle lock
// via keyboard) the locked element is visually distinct, indicating
// you can't move/resize
if (element.locked) {
return {};
}
let omitSides: { [T in TransformHandleType]?: boolean } = {};
if (element.type === "freedraw" || isLinearElement(element)) {
if (element.points.length === 2) {
// only check the last point because starting point is always (0,0)
const [, p1] = element.points;
if (p1[0] === 0 || p1[1] === 0) {
omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
} else if (p1[0] > 0 && p1[1] < 0) {
omitSides = OMIT_SIDES_FOR_LINE_SLASH;
} else if (p1[0] > 0 && p1[1] > 0) {
omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
} else if (p1[0] < 0 && p1[1] > 0) {
omitSides = OMIT_SIDES_FOR_LINE_SLASH;
} else if (p1[0] < 0 && p1[1] < 0) {
omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
}
}
} else if (isTextElement(element)) {
omitSides = OMIT_SIDES_FOR_TEXT_ELEMENT;
}
const dashedLineMargin = isLinearElement(element)
? DEFAULT_SPACING + 8
: DEFAULT_SPACING;
return getTransformHandlesFromCoords(
getElementAbsoluteCoords(element),
element.angle,
zoom,
pointerType,
omitSides,
dashedLineMargin,
);
};
export const shouldShowBoundingBox = (
elements: NonDeletedExcalidrawElement[],
appState: AppState,
) => {
if (appState.editingLinearElement) {
return false;
}
if (elements.length > 1) {
return true;
}
const element = elements[0];
if (!isLinearElement(element)) {
return true;
}
return element.points.length > 2;
};