@stoplight/elements-dev-portal 1.4.7 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -7,12 +7,10 @@ var React = require('react');
7
7
  var elementsCore = require('@stoplight/elements-core');
8
8
  var path = require('@stoplight/path');
9
9
  var types = require('@stoplight/types');
10
- var faSearch = require('@fortawesome/free-solid-svg-icons/faSearch');
11
- var lodash = require('lodash');
12
- var reactRouterDom = require('react-router-dom');
13
10
  var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons');
11
+ var flow = require('lodash/flow.js');
12
+ var reactRouterDom = require('react-router-dom');
14
13
  var reactQuery = require('react-query');
15
- var useDebounce = require('use-debounce');
16
14
 
17
15
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
18
16
 
@@ -36,6 +34,7 @@ function _interopNamespace(e) {
36
34
 
37
35
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
38
36
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
37
+ var flow__default = /*#__PURE__*/_interopDefaultLegacy(flow);
39
38
 
40
39
  const BranchSelector = ({ branchSlug, branches, onChange }) => {
41
40
  const currentBranch = branches.find(branch => (!branchSlug ? branch.is_default : branch.slug === branchSlug));
@@ -62,30 +61,28 @@ const BranchSelector = ({ branchSlug, branches, onChange }) => {
62
61
 
63
62
  const PlatformContext = React__namespace.createContext({ platformUrl: 'https://stoplight.io' });
64
63
  const PlatformProvider = ({ platformUrl = 'https://stoplight.io', platformAuthToken, children, }) => {
65
- return (React__namespace.createElement(PlatformContext.Provider, { value: { platformUrl, platformAuthToken } },
66
- React__namespace.createElement(mosaic.Provider, null, children)));
64
+ return React__namespace.createElement(PlatformContext.Provider, { value: { platformUrl, platformAuthToken } }, children);
67
65
  };
68
- const DevPortalProvider = elementsCore.withQueryClientProvider(PlatformProvider);
66
+ const DevPortalProvider = elementsCore.withPersistenceBoundary(elementsCore.withQueryClientProvider(elementsCore.withMosaicProvider(PlatformProvider)));
69
67
 
70
68
  const NodeContent = ({ node, Link, hideTryIt, hideTryItPanel, hideMocking, hideExport, tryItCredentialsPolicy, tryItCorsProxy, }) => {
71
- return (React__namespace.createElement(elementsCore.PersistenceContextProvider, null,
72
- React__namespace.createElement(NodeLinkContext.Provider, { value: [node, Link] },
73
- React__namespace.createElement(elementsCore.MarkdownComponentsProvider, { value: { a: LinkComponent } },
74
- React__namespace.createElement(elementsCore.MockingProvider, { mockUrl: node.links.mock_url, hideMocking: hideMocking },
75
- React__namespace.createElement(elementsCore.Docs, { nodeType: node.type, nodeData: node.data, nodeTitle: node.title, layoutOptions: {
76
- hideTryIt: hideTryIt,
77
- hideTryItPanel: hideTryItPanel,
78
- hideExport: hideExport || node.links.export_url === undefined,
79
- }, useNodeForRefResolving: true, tryItCorsProxy: tryItCorsProxy, exportProps: [types.NodeType.HttpService, types.NodeType.Model].includes(node.type)
80
- ? {
81
- original: {
82
- href: node.links.export_url,
83
- },
84
- bundled: {
85
- href: getBundledUrl(node.links.export_url),
86
- },
87
- }
88
- : undefined, tryItCredentialsPolicy: tryItCredentialsPolicy }))))));
69
+ return (React__namespace.createElement(NodeLinkContext.Provider, { value: [node, Link] },
70
+ React__namespace.createElement(elementsCore.MarkdownComponentsProvider, { value: { a: LinkComponent } },
71
+ React__namespace.createElement(elementsCore.MockingProvider, { mockUrl: node.links.mock_url, hideMocking: hideMocking },
72
+ React__namespace.createElement(elementsCore.Docs, { nodeType: node.type, nodeData: node.data, nodeTitle: node.title, layoutOptions: {
73
+ hideTryIt: hideTryIt,
74
+ hideTryItPanel: hideTryItPanel,
75
+ hideExport: hideExport || node.links.export_url === undefined,
76
+ }, useNodeForRefResolving: true, tryItCorsProxy: tryItCorsProxy, exportProps: [types.NodeType.HttpService, types.NodeType.Model].includes(node.type)
77
+ ? {
78
+ original: {
79
+ href: node.links.export_url,
80
+ },
81
+ bundled: {
82
+ href: getBundledUrl(node.links.export_url),
83
+ },
84
+ }
85
+ : undefined, tryItCredentialsPolicy: tryItCredentialsPolicy })))));
89
86
  };
90
87
  const NodeLinkContext = React__namespace.createContext(undefined);
91
88
  const externalRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
@@ -102,7 +99,7 @@ const LinkComponent = ({ children, href }) => {
102
99
  const decodedResolvedUriWithoutAnchor = decodeURIComponent(resolvedUriWithoutAnchor);
103
100
  const edge = node.outbound_edges.find(edge => edge.uri === decodedUrl || edge.uri === decodedResolvedUriWithoutAnchor);
104
101
  if (edge) {
105
- return (React__namespace.createElement(Link, { to: edge.slug, hash: hash }, children));
102
+ return React__namespace.createElement(Link, { to: `${edge.slug}${hash && `#${hash}`}` }, children);
106
103
  }
107
104
  }
108
105
  return React__namespace.createElement("a", { href: href }, children);
@@ -117,7 +114,7 @@ function getBundledUrl(url) {
117
114
  return bundledUrl.toString();
118
115
  }
119
116
 
120
- const SearchImpl = ({ search, searchResults, isOpen, onClose, onClick, onSearch }) => {
117
+ const SearchImpl = ({ isLoading, search, searchResults, isOpen, onClose, onClick, onSearch }) => {
121
118
  const listBoxRef = React__namespace.useRef(null);
122
119
  const onChange = React__namespace.useCallback(e => onSearch(e.currentTarget.value), [onSearch]);
123
120
  const onKeyDown = React__namespace.useCallback(e => {
@@ -134,7 +131,7 @@ const SearchImpl = ({ search, searchResults, isOpen, onClose, onClick, onSearch
134
131
  onClick(selectedResult);
135
132
  }
136
133
  }, [searchResults, onClick]);
137
- return (React__namespace.createElement(mosaic.Modal, { renderHeader: () => (React__namespace.createElement(mosaic.Input, { appearance: "minimal", borderB: true, size: "lg", icon: React__namespace.createElement(mosaic.Box, { as: mosaic.Icon, ml: 1, icon: faSearch.faSearch }), autoFocus: true, placeholder: "Search...", value: search, onChange: onChange, onKeyDown: onKeyDown })), isOpen: !!isOpen, onClose: onClose }, searchResults && searchResults.length > 0 ? (React__namespace.createElement(mosaic.ListBox, { ref: listBoxRef, "aria-label": "Search", overflowY: "auto", h: 80, m: -5, items: searchResults, selectionMode: "single", onSelectionChange: onSelectionChange }, (searchResult) => {
134
+ return (React__namespace.createElement(mosaic.Modal, { renderHeader: () => (React__namespace.createElement(mosaic.Input, { appearance: "minimal", borderB: true, size: "lg", icon: React__namespace.createElement(mosaic.Box, { as: mosaic.Icon, ml: 1, icon: isLoading ? freeSolidSvgIcons.faSpinner : freeSolidSvgIcons.faSearch, spin: isLoading }), autoFocus: true, placeholder: "Search...", value: search, onChange: onChange, onKeyDown: onKeyDown })), isOpen: !!isOpen, onClose: onClose }, searchResults && searchResults.length > 0 ? (React__namespace.createElement(mosaic.ListBox, { ref: listBoxRef, "aria-label": "Search", overflowY: "auto", h: 80, m: -5, items: searchResults, selectionMode: "single", onSelectionChange: onSelectionChange }, (searchResult) => {
138
135
  var _a, _b;
139
136
  return (React__namespace.createElement(mosaic.ListBoxItem, { key: `${searchResult.id}-${searchResult.project_id}`, textValue: searchResult.title },
140
137
  React__namespace.createElement(mosaic.Box, { p: 3, borderB: true },
@@ -145,7 +142,7 @@ const SearchImpl = ({ search, searchResults, isOpen, onClose, onClick, onSearch
145
142
  React__namespace.createElement(mosaic.Box, { dangerouslySetInnerHTML: { __html: (_b = searchResult.highlighted.summary) !== null && _b !== void 0 ? _b : '' }, color: "muted", fontSize: "sm", mt: 1, ml: 6 }))));
146
143
  })) : (React__namespace.createElement(mosaic.Flex, { w: "full", h: 80, align: "center", justify: "center", m: -5 }, "No search results"))));
147
144
  };
148
- const Search = lodash.flow(elementsCore.withStyles, elementsCore.withPersistenceBoundary, elementsCore.withMosaicProvider, elementsCore.withQueryClientProvider)(SearchImpl);
145
+ const Search = flow__default["default"](elementsCore.withStyles, elementsCore.withPersistenceBoundary, elementsCore.withMosaicProvider, elementsCore.withQueryClientProvider)(SearchImpl);
149
146
 
150
147
  /*! *****************************************************************************
151
148
  Copyright (c) Microsoft Corporation.
@@ -209,7 +206,7 @@ const UpgradeToStarter = () => (React__default["default"].createElement(mosaic.F
209
206
  React__default["default"].createElement(mosaic.Icon, { icon: freeSolidSvgIcons.faExclamationTriangle, size: "4x" }),
210
207
  React__default["default"].createElement(mosaic.Box, { pt: 3 }, "Please upgrade your Stoplight Workspace to the Starter Plan to use Elements Dev Portal in production.")));
211
208
 
212
- const appVersion = '1.4.7';
209
+ const appVersion = '1.6.0';
213
210
 
214
211
  class ResponseError extends Error {
215
212
  constructor(message, responseCode) {
@@ -323,7 +320,7 @@ const StoplightProjectImpl = ({ projectId, hideTryIt, hideMocking, hideExport, c
323
320
  elem = React__namespace.createElement(NotFound, null);
324
321
  }
325
322
  else {
326
- elem = (React__namespace.createElement(NodeContent, { node: node, Link: reactRouterDom.Link, hideTryIt: hideTryIt, hideMocking: hideMocking, hideExport: hideExport, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy }));
323
+ elem = (React__namespace.createElement(NodeContent, { node: node, Link: elementsCore.ReactRouterMarkdownLink, hideTryIt: hideTryIt, hideMocking: hideMocking, hideExport: hideExport, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy }));
327
324
  }
328
325
  const handleTocClick = () => {
329
326
  if (container.current) {
@@ -335,8 +332,8 @@ const StoplightProjectImpl = ({ projectId, hideTryIt, hideMocking, hideExport, c
335
332
  tableOfContents ? (React__namespace.createElement(TableOfContents, { activeId: (node === null || node === void 0 ? void 0 : node.id) || (nodeSlug === null || nodeSlug === void 0 ? void 0 : nodeSlug.split('-')[0]) || '', tableOfContents: tableOfContents, Link: reactRouterDom.Link, collapseTableOfContents: collapseTableOfContents, onLinkClick: handleTocClick })) : null) }, elem));
336
333
  };
337
334
  const StoplightProjectRouter = (_a) => {
338
- var { platformUrl, basePath = '/', router } = _a, props = __rest(_a, ["platformUrl", "basePath", "router"]);
339
- const { Router, routerProps } = elementsCore.useRouter(router !== null && router !== void 0 ? router : 'history', basePath);
335
+ var { platformUrl, basePath = '/', staticRouterPath = '', router = 'history' } = _a, props = __rest(_a, ["platformUrl", "basePath", "staticRouterPath", "router"]);
336
+ const { Router, routerProps } = elementsCore.useRouter(router, basePath, staticRouterPath);
340
337
  return (React__namespace.createElement(DevPortalProvider, { platformUrl: platformUrl },
341
338
  React__namespace.createElement(Router, Object.assign({}, routerProps, { key: basePath }),
342
339
  React__namespace.createElement(reactRouterDom.Route, { path: "/branches/:branchSlug/:nodeSlug", exact: true },
@@ -346,7 +343,7 @@ const StoplightProjectRouter = (_a) => {
346
343
  React__namespace.createElement(reactRouterDom.Route, { path: "/", exact: true },
347
344
  React__namespace.createElement(StoplightProjectImpl, Object.assign({}, props))))));
348
345
  };
349
- const StoplightProject = lodash.flow(elementsCore.withStyles, elementsCore.withPersistenceBoundary, elementsCore.withMosaicProvider, elementsCore.withQueryClientProvider)(StoplightProjectRouter);
346
+ const StoplightProject = elementsCore.withStyles(StoplightProjectRouter);
350
347
 
351
348
  const getNodes = ({ workspaceId, branchSlug, projectIds, search, platformUrl = 'https://stoplight.io', platformAuthToken, }) => __awaiter(void 0, void 0, void 0, function* () {
352
349
  const queryParams = [];
@@ -389,10 +386,237 @@ const getWorkspace = ({ projectIds, platformUrl = 'https://stoplight.io', platfo
389
386
  return data;
390
387
  });
391
388
 
389
+ /**
390
+ * Creates a debounced function that delays invoking `func` until after `wait`
391
+ * milliseconds have elapsed since the last time the debounced function was
392
+ * invoked, or until the next browser frame is drawn. The debounced function
393
+ * comes with a `cancel` method to cancel delayed `func` invocations and a
394
+ * `flush` method to immediately invoke them. Provide `options` to indicate
395
+ * whether `func` should be invoked on the leading and/or trailing edge of the
396
+ * `wait` timeout. The `func` is invoked with the last arguments provided to the
397
+ * debounced function. Subsequent calls to the debounced function return the
398
+ * result of the last `func` invocation.
399
+ *
400
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
401
+ * invoked on the trailing edge of the timeout only if the debounced function
402
+ * is invoked more than once during the `wait` timeout.
403
+ *
404
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
405
+ * until the next tick, similar to `setTimeout` with a timeout of `0`.
406
+ *
407
+ * If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
408
+ * invocation will be deferred until the next frame is drawn (typically about
409
+ * 16ms).
410
+ *
411
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
412
+ * for details over the differences between `debounce` and `throttle`.
413
+ *
414
+ * @category Function
415
+ * @param {Function} func The function to debounce.
416
+ * @param {number} [wait=0]
417
+ * The number of milliseconds to delay; if omitted, `requestAnimationFrame` is
418
+ * used (if available, otherwise it will be setTimeout(...,0)).
419
+ * @param {Object} [options={}] The options object.
420
+ * Specify invoking on the leading edge of the timeout.
421
+ * @param {boolean} [options.leading=false]
422
+ * The maximum time `func` is allowed to be delayed before it's invoked.
423
+ * @param {number} [options.maxWait]
424
+ * Specify invoking on the trailing edge of the timeout.
425
+ * @param {boolean} [options.trailing=true]
426
+ * @returns {Function} Returns the new debounced function.
427
+ * @example
428
+ *
429
+ * // Avoid costly calculations while the window size is in flux.
430
+ * const resizeHandler = useDebouncedCallback(calculateLayout, 150);
431
+ * window.addEventListener('resize', resizeHandler)
432
+ *
433
+ * // Invoke `sendMail` when clicked, debouncing subsequent calls.
434
+ * const clickHandler = useDebouncedCallback(sendMail, 300, {
435
+ * leading: true,
436
+ * trailing: false,
437
+ * })
438
+ * <button onClick={clickHandler}>click me</button>
439
+ *
440
+ * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
441
+ * const debounced = useDebouncedCallback(batchLog, 250, { 'maxWait': 1000 })
442
+ * const source = new EventSource('/stream')
443
+ * source.addEventListener('message', debounced)
444
+ *
445
+ * // Cancel the trailing debounced invocation.
446
+ * window.addEventListener('popstate', debounced.cancel)
447
+ *
448
+ * // Check for pending invocations.
449
+ * const status = debounced.pending() ? "Pending..." : "Ready"
450
+ */
451
+ function useDebouncedCallback(func, wait, options) {
452
+ var _this = this;
453
+ var lastCallTime = React.useRef(null);
454
+ var lastInvokeTime = React.useRef(0);
455
+ var timerId = React.useRef(null);
456
+ var lastArgs = React.useRef([]);
457
+ var lastThis = React.useRef();
458
+ var result = React.useRef();
459
+ var funcRef = React.useRef(func);
460
+ var mounted = React.useRef(true);
461
+ funcRef.current = func;
462
+ // Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
463
+ var useRAF = !wait && wait !== 0 && typeof window !== 'undefined';
464
+ if (typeof func !== 'function') {
465
+ throw new TypeError('Expected a function');
466
+ }
467
+ wait = +wait || 0;
468
+ options = options || {};
469
+ var leading = !!options.leading;
470
+ var trailing = 'trailing' in options ? !!options.trailing : true; // `true` by default
471
+ var maxing = 'maxWait' in options;
472
+ var maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : null;
473
+ React.useEffect(function () {
474
+ mounted.current = true;
475
+ return function () {
476
+ mounted.current = false;
477
+ };
478
+ }, []);
479
+ // You may have a question, why we have so many code under the useMemo definition.
480
+ //
481
+ // This was made as we want to escape from useCallback hell and
482
+ // not to initialize a number of functions each time useDebouncedCallback is called.
483
+ //
484
+ // It means that we have less garbage for our GC calls which improves performance.
485
+ // Also, it makes this library smaller.
486
+ //
487
+ // And the last reason, that the code without lots of useCallback with deps is easier to read.
488
+ // You have only one place for that.
489
+ var debounced = React.useMemo(function () {
490
+ var invokeFunc = function (time) {
491
+ var args = lastArgs.current;
492
+ var thisArg = lastThis.current;
493
+ lastArgs.current = lastThis.current = null;
494
+ lastInvokeTime.current = time;
495
+ return (result.current = funcRef.current.apply(thisArg, args));
496
+ };
497
+ var startTimer = function (pendingFunc, wait) {
498
+ if (useRAF)
499
+ cancelAnimationFrame(timerId.current);
500
+ timerId.current = useRAF ? requestAnimationFrame(pendingFunc) : setTimeout(pendingFunc, wait);
501
+ };
502
+ var shouldInvoke = function (time) {
503
+ if (!mounted.current)
504
+ return false;
505
+ var timeSinceLastCall = time - lastCallTime.current;
506
+ var timeSinceLastInvoke = time - lastInvokeTime.current;
507
+ // Either this is the first call, activity has stopped and we're at the
508
+ // trailing edge, the system time has gone backwards and we're treating
509
+ // it as the trailing edge, or we've hit the `maxWait` limit.
510
+ return (!lastCallTime.current ||
511
+ timeSinceLastCall >= wait ||
512
+ timeSinceLastCall < 0 ||
513
+ (maxing && timeSinceLastInvoke >= maxWait));
514
+ };
515
+ var trailingEdge = function (time) {
516
+ timerId.current = null;
517
+ // Only invoke if we have `lastArgs` which means `func` has been
518
+ // debounced at least once.
519
+ if (trailing && lastArgs.current) {
520
+ return invokeFunc(time);
521
+ }
522
+ lastArgs.current = lastThis.current = null;
523
+ return result.current;
524
+ };
525
+ var timerExpired = function () {
526
+ var time = Date.now();
527
+ if (shouldInvoke(time)) {
528
+ return trailingEdge(time);
529
+ }
530
+ // https://github.com/xnimorz/use-debounce/issues/97
531
+ if (!mounted.current) {
532
+ return;
533
+ }
534
+ // Remaining wait calculation
535
+ var timeSinceLastCall = time - lastCallTime.current;
536
+ var timeSinceLastInvoke = time - lastInvokeTime.current;
537
+ var timeWaiting = wait - timeSinceLastCall;
538
+ var remainingWait = maxing ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
539
+ // Restart the timer
540
+ startTimer(timerExpired, remainingWait);
541
+ };
542
+ var func = function () {
543
+ var args = [];
544
+ for (var _i = 0; _i < arguments.length; _i++) {
545
+ args[_i] = arguments[_i];
546
+ }
547
+ var time = Date.now();
548
+ var isInvoking = shouldInvoke(time);
549
+ lastArgs.current = args;
550
+ lastThis.current = _this;
551
+ lastCallTime.current = time;
552
+ if (isInvoking) {
553
+ if (!timerId.current && mounted.current) {
554
+ // Reset any `maxWait` timer.
555
+ lastInvokeTime.current = lastCallTime.current;
556
+ // Start the timer for the trailing edge.
557
+ startTimer(timerExpired, wait);
558
+ // Invoke the leading edge.
559
+ return leading ? invokeFunc(lastCallTime.current) : result.current;
560
+ }
561
+ if (maxing) {
562
+ // Handle invocations in a tight loop.
563
+ startTimer(timerExpired, wait);
564
+ return invokeFunc(lastCallTime.current);
565
+ }
566
+ }
567
+ if (!timerId.current) {
568
+ startTimer(timerExpired, wait);
569
+ }
570
+ return result.current;
571
+ };
572
+ func.cancel = function () {
573
+ if (timerId.current) {
574
+ useRAF ? cancelAnimationFrame(timerId.current) : clearTimeout(timerId.current);
575
+ }
576
+ lastInvokeTime.current = 0;
577
+ lastArgs.current = lastCallTime.current = lastThis.current = timerId.current = null;
578
+ };
579
+ func.isPending = function () {
580
+ return !!timerId.current;
581
+ };
582
+ func.flush = function () {
583
+ return !timerId.current ? result.current : trailingEdge(Date.now());
584
+ };
585
+ return func;
586
+ }, [leading, maxing, wait, maxWait, trailing, useRAF]);
587
+ return debounced;
588
+ }
589
+
590
+ function valueEquality(left, right) {
591
+ return left === right;
592
+ }
593
+ function adjustFunctionValueOfSetState(value) {
594
+ return typeof value === 'function' ? function () { return value; } : value;
595
+ }
596
+ function useStateIgnoreCallback(initialState) {
597
+ var _a = React.useState(adjustFunctionValueOfSetState(initialState)), state = _a[0], setState = _a[1];
598
+ var setStateIgnoreCallback = React.useCallback(function (value) { return setState(adjustFunctionValueOfSetState(value)); }, []);
599
+ return [state, setStateIgnoreCallback];
600
+ }
601
+ function useDebounce(value, delay, options) {
602
+ var eq = (options && options.equalityFn) || valueEquality;
603
+ var _a = useStateIgnoreCallback(value), state = _a[0], dispatch = _a[1];
604
+ var debounced = useDebouncedCallback(React.useCallback(function (value) { return dispatch(value); }, [dispatch]), delay, options);
605
+ var previousValue = React.useRef(value);
606
+ React.useEffect(function () {
607
+ // We need to use this condition otherwise we will run debounce timer for the first render (including maxWait option)
608
+ if (!eq(previousValue.current, value)) {
609
+ debounced(value);
610
+ previousValue.current = value;
611
+ }
612
+ }, [value, debounced, eq]);
613
+ return [state, { cancel: debounced.cancel, isPending: debounced.isPending, flush: debounced.flush }];
614
+ }
615
+
392
616
  function useGetNodes({ search, workspaceId, branchSlug, projectIds, pause, }) {
393
617
  const { platformUrl, platformAuthToken } = React__namespace.useContext(PlatformContext);
394
- const [debounceSearch] = useDebounce.useDebounce(search, 500);
395
- return reactQuery.useQuery(['workspaceNodes', workspaceId, branchSlug, projectIds, debounceSearch, platformUrl, platformAuthToken], () => getNodes({ workspaceId, branchSlug, projectIds, search: debounceSearch, platformUrl, platformAuthToken }), { enabled: !pause });
618
+ const [debounceSearch] = useDebounce(search, 500);
619
+ return reactQuery.useQuery(['workspaceNodes', platformUrl, platformAuthToken, workspaceId, branchSlug, projectIds, debounceSearch], () => getNodes({ workspaceId, branchSlug, projectIds, search: debounceSearch, platformUrl, platformAuthToken }), { enabled: !pause, keepPreviousData: true });
396
620
  }
397
621
 
398
622
  function useGetWorkspace({ projectIds }) {