peity_vanilla_rails 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,421 @@
1
+ /*!
2
+ Peity Vanila JS 0.0.8
3
+ Copyright © 2022 RailsJazz
4
+ https://railsjazz.com
5
+ */
6
+
7
+ const isFunction = (o) =>
8
+ o !== null && typeof o === "function" && !!o.apply;
9
+
10
+ const svgElement = (tag, attrs) => {
11
+ const element = document.createElementNS("http://www.w3.org/2000/svg", tag);
12
+ for (var attr in attrs) {
13
+ element.setAttribute(attr, attrs[attr]);
14
+ }
15
+ return element;
16
+ };
17
+
18
+ const svgSupported =
19
+ "createElementNS" in document && svgElement("svg", {}).createSVGRect();
20
+
21
+ class Peity {
22
+ static defaults = {};
23
+ static graphers = {};
24
+
25
+ constructor(element, type, options = {}) {
26
+ this.element = element;
27
+ this.type = type;
28
+ this.options = Object.assign(
29
+ {},
30
+ Peity.defaults[this.type],
31
+ JSON.parse(element.dataset["peity"] || "{}"),
32
+ options
33
+ );
34
+
35
+ if(this.element._peity) {
36
+ this.element._peity.destroy();
37
+ }
38
+ this.element._peity = this;
39
+ }
40
+
41
+ draw() {
42
+ const options = this.options;
43
+ Peity.graphers[this.type](this);
44
+ if (isFunction(options.after)) options.after.call(this, options);
45
+ }
46
+
47
+ fill() {
48
+ var fill = this.options.fill;
49
+
50
+ return isFunction(fill)
51
+ ? fill
52
+ : function (_, i) {
53
+ return fill[i % fill.length];
54
+ };
55
+ }
56
+
57
+ prepare(width, height) {
58
+ if (!this.svg) {
59
+ this.element.style.display = "none";
60
+ this.element.after(
61
+ (this.svg = svgElement("svg", {
62
+ class: "peity",
63
+ }))
64
+ );
65
+ }
66
+
67
+ this.svg.innerHTML = "";
68
+ this.svg.setAttribute("width", width);
69
+ this.svg.setAttribute("height", height);
70
+
71
+ return this.svg;
72
+ }
73
+
74
+ get values() {
75
+ return this.element.innerText
76
+ .split(this.options.delimiter)
77
+ .map((value) => parseFloat(value));
78
+ }
79
+
80
+ mount() {
81
+ if (!svgSupported) return;
82
+
83
+ this.element.addEventListener("DOMSubtreeModified", this.draw.bind(this));
84
+ this.draw();
85
+
86
+ this.mounted = true;
87
+ }
88
+
89
+ unmount() {
90
+ this.element.removeEventListener("DOMSubtreeModified", this.draw);
91
+ this.svg.remove();
92
+ this.mounted = false;
93
+ }
94
+
95
+ destroy() {
96
+ this.unmount();
97
+
98
+ delete this.element._peity;
99
+ }
100
+
101
+ static register(type, defaults, grapher) {
102
+ Peity.defaults[type] = defaults;
103
+ Peity.graphers[type] = grapher;
104
+ }
105
+ }
106
+
107
+ const renderer$2 = (peity) => {
108
+ if (!peity.options.delimiter) {
109
+ const delimiter = peity.element.innerText.match(/[^0-9\.]/);
110
+ peity.options.delimiter = delimiter ? delimiter[0] : ",";
111
+ }
112
+
113
+ let values = peity.values.map((n) => (n > 0 ? n : 0));
114
+
115
+ if (peity.options.delimiter == "/") {
116
+ let v1 = values[0];
117
+ let v2 = values[1];
118
+ values = [v1, Math.max(0, v2 - v1)];
119
+ }
120
+
121
+ let i = 0;
122
+ let length = values.length;
123
+ let sum = 0;
124
+
125
+ for (; i < length; i++) {
126
+ sum += values[i];
127
+ }
128
+
129
+ if (!sum) {
130
+ length = 2;
131
+ sum = 1;
132
+ values = [0, 1];
133
+ }
134
+
135
+ let diameter = peity.options.radius * 2;
136
+
137
+ const svg = peity.prepare(
138
+ peity.options.width || diameter,
139
+ peity.options.height || diameter
140
+ );
141
+
142
+ const width = svg.clientWidth;
143
+ const height = svg.clientHeight;
144
+ const cx = width / 2;
145
+ const cy = height / 2;
146
+
147
+ const radius = Math.min(cx, cy);
148
+ let innerRadius = peity.options.innerRadius;
149
+
150
+ if (peity.type == "donut" && !innerRadius) {
151
+ innerRadius = radius * 0.5;
152
+ }
153
+
154
+ const fill = peity.fill();
155
+
156
+ const scale = (value, radius) => {
157
+ const radians = (value / sum) * Math.PI * 2 - Math.PI / 2;
158
+
159
+ return [radius * Math.cos(radians) + cx, radius * Math.sin(radians) + cy];
160
+ };
161
+
162
+ let cumulative = 0;
163
+
164
+ for (i = 0; i < length; i++) {
165
+ const value = values[i];
166
+ const portion = value / sum;
167
+ let node;
168
+
169
+ if (portion == 0) continue;
170
+
171
+ if (portion == 1) {
172
+ if (innerRadius) {
173
+ const x2 = cx - 0.01;
174
+ const y1 = cy - radius;
175
+ const y2 = cy - innerRadius;
176
+
177
+ node = svgElement("path", {
178
+ d: [
179
+ "M",
180
+ cx,
181
+ y1,
182
+ "A",
183
+ radius,
184
+ radius,
185
+ 0,
186
+ 1,
187
+ 1,
188
+ x2,
189
+ y1,
190
+ "L",
191
+ x2,
192
+ y2,
193
+ "A",
194
+ innerRadius,
195
+ innerRadius,
196
+ 0,
197
+ 1,
198
+ 0,
199
+ cx,
200
+ y2,
201
+ ].join(" "),
202
+ "data-value": value,
203
+ });
204
+ } else {
205
+ node = svgElement("circle", {
206
+ cx: cx,
207
+ cy: cy,
208
+ "data-value": value,
209
+ r: radius,
210
+ });
211
+ }
212
+ } else {
213
+ const cumulativePlusValue = cumulative + value;
214
+
215
+ let d = ["M"].concat(
216
+ scale(cumulative, radius),
217
+ "A",
218
+ radius,
219
+ radius,
220
+ 0,
221
+ portion > 0.5 ? 1 : 0,
222
+ 1,
223
+ scale(cumulativePlusValue, radius),
224
+ "L"
225
+ );
226
+
227
+ if (innerRadius) {
228
+ d = d.concat(
229
+ scale(cumulativePlusValue, innerRadius),
230
+ "A",
231
+ innerRadius,
232
+ innerRadius,
233
+ 0,
234
+ portion > 0.5 ? 1 : 0,
235
+ 0,
236
+ scale(cumulative, innerRadius)
237
+ );
238
+ } else {
239
+ d.push(cx, cy);
240
+ }
241
+
242
+ cumulative += value;
243
+
244
+ node = svgElement("path", {
245
+ d: d.join(" "),
246
+ "data-value": value,
247
+ });
248
+ }
249
+
250
+ node.setAttribute("fill", fill.call(peity, value, i, values));
251
+
252
+ svg.append(node);
253
+ }
254
+ };
255
+
256
+ const defaults$2 = {
257
+ fill: ["#ff9900", "#fff4dd", "#ffc66e"],
258
+ radius: 8,
259
+ };
260
+
261
+ const renderer$1 = (peity) => {
262
+ const values = peity.values;
263
+ const max = Math.max.apply(
264
+ Math,
265
+ peity.options.max == undefined ? values : values.concat(peity.options.max)
266
+ );
267
+ const min = Math.min.apply(
268
+ Math,
269
+ peity.options.min == undefined ? values : values.concat(peity.options.min)
270
+ );
271
+
272
+ const svg = peity.prepare(peity.options.width, peity.options.height);
273
+ const width = svg.clientWidth;
274
+ const height = svg.clientHeight;
275
+ const diff = max - min;
276
+ const padding = peity.options.padding;
277
+ const fill = peity.fill();
278
+
279
+ const xScale = (input) => {
280
+ return (input * width) / values.length;
281
+ };
282
+
283
+ const yScale = (input) => {
284
+ return height - (diff ? ((input - min) / diff) * height : 1);
285
+ };
286
+
287
+ for (var i = 0; i < values.length; i++) {
288
+ let x = xScale(i + padding);
289
+ let w = xScale(i + 1 - padding) - x;
290
+ let value = values[i];
291
+ let valueY = yScale(value);
292
+ let y1 = valueY;
293
+ let y2 = valueY;
294
+ let h;
295
+
296
+ if (!diff) {
297
+ h = 1;
298
+ } else if (value < 0) {
299
+ y1 = yScale(Math.min(max, 0));
300
+ } else {
301
+ y2 = yScale(Math.max(min, 0));
302
+ }
303
+
304
+ h = y2 - y1;
305
+
306
+ if (h == 0) {
307
+ h = 1;
308
+ if (max > 0 && diff) y1--;
309
+ }
310
+
311
+ svg.append(
312
+ svgElement("rect", {
313
+ "data-value": value,
314
+ fill: fill.call(peity, value, i, values),
315
+ x: x,
316
+ y: y1,
317
+ width: w,
318
+ height: h,
319
+ })
320
+ );
321
+ }
322
+ };
323
+
324
+ const defaults$1 = {
325
+ delimiter: ",",
326
+ fill: ["#4D89F9"],
327
+ height: 16,
328
+ min: 0,
329
+ padding: 0.1,
330
+ width: 32,
331
+ };
332
+
333
+ const renderer = (peity) => {
334
+ const values = peity.values;
335
+ if (values.length == 1) values.push(values[0]);
336
+ const max = Math.max.apply(
337
+ Math,
338
+ peity.options.max == undefined ? values : values.concat(peity.options.max)
339
+ );
340
+ const min = Math.min.apply(
341
+ Math,
342
+ peity.options.min == undefined ? values : values.concat(peity.options.min)
343
+ );
344
+
345
+ const svg = peity.prepare(peity.options.width, peity.options.height);
346
+ const strokeWidth = peity.options.strokeWidth;
347
+ const width = svg.clientWidth;
348
+ const height = svg.clientHeight - strokeWidth;
349
+ const diff = max - min;
350
+
351
+ const xScale = (input) => {
352
+ return input * (width / (values.length - 1));
353
+ };
354
+
355
+ const yScale = (input) => {
356
+ let y = height;
357
+
358
+ if (diff) {
359
+ y -= ((input - min) / diff) * height;
360
+ }
361
+
362
+ return y + strokeWidth / 2;
363
+ };
364
+
365
+ let zero = yScale(Math.max(min, 0));
366
+ let coords = [0, zero];
367
+
368
+ for (var i = 0; i < values.length; i++) {
369
+ coords.push(xScale(i), yScale(values[i]));
370
+ }
371
+
372
+ coords.push(width, zero);
373
+
374
+ if (peity.options.fill) {
375
+ svg.append(
376
+ svgElement("polygon", {
377
+ fill: peity.options.fill,
378
+ points: coords.join(" "),
379
+ })
380
+ );
381
+ }
382
+
383
+ if (strokeWidth) {
384
+ svg.append(
385
+ svgElement("polyline", {
386
+ fill: "none",
387
+ points: coords.slice(2, coords.length - 2).join(" "),
388
+ stroke: peity.options.stroke,
389
+ "stroke-width": strokeWidth,
390
+ "stroke-linecap": "square",
391
+ })
392
+ );
393
+ }
394
+ };
395
+
396
+ const defaults = {
397
+ delimiter: ",",
398
+ fill: "#c6d9fd",
399
+ height: 16,
400
+ min: 0,
401
+ stroke: "#4d89f9",
402
+ strokeWidth: 1,
403
+ width: 32,
404
+ };
405
+
406
+ Peity.register("pie", defaults$2, renderer$2);
407
+ Peity.register("donut", defaults$2, renderer$2);
408
+ Peity.register("bar", defaults$1, renderer$1);
409
+ Peity.register("line", defaults, renderer);
410
+
411
+ const peity = function (element, type, options) {
412
+ const peity = new Peity(element, type, options);
413
+ peity.mount();
414
+
415
+ return peity;
416
+ };
417
+
418
+ peity.defaults = Peity.defaults;
419
+ peity.graphers = Peity.graphers;
420
+
421
+ export { peity as default };
@@ -0,0 +1,6 @@
1
+ var peity=function(){"use strict";
2
+ /*!
3
+ Peity Vanila JS 0.0.8
4
+ Copyright © 2022 RailsJazz
5
+ https://railsjazz.com
6
+ */const t=t=>null!==t&&"function"==typeof t&&!!t.apply,e=(t,e)=>{const i=document.createElementNS("http://www.w3.org/2000/svg",t);for(var n in e)i.setAttribute(n,e[n]);return i},i="createElementNS"in document&&e("svg",{}).createSVGRect();class n{static defaults={};static graphers={};constructor(t,e,i={}){this.element=t,this.type=e,this.options=Object.assign({},n.defaults[this.type],JSON.parse(t.dataset.peity||"{}"),i),this.element._peity&&this.element._peity.destroy(),this.element._peity=this}draw(){const e=this.options;n.graphers[this.type](this),t(e.after)&&e.after.call(this,e)}fill(){var e=this.options.fill;return t(e)?e:function(t,i){return e[i%e.length]}}prepare(t,i){return this.svg||(this.element.style.display="none",this.element.after(this.svg=e("svg",{class:"peity"}))),this.svg.innerHTML="",this.svg.setAttribute("width",t),this.svg.setAttribute("height",i),this.svg}get values(){return this.element.innerText.split(this.options.delimiter).map((t=>parseFloat(t)))}mount(){i&&(this.element.addEventListener("DOMSubtreeModified",this.draw.bind(this)),this.draw(),this.mounted=!0)}unmount(){this.element.removeEventListener("DOMSubtreeModified",this.draw),this.svg.remove(),this.mounted=!1}destroy(){this.unmount(),delete this.element._peity}static register(t,e,i){n.defaults[t]=e,n.graphers[t]=i}}const s=t=>{if(!t.options.delimiter){const e=t.element.innerText.match(/[^0-9\.]/);t.options.delimiter=e?e[0]:","}let i=t.values.map((t=>t>0?t:0));if("/"==t.options.delimiter){let t=i[0],e=i[1];i=[t,Math.max(0,e-t)]}let n=0,s=i.length,o=0;for(;n<s;n++)o+=i[n];o||(s=2,o=1,i=[0,1]);let l=2*t.options.radius;const a=t.prepare(t.options.width||l,t.options.height||l),r=a.clientWidth,h=a.clientHeight,p=r/2,d=h/2,c=Math.min(p,d);let u=t.options.innerRadius;"donut"!=t.type||u||(u=.5*c);const m=t.fill(),f=(t,e)=>{const i=t/o*Math.PI*2-Math.PI/2;return[e*Math.cos(i)+p,e*Math.sin(i)+d]};let g=0;for(n=0;n<s;n++){const s=i[n],l=s/o;let r;if(0!=l){if(1==l)if(u){const t=p-.01,i=d-c,n=d-u;r=e("path",{d:["M",p,i,"A",c,c,0,1,1,t,i,"L",t,n,"A",u,u,0,1,0,p,n].join(" "),"data-value":s})}else r=e("circle",{cx:p,cy:d,"data-value":s,r:c});else{const t=g+s;let i=["M"].concat(f(g,c),"A",c,c,0,l>.5?1:0,1,f(t,c),"L");u?i=i.concat(f(t,u),"A",u,u,0,l>.5?1:0,0,f(g,u)):i.push(p,d),g+=s,r=e("path",{d:i.join(" "),"data-value":s})}r.setAttribute("fill",m.call(t,s,n,i)),a.append(r)}}},o={fill:["#ff9900","#fff4dd","#ffc66e"],radius:8};n.register("pie",o,s),n.register("donut",o,s),n.register("bar",{delimiter:",",fill:["#4D89F9"],height:16,min:0,padding:.1,width:32},(t=>{const i=t.values,n=Math.max.apply(Math,null==t.options.max?i:i.concat(t.options.max)),s=Math.min.apply(Math,null==t.options.min?i:i.concat(t.options.min)),o=t.prepare(t.options.width,t.options.height),l=o.clientWidth,a=o.clientHeight,r=n-s,h=t.options.padding,p=t.fill(),d=t=>t*l/i.length,c=t=>a-(r?(t-s)/r*a:1);for(var u=0;u<i.length;u++){let l,a=d(u+h),m=d(u+1-h)-a,f=i[u],g=c(f),v=g,y=g;r?f<0?v=c(Math.min(n,0)):y=c(Math.max(s,0)):l=1,l=y-v,0==l&&(l=1,n>0&&r&&v--),o.append(e("rect",{"data-value":f,fill:p.call(t,f,u,i),x:a,y:v,width:m,height:l}))}})),n.register("line",{delimiter:",",fill:"#c6d9fd",height:16,min:0,stroke:"#4d89f9",strokeWidth:1,width:32},(t=>{const i=t.values;1==i.length&&i.push(i[0]);const n=Math.max.apply(Math,null==t.options.max?i:i.concat(t.options.max)),s=Math.min.apply(Math,null==t.options.min?i:i.concat(t.options.min)),o=t.prepare(t.options.width,t.options.height),l=t.options.strokeWidth,a=o.clientWidth,r=o.clientHeight-l,h=n-s,p=t=>{let e=r;return h&&(e-=(t-s)/h*r),e+l/2};let d=p(Math.max(s,0)),c=[0,d];for(var u=0;u<i.length;u++)c.push(u*(a/(i.length-1)),p(i[u]));c.push(a,d),t.options.fill&&o.append(e("polygon",{fill:t.options.fill,points:c.join(" ")})),l&&o.append(e("polyline",{fill:"none",points:c.slice(2,c.length-2).join(" "),stroke:t.options.stroke,"stroke-width":l,"stroke-linecap":"square"}))}));const l=function(t,e,i){const s=new n(t,e,i);return s.mount(),s};return l.defaults=n.defaults,l.graphers=n.graphers,l}();
data/config/importmap.rb CHANGED
@@ -1 +1,2 @@
1
- pin 'peity', to: 'peity_vanilla.esm.js'
1
+ pin 'peity-vanilla', to: 'peity-vanilla.esm.js'
2
+ pin 'peity-vanilla-rails', to: 'peity-vanilla-rails.esm.js'
@@ -1,14 +1,9 @@
1
1
  module PeityVanillaRails
