Presets and d3-style migration
On this page
Documentation index · First annotation
Choose callout types and migrate d3-style input without importing a D3 runtime into the headless core.
Presets
Presets are plain-data factories for common annotations. They do not add runtime dependencies or bypass the layout engine.
import {
encircleCallout,
pointCallout,
regionCallout,
thresholdAnnotation
} from '@ponchia/annotations';
const annotations = [
pointCallout({
id: 'peak',
point: { x: 240, y: 96 },
note: { title: 'Peak' }
}),
regionCallout({
id: 'band',
box: { x: 140, y: 120, width: 180, height: 64 },
note: { title: 'Stable band' }
}),
encircleCallout({
id: 'outliers',
points: [{ x: 420, y: 84 }, { x: 448, y: 102 }, { x: 436, y: 122 }],
pointRadius: 4,
padding: 8,
note: { title: 'Outlier cluster' }
}),
thresholdAnnotation({
id: 'limit',
orientation: 'horizontal',
value: 180,
range: [40, 600],
note: { title: 'Limit' }
})
];
Available presets: pointCallout, regionCallout, pathCallout,
encircleCallout, thresholdAnnotation, and badgeAnnotation.
encircleCallout computes an enclosing circle from host-supplied points and
renders it as structured subject geometry without importing D3. badgeAnnotation
renders a compact Bronto-style __badge marker by default and keeps the note
available for labels/ARIA without drawing a note card; pass showNote: true
when the badge should also carry a visible callout note. Presets accept the same
variant, tone, motion, style, className, data, metadata, and
placement fields as raw annotations.
D3-Style Migration
For teams used to Susie Lu's d3-annotation x/y plus dx/dy authoring
model, the root package includes a DOM-free conversion helper. It does not import D3,
mutate selections, or own chart scales; it only maps familiar annotation data
into this package's Annotation model.
import {
renderAnnotationsSvg,
resolveAnnotationLayout
} from '@ponchia/annotations';
import {
annotationsFromD3Style
} from '@ponchia/annotations/d3';
const annotations = annotationsFromD3Style([
{
id: 'peak',
type: 'annotationCalloutCircle',
x: 240,
y: 96,
dx: 88,
dy: -42,
color: '#d12f6a',
note: { title: 'Peak', label: 'D3-style offset', wrap: 24 },
subject: { radius: 12, radiusPadding: 4 },
connector: { end: 'arrow' }
},
{
id: 'encircled',
type: 'annotationCalloutCircle',
dx: 72,
dy: 24,
note: { label: 'Encircled points' },
subject: {
points: [{ x: 420, y: 84 }, { x: 448, y: 102 }, { x: 436, y: 122 }],
pointRadius: 4,
padding: 8
}
},
{
id: 'limit',
type: 'annotationXYThreshold',
x: 0,
y: 180,
dx: 32,
dy: -60,
note: { label: 'Threshold' },
subject: { x1: 40, x2: 600 }
}
]);
const layout = resolveAnnotationLayout({
annotations,
bounds: { x: 0, y: 0, width: 640, height: 360 }
});
renderAnnotationsSvg(layout);
Editable layers can round-trip React edit events back into d3-style objects:
import {
applyD3StyleAnnotationEdit
} from '@ponchia/annotations/d3';
const updated = applyD3StyleAnnotationEdit(annotation, editEvent, {
x: 'period',
y: (datum) => datum.score,
accessorsInverse: {
x: 'period',
y: (value) => ({ score: value })
}
});
Existing d3-annotation generator configuration can also move over as
collection-level generator-style configs. The bridge accepts familiar
annotations, default type, accessors, accessorsInverse, ids,
editMode, notePadding, and textWrap fields, but returns plain package
annotations instead of mutating a D3 selection:
import {
applyD3StyleAnnotationCollectionEdit,
createD3StyleAnnotationBuilder,
prepareD3StyleAnnotationCollection
} from '@ponchia/annotations/d3';
const collection = {
type: 'annotationCalloutCircle',
accessors: {
x: 'period',
y: (datum) => datum.score
},
accessorsInverse: {
x: 'period',
y: (value) => ({ score: value })
},
ids: ['peak'],
editMode: true,
notePadding: 6,
annotations: [
{
data: { period: 2, score: 9 },
dx: 72,
dy: -28,
note: { label: 'Generator-style config' },
subject: { radius: 10 }
}
]
};
const prepared = prepareD3StyleAnnotationCollection(collection);
const annotations = prepared.annotations;
const updatedCollection = applyD3StyleAnnotationCollectionEdit(collection, editEvent);
const builder = createD3StyleAnnotationBuilder()
.type('annotationCalloutCircle')
.accessors({ x: 'period', y: (datum) => datum.score })
.ids(['peak'])
.editMode(true)
.notePadding(6)
.annotations(collection.annotations);
const builderAnnotations = builder.toAnnotations();
builder.applyEdit(editEvent);
The edit helper updates anchor coordinates, dx/dy note offsets, threshold
subject ranges, and shallow datum fields when string accessors or
accessorsInverse are supplied. This mirrors the practical editable workflow
from d3-annotation while keeping this package DOM-free.
Supported type aliases include annotationLabel, annotationCallout,
annotationCalloutElbow, annotationCalloutCurve,
annotationCalloutCircle, annotationCalloutRect, annotationXYThreshold,
and annotationBadge, plus kebab-case equivalents. disable: ['subject', 'connector', 'note'] maps to hidden parts, connector waypoints map to relative
connector points, note.lineType maps to horizontal/vertical note rule lines,
note.orientation can derive the rule direction, d3-style note.align values
map to start/center/end, callout types get the same default horizontal
note rule line as d3-annotation unless lineType: 'none' is set,
note.bgPadding maps to note padding when explicit padding is absent,
note.wrapSplitter customizes deterministic text wrapping, nx/ny map to
absolute manual note coordinates while dx/dy keep offset semantics,
color maps to per-annotation CSS variables for renderers and can be combined
with the richer package style object,
annotationCalloutRect accepts negative subject.width/subject.height and
normalizes them into a positive core box, annotationCalloutCircle can compute
an encircling subject from subject.points, annotationBadge preserves
subject.x/subject.y side hints such as right/top, and accessor options
can read x/y from a supplied datum. data stays the accessor datum and is
stored in annotation metadata as datum; use annotationData for rendered
annotation-layer data-* attributes. Migrated note, subject,
subject.badge, and connector class/data hooks are preserved for rendered SVG
styling and selector-based tests. defineD3StyleAnnotationType provides a
DOM-free custom-type bridge for defaults, base-type conversion, and
post-conversion transforms when migrating reusable d3-annotation-style types.
prepareD3StyleAnnotationCollection covers the common generator-level data
shape without D3 collection selection joins, and
createD3StyleAnnotationBuilder provides a chainable DOM-free builder for
teams migrating from annotation().annotations(...).type(...).accessors(...).
This is a
migration/ergonomics bridge, not a D3 component replacement.
docs/d3-annotation-parity.json tracks the supported surface inspired by Susie
Lu's d3-svg-annotation@2.5.1: type aliases, familiar
note/subject/connector fields, custom d3-style conversion helpers, datum
accessors, inverse edit mapping, and explicit non-goals. npm run test:d3-parity proves those helpers still work and that the root package does
not export the D3 selection adapter, D3 type base, or D3 runtime custom type
component.
Use docs/migration-guide.md for a practical d3-annotation mapping table,
editable round-trip notes, and runtime differences.