소스 검색

Add onElementClick and export sceneCoordsToViewportCoords

ad1992 3 년 전
부모
커밋
5f4a5b1789
4개의 변경된 파일71개의 추가작업 그리고 9개의 파일을 삭제
  1. 21 6
      src/components/App.tsx
  2. 42 3
      src/packages/excalidraw/example/App.js
  3. 4 0
      src/packages/excalidraw/index.tsx
  4. 4 0
      src/types.ts

+ 21 - 6
src/components/App.tsx

@@ -3091,15 +3091,13 @@ class App extends React.Component<AppProps, AppState> {
     event: React.PointerEvent<HTMLCanvasElement>,
   ) => {
     this.lastPointerUp = event;
-    if (this.deviceType.isTouchScreen) {
+    let hitElement;
+    if (this.deviceType.isTouchScreen || this.props.onElementClick) {
       const scenePointer = viewportCoordsToSceneCoords(
         { clientX: event.clientX, clientY: event.clientY },
         this.state,
       );
-      const hitElement = this.getElementAtPosition(
-        scenePointer.x,
-        scenePointer.y,
-      );
+      hitElement = this.getElementAtPosition(scenePointer.x, scenePointer.y);
       this.hitLinkElement = this.getElementLinkAtPosition(
         scenePointer,
         hitElement,
@@ -3112,6 +3110,23 @@ class App extends React.Component<AppProps, AppState> {
       this.redirectToLink(event, this.deviceType.isTouchScreen);
     }
 
+    if (
+      this.state.elementType === "selection" &&
+      this.props.onElementClick &&
+      hitElement
+    ) {
+      const threshold = 5;
+      const isSinglePointClick =
+        distance2d(
+          this.lastPointerDown!.clientX,
+          this.lastPointerDown!.clientY,
+          this.lastPointerUp!.clientX,
+          this.lastPointerUp!.clientY,
+        ) <= threshold;
+      if (isSinglePointClick) {
+        this.props.onElementClick(hitElement, event);
+      }
+    }
     this.removePointer(event);
   };
 
@@ -4517,6 +4532,7 @@ class App extends React.Component<AppProps, AppState> {
       // Code below handles selection when element(s) weren't
       // drag or added to selection on pointer down phase.
       const hitElement = pointerDownState.hit.element;
+
       if (isEraserActive(this.state)) {
         const draggedDistance = distance2d(
           this.lastPointerDown!.clientX,
@@ -4550,7 +4566,6 @@ class App extends React.Component<AppProps, AppState> {
       } else if (Object.keys(pointerDownState.elementIdsToErase).length) {
         this.restoreReadyToEraseElements(pointerDownState);
       }
-
       if (
         hitElement &&
         !pointerDownState.drag.hasOccurred &&

+ 42 - 3
src/packages/excalidraw/example/App.js

@@ -9,7 +9,12 @@ import { MIME_TYPES } from "../../../constants";
 
 // This is so that we use the bundled excalidraw.development.js file instead
 // of the actual source code
-const { exportToCanvas, exportToSvg, exportToBlob } = window.Excalidraw;
+const {
+  exportToCanvas,
+  exportToSvg,
+  exportToBlob,
+  sceneCoordsToViewportCoords,
+} = window.Excalidraw;
 const Excalidraw = window.Excalidraw.default;
 
 const STAR_SVG = (
@@ -56,7 +61,7 @@ const renderFooter = () => {
 
 export default function App() {
   const excalidrawRef = useRef(null);
-
+  const excalidrawWrapperRef = useRef(null);
   const [viewModeEnabled, setViewModeEnabled] = useState(false);
   const [zenModeEnabled, setZenModeEnabled] = useState(false);
   const [gridModeEnabled, setGridModeEnabled] = useState(false);
@@ -200,6 +205,39 @@ export default function App() {
       },
     ];
   };
+
+  const onElementClick = (element) => {
+    if (element.type === "custom" && element.customType === "comment") {
+      const { x: viewPortX, y: viewPortY } = sceneCoordsToViewportCoords(
+        {
+          sceneX: element.x,
+          sceneY: element.y,
+        },
+        excalidrawRef.current.getAppState(),
+      );
+      const textarea = document.createElement("textarea");
+      Object.assign(textarea.style, {
+        position: "absolute",
+        display: "inline-block",
+        left: `${viewPortX + element.width / 2}px`,
+        top: `${viewPortY + element.height / 2}px`,
+        height: `${100}px`,
+        width: `${100}px`,
+        zIndex: 10,
+        className: "comment-textarea",
+        whiteSpace: "pre-wrap",
+        fontSize: "13px",
+      });
+      textarea.placeholder = "Start typing your comments";
+      textarea.onblur = () => {
+        textarea.remove();
+      };
+      excalidrawWrapperRef.current
+        .querySelector(".excalidraw")
+        .append(textarea);
+      textarea.focus();
+    }
+  };
   return (
     <div className="App">
       <h1> Excalidraw Example</h1>
@@ -273,7 +311,7 @@ export default function App() {
             Switch to Dark Theme
           </label>
         </div>
-        <div className="excalidraw-wrapper">
+        <div className="excalidraw-wrapper" ref={excalidrawWrapperRef}>
           <Excalidraw
             ref={excalidrawRef}
             initialData={initialStatePromiseRef.current.promise}
@@ -295,6 +333,7 @@ export default function App() {
             onLinkOpen={onLinkOpen}
             renderCustomElementWidget={renderCustomElementWidget}
             customElementsConfig={getCustomElementsConfig()}
+            onElementClick={onElementClick}
           />
         </div>
 

+ 4 - 0
src/packages/excalidraw/index.tsx

@@ -40,6 +40,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
     generateIdForFile,
     onLinkOpen,
     renderCustomElementWidget,
+    onElementClick,
   } = props;
 
   const canvasActions = props.UIOptions?.canvasActions;
@@ -109,6 +110,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
         onLinkOpen={onLinkOpen}
         renderCustomElementWidget={renderCustomElementWidget}
         customElementsConfig={customElementsConfig}
+        onElementClick={onElementClick}
       />
     </InitializeApp>
   );
@@ -207,3 +209,5 @@ export {
   newElementWith,
   bumpVersion,
 } from "../../element/mutateElement";
+
+export { sceneCoordsToViewportCoords } from "../../utils";

+ 4 - 0
src/types.ts

@@ -263,6 +263,10 @@ export interface ExcalidrawProps {
   ) => void;
   renderCustomElementWidget?: (appState: AppState) => void;
   customElementsConfig?: CustomElementConfig[];
+  onElementClick: (
+    element: NonDeleted<ExcalidrawElement>,
+    event: React.PointerEvent<HTMLCanvasElement>,
+  ) => void;
 }
 
 export type SceneData = {