rcharts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +99 -0
- data/Rakefile +21 -0
- data/app/helpers/rcharts/graph_helper/axes/axis_element/styles.rb +91 -0
- data/app/helpers/rcharts/graph_helper/axes/axis_element.rb +89 -0
- data/app/helpers/rcharts/graph_helper/axes/label_element.rb +32 -0
- data/app/helpers/rcharts/graph_helper/axes/tick_element.rb +38 -0
- data/app/helpers/rcharts/graph_helper/axes.rb +8 -0
- data/app/helpers/rcharts/graph_helper/categories/bar_builder.rb +32 -0
- data/app/helpers/rcharts/graph_helper/categories/bar_segment_element.rb +49 -0
- data/app/helpers/rcharts/graph_helper/categories/bars_element.rb +52 -0
- data/app/helpers/rcharts/graph_helper/categories/category_builder.rb +115 -0
- data/app/helpers/rcharts/graph_helper/element.rb +65 -0
- data/app/helpers/rcharts/graph_helper/element_builder.rb +42 -0
- data/app/helpers/rcharts/graph_helper/graph/axes.rb +41 -0
- data/app/helpers/rcharts/graph_helper/graph/axis/caster.rb +45 -0
- data/app/helpers/rcharts/graph_helper/graph/axis/positioning.rb +66 -0
- data/app/helpers/rcharts/graph_helper/graph/axis/ticks.rb +66 -0
- data/app/helpers/rcharts/graph_helper/graph/axis.rb +86 -0
- data/app/helpers/rcharts/graph_helper/graph/calculator.rb +91 -0
- data/app/helpers/rcharts/graph_helper/graph/composition.rb +35 -0
- data/app/helpers/rcharts/graph_helper/graph/options.rb +33 -0
- data/app/helpers/rcharts/graph_helper/graph.rb +8 -0
- data/app/helpers/rcharts/graph_helper/graph_builder.rb +270 -0
- data/app/helpers/rcharts/graph_helper/legend_entry_builder.rb +46 -0
- data/app/helpers/rcharts/graph_helper/rule_element.rb +68 -0
- data/app/helpers/rcharts/graph_helper/series/area_element.rb +50 -0
- data/app/helpers/rcharts/graph_helper/series/path.rb +153 -0
- data/app/helpers/rcharts/graph_helper/series/path_element.rb +72 -0
- data/app/helpers/rcharts/graph_helper/series/point.rb +58 -0
- data/app/helpers/rcharts/graph_helper/series/scatter_element.rb +87 -0
- data/app/helpers/rcharts/graph_helper/series/series_builder.rb +145 -0
- data/app/helpers/rcharts/graph_helper/tooltips/entry_builder.rb +47 -0
- data/app/helpers/rcharts/graph_helper/tooltips/foreign_object_element.rb +84 -0
- data/app/helpers/rcharts/graph_helper/tooltips/hover_target_element.rb +39 -0
- data/app/helpers/rcharts/graph_helper/tooltips/marker_element.rb +38 -0
- data/app/helpers/rcharts/graph_helper/tooltips/tooltip_builder.rb +44 -0
- data/app/helpers/rcharts/graph_helper/tooltips/tooltip_element.rb +45 -0
- data/app/helpers/rcharts/graph_helper.rb +249 -0
- data/lib/generators/rcharts/install/install_generator.rb +13 -0
- data/lib/generators/rcharts/install/templates/rcharts.css +392 -0
- data/lib/rcharts/engine.rb +25 -0
- data/lib/rcharts/percentage.rb +36 -0
- data/lib/rcharts/type/percentage.rb +20 -0
- data/lib/rcharts/type/symbol.rb +29 -0
- data/lib/rcharts/type.rb +9 -0
- data/lib/rcharts/version.rb +6 -0
- data/lib/rcharts.rb +52 -0
- data/lib/tasks/rcharts_tasks.rake +6 -0
- metadata +107 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
module GraphHelper
|
|
5
|
+
module Series
|
|
6
|
+
# :nodoc:
|
|
7
|
+
Point = Struct.new(:x, :y) do
|
|
8
|
+
def to_s = "#{x},#{y}"
|
|
9
|
+
|
|
10
|
+
def +(other) = Point.new(x + other.x, y + other.y)
|
|
11
|
+
|
|
12
|
+
def -(other) = Point.new(x - other.x, y - other.y)
|
|
13
|
+
|
|
14
|
+
def control(previous_point, next_point, smoothing: 1.0, reverse: false)
|
|
15
|
+
(self + handle_offset(previous_point, next_point, smoothing:, reverse:)).then do |control|
|
|
16
|
+
clamp(control, (reverse ? previous_point : next_point))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def incomplete?
|
|
21
|
+
x.nil? || y.nil?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def complete?
|
|
25
|
+
!incomplete?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
|
|
30
|
+
def handle_offset(previous_point, next_point, smoothing: 1.0, reverse: false)
|
|
31
|
+
(next_point - previous_point).handle_position(smoothing: smoothing, reverse: reverse,
|
|
32
|
+
extremum: extremum?(previous_point, next_point))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def handle_position(smoothing: 1.0, reverse: false, extremum: false)
|
|
36
|
+
Point.new(Math.cos(angle(reverse:)) * length(smoothing:),
|
|
37
|
+
extremum ? 0 : Math.sin(angle(reverse:)) * length(smoothing:))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def angle(reverse: false) = Math.atan2(y, x) + (reverse ? Math::PI : 0)
|
|
43
|
+
|
|
44
|
+
def length(smoothing: 1.0) = Math.sqrt((x**2) + (y**2)) * smoothing
|
|
45
|
+
|
|
46
|
+
def extremum?(previous_point, next_point) = (y - previous_point.y) * (next_point.y - y) <= 0
|
|
47
|
+
|
|
48
|
+
def clamp(control, limit)
|
|
49
|
+
Point.new(clamp_point(:x, control, limit), clamp_point(:y, control, limit))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def clamp_point(point, control, limit)
|
|
53
|
+
control[point].clamp(*[self[point], limit[point]].minmax)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
module GraphHelper
|
|
5
|
+
module Series
|
|
6
|
+
class ScatterElement < PathElement # :nodoc:
|
|
7
|
+
attribute :marker_size, :float, default: 10
|
|
8
|
+
attribute :marker_margin, :float, default: 2
|
|
9
|
+
attribute :marker_id
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def tags
|
|
14
|
+
series_tag
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def series_tag
|
|
18
|
+
tag.g do
|
|
19
|
+
definitions_tag + path_tag
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def path_tag
|
|
24
|
+
tag.g do
|
|
25
|
+
points.select(&:complete?).each do |point|
|
|
26
|
+
concat marker_for(point)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def definitions_tag
|
|
32
|
+
tag.defs do
|
|
33
|
+
cross_tag unless marker_id
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def cross_tag
|
|
38
|
+
tag.symbol id:, x:, y:, width:, height: do
|
|
39
|
+
line_tag + line_tag(invert_x: true)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def line_tag(invert_x: false)
|
|
44
|
+
tag.line x1: invert_x ? marker_size : marker_margin, x2: invert_x ? marker_margin : marker_size, y1:, y2:,
|
|
45
|
+
class: 'series-path'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def marker_for(point)
|
|
49
|
+
tag.use href: marker_id || "##{id}", x: Percentage.new(point.x), y: Percentage.new(point.y),
|
|
50
|
+
class: ['series-path', color_class]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def id
|
|
54
|
+
[id_hash, :marker].join('-')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def x
|
|
58
|
+
total_size / -2
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def y
|
|
62
|
+
total_size / -2
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def width
|
|
66
|
+
total_size
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def height
|
|
70
|
+
total_size
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def y1
|
|
74
|
+
marker_margin
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def y2
|
|
78
|
+
marker_size
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def total_size
|
|
82
|
+
marker_size + marker_margin
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
module GraphHelper
|
|
5
|
+
module Series
|
|
6
|
+
# = \Series Builder
|
|
7
|
+
class SeriesBuilder < ElementBuilder
|
|
8
|
+
##
|
|
9
|
+
# :attr_accessor:
|
|
10
|
+
attribute :name
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
# :attr_accessor: index
|
|
14
|
+
attribute :index, :integer, default: 0
|
|
15
|
+
|
|
16
|
+
attribute :composition, default: -> { Graph::Composition.new }
|
|
17
|
+
attribute :series_options, default: -> { {} }
|
|
18
|
+
attribute :id_hash, :string, default: -> { SecureRandom.hex(4) }
|
|
19
|
+
|
|
20
|
+
# Renders the series as a line plot. Passes through <tt>:data</tt>, and <tt>:aria</tt>,
|
|
21
|
+
# and <tt>:class</tt> options to the tag builder.
|
|
22
|
+
# <%= graph_for @sales do |graph| %>
|
|
23
|
+
# <%= graph.series do |series| %>
|
|
24
|
+
# <%= series.line smooth: 0.12 %>
|
|
25
|
+
# <% end %>
|
|
26
|
+
# <% end %>
|
|
27
|
+
#
|
|
28
|
+
# ==== Options
|
|
29
|
+
# [<tt>:smooth</tt>] Smoothing factor for the line.
|
|
30
|
+
# [<tt>:axis</tt>] The axis for the series. Defaults to the first continuous axis.
|
|
31
|
+
# [<tt>:inline_axis</tt>] The axis for the categories. Defaults to the first discrete axis.
|
|
32
|
+
def line(**)
|
|
33
|
+
path_tag(**)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Renders the series as an area plot. Passes through <tt>:data</tt>, and <tt>:aria</tt>,
|
|
37
|
+
# and <tt>:class</tt> options to the tag builder.
|
|
38
|
+
# <%= graph_for @sales do |graph| %>
|
|
39
|
+
# <%= graph.series do |series| %>
|
|
40
|
+
# <%= series.area smooth: 0.12 %>
|
|
41
|
+
# <% end %>
|
|
42
|
+
# <% end %>
|
|
43
|
+
#
|
|
44
|
+
# ==== Options
|
|
45
|
+
# [<tt>:smooth</tt>] Smoothing factor for the area.
|
|
46
|
+
# [<tt>:axis</tt>] The axis for the series. Defaults to the first continuous axis.
|
|
47
|
+
# [<tt>:inline_axis</tt>] The axis for the categories. Defaults to the first discrete axis.
|
|
48
|
+
def area(**)
|
|
49
|
+
area_tag(sign: :positive, **) + area_tag(sign: :negative, **)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Renders the series as a scatter plot. Passes through <tt>:data</tt>, and <tt>:aria</tt>,
|
|
53
|
+
# and <tt>:class</tt> options to the tag builder.
|
|
54
|
+
# <%= graph_for @sales do |graph| %>
|
|
55
|
+
# <%= graph.series do |series| %>
|
|
56
|
+
# <%= series.scatter %>
|
|
57
|
+
# <% end %>
|
|
58
|
+
# <% end %>
|
|
59
|
+
# ==== Options
|
|
60
|
+
# [<tt>:marker_size</tt>] Size of the markers. Defaults to <tt>10.0</tt>.
|
|
61
|
+
# [<tt>:marker_margin</tt>] Margin around the marker symbol. Defaults to <tt>2.0</tt>.
|
|
62
|
+
# [<tt>:marker_id</tt>] ID of a custom marker symbol (a <tt><symbol></tt> element).
|
|
63
|
+
# [<tt>:axis</tt>] The axis for the series. Defaults to the first continuous axis.
|
|
64
|
+
# [<tt>:inline_axis</tt>] The axis for the categories. Defaults to the first discrete axis.
|
|
65
|
+
def scatter(**)
|
|
66
|
+
scatter_tag(**)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def path_tag(axis: nil, inline_axis: nil, **)
|
|
72
|
+
resolve_axis :continuous, axis do |continuous_axis|
|
|
73
|
+
resolve_axis :discrete, inline_axis do |discrete_axis|
|
|
74
|
+
render PathElement.new(series: current_points(discrete_axis, continuous_axis),
|
|
75
|
+
horizontal: discrete_axis.horizontal?, series_options:, index:, id_hash:, **)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def area_tag(sign: nil, axis: nil, inline_axis: nil, **)
|
|
81
|
+
resolve_axis :continuous, axis do |continuous_axis|
|
|
82
|
+
resolve_axis :discrete, inline_axis do |discrete_axis|
|
|
83
|
+
render AreaElement.new(series: current_points(discrete_axis, continuous_axis, signed: sign),
|
|
84
|
+
previous_series: previous_points(discrete_axis, continuous_axis, signed: sign),
|
|
85
|
+
mask_series: mask_points(discrete_axis, continuous_axis),
|
|
86
|
+
block_position: block_position(continuous_axis),
|
|
87
|
+
horizontal: discrete_axis.horizontal?,
|
|
88
|
+
series_options:, index:, id_hash:, **)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def scatter_tag(axis: nil, inline_axis: nil, **)
|
|
94
|
+
resolve_axis :continuous, axis do |continuous_axis|
|
|
95
|
+
resolve_axis :discrete, inline_axis do |discrete_axis|
|
|
96
|
+
render ScatterElement.new(series: current_points(discrete_axis, continuous_axis),
|
|
97
|
+
horizontal: discrete_axis.horizontal?,
|
|
98
|
+
series_options:, index:, id_hash:, **)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def current_points(inline_axis, block_axis, signed: nil)
|
|
104
|
+
series(block_axis, signed:).to_h do |key, value|
|
|
105
|
+
[inline_axis.position_for(key), value.try { block_axis.position_for(it) }]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def previous_points(inline_axis, block_axis, signed: nil)
|
|
110
|
+
return [] if series(block_axis, signed:) == previous_series(block_axis, signed:)
|
|
111
|
+
|
|
112
|
+
previous_series(block_axis, signed:).to_h do |key, value|
|
|
113
|
+
[inline_axis.position_for(key), block_axis.position_for(value)]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def series(block_axis, signed: nil)
|
|
118
|
+
composition.signed(signed)
|
|
119
|
+
.then { block_axis.stacked? ? it.stacked : it }
|
|
120
|
+
.then { it[name] }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def previous_series(block_axis, signed: nil)
|
|
124
|
+
composition.signed(signed)
|
|
125
|
+
.then { block_axis.stacked? ? it.stacked(exclude_current: true) : it }
|
|
126
|
+
.then { it[name] }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def mask_points(inline_axis, block_axis)
|
|
130
|
+
composition.sum_complete.to_h do |key, value|
|
|
131
|
+
[inline_axis.position_for(key), value.try { block_axis.position_for(it) }]
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def block_position(block_axis)
|
|
136
|
+
block_axis.position_for([block_axis.adjusted_minimum, 0].max)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def resolve_axis(type, name = nil, &)
|
|
140
|
+
(name ? composition.axes.fetch(*name) : composition.axes.public_send(type)).then(&)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
module GraphHelper
|
|
5
|
+
module Tooltips
|
|
6
|
+
# = Tooltip Entry Builder
|
|
7
|
+
class EntryBuilder < ElementBuilder
|
|
8
|
+
##
|
|
9
|
+
# :attr_accessor:
|
|
10
|
+
attribute :value
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
# :attr_accessor:
|
|
14
|
+
attribute :name
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
# :attr_accessor: index
|
|
18
|
+
attribute :index, :integer, default: 0
|
|
19
|
+
|
|
20
|
+
attribute :series_options, default: -> { {} }
|
|
21
|
+
|
|
22
|
+
# Renders the symbol associated with the series. You can override this on a per-series basis using
|
|
23
|
+
# <tt>:series_options</tt> with GraphHelper#graph_for.
|
|
24
|
+
# To change the global set of symbols and colors, see RCharts.symbol_for and RCharts.color_class_for.
|
|
25
|
+
# <%= graph_for @sales do |graph| %>
|
|
26
|
+
# <%= graph.legend do |series| %>
|
|
27
|
+
# <%= series.symbol %>
|
|
28
|
+
# <%= series.name %>
|
|
29
|
+
# <% end %>
|
|
30
|
+
# <% end %>
|
|
31
|
+
def symbol
|
|
32
|
+
tag.span symbol_character, class: ['series-symbol', color_class]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def color_class
|
|
38
|
+
series_options.fetch(:color_class, RCharts.color_class_for(index))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def symbol_character
|
|
42
|
+
series_options.fetch(:symbol, RCharts.symbol_for(index))
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
module GraphHelper
|
|
5
|
+
module Tooltips
|
|
6
|
+
class ForeignObjectElement < Element # :nodoc:
|
|
7
|
+
attribute :inline_position, :'rcharts/percentage', default: Percentage::MIN
|
|
8
|
+
attribute :inline_axis, :'rcharts/symbol'
|
|
9
|
+
attribute :index, :integer, default: 0
|
|
10
|
+
attribute :values_count, :integer, default: 0
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def tags(&)
|
|
15
|
+
tag.foreignObject width:, height:, x:, y:, class: 'tooltip-object' do
|
|
16
|
+
container_tag(&)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def container_tag(&)
|
|
21
|
+
tag.div class: ['tooltip-container', { 'anchor-end' => anchor_bottom?, 'justify-end' => anchor_right? }], &
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def x
|
|
25
|
+
return unless horizontal_inline_axis?
|
|
26
|
+
|
|
27
|
+
anchor_right? ? Percentage::MIN : inline_position
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def y
|
|
31
|
+
return if horizontal_inline_axis?
|
|
32
|
+
|
|
33
|
+
anchor_bottom? ? Percentage::MIN : inline_position
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def width
|
|
37
|
+
return Percentage::MAX unless horizontal_inline_axis?
|
|
38
|
+
|
|
39
|
+
anchor_left? ? Percentage::MAX - inline_position : inline_position
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def height
|
|
43
|
+
return Percentage::MAX if horizontal_inline_axis?
|
|
44
|
+
|
|
45
|
+
anchor_top? ? Percentage::MAX - inline_position : inline_position
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def anchor_right?
|
|
49
|
+
return true unless horizontal_inline_axis?
|
|
50
|
+
|
|
51
|
+
first_half? == false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def anchor_left?
|
|
55
|
+
return false unless horizontal_inline_axis?
|
|
56
|
+
|
|
57
|
+
first_half?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def anchor_top?
|
|
61
|
+
return false if horizontal_inline_axis?
|
|
62
|
+
|
|
63
|
+
first_half?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def anchor_bottom?
|
|
67
|
+
return false if horizontal_inline_axis?
|
|
68
|
+
|
|
69
|
+
first_half? == false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def horizontal_inline_axis?
|
|
73
|
+
inline_axis == :x
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def first_half?
|
|
77
|
+
return if values_count.zero? # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
|
|
78
|
+
|
|
79
|
+
index < values_count / 2.0
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
module GraphHelper
|
|
5
|
+
module Tooltips
|
|
6
|
+
class HoverTargetElement < Element # :nodoc:
|
|
7
|
+
attribute :inline_position, :'rcharts/percentage', default: Percentage::MIN
|
|
8
|
+
attribute :inline_size, :'rcharts/percentage', default: Percentage::MIN
|
|
9
|
+
attribute :inline_axis, :'rcharts/symbol'
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def tags(&)
|
|
14
|
+
tag.rect x:, y:, width:, height:, class: 'tooltip-hover-target'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def x
|
|
18
|
+
inline_position - (inline_size / 2) if horizontal_inline_axis?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def y
|
|
22
|
+
inline_position - (inline_size / 2) unless horizontal_inline_axis?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def width
|
|
26
|
+
horizontal_inline_axis? ? inline_size : Percentage::MAX
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def height
|
|
30
|
+
horizontal_inline_axis? ? Percentage::MAX : inline_size
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def horizontal_inline_axis?
|
|
34
|
+
inline_axis == :x
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
module GraphHelper
|
|
5
|
+
module Tooltips
|
|
6
|
+
class MarkerElement < Element # :nodoc:
|
|
7
|
+
attribute :inline_position, :'rcharts/percentage'
|
|
8
|
+
attribute :inline_axis, :'rcharts/symbol'
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def tags(&)
|
|
13
|
+
tag.line x1:, x2:, y1:, y2:, class: 'tooltip-marker'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def x1
|
|
17
|
+
horizontal_inline_axis? ? inline_position : Percentage::MIN
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def x2
|
|
21
|
+
horizontal_inline_axis? ? inline_position : Percentage::MAX
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def y1
|
|
25
|
+
horizontal_inline_axis? ? Percentage::MIN : inline_position
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def y2
|
|
29
|
+
horizontal_inline_axis? ? Percentage::MAX : inline_position
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def horizontal_inline_axis?
|
|
33
|
+
inline_axis == :x
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
module GraphHelper
|
|
5
|
+
module Tooltips
|
|
6
|
+
# = Tooltip Builder
|
|
7
|
+
class TooltipBuilder < ElementBuilder
|
|
8
|
+
##
|
|
9
|
+
# :attr_accessor:
|
|
10
|
+
attribute :name
|
|
11
|
+
|
|
12
|
+
attribute :values, default: -> { {} }
|
|
13
|
+
attribute :series_options, default: -> { {} }
|
|
14
|
+
|
|
15
|
+
# Renders one or more series present in the data. For each series yields an EntryBuilder which
|
|
16
|
+
# contains the index, key, and value. See EntryBuilder for details.
|
|
17
|
+
# <%= graph_for @annual_sales do |graph| %>
|
|
18
|
+
# <%= graph.tooltips do |category| %>
|
|
19
|
+
# <h4><%= category.name %></h4>
|
|
20
|
+
# <%= category.series do |series| %>
|
|
21
|
+
# <%= series.name %>
|
|
22
|
+
# <%= series.value %>
|
|
23
|
+
# <% end %>
|
|
24
|
+
# <% end %>
|
|
25
|
+
# <% end %>
|
|
26
|
+
def series(**, &)
|
|
27
|
+
capture do
|
|
28
|
+
series_options.each_key.with_index do |key, index|
|
|
29
|
+
concat series_tag(key, index, **, &)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def series_tag(name, index, **, &)
|
|
37
|
+
tag.div(**) do
|
|
38
|
+
render EntryBuilder.new(name:, value: values[name], index:, series_options: series_options[name]), &
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
module GraphHelper
|
|
5
|
+
module Tooltips
|
|
6
|
+
class TooltipElement < Element # :nodoc:
|
|
7
|
+
attribute :inline_position, :'rcharts/percentage', default: Percentage::MIN
|
|
8
|
+
attribute :inline_size, :'rcharts/percentage', default: Percentage::MIN
|
|
9
|
+
attribute :inline_axis, :'rcharts/symbol'
|
|
10
|
+
attribute :index, :integer, default: 0
|
|
11
|
+
attribute :values_count, :integer, default: 0
|
|
12
|
+
|
|
13
|
+
def tags(&)
|
|
14
|
+
tag.g class: 'tooltip' do
|
|
15
|
+
hover_target_tag + marker_tag + tooltip_tag(&)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def tooltip_tag(&)
|
|
22
|
+
foreign_object_tag do
|
|
23
|
+
content_tag(&)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def foreign_object_tag(&)
|
|
28
|
+
render ForeignObjectElement.new(inline_position:, inline_axis:, index:, values_count:), &
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def content_tag(&)
|
|
32
|
+
tag.div class: 'tooltip-content', &
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def marker_tag
|
|
36
|
+
render MarkerElement.new(inline_position:, inline_axis:)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def hover_target_tag
|
|
40
|
+
render HoverTargetElement.new(inline_position:, inline_size:, inline_axis:)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|