chrome-devtools-frontend 1.0.1555174 → 1.0.1555430

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 (66) hide show
  1. package/front_end/core/protocol_client/InspectorBackend.ts +1 -1
  2. package/front_end/core/root/Runtime.ts +0 -4
  3. package/front_end/core/sdk/DOMModel.ts +101 -7
  4. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
  5. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  6. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +1 -1
  7. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -1
  8. package/front_end/{ui/components → models}/annotations/AnnotationRepository.ts +3 -3
  9. package/front_end/models/annotations/README.md +7 -0
  10. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +8 -0
  11. package/front_end/models/stack_trace/StackTrace.ts +13 -2
  12. package/front_end/models/stack_trace/StackTraceImpl.ts +81 -6
  13. package/front_end/models/stack_trace/StackTraceModel.ts +35 -3
  14. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +45 -4
  15. package/front_end/panels/ai_assistance/components/ArtifactsViewer.ts +57 -0
  16. package/front_end/panels/ai_assistance/components/ChatView.ts +1 -0
  17. package/front_end/panels/ai_assistance/components/artifactsViewer.css +10 -0
  18. package/front_end/panels/application/preloading/PreloadingView.ts +12 -6
  19. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +230 -237
  20. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +96 -79
  21. package/front_end/panels/application/preloading/components/preloadingGrid.css +26 -29
  22. package/front_end/panels/application/preloading/preloadingView.css +6 -0
  23. package/front_end/panels/common/Annotation.ts +1 -1
  24. package/front_end/panels/common/AnnotationManager.ts +1 -1
  25. package/front_end/panels/common/ExtensionView.ts +1 -0
  26. package/front_end/panels/console/ConsoleContextSelector.ts +74 -9
  27. package/front_end/panels/console/consoleContextSelector.css +31 -29
  28. package/front_end/panels/coverage/coverageListView.css +59 -57
  29. package/front_end/panels/elements/ElementsPanel.ts +1 -1
  30. package/front_end/panels/elements/ElementsTreeElement.ts +39 -1
  31. package/front_end/panels/elements/ElementsTreeOutline.ts +23 -21
  32. package/front_end/panels/elements/TopLayerContainer.ts +26 -91
  33. package/front_end/panels/explain/components/ConsoleInsight.ts +3 -3
  34. package/front_end/panels/network/NetworkItemView.ts +1 -1
  35. package/front_end/panels/network/NetworkLogView.ts +1 -1
  36. package/front_end/panels/network/NetworkPanel.ts +1 -1
  37. package/front_end/panels/recorder/RecorderController.ts +0 -1
  38. package/front_end/panels/security/SecurityPanelSidebar.ts +5 -0
  39. package/front_end/panels/timeline/TimelineUIUtils.ts +5 -8
  40. package/front_end/panels/timeline/components/TimelineSummary.ts +75 -54
  41. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +16 -25
  42. package/front_end/panels/timeline/components/insights/Cache.ts +12 -8
  43. package/front_end/panels/timeline/components/insights/DOMSize.ts +25 -21
  44. package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +7 -7
  45. package/front_end/panels/timeline/components/insights/FontDisplay.ts +7 -5
  46. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +11 -9
  47. package/front_end/panels/timeline/components/insights/INPBreakdown.ts +7 -6
  48. package/front_end/panels/timeline/components/insights/ImageDelivery.ts +7 -5
  49. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +20 -18
  50. package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +12 -12
  51. package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +7 -7
  52. package/front_end/panels/timeline/components/insights/ModernHTTP.ts +7 -5
  53. package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +15 -13
  54. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +2 -2
  55. package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +15 -14
  56. package/front_end/panels/timeline/components/insights/Table.ts +152 -130
  57. package/front_end/panels/timeline/components/insights/ThirdParties.ts +11 -9
  58. package/front_end/panels/timeline/components/timelineSummary.css +58 -57
  59. package/front_end/panels/timeline/thirdPartyTreeView.css +109 -0
  60. package/front_end/panels/timeline/timelineDetailsView.css +2 -4
  61. package/front_end/panels/timeline/timelinePanel.css +0 -110
  62. package/front_end/ui/legacy/TabbedPane.ts +1 -1
  63. package/front_end/ui/legacy/ViewManager.ts +2 -32
  64. package/package.json +1 -1
  65. /package/front_end/{ui/components → models}/annotations/AnnotationType.ts +0 -0
  66. /package/front_end/{ui/components → models}/annotations/annotations.ts +0 -0
