plutonium 0.53.1 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium-behavior/SKILL.md +22 -0
  3. data/.claude/skills/plutonium-resource/SKILL.md +55 -0
  4. data/.claude/skills/plutonium-ui/SKILL.md +2 -1
  5. data/CHANGELOG.md +38 -0
  6. data/app/assets/plutonium.css +1 -1
  7. data/app/assets/plutonium.js +40 -6
  8. data/app/assets/plutonium.js.map +4 -4
  9. data/app/assets/plutonium.min.js +32 -32
  10. data/app/assets/plutonium.min.js.map +4 -4
  11. data/app/views/plutonium/_flash_alerts.html.erb +8 -17
  12. data/app/views/plutonium/_flash_toasts.html.erb +9 -18
  13. data/docs/public/images/reference/structured-inputs-removed.png +0 -0
  14. data/docs/public/images/reference/structured-inputs.png +0 -0
  15. data/docs/reference/resource/definition.md +110 -0
  16. data/docs/superpowers/plans/2026-06-02-structured-inputs.md +1061 -0
  17. data/docs/superpowers/plans/2026-06-02-structured-inputs.md.tasks.json +60 -0
  18. data/docs/superpowers/specs/2026-06-01-structured-inputs-design.md +191 -0
  19. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  20. data/lib/plutonium/definition/base.rb +1 -0
  21. data/lib/plutonium/definition/structured_inputs.rb +67 -0
  22. data/lib/plutonium/engine/validator.rb +11 -4
  23. data/lib/plutonium/interaction/README.md +24 -78
  24. data/lib/plutonium/interaction/base.rb +10 -2
  25. data/lib/plutonium/resource/controller.rb +6 -1
  26. data/lib/plutonium/resource/controllers/interactive_actions.rb +10 -6
  27. data/lib/plutonium/structured_inputs/param_cleaner.rb +36 -0
  28. data/lib/plutonium/structured_inputs/params_concern.rb +36 -0
  29. data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +3 -3
  30. data/lib/plutonium/ui/form/concerns/renders_structured_inputs.rb +178 -0
  31. data/lib/plutonium/ui/form/concerns/repeater_field_styles.rb +24 -0
  32. data/lib/plutonium/ui/form/resource.rb +15 -5
  33. data/lib/plutonium/ui/form/theme.rb +14 -3
  34. data/lib/plutonium/ui/modal/slideover.rb +9 -3
  35. data/lib/plutonium/version.rb +1 -1
  36. data/package.json +1 -1
  37. data/src/css/components.css +119 -5
  38. data/src/js/controllers/capture_url_controller.js +20 -7
  39. data/src/js/controllers/dirty_form_guard_controller.js +28 -4
  40. data/src/js/controllers/icon_rail_flyout_controller.js +5 -2
  41. data/src/js/controllers/register_controllers.js +2 -0
  42. data/src/js/controllers/remote_modal_controller.js +5 -0
  43. data/src/js/controllers/structured_input_row_controller.js +26 -0
  44. metadata +14 -4
  45. data/lib/plutonium/interaction/nested_attributes.rb +0 -93
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plutonium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.53.1
4
+ version: 0.55.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Froelich
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-05-31 00:00:00.000000000 Z
10
+ date: 2026-06-03 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: zeitwerk
@@ -593,6 +593,8 @@ files:
593
593
  - docs/public/images/home-index.png
594
594
  - docs/public/images/home-new.png
595
595
  - docs/public/images/home-show.png
596
+ - docs/public/images/reference/structured-inputs-removed.png
597
+ - docs/public/images/reference/structured-inputs.png
596
598
  - docs/public/images/tutorial/02-empty-index.png
597
599
  - docs/public/images/tutorial/02-index-with-posts.png
598
600
  - docs/public/images/tutorial/02-new-form-modal.png
@@ -663,6 +665,8 @@ files:
663
665
  - docs/superpowers/plans/2026-05-07-ui-layout-overhaul.md.tasks.json
664
666
  - docs/superpowers/plans/2026-05-15-public-pages-overhaul.md
665
667
  - docs/superpowers/plans/2026-05-15-public-pages-overhaul.md.tasks.json
668
+ - docs/superpowers/plans/2026-06-02-structured-inputs.md
669
+ - docs/superpowers/plans/2026-06-02-structured-inputs.md.tasks.json
666
670
  - docs/superpowers/specs/2026-04-08-plutonium-skills-overhaul-design.md
667
671
  - docs/superpowers/specs/2026-04-14-plutonium-testing-design.md
668
672
  - docs/superpowers/specs/2026-05-07-ui-layout-overhaul-design.md
@@ -671,6 +675,7 @@ files:
671
675
  - docs/superpowers/specs/2026-05-13-docs-restructure-design.md
672
676
  - docs/superpowers/specs/2026-05-15-public-pages-overhaul-design.md
673
677
  - docs/superpowers/specs/2026-05-29-avatar-component-design.md
678
+ - docs/superpowers/specs/2026-06-01-structured-inputs-design.md
674
679
  - esbuild.config.js
675
680
  - exe/pug
676
681
  - gemfiles/rails_7.gemfile
@@ -973,6 +978,7 @@ files:
973
978
  - lib/plutonium/definition/scoping.rb
974
979
  - lib/plutonium/definition/search.rb
975
980
  - lib/plutonium/definition/sorting.rb
981
+ - lib/plutonium/definition/structured_inputs.rb
976
982
  - lib/plutonium/engine.rb
977
983
  - lib/plutonium/engine/validator.rb
978
984
  - lib/plutonium/helpers.rb
@@ -986,7 +992,6 @@ files:
986
992
  - lib/plutonium/interaction/base.rb
987
993
  - lib/plutonium/interaction/concerns/scoping.rb
