From ecdce7011c0bb042c6d1c25f16cd636767a9bad6 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Thu, 11 Apr 2024 11:13:32 -0400 Subject: [PATCH 01/18] Brush WIP --- .../src/lib/components/Brush.svelte | 157 ++++++++++++++++++ .../layerchart/src/routes/_NavMenu.svelte | 1 + .../routes/docs/components/Brush/+page.svelte | 128 ++++++++++++++ .../src/routes/docs/components/Brush/+page.ts | 18 ++ 4 files changed, 304 insertions(+) create mode 100644 packages/layerchart/src/lib/components/Brush.svelte create mode 100644 packages/layerchart/src/routes/docs/components/Brush/+page.svelte create mode 100644 packages/layerchart/src/routes/docs/components/Brush/+page.ts diff --git a/packages/layerchart/src/lib/components/Brush.svelte b/packages/layerchart/src/lib/components/Brush.svelte new file mode 100644 index 000000000..272b011b4 --- /dev/null +++ b/packages/layerchart/src/lib/components/Brush.svelte @@ -0,0 +1,157 @@ + + +
+ {#if min != null} +
+ +
+
+ {/if} +
diff --git a/packages/layerchart/src/routes/_NavMenu.svelte b/packages/layerchart/src/routes/_NavMenu.svelte index c95ea60c1..5ec180562 100644 --- a/packages/layerchart/src/routes/_NavMenu.svelte +++ b/packages/layerchart/src/routes/_NavMenu.svelte @@ -66,6 +66,7 @@ 'Threshold', ], Interactions: [ + 'Brush', 'Highlight', 'HitCanvas', 'Tooltip', diff --git a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte new file mode 100644 index 000000000..44f9c8c24 --- /dev/null +++ b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte @@ -0,0 +1,128 @@ + + +

Examples

+ +

Clip

+ + +
+
+ + + + + + + + + +
+ +
+ + + + + + { + if (e.detail.xDomain) { + xDomain = e.detail.xDomain; + } + }} + /> + +
+
+
+ +

Filter data

