para 0.8.3.2 → 0.8.9
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.
- checksums.yaml +5 -5
- data/app/assets/javascripts/para/admin.coffee +2 -1
- data/app/assets/javascripts/para/admin/table.coffee +3 -5
- data/app/assets/javascripts/para/admin/tree.coffee +68 -16
- data/app/assets/javascripts/para/inputs/multi-select-input.coffee +2 -3
- data/app/assets/javascripts/para/inputs/nested_many.coffee +10 -6
- data/app/controllers/para/admin/search_controller.rb +11 -1
- data/app/helpers/para/admin/nested_inputs_helper.rb +17 -0
- data/app/helpers/para/tag_helper.rb +3 -1
- data/app/views/para/admin/resources/_tree.html.haml +4 -0
- data/app/views/para/inputs/_nested_many.html.haml +3 -3
- data/app/views/para/inputs/nested_many/_add.html.haml +1 -1
- data/app/views/para/inputs/nested_many/_add_with_subclasses.html.haml +1 -1
- data/app/views/para/inputs/nested_many/_container.html.haml +1 -1
- data/app/views/para/inputs/nested_one/_add_with_subclasses.html.haml +1 -1
- data/lib/generators/para/install/templates/initializer.rb +9 -1
- data/lib/para/config.rb +3 -0
- data/lib/para/form_builder/containers.rb +3 -1
- data/lib/para/inputs/nested_many_input.rb +7 -2
- data/lib/para/markup/resources_buttons.rb +6 -2
- data/lib/para/markup/resources_table.rb +3 -3
- data/lib/para/version.rb +1 -1
- data/vendor/assets/javascripts/Sortable.js +4144 -0
- data/vendor/assets/javascripts/jquery.sortable.js +76 -0
- metadata +9 -6
- data/vendor/assets/javascripts/html5-sortable.js +0 -142
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 21679101c22d1235aad9fbe2305f7dddab8d38f0
|
4
|
+
data.tar.gz: a1d88c43b137eb2d075e6d9f2c0e206079939f2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e9304958f70e770b5632b8636c42183fbf5aa504d8e5f5bd3f183f7e16588bd1d7bf0e571d47d54091d24526d938bd74af43cd93449931ac20d4a5178d2f77d
|
7
|
+
data.tar.gz: a2e962254b1001b3930c2a9dc62f8867f5027a9d049cbdb9659f3bc81d7093b07057dcb9855641c65110db7fe605cae6db58f994b1ba838b799186bff0f6d860
|
@@ -6,7 +6,8 @@
|
|
6
6
|
#= require jasny-bootstrap
|
7
7
|
#= require simple_form_extension
|
8
8
|
#= require jquery.scrollto
|
9
|
-
#= require
|
9
|
+
#= require Sortable
|
10
|
+
#= require jquery.sortable
|
10
11
|
#= require cocoon
|
11
12
|
#= require jquery.remote-modal-form
|
12
13
|
#= require jquery.iframe-transport
|
@@ -12,11 +12,9 @@ class Para.ResourceTable
|
|
12
12
|
|
13
13
|
@$tbody.sortable
|
14
14
|
handle: '.order-anchor'
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@$tbody.on('sortupdate', $.proxy(@sortUpdate, this))
|
15
|
+
draggable: 'tr'
|
16
|
+
ghostClass: 'sortable-placeholder'
|
17
|
+
onUpdate: $.proxy(@sortUpdate, this)
|
20
18
|
|
21
19
|
sortUpdate: ->
|
22
20
|
@$tbody.find('tr').each (i, el) ->
|
@@ -6,22 +6,75 @@ class Para.ResourceTree
|
|
6
6
|
@orderUrl = @$el.data('url')
|
7
7
|
@maxDepth = parseInt @$el.data('max-depth')
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
# Each is needed here as the sortable jQuery plugin doesn't loop over each found node
|
10
|
+
# but initializes the tree on the first found element.
|
11
|
+
$(".tree").each(@initializeSubTree)
|
12
|
+
|
13
|
+
initializeSubTree: (_i, el) =>
|
14
|
+
$(el).sortable(
|
15
|
+
group: "tree"
|
16
|
+
handle: ".handle"
|
17
|
+
draggable: ".node"
|
18
|
+
fallbackOnBody: true
|
19
|
+
swapThreshold: 0.65
|
20
|
+
animation: 150
|
21
|
+
onSort: @handleOrderUpdated
|
22
|
+
onMove: @isMovementValid
|
23
|
+
)
|
24
|
+
|
25
|
+
# Note : This method is called often (many times per second while we're dragging) and
|
26
|
+
# takes quite some processing.
|
27
|
+
isMovementValid: (e) =>
|
28
|
+
$movedNode = $(e.dragged)
|
29
|
+
$target = $(e.related)
|
30
|
+
|
31
|
+
# Calculate the deepness of the moved and target nodes
|
32
|
+
movedNodeDeepness = $movedNode.parents(".node").length - 1
|
33
|
+
# If the target is a node, the moved node root deepness is gonna be the same as the
|
34
|
+
# the target one, else the tree's parent node is counted also
|
35
|
+
targetDeepness = $target.parents(".node").length - 1
|
36
|
+
|
37
|
+
# Find the deepest node in the subtree of the moved node
|
38
|
+
$movedNodeSubtrees = $movedNode.find(".tree")
|
39
|
+
movedNodeTreeDeepness = 0
|
40
|
+
|
41
|
+
# The movedNodeTreeDeepness is the maximum deepness of a child node of the current
|
42
|
+
# moved node, relative to the moved node
|
43
|
+
$movedNodeSubtrees.each (i, el) =>
|
44
|
+
subtreeDeepness = $(el).parents(".node").length - 1
|
45
|
+
subtreeRelativeDeepness = subtreeDeepness - movedNodeDeepness
|
46
|
+
movedNodeTreeDeepness = Math.max(movedNodeTreeDeepness, subtreeRelativeDeepness)
|
47
|
+
|
48
|
+
# Calculate the final subtree deepness once we move the whole moved node subtree to
|
49
|
+
# its target position
|
50
|
+
finalSubtreeDeepnessAfterMove = movedNodeTreeDeepness + targetDeepness
|
51
|
+
|
52
|
+
# We finally validate the move only if the final subtree deepness is lower than the
|
53
|
+
# maximum allowed depth
|
54
|
+
finalSubtreeDeepnessAfterMove <= @maxDepth
|
55
|
+
|
56
|
+
handleOrderUpdated: (e) =>
|
57
|
+
|
58
|
+
# Get all involved tree leaves that may include a subtree
|
59
|
+
treeLeaves = [$(e.target), $(e.from), $(e.item).find('.tree')]
|
60
|
+
|
61
|
+
# Update their placeholder display wether they can be a drop target or not
|
62
|
+
@handlePlaceholder($el) for $el in treeLeaves
|
63
|
+
# Save the tree structure on the server
|
18
64
|
@updateOrder()
|
19
65
|
|
66
|
+
# This method checks wether a given tree leaf can be a drop target, depending
|
67
|
+
# on wether it's located at the maximum allowed depth for the tree or not, and adds or
|
68
|
+
# remove a the visual placeholder to indicate its droppable state.
|
69
|
+
#
|
20
70
|
handlePlaceholder: ($el) ->
|
21
71
|
$placeholder = $el.find("> .placeholder")
|
22
|
-
|
72
|
+
parentsCount = $el.parents('.node').length - 1
|
73
|
+
hasChildren = $el.find('> .node').length
|
74
|
+
|
75
|
+
if parentsCount >= @maxDepth or hasChildren
|
23
76
|
$placeholder.hide()
|
24
|
-
$el.children("
|
77
|
+
$el.children(".tree").each (index, el) => @handlePlaceholder($(el))
|
25
78
|
else
|
26
79
|
$placeholder.show()
|
27
80
|
|
@@ -29,9 +82,8 @@ class Para.ResourceTree
|
|
29
82
|
Para.ajax(
|
30
83
|
url: @orderUrl
|
31
84
|
method: 'patch'
|
32
|
-
data:
|
33
|
-
|
34
|
-
success: $.proxy(@orderUpdated, this)
|
85
|
+
data: { resources: @buildOrderedData() }
|
86
|
+
success: @orderUpdated
|
35
87
|
)
|
36
88
|
|
37
89
|
buildOrderedData: ->
|
@@ -41,11 +93,11 @@ class Para.ResourceTree
|
|
41
93
|
data[index] = {
|
42
94
|
id: $this.data("id"),
|
43
95
|
position: index,
|
44
|
-
parent_id: $this.
|
96
|
+
parent_id: $this.parents(".node:first").data("id")
|
45
97
|
}
|
46
98
|
data
|
47
99
|
|
48
|
-
orderUpdated:
|
100
|
+
orderUpdated: =>
|
49
101
|
# TODO: Add flash message to display ordering success
|
50
102
|
|
51
103
|
$(document).on 'page:change turbolinks:load', ->
|
@@ -122,10 +122,9 @@ class Para.MultiSelectInput extends Vertebra.View
|
|
122
122
|
|
123
123
|
@$selectedItems.sortable
|
124
124
|
handle: '.order-anchor'
|
125
|
-
|
126
|
-
placeholder: "<tr><td colspan='#{ columnsCount }'></td></tr>"
|
125
|
+
animation: 150
|
127
126
|
|
128
|
-
@$selectedItems.on('
|
127
|
+
@$selectedItems.on('sort', @selectedItemsSorted)
|
129
128
|
|
130
129
|
selectedItemsSorted: =>
|
131
130
|
indices = {}
|
@@ -13,17 +13,17 @@ class Para.NestedManyField
|
|
13
13
|
|
14
14
|
@$fieldsList.sortable
|
15
15
|
handle: '.order-anchor'
|
16
|
-
|
16
|
+
animation: 150
|
17
|
+
onUpdate: $.proxy(@handleOrderingUpdated, this)
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
sortUpdate: ->
|
21
|
-
@$fieldsList.find('.form-fields').each (i, el) ->
|
19
|
+
handleOrderingUpdated: ->
|
20
|
+
@$fieldsList.find('.form-fields:visible').each (i, el) ->
|
22
21
|
$(el).find('.resource-position-field').val(i)
|
23
22
|
|
24
23
|
initializeCocoon: ->
|
25
24
|
@$fieldsList.on 'cocoon:after-insert', $.proxy(@afterInsertField, this)
|
26
25
|
@$fieldsList.on 'cocoon:before-remove', $.proxy(@beforeRemoveField, this)
|
26
|
+
@$fieldsList.on 'cocoon:after-remove', $.proxy(@afterRemoveField, this)
|
27
27
|
|
28
28
|
afterInsertField: (e, $element) ->
|
29
29
|
if ($collapsible = $element.find('[data-open-on-insert="true"]')).length
|
@@ -32,7 +32,7 @@ class Para.NestedManyField
|
|
32
32
|
if @orderable
|
33
33
|
@$fieldsList.sortable('destroy')
|
34
34
|
@initializeOrderable()
|
35
|
-
@
|
35
|
+
@handleOrderingUpdated()
|
36
36
|
|
37
37
|
$element.simpleForm()
|
38
38
|
|
@@ -42,6 +42,10 @@ class Para.NestedManyField
|
|
42
42
|
# create an empty nested resource otherwise
|
43
43
|
$nextEl.remove() if $nextEl.is('[data-attributes-mappings]') and not $element.is('[data-persisted]')
|
44
44
|
|
45
|
+
# When a sub field is removed, update every sub field position
|
46
|
+
afterRemoveField: ->
|
47
|
+
@handleOrderingUpdated();
|
48
|
+
|
45
49
|
openInsertedField: ($field) ->
|
46
50
|
$target = $($field.attr('href'))
|
47
51
|
$target.collapse('show')
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Para
|
2
2
|
module Admin
|
3
3
|
class SearchController < ApplicationController
|
4
|
+
include Para::Helpers::ResourceName
|
5
|
+
|
4
6
|
def index
|
5
7
|
# Parse ids that are provided as string into array
|
6
8
|
if params[:q] && params[:q][:id_in].is_a?(String)
|
@@ -9,8 +11,16 @@ module Para
|
|
9
11
|
|
10
12
|
model = params[:model_name].constantize
|
11
13
|
@results = model.ransack(params[:q]).result
|
14
|
+
@results = @results.limit(params[:limit]) if params[:limit]
|
12
15
|
|
13
|
-
|
16
|
+
case params[:mode]
|
17
|
+
when "selectize"
|
18
|
+
render json: @results.map { |res|
|
19
|
+
{ text: resource_name(res), value: res.id }
|
20
|
+
}
|
21
|
+
else
|
22
|
+
render layout: false
|
23
|
+
end
|
14
24
|
end
|
15
25
|
end
|
16
26
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Para
|
2
|
+
module Admin
|
3
|
+
module NestedInputsHelper
|
4
|
+
# Helper that allows filling a parent association for a given resource, based on the
|
5
|
+
# inverse_of option of the parent resource association.
|
6
|
+
#
|
7
|
+
def with_inverse_association_for(resource, attribute_name, parent_resource)
|
8
|
+
resource.tap do
|
9
|
+
association_name = parent_resource.association(attribute_name).options[:inverse_of]
|
10
|
+
return resource unless association_name
|
11
|
+
|
12
|
+
resource.association(association_name).replace(parent_resource)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -21,7 +21,7 @@ module Para
|
|
21
21
|
allow_adding_resource = options.fetch(:addable, true)
|
22
22
|
|
23
23
|
partial = :list
|
24
|
-
partial = :tree if model.respond_to? :
|
24
|
+
partial = :tree if model.respond_to?(:roots) && can?(:tree, model)
|
25
25
|
|
26
26
|
render(
|
27
27
|
partial: find_partial_for(relation, partial),
|
@@ -48,6 +48,8 @@ module Para
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def add_button_for(component, relation, model)
|
51
|
+
return unless can?(:create, model)
|
52
|
+
|
51
53
|
partial_name = if component.subclassable?
|
52
54
|
:subclassable_add_button
|
53
55
|
else
|
@@ -4,6 +4,10 @@
|
|
4
4
|
= render partial: find_partial_for(relation, :actions), locals: { relation: relation, component: component, model: model, allow_adding_resource: allow_adding_resource }
|
5
5
|
|
6
6
|
- if resources.length > 0
|
7
|
+
- if resources.total_count > 100
|
8
|
+
.page-entries-info
|
9
|
+
= page_entries(resources)
|
10
|
+
|
7
11
|
%ul.tree.root-tree{ data: { url: component.relation_path(model.model_name.route_key, action: :tree), :"max-depth" => max_depth_for(model) } }
|
8
12
|
= render partial: find_partial_for(model, 'tree_item'), collection: resources.roots, as: :root, locals: { model: model}
|
9
13
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
.nested-many-field{ class: [('orderable' if orderable), ('nested-many-field-inset' if inset)] }
|
2
2
|
.fields-list{ id: dom_identifier }
|
3
3
|
= form.simple_fields_for attribute_name, resources, nested_attribute_name: attribute_name, orderable: orderable, track_attribute_mappings: render_partial do |nested_form|
|
4
|
-
= render partial: find_partial_for(model, 'nested_many/container', partial_dir: 'inputs'), locals: { form: nested_form, model: nested_form.object.class, subclass: subclass, nested_locals: nested_locals, inset: inset, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
4
|
+
= render partial: find_partial_for(model, 'nested_many/container', partial_dir: 'inputs'), locals: { form: nested_form, model: nested_form.object.class, subclass: subclass, nested_locals: nested_locals, inset: inset, uncollapsed: uncollapsed, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
5
5
|
-# Add button
|
6
6
|
- if add_button
|
7
7
|
- if subclass
|
8
|
-
= render partial: 'para/inputs/nested_many/add_with_subclasses', locals: { form: form, model: model, dom_identifier: dom_identifier, nested_locals: nested_locals, attribute_name: attribute_name, orderable: orderable, subclasses: subclasses, inset: inset, add_button_label: add_button_label, add_button_class: add_button_class, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
8
|
+
= render partial: 'para/inputs/nested_many/add_with_subclasses', locals: { form: form, model: model, dom_identifier: dom_identifier, nested_locals: nested_locals, attribute_name: attribute_name, orderable: orderable, subclasses: subclasses, inset: inset, uncollapsed: uncollapsed, add_button_label: add_button_label, add_button_class: add_button_class, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
9
9
|
- else
|
10
|
-
= render partial: 'para/inputs/nested_many/add', locals: { form: form, model: model, dom_identifier: dom_identifier, nested_locals: nested_locals, attribute_name: attribute_name, orderable: orderable, inset: inset, add_button_label: add_button_label, add_button_class: add_button_class, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
10
|
+
= render partial: 'para/inputs/nested_many/add', locals: { form: form, model: model, dom_identifier: dom_identifier, nested_locals: nested_locals, attribute_name: attribute_name, orderable: orderable, inset: inset, uncollapsed: uncollapsed, add_button_label: add_button_label, add_button_class: add_button_class, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
@@ -1,2 +1,2 @@
|
|
1
|
-
= link_to_add_association form, attribute_name, partial: find_partial_for(model, 'nested_many/container', partial_dir: 'inputs'), form_name: 'form', class: "btn btn-shadow add-button nested-many-inset-add-button #{add_button_class}", data: { :'association-insertion-node' => "##{ dom_identifier }", :'association-insertion-method' => 'append' }, render_options: { nested_attribute_name: attribute_name, orderable: orderable, locals: { model: model, nested_locals: nested_locals, inset: inset, render_partial: render_partial, remote_partial_params: remote_partial_params } } do
|
1
|
+
= link_to_add_association form, attribute_name, partial: find_partial_for(model, 'nested_many/container', partial_dir: 'inputs'), form_name: 'form', class: "btn btn-shadow add-button nested-many-inset-add-button #{add_button_class}", data: { :'association-insertion-node' => "##{ dom_identifier }", :'association-insertion-method' => 'append' }, render_options: { nested_attribute_name: attribute_name, orderable: orderable, locals: { model: model, nested_locals: nested_locals, inset: inset, uncollapsed: uncollapsed, render_partial: render_partial, remote_partial_params: remote_partial_params } } do
|
2
2
|
= add_button_label
|
@@ -5,5 +5,5 @@
|
|
5
5
|
%ul.dropdown-menu
|
6
6
|
- subclasses.each do |submodel|
|
7
7
|
%li
|
8
|
-
= link_to_add_association form, attribute_name, wrap_object: proc { submodel.new }, partial: find_partial_for(submodel, 'nested_many/container', partial_dir: 'inputs'), form_name: 'form', class: 'dropdown-link', data: { :'association-insertion-node' => "##{ dom_identifier }", :'association-insertion-method' => 'append' }, render_options: { nested_attribute_name: attribute_name, orderable: orderable, locals: { model: submodel, nested_locals: nested_locals, inset: inset, render_partial: render_partial, remote_partial_params: remote_partial_params } } do
|
8
|
+
= link_to_add_association form, attribute_name, wrap_object: proc { with_inverse_association_for(submodel.new, attribute_name, form.object) }, partial: find_partial_for(submodel, 'nested_many/container', partial_dir: 'inputs'), form_name: 'form', class: 'dropdown-link', data: { :'association-insertion-node' => "##{ dom_identifier }", :'association-insertion-method' => 'append' }, render_options: { nested_attribute_name: attribute_name, orderable: orderable, locals: { model: submodel, nested_locals: nested_locals, inset: inset, uncollapsed: uncollapsed, render_partial: render_partial, remote_partial_params: remote_partial_params } } do
|
9
9
|
= submodel.model_name.human
|
@@ -11,7 +11,7 @@
|
|
11
11
|
|
12
12
|
= form.remove_association_button
|
13
13
|
|
14
|
-
.panel-collapse.form-inputs.collapse{ id: form.nested_resource_dom_id, class: ('in' if
|
14
|
+
.panel-collapse.form-inputs.collapse{ id: form.nested_resource_dom_id, class: ('in' if uncollapsed && form.object.persisted?), data: { rendered: render_partial, render_path: @component.path(remote_partial_params), id: form.object.id, :"object-name" => form.object_name, :"model-name" => model.name } }
|
15
15
|
.panel-body{ data: { :"nested-form-container" => true } }
|
16
16
|
- if render_partial
|
17
17
|
= render partial: find_partial_for(model, :fields), locals: { form: form }.merge(nested_locals)
|
@@ -6,5 +6,5 @@
|
|
6
6
|
%ul.dropdown-menu
|
7
7
|
- subclasses.each do |submodel|
|
8
8
|
%li
|
9
|
-
= link_to_add_association form, attribute_name, wrap_object: proc { submodel.new }, partial: find_partial_for(submodel, 'nested_one/container', partial_dir: 'inputs'), form_name: 'form', class: 'dropdown-link', data: { :'association-insertion-node' => "##{ dom_identifier }", :'association-insertion-method' => 'append' }, render_options: { nested_attribute_name: attribute_name, locals: { form: form, model: submodel, subclass: subclass, nested_locals: nested_locals } } do
|
9
|
+
= link_to_add_association form, attribute_name, wrap_object: proc { with_inverse_association_for(submodel.new, attribute_name, form.object) }, partial: find_partial_for(submodel, 'nested_one/container', partial_dir: 'inputs'), form_name: 'form', class: 'dropdown-link', data: { :'association-insertion-node' => "##{ dom_identifier }", :'association-insertion-method' => 'append' }, render_options: { nested_attribute_name: attribute_name, locals: { form: form, model: submodel, subclass: subclass, nested_locals: nested_locals } } do
|
10
10
|
= submodel.model_name.human
|
@@ -27,9 +27,17 @@ Para.config do |config|
|
|
27
27
|
# config.resource_name_methods += [:full_name]
|
28
28
|
|
29
29
|
# Configure the theme navigation bar color not gray but blue darker
|
30
|
-
#
|
30
|
+
#
|
31
31
|
# config.dark_theme = true
|
32
32
|
|
33
|
+
# Do not uncollapse by default "inset" nested fields, which makes all such nested field
|
34
|
+
# blocks get collapsed on page load, and can be easier to read for big blocks.
|
35
|
+
#
|
36
|
+
# This also speeds up page loading when used in conjunction to remote partial loading
|
37
|
+
# for nested fields
|
38
|
+
#
|
39
|
+
# config.uncollapse_inset_nested_fields = false
|
40
|
+
|
33
41
|
# Set if the Para::Breadcrumb::Controller module should be included into the
|
34
42
|
# app, allowing easy breadcrumbs management in the app.
|
35
43
|
#
|
data/lib/para/config.rb
CHANGED
@@ -42,6 +42,9 @@ module Para
|
|
42
42
|
mattr_accessor :dark_theme
|
43
43
|
@@dark_theme = false
|
44
44
|
|
45
|
+
mattr_accessor :uncollapse_inset_nested_fields
|
46
|
+
@@uncollapse_inset_nested_fields = true
|
47
|
+
|
45
48
|
# Allows changing default cache store used by Para to store jobs through
|
46
49
|
# the ActiveJob::Status gem
|
47
50
|
#
|
@@ -27,7 +27,9 @@ module Para
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def actions_buttons_for(options)
|
30
|
-
names = [:submit, :submit_and_edit
|
30
|
+
names = [:submit, :submit_and_edit]
|
31
|
+
names << :submit_and_add_another if template.can?(:create, object.class)
|
32
|
+
names << :cancel
|
31
33
|
|
32
34
|
names.select! { |name| Array.wrap(options[:only]).include?(name) } if options[:only]
|
33
35
|
names.reject! { |name| Array.wrap(options[:except]).include?(name) } if options[:except]
|
@@ -32,6 +32,7 @@ module Para
|
|
32
32
|
add_button_label: add_button_label,
|
33
33
|
add_button_class: add_button_class,
|
34
34
|
inset: inset?,
|
35
|
+
uncollapsed: uncollapsed?,
|
35
36
|
render_partial: render_partial?,
|
36
37
|
remote_partial_params: remote_partial_params
|
37
38
|
}
|
@@ -50,9 +51,13 @@ module Para
|
|
50
51
|
def inset?
|
51
52
|
options.fetch(:inset, false)
|
52
53
|
end
|
53
|
-
|
54
|
+
|
55
|
+
def uncollapsed?
|
56
|
+
inset? && Para.config.uncollapse_inset_nested_fields
|
57
|
+
end
|
58
|
+
|
54
59
|
def render_partial?
|
55
|
-
options[:render_partial] || object.errors.any? || (object.persisted? &&
|
60
|
+
options[:render_partial] || object.errors.any? || (object.persisted? && uncollapsed?)
|
56
61
|
end
|
57
62
|
|
58
63
|
def remote_partial_params
|
@@ -2,14 +2,14 @@ module Para
|
|
2
2
|
module Markup
|
3
3
|
class ResourcesButtons < Para::Markup::Component
|
4
4
|
attr_reader :component
|
5
|
-
|
5
|
+
|
6
6
|
def initialize(component, view)
|
7
7
|
@component = component
|
8
8
|
super(view)
|
9
9
|
end
|
10
10
|
|
11
11
|
def clone_button(resource)
|
12
|
-
return unless resource.class.cloneable?
|
12
|
+
return unless resource.class.cloneable? && view.can?(:clone, resource)
|
13
13
|
|
14
14
|
path = component.relation_path(
|
15
15
|
resource, action: :clone, return_to: view.request.fullpath
|
@@ -29,6 +29,8 @@ module Para
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def edit_button(resource)
|
32
|
+
return unless view.can?(:edit, resource)
|
33
|
+
|
32
34
|
path = component.relation_path(
|
33
35
|
resource, action: :edit, return_to: view.request.fullpath
|
34
36
|
)
|
@@ -39,6 +41,8 @@ module Para
|
|
39
41
|
end
|
40
42
|
|
41
43
|
def delete_button(resource)
|
44
|
+
return unless view.can?(:delete, resource)
|
45
|
+
|
42
46
|
path = component.relation_path(resource, return_to: view.request.fullpath)
|
43
47
|
|
44
48
|
options = {
|
@@ -5,7 +5,7 @@ module Para
|
|
5
5
|
self.default_actions = [:edit, :clone, :delete]
|
6
6
|
|
7
7
|
attr_reader :component, :model, :orderable, :actions
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(component, view)
|
10
10
|
@component = component
|
11
11
|
super(view)
|
@@ -15,7 +15,7 @@ module Para
|
|
15
15
|
@model = options.delete(:model)
|
16
16
|
|
17
17
|
if !options.key?(:orderable) || options.delete(:orderable)
|
18
|
-
@orderable = model.orderable?
|
18
|
+
@orderable = model.orderable? && view.can?(:order, model)
|
19
19
|
end
|
20
20
|
|
21
21
|
@actions = build_actions(options.delete(:actions))
|
@@ -93,7 +93,7 @@ module Para
|
|
93
93
|
|
94
94
|
content_tag(:th, options) do
|
95
95
|
if (sort = options.delete(:sort))
|
96
|
-
|
96
|
+
view.sort_link(search, *sort, label, hide_indicator: true)
|
97
97
|
elsif searchable?(field_name)
|
98
98
|
view.sort_link(search, field_name, label, hide_indicator: true)
|
99
99
|
else
|