2
2
  class Railtie < ::Rails::Engine
3
- PRECOMPILE_ASSETS = Dir[root.join("app/assets/javascripts/**/*")]
4
- initializer 'peity_vanilla_rails.assets' do |app|
5
- if app.config.respond_to?(:assets)
6
- app.config.assets.precompile += PRECOMPILE_ASSETS
7
- end
8
- end
9
-
3
+ PRECOMPILE_ASSETS = %w( peity-vanilla-rails.esm.js peity-vanilla.esm.js )
10
4
  initializer "peity_vanilla_rails.importmap", before: "importmap" do |app|
11
5
  if Rails.application.respond_to?(:importmap)
6
+ app.config.assets.precompile += PRECOMPILE_ASSETS
12
7
  app.config.importmap.paths << root.join("config/importmap.rb")
13
8
  end
14
9
  end
@@ -1,52 +1,28 @@
1
1
  module PeityVanillaRails
2
2
  module Helpers
3
-
4
- def peity_line_chart(data, options: {})
5
- peity_chart(data, "line", ",", options: options)
3
+
4
+ def peity_line_chart(data, id: nil, options: {})
5
+ peity_chart(data, "line", ",", id: id, options: options)
6
6
  end
7
7
 
8
- def peity_bar_chart(data, options: {})
9
- peity_chart(data, "bar", ",", options: options)
8
+ def peity_bar_chart(data, id: nil, options: {})
9
+ peity_chart(data, "bar", ",", id: id, options: options)
10
10
  end
