Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 638d730cd5 | |||
| 80e83374d4 |
@@ -0,0 +1,63 @@
|
|||||||
|
import type { ExcalidrawElement } from "@excalidraw/element/types";
|
||||||
|
|
||||||
|
export type StrokeWidthKey = "thin" | "medium" | "bold";
|
||||||
|
|
||||||
|
export const STROKE_WIDTH_KEYS: readonly StrokeWidthKey[] = [
|
||||||
|
"thin",
|
||||||
|
"medium",
|
||||||
|
"bold",
|
||||||
|
];
|
||||||
|
|
||||||
|
export const STROKE_WIDTH: Readonly<
|
||||||
|
Record<StrokeWidthKey | "extraBold", ExcalidrawElement["strokeWidth"]>
|
||||||
|
> = {
|
||||||
|
thin: 1,
|
||||||
|
medium: 2,
|
||||||
|
bold: 4,
|
||||||
|
extraBold: 8, // unused (may be introduced in the future)
|
||||||
|
};
|
||||||
|
|
||||||
|
// freedraw schema 2.0 uses thinner stroke, but to maintain backwards and
|
||||||
|
// forwards compatibility, instead of changing the shape renderer, we scale
|
||||||
|
// the stroke width by 1/2 (previous, thin was 1, medium 2 etc.)
|
||||||
|
//
|
||||||
|
// note that in the UI, STROKE_WIDTH.thin == FREEDRAW_STROKE_WIDTH.thin still
|
||||||
|
export const FREEDRAW_STROKE_WIDTH: Readonly<
|
||||||
|
Record<StrokeWidthKey | "extraBold", ExcalidrawElement["strokeWidth"]>
|
||||||
|
> = {
|
||||||
|
thin: 0.5,
|
||||||
|
medium: 1,
|
||||||
|
bold: 2,
|
||||||
|
extraBold: 4, // legacy (may be used again in the future)
|
||||||
|
};
|
||||||
|
|
||||||
|
const STROKE_WIDTH_TO_KEY = {
|
||||||
|
generic: Object.fromEntries(
|
||||||
|
Object.entries(STROKE_WIDTH).map(([key, value]) => [value, key]),
|
||||||
|
) as Record<ExcalidrawElement["strokeWidth"], StrokeWidthKey | undefined>,
|
||||||
|
freedraw: Object.fromEntries(
|
||||||
|
Object.entries(FREEDRAW_STROKE_WIDTH).map(([key, value]) => [value, key]),
|
||||||
|
) as Record<ExcalidrawElement["strokeWidth"], StrokeWidthKey | undefined>,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStrokeWidthKeyForElement = (
|
||||||
|
element: Pick<ExcalidrawElement, "type" | "strokeWidth">,
|
||||||
|
): StrokeWidthKey | null => {
|
||||||
|
const strokeWidthToKey =
|
||||||
|
element.type === "freedraw"
|
||||||
|
? STROKE_WIDTH_TO_KEY.freedraw
|
||||||
|
: STROKE_WIDTH_TO_KEY.generic;
|
||||||
|
|
||||||
|
return strokeWidthToKey[element.strokeWidth] ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStrokeWidthByKey = (
|
||||||
|
elementType: ExcalidrawElement["type"],
|
||||||
|
strokeWidthKey: StrokeWidthKey,
|
||||||
|
): ExcalidrawElement["strokeWidth"] => {
|
||||||
|
return elementType === "freedraw"
|
||||||
|
? FREEDRAW_STROKE_WIDTH[strokeWidthKey]
|
||||||
|
: STROKE_WIDTH[strokeWidthKey];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_ELEMENT_STROKE_WIDTH_KEY: StrokeWidthKey = "medium";
|
||||||
@@ -5,6 +5,10 @@ import type {
|
|||||||
import type { AppProps, AppState } from "@excalidraw/excalidraw/types";
|
import type { AppProps, AppState } from "@excalidraw/excalidraw/types";
|
||||||
|
|
||||||
import { COLOR_PALETTE } from "./colors";
|
import { COLOR_PALETTE } from "./colors";
|
||||||
|
import {
|
||||||
|
STROKE_WIDTH,
|
||||||
|
DEFAULT_ELEMENT_STROKE_WIDTH_KEY,
|
||||||
|
} from "./constants.strokeWidth";
|
||||||
|
|
||||||
export const supportsResizeObserver =
|
export const supportsResizeObserver =
|
||||||
typeof window !== "undefined" && "ResizeObserver" in window;
|
typeof window !== "undefined" && "ResizeObserver" in window;
|
||||||
@@ -404,48 +408,6 @@ export const ROUGHNESS = {
|
|||||||
cartoonist: 2,
|
cartoonist: 2,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type StrokeWidthKey = "thin" | "medium" | "bold";
|
|
||||||
|
|
||||||
export const STROKE_WIDTH_KEYS: readonly StrokeWidthKey[] = [
|
|
||||||
"thin",
|
|
||||||
"medium",
|
|
||||||
"bold",
|
|
||||||
];
|
|
||||||
|
|
||||||
export const STROKE_WIDTH: Readonly<
|
|
||||||
Record<StrokeWidthKey | "extraBold", ExcalidrawElement["strokeWidth"]>
|
|
||||||
> = {
|
|
||||||
thin: 1,
|
|
||||||
medium: 2,
|
|
||||||
bold: 4,
|
|
||||||
extraBold: 8, // unused (may be introduced in the future)
|
|
||||||
};
|
|
||||||
|
|
||||||
// freedraw schema 2.0 uses thinner stroke, but to maintain backwards and
|
|
||||||
// forwards compatibility, instead of changing the shape renderer, we scale
|
|
||||||
// the stroke width by 1/2 (previous, thin was 1, medium 2 etc.)
|
|
||||||
//
|
|
||||||
// note that in the UI, STROKE_WIDTH.thin == FREEDRAW_STROKE_WIDTH.thin still
|
|
||||||
export const FREEDRAW_STROKE_WIDTH: Readonly<
|
|
||||||
Record<StrokeWidthKey | "extraBold", ExcalidrawElement["strokeWidth"]>
|
|
||||||
> = {
|
|
||||||
thin: 0.5,
|
|
||||||
medium: 1,
|
|
||||||
bold: 2,
|
|
||||||
extraBold: 4, // legacy (may be used again in the future)
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getStrokeWidthByKey = (
|
|
||||||
elementType: ExcalidrawElement["type"],
|
|
||||||
strokeWidthKey: StrokeWidthKey,
|
|
||||||
): ExcalidrawElement["strokeWidth"] => {
|
|
||||||
return elementType === "freedraw"
|
|
||||||
? FREEDRAW_STROKE_WIDTH[strokeWidthKey]
|
|
||||||
: STROKE_WIDTH[strokeWidthKey];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DEFAULT_ELEMENT_STROKE_WIDTH_KEY: StrokeWidthKey = "medium";
|
|
||||||
|
|
||||||
export const DEFAULT_ELEMENT_PROPS: {
|
export const DEFAULT_ELEMENT_PROPS: {
|
||||||
strokeColor: ExcalidrawElement["strokeColor"];
|
strokeColor: ExcalidrawElement["strokeColor"];
|
||||||
backgroundColor: ExcalidrawElement["backgroundColor"];
|
backgroundColor: ExcalidrawElement["backgroundColor"];
|
||||||
@@ -552,4 +514,4 @@ export const MOBILE_ACTION_BUTTON_BG = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const DEFAULT_STROKE_STREAMLINE = 0.5;
|
export const DEFAULT_STROKE_STREAMLINE = 0.5;
|
||||||
export const DEFAULT_STROKE_STREAMLINE_PRECISE = 0.2;
|
export const DEFAULT_STROKE_STREAMLINE_PRECISE = 0.3;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export * from "./binary-heap";
|
|||||||
export * from "./bounds";
|
export * from "./bounds";
|
||||||
export * from "./colors";
|
export * from "./colors";
|
||||||
export * from "./constants";
|
export * from "./constants";
|
||||||
|
export * from "./constants.strokeWidth";
|
||||||
export * from "./font-metadata";
|
export * from "./font-metadata";
|
||||||
export * from "./queue";
|
export * from "./queue";
|
||||||
export * from "./keys";
|
export * from "./keys";
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.base.json",
|
"extends": "../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist/types",
|
"outDir": "./dist/types"
|
||||||
"rootDir": "../"
|
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "global.d.ts"],
|
"include": ["src/**/*", "global.d.ts"],
|
||||||
"exclude": ["**/*.test.*", "tests", "types", "examples", "dist"]
|
"exclude": ["**/*.test.*", "tests", "types", "examples", "dist"]
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
LINE_POLYGON_POINT_MERGE_DISTANCE,
|
LINE_POLYGON_POINT_MERGE_DISTANCE,
|
||||||
applyDarkModeFilter,
|
applyDarkModeFilter,
|
||||||
DEFAULT_STROKE_STREAMLINE,
|
DEFAULT_STROKE_STREAMLINE,
|
||||||
|
DEFAULT_STROKE_STREAMLINE_PRECISE,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import { RoughGenerator } from "roughjs/bin/generator";
|
import { RoughGenerator } from "roughjs/bin/generator";
|
||||||
@@ -1185,15 +1186,20 @@ const VARIABLE_WIDTH_FREEDRAW = {
|
|||||||
SIZE_FACTOR: 4.25,
|
SIZE_FACTOR: 4.25,
|
||||||
THINNING: 0.6,
|
THINNING: 0.6,
|
||||||
SMOOTHING: 0.5,
|
SMOOTHING: 0.5,
|
||||||
|
STREAMLINE: DEFAULT_STROKE_STREAMLINE,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const CONSTANT_WIDTH_FREEDRAW = {
|
const CONSTANT_WIDTH_FREEDRAW = {
|
||||||
/** Stroke size relative to `strokeWidth` for uniform (laser) strokes. */
|
/** Stroke size relative to `strokeWidth` for uniform (laser) strokes. */
|
||||||
SIZE_FACTOR: 1.4,
|
SIZE_FACTOR: 1.4,
|
||||||
|
STREAMLINE: DEFAULT_STROKE_STREAMLINE_PRECISE,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const getFreedrawStreamline = (element: ExcalidrawFreeDrawElement) =>
|
const getFreedrawStreamline = (element: ExcalidrawFreeDrawElement) =>
|
||||||
element.strokeOptions?.streamline ?? DEFAULT_STROKE_STREAMLINE;
|
element.strokeOptions?.streamline ??
|
||||||
|
(element.strokeOptions?.variability === "constant"
|
||||||
|
? CONSTANT_WIDTH_FREEDRAW.STREAMLINE
|
||||||
|
: VARIABLE_WIDTH_FREEDRAW.STREAMLINE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pressure-sensitive (variable width) freedraw outline, rendered with
|
* Pressure-sensitive (variable width) freedraw outline, rendered with
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.base.json",
|
"extends": "../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "../",
|
|
||||||
"outDir": "./dist/types"
|
"outDir": "./dist/types"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "global.d.ts"],
|
"include": ["src/**/*", "global.d.ts"],
|
||||||
|
|||||||
@@ -112,6 +112,9 @@ export const actionClearCanvas = register({
|
|||||||
theme: appState.theme,
|
theme: appState.theme,
|
||||||
penMode: appState.penMode,
|
penMode: appState.penMode,
|
||||||
penDetected: appState.penDetected,
|
penDetected: appState.penDetected,
|
||||||
|
currentItemStrokeVariability: appState.penDetected
|
||||||
|
? "variable"
|
||||||
|
: "constant",
|
||||||
exportBackground: appState.exportBackground,
|
exportBackground: appState.exportBackground,
|
||||||
exportEmbedScene: appState.exportEmbedScene,
|
exportEmbedScene: appState.exportEmbedScene,
|
||||||
gridSize: appState.gridSize,
|
gridSize: appState.gridSize,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
DEFAULT_FONT_SIZE,
|
DEFAULT_FONT_SIZE,
|
||||||
FONT_FAMILY,
|
FONT_FAMILY,
|
||||||
ROUNDNESS,
|
ROUNDNESS,
|
||||||
STROKE_WIDTH_KEYS,
|
|
||||||
VERTICAL_ALIGN,
|
VERTICAL_ALIGN,
|
||||||
KEYS,
|
KEYS,
|
||||||
randomInteger,
|
randomInteger,
|
||||||
@@ -25,6 +24,7 @@ import {
|
|||||||
invariant,
|
invariant,
|
||||||
FONT_SIZES,
|
FONT_SIZES,
|
||||||
type StrokeWidthKey,
|
type StrokeWidthKey,
|
||||||
|
getStrokeWidthKeyForElement,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import { canBecomePolygon, getNonDeletedElements } from "@excalidraw/element";
|
import { canBecomePolygon, getNonDeletedElements } from "@excalidraw/element";
|
||||||
@@ -87,7 +87,6 @@ import type { CaptureUpdateActionType } from "@excalidraw/element";
|
|||||||
|
|
||||||
import { trackEvent } from "../analytics";
|
import { trackEvent } from "../analytics";
|
||||||
import { RadioSelection } from "../components/RadioSelection";
|
import { RadioSelection } from "../components/RadioSelection";
|
||||||
import { ToolButton } from "../components/ToolButton";
|
|
||||||
import { ColorPicker } from "../components/ColorPicker/ColorPicker";
|
import { ColorPicker } from "../components/ColorPicker/ColorPicker";
|
||||||
import { FontPicker } from "../components/FontPicker/FontPicker";
|
import { FontPicker } from "../components/FontPicker/FontPicker";
|
||||||
import { IconPicker } from "../components/IconPicker";
|
import { IconPicker } from "../components/IconPicker";
|
||||||
@@ -555,23 +554,6 @@ export const actionChangeFillStyle = register<ExcalidrawElement["fillStyle"]>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const getStrokeWidthKeyForElement = (
|
|
||||||
element: ExcalidrawElement,
|
|
||||||
): StrokeWidthKey | null => {
|
|
||||||
return (
|
|
||||||
STROKE_WIDTH_KEYS.find(
|
|
||||||
(key) => getStrokeWidthByKey(element.type, key) === element.strokeWidth,
|
|
||||||
) ?? null
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStrokeWidthForElement = (
|
|
||||||
element: ExcalidrawElement,
|
|
||||||
strokeWidthKey: StrokeWidthKey,
|
|
||||||
): ExcalidrawElement["strokeWidth"] => {
|
|
||||||
return getStrokeWidthByKey(element.type, strokeWidthKey);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const actionChangeStrokeWidth = register<StrokeWidthKey>({
|
export const actionChangeStrokeWidth = register<StrokeWidthKey>({
|
||||||
name: "changeStrokeWidth",
|
name: "changeStrokeWidth",
|
||||||
label: "labels.strokeWidth",
|
label: "labels.strokeWidth",
|
||||||
@@ -582,7 +564,7 @@ export const actionChangeStrokeWidth = register<StrokeWidthKey>({
|
|||||||
return {
|
return {
|
||||||
elements: changeProperty(elements, appState, (el) =>
|
elements: changeProperty(elements, appState, (el) =>
|
||||||
newElementWith(el, {
|
newElementWith(el, {
|
||||||
strokeWidth: getStrokeWidthForElement(el, value),
|
strokeWidth: getStrokeWidthByKey(el.type, value),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
appState: { ...appState, currentItemStrokeWidthKey: value },
|
appState: { ...appState, currentItemStrokeWidthKey: value },
|
||||||
@@ -719,25 +701,6 @@ export const actionChangeFreedrawMode = register<StrokeVariability>({
|
|||||||
hasSelection ? null : appState.currentItemStrokeVariability,
|
hasSelection ? null : appState.currentItemStrokeVariability,
|
||||||
) ?? appState.currentItemStrokeVariability;
|
) ?? appState.currentItemStrokeVariability;
|
||||||
|
|
||||||
// in the compact UI the pressure setting is rendered as a single button
|
|
||||||
// that cycles between the two variability modes on click
|
|
||||||
if (data?.cycle) {
|
|
||||||
const isVariable = strokeVariability === "variable";
|
|
||||||
return (
|
|
||||||
<ToolButton
|
|
||||||
type="button"
|
|
||||||
icon={
|
|
||||||
isVariable
|
|
||||||
? strokeVariabilityVariableIcon
|
|
||||||
: strokeVariabilityConstantIcon
|
|
||||||
}
|
|
||||||
title={t("labels.pressure")}
|
|
||||||
aria-label={t("labels.pressure")}
|
|
||||||
onClick={() => updateData(isVariable ? "constant" : "variable")}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{t("labels.pressure")}</legend>
|
<legend>{t("labels.pressure")}</legend>
|
||||||
|
|||||||
@@ -395,17 +395,11 @@ const CombinedShapeProperties = ({
|
|||||||
hasStrokeWidth(element.type),
|
hasStrokeWidth(element.type),
|
||||||
)) &&
|
)) &&
|
||||||
renderAction("changeStrokeWidth")}
|
renderAction("changeStrokeWidth")}
|
||||||
{
|
{(hasFreedrawMode(appState.activeTool.type) ||
|
||||||
/* in compact UI the freedraw pressure setting is rendered as a
|
targetElements.some((element) =>
|
||||||
standalone cycle button in the compact actions list; we render
|
hasFreedrawMode(element.type),
|
||||||
it in the combined properties popup as well for clarity
|
)) &&
|
||||||
*/
|
renderAction("changeFreedrawMode")}
|
||||||
(hasFreedrawMode(appState.activeTool.type) ||
|
|
||||||
targetElements.some((element) =>
|
|
||||||
hasFreedrawMode(element.type),
|
|
||||||
)) &&
|
|
||||||
renderAction("changeFreedrawMode")
|
|
||||||
}
|
|
||||||
{(hasStrokeStyle(appState.activeTool.type) ||
|
{(hasStrokeStyle(appState.activeTool.type) ||
|
||||||
targetElements.some((element) =>
|
targetElements.some((element) =>
|
||||||
hasStrokeStyle(element.type),
|
hasStrokeStyle(element.type),
|
||||||
@@ -838,14 +832,6 @@ export const CompactShapeActions = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Freedraw pressure: standalone button cycling the variability mode */}
|
|
||||||
{(hasFreedrawMode(appState.activeTool.type) ||
|
|
||||||
targetElements.some((element) => hasFreedrawMode(element.type))) && (
|
|
||||||
<div className="compact-action-item">
|
|
||||||
{renderAction("changeFreedrawMode", { cycle: true })}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<CombinedShapeProperties
|
<CombinedShapeProperties
|
||||||
appState={appState}
|
appState={appState}
|
||||||
renderAction={renderAction}
|
renderAction={renderAction}
|
||||||
@@ -1074,11 +1060,6 @@ export const ShapesSwitcher = ({
|
|||||||
const isFullStylesPanel = stylesPanelMode === "full";
|
const isFullStylesPanel = stylesPanelMode === "full";
|
||||||
const isCompactStylesPanel = stylesPanelMode === "compact";
|
const isCompactStylesPanel = stylesPanelMode === "compact";
|
||||||
|
|
||||||
// a pen detected on a tool button's pointer-down, to be applied (enabling
|
|
||||||
// pen mode) only after the tap's `change` has committed — see the tool
|
|
||||||
// button handlers below
|
|
||||||
const pendingPenDetectionRef = useRef(false);
|
|
||||||
|
|
||||||
const SELECTION_TOOLS = [
|
const SELECTION_TOOLS = [
|
||||||
{
|
{
|
||||||
type: "selection",
|
type: "selection",
|
||||||
@@ -1177,13 +1158,8 @@ export const ShapesSwitcher = ({
|
|||||||
aria-keyshortcuts={shortcut}
|
aria-keyshortcuts={shortcut}
|
||||||
data-testid={`toolbar-${value}`}
|
data-testid={`toolbar-${value}`}
|
||||||
onPointerDown={({ pointerType }) => {
|
onPointerDown={({ pointerType }) => {
|
||||||
// Detect the pen here (pointerType is reliable on pointer-down)
|
|
||||||
// but DON'T enable pen mode yet: calling setState mid-gesture
|
|
||||||
// re-renders the controlled radio and, on iOS/iPadOS, aborts
|
|
||||||
// the ensuing click so the tool isn't selected on the first pen
|
|
||||||
// tap. Defer it until the tap's `change` has committed (below).
|
|
||||||
if (!app.state.penDetected && pointerType === "pen") {
|
if (!app.state.penDetected && pointerType === "pen") {
|
||||||
pendingPenDetectionRef.current = true;
|
app.togglePenMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === "selection") {
|
if (value === "selection") {
|
||||||
@@ -1194,21 +1170,16 @@ export const ShapesSwitcher = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onChange={() => {
|
onChange={({ pointerType }) => {
|
||||||
if (app.state.activeTool.type !== value) {
|
if (app.state.activeTool.type !== value) {
|
||||||
trackEvent("toolbar", value, "ui");
|
trackEvent("toolbar", value, "ui");
|
||||||
}
|
}
|
||||||
app.setActiveTool({ type: value });
|
if (value === "image") {
|
||||||
|
app.setActiveTool({
|
||||||
// Apply the pen detection captured on pointer-down now that the
|
type: value,
|
||||||
// tool is selected. rAF keeps the resulting re-render out of the
|
});
|
||||||
// `change` event itself. We rely on the pointer-down detection
|
} else {
|
||||||
// rather than this handler's pointerType because the latter is
|
app.setActiveTool({ type: value });
|
||||||
// unreliable on iOS (its backing ref is cleared before the
|
|
||||||
// delayed click fires).
|
|
||||||
if (pendingPenDetectionRef.current) {
|
|
||||||
pendingPenDetectionRef.current = false;
|
|
||||||
requestAnimationFrame(() => app.togglePenMode(true));
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4308,9 +4308,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
return {
|
return {
|
||||||
penMode: force ?? !prevState.penMode,
|
penMode: force ?? !prevState.penMode,
|
||||||
penDetected: true,
|
penDetected: true,
|
||||||
currentItemStrokeVariability: !prevState.penDetected
|
currentItemStrokeVariability: "variable",
|
||||||
? "variable"
|
|
||||||
: prevState.currentItemStrokeVariability,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -9017,7 +9015,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
strokeOptions: {
|
strokeOptions: {
|
||||||
variability: strokeVariability,
|
variability: strokeVariability,
|
||||||
streamline:
|
streamline:
|
||||||
event.pointerType !== "mouse"
|
strokeVariability === "constant" && event.pointerType !== "mouse"
|
||||||
? DEFAULT_STROKE_STREAMLINE_PRECISE
|
? DEFAULT_STROKE_STREAMLINE_PRECISE
|
||||||
: DEFAULT_STROKE_STREAMLINE,
|
: DEFAULT_STROKE_STREAMLINE,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -120,24 +120,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// on tablet, the pen mode button is rendered as a separate floating button
|
|
||||||
// below the compact actions menu (see LayerUI.tsx)
|
|
||||||
.App-menu_top__left > .ToolIcon__penMode {
|
|
||||||
justify-self: center;
|
|
||||||
|
|
||||||
.ToolIcon__icon {
|
|
||||||
width: var(--lg-button-size);
|
|
||||||
height: var(--lg-button-size);
|
|
||||||
background-color: var(--island-bg-color);
|
|
||||||
box-shadow: var(--shadow-island);
|
|
||||||
}
|
|
||||||
|
|
||||||
// no shadow while pen mode is active (the active fill is enough)
|
|
||||||
.ToolIcon_type_checkbox:checked + .ToolIcon__icon {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.disable-view-mode {
|
.disable-view-mode {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -235,6 +235,8 @@ const LayerUI = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const renderSelectedShapeActions = () => {
|
const renderSelectedShapeActions = () => {
|
||||||
|
const isCompactMode = isCompactStylesPanel;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section
|
<Section
|
||||||
heading="selectedShapeActions"
|
heading="selectedShapeActions"
|
||||||
@@ -242,7 +244,7 @@ const LayerUI = ({
|
|||||||
"transition-left": appState.zenModeEnabled,
|
"transition-left": appState.zenModeEnabled,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{isCompactStylesPanel ? (
|
{isCompactMode ? (
|
||||||
<Island
|
<Island
|
||||||
className={clsx("compact-shape-actions-island")}
|
className={clsx("compact-shape-actions-island")}
|
||||||
padding={0}
|
padding={0}
|
||||||
@@ -310,23 +312,6 @@ const LayerUI = ({
|
|||||||
>
|
>
|
||||||
{shouldRenderSelectedShapeActions && renderSelectedShapeActions()}
|
{shouldRenderSelectedShapeActions && renderSelectedShapeActions()}
|
||||||
</div>
|
</div>
|
||||||
{/* in compact UI the pen mode button lives outside the toolbar, as
|
|
||||||
a separate floating button below the compact actions menu
|
|
||||||
(same as we render it on mobile); shown alongside the compact
|
|
||||||
actions island, i.e. when a drawing tool or elements are
|
|
||||||
selected */}
|
|
||||||
{isCompactStylesPanel &&
|
|
||||||
!appState.viewModeEnabled &&
|
|
||||||
shouldRenderSelectedShapeActions && (
|
|
||||||
<PenModeButton
|
|
||||||
zenModeEnabled={appState.zenModeEnabled}
|
|
||||||
checked={appState.penMode}
|
|
||||||
onChange={() => onPenModeToggle(null)}
|
|
||||||
title={t("toolBar.penMode")}
|
|
||||||
isMobile
|
|
||||||
penDetected={appState.penDetected}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Stack.Col>
|
</Stack.Col>
|
||||||
{!appState.viewModeEnabled &&
|
{!appState.viewModeEnabled &&
|
||||||
appState.openDialog?.name !== "elementLinkSelector" && (
|
appState.openDialog?.name !== "elementLinkSelector" && (
|
||||||
@@ -358,18 +343,13 @@ const LayerUI = ({
|
|||||||
/>
|
/>
|
||||||
{heading}
|
{heading}
|
||||||
<Stack.Row gap={spacing.toolbarInnerRowGap}>
|
<Stack.Row gap={spacing.toolbarInnerRowGap}>
|
||||||
{/* in compact UI the pen mode button is rendered
|
<PenModeButton
|
||||||
as a separate floating button below the compact
|
zenModeEnabled={appState.zenModeEnabled}
|
||||||
actions menu */}
|
checked={appState.penMode}
|
||||||
{!isCompactStylesPanel && (
|
onChange={() => onPenModeToggle(null)}
|
||||||
<PenModeButton
|
title={t("toolBar.penMode")}
|
||||||
zenModeEnabled={appState.zenModeEnabled}
|
penDetected={appState.penDetected}
|
||||||
checked={appState.penMode}
|
/>
|
||||||
onChange={() => onPenModeToggle(null)}
|
|
||||||
title={t("toolBar.penMode")}
|
|
||||||
penDetected={appState.penDetected}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<LockButton
|
<LockButton
|
||||||
checked={appState.activeTool.locked}
|
checked={appState.activeTool.locked}
|
||||||
onChange={onLockToggle}
|
onChange={onLockToggle}
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ import {
|
|||||||
getSizeFromPoints,
|
getSizeFromPoints,
|
||||||
normalizeLink,
|
normalizeLink,
|
||||||
getLineHeight,
|
getLineHeight,
|
||||||
STROKE_WIDTH,
|
|
||||||
STROKE_WIDTH_KEYS,
|
STROKE_WIDTH_KEYS,
|
||||||
type StrokeWidthKey,
|
STROKE_WIDTH,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
import {
|
import {
|
||||||
calculateFixedPointForNonElbowArrowBinding,
|
calculateFixedPointForNonElbowArrowBinding,
|
||||||
@@ -58,6 +57,8 @@ import { getNormalizedDimensions } from "@excalidraw/element";
|
|||||||
|
|
||||||
import { isInvisiblySmallElement } from "@excalidraw/element";
|
import { isInvisiblySmallElement } from "@excalidraw/element";
|
||||||
|
|
||||||
|
import type { StrokeWidthKey } from "@excalidraw/common";
|
||||||
|
|
||||||
import type { LocalPoint, Radians } from "@excalidraw/math";
|
import type { LocalPoint, Radians } from "@excalidraw/math";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.base.json",
|
"extends": "../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "../",
|
|
||||||
"outDir": "./dist/types"
|
"outDir": "./dist/types"
|
||||||
},
|
},
|
||||||
"include": ["**/*"],
|
"include": ["**/*"],
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.base.json",
|
"extends": "../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist/types",
|
"outDir": "./dist/types"
|
||||||
"rootDir": "../"
|
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "global.d.ts"],
|
"include": ["src/**/*", "global.d.ts"],
|
||||||
"exclude": ["**/*.test.*", "tests", "types", "examples", "dist"]
|
"exclude": ["**/*.test.*", "tests", "types", "examples", "dist"]
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"description": "Generate outline for laser pointer tool",
|
"description": "Generate outline for laser pointer tool",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "./dist/types/laser-pointer/src/index.d.ts",
|
"types": "./dist/types/index.d.ts",
|
||||||
"main": "./dist/prod/index.js",
|
"main": "./dist/prod/index.js",
|
||||||
"module": "./dist/prod/index.js",
|
"module": "./dist/prod/index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./dist/types/laser-pointer/src/index.d.ts",
|
"types": "./dist/types/index.d.ts",
|
||||||
"development": "./dist/dev/index.js",
|
"development": "./dist/dev/index.js",
|
||||||
"production": "./dist/prod/index.js",
|
"production": "./dist/prod/index.js",
|
||||||
"default": "./dist/prod/index.js"
|
"default": "./dist/prod/index.js"
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.base.json",
|
"extends": "../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist/types",
|
"outDir": "./dist/types"
|
||||||
"rootDir": "../"
|
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "global.d.ts"],
|
"include": ["src/**/*", "global.d.ts"],
|
||||||
"exclude": ["**/*.test.*", "tests", "types", "examples", "dist"]
|
"exclude": ["**/*.test.*", "tests", "types", "examples", "dist"]
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.base.json",
|
"extends": "../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "../",
|
|
||||||
"outDir": "./dist/types"
|
"outDir": "./dist/types"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "global.d.ts"],
|
"include": ["src/**/*", "global.d.ts"],
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"declaration": true,
|
"declaration": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "Node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"emitDeclarationOnly": true,
|
"emitDeclarationOnly": true,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.base.json",
|
"extends": "../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "../",
|
|
||||||
"outDir": "./dist/types"
|
"outDir": "./dist/types"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "global.d.ts"],
|
"include": ["src/**/*", "global.d.ts"],
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@
|
|||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user