Module: RailsuiCharts::ChartHelper

Defined in:
lib/railsui_charts/chart_helper.rb

Constant Summary collapse

CIRCULAR_TYPES =

Forms whose placeholder should be a disc rather than a plotting area.

%i[pie donut polar_area radar].freeze

Instance Method Summary collapse

Instance Method Details

#railsui_chart(data, type: :line, **options) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/railsui_charts/chart_helper.rb', line 8

def railsui_chart(data, type: :line, **options)
  # An empty dataset is a normal day one, not an error. Rendering axes
  # around nothing looks like a chart that failed rather than a chart with
  # nothing to show yet.
  return railsui_chart_empty(**empty_options(options)) if ApexOptionsBuilder.blank?(data)

  config = ApexOptionsBuilder.new(data, type: type, **options).build
  id = options[:id] || "rui-chart-#{SecureRandom.hex(4)}"

  table = options[:accessible] != false ? accessibility_table(config, id: id) : nil

  (:div,
              id: id,
              class: "railsui-chart",
              # Charts draw when they scroll into view, so the space has to
              # be held from the start. Without it the page is short on
              # load and grows under the reader as they scroll.
              style: reserved_height(config),
              data: { controller: "railsui-chart", "railsui-chart-options-value": config.to_json }) do
    table
  end
end

#railsui_chart_empty(title: nil, description: nil, height: nil) ⇒ Object

Holds the chart's footprint so a card keeps its shape whether or not the query came back with anything.



74
75
76
# File 'lib/railsui_charts/chart_helper.rb', line 74

def railsui_chart_empty(title: nil, description: nil, height: nil)
  chart_state(:empty, title: title || "No data", description: description, height: height)
end

#railsui_chart_error(title: nil, description: nil, height: nil) ⇒ Object



78
79
80
# File 'lib/railsui_charts/chart_helper.rb', line 78

def railsui_chart_error(title: nil, description: nil, height: nil)
  chart_state(:error, title: title || "Couldn't load this chart", description: description, height: height)
end

#railsui_chart_skeleton(height: nil, type: :line, label: "Loading chart") ⇒ Object

Server-rendered placeholder for a chart whose data has not arrived yet — the thing a Turbo frame shows before it swaps in the real one. Pass the type: it will become so the placeholder is the shape being waited on.



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/railsui_charts/chart_helper.rb', line 85

def railsui_chart_skeleton(height: nil, type: :line, label: "Loading chart")
  classes = ["railsui-chart-skeleton"]
  classes << "railsui-chart-skeleton--circular" if CIRCULAR_TYPES.include?(type.to_sym)

  (:div,
              class: "railsui-chart-state railsui-chart-state--loading",
              style: state_height(height),
              role: "status",
              aria: { label: label, busy: true }) do
    (:span, "", class: classes.join(" "), aria: { hidden: true })
  end
end

#railsui_small_multiples(series, type: :line, height: 120, columns: 3, **options) ⇒ Object

One small chart per series, sharing a y-scale.

This is the honest answer when there are more categories than a single chart can hold. Eight lines on one axis is a plate of spaghetti, and a ninth colour is not distinguishable from the others anyway — facets scale where colour does not.

<%= railsui_small_multiples @plans, type: :area, columns: 3 %>


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/railsui_charts/chart_helper.rb', line 39

def railsui_small_multiples(series, type: :line, height: 120, columns: 3, **options)
  return railsui_chart_empty(**empty_options(options)) if ApexOptionsBuilder.blank?(series)

  entries = Array(series)
  shared = shared_scale(entries)

  (:div, class: "railsui-small-multiples", style: "--rui-small-multiple-columns: #{columns.to_i}") do
    safe_join(entries.each_with_index.map do |entry, index|
      (:div, class: "railsui-small-multiple") do
        safe_join([
          (:p, entry[:name] || entry["name"] || "Series #{index + 1}", class: "railsui-small-multiple__title"),
          # Every facet takes the same colour. The title carries identity
          # here, so spending a hue on it would say nothing extra.
          railsui_chart(entry[:data] || entry["data"], type: type, height: height,
                                                       yaxis: shared.merge(tickAmount: 3), **options)
        ])
      end
    end)
  end
end

#shared_scale(entries) ⇒ Object

Facets only compare if they share a scale. Left to themselves each one would fit its own data and a small series would look like a large one.



62
63
64
65
66
67
68
69
70
# File 'lib/railsui_charts/chart_helper.rb', line 62

def shared_scale(entries)
  values = entries.flat_map { |entry| Array(entry[:data] || entry["data"]).map { |point| ApexOptionsBuilder.value_of(point) } }.compact
  return {} if values.empty?

  min, max = values.minmax
  return {} if min == max

  { min: min, max: max }
end