Redesign UI to add some color,

eliminate the FAB, and make
dialogs look better
Add an about page
This commit is contained in:
CheddarCrisp 2020-02-28 10:00:41 -05:00
parent a291d68fa9
commit 67a40cfb84
9 changed files with 318 additions and 51 deletions

104
package-lock.json generated
View file

@ -2767,6 +2767,15 @@
"integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=",
"dev": true
},
"get-npm-tarball-url": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/get-npm-tarball-url/-/get-npm-tarball-url-2.0.1.tgz",
"integrity": "sha512-POrVRGyS9X5w+855/H46JGVYBGuVgJXyIkbsTCzW+sv5x2qH+rfQjc7652DzkgOskF+cqLevA2En7V0hu0gZCg==",
"dev": true,
"requires": {
"normalize-registry-url": "^1.0.0"
}
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
@ -3925,6 +3934,34 @@
"to-regex": "^3.0.1"
}
},
"needle": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.3.2.tgz",
"integrity": "sha512-DUzITvPVDUy6vczKKYTnWc/pBZ0EnjMJnQ3y+Jo5zfKFimJs7S3HFCxCRZYB9FUZcrzUQr3WsmvZgddMEIZv6w==",
"dev": true,
"requires": {
"debug": "^3.2.6",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}
}
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@ -4013,6 +4050,12 @@
"integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=",
"dev": true
},
"normalize-registry-url": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/normalize-registry-url/-/normalize-registry-url-1.0.0.tgz",
"integrity": "sha512-0v6T4851b72ykk5zEtFoN4QX/Fqyk7pouIj9xZyAvAe9jlDhAwT4z6FlwsoQCHjeuK2EGUoAwy/F4y4B1uZq9A==",
"dev": true
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@ -4857,6 +4900,12 @@
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=",
"dev": true
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true
},
"schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@ -5258,6 +5307,37 @@
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
"dev": true
},
"spdx-exceptions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
"integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
"dev": true
},
"spdx-expression-parse": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
"dev": true,
"requires": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-expression-validate": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-validate/-/spdx-expression-validate-2.0.0.tgz",
"integrity": "sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==",
"dev": true,
"requires": {
"spdx-expression-parse": "^3.0.0"
}
},
"spdx-license-ids": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
"integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
"dev": true
},
"spdy": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz",
@ -5414,6 +5494,16 @@
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
"dev": true
},
"string-replace-loader": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/string-replace-loader/-/string-replace-loader-2.2.0.tgz",
"integrity": "sha512-Ukt4ZC8+xVWdBRut3/iwnPJCNL1yV8AbVKXn8UcWdYrHgtuW4UDDAbBSi/J/CQDEWQBt824AJvPYahF23eJLRg==",
"dev": true,
"requires": {
"loader-utils": "^1.2.3",
"schema-utils": "^1.0.0"
}
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@ -6125,6 +6215,20 @@
}
}
},
"webpack-license-plugin": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/webpack-license-plugin/-/webpack-license-plugin-4.1.1.tgz",
"integrity": "sha512-CYqPx4f8e7thGIrv5aruoUAF8sTw94o3dGhZbALc4szYIjdQPGLO3lCB/YLUj7UaI+JJeuboVz0lu3QPx5k+ZA==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
"get-npm-tarball-url": "^2.0.1",
"lodash": "^4.17.10",
"needle": "^2.2.4",
"spdx-expression-validate": "^2.0.0",
"webpack-sources": "^1.3.0"
}
},
"webpack-log": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",

View file

@ -20,6 +20,7 @@
"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",
@ -27,6 +28,7 @@
"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",

64
src/ncounter/About.svelte Normal file
View file

