<div id='nav'></div> <div id='issues'><h2>Issues</h2></div> <div id='details'></div> <!-- PUT ANY TEMPLATES YOU NEED HERE --> <script id="navEJS" type="text/ejs"> <h2>Filter</h2> <ul> <li id='critical'> <%== can.route.link("Critical",{filter: "critical"}) %> </li> <li id='important'> <%== can.route.link("Important",{filter: "important"}) %> </li> <li id='unimportant'> <%== can.route.link("Navel Lint",{filter: "unimportant"}) %> </li> </ul> </script> <script id="issuesEJS" type="text/ejs"> <h2>Select An Issue for More Details</h2> <% list( issues, function(issue){ %> <div class="issue" id="issue_<%= issue.id %>" <%= (el)-> el.data("issue", issue) %>> <h3><%= issue.attr('name') %></h3> <p class='status'>STATUS: <%= issue.attr('status') %></p> </div> <% }) %> </script> <script id="issueEJS" type='text/ejs'> <h2>#<%= issue.id %> Details</h2> <p><strong>STATUS</strong>: <%= issue.attr('status') %></p> <p class='description'><strong>DESCRIPTION</strong>: <%= issue.attr('description') %></p> </script>
// mock up /issues and /issues/{id} (function(){ var types = {}, id=0, all = []; can.each(["critical","important","unimportant"],function(name){ var num = Math.floor(4*Math.random()); types[name] = can.map(new Array(num+1),function(){ id++; return { status : name, name : "issue #"+id, id: id, description: "this is a description for "+id } }) all = all.concat(types[name]) }) can.fixture("/issues", function(settings){ return types[settings.data.status] }) can.fixture("/issues/{id}", function(settings){ return $.grep(all, function(issueData){ return issueData.id == settings.data.id })[0] }) })(); // An Issue model for loading issues var Issue = can.Model({ findAll : "/issues", findOne : "/issues/{id}" },{}); // A Nav control for the left-side navigation var Nav = can.Control({ init : function(){ // draw the nav template this.element.html(can.view("navEJS") ); // highlight the current active element this.highlight(); }, // when the route filter property changes, highlight acitve "{can.route} filter" : "highlight", // remove the old active element, highlight the new one highlight: function(){ this.element.find('li').removeClass('active') $("#"+can.route.attr('filter') ).addClass('active') } }); // An Issue control for showing a list of issues // for a given filter. var Issues = can.Control({ init : function(){ // draw the issues this.render() }, // draw the issues when filter changes "{can.route} filter" : "render", render : function(){ // if we have selected a filter if( can.route.attr('filter') ) { // retrieve the issues Issue.findAll({ status: can.route.attr('filter') }, $.proxy(function(issues){ // render them with issuesEJS this.element.html( can.view("issuesEJS", {issues: issues}) ) }, this)); } else { this.element.html("<h2>Issues</h2>") } }, // when a user clicks an issue ".issue click" : function(el, ev){ // update the route id property // with that issue's id can.route.attr("id", el.data("issue").id ); }, // when the route id property changes "{can.route} id" : function(route, ev, id){ // remove current active this.element.children('.active').removeClass("active"); // highlight new active this.element.children("#issue_"+id).addClass("active") } }) // A Details control to show detailed information // for an issue var Details = can.Control({ // when the id property changes, update the details "{can.route} id" : function(route, ev, id){ var el = this.element; if( id ) { can.view("issueEJS",{ issue: Issue.findOne({id: id}) }).then(function(frag){ el.html(frag); }) } } }); // SETUP CODE // declare routes can.route(":filter-issues"); can.route(":filter-issues/:id"); // initialize controls new Nav("#nav"); new Issues("#issues"); new Details("#details");
@import url(http://fonts.googleapis.com/css?family=Lato:100,400,700); * { font-family: 'Lato', sans-serif; font-size: 12px; text-shadow: 0 -1px 0 #ffffff; } a { text-decoration: none; color: #666; display: block; position: relative; } a:hover { text-decoration: none; } a:before, .issue:before { content: ""; border-color: transparent #636363; border-style: solid; border-width: 0.3em 0 0.3em 0.4em; display: block; height: 0; width: 0; position: absolute; top: 5px; right: 5px; } h2 { color: #fff; background: #165489; text-shadow: 0 -1px 0 #000000; font-size: 15px; font-weight: bold; line-height: 30px; padding: 0 5px; } h3 { font-weight: bold; } #nav { position: fixed; left: 0px; width: 20%; bottom: 0px; border-right: solid 1px #eee; top: 0px; } #nav li { padding: 5px; border-bottom: solid 1px #eee; } #nav li:hover { background: #eee } .active { background-color: #eee; text-decoration: none; } #issues { position: fixed; left: 20%; bottom: 0px; top: 0px; right: 30%; overflow: auto; } .issue { padding: 5px; cursor: pointer; border-bottom: 1px solid #eee; position: relative; } .issue:hover { background: #eee; } .issue:before { top: 45%; right: 10px; } #details { position: fixed; right: 0px; width: 30%; bottom: 0px; border-left: solid 1px #eee; top: 0px; } #details p { padding: 5px; } .status {color: #999;} .description {background: #feffef; }