@@ -6,15 +6,17 @@ import './Table.js';
6
6
 
7
7
  import type {ModernHTTPInsightModel} from '../../../../models/trace/insights/ModernHTTP.js';
8
8
  import * as Trace from '../../../../models/trace/trace.js';
9
+ import * as UI from '../../../../ui/legacy/legacy.js';
9
10
  import * as Lit from '../../../../ui/lit/lit.js';
10
11
 
11
12
  import {BaseInsightComponent} from './BaseInsightComponent.js';
12
13
  import {eventRef} from './EventRef.js';
13
- import {createLimitedRows, renderOthersLabel, type TableData, type TableDataRow} from './Table.js';
14
+ import {createLimitedRows, renderOthersLabel, Table, type TableDataRow} from './Table.js';
14
15
 
15
16
  const {UIStrings, i18nString, createOverlayForRequest} = Trace.Insights.Models.ModernHTTP;
16
17
 
17
18
  const {html} = Lit;
19
+ const {widgetConfig} = UI.Widget;
18
20
 
19
21
  export class ModernHTTP extends BaseInsightComponent<ModernHTTPInsightModel> {
20
22
  override internalName = 'modern-http';
@@ -56,13 +58,13 @@ export class ModernHTTP extends BaseInsightComponent<ModernHTTPInsightModel> {
56
58
  // clang-format off
57
59
  return html`
58
60
  <div class="insight-section">
59
- <devtools-performance-table
60
- .data=${{
61
+ <devtools-widget .widgetConfig=${widgetConfig(Table, {
62
+ data: {
61
63
  insight: this,
62
64
  headers: [i18nString(UIStrings.request), i18nString(UIStrings.protocol)],
63
65
  rows,
64
- } as TableData}>
65
- </devtools-performance-table>
66
+ }})}>
67
+ </devtools-widget>
66
68
  </div>`;
67
69
  // clang-format on
68
70
  }
@@ -10,6 +10,7 @@ import type {
10
10
  CriticalRequestNode, NetworkDependencyTreeInsightModel} from
11
11
  '../../../../models/trace/insights/NetworkDependencyTree.js';
12
12
  import * as Trace from '../../../../models/trace/trace.js';
13
+ import * as UI from '../../../../ui/legacy/legacy.js';
13
14
  import * as Lit from '../../../../ui/lit/lit.js';
14
15
 
15
16
  import {BaseInsightComponent} from './BaseInsightComponent.js';
@@ -17,11 +18,12 @@ import {eventRef} from './EventRef.js';
17
18
  import {md} from './Helpers.js';
18
19
  import networkDependencyTreeInsightStyles from './networkDependencyTreeInsight.css.js';
19
20
  import {nodeLink} from './NodeLink.js';
20
- import {renderOthersLabel, type TableData, type TableDataRow} from './Table.js';
21
+ import {renderOthersLabel, Table, type TableDataRow} from './Table.js';
21
22
 
22
23
  const {UIStrings, i18nString} = Trace.Insights.Models.NetworkDependencyTree;
23
24
 
24
25
  const {html} = Lit;
26
+ const {widgetConfig} = UI.Widget;
25
27
 
26
28
  export const MAX_CHAINS_TO_SHOW = 5;
27
29
 
@@ -115,13 +117,13 @@ export class NetworkDependencyTree extends BaseInsightComponent<NetworkDependenc
115
117
 
116
118
  // clang-format off
117
119
  return html`
118
- <devtools-performance-table
119
- .data=${{
120
+ <devtools-widget .widgetConfig=${widgetConfig(Table, {
121
+ data: {
120
122
  insight: this,
121
123
  headers: [i18nString(UIStrings.columnRequest), i18nString(UIStrings.columnTime)],
122
124
  rows,
123
- } as TableData}>
124
- </devtools-performance-table>
125
+ }})}>
126
+ </devtools-widget>
125
127
  `;
126
128
  // clang-format on
127
129
  }
