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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -2
  3. data/README.md +56 -190
  4. data/app/assets/builds/katalyst/tables.esm.js +17 -47
  5. data/app/assets/builds/katalyst/tables.js +17 -47
  6. data/app/assets/builds/katalyst/tables.min.js +1 -1
  7. data/app/assets/builds/katalyst/tables.min.js.map +1 -1
  8. data/app/components/concerns/katalyst/tables/has_table_content.rb +17 -8
  9. data/app/components/concerns/katalyst/tables/identifiable.rb +51 -0
  10. data/app/components/concerns/katalyst/tables/orderable.rb +35 -105
  11. data/app/components/concerns/katalyst/tables/selectable.rb +18 -75
  12. data/app/components/concerns/katalyst/tables/sortable.rb +51 -17
  13. data/app/components/katalyst/table_component.html.erb +4 -4
  14. data/app/components/katalyst/table_component.rb +271 -53
  15. data/app/components/katalyst/tables/body_row_component.html.erb +5 -0
  16. data/app/components/katalyst/tables/body_row_component.rb +4 -31
  17. data/app/components/katalyst/tables/cell_component.rb +85 -0
  18. data/app/components/katalyst/tables/{body → cells}/boolean_component.rb +8 -2
  19. data/app/components/katalyst/tables/{body → cells}/currency_component.rb +7 -7
  20. data/app/components/katalyst/tables/{body → cells}/date_component.rb +12 -9
  21. data/app/components/katalyst/tables/{body → cells}/date_time_component.rb +13 -10
  22. data/app/components/katalyst/tables/{body → cells}/number_component.rb +5 -5
  23. data/app/components/katalyst/tables/cells/ordinal_component.rb +44 -0
  24. data/app/components/katalyst/tables/{body → cells}/rich_text_component.rb +8 -5
  25. data/app/components/katalyst/tables/cells/select_component.rb +39 -0
  26. data/app/components/katalyst/tables/data.rb +30 -0
  27. data/app/components/katalyst/tables/header_row_component.html.erb +5 -0
  28. data/app/components/katalyst/tables/header_row_component.rb +4 -25
  29. data/app/components/katalyst/tables/label.rb +37 -0
  30. data/app/components/katalyst/tables/orderable/form_component.rb +38 -0
  31. data/app/components/katalyst/tables/selectable/form_component.html.erb +3 -3
  32. data/app/components/katalyst/tables/selectable/form_component.rb +8 -11
  33. data/app/controllers/concerns/katalyst/tables/backend.rb +2 -28
  34. data/app/helpers/katalyst/tables/frontend.rb +48 -2
  35. data/app/javascript/tables/application.js +0 -5
  36. data/app/javascript/tables/orderable/form_controller.js +8 -6
  37. data/app/javascript/tables/orderable/item_controller.js +9 -0
  38. data/app/models/concerns/katalyst/tables/collection/core.rb +6 -1
  39. data/app/models/concerns/katalyst/tables/collection/sorting.rb +85 -17
  40. data/app/models/katalyst/tables/collection/array.rb +38 -0
  41. data/app/models/katalyst/tables/collection/base.rb +4 -0
  42. data/config/locales/tables.en.yml +0 -6
  43. data/lib/katalyst/tables/config.rb +23 -0
  44. data/lib/katalyst/tables.rb +9 -0
  45. metadata +22 -29
  46. data/app/components/concerns/katalyst/tables/body/typed_columns.rb +0 -132
  47. data/app/components/concerns/katalyst/tables/configurable_component.rb +0 -52
  48. data/app/components/concerns/katalyst/tables/header/typed_columns.rb +0 -179
  49. data/app/components/katalyst/tables/body/attachment_component.rb +0 -58
  50. data/app/components/katalyst/tables/body/link_component.rb +0 -40
  51. data/app/components/katalyst/tables/body_cell_component.rb +0 -55
  52. data/app/components/katalyst/tables/header/attachment_component.rb +0 -15
  53. data/app/components/katalyst/tables/header/boolean_component.rb +0 -15
  54. data/app/components/katalyst/tables/header/currency_component.rb +0 -15
  55. data/app/components/katalyst/tables/header/date_component.rb +0 -15
  56. data/app/components/katalyst/tables/header/date_time_component.rb +0 -15
  57. data/app/components/katalyst/tables/header/link_component.rb +0 -15
  58. data/app/components/katalyst/tables/header/number_component.rb +0 -15
  59. data/app/components/katalyst/tables/header/rich_text_component.rb +0 -15
  60. data/app/components/katalyst/tables/header_cell_component.rb +0 -97
  61. data/app/helpers/katalyst/tables/frontend/helper.rb +0 -31
  62. data/app/javascript/tables/turbo/collection_controller.js +0 -38
  63. 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.cell :name do |cell| %>
