@kwiz/fluentui
Version:
KWIZ common controls for FluentUI
150 lines (142 loc) • 6 kB
text/typescript
import { isDebug, isFunction } from "@kwiz/common";
import { MutableRefObject, useEffect, useRef, useState } from "react";
import { KnownClassNames } from "../styles/styles";
import { useEffectOnlyOnMount } from "./hooks";
export function useTrackFocus<elmType extends HTMLElement>(props: { onFocus: () => void, onLoseFocus: () => void, ref?: MutableRefObject<elmType> }) {
const wrapperDiv: MutableRefObject<elmType> = props.ref || useRef<HTMLDivElement>(null) as any;
useEffect(() => {
function focusIn(e: FocusEvent) {
let elm = e.target as HTMLElement;//document.activeElement;
if (wrapperDiv.current) {
while (elm && elm !== wrapperDiv.current) {
elm = elm.parentElement;
}
} else elm = null;
if (wrapperDiv.current && elm === wrapperDiv.current) props.onFocus();
else props.onLoseFocus();
}
if (wrapperDiv.current) {
if (wrapperDiv.current) wrapperDiv.current.tabIndex = 1;
window.addEventListener("focusin", focusIn);
// Remove event listener on cleanup
return () => window.removeEventListener("focusin", focusIn);
}
}, [wrapperDiv.current]);
return wrapperDiv;
}
export function useWindowSize() {
// Initialize state with undefined width/height so server and client renders match
// Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
const [windowSize, setWindowSize] = useState<{
width: number,
height: number
}>({
width: undefined,
height: undefined
});
useEffect(() => {
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
});
}
// Add event listener
window.addEventListener("resize", handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener("resize", handleResize);
}, useEffectOnlyOnMount);
return windowSize;
}
export function useElementSize(elm: HTMLElement) {
// Initialize state with undefined width/height so server and client renders match
// Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
const [elmSize, setELmSize] = useState<{
width: number,
height: number
}>({
width: undefined,
height: undefined
});
useEffect(() => {
if (elm) {
// Handler to call on elm resize
function handleResize() {
// Set elm width/height to state
setELmSize({
width: (elm instanceof Window) ? elm.innerWidth : elm.clientWidth,
height: (elm instanceof Window) ? elm.innerHeight : elm.clientHeight,
});
}
// Add event listener
const observer = new ResizeObserver(handleResize);
observer.observe(elm);
// Call handler right away so state gets updated with initial elm size
handleResize();
// Remove event listener on cleanup
return () => observer.disconnect();
}
}, [elm]);
return elmSize;
}
export function useIsInPrint() {
// Initialize state with media query
const [printMode, setPrintMode] = useState<boolean>(window.matchMedia ? window.matchMedia('print').matches : false);
useEffect(() => {
if (printMode)
document.body.classList.add(KnownClassNames.print);
else
document.body.classList.remove(KnownClassNames.print);
}, [printMode]);
useEffect(() => {
const forcePrintOn = () => setPrintMode(true);
const forcePrintOff = () => setPrintMode(false);
function printDebugHelper(e: KeyboardEvent) {
if (e.ctrlKey && e.shiftKey && e.altKey) {
if (e.key.toLocaleLowerCase() === "q") {
forcePrintOff();
}
else {
console.warn('forced print mode - to exit refresh to ctrl+shift+alt+q');
forcePrintOn();
}
}
}
// Add event listener
window.addEventListener("beforeprint", forcePrintOn);
window.addEventListener("afterprint", forcePrintOff);
if (isDebug())
window.addEventListener("keydown", printDebugHelper);
// Remove event listener on cleanup
return () => {
window.removeEventListener("beforeprint", forcePrintOn);
window.removeEventListener("afterprint", forcePrintOff);
if (isDebug())
window.removeEventListener("keydown", printDebugHelper);
};
}, useEffectOnlyOnMount);
return printMode;
}
export function useKeyDown(options: {
//default use document
elm?: HTMLElement | Document;
onEnter?: (e: KeyboardEvent) => void;
onEscape?: (e: KeyboardEvent) => void;
onKeyDown?: (e: KeyboardEvent) => void;
}) {
let elm = options.elm || document;
useEffect(() => {
let handler = (e: KeyboardEvent) => {
if (e.key === "Enter" && isFunction(options.onEnter)) options.onEnter(e);
else if (e.key === "Escape" && isFunction(options.onEscape)) options.onEscape(e);
if (isFunction(options.onKeyDown))
options.onKeyDown(e);
};
elm.addEventListener("keydown", handler);
return () => elm.removeEventListener("keydown", handler);
}, [elm, options.onEnter, options.onEscape, options.onKeyDown]);
}