plutonium 0.15.6 → 0.15.8

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/plutonium.css +1 -1
  3. data/app/assets/plutonium.js +25 -11
  4. data/app/assets/plutonium.js.map +2 -2
  5. data/app/assets/plutonium.min.js +4 -4
  6. data/app/assets/plutonium.min.js.map +3 -3
  7. data/app/views/layouts/rodauth.html.erb +2 -2
  8. data/docs/guide/getting-started/installation.md +2 -1
  9. data/docs/guide/getting-started/resources.md +8 -12
  10. data/docs/public/templates/plutonium.rb +8 -0
  11. data/lib/generators/pu/core/assets/assets_generator.rb +1 -1
  12. data/lib/generators/pu/core/assets/templates/tailwind.config.js +11 -1
  13. data/lib/generators/pu/eject/layout/layout_generator.rb +3 -3
  14. data/lib/generators/pu/eject/shell/shell_generator.rb +3 -3
  15. data/lib/generators/pu/extra/colorized_logger/colorized_logger_generator.rb +21 -0
  16. data/lib/generators/pu/extra/colorized_logger/templates/config/initializers/colorized_logger.rb +22 -0
  17. data/lib/generators/pu/gem/dotenv/dotenv_generator.rb +1 -1
  18. data/lib/generators/pu/gem/letter_opener/letter_opener_generator.rb +21 -0
  19. data/lib/generators/pu/gem/redis/redis_generator.rb +0 -2
  20. data/lib/generators/pu/gem/standard/standard_generator.rb +19 -0
  21. data/lib/generators/pu/lib/plutonium_generators/generator.rb +1 -1
  22. data/lib/generators/pu/res/conn/conn_generator.rb +1 -1
  23. data/lib/plutonium/core/controllers/authorizable.rb +1 -1
  24. data/lib/plutonium/definition/actions.rb +6 -2
  25. data/lib/plutonium/definition/base.rb +1 -0
  26. data/lib/plutonium/definition/nested_inputs.rb +19 -0
  27. data/lib/plutonium/resource/controller.rb +13 -9
  28. data/lib/plutonium/resource/controllers/authorizable.rb +2 -2
  29. data/lib/plutonium/resource/controllers/interactive_actions.rb +1 -1
  30. data/lib/plutonium/resource/controllers/presentable.rb +15 -8
  31. data/lib/plutonium/ui/block.rb +13 -0
  32. data/lib/plutonium/ui/component/kit.rb +10 -0
  33. data/lib/plutonium/ui/display/resource.rb +29 -11
  34. data/lib/plutonium/ui/display/theme.rb +1 -1
  35. data/lib/plutonium/ui/dyna_frame/content.rb +2 -2
  36. data/lib/plutonium/ui/dyna_frame/host.rb +20 -0
  37. data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +285 -0
  38. data/lib/plutonium/ui/form/resource.rb +39 -29
  39. data/lib/plutonium/ui/form/theme.rb +1 -1
  40. data/lib/plutonium/ui/frame_navigator_panel.rb +53 -0
  41. data/lib/plutonium/ui/panel.rb +63 -0
  42. data/lib/plutonium/ui/skeleton_table.rb +29 -0
  43. data/lib/plutonium/ui/table/resource.rb +1 -1
  44. data/lib/plutonium/version.rb +1 -1
  45. data/package-lock.json +2 -2
  46. data/package.json +1 -1
  47. data/src/js/controllers/frame_navigator_controller.js +25 -8
  48. data/src/js/controllers/nested_resource_form_fields_controller.js +2 -2
  49. data/tailwind.config.js +11 -1
  50. data/tailwind.options.js +7 -1
  51. metadata +13 -3
  52. data/lib/generators/pu/gem/redis/templates/.keep +0 -0
@@ -12,8 +12,8 @@
12
12
  <%= yield %>
13
13
  </div>
14
14
  </div>
15
- <div class="mt-4 flex items-center font-medium text-blue-600 dark:text-blue-500 hover:underline">
15
+ <div class="mt-4 flex items-center font-medium text-secondary-600 dark:text-secondary-400 hover:underline">
16
16
  <%= render_icon "outline/home" %>
