diff --git a/packages/editor/src/lib/components/Canvas.tsx b/packages/editor/src/lib/components/Canvas.tsx
index 197463f6e..6f99bdad1 100644
--- a/packages/editor/src/lib/components/Canvas.tsx
+++ b/packages/editor/src/lib/components/Canvas.tsx
@@ -355,11 +355,16 @@ const HoveredShapeIndicator = function HoveredShapeIndicator() {
const isCoarsePointer = useValue('coarse pointer', () => editor.instanceState.isCoarsePointer, [
editor,
])
+ const isHoveringCanvas = useValue(
+ 'hovering canvas',
+ () => editor.instanceState.isHoveringCanvas,
+ [editor]
+ )
const hoveredShapeId = useValue('hovered id', () => editor.currentPageState.hoveredShapeId, [
editor,
])
- if (isCoarsePointer || !hoveredShapeId || !HoveredShapeIndicator) return null
+ if (isCoarsePointer || !isHoveringCanvas || !hoveredShapeId || !HoveredShapeIndicator) return null
return
}
diff --git a/packages/editor/src/lib/hooks/useCanvasEvents.ts b/packages/editor/src/lib/hooks/useCanvasEvents.ts
index 4eb93ea78..bd4c029bb 100644
--- a/packages/editor/src/lib/hooks/useCanvasEvents.ts
+++ b/packages/editor/src/lib/hooks/useCanvasEvents.ts
@@ -74,6 +74,20 @@ export function useCanvasEvents() {
})
}
+ function onPointerEnter(e: React.PointerEvent) {
+ if ((e as any).isKilled) return
+ if (editor.instanceState.isPenMode && e.pointerType !== 'pen') return
+ const canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'
+ editor.updateInstanceState({ isHoveringCanvas: canHover ? true : null })
+ }
+
+ function onPointerLeave(e: React.PointerEvent) {
+ if ((e as any).isKilled) return
+ if (editor.instanceState.isPenMode && e.pointerType !== 'pen') return
+ const canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'
+ editor.updateInstanceState({ isHoveringCanvas: canHover ? false : null })
+ }
+
function onTouchStart(e: React.TouchEvent) {
;(e as any).isKilled = true
// todo: investigate whether this effects keyboard shortcuts
@@ -118,6 +132,8 @@ export function useCanvasEvents() {
onPointerDown,
onPointerMove,
onPointerUp,
+ onPointerEnter,
+ onPointerLeave,
onDragOver,
onDrop,
onTouchStart,
diff --git a/packages/tlschema/api-report.md b/packages/tlschema/api-report.md
index 190712b85..5ba13e082 100644
--- a/packages/tlschema/api-report.md
+++ b/packages/tlschema/api-report.md
@@ -1033,6 +1033,7 @@ export interface TLInstance extends BaseRecord<'instance', TLInstanceId> {
isFocusMode: boolean;
// (undocumented)
isGridMode: boolean;
+ isHoveringCanvas: boolean | null;
// (undocumented)
isPenMode: boolean;
// (undocumented)
diff --git a/packages/tlschema/src/migrations.test.ts b/packages/tlschema/src/migrations.test.ts
index ce642475c..8a629f20a 100644
--- a/packages/tlschema/src/migrations.test.ts
+++ b/packages/tlschema/src/migrations.test.ts
@@ -1535,6 +1535,18 @@ describe('Adding canSnap to line handles', () => {
})
})
+describe('add isHoveringCanvas to TLInstance', () => {
+ const { up, down } = instanceMigrations.migrators[instanceVersions.AddHoveringCanvas]
+
+ test('up works as expected', () => {
+ expect(up({})).toEqual({ isHoveringCanvas: null })
+ })
+
+ test('down works as expected', () => {
+ expect(down({ isHoveringCanvas: null })).toEqual({})
+ })
+})
+
/* --- PUT YOUR MIGRATIONS TESTS ABOVE HERE --- */
for (const migrator of allMigrators) {
diff --git a/packages/tlschema/src/records/TLInstance.ts b/packages/tlschema/src/records/TLInstance.ts
index 6d294869f..4717b8acc 100644
--- a/packages/tlschema/src/records/TLInstance.ts
+++ b/packages/tlschema/src/records/TLInstance.ts
@@ -40,6 +40,11 @@ export interface TLInstance extends BaseRecord<'instance', TLInstanceId> {
isFocused: boolean
devicePixelRatio: number
isCoarsePointer: boolean
+ /**
+ * Will be null if the pointer doesn't support hovering (e.g. touch), but true or false
+ * otherwise
+ */
+ isHoveringCanvas: boolean | null
openMenus: string[]
isChangingStyle: boolean
isReadonly: boolean
@@ -85,6 +90,7 @@ export function createInstanceRecordType(stylesById: Map {
@@ -453,6 +461,19 @@ export const instanceMigrations = defineMigrations({
}
},
},
+ [instanceVersions.AddHoveringCanvas]: {
+ up: (record) => {
+ return {
+ ...record,
+ isHoveringCanvas: null,
+ }
+ },
+ down: ({ isHoveringCanvas: _, ...record }) => {
+ return {
+ ...record,
+ }
+ },
+ },
},
})