From 850faacfd2ec4ef8ca4a71ce625274a4156d4e41 Mon Sep 17 00:00:00 2001 From: dudubtw Date: Wed, 20 Nov 2024 12:44:06 -0300 Subject: [PATCH 1/2] - Made so the popover can flip when a custom position is passed. - Added `asChild` like api to the popover trigger when you pass the children as a function. - Added keyboard focus ring to the list when the user focus the input. - Update the speed popup to use the list component. - Remove the filter instead of disabling them when the filters are collapsed. - The focus trap library we were using had some issues, moved it inside our project so we can best customize it to our needs. --- bun.lockb | Bin 317916 -> 317143 bytes package.json | 1 - .../src/components/popover/Popover.tsx | 59 +++-- .../src/components/popover/PopoverContent.tsx | 8 +- .../src/components/popover/PopoverTrigger.tsx | 38 +++- .../src/components/raw-list/RawList.tsx | 2 +- .../selectable-list/SelectableListItem.tsx | 15 +- .../song/song-detail/SongControls.tsx | 47 ++-- .../song/song-list-search/SongListSearch.tsx | 8 +- src/renderer/src/lib/focus-trap.ts | 205 ++++++++++++++++++ .../roving-focus-group/rovingFocusGroup.ts | 20 +- .../src/scenes/main-scene/MainScene.tsx | 23 +- 12 files changed, 327 insertions(+), 99 deletions(-) create mode 100644 src/renderer/src/lib/focus-trap.ts diff --git a/bun.lockb b/bun.lockb index b1b27fe5769191ac2b0b55922d85793e2841e0df..79e1130d7bb9c6779c7835e5c203eca76697b45e 100755 GIT binary patch delta 59348 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 { const trigger = triggerRef(); const content = contentRef(); @@ -125,9 +108,12 @@ function useProviderValue(props: Props) { return; } - const middleware: Middleware[] = [offset(props.offset)]; - if (typeof props.position !== "undefined") { - middleware.push(useCustomCoords); + const [x, y] = props.position?.() ?? []; + const hasCustomPosition = x !== undefined && y !== undefined; + + const middleware: Middleware[] = []; + if (!hasCustomPosition) { + middleware.push(offset(props.offset)); } if (typeof props.flip !== "undefined") { middleware.push(flip(props.flip === true ? undefined : props.flip)); @@ -136,10 +122,35 @@ function useProviderValue(props: Props) { middleware.push(shift(props.shift === true ? undefined : props.shift)); } - computePosition(trigger, content, { - placement: props.placement, - strategy: "fixed", + const getBoundingClientRect = () => { + const triggerRect = trigger.getBoundingClientRect(); + if (!hasCustomPosition) { + return triggerRect; + } + + return { + x, + y, + top: y, + right: x, + bottom: y, + left: x, + width: 0, + height: 0, + }; + }; + + const getPlacement = () => { + if (hasCustomPosition) { + return DEFAULT_CUSTOM_POSITION_PLACEMENT; + } + return props.placement; + }; + + computePosition({ getBoundingClientRect }, content, { middleware, + strategy: "fixed", + placement: getPlacement(), }).then(setPosition); }; diff --git a/src/renderer/src/components/popover/PopoverContent.tsx b/src/renderer/src/components/popover/PopoverContent.tsx index 75b28ef2..ea663266 100644 --- a/src/renderer/src/components/popover/PopoverContent.tsx +++ b/src/renderer/src/components/popover/PopoverContent.tsx @@ -1,7 +1,7 @@ import { popoverStack, usePopover } from "./Popover"; import { ComputePositionReturn } from "@floating-ui/dom"; import { cn, sn } from "@renderer/lib/css.utils"; -import createFocusTrap from "solid-focus-trap"; +import createFocusTrap from "@renderer/lib/focus-trap"; import { Component, createMemo, onCleanup, onMount, Show } from "solid-js"; import { JSX } from "solid-js/jsx-runtime"; @@ -20,18 +20,18 @@ export type Props = JSX.IntrinsicElements["div"]; const PopoverContent: Component = (props) => { const state = usePopover(); - const isLastPopoupOpen = createMemo(() => { + const isLastPopupOpen = createMemo(() => { return state.isOpen() && popoverStack().at(-1) === state.id(); }); createFocusTrap({ element: state.contentRef, - enabled: isLastPopoupOpen, + enabled: () => isLastPopupOpen(), }); onMount(() => { const handleKeyUp = (e: KeyboardEvent) => { - if (!isLastPopoupOpen()) { + if (!isLastPopupOpen()) { return; } diff --git a/src/renderer/src/components/popover/PopoverTrigger.tsx b/src/renderer/src/components/popover/PopoverTrigger.tsx index ce8d2e51..2a3333fb 100644 --- a/src/renderer/src/components/popover/PopoverTrigger.tsx +++ b/src/renderer/src/components/popover/PopoverTrigger.tsx @@ -1,20 +1,36 @@ import { usePopover } from "./Popover"; -import { Component } from "solid-js"; +import { Component, splitProps, Switch, Match } from "solid-js"; import { JSX } from "solid-js/jsx-runtime"; -export type Props = JSX.IntrinsicElements["button"]; -const PopoverTrigger: Component = (props) => { +type PartialButtonProps = Partial; +export type Props = Omit & { + children?: JSX.Element | ((props: PartialButtonProps) => JSX.Element); +}; + +const PopoverTrigger: Component = (_props) => { const state = usePopover(); + const [props, rest] = splitProps(_props, ["children"]); + + const triggerProps: PartialButtonProps = { + ref: state.setTriggerRef, + // it complains about "data-open" not being a string for some reason + // so we have to cast it to a string + ["data-open" as string]: state.isOpen(), + onClick: () => { + state?.open(); + }, + ...rest, + }; return ( - + + ); }; diff --git a/src/renderer/src/components/raw-list/RawList.tsx b/src/renderer/src/components/raw-list/RawList.tsx index de544a40..d11e30f6 100644 --- a/src/renderer/src/components/raw-list/RawList.tsx +++ b/src/renderer/src/components/raw-list/RawList.tsx @@ -10,7 +10,7 @@ export const RawListItem: Component = (_props) return ( - + + {(triggerProps) => ( + + )} + @@ -234,9 +232,16 @@ const RightPart = () => {
- - {(amount) => {amount}} - + setSpeed(Number(newSpeed))} + > + + {(amount) => ( + {amount} + )} + + @@ -247,18 +252,4 @@ const RightPart = () => { ); }; -type SpeedOptionProps = { - amount: number; -}; -const SpeedOption: ParentComponent = (props) => { - return ( - - ); -}; - export default SongControls; diff --git a/src/renderer/src/components/song/song-list-search/SongListSearch.tsx b/src/renderer/src/components/song/song-list-search/SongListSearch.tsx index 874309ab..ba643b28 100644 --- a/src/renderer/src/components/song/song-list-search/SongListSearch.tsx +++ b/src/renderer/src/components/song/song-list-search/SongListSearch.tsx @@ -6,7 +6,7 @@ import { SongListSearchTags } from "./SongListSearchTags"; import Button from "@renderer/components/button/Button"; import { Input } from "@renderer/components/input/Input"; import { FilterIcon, SearchIcon, FilterXIcon } from "lucide-solid"; -import { Accessor, Component, createSignal, Match, Setter, Signal, Switch } from "solid-js"; +import { Accessor, Component, createSignal, Match, Setter, Signal, Switch, Show } from "solid-js"; export type SearchProps = { tags: Signal; @@ -109,8 +109,10 @@ const SongListSearch: Component = (props) => { }} >
- - + + + +
diff --git a/src/renderer/src/lib/focus-trap.ts b/src/renderer/src/lib/focus-trap.ts new file mode 100644 index 00000000..ab69c1aa --- /dev/null +++ b/src/renderer/src/lib/focus-trap.ts @@ -0,0 +1,205 @@ +import { createEffect, createMemo, createSignal, mergeProps, onCleanup, untrack } from "solid-js"; + +type MaybeAccessor = T | (() => T); +type MaybeAccessorValue> = T extends () => unknown + ? ReturnType + : T; + +const focusableElementSelector = + 'a[href]:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), textarea:not([tabindex="-1"]), select:not([tabindex="-1"]), details:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])'; + +const EVENT_INITIAL_FOCUS = "focusTrap.initialFocus"; +const EVENT_FINAL_FOCUS = "focusTrap.finalFocus"; +const EVENT_OPTIONS = { bubbles: false, cancelable: true }; + +const access = >(v: T): MaybeAccessorValue => + typeof v === "function" ? v() : v; + +const afterPaint = (fn: () => void) => requestAnimationFrame(() => requestAnimationFrame(fn)); + +/** + * Traps focus inside the given element. Is aware of changes being made to the DOM tree inside the focus trap by using a `MutationObserver`. + * + * @param props.element - Element to trap focus in. + * @param props.enabled - If the focus trap is enabled. *Default = `true`* + * @param props.observeChanges - Whether to watch for changes being made to the DOM tree inside the focus trap and reload the focus trap accordingly. *Default = `true`* + * @param props.initialFocusElement - The element to receive focus when the focus trap is activated. *Default = The first focusable element inside `element`* + * @param props.restoreFocus - If the focus should be restored to the element the focus was on initially when the focus trap is deactivated. *Default = `true`* + * @param props.finalFocusElement - The element to receive focus when the focus trap is deactivated (`enabled` = `false`). *Default = The element the focus was on initially* + * @param props.onInitialFocus - Callback fired when focus moves inside the focus trap. Can be prevented by calling `event.preventDefault`. + * @param props.onFinalFocus - Callback fired when focus moves outside the focus trap. Can be prevented by calling `event.preventDefault`. + */ +const createFocusTrap = (props: { + element: MaybeAccessor; + enabled?: MaybeAccessor; + observeChanges?: MaybeAccessor; + initialFocusElement?: MaybeAccessor; + restoreFocus?: MaybeAccessor; + finalFocusElement?: MaybeAccessor; + onInitialFocus?: (event: Event) => void; + onFinalFocus?: (event: Event) => void; +}) => { + const defaultedProps = mergeProps( + { + enabled: true, + observeChanges: true, + restoreFocus: true, + }, + props, + ); + + const [focusableElements, setFocusableElements] = createSignal(null); + const firstFocusElement = createMemo(() => { + const _focusableElements = focusableElements(); + if (!_focusableElements) return null; + return _focusableElements[0] ?? null; + }); + const lastFocusElement = createMemo(() => { + const _focusableElements = focusableElements(); + if (!_focusableElements) return null; + return _focusableElements[_focusableElements.length - 1] ?? null; + }); + + let originalFocusedElement: HTMLElement | null = null; + + const mutationObserverCallback = () => { + loadFocusTrap(access(defaultedProps.element)!); + if (document.activeElement === null || document.activeElement === document.body) { + initialFocus(access(defaultedProps.element)!); + } + }; + + createEffect(() => { + const container = access(defaultedProps.element); + if (container && access(defaultedProps.enabled)) { + originalFocusedElement = document.activeElement as HTMLElement | null; + + untrack(() => { + loadFocusTrap(container); + initialFocus(container); + }); + + const observer = new MutationObserver(mutationObserverCallback); + if (access(defaultedProps.observeChanges)) { + observer.observe(container, { + subtree: true, + childList: true, + attributes: true, + attributeFilter: ["tabindex"], + }); + } + + onCleanup(() => { + if (access(defaultedProps.observeChanges)) { + observer.disconnect(); + } + setFocusableElements(null); + restoreFocus(container); + }); + } + }); + + createEffect(() => { + const _focusableElements = focusableElements(); + if (_focusableElements === null || _focusableElements.length !== 0) return; + + document.addEventListener("keydown", preventFocusChange); + onCleanup(() => { + document.removeEventListener("keydown", preventFocusChange); + }); + }); + + createEffect(() => { + const _firstFocusElement = firstFocusElement(); + if (!_firstFocusElement) return; + + _firstFocusElement.addEventListener("keydown", onFirstFocusElementKeyDown); + onCleanup(() => { + _firstFocusElement.removeEventListener("keydown", onFirstFocusElementKeyDown); + }); + }); + + createEffect(() => { + const _lastFocusElement = lastFocusElement(); + if (!_lastFocusElement) return; + + _lastFocusElement.addEventListener("keydown", onLastFocusElementKeyDown); + onCleanup(() => { + _lastFocusElement.removeEventListener("keydown", onLastFocusElementKeyDown); + }); + }); + + const loadFocusTrap = (container: HTMLElement) => { + setFocusableElements( + Array.from(container.querySelectorAll(focusableElementSelector)) as HTMLElement[], + ); + }; + + const initialFocus = (container: HTMLElement) => { + const initialFocusElement = + access(defaultedProps.initialFocusElement) ?? firstFocusElement() ?? container; + const onInitialFocus = defaultedProps.onInitialFocus; + + let event: CustomEvent | undefined; + if (onInitialFocus) { + event = new CustomEvent(EVENT_INITIAL_FOCUS, EVENT_OPTIONS); + container.addEventListener(EVENT_INITIAL_FOCUS, onInitialFocus); + container.dispatchEvent(event); + container.removeEventListener(EVENT_INITIAL_FOCUS, onInitialFocus); + } + + if (event?.defaultPrevented === true) { + return; + } + + afterPaint(() => initialFocusElement.focus()); + }; + + const onFirstFocusElementKeyDown = (event: KeyboardEvent) => { + if (event.key === "Tab" && event.shiftKey) { + event.preventDefault(); + lastFocusElement()!.focus(); + } + }; + + const onLastFocusElementKeyDown = (event: KeyboardEvent) => { + if (event.key === "Tab" && !event.shiftKey) { + event.preventDefault(); + firstFocusElement()!.focus(); + } + }; + + const preventFocusChange = (event: KeyboardEvent) => { + if (event.key === "Tab") { + event.preventDefault(); + } + }; + + const restoreFocus = (container: HTMLElement) => { + const restoreFocus = access(defaultedProps.restoreFocus); + if (!restoreFocus) return; + + const finalFocusElement = access(defaultedProps.finalFocusElement) ?? originalFocusedElement; + + if (!finalFocusElement) { + return; + } + + let event: CustomEvent | undefined; + const onFinalFocus = defaultedProps.onFinalFocus; + if (onFinalFocus) { + event = new CustomEvent(EVENT_FINAL_FOCUS, EVENT_OPTIONS); + container.addEventListener(EVENT_FINAL_FOCUS, onFinalFocus); + container.dispatchEvent(event); + container.removeEventListener(EVENT_FINAL_FOCUS, onFinalFocus); + } + + if (event?.defaultPrevented === true) { + return; + } + + afterPaint(() => finalFocusElement.focus()); + }; +}; + +export default createFocusTrap; diff --git a/src/renderer/src/lib/roving-focus-group/rovingFocusGroup.ts b/src/renderer/src/lib/roving-focus-group/rovingFocusGroup.ts index ef939ba9..b1cbb214 100644 --- a/src/renderer/src/lib/roving-focus-group/rovingFocusGroup.ts +++ b/src/renderer/src/lib/roving-focus-group/rovingFocusGroup.ts @@ -2,8 +2,11 @@ import useControllableState from "../controllable-state"; import { Accessor, createMemo, createSignal, onCleanup, onMount } from "solid-js"; import { DOMElement } from "solid-js/jsx-runtime"; -const ITEM_DATA_ATTR = "data-item"; -const DEFAULT_SELECTED_VALUE = ""; +export const ITEM_DATA_ATTR = "data-item"; +export const ITEM_DATA_FOCUSED = "data-focused"; +export const ITEM_DATA_FOCUS_KEYBOARD = "data-focused-keyboard"; +export const ITEM_DATA_FOCUS_MOUSE = "data-focused-mouse"; +export const DEFAULT_SELECTED_VALUE = ""; const canFocus = ( element: Element | null | undefined, @@ -27,6 +30,9 @@ type Params = { export function useRovingFocusGroup(props: Params = {}) { let container!: HTMLElement; + const [currentInteractionMode, setCurrentInteractionMode] = createSignal<"mouse" | "keyboard">( + "mouse", + ); const [registeredTabs, setRegisteredTabs] = createSignal([]); const [hasMounted, setHasMounted] = createSignal(false); @@ -72,6 +78,7 @@ export function useRovingFocusGroup(props: Params = {}) { return; } + setCurrentInteractionMode("keyboard"); props.onKeyUp?.(); const orderedNodes = findOrderedNodes(); @@ -152,6 +159,7 @@ export function useRovingFocusGroup(props: Params = {}) { currentlyActiveElement, onListmounted, tryFocusFirstItem, + currentInteractionMode, value: currentStopId, attrs: { ref: (node: HTMLElement) => { @@ -176,6 +184,7 @@ export function useRovingFocusGroup(props: Params = {}) { return; } + setCurrentInteractionMode("mouse"); const optionToFocus = getCurrentOption(); if (!canFocus(optionToFocus)) { return; @@ -225,12 +234,15 @@ export function useRovingFocusGroup(props: Params = {}) { return { isSelected, tabIndex, - attrs: { + attrs: () => ({ onKeyUp: handleItemKeyUp, onClick: handleClick, onPointerMove: handlePointerMove, [ITEM_DATA_ATTR]: tabStopId ?? "fallback", - }, + [ITEM_DATA_FOCUS_KEYBOARD]: isSelected() && currentInteractionMode() === "keyboard", + [ITEM_DATA_FOCUS_MOUSE]: isSelected() && currentInteractionMode() === "mouse", + [ITEM_DATA_FOCUSED]: isSelected(), + }), }; }, }; diff --git a/src/renderer/src/scenes/main-scene/MainScene.tsx b/src/renderer/src/scenes/main-scene/MainScene.tsx index d5cf72ac..817f7da6 100644 --- a/src/renderer/src/scenes/main-scene/MainScene.tsx +++ b/src/renderer/src/scenes/main-scene/MainScene.tsx @@ -17,7 +17,7 @@ import { song } from "@renderer/components/song/song.utils"; import { WindowsControls } from "@renderer/components/windows-control/WindowsControl"; import { os } from "@renderer/lib/os"; import { Layers3Icon } from "lucide-solid"; -import { Accessor, Component, createSignal, Match, Switch } from "solid-js"; +import { Accessor, Component, Match, Switch } from "solid-js"; const MainScene: Component = () => { const { maxSidebarWidth, offsetFromPanel } = useMainResizableOptions(); @@ -79,22 +79,25 @@ const MainScene: Component = () => { }; const Queue: Component = () => { - const [isOpen, setIsOpen] = createSignal(false); - return ( - setIsOpen(true)} class="no-drag absolute right-2 top-2 z-10"> - - + + {(triggerProps) => ( + + )} + From c66174d253d616666e8b2f9f7237396ece0ac45e Mon Sep 17 00:00:00 2001 From: dudubtw Date: Wed, 20 Nov 2024 12:55:59 -0300 Subject: [PATCH 2/2] - Apply new list attrs to the dropdown and tabs --- .../src/components/dropdown-list/DropdownListItem.tsx | 7 ++----- src/renderer/src/components/tabs/TabsTrigger.tsx | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/components/dropdown-list/DropdownListItem.tsx b/src/renderer/src/components/dropdown-list/DropdownListItem.tsx index ddab6caf..acece819 100644 --- a/src/renderer/src/components/dropdown-list/DropdownListItem.tsx +++ b/src/renderer/src/components/dropdown-list/DropdownListItem.tsx @@ -7,7 +7,7 @@ export type Props = JSX.IntrinsicElements["button"]; const DropdownListItem: Component = (props) => { const state = useDropdownList(); const value = state.namespace.create(); - const { attrs, tabIndex, isSelected } = state.item(value, { + const { attrs, tabIndex } = state.item(value, { onPointerMove: state.handleItemPointerMove, }); @@ -19,10 +19,7 @@ const DropdownListItem: Component = (props) => { ); diff --git a/src/renderer/src/components/tabs/TabsTrigger.tsx b/src/renderer/src/components/tabs/TabsTrigger.tsx index eb906d3c..77fbd50c 100644 --- a/src/renderer/src/components/tabs/TabsTrigger.tsx +++ b/src/renderer/src/components/tabs/TabsTrigger.tsx @@ -16,7 +16,7 @@ const TabsTrigger: Component = (_props) => {