<!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)
}