@parca/profile 0.19.35 → 0.19.36

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/CHANGELOG.md CHANGED
@@ -3,6 +3,10 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.19.36](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.35...@parca/profile@0.19.36) (2025-08-15)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
6
10
  ## [0.19.35](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.34...@parca/profile@0.19.35) (2025-08-12)
7
11
 
8
12
  **Note:** Version bump only for package @parca/profile
@@ -1,5 +1,8 @@
1
1
  import { type ProfileFilter } from './useProfileFilters';
2
2
  export declare const isFilterComplete: (filter: ProfileFilter) => boolean;
3
- declare const ProfileFilters: () => JSX.Element;
3
+ export interface ProfileFiltersProps {
4
+ readOnly?: boolean;
5
+ }
6
+ declare const ProfileFilters: ({ readOnly }?: ProfileFiltersProps) => JSX.Element;
4
7
  export default ProfileFilters;
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/ProfileFilters/index.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAAoB,KAAK,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAE1E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,aAAa,KAAG,OASxD,CAAC;AAyIF,QAAA,MAAM,cAAc,QAAO,GAAG,CAAC,OAmL9B,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ProfileView/components/ProfileFilters/index.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAAoB,KAAK,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAE1E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,aAAa,KAAG,OASxD,CAAC;AAyIF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,cAAc,GAAI,eAAoB,mBAAwB,KAAG,GAAG,CAAC,OAsM1E,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -139,7 +139,7 @@ const numberMatchTypeItems = [
139
139
  },
140
140
  },
141
141
  ];
