codekingpro/portable-devtools
114k
1/////////////////////////////////////////////////////////////2//3// pgAdmin 4 - PostgreSQL Tools4//5// Copyright (C) 2013 - 2024, The pgAdmin Development Team6// This software is released under the PostgreSQL Licence7//8//////////////////////////////////////////////////////////////9import {useRef, useEffect, useState, useCallback, useLayoutEffect} from 'react';10import moment from 'moment';11import { isMac } from './keyboard_shortcuts';12 13/* React hook for setInterval */14export function useInterval(callback, delay) {15 const savedCallback = useRef();16 savedCallback.current = callback;17 18 useEffect(() => {19 function tick() {20 savedCallback.current();21 }22 23 if(delay > -1) {24 tick();25 let id = setInterval(tick, delay);26 return () => clearInterval(id);27 }28 }, [delay]);29}30 31export function useDelayedCaller(callback) {32 let timer;33 useEffect(() => {34 return () => clearTimeout(timer);35 }, []);36 37 return (delay)=>{38 timer = setTimeout(() => {39 callback();40 }, delay);41 };42}43 44export function usePrevious(value) {45 const ref = useRef();46 useEffect(() => {47 ref.current = value;48 });49 return ref.current;50}51 52export function useDelayDebounce(callback, args, delay) {53 useEffect(() => {54 const delayDebounceFn = setTimeout(() => {55 if (args) {56 callback(args);57 }58 }, delay);59 return () => clearTimeout(delayDebounceFn);60 }, [args]);61}62 63export function useOnScreen(ref) {64 const [intersecting, setIntersecting] = useState(false);65 const observer = new IntersectionObserver(66 ([entry]) => {67 setIntersecting(entry.intersecting);68 }69 );70 useEffect(() => {71 if(ref?.current) {72 observer.observe(ref.current);73 }74 // Remove the observer as soon as the component is unmounted75 return () => { observer.disconnect(); };76 }, []);77 78 return intersecting;79}80 81export function useIsMounted() {82 const ref = useRef(true);83 useEffect(() => {84 return () => {85 ref.current = false;86 };87 }, []);88 return useCallback(() => ref.current, []);89}90 91export function useStopwatch() {92 const prevTime = useRef(new Date());93 const [totalMsec, setTotalMsec] = useState(0);94 const [isRunning, setIsRunning] = useState(false);95 96 useInterval(() => {97 setTotalMsec(moment(new Date()).diff(prevTime.current));98 }, isRunning ? 100 : -1);99 100 function start(startTime) {101 prevTime.current = startTime || new Date();102 setIsRunning(true);103 }104 105 const pause = (endTime)=>{106 setIsRunning(false);107 setTotalMsec(moment(endTime || new Date()).diff(prevTime.current));108 };109 110 function reset() {111 setTotalMsec(0);112 }113 114 let msec = totalMsec;115 /* Extract seconds from millisecs */116 let seconds = parseInt(msec/1000);117 msec = msec%1000;118 119 /* Extract mins from seconds */120 let minutes = parseInt(seconds/60);121 seconds = seconds%60;122 123 /* Extract hrs from mins */124 let hours = parseInt(minutes/60);125 minutes = minutes%60;126 127 return {128 hours: hours,129 minutes: minutes,130 seconds: seconds,131 msec: msec,132 start, pause, reset, isRunning,133 };134}135 136/*137 shortcuts = [138 {139 // From the preferences140 shortcut: {141 'control': true,142 'shift': false,143 'alt': true,144 'key': {145 'key_code': 73,146 'char': 'I',147 },148 },149 options: {150 callback: ()=>{}151 enabled?: boolean optional152 }153 }154 ]155*/156export function useKeyboardShortcuts(shortcuts, eleRef) {157 const shortcutsRef = useRef(shortcuts);158 159 const matchFound = (shortcut, e)=>{160 if(!shortcut) return false;161 let keyCode = e.which || e.keyCode;162 const ctrlKey = (isMac() && shortcut.ctrl_is_meta) ? e.metaKey : e.ctrlKey;163 164 return Boolean(shortcut.alt) == e.altKey &&165 Boolean(shortcut.shift) == e.shiftKey &&166 Boolean(shortcut.control) == ctrlKey &&167 shortcut.key.key_code == keyCode;168 };169 useEffect(()=>{170 let ele = eleRef.current ?? document;171 const keydownCallback = (e)=>{172 Promise.resolve(0).then(()=>{173 let allListeners = _.filter(shortcutsRef.current, (s)=>matchFound(s.shortcut, e));174 for(const {options} of allListeners) {175 Promise.resolve(0).then(()=>{176 if(options.callback && (options.enabled ?? true)) {177 e.preventDefault();178 e.stopPropagation();179 options.callback(e);180 }181 });182 }183 });184 };185 ele.addEventListener('keydown', keydownCallback);186 return ()=>{187 ele.removeEventListener('keydown', keydownCallback);188 };189 }, [eleRef.current]);190 191 useEffect(()=>{192 shortcutsRef.current = shortcuts;193 }, [shortcuts]);194}195 196export function useWindowSize() {197 const [size, setSize] = useState([999999, 999999]);198 useLayoutEffect(() => {199 function updateSize() {200 setSize([window.innerWidth, window.innerHeight]);201 }202 window.addEventListener('resize', updateSize);203 updateSize();204 return () => window.removeEventListener('resize', updateSize);205 }, []);206 return size;207}208 