katalyst-tables 3.0.0.beta1 → 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 +12 -2
- data/README.md +56 -190
- 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/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 -75
- 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 +271 -53
- data/app/components/katalyst/tables/body_row_component.html.erb +5 -0
- data/app/components/katalyst/tables/body_row_component.rb +4 -31
- data/app/components/katalyst/tables/cell_component.rb +85 -0
- data/app/components/katalyst/tables/{body → cells}/boolean_component.rb +8 -2
- data/app/components/katalyst/tables/{body → cells}/currency_component.rb +7 -7
- data/app/components/katalyst/tables/{body → cells}/date_component.rb +12 -9
- data/app/components/katalyst/tables/{body → cells}/date_time_component.rb +13 -10
- data/app/components/katalyst/tables/{body → cells}/number_component.rb +5 -5
- data/app/components/katalyst/tables/cells/ordinal_component.rb +44 -0
- data/app/components/katalyst/tables/{body → cells}/rich_text_component.rb +8 -5
- 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/header_row_component.html.erb +5 -0
- data/app/components/katalyst/tables/header_row_component.rb +4 -25
- 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 +3 -3
- 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/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/config/locales/tables.en.yml +0 -6
- data/lib/katalyst/tables/config.rb +23 -0
- data/lib/katalyst/tables.rb +9 -0
- metadata +22 -29
- data/app/components/concerns/katalyst/tables/body/typed_columns.rb +0 -132
- data/app/components/concerns/katalyst/tables/configurable_component.rb +0 -52
- data/app/components/concerns/katalyst/tables/header/typed_columns.rb +0 -179
- data/app/components/katalyst/tables/body/attachment_component.rb +0 -58
- data/app/components/katalyst/tables/body/link_component.rb +0 -40
- data/app/components/katalyst/tables/body_cell_component.rb +0 -55
- data/app/components/katalyst/tables/header/attachment_component.rb +0 -15
- data/app/components/katalyst/tables/header/boolean_component.rb +0 -15
- data/app/components/katalyst/tables/header/currency_component.rb +0 -15
- data/app/components/katalyst/tables/header/date_component.rb +0 -15
- data/app/components/katalyst/tables/header/date_time_component.rb +0 -15
- data/app/components/katalyst/tables/header/link_component.rb +0 -15
- data/app/components/katalyst/tables/header/number_component.rb +0 -15
- data/app/components/katalyst/tables/header/rich_text_component.rb +0 -15
- data/app/components/katalyst/tables/header_cell_component.rb +0 -97
- 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 -120
@@ -4,103 +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
|
-
#
|
35
|
-
#
|
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
|
+
#
|
36
47
|
# In addition to these options, standard HTML attributes can be passed which will be added to the table tag.
|
37
48
|
def initialize(collection:,
|
38
|
-
sorting: nil,
|
39
49
|
header: true,
|
40
50
|
caption: true,
|
41
|
-
generate_ids:
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@sorting = sorting || html_attributes.delete(:sort) # backwards compatibility with old `sort` option
|
51
|
+
generate_ids: false,
|
52
|
+
object_name: nil,
|
53
|
+
partial: nil,
|
54
|
+
as: nil,
|
55
|
+
**)
|
56
|
+
@collection = normalize_collection(collection)
|
48
57
|
|
49
58
|
# header: true means render the header row, header: false means no header row, if a hash, passes as options
|
50
|
-
@
|
51
|
-
@header_options = (header if header.is_a?(Hash)) || {}
|
59
|
+
@header_options = header
|
52
60
|
|
53
61
|
# caption: true means render the caption, caption: false means no caption, if a hash, passes as options
|
54
|
-
@
|
55
|
-
@caption_options = (caption if caption.is_a?(Hash)) || {}
|
62
|
+
@caption_options = caption
|
56
63
|
|
57
|
-
@
|
64
|
+
@header_row_callbacks = []
|
65
|
+
@body_row_callbacks = []
|
66
|
+
@header_row_cell_callbacks = []
|
67
|
+
@body_row_cell_callbacks = []
|
58
68
|
|
59
|
-
super(**
|
69
|
+
super(generate_ids:, object_name:, partial:, as:, **)
|
60
70
|
end
|
61
71
|
|
62
|
-
def
|
63
|
-
|
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
|
64
94
|
end
|
65
95
|
|
66
|
-
def
|
67
|
-
|
96
|
+
def inspect
|
97
|
+
"#<#{self.class.name} collection: #{collection.inspect}>"
|
68
98
|
end
|
69
99
|
|
70
|
-
|
71
|
-
|
100
|
+
delegate :header?, :body?, to: :@current_row
|
101
|
+
|
102
|
+
def row
|
103
|
+
@current_row
|
72
104
|
end
|
73
105
|
|
74
|
-
def
|
75
|
-
@
|
106
|
+
def record
|
107
|
+
@current_record
|
76
108
|
end
|
77
109
|
|
78
|
-
|
79
|
-
|
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
|
80
120
|
end
|
81
121
|
|
82
|
-
|
83
|
-
|
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
|
+
), &)
|
84
141
|
end
|
142
|
+
alias cell text
|
85
143
|
|
86
|
-
|
87
|
-
|
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
|
+
), &)
|
163
|
+
end
|
88
164
|
|
89
|
-
|
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
|
+
), &)
|
90
186
|
end
|
91
187
|
|
92
|
-
|
93
|
-
|
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
|
+
), &)
|
94
210
|
end
|
95
211
|
|
96
|
-
|
97
|
-
|
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
|
+
), &)
|
98
231
|
end
|
99
232
|
|
100
|
-
|
101
|
-
|
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
|
102
312
|
|
103
|
-
|
104
|
-
|
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
|
105
323
|
end
|
106
324
|
end
|
@@ -4,29 +4,11 @@ module Katalyst
|
|
4
4
|
module Tables
|
5
5
|
class BodyRowComponent < ViewComponent::Base # :nodoc:
|
6
6
|
include Katalyst::HtmlAttributes
|
7
|
-
include Body::TypedColumns
|
8
7
|
|
9
|
-
renders_many :
|
8
|
+
renders_many :cells, ->(cell) { cell }
|
10
9
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
@table = table
|
15
|
-
@record = record
|
16
|
-
end
|
17
|
-
|
18
|
-
def call
|
19
|
-
content # generate content before rendering
|
20
|
-
|
21
|
-
tag.tr(**html_attributes) do
|
22
|
-
columns.each do |column|
|
23
|
-
concat(column.to_s)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def cell(attribute, **, &)
|
29
|
-
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
|
30
12
|
end
|
31
13
|
|
32
14
|
def header?
|
@@ -37,18 +19,9 @@ module Katalyst
|
|
37
19
|
true
|
38
20
|
end
|
39
21
|
|
40
|
-
def default_html_attributes
|
41
|
-
return {} unless @table.generate_ids?
|
42
|
-
|
43
|
-
{ id: dom_id(@record) }
|
44
|
-
end
|
45
|
-
|
46
22
|
def inspect
|
47
|
-
"#<#{self.class.name}
|
23
|
+
"#<#{self.class.name}>"
|
48
24
|
end
|
49
|
-
|
50
|
-
# Backwards compatibility with tables 1.0
|
51
|
-
alias_method :options, :html_attributes=
|
52
25
|
end
|
53
26
|
end
|
54
27
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Katalyst
|
4
|
+
module Tables
|
5
|
+
class CellComponent < ViewComponent::Base # :nodoc:
|
6
|
+
include Katalyst::HtmlAttributes
|
7
|
+
|
8
|
+
attr_reader :collection, :row, :column, :record
|
9
|
+
|
10
|
+
def initialize(collection:, row:, column:, record:, label:, heading:, **)
|
11
|
+
super(**)
|
12
|
+
|
13
|
+
@collection = collection
|
14
|
+
@row = row
|
15
|
+
@column = column
|
16
|
+
@record = record
|
17
|
+
@heading = heading
|
18
|
+
|
19
|
+
if @row.header?
|
20
|
+
@label = Label.new(collection:, column:, label:)
|
21
|
+
else
|
22
|
+
@data = Data.new(record:, column:)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return true if the cell is a heading cell (th).
|
27
|
+
def heading?
|
28
|
+
@row.header? || @heading
|
29
|
+
end
|
30
|
+
|
31
|
+
# Adds a component to wrap the content of the cell, similar to a layout in Rails views.
|
32
|
+
def with_content_wrapper(component)
|
33
|
+
@content_wrapper = component
|
34
|
+
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def call
|
39
|
+
content = if content?
|
40
|
+
self.content
|
41
|
+
elsif @row.header?
|
42
|
+
label
|
43
|
+
else
|
44
|
+
rendered_value
|
45
|
+
end
|
46
|
+
|
47
|
+
content = @content_wrapper.with_content(content).render_in(view_context) if @content_wrapper
|
48
|
+
|
49
|
+
concat(content_tag(cell_tag, content, **html_attributes))
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return the rendered and sanitized label for the column.
|
53
|
+
def label
|
54
|
+
@label&.to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return the raw value of the cell (i.e. the value of the data read from the record)
|
58
|
+
def value
|
59
|
+
@data&.value
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return the serialized and sanitised data value for rendering in the cell.
|
63
|
+
def rendered_value
|
64
|
+
@data&.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
# Serialize data for use in blocks, i.e.
|
68
|
+
# row.text(:name) { |cell| tag.span(cell) }
|
69
|
+
def to_s
|
70
|
+
# Note, this can't be `content` because the block is evaluated in order to produce content.
|
71
|
+
rendered_value
|
72
|
+
end
|
73
|
+
|
74
|
+
def inspect
|
75
|
+
"#<#{self.class.name} method: #{@method.inspect}>"
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def cell_tag
|
81
|
+
heading? ? :th : :td
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -2,12 +2,18 @@
|
|
2
2
|
|
3
3
|
module Katalyst
|
4
4
|
module Tables
|
5
|
-
module
|
5
|
+
module Cells
|
6
6
|
# Shows Yes/No for boolean values
|
7
|
-
class BooleanComponent <
|
7
|
+
class BooleanComponent < CellComponent
|
8
8
|
def rendered_value
|
9
9
|
value ? "Yes" : "No"
|
10
10
|
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def default_html_attributes
|
15
|
+
{ class: "type-boolean" }
|
16
|
+
end
|
11
17
|
end
|
12
18
|
end
|
13
19
|
end
|
@@ -1,17 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
using Katalyst::HtmlAttributes::HasHtmlAttributes
|
4
|
-
|
5
3
|
module Katalyst
|
6
4
|
module Tables
|
7
|
-
module
|
5
|
+
module Cells
|
8
6
|
# Formats the value as a money value
|
9
7
|
#
|
10
8
|
# The value is expected to be in cents.
|
11
9
|
# Adds a class to the cell to allow for custom styling
|
12
|
-
class CurrencyComponent <
|
13
|
-
def initialize(
|
14
|
-
super(
|
10
|
+
class CurrencyComponent < CellComponent
|
11
|
+
def initialize(options:, **)
|
12
|
+
super(**)
|
15
13
|
|
16
14
|
@options = options
|
17
15
|
end
|
@@ -20,8 +18,10 @@ module Katalyst
|
|
20
18
|
value.present? ? number_to_currency(value / 100.0, @options) : ""
|
21
19
|
end
|
22
20
|
|
21
|
+
private
|
22
|
+
|
23
23
|
def default_html_attributes
|
24
|
-
|
24
|
+
{ class: "type-currency" }
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -2,13 +2,13 @@
|
|
2
2
|
|
3
3
|
module Katalyst
|
4
4
|
module Tables
|
5
|
-
module
|
5
|
+
module Cells
|
6
6
|
# Formats the value as a date
|
7
|
-
# @param format [String] date format
|
7
|
+
# @param format [String] date format
|
8
8
|
# @param relative [Boolean] if true, the date may be(if within 5 days) shown as a relative date
|
9
|
-
class DateComponent <
|
10
|
-
def initialize(
|
11
|
-
super(
|
9
|
+
class DateComponent < CellComponent
|
10
|
+
def initialize(format:, relative:, **)
|
11
|
+
super(**)
|
12
12
|
|
13
13
|
@format = format
|
14
14
|
@relative = relative
|
@@ -24,6 +24,13 @@ module Katalyst
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
+
def default_html_attributes
|
28
|
+
{
|
29
|
+
class: "type-date",
|
30
|
+
title: (absolute_time if row.body? && @relative && value.present? && days_ago_in_words(value).present?),
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
27
34
|
def absolute_time
|
28
35
|
value.present? ? I18n.l(value, format: @format) : ""
|
29
36
|
end
|
@@ -36,10 +43,6 @@ module Katalyst
|
|
36
43
|
end
|
37
44
|
end
|
38
45
|
|
39
|
-
def default_html_attributes
|
40
|
-
@relative && value.present? && days_ago_in_words(value).present? ? { title: absolute_time } : {}
|
41
|
-
end
|
42
|
-
|
43
46
|
def days_ago_in_words(value)
|
44
47
|
from_time = value.to_time
|
45
48
|
to_time = Date.current.to_time
|
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
module Katalyst
|
4
4
|
module Tables
|
5
|
-
module
|
5
|
+
module Cells
|
6
6
|
# Formats the value as a datetime
|
7
|
-
# @param format [String] datetime format
|
7
|
+
# @param format [String] datetime format
|
8
8
|
# @param relative [Boolean] if true, the datetime may be(if today) shown as a relative date/time
|
9
|
-
class DateTimeComponent <
|
9
|
+
class DateTimeComponent < CellComponent
|
10
10
|
include ActionView::Helpers::DateHelper
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
super(
|
12
|
+
def initialize(format:, relative:, **)
|
13
|
+
super(**)
|
14
14
|
|
15
|
-
@format
|
15
|
+
@format = format
|
16
16
|
@relative = relative
|
17
17
|
end
|
18
18
|
|
@@ -26,6 +26,13 @@ module Katalyst
|
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
|
+
def default_html_attributes
|
30
|
+
{
|
31
|
+
class: "type-datetime",
|
32
|
+
title: (absolute_time if row.body? && @relative && today?),
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
29
36
|
def absolute_time
|
30
37
|
value.present? ? I18n.l(value, format: @format) : ""
|
31
38
|
end
|
@@ -47,10 +54,6 @@ module Katalyst
|
|
47
54
|
absolute_time
|
48
55
|
end
|
49
56
|
end
|
50
|
-
|
51
|
-
def default_html_attributes
|
52
|
-
@relative && today? ? { title: absolute_time } : {}
|
53
|
-
end
|
54
57
|
end
|
55
58
|
end
|
56
59
|
end
|