Compare commits
10 commits
bb40c09cda
...
602dbf5f3f
Author | SHA1 | Date | |
---|---|---|---|
602dbf5f3f | |||
4679d04076 | |||
1879d3a420 | |||
e52dbb6b9a | |||
d057048c66 | |||
4a572324e0 | |||
bf43bdba09 | |||
5b504ae3d6 | |||
75f80460f5 | |||
7fa7dd4e0f |
14 changed files with 5376 additions and 6226 deletions
11255
package-lock.json
generated
11255
package-lock.json
generated
File diff suppressed because it is too large
Load diff
51
package.json
51
package.json
|
@ -11,31 +11,30 @@
|
|||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@beyonk/google-fonts-webpack-plugin": "^1.2.3",
|
||||
"chart.js": "^2.9.3",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"css-loader": "^3.4.2",
|
||||
"file-loader": "^5.1.0",
|
||||
"google-fonts-webpack-plugin": "^0.4.4",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"idb-keyval": "^3.2.0",
|
||||
"reset-css": "^5.0.1",
|
||||
"serviceworker-webpack-plugin": "^1.0.1",
|
||||
"string-replace-loader": "^2.2.0",
|
||||
"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-license-plugin": "^4.1.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"
|
||||
"chart.js": "^4.4.2",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"css-loader": "^7.1.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.6.0",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"reset-css": "^5.0.2",
|
||||
"string-replace-loader": "^3.1.0",
|
||||
"style-loader": "^4.0.0",
|
||||
"svelte": "^4.2.13",
|
||||
"svelte-loader": "^3.2.0",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"uuid": "^9.0.1",
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.0.4",
|
||||
"webpack-license-plugin": "^4.4.2",
|
||||
"webpack-merge": "^5.10.0",
|
||||
"workbox-broadcast-update": "^7.0.0",
|
||||
"workbox-cacheable-response": "^7.0.0",
|
||||
"workbox-expiration": "^7.0.0",
|
||||
"workbox-routing": "^7.0.0",
|
||||
"workbox-strategies": "^7.0.0",
|
||||
"workbox-window": "^7.0.0"
|
||||
}
|
||||
}
|
||||
|
|
7
shell.nix
Normal file
7
shell.nix
Normal file
|
@ -0,0 +1,7 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
pkgs.mkShell {
|
||||
# nativeBuildInputs is usually what you want -- tools you need to run
|
||||
nativeBuildInputs = with pkgs.buildPackages; [
|
||||
nodejs_18
|
||||
];
|
||||
}
|
|
@ -5,7 +5,16 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Web app for keeping track of anything you want to count.">
|
||||
<title>NCounter</title>
|
||||
${require('./icon/html_code.html')}
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
|
||||
<link rel="manifest" href="icons/site.webmanifest">
|
||||
<link rel="mask-icon" href="icons/safari-pinned-tab.svg" color="#3a86b7">
|
||||
<link rel="shortcut icon" href="icons/favicon.ico">
|
||||
<meta name="msapplication-TileColor" content="#dce6ef">
|
||||
<meta name="msapplication-config" content="icons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#dce6ef">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
|
16
src/index.js
16
src/index.js
|
@ -1,10 +1,18 @@
|
|||
import App from './ncounter/App.svelte';
|
||||
import runtime from 'serviceworker-webpack-plugin/lib/runtime';
|
||||
import 'reset-css/reset.css';
|
||||
import './ncounter/site.css';
|
||||
import {Workbox} from 'workbox-window/Workbox.mjs';
|
||||
|
||||
const app = new App({ target: document.body });
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
const registration = runtime.register();
|
||||
}
|
||||
const wb = new Workbox('sw.js');
|
||||
|
||||
new App({ target: document.body });
|
||||
wb.addEventListener('message', async (event) => {
|
||||
if (event.data.type === 'CACHE_UPDATED') {
|
||||
app.SetUpdateAvailable();
|
||||
}
|
||||
});
|
||||
|
||||
wb.register();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<div class="about">
|
||||
<button class="close" on:click={ () => { dispatch('close'); } }><i class="material-icons">close</i></button>
|
||||
<button class="close" on:click={ () => { dispatch('close'); } }><span class="material-symbols-outlined">close</span></button>
|
||||
<header>
|
||||
<h1>NCounter</h1>
|
||||
<h2>Release |BUILD_DATE|</h2>
|
||||
|
|
|
@ -4,8 +4,16 @@
|
|||
<Counter counterId={ counter.id }></Counter>
|
||||
{/each}
|
||||
</div>
|
||||
{#if showUpdateMessage}
|
||||
<div class="update-message">
|
||||
An update is available. <button class="reload" on:click="{ () => { window.location.reload(); } }">Load now</button><button class="later" on:click="{ () => { showUpdateMessage = false; } }">Later</button>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="tool-bar">
|
||||
<button class="add" on:click="{ showAdd }"><i class="material-icons">add</i></button>
|
||||
<button class="add" on:click="{ showAdd }"><span class="material-symbols-outlined">add</span></button>
|
||||
{#if updateAvailable}<button class="update"
|
||||
class:animate="{!ignoreUpdate}"
|
||||
on:click={() => { ignoreUpdate = true; showUpdateMessage = !showUpdateMessage; }}><span class="material-symbols-outlined">arrow_upward</span></button>{/if}
|
||||
<button class="about" on:click="{ () => { showAbout = true; } }">?</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -36,6 +44,10 @@ let showAbout = false;
|
|||
let newCounter = null;
|
||||
let dialogForm = null;
|
||||
|
||||
let updateAvailable = false;
|
||||
let ignoreUpdate = false;
|
||||
let showUpdateMessage = false;
|
||||
|
||||
function focus(node){
|
||||
node.focus();
|
||||
}
|
||||
|
@ -68,6 +80,10 @@ function add(){
|
|||
|
||||
showAddDialog = false;
|
||||
}
|
||||
|
||||
export function SetUpdateAvailable(){
|
||||
updateAvailable = true;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.app {
|
||||
|
@ -117,8 +133,71 @@ function add(){
|
|||
border: 2px solid var(--highlight-color);
|
||||
}
|
||||
|
||||
.add {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
-webkit-text-stroke: 0 rgba(204, 204, 0, 0);
|
||||
}
|
||||
|
||||
15% {
|
||||
-webkit-text-stroke: 3px rgba(204, 204, 0, 1);
|
||||
}
|
||||
|
||||
30% {
|
||||
-webkit-text-stroke: 0 rgba(204, 204, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.update {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.update.animate {
|
||||
animation-name: pulse;
|
||||
animation-duration: 5s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.about {
|
||||
margin-left: auto;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.update-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
padding: 12px;
|
||||
|
||||
background-color: var(--background-primary);
|
||||
color: var(--text-primary);
|
||||
|
||||
box-shadow: 0 -1px 3px rgba(0,0,0,0.12), 0 -1px 2px rgba(0,0,0,0.24);
|
||||
}
|
||||
|
||||
.update-message > button {
|
||||
width: auto;
|
||||
height: 24px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@media(hover: hover){
|
||||
.update-message > button:hover {
|
||||
outline: 2px solid var(--button-color);
|
||||
}
|
||||
}
|
||||
|
||||
.update-message > button:focus {
|
||||
outline: 2px solid var(--button-color);
|
||||
}
|
||||
|
||||
.reload {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.later {
|
||||
margin-left: 16px;
|
||||
}
|
||||
</style>
|
|
@ -5,14 +5,14 @@
|
|||
<MyProgress value={ counter.value } max={ counter.max }></MyProgress>
|
||||
{/if}
|
||||
<div class="controls">
|
||||
<button on:click={ () => { showIncrement = true; } }><i class="material-icons">add</i></button>
|
||||
<button on:click={ () => { showSet = true; } }><i class="material-icons">edit</i></button>
|
||||
<button class="reset" on:click={ reset }><i class="material-icons">replay</i></button>
|
||||
<button on:click={ () => { showIncrement = true; } }><span class="material-symbols-outlined">add</span></button>
|
||||
<button on:click={ () => { showSet = true; } }><span class="material-symbols-outlined">edit</span></button>
|
||||
<button class="reset" on:click={ reset }><span class="material-symbols-outlined">replay</span></button>
|
||||
{#if counter.saveHistory}
|
||||
<button class="history" on:click={ () => { showHistory = true; } }><i class="material-icons">show_chart</i></button>
|
||||
<button class="history" on:click={ () => { showHistory = true; } }><span class="material-symbols-outlined">show_chart</span></button>
|
||||
{/if}
|
||||
<button on:click={ startEdit }><i class="material-icons">settings</i></button>
|
||||
<button on:click={ remove }><i class="material-icons">delete</i></button>
|
||||
<button on:click={ startEdit }><span class="material-symbols-outlined">settings</span></button>
|
||||
<button on:click={ remove }><span class="material-symbols-outlined">delete</span></button>
|
||||
</div>
|
||||
{#if showValueDialog}
|
||||
<div class="dialog">
|
||||
|
@ -47,7 +47,6 @@
|
|||
<script>
|
||||
import MyProgress from './MyProgress.svelte';
|
||||
import History from './History.svelte';
|
||||
import { tick } from 'svelte';
|
||||
import { counters } from './db.js';
|
||||
|
||||
export let counterId;
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
<div class="history-container">
|
||||
<header>
|
||||
<h1>{ counter.title }</h1>
|
||||
<button class="close" on:click={ close }><i class="material-icons">close</i></button>
|
||||
<button class="close" on:click={ close }><span class="material-symbols-outlined">close</span></button>
|
||||
</header>
|
||||
<canvas class="chart-container" bind:this={ chartEl } width="{ window.outerWidth }" height="250">
|
||||
</canvas>
|
||||
<div class="table-container">
|
||||
<table class="history">
|
||||
<thead>
|
||||
|
@ -27,7 +25,6 @@
|
|||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { counters } from './db.js';
|
||||
import Chart from 'chart.js';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
|
@ -47,77 +44,9 @@ $: history = counter.history.map(x => {
|
|||
value: x.value
|
||||
}
|
||||
});
|
||||
$: dataset = counter.history.reduce((acc, x) => {
|
||||
return {
|
||||
labels: [...acc.labels, x.time],
|
||||
datasets: [
|
||||
{
|
||||
...acc.datasets[0],
|
||||
data: [...acc.datasets[0].data, x.value]
|
||||
}
|
||||
]
|
||||
}
|
||||
}, {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
borderColor: darkMode ? 'rgba(255, 255, 255, 0.25)' : undefined,
|
||||
backgroundColor: darkMode ? 'rgba(255, 255, 255, 0.25)' : undefined,
|
||||
lineTension: 0.1,
|
||||
data: []
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
export let counterId;
|
||||
|
||||
let chartEl;
|
||||
|
||||
const themeMatch = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
let darkMode = themeMatch.matches;
|
||||
themeMatch.addListener((e) => {
|
||||
darkMode = e.matches;
|
||||
});
|
||||
|
||||
$: {
|
||||
if(chartEl){
|
||||
const theme = darkMode ?
|
||||
{
|
||||
gridLines: {
|
||||
color: 'rgba(255, 255, 255, 0.1)',
|
||||
zeroLineColor: 'rgba(255, 255, 255, 0.25)'
|
||||
},
|
||||
ticks: {
|
||||
fontColor: '#FFFFFF'
|
||||
}
|
||||
} :
|
||||
{} //Use defaults for light mode;
|
||||
|
||||
new Chart(chartEl, {
|
||||
type: 'line',
|
||||
data: dataset,
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
...theme,
|
||||
type: 'time'
|
||||
}
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
...theme
|
||||
}
|
||||
]
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function close(){
|
||||
dispatch('close');
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { set as setDb, get as getDb } from 'idb-keyval';
|
||||
import { writable } from 'svelte/store';
|
||||
import uuid from 'uuid/v4';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
function createCounters(){
|
||||
const {subscribe,set,update:updateVal} = writable([]);
|
||||
|
|
|
@ -2,6 +2,7 @@ import {registerRoute,setDefaultHandler} from 'workbox-routing';
|
|||
import {CacheFirst, StaleWhileRevalidate, NetworkFirst} from 'workbox-strategies';
|
||||
import {CacheableResponsePlugin} from 'workbox-cacheable-response';
|
||||
import {ExpirationPlugin} from 'workbox-expiration';
|
||||
import {BroadcastUpdatePlugin} from 'workbox-broadcast-update';
|
||||
|
||||
// Cache the Google Fonts stylesheets with a stale-while-revalidate strategy.
|
||||
registerRoute(
|
||||
|
@ -32,6 +33,9 @@ registerRoute(
|
|||
/\.(?:js|css|png)$/,
|
||||
new StaleWhileRevalidate({
|
||||
cacheName: 'static-resources',
|
||||
plugins: [
|
||||
new BroadcastUpdatePlugin(),
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
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');
|
||||
const LicensePlugin = require('webpack-license-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
|
@ -11,55 +9,43 @@ const year = now.getFullYear().toString();
|
|||
const releaseDate = now.getFullYear() + "-" + (now.getMonth() + 1).toString().padStart(2, '0') + "-" + now.getDate().toString().padStart(2, '0');
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
entry: {
|
||||
index: './src/index.js',
|
||||
sw: './src/sw.js'
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'NCounter',
|
||||
excludeChunks: ['sw'],
|
||||
template: 'src/index.html'
|
||||
}),
|
||||
new GoogleFontsPlugin({
|
||||
fonts: [
|
||||
{ family: "Material Icons" }
|
||||
],
|
||||
local: false
|
||||
}),
|
||||
new ServiceWorkerWebpackPlugin({
|
||||
entry: path.join(__dirname, 'src/sw.js'),
|
||||
publicPath: './'
|
||||
}),
|
||||
new LicensePlugin(),
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: 'src/icon/*.png', to: 'icons', flatten: true
|
||||
},
|
||||
{
|
||||
from: 'src/icon/safari-pinned-tab.svg', to: 'icons'
|
||||
},
|
||||
{
|
||||
from: 'src/icon/favicon.ico', to: 'icons'
|
||||
},
|
||||
{
|
||||
from: 'src/icon/site.webmanifest', to: 'icons'
|
||||
},
|
||||
{
|
||||
from: 'src/icon/browserconfig.xml', to: 'icons'
|
||||
}
|
||||
])
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: 'src/icon/*.png', to: path.resolve(__dirname, 'dist', 'icons', '[name][ext]')
|
||||
},
|
||||
{
|
||||
from: 'src/icon/safari-pinned-tab.svg', to: 'icons'
|
||||
},
|
||||
{
|
||||
from: 'src/icon/favicon.ico', to: 'icons'
|
||||
},
|
||||
{
|
||||
from: 'src/icon/site.webmanifest', to: 'icons'
|
||||
},
|
||||
{
|
||||
from: 'src/icon/browserconfig.xml', to: 'icons'
|
||||
}
|
||||
]
|
||||
})
|
||||
],
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'dist')
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
interpolate: true
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.svelte$/,
|
||||
exclude: /node_modules/,
|
||||
|
@ -89,4 +75,7 @@ module.exports = {
|
|||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
conditionNames: ['svelte']
|
||||
},
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
const merge = require('webpack-merge');
|
||||
const { merge } = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'development',
|
||||
devtool: 'inline-source-map',
|
||||
devServer: {
|
||||
contentBase: './dist',
|
||||
static: './dist',
|
||||
}
|
||||
});
|
|
@ -1,6 +1,18 @@
|
|||
const merge = require('webpack-merge');
|
||||
const webpack = require('webpack');
|
||||
const { merge } = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
const Terser = require('terser-webpack-plugin');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production',
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
}),
|
||||
],
|
||||
optimization:{
|
||||
minimizer: [new Terser({
|
||||
test: /\.m?js$/,
|
||||
})]
|
||||
}
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue