xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.ExplorePage/explore_page.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2024 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import m from 'mithril';
16import {PageWithTraceAttrs} from '../../public/page';
17import {SqlTableState as SqlTableViewState} from '../../components/widgets/sql/table/state';
18import {SqlTable as SqlTableView} from '../../components/widgets/sql/table/table';
19import {exists} from '../../base/utils';
20import {Menu, MenuItem, MenuItemAttrs} from '../../widgets/menu';
21import {Button} from '../../widgets/button';
22import {Icons} from '../../base/semantic_icons';
23import {DetailsShell} from '../../widgets/details_shell';
24import {
25  Chart,
26  ChartOption,
27  createChartConfigFromSqlTableState,
28  renderChartComponent,
29} from '../../components/widgets/charts/chart';
30import {AddChartMenuItem} from '../../components/widgets/charts/add_chart_menu';
31import {
32  CollapsiblePanel,
33  CollapsiblePanelVisibility,
34} from '../../components/widgets/collapsible_panel';
35import {Trace} from '../../public/trace';
36import SqlModulesPlugin from '../dev.perfetto.SqlModules';
37
38export interface ExploreTableState {
39  sqlTableViewState?: SqlTableViewState;
40  selectedTableName?: string;
41}
42
43interface ExplorePageAttrs extends PageWithTraceAttrs {
44  readonly state: ExploreTableState;
45  readonly charts: Chart[];
46}
47
48export class ExplorePage implements m.ClassComponent<ExplorePageAttrs> {
49  private visibility = CollapsiblePanelVisibility.VISIBLE;
50
51  // Show menu with standard library tables
52  private renderSelectableTablesMenuItems(
53    trace: Trace,
54    state: ExploreTableState,
55  ): m.Vnode<MenuItemAttrs, unknown>[] {
56    const sqlModules = trace.plugins
57      .getPlugin(SqlModulesPlugin)
58      .getSqlModules();
59    return sqlModules.listTables().map((tableName) => {
60      const sqlTable = sqlModules
61        .getModuleForTable(tableName)
62        ?.getTable(tableName);
63      const sqlTableViewDescription = sqlModules
64        .getModuleForTable(tableName)
65        ?.getSqlTableDescription(tableName);
66
67      return m(MenuItem, {
68        label: tableName,
69        onclick: () => {
70          if (
71            (state.selectedTableName &&
72              tableName === state.selectedTableName) ||
73            sqlTable === undefined ||
74            sqlTableViewDescription === undefined
75          ) {
76            return;
77          }
78
79          state.selectedTableName = sqlTable.name;
80          state.sqlTableViewState = new SqlTableViewState(
81            trace,
82            {
83              name: tableName,
84              columns: sqlTable.getTableColumns(),
85            },
86            {imports: sqlTableViewDescription.imports},
87          );
88        },
89      });
90    });
91  }
92
93  private renderSqlTable(state: ExploreTableState, charts: Chart[]) {
94    const sqlTableViewState = state.sqlTableViewState;
95
96    if (sqlTableViewState === undefined) return;
97
98    const range = sqlTableViewState.getDisplayedRange();
99    const rowCount = sqlTableViewState.getTotalRowCount();
100
101    const navigation = [
102      exists(range) &&
103        exists(rowCount) &&
104        `Showing rows ${range.from}-${range.to} of ${rowCount}`,
105      m(Button, {
106        icon: Icons.GoBack,
107        disabled: !sqlTableViewState.canGoBack(),
108        onclick: () => sqlTableViewState!.goBack(),
109      }),
110      m(Button, {
111        icon: Icons.GoForward,
112        disabled: !sqlTableViewState.canGoForward(),
113        onclick: () => sqlTableViewState!.goForward(),
114      }),
115    ];
116
117    return m(
118      DetailsShell,
119      {
120        title: 'Explore Table',
121        buttons: navigation,
122        fillParent: false,
123      },
124      m(SqlTableView, {
125        state: sqlTableViewState,
126        addColumnMenuItems: (column, columnAlias) =>
127          m(AddChartMenuItem, {
128            chartConfig: createChartConfigFromSqlTableState(
129              column,
130              columnAlias,
131              sqlTableViewState,
132            ),
133            chartOptions: [ChartOption.HISTOGRAM],
134            addChart: (chart) => charts.push(chart),
135          }),
136      }),
137    );
138  }
139
140  view({attrs}: m.CVnode<ExplorePageAttrs>) {
141    const {trace, state, charts} = attrs;
142
143    return m(
144      '.page.explore-page',
145      m(
146        '.chart-container',
147        m(Menu, this.renderSelectableTablesMenuItems(trace, state)),
148      ),
149      m(
150        '.chart-container',
151        charts.map((chart) => renderChartComponent(chart)),
152      ),
153      state.selectedTableName &&
154        m(CollapsiblePanel, {
155          visibility: this.visibility,
156          setVisibility: (visibility) => {
157            this.visibility = visibility;
158          },
159          tabs: [this.renderSqlTable(state, charts)],
160        }),
161    );
162  }
163}
164