@@ -240,13 +242,13 @@ export class NetworkDependencyTree extends BaseInsightComponent<NetworkDependenc
240
242
  <div class="insight-section">
241
243
  ${preconnectOriginsTableTitle}
242
244
  ${this.#renderTooManyPreconnectsWarning()}
243
- <devtools-performance-table
244
- .data=${{
245
+ <devtools-widget .widgetConfig=${widgetConfig(Table, {
246
+ data: {
245
247
  insight: this,
246
248
  headers: [i18nString(UIStrings.columnOrigin), i18nString(UIStrings.columnSource)],
247
249
  rows,
248
- } as TableData}>
249
- </devtools-performance-table>
250
+ }})}>
251
+ </devtools-widget>
250
252
  </div>
251
253
  `;
252
254
  // clang-format on
@@ -283,13 +285,13 @@ export class NetworkDependencyTree extends BaseInsightComponent<NetworkDependenc
283
285
  return html`
284
286
  <div class="insight-section">
285
287
  ${estSavingTableTitle}
286
- <devtools-performance-table
287
- .data=${{
288
+ <devtools-widget .widgetConfig=${widgetConfig(Table, {
289
+ data: {
288
290
  insight: this,
289
291
  headers: [i18nString(UIStrings.columnOrigin), i18nString(UIStrings.columnWastedMs)],
290
292
  rows,
291
- } as TableData}>
292
- </devtools-performance-table>
293
+ }})}>
294
+ </devtools-widget>
293
295
  </div>
294
296
  `;
295
297
  // clang-format on
@@ -58,13 +58,13 @@ export class RenderBlocking extends BaseInsightComponent<RenderBlockingInsightMo
58
58
  // clang-format off
59
59
  return html`
60
60
  <div class="insight-section">
61
- <devtools-performance-table
61
+ <devtools-widget
62
62
  .data=${{
63
63
  insight: this,
64
64
  headers: [i18nString(UIStrings.renderBlockingRequest), i18nString(UIStrings.duration)],
65
65
  rows,
66
66
  }}>
67
- </devtools-performance-table>
67
+ </devtools-widget>
68
68
  </div>
69
69
  `;
70
70
  // clang-format on
@@ -2,7 +2,6 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import './Table.js';
6
5
  import '../../../../ui/components/linkifier/linkifier.js';
7
6
 
8
7
  import * as i18n from '../../../../core/i18n/i18n.js';
@@ -12,14 +11,16 @@ import type * as Protocol from '../../../../generated/protocol.js';
12
11
  import type {SlowCSSSelectorInsightModel} from '../../../../models/trace/insights/SlowCSSSelector.js';
13
12
  import * as Trace from '../../../../models/trace/trace.js';
14
13
  import type * as Linkifier from '../../../../ui/components/linkifier/linkifier.js';
14
+ import * as UI from '../../../../ui/legacy/legacy.js';
15
15
  import * as Lit from '../../../../ui/lit/lit.js';
16
16
 
17
17
  import {BaseInsightComponent} from './BaseInsightComponent.js';
18
- import type {TableData} from './Table.js';
18
+ import {Table} from './Table.js';
19
19
 
20
20
  const {UIStrings, i18nString} = Trace.Insights.Models.SlowCSSSelector;
21
21
 
22
22
  const {html} = Lit;
23
+ const {widgetConfig} = UI.Widget;
23
24
 
24
25
  export class SlowCSSSelector extends BaseInsightComponent<SlowCSSSelectorInsightModel> {
25
26
  override internalName = 'slow-css-selector';
@@ -106,8 +107,8 @@ export class SlowCSSSelector extends BaseInsightComponent<SlowCSSSelectorInsight
106
107
  // clang-format off
107
108
  const sections = [html`
108
109
  <div class="insight-section">
109
- <devtools-performance-table
110
- .data=${{
110
+ <devtools-widget .widgetConfig=${widgetConfig(Table, {
111
+ data: {
111
112
  insight: this,
112
113
  headers: [i18nString(UIStrings.total), ''],
113
114
  rows: [
@@ -115,8 +116,8 @@ export class SlowCSSSelector extends BaseInsightComponent<SlowCSSSelectorInsight
115
116
  {values: [i18nString(UIStrings.matchCount), this.model.totalMatchCount]},
116
117
  {values: [i18nString(UIStrings.elapsed), i18n.TimeUtilities.millisToString(this.model.totalElapsedMs)]},
117
118
  ],
118
- } as TableData}>
119
- </devtools-performance-table>
119
+ }})}>
120
+ </devtools-widget>
120
121
  </div>
121
122
  `];
122
123
  // clang-format on
@@ -126,14 +127,14 @@ export class SlowCSSSelector extends BaseInsightComponent<SlowCSSSelectorInsight
126
127
  // clang-format off
127
128
  sections.push(html`
128
129
  <div class="insight-section">
129
- <devtools-performance-table
130
- .data=${{
130
+ <devtools-widget .widgetConfig=${widgetConfig(Table, {
131
+ data: {
131
132
  insight: this,
132
133
  headers: [`${i18nString(UIStrings.topSelectorElapsedTime)}: ${time(Trace.Types.Timing.Micro(selector['elapsed (us)']))}`],
133
134
  rows: [{
134
135
  values: [html`${selector.selector} ${Lit.Directives.until(this.getSelectorLinks(cssModel, selector))}`]}]
135
- }} as TableData>
136
- </devtools-performance-table>
136
+ }})}>
137
+ </devtools-widget>
137
138
  </div>
138
139
  `);
139
140
  // clang-format on
@@ -144,15 +145,15 @@ export class SlowCSSSelector extends BaseInsightComponent<SlowCSSSelectorInsight
144
145
  // clang-format off
145
146
  sections.push(html`
146
147
  <div class="insight-section">
147
- <devtools-performance-table
148
- .data=${{
148
+ <devtools-widget .widgetConfig=${widgetConfig(Table, {
149
+ data: {
149
150
  insight: this,
150
151
  headers: [`${i18nString(UIStrings.topSelectorMatchAttempt)}: ${selector['match_attempts']}`],
151
152
  rows: [{
152
153
  values: [html`${selector.selector} ${Lit.Directives.until(this.getSelectorLinks(cssModel, selector))}` as unknown as string],
153
154
  }]
154
- }} as TableData}>
155
- </devtools-performance-table>
155
+ }})}>
156
+ </devtools-widget>
156
157
  </div>
