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]

Reply via email to