beyond-rails 0.0.240 → 0.0.242

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27fa06609359c2b1ee1113771ce48fa3c2693f925e1b3f909e85d18c08766f08
4
- data.tar.gz: 9ac7de120419f1aa97f874876694fe9ace1ec2239657199afff0d32980c835d3
3
+ metadata.gz: 0b3bbdec941b619698d3d2056d2f14b9e5ac05a9ceca5da77cae6ed3b92880e4
4
+ data.tar.gz: da7470c133596ff14a1f6223b44877e62c60d0b8519a7cceb1a2c60384aaece5
5
5
  SHA512:
6
- metadata.gz: 1a54840c057c320dce8f19ebefb6388877e20bcebdf728a50cc5b8193822cb2a948e55423ed3802d20f1fa6628486947c79c847ea63a5637462b04a1400633ef
7
- data.tar.gz: 399fd279bfc53c652babd56f10e481ce33bc994df4391588f26729825b971923ce51186ffe64e48238b3135e834b58131e0fd742ab1bad49107a0397f470339a
6
+ metadata.gz: eb5ad03f6463c486d3d4fd258893a89e597f9fba5638eb4a1da711885e93a8e27905b0d247c320534b92a28c62129609fd43bf56d4de5cdcc8b665365a57ce31
7
+ data.tar.gz: d8a09786eea15028edc4d9b38d5531e02c6656707f7f5dcf976aa0e21d2473c7657cf27fb770687b126668033c5170083c7c89422938da4b113ddd4752d18b8d
@@ -4,12 +4,7 @@ import isDef from '../utils/isDef'
4
4
  import isUndef from '../utils/isUndef'
5
5
  import isInt from '../utils/isInt'
6
6
  import { mem, range, sortBy, throttle, uniqBy } from '../utils'
7
-
8
- const defaultBarStyles = [
9
- '#5469d4',
10
- '#7c54d4',
11
- '#a254d4'
12
- ]
7
+ import { DEFAULT_CHART_STYLES } from '../consts'
13
8
 
14
9
  @supportDom
15
10
  @chartCommon
