UNPKG

3.43 kBJavaScriptView Raw
1'use strict'
2
3const d3 = require('./d3.js')
4const icons = require('./icons.js')
5const categories = require('./categories.js')
6const EventEmitter = require('events')
7
8function getTextNodeBoundingRect (textNode) {
9 const range = document.createRange()
10 range.selectNode(textNode)
11 return range.getBoundingClientRect()
12}
13
14class Issue {
15 constructor (id, detected, title) {
16 this.graphId = `graph-${id}`
17 this.detected = detected
18 this.title = title
19 }
20}
21
22class Alert extends EventEmitter {
23 constructor () {
24 super()
25
26 this.analysis = null
27 this.opened = false
28 this.fullTitleWidth = null
29
30 this.container = d3.select('#alert')
31
32 this.summary = this.container.append('div')
33 .classed('summary', true)
34 .on('click', () => {
35 if (this.container.classed('has-issue')) this.emit(this.opened ? 'close' : 'open')
36 })
37
38 this.alert = this.summary.append('svg')
39 .classed('alert', true)
40 .call(icons.insertIcon('warning'))
41
42 this.title = this.summary.append('div')
43 .classed('title', true)
44
45 this.titleTextNode = document.createTextNode('')
46 this.title.node().appendChild(this.titleTextNode)
47
48 this.toggle = this.summary.append('div')
49 .classed('toggle', true)
50 this.toggle.append('svg')
51 .classed('arrow-down', true)
52 .call(icons.insertIcon('arrow-down'))
53 this.toggle.append('svg')
54 .classed('arrow-up', true)
55 .call(icons.insertIcon('arrow-up'))
56
57 this.details = this.container.append('ul')
58 .classed('details', true)
59 }
60
61 _setTitleText (title) {
62 this.titleTextNode.textContent = `Detected ${title}`
63 }
64
65 setData (data) {
66 this.analysis = data.analysis
67
68 // Set issue marker
69 const content = categories.getContent(this.analysis.issueCategory)
70 this.container.classed('has-issue', content.issue)
71
72 // Set items
73 const issues = this.analysis.issues
74 const memory = issues.memory.external || issues.memory.heapTotal ||
75 issues.memory.heapUsage || issues.memory.rss
76
77 const issuesAsData = [
78 new Issue('cpu', issues.cpu, 'CPU Usage'),
79 new Issue('memory', memory, 'Memory Usage'),
80 new Issue('delay', issues.delay, 'Event Loop Delay'),
81 new Issue('handles', issues.handles, 'Active Handles')
82 ].filter((issue) => issue.detected)
83
84 this.details
85 .selectAll('li')
86 .data(issuesAsData)
87 .enter()
88 .append('li')
89 .on('click', (d) => this.emit('click', d.graphId))
90 .on('mouseover', (d) => this.emit('hover-in', d.graphId))
91 .on('mouseout', (d) => this.emit('hover-out', d.graphId))
92 .append('span')
93 .text((d) => d.title)
94
95 // Set title text now, such that the width is calculated correctly
96 this._setTitleText(content.title)
97 this.fullTitleWidth = getTextNodeBoundingRect(this.titleTextNode).width
98 }
99
100 draw () {
101 const content = categories.getContent(this.analysis.issueCategory)
102
103 // If there is not enough space, shorten the title text
104 const titleNode = this.title.node()
105 if (parseInt(window.getComputedStyle(titleNode).width) < this.fullTitleWidth) {
106 this._setTitleText(content.issue ? 'issue' : 'no issue')
107 } else {
108 this._setTitleText(content.title)
109 }
110 }
111
112 open () {
113 this.opened = true
114 this.container.classed('open', true)
115 }
116
117 close () {
118 this.opened = false
119 this.container.classed('open', false)
120 }
121}
122
123module.exports = new Alert()