This is an automated email from the ASF dual-hosted git repository.

vogievetsky pushed a commit to branch explore_source
in repository https://gitbox.apache.org/repos/asf/druid.git

commit bd72d0f1fe9343091fdabbfa1c7c2606071a268e
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Mon Aug 12 22:18:30 2024 -0700

    bar chart better
---
 .../_save/tiles/filter-tile/filter-tile.tsx        |  80 -------
 .../tiles/hello-world-tile/hello-world-tile.scss   |  24 --
 .../tiles/hello-world-tile/hello-world-tile.tsx    |  45 ----
 web-console/_save/tiles/index.ts                   |  23 --
 .../_save/tiles/picker-tile/picker-tile.scss       |  24 --
 .../_save/tiles/picker-tile/picker-tile.tsx        |  70 ------
 web-console/_save/tiles/table-tile/table-tile.scss |  23 --
 web-console/_save/tiles/table-tile/table-tile.tsx  |  94 --------
 .../src/components/header-bar/header-bar.tsx       |   9 -
 web-console/src/console-application.tsx            |   6 -
 web-console/src/modules/bar-chart-module.tsx       | 206 ++++++++---------
 web-console/src/modules/module-pane.tsx            |   8 +-
 web-console/src/utils/local-storage-keys.tsx       |   2 -
 .../src/views/explore-view/explore-view.tsx        |   7 +-
 web-console/src/views/index.ts                     |   1 -
 web-console/src/views/tiles-view/common.ts         |  23 --
 .../edit-tile-dialog/edit-tile-dialog.tsx          | 118 ----------
 web-console/src/views/tiles-view/tiles-view.scss   |  36 ---
 web-console/src/views/tiles-view/tiles-view.tsx    | 257 ---------------------
 .../src/views/tiles-view/utils/inline-state.ts     |  41 ----
 20 files changed, 110 insertions(+), 987 deletions(-)

