baldur 0.2.0 → 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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/TODO.md +7 -1
  4. data/app/assets/stylesheets/baldur/application/components/auth-page.css +7 -0
  5. data/app/assets/tailwind/baldur/engine.css +2 -2
  6. data/app/helpers/baldur/marketing_helper.rb +71 -69
  7. data/app/helpers/baldur/optional/auth_page_helper.rb +4 -2
  8. data/app/helpers/baldur/optional/google_auth_helper.rb +2 -2
  9. data/app/helpers/baldur/optional/panel_secondary_helper.rb +3 -2
  10. data/app/helpers/baldur/ui_helper.rb +151 -138
  11. data/app/helpers/baldur/ui_helper_feedback.rb +38 -36
  12. data/app/helpers/baldur/ui_helper_forms.rb +81 -76
  13. data/app/helpers/baldur/ui_helper_sidebar.rb +6 -5
  14. data/app/helpers/baldur/ui_helper_unavailable.rb +17 -15
  15. data/app/views/baldur/components/_theme_toggle.html.erb +23 -0
  16. data/app/views/baldur/optional/_auth_page.html.erb +6 -0
  17. data/baldur.gemspec +23 -23
  18. data/config/importmap.rb +2 -2
  19. data/lib/baldur/configuration.rb +6 -6
  20. data/lib/baldur/engine.rb +3 -3
  21. data/lib/baldur/version.rb +1 -1
  22. data/lib/baldur.rb +5 -5
  23. data/lib/generators/baldur/install/install_generator.rb +19 -19
  24. data/lib/generators/baldur/install/templates/baldur_initializer.rb +7 -7
  25. data/lib/generators/baldur/install/templates/theme.css +32 -6
  26. data/lib/generators/baldur/install_google_auth/install_google_auth_generator.rb +2 -2
  27. data/lib/generators/baldur/install_panel_right/install_panel_right_generator.rb +2 -2
  28. data/lib/generators/baldur/install_panel_secondary/install_panel_secondary_generator.rb +3 -3
  29. data/script/verify_host_install +59 -31
  30. data/test/confirmation_modal_helper_test.rb +34 -34
  31. data/test/csp_rendering_test.rb +21 -21
  32. data/test/engine_css_test.rb +36 -0
  33. data/test/fixtures/shared/_brand_lockup.html.erb +1 -0
  34. data/test/gemspec_test.rb +7 -5
  35. data/test/install_generator_test.rb +25 -21
  36. data/test/install_panel_secondary_generator_test.rb +12 -9
  37. data/test/marketing_helper_test.rb +7 -7
  38. data/test/run_all.rb +1 -1
  39. data/test/sidebar_helper_test.rb +16 -16
  40. data/test/test_helper.rb +14 -14
  41. data/test/theme_toggle_helper_test.rb +119 -0
  42. data/test/tmp/install_generator/app/assets/stylesheets/theme.css +32 -6
  43. data/test/tmp/install_generator/config/initializers/baldur.rb +7 -7
  44. metadata +5 -1
@@ -1,4 +1,4 @@
1
- require "securerandom"
1
+ require 'securerandom'
2
2
 
3
3
  module Baldur
4
4
  module UiHelper
@@ -9,14 +9,14 @@ module Baldur
9
9
  include Baldur::UiHelperForms
10
10
 
11
11
  def ui_icon(name, class_name: nil)
12
- normalized_name = name.to_s.tr("_", "-")
13
- svg_options = LucideRails.default_options.merge("class" => class_name, "aria-hidden" => "true")
12
+ normalized_name = name.to_s.tr('_', '-')
13
+ svg_options = LucideRails.default_options.merge('class' => class_name, 'aria-hidden' => 'true')
14
14
 
15
15
  content_tag(:svg, LucideRails::IconProvider.icon(normalized_name).html_safe, svg_options)
16
16
  end
17
17
 
18
18
  def ui_button(**options)
19
- baldur_render "baldur/components/button", **options
19
+ baldur_render 'baldur/components/button', **options
20
20
  end
21
21
 
22
22
  def ui_action_row(primary_button:, secondary_button: nil, extra_buttons: [], classes: nil)
