plutonium 0.52.0 → 0.53.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.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium-resource/SKILL.md +6 -4
- data/.claude/skills/plutonium-tenancy/SKILL.md +9 -4
- data/.claude/skills/plutonium-ui/SKILL.md +29 -5
- data/CHANGELOG.md +16 -0
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +257 -11
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +39 -39
- data/app/assets/plutonium.min.js.map +4 -4
- data/app/views/plutonium/_resource_header.html.erb +2 -1
- data/docs/.vitepress/config.ts +1 -0
- data/docs/guides/authentication.md +1 -1
- data/docs/guides/custom-actions.md +2 -1
- data/docs/guides/customizing-ui.md +6 -5
- data/docs/guides/multi-tenancy.md +6 -6
- data/docs/guides/theming.md +1 -1
- data/docs/public/images/components/avatar.png +0 -0
- data/docs/reference/auth/accounts.md +1 -1
- data/docs/reference/behavior/policies.md +1 -1
- data/docs/reference/configuration.md +61 -0
- data/docs/reference/resource/actions.md +2 -1
- data/docs/reference/resource/definition.md +4 -3
- data/docs/reference/tenancy/entity-scoping.md +12 -13
- data/docs/reference/ui/components.md +53 -0
- data/docs/reference/ui/forms.md +1 -1
- data/docs/reference/ui/pages.md +6 -5
- data/docs/superpowers/specs/2026-05-29-avatar-component-design.md +153 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/lite/solid_errors/solid_errors_generator.rb +7 -3
- data/lib/plutonium/action/base.rb +43 -63
- data/lib/plutonium/configuration.rb +7 -0
- data/lib/plutonium/definition/actions.rb +10 -11
- data/lib/plutonium/definition/base.rb +29 -0
- data/lib/plutonium/helpers/assets_helper.rb +0 -30
- data/lib/plutonium/helpers/content_helper.rb +0 -44
- data/lib/plutonium/helpers/display_helper.rb +0 -62
- data/lib/plutonium/helpers/turbo_helper.rb +0 -4
- data/lib/plutonium/helpers.rb +0 -2
- data/lib/plutonium/resource/definition.rb +0 -42
- data/lib/plutonium/ui/action_button.rb +4 -3
- data/lib/plutonium/ui/avatar.rb +182 -0
- data/lib/plutonium/ui/component/kit.rb +2 -0
- data/lib/plutonium/ui/form/base.rb +16 -2
- data/lib/plutonium/ui/form/components/secure_association.rb +3 -2
- data/lib/plutonium/ui/form/resource.rb +58 -0
- data/lib/plutonium/ui/form/theme.rb +7 -3
- data/lib/plutonium/ui/grid/card.rb +10 -26
- data/lib/plutonium/ui/modal/base.rb +36 -1
- data/lib/plutonium/ui/modal/centered.rb +24 -6
- data/lib/plutonium/ui/modal/slideover.rb +26 -11
- data/lib/plutonium/ui/nav_user.rb +3 -23
- data/lib/plutonium/ui/page/edit.rb +6 -3
- data/lib/plutonium/ui/page/interactive_action.rb +5 -3
- data/lib/plutonium/ui/page/new.rb +6 -3
- data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/src/css/components.css +38 -1
- data/src/css/slim_select.css +3 -2
- data/src/js/controllers/dirty_form_guard_controller.js +165 -0
- data/src/js/controllers/register_controllers.js +2 -0
- data/src/js/controllers/remote_modal_controller.js +53 -19
- data/src/js/turbo/index.js +1 -0
- data/src/js/turbo/turbo_confirm.js +128 -0
- metadata +10 -6
- data/lib/plutonium/helpers/attachment_helper.rb +0 -73
- data/lib/plutonium/helpers/table_helper.rb +0 -35
- /data/lib/generators/pu/rodauth/templates/app/views/rodauth_mailer/{password_changed.text.erb → change_password_notify.text.erb} +0 -0
|
@@ -5,41 +5,9 @@ require "active_support/string_inquirer"
|
|
|
5
5
|
module Plutonium
|
|
6
6
|
module Action
|
|
7
7
|
# Base class for all actions in the Plutonium framework.
|
|
8
|
-
#
|
|
9
|
-
# @attr_reader [Symbol] name The name of the action.
|
|
10
|
-
# @attr_reader [String] label The human-readable label for the action.
|
|
11
|
-
# @attr_reader [String, nil] icon The icon associated with the action.
|
|
12
|
-
# @attr_reader [RouteOptions] route_options The routing options for the action.
|
|
13
|
-
# @attr_reader [String, nil] confirmation The confirmation message for the action.
|
|
14
|
-
# @attr_reader [String, nil] turbo_frame The Turbo Frame ID for the action.
|
|
15
|
-
# @attr_reader [Symbol, nil] color The color associated with the action.
|
|
16
|
-
# @attr_reader [Symbol, nil] category The category of the action.
|
|
17
|
-
# @attr_reader [Integer] position The position of the action within its category.
|
|
18
8
|
class Base
|
|
19
|
-
attr_reader :name, :label, :description, :icon, :route_options, :confirmation, :turbo, :
|
|
20
|
-
|
|
21
|
-
# Initialize a new action.
|
|
22
|
-
#
|
|
23
|
-
# @param [Symbol] name The name of the action.
|
|
24
|
-
# @param [Hash] options The options for the action.
|
|
25
|
-
# @option options [String] :label The human-readable label for the action.
|
|
26
|
-
# @option options [String] :description The human-readable description for the action.
|
|
27
|
-
# @option options [String] :icon The icon associated with the action (e.g., 'fa-edit' for Font Awesome).
|
|
28
|
-
# @option options [Symbol] :color The color associated with the action (e.g., :primary, :secondary, :success, :warning, :danger).
|
|
29
|
-
# @option options [String] :confirmation The confirmation message to display before executing the action.
|
|
30
|
-
# @option options [RouteOptions, Hash] :route_options The routing options for the action.
|
|
31
|
-
# @option options [String] :turbo_frame The Turbo Frame ID for the action (used in Hotwire/Turbo Drive applications).
|
|
32
|
-
# @option options [String, Symbol] :return_to Override the return_to URL for this action. If not provided, defaults to current URL.
|
|
33
|
-
# @option options [Boolean] :bulk_action (false) If true, applies to a bulk selection of records (e.g., "Mark Selected as Read").
|
|
34
|
-
# @option options [Boolean] :collection_record_action (false) If true, applies to records in a collection (e.g., "Edit Record" button in a table).
|
|
35
|
-
# @option options [Boolean] :record_action (false) If true, applies to an individual record (e.g., "Delete" button on a Show page).
|
|
36
|
-
# @option options [Boolean] :resource_action (false) If true, applies to the entire resource and can be used in any context (e.g., "Import from CSV").
|
|
37
|
-
# @option options [Symbol] :category The category of the action. Determines visibility and grouping.
|
|
38
|
-
# Valid values include:
|
|
39
|
-
# @option options [Symbol] :primary Always shown and given prominence in the UI.
|
|
40
|
-
# @option options [Symbol] :secondary Shown in secondary menus or less prominent areas.
|
|
41
|
-
# @option options [Symbol] :danger Actions that require caution, often destructive operations.
|
|
42
|
-
# @option options [Integer] :position (50) The position of the action in its group. Lower numbers appear first.
|
|
9
|
+
attr_reader :name, :label, :description, :icon, :route_options, :confirmation, :turbo, :color, :category, :position, :return_to
|
|
10
|
+
|
|
43
11
|
def initialize(name, **options)
|
|
44
12
|
@name = name.to_sym
|
|
45
13
|
@label = options[:label] || @name.to_s.titleize
|
|
@@ -57,51 +25,53 @@ module Plutonium
|
|
|
57
25
|
@resource_action = options[:resource_action] || false
|
|
58
26
|
@category = ActiveSupport::StringInquirer.new((options[:category] || :secondary).to_s)
|
|
59
27
|
@position = options[:position] || 50
|
|
60
|
-
@
|
|
61
|
-
|
|
28
|
+
@modal_mode = options[:modal]
|
|
29
|
+
@modal_size = options[:size]
|
|
30
|
+
validate_modal_mode!
|
|
31
|
+
validate_modal_size!
|
|
62
32
|
|
|
63
33
|
freeze
|
|
64
34
|
end
|
|
65
35
|
|
|
66
|
-
#
|
|
67
|
-
def
|
|
68
|
-
@
|
|
36
|
+
# Resolves to the definition's `modal_mode` when unset on the action.
|
|
37
|
+
def modal_mode(definition = nil)
|
|
38
|
+
return @modal_mode if @modal_mode || definition.nil?
|
|
39
|
+
definition.modal_mode
|
|
69
40
|
end
|
|
70
41
|
|
|
71
|
-
#
|
|
72
|
-
def
|
|
73
|
-
@
|
|
42
|
+
# Resolves to the definition's `modal_size` when unset on the action.
|
|
43
|
+
def modal_size(definition = nil)
|
|
44
|
+
return @modal_size if @modal_size || definition.nil?
|
|
45
|
+
definition.modal_size
|
|
74
46
|
end
|
|
75
47
|
|
|
76
|
-
#
|
|
77
|
-
|
|
78
|
-
|
|
48
|
+
# Downgrades the remote-modal frame to nil when the definition has
|
|
49
|
+
# `modal false`, so the link navigates as a full page instead of
|
|
50
|
+
# targeting a frame that won't exist. Other frames pass through.
|
|
51
|
+
def turbo_frame(definition = nil)
|
|
52
|
+
return nil if definition && targets_remote_modal? && definition.modal_mode == false
|
|
53
|
+
@turbo_frame
|
|
79
54
|
end
|
|
80
55
|
|
|
81
|
-
|
|
82
|
-
def
|
|
83
|
-
|
|
84
|
-
|
|
56
|
+
def bulk_action? = @bulk_action
|
|
57
|
+
def collection_record_action? = @collection_record_action
|
|
58
|
+
def record_action? = @record_action
|
|
59
|
+
def resource_action? = @resource_action
|
|
85
60
|
|
|
86
61
|
def permitted_by?(policy)
|
|
87
62
|
policy.allowed_to?(:"#{name}?")
|
|
88
63
|
end
|
|
89
64
|
|
|
90
65
|
# Returns a new Action with the given options merged over this one.
|
|
91
|
-
# Used by the resource definition to derive variants (e.g. dropping
|
|
92
|
-
# `turbo_frame` when `modal false` is configured) without mutating
|
|
93
|
-
# the frozen original.
|
|
94
66
|
def with(**overrides)
|
|
95
67
|
self.class.new(name, **to_options.merge(overrides))
|
|
96
68
|
end
|
|
97
69
|
|
|
98
70
|
protected
|
|
99
71
|
|
|
100
|
-
# Canonical
|
|
72
|
+
# Canonical option hash for reconstruction via `with`. Every
|
|
101
73
|
# attribute set in `initialize` MUST appear here; otherwise
|
|
102
74
|
# `with(**overrides)` would silently drop it on round-trip.
|
|
103
|
-
# `category` is exposed as a Symbol since `initialize` re-wraps
|
|
104
|
-
# it in StringInquirer.
|
|
105
75
|
def to_options
|
|
106
76
|
{
|
|
107
77
|
label: @label,
|
|
@@ -119,21 +89,31 @@ module Plutonium
|
|
|
119
89
|
resource_action: @resource_action,
|
|
120
90
|
category: @category.to_sym,
|
|
121
91
|
position: @position,
|
|
122
|
-
modal: @
|
|
92
|
+
modal: @modal_mode,
|
|
93
|
+
size: @modal_size
|
|
123
94
|
}
|
|
124
95
|
end
|
|
125
96
|
|
|
126
97
|
private
|
|
127
98
|
|
|
128
|
-
def
|
|
129
|
-
|
|
130
|
-
|
|
99
|
+
def targets_remote_modal?
|
|
100
|
+
@turbo_frame == Plutonium::REMOTE_MODAL_FRAME
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def validate_modal_mode!
|
|
104
|
+
return if @modal_mode.nil?
|
|
105
|
+
return if [:centered, :slideover].include?(@modal_mode)
|
|
106
|
+
raise ArgumentError, "modal must be :centered or :slideover, got #{@modal_mode.inspect}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def validate_modal_size!
|
|
110
|
+
return if @modal_size.nil?
|
|
111
|
+
return if Plutonium::UI::Modal::Base::VALID_SIZES.include?(@modal_size)
|
|
112
|
+
raise ArgumentError,
|
|
113
|
+
"size must be one of #{Plutonium::UI::Modal::Base::VALID_SIZES.inspect}, " \
|
|
114
|
+
"got #{@modal_size.inspect}"
|
|
131
115
|
end
|
|
132
116
|
|
|
133
|
-
# Build RouteOptions from the provided options
|
|
134
|
-
#
|
|
135
|
-
# @param [RouteOptions, Hash, nil] options The routing options
|
|
136
|
-
# @return [RouteOptions] The built RouteOptions object
|
|
137
117
|
def build_route_options(options)
|
|
138
118
|
case options
|
|
139
119
|
when RouteOptions
|
|
@@ -30,6 +30,12 @@ module Plutonium
|
|
|
30
30
|
# @return [Symbol] :classic (legacy Header/Sidebar) or :modern (Topbar/IconRail)
|
|
31
31
|
attr_accessor :shell
|
|
32
32
|
|
|
33
|
+
# @return [String] host URL of the Navii avatar service (no path), used by
|
|
34
|
+
# {Plutonium::UI::Avatar} as the default profile-image fallback. The
|
|
35
|
+
# component appends the `/avatar/:seed` route. Repoint this to self-host
|
|
36
|
+
# or proxy the service.
|
|
37
|
+
attr_accessor :navii_host_url
|
|
38
|
+
|
|
33
39
|
# Map of version numbers to their default configurations
|
|
34
40
|
VERSION_DEFAULTS = {
|
|
35
41
|
1.0 => proc do |config|
|
|
@@ -52,6 +58,7 @@ module Plutonium
|
|
|
52
58
|
@cache_discovery = !Rails.env.development?
|
|
53
59
|
@enable_hotreload = Rails.env.development?
|
|
54
60
|
@shell = :modern
|
|
61
|
+
@navii_host_url = "https://api.navii.dev"
|
|
55
62
|
end
|
|
56
63
|
|
|
57
64
|
# Load default configuration for a specific version
|
|
@@ -6,19 +6,19 @@ module Plutonium
|
|
|
6
6
|
included do
|
|
7
7
|
defineable_prop :action
|
|
8
8
|
|
|
9
|
-
def self.action(name, interaction: nil, **)
|
|
9
|
+
def self.action(name, interaction: nil, **opts)
|
|
10
10
|
defined_actions[name] = if interaction
|
|
11
|
-
Plutonium::Action::Interactive::Factory.create(name, interaction:, **)
|
|
11
|
+
Plutonium::Action::Interactive::Factory.create(name, interaction:, **opts)
|
|
12
12
|
else
|
|
13
|
-
Plutonium::Action::Simple.new(name, **)
|
|
13
|
+
Plutonium::Action::Simple.new(name, **opts)
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def action(name, interaction: nil, **)
|
|
17
|
+
def action(name, interaction: nil, **opts)
|
|
18
18
|
instance_defined_actions[name] = if interaction
|
|
19
|
-
Plutonium::Action::Interactive::Factory.create(name, interaction:, **)
|
|
19
|
+
Plutonium::Action::Interactive::Factory.create(name, interaction:, **opts)
|
|
20
20
|
else
|
|
21
|
-
Plutonium::Action::Simple.new(name, **)
|
|
21
|
+
Plutonium::Action::Simple.new(name, **opts)
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -32,12 +32,10 @@ module Plutonium
|
|
|
32
32
|
|
|
33
33
|
# standard CRUD actions
|
|
34
34
|
|
|
35
|
-
# turbo_frame for :new and :edit is set by
|
|
36
|
-
# Resource::Definition.configure_crud_modal_targets! based on the
|
|
37
|
-
# `modal` config. Don't hard-code it here.
|
|
38
35
|
action(:new, route_options: {action: :new},
|
|
39
36
|
resource_action: true, category: :primary,
|
|
40
|
-
icon: Phlex::TablerIcons::Plus, position: 10
|
|
37
|
+
icon: Phlex::TablerIcons::Plus, position: 10,
|
|
38
|
+
turbo_frame: Plutonium::REMOTE_MODAL_FRAME)
|
|
41
39
|
|
|
42
40
|
action(:show, route_options: {action: :show},
|
|
43
41
|
collection_record_action: true, category: :primary,
|
|
@@ -45,7 +43,8 @@ module Plutonium
|
|
|
45
43
|
|
|
46
44
|
action(:edit, route_options: {action: :edit},
|
|
47
45
|
record_action: true, collection_record_action: true, category: :primary,
|
|
48
|
-
icon: Phlex::TablerIcons::Edit, position: 20
|
|
46
|
+
icon: Phlex::TablerIcons::Edit, position: 20,
|
|
47
|
+
turbo_frame: Plutonium::REMOTE_MODAL_FRAME)
|
|
49
48
|
|
|
50
49
|
action(:destroy, route_options: {method: :delete},
|
|
51
50
|
record_action: true, collection_record_action: true, category: :danger,
|
|
@@ -86,6 +86,35 @@ module Plutonium
|
|
|
86
86
|
# false = always hide
|
|
87
87
|
inheritable_config_attr :submit_and_continue
|
|
88
88
|
|
|
89
|
+
# modals — drive how :new / :edit and interactive actions render.
|
|
90
|
+
# Actions read these lazily at render time, so override order and
|
|
91
|
+
# subclass inheritance both work naturally.
|
|
92
|
+
VALID_MODAL_MODES = [:centered, :slideover, false].freeze
|
|
93
|
+
|
|
94
|
+
inheritable_config_attr :modal_mode, :modal_size
|
|
95
|
+
modal_mode :slideover
|
|
96
|
+
modal_size :md
|
|
97
|
+
|
|
98
|
+
# Sets `modal_mode` and `modal_size` together with validation.
|
|
99
|
+
#
|
|
100
|
+
# - :slideover (default) — slide-in panel from the right
|
|
101
|
+
# - :centered — centered dialog
|
|
102
|
+
# - false — no modal; new/edit are full standalone pages
|
|
103
|
+
#
|
|
104
|
+
# `size:` see Plutonium::UI::Modal::Base::VALID_SIZES. `:auto`
|
|
105
|
+
# hugs the form's natural width.
|
|
106
|
+
def self.modal(mode, size: :md)
|
|
107
|
+
unless VALID_MODAL_MODES.include?(mode)
|
|
108
|
+
raise ArgumentError, "modal must be one of #{VALID_MODAL_MODES.inspect}, got #{mode.inspect}"
|
|
109
|
+
end
|
|
110
|
+
unless Plutonium::UI::Modal::Base::VALID_SIZES.include?(size)
|
|
111
|
+
raise ArgumentError,
|
|
112
|
+
"modal size must be one of #{Plutonium::UI::Modal::Base::VALID_SIZES.inspect}, got #{size.inspect}"
|
|
113
|
+
end
|
|
114
|
+
modal_mode mode
|
|
115
|
+
modal_size size
|
|
116
|
+
end
|
|
117
|
+
|
|
89
118
|
def initialize
|
|
90
119
|
super
|
|
91
120
|
end
|
|
@@ -4,29 +4,6 @@ module Plutonium
|
|
|
4
4
|
module Helpers
|
|
5
5
|
# Helper module for managing asset-related functionality
|
|
6
6
|
module AssetsHelper
|
|
7
|
-
# Generate a stylesheet tag for the resource
|
|
8
|
-
#
|
|
9
|
-
# @return [ActiveSupport::SafeBuffer] HTML stylesheet link tag
|
|
10
|
-
def resource_stylesheet_tag
|
|
11
|
-
url = resource_asset_url_for(:css, resource_stylesheet_asset)
|
|
12
|
-
stylesheet_link_tag(url, "data-turbo-track": "reload")
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Generate a script tag for the resource
|
|
16
|
-
#
|
|
17
|
-
# @return [ActiveSupport::SafeBuffer] HTML script tag
|
|
18
|
-
def resource_script_tag
|
|
19
|
-
url = resource_asset_url_for(:js, resource_script_asset)
|
|
20
|
-
javascript_include_tag(url, "data-turbo-track": "reload", type: "module")
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Generate a favicon link tag
|
|
24
|
-
#
|
|
25
|
-
# @return [ActiveSupport::SafeBuffer] HTML favicon link tag
|
|
26
|
-
def resource_favicon_tag
|
|
27
|
-
favicon_link_tag(resource_favicon_asset)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
7
|
# Generate an image tag for the logo
|
|
31
8
|
#
|
|
32
9
|
# @param classname [String] CSS class name for the image tag
|
|
@@ -56,13 +33,6 @@ module Plutonium
|
|
|
56
33
|
Plutonium.configuration.assets.script
|
|
57
34
|
end
|
|
58
35
|
|
|
59
|
-
# Get the favicon asset path
|
|
60
|
-
#
|
|
61
|
-
# @return [String] path to the favicon asset
|
|
62
|
-
def resource_favicon_asset
|
|
63
|
-
Plutonium.configuration.assets.favicon
|
|
64
|
-
end
|
|
65
|
-
|
|
66
36
|
private
|
|
67
37
|
|
|
68
38
|
# Generate the appropriate asset URL based on the environment
|
|
@@ -17,50 +17,6 @@ module Plutonium
|
|
|
17
17
|
}
|
|
18
18
|
)
|
|
19
19
|
end
|
|
20
|
-
|
|
21
|
-
def read_more(content, clamp = 4)
|
|
22
|
-
return if content.blank?
|
|
23
|
-
|
|
24
|
-
# Stimulus Read More (https://www.stimulus-components.com/docs/stimulus-read-more/)
|
|
25
|
-
style = "overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; " \
|
|
26
|
-
"-webkit-line-clamp: var(--read-more-line-clamp, #{clamp});"
|
|
27
|
-
|
|
28
|
-
tag.div(
|
|
29
|
-
data: {
|
|
30
|
-
controller: "read-more",
|
|
31
|
-
read_more_more_text_value: "Read more",
|
|
32
|
-
read_more_less_text_value: "Read less"
|
|
33
|
-
}
|
|
34
|
-
) do
|
|
35
|
-
concat tag.div(content,
|
|
36
|
-
style:,
|
|
37
|
-
data: {read_more_target: "content"})
|
|
38
|
-
|
|
39
|
-
next unless content.lines.size > clamp
|
|
40
|
-
|
|
41
|
-
concat tag.button("Read more",
|
|
42
|
-
class: "btn btn-sm btn-link text-decoration-none ps-0",
|
|
43
|
-
data: {action: "read-more#toggle"})
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def quill(content)
|
|
48
|
-
return if content.blank?
|
|
49
|
-
|
|
50
|
-
tag.div(
|
|
51
|
-
content,
|
|
52
|
-
class: "ql-viewer",
|
|
53
|
-
data: {
|
|
54
|
-
controller: "quill-viewer"
|
|
55
|
-
}
|
|
56
|
-
)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def clamp_content(content)
|
|
60
|
-
return if content.blank?
|
|
61
|
-
|
|
62
|
-
tag.div content, class: "clamped-content"
|
|
63
|
-
end
|
|
64
20
|
end
|
|
65
21
|
end
|
|
66
22
|
end
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
module Plutonium
|
|
2
2
|
module Helpers
|
|
3
3
|
module DisplayHelper
|
|
4
|
-
# def tooltip(text)
|
|
5
|
-
# text = sanitize text
|
|
6
|
-
# "title=\"#{text}\" data-controller=\"tooltip\" data-bs-title=\"#{text}\"".html_safe
|
|
7
|
-
# end
|
|
8
|
-
|
|
9
4
|
def resource_name(resource_class, count = 1)
|
|
10
5
|
resource_class.model_name.human.pluralize(count)
|
|
11
6
|
end
|
|
@@ -32,59 +27,10 @@ module Plutonium
|
|
|
32
27
|
end
|
|
33
28
|
end
|
|
34
29
|
|
|
35
|
-
def display_field(value:, helper: nil, **options)
|
|
36
|
-
return "-" unless value.present?
|
|
37
|
-
|
|
38
|
-
stack_multiple = options.key?(:stack_multiple) ? options.delete(:stack_multiple) : helper != :display_name_of
|
|
39
|
-
|
|
40
|
-
# clean options list
|
|
41
|
-
options.select! { |k, _v| !k.starts_with? "pu_" }
|
|
42
|
-
|
|
43
|
-
if value.respond_to?(:each) && stack_multiple
|
|
44
|
-
tag.ul class: "list-unstyled m-0" do
|
|
45
|
-
value.each do |val|
|
|
46
|
-
rendered = display_field_value(value: val, helper:, **options)
|
|
47
|
-
concat tag.li(rendered)
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
else
|
|
51
|
-
rendered = display_field_value(value:, helper:, **options)
|
|
52
|
-
tag.span rendered
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
30
|
def display_datetime_value(value)
|
|
57
31
|
timeago value
|
|
58
32
|
end
|
|
59
33
|
|
|
60
|
-
def display_field_value(value:, helper: nil, title: nil, **)
|
|
61
|
-
title = (title != false) ? title || display_name_of(value) : nil
|
|
62
|
-
rendered = helper.present? ? send(helper, value, **) : value
|
|
63
|
-
tag.span rendered, title:
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def display_association_value(association)
|
|
67
|
-
display_name = display_name_of(association)
|
|
68
|
-
if registered_resources.include?(association.class)
|
|
69
|
-
link_to display_name, resource_url_for(association, parent: nil),
|
|
70
|
-
class: "font-medium text-primary-600 dark:text-primary-500"
|
|
71
|
-
else
|
|
72
|
-
display_name
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def display_numeric_value(value)
|
|
77
|
-
number_with_delimiter value
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def display_boolean_value(value)
|
|
81
|
-
tag.input type: :checkbox, class: "form-check-input", checked: value, disabled: true
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def display_url_value(value)
|
|
85
|
-
link_to nil, value, class: "font-medium text-primary-600 dark:text-primary-500", target: :blank
|
|
86
|
-
end
|
|
87
|
-
|
|
88
34
|
def display_name_of(obj, separator: ", ")
|
|
89
35
|
return unless obj.present?
|
|
90
36
|
|
|
@@ -103,14 +49,6 @@ module Plutonium
|
|
|
103
49
|
# Oh well. Just convert it to a string.
|
|
104
50
|
obj.to_s
|
|
105
51
|
end
|
|
106
|
-
|
|
107
|
-
def display_clamped_quill(value)
|
|
108
|
-
clamp_content quill(value)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def display_attachment_value(value, **, &)
|
|
112
|
-
attachment_preview(value, **, &)
|
|
113
|
-
end
|
|
114
52
|
end
|
|
115
53
|
end
|
|
116
54
|
end
|
|
@@ -16,10 +16,6 @@ module Plutonium
|
|
|
16
16
|
# modal frame specifically.
|
|
17
17
|
def in_secondary_modal? = current_turbo_frame == Plutonium::REMOTE_MODAL_SECONDARY_FRAME
|
|
18
18
|
|
|
19
|
-
def remote_modal_frame_tag(&)
|
|
20
|
-
turbo_frame_tag(Plutonium::REMOTE_MODAL_FRAME, &)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
19
|
# Returns a turbo-frame-scoped element id. Two identically-named forms
|
|
24
20
|
# can be on the page simultaneously (e.g. a primary modal opens a
|
|
25
21
|
# secondary modal, each rendering an `id="resource-form"`). When the
|
data/lib/plutonium/helpers.rb
CHANGED
|
@@ -3,10 +3,8 @@ module Plutonium
|
|
|
3
3
|
def self.included(base)
|
|
4
4
|
base.class_eval do
|
|
5
5
|
include Plutonium::Helpers::ApplicationHelper
|
|
6
|
-
include Plutonium::Helpers::AttachmentHelper
|
|
7
6
|
include Plutonium::Helpers::ContentHelper
|
|
8
7
|
include Plutonium::Helpers::DisplayHelper
|
|
9
|
-
include Plutonium::Helpers::TableHelper
|
|
10
8
|
include Plutonium::Helpers::TurboHelper
|
|
11
9
|
include Plutonium::Helpers::TurboStreamActionsHelper
|
|
12
10
|
include Plutonium::Helpers::AssetsHelper
|
|
@@ -1,48 +1,6 @@
|
|
|
1
1
|
module Plutonium
|
|
2
2
|
module Resource
|
|
3
3
|
class Definition < Plutonium::Definition::Base
|
|
4
|
-
class_attribute :modal_mode, default: :slideover, instance_accessor: false
|
|
5
|
-
|
|
6
|
-
VALID_MODAL_MODES = [:centered, :slideover, false].freeze
|
|
7
|
-
|
|
8
|
-
# Sets how :new / :edit actions render.
|
|
9
|
-
# - :slideover (default) — slide-in panel from the right
|
|
10
|
-
# - :centered — centered dialog
|
|
11
|
-
# - false — no modal; new/edit are full standalone pages
|
|
12
|
-
def self.modal(mode)
|
|
13
|
-
unless VALID_MODAL_MODES.include?(mode)
|
|
14
|
-
raise ArgumentError, "modal must be one of #{VALID_MODAL_MODES.inspect}, got #{mode.inspect}"
|
|
15
|
-
end
|
|
16
|
-
self.modal_mode = mode
|
|
17
|
-
configure_crud_modal_targets!
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Re-derives the default :new / :edit actions so their turbo_frame
|
|
21
|
-
# matches the current `modal_mode`. Called when `.modal` is set
|
|
22
|
-
# and once at Resource::Definition load (so the default
|
|
23
|
-
# :slideover state propagates to the action records). Subclasses
|
|
24
|
-
# inherit those records via DefineableProps#inherited (deep_dup);
|
|
25
|
-
# calling `.modal` on a subclass re-runs this method locally.
|
|
26
|
-
def self.configure_crud_modal_targets!
|
|
27
|
-
target = (modal_mode == false) ? nil : Plutonium::REMOTE_MODAL_FRAME
|
|
28
|
-
[:new, :edit].each do |name|
|
|
29
|
-
action = defined_actions[name]
|
|
30
|
-
next unless action
|
|
31
|
-
next if action.turbo_frame == target
|
|
32
|
-
defined_actions[name] = action.with(turbo_frame: target)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def modal
|
|
37
|
-
self.class.modal_mode
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Apply the default modal target ("remote_modal") to :new / :edit
|
|
41
|
-
# so resources that never call `.modal` still get the slideover
|
|
42
|
-
# behavior. Subclasses inherit the configured actions via
|
|
43
|
-
# DefineableProps' deep_dup; calling `.modal` on a subclass
|
|
44
|
-
# re-runs the configuration locally.
|
|
45
|
-
configure_crud_modal_targets!
|
|
46
4
|
end
|
|
47
5
|
end
|
|
48
6
|
end
|
|
@@ -50,7 +50,7 @@ module Plutonium
|
|
|
50
50
|
link_to(
|
|
51
51
|
url_with_return_to,
|
|
52
52
|
class: button_classes,
|
|
53
|
-
data: {turbo_frame: @action.turbo_frame}.merge(@extra_data)
|
|
53
|
+
data: {turbo_frame: @action.turbo_frame(current_definition)}.merge(@extra_data)
|
|
54
54
|
) do
|
|
55
55
|
render_button_content
|
|
56
56
|
end
|
|
@@ -67,7 +67,7 @@ module Plutonium
|
|
|
67
67
|
data: {
|
|
68
68
|
turbo: @action.turbo,
|
|
69
69
|
turbo_confirm: @action.confirmation.presence,
|
|
70
|
-
turbo_frame: @action.turbo_frame
|
|
70
|
+
turbo_frame: @action.turbo_frame(current_definition)
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
) do
|
|
@@ -84,7 +84,8 @@ module Plutonium
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
# Add turbo frame if specified
|
|
87
|
-
|
|
87
|
+
frame = @action.turbo_frame(current_definition)
|
|
88
|
+
link_attrs[:data] = {turbo_frame: frame} if frame
|
|
88
89
|
|
|
89
90
|
# Add confirmation and method for non-GET requests
|
|
90
91
|
if @action.confirmation || @action.route_options.method != :get
|