katalyst-tables 1.1.0 → 2.0.0

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: 5482118f21175e8c1dbff1a397fa7b5ff68480783a2c47da2a92c0436c66e739
4
- data.tar.gz: 99e70f74ef353cce895305e5ad39a98dc690efd7f0c68d4305867fbcdc23c941
3
+ metadata.gz: 4c4a55f7a1b45c9f70f357eaa670debf6457aca92a8ac7ea8b10df20140d1c23
4
+ data.tar.gz: 620cff7771f9de2d74c613d6b107136ea34c494862d2da4e612ccb55d8dee51d
5
5
  SHA512:
6
- metadata.gz: 219380d0ccb99e001c9604594d0aa68a58925bbd745d87898af359316192e61727d3549c4ab64198c31656c663ec00809d93ddc8034921143f8169fcb49f7769
7
- data.tar.gz: b9d8a9e03604f9b39389d218bfceb4d354343bd0d1da4ff3bd7ca55f66446ba8f5fbc8430b9d5b22974f1210eed344ce0d579bccb209e29a0d35ca59982d4c63
6
+ metadata.gz: 15cc508fdbb3ad40baec51314fde01c3889b670d4ccd6366ea49171a45b979d441a4fb3fafe65e5bd07093375615b6d298ebe091f240122edf9d68b10471f251
7
+ data.tar.gz: e5f4186c070c81c4645bc3cf8a378927f502df6084d235c38416d98eb7e4ab45c775da2d22338d32bf5063774a575b4c7a92a556468f1b35d18c809db421922c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [2.0.0]
4
+
5
+ - Replaces builders with view_components
6
+ We want view components to be the way to build custom tables, and add more
7
+ complete features to the gem. This will be a breaking change, but we have
8
+ tried hard to retain compatibility with existing code. Unless you are using
9
+ a custom builder then it's unlikely that you will see any changes.
10
+ - If you are using custom builders, then you will need to update them to use
11
+ view components. See [[README.md]] for examples.
12
+
3
13
  ## [1.1.0] - 2023-07-18
4
14
 
5
15
  - Replaces `param_key` with `i18n_key` for attribute lookup in locale file
data/README.md CHANGED
@@ -7,18 +7,16 @@ Tools for building HTML tables from ActiveRecord collections.
7
7
  Add this line to your application's Gemfile:
8
8
 
9
9
  ```ruby
10
- gem "katalyst-tables", git: "https://github.com/katalyst/katalyst-tables", branch: "main"
10
+ gem "katalyst-tables"
11
11
  ```
12
12
 
13
13
  And then execute:
14
14
 
15
15
  $ bundle install
16
16
 
17
- **Reminder:** If you have a rails server running, remember to restart the server to prevent the `uninitialized constant` error.
18
-
19
17
  ## Usage
20
18
 
21
- This gem provides two entry points: Frontend for use in your views, and Backend for use in your controllers. The backend
19
+ This gem provides two entry points: `Frontend` for use in your views, and `Backend` for use in your controllers. The backend
22
20
  entry point is optional, as it's only required if you want to support sorting by column headers.
23
21
 
24
22
  ### Frontend
@@ -35,7 +33,7 @@ Add `include Katalyst::Tables::Frontend` to your `ApplicationHelper` or similar.
35
33
  <% end %>
36
34
  ```
37
35
 
38
- `table_builder` will call your block once per row and accumulate the cells you generate into rows:
36
+ The table builder will call your block once per row and accumulate the cells you generate into rows:
39
37
 
40
38
  ```html
41
39
 
@@ -102,14 +100,14 @@ All cells generated in the table header iteration will automatically be header c
102
100
  in your body rows by passing `heading: true` when you generate the cell.
103
101
 
104
102
  ```erb
105
- <%= row.cell :id, heading: true %>
103
+ <% row.cell :id, heading: true %>
106
104
  ```
107
105
 
108
- The table header cells default to showing the titleized column name, but you can customize this in one of two ways:
106
+ The table header cells default to showing the capitalized column name, but you can customize this in one of two ways:
109
107
 
