Name

HTML::D3 - A simple Perl module for generating charts using D3.js.

Version

Version 0.18

Synopsis

use HTML::D3;

my $chart = HTML::D3->new(
    width => 1024,
    height => 768,
    title => 'Sample Bar Chart'
);

my $data = [
    ['Category 1', 10],
    ['Category 2', 20],
    ['Category 3', 30]
];

my $html = $chart->render_bar_chart($data);
print $html;

$chart = HTML::D3->new(title => 'Sales Data');

$data = [
    ['Product A', 100],
    ['Product B', 150],
    ['Product C', 200]
];

$html = $chart->render_line_chart($data);
print $html;

Description

HTML::D3 is a Perl module that provides functionality to create simple charts using D3.js. The module generates HTML and JavaScript code to render the chart in a web browser.

Methods

The =head3 API SPECIFICATION subsections use Params::Validate::Strict schema syntax (type => 'arrayref' etc.) as a documentation convention. The module is also used at runtime in new() to validate constructor arguments; it is therefore a required runtime dependency. The schemas describe the parameter contract in machine-readable notation and can be plumbed into a WAF or test generator if desired.

New

my $chart = HTML::D3->new(%args);

Creates a new HTML::D3 object. Accepts the following optional arguments:

Render_Bar_Chart

my $html = $chart->render_bar_chart($data);

Generates HTML and JavaScript code to render a bar chart. Accepts the following arguments:

Returns a string containing the HTML and JavaScript code for the chart.

Errors

Api Specification

Input
{
    data => {
            type => 'arrayref',
            element_type => [ 'string', 'number' ]
    }
}

Each element of C<$data> is C<[ Str, Num ]>; passing C<undef> or a
non-arrayref dies.
Output
Str -- complete HTML5 document starting with C<< <!DOCTYPE html> >>;
       D3.js loaded from CDN; bar chart rendered with C<d3.scaleBand>.

Render_Animated_Bar_Chart

my $html = $chart->render_animated_bar_chart($data);

Generates HTML and JavaScript code to render a bar chart where each bar grows upward from the baseline on page load. Bars are staggered so they rise one-after-another from left to right. Accepts the following arguments:

Returns a string containing the complete HTML5 document.

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
}

Each element of C<$data> is C<[ Str, Num ]>; passing C<undef> or a
non-arrayref dies.
Output
Str -- complete HTML5 document; each bar animates from height=0 upward
       using C<d3.transition()> with a staggered per-bar delay.

Render_Line_Chart

my $html = $chart->render_line_chart($data);

Generates HTML and JavaScript code to render a line chart. Accepts the following arguments:

Returns a string containing the HTML and JavaScript code for the chart.

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
}

Each element of C<$data> is C<[ Str, Num ]>; passing C<undef> or a
non-arrayref dies.
Output
Str -- complete HTML5 document; line chart with C<d3.scalePoint> and C<d3.line()>.

Render_Animated_Line_Chart

my $html = $chart->render_animated_line_chart($data);

Generates HTML and JavaScript code to render a line chart where the line draws itself from left to right on page load, followed by each data-point circle fading in once the line is complete. Accepts the following arguments:

Returns a string containing the complete HTML5 document.

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
}

Each element of C<$data> is C<[ Str, Num ]>; passing C<undef> or a
non-arrayref dies.
Output
Str -- complete HTML5 document; the line path animates via
       C<stroke-dashoffset> with C<d3.easeLinear>; data-point circles
       fade in with C<opacity> after the line transition completes.

Render_Pie_Chart

my $html = $chart->render_pie_chart($data);
my $html = $chart->render_pie_chart($data, { separator => ':' });

Generates HTML and JavaScript code to render a pie chart. Each slice is coloured with d3.schemeCategory10; percentage labels appear inside each slice and a colour legend is shown to the right of the pie. Accepts the following arguments:

Returns a string containing the complete HTML5 document.

Errors

Api Specification

