baldur 0.1.1
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 +7 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +318 -0
- data/TODO.md +6 -0
- data/app/assets/javascripts/baldur/controllers/accordion_controller.js +148 -0
- data/app/assets/javascripts/baldur/controllers/alert_controller.js +209 -0
- data/app/assets/javascripts/baldur/controllers/date_field_controller.js +558 -0
- data/app/assets/javascripts/baldur/controllers/details_menu_controller.js +30 -0
- data/app/assets/javascripts/baldur/controllers/form_submit_controller.js +7 -0
- data/app/assets/javascripts/baldur/controllers/marketing_pricing_controller.js +47 -0
- data/app/assets/javascripts/baldur/controllers/marketing_tabs_controller.js +118 -0
- data/app/assets/javascripts/baldur/controllers/menu_select_controller.js +401 -0
- data/app/assets/javascripts/baldur/controllers/mobile_sidebar_controller.js +13 -0
- data/app/assets/javascripts/baldur/controllers/modal_controller.js +149 -0
- data/app/assets/javascripts/baldur/controllers/panel_right_controller.js +1 -0
- data/app/assets/javascripts/baldur/controllers/panel_secondary_controller.js +129 -0
- data/app/assets/javascripts/baldur/controllers/segmented_tabs_controller.js +38 -0
- data/app/assets/javascripts/baldur/controllers/sidebar_controller.js +77 -0
- data/app/assets/javascripts/baldur/controllers/smooth_scroll_controller.js +29 -0
- data/app/assets/javascripts/baldur/controllers/snackbar_controller.js +158 -0
- data/app/assets/javascripts/baldur/controllers/table_disclosure_controller.js +46 -0
- data/app/assets/javascripts/baldur/controllers/theme_controller.js +90 -0
- data/app/assets/javascripts/baldur/controllers/tooltip_controller.js +136 -0
- data/app/assets/javascripts/baldur/lib/animation-helpers.js +56 -0
- data/app/assets/javascripts/baldur/lib/dom-helpers.js +80 -0
- data/app/assets/javascripts/baldur/lib/field-validation-helpers.js +36 -0
- data/app/assets/javascripts/baldur/lib/focus-management.js +89 -0
- data/app/assets/javascripts/baldur/lib/formatting-helpers.js +100 -0
- data/app/assets/javascripts/baldur/lib/lucide.js +20 -0
- data/app/assets/javascripts/baldur/lib/snackbar.js +50 -0
- data/app/assets/javascripts/baldur/lib/storage-helpers.js +50 -0
- data/app/assets/stylesheets/baldur/application/components/alert.css +226 -0
- data/app/assets/stylesheets/baldur/application/components/app_bar.css +41 -0
- data/app/assets/stylesheets/baldur/application/components/button.css +173 -0
- data/app/assets/stylesheets/baldur/application/components/card.css +63 -0
- data/app/assets/stylesheets/baldur/application/components/chart.css +40 -0
- data/app/assets/stylesheets/baldur/application/components/chip.css +51 -0
- data/app/assets/stylesheets/baldur/application/components/dialog.css +81 -0
- data/app/assets/stylesheets/baldur/application/components/forms.css +624 -0
- data/app/assets/stylesheets/baldur/application/components/layout.css +2 -0
- data/app/assets/stylesheets/baldur/application/components/list.css +15 -0
- data/app/assets/stylesheets/baldur/application/components/menu.css +300 -0
- data/app/assets/stylesheets/baldur/application/components/panel-right.css +1 -0
- data/app/assets/stylesheets/baldur/application/components/panel-secondary.css +71 -0
- data/app/assets/stylesheets/baldur/application/components/progress.css +84 -0
- data/app/assets/stylesheets/baldur/application/components/segmented-buttons.css +117 -0
- data/app/assets/stylesheets/baldur/application/components/settings-nav.css +84 -0
- data/app/assets/stylesheets/baldur/application/components/sidebar.css +123 -0
- data/app/assets/stylesheets/baldur/application/components/snackbar.css +179 -0
- data/app/assets/stylesheets/baldur/application/components/stepper.css +124 -0
- data/app/assets/stylesheets/baldur/application/components/switch.css +105 -0
- data/app/assets/stylesheets/baldur/application/components/table.css +331 -0
- data/app/assets/stylesheets/baldur/application/components/timeline.css +184 -0
- data/app/assets/stylesheets/baldur/application/components/utilities.css +180 -0
- data/app/assets/stylesheets/baldur/application/global.css +125 -0
- data/app/assets/stylesheets/baldur/application/marketing/layout.css +36 -0
- data/app/assets/stylesheets/baldur/application/motion.css +125 -0
- data/app/assets/stylesheets/baldur/application/theme.css +329 -0
- data/app/assets/stylesheets/baldur/theme/dark.css +90 -0
- data/app/assets/stylesheets/baldur/theme/light.css +82 -0
- data/app/assets/stylesheets/baldur.css +27 -0
- data/app/assets/stylesheets/baldur_panel_right.css +1 -0
- data/app/assets/stylesheets/baldur_panel_secondary.css +1 -0
- data/app/assets/tailwind/baldur/engine.css +5 -0
- data/app/helpers/baldur/compatibility/ui_aliases.rb +7 -0
- data/app/helpers/baldur/marketing_helper.rb +121 -0
- data/app/helpers/baldur/optional/auth_page_helper.rb +17 -0
- data/app/helpers/baldur/optional/google_auth_helper.rb +16 -0
- data/app/helpers/baldur/optional/panel_right_helper.rb +7 -0
- data/app/helpers/baldur/optional/panel_secondary_helper.rb +26 -0
- data/app/helpers/baldur/render_helper.rb +13 -0
- data/app/helpers/baldur/ui_helper.rb +217 -0
- data/app/helpers/baldur/ui_helper_feedback.rb +93 -0
- data/app/helpers/baldur/ui_helper_forms.rb +230 -0
- data/app/helpers/baldur/ui_helper_unavailable.rb +98 -0
- data/app/views/baldur/components/_accordion.html.erb +30 -0
- data/app/views/baldur/components/_action_row.html.erb +6 -0
- data/app/views/baldur/components/_alert.html.erb +61 -0
- data/app/views/baldur/components/_badge.html.erb +25 -0
- data/app/views/baldur/components/_button.html.erb +81 -0
- data/app/views/baldur/components/_card.html.erb +40 -0
- data/app/views/baldur/components/_chart_card.html.erb +42 -0
- data/app/views/baldur/components/_checkbox.html.erb +27 -0
- data/app/views/baldur/components/_date_field.html.erb +43 -0
- data/app/views/baldur/components/_google_sign_in_button.html.erb +1 -0
- data/app/views/baldur/components/_kebab_menu.html.erb +36 -0
- data/app/views/baldur/components/_kpi.html.erb +45 -0
- data/app/views/baldur/components/_menu_select.html.erb +78 -0
- data/app/views/baldur/components/_modal.html.erb +54 -0
- data/app/views/baldur/components/_pagination.html.erb +61 -0
- data/app/views/baldur/components/_segmented_buttons.html.erb +51 -0
- data/app/views/baldur/components/_settings_nav.html.erb +41 -0
- data/app/views/baldur/components/_snackbar.html.erb +42 -0
- data/app/views/baldur/components/_snackbar_stack.html.erb +13 -0
- data/app/views/baldur/components/_stepper.html.erb +39 -0
- data/app/views/baldur/components/_table.html.erb +117 -0
- data/app/views/baldur/components/_table_card.html.erb +86 -0
- data/app/views/baldur/components/_table_footer.html.erb +68 -0
- data/app/views/baldur/components/_text_field.html.erb +33 -0
- data/app/views/baldur/components/_tooltip.html.erb +73 -0
- data/app/views/baldur/marketing/_cta_banner.html.erb +20 -0
- data/app/views/baldur/marketing/_faq_section.html.erb +37 -0
- data/app/views/baldur/marketing/_features_section.html.erb +67 -0
- data/app/views/baldur/marketing/_footer.html.erb +38 -0
- data/app/views/baldur/marketing/_hero_section.html.erb +259 -0
- data/app/views/baldur/marketing/_pricing_tables.html.erb +99 -0
- data/app/views/baldur/marketing/_testimonials_section.html.erb +80 -0
- data/app/views/baldur/marketing/_top_nav.html.erb +28 -0
- data/app/views/baldur/optional/_auth_page.html.erb +21 -0
- data/app/views/baldur/optional/_google_sign_in_button.html.erb +19 -0
- data/app/views/baldur/optional/_panel_right.html.erb +1 -0
- data/app/views/baldur/optional/_panel_secondary.html.erb +34 -0
- data/baldur.gemspec +30 -0
- data/config/importmap.rb +2 -0
- data/lib/baldur/configuration.rb +24 -0
- data/lib/baldur/engine.rb +10 -0
- data/lib/baldur/version.rb +3 -0
- data/lib/baldur.rb +17 -0
- data/lib/generators/baldur/install/install_generator.rb +113 -0
- data/lib/generators/baldur/install/templates/baldur_initializer.rb +19 -0
- data/lib/generators/baldur/install/templates/fonts.css +14 -0
- data/lib/generators/baldur/install/templates/theme.css +27 -0
- data/lib/generators/baldur/install/templates/ui_helper.rb +4 -0
- data/lib/generators/baldur/install_google_auth/install_google_auth_generator.rb +15 -0
- data/lib/generators/baldur/install_panel_right/install_panel_right_generator.rb +9 -0
- data/lib/generators/baldur/install_panel_secondary/install_panel_secondary_generator.rb +21 -0
- data/script/verify_host_install +111 -0
- data/test/gemspec_test.rb +11 -0
- data/test/install_generator_test.rb +35 -0
- data/test/install_panel_secondary_generator_test.rb +21 -0
- data/test/marketing_helper_test.rb +38 -0
- data/test/run_all.rb +3 -0
- data/test/test_helper.rb +9 -0
- data/test/tmp/install_generator/app/assets/stylesheets/fonts.css +14 -0
- data/test/tmp/install_generator/app/assets/stylesheets/theme.css +27 -0
- data/test/tmp/install_generator/app/assets/tailwind/application.css +4 -0
- data/test/tmp/install_generator/app/helpers/ui_helper.rb +4 -0
- data/test/tmp/install_generator/app/javascript/controllers/accordion_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/date_field_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/details_menu_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/form_submit_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/marketing_pricing_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/marketing_tabs_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/menu_select_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/modal_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/segmented_tabs_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/sidebar_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/smooth_scroll_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/snackbar_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/theme_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/controllers/tooltip_controller.js +1 -0
- data/test/tmp/install_generator/app/javascript/lib/animation-helpers.js +1 -0
- data/test/tmp/install_generator/app/javascript/lib/dom-helpers.js +1 -0
- data/test/tmp/install_generator/app/javascript/lib/field-validation-helpers.js +1 -0
- data/test/tmp/install_generator/app/javascript/lib/focus-management.js +1 -0
- data/test/tmp/install_generator/app/javascript/lib/formatting-helpers.js +1 -0
- data/test/tmp/install_generator/app/javascript/lib/snackbar.js +1 -0
- data/test/tmp/install_generator/app/javascript/lib/storage-helpers.js +1 -0
- data/test/tmp/install_generator/config/initializers/baldur.rb +19 -0
- data/test/tmp/install_panel_secondary_generator/app/assets/tailwind/application.css +2 -0
- data/test/tmp/install_panel_secondary_generator/app/helpers/panel_secondary_helper.rb +3 -0
- data/test/tmp/install_panel_secondary_generator/app/javascript/controllers/panel_secondary_controller.js +1 -0
- metadata +259 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<%
|
|
2
|
+
actions_content = if actions.respond_to?(:call)
|
|
3
|
+
capture(&actions)
|
|
4
|
+
else
|
|
5
|
+
actions
|
|
6
|
+
end
|
|
7
|
+
title_meta_content = if title_meta.respond_to?(:call)
|
|
8
|
+
capture(&title_meta)
|
|
9
|
+
else
|
|
10
|
+
title_meta
|
|
11
|
+
end
|
|
12
|
+
controls_content = if controls.respond_to?(:call)
|
|
13
|
+
capture(&controls)
|
|
14
|
+
else
|
|
15
|
+
controls
|
|
16
|
+
end
|
|
17
|
+
footer_content = if footer.respond_to?(:call)
|
|
18
|
+
capture(&footer)
|
|
19
|
+
else
|
|
20
|
+
footer
|
|
21
|
+
end
|
|
22
|
+
controls_position = local_assigns[:controls_position].to_s == "header" ? "header" : "row"
|
|
23
|
+
header_controls_content = controls_position == "header" ? controls_content : nil
|
|
24
|
+
row_controls_content = controls_position == "row" ? controls_content : nil
|
|
25
|
+
header_present = title.present? || title_meta_content.present? || description.present? || actions_content.present? || header_controls_content.present?
|
|
26
|
+
controls_present = row_controls_content.present?
|
|
27
|
+
footer_present = footer_content.present?
|
|
28
|
+
card_classes = ["table-card", local_assigns[:class]].compact.join(" ")
|
|
29
|
+
%>
|
|
30
|
+
<div class="<%= card_classes %>">
|
|
31
|
+
<% if header_present %>
|
|
32
|
+
<div class="table-card__header table-card__section--top">
|
|
33
|
+
<div class="table-card__header-main">
|
|
34
|
+
<% if title.present? || title_meta_content.present? %>
|
|
35
|
+
<div class="table-card__header-title-row">
|
|
36
|
+
<% if title.present? %>
|
|
37
|
+
<h3 class="text-lg font-semibold text-[color:var(--color-on-surface)]"><%= title %></h3>
|
|
38
|
+
<% end %>
|
|
39
|
+
<% if title_meta_content.present? %>
|
|
40
|
+
<div class="table-card__title-meta"><%= title_meta_content %></div>
|
|
41
|
+
<% end %>
|
|
42
|
+
</div>
|
|
43
|
+
<% end %>
|
|
44
|
+
<% if description.present? %>
|
|
45
|
+
<p class="text-sm text-muted"><%= description %></p>
|
|
46
|
+
<% end %>
|
|
47
|
+
</div>
|
|
48
|
+
<% if actions_content.present? || header_controls_content.present? %>
|
|
49
|
+
<div class="table-card__header-side">
|
|
50
|
+
<% if actions_content.present? %>
|
|
51
|
+
<div class="table-card__header-actions">
|
|
52
|
+
<%= actions_content %>
|
|
53
|
+
</div>
|
|
54
|
+
<% end %>
|
|
55
|
+
<% if header_controls_content.present? %>
|
|
56
|
+
<div class="table-card__header-controls">
|
|
57
|
+
<%= header_controls_content %>
|
|
58
|
+
</div>
|
|
59
|
+
<% end %>
|
|
60
|
+
</div>
|
|
61
|
+
<% end %>
|
|
62
|
+
</div>
|
|
63
|
+
<% end %>
|
|
64
|
+
|
|
65
|
+
<% if controls_present %>
|
|
66
|
+
<% controls_classes = ["table-card__controls"] %>
|
|
67
|
+
<% controls_classes << "table-card__section--top" unless header_present %>
|
|
68
|
+
<% controls_classes << "table-card__section--bottom" unless footer_present %>
|
|
69
|
+
<div class="<%= controls_classes.join(" ") %>">
|
|
70
|
+
<%= row_controls_content %>
|
|
71
|
+
</div>
|
|
72
|
+
<% end %>
|
|
73
|
+
|
|
74
|
+
<% body_classes = ["table-card__body"] %>
|
|
75
|
+
<% body_classes << "table-card__section--top" unless header_present || controls_present %>
|
|
76
|
+
<% body_classes << "table-card__section--bottom" unless footer_present %>
|
|
77
|
+
<div class="<%= body_classes.join(" ") %>">
|
|
78
|
+
<%= body %>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<% if footer_present %>
|
|
82
|
+
<div class="table-card__footer table-card__section--bottom">
|
|
83
|
+
<%= footer_content %>
|
|
84
|
+
</div>
|
|
85
|
+
<% end %>
|
|
86
|
+
</div>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<% require "uri" %>
|
|
2
|
+
<%
|
|
3
|
+
rows_param = rows_per_page_param.to_s.presence
|
|
4
|
+
rows_options = Array(rows_per_page_options).map(&:to_i).select(&:positive?).uniq
|
|
5
|
+
rows_selected = rows_per_page_selected.to_i
|
|
6
|
+
rows_selected = per_page.to_i if rows_selected <= 0
|
|
7
|
+
rows_enabled = rows_param.present? && rows_options.present?
|
|
8
|
+
pagination_enabled = total_pages.to_i > 1
|
|
9
|
+
return unless rows_enabled || pagination_enabled
|
|
10
|
+
rows_selected = rows_options.first if rows_enabled && !rows_options.include?(rows_selected)
|
|
11
|
+
base_path = begin
|
|
12
|
+
path_builder.call(current_page)
|
|
13
|
+
rescue
|
|
14
|
+
nil
|
|
15
|
+
end
|
|
16
|
+
pagination_page_param = base_path.present? ? Rack::Utils.parse_query(URI.parse(base_path).query.to_s).keys.find { |key| key.to_s.include?("page") } : nil
|
|
17
|
+
pagination_page_param ||= "page"
|
|
18
|
+
rows_form_action = base_path.present? ? URI.parse(base_path).path.presence || request.path : request.path
|
|
19
|
+
rows_form_hidden_params = request.query_parameters.except(rows_param, pagination_page_param)
|
|
20
|
+
showing_from = if total_count.to_i.positive? && per_page.to_i.positive?
|
|
21
|
+
((current_page - 1) * per_page) + 1
|
|
22
|
+
else
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
25
|
+
showing_to = if total_count.to_i.positive? && per_page.to_i.positive?
|
|
26
|
+
[ current_page * per_page, total_count ].min
|
|
27
|
+
else
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
%>
|
|
31
|
+
|
|
32
|
+
<div class="flex flex-wrap items-start justify-between gap-3">
|
|
33
|
+
<div class="flex min-w-0 flex-col gap-2">
|
|
34
|
+
<% if rows_enabled %>
|
|
35
|
+
<%= form_with url: rows_form_action, method: :get, local: true, class: "flex flex-wrap items-center gap-2 text-xs text-muted", data: { controller: "form-submit" }, html: { aria: { label: "Items per page" } } do %>
|
|
36
|
+
<% rows_form_hidden_params.each do |key, value| %>
|
|
37
|
+
<% Array(value).each do |entry| %>
|
|
38
|
+
<%= hidden_field_tag key, entry %>
|
|
39
|
+
<% end %>
|
|
40
|
+
<% end %>
|
|
41
|
+
<%= hidden_field_tag pagination_page_param, 1 %>
|
|
42
|
+
<span>Show</span>
|
|
43
|
+
<%= ui_menu_select_tag rows_param,
|
|
44
|
+
options: rows_options.map { |option| { value: option.to_s, label: option.to_s } },
|
|
45
|
+
selected: rows_selected.to_s,
|
|
46
|
+
wrapper_class: "w-24",
|
|
47
|
+
input_data: { action: "change->form-submit#submit" } %>
|
|
48
|
+
<span>items per page</span>
|
|
49
|
+
<% end %>
|
|
50
|
+
<% end %>
|
|
51
|
+
|
|
52
|
+
<div class="text-xs text-muted">
|
|
53
|
+
<% if showing_from && showing_to %>
|
|
54
|
+
Showing <%= showing_from %>-<%= showing_to %> of <%= total_count %>
|
|
55
|
+
<% else %>
|
|
56
|
+
Page <%= current_page %> of <%= total_pages %>
|
|
57
|
+
<% end %>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<% if pagination_enabled %>
|
|
62
|
+
<%= ui_pagination(
|
|
63
|
+
current_page: current_page,
|
|
64
|
+
total_pages: total_pages,
|
|
65
|
+
path_builder: path_builder
|
|
66
|
+
) %>
|
|
67
|
+
<% end %>
|
|
68
|
+
</div>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<div class="<%= wrapper_classes %>">
|
|
2
|
+
<% if label.present? %>
|
|
3
|
+
<label class="field__label" for="<%= input_options[:id] %>"><%= label %></label>
|
|
4
|
+
<% end %>
|
|
5
|
+
<div class="text-field__input">
|
|
6
|
+
<% if prefix.present? %>
|
|
7
|
+
<span class="text-field__prefix"><%= prefix %></span>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% if multiline %>
|
|
10
|
+
<%= tag.textarea(input_value.to_s, **input_options.except(:type, :value)) %>
|
|
11
|
+
<% else %>
|
|
12
|
+
<%= tag.input(**input_options) %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% if suffix.present? %>
|
|
15
|
+
<span class="text-field__suffix"><%= suffix %></span>
|
|
16
|
+
<% end %>
|
|
17
|
+
</div>
|
|
18
|
+
<%
|
|
19
|
+
support_fragments = []
|
|
20
|
+
support_fragments << supporting_text if supporting_text.present?
|
|
21
|
+
support_fragments << supporting_slot if supporting_slot.present?
|
|
22
|
+
support_output = safe_join(support_fragments, " ")
|
|
23
|
+
support_hidden = support_output.blank?
|
|
24
|
+
%>
|
|
25
|
+
<p class="field__supporting"
|
|
26
|
+
id="<%= support_id %>"
|
|
27
|
+
data-field-support="true"
|
|
28
|
+
data-default-support="<%= supporting_text.to_s %>"
|
|
29
|
+
aria-live="polite"
|
|
30
|
+
<%= "hidden" if support_hidden %>>
|
|
31
|
+
<%= support_output %>
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<% tooltip_id = "tooltip-#{SecureRandom.hex(4)}" %>
|
|
2
|
+
<% inline = !!local_assigns[:inline] %>
|
|
3
|
+
<% wrapper_tag = inline ? :span : :div %>
|
|
4
|
+
<% bubble_tag = inline ? :span : :div %>
|
|
5
|
+
<% wrapper_classes = ["relative inline-flex items-center group", ("align-middle shrink-0" if inline), wrapper_class].compact.join(" ") %>
|
|
6
|
+
<% icon_variant = variant.to_s == "icon" %>
|
|
7
|
+
<% trigger_classes = if icon_variant
|
|
8
|
+
[
|
|
9
|
+
"inline-flex items-center justify-center rounded-full p-1 text-muted hover:text-base-content",
|
|
10
|
+
("align-middle leading-none shrink-0" if inline),
|
|
11
|
+
"focus:outline-none focus:ring-2 focus:ring-primary/30",
|
|
12
|
+
trigger_class
|
|
13
|
+
].compact.join(" ")
|
|
14
|
+
else
|
|
15
|
+
[
|
|
16
|
+
"inline-flex items-center gap-1 text-sm text-on-surface/70 cursor-pointer",
|
|
17
|
+
"border-b border-dashed border-current pb-0.5",
|
|
18
|
+
"transition-colors hover:text-on-surface",
|
|
19
|
+
trigger_class
|
|
20
|
+
].compact.join(" ")
|
|
21
|
+
end %>
|
|
22
|
+
<% bubble_classes = [
|
|
23
|
+
"absolute left-0 top-full z-30 mt-2 hidden w-max max-w-[90vw] sm:max-w-[32rem] rounded-lg",
|
|
24
|
+
"border border-[color:var(--color-outline)]",
|
|
25
|
+
"bg-[color:var(--color-inverse-surface)]",
|
|
26
|
+
"text-[color:var(--color-inverse-on-surface)]",
|
|
27
|
+
"text-left whitespace-normal break-words",
|
|
28
|
+
"dark:bg-[color:var(--color-surface-lowest)]",
|
|
29
|
+
"dark:text-[color:var(--color-on-surface)]",
|
|
30
|
+
"p-3 text-sm font-normal",
|
|
31
|
+
"shadow-[var(--elev-1)]",
|
|
32
|
+
(inline ? "data-[state=open]:inline-block" : "data-[state=open]:block"),
|
|
33
|
+
bubble_class
|
|
34
|
+
].compact.join(" ") %>
|
|
35
|
+
|
|
36
|
+
<%= content_tag wrapper_tag,
|
|
37
|
+
class: wrapper_classes,
|
|
38
|
+
data: {
|
|
39
|
+
controller: "tooltip",
|
|
40
|
+
action: "mouseenter->tooltip#open mouseleave->tooltip#close focusin->tooltip#open focusout->tooltip#close"
|
|
41
|
+
} do %>
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
class="<%= trigger_classes %>"
|
|
45
|
+
data-tooltip-target="trigger"
|
|
46
|
+
data-action="click->tooltip#toggle"
|
|
47
|
+
aria-describedby="<%= tooltip_id %>"
|
|
48
|
+
aria-expanded="false"
|
|
49
|
+
>
|
|
50
|
+
<% if icon_variant %>
|
|
51
|
+
<span class="sr-only"><%= text %></span>
|
|
52
|
+
<%= ui_icon(icon, class_name: "h-4 w-4") %>
|
|
53
|
+
<% else %>
|
|
54
|
+
<span><%= text %></span>
|
|
55
|
+
<% end %>
|
|
56
|
+
<% if show_icon && !icon_variant %>
|
|
57
|
+
<%= ui_icon(icon, class_name: "h-4 w-4") %>
|
|
58
|
+
<% end %>
|
|
59
|
+
</button>
|
|
60
|
+
<%= content_tag bubble_tag,
|
|
61
|
+
content,
|
|
62
|
+
id: tooltip_id,
|
|
63
|
+
role: "tooltip",
|
|
64
|
+
class: bubble_classes,
|
|
65
|
+
style: "text-align: left;",
|
|
66
|
+
data: {
|
|
67
|
+
tooltip_target: "bubble",
|
|
68
|
+
state: "closed"
|
|
69
|
+
},
|
|
70
|
+
aria: {
|
|
71
|
+
hidden: "true"
|
|
72
|
+
} %>
|
|
73
|
+
<% end %>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<%
|
|
2
|
+
payload = local_assigns.fetch(:banner)
|
|
3
|
+
section_attributes = {
|
|
4
|
+
class: ["marketing-cta-banner px-12 py-16 sm:px-12", local_assigns[:classes]].compact.join(" ")
|
|
5
|
+
}
|
|
6
|
+
section_attributes[:id] = payload[:id] if payload[:id].present?
|
|
7
|
+
%>
|
|
8
|
+
<section <%= tag.attributes(section_attributes) %>>
|
|
9
|
+
<div class="mx-auto flex max-w-6xl flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
|
10
|
+
<div class="max-w-3xl">
|
|
11
|
+
<h2 class="text-neutral-50"><%= payload[:title] %></h2>
|
|
12
|
+
<p class="mt-4 text-neutral-50"><%= payload[:body] %></p>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="flex flex-wrap gap-3">
|
|
15
|
+
<%= link_to payload[:cta_label],
|
|
16
|
+
payload[:cta_href],
|
|
17
|
+
class: "inline-flex h-11 items-center justify-center rounded-[1rem] bg-white px-5 text-base font-semibold text-[#1f1f27] shadow-[var(--elev-1)] transition-transform duration-150 hover:-translate-y-0.5" %>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</section>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<section id="<%= id %>" class="<%= ["marketing-faq-section bg-surface px-6 py-16 sm:px-12", local_assigns[:classes]].compact.join(" ") %>">
|
|
2
|
+
<div class="<%= ["mx-auto", (local_assigns[:container_class].presence || "max-w-4xl")].join(" ") %>">
|
|
3
|
+
<h2><%= title %></h2>
|
|
4
|
+
<% if description.present? %>
|
|
5
|
+
<p class="mt-3 text-soft"><%= description %></p>
|
|
6
|
+
<% end %>
|
|
7
|
+
<div class="mt-8 rounded-3xl border border-outline bg-surface-high px-6" data-controller="accordion">
|
|
8
|
+
<% Array(items).each_with_index do |item, index| %>
|
|
9
|
+
<% button_action = ui_marketing_action_string("click->accordion#toggle", item[:button_action], item[:button_actions]) %>
|
|
10
|
+
<div data-accordion-target="item" data-open="<%= item.key?(:open) ? !!item[:open] : index.zero? %>" class="<%= index.zero? ? "" : "border-t border-outline" %>">
|
|
11
|
+
<button
|
|
12
|
+
type="button"
|
|
13
|
+
class="flex w-full items-center gap-2 py-6 text-left text-lg font-semibold text-on-surface"
|
|
14
|
+
data-action="<%= button_action %>"
|
|
15
|
+
<% (item[:button_data] || {}).each do |key, value| %>
|
|
16
|
+
data-<%= key.to_s.dasherize %>="<%= value %>"
|
|
17
|
+
<% end %>
|
|
18
|
+
aria-expanded="<%= (item.key?(:open) ? !!item[:open] : index.zero?) ? "true" : "false" %>"
|
|
19
|
+
aria-controls="<%= item[:id].presence || "#{id}-item-#{index}" %>"
|
|
20
|
+
>
|
|
21
|
+
<span aria-hidden="true" data-accordion-target="icon"><%= ui_icon("chevron-right", class_name: "h-[18px] w-[18px]") %></span>
|
|
22
|
+
<span><%= item[:question] %></span>
|
|
23
|
+
</button>
|
|
24
|
+
<div
|
|
25
|
+
id="<%= item[:id].presence || "#{id}-item-#{index}" %>"
|
|
26
|
+
data-accordion-target="content"
|
|
27
|
+
aria-hidden="<%= (item.key?(:open) ? !!item[:open] : index.zero?) ? "false" : "true" %>"
|
|
28
|
+
>
|
|
29
|
+
<div class="pb-6 pl-7 pr-1">
|
|
30
|
+
<p class="text-soft"><%= item[:answer] %></p>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<% end %>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</section>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<%
|
|
2
|
+
resolved_tabs = Array(tabs)
|
|
3
|
+
selected_tab = resolved_tabs.find { |tab| tab[:selected] } || resolved_tabs.first
|
|
4
|
+
selected_value = selected_tab&.dig(:value)
|
|
5
|
+
tab_segments = resolved_tabs.map do |tab|
|
|
6
|
+
{
|
|
7
|
+
label: tab[:label],
|
|
8
|
+
value: tab[:value],
|
|
9
|
+
selected: tab[:value] == selected_value,
|
|
10
|
+
data: { action: "marketing-tabs#select", marketing_tabs_target: "option" }
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
%>
|
|
14
|
+
<section id="<%= id %>" class="<%= ["marketing-features-section bg-surface px-12 py-16 sm:px-12", local_assigns[:classes]].compact.join(" ") %>">
|
|
15
|
+
<div class="mx-auto max-w-6xl rounded-[40px] bg-accent/10 px-12 py-14 sm:px-12">
|
|
16
|
+
<div class="space-y-8" data-controller="marketing-tabs" data-marketing-tabs-tab-value="<%= selected_value %>">
|
|
17
|
+
<div class="text-center">
|
|
18
|
+
<h2><%= title %></h2>
|
|
19
|
+
<% if description.present? %>
|
|
20
|
+
<p class="mt-3 text-sm text-soft"><%= description %></p>
|
|
21
|
+
<% end %>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="flex justify-center">
|
|
24
|
+
<%= ui_segmented_buttons(aria_label: "Use case tabs", items: tab_segments) %>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="overflow-hidden transition-[height] duration-300 ease-out" data-marketing-tabs-target="panels">
|
|
28
|
+
<% resolved_tabs.each do |tab| %>
|
|
29
|
+
<% active = tab[:value] == selected_value %>
|
|
30
|
+
<% panel_classes = [("motion-slide-up" if active), ("hidden" unless active)].compact.join(" ") %>
|
|
31
|
+
<div data-marketing-tabs-target="panel"
|
|
32
|
+
data-tab="<%= tab[:value] %>"
|
|
33
|
+
role="tabpanel"
|
|
34
|
+
class="<%= panel_classes %>"
|
|
35
|
+
<%= 'hidden aria-hidden="true"' unless active %>>
|
|
36
|
+
<div class="grid items-start gap-8 md:grid-cols-[0.9fr_1.1fr]">
|
|
37
|
+
<div class="rounded-3xl border border-outline-variant bg-surface p-6">
|
|
38
|
+
<h3 class="text-2xl font-semibold text-on-surface"><%= tab[:panel_title].presence || tab[:label] %></h3>
|
|
39
|
+
<p class="mt-3 text-sm text-soft"><%= tab[:panel_body] %></p>
|
|
40
|
+
<% if tab[:visual].present? %>
|
|
41
|
+
<div class="mt-6"><%= ui_marketing_render_content(tab[:visual]) %></div>
|
|
42
|
+
<% else %>
|
|
43
|
+
<div class="mt-6 h-40 rounded-2xl border border-outline bg-surface-high"></div>
|
|
44
|
+
<% end %>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="grid gap-5">
|
|
47
|
+
<p class="text-sm font-semibold uppercase tracking-wide text-muted"><%= question_label %></p>
|
|
48
|
+
<% Array(tab[:cards]).each do |card| %>
|
|
49
|
+
<article class="rounded-3xl border border-outline bg-surface-high p-6">
|
|
50
|
+
<h3 class="text-lg font-semibold"><%= card[:title] %></h3>
|
|
51
|
+
<p class="mt-3 text-sm text-soft"><%= card[:body] %></p>
|
|
52
|
+
</article>
|
|
53
|
+
<% end %>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<% end %>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<% if cta.present? %>
|
|
61
|
+
<div class="flex justify-center">
|
|
62
|
+
<%= ui_button(**cta) %>
|
|
63
|
+
</div>
|
|
64
|
+
<% end %>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</section>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<%
|
|
2
|
+
resolved_brand = local_assigns.fetch(:brand).symbolize_keys
|
|
3
|
+
resolved_brand[:logo_class] = resolved_brand[:logo_class].presence || "h-10 w-10 rounded-lg"
|
|
4
|
+
resolved_brand[:wordmark_class] = resolved_brand[:wordmark_class].presence || "text-2xl font-bold text-on-surface"
|
|
5
|
+
resolved_copyright = local_assigns[:copyright].presence || "© #{Time.current.year} #{resolved_brand[:name]}. All rights reserved."
|
|
6
|
+
%>
|
|
7
|
+
<footer class="<%= ["marketing-footer border-t border-outline-variant bg-surface px-12 py-12 text-sm text-soft", local_assigns[:classes]].compact.join(" ") %>">
|
|
8
|
+
<div class="mx-auto flex max-w-6xl flex-col gap-8 md:flex-row md:items-start md:justify-between">
|
|
9
|
+
<div>
|
|
10
|
+
<span class="brand-lockup">
|
|
11
|
+
<%= image_tag resolved_brand[:logo_src], alt: resolved_brand[:logo_alt], class: ["brand-lockup__logo", resolved_brand[:logo_class]].join(" ") %>
|
|
12
|
+
<span class="<%= ["brand-lockup__wordmark", resolved_brand[:wordmark_class]].join(" ") %>"><%= resolved_brand[:wordmark] %></span>
|
|
13
|
+
</span>
|
|
14
|
+
<p class="mt-2 max-w-md text-soft"><%= description %></p>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="grid grid-cols-2 gap-8 sm:grid-cols-3">
|
|
17
|
+
<% Array(columns).each do |column| %>
|
|
18
|
+
<div>
|
|
19
|
+
<h4 class="font-semibold text-on-surface"><%= column[:title] %></h4>
|
|
20
|
+
<ul class="mt-3 space-y-2 text-soft">
|
|
21
|
+
<% Array(column[:links]).each do |link| %>
|
|
22
|
+
<li>
|
|
23
|
+
<% if link[:href].present? %>
|
|
24
|
+
<%= link_to link[:label], link[:href], class: link[:class].presence || "hover:text-primary" %>
|
|
25
|
+
<% else %>
|
|
26
|
+
<span><%= link[:label] %></span>
|
|
27
|
+
<% end %>
|
|
28
|
+
</li>
|
|
29
|
+
<% end %>
|
|
30
|
+
</ul>
|
|
31
|
+
</div>
|
|
32
|
+
<% end %>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="mx-auto mt-8 max-w-6xl border-t border-outline-variant pt-6 text-xs text-soft">
|
|
36
|
+
<p><%= resolved_copyright %></p>
|
|
37
|
+
</div>
|
|
38
|
+
</footer>
|