From c5d1392358ca71caba3b55a86676cc0d9d054fd4 Mon Sep 17 00:00:00 2001 From: gka Date: Tue, 3 Jun 2025 08:20:31 +0200 Subject: [PATCH 01/23] ts: add typescript def for mark defaults --- src/lib/types.ts | 178 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 154 insertions(+), 24 deletions(-) diff --git a/src/lib/types.ts b/src/lib/types.ts index e2a58fb9..b485dd2c 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -4,6 +4,32 @@ import type { MouseEventHandler } from 'svelte/elements'; import type { MarkerShape } from './marks/helpers/Marker.svelte'; import type { Writable } from 'svelte/store'; import type * as CSS from 'csstype'; +import type { AreaMarkProps } from './marks/Area.svelte'; +import type { ArrowMarkProps } from './marks/Arrow.svelte'; +import type { AxisXMarkProps } from './marks/AxisX.svelte'; +import type { AxisYMarkProps } from './marks/AxisY.svelte'; +import type { BarXMarkProps } from './marks/BarX.svelte'; +import type { CellMarkProps } from './marks/Cell.svelte'; +import type { DotMarkProps } from './marks/Dot.svelte'; +import type { FrameMarkProps } from './marks/Frame.svelte'; +import type { GeoMarkProps } from './marks/Geo.svelte'; +import type { GraticuleMarkProps } from './marks/Graticule.svelte'; +import type { LineMarkProps } from './marks/Line.svelte'; +import type { LinkMarkProps } from './marks/Link.svelte'; +import type { RectMarkProps } from './marks/Rect.svelte'; +import type { RuleXMarkProps } from './marks/RuleX.svelte'; +import type { SphereMarkProps } from './marks/Sphere.svelte'; +import type { SpikeMarkProps } from './marks/Spike.svelte'; +import type { TextMarkProps } from './marks/Text.svelte'; +import type { TickXMarkProps } from './marks/TickX.svelte'; +import type { VectorMarkProps } from './marks/Vector.svelte'; +import type { BrushMarkProps } from './marks/Brush.svelte'; +import type { BrushXMarkProps } from './marks/BrushX.svelte'; +import type { BrushYMarkProps } from './marks/BrushY.svelte'; +import type { RectXMarkProps } from './marks/RectX.svelte'; +import type { RectYMarkProps } from './marks/RectY.svelte'; +import type { RuleYMarkProps } from './marks/RuleY.svelte'; +import type { TickYMarkProps } from './marks/TickY.svelte'; export type MarkType = | 'area' @@ -867,53 +893,157 @@ export type DefaultOptions = { */ inset: number; /** - * default tick line length + * default color scheme */ - tickSize: number; + colorScheme: ColorScheme; /** - * default padding between tick line and tick label + * locale, used for automatic axis ticks */ - tickPadding: number; + locale: string; /** - * default font size for tick labels + * default number format for axis ticks */ - tickFontSize: number; + numberFormat: Intl.NumberFormatOptions; /** - * default anchor for x axis + * default dot radius for line markers, used in dot, circle, and circle-stroke markers */ - axisXAnchor: 'bottom' | 'top'; + markerDotRadius: number; /** - * default anchor for y axis + * default props for area marks */ - axisYAnchor: 'left' | 'right'; + area: Omit; /** - * default spacing between ticks in AxisX and GridX + * default props for arrow marks */ - xTickSpacing: number; + arrow: Omit; /** - * default spacing between ticks in AxisY and GridY + * default props for axis marks, applied to both axisX and axisY marks */ - yTickSpacing: number; + axis: BaseMarkProps; /** - * default color scheme + * default props for axisX marks */ - colorScheme: ColorScheme; + axisX: Omit; /** - * default step for graticule, in degrees + * default props for axisY marks */ - graticuleStep: number; + axisY: Omit; /** - * locale, used for automatic axis ticks + * default props for bar marks, applied to both barX and barY marks */ - locale: string; + bar: Omit; /** - * default number format for axis ticks + * default props for barX marks */ - numberFormat: Intl.NumberFormatOptions; + barX: Omit; /** - * default dot radius for line markers, used in dot, circle, and circle-stroke markers + * default props for barY marks */ - markerDotRadius: number; + barY: Omit; + /** + * default props for brush marks, applied to brush, brushX and brushY marks + */ + brush: Omit; + /** + * default props for brushX marks + */ + brushX: Omit; + /** + * default props for brushY marks + */ + brushY: Omit; + /** + * default props for cell marks + */ + cell: Omit; + /** + * default props for dot marks + */ + dot: Omit; + /** + * default props for frame marks + */ + frame: FrameMarkProps; + /** + * default props for geo marks + */ + geo: Omit; + /** + * default props for graticule marks + */ + graticule: Omit; + /** + * default props for grid marks, applied to both gridX and gridY marks + */ + grid: Omit; + /** + * default props for gridX marks + */ + gridX: Omit; + /** + * default props for gridY marks + */ + gridY: Omit; + /** + * default props for line marks + */ + line: Omit; + /** + * default props for link marks + */ + link: Omit; + /** + * default props for rect marks, applied to rect and rectX marks + */ + rect: Omit; + /** + * default props for rectX marks + */ + rectX: Omit; + /** + * default props for rectY marks + */ + rectY: Omit; + /** + * default props for rule marks + */ + rule: Omit; + /** + * default props for rule marks + */ + ruleX: Omit; + /** + * default props for rule marks + */ + ruleY: Omit; + /** + * default props for sphere marks + */ + sphere: SphereMarkProps; + /** + * default props for spike marks + */ + spike: Omit; + /** + * default props for text marks + */ + text: Omit; + /** + * default props for tick marks, applied to tickX and tickY marks + */ + tick: Omit; + /** + * default props for tickX marks + */ + tickX: Omit; + /** + * default props for tickY marks + */ + tickY: Omit; + /** + * default props for vector marks + */ + vector: Omit; }; export type MapIndexObject = { From 80c23d70e7cb6b277f0610ffee88e63e8f8fe416 Mon Sep 17 00:00:00 2001 From: gka Date: Tue, 3 Jun 2025 08:32:46 +0200 Subject: [PATCH 02/23] apply area defaults --- src/lib/marks/Area.svelte | 16 +++++++++++++--- src/lib/marks/AreaX.svelte | 10 +++++++--- src/lib/marks/AreaY.svelte | 9 +++++++-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/lib/marks/Area.svelte b/src/lib/marks/Area.svelte index 0dfa4710..fc5f9d3e 100644 --- a/src/lib/marks/Area.svelte +++ b/src/lib/marks/Area.svelte @@ -39,12 +39,22 @@ ConstantAccessor, ChannelAccessor, ScaledDataRecord, - LinkableMarkProps + LinkableMarkProps, + DefaultOptions } from '../types.js'; import type { RawValue } from '$lib/types.js'; import type { StackOptions } from '$lib/transforms/stack.js'; - let { + let markProps: AreaMarkProps = $props(); + + const DEFAULTS = { + fill: 'currentColor', + curve: 'linear', + tension: 0, + ...getContext('svelteplot/_defaults').area + }; + + const { data, /** the curve */ curve = 'linear', @@ -52,7 +62,7 @@ class: className = '', canvas = false, ...options - }: AreaMarkProps = $props(); + }: AreaMarkProps = $derived({ ...DEFAULTS, ...markProps }); const { getPlotState } = getContext('svelteplot'); const plot = $derived(getPlotState()); diff --git a/src/lib/marks/AreaX.svelte b/src/lib/marks/AreaX.svelte index 0a10c6fb..cc06b0b8 100644 --- a/src/lib/marks/AreaX.svelte +++ b/src/lib/marks/AreaX.svelte @@ -7,15 +7,19 @@ import { renameChannels } from '$lib/transforms/rename.js'; import { stackX } from '$lib/transforms/stack.js'; import { recordizeX } from '$lib/transforms/recordize.js'; - import type { ChannelAccessor } from '../types.js'; + import type { ChannelAccessor, DefaultOptions } from '../types.js'; + import { getContext } from 'svelte'; type AreaXProps = Omit & { x?: ChannelAccessor; y?: ChannelAccessor; }; - // we're discarding y1 and y2 props since they are not - let { data, stack, ...options }: AreaXProps = $props(); + let markProps: AreaMarkProps = $props(); + + const DEFAULTS = getContext('svelteplot/_defaults').areaX; + + const { data, stack, ...options }: AreaMarkProps = $derived({ ...DEFAULTS, ...markProps }); const args = $derived( renameChannels( diff --git a/src/lib/marks/AreaY.svelte b/src/lib/marks/AreaY.svelte index 05403a0c..6315ce7e 100644 --- a/src/lib/marks/AreaY.svelte +++ b/src/lib/marks/AreaY.svelte @@ -7,7 +7,8 @@ import { renameChannels } from '$lib/transforms/rename.js'; import { stackY } from '$lib/transforms/stack.js'; import { recordizeY } from '$lib/transforms/recordize.js'; - import type { ChannelAccessor } from '../types.js'; + import type { ChannelAccessor, DefaultOptions } from '../types.js'; + import { getContext } from 'svelte'; /** * AreaY mark foo @@ -17,7 +18,11 @@ y?: ChannelAccessor; }; - let { data, stack, ...options }: AreaYProps = $props(); + let markProps: AreaMarkProps = $props(); + + const DEFAULTS = getContext('svelteplot/_defaults').areaY; + + const { data, stack, ...options }: AreaMarkProps = $derived({ ...DEFAULTS, ...markProps }); const args = $derived( renameChannels( From 100bb88bc0bb879513f5ce3b46ca67ebbbab404d Mon Sep 17 00:00:00 2001 From: gka Date: Tue, 3 Jun 2025 08:33:39 +0200 Subject: [PATCH 03/23] redo screenshots --- screenshot-examples.js | 6 ++++-- .../examples/[group]/[page]/+page.svelte | 13 ++++++++++--- .../examples/axis/datawrapper-ticks.dark.png | Bin 42429 -> 42651 bytes static/examples/axis/datawrapper-ticks.png | Bin 41328 -> 41544 bytes static/examples/axis/major-minor.dark.png | Bin 39226 -> 39353 bytes static/examples/axis/major-minor.png | Bin 38025 -> 38151 bytes static/examples/axis/tick-count.dark.png | Bin 40094 -> 40251 bytes static/examples/axis/tick-count.png | Bin 38807 -> 38941 bytes static/examples/axis/tick-interval.dark.png | Bin 34703 -> 34838 bytes static/examples/axis/tick-interval.png | Bin 33391 -> 33526 bytes static/examples/axis/tick-spacing.dark.png | Bin 48073 -> 48220 bytes static/examples/axis/tick-spacing.png | Bin 46727 -> 46876 bytes static/examples/bar/defaults.dark.png | Bin 0 -> 10157 bytes static/examples/bar/defaults.png | Bin 0 -> 9767 bytes static/examples/bar/linked-bars.dark.png | Bin 6613 -> 9883 bytes static/examples/bar/linked-bars.png | Bin 6644 -> 9927 bytes static/examples/bar/shuffled-bars.dark.png | Bin 34297 -> 34430 bytes static/examples/bar/shuffled-bars.png | Bin 34359 -> 34492 bytes static/examples/brush/constrained.dark.png | Bin 0 -> 72954 bytes static/examples/brush/constrained.png | Bin 0 -> 71465 bytes static/examples/brush/filter.dark.png | Bin 0 -> 72954 bytes static/examples/brush/filter.png | Bin 0 -> 71465 bytes .../examples/brush/overview-detail.dark.png | Bin 0 -> 42384 bytes static/examples/brush/overview-detail.png | Bin 0 -> 41389 bytes .../examples/brush/zoomable-scatter.dark.png | Bin 0 -> 70031 bytes static/examples/brush/zoomable-scatter.png | Bin 0 -> 69399 bytes static/examples/dot/0-scatterplot.dark.png | Bin 66088 -> 66234 bytes static/examples/dot/0-scatterplot.png | Bin 62582 -> 62719 bytes .../dot/1-colored-scatterplot.dark.png | Bin 73083 -> 73221 bytes static/examples/dot/1-colored-scatterplot.png | Bin 70957 -> 71094 bytes static/examples/dot/2-symbol-channel.dark.png | Bin 70787 -> 78033 bytes static/examples/dot/2-symbol-channel.png | Bin 70220 -> 77394 bytes static/examples/dot/3-dot-plot.dark.png | Bin 87538 -> 87672 bytes static/examples/dot/3-dot-plot.png | Bin 87326 -> 87461 bytes static/examples/dot/bubble-matrix.dark.png | Bin 32718 -> 35752 bytes static/examples/dot/bubble-matrix.png | Bin 31262 -> 34257 bytes static/examples/geo/earthquakes.dark.png | Bin 128429 -> 128642 bytes static/examples/geo/earthquakes.png | Bin 126093 -> 126298 bytes .../geo/us-choropleth-canvas.dark.png | Bin 156870 -> 166489 bytes static/examples/geo/us-choropleth-canvas.png | Bin 151453 -> 161136 bytes static/examples/geo/us-choropleth.dark.png | Bin 113851 -> 123602 bytes static/examples/geo/us-choropleth.png | Bin 111471 -> 121193 bytes .../examples/grid/clipped-gridlines.dark.png | Bin 36037 -> 36174 bytes static/examples/grid/clipped-gridlines.png | Bin 34927 -> 35062 bytes static/examples/line/apple-stock.dark.png | Bin 35015 -> 35150 bytes static/examples/line/apple-stock.png | Bin 33749 -> 33884 bytes static/examples/line/gradient-line.dark.png | Bin 49058 -> 49193 bytes static/examples/line/gradient-line.png | Bin 46835 -> 46972 bytes static/examples/line/line-grouping.dark.png | Bin 33873 -> 34008 bytes static/examples/line/line-grouping.png | Bin 33735 -> 33870 bytes static/examples/line/tour-de-france.dark.png | Bin 46834 -> 47034 bytes static/examples/line/tour-de-france.png | Bin 45723 -> 45926 bytes static/examples/regression/cars.dark.png | Bin 68300 -> 76134 bytes static/examples/regression/cars.png | Bin 68311 -> 76162 bytes static/examples/regression/faceted.dark.png | Bin 77979 -> 78205 bytes static/examples/regression/faceted.png | Bin 76072 -> 76338 bytes static/examples/regression/grouped.dark.png | Bin 79770 -> 85954 bytes static/examples/regression/grouped.png | Bin 77284 -> 83433 bytes static/examples/regression/loess.dark.png | Bin 61264 -> 65786 bytes static/examples/regression/loess.png | Bin 61128 -> 65595 bytes static/examples/regression/log.dark.png | Bin 68860 -> 69027 bytes static/examples/regression/log.png | Bin 68680 -> 68844 bytes 62 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 static/examples/bar/defaults.dark.png create mode 100644 static/examples/bar/defaults.png create mode 100644 static/examples/brush/constrained.dark.png create mode 100644 static/examples/brush/constrained.png create mode 100644 static/examples/brush/filter.dark.png create mode 100644 static/examples/brush/filter.png create mode 100644 static/examples/brush/overview-detail.dark.png create mode 100644 static/examples/brush/overview-detail.png create mode 100644 static/examples/brush/zoomable-scatter.dark.png create mode 100644 static/examples/brush/zoomable-scatter.png diff --git a/screenshot-examples.js b/screenshot-examples.js index fe6dc989..593ec016 100644 --- a/screenshot-examples.js +++ b/screenshot-examples.js @@ -94,7 +94,9 @@ const takeScreenshot = async (page, urlPath, outputPath, isDarkMode = false) => const finalOutputPath = outputPath.replace('.png', `${themeSuffix}.png`); // Wait for the Plot component to be rendered - await page.waitForSelector('.content > figure.svelteplot', { timeout: 10000 }); + await page.waitForSelector('.content figure.svelteplot ', { + timeout: 10000 + }); // Toggle dark mode if needed if (isDarkMode) { @@ -112,7 +114,7 @@ const takeScreenshot = async (page, urlPath, outputPath, isDarkMode = false) => // Get the Plot SVG element const elementHandle = await page.evaluateHandle(() => - document.querySelector('.content > figure.svelteplot > .plot-body > svg') + document.querySelector('.content .screenshot') ); // Take a screenshot of the element diff --git a/src/routes/examples/[group]/[page]/+page.svelte b/src/routes/examples/[group]/[page]/+page.svelte index c072337a..e914a24e 100644 --- a/src/routes/examples/[group]/[page]/+page.svelte +++ b/src/routes/examples/[group]/[page]/+page.svelte @@ -1,4 +1,4 @@ - + + + + + + diff --git a/src/routes/examples/brush/_index.svelte b/src/routes/examples/brush/_index.svelte new file mode 100644 index 00000000..fedffde8 --- /dev/null +++ b/src/routes/examples/brush/_index.svelte @@ -0,0 +1,5 @@ + + +