Input
{
    data => {
            type => 'arrayref',
            element_type => [ 'string', 'number' ]
    },
    opts => { type => 'hashref', optional => 1, default => {} },
}

Each element of C<$data> is C<[ Str, Num ]>; passing C<undef> or a
non-arrayref dies.
Recognised C<opts> key: C<separator> (string, default C<'/'>)
- character shown between label and value in the SVG legend.
Output
Str -- complete HTML5 document; pie rendered with C<d3.pie()> and
       C<d3.arc()>; slices coloured with C<d3.schemeCategory10>;
       percentage label inside each slice; legend to the right.

Render_Animated_Pie_Chart

my $html = $chart->render_animated_pie_chart($data);
my $html = $chart->render_animated_pie_chart($data, { separator => ':' });

Generates HTML and JavaScript code to render an animated pie chart where each slice fans out from zero angle on page load using attrTween and d3.interpolate. Percentage labels fade in once all slices are drawn. Accepts the following arguments:

Returns a string containing the complete HTML5 document.

Errors

Api Specification

Input
{
    data => {
            type => 'arrayref',
            element_type => [ 'string', 'number' ]
    },
    opts => { type => 'hashref', optional => 1, default => {} },
}

Each element of C<$data> is C<[ Str, Num ]>; passing C<undef> or a
non-arrayref dies.
Recognised C<opts> key: C<separator> (string, default C<'/'>)
- character shown between label and value in the SVG legend.
Output
Str -- complete HTML5 document; slices animate via C<attrTween> with
       C<d3.interpolate> (1000 ms); percentage labels fade in afterwards.

Render_Pie_Chart_Snippet

my $fragment = $chart->render_pie_chart_snippet(\@slices);
my $fragment = $chart->render_pie_chart_snippet(\@slices, \%opts);
# $fragment->{svg_id} - always 'pie_chart'
# $fragment->{html}   - embeddable fragment; caller must load D3 v7

Generates an embeddable pie or donut chart fragment for use in existing HTML layouts. Returns { svg_id => 'pie_chart', html => Str }. The caller is responsible for loading D3 v7 before embedding the fragment.

Data Format

Each element of \@slices is [$label, $value] or [$label, $value, \%extra]. Negative values are silently converted to their absolute value. Zero-value slices are silently omitted. \%extra key/value pairs are shown as additional rows in the hover tooltip.

Options (\%Opts)

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
    opts => { type => 'hashref', optional => 1, default => {} },
}

Each element of C<$data> is C<[ Str, Num ]> or C<[ Str, Num, HashRef ]>;
passing C<undef> or a non-arrayref dies.
Recognised C<opts> keys: C<animated> (boolean, default C<0>),
C<donut> (boolean, default C<0>), C<sort_slices> (string: C<'value'>,
C<'label'>, or C<'none'>; default C<'none'>), C<max_slices> (integer,
default C<0>), C<legend> (boolean, default C<1>),
C<color_scheme> (string, default C<'tableau10'>),
C<separator> (string, default C<'/'> - shown between label and value in
legend entries).
Output
HashRef -- C<{ svg_id =E<gt> 'pie_chart', html =E<gt> Str }>;
           embeddable fragment; no DOCTYPE, no page shell, no D3 CDN tag.

Render_Heatmap_Snippet

my $fragment = $chart->render_heatmap_snippet(\@triples);
my $fragment = $chart->render_heatmap_snippet(\@triples, \%opts);
# $fragment->{svg_id} - always 'heatmap'
# $fragment->{html}   - embeddable fragment; caller must load D3 v7

Generates an embeddable grid heatmap for use in existing HTML layouts. Each cell sits at the intersection of an X-axis label and a Y-axis label; its colour encodes the cell's numeric value using a sequential D3 colour scale. Returns { svg_id => 'heatmap', html => Str }. The caller is responsible for loading D3 v7 before embedding the fragment.

Data Format

