card 1.93.5 → 1.93.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/db/migrate_core_cards/data/1.12_stylesheets/classic_cards.scss +1 -1
  4. data/db/migrate_core_cards/data/1.12_stylesheets/traditional.scss +2 -1
  5. data/lib/card/format/nest.rb +4 -1
  6. data/lib/card/format/nest/mode.rb +11 -3
  7. data/lib/card/query.rb +6 -2
  8. data/lib/card/query/sorting.rb +21 -12
  9. data/lib/card/query/sql_statement.rb +1 -2
  10. data/lib/card/view/options.rb +17 -12
  11. data/mod/Modfile +1 -0
  12. data/mod/core/layout/simple_modal.html +3 -0
  13. data/mod/core/set/all/fetch_helper.rb +42 -30
  14. data/mod/core/spec/format/html_format_spec.rb +10 -17
  15. data/mod/core/spec/set/all/fetch_helper_spec.rb +48 -0
  16. data/mod/machines/file/all_script_machine_output/file.js +132 -18
  17. data/mod/machines/lib/javascript/decko.js.coffee +11 -2
  18. data/mod/machines/lib/javascript/decko_filter.js.coffee +116 -9
  19. data/mod/machines/lib/stylesheets/style_cards.scss +24 -3
  20. data/mod/pointer/lib/javascript/script_pointer_config.js.coffee +16 -6
  21. data/mod/pointer/set/abstract/00_paging_params.rb +17 -4
  22. data/mod/pointer/set/abstract/02_pointer/filtered.rb +48 -0
  23. data/mod/pointer/set/self/input_options.rb +1 -0
  24. data/mod/pointer/spec/set/self/input_options_spec.rb +1 -1
  25. data/mod/pointer/template/abstract/02_pointer/filtered/filter_items.haml +34 -0
  26. data/mod/pointer/template/abstract/02_pointer/filtered/filtered_list_input.haml +30 -0
  27. data/mod/search/lib/card/filter_query.rb +81 -0
  28. data/mod/search/set/abstract/00_filter_helper.rb +51 -0
  29. data/mod/search/set/abstract/01_filter_form_helper.rb +77 -0
  30. data/mod/search/set/abstract/01_search_params.rb +3 -11
  31. data/mod/search/set/abstract/02_filter_formgroups.rb +134 -0
  32. data/mod/search/set/abstract/03_filter.rb +117 -0
  33. data/mod/search/set/abstract/04_right_filter_form.rb +23 -0
  34. data/mod/search/set/abstract/search.rb +44 -10
  35. data/mod/search/set/abstract/wql_search.rb +22 -18
  36. data/mod/search/spec/set/{all → abstract}/filter_spec.rb +6 -5
  37. data/mod/search/template/{all/filter → abstract/03_filter}/_filter_input.haml +0 -0
  38. data/mod/search/template/{all/filter → abstract/03_filter}/filter_form.haml +9 -7
  39. data/mod/search/template/abstract/search/checkbox_item.haml +7 -0
  40. data/mod/search/template/abstract/search/select_item.haml +14 -0
  41. data/mod/standard/set/all/rich_html/editing.rb +4 -4
  42. data/mod/standard/set/all/rich_html/form_elements.rb +11 -2
  43. data/mod/standard/set/all/rich_html/modal.rb +26 -19
  44. data/mod/standard/set/all/rich_html/new.rb +8 -2
  45. data/mod/standard/set/all/rich_html/wrapper.rb +22 -18
  46. data/mod/standard/set/type/cardtype.rb +2 -2
  47. data/mod/standard/spec/set/all/rich_html/editing_spec.rb +0 -1
  48. data/mod/utility/set/abstract/utility.rb +13 -0
  49. data/mod/utility/spec/set/abstract/utility_spec.rb +16 -0
  50. metadata +21 -7
  51. data/mod/search/set/all/filter.rb +0 -9
@@ -11,10 +11,13 @@ $.extend decko.editorContentFunctionMap,
11
11
  pointerContent @find('input:checked').map( -> $(this).val() )
12
12
  '.pointer-select-list': ->
