git.fiddlerwoaroof.com
Browse code

Initial commit

fiddlerwoaroof authored on 11/03/2017 23:02:22
Showing 9 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,7 @@
1
+{
2
+  "presets": [
3
+    ["latest", {
4
+      "es2015": { "modules": false }
5
+    }]
6
+  ]
7
+}
0 8
new file mode 100644
... ...
@@ -0,0 +1,5 @@
1
+.DS_Store
2
+node_modules/
3
+dist/
4
+npm-debug.log
5
+yarn-error.log
0 6
new file mode 100644
... ...
@@ -0,0 +1,18 @@
1
+# time-tracker
2
+
3
+> A Vue.js project
4
+
5
+## Build Setup
6
+
7
+``` bash
8
+# install dependencies
9
+npm install
10
+
11
+# serve with hot reload at localhost:8080
12
+npm run dev
13
+
14
+# build for production with minification
15
+npm run build
16
+```
17
+
18
+For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader).
0 19
new file mode 100644
... ...
@@ -0,0 +1,11 @@
1
+<!DOCTYPE html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="utf-8">
5
+    <title>time-tracker</title>
6
+  </head>
7
+  <body>
8
+    <div id="app"></div>
9
+    <script src="/dist/build.js"></script>
10
+  </body>
11
+</html>
0 12
new file mode 100644
... ...
@@ -0,0 +1,29 @@
1
+{
2
+  "name": "time-tracker",
3
+  "description": "A Vue.js project",
4
+  "version": "1.0.0",
5
+  "author": "fiddlerwoaroof <fiddlerwoaroof@gmail.com>",
6
+  "private": true,
7
+  "scripts": {
8
+    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot --host 0.0.0.0",
9
+    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
10
+  },
11
+  "dependencies": {
12
+    "vue": "^2.2.1",
13
+    "vuex": "^2.2.1"
14
+  },
15
+  "devDependencies": {
16
+    "babel-core": "^6.0.0",
17
+    "babel-loader": "^6.0.0",
18
+    "babel-preset-latest": "^6.0.0",
19
+    "cross-env": "^3.0.0",
20
+    "css-loader": "^0.25.0",
21
+    "file-loader": "^0.9.0",
22
+    "node-sass": "^4.5.0",
23
+    "sass-loader": "^5.0.1",
24
+    "vue-loader": "^11.1.4",
25
+    "vue-template-compiler": "^2.2.1",
26
+    "webpack": "^2.2.0",
27
+    "webpack-dev-server": "^2.2.0"
28
+  }
29
+}
0 30
new file mode 100644
... ...
@@ -0,0 +1,213 @@
1
+<template>
2
+    <div id="app">
3
+	<header>
4
+	    <h1>
5
+		Timer
6
+	    </h1>
7
+	    <div>
8
+		<button @click="mark">Go</button>
9
+	    </div>
10
+	</header>
11
+	<ol>
12
+	    <li class=header>
13
+		<div>Time</div><div>Lap Time</div><div>Cumulative</div>
14
+	    </li>
15
+		
16
+	    <li v-for="[time,diff,cum,new_annotation] in times">
17
+		<div class="time">{{formatTime(time.time)}}</div>
18
+		<div class="diff"><div class="time">{{formatDiff(diff)[0]}}</div><div class="mills">.{{formatDiff(diff)[1]}}</div></div>
19
+		<div class="cum"><div class="time">{{formatDiff(cum)[0]}}</div><div class="mills">.{{formatDiff(cum)[1]}}</div></div>
20
+		<div class="actions">
21
+		    <form @submit.prevent="annotate(time.id, new_annotation)">
22
+		    <input type=text v-model="new_annotation">
23
+		    <button type="submit">+</button>
24
+		    </form>
25
+		</div>
26
+		<ul v-show="time.annotations.length > 0">
27
+		    <li class="annotation" v-for="annotation in time.annotations">
28
+			<div>
29
+			    {{annotation.text}}
30
+			</div>
31
+			<time datetime="annotation.time">{{formatTime(annotation.time)}}</time>
32
+		    </li>
33
+		</ul>
34
+		
35
+	    </li>
36
+	</ol>
37
+    </div>
38
+  </template>
39
+
40
+<script>
41
+
42
+import {mapGetters} from 'vuex';
43
+
44
+export default {
45
+    name: 'app',
46
+    data () {
47
+	return {
48
+	    msg: 'Welcome to Your Vue.js App',
49
+	}
50
+    },
51
+
52
+    computed: {
53
+	times() {
54
+	    let result = [];
55
+	    for (let cur of this.$store.state.times) {
56
+		let diff = 0, cum = 0;
57
+		if (cur.id > 0) {
58
+		    cum = cur.time - result[cur.id-1][0].time;
59
+		    diff = cur.time-result[0][0].time
60
+		}
61
+
62
+		result.unshift([cur, diff, cum, '']);
63
+	    }
64
+	    return result;
65
+	},
66
+    },
67
+
68
+    methods: {
69
+	annotate(idx, annotation) {
70
+	    this.$store.commit('annotate', {idx, annotation});
71
+	},
72
+	mark() {
73
+	    this.$store.commit('add');
74
+	},
75
+
76
+	formatTime: function (time) {
77
+	    return (new Intl.DateTimeFormat('en-US', {
78
+		year:'numeric', month:'numeric', day:'numeric', hour:'2-digit', minute: '2-digit', second:'2-digit'
79
+	    })).format(time);
80
+	},
81
+
82
+	formatDiff(diff) {
83
+	    let secs = Math.floor(diff/1000),
84
+		mills = diff % 1000,
85
+		mins = Math.floor(secs/60);
86
+	    secs = secs % 60;
87
+	    mins = `${mins}`;
88
+	    if (mills.length < 3) {
89
+		let pad = '0'*(3-mills.length);
90
+		mills = pad + mills;
91
+	    }
92
+	    let result = `${secs}`
93
+	    if (mins > 0) {
94
+		result = `${mins}:${result.padStart(2,'0')}`;
95
+	    }
96
+	    return [result,mills];
97
+	}
98
+    },
99
+}
100
+    </script>
101
+
102
+<style lang="scss">
103
+  * { box-sizing: border-box; }
104
+    #app {
105
+	font-family: 'Lato', Helvetica, Arial, sans-serif;
106
+	-webkit-font-smoothing: antialiased;
107
+	-moz-osx-font-smoothing: grayscale;
108
+	text-align: right;
109
+	color: #2c3e50;
110
+    }
111
+
112
+    header {
113
+	display: grid;
114
+	grid-template-columns: 75fr 25fr;
115
+	height: 3em;
116
+	line-height: 3em;
117
+	padding: 0;
118
+    }
119
+    
120
+    h1, h2 {
121
+	text-align: center;
122
+	font-size: 1.25em;
123
+	padding: 0;
124
+	margin: 0;
125
+    }
126
+
127
+    ul {
128
+	list-style-type: none;
129
+	padding: 0;
130
+    }
131
+
132
+    a {
133
+	color: #42b983;
134
+    }
135
+
136
+    ol {
137
+	position: absolute;
138
+	width: 100%;
139
+	top: 4em;
140
+	bottom: 0;
141
+	left: 0;
142
+	margin: 0;
143
+	padding: 0 1em;
144
+	padding-top: 3.25em;
145
+	overflow-y: scroll;
146
+	overflow-x: auto;
147
+    }
148
+
149
+    ol > li {
150
+	display: grid;
151
+	grid-template-columns: 25fr 12.5fr 12.5fr 25fr 25fr;
152
+	padding: 0.5em;
153
+	margin-bottom: 0.25em;
154
+    }
155
+
156
+    li.header {
157
+	position: fixed;
158
+	top: 4em;
159
+	left: 1em;
160
+	right: 1em;
161
+	background: #eee;
162
+    }
163
+
164
+    ul {
165
+	/* grid-column-start: 2; */
166
+	/* grid-column-end: span 2; */
167
+	padding: 0.25em;
168
+	margin: 0.25em;
169
+	padding-right: 0;
170
+	margin-right: 0;
171
+    }
172
+    ul > li {
173
+	padding: 0.2em 1em;
174
+	background: #ffb;
175
+	font-style: italic;
176
+    }
177
+    ul > li+li {
178
+	border-top: 0.25em double #eee;
179
+    }
180
+
181
+    .diff, .cum {
182
+	text-align: right;
183
+    }
184
+    
185
+    .diff div, .cum div {
186
+	display: inline-block;
187
+    }
188
+
189
+    .diff .time, .cum .time {
190
+	max-width: 80%;
191
+	text-align: right;
192
+    }
193
+
194
+    .diff .mills, .cum .mills {
195
+	width: 20%;
196
+	text-align: left;
197
+    }
198
+
199
+    button {
200
+	height: 100%;
201
+    }
202
+
203
+    .annotation {
204
+	color: #888;
205
+	}
206
+    .annotation time::before {
207
+	content: "— "
208
+    }
209
+    .annotation time {
210
+	font-size: 60%;
211
+
212
+    }
213
+</style>
0 214
new file mode 100644
1 215
Binary files /dev/null and b/src/assets/logo.png differ
2 216
new file mode 100644
... ...
@@ -0,0 +1,23 @@
1
+import Vue from 'vue';
2
+import Vuex from 'vuex';
3
+import App from './App.vue';
4
+Vue.use(Vuex);
5
+
6
+new Vue({
7
+    el: '#app',
8
+    render: h => h(App),
9
+    store: new Vuex.Store({
10
+	state: {
11
+	    times: []
12
+	},
13
+	mutations: {
14
+	    add ({times}) {
15
+		times.push({id: times.length, time: new Date(), annotations: []});
16
+	    },
17
+
18
+	    annotate({times}, {idx, annotation}) {
19
+		times[idx].annotations.unshift({time: new Date(), text: annotation});
20
+	    }
21
+	}
22
+    })
23
+})
0 24
new file mode 100644
... ...
@@ -0,0 +1,75 @@
1
+var path = require('path')
2
+var webpack = require('webpack')
3
+
4
+module.exports = {
5
+  entry: './src/main.js',
6
+  output: {
7
+    path: path.resolve(__dirname, './dist'),
8
+    publicPath: '/dist/',
9
+    filename: 'build.js'
10
+  },
11
+  module: {
12
+    rules: [
13
+      {
14
+        test: /\.vue$/,
15
+        loader: 'vue-loader',
16
+        options: {
17
+          loaders: {
18
+            // Since sass-loader (weirdly) has SCSS as its default parse mode, we map
19
+            // the "scss" and "sass" values for the lang attribute to the right configs here.
20
+            // other preprocessors should work out of the box, no loader config like this necessary.
21
+            'scss': 'vue-style-loader!css-loader!sass-loader',
22
+            'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
23
+          }
24
+          // other vue-loader options go here
25
+        }
26
+      },
27
+      {
28
+        test: /\.js$/,
29
+        loader: 'babel-loader',
30
+        exclude: /node_modules/
31
+      },
32
+      {
33
+        test: /\.(png|jpg|gif|svg)$/,
34
+        loader: 'file-loader',
35
+        options: {
36
+          name: '[name].[ext]?[hash]'
37
+        }
38
+      }
39
+    ]
40
+  },
41
+  resolve: {
42
+    alias: {
43
+      'vue$': 'vue/dist/vue.esm.js'
44
+    }
45
+  },
46
+  devServer: {
47
+    historyApiFallback: true,
48
+    noInfo: true
49
+  },
50
+  performance: {
51
+    hints: false
52
+  },
53
+  devtool: '#eval-source-map'
54
+}
55
+
56
+if (process.env.NODE_ENV === 'production') {
57
+  module.exports.devtool = '#source-map'
58
+  // http://vue-loader.vuejs.org/en/workflow/production.html
59
+  module.exports.plugins = (module.exports.plugins || []).concat([
60
+    new webpack.DefinePlugin({
61
+      'process.env': {
62
+        NODE_ENV: '"production"'
63
+      }
64
+    }),
65
+    new webpack.optimize.UglifyJsPlugin({
66
+      sourceMap: true,
67
+      compress: {
68
+        warnings: false
69
+      }
70
+    }),
71
+    new webpack.LoaderOptionsPlugin({
72
+      minimize: true
73
+    })
74
+  ])
75
+}