@parca/profile 0.19.138 → 0.19.140

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.
Files changed (138) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.d.ts.map +1 -1
  3. package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.js +11 -13
  4. package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts.map +1 -1
  5. package/dist/ProfileExplorer/ProfileExplorerCompare.js +4 -9
  6. package/dist/ProfileFlameChart/SamplesStrips/index.d.ts +2 -2
  7. package/dist/ProfileFlameChart/SamplesStrips/index.d.ts.map +1 -1
  8. package/dist/ProfileFlameChart/index.d.ts.map +1 -1
  9. package/dist/ProfileFlameChart/index.js +13 -19
  10. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.d.ts.map +1 -1
  11. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.js +8 -8
  12. package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.d.ts.map +1 -1
  13. package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.js +4 -3
  14. package/dist/ProfileFlameGraph/index.d.ts.map +1 -1
  15. package/dist/ProfileFlameGraph/index.js +6 -4
  16. package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
  17. package/dist/ProfileMetricsGraph/index.js +4 -6
  18. package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
  19. package/dist/ProfileSelector/MetricsGraphSection.js +5 -10
  20. package/dist/ProfileSelector/index.d.ts.map +1 -1
  21. package/dist/ProfileSelector/index.js +27 -25
  22. package/dist/ProfileSelector/useAutoQuerySelector.d.ts.map +1 -1
  23. package/dist/ProfileSelector/useAutoQuerySelector.js +3 -0
  24. package/dist/ProfileTypeSelector/index.d.ts.map +1 -1
  25. package/dist/ProfileTypeSelector/index.js +4 -0
  26. package/dist/ProfileView/components/ActionButtons/SortByDropdown.d.ts.map +1 -1
  27. package/dist/ProfileView/components/ActionButtons/SortByDropdown.js +5 -5
  28. package/dist/ProfileView/components/ColorStackLegend.d.ts.map +1 -1
  29. package/dist/ProfileView/components/ColorStackLegend.js +2 -3
  30. package/dist/ProfileView/components/InvertCallStack/index.d.ts.map +1 -1
  31. package/dist/ProfileView/components/InvertCallStack/index.js +5 -4
  32. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts +1 -2
  33. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts.map +1 -1
  34. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.js +14 -16
  35. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.test.js +84 -170
  36. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -1
  37. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.js +16 -20
  38. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.d.ts.map +1 -1
  39. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.js +4 -5
  40. package/dist/ProfileView/components/Toolbars/index.d.ts +2 -2
  41. package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
  42. package/dist/ProfileView/components/Toolbars/index.js +1 -1
  43. package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
  44. package/dist/ProfileView/components/ViewSelector/index.js +8 -14
  45. package/dist/ProfileView/context/DashboardContext.d.ts.map +1 -1
  46. package/dist/ProfileView/context/DashboardContext.js +6 -6
  47. package/dist/ProfileView/hooks/useResetFlameGraphState.d.ts.map +1 -1
  48. package/dist/ProfileView/hooks/useResetFlameGraphState.js +5 -4
  49. package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.d.ts.map +1 -1
  50. package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.js +25 -26
  51. package/dist/ProfileView/hooks/useResetStateOnSeriesChange.d.ts.map +1 -1
  52. package/dist/ProfileView/hooks/useResetStateOnSeriesChange.js +13 -8
  53. package/dist/ProfileView/hooks/useVisualizationState.d.ts +3 -3
  54. package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
  55. package/dist/ProfileView/hooks/useVisualizationState.js +35 -51
  56. package/dist/ProfileViewWithData.d.ts.map +1 -1
  57. package/dist/ProfileViewWithData.js +19 -28
  58. package/dist/Sandwich/index.d.ts.map +1 -1
  59. package/dist/Sandwich/index.js +4 -3
  60. package/dist/SourceView/index.d.ts.map +1 -1
  61. package/dist/SourceView/index.js +4 -2
  62. package/dist/SourceView/useSelectedLineRange.d.ts.map +1 -1
  63. package/dist/SourceView/useSelectedLineRange.js +21 -16
  64. package/dist/Table/MoreDropdown.d.ts.map +1 -1
  65. package/dist/Table/MoreDropdown.js +8 -11
  66. package/dist/Table/TableContextMenu.d.ts.map +1 -1
  67. package/dist/Table/TableContextMenu.js +10 -13
  68. package/dist/Table/hooks/useTableConfiguration.d.ts.map +1 -1
  69. package/dist/Table/hooks/useTableConfiguration.js +3 -4
  70. package/dist/Table/index.d.ts.map +1 -1
  71. package/dist/Table/index.js +11 -9
  72. package/dist/TopTable/index.d.ts.map +1 -1
  73. package/dist/TopTable/index.js +3 -4
  74. package/dist/hooks/urlParsers.d.ts +18 -0
  75. package/dist/hooks/urlParsers.d.ts.map +1 -0
  76. package/dist/hooks/urlParsers.js +32 -0
  77. package/dist/hooks/useColorBy.d.ts +5 -0
  78. package/dist/hooks/useColorBy.d.ts.map +1 -0
  79. package/dist/hooks/useColorBy.js +26 -0
  80. package/dist/hooks/useCompareModeMeta.d.ts.map +1 -1
  81. package/dist/hooks/useCompareModeMeta.js +55 -86
  82. package/dist/hooks/useDashboardItems.d.ts +5 -0
  83. package/dist/hooks/useDashboardItems.d.ts.map +1 -0
  84. package/dist/hooks/useDashboardItems.js +27 -0
  85. package/dist/hooks/useQueryState.d.ts +3 -3
  86. package/dist/hooks/useQueryState.d.ts.map +1 -1
  87. package/dist/hooks/useQueryState.js +105 -105
  88. package/dist/hooks/useQueryState.test.js +186 -302
  89. package/dist/index.d.ts +3 -2
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +3 -12
  92. package/dist/useSumBy.d.ts +1 -1
  93. package/dist/useSumBy.d.ts.map +1 -1
  94. package/dist/useSumBy.js +2 -2
  95. package/package.json +12 -11
  96. package/src/GraphTooltipArrow/useGraphTooltipMetaInfo/index.ts +11 -13
  97. package/src/ProfileExplorer/ProfileExplorerCompare.tsx +4 -9
  98. package/src/ProfileFlameChart/SamplesStrips/index.tsx +2 -2
  99. package/src/ProfileFlameChart/index.tsx +21 -28
  100. package/src/ProfileFlameGraph/FlameGraphArrow/ContextMenu.tsx +10 -9
  101. package/src/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.tsx +5 -3
  102. package/src/ProfileFlameGraph/index.tsx +6 -9
  103. package/src/ProfileMetricsGraph/index.tsx +6 -8
  104. package/src/ProfileSelector/MetricsGraphSection.tsx +5 -10
  105. package/src/ProfileSelector/index.tsx +32 -31
  106. package/src/ProfileSelector/useAutoQuerySelector.ts +5 -0
  107. package/src/ProfileTypeSelector/index.tsx +4 -0
  108. package/src/ProfileView/components/ActionButtons/SortByDropdown.tsx +10 -6
  109. package/src/ProfileView/components/ColorStackLegend.tsx +2 -4
  110. package/src/ProfileView/components/InvertCallStack/index.tsx +5 -4
  111. package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.test.tsx +94 -192
  112. package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.ts +21 -21
  113. package/src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx +24 -25
  114. package/src/ProfileView/components/Toolbars/TableColumnsDropdown.tsx +4 -5
  115. package/src/ProfileView/components/Toolbars/index.tsx +3 -3
  116. package/src/ProfileView/components/ViewSelector/index.tsx +9 -16
  117. package/src/ProfileView/context/DashboardContext.tsx +6 -6
  118. package/src/ProfileView/hooks/useResetFlameGraphState.ts +6 -4
  119. package/src/ProfileView/hooks/useResetStateOnProfileTypeChange.ts +24 -26
  120. package/src/ProfileView/hooks/useResetStateOnSeriesChange.ts +16 -8
  121. package/src/ProfileView/hooks/useVisualizationState.ts +61 -69
  122. package/src/ProfileViewWithData.tsx +29 -35
  123. package/src/Sandwich/index.tsx +4 -3
  124. package/src/SourceView/index.tsx +4 -2
  125. package/src/SourceView/useSelectedLineRange.ts +34 -19
  126. package/src/Table/MoreDropdown.tsx +9 -11
  127. package/src/Table/TableContextMenu.tsx +10 -13
  128. package/src/Table/hooks/useTableConfiguration.tsx +3 -4
  129. package/src/Table/index.tsx +12 -21
  130. package/src/TopTable/index.tsx +3 -4
  131. package/src/hooks/urlParsers.ts +38 -0
  132. package/src/hooks/useColorBy.ts +42 -0
  133. package/src/hooks/useCompareModeMeta.ts +61 -91
  134. package/src/hooks/useDashboardItems.ts +46 -0
  135. package/src/hooks/useQueryState.test.tsx +275 -345
  136. package/src/hooks/useQueryState.ts +136 -118
  137. package/src/index.tsx +16 -15
  138. package/src/useSumBy.ts +3 -3