Each element of \@triples is [$x_label, $y_label, $value]. $value must be numeric or undef (undef rows are silently skipped). Zero is a valid value and maps to the lightest cell colour. The caller is responsible for any aggregation: if multiple triples share the same (x_label, y_label) pair, the last one wins.

Options (\%Opts)

Errors

Side Effects

Appends a tooltip div to the page when the fragment is rendered in the browser.

Api Specification

Input
{
    data => { type => 'arrayref' },
    opts => { type => 'hashref', optional => 1, default => {} },
}

Each element of C<$data> is C<[ Str, Str, Num|undef ]>.
Recognised C<opts> keys: C<color_scheme> (string, default C<'YlOrRd'>),
C<x_label> (string, default C<''>), C<y_label> (string, default C<''>),
C<val_label> (string, default C<'Value'>), C<show_values> (boolean,
default C<0>), C<cell_padding> (integer 0-8, default C<2>),
C<legend> (boolean, default C<1>), C<animated> (boolean, default C<0>).
Output
HashRef -- C<{ svg_id =E<gt> 'heatmap', html =E<gt> Str }>;
           embeddable fragment; no DOCTYPE, no page shell, no D3 CDN tag.

Render_Bar_Chart_Snippet

my $result = $chart->render_bar_chart_snippet(\@bars);
my $result = $chart->render_bar_chart_snippet(\@bars, \%opts);

Generates an embeddable D3.js v7 bar chart fragment. Returns a hashref { svg_id => 'bar_chart', html => $str } where $str is a self-contained HTML fragment (no page shell, no D3 CDN tag) that the caller embeds directly after loading D3.js. $str is a Perl character string with the UTF-8 flag set (or pure ASCII when all labels are ASCII).

Each element of \@bars is an array reference:

[ $label, $value ]
[ $label, $value, \%extra ]

$label is the category name (string); $value is a non-negative number (negative values are silently converted to their absolute value); the optional \%extra hashref supplies additional key/value pairs shown in the hover tooltip. Data points with an undefined $value are silently skipped.

Options (\%Opts)

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
    opts => { type => 'hashref', optional => 1 },
    orientation => { type => 'string', memberof => [ 'vertical', 'horizontal' ], optional => 1 },
    sort_bars => { type => 'string', memberof => [ 'value', 'label', 'none' ], optional => 1 }
}

Each element of C<$data>: C<[ Str, Num ]> or C<[ Str, Num, HashRef ]>;
undef C<$value> silently skipped; negative C<$value> becomes positive.
Output
HashRef -- { svg_id => 'bar_chart', html => Str }
html is a Perl character string (UTF-8 flag set, or pure ASCII).

Render_Line_Chart_With_Tooltips

$html = $chart->render_line_chart_with_tooltips($data);

Generates HTML and JavaScript code to render a line chart with mouseover tooltips. Accepts the following arguments:

Returns a string containing the HTML and JavaScript code for the chart. The JavaScript tooltip strings use <\/b> (with a backslash) rather than </b> to satisfy html-tidy's requirement that </ followed by a letter not appear literally inside <script> blocks.

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
}

Each element of C<$data> is C<[ Str, Num ]>; passing C<undef> or a
non-arrayref dies.
Output
Str -- complete HTML5 document; mouseover tooltip reveals label and value.
       Tooltip strings use C<< <\/b> >> not C<< </b> >>.

Render_Line_Chart_Snippet

my $fragment = $chart->render_line_chart_snippet($data);
my $fragment = $chart->render_line_chart_snippet($data, \%opts);
# $fragment->{svg_id} - the id attribute of the <svg> element
# $fragment->{html}   - embeddable HTML fragment (style + svg + script)

Generates an embeddable HTML fragment for a line chart with mouseover tooltips. Unlike render_line_chart_with_tooltips, this method returns a fragment with no <!DOCTYPE>, <html>, <head>, or <body> wrapper, suitable for splicing directly into a Mojolicious TT (or any other) layout.

The caller is responsible for loading D3 in the page <head>, e.g.:

