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,249 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
# = \RCharts \Graph Helper
|
|
5
|
+
#
|
|
6
|
+
# The \graph helper is designed to make it easy to create graphs from hashes of data without having to write any SVGs.
|
|
7
|
+
#
|
|
8
|
+
# Creating a graph involves more bookkeeping than you might expect. The axes need to have nice round numbers which fit
|
|
9
|
+
# the data provided, the positions of lines, curves, and bars need to be scaled according to the minimum and maximum
|
|
10
|
+
# nice round numbers as determined, and for stacked bar charts and area charts you need to know how much space
|
|
11
|
+
# has already been used by previous series. All of this bookkeeping can be taken care of by using #graph_for, which
|
|
12
|
+
# takes a hash and instantiates a GraphHelper::GraphBuilder object. This builder object sets up abstract axes for
|
|
13
|
+
# positioning and provides methods to render different parts of the graph using the data in the hash. For example,
|
|
14
|
+
# given a team's favorite fruits, where the keys are fruits and the values are numbers of people,
|
|
15
|
+
# { 'Apples' => 1, 'Pears' => 8, 'Oranges' => 2, 'Bananas' => 10 }
|
|
16
|
+
# you can render a nice simple bar chart:
|
|
17
|
+
# <%= graph_for @team.favorite_fruits do |graph| %>
|
|
18
|
+
# <%= graph.rules :y %>
|
|
19
|
+
# <%= graph.rules :x, short: true %>
|
|
20
|
+
# <%= graph.axis :y do |value| %>
|
|
21
|
+
# <%= number_to_human value %>
|
|
22
|
+
# <% end %>
|
|
23
|
+
# <%= graph.axis :x do |value| %>
|
|
24
|
+
# <%= value.humanize %>
|
|
25
|
+
# <% end %>
|
|
26
|
+
# <%= graph.categories do |category| %>
|
|
27
|
+
# <%= category.bar %>
|
|
28
|
+
# <% end %>
|
|
29
|
+
# <% end %>
|
|
30
|
+
# This simple bar chart would yield the following (the hashes in the IDs will differ):
|
|
31
|
+
# <div class="rcharts-chart">
|
|
32
|
+
# <svg class="grid">
|
|
33
|
+
# <svg class="grid-rule-container">
|
|
34
|
+
# <line x1="0.0%" x2="100.0%" y1="100.0%" y2="100.0%" class="grid-rule"></line>
|
|
35
|
+
# </svg>
|
|
36
|
+
# <svg class="grid-rule-container">
|
|
37
|
+
# <line x1="0.0%" x2="100.0%" y1="88.88888888888889%" y2="88.88888888888889%" class="grid-rule"></line>
|
|
38
|
+
# </svg>
|
|
39
|
+
# <!-- ... -->
|
|
40
|
+
# <svg class="grid-rule-container">
|
|
41
|
+
# <line x1="0.0%" x2="100.0%" y1="11.111111111111114%" y2="11.111111111111114%" class="grid-rule"></line>
|
|
42
|
+
# </svg>
|
|
43
|
+
# <svg class="grid-rule-container">
|
|
44
|
+
# <line x1="0.0%" x2="100.0%" y1="0.0%" y2="0.0%" class="grid-rule"></line>
|
|
45
|
+
# </svg>
|
|
46
|
+
# </svg>
|
|
47
|
+
# <svg class="grid">
|
|
48
|
+
# <svg y="100%" class="grid-rule-container">
|
|
49
|
+
# <line x1="10.0%" x2="10.0%" y1="0.0%" y2="5.0" class="grid-rule"></line>
|
|
50
|
+
# </svg>
|
|
51
|
+
# <svg y="100%" class="grid-rule-container">
|
|
52
|
+
# <line x1="36.66666666666667%" x2="36.66666666666667%" y1="0.0%" y2="5.0" class="grid-rule"></line>
|
|
53
|
+
# </svg>
|
|
54
|
+
# <svg y="100%" class="grid-rule-container">
|
|
55
|
+
# <line x1="63.33333333333334%" x2="63.33333333333334%" y1="0.0%" y2="5.0" class="grid-rule"></line>
|
|
56
|
+
# </svg>
|
|
57
|
+
# <svg y="100%" class="grid-rule-container">
|
|
58
|
+
# <line x1="90.0%" x2="90.0%" y1="0.0%" y2="5.0" class="grid-rule"></line>
|
|
59
|
+
# </svg>
|
|
60
|
+
# </svg>
|
|
61
|
+
# <style></style>
|
|
62
|
+
# <div class="axis" data-name="y" data-index="0">
|
|
63
|
+
# <svg class="axis-ticks" width="2ch" data-axis="272261975180431324">
|
|
64
|
+
# <svg class="axis-tick" x="100.0%" y="100.0%">
|
|
65
|
+
# <text class="axis-tick-text" data-inline-axis="y" data-inline-axis-index="0"> 1 </text>
|
|
66
|
+
# </svg>
|
|
67
|
+
# <svg class="axis-tick" x="100.0%" y="88.88888888888889%">
|
|
68
|
+
# <text class="axis-tick-text" data-inline-axis="y" data-inline-axis-index="0"> 2 </text>
|
|
69
|
+
# </svg>
|
|
70
|
+
# <!-- ... -->
|
|
71
|
+
# <svg class="axis-tick" x="100.0%" y="11.111111111111114%">
|
|
72
|
+
# <text class="axis-tick-text" data-inline-axis="y" data-inline-axis-index="0"> 9 </text>
|
|
73
|
+
# </svg>
|
|
74
|
+
# <svg class="axis-tick" x="100.0%" y="0.0%">
|
|
75
|
+
# <text class="axis-tick-text" data-inline-axis="y" data-inline-axis-index="0"> 10 </text>
|
|
76
|
+
# </svg>
|
|
77
|
+
# </svg>
|
|
78
|
+
# </div>
|
|
79
|
+
# <style>
|
|
80
|
+
# /* ... */
|
|
81
|
+
# </style>
|
|
82
|
+
# <div class="axis" data-name="x" data-index="0">
|
|
83
|
+
# <svg class="axis-ticks" data-axis="1581349059956683109">
|
|
84
|
+
# <svg class="axis-tick" x="10.0%" y="0.0%">
|
|
85
|
+
# <text class="axis-tick-text" data-inline-axis="x" data-inline-axis-index="0"> Apples </text>
|
|
86
|
+
# </svg>
|
|
87
|
+
# <svg class="axis-tick" x="36.66666666666667%" y="0.0%">
|
|
88
|
+
# <text class="axis-tick-text" data-inline-axis="x" data-inline-axis-index="0"> Pears </text>
|
|
89
|
+
# </svg>
|
|
90
|
+
# <svg class="axis-tick" x="63.33333333333334%" y="0.0%">
|
|
91
|
+
# <text class="axis-tick-text" data-inline-axis="x" data-inline-axis-index="0"> Oranges </text>
|
|
92
|
+
# </svg>
|
|
93
|
+
# <svg class="axis-tick" x="90.0%" y="0.0%">
|
|
94
|
+
# <text class="axis-tick-text" data-inline-axis="x" data-inline-axis-index="0"> Bananas </text>
|
|
95
|
+
# </svg>
|
|
96
|
+
# </svg>
|
|
97
|
+
# </div>
|
|
98
|
+
# <svg class="category-container">
|
|
99
|
+
# <svg x="2.5%" width="15.0%" height="100.0%" class="category">
|
|
100
|
+
# <rect x="0.0%" y="100.0%" height="11.11111111111111%" width="100.0%" class="series-shape blue"></rect>
|
|
101
|
+
# </svg>
|
|
102
|
+
# <svg x="29.16666666666667%" width="15.0%" height="100.0%" class="category">
|
|
103
|
+
# <rect x="0.0%" y="22.22222222222223%" height="88.88888888888889%" width="100.0%" class="series-shape blue"></rect>
|
|
104
|
+
# </svg>
|
|
105
|
+
# <svg x="55.83333333333334%" width="15.0%" height="100.0%" class="category">
|
|
106
|
+
# <rect x="0.0%" y="88.88888888888889%" height="22.22222222222222%" width="100.0%" class="series-shape blue"></rect>
|
|
107
|
+
# </svg>
|
|
108
|
+
# <svg x="82.5%" width="15.0%" height="100.0%" class="category">
|
|
109
|
+
# <rect x="0.0%" y="0.0%" height="111.11111111111111%" width="100.0%" class="series-shape blue"></rect>
|
|
110
|
+
# </svg>
|
|
111
|
+
# </svg>
|
|
112
|
+
# </div>
|
|
113
|
+
# If you look at the structure of the rendered markup and the class names, you'll see that the order of the different
|
|
114
|
+
# parts of the graph corresponds to the order they were rendered in the <tt>graph_for</tt> block.
|
|
115
|
+
module GraphHelper
|
|
116
|
+
# Creates a graph from a hash.
|
|
117
|
+
#
|
|
118
|
+
# Suppose you have a book model with sales data as a hash, where the keys are the months and the values are
|
|
119
|
+
# the sales figures:
|
|
120
|
+
#
|
|
121
|
+
# <%= graph_for @book.sales do |graph| %>
|
|
122
|
+
# <%= graph.rules :y %>
|
|
123
|
+
# <%= graph.axis :y do |value| %>
|
|
124
|
+
# <%= number_to_rounded value %>
|
|
125
|
+
# <% end %>
|
|
126
|
+
# <%= graph.axis :x do |value| %>
|
|
127
|
+
# <%= value %>
|
|
128
|
+
# <% end %>
|
|
129
|
+
# <%= graph.series do |series| %>
|
|
130
|
+
# <%= series.line smooth: 0.16 %>
|
|
131
|
+
# <% end %>
|
|
132
|
+
# <% end %>
|
|
133
|
+
#
|
|
134
|
+
# The <tt>graph</tt> variable is an GraphHelper::GraphBuilder object which contains the hash of sales
|
|
135
|
+
# figures, and provides methods to render different parts of the graph using the data in the hash. For example,
|
|
136
|
+
# <%= graph.rules :y %>
|
|
137
|
+
# renders an <tt><svg></tt> tag with horizontal lines aligned with each tick on the Y-axis.
|
|
138
|
+
# The Y-axis itself hasn't been rendered yet, but the tick positioning information has been calculated and stored
|
|
139
|
+
# by the builder for reuse later. The following lines
|
|
140
|
+
# <%= graph.axis :y do |value| %>
|
|
141
|
+
# <%= number_with_delimiter value %>
|
|
142
|
+
# <% end %>
|
|
143
|
+
# then render the axis using the tick positions calculated previously. The block is passed <tt>value</tt>, which
|
|
144
|
+
# is the value associated with the tick. You can use a helper like <tt>number_with_delimiter</tt> to format this
|
|
145
|
+
# however you wish.
|
|
146
|
+
#
|
|
147
|
+
# To actually render the plot itself, you can use GraphHelper::GraphBuilder#series. This will iterate
|
|
148
|
+
# through all the series in the hash and for each one yield a GraphHelper::Series::SeriesBuilder object.
|
|
149
|
+
# <%= graph.series do |series| %>
|
|
150
|
+
# <%= series.line smooth: 0.16 %>
|
|
151
|
+
# <% end %>
|
|
152
|
+
# This example will render a line plot for every series (only one in this case) with smoothing applied.
|
|
153
|
+
#
|
|
154
|
+
# Bar charts work differently because the iteration is by category, not series:
|
|
155
|
+
# <%= graph.categories do |category| %>
|
|
156
|
+
# <%= category.bar %>
|
|
157
|
+
# <% end %>
|
|
158
|
+
# This will render a bar for every category (each of the months).
|
|
159
|
+
#
|
|
160
|
+
# For more information, see GraphHelper::GraphBuilder#series and GraphHelper::GraphBuilder#categories.
|
|
161
|
+
#
|
|
162
|
+
# === Providing data
|
|
163
|
+
#
|
|
164
|
+
# How does graph builder interpret the hash? The default assumption is that the hash keys are the categories,
|
|
165
|
+
# on the X-axis, and the values are the series data on the Y-axis. For example, if you have a hash like this:
|
|
166
|
+
# { 'January' => { predicted: 10, actual: 20 }, 'February' => { predicted: 20, actual: 30 } }
|
|
167
|
+
# then the categories will be <tt>['January', 'February']</tt> and the series will be
|
|
168
|
+
# <tt>[:predicted, :actual]</tt>. If you only have one series, then you don't need to specify it in a hash:
|
|
169
|
+
# { 'January' => 40, 'February' => 50 }
|
|
170
|
+
# In this case the single series is actually labelled <tt>nil</tt>, the default series name,
|
|
171
|
+
# so you don't need to specify it when rendering plots.
|
|
172
|
+
#
|
|
173
|
+
# In this case the hash keys are all strings, so the X-axis is treated as categorical, which means extra
|
|
174
|
+
# padding on a bar chart. \Axes can be continuous (values that can be interpolated),
|
|
175
|
+
# discrete (values that cannot be interpolated e.g. because they must be integers),
|
|
176
|
+
# or categorical (values that represent characteristics), and in this system categorical is treated as
|
|
177
|
+
# a subcase of discrete. So if the hash keys had been month numbers, for example, but you wanted them to be treated
|
|
178
|
+
# as categorical, then it would be necessary to specify the axis type as <tt>discrete: :categorical</tt>.
|
|
179
|
+
# <%= graph_for @book.sales, axis_options: { x: { discrete: :categorical } do |graph| %>
|
|
180
|
+
# If you wanted to treat them as discrete, then you would specify the axis type as <tt>discrete: true</tt>.
|
|
181
|
+
# <%= graph_for @book.sales, axis_options: { x: { discrete: true } do |graph| %>
|
|
182
|
+
#
|
|
183
|
+
# === Customizing the styling
|
|
184
|
+
#
|
|
185
|
+
# Running <tt>rails rcharts:install</tt> copies a stylesheet to <tt>app/assets/stylesheets/rcharts.css</tt>.
|
|
186
|
+
# At the beginning of the stylesheet are default values for a number of variables used later.
|
|
187
|
+
# /* app/assets/stylesheets/rcharts.css */
|
|
188
|
+
#
|
|
189
|
+
# :where(.rcharts-chart) {
|
|
190
|
+
# --red: #F44336;
|
|
191
|
+
# --blue: #2196F3;
|
|
192
|
+
# --green: #4CAF50;
|
|
193
|
+
# /* ... */
|
|
194
|
+
# }
|
|
195
|
+
# If you only want to override these values, you can add a rule to your application stylesheet with
|
|
196
|
+
# higher specificity. When updates are made to the installed stylesheet, you can replace your version with
|
|
197
|
+
# the new version, and your overrides will be preserved.
|
|
198
|
+
# /* app/assets/stylesheets/application.css */
|
|
199
|
+
#
|
|
200
|
+
# .rcharts-chart {
|
|
201
|
+
# --red: #FF0000;
|
|
202
|
+
# }
|
|
203
|
+
# For more radical overrides, you can change the installed stylesheet and integrate subsequent updates
|
|
204
|
+
# yourself.
|
|
205
|
+
#
|
|
206
|
+
# === Setting options
|
|
207
|
+
#
|
|
208
|
+
# You can set data attributes using the <tt>:data</tt> key, and other attributes using the <tt>:html</tt> key.
|
|
209
|
+
# You can also use the <tt>:builder</tt> key to specify your own subclass of GraphBuilder to use to render the
|
|
210
|
+
# chart. Other options are passed through to the builder, in particular <tt>:axis_options</tt> and
|
|
211
|
+
# <tt>:series_options</tt>.
|
|
212
|
+
#
|
|
213
|
+
# ==== Axis options
|
|
214
|
+
#
|
|
215
|
+
# The <tt>:axis_options</tt> key is used to specify options for the layout axes. For example, the following options
|
|
216
|
+
# could be used with a horizontal bar chart:
|
|
217
|
+
# <%= graph_for @book.sales,
|
|
218
|
+
# axis_options: { x: { minimum: 0, stacked: true, values_method: :values },
|
|
219
|
+
# y: { values_method: :keys } } do |graph| %>
|
|
220
|
+
# It might seem unintuitive to need to specify these options here as opposed to when rendering the axes. The reason
|
|
221
|
+
# is that the axes abstractly affect many aspects of the chart even if they aren't rendered: for example,
|
|
222
|
+
# if categories are positioned along the Y-axis, then bar charts will need to be rendered horizontally.
|
|
223
|
+
#
|
|
224
|
+
# The available options are:
|
|
225
|
+
# [<tt>:discrete</tt>] used to specify that the axis is continuous, using <tt>false</tt>,
|
|
226
|
+
# discrete, using <tt>true</tt>, or categorical, using <tt>:categorical</tt>. String values are
|
|
227
|
+
# interpreted as categorical.
|
|
228
|
+
# [<tt>:minimum</tt>] used to force the axis to assume a lower bound (otherwise the axis will use a rounded version
|
|
229
|
+
# of the lowest value)
|
|
230
|
+
# [<tt>:stacked</tt>] set this to <tt>true</tt> to stack series on top of each other, such as with bar charts and
|
|
231
|
+
# area charts
|
|
232
|
+
# [<tt>:values_method</tt>] used to specify a callable to generate the values for the axis. This is useful if you
|
|
233
|
+
# want to use a method other than <tt>keys</tt> and <tt>values</tt> to generate the
|
|
234
|
+
# X- and Y-axis values (the defaults)
|
|
235
|
+
# ==== \Series options
|
|
236
|
+
#
|
|
237
|
+
# The <tt>:series_options</tt> key is used to specify options for individual series, currently <tt>color_class</tt>
|
|
238
|
+
# and <tt>symbol</tt>.
|
|
239
|
+
# For example, you might want to render chart with a different color or symbol for one of the series:
|
|
240
|
+
# <%= graph_for @book.sales,
|
|
241
|
+
# series_options: { coffee_books: { color_class: 'mocha', symbol: '☕' } } do |graph| %>
|
|
242
|
+
# This will affect any lines, areas, markers, bars, legend entries, and tooltips which reference the series.
|
|
243
|
+
def graph_for(object, builder: GraphBuilder, data: {}, html: {}, **, &)
|
|
244
|
+
tag.div class: 'rcharts-chart', data:, **html do
|
|
245
|
+
render builder.new(graphable: object, **), &
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
class InstallGenerator < Rails::Generators::Base # :nodoc:
|
|
5
|
+
namespace :'rcharts:install'
|
|
6
|
+
|
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
|
8
|
+
|
|
9
|
+
def create_rcharts_files
|
|
10
|
+
template 'rcharts.css', 'app/assets/stylesheets/rcharts.css'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
@charset "UTF-8";
|
|
2
|
+
|
|
3
|
+
:where(.rcharts-chart) {
|
|
4
|
+
--red: #F44336;
|
|
5
|
+
--blue: #2196F3;
|
|
6
|
+
--green: #4CAF50;
|
|
7
|
+
--orange: #FF9800;
|
|
8
|
+
--purple: #9C27B0;
|
|
9
|
+
|
|
10
|
+
--base: #FFFFFF;
|
|
11
|
+
--base-2: #FAFAFA;
|
|
12
|
+
--base-3: #E0E0E0;
|
|
13
|
+
--foreground: #212121;
|
|
14
|
+
|
|
15
|
+
--gutter: 0.5rem;
|
|
16
|
+
|
|
17
|
+
--transition: all 0.125s ease-in-out;
|
|
18
|
+
--series-transition-duration: 0.5s;
|
|
19
|
+
|
|
20
|
+
--tooltip-border: 1px solid var(--base-3);
|
|
21
|
+
--tooltip-radius: 0.25rem;
|
|
22
|
+
--tooltip-shadow: 0 0 0.25rem var(--base-3);
|
|
23
|
+
|
|
24
|
+
--font-family: system-ui, sans-serif;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.rcharts-chart {
|
|
28
|
+
font-family: var(--font-family), sans-serif;
|
|
29
|
+
display: grid;
|
|
30
|
+
grid-template-columns: 1fr;
|
|
31
|
+
place-items: stretch;
|
|
32
|
+
|
|
33
|
+
svg {
|
|
34
|
+
position: relative;
|
|
35
|
+
display: block;
|
|
36
|
+
vertical-align: middle;
|
|
37
|
+
min-width: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.series-path {
|
|
41
|
+
&.red {
|
|
42
|
+
stroke: var(--red);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&.blue {
|
|
46
|
+
stroke: var(--blue);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&.green {
|
|
50
|
+
stroke: var(--green);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&.orange {
|
|
54
|
+
stroke: var(--orange);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&.purple {
|
|
58
|
+
stroke: var(--purple);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.series-shape {
|
|
63
|
+
&.red {
|
|
64
|
+
fill: var(--red);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&.blue {
|
|
68
|
+
fill: var(--blue);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
&.green {
|
|
72
|
+
fill: var(--green);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
&.orange {
|
|
76
|
+
fill: var(--orange);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&.purple {
|
|
80
|
+
fill: var(--purple);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.series-symbol {
|
|
85
|
+
font-family: sans-serif;
|
|
86
|
+
|
|
87
|
+
&.red {
|
|
88
|
+
color: var(--red);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&.blue {
|
|
92
|
+
color: var(--blue);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&.green {
|
|
96
|
+
color: var(--green);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
&.orange {
|
|
100
|
+
color: var(--orange);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
&.purple {
|
|
104
|
+
color: var(--purple);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.axis {
|
|
109
|
+
font-size: small;
|
|
110
|
+
overflow: visible;
|
|
111
|
+
display: grid;
|
|
112
|
+
gap: var(--gutter);
|
|
113
|
+
|
|
114
|
+
.axis-ticks {
|
|
115
|
+
overflow: visible;
|
|
116
|
+
transition: var(--transition);
|
|
117
|
+
|
|
118
|
+
.axis-tick {
|
|
119
|
+
overflow: visible;
|
|
120
|
+
|
|
121
|
+
.axis-tick-text {
|
|
122
|
+
fill: var(--foreground);
|
|
123
|
+
transition: var(--transition);
|
|
124
|
+
|
|
125
|
+
&[data-inline-axis='x'] {
|
|
126
|
+
dominant-baseline: hanging;
|
|
127
|
+
text-anchor: middle;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&[data-inline-axis='y'] {
|
|
131
|
+
dominant-baseline: middle;
|
|
132
|
+
|
|
133
|
+
&[data-inline-axis-index='0'] {
|
|
134
|
+
text-anchor: end;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
&[data-inline-axis-index='1'] {
|
|
138
|
+
text-anchor: start;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
&[data-name='x'] {
|
|
146
|
+
grid-column: 1;
|
|
147
|
+
container-type: inline-size;
|
|
148
|
+
|
|
149
|
+
.axis-ticks {
|
|
150
|
+
width: 100%;
|
|
151
|
+
height: 1lh;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
&[data-index='0'] {
|
|
155
|
+
grid-row-start: 2;
|
|
156
|
+
padding-block-start: var(--gutter);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
&[data-index='1'] {
|
|
160
|
+
grid-row-end: 1;
|
|
161
|
+
padding-block-end: var(--gutter);
|
|
162
|
+
|
|
163
|
+
.axis-ticks {
|
|
164
|
+
grid-row-start: 1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.axis-label {
|
|
168
|
+
grid-row-end: 1;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.axis-label-text {
|
|
175
|
+
fill: var(--foreground);
|
|
176
|
+
dominant-baseline: middle;
|
|
177
|
+
text-anchor: middle;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
&[data-name='y'] {
|
|
181
|
+
grid-row: 1;
|
|
182
|
+
|
|
183
|
+
> * {
|
|
184
|
+
grid-row: 1;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
> .axis-ticks {
|
|
188
|
+
height: 100%;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
&[data-index='0'] {
|
|
192
|
+
grid-column-end: 1;
|
|
193
|
+
padding-inline-end: var(--gutter);
|
|
194
|
+
|
|
195
|
+
> .axis-ticks {
|
|
196
|
+
grid-column-start: 1;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
> .axis-label {
|
|
200
|
+
grid-column-end: 1;
|
|
201
|
+
|
|
202
|
+
.axis-label-text {
|
|
203
|
+
rotate: -90deg;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
&[data-index='1'] {
|
|
209
|
+
grid-column-start: -1;
|
|
210
|
+
padding-inline-start: var(--gutter);
|
|
211
|
+
|
|
212
|
+
> .axis-ticks {
|
|
213
|
+
grid-column-end: 1;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
> .axis-label {
|
|
217
|
+
grid-column-start: 1;
|
|
218
|
+
|
|
219
|
+
.axis-label-text {
|
|
220
|
+
rotate: 90deg;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.grid {
|
|
229
|
+
grid-area: 1 / 1;
|
|
230
|
+
overflow: visible;
|
|
231
|
+
|
|
232
|
+
.grid-rule-container {
|
|
233
|
+
overflow: visible;
|
|
234
|
+
|
|
235
|
+
.grid-rule {
|
|
236
|
+
stroke-width: 1;
|
|
237
|
+
stroke: var(--base-3);
|
|
238
|
+
transition: var(--transition);
|
|
239
|
+
transition-duration: var(--series-transition-duration);
|
|
240
|
+
|
|
241
|
+
&.emphasis {
|
|
242
|
+
stroke: var(--foreground);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.legend {
|
|
249
|
+
font-size: small;
|
|
250
|
+
grid-column: 1;
|
|
251
|
+
display: flex;
|
|
252
|
+
flex-wrap: wrap;
|
|
253
|
+
place-content: center;
|
|
254
|
+
column-gap: 1rem;
|
|
255
|
+
row-gap: 0.5rem;
|
|
256
|
+
margin: 0;
|
|
257
|
+
padding: 0;
|
|
258
|
+
|
|
259
|
+
.legend-item {
|
|
260
|
+
display: inline;
|
|
261
|
+
white-space: nowrap;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
&[data-placement='top'], &[data-placement='bottom'] {
|
|
265
|
+
grid-column: 1;
|
|
266
|
+
flex-direction: row;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
&[data-placement='top'] {
|
|
270
|
+
grid-row-end: -2;
|
|
271
|
+
padding-block-end: var(--gutter);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
&[data-placement='bottom'] {
|
|
275
|
+
grid-row-start: 3;
|
|
276
|
+
padding-block-start: var(--gutter);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
&[data-placement='left'], &[data-placement='right'] {
|
|
280
|
+
grid-row: 1;
|
|
281
|
+
flex-direction: column;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
&[data-placement='left'] {
|
|
285
|
+
grid-column-end: -4;
|
|
286
|
+
grid-column-start: span 1;
|
|
287
|
+
padding-inline-end: var(--gutter);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
&[data-placement='right'] {
|
|
291
|
+
grid-column-start: 3;
|
|
292
|
+
padding-inline-start: var(--gutter);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.series-container {
|
|
298
|
+
overflow: visible;
|
|
299
|
+
grid-column: 1;
|
|
300
|
+
grid-row: 1;
|
|
301
|
+
|
|
302
|
+
.series {
|
|
303
|
+
overflow: visible;
|
|
304
|
+
|
|
305
|
+
.series-path {
|
|
306
|
+
fill: none;
|
|
307
|
+
vector-effect: non-scaling-stroke;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.series-path, .series-shape {
|
|
312
|
+
transition: var(--transition);
|
|
313
|
+
transition-duration: var(--series-transition-duration);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.series-path {
|
|
317
|
+
stroke-width: 3.5pt;
|
|
318
|
+
|
|
319
|
+
&:is(path) {
|
|
320
|
+
stroke-linecap: square;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.category-container {
|
|
326
|
+
grid-column-start: 1;
|
|
327
|
+
grid-row: 1;
|
|
328
|
+
|
|
329
|
+
.series-shape {
|
|
330
|
+
transition: var(--transition);
|
|
331
|
+
transition-duration: var(--series-transition-duration);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.tooltips {
|
|
336
|
+
overflow: visible;
|
|
337
|
+
grid-column: 1;
|
|
338
|
+
grid-row: 1;
|
|
339
|
+
|
|
340
|
+
.tooltip {
|
|
341
|
+
.tooltip-hover-target {
|
|
342
|
+
fill: transparent;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.tooltip-marker {
|
|
346
|
+
stroke-dasharray: 4;
|
|
347
|
+
stroke: var(--base-3);
|
|
348
|
+
opacity: 0;
|
|
349
|
+
|
|
350
|
+
.tooltip:hover & {
|
|
351
|
+
opacity: 1;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.tooltip-object {
|
|
356
|
+
pointer-events: none;
|
|
357
|
+
overflow: visible;
|
|
358
|
+
display: flex;
|
|
359
|
+
flex-direction: column;
|
|
360
|
+
align-items: start;
|
|
361
|
+
|
|
362
|
+
&:has(.anchor-end) {
|
|
363
|
+
align-content: end;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.tooltip-container {
|
|
368
|
+
padding: 1rem;
|
|
369
|
+
display: flex;
|
|
370
|
+
flex-direction: row;
|
|
371
|
+
|
|
372
|
+
&.justify-end {
|
|
373
|
+
justify-items: end;
|
|
374
|
+
justify-content: end;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.tooltip-content {
|
|
379
|
+
opacity: 0;
|
|
380
|
+
background: var(--base);
|
|
381
|
+
border: var(--tooltip-border);
|
|
382
|
+
border-radius: var(--tooltip-radius);
|
|
383
|
+
font: var(--foreground);
|
|
384
|
+
box-shadow: var(--tooltip-shadow);
|
|
385
|
+
|
|
386
|
+
.tooltip:hover & {
|
|
387
|
+
opacity: 1;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RCharts
|
|
4
|
+
class Engine < ::Rails::Engine # :nodoc:
|
|
5
|
+
isolate_namespace RCharts
|
|
6
|
+
|
|
7
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
|
8
|
+
inflect.acronym 'RCharts'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
config.rcharts = ActiveSupport::OrderedOptions.new
|
|
12
|
+
config.rcharts.series_color_classes = %w[blue red green orange purple]
|
|
13
|
+
config.rcharts.series_symbols = %w[● ■ ◆ ▲ ▼]
|
|
14
|
+
|
|
15
|
+
initializer 'rcharts.configure' do |app|
|
|
16
|
+
RCharts.series_color_classes = app.config.rcharts.series_color_classes
|
|
17
|
+
RCharts.series_symbols = app.config.rcharts.series_symbols
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
initializer 'rcharts.types' do
|
|
21
|
+
ActiveModel::Type.register :'rcharts/percentage', RCharts::Type::Percentage
|
|
22
|
+
ActiveModel::Type.register :'rcharts/symbol', RCharts::Type::Symbol
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|