From 21ae80faba1730a6e78033cd64d258a207199491 Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 7 Jun 2024 20:49:46 -0400 Subject: [PATCH 1/5] deriveScale prop --- package-lock.json | 24 +++---- src/_components/ColumnGrouped.svelte | 69 ++++++++++++++++++ src/_data/yearGroups.js | 27 +++++++ src/lib/LayerCake.svelte | 26 +++++++ src/routes/_examples.js | 2 + src/routes/_examples/ColumnGrouped.svelte | 86 +++++++++++++++++++++++ 6 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 src/_components/ColumnGrouped.svelte create mode 100644 src/_data/yearGroups.js create mode 100644 src/routes/_examples/ColumnGrouped.svelte diff --git a/package-lock.json b/package-lock.json index 72656a576..304d76c24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1026,6 +1026,18 @@ "vite": "^5.0.0" } }, + "node_modules/@sveltejs/vite-plugin-svelte/node_modules/svelte-hmr": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", + "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", + "dev": true, + "engines": { + "node": "^12.20 || ^14.13.1 || >= 16" + }, + "peerDependencies": { + "svelte": "^3.19.0 || ^4.0.0" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -4442,18 +4454,6 @@ } } }, - "node_modules/svelte-hmr": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", - "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", - "dev": true, - "engines": { - "node": "^12.20 || ^14.13.1 || >= 16" - }, - "peerDependencies": { - "svelte": "^3.19.0 || ^4.0.0" - } - }, "node_modules/svelte-preprocess": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz", diff --git a/src/_components/ColumnGrouped.svelte b/src/_components/ColumnGrouped.svelte new file mode 100644 index 000000000..0405cf9e8 --- /dev/null +++ b/src/_components/ColumnGrouped.svelte @@ -0,0 +1,69 @@ + + + + + {#each $data as group, i} + + {#each group.values as d, i} + {@const colHeight = columnHeight(d)} + {@const got = $subgroupScale($r(d))} + {@const xPos = Array.isArray(got) ? got[0] : got} + {@const colWidth = $subgroupScale.bandwidth ? $subgroupScale.bandwidth() : columnWidth(d)} + {@const yValue = $y(d)} + {@const subgroupRange = $subgroupScale.range()} + + + {#if showLabels && yValue} + {yValue} + {/if} + + {/each} + + {/each} + + + diff --git a/src/_data/yearGroups.js b/src/_data/yearGroups.js new file mode 100644 index 000000000..580d04b62 --- /dev/null +++ b/src/_data/yearGroups.js @@ -0,0 +1,27 @@ +export default [ + { + year: '1979', + apples: 2, + bananas: 15, + }, + { + year: '1980', + apples: 3, + bananas: 10, + }, + { + year: '1981', + apples: 5, + bananas: 8, + }, + { + year: '1982', + apples: 8, + bananas: 5, + }, + { + year: '1983', + apples: 18, + bananas: 3, + } +]; diff --git a/src/lib/LayerCake.svelte b/src/lib/LayerCake.svelte index 74cb64434..871b1f28c 100644 --- a/src/lib/LayerCake.svelte +++ b/src/lib/LayerCake.svelte @@ -123,6 +123,9 @@ /** @type {{ x?: [min: Number, max: Number], y?: [min: Number, max: Number], r?: [min: Number, max: Number], z?: [min: Number, max: Number] }} [extents] Manually set the extents of the x, y or r scale as a two-dimensional array of the min and max you want. Setting values here will skip any dynamic extent calculation of the data for that dimension. */ export let extents = {}; + /** @type {{ xScale?: Function, yScale?: Function, rScale?: Function, zScale?: Function }} [deriveScales] Alter one or more scales basd values of others. */ + export let deriveScales = {}; + /** @type {Array} [flatData=data] A flat version of data. */ export let flatData = undefined; @@ -216,6 +219,7 @@ const _rDomainSort = writable(rDomainSort); const _config = writable(config); const _custom = writable(custom); + const _deriveScales = writable(deriveScales); $: $_percentRange = percentRange; $: $_containerWidth = containerWidth; @@ -254,6 +258,7 @@ $: $_rScale = rScale; $: $_custom = custom; $: $_config = config; + $: $_deriveScales = deriveScales; /* -------------------------------------------- * Create derived values @@ -424,7 +429,28 @@ return $width / $height; }); + const derivedScales = Object.fromEntries(Object.entries($_deriveScales).map(([name, fn]) => { + return [name, writable(fn({ + xScale: $xScale_d.copy(), + yScale: $yScale_d.copy(), + zScale: $zScale_d.copy(), + rScale: $rScale_d.copy() + }))] + })); + + $: if ($_deriveScales) { + for (const [name, fn] of Object.entries($_deriveScales)) { + derivedScales[name].set(fn({ + xScale: $xScale_d.copy(), + yScale: $yScale_d.copy(), + zScale: $zScale_d.copy(), + rScale: $rScale_d.copy() + })); + } + } + $: context = { + ...derivedScales, activeGetters: activeGetters_d, width: width_d, height: height_d, diff --git a/src/routes/_examples.js b/src/routes/_examples.js index 3e3e05dd2..79da5c1b5 100644 --- a/src/routes/_examples.js +++ b/src/routes/_examples.js @@ -4,6 +4,7 @@ import MapSvg from './_examples/MapSvg.svelte'; import Column from './_examples/Column.svelte'; import AreaStacked from './_examples/AreaStacked.svelte'; import ColumnStacked from './_examples/ColumnStacked.svelte'; +import ColumnGrouped from './_examples/ColumnGrouped.svelte'; import MultiLine from './_examples/MultiLine.svelte'; import MapLayered from './_examples/MapLayered.svelte'; import Bar from './_examples/Bar.svelte'; @@ -51,5 +52,6 @@ export default [ { replPath: '9d0e23f494f645b4a9623c46474462f6?version=3.46.2', title: 'Beeswarm, force layout', slug: 'BeeswarmForce', component: BeeswarmForce }, // { replPath: '', title: 'Force-directed graph', slug: 'ForceDirectedGraph', component: ForceDirectedGraph }, { replPath: '1879eb5e27f74784a69b65a11844f373?version=3.46.2', title: 'Circle pack, force layout', slug: 'CirclePackForce', component: CirclePackForce }, + { replPath: '', title: 'Grouped column', slug: 'ColumnGrouped', component: ColumnGrouped }, ]; diff --git a/src/routes/_examples/ColumnGrouped.svelte b/src/routes/_examples/ColumnGrouped.svelte new file mode 100644 index 000000000..2a1997993 --- /dev/null +++ b/src/routes/_examples/ColumnGrouped.svelte @@ -0,0 +1,86 @@ + + + + +
+ { + rScale.range([0, xScale.bandwidth()]); + return rScale; + } + }} + + yDomain={[0, null]} + data={groups} + {flatData} + debug={false} + > + + + + + + + + +
From 20c5bb7d9ac46a7aac2393c19fec839660bbcb5e Mon Sep 17 00:00:00 2001 From: mhkeller Date: Fri, 7 Jun 2024 21:08:13 -0400 Subject: [PATCH 2/5] fix subgroupkey in example --- src/routes/_examples/ColumnGrouped.svelte | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/_examples/ColumnGrouped.svelte b/src/routes/_examples/ColumnGrouped.svelte index 2a1997993..b4cb31e9b 100644 --- a/src/routes/_examples/ColumnGrouped.svelte +++ b/src/routes/_examples/ColumnGrouped.svelte @@ -11,13 +11,13 @@ const groupName = 'year'; const subgroupNames = ['apples', 'bananas']; - const subgroups = groupLonger(data, subgroupNames); - const flatData = flatten(subgroups, d => d.values); - + const subgroupKey = 'subgroup'; const xKey = groupName; const yKey = 'value'; - const subgroupKey = 'subgroup'; + // TODO, make this easier to understand + const subgroups = groupLonger(data, subgroupNames); + const flatData = flatten(subgroups, d => d.values.map(q => ({ ...q, [subgroupKey]: q.group }))); const groups = data.map(d => { return { [xKey]: d[xKey], @@ -69,7 +69,7 @@ yDomain={[0, null]} data={groups} {flatData} - debug={false} + debug={true} > Date: Sun, 23 Jun 2024 13:09:50 -0400 Subject: [PATCH 3/5] whitespace --- src/lib/LayerCake.svelte | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/LayerCake.svelte b/src/lib/LayerCake.svelte index 871b1f28c..ea900eeb5 100644 --- a/src/lib/LayerCake.svelte +++ b/src/lib/LayerCake.svelte @@ -122,10 +122,8 @@ export let padding = {}; /** @type {{ x?: [min: Number, max: Number], y?: [min: Number, max: Number], r?: [min: Number, max: Number], z?: [min: Number, max: Number] }} [extents] Manually set the extents of the x, y or r scale as a two-dimensional array of the min and max you want. Setting values here will skip any dynamic extent calculation of the data for that dimension. */ export let extents = {}; - - /** @type {{ xScale?: Function, yScale?: Function, rScale?: Function, zScale?: Function }} [deriveScales] Alter one or more scales basd values of others. */ + /** @type {Object.} [deriveScales] Create new scales based on values of others. Takes an object where each key is an arbitrary name for the derived scale. The value is a function that receives an object with available scales and returns the new scale. */ export let deriveScales = {}; - /** @type {Array} [flatData=data] A flat version of data. */ export let flatData = undefined; From 4d5e764179f937be17ebc362aefaf6f361d15a60 Mon Sep 17 00:00:00 2001 From: mhkeller Date: Sun, 23 Jun 2024 13:55:07 -0400 Subject: [PATCH 4/5] add deriveScales docs --- src/content/guide/03-layercake-props.md | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/content/guide/03-layercake-props.md b/src/content/guide/03-layercake-props.md index 18459ce4b..87cb0922c 100644 --- a/src/content/guide/03-layercake-props.md +++ b/src/content/guide/03-layercake-props.md @@ -322,6 +322,38 @@ Reverse the default r range. By default this is `false` and the range is `[1, 25 This is ignored if you set [rRange](/guide#rrange). +### deriveScales `{Object.}` + +Sometimes you want to create scales that are based on one of your main x, y, z or r scales, such as for [grouped column](/example/ColumnGrouped) charts where you have two x-axis scales, one that uses the bandwidth of the other as its range. + +Use this prop to create a scale derived from others. It takes an object where each key is an arbitrary name for the derived scale. The value is a function that receives an object with available scales and returns the new scale. + +The key names becomes stores on the LayerCake context object. + +> Scales are copied with `.copy()` before being passed in so you can't accidentally alter them. + +```svelte + { + rScale.range([0, xScale.bandwidth()]); + return rScale; + }, + someOtherScale: ({ yScale, zScale}) => { + // Do whatever you want here + // as long as you return a D3 Scale + yScale.domain([yScale.domain()[0], zScale.domain()[1]]) + return yScale; + } + }} +> +``` + + ### extents `Object` Manually set the extents of the x, y or r scale. Setting values here will skip any dynamic extent calculation of the data for that dimension. This is similar to setting a fixed domain using `xDomain`, `yDomain`, `rDomain` or `zDomain` with the exception that this prop has the performance improvement of skipping the domain calculation. It may be removed in future versions, however. See [Issue #179](https://github.com/mhkeller/layercake/issues/179). From 99fe091974733b8d550540de0d605ddf4ff45d18 Mon Sep 17 00:00:00 2001 From: mhkeller Date: Wed, 28 Aug 2024 15:05:43 -0400 Subject: [PATCH 5/5] add beta release tag --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b9e88ccf9..128801d45 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "layercake", - "version": "8.3.4", + "version": "8.4.0-beta.1", "scripts": { "dev": "vite dev", "build": "vite build",