7
+ # <%= row.text :name do |cell| %>
8
8
  # <%= link_to cell.value, person %>
9
9
  # <% end %>
10
- # <%= row.cell :email %>
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
- config_component :header_row, default: "Katalyst::Tables::HeaderRowComponent"
21
- config_component :header_cell, default: "Katalyst::Tables::HeaderCellComponent"
22
- config_component :body_row, default: "Katalyst::Tables::BodyRowComponent"
23
- config_component :body_cell, default: "Katalyst::Tables::BodyCellComponent"
24
- config_component :caption, default: "Katalyst::Tables::EmptyCaptionComponent"
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
- # - `collection`: the collection to render
29
- # - `sorting`: the sorting to apply to the collection (defaults to collection.storing if available)
30
- # - `header`: whether to render the header row (defaults to true, supports options)
31
- # - `caption`: whether to render the caption (defaults to true, supports options)
32
- # - `generate_ids`: whether to generate ids for each row (defaults to true)
33
- # - `object_name`: the name of the object to use for partial rendering (defaults to collection.model_name.i18n_key)
34
- # - `partial`: the name of the partial to use for rendering each row (defaults to to_partial_path on the object)
35
- # - `as`: the name of the local variable to use for rendering each row (defaults to collection.model_name.param_key)
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: true,
42
- **html_attributes)
43
- @collection = collection
44
-
45
- # sorting: instance of Katalyst::Tables::Collection::SortForm.
46
- # If not provided will be inferred from the collection.
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
- @header = header
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
- @caption = caption
55
- @caption_options = (caption if caption.is_a?(Hash)) || {}
62
+ @caption_options = caption
56
63
 
57
- @generate_ids = generate_ids
64
+ @header_row_callbacks = []
65
+ @body_row_callbacks = []
66
+ @header_row_cell_callbacks = []
67
+ @body_row_cell_callbacks = []
58
68
 
59
- super(**html_attributes)
69
+ super(generate_ids:, object_name:, partial:, as:, **)
60
70
  end
61
71
 
62
- def id
63
- html_attributes[:id]
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 caption?
67
- @caption.present?
96
+ def inspect
97
+ "#<#{self.class.name} collection: #{collection.inspect}>"
68
98
  end
69
99
 
70
- def caption
71
- caption_component&.new(self)
100
+ delegate :header?, :body?, to: :@current_row
101
+
102
+ def row
103
+ @current_row
72
104
  end
73
105
 
74
- def header?
75
- @header.present?
106
+ def record
107
+ @current_record
76
108
  end
77
109
 
78
- def header_row
79
- header_row_component.new(self, **@header_options)
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
- def body_row(record)
83
- body_row_component.new(self, record)
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
- def sorting
87
- return @sorting if @sorting.present?
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
- collection.sorting if collection.respond_to?(:sorting)
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
- def generate_ids?
93
- @generate_ids.present?
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
- def inspect
97
- "#<#{self.class.name} collection: #{collection.inspect}>"
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
- define_html_attribute_methods(:thead_attributes)
101
- define_html_attribute_methods(:tbody_attributes)
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
- # Backwards compatibility with tables 1.0
104
- alias_method :options, :html_attributes=
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
@@ -0,0 +1,5 @@
1
+ <%= tag.tr(**html_attributes) do %>
2
+ <% cells.each do |cell| %>
3
+ <%= cell %>
4
+ <% end %>
5
+ <% 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 :columns, ->(component) { component }
8
+ renders_many :cells, ->(cell) { cell }
10
9
 
11
- def initialize(table, record)
12
- super()
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} record: #{record.inspect}>"
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 Body
5
+ module Cells
6
6
  # Shows Yes/No for boolean values
7
- class BooleanComponent < BodyCellComponent
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 Body
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 < BodyCellComponent
13
- def initialize(table, record, attribute, options: {}, **html_attributes)
14
- super(table, record, attribute, **html_attributes)
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
- super.merge_html(class: "type-currency")
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 Body
5
+ module Cells
6
6
  # Formats the value as a date
7
- # @param format [String] date format, defaults to :table
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 < BodyCellComponent
10
- def initialize(table, record, attribute, format: :table, relative: true, **options)
11
- super(table, record, attribute, **options)
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 Body
5
+ module Cells
6
6
  # Formats the value as a datetime
7
- # @param format [String] datetime format, defaults to :admin
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 < BodyCellComponent
9
+ class DateTimeComponent < CellComponent
10
10
  include ActionView::Helpers::DateHelper
11
11
 
12
- def initialize(table, record, attribute, format: :table, relative: true, **options)
13
- super(table, record, attribute, **options)
12
+ def initialize(format:, relative:, **)
13
+ super(**)
14
14
 
15
- @format = 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