Page MenuHomec4science

devtools.js
No OneTemporary

File Metadata

Created
Mon, Jul 21, 16:47

devtools.js

import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
import React from 'react';
import { useQueryClient } from 'react-query';
import { matchSorter } from 'match-sorter';
import useLocalStorage from './useLocalStorage';
import { useIsMounted, useSafeState } from './utils';
import { Panel, QueryKeys, QueryKey, Button, Code, Input, Select, ActiveQueryPanel } from './styledComponents';
import { ThemeProvider, defaultTheme as theme } from './theme';
import { getQueryStatusLabel, getQueryStatusColor } from './utils';
import Explorer from './Explorer';
import Logo from './Logo';
import { noop } from '../core/utils';
var isServer = typeof window === 'undefined';
export function ReactQueryDevtools(_ref) {
var initialIsOpen = _ref.initialIsOpen,
_ref$panelProps = _ref.panelProps,
panelProps = _ref$panelProps === void 0 ? {} : _ref$panelProps,
_ref$closeButtonProps = _ref.closeButtonProps,
closeButtonProps = _ref$closeButtonProps === void 0 ? {} : _ref$closeButtonProps,
_ref$toggleButtonProp = _ref.toggleButtonProps,
toggleButtonProps = _ref$toggleButtonProp === void 0 ? {} : _ref$toggleButtonProp,
_ref$position = _ref.position,
position = _ref$position === void 0 ? 'bottom-left' : _ref$position,
_ref$containerElement = _ref.containerElement,
Container = _ref$containerElement === void 0 ? 'aside' : _ref$containerElement,
styleNonce = _ref.styleNonce;
var rootRef = React.useRef(null);
var panelRef = React.useRef(null);
var _useLocalStorage = useLocalStorage('reactQueryDevtoolsOpen', initialIsOpen),
isOpen = _useLocalStorage[0],
setIsOpen = _useLocalStorage[1];
var _useLocalStorage2 = useLocalStorage('reactQueryDevtoolsHeight', null),
devtoolsHeight = _useLocalStorage2[0],
setDevtoolsHeight = _useLocalStorage2[1];
var _useSafeState = useSafeState(false),
isResolvedOpen = _useSafeState[0],
setIsResolvedOpen = _useSafeState[1];
var _useSafeState2 = useSafeState(false),
isResizing = _useSafeState2[0],
setIsResizing = _useSafeState2[1];
var isMounted = useIsMounted();
var _handleDragStart = function handleDragStart(panelElement, startEvent) {
var _panelElement$getBoun;
if (startEvent.button !== 0) return; // Only allow left click for drag
setIsResizing(true);
var dragInfo = {
originalHeight: (_panelElement$getBoun = panelElement == null ? void 0 : panelElement.getBoundingClientRect().height) != null ? _panelElement$getBoun : 0,
pageY: startEvent.pageY
};
var run = function run(moveEvent) {
var delta = dragInfo.pageY - moveEvent.pageY;
var newHeight = (dragInfo == null ? void 0 : dragInfo.originalHeight) + delta;
setDevtoolsHeight(newHeight);
if (newHeight < 70) {
setIsOpen(false);
} else {
setIsOpen(true);
}
};
var unsub = function unsub() {
setIsResizing(false);
document.removeEventListener('mousemove', run);
document.removeEventListener('mouseUp', unsub);
};
document.addEventListener('mousemove', run);
document.addEventListener('mouseup', unsub);
};
React.useEffect(function () {
setIsResolvedOpen(isOpen != null ? isOpen : false);
}, [isOpen, isResolvedOpen, setIsResolvedOpen]); // Toggle panel visibility before/after transition (depending on direction).
// Prevents focusing in a closed panel.
React.useEffect(function () {
var ref = panelRef.current;
if (ref) {
var handlePanelTransitionStart = function handlePanelTransitionStart() {
if (ref && isResolvedOpen) {
ref.style.visibility = 'visible';
}
};
var handlePanelTransitionEnd = function handlePanelTransitionEnd() {
if (ref && !isResolvedOpen) {
ref.style.visibility = 'hidden';
}
};
ref.addEventListener('transitionstart', handlePanelTransitionStart);
ref.addEventListener('transitionend', handlePanelTransitionEnd);
return function () {
ref.removeEventListener('transitionstart', handlePanelTransitionStart);
ref.removeEventListener('transitionend', handlePanelTransitionEnd);
};
}
}, [isResolvedOpen]);
React[isServer ? 'useEffect' : 'useLayoutEffect'](function () {
if (isResolvedOpen) {
var _rootRef$current, _rootRef$current$pare;
var previousValue = (_rootRef$current = rootRef.current) == null ? void 0 : (_rootRef$current$pare = _rootRef$current.parentElement) == null ? void 0 : _rootRef$current$pare.style.paddingBottom;
var run = function run() {
var _panelRef$current, _rootRef$current2;
var containerHeight = (_panelRef$current = panelRef.current) == null ? void 0 : _panelRef$current.getBoundingClientRect().height;
if ((_rootRef$current2 = rootRef.current) == null ? void 0 : _rootRef$current2.parentElement) {
rootRef.current.parentElement.style.paddingBottom = containerHeight + "px";
}
};
run();
if (typeof window !== 'undefined') {
window.addEventListener('resize', run);
return function () {
var _rootRef$current3;
window.removeEventListener('resize', run);
if (((_rootRef$current3 = rootRef.current) == null ? void 0 : _rootRef$current3.parentElement) && typeof previousValue === 'string') {
rootRef.current.parentElement.style.paddingBottom = previousValue;
}
};
}
}
}, [isResolvedOpen]);
var _panelProps$style = panelProps.style,
panelStyle = _panelProps$style === void 0 ? {} : _panelProps$style,
otherPanelProps = _objectWithoutPropertiesLoose(panelProps, ["style"]);
var _closeButtonProps$sty = closeButtonProps.style,
closeButtonStyle = _closeButtonProps$sty === void 0 ? {} : _closeButtonProps$sty,
onCloseClick = closeButtonProps.onClick,
otherCloseButtonProps = _objectWithoutPropertiesLoose(closeButtonProps, ["style", "onClick"]);
var _toggleButtonProps$st = toggleButtonProps.style,
toggleButtonStyle = _toggleButtonProps$st === void 0 ? {} : _toggleButtonProps$st,
onToggleClick = toggleButtonProps.onClick,
otherToggleButtonProps = _objectWithoutPropertiesLoose(toggleButtonProps, ["style", "onClick"]); // Do not render on the server
if (!isMounted()) return null;
return /*#__PURE__*/React.createElement(Container, {
ref: rootRef,
className: "ReactQueryDevtools",
"aria-label": "React Query Devtools"
}, /*#__PURE__*/React.createElement(ThemeProvider, {
theme: theme
}, /*#__PURE__*/React.createElement(ReactQueryDevtoolsPanel, _extends({
ref: panelRef,
styleNonce: styleNonce
}, otherPanelProps, {
style: _extends({
position: 'fixed',
bottom: '0',
right: '0',
zIndex: 99999,
width: '100%',
height: devtoolsHeight != null ? devtoolsHeight : 500,
maxHeight: '90%',
boxShadow: '0 0 20px rgba(0,0,0,.3)',
borderTop: "1px solid " + theme.gray,
transformOrigin: 'top',
// visibility will be toggled after transitions, but set initial state here
visibility: isOpen ? 'visible' : 'hidden'
}, panelStyle, isResizing ? {
transition: "none"
} : {
transition: "all .2s ease"
}, isResolvedOpen ? {
opacity: 1,
pointerEvents: 'all',
transform: "translateY(0) scale(1)"
} : {
opacity: 0,
pointerEvents: 'none',
transform: "translateY(15px) scale(1.02)"
}),
isOpen: isResolvedOpen,
setIsOpen: setIsOpen,
handleDragStart: function handleDragStart(e) {
return _handleDragStart(panelRef.current, e);
}
})), isResolvedOpen ? /*#__PURE__*/React.createElement(Button, _extends({
type: "button",
"aria-controls": "ReactQueryDevtoolsPanel",
"aria-haspopup": "true",
"aria-expanded": "true"
}, otherCloseButtonProps, {
onClick: function onClick(e) {
setIsOpen(false);
onCloseClick && onCloseClick(e);
},
style: _extends({
position: 'fixed',
zIndex: 99999,
margin: '.5em',
bottom: 0
}, position === 'top-right' ? {
right: '0'
} : position === 'top-left' ? {
left: '0'
} : position === 'bottom-right' ? {
right: '0'
} : {
left: '0'
}, closeButtonStyle)
}), "Close") : null), !isResolvedOpen ? /*#__PURE__*/React.createElement("button", _extends({
type: "button"
}, otherToggleButtonProps, {
"aria-label": "Open React Query Devtools",
"aria-controls": "ReactQueryDevtoolsPanel",
"aria-haspopup": "true",
"aria-expanded": "false",
onClick: function onClick(e) {
setIsOpen(true);
onToggleClick && onToggleClick(e);
},
style: _extends({
background: 'none',
border: 0,
padding: 0,
position: 'fixed',
zIndex: 99999,
display: 'inline-flex',
fontSize: '1.5em',
margin: '.5em',
cursor: 'pointer',
width: 'fit-content'
}, position === 'top-right' ? {
top: '0',
right: '0'
} : position === 'top-left' ? {
top: '0',
left: '0'
} : position === 'bottom-right' ? {
bottom: '0',
right: '0'
} : {
bottom: '0',
left: '0'
}, toggleButtonStyle)
}), /*#__PURE__*/React.createElement(Logo, {
"aria-hidden": true
})) : null);
}
var getStatusRank = function getStatusRank(q) {
return q.state.isFetching ? 0 : !q.getObserversCount() ? 3 : q.isStale() ? 2 : 1;
};
var sortFns = {
'Status > Last Updated': function StatusLastUpdated(a, b) {
var _sortFns$LastUpdated;
return getStatusRank(a) === getStatusRank(b) ? (_sortFns$LastUpdated = sortFns['Last Updated']) == null ? void 0 : _sortFns$LastUpdated.call(sortFns, a, b) : getStatusRank(a) > getStatusRank(b) ? 1 : -1;
},
'Query Hash': function QueryHash(a, b) {
return a.queryHash > b.queryHash ? 1 : -1;
},
'Last Updated': function LastUpdated(a, b) {
return a.state.dataUpdatedAt < b.state.dataUpdatedAt ? 1 : -1;
}
};
export var ReactQueryDevtoolsPanel = /*#__PURE__*/React.forwardRef(function ReactQueryDevtoolsPanel(props, ref) {
var _activeQuery$state;
var _props$isOpen = props.isOpen,
isOpen = _props$isOpen === void 0 ? true : _props$isOpen,
styleNonce = props.styleNonce,
setIsOpen = props.setIsOpen,
handleDragStart = props.handleDragStart,
panelProps = _objectWithoutPropertiesLoose(props, ["isOpen", "styleNonce", "setIsOpen", "handleDragStart"]);
var queryClient = useQueryClient();
var queryCache = queryClient.getQueryCache();
var _useLocalStorage3 = useLocalStorage('reactQueryDevtoolsSortFn', Object.keys(sortFns)[0]),
sort = _useLocalStorage3[0],
setSort = _useLocalStorage3[1];
var _useLocalStorage4 = useLocalStorage('reactQueryDevtoolsFilter', ''),
filter = _useLocalStorage4[0],
setFilter = _useLocalStorage4[1];
var _useLocalStorage5 = useLocalStorage('reactQueryDevtoolsSortDesc', false),
sortDesc = _useLocalStorage5[0],
setSortDesc = _useLocalStorage5[1];
var sortFn = React.useMemo(function () {
return sortFns[sort];
}, [sort]);
React[isServer ? 'useEffect' : 'useLayoutEffect'](function () {
if (!sortFn) {
setSort(Object.keys(sortFns)[0]);
}
}, [setSort, sortFn]);
var _useSafeState3 = useSafeState(Object.values(queryCache.findAll())),
unsortedQueries = _useSafeState3[0],
setUnsortedQueries = _useSafeState3[1];
var _useLocalStorage6 = useLocalStorage('reactQueryDevtoolsActiveQueryHash', ''),
activeQueryHash = _useLocalStorage6[0],
setActiveQueryHash = _useLocalStorage6[1];
var queries = React.useMemo(function () {
var sorted = [].concat(unsortedQueries).sort(sortFn);
if (sortDesc) {
sorted.reverse();
}
if (!filter) {
return sorted;
}
return matchSorter(sorted, filter, {
keys: ['queryHash']
}).filter(function (d) {
return d.queryHash;
});
}, [sortDesc, sortFn, unsortedQueries, filter]);
var activeQuery = React.useMemo(function () {
return queries.find(function (query) {
return query.queryHash === activeQueryHash;
});
}, [activeQueryHash, queries]);
var hasFresh = queries.filter(function (q) {
return getQueryStatusLabel(q) === 'fresh';
}).length;
var hasFetching = queries.filter(function (q) {
return getQueryStatusLabel(q) === 'fetching';
}).length;
var hasStale = queries.filter(function (q) {
return getQueryStatusLabel(q) === 'stale';
}).length;
var hasInactive = queries.filter(function (q) {
return getQueryStatusLabel(q) === 'inactive';
}).length;
React.useEffect(function () {
if (isOpen) {
var unsubscribe = queryCache.subscribe(function () {
setUnsortedQueries(Object.values(queryCache.getAll()));
}); // re-subscribing after the panel is closed and re-opened won't trigger the callback,
// So we'll manually populate our state
setUnsortedQueries(Object.values(queryCache.getAll()));
return unsubscribe;
}
return undefined;
}, [isOpen, sort, sortFn, sortDesc, setUnsortedQueries, queryCache]);
var handleRefetch = function handleRefetch() {
var promise = activeQuery == null ? void 0 : activeQuery.fetch();
promise == null ? void 0 : promise.catch(noop);
};
return /*#__PURE__*/React.createElement(ThemeProvider, {
theme: theme
}, /*#__PURE__*/React.createElement(Panel, _extends({
ref: ref,
className: "ReactQueryDevtoolsPanel",
"aria-label": "React Query Devtools Panel",
id: "ReactQueryDevtoolsPanel"
}, panelProps), /*#__PURE__*/React.createElement("style", {
nonce: styleNonce,
dangerouslySetInnerHTML: {
__html: "\n .ReactQueryDevtoolsPanel * {\n scrollbar-color: " + theme.backgroundAlt + " " + theme.gray + ";\n }\n\n .ReactQueryDevtoolsPanel *::-webkit-scrollbar, .ReactQueryDevtoolsPanel scrollbar {\n width: 1em;\n height: 1em;\n }\n\n .ReactQueryDevtoolsPanel *::-webkit-scrollbar-track, .ReactQueryDevtoolsPanel scrollbar-track {\n background: " + theme.backgroundAlt + ";\n }\n\n .ReactQueryDevtoolsPanel *::-webkit-scrollbar-thumb, .ReactQueryDevtoolsPanel scrollbar-thumb {\n background: " + theme.gray + ";\n border-radius: .5em;\n border: 3px solid " + theme.backgroundAlt + ";\n }\n "
}
}), /*#__PURE__*/React.createElement("div", {
style: {
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '4px',
marginBottom: '-4px',
cursor: 'row-resize',
zIndex: 100000
},
onMouseDown: handleDragStart
}), /*#__PURE__*/React.createElement("div", {
style: {
flex: '1 1 500px',
minHeight: '40%',
maxHeight: '100%',
overflow: 'auto',
borderRight: "1px solid " + theme.grayAlt,
display: isOpen ? 'flex' : 'none',
flexDirection: 'column'
}
}, /*#__PURE__*/React.createElement("div", {
style: {
padding: '.5em',
background: theme.backgroundAlt,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}
}, /*#__PURE__*/React.createElement("button", {
type: "button",
"aria-label": "Close React Query Devtools",
"aria-controls": "ReactQueryDevtoolsPanel",
"aria-haspopup": "true",
"aria-expanded": "true",
onClick: function onClick() {
return setIsOpen(false);
},
style: {
display: 'inline-flex',
background: 'none',
border: 0,
padding: 0,
marginRight: '.5em',
cursor: 'pointer'
}
}, /*#__PURE__*/React.createElement(Logo, {
"aria-hidden": true
})), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
flexDirection: 'column'
}
}, /*#__PURE__*/React.createElement(QueryKeys, {
style: {
marginBottom: '.5em'
}
}, /*#__PURE__*/React.createElement(QueryKey, {
style: {
background: theme.success,
opacity: hasFresh ? 1 : 0.3
}
}, "fresh ", /*#__PURE__*/React.createElement(Code, null, "(", hasFresh, ")")), ' ', /*#__PURE__*/React.createElement(QueryKey, {
style: {
background: theme.active,
opacity: hasFetching ? 1 : 0.3
}
}, "fetching ", /*#__PURE__*/React.createElement(Code, null, "(", hasFetching, ")")), ' ', /*#__PURE__*/React.createElement(QueryKey, {
style: {
background: theme.warning,
color: 'black',
textShadow: '0',
opacity: hasStale ? 1 : 0.3
}
}, "stale ", /*#__PURE__*/React.createElement(Code, null, "(", hasStale, ")")), ' ', /*#__PURE__*/React.createElement(QueryKey, {
style: {
background: theme.gray,
opacity: hasInactive ? 1 : 0.3
}
}, "inactive ", /*#__PURE__*/React.createElement(Code, null, "(", hasInactive, ")"))), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
alignItems: 'center'
}
}, /*#__PURE__*/React.createElement(Input, {
placeholder: "Filter",
"aria-label": "Filter by queryhash",
value: filter != null ? filter : '',
onChange: function onChange(e) {
return setFilter(e.target.value);
},
onKeyDown: function onKeyDown(e) {
if (e.key === 'Escape') setFilter('');
},
style: {
flex: '1',
marginRight: '.5em',
width: '100%'
}
}), !filter ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Select, {
"aria-label": "Sort queries",
value: sort,
onChange: function onChange(e) {
return setSort(e.target.value);
},
style: {
flex: '1',
minWidth: 75,
marginRight: '.5em'
}
}, Object.keys(sortFns).map(function (key) {
return /*#__PURE__*/React.createElement("option", {
key: key,
value: key
}, "Sort by ", key);
})), /*#__PURE__*/React.createElement(Button, {
type: "button",
onClick: function onClick() {
return setSortDesc(function (old) {
return !old;
});
},
style: {
padding: '.3em .4em'
}
}, sortDesc ? '⬇ Desc' : '⬆ Asc')) : null))), /*#__PURE__*/React.createElement("div", {
style: {
overflowY: 'auto',
flex: '1'
}
}, queries.map(function (query, i) {
var isDisabled = query.getObserversCount() > 0 && !query.isActive();
return /*#__PURE__*/React.createElement("div", {
key: query.queryHash || i,
role: "button",
"aria-label": "Open query details for " + query.queryHash,
onClick: function onClick() {
return setActiveQueryHash(activeQueryHash === query.queryHash ? '' : query.queryHash);
},
style: {
display: 'flex',
borderBottom: "solid 1px " + theme.grayAlt,
cursor: 'pointer',
background: query === activeQuery ? 'rgba(255,255,255,.1)' : undefined
}
}, /*#__PURE__*/React.createElement("div", {
style: {
flex: '0 0 auto',
width: '2em',
height: '2em',
background: getQueryStatusColor(query, theme),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: 'bold',
textShadow: getQueryStatusLabel(query) === 'stale' ? '0' : '0 0 10px black',
color: getQueryStatusLabel(query) === 'stale' ? 'black' : 'white'
}
}, query.getObserversCount()), isDisabled ? /*#__PURE__*/React.createElement("div", {
style: {
flex: '0 0 auto',
height: '2em',
background: theme.gray,
display: 'flex',
alignItems: 'center',
fontWeight: 'bold',
padding: '0 0.5em'
}
}, "disabled") : null, /*#__PURE__*/React.createElement(Code, {
style: {
padding: '.5em'
}
}, "" + query.queryHash));
}))), activeQuery ? /*#__PURE__*/React.createElement(ActiveQueryPanel, null, /*#__PURE__*/React.createElement("div", {
style: {
padding: '.5em',
background: theme.backgroundAlt,
position: 'sticky',
top: 0,
zIndex: 1
}
}, "Query Details"), /*#__PURE__*/React.createElement("div", {
style: {
padding: '.5em'
}
}, /*#__PURE__*/React.createElement("div", {
style: {
marginBottom: '.5em',
display: 'flex',
alignItems: 'start',
justifyContent: 'space-between'
}
}, /*#__PURE__*/React.createElement(Code, {
style: {
lineHeight: '1.8em'
}
}, /*#__PURE__*/React.createElement("pre", {
style: {
margin: 0,
padding: 0,
overflow: 'auto'
}
}, JSON.stringify(activeQuery.queryKey, null, 2))), /*#__PURE__*/React.createElement("span", {
style: {
padding: '0.3em .6em',
borderRadius: '0.4em',
fontWeight: 'bold',
textShadow: '0 2px 10px black',
background: getQueryStatusColor(activeQuery, theme),
flexShrink: 0
}
}, getQueryStatusLabel(activeQuery))), /*#__PURE__*/React.createElement("div", {
style: {
marginBottom: '.5em',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}
}, "Observers: ", /*#__PURE__*/React.createElement(Code, null, activeQuery.getObserversCount())), /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}
}, "Last Updated:", ' ', /*#__PURE__*/React.createElement(Code, null, new Date(activeQuery.state.dataUpdatedAt).toLocaleTimeString()))), /*#__PURE__*/React.createElement("div", {
style: {
background: theme.backgroundAlt,
padding: '.5em',
position: 'sticky',
top: 0,
zIndex: 1
}
}, "Actions"), /*#__PURE__*/React.createElement("div", {
style: {
padding: '0.5em'
}
}, /*#__PURE__*/React.createElement(Button, {
type: "button",
onClick: handleRefetch,
disabled: activeQuery.state.isFetching,
style: {
background: theme.active
}
}, "Refetch"), ' ', /*#__PURE__*/React.createElement(Button, {
type: "button",
onClick: function onClick() {
return queryClient.invalidateQueries(activeQuery);
},
style: {
background: theme.warning,
color: theme.inputTextColor
}
}, "Invalidate"), ' ', /*#__PURE__*/React.createElement(Button, {
type: "button",
onClick: function onClick() {
return queryClient.resetQueries(activeQuery);
},
style: {
background: theme.gray
}
}, "Reset"), ' ', /*#__PURE__*/React.createElement(Button, {
type: "button",
onClick: function onClick() {
return queryClient.removeQueries(activeQuery);
},
style: {
background: theme.danger
}
}, "Remove")), /*#__PURE__*/React.createElement("div", {
style: {
background: theme.backgroundAlt,
padding: '.5em',
position: 'sticky',
top: 0,
zIndex: 1
}
}, "Data Explorer"), /*#__PURE__*/React.createElement("div", {
style: {
padding: '.5em'
}
}, /*#__PURE__*/React.createElement(Explorer, {
label: "Data",
value: activeQuery == null ? void 0 : (_activeQuery$state = activeQuery.state) == null ? void 0 : _activeQuery$state.data,
defaultExpanded: {}
})), /*#__PURE__*/React.createElement("div", {
style: {
background: theme.backgroundAlt,
padding: '.5em',
position: 'sticky',
top: 0,
zIndex: 1
}
}, "Query Explorer"), /*#__PURE__*/React.createElement("div", {
style: {
padding: '.5em'
}
}, /*#__PURE__*/React.createElement(Explorer, {
label: "Query",
value: activeQuery,
defaultExpanded: {
queryKey: true
}
}))) : null));
});

Event Timeline