@ -0,0 +1,64 @@
<div class="about">
<button class="close" on:click={ () => { dispatch('close'); } }><i class="material-icons">close</i></button>
<header>
<h1>NCounter</h1>
<h2>Release |BUILD_DATE|</h2>
<h2>&copy; |BUILD_YEAR|</h2>
</header>
<div class="licenses">
<h2>Open Source License Information</h2>
<Licenses></Licenses>
</div>
</div>
<script>
import Licenses from './Licenses.svelte';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
</script>
<style>
.about {
background-color: white;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 3;
display: flex;
flex-direction: column;
align-items: stretch;
overflow: auto;
padding: 5px;
}
.about > header {
flex: center;
text-align: center;
}
h1 {
font-weight: 600;
font-size: 24px;
}
h2 {
font-size: 18px;
}
.licenses h2{
font-weight: 600;
}
.close {
position: absolute;
top: 15px;
right: 15px;
}
.licenses {
margin-top: 32px;
}
</style>

View file

@ -1,27 +1,36 @@
<div class="app">
<button class="add" on:click="{ showAdd }"><i class="material-icons">add</i></button>
<div class="counter-list">
{#each $counters as counter}
<Counter counterId={ counter.id }></Counter>
{/each}
</div>
<div class="tool-bar">
<button class="add" on:click="{ showAdd }"><i class="material-icons">add</i></button>
<button class="about" on:click="{ () => { showAbout = true; } }">?</button>
</div>
</div>
{#if showAddDialog}
<div class="dialog">
<div class="dialog-content">
<label>Name: <input type="text" bind:value={newCounter.title} use:focus/></label>
<label>Initial Value: <input type="number" bind:value={newCounter.initialValue} /></label>
<label>Target: <input type="number" bind:value={newCounter.max} /></label>
<label>Save history: <input type="checkbox" bind:checked={newCounter.saveHistory} /></label>
<label>Name<input type="text" bind:value={newCounter.title} use:focus/></label>
<label>Initial Value<input type="number" bind:value={newCounter.initialValue} /></label>
<label>Target<input type="number" bind:value={newCounter.max} /></label>
<label>Save history<input type="checkbox" bind:checked={newCounter.saveHistory} /></label>
<button on:click={ () => { showAddDialog = false; } }>Cancel</button>
<button class="ok" on:click={ add }>OK</button>
</div>
</div>
{/if}
{#if showAbout}
<About on:close="{ () => { showAbout = false; } }"></About>
{/if}
<script>
import About from './About.svelte';
import Counter from './Counter.svelte';
import { counters } from './db.js';
let showAddDialog = false;
let showAbout = false;
let newCounter = null;
function focus(node){
@ -50,34 +59,16 @@ function add(){
showAddDialog = false;
}
</script>
<style>
.add {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
bottom: 15px;
right: 15px;
z-index: 2;
background-color: #90C090;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
width: 40px;
height: 40px;
color: white;
}
<style lang="scss">
.ok {
margin-left: auto;
}
.app {
padding: 5px;
display: flex;
flex-direction: column;
align-items: stretch;
position: fixed;
top: 0;
right: 0;
@ -85,7 +76,35 @@ function add(){
left: 0;
z-index: 1;
}
.counter-list {
padding: 5px;
overflow: auto;
flex: 1 0 0;
background-color: #8baec6;
}
.tool-bar {
flex: none;
display: flex;
border-top: 2px solid #3A86B7;
}
.tool-bar > button {
background-color: #3A86B7;
color: white;
}
.tool-bar > button:focus, .tool-bar > button:hover {
border: 2px solid #303030;
}
.about {
margin-left: auto;
font-size: 20px;
}
</style>

View file

@ -27,10 +27,10 @@
{#if showEditDialog}
<div class="dialog">
<div class="dialog-content">
<label>Name: <input type="text" bind:value={editingCounter.title} use:focus/></label>
<label>Initial Value: <input type="number" bind:value={editingCounter.initialValue} /></label>
<label>Target: <input type="number" bind:value={editingCounter.max} /></label>
<label>Save history: <input type="checkbox" bind:checked={editingCounter.saveHistory} /></label>
<label>Name<input type="text" bind:value={editingCounter.title} use:focus/></label>
<label>Initial Value<input type="number" bind:value={editingCounter.initialValue} /></label>
<label>Target<input type="number" bind:value={editingCounter.max} /></label>
<label>Save history<input type="checkbox" bind:checked={editingCounter.saveHistory} /></label>
<button on:click={ () => { showEditDialog = false; } }>Cancel</button>
<button class="ok" on:click={ finishEdit }>OK</button>
</div>
@ -122,7 +122,10 @@ function remove(){
<style>
.counter{
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
margin-bottom: 12px;
background-color: #FCFCFC;
margin-bottom: 4px;
display: flex;
flex-direction: column;
@ -130,10 +133,6 @@ function remove(){
padding: 5px;
}
.counter:last-child {
margin-bottom: 72px;
}
.controls {
display: flex;
}
@ -151,7 +150,7 @@ header {
}
.reset {
margin-left: 24px;
margin-left: 12px;
}
.reset + button {
@ -159,7 +158,7 @@ header {
}
.history {
margin-right: 24px;
margin-right: 12px;
}
.dialog-value {

View file

@ -107,8 +107,6 @@ function close(){
display: flex;
flex-direction: column;
align-items: stretch;
overflow: auto;
}
header {
@ -124,6 +122,8 @@ function close(){
.table-container {
position: relative;
overflow: auto;
}
.close {

View file

@ -0,0 +1,39 @@
<div>
{#each licenses as license}
<header>
<h1>{license.name} by {license.author}</h1>
<div>Source: <a href="{license.repository}" target="_blank">{license.repository}</a></div>
</header>
<pre>{license.licenseText}</pre>
{/each}
{#if !licenses}
<progress></progress>
{/if}
</div>
<script>
let licenses = [];
(async () => {
licenses = await fetch('oss-licenses.json')
.then(response => {
if(response.ok)
return response.json();
});
})();
</script>
<style>
header {
margin-top: 12px;
}
header h1 {
font-weight: 600;
}
pre {
margin: 5px;
padding: 5px;
white-space: pre-wrap;
background-color: #F0F0F0;
}
</style>

View file

@ -25,7 +25,21 @@ body {
button {
background: 0;
border: 0;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-family: inherit;
}
button:focus, button:hover {
border: 2px solid #3A86B7;
}
.dialog {
@ -47,14 +61,13 @@ button {
}
.dialog-content {
background-color: #E0E0E0;
background-color: #FCFCFC;
pointer-events: all;
width: 300px;
width: 315px;
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;
padding: 10px;
display: flex;
flex-wrap: wrap;
@ -64,16 +77,30 @@ button {
display: flex;
width: 100%;
margin-bottom: 5px;
align-items: center;
height: 26px;
font-weight: 600;
}
.dialog-content > label > input {
margin-left: auto;
}
.dialog-content input[type='text'], .dialog-content input[type='number'] {
width: 175px;
}
.dialog-content button {
padding: 10px;
font-size: 14px;
font-weight: 600;
width: auto;
}
.dialog-content button:focus, .dialog-content button:hover {
border: 0;
outline: 2px solid #3A86B7;
}
input[type="text"],

View file

@ -3,6 +3,7 @@ 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');
module.exports = {
entry: './src/index.js',
@ -21,7 +22,8 @@ module.exports = {
new ServiceWorkerWebpackPlugin({
entry: path.join(__dirname, 'src/sw.js'),
publicPath: './'
})
}),
new LicensePlugin()
],
output: {
filename: 'index.js',
@ -32,7 +34,18 @@ module.exports = {
{
test: /\.svelte$/,
exclude: /node_modules/,
loader: 'svelte-loader'
use: [
'svelte-loader',
{
loader: 'string-replace-loader',
options: {
multiple: [
{ search: '|BUILD_YEAR|', replace: '2020' },
{ search: '|BUILD_DATE|', replace: '2020-02-28'}
]
}
}
]
},
{
test: /\.css$/,