katalyst-tables 2.6.0 → 3.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 +4 -4
- data/CHANGELOG.md +16 -1
- data/README.md +57 -213
- data/app/assets/builds/katalyst/tables.esm.js +17 -47
- data/app/assets/builds/katalyst/tables.js +17 -47
- data/app/assets/builds/katalyst/tables.min.js +1 -1
- data/app/assets/builds/katalyst/tables.min.js.map +1 -1
- data/app/assets/stylesheets/katalyst/tables/_index.scss +1 -0
- data/app/assets/stylesheets/katalyst/tables/_ordinal.scss +38 -0
- data/app/assets/stylesheets/katalyst/tables/_table.scss +123 -0
- data/app/assets/stylesheets/katalyst/tables/typed-columns/_boolean.scss +4 -0
- data/app/assets/stylesheets/katalyst/tables/typed-columns/_currency.scss +5 -0
- data/app/assets/stylesheets/katalyst/tables/typed-columns/_date.scss +4 -0
- data/app/assets/stylesheets/katalyst/tables/typed-columns/_datetime.scss +4 -0
- data/app/assets/stylesheets/katalyst/tables/typed-columns/_index.scss +5 -0
- data/app/assets/stylesheets/katalyst/tables/typed-columns/_number.scss +5 -0
- data/app/components/concerns/katalyst/tables/has_table_content.rb +17 -8
- data/app/components/concerns/katalyst/tables/identifiable.rb +51 -0
- data/app/components/concerns/katalyst/tables/orderable.rb +35 -105
- data/app/components/concerns/katalyst/tables/selectable.rb +18 -74
- data/app/components/concerns/katalyst/tables/sortable.rb +51 -17
- data/app/components/katalyst/table_component.html.erb +4 -4
- data/app/components/katalyst/table_component.rb +277 -47
- data/app/components/katalyst/tables/body_row_component.html.erb +5 -0
- data/app/components/katalyst/tables/body_row_component.rb +4 -24
- data/app/components/katalyst/tables/cell_component.rb +85 -0
- data/app/components/katalyst/tables/cells/boolean_component.rb +20 -0
- data/app/components/katalyst/tables/cells/currency_component.rb +29 -0
- data/app/components/katalyst/tables/cells/date_component.rb +67 -0
- data/app/components/katalyst/tables/cells/date_time_component.rb +60 -0
- data/app/components/katalyst/tables/cells/number_component.rb +22 -0
- data/app/components/katalyst/tables/cells/ordinal_component.rb +44 -0
- data/app/components/katalyst/tables/cells/rich_text_component.rb +21 -0
- data/app/components/katalyst/tables/cells/select_component.rb +39 -0
- data/app/components/katalyst/tables/data.rb +30 -0
- data/app/components/katalyst/tables/empty_caption_component.rb +1 -1
- data/app/components/katalyst/tables/header_row_component.html.erb +5 -0
- data/app/components/katalyst/tables/header_row_component.rb +4 -24
- data/app/components/katalyst/tables/label.rb +37 -0
- data/app/components/katalyst/tables/orderable/form_component.rb +38 -0
- data/app/components/katalyst/tables/selectable/form_component.html.erb +6 -4
- data/app/components/katalyst/tables/selectable/form_component.rb +8 -11
- data/app/controllers/concerns/katalyst/tables/backend.rb +2 -28
- data/app/helpers/katalyst/tables/frontend.rb +48 -2
- data/app/javascript/tables/application.js +0 -5
- data/app/javascript/tables/orderable/form_controller.js +8 -6
- data/app/javascript/tables/orderable/item_controller.js +9 -0
- data/app/models/concerns/katalyst/tables/collection/core.rb +6 -1
- data/app/models/concerns/katalyst/tables/collection/pagination.rb +8 -1
- data/app/models/concerns/katalyst/tables/collection/sorting.rb +85 -17
- data/app/models/katalyst/tables/collection/array.rb +38 -0
- data/app/models/katalyst/tables/collection/base.rb +4 -0
- data/lib/katalyst/tables/config.rb +23 -0
- data/lib/katalyst/tables.rb +9 -0
- metadata +32 -15
- data/app/components/concerns/katalyst/tables/configurable_component.rb +0 -52
- data/app/components/concerns/katalyst/tables/turbo_replaceable.rb +0 -79
- data/app/components/katalyst/tables/body_cell_component.rb +0 -47
- data/app/components/katalyst/tables/header_cell_component.rb +0 -65
- data/app/components/katalyst/turbo/pagy_nav_component.rb +0 -23
- data/app/components/katalyst/turbo/table_component.rb +0 -45
- data/app/helpers/katalyst/tables/frontend/helper.rb +0 -31
- data/app/javascript/tables/turbo/collection_controller.js +0 -38
- data/app/models/katalyst/tables/collection/sort_form.rb +0 -102
@@ -10,82 +10,26 @@ module Katalyst
|
|
10
10
|
FORM_CONTROLLER = "tables--selection--form"
|
11
11
|
ITEM_CONTROLLER = "tables--selection--item"
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
included do
|
18
|
-
# Add `selectable` slot to table component
|
19
|
-
config_component :selection, default: "Katalyst::Tables::Selectable::FormComponent"
|
20
|
-
renders_one(:selection, lambda do |**attrs|
|
21
|
-
selection_component.new(table: self, **attrs)
|
22
|
-
end)
|
23
|
-
end
|
24
|
-
|
25
|
-
# Support for extending a table component instance
|
26
|
-
# Adds methods to the table component instance
|
27
|
-
def self.extended(table)
|
28
|
-
table.extend(TableMethods)
|
29
|
-
|
30
|
-
# ensure row components support selectable column calls
|
31
|
-
table.send(:add_selectable_columns)
|
32
|
-
end
|
33
|
-
|
34
|
-
def initialize(**attributes)
|
35
|
-
super
|
36
|
-
|
37
|
-
# ensure row components support selectable column calls
|
38
|
-
add_selectable_columns
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
# Add `selectable` columns to row components
|
44
|
-
def add_selectable_columns
|
45
|
-
header_row_component.include(HeaderRow)
|
46
|
-
body_row_component.include(BodyRow)
|
47
|
-
end
|
48
|
-
|
49
|
-
# Methods required to emulate a slot when extending an existing table.
|
50
|
-
module TableMethods
|
51
|
-
def with_selection(**attrs)
|
52
|
-
@selection = FormComponent.new(table: self, **attrs)
|
53
|
-
|
54
|
-
self
|
55
|
-
end
|
56
|
-
|
57
|
-
def selectable?
|
58
|
-
@selection.present?
|
59
|
-
end
|
60
|
-
|
61
|
-
def selection
|
62
|
-
@selection ||= FormComponent.new(table: self)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
module HeaderRow # :nodoc:
|
67
|
-
def selection
|
68
|
-
cell(:_selection, class: "selection", label: "")
|
69
|
-
end
|
13
|
+
# Returns the default dom id for the selection form, uses the table's
|
14
|
+
# default id with '_selection' appended.
|
15
|
+
def self.default_form_id(collection)
|
16
|
+
"#{Identifiable::Defaults.default_table_id(collection)}_selection_form"
|
70
17
|
end
|
71
18
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
tag.input(type: :checkbox)
|
87
|
-
end
|
88
|
-
end
|
19
|
+
# Adds the selection column to the table
|
20
|
+
#
|
21
|
+
# @param params [Hash] params to pass to the controller for selected rows
|
22
|
+
# @param form_id [String] id of the form element that will submit the selected row params
|
23
|
+
# @param ** [Hash] HTML attributes to be added to column cells
|
24
|
+
# @param & [Proc] optional block to alter the cell content
|
25
|
+
# @return [void]
|
26
|
+
#
|
27
|
+
# @example Render a select column
|
28
|
+
# <% row.select %> # => <td><input type="checkbox" ...></td>
|
29
|
+
def select(params: { id: record&.id }, form_id: Selectable.default_form_id(collection), **, &)
|
30
|
+
with_cell(Cells::SelectComponent.new(
|
31
|
+
collection:, row:, column: :_select, record:, label: "", heading: false, params:, form_id:, **,
|
32
|
+
), &)
|
89
33
|
end
|
90
34
|
end
|
91
35
|
end
|
@@ -7,25 +7,59 @@ module Katalyst
|
|
7
7
|
module Sortable
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
def initialize(**)
|
11
|
+
super(**)
|
12
|
+
|
13
|
+
@header_row_cell_callbacks << method(:add_sorting_to_cell) if collection.sortable?
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def add_sorting_to_cell(cell)
|
19
|
+
if collection.sortable?(cell.column)
|
20
|
+
cell.update_html_attributes(data: { sort: collection.sort_status(cell.column) })
|
21
|
+
cell.with_content_wrapper(SortableHeaderComponent.new(collection:, cell:))
|
22
|
+
end
|
13
23
|
end
|
14
24
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
class SortableHeaderComponent < ViewComponent::Base
|
26
|
+
include Katalyst::HtmlAttributes
|
27
|
+
|
28
|
+
attr_reader :collection, :cell
|
29
|
+
|
30
|
+
delegate :column, to: :cell
|
31
|
+
|
32
|
+
def initialize(collection:, cell:, **)
|
33
|
+
super(**)
|
34
|
+
|
35
|
+
@collection = collection
|
36
|
+
@cell = cell
|
37
|
+
end
|
38
|
+
|
39
|
+
def call
|
40
|
+
link_to(content, sort_url, **html_attributes)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Generates a url for applying/toggling sort for the given column.
|
44
|
+
def sort_url
|
45
|
+
# rubocop:disable Metrics/AbcSize
|
46
|
+
# Implementation inspired by pagy's `pagy_url_for` helper.
|
47
|
+
# Preserve any existing GET parameters
|
48
|
+
# CAUTION: these parameters are not sanitised
|
49
|
+
sort = column && collection.toggle_sort(column)
|
50
|
+
params = if sort && !sort.eql?(collection.default_sort)
|
51
|
+
request.GET.merge("sort" => sort).except("page")
|
52
|
+
else
|
53
|
+
request.GET.except("page", "sort")
|
54
|
+
end
|
55
|
+
query_string = params.empty? ? "" : "?#{Rack::Utils.build_nested_query(params)}"
|
56
|
+
|
57
|
+
"#{request.path}#{query_string}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def default_html_attributes
|
61
|
+
{ data: { turbo_action: "replace" } }
|
62
|
+
end
|
29
63
|
end
|
30
64
|
end
|
31
65
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
<%= tag.table(**html_attributes) do %>
|
2
2
|
<%= render caption if caption? %>
|
3
|
-
<% if
|
3
|
+
<% if header_row? %>
|
4
4
|
<%= tag.thead(**thead_attributes) do %>
|
5
|
-
<%= header_row
|
5
|
+
<%= header_row %>
|
6
6
|
<% end %>
|
7
7
|
<% end %>
|
8
8
|
<%= tag.tbody(**tbody_attributes) do %>
|
9
|
-
<%
|
10
|
-
<%= body_row
|
9
|
+
<% body_rows.each do |body_row| %>
|
10
|
+
<%= body_row %>
|
11
11
|
<% end %>
|
12
12
|
<% end %>
|
13
13
|
<% end %>
|
@@ -4,91 +4,321 @@ module Katalyst
|
|
4
4
|
# A component for rendering a table from a collection, with a header row.
|
5
5
|
# ```erb
|
6
6
|
# <%= Katalyst::TableComponent.new(collection: @people) do |row, person| %>
|
7
|
-
# <%= row.
|
7
|
+
# <%= row.text :name do |cell| %>
|
8
8
|
# <%= link_to cell.value, person %>
|
9
9
|
# <% end %>
|
10
|
-
# <%= row.
|
10
|
+
# <%= row.text :email %>
|
11
11
|
# <% end %>
|
12
12
|
# ```
|
13
13
|
class TableComponent < ViewComponent::Base
|
14
14
|
include Katalyst::HtmlAttributes
|
15
|
-
include Tables::ConfigurableComponent
|
16
15
|
include Tables::HasTableContent
|
17
16
|
|
17
|
+
# Load table extensions. This allows users to disable specific extensions
|
18
|
+
# if they want to implement alternatives, e.g. a different sorting UI.
|
19
|
+
Katalyst::Tables.config.component_extensions.each do |extension|
|
20
|
+
include extension.constantize
|
21
|
+
end
|
22
|
+
|
18
23
|
attr_reader :collection, :object_name
|
19
24
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
+
renders_one :caption, Katalyst::Tables::EmptyCaptionComponent
|
26
|
+
renders_one :header_row, Katalyst::Tables::HeaderRowComponent
|
27
|
+
renders_many :body_rows, Katalyst::Tables::BodyRowComponent
|
28
|
+
|
29
|
+
define_html_attribute_methods(:thead_attributes)
|
30
|
+
define_html_attribute_methods(:tbody_attributes)
|
25
31
|
|
26
32
|
# Construct a new table component. This entry point supports a large number
|
27
33
|
# of options for customizing the table. The most common options are:
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
34
|
+
# @param collection [Katalyst::Tables::Collection::Core] the collection to render
|
35
|
+
# @param header [Boolean] whether to render the header row (defaults to true, supports options)
|
36
|
+
# @param caption [Boolean,Hash] whether to render the caption (defaults to true, supports options)
|
37
|
+
# @param generate_ids [Boolean] whether to generate dom ids for the table and rows
|
38
|
+
#
|
39
|
+
# If no block is provided when the table is rendered then the table will look for a row partial:
|
40
|
+
# @param object_name [Symbol] the name of the object to use for partial rendering
|
41
|
+
# (defaults to collection.model_name.i18n_key)
|
42
|
+
# @param partial [String] the name of the partial to use for rendering each row
|
43
|
+
# (defaults to to_partial_path on the object)
|
44
|
+
# @param as [Symbol] the name of the local variable to use for rendering each row
|
45
|
+
# (defaults to collection.model_name.param_key)
|
46
|
+
#
|
35
47
|
# In addition to these options, standard HTML attributes can be passed which will be added to the table tag.
|
36
48
|
def initialize(collection:,
|
37
|
-
sorting: nil,
|
38
49
|
header: true,
|
39
|
-
caption:
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
@
|
50
|
+
caption: true,
|
51
|
+
generate_ids: false,
|
52
|
+
object_name: nil,
|
53
|
+
partial: nil,
|
54
|
+
as: nil,
|
55
|
+
**)
|
56
|
+
@collection = normalize_collection(collection)
|
46
57
|
|
47
58
|
# header: true means render the header row, header: false means no header row, if a hash, passes as options
|
48
|
-
@
|
49
|
-
@header_options = (header if header.is_a?(Hash)) || {}
|
59
|
+
@header_options = header
|
50
60
|
|
51
61
|
# caption: true means render the caption, caption: false means no caption, if a hash, passes as options
|
52
|
-
@
|
53
|
-
|
62
|
+
@caption_options = caption
|
63
|
+
|
64
|
+
@header_row_callbacks = []
|
65
|
+
@body_row_callbacks = []
|
66
|
+
@header_row_cell_callbacks = []
|
67
|
+
@body_row_cell_callbacks = []
|
54
68
|
|
55
|
-
super(**
|
69
|
+
super(generate_ids:, object_name:, partial:, as:, **)
|
56
70
|
end
|
57
71
|
|
58
|
-
def
|
59
|
-
|
72
|
+
def before_render
|
73
|
+
super
|
74
|
+
|
75
|
+
if @caption_options
|
76
|
+
options = (@caption_options.is_a?(Hash) ? @caption_options : {})
|
77
|
+
with_caption(self, **options)
|
78
|
+
end
|
79
|
+
|
80
|
+
if @header_options
|
81
|
+
options = @header_options.is_a?(Hash) ? @header_options : {}
|
82
|
+
with_header_row(**options) do |row|
|
83
|
+
@header_row_callbacks.each { |callback| callback.call(row, record) }
|
84
|
+
row_content(row, nil)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
collection.each do |record|
|
89
|
+
with_body_row do |row|
|
90
|
+
@body_row_callbacks.each { |callback| callback.call(row, record) }
|
91
|
+
row_content(row, record)
|
92
|
+
end
|
93
|
+
end
|
60
94
|
end
|
61
95
|
|
62
|
-
def
|
63
|
-
|
96
|
+
def inspect
|
97
|
+
"#<#{self.class.name} collection: #{collection.inspect}>"
|
64
98
|
end
|
65
99
|
|
66
|
-
|
67
|
-
|
100
|
+
delegate :header?, :body?, to: :@current_row
|
101
|
+
|
102
|
+
def row
|
103
|
+
@current_row
|
68
104
|
end
|
69
105
|
|
70
|
-
def
|
71
|
-
|
106
|
+
def record
|
107
|
+
@current_record
|
72
108
|
end
|
73
109
|
|
74
|
-
|
75
|
-
|
110
|
+
# When rendering a row we pass the table to the row instead of the row itself. This lets the table define the
|
111
|
+
# column entry points so it's easy to define column extensions in subclasses. When a user wants to set html
|
112
|
+
# attributes on the row, they will call `row.html_attributes = { ... }`, so we need to proxy that call to the
|
113
|
+
# current row (if set).
|
114
|
+
def html_attributes=(attributes)
|
115
|
+
if row.present?
|
116
|
+
row.html_attributes = attributes
|
117
|
+
else
|
118
|
+
@html_attributes = HtmlAttributes.options_to_html_attributes(attributes)
|
119
|
+
end
|
76
120
|
end
|
77
121
|
|
78
|
-
|
79
|
-
|
122
|
+
# Generates a column from values rendered as text.
|
123
|
+
#
|
124
|
+
# @param column [Symbol] the column's name, called as a method on the record
|
125
|
+
# @param label [String|nil] the label to use for the column header
|
126
|
+
# @param heading [boolean] if true, data cells will use `th` tags
|
127
|
+
# @param ** [Hash] HTML attributes to be added to column cells
|
128
|
+
# @param & [Proc] optional block to wrap the cell content
|
129
|
+
#
|
130
|
+
# If a block is provided, it will be called with the cell component as an argument.
|
131
|
+
# @yieldparam cell [Katalyst::Tables::CellComponent] the cell component
|
132
|
+
#
|
133
|
+
# @return [void]
|
134
|
+
#
|
135
|
+
# @example Render a generic text column for any value that supports `to_s`
|
136
|
+
# <% row.text :name %> # label => <th>Name</th>, data => <td>John Doe</td>
|
137
|
+
def text(column, label: nil, heading: false, **, &)
|
138
|
+
with_cell(Tables::CellComponent.new(
|
139
|
+
collection:, row:, column:, record:, label:, heading:, **,
|
140
|
+
), &)
|
141
|
+
end
|
142
|
+
alias cell text
|
80
143
|
|
81
|
-
|
144
|
+
# Generates a column from boolean values rendered as "Yes" or "No".
|
145
|
+
#
|
146
|
+
# @param column [Symbol] the column's name, called as a method on the record
|
147
|
+
# @param label [String|nil] the label to use for the column header
|
148
|
+
# @param heading [boolean] if true, data cells will use `th` tags
|
149
|
+
# @param ** [Hash] HTML attributes to be added to column cells
|
150
|
+
# @param & [Proc] optional block to alter the cell content
|
151
|
+
#
|
152
|
+
# If a block is provided, it will be called with the boolean cell component as an argument.
|
153
|
+
# @yieldparam cell [Katalyst::Tables::Cells::BooleanComponent] the cell component
|
154
|
+
#
|
155
|
+
# @return [void]
|
156
|
+
#
|
157
|
+
# @example Render a boolean column indicating whether the record is active
|
158
|
+
# <% row.boolean :active %> # => <td>Yes</td>
|
159
|
+
def boolean(column, label: nil, heading: false, **, &)
|
160
|
+
with_cell(Tables::Cells::BooleanComponent.new(
|
161
|
+
collection:, row:, column:, record:, label:, heading:, **,
|
162
|
+
), &)
|
82
163
|
end
|
83
164
|
|
84
|
-
|
85
|
-
|
165
|
+
# Generates a column from date values rendered using I18n.l.
|
166
|
+
# The default format is :default, can be configured or overridden.
|
167
|
+
#
|
168
|
+
# @param column [Symbol] the column's name, called as a method on the record
|
169
|
+
# @param label [String|nil] the label to use for the column header
|
170
|
+
# @param heading [boolean] if true, data cells will use `th` tags
|
171
|
+
# @param format [Symbol] the I18n date format to use when rendering
|
172
|
+
# @param relative [Boolean] if true, the date may be shown as a relative date (if within 5 days)
|
173
|
+
# @param ** [Hash] HTML attributes to be added to column cells
|
174
|
+
#
|
175
|
+
# If a block is provided, it will be called with the date cell component as an argument.
|
176
|
+
# @yieldparam cell [Katalyst::Tables::Cells::DateComponent] the cell component
|
177
|
+
#
|
178
|
+
# @return [void]
|
179
|
+
#
|
180
|
+
# @example Render a date column describing when the record was created
|
181
|
+
# <% row.date :created_at %> # => <td>29 Feb 2024</td>
|
182
|
+
def date(column, label: nil, heading: false, format: Tables.config.date_format, relative: true, **, &)
|
183
|
+
with_cell(Tables::Cells::DateComponent.new(
|
184
|
+
collection:, row:, column:, record:, label:, heading:, format:, relative:, **,
|
185
|
+
), &)
|
86
186
|
end
|
87
187
|
|
88
|
-
|
89
|
-
|
188
|
+
# Generates a column from datetime values rendered using I18n.l.
|
189
|
+
# The default format is :default, can be configured or overridden.
|
190
|
+
#
|
191
|
+
# @param column [Symbol] the column's name, called as a method on the record
|
192
|
+
# @param label [String|nil] the label to use for the column header
|
193
|
+
# @param heading [boolean] if true, data cells will use `th` tags
|
194
|
+
# @param format [Symbol] the I18n datetime format to use when rendering
|
195
|
+
# @param relative [Boolean] if true, the datetime may be(if today) shown as a relative date/time
|
196
|
+
# @param ** [Hash] HTML attributes to be added to column cells
|
197
|
+
# @param & [Proc] optional block to alter the cell content
|
198
|
+
#
|
199
|
+
# If a block is provided, it will be called with the date time cell component as an argument.
|
200
|
+
# @yieldparam cell [Katalyst::Tables::Cells::DateTimeComponent] the cell component
|
201
|
+
#
|
202
|
+
# @return [void]
|
203
|
+
#
|
204
|
+
# @example Render a datetime column describing when the record was created
|
205
|
+
# <% row.datetime :created_at %> # => <td>29 Feb 2024, 5:00pm</td>
|
206
|
+
def datetime(column, label: nil, heading: false, format: Tables.config.datetime_format, relative: true, **, &)
|
207
|
+
with_cell(Tables::Cells::DateTimeComponent.new(
|
208
|
+
collection:, row:, column:, record:, label:, heading:, format:, relative:, **,
|
209
|
+
), &)
|
210
|
+
end
|
90
211
|
|
91
|
-
#
|
92
|
-
|
212
|
+
# Generates a column from numeric values formatted appropriately.
|
213
|
+
#
|
214
|
+
# @param column [Symbol] the column's name, called as a method on the record
|
215
|
+
# @param label [String|nil] the label to use for the column header
|
216
|
+
# @param heading [boolean] if true, data cells will use `th` tags
|
217
|
+
# @param ** [Hash] HTML attributes to be added to column cells
|
218
|
+
# @param & [Proc] optional block to alter the cell content
|
219
|
+
#
|
220
|
+
# If a block is provided, it will be called with the number cell component as an argument.
|
221
|
+
# @yieldparam cell [Katalyst::Tables::Cells::NumberComponent] the cell component
|
222
|
+
#
|
223
|
+
# @return [void]
|
224
|
+
#
|
225
|
+
# @example Render the number of comments on a post
|
226
|
+
# <% row.number :comment_count %> # => <td>0</td>
|
227
|
+
def number(column, label: nil, heading: false, **, &)
|
228
|
+
with_cell(Tables::Cells::NumberComponent.new(
|
229
|
+
collection:, row:, column:, record:, label:, heading:, **,
|
230
|
+
), &)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Generates a column from numeric values rendered using `number_to_currency`.
|
234
|
+
#
|
235
|
+
# @param column [Symbol] the column's name, called as a method on the record
|
236
|
+
# @param label [String|nil] the label to use for the column header
|
237
|
+
# @param heading [boolean] if true, data cells will use `th` tags
|
238
|
+
# @param options [Hash] options to be passed to `number_to_currency`
|
239
|
+
# @param ** [Hash] HTML attributes to be added to column cells
|
240
|
+
# @param & [Proc] optional block to alter the cell content
|
241
|
+
#
|
242
|
+
# If a block is provided, it will be called with the currency cell component as an argument.
|
243
|
+
# @yieldparam cell [Katalyst::Tables::Cells::CurrencyComponent] the cell component
|
244
|
+
#
|
245
|
+
# @return [void]
|
246
|
+
#
|
247
|
+
# @example Render a currency column for the price of a product
|
248
|
+
# <% row.currency :price %> # => <td>$3.50</td>
|
249
|
+
def currency(column, label: nil, heading: false, options: {}, **, &)
|
250
|
+
with_cell(Tables::Cells::CurrencyComponent.new(
|
251
|
+
collection:, row:, column:, record:, label:, heading:, options:, **,
|
252
|
+
), &)
|
253
|
+
end
|
254
|
+
|
255
|
+
# Generates a column containing HTML markup.
|
256
|
+
#
|
257
|
+
# @param column [Symbol] the column's name, called as a method on the record
|
258
|
+
# @param label [String|nil] the label to use for the column header
|
259
|
+
# @param heading [boolean] if true, data cells will use `th` tags
|
260
|
+
# @param ** [Hash] HTML attributes to be added to column cells
|
261
|
+
# @param & [Proc] optional block to alter the cell content
|
262
|
+
#
|
263
|
+
# If a block is provided, it will be called with the rich text cell component as an argument.
|
264
|
+
# @yieldparam cell [Katalyst::Tables::Cells::RichTextComponent] the cell component
|
265
|
+
#
|
266
|
+
# @return [void]
|
267
|
+
#
|
268
|
+
# @note This method assumes that the method returns HTML-safe content.
|
269
|
+
# If the content is not HTML-safe, it will be escaped.
|
270
|
+
#
|
271
|
+
# @example Render a description column containing HTML markup
|
272
|
+
# <% row.rich_text :description %> # => <td><em>Emphasis</em></td>
|
273
|
+
def rich_text(column, label: nil, heading: false, options: {}, **, &)
|
274
|
+
with_cell(Tables::Cells::RichTextComponent.new(
|
275
|
+
collection:, row:, column:, record:, label:, heading:, options:, **,
|
276
|
+
), &)
|
277
|
+
end
|
278
|
+
|
279
|
+
private
|
280
|
+
|
281
|
+
# Extension point for subclasses and extensions to customize header row rendering.
|
282
|
+
def add_header_row_callback(&block)
|
283
|
+
@header_row_callbacks << block
|
284
|
+
end
|
285
|
+
|
286
|
+
# Extension point for subclasses and extensions to customize body row rendering.
|
287
|
+
def add_body_row_callback(&block)
|
288
|
+
@body_row_callbacks << block
|
289
|
+
end
|
290
|
+
|
291
|
+
# Extension point for subclasses and extensions to customize header row cell rendering.
|
292
|
+
def add_header_row_cell_callback(&block)
|
293
|
+
@header_row_cell_callbacks << block
|
294
|
+
end
|
295
|
+
|
296
|
+
# Extension point for subclasses and extensions to customize body row cell rendering.
|
297
|
+
def add_body_row_cell_callback(&block)
|
298
|
+
@body_row_cell_callbacks << block
|
299
|
+
end
|
300
|
+
|
301
|
+
# @internal proxy calls to row.with_cell and apply callbacks
|
302
|
+
def with_cell(cell, &)
|
303
|
+
if row.header?
|
304
|
+
@header_row_cell_callbacks.each { |callback| callback.call(cell) }
|
305
|
+
# note, block is silently dropped, it's not used for headers
|
306
|
+
@current_row.with_cell(cell)
|
307
|
+
else
|
308
|
+
@body_row_cell_callbacks.each { |callback| callback.call(cell) }
|
309
|
+
@current_row.with_cell(cell, &)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
def normalize_collection(collection)
|
314
|
+
case collection
|
315
|
+
when Array
|
316
|
+
Tables::Collection::Array.new.apply(collection)
|
317
|
+
when ActiveRecord::Relation
|
318
|
+
Tables::Collection::Base.new.apply(collection)
|
319
|
+
else
|
320
|
+
collection
|
321
|
+
end
|
322
|
+
end
|
93
323
|
end
|
94
324
|
end
|
@@ -5,27 +5,10 @@ module Katalyst
|
|
5
5
|
class BodyRowComponent < ViewComponent::Base # :nodoc:
|
6
6
|
include Katalyst::HtmlAttributes
|
7
7
|
|
8
|
-
renders_many :
|
8
|
+
renders_many :cells, ->(cell) { cell }
|
9
9
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
@table = table
|
14
|
-
@record = record
|
15
|
-
end
|
16
|
-
|
17
|
-
def call
|
18
|
-
content # generate content before rendering
|
19
|
-
|
20
|
-
tag.tr(**html_attributes) do
|
21
|
-
columns.each do |column|
|
22
|
-
concat(column.to_s)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def cell(attribute, **, &)
|
28
|
-
with_column(@table.body_cell_component.new(@table, @record, attribute, **), &)
|
10
|
+
def before_render
|
11
|
+
content # ensure content is rendered so html_attributes can be set
|
29
12
|
end
|
30
13
|
|
31
14
|
def header?
|
@@ -37,11 +20,8 @@ module Katalyst
|
|
37
20
|
end
|
38
21
|
|
39
22
|
def inspect
|
40
|
-
"#<#{self.class.name}
|
23
|
+
"#<#{self.class.name}>"
|
41
24
|
end
|
42
|
-
|
43
|
-
# Backwards compatibility with tables 1.0
|
44
|
-
alias_method :options, :html_attributes=
|
45
25
|
end
|
46
26
|
end
|
47
27
|
end
|