110
108
  * Set the value inline
111
109
  ```erb
112
- <%= row.cell :id, label: "ID" %>
110
+ <% row.cell :id, label: "ID" %>
113
111
  ```
114
112
  * Define a translation for the attribute
115
113
  ```yml
@@ -261,56 +259,42 @@ You can write a custom builder that helps generate this type of table by adding
261
259
  for generating the actions. This allows for a declarative table syntax, something like this:
262
260
 
263
261
  ```erb
264
- <%= table_with(collection: collection, builder: Test::ActionTable) do |row| %>
265
- <%= row.cell :name %>
266
- <%= row.actions do |cell| %>
262
+ <%= table_with(collection: collection, component: ActionTableComponent) do |row| %>
263
+ <% row.cell :name %>
264
+ <% row.actions do |cell| %>
267
265
  <%= cell.action "Edit", :edit %>
268
266
  <%= cell.action "Delete", :delete, method: :delete %>
269
267
  <% end %>
270
268
  <% end %>
271
269
  ```
272
270
 
273
- And the custom builder:
271
+ And the customized component:
274
272
 
275
273
  ```ruby
276
- class ActionTable < Katalyst::Tables::Frontend::TableBuilder
277
- def build(&block)
278
- (@html_options[:class] ||= []) << "action-table"
279
- super
280
- end
281
-
282
- def table_header_row(builder = ActionHeaderRow, &block)
283
- super
284
- end
274
+ class ActionTableComponent < Katalyst::TableComponent
285
275
 
286
- def table_header_cell(method, builder = ActionHeaderCell, **options)
287
- super
288
- end
276
+ config.header_row = "ActionHeaderRow"
277
+ config.body_row = "ActionBodyRow"
278
+ config.body_cell = "ActionBodyCell"
289
279
 
290
- def table_body_row(object, builder = ActionBodyRow, &block)
280
+ def call
281
+ options(class: "action-table")
291
282
  super
292
283
  end
293
284
 
294
- def table_body_cell(object, method, builder = ActionBodyCell, **options, &block)
295
- super
296
- end
297
-
298
- class ActionHeaderRow < Katalyst::Tables::Frontend::Builder::HeaderRow
285
+ class ActionHeaderRow < Katalyst::Tables::HeaderRowComponent
299
286
  def actions(&block)
300
- cell(:actions, class: "actions", label: "")
287
+ cell(:actions, class: "actions", label: "", &block)
301
288
  end
302
289
  end
303
290
 
304
- class ActionHeaderCell < Katalyst::Tables::Frontend::Builder::HeaderCell
305
- end
306
-
307
- class ActionBodyRow < Katalyst::Tables::Frontend::Builder::BodyRow
291
+ class ActionBodyRow < Katalyst::Tables::BodyRowComponent
308
292
  def actions(&block)
309
293
  cell(:actions, class: "actions", &block)
310
294
  end
311
295
  end
312
296
 
313
- class ActionBodyCell < Katalyst::Tables::Frontend::Builder::BodyCell
297
+ class ActionBodyCell < Katalyst::Tables::BodyCellComponent
314
298
  def action(label, href, **opts)
315
299
  content_tag :a, label, { href: href }.merge(opts)
316
300
  end
@@ -318,11 +302,11 @@ class ActionTable < Katalyst::Tables::Frontend::TableBuilder
318
302
  end
319
303
  ```
320
304
 
321
- If you have a table builder you want to reuse, you can set it as a default for some or all of your controllers:
305
+ If you have a table component you want to reuse, you can set it as a default for some or all of your controllers:
322
306
 
323
307
  ```html
324
308
  class ApplicationController < ActiveController::Base
325
- default_table_builder ActionTableBuilder
309
+ default_table_component ActionTableComponent
326
310
  end