<script src="https://d3js.org/d3.v7.min.js"></script>

Arguments

Return Value

A hash reference with:

Errors

Side Effects

None. The method is read-only.

Api Specification

{
    data => { type => 'arrayref' },
    opts => {
        type     => 'hashref',
        optional => 1,
        keys     => {
            id         => { type => 'string',  optional => 1 },
            responsive => { type => 'boolean', optional => 1 },
        },
    },
}

Render_Zoomable_Line_Chart_Snippet

my $fragment = $chart->render_zoomable_line_chart_snippet($data);
my $fragment = $chart->render_zoomable_line_chart_snippet($data, { animated => 1 });
# $fragment->{svg_id} - the id attribute of the <svg> element
# $fragment->{html}   - embeddable HTML fragment (style + button + svg + script)

Like render_line_chart_snippet, but adds brush-to-zoom: the user can drag across a range of the x-axis to zoom into that region. A Reset zoom button (hidden until a zoom is active) returns the chart to its original extent. Subsequent brushes on the zoomed view zoom in further; Reset always returns to the full dataset.

The caller is responsible for loading D3 in the page <head>.

Accepts the same arguments as render_line_chart_snippet: an array reference of data points, each [$x, $y] or [$x, $y, \%extra], plus an optional second argument $opts (hashref).

Options

Api Specification

Input
{
    data => { type => 'arrayref' },
    opts => { type => 'hashref', optional => 1, default => {} },
}

Each element of C<$data> is C<[ Str, Num ]> or C<[ Str, Num, HashRef ]>;
passing C<undef> or a non-arrayref dies.
Recognised C<opts> key: C<animated> (boolean, default C<0>).
Output
HashRef -- C<{ svg_id =E<gt> 'chart', html =E<gt> Str }>;
           embeddable fragment; no DOCTYPE, no page shell, no D3 CDN tag.

Errors

Dies with Data must be an array of arrays if $data is not an arrayref.

Render_Multi_Series_Line_Chart_With_Tooltips

$html = $chart->render_multi_series_line_chart_with_tooltips($data);

Generates HTML and JavaScript code to render a chart of many lines with mouseover tooltips.

Accepts the following arguments:

Returns a string containing the HTML and JavaScript code for the chart. Tooltip strings use <\/b> rather than </b> for html-tidy compliance.

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
}

Each element of C<$data> is a hashref with keys C<name> (string) and
C<data> (arrayref of hashrefs with C<label> and C<value> keys);
passing C<undef> or a non-arrayref dies.
Output
Str -- complete HTML5 document; one coloured line per series with mouseover tooltips.

Render_Multi_Series_Line_Chart_With_Animated_Tooltips

$html = $chart->render_multi_series_line_chart_with_animated_tooltips($data);

Generates HTML and JavaScript code to render a chart of many lines with animated mouseover tooltips.

Accepts the following arguments:

Returns a string containing the complete HTML5 document. The tooltip appears with a CSS translateY slide-in animation. Tooltip strings use <\/b> for html-tidy compliance.

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
}

Each element of C<$data> is a hashref with keys C<name> (string) and
C<data> (arrayref of hashrefs with C<label> and C<value> keys);
passing C<undef> or a non-arrayref dies.
Output
Str -- complete HTML5 document; animated tooltip uses CSS translateY transition.

Render_Multi_Series_Line_Chart_With_Legends

$html = $chart->render_multi_series_line_chart_with_legends($data);

Generates HTML and JavaScript code to render a chart of many lines with a static colour legend. Each series gets a labelled colour swatch in the legend area.

Accepts the following arguments:

Returns a string containing the complete HTML5 document. The stylesheet defines a .legend CSS class used by the D3-generated legend elements.

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
}

Each element of C<$data> is a hashref with keys C<name> (string) and
C<data> (arrayref of hashrefs with C<label> and C<value> keys);
passing C<undef> or a non-arrayref dies.
Output
Str -- complete HTML5 document; static colour legend rendered as SVG C<g> elements
       with the C<.legend> CSS class applied via D3 C<.attr("class", "legend")>.

