plutonium 0.37.0 → 0.39.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-controller/SKILL.md +38 -2
- data/.claude/skills/plutonium-definition-actions/SKILL.md +13 -0
- data/.claude/skills/plutonium-definition-fields/SKILL.md +33 -0
- data/.claude/skills/plutonium-nested-resources/SKILL.md +85 -23
- data/.claude/skills/plutonium-policy/SKILL.md +93 -6
- data/CHANGELOG.md +42 -0
- data/CLAUDE.md +8 -10
- data/CONTRIBUTING.md +6 -8
- data/Rakefile +16 -1
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +9371 -11492
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +55 -55
- data/app/assets/plutonium.min.js.map +4 -4
- data/docs/guides/custom-actions.md +14 -0
- data/docs/guides/index.md +5 -0
- data/docs/guides/nested-resources.md +139 -32
- data/docs/guides/troubleshooting.md +82 -0
- data/docs/og-image.html +84 -0
- data/docs/public/og-image.png +0 -0
- data/docs/reference/controller/index.md +6 -2
- data/docs/reference/definition/actions.md +14 -0
- data/docs/reference/definition/fields.md +33 -0
- data/docs/reference/model/index.md +1 -1
- data/docs/reference/policy/index.md +77 -6
- data/gemfiles/rails_7.gemfile.lock +5 -5
- data/gemfiles/rails_8.0.gemfile.lock +5 -5
- data/gemfiles/rails_8.1.gemfile.lock +5 -5
- data/lib/generators/pu/rodauth/install_generator.rb +7 -11
- data/lib/generators/pu/rodauth/templates/app/rodauth/rodauth_plugin.rb.tt +3 -5
- data/lib/plutonium/auth/sequel_adapter.rb +76 -0
- data/lib/plutonium/core/controller.rb +143 -19
- data/lib/plutonium/core/controllers/association_resolver.rb +86 -0
- data/lib/plutonium/helpers/display_helper.rb +12 -0
- data/lib/plutonium/query/filters/association.rb +25 -3
- data/lib/plutonium/resource/controller.rb +91 -9
- data/lib/plutonium/resource/controllers/authorizable.rb +17 -4
- data/lib/plutonium/resource/controllers/crud_actions.rb +7 -5
- data/lib/plutonium/resource/controllers/interactive_actions.rb +9 -0
- data/lib/plutonium/resource/controllers/presentable.rb +15 -11
- data/lib/plutonium/resource/policy.rb +85 -2
- data/lib/plutonium/resource/record/routes.rb +31 -1
- data/lib/plutonium/routing/mapper_extensions.rb +49 -10
- data/lib/plutonium/routing/route_set_extensions.rb +3 -0
- data/lib/plutonium/ui/action_button.rb +72 -11
- data/lib/plutonium/ui/actions_dropdown.rb +3 -25
- data/lib/plutonium/ui/breadcrumbs.rb +2 -2
- data/lib/plutonium/ui/component/methods.rb +10 -3
- data/lib/plutonium/ui/display/resource.rb +5 -2
- data/lib/plutonium/ui/form/base.rb +1 -1
- data/lib/plutonium/ui/form/components/key_value_store.rb +17 -5
- data/lib/plutonium/ui/form/interaction.rb +5 -5
- data/lib/plutonium/ui/form/query.rb +1 -1
- data/lib/plutonium/ui/form/resource.rb +1 -1
- data/lib/plutonium/ui/layout/base.rb +1 -1
- data/lib/plutonium/ui/layout/basic_layout.rb +2 -2
- data/lib/plutonium/ui/layout/resource_layout.rb +2 -2
- data/lib/plutonium/ui/layout/rodauth_layout.rb +2 -2
- data/lib/plutonium/ui/page/index.rb +1 -1
- data/lib/plutonium/ui/page/interactive_action.rb +1 -1
- data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +3 -25
- data/lib/plutonium/version.rb +1 -1
- data/lib/tasks/release.rake +1 -1
- data/package.json +6 -5
- data/plutonium.gemspec +2 -2
- data/src/js/controllers/key_value_store_controller.js +6 -0
- data/src/js/controllers/resource_drop_down_controller.js +3 -3
- data/yarn.lock +1465 -693
- metadata +10 -7
- data/app/javascript/controllers/key_value_store_controller.js +0 -119
|
@@ -17,6 +17,13 @@ module Plutonium
|
|
|
17
17
|
secondary: {default: "pu-btn-secondary", soft: "pu-btn-soft-secondary"}
|
|
18
18
|
}.freeze
|
|
19
19
|
|
|
20
|
+
# Color to CSS class mapping for dropdown item variants
|
|
21
|
+
DROPDOWN_COLOR_CLASSES = {
|
|
22
|
+
danger: "text-danger-600 dark:text-danger-400 hover:bg-danger-50 dark:hover:bg-danger-900/30"
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
DROPDOWN_DEFAULT_COLOR = "text-[var(--pu-text)] hover:bg-[var(--pu-surface-alt)]"
|
|
26
|
+
|
|
20
27
|
def initialize(action, url:, variant: :default)
|
|
21
28
|
@action = action
|
|
22
29
|
@url = url
|
|
@@ -24,24 +31,23 @@ module Plutonium
|
|
|
24
31
|
end
|
|
25
32
|
|
|
26
33
|
def view_template
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
case @variant
|
|
35
|
+
when :dropdown, :row_dropdown
|
|
36
|
+
render_dropdown_item
|
|
29
37
|
else
|
|
30
|
-
|
|
38
|
+
if @action.route_options.method == :get
|
|
39
|
+
render_link
|
|
40
|
+
else
|
|
41
|
+
render_button
|
|
42
|
+
end
|
|
31
43
|
end
|
|
32
44
|
end
|
|
33
45
|
|
|
34
46
|
private
|
|
35
47
|
|
|
36
48
|
def render_link
|
|
37
|
-
uri = URI.parse(@url)
|
|
38
|
-
params = Rack::Utils.parse_nested_query(uri.query)
|
|
39
|
-
params["return_to"] = @action.return_to.nil? ? request.original_url : @action.return_to
|
|
40
|
-
uri.query = params.to_query
|
|
41
|
-
uri.to_s
|
|
42
|
-
|
|
43
49
|
link_to(
|
|
44
|
-
|
|
50
|
+
url_with_return_to,
|
|
45
51
|
class: button_classes,
|
|
46
52
|
data: {turbo_frame: @action.turbo_frame}
|
|
47
53
|
) do
|
|
@@ -53,7 +59,7 @@ module Plutonium
|
|
|
53
59
|
button_to(
|
|
54
60
|
@url,
|
|
55
61
|
method: @action.route_options.method,
|
|
56
|
-
name: :return_to, value:
|
|
62
|
+
name: :return_to, value: return_to_url,
|
|
57
63
|
class: "inline-block",
|
|
58
64
|
form: {
|
|
59
65
|
data: {
|
|
@@ -69,6 +75,28 @@ module Plutonium
|
|
|
69
75
|
end
|
|
70
76
|
end
|
|
71
77
|
|
|
78
|
+
def render_dropdown_item
|
|
79
|
+
link_attrs = {
|
|
80
|
+
href: url_with_return_to,
|
|
81
|
+
class: dropdown_item_classes
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Add turbo frame if specified
|
|
85
|
+
link_attrs[:data] = {turbo_frame: @action.turbo_frame} if @action.turbo_frame
|
|
86
|
+
|
|
87
|
+
# Add confirmation and method for non-GET requests
|
|
88
|
+
if @action.confirmation || @action.route_options.method != :get
|
|
89
|
+
link_attrs[:data] ||= {}
|
|
90
|
+
link_attrs[:data][:turbo_method] = @action.route_options.method if @action.route_options.method != :get
|
|
91
|
+
link_attrs[:data][:turbo_confirm] = @action.confirmation if @action.confirmation
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
a(**link_attrs) do
|
|
95
|
+
render @action.icon.new(class: "w-4 h-4") if @action.icon
|
|
96
|
+
span { @action.label }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
72
100
|
def render_button_content
|
|
73
101
|
if @action.icon
|
|
74
102
|
render @action.icon.new(class: icon_classes)
|
|
@@ -100,6 +128,39 @@ module Plutonium
|
|
|
100
128
|
# Table variant uses soft (tinted) buttons, default uses solid buttons
|
|
101
129
|
(@variant == :table) ? color_mapping[:soft] : color_mapping[:default]
|
|
102
130
|
end
|
|
131
|
+
|
|
132
|
+
def dropdown_item_classes
|
|
133
|
+
base_classes = "flex items-center gap-2 text-sm transition-colors"
|
|
134
|
+
size_classes = (@variant == :row_dropdown) ? "px-3 py-1.5" : "px-4 py-2"
|
|
135
|
+
|
|
136
|
+
# Use same color determination as buttons: color || category
|
|
137
|
+
color_key = (@action.color || @action.category)&.to_sym
|
|
138
|
+
color_classes = DROPDOWN_COLOR_CLASSES[color_key] || DROPDOWN_DEFAULT_COLOR
|
|
139
|
+
|
|
140
|
+
"#{base_classes} #{size_classes} #{color_classes}"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def url_with_return_to
|
|
144
|
+
uri = URI.parse(@url)
|
|
145
|
+
params = Rack::Utils.parse_nested_query(uri.query)
|
|
146
|
+
params["return_to"] = return_to_url
|
|
147
|
+
uri.query = params.to_query
|
|
148
|
+
uri.to_s
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def default_return_to
|
|
152
|
+
# When in a turbo frame with a parent, return to parent's show page
|
|
153
|
+
# instead of the frame's URL (which would be the nested index)
|
|
154
|
+
if current_turbo_frame && current_parent
|
|
155
|
+
resource_url_for(current_parent, parent: nil)
|
|
156
|
+
else
|
|
157
|
+
request.original_url
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def return_to_url
|
|
162
|
+
@action.return_to.nil? ? default_return_to : @action.return_to
|
|
163
|
+
end
|
|
103
164
|
end
|
|
104
165
|
end
|
|
105
166
|
end
|
|
@@ -56,35 +56,13 @@ module Plutonium
|
|
|
56
56
|
|
|
57
57
|
def render_danger_actions
|
|
58
58
|
div(class: "py-1") do
|
|
59
|
-
danger_actions.each { |action| render_action_item(action
|
|
59
|
+
danger_actions.each { |action| render_action_item(action) }
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
def render_action_item(action
|
|
63
|
+
def render_action_item(action)
|
|
64
64
|
url = route_options_to_url(action.route_options, @subject)
|
|
65
|
-
|
|
66
|
-
link_attrs = {
|
|
67
|
-
href: url,
|
|
68
|
-
class: tokens(
|
|
69
|
-
"flex items-center gap-2 px-4 py-2 text-sm transition-colors",
|
|
70
|
-
danger ? "text-danger-600 dark:text-danger-400 hover:bg-danger-50 dark:hover:bg-danger-900/30" : "text-[var(--pu-text)] hover:bg-[var(--pu-surface-alt)]"
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
# Add turbo frame if specified
|
|
75
|
-
link_attrs[:data] = {turbo_frame: action.turbo_frame} if action.turbo_frame
|
|
76
|
-
|
|
77
|
-
# Add confirmation if specified
|
|
78
|
-
if action.confirmation
|
|
79
|
-
link_attrs[:data] ||= {}
|
|
80
|
-
link_attrs[:data][:turbo_method] = :delete if action.route_options.method == :delete
|
|
81
|
-
link_attrs[:data][:turbo_confirm] = action.confirmation
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
a(**link_attrs) do
|
|
85
|
-
render action.icon.new(class: "w-4 h-4") if action.icon
|
|
86
|
-
span { action.label }
|
|
87
|
-
end
|
|
65
|
+
render ActionButton.new(action, url: url, variant: :dropdown)
|
|
88
66
|
end
|
|
89
67
|
|
|
90
68
|
def secondary_actions
|
|
@@ -15,7 +15,7 @@ module Plutonium
|
|
|
15
15
|
# Dashboard
|
|
16
16
|
li(class: "inline-flex items-center") do
|
|
17
17
|
a(
|
|
18
|
-
href:
|
|
18
|
+
href: root_path,
|
|
19
19
|
class: "inline-flex items-center text-sm font-medium text-[var(--pu-text-muted)] hover:text-primary-600 transition-colors"
|
|
20
20
|
) do
|
|
21
21
|
svg(
|
|
@@ -101,7 +101,7 @@ module Plutonium
|
|
|
101
101
|
d: "m1 9 4-4-4-4"
|
|
102
102
|
)
|
|
103
103
|
end
|
|
104
|
-
link_to
|
|
104
|
+
link_to nestable_resource_name_plural(resource_class),
|
|
105
105
|
resource_url_for(resource_class),
|
|
106
106
|
class: "ms-1 text-sm font-medium text-[var(--pu-text-muted)] hover:text-primary-600 md:ms-2 transition-colors"
|
|
107
107
|
end
|
|
@@ -11,15 +11,19 @@ module Plutonium
|
|
|
11
11
|
private
|
|
12
12
|
|
|
13
13
|
def params
|
|
14
|
-
|
|
14
|
+
view_context.controller.params
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def request
|
|
18
|
-
|
|
18
|
+
view_context.controller.request
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def pagy_instance
|
|
22
|
-
|
|
22
|
+
view_context.controller.instance_variable_get(:@pagy)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def controller
|
|
26
|
+
view_context.controller
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
delegate \
|
|
@@ -29,6 +33,7 @@ module Plutonium
|
|
|
29
33
|
:resource_record?,
|
|
30
34
|
:resource_name,
|
|
31
35
|
:resource_name_plural,
|
|
36
|
+
:nestable_resource_name_plural,
|
|
32
37
|
:display_name_of,
|
|
33
38
|
:resource_url_for,
|
|
34
39
|
:route_options_to_url,
|
|
@@ -46,6 +51,8 @@ module Plutonium
|
|
|
46
51
|
:allowed_to?,
|
|
47
52
|
:registered_resources,
|
|
48
53
|
:root_path,
|
|
54
|
+
:make_page_title,
|
|
55
|
+
:resource_logo_tag,
|
|
49
56
|
to: :view_context
|
|
50
57
|
end
|
|
51
58
|
end
|
|
@@ -48,11 +48,14 @@ module Plutonium
|
|
|
48
48
|
|
|
49
49
|
title = object.class.human_attribute_name(name)
|
|
50
50
|
src = case reflection.macro
|
|
51
|
-
when :belongs_to
|
|
51
|
+
when :belongs_to
|
|
52
52
|
associated = object.public_send name
|
|
53
53
|
resource_url_for(associated, parent: nil) if associated
|
|
54
|
+
when :has_one
|
|
55
|
+
associated = object.public_send name
|
|
56
|
+
resource_url_for(associated, parent: object, association: name)
|
|
54
57
|
when :has_many
|
|
55
|
-
resource_url_for(reflection.klass, parent: object)
|
|
58
|
+
resource_url_for(reflection.klass, parent: object, association: name)
|
|
56
59
|
end
|
|
57
60
|
|
|
58
61
|
next unless src
|
|
@@ -109,7 +109,7 @@ module Plutonium
|
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
def form_action
|
|
112
|
-
return @form_action unless object.present? && @form_action != false &&
|
|
112
|
+
return @form_action unless object.present? && @form_action != false && view_context.present?
|
|
113
113
|
|
|
114
114
|
@form_action ||= url_for(object, action: object.new_record? ? :create : :update)
|
|
115
115
|
end
|
|
@@ -60,6 +60,12 @@ module Plutonium
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def render_key_value_pairs
|
|
63
|
+
# Hidden sentinel input ensures the field is always present in params when the
|
|
64
|
+
# component is rendered. Without this, removing all pairs would submit nothing,
|
|
65
|
+
# making it impossible to distinguish "field not in form" from "field cleared".
|
|
66
|
+
# This allows normalize_input to return nil (preserve existing) vs {} (clear field).
|
|
67
|
+
input(type: :hidden, name: "#{field_name}[_submitted]", value: "1", autocomplete: "off", hidden: true)
|
|
68
|
+
|
|
63
69
|
div(class: "key-value-pairs space-y-2", data_key_value_store_target: "container") do
|
|
64
70
|
pairs.each_with_index do |(key, value), index|
|
|
65
71
|
render_key_value_pair(key, value, index)
|
|
@@ -196,19 +202,25 @@ module Plutonium
|
|
|
196
202
|
attributes.fetch(:limit, DEFAULT_LIMIT)
|
|
197
203
|
end
|
|
198
204
|
|
|
199
|
-
# Override from ExtractsInput concern to normalize form parameters
|
|
205
|
+
# Override from ExtractsInput concern to normalize form parameters.
|
|
206
|
+
# Returns nil if field wasn't submitted (preserves existing value),
|
|
207
|
+
# or a Hash (possibly empty) if the field was in the form.
|
|
200
208
|
def normalize_input(input_value)
|
|
201
209
|
case input_value
|
|
202
210
|
when Hash
|
|
203
|
-
|
|
211
|
+
# Remove the sentinel key before processing
|
|
212
|
+
params = input_value.except("_submitted", :_submitted)
|
|
213
|
+
|
|
214
|
+
if params.keys.all? { |k| k.to_s.match?(/^\d+$/) }
|
|
204
215
|
# Handle indexed form params: {"0" => {"key" => "foo", "value" => "bar"}}
|
|
205
|
-
process_indexed_params(
|
|
216
|
+
process_indexed_params(params)
|
|
206
217
|
else
|
|
207
218
|
# Handle direct hash params
|
|
208
|
-
|
|
219
|
+
params.reject { |k, v| k.blank? || (v.blank? && v != false) }
|
|
209
220
|
end
|
|
210
221
|
when nil
|
|
211
|
-
|
|
222
|
+
# Field was not submitted at all - preserve existing value
|
|
223
|
+
nil
|
|
212
224
|
end
|
|
213
225
|
end
|
|
214
226
|
|
|
@@ -16,7 +16,7 @@ module Plutonium
|
|
|
16
16
|
|
|
17
17
|
def form_action
|
|
18
18
|
# Build the correct commit URL for the interactive action
|
|
19
|
-
action =
|
|
19
|
+
action = current_interactive_action
|
|
20
20
|
return nil unless action
|
|
21
21
|
|
|
22
22
|
# Create route options for the commit action (convert GET to POST action)
|
|
@@ -28,8 +28,8 @@ module Plutonium
|
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
# Use existing infrastructure to build the URL
|
|
31
|
-
subject = action.record_action? ?
|
|
32
|
-
|
|
31
|
+
subject = action.record_action? ? resource_record! : resource_class
|
|
32
|
+
route_options_to_url(commit_route_options, subject)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def commit_action_name(action_name)
|
|
@@ -52,10 +52,10 @@ module Plutonium
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def render_bulk_action_ids
|
|
55
|
-
action =
|
|
55
|
+
action = current_interactive_action
|
|
56
56
|
return unless action&.bulk_action?
|
|
57
57
|
|
|
58
|
-
ids = Array(
|
|
58
|
+
ids = Array(params[:ids])
|
|
59
59
|
ids.each do |id|
|
|
60
60
|
input(type: :hidden, name: "ids[]", value: id)
|
|
61
61
|
end
|
|
@@ -207,7 +207,7 @@ module Plutonium
|
|
|
207
207
|
def count_active_filters
|
|
208
208
|
count = 0
|
|
209
209
|
query_object.filter_definitions.each do |filter_name, _|
|
|
210
|
-
filter_params =
|
|
210
|
+
filter_params = params.dig(:q, filter_name)
|
|
211
211
|
next unless filter_params.is_a?(Hash) || filter_params.is_a?(ActionController::Parameters)
|
|
212
212
|
|
|
213
213
|
filter_params.each_value do |v|
|
|
@@ -58,7 +58,7 @@ module Plutonium
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def form_action
|
|
61
|
-
return @form_action unless object.present? && @form_action != false &&
|
|
61
|
+
return @form_action unless object.present? && @form_action != false && view_context.present?
|
|
62
62
|
|
|
63
63
|
@form_action ||= resource_url_for(object, action: object.new_record? ? :create : :update)
|
|
64
64
|
end
|
|
@@ -22,7 +22,7 @@ module Plutonium
|
|
|
22
22
|
|
|
23
23
|
def lang = nil
|
|
24
24
|
|
|
25
|
-
def page_title =
|
|
25
|
+
def page_title = view_context.controller.instance_variable_get(:@page_title)
|
|
26
26
|
|
|
27
27
|
def html_attributes = {lang:, data_controller: "color-mode"}
|
|
28
28
|
|
|
@@ -7,7 +7,7 @@ module Plutonium
|
|
|
7
7
|
private
|
|
8
8
|
|
|
9
9
|
def page_title
|
|
10
|
-
|
|
10
|
+
controller.instance_variable_get(:@page_title)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def main_attributes = mix(super, {
|
|
@@ -26,7 +26,7 @@ module Plutonium
|
|
|
26
26
|
|
|
27
27
|
def render_logo
|
|
28
28
|
link_to root_path, class: "flex items-center text-2xl font-semibold text-[var(--pu-text)] mb-2" do
|
|
29
|
-
|
|
29
|
+
resource_logo_tag classname: "w-24 h-24 mr-2 rounded-[var(--pu-radius-md)]"
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -56,35 +56,13 @@ module Plutonium
|
|
|
56
56
|
|
|
57
57
|
def render_danger_actions
|
|
58
58
|
div(class: "py-1") do
|
|
59
|
-
danger_actions.each { |action| render_action_item(action
|
|
59
|
+
danger_actions.each { |action| render_action_item(action) }
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
def render_action_item(action
|
|
63
|
+
def render_action_item(action)
|
|
64
64
|
url = route_options_to_url(action.route_options, @record)
|
|
65
|
-
|
|
66
|
-
link_attrs = {
|
|
67
|
-
href: url,
|
|
68
|
-
class: tokens(
|
|
69
|
-
"flex items-center gap-2 px-3 py-1.5 text-sm transition-colors",
|
|
70
|
-
danger ? "text-danger-600 dark:text-danger-400 hover:bg-danger-50 dark:hover:bg-danger-900/30" : "text-[var(--pu-text)] hover:bg-[var(--pu-surface-alt)]"
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
# Add turbo frame if specified
|
|
75
|
-
link_attrs[:data] = {turbo_frame: action.turbo_frame} if action.turbo_frame
|
|
76
|
-
|
|
77
|
-
# Add confirmation if specified
|
|
78
|
-
if action.confirmation
|
|
79
|
-
link_attrs[:data] ||= {}
|
|
80
|
-
link_attrs[:data][:turbo_method] = action.route_options.method if action.route_options.method
|
|
81
|
-
link_attrs[:data][:turbo_confirm] = action.confirmation
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
a(**link_attrs) do
|
|
85
|
-
render action.icon.new(class: "w-4 h-4") if action.icon
|
|
86
|
-
span { action.label }
|
|
87
|
-
end
|
|
65
|
+
render Plutonium::UI::ActionButton.new(action, url: url, variant: :row_dropdown)
|
|
88
66
|
end
|
|
89
67
|
|
|
90
68
|
def secondary_actions
|
data/lib/plutonium/version.rb
CHANGED
data/lib/tasks/release.rake
CHANGED
|
@@ -102,7 +102,7 @@ namespace :release do
|
|
|
102
102
|
desc "Build front-end assets"
|
|
103
103
|
task :build_frontend do
|
|
104
104
|
puts "Building front-end assets..."
|
|
105
|
-
system("
|
|
105
|
+
system("yarn build") || abort("Front-end build failed")
|
|
106
106
|
puts "✓ Built front-end assets"
|
|
107
107
|
end
|
|
108
108
|
|
data/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radioactive-labs/plutonium",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"description": "Build production-ready Rails apps in minutes, not days. Convention-driven, fully customizable, AI-ready.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/js/core.js",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@tailwindcss/forms": "^0.5.10",
|
|
34
34
|
"@tailwindcss/postcss": "^4.0.9",
|
|
35
35
|
"@tailwindcss/typography": "^0.5.16",
|
|
36
|
+
"chokidar-cli": "^3.0.0",
|
|
36
37
|
"concurrently": "^8.2.2",
|
|
37
38
|
"cssnano": "^7.0.2",
|
|
38
39
|
"esbuild": "^0.20.1",
|
|
@@ -48,10 +49,10 @@
|
|
|
48
49
|
"vitepress-plugin-mermaid": "^2.0.17"
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
51
|
-
"dev": "concurrently \"
|
|
52
|
-
"build": "
|
|
53
|
-
"prepare": "
|
|
54
|
-
"css:dev": "postcss src/css/plutonium.entry.css -o src/build/plutonium.css --
|
|
52
|
+
"dev": "concurrently \"yarn css:dev\" \"yarn js:dev\"",
|
|
53
|
+
"build": "yarn js:prod && yarn css:prod",
|
|
54
|
+
"prepare": "yarn build",
|
|
55
|
+
"css:dev": "chokidar \"src/css/**/*.css\" \"src/js/**/*.js\" \"app/views/**/*.{rb,erb,js}\" \"lib/plutonium/**/*.rb\" -c \"postcss src/css/plutonium.entry.css -o src/build/plutonium.css --dev\" --initial",
|
|
55
56
|
"js:dev": "node esbuild.config.js --dev",
|
|
56
57
|
"css:prod": "postcss src/css/plutonium.entry.css -o app/assets/plutonium.css && postcss src/css/plutonium.entry.css -o src/dist/css/plutonium.css",
|
|
57
58
|
"js:prod": "node esbuild.config.js",
|
data/plutonium.gemspec
CHANGED
|
@@ -44,10 +44,10 @@ Gem::Specification.new do |spec|
|
|
|
44
44
|
spec.add_dependency "phlex-rails"
|
|
45
45
|
spec.add_dependency "phlex-tabler_icons"
|
|
46
46
|
spec.add_dependency "phlexi-field", ">= 0.2.0"
|
|
47
|
-
spec.add_dependency "phlexi-form", ">= 0.
|
|
47
|
+
spec.add_dependency "phlexi-form", ">= 0.14.1"
|
|
48
48
|
spec.add_dependency "phlexi-table", ">= 0.2.0"
|
|
49
49
|
spec.add_dependency "phlexi-display", ">= 0.2.0"
|
|
50
|
-
spec.add_dependency "phlexi-menu", ">= 0.4.
|
|
50
|
+
spec.add_dependency "phlexi-menu", ">= 0.4.1"
|
|
51
51
|
spec.add_dependency "tailwind_merge"
|
|
52
52
|
spec.add_dependency "phlex-slotable", ">= 1.0.0"
|
|
53
53
|
spec.add_dependency "redcarpet"
|
|
@@ -25,6 +25,7 @@ export default class extends Controller {
|
|
|
25
25
|
this.updatePairIndices(newPair, index)
|
|
26
26
|
|
|
27
27
|
this.containerTarget.appendChild(newPair)
|
|
28
|
+
this.updateIndices()
|
|
28
29
|
this.updateAddButtonState()
|
|
29
30
|
|
|
30
31
|
// Focus on the key input of the new pair
|
|
@@ -52,9 +53,11 @@ export default class extends Controller {
|
|
|
52
53
|
|
|
53
54
|
if (keyInput) {
|
|
54
55
|
keyInput.name = keyInput.name.replace(/\[\d+\]/, `[${index}]`)
|
|
56
|
+
keyInput.id = keyInput.id.replace(/_\d+_/, `_${index}_`)
|
|
55
57
|
}
|
|
56
58
|
if (valueInput) {
|
|
57
59
|
valueInput.name = valueInput.name.replace(/\[\d+\]/, `[${index}]`)
|
|
60
|
+
valueInput.id = valueInput.id.replace(/_\d+_/, `_${index}_`)
|
|
58
61
|
}
|
|
59
62
|
})
|
|
60
63
|
}
|
|
@@ -65,6 +68,9 @@ export default class extends Controller {
|
|
|
65
68
|
if (input.name) {
|
|
66
69
|
input.name = input.name.replace('__INDEX__', index)
|
|
67
70
|
}
|
|
71
|
+
if (input.id) {
|
|
72
|
+
input.id = input.id.replace('___INDEX___', `_${index}_`)
|
|
73
|
+
}
|
|
68
74
|
})
|
|
69
75
|
}
|
|
70
76
|
|
|
@@ -42,14 +42,14 @@ export default class extends Controller {
|
|
|
42
42
|
{
|
|
43
43
|
name: 'flip',
|
|
44
44
|
options: {
|
|
45
|
-
fallbackPlacements: ['
|
|
46
|
-
boundary: '
|
|
45
|
+
fallbackPlacements: ['bottom-end', 'bottom-start', 'top', 'top-end', 'top-start'],
|
|
46
|
+
boundary: 'viewport',
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
name: 'preventOverflow',
|
|
51
51
|
options: {
|
|
52
|
-
boundary: '
|
|
52
|
+
boundary: 'viewport',
|
|
53
53
|
altAxis: true,
|
|
54
54
|
padding: 8,
|
|
55
55
|
},
|