Class: LightningUiKit::ChartComponent

Inherits:
BaseComponent
  • Object
show all
Defined in:
app/components/lightning_ui_kit/chart_component.rb

Constant Summary collapse

TYPES =
%i[bar line area].freeze
VIEW_WIDTH =
640
PAD_RIGHT =
16
PAD_TOP =
12
AXIS_PAD_LEFT =
44

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data:, type: :bar, series: nil, x_key: :label, height: 260, show_grid: true, show_legend: true, show_axis: true, **options) ⇒ ChartComponent

Returns a new instance of ChartComponent.



9
10
11
12
13
14
15
16
17
18
19
# File 'app/components/lightning_ui_kit/chart_component.rb', line 9

def initialize(data:, type: :bar, series: nil, x_key: :label, height: 260, show_grid: true, show_legend: true, show_axis: true, **options)
  @data = Array(data).map { |row| row.respond_to?(:to_h) ? row.to_h.symbolize_keys : row }
  @type = TYPES.include?(type) ? type : :bar
  @x_key = x_key.to_sym
  @height = height.to_i
  @show_grid = show_grid
  @show_legend = show_legend
  @show_axis = show_axis
  @series = build_series(series)
  @options = options
end

Instance Attribute Details

#heightObject (readonly)

Returns the value of attribute height.



21
22
23
# File 'app/components/lightning_ui_kit/chart_component.rb', line 21

def height
  @height
end

#seriesObject (readonly)

Returns the value of attribute series.



21
22
23
# File 'app/components/lightning_ui_kit/chart_component.rb', line 21

def series
  @series
end

#typeObject (readonly)

Returns the value of attribute type.



21
22
23
# File 'app/components/lightning_ui_kit/chart_component.rb', line 21

def type
  @type
end

Instance Method Details

#area?Boolean

Returns:

  • (Boolean)


27
28
29
# File 'app/components/lightning_ui_kit/chart_component.rb', line 27

def area?
  @type == :area
end

#area_path(points) ⇒ Object



155
156
157
158
159
160
161
162
# File 'app/components/lightning_ui_kit/chart_component.rb', line 155

def area_path(points)
  return "" if points.empty?

  segments = ["M #{points.first[0].round(2)} #{plot_bottom.round(2)}"]
  points.each { |x, y| segments << "L #{x.round(2)} #{y.round(2)}" }
  segments << "L #{points.last[0].round(2)} #{plot_bottom.round(2)} Z"
  segments.join(" ")
end

#bar?Boolean

Returns:

  • (Boolean)


23
24
25
# File 'app/components/lightning_ui_kit/chart_component.rb', line 23

def bar?
  @type == :bar
end

#bar_path(bar) ⇒ Object

Path for a bar with rounded top corners and a square base.



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'app/components/lightning_ui_kit/chart_component.rb', line 165

def bar_path(bar)
  x = bar[:x]
  y = bar[:y]
  w = bar[:width]
  h = bar[:height]
  return "" if h <= 0 || w <= 0

  r = [6.0, w / 2.0, h].min
  x2 = x + w
  bottom = y + h
  [
    "M #{r2(x)} #{r2(bottom)}",
    "L #{r2(x)} #{r2(y + r)}",
    "Q #{r2(x)} #{r2(y)} #{r2(x + r)} #{r2(y)}",
    "L #{r2(x2 - r)} #{r2(y)}",
    "Q #{r2(x2)} #{r2(y)} #{r2(x2)} #{r2(y + r)}",
    "L #{r2(x2)} #{r2(bottom)}",
    "Z"
  ].join(" ")
end

#barsObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'app/components/lightning_ui_kit/chart_component.rb', line 120

def bars
  group_width = bar_group_width
  inner = group_width * 0.2
  band = group_width - inner
  bar_width = @series.empty? ? band : band / @series.size

  @data.each_with_index.flat_map do |row, i|
    group_x = plot_left + i * group_width + inner / 2.0
    @series.each_with_index.map do |s, si|
      value = value_for(row, s)
      y = y_for(value)
      {
        x: group_x + si * bar_width,
        y: y,
        width: bar_width * 0.9,
        height: [plot_bottom - y, 0].max,
        color: s[:color],
        label: x_label(row),
        series_label: s[:label],
        value: format_number(value)
      }
    end
  end
end

#chart_idObject



54
55
56
# File 'app/components/lightning_ui_kit/chart_component.rb', line 54

def chart_id
  @chart_id ||= "lui-chart-#{object_id}"
end

#classesObject



47
48
49
50
51
52
# File 'app/components/lightning_ui_kit/chart_component.rb', line 47

def classes
  merge_classes([
    "lui:relative lui:w-full lui:text-foreground",
    @options[:class]
  ].compact.join(" "))
end

#countObject



88
# File 'app/components/lightning_ui_kit/chart_component.rb', line 88

def count = @data.size

#data_attrsObject



66
67
68
# File 'app/components/lightning_ui_kit/chart_component.rb', line 66

def data_attrs
  @options[:data] || {}
end

#empty?Boolean

Returns:

  • (Boolean)


31
32
33
# File 'app/components/lightning_ui_kit/chart_component.rb', line 31

def empty?
  @data.empty? || @series.empty?
end