327
311
  ```
328
312
 
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ # A component for rendering a table from a collection, with a header row.
5
+ # ```erb
6
+ # <%= Katalyst::TableComponent.new(collection: @people) do |row, person| %>
7
+ # <%= row.cell :name do |cell| %>
8
+ # <%= link_to cell.value, person %>
9
+ # <% end %>
10
+ # <%= row.cell :email %>
11
+ # <% end %>
12
+ # ```
13
+ class TableComponent < ViewComponent::Base
14
+ include ActiveSupport::Configurable
15
+ include Tables::Frontend::Helper
16
+
17
+ attr_reader :collection, :sort, :object_name
18
+
19
+ # Workaround: ViewComponent::Base.config is incompatible with ActiveSupport::Configurable
20
+ @_config = Class.new(Configuration).new
21
+
22
+ config_accessor :header_row
23
+ config_accessor :header_cell
24
+ config_accessor :body_row
25
+ config_accessor :body_cell
26
+
27
+ def initialize(collection:,
28
+ sort: nil,
29
+ header: true,
30
+ object_name: collection.try(:model_name)&.i18n_key,
31
+ **html_options)
32
+ super
33
+
34
+ @collection = collection
35
+ @sort = sort
36
+ @header = header
37
+ @object_name = object_name
38
+ end
39
+
40
+ def call
41
+ tag.table(**@html_options) do
42
+ thead + tbody
43
+ end
44
+ end
45
+
46
+ def thead
47
+ return "".html_safe unless @header
48
+
49
+ tag.thead do
50
+ concat(render_header)
51
+ end
52
+ end
53
+
54
+ def tbody
55
+ tag.tbody do
56
+ collection.each do |record|
57
+ concat(render_row(record))
58
+ end
59
+ end
60
+ end
61
+
62
+ def render_header
63
+ # extract the column's block from the slot and pass it to the cell for rendering
64
+ self.class.header_row_component.new(self).render_in(view_context, &@__vc_render_in_block)
65
+ end
66
+
67
+ def render_row(record)
68
+ # extract the column's block from the slot and pass it to the cell for rendering
69
+ block = @__vc_render_in_block
70
+ self.class.body_row_component.new(self, record).render_in(view_context) do |row|
71
+ block.call(row, record)
72
+ end
73
+ end
74
+
75
+ def self.header_row_component
76
+ @header_row_component ||= const_get(config.header_row || "Katalyst::Tables::HeaderRowComponent")
77
+ end
78
+
79
+ def self.header_cell_component
80
+ @header_cell_component ||= const_get(config.header_cell || "Katalyst::Tables::HeaderCellComponent")
81
+ end
82
+
83
+ def self.body_row_component
84
+ @body_row_component ||= const_get(config.body_row || "Katalyst::Tables::BodyRowComponent")
85
+ end
86
+
87
+ def self.body_cell_component
88
+ @body_cell_component ||= const_get(config.body_cell || "Katalyst::Tables::BodyCellComponent")
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ class BodyCellComponent < ViewComponent::Base # :nodoc:
6
+ include Frontend::Helper
7
+
8
+ attr_reader :record
9
+
10
+ def initialize(table, record, attribute, heading: false, **html_options)
11
+ super(**html_options)
12
+
13
+ @table = table
14
+ @record = record
15
+ @attribute = attribute
16
+ @type = heading ? :th : :td
17
+ end
18
+
19
+ def before_render
20
+ # fallback if no content block is given
21
+ with_content(value.to_s) unless content?
22
+ end
23
+
24
+ def call
25
+ content # ensure content is set before rendering options
26
+
27
+ content_tag(@type, content, **@html_options)
28
+ end
29
+
30
+ # @return the object for this row.
31
+ def object
32
+ @record
33
+ end
34
+
35
+ def value
36
+ @record.public_send(@attribute)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ class BodyRowComponent < ViewComponent::Base # :nodoc:
6
+ include Frontend::Helper
7
+
8
+ renders_many :columns, ->(component) { component }
9
+
10
+ def initialize(table, record)
11
+ super()
12
+
13
+ @table = table
14
+ @record = record
15
+ end
16
+
17
+ def call
18
+ content # generate content before rendering
19
+
20
+ tag.tr(**@html_options) do
21
+ columns.each do |column|
22
+ concat(column.to_s)
23
+ end
24
+ end
25
+ end
26
+
27
+ def cell(attribute, **options, &block)
28
+ with_column(@table.class.body_cell_component.new(@table, @record, attribute, **options), &block)
29
+ end
30
+
31
+ def header?
32
+ false
33
+ end
34
+
35
+ def body?
36
+ true
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ class HeaderCellComponent < ViewComponent::Base # :nodoc:
6
+ include Frontend::Helper
7
+
8
+ delegate :object_name, :sort, to: :@table
9
+
10
+ def initialize(table, attribute, label: nil, **html_options)
11
+ super(**html_options)
12
+
13
+ @table = table
14
+ @attribute = attribute
15
+ @value = label
16
+ end
17
+
18
+ def call
19
+ content = if @table.sort&.supports?(@table.collection, @attribute)
20
+ sort_link(value) # writes to html_options
21
+ else
22
+ value
23
+ end
24
+
25
+ tag.th(content, **@html_options)
26
+ end
27
+
28
+ def value
29
+ if !@value.nil?
30
+ @value
31
+ elsif object_name.present?
32
+ translation
33
+ else
34
+ default_value
35
+ end
36
+ end
37
+
38
+ def translation(key = "activerecord.attributes.#{object_name}.#{@attribute}")
39
+ translate(key, default: default_value)
40
+ end
41
+
42
+ def default_value
43
+ @attribute.to_s.humanize.capitalize
44
+ end
45
+
46
+ private
47
+
48
+ def sort_link(content)
49
+ (@html_options["data"] ||= {})["sort"] = sort.status(@attribute)
50
+ link_to(content, sort_url_for(sort: sort.toggle(@attribute)))
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ class HeaderRowComponent < ViewComponent::Base # :nodoc:
6
+ include Frontend::Helper
7
+
8
+ renders_many :columns, ->(component) { component }
9
+
10
+ def initialize(table)
11
+ super()
12
+
13
+ @table = table
14
+ end
15
+
16
+ def call
17
+ content # generate content before rendering
18
+
19
+ tag.tr(**@html_options) do
20
+ columns.each do |column|
21
+ concat(column.to_s)
22
+ end
23
+ end
24
+ end
25
+
26
+ def cell(attribute, **options, &block)
27
+ with_column(@table.class.header_cell_component.new(@table, attribute, **options), &block)
28
+ end
29
+
30
+ def header?
31
+ true
32
+ end
33
+
34
+ def body?
35
+ false
36
+ end
37
+ end
38
+ end
39
+ end
@@ -28,23 +28,23 @@ module Katalyst
28
28
  end
