voidable-hotwire 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/app/views/devise/confirmations/new.html.erb +22 -0
  3. data/app/views/devise/passwords/edit.html.erb +27 -0
  4. data/app/views/devise/passwords/new.html.erb +21 -0
  5. data/app/views/devise/registrations/edit.html.erb +50 -0
  6. data/app/views/devise/registrations/new.html.erb +29 -0
  7. data/app/views/devise/sessions/new.html.erb +35 -0
  8. data/app/views/devise/shared/_error_messages.html.erb +10 -0
  9. data/app/views/devise/shared/_links.html.erb +33 -0
  10. data/app/views/devise/unlocks/new.html.erb +22 -0
  11. data/lib/generators/voidable/devise_views/devise_views_generator.rb +15 -0
  12. data/lib/generators/voidable/install/install_generator.rb +112 -0
  13. data/lib/generators/voidable/install/templates/_settings_menu.html.erb +31 -0
  14. data/lib/generators/voidable/install/templates/application_sidebar.html.erb.tt +86 -0
  15. data/lib/generators/voidable/install/templates/application_topbar.html.erb.tt +69 -0
  16. data/lib/generators/voidable/install/templates/initializer.rb.tt +2 -0
  17. data/lib/generators/voidable/install/templates/settings_controller.rb.tt +7 -0
  18. data/lib/generators/voidable/install/templates/sidebar.html.erb.tt +86 -0
  19. data/lib/generators/voidable/install/templates/topbar.html.erb.tt +69 -0
  20. data/lib/generators/voidable/install/templates/voidable-devise.css +37 -0
  21. data/lib/generators/voidable/install/templates/voidable-layout-sidebar.css +460 -0
  22. data/lib/generators/voidable/install/templates/voidable-layout-topbar.css +361 -0
  23. data/lib/generators/voidable/install/templates/voidable-layout.js +41 -0
  24. data/lib/templates/erb/scaffold/_form.html.erb.tt +80 -0
  25. data/lib/templates/erb/scaffold/edit.html.erb.tt +18 -0
  26. data/lib/templates/erb/scaffold/index.html.erb.tt +65 -0
  27. data/lib/templates/erb/scaffold/new.html.erb.tt +3 -0
  28. data/lib/templates/erb/scaffold/partial.html.erb.tt +17 -0
  29. data/lib/templates/erb/scaffold/show.html.erb.tt +56 -0
  30. data/lib/voidable/hotwire/engine.rb +5 -0
  31. data/lib/voidable/hotwire/version.rb +1 -1
  32. metadata +29 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '029e2a1953a40d0450185108452061a44fc481aa8aaae0c7b28782e08ca7fc09'
4
- data.tar.gz: 259637c4581390e625f31bc5308e108a7fc133b28a28f46eff1905efcea65a10
3
+ metadata.gz: 9abae5966616acf1bb8a037ed7eaae7433e5a0c8ac72b47896739281ba0ecd10
4
+ data.tar.gz: 8d570608eeb27e61ffb72429b258040fc7b9a5855457208693541a5312ae34e7
5
5
  SHA512:
6
- metadata.gz: 5515541709822ddda8bb0b297f3a54b1fd4d2be122228db8f01c73abff7025b596e4e734f16b7fbea8eb51b43f7c3e87b45be43981658558fff0d5ba8a1e74c1
7
- data.tar.gz: dce17dfbbfa35cbddefe72c203a786cd791cf8b0e15711d418b16d53d609e448360f3024a0abeb1e7884fec9198762339b70f536eedbb4fd126fe0f5ec3c9e41
6
+ metadata.gz: f20748f43e723982c55976bc44df0814b5267d8650074e4c3376d6eeac1a22a9898a2a90590e841e8c78878a85b4f17aae215978877a03b56f8f62133dbac2e8
7
+ data.tar.gz: 7e29e9da0a0736d47e230319b523a704835c56ff4df2e4d47169fb1e309a0b8235508c06747119dd8721b32d918e3d01f4d3143f47fd6b5fbd1a2641a6946967
@@ -0,0 +1,22 @@
1
+ <div class="devise-container">
2
+ <void-card>
3
+ <div class="devise-card-body">
4
+ <h1 class="devise-heading">Resend confirmation instructions</h1>
5
+ <p class="devise-description">Enter the email address you used to register and we'll send you a new confirmation link.</p>
6
+
7
+ <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: "devise-form" }) do |f| %>
8
+ <%= render "devise/shared/error_messages", resource: resource %>
9
+
10
+ <void-field label="Email">
11
+ <void-input type="email" name="<%= resource_name %>[email]" value="<%= (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>" autofocus autocomplete="email" placeholder="you@example.com"></void-input>
12
+ </void-field>
13
+
14
+ <div class="devise-submit">
15
+ <void-button type="submit" color="success" class="devise-btn-full">Resend confirmation instructions</void-button>
16
+ </div>
17
+ <% end %>
18
+
19
+ <%= render "devise/shared/links" %>
20
+ </div>
21
+ </void-card>
22
+ </div>
@@ -0,0 +1,27 @@
1
+ <div class="devise-container">
2
+ <void-card>
3
+ <div class="devise-card-body">
4
+ <h1 class="devise-heading">Change your password</h1>
5
+
6
+ <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: "devise-form" }) do |f| %>
7
+ <%= render "devise/shared/error_messages", resource: resource %>
8
+
9
+ <%= f.hidden_field :reset_password_token %>
10
+
11
+ <void-field label="New password" helper="6 characters minimum">
12
+ <void-action-input type="password" name="<%= resource_name %>[password]" icon="eye" action-label="Toggle visibility" autofocus autocomplete="new-password"></void-action-input>
13
+ </void-field>
14
+
15
+ <void-field label="Confirm new password">
16
+ <void-action-input type="password" name="<%= resource_name %>[password_confirmation]" icon="eye" action-label="Toggle visibility" autocomplete="new-password"></void-action-input>
17
+ </void-field>
18
+
19
+ <div class="devise-submit">
20
+ <void-button type="submit" color="success" class="devise-btn-full">Change my password</void-button>
21
+ </div>
22
+ <% end %>
23
+
24
+ <%= render "devise/shared/links" %>
25
+ </div>
26
+ </void-card>
27
+ </div>
@@ -0,0 +1,21 @@
1
+ <div class="devise-container">
2
+ <void-card>
3
+ <div class="devise-card-body">
4
+ <h1 class="devise-heading">Forgot your password?</h1>
5
+
6
+ <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: "devise-form" }) do |f| %>
7
+ <%= render "devise/shared/error_messages", resource: resource %>
8
+
9
+ <void-field label="Email">
10
+ <void-input type="email" name="<%= resource_name %>[email]" value="<%= resource.email %>" autofocus autocomplete="email" placeholder="you@example.com"></void-input>
11
+ </void-field>
12
+
13
+ <div class="devise-submit">
14
+ <void-button type="submit" color="success" class="devise-btn-full">Send reset instructions</void-button>
15
+ </div>
16
+ <% end %>
17
+
18
+ <%= render "devise/shared/links" %>
19
+ </div>
20
+ </void-card>
21
+ </div>
@@ -0,0 +1,50 @@
1
+ <div class="devise-container devise-container--wide">
2
+ <void-card>
3
+ <div class="devise-card-body">
4
+ <h1 class="devise-heading">Account settings</h1>
5
+
6
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: "devise-form" }) do |f| %>
7
+ <%= render "devise/shared/error_messages", resource: resource %>
8
+
9
+ <void-field label="Email">
10
+ <void-input type="email" name="<%= resource_name %>[email]" value="<%= resource.email %>" autofocus autocomplete="email"></void-input>
11
+ </void-field>
12
+
13
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
14
+ <void-alert>Currently waiting confirmation for: <%= resource.unconfirmed_email %></void-alert>
15
+ <% end %>
16
+
17
+ <void-field label="New password" helper="Leave blank if you don't want to change it">
18
+ <void-action-input type="password" name="<%= resource_name %>[password]" icon="eye" action-label="Toggle visibility" autocomplete="new-password"></void-action-input>
19
+ </void-field>
20
+
21
+ <void-field label="Confirm new password">
22
+ <void-action-input type="password" name="<%= resource_name %>[password_confirmation]" icon="eye" action-label="Toggle visibility" autocomplete="new-password"></void-action-input>
23
+ </void-field>
24
+
25
+ <void-field label="Current password" helper="We need your current password to confirm changes">
26
+ <void-action-input type="password" name="<%= resource_name %>[current_password]" icon="eye" action-label="Toggle visibility" autocomplete="current-password"></void-action-input>
27
+ </void-field>
28
+
29
+ <div class="devise-submit">
30
+ <void-button type="submit" color="success" class="devise-btn-full">Update</void-button>
31
+ </div>
32
+ <% end %>
33
+ </div>
34
+ </void-card>
35
+
36
+ <div class="danger-zone">
37
+ <p class="devise-danger-label">Danger zone</p>
38
+ <void-button variant="outline" color="error" size="sm" onclick="document.getElementById('delete-dialog').open = true">Delete my account</void-button>
39
+ </div>
40
+
41
+ <void-dialog id="delete-dialog" heading="Delete account" size="sm">
42
+ <p class="danger-zone-description">
43
+ Are you sure you want to delete your account? This action cannot be undone.
44
+ </p>
45
+ <div class="action-bar">
46
+ <void-button variant="ghost" size="sm" onclick="document.getElementById('delete-dialog').open = false">Cancel</void-button>
47
+ <void-button color="error" size="sm"><a href="<%= registration_path(resource_name) %>" data-turbo-method="delete">Delete</a></void-button>
48
+ </div>
49
+ </void-dialog>
50
+ </div>
@@ -0,0 +1,29 @@
1
+ <div class="devise-container">
2
+ <void-card>
3
+ <div class="devise-card-body">
4
+ <h1 class="devise-heading">Sign up</h1>
5
+
6
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "devise-form" }) do |f| %>
7
+ <%= render "devise/shared/error_messages", resource: resource %>
8
+
9
+ <void-field label="Email">
10
+ <void-input type="email" name="<%= resource_name %>[email]" value="<%= resource.email %>" autofocus autocomplete="email" placeholder="you@example.com"></void-input>
11
+ </void-field>
12
+
13
+ <void-field label="Password" helper="<%= @minimum_password_length %> characters minimum">
14
+ <void-action-input type="password" name="<%= resource_name %>[password]" icon="eye" action-label="Toggle visibility" autocomplete="new-password" minlength="<%= @minimum_password_length %>"></void-action-input>
15
+ </void-field>
16
+
17
+ <void-field label="Password confirmation">
18
+ <void-action-input type="password" name="<%= resource_name %>[password_confirmation]" icon="eye" action-label="Toggle visibility" autocomplete="new-password"></void-action-input>
19
+ </void-field>
20
+
21
+ <div class="devise-submit">
22
+ <void-button type="submit" color="success" class="devise-btn-full">Sign up</void-button>
23
+ </div>
24
+ <% end %>
25
+
26
+ <%= render "devise/shared/links" %>
27
+ </div>
28
+ </void-card>
29
+ </div>
@@ -0,0 +1,35 @@
1
+ <div class="devise-container">
2
+ <void-card>
3
+ <div class="devise-card-body">
4
+ <h1 class="devise-heading">Sign in</h1>
5
+
6
+ <%= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "devise-form" }) do |f| %>
7
+ <%= render "devise/shared/error_messages", resource: resource %>
8
+
9
+ <void-field label="Email">
10
+ <void-input type="email" name="<%= resource_name %>[email]" value="<%= resource.email %>" autofocus autocomplete="email" placeholder="you@example.com"></void-input>
11
+ </void-field>
12
+
13
+ <void-field label="Password">
14
+ <void-action-input type="password" name="<%= resource_name %>[password]" icon="eye" action-label="Toggle visibility" autocomplete="current-password"></void-action-input>
15
+ </void-field>
16
+
17
+ <% if devise_mapping.rememberable? %>
18
+ <void-field label="Remember me">
19
+ <void-switch name="<%= resource_name %>[remember_me]"></void-switch>
20
+ </void-field>
21
+ <% end %>
22
+
23
+ <div class="devise-submit">
24
+ <void-button type="submit" color="success" class="devise-btn-full">Sign in</void-button>
25
+ </div>
26
+
27
+ <% if devise_mapping.recoverable? %>
28
+ <void-button variant="ghost" class="devise-btn-full"><a href="<%= new_password_path(resource_name) %>">Forgot your password?</a></void-button>
29
+ <% end %>
30
+ <% end %>
31
+
32
+ <%= render "devise/shared/links" %>
33
+ </div>
34
+ </void-card>
35
+ </div>
@@ -0,0 +1,10 @@
1
+ <% if resource.errors.any? %>
2
+ <void-alert color="error" data-turbo-temporary>
3
+ <%= pluralize(resource.errors.count, "error") %> prohibited this operation:
4
+ <ul class="devise-error-list">
5
+ <% resource.errors.full_messages.each do |message| %>
6
+ <li><%= message %></li>
7
+ <% end %>
8
+ </ul>
9
+ </void-alert>
10
+ <% end %>
@@ -0,0 +1,33 @@
1
+ <div class="devise-divider">
2
+ <div class="devise-divider-line"></div>
3
+ <span class="devise-divider-text">or</span>
4
+ <div class="devise-divider-line"></div>
5
+ </div>
6
+
7
+ <div class="devise-links">
8
+ <%- if controller_name != 'sessions' %>
9
+ <void-button variant="ghost" class="devise-btn-full"><a href="<%= new_session_path(resource_name) %>">Sign in</a></void-button>
10
+ <% end %>
11
+
12
+ <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
13
+ <void-button variant="ghost" color="info" class="devise-btn-full"><a href="<%= new_registration_path(resource_name) %>">Sign up</a></void-button>
14
+ <% end %>
15
+
16
+ <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' && controller_name != 'sessions' %>
17
+ <void-button variant="ghost" class="devise-btn-full"><a href="<%= new_password_path(resource_name) %>">Forgot your password?</a></void-button>
18
+ <% end %>
19
+
20
+ <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
21
+ <void-button variant="ghost" class="devise-btn-full"><a href="<%= new_confirmation_path(resource_name) %>">Didn't receive confirmation instructions?</a></void-button>
22
+ <% end %>
23
+
24
+ <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
25
+ <void-button variant="ghost" class="devise-btn-full"><a href="<%= new_unlock_path(resource_name) %>">Didn't receive unlock instructions?</a></void-button>
26
+ <% end %>
27
+
28
+ <%- if devise_mapping.omniauthable? %>
29
+ <%- resource_class.omniauth_providers.each do |provider| %>
30
+ <%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false } %>
31
+ <% end %>
32
+ <% end %>
33
+ </div>
@@ -0,0 +1,22 @@
1
+ <div class="devise-container">
2
+ <void-card>
3
+ <div class="devise-card-body">
4
+ <h1 class="devise-heading">Resend unlock instructions</h1>
5
+ <p class="devise-description">Enter your email address and we'll send you instructions to unlock your account.</p>
6
+
7
+ <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: "devise-form" }) do |f| %>
8
+ <%= render "devise/shared/error_messages", resource: resource %>
9
+
10
+ <void-field label="Email">
11
+ <void-input type="email" name="<%= resource_name %>[email]" value="<%= resource.email %>" autofocus autocomplete="email" placeholder="you@example.com"></void-input>
12
+ </void-field>
13
+
14
+ <div class="devise-submit">
15
+ <void-button type="submit" color="success" class="devise-btn-full">Resend unlock instructions</void-button>
16
+ </div>
17
+ <% end %>
18
+
19
+ <%= render "devise/shared/links" %>
20
+ </div>
21
+ </void-card>
22
+ </div>
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voidable
4
+ module Generators
5
+ class DeviseViewsGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("../../../../app/views/devise", __dir__)
7
+
8
+ desc "Copy Voidable-styled Devise views into your application"
9
+
10
+ def copy_views
11
+ directory ".", "app/views/devise"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,112 @@
1
+ module Voidable
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("templates", __dir__)
5
+ desc "Install Voidable UI into your Rails application"
6
+
7
+ class_option :layout, type: :string, default: "topbar", enum: ["topbar", "sidebar", "switching"],
8
+ desc: "Application layout style (topbar, sidebar, or switching)"
9
+
10
+ def create_initializer
11
+ template "initializer.rb.tt", "config/initializers/voidable.rb"
12
+ end
13
+
14
+ def pin_packages
15
+ importmap = "config/importmap.rb"
16
+ return unless File.exist?(importmap)
17
+ return if File.read(importmap).include?("@voidable/ui")
18
+ append_to_file importmap, <<~RUBY
19
+
20
+ # Voidable UI
21
+ pin "@voidable/ui", to: "https://ga.jspm.io/npm:@voidable/ui@0.0.1/dist/ui.js"
22
+ pin "@voidable/ui-hotwire", to: "https://ga.jspm.io/npm:@voidable/ui-hotwire@0.0.1/dist/hotwire.js"
23
+ RUBY
24
+ say "Pinned @voidable/ui and @voidable/ui-hotwire in importmap", :green
25
+ end
26
+
27
+ def create_layout
28
+ copy_file "_settings_menu.html.erb", "app/views/layouts/_settings_menu.html.erb"
29
+
30
+ case options[:layout]
31
+ when "sidebar"
32
+ template "application_sidebar.html.erb.tt", "app/views/layouts/application.html.erb", force: true
33
+ when "switching"
34
+ template "application_topbar.html.erb.tt", "app/views/layouts/application.html.erb", force: true
35
+ template "topbar.html.erb.tt", "app/views/layouts/topbar.html.erb"
36
+ template "sidebar.html.erb.tt", "app/views/layouts/sidebar.html.erb"
37
+ else
38
+ template "application_topbar.html.erb.tt", "app/views/layouts/application.html.erb", force: true
39
+ end
40
+ say "Created Voidable application layout (#{options[:layout]})", :green
41
+ end
42
+
43
+ def create_layout_assets
44
+ copy_file "voidable-layout.js", "app/assets/javascripts/voidable-layout.js"
45
+ copy_file "voidable-devise.css", "app/assets/stylesheets/voidable-devise.css"
46
+
47
+ assets_init = "config/initializers/assets.rb"
48
+ if File.exist?(assets_init) && !File.read(assets_init).include?("javascripts")
49
+ append_to_file assets_init, <<~RUBY
50
+
51
+ # Voidable layout scripts
52
+ Rails.application.config.assets.paths << Rails.root.join("app/assets/javascripts")
53
+ RUBY
54
+ end
55
+
56
+ case options[:layout]
57
+ when "sidebar"
58
+ copy_file "voidable-layout-sidebar.css", "app/assets/stylesheets/voidable-layout.css", force: true
59
+ when "switching"
60
+ copy_file "voidable-layout-topbar.css", "app/assets/stylesheets/voidable-layout-topbar.css"
61
+ copy_file "voidable-layout-sidebar.css", "app/assets/stylesheets/voidable-layout-sidebar.css"
62
+ else
63
+ copy_file "voidable-layout-topbar.css", "app/assets/stylesheets/voidable-layout.css", force: true
64
+ end
65
+ say "Created Voidable layout assets", :green
66
+ end
67
+
68
+ def setup_layout_switching
69
+ return unless options[:layout] == "switching"
70
+
71
+ template "settings_controller.rb.tt", "app/controllers/settings_controller.rb"
72
+
73
+ route 'post "toggle_layout", to: "settings#toggle_layout"'
74
+
75
+ inject_into_class "app/controllers/application_controller.rb", "ApplicationController", <<-RUBY
76
+
77
+ layout :resolve_layout
78
+
79
+ private
80
+
81
+ def resolve_layout
82
+ cookies[:layout] || "topbar"
83
+ end
84
+ RUBY
85
+
86
+ say "Configured layout switching (topbar/sidebar toggle)", :green
87
+ end
88
+
89
+ def add_js_import
90
+ js_entrypoint = "app/javascript/application.js"
91
+ return unless File.exist?(js_entrypoint)
92
+ return if File.read(js_entrypoint).include?("@voidable/ui")
93
+ prepend_to_file js_entrypoint, "import \"@voidable/ui\";\nimport \"@voidable/ui-hotwire\";\n\n"
94
+ say "Added Voidable imports to application.js", :green
95
+ end
96
+
97
+ private
98
+
99
+ def app_name
100
+ Rails.application.class.module_parent_name.underscore
101
+ end
102
+
103
+ public
104
+
105
+ def install_devise_views
106
+ return unless defined?(Devise)
107
+ generate "voidable:devise_views"
108
+ say "Installed Voidable-styled Devise views", :green
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,31 @@
1
+ <void-popover position="<%= local_assigns[:popover_position] || 'top' %>" id="settings-popover">
2
+ <button class="settings-btn" aria-label="View Settings" title="View Settings">
3
+ <i class="ti ti-settings"></i>
4
+ </button>
5
+ <div class="settings-menu">
6
+ <% if defined?(Devise) && user_signed_in? %>
7
+ <div class="settings-menu-item">
8
+ <a href="<%= edit_user_registration_path %>" class="settings-menu-action">
9
+ <i class="ti ti-user"></i>
10
+ Profile
11
+ </a>
12
+ </div>
13
+ <% end %>
14
+ <label class="settings-menu-item">
15
+ <i class="ti ti-moon"></i>
16
+ <span>Dark mode</span>
17
+ <void-switch id="theme-toggle" size="sm" aria-label="Toggle theme"></void-switch>
18
+ </label>
19
+ <% if local_assigns[:layout_toggle] %>
20
+ <div class="settings-menu-item">
21
+ <form action="/toggle_layout" method="post" class="settings-menu-form">
22
+ <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
23
+ <button type="submit" class="settings-menu-action">
24
+ <i class="ti ti-<%= local_assigns[:layout_toggle_icon] %>"></i>
25
+ <%= local_assigns[:layout_toggle_label] %>
26
+ </button>
27
+ </form>
28
+ </div>
29
+ <% end %>
30
+ </div>
31
+ </void-popover>
@@ -0,0 +1,86 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title><%%= content_for(:title) || "<%= app_name.titleize %>" %></title>
7
+ <%%= csrf_meta_tags %>
8
+ <%%= csp_meta_tag %>
9
+ <%%= yield :head %>
10
+ <%%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
11
+ <%%= stylesheet_link_tag "voidable-theme", "data-turbo-track": "reload" %>
12
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3.44.0/dist/tabler-icons.min.css">
13
+ <%%= stylesheet_link_tag "voidable-layout", "data-turbo-track": "reload" %>
14
+ <%%= stylesheet_link_tag "voidable-devise", "data-turbo-track": "reload" %>
15
+ <%%= javascript_importmap_tags %>
16
+ </head>
17
+
18
+ <body>
19
+ <div class="app-shell">
20
+ <void-sidebar width="220px">
21
+ <div class="sidebar-header">
22
+ <div class="sidebar-brand-initial"><%= app_name.first.upcase %></div>
23
+ <div class="sidebar-brand-info">
24
+ <div class="sidebar-brand-name"><%= app_name.titleize %></div>
25
+ <div class="sidebar-brand-env">Production</div>
26
+ </div>
27
+ </div>
28
+
29
+ <nav class="sidebar-nav">
30
+ <div class="sidebar-nav-section">
31
+ <div class="sidebar-nav-label">Main</div>
32
+ <div class="sidebar-nav-item">
33
+ <%%= link_to "Dashboard", root_path %>
34
+ </div>
35
+ </div>
36
+
37
+ </nav>
38
+
39
+ <div class="sidebar-footer">
40
+ <div class="sidebar-footer-row">
41
+ <%% if defined?(Devise) && user_signed_in? %>
42
+ <span class="sidebar-user-email"><%%= current_user.email %></span>
43
+ <%% end %>
44
+ <%%= render "layouts/settings_menu", popover_position: "top" %>
45
+ </div>
46
+ <%% if defined?(Devise) && user_signed_in? %>
47
+ <void-button variant="ghost" size="sm" class="sidebar-sign-in"><a href="<%%= destroy_user_session_path %>" data-turbo-method="delete">Sign out</a></void-button>
48
+ <%% elsif defined?(Devise) %>
49
+ <void-button variant="ghost" size="sm" class="sidebar-sign-in"><a href="<%%= new_user_session_path %>">Sign in</a></void-button>
50
+ <%% end %>
51
+ </div>
52
+ </void-sidebar>
53
+
54
+ <main class="app-main">
55
+ <div class="app-content-header">
56
+ <div class="app-content-header-left">
57
+ <span class="app-content-header-breadcrumb"><%%= content_for(:breadcrumb) || content_for(:title) || "<%= app_name.titleize %>" %></span>
58
+ <%% if content_for?(:subtitle) %>
59
+ <span class="app-content-header-subtitle"><%%= content_for(:subtitle) %></span>
60
+ <%% end %>
61
+ </div>
62
+ <div class="app-content-header-right">
63
+ <void-action-input icon="search" placeholder="Search…" size="sm" tooltip-position="bottom" class="app-content-header-search"></void-action-input>
64
+ <%%= content_for(:header_actions) %>
65
+ </div>
66
+ </div>
67
+ <div class="app-content-body">
68
+ <%% if notice.present? %>
69
+ <div class="flash-messages">
70
+ <void-alert color="success" dismissible><%%= notice %></void-alert>
71
+ </div>
72
+ <%% end %>
73
+ <%% if alert.present? %>
74
+ <div class="flash-messages">
75
+ <void-alert color="error" dismissible><%%= alert %></void-alert>
76
+ </div>
77
+ <%% end %>
78
+
79
+ <%%= yield %>
80
+ </div>
81
+ </main>
82
+ </div>
83
+
84
+ <%%= javascript_include_tag "voidable-layout", "data-turbo-track": "reload" %>
85
+ </body>
86
+ </html>
@@ -0,0 +1,69 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title><%%= content_for(:title) || "<%= app_name.titleize %>" %></title>
7
+ <%%= csrf_meta_tags %>
8
+ <%%= csp_meta_tag %>
9
+ <%%= yield :head %>
10
+ <%%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
11
+ <%%= stylesheet_link_tag "voidable-theme", "data-turbo-track": "reload" %>
12
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3.44.0/dist/tabler-icons.min.css">
13
+ <%%= stylesheet_link_tag "voidable-layout", "data-turbo-track": "reload" %>
14
+ <%%= stylesheet_link_tag "voidable-devise", "data-turbo-track": "reload" %>
15
+ <%%= javascript_importmap_tags %>
16
+ </head>
17
+
18
+ <body>
19
+ <nav class="app-nav">
20
+ <div class="app-nav-inner">
21
+ <a href="/" class="app-nav-brand"><%= app_name.titleize %></a>
22
+ <div class="app-nav-right">
23
+ <span class="app-nav-auth">
24
+ <%% if defined?(Devise) && user_signed_in? %>
25
+ <span><%%= current_user.email %></span>
26
+ <%% elsif defined?(Devise) %>
27
+ <a href="<%%= new_user_session_path %>">Sign in</a>
28
+ <%% end %>
29
+ </span>
30
+ <%%= render "layouts/settings_menu", popover_position: "bottom" %>
31
+ <%% if defined?(Devise) && user_signed_in? %>
32
+ <void-button variant="ghost" size="sm"><a href="<%%= destroy_user_session_path %>" data-turbo-method="delete">Sign out</a></void-button>
33
+ <%% end %>
34
+ </div>
35
+ </div>
36
+ </nav>
37
+
38
+ <main class="app-main">
39
+ <div class="app-content-header">
40
+ <div class="app-content-header-left">
41
+ <span class="app-content-header-breadcrumb"><%%= content_for(:breadcrumb) || content_for(:title) || "<%= app_name.titleize %>" %></span>
42
+ <%% if content_for?(:subtitle) %>
43
+ <span class="app-content-header-subtitle"><%%= content_for(:subtitle) %></span>
44
+ <%% end %>
45
+ </div>
46
+ <div class="app-content-header-right">
47
+ <void-action-input icon="search" placeholder="Search..." size="sm" tooltip-position="bottom" class="app-content-header-search"></void-action-input>
48
+ <%%= content_for(:header_actions) %>
49
+ </div>
50
+ </div>
51
+ <div class="app-content-body">
52
+ <%% if notice.present? %>
53
+ <div class="flash-messages">
54
+ <void-alert color="success" dismissible><%%= notice %></void-alert>
55
+ </div>
56
+ <%% end %>
57
+ <%% if alert.present? %>
58
+ <div class="flash-messages">
59
+ <void-alert color="error" dismissible><%%= alert %></void-alert>
60
+ </div>
61
+ <%% end %>
62
+
63
+ <%%= yield %>
64
+ </div>
65
+ </main>
66
+
67
+ <%%= javascript_include_tag "voidable-layout", "data-turbo-track": "reload" %>
68
+ </body>
69
+ </html>
@@ -0,0 +1,2 @@
1
+ # Voidable UI configuration
2
+ # See https://voidable.dev for documentation
@@ -0,0 +1,7 @@
1
+ class SettingsController < ApplicationController
2
+ def toggle_layout
3
+ current = cookies[:layout] || "topbar"
4
+ cookies[:layout] = current == "sidebar" ? "topbar" : "sidebar"
5
+ redirect_back(fallback_location: root_path)
6
+ end
7
+ end