implement go to project with autocomplete

This commit is contained in:
Reinier Balt 2013-07-20 23:21:24 +02:00
parent 01005cc3d3
commit 79fdd5d625
12 changed files with 210 additions and 100 deletions

View file

@ -1,13 +1,6 @@
GIT
remote: https://github.com/rails/actionpack-xml_parser
revision: 246653ab3670f329176c1e77e6cd1a632466f06e
specs:
actionpack-xml_parser (1.0.0)
actionpack (>= 4.0.0.rc1, < 4.1)
GIT
remote: git://github.com/seyhunak/twitter-bootstrap-rails.git
revision: 9a0a31096ecee572638132d5ed94bc5500f74330
revision: 3ebe7b161a7699e56431d546aa98034483a3bb3c
branch: master
specs:
twitter-bootstrap-rails (2.2.7)
@ -16,6 +9,13 @@ GIT
rails (>= 3.1)
railties (>= 3.1)
GIT
remote: https://github.com/rails/actionpack-xml_parser
revision: 246653ab3670f329176c1e77e6cd1a632466f06e
specs:
actionpack-xml_parser (1.0.0)
actionpack (>= 4.0.0.rc1, < 4.1)
GEM
remote: https://rubygems.org/
specs:
@ -74,13 +74,14 @@ GEM
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.6.2)
coffee-script-source (1.6.3)
commonjs (0.2.6)
cucumber (1.3.2)
cucumber (1.3.5)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.12.0)
multi_json (~> 1.3)
multi_json (~> 1.7.5)
multi_test (>= 0.0.2)
cucumber-rails (1.3.0)
capybara (>= 1.1.2)
cucumber (>= 1.1.8)
@ -101,7 +102,7 @@ GEM
hike (1.2.3)
htmlentities (4.3.1)
i18n (0.6.4)
jquery-rails (3.0.1)
jquery-rails (3.0.4)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
less (2.3.2)
@ -115,13 +116,14 @@ GEM
treetop (~> 1.4.8)
metaclass (0.0.1)
mime-types (1.23)
mini_portile (0.5.0)
mini_portile (0.5.1)
minitest (4.7.5)
mocha (0.14.0)
metaclass (~> 0.0.1)
mousetrap-rails (0.0.10)
mousetrap-rails (0.0.11)
multi_json (1.7.7)
mysql2 (0.3.11)
multi_test (0.0.2)
mysql2 (0.3.13)
nokogiri (1.6.0)
mini_portile (~> 0.5.0)
polyglot (0.3.3)
@ -147,12 +149,12 @@ GEM
thor (>= 0.18.1, < 2.0)
rake (10.1.0)
ref (1.0.5)
rspec-expectations (2.13.0)
rspec-expectations (2.14.0)
diff-lcs (>= 1.1.3, < 2.0)
rubyzip (0.9.9)
safe_yaml (0.9.3)
sanitize (2.0.4)
nokogiri (~> 1.6.0)
safe_yaml (0.9.4)
sanitize (2.0.6)
nokogiri (>= 1.4.4)
sass (3.2.9)
sass-rails (4.0.0)
railties (>= 4.0.0.beta, < 5.0)
@ -187,17 +189,17 @@ GEM
thread_safe (0.1.0)
atomic
tilt (1.4.1)
timecop (0.6.1)
tolk (1.3.9)
timecop (0.6.2)
tolk (1.3.11)
safe_yaml (~> 0.8)
will_paginate
treetop (1.4.14)
polyglot
polyglot (>= 0.3.1)
turbolinks (1.2.0)
turbolinks (1.3.0)
coffee-rails
tzinfo (0.3.37)
uglifier (2.1.1)
uglifier (2.1.2)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
uniform_notifier (1.2.0)

View file