Render_Multi_Series_Line_Chart_With_Interactive_Legends

$html = $chart->render_multi_series_line_chart_with_interactive_legends($data);

Generates HTML and JavaScript code to render a chart of many lines with interactive legends to filter, highlight or modify elements based on legend selections.

Accepts the following arguments:

Returns a string containing the complete HTML5 document. Clicking a legend entry toggles that series' opacity using an isVisible boolean flag in the D3 click handler (opacity is set to isVisible ? 0 : 1 on each click).

Errors

Api Specification

Input
{
    data => { type => 'arrayref' },
}

Each element of C<$data> is a hashref with keys C<name> (string) and
C<data> (arrayref of hashrefs with C<label> and C<value> keys);
passing C<undef> or a non-arrayref dies.
Output
Str -- complete HTML5 document; legend clicks toggle series visibility.
       The C<isVisible> JS variable tracks current visibility state.
       Opacity toggled by C<isVisible ? 0 : 1>.

Render_Scatter_Chart_Snippet

my $fragment = $chart->render_scatter_chart_snippet($data);
my $fragment = $chart->render_scatter_chart_snippet($data, \%opts);
# $fragment->{svg_id} - the id attribute of the <svg> element
# $fragment->{html}   - embeddable HTML fragment (style + svg + script)

Generates an embeddable HTML fragment for a scatter plot with mouseover tooltips. Returns a fragment with no <!DOCTYPE>, <html>, <head>, or <body> wrapper; the caller is responsible for loading D3 in the page.

Arguments

Return Value

A hash reference with svg_id (string) and html (Perl character string).

Errors

Api Specification

{
    data => { type => 'arrayref' },
    opts => {
        type     => 'hashref',
        optional => 1,
        keys     => {
            id          => { type => 'string',  optional => 1 },
            color       => { type => 'string',  optional => 1, default => 'steelblue' },
            x_label     => { type => 'string',  optional => 1, default => '' },
            y_label     => { type => 'string',  optional => 1, default => '' },
            value_label => { type => 'string',  optional => 1, default => 'Value' },
            animated    => { type => 'boolean', optional => 1, default => 0 },
            responsive  => { type => 'boolean', optional => 1, default => 0 },
        },
    },
}

Render_Table_Snippet

my $fragment = $chart->render_table_snippet($data);
my $fragment = $chart->render_table_snippet($data, \%opts);
# $fragment->{table_id} - the id attribute of the <table> element
# $fragment->{html}     - embeddable HTML fragment (style + table + script)

Generates an embeddable HTML fragment for a sortable, filterable data table. Returns a fragment with no page-shell wrapper; the caller loads D3 if needed.

Arguments

Return Value

A hash reference with table_id (string) and html (Perl character string). Note: returns table_id, not svg_id, because this method renders an HTML table rather than an SVG chart.

Errors

Api Specification

{
    data => { type => 'arrayref' },
    opts => {
        type     => 'hashref',
        optional => 1,
        keys     => {
            id       => { type => 'string',  optional => 1, default => 'data_table' },
            sortable => { type => 'boolean', optional => 1, default => 1 },
            caption  => { type => 'string',  optional => 1, default => '' },
        },
    },
}

Support

This module is provided as-is without any warranty.

Please report any bugs or feature requests to bug-html-d3 at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=HTML-D3. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

You can find documentation for this module with the perldoc command.

perldoc HTML::D3

You can also look for information at:

Bugs

It would help to have the render routine to return the head and body components separately.

See Also

Author

Nigel Horne njh@nigelhorne.com

Formal Specification

Render_Bar_Chart

render_bar_chart : HTML::D3 × (ArrayRef | undef) → Str ∪ ⊥

pre  data = undef              ⇒ die "Data is not optional"
pre  ref(data) ≠ 'ARRAY'      ⇒ die "Data must be an array of arrays"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post ∀ d ∈ data . d[0] ⊆ result

