11<template lang="pug">
2- div
3- div( v-for ="cat in category_hierarchy" , style ="padding: 0.1em 0 0.1em 0" )
4- span( :style ="'padding-left: ' + (1.4 * cat.depth - 1) + 'em'" )
5- span( v-if ="cat.depth > 0" , style ="opacity: 0.7" ) #[ icon.mr-1 ( name ="caret-right" ) ]
2+ div( style ="font-size: 0.9em" )
3+ div( v-for ="cat in category_hierarchy" , style ="padding: 0.1em 0 0.1em 0" @click ="toggle(cat)" v-if ="parents_expanded(cat)" )
4+ span( :style ="'padding-left: ' + (1.4 * cat.depth) + 'em'" )
5+
6+ // icon
7+ span( v-if ="cat.children.length > 0" , style ="opacity: 0.8" )
8+ b( v-if ="!expanded.has(cat.name_pretty)" )
9+ icon.mr-1 ( name ="regular/plus-square" , scale ="0.8" )
10+ b( v-else )
11+ icon.mr-1 ( name ="regular/minus-square" , scale ="0.8" )
12+ span( v-else , style ="opacity: 0.6" )
13+ icon( name ="circle" , scale ="0.4" , style ="margin-left: 1em; margin-right: 1.22em;" )
14+
15+ // title
616 | {{cat.subname}}
17+
18+ // time
719 span( style ="float: right" )
8- | {{cat.duration | friendlyduration}}
20+ span( v-if ="show_perc" )
21+ | {{Math.round(100 * cat.duration / total_duration, 1)}}%
22+ span( v-else )
23+ | {{cat.duration | friendlyduration}}
24+ hr
25+ // TODO: Make configurable in a cleaner way (figure out a way to configure visualizations generally)
26+ b-checkbox( v-model ="show_perc" size ="sm" ) Show percent
927</template >
1028
1129<script >
12- import ' vue-awesome/icons/caret-right' ;
30+ import ' vue-awesome/icons/circle' ;
31+ import ' vue-awesome/icons/regular/plus-square' ;
32+ import ' vue-awesome/icons/regular/minus-square' ;
1333const _ = require (' lodash' );
1434const classes = require (' ~/util/classes.ts' );
1535
36+ function _get_child_cats (cat , all_cats ) {
37+ return _ .filter (all_cats, c => _ .isEqual (c .parent , cat .name ));
38+ }
39+
40+ function _assign_children (parent , all_cats ) {
41+ const child_cats = _get_child_cats (parent, all_cats);
42+ // Recurse
43+ _ .map (child_cats, c => _assign_children (c, all_cats));
44+ parent .children = _ .sortBy (child_cats, cc => - cc .duration );
45+ }
46+
47+ // Flattens the category hierarchy
48+ function _flatten_hierarchy (c ) {
49+ if (! c .children ) return [];
50+ return _ .flattenDeep ([c, _ .map (c .children , cc => _flatten_hierarchy (cc))]);
51+ }
52+
1653export default {
1754 name: ' aw-categorytree' ,
1855 props: {
1956 events: { type: Array },
2057 },
58+ data : function () {
59+ return {
60+ expanded: new Set (),
61+ show_perc: false ,
62+ };
63+ },
2164 computed: {
65+ total_duration : function () {
66+ // sum top-level categories
67+ const top_c = _ .filter (this .category_hierarchy , c => c .depth == 0 );
68+ return _ .sumBy (top_c, c => c .duration );
69+ },
2270 category_hierarchy : function () {
2371 const events = JSON .parse (JSON .stringify (this .events ));
2472
@@ -39,33 +87,43 @@ export default {
3987 return c;
4088 });
4189
42- function _get_child_cats (cat , all_cats ) {
43- return _ .filter (all_cats, c => _ .isEqual (c .parent , cat .name ));
44- }
45-
46- function _assign_children (parent , all_cats ) {
47- const child_cats = _get_child_cats (parent, all_cats);
48- // Recurse
49- _ .map (child_cats, c => _assign_children (c, all_cats));
50- parent .children = _ .sortBy (child_cats, cc => - cc .duration );
51- }
52-
5390 const cats_with_depth0 = _ .sortBy (
5491 _ .filter (cats, c => c .depth == 0 ),
5592 c => - c .duration
5693 );
5794 _ .map (cats_with_depth0, c => _assign_children (c, cats));
5895
59- // Flattens the category hierarchy
60- function _flatten_hierarchy (c ) {
61- if (! c .children ) return [];
62- return _ .flattenDeep ([c, _ .map (c .children , cc => _flatten_hierarchy (cc))]);
63- }
6496 cats = _ .flatten (_ .map (cats_with_depth0, c => _flatten_hierarchy (c)));
6597 // console.log(cats);
6698 // TODO: If a category has children, but also activity attributed directly to the parent that does not belong to a child, then create a "Other" child containing the activity.
6799 return cats;
68100 },
69101 },
102+ methods: {
103+ get_category : function (cat_arr ) {
104+ return _ .find (this .category_hierarchy , c => _ .isEqual (c .name , cat_arr));
105+ },
106+ toggle : function (cat ) {
107+ if (this .expanded .has (cat .name_pretty )) {
108+ this .expanded .delete (cat .name_pretty );
109+ } else {
110+ this .expanded .add (cat .name_pretty );
111+ }
112+ // needed to trigger update, since Set isn't reactive in Vue 2
113+ this .expanded = new Set (this .expanded );
114+ },
115+ parents_expanded : function (cat ) {
116+ if (cat === undefined || ! cat .parent ) {
117+ // top-level category
118+ return true ;
119+ }
120+ return (
121+ // Check grandparents recursively
122+ this .parents_expanded (this .get_category (cat .parent )) &&
123+ // Check parent
124+ this .expanded .has (cat .parent .join (' >' ))
125+ );
126+ },
127+ },
70128};
71129 </script >
0 commit comments