@har-analyzer/components 0.0.11 → 0.0.13

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/README.md CHANGED
@@ -1,14 +1,20 @@
1
1
  # @har-analyzer/components
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@har-analyzer/components.svg?style=flat)](https://www.npmjs.com/package/@har-analyzer/components)
4
+ [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/@har-analyzer/components.svg?style=flat)](https://bundlephobia.com/package/@har-analyzer/components)
5
+ [![build status](https://img.shields.io/github/actions/workflow/status/theallanjoshua/har-analyzer/publish-har-analyzer-components.yml?branch=main&style=flat)](https://github.com/theallanjoshua/har-analyzer/actions)
6
+
7
+
3
8
  Reusable React components for HAR Analyzer.
4
9
 
5
10
  ## ✨ Features
6
11
 
7
- - 📦 **Drop-in Dashboard**: Use the full `<HARAnalyzer />` component for a complete, ready-to-run HTTP Archive visualization tool, or compose individual panels.
12
+ - 📦 **Drop-in Dashboard**: Use the full `<HAREntriesViewer />` component for a complete, ready-to-run HTTP Archive visualization tool, or compose individual panels.
13
+ - ⚖️ **Entry Comparison**: Compare multiple HAR entries side-by-side to easily spot differences in headers, payloads, and responses by simply selecting multiple rows in the network table.
8
14
  - 🔍 **Advanced Filtering**: Filter network entries quickly by content types (JSON, HTML, JS, CSS, etc.) or error statuses via `<HAREntriesFilters />`.
9
15
  - 🕵️ **Deep Inspection**: Inspect detailed request/response headers, decoded payloads, and syntax-highlighted response bodies with `<ViewHAREntry />`.
10
- - 🛠️ **Persistent Preferences**: Context providers remember user preferences like themes (dark/light mode), table column layouts, and dashboard layouts effortlessly via the `<HARAnalyzerPreferencesProvider />`.
11
- - 🎨 **Modern UI**: Built on top of the [Cloudscape Design System](https://cloudscape.design/).
16
+ - 🛠️ **Persistent Preferences**: Context providers remember user preferences like themes (dark/light mode) and table column layouts effortlessly via the `<HARAnalyzerPreferencesProvider />`.
17
+ - 🎨 **Cloudscape UI**: Built on top of the [Cloudscape Design System](https://cloudscape.design/).
12
18
  - ⌨️ **Type Safe**: Fully written in TypeScript with exported utility types (`HARContent`, `HAREntry`) for standard HTTP Archive structures.
13
19
 
14
20
  ## Installation
@@ -144,7 +150,7 @@ function App() {
144
150
  return (
145
151
  <ListHAREntries
146
152
  harEntries={yourHarEntriesArray}
147
- onChange={(entry) => console.log('Selected:', entry)}
153
+ onChange={(entries) => console.log('Selected:', entries)}
148
154
  />
149
155
  );
150
156
  }
@@ -152,10 +158,10 @@ function App() {
152
158
 
153
159
  **Props:**
154
160
 
155
- | Name | Type | Required | Description |
156
- |-----------|-----------|----------|--------------------------------------|
157
- | harEntries| HAREntry[]| Yes | The entries to display in the table. |
158
- | onChange | function | Yes | Callback when an entry is selected. |
161
+ | Name | Type | Required | Description |
162
+ |-----------|-------------------------------------------|----------|--------------------------------------|
163
+ | harEntries| HAREntry[] | Yes | The entries to display in the table. |
164
+ | onChange | (selectedHAREntries: HAREntry[]) => void | Yes | Callback when entries are selected. |
159
165
 
160
166
  ---
161
167
 
@@ -1,35 +1,35 @@
1
- import { jsx as o, jsxs as w } from "react/jsx-runtime";
2
- import { useState as I, useEffect as L, useCallback as $, useMemo as p } from "react";
3
- import O from "@cloudscape-design/board-components/board";
4
- import z from "@cloudscape-design/board-components/board-item";
5
- import N from "@cloudscape-design/components/button";
6
- import B from "@cloudscape-design/components/header";
7
- import P from "../list-har-entries.js";
8
- import k from "../view-har-entry.js";
9
- import { useHARAnalyzerPreferences as y } from "../har-analyzer-preferences.js";
10
- import { d as v, b as x, a as U } from "./har.js";
11
- import j from "@cloudscape-design/components/space-between";
12
- import M from "@cloudscape-design/components/multiselect";
13
- import { C as V } from "./content-type.js";
14
- import W from "@cloudscape-design/components/segmented-control";
15
- function Y({ children: e, ...n }) {
16
- return /* @__PURE__ */ o(j, { direction: "horizontal", size: "s", ...n, children: e });
1
+ import { jsx as o, jsxs as x } from "react/jsx-runtime";
2
+ import { useState as y, useMemo as F } from "react";
3
+ import k from "@cloudscape-design/board-components/board";
4
+ import U from "@cloudscape-design/board-components/board-item";
5
+ import M from "@cloudscape-design/components/button";
6
+ import j from "@cloudscape-design/components/header";
7
+ import Y from "../list-har-entries.js";
8
+ import G from "../view-har-entry.js";
9
+ import { d as f, b as V, a as W } from "./har.js";
10
+ import q from "@cloudscape-design/components/space-between";
11
+ import { useHARAnalyzerPreferences as H } from "../har-analyzer-preferences.js";
12
+ import J from "@cloudscape-design/components/multiselect";
13
+ import { C as K } from "./content-type.js";
14
+ import Q from "@cloudscape-design/components/segmented-control";
15
+ function X({ children: e, ...n }) {
16
+ return /* @__PURE__ */ o(q, { direction: "horizontal", size: "s", ...n, children: e });
17
17
  }
18
- const G = {
18
+ const Z = {
19
19
  dragHandleAriaLabel: "Drag handle",
20
20
  dragHandleAriaDescription: "Use Space or Enter to activate drag, arrow keys to move, Space or Enter to submit, or Escape to discard. Be sure to temporarily disable any screen reader navigation feature that may interfere with the functionality of the arrow keys.",
21
21
  resizeHandleAriaLabel: "Resize handle",
22
22
  resizeHandleAriaDescription: "Use Space or Enter to activate resize, arrow keys to move, Space or Enter to submit, or Escape to discard. Be sure to temporarily disable any screen reader navigation feature that may interfere with the functionality of the arrow keys."
23
23
  };
24
- function u(e, n, t) {
24
+ function p(e, n, t) {
25
25
  const r = n.length > 0 ? `Conflicts with ${n.map((s) => s.id).join(", ")}.` : "", i = t.length > 0 ? `Disturbed ${t.length} items.` : "";
26
26
  return [e, r, i].filter(Boolean).join(" ");
27
27
  }
28
- const q = {
28
+ const ee = {
29
29
  liveAnnouncementDndStarted: (e) => e === "resize" ? "Resizing" : "Dragging",
30
30
  liveAnnouncementDndItemReordered: (e) => {
31
31
  const n = `column ${e.placement.x + 1}`, t = `row ${e.placement.y + 1}`;
32
- return u(
32
+ return p(
33
33
  `Item moved to ${e.direction === "horizontal" ? n : t}.`,
34
34
  e.conflicts,
35
35
  e.disturbed
@@ -37,20 +37,20 @@ const q = {
37
37
  },
38
38
  liveAnnouncementDndItemResized: (e) => {
39
39
  const n = e.isMinimalColumnsReached ? " (minimal)" : "", t = e.isMinimalRowsReached ? " (minimal)" : "", r = e.direction === "horizontal" ? `columns ${e.placement.width}${n}` : `rows ${e.placement.height}${t}`;
40
- return u(`Item resized to ${r}.`, e.conflicts, e.disturbed);
40
+ return p(`Item resized to ${r}.`, e.conflicts, e.disturbed);
41
41
  },
42
42
  liveAnnouncementDndItemInserted: (e) => {
43
43
  const n = `column ${e.placement.x + 1}`, t = `row ${e.placement.y + 1}`;
44
- return u(`Item inserted to ${n}, ${t}.`, e.conflicts, e.disturbed);
44
+ return p(`Item inserted to ${n}, ${t}.`, e.conflicts, e.disturbed);
45
45
  },
46
46
  liveAnnouncementDndCommitted: (e) => `${e} committed`,
47
47
  liveAnnouncementDndDiscarded: (e) => `${e} discarded`,
48
- liveAnnouncementItemRemoved: (e) => u(`Removed item ${e.item.id}.`, [], e.disturbed),
48
+ liveAnnouncementItemRemoved: (e) => p(`Removed item ${e.item.id}.`, [], e.disturbed),
49
49
  navigationAriaLabel: "Board navigation",
50
50
  navigationAriaDescription: "Click on non-empty item to move focus over",
51
51
  navigationItemAriaLabel: (e) => e ? e.id : "Empty"
52
52
  };
53
- function J(e) {
53
+ function te(e) {
54
54
  const {
55
55
  components: n,
56
56
  definitions: t,
@@ -58,25 +58,25 @@ function J(e) {
58
58
  empty: i
59
59
  } = e;
60
60
  return /* @__PURE__ */ o(
61
- O,
61
+ k,
62
62
  {
63
- i18nStrings: q,
63
+ i18nStrings: ee,
64
64
  items: t,
65
- renderItem: ({ id: s }, { removeItem: a }) => {
65
+ renderItem: ({ id: s }, { removeItem: d }) => {
66
66
  const {
67
- title: l,
68
- content: E,
69
- actions: f,
70
- counter: d,
71
- canRemove: m
67
+ title: h,
68
+ content: u,
69
+ actions: A,
70
+ counter: R,
71
+ canRemove: E
72
72
  } = n[s] ?? {};
73
73
  return /* @__PURE__ */ o(
74
- z,
74
+ U,
75
75
  {
76
- header: /* @__PURE__ */ o(B, { counter: d, actions: f, children: l }),
77
- i18nStrings: G,
78
- settings: m && /* @__PURE__ */ o(N, { variant: "icon", iconName: "close", onClick: a }),
79
- children: E
76
+ header: /* @__PURE__ */ o(j, { counter: R, actions: A, children: h }),
77
+ i18nStrings: Z,
78
+ settings: E && /* @__PURE__ */ o(M, { variant: "icon", iconName: "close", onClick: d }),
79
+ children: u
80
80
  }
81
81
  );
82
82
  },
@@ -87,123 +87,124 @@ function J(e) {
87
87
  }
88
88
  );
89
89
  }
90
- function K(e, n) {
91
- return y(`boardDefinitionsPreference_${e}`, n);
90
+ function w() {
91
+ return H("shouldFilterErrors", !1);
92
92
  }
93
- function S() {
94
- return y("shouldFilterErrors", !1);
93
+ const ne = [];
94
+ function b() {
95
+ return H("contentTypeFilters", ne);
95
96
  }
96
- const Q = [];
97
- function T() {
98
- return y("contentTypeFilters", Q);
99
- }
100
- const D = V.map((e) => ({
97
+ const S = K.map((e) => ({
101
98
  value: e,
102
99
  label: e
103
100
  }));
104
- function X({ contentTypeFilters: e, onChange: n }) {
105
- const t = D.filter(
101
+ function re({ contentTypeFilters: e, onChange: n }) {
102
+ const t = S.filter(
106
103
  ({ value: i }) => e.includes(i)
107
104
  ), r = (i) => {
108
- const s = i.map(({ value: a }) => a);
105
+ const s = i.map(({ value: d }) => d);
109
106
  n(s);
110
107
  };
111
108
  return /* @__PURE__ */ o(
112
- M,
109
+ J,
113
110
  {
114
111
  placeholder: "Filter by content type",
115
112
  selectedOptions: t,
116
113
  onChange: ({ detail: i }) => {
117
114
  r(i.selectedOptions);
118
115
  },
119
- options: D
116
+ options: S
120
117
  }
121
118
  );
122
119
  }
123
- const A = "ERRORS", Z = "ALL";
124
- function ee({ shouldFilterErrors: e, onChange: n }) {
120
+ const I = "ERRORS", oe = "ALL";
121
+ function ie({ shouldFilterErrors: e, onChange: n }) {
125
122
  return /* @__PURE__ */ o(
126
- W,
123
+ Q,
127
124
  {
128
125
  options: [
129
126
  { text: "All", id: "ALL" },
130
- { text: "Errors", id: A }
127
+ { text: "Errors", id: I }
131
128
  ],
132
- selectedId: e ? A : Z,
129
+ selectedId: e ? I : oe,
133
130
  onChange: ({ detail: t }) => {
134
- const r = t.selectedId === A;
131
+ const r = t.selectedId === I;
135
132
  n(r);
136
133
  }
137
134
  }
138
135
  );
139
136
  }
140
- function te() {
141
- const [e, n] = T(), [t, r] = S();
142
- return /* @__PURE__ */ w(Y, { children: [
143
- /* @__PURE__ */ o(X, { contentTypeFilters: e, onChange: n }),
144
- /* @__PURE__ */ o(ee, { shouldFilterErrors: t, onChange: r })
137
+ function se() {
138
+ const [e, n] = b(), [t, r] = w();
139
+ return /* @__PURE__ */ x(X, { children: [
140
+ /* @__PURE__ */ o(re, { contentTypeFilters: e, onChange: n }),
141
+ /* @__PURE__ */ o(ie, { shouldFilterErrors: t, onChange: r })
145
142
  ] });
146
143
  }
147
- const C = 4, F = "har-entries-viewer", H = "selected-har-entry-viewer", _ = [
144
+ const $ = {
145
+ rowSpan: 4,
146
+ columnSpan: 12
147
+ }, T = "har-entries-viewer", ce = [
148
148
  {
149
- id: F,
150
- rowSpan: C,
151
- columnSpan: 12
152
- }
153
- ], ne = [
154
- ..._,
155
- {
156
- id: H,
157
- rowSpan: C,
158
- columnSpan: 12
149
+ ...$,
150
+ id: T
159
151
  }
160
152
  ];
161
- function Ae(e) {
162
- const { title: n = "HAR Entries", harEntries: t } = e, [r, i] = I(), [s, a] = I(_);
163
- L(() => {
164
- r && (t.map(v).includes(v(r)) || i(void 0));
165
- }, [t, r]);
166
- const [l, E] = K(
167
- "har-contents-viewer",
168
- ne
169
- ), f = (c) => {
170
- a(c), c.length > 1 && E(c);
171
- }, d = $((c) => {
172
- i(c), s.length === 1 && a(l);
173
- }, [l, s]), m = p(() => x(t), [t]), [R] = T(), [g] = S(), h = p(() => U(g ? m : t, R), [g, m, t, R]), b = p(() => ({
174
- [F]: {
153
+ function ve(e) {
154
+ const { title: n = "HAR Entries", harEntries: t } = e, [r, i] = y(t), [s, d] = y([]), [h, u] = y(ce);
155
+ if (t !== r) {
156
+ i(t);
157
+ const c = t.map(f), a = s.filter((m) => c.includes(f(m)));
158
+ a.length !== s.length && d(a);
159
+ }
160
+ const A = (c) => {
161
+ u(c);
162
+ }, R = (c) => {
163
+ d(c);
164
+ const a = c.map(f);
165
+ u((m) => {
166
+ const g = m.filter(({ id: l }) => a.includes(l) || l === T), z = g.map(({ id: l }) => l), O = a.filter((l) => !z.includes(l)), { rowSpan: B, columnSpan: N } = g.at(1) ?? $, P = O.map((l) => ({
167
+ rowSpan: B,
168
+ columnSpan: N,
169
+ id: l
170
+ }));
171
+ return [...g, ...P];
172
+ });
173
+ }, E = F(() => V(r), [r]), [v] = b(), [D] = w(), C = F(() => W(D ? E : r, v), [D, E, r, v]), L = s.reduce((c, a) => {
174
+ const m = f(a);
175
+ return {
176
+ ...c,
177
+ [m]: {
178
+ title: "Details",
179
+ content: /* @__PURE__ */ o(G, { harEntry: a })
180
+ }
181
+ };
182
+ }, {}), _ = {
183
+ [T]: {
175
184
  title: n,
176
- counter: `(${h.length}/${t.length})`,
177
- actions: /* @__PURE__ */ o(te, {}),
185
+ counter: `(${C.length}/${r.length})`,
186
+ actions: /* @__PURE__ */ o(se, {}),
178
187
  content: /* @__PURE__ */ o(
179
- P,
188
+ Y,
180
189
  {
181
- harEntries: h,
182
- onChange: d
190
+ harEntries: C,
191
+ onChange: R
183
192
  }
184
193
  )
185
194
  },
186
- [H]: r ? {
187
- title: "Details",
188
- content: /* @__PURE__ */ o(k, { harEntry: r }),
189
- canRemove: !0
190
- } : {
191
- title: "Select an entry to see details",
192
- content: null,
193
- canRemove: !0
194
- }
195
- }), [h, t, d, r, n]);
195
+ ...L
196
+ };
196
197
  return /* @__PURE__ */ o(
197
- J,
198
+ te,
198
199
  {
199
- components: b,
200
- definitions: s,
201
- onDefinitionsChange: f
200
+ components: _,
201
+ definitions: h,
202
+ onDefinitionsChange: A
202
203
  }
203
204
  );
204
205
  }
205
206
  export {
206
- Ae as H,
207
- te as a,
208
- Y as b
207
+ ve as H,
208
+ se as a,
209
+ X as b
209
210
  };
@@ -2,7 +2,7 @@ import { HAREntry } from '../../utils/har';
2
2
  interface ListHAREntriesProps {
3
3
  id?: string;
4
4
  harEntries: HAREntry[];
5
- onChange: (selectedHAREntry: HAREntry) => void;
5
+ onChange: (selectedHAREntries: HAREntry[]) => void;
6
6
  }
7
7
  export default function ListHAREntries({ id, harEntries, onChange, }: ListHAREntriesProps): import("react/jsx-runtime").JSX.Element;
8
8
  export {};
@@ -2118,11 +2118,8 @@ function tr({
2118
2118
  items: t,
2119
2119
  getRowId: Ue,
2120
2120
  empty: "No HAR entries found",
2121
- selectionType: "single",
2122
- onSelectionChange: (a) => {
2123
- const i = a[0];
2124
- i && n(i);
2125
- }
2121
+ selectionType: "multi",
2122
+ onSelectionChange: n
2126
2123
  }
2127
2124
  );
2128
2125
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@har-analyzer/components",
3
3
  "type": "module",
4
- "version": "0.0.11",
4
+ "version": "0.0.13",
5
5
  "description": "Reusable React components for HAR Analyzer",
6
6
  "author": "Allan Joshua",
7
7
  "license": "MIT",
@@ -1,2 +0,0 @@
1
- import { EnhancedBoardProps } from '../components/enhanced-board';
2
- export default function useBoardDefinitionsPreference(boardId: string, defaultDefinitionsPreference: EnhancedBoardProps['definitions']): readonly [import('../components/enhanced-board/constants/i18n').EnhancedBoardDefinitions, (newPreference: import('../components/enhanced-board/constants/i18n').EnhancedBoardDefinitions) => void];