Skip to content

Commit

Permalink
feat(circle-packing): restore border support
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc authored and wyze committed Apr 26, 2021
1 parent 5649103 commit 2911b61
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 14 deletions.
55 changes: 55 additions & 0 deletions packages/circle-packing/src/CircleHtml.tsx
@@ -0,0 +1,55 @@
import React from 'react'
import { animated, to, SpringValue, Interpolation } from 'react-spring'
import { CircleProps } from './types'
import { useNodeMouseHandlers } from './hooks'

export const interpolatePosition = (
positionValue: SpringValue<number>,
radiusValue: Interpolation<number>
) => to([positionValue, radiusValue], (position, radius) => position - radius)

export const interpolateSize = (radiusValue: Interpolation<number>) =>
to([radiusValue], radius => radius * 2)

export const interpolateBorderWidth = (borderWidth: number, radiusValue: Interpolation<number>) =>
to([radiusValue], radius => Math.min(borderWidth, radius))

export const CircleHtml = <RawDatum,>({
node,
style,
onMouseEnter,
onMouseMove,
onMouseLeave,
onClick,
}: CircleProps<RawDatum>) => {
const size = interpolateSize(style.radius)

const handlers = useNodeMouseHandlers<RawDatum>(node, {
onMouseEnter,
onMouseMove,
onMouseLeave,
onClick,
})

return (
<animated.div
style={{
position: 'absolute',
top: interpolatePosition(style.y, style.radius),
left: interpolatePosition(style.x, style.radius),
height: size,
width: size,
borderRadius: style.radius,
backgroundColor: style.color,
borderWidth: interpolateBorderWidth(style.borderWidth, style.radius),
borderStyle: 'solid',
borderColor: style.borderColor,
boxSizing: 'border-box',
}}
onMouseEnter={handlers.onMouseEnter}
onMouseMove={handlers.onMouseMove}
onMouseLeave={handlers.onMouseLeave}
onClick={handlers.onClick}
/>
)
}
4 changes: 4 additions & 0 deletions packages/circle-packing/src/CirclePacking.tsx
Expand Up @@ -38,6 +38,8 @@ const InnerCirclePacking = <RawDatum,>({
>,
colorBy = defaultProps.colorBy,
childColor = defaultProps.childColor as InheritedColorConfig<ComputedDatum<RawDatum>>,
borderWidth = defaultProps.borderWidth,
borderColor = defaultProps.borderColor as InheritedColorConfig<ComputedDatum<RawDatum>>,
circleComponent = CircleSvg,
enableLabels = defaultProps.enableLabels,
label = defaultProps.label,
Expand Down Expand Up @@ -87,6 +89,8 @@ const InnerCirclePacking = <RawDatum,>({
<Circles<RawDatum>
key="circles"
nodes={zoomedNodes}
borderWidth={borderWidth}
borderColor={borderColor}
isInteractive={isInteractive}
onMouseEnter={onMouseEnter}
onMouseMove={onMouseMove}
Expand Down
22 changes: 14 additions & 8 deletions packages/circle-packing/src/CirclePackingCanvas.tsx
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useRef, createElement } from 'react'
import { useDimensions, useTheme, Container } from '@nivo/core'
import { InheritedColorConfig, OrdinalColorScaleConfig } from '@nivo/colors'
import { InheritedColorConfig, OrdinalColorScaleConfig, useInheritedColor } from '@nivo/colors'
import { useTooltip } from '@nivo/tooltip'
import { CirclePackingCanvasProps, ComputedDatum } from './types'
import { defaultProps } from './props'
Expand Down Expand Up @@ -34,6 +34,8 @@ const InnerCirclePackingCanvas = <RawDatum,>({
>,
colorBy = defaultProps.colorBy,
childColor = defaultProps.childColor as InheritedColorConfig<ComputedDatum<RawDatum>>,
borderWidth = defaultProps.borderWidth,
borderColor = defaultProps.borderColor as InheritedColorConfig<ComputedDatum<RawDatum>>,
enableLabels = defaultProps.enableLabels,
label = defaultProps.label,
labelsFilter,
Expand Down Expand Up @@ -80,6 +82,8 @@ const InnerCirclePackingCanvas = <RawDatum,>({
textColor: labelTextColor,
})

const getBorderColor = useInheritedColor<ComputedDatum<RawDatum>>(borderColor, theme)

useEffect(() => {
if (!canvasEl.current) return

Expand All @@ -97,19 +101,19 @@ const InnerCirclePackingCanvas = <RawDatum,>({
ctx.translate(margin.left, margin.top)

zoomedNodes.forEach(node => {
//if (borderWidth > 0) {
// this.ctx.strokeStyle = getBorderColor(node)
// this.ctx.lineWidth = borderWidth
//}
if (borderWidth > 0) {
ctx.strokeStyle = getBorderColor(node)
ctx.lineWidth = borderWidth
}

ctx.beginPath()
ctx.arc(node.x, node.y, node.radius, 0, 2 * Math.PI)
ctx.fillStyle = node.color
ctx.fill()

//if (borderWidth > 0) {
// this.ctx.stroke()
//}
if (borderWidth > 0) {
ctx.stroke()
}
})

if (enableLabels) {
Expand All @@ -135,6 +139,8 @@ const InnerCirclePackingCanvas = <RawDatum,>({
zoomedNodes,
enableLabels,
labels,
borderWidth,
getBorderColor,
])

const getNodeFromMouseEvent = useMouseCircleDetection<RawDatum>({
Expand Down
4 changes: 4 additions & 0 deletions packages/circle-packing/src/CirclePackingHtml.tsx
Expand Up @@ -32,6 +32,8 @@ export const InnerCirclePackingHtml = <RawDatum,>({
>,
colorBy = defaultProps.colorBy,
childColor = defaultProps.childColor as InheritedColorConfig<ComputedDatum<RawDatum>>,
borderWidth = defaultProps.borderWidth,
borderColor = defaultProps.borderColor as InheritedColorConfig<ComputedDatum<RawDatum>>,
circleComponent = CircleHtml,
enableLabels = defaultProps.enableLabels,
label = defaultProps.label,
Expand Down Expand Up @@ -81,6 +83,8 @@ export const InnerCirclePackingHtml = <RawDatum,>({
<Circles<RawDatum>
key="circles"
nodes={zoomedNodes}
borderWidth={borderWidth}
borderColor={borderColor}
isInteractive={isInteractive}
onMouseEnter={onMouseEnter}
onMouseMove={onMouseMove}
Expand Down
2 changes: 2 additions & 0 deletions packages/circle-packing/src/CircleSvg.tsx
Expand Up @@ -25,6 +25,8 @@ export const CircleSvg = <RawDatum,>({
cy={style.y}
r={style.radius}
fill={style.color}
stroke={style.borderColor}
strokeWidth={style.borderWidth}
opacity={style.opacity}
onMouseEnter={handlers.onMouseEnter}
onMouseMove={handlers.onMouseMove}
Expand Down
23 changes: 20 additions & 3 deletions packages/circle-packing/src/Circles.tsx
@@ -1,6 +1,7 @@
import React, { createElement, useMemo, MouseEvent } from 'react'
import { useTransition, to, SpringValue } from 'react-spring'
import { useMotionConfig } from '@nivo/core'
import { useMotionConfig, useTheme } from '@nivo/core'
import { useInheritedColor } from '@nivo/colors'
import { useTooltip } from '@nivo/tooltip'
import { ComputedDatum, CircleComponent, MouseHandlers, CirclePackingCommonProps } from './types'

Expand All @@ -14,37 +15,46 @@ export const interpolateRadius = (radiusValue: SpringValue<number>) =>

type CirclesProps<RawDatum> = {
nodes: ComputedDatum<RawDatum>[]
borderWidth: CirclePackingCommonProps<RawDatum>['borderWidth']
borderColor: CirclePackingCommonProps<RawDatum>['borderColor']
component: CircleComponent<RawDatum>
isInteractive: CirclePackingCommonProps<RawDatum>['isInteractive']
tooltip: CirclePackingCommonProps<RawDatum>['tooltip']
} & MouseHandlers<RawDatum>

const getTransitionPhases = <RawDatum,>() => ({
const getTransitionPhases = <RawDatum,>(
getBorderColor: (node: ComputedDatum<RawDatum>) => string
) => ({
enter: (node: ComputedDatum<RawDatum>) => ({
x: node.x,
y: node.y,
radius: 0,
color: node.color,
borderColor: getBorderColor(node),
opacity: 0,
}),
update: (node: ComputedDatum<RawDatum>) => ({
x: node.x,
y: node.y,
radius: node.radius,
color: node.color,
borderColor: getBorderColor(node),
opacity: 1,
}),
leave: (node: ComputedDatum<RawDatum>) => ({
x: node.x,
y: node.y,
radius: 0,
color: node.color,
borderColor: getBorderColor(node),
opacity: 0,
}),
})

export const Circles = <RawDatum,>({
nodes,
borderWidth,
borderColor,
component,
isInteractive,
onMouseEnter,
Expand Down Expand Up @@ -92,7 +102,12 @@ export const Circles = <RawDatum,>({

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

const transitionPhases = useMemo(() => getTransitionPhases<RawDatum>(), [])
const theme = useTheme()
const getBorderColor = useInheritedColor<ComputedDatum<RawDatum>>(borderColor, theme)

const transitionPhases = useMemo(() => getTransitionPhases<RawDatum>(getBorderColor), [
getBorderColor,
])

const transition = useTransition<
ComputedDatum<RawDatum>,
Expand All @@ -101,6 +116,7 @@ export const Circles = <RawDatum,>({
y: number
radius: number
color: string
borderColor: string
opacity: number
}
>(nodes, {
Expand All @@ -123,6 +139,7 @@ export const Circles = <RawDatum,>({
style: {
...transitionProps,
radius: interpolateRadius(transitionProps.radius),
borderWidth,
},
onMouseEnter: handleMouseEnter,
onMouseMove: handleMouseMove,
Expand Down
5 changes: 5 additions & 0 deletions packages/circle-packing/src/props.ts
Expand Up @@ -14,6 +14,11 @@ export const defaultProps = {
from: 'color',
modifiers: [['darker', 0.3]],
},
borderWidth: 0,
borderColor: {
from: 'color',
modifiers: [['darker', 0.3]],
},
enableLabels: true,
label: 'id',
labelTextColor: {
Expand Down
2 changes: 2 additions & 0 deletions packages/circle-packing/src/types.ts
Expand Up @@ -103,6 +103,8 @@ export type CircleProps<RawDatum> = {
radius: Interpolation<number>
color: SpringValue<string>
opacity: SpringValue<number>
borderWidth: number
borderColor: SpringValue<string>
}
} & MouseHandlers<RawDatum>

Expand Down
5 changes: 4 additions & 1 deletion website/src/data/components/circle-packing/props.js
Expand Up @@ -280,7 +280,10 @@ const props = [
key: 'labelsFilter',
help: 'Filter labels using custom conditions.',
description: `
Can be used to only show labels at a certain depth for example:
Please note that at this stage, nodes are already excluded
according to \`labelsSkipRadius\`.
This can be used to only show labels at a certain depth for example:
\`\`\`
<CirclePacking
Expand Down
5 changes: 3 additions & 2 deletions website/src/pages/circle-packing/index.js
Expand Up @@ -20,7 +20,7 @@ const initialProperties = {
colors: { scheme: 'nivo' },
colorBy: 'depth',
childColor: 'noinherit',
padding: 2,
padding: 4,
leavesOnly: false,
enableLabels: true,
label: 'id',
Expand All @@ -30,9 +30,10 @@ const initialProperties = {
from: 'color',
modifiers: [['darker', 2]],
},
borderWidth: 2,
borderWidth: 1,
borderColor: {
from: 'color',
modifiers: [['darker', 0.5]],
},
defs: [
patternLinesDef('lines', {
Expand Down

0 comments on commit 2911b61

Please sign in to comment.