Skip to content

Commit 52b2360

Browse files
nerumo0xbrayo
andauthored
refactor: migrate timeline.js to typescript (#680)
* refactor: migrate timeline.js to typescript * refactor: migrate timeline-simple.js to typescript * refactor: migrate periodusage.js to typescript * refactor: add an interface for chart data makes it more typesafe --------- Co-authored-by: Brayo <vukubrian@gmail.com>
1 parent 052656a commit 52b2360

File tree

8 files changed

+76
-61
lines changed

8 files changed

+76
-61
lines changed

src/util/color.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ function fallbackColor(str: string): string {
109109
}
110110
}
111111

112-
export function getTitleAttr(bucket: IBucket, e: IEvent) {
112+
export function getTitleAttr(bucket: { type?: string }, e: IEvent) {
113113
if (bucket.type == 'currentwindow') {
114114
return e.data.app;
115115
} else if (bucket.type == 'web.tab.current') {

src/visualizations/PeriodUsage.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ svg {
1717
// NOTE: This is just a Vue.js component wrapper for periodusage.js
1818
// Code should generally go in the framework-independent file.
1919
20-
import periodusage from './periodusage.js';
20+
import periodusage from './periodusage';
2121
2222
export default {
2323
name: 'aw-periodusage',

src/visualizations/TimelineInspect.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ svg {
1010
</style>
1111

1212
<script lang="ts">
13-
// NOTE: This is just a Vue.js component wrapper for timeline.js
13+
// NOTE: This is just a Vue.js component wrapper for timeline.ts
1414
// Code should generally go in the framework-independent file.
1515
16-
import timeline from './timeline.js';
16+
import timeline from './timeline';
1717
1818
export default {
1919
name: 'aw-timeline',

src/visualizations/TimelineSimple.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ svg {
1313
// NOTE: This is just a Vue.js component wrapper for timeline-simple.js
1414
// Code should generally go in the framework-independent file.
1515
16-
import timeline_simple from './timeline-simple.js';
16+
import timeline_simple from './timeline-simple';
1717
1818
export default {
1919
name: 'aw-timeline',

src/visualizations/VisTimeline.vue

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,24 @@ import Color from 'color';
4040
import { buildTooltip } from '../util/tooltip.js';
4141
import { getCategoryColorFromEvent, getTitleAttr } from '../util/color';
4242
import { getSwimlane } from '../util/swimlane.js';
43+
import { IEvent } from '../util/interfaces';
4344
4445
import { Timeline } from 'vis-timeline/esnext';
4546
import 'vis-timeline/styles/vis-timeline-graph2d.css';
4647
import EventEditor from '~/components/EventEditor.vue';
4748
4849
let isAlertWarningShown = false;
50+
51+
interface IChartDataItem {
52+
bucketId: string;
53+
title: string;
54+
tooltip: string;
55+
start: Date;
56+
end: Date;
57+
color: string;
58+
event: IEvent;
59+
swimlane: string;
60+
}
4961
export default {
5062
components: {
5163
EventEditor,
@@ -99,8 +111,8 @@ export default {
99111
return [];
100112
}
101113
},
102-
chartData() {
103-
const data = [];
114+
chartData(): IChartDataItem[] {
115+
const data: IChartDataItem[] = [];
104116
_.each(this.bucketsFromEither, bucket => {
105117
if (bucket.events === undefined) {
106118
return;
@@ -114,17 +126,16 @@ export default {
114126
}
115127
events.sort((a, b) => a.timestamp.valueOf() - b.timestamp.valueOf());
116128
_.each(events, e => {
117-
const color = getCategoryColorFromEvent(bucket, e);
118-
data.push([
119-
bucket.id,
120-
getTitleAttr(bucket, e),
121-
buildTooltip(bucket, e),
122-
new Date(e.timestamp),
123-
new Date(moment(e.timestamp).add(e.duration, 'seconds').valueOf()),
124-
color,
125-
getSwimlane(bucket, color, this.swimlane, e),
126-
e,
127-
]);
129+
data.push({
130+
bucketId: bucket.id,
131+
title: getTitleAttr(bucket, e),
132+
tooltip: buildTooltip(bucket, e),
133+
start: new Date(e.timestamp),
134+
end: new Date(moment(e.timestamp).add(e.duration, 'seconds').valueOf()),
135+
color: getCategoryColorFromEvent(bucket, e),
136+
event: e,
137+
swimlane: getSwimlane(bucket, e.color, this.swimlane, e),
138+
});
128139
});
129140
});
130141
return data;
@@ -170,7 +181,7 @@ export default {
170181
if (properties.items.length == 0) {
171182
return;
172183
} else if (properties.items.length == 1) {
173-
const event = this.chartData[properties.items[0]][7];
184+
const event = this.chartData[properties.items[0]].event;
174185
const groupId = this.items[properties.items[0]].group;
175186
const bucketId = _.find(this.groups, g => g.id == groupId).content;
176187
@@ -221,18 +232,18 @@ export default {
221232
});
222233
223234
// Build items
224-
const items = _.map(this.chartData, (row, i) => {
225-
const bgColor = row[5];
235+
const items = _.map(this.chartData, (item, i) => {
236+
const bgColor = item.color;
226237
const borderColor = Color(bgColor).darken(0.3);
227238
return {
228239
id: String(i),
229-
group: row[0],
230-
content: row[1],
231-
title: row[2],
232-
start: moment(row[3]),
233-
end: moment(row[4]),
240+
group: item.bucketId,
241+
content: item.title,
242+
title: item.tooltip,
243+
start: moment(item.start),
244+
end: moment(item.end),
234245
style: `background-color: ${bgColor}; border-color: ${borderColor}`,
235-
subgroup: row[6],
246+
subgroup: item.swimlane,
236247
};
237248
});
238249
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import moment from 'moment';
44

55
import { seconds_to_duration, get_hour_offset } from '../util/time.ts';
66

7-
function create(svg_elem) {
7+
function create(svg_elem: SVGElement) {
88
// Clear element
99
svg_elem.innerHTML = '';
1010
}
1111

12-
function set_status(svg_elem, msg) {
12+
function set_status(svg_elem: SVGElement, msg: string) {
1313
// Select svg canvas
1414
svg_elem.innerHTML = '';
1515
const svg = d3.select(svg_elem);
@@ -29,7 +29,7 @@ const diagramcolor = '#aaa';
2929
const diagramcolor_selected = '#fc5';
3030
const diagramcolor_focused = '#adf';
3131

32-
function update(svg_elem, usage_arr, onPeriodClicked) {
32+
function update(svg_elem: SVGElement, usage_arr, onPeriodClicked) {
3333
const dateformat = 'YYYY-MM-DD';
3434

3535
// No apps, sets status to "No data"
@@ -56,15 +56,15 @@ function update(svg_elem, usage_arr, onPeriodClicked) {
5656
const width = 100 / usage_arr.length - padding;
5757
const center_elem = Math.floor(usage_arr.length / 2);
5858

59-
_.each(usage_arr, (events, i) => {
59+
_.each(usage_arr, (events, i: number) => {
6060
const usage_time = get_usage_time(events);
6161
const height = 85 * (usage_time / longest_usage);
6262
let date = '';
6363
if (events.length > 0) {
6464
// slice off so it's only the day
6565
date = moment(events[0].timestamp).subtract(get_hour_offset(), 'hours').format(dateformat);
6666
}
67-
const color = i == center_elem ? diagramcolor_selected : diagramcolor;
67+
const color = i === center_elem ? diagramcolor_selected : diagramcolor;
6868
const offset = 50;
6969

7070
const x = i * padding + i * width + 0.25 * width;
@@ -95,7 +95,7 @@ function update(svg_elem, usage_arr, onPeriodClicked) {
9595
.attr('ry', 3)
9696
.attr(
9797
'style',
98-
i == center_elem ? 'stroke: black; stroke-width: 1;' : 'stroke: #222; stroke-width: 1;'
98+
i === center_elem ? 'stroke: black; stroke-width: 1;' : 'stroke: #222; stroke-width: 1;'
9999
)
100100
.attr('width', width + '%')
101101
.attr('height', height + offset + '%')
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function set_status(svg_el, text) {
3131
.attr('fill', 'black');
3232
}
3333

34-
function update(svg_el, events, event_type) {
34+
function update(svg_el, events, event_type: string) {
3535
const timeline = d3.select(svg_el);
3636
timeline.selectAll('*').remove();
3737

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
1-
'use strict';
2-
31
/*
42
* Creates a colored timeline where you can hover for more info.
53
*/
64

7-
const d3 = require('d3');
8-
const Color = require('color');
9-
const _ = require('lodash');
10-
const moment = require('moment');
5+
import * as d3 from 'd3';
6+
import Color from 'color';
7+
import _ from 'lodash';
8+
import moment from 'moment';
119

1210
import { getColorFromString } from '../util/color';
13-
1411
import { seconds_to_duration } from '../util/time';
12+
import { IEvent } from '../util/interfaces';
1513

16-
function show_info(container, elem_id) {
17-
const title_event_box = container.querySelector('#' + elem_id);
18-
const titleinfo = container.querySelector('.titleinfo-container');
14+
function show_info(container: HTMLElement, elem_id: string): void {
15+
const title_event_box = container.querySelector('#' + elem_id) as HTMLElement;
16+
const titleinfo = container.querySelector('.titleinfo-container') as HTMLElement;
1917
titleinfo.innerHTML = title_event_box.innerHTML;
20-
titleinfo.style.height = title_event_box.getAttribute('height');
18+
titleinfo.style.height = title_event_box.getAttribute('height') || '';
2119
}
2220

23-
function create(container) {
21+
function create(container: HTMLElement): void {
2422
// Clear element
2523
container.innerHTML = '';
2624

@@ -32,7 +30,7 @@ function create(container) {
3230
.attr('width', '100%');
3331

3432
// Hidden svg image that stores all titleinfo for each timeperiod
35-
d3.select(container).append('div').attr('class', 'titleinfo-list').attr('display', 'none');
33+
d3.select(container).append('div').attr('class', 'titleinfo-list').style('display', 'none');
3634

3735
// Container for titleinfo that has a fixed size and a overflow scroll
3836
const titleinfo_container = document.createElement('div');
@@ -44,14 +42,14 @@ function create(container) {
4442
// Titleinfo box that changes content depending on what was timeperiod was last recently hovered on
4543
d3.select(titleinfo_container)
4644
.append('div')
47-
.attr('width', '100%')
45+
.style('width', '100%')
4846
.attr('class', 'titleinfo-container');
4947
}
5048

51-
function set_status(container, text) {
52-
const timeline_elem = container.querySelector('.apptimeline');
53-
const titleinfo_list_elem = container.querySelector('.titleinfo-list');
54-
const titleinfo_container_elem = container.querySelector('.titleinfo-container');
49+
function set_status(container: HTMLElement, text: string): void {
50+
const timeline_elem = container.querySelector('.apptimeline') as HTMLElement;
51+
const titleinfo_list_elem = container.querySelector('.titleinfo-list') as HTMLElement;
52+
const titleinfo_container_elem = container.querySelector('.titleinfo-container') as HTMLElement;
5553

5654
const timeline = d3.select(timeline_elem);
5755
timeline_elem.innerHTML = '';
@@ -68,14 +66,20 @@ function set_status(container, text) {
6866
.attr('fill', 'black');
6967
}
7068

71-
function update(container, events, showAFK, chunkfunc, eventfunc) {
69+
function update(
70+
container: HTMLElement,
71+
events: IEvent[],
72+
showAFK: boolean,
73+
chunkfunc: (event: IEvent) => string,
74+
eventfunc: (subevent: IEvent) => string
75+
): HTMLElement {
7276
const timeline = d3.select(container.querySelector('.apptimeline')).html(null);
7377
const titleinfo_list = d3.select(container.querySelector('.titleinfo-list')).html(null);
7478
d3.select(container.querySelector('.titleinfo-container')).html(null);
7579

7680
if (events.length <= 0) {
7781
set_status(container, 'No data');
78-
return;
82+
return container;
7983
}
8084

8185
const firstEvent = events[0];
@@ -92,12 +96,12 @@ function update(container, events, showAFK, chunkfunc, eventfunc) {
9296
_.each(events, function (e, i) {
9397
// Timeline rect
9498

95-
let eventX, eventWidth;
99+
let eventX: string | number, eventWidth: number;
96100
if (showAFK) {
97101
const eventBegin = moment(e.timestamp);
98102
eventX = eventBegin.diff(timeStart, 'seconds', true) / secSinceStart;
99103
eventX = eventX * 100 + '%';
100-
eventWidth = (e.duration / secSinceStart) * 100 + '%';
104+
eventWidth = (e.duration / secSinceStart) * 100;
101105
} else {
102106
eventX = curr_x;
103107
eventWidth = (e.duration / totalDuration) * 100;
@@ -112,7 +116,7 @@ function update(container, events, showAFK, chunkfunc, eventfunc) {
112116
.append('rect')
113117
.attr('x', eventX)
114118
.attr('y', 0)
115-
.attr('width', eventWidth)
119+
.attr('width', eventWidth + '%')
116120
.attr('height', 10)
117121
.style('fill', appcolor)
118122
.on('mouseover', () => {
@@ -141,7 +145,7 @@ function update(container, events, showAFK, chunkfunc, eventfunc) {
141145

142146
// Titleinfo
143147
const infolist = infobox.append('table');
144-
_.each(e.data.subevents, function (t) {
148+
_.each(e.data.subevents, function (t: IEvent) {
145149
const inforow = infolist.append('tr');
146150
// Clocktime
147151
const clocktime = moment(t.timestamp).format('HH:mm:ss');
@@ -160,7 +164,7 @@ function update(container, events, showAFK, chunkfunc, eventfunc) {
160164
}
161165

162166
export default {
163-
create: create,
164-
update: update,
165-
set_status: set_status,
167+
create,
168+
update,
169+
set_status,
166170
};

0 commit comments

Comments
 (0)