13
13
  pointerContent @find('.pointer-select select').map( -> $(this).val() )
14
- '.pointer-mixed': ->
15
- element = '.pointer-checkbox-sublist input:checked,\
16
- .pointer-sublist-ul input'
17
- pointerContent @find(element).map( -> $(this).val() )
14
+ '._pointer-filtered-list': ->
15
+ pointerContent @find('._filtered-list-item').map( -> $(this).data('cardName') )
16
+ # can't find evidence that the following is in use: #efm
17
+ # '.pointer-mixed': ->
18
+ # element = '.pointer-checkbox-sublist input:checked,\
19
+ # .pointer-sublist-ul input'
20
+ # pointerContent @find(element).map( -> $(this).val() )
18
21
  # must happen after pointer-list-ul, I think
19
22
  '.perm-editor': -> permissionsContent this
20
23
 
@@ -22,6 +25,9 @@ decko.editorInitFunctionMap['.pointer-list-editor'] = ->
22
25
  @sortable({handle: '.handle', cancel: ''})
23
26
  decko.initPointerList @find('input')
24
27
 
28
+ decko.editorInitFunctionMap['._pointer-filtered-list'] = ->
29
+ @sortable({handle: '._handle', cancel: ''})
30
+
25
31
  $.extend decko,
26
32
  initPointerList: (input) ->
27
33
  decko.initAutoCardPlete input
@@ -32,9 +38,13 @@ $.extend decko,
32
38
  url = decko.rootPath + '/' + optionsCard + '.json?view=name_complete'
33
39
  input.autocomplete { source: decko.prepUrl(url) }
34
40
 
41
+ pointerContent: (vals) ->
42
+ list = $.map $.makeArray(vals), (v) -> if v then '[[' + v + ']]'
43
+ $.makeArray(list).join "\n"
44
+
35
45
  pointerContent = (vals) ->
36
- list = $.map $.makeArray(vals), (v) -> if v then '[[' + v + ']]'
37
- $.makeArray(list).join "\n"
46
+ decko.pointerContent vals
47
+ # deprecated. backwards compatibility
38
48
 
39
49
  permissionsContent = (ed) ->
40
50
  return '_left' if ed.find('#inherit').is(':checked')
@@ -1,11 +1,24 @@
1
1
  format do
2
2
  def limit_param
3
- @limit ||=
4
- Env.params[:limit].present? ? Env.params.delete(:limit).to_i : default_limit
3
+ @limit ||= contextual_param(:limit) || default_limit
5
4
  end
6
5
 
7
6
  def offset_param
8
- @offset ||=
9
- Env.params[:offset].present? ? Env.params.delete(:offset).to_i : 0
7
+ @offset ||= contextual_param(:offset) || 0
8
+ end
9
+
10
+ def contextual_param param
11
+ env_search_param(param) || voo_search_param(param)
12
+ end
13
+
14
+ def env_search_param param
15
+ return unless focal?
16
+ val = Env.params[param]
17
+ val.present? && val.to_i
18
+ end
19
+
20
+ def voo_search_param param
21
+ return unless voo&.wql
22
+ voo.wql[param]
10
23
  end
11
24
  end
@@ -0,0 +1,48 @@
1
+
2
+ format :html do
3
+ view :filtered_list, tags: :unknown_ok do
4
+ with_nest_mode :normal do
5
+ class_up "card-slot", editor_id
6
+ wrap do
7
+ haml :filtered_list_input
8
+ end
9
+ end
10
+ end
11
+
12
+ def filtered_list_input
13
+ _render_filtered_list
14
+ end
15
+
16
+ def filtered_list_item item_card
17
+ nest_item item_card do |rendered, item_view|
18
+ wrap_item rendered, item_view
19
+ end
20
+ end
21
+
22
+ # for override
23
+ # @return [Card] search card on which filtering is based
24
+ def filter_card
25
+ filter_card_from_params || default_filter_card
26
+ end
27
+
28
+ def default_filter_card
29
+ fcard = card.options_rule_card || Card[:all]
30
+ return fcard if fcard.respond_to? :wql_hash
31
+ fcard.fetch trait: :referred_to_by, new: {}
32
+ end
33
+
34
+ def filter_card_from_params
35
+ return unless params[:filter_card]
36
+ Card.fetch params[:filter_card], new: {}
37
+ end
38
+
39
+ view :filter_items, tags: :unknown_ok do
40
+ haml :filter_items
41
+ end
42
+
43
+ # currently actually used as a class
44
+ # (because we don't have api to override slot's id)
45
+ def editor_id
46
+ @editor_id ||= "editor#{unique_id}"
47
+ end
48
+ end
@@ -6,6 +6,7 @@ add_to_basket :options, "checkbox"
6
6
  add_to_basket :options, "select"
