Class: RailsuiCharts::ApexOptionsBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/railsui_charts/apex_options_builder.rb

Constant Summary collapse

SUPPORTED_TYPES =
%i[line area bar column sparkline pie donut scatter bubble radar polar_area range_bar].freeze
BAR_THICKNESS =

Bars never fill their band. The leftover space is what keeps a chart quiet, so thickness scales down as the category count drops.

[[3, "30%"], [6, "42%"], [12, "58%"]].freeze
DEFAULT_BAR_THICKNESS =
"70%"
COMPARABLE_TYPES =

Series that support an overlaid previous-period comparison.

%i[line area column sparkline].freeze
LEGEND_INSET =

Both sides of a dual axis are cut into this many intervals, so one grid serves both scales.

28
LEGEND_ITEM_GAP =
8
LEGEND_MOBILE_ITEM_GAP =
6
AXIS_INTERVALS =
4
AXIS_LABEL_GUTTER =

Apex reserves a gutter between the axis labels and the edge of its canvas. On its own that is invisible; inside a card it is not, because the card's heading starts at the content edge and the axis labels start this far inside it. Cancelling it lines the chart up with the text above it.

16
CATEGORY_AXIS_GUTTER =

A category axis reserves almost nothing, because Apex sizes that column to the text itself — the only thing outside it is the grid's own left padding. Cancelling the full gutter here pushed the longest name twelve pixels past the edge of the card.

4
CIRCULAR_LEGEND_HEIGHT =

Two rows' worth. Circular charts cap at four categories, which is the most that can wrap onto a second line in a narrow card.

52

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data, type: :line, compare: nil, **options) ⇒ ApexOptionsBuilder

Returns a new instance of ApexOptionsBuilder.



65
66
67
68
69
70
71
# File 'lib/railsui_charts/apex_options_builder.rb', line 65

def initialize(data, type: :line, compare: nil, **options)
  @series = extract_series(data)
  @data = @series.first[:data]
  @compare = compare.nil? ? nil : normalize_data(compare)
  @type = validate_type(type)
  @options = options
end

Class Method Details

.blank?(data) ⇒ Boolean

No points, or points that carry no value. A series of zeroes is real data and is not blank — a quiet day still has something to say.

Returns:

  • (Boolean)


40
41
42
43
44
45
46
47
48
# File 'lib/railsui_charts/apex_options_builder.rb', line 40

def self.blank?(data)
  return true if data.nil?
  return data.all? { |series| blank?(series[:data] || series["data"]) } if series_form?(data)

  points = data.respond_to?(:to_a) && !data.is_a?(Array) ? data.to_a : Array(data)
  return true if points.empty?

  points.all? { |point| value_of(point).nil? }
end

.series_form?(data) ⇒ Boolean

A single series can be given bare; several arrive as an array of { name:, data: }. The data key is what tells the two apart, since a bare series is an array of values or of { x:, y: } points.

Returns:

  • (Boolean)


76
77
78
79
# File 'lib/railsui_charts/apex_options_builder.rb', line 76

def self.series_form?(data)
  data.is_a?(Array) && data.any? &&
    data.all? { |entry| entry.is_a?(Hash) && (entry.key?(:data) || entry.key?("data")) }
end

.value_of(point) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/railsui_charts/apex_options_builder.rb', line 50

def self.value_of(point)
  case point
  when Hash
    point = point.symbolize_keys if point.respond_to?(:symbolize_keys)
    # A span carries its value in its edges rather than in a `y`. Without
    # this a timeline looks empty to `blank?` and renders the empty state
    # instead of itself — data present, chart gone, nothing raised.
    return point[:from] || point[:to] if point.key?(:from) || point.key?(:to)

    point[:y]
  when Array then point[1]
  else point
  end
end

Instance Method Details

#buildObject



81
82
83
84
85
86
87
88
89
90
# File 'lib/railsui_charts/apex_options_builder.rb', line 81

def build
  config = deep_merge(base_options, chart_specific_options, comparison_options, multi_series_options, stacked_options, user_options)
  return config if sparkline?

  # Derived from the finished config, not from the defaults. Apex swaps a
  # breakpoint's axis object in wholesale rather than merging it, so
  # anything already decided — which side the scale hangs on, hidden
  # labels — has to be carried across or it is lost at that width.
  config.merge(responsive: responsive_options(config))
end