@ -47,12 +47,14 @@ $ ->
# ADD: a is bound in navbar
# GO TO
# Mousetrap.bind 'g h', TracksApp.go_home
Mousetrap.bind 'G', -> TracksApp.go_menu()
Mousetrap.bind 'g h', -> TracksApp.go_home()
Mousetrap.bind 'g c', -> alert("go context")
# Mousetrap.bind 'g C', TracksApp.go_contexts
Mousetrap.bind 'g C', -> TracksApp.go_contexts()
Mousetrap.bind 'g t', -> alert("go tag")
Mousetrap.bind 'g p', -> alert("go project")
# Mousetrap.bind 'g P', TracksApp.go_projects
Mousetrap.bind 'g p', -> TracksApp.go_project()
Mousetrap.bind 'g P', -> TracksApp.go_projects()
Mousetrap.bind 'g s', -> TracksApp.go_starred()
# VIEW
Mousetrap.bind 'v p', -> alert("group by project")

View file

@ -1,80 +1,104 @@
# Tracks specific coffeescript
# TracksApp =
# goto_page: (page) -> window.location.href = page
# go_home: this.goto_page "/"
# go_contexts: this.goto_page "/contexts"
# go_projects: this.goto_page "/projects"
TracksApp =
goto_page: (page) -> window.location.href = page
go_home: -> TracksApp.goto_page "/"
go_contexts: -> TracksApp.goto_page "/contexts"
go_projects: -> TracksApp.goto_page "/projects"
go_starred: -> TracksApp.goto_page "/tag/starred"
createSubmenu: (todo, itemToAddBefore) ->
template_clone = $("div.todo-sub-menu-template").clone()
itemToAddBefore.before(template_clone)
todo_menu = todo.find("div.todo-sub-menu-template")
todo_menu.removeClass("todo-sub-menu-template")
todo_menu.addClass("todo-sub-menu")
todo_menu.removeClass("hide")
go_project: ->
$("input#tracks-goto-project").val("")
$('div#tracks-go-project-dialog').on 'shown', -> $("input#tracks-goto-project").focus()
$('div#tracks-go-project-dialog').modal()
appendTodoSubMenu: (todo) ->
if todo.find("div.todo-sub-menu").length is 0
notes_row = todo.find(".todo-notes").parent()
submenu = TracksApp.createSubmenu(todo, notes_row)
else
todo.find("div.todo-sub-menu").removeClass("hide")
go_menu: -> $('div#tracks-goto-dialog').modal()
add_todo: -> $('div#tracks-add-action-dialog').modal()
selectTodo: (new_todo) ->
selected_item = $("div.todo-item.selected-item")
selected_item.find("div.todo-sub-menu").addClass("hide")
selected_item.find("span.todo-item-detail").addClass("hide")
selected_item.removeClass("selected-item")
TracksApp.appendTodoSubMenu(new_todo)
new_todo.find("span.todo-item-detail").removeClass("hide")
new_todo.addClass("selected-item")
createSubmenu: (todo, itemToAddBefore) ->
template_clone = $("div.todo-sub-menu-template").clone()
itemToAddBefore.before(template_clone)
todo_menu = todo.find("div.todo-sub-menu-template")
todo_menu.removeClass("todo-sub-menu-template")
todo_menu.addClass("todo-sub-menu")
todo_menu.removeClass("hide")
selectPrevNext: (go_next) ->
current = prev = next = null
stop = false
$("div.todo-item").each ->
if stop
next = $(this)
return false
appendTodoSubMenu: (todo) ->
if todo.find("div.todo-sub-menu").length is 0
notes_row = todo.find(".todo-notes").parent()
submenu = TracksApp.createSubmenu(todo, notes_row)
else
todo.find("div.todo-sub-menu").removeClass("hide")
prev = current
current = $(this)
selectTodo: (new_todo) ->
selected_item = $("div.todo-item.selected-item")
selected_item.find("div.todo-sub-menu").addClass("hide")
selected_item.find("span.todo-item-detail").addClass("hide")
selected_item.removeClass("selected-item")
TracksApp.appendTodoSubMenu(new_todo)
new_todo.find("span.todo-item-detail").removeClass("hide")
new_todo.addClass("selected-item")
if $(this).hasClass("selected-item")
stop = true
if go_next
TracksApp.selectTodo(prev) if prev?
return prev
else
TracksApp.selectTodo(next) if next?
return next
selectPrev: ->
unless TracksApp.selectPrevNext(true)?
TracksApp.selectTodo($("div.todo-item").last())
selectNext: ->
unless TracksApp.selectPrevNext(false)?
TracksApp.selectTodo($("div.todo-item").first())
selectPrevNext: (go_next) ->
current = prev = next = null
stop = false
$("div.todo-item").each ->
if stop
next = $(this)
return false
prev = current
current = $(this)
if $(this).hasClass("selected-item")
stop = true
if go_next
TracksApp.selectTodo(prev) if prev?
return prev
else
TracksApp.selectTodo(next) if next?
return next
selectPrev: ->
unless TracksApp.selectPrevNext(true)?
TracksApp.selectTodo($("div.todo-item").last())
selectNext: ->
unless TracksApp.selectPrevNext(false)?
TracksApp.selectTodo($("div.todo-item").first())
# Make TracksApp globally accessible. From http://stackoverflow.com/questions/4214731/coffeescript-global-variables
root = exports ? this
root.TracksApp = TracksApp
$ ->
$("a#menu-keyboard-shotcuts").click -> $('div#tracks-shortcuts-dialog').modal()
$("a#menu-keyboard-shotcuts").click -> $('div#tracks-shortcuts-dialog').modal()
$("a.button-add-todo").click -> $('div#tracks-add-action-dialog').modal()
$("a.button-add-todo").click -> TracksApp.add_todo()
$("a.button-home").click -> TracksApp.go_home()
$("a.button-goto").click -> TracksApp.go_menu()
$("i.icon-book").click ->
notes_id = $( this ).attr("data-note-id")
notes_div = $("div#" + notes_id )
notes_div.toggleClass("hide")
todo_item = $(this).parent().parent().parent().parent()
TracksApp.selectTodo(todo_item)
$("i.icon-book").click ->
notes_id = $( this ).attr("data-note-id")
notes_div = $("div#" + notes_id )
notes_div.toggleClass("hide")
todo_item = $(this).parent().parent().parent().parent()
TracksApp.selectTodo(todo_item)
$("span.todo-item-description-container").click ->
TracksApp.selectTodo( $(this).parent().parent().parent() )
$("span.todo-item-description-container").click ->
TracksApp.selectTodo( $(this).parent().parent().parent() )
$('.ajax-typeahead').typeahead
minLength: 3,
source: (query, process) ->
typeaheadURL = $(this)[0].$element[0].dataset.link
return $.ajax
url: typeaheadURL,
type: 'get',
data: {"query": query},
dataType: 'json',
success: (json) ->
$("input#tracks-json-result").val(json)
map = $.map json, (data, item) -> data.value
return process(map)

View file

@ -19,6 +19,9 @@ div.tracks-middle {
div.btn-toolbar {
margin: 0px 15px 0px 0px;
}
input {
width: 100px;
}
}
div#tracks-login-navbar {
@ -149,6 +152,20 @@ div.todos-container {
}
}
/* Dialogs */
.modal-body {
overflow-y: visible;
}
div#tracks-go-project-dialog {
input.input-medium {
width : 95%;
}
}
/* Generic */
div.hide_me {
display: none;
}

View file

@ -52,7 +52,8 @@ class ProjectsController < ApplicationController
end
format.autocomplete do
projects = current_user.projects.active + current_user.projects.hidden
render :text => for_autocomplete(projects, params[:term])
term = params[:term] || params[:query]
render :text => for_autocomplete(projects, term)
end
end
end

View file

@ -1,7 +1,7 @@
<%
@not_done = @not_done_todos.select {|t| t.context_id == context.id }
# invalidate the cache every day because of staleness or
# rendering of "due in x days" that change without touching updated at of the todo
# showing of "due in x days" that change without touching updated at of the todo
cache [context, source_view_key, current_user.date.strftime("%Y%m%d"), @tag_name] do
-%>
<%=

View file

@ -44,10 +44,12 @@
<% cache [:footer] do -%>
<%= render partial: "shared/footer" %>
<% # dialogs -%>
<% # dialogs / partials -%>
<%= render partial: "shared/keyboard_shortcuts" %>
<%= render partial: "shared/add_new_action" %>
<%= render partial: "shared/goto" %>
<%= render partial: "todos/todo_sub_menu" %>
<%= render partial: "projects/go_project" %>
<% end -%>
<%
# Javascripts

