plutonium 0.15.13 → 0.15.14
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/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +155 -4
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +8 -8
- data/app/assets/plutonium.min.js.map +4 -4
- data/app/views/components/resource_header/resource_header_component.rb +1 -1
- data/app/views/components/resource_layout/resource_layout_component.html.erb +6 -2
- data/app/views/layouts/resource.html.erb +1 -14
- data/app/views/layouts/rodauth.html.erb +2 -18
- data/app/views/plutonium/_resource_header.html.erb +4 -2
- data/app/views/plutonium/_resource_sidebar.html.erb +1 -1
- data/docs/public/templates/plutonium.rb +6 -12
- data/lib/generators/pu/gem/standard/standard_generator.rb +14 -1
- data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +1 -1
- data/lib/plutonium/ui/color_mode_selector.rb +86 -0
- data/lib/plutonium/ui/component/kit.rb +2 -0
- data/lib/plutonium/ui/component/methods.rb +2 -0
- data/lib/plutonium/ui/dyna_frame/host.rb +2 -2
- data/lib/plutonium/ui/layout/base.rb +135 -0
- data/lib/plutonium/ui/layout/header.rb +121 -0
- data/lib/plutonium/ui/layout/resource_layout.rb +26 -0
- data/lib/plutonium/ui/layout/rodauth_layout.rb +34 -0
- data/lib/plutonium/ui/layout/sidebar.rb +56 -0
- data/lib/plutonium/ui/table/resource.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium.rb +1 -1
- data/lib/rodauth/features/case_insensitive_login.rb +10 -4
- data/package-lock.json +2 -2
- data/package.json +1 -1
- data/src/js/controllers/header_controller.js +184 -0
- data/src/js/controllers/register_controllers.js +4 -2
- data/src/js/controllers/{resource_header_controller.js → sidebar_controller.js} +2 -2
- metadata +25 -4
- /data/lib/rodauth/{loader.rb → plugins.rb} +0 -0
@@ -13,7 +13,7 @@ module PlutoniumUi
|
|
13
13
|
# base attributes go here
|
14
14
|
{
|
15
15
|
classname: "resource-header bg-white border-b border-gray-200 px-4 py-2.5 dark:bg-gray-800 dark:border-gray-700 fixed left-0 right-0 top-0 z-50",
|
16
|
-
controller: "
|
16
|
+
controller: "header"
|
17
17
|
}
|
18
18
|
end
|
19
19
|
|
@@ -29,10 +29,14 @@
|
|
29
29
|
|
30
30
|
<%= assets %>
|
31
31
|
<%= head %>
|
32
|
-
<%#
|
32
|
+
<%#
|
33
|
+
|
34
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.snow.min.css" integrity="sha512-/FHUK/LsH78K9XTqsR9hbzr21J8B8RwHR/r8Jv9fzry6NVAOVIGFKQCNINsbhK7a1xubVu2r5QZcz2T9cKpubw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
33
35
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.min.js" integrity="sha512-P2W2rr8ikUPfa31PLBo5bcBQrsa+TNj8jiKadtaIrHQGMo6hQM6RdPjQYxlNguwHz8AwSQ28VkBK6kHBLgd/8g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
34
36
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.css" integrity="sha512-GvqWM4KWH8mbgWIyvwdH8HgjUbyZTXrCq0sjGij9fDNiXz3vJoy3jCcAaWNekH2rJe4hXVWCJKN+bEW8V7AAEQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
35
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.js" integrity="sha512-0E8oaoA2v32h26IycsmRDShtQ8kMgD91zWVBxdIvUCjU3xBw81PV61QBsBqNQpWkp/zYJZip8Ag3ifmzz1wCKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
37
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.js" integrity="sha512-0E8oaoA2v32h26IycsmRDShtQ8kMgD91zWVBxdIvUCjU3xBw81PV61QBsBqNQpWkp/zYJZip8Ag3ifmzz1wCKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
38
|
+
|
39
|
+
%>
|
36
40
|
</head>
|
37
41
|
<body class="<%= body_classname %>">
|
38
42
|
<%= header %>
|
@@ -1,16 +1,3 @@
|
|
1
|
-
<%=
|
2
|
-
page_title: make_page_title(@page_title) do |layout| %>
|
3
|
-
<% layout.with_favicon do %>
|
4
|
-
<%= resource_favicon_tag %>
|
5
|
-
<% end %>
|
6
|
-
|
7
|
-
<% layout.with_header do %>
|
8
|
-
<%= render("resource_header", sidebar_toggle: "#{current_engine.dom_id}-drawer") %>
|
9
|
-
<% end %>
|
10
|
-
|
11
|
-
<% layout.with_sidebar do %>
|
12
|
-
<%= render("resource_sidebar") %>
|
13
|
-
<% end %>
|
14
|
-
|
1
|
+
<%= render Plutonium::UI::Layout::ResourceLayout.new do |layout| %>
|
15
2
|
<%= yield %>
|
16
3
|
<% end %>
|
@@ -1,19 +1,3 @@
|
|
1
|
-
<%=
|
2
|
-
|
3
|
-
main_classname: "flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0" do |layout| %>
|
4
|
-
<% layout.with_favicon do %>
|
5
|
-
<%= resource_favicon_tag %>
|
6
|
-
<% end %>
|
7
|
-
<%= link_to root_path, class: "flex items-center text-2xl font-semibold text-gray-900 dark:text-white" do %>
|
8
|
-
<%= resource_logo_tag classname: "w-24 h-24 mr-2" %>
|
9
|
-
<% end %>
|
10
|
-
<div class="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
|
11
|
-
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
|
12
|
-
<%= yield %>
|
13
|
-
</div>
|
14
|
-
</div>
|
15
|
-
<div class="mt-4 flex items-center font-medium text-secondary-600 dark:text-secondary-400 hover:underline">
|
16
|
-
<%= render_icon "outline/home" %>
|
17
|
-
<%= link_to "Home", root_path, class: "font-medium text-secondary-600 dark:text-secondary-400" %>
|
18
|
-
</div>
|
1
|
+
<%= render Plutonium::UI::Layout::RodauthLayout.new do |layout| %>
|
2
|
+
<%= yield %>
|
19
3
|
<% end %>
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
<%= render Plutonium::UI::Layout::Header.new do |header| %>
|
2
|
+
<% header.with_brand_logo do %>
|
3
|
+
<%= resource_logo_tag(classname: "mr-3 h-10") %>
|
4
|
+
<% end %>
|
2
5
|
|
3
|
-
<%= render_component :resource_header, sidebar_toggle: do |header| %>
|
4
6
|
<% header.with_action do %>
|
5
7
|
<%=
|
6
8
|
render_component :nav_grid_menu, label: "Apps" do |menu|
|
@@ -1,29 +1,23 @@
|
|
1
1
|
after_bundle do
|
2
2
|
# We just installed Rails, let's create a commit
|
3
|
-
git
|
4
|
-
git commit: %( -m 'initial commit' )
|
3
|
+
git(add: ".") && git(commit: %( -m 'initial commit' ))
|
5
4
|
|
6
5
|
# Run the base install
|
7
6
|
rails_command "app:template LOCATION=https://radioactive-labs.github.io/plutonium-core/templates/base.rb"
|
8
7
|
|
9
8
|
# Add development tools
|
10
9
|
generate "pu:gem:dotenv"
|
11
|
-
git
|
12
|
-
git commit: %( -m 'add dotenv' )
|
10
|
+
git(add: ".") && git(commit: %( -m 'add dotenv' ))
|
13
11
|
|
14
12
|
generate "pu:gem:annotate"
|
15
|
-
git
|
16
|
-
git commit: %( -m 'add annotate' )
|
13
|
+
git(add: ".") && git(commit: %( -m 'add annotate' ))
|
17
14
|
|
18
15
|
generate "pu:gem:standard"
|
19
|
-
git
|
20
|
-
git commit: %( -m 'add standardrb' )
|
16
|
+
git(add: ".") && git(commit: %( -m 'add standardrb' ))
|
21
17
|
|
22
18
|
generate "pu:gem:letter_opener"
|
23
|
-
git
|
24
|
-
git commit: %( -m 'add letter_opener' )
|
19
|
+
git(add: ".") && git(commit: %( -m 'add letter_opener' ))
|
25
20
|
|
26
21
|
generate "pu:core:assets"
|
27
|
-
git
|
28
|
-
git commit: %( -m 'integrate assets' )
|
22
|
+
git(add: ".") && git(commit: %( -m 'integrate assets' ))
|
29
23
|
end
|
@@ -10,10 +10,23 @@ module Pu
|
|
10
10
|
desc "Set up standardrb"
|
11
11
|
|
12
12
|
def start
|
13
|
-
|
13
|
+
add_standard
|
14
|
+
remove_rubocop_rails_omakase
|
14
15
|
rescue => e
|
15
16
|
exception "#{self.class} failed:", e
|
16
17
|
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def add_standard
|
22
|
+
bundle "standard", version: ">= 1.35.1", group: :development
|
23
|
+
end
|
24
|
+
|
25
|
+
def remove_rubocop_rails_omakase
|
26
|
+
run "bundle remove rubocop-rails-omakase"
|
27
|
+
gsub_file "Gemfile", /\n.*\n.*# Omakase Ruby styling.*/, ""
|
28
|
+
remove_file ".rubocop.yml"
|
29
|
+
end
|
17
30
|
end
|
18
31
|
end
|
19
32
|
end
|
@@ -500,7 +500,7 @@ module PlutoniumGenerators
|
|
500
500
|
def hash_to_cli_options(hash)
|
501
501
|
hash.map do |key, value|
|
502
502
|
formatted_value = value.is_a?(Array) ? value.join(",") : value
|
503
|
-
"--#{key.to_s.tr("_", "-")}
|
503
|
+
"--#{key.to_s.tr("_", "-")}=\"#{formatted_value}\""
|
504
504
|
end.join(" ")
|
505
505
|
end
|
506
506
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
# Component for selecting color mode (light/dark/system)
|
6
|
+
# @example Basic usage
|
7
|
+
# render ColorModeSelector.new
|
8
|
+
class ColorModeSelector < Plutonium::UI::Component::Base
|
9
|
+
# Common CSS classes used across the component
|
10
|
+
COMMON_CLASSES = {
|
11
|
+
button: "w-full block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600",
|
12
|
+
icon: "w-6 h-6 text-gray-800 dark:text-white",
|
13
|
+
trigger: "inline-flex justify-center p-2 text-gray-500 rounded cursor-pointer dark:hover:text-white dark:text-gray-200 hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600",
|
14
|
+
dropdown: "hidden z-50 my-4 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700"
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
# Available color modes with their associated icons and actions
|
18
|
+
COLOR_MODES = [
|
19
|
+
{label: "Light", icon: Phlex::TablerIcons::Sun, action: "setLightColorMode"},
|
20
|
+
{label: "Dark", icon: Phlex::TablerIcons::Moon, action: "setDarkColorMode"},
|
21
|
+
{label: "System", icon: Phlex::TablerIcons::DeviceDesktop, action: "setSystemColorMode"}
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
# Renders the color mode selector
|
25
|
+
# @return [void]
|
26
|
+
def template
|
27
|
+
div(data_controller: "resource-drop-down") do
|
28
|
+
render_dropdown_trigger
|
29
|
+
render_dropdown_menu
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# @private
|
36
|
+
def render_dropdown_trigger
|
37
|
+
button(
|
38
|
+
type: "button",
|
39
|
+
data_resource_drop_down_target: "trigger",
|
40
|
+
class: COMMON_CLASSES[:trigger]
|
41
|
+
) do
|
42
|
+
render Phlex::TablerIcons::Adjustments.new(class: COMMON_CLASSES[:icon])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @private
|
47
|
+
def render_dropdown_menu
|
48
|
+
div(
|
49
|
+
class: COMMON_CLASSES[:dropdown],
|
50
|
+
data_resource_drop_down_target: "menu"
|
51
|
+
) do
|
52
|
+
render_color_mode_options
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @private
|
57
|
+
def render_color_mode_options
|
58
|
+
ul(class: "py-1", role: "none") do
|
59
|
+
COLOR_MODES.each do |mode|
|
60
|
+
render_color_mode_button(**mode)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @private
|
66
|
+
# @param label [String] The text label for the button
|
67
|
+
# @param icon [Class] The TablerIcon class to render
|
68
|
+
# @param action [String] The color-mode controller action to trigger
|
69
|
+
def render_color_mode_button(label:, icon:, action:)
|
70
|
+
li do
|
71
|
+
button(
|
72
|
+
type: "button",
|
73
|
+
class: COMMON_CLASSES[:button],
|
74
|
+
role: "menuitem",
|
75
|
+
data_action: "click->color-mode##{action}"
|
76
|
+
) do
|
77
|
+
div(class: "flex justify-start") do
|
78
|
+
render icon.new(class: COMMON_CLASSES[:icon])
|
79
|
+
plain " #{label}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -35,6 +35,8 @@ module Plutonium
|
|
35
35
|
def TableInfo(...) = render Plutonium::UI::Table::Components::PagyInfo.new(...)
|
36
36
|
|
37
37
|
def TablePagination(...) = render Plutonium::UI::Table::Components::PagyPagination.new(...)
|
38
|
+
|
39
|
+
def ColorModeSelector(...) = render Plutonium::UI::ColorModeSelector.new(...)
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
@@ -4,8 +4,8 @@ module Plutonium
|
|
4
4
|
class Host < Plutonium::UI::Component::Base
|
5
5
|
include Phlex::Rails::Helpers::TurboFrameTag
|
6
6
|
|
7
|
-
def initialize(src:, loading:,
|
8
|
-
@id = id
|
7
|
+
def initialize(src:, loading:, **attributes)
|
8
|
+
@id = attributes.delete(:id) || SecureRandom.alphanumeric(8, chars: [*"a".."z"])
|
9
9
|
@src = src
|
10
10
|
@loading = loading
|
11
11
|
@attributes = attributes
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Plutonium
|
2
|
+
module UI
|
3
|
+
module Layout
|
4
|
+
class Base < Plutonium::UI::Component::Base
|
5
|
+
include Plutonium::Helpers::AssetsHelper
|
6
|
+
include Phlex::Rails::Helpers::CSPMetaTag
|
7
|
+
include Phlex::Rails::Helpers::CSRFMetaTags
|
8
|
+
include Phlex::Rails::Helpers::FaviconLinkTag
|
9
|
+
include Phlex::Rails::Helpers::StyleSheetLinkTag
|
10
|
+
include Phlex::Rails::Helpers::JavaScriptIncludeTag
|
11
|
+
|
12
|
+
def view_template(&)
|
13
|
+
doctype
|
14
|
+
html(**html_attributes) {
|
15
|
+
render_head
|
16
|
+
render_body(&)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def lang = nil
|
23
|
+
|
24
|
+
def page_title = helpers.controller.instance_variable_get(:@page_title)
|
25
|
+
|
26
|
+
def html_attributes = {lang:, data_controller: "resource-layout color-mode"}
|
27
|
+
|
28
|
+
def body_attributes = {class: "antialiased min-h-screen bg-gray-50 dark:bg-gray-900"}
|
29
|
+
|
30
|
+
def main_attributes = {class: "p-4 min-h-screen"}
|
31
|
+
|
32
|
+
def render_head
|
33
|
+
head {
|
34
|
+
render_title
|
35
|
+
render_metatags
|
36
|
+
render_security_metatags
|
37
|
+
render_turbo_metatags
|
38
|
+
render_font_tags
|
39
|
+
render_favicon_tag
|
40
|
+
render_assets_tags
|
41
|
+
|
42
|
+
# plain assets
|
43
|
+
# plain head
|
44
|
+
# <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.snow.min.css" integrity="sha512-/FHUK/LsH78K9XTqsR9hbzr21J8B8RwHR/r8Jv9fzry6NVAOVIGFKQCNINsbhK7a1xubVu2r5QZcz2T9cKpubw==" crossorigin="anonymous" referrerpolicy="no-referrer" /> '
|
45
|
+
# <script src="https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.min.js" integrity="sha512-P2W2rr8ikUPfa31PLBo5bcBQrsa+TNj8jiKadtaIrHQGMo6hQM6RdPjQYxlNguwHz8AwSQ28VkBK6kHBLgd/8g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
46
|
+
# <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.css" integrity="sha512-GvqWM4KWH8mbgWIyvwdH8HgjUbyZTXrCq0sjGij9fDNiXz3vJoy3jCcAaWNekH2rJe4hXVWCJKN+bEW8V7AAEQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
47
|
+
# <script src="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.js" integrity="sha512-0E8oaoA2v32h26IycsmRDShtQ8kMgD91zWVBxdIvUCjU3xBw81PV61QBsBqNQpWkp/zYJZip8Ag3ifmzz1wCKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def render_body(&)
|
52
|
+
body(**body_attributes) {
|
53
|
+
render_before_main
|
54
|
+
render_main(&)
|
55
|
+
render_after_main
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def render_before_main
|
60
|
+
end
|
61
|
+
|
62
|
+
def render_main(&)
|
63
|
+
main(**main_attributes) {
|
64
|
+
render_flash
|
65
|
+
render_before_content
|
66
|
+
render_content(&)
|
67
|
+
render_after_content
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def render_flash
|
72
|
+
render "flash"
|
73
|
+
end
|
74
|
+
|
75
|
+
def render_before_content
|
76
|
+
end
|
77
|
+
|
78
|
+
def render_after_main
|
79
|
+
end
|
80
|
+
|
81
|
+
def render_content(&)
|
82
|
+
yield if block_given?
|
83
|
+
end
|
84
|
+
|
85
|
+
def render_after_content
|
86
|
+
end
|
87
|
+
|
88
|
+
def render_title
|
89
|
+
title { page_title } if page_title
|
90
|
+
end
|
91
|
+
|
92
|
+
def render_metatags
|
93
|
+
meta(charset: "utf-8")
|
94
|
+
meta(name: "viewport", content: "width=device-width,initial-scale=1")
|
95
|
+
end
|
96
|
+
|
97
|
+
def render_security_metatags
|
98
|
+
csrf_meta_tags
|
99
|
+
csp_meta_tag
|
100
|
+
end
|
101
|
+
|
102
|
+
def render_turbo_metatags
|
103
|
+
meta(name: "turbo-cache-control", content: "no-cache")
|
104
|
+
meta(name: "turbo-refresh-method", content: "morph")
|
105
|
+
meta(name: "turbo-refresh-scroll", content: "preserve")
|
106
|
+
end
|
107
|
+
|
108
|
+
def render_font_tags
|
109
|
+
link(rel: "preconnect", href: "https://fonts.googleapis.com")
|
110
|
+
link(rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: true)
|
111
|
+
link(href: "https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap", rel: "stylesheet")
|
112
|
+
end
|
113
|
+
|
114
|
+
def render_favicon_tag
|
115
|
+
favicon_link_tag(Plutonium.configuration.assets.favicon)
|
116
|
+
end
|
117
|
+
|
118
|
+
def render_assets_tags
|
119
|
+
render_asset_style_tags
|
120
|
+
render_asset_scripts_tags
|
121
|
+
end
|
122
|
+
|
123
|
+
def render_asset_style_tags
|
124
|
+
url = resource_asset_url_for(:css, resource_stylesheet_asset)
|
125
|
+
stylesheet_link_tag(url, "data-turbo-track": "reload")
|
126
|
+
end
|
127
|
+
|
128
|
+
def render_asset_scripts_tags
|
129
|
+
url = resource_asset_url_for(:js, resource_script_asset)
|
130
|
+
javascript_include_tag(url, "data-turbo-track": "reload", type: "module")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require "phlex/slotable"
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Layout
|
6
|
+
# @class Header
|
7
|
+
# A flexible, responsive header component that can include brand information, navigation elements,
|
8
|
+
# and custom actions.
|
9
|
+
#
|
10
|
+
# @example Basic usage with brand name
|
11
|
+
# Header.new do |header|
|
12
|
+
# header.with_brand_name { "My App" }
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @example With brand logo and actions
|
16
|
+
# Header.new do |header|
|
17
|
+
# header.with_brand_logo { image_tag("logo.svg") }
|
18
|
+
# header.with_action { button "Settings" }
|
19
|
+
# end
|
20
|
+
class Header < Base
|
21
|
+
include Phlex::Slotable
|
22
|
+
include Phlex::Rails::Helpers::Routes
|
23
|
+
|
24
|
+
# @!method brand_name
|
25
|
+
# Defines the slot for the brand name content
|
26
|
+
# @yield The block containing the brand name content
|
27
|
+
slot :brand_name
|
28
|
+
|
29
|
+
# @!method brand_logo
|
30
|
+
# Defines the slot for the brand logo content
|
31
|
+
# @yield The block containing the brand logo content
|
32
|
+
slot :brand_logo
|
33
|
+
|
34
|
+
# @!method action
|
35
|
+
# Defines multiple slots for header actions (e.g., buttons, dropdowns)
|
36
|
+
# @yield The block containing each action's content
|
37
|
+
slot :action, collection: true
|
38
|
+
|
39
|
+
# Renders the header component with all its configured elements
|
40
|
+
# @note The header is fixed positioned and includes responsive design considerations
|
41
|
+
# @return [void]
|
42
|
+
def view_template
|
43
|
+
nav(
|
44
|
+
class: "bg-white border-b border-gray-200 px-4 py-2.5 dark:bg-gray-800 dark:border-gray-700 fixed left-0 right-0 top-0 z-50",
|
45
|
+
data: {
|
46
|
+
controller: :header,
|
47
|
+
header_sidebar_outlet: "#sidebar-navigation"
|
48
|
+
}
|
49
|
+
) do
|
50
|
+
div(class: "flex flex-wrap justify-between items-center") do
|
51
|
+
render_brand_section
|
52
|
+
render_actions if action_slots?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Renders the left section of the header including sidebar toggle, brand elements,
|
60
|
+
# and any yielded content
|
61
|
+
# @private
|
62
|
+
def render_brand_section
|
63
|
+
div(class: "flex justify-start items-center") do
|
64
|
+
render_sidebar_toggle
|
65
|
+
render_brand if brand_name_slot? || brand_logo_slot?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Renders the sidebar toggle button for mobile views
|
70
|
+
# @private
|
71
|
+
def render_sidebar_toggle
|
72
|
+
button(
|
73
|
+
data_action: "header#toggleDrawer",
|
74
|
+
aria_controls: "#sidebar-navigation",
|
75
|
+
class: %(p-2 mr-2 text-gray-600 rounded-lg cursor-pointer lg:hidden hover:text-gray-900
|
76
|
+
hover:bg-gray-100 focus:bg-gray-100 dark:focus:bg-gray-700 focus:ring-2
|
77
|
+
focus:ring-gray-100 dark:focus:ring-gray-700 dark:text-gray-200
|
78
|
+
dark:hover:bg-gray-700 dark:hover:text-white)
|
79
|
+
) do
|
80
|
+
render_toggle_icons
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Renders the brand section with logo and/or name
|
85
|
+
# @private
|
86
|
+
def render_brand
|
87
|
+
a(href: root_path, class: "flex items-center justify-between md:min-w-60") do
|
88
|
+
render brand_logo_slot if brand_logo_slot?
|
89
|
+
if brand_name_slot?
|
90
|
+
span(class: "self-center text-2xl font-semibold whitespace-nowrap dark:text-white hidden xs:block") do
|
91
|
+
render brand_name_slot
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Renders the toggle icons for the sidebar button
|
98
|
+
# @private
|
99
|
+
def render_toggle_icons
|
100
|
+
span(data_header_target: "open") do
|
101
|
+
render Phlex::TablerIcons::Menu.new(class: "w-6 h-6")
|
102
|
+
end
|
103
|
+
|
104
|
+
span(data_header_target: "close", class: "hidden", aria_hidden: "true") do
|
105
|
+
render Phlex::TablerIcons::X.new(class: "w-6 h-6")
|
106
|
+
end
|
107
|
+
|
108
|
+
span(class: "sr-only") { "Toggle sidebar" }
|
109
|
+
end
|
110
|
+
|
111
|
+
# Renders the action buttons section
|
112
|
+
# @private
|
113
|
+
def render_actions
|
114
|
+
div(class: "flex items-center lg:order-2") do
|
115
|
+
action_slots.each { |action| render action }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Plutonium
|
2
|
+
module UI
|
3
|
+
module Layout
|
4
|
+
class ResourceLayout < Base
|
5
|
+
private
|
6
|
+
|
7
|
+
def main_attributes = mix(super, {
|
8
|
+
class: "pt-20 lg:ml-64"
|
9
|
+
})
|
10
|
+
|
11
|
+
def page_title
|
12
|
+
helpers.make_page_title(
|
13
|
+
helpers.controller.instance_variable_get(:@page_title)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_before_main
|
18
|
+
super
|
19
|
+
|
20
|
+
render("resource_header")
|
21
|
+
render("resource_sidebar")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Plutonium
|
2
|
+
module UI
|
3
|
+
module Layout
|
4
|
+
class RodauthLayout < Base
|
5
|
+
include Phlex::Rails::Helpers::LinkTo
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def page_title
|
10
|
+
helpers.controller.instance_variable_get(:@page_title)
|
11
|
+
end
|
12
|
+
|
13
|
+
def main_attributes = mix(super, {
|
14
|
+
class: "flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0"
|
15
|
+
})
|
16
|
+
|
17
|
+
def render_content(&)
|
18
|
+
link_to root_path, class: "flex items-center text-2xl font-semibold text-gray-900 dark:text-white" do
|
19
|
+
helpers.resource_logo_tag classname: "w-24 h-24 mr-2"
|
20
|
+
end
|
21
|
+
|
22
|
+
div(class: "w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700") {
|
23
|
+
div(class: "p-6 space-y-4 md:space-y-6 sm:p-8", &)
|
24
|
+
}
|
25
|
+
|
26
|
+
div(class: "mt-4 flex items-center font-medium text-secondary-600 dark:text-secondary-400 hover:underline") {
|
27
|
+
render Phlex::TablerIcons::Home2.new
|
28
|
+
link_to "Home", root_path, class: "font-medium text-secondary-600 dark:text-secondary-400"
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Layout
|
6
|
+
# A sidebar navigation component that provides a responsive layout with light/dark mode toggle
|
7
|
+
# @example Basic usage with navigation content
|
8
|
+
# render Sidebar.new do
|
9
|
+
# ...
|
10
|
+
# end
|
11
|
+
class Sidebar < Base
|
12
|
+
include Phlex::Slotable
|
13
|
+
|
14
|
+
# Renders the sidebar navigation template
|
15
|
+
# @yield [void] The block containing sidebar content
|
16
|
+
# @return [void]
|
17
|
+
def view_template(&)
|
18
|
+
render_sidebar_container do
|
19
|
+
render_content(&) if block_given?
|
20
|
+
render_color_mode_controls
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# @private
|
27
|
+
def render_sidebar_container(&)
|
28
|
+
aside(
|
29
|
+
id: "sidebar-navigation",
|
30
|
+
aria: {label: "Sidebar Navigation"},
|
31
|
+
data: {controller: :sidebar},
|
32
|
+
class: "fixed top-0 left-0 z-40 w-64 h-screen pt-14 transition-transform -translate-x-full lg:translate-x-0",
|
33
|
+
&
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @private
|
38
|
+
def render_content(&)
|
39
|
+
div(
|
40
|
+
id: "sidebar-navigation-content",
|
41
|
+
data: {turbo_permanent: true},
|
42
|
+
class: "overflow-y-auto py-5 px-3 h-full bg-white border-r border-gray-200 dark:bg-gray-800 dark:border-gray-700",
|
43
|
+
&
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @private
|
48
|
+
def render_color_mode_controls
|
49
|
+
div(class: "hidden absolute bottom-0 left-0 justify-center p-4 space-x-4 w-full lg:flex bg-white dark:bg-gray-800 z-20 border-r border-gray-200 dark:border-gray-700") do
|
50
|
+
render ColorModeSelector.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -94,7 +94,7 @@ module Plutonium
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def render_footer
|
97
|
-
div(class: "sticky dyna:static bottom-[-2px] mt-1 p-4 pb-6 w-full z-
|
97
|
+
div(class: "lg:sticky lg:dyna:static bottom-[-2px] mt-1 p-4 pb-6 w-full z-30 bg-gray-50 dark:bg-gray-900") {
|
98
98
|
TableInfo(pagy_instance)
|
99
99
|
TablePagination(pagy_instance)
|
100
100
|
}
|