29
29
 
30
30
  included do
31
- class_attribute :_default_table_builder, instance_accessor: false
31
+ class_attribute :_default_table_component, instance_accessor: false
32
32
  end
33
33
 
34
34
  class_methods do
35
- # Set the table builder to be used as the default for all tables
35
+ # Set the table component to be used as the default for all tables
36
36
  # in the views rendered by this controller and its subclasses.
37
37
  #
38
38
  # ==== Parameters
39
- # * <tt>builder</tt> - Default table builder, an instance of +Katalyst::Tables::Frontend::TableBuilder+
40
- def default_table_builder(builder)
41
- self._default_table_builder = builder
39
+ # * <tt>component</tt> - Default table component, an instance of +Katalyst::TableComponent+
40
+ def default_table_component(component)
41
+ self._default_table_component = component
42
42
  end
43
43
  end
44
44
 
45
- # Default table builder for the controller
46
- def default_table_builder
47
- self.class._default_table_builder
45
+ # Default table component for this controller
46
+ def default_table_component
47
+ self.class._default_table_component
48
48
  end
49
49
  end
50
50
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+
5
+ module Katalyst
6
+ module Tables
7
+ class Engine < ::Rails::Engine # :nodoc:
8
+ isolate_namespace Katalyst::Tables
9
+ end
10
+ end
11
+ end
@@ -6,6 +6,18 @@ module Katalyst
6
6
  module Helper # :nodoc:
