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