@@ -25,208 +25,221 @@ module Baldur
25
25
  buttons.concat(Array(extra_buttons).compact)
26
26
  buttons << primary_button if primary_button.present?
27
27
 
28
- baldur_render "baldur/components/action_row", buttons: buttons, class: classes
28
+ baldur_render 'baldur/components/action_row', buttons: buttons, class: classes
29
29
  end
30
30
 
31
- def ui_card(title:, description: nil, badge: nil, icon: nil, actions: nil, variant: :default, classes: nil, body_class: nil, body: nil, &block)
31
+ def ui_card(title:, description: nil, badge: nil, icon: nil, actions: nil, variant: :default, classes: nil,
32
+ body_class: nil, body: nil, &block)
32
33
  body_content = body
33
34
  body_content = capture(&block) if block_given?
34
- baldur_render "baldur/components/card",
35
- title: title,
36
- description: description,
37
- badge: badge,
38
- icon: icon,
39
- actions: actions,
40
- variant: variant,
41
- class: classes,
42
- body_class: body_class,
43
- body: body_content
44
- end
45
-
46
- def ui_table(columns:, rows:, empty_state: "No records yet", **options)
47
- card_options = options.slice(:title, :title_meta, :description, :actions, :controls, :controls_position, :footer, :classes)
48
- table_options = options.except(:title, :title_meta, :description, :actions, :controls, :controls_position, :footer, :classes)
49
- table_markup = baldur_render "baldur/components/table",
50
- **{
51
- columns: columns,
52
- rows: rows,
53
- empty_state: empty_state
54
- }.merge(table_options)
35
+ baldur_render 'baldur/components/card',
36
+ title: title,
37
+ description: description,
38
+ badge: badge,
39
+ icon: icon,
40
+ actions: actions,
41
+ variant: variant,
42
+ class: classes,
43
+ body_class: body_class,
44
+ body: body_content
45
+ end
46
+
47
+ def ui_table(columns:, rows:, empty_state: 'No records yet', **options)
48
+ card_options = options.slice(:title, :title_meta, :description, :actions, :controls, :controls_position, :footer,
49
+ :classes)
50
+ table_options = options.except(:title, :title_meta, :description, :actions, :controls, :controls_position,
51
+ :footer, :classes)
52
+ table_markup = baldur_render 'baldur/components/table',
53
+ **{
54
+ columns: columns,
55
+ rows: rows,
56
+ empty_state: empty_state
57
+ }.merge(table_options)
55
58
  return table_markup if card_options.values.all?(&:blank?)
56
59
 
57
60
  ui_table_card(**card_options, body: table_markup)
58
61
  end
59
62
 
60
- def ui_table_card(title: nil, title_meta: nil, description: nil, actions: nil, controls: nil, controls_position: :row, footer: nil, classes: nil, body: nil, &block)
63
+ def ui_table_card(title: nil, title_meta: nil, description: nil, actions: nil, controls: nil,
64
+ controls_position: :row, footer: nil, classes: nil, body: nil, &block)
61
65
  body_content = body
62
66
  body_content = capture(&block) if block_given?
63
- baldur_render "baldur/components/table_card",
64
- title: title,
65
- title_meta: title_meta,
66
- description: description,
67
- actions: actions,
68
- controls: controls,
69
- controls_position: controls_position,
70
- footer: footer,
71
- class: classes,
72
- body: body_content
67
+ baldur_render 'baldur/components/table_card',
68
+ title: title,
69
+ title_meta: title_meta,
70
+ description: description,
71
+ actions: actions,
72
+ controls: controls,
73
+ controls_position: controls_position,
74
+ footer: footer,
75
+ class: classes,
76
+ body: body_content
73
77
  end
74
78
 
75
79
  def ui_chart_card(title: nil, description: nil, actions: nil, footer: nil, classes: nil, body: nil, &block)
76
80
  body_content = body
77
81
  body_content = capture(&block) if block_given?
78
- baldur_render "baldur/components/chart_card",
79
- title: title,
80
- description: description,
81
- actions: actions,
82
- footer: footer,
83
- class: classes,
84
- body: body_content
85
- end
86
-
87
- def ui_table_footer(current_page:, total_pages:, path_builder:, total_count: nil, per_page: nil, rows_per_page_param: nil, rows_per_page_options: [], rows_per_page_selected: nil)
88
- baldur_render "baldur/components/table_footer",
89
- current_page: current_page.to_i,
90
- total_pages: total_pages.to_i,
91
- total_count: total_count,
92
- per_page: per_page,
93
- rows_per_page_param: rows_per_page_param,
94
- rows_per_page_options: Array(rows_per_page_options),
95
- rows_per_page_selected: rows_per_page_selected,
96
- path_builder: path_builder
97
- end
98
-
99
- def ui_table_truncated_text(text, class_name: "block truncate", title: nil)
82
+ baldur_render 'baldur/components/chart_card',
83
+ title: title,
84
+ description: description,
85
+ actions: actions,
86
+ footer: footer,
87
+ class: classes,
88
+ body: body_content
89
+ end
90
+
91
+ def ui_table_footer(current_page:, total_pages:, path_builder:, total_count: nil, per_page: nil,
92
+ rows_per_page_param: nil, rows_per_page_options: [], rows_per_page_selected: nil)
93
+ baldur_render 'baldur/components/table_footer',
94
+ current_page: current_page.to_i,
95
+ total_pages: total_pages.to_i,
96
+ total_count: total_count,
97
+ per_page: per_page,
98
+ rows_per_page_param: rows_per_page_param,
99
+ rows_per_page_options: Array(rows_per_page_options),
100
+ rows_per_page_selected: rows_per_page_selected,
101
+ path_builder: path_builder
102
+ end
103
+
104
+ def ui_table_truncated_text(text, class_name: 'block truncate', title: nil)
100
105
  value = text.to_s.strip
101
- return "" if value.blank?
106
+ return '' if value.blank?
102
107
 
103
- content_tag(:span, value, class: class_name, title: (title.presence || value))
108
+ content_tag(:span, value, class: class_name, title: title.presence || value)
104
109
  end
105
110
 
106
111
  def ui_expandable_table_list(items)
107
112
  values = Array(items).compact.map(&:to_s).map(&:strip).reject(&:blank?)
108
- return "" if values.empty?
113
+ return '' if values.empty?
109
114
 
110
- summary = values.join(", ")
115
+ summary = values.join(', ')
111
116
  return ui_table_truncated_text(summary) if values.size <= 2
112
117
 
113
- content_tag(:div, class: "table-disclosure", data: { controller: "table-disclosure" }) do
118
+ content_tag(:div, class: 'table-disclosure', data: { controller: 'table-disclosure' }) do
114
119
  safe_join(
115
120
  [
116
121
  content_tag(
117
122
  :button,
118
- type: "button",
119
- class: "table-disclosure__trigger",
120
- aria: { expanded: "false" },
123
+ type: 'button',
124
+ class: 'table-disclosure__trigger',
125
+ aria: { expanded: 'false' },
121
126
  data: {
122
- action: "click->table-disclosure#toggle",
123
- table_disclosure_target: "trigger"
127
+ action: 'click->table-disclosure#toggle',
128
+ table_disclosure_target: 'trigger'
124
129
  }
125
130
  ) do
126
131
  safe_join(
127
132
  [
128
- content_tag(:span, "#{values.size} items", class: "table-disclosure__summary", title: summary),
129
- content_tag(:span, "Expand", class: "table-disclosure__toggle table-disclosure__toggle--collapsed"),
130
- content_tag(:span, "Collapse", class: "table-disclosure__toggle table-disclosure__toggle--expanded")
133
+ content_tag(:span, "#{values.size} items", class: 'table-disclosure__summary', title: summary),
134
+ content_tag(:span, 'Expand', class: 'table-disclosure__toggle table-disclosure__toggle--collapsed'),
135
+ content_tag(:span, 'Collapse', class: 'table-disclosure__toggle table-disclosure__toggle--expanded')
131
136
  ]
132
137
  )
133
138
  end,
134
- content_tag(:div, class: "table-disclosure__content", data: { table_disclosure_target: "content" }) do
135
- safe_join(values.map { |value| content_tag(:div, value, class: "table-disclosure__content-item") })
139
+ content_tag(:div, class: 'table-disclosure__content', data: { table_disclosure_target: 'content' }) do
140
+ safe_join(values.map { |value| content_tag(:div, value, class: 'table-disclosure__content-item') })
136
141
  end
137
142
  ]
138
143
  )
