Skip to content

Commit

Permalink
Pass targetRef instead of target, stop Escape clearing filters
Browse files Browse the repository at this point in the history
  • Loading branch information
apata committed Jan 29, 2025
1 parent f6985a6 commit ce86bbe
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 88 deletions.
4 changes: 2 additions & 2 deletions assets/js/dashboard/components/search-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ export const SearchInput = ({
type="keyup"
handler={blurSearchBox}
shouldIgnoreWhen={[isModifierPressed, () => !isFocused]}
target={searchBoxRef.current}
targetRef={searchBoxRef}
/>
<Keybind
keyboardKey="/"
type="keyup"
handler={focusSearchBox}
shouldIgnoreWhen={[isModifierPressed, () => isFocused]}
target={document}
targetRef="document"
/>
<input
onBlur={() => setIsFocused(false)}
Expand Down
2 changes: 1 addition & 1 deletion assets/js/dashboard/datepicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ export default function QueryPeriodPicker({
type="keydown"
handler={onClick || closeMenu}
shouldIgnoreWhen={[isModifierPressed, isTyping]}
target={document}
targetRef="document"
/>
) : (
<NavigateKeybind
Expand Down
43 changes: 34 additions & 9 deletions assets/js/dashboard/keybinding.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* @format */
import React, { ReactNode, useCallback, useEffect } from 'react'
import React, { ReactNode, RefObject, useCallback, useEffect } from 'react'
import {
AppNavigationTarget,
useAppNavigate
Expand Down Expand Up @@ -65,15 +65,15 @@ type KeybindOptions = {
type: KeyboardEventType
handler: (event: KeyboardEvent) => void
shouldIgnoreWhen?: Array<(event: KeyboardEvent) => boolean>
target?: Document | HTMLElement | null
targetRef?: 'document' | RefObject<HTMLElement> | null
}

function useKeybind({
keyboardKey,
type,
handler,
shouldIgnoreWhen = [],
target
targetRef
}: KeybindOptions) {
const wrappedHandler = useCallback(
(event: KeyboardEvent) => {
Expand All @@ -85,22 +85,23 @@ function useKeybind({
) as EventListener

useEffect(() => {
const element = targetRef === 'document' ? document : targetRef?.current
const registerKeybind = (t: HTMLElement | Document) =>
t.addEventListener(type, wrappedHandler)

const deregisterKeybind = (t: HTMLElement | Document) =>
t.removeEventListener(type, wrappedHandler)

if (target) {
registerKeybind(target)
if (element) {
registerKeybind(element)
}

return () => {
if (target) {
deregisterKeybind(target)
if (element) {
deregisterKeybind(element)
}
}
}, [target, type, wrappedHandler])
}, [targetRef, type, wrappedHandler])
}

export function Keybind(opts: KeybindOptions) {
Expand Down Expand Up @@ -129,7 +130,7 @@ export function NavigateKeybind({
type={type}
handler={handler}
shouldIgnoreWhen={[isModifierPressed, isTyping]}
target={document}
targetRef="document"
/>
)
}
Expand All @@ -141,3 +142,27 @@ export function KeybindHint({ children }: { children: ReactNode }) {
</kbd>
)
}

export function BlurMenuButtonOnEscape({
targetRef: targetRef
}: {
targetRef: RefObject<HTMLElement>
}) {
return (
<Keybind
keyboardKey="Escape"
type="keyup"
handler={(event) => {
const t = event.target as HTMLElement | null
if (typeof t?.blur === 'function') {
if (t === targetRef.current) {
t.blur()
event.stopPropagation()
}
}
}}
targetRef={targetRef}
shouldIgnoreWhen={[isModifierPressed, isTyping]}
/>
)
}
60 changes: 4 additions & 56 deletions assets/js/dashboard/nav-menu/filter-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { Popover, Transition } from '@headlessui/react'
import { popover } from '../components/popover'
import classNames from 'classnames'
import { AppNavigationLink } from '../navigation/use-app-navigate'
import { isModifierPressed, isTyping, Keybind } from '../keybinding'
import { BlurMenuButtonOnEscape } from '../keybinding'

export function getFilterListItems({
propsAvailable
Expand Down Expand Up @@ -49,33 +49,17 @@ export function getFilterListItems({
export const FilterMenu = () => {
const site = useSiteContext()
const columns = useMemo(() => getFilterListItems(site), [site])
const ref = useRef<HTMLDivElement>(null)
const ref = useRef<HTMLButtonElement>(null)
return (
<Popover
className="shrink-0 md:relative"
ref={ref}
data-no-clear-filters-on-escape={true}
>
{({ close }) => (
<>
<Keybind
keyboardKey="Escape"
type="keyup"
handler={(event) => {
// ;(event as unknown as Record<string, unknown>).hi = true
event.stopPropagation()
event.preventDefault()
// console.log(`Inner ${open}`, event)
// if (open) {
// handler()
// }
// // return true
}}
target={ref.current}
shouldIgnoreWhen={[isModifierPressed, isTyping]}
/>

<BlurMenuButtonOnEscape targetRef={ref} />
<Popover.Button
ref={ref}
className={classNames(
'flex items-center gap-1',
'h-9 px-3',
Expand All @@ -100,11 +84,6 @@ export const FilterMenu = () => {
'flex'
)}
>
<StopEscapePropagation
// open={open}
target={ref.current}
// handler={close}
/>
{columns.map((filterGroups, index) => (
<div key={index} className="flex flex-col w-1/2">
{filterGroups.map(({ title, modals }) => (
Expand Down Expand Up @@ -141,34 +120,3 @@ export const FilterMenu = () => {
</Popover>
)
}

const StopEscapePropagation = ({
// open,
// handler,
target
}: {
// open: boolean
// handler: () => void
target: HTMLDivElement | null
}) => {
// useEffect(() => {}, [])
// return null
return (
<Keybind
keyboardKey="Escape"
type="keyup"
handler={(event) => {
// ;(event as unknown as Record<string, unknown>).hi = true
event.stopPropagation()
event.preventDefault()
// console.log(`Inner ${open}`, event)
// if (open) {
// handler()
// }
// // return true
}}
target={target}
shouldIgnoreWhen={[isModifierPressed, isTyping]}
/>
)
}
46 changes: 27 additions & 19 deletions assets/js/dashboard/stats/graph/interval-picker.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { Fragment, useCallback, useEffect, useRef } from 'react';
import React, { Fragment, useRef } from 'react';
import { Menu, Transition } from '@headlessui/react';
import { ChevronDownIcon } from '@heroicons/react/20/solid';
import classNames from 'classnames';
import * as storage from '../../util/storage';
import { isKeyPressed, isModifierPressed, isTyping } from '../../keybinding';
import { BlurMenuButtonOnEscape, isModifierPressed, isTyping, Keybind } from '../../keybinding';
import { useQueryContext } from '../../query-context';
import { useSiteContext } from '../../site-context';
import { useMatch } from 'react-router-dom';
import { rootRoute } from '../../router';

const INTERVAL_LABELS = {
'minute': 'Minutes',
Expand Down Expand Up @@ -71,19 +73,6 @@ function storeInterval(period, domain, interval) {
storage.setItem(`interval__${period}__${domain}`, interval)
}

function useIKeybinding(ref) {
const handleKeyPress = useCallback((event) => {
if (isKeyPressed(event, { keyboardKey: "i", shouldIgnoreWhen: [isTyping, isModifierPressed] })) {
ref.current?.click()
}
}, [ref])

useEffect(() => {
document.addEventListener('keydown', handleKeyPress)
return () => document.removeEventListener('keydown', handleKeyPress)
}, [handleKeyPress])
}

export const getCurrentInterval = function(site, query) {
const options = validIntervals(site, query)

Expand All @@ -101,7 +90,7 @@ export function IntervalPicker({ onIntervalUpdate }) {
const menuElement = useRef(null)
const {query} = useQueryContext();
const site = useSiteContext();
useIKeybinding(menuElement)
const dashboardRouteMatch = useMatch(rootRoute.path)

if (query.period == 'realtime') return null

Expand Down Expand Up @@ -134,7 +123,22 @@ export function IntervalPicker({ onIntervalUpdate }) {
<Menu as="div" className="relative inline-block pl-2">
{({ open }) => (
<>
<Menu.Button ref={menuElement} className="text-sm inline-flex focus:outline-none text-gray-700 dark:text-gray-300 hover:text-indigo-600 dark:hover:text-indigo-600 items-center">
{!!dashboardRouteMatch && (
<Keybind
targetRef="document"
type="keydown"
keyboardKey="i"
handler={() => {
menuElement.current?.click()
}}
shouldIgnoreWhen={[isModifierPressed, isTyping]}
/>
)}
<BlurMenuButtonOnEscape targetRef={menuElement} />
<Menu.Button
ref={menuElement}
className="text-sm inline-flex focus:outline-none text-gray-700 dark:text-gray-300 hover:text-indigo-600 dark:hover:text-indigo-600 items-center"
>
{INTERVAL_LABELS[currentInterval]}
<ChevronDownIcon className="ml-1 h-4 w-4" aria-hidden="true" />
</Menu.Button>
Expand All @@ -147,8 +151,12 @@ export function IntervalPicker({ onIntervalUpdate }) {
enterTo="opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95">
<Menu.Items className="py-1 text-left origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5 focus:outline-none z-10" static>
leaveTo="opacity-0 scale-95"
>
<Menu.Items
className="py-1 text-left origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5 focus:outline-none z-10"
static
>
{options.map(renderDropdownItem)}
</Menu.Items>
</Transition>
Expand Down
2 changes: 1 addition & 1 deletion assets/js/dashboard/stats/modals/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class Modal extends React.Component {
render() {
return createPortal(
<>
<Keybind keyboardKey="Escape" type="keyup" handler={this.props.onClose} target={document} shouldIgnoreWhen={[isModifierPressed, isTyping]} />
<Keybind keyboardKey="Escape" type="keyup" handler={this.props.onClose} targetRef="document" shouldIgnoreWhen={[isModifierPressed, isTyping]} />
<div className="modal is-open" onClick={this.props.onClick}>
<div className="modal__overlay">
<button className="modal__close"></button>
Expand Down

0 comments on commit ce86bbe

Please sign in to comment.