Skip to content

Commit

Permalink
Color palette for metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
insmac committed Nov 22, 2024
1 parent 46b0d13 commit df3bf11
Show file tree
Hide file tree
Showing 39 changed files with 1,134 additions and 7 deletions.
559 changes: 559 additions & 0 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion packages/browser-tests/questdb
Submodule questdb updated 469 files
1 change: 1 addition & 0 deletions packages/react-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@radix-ui/react-alert-dialog": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-popover": "0.1.6",
"@radix-ui/react-switch": "^1.0.2",
"@styled-icons/bootstrap": "^10.47.0",
"@styled-icons/remix-line": "^10.18.0",
Expand Down
86 changes: 86 additions & 0 deletions packages/react-components/src/components/Popover/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import styled from "styled-components";
import * as RadixPopover from "@radix-ui/react-popover";
import type { PopoverProps } from "@radix-ui/react-popover";
import { X } from "@styled-icons/bootstrap/X";
import React from "react";
import { Heading } from "../Heading";

const ALIGN_OPTIONS: readonly ["start", "center", "end"] = [
"start",
"center",
"end",
];
export type Align = (typeof ALIGN_OPTIONS)[number];

const StyledPopoverContent = styled(RadixPopover.Content)`
display: flex;
flex-direction: column;
background: ${({ theme }) => theme.color.backgroundLighter};
border: 1px solid ${({ theme }) => theme.color.selection};
box-shadow: 0 7px 30px -10px ${({ theme }) => theme.color.black};
outline: none;
`;

const StyledPopoverClose = styled(RadixPopover.Close).attrs({
"aria-label": "Close",
asChild: true,
})`
appearance: initial;
margin-left: auto;
cursor: pointer;
`;

const Header = styled.div`
display: flex;
padding: 2rem;
align-items: center;
justify-content: space-between;
border-bottom: 0.1rem ${({ theme }) => theme.color.background} solid;
`;

const ContentWrapper = styled.div`
width: 100%;
`;

type Props = {
children: React.ReactNode;
withCloseButton?: boolean;
trigger: React.ReactNode;
width?: number | string;
title?: string;
open?: PopoverProps["open"];
onOpenChange?: (isOpen: boolean) => void;
align?: Align;
};

export const Popover = ({
children,
withCloseButton = false,
width,
trigger,
title,
open,
onOpenChange,
align,
}: Props) => (
<RadixPopover.Root onOpenChange={onOpenChange} open={open}>
<RadixPopover.Trigger asChild>{trigger}</RadixPopover.Trigger>
<StyledPopoverContent
style={{ width: width ?? "auto" }}
align={align}
sideOffset={10}
>
{(title || withCloseButton) && (
<Header>
{title && <Heading level={5}>{title}</Heading>}
{withCloseButton && (
<StyledPopoverClose>
<X size="18px" />
</StyledPopoverClose>
)}
</Header>
)}
<ContentWrapper>{children}</ContentWrapper>
</StyledPopoverContent>
</RadixPopover.Root>
);
1 change: 1 addition & 0 deletions packages/react-components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { Popover } from "./components/Popover";
export { AlertDialog } from "./components/AlertDialog";
export { Badge } from "./components/Badge";
export { Box } from "./components/Box";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { AddChart } from "@styled-icons/material"
import { MetricType, metricTypeLabel } from "./utils"
import { useEditor } from "../../../providers"
import merge from "lodash.merge"
import { defaultColor, getColorForNewMetric } from "./color-palette"

