admin_widgets 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/admin_widgets.gemspec +26 -0
- data/lib/admin_widgets.rb +8 -0
- data/lib/admin_widgets/base_form_widget.rb +65 -0
- data/lib/admin_widgets/base_widget.rb +33 -0
- data/lib/admin_widgets/delegation.rb +20 -0
- data/lib/admin_widgets/engine.rb +22 -0
- data/lib/admin_widgets/extensions/action_view_ext.rb +14 -0
- data/lib/admin_widgets/extensions/simple_form_ext.rb +16 -0
- data/lib/admin_widgets/filters_widget.rb +59 -0
- data/lib/admin_widgets/form/fieldset_widget.rb +102 -0
- data/lib/admin_widgets/form_widget.rb +24 -0
- data/lib/admin_widgets/helper.rb +11 -0
- data/lib/admin_widgets/list_widget.rb +210 -0
- data/lib/admin_widgets/memoization.rb +15 -0
- data/lib/admin_widgets/scopes_widget.rb +62 -0
- data/lib/admin_widgets/show/fieldset_widget.rb +27 -0
- data/lib/admin_widgets/show_widget.rb +35 -0
- data/lib/admin_widgets/version.rb +3 -0
- data/lib/admin_widgets/view_proxy.rb +49 -0
- data/lib/admin_widgets/wizard_widget.rb +79 -0
- metadata +80 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "admin_widgets/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "admin_widgets"
|
7
|
+
s.version = AdminWidgets::VERSION
|
8
|
+
s.authors = ["DanX~"]
|
9
|
+
s.email = ["danx.exe@gmail.com"]
|
10
|
+
s.homepage = "http://www.mobvox.com.br"
|
11
|
+
s.summary = "Awesome Admin Widgets"
|
12
|
+
s.description = "Super Awesome Admin Widgets"
|
13
|
+
|
14
|
+
s.rubyforge_project = "admin_widgets"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
|
25
|
+
s.add_runtime_dependency "apotomo"
|
26
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class BaseFormWidget < BaseWidget
|
3
|
+
|
4
|
+
attr_reader :form
|
5
|
+
|
6
|
+
# TODO: WTF? Who the hell is overriding the 'form' method?
|
7
|
+
def builder
|
8
|
+
form
|
9
|
+
end
|
10
|
+
|
11
|
+
memoize :resource_class, proc { helper.resource_class }
|
12
|
+
memoize :resource, proc { helper.resource }
|
13
|
+
memoize :params, proc { helper.params }
|
14
|
+
memoize :as, nil
|
15
|
+
memoize :url, proc { '' }
|
16
|
+
memoize :cancel_url, proc { helper.respond_to?(:collection_path) ? helper.collection_path : false }
|
17
|
+
memoize :css_class, 'form'
|
18
|
+
|
19
|
+
delegate :object, :to => :form
|
20
|
+
|
21
|
+
def content
|
22
|
+
rawtext around_form
|
23
|
+
end
|
24
|
+
|
25
|
+
def around_form
|
26
|
+
form_method = @form ? :simple_fields_for : :simple_form_for
|
27
|
+
|
28
|
+
helper.send(form_method, resource, :url => url, :as => as, :class => css_class, :html => @html) do |f|
|
29
|
+
@form = f # IMPORTANT!
|
30
|
+
|
31
|
+
capture do
|
32
|
+
form_content
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def form_content
|
38
|
+
content_block
|
39
|
+
buttons
|
40
|
+
end
|
41
|
+
|
42
|
+
# FIXME: Beware, arcane magic below. We are polluting the root widget with a instance variable, is there a better way to do this?
|
43
|
+
def autofocus_done?
|
44
|
+
root.instance_variable_get(:@autofocus_done)
|
45
|
+
end
|
46
|
+
def autofocus!(options = {})
|
47
|
+
root.instance_variable_set(:@autofocus_done, true)
|
48
|
+
{ autofocus: true }.merge(options)
|
49
|
+
end
|
50
|
+
|
51
|
+
# DSL methods
|
52
|
+
|
53
|
+
def field(name, options = {})
|
54
|
+
options = autofocus!(options) unless autofocus_done?
|
55
|
+
|
56
|
+
condition = options[:if]
|
57
|
+
return if (condition.present? && !resource.instance_eval(&condition))
|
58
|
+
|
59
|
+
form = (options[:nested] === false && self.respond_to?(:parent)) ? parent.form : self.form
|
60
|
+
|
61
|
+
rawtext form.input(name, options)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class BaseWidget < Erector::Widget
|
3
|
+
extend AdminWidgets::Memoization
|
4
|
+
extend AdminWidgets::Delegation
|
5
|
+
|
6
|
+
# Outputs to a new buffer
|
7
|
+
def capture(&block)
|
8
|
+
original, @_output = output, Erector::Output.new
|
9
|
+
instance_eval &block
|
10
|
+
original.widgets.concat(output.widgets)
|
11
|
+
output.to_s
|
12
|
+
ensure
|
13
|
+
@_output = original
|
14
|
+
end
|
15
|
+
|
16
|
+
def root
|
17
|
+
parent and parent.root or self
|
18
|
+
end
|
19
|
+
|
20
|
+
def helper
|
21
|
+
@controller.view_context
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method_name, *args, &block)
|
25
|
+
rawtext helper.send(method_name, *args, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def content_block
|
29
|
+
instance_eval &@block if @block
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
module Delegation
|
3
|
+
def delegate(*methods)
|
4
|
+
options = methods.last
|
5
|
+
keys = methods[0..-2]
|
6
|
+
|
7
|
+
if options.is_a?(Hash) and options[:hash]
|
8
|
+
keys.each do |key|
|
9
|
+
define_method key do
|
10
|
+
hash = self.send(options[:to])
|
11
|
+
hash.send(:[], key)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
else
|
15
|
+
super *methods
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class Engine < Rails::Engine
|
3
|
+
config.to_prepare do
|
4
|
+
require_dependency 'admin_widgets/helper'
|
5
|
+
require_dependency 'admin_widgets/memoization'
|
6
|
+
require_dependency 'admin_widgets/delegation'
|
7
|
+
require_dependency 'admin_widgets/base_widget'
|
8
|
+
require_dependency 'admin_widgets/base_form_widget'
|
9
|
+
require_dependency 'admin_widgets/form_widget'
|
10
|
+
require_dependency 'admin_widgets/form/fieldset_widget'
|
11
|
+
require_dependency 'admin_widgets/wizard_widget'
|
12
|
+
require_dependency 'admin_widgets/list_widget'
|
13
|
+
require_dependency 'admin_widgets/scopes_widget'
|
14
|
+
require_dependency 'admin_widgets/filters_widget'
|
15
|
+
require_dependency 'admin_widgets/show_widget'
|
16
|
+
require_dependency 'admin_widgets/show/fieldset_widget'
|
17
|
+
|
18
|
+
ActionView::Base.send :include, AdminWidgets::Helper
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActionView::Helpers::FormOptionsHelper
|
2
|
+
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
|
3
|
+
options = collection.map do |element|
|
4
|
+
option_html_attributes = (Array === element && element.size == 3 ? element.last : nil)
|
5
|
+
[element.send(text_method), element.send(value_method), option_html_attributes].compact
|
6
|
+
end
|
7
|
+
selected, disabled = extract_selected_and_disabled(selected)
|
8
|
+
select_deselect = {}
|
9
|
+
select_deselect[:selected] = extract_values_from_collection(collection, value_method, selected)
|
10
|
+
select_deselect[:disabled] = extract_values_from_collection(collection, value_method, disabled)
|
11
|
+
|
12
|
+
options_for_select(options, select_deselect)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class SimpleForm::Inputs::CollectionInput
|
2
|
+
def detect_common_display_methods
|
3
|
+
collection_classes = detect_collection_classes
|
4
|
+
|
5
|
+
if collection_classes.include?(Array)
|
6
|
+
{ :label => :first, :value => :second }
|
7
|
+
elsif collection_includes_basic_objects?(collection_classes)
|
8
|
+
{ :label => :to_s, :value => :to_s }
|
9
|
+
else
|
10
|
+
sample = collection.first || collection.last
|
11
|
+
|
12
|
+
{ :label => SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
|
13
|
+
:value => SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class FiltersWidget < BaseFormWidget
|
3
|
+
|
4
|
+
memoize :resource, nil
|
5
|
+
memoize :as, 'search'
|
6
|
+
memoize :resource, 'search'
|
7
|
+
memoize :url, proc { helper.search_resources_path }
|
8
|
+
memoize :method, 'post'
|
9
|
+
|
10
|
+
def form_content
|
11
|
+
hidden_fields
|
12
|
+
content_block
|
13
|
+
buttons
|
14
|
+
end
|
15
|
+
|
16
|
+
# DSL methods
|
17
|
+
|
18
|
+
def field(name, operator, options = {})
|
19
|
+
field_name = "#{name}_#{operator}"
|
20
|
+
field_label = resource_class.human_attribute_name(name)
|
21
|
+
field_value = params[as][field_name] rescue ''
|
22
|
+
|
23
|
+
multiple = [:in, :all].include?(operator.to_sym)
|
24
|
+
field_value = field_value.to_s.split(',') if multiple && field_value && !field_value.is_a?(Array)
|
25
|
+
|
26
|
+
if options[:collection].present?
|
27
|
+
value = nil
|
28
|
+
selected = field_value
|
29
|
+
include_blank = true
|
30
|
+
else
|
31
|
+
value = field_value
|
32
|
+
selected = nil
|
33
|
+
include_blank = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
default_options = { :required => false, :label => field_label, :input_html => { :value => value, :multiple => multiple }, :selected => selected, :include_blank => include_blank, :hidden_field => false }
|
37
|
+
rawtext form.input(field_name, default_options.merge(options))
|
38
|
+
end
|
39
|
+
|
40
|
+
def hidden_fields
|
41
|
+
div :class => ['hidden-fields'] do
|
42
|
+
%w[scope sort_field sort_direction].each do |hidden_field|
|
43
|
+
rawtext form.input(hidden_field, :input_html => { :name => hidden_field, :value => params[hidden_field] }, :as => :hidden)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def buttons
|
49
|
+
div :class => ['group', 'navform', 'wat-cf'] do
|
50
|
+
rawtext helper.button_tag(:class => 'button', :name => 'filter', :value => as) {
|
51
|
+
helper.image_tag('search-button.png') +
|
52
|
+
helper.t('resources.actions.filter')
|
53
|
+
}
|
54
|
+
rawtext helper.link_to(helper.t('resources.actions.reset_filter'), cancel_url, :class => "text_button_padding link_button") if cancel_url
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module AdminWidgets::Form
|
2
|
+
class FieldsetWidget < AdminWidgets::FormWidget
|
3
|
+
|
4
|
+
attr_reader :parent
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def resource
|
8
|
+
form.object
|
9
|
+
end
|
10
|
+
|
11
|
+
delegate :helper, :to => :parent
|
12
|
+
|
13
|
+
memoize :repeatable, false
|
14
|
+
memoize :nested, true
|
15
|
+
memoize :legend, proc { resource_class.human_attribute_name(name) }
|
16
|
+
memoize :wrapper_html, {}
|
17
|
+
|
18
|
+
def visible
|
19
|
+
return @visible = true if @visible.nil?
|
20
|
+
return @visible = @visible.call(resource) if @visible.respond_to? :call
|
21
|
+
@visible
|
22
|
+
end
|
23
|
+
|
24
|
+
def content
|
25
|
+
return simple_fieldset unless nested
|
26
|
+
|
27
|
+
around_form do
|
28
|
+
around_repeatable_collection do
|
29
|
+
parent.form.simple_fields_for(name) do |form|
|
30
|
+
@form = form # IMPORTANT!
|
31
|
+
|
32
|
+
around_repeatable_group do
|
33
|
+
content_block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def before
|
41
|
+
@before = capture do
|
42
|
+
div :class => 'before' do
|
43
|
+
yield
|
44
|
+
end
|
45
|
+
end
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def after
|
50
|
+
@after = capture do
|
51
|
+
div :class => 'after' do
|
52
|
+
yield
|
53
|
+
end
|
54
|
+
end
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def simple_fieldset
|
59
|
+
@form = parent.form
|
60
|
+
around_form do
|
61
|
+
content_block
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def around_form
|
66
|
+
wrapper_attrs = { :class => 'fieldset-wrapper' }
|
67
|
+
wrapper_attrs.merge! :style => 'display: none' unless visible
|
68
|
+
wrapper_attrs.merge! wrapper_html
|
69
|
+
|
70
|
+
div wrapper_attrs do
|
71
|
+
if legend
|
72
|
+
element('fieldset') do
|
73
|
+
element('legend', legend)
|
74
|
+
inside = capture { yield }
|
75
|
+
rawtext @before
|
76
|
+
rawtext inside
|
77
|
+
rawtext @after
|
78
|
+
end
|
79
|
+
else
|
80
|
+
yield
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def around_repeatable_collection
|
86
|
+
return yield unless repeatable
|
87
|
+
|
88
|
+
div :class => 'repeatable-collection', :'data-repeatable' => repeatable do
|
89
|
+
yield
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def around_repeatable_group
|
94
|
+
return yield unless repeatable
|
95
|
+
|
96
|
+
div :class => ['repeatable-group', form.object.class.name.underscore], :id => form.object._id do
|
97
|
+
yield
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class FormWidget < BaseFormWidget
|
3
|
+
|
4
|
+
memoize :url, proc { helper.respond_to?(:resource_or_collection_path) ? helper.resource_or_collection_path : '' }
|
5
|
+
memoize :save_button, true
|
6
|
+
|
7
|
+
# DSL methods
|
8
|
+
|
9
|
+
def fieldset(name, options = {}, &block)
|
10
|
+
widget AdminWidgets::Form::FieldsetWidget.new(options.merge(:name => name, :parent => self, :block => block))
|
11
|
+
end
|
12
|
+
|
13
|
+
def buttons
|
14
|
+
div :class => ['group', 'navform', 'wat-cf'] do
|
15
|
+
rawtext helper.button_tag(:class => 'button') {
|
16
|
+
helper.image_tag('icons/tick.png') +
|
17
|
+
helper.t('resources.actions.save')
|
18
|
+
} if save_button
|
19
|
+
rawtext helper.link_to(helper.t('resources.actions.cancel'), cancel_url, :class => "text_button_padding link_button") if cancel_url
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
module Helper
|
3
|
+
|
4
|
+
def admin_widget(name, options = {}, &block)
|
5
|
+
widget_class = "::AdminWidgets::#{name.to_s.camelize}Widget".constantize
|
6
|
+
options = options.merge(:controller => controller, :block => block)
|
7
|
+
widget_class.new(options).to_html
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class ListWidget < BaseWidget
|
3
|
+
|
4
|
+
attr_accessor :table_class
|
5
|
+
attr_reader :fields
|
6
|
+
|
7
|
+
# Options
|
8
|
+
|
9
|
+
memoize :table_id, proc { "#{resource_name}-table" }
|
10
|
+
memoize :table_class, 'table'
|
11
|
+
memoize :th_class, 'table-column'
|
12
|
+
memoize :pagination_class, ['actions-bar', 'wat-cf']
|
13
|
+
memoize :row_class, ActionView::Helpers::TextHelper::Cycle.new(:odd, :even)
|
14
|
+
memoize :resource_class, proc { helper.resource_class }
|
15
|
+
memoize :resource_name, proc { resource_class.name.underscore.pluralize }
|
16
|
+
memoize :params, proc { helper.params }
|
17
|
+
|
18
|
+
memoize :row_actions, true
|
19
|
+
|
20
|
+
memoize :page_param, :page
|
21
|
+
memoize :per_page_param, :per_page
|
22
|
+
memoize :page, proc { params[page_param].presence || 1 }
|
23
|
+
memoize :per_page, proc { params[per_page_param].presence || 25 }
|
24
|
+
|
25
|
+
memoize :column_sorting, true
|
26
|
+
memoize :sort_field_param, :sort_field
|
27
|
+
memoize :sort_direction_param, :sort_direction
|
28
|
+
memoize :sort_field, proc { params[sort_field_param].presence || :created_at }
|
29
|
+
memoize :sort_direction, proc { params[sort_direction_param].presence || :desc }
|
30
|
+
|
31
|
+
memoize :search_param, :search
|
32
|
+
memoize :search, proc { params[search_param] }
|
33
|
+
|
34
|
+
memoize :collection, proc { helper.collection }
|
35
|
+
memoize :scoped_collection, proc { collection.send(sort_direction, sort_field).page(page).per(per_page) }
|
36
|
+
|
37
|
+
|
38
|
+
delegate :edit_resource_path, :to => :helper
|
39
|
+
delegate :resource_path, :to => :helper
|
40
|
+
|
41
|
+
def initialize(*attrs)
|
42
|
+
super(*attrs)
|
43
|
+
|
44
|
+
@fields = []
|
45
|
+
end
|
46
|
+
|
47
|
+
def content
|
48
|
+
content_block
|
49
|
+
div :class => 'table-wrapper' do
|
50
|
+
div :class => 'table-controls'
|
51
|
+
div :class => 'table-content' do
|
52
|
+
table :class => table_class, :id => table_id, :'data-highlight-id' => helper.flash[:highlight_id] do
|
53
|
+
header
|
54
|
+
body
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
pagination
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# Content methods
|
63
|
+
|
64
|
+
def header
|
65
|
+
thead do
|
66
|
+
tr do
|
67
|
+
th :class => 'first'
|
68
|
+
fields.each do |field|
|
69
|
+
th :class => th_class, :'data-field-name' => field.name do
|
70
|
+
header = Header.new(self, field)
|
71
|
+
rawtext(column_sorting ? helper.link_to(header.label, header.url, :class => ['sortable', header.css_class]) : header.label)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
th :class => 'last'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def body
|
80
|
+
tbody do
|
81
|
+
scoped_collection.each do |object|
|
82
|
+
tr :class => row_class, :'data-id' => object.id do
|
83
|
+
td :class => 'first'
|
84
|
+
fields.each do |field|
|
85
|
+
td field.value_for(object)
|
86
|
+
end
|
87
|
+
td :class => 'last' do
|
88
|
+
row_actions_for(object) if row_actions
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def pagination
|
96
|
+
div :class => pagination_class do
|
97
|
+
div :class => 'results' do
|
98
|
+
count = [scoped_collection.count(true), scoped_collection.total_count].join(I18n.t('of', :scope => 'views.pagination'))
|
99
|
+
label = helper.t('views.pagination.results')
|
100
|
+
rawtext "#{label} #{count}"
|
101
|
+
end
|
102
|
+
rawtext helper.paginate scoped_collection, :outer_window => 1, :inner_window => 5, :params => { :search => nil }, :param_name => page_param
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def row_actions_for(object)
|
107
|
+
rawtext helper.link_to(label = helper.t('resources.actions.edit'), edit_resource_path(object), :class => 'edit', :alt => label)
|
108
|
+
rawtext helper.link_to(label = helper.t('resources.actions.destroy'), resource_path(object), :method => :delete, :confirm => helper.t('resources.destroy.confirm'), :class => 'destroy', :alt => label)
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# DSL methods
|
113
|
+
|
114
|
+
def field(name, options = {}, &block)
|
115
|
+
|
116
|
+
value_callback = lambda do |name, options|
|
117
|
+
attribute_path = name.to_s.split('.')
|
118
|
+
value = attribute_path.inject(self) {|r, v| r.try(v) rescue '' }
|
119
|
+
|
120
|
+
# FIXME: This is bad code and I should feel bad. Extract it to a presenter maybe?
|
121
|
+
if value.is_a? Time
|
122
|
+
I18n.l(value, :format => :short)
|
123
|
+
elsif value.is_a? Date
|
124
|
+
I18n.l(value, :format => :default)
|
125
|
+
elsif value.is_a? Array
|
126
|
+
value.join(', ')
|
127
|
+
elsif options[:i18n]
|
128
|
+
I18n.t(value, :scope => options[:i18n])
|
129
|
+
else
|
130
|
+
value
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
@fields << Field.new(self, name, options, value_callback)
|
135
|
+
|
136
|
+
# Haml support hack
|
137
|
+
''
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# Helper classes
|
142
|
+
|
143
|
+
class Field
|
144
|
+
attr_accessor :parent, :name, :options, :value_callback
|
145
|
+
|
146
|
+
def initialize(parent, name, options, value_callback)
|
147
|
+
@parent, @name, @options, @value_callback = parent, name, options, value_callback
|
148
|
+
end
|
149
|
+
|
150
|
+
def value_for(object)
|
151
|
+
object.instance_exec(name, options, &value_callback)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class Header
|
156
|
+
extend AdminWidgets::Delegation
|
157
|
+
|
158
|
+
attr_accessor :parent, :field
|
159
|
+
|
160
|
+
def initialize(parent, field)
|
161
|
+
@parent, @field = parent, field
|
162
|
+
end
|
163
|
+
|
164
|
+
delegate :helper, :params, :resource_class, :to => :parent
|
165
|
+
with_options :to => :params, :hash => true do |c|
|
166
|
+
c.delegate :sort_direction
|
167
|
+
c.delegate :per_page
|
168
|
+
c.delegate :page
|
169
|
+
c.delegate :where
|
170
|
+
c.delegate :scope
|
171
|
+
end
|
172
|
+
|
173
|
+
def sort_field
|
174
|
+
field.name
|
175
|
+
end
|
176
|
+
|
177
|
+
def current_sort_field?
|
178
|
+
helper.params[:sort_field] == sort_field.to_s
|
179
|
+
end
|
180
|
+
|
181
|
+
def next_sort_direction
|
182
|
+
(current_sort_field? && sort_direction == 'asc') ? 'desc' : 'asc'
|
183
|
+
end
|
184
|
+
|
185
|
+
def sort_params
|
186
|
+
{
|
187
|
+
:sort_field => sort_field,
|
188
|
+
:sort_direction => next_sort_direction,
|
189
|
+
:per_page => per_page,
|
190
|
+
:page => page,
|
191
|
+
:where => where,
|
192
|
+
:scope => scope
|
193
|
+
}
|
194
|
+
end
|
195
|
+
|
196
|
+
def label
|
197
|
+
resource_class.human_attribute_name(field.name)
|
198
|
+
end
|
199
|
+
|
200
|
+
def url
|
201
|
+
helper.collection_path(sort_params)
|
202
|
+
end
|
203
|
+
|
204
|
+
def css_class
|
205
|
+
sort_direction if current_sort_field?
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
module Memoization
|
3
|
+
def memoize(attribute, val_or_callback)
|
4
|
+
ivar = "@#{attribute}"
|
5
|
+
|
6
|
+
define_method attribute do
|
7
|
+
if (current_val = instance_variable_get(ivar)).nil?
|
8
|
+
value = val_or_callback.respond_to?(:call) ? instance_eval(&val_or_callback) : val_or_callback
|
9
|
+
current_val = instance_variable_set(ivar, value)
|
10
|
+
end
|
11
|
+
current_val
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class ScopesWidget < BaseWidget
|
3
|
+
|
4
|
+
def content
|
5
|
+
div :class => 'scopes' do
|
6
|
+
content_block
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
# DSL methods
|
12
|
+
|
13
|
+
def scope(name, options = {})
|
14
|
+
rawtext Scope.new(self, name, options).to_html
|
15
|
+
|
16
|
+
# Haml support hack
|
17
|
+
''
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
# Options
|
22
|
+
|
23
|
+
memoize :collection, proc { helper.collection }
|
24
|
+
memoize :resource_class, proc { helper.resource_class }
|
25
|
+
memoize :current_scope, proc { helper.current_scope }
|
26
|
+
memoize :params, proc { helper.params.reject{ |k,v| k == 'search' } }
|
27
|
+
memoize :i18n_scope, proc { "mongoid.scopes.#{resource_class.to_s.underscore}" }
|
28
|
+
|
29
|
+
|
30
|
+
# Helper classes
|
31
|
+
|
32
|
+
class Scope < BaseWidget
|
33
|
+
attr_accessor :parent, :name, :options
|
34
|
+
|
35
|
+
def initialize(parent, name, options = {})
|
36
|
+
super options.merge(:parent => parent, :name => name)
|
37
|
+
end
|
38
|
+
|
39
|
+
delegate :helper, :collection, :current_scope, :i18n_scope, :to => :parent
|
40
|
+
|
41
|
+
memoize :label, proc { I18n.t(name, :scope => i18n_scope) }
|
42
|
+
memoize :full_label, proc { label }
|
43
|
+
memoize :default, false
|
44
|
+
memoize :scope, proc { default ? nil : name }
|
45
|
+
memoize :current_css_class, proc { 'current' if current? }
|
46
|
+
memoize :css_class, proc { ['scope', current_css_class] }
|
47
|
+
|
48
|
+
def current?
|
49
|
+
(current_scope.nil? && default) || (current_scope.to_s == name.to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
def params
|
53
|
+
parent.params.merge(:scope => scope)
|
54
|
+
end
|
55
|
+
|
56
|
+
def content
|
57
|
+
rawtext helper.link_to(full_label, params, :class => css_class)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module AdminWidgets::Show
|
2
|
+
class FieldsetWidget < AdminWidgets::ShowWidget
|
3
|
+
|
4
|
+
attr_reader :parent
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
memoize :resource, proc { parent.resource.send(name) }
|
8
|
+
memoize :legend, proc { parent.resource_class.human_attribute_name(name) }
|
9
|
+
|
10
|
+
delegate :helper, :to => :parent
|
11
|
+
|
12
|
+
def content
|
13
|
+
rawtext(around_fieldset do
|
14
|
+
super
|
15
|
+
end)
|
16
|
+
end
|
17
|
+
|
18
|
+
def around_fieldset
|
19
|
+
element('fieldset') do
|
20
|
+
element('legend', legend)
|
21
|
+
|
22
|
+
yield
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class ShowWidget < BaseWidget
|
3
|
+
|
4
|
+
attr_reader :builder
|
5
|
+
|
6
|
+
memoize :resource, proc { helper.resource }
|
7
|
+
memoize :resource_class, proc { resource.class }
|
8
|
+
|
9
|
+
def content
|
10
|
+
rawtext show
|
11
|
+
end
|
12
|
+
|
13
|
+
def show
|
14
|
+
helper.show_for(resource) do |builder|
|
15
|
+
@builder = builder
|
16
|
+
|
17
|
+
capture do
|
18
|
+
content_block
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def field(name, options = {})
|
24
|
+
condition = options[:if]
|
25
|
+
return if (condition.present? && !resource.instance_eval(&condition))
|
26
|
+
|
27
|
+
rawtext resource.reflect_on_association(name) ? builder.association(name, options) : builder.attribute(name, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def fieldset(name, options = {}, &block)
|
31
|
+
widget AdminWidgets::Show::FieldsetWidget.new(options.merge(:name => name, :parent => self, :block => block))
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class ViewProxy < ActiveSupport::BasicObject
|
3
|
+
|
4
|
+
attr_accessor :widget
|
5
|
+
|
6
|
+
def initialize(view, widget)
|
7
|
+
@widget = widget
|
8
|
+
@view = buffered_view(view)
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method_name, *args, &block)
|
12
|
+
target = case
|
13
|
+
when widget.dsl_methods.include?(method_name.to_sym) then @widget
|
14
|
+
else @view
|
15
|
+
end
|
16
|
+
|
17
|
+
target.send(method_name, *args, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Manually initialize output buffers
|
23
|
+
# This is needed for ActionView::Helpers::CaptureHelper
|
24
|
+
def buffered_view(view)
|
25
|
+
@view || begin
|
26
|
+
init_output_buffer!(view)
|
27
|
+
init_haml_buffer!(view)
|
28
|
+
|
29
|
+
view
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def init_output_buffer!(view)
|
34
|
+
view.output_buffer = ::ActionView::OutputBuffer.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def init_haml_buffer!(view)
|
38
|
+
return unless defined? ::Haml
|
39
|
+
|
40
|
+
class << view
|
41
|
+
include ::Haml::Helpers unless included_modules.to_a.include?(::Haml::Helpers)
|
42
|
+
end
|
43
|
+
|
44
|
+
view.init_haml_helpers
|
45
|
+
view.instance_variable_set :@haml_buffer, ::Haml::Buffer.new(nil, :preserve => [], :encoding => 'UTF-8')
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module AdminWidgets
|
2
|
+
class WizardWidget < BaseFormWidget
|
3
|
+
|
4
|
+
memoize :url, proc { helper.resource_or_collection_path }
|
5
|
+
memoize :resource, proc { helper.resource }
|
6
|
+
memoize :resource_plural_name, proc { resource.class.name.underscore.pluralize }
|
7
|
+
memoize :display_finish_button, proc { true }
|
8
|
+
|
9
|
+
attr_reader :current_step
|
10
|
+
|
11
|
+
# DSL methods
|
12
|
+
|
13
|
+
def fieldset(name, options = {}, &block)
|
14
|
+
widget AdminWidgets::Form::FieldsetWidget.new(options.merge(:name => name, :parent => self, :block => block))
|
15
|
+
end
|
16
|
+
|
17
|
+
def steps_header
|
18
|
+
|
19
|
+
# Default button
|
20
|
+
rawtext helper.button_tag(:class => "default-step-button", :style => 'border:0;display:block;height:0;overflow:hidden;padding:0;', :name => 'change_step', :value => (@current_step.to_i + 1)) { 'Next' }
|
21
|
+
|
22
|
+
ul :class => 'steps' do
|
23
|
+
@steps.each_with_index do |step, index|
|
24
|
+
li do
|
25
|
+
active = @current_step == index + 1 ? ' active' : ''
|
26
|
+
rawtext helper.button_tag(:class => "step#{active}", :name => 'change_step', :value => index + 1) {
|
27
|
+
helper.t(step, :scope => "wizards.#{resource_plural_name}.steps", :index => index + 1)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def form_content
|
35
|
+
content_block
|
36
|
+
steps_header
|
37
|
+
rawtext @content_buffer
|
38
|
+
buttons
|
39
|
+
end
|
40
|
+
|
41
|
+
def buttons
|
42
|
+
div :class => ['group', 'navform', 'wat-cf'] do
|
43
|
+
rawtext helper.button_tag(:class => 'big button finish', :name => 'change_step', :value => @steps.size + 1) {
|
44
|
+
helper.image_tag('icons/tick.png') +
|
45
|
+
helper.t('resources.actions.finish')
|
46
|
+
} if display_finish_button && @current_step == @steps.count
|
47
|
+
rawtext helper.button_tag(:class => 'big button next', :name => 'change_step', :value => @current_step + 1) {
|
48
|
+
helper.image_tag('icons/next.png') +
|
49
|
+
helper.t('resources.actions.next')
|
50
|
+
} if @current_step < @steps.count
|
51
|
+
rawtext helper.button_tag(:class => 'big button previous', :name => 'change_step', :value => @current_step - 1) {
|
52
|
+
helper.image_tag('icons/previous.png') +
|
53
|
+
helper.t('resources.actions.previous')
|
54
|
+
} if @current_step > 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def step(name, &block)
|
59
|
+
@current_step ||= 1
|
60
|
+
@content_buffer ||= ''
|
61
|
+
@steps = [] if !@steps
|
62
|
+
@steps << name
|
63
|
+
@content_buffer += capture do
|
64
|
+
div :class => 'step' do
|
65
|
+
instance_eval &block if block
|
66
|
+
end if @current_step == @steps.count
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def view(&block)
|
71
|
+
@content_buffer ||= ''
|
72
|
+
|
73
|
+
@content_buffer += capture do
|
74
|
+
instance_eval &block if block
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: admin_widgets
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- DanX~
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: apotomo
|
16
|
+
requirement: &2157956000 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2157956000
|
25
|
+
description: Super Awesome Admin Widgets
|
26
|
+
email:
|
27
|
+
- danx.exe@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- Rakefile
|
35
|
+
- admin_widgets.gemspec
|
36
|
+
- lib/admin_widgets.rb
|
37
|
+
- lib/admin_widgets/base_form_widget.rb
|
38
|
+
- lib/admin_widgets/base_widget.rb
|
39
|
+
- lib/admin_widgets/delegation.rb
|
40
|
+
- lib/admin_widgets/engine.rb
|
41
|
+
- lib/admin_widgets/extensions/action_view_ext.rb
|
42
|
+
- lib/admin_widgets/extensions/simple_form_ext.rb
|
43
|
+
- lib/admin_widgets/filters_widget.rb
|
44
|
+
- lib/admin_widgets/form/fieldset_widget.rb
|
45
|
+
- lib/admin_widgets/form_widget.rb
|
46
|
+
- lib/admin_widgets/helper.rb
|
47
|
+
- lib/admin_widgets/list_widget.rb
|
48
|
+
- lib/admin_widgets/memoization.rb
|
49
|
+
- lib/admin_widgets/scopes_widget.rb
|
50
|
+
- lib/admin_widgets/show/fieldset_widget.rb
|
51
|
+
- lib/admin_widgets/show_widget.rb
|
52
|
+
- lib/admin_widgets/version.rb
|
53
|
+
- lib/admin_widgets/view_proxy.rb
|
54
|
+
- lib/admin_widgets/wizard_widget.rb
|
55
|
+
homepage: http://www.mobvox.com.br
|
56
|
+
licenses: []
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ! '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project: admin_widgets
|
75
|
+
rubygems_version: 1.8.10
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Awesome Admin Widgets
|
79
|
+
test_files: []
|
80
|
+
has_rdoc:
|