From 6db982a601f50bd600ddefc589afd328ae91a567 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 5 Jul 2018 22:32:46 +0100 Subject: [PATCH 001/164] create basic files for "elmish" Todo List #44 --- examples/todo-list/app.js | 89 +++++++++++++++++++++++++++++++++++ examples/todo-list/elmish.js | 89 +++++++++++++++++++++++++++++++++++ examples/todo-list/index.html | 29 ++++++++++++ examples/todo-list/test.js | 74 +++++++++++++++++++++++++++++ todo-list.md | 0 5 files changed, 281 insertions(+) create mode 100644 examples/todo-list/app.js create mode 100644 examples/todo-list/elmish.js create mode 100644 examples/todo-list/index.html create mode 100644 examples/todo-list/test.js create mode 100644 todo-list.md diff --git a/examples/todo-list/app.js b/examples/todo-list/app.js new file mode 100644 index 00000000..a28d87c4 --- /dev/null +++ b/examples/todo-list/app.js @@ -0,0 +1,89 @@ +// Define the Component's Actions: +var Inc = 'inc'; // increment the counter +var Dec = 'dec'; // decrement the counter +var Res = 'reset'; // reset counter: git.io/v9KJk + +function update(model, action) { // Update function takes the current state + var parts = action ? action.split('-') : []; // e.g: inc-0 where 0 is the counter "id" + var act = parts[0]; + var index = parts[1] || 0; + var new_model = JSON.parse(JSON.stringify(model)) // "clone" the model + switch(act) { // and an action (String) runs a switch + case Inc: + new_model.counters[index] = model.counters[index] + 1; + break; + case Dec: + new_model.counters[index] = model.counters[index] - 1; + break; + case Res: // use ES6 Array.fill to create a new array with values set to 0: + new_model.counters[index] = 0; + break; + default: return model; // if action not defined, return curent state. + } + return new_model; +} + +function view(signal, model, root) { + empty(root); // clear root element before re-rendering the App (DOM). + model.counters.map(function(counter, index) { + return container(index, [ // wrap DOM nodes in an "container" + button('+', signal, Inc + '-' + index), // append index to action + div('count', counter), // create div w/ count as text + button('-', signal, Dec + '-' + index), // decrement counter + button('Reset', signal, Res + '-' + index) // reset counter + ]); + }).forEach(function (el) { root.appendChild(el) }); // forEach is ES5 so IE9+ +} + +// Mount Function receives all MUV and mounts the app in the "root" DOM Element +function mount(model, update, view, root_element_id) { + var root = document.getElementById(root_element_id); // root DOM element + function signal(action) { // signal function takes action + return function callback() { // and returns callback + model = update(model, action); // update model according to action + view(signal, model, root); // subsequent re-rendering + }; + }; + view(signal, model, root); // render initial model (once) +} + +// The following are "Helper" Functions which each "Do ONLY One Thing" and are +// used in the "View" function to render the Model (State) to the Browser DOM: + +// empty the contents of a given DOM element "node" (before re-rendering) +function empty(node) { + while (node.firstChild) { + node.removeChild(node.firstChild); + } +} // Inspired by: stackoverflow.com/a/3955238/1148249 + +// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section +function container(index, elements) { + var con = document.createElement('section'); + con.id = index; + con.className = 'counter'; + elements.forEach(function(el) { con.appendChild(el) }); + return con; +} + +function button(text, signal, action) { + var button = document.createElement('button'); + var text = document.createTextNode(text); // human-readable button text + button.appendChild(text); // text goes *inside* not attrib + button.className = action.split('-')[0]; // use action as CSS class + button.id = action; + // console.log(signal, ' action:', action) + button.onclick = signal(action); // onclick tells how to process + return button; // return the DOM node(s) +} // how to create a button in JavaScript: stackoverflow.com/a/8650996/1148249 + +function div(divid, text) { + var div = document.createElement('div'); + div.id = divid; + div.className = divid; + if(text !== undefined) { // if text is passed in render it in a "Text Node" + var txt = document.createTextNode(text); + div.appendChild(txt); + } + return div; +} diff --git a/examples/todo-list/elmish.js b/examples/todo-list/elmish.js new file mode 100644 index 00000000..a28d87c4 --- /dev/null +++ b/examples/todo-list/elmish.js @@ -0,0 +1,89 @@ +// Define the Component's Actions: +var Inc = 'inc'; // increment the counter +var Dec = 'dec'; // decrement the counter +var Res = 'reset'; // reset counter: git.io/v9KJk + +function update(model, action) { // Update function takes the current state + var parts = action ? action.split('-') : []; // e.g: inc-0 where 0 is the counter "id" + var act = parts[0]; + var index = parts[1] || 0; + var new_model = JSON.parse(JSON.stringify(model)) // "clone" the model + switch(act) { // and an action (String) runs a switch + case Inc: + new_model.counters[index] = model.counters[index] + 1; + break; + case Dec: + new_model.counters[index] = model.counters[index] - 1; + break; + case Res: // use ES6 Array.fill to create a new array with values set to 0: + new_model.counters[index] = 0; + break; + default: return model; // if action not defined, return curent state. + } + return new_model; +} + +function view(signal, model, root) { + empty(root); // clear root element before re-rendering the App (DOM). + model.counters.map(function(counter, index) { + return container(index, [ // wrap DOM nodes in an "container" + button('+', signal, Inc + '-' + index), // append index to action + div('count', counter), // create div w/ count as text + button('-', signal, Dec + '-' + index), // decrement counter + button('Reset', signal, Res + '-' + index) // reset counter + ]); + }).forEach(function (el) { root.appendChild(el) }); // forEach is ES5 so IE9+ +} + +// Mount Function receives all MUV and mounts the app in the "root" DOM Element +function mount(model, update, view, root_element_id) { + var root = document.getElementById(root_element_id); // root DOM element + function signal(action) { // signal function takes action + return function callback() { // and returns callback + model = update(model, action); // update model according to action + view(signal, model, root); // subsequent re-rendering + }; + }; + view(signal, model, root); // render initial model (once) +} + +// The following are "Helper" Functions which each "Do ONLY One Thing" and are +// used in the "View" function to render the Model (State) to the Browser DOM: + +// empty the contents of a given DOM element "node" (before re-rendering) +function empty(node) { + while (node.firstChild) { + node.removeChild(node.firstChild); + } +} // Inspired by: stackoverflow.com/a/3955238/1148249 + +// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section +function container(index, elements) { + var con = document.createElement('section'); + con.id = index; + con.className = 'counter'; + elements.forEach(function(el) { con.appendChild(el) }); + return con; +} + +function button(text, signal, action) { + var button = document.createElement('button'); + var text = document.createTextNode(text); // human-readable button text + button.appendChild(text); // text goes *inside* not attrib + button.className = action.split('-')[0]; // use action as CSS class + button.id = action; + // console.log(signal, ' action:', action) + button.onclick = signal(action); // onclick tells how to process + return button; // return the DOM node(s) +} // how to create a button in JavaScript: stackoverflow.com/a/8650996/1148249 + +function div(divid, text) { + var div = document.createElement('div'); + div.id = divid; + div.className = divid; + if(text !== undefined) { // if text is passed in render it in a "Text Node" + var txt = document.createTextNode(text); + div.appendChild(txt); + } + return div; +} diff --git a/examples/todo-list/index.html b/examples/todo-list/index.html new file mode 100644 index 00000000..3d78a4d9 --- /dev/null +++ b/examples/todo-list/index.html @@ -0,0 +1,29 @@ + + + + + + Elm Architecture in JS - Counter Reset + + + + + +
+ + + +
+
+ + + + + + + + + diff --git a/examples/todo-list/test.js b/examples/todo-list/test.js new file mode 100644 index 00000000..7768095c --- /dev/null +++ b/examples/todo-list/test.js @@ -0,0 +1,74 @@ +var id = 'test-app'; + +test('update({counters:[0]}) returns {counters:[0]} (current state unmodified)', + function(assert) { + var result = update({counters:[0]}); + assert.equal(result.counters[0], 0); +}); + +test('Test Update increment: update(1, "inc") returns 2', function(assert) { + var result = update({counters: [1] }, "inc"); + console.log('result', result); + assert.equal(result.counters[0], 2); +}); + + +test('Test Update decrement: update(1, "dec") returns 0', function(assert) { + var result = update({counters: [1] }, "dec"); + assert.equal(result.counters[0], 0); +}); + +test('Test negative state: update(-9, "inc") returns -8', function(assert) { + var result = update({counters: [-9] }, "inc"); + assert.equal(result.counters[0], -8); +}); + +test('mount({model: 7, update: update, view: view}, "' + + id +'") sets initial state to 7', function(assert) { + mount({counters:[7]}, update, view, id); + var state = document.getElementById(id) + .getElementsByClassName('count')[0].textContent; + assert.equal(state, 7); +}); + +test('empty("test-app") should clear DOM in root node', function(assert) { + empty(document.getElementById(id)); + mount({counters:[7]}, update, view, id); + empty(document.getElementById(id)); + var result = document.getElementById(id).innerHtml + assert.equal(result, undefined); +}); + +test('click on "+" button to re-render state (increment model by 1)', +function(assert) { + document.body.appendChild(div(id)); + mount({counters:[7]}, update, view, id); + document.getElementById(id).getElementsByClassName('inc')[0].click(); + var state = document.getElementById(id) + .getElementsByClassName('count')[0].textContent; + assert.equal(state, 8); // model was incremented successfully + empty(document.getElementById(id)); // clean up after tests +}); + +// Reset Functionality + +test('Test reset counter when model/state is 6 returns 0', function(assert) { + var result = update({counters:[7]}, "reset"); + assert.equal(result.counters[0], 0); +}); + +test('reset button should be present on page', function(assert) { + var reset = document.getElementsByClassName('reset'); + assert.equal(reset.length, 3); +}); + +test('Click reset button resets state to 0', function(assert) { + mount({counters:[7]}, update, view, id); + var root = document.getElementById(id); + assert.equal(root.getElementsByClassName('count')[0].textContent, 7); + var btn = root.getElementsByClassName("reset")[0]; // click reset button + btn.click(); // Click the Reset button! + var state = root.getElementsByClassName('count')[0].textContent; + assert.equal(state, 0); // state was successfully reset to 0! + empty(root); // clean up after tests +}); diff --git a/todo-list.md b/todo-list.md new file mode 100644 index 00000000..e69de29b From cc1b8b8135022ac14d922f39c111a3e0600150cd Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 9 Jul 2018 23:16:25 +0100 Subject: [PATCH 002/164] adds todo-list.md (outline) for #44 --- examples/todo-list/{app.js => todo-app.js} | 0 todo-list.md | 40 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) rename examples/todo-list/{app.js => todo-app.js} (100%) diff --git a/examples/todo-list/app.js b/examples/todo-list/todo-app.js similarity index 100% rename from examples/todo-list/app.js rename to examples/todo-list/todo-app.js diff --git a/todo-list.md b/todo-list.md index e69de29b..0c7d5a71 100644 --- a/todo-list.md +++ b/todo-list.md @@ -0,0 +1,40 @@ +# Elm(ish) Todo List (TodoMVC) Mini App + +If you've made it this far, give yourself a pat on the back! + +## Why? + +Practice your understanding of The Elm Architecture (TEA) +by creating a "real world" useable App. + +## What? + +Use our "TEA" knowledge to build a simple "Todo List" Application +in 30 mins or less! + +### Todo List? + +If you are _unfamiliar_ with Todo lists, + +### TodoMVC? + +If you have + ++ Website: http://todomvc.com ++ GitHub project: https://github.com/tastejs/todomvc + +## _Who?_ + +This tutorial is for everyone who wants to _apply_ their "TEA" knowledge +and _think_ about the basics of a Todo List Application. + + +## _How?_ + + +### Todo List Basics + +> Thinking about a challenge from +["first principals"](https://en.wikipedia.org/wiki/First_principle) +is a great way to understanding it. +This is the "physics" approach. see: From 8908312f3b760592b155a24d0a022f1a23850e33 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 10 Jul 2018 20:56:39 +0100 Subject: [PATCH 003/164] add base TodoMVC CSS file from https://github.com/tastejs/todomvc/tree/gh-pages/examples/vanillajs for #44 --- examples/todo-list/index.html | 11 +- examples/todo-list/todo.html | 51 +++ examples/todo-list/todomvc-app.css | 379 +++++++++++++++++++++ examples/todo-list/todomvc-common-base.css | 141 ++++++++ 4 files changed, 578 insertions(+), 4 deletions(-) create mode 100644 examples/todo-list/todo.html create mode 100644 examples/todo-list/todomvc-app.css create mode 100644 examples/todo-list/todomvc-common-base.css diff --git a/examples/todo-list/index.html b/examples/todo-list/index.html index 3d78a4d9..e34391b0 100644 --- a/examples/todo-list/index.html +++ b/examples/todo-list/index.html @@ -1,18 +1,21 @@ - + - Elm Architecture in JS - Counter Reset + Elm(ish) Todo List! - + + +
- + +
diff --git a/examples/todo-list/todo.html b/examples/todo-list/todo.html new file mode 100644 index 00000000..73b59069 --- /dev/null +++ b/examples/todo-list/todo.html @@ -0,0 +1,51 @@ + + + + + VanillaJS • TodoMVC + + + + +
+
+

todos

+ +
+
+ + +
    +
    + +
    + + + + + + + + + + + diff --git a/examples/todo-list/todomvc-app.css b/examples/todo-list/todomvc-app.css new file mode 100644 index 00000000..3ac79f05 --- /dev/null +++ b/examples/todo-list/todomvc-app.css @@ -0,0 +1,379 @@ +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + font-weight: inherit; + color: inherit; + -webkit-appearance: none; + appearance: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #f5f5f5; + color: #4d4d4d; + min-width: 230px; + max-width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-weight: 300; +} + +:focus { + outline: 0; +} + +.hidden { + display: none; +} + +.todoapp { + background: #fff; + margin: 130px 0 40px 0; + position: relative; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.1); +} + +.todoapp input::-webkit-input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp input::-moz-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp input::input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp h1 { + position: absolute; + top: -155px; + width: 100%; + font-size: 100px; + font-weight: 100; + text-align: center; + color: rgba(175, 47, 47, 0.15); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +.new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + font-weight: inherit; + line-height: 1.4em; + border: 0; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.003); + box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); +} + +.main { + position: relative; + z-index: 2; + border-top: 1px solid #e6e6e6; +} + +.toggle-all { + width: 1px; + height: 1px; + border: none; /* Mobile Safari */ + opacity: 0; + position: absolute; + right: 100%; + bottom: 100%; +} + +.toggle-all + label { + width: 60px; + height: 34px; + font-size: 0; + position: absolute; + top: -52px; + left: -13px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); +} + +.toggle-all + label:before { + content: '❯'; + font-size: 22px; + color: #e6e6e6; + padding: 10px 27px 10px 27px; +} + +.toggle-all:checked + label:before { + color: #737373; +} + +.todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +.todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px solid #ededed; +} + +.todo-list li:last-child { + border-bottom: none; +} + +.todo-list li.editing { + border-bottom: none; + padding: 0; +} + +.todo-list li.editing .edit { + display: block; + width: 506px; + padding: 12px 16px; + margin: 0 0 0 43px; +} + +.todo-list li.editing .view { + display: none; +} + +.todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + border: none; /* Mobile Safari */ + -webkit-appearance: none; + appearance: none; +} + +.todo-list li .toggle { + opacity: 0; +} + +.todo-list li .toggle + label { + /* + Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 + IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ + */ + background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); + background-repeat: no-repeat; + background-position: center left; +} + +.todo-list li .toggle:checked + label { + background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E'); +} + +.todo-list li label { + word-break: break-all; + padding: 15px 15px 15px 60px; + display: block; + line-height: 1.2; + transition: color 0.4s; +} + +.todo-list li.completed label { + color: #d9d9d9; + text-decoration: line-through; +} + +.todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 30px; + color: #cc9a9a; + margin-bottom: 11px; + transition: color 0.2s ease-out; +} + +.todo-list li .destroy:hover { + color: #af5b5e; +} + +.todo-list li .destroy:after { + content: '×'; +} + +.todo-list li:hover .destroy { + display: block; +} + +.todo-list li .edit { + display: none; +} + +.todo-list li.editing:last-child { + margin-bottom: -1px; +} + +.footer { + color: #777; + padding: 10px 15px; + height: 20px; + text-align: center; + border-top: 1px solid #e6e6e6; +} + +.footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 50px; + overflow: hidden; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), + 0 8px 0 -3px #f6f6f6, + 0 9px 1px -3px rgba(0, 0, 0, 0.2), + 0 16px 0 -6px #f6f6f6, + 0 17px 2px -6px rgba(0, 0, 0, 0.2); +} + +.todo-count { + float: left; + text-align: left; +} + +.todo-count strong { + font-weight: 300; +} + +.filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +.filters li { + display: inline; +} + +.filters li a { + color: inherit; + margin: 3px; + padding: 3px 7px; + text-decoration: none; + border: 1px solid transparent; + border-radius: 3px; +} + +.filters li a:hover { + border-color: rgba(175, 47, 47, 0.1); +} + +.filters li a.selected { + border-color: rgba(175, 47, 47, 0.2); +} + +.clear-completed, +html .clear-completed:active { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + cursor: pointer; +} + +.clear-completed:hover { + text-decoration: underline; +} + +.info { + margin: 65px auto 0; + color: #bfbfbf; + font-size: 10px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-align: center; +} + +.info p { + line-height: 1; +} + +.info a { + color: inherit; + text-decoration: none; + font-weight: 400; +} + +.info a:hover { + text-decoration: underline; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox +*/ +@media screen and (-webkit-min-device-pixel-ratio:0) { + .toggle-all, + .todo-list li .toggle { + background: none; + } + + .todo-list li .toggle { + height: 40px; + } +} + +@media (max-width: 430px) { + .footer { + height: 50px; + } + + .filters { + bottom: 10px; + } +} diff --git a/examples/todo-list/todomvc-common-base.css b/examples/todo-list/todomvc-common-base.css new file mode 100644 index 00000000..da65968a --- /dev/null +++ b/examples/todo-list/todomvc-common-base.css @@ -0,0 +1,141 @@ +hr { + margin: 20px 0; + border: 0; + border-top: 1px dashed #c5c5c5; + border-bottom: 1px dashed #f7f7f7; +} + +.learn a { + font-weight: normal; + text-decoration: none; + color: #b83f45; +} + +.learn a:hover { + text-decoration: underline; + color: #787e7e; +} + +.learn h3, +.learn h4, +.learn h5 { + margin: 10px 0; + font-weight: 500; + line-height: 1.2; + color: #000; +} + +.learn h3 { + font-size: 24px; +} + +.learn h4 { + font-size: 18px; +} + +.learn h5 { + margin-bottom: 0; + font-size: 14px; +} + +.learn ul { + padding: 0; + margin: 0 0 30px 25px; +} + +.learn li { + line-height: 20px; +} + +.learn p { + font-size: 15px; + font-weight: 300; + line-height: 1.3; + margin-top: 0; + margin-bottom: 0; +} + +#issue-count { + display: none; +} + +.quote { + border: none; + margin: 20px 0 60px 0; +} + +.quote p { + font-style: italic; +} + +.quote p:before { + content: '“'; + font-size: 50px; + opacity: .15; + position: absolute; + top: -20px; + left: 3px; +} + +.quote p:after { + content: '”'; + font-size: 50px; + opacity: .15; + position: absolute; + bottom: -42px; + right: 3px; +} + +.quote footer { + position: absolute; + bottom: -40px; + right: 0; +} + +.quote footer img { + border-radius: 3px; +} + +.quote footer a { + margin-left: 5px; + vertical-align: middle; +} + +.speech-bubble { + position: relative; + padding: 10px; + background: rgba(0, 0, 0, .04); + border-radius: 5px; +} + +.speech-bubble:after { + content: ''; + position: absolute; + top: 100%; + right: 30px; + border: 13px solid transparent; + border-top-color: rgba(0, 0, 0, .04); +} + +.learn-bar > .learn { + position: absolute; + width: 272px; + top: 8px; + left: -300px; + padding: 10px; + border-radius: 5px; + background-color: rgba(255, 255, 255, .6); + transition-property: left; + transition-duration: 500ms; +} + +@media (min-width: 899px) { + .learn-bar { + width: auto; + padding-left: 300px; + } + + .learn-bar > .learn { + left: 8px; + } +} From 7813efd5cb22166f5ff52b61ac5f8f9e1c61fb9d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 11 Jul 2018 22:44:41 +0100 Subject: [PATCH 004/164] adds complete TodoMVC requirements to todo-list.md for #44 --- todo-list.md | 152 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 143 insertions(+), 9 deletions(-) diff --git a/todo-list.md b/todo-list.md index 0c7d5a71..ead26ed4 100644 --- a/todo-list.md +++ b/todo-list.md @@ -1,40 +1,174 @@ # Elm(ish) Todo List (TodoMVC) Mini App If you've made it this far, give yourself a pat on the back! +You are about to "_level up_" your JavaScript and "TEA" skills! + ## Why? -Practice your understanding of The Elm Architecture (TEA) -by creating a "real world" useable App. +Consolidate your understanding of The Elm Architecture (TEA) +by creating a "real world" _useable_ App. ## What? -Use our "TEA" knowledge to build a simple "Todo List" Application -in 30 mins or less! +_Use_ our "TEA" knowledge to build a simple "Todo List" Application.
    +Along the way we will touch upon: + ++ [x] The Document Object Model (DOM) ++ [x] Browser Routing/Navigation ++ [x] Local Storage for Offline Support + +We will be abstracting all "TEA" related code +into a file called `elmish.js` +so that our Todo List application can be as simple as possible. ### Todo List? If you are _unfamiliar_ with Todo lists, +they are a way of keeping a list of the tasks that need to be done. +see: https://en.wikipedia.org/wiki/Time_management#Setting_priorities_and_goals + +Todo Lists or "Checklists" are the _best_ way of tracking tasks. +Atul Gawande wrote a _fantastic_ book on this subject: +https://www.amazon.com/Checklist-Manifesto-How-Things-Right/dp/0312430000 +Watch: https://www.youtube.com/results?search_query=checklist+manifesto ### TodoMVC? -If you have +If you have not come across TodoMVC before, +it's a sample application to showcase various "frontend" frameworks. +![TodoMVC-intro](https://user-images.githubusercontent.com/194400/42624420-4528a3c6-85bd-11e8-8b92-9b1c8951ba35.png) + + +We highly recommend checking out the following links: + Website: http://todomvc.com + GitHub project: https://github.com/tastejs/todomvc +For our purposes we will simply be re-using the **`CSS`** +to make our TEA Todo List _look_ nice. +All the JavaScript code will be written "_from scratch_" +to ensure that everything is clear. + ## _Who?_ This tutorial is for everyone who wants to _apply_ their "TEA" knowledge and _think_ about the basics of a Todo List Application. +> As always, if you get "stuck", _please_ open an issue: +https://github.com/dwyl/learn-elm-architecture-in-javascript/issues +by opening a question you help _everyone_ learn more effectively! + ## _How?_ +Our _first_ step is to _analyse_ the required functionality of a Todo List. + +### Todo List _Basic_ Functionality + +A todo list has only 2 basic functions: + +1. **Add** a `new` item to the list when the **`[Enter]`** key is pressed +2. **Check-off** an item as "**completed**" (_done/finished_) + +> **Add** item and **Check-off** is _exactly_ the "functionality" +you would have in a _paper_-based Todo List. + +#### TodoMVC "Advanced" Functionality + +In _addition_ to these basic functions, +**TodoMVC** has the ability to: ++ **Un-check** an item as to make it "**active**" (_still to be done_) ++ **Double-click/tap** on todo **item description** to **`edit` it**. ++ **Mark _all_ as complete** ++ **Click `X`** on item row to remove from list. -### Todo List Basics +#### `