const StyledDescription = styled(Dialog.Description)`
display: grid;
Expand Down Expand Up @@ -53,6 +54,17 @@ export const AddMetricDialog = ({ open, onOpenChange }: Props) => {

const buffer = buffers.find((b) => b.id === activeBuffer?.id)

const metrics = buffer?.metricsViewState?.metrics ?? []
const previousMetric =
metrics.length > 0 ? metrics[metrics.length - 1] : undefined

const color = previousMetric
? getColorForNewMetric(
metrics.map((m) => m.color),
previousMetric.color,
)
: defaultColor

const handleSelectMetric = async (metricType: MetricType) => {
if (buffer?.id) {
const newBuffer = merge(buffer, {
Expand All @@ -62,6 +74,7 @@ export const AddMetricDialog = ({ open, onOpenChange }: Props) => {
{
metricType,
position: buffer.metricsViewState?.metrics?.length ?? 0,
color,
},
],
},
Expand Down
73 changes: 73 additions & 0 deletions packages/web-console/src/scenes/Editor/Metrics/color-palette.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from "react"
import styled from "styled-components"
import { Box } from "@questdb/react-components"
import { Check } from "@styled-icons/boxicons-regular"

export const colors = [
"#bbbbbb",
"#f8f8f2",
"#6272a4",
"#ff5555",
"#ffb86c",
"#f1fa8c",
"#50fa7b",
"#bd93f9",
"#8be9fd",
"#d14671",
"#fafafa",
]

export const defaultColor = "#8be9fd"

export const getColorForNewMetric = (
existingColors: string[],
lastColor: string,
) => {
const filteredColorPalette = colors.filter((color) => color !== lastColor)
const filterExisting = filteredColorPalette.filter(
(color) => !existingColors.includes(color),
)
return filterExisting.length > 0
? filterExisting[Math.floor(Math.random() * filterExisting.length)]
: filteredColorPalette[
Math.floor(Math.random() * filteredColorPalette.length)
]
}

const Root = styled.div`
padding: 0.5rem;
`

const ColorBox = styled.div`
position: relative;
width: 1.6rem;
height: 1.6rem;
cursor: pointer;
`

const CheckIcon = styled(Check)`
position: absolute;
color: black;
`

export const ColorPalette = ({
selectedColor,
onSelect,
}: {
selectedColor: string
onSelect: (color: string) => void
}) => (
<Root>
<Box gap="0.5rem">
{colors.map((color) => (
<ColorBox
key={color}
style={{ backgroundColor: color }}
onClick={() => onSelect(color)}
>
{selectedColor === color && <CheckIcon size="16px" />}
</ColorBox>
))}
</Box>
</Root>
)
3 changes: 3 additions & 0 deletions packages/web-console/src/scenes/Editor/Metrics/graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type Props = {
beforeLabel?: React.ReactNode
loading?: boolean
data: uPlot.AlignedData
colors: string[]
duration: MetricDuration
yValue: (rawValue: number) => string
actions?: React.ReactNode
Expand All @@ -72,6 +73,7 @@ export const Graph = ({
label,
beforeLabel,
data,
colors,
duration,
yValue,
loading,
Expand All @@ -91,6 +93,7 @@ export const Graph = ({

const graphOptions = useGraphOptions({
data,
colors,
duration,
timeRef,
valueRef,
Expand Down
11 changes: 11 additions & 0 deletions packages/web-console/src/scenes/Editor/Metrics/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ export const Metrics = () => {
}
}

const handleColorChange = (metric: Metric, color: string) => {
if (buffer?.id && buffer?.metricsViewState?.metrics) {
updateMetrics(
buffer?.metricsViewState?.metrics.map((m) =>
m.position === metric.position ? { ...m, color } : m,
),
)
}
}

useEffect(() => {
const metrics = buffer?.metricsViewState?.metrics
const metricDuration = buffer?.metricsViewState?.metricDuration
Expand Down Expand Up @@ -254,6 +264,7 @@ export const Metrics = () => {
metricDuration={metricDuration}
onRemove={handleRemoveMetric}
onTableChange={handleTableChange}
onColorChange={handleColorChange}
/>
))}
</Charts>
Expand Down
50 changes: 45 additions & 5 deletions packages/web-console/src/scenes/Editor/Metrics/metric.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import * as QuestDB from "../../../utils/questdb"
import { Graph } from "./graph"
import uPlot from "uplot"
import styled from "styled-components"
import { Box, Button } from "@questdb/react-components"
import { Error, Trash } from "@styled-icons/boxicons-regular"
import { Box, Button, ForwardRef, Popover } from "@questdb/react-components"
import { Error, Palette, Trash } from "@styled-icons/boxicons-regular"
import { useSelector } from "react-redux"
import { selectors } from "../../../store"
import isEqual from "lodash.isequal"
import { useLocalStorage } from "../../../providers/LocalStorageProvider"
import { TableSelector } from "./table-selector"
import { IconWithTooltip } from "../../../components/IconWithTooltip"
import { ColorPalette } from "./color-palette"

const MetricInfoRoot = styled(Box).attrs({
align: "center",
Expand All @@ -29,6 +31,11 @@ const MetricInfoRoot = styled(Box).attrs({
height: 25rem;
`

