|
| 1 | +import { edgesIterator, postOrderIterator } from './traversal' |
1 | 2 | import type { Tree } from './types'
|
2 | 3 | import { computeLeftShiftLayout } from './layouts'
|
3 |
| -import { postOrderIterator } from './traversal' |
4 | 4 |
|
5 | 5 | export interface BeautifulTreeProps {
|
6 | 6 | readonly id: string
|
7 | 7 | readonly svgProps: {
|
8 | 8 | readonly width: number
|
9 | 9 | readonly height: number
|
| 10 | + readonly sizeUnit?: '%' | 'em' | 'px' | 'rem' |
10 | 11 | }
|
11 | 12 | readonly tree: Tree
|
12 | 13 | }
|
13 | 14 |
|
14 |
| -export function BeautifulTree( |
15 |
| - props: Readonly<BeautifulTreeProps>, |
16 |
| -): JSX.Element { |
17 |
| - const treeWithLayout = computeLeftShiftLayout(props.tree) |
18 |
| - const orderedNodes = [...postOrderIterator(treeWithLayout)] |
| 15 | +export function BeautifulTree({ |
| 16 | + id, |
| 17 | + svgProps, |
| 18 | + tree, |
| 19 | +}: Readonly<BeautifulTreeProps>): JSX.Element { |
| 20 | + const { tree: treeWithLayout, maxX, maxY } = computeLeftShiftLayout(tree) |
| 21 | + const { width, height, sizeUnit = 'px' } = svgProps |
19 | 22 |
|
20 |
| - let maxX = 0 |
21 |
| - let maxY = 0 |
22 |
| - for (const node of orderedNodes) { |
23 |
| - if (node.meta.abstractPosition.x > maxX) { |
24 |
| - maxX = node.meta.abstractPosition.x |
25 |
| - } |
26 |
| - if (node.meta.abstractPosition.y > maxY) { |
27 |
| - maxY = node.meta.abstractPosition.y |
28 |
| - } |
29 |
| - } |
| 23 | + const xDivisor = maxX + 2 |
| 24 | + const yDivisor = maxY + 2 |
30 | 25 |
|
31 | 26 | return (
|
32 | 27 | <svg
|
33 | 28 | xmlns="http://www.w3.org/2000/svg"
|
34 |
| - id={props.id} |
35 |
| - viewBox={`0 0 ${props.svgProps.width} ${props.svgProps.height}`} |
| 29 | + id={id} |
| 30 | + viewBox={`0 0 ${width} ${height}`} |
36 | 31 | style={{
|
37 |
| - width: `${props.svgProps.width}px`, |
38 |
| - height: `${props.svgProps.height}px`, |
| 32 | + width: `${width}${sizeUnit}`, |
| 33 | + height: `${height}${sizeUnit}`, |
39 | 34 | }}
|
40 | 35 | className={'beautiful-tree-react'}
|
41 | 36 | >
|
42 |
| - {orderedNodes.map((node, idx) => { |
| 37 | + {/* TODO: introduce edge "styles" (straight, cornered, curved..., plus CSS styles) */} |
| 38 | + {[...edgesIterator(treeWithLayout)].map((edge, idx) => { |
| 39 | + return ( |
| 40 | + <line |
| 41 | + key={`${id}-edge-${idx}`} |
| 42 | + x1={((edge.start.x + 1) * width) / xDivisor} |
| 43 | + y1={((edge.start.y + 1) * height) / yDivisor} |
| 44 | + x2={((edge.end.x + 1) * width) / xDivisor} |
| 45 | + y2={((edge.end.y + 1) * height) / yDivisor} |
| 46 | + stroke="black" |
| 47 | + /> |
| 48 | + ) |
| 49 | + })} |
| 50 | + |
| 51 | + {[...postOrderIterator(treeWithLayout)].map((node, idx) => { |
43 | 52 | const aX = node.meta.abstractPosition.x
|
44 | 53 | const aY = node.meta.abstractPosition.y
|
45 | 54 | return (
|
46 | 55 | <circle
|
47 |
| - key={`${props.id}-node-${idx}`} |
48 |
| - cx={((aX + 1) * props.svgProps.width) / (maxX + 2)} |
49 |
| - cy={((aY + 1) * props.svgProps.height) / (maxY + 2)} |
50 |
| - stroke="blue" |
51 |
| - fill="purple" |
| 56 | + key={`${id}-node-${idx}`} |
| 57 | + className={'beautiful-tree-node'} |
| 58 | + cx={((aX + 1) * width) / xDivisor} |
| 59 | + cy={((aY + 1) * height) / yDivisor} |
| 60 | + stroke="black" |
| 61 | + fill="white" |
52 | 62 | r="5"
|
53 | 63 | />
|
54 | 64 | )
|
|
0 commit comments