142
- const ProfileFilters = () => {
142
+ const ProfileFilters = ({ readOnly = false } = {}) => {
143
143
  const { profileSource } = useProfileViewContext();
144
144
  const currentProfileType = profileSource?.ProfileType()?.toString();
145
145
  const filterTypeItems = getFilterTypeItems(currentProfileType);
@@ -158,7 +158,7 @@ const ProfileFilters = () => {
158
158
  const isNumberField = filter.field === 'address' || filter.field === 'line_number';
159
159
  const matchTypeItems = isNumberField ? numberMatchTypeItems : stringMatchTypeItems;
160
160
  const isPresetFilter = filter.type != null && isPresetKey(filter.type);
161
- return (_jsxs("div", { className: "flex items-center gap-0", children: [_jsx(Select, { items: filterTypeItems, selectedKey: filter.type, placeholder: "Select Filter", onSelection: key => {
161
+ return (_jsxs("div", { className: "flex items-center gap-0", children: [_jsx(Select, { items: filterTypeItems, selectedKey: filter.type, placeholder: "Select Filter", disabled: readOnly, onSelection: key => {
162
162
  // Check if this is a preset selection
163
163
  if (isPresetKey(key)) {
164
164
  const preset = getPresetByKey(key);
@@ -190,7 +190,7 @@ const ProfileFilters = () => {
190
190
  });
191
191
  }
192
192
  }
193
- }, className: cx('rounded-l-md pr-1 gap-0 focus:z-50 focus:relative focus:outline-1', isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none', filter.type != null ? 'border-r-0 w-auto' : 'w-32') }), filter.type != null && !isPresetFilter && (_jsxs(_Fragment, { children: [_jsx(Select, { items: fieldItems, selectedKey: filter.field ?? '', onSelection: key => {
193
+ }, className: cx('gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1', readOnly && isPresetFilter ? 'rounded-md' : 'rounded-l-md rounded-r-none', !readOnly && (isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none'), readOnly ? 'w-auto' : filter.type != null ? 'border-r-0 w-auto' : 'w-32'), hideCaretDropdown: readOnly }), filter.type != null && !isPresetFilter && (_jsxs(_Fragment, { children: [_jsx(Select, { items: fieldItems, selectedKey: filter.field ?? '', disabled: readOnly, onSelection: key => {
194
194
  const newField = key;
195
195
  const isNewFieldNumber = newField === 'address' || newField === 'line_number';
196
196
  const isCurrentFieldNumber = filter.field === 'address' || filter.field === 'line_number';
@@ -203,7 +203,7 @@ const ProfileFilters = () => {
203
203
  else {
204
204
  updateFilter(filter.id, { field: newField });
205
205
  }
206
- }, className: "rounded-none border-r-0 w-32 pr-1 gap-0 focus:z-50 focus:relative focus:outline-1" }), _jsx(Select, { items: matchTypeItems, selectedKey: filter.matchType ?? '', onSelection: key => updateFilter(filter.id, { matchType: key }), className: "rounded-none border-r-0 pr-1 gap-0 focus:z-50 focus:relative focus:outline-1" }), _jsx(Input, { placeholder: "Value", value: filter.value, onChange: e => updateFilter(filter.id, { value: e.target.value }), onKeyDown: handleKeyDown, className: "rounded-none w-36 text-sm focus:outline-1" })] })), _jsx(Button, { variant: "neutral", onClick: () => {
206
+ }, className: cx('rounded-none border-r-0 w-32 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Select, { items: matchTypeItems, selectedKey: filter.matchType ?? '', disabled: readOnly, onSelection: key => updateFilter(filter.id, { matchType: key }), className: cx('rounded-none border-r-0 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Input, { placeholder: "Value", value: filter.value, disabled: readOnly, onChange: e => updateFilter(filter.id, { value: e.target.value }), onKeyDown: handleKeyDown, className: "rounded-none w-36 text-sm focus:outline-1" })] })), !readOnly && (_jsx(Button, { variant: "neutral", onClick: () => {
207
207
  // If we're displaying local filters and this is the last one, reset everything
208
208
  if (localFilters.length > 0 && localFilters.length === 1) {
209
209
  resetFilters();
@@ -216,7 +216,9 @@ const ProfileFilters = () => {
216
216
  else {
217
217
  removeFilter(filter.id);
218
218
  }
219
- }, className: cx('h-[38px] p-3', filter.type != null ? 'rounded-none rounded-r-md' : 'rounded-l-none rounded-r-md'), children: _jsx(Icon, { icon: "mdi:close", className: "h-4 w-4" }) })] }, filter.id));
220
- }), localFilters.length > 0 && (_jsx(Button, { variant: "neutral", onClick: addFilter, className: "p-3 h-[38px]", children: _jsx(Icon, { icon: "mdi:filter-plus-outline", className: "h-4 w-4" }) })), localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (_jsxs(Button, { variant: "neutral", onClick: addFilter, className: "flex items-center gap-2", children: [_jsx(Icon, { icon: "mdi:filter-outline", className: "h-4 w-4" }), _jsx("span", { children: "Filter" })] }))] }), localFilters.length > 0 && (_jsx(Button, { variant: "primary", onClick: onApplyFilters, disabled: !hasUnsavedChanges || !localFilters.some(isFilterComplete), className: cx('flex items-center gap-2 sticky top-0'), children: _jsx("span", { children: "Apply" }) }))] }));
219
+ }, className: cx('h-[38px] p-3', filter.type != null
220
+ ? 'rounded-none rounded-r-md'
221
+ : 'rounded-l-none rounded-r-md'), children: _jsx(Icon, { icon: "mdi:close", className: "h-4 w-4" }) }))] }, filter.id));
222
+ }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "neutral", onClick: addFilter, className: "p-3 h-[38px]", children: _jsx(Icon, { icon: "mdi:filter-plus-outline", className: "h-4 w-4" }) })), !readOnly && localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (_jsxs(Button, { variant: "neutral", onClick: addFilter, className: "flex items-center gap-2", children: [_jsx(Icon, { icon: "mdi:filter-outline", className: "h-4 w-4" }), _jsx("span", { children: "Filter" })] }))] }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "primary", onClick: onApplyFilters, disabled: !hasUnsavedChanges || !localFilters.some(isFilterComplete), className: cx('flex items-center gap-2 sticky top-0'), children: _jsx("span", { children: "Apply" }) }))] }));
221
223
  };
222
224
  export default ProfileFilters;
package/dist/index.d.ts CHANGED
@@ -11,6 +11,8 @@ export * from './utils';
11
11
  export * from './ProfileTypeSelector';
12
12
  export * from './SourceView';
13
13
  export * from './ProfileMetricsGraph';
