plutonium 0.54.0 → 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 (38) 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 +14 -0
  6. data/app/assets/plutonium.css +1 -1
  7. data/app/assets/plutonium.js +18 -0
  8. data/app/assets/plutonium.js.map +4 -4
  9. data/app/assets/plutonium.min.js +30 -30
  10. data/app/assets/plutonium.min.js.map +4 -4
  11. data/docs/public/images/reference/structured-inputs-removed.png +0 -0
  12. data/docs/public/images/reference/structured-inputs.png +0 -0
  13. data/docs/reference/resource/definition.md +110 -0
  14. data/docs/superpowers/plans/2026-06-02-structured-inputs.md +1061 -0
  15. data/docs/superpowers/plans/2026-06-02-structured-inputs.md.tasks.json +60 -0
  16. data/docs/superpowers/specs/2026-06-01-structured-inputs-design.md +191 -0
  17. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  18. data/lib/plutonium/definition/base.rb +1 -0
  19. data/lib/plutonium/definition/structured_inputs.rb +67 -0
  20. data/lib/plutonium/interaction/README.md +24 -78
  21. data/lib/plutonium/interaction/base.rb +10 -2
  22. data/lib/plutonium/resource/controller.rb +6 -1
  23. data/lib/plutonium/resource/controllers/interactive_actions.rb +10 -6
  24. data/lib/plutonium/structured_inputs/param_cleaner.rb +36 -0
  25. data/lib/plutonium/structured_inputs/params_concern.rb +36 -0
  26. data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +3 -3
  27. data/lib/plutonium/ui/form/concerns/renders_structured_inputs.rb +178 -0
  28. data/lib/plutonium/ui/form/concerns/repeater_field_styles.rb +24 -0
  29. data/lib/plutonium/ui/form/resource.rb +4 -1
  30. data/lib/plutonium/ui/modal/slideover.rb +9 -3
  31. data/lib/plutonium/version.rb +1 -1
  32. data/package.json +1 -1
  33. data/src/css/components.css +10 -5
  34. data/src/js/controllers/register_controllers.js +2 -0
  35. data/src/js/controllers/structured_input_row_controller.js +26 -0
  36. metadata +14 -5
  37. data/docs/superpowers/specs/2026-06-01-interaction-repeater-inputs-design.md +0 -178
  38. data/lib/plutonium/interaction/nested_attributes.rb +0 -93
@@ -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