17
- <%= link_to "Home", root_path, class: "font-medium text-blue-600 dark:text-blue-500" %>
17
+ <%= link_to "Home", root_path, class: "font-medium text-secondary-600 dark:text-secondary-400" %>
18
18
  </div>
19
19
  <% end %>
@@ -3,6 +3,7 @@
3
3
  ::: tip VERSION REQUIREMENTS
4
4
  - Ruby 3.2.2 or higher
5
5
  - Rails 7.1 or higher
6
+ - Node.js and Yarn
6
7
  :::
7
8
 
8
9
  ## Quick Start
@@ -151,7 +152,7 @@ bin/importmap pin @radioactive-labs/plutonium
151
152
  ```
152
153
 
153
154
  ```bash [esbuild]
154
- npm install @radioactive-labs/plutonium
155
+ yarn add @radioactive-labs/plutonium
155
156
  ```
156
157
  :::
157
158
 
@@ -17,7 +17,8 @@ Resources are the core building blocks of a Plutonium application. A resource re
17
17
  The fastest way to create a resource is using the scaffold generator:
18
18
 
19
19
  ```bash
20
- rails generate pu:res:scaffold Blog user:belongs_to title:string content:text state:string published_at:datetime?
20
+ rails generate pu:res:scaffold Blog user:belongs_to \
21
+ title:string content:text 'published_at:datetime?'
21
22
  ```
22
23
 
23
24
  This generates several files, including:
@@ -40,11 +41,11 @@ class BlogPolicy < Plutonium::Resource::Policy
40
41
  end
41
42
 
42
43
  def permitted_attributes_for_create
43
- %i[user title content state published_at]
44
+ %i[user title content published_at]
44
45
  end
45
46
 
46
47
  def permitted_attributes_for_read
47
- %i[user title content state published_at]
48
+ %i[user title content published_at]
48
49
  end
49
50
  end
50
51
  ```
@@ -83,12 +84,12 @@ end
83
84
  class BlogDefinition < Plutonium::Resource::Definition
84
85
  # Customize how fields are displayed
85
86
  display :title, class: "col-span-full"
86
- display :content do |f|
87
+ display :content, class: "col-span-full" do |f|
87
88
  f.text_tag class: "prose dark:prose-invert"
88
89
  end
89
90
 
90
91
  # Custom column display in tables
91
- column :state, align: :right
92
+ column :published_at, align: :end
92
93
  end
