Commit b15b9498d25805e93c9dc8004143f89766932534
1 parent
79818886
Exists in
master
and in
29 other branches
Link_List_Block_Improvements: Add Drag and Drop and auto-complete to LinkListBlock
(ActionItem2582) - Changed link list html structure from table to ul/li - Added image that indicates draggable area - Added Drag and Drop javascript - Added button that delete the link - Added auto complete to Address field - Corrected syntax error on token_helper Signed-off-by: David Carlos <ddavidcarlos1392@gmail.com> Signed-off-by: Fabio Teixeira <fabio1079@gmail.com> Signed-off-by: Gustavo Jaruga <darksshades@gmail.com> Signed-off-by: Luciano Prestes <lucianopcbr@gmail.com> Signed-off-by: Matheus Faria <matheus.sousa.faria@gmail.com>
Showing
7 changed files
with
187 additions
and
35 deletions
Show diff stats
app/controllers/box_organizer_controller.rb
@@ -80,6 +80,18 @@ class BoxOrganizerController < ApplicationController | @@ -80,6 +80,18 @@ class BoxOrganizerController < ApplicationController | ||
80 | render :action => 'edit', :layout => false | 80 | render :action => 'edit', :layout => false |
81 | end | 81 | end |
82 | 82 | ||
83 | + def search_autocomplete | ||
84 | + if request.xhr? and params[:query] | ||
85 | + search = params[:query] | ||
86 | + articles = @profile.articles.find(:all, :conditions=>"name ILIKE '%#{search}%' or path ILIKE '%#{search}%'", :limit=>20) | ||
87 | + path_list = articles.map { |content| content.path } | ||
88 | + | ||
89 | + render :json => path_list.to_json | ||
90 | + else | ||
91 | + redirect_to "/" | ||
92 | + end | ||
93 | + end | ||
94 | + | ||
83 | def save | 95 | def save |
84 | @block = boxes_holder.blocks.find(params[:id]) | 96 | @block = boxes_holder.blocks.find(params[:id]) |
85 | @block.update_attributes(params[:block]) | 97 | @block.update_attributes(params[:block]) |
app/helpers/token_helper.rb
@@ -27,7 +27,7 @@ module TokenHelper | @@ -27,7 +27,7 @@ module TokenHelper | ||
27 | hintText: #{options[:hint_text].to_json}, | 27 | hintText: #{options[:hint_text].to_json}, |
28 | noResultsText: #{options[:no_results_text].to_json}, | 28 | noResultsText: #{options[:no_results_text].to_json}, |
29 | searchingText: #{options[:searching_text].to_json}, | 29 | searchingText: #{options[:searching_text].to_json}, |
30 | - searchDelay: #{options[:serach_delay].to_json}, | 30 | + searchDelay: #{options[:search_delay].to_json}, |
31 | preventDuplicates: #{options[:prevent_duplicates].to_json}, | 31 | preventDuplicates: #{options[:prevent_duplicates].to_json}, |
32 | backspaceDeleteItem: #{options[:backspace_delete_item].to_json}, | 32 | backspaceDeleteItem: #{options[:backspace_delete_item].to_json}, |
33 | queryParam: #{name.to_json}, | 33 | queryParam: #{name.to_json}, |
app/views/box_organizer/_link_list_block.rhtml
1 | +<%= javascript_include_tag "edit-link-list.js" %> | ||
2 | + | ||
1 | <strong><%= _('Links') %></strong> | 3 | <strong><%= _('Links') %></strong> |
2 | <div id='edit-link-list-block'> | 4 | <div id='edit-link-list-block'> |
3 | -<table id='links' class='noborder'> | ||
4 | - <tr> | ||
5 | - <th><%= _('Icon') %></th> | ||
6 | - <th><%= _('Name') %></th> | ||
7 | - <th><%= _('Address') %></th> | ||
8 | - <th><%= _('Title') %></th> | ||
9 | - <th><%= _('Target') %></th> | ||
10 | - </tr> | ||
11 | - <% for link in @block.links do %> | ||
12 | - <tr> | ||
13 | - <td><%= icon_selector(link['icon']) %></td> | ||
14 | - <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td> | ||
15 | - <td class='cel-address'><%= text_field_tag 'block[links][][address]', link[:address], :class => 'link-address' %></td> | ||
16 | - <td><%= text_field_tag 'block[links][][title]', link[:title], :class => 'link-title' %></td> | ||
17 | - <td><%= select_tag('block[links][][target]', options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target])) %></td> | ||
18 | - </tr> | ||
19 | - <% end %> | ||
20 | -</table> | 5 | + <ul class='link-list-header'> |
6 | + <li class='link-list-icon'><%= _('Icon') %></li> | ||
7 | + <li class='link-list-name'><%= _('Name') %></li> | ||
8 | + <li class='link-list-address'><%= _('Address') %></li> | ||
9 | + <li class='link-list-target'><%= _('Target') %></li> | ||
10 | + </ul> | ||
11 | + <ul id="dropable-link-list"> | ||
12 | + <% for link in @block.links do %> | ||
13 | + <li> | ||
14 | + <ul class="link-list-row"> | ||
15 | + <li> | ||
16 | + <%= icon_selector(link['icon']) %> | ||
17 | + </li> | ||
18 | + <li> | ||
19 | + <%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %> | ||
20 | + </li> | ||
21 | + <li> | ||
22 | + <%= text_field_tag 'block[links][][address]', link[:address], :class => 'link-address' %> | ||
23 | + </li> | ||
24 | + <li> | ||
25 | + <%= select_tag('block[links][][target]', options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target])) %> | ||
26 | + </li> | ||
27 | + <li> | ||
28 | + <%= button_without_text("", _('Delete'), "#" , :class=>"icon-delete delete-link-list-row") %> | ||
29 | + </li> | ||
30 | + </ul> | ||
31 | + </li> | ||
32 | + <% end %> | ||
33 | + </ul> | ||
34 | + <input type="hidden" id="page_url" value="<%=url_for(:action=>'search_autocomplete')%>" /> | ||
21 | </div> | 35 | </div> |
22 | 36 | ||
23 | <%= link_to_function(_('New link'), nil, :class => 'button icon-add with-text') do |page| | 37 | <%= link_to_function(_('New link'), nil, :class => 'button icon-add with-text') do |page| |
24 | - page.insert_html :bottom, 'links', content_tag('tr', | ||
25 | - content_tag('td', icon_selector('ok')) + | ||
26 | - content_tag('td', text_field_tag('block[links][][name]', '', :maxlength => 20)) + | ||
27 | - content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'link-address'), :class => 'cel-address') + | ||
28 | - content_tag('td', text_field_tag('block[links][][title]', '', :class => 'link-title')) + | ||
29 | - content_tag('td', select_tag('block[links][][target]', options_for_select(LinkListBlock::TARGET_OPTIONS, '_self'))) | 38 | + page.insert_html :bottom, 'dropable-link-list', content_tag('li', |
39 | + content_tag('ul', | ||
40 | + content_tag('li', icon_selector('ok')) + | ||
41 | + content_tag('li', text_field_tag('block[links][][name]', '', :maxlength => 20)) + | ||
42 | + content_tag('li', text_field_tag('block[links][][address]', nil, :class => 'link-address')) + | ||
43 | + content_tag('li', select_tag('block[links][][target]', | ||
44 | + options_for_select(LinkListBlock::TARGET_OPTIONS, '_self'))) + | ||
45 | + content_tag('li', button_without_text("", _('Delete'), "#" , :class=>"icon-delete delete-link-list-row")), | ||
46 | + :class=>"link-list-row new_link_row") | ||
30 | ) + | 47 | ) + |
31 | - javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight") | 48 | + javascript_tag("new_link_action()") |
32 | end %> | 49 | end %> |
1.11 KB
@@ -0,0 +1,48 @@ | @@ -0,0 +1,48 @@ | ||
1 | +function send_ajax(source_url) { | ||
2 | + jQuery(".link-address").autocomplete({ | ||
3 | + source : function(request, response){ | ||
4 | + jQuery.ajax({ | ||
5 | + type: "GET", | ||
6 | + url: source_url, | ||
7 | + data: {query: request.term}, | ||
8 | + success: function(result){ | ||
9 | + response(result); | ||
10 | + }, | ||
11 | + error: function(ajax, stat, errorThrown) { | ||
12 | + console.log('Link not found : ' + errorThrown); | ||
13 | + } | ||
14 | + }); | ||
15 | + }, | ||
16 | + | ||
17 | + minLength: 3 | ||
18 | + }); | ||
19 | +} | ||
20 | + | ||
21 | +function new_link_action(){ | ||
22 | + send_ajax(jQuery("#page_url").val()); | ||
23 | + | ||
24 | + jQuery(".delete-link-list-row").click(function(){ | ||
25 | + jQuery(this).parent().parent().remove(); | ||
26 | + }); | ||
27 | + | ||
28 | + jQuery(".link-address").blur(function(){ | ||
29 | + var value = jQuery(this).val(); | ||
30 | + var search = /(^$)|(^\/$)|(http|www).*|.*\{profile\}.*|.*(\.com|\.org|\.net|\.edu|\.gov|\.info|\.eu)/; | ||
31 | + | ||
32 | + if( !search.test(value) ) { | ||
33 | + value = "/{profile}/"+value; | ||
34 | + jQuery(this).val(value); | ||
35 | + } | ||
36 | + }); | ||
37 | + | ||
38 | + jQuery(document).scrollTop(jQuery('#dropable-link-list').scrollTop()); | ||
39 | +} | ||
40 | + | ||
41 | +jQuery(document).ready(function(){ | ||
42 | + new_link_action(); | ||
43 | + | ||
44 | + jQuery("#dropable-link-list").sortable({ | ||
45 | + revert: true, | ||
46 | + axis: "y" | ||
47 | + }); | ||
48 | +}); | ||
0 | \ No newline at end of file | 49 | \ No newline at end of file |
public/stylesheets/application.css
@@ -1859,21 +1859,85 @@ a.button.disabled, input.disabled { | @@ -1859,21 +1859,85 @@ a.button.disabled, input.disabled { | ||
1859 | text-decoration: none; | 1859 | text-decoration: none; |
1860 | } | 1860 | } |
1861 | /* ==> blocks/link-list-block.css <<= */ | 1861 | /* ==> blocks/link-list-block.css <<= */ |
1862 | - | ||
1863 | #edit-link-list-block { | 1862 | #edit-link-list-block { |
1864 | - width: 820px; | 1863 | + width: 620px; |
1864 | + position: relative; | ||
1865 | + left: -24px; | ||
1865 | } | 1866 | } |
1866 | 1867 | ||
1867 | -#edit-link-list-block table { | ||
1868 | - width: auto; | ||
1869 | - margin-bottom: 10px; | 1868 | +.link-list-header { |
1869 | + width: 98%; | ||
1870 | + height: 25px; | ||
1871 | + padding: 10px 1px 10px 10px; | ||
1872 | + margin-bottom: 5px; | ||
1873 | + cursor: pointer; | ||
1870 | } | 1874 | } |
1871 | -#edit-link-list-block table .cel-address { | ||
1872 | - width: 220px; | 1875 | + |
1876 | +#dropable-link-list { | ||
1877 | + padding-left: 23px; | ||
1878 | + margin-top: -12px; | ||
1873 | } | 1879 | } |
1874 | -#edit-link-list-block table .cel-address input { | ||
1875 | - width: 100%; | 1880 | + |
1881 | +#dropable-link-list li { | ||
1882 | + list-style-type: none; | ||
1883 | +} | ||
1884 | + | ||
1885 | +.link-list-row { | ||
1886 | + line-height: 25px; | ||
1887 | + margin-bottom: 5px; | ||
1888 | + padding: 10px 1px 10px 10px; | ||
1889 | + cursor: pointer; | ||
1890 | + width: 90%; | ||
1891 | +} | ||
1892 | +.link-list-row:hover { | ||
1893 | + background: #aaa url(/images/drag-and-drop.png) no-repeat; | ||
1894 | + background-position: 98% 15px; | ||
1895 | +} | ||
1896 | + | ||
1897 | +.link-list-row li { | ||
1898 | + list-style-type: none; | ||
1899 | + display: inline; | ||
1900 | + margin-left: 5px; | ||
1876 | } | 1901 | } |
1902 | + | ||
1903 | +.link-list-row li div { | ||
1904 | + float: left; | ||
1905 | + margin-top: 4px; | ||
1906 | +} | ||
1907 | + | ||
1908 | +.link-list-row li a { | ||
1909 | + line-height: 27px !important; | ||
1910 | + padding-right: 5px; | ||
1911 | +} | ||
1912 | + | ||
1913 | +.link-list-header li { | ||
1914 | + list-style-type: none; | ||
1915 | + display: inline; | ||
1916 | + font-weight: bold; | ||
1917 | + font-size: 14px; | ||
1918 | + text-align: center; | ||
1919 | +} | ||
1920 | + | ||
1921 | +.link-list-icon { | ||
1922 | + margin-left: 14px; | ||
1923 | +} | ||
1924 | + | ||
1925 | +.link-list-name { | ||
1926 | + margin-left: 40px; | ||
1927 | +} | ||
1928 | + | ||
1929 | +.link-list-address { | ||
1930 | + margin-left: 90px; | ||
1931 | +} | ||
1932 | + | ||
1933 | +.link-list-target { | ||
1934 | + margin-left: 77px; | ||
1935 | +} | ||
1936 | + | ||
1937 | +.new_link_row li { | ||
1938 | + margin-left: 7px; | ||
1939 | +} | ||
1940 | + | ||
1877 | #content .link-list-block { | 1941 | #content .link-list-block { |
1878 | padding: 10px 0px 10px 10px; | 1942 | padding: 10px 0px 10px 10px; |
1879 | } | 1943 | } |
test/functional/profile_design_controller_test.rb
@@ -320,6 +320,17 @@ class ProfileDesignControllerTest < ActionController::TestCase | @@ -320,6 +320,17 @@ class ProfileDesignControllerTest < ActionController::TestCase | ||
320 | assert_tag :input, :attributes => { :type => 'radio', :value => 'except_home_page'} | 320 | assert_tag :input, :attributes => { :type => 'radio', :value => 'except_home_page'} |
321 | end | 321 | end |
322 | 322 | ||
323 | + should 'return a list of paths related to the words used in the query search' do | ||
324 | + article1 = fast_create(Article, :profile_id => @profile.id, :name => "Some thing") | ||
325 | + article2 = fast_create(Article, :profile_id => @profile.id, :name => "Some article") | ||
326 | + article3 = fast_create(Article, :profile_id => @profile.id, :name => "Not an article") | ||
327 | + | ||
328 | + xhr :get, :search_autocomplete, :profile => 'designtestuser' , :query => 'Some' | ||
329 | + assert_response :success | ||
330 | + assert_equal @response.body.include?(article1.path), true | ||
331 | + assert_equal @response.body.include?(article2.path), true | ||
332 | + assert_equal @response.body.include?(article3.path), false | ||
333 | + end | ||
323 | 334 | ||
324 | ###################################################### | 335 | ###################################################### |
325 | # END - tests for BoxOrganizerController features | 336 | # END - tests for BoxOrganizerController features |