diff --git a/.changeset/poor-waves-protect.md b/.changeset/poor-waves-protect.md new file mode 100644 index 000000000..edd412c29 --- /dev/null +++ b/.changeset/poor-waves-protect.md @@ -0,0 +1,5 @@ +--- +'layerchart': minor +--- + +feat(Bar): Add `none` rounded option diff --git a/.changeset/tough-mangos-tie.md b/.changeset/tough-mangos-tie.md new file mode 100644 index 000000000..98ae14e2b --- /dev/null +++ b/.changeset/tough-mangos-tie.md @@ -0,0 +1,5 @@ +--- +'layerchart': minor +--- + +feat(Bar): Support `rounded="edge"` for value and orientation aware single edge rounding diff --git a/.changeset/yellow-maps-tell.md b/.changeset/yellow-maps-tell.md new file mode 100644 index 000000000..61a4dd310 --- /dev/null +++ b/.changeset/yellow-maps-tell.md @@ -0,0 +1,5 @@ +--- +'layerchart': minor +--- + +feat(BarChart): Use new `rounded="edge"` for better default rounding, especially with `seriesLayout="stack"` diff --git a/packages/layerchart/src/lib/components/Bar.svelte b/packages/layerchart/src/lib/components/Bar.svelte index a0fa1d8e0..97b1bd510 100644 --- a/packages/layerchart/src/lib/components/Bar.svelte +++ b/packages/layerchart/src/lib/components/Bar.svelte @@ -5,10 +5,12 @@ import Rect from './Rect.svelte'; import Spline from './Spline.svelte'; - import { createDimensionGetter } from '$lib/utils/rect.js'; - import type { Accessor } from '../utils/common.js'; + import { createDimensionGetter } from '../utils/rect.js'; + import { isScaleBand } from '../utils/scales.js'; + import { accessor, type Accessor } from '../utils/common.js'; + import { greatestAbs } from '@layerstack/utils/array'; - const { x: xContext, y: yContext } = chartContext(); + const { x: xContext, y: yContext, xScale } = chartContext(); export let bar: Object; @@ -40,6 +42,8 @@ /** Control which corners are rounded with radius. Uses instead of when not set to `all` */ export let rounded: | 'all' + | 'none' + | 'edge' | 'top' | 'bottom' | 'left' @@ -65,10 +69,27 @@ }); $: dimensions = $getDimensions(bar) ?? { x: 0, y: 0, width: 0, height: 0 }; - $: topLeft = ['all', 'top', 'left', 'top-left'].includes(rounded); - $: topRight = ['all', 'top', 'right', 'top-right'].includes(rounded); - $: bottomLeft = ['all', 'bottom', 'left', 'bottom-left'].includes(rounded); - $: bottomRight = ['all', 'bottom', 'right', 'bottom-right'].includes(rounded); + $: isVertical = isScaleBand($xScale); + $: valueAccessor = accessor(isVertical ? y : x); + $: value = valueAccessor(bar); + $: resolvedValue = Array.isArray(value) ? greatestAbs(value) : value; + + // Resolved `rounded="edge"` based on orientation and value + $: _rounded = + rounded === 'edge' + ? isVertical + ? resolvedValue >= 0 + ? 'top' + : 'bottom' + : resolvedValue >= 0 + ? 'right' + : 'left' + : rounded; + + $: topLeft = ['all', 'top', 'left', 'top-left'].includes(_rounded); + $: topRight = ['all', 'top', 'right', 'top-right'].includes(_rounded); + $: bottomLeft = ['all', 'bottom', 'left', 'bottom-left'].includes(_rounded); + $: bottomRight = ['all', 'bottom', 'right', 'bottom-right'].includes(_rounded); $: width = dimensions.width; $: height = dimensions.height; @@ -87,7 +108,7 @@ .join(''); -{#if rounded === 'all'} +{#if _rounded === 'all' || radius === 0} ['spring'] = undefined; export let tweened: ComponentProps['tweened'] = undefined; + + $: _data = chartDataArray(data ?? $contextData); - {#each chartDataArray(data ?? $contextData) as item} + {#each _data as d, i} d.stackData[i] + : (s.value ?? (s.data ? undefined : s.key)); const barsProps: ComponentProps = { data: s.data, - x: !isVertical - ? stackSeries - ? (d) => d.stackData[i] - : (s.value ?? (s.data ? undefined : s.key)) - : undefined, - y: isVertical - ? stackSeries - ? (d) => d.stackData[i] - : (s.value ?? (s.data ? undefined : s.key)) - : undefined, + x: !isVertical ? valueAccesor : undefined, + y: isVertical ? valueAccesor : undefined, x1: isVertical && groupSeries ? (d) => s.value ?? s.key : undefined, y1: !isVertical && groupSeries ? (d) => s.value ?? s.key : undefined, + rounded: seriesLayout.startsWith('stack') && i !== series.length - 1 ? 'none' : 'edge', radius: 4, strokeWidth: 1, fill: s.color, diff --git a/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte b/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte index d67f153f0..049add203 100644 --- a/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte @@ -229,13 +229,11 @@ { key: 'value', color: 'hsl(var(--color-primary))', - props: { rounded: 'top' }, }, { key: 'baseline', value: (d) => -d.baseline, color: 'hsl(var(--color-secondary))', - props: { rounded: 'bottom' }, }, ]} /> @@ -261,12 +259,10 @@ key: 'male', value: (d) => -d.male, color: 'hsl(var(--color-primary))', - props: { rounded: 'left' }, }, { key: 'female', color: 'hsl(var(--color-secondary))', - props: { rounded: 'right' }, }, ]} > @@ -316,13 +312,11 @@ key: 'male', value: (d) => -d.male / totalPopulation, color: 'hsl(var(--color-primary))', - props: { rounded: 'left' }, }, { key: 'female', value: (d) => d.female / totalPopulation, color: 'hsl(var(--color-secondary))', - props: { rounded: 'right' }, }, ]} > @@ -549,7 +543,12 @@ data={wideData} x="year" series={[ - { key: 'apples', value: (d) => -d.apples, color: 'hsl(var(--color-danger))' }, + { + key: 'apples', + value: (d) => -d.apples, + color: 'hsl(var(--color-danger))', + props: { rounded: 'bottom' }, + }, { key: 'bananas', color: 'hsl(var(--color-warning))', @@ -797,7 +796,7 @@ axis="x" bandPadding={0.1} props={{ - bars: { radius: 4, strokeWidth: 0 }, + bars: { radius: 4, strokeWidth: 0, rounded: 'all' }, highlight: { bar: { radius: 4, class: 'stroke-current stroke-2 fill-none' } }, xAxis: { ticks: (scale) => scaleTime(scale.domain(), scale.range()).ticks() }, rule: { y: false }, diff --git a/packages/layerchart/src/routes/docs/examples/Bars/+page.svelte b/packages/layerchart/src/routes/docs/examples/Bars/+page.svelte index 68b136b4d..586efa285 100644 --- a/packages/layerchart/src/routes/docs/examples/Bars/+page.svelte +++ b/packages/layerchart/src/routes/docs/examples/Bars/+page.svelte @@ -126,7 +126,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -179,7 +179,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -215,11 +215,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -255,15 +251,11 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + - + @@ -299,7 +291,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -324,7 +316,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -351,7 +343,7 @@ rule /> - + @@ -378,7 +370,7 @@ rule /> - + @@ -406,7 +398,7 @@ ticks={4} rule /> - + @@ -433,7 +425,7 @@ ticks={(scale) => scaleTime(scale.domain(), scale.range()).ticks(4)} rule /> - + @@ -460,7 +452,7 @@ rule /> - + @@ -491,7 +483,6 @@ {#each data as bar, i} @@ -522,7 +513,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -556,7 +547,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + format(d, PeriodType.Day, { variant: 'short' })} rule /> - + - + - + format(d, PeriodType.Day, { variant: 'short' })} rule /> - - + + @@ -711,14 +702,8 @@ format(d, PeriodType.Day, { variant: 'short' })} /> - - -d.baseline} - radius={4} - rounded="left" - strokeWidth={1} - class="fill-secondary" - /> + + -d.baseline} rounded="left" strokeWidth={1} class="fill-secondary" /> @@ -771,7 +756,6 @@ x: { duration: 500, easing: cubicInOut }, width: { duration: 500, easing: cubicInOut }, }} - radius={4} strokeWidth={1} class="fill-primary" /> @@ -819,7 +803,6 @@ x: { duration: 500, easing: cubicInOut, delay: i * 30 }, width: { duration: 500, easing: cubicInOut, delay: i * 30 }, }} - radius={4} strokeWidth={1} class="fill-primary" /> @@ -856,7 +839,7 @@ - + @@ -909,7 +892,7 @@ - + @@ -962,7 +945,7 @@ - + @@ -1019,7 +1002,7 @@ - + @@ -1096,7 +1079,6 @@ format(d, PeriodType.Day, { variant: 'short' })} rule /> - + diff --git a/packages/layerchart/src/routes/docs/examples/Columns/+page.svelte b/packages/layerchart/src/routes/docs/examples/Columns/+page.svelte index f0168b179..840076de3 100644 --- a/packages/layerchart/src/routes/docs/examples/Columns/+page.svelte +++ b/packages/layerchart/src/routes/docs/examples/Columns/+page.svelte @@ -126,7 +126,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -179,7 +179,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -215,11 +215,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -255,15 +251,11 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + - + @@ -299,7 +291,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -324,7 +316,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -352,7 +344,7 @@ rule /> - + @@ -380,7 +372,7 @@ rule /> - + @@ -408,7 +400,7 @@ ticks={4} rule /> - + @@ -435,7 +427,7 @@ ticks={(scale) => scaleTime(scale.domain(), scale.range()).ticks(4)} rule /> - + @@ -462,7 +454,7 @@ rule /> - + @@ -493,7 +485,6 @@ {#each data as bar, i} @@ -524,7 +515,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + @@ -558,7 +549,7 @@ format={(d) => format(d, PeriodType.Day, { variant: 'short' })} rule /> - + format(d, PeriodType.Day, { variant: 'short' })} rule /> - + - + - + format(d, PeriodType.Day, { variant: 'short' })} rule /> - - + + @@ -714,14 +705,8 @@ format(Math.abs(d), 'integer')} /> format(d, PeriodType.Day, { variant: 'short' })} /> - - -d.baseline} - radius={4} - rounded="bottom" - strokeWidth={1} - class="fill-secondary" - /> + + -d.baseline} rounded="bottom" strokeWidth={1} class="fill-secondary" /> @@ -773,7 +758,6 @@ y: { duration: 500, easing: cubicInOut }, height: { duration: 500, easing: cubicInOut }, }} - radius={4} strokeWidth={1} class="fill-primary" /> @@ -821,7 +805,6 @@ y: { duration: 500, easing: cubicInOut, delay: i * 30 }, height: { duration: 500, easing: cubicInOut, delay: i * 30 }, }} - radius={4} strokeWidth={1} class="fill-primary" /> @@ -858,7 +841,7 @@ - + @@ -911,7 +894,7 @@ - + @@ -964,7 +947,7 @@ - + @@ -1021,7 +1004,7 @@ - + @@ -1097,7 +1080,6 @@ format(d, PeriodType.Day, { variant: 'short' })} rule /> - +