#gradient_id(index) ⇒ Object



58
59
60
# File 'app/components/lightning_ui_kit/chart_component.rb', line 58

def gradient_id(index)
  "#{chart_id}-grad-#{index}"
end

#hover_columnsObject

Invisible full-height bands (one per category) that drive the hover tooltip.



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'app/components/lightning_ui_kit/chart_component.rb', line 187

def hover_columns
  step = (count > 1) ? plot_width.to_f / (count - 1) : plot_width.to_f

  @data.each_with_index.map do |row, i|
    if bar?
      x = plot_left + i * bar_group_width
      w = bar_group_width
    else
      x = point_x(i) - step / 2.0
      w = step
    end
    left = [x, plot_left].max
    right = [x + w, plot_right].min
    {x: left, width: [right - left, 0].max, label: x_label(row), payload: payload_for(i)}
  end
end

#line_points(series) ⇒ Object



145
146
147
148
149
# File 'app/components/lightning_ui_kit/chart_component.rb', line 145

def line_points(series)
  @data.each_with_index.map do |row, i|
    [point_x(i), y_for(value_for(row, series))]
  end
end

#payload_for(index) ⇒ Object



204
205
206
207
208
209
# File 'app/components/lightning_ui_kit/chart_component.rb', line 204

def payload_for(index)
  row = @data[index]
  @series.map do |s|
    {label: s[:label], value: format_number(value_for(row, s)), color: s[:color]}
  end.to_json
end

#plot_bottomObject



82
# File 'app/components/lightning_ui_kit/chart_component.rb', line 82

def plot_bottom = @height - (@show_axis ? 28 : 8)

#plot_heightObject



86
# File 'app/components/lightning_ui_kit/chart_component.rb', line 86

def plot_height = plot_bottom - plot_top

#plot_leftObject



76
# File 'app/components/lightning_ui_kit/chart_component.rb', line 76

def plot_left = @show_axis ? AXIS_PAD_LEFT : 8

#plot_rightObject



78
# File 'app/components/lightning_ui_kit/chart_component.rb', line 78

def plot_right = VIEW_WIDTH - PAD_RIGHT

#plot_topObject



80
# File 'app/components/lightning_ui_kit/chart_component.rb', line 80

def plot_top = PAD_TOP

#plot_widthObject



84
# File 'app/components/lightning_ui_kit/chart_component.rb', line 84

def plot_width = plot_right - plot_left

#point_x(index) ⇒ Object



100
101
102
103
104
# File 'app/components/lightning_ui_kit/chart_component.rb', line 100

def point_x(index)
  return plot_left + plot_width / 2.0 if count <= 1

  plot_left + (index.to_f / (count - 1)) * plot_width
end

#polyline(points) ⇒ Object



151
152
153
# File 'app/components/lightning_ui_kit/chart_component.rb', line 151

def polyline(points)
  points.map { |x, y| "#{x.round(2)},#{y.round(2)}" }.join(" ")
end

#show_axis?Boolean

Returns:

  • (Boolean)


39
40
41
# File 'app/components/lightning_ui_kit/chart_component.rb', line 39

def show_axis?
  @show_axis
end

#show_grid?Boolean

Returns:

  • (Boolean)


35
36
37
# File 'app/components/lightning_ui_kit/chart_component.rb', line 35

def show_grid?
  @show_grid
end

#show_legend?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'app/components/lightning_ui_kit/chart_component.rb', line 43

def show_legend?
  @show_legend && @series.any?
end

#stimulus_dataObject



62
63
64
# File 'app/components/lightning_ui_kit/chart_component.rb', line 62

def stimulus_data
  {controller: "lui-chart"}.merge(data_attrs)
end

#value_for(row, series) ⇒ Object



92
# File 'app/components/lightning_ui_kit/chart_component.rb', line 92

def value_for(row, series) = row[series[:key]].to_f

#view_heightObject



74
# File 'app/components/lightning_ui_kit/chart_component.rb', line 74

def view_height = @height

#view_widthObject

--- geometry ---



72
# File 'app/components/lightning_ui_kit/chart_component.rb', line 72

def view_width = VIEW_WIDTH

#x_label(row) ⇒ Object



90
# File 'app/components/lightning_ui_kit/chart_component.rb', line 90

def x_label(row) = row[@x_key]

#x_positionsObject



113
114
115
116
117
118
# File 'app/components/lightning_ui_kit/chart_component.rb', line 113

def x_positions
  @data.each_with_index.map do |row, i|
    x = bar? ? plot_left + i * bar_group_width + bar_group_width / 2.0 : point_x(i)
    {x: x, label: x_label(row)}
  end
end

#y_for(value) ⇒ Object



94
95
96
97
98
# File 'app/components/lightning_ui_kit/chart_component.rb', line 94

def y_for(value)
  return plot_bottom if chart_max.zero?

  plot_bottom - (value.to_f / chart_max) * plot_height
end

#y_ticks(steps = 4) ⇒ Object



106
107
108
109
110
111
# File 'app/components/lightning_ui_kit/chart_component.rb', line 106

def y_ticks(steps = 4)
  (0..steps).map do |i|
    value = chart_max * i / steps
    {label: format_number(value), y: y_for(value)}
  end
end