7
7
  extend ActiveSupport::Concern
8
8
 
9
+ def initialize(**options)
10
+ super()
11
+
12
+ options(**options)
13
+ end
14
+
15
+ # Add HTML options to the current component.
16
+ def options(html: {}, **options)
17
+ @html_options = options.slice(:id, :aria, :class, :data).merge(html)
18
+ @html_options.stringify_keys!
19
+ end
20
+
9
21
  # Generates a url for applying/toggling sort for the given column.
10
22
  #
11
23
  # @param sort [String, nil] sort parameter to apply, or nil to remove sorting
@@ -14,18 +26,15 @@ module Katalyst
14
26
  # Implementation inspired by pagy's `pagy_url_for` helper.
15
27
  # Preserve any existing GET parameters
16
28
  # CAUTION: these parameters are not sanitised
17
- params = request.GET.merge("sort" => sort).except("page")
29
+ params = if sort
30
+ request.GET.merge("sort" => sort).except("page")
31
+ else
32
+ request.GET.except("page", "sort")
33
+ end
18
34
  query_string = params.empty? ? "" : "?#{Rack::Utils.build_nested_query(params)}"
19
35
 
20
36
  "#{request.path}#{query_string}"
21
37
  end
22
-
23
- private
24
-
25
- def html_options_for_table_with(html: {}, **options)
26
- html_options = options.slice(:id, :class, :data).merge(html)
27
- html_options.stringify_keys!
28
- end
29
38
  end
30
39
  end
31
40
  end
@@ -1,51 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "frontend/builder/base"
4
- require_relative "frontend/builder/body_cell"
5
- require_relative "frontend/builder/body_row"
6
- require_relative "frontend/builder/header_cell"
7
- require_relative "frontend/builder/header_row"
8
3
  require_relative "frontend/helper"
9
- require_relative "frontend/table_builder"
10
4
 
11
5
  module Katalyst
12
6
  module Tables
13
7
  # View Helper for generating HTML tables. Include in your ApplicationHelper, or similar.
14
8
  module Frontend
15
- include Helper
16
-
17
- def table_with(collection:, **options, &block)
18
- table_options = options.slice(:header, :object_name, :sort)
19
-
20
- table_options[:object_name] ||= collection.try(:model_name)&.i18n_key
21
-
22
- html_options = html_options_for_table_with(**options)
23
-
24
- builder = options.fetch(:builder) { default_table_builder_class }
25
- builder.new(self, collection, table_options, html_options).build(&block)
26
- end
27
-
28
- def table_header_row(table, builder, &block)
29
- builder.new(table).build(&block)
30
- end
31
-
32
- def table_header_cell(table, method, builder, **options, &block)
33
- builder.new(table, method, **options).build(&block)
34
- end
35
-
36
- def table_body_row(table, object, builder, &block)
37
- builder.new(table, object).build(&block)
38
- end
39
-
40
- def table_body_cell(table, object, method, builder, **options, &block)
41
- builder.new(table, object, method, **options).build(&block)
9
+ def table_with(collection:, component: nil, **options, &block)
10
+ component ||= default_table_component_class
11
+ render(component.new(collection: collection, **options), &block)
42
12
  end
43
13
 
44
14
  private
45
15
 
46
- def default_table_builder_class
47
- builder = controller.try(:default_table_builder) || TableBuilder
48
- builder.respond_to?(:constantize) ? builder.constantize : builder
16
+ def default_table_component_class
17
+ component = controller.try(:default_table_component) || TableComponent
18
+ component.respond_to?(:constantize) ? component.constantize : component
49
19
  end
50
20
  end
51
21
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Katalyst
4
4
  module Tables
5
- VERSION = "1.1.0"
5
+ VERSION = "2.0.0"
6
6
  end
7
7
  end
@@ -1,9 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "view_component"
4
+
3
5
  require_relative "tables/backend"
6
+ require_relative "tables/engine"
4
7
  require_relative "tables/frontend"