Render_Line_Chart

render_line_chart : HTML::D3 × (ArrayRef | undef) → Str ∪ ⊥

pre  ref(data) ≠ 'ARRAY'  ⇒ die "Data must be an array of arrays"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post "d3.scalePoint" ⊆ result ∧ "d3.line()" ⊆ result

Render_Animated_Bar_Chart

render_animated_bar_chart : HTML::D3 × (ArrayRef | undef) → Str ∪ ⊥

pre  data = undef              ⇒ die "Data is not optional"
pre  ref(data) ≠ 'ARRAY'      ⇒ die "Data must be an array of arrays"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post ".transition()" ⊆ result ∧ ".delay(" ⊆ result

Render_Animated_Line_Chart

render_animated_line_chart : HTML::D3 × (ArrayRef | undef) → Str ∪ ⊥

pre  ref(data) ≠ 'ARRAY'  ⇒ die "Data must be an array of arrays"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post "stroke-dashoffset" ⊆ result ∧ "d3.easeLinear" ⊆ result

Render_Pie_Chart

render_pie_chart : HTML::D3 × (ArrayRef | undef) × (HashRef | undef) → Str ∪ ⊥

pre  data = undef              ⇒ die "Data is not optional"
pre  ref(data) ≠ 'ARRAY'      ⇒ die "Data must be an array of arrays"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post "d3.pie()" ⊆ result ∧ "d3.arc()" ⊆ result ∧ "d3.schemeCategory10" ⊆ result
post opts.separator = S        ⇒  " S " ⊆ result (SVG legend: label S value)

Render_Animated_Pie_Chart

render_animated_pie_chart : HTML::D3 × (ArrayRef | undef) × (HashRef | undef) → Str ∪ ⊥

pre  data = undef              ⇒ die "Data is not optional"
pre  ref(data) ≠ 'ARRAY'      ⇒ die "Data must be an array of arrays"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post "attrTween" ⊆ result ∧ "d3.interpolate" ⊆ result
post opts.separator = S        ⇒  " S " ⊆ result (SVG legend: label S value)

Render_Line_Chart_Snippet

render_line_chart_snippet :
    HTML::D3 × (ArrayRef | undef) × (HashRef | undef) → HashRef ∪ ⊥

pre  ref(data) ≠ 'ARRAY'  ⇒ die "Data must be an array of arrays"
post result ∈ HashRef
post result.svg_id = opts.id // "chart"
post result.html ∈ Str
post "<!DOCTYPE" ∉ result.html

Render_Zoomable_Line_Chart_Snippet

render_zoomable_line_chart_snippet :
    HTML::D3 × (ArrayRef | undef) × (HashRef | undef) → HashRef ∪ ⊥

pre  ref(data) ≠ 'ARRAY'  ⇒ die "Data must be an array of arrays"
post result ∈ HashRef
post result.svg_id = opts.id // "chart"
post result.html ∈ Str
post "<!DOCTYPE" ∉ result.html
post "d3.brushX()" ⊆ result.html
post opts.animated = 1  ⇒  "stroke-dashoffset" ⊆ result.html
                          ∧ "initialDrawDone" ⊆ result.html

Render_Pie_Chart_Snippet

render_pie_chart_snippet :
    HTML::D3 × (ArrayRef | undef) × (HashRef | undef) → HashRef ∪ ⊥

pre  ref(data) ≠ 'ARRAY'  ⇒ die "Data must be an array of arrays"
pre  ∀ d ∈ data . d[1] < 0  ⇒  d[1] := |d[1]|      -- negative → absolute
pre  ∀ d ∈ data . d[1] = 0  ⇒  d ∉ result           -- zero → omitted
post result ∈ HashRef
post result.svg_id = "pie_chart"
post result.html ∈ Str
post "<!DOCTYPE" ∉ result.html
post "d3.pie()" ⊆ result.html ∧ "schemeTableau10" ⊆ result.html
post opts.animated = 1  ⇒  "attrTween" ⊆ result.html
                          ∧ "initialDrawDone" ⊆ result.html