139
144
  end
140
145
  end
141
146
 
142
- def ui_pagination(current_page:, total_pages:, path_builder:, total_count: nil, per_page: nil, window: 2, rows_per_page_param: nil, rows_per_page_options: [], rows_per_page_selected: nil)
143
- baldur_render "baldur/components/pagination",
144
- current_page: current_page.to_i,
145
- total_pages: total_pages.to_i,
146
- total_count: total_count,
147
- per_page: per_page,
148
- rows_per_page_param: rows_per_page_param,
149
- rows_per_page_options: Array(rows_per_page_options),
150
- rows_per_page_selected: rows_per_page_selected,
151
- path_builder: path_builder,
152
- pages: ui_pagination_pages(current_page: current_page.to_i, total_pages: total_pages.to_i, window: window.to_i)
147
+ def ui_pagination(current_page:, total_pages:, path_builder:, total_count: nil, per_page: nil, window: 2,
148
+ rows_per_page_param: nil, rows_per_page_options: [], rows_per_page_selected: nil)
149
+ baldur_render 'baldur/components/pagination',
150
+ current_page: current_page.to_i,
151
+ total_pages: total_pages.to_i,
152
+ total_count: total_count,
153
+ per_page: per_page,
154
+ rows_per_page_param: rows_per_page_param,
155
+ rows_per_page_options: Array(rows_per_page_options),
156
+ rows_per_page_selected: rows_per_page_selected,
157
+ path_builder: path_builder,
158
+ pages: ui_pagination_pages(current_page: current_page.to_i, total_pages: total_pages.to_i,
159
+ window: window.to_i)
153
160
  end
154
161
 
155
- def ui_modal(id:, title:, description: nil, submit_label: "Continue", close_label: "Cancel", **options, &block)
162
+ def ui_modal(id:, title:, description: nil, submit_label: 'Continue', close_label: 'Cancel', **options, &block)
156
163
  body = block_given? ? capture(&block) : nil
157
- baldur_render "baldur/components/modal",
158
- **{
159
- id: id,
160
- title: title,
161
- description: description,
162
- submit_label: submit_label,
163
- close_label: close_label,
164
- body: body
165
- }.merge(options)
164
+ baldur_render 'baldur/components/modal',
165
+ **{
166
+ id: id,
167
+ title: title,
168
+ description: description,
169
+ submit_label: submit_label,
170
+ close_label: close_label,
171
+ body: body
172
+ }.merge(options)
166
173
  end
167
174
 
168
175
  def ui_modal_host(id:, classes: nil, &block)
169
176
  body = block_given? ? capture(&block) : nil
170
- baldur_render "baldur/components/modal_host",
171
- id: id,
172
- classes: classes,
173
- body: body
177
+ baldur_render 'baldur/components/modal_host',
178
+ id: id,
179
+ classes: classes,
180
+ body: body
174
181
  end
175
182
 
176
- def ui_confirmation_modal(host_id:, dialog_id:, title:, description: nil, tone: :default, confirm_label: "Confirm", cancel_label: "Cancel", confirm_button_options: {}, cancel_button_options: {}, type_to_confirm: nil, body: nil, &block)
183
+ def ui_confirmation_modal(host_id:, dialog_id:, title:, description: nil, tone: :default, confirm_label: 'Confirm',
184
+ cancel_label: 'Cancel', confirm_button_options: {}, cancel_button_options: {}, type_to_confirm: nil, body: nil, &block)
177
185
  body = block_given? ? capture(&block) : body
178
186
  type_to_confirm = nil unless type_to_confirm.is_a?(Hash)
