plutonium 0.15.6 → 0.15.8

Sign up to get free protection for your applications and to get access to all the features.
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