semiotic 3.1.2 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +173 -213
- package/LICENSE +197 -10
- package/README.md +12 -11
- package/ai/dist/componentRegistry.js +6 -0
- package/ai/dist/mcp-server.js +115 -5
- package/ai/examples.md +184 -0
- package/ai/schema.json +4140 -888
- package/ai/system-prompt.md +36 -1
- package/dist/components/ChartContainer.d.ts +2 -0
- package/dist/components/DataSummaryContext.d.ts +12 -0
- package/dist/components/ThemeProvider.d.ts +5 -3
- package/dist/components/Tooltip/FlippingTooltip.d.ts +34 -0
- package/dist/components/charts/geo/ChoroplethMap.d.ts +1 -1
- package/dist/components/charts/index.d.ts +12 -1
- package/dist/components/charts/ordinal/BarChart.d.ts +4 -1
- package/dist/components/charts/ordinal/BoxPlot.d.ts +4 -1
- package/dist/components/charts/ordinal/DonutChart.d.ts +1 -1
- package/dist/components/charts/ordinal/DotPlot.d.ts +4 -1
- package/dist/components/charts/ordinal/FunnelChart.d.ts +57 -0
- package/dist/components/charts/ordinal/GroupedBarChart.d.ts +4 -1
- package/dist/components/charts/ordinal/Histogram.d.ts +17 -2
- package/dist/components/charts/ordinal/LikertChart.d.ts +94 -0
- package/dist/components/charts/ordinal/PieChart.d.ts +1 -1
- package/dist/components/charts/ordinal/RidgelinePlot.d.ts +12 -7
- package/dist/components/charts/ordinal/StackedBarChart.d.ts +4 -1
- package/dist/components/charts/ordinal/SwarmPlot.d.ts +15 -1
- package/dist/components/charts/ordinal/SwimlaneChart.d.ts +65 -0
- package/dist/components/charts/ordinal/ViolinPlot.d.ts +17 -2
- package/dist/components/charts/realtime/RealtimeHistogram.d.ts +20 -0
- package/dist/components/charts/shared/annotationResolvers.d.ts +28 -0
- package/dist/components/charts/shared/colorManipulation.d.ts +15 -0
- package/dist/components/charts/shared/formatUtils.d.ts +28 -0
- package/dist/components/charts/shared/hatchPattern.d.ts +35 -0
- package/dist/components/charts/shared/hooks.d.ts +39 -3
- package/dist/components/charts/shared/legendUtils.d.ts +2 -1
- package/dist/components/charts/shared/selectionUtils.d.ts +16 -1
- package/dist/components/charts/shared/statisticalOverlays.d.ts +49 -5
- package/dist/components/charts/shared/statsTooltip.d.ts +11 -0
- package/dist/components/charts/shared/types.d.ts +26 -2
- package/dist/components/charts/shared/useChartSetup.d.ts +12 -2
- package/dist/components/charts/shared/useLikertAggregation.d.ts +51 -0
- package/dist/components/charts/shared/useOrdinalBrush.d.ts +28 -0
- package/dist/components/charts/shared/useOrdinalStreaming.d.ts +54 -0
- package/dist/components/charts/shared/useStreamingLegend.d.ts +2 -2
- package/dist/components/charts/shared/validateProps.d.ts +2 -2
- package/dist/components/charts/shared/validationMap.d.ts +12 -0
- package/dist/components/charts/xy/AreaChart.d.ts +11 -0
- package/dist/components/charts/xy/Heatmap.d.ts +1 -1
- package/dist/components/charts/xy/MinimapChart.d.ts +1 -1
- package/dist/components/charts/xy/MultiAxisLineChart.d.ts +71 -0
- package/dist/components/charts/xy/StackedAreaChart.d.ts +11 -0
- package/dist/components/realtime/types.d.ts +6 -0
- package/dist/components/semiotic-ai.d.ts +4 -0
- package/dist/components/semiotic-ordinal.d.ts +5 -0
- package/dist/components/semiotic-themes.d.ts +80 -0
- package/dist/components/semiotic-utils.d.ts +30 -0
- package/dist/components/semiotic-xy.d.ts +1 -0
- package/dist/components/semiotic.d.ts +11 -5
- package/dist/components/store/LinkedCrosshairStore.d.ts +11 -0
- package/dist/components/store/ThemeStore.d.ts +22 -2
- package/dist/components/store/useSelection.d.ts +1 -0
- package/dist/components/stream/AccessibleDataTable.d.ts +28 -6
- package/dist/components/stream/FocusRing.d.ts +33 -0
- package/dist/components/stream/OrdinalBrushOverlay.d.ts +43 -0
- package/dist/components/stream/OrdinalPipelineStore.d.ts +16 -0
- package/dist/components/stream/OrdinalSVGOverlay.d.ts +2 -1
- package/dist/components/stream/PipelineStore.d.ts +7 -47
- package/dist/components/stream/SVGOverlay.d.ts +9 -3
- package/dist/components/stream/SceneGraph.d.ts +6 -1
- package/dist/components/stream/XYBrushOverlay.d.ts +47 -0
- package/dist/components/stream/accessorUtils.d.ts +14 -0
- package/dist/components/stream/geoTypes.d.ts +5 -1
- package/dist/components/stream/keyboardNav.d.ts +85 -9
- package/dist/components/stream/layouts/hierarchySceneBuilders.d.ts +35 -0
- package/dist/components/stream/layouts/hierarchyUtils.d.ts +25 -0
- package/dist/components/stream/networkTypes.d.ts +7 -1
- package/dist/components/stream/ordinalSceneBuilders/barFunnelScene.d.ts +27 -0
- package/dist/components/stream/ordinalSceneBuilders/funnelScene.d.ts +26 -0
- package/dist/components/stream/ordinalSceneBuilders/swimlaneScene.d.ts +12 -0
- package/dist/components/stream/ordinalTypes.d.ts +30 -4
- package/dist/components/stream/pipelineDecay.d.ts +20 -0
- package/dist/components/stream/pipelinePulse.d.ts +24 -0
- package/dist/components/stream/pipelineTransitions.d.ts +59 -0
- package/dist/components/stream/renderers/barFunnelCanvasRenderer.d.ts +12 -0
- package/dist/components/stream/renderers/pointCanvasRenderer.d.ts +2 -1
- package/dist/components/stream/renderers/trapezoidCanvasRenderer.d.ts +15 -0
- package/dist/components/stream/sceneUtils.d.ts +10 -0
- package/dist/components/stream/types.d.ts +29 -4
- package/dist/components/stream/useMediaPreferences.d.ts +11 -0
- package/dist/components/stream/xySceneBuilders/areaScene.d.ts +13 -0
- package/dist/components/stream/xySceneBuilders/barScene.d.ts +18 -0
- package/dist/components/stream/xySceneBuilders/boundsScene.d.ts +8 -0
- package/dist/components/stream/xySceneBuilders/candlestickScene.d.ts +10 -0
- package/dist/components/stream/xySceneBuilders/emitPointNodes.d.ts +13 -0
- package/dist/components/stream/xySceneBuilders/heatmapScene.d.ts +3 -0
- package/dist/components/stream/xySceneBuilders/lineScene.d.ts +12 -0
- package/dist/components/stream/xySceneBuilders/pointScene.d.ts +12 -0
- package/dist/components/stream/xySceneBuilders/swarmScene.d.ts +10 -0
- package/dist/components/stream/xySceneBuilders/types.d.ts +93 -0
- package/dist/components/stream/xySceneBuilders/waterfallScene.d.ts +12 -0
- package/dist/geo.min.js +1 -1
- package/dist/geo.module.min.js +1 -1
- package/dist/network.min.js +1 -1
- package/dist/network.module.min.js +1 -1
- package/dist/ordinal.min.js +1 -1
- package/dist/ordinal.module.min.js +1 -1
- package/dist/realtime.min.js +1 -1
- package/dist/realtime.module.min.js +1 -1
- package/dist/semiotic-ai-statisticalOverlays-C2PPlmXv.js +1 -0
- package/dist/semiotic-ai.d.ts +4 -0
- package/dist/semiotic-ai.min.js +1 -1
- package/dist/semiotic-ai.module.min.js +1 -1
- package/dist/semiotic-ordinal.d.ts +5 -0
- package/dist/semiotic-statisticalOverlays-DGX_WWc5.js +1 -0
- package/dist/semiotic-themes.d.ts +80 -0
- package/dist/semiotic-themes.min.js +1 -0
- package/dist/semiotic-themes.module.min.js +1 -0
- package/dist/semiotic-utils.d.ts +30 -0
- package/dist/semiotic-utils.min.js +1 -0
- package/dist/semiotic-utils.module.min.js +1 -0
- package/dist/semiotic-xy.d.ts +1 -0
- package/dist/semiotic.d.ts +11 -5
- package/dist/semiotic.min.js +1 -1
- package/dist/semiotic.module.min.js +1 -1
- package/dist/server.min.js +1 -1
- package/dist/server.module.min.js +1 -1
- package/dist/xy-statisticalOverlays-C2PPlmXv.js +1 -0
- package/dist/xy.min.js +1 -1
- package/dist/xy.module.min.js +1 -1
- package/package.json +38 -12
- package/dist/semiotic-ai-statisticalOverlays-C1f7TYyD.js +0 -1
- package/dist/semiotic-statisticalOverlays-C1f7TYyD.js +0 -1
- package/dist/xy-statisticalOverlays-C1f7TYyD.js +0 -1
package/CLAUDE.md
CHANGED
|
@@ -2,49 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
## Quick Start
|
|
4
4
|
- Install: `npm install semiotic`
|
|
5
|
-
- Import: `semiotic`, `semiotic/xy`, `semiotic/ordinal`, `semiotic/network`, `semiotic/geo`, `semiotic/realtime`, `semiotic/ai`, `semiotic/data`, `semiotic/server`
|
|
5
|
+
- Import: `semiotic`, `semiotic/xy`, `semiotic/ordinal`, `semiotic/network`, `semiotic/geo`, `semiotic/realtime`, `semiotic/ai`, `semiotic/data`, `semiotic/server`, `semiotic/themes`, `semiotic/utils`
|
|
6
6
|
- CLI: `npx semiotic-ai [--schema|--compact|--examples|--doctor]`
|
|
7
7
|
- MCP: `npx semiotic-mcp`
|
|
8
|
-
- Every HOC has a built-in error boundary
|
|
8
|
+
- Every HOC has a built-in error boundary and dev-mode validation warnings
|
|
9
9
|
|
|
10
10
|
## Architecture
|
|
11
11
|
- **HOC Charts**: Simple props, sensible defaults. **Stream Frames**: Full control.
|
|
12
|
-
- **Always use HOC charts**
|
|
12
|
+
- **Always use HOC charts** unless you need control they don't expose. Stream Frames (`StreamNetworkFrame`, `StreamXYFrame`, `StreamOrdinalFrame`, `StreamGeoFrame`) are low-level — they pass `RealtimeNode`/`RealtimeEdge` wrappers in callbacks, not your data.
|
|
13
13
|
- Every HOC accepts `frameProps` to pass through. TypeScript `strict: true`.
|
|
14
14
|
|
|
15
15
|
## Common Props (all HOCs)
|
|
16
|
-
`title`, `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `enableHover` (true), `tooltip` (boolean | `(datum) => ReactNode` |
|
|
16
|
+
`title`, `description` (overrides aria-label), `summary` (sr-only note), `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `color` (uniform fill — overrides theme/colorScheme), `enableHover` (true), `tooltip` (boolean | `(datum) => ReactNode` | `{ fields?, title?, format?, style? }`), `showLegend`, `showGrid` (false), `frameProps`, `onObservation`, `onClick`, `chartId`, `loading` (false), `emptyContent`, `legendInteraction` ("none"|"highlight"|"isolate"), `legendPosition` ("right"|"left"|"top"|"bottom"), `emphasis` ("primary"|"secondary"), `annotations` (array), `accessibleTable` (true)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
`tooltip` accepts: `true` (default tooltip), `false` (disabled), a **function** `(datum: Record<string, any>) => ReactNode`, or a config `{ fields?: string[], title?: accessor, format?: fn, style?: CSSProperties }`. The function form receives your raw data object directly.
|
|
18
|
+
`onClick` receives `(datum, { x, y })` — the original datum and pixel coordinates. Works on lines, bars, areas, pie slices, nodes, and geo features.
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
`onObservation` receives a `ChartObservation` with `type` and event-specific fields:
|
|
23
|
-
- **hover**: `{ type: "hover", datum: <your data>, x, y, timestamp, chartType, chartId }`
|
|
24
|
-
- **hover-end**: `{ type: "hover-end", timestamp, chartType, chartId }`
|
|
25
|
-
- **click**: `{ type: "click", datum: <your data>, x, y, timestamp, chartType, chartId }`
|
|
26
|
-
- **brush**: `{ type: "brush", extent: { x: [min, max], y: [min, max] }, timestamp, chartType }`
|
|
27
|
-
- **selection**: `{ type: "selection", selection: { name, fields }, timestamp, chartType }`
|
|
28
|
-
|
|
29
|
-
The `datum` field contains your original data object (not a wrapper).
|
|
20
|
+
`onObservation` receives `{ type: "hover"|"hover-end"|"click"|"brush"|"selection", datum?, x?, y?, timestamp, chartType, chartId }`. The `datum` is your original data object.
|
|
30
21
|
|
|
31
22
|
## XY Charts (`semiotic/xy`)
|
|
32
23
|
|
|
33
|
-
**LineChart** — `data`, `xAccessor` ("x"), `yAccessor` ("y"), `lineBy`, `lineDataAccessor` ("coordinates"), `colorBy`, `colorScheme`, `curve`, `lineWidth` (2), `showPoints`, `pointRadius` (3), `fillArea`, `areaOpacity` (0.3), `anomaly
|
|
34
|
-
|
|
24
|
+
**LineChart** — `data`, `xAccessor` ("x"), `yAccessor` ("y"), `lineBy`, `lineDataAccessor` ("coordinates"), `colorBy`, `colorScheme`, `curve`, `lineWidth` (2), `showPoints`, `pointRadius` (3), `fillArea`, `areaOpacity` (0.3), `anomaly`, `forecast`, `directLabel`, `gapStrategy` ("break"|"interpolate"|"zero"), `xScaleType`/`yScaleType` ("linear"|"log")
|
|
35
25
|
**AreaChart** — LineChart props + `areaBy`, `y0Accessor` (band/ribbon), `gradientFill` (boolean|{topOpacity,bottomOpacity}), `areaOpacity` (0.7), `showLine` (true)
|
|
36
|
-
|
|
37
|
-
**StackedAreaChart** — flat array data + `areaBy` (required, groups into stacked areas), `colorBy`, `normalize` (false). Do NOT use `lineBy` or `lineDataAccessor` — those are LineChart props.
|
|
38
|
-
|
|
26
|
+
**StackedAreaChart** — flat array + `areaBy` (required), `colorBy`, `normalize`. Do NOT use `lineBy` or `lineDataAccessor`.
|
|
39
27
|
**Scatterplot** — `data`, `xAccessor`, `yAccessor`, `colorBy`, `sizeBy`, `sizeRange`, `pointRadius` (5), `pointOpacity` (0.8), `marginalGraphics`
|
|
40
|
-
|
|
41
28
|
**BubbleChart** — Scatterplot + `sizeBy` (required), `sizeRange` ([5,40]), `bubbleOpacity` (0.6)
|
|
42
|
-
|
|
43
|
-
**
|
|
44
|
-
|
|
45
|
-
**
|
|
46
|
-
|
|
47
|
-
**Heatmap** — `data`, `xAccessor`, `yAccessor`, `valueAccessor`, `colorScheme` ("blues"|"reds"|"greens"|"viridis" or custom), `showValues`, `cellBorderColor`. Accessors can be string field names (including string/categorical fields) or functions.
|
|
29
|
+
**ConnectedScatterplot** — `data`, `xAccessor`, `yAccessor`, `orderAccessor` (sequencing field), `pointRadius` (4)
|
|
30
|
+
**QuadrantChart** — Scatterplot + `quadrants` (required: `{ topRight, topLeft, bottomRight, bottomLeft }` each `{ label, color, opacity? }`), `xCenter`, `yCenter`, `centerlineStyle`, `showQuadrantLabels` (true). Supports push API.
|
|
31
|
+
**MultiAxisLineChart** — Dual Y-axis. `data`, `xAccessor` ("x"), `series` (required: array of `{ yAccessor, label?, color?, format?, extent? }`), `colorScheme`, `curve` ("monotoneX"), `lineWidth` (2). Data unitized to [0,1] internally; left axis=series[0], right axis=series[1] in original units. For push API, provide `series[].extent` for stable unitization. Falls back to standard multi-line if not exactly 2 series.
|
|
32
|
+
**Heatmap** — `data`, `xAccessor`, `yAccessor`, `valueAccessor`, `colorScheme` ("blues"|"reds"|"greens"|"viridis"), `showValues`, `cellBorderColor`. Supports string/categorical axes.
|
|
48
33
|
|
|
49
34
|
## Ordinal Charts (`semiotic/ordinal`)
|
|
50
35
|
|
|
@@ -53,284 +38,259 @@ The `datum` field contains your original data object (not a wrapper).
|
|
|
53
38
|
**GroupedBarChart** — + `groupBy` (required), `barPadding` (60)
|
|
54
39
|
**SwarmPlot** — `data`, `categoryAccessor`, `valueAccessor`, `colorBy`, `sizeBy`, `pointRadius`, `pointOpacity`
|
|
55
40
|
**BoxPlot** — + `showOutliers`, `outlierRadius`
|
|
56
|
-
**Histogram** — + `bins` (25), `relative`. Always horizontal. `categoryAccessor`
|
|
41
|
+
**Histogram** — + `bins` (25), `relative`. Always horizontal. `categoryAccessor` optional (defaults to `"category"`).
|
|
57
42
|
**ViolinPlot** — + `bins`, `curve`, `showIQR`
|
|
43
|
+
**RidgelinePlot** — + `bins`, `amplitude` (1.5, unitless multiplier of lane width)
|
|
58
44
|
**DotPlot** — + `sort` (true), `dotRadius`, `showGrid` default true
|
|
59
|
-
**PieChart** — `data`, `categoryAccessor`, `valueAccessor`, `colorBy`, `startAngle
|
|
60
|
-
**DonutChart** — PieChart + `innerRadius` (60), `centerContent` (ReactNode
|
|
45
|
+
**PieChart** — `data`, `categoryAccessor`, `valueAccessor`, `colorBy`, `startAngle`
|
|
46
|
+
**DonutChart** — PieChart + `innerRadius` (60), `centerContent` (ReactNode)
|
|
47
|
+
**FunnelChart** — `data`, `stepAccessor` ("step"), `valueAccessor` ("value"), `categoryAccessor` (optional), `colorBy`, `connectorOpacity` (0.3), `orientation` ("horizontal"|"vertical"). Horizontal: centered bars with trapezoid connectors. Vertical: bars with diagonal hatch for dropoff. Multi-category: `categoryAccessor="channel"` mirrors (horizontal) or groups (vertical).
|
|
48
|
+
**SwimlaneChart** — `data`, `categoryAccessor` ("category"), `subcategoryAccessor` (required), `valueAccessor` ("value"), `colorBy` (defaults to subcategoryAccessor), `colorScheme`, `orientation` ("horizontal"|"vertical"), `barPadding` (40). Renders categorical lanes with items stacked sequentially — unlike StackedBarChart, the same subcategory can appear multiple times in the same lane. Items stack left-to-right (horizontal) or bottom-to-top (vertical) in data order. Wraps StreamOrdinalFrame with `chartType="swimlane"`. Supports push API for streaming.
|
|
49
|
+
|
|
50
|
+
**LikertChart** — `data`, `categoryAccessor` ("question"), `valueAccessor` ("score", raw mode) or `levelAccessor`+`countAccessor` ("count", pre-aggregated mode), `levels` (required, ordered negative→positive), `orientation` ("horizontal"|"vertical"), `colorScheme`. Horizontal (default): diverging bar chart centered at 0% — negative levels extend left, positive right, neutral (odd count) split 50/50 across centerline. Vertical: stacked 100% bar chart. Supports any scale size (3-point to 7-point+). Raw mode aggregates integer scores automatically (1-based: score 1 → levels[0]). The `levels` array order defines polarity — first half negative, second half positive, center neutral if odd. Supports push API for streaming — accumulates raw data internally and re-aggregates percentages on each push.
|
|
51
|
+
|
|
52
|
+
All ordinal HOCs support `colorBy` and `colorScheme`. `categoryFormat` (`(label: string, index?: number) => string`) customizes individual tick labels (truncation, formatting). `showCategoryTicks` (default true) hides per-tick labels when false — margins auto-adjust. For distribution charts with `colorBy`, set `showCategoryTicks={false}` since the legend identifies categories.
|
|
61
53
|
|
|
62
54
|
## Network Charts (`semiotic/network`)
|
|
63
55
|
|
|
64
|
-
**ForceDirectedGraph** — `nodes`, `edges`, `nodeIDAccessor`, `sourceAccessor`, `targetAccessor`, `colorBy`, `colorScheme`, `nodeSize` (number|string|fn), `nodeSizeRange`, `edgeWidth`, `edgeColor`, `edgeOpacity`, `iterations` (300), `forceStrength` (0.1), `showLabels`, `nodeLabel`, `
|
|
65
|
-
**SankeyDiagram** — `edges`, `nodes`, `valueAccessor`, `
|
|
56
|
+
**ForceDirectedGraph** — `nodes`, `edges`, `nodeIDAccessor`, `sourceAccessor`, `targetAccessor`, `colorBy`, `colorScheme`, `nodeSize` (number|string|fn), `nodeSizeRange`, `edgeWidth`, `edgeColor`, `edgeOpacity`, `iterations` (300), `forceStrength` (0.1), `showLabels`, `nodeLabel`, `legendInteraction`
|
|
57
|
+
**SankeyDiagram** — `edges`, `nodes`, `valueAccessor`, `nodeIdAccessor` ("id"), `sourceAccessor` ("source"), `targetAccessor` ("target"), `colorBy`, `edgeColorBy` ("source"|"target"|"gradient"|fn), `orientation`, `nodeAlign`, `nodeWidth`, `nodePaddingRatio`, `nodeLabel`, `showLabels`, `edgeOpacity`
|
|
66
58
|
**ChordDiagram** — `edges`, `nodes`, `valueAccessor`, `edgeColorBy`, `padAngle`, `groupWidth`, `showLabels`
|
|
67
59
|
**TreeDiagram** — `data` (root), `layout`, `orientation`, `childrenAccessor`, `colorBy`, `colorByDepth`, `edgeStyle`
|
|
68
60
|
**Treemap** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`, `showLabels`, `labelMode`
|
|
69
61
|
**CirclePack** — `data` (root), `childrenAccessor`, `valueAccessor`, `colorBy`, `colorByDepth`, `circleOpacity`
|
|
70
|
-
**OrbitDiagram** — animated radial/orbital hierarchy.
|
|
62
|
+
**OrbitDiagram** — animated radial/orbital hierarchy. `data` (root), `childrenAccessor`, `nodeIdAccessor`, `orbitMode` ("flat"|"solar"|"atomic"|number[]), `speed` (0.25), `revolution`, `eccentricity`, `orbitSize`, `nodeRadius`, `showRings`, `showLabels`, `animated` (true), `colorBy`, `colorByDepth`. For static radial trees, use `TreeDiagram layout="radial"`.
|
|
71
63
|
|
|
72
64
|
## Geo Charts (`semiotic/geo`)
|
|
73
65
|
|
|
74
|
-
|
|
66
|
+
Import from `semiotic/geo` — NOT `semiotic` — to avoid pulling d3-geo into non-geo bundles.
|
|
75
67
|
|
|
76
|
-
**ChoroplethMap** — `areas` (GeoJSON Feature[] or
|
|
68
|
+
**ChoroplethMap** — `areas` (GeoJSON Feature[] or "world-110m"), `valueAccessor`, `colorScheme`, `areaOpacity` (1), `projection` ("equalEarth"), `graticule`, `tooltip`, `showLegend`
|
|
77
69
|
**ProportionalSymbolMap** — `points`, `xAccessor` ("lon"), `yAccessor` ("lat"), `sizeBy`, `sizeRange` ([3,30]), `colorBy`, `areas` (optional background), `projection`
|
|
78
|
-
**FlowMap** — `flows
|
|
79
|
-
**DistanceCartogram** — `points`, `center` (id
|
|
80
|
-
|
|
81
|
-
All geo HOCs support: `selection`, `linkedHover`, `onObservation`, `showLegend`, `legendInteraction`, `tooltip`, `loading`, `emptyContent`, `frameProps`, `fitPadding` (0–1 fraction, insets auto-fit projection from edges), `zoomable` (defaults true with tileURL, false otherwise), `zoomExtent`, `onZoom`, `dragRotate`, `graticule`, `tileURL`, `tileAttribution`, `tileCacheSize`
|
|
82
|
-
|
|
83
|
-
**Zoom/Pan**: All geo charts accept `zoomable` (boolean), `zoomExtent` ([minZoom, maxZoom], default [1, 8]), and `onZoom` (callback with `{ projection, zoom }`). Re-renders projection directly on every zoom tick (no CSS transform). Imperative API: `ref.current.getZoom()`, `ref.current.resetZoom()`.
|
|
84
|
-
|
|
85
|
-
**Geo Particles**: `FlowMap` and `StreamGeoFrame` support `showParticles` (boolean) and `particleStyle` to animate dots flowing along line paths. Uses `GeoParticlePool` — an object-pool polyline particle system. Particle `color` accepts: `"source"` (inherit line stroke), a CSS string, or `(datum) => string` for per-line color.
|
|
70
|
+
**FlowMap** — `flows`, `nodes`, `xAccessor`, `yAccessor`, `nodeIdAccessor`, `valueAccessor`, `edgeColorBy`, `edgeOpacity` (0.6), `edgeWidthRange` ([1,8]), `lineType` ("geo"|"line"), `showParticles`, `particleStyle`
|
|
71
|
+
**DistanceCartogram** — `points`, `center` (id), `costAccessor`, `strength` (0-1), `lineMode`, `showRings` (true|false|number[]), `ringStyle`, `showNorth`, `costLabel`, `transition`, `pointRadius`
|
|
86
72
|
|
|
87
|
-
|
|
73
|
+
All geo HOCs: `fitPadding` (0–1), `zoomable` (defaults true with tileURL), `zoomExtent` ([1,8]), `onZoom`, `dragRotate` (true for orthographic), `graticule`, `tileURL`, `tileAttribution`, `tileCacheSize`, `selection`, `linkedHover`, `onObservation`
|
|
88
74
|
|
|
89
|
-
**
|
|
90
|
-
|
|
91
|
-
**
|
|
92
|
-
|
|
93
|
-
**Reference geography**: `resolveReferenceGeography("world-110m")` returns GeoJSON features from Natural Earth data (world-atlas). Supported: `"world-110m"`, `"world-50m"`, `"land-110m"`, `"land-50m"`. All geo HOCs accept `areas` as `GeoJSON.Feature[]` or a reference string.
|
|
94
|
-
|
|
95
|
-
**mergeData(features, data, { featureKey, dataKey })** — join external data into GeoJSON features by key field. Supports nested paths (e.g., `"properties.iso_a3"`). World-atlas uses ISO 3166-1 numeric codes as the `id` field. Also available from `semiotic/data` as a general join-by-key utility.
|
|
75
|
+
**Tiles**: `tileURL` accepts string template (`{z}/{x}/{y}`) or function. Mercator only. OSM tiles are dev-only — use commercial provider with env var key in production.
|
|
76
|
+
**Zoom**: Imperative: `ref.current.getZoom()`, `ref.current.resetZoom()`.
|
|
77
|
+
**Reference geography**: `resolveReferenceGeography("world-110m"|"world-50m"|"land-110m"|"land-50m")` returns GeoJSON features.
|
|
78
|
+
**mergeData(features, data, { featureKey, dataKey })** — join data into GeoJSON by key. World-atlas uses ISO numeric codes as `id`.
|
|
96
79
|
|
|
97
80
|
```jsx
|
|
98
|
-
// World choropleth with reference geography + data joining
|
|
99
81
|
import { ChoroplethMap, resolveReferenceGeography, mergeData } from "semiotic/geo"
|
|
100
82
|
const world = await resolveReferenceGeography("world-110m")
|
|
101
83
|
const areas = mergeData(world, gdpData, { featureKey: "id", dataKey: "id" })
|
|
102
84
|
<ChoroplethMap areas={areas} valueAccessor="gdpPerCapita" colorScheme="viridis"
|
|
103
85
|
projection="equalEarth" zoomable tooltip />
|
|
104
|
-
|
|
105
|
-
// Distance cartogram (ORBIS-style) with concentric rings overlay
|
|
106
|
-
import { DistanceCartogram } from "semiotic/geo"
|
|
107
|
-
<DistanceCartogram
|
|
108
|
-
points={cities} center="rome" costAccessor="travelDays"
|
|
109
|
-
strength={0.8} lines={routes} showLegend zoomable
|
|
110
|
-
showRings costLabel="days" showNorth
|
|
111
|
-
ringStyle={{ stroke: "#999", strokeWidth: 0.5 }}
|
|
112
|
-
/>
|
|
113
|
-
|
|
114
|
-
// Tile map basemap with proportional symbols
|
|
115
|
-
<ProportionalSymbolMap
|
|
116
|
-
points={earthquakes} xAccessor="lon" yAccessor="lat"
|
|
117
|
-
sizeBy="magnitude" sizeRange={[2, 20]}
|
|
118
|
-
projection="mercator" zoomable
|
|
119
|
-
tileURL="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
120
|
-
tileAttribution="© OpenStreetMap contributors"
|
|
121
|
-
/>
|
|
122
|
-
|
|
123
|
-
// Streaming geo points with zoom
|
|
124
|
-
const geoRef = useRef()
|
|
125
|
-
geoRef.current.push({ lon: -122.4, lat: 37.8, value: 42 })
|
|
126
|
-
<StreamGeoFrame ref={geoRef} projection="mercator" xAccessor="lon" yAccessor="lat"
|
|
127
|
-
runtimeMode="streaming" decay={{ type: "linear", minOpacity: 0.1 }}
|
|
128
|
-
zoomable zoomExtent={[1, 12]} onZoom={({ zoom }) => console.log(zoom)} />
|
|
129
86
|
```
|
|
130
87
|
|
|
88
|
+
**StreamGeoFrame** — low-level frame. Push API: `ref.current.push(datum)`, `.pushMany()`, `.clear()`. Props: `projection`, `areas`, `points`, `lines`, `xAccessor`, `yAccessor`, `areaStyle`, `pointStyle`, `lineStyle`, `graticule`, `zoomable`, `decay`, `pulse`, `transition`.
|
|
89
|
+
|
|
131
90
|
## Realtime Charts (`semiotic/realtime`)
|
|
132
91
|
|
|
133
92
|
Push API: `chartRef.current.push({ time, value })`
|
|
134
93
|
|
|
135
|
-
**IMPORTANT**: All pushed data must include a time field (default: `"time"`).
|
|
94
|
+
**IMPORTANT**: All pushed data must include a time field (default: `"time"`). Set `timeAccessor` if your field differs. Without valid time field, charts render blank.
|
|
136
95
|
|
|
137
|
-
|
|
96
|
+
**RealtimeLineChart** — `timeAccessor` ("time"), `valueAccessor` ("value"), `windowSize` (200), `windowMode`, `stroke`, `strokeWidth`
|
|
97
|
+
**RealtimeHistogram** — `binSize` (required), `timeAccessor`, `valueAccessor`, `categoryAccessor`, `colors`, `brush` (boolean|"x"|object, defaults to `{ dimension: "x", snap: "bin" }` when `true`), `onBrush`, `linkedBrush` (cross-chart coordination)
|
|
98
|
+
**RealtimeSwarmChart** — `timeAccessor`, `valueAccessor`, `categoryAccessor`, `radius`, `opacity`
|
|
99
|
+
**RealtimeWaterfallChart** — `timeAccessor`, `valueAccessor`, `positiveColor`, `negativeColor`
|
|
100
|
+
**RealtimeHeatmap** — `timeAccessor`, `valueAccessor`, `heatmapXBins`, `heatmapYBins`, `aggregation`
|
|
101
|
+
**Streaming Sankey** — `StreamNetworkFrame` with `chartType="sankey"`, `showParticles`, `particleStyle`. Push individual edges: `ref.current.push({ source, target, value })`.
|
|
138
102
|
|
|
139
|
-
|
|
140
|
-
**RealtimeHistogram** — **`binSize`** (required), **`timeAccessor`** ("time"), **`valueAccessor`** ("value"), `categoryAccessor`, `colors`. Time field is required even though this shows a distribution — it's used for windowing.
|
|
141
|
-
**RealtimeSwarmChart** — **`timeAccessor`** ("time"), **`valueAccessor`** ("value"), `categoryAccessor`, `radius`, `opacity`
|
|
142
|
-
**RealtimeWaterfallChart** — **`timeAccessor`** ("time"), **`valueAccessor`** ("value"), `positiveColor`, `negativeColor`
|
|
143
|
-
**RealtimeHeatmap** — **`timeAccessor`** ("time"), **`valueAccessor`** ("value"), `heatmapXBins`, `heatmapYBins`, `aggregation`. Both accessors must match your data fields or the chart renders blank.
|
|
144
|
-
**Streaming Sankey** — `StreamNetworkFrame` with `chartType="sankey"`, `showParticles` (boolean), `particleStyle` (`{ radius, opacity, speedMultiplier, maxPerEdge, colorBy }`), `tensionConfig`, `thresholds`. Push **individual edges**: `ref.current.push({ source: "A", target: "B", value: 42 })`. Use `ref.current.pushMany([...edges])` for batches.
|
|
103
|
+
Encoding: `decay`, `pulse`, `transition`, `staleness` — compose freely on all streaming charts.
|
|
145
104
|
|
|
146
|
-
Realtime
|
|
105
|
+
All Realtime* charts accept `data` props for static mode (no push API needed). RealtimeHistogram brush supports bin-snapping (`snap: "bin"`) and streaming tracking — the brush shrinks as selected bins scroll off and auto-clears when fully evicted. Bin snapping uses actual computed bin boundaries (data-driven), not a uniform grid — works with irregular bin widths. `snapDuring: true` enables continuous snap feedback during drag (not just on release).
|
|
147
106
|
|
|
148
|
-
###
|
|
107
|
+
### Push API on HOC charts
|
|
108
|
+
Most HOC charts support push via `forwardRef`. **Omit** `data`/`nodes`/`edges` — do NOT pass `data={[]}`.
|
|
149
109
|
```jsx
|
|
150
|
-
|
|
151
|
-
ref.current.push({
|
|
152
|
-
ref.current.
|
|
153
|
-
ref.current.
|
|
110
|
+
const ref = useRef()
|
|
111
|
+
ref.current.push({ x: 1, y: 2 }) // single
|
|
112
|
+
ref.current.pushMany([...points]) // batch
|
|
113
|
+
ref.current.clear() // reset
|
|
114
|
+
ref.current.getData() // read
|
|
115
|
+
<Scatterplot ref={ref} xAccessor="x" yAccessor="y" />
|
|
154
116
|
```
|
|
117
|
+
Supported: all XY, ordinal, network (Force, Sankey, Chord), geo point charts. **Not supported**: hierarchy charts (Tree, Treemap, CirclePack, Orbit), ChoroplethMap, FlowMap, ScatterplotMatrix.
|
|
155
118
|
|
|
156
|
-
|
|
157
|
-
|
|
119
|
+
## Stream Frame Callbacks (advanced)
|
|
120
|
+
Frame callbacks (`nodeStyle`, `edgeStyle`, `nodeSize` as fn) receive `RealtimeNode`/`RealtimeEdge` wrappers. Access original data via `.data`:
|
|
158
121
|
```jsx
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
chartRef.current.pushMany([...points]) // batch
|
|
162
|
-
chartRef.current.clear() // reset
|
|
163
|
-
chartRef.current.getData() // read current data
|
|
164
|
-
<Scatterplot ref={chartRef} xAccessor="x" yAccessor="y" />
|
|
122
|
+
// WRONG: nodeSize={(d) => d.weight} — d.weight is undefined
|
|
123
|
+
// RIGHT: nodeSize={(d) => d.data?.weight} — or use string: nodeSize="weight"
|
|
165
124
|
```
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Supported: all XY charts (LineChart, AreaChart, Scatterplot, etc.), all ordinal charts (BarChart, Histogram, etc.), network charts (ForceDirectedGraph, SankeyDiagram, ChordDiagram), and geo point charts (ProportionalSymbolMap, DistanceCartogram). **Not supported**: hierarchy charts (TreeDiagram, Treemap, CirclePack, OrbitDiagram) — their root-object data shape is incompatible with flat push. ChoroplethMap (area-based, not point-based), FlowMap (line-based), and ScatterplotMatrix also do not support push.
|
|
125
|
+
Same applies to `frameProps` style functions on HOCs. `customHoverBehavior`/`customClickBehavior` receive `{ type, data, x, y } | null`. `tooltipContent` receives `{ type, data }`.
|
|
169
126
|
|
|
170
|
-
##
|
|
171
|
-
|
|
127
|
+
## Hover Indicator
|
|
128
|
+
The hover dot automatically matches the hovered element's color (line stroke, point fill, etc.). Override via `frameProps`:
|
|
172
129
|
```jsx
|
|
173
|
-
|
|
174
|
-
// RIGHT: nodeSize={(d) => d.data?.weight} — d.data is your original node object
|
|
175
|
-
// RIGHT: nodeSize="weight" — string accessor handles this automatically
|
|
176
|
-
// WRONG: nodeStyle={(d) => ({ fill: d.datum.color })} — .datum does not exist
|
|
177
|
-
// RIGHT: nodeStyle={(d) => ({ fill: d.data?.color })} — use .data
|
|
130
|
+
<LineChart frameProps={{ hoverAnnotation: { pointColor: "#ff0000" } }} />
|
|
178
131
|
```
|
|
179
|
-
|
|
180
|
-
`tooltipContent` receives `{ type: "node"|"edge", data: <your raw object> }`.
|
|
132
|
+
Fallback chain: `pointColor` → element color → `--semiotic-primary` CSS var → `#007bff`.
|
|
181
133
|
|
|
182
134
|
## Coordinated Views
|
|
183
135
|
|
|
184
|
-
**LinkedCharts** —
|
|
185
|
-
**CategoryColorProvider** —
|
|
136
|
+
**LinkedCharts** — `selections` (resolution: "union"|"intersect"|"crossfilter"), `showLegend`, `legendPosition`, `legendInteraction`, `legendSelectionName`, `legendField`
|
|
137
|
+
**CategoryColorProvider** — `colors` (map) or `categories` + `colorScheme`
|
|
186
138
|
Chart props: `selection`, `linkedHover`, `linkedBrush`. Hooks: `useSelection`, `useLinkedHover`, `useBrushSelection`, `useFilteredData`
|
|
187
|
-
**ScatterplotMatrix** — `data`, `fields`, `colorBy`, `cellSize`, `hoverMode`, `brushMode`
|
|
188
|
-
|
|
189
|
-
## ChartContainer
|
|
190
|
-
|
|
191
|
-
**ChartContainer** — wrapper with title, subtitle, status indicator, toolbar actions. Props: `title`, `subtitle`, `height` (default **400** — set this to match your chart's height or you'll get extra whitespace), `width` (default "100%"), `status` ("live"|"stale"|"error"), `loading`, `error`, `errorBoundary`, `actions` (`{ export, fullscreen, copyConfig }`), `controls`, `style`, `className`
|
|
192
|
-
|
|
193
|
-
When using `ChartContainer` with a chart that has `size={[w, h]}`, always set `height={h}` on the container to avoid a mismatch.
|
|
194
139
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
**
|
|
198
|
-
**
|
|
140
|
+
**Linked crosshair** (coordinate-based hover sync): `linkedHover={{ name: "sync", mode: "x-position", xField: "time" }}` broadcasts the hovered X data value. Other charts with the same `linkedHover` name render a synced vertical crosshair at that X position. Each chart shows its own Y values independently. Use for dashboards with multiple time-series at different scales.
|
|
141
|
+
**ScatterplotMatrix** — `data`, `fields`, `colorBy`, `cellSize`, `hoverMode`, `brushMode`
|
|
142
|
+
**ChartContainer** — `title`, `subtitle`, `height` (400), `width` ("100%"), `status`, `loading`, `error`, `errorBoundary`, `actions` ({ export, fullscreen, copyConfig, dataSummary }), `controls`
|
|
143
|
+
**ChartGrid** — `columns` (number|"auto"), `minCellWidth` (300), `gap` (16). `emphasis="primary"` spans two columns.
|
|
144
|
+
**ContextLayout** — `context` (ReactNode), `position`, `contextSize` (250)
|
|
199
145
|
|
|
200
146
|
## Key Patterns
|
|
201
147
|
|
|
202
148
|
```jsx
|
|
203
|
-
//
|
|
204
|
-
<ForceDirectedGraph
|
|
205
|
-
nodes={[{ id: "A", group: "eng", weight: 10 }, { id: "B", group: "design", weight: 5 }]}
|
|
206
|
-
edges={[{ source: "A", target: "B" }]}
|
|
207
|
-
colorBy="group"
|
|
208
|
-
nodeSize="weight" // string accessor → reads node.weight, scales to nodeSizeRange
|
|
209
|
-
nodeSizeRange={[5, 25]}
|
|
210
|
-
showLabels
|
|
211
|
-
showLegend
|
|
212
|
-
tooltip={(d) => <div>{d.data.id}: {d.data.weight}</div>}
|
|
213
|
-
frameProps={{
|
|
214
|
-
customClickBehavior: (d) => { if (d?.type === "node") console.log(d.data) },
|
|
215
|
-
background: "#f5f5f5",
|
|
216
|
-
}}
|
|
217
|
-
/>
|
|
218
|
-
|
|
219
|
-
// Cross-highlighting dashboard with column spanning
|
|
220
|
-
// emphasis="primary" makes a chart span 2 columns in ChartGrid
|
|
149
|
+
// Cross-highlighting dashboard
|
|
221
150
|
<CategoryColorProvider categories={["North", "South", "East"]}>
|
|
222
151
|
<LinkedCharts>
|
|
223
152
|
<ChartGrid columns={2}>
|
|
224
153
|
<LineChart data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} emphasis="primary" responsiveWidth />
|
|
225
154
|
<BarChart data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} responsiveWidth />
|
|
226
|
-
<Scatterplot data={d} colorBy="region" linkedHover={{ name: "hl", fields: ["region"] }} selection={{ name: "hl" }} responsiveWidth />
|
|
227
155
|
</ChartGrid>
|
|
228
156
|
</LinkedCharts>
|
|
229
157
|
</CategoryColorProvider>
|
|
230
158
|
|
|
231
|
-
// Forecast + anomaly
|
|
159
|
+
// Forecast + anomaly
|
|
232
160
|
<LineChart data={ts} xAccessor="time" yAccessor="value"
|
|
233
161
|
forecast={{ trainEnd: 60, steps: 15, confidence: 0.95 }}
|
|
234
162
|
anomaly={{ threshold: 2 }} />
|
|
235
163
|
|
|
236
|
-
//
|
|
164
|
+
// Pre-computed forecast bounds
|
|
237
165
|
<LineChart data={ml} xAccessor="time" yAccessor="value"
|
|
238
166
|
forecast={{ isTraining: "isTraining", isForecast: "isForecast", isAnomaly: "isAnomaly", upperBounds: "upper", lowerBounds: "lower" }} />
|
|
239
167
|
|
|
240
|
-
//
|
|
241
|
-
<StackedAreaChart data={flatData} xAccessor="month" yAccessor="value"
|
|
242
|
-
areaBy="category" colorBy="category" />
|
|
243
|
-
|
|
244
|
-
// Percentile band (p5–p95) with main line (p50) — MUST layer two charts
|
|
245
|
-
// AreaChart with y0Accessor renders the band; showLine only draws the TOP edge (p95), not p50
|
|
246
|
-
// To show a separate main line, add a LineChart on top:
|
|
168
|
+
// Percentile band — layer AreaChart + LineChart
|
|
247
169
|
<>
|
|
248
170
|
<AreaChart data={d} xAccessor="x" yAccessor="p95" y0Accessor="p5"
|
|
249
171
|
showLine={false} areaOpacity={0.3} gradientFill />
|
|
250
172
|
<LineChart data={d} xAccessor="x" yAccessor="p50" lineWidth={2} />
|
|
251
173
|
</>
|
|
252
|
-
// Simple gradient area (no band):
|
|
253
|
-
<AreaChart data={d} xAccessor="x" yAccessor="y" gradientFill />
|
|
254
174
|
|
|
255
|
-
//
|
|
256
|
-
const ref = useRef()
|
|
257
|
-
ref.current.push({ time: Date.now(), value: 42 })
|
|
258
|
-
<RealtimeLineChart ref={ref} timeAccessor="time" valueAccessor="value" />
|
|
259
|
-
|
|
260
|
-
// Realtime histogram — time field required even for distribution charts
|
|
261
|
-
const histRef = useRef()
|
|
262
|
-
histRef.current.push({ time: Date.now(), value: Math.abs(delta) })
|
|
263
|
-
<RealtimeHistogram ref={histRef} timeAccessor="time" valueAccessor="value" binSize={100} />
|
|
264
|
-
|
|
265
|
-
// Streaming sankey with particles — push individual edges, NOT full snapshots
|
|
175
|
+
// Streaming sankey with particles
|
|
266
176
|
const sankeyRef = useRef()
|
|
267
|
-
sankeyRef.current.push({ source: "Web", target: "API", value: 1 })
|
|
268
|
-
sankeyRef
|
|
269
|
-
{
|
|
270
|
-
{
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
ref={sankeyRef}
|
|
274
|
-
chartType="sankey"
|
|
275
|
-
showParticles={true}
|
|
276
|
-
particleStyle={{ radius: 2, colorBy: "source", speedMultiplier: 1.5 }}
|
|
277
|
-
width={600} height={400}
|
|
278
|
-
/>
|
|
279
|
-
|
|
280
|
-
// SSR — renderToStaticSVG takes frame type string, not component name
|
|
177
|
+
sankeyRef.current.push({ source: "Web", target: "API", value: 1 })
|
|
178
|
+
<StreamNetworkFrame ref={sankeyRef} chartType="sankey"
|
|
179
|
+
showParticles particleStyle={{ radius: 2, colorBy: "source" }}
|
|
180
|
+
width={600} height={400} />
|
|
181
|
+
|
|
182
|
+
// SSR
|
|
281
183
|
import { renderOrdinalToStaticSVG } from "semiotic/server"
|
|
282
|
-
const svg = renderOrdinalToStaticSVG({
|
|
283
|
-
data, categoryAccessor: "category", valueAccessor: "value", width: 600, height: 400
|
|
284
|
-
})
|
|
184
|
+
const svg = renderOrdinalToStaticSVG({ data, categoryAccessor: "cat", valueAccessor: "val", width: 600, height: 400 })
|
|
285
185
|
```
|
|
286
186
|
|
|
287
187
|
## Annotations
|
|
288
|
-
|
|
188
|
+
|
|
189
|
+
All HOCs accept `annotations` (array). Coordinates use your data field names. Network/orbit use `nodeId`.
|
|
190
|
+
|
|
191
|
+
**Positioning**: `widget` (React content at data coords — v3 replacement for v2 `htmlAnnotationRules`; props: `content`, `dx`, `dy`, `width`, `height`, `anchor`), `label` (callout with connector), `callout` (circle + label), `text` (plain text), `bracket`
|
|
192
|
+
**Reference lines**: `y-threshold` (`value`, `label`, `color`, `labelPosition`: "left"|"center"|"right", `strokeDasharray`), `x-threshold` (`labelPosition`: "top"|"center"|"bottom"), `band` (`y0`, `y1`, `label`, `fill`)
|
|
193
|
+
**Ordinal**: `category-highlight` (`category`, `color`, `opacity`, `label`) — highlights a category column/row. Works on BarChart, StackedBarChart, etc. `y-threshold` also works on vertical ordinal charts.
|
|
194
|
+
**Enclosures**: `enclose` (circle around `coordinates`), `rect-enclose`, `highlight` (`filter` fn or `field`+`value`)
|
|
195
|
+
**Statistical** (XY): `trend` (`method`: linear/polynomial/loess), `envelope`, `anomaly-band`, `forecast`
|
|
196
|
+
**Streaming anchors**: `"fixed"` (default), `"latest"` (tracks newest datum), `"sticky"` (freezes when evicted)
|
|
197
|
+
|
|
198
|
+
Custom rendering: `frameProps.svgAnnotationRules = (annotation, index, context) => ReactNode | null`. Context has `scales`, `width`, `height`, `data`. Colors inherit from theme (`--semiotic-primary`, `--semiotic-text-secondary`).
|
|
199
|
+
|
|
289
200
|
```jsx
|
|
290
|
-
|
|
291
|
-
|
|
201
|
+
<LineChart data={data} xAccessor="time" yAccessor="latency"
|
|
202
|
+
annotations={[
|
|
203
|
+
{ type: "y-threshold", value: 200, label: "SLA limit", color: "#e45050" },
|
|
204
|
+
{ type: "widget", time: 42, latency: 850, dy: -30, content: <span>Incident</span> },
|
|
205
|
+
]} />
|
|
292
206
|
```
|
|
293
207
|
|
|
208
|
+
## Theming
|
|
209
|
+
|
|
210
|
+
Charts are themeable via CSS custom properties on any ancestor element. Key vars: `--semiotic-bg`, `--semiotic-text`, `--semiotic-text-secondary`, `--semiotic-border`, `--semiotic-grid`, `--semiotic-primary`, `--semiotic-focus`, `--semiotic-font-family`, `--semiotic-border-radius`, `--semiotic-tooltip-bg`/`text`/`radius`/`font-size`/`shadow`, `--semiotic-selection-color`/`opacity`, `--semiotic-diverging`.
|
|
211
|
+
|
|
212
|
+
```jsx
|
|
213
|
+
import { ThemeProvider } from "semiotic"
|
|
214
|
+
<ThemeProvider theme="tufte"> {/* Named preset */}
|
|
215
|
+
<ThemeProvider theme={{ colors: { primary: "#ff6b6b", categorical: [...] } }}> {/* Custom */}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Color resolution priority** (when `colorBy` is set): explicit `colorScheme` prop > ThemeProvider `colors.categorical` > `"category10"` fallback. This means ThemeProvider categorical colors automatically apply to all charts — no need to pass `colorScheme` on every component.
|
|
219
|
+
|
|
220
|
+
Presets: `light`, `dark`, `high-contrast`, `pastels`, `pastels-dark`, `bi-tool`, `bi-tool-dark`, `italian`, `italian-dark`, `tufte`, `tufte-dark`, `journalist`, `journalist-dark`, `playful`, `playful-dark`, `carbon`, `carbon-dark`.
|
|
221
|
+
|
|
222
|
+
Serialization (`semiotic/themes`): `themeToCSS(theme, selector)`, `themeToTokens(theme)`, `resolveThemePreset(name)`.
|
|
223
|
+
Color-blind palette: `import { COLOR_BLIND_SAFE_CATEGORICAL } from "semiotic"` (8-color Wong 2011).
|
|
224
|
+
IBM Carbon palette: `import { CARBON_CATEGORICAL_14, CARBON_ALERT } from "semiotic"` (14-color categorical + 4 alert colors).
|
|
225
|
+
|
|
226
|
+
**`semiotic/utils`** (~137KB, ~10% of full bundle) — Lightweight entry point for utilities without any chart components:
|
|
227
|
+
- **Theme**: `ThemeProvider`, `useTheme`, `LIGHT_THEME`, `DARK_THEME`, `HIGH_CONTRAST_THEME`, `COLOR_BLIND_SAFE_CATEGORICAL`, `CARBON_CATEGORICAL_14`, `CARBON_ALERT`, `themeToCSS`, `themeToTokens`, `resolveThemePreset`, `THEME_PRESETS`
|
|
228
|
+
- **Format**: `adaptiveTimeTicks`, `smartTickFormat`
|
|
229
|
+
- **Color**: `darkenColor`, `lightenColor`
|
|
230
|
+
- **Patterns**: `createHatchPattern`
|
|
231
|
+
- **Validation**: `validateProps`, `diagnoseConfig`
|
|
232
|
+
- **Serialization**: `toConfig`, `fromConfig`, `toURL`, `fromURL`, `copyConfig`, `configToJSX`, `serializeSelections`, `deserializeSelections`, `exportChart`
|
|
233
|
+
- **Vega-Lite**: `fromVegaLite` — convert Vega-Lite specs to Semiotic configs
|
|
234
|
+
- **Data structures**: `RingBuffer`, `IncrementalExtent`
|
|
235
|
+
- **Tooltip**: `normalizeTooltip`
|
|
236
|
+
|
|
237
|
+
Key: `ThemeProvider` sets CSS vars on a wrapper div (no React context). Canvas charts read vars via `getComputedStyle`. `exportChart` inlines computed styles.
|
|
238
|
+
|
|
239
|
+
**Dark/light mode merge rules:** String preset (e.g. `"dark"`) → full replacement with that preset's theme. Object with `mode` (e.g. `{ mode: "dark", colors: { categorical: [...] } }`) → merges onto the matching base theme (`DARK_THEME` or `LIGHT_THEME`), so background/text/grid adapt while your overrides are preserved. Object without `mode` → shallow-merges onto the current theme (partial override). ThemeProvider is reactive — changing the `theme` prop re-applies immediately.
|
|
240
|
+
|
|
241
|
+
**CSS interop:** Host app `--semiotic-*` vars on `:root` are overridden by ThemeProvider's closer wrapper div. To let app tokens flow through, either skip ThemeProvider and set `--semiotic-*` vars in CSS, or use the hybrid approach (ThemeProvider for palette only, CSS vars for chrome).
|
|
242
|
+
|
|
294
243
|
## Server-Side Rendering
|
|
295
|
-
-
|
|
296
|
-
- `
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
- Works with Next.js App Router, Remix, Astro
|
|
300
|
-
- **Geo SSR requires pre-resolved features**: `renderGeoToStaticSVG` is synchronous — pass GeoJSON features directly, not reference strings like `"world-110m"`. Call `await resolveReferenceGeography("world-110m")` first and pass the result as `areas`.
|
|
244
|
+
- HOC charts and Frames render SVG automatically in server environments
|
|
245
|
+
- `renderXYToStaticSVG(props)`, `renderOrdinalToStaticSVG(props)`, `renderNetworkToStaticSVG(props)`, `renderGeoToStaticSVG(props)` from `semiotic/server`
|
|
246
|
+
- `frameType` is `"xy"|"ordinal"|"network"|"geo"` (NOT component names)
|
|
247
|
+
- Geo SSR requires pre-resolved features (synchronous — call `resolveReferenceGeography` first)
|
|
248
|
+
- Works with Next.js App Router, Remix, Astro
|
|
301
249
|
|
|
302
250
|
## AI Features
|
|
303
|
-
- `onObservation` — structured events
|
|
304
|
-
- `
|
|
305
|
-
- `
|
|
306
|
-
- `
|
|
307
|
-
- `
|
|
308
|
-
- `
|
|
309
|
-
- `
|
|
310
|
-
- `exportChart(containerDiv, { format: "png"|"svg" })` — pass the **wrapper div** (not the SVG element); it finds canvas + SVG internally. Default: PNG, composites canvas + SVG layers
|
|
311
|
-
- `npx semiotic-ai --doctor` — validate component + props JSON from CLI (uses both validateProps and diagnoseConfig)
|
|
251
|
+
- `onObservation` / `useChartObserver` — structured events across charts
|
|
252
|
+
- `toConfig`/`fromConfig`/`toURL`/`fromURL`/`copyConfig`/`configToJSX` — serialization
|
|
253
|
+
- `DetailsPanel` — click-driven detail panel in `ChartContainer`
|
|
254
|
+
- `validateProps(componentName, props)` — prop validation with typo suggestions
|
|
255
|
+
- `diagnoseConfig(componentName, props)` — anti-pattern detector (13+ checks)
|
|
256
|
+
- `exportChart(containerDiv, { format: "png"|"svg" })` — pass wrapper div, composites canvas+SVG
|
|
257
|
+
- `npx semiotic-ai --doctor` — CLI validation
|
|
312
258
|
|
|
313
|
-
##
|
|
259
|
+
## Canvas Pattern Fills
|
|
314
260
|
|
|
315
|
-
|
|
261
|
+
`createHatchPattern({ background, stroke, lineWidth, spacing, angle })` from `semiotic` — returns `CanvasPattern | null` for use as `fill` in style functions. Used by FunnelChart vertical mode for dropoff bars.
|
|
316
262
|
|
|
317
|
-
|
|
263
|
+
## Accessibility
|
|
318
264
|
|
|
319
|
-
|
|
265
|
+
Charts render with `role="group"` (outer interactive wrapper, keyboard/focus) and `role="img"` (inner canvas, read by assistive tech). SVG overlays include `<title>` and `<desc>`.
|
|
320
266
|
|
|
321
|
-
**
|
|
267
|
+
**Keyboard navigation**: Arrow keys navigate data points. In XY/ordinal charts, ArrowRight/Left moves within a series, ArrowUp/Down switches series. In network charts, arrows move to the spatially nearest node in the pressed direction; Enter cycles edge-connected neighbors. Home/End jump to first/last. PageUp/PageDown skip 10%. Escape clears focus.
|
|
322
268
|
|
|
323
|
-
**
|
|
269
|
+
**Focus ring**: Shape-adaptive dashed ring (circle for points, rect for bars, arc for wedges). Color: `--semiotic-focus` CSS var.
|
|
324
270
|
|
|
325
|
-
**
|
|
271
|
+
**Data summary**: `accessibleTable` (default true) renders a sr-only summary. Activate via keyboard focus or `actions.dataSummary` in ChartContainer. JIT-computed — no render cost until activated.
|
|
326
272
|
|
|
327
|
-
**
|
|
273
|
+
**Reduced motion**: `prefers-reduced-motion` auto-detected. Transitions skip to end state, orbit stops, pulse/decay disabled.
|
|
328
274
|
|
|
329
|
-
**
|
|
275
|
+
**High contrast**: `forced-colors` / `prefers-contrast: more` auto-detected. ThemeProvider applies `HIGH_CONTRAST_THEME` automatically.
|
|
330
276
|
|
|
331
|
-
**
|
|
277
|
+
**Hooks** (from `semiotic`): `useReducedMotion()`, `useHighContrast()` — SSR-safe, return `false` on server.
|
|
332
278
|
|
|
333
|
-
|
|
279
|
+
## Known Pitfalls
|
|
334
280
|
|
|
335
|
-
|
|
336
|
-
|
|
281
|
+
- **Tooltip datum shape**: HOC tooltip functions get raw data. Frame `tooltipContent` gets wrapped data — use `d.data`.
|
|
282
|
+
- **Tooltip positioning**: Tooltips auto-flip when near container edges (right→left, bottom→top). Custom `tooltip` content should not add its own background — the wrapper provides `--semiotic-tooltip-bg`, `--semiotic-tooltip-text`, etc. Override wrapper styles via CSS custom properties, not inline styles.
|
|
283
|
+
- **Legend positioning**: "bottom" auto-expands margin ~80px. For narrow charts (<400px), prefer "bottom" or "top".
|
|
284
|
+
- **MultiAxisLineChart legend**: Always use `legendPosition="bottom"` (or `"top"`) — the right-hand axis occupies the space where a right-side legend would go.
|
|
285
|
+
- **Log scale**: Clamps domain min to 1e-6 (log(0) undefined).
|
|
286
|
+
- **barPadding**: Pixel value, defaults 40/60. Reduce for small charts.
|
|
287
|
+
- **Horizontal bars**: Need wider left margin with long labels: `margin={{ left: 120 }}`.
|
|
288
|
+
- **LinkedCharts legends**: `CategoryColorProvider` suppresses child legends. Force with `showLegend={true}`.
|
|
289
|
+
- **Push API**: Omit `data` prop entirely. `data={[]}` clears pushed data every render.
|
|
290
|
+
- **frameProps style functions**: Bypass HOC color resolution — use `colorBy` prop instead. Frame style functions receive `(datum, categoryName)`, not `(datum, index)`.
|
|
291
|
+
- **v2 migration**: `htmlAnnotationRules` → `widget` annotations + `svgAnnotationRules`. v2 `summaryStyle` index-based coloring → v3 category-string-based.
|
|
292
|
+
- **accessibleTable**: Direct prop on HOCs. Set `accessibleTable={false}` to disable the sr-only data summary.
|
|
293
|
+
|
|
294
|
+
## Performance
|
|
295
|
+
|
|
296
|
+
Prefer string accessors (`xAccessor="value"`) over function accessors — always referentially stable. If you must use functions, memoize with `useCallback` or define outside the component. The pipeline uses `.toString()` comparison for inline arrows but this fails for closures capturing changing variables.
|