View file

@ -0,0 +1,17 @@
<div id="tracks-go-project-dialog" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Go to project</h3>
</div>
<div class="modal-body">
<form class="form-search">
<input type="text" id="tracks-goto-project" class="input-medium search-query ajax-typeahead" placeholder="Type (part of) project name"
data-link="/projects.autocomplete">
<input type="hidden" id="tracks-json-result">
</form>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
<button class="btn btn-primary">Go to project</button>
</div>
</div>`

View file

@ -1,7 +1,7 @@
<%
@not_done = @not_done_todos.select {|t| t.project_id == project.id }
# invalidate the cache every day because of staleness or
# rendering of "due in x days" that change without touching updated at of the todo
# showing of "due in x days" that change without touching updated at of the todo
cache [project, source_view_key, current_user.date.strftime("%Y%m%d"), @tag_name] do
-%>
<%=

View file

@ -0,0 +1,29 @@
<div id="tracks-goto-dialog" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Go to...</h3>
</div>
<div class="modal-body">
<div>
<h4>Go to project...</h4>
<ul>
<% current_user.projects.active.each do |project| -%>
<%= content_tag(:li){ link_to project.name, project_path(project)} %>
<% end -%>
<li><%= link_to "All projects", projects_path %></li>
</ul>
</div>
<div>
<h4>Go to context...</h4>
<ul>
<% current_user.contexts.active.each do |context| -%>
<%= content_tag(:li){ link_to context.name, context_path(context)} %>
<% end -%>
<li><%= link_to "All contexts", contexts_path %></li>
</ul>
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
</div>

View file

@ -3,13 +3,19 @@
<div class="container-fluid">
<a class="btn btn-navbar" data-target=".nav-collapse" data-toggle="collapse">
<span class="icon-bar">A</span>
<span class="icon-bar">B</span>
<span class="icon-bar">C</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="#"><span id="badge_count" class="badge_count"><%= @badge_count || @count %></span></a>
<div class="btn-navbar" data-target=".nav-collapse">
<a class="btn btn-primary btn-small button-home" title="Go to home page"><i class="icon-home icon-large"></i></a>
<a class="btn btn-primary btn-small button-add-todo" title="Add a new action"><i class="icon-plus icon-large"></i></a>
<a class="btn btn-primary btn-small button-goto" title="Go to..."><i class="icon-mail-forward icon-large"></i></a>
</div>
<div class="container-fluid nav-collapse">
<ul class="nav">
<li><%= menu_item("home", root_path) %></li>
@ -57,11 +63,11 @@
</ul>
</li>
</ul>
<ul class="nav pull-right">
<li>
<div class="btn-toolbar">
<a class="btn btn-primary btn-small button-add-todo" title="Add a new action" data-keybinding="a"><i class="icon-plus icon-large"></i></a>
<a class="btn btn-primary btn-small button-add-todo" title="Add a new action" data-keybinding="a"><i class="icon-plus icon-large"></i></a>
<a class="btn btn-primary btn-small button-goto" title="Go to..." data-keybinding="G"><i class="icon-mail-forward icon-large"></i></a>
</div>
</li>
<li>
@ -71,8 +77,8 @@
</li>
<li><%= link_to("<i class='icon-signout'></i>".html_safe, logout_path, title: "#{t('common.logout')} (#{current_user.display_name})") %></li>
</ul>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,2 +1,12 @@
# Have Mini Profiler show up on the right
Rack::MiniProfiler.config.position = 'right'
Rack::MiniProfiler.config.position = 'right'
# Have Mini Profiler start in hidden mode - display with short cut (defaulted to 'Alt+P')
Rack::MiniProfiler.config.start_hidden = true
# Don't collect backtraces on SQL queries that take less than 5 ms to execute
# (necessary on Rubies earlier than 2.0)
# Rack::MiniProfiler.config.backtrace_threshold_ms = 5
# Use memory storage
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore