Skip to content

Commit

Permalink
feat(website): add a dedicated control for chart annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Dec 31, 2021
1 parent 07a1e33 commit 9060197
Show file tree
Hide file tree
Showing 7 changed files with 352 additions and 259 deletions.
6 changes: 0 additions & 6 deletions packages/annotations/src/types.ts
Expand Up @@ -63,12 +63,6 @@ export interface BaseAnnotationSpec<Datum> {
noteY: RelativeOrAbsolutePosition
noteWidth?: number
noteTextOffset?: number
// circle/dot
// size?: number
// // rect
// width?: number
// // rect
// height?: number
}

// This annotation can be used to draw a circle
Expand Down
197 changes: 197 additions & 0 deletions website/src/components/controls/AnnotationsControl.tsx
@@ -0,0 +1,197 @@
import React, { memo, useCallback, useMemo } from 'react'
import omit from 'lodash/omit'
import { AnnotationMatcher } from '@nivo/annotations'
import { ChartProperty, Flavor } from '../../types'
import { AnnotationsControlConfig, ArrayControlConfig } from './types'
import { ArrayControl } from './ArrayControl'

const fixAnnotation = (annotation: AnnotationMatcher<any>): AnnotationMatcher<any> => {
let adjusted: AnnotationMatcher<any> = annotation

if (annotation.type === 'rect') {
if (annotation.borderRadius === undefined) {
adjusted = { ...annotation, borderRadius: 2 }
}
} else {
if ((annotation as any).borderRadius !== undefined) {
adjusted = omit(annotation as any, 'borderRadius') as AnnotationMatcher<any>
}
}

return adjusted
}

interface AnnotationsControlProps {
id: string
property: ChartProperty
flavors: Flavor[]
currentFlavor: Flavor
config: AnnotationsControlConfig
value: AnnotationMatcher<any>[]
onChange: (annotations: AnnotationMatcher<any>[]) => void
context?: any
}

export const AnnotationsControl = memo(
({
id,
property,
flavors,
currentFlavor,
value,
config: { createDefaults },
onChange,
}: AnnotationsControlProps) => {
const arrayProperty: Omit<ChartProperty, 'control'> & {
control: ArrayControlConfig<AnnotationMatcher<any>>
} = useMemo(
() => ({
key: property.key,
group: property.group,
help: property.help,
type: property.type,
required: property.required,
flavors: property.flavors,
defaultValue: property.defaultValue,
control: {
type: 'array',
shouldCreate: true,
addLabel: 'add annotation',
shouldRemove: true,
getItemTitle: (index, annotation) =>
`annotation[${index}] '${annotation.note}' (${annotation.type})`,
defaults: createDefaults,
props: [
{
key: 'type',
flavors: property.flavors,
help: `Annotation type.`,
type: `'dot' | 'circle' | 'rect'`,
required: true,
control: {
type: 'choices',
choices: [
{ value: 'dot', label: 'dot' },
{ value: 'circle', label: 'circle' },
{ value: 'rect', label: 'rect' },
],
},
},
{
key: 'match',
flavors: property.flavors,
help: 'Annotation matcher.',
required: true,
type: 'object',
control: {
type: 'object',
isOpenedByDefault: true,
props: [
{
key: 'id',
required: false,
flavors: property.flavors,
help: 'Match elements having the provided ID.',
type: 'string | number',
control: {
type: 'text',
},
},
],
},
},
{
key: 'borderRadius',
flavors,
help: `Rect border radius.`,
type: 'number',
required: false,
when: (settings: any) => settings.type === 'rect',
control: {
type: 'range',
min: 0,
max: 12,
},
},
{
key: 'note',
flavors,
help: `Annotation note.`,
type: 'text',
required: true,
control: { type: 'text' },
},
{
key: 'noteX',
flavors,
help: `Annotation note x position.`,
type: 'number',
required: true,
control: {
type: 'range',
min: -300,
max: 300,
step: 5,
},
},
{
key: 'noteY',
flavors,
help: `Annotation note y position.`,
type: 'number',
required: true,
control: {
type: 'range',
min: -300,
max: 300,
step: 5,
},
},
{
key: 'noteTextOffset',
flavors,
help: `Annotation note text offset.`,
type: 'number',
required: false,
control: {
type: 'range',
min: -64,
max: 64,
},
},
{
key: 'offset',
flavors,
help: `Offset from annotated element.`,
type: 'number',
required: false,
control: {
type: 'range',
min: 0,
max: 32,
},
},
],
},
}),
[property, createDefaults]
)

const handleChange = useCallback(
(annotations: AnnotationMatcher<any>[]) => onChange(annotations.map(fixAnnotation)),
[onChange]
)

return (
<ArrayControl<AnnotationMatcher<any>>
id={id}
property={arrayProperty as ChartProperty<any>}
value={value}
flavors={flavors}
currentFlavor={currentFlavor}
config={arrayProperty.control}
onChange={handleChange}
/>
)
}
)

0 comments on commit 9060197

Please sign in to comment.