Latest updates
This commit is contained in:
parent
aace75679a
commit
b2208a7e58
15 changed files with 1267 additions and 131 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
node_modules/
|
||||
dist/
|
52
dist/index.html
vendored
52
dist/index.html
vendored
|
@ -1,52 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<title>NCounter</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
button {
|
||||
background: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
background-color: rgba(0,0,0,0.25);
|
||||
|
||||
z-index: 12;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
background-color: #E0E0E0;
|
||||
pointer-events: all;
|
||||
|
||||
width: 250px;
|
||||
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
|
||||
padding: 5px;
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
1
dist/index.js
vendored
1
dist/index.js
vendored
File diff suppressed because one or more lines are too long
987
package-lock.json
generated
987
package-lock.json
generated
File diff suppressed because it is too large
Load diff
20
package.json
20
package.json
|
@ -5,16 +5,30 @@
|
|||
"private": true,
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "webpack",
|
||||
"start": "webpack-dev-server --open"
|
||||
"build": "webpack --config webpack.prod.js",
|
||||
"start": "webpack-dev-server --open --config webpack.dev.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@beyonk/google-fonts-webpack-plugin": "^1.2.3",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"google-fonts-webpack-plugin": "^0.4.4",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"idb-keyval": "^3.2.0",
|
||||
"serviceworker-webpack-plugin": "^1.0.1",
|
||||
"style-loader": "^1.1.3",
|
||||
"svelte": "^3.4.1",
|
||||
"svelte-loader": "^2.13.4",
|
||||
"uuid": "^3.4.0",
|
||||
"webpack": "^4.31.0",
|
||||
"webpack-cli": "^3.3.2",
|
||||
"webpack-dev-server": "^3.4.1"
|
||||
"webpack-dev-server": "^3.4.1",
|
||||
"webpack-merge": "^4.2.2",
|
||||
"workbox-cacheable-response": "^5.0.0",
|
||||
"workbox-expiration": "^5.0.0",
|
||||
"workbox-routing": "^5.0.0",
|
||||
"workbox-strategies": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
import App from './ncounter/App.svelte';
|
||||
import runtime from 'serviceworker-webpack-plugin/lib/runtime';
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
const registration = runtime.register();
|
||||
}
|
||||
|
||||
new App({ target: document.body });
|
|
@ -1,6 +1,6 @@
|
|||
<div class="app">
|
||||
{#each counters as counter}
|
||||
<Counter counter={ counter } on:save={() => { save(counter); } }></Counter>
|
||||
{#each $counters as counter}
|
||||
<Counter counterId={ counter.id }></Counter>
|
||||
{/each}
|
||||
</div>
|
||||
<button class="add" on:click="{ showAdd }"><i class="material-icons">add</i></button>
|
||||
|
@ -12,38 +12,19 @@
|
|||
<label>Target: <input type="number" bind:value={newCounter.max} /></label>
|
||||
<label>Click counter: <input type="checkbox" bind:value={newCounter.isClickCounter} /></label>
|
||||
<button on:click={ () => { showAddDialog = false; } }>Cancel</button>
|
||||
<button class="ok" on:click={ addCounter }>OK</button>
|
||||
<button class="ok" on:click={ add }>OK</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<script>
|
||||
import Counter from './Counter.svelte';
|
||||
import { counters } from './db.js';
|
||||
import './site.css';
|
||||
|
||||
let showAddDialog = false;
|
||||
|
||||
let newCounter = null;
|
||||
|
||||
let counters = [
|
||||
{
|
||||
title: 'Test 1',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
title: 'Test 2',
|
||||
value: 10
|
||||
},
|
||||
{
|
||||
title: 'Max Test',
|
||||
value: 0,
|
||||
max: 100
|
||||
},
|
||||
{
|
||||
title: 'Click Test',
|
||||
value: 0,
|
||||
isClickCounter: true
|
||||
}
|
||||
];
|
||||
|
||||
function focus(node){
|
||||
node.focus();
|
||||
}
|
||||
|
@ -56,7 +37,8 @@ function showAdd(){
|
|||
newCounter = {
|
||||
title: '',
|
||||
value: 0,
|
||||
isClickCounter: false
|
||||
isClickCounter: false,
|
||||
history: []
|
||||
}
|
||||
|
||||
console.log({ newCounter });
|
||||
|
@ -64,8 +46,8 @@ function showAdd(){
|
|||
showAddDialog = true;
|
||||
}
|
||||
|
||||
function addCounter(){
|
||||
counters = [...counters, newCounter];
|
||||
function add(){
|
||||
counters.add(newCounter);
|
||||
|
||||
showAddDialog = false;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
{#if !counter.isClickCounter}
|
||||
<button on:click={ () => { showIncrement = true; } }><i class="material-icons">add</i></button>
|
||||
{/if}
|
||||
<span class="message">{ message }</span>
|
||||
<button class="reset" on:click={ reset }><i class="material-icons">undo</i></button>
|
||||
<button on:click={ save }><i class="material-icons">save</i></button>
|
||||
<button on:click={ remove }><i class="material-icons">delete</i></button>
|
||||
</div>
|
||||
{#if showIncrement}
|
||||
<div class="dialog">
|
||||
|
@ -25,20 +27,43 @@
|
|||
import MyProgress from './MyProgress.svelte';
|
||||
import { tick } from 'svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { counters } from './db.js';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let counter;
|
||||
export let counterId;
|
||||
|
||||
$: counter = $counters.find(x => x.id == counterId);
|
||||
|
||||
let incrementValue;
|
||||
let showIncrement;
|
||||
let message = '';
|
||||
|
||||
const formatter = new Intl.DateTimeFormat('en-US',
|
||||
{
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
});
|
||||
|
||||
function focus(node){
|
||||
node.focus();
|
||||
}
|
||||
|
||||
function update(newCounter){
|
||||
counters.update(newCounter);
|
||||
}
|
||||
|
||||
function increment(){
|
||||
counter.value += incrementValue;
|
||||
const newCounter = {
|
||||
...counter,
|
||||
value: counter.value + incrementValue
|
||||
}
|
||||
|
||||
update(newCounter);
|
||||
|
||||
showIncrement = false;
|
||||
incrementValue = null;
|
||||
}
|
||||
|
@ -46,22 +71,52 @@ function increment(){
|
|||
function reset(e){
|
||||
e.stopPropagation();
|
||||
|
||||
if(confirm(`Reset ${counter.title}?`))
|
||||
counter.value = 0;
|
||||
if(confirm(`Reset ${counter.title}?`)){
|
||||
const newCounter = {
|
||||
...counter,
|
||||
value: 0
|
||||
}
|
||||
|
||||
update(newCounter);
|
||||
}
|
||||
}
|
||||
|
||||
function save(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
dispatch('save');
|
||||
counter.value = 0;
|
||||
const time = new Date();
|
||||
|
||||
const data = {
|
||||
time,
|
||||
value: counter.value
|
||||
}
|
||||
|
||||
const newCounter = {
|
||||
...counter,
|
||||
history: [...counter.history, data]
|
||||
}
|
||||
|
||||
message = `Saved ${data.value} at ${formatter.format(data.time)}`;
|
||||
setTimeout(() => message = '', 5000);
|
||||
|
||||
update(newCounter);
|
||||
}
|
||||
|
||||
function remove(){
|
||||
if(confirm(`Remove ${counter.title}?`))
|
||||
counters.remove(counter);
|
||||
}
|
||||
|
||||
function clickCount() {
|
||||
if(!counter.isClickCounter)
|
||||
return;
|
||||
|
||||
counter.value++;
|
||||
const newCounter = {
|
||||
...counter,
|
||||
value: counter.value + 1
|
||||
}
|
||||
|
||||
update(newCounter);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
@ -79,8 +134,9 @@ function clickCount() {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.reset {
|
||||
margin-left: auto;
|
||||
.message {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header {
|
||||
|
|
57
src/ncounter/db.js
Normal file
57
src/ncounter/db.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { set as setDb, get as getDb } from 'idb-keyval';
|
||||
import { writable } from 'svelte/store';
|
||||
import uuid from 'uuid/v4';
|
||||
|
||||
function createCounters(){
|
||||
const {subscribe,set,update:updateVal} = writable([]);
|
||||
|
||||
getDb('counters').then(counters => {
|
||||
if(counters)
|
||||
set(counters);
|
||||
});
|
||||
|
||||
function add(counter){
|
||||
const newCounter = { id: uuid(), ...counter };
|
||||
|
||||
updateVal(counters => {
|
||||
const newCounters = [...counters, newCounter];
|
||||
|
||||
setDb('counters', newCounters);
|
||||
|
||||
return newCounters;
|
||||
});
|
||||
}
|
||||
|
||||
function update(counter){
|
||||
updateVal(counters => {
|
||||
const counterIndex = counters.findIndex(x => x.id == counter.id);
|
||||
|
||||
const newCounters = [...counters.slice(0, counterIndex), counter, ...counters.slice(counterIndex + 1)];
|
||||
|
||||
setDb('counters', newCounters);
|
||||
|
||||
return newCounters;
|
||||
});
|
||||
}
|
||||
|
||||
function remove(counter){
|
||||
updateVal(counters => {
|
||||
const counterIndex = counters.findIndex(x => x.id == counter.id);
|
||||
|
||||
const newCounters = [...counters.slice(0, counterIndex), ...counters.slice(counterIndex + 1)];
|
||||
|
||||
setDb('counters', newCounters);
|
||||
|
||||
return newCounters;
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
add,
|
||||
update,
|
||||
remove
|
||||
};
|
||||
}
|
||||
|
||||
export const counters = createCounters();
|
40
src/ncounter/site.css
Normal file
40
src/ncounter/site.css
Normal file
|
@ -0,0 +1,40 @@
|
|||
body {
|
||||
font-family: -apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
button {
|
||||
background: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
background-color: rgba(0,0,0,0.25);
|
||||
|
||||
z-index: 12;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
background-color: #E0E0E0;
|
||||
pointer-events: all;
|
||||
|
||||
width: 250px;
|
||||
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
|
||||
padding: 5px;
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
41
src/sw.js
Normal file
41
src/sw.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import {registerRoute} from 'workbox-routing';
|
||||
import {CacheFirst, StaleWhileRevalidate, NetworkFirst} from 'workbox-strategies';
|
||||
import {CacheableResponsePlugin} from 'workbox-cacheable-response';
|
||||
import {ExpirationPlugin} from 'workbox-expiration';
|
||||
|
||||
// Cache the Google Fonts stylesheets with a stale-while-revalidate strategy.
|
||||
registerRoute(
|
||||
/^https:\/\/fonts\.googleapis\.com/,
|
||||
new StaleWhileRevalidate({
|
||||
cacheName: 'google-fonts-stylesheets',
|
||||
})
|
||||
);
|
||||
|
||||
// Cache the underlying font files with a cache-first strategy for 1 year.
|
||||
registerRoute(
|
||||
/^https:\/\/fonts\.gstatic\.com/,
|
||||
new CacheFirst({
|
||||
cacheName: 'google-fonts-webfonts',
|
||||
plugins: [
|
||||
new CacheableResponsePlugin({
|
||||
statuses: [0, 200],
|
||||
}),
|
||||
new ExpirationPlugin({
|
||||
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||
maxEntries: 30,
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
registerRoute(
|
||||
/\.(?:js|css)$/,
|
||||
new StaleWhileRevalidate({
|
||||
cacheName: 'static-resources',
|
||||
})
|
||||
);
|
||||
|
||||
registerRoute(
|
||||
'/',
|
||||
new NetworkFirst()
|
||||
)
|
44
webpack.common.js
Normal file
44
webpack.common.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const GoogleFontsPlugin = require('@beyonk/google-fonts-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'NCounter',
|
||||
}),
|
||||
new GoogleFontsPlugin({
|
||||
fonts: [
|
||||
{ family: "Material Icons" }
|
||||
],
|
||||
local: false
|
||||
}),
|
||||
new ServiceWorkerWebpackPlugin({
|
||||
entry: path.join(__dirname, 'src/sw.js'),
|
||||
})
|
||||
],
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
path: path.resolve(__dirname, 'dist')
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.svelte$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'svelte-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
path: path.resolve(__dirname, 'dist')
|
||||
},
|
||||
devServer: {
|
||||
contentBase: './dist'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.svelte$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'svelte-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
devtool: 'inline-source-map'
|
||||
}
|
10
webpack.dev.js
Normal file
10
webpack.dev.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'development',
|
||||
devtool: 'inline-source-map',
|
||||
devServer: {
|
||||
contentBase: './dist',
|
||||
}
|
||||
});
|
6
webpack.prod.js
Normal file
6
webpack.prod.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production',
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue