Revision history for HTML-D3 - Visualise various charts using D3
0.20 Wed Oct 14 08:01:05 PM EDT 2026
[ Enhancements ]
- render_histogram_snippet: new 'binned' option for pre-computed bins, so a
caller can bin server-side and have the exact edges it chose rendered
without the browser re-binning. Each element of the data arrayref is a
hashref with a count plus either a label or a numeric lo/hi pair (the
label is then derived as "[lo,hi)"); d3.bin() is not called on this path
and the supplied counts are authoritative. New 'scheme' option supplies
the bar fill (first colour of the D3 categorical scheme) when 'color' is
absent. New errors: 'Data must contain at least one bin', 'Each
pre-binned entry must be a hash reference', 'Bin count must be numeric',
'Bin count must not be negative', 'Each pre-binned entry must have a
label or numeric lo and hi'. The non-binned path is unchanged, including
its 'Data must be an array of numeric values' error, and the bin-label
tooltip now escapes the label.
- render_scatter_chart_snippet: new per-point colour and size dimensions.
An optional fourth element per point may carry { series => Str, size => Num };
the new opts colour_by_series (colour from the series value, with a
legend), scheme (D3 categorical scheme, default 'tableau10'), series_col
and size_col (tooltip row labels) and radius_range ([min, max] pixel
radii mapped by area with d3.scaleSqrt over the observed size domain)
render them. Colour and size are independent: a point may carry both.
A point with no size value keeps a fixed radius and a point with no
series value keeps the default colour, so output for data with neither is
unchanged.
- render_multi_series_line_chart_snippet: new snippet method that generates
an embeddable multi-series line chart fragment rather than a whole page,
so a host application can keep its own page chrome. Accepts an arrayref
of { name, data => [ { label, value }, ... ] } series and an optional
\%opts hashref: scheme, x_label, y_label, animated, responsive, id.
Series need not share labels (the X domain is the union, in first-seen
order), each series gets a line and its own dots, and a clickable legend
toggles each series. Returns { svg_id => 'chart', html => Str }.
[ Documentation ]
- The three capabilities above are documented in the POD, README.md and the
formal specification, with new tests in t/snippet_histogram.t,
t/snippet_scatter.t and t/snippet_multi_series_line.t.
0.19 Tue Oct 13 03:35:05 PM EDT 2026
[ Enhancements ]
- render_stacked_bar_snippet: new snippet method that generates an embeddable
D3 v7 stacked bar chart. Accepts an arrayref of hashrefs (one row per
category/segment combination) and an optional \%opts hashref. Opts: cat_col,
seg_col, val_col (row-hash key names, defaults 'cat'/'seg'/'val'), orient
('v' vertical / 'h' horizontal, default 'v'), scheme (D3 categorical colour
scheme: tableau10/category10/set2/set3/paired, default 'tableau10'), x_label
(axis label, default ''), sort (undef / 'value' desc-total / 'label' alpha).
Multiple rows sharing the same cat+seg are summed on the Perl side before
encoding. Uses d3.stack() with d3.stackOrderNone / d3.stackOffsetNone.
Includes an interactive legend: clicking a segment label toggles that layer
on/off and re-stacks the visible segments. Tooltip shows category, segment,
and value on mouseover. Returns { svg_id => 'stacked_bar', html => Str }.
- render_combo_snippet: new snippet method that generates an embeddable combo
chart — bars for one measure on the left Y axis, a line for another on the
right Y axis, sharing the same X (category/band) axis. Accepts an arrayref
of hashrefs and \%opts: x_col, bar_col, line_col (row-hash key names,
defaults 'x'/'bar_val'/'line_val'), bar_label, line_label (legend/axis
labels, defaults 'Bar'/'Line'), bar_color (default 'steelblue'), line_color
(default '#e15759'). Two independent d3.scaleLinear domains (left for bars,
right for line); bars rendered first, line path and dots on top. Both axes
labelled. Tooltip on bar and dot mouseover shows all three measures.
Returns { svg_id => 'combo_chart', html => Str }.
- render_treemap_snippet: new snippet method that generates an embeddable D3
v7 treemap using d3.treemapSquarify tiling. Accepts two data formats:
(1) flat-path arrayref of hashrefs [{path => 'Root/A/X', value => N}, ...]
converted to a stratify hierarchy via d3.stratify() inside the generated JS;
(2) a nested hashref tree with name/children/value structure passed directly
to d3.hierarchy(). Format is detected by ref() of the first argument.
\%opts: path_sep (separator string for flat paths, default '/'), scheme
(categorical colour scheme, default 'tableau10'), label (bool, default 1;
shows node name inside each cell, clipped to cell width). Tooltip shows node
name and value. Returns { svg_id => 'treemap', html => Str }.
- render_histogram_snippet: new snippet method that generates an embeddable
frequency histogram. Accepts an arrayref of numeric scalars (pre-extracted
by the caller — no column-extraction logic inside the module) and \%opts:
bins (positive integer, default 20; pass undef for automatic Sturges count:
ceil(log2(n)+1)), x_label (axis label, default ''), color (bar fill colour,
default 'steelblue'). Uses d3.bin() with xScale.ticks(numBins) thresholds.
Tooltip shows bin range and count on mouseover.
Returns { svg_id => 'histogram', html => Str }.
0.18 Wed Sep 23 11:10:09 AM EDT 2026
[ Enhancements ]
- render_bar_chart_snippet: new snippet method that generates an embeddable
D3 v7 bar chart. Accepts an array of [$label, $value] (or
[$label, $value, \%extra]) pairs and an optional \%opts hashref with keys:
orientation ('vertical'/'horizontal', default 'vertical'),
sort_bars ('value'/'label'/'none', default 'none'),
max_bars (int, default 0; collapses tail into "Other"),
color (CSS colour or 'categorical' for Tableau-10 palette, default
'steelblue'), show_values (bool, default 0; prints values on bars),
animated (bool, default 0; bars grow from baseline with 800 ms stagger;
respects prefers-reduced-motion), value_label (tooltip label, default
'Value'), x_label (bottom-axis label, default ''). Returns
{ svg_id => 'bar_chart', html => Str }. Negative values are silently
converted to their absolute value; undef values are silently skipped.
X-axis labels are rotated -45 degrees when more than 8 bars are shown in
vertical orientation. Extra \%extra fields appear as additional tooltip
rows. Returns a Perl character string (UTF-8 flag set).
- render_scatter_chart_snippet: new snippet method that generates an
embeddable D3 v7 scatter chart. Accepts an array of [$x, $y] (or
[$x, $y, \%extra]) pairs; both x and y must be numeric. Optional \%opts:
id (default 'scatter_chart'), color (CSS colour, default 'steelblue'),
x_label, y_label, value_label (tooltip label, default 'Value'), animated
(bool; circles fade in from opacity 0; respects prefers-reduced-motion),
responsive. Returns { svg_id => Str, html => Str }. Uses d3.scaleLinear
for both axes; includes esc() XSS helper in generated JavaScript.
- render_table_snippet: new snippet method that generates an embeddable HTML
table with optional D3-powered column sorting. Accepts an array of array
references; the first row is the header. Optional \%opts: id (default
'data_table'), sortable (bool, default 1; adds clickable column headers
with D3 sort), caption (string; emits a <caption> element). Returns
{ table_id => Str, html => Str } (note: table_id, not svg_id). All header
and cell content is HTML-escaped.
- id opt: all five existing snippet methods (render_line_chart_snippet,
render_zoomable_line_chart_snippet, render_pie_chart_snippet,
render_heatmap_snippet, render_bar_chart_snippet) now accept an id key in
\%opts that overrides the default svg_id. The returned svg_id reflects the
override. Derived element ids (tooltip div, legend div, etc.) are computed
from the svg_id string. Fully backward-compatible: omitting id gives the
same default id as before.
- responsive opt: all render methods now support a responsive display mode.
Pass responsive => 1 to the constructor to make all full-page renders emit
viewBox="0 0 W H" width="100%" height="auto" instead of fixed pixel
dimensions on the SVG element. Snippet methods additionally accept
responsive in their per-call \%opts (takes precedence over the constructor
setting). Default is 0 (fixed dimensions), so existing callers are
unaffected. Uses new private helper _svg_attrs().
[ Bug Fixes ]
- render_pie_chart_snippet: tooltip handler was missing HTML sanitisation.
Added esc() JavaScript helper (same as bar/heatmap/scatter snippets) and
applied it to slice labels and extra-field keys and values in the
mouseover handler. Prevents XSS when label or extra fields contain HTML
special characters.
- render_line_chart_snippet: completed API documentation (Arguments, Options,
Return value, Errors, Side effects, API SPECIFICATION). Now accepts an
optional second argument \%opts with id and responsive keys.
0.17 Tue Sep 22 08:45:56 PM EDT 2026
[ Bug Fixes ]
- All render_*_snippet methods now return a Perl character string (UTF-8
flag set) in the {html} value instead of a UTF-8 byte string. The
previous use of encode_json() produced octets; any caller that
concatenated the result with a character string (e.g. a Mojolicious
Template Toolkit response) would silently double-encode non-ASCII
characters, producing mojibake. Fixed by replacing encode_json() with
a module-level JSON::MaybeXS instance configured with utf8 => 0, which
serialises to a character string.
0.16 Mon Sep 21 07:34:35 PM EDT 2026
[ Enhancements ]
- render_heatmap_snippet: new snippet method that generates an embeddable
D3 v7 grid heatmap. Accepts an array of [$x, $y, $value] triples and an
optional \%opts hashref with keys: color_scheme (YlOrRd/Blues/Greens/
Purples/RdPu/YlGnBu, default YlOrRd), x_label, y_label, val_label
(tooltip label, default 'Value'), show_values (print value in each cell,
auto-suppressed when cell < 28px), cell_padding (0-8 px gap, default 2),
legend (colour-scale bar, default on), animated (fade-in with
prefers-reduced-motion support, default off). Returns
{ svg_id => 'heatmap', html => Str }. undef values are silently skipped;
duplicate (x, y) pairs: last write wins. Requires Scalar::Util
looks_like_number (already in core distribution).
0.15 Wed Sep 16 01:51:38 PM EDT 2026
[ Enhancements ]
- render_pie_chart, render_animated_pie_chart, render_pie_chart_snippet: new
optional C<separator> key in the C<%opts> second argument controls the
character shown between label and value in the legend (default C<'/'>).
render_pie_chart and render_animated_pie_chart now accept the optional
second hashref argument for the first time; existing callers passing only
C<$data> are unaffected.
0.14 Tue Sep 15 09:12:17 PM EDT 2026
[ Enhancements ]
- render_pie_chart_snippet: major upgrade. Now accepts an optional second
argument \%opts with six keys:
- animated (bool, default 0): fan slices in from arc-length 0 using
attrTween / d3.easeBackOut.overshoot(1.2) with 800 ms staggered
transitions; legend fades in after; respects prefers-reduced-motion
- donut (bool, default 0): inner radius 38% of outer; total sum shown
in the centre hole
- sort_slices ('value'|'label'|'none', default 'none'): sort order
- max_slices (int, default 0): collapse tail into an "Other" slice
- legend (bool, default 1): HTML legend panel with colour swatches
- color_scheme (string, default 'tableau10'): supports tableau10,
category10, set2, set3, paired
Negative values are silently converted to their absolute value; zero-
value slices are silently omitted. Optional \%extra hashref per data
point is shown as additional rows in the hover tooltip.
svg_id changes from 'chart' to 'pie_chart' for namespace clarity.
(Breaking change in svg_id value; all other behaviour is backward-
compatible when opts are omitted.)
0.13 Tue Sep 15 06:05:45 PM EDT 2026
[ Enhancements ]
- render_zoomable_line_chart_snippet: accept an optional second argument
{ animated => 1 } to animate the initial line draw on page load.
The line traces itself left-to-right via stroke-dashoffset / d3.easeLinear
(1200 ms); data-point circles then fade in from opacity 0 over 300 ms
(after a 1200 ms delay so they appear only once the line reaches them).
Subsequent zoom and reset redraws are never animated.
Respects prefers-reduced-motion: when the user has requested reduced
motion the chart draws immediately at full opacity.
Omitting the second argument or passing { animated => 0 } gives the
existing behaviour unchanged (backward-compatible).
0.12 Sun Sep 13 08:14:05 AM EDT 2026
[ Enhancements ]
- Added render_animated_bar_chart(): bar chart with on-load grow animation —
each bar rises from the baseline using d3.transition() with an 800 ms
duration and a 100 ms per-bar stagger, so bars appear one after another
from left to right
- Added render_animated_line_chart(): line chart with on-load draw animation —
the line traces itself left-to-right via the stroke-dashoffset technique with
d3.easeLinear (1500 ms), followed by data-point circles fading in from
opacity 0 once the line is complete
- Added render_pie_chart(): full-page pie chart with percentage labels inside
each slice and a colour legend; slices coloured with d3.schemeCategory10
- Added render_animated_pie_chart(): same as render_pie_chart() but slices fan
out from zero on page load via attrTween/d3.interpolate (1000 ms), with
percentage labels fading in once drawing is complete
- Added render_pie_chart_snippet(): embeddable pie chart fragment returning
{svg_id, html} — no page shell, no D3 CDN tag; caller loads D3
[ Tests ]
- Test::HTML::T5 doesn't build on a number of platforms, so
remove the dependancy. Hopefully this will be fixed at sometime.
0.11 Thu Sep 10 09:32:08 PM EDT 2026
[ Bug Fixes ]
- render_zoomable_line_chart_snippet: the y-axis lower bound was
hard-coded to 0, causing data points with negative Y values to be
plotted off-screen below the x-axis. Changed to
Math.min(0, d3.min(newData, d => d.value)) so the domain extends
downward when the data contains negative values (e.g.
accounting-notation amounts).
0.10 Fri Aug 28 08:57:09 PM EDT 2026
[ Enhancements ]
- Added render_zoomable_line_chart_snippet(): embeddable line chart with brush-to-zoom — drag to select an x-axis range, animated transition to the zoomed view, incremental re-zoom on the zoomed view, and a Reset zoom button to restore the full dataset
0.09 Thu Aug 27 09:16:02 PM EDT 2026
[ Enhancements ]
- render_line_chart_snippet: accept an optional third element per data pair — a hashref of extra key/value pairs serialised as an "extra" property in the D3 data binding and appended as additional rows in the mouseover tooltip
0.08 Thu Aug 27 01:30:10 PM EDT 2026
[ Enhancements ]
- Use Params::Get
- Added test dashboard
- Added support for Object::Configure
- Added render_line_chart_snippet() returning an embeddable {svg_id, html} fragment for use in Mojolicious TT (and similar) layouts without post-processing
[ Bug Fixes ]
- Escape </b> as <\/b> in all JavaScript tooltip .html() strings to fix html-tidy '<' + '/' + letter failures on cpantesters
https://www.cpantesters.org/cpan/report/8321add6-d178-11ef-8e88-00fe6d8775ea
0.07 Fri Jan 10 21:16:49 EST 2025
Added support for interactive legends
0.06 Thu Jan 9 21:51:36 EST 2025
Added support for legends
0.05 Wed Jan 8 21:04:21 EST 2025
Added support for animated tooltips
0.04 Tue Jan 7 21:26:42 EST 2025
Added support for line charts with tooltips
Added support for multiple line charts with tooltips
0.03 Mon Jan 6 20:55:12 EST 2025
Added line chart support
Use JSON::MaybeXS instead of JSON
0.02 Mon Jan 6 10:13:11 EST 2025
Fix CI breakage
0.01 Mon Jan 6 09:47:22 EST 2025
First draft - POC: generates bar charts only
Keyboard Shortcuts
Global
s
Focus search bar
?
Bring up this help dialog
GitHub
gp
Go to pull requests
gi
Go to GitHub issues (only if GitHub is preferred repository)