+ + +
+
+ + + + + + + +
+ +
+ + + + + + { + if (e.detail.xDomain) { + xDomain = e.detail.xDomain; + } + }} + /> + +
+
+
diff --git a/packages/layerchart/src/routes/docs/components/Brush/+page.ts b/packages/layerchart/src/routes/docs/components/Brush/+page.ts new file mode 100644 index 000000000..e5984947e --- /dev/null +++ b/packages/layerchart/src/routes/docs/components/Brush/+page.ts @@ -0,0 +1,18 @@ +import { parse } from 'svelte-ux'; + +import api from '$lib/components/Brush.svelte?raw&sveld'; +import source from '$lib/components/Brush.svelte?raw'; +import pageSource from './+page.svelte?raw'; + +export async function load() { + return { + appleStock: await fetch('/data/examples/date/apple-stock.json').then(async (r) => + parse(await r.text()) + ), + meta: { + api, + source, + pageSource, + }, + }; +} From 3a4ab6bd316f707d6ccd7c994046bf29c82455d7 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 12 Apr 2024 22:23:13 -0400 Subject: [PATCH 02/18] [ChartClipPath] Rename `includePadding` to `full` to match `` --- .changeset/purple-tools-eat.md | 5 +++++ .../src/lib/components/ChartClipPath.svelte | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 .changeset/purple-tools-eat.md diff --git a/.changeset/purple-tools-eat.md b/.changeset/purple-tools-eat.md new file mode 100644 index 000000000..197227d9b --- /dev/null +++ b/.changeset/purple-tools-eat.md @@ -0,0 +1,5 @@ +--- +'layerchart': minor +--- + +[ChartClipPath] Remove padding by default (opt-in with `full`) diff --git a/packages/layerchart/src/lib/components/ChartClipPath.svelte b/packages/layerchart/src/lib/components/ChartClipPath.svelte index 319f3c0a6..670908fb8 100644 --- a/packages/layerchart/src/lib/components/ChartClipPath.svelte +++ b/packages/layerchart/src/lib/components/ChartClipPath.svelte @@ -5,15 +5,15 @@ const { width, height, padding } = getContext('LayerCake'); - /** Whether clipping should include chart padding (ex. axis) */ - export let includePadding = false; + /** Include padding area (ex. axis) */ + export let full = false; From 8e1d9f18f75b2c53ffbbae5ee98e44a8a7b1120d Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 12 Apr 2024 22:26:35 -0400 Subject: [PATCH 03/18] [Frame] Expose `rectEl` and forward `mousedown` and `touchstart` events --- .changeset/five-phones-tease.md | 5 +++++ packages/layerchart/src/lib/components/Frame.svelte | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 .changeset/five-phones-tease.md diff --git a/.changeset/five-phones-tease.md b/.changeset/five-phones-tease.md new file mode 100644 index 000000000..4d0110c38 --- /dev/null +++ b/.changeset/five-phones-tease.md @@ -0,0 +1,5 @@ +--- +"layerchart": patch +--- + +[Frame] Expose `rectEl` and forward `mousedown` and `touchstart` events diff --git a/packages/layerchart/src/lib/components/Frame.svelte b/packages/layerchart/src/lib/components/Frame.svelte index 1be6c57ff..8d9bb0dde 100644 --- a/packages/layerchart/src/lib/components/Frame.svelte +++ b/packages/layerchart/src/lib/components/Frame.svelte @@ -5,6 +5,9 @@ /** Include padding area */ export let full = false; + + /** Access underlying `` element */ + export let rectEl: SVGRectElement; From 19eebce45ffa0e33375be9749be0143175118d50 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 12 Apr 2024 22:47:24 -0400 Subject: [PATCH 04/18] [Brush] Forward `mousedown` and `touchstart` events --- .changeset/itchy-cameras-guess.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/itchy-cameras-guess.md diff --git a/.changeset/itchy-cameras-guess.md b/.changeset/itchy-cameras-guess.md new file mode 100644 index 000000000..1859769ec --- /dev/null +++ b/.changeset/itchy-cameras-guess.md @@ -0,0 +1,5 @@ +--- +'layerchart': patch +--- + +[Brush] Forward `mousedown` and `touchstart` events From ceec72b48c6147b7a78db01799b475e531552cf3 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 12 Apr 2024 22:49:33 -0400 Subject: [PATCH 05/18] [Brush] Use SVG/rect instaed of HTML/div --- .../src/lib/components/Brush.svelte | 73 ++++++++++--------- .../routes/docs/components/Brush/+page.svelte | 32 ++++---- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/packages/layerchart/src/lib/components/Brush.svelte b/packages/layerchart/src/lib/components/Brush.svelte index 272b011b4..c414419e6 100644 --- a/packages/layerchart/src/lib/components/Brush.svelte +++ b/packages/layerchart/src/lib/components/Brush.svelte @@ -3,17 +3,21 @@ import { createEventDispatcher, getContext } from 'svelte'; import { clamp, cls } from 'svelte-ux'; + import Frame from './Frame.svelte'; + import Group from './Group.svelte'; let min: number | null = null; let max: number | null = null; - let brushEl: HTMLDivElement; + export let handleWidth = 5; + + let frameEl: SVGRectElement; const dispatch = createEventDispatcher<{ change: { xDomain?: [any, any]; yDomain?: [any, any] }; }>(); - const { xScale, padding } = getContext('LayerCake'); + const { xScale, width, height } = getContext('LayerCake'); export let classes: { root?: string; @@ -25,7 +29,7 @@ * Convert pixel value `x` to percent of element's width */ function pixelToPercent(x: number) { - const { left, right } = brushEl.getBoundingClientRect(); + const { left, right } = frameEl.getBoundingClientRect(); const scale = scaleLinear([left, right], [0, 1]).clamp(true); return scale(x); } @@ -60,7 +64,7 @@ if (e instanceof TouchEvent) { if (e.changedTouches.length !== 1) return; if (e.changedTouches[0].identifier !== startTouch?.identifier) return; - } else if (e.target === brushEl) { + } else if (e.target === frameEl) { clear(); } @@ -110,48 +114,47 @@ // Track last min/max to fix infinite loop let lastExtents: [number | null, number | null] = [null, null]; - $: console.log({ min, max, lastExtents }); $: if ( ((min == null && max == null) || min !== max) && (lastExtents[0] !== min || lastExtents[1] !== max) ) { lastExtents = [min, max]; - console.log('dispatching change'); dispatch('change', { xDomain: [domainScale.invert(min ?? 0), domainScale.invert(max ?? 1)] }); } - $: left = 100 * (min ?? 0); - $: right = 100 * (1 - (max ?? 1)); + $: left = $width * (min ?? 0); + $: right = $width * (max ?? 0); -
- {#if min != null} -
+ + + + + -
+ -
+ + + - {/if} -
+ + diff --git a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte index 44f9c8c24..cfab3c427 100644 --- a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte @@ -24,7 +24,7 @@

Examples

-

Clip

+

Clip data

@@ -61,15 +61,14 @@ > + { + if (e.detail.xDomain) { + xDomain = e.detail.xDomain; + } + }} + /> - - { - if (e.detail.xDomain) { - xDomain = e.detail.xDomain; - } - }} - />
@@ -113,15 +112,14 @@ > + { + if (e.detail.xDomain) { + xDomain = e.detail.xDomain; + } + }} + /> - - { - if (e.detail.xDomain) { - xDomain = e.detail.xDomain; - } - }} - />
From 599185a3528f1cd37c8b8be24669822d881558a6 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 12 Apr 2024 23:06:00 -0400 Subject: [PATCH 06/18] Forward `dblclick` event --- .changeset/five-phones-tease.md | 4 ++-- .changeset/pretty-vans-exist.md | 5 +++++ packages/layerchart/src/lib/components/Frame.svelte | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/pretty-vans-exist.md diff --git a/.changeset/five-phones-tease.md b/.changeset/five-phones-tease.md index 4d0110c38..d09476a1e 100644 --- a/.changeset/five-phones-tease.md +++ b/.changeset/five-phones-tease.md @@ -1,5 +1,5 @@ --- -"layerchart": patch +'layerchart': patch --- -[Frame] Expose `rectEl` and forward `mousedown` and `touchstart` events +[Frame] Expose `rectEl` and forward `mousedown`, `touchstart`, and `dblclick` events diff --git a/.changeset/pretty-vans-exist.md b/.changeset/pretty-vans-exist.md new file mode 100644 index 000000000..0a69ef836 --- /dev/null +++ b/.changeset/pretty-vans-exist.md @@ -0,0 +1,5 @@ +--- +"layerchart": patch +--- + +[Group] Forward `dblclick` event diff --git a/packages/layerchart/src/lib/components/Frame.svelte b/packages/layerchart/src/lib/components/Frame.svelte index 8d9bb0dde..737d7c09c 100644 --- a/packages/layerchart/src/lib/components/Frame.svelte +++ b/packages/layerchart/src/lib/components/Frame.svelte @@ -18,6 +18,7 @@ on:click on:mousedown on:touchstart + on:dblclick bind:this={rectEl} {...$$restProps} /> From 0f497bb43335e112161fd3a0c3c2a74cc0cc298c Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 12 Apr 2024 23:10:27 -0400 Subject: [PATCH 07/18] [Brush] Support double clicking on frame to select all, double clicking on range to clear. Move classes to Group contrainers --- .../src/lib/components/Brush.svelte | 29 +++++++++++++++---- .../routes/docs/components/Brush/+page.svelte | 28 +++--------------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/packages/layerchart/src/lib/components/Brush.svelte b/packages/layerchart/src/lib/components/Brush.svelte index c414419e6..560fd7e89 100644 --- a/packages/layerchart/src/lib/components/Brush.svelte +++ b/packages/layerchart/src/lib/components/Brush.svelte @@ -109,6 +109,11 @@ max = null; } + function selectAll() { + min = 0; + max = 1; + } + // Map percentage back to domain $: domainScale = $xScale.copy().range([0, 1]); @@ -131,30 +136,42 @@ class={cls('frame', 'fill-transparent')} on:mousedown={reset} on:touchstart={reset} + on:dblclick={() => selectAll()} bind:rectEl={frameEl} /> - + clear()} + > - + - + diff --git a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte index cfab3c427..084da8991 100644 --- a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte @@ -50,22 +50,12 @@
- + { - if (e.detail.xDomain) { - xDomain = e.detail.xDomain; - } + xDomain = e.detail.xDomain; }} /> @@ -101,22 +91,12 @@
- + { - if (e.detail.xDomain) { - xDomain = e.detail.xDomain; - } + xDomain = e.detail.xDomain; }} /> From 843ea03892ea1feb1fd08d0fb5d14c2f7f5f6a21 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Sat, 13 Apr 2024 01:15:41 -0400 Subject: [PATCH 08/18] [Brush] Use domain values for min/max instead of percentages to enable initializing selecction and reduce mapping (pixels <-> domain instead of pixels <-> percent <-> domain) --- .../src/lib/components/Brush.svelte | 81 +++++++++--------- .../routes/docs/components/Brush/+page.svelte | 84 ++++++++++++++++++- 2 files changed, 124 insertions(+), 41 deletions(-) diff --git a/packages/layerchart/src/lib/components/Brush.svelte b/packages/layerchart/src/lib/components/Brush.svelte index 560fd7e89..35d436f29 100644 --- a/packages/layerchart/src/lib/components/Brush.svelte +++ b/packages/layerchart/src/lib/components/Brush.svelte @@ -1,23 +1,23 @@ diff --git a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte index 084da8991..61532a5c8 100644 --- a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte @@ -8,13 +8,13 @@ import Area from '$lib/components/Area.svelte'; import Axis from '$lib/components/Axis.svelte'; import Brush from '$lib/components/Brush.svelte'; - import Points from '$lib/components/Points.svelte'; import ChartClipPath from '$lib/components/ChartClipPath.svelte'; export let data; let xDomain = [null, null]; + let xDomain2 = [new Date('2010-01-01'), new Date('2011-12-31')]; $: filteredData = xDomain[0] != null && xDomain[1] != null @@ -24,6 +24,48 @@

