Project

General

Profile

Feature #34420 » 0001-Improve-query-columns-selector-by-replacing-the-comb.patch

Marius BĂLTEANU, 2021-03-06 21:10

View differences:

Gemfile
4 4
gem 'bundler', '>= 1.12.0'
5 5

  
6 6
gem 'rails', '5.2.4.4'
7
gem "view_component", require: "view_component/engine"
7 8
gem 'sprockets', '~> 3.7.2' if RUBY_VERSION < '2.5'
8 9
gem 'rouge', '~> 3.26.0'
9 10
gem 'request_store', '~> 1.5.0'
app/components/select_component.html.erb
1
<div class="drdn <%= @css_classes %>">
2
  <span class="drdn-trigger">
3
    <span class="selected"><%= @selected_items_count  %></span> out of <span class="total"><%= @selected_items_count + @available_items_count %></span> selected
4
  </span>
5
  <div class="drdn-content">
6
    <div class="quick-search">
7
      <%= search_field_tag('q', '', :id => nil, :class => 'autocomplete',  :autocomplete => 'off') %>
8
    </div>
9
    <ul class="drdn-items">
10
      <% @items.each do |c| %>
11
        <li>
12
          <div class="item-content">
13
            <%= check_box_tag "#{@name}[]", c[1], c.last, id: "#{@name}_#{c[1]}" %>
14
            <%= label_tag "#{@name}_#{c[1]}", c.first, :class => 'label-item'%>
15
          </div>
16
          <% if @sortable %>
17
            <div class="item-handler">
18
              <span class="icon-only icon-sort-handle sort-handle"></span>
19
            </div>
20
          <% end %>
21
        </li>
22
      <% end %>
23
    </ul>
24
  </div>
25
</div>
26

  
27
<%= javascript_tag do %>
28
  enableAutocomplete(document.querySelector(".query-columns"));
29
<% end %>
app/components/select_component.rb
1
# frozen_string_literal: true
2

  
3
class SelectComponent < ViewComponent::Base
4
  def initialize(
5
    items:,
6
    name:,
7
    css_class:
8
  )
9

  
10
    @selected_items_count = items.count{|v| v[2] == true }
11
    @available_items_count = items.count - @selected_items_count
12
    @sortable = true
13
    @name = name
14
    @items = items
15
    @css_classes = css_classes(css_class)
16
  end
17

  
18
  private
19

  
20
  def css_classes(css_class)
21
    s = +''
22
    s << css_class
23
    s << " dropdown-sortable" if @sortable
24
    s
25
  end
26
end
app/helpers/queries_helper.rb
131 131
      reject(&:frozen?).collect {|column| [column.caption, column.name]}
132 132
  end
133 133

  
134
  def query_options_for_multiselect(query)
135
    selected_columns = query_selected_inline_columns_options(query).map{|s| s.append(true)}
136
    available_columns = query_available_inline_columns_options(query).map{|s| s.append(false)}
137
    columns = selected_columns + available_columns
138
    columns
139
  end
140

  
134 141
  def render_query_columns_selection(query, options={})
135 142
    tag_name = (options[:name] || 'c') + '[]'
136 143
    render :partial => 'queries/columns', :locals => {:query => query, :tag_name => tag_name}
app/views/queries/_columns.html.erb
1
<% tag_id = tag_name.gsub(/[\[\]]+/, '_').sub(/_+$/, '') %>
2
<% available_tag_id = "available_#{tag_id}" %>
3
<% selected_tag_id = "selected_#{tag_id}" %>
4

  
5
<span class="query-columns">
6
  <span>
7
      <%= label_tag available_tag_id, l(:description_available_columns) %>
8
      <%= select_tag 'available_columns',
9
              options_for_select(query_available_inline_columns_options(query)),
10
              :id => available_tag_id,
11
              :multiple => true, :size => 10,
12
              :ondblclick => "moveOptions(this.form.#{available_tag_id}, this.form.#{selected_tag_id});" %>
13
  </span>
14
  <span class="buttons">
15
      <input type="button" value="&#8594;" class="move-right"
16
       onclick="moveOptions(this.form.<%= available_tag_id %>, this.form.<%= selected_tag_id %>);" />
17
      <input type="button" value="&#8592;" class="move-left"
18
       onclick="moveOptions(this.form.<%= selected_tag_id %>, this.form.<%= available_tag_id %>);" />
19
  </span>
20
  <span>
21
      <%= label_tag selected_tag_id, l(:description_selected_columns) %>
22
      <%= select_tag tag_name,
23
              options_for_select(query_selected_inline_columns_options(query)),
24
              :id => selected_tag_id,
25
              :multiple => true, :size => 10,
26
              :ondblclick => "moveOptions(this.form.#{selected_tag_id}, this.form.#{available_tag_id});" %>
27
  </span>
28
  <span class="buttons">
29
      <input type="button" value="&#8648;" onclick="moveOptionTop(this.form.<%= selected_tag_id %>);" />
30
      <input type="button" value="&#8593;" onclick="moveOptionUp(this.form.<%= selected_tag_id %>);" />
31
      <input type="button" value="&#8595;" onclick="moveOptionDown(this.form.<%= selected_tag_id %>);" />
32
      <input type="button" value="&#8650;" onclick="moveOptionBottom(this.form.<%= selected_tag_id %>);" />
