baldur 0.1.6 → 0.1.7
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/README.md +93 -0
- data/TODO.md +1 -0
- data/app/assets/stylesheets/baldur/application/components/auth-page.css +22 -0
- data/app/assets/stylesheets/baldur.css +1 -0
- data/app/helpers/baldur/ui_helper.rb +1 -0
- data/app/helpers/baldur/ui_helper_sidebar.rb +104 -0
- data/app/views/baldur/components/_sidebar.html.erb +158 -0
- data/app/views/baldur/optional/_auth_page.html.erb +3 -3
- data/lib/baldur/version.rb +1 -1
- data/lib/generators/baldur/install/install_generator.rb +1 -0
- data/test/install_generator_test.rb +1 -0
- data/test/sidebar_helper_test.rb +69 -0
- data/test/test_helper.rb +2 -1
- data/test/tmp/install_generator/app/javascript/controllers/mobile_sidebar_controller.js +1 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0dd4cd67a133fc0b38f6f20c08353714a707643cac229c33d1684092d7049ea9
|
|
4
|
+
data.tar.gz: 5ca96debff6a918d861b49869d80de403a70559b3f0858a47eb99bdecf66f91e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2d36011fdd64faddafa75c73f9fbcb4d6da1256b65cff928c177c412d895d1f27a3ed2163231435d20529e2fa1f0a4a9802b385a0a9d05c4af20a2bfb2ff4f7
|
|
7
|
+
data.tar.gz: c854ed1bdcdae904302b3c58aede5444fd34c5025e3224b89166ff16d2aab4509900e80f499d359680f9028cb51a90511af74938ecdd0632479ec19ac285b69a
|
data/README.md
CHANGED
|
@@ -90,6 +90,16 @@ Canonical Ruby internals live under `Baldur::*`, but the default DX is `ui_*` he
|
|
|
90
90
|
|
|
91
91
|
Not every `ui_*` helper is part of the base install. Some helpers belong to optional surfaces and require the matching generator. `ui_auth_page` is part of the default install; `ui_panel_secondary` and google-auth helpers are optional.
|
|
92
92
|
|
|
93
|
+
For authenticated application shells, use `ui_sidebar`. It is part of the base install and includes:
|
|
94
|
+
|
|
95
|
+
- desktop sidebar shell markup
|
|
96
|
+
- mobile sidebar shell markup
|
|
97
|
+
- `sidebar` and `mobile-sidebar` controller wiring
|
|
98
|
+
- default brand rendering from `config.marketing_brand`
|
|
99
|
+
- default nav rendering for primary and secondary links
|
|
100
|
+
|
|
101
|
+
Host apps provide the route arrays, active-state logic, and any optional slot content.
|
|
102
|
+
|
|
93
103
|
Examples:
|
|
94
104
|
|
|
95
105
|
```erb
|
|
@@ -102,8 +112,91 @@ Examples:
|
|
|
102
112
|
<%= ui_panel_secondary(id: "assistant", title: "Assistant", trigger_label: "Open") do %>
|
|
103
113
|
<p>Panel content</p>
|
|
104
114
|
<% end %>
|
|
115
|
+
|
|
116
|
+
<%= ui_sidebar(
|
|
117
|
+
brand_path: root_path,
|
|
118
|
+
primary_links: [
|
|
119
|
+
{ name: "Dashboard", path: root_path, icon: "layout-dashboard", active: current_page?(root_path) }
|
|
120
|
+
]
|
|
121
|
+
) do |_sidebar| %>
|
|
122
|
+
<main class="flex-1 p-6">
|
|
123
|
+
<h1>Dashboard</h1>
|
|
124
|
+
</main>
|
|
125
|
+
<% end %>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`ui_sidebar` accepts these link keys:
|
|
129
|
+
|
|
130
|
+
- `name` required label text
|
|
131
|
+
- `path` required destination
|
|
132
|
+
- `icon` optional Lucide icon name, defaults to `circle`
|
|
133
|
+
- `active` optional boolean for `aria-current="page"`
|
|
134
|
+
- `title` optional hover title
|
|
135
|
+
- `method` optional Rails link method
|
|
136
|
+
- `data` optional data attributes hash
|
|
137
|
+
- `html_options` optional extra HTML attributes hash
|
|
138
|
+
|
|
139
|
+
Default brand behavior:
|
|
140
|
+
|
|
141
|
+
- `brand_name`, `brand_wordmark`, and `brand_logo` are optional
|
|
142
|
+
- when omitted, Baldur resolves them from `config.marketing_brand`
|
|
143
|
+
- `brand_path` defaults to `#` if the host app does not provide one
|
|
144
|
+
|
|
145
|
+
Mobile behavior:
|
|
146
|
+
|
|
147
|
+
- mobile nav mirrors desktop primary and secondary links automatically
|
|
148
|
+
- the install generator now ships both `sidebar_controller.js` and `mobile_sidebar_controller.js`
|
|
149
|
+
|
|
150
|
+
You can adopt `ui_sidebar` with no slots. The block content becomes the main app surface beside the sidebar:
|
|
151
|
+
|
|
152
|
+
```erb
|
|
153
|
+
<%= ui_sidebar(
|
|
154
|
+
brand_path: root_path,
|
|
155
|
+
primary_links: sidebar_primary_links,
|
|
156
|
+
secondary_links: sidebar_secondary_links,
|
|
157
|
+
secondary_label: "Admin"
|
|
158
|
+
) do |_sidebar| %>
|
|
159
|
+
<main id="main-content" class="flex-1 p-6">
|
|
160
|
+
<%= yield %>
|
|
161
|
+
</main>
|
|
162
|
+
<% end %>
|
|
105
163
|
```
|
|
106
164
|
|
|
165
|
+
For host-specific sidebar surfaces, prefer slots. `*_content` params are also supported for incremental adoption.
|
|
166
|
+
|
|
167
|
+
```erb
|
|
168
|
+
<%= ui_sidebar(
|
|
169
|
+
brand_path: root_path,
|
|
170
|
+
primary_links: sidebar_primary_links,
|
|
171
|
+
secondary_links: sidebar_secondary_links,
|
|
172
|
+
secondary_label: "Admin"
|
|
173
|
+
) do |sidebar| %>
|
|
174
|
+
<% sidebar.with_header do %>
|
|
175
|
+
<%= render "layouts/tenant_switcher" %>
|
|
176
|
+
<% end %>
|
|
177
|
+
|
|
178
|
+
<% sidebar.with_footer do %>
|
|
179
|
+
<div class="space-y-2">
|
|
180
|
+
<p class="text-sm text-muted"><%= current_user.email %></p>
|
|
181
|
+
<%= ui_button(label: "Sign out", href: logout_path, method: :delete, variant: :text, size: :sm, icon: "log-out") %>
|
|
182
|
+
</div>
|
|
183
|
+
<% end %>
|
|
184
|
+
<% end %>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Host apps own:
|
|
188
|
+
|
|
189
|
+
- route definitions
|
|
190
|
+
- active-state logic
|
|
191
|
+
- app-specific header/footer content
|
|
192
|
+
|
|
193
|
+
Baldur owns:
|
|
194
|
+
|
|
195
|
+
- desktop and mobile sidebar shell markup
|
|
196
|
+
- toggle behavior wiring
|
|
197
|
+
- default brand rendering
|
|
198
|
+
- default nav rendering
|
|
199
|
+
|
|
107
200
|
External triggers can open a Baldur panel declaratively:
|
|
108
201
|
|
|
109
202
|
```erb
|
data/TODO.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Baldur TODO
|
|
2
2
|
|
|
3
3
|
- Improve RubyGems page description for DX
|
|
4
|
+
- Reduce min required versions for Rails and TailwindCSS to maximise support
|
|
4
5
|
|
|
5
6
|
- Add a dedicated dummy app in the extracted gem repo for visual smoke checks.
|
|
6
7
|
- Add a component inventory/showcase page in that dummy app.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
.auth-page {
|
|
3
|
+
display: flex;
|
|
4
|
+
min-height: 100vh;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
padding: var(--space-6);
|
|
8
|
+
background: var(--color-surface-low);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.auth-page__container {
|
|
12
|
+
width: 32rem;
|
|
13
|
+
max-width: 100%;
|
|
14
|
+
margin-inline: auto;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.auth-page__brand {
|
|
18
|
+
display: flex;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
margin-bottom: var(--space-6);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
@import "./baldur/application/components/utilities.css";
|
|
7
7
|
@import "./baldur/application/marketing/layout.css";
|
|
8
8
|
@import "./baldur/application/components/app_bar.css";
|
|
9
|
+
@import "./baldur/application/components/auth-page.css";
|
|
9
10
|
@import "./baldur/application/components/alert.css";
|
|
10
11
|
@import "./baldur/application/components/button.css";
|
|
11
12
|
@import "./baldur/application/components/card.css";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
module Baldur
|
|
2
|
+
module UiHelperSidebar
|
|
3
|
+
include Baldur::RenderHelper
|
|
4
|
+
include Baldur::MarketingHelper
|
|
5
|
+
|
|
6
|
+
class SidebarBuilder
|
|
7
|
+
attr_reader :header_content, :footer_content, :mobile_header_content, :mobile_footer_content
|
|
8
|
+
|
|
9
|
+
def initialize(view_context)
|
|
10
|
+
@view_context = view_context
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def with_header(&block)
|
|
14
|
+
@header_content = capture_slot(&block)
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def with_footer(&block)
|
|
19
|
+
@footer_content = capture_slot(&block)
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def with_mobile_header(&block)
|
|
24
|
+
@mobile_header_content = capture_slot(&block)
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def with_mobile_footer(&block)
|
|
29
|
+
@mobile_footer_content = capture_slot(&block)
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def capture_slot(&block)
|
|
36
|
+
return unless block_given?
|
|
37
|
+
|
|
38
|
+
@view_context.capture(&block)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def ui_sidebar(primary_links:, secondary_links: [], secondary_label: nil, brand_path: nil, brand_name: nil, brand_wordmark: nil, brand_logo: nil, header_content: nil, footer_content: nil, mobile_header_content: nil, mobile_footer_content: nil, shell_class: nil, &block)
|
|
43
|
+
builder = SidebarBuilder.new(self)
|
|
44
|
+
body_content = capture(builder, &block) if block_given?
|
|
45
|
+
|
|
46
|
+
baldur_render "baldur/components/sidebar",
|
|
47
|
+
brand: ui_sidebar_resolve_brand(
|
|
48
|
+
brand_path: brand_path,
|
|
49
|
+
brand_name: brand_name,
|
|
50
|
+
brand_wordmark: brand_wordmark,
|
|
51
|
+
brand_logo: brand_logo
|
|
52
|
+
),
|
|
53
|
+
primary_links: ui_sidebar_normalize_links(primary_links),
|
|
54
|
+
secondary_links: ui_sidebar_normalize_links(secondary_links),
|
|
55
|
+
secondary_label: secondary_label,
|
|
56
|
+
header_content: builder.header_content.presence || header_content,
|
|
57
|
+
footer_content: builder.footer_content.presence || footer_content,
|
|
58
|
+
mobile_header_content: builder.mobile_header_content.presence || mobile_header_content || builder.header_content.presence || header_content,
|
|
59
|
+
mobile_footer_content: builder.mobile_footer_content.presence || mobile_footer_content || builder.footer_content.presence || footer_content,
|
|
60
|
+
shell_class: shell_class,
|
|
61
|
+
collapsed: ui_sidebar_collapsed?,
|
|
62
|
+
body_content: body_content
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def ui_sidebar_resolve_brand(brand_path:, brand_name:, brand_wordmark:, brand_logo:)
|
|
68
|
+
overrides = {
|
|
69
|
+
name: brand_name,
|
|
70
|
+
wordmark: brand_wordmark,
|
|
71
|
+
logo_src: brand_logo,
|
|
72
|
+
href: brand_path.presence || "#"
|
|
73
|
+
}.compact
|
|
74
|
+
|
|
75
|
+
ui_marketing_resolve_brand(overrides)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def ui_sidebar_normalize_links(links)
|
|
79
|
+
Array(links).filter_map do |link|
|
|
80
|
+
next if link.blank?
|
|
81
|
+
|
|
82
|
+
normalized = link.to_h.symbolize_keys
|
|
83
|
+
next if normalized[:name].blank? || normalized[:path].blank?
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
name: normalized[:name].to_s,
|
|
87
|
+
path: normalized[:path],
|
|
88
|
+
icon: normalized[:icon].presence || "circle",
|
|
89
|
+
active: !!normalized[:active],
|
|
90
|
+
title: normalized[:title].presence || normalized[:name].to_s,
|
|
91
|
+
method: normalized[:method],
|
|
92
|
+
data: normalized[:data].respond_to?(:to_h) ? normalized[:data].to_h : nil,
|
|
93
|
+
html_options: normalized[:html_options].respond_to?(:to_h) ? normalized[:html_options].to_h.symbolize_keys : {}
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def ui_sidebar_collapsed?
|
|
99
|
+
return false unless respond_to?(:cookies)
|
|
100
|
+
|
|
101
|
+
cookies["baldur-sidebar-collapsed"] == "true"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<%
|
|
2
|
+
resolved_brand = local_assigns.fetch(:brand).symbolize_keys
|
|
3
|
+
primary_links = Array(local_assigns[:primary_links]).compact
|
|
4
|
+
secondary_links = Array(local_assigns[:secondary_links]).compact
|
|
5
|
+
secondary_label = local_assigns[:secondary_label]
|
|
6
|
+
header_content = local_assigns[:header_content]
|
|
7
|
+
footer_content = local_assigns[:footer_content]
|
|
8
|
+
mobile_header_content = local_assigns[:mobile_header_content]
|
|
9
|
+
mobile_footer_content = local_assigns[:mobile_footer_content]
|
|
10
|
+
body_content = local_assigns[:body_content]
|
|
11
|
+
shell_class = local_assigns[:shell_class]
|
|
12
|
+
collapsed = !!local_assigns[:collapsed]
|
|
13
|
+
|
|
14
|
+
brand_markup = content_tag(:span, class: "brand-lockup inline-flex items-center gap-2 text-[color:var(--color-on-surface)]") do
|
|
15
|
+
safe_join(
|
|
16
|
+
[
|
|
17
|
+
image_tag(resolved_brand[:logo_src], alt: resolved_brand[:logo_alt], class: "brand-lockup__logo h-9 w-9 rounded-xl"),
|
|
18
|
+
content_tag(:span, resolved_brand[:wordmark], class: "brand-lockup__wordmark sidebar__brand-name text-lg font-semibold text-[color:var(--color-on-surface)]")
|
|
19
|
+
]
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
brand_markup = link_to(brand_markup, resolved_brand[:href], class: "inline-flex no-underline") if resolved_brand[:href].present?
|
|
23
|
+
|
|
24
|
+
render_nav_link = lambda do |link, mobile: false|
|
|
25
|
+
options = link[:html_options].deep_dup
|
|
26
|
+
options[:title] = link[:title] if link[:title].present?
|
|
27
|
+
options[:data] = (options[:data] || {}).merge(link[:data] || {}) if link[:data].present?
|
|
28
|
+
options[:method] = link[:method] if link[:method].present?
|
|
29
|
+
options[:aria] = (options[:aria] || {}).merge(current: (link[:active] ? "page" : nil))
|
|
30
|
+
|
|
31
|
+
if mobile
|
|
32
|
+
mobile_class = link[:active] ? "bg-primary/10 text-primary" : "text-soft hover:bg-surface-lowest"
|
|
33
|
+
options[:class] = ["flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium", mobile_class, options[:class]].compact.join(" ")
|
|
34
|
+
link_to(link[:path], **options) do
|
|
35
|
+
safe_join(
|
|
36
|
+
[
|
|
37
|
+
ui_icon(link[:icon], class_name: "h-5 w-5 shrink-0"),
|
|
38
|
+
content_tag(:span, link[:name])
|
|
39
|
+
]
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
options[:class] = ["sidebar__link", options[:class]].compact.join(" ")
|
|
44
|
+
link_to(link[:path], **options) do
|
|
45
|
+
safe_join(
|
|
46
|
+
[
|
|
47
|
+
ui_icon(link[:icon], class_name: "sidebar__icon"),
|
|
48
|
+
content_tag(:span, link[:name], class: "sidebar__label")
|
|
49
|
+
]
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
%>
|
|
55
|
+
|
|
56
|
+
<div class="<%= ["sidebar-shell min-h-screen bg-surface-low md:flex", shell_class].compact.join(" ") %>"
|
|
57
|
+
data-controller="sidebar"
|
|
58
|
+
data-sidebar-storage-key-value="baldur-sidebar-collapsed"
|
|
59
|
+
data-sidebar-collapsed-value="<%= collapsed %>">
|
|
60
|
+
<aside class="sidebar hidden shrink-0 border-r border-[color:var(--color-outline-variant)] bg-[color:var(--color-surface)] px-3 py-4 md:flex flex-col"
|
|
61
|
+
aria-label="Primary navigation">
|
|
62
|
+
<div class="sidebar__header flex items-center justify-between gap-2">
|
|
63
|
+
<%= brand_markup %>
|
|
64
|
+
<button type="button"
|
|
65
|
+
class="icon-button sidebar__toggle"
|
|
66
|
+
aria-label="Toggle navigation"
|
|
67
|
+
aria-expanded="<%= (!collapsed).to_s %>"
|
|
68
|
+
data-sidebar-target="toggleButton"
|
|
69
|
+
data-action="sidebar#toggle">
|
|
70
|
+
<span aria-hidden="true" data-sidebar-target="toggleIcon"><%= ui_icon(collapsed ? "chevron-right" : "chevron-left", class_name: "h-5 w-5") %></span>
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<% if header_content.present? %>
|
|
75
|
+
<div class="sidebar__menu mt-4">
|
|
76
|
+
<%= header_content %>
|
|
77
|
+
</div>
|
|
78
|
+
<% end %>
|
|
79
|
+
|
|
80
|
+
<nav class="mt-6 flex flex-1 flex-col gap-1" aria-label="Primary navigation links">
|
|
81
|
+
<% primary_links.each do |link| %>
|
|
82
|
+
<%= render_nav_link.call(link) %>
|
|
83
|
+
<% end %>
|
|
84
|
+
|
|
85
|
+
<% if secondary_links.any? %>
|
|
86
|
+
<div class="my-6 border-t border-[color:var(--color-outline-variant)]"></div>
|
|
87
|
+
<% if secondary_label.present? %>
|
|
88
|
+
<p class="sidebar__section-label mb-2 px-3 text-xs font-semibold uppercase tracking-wide text-[color:var(--color-on-surface-variant)]"><%= secondary_label %></p>
|
|
89
|
+
<% end %>
|
|
90
|
+
<% secondary_links.each do |link| %>
|
|
91
|
+
<%= render_nav_link.call(link) %>
|
|
92
|
+
<% end %>
|
|
93
|
+
<% end %>
|
|
94
|
+
</nav>
|
|
95
|
+
|
|
96
|
+
<% if footer_content.present? %>
|
|
97
|
+
<div class="sidebar__footer mt-auto border-t border-[color:var(--color-outline-variant)] pt-4">
|
|
98
|
+
<div class="sidebar__footer-inner flex flex-col items-start gap-3">
|
|
99
|
+
<%= footer_content %>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<% end %>
|
|
103
|
+
|
|
104
|
+
<button type="button" class="sidebar__toggle-hit-area" aria-label="Toggle navigation" data-action="click->sidebar#toggle"></button>
|
|
105
|
+
</aside>
|
|
106
|
+
|
|
107
|
+
<div class="md:hidden" data-controller="mobile-sidebar">
|
|
108
|
+
<div class="sticky top-0 z-10 flex items-center justify-between border-b border-outline-variant bg-surface-highest px-4 py-3">
|
|
109
|
+
<div><%= brand_markup %></div>
|
|
110
|
+
<button type="button" class="icon-button" aria-label="Toggle navigation" data-action="mobile-sidebar#toggle">
|
|
111
|
+
<%= ui_icon("menu", class_name: "h-6 w-6") %>
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div id="mobile-sidebar" class="hidden fixed inset-0 z-40 flex" data-mobile-sidebar-target="panel">
|
|
116
|
+
<div class="fixed inset-0 bg-scrim" data-action="click->mobile-sidebar#close"></div>
|
|
117
|
+
<div class="relative flex w-full max-w-xs flex-1 flex-col bg-surface-highest">
|
|
118
|
+
<div class="absolute right-0 top-0 -mr-12 pt-2">
|
|
119
|
+
<button type="button" class="icon-button text-on-inverse" aria-label="Close navigation" data-action="mobile-sidebar#close">
|
|
120
|
+
<%= ui_icon("x", class_name: "h-6 w-6 text-on-inverse") %>
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div class="flex-1 overflow-y-auto px-4 pb-4 pt-5">
|
|
125
|
+
<% if mobile_header_content.present? %>
|
|
126
|
+
<div class="mb-5"><%= mobile_header_content %></div>
|
|
127
|
+
<% end %>
|
|
128
|
+
|
|
129
|
+
<nav class="space-y-1" aria-label="Mobile primary navigation links">
|
|
130
|
+
<% primary_links.each do |link| %>
|
|
131
|
+
<%= render_nav_link.call(link, mobile: true) %>
|
|
132
|
+
<% end %>
|
|
133
|
+
|
|
134
|
+
<% if secondary_links.any? %>
|
|
135
|
+
<div class="my-5 border-t border-outline-variant"></div>
|
|
136
|
+
<% if secondary_label.present? %>
|
|
137
|
+
<p class="mb-2 px-3 text-xs font-semibold uppercase tracking-wide text-on-surface-variant"><%= secondary_label %></p>
|
|
138
|
+
<% end %>
|
|
139
|
+
<% secondary_links.each do |link| %>
|
|
140
|
+
<%= render_nav_link.call(link, mobile: true) %>
|
|
141
|
+
<% end %>
|
|
142
|
+
<% end %>
|
|
143
|
+
</nav>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<% if mobile_footer_content.present? %>
|
|
147
|
+
<div class="border-t border-outline-variant px-4 py-4">
|
|
148
|
+
<%= mobile_footer_content %>
|
|
149
|
+
</div>
|
|
150
|
+
<% end %>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<div class="flex min-w-0 flex-1 flex-col">
|
|
156
|
+
<%= body_content %>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<%
|
|
2
2
|
resolved_brand_path = local_assigns[:brand_path].presence || "#"
|
|
3
|
-
auth_shell_classes = ["auth-page",
|
|
3
|
+
auth_shell_classes = ["auth-page", local_assigns[:shell_class]].compact.join(" ")
|
|
4
4
|
auth_card_class = local_assigns[:card_class]
|
|
5
5
|
%>
|
|
6
6
|
|
|
7
7
|
<div class="<%= auth_shell_classes %>">
|
|
8
|
-
<div class="
|
|
9
|
-
<div class="
|
|
8
|
+
<div class="auth-page__container">
|
|
9
|
+
<div class="auth-page__brand">
|
|
10
10
|
<%= link_to resolved_brand_path, class: "inline-flex items-center justify-center no-underline" do %>
|
|
11
11
|
<%= render "shared/brand_lockup",
|
|
12
12
|
logo_class: "h-10 w-10 rounded-xl",
|
data/lib/baldur/version.rb
CHANGED
|
@@ -30,6 +30,7 @@ class BaldurInstallGeneratorTest < Rails::Generators::TestCase
|
|
|
30
30
|
assert_file "config/initializers/baldur.rb"
|
|
31
31
|
assert_file "app/assets/stylesheets/fonts.css"
|
|
32
32
|
assert_file "app/assets/stylesheets/theme.css"
|
|
33
|
+
assert_file "app/javascript/controllers/mobile_sidebar_controller.js", /export \{ default \} from "baldur\/controllers\/mobile_sidebar_controller"/
|
|
33
34
|
assert_file "app/javascript/controllers/sidebar_controller.js", /export \{ default \} from "baldur\/controllers\/sidebar_controller"/
|
|
34
35
|
assert_file "app/javascript/lib/snackbar.js", /export \* from "baldur\/lib\/snackbar"/
|
|
35
36
|
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
require "action_controller"
|
|
4
|
+
|
|
5
|
+
class BaldurSidebarHelperTest < Minitest::Test
|
|
6
|
+
class TestController < ActionController::Base
|
|
7
|
+
append_view_path File.expand_path("../app/views", __dir__)
|
|
8
|
+
helper Baldur::UiHelper
|
|
9
|
+
helper Baldur::UiHelperSidebar
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def setup
|
|
13
|
+
@original_brand = Baldur.config.marketing_brand
|
|
14
|
+
Baldur.config.marketing_brand = {
|
|
15
|
+
name: "Acme",
|
|
16
|
+
wordmark: "Acme Ops",
|
|
17
|
+
logo_src: "/acme.svg",
|
|
18
|
+
logo_alt: "Acme logo"
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def teardown
|
|
23
|
+
Baldur.config.marketing_brand = @original_brand
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_sidebar_uses_configured_brand_and_mirrors_links_for_mobile
|
|
27
|
+
html = TestController.render(
|
|
28
|
+
inline: <<~ERB,
|
|
29
|
+
<%= ui_sidebar(
|
|
30
|
+
brand_path: "/home",
|
|
31
|
+
primary_links: [{ name: "Dashboard", path: "/dashboard", icon: "layout-dashboard", active: true }],
|
|
32
|
+
secondary_links: [{ name: "Settings", path: "/settings", icon: "settings", active: false }],
|
|
33
|
+
secondary_label: "Admin"
|
|
34
|
+
) %>
|
|
35
|
+
ERB
|
|
36
|
+
formats: [ :html ]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
assert_includes html, 'data-controller="sidebar"'
|
|
40
|
+
assert_includes html, 'data-controller="mobile-sidebar"'
|
|
41
|
+
assert_includes html, "Acme Ops"
|
|
42
|
+
assert_includes html, 'href="/dashboard"'
|
|
43
|
+
assert_includes html, 'href="/settings"'
|
|
44
|
+
assert_includes html, 'aria-current="page"'
|
|
45
|
+
assert_includes html, "Admin"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_sidebar_renders_slot_content_and_mobile_fallbacks
|
|
49
|
+
html = TestController.render(
|
|
50
|
+
inline: <<~ERB,
|
|
51
|
+
<%= ui_sidebar(primary_links: [{ name: "Dashboard", path: "/dashboard" }]) do |sidebar| %>
|
|
52
|
+
<% sidebar.with_header do %>
|
|
53
|
+
<div class="tenant-switcher">Tenant Switcher</div>
|
|
54
|
+
<% end %>
|
|
55
|
+
<% sidebar.with_footer do %>
|
|
56
|
+
<div class="user-menu">Sign out</div>
|
|
57
|
+
<% end %>
|
|
58
|
+
<% end %>
|
|
59
|
+
ERB
|
|
60
|
+
formats: [ :html ]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
assert_includes html, "Tenant Switcher"
|
|
64
|
+
assert_includes html, "user-menu"
|
|
65
|
+
assert_includes html, "Sign out"
|
|
66
|
+
assert_operator html.scan("Tenant Switcher").size, :>=, 2
|
|
67
|
+
assert_operator html.scan("Sign out").size, :>=, 2
|
|
68
|
+
end
|
|
69
|
+
end
|
data/test/test_helper.rb
CHANGED
|
@@ -11,5 +11,6 @@ require_relative "../app/helpers/baldur/render_helper"
|
|
|
11
11
|
require_relative "../app/helpers/baldur/ui_helper_feedback"
|
|
12
12
|
require_relative "../app/helpers/baldur/ui_helper_unavailable"
|
|
13
13
|
require_relative "../app/helpers/baldur/ui_helper_forms"
|
|
14
|
-
require_relative "../app/helpers/baldur/ui_helper"
|
|
15
14
|
require_relative "../app/helpers/baldur/marketing_helper"
|
|
15
|
+
require_relative "../app/helpers/baldur/ui_helper_sidebar"
|
|
16
|
+
require_relative "../app/helpers/baldur/ui_helper"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "baldur/controllers/mobile_sidebar_controller"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: baldur
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Varun Murkar
|
|
@@ -106,6 +106,7 @@ files:
|
|
|
106
106
|
- app/assets/stylesheets/baldur.css
|
|
107
107
|
- app/assets/stylesheets/baldur/application/components/alert.css
|
|
108
108
|
- app/assets/stylesheets/baldur/application/components/app_bar.css
|
|
109
|
+
- app/assets/stylesheets/baldur/application/components/auth-page.css
|
|
109
110
|
- app/assets/stylesheets/baldur/application/components/button.css
|
|
110
111
|
- app/assets/stylesheets/baldur/application/components/card.css
|
|
111
112
|
- app/assets/stylesheets/baldur/application/components/chart.css
|
|
@@ -146,6 +147,7 @@ files:
|
|
|
146
147
|
- app/helpers/baldur/ui_helper.rb
|
|
147
148
|
- app/helpers/baldur/ui_helper_feedback.rb
|
|
148
149
|
- app/helpers/baldur/ui_helper_forms.rb
|
|
150
|
+
- app/helpers/baldur/ui_helper_sidebar.rb
|
|
149
151
|
- app/helpers/baldur/ui_helper_unavailable.rb
|
|
150
152
|
- app/views/baldur/components/_accordion.html.erb
|
|
151
153
|
- app/views/baldur/components/_action_row.html.erb
|
|
@@ -164,6 +166,7 @@ files:
|
|
|
164
166
|
- app/views/baldur/components/_pagination.html.erb
|
|
165
167
|
- app/views/baldur/components/_segmented_buttons.html.erb
|
|
166
168
|
- app/views/baldur/components/_settings_nav.html.erb
|
|
169
|
+
- app/views/baldur/components/_sidebar.html.erb
|
|
167
170
|
- app/views/baldur/components/_snackbar.html.erb
|
|
168
171
|
- app/views/baldur/components/_snackbar_stack.html.erb
|
|
169
172
|
- app/views/baldur/components/_stepper.html.erb
|
|
@@ -205,6 +208,7 @@ files:
|
|
|
205
208
|
- test/install_panel_secondary_generator_test.rb
|
|
206
209
|
- test/marketing_helper_test.rb
|
|
207
210
|
- test/run_all.rb
|
|
211
|
+
- test/sidebar_helper_test.rb
|
|
208
212
|
- test/test_helper.rb
|
|
209
213
|
- test/tmp/install_generator/app/assets/stylesheets/fonts.css
|
|
210
214
|
- test/tmp/install_generator/app/assets/stylesheets/theme.css
|
|
@@ -217,6 +221,7 @@ files:
|
|
|
217
221
|
- test/tmp/install_generator/app/javascript/controllers/marketing_pricing_controller.js
|
|
218
222
|
- test/tmp/install_generator/app/javascript/controllers/marketing_tabs_controller.js
|
|
219
223
|
- test/tmp/install_generator/app/javascript/controllers/menu_select_controller.js
|
|
224
|
+
- test/tmp/install_generator/app/javascript/controllers/mobile_sidebar_controller.js
|
|
220
225
|
- test/tmp/install_generator/app/javascript/controllers/modal_controller.js
|
|
221
226
|
- test/tmp/install_generator/app/javascript/controllers/segmented_tabs_controller.js
|
|
222
227
|
- test/tmp/install_generator/app/javascript/controllers/sidebar_controller.js
|