card-mod-list 0.14.2 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/assets/script/pointer_config.js.coffee +78 -0
  3. data/assets/script/pointer_list_editor.js.coffee +68 -0
  4. data/assets/style/list.scss +102 -0
  5. data/config/locales/de.yml +2 -0
  6. data/config/locales/en.yml +5 -0
  7. data/data/files/mod_list_script_asset_output/file.js +4 -0
  8. data/data/real.yml +41 -0
  9. data/data/test.yml +12 -0
  10. data/db/migrate_core_cards/20160804112340_add_pointer_cards.rb +1 -1
  11. data/set/abstract/{01_pointer → 01_list}/events.rb +36 -0
  12. data/set/abstract/01_list/html_views/click_select_input.haml +9 -0
  13. data/set/abstract/{01_pointer → 01_list}/html_views/list_input.haml +4 -6
  14. data/set/abstract/{01_pointer → 01_list}/html_views/list_item.haml +4 -6
  15. data/set/abstract/{01_pointer → 01_list}/html_views/radio_input.haml +3 -3
  16. data/set/abstract/{01_pointer → 01_list}/html_views.rb +27 -29
  17. data/set/abstract/{01_pointer → 01_list}/options_api.rb +3 -1
  18. data/set/abstract/{01_pointer → 01_list}/other_views.rb +0 -6
  19. data/set/abstract/{01_pointer.rb → 01_list.rb} +9 -0
  20. data/set/abstract/{code_pointer.rb → codename_list.rb} +1 -1
  21. data/set/abstract/delist.rb +8 -0
  22. data/set/abstract/{id_pointer.rb → id_list.rb} +14 -1
  23. data/set/abstract/reference_list.rb +11 -0
  24. data/set/all/supports_content_options.rb +1 -9
  25. data/set/right/content_option_view.rb +2 -0
  26. data/set/right/content_options.rb +2 -0
  27. data/set/self/input_options.rb +1 -1
  28. data/set/type/_reference_list_input_row.haml +21 -0
  29. data/set/type/link_list.rb +7 -4
  30. data/set/type/link_list_input.haml +21 -0
  31. data/set/type/list.rb +1 -10
  32. data/set/type/nest_list.rb +1 -6
  33. data/set/type/nest_list_input.haml +21 -0
  34. data/set/type/pointer.rb +1 -10
  35. metadata +35 -26
  36. data/set/abstract/01_pointer/html_views/autocomplete_input.haml +0 -3
  37. data/set/abstract/01_pointer/html_views/filter/filter_items.haml +0 -28
  38. data/set/abstract/01_pointer/html_views/filter/filtered_list_input.haml +0 -16
  39. data/set/abstract/01_pointer/html_views/filter.rb +0 -63
  40. data/set/type/link_list/link_list_input.haml +0 -24
  41. data/set/type/nest_list/nest_list_input.haml +0 -28
  42. /data/set/abstract/{01_pointer → 01_list}/html_views/checkbox_input.haml +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 936f29a9dc0812699b8f46d0415fa34e025dabebfdfde79499cdc2e77d4da935
4
- data.tar.gz: b0f2e5cdd51a0d5ff536e70bc1aa48d5f048fddbd00d6620adcfb972055d1e96
3
+ metadata.gz: ac78aa043b64d282fef3947e6a579fbe664bf2ce1096d32ed1ec36680f724ce3
4
+ data.tar.gz: 5ba568975124684904fb06fe6bd41b01f28e2f30c9828bc6402c94349ed92283
5
5
  SHA512:
