Skip to content

Commit

Permalink
feat(tree): add support for labels
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed May 12, 2024
1 parent dbea7d8 commit 874a147
Show file tree
Hide file tree
Showing 23 changed files with 737 additions and 56 deletions.
2 changes: 1 addition & 1 deletion packages/core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export type CompleteTheme = {
}
}
labels: {
text: Partial<React.CSSProperties>
text: TextStyle
}
markers: {
lineColor: string
Expand Down
41 changes: 41 additions & 0 deletions packages/tree/src/Label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { animated, to } from '@react-spring/web'
import { useTheme } from '@nivo/core'
import { LabelComponentProps } from './types'

export const Label = <Datum,>({ label, animatedProps }: LabelComponentProps<Datum>) => {
const theme = useTheme()

return (
<animated.g
transform={to([animatedProps.x, animatedProps.y], (x, y) => `translate(${x}, ${y})`)}
style={{
pointerEvents: 'none',
}}
>
<animated.g transform={animatedProps.rotation.to(rotation => `rotate(${rotation})`)}>
{theme.labels.text.outlineWidth > 0 && (
<text
style={{
...theme.labels.text,
fill: theme.labels.text.outlineColor,
}}
stroke={theme.labels.text.outlineColor}
strokeWidth={theme.labels.text.outlineWidth}
strokeLinejoin="round"
textAnchor={label.textAnchor}
dominantBaseline={label.baseline}
>
{label.label}
</text>
)}
<text
style={theme.labels.text}
textAnchor={label.textAnchor}
dominantBaseline={label.baseline}
>
{label.label}
</text>
</animated.g>
</animated.g>
)
}
69 changes: 69 additions & 0 deletions packages/tree/src/Labels.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { createElement } from 'react'
import { useTransition } from '@react-spring/web'
import { useMotionConfig } from '@nivo/core'
import {
CommonProps,
ComputedLabel,
ComputedNode,
LabelAnimatedProps,
LabelComponent,
LabelsPosition,
Layout,
} from './types'
import { useLabels } from './labelsHooks'

interface LabelsProps<Datum> {
nodes: ComputedNode<Datum>[]
label: Exclude<CommonProps<Datum>['label'], undefined>
layout: Layout
labelsPosition: LabelsPosition
orientLabel: boolean
labelOffset: number
labelComponent: LabelComponent<Datum>
}

const regularTransition = <Datum,>(label: ComputedLabel<Datum>): LabelAnimatedProps => ({
x: label.x,
y: label.y,
rotation: label.rotation,
})
const leaveTransition = <Datum,>(label: ComputedLabel<Datum>): LabelAnimatedProps => ({
x: label.x,
y: label.y,
rotation: label.rotation,
})

export const Labels = <Datum,>({
nodes,
label,
layout,
labelsPosition,
orientLabel,
labelOffset,
labelComponent,
}: LabelsProps<Datum>) => {
const labels = useLabels({ nodes, label, layout, labelsPosition, orientLabel, labelOffset })

const { animate, config: springConfig } = useMotionConfig()

const transition = useTransition<ComputedLabel<Datum>, LabelAnimatedProps>(labels, {
keys: label => label.id,
from: regularTransition,
enter: regularTransition,
update: regularTransition,
leave: leaveTransition,
config: springConfig,
immediate: !animate,
})

return (
<>
{transition((animatedProps, label) =>
createElement(labelComponent, {
label,
animatedProps,
})
)}
</>
)
}
22 changes: 22 additions & 0 deletions packages/tree/src/Tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { svgDefaultProps } from './defaults'
import { useTree } from './hooks'
import { Links } from './Links'
import { Nodes } from './Nodes'
import { Labels } from './Labels'
import { Mesh } from './Mesh'

type InnerTreeProps<Datum> = Omit<
Expand All @@ -31,6 +32,12 @@ const InnerTree = <Datum,>({
inactiveLinkThickness,
linkColor = svgDefaultProps.linkColor,
linkComponent = svgDefaultProps.linkComponent,
enableLabel = svgDefaultProps.enableLabel,
label = svgDefaultProps.label,
labelsPosition = svgDefaultProps.labelsPosition,
orientLabel = svgDefaultProps.orientLabel,
labelOffset = svgDefaultProps.labelOffset,
labelComponent = svgDefaultProps.labelComponent,
layers = svgDefaultProps.layers,
isInteractive = svgDefaultProps.isInteractive,
useMesh = svgDefaultProps.useMesh,
Expand Down Expand Up @@ -124,6 +131,21 @@ const InnerTree = <Datum,>({
)
}

if (layers.includes('labels') && enableLabel) {
layerById.labels = (
<Labels<Datum>
key="labels"
label={label}
nodes={nodes}
layout={layout}
labelsPosition={labelsPosition}
orientLabel={orientLabel}
labelOffset={labelOffset}
labelComponent={labelComponent}
/>
)
}

if (layers.includes('mesh') && isInteractive && useMesh) {
layerById.mesh = (
<Mesh<Datum>
Expand Down
15 changes: 14 additions & 1 deletion packages/tree/src/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CommonProps, TreeSvgProps } from './types'
import { Node } from './Node'
import { Link } from './Link'
import { Label } from './Label'

export const commonDefaultProps: Pick<
CommonProps<any>,
Expand All @@ -12,6 +13,12 @@ export const commonDefaultProps: Pick<
| 'linkCurve'
| 'linkThickness'
| 'linkColor'
| 'enableLabel'
| 'label'
| 'labelsPosition'
| 'orientLabel'
| 'labelOffset'
| 'labelComponent'
| 'isInteractive'
| 'useMesh'
| 'meshDetectionThreshold'
Expand All @@ -32,6 +39,12 @@ export const commonDefaultProps: Pick<
linkCurve: 'bump',
linkThickness: 1,
linkColor: { from: 'source.color', modifiers: [['opacity', 0.3]] },
enableLabel: true,
label: 'id',
labelsPosition: 'outward',
orientLabel: true,
labelOffset: 6,
labelComponent: Label,
isInteractive: true,
useMesh: true,
meshDetectionThreshold: Infinity,
Expand All @@ -48,7 +61,7 @@ export const commonDefaultProps: Pick<
export const svgDefaultProps: typeof commonDefaultProps &
Required<Pick<TreeSvgProps<any>, 'layers' | 'nodeComponent' | 'linkComponent'>> = {
...commonDefaultProps,
layers: ['links', 'nodes', 'mesh'],
layers: ['links', 'nodes', 'labels', 'mesh'],
nodeComponent: Node,
linkComponent: Link,
}
2 changes: 2 additions & 0 deletions packages/tree/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ const useNodes = <Datum>({
return {
path: [...node.ancestorIds!, id],
uid: node.uid!,
isRoot: node.depth === 0,
isLeaf: node.height === 0,
ancestorIds: node.ancestorIds!,
ancestorUids: node.ancestorUids!,
descendantUids: node.descendantUids!,
Expand Down
1 change: 1 addition & 0 deletions packages/tree/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './Tree'
export * from './ResponsiveTree'
export * from './hooks'
export * from './labelsHooks'
export * from './types'
export * from './defaults'
Loading

0 comments on commit 874a147

Please sign in to comment.