const ActionButton = styled(Button)`
padding: 0;
width: 3rem;
`

const graphDataConfigs = {
[MetricType.LATENCY]: {
getData: (latency: Latency[]): uPlot.AlignedData => [
Expand Down Expand Up @@ -58,16 +65,19 @@ export const Metric = ({
metricDuration,
onRemove,
onTableChange,
onColorChange,
}: {
metric: MetricItem
metricDuration: MetricDuration
onRemove: (metric: MetricItem) => void
onTableChange: (metric: MetricItem, tableId: number) => void
onColorChange: (metric: MetricItem, color: string) => void
}) => {
const { quest } = useContext(QuestContext)
const [loading, setLoading] = useState(false)
const [data, setData] = useState<uPlot.AlignedData>()
const [lastMetric, setLastMetric] = useState<MetricItem>()
const [colorPickerOpen, setColorPickerOpen] = useState(false)

const tables = useSelector(selectors.query.getTables)

Expand Down Expand Up @@ -155,6 +165,7 @@ export const Metric = ({
return (
<Graph
data={metric.tableId && data ? data : [[], []]}
colors={[metric.color]}
loading={loading}
duration={metricDuration}
label={metricTypeLabel[metric.metricType]}
Expand All @@ -176,9 +187,38 @@ export const Metric = ({
}
actions={
<Box gap="0.5rem" align="center">
<Button skin="transparent" onClick={() => onRemove(metric)}>
<Trash size="18px" />
</Button>
<IconWithTooltip
icon={
<ForwardRef>
<Popover
open={colorPickerOpen}
onOpenChange={setColorPickerOpen}
trigger={
<ActionButton skin="transparent">
<Palette size="18px" />
</ActionButton>
}
align="center"
>
<ColorPalette
onSelect={(color) => onColorChange(metric, color)}
selectedColor={metric.color}
/>
</Popover>
</ForwardRef>
}
tooltip="Choose series color"
placement="top"
/>
<IconWithTooltip
icon={
<ActionButton skin="transparent" onClick={() => onRemove(metric)}>
<Trash size="18px" />
</ActionButton>
}
tooltip="Remove metric"
placement="top"
/>
</Box>
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MetricDuration, durationInMinutes } from "./utils"

type Params = {
data: uPlot.AlignedData
colors: string[]
duration: MetricDuration
tickValue?: (rawValue: number) => string
xValue?: (rawValue: number, index: number, ticks: number[]) => string | null
Expand Down Expand Up @@ -43,6 +44,7 @@ const valuePlugin = (

export const useGraphOptions = ({
data,
colors,
duration,
tickValue = (rawValue) => (+rawValue).toString(),
xValue,
Expand Down Expand Up @@ -100,7 +102,7 @@ export const useGraphOptions = ({
points: {
show: data[0].length === 1,
},
stroke: theme.color.cyan,
stroke: colors[0],
width: 2,
value: (_self, rawValue) => yValue(rawValue),
},
Expand Down
Loading

0 comments on commit df3bf11

Please sign in to comment.