7
7
  add_to_basket :options, "multiselect"
8
8
  add_to_basket :options, "list"
9
+ add_to_basket :options, "filtered list"
9
10
 
10
11
  def content
11
12
  options.to_pointer_content
@@ -1,7 +1,7 @@
1
1
  describe Card::Set::Self::InputOptions do
2
2
  it "loads the self set" do
3
3
  expect(Card[:input, :right, :options].item_names).to contain_exactly(
4
- "radio", "checkbox", "select", "multiselect", "list", "ace editor",
4
+ "radio", "checkbox", "select", "multiselect", "list", "ace editor", "filtered list",
5
5
  "prosemirror editor", "tinymce editor", "text area", "text field", "calendar"
6
6
  )
7
7
  end
@@ -0,0 +1,34 @@
1
+ ._filter-items.container-fluid
2
+ .row
3
+ = nest filter_card, view: :filter_form
4
+ ._unselected.col-6.border.mt-2.nopadding
5
+ = nest filter_card, view: :select_item,
6
+ items: { view: implicit_item_view },
7
+ wql: { limit: 10 }
8
+ ._selected.col-6.border.mt-2.nopadding
9
+ .selected-box
10
+ .card-header
11
+ %h5
12
+ Selected
13
+ .badge.badge-secondary
14
+ %span._selected-items
15
+ 0
16
+ ._selected-item-list{ style: "display:none" }
17
+ .p-3.deselector
18
+ %input#deselect-all._deselect-all{ type: "checkbox", checked: true, disabled: true }
19
+ %label{ for: "deselect-all" }
20
+ deselect
21
+ %span._selected-items
22
+ 0
23
+ following
24
+ ._selected-bin
25
+ ._filter-help.alert.alert-secondary
26
+ Filter and select items to add them here.
27
+ .form-group
28
+ .selected-item-buttons
29
+ = button_tag "Cancel", class: "cancel-modal", data: { dismiss: :modal }
30
+ - select_href = path item: params[:item], filter_card: params[:filter_card]
31
+ = button_tag "Add Selected", class: "_add-selected slotter close-modal",
32
+ disabled: true,
33
+ href: select_href,
34
+ data: { "slot-selector": ".#{params[:editor_id]}" }
@@ -0,0 +1,30 @@
1
+ %div.filtered-list-editor
2
+ %ul.filtered-list-review._pointer-filtered-list.list-group.vertical.nopadding
3
+ - card.item_cards(context: :raw).each do |item_card|
4
+ %li._filtered-list-item.clearfix{ data: item_card.format.wrap_data(false) }
5
+ %span._handle.float-left.m-2
6
+ = icon_tag :reorder
7
+ - nest_item item_card do |rendered, view|
8
+ %span{ class: "item-#{view} float-left w-75"}
9
+ = rendered
10
+ %span.filtered-list-item-button
11
+ %button._filtered-list-item-delete.btn.btn-secondary.btn-sm.m-2{:type => "button"}
12
+ = icon_tag :remove
13
+ %br
14
+ .clearfix
15
+ - path_opts = { view: :filter_items,
16
+ item: implicit_item_view,
17
+ filter_card: filter_card.name,
18
+ layout: :simple_modal,
19
+ editor_id: editor_id,
20
+ slot: { hide: :modal_footer },
21
+ filter: { not_ids: card.item_ids.map(&:to_s).join(",") } }
22
+ = render_modal_link title: "Add Item",
23
+ link_opts: { class: "btn btn-sm btn-primary",
24
+ "data-target": "#modal-filtered-list",
25
+ path: path_opts }
26
+ = render_modal_slot modal_id: "filtered-list", dialog_class: "modal-lg"
27
+
28
+ // note: passing item and filter card because in some cases (eg Project+Metric on wikirate)
29
+ // the link was losing set-identifying information (type of left)
30
+ // would be preferable to have a more general solution to retain set-identifying info.
@@ -0,0 +1,81 @@
1
+ class Card
2
+ # Class for generating WQL based on filter params
3
+ class FilterQuery
4
+ def initialize filter_keys_with_values, extra_wql={}
5
+ @filter_wql = Hash.new { |h, k| h[k] = [] }
6
+ @rules = yield if block_given?
7
+ @rules ||= {}
8
+ @filter_keys_with_values = filter_keys_with_values
9
+ @extra_wql = extra_wql
10
+ prepare_filter_wql
11
+ end
12
+
13
+ def add_to_wql key, value
14
+ @filter_wql[key] << value
15
+ end
16
+
17
+ def add_rule key, value
18
+ return unless value.present?
19
+ case @rules[key]
20
+ when Symbol
21
+ send("#{@rules[key]}_rule", key, value)
22
+ when Proc
23
+ @rules[key].call(key, value).each do |wql_key, val|
24
+ @filter_wql[wql_key] << val
25
+ end
26
+ else
27
+ send("#{key}_wql", value)
28
+ end
29
+ end
30
+
31
+ def to_wql
32
+ @wql = {}
33
+ @filter_wql.each do |wql_key, values|
34
+ next if values.empty?
35
+ case wql_key
36
+ when :right_plus, :left_plus, :type
37
+ merge_using_and wql_key, values
38
+ else
39
+ merge_using_array wql_key, values
40
+ end
41
+ end
42
+ @wql.merge @extra_wql
43
+ end
44
+
45
+ private
46
+
47
+ def prepare_filter_wql
48
+ @filter_keys_with_values.each do |key, values|
49
+ add_rule key, values
50
+ end
51
+ end
52
+
53
+ def merge_using_array wql_key, values
54
+ @wql[wql_key] = values.one? ? values.first : values
55
+ end
56
+
57
+ def merge_using_and wql_key, values
58
+ hash = build_nested_hash wql_key, values
59
+ @wql.deep_merge! hash
60
+ end
61
+
62
+ # nest values with the same key using :and
63
+ def build_nested_hash key, values
64
+ return { key => values[0] } if values.one?
65
+ val = values.pop
66
+ { key => val,
67
+ and: build_nested_hash(key, values) }
68
+ end
69
+
70
+ def name_wql name
71
+ return unless name.present?
72
+ @filter_wql[:name] = ["match", name]
73
+ end
74
+
75
+ # FIXME: move to wikirate
76
+ def project_wql project
77
+ return unless project.present?
78
+ @filter_wql[:referred_to_by] << { left: { name: project } }
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,51 @@
1
+
2
+ def sort_hash
3
+ { sort: sort_param }
4
+ end
5
+
6
+ def filter_param field
7
+ filter_hash[field.to_sym]
8
+ end
9
+
10
+ def filter_hash
11
+ @filter_hash ||= begin
12
+ filter = Env.params[:filter]
13
+ filter = filter.to_unsafe_h if filter&.respond_to?(:to_unsafe_h)
14
+ filter.is_a?(Hash) ? filter : {}
15
+ end
16
+ end
17
+
18
+ def sort_param
19
+ Env.params[:sort] if Env.params[:sort].present?
20
+ end
21
+
22
+ def filter_keys_with_values
23
+ (filter_keys + advanced_filter_keys).map do |key|
24
+ values = filter_param(key)
25
+ next unless values.present?
26
+ [key, values]
27
+ end.compact
28
+ end
29
+
30
+ def default_filter_option
31
+ {}
32
+ end
33
+
34
+ def offset
35
+ param_to_i :offset, 0
36
+ end
37
+
38
+ format do
39
+ delegate :filter_hash, :sort_hash, :filter_param, :sort_param,
40
+ :all_filter_keys, to: :card
41
+ end
42
+
43
+ format :html do
44
+ def extra_paging_path_args
45
+ { filter: filter_hash }.merge sort_hash
46
+ end
47
+
48
+ def filter_active?
49
+ filter_hash.present?
50
+ end
51
+ end
@@ -0,0 +1,77 @@
1
+ include_set Abstract::FilterHelper
2
+
3
+ format :html do
4
+ def select_filter field, _label=nil, default=nil, options=nil
5
+ options ||= filter_options field
6
+ options.unshift(["--", ""]) unless default
7
+ select_filter_tag field, default, options
8
+ end
9
+
10
+ def multiselect_filter field, _label=nil, default=nil, options=nil
11
+ options ||= filter_options field
12
+ multiselect_filter_tag field, default, options
13
+ end
14
+
15
+ def text_filter field, opts={}
16
+ name = filter_name field
17
+ add_class opts, "form-control"
18
+ # formgroup filter_label(field), class: "filter-input" do
19
+ text_field_tag name, filter_param(field), opts
20
+ # end
21
+ end
22
+
23
+ def select_filter_type_based type_codename, order="asc"
24
+ # take the card name as default label
25
+ options = type_options type_codename, order
26
+ select_filter type_codename, nil, nil, options
27
+ end
28
+
29
+ def autocomplete_filter type_code, options_card=nil
30
+ options_card ||= Card::Name[type_code, :type, :by_name]
31
+ text_filter type_code, class: "#{type_code}_autocomplete",
32
+ "data-options-card": options_card
33
+ end
34
+
35
+ def multiselect_filter_type_based type_codename
36
+ options = type_options type_codename
37
+ multiselect_filter type_codename, nil, nil, options
38
+ end
39
+
40
+ def multiselect_filter_tag field, default, options, html_options={}
41
+ html_options[:multiple] = true
42
+ select_filter_tag field, default, options, html_options
43
+ end
44
+
45
+ def select_filter_tag field, default, options, html_options={}
46
+ name = filter_name field, html_options[:multiple]
47
+ default = filter_param(field) || default
48
+ options = options_for_select(options, default)
49
+
50
+ css_class =
51
+ html_options[:multiple] ? "pointer-multiselect" : "pointer-select"
52
+ add_class(html_options, css_class + " filter-input #{field} _filter_input_field")
53
+
54
+ select_tag name, options, html_options
55
+ end
56
+
57
+ def filter_name field, multi=false
58
+ "filter[#{field}]#{'[]' if multi}"
59
+ end
60
+
61
+ def filter_options field
62
+ raw = send("#{field}_options")
63
+ raw.is_a?(Array) ? raw : option_hash_to_array(raw)
64
+ end
65
+
66
+ def option_hash_to_array hash
67
+ hash.each_with_object([]) do |(key, value), array|
68
+ array << [key, value.to_s.downcase]
69
+ array
70
+ end
71
+ end
72
+
73
+ def type_options type_codename, order="asc"
74
+ type_card = Card[type_codename]
75
+ Card.search type_id: type_card.id, return: :name, sort: "name", dir: order
76
+ end
77
+ end
@@ -6,25 +6,17 @@ format do
6
6
  end
7
7
 
8
8
  def search_params
9
- @search_params ||= begin
10
- p = default_search_params.clone
11
- offset_and_limit_search_params p if focal?
12
- p
13
- end
9
+ @search_params ||= default_search_params
14
10
  end
15
11
 
12
+ # used for override
16
13
  def default_search_params
17
- { limit: default_limit }
14
+ { limit: limit_param, offset: offset_param }
18
15
  end
19
16
 
20
17
  def default_limit
21
18
  100
22
19
  end
23
-
24
- def offset_and_limit_search_params hash
25
- hash[:offset] = offset_param
26
- hash[:limit] = limit_param
27
- end
28
20
  end
29
21
 
30
22
  format :html do