93
94
  ```
94
95
  :::
@@ -140,7 +141,6 @@ module Blogs
140
141
 
141
142
  def execute
142
143
  if resource.update(
143
- state: "published",
144
144
  published_at: publish_date
145
145
  )
146
146
  succeed(resource)
@@ -175,20 +175,16 @@ class BlogDefinition < Plutonium::Resource::Definition
175
175
  end
176
176
 
177
177
  # Add filters
178
- filter :state,
179
- with: SelectFilter,
180
- choices: %w[draft published]
181
-
182
178
  filter :published_at,
183
179
  with: DateFilter,
184
180
  predicate: :gteq
185
181
 
186
182
  # Add scopes
187
183
  scope :published do |scope|
188
- where(state: "published")
184
+ scope.where.not(published_at: nil)
189
185
  end
190
186
  scope :draft do |scope|
191
- where(state: "draft")
187
+ scope.where(published_at: nil)
192
188
  end
193
189
 
194
190
  # Configure sorting
@@ -15,6 +15,14 @@ after_bundle do
15
15
  git add: "."
16
16
  git commit: %( -m 'add annotate' )
17
17
 
18
+ generate "pu:gem:standard"
19
+ git add: "."
20
+ git commit: %( -m 'add standardrb' )
21
+
22
+ generate "pu:gem:letter_opener"
23
+ git add: "."
24
+ git commit: %( -m 'add letter_opener' )
25
+
18
26
  generate "pu:core:assets"
19
27
  git add: "."
20
28
  git commit: %( -m 'integrate assets' )
@@ -26,7 +26,7 @@ module Pu
26
26
  end
27
27
 
28
28
  def install_dependencies
29
- run "npm install @radioactive-labs/plutonium flowbite @tailwindcss/forms"
29
+ run "yarn add @radioactive-labs/plutonium flowbite @tailwindcss/forms"
30
30
  end
31
31
 
32
32
  def configure_application
@@ -1,12 +1,22 @@
1
1
  const { execSync } = require('child_process');
2
2
  const plutoniumGemPath = execSync("bundle show plutonium").toString().trim();
3
3
  const plutoniumTailwindConfig = require(`${plutoniumGemPath}/tailwind.options.js`)
4
+ const tailwindPlugin = require('tailwindcss/plugin')
4
5
 
5
6
  module.exports = {
6
7
  darkMode: plutoniumTailwindConfig.darkMode,
7
8
  plugins: [
8
9
  // add plugins here
9
- ].concat(plutoniumTailwindConfig.plugins.map((plugin) => require(plugin))),
10
+ ].concat(plutoniumTailwindConfig.plugins.map(function (plugin) {
11
+ switch (typeof plugin) {
12
+ case "function":
13
+ return tailwindPlugin(plugin)
14
+ case "string":
15
+ return require(plugin)
16
+ default:
17
+ throw Error(`unsupported plugin: ${plugin}: ${(typeof plugin)}`)
18
+ }
19
+ })),
10
20
  theme: plutoniumTailwindConfig.theme,
11
21
  content: [
12
22
  `${__dirname}/app/**/*.rb`,
@@ -15,7 +15,7 @@ module Pu
15
15
  class_option :rodauth, type: :boolean
16
16
 
17
17
  def start
18
- destination_dir = (destination_app == "main_app") ? "app/views/" : "packages/#{destination_app}/app/views/"
18
+ destination_dir = (destination_portal == "main_app") ? "app/views/" : "packages/#{destination_portal}/app/views/"
19
19
  [
20
20
  "layouts/resource.html.erb"
21
21
  ].each do |file|
@@ -27,8 +27,8 @@ module Pu
27
27
 
28
28
  private
29
29
 
30
- def destination_app
31
- @destination_app || select_app(options[:dest], msg: "Select destination app")
30
+ def destination_portal
31
+ @destination_portal || select_portal(options[:dest], msg: "Select destination portal")
32
32
  end
33
33
 
34
34
  def copy_file(source_path, destination_path)
@@ -14,7 +14,7 @@ module Pu
14
14
  class_option :dest, type: :string
15
15
 
16
16
  def start
17
- destination_dir = (destination_app == "main_app") ? "app/views/" : "packages/#{destination_app}/app/views"
17
+ destination_dir = (destination_portal == "main_app") ? "app/views/" : "packages/#{destination_portal}/app/views"
18
18
  [
19
19
  "plutonium/_resource_header.html.erb",
20
20
  "plutonium/_resource_sidebar.html.erb"
@@ -27,8 +27,8 @@ module Pu
27
27
 
28
28
  private
29
29
 
30
- def destination_app
31
- @destination_app || select_app(options[:dest], msg: "Select destination app")
30
+ def destination_portal
31
+ @destination_portal || select_portal(options[:dest], msg: "Select destination portal")
32
32
  end
33
33
 
34
34
  def copy_file(source_path, destination_path)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../lib/plutonium_generators"
4
+
5
+ module Pu
6
+ module Extra
7
+ class ColorizedLoggerGenerator < Rails::Generators::Base
8
+ include PlutoniumGenerators::Generator
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ desc "Set up a colorized logger"
13
+
14
+ def start
15
+ copy_file "config/initializers/colorized_logger.rb"
16
+ rescue => e
17
+ exception "#{self.class} failed:", e
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ return unless Rails.env.development?
4
+
5
+ # Taken from https://gist.github.com/kyrylo/3d90f7a656d1a0accf244b8f1d25999b?permalink_comment_id=5264120#gistcomment-5264120
6
+
7
+ module ColorizedLogger
8
+ %i[debug info warn error fatal unknown].each do |level|
9
+ color = case level
10
+ when :debug then "\e[0;36m" # Cyan text
11
+ when :info then "\e[0;32m" # Green text
12
+ when :warn then "\e[1;33m" # Yellow text
13
+ when :error, :fatal then "\e[1;31m" # Red text
14
+ else "\e[0m" # Terminal default
15
+ end
16
+
17
+ define_method(level) do |progname = nil, &block|
18
+ super(color + (progname || (block && block.call)).to_s + "\e[0m")
19
+ end
20
+ end
21
+ end
22
+ Rails.logger.extend(ColorizedLogger)
@@ -21,7 +21,7 @@ module Pu
21
21
 
22
22
  gitignore "!/.env.template", "!/.env.local.template", "!/.env"
23
23
 
24
- insert_into_file "Gemfile", "\ngem \"dotenv\", :groups => [:development, :test]\n", after: /^gem ["']rails["'].*\n/
24
+ insert_into_file "Gemfile", "\ngem \"dotenv\", groups: %i[development test]\n", after: /^gem ["']rails["'].*\n/
25
25
  bundle!
26
26
  end
27
27
  rescue => e
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../lib/plutonium_generators"
4
+
5
+ module Pu
6
+ module Gem
7
+ class LetterOpenerGenerator < Rails::Generators::Base
8
+ include PlutoniumGenerators::Generator
9
+
10
+ desc "Set up letter_opener"
11
+
12
+ def start
13
+ bundle "letter_opener", group: :development
14
+ environment "config.action_mailer.delivery_method = :letter_opener", env: :development
15
+ environment "config.action_mailer.perform_deliveries = true", env: :development
16
+ rescue => e
17
+ exception "#{self.class} failed:", e
18
+ end
19
+ end
20
+ end
21
+ end
@@ -7,8 +7,6 @@ module Pu
7
7
  class RedisGenerator < Rails::Generators::Base
8
8
  include PlutoniumGenerators::Generator
9
9
 
10
- source_root File.expand_path("templates", __dir__)
11
-
12
10
  desc "Set up redis"
13
11
 
14
12
  def start
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../lib/plutonium_generators"
4
+
5
+ module Pu
6
+ module Gem
7
+ class StandardGenerator < Rails::Generators::Base
8
+ include PlutoniumGenerators::Generator
9
+
10
+ desc "Set up standardrb"
11
+
12
+ def start
13
+ bundle "standardrb"
14
+ rescue => e
15
+ exception "#{self.class} failed:", e
16
+ end
17
+ end
18
+ end
19
+ end
@@ -52,7 +52,7 @@ module PlutoniumGenerators
52
52
  end
53
53
  end
54
54
 
55
- def select_app(selected_package = nil, msg: "Select app")
55
+ def select_portal(selected_package = nil, msg: "Select portal")
56
56
  select_package(selected_package, msg: msg, pkgs: available_apps)
57
57
  end
58
58
 
@@ -22,7 +22,7 @@ module Pu
22
22
  error "No resources found" if available_resources.blank?
23
23
  selected_resources = prompt.multi_select("Select resources", available_resources)
24
24
 
25
- @app_namespace = select_app.camelize
25
+ @app_namespace = select_portal.camelize
26
26
 
27
27
  selected_resources.each do |resource|
28
28
  @resource_class = resource
@@ -26,7 +26,7 @@ module Plutonium
26
26
  end
27
27
 
28
28
  def entity_scope_for_authorize
29
- scoped_to_entity? ? current_scoped_entity : nil
29
+ current_scoped_entity if scoped_to_entity?
30
30
  end
31
31
 
32
32
  def verify_authorized
@@ -14,8 +14,12 @@ module Plutonium
14
14
  end
15
15
  end
16
16
 
17
- def action(name, **)
18
- instance_defined_actions[name] = Plutonium::Action::Simple.new(name, **)
17
+ def action(name, interaction: nil, **)
18
+ instance_defined_actions[name] = if interaction
19
+ Plutonium::Action::Interactive::Factory.create(name, interaction:, **)
20
+ else
21
+ Plutonium::Action::Simple.new(name, **)
22
+ end
19
23
  end
20
24
 
21
25
  def defined_actions
@@ -29,6 +29,7 @@ module Plutonium
29
29
  include Actions
30
30
  include Sorting
31
31
  include Search
32
+ include NestedInputs
32
33
 
33
34
  class IndexPage < Plutonium::UI::Page::Index; end
34
35
 
@@ -0,0 +1,19 @@
1
+ module Plutonium
2
+ module Definition
3
+ module NestedInputs
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ defineable_prop :nested_input
8
+
9
+ # def self.nested_input(name, with: nil, **)
10
+ # defined_nested_inputs[name] = {}
11
+ # end
12
+
13
+ # def nested_input(name, with: nil, **)
14
+ # instance_defined_nested_inputs[name] = {}
15
+ # end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -34,7 +34,7 @@ module Plutonium
34
34
  # Gets the resource class for the controller
35
35
  # @return [ActiveRecord::Base] The resource class
36
36
  def resource_class
37
- return @resource_class if @resource_class.present?
37
+ return @resource_class if @resource_class
38
38
 
39
39
  name.to_s.gsub(/^#{current_package}::/, "").gsub(/Controller$/, "").classify.constantize
40
40
  rescue NameError
@@ -52,14 +52,14 @@ module Plutonium
52
52
  # Returns the resource record based on path parameters
53
53
  # @return [ActiveRecord::Base, nil] The resource record
54
54
  def resource_record
55
- @resource_record ||= current_authorized_scope.from_path_param(params[:id]).first! if params[:id].present?
55
+ @resource_record ||= current_authorized_scope.from_path_param(params[:id]).first! if params[:id]
56
56
  @resource_record
57
57
  end
58
58
 
59
59
  # Returns the submitted resource parameters
60
60
  # @return [Hash] The submitted resource parameters
61
61
  def submitted_resource_params
62
- @submitted_resource_params ||= build_form(resource_class.new).extract_input(params)[resource_param_key.to_sym]
62
+ @submitted_resource_params ||= build_form(resource_class.new).extract_input(params, view_context:)[resource_param_key.to_sym]
63
63
  end
64
64
 
65
65
  # Returns the resource parameters, including scoped and parent parameters
@@ -125,19 +125,23 @@ module Plutonium
125
125
  # Applies submitted resource params if they have been passed
126
126
  def maybe_apply_submitted_resource_params!
127
127
  ensure_get_request
128
- resource_record.attributes = submitted_resource_params if params[resource_param_key].present?
128
+ resource_record.attributes = submitted_resource_params if params[resource_param_key]
129
129
  end
130
130
 
131
131
  # Returns the current parent based on path parameters
132
132
  # @return [ActiveRecord::Base, nil] The current parent
133
133
  def current_parent
134
- return unless parent_route_param.present?
134
+ return unless parent_route_param
135
135
 
136
136
  @current_parent ||= begin
137
137
  parent_route_key = parent_route_param.to_s.gsub(/_id$/, "").to_sym
138
138
  parent_class = current_engine.resource_register.route_key_lookup[parent_route_key]
139
- parent_scope = parent_class.from_path_param(params[parent_route_param])
140
- parent_scope = parent_scope.associated_with(current_scoped_entity) if scoped_to_entity?
139
+ parent_scope = if scoped_to_entity?
140
+ parent_class.associated_with(current_scoped_entity)
141
+ else
142
+ parent_class.all
143
+ end
144
+ parent_scope = parent_scope.from_path_param(params[parent_route_param])
141
145
  current_parent = parent_scope.first!
142
146
  authorize! current_parent, to: :read?
143
147
  current_parent
@@ -153,7 +157,7 @@ module Plutonium
153
157
  # Returns the parent input parameter
154
158
  # @return [Symbol, nil] The parent input parameter
155
159
  def parent_input_param
156
- return unless current_parent.present?
160
+ return unless current_parent
157
161
 
158
162
  resource_class.reflect_on_all_associations(:belongs_to).find { |assoc| assoc.klass.name == current_parent.class.name }&.name&.to_sym
159
163
  end
@@ -182,7 +186,7 @@ module Plutonium
182
186
  # Overrides parent parameters
183
187
  # @param [Hash] input_params The input parameters
184
188
  def override_parent_params(input_params)
185
- if current_parent.present?
189
+ if current_parent
186
190
  if input_params.key?(parent_input_param) || resource_class.method_defined?(:"#{parent_input_param}=")
187
191
  input_params[parent_input_param] = current_parent
188
192
  end
@@ -112,7 +112,7 @@ module Plutonium
112
112
  #
113
113
  # @return [Hash] default context for the current resource's policy
114
114
  def current_policy_context
115
- {scope: current_parent || entity_scope_for_authorize}
115
+ {entity_scope: current_parent || entity_scope_for_authorize}
116
116
  end
117
117
 
118
118
  # Authorizes the current action for the given record of the current resource
@@ -130,7 +130,7 @@ module Plutonium
130
130
  #
131
131
  # @return [Array<Symbol>] the list of permitted attributes for the current action
132
132
  def permitted_attributes
133
- @permitted_attributes ||= current_policy.send_with_report(:"permitted_attributes_for_#{action_name}")
133
+ @permitted_attributes ||= current_policy.send_with_report(:"permitted_attributes_for_#{action_name}").freeze
134
134
  end
135
135
 
136
136
  # Returns the list of permitted associations for the current resource
@@ -232,7 +232,7 @@ module Plutonium
232
232
  @submitted_interaction_params ||= current_interactive_action
233
233
  .interaction
234
234
  .build_form(nil)
235
- .extract_input(params)[:interaction]
235
+ .extract_input(params, view_context:)[:interaction]
236
236
  end
237
237
 
238
238
  def redirect_url_after_action_on(resource_record_or_resource_class)
@@ -5,7 +5,6 @@ module Plutonium
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- helper_method :presentable_attributes, :present_associations?
9
8
  helper_method :build_form, :build_detail, :build_collection
10
9
  end
11
10
 
@@ -14,12 +13,24 @@ module Plutonium
14
13
  def presentable_attributes
15
14
  @presentable_attributes ||= begin
16
15
  presentable_attributes = permitted_attributes
17
- presentable_attributes -= [scoped_entity_param_key, :"#{scoped_entity_param_key}_id"] if scoped_to_entity?
18
- presentable_attributes -= [parent_input_param, :"#{parent_input_param}_id"] if current_parent.present?
16
+ if current_parent
17
+ presentable_attributes -= [parent_input_param, :"#{parent_input_param}_id"]
18
+ elsif scoped_to_entity?
19
+ presentable_attributes -= [scoped_entity_param_key, :"#{scoped_entity_param_key}_id"]
20
+ end
19
21
  presentable_attributes
20
22
  end
21
23
  end
22
24
 
25
+ def submittable_attributes
26
+ @submittable_attributes ||= begin
27
+ submittable_attributes = permitted_attributes
28
+ submittable_attributes -= [parent_input_param, :"#{parent_input_param}_id"] if current_parent
29
+ submittable_attributes -= [scoped_entity_param_key, :"#{scoped_entity_param_key}_id"] if scoped_to_entity?
30
+ submittable_attributes
31
+ end
32
+ end
33
+
23
34
  def build_collection
24
35
  current_definition.collection_class.new(@resource_records, resource_fields: presentable_attributes, resource_definition: current_definition)
25
36
  end
@@ -29,11 +40,7 @@ module Plutonium
29
40
  end
30
41
 
31
42
  def build_form(record = resource_record)
32
- current_definition.form_class.new(record, resource_fields: presentable_attributes, resource_definition: current_definition)
33
- end
34
-
35
- def present_associations?
36
- current_parent.nil?
43
+ current_definition.form_class.new(record, resource_fields: submittable_attributes, resource_definition: current_definition)
37
44
  end
38
45
  end
39
46
  end
@@ -0,0 +1,13 @@
1
+ module Plutonium
2
+ module UI
3
+ class Block < Plutonium::UI::Component::Base
4
+ def view_template(&)
5
+ raise ArgumentError, "Block requires a content block" unless block_given?
6
+
7
+ div class: "relative bg-white dark:bg-gray-800 shadow-md sm:rounded-lg my-3" do
8
+ yield
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -10,6 +10,16 @@ module Plutonium
10
10
 
11
11
  def Breadcrumbs(...) = render Plutonium::UI::Breadcrumbs.new(...)
12
12
 
13
+ def SkeletonTable(...) = render Plutonium::UI::SkeletonTable.new(...)
14
+
15
+ def Block(...) = render Plutonium::UI::Block.new(...)
16
+
17
+ def Panel(...) = render Plutonium::UI::Panel.new(...)
18
+
19
+ def FrameNavigatorPanel(...) = render Plutonium::UI::FrameNavigatorPanel.new(...)
20
+
21
+ def DynaFrameHost(...) = render Plutonium::UI::DynaFrame::Host.new(...)
22
+
13
23
  def DynaFrameContent(...) = render Plutonium::UI::DynaFrame::Content.new(...)
14
24
 
15
25
  def PageHeader(...) = render Plutonium::UI::PageHeader.new(...)
@@ -21,19 +21,37 @@ module Plutonium
21
21
  private
22
22
 
23
23
  def render_fields
24
- fields_wrapper {
25
- resource_fields.each { |name|
26
- render_resource_field name
27
- }
28
- }
24
+ Block do
25
+ fields_wrapper do
26
+ resource_fields.each do |name|
27
+ render_resource_field name
28
+ end
29
+ end
30
+ end
29
31
  end
30
32
 
31
33
  def render_associations
32
- nil
33
- # TODO
34
- # resource_associations.each do |name, renderer|
35
- # # <%= render renderer.with(record: details.record) %>
36
- # end
34
+ resource_associations.each do |name|
35
+ reflection = object.class.reflect_on_association name
36
+
37
+ if !reflection
38
+ raise ArgumentError,
39
+ "unknown association #{object.class}##{name} defined in #permitted_associations"
40
+ elsif !registered_resources.include?(reflection.klass)
41
+ raise ArgumentError,
42
+ "#{object.class}##{name} defined in #permitted_associations, but #{reflection.klass} is not a registered resource"
43
+ end
44
+
45
+ title = object.class.human_attribute_name(name)
46
+ src = case reflection.macro
47
+ when :belongs_to
48
+ associated = object.public_send name
49
+ resource_url_for(associated, parent: nil) if associated
50
+ when :has_many
51
+ resource_url_for(reflection.klass, parent: object)
52
+ end
53
+ FrameNavigatorPanel(title:, src:) if src
54
+ end
37
55
  end
38
56
 
39
57
  def render_resource_field(name)
@@ -73,7 +91,7 @@ module Plutonium
73
91
  end
74
92
 
75
93
  def present_associations?
76
- current_parent.nil?
94
+ current_turbo_frame.nil?
77
95
  end
78
96
  end
79
97
  end
@@ -6,7 +6,7 @@ module Plutonium
6
6
  class Theme < Phlexi::Display::Theme
7
7
  def self.theme
8
8
  super.merge({
9
- base: "relative bg-white dark:bg-gray-800 shadow-md sm:rounded-lg my-3",
9
+ base: "",
10
10
  value_wrapper: "max-h-[300px] overflow-y-auto",
11
11
  fields_wrapper: "p-6 grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-6 gap-y-10 grid-flow-row-dense",
12
12
  label: "text-base font-bold text-gray-500 dark:text-gray-400 mb-1",
@@ -6,10 +6,10 @@ module Plutonium
6
6
 
7
7
  def view_template
8
8
  if current_turbo_frame.present?
9
- render turbo_frame_tag(current_turbo_frame) {
9
+ turbo_frame_tag(current_turbo_frame) do
10
10
  render "flash"
11
11
  yield
12
- }
12
+ end
13
13
  else
14
14
  yield
15
15
  end
@@ -0,0 +1,20 @@
1
+ module Plutonium
2
+ module UI
3
+ module DynaFrame
4
+ class Host < Plutonium::UI::Component::Base
5
+ include Phlex::Rails::Helpers::TurboFrameTag
6
+
7
+ def initialize(src:, loading:, id: SecureRandom.hex, **attributes)
8
+ @id = id
9
+ @src = src
10
+ @loading = loading
11
+ @attributes = attributes
12
+ end
13
+
14
+ def view_template(&)
15
+ turbo_frame_tag(@id, src: @src, loading: @loading, **@attributes, class: 'dyna', &)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end