UNPKG

3.55 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 !== 'none' ||
75 issues.memory.heapTotal !== 'none' ||
76 issues.memory.heapUsage !== 'none' ||
77 issues.memory.rss !== 'none'
78
79 const issuesAsData = [
80 new Issue('cpu', issues.cpu !== 'none', 'CPU Usage'),
81 new Issue('memory', memory, 'Memory Usage'),
82 new Issue('delay', issues.delay !== 'none', 'Event Loop Delay'),
83 new Issue('handles', issues.handles !== 'none', 'Active Handles')
84 ].filter((issue) => issue.detected)
85
86 this.details
87 .selectAll('li')
88 .data(issuesAsData)
89 .enter()
90 .append('li')
91 .on('click', (d) => this.emit('click', d.graphId))
92 .on('mouseover', (d) => this.emit('hover-in', d.graphId))
93 .on('mouseout', (d) => this.emit('hover-out', d.graphId))
94 .append('span')
95 .text((d) => d.title)
96
97 // Set title text now, such that the width is calculated correctly
98 this._setTitleText(content.title)
99 this.fullTitleWidth = getTextNodeBoundingRect(this.titleTextNode).width
100 }
101
102 draw () {
103 const content = categories.getContent(this.analysis.issueCategory)
104
105 // If there is not enough space, shorten the title text
106 const titleNode = this.title.node()
107 if (parseInt(window.getComputedStyle(titleNode).width) < this.fullTitleWidth) {
108 this._setTitleText(content.issue ? 'issue' : 'no issue')
109 } else {
110 this._setTitleText(content.title)
111 }
112 }
113
114 open () {
115 this.opened = true
116 this.container.classed('open', true)
117 }
118
119 close () {
120 this.opened = false
121 this.container.classed('open', false)
122 }
123}
124
125module.exports = new Alert()