Skip to content

Commit 4c85a54

Browse files
committed
docs(Treemap): Add non-interactive (simple) example to Treemap component docs and fix tooltip
1 parent 044a486 commit 4c85a54

3 files changed

Lines changed: 185 additions & 4 deletions

File tree

Lines changed: 182 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,185 @@
11
<script lang="ts">
2-
import { Chart, Svg } from 'layerchart';
2+
import { hierarchy, type HierarchyNode } from 'd3-hierarchy';
3+
import { scaleSequential, scaleOrdinal } from 'd3-scale';
4+
import * as chromatic from 'd3-scale-chromatic';
5+
import { hsl } from 'd3-color';
6+
7+
import { Field, RangeField, ToggleGroup, ToggleOption } from 'svelte-ux';
8+
import { format, sortFunc } from '@layerstack/utils';
9+
import { cls } from '@layerstack/tailwind';
10+
311
import Preview from '$lib/docs/Preview.svelte';
12+
13+
import {
14+
Chart,
15+
Group,
16+
Rect,
17+
RectClipPath,
18+
Svg,
19+
Text,
20+
Tooltip,
21+
Treemap,
22+
findAncestor,
23+
} from 'layerchart';
24+
import { type ComponentProps } from 'svelte';
25+
26+
let { data } = $props();
27+
28+
const root = hierarchy(data.flare)
29+
.sum((d) => d.value)
30+
.sort(sortFunc('value', 'desc'));
31+
32+
let tile: ComponentProps<typeof Treemap>['tile'] = $state('squarify');
33+
let colorBy = $state('children');
34+
35+
let paddingOuter = $state(4);
36+
let paddingInner = $state(4);
37+
let paddingTop = $state(20);
38+
let paddingBottom = $state(0);
39+
let paddingLeft = $state(0);
40+
let paddingRight = $state(0);
41+
42+
const sequentialColor = scaleSequential([4, -1], chromatic.interpolateGnBu);
43+
const ordinalColor = scaleOrdinal(
44+
chromatic.schemeSpectral[9].filter((c) => hsl(c).h < 60 || hsl(c).h > 90) // filter out hard to see yellow and green
45+
);
46+
47+
function getNodeColor(node: HierarchyNode<any>, colorBy: string) {
48+
switch (colorBy) {
49+
case 'children':
50+
return node.children ? 'var(--color-primary-500)' : 'var(--color-primary-400)';
51+
case 'depth':
52+
return sequentialColor(node.depth).toString();
53+
case 'parent':
54+
const colorParent = findAncestor(node, (n) => n.depth === 1);
55+
return colorParent
56+
? hsl(ordinalColor(colorParent.data.name))
57+
.brighter(node.depth * 0.3)
58+
.toString()
59+
: '#ddd';
60+
}
61+
return '';
62+
}
463
</script>
64+
65+
<h1>Example</h1>
66+
67+
<h1>Complex</h1>
68+
69+
<div class="grid gap-1 mb-4">
70+
<div class="grid grid-cols-[6fr_3fr] gap-1">
71+
<Field label="Tile">
72+
<ToggleGroup bind:value={tile} variant="outline" size="sm" inset class="w-full">
73+
<ToggleOption value="squarify">Squarify</ToggleOption>
74+
<ToggleOption value="resquarify">Resquarify</ToggleOption>
75+
<ToggleOption value="binary">Binary</ToggleOption>
76+
<ToggleOption value="slice">Slice</ToggleOption>
77+
<ToggleOption value="dice">Dice</ToggleOption>
78+
<ToggleOption value="sliceDice">Slice / Dice</ToggleOption>
79+
</ToggleGroup>
80+
</Field>
81+
<Field label="Color By">
82+
<ToggleGroup bind:value={colorBy} variant="outline" size="sm" inset class="w-full">
83+
<ToggleOption value="children">Children</ToggleOption>
84+
<ToggleOption value="depth">Depth</ToggleOption>
85+
<ToggleOption value="parent">Parent</ToggleOption>
86+
</ToggleGroup>
87+
</Field>
88+
</div>
89+
<div class="grid grid-cols-2 gap-2">
90+
<RangeField label="Padding Outer" bind:value={paddingOuter} />
91+
<RangeField label="Padding Inner" bind:value={paddingInner} />
92+
</div>
93+
<div class="grid grid-cols-4 gap-2">
94+
<RangeField label="Padding Top" bind:value={paddingTop} />
95+
<RangeField label="Padding Bottom" bind:value={paddingBottom} />
96+
<RangeField label="Padding Left" bind:value={paddingLeft} />
97+
<RangeField label="Padding Right" bind:value={paddingRight} />
98+
</div>
99+
</div>
100+
101+
<Preview>
102+
<div class="h-[800px]">
103+
<Chart>
104+
{#snippet children({ context })}
105+
<Svg>
106+
<Treemap
107+
hierarchy={root.copy()}
108+
{tile}
109+
{paddingOuter}
110+
{paddingInner}
111+
{paddingTop}
112+
{paddingBottom}
113+
{paddingLeft}
114+
{paddingRight}
115+
>
116+
{#snippet children({ nodes })}
117+
{#each nodes as node}
118+
<Group
119+
x={node.x0}
120+
y={node.y0}
121+
onpointermove={(e) => context.tooltip.show(e, node)}
122+
onpointerleave={context.tooltip.hide}
123+
>
124+
{@const nodeWidth = node.x1 - node.x0}
125+
{@const nodeHeight = node.y1 - node.y0}
126+
{@const nodeColor = getNodeColor(node, colorBy)}
127+
<Rect
128+
width={nodeWidth}
129+
height={nodeHeight}
130+
stroke={colorBy === 'children'
131+
? 'var(--color-primary-content)'
132+
: hsl(nodeColor).darker(1).toString()}
133+
stroke-opacity={colorBy === 'children' ? 0.2 : 1}
134+
fill={nodeColor}
135+
fillOpacity={node.children ? 0.5 : 1}
136+
rx={5}
137+
/>
138+
<RectClipPath width={nodeWidth} height={nodeHeight}>
139+
<text
140+
x={4}
141+
y={16 * 0.6 + 4}
142+
class={cls(
143+
'text-[10px] font-medium',
144+
colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
145+
)}
146+
>
147+
<tspan>{node.data.name}</tspan>
148+
{#if node.children}
149+
<tspan class="text-[8px] font-extralight">
150+
{format(node.value ?? 0, 'integer')}
151+
</tspan>
152+
{/if}
153+
</text>
154+
155+
{#if !node.children}
156+
<Text
157+
value={format(node.value ?? 0, 'integer')}
158+
class={cls(
159+
'text-[8px] font-extralight',
160+
colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
161+
)}
162+
verticalAnchor="start"
163+
x={4}
164+
y={16}
165+
/>
166+
{/if}
167+
</RectClipPath>
168+
</Group>
169+
{/each}
170+
{/snippet}
171+
</Treemap>
172+
</Svg>
173+
174+
<Tooltip.Root>
175+
{#snippet children({ data })}
176+
<Tooltip.Header>{data.data.name}</Tooltip.Header>
177+
<Tooltip.List>
178+
<Tooltip.Item label="value" value={data.value} format="integer" />
179+
</Tooltip.List>
180+
{/snippet}
181+
</Tooltip.Root>
182+
{/snippet}
183+
</Chart>
184+
</div>
185+
</Preview>

packages/layerchart/src/routes/docs/components/Treemap/+page.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import pageSource from './+page.svelte?raw';
44

55
export async function load() {
66
return {
7+
flare: await fetch('/data/examples/hierarchy/flare.json').then((r) => r.json()),
78
meta: {
89
api,
910
source,

packages/layerchart/src/routes/docs/examples/Treemap/+page.svelte

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,8 @@
9393
let paddingRight = $state(0);
9494
9595
const sequentialColor = scaleSequential([4, -1], chromatic.interpolateGnBu);
96-
// filter out hard to see yellow and green
9796
const ordinalColor = scaleOrdinal(
98-
chromatic.schemeSpectral[9].filter((c) => hsl(c).h < 60 || hsl(c).h > 90)
97+
chromatic.schemeSpectral[9].filter((c) => hsl(c).h < 60 || hsl(c).h > 90) // filter out hard to see yellow and green
9998
);
10099
// const ordinalColor = scaleOrdinal(chromatic.schemeCategory10)
101100
@@ -256,7 +255,7 @@
256255

257256
<Tooltip.Root>
258257
{#snippet children({ data })}
259-
<Tooltip.Header>{data.name}</Tooltip.Header>
258+
<Tooltip.Header>{data.data.name}</Tooltip.Header>
260259
<Tooltip.List>
261260
<Tooltip.Item label="value" value={data.value} format="integer" />
262261
</Tooltip.List>

0 commit comments

Comments
 (0)