diff --git a/src/frontend/index.scss b/src/frontend/index.scss index 9807f32..af2312b 100644 --- a/src/frontend/index.scss +++ b/src/frontend/index.scss @@ -7,6 +7,11 @@ font-display: swap; } +/* CSS Variables */ +:root { + --embeddable-pointer-events: all; +} + /* Override Excalidraw styles */ body { @@ -77,6 +82,6 @@ body { .excalidraw__embeddable__outer { /* 3rd layer */ padding: 0px !important; - pointer-events: all !important; + pointer-events: var(--embeddable-pointer-events, all) !important; display: flex; } diff --git a/src/frontend/src/CustomEmbeddableRenderer.tsx b/src/frontend/src/CustomEmbeddableRenderer.tsx index ee65e42..9e6bb87 100644 --- a/src/frontend/src/CustomEmbeddableRenderer.tsx +++ b/src/frontend/src/CustomEmbeddableRenderer.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { debounce } from './utils/debounce'; import type { NonDeleted, ExcalidrawEmbeddableElement } from '@atyrode/excalidraw/element/types'; import type { AppState } from '@atyrode/excalidraw/types'; import { @@ -85,3 +86,24 @@ export const renderCustomEmbeddable = ( ); } }; + +// Track scrolling state +let isScrolling = false; + +export const lockEmbeddables = () => { + if (!isScrolling) { + isScrolling = true; + // Set pointer-events to none during scrolling + document.documentElement.style.setProperty('--embeddable-pointer-events', 'none'); + } + + // Reset the pointer-events after scrolling stops + debouncedScrollEnd(); +}; + +// Create a debounced function to detect when scrolling ends +const debouncedScrollEnd = debounce(() => { + isScrolling = false; + // Set pointer-events back to all when not scrolling + document.documentElement.style.setProperty('--embeddable-pointer-events', 'all'); +}, 150); // 150ms debounce seems reasonable, but can be adjusted as needed diff --git a/src/frontend/src/ExcalidrawWrapper.tsx b/src/frontend/src/ExcalidrawWrapper.tsx index 0d084c8..b2095e1 100644 --- a/src/frontend/src/ExcalidrawWrapper.tsx +++ b/src/frontend/src/ExcalidrawWrapper.tsx @@ -5,7 +5,7 @@ import type { ExcalidrawImperativeAPI } from '@atyrode/excalidraw/types'; import type { NonDeletedExcalidrawElement } from '@atyrode/excalidraw/element/types'; import type { AppState } from '@atyrode/excalidraw/types'; import { MainMenuConfig } from './ui/MainMenu'; -import { renderCustomEmbeddable } from './CustomEmbeddableRenderer'; +import { lockEmbeddables, renderCustomEmbeddable } from './CustomEmbeddableRenderer'; import AuthDialog from './ui/AuthDialog'; import BackupsModal from './ui/BackupsDialog'; @@ -25,6 +25,7 @@ interface ExcalidrawWrapperProps { setExcalidrawAPI: (api: ExcalidrawImperativeAPI) => void; initialData?: any; onChange: (elements: NonDeletedExcalidrawElement[], state: AppState) => void; + onScrollChange: (scrollX: number, scrollY: number) => void; MainMenu: any; renderTopRightUI?: () => React.ReactNode; isAuthenticated?: boolean | null; @@ -37,6 +38,7 @@ export const ExcalidrawWrapper: React.FC = ({ setExcalidrawAPI, initialData, onChange, + onScrollChange, MainMenu, renderTopRightUI, isAuthenticated = null, @@ -80,6 +82,7 @@ export const ExcalidrawWrapper: React.FC = ({ initialData: initialData ?? defaultInitialData, onChange: onChange, name: "Pad.ws", + onScrollChange: lockEmbeddables, validateEmbeddable: true, renderEmbeddable: (element, appState) => renderCustomEmbeddable(element, appState, excalidrawAPI), renderTopRightUI: renderTopRightUI ?? (() => (