179
- baldur_render "baldur/components/confirmation_modal",
180
- host_id: host_id,
181
- dialog_id: dialog_id,
182
- title: title,
183
- description: description,
184
- tone: tone,
185
- confirm_label: confirm_label,
186
- cancel_label: cancel_label,
187
- confirm_button_options: confirm_button_options,
188
- cancel_button_options: cancel_button_options,
189
- type_to_confirm: type_to_confirm,
190
- body: body
187
+ baldur_render 'baldur/components/confirmation_modal',
188
+ host_id: host_id,
189
+ dialog_id: dialog_id,
190
+ title: title,
191
+ description: description,
192
+ tone: tone,
193
+ confirm_label: confirm_label,
194
+ cancel_label: cancel_label,
195
+ confirm_button_options: confirm_button_options,
196
+ cancel_button_options: cancel_button_options,
197
+ type_to_confirm: type_to_confirm,
198
+ body: body
191
199
  end
192
200
 
193
201
  def ui_badge(text:, variant: :default, size: :sm, html_options: {})
194
- baldur_render "baldur/components/badge", text: text, variant: variant, size: size, html_options: html_options
202
+ baldur_render 'baldur/components/badge', text: text, variant: variant, size: size, html_options: html_options
203
+ end
204
+
205
+ def ui_kpi(label:, value:, id: nil, caption: nil, trend: nil, variant: :default, caption_icon: nil, action: nil)
206
+ baldur_render 'baldur/components/kpi',
207
+ id: id,
208
+ label: label,
209
+ value: value,
210
+ caption: caption,
211
+ trend: trend,
212
+ variant: variant,
213
+ caption_icon: caption_icon,
214
+ action: action
195
215
  end
196
216
 
197
- def ui_kpi(id: nil, label:, value:, caption: nil, trend: nil, variant: :default, caption_icon: nil, action: nil)
198
- baldur_render "baldur/components/kpi",
199
- id: id,
200
- label: label,
201
- value: value,
202
- caption: caption,
203
- trend: trend,
204
- variant: variant,
205
- caption_icon: caption_icon,
206
- action: action
217
+ def ui_segmented_buttons(items:, aria_label: 'Tabs', classes: nil)
218
+ baldur_render 'baldur/components/segmented_buttons', items: items, aria_label: aria_label, classes: classes
207
219
  end
208
220
 
209
- def ui_segmented_buttons(items:, aria_label: "Tabs", classes: nil)
210
- baldur_render "baldur/components/segmented_buttons", items: items, aria_label: aria_label, classes: classes
221
+ def ui_tooltip(text:, content:, show_icon: true, icon: 'circle-help', variant: :link, wrapper_class: nil,
222
+ trigger_class: nil, bubble_class: nil, inline: false)
223
+ baldur_render 'baldur/components/tooltip',
224
+ text: text,
225
+ content: content,
226
+ show_icon: show_icon,
227
+ icon: icon,
228
+ variant: variant,
229
+ wrapper_class: wrapper_class,
230
+ trigger_class: trigger_class,
231
+ bubble_class: bubble_class,
232
+ inline: inline
211
233
  end
212
234
 
213
- def ui_tooltip(text:, content:, show_icon: true, icon: "circle-help", variant: :link, wrapper_class: nil, trigger_class: nil, bubble_class: nil, inline: false)
214
- baldur_render "baldur/components/tooltip",
215
- text: text,
216
- content: content,
217
- show_icon: show_icon,
218
- icon: icon,
219
- variant: variant,
220
- wrapper_class: wrapper_class,
221
- trigger_class: trigger_class,
222
- bubble_class: bubble_class,
223
- inline: inline
235
+ def ui_theme_toggle(aria_label: "Toggle theme", classes: nil)
236
+ baldur_render 'baldur/components/theme_toggle', aria_label: aria_label, classes: classes
224
237
  end
225
238
 
226
239
  def ui_pagination_pages(current_page:, total_pages:, window:)
227
240
  return [] if total_pages <= 1
228
241
 
229
- raw_pages = [ 1 ]
242
+ raw_pages = [1]
230
243
  raw_pages.concat(((current_page - window)..(current_page + window)).to_a)
231
244
  raw_pages << total_pages
232
245
  normalized = raw_pages.select { |page| page.between?(1, total_pages) }.uniq.sort
@@ -1,44 +1,46 @@
1
1
  module Baldur
2
2
  module UiHelperFeedback
