plutonium 0.15.12 → 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/generators/pu/rodauth/concerns/configuration.rb +1 -0
 - 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 +2 -0
 - data/lib/rodauth/features/case_insensitive_login.rb +21 -0
 - data/lib/rodauth/plugins.rb +1 -0
 - 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 +26 -3
 
| 
         @@ -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
         
     |