diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..123cc21 --- /dev/null +++ b/src/index.html @@ -0,0 +1,19 @@ + + + + + + + + book-tracker + + + + + + + + + + + \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..eef542d --- /dev/null +++ b/src/index.js @@ -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 `` tag with the content of the Choo app \ No newline at end of file diff --git a/src/index.scss b/src/index.scss new file mode 100644 index 0000000..b6f4439 --- /dev/null +++ b/src/index.scss @@ -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 , 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'; diff --git a/src/styles/picnic-customizations/_custom.scss b/src/styles/picnic-customizations/_custom.scss new file mode 100644 index 0000000..93d1b36 --- /dev/null +++ b/src/styles/picnic-customizations/_custom.scss @@ -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; + } +} diff --git a/src/styles/picnic-customizations/theme/_colors.scss b/src/styles/picnic-customizations/theme/_colors.scss new file mode 100644 index 0000000..f9ca4fa --- /dev/null +++ b/src/styles/picnic-customizations/theme/_colors.scss @@ -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; diff --git a/src/styles/picnic-customizations/theme/_theme.scss b/src/styles/picnic-customizations/theme/_theme.scss new file mode 100644 index 0000000..9019d8f --- /dev/null +++ b/src/styles/picnic-customizations/theme/_theme.scss @@ -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; diff --git a/src/views/controller.js b/src/views/controller.js new file mode 100644 index 0000000..e422f26 --- /dev/null +++ b/src/views/controller.js @@ -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]; + } +} \ No newline at end of file diff --git a/src/views/home/controller.js b/src/views/home/controller.js new file mode 100644 index 0000000..e47c963 --- /dev/null +++ b/src/views/home/controller.js @@ -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]; + } +} \ No newline at end of file diff --git a/src/views/home/index.js b/src/views/home/index.js new file mode 100644 index 0000000..8052518 --- /dev/null +++ b/src/views/home/index.js @@ -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`
+

An attempt at a viable alternative to Goodreads

+ +
+
+
+
+

Still gotta figure out a design.

+
+
+
+
+
+
+

It's early days, my friends!

+
+
+
+
+ +
+ ${controller.messages.map(message => { + return html`

${message}

`; + })} +
+
`, + ]; +} \ No newline at end of file diff --git a/src/views/home/styles.scss b/src/views/home/styles.scss new file mode 100644 index 0000000..e92a2b3 --- /dev/null +++ b/src/views/home/styles.scss @@ -0,0 +1,8 @@ +.test { + background-color: #dddddd; + padding: 10px; + + p { + border: 1px solid #444444; + } +} \ No newline at end of file diff --git a/src/views/manager.js b/src/views/manager.js new file mode 100644 index 0000000..c588776 --- /dev/null +++ b/src/views/manager.js @@ -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`
loading
`; + 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` +
+ +
+ +
+ ${htmlContent} +
+`; + + return view; +} \ No newline at end of file