11import type { OutputColumnMetadata } from "@internal/clickhouse" ;
2- import { BarChart , LineChart } from "lucide-react" ;
2+ import { BarChart , LineChart , Plus , XIcon } from "lucide-react" ;
33import { useCallback , useEffect , useMemo , useRef } from "react" ;
44import { cn } from "~/utils/cn" ;
55import { 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