157
158
  `);
158
159
  // clang-format on
@@ -1,11 +1,10 @@
1
1
  // Copyright 2024 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
- /* eslint-disable @devtools/no-lit-render-outside-of-view */
5
4
 
6
5
  import * as i18n from '../../../../core/i18n/i18n.js';
7
6
  import type * as Trace from '../../../../models/trace/trace.js';
8
- import * as ComponentHelpers from '../../../../ui/components/helpers/helpers.js';
7
+ import * as UI from '../../../../ui/legacy/legacy.js';
9
8
  import * as Lit from '../../../../ui/lit/lit.js';
10
9
 
11
10
  import type * as BaseInsightComponent from './BaseInsightComponent.js';
@@ -48,7 +47,7 @@ export interface TableState {
48
47
  selectionIsSticky: boolean;
49
48
  }
50
49
 
51
- export interface TableData {
50
+ interface TableData {
52
51
  insight: BaseInsightComponent;
53
52
  headers: string[];
54
53
  rows: TableDataRow[];
@@ -60,6 +59,11 @@ export interface TableDataRow {
60
59
  subRows?: TableDataRow[];
61
60
  }
62
61
 
62
+ interface FlattenedTableDataRow {
63
+ row: TableDataRow;
64
+ depth: number;
65
+ }
66
+
63
67
  export function renderOthersLabel(numOthers: number): string {
64
68
  return i18nString(UIStrings.others, {PH1: numOthers});
65
69
  }
@@ -94,113 +98,195 @@ export function createLimitedRows<T>(arr: T[], aggregator: RowLimitAggregator<T>
94
98
  return items;
95
99
  }
96
100
 
97
- export class Table extends HTMLElement {
98
- readonly #shadow = this.attachShadow({mode: 'open'});
101
+ interface ViewInput {
102
+ interactive: boolean;
103
+ headers: string[];
104
+ flattenedRows: FlattenedTableDataRow[];
105
+
106
+ onHoverRow: (row: TableDataRow, rowEl: HTMLElement) => void;
107
+ onClickRow: (row: TableDataRow, rowEl: HTMLElement) => void;
108
+ onMouseLeave: () => void;
109
+ }
110
+
111
+ type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
112
+
113
+ export const DEFAULT_VIEW: View = (input, output, target) => {
114
+ const {
115
+ interactive,
116
+ headers,
117
+ flattenedRows,
118
+ onHoverRow,
119
+ onClickRow,
120
+ onMouseLeave,
121
+ } = input;
122
+
123
+ const numColumns = headers.length;
124
+
125
+ function renderRow({row, depth}: FlattenedTableDataRow): Lit.TemplateResult {
126
+ const thStyles = Lit.Directives.styleMap({
127
+ paddingLeft: `calc(${depth} * var(--sys-size-5))`,
128
+ backgroundImage: `repeating-linear-gradient(
129
+ to right,
130
+ var(--sys-color-tonal-outline) 0 var(--sys-size-1),
131
+ transparent var(--sys-size-1) var(--sys-size-5)
132
+ )`,
133
+ backgroundPosition: '0 0',
134
+ backgroundRepeat: 'no-repeat',
135
+ backgroundSize: `calc(${depth} * var(--sys-size-5))`,
136
+ });
137
+ const trStyles = Lit.Directives.styleMap({
138
+ color: depth ? 'var(--sys-color-on-surface-subtle)' : '',
139
+ });
140
+ const columnEls = row.values.map(
141
+ (value, i) => i === 0 ? html`<th
142
+ scope="row"
143
+ colspan=${i === row.values.length - 1 ? numColumns - i : 1}
144
+ style=${thStyles}>${value}
145
+ </th>` :
146
+ html`<td>${value}</td>`);
147
+ return html`<tr style=${trStyles}>${columnEls}</tr>`;
148
+ }
149
+
150
+ const findRowAndEl = (el: HTMLElement): {row: TableDataRow, rowEl: HTMLElement} => {
151
+ const rowEl = el.closest('tr') as HTMLTableRowElement;
152
+ const row = flattenedRows[rowEl.sectionRowIndex].row;
153
+ return {row, rowEl};
154
+ };
155
+
156
+ // clang-format off
157
+ Lit.render(html`
158
+ <style>${tableStyles}</style>
159
+ <table
160
+ class=${Lit.Directives.classMap({
161
+ interactive,
162
+ })}
163
+ @mouseleave=${interactive ? onMouseLeave : null}>
164
+ <thead>
165
+ <tr>
166
+ ${headers.map(h => html`<th scope="col">${h}</th>`)}
167
+ </tr>
168
+ </thead>
169
+ <tbody
170
+ @mouseover=${interactive ? (e: Event) => {
171
+ const {row, rowEl} = findRowAndEl(e.target as HTMLElement);
172
+ onHoverRow(row, rowEl);
173
+ } : null}
174
+ @click=${interactive ? (e: Event) => {
175
+ const {row, rowEl} = findRowAndEl(e.target as HTMLElement);
176
+ onClickRow(row, rowEl);
177
+ } : null}
178
+ >${flattenedRows.map(renderRow)}</tbody>
179
+ </table>`,
180
+ target);
181
+ // clang-format on
182
+ };
183
+
184
+ export class Table extends UI.Widget.Widget {
185
+ #view: View;
99
186
  #insight?: BaseInsightComponent;
100
187
  #state?: TableState;
101
188
  #headers?: string[];
102
189
  /** The rows as given as by the user, which may include recursive rows via subRows. */
103
190
  #rows?: TableDataRow[];
104
191
  /** All rows/subRows, in the order that they appear visually. This is the result of traversing `#rows` and any subRows found. */
