effective_bootstrap 0.13.2 → 0.13.3

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 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