|
| 1 | +import React, { useState } from 'react' |
| 2 | +import { treemapBinary, treemapDice, treemapResquarify, treemapSlice, treemapSliceDice, treemapSquarify } from 'd3-hierarchy' |
| 3 | +import { VisSingleContainer, VisTreemap } from '@unovis/react' |
| 4 | +import type { TreemapDatum, TreemapTileFunction } from '@unovis/ts' |
| 5 | + |
| 6 | +export const title = 'Treemap: Tiling Methods' |
| 7 | +export const subTitle = 'D3 tiling methods with sorting' |
| 8 | + |
| 9 | +type TreemapExampleDatum = { |
| 10 | + name: string; |
| 11 | + value: number; |
| 12 | + group?: string; |
| 13 | + subgroup?: string; |
| 14 | +} |
| 15 | + |
| 16 | +const data: TreemapExampleDatum[] = [ |
| 17 | + { name: 'Enterprise', value: 22, group: 'Revenue', subgroup: 'Subscriptions' }, |
| 18 | + { name: 'Starter', value: 14, group: 'Revenue', subgroup: 'Subscriptions' }, |
| 19 | + { name: 'Free Trial', value: 5, group: 'Revenue', subgroup: 'Subscriptions' }, |
| 20 | + { name: 'Onboarding', value: 12, group: 'Revenue', subgroup: 'Services' }, |
| 21 | + { name: 'Consulting', value: 8, group: 'Revenue', subgroup: 'Services' }, |
| 22 | + { name: 'Compute', value: 16, group: 'Expenses', subgroup: 'Infrastructure' }, |
| 23 | + { name: 'Storage', value: 9, group: 'Expenses', subgroup: 'Infrastructure' }, |
| 24 | + { name: 'Networking', value: 6, group: 'Expenses', subgroup: 'Infrastructure' }, |
| 25 | + { name: 'Paid Search', value: 1, group: 'Expenses', subgroup: 'Marketing' }, |
| 26 | + { name: 'Events', value: 4, group: 'Expenses', subgroup: 'Marketing' }, |
| 27 | + { name: 'Platform', value: 18, group: 'Engineering', subgroup: 'Core' }, |
| 28 | + { name: 'Mobile', value: 10, group: 'Engineering', subgroup: 'Core' }, |
| 29 | + { name: 'ML Pipeline', value: 13, group: 'Engineering', subgroup: 'Data' }, |
| 30 | + { name: 'Analytics', value: 7, group: 'Engineering', subgroup: 'Data' }, |
| 31 | +] |
| 32 | + |
| 33 | +const TILE_OPTIONS: { key: string; label: string; tile: TreemapTileFunction<TreemapDatum<TreemapExampleDatum>> }[] = [ |
| 34 | + { key: 'squarify', label: 'Squarify (default)', tile: treemapSquarify }, |
| 35 | + { key: 'resquarify', label: 'Resquarify (stable for animation)', tile: treemapResquarify }, |
| 36 | + { key: 'binary', label: 'Binary', tile: treemapBinary }, |
| 37 | + { key: 'dice', label: 'Dice (horizontal)', tile: treemapDice }, |
| 38 | + { key: 'slice', label: 'Slice (vertical)', tile: treemapSlice }, |
| 39 | + { key: 'sliceDice', label: 'Slice-Dice (alternating)', tile: treemapSliceDice }, |
| 40 | +] |
| 41 | + |
| 42 | +export const component = (): React.ReactElement => { |
| 43 | + const [selectedKey, setSelectedKey] = useState('squarify') |
| 44 | + const selected = TILE_OPTIONS.find(o => o.key === selectedKey) |
| 45 | + |
| 46 | + return ( |
| 47 | + <> |
| 48 | + <select |
| 49 | + value={selectedKey} |
| 50 | + onChange={(e) => setSelectedKey(e.target.value)} |
| 51 | + style={{ marginBottom: 10, padding: '6px 10px' }} |
| 52 | + > |
| 53 | + {TILE_OPTIONS.map(({ key, label }) => ( |
| 54 | + <option key={key} value={key}>{label}</option> |
| 55 | + ))} |
| 56 | + </select> |
| 57 | + <VisSingleContainer height={360}> |
| 58 | + <VisTreemap |
| 59 | + data={data} |
| 60 | + value={(d: TreemapExampleDatum) => d.value} |
| 61 | + layers={[ |
| 62 | + (d: TreemapExampleDatum) => d.group, |
| 63 | + (d: TreemapExampleDatum) => d.subgroup, |
| 64 | + (d: TreemapExampleDatum) => d.name, |
| 65 | + ]} |
| 66 | + tileFunction={selected?.tile} |
| 67 | + tileSort={(a, b) => { |
| 68 | + return b.value - a.value |
| 69 | + }} |
| 70 | + tilePadding={8} |
| 71 | + tilePaddingTop={20} |
| 72 | + labelOffsetX={6} |
| 73 | + labelOffsetY={6} |
| 74 | + labelInternalNodes |
| 75 | + minTileSizeForLabel={32} |
| 76 | + /> |
| 77 | + </VisSingleContainer> |
| 78 | + </> |
| 79 | + ) |
| 80 | +} |
0 commit comments