Brush examples

diff --git a/src/routes/examples/brush/constrained.svelte b/src/routes/examples/brush/constrained.svelte new file mode 100644 index 00000000..41bc4fda --- /dev/null +++ b/src/routes/examples/brush/constrained.svelte @@ -0,0 +1,43 @@ + + + + + + (brush.enabled ? 'gray' : d.species)} + symbol="species" /> + {#if brush.enabled} + + + d.culmen_length_mm >= brush.x1 && + d.culmen_length_mm <= brush.x2 && + d.culmen_depth_mm >= brush.y1 && + d.culmen_depth_mm <= brush.y2} + x="culmen_length_mm" + y="culmen_depth_mm" + stroke="species" + symbol="species" /> + {/if} + + diff --git a/src/routes/examples/brush/filter.svelte b/src/routes/examples/brush/filter.svelte new file mode 100644 index 00000000..69362363 --- /dev/null +++ b/src/routes/examples/brush/filter.svelte @@ -0,0 +1,43 @@ + + + + + + (brush.enabled ? 'gray' : d.species)} + symbol="species" /> + {#if brush.enabled} + + + d.culmen_length_mm >= brush.x1 && + d.culmen_length_mm <= brush.x2 && + d.culmen_depth_mm >= brush.y1 && + d.culmen_depth_mm <= brush.y2} + x="culmen_length_mm" + y="culmen_depth_mm" + stroke="species" + symbol="species" /> + {/if} + + diff --git a/src/routes/examples/brush/overview-detail.svelte b/src/routes/examples/brush/overview-detail.svelte new file mode 100644 index 00000000..4e1564d3 --- /dev/null +++ b/src/routes/examples/brush/overview-detail.svelte @@ -0,0 +1,73 @@ + + + + + +
+ + + + {#if brush.enabled} + + + {/if} + + +
+ + + + + diff --git a/src/routes/examples/brush/zoomable-scatter.svelte b/src/routes/examples/brush/zoomable-scatter.svelte new file mode 100644 index 00000000..08a9fef5 --- /dev/null +++ b/src/routes/examples/brush/zoomable-scatter.svelte @@ -0,0 +1,80 @@ + + + + +
+ + + {#if !isZoomedIn} + { + if (e.brush.enabled) { + domainX = [e.brush.x1, e.brush.x2]; + domainY = [e.brush.y1, e.brush.y2]; + brush.enabled = false; + isZoomedIn = true; + } + }} /> + {:else} + + {/if} + +
From e706fe77b16ba7d05298e105d177d422bb5b5064 Mon Sep 17 00:00:00 2001 From: gka Date: Tue, 3 Jun 2025 08:34:26 +0200 Subject: [PATCH 05/23] docs: more flexible example sorting --- src/lib/ui/ExamplesGrid.svelte | 2 +- src/routes/examples/+page.svelte | 21 ++++++++++++++++--- src/routes/examples/[group]/+page.svelte | 16 ++++++++++++-- .../examples/axis/datawrapper-ticks.svelte | 2 +- .../examples/geo/us-choropleth-canvas.svelte | 1 + src/routes/examples/geo/us-choropleth.svelte | 1 + 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/lib/ui/ExamplesGrid.svelte b/src/lib/ui/ExamplesGrid.svelte index 99ab7d33..e16a8484 100644 --- a/src/lib/ui/ExamplesGrid.svelte +++ b/src/lib/ui/ExamplesGrid.svelte @@ -36,7 +36,7 @@ border: 1px solid #88888822; border-radius: 2px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05); - padding: 1.5ex; + padding: 1.5ex 1.5ex 0.4ex 1.5ex; } &:hover { diff --git a/src/routes/examples/+page.svelte b/src/routes/examples/+page.svelte index 160b6564..c22d8bc4 100644 --- a/src/routes/examples/+page.svelte +++ b/src/routes/examples/+page.svelte @@ -15,7 +15,7 @@ ]; -

@@ -87,7 +100,9 @@ ].title}