diff --git a/web-console/_save/tiles/filter-tile/filter-tile.tsx 
b/web-console/_save/tiles/filter-tile/filter-tile.tsx
deleted file mode 100644
index 92220f9cd98..00000000000
--- a/web-console/_save/tiles/filter-tile/filter-tile.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { SqlExpression, SqlLiteral, SqlQuery } from '@druid-toolkit/query';
-import React from 'react';
-
-import { Loader } from '../../../../components';
-import { useQueryManager } from '../../../../hooks';
-import { FilterPane } from '../../../explore-view/filter-pane/filter-pane';
-import type { QuerySource } from '../../../explore-view/utils';
-import { TileRepository } from '../../tile-repository';
-
-// import './overall-tile.scss';
-
-interface FilterTileConfig {
-  source: string;
-}
-
-TileRepository.registerTile<FilterTileConfig>({
-  type: 'filter',
-  title: 'Filter',
-  description: 'Shows the count',
-  parameterDefinitions: {
-    source: {
-      type: 'string',
-      // required: true,
-    },
-  },
-  component: function FilterTile(props) {
-    const { config, myPublicState, setPublicState, runSqlQuery } = props;
-    const { source } = config;
-
-    const [querySourceState] = useQueryManager<string, QuerySource>({
-      query: source,
-      processQuery: async source => {
-        const r = await runSqlQuery(`SELECT * FROM (${source}) LIMIT 0`);
-
-        return {
-          query: SqlQuery.parse(source),
-          columns: r.header.map(c => {
-            return {
-              expression: SqlLiteral.NULL,
-              name: c.name,
-              sqlType: c.sqlType,
-            };
-          }),
-        };
-      },
-    });
-
-    if (querySourceState.loading) return <Loader />;
-
-    const filter = SqlExpression.maybeParse(myPublicState['filter']) || 
SqlLiteral.TRUE;
-    return (
-      <div className="filter-tile" style={{ marginTop: '22px' }}>
-        <FilterPane
-          querySource={querySourceState.data}
-          filter={filter}
-          onFilterChange={f => setPublicState('filter', String(f))}
-          runSqlQuery={runSqlQuery}
-        />
-      </div>
-    );
-  },
-});
diff --git a/web-console/_save/tiles/hello-world-tile/hello-world-tile.scss 
b/web-console/_save/tiles/hello-world-tile/hello-world-tile.scss
deleted file mode 100644
index 10a4fd21457..00000000000
--- a/web-console/_save/tiles/hello-world-tile/hello-world-tile.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-.hello-world-tile {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  font-size: 26px;
-}
diff --git a/web-console/_save/tiles/hello-world-tile/hello-world-tile.tsx 
b/web-console/_save/tiles/hello-world-tile/hello-world-tile.tsx
deleted file mode 100644
index 37b7ee08124..00000000000
--- a/web-console/_save/tiles/hello-world-tile/hello-world-tile.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import React from 'react';
-
-import { TileRepository } from '../../tile-repository';
-
-import './hello-world-tile.scss';
-
-interface HelloWorldTileConfig {
-  alt?: string;
-}
-
-TileRepository.registerTile<HelloWorldTileConfig>({
-  type: 'hello-world',
-  title: 'Hello World',
-  description: 'Just a simple hello world',
-  configFields: [
-    {
-      name: 'alt',
-      type: 'string',
-      defaultValue: 'World',
-    },
-  ],
-  component: function HelloWorldModule(props) {
-    const { config, setPublicState } = props;
-    setPublicState('x', 'Hi there');
-    return <div className="hello-world-tile">{`Hello ${config.alt ?? 
'World'}`}</div>;
-  },
-});
diff --git a/web-console/_save/tiles/index.ts b/web-console/_save/tiles/index.ts
deleted file mode 100644
index 2ab33beb5bf..00000000000
--- a/web-console/_save/tiles/index.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import './hello-world-tile/hello-world-tile';
-import './overall-tile/overall-tile';
-import './picker-tile/picker-tile';
-import './filter-tile/filter-tile';
-import './table-tile/table-tile';
diff --git a/web-console/_save/tiles/picker-tile/picker-tile.scss 
b/web-console/_save/tiles/picker-tile/picker-tile.scss
deleted file mode 100644
index cb35dea9222..00000000000
--- a/web-console/_save/tiles/picker-tile/picker-tile.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-.picker-tile {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  font-size: 26px;
-}
diff --git a/web-console/_save/tiles/picker-tile/picker-tile.tsx 
b/web-console/_save/tiles/picker-tile/picker-tile.tsx
deleted file mode 100644
index c7a43373678..00000000000
--- a/web-console/_save/tiles/picker-tile/picker-tile.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { Button, Menu, MenuItem } from '@blueprintjs/core';
-import { IconNames } from '@blueprintjs/icons';
-import { Popover2 } from '@blueprintjs/popover2';
-import React from 'react';
-
-import { TileRepository } from '../../tile-repository';
-
-import './picker-tile.scss';
-
-interface OverallTileConfig {
-  options: string[];
-}
-
-TileRepository.registerTile<OverallTileConfig>({
-  type: 'picker',
-  title: 'Picker',
-  description: 'Shows the count',
-  parameterDefinitions: {
-    options: {
-      type: 'json',
-      defaultValue: [],
-      required: true,
-    },
-  },
-  component: function PickerModule(props) {
-    const { config, myPublicState, setPublicState } = props;
-    const { options } = config;
-
-    return (
-      <div className="picker-tile">
-        <Popover2
-          content={
-            <Menu>
-              {options.map((option, i) => (
-                <MenuItem
-                  key={i}
-                  text={option}
-                  onClick={() => setPublicState('selected', option)}
-                />
-              ))}
-            </Menu>
-          }
-        >
-          <Button
-            text={`Pick: ${myPublicState['selected'] ?? '?'}`}
-            rightIcon={IconNames.CARET_DOWN}
-          />
-        </Popover2>
-      </div>
-    );
-  },
-});
diff --git a/web-console/_save/tiles/table-tile/table-tile.scss 
b/web-console/_save/tiles/table-tile/table-tile.scss
deleted file mode 100644
index 8b0240fed27..00000000000
--- a/web-console/_save/tiles/table-tile/table-tile.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-.table-tile {
-  .generic-output-table {
-    height: 100%;
-  }
-}
diff --git a/web-console/_save/tiles/table-tile/table-tile.tsx 
b/web-console/_save/tiles/table-tile/table-tile.tsx
deleted file mode 100644
index dc3db33f7dd..00000000000
--- a/web-console/_save/tiles/table-tile/table-tile.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { sql } from '@druid-toolkit/query';
-import React, { useMemo } from 'react';
-
-import { Issue, Loader } from '../../../../components';
-import { useQueryManager } from '../../../../hooks';
-import { GenericOutputTable } from '../../../explore-view/modules/components';
-import { TileRepository } from '../../tile-repository';
-import { inlineState } from '../../utils/inline-state';
-
-import './table-tile.scss';
-
-interface TableTileConfig {
-  source: string;
-  splitColumn: string;
-}
-
-TileRepository.registerTile<TableTileConfig>({
-  type: 'table',
-  title: 'Table',
-  description: 'A universal table',
-  configFields: [
-    {
-      name: 'source',
-      type: 'string',
-      required: true,
-    },
-    {
-      name: 'splitColumn',
-      type: 'string',
-      required: true,
-    },
-  ],
-  component: function TableTile(props) {
-    const { config, publicState, setPublicState, runSqlQuery } = props;
-    const { source, splitColumn } = config;
-
-    const query = useMemo(() => {
-      return String(sql`
-        SELECT
-          "${splitColumn}",
-          COUNT(*) AS "cnt"
-        FROM (${inlineState(source, publicState)})
-        GROUP BY 1
-        ORDER BY 2 DESC
-      `);
-    }, [source, publicState]);
-
-    const [resultState] = useQueryManager<string, any>({
-      query: query,
-      processQuery: async query => {
-        setPublicState('query', query);
-        return await runSqlQuery(query);
-      },
-    });
-
-    if (resultState.loading) return <Loader />;
-
-    const errorMessage = resultState.getErrorMessage();
-    if (errorMessage) return <Issue issue={errorMessage} />;
-
-    return (
-      <div className="table-tile">
-        {resultState.data && (
-          <GenericOutputTable
-            runeMode={false}
-            queryResult={resultState.data}
-            showTypeIcons={false}
-            onQueryAction={action => {
-              console.log(action);
-            }}
-          />
-        )}
-      </div>
-    );
-  },
-});
diff --git a/web-console/src/components/header-bar/header-bar.tsx 
b/web-console/src/components/header-bar/header-bar.tsx
index f9142d749ac..aed66798299 100644
--- a/web-console/src/components/header-bar/header-bar.tsx
+++ b/web-console/src/components/header-bar/header-bar.tsx
@@ -70,7 +70,6 @@ export type HeaderActiveTab =
   | 'workbench'
   | 'sql-data-loader'
   | 'explore'
