rails_admin_sort_embedded 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/rails_admin/jquery.mjs.nestedSortable.js +639 -0
- data/app/assets/javascripts/rails_admin/rails_admin_sort_embedded.js.coffee +71 -0
- data/app/assets/stylesheets/rails_admin/rails_admin_sort_embedded.css.scss +199 -0
- data/app/views/rails_admin/main/sort_embedded.html.haml +15 -0
- data/config/locales/en.nested_set.yml +9 -0
- data/config/locales/es.nested_set.yml +9 -0
- data/config/locales/ru.nested_set.yml +9 -0
- data/lib/rails_admin_sort_embedded.rb +14 -0
- data/lib/rails_admin_sort_embedded/action.rb +83 -0
- data/lib/rails_admin_sort_embedded/configuration.rb +21 -0
- data/lib/rails_admin_sort_embedded/engine.rb +12 -0
- data/lib/rails_admin_sort_embedded/helper.rb +118 -0
- data/lib/rails_admin_sort_embedded/model.rb +9 -0
- data/lib/rails_admin_sort_embedded/version.rb +3 -0
- data/rails_admin_sort_embedded.gemspec +21 -0
- metadata +81 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
#= require rails_admin/jquery.mjs.nestedSortable
|
2
|
+
|
3
|
+
show_flash = (data)->
|
4
|
+
$flash = $('<div>')
|
5
|
+
.addClass('nestable-flash alert')
|
6
|
+
.append($('<button>').addClass('close').data('dismiss', 'alert').html('×'))
|
7
|
+
.append($('<span>').addClass('body').html(data))
|
8
|
+
$('#rails_admin_nestable').append($flash)
|
9
|
+
$flash.fadeIn(200).delay(2000).fadeOut 200, -> $(this).remove()
|
10
|
+
|
11
|
+
js_tree_toggle = (e)->
|
12
|
+
e.preventDefault()
|
13
|
+
e.stopPropagation()
|
14
|
+
$t = $(this)
|
15
|
+
$t.html "<i class=\"fa fa-spinner fa-spin\"></i>"
|
16
|
+
$.ajax
|
17
|
+
type: "POST"
|
18
|
+
url: $t.attr("href")
|
19
|
+
data:
|
20
|
+
ajax: true
|
21
|
+
success: (r) ->
|
22
|
+
$t.attr "href", r.href
|
23
|
+
$t.attr "class", r.class + ' js-tree-toggle'
|
24
|
+
$t.text r.text
|
25
|
+
$t.parent().attr "title", r.text
|
26
|
+
return
|
27
|
+
error: (e) ->
|
28
|
+
alert e.responseText
|
29
|
+
return
|
30
|
+
|
31
|
+
init = ->
|
32
|
+
$('.rails_admin_sort_embedded').each ->
|
33
|
+
$t = $(this)
|
34
|
+
tree_config = $t.data('config')
|
35
|
+
$t.nestedSortable
|
36
|
+
handle: '.dd-handle',
|
37
|
+
items: ".dd-item"
|
38
|
+
maxLevels: 1 #tree_config["max_depth"]
|
39
|
+
placeholder: "dd-placeholder"
|
40
|
+
tolerance: 'pointer',
|
41
|
+
toleranceElement: '> div',
|
42
|
+
|
43
|
+
update: (event, ui) ->
|
44
|
+
|
45
|
+
ids_array = []
|
46
|
+
ui.item.closest("ol").find("li").each ->
|
47
|
+
ids_array.push $(this).data("id")
|
48
|
+
|
49
|
+
$.ajax
|
50
|
+
type: "POST"
|
51
|
+
dataType: "html"
|
52
|
+
url: tree_config["update_url"]
|
53
|
+
data:
|
54
|
+
#item_id: ui.item.data("id")
|
55
|
+
embedded_model: tree_config["embedded_model"]
|
56
|
+
embedded_field: tree_config["embedded_field"]
|
57
|
+
#parent_id: ui.item.parent().parent().data("id")
|
58
|
+
#prev_id: ui.item.prev().data("id")
|
59
|
+
#next_id: ui.item.next().data("id")
|
60
|
+
ids_array: ids_array.join(" ")
|
61
|
+
|
62
|
+
error: (xhr, status, error) ->
|
63
|
+
show_flash('Nested Set: fatal error')
|
64
|
+
|
65
|
+
success: (data) ->
|
66
|
+
show_flash(data)
|
67
|
+
|
68
|
+
$(document).off('pjax:end.rails_admin_sort_embedded').on('pjax:end.rails_admin_sort_embedded', init)
|
69
|
+
$(document).off('ready.rails_admin_sort_embedded').on('ready.rails_admin_sort_embedded', init)
|
70
|
+
$(document).on('click', '.js-tree-toggle', js_tree_toggle)
|
71
|
+
|
@@ -0,0 +1,199 @@
|
|
1
|
+
/**
|
2
|
+
* jQuery Nestable
|
3
|
+
*/
|
4
|
+
|
5
|
+
.dd {
|
6
|
+
position: relative;
|
7
|
+
display: block;
|
8
|
+
margin: 0;
|
9
|
+
padding: 0;
|
10
|
+
/* max-width: 600px; */
|
11
|
+
list-style: none;
|
12
|
+
font-size: 13px;
|
13
|
+
line-height: 20px;
|
14
|
+
}
|
15
|
+
|
16
|
+
.dd-list, .dd-list ol {
|
17
|
+
display: block;
|
18
|
+
position: relative;
|
19
|
+
margin: 0;
|
20
|
+
padding: 0;
|
21
|
+
list-style: none;
|
22
|
+
}
|
23
|
+
|
24
|
+
.dd-list ol {
|
25
|
+
padding-left: 30px;
|
26
|
+
}
|
27
|
+
|
28
|
+
.dd-item, .dd-placeholder {
|
29
|
+
display: block;
|
30
|
+
position: relative;
|
31
|
+
margin: 0;
|
32
|
+
padding: 0;
|
33
|
+
min-height: 20px;
|
34
|
+
font-size: 13px;
|
35
|
+
line-height: 20px;
|
36
|
+
}
|
37
|
+
|
38
|
+
.dd-item .label {
|
39
|
+
margin-right: 10px;
|
40
|
+
}
|
41
|
+
|
42
|
+
.dd-item img {
|
43
|
+
display: inline-block;
|
44
|
+
margin-left: 10px;
|
45
|
+
}
|
46
|
+
|
47
|
+
.dd-item > div {
|
48
|
+
// position: relative
|
49
|
+
}
|
50
|
+
|
51
|
+
.dd-handle {
|
52
|
+
display: block;
|
53
|
+
position: absolute;
|
54
|
+
top: 0;
|
55
|
+
left: 0;
|
56
|
+
bottom: 0;
|
57
|
+
width: 30px;
|
58
|
+
padding: 5px 10px;
|
59
|
+
color: #333;
|
60
|
+
text-decoration: none;
|
61
|
+
font-weight: bold;
|
62
|
+
border: 1px solid #ccc;
|
63
|
+
background: #fafafa;
|
64
|
+
background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
|
65
|
+
background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
|
66
|
+
background: linear-gradient(top, #fafafa 0%, #eee 100%);
|
67
|
+
-webkit-border-radius: 3px;
|
68
|
+
border-radius: 3px;
|
69
|
+
box-sizing: border-box;
|
70
|
+
-moz-box-sizing: border-box;
|
71
|
+
cursor: move;
|
72
|
+
}
|
73
|
+
|
74
|
+
.dd-handle:hover {
|
75
|
+
color: #2ea8e5;
|
76
|
+
background: #fff;
|
77
|
+
}
|
78
|
+
|
79
|
+
.dd-item {
|
80
|
+
margin-top: 5px
|
81
|
+
}
|
82
|
+
|
83
|
+
.dd-placeholder {
|
84
|
+
margin: 5px 0;
|
85
|
+
padding: 0;
|
86
|
+
min-height: 30px;
|
87
|
+
background: #f2fbff;
|
88
|
+
border: 1px dashed #b6bcbf;
|
89
|
+
&.mjs-nestedSortable-error {
|
90
|
+
border: 1px solid red;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
.dd3-content {
|
95
|
+
display: block;
|
96
|
+
min-height: 30px;
|
97
|
+
padding: 5px 10px 5px 40px;
|
98
|
+
color: #333;
|
99
|
+
text-decoration: none;
|
100
|
+
font-weight: bold;
|
101
|
+
border: 1px solid #ccc;
|
102
|
+
background: #fafafa;
|
103
|
+
background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
|
104
|
+
background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
|
105
|
+
background: linear-gradient(top, #fafafa 0%, #eee 100%);
|
106
|
+
-webkit-border-radius: 3px;
|
107
|
+
border-radius: 3px;
|
108
|
+
box-sizing: border-box;
|
109
|
+
-moz-box-sizing: border-box;
|
110
|
+
}
|
111
|
+
|
112
|
+
.dd3-content:hover {
|
113
|
+
color: #2ea8e5;
|
114
|
+
background: #fff;
|
115
|
+
}
|
116
|
+
|
117
|
+
.dd-dragel > .dd3-item > .dd3-content {
|
118
|
+
margin: 0;
|
119
|
+
}
|
120
|
+
|
121
|
+
.dd3-item > button {
|
122
|
+
margin-left: 30px;
|
123
|
+
}
|
124
|
+
|
125
|
+
.dd3-handle {
|
126
|
+
position: absolute;
|
127
|
+
margin: 0;
|
128
|
+
left: 0;
|
129
|
+
top: 0;
|
130
|
+
cursor: pointer;
|
131
|
+
width: 30px;
|
132
|
+
text-indent: 100px;
|
133
|
+
white-space: nowrap;
|
134
|
+
overflow: hidden;
|
135
|
+
border: 1px solid #aaa;
|
136
|
+
background: #ddd;
|
137
|
+
background: -webkit-linear-gradient(top, #ddd 0%, #bbb 100%);
|
138
|
+
background: -moz-linear-gradient(top, #ddd 0%, #bbb 100%);
|
139
|
+
background: linear-gradient(top, #ddd 0%, #bbb 100%);
|
140
|
+
border-top-right-radius: 0;
|
141
|
+
border-bottom-right-radius: 0;
|
142
|
+
}
|
143
|
+
|
144
|
+
.dd3-handle:before {
|
145
|
+
content: '≡';
|
146
|
+
display: block;
|
147
|
+
position: absolute;
|
148
|
+
left: 0;
|
149
|
+
top: 3px;
|
150
|
+
width: 100%;
|
151
|
+
text-align: center;
|
152
|
+
text-indent: 0;
|
153
|
+
color: #fff;
|
154
|
+
font-size: 20px;
|
155
|
+
font-weight: normal;
|
156
|
+
}
|
157
|
+
|
158
|
+
.dd3-handle:hover {
|
159
|
+
background: #ddd;
|
160
|
+
}
|
161
|
+
|
162
|
+
.dd3-content .links {
|
163
|
+
width: auto;
|
164
|
+
}
|
165
|
+
|
166
|
+
.dd3-content ul.inline.actions {
|
167
|
+
margin: 0;
|
168
|
+
padding: 0;
|
169
|
+
list-style: none;
|
170
|
+
display: block
|
171
|
+
}
|
172
|
+
|
173
|
+
.dd3-content ul.inline.actions li {
|
174
|
+
display: inline-block
|
175
|
+
}
|
176
|
+
|
177
|
+
.dd3-content ul.inline.actions li a {
|
178
|
+
width: 14px;
|
179
|
+
height: 16px;
|
180
|
+
text-decoration: none;
|
181
|
+
}
|
182
|
+
|
183
|
+
.dd3-content ul.inline.actions li a:hover {
|
184
|
+
text-decoration: none;
|
185
|
+
}
|
186
|
+
|
187
|
+
#rails_admin_nestable {
|
188
|
+
position: relative;
|
189
|
+
margin-bottom: 100px;
|
190
|
+
}
|
191
|
+
|
192
|
+
.nestable-flash {
|
193
|
+
position: fixed;
|
194
|
+
top: 80px;
|
195
|
+
width: auto;
|
196
|
+
right: 20px;
|
197
|
+
display: none;
|
198
|
+
min-width: 150px;
|
199
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
= stylesheet_link_tag 'rails_admin/rails_admin_sort_embedded'
|
2
|
+
= javascript_include_tag 'rails_admin/rails_admin_sort_embedded'
|
3
|
+
|
4
|
+
.controls
|
5
|
+
%ul.nav.nav-tabs
|
6
|
+
- sort_embedded_fields.each do |f|
|
7
|
+
%li
|
8
|
+
= link_to I18n.t("rails_admin.sort_embedded.#{@object.class.name.tableize}.#{f}"), "#sort_embedded_#{f}", title: f, "data-toggle" => "tab"
|
9
|
+
|
10
|
+
.tab-content
|
11
|
+
- sort_embedded_fields.each do |f|
|
12
|
+
.tab-pane{id: "sort_embedded_#{f}"}
|
13
|
+
.row-fluid
|
14
|
+
.span12#rails_admin_nestable
|
15
|
+
= rails_admin_sort_embedded @object.send(f).to_a, {embedded_field: f}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RailsAdminSortEmbedded
|
2
|
+
|
3
|
+
end
|
4
|
+
|
5
|
+
require "rails_admin_sort_embedded/version"
|
6
|
+
require 'rails_admin_sort_embedded/engine'
|
7
|
+
|
8
|
+
require 'rails_admin/config/actions'
|
9
|
+
require 'rails_admin/config/model'
|
10
|
+
|
11
|
+
require 'rails_admin_sort_embedded/configuration'
|
12
|
+
require 'rails_admin_sort_embedded/action'
|
13
|
+
require 'rails_admin_sort_embedded/model'
|
14
|
+
require 'rails_admin_sort_embedded/helper'
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module RailsAdmin
|
2
|
+
module Config
|
3
|
+
module Actions
|
4
|
+
class SortEmbedded < Base
|
5
|
+
RailsAdmin::Config::Actions.register(self)
|
6
|
+
|
7
|
+
# Is the action acting on the root level (Example: /admin/contact)
|
8
|
+
register_instance_option :root? do
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
register_instance_option :collection? do
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
# Is the action on an object scope (Example: /admin/team/1/edit)
|
17
|
+
register_instance_option :member? do
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
register_instance_option :route_fragment do
|
22
|
+
'sort_embedded'
|
23
|
+
end
|
24
|
+
|
25
|
+
register_instance_option :controller do
|
26
|
+
Proc.new do |klass|
|
27
|
+
@sort_conf = ::RailsAdminSortEmbedded::Configuration.new @abstract_model
|
28
|
+
|
29
|
+
if params['id'].present?
|
30
|
+
if request.get?
|
31
|
+
# @nodes = list_entries(@model_config, :index, nil, nil).sort { |a,b| a.lft <=> b.lft }
|
32
|
+
render action: @action.template_name
|
33
|
+
|
34
|
+
elsif request.post?
|
35
|
+
begin
|
36
|
+
ids = params[:ids_array].to_s.split(" ")
|
37
|
+
embedded_model = params[:embedded_model].to_s
|
38
|
+
embedded_field = params[:embedded_field].to_s
|
39
|
+
# item_id = params[:item_id].to_s
|
40
|
+
# parent_id = params[:parent_id].to_s
|
41
|
+
# prev_id = params[:prev_id].to_s
|
42
|
+
# next_id = params[:next_id].to_s
|
43
|
+
|
44
|
+
if ids.empty?
|
45
|
+
return render text: 'Nested set UI error: node ids not defined', status: 500
|
46
|
+
end
|
47
|
+
|
48
|
+
main_obj = @object
|
49
|
+
embedded = main_obj.send(embedded_field)
|
50
|
+
ids.each_with_index do |id, i|
|
51
|
+
embedded.find(id).update(order: i)
|
52
|
+
end
|
53
|
+
|
54
|
+
message = "<strong>#{I18n.t('admin.actions.sort_embedded.success')}!</strong>"
|
55
|
+
rescue Exception => e
|
56
|
+
|
57
|
+
|
58
|
+
main_obj = @object
|
59
|
+
embedded = main_obj.send(embedded_field).sorted
|
60
|
+
ids.each_with_index do |id, i|
|
61
|
+
embedded.find(id).update(order: i)
|
62
|
+
end
|
63
|
+
|
64
|
+
message = "<strong>#{I18n.t('admin.actions.sort_embedded.error')}</strong>: #{e}"
|
65
|
+
end
|
66
|
+
|
67
|
+
render text: message
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
register_instance_option :link_icon do
|
74
|
+
'icon-move'
|
75
|
+
end
|
76
|
+
|
77
|
+
register_instance_option :http_methods do
|
78
|
+
[:get, :post]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RailsAdminSortEmbedded
|
2
|
+
class Configuration
|
3
|
+
def initialize(abstract_model)
|
4
|
+
@abstract_model = abstract_model
|
5
|
+
end
|
6
|
+
|
7
|
+
def options
|
8
|
+
@options ||= {
|
9
|
+
fields: [],
|
10
|
+
thumbnail_fields: [:image, :cover],
|
11
|
+
thumbnail_size: :thumb,
|
12
|
+
thumbnail_gem: :paperclip,
|
13
|
+
}.merge(config || {})
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
def config
|
18
|
+
::RailsAdmin::Config.model(@abstract_model.model).sort_embedded || {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module RailsAdminSortEmbedded
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
|
4
|
+
initializer "RailsAdminSortEmbedded precompile hook", group: :all do |app|
|
5
|
+
app.config.assets.precompile += %w(rails_admin/rails_admin_sort_embedded.js rails_admin/rails_admin_sort_embedded.css)
|
6
|
+
end
|
7
|
+
|
8
|
+
initializer 'Include RailsAdminSortEmbedded::Helper' do |app|
|
9
|
+
ActionView::Base.send :include, RailsAdminSortEmbedded::Helper
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|