katalyst-tables 1.1.0 → 2.1.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 +25 -0
- data/README.md +166 -134
- data/app/assets/config/katalyst-tables.js +1 -0
- data/app/assets/javascripts/controllers/tables/turbo_collection_controller.js +22 -0
- data/app/components/concerns/katalyst/tables/configurable_component.rb +31 -0
- data/app/components/concerns/katalyst/tables/has_html_attributes.rb +45 -0
- data/app/components/concerns/katalyst/tables/has_table_content.rb +33 -0
- data/app/components/concerns/katalyst/tables/sortable.rb +32 -0
- data/app/components/concerns/katalyst/tables/turbo_replaceable.rb +62 -0
- data/app/components/katalyst/table_component.rb +102 -0
- data/app/components/katalyst/tables/body_cell_component.rb +40 -0
- data/app/components/katalyst/tables/body_row_component.rb +40 -0
- data/app/components/katalyst/tables/empty_caption_component.html.erb +6 -0
- data/app/components/katalyst/tables/empty_caption_component.rb +38 -0
- data/app/components/katalyst/tables/header_cell_component.rb +58 -0
- data/app/components/katalyst/tables/header_row_component.rb +40 -0
- data/app/components/katalyst/tables/pagy_nav_component.rb +26 -0
- data/app/components/katalyst/turbo/pagy_nav_component.rb +23 -0
- data/app/components/katalyst/turbo/table_component.rb +48 -0
- data/app/models/concerns/katalyst/tables/collection/core.rb +71 -0
- data/app/models/concerns/katalyst/tables/collection/pagination.rb +66 -0
- data/app/models/concerns/katalyst/tables/collection/sorting.rb +63 -0
- data/app/models/katalyst/tables/collection.rb +32 -0
- data/config/importmap.rb +7 -0
- data/lib/katalyst/tables/backend/sort_form.rb +16 -2
- data/lib/katalyst/tables/backend.rb +8 -8
- data/lib/katalyst/tables/engine.rb +24 -0
- data/lib/katalyst/tables/frontend/helper.rb +8 -9
- data/lib/katalyst/tables/frontend.rb +6 -36
- data/lib/katalyst/tables/version.rb +1 -1
- data/lib/katalyst/tables.rb +5 -0
- metadata +54 -9
- data/lib/katalyst/tables/frontend/builder/base.rb +0 -63
- data/lib/katalyst/tables/frontend/builder/body_cell.rb +0 -31
- data/lib/katalyst/tables/frontend/builder/body_row.rb +0 -29
- data/lib/katalyst/tables/frontend/builder/header_cell.rb +0 -55
- data/lib/katalyst/tables/frontend/builder/header_row.rb +0 -23
- data/lib/katalyst/tables/frontend/table_builder.rb +0 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a572503d453b3b8b98a82942a48c5e2772cec4023417e8db71ed2cd8299331a
|
4
|
+
data.tar.gz: 4761c4fa4b21aa8c91f70c2118e27bd68dfd700ffc419ed6f7eae299b582710f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa9c1dc28479dc744c18804999d5f34ea9937e1500dbe8c01a1f2c183439077dfdd7047dbb8ce2ea0f93ef43bcfcea15eee6d08b2606f3682a00747e590cbc3b
|
7
|
+
data.tar.gz: 45c36c312e7f6efa8b11409d4822d135c961eba6ade51055b4671371b3b1a6e25a01c9fd1e059c0a89fa5da6bea3e7fe56fda267a1b3d7bfdf51e515b7b8a696
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [2.1.0]
|
4
|
+
|
5
|
+
- Add Collection model for building collections in a controller from params.
|
6
|
+
- See [[README.md]] for examples
|
7
|
+
- Add turbo entry points for table and pagy_nav
|
8
|
+
- See [[README.md]] for examples
|
9
|
+
- Add support for row partials when content is not provided
|
10
|
+
- See [[README.md]] for examples
|
11
|
+
- Add messages when table is empty, off by default (caption: true)
|
12
|
+
- Add PagyNavComponent for rendering `pagy_nav` from a collection.
|
13
|
+
- Replaces internal references to SortForm to use `sorting` instead
|
14
|
+
- No changes required to existing code unless you were using the internal
|
15
|
+
classes directly
|
16
|
+
- Change allows sort param and sorting model to co-exist
|
17
|
+
|
18
|
+
## [2.0.0]
|
19
|
+
|
20
|
+
- Replaces builders with view_components
|
21
|
+
We want view components to be the way to build custom tables, and add more
|
22
|
+
complete features to the gem. This will be a breaking change, but we have
|
23
|
+
tried hard to retain compatibility with existing code. Unless you are using
|
24
|
+
a custom builder then it's unlikely that you will see any changes.
|
25
|
+
- If you are using custom builders, then you will need to update them to use
|
26
|
+
view components. See [[README.md]] for examples.
|
27
|
+
|
3
28
|
## [1.1.0] - 2023-07-18
|
4
29
|
|
5
30
|
- Replaces `param_key` with `i18n_key` for attribute lookup in locale file
|
data/README.md
CHANGED
@@ -7,87 +7,108 @@ 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"
|
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
|
22
|
-
|
19
|
+
This gem provides entry points for backend and frontend concerns:
|
20
|
+
* `Katalyst::TableComponent` can be used render encapsulated tables, it calls a
|
21
|
+
partial for each row.
|
22
|
+
* `Katalyst::Tables::Frontend` provides `table_with` for inline table generation
|
23
|
+
* `Katalyst::Tables::Collection::Base` provides a default entry point for
|
24
|
+
building collections in your controller actions.
|
23
25
|
|
24
|
-
|
26
|
+
## Frontend
|
25
27
|
|
26
|
-
|
28
|
+
Use `Katalyst::TableComponent` to build a table component from an ActiveRecord
|
29
|
+
collection, or from a `Katalyst::Tables::Collection::Base` instance.
|
30
|
+
|
31
|
+
For example, if you render `Katalyst::TableComponent.new(collection: @people)`,
|
32
|
+
the table component will look for a partial called `_person.html+row.erb` and
|
33
|
+
render it for each row (and once for the header row).
|
27
34
|
|
28
35
|
```erb
|
29
|
-
|
30
|
-
|
31
|
-
<%=
|
32
|
-
<%= row.cell :actions do %>
|
33
|
-
<%= link_to "Edit", person %>
|
34
|
-
<% end %>
|
36
|
+
<%# locals: { row:, person: nil } %>
|
37
|
+
<% row.cell :name do |cell| %>
|
38
|
+
<%= link_to cell.value, [:edit, person] %>
|
35
39
|
<% end %>
|
40
|
+
<% row.cell :email %>
|
36
41
|
```
|
37
42
|
|
38
|
-
|
43
|
+
The table component will call your partial once per row and accumulate the cells
|
44
|
+
you generate into rows, including a header row:
|
39
45
|
|
40
46
|
```html
|
41
47
|
|
42
48
|
<table>
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
<
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
</
|
56
|
-
<
|
57
|
-
|
58
|
-
|
59
|
-
<td><a href="/people/2/edit">Edit</a></td>
|
60
|
-
</tr>
|
61
|
-
</tbody>
|
49
|
+
<thead>
|
50
|
+
<tr>
|
51
|
+
<th>Name</th>
|
52
|
+
<th>Email</th>
|
53
|
+
</tr>
|
54
|
+
</thead>
|
55
|
+
<tbody>
|
56
|
+
<tr>
|
57
|
+
<td><a href="/people/1/edit">Alice</a></td>
|
58
|
+
<td>alice@acme.org</td>
|
59
|
+
</tr>
|
60
|
+
<tr>
|
61
|
+
<td><a href="/people/2/edit">Bob</a></td>
|
62
|
+
<td>bob@acme.org</td>
|
63
|
+
</tr>
|
64
|
+
</tbody>
|
62
65
|
</table>
|
63
66
|
```
|
64
67
|
|
65
|
-
|
68
|
+
You can customize the partial and/or the name of the resource in a similar style
|
69
|
+
to view partials:
|
66
70
|
|
67
|
-
|
71
|
+
```erb
|
72
|
+
<%= render Katalyst::TableComponent.new(collection: @employees, as: :person, partial: "person") %>
|
73
|
+
```
|
68
74
|
|
69
|
-
|
75
|
+
### Inline tables
|
76
|
+
|
77
|
+
You can use the `table_with` helper to generate a table inline in your view without explicitly interacting with the
|
78
|
+
table component. This is primarily intended for backwards compatibility, but it can be useful for simple tables.
|
79
|
+
|
80
|
+
Add `include Katalyst::Tables::Frontend` to your `ApplicationHelper` or similar.
|
70
81
|
|
71
82
|
```erb
|
72
|
-
<%= table_with collection: @people
|
73
|
-
|
83
|
+
<%= table_with collection: @people do |row, person| %>
|
84
|
+
<% row.cell :name do |cell| %>
|
85
|
+
<%= link_to cell.value, [:edit, person] %>
|
86
|
+
<% end %>
|
87
|
+
<% row.cell :email %>
|
74
88
|
<% end %>
|
75
89
|
```
|
76
90
|
|
91
|
+
### HTML Attributes
|
92
|
+
|
93
|
+
You can add custom attributes on table, row, and cell tags.
|
94
|
+
|
95
|
+
The table tag takes attributes passed to `TableComponent` or via the call to `table_with`, similar to `form_with`:
|
96
|
+
|
97
|
+
```erb
|
98
|
+
<%= TableComponent.new(collection: @people, id: "people-table")
|
99
|
+
```
|
100
|
+
|
77
101
|
Cells support the same approach:
|
78
102
|
|
79
103
|
```erb
|
80
104
|
<%= row.cell :name, class: "name" %>
|
81
105
|
```
|
82
106
|
|
83
|
-
Rows do not get called directly, so instead you can
|
107
|
+
Rows do not get called directly, so instead you can assign to `html_attributes` on the row builder to customize row tag
|
84
108
|
generation.
|
85
109
|
|
86
110
|
```erb
|
87
|
-
|
88
|
-
<% row.options data: { id: person.id } if row.body? %>
|
89
|
-
...
|
90
|
-
<% end %>
|
111
|
+
<% row.html_attributes = { id: person.id } if row.body? %>
|
91
112
|
```
|
92
113
|
|
93
114
|
Note: because the row builder gets called to generate the header row, you may need to guard calls that access the
|
@@ -95,21 +116,21 @@ Note: because the row builder gets called to generate the header row, you may ne
|
|
95
116
|
|
96
117
|
#### Headers
|
97
118
|
|
98
|
-
|
99
|
-
|
119
|
+
Tables will automatically generate a header row for you by calling your row partial or provided block with no object.
|
120
|
+
During this call, `row.header?` is true, `row.body?` is false, and the object (`person`) is nil.
|
100
121
|
|
101
122
|
All cells generated in the table header iteration will automatically be header cells, but you can also make header cells
|
102
123
|
in your body rows by passing `heading: true` when you generate the cell.
|
103
124
|
|
104
125
|
```erb
|
105
|
-
|
126
|
+
<% row.cell :id, heading: true %>
|
106
127
|
```
|
107
128
|
|
108
|
-
The table header cells default to showing the
|
129
|
+
The table header cells default to showing the capitalized column name, but you can customize this in one of two ways:
|
109
130
|
|
110
131
|
* Set the value inline
|
111
132
|
```erb
|
112
|
-
|
133
|
+
<% row.cell :id, label: "ID" %>
|
113
134
|
```
|
114
135
|
* Define a translation for the attribute
|
115
136
|
```yml
|
@@ -132,8 +153,8 @@ the table cell. This is often all you need to do, but if you do want to customis
|
|
132
153
|
the value you can pass a block instead:
|
133
154
|
|
134
155
|
```erb
|
135
|
-
|
136
|
-
|
156
|
+
<% row.cell :status do %>
|
157
|
+
<%= person.password.present? ? "Active" : "Invited" %>
|
137
158
|
<% end %>
|
138
159
|
```
|
139
160
|
|
@@ -141,20 +162,61 @@ In the context of the block you have access the cell builder if you simply
|
|
141
162
|
want to extend the default behaviour:
|
142
163
|
|
143
164
|
```erb
|
144
|
-
|
145
|
-
|
165
|
+
<% row.cell :status do |cell| %>
|
166
|
+
<%= link_to cell.value, person %>
|
146
167
|
<% end %>
|
147
168
|
```
|
148
169
|
|
149
|
-
You can also
|
150
|
-
please note that this will replace any options passed to the cell
|
170
|
+
You can also assign to `html_attributes` on the cell builder, similar to the row
|
171
|
+
builder, but please note that this will replace any options passed to the cell
|
172
|
+
as arguments.
|
173
|
+
|
174
|
+
## Collections
|
151
175
|
|
152
|
-
|
176
|
+
The `Katalyst::Tables::Collection::Base` class provides a convenient way to
|
177
|
+
manage collections in your controller actions. It is designed to be used with
|
178
|
+
Pagy for pagination and provides built-in sorting when used with ActiveRecord
|
179
|
+
collections. Sorting and Pagination are off by default, but you can create
|
180
|
+
a custom `ApplicationCollection` class that sets them on by default.
|
153
181
|
|
154
|
-
|
155
|
-
|
182
|
+
```ruby
|
183
|
+
class ApplicationCollection < Katalyst::Tables::Collection::Base
|
184
|
+
config.sorting = "name" # requires models have a name attribute
|
185
|
+
config.pagination = true
|
186
|
+
end
|
187
|
+
```
|
156
188
|
|
157
|
-
|
189
|
+
You can then use this class in your controller actions:
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
class PeopleController < ApplicationController
|
193
|
+
def index
|
194
|
+
@people = ApplicationCollection.new.with_params(params).apply(People.all)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
```
|
198
|
+
|
199
|
+
Collections can be passed directly to `TableComponent` and it will automatically
|
200
|
+
detect features such as sorting and generate the appropriate table header links.
|
201
|
+
|
202
|
+
```erb
|
203
|
+
<%= render TableComponent.new(collection: @people) %>
|
204
|
+
```
|
205
|
+
|
206
|
+
## Sort
|
207
|
+
|
208
|
+
When sort is enabled, table columns will be automatically sortable in the
|
209
|
+
frontend for any column that corresponds to an attribute on the model. You can
|
210
|
+
also add sorting to non-attribute columns by defining a scope in your
|
211
|
+
model:
|
212
|
+
|
213
|
+
```
|
214
|
+
scope :order_by_status, ->(direction) { ... }
|
215
|
+
```
|
216
|
+
|
217
|
+
You can also use sort without using collections, this was the primary backend
|
218
|
+
interface for V1 and takes design cues from Pagy. Start by including the backend
|
219
|
+
in your controller(s):
|
158
220
|
|
159
221
|
```ruby
|
160
222
|
include Katalyst::Tables::Backend
|
@@ -185,55 +247,47 @@ links and show the current sort state:
|
|
185
247
|
<%= table_with collection: @people, sort: @sort do |row, person| %>
|
186
248
|
<%= row.cell :name %>
|
187
249
|
<%= row.cell :email %>
|
188
|
-
<%= row.cell :actions do %>
|
189
|
-
<%= link_to "Edit", person %>
|
190
|
-
<% end %>
|
191
250
|
<% end %>
|
192
251
|
```
|
193
252
|
|
194
|
-
|
195
|
-
automatically sortable in the frontend.
|
253
|
+
## Pagination
|
196
254
|
|
197
|
-
|
198
|
-
model:
|
255
|
+
This gem designed to work with [pagy](https://github.com/ddnexus/pagy/).
|
199
256
|
|
200
|
-
|
201
|
-
|
202
|
-
|
257
|
+
If you use collections and enable pagination then pagy will be called internally
|
258
|
+
and the pagy metadata will be available as `pagination` on the collection.
|
259
|
+
|
260
|
+
`Katalyst::Tables::PagyNavComponent` can be used to render the pagination links
|
261
|
+
for a collection.
|
203
262
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
calling `table_sort`.
|
263
|
+
```erb
|
264
|
+
<%= render Katalyst::Tables::PagyNavComponent.new(collection: @people) %>
|
265
|
+
```
|
208
266
|
|
209
|
-
|
267
|
+
## Turbo streams
|
210
268
|
|
211
|
-
|
269
|
+
This gem provides turbo stream entry points for table and pagy_nav. These are
|
270
|
+
identical in the options they support, but they require ids, and they will
|
271
|
+
automatically render turbo stream replace tags when rendered as part of a turbo
|
272
|
+
stream response.
|
212
273
|
|
213
|
-
|
274
|
+
To take full advantage of this feature, we suggest you build the component in
|
275
|
+
your controller and pass it to the view. This allows you to use the same
|
276
|
+
controller for both HTML and turbo responses.
|
214
277
|
|
215
278
|
```ruby
|
216
|
-
|
217
279
|
def index
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
280
|
+
collection = ApplicationCollection.new.with_params(params).apply(People.all)
|
281
|
+
table = Katalyst::Turbo::TableComponent.new(collection:, id: "people")
|
282
|
+
|
283
|
+
respond_to do |format|
|
284
|
+
format.html { render locals: { table: table } }
|
285
|
+
format.turbo_stream { render table }
|
286
|
+
end
|
222
287
|
end
|
223
288
|
```
|
224
289
|
|
225
|
-
|
226
|
-
<%= table_with collection: @people, sort: @sort do |row, person| %>
|
227
|
-
<%= row.cell :name %>
|
228
|
-
<%= row.cell :email %>
|
229
|
-
<%= row.cell :actions do %>
|
230
|
-
<%= link_to "Edit", person %>
|
231
|
-
<% end %>
|
232
|
-
<% end %>
|
233
|
-
<%== pagy_nav(@pagy) %>
|
234
|
-
```
|
235
|
-
|
236
|
-
### Customization
|
290
|
+
## Customization
|
237
291
|
|
238
292
|
A common pattern we use is to have a cell at the end of the table for actions. For example:
|
239
293
|
|
@@ -257,75 +311,53 @@ A common pattern we use is to have a cell at the end of the table for actions. F
|
|
257
311
|
</table>
|
258
312
|
```
|
259
313
|
|
260
|
-
You can write a custom
|
261
|
-
|
314
|
+
You can write a custom component that helps generate this type of table by
|
315
|
+
adding the required classes and adding helpers for generating the actions.
|
316
|
+
This allows for a declarative table syntax, something like this:
|
262
317
|
|
263
318
|
```erb
|
264
|
-
<%=
|
265
|
-
|
266
|
-
|
319
|
+
<%= render ActionTableComponent.new(collection:) do |row| %>
|
320
|
+
<% row.cell :name %>
|
321
|
+
<% row.actions do |cell| %>
|
267
322
|
<%= cell.action "Edit", :edit %>
|
268
323
|
<%= cell.action "Delete", :delete, method: :delete %>
|
269
324
|
<% end %>
|
270
325
|
<% end %>
|
271
326
|
```
|
272
327
|
|
273
|
-
And the
|
328
|
+
And the customized component:
|
274
329
|
|
275
330
|
```ruby
|
276
|
-
class
|
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
|
285
|
-
|
286
|
-
def table_header_cell(method, builder = ActionHeaderCell, **options)
|
287
|
-
super
|
288
|
-
end
|
289
|
-
|
290
|
-
def table_body_row(object, builder = ActionBodyRow, &block)
|
291
|
-
super
|
292
|
-
end
|
331
|
+
class ActionTableComponent < Katalyst::TableComponent
|
293
332
|
|
294
|
-
|
295
|
-
|
333
|
+
config.header_row = "ActionHeaderRow"
|
334
|
+
config.body_row = "ActionBodyRow"
|
335
|
+
config.body_cell = "ActionBodyCell"
|
336
|
+
|
337
|
+
def default_attributes
|
338
|
+
{ class: "action-table" }
|
296
339
|
end
|
297
340
|
|
298
|
-
class ActionHeaderRow < Katalyst::Tables::
|
341
|
+
class ActionHeaderRow < Katalyst::Tables::HeaderRowComponent
|
299
342
|
def actions(&block)
|
300
|
-
cell(:actions, class: "actions", label: "")
|
343
|
+
cell(:actions, class: "actions", label: "", &block)
|
301
344
|
end
|
302
345
|
end
|
303
346
|
|
304
|
-
class
|
305
|
-
end
|
306
|
-
|
307
|
-
class ActionBodyRow < Katalyst::Tables::Frontend::Builder::BodyRow
|
347
|
+
class ActionBodyRow < Katalyst::Tables::BodyRowComponent
|
308
348
|
def actions(&block)
|
309
349
|
cell(:actions, class: "actions", &block)
|
310
350
|
end
|
311
351
|
end
|
312
352
|
|
313
|
-
class ActionBodyCell < Katalyst::Tables::
|
314
|
-
def action(label, href, **
|
315
|
-
content_tag
|
353
|
+
class ActionBodyCell < Katalyst::Tables::BodyCellComponent
|
354
|
+
def action(label, href, **attrs)
|
355
|
+
content_tag(:a, label, href: href, **attrs)
|
316
356
|
end
|
317
357
|
end
|
318
358
|
end
|
319
359
|
```
|
320
360
|
|
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:
|
322
|
-
|
323
|
-
```html
|
324
|
-
class ApplicationController < ActiveController::Base
|
325
|
-
default_table_builder ActionTableBuilder
|
326
|
-
end
|
327
|
-
```
|
328
|
-
|
329
361
|
## Development
|
330
362
|
|
331
363
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests.
|
@@ -0,0 +1 @@
|
|
1
|
+
//= link_tree ../javascripts
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class TurboCollectionController extends Controller {
|
4
|
+
static values = {
|
5
|
+
url: String,
|
6
|
+
sort: String,
|
7
|
+
}
|
8
|
+
|
9
|
+
urlValueChanged(url) {
|
10
|
+
window.history.replaceState({}, "", this.urlValue);
|
11
|
+
}
|
12
|
+
|
13
|
+
sortValueChanged(sort) {
|
14
|
+
document.querySelectorAll(this.#sortSelector).forEach((input) => {
|
15
|
+
if (input) input.value = sort;
|
16
|
+
});
|
17
|
+
}
|
18
|
+
|
19
|
+
get #sortSelector() {
|
20
|
+
return "input[name='sort']";
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Katalyst
|
4
|
+
module Tables
|
5
|
+
module ConfigurableComponent # :nodoc:
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
include ActiveSupport::Configurable
|
9
|
+
|
10
|
+
included do
|
11
|
+
# Workaround: ViewComponent::Base.config is incompatible with ActiveSupport::Configurable
|
12
|
+
@_config = Class.new(ActiveSupport::Configurable::Configuration).new
|
13
|
+
end
|
14
|
+
|
15
|
+
class_methods do
|
16
|
+
# Define a configurable sub-component.
|
17
|
+
def config_component(name, component_name: "#{name}_component", default: nil)
|
18
|
+
config_accessor(name)
|
19
|
+
config.public_send("#{name}=", default)
|
20
|
+
define_method(component_name) do
|
21
|
+
instance_variable_get("@#{component_name}") if instance_variable_defined?("@#{component_name}")
|
22
|
+
|
23
|
+
klass = config.public_send(name)
|
24
|
+
component = self.class.const_get(klass) if klass
|
25
|
+
instance_variable_set("@#{component_name}", component) if component
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "html_attributes_utils"
|
4
|
+
|
5
|
+
module Katalyst
|
6
|
+
module Tables
|
7
|
+
module HasHtmlAttributes # :nodoc:
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
using HTMLAttributesUtils
|
11
|
+
|
12
|
+
DEFAULT_MERGEABLE_ATTRIBUTES = [
|
13
|
+
*HTMLAttributesUtils::DEFAULT_MERGEABLE_ATTRIBUTES,
|
14
|
+
%i[data controller],
|
15
|
+
%i[data action]
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
def initialize(**options)
|
19
|
+
super(**options.except(:id, :aria, :class, :data, :html))
|
20
|
+
|
21
|
+
self.html_attributes = options
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add HTML options to the current component.
|
25
|
+
# Public method for customizing components from within
|
26
|
+
def html_attributes=(options)
|
27
|
+
@html_attributes = options.slice(:id, :aria, :class, :data).merge(options.fetch(:html, {}))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Backwards compatibility with tables 1.0
|
31
|
+
alias options html_attributes=
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def html_attributes
|
36
|
+
default_attributes
|
37
|
+
.deep_merge_html_attributes(@html_attributes, mergeable_attributes: DEFAULT_MERGEABLE_ATTRIBUTES)
|
38
|
+
end
|
39
|
+
|
40
|
+
def default_attributes
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Katalyst
|
4
|
+
module Tables
|
5
|
+
module HasTableContent # :nodoc:
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def initialize(object_name: nil, partial: nil, as: nil, **options)
|
9
|
+
super(**options)
|
10
|
+
|
11
|
+
@object_name = object_name || model_name&.i18n_key
|
12
|
+
@partial = partial
|
13
|
+
@as = as
|
14
|
+
end
|
15
|
+
|
16
|
+
def model_name
|
17
|
+
collection.model_name if collection.respond_to?(:model_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def row_proc
|
23
|
+
@row_proc ||= @__vc_render_in_block || method(:row_partial)
|
24
|
+
end
|
25
|
+
|
26
|
+
def row_partial(row, record = nil)
|
27
|
+
partial = @partial || model_name&.param_key&.to_s
|
28
|
+
as = @as || model_name&.param_key&.to_sym
|
29
|
+
render(partial: partial, variants: [:row], locals: { as => record, row: row })
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Katalyst
|
4
|
+
module Tables
|
5
|
+
# Extension to add sorting support to a collection.
|
6
|
+
# Assumes collection and sorting are available in the current scope.
|
7
|
+
module Sortable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
# Returns true when the given attribute is sortable.
|
11
|
+
def sortable?(attribute)
|
12
|
+
sorting&.supports?(collection, attribute)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Generates a url for applying/toggling sort for the given column.
|
16
|
+
def sort_url(attribute) # rubocop:disable Metrics/AbcSize
|
17
|
+
# Implementation inspired by pagy's `pagy_url_for` helper.
|
18
|
+
# Preserve any existing GET parameters
|
19
|
+
# CAUTION: these parameters are not sanitised
|
20
|
+
sort = attribute && sorting.toggle(attribute)
|
21
|
+
params = if sort && !sort.eql?(sorting.default)
|
22
|
+
request.GET.merge("sort" => sort).except("page")
|
23
|
+
else
|
24
|
+
request.GET.except("page", "sort")
|
25
|
+
end
|
26
|
+
query_string = params.empty? ? "" : "?#{Rack::Utils.build_nested_query(params)}"
|
27
|
+
|
28
|
+
"#{request.path}#{query_string}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|