Skip to content

Commit 3ea6bc2

Browse files
committed
Support multiple series
1 parent c172447 commit 3ea6bc2

File tree

1 file changed

+112
-50
lines changed

1 file changed

+112
-50
lines changed

apps/webapp/app/components/code/ChartConfigPanel.tsx

Lines changed: 112 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { OutputColumnMetadata } from "@internal/clickhouse";
2-
import { BarChart, LineChart } from "lucide-react";
2+
import { BarChart, LineChart, Plus, XIcon } from "lucide-react";
33
import { useCallback, useEffect, useMemo, useRef } from "react";
44
import { cn } from "~/utils/cn";
55
import { Header3 } from "../primitives/Headers";
@@ -322,31 +322,87 @@ export function ChartConfigPanel({ columns, config, onChange, className }: Chart
322322
</Select>
323323
</ConfigField>
324324

325-
{/* Y-Axis */}
326-
<ConfigField label="Y-Axis">
325+
{/* Y-Axis / Series */}
326+
<ConfigField label={config.yAxisColumns.length > 1 ? "Series" : "Y-Axis"}>
327327
{yAxisOptions.length === 0 ? (
328328
<span className="text-xs text-text-dimmed">No numeric columns</span>
329329
) : (
330-
<Select
331-
value={config.yAxisColumns[0] ?? ""}
332-
setValue={(value) => updateConfig({ yAxisColumns: value ? [value] : [] })}
333-
variant="tertiary/small"
334-
placeholder="Select column"
335-
items={yAxisOptions}
336-
dropdownIcon
337-
className="min-w-[140px]"
338-
>
339-
{(items) =>
340-
items.map((item) => (
341-
<SelectItem key={item.value} value={item.value}>
342-
<span className="flex items-center gap-2">
343-
<span>{item.label}</span>
344-
<TypeBadge type={item.type} />
345-
</span>
346-
</SelectItem>
347-
))
348-
}
349-
</Select>
330+
<div className="flex flex-col gap-1.5">
331+
{config.yAxisColumns.map((col, index) => (
332+
<div key={index} className="flex items-center gap-1">
333+
<Select
334+
value={col}
335+
setValue={(value) => {
336+
const newColumns = [...config.yAxisColumns];
337+
if (value) {
338+
newColumns[index] = value;
339+
} else {
340+
newColumns.splice(index, 1);
341+
}
342+
updateConfig({ yAxisColumns: newColumns });
343+
}}
344+
variant="tertiary/small"
345+
placeholder="Select column"
346+
items={yAxisOptions.filter(
347+
(opt) => opt.value === col || !config.yAxisColumns.includes(opt.value)
348+
)}
349+
dropdownIcon
350+
className="min-w-[140px] flex-1"
351+
>
352+
{(items) =>
353+
items.map((item) => (
354+
<SelectItem key={item.value} value={item.value}>
355+
<span className="flex items-center gap-2">
356+
<span>{item.label}</span>
357+
<TypeBadge type={item.type} />
358+
</span>
359+
</SelectItem>
360+
))
361+
}
362+
</Select>
363+
{index > 0 && (
364+
<button
365+
type="button"
366+
onClick={() => {
367+
const newColumns = config.yAxisColumns.filter((_, i) => i !== index);
368+
updateConfig({ yAxisColumns: newColumns });
369+
}}
370+
className="rounded p-1 text-text-dimmed hover:bg-charcoal-700 hover:text-text-bright"
371+
title="Remove series"
372+
>
373+
<XIcon className="h-3.5 w-3.5" />
374+
</button>
375+
)}
376+
</div>
377+
))}
378+
379+
{/* Add another series button - disabled when group by is set */}
380+
{config.yAxisColumns.length < yAxisOptions.length && !config.groupByColumn && (
381+
<button
382+
type="button"
383+
onClick={() => {
384+
const availableColumns = yAxisOptions.filter(
385+
(opt) => !config.yAxisColumns.includes(opt.value)
386+
);
387+
if (availableColumns.length > 0) {
388+
updateConfig({
389+
yAxisColumns: [...config.yAxisColumns, availableColumns[0].value],
390+
});
391+
}
392+
}}
393+
className="flex items-center gap-1 self-start rounded px-1 py-0.5 text-xs text-text-dimmed hover:bg-charcoal-700 hover:text-text-bright"
394+
>
395+
<Plus className="h-3 w-3" />
396+
Add series
397+
</button>
398+
)}
399+
400+
{config.groupByColumn && config.yAxisColumns.length === 1 && (
401+
<span className="text-xxs text-text-dimmed">
402+
Remove group by to add multiple series
403+
</span>
404+
)}
405+
</div>
350406
)}
351407
</ConfigField>
352408

@@ -370,36 +426,42 @@ export function ChartConfigPanel({ columns, config, onChange, className }: Chart
370426
</Select>
371427
</ConfigField>
372428

373-
{/* Group By */}
429+
{/* Group By - disabled when multiple series are selected */}
374430
<ConfigField label="Group by">
375-
<Select
376-
value={config.groupByColumn ?? "__none__"}
377-
setValue={(value) =>
378-
updateConfig({ groupByColumn: value === "__none__" ? null : value })
379-
}
380-
variant="tertiary/small"
381-
placeholder="None"
382-
items={groupByOptions}
383-
dropdownIcon
384-
className="min-w-[140px]"
385-
text={(t) => (t === "__none__" ? "None" : t)}
386-
>
387-
{(items) =>
388-
items.map((item) => (
389-
<SelectItem key={item.value} value={item.value}>
390-
<span className="flex items-center gap-2">
391-
<span>{item.label}</span>
392-
{item.type && <TypeBadge type={item.type} />}
393-
</span>
394-
</SelectItem>
395-
))
396-
}
397-
</Select>
431+
{config.yAxisColumns.length > 1 ? (
432+
<span className="text-xs text-text-dimmed">
433+
Not available with multiple series
434+
</span>
435+
) : (
436+
<Select
437+
value={config.groupByColumn ?? "__none__"}
438+
setValue={(value) =>
439+
updateConfig({ groupByColumn: value === "__none__" ? null : value })
440+
}
441+
variant="tertiary/small"
442+
placeholder="None"
443+
items={groupByOptions}
444+
dropdownIcon
445+
className="min-w-[140px]"
446+
text={(t) => (t === "__none__" ? "None" : t)}
447+
>
448+
{(items) =>
449+
items.map((item) => (
450+
<SelectItem key={item.value} value={item.value}>
451+
<span className="flex items-center gap-2">
452+
<span>{item.label}</span>
453+
{item.type && <TypeBadge type={item.type} />}
454+
</span>
455+
</SelectItem>
456+
))
457+
}
458+
</Select>
459+
)}
398460
</ConfigField>
399461

400-
{/* Stacked toggle (only when grouped) */}
401-
{config.groupByColumn && (
402-
<ConfigField label="Stack groups">
462+
{/* Stacked toggle (when grouped or multiple series) */}
463+
{(config.groupByColumn || config.yAxisColumns.length > 1) && (
464+
<ConfigField label={config.groupByColumn ? "Stack groups" : "Stack series"}>
403465
<Switch
404466
variant="medium"
405467
checked={config.stacked}

0 commit comments

Comments
 (0)