plutonium 0.34.1 → 0.35.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/skill.md +53 -0
- data/.claude/skills/{assets → plutonium-assets}/SKILL.md +13 -8
- data/.claude/skills/{connect-resource → plutonium-connect-resource}/SKILL.md +1 -1
- data/.claude/skills/{controller → plutonium-controller}/SKILL.md +27 -13
- data/.claude/skills/{create-resource → plutonium-create-resource}/SKILL.md +1 -1
- data/.claude/skills/{definition → plutonium-definition}/SKILL.md +10 -10
- data/.claude/skills/{definition-actions → plutonium-definition-actions}/SKILL.md +34 -9
- data/.claude/skills/{definition-fields → plutonium-definition-fields}/SKILL.md +38 -10
- data/.claude/skills/plutonium-definition-query/SKILL.md +356 -0
- data/.claude/skills/{forms → plutonium-forms}/SKILL.md +6 -6
- data/.claude/skills/{installation → plutonium-installation}/SKILL.md +9 -9
- data/.claude/skills/{interaction → plutonium-interaction}/SKILL.md +20 -19
- data/.claude/skills/{model → plutonium-model}/SKILL.md +3 -3
- data/.claude/skills/{model-features → plutonium-model-features}/SKILL.md +3 -3
- data/.claude/skills/{nested-resources → plutonium-nested-resources}/SKILL.md +5 -5
- data/.claude/skills/{package → plutonium-package}/SKILL.md +7 -8
- data/.claude/skills/{policy → plutonium-policy}/SKILL.md +26 -4
- data/.claude/skills/{portal → plutonium-portal}/SKILL.md +33 -31
- data/.claude/skills/{resource → plutonium-resource}/SKILL.md +27 -27
- data/.claude/skills/{rodauth → plutonium-rodauth}/SKILL.md +5 -5
- data/.claude/skills/plutonium-theming/SKILL.md +424 -0
- data/.claude/skills/{views → plutonium-views}/SKILL.md +7 -7
- data/CHANGELOG.md +52 -0
- data/CLAUDE.md +215 -0
- data/CONTRIBUTING.md +72 -18
- data/README.md +100 -19
- data/app/assets/plutonium.css +1 -11
- data/app/assets/plutonium.js +1685 -1146
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +70 -70
- data/app/assets/plutonium.min.js.map +4 -4
- data/app/views/resource/interactive_bulk_action.html.erb +1 -5
- data/app/views/rodauth/_email_auth_request_form.html.erb +1 -1
- data/app/views/rodauth/_login_form.html.erb +15 -55
- data/app/views/rodauth/_login_form_footer.html.erb +2 -2
- data/app/views/rodauth/_password_visibility.html.erb +2 -8
- data/app/views/rodauth/add_recovery_codes.html.erb +2 -2
- data/app/views/rodauth/change_login.html.erb +36 -19
- data/app/views/rodauth/change_password.html.erb +34 -10
- data/app/views/rodauth/close_account.html.erb +12 -4
- data/app/views/rodauth/confirm_password.html.erb +19 -17
- data/app/views/rodauth/create_account.html.erb +30 -109
- data/app/views/rodauth/email_auth.html.erb +1 -1
- data/app/views/rodauth/logout.html.erb +4 -4
- data/app/views/rodauth/otp_auth.html.erb +13 -4
- data/app/views/rodauth/otp_disable.html.erb +12 -4
- data/app/views/rodauth/otp_setup.html.erb +29 -12
- data/app/views/rodauth/otp_unlock.html.erb +19 -10
- data/app/views/rodauth/otp_unlock_not_available.html.erb +7 -7
- data/app/views/rodauth/recovery_auth.html.erb +12 -4
- data/app/views/rodauth/recovery_codes.html.erb +12 -4
- data/app/views/rodauth/remember.html.erb +7 -7
- data/app/views/rodauth/reset_password.html.erb +23 -7
- data/app/views/rodauth/reset_password_request.html.erb +14 -10
- data/app/views/rodauth/sms_auth.html.erb +13 -4
- data/app/views/rodauth/sms_confirm.html.erb +13 -4
- data/app/views/rodauth/sms_disable.html.erb +12 -4
- data/app/views/rodauth/sms_request.html.erb +1 -1
- data/app/views/rodauth/sms_setup.html.erb +23 -7
- data/app/views/rodauth/two_factor_auth.html.erb +2 -2
- data/app/views/rodauth/two_factor_disable.html.erb +12 -4
- data/app/views/rodauth/two_factor_manage.html.erb +7 -7
- data/app/views/rodauth/unlock_account.html.erb +13 -5
- data/app/views/rodauth/unlock_account_request.html.erb +2 -2
- data/app/views/rodauth/verify_account.html.erb +25 -7
- data/app/views/rodauth/verify_account_resend.html.erb +14 -10
- data/app/views/rodauth/verify_login_change.html.erb +1 -1
- data/app/views/rodauth/webauthn_auth.html.erb +1 -1
- data/app/views/rodauth/webauthn_remove.html.erb +18 -8
- data/app/views/rodauth/webauthn_setup.html.erb +12 -4
- data/docs/.vitepress/config.ts +15 -26
- data/docs/.vitepress/theme/custom.css +388 -29
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/tutorial/02-first-resource.md +9 -0
- data/docs/getting-started/tutorial/06-nested-resources.md +2 -2
- data/docs/getting-started/tutorial/07-author-portal.md +191 -0
- data/docs/getting-started/tutorial/{07-customizing-ui.md → 08-customizing-ui.md} +7 -7
- data/docs/getting-started/tutorial/index.md +5 -2
- data/docs/guides/authorization.md +33 -0
- data/docs/guides/creating-packages.md +12 -16
- data/docs/guides/custom-actions.md +36 -0
- data/docs/guides/search-filtering.md +121 -42
- data/docs/guides/theming.md +232 -36
- data/docs/index.md +203 -57
- data/docs/public/og-image.png +0 -0
- data/docs/reference/controller/index.md +14 -16
- data/docs/reference/definition/actions.md +38 -3
- data/docs/reference/definition/fields.md +3 -3
- data/docs/reference/definition/index.md +2 -2
- data/docs/reference/generators/index.md +0 -1
- data/docs/reference/interaction/index.md +14 -10
- data/docs/reference/model/index.md +0 -1
- data/docs/reference/portal/index.md +13 -27
- 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/pkg/portal/portal_generator.rb +0 -2
- data/lib/generators/pu/pkg/portal/templates/app/views/package/dashboard/index.html.erb +28 -72
- data/lib/plutonium/action/interactive.rb +2 -2
- data/lib/plutonium/core/controller.rb +2 -1
- data/lib/plutonium/definition/actions.rb +2 -2
- data/lib/plutonium/lib/deep_freezer.rb +3 -7
- data/lib/plutonium/query/filter.rb +14 -0
- data/lib/plutonium/query/filters/association.rb +49 -0
- data/lib/plutonium/query/filters/boolean.rb +35 -0
- data/lib/plutonium/query/filters/date.rb +97 -0
- data/lib/plutonium/query/filters/date_range.rb +58 -0
- data/lib/plutonium/query/filters/select.rb +55 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +24 -6
- data/lib/plutonium/resource/controllers/interactive_actions.rb +76 -58
- data/lib/plutonium/resource/controllers/queryable.rb +4 -2
- data/lib/plutonium/resource/query_object.rb +1 -1
- data/lib/plutonium/ui/action_button.rb +23 -65
- data/lib/plutonium/ui/actions_dropdown.rb +103 -0
- data/lib/plutonium/ui/block.rb +1 -1
- data/lib/plutonium/ui/breadcrumbs.rb +12 -19
- data/lib/plutonium/ui/color_mode_selector.rb +1 -1
- data/lib/plutonium/ui/component/kit.rb +6 -0
- data/lib/plutonium/ui/component_classes.rb +102 -0
- data/lib/plutonium/ui/display/base.rb +15 -0
- data/lib/plutonium/ui/display/components/attachment.rb +6 -5
- data/lib/plutonium/ui/display/components/boolean.rb +23 -0
- data/lib/plutonium/ui/display/components/color.rb +23 -0
- data/lib/plutonium/ui/display/resource.rb +1 -1
- data/lib/plutonium/ui/display/theme.rb +29 -15
- data/lib/plutonium/ui/empty_card.rb +3 -3
- data/lib/plutonium/ui/form/base.rb +20 -0
- data/lib/plutonium/ui/form/components/key_value_store.rb +11 -11
- data/lib/plutonium/ui/form/components/resource_select.rb +31 -0
- data/lib/plutonium/ui/form/components/secure_association.rb +1 -2
- data/lib/plutonium/ui/form/components/uppy.rb +5 -4
- data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +4 -4
- data/lib/plutonium/ui/form/interaction.rb +17 -1
- data/lib/plutonium/ui/form/query.rb +133 -80
- data/lib/plutonium/ui/form/theme.rb +50 -35
- data/lib/plutonium/ui/frame_navigator_panel.rb +2 -2
- data/lib/plutonium/ui/layout/base.rb +1 -1
- data/lib/plutonium/ui/layout/header.rb +4 -7
- data/lib/plutonium/ui/layout/rodauth_layout.rb +7 -7
- data/lib/plutonium/ui/layout/sidebar.rb +1 -1
- data/lib/plutonium/ui/nav_grid_menu.rb +7 -6
- data/lib/plutonium/ui/nav_user.rb +9 -8
- data/lib/plutonium/ui/page/interactive_action.rb +5 -5
- data/lib/plutonium/ui/page_header.rb +29 -10
- data/lib/plutonium/ui/panel.rb +4 -4
- data/lib/plutonium/ui/sidebar_menu.rb +8 -8
- data/lib/plutonium/ui/skeleton_table.rb +7 -8
- data/lib/plutonium/ui/tab_list.rb +5 -5
- data/lib/plutonium/ui/table/base.rb +3 -0
- data/lib/plutonium/ui/table/components/attachment.rb +4 -3
- data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +82 -0
- data/lib/plutonium/ui/table/components/pagy_info.rb +2 -2
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +13 -8
- data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +101 -0
- data/lib/plutonium/ui/table/components/scopes_bar.rb +2 -2
- data/lib/plutonium/ui/table/components/selection_column.rb +100 -0
- data/lib/plutonium/ui/table/display_theme.rb +6 -6
- data/lib/plutonium/ui/table/resource.rb +93 -52
- data/lib/plutonium/ui/table/theme.rb +28 -15
- data/lib/plutonium/version.rb +1 -1
- data/package.json +2 -2
- data/plutonium.gemspec +5 -4
- data/src/css/components.css +471 -0
- data/src/css/intl_tel_input.css +2 -2
- data/src/css/plutonium.css +2 -0
- data/src/css/tokens.css +149 -0
- data/src/js/controllers/bulk_actions_controller.js +109 -0
- data/src/js/controllers/filter_panel_controller.js +35 -0
- data/src/js/controllers/register_controllers.js +5 -1
- data/src/js/controllers/resource_drop_down_controller.js +25 -1
- data/src/js/controllers/slim_select_controller.js +6 -2
- data/src/js/turbo/turbo_actions.js +1 -1
- metadata +52 -39
- data/.claude/skills/definition-query/SKILL.md +0 -334
- data/docs/concepts/architecture.md +0 -226
- data/docs/concepts/auto-detection.md +0 -254
- data/docs/concepts/index.md +0 -61
- data/docs/concepts/packages-portals.md +0 -304
- data/docs/concepts/resources.md +0 -224
- data/docs/cookbook/blog.md +0 -411
- data/docs/cookbook/index.md +0 -289
- data/docs/cookbook/saas.md +0 -481
- data/docs/public/CLAUDE.md +0 -578
- data/lib/generators/pu/pkg/portal/templates/app/controllers/resource_controller.rb.tt +0 -5
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module UI
|
|
5
|
+
# Dropdown menu for secondary and danger actions
|
|
6
|
+
# Groups actions by category with danger actions shown after a divider
|
|
7
|
+
class ActionsDropdown < Plutonium::UI::Component::Base
|
|
8
|
+
def initialize(actions:, subject:)
|
|
9
|
+
@actions = actions
|
|
10
|
+
@subject = subject
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template
|
|
14
|
+
div(data: {controller: "resource-drop-down"}) do
|
|
15
|
+
render_trigger_button
|
|
16
|
+
render_dropdown_menu
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def render_trigger_button
|
|
23
|
+
button(
|
|
24
|
+
type: "button",
|
|
25
|
+
class: "pu-btn pu-btn-md pu-btn-outline",
|
|
26
|
+
aria: {expanded: "false", haspopup: "true"},
|
|
27
|
+
data: {resource_drop_down_target: "trigger"}
|
|
28
|
+
) do
|
|
29
|
+
span(class: "sr-only") { "Open actions menu" }
|
|
30
|
+
plain "Actions"
|
|
31
|
+
render Phlex::TablerIcons::ChevronDown.new(class: "w-4 h-4 ml-1")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def render_dropdown_menu
|
|
36
|
+
div(
|
|
37
|
+
class: "hidden absolute right-0 z-50 mt-2 w-48 origin-top-right bg-[var(--pu-surface)] border border-[var(--pu-border)] rounded-[var(--pu-radius-lg)] overflow-hidden",
|
|
38
|
+
style: "box-shadow: var(--pu-shadow-lg)",
|
|
39
|
+
data: {resource_drop_down_target: "menu"}
|
|
40
|
+
) do
|
|
41
|
+
render_secondary_actions if secondary_actions.any?
|
|
42
|
+
render_danger_divider if secondary_actions.any? && danger_actions.any?
|
|
43
|
+
render_danger_actions if danger_actions.any?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def render_secondary_actions
|
|
48
|
+
div(class: "py-1") do
|
|
49
|
+
secondary_actions.each { |action| render_action_item(action) }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def render_danger_divider
|
|
54
|
+
div(class: "border-t border-[var(--pu-border-muted)]")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def render_danger_actions
|
|
58
|
+
div(class: "py-1") do
|
|
59
|
+
danger_actions.each { |action| render_action_item(action, danger: true) }
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def render_action_item(action, danger: false)
|
|
64
|
+
url = route_options_to_url(action.route_options, @subject)
|
|
65
|
+
|
|
66
|
+
link_attrs = {
|
|
67
|
+
href: url,
|
|
68
|
+
class: tokens(
|
|
69
|
+
"flex items-center gap-2 px-4 py-2 text-sm transition-colors",
|
|
70
|
+
danger ? "text-danger-600 dark:text-danger-400 hover:bg-danger-50 dark:hover:bg-danger-900/30" : "text-[var(--pu-text)] hover:bg-[var(--pu-surface-alt)]"
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Add turbo frame if specified
|
|
75
|
+
link_attrs[:data] = {turbo_frame: action.turbo_frame} if action.turbo_frame
|
|
76
|
+
|
|
77
|
+
# Add confirmation if specified
|
|
78
|
+
if action.confirmation
|
|
79
|
+
link_attrs[:data] ||= {}
|
|
80
|
+
link_attrs[:data][:turbo_method] = :delete if action.route_options.method == :delete
|
|
81
|
+
link_attrs[:data][:turbo_confirm] = action.confirmation
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
a(**link_attrs) do
|
|
85
|
+
render action.icon.new(class: "w-4 h-4") if action.icon
|
|
86
|
+
span { action.label }
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def secondary_actions
|
|
91
|
+
@secondary_actions ||= @actions.select { |a| a.category.secondary? }.sort_by(&:position)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def danger_actions
|
|
95
|
+
@danger_actions ||= @actions.select { |a| a.category.danger? }.sort_by(&:position)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def render?
|
|
99
|
+
@actions.any?
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/plutonium/ui/block.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Plutonium
|
|
|
4
4
|
def view_template(&)
|
|
5
5
|
raise ArgumentError, "Block requires a content block" unless block_given?
|
|
6
6
|
|
|
7
|
-
div class: "relative bg-
|
|
7
|
+
div class: "relative bg-[var(--pu-surface)] rounded-[var(--pu-radius-lg)] my-3", style: "box-shadow: var(--pu-shadow-md)" do
|
|
8
8
|
yield
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -6,20 +6,17 @@ module Plutonium
|
|
|
6
6
|
|
|
7
7
|
def view_template
|
|
8
8
|
nav(
|
|
9
|
-
class:
|
|
10
|
-
"flex py-3 text-gray-700 mb-2",
|
|
9
|
+
class: "flex py-3 mb-2",
|
|
11
10
|
aria_label: "Breadcrumb"
|
|
12
11
|
) do
|
|
13
12
|
ol(
|
|
14
|
-
class:
|
|
15
|
-
"inline-flex items-center space-x-1 md:space-x-2 rtl:space-x-reverse"
|
|
13
|
+
class: "inline-flex items-center gap-1 md:gap-2"
|
|
16
14
|
) do
|
|
17
15
|
# Dashboard
|
|
18
16
|
li(class: "inline-flex items-center") do
|
|
19
17
|
a(
|
|
20
18
|
href: helpers.root_path,
|
|
21
|
-
class:
|
|
22
|
-
"inline-flex items-center text-sm font-medium text-gray-700 hover:text-primary-600 dark:text-gray-200 dark:hover:text-white"
|
|
19
|
+
class: "inline-flex items-center text-sm font-medium text-[var(--pu-text-muted)] hover:text-primary-600 transition-colors"
|
|
23
20
|
) do
|
|
24
21
|
svg(
|
|
25
22
|
class: "w-3 h-3 me-2.5",
|
|
@@ -42,7 +39,7 @@ module Plutonium
|
|
|
42
39
|
# Parent Resource
|
|
43
40
|
li(class: "flex items-center") do
|
|
44
41
|
svg(
|
|
45
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-
|
|
42
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-[var(--pu-text-subtle)]",
|
|
46
43
|
aria_hidden: "true",
|
|
47
44
|
xmlns: "http://www.w3.org/2000/svg",
|
|
48
45
|
fill: "none",
|
|
@@ -58,14 +55,13 @@ module Plutonium
|
|
|
58
55
|
end
|
|
59
56
|
link_to resource_name_plural(current_parent.class),
|
|
60
57
|
resource_url_for(current_parent.class, parent: nil),
|
|
61
|
-
class:
|
|
62
|
-
"ms-1 text-sm font-medium text-gray-700 hover:text-primary-600 md:ms-2 dark:text-gray-200 dark:hover:text-white"
|
|
58
|
+
class: "ms-1 text-sm font-medium text-[var(--pu-text-muted)] hover:text-primary-600 md:ms-2 transition-colors"
|
|
63
59
|
end
|
|
64
60
|
|
|
65
61
|
# Parent Itself
|
|
66
62
|
li(class: "flex items-center") do
|
|
67
63
|
svg(
|
|
68
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-
|
|
64
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-[var(--pu-text-subtle)]",
|
|
69
65
|
aria_hidden: "true",
|
|
70
66
|
xmlns: "http://www.w3.org/2000/svg",
|
|
71
67
|
fill: "none",
|
|
@@ -81,8 +77,7 @@ module Plutonium
|
|
|
81
77
|
end
|
|
82
78
|
link_to display_name_of(current_parent),
|
|
83
79
|
resource_url_for(current_parent, parent: nil),
|
|
84
|
-
class:
|
|
85
|
-
"ms-1 text-sm font-medium text-gray-700 hover:text-primary-600 md:ms-2 dark:text-gray-200 dark:hover:text-white"
|
|
80
|
+
class: "ms-1 text-sm font-medium text-[var(--pu-text-muted)] hover:text-primary-600 md:ms-2 transition-colors"
|
|
86
81
|
end
|
|
87
82
|
end
|
|
88
83
|
|
|
@@ -92,7 +87,7 @@ module Plutonium
|
|
|
92
87
|
# Record Resource
|
|
93
88
|
li(class: "flex items-center") do
|
|
94
89
|
svg(
|
|
95
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-
|
|
90
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-[var(--pu-text-subtle)]",
|
|
96
91
|
aria_hidden: "true",
|
|
97
92
|
xmlns: "http://www.w3.org/2000/svg",
|
|
98
93
|
fill: "none",
|
|
@@ -108,8 +103,7 @@ module Plutonium
|
|
|
108
103
|
end
|
|
109
104
|
link_to resource_name_plural(resource_class),
|
|
110
105
|
resource_url_for(resource_class),
|
|
111
|
-
class:
|
|
112
|
-
"ms-1 text-sm font-medium text-gray-700 hover:text-primary-600 md:ms-2 dark:text-gray-200 dark:hover:text-white"
|
|
106
|
+
class: "ms-1 text-sm font-medium text-[var(--pu-text-muted)] hover:text-primary-600 md:ms-2 transition-colors"
|
|
113
107
|
end
|
|
114
108
|
end
|
|
115
109
|
|
|
@@ -117,7 +111,7 @@ module Plutonium
|
|
|
117
111
|
if resource_record!.persisted? && action_name != "show"
|
|
118
112
|
li(class: "flex items-center") do
|
|
119
113
|
svg(
|
|
120
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-
|
|
114
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-[var(--pu-text-subtle)]",
|
|
121
115
|
aria_hidden: "true",
|
|
122
116
|
xmlns: "http://www.w3.org/2000/svg",
|
|
123
117
|
fill: "none",
|
|
@@ -133,8 +127,7 @@ module Plutonium
|
|
|
133
127
|
end
|
|
134
128
|
link_to display_name_of(resource_record!),
|
|
135
129
|
resource_url_for(resource_record!),
|
|
136
|
-
class:
|
|
137
|
-
"ms-1 text-sm font-medium text-gray-700 hover:text-primary-600 md:ms-2 dark:text-gray-200 dark:hover:text-white"
|
|
130
|
+
class: "ms-1 text-sm font-medium text-[var(--pu-text-muted)] hover:text-primary-600 md:ms-2 transition-colors"
|
|
138
131
|
end
|
|
139
132
|
end
|
|
140
133
|
end
|
|
@@ -142,7 +135,7 @@ module Plutonium
|
|
|
142
135
|
# Trailing Caret
|
|
143
136
|
li(class: "flex items-center") do
|
|
144
137
|
svg(
|
|
145
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-
|
|
138
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-1 text-[var(--pu-text-subtle)]",
|
|
146
139
|
aria_hidden: "true",
|
|
147
140
|
xmlns: "http://www.w3.org/2000/svg",
|
|
148
141
|
fill: "none",
|
|
@@ -8,7 +8,7 @@ module Plutonium
|
|
|
8
8
|
class ColorModeSelector < Plutonium::UI::Component::Base
|
|
9
9
|
# Common CSS classes used across the component
|
|
10
10
|
COMMON_CLASSES = {
|
|
11
|
-
button: "inline-flex justify-center items-center p-2 text-
|
|
11
|
+
button: "inline-flex justify-center items-center p-2 text-[var(--pu-text-muted)] rounded-[var(--pu-radius-md)] cursor-pointer hover:text-[var(--pu-text)] hover:bg-[var(--pu-surface-alt)] transition-colors duration-200",
|
|
12
12
|
icon: "w-5 h-5"
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
@@ -70,6 +70,8 @@ module Plutonium
|
|
|
70
70
|
|
|
71
71
|
def BuildActionButton(...) = Plutonium::UI::ActionButton.new(...)
|
|
72
72
|
|
|
73
|
+
def BuildActionsDropdown(...) = Plutonium::UI::ActionsDropdown.new(...)
|
|
74
|
+
|
|
73
75
|
def BuildEmptyCard(...) = Plutonium::UI::EmptyCard.new(...)
|
|
74
76
|
|
|
75
77
|
def BuildTableSearchBar(...) = Plutonium::UI::Table::Components::SearchBar.new(...)
|
|
@@ -80,6 +82,10 @@ module Plutonium
|
|
|
80
82
|
|
|
81
83
|
def BuildTablePagination(...) = Plutonium::UI::Table::Components::PagyPagination.new(...)
|
|
82
84
|
|
|
85
|
+
def BuildRowActionsDropdown(...) = Plutonium::UI::Table::Components::RowActionsDropdown.new(...)
|
|
86
|
+
|
|
87
|
+
def BuildBulkActionsToolbar(...) = Plutonium::UI::Table::Components::BulkActionsToolbar.new(...)
|
|
88
|
+
|
|
83
89
|
def BuildColorModeSelector(...) = Plutonium::UI::ColorModeSelector.new(...)
|
|
84
90
|
end
|
|
85
91
|
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module UI
|
|
5
|
+
# Centralized CSS class mappings for Plutonium UI components.
|
|
6
|
+
# Provides reusable class constants that leverage the design token system.
|
|
7
|
+
module ComponentClasses
|
|
8
|
+
# Button component classes
|
|
9
|
+
module Button
|
|
10
|
+
BASE = "pu-btn"
|
|
11
|
+
SIZE_DEFAULT = "pu-btn-md"
|
|
12
|
+
SIZE_SM = "pu-btn-sm"
|
|
13
|
+
SIZE_XS = "pu-btn-xs"
|
|
14
|
+
|
|
15
|
+
VARIANTS = {
|
|
16
|
+
primary: "pu-btn-primary",
|
|
17
|
+
secondary: "pu-btn-secondary",
|
|
18
|
+
danger: "pu-btn-danger",
|
|
19
|
+
success: "pu-btn-success",
|
|
20
|
+
warning: "pu-btn-warning",
|
|
21
|
+
info: "pu-btn-info",
|
|
22
|
+
accent: "pu-btn-accent",
|
|
23
|
+
ghost: "pu-btn-ghost"
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
SOFT_VARIANTS = {
|
|
27
|
+
primary: "pu-btn-soft-primary",
|
|
28
|
+
secondary: "pu-btn-soft-secondary",
|
|
29
|
+
danger: "pu-btn-soft-danger",
|
|
30
|
+
success: "pu-btn-soft-success",
|
|
31
|
+
warning: "pu-btn-soft-warning",
|
|
32
|
+
info: "pu-btn-soft-info",
|
|
33
|
+
accent: "pu-btn-soft-accent"
|
|
34
|
+
}.freeze
|
|
35
|
+
|
|
36
|
+
def self.classes(variant: :primary, size: :default, soft: false)
|
|
37
|
+
variant_class = soft ? SOFT_VARIANTS[variant] : VARIANTS[variant]
|
|
38
|
+
size_class = case size
|
|
39
|
+
when :sm then SIZE_SM
|
|
40
|
+
when :xs then SIZE_XS
|
|
41
|
+
else SIZE_DEFAULT
|
|
42
|
+
end
|
|
43
|
+
"#{BASE} #{size_class} #{variant_class}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Table component classes
|
|
48
|
+
module Table
|
|
49
|
+
WRAPPER = "pu-table-wrapper"
|
|
50
|
+
BASE = "pu-table"
|
|
51
|
+
HEADER = "pu-table-header"
|
|
52
|
+
HEADER_CELL = "pu-table-header-cell"
|
|
53
|
+
BODY_ROW = "pu-table-body-row"
|
|
54
|
+
BODY_ROW_SELECTED = "pu-table-body-row-selected"
|
|
55
|
+
BODY_CELL = "pu-table-body-cell"
|
|
56
|
+
SELECTION_CELL = "pu-selection-cell"
|
|
57
|
+
CHECKBOX = "pu-checkbox"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Form component classes
|
|
61
|
+
module Form
|
|
62
|
+
WRAPPER = "pu-card"
|
|
63
|
+
BODY = "pu-card-body space-y-6"
|
|
64
|
+
FIELDS_WRAPPER = "grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-4 grid-flow-row-dense"
|
|
65
|
+
ACTIONS_WRAPPER = "flex justify-end gap-2"
|
|
66
|
+
|
|
67
|
+
INPUT = "pu-input"
|
|
68
|
+
INPUT_INVALID = "pu-input pu-input-invalid"
|
|
69
|
+
INPUT_VALID = "pu-input pu-input-valid"
|
|
70
|
+
|
|
71
|
+
LABEL = "pu-label"
|
|
72
|
+
HINT = "pu-hint"
|
|
73
|
+
ERROR = "pu-error"
|
|
74
|
+
BUTTON = "pu-btn pu-btn-md pu-btn-primary"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Toolbar component classes
|
|
78
|
+
module Toolbar
|
|
79
|
+
WRAPPER = "pu-toolbar"
|
|
80
|
+
TEXT = "pu-toolbar-text"
|
|
81
|
+
ACTIONS = "pu-toolbar-actions"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Card component classes
|
|
85
|
+
module Card
|
|
86
|
+
BASE = "pu-card"
|
|
87
|
+
BODY = "pu-card-body"
|
|
88
|
+
HEADER = "pu-panel-header"
|
|
89
|
+
TITLE = "pu-panel-title"
|
|
90
|
+
DESCRIPTION = "pu-panel-description"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Empty state classes
|
|
94
|
+
module EmptyState
|
|
95
|
+
WRAPPER = "pu-empty-state"
|
|
96
|
+
ICON = "pu-empty-state-icon"
|
|
97
|
+
TITLE = "pu-empty-state-title"
|
|
98
|
+
DESCRIPTION = "pu-empty-state-description"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -16,6 +16,7 @@ module Plutonium
|
|
|
16
16
|
def markdown_tag(**, &)
|
|
17
17
|
create_component(Plutonium::UI::Display::Components::Markdown, :markdown, **, &)
|
|
18
18
|
end
|
|
19
|
+
alias_method :rich_text_tag, :markdown_tag
|
|
19
20
|
|
|
20
21
|
def attachment_tag(**, &)
|
|
21
22
|
create_component(Plutonium::UI::Display::Components::Attachment, :attachment, **, &)
|
|
@@ -24,6 +25,20 @@ module Plutonium
|
|
|
24
25
|
def phlexi_render_tag(**, &)
|
|
25
26
|
create_component(Plutonium::UI::Display::Components::PhlexiRender, :phlexi_render, **, &)
|
|
26
27
|
end
|
|
28
|
+
|
|
29
|
+
def boolean_tag(**, &)
|
|
30
|
+
create_component(Plutonium::UI::Display::Components::Boolean, :boolean, **, &)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def color_tag(**, &)
|
|
34
|
+
create_component(Plutonium::UI::Display::Components::Color, :color, **, &)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Type aliases for common column types
|
|
38
|
+
alias_method :float_tag, :number_tag
|
|
39
|
+
alias_method :decimal_tag, :number_tag
|
|
40
|
+
alias_method :jsonb_tag, :json_tag
|
|
41
|
+
alias_method :key_value_tag, :hstore_tag
|
|
27
42
|
alias_method :phlexi_tag, :phlexi_render_tag
|
|
28
43
|
end
|
|
29
44
|
|
|
@@ -34,12 +34,13 @@ module Plutonium
|
|
|
34
34
|
return unless attachment.url.present?
|
|
35
35
|
|
|
36
36
|
div(
|
|
37
|
-
class: "w-full aspect-square bg-
|
|
37
|
+
class: "w-full aspect-square bg-[var(--pu-surface)] border border-[var(--pu-border)] rounded-[var(--pu-radius-md)] hover:bg-[var(--pu-surface-alt)] transition-all duration-300",
|
|
38
|
+
style: "box-shadow: var(--pu-shadow-sm)",
|
|
38
39
|
data: {attachment_preview_target: "thumbnail"}
|
|
39
40
|
) do
|
|
40
41
|
a(
|
|
41
42
|
href: attachment.url,
|
|
42
|
-
class: "block aspect-square overflow-hidden rounded-
|
|
43
|
+
class: "block aspect-square overflow-hidden rounded-[var(--pu-radius-md)]",
|
|
43
44
|
target: :blank,
|
|
44
45
|
data: {attachment_preview_target: "thumbnailLink"}
|
|
45
46
|
) do
|
|
@@ -50,7 +51,7 @@ module Plutonium
|
|
|
50
51
|
class: "w-full h-full object-cover"
|
|
51
52
|
)
|
|
52
53
|
else
|
|
53
|
-
div(class: "w-full h-full flex items-center justify-center text-
|
|
54
|
+
div(class: "w-full h-full flex items-center justify-center text-[var(--pu-text-muted)] font-mono") do
|
|
54
55
|
".#{attachment_extension(attachment)}"
|
|
55
56
|
end
|
|
56
57
|
end
|
|
@@ -61,11 +62,11 @@ module Plutonium
|
|
|
61
62
|
def render_caption(attachment)
|
|
62
63
|
return if attributes[:caption] == false
|
|
63
64
|
|
|
64
|
-
div(class: "w-full p-2 text-sm text-
|
|
65
|
+
div(class: "w-full p-2 text-sm text-[var(--pu-text-muted)] truncate text-center") do
|
|
65
66
|
caption = attributes[:caption] || attachment.filename
|
|
66
67
|
a(
|
|
67
68
|
href: attachment.url,
|
|
68
|
-
class: "hover:text-primary-600 dark:hover:text-primary-
|
|
69
|
+
class: "hover:text-primary-600 dark:hover:text-primary-400 transition-colors duration-200",
|
|
69
70
|
target: :blank
|
|
70
71
|
) do
|
|
71
72
|
phlexi_render(caption) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module UI
|
|
5
|
+
module Display
|
|
6
|
+
module Components
|
|
7
|
+
class Boolean < Phlexi::Display::Components::Base
|
|
8
|
+
include Phlexi::Display::Components::Concerns::DisplaysValue
|
|
9
|
+
|
|
10
|
+
def render_value(value)
|
|
11
|
+
p(**attributes) do
|
|
12
|
+
if value
|
|
13
|
+
render Phlex::TablerIcons::Check.new(class: "inline-block w-5 h-5 text-green-600")
|
|
14
|
+
else
|
|
15
|
+
render Phlex::TablerIcons::X.new(class: "inline-block w-5 h-5 text-red-500")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module UI
|
|
5
|
+
module Display
|
|
6
|
+
module Components
|
|
7
|
+
class Color < Phlexi::Display::Components::Base
|
|
8
|
+
include Phlexi::Display::Components::Concerns::DisplaysValue
|
|
9
|
+
|
|
10
|
+
def render_value(value)
|
|
11
|
+
div(**attributes, class: "flex items-center gap-2") do
|
|
12
|
+
div(
|
|
13
|
+
class: "w-6 h-6 rounded border border-[var(--pu-border)]",
|
|
14
|
+
style: "background-color: #{value};"
|
|
15
|
+
)
|
|
16
|
+
span(class: "text-sm text-[var(--pu-text-muted)]") { value }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -59,7 +59,7 @@ module Plutonium
|
|
|
59
59
|
|
|
60
60
|
tablist.with_tab(
|
|
61
61
|
identifier: title.parameterize,
|
|
62
|
-
title: -> { h5(class: "text-2xl font-bold tracking-tight text-
|
|
62
|
+
title: -> { h5(class: "text-2xl font-bold tracking-tight text-[var(--pu-text)]") { title } }
|
|
63
63
|
) do
|
|
64
64
|
FrameNavigatorPanel(title: "", src:, panel_id: "association-panel-#{title.parameterize}")
|
|
65
65
|
end
|
|
@@ -8,21 +8,35 @@ module Plutonium
|
|
|
8
8
|
super.merge({
|
|
9
9
|
base: "",
|
|
10
10
|
value_wrapper: "max-h-[300px] overflow-y-auto",
|
|
11
|
-
fields_wrapper: "p-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
fields_wrapper: "p-8 grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-x-8 gap-y-8 grid-flow-row-dense",
|
|
12
|
+
|
|
13
|
+
# Labels and descriptions
|
|
14
|
+
label: "text-sm font-semibold uppercase tracking-wide text-[var(--pu-text-muted)] mb-2",
|
|
15
|
+
description: "text-sm text-[var(--pu-text-subtle)]",
|
|
16
|
+
placeholder: "text-lg text-[var(--pu-text-subtle)] italic",
|
|
17
|
+
|
|
18
|
+
# Value types
|
|
19
|
+
string: "text-lg text-[var(--pu-text)] whitespace-pre-line leading-relaxed",
|
|
20
|
+
text: "text-lg text-[var(--pu-text)] whitespace-pre-line leading-relaxed",
|
|
21
|
+
link: "text-lg text-primary-600 dark:text-primary-400 hover:text-primary-500 dark:hover:text-primary-300 whitespace-pre-line transition-colors",
|
|
22
|
+
|
|
23
|
+
# Color display
|
|
24
|
+
color: "flex items-center text-lg text-[var(--pu-text)] whitespace-pre-line",
|
|
25
|
+
color_indicator: "w-10 h-10 rounded-lg mr-3 shadow-sm border border-[var(--pu-border)]",
|
|
26
|
+
|
|
27
|
+
# Contact info
|
|
28
|
+
email: "flex items-center gap-2 text-lg text-primary-600 dark:text-primary-400 hover:text-primary-500 transition-colors",
|
|
29
|
+
phone: "flex items-center gap-2 text-lg text-primary-600 dark:text-primary-400 hover:text-primary-500 transition-colors",
|
|
30
|
+
|
|
31
|
+
# Structured content
|
|
32
|
+
json: "text-sm text-[var(--pu-text)] whitespace-pre font-mono bg-[var(--pu-surface-alt)] border border-[var(--pu-border-muted)] rounded-[var(--pu-radius-md)] p-4 overflow-x-auto",
|
|
33
|
+
prefixed_icon: "w-6 h-6 mr-2 text-[var(--pu-text-muted)]",
|
|
34
|
+
markdown: "format dark:format-invert format-primary max-w-none",
|
|
35
|
+
|
|
36
|
+
# Attachments
|
|
37
|
+
attachment_value_wrapper: "grid grid-cols-[repeat(auto-fill,minmax(0,200px))] gap-4",
|
|
38
|
+
|
|
39
|
+
# Render delegation
|
|
26
40
|
phlexi_render: :string
|
|
27
41
|
})
|
|
28
42
|
end
|
|
@@ -8,9 +8,9 @@ module Plutonium
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def view_template
|
|
11
|
-
div(class: "
|
|
12
|
-
div(class: "
|
|
13
|
-
p(class: "
|
|
11
|
+
div(class: "pu-card") do
|
|
12
|
+
div(class: "pu-empty-state") do
|
|
13
|
+
p(class: "pu-empty-state-description") { message }
|
|
14
14
|
yield if block_given?
|
|
15
15
|
end
|
|
16
16
|
end
|
|
@@ -39,6 +39,10 @@ module Plutonium
|
|
|
39
39
|
create_component(Components::KeyValueStore, :key_value_store, **, &)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
def resource_select_tag(**attributes, &)
|
|
43
|
+
create_component(Components::ResourceSelect, :select, **attributes, &)
|
|
44
|
+
end
|
|
45
|
+
|
|
42
46
|
def secure_association_tag(**attributes, &)
|
|
43
47
|
attributes[:data_controller] = tokens(attributes[:data_controller], "slim-select") # TODO: put this behind a config
|
|
44
48
|
create_component(Components::SecureAssociation, :association, **attributes, &)
|
|
@@ -60,6 +64,21 @@ module Plutonium
|
|
|
60
64
|
alias_method :basic_polymorphic_belongs_to_tag, :polymorphic_belongs_to_tag
|
|
61
65
|
# use new methods as defaults
|
|
62
66
|
alias_method :polymorphic_belongs_to_tag, :secure_polymorphic_association_tag
|
|
67
|
+
|
|
68
|
+
# Type aliases for common column types that map to different input types
|
|
69
|
+
alias_method :integer_tag, :number_tag
|
|
70
|
+
alias_method :float_tag, :number_tag
|
|
71
|
+
alias_method :decimal_tag, :number_tag
|
|
72
|
+
alias_method :text_tag, :textarea_tag
|
|
73
|
+
alias_method :datetime_tag, :flatpickr_tag
|
|
74
|
+
alias_method :date_tag, :flatpickr_tag
|
|
75
|
+
alias_method :time_tag, :flatpickr_tag
|
|
76
|
+
alias_method :rich_text_tag, :markdown_tag
|
|
77
|
+
alias_method :json_tag, :textarea_tag
|
|
78
|
+
alias_method :jsonb_tag, :textarea_tag
|
|
79
|
+
alias_method :hstore_tag, :key_value_store_tag
|
|
80
|
+
alias_method :key_value_tag, :key_value_store_tag
|
|
81
|
+
alias_method :association_tag, :secure_association_tag
|
|
63
82
|
end
|
|
64
83
|
|
|
65
84
|
private
|
|
@@ -93,6 +112,7 @@ module Plutonium
|
|
|
93
112
|
def initialize_attributes
|
|
94
113
|
super
|
|
95
114
|
|
|
115
|
+
attributes[:id] ||= :resource_form
|
|
96
116
|
attributes["data-controller"] = "form"
|
|
97
117
|
end
|
|
98
118
|
end
|