baldur 0.1.7 → 0.2.3

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/README.md +78 -376
  4. data/TODO.md +53 -5
  5. data/app/assets/javascripts/baldur/controllers/confirmation_controller.js +23 -0
  6. data/app/assets/stylesheets/baldur/application/components/auth-page.css +7 -0
  7. data/app/assets/stylesheets/baldur/application/components/confirmation.css +11 -0
  8. data/app/assets/stylesheets/baldur/application/components/sidebar.css +234 -0
  9. data/app/assets/stylesheets/baldur.css +1 -0
  10. data/app/assets/tailwind/baldur/engine.css +2 -2
  11. data/app/helpers/baldur/marketing_helper.rb +71 -69
  12. data/app/helpers/baldur/optional/auth_page_helper.rb +4 -2
  13. data/app/helpers/baldur/optional/google_auth_helper.rb +2 -2
  14. data/app/helpers/baldur/optional/panel_secondary_helper.rb +3 -2
  15. data/app/helpers/baldur/ui_helper.rb +159 -121
  16. data/app/helpers/baldur/ui_helper_feedback.rb +38 -36
  17. data/app/helpers/baldur/ui_helper_forms.rb +81 -76
  18. data/app/helpers/baldur/ui_helper_sidebar.rb +6 -5
  19. data/app/helpers/baldur/ui_helper_unavailable.rb +17 -15
  20. data/app/views/baldur/components/_confirmation_modal.html.erb +99 -0
  21. data/app/views/baldur/components/_modal_host.html.erb +10 -0
  22. data/app/views/baldur/components/_sidebar.html.erb +30 -30
  23. data/app/views/baldur/components/_theme_toggle.html.erb +23 -0
  24. data/app/views/baldur/optional/_auth_page.html.erb +6 -0
  25. data/baldur.gemspec +23 -23
  26. data/config/importmap.rb +2 -2
  27. data/lib/baldur/configuration.rb +6 -6
  28. data/lib/baldur/engine.rb +3 -3
  29. data/lib/baldur/version.rb +1 -1
  30. data/lib/baldur.rb +5 -5
  31. data/lib/generators/baldur/install/install_generator.rb +20 -19
  32. data/lib/generators/baldur/install/templates/baldur_initializer.rb +7 -7
  33. data/lib/generators/baldur/install/templates/theme.css +32 -6
  34. data/lib/generators/baldur/install_google_auth/install_google_auth_generator.rb +2 -2
  35. data/lib/generators/baldur/install_panel_right/install_panel_right_generator.rb +2 -2
  36. data/lib/generators/baldur/install_panel_secondary/install_panel_secondary_generator.rb +3 -3
  37. data/script/verify_host_install +59 -31
  38. data/test/confirmation_modal_helper_test.rb +241 -0
  39. data/test/csp_rendering_test.rb +21 -21
  40. data/test/engine_css_test.rb +36 -0
  41. data/test/fixtures/shared/_brand_lockup.html.erb +1 -0
  42. data/test/gemspec_test.rb +7 -5
  43. data/test/install_generator_test.rb +25 -21
  44. data/test/install_panel_secondary_generator_test.rb +12 -9
  45. data/test/marketing_helper_test.rb +7 -7
  46. data/test/run_all.rb +1 -1
  47. data/test/sidebar_helper_test.rb +16 -16
  48. data/test/test_helper.rb +14 -14
  49. data/test/theme_toggle_helper_test.rb +119 -0
  50. data/test/tmp/install_generator/app/assets/stylesheets/theme.css +32 -6
  51. data/test/tmp/install_generator/app/javascript/controllers/confirmation_controller.js +1 -0
  52. data/test/tmp/install_generator/config/initializers/baldur.rb +7 -7
  53. metadata +20 -8