@@ -13,7 +13,8 @@
13
13
 
14
14
  import {useCallback, useMemo} from 'react';
15
15
 
16
- import {useURLStateBatch, useURLStateCustom, type ParamValueSetterCustom} from '@parca/components';
16
+ import {createParser, useQueryState} from 'nuqs';
17
+
17
18
  import {safeDecode} from '@parca/utilities';
18
19
 
19
20
  import {isPresetKey} from './filterPresets';
@@ -137,31 +138,32 @@ export const decodeProfileFilters = (encoded: string): ProfileFilter[] => {
137
138
  }
138
139
  };
139
140
 
141
+ const profileFiltersParser = createParser<ProfileFilter[]>({
142
+ parse: (value: string) => decodeProfileFilters(value),
143
+ serialize: (value: ProfileFilter[]) => encodeProfileFilters(value),
144
+ eq: (a, b) => encodeProfileFilters(a) === encodeProfileFilters(b),
145
+ })
146
+ .withDefault([])
147
+ .withOptions({history: 'replace'});
148
+
140
149
  export const useProfileFiltersUrlState = (): {
141
150
  appliedFilters: ProfileFilter[];
142
- setAppliedFilters: ParamValueSetterCustom<ProfileFilter[]>;
151
+ setAppliedFilters: (filters: ProfileFilter[]) => void;
143
152
  forceApplyFilters: (filters: ProfileFilter[]) => void;
144
153
  } => {
145
- const batchUpdates = useURLStateBatch();
146
-
147
- // Store applied filters in URL state for persistence using compact encoding
148
- const [appliedFilters, setAppliedFilters] = useURLStateCustom<ProfileFilter[]>(
149
- `profile_filters`,
150
- {
151
- parse: value => {
152
- return decodeProfileFilters(value as string);
153
- },
154
- stringify: value => {
155
- return encodeProfileFilters(value);
156
- },
157
- defaultValue: [],
158
- }
159
- );
154
+ const [appliedFilters, setRawFilters] = useQueryState('profile_filters', profileFiltersParser);
160
155
 
161
156
  const memoizedAppliedFilters = useMemo(() => {
162
157
  return appliedFilters ?? [];
163
158
  }, [appliedFilters]);
164
159
 
160
+ const setAppliedFilters = useCallback(
161
+ (filters: ProfileFilter[]) => {
162
+ void setRawFilters(filters);
163
+ },
164
+ [setRawFilters]
165
+ );
166
+
165
167
  // Force apply filters (bypasses preserve-existing strategy)
166
168
  const forceApplyFilters = useCallback(
167
169
  (filters: ProfileFilter[]) => {
@@ -172,11 +174,9 @@ export const useProfileFiltersUrlState = (): {
172
174
  return f.value !== '' && f.type != null && f.field != null && f.matchType != null;
173
175
  });
174
176
 
175
- batchUpdates(() => {
176
- setAppliedFilters(validFilters);
177
- });
177
+ setAppliedFilters(validFilters);
178
178
  },
179
- [batchUpdates, setAppliedFilters]
179
+ [setAppliedFilters]
180
180
  );