post opts.donut = 1     ⇒  "innerRadius" ⊆ result.html
post opts.max_slices = N ∧ N ≥ 2 ∧ |data| > N
                        ⇒  |result_slices| = N ∧ "Other" ∈ result_labels
post opts.separator = S ⇒  " S " ⊆ result.html (HTML legend: label S value (pct%))

Render_Heatmap_Snippet

render_heatmap_snippet :
    HTML::D3 × (ArrayRef | undef) × (HashRef | undef) → HashRef ∪ ⊥

pre  ref(data) ≠ 'ARRAY'       ⇒ die "Data must be an array of arrays"
pre  ∃ pt ∈ data . ref(pt) ≠ 'ARRAY'
                               ⇒ die "Each data point must be an array reference"
pre  ∃ pt ∈ data . |pt| < 3   ⇒ die "Each data point must have at least 3 elements"
pre  ∃ pt ∈ data . defined(pt[2]) ∧ ¬numeric(pt[2])
                               ⇒ die "Value must be numeric"
pre  opts.color_scheme = S ∧ S ∉ {YlOrRd,Blues,Greens,Purples,RdPu,YlGnBu}
                               ⇒ die "Unknown color_scheme: S"
pre  opts.cell_padding = N ∧ (N < 0 ∨ N > 8)
                               ⇒ die "cell_padding must be between 0 and 8"
pre  ∀ pt ∈ data . pt[2] = undef ⇒ pt ∉ result     -- undef rows skipped
pre  ∃ pt₁,pt₂ ∈ data . pt₁[0]=pt₂[0] ∧ pt₁[1]=pt₂[1]
                               ⇒ last-write wins
post result ∈ HashRef
post result.svg_id = "heatmap"
post result.html ∈ Str
post "<!DOCTYPE" ∉ result.html
post "scaleSequential" ⊆ result.html
post opts.animated = 1         ⇒ "prefers-reduced-motion" ⊆ result.html
post opts.legend = 1           ⇒ "linearGradient" ⊆ result.html

Render_Bar_Chart_Snippet

render_bar_chart_snippet :
    HTML::D3 × (ArrayRef | undef) × (HashRef | undef) → HashRef ∪ ⊥

pre  ref(data) ≠ 'ARRAY'       ⇒ die "Data must be an array of arrays"
pre  ∃ pt ∈ data . ref(pt) ≠ 'ARRAY'
                               ⇒ die "Each data point must be an array reference"
pre  ∃ pt ∈ data . |pt| < 2   ⇒ die "Each data point must have at least 2 elements"
pre  ∃ pt ∈ data . defined(pt[1]) ∧ ¬numeric(pt[1])
                               ⇒ die "Value must be numeric"
pre  opts.orientation ∉ {'vertical','horizontal'}
                               ⇒ die "orientation must be 'vertical' or 'horizontal'"
pre  opts.sort_bars ∉ {'value','label','none'}
                               ⇒ die "sort_bars must be 'value', 'label', or 'none'"
pre  ∀ pt ∈ data . pt[1] < 0  ⇒  pt[1] := |pt[1]|      -- negative → absolute
pre  ∀ pt ∈ data . pt[1] = undef ⇒ pt ∉ result          -- undef rows skipped
post result ∈ HashRef
post result.svg_id = opts.id // "bar_chart"
post result.html ∈ Str
post "<!DOCTYPE" ∉ result.html
post "d3.scaleBand" ⊆ result.html ∧ "d3.scaleLinear" ⊆ result.html
post opts.animated = 1         ⇒ "prefers-reduced-motion" ⊆ result.html
post opts.color = 'categorical' ⇒ "schemeTableau10" ⊆ result.html
post opts.max_bars = N ∧ N ≥ 2 ∧ |data| > N
                        ⇒ |result_bars| = N ∧ "Other" ∈ result_labels

