Closed techniq closed 2 months ago
Really neat to see this idea coming together. It's been on my mind, too, over the years. One idea I was kicking around was a way to have both a simplified API and allow for ad hoc customization as discussed here https://github.com/mhkeller/layercake/discussions/174. I haven't done a proof of concept with the snippets API but it could be a neat way to swap out the default components with your own once your charts gets more customized.
Really neat to see this idea coming together. It's been on my mind, too, over the years. One idea I was kicking around was a way to have both a simplified API and allow for ad hoc customization as discussed here mhkeller/layercake#174. I haven't done a proof of concept with the snippets API but it could be a neat way to swap out the default components with your own once your charts gets more customized.
@mhkeller That's the goal with these simplified charts 😁 - Provide a great default/initial experience for common use cases, but also provide various extension points as you need to customize:
Chart
instance and the inner chart components such as Axis
Basically get you setup quickly, but get out of your way.
These simplified charts also provide better integration between components (ex. series
prop which adds the additional marks, tooltip items with colors, etc)
Some of the extension/overrides available thus far:
Pass any prop to the simplified chart instance that passes down to the underlying <Chart>
instance
<AreaChart padding={{ left: 24, bottom: 16 }} ... />
Provide additional props (or overrides) on the inner components without requiring a full component overrides.
<BarChart
props={{
axisLeft: { rule: false },
axisBottom: { format: (value) => format(Math.abs(value), "metric") },
}}>
More granular overrides via named slots (such as marks
, axis
, tooltip
, etc)
Area
, but keep default axis, tooltip, etc (example)<AreaChart data={dateSeriesData} x="date" y="value">
<svelte:fragment slot="marks">
<LinearGradient class="from-primary/50 to-primary/0" vertical let:url>
<Area line={{ class: "stroke-2 stroke-primary" }} fill={url} />
</LinearGradient>
</svelte:fragment>
</AreaChart>
<ScatterChart {data} x="x" y="y">
<svelte:fragment slot="tooltip" let:x let:y let:padding let:height>
<Tooltip.Root
x={padding.left}
y="data"
anchor="right"
contained={false}
class="text-[10px] font-semibold text-primary bg-surface-100 mr-[2px] px-1 py-[2px] border border-primary rounded whitespace-nowrap"
let:data
>
{format(y(data), "integer")}
</Tooltip.Root>
<Tooltip.Root
x="data"
y={height}
anchor="top"
class="text-[10px] font-semibold text-primary bg-surface-100 mt-[1px] px-2 py-[1px] border border-primary rounded whitespace-nowrap"
contained={false}
let:data
>
{format(x(data), "integer")}
</Tooltip.Root>
</svelte:fragment>
</ScatterChart>
<BarChart data={dateSeriesData} x="date" y="value" labels />
Customize all components used, but still include all the benefits of default chart type instance (domains, tooltip mode, series support, etc) and simplified imports (ex. no d3-scale
, for example)
<AreaChart data={dateSeriesData} x="date" y="value" let:x let:y>
<Svg>
<Axis placement="left" grid rule />
<Axis
placement="bottom"
format={(d) => format(d, PeriodType.Day, { variant: "short" })}
rule
/>
<Area
line={{ class: "stroke-2 stroke-primary" }}
class="fill-primary/30"
/>
<Highlight points lines />
</Svg>
<Tooltip.Root let:data>
<Tooltip.Header>{format(x(data), PeriodType.DayTime)}</Tooltip.Header>
<Tooltip.List>
<Tooltip.Item label="value" value={y(data)} />
</Tooltip.List>
</Tooltip.Root>
</AreaChart>
I'm still feeling this out, but take a look at AreaChart, BarChart, LineChart, PieChart, and ScatterChart for examples of many of these concepts.
I'm currently working on overhauling how stacks (BarChart, AreaChart) and groups (BarChart) are implemented (eyeing derived scales PR for groups 😁). Stacks along with a few additional use cases are likely what will be included in the first release, and will consider adding more chart types (hierarchy, force, geo, etc) over time.
This idea has been in my head for some time, but I figure it's time to formally write it down, as it would simplify the "basic" use cases (especially for exploration or quick visualizations) and enable additional use cases similar to the newly released shadcn/ui Charts. While I was initially adverse to the idea, I think creating
BarChart
,AreaChart
,LineChart
, etc will provide a lot of value, including:<BarChart {data} />
) if you align with conventionsd3-scale
, many LayerChart components, ...)The general idea is
is a streamlined instance of
Possibly also provide a default slot impl, so
is a streamlined instance of
Items to consider
scaleBand().padding(0.4)
maybe as<BarChart bandPadding={0.4}>
$$restProps
to underlyingChart
such as<BarChart {data} x="date" y="value">
props
to target internal components, but recommend overriding slots (named or default) as use cases become more complicatedmarks
slot for easy styling with<LinearGradient let:url>
component0
min domain if data includes negative valuesyBaseline
<Rule={x}>
unlessxDomain
does not include0
in min/max range (same fory
)xDomain
includes negative values<AreaChart tooltip={false}>
)<Legend>
(ex.<Chart legend={....}>
)Initial charts to support
scaleBand
/scaleLinear
band
orbisect-x
/bisect-y
scaleTime
orscaleLinear
/scaleLinear
bisect-x
scaleTime
orscaleLinear
/scaleLinear
bisect-x
scaleLinear
voronoi
orquadtree
manual
(path)Related: #68