6
- metadata.gz: f0887a41a4bf60c73651fc7f554d7cafbdb2eb4fb1bd98c98a6728d605354370ec6d4f886044a0226199e3ba43eb83440f3675cfa83c46bb9eccaaf40ed4390c
7
- data.tar.gz: bb6cdffafe0c79c23fdba644f714ecb3a1e27eda99a04a4bba0d32ca3273723fee30edd354c487bbbdf2848fc7c94d9348b767f246b43c38133fec2e42a94f35
6
+ metadata.gz: d37b057c5f7939f9f245c9a2b23357ceb28548224883aed065a0186ca0e0a340b8ae372b8fd382d8f2292cffad5cd52914bff093c286939ece0b4ae2b5e36794
7
+ data.tar.gz: 8ce9586932e255a329493c71455a00ed04aa80d64e0251d71e839c4d36e98a87101b0c1d4880402ab12ecfb9768269eab555d8e662f3cccd2fe9bd35054b6274
@@ -0,0 +1,78 @@
1
+ $.extend decko.editors.content,
2
+ 'select.pointer-select': ->
3
+ pointerContent @val()
4
+ 'select.pointer-multiselect': ->
5
+ pointerContent @val()
6
+ '.pointer-radio-list': ->
7
+ pointerContent @find('input:checked').val()
8
+ '.pointer-list-ul': ->
9
+ pointerContent @find('input').map( -> $(this).val() )
10
+ '.pointer-link-list-ul': ->
11
+ decko.linkListContent @find('.input-group')
12
+ '._nest-list-ul': ->
13
+ decko.nestListContent @find('.input-group')
14
+ '.pointer-checkbox-list': ->
15
+ pointerContent @find('input:checked').map( -> $(this).val() )
16
+ '.pointer-select-list': ->
17
+ pointerContent @find('.pointer-select select').map( -> $(this).val() )
18
+ '._filtered-list': ->
19
+ pointerContent @find('._filtered-list-item').map( -> $(this).data('cardName') )
20
+ '._pointer-list': ->
21
+ pointerContent @find('._pointer-item').map( -> $(this).val() )
22
+ '._click-select-editor': ->
23
+ pointerContent @find('._select-item.selected').map( ->
24
+ $($(this).find('[data-card-name]')[0]).data('cardName'))
25
+ '._click-multiselect-editor': ->
26
+ pointerContent @find('._select-item.selected').map( ->
27
+ $($(this).find('[data-card-name]')[0]).data('cardName'))
28
+ # can't find evidence that the following is in use: #efm
29
+ # '.pointer-mixed': ->
30
+ # element = '.pointer-checkbox-sublist input:checked,\
31
+ # .pointer-sublist-ul input'
32
+ # pointerContent @find(element).map( -> $(this).val() )
33
+ # must happen after pointer-list-ul, I think
34
+ '.perm-editor': -> permissionsContent this
35
+
36
+ $.extend decko.editors.init,
37
+ ".pointer-list-editor": ->
38
+ @sortable handle: '.handle', cancel: ''
39
+ decko.initPointerList @find('input')
40
+
41
+ "._filtered-list" : ->
42
+ @sortable handle: '._handle', cancel: ''
43
+
44
+ $.extend decko,
45
+ initPointerList: (input) ->
46
+ decko.initAutoCardPlete input
47
+
48
+ pointerContent: (vals) ->
49
+ $.makeArray(vals).join "\n"
50
+
51
+ linkListContent: (input_groups) ->
52
+ vals = input_groups.map( ->
53
+ v = $(this).find('input._reference').val()
54
+ title = $(this).find('input._title').val()
55
+ v += "|#{title}" if title.length > 0
56
+ v
57
+ )
58
+ list = $.map $.makeArray(vals), (v) -> if v then '[[' + v + ']]'
59
+ $.makeArray(list).join "\n"
60
+
61
+ nestListContent: (input_groups) ->
62
+ vals = input_groups.map( ->
63
+ v = $(this).find('input._reference').val()
64
+ options = $(this).find('input._nest-options').val()
65
+ v += "|#{options}" if options.length > 0
66
+ v
67
+ )
68
+ list = $.map $.makeArray(vals), (v) -> if v then '{{' + v + '}}'
69
+ $.makeArray(list).join "\n"
70
+
71
+ pointerContent = (vals) ->
72
+ decko.pointerContent vals
73
+
74
+ permissionsContent = (ed) ->
75
+ return '_left' if ed.find('#inherit').is(':checked')
76
+ groups = ed.find('.perm-group input:checked').map( -> $(this).val() )
77
+ indivs = ed.find('.perm-indiv input' ).map( -> $(this).val() )
78
+ pointerContent $.makeArray(groups).concat($.makeArray(indivs))
@@ -0,0 +1,68 @@
1
+ $(window).ready ->
2
+ # add pointer item when clicking on "add another" button
3
+ $('body').on 'click', '._pointer-item-add', (event)->
4
+ decko.addPointerItem this
5
+ event.preventDefault() # Prevent link from following its href
6
+
7
+ # add pointer item when you hit enter in an item
8
+ $('body').on 'keydown', '.pointer-item-text', (event)->
9
+ if event.key == 'Enter'
10
+ decko.addPointerItem this
11
+ event.preventDefault() # was triggering extra item in unrelated pointer
12
+
13
+ # enable/disable add
14
+ $('body').on 'keyup', '.pointer-item-text', (_event)->
15
+ decko.updateAddItemButton this
16
+
17
+ $('body').on 'click', '.pointer-item-delete', ->
18
+ item = $(this).closest 'li'
19
+ list = item.closest('ul')
20
+ if list.find('.pointer-li').length > 1
21
+ item.remove()
22
+ else
23
+ item.find('input').val ''
24
+ decko.updateAddItemButton(list)
25
+
26
+ decko.slot.ready (slot) ->
27
+ slot.find('.pointer-list-editor').each ->
28
+ decko.updateAddItemButton this
29
+
30
+ $.extend decko,
31
+ addPointerItem: (el) ->
32
+ slot = $(el).slot()
33
+ slot.trigger "decko.slot.destroy"
34
+ # why is this necessary?
35
+ # this can have a lot of side effects in a multi-card form.
36
+
37
+ newInput = decko.nextPointerInput decko.lastPointerItem(el)
38
+ newInput.val ''
39
+
40
+ slot.trigger "decko.slot.ready"
41
+ decko.initializeEditors slot
42
+ # should be (but is not) handled by decko.slot.ready
43
+ # without this, "add another" was breaking tinymce editors in same slot
44
+
45
+ newInput.first().focus()
46
+ decko.updateAddItemButton el
47
+ decko.initPointerList newInput
48
+
49
+ nextPointerInput: (lastItem)->
50
+ lastInputs = lastItem.find 'input'
51
+ all_empty = true
52
+ for input in lastInputs
53
+ all_empty = all_empty and $(input).val() == ''
54
+ return lastInputs if all_empty
55
+
56
+ newItem = lastItem.clone()
57
+ lastItem.after newItem
58
+ newItem.attr("data-index", parseInt(lastItem.attr("data-index") + 1))
59
+ newItem.trigger "decko.item.added"
60
+ newItem.find 'input'
61
+
62
+ lastPointerItem: (el)->
63
+ $(el).closest('.content-editor').find '.pointer-li:last'
64
+
65
+ updateAddItemButton: (el)->
66
+ button = $(el).closest('.content-editor').find '._pointer-item-add'
67
+ disabled = decko.lastPointerItem(el).find('input').val() == ''
68
+ button.prop 'disabled', disabled
@@ -0,0 +1,102 @@
1
+ ul.pointer {
2
+ margin: 0;
3
+ padding: 0;
4
+ }
5
+
6
+ .pointer-list-editor {
7
+ margin: 0px;
8
+ padding: 0px;
9
+ li {
10
+ list-style: none;
11
+ white-space: nowrap;
12
+ }
13
+
14
+ .input-group-btn {
15
+ .btn {
16
+ border-bottom-width: 1px;
17
+ }
18
+ }
19
+
20
+ ._pointer-item-add {
21
+ margin-top: 10px;
22
+ }
23
+
24
+ .input-group-addon.handle {
25
+ padding-left: 1px;
26
+ padding-right: 1px;
27
+
28
+ .glyphicon-option-vertical.left {
29
+ margin-right: -7px;
30
+ }
31
+ .glyphicon-option-vertical.right {
32
+ margin-left: -7px;
33
+ }
34
+ }
35
+ }
36
+
37
+ /*---------- ( Link list )-------------*/
38
+
39
+ .group-text-middle {
40
+ border-left: none;
41
+ border-right: none;
42
+ }
43
+
44
+ .group-text-left {
45
+ border-right: none;
46
+ }
47
+
48
+ .group-text-right {
49
+ border-left: none;
50
+ }
51
+
52
+
53
+ /*---------- ( Nest list editor )-------------*/
54
+
55
+ .input-nest-syntax {
56
+ background-color: $input-bg;
57
+ font-weight: 200;
58
+ font-size: 1.3rem;
59
+ padding-top: 0.1rem;
60
+ padding-bottom: 0.1rem;
61
+ }
62
+
63
+ .nest-form-header .input-group {
64
+ height: 2rem;
65
+
66
+ .input-group-text {
67
+ visibility: hidden;
68
+ }
69
+ }
70
+
71
+ .one-line-pointer-list {
72
+ div.pointer-item {
73
+ display: inline-block
74
+ }
75
+ }
76
+
77
+ /*-------------------- */
78
+ /*- SEARCH & POINTER - */
79
+ /*-------------------- */
80
+
81
+ // TODO: use shared class for pointers and searches
82
+
83
+ div.pointer-list,
84
+ div.search-result-list {
85
+ list-style: none;
86
+ padding: 5px 0;
87
+ margin: 0;
88
+ .closed-view > .d0-card-frame {
89
+ margin-bottom: 0;
90
+ }
91
+ }
92
+ div.pointer-list .pointer-item,
93
+ div.search-result-list .search-result-item {
94
+ margin: 0;
95
+ }
96
+ .search-result-item.item-link,
97
+ .pointer-item.item-link,
98
+ .search-result-item.item-name,
99
+ .pointer-item.item-name {
100
+ text-indent: -15px;
101
+ padding-left: 15px;
102
+ }
@@ -0,0 +1,2 @@
1
+ de:
2
+ list_invalid_item_type: "%{item} hat einen ungültigen Typ (%{type)). Nur %{allowed_types} sind erlaubt."
@@ -0,0 +1,5 @@
1
+ en:
2
+ list_invalid_item_type:
3
+ "%{item} has an invalid type: %{type}. Allowed types: %{allowed_types}."
4
+ list_duplicate_items_names:
5
+ "duplicate item names: %{duplicates}"
@@ -0,0 +1,4 @@
1
+ // pointer_config.js.coffee
2
+ (function(){var t,n;$.extend(decko.editors.content,{"select.pointer-select":function(){return n(this.val())},"select.pointer-multiselect":function(){return n(this.val())},".pointer-radio-list":function(){return n(this.find("input:checked").val())},".pointer-list-ul":function(){return n(this.find("input").map(function(){return $(this).val()}))},".pointer-link-list-ul":function(){return decko.linkListContent(this.find(".input-group"))},"._nest-list-ul":function(){return decko.nestListContent(this.find(".input-group"))},".pointer-checkbox-list":function(){return n(this.find("input:checked").map(function(){return $(this).val()}))},".pointer-select-list":function(){return n(this.find(".pointer-select select").map(function(){return $(this).val()}))},"._filtered-list":function(){return n(this.find("._filtered-list-item").map(function(){return $(this).data("cardName")}))},"._pointer-list":function(){return n(this.find("._pointer-item").map(function(){return $(this).val()}))},"._click-select-editor":function(){return n(this.find("._select-item.selected").map(function(){return $($(this).find("[data-card-name]")[0]).data("cardName")}))},"._click-multiselect-editor":function(){return n(this.find("._select-item.selected").map(function(){return $($(this).find("[data-card-name]")[0]).data("cardName")}))},".perm-editor":function(){return t(this)}}),$.extend(decko.editors.init,{".pointer-list-editor":function(){return this.sortable({handle:".handle",cancel:""}),decko.initPointerList(this.find("input"))},"._filtered-list":function(){return this.sortable({handle:"._handle",cancel:""})}}),$.extend(decko,{initPointerList:function(t){return decko.initAutoCardPlete(t)},pointerContent:function(t){return $.makeArray(t).join("\n")},linkListContent:function(t){var n,i;return i=t.map(function(){var t,n;return n=$(this).find("input._reference").val(),(t=$(this).find("input._title").val()).length>0&&(n+="|"+t),n}),n=$.map($.makeArray(i),function(t){if(t)return"[["+t+"]]"}),$.makeArray(n).join("\n")},nestListContent:function(t){var n,i;return i=t.map(function(){var t,n;return n=$(this).find("input._reference").val(),(t=$(this).find("input._nest-options").val()).length>0&&(n+="|"+t),n}),n=$.map($.makeArray(i),function(t){if(t)return"{{"+t+"}}"}),$.makeArray(n).join("\n")}}),n=function(t){return decko.pointerContent(t)},t=function(t){var i,e;return t.find("#inherit").is(":checked")?"_left":(i=t.find(".perm-group input:checked").map(function(){return $(this).val()}),e=t.find(".perm-indiv input").map(function(){return $(this).val()}),n($.makeArray(i).concat($.makeArray(e))))}}).call(this);
3
+ // pointer_list_editor.js.coffee
4
+ (function(){$(window).ready(function(){return $("body").on("click","._pointer-item-add",function(t){return decko.addPointerItem(this),t.preventDefault()}),$("body").on("keydown",".pointer-item-text",function(t){if("Enter"===t.key)return decko.addPointerItem(this),t.preventDefault()}),$("body").on("keyup",".pointer-item-text",function(){return decko.updateAddItemButton(this)}),$("body").on("click",".pointer-item-delete",function(){var t,e;return(e=(t=$(this).closest("li")).closest("ul")).find(".pointer-li").length>1?t.remove():t.find("input").val(""),decko.updateAddItemButton(e)})}),decko.slot.ready(function(t){return t.find(".pointer-list-editor").each(function(){return decko.updateAddItemButton(this)})}),$.extend(decko,{addPointerItem:function(t){var e,n;return(n=$(t).slot()).trigger("decko.slot.destroy"),(e=decko.nextPointerInput(decko.lastPointerItem(t))).val(""),n.trigger("decko.slot.ready"),decko.initializeEditors(n),e.first().focus(),decko.updateAddItemButton(t),decko.initPointerList(e)},nextPointerInput:function(t){var e,n,o,i,d,r;for(e=!0,n=0,d=(i=t.find("input")).length;n<d;n++)o=i[n],e=e&&""===$(o).val();return e?i:(r=t.clone(),t.after(r),r.attr("data-index",parseInt(t.attr("data-index")+1)),r.trigger("decko.item.added"),r.find("input"))},lastPointerItem:function(t){return $(t).closest(".content-editor").find(".pointer-li:last")},updateAddItemButton:function(t){var e,n;return e=$(t).closest(".content-editor").find("._pointer-item-add"),n=""===decko.lastPointerItem(t).find("input").val(),e.prop("disabled",n)}})}).call(this);
data/data/real.yml ADDED
@@ -0,0 +1,41 @@
1
+ ---
2
+ - :name: List
3
+ :type: :cardtype
4
+ :codename: list
5
+ :fields:
6
+ :description: |-
7
+ <p>Lists list one or more other cards. [[http://decko.org/:list|more]]</p>
8
+
9
+ - :name: Pointer
10
+ :type: :cardtype
11
+ :codename: pointer
12
+ :fields:
13
+ :description: |-
14
+ <p>Pointers point another card. [[http://decko.org/:pointer|more]]</p>
15
+
16
+ - :name: Nest list
17
+ :type: :cardtype
18
+ :codename: nest_list
19
+ - :name: Link list
20
+ :type: :cardtype
21
+ :codename: link_list
22
+
23
+ - :name: "*content options"
24
+ :type: :setting
25
+ :codename: content_options
26
+
27
+ - :name: "*content option view"
28
+ :type: :setting
29
+ :codename: content_option_view
30
+
31
+ - :name:
32
+ - :structure
33
+ - :right
34
+ - :default
35
+ :type: :nest_list
36
+
37
+ - :name:
38
+ - :all
39
+ - :content_option_view
40
+ :type: :phrase
41
+ :content: smart_label
data/data/test.yml ADDED
@@ -0,0 +1,12 @@
1
+ ---
2
+ - :name:
3
+ - friends
4
+ - :right
5
+ - :default
6
+ :type: :list
7
+ - :name:
8
+ - friends
9
+ - :right
10
+ - :content_options
11
+ :type: :search_type
12
+ :content: '{"type":"User"}'
@@ -2,7 +2,7 @@
2
2
 
3
3
  class AddPointerCards < Cardio::Migration
4
4
  def up
5
- ensure_card name: "script: pointer config",
5
+ Card.ensure name: "script: pointer config",
6
6
  type_id: Card::CoffeeScriptID,
7
7
  codename: "script_pointer_config"
8
8
  end
@@ -10,6 +10,42 @@ event :insert_item_event, :prepare_to_validate, on: :save, when: :item_to_insert
10
10
  insert_item index.to_i, item_to_insert
11
11
  end
12
12
 
13
+ event :validate_item_type, :validate, on: :save, when: :validate_item_type? do
14
+ ok_types = Array.wrap(ok_item_types).map(&:codename)
15
+ item_cards.each do |item|
16
+ next if ok_types.include? item.type_code
17
+
18
+ errors.add :content, t(:list_invalid_item_type,
19
+ item: item.name,
20
+ type: item.type,
21
+ allowed_types: ok_types.map(&:cardname).to_sentence)
22
+ end
23
+ end
24
+
25
+ event :validate_item_uniqueness, :validate, on: :save, when: :unique_items? do
26
+ return unless (dupes = duplicate_item_names)&.present?
27
+
28
+ errors.add :content, t(:list_duplicate_items_names, duplicates: dupes.to_sentence)
29
+ end
30
+
31
+ def duplicate_item_names
32
+ names = item_names
33
+ names.uniq.select { |n| names.count(n) > 1 }
34
+ end
35
+
36
+ # for override
37
+ def unique_items?
38
+ false
39
+ end
40
+
41
+ def ok_item_types
42
+ nil
43
+ end
44
+
45
+ def validate_item_type?
46
+ ok_item_types.present?
47
+ end
48
+
13
49
  def item_to_insert
14
50
  Env.params["insert_item"]
15
51
  end
@@ -0,0 +1,9 @@
1
+ .d-flex.flex-wrap{class:"_click-#{multiselect ? 'multi' : ''}select-editor"}
2
+ - options_hash = card.options_hash
3
+ - if options_hash.empty?
4
+ no options
5
+ - else
6
+ - options_hash.each do |option_label, option_name|
7
+ - checked = option_name == card.first_name
8
+ ._select-item{class: checked && "selected"}
9
+ = nest option_name, view: item_view
@@ -2,14 +2,12 @@
2
2
  - items.each do |item|
3
3
  %li.pointer-li.mb-1
4
4
  %span.input-group
5
- .input-group-prepend.handle
6
- %span.input-group-text
7
- = icon_tag :reorder
5
+ %span.input-group-text.handle
6
+ = icon_tag :reorder
8
7
  = text_field_tag "pointer_item", item, class: "pointer-item-text form-control",
9
8
  "data-options-card": options_card
10
- .input-group-append
11
- %button.pointer-item-delete.btn.btn-secondary{ type: "button"}
12
- = icon_tag :remove
9
+ %button.pointer-item-delete.btn.btn-secondary{ type: "button"}
10
+ = icon_tag :remove
13
11
  %span.input-group
14
12
  %button._pointer-item-add.btn.btn-secondary{ type: "submit" }
15
13
  = icon_tag "add"
@@ -2,11 +2,9 @@
2
2
 
3
3
  %li.pointer-li.mb-1
4
4
  %span.input-group
5
- .input-group-prepend.handle
6
- %span.input-group-text
7
- = icon_tag :reorder
5
+ %span.input-group-text.handle
6
+ = icon_tag :reorder
8
7
  = text_field_tag "pointer_item", item, class: "pointer-item-text form-control",
9
8
  "data-options-card": options_card
10
- .input-group-append
11
- %button.pointer-item-delete.btn.btn-secondary{:type => "button"}
12
- = icon_tag :remove
9
+ %button.pointer-item-delete.btn.btn-secondary{:type => "button"}
10
+ = icon_tag :remove
@@ -5,9 +5,9 @@
5
5
  - else
6
6
  - input_name = "pointer_radio_button-#{card.key}"
7
7
  - options_hash.each do |option_label, option_name|
8
- - checked = option_name == card.item_name
8
+ - checked = option_name == card.first_name
9
9
  - id = "pointer-radio-#{option_name.to_name.key}"
10
- %li.pointer-radio.radio
10
+ %li.pointer-radio.radio.form-check
11
11
  = radio_button_tag input_name, option_name, checked,
12
- id: id, class: "pointer-radio-button #{'_submit-on-change' if submit_on_change}"
12
+ id: id, class: "pointer-radio-button form-check-input #{'_submit-on-change' if submit_on_change}"
13
13
  = option_label option_label, id
@@ -3,15 +3,12 @@ format :html do
3
3
  standard_pointer_core
4
4
  end
5
5
 
6
- view :item_cores, cache: :never do
7
- card.known_item_cards.map do |item|
8
- nest item, view: :core
9
- end.join "\n"
10
- end
11
-
12
6
  def standard_pointer_core
13
7
  with_paging do |paging_args|
14
- wrap_with :div, standard_pointer_items(paging_args), class: "pointer-list"
8
+ wrap_with :div,
9
+ class: "card-list card-list-#{item_view_options[:view]} pointer-list" do
10
+ standard_pointer_items(paging_args)
11
+ end
15
12
  end
16
13
  end
17
14
 
@@ -20,8 +17,8 @@ format :html do
20
17
  end
21
18
 
22
19
  view :one_line_content do
23
- item_view = implicit_item_view
24
- item_view = item_view == "name" ? "name" : "link"
20
+ item_view = implicit_item_view&.to_sym
21
+ item_view = item_view == :name ? :name : :link
25
22
  wrap_with :div, class: "pointer-list one-line-pointer-list" do
26
23
  # limit to first 10 items to optimize
27
24
  pointer_items(view: item_view, limit: 10, offset: 0).join ", "
@@ -36,6 +33,10 @@ format :html do
36
33
  _render_hidden_content_field + super()
37
34
  end
38
35
 
36
+ def hidden_input
37
+ ""
38
+ end
39
+
39
40
  def default_input_type
40
41
  :list
41
42
  end
@@ -57,7 +58,7 @@ format :html do
57
58
  end
58
59
 
59
60
  def autocomplete_input
60
- haml :autocomplete_input, item: items_for_input.first, options_card: options_card_name
61
+ autocomplete_field items_for_input.first, options_card_name
61
62
  end
62
63
 
63
64
  def checkbox_input
@@ -71,34 +72,31 @@ format :html do
71
72
  def select_input
72
73
  options = { "-- Select --" => "" }.merge card.options_hash
73
74
  select_tag("pointer_select-#{unique_id}",
74
- options_for_select(options, card.item_name),
75
+ options_for_select(options, card.first_name),
75
76
  class: "pointer-select form-control")
76
77
  end
77
78
 
78
- def multiselect_input
79
- select_tag "pointer_multiselect-#{unique_id}",
80
- options_for_select(card.options_hash, card.item_names),
81
- multiple: true, class: "pointer-multiselect form-control"
79
+ def bar_select_input
80
+ raw haml(:click_select_input, item_view: :bar, multiselect: false)
82
81
  end
83
82
 
84
- def add_item_modal_link
85
- modal_link "Add Item",
86
- size: :large,
87
- class: "btn btn-sm btn-outline-secondary _add-item-link",
88
- path: { view: :filter_items_modal,
89
- item: implicit_item_view,
90
- filter_card: filter_card.name,
91
- slot_selector: filtered_list_slot_class,
92
- item_selector: "_filtered-list-item",
93
- slot: { hide: [:modal_footer] },
94
- filter: { not_ids: not_ids_value } }
83
+ def box_select_input
84
+ raw haml(:click_select_input, item_view: :box, multiselect: false)
95
85
  end
96
86
 
97
- def not_ids_value
98
- card.item_ids.map(&:to_s).join(",")
87
+ def bar_multiselect_input
88
+ raw haml(:click_select_input, item_view: :bar, multiselect: true)
99
89
  end
100
90
 
101
- def add_item_overlay_link; end
91
+ def box_multiselect_input
92
+ raw haml(:click_select_input, item_view: :box, multiselect: true)
93
+ end
94
+
95
+ def multiselect_input
96
+ select_tag "pointer_multiselect-#{unique_id}",
97
+ options_for_select(card.options_hash, card.item_names),
98
+ multiple: true, class: "pointer-multiselect form-control"
99
+ end
102
100
 
103
101
  def one_line_content
104
102
  if count == 1
@@ -68,7 +68,9 @@ end
68
68
 
69
69
  format :html do
70
70
  def option_label option_name, id
71
- %(<label for="#{id}">#{option_label_text option_name}</label>)
71
+ wrap_with :label, class: "form-check-label", for: id do
72
+ option_label_text option_name
73
+ end
72
74
  end
73
75
 
74
76
  def option_view
@@ -5,12 +5,6 @@ format do
5
5
  20
6
6
  end
7
7
 
8
- def item_links args={}
9
- card.item_cards(args).map do |item_card|
10
- subformat(item_card).render_link
11
- end
12
- end
13
-
14
8
  def nest_item_array
15
9
  card.item_cards.map do |item|
16
10
  nest_item item
@@ -28,6 +28,15 @@ def swap_names old_name, new_name
28
28
  end
29
29
  end
30
30
 
31
+ format :html do
32
+ view :view_list do
33
+ %i[bar box closed titled labeled].map do |view|
34
+ voo.items[:view] = view
35
+ wrap_with :p, [content_tag(:h3, "#{view} items"), render_content]
36
+ end
37
+ end
38
+ end
39
+
31
40
  private
32
41
 
33
42
  def chunk_class
@@ -1,4 +1,4 @@
1
- include_set Abstract::Pointer
1
+ include_set Abstract::List
2
2
 
3
3
  def self.included host_class
4
4
  basket[host_class.basket_name] = []
@@ -0,0 +1,8 @@
1
+ # remove set members from all lists that reference them upon deletion
2
+ event :delist, :prepare_to_store, on: :delete do
3
+ referers.each do |referer|
4
+ next unless referer.is_a? Abstract::List
5
+ referer.drop_item name
6
+ subcard referer
7
+ end
8
+ end
@@ -20,6 +20,19 @@ def item_names args={}
20
20
  item_ids(args).map(&:cardname).compact
21
21
  end
22
22
 
23
- def export_content
23
+ def pod_content
24
24
  item_names.join "\n"
25
25
  end
26
+
27
+ def replace_references _old_name, _new_name
28
+ # noop
29
+ end
30
+
31
+ # override reference creation so there are no referee_keys
32
+ # (referee_keys can screw things up for these cards when things get renamed)
33
+ def create_references_out
34
+ referee_ids = item_ids
35
+ return if referee_ids.empty?
36
+
37
+ Reference.mass_insert(referee_ids.map { |rid| [id, rid, "null", "'L'"] })
38
+ end
@@ -0,0 +1,11 @@
1
+ format do
2
+ def chunk_list
3
+ :references
4
+ end
5
+ end
6
+
7
+ format :html do
8
+ def header_field text
9
+ content_tag :span, text, class: "form-control border-0 control-label"
10
+ end
11
+ end
@@ -1,12 +1,4 @@
1
- basket[:list_input_options] = [
2
- "radio",
3
- "checkbox",
4
- "select",
5
- "multiselect",
6
- "list",
7
- "filtered list",
8
- "autocomplete"
9
- ]
1
+ basket[:list_input_options] = %w[radio checkbox select multiselect list autocomplete]
10
2
 
11
3
  def supports_content_options?
12
4
  false
@@ -1,3 +1,5 @@
1
+ assign_type :phrase
2
+
1
3
  format :html do
2
4
  def quick_edit
3
5
  if card.left.prototype_default_card&.try(:show_content_options?) &&
@@ -1,3 +1,5 @@
1
+ assign_type :list
2
+
1
3
  def default_limit
2
4
  cql_limit = fetch_query.limit if respond_to?(:fetch_query)
3
5
  cql_limit || 50
@@ -1,4 +1,4 @@
1
- include_set Abstract::Pointer
1
+ include_set Abstract::List
2
2
 
3
3
  def content
4
4
  basket[:list_input_options].to_pointer_content
@@ -0,0 +1,21 @@
1
+ %div.input-group
2
+ %span.input-group-text.handle
3
+ = icon_tag :reorder
4
+ %span.input-group-text.text-muted.group-text-left.input-nest-syntax
5
+ = prefix
6
+ = name_field
7
+ %span.input-group-text.text-muted.group-text-middle.input-nest-syntax
8
+ = "|"
9
+ = options_field
10
+ %span.input-group-text.text-muted.group-text-end.input-nest-syntax
11
+ = postfix
12
+ .input-group-append
13
+ %span.input-group-text.p-0
14
+ %button.pointer-item-delete.btn.btn-secondary{ type: "button"}
15
+ = icon_tag :remove
16
+ - if show_edit_button
17
+ .input-group-append.ml-2
18
+ %span.input-group-text.p-0.border-primary
19
+ %button.btn.btn-primary._open-nest-editor{ type: "button"}
20
+ = icon_tag :edit
21
+
@@ -1,4 +1,5 @@
1
1
  include_set Abstract::Items
2
+ include_set Abstract::ReferenceList
2
3
 
3
4
  def raw_item_strings content
4
5
  reference_chunks(content).map(&:referee_name)
@@ -10,10 +11,12 @@ def item_titles default_to_name=true
10
11
  end
11
12
  end
12
13
 
13
- format do
14
- def chunk_list
15
- :references
16
- end
14
+ def standardize_item string
15
+ string.match?(/^\[/) ? string : "[[#{string}]]"
16
+ end
17
+
18
+ def to_pointer_content
19
+ map { |item| item.to_s.strip }.join "\n"
17
20
  end
18
21
 
19
22
  format :html do
@@ -0,0 +1,21 @@
1
+ .nest-form-header
2
+ = haml_partial :reference_list_input_row,
3
+ name_field: header_field("Link target"),
4
+ options_field: header_field("Link text"),
5
+ postfix: "}}",
6
+ prefix: "{{",
7
+ show_edit_button: false
8
+ %ul{class: ul_classes}
9
+ - items.each.with_index do |(reference, title), index|
10
+ %li.pointer-li.mb-2._nest-form{"data-index": index}
11
+ = haml_partial :reference_list_input_row,
12
+ name_field: name_autocomplete_field(reference, "_reference pointer-item-text form-control group-text-middle"),
13
+ options_field: text_field_tag("pointer_item_title", title, class: "_title pointer-item-text form-control group-text-middle"),
14
+ prefix: "[[",
15
+ postfix: "]]",
16
+ show_edit_button: false
17
+
18
+ %span.input-group
19
+ %button._pointer-item-add.btn.btn-secondary{ type: "submit" }
20
+ = icon_tag "add"
21
+ add another
data/set/type/list.rb CHANGED
@@ -1,10 +1 @@
1
- include_set Abstract::Pointer
2
-
3
- format :html do
4
- view :view_list do
5
- %i[info_bar bar box closed titled labeled].map do |view|
6
- voo.items[:view] = view
7
- wrap_with :p, [content_tag(:h3, "#{view} items"), render_content]
8
- end
9
- end
10
- end
1
+ include_set Abstract::List
@@ -1,4 +1,5 @@
1
1
  include_set Abstract::Items
2
+ include_set Abstract::ReferenceList
2
3
 
3
4
  def raw_item_strings content
4
5
  reference_chunks(content).map(&:referee_raw_name)
@@ -12,12 +13,6 @@ def items_content array
12
13
  standardized_items(array).join "\n"
13
14
  end
14
15
 
15
- format do
16
- def chunk_list
17
- :references
18
- end
19
- end
20
-
21
16
  format :html do
22
17
  def input_type
23
18
  :nest_list
@@ -0,0 +1,21 @@
1
+ .nest-form-header
2
+ = haml_partial :reference_list_input_row,
3
+ name_field: header_field("Name"),
4
+ options_field: header_field("Options"),
5
+ postfix: "}}",
6
+ prefix: "{{",
7
+ show_edit_button: false
8
+ %ul{class: ul_classes}
9
+ - items.each.with_index do |(reference, title), index|
10
+ %li.pointer-li.mb-2._nest-form{"data-index": index}
11
+ = haml_partial :reference_list_input_row,
12
+ name_field: name_autocomplete_field(reference, "_reference pointer-item-text form-control group-text-middle"),
13
+ options_field: text_field_tag("pointer_item_title", title, class: "_title _nest-options pointer-item-text form-control group-text-middle"),
14
+ prefix: "{{",
15
+ postfix: "}}",
16
+ show_edit_button: true
17
+
18
+ %span.input-group
19
+ %button._pointer-item-add.btn.btn-secondary{ type: "submit" }
20
+ = icon_tag "add"
21
+ add another
data/set/type/pointer.rb CHANGED
@@ -1,10 +1 @@
1
- include_set Abstract::Pointer
2
-
3
- format :html do
4
- view :view_list do
5
- %i[info_bar bar box closed titled labeled].map do |view|
6
- voo.items[:view] = view
7
- wrap_with :p, [content_tag(:h3, "#{view} items"), render_content]
8
- end
9
- end
10
- end
1
+ include_set Abstract::List
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: card-mod-list
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.2
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ethan McCutchen
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-01-08 00:00:00.000000000 Z
13
+ date: 2023-03-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: card
@@ -18,42 +18,42 @@ dependencies:
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 1.104.2
21
+ version: 1.105.1
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - '='
27
27
  - !ruby/object:Gem::Version
28
- version: 1.104.2
28
+ version: 1.105.1
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: card-mod-format
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
33
  - - '='
34
34
  - !ruby/object:Gem::Version
35
- version: 0.14.2
35
+ version: 0.15.1
36
36
  type: :runtime
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - '='
41
41
  - !ruby/object:Gem::Version
42
- version: 0.14.2
42
+ version: 0.15.1
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: card-mod-collection
45
45
  requirement: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - '='
48
48
  - !ruby/object:Gem::Version
49
- version: 0.14.2
49
+ version: 0.15.1
50
50
  type: :runtime
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - '='
55
55
  - !ruby/object:Gem::Version
56
- version: 0.14.2
56
+ version: 0.15.1
57
57
  description: ''
58
58
  email:
59
59
  - info@decko.org
@@ -61,34 +61,42 @@ executables: []
61
61
  extensions: []
62
62
  extra_rdoc_files: []
63
63
  files:
64
+ - assets/script/pointer_config.js.coffee
65
+ - assets/script/pointer_list_editor.js.coffee
66
+ - assets/style/list.scss
67
+ - config/locales/de.yml
68
+ - config/locales/en.yml
69
+ - data/files/mod_list_script_asset_output/file.js
70
+ - data/real.yml
71
+ - data/test.yml
64
72
  - db/migrate_core_cards/20160804112340_add_pointer_cards.rb
65
73
  - db/migrate_core_cards/20211107155032_out_with_the_brackets.rb
66
- - set/abstract/01_pointer.rb
67
- - set/abstract/01_pointer/events.rb
68
- - set/abstract/01_pointer/html_views.rb
69
- - set/abstract/01_pointer/html_views/autocomplete_input.haml
70
- - set/abstract/01_pointer/html_views/checkbox_input.haml
71
- - set/abstract/01_pointer/html_views/filter.rb
72
- - set/abstract/01_pointer/html_views/filter/filter_items.haml
73
- - set/abstract/01_pointer/html_views/filter/filtered_list_input.haml
74
- - set/abstract/01_pointer/html_views/list_input.haml
75
- - set/abstract/01_pointer/html_views/list_item.haml
76
- - set/abstract/01_pointer/html_views/radio_input.haml
77
- - set/abstract/01_pointer/options_api.rb
78
- - set/abstract/01_pointer/other_views.rb
79
- - set/abstract/code_pointer.rb
80
- - set/abstract/id_pointer.rb
74
+ - set/abstract/01_list.rb
75
+ - set/abstract/01_list/events.rb
76
+ - set/abstract/01_list/html_views.rb
77
+ - set/abstract/01_list/html_views/checkbox_input.haml
78
+ - set/abstract/01_list/html_views/click_select_input.haml
79
+ - set/abstract/01_list/html_views/list_input.haml
80
+ - set/abstract/01_list/html_views/list_item.haml
81
+ - set/abstract/01_list/html_views/radio_input.haml
82
+ - set/abstract/01_list/options_api.rb
83
+ - set/abstract/01_list/other_views.rb
84
+ - set/abstract/codename_list.rb
85
+ - set/abstract/delist.rb
86
+ - set/abstract/id_list.rb
87
+ - set/abstract/reference_list.rb
81
88
  - set/all/supports_content_options.rb
82
89
  - set/right/content_option_view.rb
83
90
  - set/right/content_options.rb
84
91
  - set/self/content_option_view.rb
85
92
  - set/self/content_options.rb
86
93
  - set/self/input_options.rb
94
+ - set/type/_reference_list_input_row.haml
87
95
  - set/type/link_list.rb
88
- - set/type/link_list/link_list_input.haml
96
+ - set/type/link_list_input.haml
89
97
  - set/type/list.rb
90
98
  - set/type/nest_list.rb
91
- - set/type/nest_list/nest_list_input.haml
99
+ - set/type/nest_list_input.haml
92
100
  - set/type/pointer.rb
93
101
  homepage: https://decko.org
94
102
  licenses:
@@ -100,6 +108,7 @@ metadata:
100
108
  wiki_uri: https://decko.org
101
109
  documentation_url: http://docs.decko.org/
102
110
  card-mod: list
111
+ card-mod-group: gem-defaults
103
112
  post_install_message:
104
113
  rdoc_options: []
105
114
  require_paths:
@@ -115,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
124
  - !ruby/object:Gem::Version
116
125
  version: '0'
117
126
  requirements: []
118
- rubygems_version: 3.2.15
127
+ rubygems_version: 3.3.11
119
128
  signing_key:
120
129
  specification_version: 4
121
130
  summary: list of cards
@@ -1,3 +0,0 @@
1
- .pointer-list-editor.pointer-list-ul
2
- = text_field_tag 'pointer_item', item, class: "pointer-item-text form-control",
3
- "data-options-card": options_card
@@ -1,28 +0,0 @@
1
- ._filter-items.container-fluid.nodblclick._noFilterUrlUpdates
2
- .row
3
- = nest filter_card, view: :filter_form
4
- ._unselected.col-6.border.mt-2.px-0
5
- = nest filter_card, view: :select_item,
6
- items: { view: implicit_item_view },
7
- cql: { limit: 10 }
8
- ._selected.col-6.border.mt-2.pl-0.pr-0
9
- .selected-box
10
- .card-header
11
- %h5
12
- Selected
13
- .badge.badge-secondary
14
- %span._selected-items 0
15
- ._selected-item-list{ style: "display:none" }
16
- .deselector.p-2
17
- %input#deselect-all._deselect-all{ type: "checkbox", checked: true, disabled: true }
18
- %label{ for: "deselect-all" }
19
- deselect
20
- %span._selected-items 0
21
- following
22
- ._selected-bin.p-2
23
- ._filter-help.alert.alert-secondary
24
- Filter and select items to add them here.
25
- .form-group
26
- .selected-item-buttons.p-2
27
- = button_tag "Cancel", class: "cancel-modal", data: { dismiss: :modal }
28
- = render :add_selected_link
@@ -1,16 +0,0 @@
1
- %div.filtered-list-editor
2
- %ul.pointer-list.filtered-list-review._pointer-filtered-list.list-group.vertical.p-0
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
- = add_item_modal_link
16
-
@@ -1,63 +0,0 @@
1
- format :html do
2
- view :filtered_list, unknown: true do
3
- filtered_list_input
4
- end
5
-
6
- view :filter_items_modal, unknown: true, wrap: :modal do
7
- render_filter_items
8
- end
9
-
10
- view :filter_items, unknown: true, wrap: :slot, template: :haml
11
-
12
- def filtered_list_input
13
- with_nest_mode :normal do
14
- class_up "card-slot", filtered_list_slot_class
15
- with_class_up "card-slot", filtered_list_slot_class do
16
- wrap do
17
- haml :filtered_list_input
18
- end
19
- end
20
- end
21
- end
22
-
23
- # NOCACHE because params alter view
24
- view :add_selected_link, cache: :never, unknown: true do
25
- link_to "Add Selected",
26
- path: { filter_card: params[:filter_card] },
27
- class: "_add-selected slotter _close-modal btn btn-primary disabled",
28
- data: { "slot-selector": ".#{params[:slot_selector]}",
29
- "item-selector": ".#{params[:item_selector]}",
30
- remote: true }
31
- end
32
-
33
- def filtered_list_item item_card
34
- nest_item item_card do |rendered, item_view|
35
- wrap_item rendered, item_view
36
- end
37
- end
38
-
39
- # for override
40
- # @return [Card] search card on which filtering is based
41
- def filter_card
42
- filter_card_from_params || default_filter_card
43
- end
44
-
45
- def default_filter_card
46
- fcard = card.options_card
47
- return fcard if fcard.respond_to? :cql_hash
48
-
49
- fcard.fetch :referred_to_by, new: {}
50
- end
51
-
52
- def filter_card_from_params
53
- return unless params[:filter_card]
54
-
55
- Card.fetch params[:filter_card], new: {}
56
- end
57
-
58
- # currently actually used as a class
59
- # (because we don't have api to override slot's id)
60
- def filtered_list_slot_class
61
- @filtered_list_slot_class ||= "filtered-list-#{unique_id}"
62
- end
63
- end
@@ -1,24 +0,0 @@
1
- %ul{class: ul_classes}
2
- - items.each.with_index do |(reference, title), index|
3
- %li.pointer-li.mb-1{"data-index": index}
4
- %span.input-group
5
- .input-group-prepend.handle
6
- %span.input-group-text
7
- = icon_tag :reorder
8
- %span.input-group-text.text-muted.group-text-left
9
- [[
10
- = text_field_tag "pointer_item", reference, class: "_reference pointer-item-text form-control group-text-middle",
11
- "data-options-card": options_card
12
- %span.input-group-text.text-muted.group-text-middle
13
- |
14
- = text_field_tag "pointer_item_title", title, class: "_title pointer-item-text form-control group-text-middle"
15
- %span.input-group-text.text-muted.group-text-right
16
- ]]
17
- .input-group-append
18
- %span.input-group-text.p-0
19
- %button.pointer-item-delete.btn.btn-secondary{ type: "button"}
20
- = icon_tag :remove
21
- %span.input-group
22
- %button._pointer-item-add.btn.btn-secondary{ type: "submit" }
23
- = icon_tag "add"
24
- add another
@@ -1,28 +0,0 @@
1
- %ul{class: ul_classes}
2
- - items.each.with_index do |(reference, title), index|
3
- %li.pointer-li.mb-1._nest-form{"data-index": index}
4
- %span.input-group
5
- .input-group-prepend.handle
6
- %span.input-group-text
7
- = icon_tag :reorder
8
- %span.input-group-text.text-muted.group-text-left
9
- {{
10
- = text_field_tag "pointer_item", reference, class: "_reference pointer-item-text form-control group-text-middle"
11
- %span.input-group-text.text-muted.group-text-middle
12
- = "|"
13
- = text_field_tag "pointer_item_title", title, class: "_title _nest-options pointer-item-text form-control group-text-middle"
14
- %span.input-group-text.text-muted.group-text-right
15
- }}
16
- .input-group-append
17
- %span.input-group-text.p-0
18
- %button.btn.btn-secondary._open-nest-editor{ type: "button"}
19
- = icon_tag :edit
20
- .input-group-append
21
- %span.input-group-text.p-0
22
- %button.pointer-item-delete.btn.btn-secondary{ type: "button"}
23
- = icon_tag :remove
24
-
25
- %span.input-group
26
- %button._pointer-item-add.btn.btn-secondary{ type: "submit" }
27
- = icon_tag "add"
28
- add another