Skip to main content

module salix::demo::todomvc::TodoMVC

rascal-0.34.0
salix-core-0.2.3

Usage

import salix::demo::todomvc::TodoMVC;

Source code

http://github.com/usethesource/salix-core/src/main/rascal/salix/demo/todomvc/TodoMVC.rsc

Dependencies

import salix::HTML;
import salix::Node;
import salix::Core;
import salix::App;
import salix::Index;
import List;

alias Model

tuple[list[Entry] entries, str field, int uid, str visibility]

alias Entry

tuple[str description, bool completed, bool editing, int id]

function todoMVCApp

SalixApp[Model] todoMVCApp(str id = "todoMVC") 
= makeApp(id, emptyModel, withIndex("TodoMVC", id, view, css = ["/salix/demo/todomvc/style.css"]), update);

function todoMVCWebApp

App[Model] todoMVCWebApp() 
= webApp(
todoMVCApp(),
|project://salix/src/main/rascal|
);

function emptyModel

Model emptyModel() = <[], "", 0, "All">;

function newEntry

Entry newEntry(str desc, int id) = <desc, false, false, id>;

data Msg

data Msg  
= noOp()
| updateField(str x)
| editingEntry(int id, bool editing)
| updateEntry(int id, str desc)
| add()
| delete(int id)
| deleteComplete()
| check(int id, bool checked)
| checkAll(bool checked)
| changeVisibility(str filt)
;

function updateEntry

Msg(str) updateEntry(int id) = Msg(str x) { return updateEntry(id, x); };

function update

Model update(Msg msg, Model model) {
switch (msg) {
case noOp():
;

case add(): {
if (model.field != "") {
model.uid += 1;
model.entries += [newEntry(model.field, model.uid)];
model.field = "";
}
}

case updateField(str s):
model.field = s;

case editingEntry(int id, bool isEditing):
if (int i <- [0..size(model.entries)], model.entries[i].id == id) {
model.entries[i].editing = isEditing;
}
//batch([attempt(Msg(value x) { return noOp(); }, focus /* ??? */)])>;

case updateEntry(int id, str task):
if (int i <- [0..size(model.entries)], model.entries[i].id == id) {
model.entries[i].description = task;
}

case delete(int id):
model.entries = [ e | Entry e <- model.entries, e.id != id ];

case deleteComplete():
model.entries = [ e | Entry e <- model.entries, !e.completed ];

case check(int id, bool isCompleted):
if (int i <- [0..size(model.entries)], model.entries[i].id == id) {
model.entries[i].completed = isCompleted;
}

case checkAll(bool isCompleted):
model.entries = [ e[completed=isCompleted] | Entry e <- model.entries ];

case changeVisibility(str visibility):
model.visibility = visibility;
}

return model;
}

function view

void view(Model model) {
div(class("todomvc-wrapper"), style(<"visibility", "hidden">), () {
section(class("todoapp"), () {
viewInput(model.field);
viewEntries(model.visibility, model.entries);
viewControls(model.visibility, model.entries);
});
infoFooter();
});
}

function viewInput

void viewInput(str task) {
header(class("header"), () {
h1("todos");
input(class("new-todo"),
placeholder("What needs to be done?"),
autofocus(true),
\value(task),
name("newTodo"),
onInput(updateField),
onEnter(add()));
});
}

function onEnter

Attr onEnter(Msg msg) = onKeyDown(Msg (int key) {
if (key == 13) {
return msg;
}
return noOp();
});

function viewEntries

void viewEntries(str visibility, list[Entry] entries) {
bool isVisible(Entry todo) = todo.completed
when visibility == "Completed";

bool isVisible(Entry todo) = !todo.completed
when visibility == "Active";

default bool isVisible(Entry _) = true;

bool allCompleted = all(e <- entries, e.completed);

str cssVisibility = entries == [] ? "hidden" : "visible";

section(class("main"), style(<"visibility", cssVisibility>), () {

input(class("toggle-all"),
\type("checkbox"), name("toggle"), checked(allCompleted),
onClick(checkAll(!allCompleted)));

label(\for("toggle-all"), "Mark all as complete");

ul(class("todo-list"), () {
for (Entry e <- entries, isVisible(e)) {
viewEntry(e);
}
});

});

}

function viewEntry

void viewEntry(Entry todo) {
li(classList(<"completed", todo.completed>, <"editing", todo.editing>), () {
div(class("view"), () {
input(class("toggle"), \type("checkbox"), checked(todo.completed),
onClick(check(todo.id, !todo.completed)));

label(onDoubleClick(editingEntry(todo.id, true)), todo.description);

button(class("destroy"), onClick(delete(todo.id)));
});

input(class("edit"), \value(todo.description), name("title"),
id("todo-<todo.id>"), onInput(updateEntry(todo.id)),
onBlur(editingEntry(todo.id, false)),
onEnter(editingEntry(todo.id, false)));
});
}

function viewControls

void viewControls(str visibility, list[Entry] entries) {
entriesCompleted = size([ e | e <- entries, e.completed ]);
entriesLeft = size(entries) - entriesCompleted;

footer(class("footer"), hidden(entries == []), () {
viewControlsCount(entriesLeft);
viewControlsFilters(visibility);
viewControlsClear(entriesCompleted);
});
}

function viewControlsCount

void viewControlsCount(int entriesLeft) {
str item = entriesLeft == 1 ? " item" : " items";
span(class("todo-count"), () {
strong("<entriesLeft>");
text("<item> left");
});
}

function viewControlsFilters

void viewControlsFilters(str visibility) {
ul(class("filters"), () {
visibilitySwap("#/", "All", visibility);
visibilitySwap("#/active", "Active", visibility);
visibilitySwap("#/completed", "Completed", visibility);
});
}

function visibilitySwap

void visibilitySwap(str uri, str visibility, str actualVisibility) {
li(onClick(changeVisibility(visibility)), () {
a(href(uri), classList(<"selected", visibility == actualVisibility>), visibility);
});
}

function viewControlsClear

void viewControlsClear(int entriesCompleted) {
button(class("clear-completed"), hidden(entriesCompleted == 0),
onClick(deleteComplete()), "Clear completed (<entriesCompleted>)");
}

function infoFooter

void infoFooter() {
footer(class("info"), () {
p("Double-click to edit a todo");
p(() {
text("Written by ");
a(href("http://www.cwi.nl~/storm"), "Tijs van der Storm");
text(", transcribed from ");
a(href("https://github.com/evancz/elm-todomvc"), "Evan\'s version in Elm");
});
p(() {
text("Based on ");
a(href("http://todomvc.com"), "TodoMVC");
});
});
}