plutonium 0.15.6 → 0.15.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) 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/eject/layout/layout_generator.rb +3 -3
  13. data/lib/generators/pu/eject/shell/shell_generator.rb +3 -3
  14. data/lib/generators/pu/gem/dotenv/dotenv_generator.rb +1 -1
  15. data/lib/generators/pu/gem/letter_opener/letter_opener_generator.rb +21 -0
  16. data/lib/generators/pu/gem/redis/redis_generator.rb +0 -2
  17. data/lib/generators/pu/gem/standard/standard_generator.rb +19 -0
  18. data/lib/generators/pu/lib/plutonium_generators/generator.rb +1 -1
  19. data/lib/generators/pu/res/conn/conn_generator.rb +1 -1
  20. data/lib/plutonium/definition/actions.rb +6 -2
  21. data/lib/plutonium/definition/base.rb +1 -0
  22. data/lib/plutonium/definition/nested_inputs.rb +19 -0
  23. data/lib/plutonium/resource/controller.rb +1 -1
  24. data/lib/plutonium/resource/controllers/interactive_actions.rb +1 -1
  25. data/lib/plutonium/resource/controllers/presentable.rb +1 -5
  26. data/lib/plutonium/ui/block.rb +13 -0
  27. data/lib/plutonium/ui/component/kit.rb +10 -0
  28. data/lib/plutonium/ui/display/resource.rb +29 -11
  29. data/lib/plutonium/ui/display/theme.rb +1 -1
  30. data/lib/plutonium/ui/dyna_frame/content.rb +2 -2
  31. data/lib/plutonium/ui/dyna_frame/host.rb +20 -0
  32. data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +282 -0
  33. data/lib/plutonium/ui/form/resource.rb +39 -29
  34. data/lib/plutonium/ui/form/theme.rb +1 -1
  35. data/lib/plutonium/ui/frame_navigator_panel.rb +53 -0
  36. data/lib/plutonium/ui/panel.rb +63 -0
  37. data/lib/plutonium/ui/skeleton_table.rb +29 -0
  38. data/lib/plutonium/ui/table/resource.rb +1 -1
  39. data/lib/plutonium/version.rb +1 -1
  40. data/src/js/controllers/frame_navigator_controller.js +25 -8
  41. data/src/js/controllers/nested_resource_form_fields_controller.js +2 -2
  42. metadata +11 -3
  43. 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
@@ -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)
@@ -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
@@ -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
@@ -59,7 +59,7 @@ module Plutonium
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
@@ -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,7 @@ module Plutonium
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- helper_method :presentable_attributes, :present_associations?
8
+ helper_method :presentable_attributes
9
9
  helper_method :build_form, :build_detail, :build_collection
10
10
  end
11
11
 
@@ -31,10 +31,6 @@ module Plutonium
31
31
  def build_form(record = resource_record)
32
32
  current_definition.form_class.new(record, resource_fields: presentable_attributes, resource_definition: current_definition)
33
33
  end
34
-
35
- def present_associations?
36
- current_parent.nil?
37
- end
38
34
  end
39
35
  end
40
36
  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, &)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end