katalyst-tables 1.1.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|