diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts index cd3bd7a15..a7da91434 100644 --- a/packages/common/src/constants.ts +++ b/packages/common/src/constants.ts @@ -438,6 +438,7 @@ export const TOOL_TYPE = { magicframe: "magicframe", embeddable: "embeddable", laser: "laser", + blur: "blur", } as const; export const EDITOR_LS_KEYS = { diff --git a/packages/element/src/Shape.ts b/packages/element/src/Shape.ts index 4def41957..1f6de9ec8 100644 --- a/packages/element/src/Shape.ts +++ b/packages/element/src/Shape.ts @@ -98,6 +98,7 @@ export const generateRoughOptions = ( case "iframe": case "embeddable": case "diamond": + case "blur": case "ellipse": { options.fillStyle = element.fillStyle; options.fill = isTransparent(element.backgroundColor) @@ -326,6 +327,7 @@ export const _generateElementShape = ( switch (element.type) { case "rectangle": case "iframe": + case "blur": case "embeddable": { let shape: ElementShapes[typeof element.type]; // this is for rendering the stroke/bg of the embeddable, especially diff --git a/packages/element/src/comparisons.ts b/packages/element/src/comparisons.ts index 75fac889d..3aee07b05 100644 --- a/packages/element/src/comparisons.ts +++ b/packages/element/src/comparisons.ts @@ -10,7 +10,10 @@ export const hasBackground = (type: ElementOrToolType) => type === "freedraw"; export const hasStrokeColor = (type: ElementOrToolType) => - type !== "image" && type !== "frame" && type !== "magicframe"; + type !== "image" && + type !== "frame" && + type !== "magicframe" && + type !== "blur"; export const hasStrokeWidth = (type: ElementOrToolType) => type === "rectangle" || @@ -39,6 +42,10 @@ export const canChangeRoundness = (type: ElementOrToolType) => type === "diamond" || type === "image"; +export const canChangeBlur = (type: ElementOrToolType) => type === "blur"; + +export const canChangeLayer = (type: ElementOrToolType) => type !== "blur"; + export const toolIsArrow = (type: ElementOrToolType) => type === "arrow"; export const canHaveArrowheads = (type: ElementOrToolType) => type === "arrow"; diff --git a/packages/element/src/newElement.ts b/packages/element/src/newElement.ts index 53a2f05ae..5b9d252df 100644 --- a/packages/element/src/newElement.ts +++ b/packages/element/src/newElement.ts @@ -46,6 +46,7 @@ import type { ExcalidrawArrowElement, FixedSegment, ExcalidrawElbowArrowElement, + ExcalidrawBlurElement, } from "./types"; export type ElementConstructorOpts = MarkOptional< @@ -212,6 +213,25 @@ export const newMagicFrameElement = ( return frameElement; }; +export const newBlurElement = ( + opts: { + blur: number; + } & ElementConstructorOpts, +): NonDeleted => { + const blurElement = newElementWith( + { + ..._newElementBase("blur", opts), + type: "blur", + blur: opts.blur, + fillStyle: "solid", + backgroundColor: "#000000", + }, + {}, + ); + + return blurElement; +}; + /** computes element x/y offset based on textAlign/verticalAlign */ const getTextElementPositionOffsets = ( opts: { diff --git a/packages/element/src/renderElement.ts b/packages/element/src/renderElement.ts index c8091e8ed..36f6d3bdf 100644 --- a/packages/element/src/renderElement.ts +++ b/packages/element/src/renderElement.ts @@ -404,6 +404,8 @@ const drawElementOnCanvas = ( rc.draw(ShapeCache.get(element)!); break; } + case "blur": + break; case "arrow": case "line": { context.lineJoin = "round"; @@ -814,6 +816,7 @@ export const renderElement = ( case "image": case "text": case "iframe": + case "blur": case "embeddable": { // TODO investigate if we can do this in situ. Right now we need to call // beforehand because math helpers (such as getElementAbsoluteCoords) diff --git a/packages/element/src/shapes.ts b/packages/element/src/shapes.ts index 96542c538..cd690e728 100644 --- a/packages/element/src/shapes.ts +++ b/packages/element/src/shapes.ts @@ -58,6 +58,7 @@ export const getElementShape = ( case "embeddable": case "image": case "iframe": + case "blur": case "text": case "selection": return getPolygonShape(element); diff --git a/packages/element/src/typeChecks.ts b/packages/element/src/typeChecks.ts index 54619726d..9369b6c60 100644 --- a/packages/element/src/typeChecks.ts +++ b/packages/element/src/typeChecks.ts @@ -28,6 +28,7 @@ import type { PointBinding, FixedPointBinding, ExcalidrawFlowchartNodeElement, + ExcalidrawBlurElement, } from "./types"; export const isInitializedImageElement = ( @@ -107,6 +108,12 @@ export const isLinearElement = ( return element != null && isLinearElementType(element.type); }; +export const isBlurElement = ( + element?: ExcalidrawElement | null, +): element is ExcalidrawBlurElement => { + return element != null && isBlurElementType(element.type); +}; + export const isArrowElement = ( element?: ExcalidrawElement | null, ): element is ExcalidrawArrowElement => { @@ -127,6 +134,10 @@ export const isLinearElementType = ( ); }; +export const isBlurElementType = (elementType: ElementOrToolType): boolean => { + return elementType === "blur"; +}; + export const isBindingElement = ( element?: ExcalidrawElement | null, includeLocked = true, @@ -231,6 +242,7 @@ export const isExcalidrawElement = ( case "frame": case "magicframe": case "image": + case "blur": case "selection": { return true; } diff --git a/packages/element/src/types.ts b/packages/element/src/types.ts index 3b40135d5..fd00f8a28 100644 --- a/packages/element/src/types.ts +++ b/packages/element/src/types.ts @@ -89,6 +89,11 @@ export type ExcalidrawRectangleElement = _ExcalidrawElementBase & { type: "rectangle"; }; +export type ExcalidrawBlurElement = _ExcalidrawElementBase & { + type: "blur"; + blur: number; +}; + export type ExcalidrawDiamondElement = _ExcalidrawElementBase & { type: "diamond"; }; @@ -212,7 +217,8 @@ export type ExcalidrawElement = | ExcalidrawFrameElement | ExcalidrawMagicFrameElement | ExcalidrawIframeElement - | ExcalidrawEmbeddableElement; + | ExcalidrawEmbeddableElement + | ExcalidrawBlurElement; export type ExcalidrawNonSelectionElement = Exclude< ExcalidrawElement, diff --git a/packages/excalidraw/actions/actionAlign.tsx b/packages/excalidraw/actions/actionAlign.tsx index 46023d61e..3226cc815 100644 --- a/packages/excalidraw/actions/actionAlign.tsx +++ b/packages/excalidraw/actions/actionAlign.tsx @@ -8,6 +8,8 @@ import { KEYS, arrayToMap, getShortcutKey } from "@excalidraw/common"; import { alignElements } from "@excalidraw/element/align"; +import { useContext } from "react"; + import type { ExcalidrawElement } from "@excalidraw/element/types"; import type { Alignment } from "@excalidraw/element/align"; @@ -27,9 +29,14 @@ import { t } from "../i18n"; import { isSomeElementSelected } from "../scene"; import { CaptureUpdateAction } from "../store"; -import { register } from "./register"; +import { + ExcalidrawPropsCustomOptionsContext, + type AppClassProperties, + type AppState, + type UIAppState, +} from "../types"; -import type { AppClassProperties, AppState, UIAppState } from "../types"; +import { register } from "./register"; export const alignActionsPredicate = ( appState: UIAppState, @@ -87,19 +94,40 @@ export const actionAlignTop = register({ }, keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_UP, - PanelComponent: ({ elements, appState, updateData, app }) => ( -