chrome-devtools-frontend 1.0.1654411 → 1.0.1656897
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/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +116 -0
- package/.agents/skills/ui-eng-vision-logic-consolidator/SKILL.md +82 -0
- package/.agents/skills/ui-eng-vision-orchestrator/SKILL.md +123 -0
- package/.agents/skills/ui-eng-vision-test-scaffolder/SKILL.md +125 -0
- package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +75 -0
- package/AUTHORS +1 -0
- package/config/owner/COMMON_OWNERS +0 -4
- package/docs/playbook.md +1 -1
- package/front_end/Tests.js +1 -0
- package/front_end/core/common/Settings.ts +47 -30
- package/front_end/core/sdk/CPUThrottlingManager.ts +10 -159
- package/front_end/core/sdk/ConsoleModel.ts +13 -11
- package/front_end/core/sdk/DOMDebuggerModel.ts +11 -5
- package/front_end/core/sdk/DOMModel.ts +7 -5
- package/front_end/core/sdk/EventBreakpointsModel.ts +9 -5
- package/front_end/core/sdk/IsolateManager.ts +6 -6
- package/front_end/core/sdk/ResourceTreeModel.ts +5 -3
- package/front_end/core/sdk/SourceMapCache.ts +2 -4
- package/front_end/core/sdk/SourceMapManager.ts +8 -7
- package/front_end/core/sdk/TargetManager.ts +17 -0
- package/front_end/entrypoints/devtools_app/devtools_app.ts +0 -1
- package/front_end/entrypoints/main/MainImpl.ts +12 -36
- package/front_end/entrypoints/main/SimpleApp.ts +0 -13
- package/front_end/entrypoints/main/main-meta.ts +1 -1
- package/front_end/foundation/Universe.ts +110 -3
- package/front_end/generated/InspectorBackendCommands.ts +5 -1
- package/front_end/generated/SupportedCSSProperties.js +2 -6
- package/front_end/generated/protocol-mapping.d.ts +8 -0
- package/front_end/generated/protocol-proxy-api.d.ts +15 -0
- package/front_end/generated/protocol.ts +36 -1
- package/front_end/models/ai_assistance/AiConversation.ts +0 -6
- package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +1 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +2 -156
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -24
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +2 -358
- package/front_end/models/ai_assistance/ai_assistance.ts +0 -6
- package/front_end/models/ai_assistance/contexts/RequestContext.snapshot.txt +0 -1
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +0 -2
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -14
- package/front_end/models/autofill_manager/AutofillManager.ts +1 -2
- package/front_end/models/crux-manager/CrUXManager.ts +16 -11
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
- package/front_end/models/persistence/AutomaticFileSystemManager.ts +14 -13
- package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +12 -9
- package/front_end/models/persistence/IsolatedFileSystemManager.ts +18 -9
- package/front_end/models/project_settings/ProjectSettingsModel.ts +12 -11
- package/front_end/models/trace/handlers/ScreenshotsHandler.ts +14 -59
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +3 -29
- package/front_end/panels/application/DeviceBoundSessionsView.ts +15 -3
- package/front_end/panels/application/FrameDetailsView.ts +2 -1
- package/front_end/panels/application/ServiceWorkersView.ts +193 -161
- package/front_end/panels/application/serviceWorkersView.css +8 -0
- package/front_end/panels/common/CPUThrottlingOption.ts +142 -0
- package/front_end/panels/common/ThrottlingUtils.ts +9 -4
- package/front_end/panels/common/common.ts +1 -1
- package/front_end/panels/console/ConsoleViewMessage.ts +45 -0
- package/front_end/panels/elements/ElementsPanel.ts +35 -102
- package/front_end/panels/elements/ElementsTreeOutline.ts +0 -4
- package/front_end/panels/elements/elements-meta.ts +3 -2
- package/front_end/panels/emulation/DeviceModeToolbar.ts +46 -45
- package/front_end/panels/mobile_throttling/CalibrationController.ts +6 -5
- package/front_end/panels/mobile_throttling/MobileThrottlingSelector.ts +1 -1
- package/front_end/panels/mobile_throttling/ThrottlingManager.ts +40 -13
- package/front_end/panels/mobile_throttling/ThrottlingPresets.ts +13 -17
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +15 -13
- package/front_end/panels/network/NetworkLogView.ts +0 -9
- package/front_end/panels/network/NetworkPanel.ts +1 -63
- package/front_end/panels/network/RequestInitiatorView.ts +29 -4
- package/front_end/panels/network/components/HeaderSectionRow.css +9 -7
- package/front_end/panels/network/components/HeaderSectionRow.ts +20 -1
- package/front_end/panels/network/components/RequestHeaderSection.css +5 -3
- package/front_end/panels/network/components/RequestHeaderSection.ts +1 -2
- package/front_end/panels/network/components/RequestHeadersView.css +1 -1
- package/front_end/panels/security/SecurityPanel.ts +36 -23
- package/front_end/panels/settings/SettingsScreen.ts +18 -103
- package/front_end/panels/settings/settings-meta.ts +0 -24
- package/front_end/panels/timeline/TimelineController.ts +3 -2
- package/front_end/panels/timeline/TimelinePanel.ts +20 -3
- package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +15 -14
- package/front_end/panels/timeline/timelineFlamechartPopover.css +2 -0
- package/front_end/panels/utils/utils.ts +25 -24
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +34 -55
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +96 -83
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts +10 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/CDPSession.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/CDPSession.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPResponse.d.ts +3 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPResponse.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPResponse.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Target.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Target.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/locators/locators.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js +2 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Realm.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/core/Realm.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js +5 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Frame.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/FrameManager.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/FrameManager.js +23 -13
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/FrameManager.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPResponse.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPResponse.js +4 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPResponse.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Input.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/LifecycleWatcher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/LifecycleWatcher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Target.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts +3 -41
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/WebMCP.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/common/ConnectOptions.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.js +4 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/BrowserLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/LaunchOptions.d.ts +5 -0
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/LaunchOptions.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.js +5 -5
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/httpUtils.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/types.d.ts +34 -55
- package/front_end/third_party/puppeteer/package/package.json +4 -4
- package/front_end/third_party/puppeteer/package/src/api/Browser.ts +13 -1
- package/front_end/third_party/puppeteer/package/src/api/BrowserContext.ts +2 -3
- package/front_end/third_party/puppeteer/package/src/api/CDPSession.ts +3 -1
- package/front_end/third_party/puppeteer/package/src/api/HTTPResponse.ts +3 -2
- package/front_end/third_party/puppeteer/package/src/api/Realm.ts +1 -2
- package/front_end/third_party/puppeteer/package/src/api/Target.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/api/locators/locators.ts +1 -2
- package/front_end/third_party/puppeteer/package/src/bidi/HTTPResponse.ts +3 -1
- package/front_end/third_party/puppeteer/package/src/bidi/core/Realm.ts +1 -3
- package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +13 -3
- package/front_end/third_party/puppeteer/package/src/cdp/Frame.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/cdp/FrameManager.ts +24 -13
- package/front_end/third_party/puppeteer/package/src/cdp/HTTPResponse.ts +4 -1
- package/front_end/third_party/puppeteer/package/src/cdp/Input.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/cdp/LifecycleWatcher.ts +1 -4
- package/front_end/third_party/puppeteer/package/src/cdp/Target.ts +2 -4
- package/front_end/third_party/puppeteer/package/src/cdp/WebMCP.ts +7 -76
- package/front_end/third_party/puppeteer/package/src/common/ConnectOptions.ts +1 -4
- package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +4 -1
- package/front_end/third_party/puppeteer/package/src/node/LaunchOptions.ts +5 -0
- package/front_end/third_party/puppeteer/package/src/revisions.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/util/httpUtils.ts +5 -5
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/third_party/puppeteer/third_party/parsel/README.chromium +1 -1
- package/front_end/ui/components/dialogs/Dialog.ts +3 -0
- package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +0 -7
- package/front_end/ui/helpers/OpenInNewTab.ts +15 -7
- package/front_end/ui/legacy/TabbedPane.ts +1 -108
- package/front_end/ui/legacy/components/data_grid/DataGrid.ts +9 -0
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +172 -123
- package/front_end/ui/legacy/components/data_grid/DataGridExporter.ts +89 -0
- package/front_end/ui/legacy/components/data_grid/dataGrid.css +10 -0
- package/front_end/ui/legacy/components/data_grid/data_grid.ts +3 -1
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +89 -40
- package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
- package/package.json +1 -1
- package/front_end/Images/navigationControls.png +0 -0
- package/front_end/Images/navigationControls_2x.png +0 -0
- package/front_end/Images/popoverArrows.png +0 -0
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -746
- package/front_end/entrypoints/greendev_floaty/floaty.css +0 -301
- package/front_end/entrypoints/greendev_floaty/floaty.html +0 -38
- package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -415
- package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +0 -834
- package/front_end/models/ai_assistance/agents/GreenDevAgentAntigravityCliSocketClient.ts +0 -53
- package/front_end/models/ai_assistance/agents/GreenDevAgentGeminiCliSocketClient.ts +0 -117
- package/front_end/models/annotations/AnnotationRepository.ts +0 -238
- package/front_end/models/annotations/AnnotationType.ts +0 -10
- package/front_end/models/annotations/README.md +0 -7
- package/front_end/models/annotations/annotations.ts +0 -6
- package/front_end/models/greendev/Prototypes.ts +0 -68
- package/front_end/models/greendev/README.md +0 -5
- package/front_end/models/greendev/greendev.ts +0 -5
- package/front_end/panels/common/Annotation.ts +0 -184
- package/front_end/panels/common/AnnotationManager.ts +0 -208
- package/front_end/panels/common/annotation.css +0 -40
- package/front_end/panels/greendev/GreenDevPanel.css +0 -282
- package/front_end/panels/greendev/GreenDevPanel.ts +0 -364
- package/front_end/panels/greendev/GreenDevShared.ts +0 -13
- package/front_end/panels/greendev/greendev-meta.ts +0 -52
- package/front_end/panels/greendev/greendev.ts +0 -9
|
@@ -12,6 +12,8 @@ import dataGridStyles from './dataGrid.css.js';
|
|
|
12
12
|
import {
|
|
13
13
|
Align,
|
|
14
14
|
type ColumnDescriptor,
|
|
15
|
+
DataGridImpl,
|
|
16
|
+
DataGridNode,
|
|
15
17
|
DataType,
|
|
16
18
|
Events as DataGridEvents,
|
|
17
19
|
Order,
|
|
@@ -50,10 +52,15 @@ const DUMMY_COLUMN_ID = 'dummy'; // SortableDataGrid.create requires at least o
|
|
|
50
52
|
* @attribute striped If true, the data grid will have striped rows.
|
|
51
53
|
* @attribute displayName
|
|
52
54
|
*/
|
|
55
|
+
type DataGridElementNode = SortableNode|DynamicHeightNode;
|
|
56
|
+
|
|
57
|
+
const elementToNode = new WeakMap<Element, DataGridElementNode>();
|
|
58
|
+
|
|
53
59
|
export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate {
|
|
54
60
|
static readonly observedAttributes = ['striped', 'name', 'inline', 'resize'];
|
|
55
61
|
|
|
56
|
-
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
#dataGrid!: DataGridImpl<any>;
|
|
57
64
|
#resizeObserver = new ResizeObserver(() => {
|
|
58
65
|
if (!this.inline) {
|
|
59
66
|
this.#dataGrid.onResize();
|
|
@@ -69,6 +76,18 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
69
76
|
super();
|
|
70
77
|
// TODO(dsv): Move this to the data_grid.css once all the data grid usage is migrated to this web component.
|
|
71
78
|
this.style.display = 'flex';
|
|
79
|
+
|
|
80
|
+
const autoRowHeight = this.getAttribute('row-height') === 'auto';
|
|
81
|
+
if (autoRowHeight) {
|
|
82
|
+
this.#dataGrid = new DataGridImpl<DynamicHeightNode>({
|
|
83
|
+
displayName: this.getAttribute('name') ?? '',
|
|
84
|
+
columns: [],
|
|
85
|
+
});
|
|
86
|
+
this.#dataGrid.element.classList.add('auto-row-height');
|
|
87
|
+
} else {
|
|
88
|
+
this.#dataGrid = SortableDataGrid.create([DUMMY_COLUMN_ID], [], '') as SortableDataGrid<SortableNode>;
|
|
89
|
+
}
|
|
90
|
+
|
|
72
91
|
this.#dataGrid.element.style.flex = 'auto';
|
|
73
92
|
|
|
74
93
|
this.#shadowRoot = UI.UIUtils.createShadowRootWithCoreStyles(this, {delegatesFocus: true, cssFile: dataGridStyles});
|
|
@@ -77,8 +96,8 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
77
96
|
this.#dataGrid.addEventListener(
|
|
78
97
|
DataGridEvents.SELECTED_NODE,
|
|
79
98
|
e => (e.data as DataGridElementNode).configElement.dispatchEvent(new CustomEvent('select')));
|
|
80
|
-
this.#dataGrid.addEventListener(
|
|
81
|
-
|
|
99
|
+
this.#dataGrid.addEventListener(DataGridEvents.DESELECTED_NODE,
|
|
100
|
+
() => this.dispatchEvent(new CustomEvent('deselect')));
|
|
82
101
|
this.#dataGrid.addEventListener(
|
|
83
102
|
DataGridEvents.OPENED_NODE,
|
|
84
103
|
e => (e.data as DataGridElementNode).configElement.dispatchEvent(new CustomEvent('open')));
|
|
@@ -169,8 +188,12 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
169
188
|
}
|
|
170
189
|
|
|
171
190
|
set filters(filters: TextUtils.TextUtils.ParsedFilter[]) {
|
|
172
|
-
this.#dataGrid
|
|
173
|
-
|
|
191
|
+
if (this.#dataGrid instanceof SortableDataGrid) {
|
|
192
|
+
this.#dataGrid.setFilters(filters);
|
|
193
|
+
this.#dataGrid.element.setAttribute('aria-rowcount', String(this.#dataGrid.getNumberOfRows()));
|
|
194
|
+
} else {
|
|
195
|
+
this.#dataGrid.element.setAttribute('aria-rowcount', String(this.#dataGrid.rootNode().children.length));
|
|
196
|
+
}
|
|
174
197
|
}
|
|
175
198
|
|
|
176
199
|
get columns(): ColumnDescriptor[] {
|
|
@@ -239,9 +262,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
239
262
|
}
|
|
240
263
|
}
|
|
241
264
|
const visibleColumns = new Set(this.#columns.map(({id}) => id).filter(id => !this.#hiddenColumns.has(id)));
|
|
242
|
-
|
|
243
|
-
this.#dataGrid.setColumnsVisibility(visibleColumns);
|
|
244
|
-
}
|
|
265
|
+
this.#dataGrid.setColumnsVisibility(visibleColumns);
|
|
245
266
|
this.#dataGrid.setEditCallback(hasEditableColumn ? this.#editCallback.bind(this) : undefined, INTERNAL_TOKEN);
|
|
246
267
|
this.#dataGrid.deleteCallback = hasEditableColumn ? this.#deleteCallback.bind(this) : undefined;
|
|
247
268
|
}
|
|
@@ -306,7 +327,9 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
306
327
|
const parentNode = parentDataGridNode || this.#dataGrid.rootNode();
|
|
307
328
|
const nextNode = this.#findNextExistingNode(element);
|
|
308
329
|
const index = nextNode ? parentNode.children.indexOf(nextNode) : parentNode.children.length;
|
|
309
|
-
|
|
330
|
+
|
|
331
|
+
const node = this.#dataGrid instanceof SortableDataGrid ? new SortableNode(element, this) :
|
|
332
|
+
new DynamicHeightNode(element, this);
|
|
310
333
|
this.#updateHasChildren(node, element);
|
|
311
334
|
if ((parentRow || node.hasChildren()) && !this.#dataGrid.disclosureColumnId) {
|
|
312
335
|
this.#dataGrid.disclosureColumnId = this.#columns[0].id;
|
|
@@ -382,7 +405,9 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
382
405
|
this.#dataGrid.creationNode = undefined;
|
|
383
406
|
} else if (!getNode(placeholder)) {
|
|
384
407
|
this.#dataGrid.creationNode?.remove();
|
|
385
|
-
|
|
408
|
+
|
|
409
|
+
const node = this.#dataGrid instanceof SortableDataGrid ? new SortableNode(placeholder, this) :
|
|
410
|
+
new DynamicHeightNode(placeholder, this);
|
|
386
411
|
this.#dataGrid.creationNode = node;
|
|
387
412
|
this.#dataGrid.rootNode().appendChild(node);
|
|
388
413
|
}
|
|
@@ -404,9 +429,8 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
404
429
|
}
|
|
405
430
|
}
|
|
406
431
|
|
|
407
|
-
#editCallback(
|
|
408
|
-
|
|
409
|
-
moveDirection?: string): void {
|
|
432
|
+
#editCallback(node: DataGridElementNode, columnId: string, valueBeforeEditing: string, newText: string,
|
|
433
|
+
moveDirection?: string): void {
|
|
410
434
|
if (node.isCreationNode) {
|
|
411
435
|
this.#usedCreationNode = node;
|
|
412
436
|
let hasNextEditableColumn = false;
|
|
@@ -432,9 +456,8 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
432
456
|
override addEventListener<K extends keyof HTMLElementEventMap>(
|
|
433
457
|
type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => void,
|
|
434
458
|
options?: boolean|AddEventListenerOptions|undefined): void;
|
|
435
|
-
override addEventListener(
|
|
436
|
-
|
|
437
|
-
options?: boolean|AddEventListenerOptions|undefined): void;
|
|
459
|
+
override addEventListener(type: string, listener: EventListenerOrEventListenerObject,
|
|
460
|
+
options?: boolean|AddEventListenerOptions|undefined): void;
|
|
438
461
|
override addEventListener(...args: Parameters<HTMLElement['addEventListener']>): void {
|
|
439
462
|
super.addEventListener(...args);
|
|
440
463
|
if (args[0] === 'refresh') {
|
|
@@ -447,137 +470,163 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
447
470
|
}
|
|
448
471
|
}
|
|
449
472
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
class DataGridElementNode extends SortableDataGridNode<DataGridElementNode> {
|
|
453
|
-
#configElement: Element;
|
|
454
|
-
#dataGridElement: DataGridElement;
|
|
455
|
-
#addedClasses = new Set<string>();
|
|
456
|
-
constructor(configElement: Element, dataGridElement: DataGridElement) {
|
|
457
|
-
super();
|
|
458
|
-
this.#configElement = configElement;
|
|
459
|
-
elementToNode.set(configElement, this);
|
|
460
|
-
this.#dataGridElement = dataGridElement;
|
|
461
|
-
this.#updateData();
|
|
462
|
-
this.isCreationNode = hasBooleanAttribute(this.#configElement, 'placeholder');
|
|
463
|
-
}
|
|
473
|
+
type Constructor<T = object> = new (...args: any[]) => T;
|
|
464
474
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
475
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-function-return-type
|
|
476
|
+
function nodeMixin<TBase extends Constructor<DataGridNode<any>>>(base: TBase) {
|
|
477
|
+
return class extends base {
|
|
478
|
+
#configElement!: Element;
|
|
479
|
+
#dataGridElement!: DataGridElement;
|
|
480
|
+
#addedClasses = new Set<string>();
|
|
468
481
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
continue;
|
|
475
|
-
}
|
|
476
|
-
const column = this.#dataGridElement.columns[i];
|
|
477
|
-
if (column.dataType === DataType.BOOLEAN) {
|
|
478
|
-
this.data[column.id] = hasBooleanAttribute(cell, 'data-value') || cell.textContent === 'true';
|
|
479
|
-
} else {
|
|
480
|
-
this.data[column.id] = cell.dataset.value ?? cell.textContent ?? '';
|
|
481
|
-
}
|
|
482
|
+
initElementNode(configElement: Element, dataGridElement: DataGridElement): void {
|
|
483
|
+
this.#configElement = configElement;
|
|
484
|
+
this.#dataGridElement = dataGridElement;
|
|
485
|
+
this.#updateData();
|
|
486
|
+
this.isCreationNode = hasBooleanAttribute(this.#configElement, 'placeholder');
|
|
482
487
|
}
|
|
483
|
-
}
|
|
484
488
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
element.addEventListener('click', this.#onRowMouseEvent.bind(this));
|
|
488
|
-
element.addEventListener('mouseenter', this.#onRowMouseEvent.bind(this));
|
|
489
|
-
element.addEventListener('mouseleave', this.#onRowMouseEvent.bind(this));
|
|
490
|
-
if (this.#configElement.hasAttribute('style')) {
|
|
491
|
-
element.setAttribute('style', this.#configElement.getAttribute('style') || '');
|
|
492
|
-
}
|
|
493
|
-
for (const classToAdd of this.#configElement.classList) {
|
|
494
|
-
element.classList.add(classToAdd);
|
|
489
|
+
get configElement(): Element {
|
|
490
|
+
return this.#configElement;
|
|
495
491
|
}
|
|
496
|
-
return element;
|
|
497
|
-
}
|
|
498
492
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
super.refresh();
|
|
502
|
-
const existingElement = this.existingElement();
|
|
503
|
-
if (!existingElement) {
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
if (this.#configElement.hasAttribute('style')) {
|
|
507
|
-
existingElement.setAttribute('style', this.#configElement.getAttribute('style') || '');
|
|
493
|
+
get dataGridElement(): DataGridElement {
|
|
494
|
+
return this.#dataGridElement;
|
|
508
495
|
}
|
|
509
|
-
|
|
510
|
-
|
|
496
|
+
|
|
497
|
+
#updateData(): void {
|
|
498
|
+
const cells = [...this.#configElement.children].filter(c => c.tagName === 'TD');
|
|
499
|
+
for (let i = 0; i < this.#dataGridElement.columns.length; ++i) {
|
|
500
|
+
const cell = cells[i] as HTMLElement;
|
|
501
|
+
if (!cell) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
const column = this.#dataGridElement.columns[i];
|
|
505
|
+
if (column.dataType === DataType.BOOLEAN) {
|
|
506
|
+
this.data[column.id] = hasBooleanAttribute(cell, 'data-value') || cell.textContent === 'true';
|
|
507
|
+
} else {
|
|
508
|
+
this.data[column.id] = cell.dataset.value ?? cell.textContent ?? '';
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
511
|
}
|
|
512
|
-
|
|
513
|
-
|
|
512
|
+
|
|
513
|
+
override createElement(): HTMLElement {
|
|
514
|
+
const element = super.createElement();
|
|
515
|
+
element.addEventListener('click', this.#onRowMouseEvent.bind(this));
|
|
516
|
+
element.addEventListener('mouseenter', this.#onRowMouseEvent.bind(this));
|
|
517
|
+
element.addEventListener('mouseleave', this.#onRowMouseEvent.bind(this));
|
|
518
|
+
if (this.#configElement.hasAttribute('style')) {
|
|
519
|
+
element.setAttribute('style', this.#configElement.getAttribute('style') || '');
|
|
520
|
+
}
|
|
521
|
+
for (const classToAdd of this.#configElement.classList) {
|
|
522
|
+
element.classList.add(classToAdd);
|
|
523
|
+
}
|
|
524
|
+
return element;
|
|
514
525
|
}
|
|
515
|
-
}
|
|
516
526
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
527
|
+
override refresh(): void {
|
|
528
|
+
this.#updateData();
|
|
529
|
+
super.refresh();
|
|
530
|
+
const existingElement = this.existingElement();
|
|
531
|
+
if (!existingElement) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (this.#configElement.hasAttribute('style')) {
|
|
535
|
+
existingElement.setAttribute('style', this.#configElement.getAttribute('style') || '');
|
|
536
|
+
}
|
|
537
|
+
for (const addedClass of this.#addedClasses) {
|
|
538
|
+
existingElement.classList.remove(addedClass);
|
|
539
|
+
}
|
|
540
|
+
for (const classToAdd of this.#configElement.classList) {
|
|
541
|
+
existingElement.classList.add(classToAdd);
|
|
542
|
+
}
|
|
522
543
|
}
|
|
523
544
|
|
|
524
|
-
|
|
525
|
-
targetInConfigRow
|
|
545
|
+
#onRowMouseEvent(event: MouseEvent): void {
|
|
546
|
+
const targetInConfigRow = UI.UIUtils.HTMLElementWithLightDOMTemplate.findCorrespondingElement(
|
|
547
|
+
event.target as HTMLElement, event.currentTarget as HTMLElement, this.#configElement);
|
|
548
|
+
if (!targetInConfigRow) {
|
|
549
|
+
throw new Error('Cell click event target not found in the data grid');
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (targetInConfigRow instanceof HTMLElement) {
|
|
553
|
+
targetInConfigRow?.dispatchEvent(new MouseEvent(event.type, {bubbles: true, composed: true}));
|
|
554
|
+
}
|
|
526
555
|
}
|
|
527
|
-
}
|
|
528
556
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
557
|
+
override createCells(element: Element): void {
|
|
558
|
+
const configCells = [...this.#configElement.querySelectorAll('td')];
|
|
559
|
+
const hasCollspan = configCells.some(cell => cell.hasAttribute('colspan'));
|
|
560
|
+
if (!hasCollspan) {
|
|
561
|
+
super.createCells(element);
|
|
562
|
+
} else {
|
|
563
|
+
for (const cell of configCells) {
|
|
564
|
+
element.appendChild(cell.cloneNode(true));
|
|
565
|
+
}
|
|
537
566
|
}
|
|
538
567
|
}
|
|
539
|
-
}
|
|
540
568
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
569
|
+
override createCell(columnId: string): HTMLElement {
|
|
570
|
+
const index = this.#dataGridElement.columns.findIndex(({id}) => id === columnId);
|
|
571
|
+
if (this.#dataGridElement.columns[index].dataType === DataType.BOOLEAN) {
|
|
572
|
+
const cell = super.createCell(columnId);
|
|
573
|
+
cell.setAttribute('part', `${columnId}-column`);
|
|
574
|
+
return cell;
|
|
575
|
+
}
|
|
576
|
+
const cell = this.createTD(columnId);
|
|
545
577
|
cell.setAttribute('part', `${columnId}-column`);
|
|
578
|
+
const configCells = [...this.#configElement.children].filter(c => c.tagName === 'TD') as HTMLTableCellElement[];
|
|
579
|
+
const configCell = configCells[index];
|
|
580
|
+
if (this.isCreationNode && !configCell) {
|
|
581
|
+
return cell;
|
|
582
|
+
}
|
|
583
|
+
if (!configCell) {
|
|
584
|
+
throw new Error(`Column ${columnId} not found in the data grid`);
|
|
585
|
+
}
|
|
586
|
+
for (const child of configCell.childNodes) {
|
|
587
|
+
cell.appendChild(child.cloneNode(true));
|
|
588
|
+
}
|
|
589
|
+
for (const cssClass of configCell.classList) {
|
|
590
|
+
cell.classList.add(cssClass);
|
|
591
|
+
}
|
|
592
|
+
cell.title = configCell.title;
|
|
593
|
+
if (configCell.hasAttribute('aria-label')) {
|
|
594
|
+
this.setCellAccessibleName(configCell.getAttribute('aria-label') || '', cell, columnId);
|
|
595
|
+
}
|
|
596
|
+
const style = configCell.getAttribute('style');
|
|
597
|
+
if (style !== null) {
|
|
598
|
+
cell.setAttribute('style', style);
|
|
599
|
+
}
|
|
600
|
+
|
|
546
601
|
return cell;
|
|
547
602
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
}
|
|
555
|
-
if (!configCell) {
|
|
556
|
-
throw new Error(`Column ${columnId} not found in the data grid`);
|
|
557
|
-
}
|
|
558
|
-
for (const child of configCell.childNodes) {
|
|
559
|
-
cell.appendChild(child.cloneNode(true));
|
|
560
|
-
}
|
|
561
|
-
for (const cssClass of configCell.classList) {
|
|
562
|
-
cell.classList.add(cssClass);
|
|
563
|
-
}
|
|
564
|
-
cell.title = configCell.title;
|
|
565
|
-
if (configCell.hasAttribute('aria-label')) {
|
|
566
|
-
this.setCellAccessibleName(configCell.getAttribute('aria-label') || '', cell, columnId);
|
|
567
|
-
}
|
|
568
|
-
const style = configCell.getAttribute('style');
|
|
569
|
-
if (style !== null) {
|
|
570
|
-
cell.setAttribute('style', style);
|
|
603
|
+
|
|
604
|
+
override deselect(): void {
|
|
605
|
+
super.deselect();
|
|
606
|
+
if (this.isCreationNode) {
|
|
607
|
+
this.#dataGridElement.dispatchEvent(new CustomEvent('create', {detail: this.data}));
|
|
608
|
+
}
|
|
571
609
|
}
|
|
610
|
+
};
|
|
611
|
+
}
|
|
572
612
|
|
|
573
|
-
|
|
613
|
+
// clang-format off
|
|
614
|
+
class SortableNode extends nodeMixin(SortableDataGridNode<SortableNode>) {
|
|
615
|
+
// clang-format on
|
|
616
|
+
constructor(configElement: Element, dataGridElement: DataGridElement) {
|
|
617
|
+
super();
|
|
618
|
+
this.initElementNode(configElement, dataGridElement);
|
|
619
|
+
elementToNode.set(configElement, this);
|
|
574
620
|
}
|
|
621
|
+
}
|
|
575
622
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
623
|
+
// clang-format off
|
|
624
|
+
class DynamicHeightNode extends nodeMixin(DataGridNode<DynamicHeightNode>) {
|
|
625
|
+
// clang-format on
|
|
626
|
+
constructor(configElement: Element, dataGridElement: DataGridElement) {
|
|
627
|
+
super();
|
|
628
|
+
this.initElementNode(configElement, dataGridElement);
|
|
629
|
+
elementToNode.set(configElement, this);
|
|
581
630
|
}
|
|
582
631
|
}
|
|
583
632
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import type {DataGridImpl} from './DataGrid.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Exports the visual contents of a DataGrid as a GitHub Flavored Markdown table.
|
|
9
|
+
*/
|
|
10
|
+
export function exportToMarkdown(dataGrid: DataGridImpl<unknown>): string {
|
|
11
|
+
const grid = serializeGrid(dataGrid, escapeMarkdown);
|
|
12
|
+
if (grid.length === 0) {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
const header = '| ' + grid[0].join(' | ') + ' |';
|
|
16
|
+
const separator = '| ' + grid[0].map(() => '---').join(' | ') + ' |';
|
|
17
|
+
const body = grid.slice(1).map(row => '| ' + row.join(' | ') + ' |');
|
|
18
|
+
return [header, separator, ...body].join('\n');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function exportToCSV(dataGrid: DataGridImpl<unknown>): string {
|
|
22
|
+
const grid = serializeGrid(dataGrid, escapeCSV);
|
|
23
|
+
return grid.map(row => row.join(',')).join('\n');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function serializeGrid(dataGrid: DataGridImpl<unknown>, cellEscaper: (val: string) => string): string[][] {
|
|
27
|
+
const columns = dataGrid.visibleColumnsArray;
|
|
28
|
+
if (columns.length === 0) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const rows = dataGrid.rootNode().children;
|
|
32
|
+
|
|
33
|
+
const result: string[][] = [];
|
|
34
|
+
// Header
|
|
35
|
+
result.push(columns.map(col => cellEscaper(String(col.title || ''))));
|
|
36
|
+
|
|
37
|
+
// Body
|
|
38
|
+
for (const row of rows) {
|
|
39
|
+
result.push(columns.map(col => {
|
|
40
|
+
const cellValue = row.data[col.id];
|
|
41
|
+
const cellText = extractText(cellValue);
|
|
42
|
+
return cellEscaper(cellText);
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function extractText(value: unknown): string {
|
|
50
|
+
if (value instanceof Node) {
|
|
51
|
+
return value.textContent || '';
|
|
52
|
+
}
|
|
53
|
+
if (value === undefined || value === null) {
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
return String(value);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Escapes a string so it can be safely rendered within a Markdown table cell.
|
|
61
|
+
*
|
|
62
|
+
* It performs three operations:
|
|
63
|
+
* 1. HTML-escapes `<`, `>`, and `&` to prevent text from being parsed as raw HTML.
|
|
64
|
+
* 2. Backslash-escapes Markdown control characters (e.g., `*`, `_`, `[`) to avoid unexpected formatting.
|
|
65
|
+
* 3. Replaces newlines with `<br>` tags since Markdown tables do not support multiline cells.
|
|
66
|
+
*/
|
|
67
|
+
function escapeMarkdown(val: string): string {
|
|
68
|
+
return val.replace(/&/g, '&')
|
|
69
|
+
.replace(/</g, '<')
|
|
70
|
+
.replace(/>/g, '>')
|
|
71
|
+
.replace(/([\\`*_{}[\]()#+\-.!|])/g, '\\$1')
|
|
72
|
+
.replace(/\r?\n/g, '<br>');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function escapeCSV(val: string): string {
|
|
76
|
+
let needQuotes = false;
|
|
77
|
+
let escaped = val;
|
|
78
|
+
if (escaped.includes('"')) {
|
|
79
|
+
escaped = escaped.replace(/"/g, '""');
|
|
80
|
+
needQuotes = true;
|
|
81
|
+
}
|
|
82
|
+
if (escaped.includes(',') || escaped.includes('\n') || escaped.includes('\r')) {
|
|
83
|
+
needQuotes = true;
|
|
84
|
+
}
|
|
85
|
+
if (needQuotes) {
|
|
86
|
+
return `"${escaped}"`;
|
|
87
|
+
}
|
|
88
|
+
return escaped;
|
|
89
|
+
}
|
|
@@ -110,6 +110,16 @@
|
|
|
110
110
|
padding: 1px 4px;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
.data-grid.auto-row-height tbody tr {
|
|
114
|
+
height: auto;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.data-grid.auto-row-height td {
|
|
118
|
+
white-space: normal;
|
|
119
|
+
height: auto;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
113
123
|
.data-grid td {
|
|
114
124
|
vertical-align: top;
|
|
115
125
|
user-select: text;
|
|
@@ -7,11 +7,13 @@ import './ViewportDataGrid.js';
|
|
|
7
7
|
import './SortableDataGrid.js';
|
|
8
8
|
import './ShowMoreDataGridNode.js';
|
|
9
9
|
import './DataGridElement.js';
|
|
10
|
+
import './DataGridExporter.js';
|
|
10
11
|
|
|
11
12
|
import * as DataGrid from './DataGrid.js';
|
|
13
|
+
import * as DataGridExporter from './DataGridExporter.js';
|
|
12
14
|
import * as ShowMoreDataGridNode from './ShowMoreDataGridNode.js';
|
|
13
15
|
import * as SortableDataGrid from './SortableDataGrid.js';
|
|
14
16
|
import * as ViewportDataGrid from './ViewportDataGrid.js';
|
|
15
17
|
|
|
16
18
|
export {ifExpanded} from './DataGridElement.js';
|
|
17
|
-
export {DataGrid, ShowMoreDataGridNode, SortableDataGrid, ViewportDataGrid};
|
|
19
|
+
export {DataGrid, DataGridExporter, ShowMoreDataGridNode, SortableDataGrid, ViewportDataGrid};
|