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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +99 -0
  4. data/Rakefile +21 -0
  5. data/app/helpers/rcharts/graph_helper/axes/axis_element/styles.rb +91 -0
  6. data/app/helpers/rcharts/graph_helper/axes/axis_element.rb +89 -0
  7. data/app/helpers/rcharts/graph_helper/axes/label_element.rb +32 -0
  8. data/app/helpers/rcharts/graph_helper/axes/tick_element.rb +38 -0
  9. data/app/helpers/rcharts/graph_helper/axes.rb +8 -0
  10. data/app/helpers/rcharts/graph_helper/categories/bar_builder.rb +32 -0
  11. data/app/helpers/rcharts/graph_helper/categories/bar_segment_element.rb +49 -0
  12. data/app/helpers/rcharts/graph_helper/categories/bars_element.rb +52 -0
  13. data/app/helpers/rcharts/graph_helper/categories/category_builder.rb +115 -0
  14. data/app/helpers/rcharts/graph_helper/element.rb +65 -0
  15. data/app/helpers/rcharts/graph_helper/element_builder.rb +42 -0
  16. data/app/helpers/rcharts/graph_helper/graph/axes.rb +41 -0
  17. data/app/helpers/rcharts/graph_helper/graph/axis/caster.rb +45 -0
  18. data/app/helpers/rcharts/graph_helper/graph/axis/positioning.rb +66 -0
  19. data/app/helpers/rcharts/graph_helper/graph/axis/ticks.rb +66 -0
  20. data/app/helpers/rcharts/graph_helper/graph/axis.rb +86 -0
  21. data/app/helpers/rcharts/graph_helper/graph/calculator.rb +91 -0
  22. data/app/helpers/rcharts/graph_helper/graph/composition.rb +35 -0
  23. data/app/helpers/rcharts/graph_helper/graph/options.rb +33 -0
  24. data/app/helpers/rcharts/graph_helper/graph.rb +8 -0
  25. data/app/helpers/rcharts/graph_helper/graph_builder.rb +270 -0
  26. data/app/helpers/rcharts/graph_helper/legend_entry_builder.rb +46 -0
  27. data/app/helpers/rcharts/graph_helper/rule_element.rb +68 -0
  28. data/app/helpers/rcharts/graph_helper/series/area_element.rb +50 -0
  29. data/app/helpers/rcharts/graph_helper/series/path.rb +153 -0
  30. data/app/helpers/rcharts/graph_helper/series/path_element.rb +72 -0
  31. data/app/helpers/rcharts/graph_helper/series/point.rb +58 -0
  32. data/app/helpers/rcharts/graph_helper/series/scatter_element.rb +87 -0
  33. data/app/helpers/rcharts/graph_helper/series/series_builder.rb +145 -0
  34. data/app/helpers/rcharts/graph_helper/tooltips/entry_builder.rb +47 -0
  35. data/app/helpers/rcharts/graph_helper/tooltips/foreign_object_element.rb +84 -0
  36. data/app/helpers/rcharts/graph_helper/tooltips/hover_target_element.rb +39 -0
  37. data/app/helpers/rcharts/graph_helper/tooltips/marker_element.rb +38 -0
  38. data/app/helpers/rcharts/graph_helper/tooltips/tooltip_builder.rb +44 -0
  39. data/app/helpers/rcharts/graph_helper/tooltips/tooltip_element.rb +45 -0
  40. data/app/helpers/rcharts/graph_helper.rb +249 -0
  41. data/lib/generators/rcharts/install/install_generator.rb +13 -0
  42. data/lib/generators/rcharts/install/templates/rcharts.css +392 -0
  43. data/lib/rcharts/engine.rb +25 -0
  44. data/lib/rcharts/percentage.rb +36 -0
  45. data/lib/rcharts/type/percentage.rb +20 -0
  46. data/lib/rcharts/type/symbol.rb +29 -0
  47. data/lib/rcharts/type.rb +9 -0
  48. data/lib/rcharts/version.rb +6 -0
  49. data/lib/rcharts.rb +52 -0
  50. data/lib/tasks/rcharts_tasks.rake +6 -0
  51. 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