@cratis/components 4.21.1 → 4.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -9
- package/compat-manifest.json +16 -16
- package/dist/esm/Canvas/Canvas.d.ts +12 -81
- package/dist/esm/Canvas/Canvas.d.ts.map +1 -1
- package/dist/esm/Canvas/Canvas.js +83 -676
- package/dist/esm/Canvas/Canvas.js.map +1 -1
- package/dist/esm/Canvas/CanvasCaptureAttributes.d.ts +10 -0
- package/dist/esm/Canvas/CanvasCaptureAttributes.d.ts.map +1 -0
- package/dist/esm/Canvas/CanvasCaptureAttributes.js +4 -0
- package/dist/esm/Canvas/CanvasCaptureAttributes.js.map +1 -0
- package/dist/esm/Canvas/CanvasContext.d.ts +9 -0
- package/dist/esm/Canvas/CanvasContext.d.ts.map +1 -0
- package/dist/esm/Canvas/CanvasContext.js +4 -0
- package/dist/esm/Canvas/CanvasContext.js.map +1 -0
- package/dist/esm/Canvas/CanvasHandle.d.ts +13 -0
- package/dist/esm/Canvas/CanvasHandle.d.ts.map +1 -0
- package/dist/esm/Canvas/CanvasHandle.js +4 -0
- package/dist/esm/Canvas/CanvasHandle.js.map +1 -0
- package/dist/esm/Canvas/CanvasItem.js +4 -1
- package/dist/esm/Canvas/CanvasItem.js.map +1 -1
- package/dist/esm/Canvas/CanvasItemData.d.ts +10 -0
- package/dist/esm/Canvas/CanvasItemData.d.ts.map +1 -0
- package/dist/esm/Canvas/CanvasItemData.js +4 -0
- package/dist/esm/Canvas/CanvasItemData.js.map +1 -0
- package/dist/esm/Canvas/CanvasItemRegistryContext.d.ts +5 -0
- package/dist/esm/Canvas/CanvasItemRegistryContext.d.ts.map +1 -0
- package/dist/esm/Canvas/CanvasItemRegistryContext.js +6 -0
- package/dist/esm/Canvas/CanvasItemRegistryContext.js.map +1 -0
- package/dist/esm/Canvas/CanvasItemRegistryContextValue.d.ts +26 -0
- package/dist/esm/Canvas/CanvasItemRegistryContextValue.d.ts.map +1 -0
- package/dist/esm/Canvas/CanvasItemRegistryContextValue.js +4 -0
- package/dist/esm/Canvas/CanvasItemRegistryContextValue.js.map +1 -0
- package/dist/esm/Canvas/CanvasItemRegistryEntry.d.ts +19 -0
- package/dist/esm/Canvas/CanvasItemRegistryEntry.d.ts.map +1 -0
- package/dist/esm/Canvas/CanvasItemRegistryEntry.js +4 -0
- package/dist/esm/Canvas/CanvasItemRegistryEntry.js.map +1 -0
- package/dist/esm/Canvas/WebKitGestureEvent.d.ts +13 -0
- package/dist/esm/Canvas/WebKitGestureEvent.d.ts.map +1 -0
- package/dist/esm/Canvas/WebKitGestureEvent.js +4 -0
- package/dist/esm/Canvas/WebKitGestureEvent.js.map +1 -0
- package/dist/esm/Canvas/canvasEasing.d.ts +8 -0
- package/dist/esm/Canvas/canvasEasing.d.ts.map +1 -0
- package/dist/esm/Canvas/canvasEasing.js +8 -0
- package/dist/esm/Canvas/canvasEasing.js.map +1 -0
- package/dist/esm/Canvas/canvasGestureTuning.d.ts +6 -0
- package/dist/esm/Canvas/canvasGestureTuning.d.ts.map +1 -0
- package/dist/esm/Canvas/canvasGestureTuning.js +7 -0
- package/dist/esm/Canvas/canvasGestureTuning.js.map +1 -0
- package/dist/esm/Canvas/index.js +2 -1
- package/dist/esm/Canvas/index.js.map +1 -1
- package/dist/esm/Canvas/shapes/Region/Region.js +4 -1
- package/dist/esm/Canvas/shapes/Region/Region.js.map +1 -1
- package/dist/esm/Canvas/useCanvasCamera.d.ts +37 -0
- package/dist/esm/Canvas/useCanvasCamera.d.ts.map +1 -0
- package/dist/esm/Canvas/useCanvasCamera.js +79 -0
- package/dist/esm/Canvas/useCanvasCamera.js.map +1 -0
- package/dist/esm/Canvas/useCanvasItemRegistry.d.ts +14 -0
- package/dist/esm/Canvas/useCanvasItemRegistry.d.ts.map +1 -0
- package/dist/esm/Canvas/useCanvasItemRegistry.js +59 -0
- package/dist/esm/Canvas/useCanvasItemRegistry.js.map +1 -0
- package/dist/esm/Canvas/useCanvasNavigation.d.ts +36 -0
- package/dist/esm/Canvas/useCanvasNavigation.d.ts.map +1 -0
- package/dist/esm/Canvas/useCanvasNavigation.js +186 -0
- package/dist/esm/Canvas/useCanvasNavigation.js.map +1 -0
- package/dist/esm/Canvas/useCanvasPointerGestures.d.ts +34 -0
- package/dist/esm/Canvas/useCanvasPointerGestures.d.ts.map +1 -0
- package/dist/esm/Canvas/useCanvasPointerGestures.js +178 -0
- package/dist/esm/Canvas/useCanvasPointerGestures.js.map +1 -0
- package/dist/esm/Canvas/useCanvasWheelZoom.d.ts +23 -0
- package/dist/esm/Canvas/useCanvasWheelZoom.d.ts.map +1 -0
- package/dist/esm/Canvas/useCanvasWheelZoom.js +146 -0
- package/dist/esm/Canvas/useCanvasWheelZoom.js.map +1 -0
- package/dist/esm/Canvas/useLatestRef.d.ts +8 -0
- package/dist/esm/Canvas/useLatestRef.d.ts.map +1 -0
- package/dist/esm/Canvas/useLatestRef.js +12 -0
- package/dist/esm/Canvas/useLatestRef.js.map +1 -0
- package/dist/esm/Canvas/usePixiApplication.d.ts +32 -0
- package/dist/esm/Canvas/usePixiApplication.d.ts.map +1 -0
- package/dist/esm/Canvas/usePixiApplication.js +92 -0
- package/dist/esm/Canvas/usePixiApplication.js.map +1 -0
- package/dist/esm/Canvas/usePixiItems.d.ts +19 -0
- package/dist/esm/Canvas/usePixiItems.d.ts.map +1 -0
- package/dist/esm/Canvas/usePixiItems.js +49 -0
- package/dist/esm/Canvas/usePixiItems.js.map +1 -0
- package/dist/esm/Chat/ChatSidebar.d.ts.map +1 -1
- package/dist/esm/Chat/ChatSidebar.js +1 -1
- package/dist/esm/Chat/ChatSidebar.js.map +1 -1
- package/dist/esm/ClassNames/classNames.d.ts +7 -0
- package/dist/esm/ClassNames/classNames.d.ts.map +1 -0
- package/dist/esm/ClassNames/classNames.js.map +1 -0
- package/dist/esm/Common/Breadcrumbs.d.ts.map +1 -1
- package/dist/esm/Common/Breadcrumbs.js +1 -1
- package/dist/esm/Common/Breadcrumbs.js.map +1 -1
- package/dist/esm/Common/ComboBox.d.ts.map +1 -1
- package/dist/esm/Common/ComboBox.js +1 -1
- package/dist/esm/Common/ComboBox.js.map +1 -1
- package/dist/esm/Common/DatePickerInputImplementation.d.ts.map +1 -1
- package/dist/esm/Common/DatePickerInputImplementation.js +1 -1
- package/dist/esm/Common/DatePickerInputImplementation.js.map +1 -1
- package/dist/esm/Common/NumberInput.d.ts.map +1 -1
- package/dist/esm/Common/NumberInput.js +1 -1
- package/dist/esm/Common/NumberInput.js.map +1 -1
- package/dist/esm/Common/Tabs.d.ts.map +1 -1
- package/dist/esm/Common/Tabs.js +1 -1
- package/dist/esm/Common/Tabs.js.map +1 -1
- package/dist/esm/Common/TagGroup.d.ts.map +1 -1
- package/dist/esm/Common/TagGroup.js +1 -1
- package/dist/esm/Common/TagGroup.js.map +1 -1
- package/dist/esm/Common/ToggleGroup.d.ts.map +1 -1
- package/dist/esm/Common/ToggleGroup.js +1 -1
- package/dist/esm/Common/ToggleGroup.js.map +1 -1
- package/dist/esm/DataTables/ColumnFilterMenu.d.ts.map +1 -1
- package/dist/esm/DataTables/ColumnFilterMenu.js +1 -1
- package/dist/esm/DataTables/ColumnFilterMenu.js.map +1 -1
- package/dist/esm/DataTables/DataTableBody.js +1 -1
- package/dist/esm/DataTables/DataTableBody.js.map +1 -1
- package/dist/esm/DataTables/DataTableCore.js +1 -1
- package/dist/esm/DataTables/DataTableCore.js.map +1 -1
- package/dist/esm/DataTables/DataTableHeaderCell.js +1 -1
- package/dist/esm/DataTables/DataTableHeaderCell.js.map +1 -1
- package/dist/esm/DataTables/DataTableRow.js +1 -1
- package/dist/esm/DataTables/DataTableRow.js.map +1 -1
- package/dist/esm/DataTables/DataTableSearch.js +1 -1
- package/dist/esm/DataTables/DataTableSearch.js.map +1 -1
- package/dist/esm/Dialogs/DialogImplementation.d.ts.map +1 -1
- package/dist/esm/Dialogs/DialogImplementation.js +1 -1
- package/dist/esm/Dialogs/DialogImplementation.js.map +1 -1
- package/dist/esm/Display/Message.d.ts +8 -1
- package/dist/esm/Display/Message.d.ts.map +1 -1
- package/dist/esm/Display/Message.js +1 -1
- package/dist/esm/Display/Message.js.map +1 -1
- package/dist/esm/Dropdown/DropdownImplementation.d.ts.map +1 -1
- package/dist/esm/Dropdown/DropdownImplementation.js +1 -1
- package/dist/esm/Dropdown/DropdownImplementation.js.map +1 -1
- package/dist/esm/Notifications/Toaster.d.ts.map +1 -1
- package/dist/esm/Notifications/Toaster.js +1 -1
- package/dist/esm/Notifications/Toaster.js.map +1 -1
- package/dist/esm/PivotViewer/components/PivotCanvas.d.ts +7 -50
- package/dist/esm/PivotViewer/components/PivotCanvas.d.ts.map +1 -1
- package/dist/esm/PivotViewer/components/PivotCanvas.js +34 -433
- package/dist/esm/PivotViewer/components/PivotCanvas.js.map +1 -1
- package/dist/esm/PivotViewer/components/PivotCanvasProps.d.ts +51 -0
- package/dist/esm/PivotViewer/components/PivotCanvasProps.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/PivotCanvasProps.js +4 -0
- package/dist/esm/PivotViewer/components/PivotCanvasProps.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/PivotRenderContext.d.ts +16 -0
- package/dist/esm/PivotViewer/components/pivot/PivotRenderContext.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/PivotRenderContext.js +4 -0
- package/dist/esm/PivotViewer/components/pivot/PivotRenderContext.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/PivotStage.d.ts +19 -0
- package/dist/esm/PivotViewer/components/pivot/PivotStage.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/PivotStage.js +4 -0
- package/dist/esm/PivotViewer/components/pivot/PivotStage.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/PivotTransitionState.d.ts +21 -0
- package/dist/esm/PivotViewer/components/pivot/PivotTransitionState.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/PivotTransitionState.js +4 -0
- package/dist/esm/PivotViewer/components/pivot/PivotTransitionState.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/asReactMouseEvent.d.ts +8 -0
- package/dist/esm/PivotViewer/components/pivot/asReactMouseEvent.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/asReactMouseEvent.js +6 -0
- package/dist/esm/PivotViewer/components/pivot/asReactMouseEvent.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/sprites.js +1 -1
- package/dist/esm/PivotViewer/components/pivot/usePivotCardColors.d.ts +22 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotCardColors.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotCardColors.js +25 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotCardColors.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotGroupBackgrounds.d.ts +8 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotGroupBackgrounds.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotGroupBackgrounds.js +53 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotGroupBackgrounds.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotScrollSync.d.ts +8 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotScrollSync.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotScrollSync.js +100 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotScrollSync.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotSelectionHighlight.d.ts +7 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotSelectionHighlight.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotSelectionHighlight.js +35 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotSelectionHighlight.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotSpriteSync.d.ts +8 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotSpriteSync.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotSpriteSync.js +123 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotSpriteSync.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotStage.d.ts +21 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotStage.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotStage.js +171 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotStage.js.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotTransitionState.d.ts +13 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotTransitionState.d.ts.map +1 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotTransitionState.js +31 -0
- package/dist/esm/PivotViewer/components/pivot/usePivotTransitionState.js.map +1 -0
- package/dist/esm/TimeMachine/EventsView.d.ts.map +1 -1
- package/dist/esm/TimeMachine/EventsView.js +1 -1
- package/dist/esm/TimeMachine/EventsView.js.map +1 -1
- package/dist/esm/theme.css +11 -0
- package/package.json +1 -1
- package/dist/esm/DataTables/classNames.d.ts +0 -7
- package/dist/esm/DataTables/classNames.d.ts.map +0 -1
- package/dist/esm/DataTables/classNames.js.map +0 -1
- /package/dist/esm/{DataTables → ClassNames}/classNames.js +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const ZOOM_INTENSITY = 0.008;
|
|
2
|
+
export declare const GESTURE_SETTLE_MS = 150;
|
|
3
|
+
export declare const MOMENTUM_SAMPLE_WINDOW_MS = 100;
|
|
4
|
+
export declare const MOMENTUM_MIN_VELOCITY = 0.02;
|
|
5
|
+
export declare const MOMENTUM_FRICTION = 0.0035;
|
|
6
|
+
//# sourceMappingURL=canvasGestureTuning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvasGestureTuning.d.ts","sourceRoot":"","sources":["../../../Canvas/canvasGestureTuning.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,QAAQ,CAAC;AAIpC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAQrC,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAI7C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAI1C,eAAO,MAAM,iBAAiB,SAAS,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const ZOOM_INTENSITY = 0.008;
|
|
2
|
+
const GESTURE_SETTLE_MS = 150;
|
|
3
|
+
const MOMENTUM_SAMPLE_WINDOW_MS = 100;
|
|
4
|
+
const MOMENTUM_MIN_VELOCITY = 0.02;
|
|
5
|
+
|
|
6
|
+
export { GESTURE_SETTLE_MS, MOMENTUM_MIN_VELOCITY, MOMENTUM_SAMPLE_WINDOW_MS, ZOOM_INTENSITY };
|
|
7
|
+
//# sourceMappingURL=canvasGestureTuning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvasGestureTuning.js","sources":["../../../Canvas/canvasGestureTuning.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nexport const ZOOM_INTENSITY = 0.008;\n\n// How long after the last wheel event a gesture counts as settled - the moment the crisp resting zoom is\n// re-applied and the held-back virtualization updates flush.\nexport const GESTURE_SETTLE_MS = 150;\n\n// A trackpad's momentum comes for free — the OS keeps delivering decaying wheel events after the\n// fingers lift. A touch drag has no such thing: `touch-action: none` hands the whole gesture to us,\n// so lifting a finger stops the pan dead unless we fake the same decay ourselves. These tune that feel.\n// Only samples within this trailing window (from the drag's last moment, not its whole history)\n// contribute to the release velocity, so a drag that was moving fast but came to rest before the\n// finger actually lifted correctly produces no momentum.\nexport const MOMENTUM_SAMPLE_WINDOW_MS = 100;\n\n// Below this speed (px/ms) momentum is imperceptible — used both to skip starting it on a slow\n// release and to end the decay loop once it coasts down to a stop.\nexport const MOMENTUM_MIN_VELOCITY = 0.02;\n\n// Exponential decay rate, chosen so velocity halves roughly every 200ms — fast enough to feel\n// responsive, slow enough to read as a coast rather than a snap.\nexport const MOMENTUM_FRICTION = 0.0035;\n"],"names":[],"mappings":"AAGO,MAAM,cAAc,GAAG;AAIvB,MAAM,iBAAiB,GAAG;AAQ1B,MAAM,yBAAyB,GAAG;AAIlC,MAAM,qBAAqB,GAAG;;"}
|
package/dist/esm/Canvas/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { Canvas
|
|
1
|
+
export { Canvas } from './Canvas.js';
|
|
2
2
|
export { CanvasItem } from './CanvasItem.js';
|
|
3
3
|
export { CanvasMinimap } from './CanvasMinimap.js';
|
|
4
4
|
export { CanvasControls } from './CanvasControls.js';
|
|
@@ -33,4 +33,5 @@ export { MentionSuggestions } from '../Chat/Kit/Mentions/MentionSuggestions.js';
|
|
|
33
33
|
export { extractMentions } from '../Chat/Kit/Mentions/extractMentions.js';
|
|
34
34
|
export { findMentionRanges } from '../Chat/Kit/Mentions/findMentionRanges.js';
|
|
35
35
|
export { mentionSegments } from '../Chat/Kit/Mentions/mentionSegments.js';
|
|
36
|
+
export { CanvasItemRegistryContext } from './CanvasItemRegistryContext.js';
|
|
36
37
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useRef, useContext, useSyncExternalStore, useEffect, useCallback } from 'react';
|
|
3
|
-
import
|
|
3
|
+
import '../../CanvasMinimap.js';
|
|
4
|
+
import 'react-icons/fa6';
|
|
5
|
+
import { CanvasItemRegistryContext } from '../../CanvasItemRegistryContext.js';
|
|
6
|
+
import 'pixi.js';
|
|
4
7
|
import { useOptionalMessenger } from '../../../Common/messaging/useOptionalMessenger.js';
|
|
5
8
|
import { ItemAddedToRegion } from './ItemAddedToRegion.js';
|
|
6
9
|
import { ItemRemovedFromRegion } from './ItemRemovedFromRegion.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Region.js","sources":["../../../../../Canvas/shapes/Region/Region.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport type React from 'react';\nimport {\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { CanvasItemRegistryContext, type CanvasItemRegistryEntry } from '../../Canvas';\nimport { useOptionalMessenger } from '../../../Common/messaging/useOptionalMessenger';\nimport { ItemAddedToRegion } from './ItemAddedToRegion';\nimport { ItemRemovedFromRegion } from './ItemRemovedFromRegion';\nimport { itemsWithinRegion } from './regionContainment';\n\nconst MIN_SIZE = 100;\n\n// Stable fallbacks for a Region rendered without a Canvas item registry above it (or with a\n// registry value predating getSnapshot/subscribe): the store below then reads an unchanging empty\n// map and never notifies, keeping every render on the exact same code path with membership\n// permanently empty. Module-level so their identities never change between renders — a changing\n// subscribe identity would make useSyncExternalStore resubscribe every render.\nconst EMPTY_REGISTRY: ReadonlyMap<string, CanvasItemRegistryEntry> = new Map();\nconst getEmptyRegistry = (): ReadonlyMap<string, CanvasItemRegistryEntry> =>\n EMPTY_REGISTRY;\nconst subscribeToNothing = (): (() => void) => () => {\n /* nothing to unsubscribe */\n};\n\nconst HANDLE_KEYS = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'] as const;\ntype HandleKey = (typeof HANDLE_KEYS)[number];\n\nconst HANDLE_POSITIONS: Record<HandleKey, React.CSSProperties> = {\n nw: { top: -4, left: -4 },\n n: { top: -4, left: '50%', transform: 'translateX(-50%)' },\n ne: { top: -4, right: -4 },\n e: { top: '50%', right: -4, transform: 'translateY(-50%)' },\n se: { bottom: -4, right: -4 },\n s: { bottom: -4, left: '50%', transform: 'translateX(-50%)' },\n sw: { bottom: -4, left: -4 },\n w: { top: '50%', left: -4, transform: 'translateY(-50%)' },\n};\n\nconst RESIZE_CURSORS: Record<HandleKey, string> = {\n nw: 'nw-resize',\n n: 'n-resize',\n ne: 'ne-resize',\n e: 'e-resize',\n se: 'se-resize',\n s: 's-resize',\n sw: 'sw-resize',\n w: 'w-resize',\n};\n\n/** Controlled data rendered by {@link Region}. */\nexport interface RegionData {\n /** Stable region identity. */\n id: string;\n /** World-space horizontal position. */\n x: number;\n /** World-space vertical position. */\n y: number;\n /** Region width. */\n width: number;\n /** Region height. */\n height: number;\n /** Editable region label. */\n name: string;\n}\n\n/** Props for a fully controlled movable, resizable, labeled Canvas region. */\nexport interface RegionProps {\n /** Current region data. */\n region: RegionData;\n /** Whether resize handles are visible. */\n selected: boolean;\n\n /**\n * Selects this region. `additive` reports that the gesture carried a shift/meta/ctrl modifier, so\n * a board supporting multi-select can extend its selection instead of replacing it.\n */\n onSelect: (id: string, additive: boolean) => void;\n\n /** Reports drag movement. */\n onMove: (id: string, x: number, y: number) => void;\n /** Reports drag completion. */\n onMoveEnd?: (id: string) => void;\n /** Reports resize movement and resulting bounds. */\n onResize: (id: string, x: number, y: number, width: number, height: number) => void;\n /** Reports resize completion. */\n onResizeEnd?: (id: string) => void;\n /** Reports committed label edits. */\n onNameChange: (id: string, name: string) => void;\n\n /**\n * Visual nesting only — rendered inside the region's own coordinate space (its top-left corner) so\n * a host that also places other shapes at region-relative coordinates lines them up correctly.\n * Rendering something here does not make it a \"member\": the region detects containment against\n * sibling `CanvasItem`s that carry an `id` and *reports* it over the Arc messenger (see\n * {@link ItemAddedToRegion}/{@link ItemRemovedFromRegion}), but it never moves members, persists\n * nothing, and knows nothing about item types — deciding what membership means, storing it, and\n * moving members along with the region is board-level orchestration left entirely to the host.\n */\n children?: React.ReactNode;\n}\n\n/**\n * A resizable, draggable, labeled box a host can place other shapes inside of — the generic shell\n * behind a \"region\" or \"group\" affordance on a board. Presentational and fully controlled: like\n * {@link Note}, it owns no position/size/name state of its own and reads `region` fresh every render,\n * reporting every change through callbacks instead of committing anything locally. A host that is slow\n * to feed a new `region` back after a callback will see the box spring back to its last known value —\n * the same trade-off `Note` makes for its own drag/resize/rename.\n *\n * Opt-in membership reporting: when an Arc messenger is resolvable, the region watches the Canvas\n * item registry and publishes {@link ItemAddedToRegion}/{@link ItemRemovedFromRegion} whenever a\n * sibling `CanvasItem` carrying an `id` gains or loses containment (by center point) — including\n * when the region itself is moved or resized over items. By convention the `CanvasItem` wrapping\n * this region is given `id={region.id}`, which is how the region recognizes and excludes itself.\n * Without an `ArcContext.Provider` (or with items that carry no `id`) all of this is silently inert.\n */\nexport const Region = ({\n region,\n selected,\n onSelect,\n onMove,\n onMoveEnd,\n onResize,\n onResizeEnd,\n onNameChange,\n children,\n}: RegionProps) => {\n const [isEditing, setIsEditing] = useState(false);\n\n const regionRef = useRef<HTMLDivElement>(null);\n const titleBarRef = useRef<HTMLDivElement>(null);\n const clickedTitleBarRef = useRef(false);\n\n // ── Membership reporting ────────────────────────────────────────────────\n\n const registry = useContext(CanvasItemRegistryContext);\n const publish = useOptionalMessenger();\n\n // The registry's stable-reference snapshot: its identity only changes when content actually\n // changed, so this re-renders exactly once per register/unregister/bounds-update. Bounds\n // updates can arrive per-frame during a drag — acceptable, since the diff below only publishes\n // when membership itself changed. The third argument keeps the hook safe under server\n // rendering, where there is nothing to subscribe to.\n const registrySnapshot = useSyncExternalStore(\n registry?.subscribe ?? subscribeToNothing,\n registry?.getSnapshot ?? getEmptyRegistry,\n getEmptyRegistry,\n );\n\n // Membership as of the last published state. A ref, not state: it is bookkeeping for diffing,\n // and updating it must not itself cause a render.\n const membershipRef = useRef<ReadonlySet<string>>(new Set());\n\n useEffect(() => {\n // No resolvable messenger means the whole mechanism is opted out: no containment math, no\n // diffing, nothing published — identical to the component's behavior before it existed.\n if (!publish) return;\n\n const within = itemsWithinRegion(\n { x: region.x, y: region.y, width: region.width, height: region.height },\n registrySnapshot,\n region.id,\n );\n const current = new Set(within);\n const previous = membershipRef.current;\n current.forEach((itemId) => {\n if (!previous.has(itemId)) publish(new ItemAddedToRegion(region.id, itemId));\n });\n previous.forEach((itemId) => {\n if (!current.has(itemId))\n publish(new ItemRemovedFromRegion(region.id, itemId));\n });\n membershipRef.current = current;\n }, [\n publish,\n registrySnapshot,\n region.id,\n region.x,\n region.y,\n region.width,\n region.height,\n ]);\n\n const dragRef = useRef<{\n clientX: number;\n clientY: number;\n startX: number;\n startY: number;\n zoom: number;\n } | null>(null);\n\n const resizeRef = useRef<{\n handle: HandleKey;\n clientX: number;\n clientY: number;\n startX: number;\n startY: number;\n startWidth: number;\n startHeight: number;\n zoom: number;\n } | null>(null);\n\n const readEffectiveZoom = useCallback((): number => {\n if (!regionRef.current) return 1;\n const renderedWidth = regionRef.current.getBoundingClientRect().width;\n return renderedWidth > 0 ? renderedWidth / region.width : 1;\n }, [region.width]);\n\n // ── Move ────────────────────────────────────────────────────────────────\n\n // Only the title bar is a drag handle. A press anywhere on the region's body is deliberately left\n // to bubble up to the host: a drag from there is free to become a rubber-band selection instead, and\n // a plain click can select the region itself — a region is a backdrop you can both sweep a\n // selection across and click to select, not a surface that swallows the gesture.\n const handlePointerDown = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (isEditing) return;\n clickedTitleBarRef.current =\n titleBarRef.current?.contains(event.target as Node) ?? false;\n if (!clickedTitleBarRef.current) return;\n event.stopPropagation();\n onSelect(region.id, event.shiftKey || event.metaKey || event.ctrlKey);\n dragRef.current = {\n clientX: event.clientX,\n clientY: event.clientY,\n startX: region.x,\n startY: region.y,\n zoom: readEffectiveZoom(),\n };\n event.currentTarget.setPointerCapture(event.pointerId);\n },\n [isEditing, region.id, region.x, region.y, onSelect, readEffectiveZoom],\n );\n\n const handlePointerMove = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (!dragRef.current || !event.buttons) return;\n const { clientX, clientY, startX, startY, zoom } = dragRef.current;\n const deltaX = (event.clientX - clientX) / zoom;\n const deltaY = (event.clientY - clientY) / zoom;\n onMove(region.id, startX + deltaX, startY + deltaY);\n },\n [region.id, onMove],\n );\n\n const handlePointerUp = useCallback(() => {\n if (dragRef.current) {\n dragRef.current = null;\n onMoveEnd?.(region.id);\n }\n }, [region.id, onMoveEnd]);\n\n // ── Resize ──────────────────────────────────────────────────────────────\n\n const handleResizePointerDown = useCallback(\n (handle: HandleKey) => (event: React.PointerEvent<HTMLDivElement>) => {\n clickedTitleBarRef.current = false;\n event.stopPropagation();\n resizeRef.current = {\n handle,\n clientX: event.clientX,\n clientY: event.clientY,\n startX: region.x,\n startY: region.y,\n startWidth: region.width,\n startHeight: region.height,\n zoom: readEffectiveZoom(),\n };\n event.currentTarget.setPointerCapture(event.pointerId);\n },\n [region.x, region.y, region.width, region.height, readEffectiveZoom],\n );\n\n const handleResizePointerMove = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (!resizeRef.current || !event.buttons) return;\n const {\n handle,\n clientX,\n clientY,\n startX,\n startY,\n startWidth,\n startHeight,\n zoom,\n } = resizeRef.current;\n const deltaX = (event.clientX - clientX) / zoom;\n const deltaY = (event.clientY - clientY) / zoom;\n\n let newX = startX;\n let newY = startY;\n let newWidth = startWidth;\n let newHeight = startHeight;\n\n if (handle === 'e' || handle === 'ne' || handle === 'se') {\n newWidth = Math.max(MIN_SIZE, startWidth + deltaX);\n }\n if (handle === 'w' || handle === 'nw' || handle === 'sw') {\n newWidth = Math.max(MIN_SIZE, startWidth - deltaX);\n newX = startX + startWidth - newWidth;\n }\n if (handle === 's' || handle === 'se' || handle === 'sw') {\n newHeight = Math.max(MIN_SIZE, startHeight + deltaY);\n }\n if (handle === 'n' || handle === 'ne' || handle === 'nw') {\n newHeight = Math.max(MIN_SIZE, startHeight - deltaY);\n newY = startY + startHeight - newHeight;\n }\n\n onResize(region.id, newX, newY, newWidth, newHeight);\n },\n [region.id, onResize],\n );\n\n const handleResizePointerUp = useCallback(() => {\n if (resizeRef.current) {\n resizeRef.current = null;\n onResizeEnd?.(region.id);\n }\n }, [region.id, onResizeEnd]);\n\n // ── Rename ──────────────────────────────────────────────────────────────\n\n const handleDoubleClick = useCallback((event: React.MouseEvent<HTMLDivElement>) => {\n if (!clickedTitleBarRef.current) return;\n event.stopPropagation();\n setIsEditing(true);\n }, []);\n\n const commitRename = useCallback(\n (newName: string) => {\n setIsEditing(false);\n const trimmed = newName.trim();\n if (trimmed && trimmed !== region.name) onNameChange(region.id, trimmed);\n },\n [region.id, region.name, onNameChange],\n );\n\n const handleInputBlur = useCallback(\n (event: React.FocusEvent<HTMLInputElement>) =>\n commitRename(event.currentTarget.value),\n [commitRename],\n );\n\n const handleInputKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') commitRename(event.currentTarget.value);\n else if (event.key === 'Escape') setIsEditing(false);\n },\n [commitRename],\n );\n\n return (\n <>\n <div\n ref={regionRef}\n className={`canvas-region${selected ? ' canvas-region--selected' : ''}`}\n style={{ width: region.width, height: region.height }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n onDoubleClick={handleDoubleClick}\n >\n <div ref={titleBarRef} className='canvas-region__title-bar'>\n {isEditing ? (\n <input\n className='canvas-region__name-input'\n type='text'\n defaultValue={region.name}\n autoFocus\n onBlur={handleInputBlur}\n onKeyDown={handleInputKeyDown}\n onPointerDown={(event) => event.stopPropagation()}\n />\n ) : (\n <span className='canvas-region__name'>{region.name}</span>\n )}\n </div>\n\n {selected &&\n HANDLE_KEYS.map((handle) => (\n <div\n key={handle}\n className='canvas-region__handle'\n style={{\n ...HANDLE_POSITIONS[handle],\n cursor: RESIZE_CURSORS[handle],\n }}\n onPointerDown={handleResizePointerDown(handle)}\n onPointerMove={handleResizePointerMove}\n onPointerUp={handleResizePointerUp}\n onPointerCancel={handleResizePointerUp}\n />\n ))}\n </div>\n {/* Member items are positioned relative to the region's top-left (region.x/region.y). They\n live outside the bordered box so the region's border does not offset their coordinate\n origin — otherwise items would jump by the border width when moved into or out of a\n region. */}\n <div className='canvas-region__items'>{children}</div>\n </>\n );\n};\n"],"names":["_jsxs","_Fragment","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Region.js","sources":["../../../../../Canvas/shapes/Region/Region.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport type React from 'react';\nimport {\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { CanvasItemRegistryContext, type CanvasItemRegistryEntry } from '../../Canvas';\nimport { useOptionalMessenger } from '../../../Common/messaging/useOptionalMessenger';\nimport { ItemAddedToRegion } from './ItemAddedToRegion';\nimport { ItemRemovedFromRegion } from './ItemRemovedFromRegion';\nimport { itemsWithinRegion } from './regionContainment';\n\nconst MIN_SIZE = 100;\n\n// Stable fallbacks for a Region rendered without a Canvas item registry above it (or with a\n// registry value predating getSnapshot/subscribe): the store below then reads an unchanging empty\n// map and never notifies, keeping every render on the exact same code path with membership\n// permanently empty. Module-level so their identities never change between renders — a changing\n// subscribe identity would make useSyncExternalStore resubscribe every render.\nconst EMPTY_REGISTRY: ReadonlyMap<string, CanvasItemRegistryEntry> = new Map();\nconst getEmptyRegistry = (): ReadonlyMap<string, CanvasItemRegistryEntry> =>\n EMPTY_REGISTRY;\nconst subscribeToNothing = (): (() => void) => () => {\n /* nothing to unsubscribe */\n};\n\nconst HANDLE_KEYS = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'] as const;\ntype HandleKey = (typeof HANDLE_KEYS)[number];\n\nconst HANDLE_POSITIONS: Record<HandleKey, React.CSSProperties> = {\n nw: { top: -4, left: -4 },\n n: { top: -4, left: '50%', transform: 'translateX(-50%)' },\n ne: { top: -4, right: -4 },\n e: { top: '50%', right: -4, transform: 'translateY(-50%)' },\n se: { bottom: -4, right: -4 },\n s: { bottom: -4, left: '50%', transform: 'translateX(-50%)' },\n sw: { bottom: -4, left: -4 },\n w: { top: '50%', left: -4, transform: 'translateY(-50%)' },\n};\n\nconst RESIZE_CURSORS: Record<HandleKey, string> = {\n nw: 'nw-resize',\n n: 'n-resize',\n ne: 'ne-resize',\n e: 'e-resize',\n se: 'se-resize',\n s: 's-resize',\n sw: 'sw-resize',\n w: 'w-resize',\n};\n\n/** Controlled data rendered by {@link Region}. */\nexport interface RegionData {\n /** Stable region identity. */\n id: string;\n /** World-space horizontal position. */\n x: number;\n /** World-space vertical position. */\n y: number;\n /** Region width. */\n width: number;\n /** Region height. */\n height: number;\n /** Editable region label. */\n name: string;\n}\n\n/** Props for a fully controlled movable, resizable, labeled Canvas region. */\nexport interface RegionProps {\n /** Current region data. */\n region: RegionData;\n /** Whether resize handles are visible. */\n selected: boolean;\n\n /**\n * Selects this region. `additive` reports that the gesture carried a shift/meta/ctrl modifier, so\n * a board supporting multi-select can extend its selection instead of replacing it.\n */\n onSelect: (id: string, additive: boolean) => void;\n\n /** Reports drag movement. */\n onMove: (id: string, x: number, y: number) => void;\n /** Reports drag completion. */\n onMoveEnd?: (id: string) => void;\n /** Reports resize movement and resulting bounds. */\n onResize: (id: string, x: number, y: number, width: number, height: number) => void;\n /** Reports resize completion. */\n onResizeEnd?: (id: string) => void;\n /** Reports committed label edits. */\n onNameChange: (id: string, name: string) => void;\n\n /**\n * Visual nesting only — rendered inside the region's own coordinate space (its top-left corner) so\n * a host that also places other shapes at region-relative coordinates lines them up correctly.\n * Rendering something here does not make it a \"member\": the region detects containment against\n * sibling `CanvasItem`s that carry an `id` and *reports* it over the Arc messenger (see\n * {@link ItemAddedToRegion}/{@link ItemRemovedFromRegion}), but it never moves members, persists\n * nothing, and knows nothing about item types — deciding what membership means, storing it, and\n * moving members along with the region is board-level orchestration left entirely to the host.\n */\n children?: React.ReactNode;\n}\n\n/**\n * A resizable, draggable, labeled box a host can place other shapes inside of — the generic shell\n * behind a \"region\" or \"group\" affordance on a board. Presentational and fully controlled: like\n * {@link Note}, it owns no position/size/name state of its own and reads `region` fresh every render,\n * reporting every change through callbacks instead of committing anything locally. A host that is slow\n * to feed a new `region` back after a callback will see the box spring back to its last known value —\n * the same trade-off `Note` makes for its own drag/resize/rename.\n *\n * Opt-in membership reporting: when an Arc messenger is resolvable, the region watches the Canvas\n * item registry and publishes {@link ItemAddedToRegion}/{@link ItemRemovedFromRegion} whenever a\n * sibling `CanvasItem` carrying an `id` gains or loses containment (by center point) — including\n * when the region itself is moved or resized over items. By convention the `CanvasItem` wrapping\n * this region is given `id={region.id}`, which is how the region recognizes and excludes itself.\n * Without an `ArcContext.Provider` (or with items that carry no `id`) all of this is silently inert.\n */\nexport const Region = ({\n region,\n selected,\n onSelect,\n onMove,\n onMoveEnd,\n onResize,\n onResizeEnd,\n onNameChange,\n children,\n}: RegionProps) => {\n const [isEditing, setIsEditing] = useState(false);\n\n const regionRef = useRef<HTMLDivElement>(null);\n const titleBarRef = useRef<HTMLDivElement>(null);\n const clickedTitleBarRef = useRef(false);\n\n // ── Membership reporting ────────────────────────────────────────────────\n\n const registry = useContext(CanvasItemRegistryContext);\n const publish = useOptionalMessenger();\n\n // The registry's stable-reference snapshot: its identity only changes when content actually\n // changed, so this re-renders exactly once per register/unregister/bounds-update. Bounds\n // updates can arrive per-frame during a drag — acceptable, since the diff below only publishes\n // when membership itself changed. The third argument keeps the hook safe under server\n // rendering, where there is nothing to subscribe to.\n const registrySnapshot = useSyncExternalStore(\n registry?.subscribe ?? subscribeToNothing,\n registry?.getSnapshot ?? getEmptyRegistry,\n getEmptyRegistry,\n );\n\n // Membership as of the last published state. A ref, not state: it is bookkeeping for diffing,\n // and updating it must not itself cause a render.\n const membershipRef = useRef<ReadonlySet<string>>(new Set());\n\n useEffect(() => {\n // No resolvable messenger means the whole mechanism is opted out: no containment math, no\n // diffing, nothing published — identical to the component's behavior before it existed.\n if (!publish) return;\n\n const within = itemsWithinRegion(\n { x: region.x, y: region.y, width: region.width, height: region.height },\n registrySnapshot,\n region.id,\n );\n const current = new Set(within);\n const previous = membershipRef.current;\n current.forEach((itemId) => {\n if (!previous.has(itemId)) publish(new ItemAddedToRegion(region.id, itemId));\n });\n previous.forEach((itemId) => {\n if (!current.has(itemId))\n publish(new ItemRemovedFromRegion(region.id, itemId));\n });\n membershipRef.current = current;\n }, [\n publish,\n registrySnapshot,\n region.id,\n region.x,\n region.y,\n region.width,\n region.height,\n ]);\n\n const dragRef = useRef<{\n clientX: number;\n clientY: number;\n startX: number;\n startY: number;\n zoom: number;\n } | null>(null);\n\n const resizeRef = useRef<{\n handle: HandleKey;\n clientX: number;\n clientY: number;\n startX: number;\n startY: number;\n startWidth: number;\n startHeight: number;\n zoom: number;\n } | null>(null);\n\n const readEffectiveZoom = useCallback((): number => {\n if (!regionRef.current) return 1;\n const renderedWidth = regionRef.current.getBoundingClientRect().width;\n return renderedWidth > 0 ? renderedWidth / region.width : 1;\n }, [region.width]);\n\n // ── Move ────────────────────────────────────────────────────────────────\n\n // Only the title bar is a drag handle. A press anywhere on the region's body is deliberately left\n // to bubble up to the host: a drag from there is free to become a rubber-band selection instead, and\n // a plain click can select the region itself — a region is a backdrop you can both sweep a\n // selection across and click to select, not a surface that swallows the gesture.\n const handlePointerDown = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (isEditing) return;\n clickedTitleBarRef.current =\n titleBarRef.current?.contains(event.target as Node) ?? false;\n if (!clickedTitleBarRef.current) return;\n event.stopPropagation();\n onSelect(region.id, event.shiftKey || event.metaKey || event.ctrlKey);\n dragRef.current = {\n clientX: event.clientX,\n clientY: event.clientY,\n startX: region.x,\n startY: region.y,\n zoom: readEffectiveZoom(),\n };\n event.currentTarget.setPointerCapture(event.pointerId);\n },\n [isEditing, region.id, region.x, region.y, onSelect, readEffectiveZoom],\n );\n\n const handlePointerMove = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (!dragRef.current || !event.buttons) return;\n const { clientX, clientY, startX, startY, zoom } = dragRef.current;\n const deltaX = (event.clientX - clientX) / zoom;\n const deltaY = (event.clientY - clientY) / zoom;\n onMove(region.id, startX + deltaX, startY + deltaY);\n },\n [region.id, onMove],\n );\n\n const handlePointerUp = useCallback(() => {\n if (dragRef.current) {\n dragRef.current = null;\n onMoveEnd?.(region.id);\n }\n }, [region.id, onMoveEnd]);\n\n // ── Resize ──────────────────────────────────────────────────────────────\n\n const handleResizePointerDown = useCallback(\n (handle: HandleKey) => (event: React.PointerEvent<HTMLDivElement>) => {\n clickedTitleBarRef.current = false;\n event.stopPropagation();\n resizeRef.current = {\n handle,\n clientX: event.clientX,\n clientY: event.clientY,\n startX: region.x,\n startY: region.y,\n startWidth: region.width,\n startHeight: region.height,\n zoom: readEffectiveZoom(),\n };\n event.currentTarget.setPointerCapture(event.pointerId);\n },\n [region.x, region.y, region.width, region.height, readEffectiveZoom],\n );\n\n const handleResizePointerMove = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (!resizeRef.current || !event.buttons) return;\n const {\n handle,\n clientX,\n clientY,\n startX,\n startY,\n startWidth,\n startHeight,\n zoom,\n } = resizeRef.current;\n const deltaX = (event.clientX - clientX) / zoom;\n const deltaY = (event.clientY - clientY) / zoom;\n\n let newX = startX;\n let newY = startY;\n let newWidth = startWidth;\n let newHeight = startHeight;\n\n if (handle === 'e' || handle === 'ne' || handle === 'se') {\n newWidth = Math.max(MIN_SIZE, startWidth + deltaX);\n }\n if (handle === 'w' || handle === 'nw' || handle === 'sw') {\n newWidth = Math.max(MIN_SIZE, startWidth - deltaX);\n newX = startX + startWidth - newWidth;\n }\n if (handle === 's' || handle === 'se' || handle === 'sw') {\n newHeight = Math.max(MIN_SIZE, startHeight + deltaY);\n }\n if (handle === 'n' || handle === 'ne' || handle === 'nw') {\n newHeight = Math.max(MIN_SIZE, startHeight - deltaY);\n newY = startY + startHeight - newHeight;\n }\n\n onResize(region.id, newX, newY, newWidth, newHeight);\n },\n [region.id, onResize],\n );\n\n const handleResizePointerUp = useCallback(() => {\n if (resizeRef.current) {\n resizeRef.current = null;\n onResizeEnd?.(region.id);\n }\n }, [region.id, onResizeEnd]);\n\n // ── Rename ──────────────────────────────────────────────────────────────\n\n const handleDoubleClick = useCallback((event: React.MouseEvent<HTMLDivElement>) => {\n if (!clickedTitleBarRef.current) return;\n event.stopPropagation();\n setIsEditing(true);\n }, []);\n\n const commitRename = useCallback(\n (newName: string) => {\n setIsEditing(false);\n const trimmed = newName.trim();\n if (trimmed && trimmed !== region.name) onNameChange(region.id, trimmed);\n },\n [region.id, region.name, onNameChange],\n );\n\n const handleInputBlur = useCallback(\n (event: React.FocusEvent<HTMLInputElement>) =>\n commitRename(event.currentTarget.value),\n [commitRename],\n );\n\n const handleInputKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') commitRename(event.currentTarget.value);\n else if (event.key === 'Escape') setIsEditing(false);\n },\n [commitRename],\n );\n\n return (\n <>\n <div\n ref={regionRef}\n className={`canvas-region${selected ? ' canvas-region--selected' : ''}`}\n style={{ width: region.width, height: region.height }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n onDoubleClick={handleDoubleClick}\n >\n <div ref={titleBarRef} className='canvas-region__title-bar'>\n {isEditing ? (\n <input\n className='canvas-region__name-input'\n type='text'\n defaultValue={region.name}\n autoFocus\n onBlur={handleInputBlur}\n onKeyDown={handleInputKeyDown}\n onPointerDown={(event) => event.stopPropagation()}\n />\n ) : (\n <span className='canvas-region__name'>{region.name}</span>\n )}\n </div>\n\n {selected &&\n HANDLE_KEYS.map((handle) => (\n <div\n key={handle}\n className='canvas-region__handle'\n style={{\n ...HANDLE_POSITIONS[handle],\n cursor: RESIZE_CURSORS[handle],\n }}\n onPointerDown={handleResizePointerDown(handle)}\n onPointerMove={handleResizePointerMove}\n onPointerUp={handleResizePointerUp}\n onPointerCancel={handleResizePointerUp}\n />\n ))}\n </div>\n {/* Member items are positioned relative to the region's top-left (region.x/region.y). They\n live outside the bordered box so the region's border does not offset their coordinate\n origin — otherwise items would jump by the border width when moved into or out of a\n region. */}\n <div className='canvas-region__items'>{children}</div>\n </>\n );\n};\n"],"names":["_jsxs","_Fragment","_jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import type * as PIXI from 'pixi.js';
|
|
3
|
+
import type { CanvasMinimapHandle } from './CanvasMinimap.js';
|
|
4
|
+
/** The camera state and surfaces {@link useCanvasCamera} writes the transform to. */
|
|
5
|
+
export interface CanvasCameraOptions {
|
|
6
|
+
appRef: RefObject<PIXI.Application | null>;
|
|
7
|
+
worldRef: RefObject<PIXI.Container | null>;
|
|
8
|
+
minimapRef: RefObject<CanvasMinimapHandle | null>;
|
|
9
|
+
overlayRef: RefObject<HTMLDivElement | null>;
|
|
10
|
+
zoomLayerRef: RefObject<HTMLDivElement | null>;
|
|
11
|
+
panRef: RefObject<{
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}>;
|
|
15
|
+
zoomRef: RefObject<number>;
|
|
16
|
+
onTransformChangeRef: RefObject<((zoom: number, pan: {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
}) => void) | undefined>;
|
|
20
|
+
render: () => void;
|
|
21
|
+
minZoom: number;
|
|
22
|
+
maxZoom: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Applies the camera transform to the Pixi world, the HTML overlay and the minimap, coalesces
|
|
26
|
+
* gesture frames, and zooms toward a focus point.
|
|
27
|
+
* @param options The camera state, surfaces and zoom bounds.
|
|
28
|
+
* @returns The transform operations every input path shares.
|
|
29
|
+
*/
|
|
30
|
+
export declare const useCanvasCamera: ({ appRef, worldRef, minimapRef, overlayRef, zoomLayerRef, panRef, zoomRef, onTransformChangeRef, render, minZoom, maxZoom, }: CanvasCameraOptions) => {
|
|
31
|
+
applyWorldTransform: () => void;
|
|
32
|
+
refreshMinimap: () => void;
|
|
33
|
+
scheduleTransformApply: () => void;
|
|
34
|
+
noteGestureActivity: () => void;
|
|
35
|
+
zoomTowards: (focusX: number, focusY: number, factor: number) => void;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=useCanvasCamera.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanvasCamera.d.ts","sourceRoot":"","sources":["../../../Canvas/useCanvasCamera.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvE,OAAO,KAAK,KAAK,IAAI,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAiB,CAAC;AAM3D,qFAAqF;AACrF,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC3C,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC3C,UAAU,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IAClD,UAAU,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC7C,YAAY,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC/C,MAAM,EAAE,SAAS,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC3B,oBAAoB,EAAE,SAAS,CAC3B,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,SAAS,CACtE,CAAC;IACF,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,iIAYzB,mBAAmB;;;;;0BA8FL,MAAM,UAAU,MAAM,UAAU,MAAM;CAyBtD,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { canvasGesture } from './canvasGesture.js';
|
|
3
|
+
import { GESTURE_SETTLE_MS } from './canvasGestureTuning.js';
|
|
4
|
+
import { canvasTransformActivity } from './canvasTransformActivity.js';
|
|
5
|
+
import { applyZoomLayer } from './zoomMechanism.js';
|
|
6
|
+
|
|
7
|
+
const useCanvasCamera = ({ appRef, worldRef, minimapRef, overlayRef, zoomLayerRef, panRef, zoomRef, onTransformChangeRef, render, minZoom, maxZoom, }) => {
|
|
8
|
+
const gestureActiveRef = useRef(false);
|
|
9
|
+
const gestureSettleTimerRef = useRef(undefined);
|
|
10
|
+
const transformFramePendingRef = useRef(false);
|
|
11
|
+
const refreshMinimap = useCallback(() => {
|
|
12
|
+
const app = appRef.current;
|
|
13
|
+
if (!app || !minimapRef.current)
|
|
14
|
+
return;
|
|
15
|
+
minimapRef.current.update(panRef.current, zoomRef.current, app.renderer.width, app.renderer.height);
|
|
16
|
+
}, [appRef, minimapRef, panRef, zoomRef]);
|
|
17
|
+
const applyWorldTransform = useCallback(() => {
|
|
18
|
+
const world = worldRef.current;
|
|
19
|
+
if (!world)
|
|
20
|
+
return;
|
|
21
|
+
world.position.set(panRef.current.x, panRef.current.y);
|
|
22
|
+
world.scale.set(zoomRef.current);
|
|
23
|
+
if (overlayRef.current) {
|
|
24
|
+
overlayRef.current.style.transform = `translate(${panRef.current.x}px, ${panRef.current.y}px)`;
|
|
25
|
+
}
|
|
26
|
+
if (zoomLayerRef.current) {
|
|
27
|
+
applyZoomLayer(zoomLayerRef.current, zoomRef.current, gestureActiveRef.current);
|
|
28
|
+
}
|
|
29
|
+
onTransformChangeRef.current?.(zoomRef.current, panRef.current);
|
|
30
|
+
canvasTransformActivity.notify();
|
|
31
|
+
}, [onTransformChangeRef, overlayRef, panRef, worldRef, zoomLayerRef, zoomRef]);
|
|
32
|
+
const scheduleTransformApply = useCallback(() => {
|
|
33
|
+
if (transformFramePendingRef.current)
|
|
34
|
+
return;
|
|
35
|
+
transformFramePendingRef.current = true;
|
|
36
|
+
requestAnimationFrame(() => {
|
|
37
|
+
transformFramePendingRef.current = false;
|
|
38
|
+
applyWorldTransform();
|
|
39
|
+
refreshMinimap();
|
|
40
|
+
render();
|
|
41
|
+
});
|
|
42
|
+
}, [applyWorldTransform, refreshMinimap, render]);
|
|
43
|
+
const noteGestureActivity = useCallback(() => {
|
|
44
|
+
gestureActiveRef.current = true;
|
|
45
|
+
canvasGesture.set(true);
|
|
46
|
+
clearTimeout(gestureSettleTimerRef.current);
|
|
47
|
+
gestureSettleTimerRef.current = setTimeout(() => {
|
|
48
|
+
gestureActiveRef.current = false;
|
|
49
|
+
applyWorldTransform();
|
|
50
|
+
canvasGesture.set(false);
|
|
51
|
+
}, GESTURE_SETTLE_MS);
|
|
52
|
+
}, [applyWorldTransform]);
|
|
53
|
+
useEffect(() => () => {
|
|
54
|
+
clearTimeout(gestureSettleTimerRef.current);
|
|
55
|
+
canvasGesture.set(false);
|
|
56
|
+
}, []);
|
|
57
|
+
const zoomTowards = useCallback((focusX, focusY, factor) => {
|
|
58
|
+
const newZoom = Math.max(minZoom, Math.min(maxZoom, zoomRef.current * factor));
|
|
59
|
+
const worldX = (focusX - panRef.current.x) / zoomRef.current;
|
|
60
|
+
const worldY = (focusY - panRef.current.y) / zoomRef.current;
|
|
61
|
+
panRef.current = {
|
|
62
|
+
x: focusX - worldX * newZoom,
|
|
63
|
+
y: focusY - worldY * newZoom,
|
|
64
|
+
};
|
|
65
|
+
zoomRef.current = newZoom;
|
|
66
|
+
noteGestureActivity();
|
|
67
|
+
scheduleTransformApply();
|
|
68
|
+
}, [minZoom, maxZoom, noteGestureActivity, scheduleTransformApply, panRef, zoomRef]);
|
|
69
|
+
return {
|
|
70
|
+
applyWorldTransform,
|
|
71
|
+
refreshMinimap,
|
|
72
|
+
scheduleTransformApply,
|
|
73
|
+
noteGestureActivity,
|
|
74
|
+
zoomTowards,
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export { useCanvasCamera };
|
|
79
|
+
//# sourceMappingURL=useCanvasCamera.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanvasCamera.js","sources":["../../../Canvas/useCanvasCamera.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useCallback, useEffect, useRef, type RefObject } from 'react';\nimport type * as PIXI from 'pixi.js';\nimport type { CanvasMinimapHandle } from './CanvasMinimap';\nimport { canvasGesture } from './canvasGesture';\nimport { GESTURE_SETTLE_MS } from './canvasGestureTuning';\nimport { canvasTransformActivity } from './canvasTransformActivity';\nimport { applyZoomLayer } from './zoomMechanism';\n\n/** The camera state and surfaces {@link useCanvasCamera} writes the transform to. */\nexport interface CanvasCameraOptions {\n appRef: RefObject<PIXI.Application | null>;\n worldRef: RefObject<PIXI.Container | null>;\n minimapRef: RefObject<CanvasMinimapHandle | null>;\n overlayRef: RefObject<HTMLDivElement | null>;\n zoomLayerRef: RefObject<HTMLDivElement | null>;\n panRef: RefObject<{ x: number; y: number }>;\n zoomRef: RefObject<number>;\n onTransformChangeRef: RefObject<\n ((zoom: number, pan: { x: number; y: number }) => void) | undefined\n >;\n render: () => void;\n minZoom: number;\n maxZoom: number;\n}\n\n/**\n * Applies the camera transform to the Pixi world, the HTML overlay and the minimap, coalesces\n * gesture frames, and zooms toward a focus point.\n * @param options The camera state, surfaces and zoom bounds.\n * @returns The transform operations every input path shares.\n */\nexport const useCanvasCamera = ({\n appRef,\n worldRef,\n minimapRef,\n overlayRef,\n zoomLayerRef,\n panRef,\n zoomRef,\n onTransformChangeRef,\n render,\n minZoom,\n maxZoom,\n}: CanvasCameraOptions) => {\n // Wheel gestures coalesce onto animation frames (trackpads deliver several events per frame, and each\n // direct style write would reflow), and while a gesture is in motion the zoom layer stays on the\n // composited transform path with virtualization told to hold still. The settle timer restores the crisp\n // resting state shortly after the last wheel event.\n const gestureActiveRef = useRef(false);\n const gestureSettleTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined,\n );\n const transformFramePendingRef = useRef(false);\n\n const refreshMinimap = useCallback(() => {\n const app = appRef.current;\n if (!app || !minimapRef.current) return;\n minimapRef.current.update(\n panRef.current,\n zoomRef.current,\n app.renderer.width,\n app.renderer.height,\n );\n }, [appRef, minimapRef, panRef, zoomRef]);\n\n const applyWorldTransform = useCallback(() => {\n const world = worldRef.current;\n if (!world) return;\n world.position.set(panRef.current.x, panRef.current.y);\n world.scale.set(zoomRef.current);\n // Pan is a CSS transform on the outer overlay; zoom is applied to an inner layer. The zoom\n // mechanism is hybrid, split at 100% (the two are identical there, so the switch is seamless):\n // - Above 100% we use CSS `zoom`, which re-lays-out and re-rasterizes at the effective\n // resolution — `transform: scale()` would rasterize once at 1x and stretch, blurring text\n // when UPSCALING in Safari. EXCEPT on multi-touch-capable devices (iPad and friends), which\n // always use `transform: scale()` even above 100% — see zoomMechanism.ts for why.\n // - At or below 100% we use `transform: scale()`, which is GPU-composited: zooming out stays\n // smooth (no per-frame full-board reflow) and text scales down properly. CSS `zoom` instead\n // reflows every step (jerky) and clamps font-size to a floor when zoomed out, so labels stop\n // shrinking and word-wrap. Downscaling never blurs, so the crispness reason does not apply here.\n if (overlayRef.current) {\n overlayRef.current.style.transform = `translate(${panRef.current.x}px, ${panRef.current.y}px)`;\n }\n if (zoomLayerRef.current) {\n applyZoomLayer(\n zoomLayerRef.current,\n zoomRef.current,\n gestureActiveRef.current,\n );\n }\n onTransformChangeRef.current?.(zoomRef.current, panRef.current);\n // Every applied transform frame is announced so followers (cursors, selection toolbar, tour\n // anchors) can ride the transform instead of polling it on their own animation-frame loops.\n canvasTransformActivity.notify();\n }, [onTransformChangeRef, overlayRef, panRef, worldRef, zoomLayerRef, zoomRef]);\n\n // One transform apply per animation frame, no matter how many wheel events arrived - the refs already\n // hold the latest pan/zoom, so coalescing loses nothing and avoids multiple reflows per frame.\n // No React state is touched here: a gesture must cause zero Canvas re-renders (the controls'\n // zoom readout polls the ref on its own slow interval instead).\n const scheduleTransformApply = useCallback(() => {\n if (transformFramePendingRef.current) return;\n transformFramePendingRef.current = true;\n requestAnimationFrame(() => {\n transformFramePendingRef.current = false;\n applyWorldTransform();\n refreshMinimap();\n render();\n });\n }, [applyWorldTransform, refreshMinimap, render]);\n\n // Marks the gesture active (composited zoom path, virtualization held still) and re-arms the settle\n // timer. On settle the crisp resting hybrid is re-applied through the same applyWorldTransform as\n // every gesture frame — one writer, one atomic style pass — and a new gesture cancels the timer,\n // so a settle can never land in the middle of the next gesture.\n const noteGestureActivity = useCallback(() => {\n gestureActiveRef.current = true;\n canvasGesture.set(true);\n clearTimeout(gestureSettleTimerRef.current);\n gestureSettleTimerRef.current = setTimeout(() => {\n gestureActiveRef.current = false;\n applyWorldTransform();\n canvasGesture.set(false);\n }, GESTURE_SETTLE_MS);\n }, [applyWorldTransform]);\n\n useEffect(\n () => () => {\n clearTimeout(gestureSettleTimerRef.current);\n canvasGesture.set(false);\n },\n [],\n );\n\n // Zooms toward a fixed focus point on the container, holding the world point under it still —\n // the one zoom every input path shares, whether the factor came from a wheel delta or a pinch.\n const zoomTowards = useCallback(\n (focusX: number, focusY: number, factor: number) => {\n const newZoom = Math.max(\n minZoom,\n Math.min(maxZoom, zoomRef.current * factor),\n );\n const worldX = (focusX - panRef.current.x) / zoomRef.current;\n const worldY = (focusY - panRef.current.y) / zoomRef.current;\n panRef.current = {\n x: focusX - worldX * newZoom,\n y: focusY - worldY * newZoom,\n };\n zoomRef.current = newZoom;\n noteGestureActivity();\n scheduleTransformApply();\n },\n [minZoom, maxZoom, noteGestureActivity, scheduleTransformApply, panRef, zoomRef],\n );\n\n return {\n applyWorldTransform,\n refreshMinimap,\n scheduleTransformApply,\n noteGestureActivity,\n zoomTowards,\n };\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MinimapItem } from './CanvasMinimap.js';
|
|
2
|
+
import type { CanvasItemRegistryContextValue } from './CanvasItemRegistryContextValue.js';
|
|
3
|
+
import type { CanvasItemRegistryEntry } from './CanvasItemRegistryEntry.js';
|
|
4
|
+
/**
|
|
5
|
+
* The registry CanvasItem children publish their measured bounds to, with the minimap items
|
|
6
|
+
* derived from it.
|
|
7
|
+
* @returns The registry, its context value, and the derived minimap items.
|
|
8
|
+
*/
|
|
9
|
+
export declare const useCanvasItemRegistry: () => {
|
|
10
|
+
itemRegistryRef: import("react").RefObject<Map<string, CanvasItemRegistryEntry>>;
|
|
11
|
+
registryContextValue: CanvasItemRegistryContextValue;
|
|
12
|
+
autoMinimapItems: MinimapItem[];
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=useCanvasItemRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanvasItemRegistry.d.ts","sourceRoot":"","sources":["../../../Canvas/useCanvasItemRegistry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,qCAAkC,CAAC;AACvF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA2B,CAAC;AAEzE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;CAmFjC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useRef, useState, useCallback, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
const useCanvasItemRegistry = () => {
|
|
4
|
+
const itemRegistryRef = useRef(new Map());
|
|
5
|
+
const [itemRegistryVersion, setItemRegistryVersion] = useState(0);
|
|
6
|
+
const registrySnapshotRef = useRef(new Map());
|
|
7
|
+
const registryListenersRef = useRef(new Set());
|
|
8
|
+
const notifyRegistryChanged = useCallback(() => {
|
|
9
|
+
registrySnapshotRef.current = new Map(itemRegistryRef.current);
|
|
10
|
+
setItemRegistryVersion((version) => version + 1);
|
|
11
|
+
registryListenersRef.current.forEach((listener) => listener());
|
|
12
|
+
}, []);
|
|
13
|
+
const registerItem = useCallback((id, entry) => {
|
|
14
|
+
const existing = itemRegistryRef.current.get(id);
|
|
15
|
+
if (existing &&
|
|
16
|
+
existing.x === entry.x &&
|
|
17
|
+
existing.y === entry.y &&
|
|
18
|
+
existing.width === entry.width &&
|
|
19
|
+
existing.height === entry.height &&
|
|
20
|
+
existing.anonymous === entry.anonymous)
|
|
21
|
+
return;
|
|
22
|
+
itemRegistryRef.current.set(id, entry);
|
|
23
|
+
notifyRegistryChanged();
|
|
24
|
+
}, [notifyRegistryChanged]);
|
|
25
|
+
const unregisterItem = useCallback((id) => {
|
|
26
|
+
if (!itemRegistryRef.current.delete(id))
|
|
27
|
+
return;
|
|
28
|
+
notifyRegistryChanged();
|
|
29
|
+
}, [notifyRegistryChanged]);
|
|
30
|
+
const getRegistrySnapshot = useCallback(() => registrySnapshotRef.current, []);
|
|
31
|
+
const subscribeToRegistry = useCallback((listener) => {
|
|
32
|
+
registryListenersRef.current.add(listener);
|
|
33
|
+
return () => {
|
|
34
|
+
registryListenersRef.current.delete(listener);
|
|
35
|
+
};
|
|
36
|
+
}, []);
|
|
37
|
+
const registryContextValue = useMemo(() => ({
|
|
38
|
+
register: registerItem,
|
|
39
|
+
unregister: unregisterItem,
|
|
40
|
+
getSnapshot: getRegistrySnapshot,
|
|
41
|
+
subscribe: subscribeToRegistry,
|
|
42
|
+
}), [registerItem, unregisterItem, getRegistrySnapshot, subscribeToRegistry]);
|
|
43
|
+
const autoMinimapItems = useMemo(() => {
|
|
44
|
+
const result = [];
|
|
45
|
+
itemRegistryRef.current.forEach((entry) => {
|
|
46
|
+
result.push({
|
|
47
|
+
x: entry.x,
|
|
48
|
+
y: entry.y,
|
|
49
|
+
width: entry.width,
|
|
50
|
+
height: entry.height,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
return result;
|
|
54
|
+
}, [itemRegistryVersion]);
|
|
55
|
+
return { itemRegistryRef, registryContextValue, autoMinimapItems };
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export { useCanvasItemRegistry };
|
|
59
|
+
//# sourceMappingURL=useCanvasItemRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanvasItemRegistry.js","sources":["../../../Canvas/useCanvasItemRegistry.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useCallback, useMemo, useRef, useState } from 'react';\nimport type { MinimapItem } from './CanvasMinimap';\nimport type { CanvasItemRegistryContextValue } from './CanvasItemRegistryContextValue';\nimport type { CanvasItemRegistryEntry } from './CanvasItemRegistryEntry';\n\n/**\n * The registry CanvasItem children publish their measured bounds to, with the minimap items\n * derived from it.\n * @returns The registry, its context value, and the derived minimap items.\n */\nexport const useCanvasItemRegistry = () => {\n const itemRegistryRef = useRef<Map<string, CanvasItemRegistryEntry>>(new Map());\n const [itemRegistryVersion, setItemRegistryVersion] = useState(0);\n\n // The read/subscribe side of the registry. The snapshot is a fresh shallow copy taken on every\n // actual content change (register/unregister guard against no-op writes below), so its identity\n // is a faithful change signal for useSyncExternalStore consumers — same reference back means\n // nothing changed. Entry objects are replaced wholesale on update, never mutated, so entries\n // shared between an old snapshot and a new one are safe to hold onto.\n const registrySnapshotRef = useRef<ReadonlyMap<string, CanvasItemRegistryEntry>>(\n new Map(),\n );\n const registryListenersRef = useRef<Set<() => void>>(new Set());\n\n const notifyRegistryChanged = useCallback(() => {\n registrySnapshotRef.current = new Map(itemRegistryRef.current);\n setItemRegistryVersion((version) => version + 1);\n registryListenersRef.current.forEach((listener) => listener());\n }, []);\n\n const registerItem = useCallback(\n (id: string, entry: CanvasItemRegistryEntry) => {\n const existing = itemRegistryRef.current.get(id);\n if (\n existing &&\n existing.x === entry.x &&\n existing.y === entry.y &&\n existing.width === entry.width &&\n existing.height === entry.height &&\n existing.anonymous === entry.anonymous\n )\n return;\n itemRegistryRef.current.set(id, entry);\n notifyRegistryChanged();\n },\n [notifyRegistryChanged],\n );\n\n const unregisterItem = useCallback(\n (id: string) => {\n if (!itemRegistryRef.current.delete(id)) return;\n notifyRegistryChanged();\n },\n [notifyRegistryChanged],\n );\n\n const getRegistrySnapshot = useCallback(\n (): ReadonlyMap<string, CanvasItemRegistryEntry> => registrySnapshotRef.current,\n [],\n );\n\n const subscribeToRegistry = useCallback((listener: () => void): (() => void) => {\n registryListenersRef.current.add(listener);\n return () => {\n registryListenersRef.current.delete(listener);\n };\n }, []);\n\n const registryContextValue = useMemo<CanvasItemRegistryContextValue>(\n () => ({\n register: registerItem,\n unregister: unregisterItem,\n getSnapshot: getRegistrySnapshot,\n subscribe: subscribeToRegistry,\n }),\n [registerItem, unregisterItem, getRegistrySnapshot, subscribeToRegistry],\n );\n\n const autoMinimapItems = useMemo((): MinimapItem[] => {\n void itemRegistryVersion; // subscribe to registry changes\n const result: MinimapItem[] = [];\n itemRegistryRef.current.forEach((entry) => {\n result.push({\n x: entry.x,\n y: entry.y,\n width: entry.width,\n height: entry.height,\n });\n });\n return result;\n }, [itemRegistryVersion]);\n\n return { itemRegistryRef, registryContextValue, autoMinimapItems };\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import type { CanvasHandle } from './CanvasHandle.js';
|
|
3
|
+
import type { CanvasItemRegistryEntry } from './CanvasItemRegistryEntry.js';
|
|
4
|
+
/** The camera state and operations {@link useCanvasNavigation} drives. */
|
|
5
|
+
export interface CanvasNavigationOptions {
|
|
6
|
+
containerRef: RefObject<HTMLDivElement | null>;
|
|
7
|
+
animationFrameRef: RefObject<number | null>;
|
|
8
|
+
panRef: RefObject<{
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}>;
|
|
12
|
+
zoomRef: RefObject<number>;
|
|
13
|
+
itemRegistryRef: RefObject<Map<string, CanvasItemRegistryEntry>>;
|
|
14
|
+
minZoom: number;
|
|
15
|
+
maxZoom: number;
|
|
16
|
+
pixiReady: boolean;
|
|
17
|
+
onHandleReadyRef: RefObject<((handle: CanvasHandle) => void) | undefined>;
|
|
18
|
+
applyWorldTransform: () => void;
|
|
19
|
+
refreshMinimap: () => void;
|
|
20
|
+
render: () => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The zoom controls, minimap panning, animated camera moves, and the imperative handle.
|
|
24
|
+
* @param options The container, camera state and operations.
|
|
25
|
+
* @returns The handlers for the integrated controls and minimap.
|
|
26
|
+
*/
|
|
27
|
+
export declare const useCanvasNavigation: ({ containerRef, animationFrameRef, panRef, zoomRef, itemRegistryRef, minZoom, maxZoom, pixiReady, onHandleReadyRef, applyWorldTransform, refreshMinimap, render, }: CanvasNavigationOptions) => {
|
|
28
|
+
handleZoomIn: () => void;
|
|
29
|
+
handleZoomOut: () => void;
|
|
30
|
+
handleZoomReset: () => void;
|
|
31
|
+
handleMinimapPan: (pan: {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
}) => void;
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=useCanvasNavigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanvasNavigation.d.ts","sourceRoot":"","sources":["../../../Canvas/useCanvasNavigation.ts"],"names":[],"mappings":"AAGA,OAAO,EAA0B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA2B,CAAC;AAGzE,0EAA0E;AAC1E,MAAM,WAAW,uBAAuB;IACpC,YAAY,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC/C,iBAAiB,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,MAAM,EAAE,SAAS,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC3B,eAAe,EAAE,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC,CAAC;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1E,mBAAmB,EAAE,MAAM,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,uKAa7B,uBAAuB;;;;4BA0DZ;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;CAsJrC,CAAC"}
|