14
+ export { default as ProfileFilters } from './ProfileView/components/ProfileFilters';
15
+ export { useProfileFiltersUrlState } from './ProfileView/components/ProfileFilters/useProfileFiltersUrlState';
14
16
  export declare const DEFAULT_PROFILE_EXPLORER_PARAM_VALUES: {
15
17
  dashboard_items: string;
16
18
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,eAAe,EAAE,EAAC,sBAAsB,EAAC,MAAM,mBAAmB,CAAC;AAC1E,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,YAAY,MAAM,yBAAyB,CAAC;AAEnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAC,qBAAqB,EAAC,MAAM,2DAA2D,CAAC;AAChG,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AAEtC,eAAO,MAAM,qCAAqC;;CAEjD,CAAC;AAEF,OAAO,EAAC,eAAe,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,YAAY,EAAE,aAAa,EAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,eAAe,EAAE,EAAC,sBAAsB,EAAC,MAAM,mBAAmB,CAAC;AAC1E,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,YAAY,MAAM,yBAAyB,CAAC;AAEnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAC,qBAAqB,EAAC,MAAM,2DAA2D,CAAC;AAChG,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AAEtC,OAAO,EAAC,OAAO,IAAI,cAAc,EAAC,MAAM,yCAAyC,CAAC;AAClF,OAAO,EAAC,yBAAyB,EAAC,MAAM,mEAAmE,CAAC;AAE5G,eAAO,MAAM,qCAAqC;;CAEjD,CAAC;AAEF,OAAO,EAAC,eAAe,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,YAAY,EAAE,aAAa,EAAC,CAAC"}
package/dist/index.js CHANGED
@@ -23,6 +23,8 @@ export * from './utils';
23
23
  export * from './ProfileTypeSelector';
24
24
  export * from './SourceView';
25
25
  export * from './ProfileMetricsGraph';
26
+ export { default as ProfileFilters } from './ProfileView/components/ProfileFilters';
27
+ export { useProfileFiltersUrlState } from './ProfileView/components/ProfileFilters/useProfileFiltersUrlState';
26
28
  export const DEFAULT_PROFILE_EXPLORER_PARAM_VALUES = {
27
29
  dashboard_items: 'flamegraph',
28
30
  };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.19.35",
3
+ "version": "0.19.36",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@floating-ui/react": "^0.27.12",
7
7
  "@headlessui/react": "^1.7.19",
8
8
  "@iconify/react": "^4.0.0",
9
9
  "@parca/client": "0.17.3",
10
- "@parca/components": "0.16.356",
10
+ "@parca/components": "0.16.357",
11
11
  "@parca/dynamicsize": "0.16.65",
12
12
  "@parca/hooks": "0.0.99",
13
13
  "@parca/icons": "0.16.72",
@@ -78,5 +78,5 @@
78
78
  "access": "public",
79
79
  "registry": "https://registry.npmjs.org/"
80
80
  },
81
- "gitHead": "c0da0b21aecf50944c7502483b8216ea11634ef2"
81
+ "gitHead": "27c6a55306acdd0bedc8d2dbd224793d2df46ec8"
82
82
  }
@@ -168,7 +168,11 @@ const numberMatchTypeItems: SelectItem[] = [
168
168
  },
169
169
  ];
170
170
 