@@ -1,4 +1,48 @@
1
1
  @layer components {
2
+ .sidebar-shell {
3
+ min-height: 100vh;
4
+ background: var(--color-surface-low);
5
+ }
6
+
7
+ .sidebar-shell__main {
8
+ min-width: 0;
9
+ flex: 1 1 auto;
10
+ display: flex;
11
+ flex-direction: column;
12
+ }
13
+
14
+ .sidebar-shell__desktop {
15
+ display: none;
16
+ flex-shrink: 0;
17
+ border-right: 1px solid var(--color-outline-variant);
18
+ background: var(--color-surface);
19
+ padding: var(--space-3);
20
+ flex-direction: column;
21
+ }
22
+
23
+ .sidebar__brand-link {
24
+ display: inline-flex;
25
+ text-decoration: none;
26
+ }
27
+
28
+ .sidebar__brand-link .brand-lockup {
29
+ display: inline-flex;
30
+ align-items: center;
31
+ gap: var(--space-2);
32
+ color: var(--color-on-surface);
33
+ }
34
+
35
+ .sidebar__brand-link .brand-lockup__logo {
36
+ width: 2.25rem;
37
+ height: 2.25rem;
38
+ border-radius: var(--radius-xl);
39
+ object-fit: cover;
40
+ }
41
+
42
+ .sidebar__brand-link .brand-lockup__wordmark {
43
+ color: var(--color-on-surface);
44
+ }
45
+
2
46
  .sidebar {
3
47
  position: sticky;
4
48
  top: 0;
@@ -21,6 +65,196 @@
21
65
  font-weight: 500;
22
66
  }
23
67
 
68
+ .sidebar__header {
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: space-between;
72
+ gap: var(--space-2);
73
+ }
74
+
75
+ .sidebar__slot--header {
76
+ margin-top: var(--space-4);
77
+ }
78
+
79
+ .sidebar__nav {
80
+ margin-top: var(--space-6);
81
+ display: flex;
82
+ flex: 1 1 auto;
83
+ flex-direction: column;
84
+ gap: var(--space-1);
85
+ }
86
+
87
+ .sidebar__section-divider {
88
+ margin-block: var(--space-6);
89
+ border-top: 1px solid var(--color-outline-variant);
90
+ }
91
+
92
+ .sidebar__section-label {
93
+ margin: 0 0 var(--space-2);
94
+ padding-inline: var(--space-3);
95
+ color: var(--color-on-surface-variant);
96
+ font-size: 0.75rem;
97
+ font-weight: 600;
98
+ letter-spacing: 0.08em;
99
+ text-transform: uppercase;
100
+ }
101
+
102
+ .sidebar__footer {
103
+ margin-top: auto;
104
+ border-top: 1px solid var(--color-outline-variant);
105
+ padding-top: var(--space-4);
106
+ }
107
+
108
+ .sidebar__footer-inner {
109
+ display: flex;
110
+ flex-direction: column;
111
+ align-items: flex-start;
112
+ gap: var(--space-3);
113
+ }
114
+
115
+ .sidebar-mobile {
116
+ display: block;
117
+ }
118
+
119
+ .sidebar-mobile__topbar {
120
+ position: sticky;
121
+ top: 0;
122
+ z-index: 10;
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: space-between;
126
+ gap: var(--space-3);
127
+ border-bottom: 1px solid var(--color-outline-variant);
128
+ background: var(--color-surface-highest);
129
+ padding: var(--space-3) var(--space-4);
130
+ }
131
+
132
+ .sidebar-mobile__brand .brand-lockup__logo {
133
+ width: 1.75rem;
134
+ height: 1.75rem;
135
+ border-radius: var(--radius-lg);
136
+ object-fit: cover;
137
+ }
138
+
139
+ .sidebar-mobile__brand .brand-lockup__wordmark {
140
+ color: var(--color-on-surface);
141
+ font-size: 1.125rem;
142
+ font-weight: 700;
143
+ }
144
+
145
+ .sidebar-mobile__panel {
146
+ position: fixed;
147
+ inset: 0;
148
+ z-index: 40;
149
+ display: flex;
150
+ }
151
+
152
+ .sidebar-mobile__scrim {
153
+ position: fixed;
154
+ inset: 0;
155
+ }
156
+
157
+ .sidebar-mobile__surface {
158
+ position: relative;
159
+ display: flex;
160
+ flex: 1 1 auto;
161
+ width: 100%;
162
+ max-width: 20rem;
163
+ flex-direction: column;
164
+ background: var(--color-surface-highest);
165
+ }
166
+
167
+ .sidebar-mobile__close-shell {
168
+ position: absolute;
169
+ top: 0;
170
+ right: 0;
171
+ margin-right: -3rem;
172
+ padding-top: var(--space-2);
173
+ }
174
+
175
+ .sidebar-mobile__close {
176
+ color: var(--color-inverse-on-surface);
177
+ }
178
+
179
+ .sidebar-mobile__close-icon {
180
+ width: 1.5rem;
181
+ height: 1.5rem;
182
+ }
183
+
184
+ .sidebar-mobile__content {
185
+ flex: 1 1 auto;
186
+ overflow-y: auto;
187
+ padding: var(--space-5) var(--space-4) var(--space-4);
188
+ }
189
+
190
+ .sidebar-mobile__slot--header {
191
+ margin-bottom: var(--space-5);
192
+ }
193
+
194
+ .sidebar-mobile__slot--footer {
195
+ border-top: 1px solid var(--color-outline-variant);
196
+ padding: var(--space-4);
197
+ }
198
+
199
+ .sidebar-mobile__nav {
200
+ display: flex;
201
+ flex-direction: column;
202
+ gap: var(--space-1);
203
+ }
204
+
205
+ .sidebar-mobile__link {
206
+ display: flex;
207
+ align-items: center;
208
+ gap: var(--space-3);
209
+ border-radius: var(--radius-lg);
210
+ padding: 0.625rem 0.75rem;
211
+ color: var(--color-text-soft);
212
+ font-size: 0.875rem;
213
+ font-weight: 500;
214
+ text-decoration: none;
215
+ }
216
+
217
+ .sidebar-mobile__link:hover,
218
+ .sidebar-mobile__link--active {
219
+ color: var(--color-primary);
220
+ background: color-mix(in srgb, var(--color-primary) 10%, transparent);
221
+ }
222
+
223
+ .sidebar-mobile__icon {
224
+ width: 1.25rem;
225
+ height: 1.25rem;
226
+ flex-shrink: 0;
227
+ }
228
+
229
+ .sidebar-mobile__section-divider {
230
+ margin-block: var(--space-5);
231
+ border-top: 1px solid var(--color-outline-variant);
232
+ }
233
+
234
+ .sidebar-mobile__section-label {
235
+ margin: 0 0 var(--space-2);
236
+ padding-inline: var(--space-3);
237
+ color: var(--color-on-surface-variant);
238
+ font-size: 0.75rem;
239
+ font-weight: 600;
240
+ letter-spacing: 0.08em;
241
+ text-transform: uppercase;
242
+ }
243
+
244
+ @media (min-width: 48rem) {
245
+ .sidebar-shell {
246
+ display: flex;
247
+ }
248
+
249
+ .sidebar-shell__desktop {
250
+ display: flex;
251
+ }
252
+
253
+ .sidebar-mobile {
254
+ display: none;
255
+ }
256
+ }
257
+
24
258
  .sidebar__toggle {
25
259
  cursor: pointer;
26
260
  pointer-events: auto;
@@ -25,4 +25,5 @@
25
25
  @import "./baldur/application/components/stepper.css";
26
26
  @import "./baldur/application/components/switch.css";
27
27
  @import "./baldur/application/components/table.css";
28
+ @import "./baldur/application/components/confirmation.css";
28
29
  @import "./baldur/application/components/timeline.css";
@@ -1,5 +1,5 @@
1
- @source "../../helpers";
2
- @source "../../views";
1
+ @source "../../../helpers";
2
+ @source "../../../views";
3
3
 
4
4
  @import "../../stylesheets/baldur.css";
5
5
  @import "../../stylesheets/baldur_panel_secondary.css";
@@ -3,82 +3,85 @@ module Baldur
3
3
  include Baldur::RenderHelper
4
4
 
5
5
  def ui_marketing_top_nav(links:, primary_action:, theme_toggle: nil, classes: nil, brand: {})
6
- baldur_render "baldur/marketing/top_nav",
7
- links: Array(links),
8
- primary_action: primary_action,
9
- theme_toggle: theme_toggle,
10
- classes: classes,
11
- brand: ui_marketing_resolve_brand(brand)
6
+ baldur_render 'baldur/marketing/top_nav',
7
+ links: Array(links),
8
+ primary_action: primary_action,
9
+ theme_toggle: theme_toggle,
10
+ classes: classes,
11
+ brand: ui_marketing_resolve_brand(brand)
12
12
  end
13
13
 
14
- def ui_marketing_hero_section(variant:, headline:, body:, primary_action:, centerpiece_image:, id: "hero", secondary_action: nil, supporting_action: nil, callouts: [], orbit_sources: [], classes: nil)
15
- baldur_render "baldur/marketing/hero_section",
16
- variant: variant,
17
- headline: headline,
18
- body: body,
19
- primary_action: primary_action,
20
- secondary_action: secondary_action,
21
- supporting_action: supporting_action,
22
- callouts: Array(callouts),
23
- orbit_sources: Array(orbit_sources),
24
- centerpiece_image: centerpiece_image,
25
- id: id,
26
- classes: classes
14
+ def ui_marketing_hero_section(variant:, headline:, body:, primary_action:, centerpiece_image:, id: 'hero',
15
+ secondary_action: nil, supporting_action: nil, callouts: [], orbit_sources: [], classes: nil)
16
+ baldur_render 'baldur/marketing/hero_section',
17
+ variant: variant,
18
+ headline: headline,
19
+ body: body,
20
+ primary_action: primary_action,
21
+ secondary_action: secondary_action,
22
+ supporting_action: supporting_action,
23
+ callouts: Array(callouts),
24
+ orbit_sources: Array(orbit_sources),
25
+ centerpiece_image: centerpiece_image,
26
+ id: id,
27
+ classes: classes
27
28
  end
28
29
 
29
- def ui_marketing_features_section(title:, tabs:, id: "use-cases", description: nil, cta: nil, question_label: "Answers to questions like:", classes: nil)
30
- baldur_render "baldur/marketing/features_section",
31
- title: title,
32
- description: description,
33
- tabs: Array(tabs),
34
- cta: cta,
35
- question_label: question_label,
36
- id: id,
37
- classes: classes
30
+ def ui_marketing_features_section(title:, tabs:, id: 'use-cases', description: nil, cta: nil,
31
+ question_label: 'Answers to questions like:', classes: nil)
32
+ baldur_render 'baldur/marketing/features_section',
33
+ title: title,
34
+ description: description,
35
+ tabs: Array(tabs),
36
+ cta: cta,
37
+ question_label: question_label,
38
+ id: id,
39
+ classes: classes
38
40
  end
39
41
 
40
- def ui_marketing_testimonials_section(variant:, title:, items:, id: "proof", classes: nil)
41
- baldur_render "baldur/marketing/testimonials_section",
42
- variant: variant,
43
- title: title,
44
- items: Array(items),
45
- id: id,
46
- classes: classes
42
+ def ui_marketing_testimonials_section(variant:, title:, items:, id: 'proof', classes: nil)
43
+ baldur_render 'baldur/marketing/testimonials_section',
44
+ variant: variant,
45
+ title: title,
46
+ items: Array(items),
47
+ id: id,
48
+ classes: classes
47
49
  end
48
50
 
49
- def ui_marketing_faq_section(title:, items:, id: "faq", description: nil, classes: nil, container_class: nil)
50
- baldur_render "baldur/marketing/faq_section",
51
- title: title,
52
- description: description,
53
- items: Array(items),
54
- id: id,
55
- classes: classes,
56
- container_class: container_class
51
+ def ui_marketing_faq_section(title:, items:, id: 'faq', description: nil, classes: nil, container_class: nil)
52
+ baldur_render 'baldur/marketing/faq_section',
53
+ title: title,
54
+ description: description,
55
+ items: Array(items),
56
+ id: id,
57
+ classes: classes,
58
+ container_class: container_class
57
59
  end
58
60
 
59
61
  def ui_marketing_cta_banner(banner:, classes: nil)
60
- baldur_render "baldur/marketing/cta_banner", banner: banner, classes: classes
62
+ baldur_render 'baldur/marketing/cta_banner', banner: banner, classes: classes
61
63
  end
62
64
 
63
- def ui_marketing_pricing_tables(headline:, subheadline:, billing_options:, plans:, shared_capabilities:, eyebrow: "Pricing", fair_use_link: nil, classes: nil)
64
- baldur_render "baldur/marketing/pricing_tables",
65
- headline: headline,
66
- subheadline: subheadline,
67
- billing_options: Array(billing_options),
68
- plans: Array(plans),
69
- shared_capabilities: shared_capabilities,
70
- eyebrow: eyebrow,
71
- fair_use_link: fair_use_link,
72
- classes: classes
65
+ def ui_marketing_pricing_tables(headline:, subheadline:, billing_options:, plans:, shared_capabilities:,
66
+ eyebrow: 'Pricing', fair_use_link: nil, classes: nil)
67
+ baldur_render 'baldur/marketing/pricing_tables',
68
+ headline: headline,
69
+ subheadline: subheadline,
70
+ billing_options: Array(billing_options),
71
+ plans: Array(plans),
72
+ shared_capabilities: shared_capabilities,
73
+ eyebrow: eyebrow,
74
+ fair_use_link: fair_use_link,
75
+ classes: classes
73
76
  end
74
77
 
75
78
  def ui_marketing_footer(columns:, description:, copyright: nil, classes: nil, brand: {})
76
- baldur_render "baldur/marketing/footer",
77
- columns: Array(columns),
78
- description: description,
79
- copyright: copyright,
80
- classes: classes,
81
- brand: ui_marketing_resolve_brand(brand)
79
+ baldur_render 'baldur/marketing/footer',
80
+ columns: Array(columns),
81
+ description: description,
82
+ copyright: copyright,
83
+ classes: classes,
84
+ brand: ui_marketing_resolve_brand(brand)
82
85
  end
83
86
 
84
87
  def ui_marketing_render_content(content)
@@ -90,18 +93,18 @@ module Baldur
90
93
  end
91
94
 
92
95
  def ui_marketing_action_string(*actions)
93
- actions.flatten.compact.map(&:to_s).map(&:strip).reject(&:blank?).uniq.join(" ")
96
+ actions.flatten.compact.map(&:to_s).map(&:strip).reject(&:blank?).uniq.join(' ')
94
97
  end
95
98
 
96
99
  def ui_marketing_resolve_brand(overrides = {})
97
100
  configured_brand = ui_marketing_configured_brand
98
101
  override_brand = overrides.to_h.symbolize_keys
99
102
  resolved_brand = configured_brand.merge(override_brand)
100
- resolved_name = resolved_brand[:name].presence || "Brand"
103
+ resolved_name = resolved_brand[:name].presence || 'Brand'
101
104
 
102
105
  resolved_brand[:name] = resolved_name
103
106
  resolved_brand[:wordmark] = resolved_brand[:wordmark].presence || resolved_name
104
- resolved_brand[:logo_src] = resolved_brand[:logo_src].presence || "/icon.png"
107
+ resolved_brand[:logo_src] = resolved_brand[:logo_src].presence || '/icon.png'
105
108
  resolved_brand[:logo_alt] = resolved_brand[:logo_alt].presence || "#{resolved_name} logo"
106
109
  resolved_brand
107
110
  end
@@ -109,13 +112,12 @@ module Baldur
109
112
  def ui_marketing_configured_brand
110
113
  config = Baldur.config
111
114
  brand = if config.respond_to?(:marketing_brand)
112
- config.public_send(:marketing_brand)
113
- elsif config.instance_variable_defined?(:@marketing_brand)
114
- config.instance_variable_get(:@marketing_brand)
115
- end
115
+ config.public_send(:marketing_brand)
116
+ elsif config.instance_variable_defined?(:@marketing_brand)
117
+ config.instance_variable_get(:@marketing_brand)
118
+ end
116
119
 
117
- resolved_brand = brand.respond_to?(:to_h) ? brand.to_h.symbolize_keys : {}
118
- resolved_brand
120
+ brand.respond_to?(:to_h) ? brand.to_h.symbolize_keys : {}
119
121
  end
120
122
  end
121
123
  end
@@ -3,13 +3,15 @@ module Baldur
3
3
  module AuthPageHelper
4
4
  include Baldur::RenderHelper
5
5
 
6
- def ui_auth_page(title:, description:, brand_path: nil, shell_class: nil, card_class: nil, notice: nil, alert: nil, &block)
7
- baldur_render "baldur/optional/auth_page",
6
+ def ui_auth_page(title:, description:, brand_path: nil, shell_class: nil, card_class: nil,
7
+ top_rail: nil, notice: nil, alert: nil, &block)
8
+ baldur_render 'baldur/optional/auth_page',
8
9
  title: title,
9
10
  description: description,
10
11
  brand_path: brand_path,
11
12
  shell_class: shell_class,
12
13
  card_class: card_class,
14
+ top_rail: top_rail,
13
15
  notice: notice,
14
16
  alert: alert,
15
17
  body: block_given? ? capture(&block) : nil
@@ -3,8 +3,8 @@ module Baldur
3
3
  module GoogleAuthHelper
4
4
  include Baldur::RenderHelper
5
5
 
6
- def google_sign_in_button(label: "Sign in with Google", href: nil, block: false, **options)
7
- baldur_render "baldur/optional/google_sign_in_button",
6
+ def google_sign_in_button(label: 'Sign in with Google', href: nil, block: false, **options)
7
+ baldur_render 'baldur/optional/google_sign_in_button',
8
8
  **{
9
9
  label: label,
10
10
  href: href || Baldur.config.default_google_sign_in_path,
@@ -3,8 +3,9 @@ module Baldur
3
3
  module PanelSecondaryHelper
4
4
  include Baldur::RenderHelper
5
5
 
6
- def ui_panel_secondary(id:, title:, trigger_label:, trigger_icon: "message-square", panel_class: nil, trigger_class: nil, shell_class: nil, shell_data: nil, panel_data: nil, trigger_data: nil, &block)
7
- baldur_render "baldur/optional/panel_secondary",
6
+ def ui_panel_secondary(id:, title:, trigger_label:, trigger_icon: 'message-square', panel_class: nil,
7
+ trigger_class: nil, shell_class: nil, shell_data: nil, panel_data: nil, trigger_data: nil, &block)
8
+ baldur_render 'baldur/optional/panel_secondary',
8
9
  id: id,
9
10
  title: title,
10
11
  trigger_label: trigger_label,