3
- def ui_alert(body: nil, variant: :notice, title: nil, icon: nil, actions: nil, collapsible: false, collapse_key: nil, collapsed_summary_action_label: "More", class_name: nil, &block)
3
+ def ui_alert(body: nil, variant: :notice, title: nil, icon: nil, actions: nil, collapsible: false,
4
+ collapse_key: nil, collapsed_summary_action_label: 'More', class_name: nil, &block)
4
5
  content = body
5
6
  content = capture(&block) if block_given?
6
7
  storage_key = collapsible ? ui_alert_storage_key(collapse_key) : nil
7
8
 
8
- baldur_render "baldur/components/alert",
9
- variant: variant,
10
- title: title,
11
- icon: icon,
12
- body: content,
13
- actions: actions,
14
- collapsible: collapsible,
15
- collapse_storage_key: storage_key,
16
- collapsed: collapsible && ui_alert_collapsed?(storage_key),
17
- collapsed_summary_action_label: collapsed_summary_action_label,
18
- class_name: class_name
9
+ baldur_render 'baldur/components/alert',
10
+ variant: variant,
11
+ title: title,
12
+ icon: icon,
13
+ body: content,
14
+ actions: actions,
15
+ collapsible: collapsible,
16
+ collapse_storage_key: storage_key,
17
+ collapsed: collapsible && ui_alert_collapsed?(storage_key),
18
+ collapsed_summary_action_label: collapsed_summary_action_label,
19
+ class_name: class_name
19
20
  end
20
21
 
21
22
  def ui_snackbar_stack(snackbars: [])
22
- baldur_render "baldur/components/snackbar_stack", snackbars: normalize_snackbars(snackbars)
23
+ baldur_render 'baldur/components/snackbar_stack', snackbars: normalize_snackbars(snackbars)
23
24
  end
24
25
 
25
- def ui_checkbox_tag(name, label: nil, description: nil, value: "1", checked: false, required: false, disabled: false, id: nil, data: nil, aria: nil, form: nil, wrapper_class: nil, input_class: nil, &block)
26
+ def ui_checkbox_tag(name, label: nil, description: nil, value: '1', checked: false, required: false,
27
+ disabled: false, id: nil, data: nil, aria: nil, form: nil, wrapper_class: nil, input_class: nil, &block)
26
28
  body = block_given? ? capture(&block) : nil
27
- baldur_render "baldur/components/checkbox",
28
- name: name,
29
- label: label,
30
- description: description,
31
- value: value,
32
- checked: checked,
33
- required: required,
34
- disabled: disabled,
35
- id: id,
36
- data: data,
37
- aria: aria,
38
- form: form,
39
- wrapper_class: wrapper_class,
40
- input_class: input_class,
41
- body: body
29
+ baldur_render 'baldur/components/checkbox',
30
+ name: name,
31
+ label: label,
32
+ description: description,
33
+ value: value,
34
+ checked: checked,
35
+ required: required,
36
+ disabled: disabled,
37
+ id: id,
38
+ data: data,
39
+ aria: aria,
40
+ form: form,
41
+ wrapper_class: wrapper_class,
42
+ input_class: input_class,
43
+ body: body
42
44
  end
43
45
 
44
46
  private
@@ -54,12 +56,12 @@ module Baldur
54
56
 
55
57
  def normalize_snackbar_payload(snackbar)
56
58
  payload = if snackbar.respond_to?(:with_indifferent_access)
57
- snackbar.with_indifferent_access
58
- elsif snackbar.respond_to?(:to_h)
59
- snackbar.to_h.with_indifferent_access
60
- else
61
- { message: snackbar }.with_indifferent_access
62
- end
59
+ snackbar.with_indifferent_access
60
+ elsif snackbar.respond_to?(:to_h)
61
+ snackbar.to_h.with_indifferent_access
62
+ else
63
+ { message: snackbar }.with_indifferent_access
64
+ end
63
65
 
64
66
  {
65
67
  variant: (payload[:variant].presence || :notice).to_sym,
@@ -85,7 +87,7 @@ module Baldur
85
87
  def ui_alert_collapsed?(storage_key)
86
88
  return false if storage_key.blank?
87
89
 
88
- cookies[storage_key] == "true"
90
+ cookies[storage_key] == 'true'
89
91
  rescue NoMethodError
90
92
  false
91
93
  end