This is an automated email from the ASF dual-hosted git repository. vogievetsky pushed a commit to branch segment_timeline2 in repository https://gitbox.apache.org/repos/asf/druid.git
commit 2243da03823a0e10a2e17880f17cc5d6d0c85f6b Author: Vadim Ogievetsky <[email protected]> AuthorDate: Thu Oct 31 13:39:13 2024 -0700 simple bubble --- .../src/components/segment-timeline/bubble.scss | 37 +++++++++++++++++ .../src/components/segment-timeline/bubble.tsx | 42 +++++++++++++++++++ .../segment-timeline/segment-bar-chart-render.tsx | 48 ++++++++++++++++++---- .../segment-timeline/segment-bar-chart.tsx | 9 ++-- .../segment-timeline/segment-timeline.tsx | 11 ++--- .../src/views/segments-view/segments-view.tsx | 2 - 6 files changed, 130 insertions(+), 19 deletions(-) diff --git a/web-console/src/components/segment-timeline/bubble.scss b/web-console/src/components/segment-timeline/bubble.scss new file mode 100644 index 00000000000..5dcf9986551 --- /dev/null +++ b/web-console/src/components/segment-timeline/bubble.scss @@ -0,0 +1,37 @@ +/* + * 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 '../../variables'; + +.bubble { + position: absolute; + @include card-like; + padding: 5px; + + &.up { + transform: translate(-50%, -100%); + } + + &.down { + transform: translate(-50%, 0); + } + + &.mute { + pointer-events: none; + } +} diff --git a/web-console/src/components/segment-timeline/bubble.tsx b/web-console/src/components/segment-timeline/bubble.tsx new file mode 100644 index 00000000000..f3a9c287b13 --- /dev/null +++ b/web-console/src/components/segment-timeline/bubble.tsx @@ -0,0 +1,42 @@ +/* + * 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 classNames from 'classnames'; +import { createPortal } from 'react-dom'; + +import './bubble.scss'; + +interface BubbleProps { + openOn: { x: number; y: number; text: string }; + direction?: 'up' | 'down'; + mute?: boolean; +} + +export const Bubble = function Bubble(props: BubbleProps) { + const { openOn, direction = 'up', mute } = props; + + return createPortal( + <div + className={classNames('bubble', direction, { mute })} + style={{ left: openOn.x, top: openOn.y }} + > + {openOn.text} + </div>, + document.body, + ); +}; diff --git a/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx b/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx index 485bbe4afdb..44407e22233 100644 --- a/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx +++ b/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx @@ -31,6 +31,7 @@ import { clamp, day, Duration, + filterMap, formatBytes, formatInteger, formatNumber, @@ -41,6 +42,7 @@ import { } from '../../utils'; import type { Margin, Stage } from '../../utils/stage'; +import { Bubble } from './bubble'; import { ChartAxis } from './chart-axis'; import type { IntervalBar, IntervalRow, IntervalStat, TrimmedIntervalRow } from './common'; import { aggregateSegmentStats } from './common'; @@ -123,7 +125,8 @@ interface SegmentBarChartRenderProps { changeDateRange(dateRange: NonNullDateRange): void; shownIntervalStat: IntervalStat; intervalRows: IntervalRow[]; - changeActiveDatasource(datasource: string | undefined): void; + focusDatasource: string | undefined; + changeFocusDatasource(datasource: string | undefined): void; } export const SegmentBarChartRender = function SegmentBarChartRender( @@ -135,7 +138,8 @@ export const SegmentBarChartRender = function SegmentBarChartRender( dateRange, changeDateRange, intervalRows, - changeActiveDatasource, + focusDatasource, + changeFocusDatasource, } = props; const [hoverOn, setHoverOn] = useState<IntervalBar>(); const [mouseDownAt, setMouseDownAt] = useState< @@ -143,6 +147,9 @@ export const SegmentBarChartRender = function SegmentBarChartRender( >(); const [dragging, setDragging] = useState<NonNullDateRange | undefined>(); const [shiftOffset, setShiftOffset] = useState<number | undefined>(); + const [bubbleOpenOn, setBubbleOpenOn] = useState< + { x: number; y: number; text: string } | undefined + >(); const svgRef = useRef<SVGSVGElement | null>(null); const expandedDateRange: NonNullDateRange = useMemo(() => { @@ -160,7 +167,8 @@ export const SegmentBarChartRender = function SegmentBarChartRender( const intervalBars = useMemo(() => { const trimDuration = new Duration(trimGranularity); - const trimmedIntervalRows = intervalRows.map(intervalRow => { + const trimmedIntervalRows = filterMap(intervalRows, intervalRow => { + if (focusDatasource && intervalRow.datasource !== focusDatasource) return; const start = trimDuration.floor(intervalRow.start, TZ_UTC); const end = trimDuration.ceil(intervalRow.end, TZ_UTC); const shownSeconds = (end.valueOf() - start.valueOf()) / 1000; @@ -197,7 +205,7 @@ export const SegmentBarChartRender = function SegmentBarChartRender( ); return stackIntervalRows(fullyGroupedSegmentRows); - }, [intervalRows, trimGranularity]); + }, [intervalRows, trimGranularity, focusDatasource]); const innerStage = stage.applyMargin(CHART_MARGIN); @@ -256,13 +264,32 @@ export const SegmentBarChartRender = function SegmentBarChartRender( } useGlobalEventListener('mousemove', (e: MouseEvent) => { - if (!mouseDownAt) return; - e.preventDefault(); - const svg = svgRef.current; if (!svg) return; const rect = svg.getBoundingClientRect(); const x = e.clientX - rect.x - CHART_MARGIN.left; + const y = e.clientY - rect.y - CHART_MARGIN.top; + + if (!mouseDownAt) { + if (0 <= x && x <= innerStage.width && 0 <= y && y <= innerStage.height) { + const time = baseTimeScale.invert(x); + const granStart = day.floor(time, TZ_UTC); + const granEnd = day.ceil(time, TZ_UTC); + setBubbleOpenOn({ + x: + rect.x + + CHART_MARGIN.left + + baseTimeScale((granStart.valueOf() + granEnd.valueOf()) / 2), + y: rect.y + CHART_MARGIN.top + innerStage.height + 10, + text: granStart.toISOString().slice(0, 10), + }); + } else { + setBubbleOpenOn(undefined); + } + return; + } + e.preventDefault(); + const b = baseTimeScale.invert(x); if (mouseDownAt.action === 'shift' || e.shiftKey) { setShiftOffset(mouseDownAt.time.valueOf() - b.valueOf()); @@ -311,6 +338,8 @@ export const SegmentBarChartRender = function SegmentBarChartRender( }; } + console.log('here'); + return ( <div className="segment-bar-chart-render"> <svg @@ -364,7 +393,7 @@ export const SegmentBarChartRender = function SegmentBarChartRender( style={{ fill: COLORIZER(intervalBar) }} onClick={ intervalBar.datasource - ? () => changeActiveDatasource(intervalBar.datasource) + ? () => changeFocusDatasource(intervalBar.datasource) : undefined } onMouseOver={() => { @@ -380,7 +409,7 @@ export const SegmentBarChartRender = function SegmentBarChartRender( {...segmentBarToRect(hoverOn)} onClick={() => { setHoverOn(undefined); - changeActiveDatasource(hoverOn.datasource); + changeFocusDatasource(hoverOn.datasource); }} /> )} @@ -428,6 +457,7 @@ export const SegmentBarChartRender = function SegmentBarChartRender( <div className="no-data-text">There are no segments in the selected range</div> </div> )} + {bubbleOpenOn && <Bubble openOn={bubbleOpenOn} mute direction="down" />} </div> ); }; diff --git a/web-console/src/components/segment-timeline/segment-bar-chart.tsx b/web-console/src/components/segment-timeline/segment-bar-chart.tsx index 73c69bb8973..17f2866abee 100644 --- a/web-console/src/components/segment-timeline/segment-bar-chart.tsx +++ b/web-console/src/components/segment-timeline/segment-bar-chart.tsx @@ -38,7 +38,8 @@ interface SegmentBarChartProps { dateRange: NonNullDateRange; changeDateRange(newDateRange: NonNullDateRange): void; shownIntervalStat: IntervalStat; - changeActiveDatasource: (datasource: string | undefined) => void; + focusDatasource: string | undefined; + changeFocusDatasource: (datasource: string | undefined) => void; } export const SegmentBarChart = function SegmentBarChart(props: SegmentBarChartProps) { @@ -48,7 +49,8 @@ export const SegmentBarChart = function SegmentBarChart(props: SegmentBarChartPr changeDateRange, stage, shownIntervalStat, - changeActiveDatasource, + focusDatasource, + changeFocusDatasource, } = props; const intervalsQuery = useMemo(() => ({ capabilities, dateRange }), [capabilities, dateRange]); @@ -140,7 +142,8 @@ export const SegmentBarChart = function SegmentBarChart(props: SegmentBarChartPr changeDateRange={changeDateRange} shownIntervalStat={shownIntervalStat} intervalRows={intervalRows} - changeActiveDatasource={changeActiveDatasource} + focusDatasource={focusDatasource} + changeFocusDatasource={changeFocusDatasource} /> ); }; diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx b/web-console/src/components/segment-timeline/segment-timeline.tsx index 83208304d69..e113fdea571 100644 --- a/web-console/src/components/segment-timeline/segment-timeline.tsx +++ b/web-console/src/components/segment-timeline/segment-timeline.tsx @@ -73,7 +73,7 @@ export const SegmentTimeline = function SegmentTimeline(props: SegmentTimelinePr const { capabilities } = props; const [stage, setStage] = useState<Stage | undefined>(); const [activeSegmentStat, setActiveSegmentStat] = useState<IntervalStat>('size'); - const [activeDatasource, setActiveDatasource] = useState<string | undefined>(); + const [focusDatasource, setFocusDatasource] = useState<string | undefined>(); const [dateRange, setDateRange] = useState<NonNullDateRange>( getDateRange(DEFAULT_SHOWN_DURATION), ); @@ -88,7 +88,7 @@ export const SegmentTimeline = function SegmentTimeline(props: SegmentTimelinePr <Select<string> items={datasourcesWzAll} onItemSelect={(selectedItem: string) => { - setActiveDatasource(selectedItem === showAll ? undefined : selectedItem); + setFocusDatasource(selectedItem === showAll ? undefined : selectedItem); }} itemRenderer={(val, { handleClick, handleFocus, modifiers }) => { if (!modifiers.matchesPredicate) return null; @@ -117,7 +117,7 @@ export const SegmentTimeline = function SegmentTimeline(props: SegmentTimelinePr }} > <Button - text={activeDatasource === null ? showAll : activeDatasource} + text={focusDatasource === null ? showAll : focusDatasource} fill rightIcon={IconNames.CARET_DOWN} /> @@ -204,8 +204,9 @@ export const SegmentTimeline = function SegmentTimeline(props: SegmentTimelinePr dateRange={dateRange} changeDateRange={setDateRange} shownIntervalStat={activeSegmentStat} - changeActiveDatasource={(datasource: string | undefined) => - setActiveDatasource(activeDatasource ? undefined : datasource) + focusDatasource={focusDatasource} + changeFocusDatasource={(datasource: string | undefined) => + setFocusDatasource(focusDatasource ? undefined : datasource) } /> )} diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx index ded3ac5474c..18dcc4c4cc3 100644 --- a/web-console/src/views/segments-view/segments-view.tsx +++ b/web-console/src/views/segments-view/segments-view.tsx @@ -272,8 +272,6 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment const { page, pageSize, filtered, sorted, visibleColumns, capabilities, groupByInterval } = query; - console.log('run', filtered); - if (capabilities.hasSql()) { const whereExpression = segmentFiltersToExpression(filtered); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