11
11
 
12
- def peity_pie_chart(data, options: {})
13
- peity_chart(data, "pie", "/", options: options)
12
+ def peity_pie_chart(data, id: nil, options: {})
13
+ peity_chart(data, "pie", "/", id: id, options: options)
14
14
  end
15
15
 
16
- def peity_donut_chart(data, options: {})
17
- peity_chart(data, "donut", "/", options: options)
16
+ def peity_donut_chart(data, id: nil, options: {})
17
+ peity_chart(data, "donut", "/", id: id, options: options)
18
18
  end
19
19
 
20
- def peity_chart(value, type, delimiter, options: {})
20
+ def peity_chart(value, type, delimiter, id: nil, options: {})
21
21
  value = value.is_a?(Array) ? value.join(delimiter) : value
22
- id = "peity_charts_#{Digest::SHA1.hexdigest([Time.now, rand].join)}"
23
- [
24
- tag.span(class: 'peity_charts', id: id, "data-peity" => options.to_json) do
25
- value
26
- end,
27
- tag.script do
28
- %Q{
29
- function init_peity_charts_#{id}() {
30
- var element = document.getElementById("#{id}");
31
-
32
- if (!element) {
33
- return;
34
- }
35
-
36
- if(element.getAttribute("data-_peity")) {
37
- return;
38
- }
39
-
40
- window.peity(element, "#{type}");
41
- }
42
-
43
- window.addEventListener('load', init_peity_charts_#{id});
44
- window.addEventListener('turbo:load', init_peity_charts_#{id});
45
- window.addEventListener('turbolinks:load', init_peity_charts_#{id});
46
- }.html_safe
47
- end
48
- ].join.html_safe
49
- end
22
+ tag.span(id: id, class: 'peity_charts', peity: type, style: 'display: none;', data: { peity: options.to_json }) do
23
+ value
24
+ end
25
+ end
50
26
 
51
27
  end
52
28
  end
@@ -1,3 +1,3 @@
1
1
  module PeityVanillaRails
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: peity_vanilla_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Kasyanchuk
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-05-01 00:00:00.000000000 Z
12
+ date: 2022-05-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -78,9 +78,10 @@ files:
78
78
  - MIT-LICENSE
79
79
  - README.md
80
80
  - Rakefile
81
- - app/assets/javascripts/peity_vanilla.esm.js
82
- - app/assets/javascripts/peity_vanilla.js
83
- - app/assets/javascripts/peity_vanilla.js.map
81
+ - app/assets/javascripts/peity-vanilla-rails.esm.js
82
+ - app/assets/javascripts/peity-vanilla-rails.min.js
83
+ - app/assets/javascripts/peity-vanilla.esm.js
84
+ - app/assets/javascripts/peity-vanilla.min.js
84
85
  - config/importmap.rb
85
86
  - lib/peity_vanilla_rails.rb
86
87
  - lib/peity_vanilla_rails/engine.rb
@@ -108,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
109
  - !ruby/object:Gem::Version
109
110
  version: '0'
110
111
  requirements: []
111
- rubygems_version: 3.2.3
112
+ rubygems_version: 3.3.7
112
113
  signing_key:
113
114
  specification_version: 4
114
115
  summary: Sparklines are small but intense charts.