effective_bootstrap 0.13.2 → 0.13.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecffafbb096f543759ef66fd919f85dbf96acebd446ba522b4427c831b7b5b55
4
- data.tar.gz: 9793e72a94792bed35abec120c8346d9c11f170e0323aa06aff49f75650d6d7f
3
+ metadata.gz: 9cc712dfbfdacf4feb17b2e3dc016f6fdefd3a9fd1935dbdf92425c2b607d99b
4
+ data.tar.gz: 7cf8df3af6713f5ad519c583374c0e2db10b91ff9eaf14bbebff19b8011da24e
5
5
  SHA512:
6
- metadata.gz: 7394dc15b00b1e4bc79cd4bdffb220fa093b034dd0dc67beea558e4ae87b6e61c4d5fb37c3c9b9e4934811b0bd420c6e0da427445009b33f881d86b5097c79f0
7
- data.tar.gz: ec4c87b103ebd7f621e6304948daeaa3adcf6078b9df9e71c6da1fba9593c98a6a97b882b3602e8b04cef5859d19d64b33d634c34107d18daed61417e0ed6c74
6
+ metadata.gz: 9b85f3fa66ddcf566a27dff5a60ab3a71a62827d150c440bfede05163016330e5f4189b29dc195d021fa900826654f94dfd479057879905e0f8b3281879168a1
7
+ data.tar.gz: 227f0c30431c24fff57d42f70d14540de193939d848b9a89bc951633e0eb7c5ac6a4f828394335a92fbada107021e6bba27453a322ee88d18c2d7af4c00de075
data/README.md CHANGED
@@ -697,8 +697,74 @@ The `f.save` is purely a input submit button.
697
697
  = f.save 'Save 2'