@@ -35,8 +30,8 @@ export default class BarChart {
35
30
  this.yLabelMargin = isDef(options.yLanelMargin) ? options.yLabelMargin : 14
36
31
 
37
32
  this.fontSize = options.fontSize || 12
38
- this.bgColor = options.bgColor || '#fff'
39
- this.barStyles = options.barStyles || defaultBarStyles
33
+ this.bg = options.bg || '#fff'
34
+ this.barStyles = options.barStyles || DEFAULT_CHART_STYLES
40
35
 
41
36
  this.yLabelRows = []
42
37
  this.barPosMap = new Map()
@@ -3,6 +3,7 @@ import chartCommon from '../decorators/chartCommon'
3
3
  import isDef from '../utils/isDef'
4
4
  import isUndef from '../utils/isUndef'
5
5
  import { mem, range, sortBy, throttle, uniqBy } from '../utils'
6
+ import { DEFAULT_CHART_STYLES } from '../consts'
6
7
 
7
8
  /**
8
9
  * -------------------------------------------------------------------------------------------------
@@ -27,11 +28,6 @@ import { mem, range, sortBy, throttle, uniqBy } from '../utils'
27
28
  * v |
28
29
  * --------------------------------------------------------------------------------------------------
29
30
  **/
30
- const defaultLineStyles = [
31
- '#5469d4',
32
- '#7c54d4',
33
- '#a254d4'
34
- ]
35
31
 
36
32
  @supportDom
37
33
  @chartCommon
@@ -56,16 +52,15 @@ export default class LineChart {
56
52
  this.xLabelMargin = isDef(options.xLabelMargin) ? options.xLabelMargin : 10
57
53
  this.yLabelMargin = isDef(options.yLanelMargin) ? options.yLabelMargin : 10
58
54
 
59
- this.lineStyles = options.lineStyles || defaultLineStyles
55
+ this.lineStyles = options.lineStyles || DEFAULT_CHART_STYLES
60
56
 
61
- this.bgColor = options.bgColor || '#fff'
57
+ this.bg = options.bg || '#fff'
62
58
  this.fontSize = options.fontSize || 12
63
59
 
64
60
  this.xStep = options.xStep
65
61
  this.yStep = options.yStep
66
62
 
67
63
  this.lineLabels = options.lineLabels || []
68
- this.lineLabelMargin = isDef(options.lineLabelMargin) ? options.lineLabelMargin : 20
69
64
 
70
65
  this.pointPosMap = new Map()
71
66
  this.xLabelRows = []
@@ -78,6 +73,7 @@ export default class LineChart {
78
73
  this.setDpr()
79
74
  this.setDomSizeIfNeeded()
80
75
  this.setCanvas()
76
+ this.setLabelBox()
81
77
  this.clear()
82
78
  this.bindMedia()
83
79
  this.bindPointMouseOver()
@@ -93,8 +89,7 @@ export default class LineChart {
93
89
  }
94
90
 
95
91
  get contentHeight() {
96
- return this.height - (this.yPadding * 2) - this.xLabelMargin -
97
- this.xLabelHeight - this.lineLabelBoxHeight
92
+ return this.height - (this.yPadding * 2) - this.xLabelMargin - this.xLabelHeight
98
93
  }
99
94
 
100
95
  get firstX() {
@@ -115,17 +110,6 @@ export default class LineChart {
115
110
  return yLabelRows[yLabelRows.length - 1].value
116
111
  }
117
112
 
118
- get lineLabelHeight() {
119
- return this.fontSize
120
- }
121
-
122
- get lineLabelBoxHeight() {
123
- if (this.lineLabels.length > 0) {
124
- return this.lineLabelMargin + this.lineLabelHeight
125
- }
126
- return 0
127
- }
128
-
129
113
  get xAxisStart() {
130
114
  return this.xPadding + (this.xLabelWidth / 2)
131
115
  }
@@ -145,7 +129,7 @@ export default class LineChart {
145
129
  }
146
130
 
147
131
  get yAxisStart() {
148
- return this.height - this.yPadding - this.lineLabelBoxHeight -
132
+ return this.height - this.yPadding -
149
133
  this.xLabelHeight - this.xLabelMargin + (this.yLabelHeight / 2)
150
134
  }
151
135
 
@@ -185,7 +169,6 @@ export default class LineChart {
185
169
  this.drawYAxis()
186
170
  this.drawBgLines()
187
171
  this.drawLines()
188
- this.drawLineLables()
189
172
  }
190
173
 
191
174
  drawBgLines() {
@@ -232,29 +215,6 @@ export default class LineChart {
232
215
  })
233
216
  }
234
217
 
235
- drawLineLables() {
236
- const { ctx, lineStyles, lineLabelHeight } = this
237
- const rectSize = 7
238
-
239
- const rectGutter = 7
240
- const labelGutter = 14
241
- const rectMargin = 1
242
- const y = this.height - this.yPadding
243
- let x = this.xPadding
244
-
245
- this.lineLabels.forEach((name, i) => {
246
- ctx.fillStyle = lineStyles[i] || '#000'
247
- ctx.fillRect(x, y - lineLabelHeight + rectMargin, rectSize, rectSize)
248
-
249
- x += (rectSize + rectGutter)
250
- ctx.fillStyle = '#000'
251
- ctx.textAlign = 'left'
252
- ctx.textBaseline = 'top'
253
- ctx.fillText(name, x, y - lineLabelHeight)
254
- x += (ctx.measureText(name).width + labelGutter)
255
- })
256
- }
257
-
258
218
  clearVerticalLine() {
259
219
  const { ctx } = this.firstLayer
260
220
  ctx.clearRect(0, 0, this.width, this.height)
@@ -279,7 +239,7 @@ export default class LineChart {
279
239
  drawXAxis() {
280
240
  const { ctx, firstX, xLabelRows, xAxisStart, xRatio } = this
281
241
 
282
- const y = this.height - this.yPadding - this.lineLabelBoxHeight
242
+ const y = this.height - this.yPadding
283
243
 
284
244
  const scaleMargin = 4
285
245
  const scaleSize = 4
@@ -538,7 +498,10 @@ export default class LineChart {
538
498
  return this.raf(() => this.clear())
539
499
  }
540
500
  this.setPointPos()
541
- this.raf(() => this.draw())
501
+ this.raf(() => {
502
+ this.drawLabels(this.lineLabels, this.lineStyles)
503
+ this.draw()
504
+ })
542
505
  }
543
506
 
544
507
  setPointPos() {
@@ -0,0 +1,241 @@
1
+ import supportDom from '../decorators/supportDom'
2
+ import chartCommon from '../decorators/chartCommon'
3
+ import isDef from '../utils/isDef'
4
+ import isUndef from '../utils/isUndef'
5
+ import { throttle } from '../utils'
6
+ import { DEFAULT_CHART_STYLES } from '../consts'
7
+
8
+ @supportDom
9
+ @chartCommon
10
+ export default class PieChart {
11
+
12
+ constructor(dom, options = {}) {
13
+ this.dom = dom
14
+ this.data = []
15
+ this.total = 0
16
+
17
+ this.options = options
18
+ this.height = options.height
19
+ this.width = options.width
20
+ this.padding = isDef(options.padding) ? options.padding : 30
21
+ this.styles = options.styles || DEFAULT_CHART_STYLES
22
+ this.bg = options.bg || '#fff'
23
+
24
+ this.init()
25
+ }
26
+
27
+ init() {
28
+ this.setDpr()
29
+ this.setDomSizeIfNeeded()
30
+ this.setCanvas()
31
+ this.setLabelBox()
32
+ this.clear()
33
+ this.bindMedia()
34
+ this.bindPointMouseOver()
35
+ }
36
+
37
+ get x() {
38
+ return this.width / 2
39
+ }
40
+
41
+ get y() {
42
+ return this.height / 2
43
+ }
44
+
45
+ get radius() {
46
+ return this.contentWidth / 2
47
+ }
48
+
49
+ get pieWidth() {
50
+ return this.radius * .3
51
+ }
52
+
53
+ get centerCircleRadius() {
54
+ return this.radius - this.pieWidth
55
+ }
56
+
57
+ get contentWidth() {
58
+ return this.width - (this.padding * 2)
59
+ }
60
+
61
+ get contentHeight() {
62
+ return this.height - (this.padding * 2)
63
+ }
64
+
65
+ bindPointMouseOver() {
66
+ if (isUndef(this.options.onPieMouseOver)) {
67
+ return
68
+ }
69
+ if (! ('onmousemove' in this.canvas)) {
70
+ return
71
+ }
72
+ this.addLayer()
73
+ const canvas = this.getHighestCanvas()
74
+ this.addEvent(canvas, 'mousemove', throttle(this.handleMouseMove.bind(this), 30))
75
+ }
76
+
77
+ draw() {
78
+ this.clear()
79
+ this.drawPie()
80
+ }
81
+
82
+ drawPie() {
83
+ const { x, y, radius, centerCircleRadius, ctx, total } = this
84
+
85
+ let distance = 0
86
+
87
+ this.data.forEach((row, i) => {
88
+
89
+ const ratio = (row.value / total)
90
+ const startAngle = Math.PI * (-.5 + 2 * distance)
91
+ const endAngle = Math.PI * (-.5 + 2 * (distance + ratio))
92
+
93
+ const options = {
94
+ style: this.styles[i]
95
+ }
96
+ this.fillArc(ctx, x, y, radius, startAngle, endAngle, options)
97
+ distance += ratio
98
+ })
99
+
100
+ this.fillCircle(ctx, x, y, centerCircleRadius, '#fff')
101
+ }
102
+
103
+ handleDprChange() {
104
+ this.setDpr()
105
+ this.refresh()
106
+ }
107
+
108
+ getPosAngle(x1, y1, x2, y2) {
109
+
110
+ let x = x2
111
+ let y = y2
112
+
113
+ if (x1 >= 0) {
114
+ x -= x1
115
+ }
116
+ if (y1 >= 0) {
117
+ y -= y1
118
+ }
119
+ if (x1 < 0) {
120
+ x += x1
121
+ }
122
+ if (y2 < 0) {
123
+ y += y2
124
+ }
125
+
126
+ let angle = Math.atan2(y, x) * 180 / Math.PI
127
+ if (angle < 0) {
128
+ angle = 180 + (180 + angle)
129
+ }
130
+ return (angle + 90) % 360
131
+ }
132
+
133
+ handleMouseMove(event) {
134
+
135
+ const { x, y } = this
136
+ const canvasMousePos = this.getMousePosInCanvas(event)
137
+ const mousePos = this.getMousePos(canvasMousePos)
138
+ const mouseX = canvasMousePos.x
139
+ const mouseY = canvasMousePos.y
140
+
141
+ const distanceToCenterPoint = Math.sqrt(Math.pow(mouseX - x, 2) +
142
+ Math.pow(mouseY - y, 2))
143
+
144
+ const inCenterCircle = distanceToCenterPoint <= this.centerCircleRadius
145
+
146
+ this.clearSliceGlow()
147
+
148
+ if (inCenterCircle) {
149
+ return this.options.onPieMouseOver(mousePos, null)
150
+ }
151
+
152
+ const inPieCircle = distanceToCenterPoint <= this.radius
153
+ if (! inPieCircle) {
154
+ return this.options.onPieMouseOver(mousePos, null)
155
+ }
156
+ const angle = this.getPosAngle(x, y, mouseX, mouseY)
157
+ const matchedRow = this.data.find(row => {
158
+ return (row.startAngle <= angle) && (angle <= row.endAngle)
159
+ })
160
+ if (matchedRow) {
161
+ this.drawSliceGlow(matchedRow)
162
+ this.options.onPieMouseOver(mousePos, matchedRow)
163
+ }
164
+ }
165
+
166
+ drawSliceGlow(row) {
167
+ const index = this.data.findIndex(r => r === row)
168
+ this.clearSliceGlow()
169
+ const { x, y, radius, centerCircleRadius } = this
170
+ const ctx = this.firstLayer.canvas.getContext('2d')
171
+
172
+ const delta = 90 * Math.PI / 180
173
+ const startAngle = (row.startAngle * Math.PI / 180) - delta
174
+ const endAngle = (row.endAngle * Math.PI / 180) - delta
175
+
176
+ const options = {
177
+ style: this.styles[index],
178
+ alpha: .3
179
+ }
180
+ const radiusDelta = (radius - centerCircleRadius) * .3
181
+ this.fillArc(ctx, x, y, radius + radiusDelta, startAngle, endAngle, options)
182
+ this.fillCircle(this.firstLayer.ctx, x, y, centerCircleRadius, '#fff')
183
+ }
184
+
185
+ clearSliceGlow() {
186
+ const ctx = this.firstLayer.canvas.getContext('2d')
187
+ ctx.clearRect(0, 0, this.width, this.height)
188
+ }
189
+
190
+ refresh() {
191
+ this.raf(() => {
192
+ this.clearCanvasSize(this.canvas)
193
+ this.layers.forEach(layer => this.clearCanvasSize(layer.canvas))
194
+ this.setDomSizeIfNeeded()
195
+ this.setCanvasSize(this.canvas)
196
+ this.layers.forEach(layer => this.setCanvasSize(layer.canvas))
197
+ this.draw()
198
+ })
199
+ }
200
+
201
+ setAngles(data) {
202
+ const { total } = this
203
+ let startAngle = 0
204
+ return data.map(row => {
205
+ const endAngle = startAngle + ((row.value / total) * 360)
206
+ const nextRow = Object.assign({}, row, { startAngle, endAngle })
207
+ startAngle = endAngle
208
+ return nextRow
209
+ })
210
+ }
211
+
212
+ handleLabelMouseOver(index) {
213
+ this.drawSliceGlow(this.data[index])
214
+ }
215
+
216
+ handleLabelMouseLeave(index) {
217
+ this.clearSliceGlow()
218
+ }
219
+
220
+ setData(data) {
221
+ this.total = data.reduce((t, row) => t + row.value, 0)
222
+ this.data = this.setAngles(data)
223
+ this.raf(() => {
224
+ const labels = this.data.map(row => row.label)
225
+ this.drawLabels(labels, this.styles)
226
+ this.draw()
227
+ })
228
+ }
229
+
230
+ destroy() {
231
+ const { dom, canvas } = this
232
+
233
+ this.unbindMedia()
234
+ this.removeAllLayers()
235
+
236
+ if (dom.contains(canvas)) {
237
+ dom.removeChild(canvas)
238
+ dom.style.removeProperty('position')
239
+ }
240
+ }
241
+ }
@@ -3,3 +3,13 @@ import locale from 'date-fns/locale/zh-TW'
3
3
  export const DEFAULT_TIMEZONE = 'Asia/Taipei'
4
4
 
5
5
  export const DEFAULT_LOCALE = locale
6
+
7
+ export const DEFAULT_CHART_STYLES = [
8
+ '#5469d4',
9
+ '#7c54d4',
10
+ '#a254d4',
11
+ '#c040a2',
12
+ '#ff5604',
13
+ '#0be4e3',
14
+ '#00d924'
15
+ ]
@@ -1,6 +1,8 @@
1
1
  import raf from '../utils/raf'
2
2
  import isUndef from '../utils/isUndef'
3
+ import isFn from '../utils/isFn'
3
4
  import { getDomPos, range, toPixel, isFunction } from '../utils'
5
+ import { DEFAULT_CHART_STYLES } from '../consts'
4
6
 
5
7
  export default function chartCommon(target) {
6
8
 
@@ -12,6 +14,7 @@ export default function chartCommon(target) {
12
14
  }
13
15
 
14
16
  init() {
17
+ this.offLabels = []
15
18
  this.layers = []
16
19
  if (isFunction(super.init)) {
17
20
  super.init()
@@ -50,10 +53,15 @@ export default function chartCommon(target) {
50
53
 
51
54
  clear() {
52
55
  const { ctx } = this
53
- ctx.fillStyle = this.bgColor
56
+ ctx.fillStyle = this.bg
54
57
  ctx.fillRect(0, 0, this.width, this.height)
55
58
  }
56
59
 
60
+ getHypotenuse(x1, y1, x2, y2) {
61
+ return Math.sqrt(Math.pow(x2 - x1, 2) +
62
+ Math.pow(y2 - y1, 2))
63
+ }
64
+
57
65
  fillCircle(ctx, x, y, radius, style, alpha) {
58
66
  ctx.save()
59
67
  ctx.beginPath()
@@ -65,6 +73,18 @@ export default function chartCommon(target) {
65
73
  ctx.restore()
66
74
  }
67
75
 
76
+ fillArc(ctx, x, y, radius, startAngle = 0, endAngle = 2 * Math.PI, options = {}) {
77
+ ctx.save()
78
+ ctx.beginPath()
79
+ ctx.arc(x, y, radius, startAngle, endAngle)
80
+ ctx.fillStyle = options.style || '#555'
81
+ ctx.globalAlpha = options.alpha || 1
82
+ ctx.lineTo(x, y)
83
+ ctx.fill()
84
+ ctx.closePath()
85
+ ctx.restore()
86
+ }
87
+
68
88
  getAutoStep(firstValue, lastValue, pointsLength) {
69
89
  return (lastValue - firstValue) / (pointsLength - 1)
70
90
  }
@@ -186,6 +206,52 @@ export default function chartCommon(target) {
186
206
  this.dom.appendChild(canvas)
187
207
  }
188
208
 
209
+ setLabelBox() {
210
+ const box = document.createElement('div')
211
+ box.className = 'chart-box'
212
+ this.labelBox = box
213
+ this.dom.appendChild(box)
214
+ }
215
+
216
+ drawLabels(labels, styles = DEFAULT_CHART_STYLES) {
217
+ if (labels.length <= 0) {
218
+ return
219
+ }
220
+ const { labelBox, handleLabelMouseOver, handleLabelMouseLeave } = this
221
+ this.dom.style.backgroundColor = this.bg
222
+
223
+ this.offLabels.forEach(off => off())
224
+ labelBox.innerHTML = ''
225
+
226
+ this.offLabels.length = 0
227
+
228
+ labels.forEach((label, i) => {
229
+
230
+ const div = document.createElement('div')
231
+ div.className = 'chart-box-item'
232
+
233
+ const square = document.createElement('div')
234
+ square.className = 'chart-box-square'
235
+ square.style.backgroundColor = styles[i]
236
+ div.appendChild(square)
237
+
238
+ const span = document.createElement('span')
239
+ span.textContent = label
240
+ div.appendChild(span)
241
+
242
+ labelBox.appendChild(div)
243
+
244
+ if (isFn(handleLabelMouseOver)) {
245
+ const off = this.addEvent(div, 'mouseover', () => this.handleLabelMouseOver(i))
246
+ this.offLabels.push(off)
247
+ }
248
+ if (isFn(handleLabelMouseLeave)) {
249
+ const off = this.addEvent(div, 'mouseleave', () => this.handleLabelMouseLeave(i))
250
+ this.offLabels.push(off)
251
+ }
252
+ })
253
+ }
254
+
189
255
  setCanvasFontSize(canvas, fontSize) {
190
256
  const ctx = canvas.getContext('2d')
191
257
  const args = ctx.font.split(' ')
@@ -25,7 +25,12 @@ export default function supportDom(target) {
25
25
 
26
26
  addEvent(dom, name, func) {
27
27
  dom.addEventListener(name, func)
28
- this._listeners.push({ dom, name, func })
28
+ const listener = { dom, name, func }
29
+ this._listeners.push(listener)
30
+
31
+ return () => {
32
+ this._listeners = this._listeners.filter(l => l !== listener)
33
+ }
29
34
  }
30
35
 
31
36
  removeEvents() {
@@ -17,6 +17,7 @@ import LineChart from './components/LineChart'
17
17
  import Menu from './components/Menu'
18
18
  import Modal from './components/Modal'
19
19
  import Navbar from './components/Navbar'
20
+ import PieChart from './components/PieChart'
20
21
  import Radio from './components/Radio'
21
22
  import SearchDropdown from './components/SearchDropdown'
22
23
  import Sidebar from './components/Sidebar'
@@ -53,6 +54,7 @@ export {
53
54
  Menu,
54
55
  Modal,
55
56
  Navbar,
57
+ PieChart,
56
58
  Radio,
57
59
  SearchDropdown,
58
60
  Sidebar,
@@ -0,0 +1,3 @@
1
+ export default function isFn(fn) {
2
+ return (typeof fn === 'function')
3
+ }
@@ -12,3 +12,24 @@
12
12
  z-index: 1;
13
13
  border: 1px solid #e8e8e8;
14
14
  }
15
+
16
+ .chart-box {
17
+ display: flex;
18
+ justify-content: center;
19
+ flex-wrap: wrap;
20
+ padding-left: 14px;
21
+ padding-right: 14px;
22
+ padding-bottom: 10px;
23
+ .chart-box-item {
24
+ cursor: default;
25
+ font-size: 12px;
26
+ display: inline-flex;
27
+ align-items: center;
28
+ margin-right: 1.4em;
29
+ .chart-box-square {
30
+ transform: translateY(1px);
31
+ @include size(10px);
32
+ margin-right: .5em;
33
+ }
34
+ }
35
+ }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beyond-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.240
4
+ version: 0.0.242
5
5
  platform: ruby
6
6
  authors:
7
7
  - kmsheng
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-11-06 00:00:00.000000000 Z
12
+ date: 2020-11-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sassc
@@ -140,6 +140,7 @@ files:
140
140
  - src/js/components/Menu.js
141
141
  - src/js/components/Modal.js
142
142
  - src/js/components/Navbar.js
143
+ - src/js/components/PieChart.js
143
144
  - src/js/components/Radio.js
144
145
  - src/js/components/SearchDropdown.js
145
146
  - src/js/components/Sidebar.js
@@ -194,6 +195,7 @@ files:
194
195
  - src/js/utils/getKey.js
195
196
  - src/js/utils/index.js
196
197
  - src/js/utils/isDef.js
198
+ - src/js/utils/isFn.js
197
199
  - src/js/utils/isInt.js
198
200
  - src/js/utils/isStr.js
199
201
  - src/js/utils/isTouchDevice.js