Add starter files and structure with comments
This commit is contained in:
parent
b0282ec705
commit
d98f93a8a4
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>book-tracker</title>
|
||||||
|
<meta name="description" content="An attempt at a viable alternative to Goodreads">
|
||||||
|
<meta name="keywords" content="books, tracking, lists, bookshelves, bookshelf, rating, reviews, reading">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="index.scss">
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<!-- Choo replaces the body tag with the app. -->
|
||||||
|
<body></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,38 @@
|
||||||
|
import choo from 'choo';
|
||||||
|
|
||||||
|
import { viewManager } from './views/manager';
|
||||||
|
|
||||||
|
const app = choo();
|
||||||
|
|
||||||
|
// App state and emitters
|
||||||
|
app.use((state, emitter) => {
|
||||||
|
// Default state variables
|
||||||
|
state.currentView = 'home';
|
||||||
|
state.viewStates = {};
|
||||||
|
|
||||||
|
// Listeners
|
||||||
|
emitter.on('DOMContentLoaded', () => {
|
||||||
|
// Emitter listeners
|
||||||
|
emitter.on('render', callback => {
|
||||||
|
// This is a dirty hack to get the callback to call *after* re-rendering.
|
||||||
|
if (callback && typeof callback === "function") {
|
||||||
|
setTimeout(() => {
|
||||||
|
callback();
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
emitter.on('changeView', newView => {
|
||||||
|
// Change the view and call render. Makes it easier to call within views.
|
||||||
|
state.currentView = newView;
|
||||||
|
emitter.emit('render', () => {});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// For the main screen, pass the viewManager function in viewManager.js,
|
||||||
|
// which is given the app's state from above and the emitter.emit method that
|
||||||
|
// triggers the app's emitter listeners.
|
||||||
|
app.route('/', viewManager);
|
||||||
|
|
||||||
|
app.mount('body'); // Overwrite the `<body>` tag with the content of the Choo app
|
|
@ -0,0 +1,42 @@
|
||||||
|
//! Picnic CSS http://www.picnicss.com/
|
||||||
|
|
||||||
|
// Imports the base variable styles
|
||||||
|
@import './styles/picnic-customizations/theme/theme';
|
||||||
|
|
||||||
|
@import './node_modules/picnic/src/vendor/compass-breakpoint/stylesheets/breakpoint';
|
||||||
|
|
||||||
|
// Normalize.css (external library)
|
||||||
|
@import './node_modules/picnic/src/plugins/normalize/plugin';
|
||||||
|
|
||||||
|
// Generic styles for things like <body>, <a> and others
|
||||||
|
// It also overwrites normalize.css a bit
|
||||||
|
@import './node_modules/picnic/src/plugins/generic/plugin';
|
||||||
|
@import './node_modules/picnic/src/plugins/fontello/plugin';
|
||||||
|
|
||||||
|
// Simple elements
|
||||||
|
@import './node_modules/picnic/src/plugins/label/plugin';
|
||||||
|
@import './node_modules/picnic/src/plugins/button/plugin';
|
||||||
|
|
||||||
|
// Forms
|
||||||
|
@import './node_modules/picnic/src/plugins/input/plugin';
|
||||||
|
@import './node_modules/picnic/src/plugins/select/plugin';
|
||||||
|
@import './node_modules/picnic/src/plugins/radio/plugin';
|
||||||
|
@import './node_modules/picnic/src/plugins/checkbox/plugin';
|
||||||
|
|
||||||
|
// Components
|
||||||
|
@import './node_modules/picnic/src/plugins/table/plugin';
|
||||||
|
@import './node_modules/picnic/src/plugins/grid/plugin';
|
||||||
|
|
||||||
|
// Extra
|
||||||
|
@import './node_modules/picnic/src/plugins/nav/plugin';
|
||||||
|
|
||||||
|
@import './node_modules/picnic/src/plugins/stack/plugin';
|
||||||
|
@import './node_modules/picnic/src/plugins/card/plugin';
|
||||||
|
@import './node_modules/picnic/src/plugins/modal/plugin';
|
||||||
|
|
||||||
|
// @import './node_modules/picnic/src/plugins/dropimage/plugin';
|
||||||
|
// @import './node_modules/picnic/src/plugins/tabs/plugin';
|
||||||
|
// @import './node_modules/picnic/src/plugins/tooltip/plugin';
|
||||||
|
|
||||||
|
// Custom global styling
|
||||||
|
@import './styles/picnic-customizations/custom';
|
|
@ -0,0 +1,190 @@
|
||||||
|
body {
|
||||||
|
font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $picnic-info;
|
||||||
|
|
||||||
|
&:not(.button) {
|
||||||
|
&:hover,
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
color: darken($picnic-info, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
// External links
|
||||||
|
// a[href^="http://"]:not([href*="localhost"]):not([href*="guts.plus"]):after,
|
||||||
|
// a[href^="https://"]:not([href*="localhost"]):not([href*="guts.plus"]):after{
|
||||||
|
// font-family: "icons";
|
||||||
|
// font-size: 70%;
|
||||||
|
// vertical-align: top;
|
||||||
|
// margin-left: 3px;
|
||||||
|
// content: "\f08e";
|
||||||
|
// }
|
||||||
|
|
||||||
|
.menu ul li {
|
||||||
|
display: inline-block;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: block;
|
||||||
|
width: 75%;
|
||||||
|
max-width: 900px;
|
||||||
|
min-width: 560px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: $picnic-separation 0;
|
||||||
|
|
||||||
|
&.wide {
|
||||||
|
width: 100%;
|
||||||
|
padding: $picnic-separation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title + .subtitle {
|
||||||
|
margin-top: -2 * $picnic-separation;
|
||||||
|
padding-top: 0;
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: $picnic-separation;
|
||||||
|
font-size: 90%;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 $picnic-separation;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
padding: 0.4em 0 0.1em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.45em;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
font-size: 1.15em;
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
font-size: 1.05em;
|
||||||
|
}
|
||||||
|
h6 {
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-stack {
|
||||||
|
@extend .stack;
|
||||||
|
width: 90%;
|
||||||
|
min-width: unset;
|
||||||
|
margin: 0 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card.info {
|
||||||
|
background: $picnic-info;
|
||||||
|
color: $picnic-white;
|
||||||
|
|
||||||
|
* {
|
||||||
|
color: $picnic-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-dull {
|
||||||
|
background: $picnic-dull;
|
||||||
|
}
|
||||||
|
.background-light {
|
||||||
|
background: $picnic-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: $picnic-dull;
|
||||||
|
* {
|
||||||
|
color: $picnic-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.light th {
|
||||||
|
background: $picnic-light;
|
||||||
|
color: $picnic-black;
|
||||||
|
|
||||||
|
* {
|
||||||
|
color: $picnic-black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paddingless {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.inline {
|
||||||
|
display: inline !important;
|
||||||
|
}
|
||||||
|
.italic {
|
||||||
|
font-style: italic !important;
|
||||||
|
}
|
||||||
|
.small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
.left-align {
|
||||||
|
text-align: left !important;
|
||||||
|
}
|
||||||
|
.right-align {
|
||||||
|
text-align: right !important;
|
||||||
|
}
|
||||||
|
.center-align {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
.clear {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.pull-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.pull-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.menu ul li {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
.container {
|
||||||
|
width: 98%;
|
||||||
|
min-width: unset;
|
||||||
|
}
|
||||||
|
.brand {
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 599px) {
|
||||||
|
.left-align-desktop {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.right-align-desktop {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.center-align-desktop {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.pull-left-desktop {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.pull-right-desktop {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Color variables
|
||||||
|
// - Cool
|
||||||
|
// - Warm
|
||||||
|
// - Gray Scale
|
||||||
|
//
|
||||||
|
// clrs, from https://github.com/mrmrs/colors
|
||||||
|
|
||||||
|
// Cool
|
||||||
|
$aqua: #7fdbff !default;
|
||||||
|
$blue: #0074d9 !default;
|
||||||
|
$navy: #001f3f !default;
|
||||||
|
$teal: #39cccc !default;
|
||||||
|
$green: #2ecc40 !default;
|
||||||
|
$olive: #3d9970 !default;
|
||||||
|
$lime: #01ff70 !default;
|
||||||
|
|
||||||
|
// Warm
|
||||||
|
$yellow: #ffdc00 !default;
|
||||||
|
$orange: #ff851b !default;
|
||||||
|
$red: #ff4136 !default;
|
||||||
|
$fuchsia: #f012be !default;
|
||||||
|
$purple: #b10dc9 !default;
|
||||||
|
$maroon: #85144b !default;
|
||||||
|
|
||||||
|
// Gray Scale
|
||||||
|
$white: #fff !default;
|
||||||
|
$silver: #ddd !default;
|
||||||
|
$gray: #aaa !default;
|
||||||
|
$black: #111 !default;
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Top level variables for Picnic CSS
|
||||||
|
// Note: some others are available under each specific plugin
|
||||||
|
|
||||||
|
@import './colors';
|
||||||
|
|
||||||
|
|
||||||
|
// Colors (from /themes/default/colors)
|
||||||
|
$picnic-white: #fff !default;
|
||||||
|
$picnic-black: #111 !default;
|
||||||
|
$picnic-primary: $teal !default;
|
||||||
|
$picnic-success: $green !default;
|
||||||
|
$picnic-info: $blue !default;
|
||||||
|
$picnic-warning: $orange !default;
|
||||||
|
$picnic-error: $red !default;
|
||||||
|
$picnic-dull: $gray !default;
|
||||||
|
$picnic-light: $silver !default;
|
||||||
|
$picnic-color-variation: 10% !default;
|
||||||
|
$picnic-transparency: .2 !default;
|
||||||
|
|
||||||
|
|
||||||
|
// Spaces
|
||||||
|
$picnic-separation: .6em !default;
|
||||||
|
$picnic-breakpoint: 60em !default;
|
||||||
|
|
||||||
|
|
||||||
|
// Shapes
|
||||||
|
$picnic-radius: .2em !default;
|
||||||
|
$picnic-border: 1px solid #ccc !default;
|
||||||
|
$picnic-shadow: 0 0 .2em rgba($picnic-black, $picnic-transparency) !default;
|
||||||
|
|
||||||
|
// Transitions
|
||||||
|
$picnic-transition: all .3s;
|
|
@ -0,0 +1,12 @@
|
||||||
|
export class ViewController {
|
||||||
|
constructor(state, viewName, defaultState = {}) {
|
||||||
|
// Store the global app state so it's accessible but out of the way.
|
||||||
|
this.appState = state;
|
||||||
|
|
||||||
|
// Give this view its own state within the appState.
|
||||||
|
if (!this.appState.viewStates.hasOwnProperty(viewName)) {
|
||||||
|
this.appState.viewStates[viewName] = defaultState;
|
||||||
|
}
|
||||||
|
this.state = this.appState.viewStates[viewName];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { ViewController } from '../controller';
|
||||||
|
|
||||||
|
export class HomeController extends ViewController {
|
||||||
|
constructor(state) {
|
||||||
|
// Super passes state, view name, and default state to ViewController,
|
||||||
|
// which stores state in this.appState and the view controller's state to this.state
|
||||||
|
super(state, 'home', {
|
||||||
|
messages: [
|
||||||
|
'hello',
|
||||||
|
'test',
|
||||||
|
'yay',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// If using controller methods in an input's onchange or onclick instance,
|
||||||
|
// either bind the class's 'this' instance to the method first...
|
||||||
|
// or use `onclick=${() => controller.submit()}` to maintain the 'this' of the class instead.
|
||||||
|
}
|
||||||
|
|
||||||
|
get messages() {
|
||||||
|
return [...this.state.messages];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
import html from 'choo/html';
|
||||||
|
|
||||||
|
import './styles.scss'; // Creates a separate CSS file, but allows better code splitting.
|
||||||
|
// We'll see if code splitting is worth it in the end or if we should combine everything into `src/index.scss`
|
||||||
|
import { HomeController } from './controller'; // The controller for this view, where processing should happen.
|
||||||
|
|
||||||
|
// This is the view function that is exported and used in the view manager.
|
||||||
|
export const homeView = (state, emit) => {
|
||||||
|
const controller = new HomeController(state);
|
||||||
|
|
||||||
|
// Returning an array in a view allows non-shared parent HTML elements.
|
||||||
|
// This one doesn't have the problem right now, but it's good to remember.
|
||||||
|
return [
|
||||||
|
html`<section>
|
||||||
|
<h2 class="subtitle">An attempt at a viable alternative to Goodreads</h2>
|
||||||
|
|
||||||
|
<article class="flex two">
|
||||||
|
<div class="half">
|
||||||
|
<div class="card">
|
||||||
|
<header>
|
||||||
|
<p>Still gotta figure out a design.</p>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="half">
|
||||||
|
<div class="card">
|
||||||
|
<header>
|
||||||
|
<p>It's early days, my friends!</p>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="test">
|
||||||
|
${controller.messages.map(message => {
|
||||||
|
return html`<p>${message}</p>`;
|
||||||
|
})}
|
||||||
|
</article>
|
||||||
|
</section>`,
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
.test {
|
||||||
|
background-color: #dddddd;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
border: 1px solid #444444;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import html from 'choo/html';
|
||||||
|
|
||||||
|
import { homeView } from './home';
|
||||||
|
|
||||||
|
export const viewManager = (state, emit) => {
|
||||||
|
// In viewManager all we are doing is checking the app's state
|
||||||
|
// and passing the state and emit to the relevant view.
|
||||||
|
let htmlContent = html`<div>loading</div>`;
|
||||||
|
switch (state.currentView) {
|
||||||
|
case 'home':
|
||||||
|
default: {
|
||||||
|
htmlContent = homeView(state, emit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a wrapper for view content that includes global header/footer
|
||||||
|
let view = html`<body>
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<div class="brand">
|
||||||
|
<a href="./">
|
||||||
|
<h1>Unnamed Book Tracker</h1>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- responsive-->
|
||||||
|
<input id="navMenu" type="checkbox" class="show">
|
||||||
|
<label for="navMenu" class="burger pseudo button">≡</label>
|
||||||
|
|
||||||
|
<div class="menu">
|
||||||
|
<a href="https://gitlab.com/Alamantus/book-tracker" class="pseudo button">Repo</a>
|
||||||
|
<a href="https://gitter.im/book-tracker/general" class="pseudo button">Chat</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
${htmlContent}
|
||||||
|
</main>
|
||||||
|
</body>`;
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
Loading…
Reference in New Issue