698
698
  ```
699
699
 
700
+ ## Table Builder
700
701
 
702
+ Use `effective_table_with(resource)` to intelligently output a table of attributes for a resource.
701
703
 
704
+ In your view:
705
+
706
+ ```
707
+ = effective_table_with(user, only: [:first_name, :last_name])
708
+ ```
709
+
710
+ will output the following html:
711
+
712
+ ```
713
+ <table class='table table-striped table-hover>
714
+ <tbody>
715
+ <tr>
716
+ <td>First Name</td>
717
+ <td>Peter</td>
718
+ </tr>
719
+ <tr>
720
+ <td>Last Name</td>
721
+ <td>Pan</td>
722
+ </tr>
723
+ </tbody>
724
+ </table>
725
+ ```
726
+
727
+ You can pass `only:` and `except:` to specify which attributes to display.
728
+
729
+ To override the content of just one row:
730
+
731
+ ```
732
+ = effective_table_with(user) do |f|
733
+ = f.content_for :first_name, label: 'Cool First Name' do
734
+ %strong= f.object.first_name
735
+ ```
736
+
737
+ will generate the html:
738
+
739
+ `<tr><td>Cool First Name</td><td><strong>Peter</strong></td></tr>`
740
+
741
+ ### Work with existing forms
742
+
743
+ The table builder is intended to display based off your existing forms.
744
+
745
+ Any `effective_form_with` or `_fields` partial will work to define the attributes displayed and the order they are displayed.
746
+
747
+ A check_box will be rendered as a boolean, text areas use simple_format, f.show_if and f.hide_if logic work.
748
+
749
+ If you use `f.text_field :first_name, label: 'Cool First Name'` in your form, it will flow through to the table.
750
+
751
+ To render a table based off an existing `effective_form_with`:
752
+
753
+ ```
754
+ = effective_table_with(user) do |f|
755
+ = render('users/form', user: user)
756
+ ```
757
+
758
+ or just the fields partial
759
+
760
+ ```
761
+ = effective_table_with(user, only: [:first_name, :last_name]) do |f|
762
+ = render('users/fields_demographics', f: f)
763
+ ```
764
+
765
+ You can specify `only:` and `except:` and use `f.content_for` to override a row in all these use cases.
766
+
767
+ All values flow through to i18n and can be overriden, same as the form labels, in the locale .yml file.
702
768
 
703
769
  ## License
704
770
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  module EffectiveFormBuilderHelper
4
4
  def effective_form_with(**options, &block)
5
+ # This allows us to call effective_form_with inside an effective_table_with and just get the fields
6
+ return @_effective_table_builder.capture(&block) if @_effective_table_builder
7
+
5
8
  # Compute the default ID
6
9
  subject = Array(options[:model] || options[:scope]).last
7
10
  class_name = (options[:scope] || subject.class.name.parameterize.underscore)
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EffectiveTableBuilderHelper
4
+
5
+ def effective_table_with(resource, options = {}, &block)
6
+ builder = Effective::TableBuilder.new(resource, self, options)
7
+ builder.render(&block)
8
+ end
9
+
10
+ end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ class TableBuilder
5
+ FILTER_PARAMETERS = [:password, :password_confirmation, :created_at, :updated_at]
6
+
7
+ attr_accessor :object, :template, :options
8
+
9
+ # A Hash of :first_name => "<tr><td>First Name</td><td>Bob</td></tr>"
10
+ attr_accessor :rows
11
+ attr_accessor :content_fors
12
+
13
+ delegate :content_tag, to: :template
14
+
15
+ def initialize(object, template, options = {}, &block)
16
+ raise('resource must be an ActiveRecord::Base') unless object.is_a?(ActiveRecord::Base)
17
+
18
+ @object = object
19
+ @template = template
20
+ @options = options
21
+
22
+ @rows = {}
23
+ @content_fors = {}
24
+ end
25
+
26
+ def render(&block)
27
+ # This runs the form partial over this table builder
28
+ capture(&block) if block_given?
29
+
30
+ # Build from the resource if we didn't do anything in the block
31
+ build_resource_rows if rows.blank?
32
+
33
+ only = Array(options[:only])
34
+ except = Array(options[:except])
35
+ filtered = filter_parameters
36
+
37
+ content = rows.merge(content_fors)
38
+ content = content.slice(*only) if only.present?
39
+ content = content.except(*except) if except.present?
40
+ content = content.except(*filtered) if filtered.present?
41
+
42
+ content_tag(:table, class: options.fetch(:class, 'table table-striped table-hover')) do
43
+ content_tag(:tbody, content.values.join.html_safe)
44
+ end
45
+ end
46
+
47
+ def capture(&block)
48
+ begin
49
+ template.instance_variable_set(:@_effective_table_builder, self)
50
+ template.capture(self, &block)
51
+ ensure
52
+ template.instance_variable_set(:@_effective_table_builder, nil)
53
+ end
54
+ end
55
+
56
+ def build_resource_rows
57
+ Effective::Resource.new(object).klass_attributes(sort: true).each do |name, options|
58
+ case options.first
59
+ when :boolean
60
+ boolean_row(name)
61
+ when :text
62
+ text_area(name)
63
+ when :string
64
+ (name.to_s.include?('email') && value(name).to_s.include?('@')) ? email_field(name) : text_field(name)
65
+ else default_row(name)
66
+ end
67
+ end
68
+ end
69
+
70
+ def filter_parameters
71
+ FILTER_PARAMETERS + Array(object.class.try(:filter_parameters)) + Array(Rails.application.config.filter_parameters)
72
+ end
73
+
74
+ def human_attribute_name(name)
75
+ if object.respond_to?(:human_attribute_name)
76
+ object.human_attribute_name(name)
77
+ else
78
+ (name.to_s.split('.').last.to_s.titleize || '')
79
+ end
80
+ end
81
+
82
+ def value(name)
83
+ object.send(name)
84
+ end
85
+
86
+ # Call default_row for any form field
87
+ def method_missing(method, *args, **kwargs, &block)
88
+ default_row(args[0], **kwargs, &block)
89
+ end
90
+
91
+ # Assign the <tr><td>...</td></tr> to the @rows Hash
92
+ def default_row(name, options = {}, &block)
93
+ rows[name] = TableRow.new(name, options, builder: self).to_html
94
+ end
95
+
96
+ def boolean_row(name, options = {})
97
+ rows[name] = TableRows::Boolean.new(name, options, builder: self).to_html
98
+ end
99
+ alias_method :check_box, :boolean_row
100
+
101
+ def collection_row(name, collection, options = {}, &block)
102
+ rows[name] = if [true, false].include?(value(name))
103
+ TableRows::Boolean.new(name, options, builder: self).to_html
104
+ else
105
+ TableRows::Collection.new(name, collection, options, builder: self).to_html
106
+ end
107
+ end
108
+ alias_method :select, :collection_row
109
+ alias_method :checks, :collection_row
110
+ alias_method :radios, :collection_row
111
+
112
+ def email_field(name, options = {})
113
+ rows[name] = TableRows::EmailField.new(name, options, builder: self).to_html
114
+ end
115
+ alias_method :email_cc_field, :email_field
116
+
117
+ def file_field(name, options = {})
118
+ rows[name] = TableRows::FileField.new(name, options, builder: self).to_html
119
+ end
120
+
121
+ def form_group(name = nil, options = {}, &block)
122
+ # Nothing to do
123
+ end
124
+
125
+ def hidden_field(name = nil, options = {})
126
+ # Nothing to do
127
+ end
128
+
129
+ def search_field(name, options = {})
130
+ # Nothing to do
131
+ end
132
+
133
+ def password_field(name, options = {})
134
+ # Nothing to do
135
+ end
136
+
137
+ def effective_address(name, options = {})
138
+ rows[name] = TableRows::EffectiveAddress.new(name, options, builder: self).to_html
139
+ end
140
+
141
+ def percent_field(name, options = {})
142
+ rows[name] = TableRows::PercentField.new(name, options, builder: self).to_html
143
+ end
144
+
145
+ def price_field(name, options = {})
146
+ rows[name] = TableRows::PriceField.new(name, options, builder: self).to_html
147
+ end
148
+
149
+ def save(name, options = {})
150
+ # Nothing to do
151
+ end
152
+
153
+ def submit(name, options = {}, &block)
154
+ # Nothing to do
155
+ end
156
+
157
+ def text_area(name, options = {})
158
+ rows[name] = TableRows::TextArea.new(name, options, builder: self).to_html
159
+ end
160
+
161
+ def url_field(name, options = {})
162
+ rows[name] = TableRows::UrlField.new(name, options, builder: self).to_html
163
+ end
164
+
165
+ def content_for(name, options = {}, &block)
166
+ content_fors[name] = TableRows::ContentFor.new(name, options, builder: self).to_html(&block)
167
+ end
168
+
169
+ # Logics
170
+ def hide_if(name, selected, &block)
171
+ template.capture(self, &block) unless value(name) == selected
172
+ end
173
+
174
+ def show_if(name, selected, &block)
175
+ template.capture(self, &block) if value(name) == selected
176
+ end
177
+
178
+ def show_if_any(name, options, &block)
179
+ template.capture(self, &block) if Array(options).include?(value(name))
180
+ end
181
+
182
+ # Has Many
183
+ def has_many(name, collection = nil, options = {}, &block)
184
+ raise('unsupported')
185
+ end
186
+
187
+ def fields_for(name, object, options = {}, &block)
188
+ builder = TableBuilder.new(object, template, options.merge(prefix: human_attribute_name(name)))
189
+ builder.render(&block)
190
+ builder.rows.each { |child, content| rows["#{name}_#{child}".to_sym] = content }
191
+ end
192
+ alias_method :effective_fields_for, :fields_for
193
+
194
+ end
195
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ class TableRow
5
+ attr_accessor :name, :options, :builder, :template
6
+
7
+ delegate :object, to: :builder
8
+ delegate :capture, :content_tag, :image_tag, :link_to, :mail_to, :icon, to: :@template
9
+
10
+ # So this takes in the options for an entire form group.
11
+ def initialize(name, options, builder:)
12
+ @builder = builder
13
+ @template = builder.template
14
+
15
+ @name = name
16
+ @options = options
17
+ end
18
+
19
+ # Intended for override
20
+ def content
21
+ value
22
+ end
23
+
24
+ # Render method
25
+ def to_html(&block)
26
+ content_tag(:tr) do
27
+ content_tag(:td, label) + content_tag(:td, content.presence || '-')
28
+ end
29
+ end
30
+
31
+ # Humanized label or the label from form
32
+ def label
33
+ text = options[:label] || builder.human_attribute_name(name)
34
+ prefix = builder.options[:prefix]
35
+
36
+ [*prefix, text].join(': ')
37
+ end
38
+
39
+ # Value from resource
40
+ def value
41
+ options[:value] || builder.value(name)
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class Boolean < Effective::TableRow
6
+
7
+ def content
8
+ if value
9
+ template.badge('YES')
10
+ else
11
+ template.badge('NO', class: 'badge badge-light')
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class Collection < Effective::TableRow
6
+
7
+ def initialize(name, collection, options, builder:)
8
+ @collection = collection
9
+ super(name, options, builder: builder)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class ContentFor < Effective::TableRow
6
+
7
+ def to_html(&block)
8
+ content_tag(:tr) do
9
+ content_tag(:td, label) + content_tag(:td, template.capture(&block))
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class EffectiveAddress < Effective::TableRow
6
+
7
+ def content
8
+ value.to_html if value.present?
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class EmailField < Effective::TableRow
6
+
7
+ def content
8
+ value.to_s.include?('@') ? template.mail_to(value) : value.presence
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class FileField < Effective::TableRow
6
+
7
+ def content
8
+ raise('todo')
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class PercentField < Effective::TableRow
6
+
7
+ def content
8
+ template.number_to_percent(value) if value.present?
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class PercentField < Effective::TableRow
6
+
7
+ def content
8
+ template.price_to_currency(value) if value.present?
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class TextArea < Effective::TableRow
6
+
7
+ def content
8
+ template.simple_format(value) if value.present?
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ module TableRows
5
+ class PercentField < Effective::TableRow
6
+
7
+ def content
8
+ template.link_to(value) if value.present?
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module EffectiveBootstrap
2
- VERSION = '0.13.2'.freeze
2
+ VERSION = '0.13.3'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_bootstrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.2
4
+ version: 0.13.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-12 00:00:00.000000000 Z
11
+ date: 2023-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -647,6 +647,7 @@ files:
647
647
  - app/helpers/effective_editor_helper.rb
648
648
  - app/helpers/effective_form_builder_helper.rb
649
649
  - app/helpers/effective_icons_helper.rb
650
+ - app/helpers/effective_table_builder_helper.rb
650
651
  - app/models/effective/form_builder.rb
651
652
  - app/models/effective/form_input.rb
652
653
  - app/models/effective/form_inputs/article_editor.rb
@@ -693,6 +694,18 @@ files:
693
694
  - app/models/effective/form_logics/hide_if.rb
694
695
  - app/models/effective/form_logics/show_if.rb
695
696
  - app/models/effective/form_logics/show_if_any.rb
697
+ - app/models/effective/table_builder.rb
698
+ - app/models/effective/table_row.rb
699
+ - app/models/effective/table_rows/boolean.rb
700
+ - app/models/effective/table_rows/collection.rb
701
+ - app/models/effective/table_rows/content_for.rb
702
+ - app/models/effective/table_rows/effective_address.rb
703
+ - app/models/effective/table_rows/email_field.rb
704
+ - app/models/effective/table_rows/file_field.rb
705
+ - app/models/effective/table_rows/percent_field.rb
706
+ - app/models/effective/table_rows/price_field.rb
707
+ - app/models/effective/table_rows/text_area.rb
708
+ - app/models/effective/table_rows/url_field.rb
696
709
  - app/views/action_text/attachables/_content_attachment.html.erb
697
710
  - app/views/action_text/attachables/content_attachments/_horizontal_rule.html.erb
698
711
  - app/views/effective/style_guide/__fields.html.haml