Add starter files and structure with comments

This commit is contained in:
Robbie Antenesse 2019-09-04 12:07:25 -06:00
parent b0282ec705
commit d98f93a8a4
11 changed files with 478 additions and 0 deletions

19
src/index.html Normal file
View File

@ -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>

38
src/index.js Normal file
View File

@ -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

42
src/index.scss Normal file
View File

@ -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';

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

12
src/views/controller.js Normal file
View File

@ -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];
}
}

View File

@ -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];
}
}

41
src/views/home/index.js Normal file
View File

@ -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>`,
];
}

View File

@ -0,0 +1,8 @@
.test {
background-color: #dddddd;
padding: 10px;
p {
border: 1px solid #444444;
}
}

44
src/views/manager.js Normal file
View File

@ -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">&#8801;</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;
}