5
8
  require_relative "tables/version"
6
9
 
10
+ require_relative "tables/engine" if Object.const_defined?("Rails")
11
+
7
12
  module Katalyst
8
13
  module Tables
9
14
  class Error < StandardError; end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katalyst-tables
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katalyst Interactive
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-18 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2023-07-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: view_component
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: Builder-style HTML table generator for building tabular index views.
14
28
  Supports sorting by columns.
15
29
  email:
@@ -21,17 +35,17 @@ files:
21
35
  - CHANGELOG.md
22
36
  - LICENSE.txt
23
37
  - README.md
38
+ - app/components/katalyst/table_component.rb
39
+ - app/components/katalyst/tables/body_cell_component.rb
40
+ - app/components/katalyst/tables/body_row_component.rb
41
+ - app/components/katalyst/tables/header_cell_component.rb
42
+ - app/components/katalyst/tables/header_row_component.rb
24
43
  - lib/katalyst/tables.rb
25
44
  - lib/katalyst/tables/backend.rb
26
45
  - lib/katalyst/tables/backend/sort_form.rb
46
+ - lib/katalyst/tables/engine.rb
27
47
  - lib/katalyst/tables/frontend.rb
28
- - lib/katalyst/tables/frontend/builder/base.rb
29
- - lib/katalyst/tables/frontend/builder/body_cell.rb
30
- - lib/katalyst/tables/frontend/builder/body_row.rb
31
- - lib/katalyst/tables/frontend/builder/header_cell.rb
32
- - lib/katalyst/tables/frontend/builder/header_row.rb
33
48
  - lib/katalyst/tables/frontend/helper.rb
34
- - lib/katalyst/tables/frontend/table_builder.rb
35
49
  - lib/katalyst/tables/version.rb
36
50
  homepage: https://github.com/katalyst/katalyst-tables
