chrome-devtools-frontend 1.0.1652307 → 1.0.1656291
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/fixing-skipped-tests/SKILL.md +32 -0
- package/.agents/skills/migrate-chromium-test/SKILL.md +144 -0
- 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/get_the_code.md +1 -2
- package/docs/playbook.md +1 -1
- package/docs/ui_engineering.md +3 -1
- package/eslint.config.mjs +0 -5
- package/front_end/Tests.js +1 -0
- package/front_end/core/common/Settings.ts +47 -30
- package/front_end/core/sdk/CPUThrottlingManager.ts +23 -167
- package/front_end/core/sdk/CSSMetadata.ts +23 -0
- package/front_end/core/sdk/CompilerSourceMappingContentProvider.ts +8 -4
- 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 +4 -0
- package/front_end/core/sdk/EmulationModel.ts +4 -0
- 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 +8 -5
- package/front_end/core/sdk/TargetManager.ts +17 -0
- package/front_end/entrypoints/devtools_app/devtools_app.ts +0 -1
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +459 -74
- package/front_end/entrypoints/main/MainImpl.ts +12 -42
- package/front_end/foundation/Universe.ts +116 -3
- package/front_end/generated/InspectorBackendCommands.ts +11 -6
- package/front_end/generated/SupportedCSSProperties.js +124 -34
- 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 +94 -1
- package/front_end/models/ai_assistance/AiConversation.ts +2 -7
- package/front_end/models/ai_assistance/README.md +4 -3
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +1 -1
- package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +1 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +13 -320
- 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 +6 -8
- package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +295 -0
- 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/PerformanceInsightFormatter.snapshot.txt +10 -10
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +12 -11
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +6 -6
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -14
- package/front_end/models/ai_assistance/skills/accessibility.md +4 -2
- package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +142 -0
- package/front_end/models/ai_assistance/tools/GetStyles.ts +1 -1
- package/front_end/models/ai_assistance/tools/README.md +21 -1
- package/front_end/models/ai_assistance/tools/{ResolveLighthousePath.ts → ResolveDevtoolsNodePath.ts} +18 -17
- package/front_end/models/ai_assistance/tools/Tool.ts +6 -1
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +4 -2
- package/front_end/models/autofill_manager/AutofillManager.ts +6 -15
- package/front_end/models/bindings/NetworkProject.ts +4 -8
- package/front_end/models/breakpoints/BreakpointManager.ts +9 -9
- package/front_end/models/emulation/DeviceModeModel.ts +24 -0
- package/front_end/models/emulation/EmulatedDevices.ts +108 -0
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +36 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +8 -0
- package/front_end/models/issues_manager/IssuesManager.ts +10 -6
- package/front_end/models/javascript_metadata/JavaScriptMetadata.ts +4 -5
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
- package/front_end/models/live-metrics/LiveMetrics.ts +6 -6
- package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +5 -5
- package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +3 -3
- package/front_end/models/logs/LogManager.ts +25 -10
- package/front_end/models/logs/NetworkLog.ts +24 -19
- package/front_end/models/persistence/AutomaticFileSystemManager.ts +14 -13
- package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +26 -14
- package/front_end/models/persistence/IsolatedFileSystem.ts +9 -11
- package/front_end/models/persistence/IsolatedFileSystemManager.ts +22 -11
- package/front_end/models/persistence/NetworkPersistenceManager.ts +73 -44
- package/front_end/models/persistence/PersistenceImpl.ts +4 -5
- package/front_end/models/project_settings/ProjectSettingsModel.ts +12 -11
- package/front_end/models/trace/EntityMapper.ts +1 -1
- package/front_end/models/trace/handlers/ScreenshotsHandler.ts +14 -59
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +12 -38
- package/front_end/panels/ai_assistance/ExportConversation.ts +4 -1
- package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
- package/front_end/panels/application/DeviceBoundSessionsView.ts +15 -3
- package/front_end/panels/application/FrameDetailsView.ts +2 -1
- package/front_end/panels/application/IndexedDBViews.ts +38 -36
- package/front_end/panels/application/ServiceWorkersView.ts +23 -16
- package/front_end/panels/application/components/AdsView.ts +4 -0
- package/front_end/panels/autofill/AutofillView.ts +1 -1
- package/front_end/panels/autofill/autofill-meta.ts +3 -2
- 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/AccessibilityTreeUtils.ts +26 -15
- package/front_end/panels/elements/AccessibilityTreeView.ts +12 -9
- package/front_end/panels/elements/ElementsPanel.ts +0 -74
- package/front_end/panels/elements/ElementsTreeOutline.ts +5 -4
- package/front_end/panels/emulation/DeviceModeToolbar.ts +46 -45
- package/front_end/panels/layer_viewer/LayerTreeOutline.ts +118 -68
- package/front_end/panels/layer_viewer/Layers3DView.ts +1 -1
- package/front_end/panels/layer_viewer/layerTreeOutline.css +20 -13
- package/front_end/panels/layers/LayersPanel.ts +1 -1
- 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 +79 -30
- package/front_end/panels/mobile_throttling/throttlingSettingsTab.css +5 -0
- package/front_end/panels/network/LinkPreloadGenerator.ts +101 -0
- package/front_end/panels/network/NetworkLogView.ts +28 -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/network/network.ts +2 -0
- package/front_end/panels/profiler/HeapSnapshotView.ts +121 -33
- package/front_end/panels/profiler/ModuleUIStrings.ts +4 -0
- 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/TimelineHistoryManager.ts +38 -37
- package/front_end/panels/timeline/TimelinePanel.ts +52 -11
- package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +20 -18
- package/front_end/panels/timeline/components/LiveMetricsView.ts +38 -38
- package/front_end/panels/timeline/components/MetricCard.ts +18 -18
- package/front_end/panels/timeline/components/liveMetricsView.css +6 -6
- package/front_end/panels/timeline/components/metricCard.css +4 -4
- package/front_end/panels/timeline/timelineFlamechartPopover.css +2 -0
- package/front_end/panels/utils/utils.ts +25 -24
- package/front_end/panels/whats_new/ReleaseNoteText.ts +3 -3
- package/front_end/panels/whats_new/resources/WNDT.md +1 -1
- 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/InspectorDrawerView.ts +7 -5
- package/front_end/ui/legacy/ListWidget.ts +12 -0
- package/front_end/ui/legacy/SplitWidget.ts +12 -11
- 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 +192 -139
- 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/object_ui/CustomPreviewComponent.ts +8 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +110 -17
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +89 -40
- package/front_end/ui/legacy/treeoutline.css +1 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
- package/package.json +4 -3
- 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
|
}
|
|
@@ -291,7 +312,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
291
312
|
|
|
292
313
|
#findNextExistingNode(element: Element): DataGridElementNode|null {
|
|
293
314
|
for (let e = element.nextElementSibling; e; e = e.nextElementSibling) {
|
|
294
|
-
const nextNode =
|
|
315
|
+
const nextNode = getNode(e);
|
|
295
316
|
if (nextNode) {
|
|
296
317
|
return nextNode;
|
|
297
318
|
}
|
|
@@ -302,11 +323,13 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
302
323
|
override addNodes(nodes: NodeList): void {
|
|
303
324
|
for (const element of this.#getDataRows(nodes)) {
|
|
304
325
|
const parentRow = element.parentElement?.closest('td')?.closest('tr');
|
|
305
|
-
const parentDataGridNode = parentRow ?
|
|
326
|
+
const parentDataGridNode = parentRow ? getNode(parentRow) : undefined;
|
|
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;
|
|
@@ -333,9 +356,9 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
333
356
|
|
|
334
357
|
override removeNodes(nodes: NodeList): void {
|
|
335
358
|
for (const element of this.#getDataRows(nodes)) {
|
|
336
|
-
const node =
|
|
359
|
+
const node = getNode(element);
|
|
337
360
|
if (node) {
|
|
338
|
-
|
|
361
|
+
removeNode(node);
|
|
339
362
|
}
|
|
340
363
|
}
|
|
341
364
|
}
|
|
@@ -345,7 +368,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
345
368
|
node = node.parentNode;
|
|
346
369
|
}
|
|
347
370
|
const dataRow = node instanceof HTMLElement ? node.closest('tr') : null;
|
|
348
|
-
const dataGridNode = dataRow ?
|
|
371
|
+
const dataGridNode = dataRow ? getNode(dataRow) : null;
|
|
349
372
|
if (dataGridNode && dataRow) {
|
|
350
373
|
if (attributeName === 'selected') {
|
|
351
374
|
if (hasBooleanAttribute(dataRow, 'selected')) {
|
|
@@ -372,7 +395,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
372
395
|
|
|
373
396
|
#updateCreationNode(): void {
|
|
374
397
|
if (this.#usedCreationNode) {
|
|
375
|
-
|
|
398
|
+
removeNode(this.#usedCreationNode);
|
|
376
399
|
this.#usedCreationNode = null;
|
|
377
400
|
this.#dataGrid.creationNode = undefined;
|
|
378
401
|
}
|
|
@@ -380,9 +403,11 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
380
403
|
if (!placeholder) {
|
|
381
404
|
this.#dataGrid.creationNode?.remove();
|
|
382
405
|
this.#dataGrid.creationNode = undefined;
|
|
383
|
-
} else if (!
|
|
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,145 +470,163 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
|
|
|
447
470
|
}
|
|
448
471
|
}
|
|
449
472
|
|
|
450
|
-
|
|
451
|
-
static #elementToNode = new WeakMap<Element, DataGridElementNode>();
|
|
452
|
-
#configElement: Element;
|
|
453
|
-
#dataGridElement: DataGridElement;
|
|
454
|
-
#addedClasses = new Set<string>();
|
|
455
|
-
constructor(configElement: Element, dataGridElement: DataGridElement) {
|
|
456
|
-
super();
|
|
457
|
-
this.#configElement = configElement;
|
|
458
|
-
DataGridElementNode.#elementToNode.set(configElement, this);
|
|
459
|
-
this.#dataGridElement = dataGridElement;
|
|
460
|
-
this.#updateData();
|
|
461
|
-
this.isCreationNode = hasBooleanAttribute(this.#configElement, 'placeholder');
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
static get(configElement: Element|undefined): DataGridElementNode|undefined {
|
|
465
|
-
return configElement && DataGridElementNode.#elementToNode.get(configElement);
|
|
466
|
-
}
|
|
473
|
+
type Constructor<T = object> = new (...args: any[]) => T;
|
|
467
474
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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>();
|
|
471
481
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
const column = this.#dataGridElement.columns[i];
|
|
480
|
-
if (column.dataType === DataType.BOOLEAN) {
|
|
481
|
-
this.data[column.id] = hasBooleanAttribute(cell, 'data-value') || cell.textContent === 'true';
|
|
482
|
-
} else {
|
|
483
|
-
this.data[column.id] = cell.dataset.value ?? cell.textContent ?? '';
|
|
484
|
-
}
|
|
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');
|
|
485
487
|
}
|
|
486
|
-
}
|
|
487
488
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
element.addEventListener('click', this.#onRowMouseEvent.bind(this));
|
|
491
|
-
element.addEventListener('mouseenter', this.#onRowMouseEvent.bind(this));
|
|
492
|
-
element.addEventListener('mouseleave', this.#onRowMouseEvent.bind(this));
|
|
493
|
-
if (this.#configElement.hasAttribute('style')) {
|
|
494
|
-
element.setAttribute('style', this.#configElement.getAttribute('style') || '');
|
|
489
|
+
get configElement(): Element {
|
|
490
|
+
return this.#configElement;
|
|
495
491
|
}
|
|
496
|
-
for (const classToAdd of this.#configElement.classList) {
|
|
497
|
-
element.classList.add(classToAdd);
|
|
498
|
-
}
|
|
499
|
-
return element;
|
|
500
|
-
}
|
|
501
492
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
super.refresh();
|
|
505
|
-
const existingElement = this.existingElement();
|
|
506
|
-
if (!existingElement) {
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
if (this.#configElement.hasAttribute('style')) {
|
|
510
|
-
existingElement.setAttribute('style', this.#configElement.getAttribute('style') || '');
|
|
493
|
+
get dataGridElement(): DataGridElement {
|
|
494
|
+
return this.#dataGridElement;
|
|
511
495
|
}
|
|
512
|
-
|
|
513
|
-
|
|
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
|
+
}
|
|
514
511
|
}
|
|
515
|
-
|
|
516
|
-
|
|
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;
|
|
517
525
|
}
|
|
518
|
-
}
|
|
519
526
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
+
}
|
|
525
543
|
}
|
|
526
544
|
|
|
527
|
-
|
|
528
|
-
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
|
+
}
|
|
529
555
|
}
|
|
530
|
-
}
|
|
531
556
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
+
}
|
|
540
566
|
}
|
|
541
567
|
}
|
|
542
|
-
}
|
|
543
568
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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);
|
|
548
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
|
+
|
|
549
601
|
return cell;
|
|
550
602
|
}
|
|
551
|
-
const cell = this.createTD(columnId);
|
|
552
|
-
cell.setAttribute('part', `${columnId}-column`);
|
|
553
|
-
const configCells = [...this.#configElement.children].filter(c => c.tagName === 'TD') as HTMLTableCellElement[];
|
|
554
|
-
const configCell = configCells[index];
|
|
555
|
-
if (this.isCreationNode && !configCell) {
|
|
556
|
-
return cell;
|
|
557
|
-
}
|
|
558
|
-
if (!configCell) {
|
|
559
|
-
throw new Error(`Column ${columnId} not found in the data grid`);
|
|
560
|
-
}
|
|
561
|
-
for (const child of configCell.childNodes) {
|
|
562
|
-
cell.appendChild(child.cloneNode(true));
|
|
563
|
-
}
|
|
564
|
-
for (const cssClass of configCell.classList) {
|
|
565
|
-
cell.classList.add(cssClass);
|
|
566
|
-
}
|
|
567
|
-
cell.title = configCell.title;
|
|
568
|
-
if (configCell.hasAttribute('aria-label')) {
|
|
569
|
-
this.setCellAccessibleName(configCell.getAttribute('aria-label') || '', cell, columnId);
|
|
570
|
-
}
|
|
571
|
-
const style = configCell.getAttribute('style');
|
|
572
|
-
if (style !== null) {
|
|
573
|
-
cell.setAttribute('style', style);
|
|
574
|
-
}
|
|
575
603
|
|
|
576
|
-
|
|
577
|
-
|
|
604
|
+
override deselect(): void {
|
|
605
|
+
super.deselect();
|
|
606
|
+
if (this.isCreationNode) {
|
|
607
|
+
this.#dataGridElement.dispatchEvent(new CustomEvent('create', {detail: this.data}));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
}
|
|
578
612
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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);
|
|
582
620
|
}
|
|
621
|
+
}
|
|
583
622
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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);
|
|
589
630
|
}
|
|
590
631
|
}
|
|
591
632
|
|
|
@@ -633,7 +674,7 @@ class IfExpandedDirective extends Lit.Directive.Directive {
|
|
|
633
674
|
if (!(element instanceof HTMLElement)) {
|
|
634
675
|
return false;
|
|
635
676
|
}
|
|
636
|
-
const node =
|
|
677
|
+
const node = getNode(element.closest('tr') ?? undefined);
|
|
637
678
|
if (!node) {
|
|
638
679
|
return false;
|
|
639
680
|
}
|
|
@@ -641,3 +682,15 @@ class IfExpandedDirective extends Lit.Directive.Directive {
|
|
|
641
682
|
}
|
|
642
683
|
}
|
|
643
684
|
export const ifExpanded = Lit.Directive.directive(IfExpandedDirective);
|
|
685
|
+
|
|
686
|
+
function getNode(element: Element|undefined): DataGridElementNode|undefined {
|
|
687
|
+
return element ? elementToNode.get(element) : undefined;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function removeNode(node: DataGridElementNode): void {
|
|
691
|
+
const configElement = node.configElement;
|
|
692
|
+
if (configElement) {
|
|
693
|
+
elementToNode.delete(configElement);
|
|
694
|
+
}
|
|
695
|
+
node.remove();
|
|
696
|
+
}
|
|
@@ -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};
|
|
@@ -81,7 +81,14 @@ export class CustomPreviewSection {
|
|
|
81
81
|
return document.createTextNode(String(jsonML));
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
if (jsonML[0] !== 'object') {
|
|
85
|
+
return this.renderElement(jsonML);
|
|
86
|
+
}
|
|
87
|
+
if (jsonML.length !== 2) {
|
|
88
|
+
Common.Console.Console.instance().error('Broken formatter: object reference must contain exactly two elements');
|
|
89
|
+
return document.createElement('span');
|
|
90
|
+
}
|
|
91
|
+
return this.layoutObjectTag(jsonML);
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
|