33
  </span>
34
</span>
35

  
36
<%= javascript_tag do %>
37
$(document).ready(function(){
38
  $('.query-columns').closest('form').submit(function(){
39
    $('#<%= selected_tag_id %> option:not(:disabled)').prop('selected', true);
40
  });
41
});
42
<% end %>
1
<%= render SelectComponent.new(
2
  items: query_options_for_multiselect(query),
3
  name: "c",
4
  css_class: "query-columns",
5
) %>
public/javascripts/application.js
1157 1157
    tribute.attach(element);
1158 1158
}
1159 1159

  
1160
function enableAutocomplete(element) {
1161
  const items = $(element).find('.drdn-items')
1162

  
1163
  if (element.classList.contains('dropdown-sortable')) {
1164
    items.sortable();
1165
    // ToDo: Replace disableSelection with CSS user-select
1166
    items.disableSelection();
1167
  }
1168

  
1169
  element.querySelector(".drdn-items").addEventListener('click', function(event) {
1170
    if (event.target.tagName.toLowerCase() === 'input') {
1171
      const checkedEl = element.querySelectorAll('.item-content input:checked').length
1172
      element.querySelector('.drdn-trigger span.selected').innerText = checkedEl
1173
    }
1174
  })
1175

  
1176
  element.querySelector("input.autocomplete").addEventListener('input', function(event) {
1177
    filterValues(element, event)
1178
  })
1179

  
1180
  function filterValues(context, event) {
1181
    var input, filter, ul, li, a, i, txtValue;
1182
    input = event.currentTarget;
1183
    filter = input.value.toLowerCase();
1184

  
1185
    ul = element.querySelector(".drdn-items");
1186
    li = ul.getElementsByTagName("li");
1187

  
1188
    for (i = 0; i < li.length; i++) {
1189
      a = li[i].querySelector("div");
1190
      txtValue = a.textContent || a.innerText;
1191

  
1192
      if (txtValue.toLowerCase().indexOf(filter) > -1) {
1193
        li[i].style.display = '';
1194
      } else {
1195
        li[i].style.display = 'none';
1196
      }
1197
    }
1198
  }
1199
}
1160 1200

  
1161 1201
$(document).ready(setupAjaxIndicator);
1162 1202
$(document).ready(hideOnLoad);
public/stylesheets/application.css
160 160
a#toggle-completed-versions {color:#999;}
161 161

  
162 162
/***** Dropdown *****/
163
.drdn {position:relative;}
163
.drdn {
164
  position: relative;
165
}
166
.drdn ul {
167
  margin: 0;
168
  padding: 0;
169
}
170
.drdn li {
171
  list-style: none;
172
  display: flex;
173
}
174
.drdn li:first-child {
175
  margin-top: 4px;
176
}
177
.drdn li .item-content {
178
  display: flex;
179
  flex-grow: 1;
180
  line-height: 26px;
181
}
182
.drdn .label-item {
183
  width: 100%;
184
}
185
.drdn li .item-handler {
186
  display: none;
187
}
188
.drdn li:hover .item-handler {
189
  display: block;
190
}
191
.drdn li .item-handler span {
192
  height: 26px;
193
}
164 194
.drdn-trigger {
165 195
  box-sizing:border-box;
166 196
  overflow:hidden;
......
196 226
  overflow:hidden;
197 227
  text-overflow: ellipsis;
198 228
  white-space:nowrap;
199
  padding:4px 8px;
229
  padding-left: 8px;
230
  padding-right: 8px;
200 231
}
201 232
.drdn-items>a:hover {text-decoration:none;}
202 233
.drdn-items>*:focus {border:1px dotted #bbb;}
......
222 253
.contextual .drdn-items {padding:2px; min-width: 160px;}
223 254
.contextual .drdn-items>a {padding: 5px 8px;}
224 255
.contextual .drdn-items>a.icon {padding-left: 24px; background-position-x: 4px;}
225
.contextual .drdn-items>a:hover {color:#2A5685; border:1px solid #628db6; background-color:#eef5fd; border-radius:3px;}
256
.contextual .drdn-items>a:hover, .query-columns .drdn-items li:hover {color:#2A5685; border:1px solid #628db6; background-color:#eef5fd; border-radius:3px;}
226 257

  
227 258
#project-jump.drdn {width:200px;display:inline-block;}
228
#project-jump .drdn-trigger {
259
#project-jump .drdn-trigger, .query-columns .drdn-trigger {
229 260
  width:100%;
230 261
  height:24px;
231 262
  display:inline-block;
......
239 270
}
240 271
#project-jump .drdn.expanded .drdn-trigger {background-image:url(../images/arrow_up.png);}
241 272
#project-jump .drdn-content {width:280px;}
242
#project-jump .drdn-items>* {color:#555 !important;}
273
#project-jump .drdn-items>* {color:#555 !important; padding-top: 4px; padding-bottom: 8px;}
243 274
#project-jump .drdn-items>a:hover {background-color:#759FCF; color:#fff !important;}
244 275

  
245 276
/***** Tables *****/
......
403 434
  height:100%;
404 435
  vertical-align: middle;
405 436
}
406
.query-columns label {
407
  display:block;
408
}
409 437
.query-columns .buttons input[type=button] {
410 438
  width:35px;
411 439
  display:block;
(4-4/4)