aboutsummaryrefslogtreecommitdiffstats
path: root/packages/excalidraw/scene/scrollbars.ts
diff options
context:
space:
mode:
authorkj_sh6042026-03-15 16:19:35 -0400
committerkj_sh6042026-03-15 16:19:35 -0400
commit6ec259a0e71174651bae95d4628138bf6fd68742 (patch)
tree5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/scene/scrollbars.ts
parent16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff)
refactor: packages/
Diffstat (limited to 'packages/excalidraw/scene/scrollbars.ts')
-rw-r--r--packages/excalidraw/scene/scrollbars.ts124
1 files changed, 124 insertions, 0 deletions
diff --git a/packages/excalidraw/scene/scrollbars.ts b/packages/excalidraw/scene/scrollbars.ts
new file mode 100644
index 0000000..70f7033
--- /dev/null
+++ b/packages/excalidraw/scene/scrollbars.ts
@@ -0,0 +1,124 @@
+import { getCommonBounds } from "../element";
+import type { InteractiveCanvasAppState } from "../types";
+import type { ScrollBars } from "./types";
+import { getGlobalCSSVariable } from "../utils";
+import { getLanguage } from "../i18n";
+import type { ExcalidrawElement } from "../element/types";
+
+export const SCROLLBAR_MARGIN = 4;
+export const SCROLLBAR_WIDTH = 6;
+export const SCROLLBAR_COLOR = "rgba(0,0,0,0.3)";
+
+export const getScrollBars = (
+ elements: readonly ExcalidrawElement[],
+ viewportWidth: number,
+ viewportHeight: number,
+ appState: InteractiveCanvasAppState,
+): ScrollBars => {
+ if (!elements.length) {
+ return {
+ horizontal: null,
+ vertical: null,
+ };
+ }
+ // This is the bounding box of all the elements
+ const [elementsMinX, elementsMinY, elementsMaxX, elementsMaxY] =
+ getCommonBounds(elements);
+
+ // Apply zoom
+ const viewportWidthWithZoom = viewportWidth / appState.zoom.value;
+ const viewportHeightWithZoom = viewportHeight / appState.zoom.value;
+
+ const viewportWidthDiff = viewportWidth - viewportWidthWithZoom;
+ const viewportHeightDiff = viewportHeight - viewportHeightWithZoom;
+
+ const safeArea = {
+ top: parseInt(getGlobalCSSVariable("sat")) || 0,
+ bottom: parseInt(getGlobalCSSVariable("sab")) || 0,
+ left: parseInt(getGlobalCSSVariable("sal")) || 0,
+ right: parseInt(getGlobalCSSVariable("sar")) || 0,
+ };
+
+ const isRTL = getLanguage().rtl;
+
+ // The viewport is the rectangle currently visible for the user
+ const viewportMinX =
+ -appState.scrollX + viewportWidthDiff / 2 + safeArea.left;
+ const viewportMinY =
+ -appState.scrollY + viewportHeightDiff / 2 + safeArea.top;
+ const viewportMaxX = viewportMinX + viewportWidthWithZoom - safeArea.right;
+ const viewportMaxY = viewportMinY + viewportHeightWithZoom - safeArea.bottom;
+
+ // The scene is the bounding box of both the elements and viewport
+ const sceneMinX = Math.min(elementsMinX, viewportMinX);
+ const sceneMinY = Math.min(elementsMinY, viewportMinY);
+ const sceneMaxX = Math.max(elementsMaxX, viewportMaxX);
+ const sceneMaxY = Math.max(elementsMaxY, viewportMaxY);
+
+ // The scrollbar represents where the viewport is in relationship to the scene
+
+ return {
+ horizontal:
+ viewportMinX === sceneMinX && viewportMaxX === sceneMaxX
+ ? null
+ : {
+ x:
+ Math.max(safeArea.left, SCROLLBAR_MARGIN) +
+ ((viewportMinX - sceneMinX) / (sceneMaxX - sceneMinX)) *
+ viewportWidth,
+ y:
+ viewportHeight -
+ SCROLLBAR_WIDTH -
+ Math.max(SCROLLBAR_MARGIN, safeArea.bottom),
+ width:
+ ((viewportMaxX - viewportMinX) / (sceneMaxX - sceneMinX)) *
+ viewportWidth -
+ Math.max(SCROLLBAR_MARGIN * 2, safeArea.left + safeArea.right),
+ height: SCROLLBAR_WIDTH,
+ },
+ vertical:
+ viewportMinY === sceneMinY && viewportMaxY === sceneMaxY
+ ? null
+ : {
+ x: isRTL
+ ? Math.max(safeArea.left, SCROLLBAR_MARGIN)
+ : viewportWidth -
+ SCROLLBAR_WIDTH -
+ Math.max(safeArea.right, SCROLLBAR_MARGIN),
+ y:
+ ((viewportMinY - sceneMinY) / (sceneMaxY - sceneMinY)) *
+ viewportHeight +
+ Math.max(safeArea.top, SCROLLBAR_MARGIN),
+ width: SCROLLBAR_WIDTH,
+ height:
+ ((viewportMaxY - viewportMinY) / (sceneMaxY - sceneMinY)) *
+ viewportHeight -
+ Math.max(SCROLLBAR_MARGIN * 2, safeArea.top + safeArea.bottom),
+ },
+ };
+};
+
+export const isOverScrollBars = (
+ scrollBars: ScrollBars,
+ x: number,
+ y: number,
+): {
+ isOverEither: boolean;
+ isOverHorizontal: boolean;
+ isOverVertical: boolean;
+} => {
+ const [isOverHorizontal, isOverVertical] = [
+ scrollBars.horizontal,
+ scrollBars.vertical,
+ ].map((scrollBar) => {
+ return (
+ scrollBar != null &&
+ scrollBar.x <= x &&
+ x <= scrollBar.x + scrollBar.width &&
+ scrollBar.y <= y &&
+ y <= scrollBar.y + scrollBar.height
+ );
+ });
+ const isOverEither = isOverHorizontal || isOverVertical;
+ return { isOverEither, isOverHorizontal, isOverVertical };
+};