fix widget focus interactions
This commit is contained in:
21 files changed
+119
-28
No files matched your search
@@ -35,7 +35,7 @@
|
||||
.surfaceHostAdvanced {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: calc(var(--mantine-z-index-modal) - 1);
|
||||
z-index: calc(var(--mantine-z-index-popover) - 1);
|
||||
width: auto;
|
||||
height: auto;
|
||||
pointer-events: none;
|
||||
|
||||
@@ -201,7 +201,7 @@ export const BoardAdvancedFocusProvider = ({ children }: PropsWithChildren) => {
|
||||
data-advanced-focus-overlay
|
||||
backgroundOpacity={0.38}
|
||||
blur={0}
|
||||
zIndex="calc(var(--mantine-z-index-modal) - 2)"
|
||||
zIndex="calc(var(--mantine-z-index-popover) - 2)"
|
||||
className={`${classes.backdrop} ${active.phase === "closing" ? classes.backdropClosing : ""}`}
|
||||
aria-hidden
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
const canScrollVertically = (element: HTMLElement, delta: number) => {
|
||||
if (element.scrollHeight <= element.clientHeight) return false;
|
||||
|
||||
const overflowY = getComputedStyle(element).overflowY;
|
||||
if (overflowY !== "auto" && overflowY !== "scroll") return false;
|
||||
|
||||
return delta < 0 ? element.scrollTop > 0 : element.scrollTop + element.clientHeight < element.scrollHeight;
|
||||
};
|
||||
|
||||
export const redirectShiftWheel = (root: HTMLElement, target: EventTarget | null, delta: number) => {
|
||||
if (delta === 0) return false;
|
||||
|
||||
let current = target instanceof HTMLElement ? target : null;
|
||||
while (current && root.contains(current)) {
|
||||
if (canScrollVertically(current, delta)) {
|
||||
current.scrollTop += delta;
|
||||
return true;
|
||||
}
|
||||
if (current === root) break;
|
||||
current = current.parentElement;
|
||||
}
|
||||
|
||||
const viewport = [...root.querySelectorAll<HTMLElement>("[data-scrollbars='y'], [data-scrollbars='xy']")].find(
|
||||
(element) => canScrollVertically(element, delta),
|
||||
);
|
||||
if (!viewport) return false;
|
||||
|
||||
viewport.scrollTop += delta;
|
||||
return true;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CSSProperties, KeyboardEvent, MutableRefObject } from "react";
|
||||
import type { CSSProperties, KeyboardEvent, MutableRefObject, WheelEvent } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { ActionIcon, Badge, Box, Card } from "@mantine/core";
|
||||
import { useElementSize, useViewportSize } from "@mantine/hooks";
|
||||
@@ -12,13 +12,20 @@ import { useRequiredBoard } from "@homarr/boards/context";
|
||||
import { useEditMode } from "@homarr/boards/edit-mode";
|
||||
import { useSettings } from "@homarr/settings";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { loadWidgetDynamic, reduceWidgetOptionsWithDefaultValues, widgetImports } from "@homarr/widgets";
|
||||
import type { WidgetDefinition } from "@homarr/widgets";
|
||||
import {
|
||||
loadWidgetDynamic,
|
||||
reduceWidgetOptionsWithDefaultValues,
|
||||
supportsAdvancedFocus as definitionSupportsAdvancedFocus,
|
||||
widgetImports,
|
||||
} from "@homarr/widgets";
|
||||
import { WidgetError } from "@homarr/widgets/errors";
|
||||
|
||||
import type { SectionItem } from "~/app/[locale]/boards/_types";
|
||||
import advancedFocusClasses from "../advanced-focus/advanced-focus.module.css";
|
||||
import { useAdvancedFocus } from "../advanced-focus/context";
|
||||
import { getAdvancedFocusClosePosition, getAdvancedFocusRect } from "../advanced-focus/geometry";
|
||||
import { redirectShiftWheel } from "../advanced-focus/wheel";
|
||||
import classes from "../sections/item.module.css";
|
||||
import { useItemActions } from "./item-actions";
|
||||
import itemContentClasses from "./item-content.module.css";
|
||||
@@ -48,7 +55,9 @@ export const BoardItemContent = ({ item }: BoardItemContentProps) => {
|
||||
const widgetStateRef = useRef<Record<string, unknown> | null>(null);
|
||||
const closeButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const { active, open, close, dismiss, hover, leave } = useAdvancedFocus();
|
||||
const supportsAdvancedFocus = item.kind !== "app";
|
||||
const supportsAdvancedFocus = definitionSupportsAdvancedFocus(
|
||||
widgetImports[item.kind].definition as WidgetDefinition,
|
||||
);
|
||||
const advancedViewId = `advanced-focus-${item.id}`;
|
||||
const activeFocus = supportsAdvancedFocus && active?.itemId === item.id ? active : null;
|
||||
const isAdvanced = activeFocus !== null;
|
||||
@@ -81,6 +90,12 @@ export const BoardItemContent = ({ item }: BoardItemContentProps) => {
|
||||
event.preventDefault();
|
||||
openAdvanced(true);
|
||||
};
|
||||
const handleWheel = (event: WheelEvent<HTMLDivElement>) => {
|
||||
if (!isAdvanced || !event.shiftKey) return;
|
||||
const delta = event.deltaY || event.deltaX;
|
||||
if (!redirectShiftWheel(event.currentTarget, event.target, delta)) return;
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -128,6 +143,8 @@ export const BoardItemContent = ({ item }: BoardItemContentProps) => {
|
||||
id={isAdvanced ? advancedViewId : undefined}
|
||||
role={isAdvanced ? "region" : undefined}
|
||||
aria-label={isAdvanced ? advancedViewLabel : undefined}
|
||||
data-advanced-focus-surface={isAdvanced || undefined}
|
||||
onWheel={handleWheel}
|
||||
radius={board.itemRadius}
|
||||
p={isAdvanced ? undefined : 0}
|
||||
h={isAdvanced ? undefined : "100%"}
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
getWidgetQueryKeys,
|
||||
getWidgetRuntimeQueries,
|
||||
reduceWidgetOptionsWithDefaultValues,
|
||||
supportsAdvancedFocus,
|
||||
widgetImports,
|
||||
} from "@homarr/widgets";
|
||||
import { WidgetEditModal } from "@homarr/widgets/modals";
|
||||
@@ -70,6 +71,7 @@ export const WidgetContextMenu = ({ item, widgetStateRef, sourceRef, children }:
|
||||
useItemActions();
|
||||
const { data: integrationData, isPending } = clientApi.integration.all.useQuery();
|
||||
const currentDefinition = useMemo(() => widgetImports[item.kind].definition, [item.kind]);
|
||||
const canOpenAdvancedFocus = supportsAdvancedFocus(currentDefinition as WidgetDefinition);
|
||||
const { gridstack } = useSectionContext().refs;
|
||||
const queryClient = useQueryClient();
|
||||
const { open: openAdvancedFocus } = useAdvancedFocus();
|
||||
@@ -250,7 +252,7 @@ export const WidgetContextMenu = ({ item, widgetStateRef, sourceRef, children }:
|
||||
>
|
||||
<Menu.ContextMenu>{children}</Menu.ContextMenu>
|
||||
<Menu.Dropdown>
|
||||
{item.kind !== "app" && (
|
||||
{canOpenAdvancedFocus && (
|
||||
<>
|
||||
<Menu.Item
|
||||
closeMenuOnClick
|
||||
|
||||
Reference in new issue
Block a user