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 affb3db90ddff066670b65262a8249a0fad37b97 Author: Vadim Ogievetsky <[email protected]> AuthorDate: Tue Aug 13 12:06:51 2024 -0700 progress --- .../_save/modules/bar-chart-echarts-module.ts | 155 --------------------- web-console/src/modules/bar-chart-module.tsx | 38 +++-- web-console/src/modules/module-pane.tsx | 13 +- .../modules/module-repository/module-repository.ts | 1 + .../droppable-container/droppable-container.tsx | 67 ++++----- .../src/views/explore-view/explore-view.tsx | 15 +- 6 files changed, 77 insertions(+), 212 deletions(-) diff --git a/web-console/_save/modules/bar-chart-echarts-module.ts b/web-console/_save/modules/bar-chart-echarts-module.ts deleted file mode 100644 index 2e3bf8681d6..00000000000 --- a/web-console/_save/modules/bar-chart-echarts-module.ts +++ /dev/null @@ -1,155 +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 { C, SqlExpression } from '@druid-toolkit/query'; -import { typedVisualModule } from '@druid-toolkit/visuals-core'; -import * as echarts from 'echarts'; - -import { highlightStore } from '../highlight-store/highlight-store'; -import { getInitQuery } from '../utils'; - -export default typedVisualModule({ - parameters: { - splitColumn: { - type: 'column', - control: { - label: 'Bar column', - // transferGroup: 'show', - required: true, - }, - }, - metric: { - type: 'aggregate', - default: { expression: SqlExpression.parse('COUNT(*)'), name: 'Count', sqlType: 'BIGINT' }, - control: { - label: 'Metric to show', - // transferGroup: 'show-agg', - required: true, - }, - }, - metricToSort: { - type: 'aggregate', - control: { - label: 'Metric to sort (default to shown)', - }, - }, - limit: { - type: 'number', - default: 5, - control: { - label: 'Max bars to show', - required: true, - }, - }, - }, - module: ({ container, host, updateWhere }) => { - const { sqlQuery } = host; - 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({ table, where, parameterValues }) { - const { splitColumn, metric, metricToSort, limit } = parameterValues; - - myChart.off('click'); - - if (!splitColumn) return; - - const v = await sqlQuery( - getInitQuery(table, where) - .addSelect(splitColumn.expression.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]); - - 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(); - }, - }; - }, -}); diff --git a/web-console/src/modules/bar-chart-module.tsx b/web-console/src/modules/bar-chart-module.tsx index a1c2b0dc1b8..ef6bcb7419b 100644 --- a/web-console/src/modules/bar-chart-module.tsx +++ b/web-console/src/modules/bar-chart-module.tsx @@ -29,6 +29,8 @@ import { ModuleRepository } from './module-repository/module-repository'; import './record-table-module.scss'; +const OVERALL_LABEL = 'Overall'; + interface RecordTableParameterValues { splitColumn: ExpressionMeta; metric: ExpressionMeta; @@ -74,16 +76,17 @@ ModuleRepository.registerModule<RecordTableParameterValues>({ }, }, component: function BarChartModule(props) { - const { querySource, parameterValues, stage, runSqlQuery } = props; + const { querySource, where, setWhere, parameterValues, stage, runSqlQuery } = props; const chartRef = useRef<ECharts>(); + const { splitColumn, metric, metricToSort, limit } = parameterValues; + const dataQuery = useMemo(() => { const source = querySource.query; - const { splitColumn, metric, metricToSort, limit } = parameterValues; - - const splitExpression = splitColumn ? splitColumn.expression : L('Overall'); + const splitExpression = splitColumn ? splitColumn.expression : L(OVERALL_LABEL); return SqlQuery.from(source) + .addWhere(where) .addSelect(splitExpression.as('dim'), { addToGroupBy: 'end' }) .addSelect(metric.expression.as('met'), { addToOrderBy: metricToSort ? undefined : 'end', @@ -93,7 +96,7 @@ ModuleRepository.registerModule<RecordTableParameterValues>({ q.addOrderBy(metricToSort.expression.toOrderByExpression('DESC')), ) .changeLimitValue(limit); - }, [querySource, parameterValues]); + }, [querySource, where, splitColumn, metric, metricToSort, limit]); const [dataState] = useQueryManager({ query: dataQuery, @@ -116,27 +119,36 @@ ModuleRepository.registerModule<RecordTableParameterValues>({ }); myChart.on('click', 'series', p => { + const label = p.name; 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, + label, 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(); - }, + onSave: + label !== OVERALL_LABEL + ? () => { + if (splitColumn) { + setWhere( + SqlExpression.parse( + // ToDo: remove SqlExpression.parse + where.toggleClauseInWhere(splitColumn.expression.equal(label)), + ), + ); + } + highlightStore.getState().dropHighlight(); + } + : undefined, }); }); - }, [chartRef.current, dataState.data]); + }, [dataState.data]); useEffect(() => { const myChart = chartRef.current; diff --git a/web-console/src/modules/module-pane.tsx b/web-console/src/modules/module-pane.tsx index a7cc32f94d3..e2d76d12e3c 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, { forwardRef, useState } from 'react'; +import React, { useState } from 'react'; import { Issue } from '../components'; @@ -46,8 +46,9 @@ export interface ModulePaneProps { moduleName: string; querySource: QuerySource; where: SqlExpression; - parameterValues: Record<string, any>; + setWhere(where: SqlExpression): void; + parameterValues: Record<string, any>; publicState: Readonly<Record<string, Readonly<Record<string, any>>>>; setPublicState(key: string, value: any): void; runSqlQuery(query: string | SqlQuery): Promise<QueryResult>; @@ -55,12 +56,13 @@ export interface ModulePaneProps { onEdit?: () => void; } -export const ModulePane = forwardRef<any, any>(function ModulePane(props: ModulePaneProps, ref) { +export const ModulePane = function ModulePane(props: ModulePaneProps) { const { moduleId, moduleName, querySource, where, + setWhere, parameterValues, publicState, setPublicState, @@ -81,6 +83,7 @@ export const ModulePane = forwardRef<any, any>(function ModulePane(props: Module stage, querySource, where, + setWhere, parameterValues: fillInDefaults(parameterValues, module.parameters), publicState, myPublicState: publicState[moduleName] ?? {}, @@ -101,7 +104,7 @@ export const ModulePane = forwardRef<any, any>(function ModulePane(props: Module setStage(newStage); }} > - <div className="module-pane" ref={ref}> + <div className="module-pane"> {content} {onEdit && ( <Button className="edit-button" icon={IconNames.EDIT} onClick={onEdit} minimal small /> @@ -119,4 +122,4 @@ export const ModulePane = forwardRef<any, any>(function ModulePane(props: Module </div> </ResizeSensor> ); -}); +}; diff --git a/web-console/src/modules/module-repository/module-repository.ts b/web-console/src/modules/module-repository/module-repository.ts index 4036e244208..e134c25eaa3 100644 --- a/web-console/src/modules/module-repository/module-repository.ts +++ b/web-console/src/modules/module-repository/module-repository.ts @@ -32,6 +32,7 @@ interface ModuleComponentProps<P> { stage: Stage; querySource: QuerySource; where: SqlExpression; + setWhere(where: SqlExpression): void; parameterValues: P; publicState: Readonly<Record<string, Readonly<Record<string, any>>>>; myPublicState: Readonly<Record<string, any>>; diff --git a/web-console/src/views/explore-view/droppable-container/droppable-container.tsx b/web-console/src/views/explore-view/droppable-container/droppable-container.tsx index 59292668121..7246177ab3b 100644 --- a/web-console/src/views/explore-view/droppable-container/droppable-container.tsx +++ b/web-console/src/views/explore-view/droppable-container/droppable-container.tsx @@ -18,7 +18,7 @@ import type { Column } from '@druid-toolkit/query'; import classNames from 'classnames'; -import React, { useState } from 'react'; +import React, { forwardRef, useState } from 'react'; import { DragHelper } from '../drag-helper'; @@ -29,35 +29,38 @@ export interface DroppableContainerProps extends React.HTMLAttributes<HTMLDivEle children?: React.ReactNode; } -export const DroppableContainer = function DroppableContainer(props: DroppableContainerProps) { - const { className, onDropColumn, children, ...rest } = props; - const [dropHover, setDropHover] = useState(false); +export const DroppableContainer = forwardRef<HTMLDivElement, DroppableContainerProps>( + function DroppableContainer(props, ref) { + const { className, onDropColumn, children, ...rest } = props; + const [dropHover, setDropHover] = useState(false); - return ( - <div - className={classNames('droppable-container', className, { 'drop-hover': dropHover })} - {...rest} - onDragOver={e => { - if (!DragHelper.dragColumn) return; - e.preventDefault(); - e.dataTransfer.dropEffect = 'move'; - setDropHover(true); - }} - onDragLeave={e => { - const currentTarget = e.currentTarget; - const relatedTarget = e.relatedTarget; - if (currentTarget.contains(relatedTarget as any)) return; - setDropHover(false); - }} - onDrop={() => { - if (!DragHelper.dragColumn) return; - const dragColumn = DragHelper.dragColumn; - DragHelper.dragColumn = undefined; - setDropHover(false); - onDropColumn(dragColumn); - }} - > - {children} - </div> - ); -}; + return ( + <div + ref={ref} + className={classNames('droppable-container', className, { 'drop-hover': dropHover })} + {...rest} + onDragOver={e => { + if (!DragHelper.dragColumn) return; + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + setDropHover(true); + }} + onDragLeave={e => { + const currentTarget = e.currentTarget; + const relatedTarget = e.relatedTarget; + if (currentTarget.contains(relatedTarget as any)) return; + setDropHover(false); + }} + onDrop={() => { + if (!DragHelper.dragColumn) return; + const dragColumn = DragHelper.dragColumn; + DragHelper.dragColumn = undefined; + setDropHover(false); + onDropColumn(dragColumn); + }} + > + {children} + </div> + ); + }, +); diff --git a/web-console/src/views/explore-view/explore-view.tsx b/web-console/src/views/explore-view/explore-view.tsx index 15637657b7f..3178b601b6f 100644 --- a/web-console/src/views/explore-view/explore-view.tsx +++ b/web-console/src/views/explore-view/explore-view.tsx @@ -23,6 +23,7 @@ import { type QueryResult, L, QueryRunner, sql, SqlQuery, SqlTable, T } from '@d import classNames from 'classnames'; import copy from 'copy-to-clipboard'; import React, { useEffect, useRef, useState } from 'react'; +import { useStore } from 'zustand'; import { ShowValueDialog } from '../../dialogs/show-value-dialog/show-value-dialog'; import { useLocalStorageState, useQueryManager } from '../../hooks'; @@ -40,6 +41,7 @@ import { ExploreState } from './explore-state'; import { FilterPane } from './filter-pane/filter-pane'; import { FullSourcePane } from './full-source-pane/full-source-pane'; import { HighlightBubble } from './highlight-bubble/highlight-bubble'; +import { highlightStore } from './highlight-store/highlight-store'; import { ModulePicker } from './module-picker/module-picker'; import { ResourcePane } from './resource-pane/resource-pane'; import { SourcePane } from './source-pane/source-pane'; @@ -173,7 +175,7 @@ export const ExploreView = React.memo(function ExploreView() { }, ); - // const { dropHighlight } = useStore(highlightStore); + const { dropHighlight } = useStore(highlightStore); const { moduleId, source, where, parameterValues, showFullSource } = exploreState; const module = ModuleRepository.getModule(moduleId); @@ -238,8 +240,6 @@ export const ExploreView = React.memo(function ExploreView() { ); } - console.log('containerRef.current', containerRef.current); - const querySource = querySourceState.getSomeData(); const effectiveShowFullSource = showFullSource || parsedError; @@ -254,7 +254,7 @@ export const ExploreView = React.memo(function ExploreView() { )} {parsedError && <div className="source-error">{`Source error: ${parsedError}`}</div>} {parsedSource && ( - <div className="explore-container" ref={containerRef}> + <div className="explore-container"> <SourcePane selectedSource={parsedSource} onSelectedSourceChange={setSource} @@ -296,7 +296,6 @@ export const ExploreView = React.memo(function ExploreView() { ]} selectedModuleId={moduleId} onSelectedModuleIdChange={id => { - setModuleId(id); // const currentParameterDefinitions = module?.parameterDefinitions || {}; // const valuesToTransfer: TransferValue[] = filterMap( // VISUAL_MODULES.find(vm => vm.moduleId === module?.moduleId)?.transfer || [], @@ -309,8 +308,8 @@ export const ExploreView = React.memo(function ExploreView() { // }, // ); // - // dropHighlight(); - // setModuleId(m); + dropHighlight(); + setModuleId(id); // resetParameterValues(); // // const newModuleDef = VISUAL_MODULES.find(vm => vm.moduleId === m); @@ -388,6 +387,7 @@ export const ExploreView = React.memo(function ExploreView() { </div> <DroppableContainer className="main-cnt" + ref={containerRef} onDropColumn={column => { onShow(column); }} @@ -401,6 +401,7 @@ export const ExploreView = React.memo(function ExploreView() { moduleName="*" querySource={querySource} where={where} + setWhere={setWhere} parameterValues={parameterValues} publicState={{}} setPublicState={() => null} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
