* feat: add mobile navigation with responsive layout and theme controls * feat: prevent workspace owners from removing themselves and update workspace dropdown styling * feat: add useClickOutside hook and event listener utilities for improved UI interactions * chore: improve mobile UI styling * refactor: improve UI layout and buttons in boards/members views feat: adjust /members layout * fix: standardize layout containers and improve mobile navigation styling * feat: update card view layout * feat: add responsive layout to board view * feat: readjust boards page layout * feat: enhance table view on mobile for members page * feat: extend mobile support with sliding side panels * fix: prevent two panels from being open at the same time * feat: close panels when clicking outside * fix: adjust font-sizing for import source --------- Co-authored-by: Henry <henry_ball@hotmail.co.uk>
60 lines
1.9 KiB
TypeScript
60 lines
1.9 KiB
TypeScript
import type { RefObject } from "react";
|
|
|
|
import { useEventListener } from "./useEventListener";
|
|
|
|
/** Supported event types. */
|
|
type EventType =
|
|
| "mousedown"
|
|
| "mouseup"
|
|
| "touchstart"
|
|
| "touchend"
|
|
| "focusin"
|
|
| "focusout";
|
|
|
|
/**
|
|
* Custom hook that handles clicks outside a specified element.
|
|
* @template T - The type of the element's reference.
|
|
* @param {RefObject<T> | RefObject<T>[]} ref - The React ref object(s) representing the element(s) to watch for outside clicks.
|
|
* @param {(event: MouseEvent | TouchEvent | FocusEvent) => void} handler - The callback function to be executed when a click outside the element occurs.
|
|
* @param {EventType} [eventType] - The mouse event type to listen for (optional, default is 'mousedown').
|
|
* @param {?AddEventListenerOptions} [eventListenerOptions] - The options object to be passed to the `addEventListener` method (optional).
|
|
* @returns {void}
|
|
* @example
|
|
* ```tsx
|
|
* const containerRef = useRef(null);
|
|
* useClickOutside([containerRef], () => {
|
|
* // Handle clicks outside the container.
|
|
* });
|
|
* ```
|
|
*/
|
|
export function useClickOutside<T extends HTMLElement = HTMLElement>(
|
|
ref: RefObject<T> | RefObject<T>[],
|
|
handler: (event: MouseEvent | TouchEvent | FocusEvent) => void,
|
|
eventType: EventType = "mousedown",
|
|
eventListenerOptions: AddEventListenerOptions = {},
|
|
): void {
|
|
useEventListener(
|
|
eventType,
|
|
(event) => {
|
|
const target = event.target as Node;
|
|
|
|
// Do nothing if the target is not connected element with document
|
|
if (!target.isConnected) {
|
|
return;
|
|
}
|
|
|
|
const isOutside = Array.isArray(ref)
|
|
? ref
|
|
.filter((r) => Boolean(r.current))
|
|
.every((r) => r.current && !r.current.contains(target))
|
|
: ref.current && !ref.current.contains(target);
|
|
|
|
if (isOutside) {
|
|
handler(event);
|
|
}
|
|
},
|
|
undefined,
|
|
eventListenerOptions,
|
|
);
|
|
}
|