105
- #flattenedRows?: TableDataRow[];
192
+ #flattenedRows?: FlattenedTableDataRow[];
106
193
  #rowToParentRow = new Map<TableDataRow, TableDataRow>();
107
194
  #interactive = false;
108
- #currentHoverIndex: number|null = null;
195
+ #currentHoverRow: TableDataRow|null = null;
196
+
197
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
198
+ super(element, {useShadowDom: true});
199
+ this.#view = view;
200
+ }
109
201
 
110
202
  set data(data: TableData) {
111
203
  this.#insight = data.insight;
112
204
  this.#state = data.insight.sharedTableState;
113
205
  this.#headers = data.headers;
114
206
  this.#rows = data.rows;
207
+ this.#flattenedRows = this.#createFlattenedRows();
115
208
  // If this table isn't interactive, don't attach mouse listeners or use CSS :hover.
116
209
  this.#interactive = this.#rows.some(row => row.overlays || row.subRows?.length);
117
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
118
- }
119
-
120
- connectedCallback(): void {
121
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
210
+ this.requestUpdate();
122
211
  }
123
212
 
124
- #onHoverRow(e: MouseEvent): void {
125
- if (!this.#flattenedRows) {
126
- return;
213
+ #createFlattenedRows(): FlattenedTableDataRow[] {
214
+ if (!this.#rows) {
215
+ return [];
127
216
  }
128
217
 
129
- if (!(e.target instanceof HTMLElement)) {
130
- return;
218
+ const rowToParentRow = this.#rowToParentRow;
219
+ rowToParentRow.clear();
220
+
221
+ const flattenedRows: FlattenedTableDataRow[] = [];
222
+ function traverse(parent: TableDataRow|null, row: TableDataRow, depth = 0): void {
223
+ if (parent) {
224
+ rowToParentRow.set(row, parent);
225
+ }
226
+
227
+ flattenedRows.push({depth, row});
228
+
229
+ for (const subRow of row.subRows ?? []) {
230
+ traverse(row, subRow, depth + 1);
231
+ }
131
232
  }
132
233
 
133
- const rowEl = e.target.closest('tr');
134
- if (!rowEl?.parentElement) {
135
- return;
234
+ for (const row of this.#rows) {
235
+ traverse(null, row);
136
236
  }
137
237
 
138
- const rowEls = [...rowEl.parentElement.children];
139
- const index = rowEl.sectionRowIndex;
140
- if (index === this.#currentHoverIndex) {
238
+ return flattenedRows;
239
+ }
240
+
241
+ #onHoverRow(row: TableDataRow, rowEl: HTMLElement): void {
242
+ if (row === this.#currentHoverRow) {
141
243
  return;
142
244
  }
143
245
 
144
- for (const el of rowEl.parentElement.querySelectorAll('.hover')) {
246
+ for (const el of this.element.querySelectorAll('.hover')) {
145
247
  el.classList.remove('hover');
146
248
  }
147
249
 
148
250
  // Add 'hover' class to all parent rows.
149
- let row: TableDataRow|undefined = this.#rowToParentRow.get(this.#flattenedRows[index]);
150
- while (row) {
151
- const index = this.#flattenedRows.indexOf(row);
152
- const rowEl = rowEls[index];
251
+ let curRow: TableDataRow|undefined = this.#rowToParentRow.get(row);
252
+ while (curRow) {
153
253
  rowEl.classList.add('hover');
154
- row = this.#rowToParentRow.get(row);
254
+ curRow = this.#rowToParentRow.get(row);
155
255
  }
156
256
 
157
- this.#currentHoverIndex = index;
257
+ this.#currentHoverRow = row;
158
258
  // Temporarily selects the row, but only if there is not already a sticky selection.
159
- this.#onSelectedRowChanged(rowEl, index, {isHover: true});
259
+ this.#onSelectedRowChanged(row, rowEl, {isHover: true});
160
260
  }
161
261
 
162
- #onClickRow(e: MouseEvent): void {
163
- if (!(e.target instanceof HTMLElement)) {
164
- return;
165
- }
166
-
167
- const rowEl = e.target.closest('tr');
168
- if (!rowEl?.parentElement) {
169
- return;
170
- }
171
-
172
- const index = [...rowEl.parentElement.children].indexOf(rowEl);
173
- if (index === -1) {
174
- return;
175
- }
176
-
262
+ #onClickRow(row: TableDataRow, rowEl: HTMLElement): void {
177
263
  // If the desired overlays consist of just a single ENTRY_OUTLINE, then
178
264
  // it is more intuitive to just select the target event.
179
- const overlays = this.#flattenedRows?.[index]?.overlays;
265
+ const overlays = row.overlays;
180
266
  if (overlays?.length === 1 && overlays[0].type === 'ENTRY_OUTLINE') {
181
- this.dispatchEvent(new EventReferenceClick(overlays[0].entry));
267
+ this.element.dispatchEvent(new EventReferenceClick(overlays[0].entry));
182
268
  return;
183
269
  }
184
270
 
185
271
  // Select the row and make it sticky.
186
- this.#onSelectedRowChanged(rowEl, index, {sticky: true});
272
+ this.#onSelectedRowChanged(row, rowEl, {sticky: true});
187
273
  }
188
274
 
189
275
  #onMouseLeave(): void {
190
- for (const el of this.shadowRoot?.querySelectorAll('.hover') ?? []) {
276
+ for (const el of this.element.shadowRoot?.querySelectorAll('.hover') ?? []) {
191
277
  el.classList.remove('hover');
192
278
  }
193
279
 
194
- this.#currentHoverIndex = null;
280
+ this.#currentHoverRow = null;
195
281
  // Unselect the row, unless it's sticky.
196
282
  this.#onSelectedRowChanged(null, null);
197
283
  }
198
284
 
199
- #onSelectedRowChanged(rowEl: HTMLElement|null, rowIndex: number|null, opts: {
285
+ #onSelectedRowChanged(row: TableDataRow|null, rowEl: HTMLElement|null, opts: {
200
286
  sticky?: boolean,
201
287
  isHover?: boolean,
202
288
  } = {}): void {
203
- if (!this.#flattenedRows || !this.#state || !this.#insight) {
289
+ if (!this.#state || !this.#insight) {
204
290
  return;
205
291
  }
206
292
 
@@ -214,8 +300,8 @@ export class Table extends HTMLElement {
214
300
  opts.sticky = false;
215
301
  }
216
302
 
217
- if (rowEl && rowIndex !== null) {
218
- const overlays = this.#flattenedRows[rowIndex].overlays;
303
+ if (rowEl && row) {
304
+ const overlays = row.overlays;
219
305
  if (overlays) {
220
306
  this.#insight.toggleTemporaryOverlays(overlays, {updateTraceWindow: !opts.isHover});
221
307
  }
@@ -229,83 +315,19 @@ export class Table extends HTMLElement {
229
315
  this.#state.selectionIsSticky = opts.sticky ?? false;
230
316
  }
231
317
 
232
- async #render(): Promise<void> {
233
- if (!this.#headers || !this.#rows) {
318
+ override performUpdate(): void {
319
+ if (!this.#headers || !this.#flattenedRows) {
234
320
  return;
235
321
  }
236
322
 
237
- const rowToParentRow = this.#rowToParentRow;
238
- rowToParentRow.clear();
239
-
240
- const numColumns = this.#headers.length;
241
- const flattenedRows: TableDataRow[] = [];
242
- const rowEls: Lit.TemplateResult[] = [];
243
- function traverse(parent: TableDataRow|null, row: TableDataRow, depth = 0): void {
244
- if (parent) {
245
- rowToParentRow.set(row, parent);
246
- }
247
-
248
- const thStyles = Lit.Directives.styleMap({
249
- paddingLeft: `calc(${depth} * var(--sys-size-5))`,
250
- backgroundImage: `repeating-linear-gradient(
251
- to right,
252
- var(--sys-color-tonal-outline) 0 var(--sys-size-1),
253
- transparent var(--sys-size-1) var(--sys-size-5)
254
- )`,
255
- backgroundPosition: '0 0',
256
- backgroundRepeat: 'no-repeat',
257
- backgroundSize: `calc(${depth} * var(--sys-size-5))`,
258
- });
259
- const trStyles = Lit.Directives.styleMap({
260
- color: depth ? 'var(--sys-color-on-surface-subtle)' : '',
261
- });
262
- const columnEls = row.values.map(
263
- (value, i) => i === 0 ? html`<th
264
- scope="row"
265
- colspan=${i === row.values.length - 1 ? numColumns - i : 1}
266
- style=${thStyles}>${value}
267
- </th>` :
268
- html`<td>${value}</td>`);
269
- rowEls.push(html`<tr style=${trStyles}>${columnEls}</tr>`);
270
-
271
- flattenedRows.push(row);
272
-
273
- for (const subRow of row.subRows ?? []) {
274
- traverse(row, subRow, depth + 1);
275
- }
276
- }
277
-
278
- for (const row of this.#rows) {
279
- traverse(null, row);
280
- }
281
-
282
- this.#flattenedRows = flattenedRows;
283
-
284
- Lit.render(
285
- html`<style>${tableStyles}</style>
286
- <table
287
- class=${Lit.Directives.classMap({
288
- interactive: this.#interactive,
289
- })}
290
- @mouseleave=${this.#interactive ? this.#onMouseLeave : null}>
291
- <thead>
292
- <tr>
293
- ${this.#headers.map(h => html`<th scope="col">${h}</th>`)}
294
- </tr>
295
- </thead>
296
- <tbody
297
- @mouseover=${this.#interactive ? this.#onHoverRow : null}
298
- @click=${this.#interactive ? this.#onClickRow : null}
299
- >${rowEls}</tbody>
300
- </table>`,
301
- this.#shadow, {host: this});
323
+ const input: ViewInput = {
324
+ interactive: this.#interactive,
325
+ headers: this.#headers,
326
+ flattenedRows: this.#flattenedRows,
327
+ onHoverRow: this.#onHoverRow.bind(this),
328
+ onClickRow: this.#onClickRow.bind(this),
329
+ onMouseLeave: this.#onMouseLeave.bind(this),
330
+ };
331
+ this.#view(input, undefined, this.contentElement);
302
332
  }
303
333
  }
304
-
305
- declare global {
306
- interface HTMLElementTagNameMap {
307
- 'devtools-performance-table': Table;
308
- }
309
- }
310
-
311
- customElements.define('devtools-performance-table', Table);
@@ -5,14 +5,16 @@
5
5
  import * as i18n from '../../../../core/i18n/i18n.js';
6
6
  import type {ThirdPartiesInsightModel} from '../../../../models/trace/insights/ThirdParties.js';
7
7
  import * as Trace from '../../../../models/trace/trace.js';
8
+ import * as UI from '../../../../ui/legacy/legacy.js';
8
9
  import * as Lit from '../../../../ui/lit/lit.js';
9
10
 
10
11
  import {BaseInsightComponent} from './BaseInsightComponent.js';
11
- import {createLimitedRows, renderOthersLabel, type RowLimitAggregator} from './Table.js';
12
+ import {createLimitedRows, renderOthersLabel, type RowLimitAggregator, Table} from './Table.js';
12
13
 
13
14
  const {UIStrings, i18nString, createOverlaysForSummary} = Trace.Insights.Models.ThirdParties;
14
15
 
15
16
  const {html} = Lit;
17
+ const {widgetConfig} = UI.Widget;
16
18
 
17
19
  const MAX_TO_SHOW = 5;
18
20
 
@@ -77,13 +79,13 @@ export class ThirdParties extends BaseInsightComponent<ThirdPartiesInsightModel>
77
79
  // clang-format off
78
80
  sections.push(html`
79
81
  <div class="insight-section">
80
- <devtools-performance-table
81
- .data=${{
82
+ <devtools-widget .widgetConfig=${widgetConfig(Table, {
83
+ data: {
82
84
  insight: this,
83
85
  headers: [i18nString(UIStrings.columnThirdParty), i18nString(UIStrings.columnTransferSize)],
84
86
  rows,
85
- }}>
86
- </devtools-performance-table>
87
+ }})}>
88
+ </devtools-widget>
87
89
  </div>
88
90
  `);
89
91
  // clang-format on
@@ -94,13 +96,13 @@ export class ThirdParties extends BaseInsightComponent<ThirdPartiesInsightModel>
94
96
  // clang-format off
95
97
  sections.push(html`
96
98
  <div class="insight-section">
97
- <devtools-performance-table
98
- .data=${{
99
+ <devtools-widget .widgetConfig=${widgetConfig(Table, {
100
+ data: {
99
101
  insight: this,
100
102
  headers: [i18nString(UIStrings.columnThirdParty), i18nString(UIStrings.columnMainThreadTime)],
101
103
  rows,
102
- }}>
103
- </devtools-performance-table>
104
+ }})}>
105
+ </devtools-widget>
104
106
  </div>
105
107
  `);
106
108
  // clang-format on