-  | 'tiles'
   | 'lookups';
 
 const DruidLogo = React.memo(function DruidLogo() {
@@ -159,14 +158,6 @@ export const HeaderBar = React.memo(function 
HeaderBar(props: HeaderBarProps) {
         disabled={!capabilities.hasSql()}
         selected={active === 'explore'}
       />
-      <MenuItem
-        icon={IconNames.APPLICATIONS}
-        text="Tiles"
-        label="(experimental)"
-        href="#tiles"
-        disabled={!capabilities.hasSql()}
-        selected={active === 'tiles'}
-      />
     </Menu>
   );
 
diff --git a/web-console/src/console-application.tsx 
b/web-console/src/console-application.tsx
index 9aa477d920d..36a0b8aa392 100644
--- a/web-console/src/console-application.tsx
+++ b/web-console/src/console-application.tsx
@@ -44,7 +44,6 @@ import {
   SqlDataLoaderView,
   SupervisorsView,
   TasksView,
-  TilesView,
   WorkbenchView,
 } from './views';
 
@@ -440,10 +439,6 @@ export class ConsoleApplication extends 
React.PureComponent<
     return this.wrapInViewContainer('explore', <ExploreView />, 'thinner');
   };
 
-  private readonly wrappedTilesView = () => {
-    return this.wrapInViewContainer('tiles', <TilesView />, 'thinner');
-  };
-
   render() {
     const { capabilities, capabilitiesLoading } = this.state;
 
@@ -504,7 +499,6 @@ export class ConsoleApplication extends React.PureComponent<
               {capabilities.hasSql() && (
                 <Route path="/explore" component={this.wrappedExploreView} />
               )}
-              {capabilities.hasSql() && <Route path="/tiles" 
component={this.wrappedTilesView} />}
 
               <Route component={this.wrappedHomeView} />
             </Switch>
diff --git a/web-console/src/modules/bar-chart-module.tsx 
b/web-console/src/modules/bar-chart-module.tsx
index ab72c78e391..a1c2b0dc1b8 100644
--- a/web-console/src/modules/bar-chart-module.tsx
+++ b/web-console/src/modules/bar-chart-module.tsx
@@ -17,110 +17,18 @@
  */
 
 import { L, SqlExpression, SqlQuery } from '@druid-toolkit/query';
+import type { ECharts } from 'echarts';
 import * as echarts from 'echarts';
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect, useMemo, useRef } from 'react';
+
+import { useQueryManager } from '../hooks';
+import { highlightStore } from 
'../views/explore-view/highlight-store/highlight-store';
 
 import type { ExpressionMeta } from './models';
 import { ModuleRepository } from './module-repository/module-repository';
 
 import './record-table-module.scss';
 
-const barChartFn = ({ container, runSqlQuery }: any) => {
-  const myChart = echarts.init(container, 'dark');
-
-  myChart.setOption({
-    tooltip: {},
-    dataset: {
-      sourceHeader: false,
-      dimensions: ['dim', 'met'],
-      source: [],
-    },
-    xAxis: {
-      type: 'category',
-      axisLabel: { interval: 0, rotate: -30 },
-    },
-    yAxis: {},
-    series: [
-      {
-        type: 'bar',
-        encode: {
-          x: 'dim',
-          y: 'met',
-        },
-      },
-    ],
-  });
-
-  return {
-    async update({ source, parameterValues }: any) {
-      const { splitColumn, metric, metricToSort, limit } = parameterValues;
-
-      myChart.off('click');
-
-      const splitExpression = splitColumn ? splitColumn.expression : 
L('Overall');
-
-      const v = await runSqlQuery(
-        SqlQuery.from(source)
-          .addSelect(splitExpression.as('dim'), { addToGroupBy: 'end' })
-          .addSelect(metric.expression.as('met'), {
-            addToOrderBy: metricToSort ? undefined : 'end',
-            direction: 'DESC',
-          })
-          .applyIf(metricToSort, q =>
-            q.addOrderBy(metricToSort.expression.toOrderByExpression('DESC')),
-          )
-          .changeLimitValue(limit),
-      );
-      myChart.setOption({
-        dataset: {
-          source: v.toObjectArray(),
-        },
-      });
-
-      myChart.on('click', 'series', p => {
-        const { dim, met } = p.data as any;
-
-        const [x, y] = myChart.convertToPixel({ seriesIndex: 0 }, [dim, met]);
-
-        console.log(x, y);
-        // highlightStore.getState().setHighlight({
-        //   label: p.name,
-        //   x,
-        //   y: y - 20,
-        //   data: [dim, met],
-        //   onDrop: () => {
-        //     highlightStore.getState().dropHighlight();
-        //   },
-        //   onSave: () => {
-        //     
updateWhere(where.toggleClauseInWhere(C(splitColumn.name).equal(p.name)));
-        //     highlightStore.getState().dropHighlight();
-        //   },
-        // });
-      });
-    },
-
-    resize() {
-      myChart.resize();
-
-      // // if there is a highlight, update its x position
-      // // by calculating new pixel position from the highlight's data
-      // const highlight = highlightStore.getState().highlight;
-      // if (highlight) {
-      //   const [x, y] = myChart.convertToPixel({ seriesIndex: 0 }, 
highlight.data as number[]);
-      //
-      //   highlightStore.getState().updateHighlight({
-      //     x,
-      //     y: y - 20,
-      //   });
-      // }
-    },
-
-    destroy() {
-      myChart.dispose();
-    },
-  };
-};
-
 interface RecordTableParameterValues {
   splitColumn: ExpressionMeta;
   metric: ExpressionMeta;
@@ -166,20 +74,108 @@ 
ModuleRepository.registerModule<RecordTableParameterValues>({
     },
   },
   component: function BarChartModule(props) {
-    const updateFn = useRef<any>(null);
-    const { querySource, parameterValues, runSqlQuery } = props;
+    const { querySource, parameterValues, stage, runSqlQuery } = props;
+    const chartRef = useRef<ECharts>();
+
+    const dataQuery = useMemo(() => {
+      const source = querySource.query;
+      const { splitColumn, metric, metricToSort, limit } = parameterValues;
+
+      const splitExpression = splitColumn ? splitColumn.expression : 
L('Overall');
+
+      return SqlQuery.from(source)
+        .addSelect(splitExpression.as('dim'), { addToGroupBy: 'end' })
+        .addSelect(metric.expression.as('met'), {
+          addToOrderBy: metricToSort ? undefined : 'end',
+          direction: 'DESC',
+        })
+        .applyIf(metricToSort, q =>
+          q.addOrderBy(metricToSort.expression.toOrderByExpression('DESC')),
+        )
+        .changeLimitValue(limit);
+    }, [querySource, parameterValues]);
+
+    const [dataState] = useQueryManager({
+      query: dataQuery,
+      processQuery: async (query: SqlQuery) => {
+        return (await runSqlQuery(query)).toObjectArray();
+      },
+    });
+
+    useEffect(() => {
+      const myChart = chartRef.current;
+      const data = dataState.data;
+      if (!myChart || !data) return;
+
+      myChart.off('click');
+
+      myChart.setOption({
+        dataset: {
+          source: data,
+        },
+      });
+
+      myChart.on('click', 'series', p => {
+        const { dim, met } = p.data as any;
+
+        const [x, y] = myChart.convertToPixel({ seriesIndex: 0 }, [dim, met]);
+
+        console.log(x, y);
+        highlightStore.getState().setHighlight({
+          label: p.name,
+          x,
+          y: y - 20,
+          data: [dim, met],
+          onDrop: () => {
+            highlightStore.getState().dropHighlight();
+          },
+          onSave: () => {
+            // 
updateWhere(where.toggleClauseInWhere(C(splitColumn.name).equal(p.name)));
+            console.log('onSave');
+            highlightStore.getState().dropHighlight();
+          },
+        });
+      });
+    }, [chartRef.current, dataState.data]);
 
     useEffect(() => {
-      if (!updateFn.current) return;
-      updateFn.current.update({ source: querySource.query, parameterValues });
-    }, [updateFn.current, querySource, parameterValues]);
+      const myChart = chartRef.current;
+      if (!myChart) return;
+      myChart.resize();
+    }, [stage]);
 
     return (
       <div
         className="bar-chart-module"
-        ref={x => {
-          if (updateFn.current) return;
-          updateFn.current = barChartFn({ container: x, runSqlQuery });
+        ref={container => {
+          if (chartRef.current) return;
+
+          const myChart = echarts.init(container, 'dark');
+
+          myChart.setOption({
+            tooltip: {},
+            dataset: {
+              sourceHeader: false,
+              dimensions: ['dim', 'met'],
+              source: [],
+            },
+            xAxis: {
+              type: 'category',
+              axisLabel: { interval: 0, rotate: -30 },
+            },
+            yAxis: {},
+            series: [
+              {
+                type: 'bar',
+                encode: {
+                  x: 'dim',
+                  y: 'met',
+                },
+              },
+            ],
+          });
+
+          chartRef.current = myChart;
         }}
         style={{ height: '100%' }}
       />
diff --git a/web-console/src/modules/module-pane.tsx 
b/web-console/src/modules/module-pane.tsx
index e1ec33ab74f..a7cc32f94d3 100644
--- a/web-console/src/modules/module-pane.tsx
+++ b/web-console/src/modules/module-pane.tsx
@@ -19,7 +19,7 @@
 import { Button, Menu, MenuItem, Popover, ResizeSensor } from 
'@blueprintjs/core';
 import { IconNames } from '@blueprintjs/icons';
 import type { QueryResult, SqlExpression, SqlQuery } from 
'@druid-toolkit/query';
-import React, { useState } from 'react';
+import React, { forwardRef, useState } from 'react';
 
 import { Issue } from '../components';
 
@@ -55,7 +55,7 @@ export interface ModulePaneProps {
   onEdit?: () => void;
 }
 
-export const ModulePane = function ModulePane(props: ModulePaneProps) {
+export const ModulePane = forwardRef<any, any>(function ModulePane(props: 
ModulePaneProps, ref) {
   const {
     moduleId,
     moduleName,
@@ -101,7 +101,7 @@ export const ModulePane = function ModulePane(props: 
ModulePaneProps) {
         setStage(newStage);
       }}
     >
-      <div className="module-pane">
+      <div className="module-pane" ref={ref}>
         {content}
         {onEdit && (
           <Button className="edit-button" icon={IconNames.EDIT} 
onClick={onEdit} minimal small />
@@ -119,4 +119,4 @@ export const ModulePane = function ModulePane(props: 
ModulePaneProps) {
       </div>
     </ResizeSensor>
   );
-};
+});
diff --git a/web-console/src/utils/local-storage-keys.tsx 
b/web-console/src/utils/local-storage-keys.tsx
index 7211d77cb34..d4efec06e22 100644
--- a/web-console/src/utils/local-storage-keys.tsx
+++ b/web-console/src/utils/local-storage-keys.tsx
@@ -57,8 +57,6 @@ export const LocalStorageKeys = {
   SQL_DATA_LOADER_CONTENT: 'sql-data-loader-content' as const,
 
   EXPLORE_STATE: 'explore-state' as const,
-
-  TILES: 'tiles' as const,
 };
 export type LocalStorageKeys = (typeof LocalStorageKeys)[keyof typeof 
LocalStorageKeys];
 
diff --git a/web-console/src/views/explore-view/explore-view.tsx 
b/web-console/src/views/explore-view/explore-view.tsx
index 4246f4e16cd..15637657b7f 100644
--- a/web-console/src/views/explore-view/explore-view.tsx
+++ b/web-console/src/views/explore-view/explore-view.tsx
@@ -163,6 +163,7 @@ async function introspectSource(source: string): 
Promise<QuerySource> {
 export const ExploreView = React.memo(function ExploreView() {
   const [shownText, setShownText] = useState<string | undefined>();
   const filterPane = useRef<{ filterOn(column: Column): void }>();
+  const containerRef = useRef<HTMLDivElement | null>(null);
 
   const [exploreState, setExploreState] = useLocalStorageState<ExploreState>(
     LocalStorageKeys.EXPLORE_STATE,
@@ -237,6 +238,8 @@ export const ExploreView = React.memo(function 
ExploreView() {
     );
   }
 
+  console.log('containerRef.current', containerRef.current);
+
   const querySource = querySourceState.getSomeData();
 
   const effectiveShowFullSource = showFullSource || parsedError;
@@ -251,7 +254,7 @@ export const ExploreView = React.memo(function 
ExploreView() {
       )}
       {parsedError && <div className="source-error">{`Source error: 
${parsedError}`}</div>}
       {parsedSource && (
-        <div className="explore-container">
+        <div className="explore-container" ref={containerRef}>
           <SourcePane
             selectedSource={parsedSource}
             onSelectedSourceChange={setSource}
@@ -426,7 +429,7 @@ export const ExploreView = React.memo(function 
ExploreView() {
           )}
         </div>
       )}
-      <HighlightBubble referenceContainer={null} />
+      <HighlightBubble referenceContainer={containerRef.current} />
     </div>
   );
 });
diff --git a/web-console/src/views/index.ts b/web-console/src/views/index.ts
index bc413874dd2..51113dd91df 100644
--- a/web-console/src/views/index.ts
+++ b/web-console/src/views/index.ts
@@ -26,5 +26,4 @@ export * from './services-view/services-view';
 export * from './sql-data-loader-view/sql-data-loader-view';
 export * from './supervisors-view/supervisors-view';
 export * from './tasks-view/tasks-view';
-export * from './tiles-view/tiles-view';
 export * from './workbench-view/workbench-view';
diff --git a/web-console/src/views/tiles-view/common.ts 
b/web-console/src/views/tiles-view/common.ts
deleted file mode 100644
index 30b3e2f17a2..00000000000
--- a/web-console/src/views/tiles-view/common.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-export interface ModuleTileConfig {
-  moduleName: string;
-  moduleId: string;
-  parameterValues: Record<string, any>;
-}
diff --git 
a/web-console/src/views/tiles-view/edit-tile-dialog/edit-tile-dialog.tsx 
b/web-console/src/views/tiles-view/edit-tile-dialog/edit-tile-dialog.tsx
deleted file mode 100644
index 405b50fc5d9..00000000000
--- a/web-console/src/views/tiles-view/edit-tile-dialog/edit-tile-dialog.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { Button, Classes, Dialog, Intent } from '@blueprintjs/core';
-import React, { useState } from 'react';
-
-import type { FormJsonTabs } from '../../../components';
-import { AutoForm, FormJsonSelector, JsonInput } from '../../../components';
-import { ModuleRepository } from 
'../../../modules/module-repository/module-repository';
-import type { ModuleTileConfig } from '../common';
-
-export interface EditTileDialogProps {
-  initTileConfig: ModuleTileConfig | undefined;
-  onSave(tileConfig: ModuleTileConfig): void;
-  onClose(): void;
-}
-
-export const EditTileDialog = React.memo(function EditTileDialog(props: 
EditTileDialogProps) {
-  const { initTileConfig, onSave, onClose } = props;
-
-  const [currentTab, setCurrentTab] = useState<FormJsonTabs>('form');
-  const [currentModuleTileConfig, setCurrentModuleTileConfig] = 
useState<Partial<ModuleTileConfig>>(
-    initTileConfig || { moduleName: `t${String(Math.random()).slice(2, 3)}}` },
-  );
-  const [jsonError, setJsonError] = useState<Error | undefined>();
-
-  const moduleDefinition =
-    typeof currentModuleTileConfig.moduleId === 'string'
-      ? ModuleRepository.getModule(currentModuleTileConfig.moduleId)
-      : undefined;
-
-  // const issueWithCurrentTileConfig = moduleDefinition
-  //   ? AutoForm.issueWithModel(currentModuleTileConfig.config, 
moduleDefinition.configFields)
-  //   : 'no tile';
-
-  const issueWithCurrentTileConfig = moduleDefinition ? undefined : 'no tile';
-
-  console.log(jsonError, issueWithCurrentTileConfig);
-
-  return (
-    <Dialog
-      className="edit-tile-dialog"
-      isOpen
-      onClose={onClose}
-      canOutsideClickClose={false}
-      title="Edit tile"
-    >
-      <div className={Classes.DIALOG_BODY}>
-        <FormJsonSelector
-          tab={currentTab}
-          onChange={t => {
-            setJsonError(undefined);
-            setCurrentTab(t);
-          }}
-        />
-        <div className="content">
-          {currentTab === 'form' ? (
-            <>
-              <AutoForm
-                fields={[
-                  {
-                    name: 'moduleName',
-                    type: 'string',
-                  },
-                  {
-                    name: 'moduleId',
-                    type: 'string',
-                    suggestions: ModuleRepository.getAllModuleIds(),
-                    required: true,
-                  },
-                ]}
-                model={currentModuleTileConfig}
-                onChange={setCurrentModuleTileConfig}
-              />
-              ToDo: fill this in
-            </>
-          ) : (
-            <JsonInput
-              value={currentModuleTileConfig}
-              onChange={setCurrentModuleTileConfig}
-              setError={setJsonError}
-              height="100%"
-            />
-          )}
-        </div>
-      </div>
-      <div className={Classes.DIALOG_FOOTER}>
-        <div className={Classes.DIALOG_FOOTER_ACTIONS}>
-          <Button text="Close" onClick={onClose} />
-          <Button
-            text="Save"
-            intent={Intent.PRIMARY}
-            disabled={Boolean(jsonError || issueWithCurrentTileConfig)}
-            onClick={() => {
-              onSave(currentModuleTileConfig as any);
-              onClose();
-            }}
-          />
-        </div>
-      </div>
-    </Dialog>
-  );
-});
diff --git a/web-console/src/views/tiles-view/tiles-view.scss 
b/web-console/src/views/tiles-view/tiles-view.scss
deleted file mode 100644
index aea0286ad18..00000000000
--- a/web-console/src/views/tiles-view/tiles-view.scss
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-.tiles-view {
-  position: relative;
-  height: 100%;
-  width: 100%;
-
-  .control-button {
-    position: absolute;
-    bottom: 5px;
-    right: 5px;
-  }
-
-  .tiles-container {
-    display: grid;
-    grid-template-columns: 1fr 1fr;
-    grid-template-rows: 400px 400px 400px;
-    gap: 10px;
-  }
-}
diff --git a/web-console/src/views/tiles-view/tiles-view.tsx 
b/web-console/src/views/tiles-view/tiles-view.tsx
deleted file mode 100644
index d67dc942b69..00000000000
--- a/web-console/src/views/tiles-view/tiles-view.tsx
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { Button, Intent, Menu, MenuItem, Popover, Position } from 
'@blueprintjs/core';
-import { IconNames } from '@blueprintjs/icons';
-import type { QueryResult } from '@druid-toolkit/query';
-import { L, QueryRunner, sql, SqlLiteral, SqlQuery, SqlTable, T } from 
'@druid-toolkit/query';
-import React from 'react';
-
-import { SpecDialog } from '../../dialogs';
-import { QuerySource } from '../../modules';
-import { ModulePane } from '../../modules/module-pane';
-import { Api } from '../../singletons';
-import {
-  deepGet,
-  localStorageGetJson,
-  LocalStorageKeys,
-  localStorageSetJson,
-  queryDruidSql,
-  removeObjectKey,
-} from '../../utils';
-
-import type { ModuleTileConfig } from './common';
-import { EditTileDialog } from './edit-tile-dialog/edit-tile-dialog';
-
-import './tiles-view.scss';
-
-// micro-cache
-const MAX_TIME_TTL = 60000;
-let lastMaxTimeTable: string | undefined;
-let lastMaxTimeValue: Date | undefined;
-let lastMaxTimeTimestamp = 0;
-
-async function getMaxTimeForTable(tableName: string): Promise<Date | 
undefined> {
-  // micro-cache get
-  if (
-    lastMaxTimeTable === tableName &&
-    lastMaxTimeValue &&
-    Date.now() < lastMaxTimeTimestamp + MAX_TIME_TTL
-  ) {
-    return lastMaxTimeValue;
-  }
-
-  const d = await queryDruidSql({
-    query: sql`SELECT MAX(__time) AS "maxTime" FROM ${T(tableName)}`,
-  });
-
-  const maxTime = new Date(deepGet(d, '0.maxTime'));
-  if (isNaN(maxTime.valueOf())) return;
-
-  // micro-cache set
-  lastMaxTimeTable = tableName;
-  lastMaxTimeValue = maxTime;
-  lastMaxTimeTimestamp = Date.now();
-
-  return maxTime;
-}
-
-function getFirstTableName(q: SqlQuery): string | undefined {
-  let tableName: string | undefined;
-  q.walk(ex => {
-    if (ex instanceof SqlTable) {
-      tableName = ex.getName();
-      return;
-    }
-    return ex;
-  });
-  return tableName;
-}
-
-const queryRunner = new QueryRunner({
-  inflateDateStrategy: 'none',
-  executor: async (payload, isSql, cancelToken) => {
-    if (isSql && payload.query.includes('MAX_DATA_TIME()')) {
-      const parsed = SqlQuery.parse(payload.query);
-      const tableName = getFirstTableName(parsed);
-      if (tableName) {
-        const maxTime = await getMaxTimeForTable(tableName);
-        if (maxTime) {
-          payload = {
-            ...payload,
-            query: payload.query.replace(/MAX_DATA_TIME\(\)/g, L(maxTime)),
-          };
-        }
-      }
-    }
-
-    console.debug('Running query:');
-    console.debug(payload.query ?? payload);
-    return Api.instance.post(`/druid/v2${isSql ? '/sql' : ''}`, payload, { 
cancelToken });
-  },
-});
-
-function runSqlQuery(query: string | SqlQuery): Promise<QueryResult> {
-  return queryRunner.runQuery({
-    query,
-  });
-}
-
-export interface TilesViewProps {}
-
-export interface TilesViewState {
-  tileConfigs: ModuleTileConfig[];
-  tilePublicState: Record<string, Record<string, any>>;
-  editTile?: {
-    tileConfig: ModuleTileConfig;
-    index: number;
-  };
-  showConfigs: boolean;
-}
-
-export class TilesView extends React.PureComponent<TilesViewProps, 
TilesViewState> {
-  constructor(props: TilesViewProps) {
-    super(props);
-
-    this.state = {
-      tileConfigs: localStorageGetJson(LocalStorageKeys.TILES) || [
-        { type: 'filter', moduleName: 'MainFilter', config: { source: 'SELECT 
* FROM wikipedia' } },
-        {
-          type: 'table',
-          moduleName: 'Table',
-          config: {
-            source: `SELECT * FROM wikipedia WHERE STATE('MainFilter', 
'filter', TRUE)`,
-            splitColumn: 'channel',
-          },
-        },
-        {
-          type: 'overall',
-          moduleName: 'Tile 3',
-          config: { source: `SELECT * FROM wikipedia WHERE STATE('MainFilter', 
'filter', TRUE)` },
-        },
-        { type: 'picker', moduleName: 'Tile 4', config: { options: ['A', 'B', 
'C'] } },
-        { type: 'hello-world', moduleName: 'Tile 2', config: { alt: 'Moon' } },
-      ],
-      tilePublicState: {},
-      showConfigs: false,
-    };
-  }
-
-  private handleTileConfigsChange(tileConfigs: ModuleTileConfig[]) {
-    localStorageSetJson(LocalStorageKeys.TILES, tileConfigs);
-    this.setState({ tileConfigs });
-  }
-
-  render() {
-    const { tileConfigs, tilePublicState, editTile, showConfigs } = this.state;
-
-    return (
-      <div className="tiles-view app-view">
-        <Popover
-          className="control-button"
-          position={Position.BOTTOM_LEFT}
-          content={
-            <Menu>
-              <MenuItem text="Show configs" onClick={() => this.setState({ 
showConfigs: true })} />
-              <MenuItem
-                text="Clear view local storage"
-                intent={Intent.DANGER}
-                onClick={() => {
-                  localStorageSetJson(LocalStorageKeys.TILES, null);
-                  window.location.reload();
-                }}
-              />
-            </Menu>
-          }
-        >
-          <Button icon={IconNames.COG} />
-        </Popover>
-        <div className="tiles-container">
-          {tileConfigs.map((moduleTileConfig, i) => (
-            <ModulePane
-              key={i}
-              moduleId={moduleTileConfig.moduleId}
-              moduleName={moduleTileConfig.moduleName}
-              querySource={new QuerySource(SqlQuery.parse('ToDo: ???'), [], 
[])}
-              where={SqlLiteral.TRUE}
-              parameterValues={moduleTileConfig.parameterValues}
-              publicState={tilePublicState}
-              setPublicState={(key, value) => {
-                setTimeout(() => {
-                  let { tilePublicState } = this.state;
-                  const myPublicState = 
tilePublicState[moduleTileConfig.moduleName] ?? {};
-                  const currentValue = myPublicState[key];
-                  if (value === currentValue) return;
-                  if (typeof value === 'undefined') {
-                    tilePublicState = {
-                      ...tilePublicState,
-                      [moduleTileConfig.moduleName]: 
removeObjectKey(myPublicState, key),
-                    };
-                  } else {
-                    tilePublicState = {
-                      ...tilePublicState,
-                      [moduleTileConfig.moduleName]: {
-                        ...myPublicState,
-                        [key]: value,
-                      },
-                    };
-                  }
-                  this.setState({ tilePublicState });
-                }, 1);
-              }}
-              runSqlQuery={runSqlQuery}
-              onEdit={() => {
-                this.setState({
-                  editTile: {
-                    tileConfig: moduleTileConfig,
-                    index: i,
-                  },
-                });
-              }}
-            />
-          ))}
-        </div>
-        {editTile && (
-          <EditTileDialog
-            initTileConfig={editTile.tileConfig}
-            onSave={newTileConfig => {
-              if (!editTile) return;
-              this.handleTileConfigsChange(
-                tileConfigs.map((t, i) => (i === editTile?.index ? 
newTileConfig : t)),
-              );
-            }}
-            onClose={() => this.setState({ editTile: undefined })}
-          />
-        )}
-        {showConfigs && (
-          <SpecDialog
-            title="Tile configs"
-            initSpec={tileConfigs}
-            onSubmit={v => {
-              if (!Array.isArray(v)) return;
-              this.setState({ tileConfigs: v });
-            }}
-            onClose={() => {
-              this.setState({ showConfigs: false });
-            }}
-          />
-        )}
-      </div>
-    );
-  }
-}
diff --git a/web-console/src/views/tiles-view/utils/inline-state.ts 
b/web-console/src/views/tiles-view/utils/inline-state.ts
deleted file mode 100644
index 467c152f2e5..00000000000
--- a/web-console/src/views/tiles-view/utils/inline-state.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { SqlExpression, SqlFunction, SqlLiteral } from '@druid-toolkit/query';
-
-export function inlineState(
-  expression: SqlExpression | string,
-  publicState: Readonly<Record<string, Readonly<Record<string, any>>>>,
-): SqlExpression {
-  return SqlExpression.parse(expression).walk(ex => {
-    if (ex instanceof SqlFunction && ex.getEffectiveFunctionName() === 
'STATE') {
-      const tileName = ex.getArgAsString(0);
-      if (!tileName) throw new Error('needs tile name');
-
-      const stateName = ex.getArgAsString(1);
-      if (!stateName) throw new Error('needs state name');
-
-      return (
-        SqlExpression.maybeParse(publicState[tileName]?.[stateName]) ||
-        ex.getArg(2) ||
-        SqlLiteral.NULL
-      ).ensureParens();
-    }
-    return ex;
-  }) as SqlExpression;
-}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to