Examples

+

Initialize selection

+ + +
+
+ + + + + + + + + +
+ +
+ + + + { + xDomain2 = e.detail.xDomain; + }} + /> + + +
+
+
+

Clip data

@@ -104,3 +146,43 @@
+ + From 9d666290ee45dacd36fd4231efddf0242ca3d69e Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Sat, 13 Apr 2024 01:16:05 -0400 Subject: [PATCH 09/18] Cleanup --- .../layerchart/src/routes/docs/components/Brush/+page.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte index 61532a5c8..32b804044 100644 --- a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte @@ -1,5 +1,4 @@ @@ -156,7 +161,7 @@ width={handleWidth} height={$height} class={cls('fill-transparent cursor-ew-resize select-none')} - on:dblclick={() => (min = xDomainMin)} + on:dblclick={() => (xDomain[0] = xDomainMin)} /> @@ -170,7 +175,7 @@ width={handleWidth} height={$height} class={cls('fill-transparent cursor-ew-resize select-none')} - on:dblclick={() => (max = xDomainMax)} + on:dblclick={() => (xDomain[1] = xDomainMax)} /> diff --git a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte index 32b804044..733047ea8 100644 --- a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte @@ -52,13 +52,7 @@ - { - xDomain2 = e.detail.xDomain; - }} - /> +
From 606855245bcaaa7700753b3ba4a230039f7edc22 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Sun, 14 Apr 2024 11:12:50 -0400 Subject: [PATCH 14/18] Update Brush examples to use Svelte UX's to localize xDomain variable per example, and use for Area fill --- .../layerchart/src/lib/components/index.ts | 1 + .../routes/docs/components/Brush/+page.svelte | 217 +++++++++--------- 2 files changed, 105 insertions(+), 113 deletions(-) diff --git a/packages/layerchart/src/lib/components/index.ts b/packages/layerchart/src/lib/components/index.ts index 8c930a3c3..5042f5034 100644 --- a/packages/layerchart/src/lib/components/index.ts +++ b/packages/layerchart/src/lib/components/index.ts @@ -8,6 +8,7 @@ export { default as Axis } from './Axis.svelte'; export { default as Bar } from './Bar.svelte'; export { default as Bars } from './Bars.svelte'; export { default as Blur } from './Blur.svelte'; +export { default as Brush } from './Brush.svelte'; export { default as Bounds } from './Bounds.svelte'; export { default as Calendar } from './Calendar.svelte'; export { default as Canvas } from './layout/Canvas.svelte'; diff --git a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte index 733047ea8..67655179b 100644 --- a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte @@ -1,5 +1,6 @@