181
181
 
182
182
  return {
@@ -16,8 +16,8 @@ import React, {useCallback, useEffect, useRef, useState} from 'react';
16
16
  import {Menu} from '@headlessui/react';
17
17
  import {Icon} from '@iconify/react';
18
18
  import cx from 'classnames';
19
+ import {useQueryState} from 'nuqs';
19
20
 
20
- import {useURLState} from '@parca/components';
21
21
  import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
22
22
  import {ProfileType} from '@parca/parser';
23
23
 
@@ -27,6 +27,7 @@ import {
27
27
  FIELD_LOCATION_ADDRESS,
28
28
  FIELD_MAPPING_FILE,
29
29
  } from '../../../ProfileFlameGraph/FlameGraphArrow';
30
+ import {boolParam, hiddenBinariesParser, stringParam} from '../../../hooks/urlParsers';
30
31
  import {useProfileViewContext} from '../../context/ProfileViewContext';
31
32
  import SwitchMenuItem from './SwitchMenuItem';
32
33
 
@@ -206,14 +207,15 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
206
207
  }) => {
207
208
  const dropdownRef = useRef<HTMLDivElement>(null);
208
209
  const [shouldOpenLeft, setShouldOpenLeft] = useState(false);
209
- const [storeSortBy] = useURLState('sort_by', {
210
- defaultValue: FIELD_FUNCTION_NAME,
211
- });
212
- const [colorStackLegend, setStoreColorStackLegend] = useURLState('color_stack_legend');
213
- const [hiddenBinaries, setHiddenBinaries] = useURLState('hidden_binaries', {
214
- defaultValue: [],
215
- alwaysReturnArray: true,
216
- });
210
+ const [storeSortBy] = useQueryState('sort_by', stringParam.withDefault(FIELD_FUNCTION_NAME));
211
+ const [colorStackLegend, setStoreColorStackLegend] = useQueryState(
212
+ 'color_stack_legend',
213
+ stringParam
214
+ );
215
+ const [hiddenBinaries, setHiddenBinaries] = useQueryState(
216
+ 'hidden_binaries',
217
+ hiddenBinariesParser
218
+ );
217
219
  const {compareMode} = useProfileViewContext();
218
220
  const [colorProfileName] = useUserPreference<string>(
219
221
  USER_PREFERENCES.FLAMEGRAPH_COLOR_PROFILE.key
@@ -223,11 +225,10 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
223
225
 
224
226
  // By default, we want delta profiles (CPU) to be relatively compared.
225
227
  // For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
226
- const compareAbsoluteDefault = profileType?.delta === false ? 'true' : 'false';
228
+ const compareAbsoluteDefault = profileType?.delta === false;
227
229
 
228
- const [compareAbsolute = compareAbsoluteDefault, setCompareAbsolute] =
229
- useURLState('compare_absolute');
230
- const isCompareAbsolute = compareAbsolute === 'true';
230
+ const [compareAbsolute, setCompareAbsolute] = useQueryState('compare_absolute', boolParam);
231
+ const isCompareAbsolute = compareAbsolute ?? compareAbsoluteDefault;
231
232
 
232
233
  useEffect(() => {
233
234
  const checkOverflow = (): void => {
@@ -248,20 +249,20 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
248
249
  }, [isTableVizOnly]);
249
250
 
250
251
  const handleBinaryToggle = (index: number): void => {
251
- const updatedBinaries = [...(hiddenBinaries as string[])];
252
+ const updatedBinaries = [...hiddenBinaries];
252
253
  updatedBinaries.splice(index, 1);
253
- setHiddenBinaries(updatedBinaries);
254
+ void setHiddenBinaries(updatedBinaries);
254
255
  };
255
256
 
256
257
  const setColorStackLegend = useCallback(
257
258
  (value: string): void => {
258
- setStoreColorStackLegend(value);
259
+ void setStoreColorStackLegend(value);
259
260
  },
260
261
  [setStoreColorStackLegend]
261
262
  );
262
263
 
263
264
  const resetLegend = (): void => {
264
- setHiddenBinaries([]);
265
+ void setHiddenBinaries([]);
265
266
  };
266
267
 
267
268
  const menuItems: MenuItemType[] = [
@@ -329,7 +330,7 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
329
330
  },
330
331
  {
331
332
  label: isCompareAbsolute ? 'Compare Relative' : 'Compare Absolute',
332
- onclick: () => setCompareAbsolute(isCompareAbsolute ? 'false' : 'true'),
333
+ onclick: () => void setCompareAbsolute(!isCompareAbsolute),
333
334
  hide: !compareMode,
334
335
  icon: isCompareAbsolute ? 'fluent-mdl2:compare' : 'fluent-mdl2:compare-uneven',
335
336
  },
@@ -359,7 +360,7 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
359
360
  },
360
361
  {
361
362
  label: 'Reset Legend',
362
- hide: hiddenBinaries === undefined || hiddenBinaries.length === 0,
363
+ hide: hiddenBinaries.length === 0,
363
364
  onclick: () => resetLegend(),
364
365
  id: 'h-reset-legend-button',
365
366
  icon: 'system-uicons:reset',
@@ -367,7 +368,7 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
367
368
  {
368
369
  label: 'Hidden Binaries',
369
370
  id: 'h-hidden-binaries',
370
- items: (hiddenBinaries as string[])?.map((binary, index) => ({
371
+ items: hiddenBinaries.map((binary, index) => ({
371
372
  label: binary,
372
373
  customSubmenu: (
373
374
  <div className="flex items-center gap-2 w-full">
@@ -383,7 +384,7 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
383
384
  </div>
384
385
  ),
385
386
  })),
386
- hide: hiddenBinaries === undefined || hiddenBinaries.length === 0,
387
+ hide: hiddenBinaries.length === 0,
387
388
  icon: 'ph:eye-closed',
388
389
  },
389
390
  ];
@@ -424,10 +425,8 @@ const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = ({
424
425
  {...item}
425
426
  onSelect={onSelect}
426
427
  closeDropdown={close}
427
- activeValueForSortBy={storeSortBy as string}
428
- activeValueForColorBy={
429
- colorBy === undefined || colorBy === '' ? 'binary' : colorBy
430
- }
428
+ activeValueForSortBy={storeSortBy}
429
+ activeValueForColorBy={colorBy}
431
430
  activeValuesForLevel={groupBy}
432
431
  renderAsDiv={item.renderAsDiv}
433
432
  />
@@ -14,14 +14,15 @@
14
14
  import {useEffect, useMemo, useState} from 'react';
15
15
 
16
16
  import {createColumnHelper, type ColumnDef} from '@tanstack/table-core';
17
+ import {useQueryState} from 'nuqs';
17
18
 
18
- import {useURLState} from '@parca/components';
19
19
  import {ProfileType} from '@parca/parser';
20
20
  import {valueFormatter} from '@parca/utilities';
21
21
 
22
22
  import {Row} from '../../../Table';
23
23
  import ColumnsVisibility from '../../../Table/ColumnsVisibility';
24
24
  import {ColumnName, addPlusSign, getRatioString} from '../../../Table/utils/functions';
25
+ import {tableColumnsParser} from '../../../hooks/urlParsers';
25
26
  import {useProfileViewContext} from '../../context/ProfileViewContext';
26
27
 
27
28
  interface Props {
@@ -32,9 +33,7 @@ interface Props {
32
33
 
33
34
  const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Element => {
34
35
  const {compareMode} = useProfileViewContext();
35
- const [tableColumns, setTableColumns] = useURLState<string[]>('table_columns', {
36
- alwaysReturnArray: true,
37
- });
36
+ const [tableColumns, setTableColumns] = useQueryState('table_columns', tableColumnsParser);
38
37
 
39
38
  const columnHelper = createColumnHelper<Row>();
40
39
 
@@ -190,7 +189,7 @@ const TableColumnsDropdown = ({profileType, total, filtered}: Props): JSX.Elemen
190
189
  const newTableColumns = (Object.keys(updatedColumns) as ColumnName[]).filter(
191
190
  col => updatedColumns[col]
192
191
  );
193
- setTableColumns(newTableColumns);
192
+ void setTableColumns(newTableColumns);
194
193
  };
195
194
 
196
195
  return (
@@ -50,7 +50,7 @@ export interface VisualisationToolbarProps {
50
50
  flamechartDimension: string[];
51
51
  setFlamechartDimension: (labels: string[]) => void;
52
52
  showVisualizationSelector?: boolean;
53
- sandwichFunctionName?: string;
53
+ sandwichFunctionName: string | null;
54
54
  alignFunctionName: string;
55
55
  setAlignFunctionName: (align: string) => void;
56
56
  colorBy: string;
@@ -75,7 +75,7 @@ export interface FlameGraphToolbarProps {
75
75
 
76
76
  export interface SandwichFlameGraphToolbarProps {
77
77
  resetSandwichFunctionName: () => void;
78
- sandwichFunctionName?: string;
78
+ sandwichFunctionName: string | null;
79
79
  }
80
80
 
81
81
  export const TableToolbar: FC<TableToolbarProps> = ({profileType, total, filtered}) => {
@@ -120,7 +120,7 @@ export const SandwichFlameGraphToolbar: FC<SandwichFlameGraphToolbarProps> = ({
120
120
  onClick={() => resetSandwichFunctionName()}
121
121
  className="w-auto"
122
122
  variant="neutral"
123
- disabled={sandwichFunctionName === undefined || sandwichFunctionName.length === 0}
123
+ disabled={sandwichFunctionName == null || sandwichFunctionName.length === 0}
124
124
  >
125
125
  Reset view
126
126
  </Button>
@@ -13,9 +13,13 @@
13
13
 
14
14
  import {ReactNode} from 'react';
15
15
 
16
- import {useParcaContext, useURLState, useURLStateBatch} from '@parca/components';
16
+ import {useQueryState} from 'nuqs';
17
+
18
+ import {useParcaContext} from '@parca/components';
17
19
 
18
20
  import {ProfileSource} from '../../../ProfileSource';
21
+ import {stringParam} from '../../../hooks/urlParsers';
22
+ import {useDashboardItems} from '../../../hooks/useDashboardItems';
19
23
  import Dropdown, {DropdownElement, InnerAction} from './Dropdown';
20
24
 
21
25
  interface Props {
@@ -23,15 +27,9 @@ interface Props {
23
27
  }
24
28
 
25
29
  const ViewSelector = ({profileSource}: Props): JSX.Element => {
26
- const [dashboardItems = ['flamegraph'], setDashboardItems] = useURLState<string[]>(
27
- 'dashboard_items',
28
- {
29
- alwaysReturnArray: true,
30
- }
31
- );
32
- const [, setSandwichFunctionName] = useURLState<string | undefined>('sandwich_function_name');
30
+ const {dashboardItems, setDashboardItems} = useDashboardItems();
31
+ const [, setSandwichFunctionName] = useQueryState('sandwich_function_name', stringParam);
33
32
  const {enableSourcesView, enableSandwichView} = useParcaContext();
34
- const batchUpdates = useURLStateBatch();
35
33
 
36
34
  const allItems: Array<{
37
35
  key: string;
@@ -129,14 +127,9 @@ const ViewSelector = ({profileSource}: Props): JSX.Element => {
129
127
  } else {
130
128
  const newDashboardItems = dashboardItems.filter(v => v !== item.key);
131
129
 
132
- // Batch updates when removing sandwich panel to combine both URL changes
130
+ setDashboardItems(newDashboardItems);
133
131
  if (item.key === 'sandwich') {
134
- batchUpdates(() => {
135
- setDashboardItems(newDashboardItems);
136
- setSandwichFunctionName(undefined);
137
- });
138
- } else {
139
- setDashboardItems(newDashboardItems);
132
+ void setSandwichFunctionName(null);
140
133
  }
141
134
  }
142
135
  },
@@ -13,8 +13,10 @@
13
13
 
14
14
  import {FC, PropsWithChildren, createContext, useContext} from 'react';
15
15
 
16
- import {useURLState} from '@parca/components';
16
+ import {useQueryState} from 'nuqs';
17
17
 
18
+ import {stringParam} from '../../hooks/urlParsers';
19
+ import {useDashboardItems} from '../../hooks/useDashboardItems';
18
20
  import {VisualizationType} from '../types/visualization';
19
21
 
20
22
  interface DashboardContextType {
@@ -27,10 +29,8 @@ interface DashboardContextType {
27
29
  const DashboardContext = createContext<DashboardContextType | undefined>(undefined);
28
30
 
29
31
  export const DashboardProvider: FC<PropsWithChildren> = ({children}) => {
30
- const [dashboardItems, setDashboardItems] = useURLState<string[]>('dashboard_items', {
31
- alwaysReturnArray: true,
32
- });
33
- const [, setSandwichFunctionName] = useURLState<string | undefined>('sandwich_function_name');
32
+ const {dashboardItems, setDashboardItems} = useDashboardItems();
33
+ const [, setSandwichFunctionName] = useQueryState('sandwich_function_name', stringParam);
34
34
 
35
35
  const handleClosePanel = (visualizationType: VisualizationType): void => {
36
36
  const newDashboardItems = dashboardItems.filter(item => item !== visualizationType);
@@ -38,7 +38,7 @@ export const DashboardProvider: FC<PropsWithChildren> = ({children}) => {
38
38
 
39
39
  // Reset sandwich function name when closing sandwich panel
40
40
  if (visualizationType === 'sandwich') {
41
- setSandwichFunctionName(undefined);
41
+ void setSandwichFunctionName(null);
42
42
  }
43
43
  };
44
44
 
@@ -11,17 +11,19 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {useURLState} from '@parca/components';
14
+ import {useQueryState} from 'nuqs';
15
+
16
+ import {stringParam} from '../../hooks/urlParsers';
15
17
 
16
18
  export const useResetFlameGraphState = (): (() => void) => {
17
- const [val, setCurPath] = useURLState('cur_path');
19
+ const [val, setCurPath] = useQueryState('cur_path', stringParam);
18
20
 
19
21
  return () => {
20
22
  setTimeout(() => {
21
- if (val === undefined) {
23
+ if (val === null) {
22
24
  return;
23
25
  }
24
- setCurPath(undefined);
26
+ void setCurPath(null);
25
27
  });
26
28
  };
27
29
  };
@@ -11,39 +11,37 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {useURLState, useURLStateBatch} from '@parca/components';
14
+ import {useQueryStates} from 'nuqs';
15
15
 
16
+ import {stringParam} from '../../hooks/urlParsers';
16
17
  import {useProfileFilters} from '../components/ProfileFilters/useProfileFilters';
17
18
 
18
19
  export const useResetStateOnProfileTypeChange = (): (() => void) => {
19
- const [groupBy, setGroupBy] = useURLState('group_by');
20
- const [curPath, setCurPath] = useURLState('cur_path');
21
- const [sumByA, setSumByA] = useURLState('sum_by_a');
22
- const [sumByB, setSumByB] = useURLState('sum_by_b');
20
+ const [state, setState] = useQueryStates(
21
+ {
22
+ group_by: stringParam,
23
+ cur_path: stringParam,
24
+ sum_by_a: stringParam,
25
+ sum_by_b: stringParam,
26
+ sandwich_function_name: stringParam,
27
+ },
28
+ {history: 'replace'}
29
+ );
23
30
  const {resetFilters} = useProfileFilters();
24
- const [sandwichFunctionName, setSandwichFunctionName] = useURLState('sandwich_function_name');
25
- const batchUpdates = useURLStateBatch();
26
31
 
27
32
  return () => {
28
- // Batch all URL state resets into a single navigation
29
- batchUpdates(() => {
30
- if (groupBy !== undefined) {
31
- setGroupBy(undefined);
32
- }
33
- if (curPath !== undefined) {
34
- setCurPath(undefined);
35
- }
36
- if (sandwichFunctionName !== undefined) {
37
- setSandwichFunctionName(undefined);
38
- }
39
- if (sumByA !== undefined) {
40
- setSumByA(undefined);
41
- }
42
- if (sumByB !== undefined) {
43
- setSumByB(undefined);
44
- }
33
+ // Atomic reset: clear all params in single URL update
34
+ const updates: Record<string, null> = {};
35
+ if (state.group_by !== null) updates.group_by = null;
36
+ if (state.cur_path !== null) updates.cur_path = null;
37
+ if (state.sandwich_function_name !== null) updates.sandwich_function_name = null;
38
+ if (state.sum_by_a !== null) updates.sum_by_a = null;
39
+ if (state.sum_by_b !== null) updates.sum_by_b = null;
45
40
 
46
- resetFilters();
47
- });
41
+ if (Object.keys(updates).length > 0) {
42
+ void setState(updates);
43
+ }
44
+
45
+ resetFilters();
48
46
  };
49
47
  };
@@ -11,19 +11,27 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {useURLState} from '@parca/components';
14
+ import {useQueryStates} from 'nuqs';
15
+
16
+ import {stringParam} from '../../hooks/urlParsers';
15
17
 
16
18
  export const useResetStateOnSeriesChange = (): (() => void) => {
17
- const [curPath, setCurPath] = useURLState('cur_path');
18
- const [sandwichFunctionName, setSandwichFunctionName] = useURLState('sandwich_function_name');
19
+ const [state, setState] = useQueryStates(
20
+ {
21
+ cur_path: stringParam,
22
+ sandwich_function_name: stringParam,
23
+ },
24
+ {history: 'replace'}
25
+ );
19
26
 
20
27
  return () => {
21
28
  setTimeout(() => {
22
- if (curPath !== undefined) {
23
- setCurPath(undefined);
24
- }
25
- if (sandwichFunctionName !== undefined) {
26
- setSandwichFunctionName(undefined);
29
+ const updates: Record<string, null> = {};
30
+ if (state.cur_path !== null) updates.cur_path = null;
31
+ if (state.sandwich_function_name !== null) updates.sandwich_function_name = null;
32
+
33
+ if (Object.keys(updates).length > 0) {
34
+ void setState(updates);
27
35
  }
28
36
  });
29
37
  };