fix: refine widget interactions

This commit is contained in:
Thomas Camlong authored and ajnart committed 2026-08-14 11:07:08 +02:00
1 parent 836a4cd46b
commit 56cc021f98
21 files changed
+163 -85

No files matched your search

@@ -76,34 +76,12 @@
pointer-events: none;
}
.surfaceControls {
position: absolute;
inset: 0;
z-index: 2;
pointer-events: none;
}
.closeButton {
position: absolute;
z-index: 2;
pointer-events: auto;
}
.touchButton {
position: absolute;
right: 8px;
bottom: 8px;
z-index: 2;
display: none;
pointer-events: auto;
}
@media (any-pointer: coarse) {
.touchButton {
display: inline-flex;
}
}
@keyframes focus-backdrop-in {
from {
opacity: 0;
@@ -37,6 +37,8 @@ interface AdvancedFocusContextValue {
const AdvancedFocusContext = createContext<AdvancedFocusContextValue | null>(null);
const HOVER_DELAY_MS = 150;
const CLOSE_DURATION_MS = 180;
const interactiveSelector =
"a[href], button, input, textarea, select, [contenteditable='true'], [role='button'], [role='link'], [role='menuitem'], [role='option']";
const isEditableTarget = (target: EventTarget | null) =>
target instanceof HTMLElement &&
@@ -131,6 +133,7 @@ export const BoardAdvancedFocusProvider = ({ children }: PropsWithChildren) => {
const handlePointerMove = (event: PointerEvent) => {
const current = activeRef.current;
if (!shiftHeldRef.current || current?.activation !== "preview") return;
if (event.target instanceof Element && event.target.closest(interactiveSelector)) return;
const underlyingSource = document
.elementsFromPoint(event.clientX, event.clientY)
@@ -22,14 +22,5 @@ export const redirectShiftWheel = (root: HTMLElement, target: EventTarget | null
if (current === root) break;
current = current.parentElement;
}
// A nested control can reach its edge before the widget's main Mantine viewport.
// Hand the gesture to that viewport instead of making Shift+wheel feel stuck.
const viewport = [...root.querySelectorAll<HTMLElement>("[data-scrollbars='y'], [data-scrollbars='xy']")].find(
(element) => isVerticalScrollContainer(element) && canScrollVertically(element, delta),
);
if (!viewport) return false;
viewport.scrollTop += delta;
return true;
return false;
};
@@ -27,11 +27,16 @@ const matches = (kind: WidgetKind, path: readonly string[], input: unknown, scop
describe("matchesWidgetItemQuery", () => {
test("matches only the current Custom API definition", () => {
const scope = createScope({ options: { definitionId: "definition-1" } });
const input = { boardId: "board-1", itemId: "item-1", definitionId: "definition-1" };
expect(matches("customApi", ["widget", "customApi", "getData"], { definitionId: "definition-1" }, scope)).toBe(
true,
expect(matches("customApi", ["widget", "customApi", "getData"], input, scope)).toBe(true);
expect(
matches("customApi", ["widget", "customApi", "getData"], { ...input, definitionId: "definition-2" }, scope),
).toBe(false);
expect(matches("customApi", ["widget", "customApi", "getData"], { ...input, itemId: "item-2" }, scope)).toBe(
false,
);
expect(matches("customApi", ["widget", "customApi", "getData"], { definitionId: "definition-2" }, scope)).toBe(
expect(matches("customApi", ["widget", "customApi", "getData"], { ...input, boardId: "board-2" }, scope)).toBe(
false,
);
});