sleek_charts 0.0.1
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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +46 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/bar-tip.js +125 -0
- data/app/assets/javascripts/base.js +6 -0
- data/app/assets/javascripts/d3/d3.tip.js +276 -0
- data/app/assets/javascripts/d3/d3.v3.js +9300 -0
- data/app/assets/javascripts/donut-tip.js +161 -0
- data/app/assets/javascripts/sleek_charts.js +9 -0
- data/app/assets/stylesheets/sleek_charts.css +86 -0
- data/lib/sleek_charts/version.rb +3 -0
- data/lib/sleek_charts.rb +6 -0
- data/sleek_charts.gemspec +23 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7391ad7d984d32aed098dc84c9cacd8e2e67bd76
|
4
|
+
data.tar.gz: 5c1bc0324d6aa3f898673b968e883b7ac3ef9d52
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fa977e8d6e25cacabfce7f96ccb1a34696f79c65838a325fe4a2e22a462ecca4021e6be68b7df01e7a42089ceda316ee7d05a86717c4f089b229c929ef9ee36e
|
7
|
+
data.tar.gz: 288931d0d01727e6421b78c62c7d193ca5294453aa7661cf633f4de21950f8e7cdf8a1e79d0e400fc3c8b30cf15def591652cfc348f1adb408384cbc1fc6fa74
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Gourav Tiwari
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# SleekCharts
|
2
|
+
|
3
|
+
Provides Bar and Donut charts with consitent tooltip for Rails applications.
|
4
|
+
|
5
|
+
## Supports Rails 3.1+
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'sleek_charts'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install sleek_charts
|
20
|
+
|
21
|
+
Once done, add below to application's assets/javascripts/application.js file
|
22
|
+
//= require sleek_charts
|
23
|
+
|
24
|
+
Also, add below to application's assets/stylesheets/application.css file
|
25
|
+
*= require sleek_charts
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Simply add bar chart to any element, e.g.
|
30
|
+
|
31
|
+
barTip(options);
|
32
|
+
//where options is a map, checkout bar-tip.js
|
33
|
+
|
34
|
+
or add donut chart to any element, e.g.
|
35
|
+
|
36
|
+
donutTip(options);
|
37
|
+
//where options is a map, checkout donut-tip.js
|
38
|
+
|
39
|
+
|
40
|
+
## Contributing
|
41
|
+
|
42
|
+
1. Fork it
|
43
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
44
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
45
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
46
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,125 @@
|
|
1
|
+
// options: it is a map, which accepts below values
|
2
|
+
//
|
3
|
+
// selector: selector to which bar chart will be added, default is 'body'
|
4
|
+
// e.g. 'div#bar-chart'
|
5
|
+
// data: data in JSON object, which contains label and value to create bar chart
|
6
|
+
// e.g. [{'label': 'weather-day', 'value': 35.1}, {'label': 'weather-night', 'value': 30.2}]
|
7
|
+
// width: width of svg element
|
8
|
+
// height: height of svg element
|
9
|
+
// margin: margin of svg element and accepts in a map
|
10
|
+
// {top: 10, right: 20, bottom: 20, left:10}
|
11
|
+
// flexRight: set it to false, when you are ok to display wider column, when there are <5-6 columns, default is true
|
12
|
+
// labelAngle: default is 0
|
13
|
+
// xDomain: x-axis domain, default is 'label'
|
14
|
+
// yDomain: y-axis domain, default is 'value'
|
15
|
+
// yAxisText: add additional text on y-axis default is ''
|
16
|
+
// tipLabel: tipText which you want to display in tip, default is ''
|
17
|
+
// tipValue: tipValue, for each bar, default is 'value'
|
18
|
+
// tipText: tipText which you want to display with 'label' and 'value', default is ''
|
19
|
+
|
20
|
+
function mergeConfigOptions(defaults,options){
|
21
|
+
var mergedConfig = {};
|
22
|
+
for (var attrname in defaults) { mergedConfig[attrname] = defaults[attrname]; }
|
23
|
+
for (var attrname in options) { mergedConfig[attrname] = options[attrname]; }
|
24
|
+
return mergedConfig;
|
25
|
+
}
|
26
|
+
|
27
|
+
function barTip(options){
|
28
|
+
var defaults = {
|
29
|
+
selector: 'body',
|
30
|
+
data: [{'label': 'weather-morning', 'value': 29.1}, {'label': 'weather-afternoon', 'value': 33.2},
|
31
|
+
{'label': 'weather-evening', 'value': 32.1}, {'label': 'weather-night', 'value': 30.2}],
|
32
|
+
width: 600,
|
33
|
+
height: 400,
|
34
|
+
margin: {top: 10, right: 20, bottom: 20, left:10},
|
35
|
+
flexRight: true,
|
36
|
+
labelAngle: -30,
|
37
|
+
xDomain: 'label',
|
38
|
+
yDomain: 'value',
|
39
|
+
yAxisText: '',
|
40
|
+
tipLabel: '',
|
41
|
+
tipValue: 'value',
|
42
|
+
tipText: ''
|
43
|
+
};
|
44
|
+
|
45
|
+
var config = (options) ? mergeConfigOptions(defaults,options) : defaults;
|
46
|
+
|
47
|
+
var data = config.data;
|
48
|
+
|
49
|
+
// Just to make sure the chart doesn't look clutter, when there are < 5-6 columns
|
50
|
+
if(config.flexRight){
|
51
|
+
config.margin['right'] = d3.max([20, (config.width - data.length * 50)]);
|
52
|
+
}
|
53
|
+
|
54
|
+
var margin = config.margin,
|
55
|
+
width = config.width - margin.left - margin.right,
|
56
|
+
height = config.height - margin.top - margin.bottom;
|
57
|
+
|
58
|
+
var x = d3.scale.ordinal()
|
59
|
+
.rangeRoundBands([0, width], .1);
|
60
|
+
|
61
|
+
var y = d3.scale.linear()
|
62
|
+
.range([height, 0]);
|
63
|
+
|
64
|
+
var xAxis = d3.svg.axis()
|
65
|
+
.scale(x)
|
66
|
+
.orient("bottom");
|
67
|
+
|
68
|
+
var yAxis = d3.svg.axis()
|
69
|
+
.scale(y)
|
70
|
+
.orient("left");
|
71
|
+
|
72
|
+
var svg = d3.select(config.selector).append("svg")
|
73
|
+
.attr("width", config.width)
|
74
|
+
.attr("height", config.height)
|
75
|
+
.append("g")
|
76
|
+
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
77
|
+
|
78
|
+
var tip = d3.tip()
|
79
|
+
.attr('class', 'd3-tip')
|
80
|
+
.offset([-10, 0])
|
81
|
+
.html(function(d) {
|
82
|
+
label = d[config.tipLabel] == undefined ? "" : " " + d[config.tipLabel];
|
83
|
+
|
84
|
+
return "<strong>" + config.tipText + label + "</strong> <span style='color:red'>" + d[config.tipValue] + "</span>";
|
85
|
+
});
|
86
|
+
console.log(svg);
|
87
|
+
svg.call(tip);
|
88
|
+
|
89
|
+
x.domain(data.map(function(d) { return d[config.xDomain]; }));
|
90
|
+
y.domain([0, d3.max(data, function(d) { return d[config.yDomain]; })]);
|
91
|
+
|
92
|
+
svg.append("g")
|
93
|
+
.attr("class", "x axis")
|
94
|
+
.attr("transform", "translate(0," + height + ")")
|
95
|
+
.call(xAxis)
|
96
|
+
.selectAll("text")
|
97
|
+
.style("text-anchor", "end")
|
98
|
+
.attr("dx", "-.8em")
|
99
|
+
.attr("dy", ".15em")
|
100
|
+
.attr("transform", function(d) {
|
101
|
+
return "rotate("+ config.labelAngle +")" ;
|
102
|
+
});
|
103
|
+
|
104
|
+
svg.append("g")
|
105
|
+
.attr("class", "y axis")
|
106
|
+
.call(yAxis)
|
107
|
+
.append("text")
|
108
|
+
.attr("transform", "rotate(-90)")
|
109
|
+
.attr("y", -40)
|
110
|
+
.attr("x", -80)
|
111
|
+
.attr("dy", "0.71em")
|
112
|
+
.style("text-anchor", "end")
|
113
|
+
.text(config.yAxisText);
|
114
|
+
|
115
|
+
svg.selectAll(".bar")
|
116
|
+
.data(data)
|
117
|
+
.enter().append("rect")
|
118
|
+
.attr("class", "bar")
|
119
|
+
.attr("x", function(d) { return x(d[config.xDomain]); })
|
120
|
+
.attr("width", x.rangeBand())
|
121
|
+
.attr("y", function(d) { return y(d[config.yDomain]); })
|
122
|
+
.attr("height", function(d) { return height - y(d[config.yDomain]); })
|
123
|
+
.on('mouseover', tip.show)
|
124
|
+
.on('mouseout', tip.hide);
|
125
|
+
}
|
@@ -0,0 +1,276 @@
|
|
1
|
+
// d3.tip
|
2
|
+
// Copyright (c) 2013 Justin Palmer
|
3
|
+
//
|
4
|
+
// Tooltips for d3.js SVG visualizations
|
5
|
+
|
6
|
+
// Public - contructs a new tooltip
|
7
|
+
//
|
8
|
+
// Returns a tip
|
9
|
+
d3.tip = function() {
|
10
|
+
var direction = d3_tip_direction,
|
11
|
+
offset = d3_tip_offset,
|
12
|
+
html = d3_tip_html,
|
13
|
+
node = initNode(),
|
14
|
+
svg = null,
|
15
|
+
point = null
|
16
|
+
|
17
|
+
function tip(vis) {
|
18
|
+
svg = getSVGNode(vis)
|
19
|
+
point = svg.createSVGPoint()
|
20
|
+
document.body.appendChild(node)
|
21
|
+
}
|
22
|
+
|
23
|
+
// Public - show the tooltip on the screen
|
24
|
+
//
|
25
|
+
// Returns a tip
|
26
|
+
tip.show = function() {
|
27
|
+
var content = html.apply(this, arguments),
|
28
|
+
poffset = offset.apply(this, arguments),
|
29
|
+
dir = direction.apply(this, arguments),
|
30
|
+
nodel = d3.select(node), i = 0,
|
31
|
+
coords
|
32
|
+
|
33
|
+
nodel.html(content)
|
34
|
+
.style({ opacity: 1, 'pointer-events': 'all' })
|
35
|
+
|
36
|
+
while(i--) nodel.classed(directions[i], false)
|
37
|
+
coords = direction_callbacks.get(dir).apply(this)
|
38
|
+
nodel.classed(dir, true).style({
|
39
|
+
top: (coords.top + poffset[0]) + 'px',
|
40
|
+
left: (coords.left + poffset[1]) + 'px'
|
41
|
+
})
|
42
|
+
|
43
|
+
return tip
|
44
|
+
}
|
45
|
+
|
46
|
+
// Public - hide the tooltip
|
47
|
+
//
|
48
|
+
// Returns a tip
|
49
|
+
tip.hide = function() {
|
50
|
+
nodel = d3.select(node)
|
51
|
+
nodel.style({ opacity: 0, 'pointer-events': 'none' })
|
52
|
+
return tip
|
53
|
+
}
|
54
|
+
|
55
|
+
// Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value.
|
56
|
+
//
|
57
|
+
// n - name of the attribute
|
58
|
+
// v - value of the attribute
|
59
|
+
//
|
60
|
+
// Returns tip or attribute value
|
61
|
+
tip.attr = function(n, v) {
|
62
|
+
if (arguments.length < 2 && typeof n === 'string') {
|
63
|
+
return d3.select(node).attr(n)
|
64
|
+
} else {
|
65
|
+
var args = Array.prototype.slice.call(arguments)
|
66
|
+
d3.selection.prototype.attr.apply(d3.select(node), args)
|
67
|
+
}
|
68
|
+
|
69
|
+
return tip
|
70
|
+
}
|
71
|
+
|
72
|
+
// Public: Proxy style calls to the d3 tip container. Sets or gets a style value.
|
73
|
+
//
|
74
|
+
// n - name of the property
|
75
|
+
// v - value of the property
|
76
|
+
//
|
77
|
+
// Returns tip or style property value
|
78
|
+
tip.style = function(n, v) {
|
79
|
+
if (arguments.length < 2 && typeof n === 'string') {
|
80
|
+
return d3.select(node).style(n)
|
81
|
+
} else {
|
82
|
+
var args = Array.prototype.slice.call(arguments)
|
83
|
+
d3.selection.prototype.style.apply(d3.select(node), args)
|
84
|
+
}
|
85
|
+
|
86
|
+
return tip
|
87
|
+
}
|
88
|
+
|
89
|
+
// Public: Set or get the direction of the tooltip
|
90
|
+
//
|
91
|
+
// v - One of n(north), s(south), e(east), or w(west), nw(northwest),
|
92
|
+
// sw(southwest), ne(northeast) or se(southeast)
|
93
|
+
//
|
94
|
+
// Returns tip or direction
|
95
|
+
tip.direction = function(v) {
|
96
|
+
if (!arguments.length) return direction
|
97
|
+
direction = v == null ? v : d3.functor(v)
|
98
|
+
|
99
|
+
return tip
|
100
|
+
}
|
101
|
+
|
102
|
+
// Public: Sets or gets the offset of the tip
|
103
|
+
//
|
104
|
+
// v - Array of [x, y] offset
|
105
|
+
//
|
106
|
+
// Returns offset or
|
107
|
+
tip.offset = function(v) {
|
108
|
+
if (!arguments.length) return offset
|
109
|
+
offset = v == null ? v : d3.functor(v)
|
110
|
+
|
111
|
+
return tip
|
112
|
+
}
|
113
|
+
|
114
|
+
// Public: sets or gets the html value of the tooltip
|
115
|
+
//
|
116
|
+
// v - String value of the tip
|
117
|
+
//
|
118
|
+
// Returns html value or tip
|
119
|
+
tip.html = function(v) {
|
120
|
+
if (!arguments.length) return html
|
121
|
+
html = v == null ? v : d3.functor(v)
|
122
|
+
|
123
|
+
return tip
|
124
|
+
}
|
125
|
+
|
126
|
+
function d3_tip_direction() { return 'n' }
|
127
|
+
function d3_tip_offset() { return [0, 0] }
|
128
|
+
function d3_tip_html() { return ' ' }
|
129
|
+
|
130
|
+
var direction_callbacks = d3.map({
|
131
|
+
n: direction_n,
|
132
|
+
s: direction_s,
|
133
|
+
e: direction_e,
|
134
|
+
w: direction_w,
|
135
|
+
nw: direction_nw,
|
136
|
+
ne: direction_ne,
|
137
|
+
sw: direction_sw,
|
138
|
+
se: direction_se
|
139
|
+
}),
|
140
|
+
|
141
|
+
directions = direction_callbacks.keys()
|
142
|
+
|
143
|
+
function direction_n() {
|
144
|
+
var bbox = getScreenBBox()
|
145
|
+
return {
|
146
|
+
top: bbox.n.y - node.offsetHeight,
|
147
|
+
left: bbox.n.x - node.offsetWidth / 2
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
function direction_s() {
|
152
|
+
var bbox = getScreenBBox()
|
153
|
+
return {
|
154
|
+
top: bbox.s.y,
|
155
|
+
left: bbox.s.x - node.offsetWidth / 2
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
function direction_e() {
|
160
|
+
var bbox = getScreenBBox()
|
161
|
+
return {
|
162
|
+
top: bbox.e.y - node.offsetHeight / 2,
|
163
|
+
left: bbox.e.x
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
function direction_w() {
|
168
|
+
var bbox = getScreenBBox()
|
169
|
+
return {
|
170
|
+
top: bbox.w.y - node.offsetHeight / 2,
|
171
|
+
left: bbox.w.x - node.offsetWidth
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
function direction_nw() {
|
176
|
+
var bbox = getScreenBBox()
|
177
|
+
return {
|
178
|
+
top: bbox.nw.y - node.offsetHeight,
|
179
|
+
left: bbox.nw.x - node.offsetWidth
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
function direction_ne() {
|
184
|
+
var bbox = getScreenBBox()
|
185
|
+
return {
|
186
|
+
top: bbox.ne.y - node.offsetHeight,
|
187
|
+
left: bbox.ne.x
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
function direction_sw() {
|
192
|
+
var bbox = getScreenBBox()
|
193
|
+
return {
|
194
|
+
top: bbox.sw.y,
|
195
|
+
left: bbox.sw.x - node.offsetWidth
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
function direction_se() {
|
200
|
+
var bbox = getScreenBBox()
|
201
|
+
return {
|
202
|
+
top: bbox.se.y,
|
203
|
+
left: bbox.e.x
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
function initNode() {
|
208
|
+
var node = d3.select(document.createElement('div'))
|
209
|
+
node.style({
|
210
|
+
position: 'absolute',
|
211
|
+
opacity: 0,
|
212
|
+
pointerEvents: 'none',
|
213
|
+
boxSizing: 'border-box'
|
214
|
+
})
|
215
|
+
|
216
|
+
return node.node()
|
217
|
+
}
|
218
|
+
|
219
|
+
function getSVGNode(el) {
|
220
|
+
el = el.node()
|
221
|
+
if(el.tagName.toLowerCase() == 'svg')
|
222
|
+
return el
|
223
|
+
|
224
|
+
return el.ownerSVGElement
|
225
|
+
}
|
226
|
+
|
227
|
+
// Private - gets the screen coordinates of a shape
|
228
|
+
//
|
229
|
+
// Given a shape on the screen, will return an SVGPoint for the directions
|
230
|
+
// n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest),
|
231
|
+
// sw(southwest).
|
232
|
+
//
|
233
|
+
// +-+-+
|
234
|
+
// | |
|
235
|
+
// + +
|
236
|
+
// | |
|
237
|
+
// +-+-+
|
238
|
+
//
|
239
|
+
// Returns an Object {n, s, e, w, nw, sw, ne, se}
|
240
|
+
function getScreenBBox() {
|
241
|
+
var target = d3.event.target,
|
242
|
+
bbox = {},
|
243
|
+
matrix = target.getScreenCTM(),
|
244
|
+
tbbox = target.getBBox(),
|
245
|
+
width = tbbox.width,
|
246
|
+
height = tbbox.height,
|
247
|
+
x = tbbox.x,
|
248
|
+
y = tbbox.y,
|
249
|
+
scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
|
250
|
+
scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
|
251
|
+
|
252
|
+
|
253
|
+
point.x = x + scrollLeft
|
254
|
+
point.y = y + scrollTop
|
255
|
+
bbox.nw = point.matrixTransform(matrix)
|
256
|
+
point.x += width
|
257
|
+
bbox.ne = point.matrixTransform(matrix)
|
258
|
+
point.y += height
|
259
|
+
bbox.se = point.matrixTransform(matrix)
|
260
|
+
point.x -= width
|
261
|
+
bbox.sw = point.matrixTransform(matrix)
|
262
|
+
point.y -= height / 2
|
263
|
+
bbox.w = point.matrixTransform(matrix)
|
264
|
+
point.x += width
|
265
|
+
bbox.e = point.matrixTransform(matrix)
|
266
|
+
point.x -= width / 2
|
267
|
+
point.y -= height / 2
|
268
|
+
bbox.n = point.matrixTransform(matrix)
|
269
|
+
point.y += height
|
270
|
+
bbox.s = point.matrixTransform(matrix)
|
271
|
+
|
272
|
+
return bbox
|
273
|
+
}
|
274
|
+
|
275
|
+
return tip
|
276
|
+
};
|