active_analytics 0.2.0 → 0.3.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 +4 -4
- data/README.md +105 -15
- data/app/controllers/active_analytics/application_controller.rb +24 -4
- data/app/controllers/active_analytics/assets_controller.rb +29 -0
- data/app/controllers/active_analytics/pages_controller.rb +9 -10
- data/app/controllers/active_analytics/referrers_controller.rb +9 -5
- data/app/controllers/active_analytics/sites_controller.rb +4 -4
- data/app/models/active_analytics/views_per_day.rb +22 -3
- data/app/views/active_analytics/assets/_charts.css +249 -0
- data/app/{assets/stylesheets/active_analytics/style.css → views/active_analytics/assets/_style.css} +37 -38
- data/app/views/active_analytics/assets/application.css.erb +2 -0
- data/app/{assets/javascripts/active_analytics → views/active_analytics/assets}/application.js +1 -3
- data/app/views/active_analytics/assets/ariato.css +875 -0
- data/app/views/active_analytics/assets/ariato.js +322 -0
- data/app/views/active_analytics/pages/_table.html.erb +6 -17
- data/app/views/active_analytics/pages/index.html.erb +1 -1
- data/app/views/active_analytics/pages/show.html.erb +7 -8
- data/app/views/active_analytics/referrers/_table.html.erb +9 -2
- data/app/views/active_analytics/referrers/index.html.erb +2 -2
- data/app/views/active_analytics/referrers/show.html.erb +6 -3
- data/app/views/active_analytics/sites/_histogram.html.erb +9 -2
- data/app/views/active_analytics/sites/_histogram_header.html.erb +10 -0
- data/app/views/active_analytics/sites/show.html.erb +2 -2
- data/app/views/layouts/active_analytics/_footer.html.erb +3 -3
- data/app/views/layouts/active_analytics/_header.html.erb +1 -3
- data/app/views/layouts/active_analytics/application.html.erb +5 -3
- data/config/routes.rb +2 -1
- data/lib/active_analytics/version.rb +1 -1
- data/lib/active_analytics.rb +47 -4
- metadata +15 -11
- data/app/assets/javascripts/active_analytics/ariato.js +0 -746
- data/app/assets/stylesheets/active_analytics/application.css +0 -15
- data/app/assets/stylesheets/active_analytics/ariato.css +0 -3548
- data/app/assets/stylesheets/active_analytics/charts.css +0 -424
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 909ce5d6ab8a28322be42f01b2d37c752ef810a31a139b7f5e035257d1c62fda
|
4
|
+
data.tar.gz: 1ca79cce7bbe415afa9341e752400891d6817e780a0b679fadcfeb1df6039786
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1835fa3cb335651ef42c46767b13da30829fb86a421f06989c203814c4b9199d784f714e4bf6885a4301befb4dae9fd1bad94b47fa660f12bd2425de4c713b51
|
7
|
+
data.tar.gz: 19fa332fbbdbc9be99e99b9a635bc57df3e2a32c4b9c0a12157c2e44446d3417eec0d702790c2a05b803858bafc50090d31ba787d5bb5e7cd52c87a4795c7e99
|
data/README.md
CHANGED
@@ -32,43 +32,133 @@ rails active_analytics:install:migrations
|
|
32
32
|
rails db:migrate
|
33
33
|
```
|
34
34
|
|
35
|
-
|
35
|
+
Add the route to ActiveAnalytics dashboard at the desired endpoint:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# config/routes.rb
|
39
|
+
mount ActiveAnalytics::Engine, at: "analytics" # http://localhost:3000/analytics
|
40
|
+
```
|
41
|
+
|
42
|
+
The next step is to collect trafic and there is 2 options.
|
43
|
+
|
44
|
+
### Record requests synchronously
|
45
|
+
|
46
|
+
This is the easiest way to start with.
|
47
|
+
However it's less performant since it triggers a write into your database for each request.
|
48
|
+
Your controllers have to call `ActiveAnalytics.record_request(request)` to record page views.
|
49
|
+
The Rails way to achieve is to use `after_action` :
|
50
|
+
|
36
51
|
```ruby
|
37
52
|
class ApplicationController < ActionController::Base
|
38
|
-
|
53
|
+
after_action :record_page_view
|
39
54
|
|
40
55
|
def record_page_view
|
41
|
-
#
|
42
|
-
#
|
43
|
-
|
56
|
+
# This is a basic example, you might need to customize some conditions.
|
57
|
+
# For most sites, it makes no sense to record anything other than HTML.
|
58
|
+
if response.content_type && response.content_type.start_with?("text/html")
|
59
|
+
# Add a condition to record only your canonical domain
|
60
|
+
# and use a gem such as crawler_detect to skip bots.
|
61
|
+
ActiveAnalytics.record_request(request)
|
62
|
+
end
|
44
63
|
end
|
45
64
|
end
|
46
65
|
```
|
47
66
|
|
48
|
-
|
67
|
+
In case you don't want to record all page views, because each application has sensitive URLs such as password reset and so on, simply define a `skip_after_action :record_page_view` in the relevant controller.
|
68
|
+
|
69
|
+
### Queue requests asynchronously
|
70
|
+
|
71
|
+
It requires more work and it's relevant if your application handle a large trafic.
|
72
|
+
The idea is to queue data into Redis because it does not require the database writing to the disk on each request.
|
73
|
+
First you have to set the Redis URL or connection.
|
49
74
|
|
50
|
-
Finally, just add the route to ActiveAnalytics dashboard at the desired endpoint:
|
51
75
|
```ruby
|
52
|
-
|
76
|
+
# File lib/patches/active_analytics.rb or config/initializers/active_analytics.rb
|
77
|
+
|
78
|
+
ActiveAnalytics.redis_url = "redis://user:password@host/1" # Default ENV["ACTIVE_ANALYTICS_REDIS_URL"] || ENV["REDIS_URL"] || "redis://localhost"
|
79
|
+
|
80
|
+
# If you use special connection options you have to instantiate it yourself
|
81
|
+
ActiveAnalytics.redis = Redis.new(
|
82
|
+
url: ENV["REDIS_URL"],
|
83
|
+
reconnect_attempts: 10,
|
84
|
+
ssl_params: {verify_mode: OpenSSL::SSL::VERIFY_NONE}
|
85
|
+
)
|
53
86
|
```
|
54
87
|
|
88
|
+
Then your controllers have to call `ActiveAnalytics.queue_request(request)` to queue page views.
|
89
|
+
The Rails way to achieve is to use `after_action` :
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
class ApplicationController < ActionController::Base
|
93
|
+
after_action :queue_page_view
|
94
|
+
|
95
|
+
def queue_page_view
|
96
|
+
# This is a basic example, you might need to customize some conditions.
|
97
|
+
# For most sites, it makes no sense to record anything other than HTML.
|
98
|
+
if response.content_type && response.content_type.start_with?("text/html")
|
99
|
+
# Add a condition to record only your canonical domain
|
100
|
+
# and use a gem such as crawler_detect to skip bots.
|
101
|
+
ActiveAnalytics.queue_request(request)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
Queued data need to be saved into the database in order to be viewable in the ActiveAnalytics dashboard.
|
108
|
+
For that, call `ActiveAnalytics.flush_queue` from a cron task or a background job.
|
109
|
+
|
110
|
+
It's up to you if you want to flush the queue every hour or every 10 minutes.
|
111
|
+
I advise to execute the last flush of the day at 23:59.
|
112
|
+
It prevents from shifting the trafic to the next day.
|
113
|
+
In that case only the last minute will be shifted to the next day, even if the flush ends after midnight.
|
114
|
+
This small imperfection allows a simpler implementation for now.
|
115
|
+
Keep it simple !
|
116
|
+
|
117
|
+
|
55
118
|
## Authentication and permissions
|
56
|
-
|
119
|
+
|
120
|
+
ActiveAnalytics cannot guess how you handle user authentication, because it is different for all Rails applications.
|
121
|
+
So you have to monkey patch `ActiveAnalytics::ApplicationController` in order to inject your own mechanism.
|
122
|
+
The patch can be saved wherever you want.
|
123
|
+
For example, I like to have all the patches in one place, so I put them in `lib/patches`.
|
57
124
|
|
58
125
|
```ruby
|
59
|
-
|
126
|
+
# lib/patches/active_analytics.rb
|
127
|
+
|
128
|
+
ActiveAnalytics::ApplicationController.class_eval do
|
129
|
+
before_action :require_admin
|
60
130
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
131
|
+
def require_admin
|
132
|
+
# This example supposes there are current_user and User#admin? methods
|
133
|
+
raise ActionController::RoutingError.new("Not found") unless current_user.try(:admin?)
|
134
|
+
end
|
65
135
|
end
|
66
136
|
end
|
67
137
|
```
|
68
138
|
|
139
|
+
Then you have to require the monkey patch.
|
140
|
+
Because it's loaded via require, it won't be reloaded in development.
|
141
|
+
Since you are not supposed to change this file often, it should not be an issue.
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
# config/application.rb
|
145
|
+
config.after_initialize do
|
146
|
+
require "patches/active_analytics"
|
147
|
+
end
|
148
|
+
```
|
149
|
+
|
150
|
+
If you use Devise, you can check the permission directly from routes.rb :
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
# config/routes.rb
|
154
|
+
authenticate :user, -> (u) { u.admin? } do # Supposing there is a User#admin? method
|
155
|
+
mount ActiveAnalytics::Engine, at: "analytics" # http://localhost:3000/analytics
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
69
159
|
## License
|
70
160
|
The gem is available as open-source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
71
161
|
|
72
162
|
Made by [Base Secrète](https://basesecrete.com).
|
73
163
|
|
74
|
-
Rails developer? Check out [RoRvsWild](https://rorvswild.com), our Ruby on Rails application monitoring tool.
|
164
|
+
Rails developer? Check out [RoRvsWild](https://rorvswild.com), our Ruby on Rails application monitoring tool.
|
@@ -3,14 +3,34 @@ module ActiveAnalytics
|
|
3
3
|
|
4
4
|
private
|
5
5
|
|
6
|
+
def from_date
|
7
|
+
@from_date ||= Date.parse(params[:from])
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_date
|
11
|
+
@to_date ||= Date.parse(params[:to])
|
12
|
+
end
|
13
|
+
|
6
14
|
def require_date_range
|
7
|
-
|
8
|
-
|
9
|
-
|
15
|
+
redirect_to(params.to_unsafe_hash.merge(from: to_date, to: from_date)) if from_date > to_date
|
16
|
+
rescue TypeError, ArgumentError # Raised by Date.parse when invalid format
|
17
|
+
redirect_to(params.to_unsafe_hash.merge(from: 7.days.ago.to_date, to: Date.today))
|
10
18
|
end
|
11
19
|
|
12
20
|
def current_views_per_days
|
13
|
-
ViewsPerDay.where(site: params[:site]).between_dates(
|
21
|
+
ViewsPerDay.where(site: params[:site]).between_dates(from_date, to_date)
|
22
|
+
end
|
23
|
+
|
24
|
+
def previous_views_per_days
|
25
|
+
ViewsPerDay.where(site: params[:site]).between_dates(previous_from_date, previous_to_date)
|
26
|
+
end
|
27
|
+
|
28
|
+
def previous_from_date
|
29
|
+
from_date - (to_date - from_date)
|
30
|
+
end
|
31
|
+
|
32
|
+
def previous_to_date
|
33
|
+
to_date - (to_date - from_date)
|
14
34
|
end
|
15
35
|
end
|
16
36
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_dependency "active_analytics/application_controller"
|
2
|
+
|
3
|
+
module ActiveAnalytics
|
4
|
+
class AssetsController < ApplicationController
|
5
|
+
protect_from_forgery except: :show
|
6
|
+
|
7
|
+
def show
|
8
|
+
if endpoints.include?(File.basename(request.path))
|
9
|
+
expires_in(1.day, public: true)
|
10
|
+
render(params[:id], mime_type: mime_type)
|
11
|
+
else
|
12
|
+
raise ActionController::RoutingError.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def endpoints
|
19
|
+
return @endpoints if @endpoints
|
20
|
+
folder = ActiveAnalytics::Engine.root.join("app/views", controller_path)
|
21
|
+
files = folder.each_child.map { |path| File.basename(path).delete_suffix(".erb") }
|
22
|
+
@endpoints = files.delete_if { |str| str.start_with?("_") }
|
23
|
+
end
|
24
|
+
|
25
|
+
def mime_type
|
26
|
+
Mime::Type.lookup_by_extension(File.extname(request.path).delete_prefix("."))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -7,19 +7,18 @@ module ActiveAnalytics
|
|
7
7
|
before_action :require_date_range
|
8
8
|
|
9
9
|
def index
|
10
|
-
|
11
|
-
@
|
12
|
-
@pages =
|
10
|
+
@histogram = ViewsPerDay::Histogram.new(current_views_per_days.order_by_date.group_by_date, from_date, to_date)
|
11
|
+
@previous_histogram = ViewsPerDay::Histogram.new(previous_views_per_days.order_by_date.group_by_date, previous_from_date, previous_to_date)
|
12
|
+
@pages = current_views_per_days.top(100).group_by_page
|
13
13
|
end
|
14
14
|
|
15
15
|
def show
|
16
|
-
|
17
|
-
|
18
|
-
@histogram = ViewsPerDay::Histogram.new(page_scope.order_by_date.group_by_date,
|
19
|
-
@
|
20
|
-
|
21
|
-
@
|
22
|
-
@previous_pages = dates_scopes.where(site: params[:site], page: page_from_params).where.not(referrer_path: nil).top(100).group_by_referrer_page
|
16
|
+
page_scope = current_views_per_days.where(page: page_from_params)
|
17
|
+
previous_page_scope = previous_views_per_days.where(page: page_from_params)
|
18
|
+
@histogram = ViewsPerDay::Histogram.new(page_scope.order_by_date.group_by_date, from_date, to_date)
|
19
|
+
@previous_histogram = ViewsPerDay::Histogram.new(previous_page_scope.order_by_date.group_by_date, previous_from_date, previous_to_date)
|
20
|
+
@next_pages = current_views_per_days.where(referrer_host: params[:site], referrer_path: page_from_params).top(100).group_by_page
|
21
|
+
@previous_pages = page_scope.top(100).group_by_referrer_page
|
23
22
|
end
|
24
23
|
end
|
25
24
|
end
|
@@ -5,14 +5,18 @@ module ActiveAnalytics
|
|
5
5
|
before_action :require_date_range
|
6
6
|
|
7
7
|
def index
|
8
|
-
|
9
|
-
@
|
10
|
-
@
|
8
|
+
@referrers = current_views_per_days.top(100).group_by_referrer_site
|
9
|
+
@histogram = ViewsPerDay::Histogram.new(current_views_per_days.order_by_date.group_by_date, from_date, to_date)
|
10
|
+
@previous_histogram = ViewsPerDay::Histogram.new(previous_views_per_days.order_by_date.group_by_date, previous_from_date, previous_to_date)
|
11
11
|
end
|
12
12
|
|
13
13
|
def show
|
14
|
-
|
15
|
-
|
14
|
+
referrer_host, referrer_path = params[:referrer].split("/", 2)
|
15
|
+
scope = current_views_per_days.where(referrer_host: referrer_host)
|
16
|
+
scope = scope.where(referrer_path: "/" + referrer_path) if referrer_path.present?
|
17
|
+
previous_scope = previous_views_per_days.where(referrer_host: params[:referrer])
|
18
|
+
@histogram = ViewsPerDay::Histogram.new(scope.order_by_date.group_by_date, from_date, to_date)
|
19
|
+
@previous_histogram = ViewsPerDay::Histogram.new(previous_scope.order_by_date.group_by_date, previous_from_date, previous_to_date)
|
16
20
|
@previous_pages = scope.top(100).group_by_referrer_page
|
17
21
|
@next_pages = scope.top(100).group_by_page
|
18
22
|
end
|
@@ -10,10 +10,10 @@ module ActiveAnalytics
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def show
|
13
|
-
|
14
|
-
@
|
15
|
-
@referrers =
|
16
|
-
@pages =
|
13
|
+
@histogram = ViewsPerDay::Histogram.new(current_views_per_days.order_by_date.group_by_date, from_date, to_date)
|
14
|
+
@previous_histogram = ViewsPerDay::Histogram.new(previous_views_per_days.order_by_date.group_by_date, previous_from_date, previous_to_date)
|
15
|
+
@referrers = current_views_per_days.top.group_by_referrer_site
|
16
|
+
@pages = current_views_per_days.top.group_by_page
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -37,8 +37,10 @@ module ActiveAnalytics
|
|
37
37
|
attr_reader :bars, :from_date, :to_date
|
38
38
|
|
39
39
|
def initialize(scope, from_date, to_date)
|
40
|
+
@scope = scope
|
41
|
+
@from_date, @to_date = from_date, to_date
|
40
42
|
@bars = scope.map { |day| Bar.new(day.day, day.total, self) }
|
41
|
-
fill_missing_days(@bars,
|
43
|
+
fill_missing_days(@bars, @from_date, @to_date)
|
42
44
|
end
|
43
45
|
|
44
46
|
def fill_missing_days(bars, from, to)
|
@@ -68,7 +70,11 @@ module ActiveAnalytics
|
|
68
70
|
end
|
69
71
|
|
70
72
|
def height
|
71
|
-
|
73
|
+
if histogram.max_value > 0
|
74
|
+
(value.to_f / histogram.max_value).round(2)
|
75
|
+
else
|
76
|
+
0
|
77
|
+
end
|
72
78
|
end
|
73
79
|
end
|
74
80
|
end
|
@@ -108,12 +114,25 @@ module ActiveAnalytics
|
|
108
114
|
end
|
109
115
|
|
110
116
|
def self.append(params)
|
117
|
+
total = params.delete(:total) || 1
|
111
118
|
params[:site] = params[:site].downcase if params[:site]
|
112
119
|
params[:page] = params[:page].downcase if params[:page]
|
113
120
|
params[:referrer_path] = nil if params[:referrer_path].blank?
|
114
121
|
params[:referrer_path] = params[:referrer_path].downcase if params[:referrer_path]
|
115
122
|
params[:referrer_host] = params[:referrer_host].downcase if params[:referrer_host]
|
116
|
-
|
123
|
+
where(params).first.try(:increment!, :total, total) || create!(params.merge(total: total))
|
124
|
+
end
|
125
|
+
|
126
|
+
SLASH = "/"
|
127
|
+
|
128
|
+
def self.split_referrer(referrer)
|
129
|
+
return [nil, nil] if referrer.blank?
|
130
|
+
if (uri = URI(referrer)).host.present?
|
131
|
+
[uri.host, uri.path.presence]
|
132
|
+
else
|
133
|
+
strings = referrer.split(SLASH, 2)
|
134
|
+
[strings[0], strings[1] ? SLASH + strings[1] : nil]
|
135
|
+
end
|
117
136
|
end
|
118
137
|
end
|
119
138
|
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
/*
|
2
|
+
* Charts.css v0.9.0 (https://ChartsCSS.org/)
|
3
|
+
* Copyright 2020 Rami Yushuvaev
|
4
|
+
* Licensed under MIT
|
5
|
+
*/
|
6
|
+
.active-analytics .charts-css {
|
7
|
+
--chart-bg-color: transparent;
|
8
|
+
--heading-size: 0px;
|
9
|
+
--primary-axis-color: rgba(var(--color-grey-100), 1);
|
10
|
+
--primary-axis-style: solid;
|
11
|
+
--primary-axis-width: 1px;
|
12
|
+
--secondary-axes-color: rgba(var(--color-grey-50), 1);
|
13
|
+
--secondary-axes-style: solid;
|
14
|
+
--secondary-axes-width: 1px;
|
15
|
+
--data-axes-color: rgba(var(--color-grey-200), 1);
|
16
|
+
--data-axes-style: solid;
|
17
|
+
--data-axes-width: 1px;
|
18
|
+
--legend-border-color: rgba(var(--color-grey-200), 1);
|
19
|
+
position: relative;
|
20
|
+
display: block;
|
21
|
+
margin: 0;
|
22
|
+
padding: 0;
|
23
|
+
border: 0;
|
24
|
+
}
|
25
|
+
|
26
|
+
/*
|
27
|
+
* Chart wrapper element
|
28
|
+
*/
|
29
|
+
|
30
|
+
.active-analytics .charts-css,
|
31
|
+
.active-analytics .charts-css::after,
|
32
|
+
.active-analytics .charts-css::before,
|
33
|
+
.active-analytics .charts-css *,
|
34
|
+
.active-analytics .charts-css *::after,
|
35
|
+
.active-analytics .charts-css *::before {
|
36
|
+
box-sizing: border-box;
|
37
|
+
}
|
38
|
+
|
39
|
+
/*
|
40
|
+
* Reset table element
|
41
|
+
*/
|
42
|
+
.active-analytics table.charts-css {
|
43
|
+
border-collapse: collapse;
|
44
|
+
border-spacing: 0;
|
45
|
+
empty-cells: show;
|
46
|
+
overflow: initial;
|
47
|
+
background-color: transparent;
|
48
|
+
}
|
49
|
+
|
50
|
+
.active-analytics table.charts-css caption,
|
51
|
+
.active-analytics table.charts-css colgroup,
|
52
|
+
.active-analytics table.charts-css thead,
|
53
|
+
.active-analytics table.charts-css tbody,
|
54
|
+
.active-analytics table.charts-css tr,
|
55
|
+
.active-analytics table.charts-css th,
|
56
|
+
.active-analytics table.charts-css td {
|
57
|
+
display: block;
|
58
|
+
margin: 0;
|
59
|
+
padding: 0;
|
60
|
+
border: 0;
|
61
|
+
background-color: transparent;
|
62
|
+
}
|
63
|
+
|
64
|
+
.active-analytics table.charts-css colgroup,
|
65
|
+
.active-analytics table.charts-css thead,
|
66
|
+
.active-analytics table.charts-css tfoot {
|
67
|
+
display: none;
|
68
|
+
}
|
69
|
+
|
70
|
+
|
71
|
+
/*
|
72
|
+
* Chart colors
|
73
|
+
*/
|
74
|
+
|
75
|
+
.active-analytics .charts-css.column tbody tr td {
|
76
|
+
background: rgba(var(--color-grey-100), 1);
|
77
|
+
padding: 0;
|
78
|
+
}
|
79
|
+
|
80
|
+
/*
|
81
|
+
* Chart data
|
82
|
+
*/
|
83
|
+
.active-analytics .charts-css.hide-data .data {
|
84
|
+
opacity: 0;
|
85
|
+
}
|
86
|
+
|
87
|
+
.active-analytics .charts-css.show-data-on-hover .data {
|
88
|
+
transition-duration: .3s;
|
89
|
+
opacity: 0;
|
90
|
+
}
|
91
|
+
|
92
|
+
.active-analytics .charts-css.show-data-on-hover tr:hover .data {
|
93
|
+
transition-duration: .3s;
|
94
|
+
opacity: 1;
|
95
|
+
}
|
96
|
+
|
97
|
+
/*
|
98
|
+
* Chart labels
|
99
|
+
*/
|
100
|
+
|
101
|
+
.active-analytics .charts-css.column:not(.show-labels) {
|
102
|
+
--labels-size: 0;
|
103
|
+
}
|
104
|
+
|
105
|
+
.active-analytics .charts-css.column:not(.show-labels) tbody tr th {
|
106
|
+
display: none;
|
107
|
+
}
|
108
|
+
|
109
|
+
.active-analytics .charts-css.column.show-labels {
|
110
|
+
--labels-size: 1.5rem;
|
111
|
+
}
|
112
|
+
|
113
|
+
.active-analytics .charts-css.column.show-labels tbody tr th {
|
114
|
+
display: flex;
|
115
|
+
justify-content: var(--labels-align, center);
|
116
|
+
align-items: center;
|
117
|
+
flex-direction: column;
|
118
|
+
}
|
119
|
+
|
120
|
+
@media (max-width: 600px) {
|
121
|
+
.active-analytics .charts-css.column.show-labels {
|
122
|
+
--labels-size: 0;
|
123
|
+
}
|
124
|
+
|
125
|
+
.active-analytics .charts-css.column.show-labels tbody tr th {
|
126
|
+
display: none;
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
/*
|
131
|
+
* Chart axes
|
132
|
+
*/
|
133
|
+
.active-analytics .charts-css.column.show-primary-axis:not(.reverse) tbody tr {
|
134
|
+
border-block-end: var(--primary-axis-width) var(--primary-axis-style) var(--primary-axis-color);
|
135
|
+
}
|
136
|
+
|
137
|
+
.active-analytics .charts-css.column.show-primary-axis.reverse tbody tr {
|
138
|
+
border-block-start: var(--primary-axis-width) var(--primary-axis-style) var(--primary-axis-color);
|
139
|
+
}
|
140
|
+
|
141
|
+
.active-analytics .charts-css.column.show-5-secondary-axes:not(.reverse) tbody tr {
|
142
|
+
background-size: 100% 20%;
|
143
|
+
background-image: linear-gradient(var(--secondary-axes-color) var(--secondary-axes-width), transparent var(--secondary-axes-width));
|
144
|
+
}
|
145
|
+
|
146
|
+
.active-analytics .charts-css.column.show-5-secondary-axes.reverse tbody tr {
|
147
|
+
background-size: 100% 20%;
|
148
|
+
background-image: linear-gradient(0deg, var(--secondary-axes-color) var(--secondary-axes-width), transparent var(--secondary-axes-width));
|
149
|
+
}
|
150
|
+
|
151
|
+
/*
|
152
|
+
* Chart tooltips
|
153
|
+
*/
|
154
|
+
.active-analytics .charts-css .tooltip {
|
155
|
+
position: absolute;
|
156
|
+
z-index: 1;
|
157
|
+
bottom: 50%;
|
158
|
+
left: 50%;
|
159
|
+
transform: translateX(-50%);
|
160
|
+
width: max-content;
|
161
|
+
padding: 5px 10px;
|
162
|
+
border-radius: 6px;
|
163
|
+
visibility: hidden;
|
164
|
+
opacity: 0;
|
165
|
+
transition: opacity .3s;
|
166
|
+
background-color: rgba(var(--color-grey-500), 1);
|
167
|
+
color: rgba(var(--color-grey-00), 1);
|
168
|
+
text-align: center;
|
169
|
+
font-size: .9rem;
|
170
|
+
}
|
171
|
+
|
172
|
+
.active-analytics .charts-css .tooltip::after {
|
173
|
+
content: "";
|
174
|
+
position: absolute;
|
175
|
+
top: 100%;
|
176
|
+
left: 50%;
|
177
|
+
margin-left: -5px;
|
178
|
+
border-width: 5px;
|
179
|
+
border-style: solid;
|
180
|
+
border-color: rgba(var(--color-grey-500), 1) transparent transparent;
|
181
|
+
}
|
182
|
+
|
183
|
+
.active-analytics .charts-css tr:hover .tooltip {
|
184
|
+
visibility: visible;
|
185
|
+
opacity: 1;
|
186
|
+
}
|
187
|
+
|
188
|
+
/*
|
189
|
+
* Column Chart
|
190
|
+
*/
|
191
|
+
.active-analytics .charts-css.column tbody {
|
192
|
+
display: flex;
|
193
|
+
justify-content: space-between;
|
194
|
+
align-items: stretch;
|
195
|
+
width: 100%;
|
196
|
+
gap: 1px;
|
197
|
+
height: calc(100% - var(--heading-size));
|
198
|
+
}
|
199
|
+
|
200
|
+
.active-analytics .charts-css.column tbody tr {
|
201
|
+
position: relative;
|
202
|
+
flex-grow: 1;
|
203
|
+
flex-shrink: 1;
|
204
|
+
flex-basis: 0;
|
205
|
+
overflow-wrap: anywhere;
|
206
|
+
display: flex;
|
207
|
+
justify-content: flex-start;
|
208
|
+
min-width: 0;
|
209
|
+
}
|
210
|
+
|
211
|
+
.active-analytics .charts-css.column tbody tr th {
|
212
|
+
position: absolute;
|
213
|
+
right: 0;
|
214
|
+
left: 0;
|
215
|
+
}
|
216
|
+
|
217
|
+
.active-analytics .charts-css.column tbody tr td {
|
218
|
+
display: flex;
|
219
|
+
justify-content: center;
|
220
|
+
width: 100%;
|
221
|
+
height: calc(100% * var(--size, 1));
|
222
|
+
position: relative;
|
223
|
+
}
|
224
|
+
|
225
|
+
.active-analytics .charts-css.column:not(.reverse) tbody tr {
|
226
|
+
align-items: flex-end;
|
227
|
+
margin-block-end: var(--labels-size);
|
228
|
+
}
|
229
|
+
|
230
|
+
.active-analytics .charts-css.column:not(.reverse) tbody tr th {
|
231
|
+
bottom: calc(-1 * var(--labels-size) - var(--primary-axis-width));
|
232
|
+
height: var(--labels-size);
|
233
|
+
color: rgba(var(--color-grey-400), 1);
|
234
|
+
font-weight: 400;
|
235
|
+
}
|
236
|
+
|
237
|
+
.active-analytics .charts-css.column:not(.reverse) tbody tr td {
|
238
|
+
align-items: flex-start;
|
239
|
+
}
|
240
|
+
|
241
|
+
.active-analytics .charts-css.column:not(.stacked) tbody tr td {
|
242
|
+
flex-grow: 1;
|
243
|
+
flex-shrink: 1;
|
244
|
+
flex-basis: 0;
|
245
|
+
}
|
246
|
+
|
247
|
+
.active-analytics .charts-css.column:not(.reverse-data) tbody {
|
248
|
+
flex-direction: row;
|
249
|
+
}
|