fix: refine widget interactions
This commit is contained in:
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,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user