Render_Scatter_Chart_Snippet

render_scatter_chart_snippet :
    HTML::D3 × (ArrayRef | undef) × (HashRef | undef) → HashRef ∪ ⊥

pre  ref(data) ≠ 'ARRAY'       ⇒ die "Data must be an array of arrays"
pre  ∃ pt ∈ data . ref(pt) ≠ 'ARRAY'
                               ⇒ die "Each data point must be an array reference"
pre  ∃ pt ∈ data . |pt| < 2   ⇒ die "Each data point must have at least 2 elements"
pre  ∃ pt ∈ data . ¬numeric(pt[0])
                               ⇒ die "X value must be numeric"
pre  ∃ pt ∈ data . ¬numeric(pt[1])
                               ⇒ die "Y value must be numeric"
post result ∈ HashRef
post result.svg_id = opts.id // "scatter_chart"
post result.html ∈ Str
post "<!DOCTYPE" ∉ result.html
post "d3.scaleLinear" ⊆ result.html
post opts.animated = 1         ⇒ "prefers-reduced-motion" ⊆ result.html

Render_Table_Snippet

render_table_snippet :
    HTML::D3 × (ArrayRef | undef) × (HashRef | undef) → HashRef ∪ ⊥

pre  ref(data) ≠ 'ARRAY'       ⇒ die "Data must be an array of arrays"
pre  |data| = 0                ⇒ die "Data must have at least one row (header row)"
pre  ∃ row ∈ data . ref(row) ≠ 'ARRAY'
                               ⇒ die "Each row must be an array reference"
post result ∈ HashRef
post result.table_id = opts.id // "data_table"
post result.html ∈ Str
post "<!DOCTYPE" ∉ result.html
post opts.sortable ≠ 0         ⇒ "dt-sortable" ⊆ result.html

Render_Line_Chart_With_Tooltips

render_line_chart_with_tooltips : HTML::D3 × (ArrayRef | undef) → Str ∪ ⊥

pre  ref(data) ≠ 'ARRAY'  ⇒ die "Data must be an array of arrays"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post "mouseover" ⊆ result
post "</b>" ∉ result ∧ "<\/b>" ∈ result

Render_Multi_Series_Line_Chart_With_Tooltips

render_multi_series_line_chart_with_tooltips : HTML::D3 × (ArrayRef | undef) → Str ∪ ⊥

pre  ref(data) ≠ 'ARRAY'  ⇒ die "Data must be an array of hashes"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post "</b>" ∉ result ∧ "<\/b>" ∈ result

Render_Multi_Series_Line_Chart_With_Animated_Tooltips

render_multi_series_line_chart_with_animated_tooltips : HTML::D3 × (ArrayRef | undef) → Str ∪ ⊥

pre  ref(data) ≠ 'ARRAY'   ⇒ die "Data must be an array of hashes"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post "translateY" ⊆ result
post "</b>" ∉ result ∧ "<\/b>" ∈ result

Render_Multi_Series_Line_Chart_With_Legends

render_multi_series_line_chart_with_legends : HTML::D3 × (ArrayRef | undef) → Str ∪ ⊥

pre  ref(data) ≠ 'ARRAY'  ⇒ die "Data must be an array of hashes"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post ".legend" ⊆ result

Render_Multi_Series_Line_Chart_With_Interactive_Legends

render_multi_series_line_chart_with_interactive_legends : HTML::D3 × (ArrayRef | undef) → Str ∪ ⊥

pre  ref(data) ≠ 'ARRAY'            ⇒ die "Data must be an array of hashes"
post result ∈ Str
post "<!DOCTYPE" ⊆ result
post "isVisible" ⊆ result
post "isVisible ? 0 : 1" ⊆ result
post ".legend" ⊆ result

Copyright 2025-2026 Nigel Horne.

Usage is subject to the GPL2 licence terms. If you use it, please let me know.