<!DOCTYPE html>
<html>

<head>
  <link href="//cdn.jsdelivr.net/picnicss/4.1.1/picnic.min.css" rel="stylesheet">
  <link rel="stylesheet" href="style.css">
  <meta charset="utf-8">
  <title>Mag.JS</title>
</head>

<body>

  <nav>
    <h1>Hello Mag.JS!</h1>
    <a target="_tab" href="https://github.com/magnumjs/mag.js">GitHub</a>
  </nav>

  <article class="card">
    <header>
      <h2>Templating Comps!</h2>
      </header>
      
    <footer>
      <div id="app">
        <todos-app>Loading..</todos-app>
      </div>
    </footer>
  </article>

  <script src="//rawgit.com/magnumjs/mag.js/master/mag-latest.min.js"></script>
  <script src="//rawgit.com/magnumjs/mag.js/master/dist/mag.addons.0.22.min.js"></script>
  <script src="//rawgit.com/magnumjs/mag.js/master/src/extends/mag-template.js"></script>

  <script src="messages/messages-module.js"></script>
  <script src="messages/messages-comps.js"></script>
  <script src="todos/todos-module.js"></script>
  <script src="todos/todos-comps.js"></script>
  <script src="todos/todos-data.js"></script>
  <script src="app.js"></script>

</body>

</html>
//Boot App:
var App = {}

App.defaultProps = {
  data: TodoStore
}

App.controller = function(props) {
  //Load template container
  this['todos-app'] = mag(Todos.templateUrl, Todos, props)

}

mag.module("app", App, App.defaultProps)
li:empty,
.hide {
  display: none;
}

a {
  display: block;
}

a:after {
  content: " \bb";
}

.mainButton {
  font-size: 1.5em;
}

nav a {
  float: right;
  margin-top: -50px;
}

body {
  background: #fff;
  text-align: left;
  width: 90%;
  max-width: 960px;
  margin: 0 auto;
  padding: 20px 0 0;
}

nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3em;
  padding: 0 .6em;
  background: #fff;
  box-shadow: 0 0 0.2em rgba(17, 17, 17, 0.2);
  z-index: 10000;
  transition: all .3s;
  transform-style: preserve-3d;
}

header {
  font-weight: bold;
  position: relative;
  border-bottom: 1px solid #eee;
  padding: .6em .8em;
}

footer {
  padding: .8em;
}

article {
  top: 100px;
}

.card {
  max-width: 100%;
  display: block;
  position: relative;
  box-shadow: 0;
  border-radius: .2em;
  border: 1px solid #ccc;
  overflow: hidden;
  text-align: left;
  background: #fff;
  margin-bottom: .6em;
  padding: 6px;
  transition: all .3s ease;
}

.message span:empty {
  display: none;
}

.message span {
  text-align: center;
  padding: 12px;
  float: right;
  color: white;
  z-index: 10;
  margin: 4px;
}

.success {
  opacity: 1;
  transition: opacity 1s ease-in-out;
  background: #2ecc40;
}

.error {
  opacity: 1;
  transition: opacity 1s ease-in-out;
  background: #ff4136;
}

.fadeIn {
  opacity: 1;
  transition: opacity 1s ease-in-out;
}

.fadeOut {
  opacity: 0
}
#Mag.JS

## plunk boilerplate of "Things"

Basic template loader example 
with mixing state and stateless components
and observable data store
<div>
  <h2>TODOS!</h2>
  <div class="add">
    <form>
      <input name="text" autocomplete="off">
      <messages></messages>
      <button>Add</button>
    </form>
  </div>
  <div class="list">
    <ul>
      <li class="hide">
        <name></name>
      </li>
    </ul>
  </div>
</div>
var Todos = {
  templateUrl: 'todos/todos.html'
}

var Messenger;
Todos.controller = function(props) {

  this.data = props.data


  //Load module template:
  mag(Messages)
    .then((messenger) => {
      //Get module instance:

      Messenger = messenger;
      this.messages = Messenger();
      props.data.getTodos()
    });

  this.didload = () => Todos.UI.mount()
}

Todos.view = function(state, props) {

  Todos.UI.list({
    items: state.data.orderBy('id')
  })

  Todos.UI.add({
    handleAdd: function(e) {
      e.preventDefault();
      if (state.text) {
        //Disable submit
        state.button = {
            _disabled: true,
          }
          //Display user notification:
        Messenger({
          message: 'Saving!'
        })

        //Promise
        props.data.addTodo(state.text)
          .then(() => {
            //Re-enable
            state.button = {
              _disabled: null,
            }

            //Display Message
            Messenger({
              message: 'Saved successfully!'
            })

          })

        state.text = ''
      } else {
        Messenger({
          message: 'Invalid - field is required!',
          type: 'error'
        })
      }
    }
  })

}
//Data store:
var TodoStore = {
  todos: [],
  setTodos: function(data) {
    mag.merge(TodoStore.todos, data);
  },
  getTodos: function() {
    return mag.request('todos/todos.json')
    .then((data) => TodoStore.setTodos(data))
  },
  orderBy: function(field) {
    return TodoStore.todos.sort(function(a, b) {
      if (a[field] > b[field]) return -1;
      if (a[field] < b[field]) return 1;
      return 0;
    })
  },
  addTodo: function(item) {
    //Emulate async
    return new Promise((resolve) => {
      setTimeout(() => {
        TodoStore.todos.push({
          id: TodoStore.todos.length + 1,
          name: item
        })
        resolve();
      }, 550)
    })
  }
}
//UI Components

Todos.UI = {
  list: () => {},
  add: () => {}
}

//Define
Todos.UI.listFun = ({items}) => ({
  li: items.map((item)=>{
    return {
      _className: {'hide': false},
      name: item.name
    }
  })
})

Todos.UI.addFun = ({handleAdd}) => ({
  form: {_onSubmit: handleAdd}
})


Todos.UI.mount = () => {
  //Attach
  Todos.UI.list = mag('list', Todos.UI.listFun)
  Todos.UI.add = mag('add', Todos.UI.addFun)
}
[{
    "id": 1,
    "name": "Milk"
  }, {
    "id": 2,
    "name": "Eggs"
  }]
<div class="message">
  <div class="messages">
    <span class="success"></span>
  </div>
</div>
var Messages = {
  templateUrl: 'messages/messages.html',
  timer: 0,
}
Messages.controller = function() {
  //Load Attach UI Components:
  this.didload = () => Messages.UI.mount()
}

Messages.view = function(state, props) {
  //Already running, but new message?
  if (Messages.timer) {
    //reset
    Messages.timer = clearTimeout(Messages.timer)
  }

  if (props.message) {
    //Fade in
    var type = props.type ? props.type : 'success'
      //show messages
    Messages.UI.message({
      messages: {
        _text: props.message,
        _class: type + ' fadeIn'
      }
    })

    //If none schedule or cancel if already
    Messages.timer = setTimeout(() => {
      //Fade out

      Messages.UI.message({
        messages: {
          _class: type + ' fadeOut',
          _text: props.message
        }
      })

      Messages.timer = props.message = 0
    }, 1000);

  }

}
//UI Components

Messages.UI = {
  message: () => {}
}

Messages.UI.messageFun = ({messages}) => ({
  span: messages
})


Messages.UI.mount = () => {
  //Attach
  Messages.UI.message = mag('messages', Messages.UI.messageFun)
}