katalyst-tables 1.1.0 → 2.0.0

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