171
- const ProfileFilters = (): JSX.Element => {
171
+ export interface ProfileFiltersProps {
172
+ readOnly?: boolean;
173
+ }
174
+
175
+ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Element => {
172
176
  const {profileSource} = useProfileViewContext();
173
177
  const currentProfileType = profileSource?.ProfileType()?.toString();
174
178
  const filterTypeItems = getFilterTypeItems(currentProfileType);
@@ -213,6 +217,7 @@ const ProfileFilters = (): JSX.Element => {
213
217
  items={filterTypeItems}
214
218
  selectedKey={filter.type}
215
219
  placeholder="Select Filter"
220
+ disabled={readOnly}
216
221
  onSelection={key => {
217
222
  // Check if this is a preset selection
218
223
  if (isPresetKey(key)) {
@@ -246,10 +251,13 @@ const ProfileFilters = (): JSX.Element => {
246
251
  }
247
252
  }}
248
253
  className={cx(
249
- 'rounded-l-md pr-1 gap-0 focus:z-50 focus:relative focus:outline-1',
250
- isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none',
251
- filter.type != null ? 'border-r-0 w-auto' : 'w-32'
254
+ 'gap-0 focus:z-50 focus:relative focus:outline-1',
255
+ readOnly ? '' : 'pr-1',
256
+ readOnly && isPresetFilter ? 'rounded-md' : 'rounded-l-md rounded-r-none',
257
+ !readOnly && (isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none'),
258
+ readOnly ? 'w-auto' : filter.type != null ? 'border-r-0 w-auto' : 'w-32'
252
259
  )}
260
+ hideCaretDropdown={readOnly}
253
261
  />
254
262
 
255
263
  {filter.type != null && !isPresetFilter && (
@@ -257,6 +265,7 @@ const ProfileFilters = (): JSX.Element => {
257
265
  <Select
258
266
  items={fieldItems}
259
267
  selectedKey={filter.field ?? ''}
268
+ disabled={readOnly}
260
269
  onSelection={key => {
261
270
  const newField = key as ProfileFilter['field'];
262
271
  const isNewFieldNumber = newField === 'address' || newField === 'line_number';
@@ -272,21 +281,31 @@ const ProfileFilters = (): JSX.Element => {
272
281
  updateFilter(filter.id, {field: newField});
273
282
  }
274
283
  }}
275
- className="rounded-none border-r-0 w-32 pr-1 gap-0 focus:z-50 focus:relative focus:outline-1"
284
+ className={cx(
285
+ 'rounded-none border-r-0 w-32 gap-0 focus:z-50 focus:relative focus:outline-1',
286
+ readOnly ? '' : 'pr-1'
287
+ )}
288
+ hideCaretDropdown={readOnly}
276
289
  />
277
290
 
278
291
  <Select
279
292
  items={matchTypeItems}
280
293
  selectedKey={filter.matchType ?? ''}
294
+ disabled={readOnly}
281
295
  onSelection={key =>
282
296
  updateFilter(filter.id, {matchType: key as ProfileFilter['matchType']})
283
297
  }
284
- className="rounded-none border-r-0 pr-1 gap-0 focus:z-50 focus:relative focus:outline-1"
298
+ className={cx(
299
+ 'rounded-none border-r-0 gap-0 focus:z-50 focus:relative focus:outline-1',
300
+ readOnly ? '' : 'pr-1'
301
+ )}
302
+ hideCaretDropdown={readOnly}
285
303
  />
286
304
 
287
305
  <Input
288
306
  placeholder="Value"
289
307
  value={filter.value}
308
+ disabled={readOnly}
290
309
  onChange={e => updateFilter(filter.id, {value: e.target.value})}
291
310
  onKeyDown={handleKeyDown}
292
311
  className="rounded-none w-36 text-sm focus:outline-1"
@@ -294,40 +313,44 @@ const ProfileFilters = (): JSX.Element => {
294
313
  </>
295
314
  )}
296
315
 
297
- <Button
298
- variant="neutral"
299
- onClick={() => {
300
- // If we're displaying local filters and this is the last one, reset everything
301
- if (localFilters.length > 0 && localFilters.length === 1) {
302
- resetFilters();
303
- }
304
- // If we're displaying applied filters and this is the last one, reset everything
305
- else if (localFilters.length === 0 && filtersToRender.length === 1) {
306
- resetFilters();
307
- }
308
- // Otherwise, just remove this specific filter
309
- else {
310
- removeFilter(filter.id);
311
- }
312
- }}
313
- className={cx(
314
- 'h-[38px] p-3',
315
- filter.type != null ? 'rounded-none rounded-r-md' : 'rounded-l-none rounded-r-md'
316
- )}
317
- >
318
- <Icon icon="mdi:close" className="h-4 w-4" />
319
- </Button>
316
+ {!readOnly && (
317
+ <Button
318
+ variant="neutral"
319
+ onClick={() => {
320
+ // If we're displaying local filters and this is the last one, reset everything
321
+ if (localFilters.length > 0 && localFilters.length === 1) {
322
+ resetFilters();
323
+ }
324
+ // If we're displaying applied filters and this is the last one, reset everything
325
+ else if (localFilters.length === 0 && filtersToRender.length === 1) {
326
+ resetFilters();
327
+ }
328
+ // Otherwise, just remove this specific filter
329
+ else {
330
+ removeFilter(filter.id);
331
+ }
332
+ }}
333
+ className={cx(
334
+ 'h-[38px] p-3',
335
+ filter.type != null
336
+ ? 'rounded-none rounded-r-md'
337
+ : 'rounded-l-none rounded-r-md'
338
+ )}
339
+ >
340
+ <Icon icon="mdi:close" className="h-4 w-4" />
341
+ </Button>
342
+ )}
320
343
  </div>
321
344
  );
322
345
  })}
323
346
 
324
- {localFilters.length > 0 && (
347
+ {!readOnly && localFilters.length > 0 && (
325
348
  <Button variant="neutral" onClick={addFilter} className="p-3 h-[38px]">
326
349
  <Icon icon="mdi:filter-plus-outline" className="h-4 w-4" />
327
350
  </Button>
328
351
  )}
329
352
 
330
- {localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (
353
+ {!readOnly && localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (
331
354
  <Button variant="neutral" onClick={addFilter} className="flex items-center gap-2">
332
355
  <Icon icon="mdi:filter-outline" className="h-4 w-4" />
333
356
  <span>Filter</span>
@@ -335,7 +358,7 @@ const ProfileFilters = (): JSX.Element => {
335
358
  )}
336
359
  </div>
337
360
 
338
- {localFilters.length > 0 && (
361
+ {!readOnly && localFilters.length > 0 && (
339
362
  <Button
340
363
  variant="primary"
341
364
  onClick={onApplyFilters}
package/src/index.tsx CHANGED
@@ -26,6 +26,9 @@ export * from './ProfileTypeSelector';
26
26
  export * from './SourceView';
27
27
  export * from './ProfileMetricsGraph';
28
28
 
29
+ export {default as ProfileFilters} from './ProfileView/components/ProfileFilters';
30
+ export {useProfileFiltersUrlState} from './ProfileView/components/ProfileFilters/useProfileFiltersUrlState';
31
+
29
32
  export const DEFAULT_PROFILE_EXPLORER_PARAM_VALUES = {
30
33
  dashboard_items: 'flamegraph',
31
34
  };