plutonium 0.49.1 → 0.51.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/.claude/skills/plutonium/SKILL.md +85 -102
- data/.claude/skills/plutonium-app/SKILL.md +572 -0
- data/.claude/skills/plutonium-auth/SKILL.md +163 -300
- data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
- data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
- data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
- data/.claude/skills/plutonium-testing/SKILL.md +6 -5
- data/.claude/skills/plutonium-ui/SKILL.md +900 -0
- data/CHANGELOG.md +37 -0
- data/Rakefile +2 -1
- data/app/assets/plutonium.css +1 -11
- data/app/assets/plutonium.js +1323 -1184
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +50 -49
- data/app/assets/plutonium.min.js.map +4 -4
- data/app/views/plutonium/_resource_header.html.erb +4 -4
- data/app/views/plutonium/_resource_sidebar.html.erb +9 -9
- data/app/views/resource/_resource_grid.html.erb +1 -0
- data/config/brakeman.ignore +25 -2
- data/docs/.vitepress/config.ts +37 -27
- data/docs/getting-started/index.md +22 -29
- data/docs/getting-started/installation.md +37 -80
- data/docs/getting-started/tutorial/index.md +4 -5
- data/docs/guides/adding-resources.md +66 -377
- data/docs/guides/authentication.md +94 -463
- data/docs/guides/authorization.md +124 -370
- data/docs/guides/creating-packages.md +94 -296
- data/docs/guides/custom-actions.md +121 -441
- data/docs/guides/index.md +22 -42
- data/docs/guides/multi-tenancy.md +116 -187
- data/docs/guides/nested-resources.md +103 -431
- data/docs/guides/search-filtering.md +123 -240
- data/docs/guides/testing.md +5 -4
- data/docs/guides/theming.md +157 -407
- data/docs/guides/troubleshooting.md +5 -3
- data/docs/guides/user-invites.md +106 -425
- data/docs/guides/user-profile.md +76 -243
- data/docs/index.md +1 -1
- data/docs/reference/app/generators.md +517 -0
- data/docs/reference/app/index.md +158 -0
- data/docs/reference/app/packages.md +146 -0
- data/docs/reference/app/portals.md +377 -0
- data/docs/reference/auth/accounts.md +230 -0
- data/docs/reference/auth/index.md +88 -0
- data/docs/reference/auth/profile.md +185 -0
- data/docs/reference/behavior/controllers.md +395 -0
- data/docs/reference/behavior/index.md +22 -0
- data/docs/reference/behavior/interactions.md +341 -0
- data/docs/reference/behavior/policies.md +417 -0
- data/docs/reference/index.md +56 -49
- data/docs/reference/resource/actions.md +423 -0
- data/docs/reference/resource/definition.md +508 -0
- data/docs/reference/resource/index.md +50 -0
- data/docs/reference/resource/model.md +348 -0
- data/docs/reference/resource/query.md +305 -0
- data/docs/reference/tenancy/entity-scoping.md +361 -0
- data/docs/reference/tenancy/index.md +36 -0
- data/docs/reference/tenancy/invites.md +393 -0
- data/docs/reference/tenancy/nested-resources.md +267 -0
- data/docs/reference/testing/index.md +287 -0
- data/docs/reference/ui/assets.md +400 -0
- data/docs/reference/ui/components.md +165 -0
- data/docs/reference/ui/displays.md +104 -0
- data/docs/reference/ui/forms.md +284 -0
- data/docs/reference/ui/index.md +30 -0
- data/docs/reference/ui/layouts.md +106 -0
- data/docs/reference/ui/pages.md +189 -0
- data/docs/reference/ui/tables.md +117 -0
- data/docs/superpowers/plans/2026-05-07-ui-layout-overhaul.md +841 -0
- data/docs/superpowers/plans/2026-05-07-ui-layout-overhaul.md.tasks.json +103 -0
- data/docs/superpowers/specs/2026-05-07-ui-layout-overhaul-design.md +270 -0
- data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
- data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
- data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +1 -0
- data/lib/generators/pu/invites/install_generator.rb +1 -0
- data/lib/generators/pu/lite/rails_pulse/rails_pulse_generator.rb +54 -5
- data/lib/plutonium/action/base.rb +44 -1
- data/lib/plutonium/action/interactive.rb +1 -1
- data/lib/plutonium/configuration.rb +4 -0
- data/lib/plutonium/definition/actions.rb +3 -0
- data/lib/plutonium/definition/base.rb +8 -0
- data/lib/plutonium/definition/index_views.rb +95 -0
- data/lib/plutonium/definition/metadata.rb +40 -0
- data/lib/plutonium/helpers/turbo_helper.rb +12 -1
- data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
- data/lib/plutonium/interaction/response/redirect.rb +1 -1
- data/lib/plutonium/query/base.rb +8 -0
- data/lib/plutonium/query/filters/association.rb +30 -8
- data/lib/plutonium/query/filters/boolean.rb +5 -0
- data/lib/plutonium/resource/controller.rb +1 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
- data/lib/plutonium/resource/controllers/presentable.rb +11 -2
- data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
- data/lib/plutonium/resource/definition.rb +42 -0
- data/lib/plutonium/resource/policy.rb +7 -0
- data/lib/plutonium/resource/query_object.rb +64 -6
- data/lib/plutonium/routing/mapper_extensions.rb +15 -0
- data/lib/plutonium/testing/resource_definition.rb +2 -2
- data/lib/plutonium/ui/action_button.rb +4 -2
- data/lib/plutonium/ui/component/kit.rb +12 -0
- data/lib/plutonium/ui/component/methods.rb +4 -0
- data/lib/plutonium/ui/display/base.rb +3 -1
- data/lib/plutonium/ui/display/resource.rb +109 -25
- data/lib/plutonium/ui/display/theme.rb +2 -1
- data/lib/plutonium/ui/dyna_frame/content.rb +8 -14
- data/lib/plutonium/ui/empty_card.rb +1 -1
- data/lib/plutonium/ui/form/base.rb +35 -3
- data/lib/plutonium/ui/form/components/hidden_wrapper.rb +25 -0
- data/lib/plutonium/ui/form/components/json.rb +58 -0
- data/lib/plutonium/ui/form/components/resource_select.rb +133 -1
- data/lib/plutonium/ui/form/components/secure_association.rb +105 -24
- data/lib/plutonium/ui/form/components/sticky_footer.rb +17 -0
- data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
- data/lib/plutonium/ui/form/resource.rb +45 -10
- data/lib/plutonium/ui/form/theme.rb +1 -1
- data/lib/plutonium/ui/frame_navigator_panel.rb +7 -4
- data/lib/plutonium/ui/grid/card.rb +235 -0
- data/lib/plutonium/ui/grid/resource.rb +149 -0
- data/lib/plutonium/ui/layout/base.rb +38 -1
- data/lib/plutonium/ui/layout/header.rb +1 -2
- data/lib/plutonium/ui/layout/icon_rail.rb +212 -0
- data/lib/plutonium/ui/layout/resource_layout.rb +10 -3
- data/lib/plutonium/ui/layout/sidebar.rb +12 -24
- data/lib/plutonium/ui/layout/topbar.rb +100 -0
- data/lib/plutonium/ui/modal/base.rb +109 -0
- data/lib/plutonium/ui/modal/centered.rb +21 -0
- data/lib/plutonium/ui/modal/slideover.rb +26 -0
- data/lib/plutonium/ui/page/base.rb +18 -6
- data/lib/plutonium/ui/page/edit.rb +13 -1
- data/lib/plutonium/ui/page/index.rb +40 -1
- data/lib/plutonium/ui/page/interactive_action.rb +8 -39
- data/lib/plutonium/ui/page/new.rb +13 -1
- data/lib/plutonium/ui/page/show.rb +8 -1
- data/lib/plutonium/ui/page_header.rb +8 -13
- data/lib/plutonium/ui/panel.rb +10 -19
- data/lib/plutonium/ui/sidebar_menu.rb +2 -25
- data/lib/plutonium/ui/tab_list.rb +29 -7
- data/lib/plutonium/ui/table/base.rb +106 -0
- data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +12 -4
- data/lib/plutonium/ui/table/components/filter_form.rb +171 -0
- data/lib/plutonium/ui/table/components/filter_pills.rb +89 -0
- data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +13 -12
- data/lib/plutonium/ui/table/components/scopes_pills.rb +67 -0
- data/lib/plutonium/ui/table/components/selection_column.rb +2 -11
- data/lib/plutonium/ui/table/components/toolbar.rb +104 -0
- data/lib/plutonium/ui/table/components/view_switcher.rb +81 -0
- data/lib/plutonium/ui/table/resource.rb +158 -89
- data/lib/plutonium/ui/table/theme.rb +14 -5
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium.rb +14 -0
- data/lib/tasks/release.rake +15 -1
- data/package.json +10 -10
- data/src/css/components.css +304 -131
- data/src/css/slim_select.css +4 -0
- data/src/css/tokens.css +101 -85
- data/src/js/controllers/autosubmit_controller.js +24 -0
- data/src/js/controllers/bulk_actions_controller.js +15 -16
- data/src/js/controllers/capture_url_controller.js +14 -0
- data/src/js/controllers/filter_panel_controller.js +77 -19
- data/src/js/controllers/frame_navigator_controller.js +34 -6
- data/src/js/controllers/icon_rail_controller.js +22 -0
- data/src/js/controllers/icon_rail_flyout_controller.js +128 -0
- data/src/js/controllers/register_controllers.js +16 -0
- data/src/js/controllers/resource_tab_list_controller.js +56 -3
- data/src/js/controllers/row_click_controller.js +21 -0
- data/src/js/controllers/slim_select_controller.js +61 -0
- data/src/js/controllers/table_column_menu_controller.js +43 -0
- data/src/js/controllers/table_header_controller.js +16 -0
- data/src/js/controllers/view_switcher_controller.js +29 -0
- data/src/js/turbo/turbo_actions.js +33 -0
- data/yarn.lock +553 -543
- metadata +71 -32
- data/.claude/skills/plutonium-assets/SKILL.md +0 -512
- data/.claude/skills/plutonium-controller/SKILL.md +0 -396
- data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
- data/.claude/skills/plutonium-definition/SKILL.md +0 -1138
- data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
- data/.claude/skills/plutonium-forms/SKILL.md +0 -465
- data/.claude/skills/plutonium-installation/SKILL.md +0 -325
- data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
- data/.claude/skills/plutonium-invites/SKILL.md +0 -408
- data/.claude/skills/plutonium-model/SKILL.md +0 -440
- data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
- data/.claude/skills/plutonium-package/SKILL.md +0 -198
- data/.claude/skills/plutonium-policy/SKILL.md +0 -456
- data/.claude/skills/plutonium-portal/SKILL.md +0 -410
- data/.claude/skills/plutonium-views/SKILL.md +0 -592
- data/docs/reference/assets/index.md +0 -496
- data/docs/reference/controller/index.md +0 -412
- data/docs/reference/definition/actions.md +0 -449
- data/docs/reference/definition/fields.md +0 -383
- data/docs/reference/definition/index.md +0 -268
- data/docs/reference/definition/query.md +0 -351
- data/docs/reference/generators/index.md +0 -648
- data/docs/reference/interaction/index.md +0 -449
- data/docs/reference/model/features.md +0 -248
- data/docs/reference/model/index.md +0 -218
- data/docs/reference/policy/index.md +0 -456
- data/docs/reference/portal/index.md +0 -379
- data/docs/reference/views/forms.md +0 -411
- data/docs/reference/views/index.md +0 -501
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Pages
|
|
2
|
+
|
|
3
|
+
Each definition has nested page classes for index / show / new / edit / interactive-action. Override the ones you need.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Definition
|
|
9
|
+
├── IndexPage → renders Table
|
|
10
|
+
├── ShowPage → renders Display
|
|
11
|
+
├── NewPage → renders Form
|
|
12
|
+
├── EditPage → renders Form
|
|
13
|
+
└── InteractiveActionPage → renders Form
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Page classes
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
class PostDefinition < ResourceDefinition
|
|
20
|
+
class IndexPage < IndexPage; end
|
|
21
|
+
class ShowPage < ShowPage; end
|
|
22
|
+
class NewPage < NewPage; end
|
|
23
|
+
class EditPage < EditPage; end
|
|
24
|
+
class InteractiveActionPage < InteractiveActionPage; end
|
|
25
|
+
class Form < Form; end
|
|
26
|
+
class Table < Table; end
|
|
27
|
+
class Display < Display; end
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Page titles, descriptions, breadcrumbs
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
class PostDefinition < ResourceDefinition
|
|
35
|
+
index_page_title "Blog Posts"
|
|
36
|
+
index_page_description "Manage all published articles"
|
|
37
|
+
show_page_title "Article Details"
|
|
38
|
+
show_page_title -> { current_record!.title } # dynamic
|
|
39
|
+
new_page_title "Create Post"
|
|
40
|
+
edit_page_title -> { "Edit: #{current_record!.title}" }
|
|
41
|
+
|
|
42
|
+
breadcrumbs true # global default
|
|
43
|
+
index_page_breadcrumbs false # per-page override
|
|
44
|
+
show_page_breadcrumbs true
|
|
45
|
+
new_page_breadcrumbs true
|
|
46
|
+
edit_page_breadcrumbs true
|
|
47
|
+
interactive_action_page_breadcrumbs true
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Page hooks (preferred over `view_template`)
|
|
52
|
+
|
|
53
|
+
Every page inherits these — use them instead of overriding `view_template` to preserve breadcrumbs, header, and DynaFrame behavior:
|
|
54
|
+
|
|
55
|
+
| Hook | Position |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `render_before_header` / `_after_header` | wraps the entire header section |
|
|
58
|
+
| `render_before_breadcrumbs` / `_after_breadcrumbs` | around the breadcrumb row |
|
|
59
|
+
| `render_before_page_header` / `_after_page_header` | around the title + actions block |
|
|
60
|
+
| `render_before_toolbar` / `_after_toolbar` | around the action toolbar |
|
|
61
|
+
| `render_before_content` / `_after_content` | around main content |
|
|
62
|
+
| `render_before_footer` / `_after_footer` | around footer/pagination |
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
class ShowPage < ShowPage
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def page_title
|
|
71
|
+
"#{object.title} — #{object.author.name}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def render_before_content
|
|
75
|
+
div(class: "alert alert-info") do
|
|
76
|
+
plain "This post has #{object.comments.count} comments"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def render_after_content
|
|
81
|
+
render RelatedPostsComponent.new(post: object)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def render_toolbar
|
|
85
|
+
div(class: "flex gap-2") do
|
|
86
|
+
button(class: "pu-btn pu-btn-md pu-btn-secondary") { "Preview" }
|
|
87
|
+
button(class: "pu-btn pu-btn-md pu-btn-primary") { "Publish" }
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Custom ERB views (full replacement)
|
|
94
|
+
|
|
95
|
+
For total control, drop the page class entirely with an ERB view at the controller path:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
app/views/posts/show.html.erb
|
|
99
|
+
packages/admin_portal/app/views/admin_portal/posts/show.html.erb
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The default view simply renders the page class:
|
|
103
|
+
|
|
104
|
+
```erb
|
|
105
|
+
<%= render current_definition.show_page_class.new %>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Mix — keep the default and add chrome around it:
|
|
109
|
+
|
|
110
|
+
```erb
|
|
111
|
+
<div class="announcement-banner">Special announcement</div>
|
|
112
|
+
<%= render current_definition.show_page_class.new %>
|
|
113
|
+
<div class="related"><%= render partial: "related" %></div>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Detecting render context
|
|
117
|
+
|
|
118
|
+
| Helper | True when |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `in_frame?` | Request targets a turbo-frame |
|
|
121
|
+
| `in_modal?` | Request renders inside a modal/slideover (primary or secondary) |
|
|
122
|
+
| `in_secondary_modal?` | Request renders inside the stacked secondary modal |
|
|
123
|
+
|
|
124
|
+
Use to pin action strips, omit nav chrome, or swap layouts.
|
|
125
|
+
|
|
126
|
+
### Stacked modals (secondary frame)
|
|
127
|
+
|
|
128
|
+
Association inputs include an inline `+` button. When the parent form is itself rendered in a modal, the `+` opens a **second stacked modal** in `Plutonium::REMOTE_MODAL_SECONDARY_FRAME` instead of replacing the primary modal. On successful create, the secondary closes and the primary frame reloads so the new record appears in the select — no developer wiring.
|
|
129
|
+
|
|
130
|
+
For custom flows: `helpers.turbo_stream_close_frame(frame_id)` and `helpers.turbo_stream_reload_frame(frame_id)` are available.
|
|
131
|
+
|
|
132
|
+
See [Forms › Association inputs](./forms#association-inputs).
|
|
133
|
+
|
|
134
|
+
## Modals & slideovers
|
|
135
|
+
|
|
136
|
+
The framework's `:new` / `:edit` actions render inline inside a modal. Choose the chrome per-resource via the definition:
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
class PostDefinition < ResourceDefinition
|
|
140
|
+
modal :slideover # default — slide-in panel from the right
|
|
141
|
+
# modal :centered # centered dialog
|
|
142
|
+
# modal false # full standalone pages (no modal)
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Custom interactive actions render in their own dialog with their own per-action `modal:` option (`:centered` default, or `:slideover`). See [Resource › Actions](/reference/resource/actions#action-options).
|
|
147
|
+
|
|
148
|
+
## Tabs on the show page
|
|
149
|
+
|
|
150
|
+
Show pages with `permitted_associations` (see [Behavior › Policy](/reference/behavior/policies#association-permissions)) render a tablist: **Details** tab first, then one tab per association. The active tab is reflected in the URL hash (`#products`, `#refund-requests`) so the page deep-links and the active state survives reload / back navigation. Tab rows scroll horizontally on narrow viewports — they don't wrap.
|
|
151
|
+
|
|
152
|
+
## Portal-specific overrides
|
|
153
|
+
|
|
154
|
+
Each portal can override page classes independently. The portal definition inherits from the base definition, and its nested classes inherit from the base's nested classes:
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
class AdminPortal::PostDefinition < ::PostDefinition
|
|
158
|
+
class ShowPage < ShowPage # inherits from ::PostDefinition::ShowPage
|
|
159
|
+
def render_after_content
|
|
160
|
+
super
|
|
161
|
+
render AdminOnlySection.new(post: object)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Available context
|
|
168
|
+
|
|
169
|
+
Inside any page / form / display / Phlex component, the same set of helpers is available — model accessors, definition/policy methods, URL helpers, `current_user`. For the full list, see [Behavior › Controllers › Key methods](/reference/behavior/controllers#key-methods) — pages inherit the same surface.
|
|
170
|
+
|
|
171
|
+
In Phlex components, Rails helpers are accessed via the `helpers` proxy:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
class MyComponent < Plutonium::UI::Component::Base
|
|
175
|
+
def view_template
|
|
176
|
+
helpers.link_to(...)
|
|
177
|
+
helpers.number_to_currency(...)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Related
|
|
183
|
+
|
|
184
|
+
- [Forms](./forms) — Form class, field builder, themes
|
|
185
|
+
- [Displays](./displays) — show-page Display class
|
|
186
|
+
- [Tables](./tables) — index-page Table class
|
|
187
|
+
- [Components](./components) — built-in component kit, custom Phlex components, DynaFrame
|
|
188
|
+
- [Layouts](./layouts) — overall shell, eject, ResourceLayout
|
|
189
|
+
- [Resource › Definition](/reference/resource/definition) — page titles, breadcrumbs, modal mode, metadata panel
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Tables
|
|
2
|
+
|
|
3
|
+
The index page's table rendering. Override the `Table` nested class in your definition for custom layouts (e.g. card grids).
|
|
4
|
+
|
|
5
|
+
## Custom table template
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
class PostDefinition < ResourceDefinition
|
|
9
|
+
class Table < Table
|
|
10
|
+
def view_template
|
|
11
|
+
render_search_bar
|
|
12
|
+
render_scopes_bar
|
|
13
|
+
|
|
14
|
+
if collection.empty?
|
|
15
|
+
render_empty_card
|
|
16
|
+
else
|
|
17
|
+
# Replace the table with a card grid
|
|
18
|
+
div(class: "grid grid-cols-3 gap-4") do
|
|
19
|
+
collection.each { |post| render PostCardComponent.new(post:) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
render_footer
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Methods
|
|
30
|
+
|
|
31
|
+
| Method | Purpose |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `render_search_bar` | Toolbar search input |
|
|
34
|
+
| `render_scopes_bar` | Quick-filter scope buttons |
|
|
35
|
+
| `render_table` | Default table rendering |
|
|
36
|
+
| `render_empty_card` | Empty state |
|
|
37
|
+
| `render_footer` | Pagination |
|
|
38
|
+
| `collection` | Paginated records |
|
|
39
|
+
| `resource_fields` | Column field names |
|
|
40
|
+
|
|
41
|
+
## Per-column customization
|
|
42
|
+
|
|
43
|
+
Prefer declaring column behavior in the **definition** rather than overriding the entire `Table`:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
class PostDefinition < ResourceDefinition
|
|
47
|
+
column :title, align: :start # default
|
|
48
|
+
column :status, align: :center
|
|
49
|
+
column :amount, align: :end
|
|
50
|
+
|
|
51
|
+
# formatter — receives just the value
|
|
52
|
+
column :description, formatter: ->(value) { value&.truncate(30) }
|
|
53
|
+
column :price, formatter: ->(value) { "$%.2f" % value if value }
|
|
54
|
+
|
|
55
|
+
# block — receives the full record
|
|
56
|
+
column :full_name do |record|
|
|
57
|
+
"#{record.first_name} #{record.last_name}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
See [Resource › Definition › Column options](/reference/resource/definition#column-options).
|
|
63
|
+
|
|
64
|
+
## Grid view
|
|
65
|
+
|
|
66
|
+
For card-based layouts as a switchable alternative to the table, use the built-in Grid view — declare `grid_fields` in the definition:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
class UserDefinition < ResourceDefinition
|
|
70
|
+
grid_fields(
|
|
71
|
+
image: :avatar,
|
|
72
|
+
header: :name,
|
|
73
|
+
subheader: :email,
|
|
74
|
+
body: :bio,
|
|
75
|
+
meta: [:role, :status],
|
|
76
|
+
footer: :last_seen_at
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
default_index_view :grid
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
See [Resource › Definition › Index views](/reference/resource/definition#index-views-table-grid). You only need a custom `Table` class when you want something neither Table nor Grid covers.
|
|
84
|
+
|
|
85
|
+
## Theming
|
|
86
|
+
|
|
87
|
+
Override the theme via a nested `Theme` class:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
class PostDefinition < ResourceDefinition
|
|
91
|
+
class Table < Table
|
|
92
|
+
class Theme < Plutonium::UI::Table::Theme
|
|
93
|
+
def self.theme
|
|
94
|
+
super.merge(
|
|
95
|
+
wrapper: "pu-table-wrapper",
|
|
96
|
+
base: "pu-table",
|
|
97
|
+
header: "pu-table-header",
|
|
98
|
+
header_cell: "pu-table-header-cell",
|
|
99
|
+
body_row: "pu-table-body-row",
|
|
100
|
+
body_cell: "pu-table-body-cell"
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Theme keys
|
|
109
|
+
|
|
110
|
+
`wrapper`, `base`, `header`, `header_cell`, `body_row`, `body_cell`, `sort_icon`.
|
|
111
|
+
|
|
112
|
+
## Related
|
|
113
|
+
|
|
114
|
+
- [Pages](./pages) — `IndexPage` render hooks (a lighter alternative for top/bottom chrome)
|
|
115
|
+
- [Components](./components) — `PostCardComponent` and other reusable Phlex pieces
|
|
116
|
+
- [Resource › Definition](/reference/resource/definition) — column configuration, grid view
|
|
117
|
+
- [Resource › Query](/reference/resource/query) — search, filters, scopes, sort
|