Examples

-

Initialize selection

- - -
-
- - - - - - - - - -
- -
- - - - - - -
-
-
-

Clip data

-
- - - - - + +
+ + + + + + + + + + + +
+ +
+ + - - - -
- -
- - - - { - xDomain = e.detail.xDomain; - }} - /> - - -
+ { + set(e.detail.xDomain); + }} + /> + +
+
+
@@ -103,44 +82,58 @@
-
- - - - - - - -
- -
- - - - { - xDomain = e.detail.xDomain; - }} - /> - - -
+ +
+ + (xDomain[0] == null || d.date >= xDomain[0]) && + (xDomain[1] == null || d.date <= xDomain[1]) + )} + x="date" + xScale={scaleTime()} + y="value" + yDomain={[0, null]} + yNice + padding={{ left: 16, bottom: 24 }} + > + + + + + + + + +
+ +
+ + + + { + set(e.detail.xDomain); + }} + /> + + +
+
- + From 7c1e1ef0e234e9e41ca82c22a7e5decb90be56d0 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Sun, 14 Apr 2024 12:41:21 -0400 Subject: [PATCH 15/18] Add randomWalk() util --- packages/layerchart/src/lib/utils/genData.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/layerchart/src/lib/utils/genData.ts b/packages/layerchart/src/lib/utils/genData.ts index 90705e7de..857a3cacb 100644 --- a/packages/layerchart/src/lib/utils/genData.ts +++ b/packages/layerchart/src/lib/utils/genData.ts @@ -1,4 +1,7 @@ import { addMinutes, startOfDay, startOfToday, subDays } from 'date-fns'; +import { cumsum } from 'd3-array'; +import { randomNormal } from 'd3-random'; + import { degreesToRadians, radiansToDegrees } from './math.js'; /** @@ -19,6 +22,14 @@ export function getRandomInteger(min: number, max: number, includeMax = true) { return Math.floor(Math.random() * (max - min + (includeMax ? 1 : 0)) + min); } +/** + * @see: https://observablehq.com/@d3/d3-cumsum + */ +export function randomWalk(options?: { count?: number }) { + const random = randomNormal(); + return Array.from(cumsum({ length: options?.count ?? 100 }, random)); +} + export function createSeries(options: { count?: number; min: number; From 7cc35865ca2bb651ada789641350ed9cba6b8a57 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Sun, 14 Apr 2024 12:43:31 -0400 Subject: [PATCH 16/18] Add sync'd brush example (using ) --- .../routes/docs/components/Brush/+page.svelte | 101 ++++++++++-------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte index 67655179b..081e3215d 100644 --- a/packages/layerchart/src/routes/docs/components/Brush/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Brush/+page.svelte @@ -1,6 +1,7 @@