better_ui 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/app/components/better_ui/pagination/pagination_component/pagination_component.html.erb +137 -0
- data/app/components/better_ui/pagination/pagination_component.rb +408 -0
- data/app/components/better_ui/table/table_component/table_component.html.erb +2 -2
- data/app/components/better_ui/table/table_component.rb +30 -0
- data/app/helpers/better_ui/application_helper.rb +74 -0
- data/lib/better_ui/version.rb +1 -1
- data/spec/components/previews/better_ui/action_messages_component_preview/all_styles.html.erb +17 -0
- data/spec/components/previews/better_ui/action_messages_component_preview/all_variants.html.erb +19 -0
- data/spec/components/previews/better_ui/action_messages_component_preview/auto_dismiss.html.erb +51 -0
- data/spec/components/previews/better_ui/action_messages_component_preview/dismissible.html.erb +19 -0
- data/spec/components/previews/better_ui/action_messages_component_preview/with_title.html.erb +17 -0
- data/spec/components/previews/better_ui/action_messages_component_preview.rb +224 -0
- data/spec/components/previews/better_ui/avatar_component_preview/all_shapes.html.erb +26 -0
- data/spec/components/previews/better_ui/avatar_component_preview/all_sizes.html.erb +24 -0
- data/spec/components/previews/better_ui/avatar_component_preview/all_variants.html.erb +12 -0
- data/spec/components/previews/better_ui/avatar_component_preview/with_initials.html.erb +22 -0
- data/spec/components/previews/better_ui/avatar_component_preview/with_status.html.erb +26 -0
- data/spec/components/previews/better_ui/avatar_component_preview.rb +73 -0
- data/spec/components/previews/better_ui/badge_component_preview/all_sizes.html.erb +29 -0
- data/spec/components/previews/better_ui/badge_component_preview/all_styles.html.erb +26 -0
- data/spec/components/previews/better_ui/badge_component_preview/all_variants.html.erb +14 -0
- data/spec/components/previews/better_ui/badge_component_preview/counter_badges.html.erb +39 -0
- data/spec/components/previews/better_ui/badge_component_preview/dot_badges.html.erb +28 -0
- data/spec/components/previews/better_ui/badge_component_preview.rb +69 -0
- data/spec/components/previews/better_ui/breadcrumb/breadcrumb_component_preview/all_separators.html.erb +47 -0
- data/spec/components/previews/better_ui/breadcrumb/breadcrumb_component_preview/default.html.erb +23 -0
- data/spec/components/previews/better_ui/breadcrumb/breadcrumb_component_preview/with_icons.html.erb +43 -0
- data/spec/components/previews/better_ui/breadcrumb/breadcrumb_component_preview.rb +38 -0
- data/spec/components/previews/better_ui/button_component_preview/all_sizes.html.erb +25 -0
- data/spec/components/previews/better_ui/button_component_preview/all_variants.html.erb +14 -0
- data/spec/components/previews/better_ui/button_component_preview/as_links.html.erb +18 -0
- data/spec/components/previews/better_ui/button_component_preview/auto_loading_submit.html.erb +112 -0
- data/spec/components/previews/better_ui/button_component_preview/external_links.html.erb +61 -0
- data/spec/components/previews/better_ui/button_component_preview/form_integration.html.erb +102 -0
- data/spec/components/previews/better_ui/button_component_preview/interactive.html.erb +149 -0
- data/spec/components/previews/better_ui/button_component_preview/link_states.html.erb +36 -0
- data/spec/components/previews/better_ui/button_component_preview/loading_states.html.erb +62 -0
- data/spec/components/previews/better_ui/button_component_preview/turbo_method_links.html.erb +98 -0
- data/spec/components/previews/better_ui/button_component_preview/with_icons.html.erb +123 -0
- data/spec/components/previews/better_ui/button_component_preview.rb +155 -0
- data/spec/components/previews/better_ui/card_component_preview/all_sizes.html.erb +10 -0
- data/spec/components/previews/better_ui/card_component_preview/all_styles.html.erb +22 -0
- data/spec/components/previews/better_ui/card_component_preview/all_variants.html.erb +10 -0
- data/spec/components/previews/better_ui/card_component_preview.rb +269 -0
- data/spec/components/previews/better_ui/container_component_preview/all_sizes.html.erb +13 -0
- data/spec/components/previews/better_ui/container_component_preview.rb +59 -0
- data/spec/components/previews/better_ui/dialog/alert_component_preview/all_variants.html.erb +17 -0
- data/spec/components/previews/better_ui/dialog/alert_component_preview/custom_button_label.html.erb +14 -0
- data/spec/components/previews/better_ui/dialog/alert_component_preview/default.html.erb +13 -0
- data/spec/components/previews/better_ui/dialog/alert_component_preview/playground.html.erb +16 -0
- data/spec/components/previews/better_ui/dialog/alert_component_preview/without_icon.html.erb +14 -0
- data/spec/components/previews/better_ui/dialog/alert_component_preview.rb +57 -0
- data/spec/components/previews/better_ui/dialog/confirm_component_preview/all_variants.html.erb +17 -0
- data/spec/components/previews/better_ui/dialog/confirm_component_preview/custom_labels.html.erb +15 -0
- data/spec/components/previews/better_ui/dialog/confirm_component_preview/danger_confirm.html.erb +15 -0
- data/spec/components/previews/better_ui/dialog/confirm_component_preview/default.html.erb +13 -0
- data/spec/components/previews/better_ui/dialog/confirm_component_preview/playground.html.erb +17 -0
- data/spec/components/previews/better_ui/dialog/confirm_component_preview.rb +60 -0
- data/spec/components/previews/better_ui/dialog/dialog_component_preview/all_sizes.html.erb +32 -0
- data/spec/components/previews/better_ui/dialog/dialog_component_preview/default.html.erb +34 -0
- data/spec/components/previews/better_ui/dialog/dialog_component_preview/no_close_button.html.erb +28 -0
- data/spec/components/previews/better_ui/dialog/dialog_component_preview/playground.html.erb +39 -0
- data/spec/components/previews/better_ui/dialog/dialog_component_preview/with_all_slots.html.erb +52 -0
- data/spec/components/previews/better_ui/dialog/dialog_component_preview.rb +51 -0
- data/spec/components/previews/better_ui/divider_component_preview/all_styles.html.erb +58 -0
- data/spec/components/previews/better_ui/divider_component_preview/with_labels.html.erb +67 -0
- data/spec/components/previews/better_ui/divider_component_preview.rb +62 -0
- data/spec/components/previews/better_ui/drawer/header_component_preview.rb +169 -0
- data/spec/components/previews/better_ui/drawer/layout_component_preview/complete_layout.html.erb +87 -0
- data/spec/components/previews/better_ui/drawer/layout_component_preview/dark_theme.html.erb +36 -0
- data/spec/components/previews/better_ui/drawer/layout_component_preview/dashboard_example.html.erb +188 -0
- data/spec/components/previews/better_ui/drawer/layout_component_preview/default.html.erb +22 -0
- data/spec/components/previews/better_ui/drawer/layout_component_preview/primary_theme.html.erb +36 -0
- data/spec/components/previews/better_ui/drawer/layout_component_preview/right_sidebar.html.erb +44 -0
- data/spec/components/previews/better_ui/drawer/layout_component_preview/with_header_only.html.erb +20 -0
- data/spec/components/previews/better_ui/drawer/layout_component_preview/with_sidebar_only.html.erb +21 -0
- data/spec/components/previews/better_ui/drawer/layout_component_preview.rb +91 -0
- data/spec/components/previews/better_ui/drawer/nav_group_component_preview/complete_navigation.html.erb +55 -0
- data/spec/components/previews/better_ui/drawer/nav_group_component_preview.rb +163 -0
- data/spec/components/previews/better_ui/drawer/nav_item_component_preview.rb +104 -0
- data/spec/components/previews/better_ui/drawer/sidebar_component_preview.rb +212 -0
- data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/all_sizes.html.erb +19 -0
- data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/default.html.erb +12 -0
- data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/disabled_items.html.erb +14 -0
- data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/placement_options.html.erb +16 -0
- data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/playground.html.erb +35 -0
- data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/with_dividers_and_headers.html.erb +18 -0
- data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/with_icons.html.erb +34 -0
- data/spec/components/previews/better_ui/dropdown/dropdown_component_preview.rb +59 -0
- data/spec/components/previews/better_ui/fa_icon_component_preview/all_sizes.html.erb +17 -0
- data/spec/components/previews/better_ui/fa_icon_component_preview/all_styles.html.erb +19 -0
- data/spec/components/previews/better_ui/fa_icon_component_preview/all_variants.html.erb +26 -0
- data/spec/components/previews/better_ui/fa_icon_component_preview/animations.html.erb +26 -0
- data/spec/components/previews/better_ui/fa_icon_component_preview/transformations.html.erb +88 -0
- data/spec/components/previews/better_ui/fa_icon_component_preview.rb +85 -0
- data/spec/components/previews/better_ui/forms/checkbox_component_preview/all_sizes.html.erb +12 -0
- data/spec/components/previews/better_ui/forms/checkbox_component_preview/all_variants.html.erb +12 -0
- data/spec/components/previews/better_ui/forms/checkbox_component_preview/form_integration.html.erb +32 -0
- data/spec/components/previews/better_ui/forms/checkbox_component_preview.rb +143 -0
- data/spec/components/previews/better_ui/forms/checkbox_group_component_preview/all_variants.html.erb +14 -0
- data/spec/components/previews/better_ui/forms/checkbox_group_component_preview/form_integration.html.erb +47 -0
- data/spec/components/previews/better_ui/forms/checkbox_group_component_preview/orientations.html.erb +34 -0
- data/spec/components/previews/better_ui/forms/checkbox_group_component_preview.rb +150 -0
- data/spec/components/previews/better_ui/forms/number_input_component_preview/all_sizes.html.erb +14 -0
- data/spec/components/previews/better_ui/forms/number_input_component_preview/form_integration.html.erb +45 -0
- data/spec/components/previews/better_ui/forms/number_input_component_preview.rb +211 -0
- data/spec/components/previews/better_ui/forms/password_input_component_preview/all_sizes.html.erb +12 -0
- data/spec/components/previews/better_ui/forms/password_input_component_preview/confirm_password_example.html.erb +29 -0
- data/spec/components/previews/better_ui/forms/password_input_component_preview/form_integration.html.erb +34 -0
- data/spec/components/previews/better_ui/forms/password_input_component_preview.rb +181 -0
- data/spec/components/previews/better_ui/forms/select_component_preview/all_sizes.html.erb +13 -0
- data/spec/components/previews/better_ui/forms/select_component_preview/all_states.html.erb +64 -0
- data/spec/components/previews/better_ui/forms/select_component_preview.rb +167 -0
- data/spec/components/previews/better_ui/forms/text_input_component_preview/all_sizes.html.erb +12 -0
- data/spec/components/previews/better_ui/forms/text_input_component_preview/all_types.html.erb +12 -0
- data/spec/components/previews/better_ui/forms/text_input_component_preview/form_integration.html.erb +33 -0
- data/spec/components/previews/better_ui/forms/text_input_component_preview.rb +247 -0
- data/spec/components/previews/better_ui/forms/textarea_component_preview/all_resize_variants.html.erb +13 -0
- data/spec/components/previews/better_ui/forms/textarea_component_preview/all_sizes.html.erb +12 -0
- data/spec/components/previews/better_ui/forms/textarea_component_preview/form_integration.html.erb +36 -0
- data/spec/components/previews/better_ui/forms/textarea_component_preview.rb +239 -0
- data/spec/components/previews/better_ui/heading_component_preview/all_alignments.html.erb +12 -0
- data/spec/components/previews/better_ui/heading_component_preview/all_levels.html.erb +7 -0
- data/spec/components/previews/better_ui/heading_component_preview/all_variants.html.erb +14 -0
- data/spec/components/previews/better_ui/heading_component_preview.rb +113 -0
- data/spec/components/previews/better_ui/link_component_preview/all_sizes.html.erb +25 -0
- data/spec/components/previews/better_ui/link_component_preview/all_styles.html.erb +14 -0
- data/spec/components/previews/better_ui/link_component_preview/all_variants.html.erb +14 -0
- data/spec/components/previews/better_ui/link_component_preview/with_icons.html.erb +66 -0
- data/spec/components/previews/better_ui/link_component_preview.rb +66 -0
- data/spec/components/previews/better_ui/pagination/pagination_component_preview/all_sizes.html.erb +13 -0
- data/spec/components/previews/better_ui/pagination/pagination_component_preview/all_styles.html.erb +13 -0
- data/spec/components/previews/better_ui/pagination/pagination_component_preview/all_variants.html.erb +13 -0
- data/spec/components/previews/better_ui/pagination/pagination_component_preview/default.html.erb +28 -0
- data/spec/components/previews/better_ui/pagination/pagination_component_preview/edge_cases.html.erb +82 -0
- data/spec/components/previews/better_ui/pagination/pagination_component_preview/with_info.html.erb +38 -0
- data/spec/components/previews/better_ui/pagination/pagination_component_preview.rb +88 -0
- data/spec/components/previews/better_ui/progress_component_preview/all_sizes.html.erb +15 -0
- data/spec/components/previews/better_ui/progress_component_preview/all_variants.html.erb +11 -0
- data/spec/components/previews/better_ui/progress_component_preview.rb +64 -0
- data/spec/components/previews/better_ui/spinner_component_preview/all_sizes.html.erb +17 -0
- data/spec/components/previews/better_ui/spinner_component_preview/all_variants.html.erb +11 -0
- data/spec/components/previews/better_ui/spinner_component_preview.rb +44 -0
- data/spec/components/previews/better_ui/table/table_component_preview/all_sizes.html.erb +28 -0
- data/spec/components/previews/better_ui/table/table_component_preview/all_variants.html.erb +34 -0
- data/spec/components/previews/better_ui/table/table_component_preview/bordered.html.erb +33 -0
- data/spec/components/previews/better_ui/table/table_component_preview/collection_mode.html.erb +31 -0
- data/spec/components/previews/better_ui/table/table_component_preview/default.html.erb +33 -0
- data/spec/components/previews/better_ui/table/table_component_preview/empty_state.html.erb +36 -0
- data/spec/components/previews/better_ui/table/table_component_preview/highlighted.html.erb +64 -0
- data/spec/components/previews/better_ui/table/table_component_preview/hoverable.html.erb +27 -0
- data/spec/components/previews/better_ui/table/table_component_preview/inside_card.html.erb +173 -0
- data/spec/components/previews/better_ui/table/table_component_preview/row_html.html.erb +64 -0
- data/spec/components/previews/better_ui/table/table_component_preview/sortable.html.erb +44 -0
- data/spec/components/previews/better_ui/table/table_component_preview/striped.html.erb +31 -0
- data/spec/components/previews/better_ui/table/table_component_preview/with_footer.html.erb +40 -0
- data/spec/components/previews/better_ui/table/table_component_preview.rb +85 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/alignments.html.erb +24 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/all_sizes.html.erb +24 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/all_variants.html.erb +24 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/bordered_style.html.erb +30 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/default.html.erb +30 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/disabled_tab.html.erb +65 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/pills_style.html.erb +34 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/turbo_mode.html.erb +40 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/vertical_left.html.erb +38 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/vertical_right.html.erb +30 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview/with_icons_and_badges.html.erb +71 -0
- data/spec/components/previews/better_ui/tabs/container_component_preview.rb +130 -0
- data/spec/components/previews/better_ui/tag_component_preview/all_styles.html.erb +14 -0
- data/spec/components/previews/better_ui/tag_component_preview/all_variants.html.erb +14 -0
- data/spec/components/previews/better_ui/tag_component_preview/as_links.html.erb +14 -0
- data/spec/components/previews/better_ui/tag_component_preview/dismissible.html.erb +34 -0
- data/spec/components/previews/better_ui/tag_component_preview.rb +56 -0
- data/spec/components/previews/better_ui/tooltip_component_preview/all_positions.html.erb +25 -0
- data/spec/components/previews/better_ui/tooltip_component_preview/variants.html.erb +37 -0
- data/spec/components/previews/better_ui/tooltip_component_preview.rb +40 -0
- metadata +174 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f946c8d53c3641506cd438e13df28f7cc23b3b341294ee790b8fb8ce6a3b5438
|
|
4
|
+
data.tar.gz: 0fa3b052a2588162f89d752460be51e318ed971152e6a903f9cfb76bbd2ca336
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 421babdbde227a74c064a9508b1dc32b4f04a31e0a96d4e3b5677492a5ed1d8e4edab11ad9e8289bf82c04424b73933559fda60a5d030cf5d42fc89fc4eb5f93
|
|
7
|
+
data.tar.gz: 708369a2f8160381dd63e3dd1eedb0524eaf74517369d9e317bb95139f315e6041ae3ce6bfcb536e4ce15b2ab9e31cc6c9cf324b90322b726329284be9332057
|
data/README.md
CHANGED
|
@@ -304,6 +304,10 @@ BetterUi includes a custom form builder for seamless Rails form integration:
|
|
|
304
304
|
|
|
305
305
|
> **Note**: You can also use ViewComponent directly with `render BetterUi::*Component.new(...)` if you prefer the explicit rendering syntax.
|
|
306
306
|
|
|
307
|
+
## Live Demo
|
|
308
|
+
|
|
309
|
+
Check out the live example application at [better-ui.pandev.it](https://better-ui.pandev.it) to see all components in action.
|
|
310
|
+
|
|
307
311
|
## Documentation
|
|
308
312
|
|
|
309
313
|
- [**Installation Guide**](doc/INSTALLATION.md) - Detailed setup and configuration instructions
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<nav aria-label="Pagination" class="<%= nav_classes %>">
|
|
2
|
+
<%# Info section %>
|
|
3
|
+
<% if info? %>
|
|
4
|
+
<div class="mb-2 text-sm text-grayscale-600">
|
|
5
|
+
<%= info %>
|
|
6
|
+
</div>
|
|
7
|
+
<% elsif auto_info_text %>
|
|
8
|
+
<div class="mb-2 text-sm text-grayscale-600">
|
|
9
|
+
<%= auto_info_text %>
|
|
10
|
+
</div>
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
<ul role="list" class="<%= list_classes %>">
|
|
14
|
+
<%# First button %>
|
|
15
|
+
<% if show_first_last %>
|
|
16
|
+
<li>
|
|
17
|
+
<% if first_page? %>
|
|
18
|
+
<span aria-disabled="true" aria-label="First page" class="<%= disabled_classes %>">
|
|
19
|
+
<% if first_label %>
|
|
20
|
+
<%= first_label %>
|
|
21
|
+
<% else %>
|
|
22
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="<%= icon_classes %>" aria-hidden="true">
|
|
23
|
+
<path fill-rule="evenodd" d="M15.79 14.77a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L11.832 10l3.938 3.71a.75.75 0 01.02 1.06zm-6 0a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L5.832 10l3.938 3.71a.75.75 0 01.02 1.06z" clip-rule="evenodd" />
|
|
24
|
+
</svg>
|
|
25
|
+
<% end %>
|
|
26
|
+
</span>
|
|
27
|
+
<% else %>
|
|
28
|
+
<a href="<%= page_url(1) %>" aria-label="First page" class="<%= nav_button_classes %>">
|
|
29
|
+
<% if first_label %>
|
|
30
|
+
<%= first_label %>
|
|
31
|
+
<% else %>
|
|
32
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="<%= icon_classes %>" aria-hidden="true">
|
|
33
|
+
<path fill-rule="evenodd" d="M15.79 14.77a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L11.832 10l3.938 3.71a.75.75 0 01.02 1.06zm-6 0a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L5.832 10l3.938 3.71a.75.75 0 01.02 1.06z" clip-rule="evenodd" />
|
|
34
|
+
</svg>
|
|
35
|
+
<% end %>
|
|
36
|
+
</a>
|
|
37
|
+
<% end %>
|
|
38
|
+
</li>
|
|
39
|
+
<% end %>
|
|
40
|
+
|
|
41
|
+
<%# Previous button %>
|
|
42
|
+
<% if show_prev_next %>
|
|
43
|
+
<li>
|
|
44
|
+
<% if first_page? %>
|
|
45
|
+
<span aria-disabled="true" aria-label="Previous page" class="<%= disabled_classes %>">
|
|
46
|
+
<% if prev_label %>
|
|
47
|
+
<%= prev_label %>
|
|
48
|
+
<% else %>
|
|
49
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="<%= icon_classes %>" aria-hidden="true">
|
|
50
|
+
<path fill-rule="evenodd" d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" clip-rule="evenodd" />
|
|
51
|
+
</svg>
|
|
52
|
+
<% end %>
|
|
53
|
+
</span>
|
|
54
|
+
<% else %>
|
|
55
|
+
<a href="<%= page_url(current_page - 1) %>" rel="prev" aria-label="Previous page" class="<%= nav_button_classes %>">
|
|
56
|
+
<% if prev_label %>
|
|
57
|
+
<%= prev_label %>
|
|
58
|
+
<% else %>
|
|
59
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="<%= icon_classes %>" aria-hidden="true">
|
|
60
|
+
<path fill-rule="evenodd" d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" clip-rule="evenodd" />
|
|
61
|
+
</svg>
|
|
62
|
+
<% end %>
|
|
63
|
+
</a>
|
|
64
|
+
<% end %>
|
|
65
|
+
</li>
|
|
66
|
+
<% end %>
|
|
67
|
+
|
|
68
|
+
<%# Page numbers %>
|
|
69
|
+
<% if show_page_numbers %>
|
|
70
|
+
<% page_items.each do |item| %>
|
|
71
|
+
<li>
|
|
72
|
+
<% if item == :gap %>
|
|
73
|
+
<span aria-hidden="true" class="<%= gap_classes %>"><%= gap_label %></span>
|
|
74
|
+
<% elsif item == current_page %>
|
|
75
|
+
<span aria-current="page" aria-label="Page <%= item %>" class="<%= active_page_classes %>"><%= item %></span>
|
|
76
|
+
<% else %>
|
|
77
|
+
<a href="<%= page_url(item) %>" aria-label="Go to page <%= item %>" class="<%= inactive_page_classes %>"><%= item %></a>
|
|
78
|
+
<% end %>
|
|
79
|
+
</li>
|
|
80
|
+
<% end %>
|
|
81
|
+
<% end %>
|
|
82
|
+
|
|
83
|
+
<%# Next button %>
|
|
84
|
+
<% if show_prev_next %>
|
|
85
|
+
<li>
|
|
86
|
+
<% if last_page? %>
|
|
87
|
+
<span aria-disabled="true" aria-label="Next page" class="<%= disabled_classes %>">
|
|
88
|
+
<% if next_label %>
|
|
89
|
+
<%= next_label %>
|
|
90
|
+
<% else %>
|
|
91
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="<%= icon_classes %>" aria-hidden="true">
|
|
92
|
+
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
|
|
93
|
+
</svg>
|
|
94
|
+
<% end %>
|
|
95
|
+
</span>
|
|
96
|
+
<% else %>
|
|
97
|
+
<a href="<%= page_url(current_page + 1) %>" rel="next" aria-label="Next page" class="<%= nav_button_classes %>">
|
|
98
|
+
<% if next_label %>
|
|
99
|
+
<%= next_label %>
|
|
100
|
+
<% else %>
|
|
101
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="<%= icon_classes %>" aria-hidden="true">
|
|
102
|
+
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
|
|
103
|
+
</svg>
|
|
104
|
+
<% end %>
|
|
105
|
+
</a>
|
|
106
|
+
<% end %>
|
|
107
|
+
</li>
|
|
108
|
+
<% end %>
|
|
109
|
+
|
|
110
|
+
<%# Last button %>
|
|
111
|
+
<% if show_first_last %>
|
|
112
|
+
<li>
|
|
113
|
+
<% if last_page? %>
|
|
114
|
+
<span aria-disabled="true" aria-label="Last page" class="<%= disabled_classes %>">
|
|
115
|
+
<% if last_label %>
|
|
116
|
+
<%= last_label %>
|
|
117
|
+
<% else %>
|
|
118
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="<%= icon_classes %>" aria-hidden="true">
|
|
119
|
+
<path fill-rule="evenodd" d="M10.21 14.77a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L6.832 10l3.938 3.71a.75.75 0 01.02 1.06zm6 0a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L12.832 10l3.938 3.71a.75.75 0 01.02 1.06z" clip-rule="evenodd" />
|
|
120
|
+
</svg>
|
|
121
|
+
<% end %>
|
|
122
|
+
</span>
|
|
123
|
+
<% else %>
|
|
124
|
+
<a href="<%= page_url(total_pages) %>" aria-label="Last page" class="<%= nav_button_classes %>">
|
|
125
|
+
<% if last_label %>
|
|
126
|
+
<%= last_label %>
|
|
127
|
+
<% else %>
|
|
128
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="<%= icon_classes %>" aria-hidden="true">
|
|
129
|
+
<path fill-rule="evenodd" d="M10.21 14.77a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L6.832 10l3.938 3.71a.75.75 0 01.02 1.06zm6 0a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L12.832 10l3.938 3.71a.75.75 0 01.02 1.06z" clip-rule="evenodd" />
|
|
130
|
+
</svg>
|
|
131
|
+
<% end %>
|
|
132
|
+
</a>
|
|
133
|
+
<% end %>
|
|
134
|
+
</li>
|
|
135
|
+
<% end %>
|
|
136
|
+
</ul>
|
|
137
|
+
</nav>
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterUi
|
|
4
|
+
module Pagination
|
|
5
|
+
class PaginationComponent < BetterUi::ApplicationComponent
|
|
6
|
+
renders_one :info
|
|
7
|
+
|
|
8
|
+
STYLES = %i[solid outline ghost soft].freeze
|
|
9
|
+
|
|
10
|
+
SIZES = {
|
|
11
|
+
xs: { item: "min-w-6 h-6 text-xs px-1.5", gap: "gap-0.5", icon: "w-3 h-3" },
|
|
12
|
+
sm: { item: "min-w-7 h-7 text-sm px-2", gap: "gap-1", icon: "w-3.5 h-3.5" },
|
|
13
|
+
md: { item: "min-w-9 h-9 text-sm px-3", gap: "gap-1.5", icon: "w-4 h-4" },
|
|
14
|
+
lg: { item: "min-w-11 h-11 text-base px-3.5", gap: "gap-2", icon: "w-5 h-5" },
|
|
15
|
+
xl: { item: "min-w-12 h-12 text-lg px-4", gap: "gap-2.5", icon: "w-5 h-5" }
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
ROUNDED = {
|
|
19
|
+
none: "rounded-none",
|
|
20
|
+
sm: "rounded-sm",
|
|
21
|
+
md: "rounded-md",
|
|
22
|
+
lg: "rounded-lg",
|
|
23
|
+
full: "rounded-full"
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
def initialize(
|
|
27
|
+
current_page:,
|
|
28
|
+
total_pages:,
|
|
29
|
+
url:,
|
|
30
|
+
variant: :primary,
|
|
31
|
+
style: :outline,
|
|
32
|
+
size: :md,
|
|
33
|
+
rounded: :md,
|
|
34
|
+
shadow: :none,
|
|
35
|
+
window: 2,
|
|
36
|
+
show_first_last: false,
|
|
37
|
+
show_prev_next: true,
|
|
38
|
+
show_page_numbers: true,
|
|
39
|
+
show_info: false,
|
|
40
|
+
per_page: nil,
|
|
41
|
+
total_count: nil,
|
|
42
|
+
prev_label: nil,
|
|
43
|
+
next_label: nil,
|
|
44
|
+
first_label: nil,
|
|
45
|
+
last_label: nil,
|
|
46
|
+
gap_label: "\u2026",
|
|
47
|
+
container_classes: nil
|
|
48
|
+
)
|
|
49
|
+
@current_page = current_page
|
|
50
|
+
@total_pages = total_pages
|
|
51
|
+
@url = url
|
|
52
|
+
@variant = validate_variant(variant)
|
|
53
|
+
@style = validate_style(style)
|
|
54
|
+
@size = validate_size(size)
|
|
55
|
+
@rounded = validate_rounded(rounded)
|
|
56
|
+
@shadow = normalize_shadow(shadow)
|
|
57
|
+
@window = window
|
|
58
|
+
@show_first_last = show_first_last
|
|
59
|
+
@show_prev_next = show_prev_next
|
|
60
|
+
@show_page_numbers = show_page_numbers
|
|
61
|
+
@show_info = show_info
|
|
62
|
+
@per_page = per_page
|
|
63
|
+
@total_count = total_count
|
|
64
|
+
@prev_label = prev_label
|
|
65
|
+
@next_label = next_label
|
|
66
|
+
@first_label = first_label
|
|
67
|
+
@last_label = last_label
|
|
68
|
+
@gap_label = gap_label
|
|
69
|
+
@container_classes = container_classes
|
|
70
|
+
|
|
71
|
+
validate_pages!
|
|
72
|
+
validate_url!
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def render?
|
|
76
|
+
@total_pages > 1
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def page_items
|
|
80
|
+
return [] if @total_pages <= 0
|
|
81
|
+
return [ 1 ] if @total_pages == 1
|
|
82
|
+
|
|
83
|
+
build_page_items
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def page_url(page)
|
|
87
|
+
@url.call(page)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def first_page?
|
|
91
|
+
@current_page == 1
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def last_page?
|
|
95
|
+
@current_page == @total_pages
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def auto_info_text
|
|
99
|
+
return nil unless @show_info && @per_page && @total_count
|
|
100
|
+
|
|
101
|
+
from = ((@current_page - 1) * @per_page) + 1
|
|
102
|
+
to = [ @current_page * @per_page, @total_count ].min
|
|
103
|
+
"Showing #{from}-#{to} of #{@total_count} results"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
attr_reader :current_page, :total_pages, :variant, :style, :size, :rounded,
|
|
109
|
+
:window, :show_first_last, :show_prev_next, :show_page_numbers,
|
|
110
|
+
:show_info, :per_page, :total_count,
|
|
111
|
+
:prev_label, :next_label, :first_label, :last_label, :gap_label,
|
|
112
|
+
:container_classes
|
|
113
|
+
|
|
114
|
+
# ============================================
|
|
115
|
+
# Page Algorithm
|
|
116
|
+
# ============================================
|
|
117
|
+
|
|
118
|
+
def build_page_items
|
|
119
|
+
window_start = [ @current_page - @window, 1 ].max
|
|
120
|
+
window_end = [ @current_page + @window, @total_pages ].min
|
|
121
|
+
|
|
122
|
+
# If everything fits without gaps, return full range
|
|
123
|
+
return (1..@total_pages).to_a if @total_pages <= (2 * @window + 3)
|
|
124
|
+
|
|
125
|
+
items = []
|
|
126
|
+
|
|
127
|
+
# Always include first page
|
|
128
|
+
items << 1
|
|
129
|
+
|
|
130
|
+
# Gap or bridging pages between first and window
|
|
131
|
+
if window_start > 2
|
|
132
|
+
if window_start == 3
|
|
133
|
+
items << 2
|
|
134
|
+
else
|
|
135
|
+
items << :gap
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Window pages (skip first and last as they are always included)
|
|
140
|
+
(window_start..window_end).each do |page|
|
|
141
|
+
items << page unless items.include?(page)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Gap or bridging pages between window and last
|
|
145
|
+
if window_end < @total_pages - 1
|
|
146
|
+
if window_end == @total_pages - 2
|
|
147
|
+
items << @total_pages - 1
|
|
148
|
+
else
|
|
149
|
+
items << :gap
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Always include last page
|
|
154
|
+
items << @total_pages unless items.include?(@total_pages)
|
|
155
|
+
|
|
156
|
+
items
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# ============================================
|
|
160
|
+
# CSS Classes
|
|
161
|
+
# ============================================
|
|
162
|
+
|
|
163
|
+
def nav_classes
|
|
164
|
+
classes = [ "flex flex-col items-center" ]
|
|
165
|
+
classes << SHADOWS[@shadow] if SHADOWS[@shadow]
|
|
166
|
+
classes << @container_classes if @container_classes
|
|
167
|
+
css_classes(*classes)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def list_classes
|
|
171
|
+
css_classes("flex items-center", SIZES[@size][:gap])
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def item_base_classes
|
|
175
|
+
css_classes(
|
|
176
|
+
"inline-flex items-center justify-center font-medium transition-colors duration-200",
|
|
177
|
+
SIZES[@size][:item],
|
|
178
|
+
ROUNDED[@rounded]
|
|
179
|
+
)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def active_page_classes
|
|
183
|
+
css_classes(item_base_classes, active_style_classes)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def inactive_page_classes
|
|
187
|
+
css_classes(item_base_classes, inactive_style_classes)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def disabled_classes
|
|
191
|
+
css_classes(item_base_classes, "opacity-40 cursor-not-allowed pointer-events-none", disabled_color_classes)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def gap_classes
|
|
195
|
+
css_classes(
|
|
196
|
+
"inline-flex items-center justify-center",
|
|
197
|
+
SIZES[@size][:item],
|
|
198
|
+
"text-grayscale-400 select-none"
|
|
199
|
+
)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def nav_button_classes
|
|
203
|
+
css_classes(item_base_classes, inactive_style_classes)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def icon_classes
|
|
207
|
+
SIZES[@size][:icon]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# ============================================
|
|
211
|
+
# Style-specific active/inactive classes
|
|
212
|
+
# ============================================
|
|
213
|
+
|
|
214
|
+
def active_style_classes
|
|
215
|
+
case @style
|
|
216
|
+
when :solid then solid_active_classes
|
|
217
|
+
when :outline then outline_active_classes
|
|
218
|
+
when :ghost then ghost_active_classes
|
|
219
|
+
when :soft then soft_active_classes
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def inactive_style_classes
|
|
224
|
+
case @style
|
|
225
|
+
when :solid then solid_inactive_classes
|
|
226
|
+
when :outline then outline_inactive_classes
|
|
227
|
+
when :ghost then ghost_inactive_classes
|
|
228
|
+
when :soft then soft_inactive_classes
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def disabled_color_classes
|
|
233
|
+
case @style
|
|
234
|
+
when :solid, :soft
|
|
235
|
+
"bg-grayscale-100 text-grayscale-400"
|
|
236
|
+
when :outline
|
|
237
|
+
"border border-grayscale-200 text-grayscale-400"
|
|
238
|
+
when :ghost
|
|
239
|
+
"text-grayscale-400"
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# --- Solid ---
|
|
244
|
+
|
|
245
|
+
def solid_active_classes
|
|
246
|
+
case @variant
|
|
247
|
+
when :primary then "bg-primary-600 text-white"
|
|
248
|
+
when :secondary then "bg-secondary-600 text-white"
|
|
249
|
+
when :accent then "bg-accent-600 text-white"
|
|
250
|
+
when :success then "bg-success-600 text-white"
|
|
251
|
+
when :danger then "bg-danger-600 text-white"
|
|
252
|
+
when :warning then "bg-warning-600 text-white"
|
|
253
|
+
when :info then "bg-info-600 text-white"
|
|
254
|
+
when :light then "bg-grayscale-200 text-grayscale-900"
|
|
255
|
+
when :dark then "bg-grayscale-900 text-grayscale-50"
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def solid_inactive_classes
|
|
260
|
+
case @variant
|
|
261
|
+
when :primary then "text-grayscale-700 hover:bg-primary-50 hover:text-primary-700"
|
|
262
|
+
when :secondary then "text-grayscale-700 hover:bg-secondary-50 hover:text-secondary-700"
|
|
263
|
+
when :accent then "text-grayscale-700 hover:bg-accent-50 hover:text-accent-700"
|
|
264
|
+
when :success then "text-grayscale-700 hover:bg-success-50 hover:text-success-700"
|
|
265
|
+
when :danger then "text-grayscale-700 hover:bg-danger-50 hover:text-danger-700"
|
|
266
|
+
when :warning then "text-grayscale-700 hover:bg-warning-50 hover:text-warning-700"
|
|
267
|
+
when :info then "text-grayscale-700 hover:bg-info-50 hover:text-info-700"
|
|
268
|
+
when :light then "text-grayscale-700 hover:bg-grayscale-100 hover:text-grayscale-900"
|
|
269
|
+
when :dark then "text-grayscale-700 hover:bg-grayscale-800 hover:text-grayscale-50"
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# --- Outline ---
|
|
274
|
+
|
|
275
|
+
def outline_active_classes
|
|
276
|
+
case @variant
|
|
277
|
+
when :primary then "border border-primary-600 bg-primary-50 text-primary-700"
|
|
278
|
+
when :secondary then "border border-secondary-600 bg-secondary-50 text-secondary-700"
|
|
279
|
+
when :accent then "border border-accent-600 bg-accent-50 text-accent-700"
|
|
280
|
+
when :success then "border border-success-600 bg-success-50 text-success-700"
|
|
281
|
+
when :danger then "border border-danger-600 bg-danger-50 text-danger-700"
|
|
282
|
+
when :warning then "border border-warning-600 bg-warning-50 text-warning-700"
|
|
283
|
+
when :info then "border border-info-600 bg-info-50 text-info-700"
|
|
284
|
+
when :light then "border border-grayscale-400 bg-grayscale-50 text-grayscale-900"
|
|
285
|
+
when :dark then "border border-grayscale-700 bg-grayscale-800 text-grayscale-50"
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def outline_inactive_classes
|
|
290
|
+
case @variant
|
|
291
|
+
when :primary then "border border-grayscale-200 text-grayscale-700 hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700"
|
|
292
|
+
when :secondary then "border border-grayscale-200 text-grayscale-700 hover:border-secondary-300 hover:bg-secondary-50 hover:text-secondary-700"
|
|
293
|
+
when :accent then "border border-grayscale-200 text-grayscale-700 hover:border-accent-300 hover:bg-accent-50 hover:text-accent-700"
|
|
294
|
+
when :success then "border border-grayscale-200 text-grayscale-700 hover:border-success-300 hover:bg-success-50 hover:text-success-700"
|
|
295
|
+
when :danger then "border border-grayscale-200 text-grayscale-700 hover:border-danger-300 hover:bg-danger-50 hover:text-danger-700"
|
|
296
|
+
when :warning then "border border-grayscale-200 text-grayscale-700 hover:border-warning-300 hover:bg-warning-50 hover:text-warning-700"
|
|
297
|
+
when :info then "border border-grayscale-200 text-grayscale-700 hover:border-info-300 hover:bg-info-50 hover:text-info-700"
|
|
298
|
+
when :light then "border border-grayscale-200 text-grayscale-700 hover:border-grayscale-400 hover:bg-grayscale-50 hover:text-grayscale-900"
|
|
299
|
+
when :dark then "border border-grayscale-200 text-grayscale-700 hover:border-grayscale-600 hover:bg-grayscale-800 hover:text-grayscale-50"
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# --- Ghost ---
|
|
304
|
+
|
|
305
|
+
def ghost_active_classes
|
|
306
|
+
case @variant
|
|
307
|
+
when :primary then "bg-primary-100 text-primary-700"
|
|
308
|
+
when :secondary then "bg-secondary-100 text-secondary-700"
|
|
309
|
+
when :accent then "bg-accent-100 text-accent-700"
|
|
310
|
+
when :success then "bg-success-100 text-success-700"
|
|
311
|
+
when :danger then "bg-danger-100 text-danger-700"
|
|
312
|
+
when :warning then "bg-warning-100 text-warning-700"
|
|
313
|
+
when :info then "bg-info-100 text-info-700"
|
|
314
|
+
when :light then "bg-grayscale-200 text-grayscale-900"
|
|
315
|
+
when :dark then "bg-grayscale-800 text-grayscale-50"
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def ghost_inactive_classes
|
|
320
|
+
case @variant
|
|
321
|
+
when :primary then "text-grayscale-700 hover:bg-primary-50 hover:text-primary-700"
|
|
322
|
+
when :secondary then "text-grayscale-700 hover:bg-secondary-50 hover:text-secondary-700"
|
|
323
|
+
when :accent then "text-grayscale-700 hover:bg-accent-50 hover:text-accent-700"
|
|
324
|
+
when :success then "text-grayscale-700 hover:bg-success-50 hover:text-success-700"
|
|
325
|
+
when :danger then "text-grayscale-700 hover:bg-danger-50 hover:text-danger-700"
|
|
326
|
+
when :warning then "text-grayscale-700 hover:bg-warning-50 hover:text-warning-700"
|
|
327
|
+
when :info then "text-grayscale-700 hover:bg-info-50 hover:text-info-700"
|
|
328
|
+
when :light then "text-grayscale-700 hover:bg-grayscale-100 hover:text-grayscale-900"
|
|
329
|
+
when :dark then "text-grayscale-700 hover:bg-grayscale-800 hover:text-grayscale-50"
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# --- Soft ---
|
|
334
|
+
|
|
335
|
+
def soft_active_classes
|
|
336
|
+
case @variant
|
|
337
|
+
when :primary then "bg-primary-100 text-primary-700 font-semibold"
|
|
338
|
+
when :secondary then "bg-secondary-100 text-secondary-700 font-semibold"
|
|
339
|
+
when :accent then "bg-accent-100 text-accent-700 font-semibold"
|
|
340
|
+
when :success then "bg-success-100 text-success-700 font-semibold"
|
|
341
|
+
when :danger then "bg-danger-100 text-danger-700 font-semibold"
|
|
342
|
+
when :warning then "bg-warning-100 text-warning-700 font-semibold"
|
|
343
|
+
when :info then "bg-info-100 text-info-700 font-semibold"
|
|
344
|
+
when :light then "bg-grayscale-200 text-grayscale-900 font-semibold"
|
|
345
|
+
when :dark then "bg-grayscale-800 text-grayscale-50 font-semibold"
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def soft_inactive_classes
|
|
350
|
+
case @variant
|
|
351
|
+
when :primary then "bg-primary-50 text-grayscale-700 hover:bg-primary-100 hover:text-primary-700"
|
|
352
|
+
when :secondary then "bg-secondary-50 text-grayscale-700 hover:bg-secondary-100 hover:text-secondary-700"
|
|
353
|
+
when :accent then "bg-accent-50 text-grayscale-700 hover:bg-accent-100 hover:text-accent-700"
|
|
354
|
+
when :success then "bg-success-50 text-grayscale-700 hover:bg-success-100 hover:text-success-700"
|
|
355
|
+
when :danger then "bg-danger-50 text-grayscale-700 hover:bg-danger-100 hover:text-danger-700"
|
|
356
|
+
when :warning then "bg-warning-50 text-grayscale-700 hover:bg-warning-100 hover:text-warning-700"
|
|
357
|
+
when :info then "bg-info-50 text-grayscale-700 hover:bg-info-100 hover:text-info-700"
|
|
358
|
+
when :light then "bg-grayscale-100 text-grayscale-700 hover:bg-grayscale-200 hover:text-grayscale-900"
|
|
359
|
+
when :dark then "bg-grayscale-700 text-grayscale-200 hover:bg-grayscale-800 hover:text-grayscale-50"
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# ============================================
|
|
364
|
+
# Validation
|
|
365
|
+
# ============================================
|
|
366
|
+
|
|
367
|
+
def validate_pages!
|
|
368
|
+
raise ArgumentError, "total_pages must be >= 0" if @total_pages.negative?
|
|
369
|
+
raise ArgumentError, "current_page must be >= 1" if @total_pages > 0 && @current_page < 1
|
|
370
|
+
if @total_pages > 0 && @current_page > @total_pages
|
|
371
|
+
raise ArgumentError, "current_page (#{@current_page}) cannot exceed total_pages (#{@total_pages})"
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def validate_url!
|
|
376
|
+
raise ArgumentError, "url must be a Proc" unless @url.is_a?(Proc)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def validate_variant(variant)
|
|
380
|
+
unless BetterUi::ApplicationComponent::VARIANTS.key?(variant)
|
|
381
|
+
raise ArgumentError, "Invalid variant: #{variant}. Must be one of: #{BetterUi::ApplicationComponent::VARIANTS.keys.join(', ')}"
|
|
382
|
+
end
|
|
383
|
+
variant
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def validate_style(style)
|
|
387
|
+
unless STYLES.include?(style)
|
|
388
|
+
raise ArgumentError, "Invalid style: #{style}. Must be one of: #{STYLES.join(', ')}"
|
|
389
|
+
end
|
|
390
|
+
style
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def validate_size(size)
|
|
394
|
+
unless SIZES.key?(size)
|
|
395
|
+
raise ArgumentError, "Invalid size: #{size}. Must be one of: #{SIZES.keys.join(', ')}"
|
|
396
|
+
end
|
|
397
|
+
size
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def validate_rounded(rounded)
|
|
401
|
+
unless ROUNDED.key?(rounded)
|
|
402
|
+
raise ArgumentError, "Invalid rounded: #{rounded}. Must be one of: #{ROUNDED.keys.join(', ')}"
|
|
403
|
+
end
|
|
404
|
+
rounded
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
<% if body_row_partial? %>
|
|
38
38
|
<%= render partial: @body_row_partial, locals: { item: item, index: index, columns: columns } %>
|
|
39
39
|
<% else %>
|
|
40
|
-
|
|
40
|
+
<%= tag.tr(**collection_row_attributes(item, index)) do %>
|
|
41
41
|
<% columns.each do |column| %>
|
|
42
42
|
<td class="<%= collection_cell_classes(column) %>"><%= column.value_for(item) %></td>
|
|
43
43
|
<% end %>
|
|
44
|
-
|
|
44
|
+
<% end %>
|
|
45
45
|
<% end %>
|
|
46
46
|
<% end %>
|
|
47
47
|
</tbody>
|
|
@@ -89,6 +89,7 @@ module BetterUi
|
|
|
89
89
|
caption: nil,
|
|
90
90
|
collection: nil,
|
|
91
91
|
row_highlighted: nil,
|
|
92
|
+
row_html: nil,
|
|
92
93
|
body_row_partial: nil,
|
|
93
94
|
header_partial: nil,
|
|
94
95
|
footer_partial: nil,
|
|
@@ -110,6 +111,7 @@ module BetterUi
|
|
|
110
111
|
@caption = caption
|
|
111
112
|
@collection = collection
|
|
112
113
|
@row_highlighted = row_highlighted
|
|
114
|
+
@row_html = row_html
|
|
113
115
|
@body_row_partial = body_row_partial
|
|
114
116
|
@header_partial = header_partial
|
|
115
117
|
@footer_partial = footer_partial
|
|
@@ -288,6 +290,34 @@ module BetterUi
|
|
|
288
290
|
].compact)
|
|
289
291
|
end
|
|
290
292
|
|
|
293
|
+
# Collection mode: full row attributes (classes + custom HTML attrs from row_html proc)
|
|
294
|
+
def collection_row_attributes(item, index)
|
|
295
|
+
base_classes = collection_row_classes(item)
|
|
296
|
+
custom_attrs = resolve_row_html(item, index)
|
|
297
|
+
custom_class = custom_attrs.delete(:class)
|
|
298
|
+
merged_class = custom_class ? css_classes(base_classes, custom_class) : base_classes
|
|
299
|
+
{ class: merged_class, **custom_attrs }
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Resolve row_html proc to a hash of HTML attributes
|
|
303
|
+
def resolve_row_html(item, index)
|
|
304
|
+
return {} if @row_html.nil?
|
|
305
|
+
|
|
306
|
+
result = if @row_html.arity == 1
|
|
307
|
+
@row_html.call(item)
|
|
308
|
+
else
|
|
309
|
+
@row_html.call(item, index)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
return {} if result.nil?
|
|
313
|
+
|
|
314
|
+
unless result.is_a?(Hash)
|
|
315
|
+
raise ArgumentError, "row_html proc must return a Hash or nil, got #{result.class}"
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
result.symbolize_keys
|
|
319
|
+
end
|
|
320
|
+
|
|
291
321
|
def collection_striped_classes
|
|
292
322
|
return nil unless @striped
|
|
293
323
|
case @variant
|