988
994
  - lib/plutonium/interaction/concerns/workflow_dsl.rb
989
- - lib/plutonium/interaction/nested_attributes.rb
990
995
  - lib/plutonium/interaction/outcome.rb
991
996
  - lib/plutonium/interaction/response/base.rb
992
997
  - lib/plutonium/interaction/response/failure.rb
@@ -1050,6 +1055,8 @@ files:
1050
1055
  - lib/plutonium/routing/mapper_extensions.rb
1051
1056
  - lib/plutonium/routing/resource_registration.rb
1052
1057
  - lib/plutonium/routing/route_set_extensions.rb
1058
+ - lib/plutonium/structured_inputs/param_cleaner.rb
1059
+ - lib/plutonium/structured_inputs/params_concern.rb
1053
1060
  - lib/plutonium/support/parameters.rb
1054
1061
  - lib/plutonium/testing.rb
1055
1062
  - lib/plutonium/testing/auth_helpers.rb
@@ -1100,6 +1107,8 @@ files:
1100
1107
  - lib/plutonium/ui/form/components/sticky_footer.rb
1101
1108
  - lib/plutonium/ui/form/components/uppy.rb
1102
1109
  - lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb
1110
+ - lib/plutonium/ui/form/concerns/renders_structured_inputs.rb
1111
+ - lib/plutonium/ui/form/concerns/repeater_field_styles.rb
1103
1112
  - lib/plutonium/ui/form/concerns/typeahead_attributes.rb
1104
1113
  - lib/plutonium/ui/form/interaction.rb
1105
1114
  - lib/plutonium/ui/form/options/inferred_types.rb
@@ -1205,6 +1214,7 @@ files:
1205
1214
  - src/js/controllers/select_navigator.js
1206
1215
  - src/js/controllers/sidebar_controller.js
1207
1216
  - src/js/controllers/slim_select_controller.js
1217
+ - src/js/controllers/structured_input_row_controller.js
1208
1218
  - src/js/controllers/table_column_menu_controller.js
1209
1219
  - src/js/controllers/table_header_controller.js
1210
1220
  - src/js/controllers/textarea_autogrow_controller.js
@@ -1229,7 +1239,7 @@ metadata:
1229
1239
  homepage_uri: https://radioactive-labs.github.io/plutonium-core/
1230
1240
  source_code_uri: https://github.com/radioactive-labs/plutonium-core
1231
1241
  post_install_message: |
1232
- ⚠️ Plutonium 0.53.1 — breaking change
1242
+ ⚠️ Plutonium 0.55.0 — breaking change
1233
1243
 
1234
1244
  Entity-scoped URL helpers and path params have been renamed from
1235
1245
  `<entity>_scope_*` to `<entity>_scoped_*`.
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Plutonium
4
- module Interaction
5
- module NestedAttributes
6
- extend ActiveSupport::Concern
7
-
8
- class_methods do
9
- # Dynamically defines writer and reader methods for handling nested
10
- # attributes in form objects or interaction classes, mimicking the
11
- # behavior of ActiveRecord's `accepts_nested_attributes_for`.
12
- #
13
- # This method allows you to pass in nested data (e.g. from a form) and
14
- # automatically build or destroy associated records based on that input.
15
- #
16
- # === Example 1: Basic usage with default naming
17
- # # If `Contact` is the associated model inferred from the
18
- # `:contacts` association:
19
- #
20
- # `accepts_nested_attributes_for :contacts`
21
- #
22
- # === Example 2: When association name and model name differ
23
- # Suppose the `User` model has a `has_many :contacts` association
24
- # pointing to a `UserContactInfo` model. You need to specify the
25
- # model name.
26
- #
27
- # `accepts_nested_attributes_for :contacts, class_name: "UserAddress"`
28
- #
29
- # This macro defines:
30
- # - `contacts_attributes=` — used to assign nested attributes,
31
- # including support for `_destroy`
32
- # - `contacts_attributes` — returns the current attributes of
33
- # associated records
34
- #
35
- # @param association [Symbol] The association name. (e.g., `:contacts`).
36
- # @param class_name [String, nil] Required if association reflection
37
- # is needed to determine the associated model class (e.g. when the
38
- # association name doesn't match the class name).
39
- # @param reject_if [Proc, Symbol, nil] Used to skip building association
40
- # records when the condition returns true.
41
- def accepts_nested_attributes_for(
42
- association,
43
- class_name: nil,
44
- reject_if: nil
45
- )
46
- destroy_values = [1, "1", "true", true]
47
-
48
- should_destroy = ->(value) { destroy_values.include?(value) }
49
-
50
- should_reject =
51
- lambda do |attrs|
52
- case reject_if
53
- when Symbol
54
- send(reject_if, attrs)
55
- when Proc
56
- reject_if.call(attrs)
57
- else
58
- false
59
- end
60
- end
61
-
62
- assoc_class =
63
- class_name&.constantize || association.to_s.classify.constantize
64
-
65
- define_method(:"#{association}_attributes=") do |attributes|
66
- result =
67
- case attributes
68
- when Hash
69
- attrs = attributes.except(:_destroy)
70
- unless should_destroy.call(attributes[:_destroy]) ||
71
- should_reject.call(attrs)
72
- assoc_class.new(attrs)
73
- end
74
- when Array
75
- attributes.filter_map do |attrs|
76
- unless should_destroy.call(attrs[:_destroy]) ||
77
- should_reject.call(attrs)
78
- assoc_class.new(attrs.except(:_destroy))
79
- end
80
- end
81
- end
82
-
83
- send(:"#{association}=", result)
84
- end
85
-
86
- define_method(:"#{association}_attributes") do
87
- Array(send(association)).map(&:attributes)
88
- end
89
- end
90
- end
91
- end
92
- end
93
- end