37
51
  licenses:
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_support"
4
- require "active_support/core_ext/module/delegation"
5
-
6
- require_relative "../helper"
7
-
8
- module Katalyst
9
- module Tables
10
- module Frontend
11
- module Builder
12
- class Base # :nodoc:
13
- include Helper
14
-
15
- attr_reader :table
16
-
17
- delegate :sort,
18
- :table_header_cell,
19
- :table_header_row,
20
- :table_body_cell,
21
- :table_body_row,
22
- :template,
23
- to: :table
24
-
25
- delegate :content_tag,
26
- :link_to,
27
- :render,
28
- :request,
29
- :translate,
30
- :with_output_buffer,
31
- to: :template
32
-
33
- def initialize(table, **options)
34
- @table = table
35
- @header = false
36
- self.options(**options)
37
- end
38
-
39
- def header?
40
- @header
41
- end
42
-
43
- def body?
44
- !@header
45
- end
46
-
47
- def options(**options)
48
- @html_options = html_options_for_table_with(**options)
49
- end
50
-
51
- private
52
-
53
- def table_tag(type, value = nil, &block)
54
- # capture output before calling tag, to allow users to modify `options` during body execution
55
- value = with_output_buffer(&block) if block_given?
56
-
57
- content_tag(type, value, @html_options, &block)
58
- end
59
- end
60
- end
61
- end
62
- end
63
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
-
5
- module Katalyst
6
- module Tables
7
- module Frontend
8
- module Builder
9
- class BodyCell < Base # :nodoc:
10
- attr_reader :object, :method
11
-
12
- def initialize(table, object, method, **options)
13
- super table, **options
14
-
15
- @type = options.fetch(:heading, false) ? :th : :td
16
- @object = object
17
- @method = method
18
- end
19
-
20
- def build
21
- table_tag(@type) { block_given? ? yield(self).to_s : value.to_s }
22
- end
23
-
24
- def value
25
- object.public_send(method)
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
-
5
- module Katalyst
6
- module Tables
7
- module Frontend
8
- module Builder
9
- class BodyRow < Base # :nodoc:
10
- attr_reader :object
11
-
12
- def initialize(table, object)
13
- super table
14
-
15
- @object = object
16
- end
17
-
18
- def build
19
- table_tag(:tr) { yield self, object }
20
- end
21
-
22
- def cell(method, **options, &block)
23
- table_body_cell(object, method, **options, &block)
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "body_cell"
4
-
5
- module Katalyst
6
- module Tables
7
- module Frontend
8
- module Builder
9
- class HeaderCell < BodyCell # :nodoc:
10
- def initialize(table, method, **options)
11
- super(table, nil, method, **options)
12
-
13
- @value = options[:label]
14
- @header = true
15
- end
16
-
17
- def build(&_block)
18
- # NOTE: block ignored intentionally but subclasses may consume it
19
- if @table.sort&.supports?(@table.collection, method)
20
- content = sort_link(value) # writes to html_options
21
- table_tag :th, content # consumes options
22
- else
23
- table_tag :th, value
24
- end
25
- end
26
-
27
- def value
28
- if !@value.nil?
29
- @value
30
- elsif @table.object_name.present?
31
- translation
32
- else
33
- default_value
34
- end
35
- end
36
-
37
- def translation(key = "activerecord.attributes.#{@table.object_name}.#{method}")
38
- translate(key, default: default_value)
39
- end
40
-
41
- def default_value
42
- method.to_s.humanize.titleize
43
- end
44
-
45
- private
46
-
47
- def sort_link(content)
48
- (@html_options["data"] ||= {})["sort"] = sort.status(method)
49
- link_to(content, sort_url_for(sort: sort.toggle(method)))
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "body_row"
4
-
5
- module Katalyst
6
- module Tables
7
- module Frontend
8
- module Builder
9
- class HeaderRow < BodyRow # :nodoc:
10
- def initialize(table)
11
- super table, nil
12
-
13
- @header = true
14
- end
15
-
16
- def cell(method, **options, &block)
17
- table_header_cell(method, **options, &block)
18
- end
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "builder/body_cell"
4
- require_relative "builder/body_row"
5
- require_relative "builder/header_cell"
6
- require_relative "builder/header_row"
7
-
8
- module Katalyst
9
- module Tables
10
- module Frontend
11
- # Builder API for generating HTML tables from ActiveRecord.
12
- # @see Frontend#table_with
13
- class TableBuilder
14
- attr_reader :template, :collection, :object_name, :sort
15
-
16
- def initialize(template, collection, options, html_options)
17
- @template = template
18
- @collection = collection
19
- @header = options.fetch(:header, true)
20
- @object_name = options.fetch(:object_name, nil)
21
- @sort = options[:sort]
22
- @html_options = html_options
23
- end
24
-
25
- def table_header_row(builder = nil, &block)
26
- @template.table_header_row(self, builder || Builder::HeaderRow, &block)
27
- end
28
-
29
- def table_header_cell(method, builder = nil, **options, &block)
30
- @template.table_header_cell(self, method, builder || Builder::HeaderCell, **options, &block)
31
- end
32
-
33
- def table_body_row(object, builder = nil, &block)
34
- @template.table_body_row(self, object, builder || Builder::BodyRow, &block)
35
- end
36
-
37
- def table_body_cell(object, method, builder = nil, **options, &block)
38
- @template.table_body_cell(self, object, method, builder || Builder::BodyCell, **options, &block)
39
- end
40
-
41
- def build(&block)
42
- template.content_tag("table", @html_options) do
43
- thead(&block) + tbody(&block)
44
- end
45
- end
46
-
47
- private
48
-
49
- def thead(&block)
50
- return "".html_safe unless @header
51
-
52
- template.content_tag("thead") do
53
- table_header_row(&block)
54
- end
55
- end
56
-
57
- def tbody(&block)
58
- template.content_tag("tbody") do
59
- buffer = ActiveSupport::SafeBuffer.new
60
-
61
- collection.each do |object|
62
- buffer << table_body_row(object, &block)
63
- end
64
-
65
- buffer
66
- end
67
- end
68
- end
69
- end
70
- end
71
- end