better_ui 0.9.0 → 0.9.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.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/lib/better_ui/version.rb +1 -1
  3. data/spec/components/previews/better_ui/action_messages_component_preview/all_styles.html.erb +17 -0
  4. data/spec/components/previews/better_ui/action_messages_component_preview/all_variants.html.erb +19 -0
  5. data/spec/components/previews/better_ui/action_messages_component_preview/auto_dismiss.html.erb +51 -0
  6. data/spec/components/previews/better_ui/action_messages_component_preview/dismissible.html.erb +19 -0
  7. data/spec/components/previews/better_ui/action_messages_component_preview/with_title.html.erb +17 -0
  8. data/spec/components/previews/better_ui/action_messages_component_preview.rb +224 -0
  9. data/spec/components/previews/better_ui/avatar_component_preview/all_shapes.html.erb +26 -0
  10. data/spec/components/previews/better_ui/avatar_component_preview/all_sizes.html.erb +24 -0
  11. data/spec/components/previews/better_ui/avatar_component_preview/all_variants.html.erb +12 -0
  12. data/spec/components/previews/better_ui/avatar_component_preview/with_initials.html.erb +22 -0
  13. data/spec/components/previews/better_ui/avatar_component_preview/with_status.html.erb +26 -0
  14. data/spec/components/previews/better_ui/avatar_component_preview.rb +73 -0
  15. data/spec/components/previews/better_ui/badge_component_preview/all_sizes.html.erb +29 -0
  16. data/spec/components/previews/better_ui/badge_component_preview/all_styles.html.erb +26 -0
  17. data/spec/components/previews/better_ui/badge_component_preview/all_variants.html.erb +14 -0
  18. data/spec/components/previews/better_ui/badge_component_preview/counter_badges.html.erb +39 -0
  19. data/spec/components/previews/better_ui/badge_component_preview/dot_badges.html.erb +28 -0
  20. data/spec/components/previews/better_ui/badge_component_preview.rb +69 -0
  21. data/spec/components/previews/better_ui/breadcrumb/breadcrumb_component_preview/all_separators.html.erb +47 -0
  22. data/spec/components/previews/better_ui/breadcrumb/breadcrumb_component_preview/default.html.erb +23 -0
  23. data/spec/components/previews/better_ui/breadcrumb/breadcrumb_component_preview/with_icons.html.erb +43 -0
  24. data/spec/components/previews/better_ui/breadcrumb/breadcrumb_component_preview.rb +38 -0
  25. data/spec/components/previews/better_ui/button_component_preview/all_sizes.html.erb +25 -0
  26. data/spec/components/previews/better_ui/button_component_preview/all_variants.html.erb +14 -0
  27. data/spec/components/previews/better_ui/button_component_preview/as_links.html.erb +18 -0
  28. data/spec/components/previews/better_ui/button_component_preview/auto_loading_submit.html.erb +112 -0
  29. data/spec/components/previews/better_ui/button_component_preview/external_links.html.erb +61 -0
  30. data/spec/components/previews/better_ui/button_component_preview/form_integration.html.erb +102 -0
  31. data/spec/components/previews/better_ui/button_component_preview/interactive.html.erb +149 -0
  32. data/spec/components/previews/better_ui/button_component_preview/link_states.html.erb +36 -0
  33. data/spec/components/previews/better_ui/button_component_preview/loading_states.html.erb +62 -0
  34. data/spec/components/previews/better_ui/button_component_preview/turbo_method_links.html.erb +98 -0
  35. data/spec/components/previews/better_ui/button_component_preview/with_icons.html.erb +123 -0
  36. data/spec/components/previews/better_ui/button_component_preview.rb +155 -0
  37. data/spec/components/previews/better_ui/card_component_preview/all_sizes.html.erb +10 -0
  38. data/spec/components/previews/better_ui/card_component_preview/all_styles.html.erb +22 -0
  39. data/spec/components/previews/better_ui/card_component_preview/all_variants.html.erb +10 -0
  40. data/spec/components/previews/better_ui/card_component_preview.rb +269 -0
  41. data/spec/components/previews/better_ui/container_component_preview/all_sizes.html.erb +13 -0
  42. data/spec/components/previews/better_ui/container_component_preview.rb +59 -0
  43. data/spec/components/previews/better_ui/dialog/alert_component_preview/all_variants.html.erb +17 -0
  44. data/spec/components/previews/better_ui/dialog/alert_component_preview/custom_button_label.html.erb +14 -0
  45. data/spec/components/previews/better_ui/dialog/alert_component_preview/default.html.erb +13 -0
  46. data/spec/components/previews/better_ui/dialog/alert_component_preview/playground.html.erb +16 -0
  47. data/spec/components/previews/better_ui/dialog/alert_component_preview/without_icon.html.erb +14 -0
  48. data/spec/components/previews/better_ui/dialog/alert_component_preview.rb +57 -0
  49. data/spec/components/previews/better_ui/dialog/confirm_component_preview/all_variants.html.erb +17 -0
  50. data/spec/components/previews/better_ui/dialog/confirm_component_preview/custom_labels.html.erb +15 -0
  51. data/spec/components/previews/better_ui/dialog/confirm_component_preview/danger_confirm.html.erb +15 -0
  52. data/spec/components/previews/better_ui/dialog/confirm_component_preview/default.html.erb +13 -0
  53. data/spec/components/previews/better_ui/dialog/confirm_component_preview/playground.html.erb +17 -0
  54. data/spec/components/previews/better_ui/dialog/confirm_component_preview.rb +60 -0
  55. data/spec/components/previews/better_ui/dialog/dialog_component_preview/all_sizes.html.erb +32 -0
  56. data/spec/components/previews/better_ui/dialog/dialog_component_preview/default.html.erb +34 -0
  57. data/spec/components/previews/better_ui/dialog/dialog_component_preview/no_close_button.html.erb +28 -0
  58. data/spec/components/previews/better_ui/dialog/dialog_component_preview/playground.html.erb +39 -0
  59. data/spec/components/previews/better_ui/dialog/dialog_component_preview/with_all_slots.html.erb +52 -0
  60. data/spec/components/previews/better_ui/dialog/dialog_component_preview.rb +51 -0
  61. data/spec/components/previews/better_ui/divider_component_preview/all_styles.html.erb +58 -0
  62. data/spec/components/previews/better_ui/divider_component_preview/with_labels.html.erb +67 -0
  63. data/spec/components/previews/better_ui/divider_component_preview.rb +62 -0
  64. data/spec/components/previews/better_ui/drawer/header_component_preview.rb +169 -0
  65. data/spec/components/previews/better_ui/drawer/layout_component_preview/complete_layout.html.erb +87 -0
  66. data/spec/components/previews/better_ui/drawer/layout_component_preview/dark_theme.html.erb +36 -0
  67. data/spec/components/previews/better_ui/drawer/layout_component_preview/dashboard_example.html.erb +188 -0
  68. data/spec/components/previews/better_ui/drawer/layout_component_preview/default.html.erb +22 -0
  69. data/spec/components/previews/better_ui/drawer/layout_component_preview/primary_theme.html.erb +36 -0
  70. data/spec/components/previews/better_ui/drawer/layout_component_preview/right_sidebar.html.erb +44 -0
  71. data/spec/components/previews/better_ui/drawer/layout_component_preview/with_header_only.html.erb +20 -0
  72. data/spec/components/previews/better_ui/drawer/layout_component_preview/with_sidebar_only.html.erb +21 -0
  73. data/spec/components/previews/better_ui/drawer/layout_component_preview.rb +91 -0
  74. data/spec/components/previews/better_ui/drawer/nav_group_component_preview/complete_navigation.html.erb +55 -0
  75. data/spec/components/previews/better_ui/drawer/nav_group_component_preview.rb +163 -0
  76. data/spec/components/previews/better_ui/drawer/nav_item_component_preview.rb +104 -0
  77. data/spec/components/previews/better_ui/drawer/sidebar_component_preview.rb +212 -0
  78. data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/all_sizes.html.erb +19 -0
  79. data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/default.html.erb +12 -0
  80. data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/disabled_items.html.erb +14 -0
  81. data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/placement_options.html.erb +16 -0
  82. data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/playground.html.erb +35 -0
  83. data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/with_dividers_and_headers.html.erb +18 -0
  84. data/spec/components/previews/better_ui/dropdown/dropdown_component_preview/with_icons.html.erb +34 -0
  85. data/spec/components/previews/better_ui/dropdown/dropdown_component_preview.rb +59 -0
  86. data/spec/components/previews/better_ui/fa_icon_component_preview/all_sizes.html.erb +17 -0
  87. data/spec/components/previews/better_ui/fa_icon_component_preview/all_styles.html.erb +19 -0
  88. data/spec/components/previews/better_ui/fa_icon_component_preview/all_variants.html.erb +26 -0
  89. data/spec/components/previews/better_ui/fa_icon_component_preview/animations.html.erb +26 -0
  90. data/spec/components/previews/better_ui/fa_icon_component_preview/transformations.html.erb +88 -0
  91. data/spec/components/previews/better_ui/fa_icon_component_preview.rb +85 -0
  92. data/spec/components/previews/better_ui/forms/checkbox_component_preview/all_sizes.html.erb +12 -0
  93. data/spec/components/previews/better_ui/forms/checkbox_component_preview/all_variants.html.erb +12 -0
  94. data/spec/components/previews/better_ui/forms/checkbox_component_preview/form_integration.html.erb +32 -0
  95. data/spec/components/previews/better_ui/forms/checkbox_component_preview.rb +143 -0
  96. data/spec/components/previews/better_ui/forms/checkbox_group_component_preview/all_variants.html.erb +14 -0
  97. data/spec/components/previews/better_ui/forms/checkbox_group_component_preview/form_integration.html.erb +47 -0
  98. data/spec/components/previews/better_ui/forms/checkbox_group_component_preview/orientations.html.erb +34 -0
  99. data/spec/components/previews/better_ui/forms/checkbox_group_component_preview.rb +150 -0
  100. data/spec/components/previews/better_ui/forms/number_input_component_preview/all_sizes.html.erb +14 -0
  101. data/spec/components/previews/better_ui/forms/number_input_component_preview/form_integration.html.erb +45 -0
  102. data/spec/components/previews/better_ui/forms/number_input_component_preview.rb +211 -0
  103. data/spec/components/previews/better_ui/forms/password_input_component_preview/all_sizes.html.erb +12 -0
  104. data/spec/components/previews/better_ui/forms/password_input_component_preview/confirm_password_example.html.erb +29 -0
  105. data/spec/components/previews/better_ui/forms/password_input_component_preview/form_integration.html.erb +34 -0
  106. data/spec/components/previews/better_ui/forms/password_input_component_preview.rb +181 -0
  107. data/spec/components/previews/better_ui/forms/select_component_preview/all_sizes.html.erb +13 -0
  108. data/spec/components/previews/better_ui/forms/select_component_preview/all_states.html.erb +64 -0
  109. data/spec/components/previews/better_ui/forms/select_component_preview.rb +167 -0
  110. data/spec/components/previews/better_ui/forms/text_input_component_preview/all_sizes.html.erb +12 -0
  111. data/spec/components/previews/better_ui/forms/text_input_component_preview/all_types.html.erb +12 -0
  112. data/spec/components/previews/better_ui/forms/text_input_component_preview/form_integration.html.erb +33 -0
  113. data/spec/components/previews/better_ui/forms/text_input_component_preview.rb +247 -0
  114. data/spec/components/previews/better_ui/forms/textarea_component_preview/all_resize_variants.html.erb +13 -0
  115. data/spec/components/previews/better_ui/forms/textarea_component_preview/all_sizes.html.erb +12 -0
  116. data/spec/components/previews/better_ui/forms/textarea_component_preview/form_integration.html.erb +36 -0
  117. data/spec/components/previews/better_ui/forms/textarea_component_preview.rb +239 -0
  118. data/spec/components/previews/better_ui/heading_component_preview/all_alignments.html.erb +12 -0
  119. data/spec/components/previews/better_ui/heading_component_preview/all_levels.html.erb +7 -0
  120. data/spec/components/previews/better_ui/heading_component_preview/all_variants.html.erb +14 -0
  121. data/spec/components/previews/better_ui/heading_component_preview.rb +113 -0
  122. data/spec/components/previews/better_ui/link_component_preview/all_sizes.html.erb +25 -0
  123. data/spec/components/previews/better_ui/link_component_preview/all_styles.html.erb +14 -0
  124. data/spec/components/previews/better_ui/link_component_preview/all_variants.html.erb +14 -0
  125. data/spec/components/previews/better_ui/link_component_preview/with_icons.html.erb +66 -0
  126. data/spec/components/previews/better_ui/link_component_preview.rb +66 -0
  127. data/spec/components/previews/better_ui/progress_component_preview/all_sizes.html.erb +15 -0
  128. data/spec/components/previews/better_ui/progress_component_preview/all_variants.html.erb +11 -0
  129. data/spec/components/previews/better_ui/progress_component_preview.rb +64 -0
  130. data/spec/components/previews/better_ui/spinner_component_preview/all_sizes.html.erb +17 -0
  131. data/spec/components/previews/better_ui/spinner_component_preview/all_variants.html.erb +11 -0
  132. data/spec/components/previews/better_ui/spinner_component_preview.rb +44 -0
  133. data/spec/components/previews/better_ui/table/table_component_preview/all_sizes.html.erb +28 -0
  134. data/spec/components/previews/better_ui/table/table_component_preview/all_variants.html.erb +34 -0
  135. data/spec/components/previews/better_ui/table/table_component_preview/bordered.html.erb +33 -0
  136. data/spec/components/previews/better_ui/table/table_component_preview/collection_mode.html.erb +31 -0
  137. data/spec/components/previews/better_ui/table/table_component_preview/default.html.erb +33 -0
  138. data/spec/components/previews/better_ui/table/table_component_preview/empty_state.html.erb +36 -0
  139. data/spec/components/previews/better_ui/table/table_component_preview/highlighted.html.erb +64 -0
  140. data/spec/components/previews/better_ui/table/table_component_preview/hoverable.html.erb +27 -0
  141. data/spec/components/previews/better_ui/table/table_component_preview/inside_card.html.erb +173 -0
  142. data/spec/components/previews/better_ui/table/table_component_preview/sortable.html.erb +44 -0
  143. data/spec/components/previews/better_ui/table/table_component_preview/striped.html.erb +31 -0
  144. data/spec/components/previews/better_ui/table/table_component_preview/with_footer.html.erb +40 -0
  145. data/spec/components/previews/better_ui/table/table_component_preview.rb +79 -0
  146. data/spec/components/previews/better_ui/tabs/container_component_preview/alignments.html.erb +24 -0
  147. data/spec/components/previews/better_ui/tabs/container_component_preview/all_sizes.html.erb +24 -0
  148. data/spec/components/previews/better_ui/tabs/container_component_preview/all_variants.html.erb +24 -0
  149. data/spec/components/previews/better_ui/tabs/container_component_preview/bordered_style.html.erb +30 -0
  150. data/spec/components/previews/better_ui/tabs/container_component_preview/default.html.erb +30 -0
  151. data/spec/components/previews/better_ui/tabs/container_component_preview/disabled_tab.html.erb +65 -0
  152. data/spec/components/previews/better_ui/tabs/container_component_preview/pills_style.html.erb +34 -0
  153. data/spec/components/previews/better_ui/tabs/container_component_preview/turbo_mode.html.erb +40 -0
  154. data/spec/components/previews/better_ui/tabs/container_component_preview/vertical_left.html.erb +38 -0
  155. data/spec/components/previews/better_ui/tabs/container_component_preview/vertical_right.html.erb +30 -0
  156. data/spec/components/previews/better_ui/tabs/container_component_preview/with_icons_and_badges.html.erb +71 -0
  157. data/spec/components/previews/better_ui/tabs/container_component_preview.rb +130 -0
  158. data/spec/components/previews/better_ui/tag_component_preview/all_styles.html.erb +14 -0
  159. data/spec/components/previews/better_ui/tag_component_preview/all_variants.html.erb +14 -0
  160. data/spec/components/previews/better_ui/tag_component_preview/as_links.html.erb +14 -0
  161. data/spec/components/previews/better_ui/tag_component_preview/dismissible.html.erb +34 -0
  162. data/spec/components/previews/better_ui/tag_component_preview.rb +56 -0
  163. data/spec/components/previews/better_ui/tooltip_component_preview/all_positions.html.erb +25 -0
  164. data/spec/components/previews/better_ui/tooltip_component_preview/variants.html.erb +37 -0
  165. data/spec/components/previews/better_ui/tooltip_component_preview.rb +40 -0
  166. metadata +164 -1
@@ -0,0 +1,149 @@
1
+ <div class="p-8 space-y-8">
2
+ <div>
3
+ <h3 class="text-lg font-semibold mb-4">Click Counter Demo</h3>
4
+ <div class="flex items-center gap-4">
5
+ <%= render BetterUi::ButtonComponent.new(
6
+ variant: :primary,
7
+ data: {
8
+ action: "click->counter#increment"
9
+ }
10
+ ) do %>
11
+ Click Me!
12
+ <% end %>
13
+
14
+ <span class="text-lg">Clicks: <span data-counter-target="count">0</span></span>
15
+ </div>
16
+ </div>
17
+
18
+ <div>
19
+ <h3 class="text-lg font-semibold mb-4">Alert Demo</h3>
20
+ <div class="flex flex-wrap gap-3">
21
+ <%= render BetterUi::ButtonComponent.new(
22
+ variant: :info,
23
+ onclick: "alert('Primary button clicked!')"
24
+ ) do %>
25
+ Show Alert
26
+ <% end %>
27
+
28
+ <%= render BetterUi::ButtonComponent.new(
29
+ variant: :success,
30
+ style: :outline,
31
+ onclick: "console.log('Success button clicked')"
32
+ ) do %>
33
+ Log to Console
34
+ <% end %>
35
+ </div>
36
+ </div>
37
+
38
+ <div>
39
+ <h3 class="text-lg font-semibold mb-4">Disabled Button Interaction</h3>
40
+ <div class="flex flex-wrap gap-3">
41
+ <%= render BetterUi::ButtonComponent.new(
42
+ variant: :primary,
43
+ disabled: true,
44
+ onclick: "alert('This should not fire')"
45
+ ) do %>
46
+ Disabled (Click Won't Work)
47
+ <% end %>
48
+
49
+ <%= render BetterUi::ButtonComponent.new(
50
+ variant: :primary,
51
+ show_loader: true,
52
+ onclick: "alert('This should not fire')"
53
+ ) do %>
54
+ Loading (Click Won't Work)
55
+ <% end %>
56
+ </div>
57
+ </div>
58
+
59
+ <div>
60
+ <h3 class="text-lg font-semibold mb-4">Button with Custom Data Attributes</h3>
61
+ <%= render BetterUi::ButtonComponent.new(
62
+ variant: :primary,
63
+ data: {
64
+ turbo_method: :post,
65
+ turbo_confirm: "Are you sure?",
66
+ custom_value: "example"
67
+ }
68
+ ) do %>
69
+ Button with Data Attributes
70
+ <% end %>
71
+ <p class="text-sm text-gray-600 mt-2">Inspect this button to see data-turbo-method, data-turbo-confirm, and data-custom-value attributes</p>
72
+ </div>
73
+
74
+ <div>
75
+ <h3 class="text-lg font-semibold mb-4">Buttons with ARIA Labels</h3>
76
+ <div class="flex flex-wrap gap-3">
77
+ <%= render BetterUi::ButtonComponent.new(
78
+ variant: :danger,
79
+ aria: { label: "Delete item" },
80
+ title: "Delete"
81
+ ) do |button| %>
82
+ <% button.with_icon_before do %>
83
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
84
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
85
+ </svg>
86
+ <% end %>
87
+ <% end %>
88
+
89
+ <%= render BetterUi::ButtonComponent.new(
90
+ variant: :primary,
91
+ aria: { label: "Edit item" },
92
+ title: "Edit"
93
+ ) do |button| %>
94
+ <% button.with_icon_before do %>
95
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
96
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
97
+ </svg>
98
+ <% end %>
99
+ <% end %>
100
+ </div>
101
+ <p class="text-sm text-gray-600 mt-2">Hover over buttons to see tooltips (title) and inspect for aria-label attributes</p>
102
+ </div>
103
+
104
+ <div>
105
+ <h3 class="text-lg font-semibold mb-4">Focus State Demo</h3>
106
+ <p class="text-sm text-gray-600 mb-2">Tab through these buttons to see focus states:</p>
107
+ <div class="flex flex-wrap gap-3">
108
+ <% (@variants || BetterUi::ApplicationComponent::VARIANTS.keys).each do |variant| %>
109
+ <%= render BetterUi::ButtonComponent.new(variant: variant) do %>
110
+ <%= variant.to_s.capitalize %>
111
+ <% end %>
112
+ <% end %>
113
+ </div>
114
+ </div>
115
+ </div>
116
+
117
+ <script data-turbo-track="reload">
118
+ // Simple counter controller for demo
119
+ if (!window.Stimulus) {
120
+ console.warn('Stimulus not loaded for interactive demo');
121
+ } else {
122
+ class CounterController extends window.Stimulus.Controller {
123
+ static targets = ["count"]
124
+
125
+ connect() {
126
+ this.count = 0;
127
+ }
128
+
129
+ increment() {
130
+ this.count++;
131
+ if (this.hasCountTarget) {
132
+ this.countTarget.textContent = this.count;
133
+ }
134
+ }
135
+ }
136
+
137
+ if (window.Stimulus.application) {
138
+ try {
139
+ window.Stimulus.application.register("counter", CounterController);
140
+ } catch(e) {
141
+ // Controller might already be registered
142
+ }
143
+ }
144
+ }
145
+ </script>
146
+
147
+ <div data-controller="counter">
148
+ <!-- Counter context for demo -->
149
+ </div>
@@ -0,0 +1,36 @@
1
+ <div class="p-8 space-y-8">
2
+ <div>
3
+ <h3 class="text-lg font-semibold mb-4">Normal Link</h3>
4
+ <div class="flex gap-3">
5
+ <%= render BetterUi::ButtonComponent.new(href: "#") { "Click Me" } %>
6
+ <%= render BetterUi::ButtonComponent.new(href: "#", style: :outline) { "Outline Link" } %>
7
+ <%= render BetterUi::ButtonComponent.new(href: "#", style: :ghost) { "Ghost Link" } %>
8
+ </div>
9
+ </div>
10
+
11
+ <div>
12
+ <h3 class="text-lg font-semibold mb-4">Disabled Links</h3>
13
+ <div class="flex gap-3">
14
+ <%= render BetterUi::ButtonComponent.new(href: "#", disabled: true) { "Disabled" } %>
15
+ <%= render BetterUi::ButtonComponent.new(href: "#", disabled: true, style: :outline) { "Disabled Outline" } %>
16
+ <%= render BetterUi::ButtonComponent.new(href: "#", disabled: true, variant: :danger) { "Disabled Danger" } %>
17
+ </div>
18
+ </div>
19
+
20
+ <div>
21
+ <h3 class="text-lg font-semibold mb-4">Loading Links</h3>
22
+ <div class="flex gap-3">
23
+ <%= render BetterUi::ButtonComponent.new(href: "#", show_loader: true) { "Loading..." } %>
24
+ <%= render BetterUi::ButtonComponent.new(href: "#", show_loader: true, variant: :success) { "Loading Success" } %>
25
+ </div>
26
+ </div>
27
+
28
+ <div>
29
+ <h3 class="text-lg font-semibold mb-4">Show Loader on Click</h3>
30
+ <p class="text-sm text-gray-600 mb-2">Click the links to see the loading state</p>
31
+ <div class="flex gap-3">
32
+ <%= render BetterUi::ButtonComponent.new(href: "#", show_loader_on_click: true) { "Click to Load" } %>
33
+ <%= render BetterUi::ButtonComponent.new(href: "#", show_loader_on_click: true, variant: :info) { "Info Link" } %>
34
+ </div>
35
+ </div>
36
+ </div>
@@ -0,0 +1,62 @@
1
+ <div class="p-8 space-y-8">
2
+ <div>
3
+ <h3 class="text-lg font-semibold mb-4">Loading States - All Styles</h3>
4
+ <div class="flex flex-wrap gap-3">
5
+ <%= render BetterUi::ButtonComponent.new(variant: :primary, style: :solid, show_loader: true) do %>
6
+ Saving
7
+ <% end %>
8
+
9
+ <%= render BetterUi::ButtonComponent.new(variant: :secondary, style: :outline, show_loader: true) do %>
10
+ Loading
11
+ <% end %>
12
+
13
+ <%= render BetterUi::ButtonComponent.new(variant: :success, style: :ghost, show_loader: true) do %>
14
+ Processing
15
+ <% end %>
16
+
17
+ <%= render BetterUi::ButtonComponent.new(variant: :info, style: :soft, show_loader: true) do %>
18
+ Updating
19
+ <% end %>
20
+ </div>
21
+ </div>
22
+
23
+ <div>
24
+ <h3 class="text-lg font-semibold mb-4">Loading States - All Sizes</h3>
25
+ <div class="flex items-center flex-wrap gap-3">
26
+ <% (@sizes || [:xs, :sm, :md, :lg, :xl]).each do |size| %>
27
+ <%= render BetterUi::ButtonComponent.new(variant: :primary, size: size, show_loader: true) do %>
28
+ Loading <%= size.to_s.upcase %>
29
+ <% end %>
30
+ <% end %>
31
+ </div>
32
+ </div>
33
+
34
+ <div>
35
+ <h3 class="text-lg font-semibold mb-4">Loading States - All Variants</h3>
36
+ <div class="flex flex-wrap gap-3">
37
+ <% (@variants || BetterUi::ApplicationComponent::VARIANTS.keys).each do |variant| %>
38
+ <%= render BetterUi::ButtonComponent.new(variant: variant, show_loader: true) do %>
39
+ <%= variant.to_s.capitalize %>
40
+ <% end %>
41
+ <% end %>
42
+ </div>
43
+ </div>
44
+
45
+ <div>
46
+ <h3 class="text-lg font-semibold mb-4">Loading vs Normal States Comparison</h3>
47
+ <div class="grid grid-cols-2 gap-4">
48
+ <div>
49
+ <p class="text-sm text-gray-600 mb-2">Normal State</p>
50
+ <%= render BetterUi::ButtonComponent.new(variant: :primary, show_loader_on_click: true, type: :submit) do %>
51
+ Submit Form
52
+ <% end %>
53
+ </div>
54
+ <div>
55
+ <p class="text-sm text-gray-600 mb-2">Loading State</p>
56
+ <%= render BetterUi::ButtonComponent.new(variant: :primary, show_loader: true) do %>
57
+ Submit Form
58
+ <% end %>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </div>
@@ -0,0 +1,98 @@
1
+ <div class="p-8 space-y-6">
2
+ <div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
3
+ <p class="text-sm text-yellow-800">
4
+ These links use <code class="bg-yellow-100 px-1 rounded">data-turbo-method</code> for non-GET requests via Turbo Drive.
5
+ Clicking them will trigger the specified HTTP method.
6
+ </p>
7
+ </div>
8
+
9
+ <div>
10
+ <h3 class="text-lg font-semibold mb-4">DELETE Method Link</h3>
11
+ <p class="text-sm text-gray-600 mb-2">Commonly used for delete actions</p>
12
+ <%= render BetterUi::ButtonComponent.new(
13
+ href: "#",
14
+ method: :delete,
15
+ variant: :danger,
16
+ style: :outline
17
+ ) do |c|
18
+ c.with_icon_before do %>
19
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
20
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
21
+ </svg>
22
+ <% end
23
+ "Delete Item"
24
+ end %>
25
+ </div>
26
+
27
+ <div>
28
+ <h3 class="text-lg font-semibold mb-4">POST Method Link</h3>
29
+ <p class="text-sm text-gray-600 mb-2">Commonly used for create actions</p>
30
+ <%= render BetterUi::ButtonComponent.new(
31
+ href: "#",
32
+ method: :post,
33
+ variant: :success
34
+ ) do |c|
35
+ c.with_icon_before do %>
36
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
37
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
38
+ </svg>
39
+ <% end
40
+ "Create Item"
41
+ end %>
42
+ </div>
43
+
44
+ <div>
45
+ <h3 class="text-lg font-semibold mb-4">PATCH Method Link</h3>
46
+ <p class="text-sm text-gray-600 mb-2">Commonly used for update actions</p>
47
+ <%= render BetterUi::ButtonComponent.new(
48
+ href: "#",
49
+ method: :patch,
50
+ variant: :info
51
+ ) do |c|
52
+ c.with_icon_before do %>
53
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
54
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
55
+ </svg>
56
+ <% end
57
+ "Update Item"
58
+ end %>
59
+ </div>
60
+
61
+ <div>
62
+ <h3 class="text-lg font-semibold mb-4">PUT Method Link</h3>
63
+ <p class="text-sm text-gray-600 mb-2">Commonly used for full resource replacement</p>
64
+ <%= render BetterUi::ButtonComponent.new(
65
+ href: "#",
66
+ method: :put,
67
+ variant: :warning,
68
+ style: :soft
69
+ ) do |c|
70
+ c.with_icon_before do %>
71
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
72
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
73
+ </svg>
74
+ <% end
75
+ "Replace Item"
76
+ end %>
77
+ </div>
78
+
79
+ <div>
80
+ <h3 class="text-lg font-semibold mb-4">Combined with Loading State</h3>
81
+ <p class="text-sm text-gray-600 mb-2">Click to see loading state while request processes</p>
82
+ <div class="flex gap-3">
83
+ <%= render BetterUi::ButtonComponent.new(
84
+ href: "#",
85
+ method: :delete,
86
+ variant: :danger,
87
+ show_loader_on_click: true
88
+ ) { "Delete with Loading" } %>
89
+
90
+ <%= render BetterUi::ButtonComponent.new(
91
+ href: "#",
92
+ method: :post,
93
+ variant: :success,
94
+ show_loader_on_click: true
95
+ ) { "Create with Loading" } %>
96
+ </div>
97
+ </div>
98
+ </div>
@@ -0,0 +1,123 @@
1
+ <div class="p-8 space-y-8">
2
+ <div>
3
+ <h3 class="text-lg font-semibold mb-4">Icon Before Text</h3>
4
+ <div class="flex flex-wrap gap-3">
5
+ <%= render BetterUi::ButtonComponent.new(variant: :primary) do |button| %>
6
+ <% button.with_icon_before do %>
7
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
8
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
9
+ </svg>
10
+ <% end %>
11
+ Login
12
+ <% end %>
13
+
14
+ <%= render BetterUi::ButtonComponent.new(variant: :success, style: :outline) do |button| %>
15
+ <% button.with_icon_before do %>
16
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
17
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
18
+ </svg>
19
+ <% end %>
20
+ Save
21
+ <% end %>
22
+
23
+ <%= render BetterUi::ButtonComponent.new(variant: :info, style: :ghost) do |button| %>
24
+ <% button.with_icon_before do %>
25
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
26
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
27
+ </svg>
28
+ <% end %>
29
+ Add New
30
+ <% end %>
31
+ </div>
32
+ </div>
33
+
34
+ <div>
35
+ <h3 class="text-lg font-semibold mb-4">Icon After Text</h3>
36
+ <div class="flex flex-wrap gap-3">
37
+ <%= render BetterUi::ButtonComponent.new(variant: :primary) do |button| %>
38
+ Continue
39
+ <% button.with_icon_after do %>
40
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
41
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"></path>
42
+ </svg>
43
+ <% end %>
44
+ <% end %>
45
+
46
+ <%= render BetterUi::ButtonComponent.new(variant: :secondary, style: :outline) do |button| %>
47
+ Next Page
48
+ <% button.with_icon_after do %>
49
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
50
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
51
+ </svg>
52
+ <% end %>
53
+ <% end %>
54
+
55
+ <%= render BetterUi::ButtonComponent.new(variant: :info, style: :soft) do |button| %>
56
+ Download
57
+ <% button.with_icon_after do %>
58
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
59
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
60
+ </svg>
61
+ <% end %>
62
+ <% end %>
63
+ </div>
64
+ </div>
65
+
66
+ <div>
67
+ <h3 class="text-lg font-semibold mb-4">Icon Only Buttons</h3>
68
+ <div class="flex flex-wrap gap-3">
69
+ <%= render BetterUi::ButtonComponent.new(variant: :danger, size: :md, aria: { label: "Delete" }) do |button| %>
70
+ <% button.with_icon_before do %>
71
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
72
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
73
+ </svg>
74
+ <% end %>
75
+ <% end %>
76
+
77
+ <%= render BetterUi::ButtonComponent.new(variant: :primary, size: :md, style: :outline, aria: { label: "Edit" }) do |button| %>
78
+ <% button.with_icon_before do %>
79
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
80
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
81
+ </svg>
82
+ <% end %>
83
+ <% end %>
84
+
85
+ <%= render BetterUi::ButtonComponent.new(variant: :secondary, size: :md, style: :ghost, aria: { label: "Settings" }) do |button| %>
86
+ <% button.with_icon_before do %>
87
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
88
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
89
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
90
+ </svg>
91
+ <% end %>
92
+ <% end %>
93
+
94
+ <%= render BetterUi::ButtonComponent.new(variant: :info, size: :sm, style: :soft, aria: { label: "Info" }) do |button| %>
95
+ <% button.with_icon_before do %>
96
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
97
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
98
+ </svg>
99
+ <% end %>
100
+ <% end %>
101
+ </div>
102
+ </div>
103
+
104
+ <div>
105
+ <h3 class="text-lg font-semibold mb-4">Different Sizes with Icons</h3>
106
+ <div class="flex items-center flex-wrap gap-3">
107
+ <%
108
+ sizes = @sizes || [:xs, :sm, :md, :lg, :xl]
109
+ icon_sizes = @icon_sizes || { xs: "w-3 h-3", sm: "w-4 h-4", md: "w-5 h-5", lg: "w-6 h-6", xl: "w-7 h-7" }
110
+ sizes.each do |size|
111
+ %>
112
+ <%= render BetterUi::ButtonComponent.new(variant: :primary, size: size) do |button| %>
113
+ <% button.with_icon_before do %>
114
+ <svg class="<%= icon_sizes[size] %>" fill="none" stroke="currentColor" viewBox="0 0 24 24">
115
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
116
+ </svg>
117
+ <% end %>
118
+ <%= size.to_s.upcase %>
119
+ <% end %>
120
+ <% end %>
121
+ </div>
122
+ </div>
123
+ </div>
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ class ButtonComponentPreview < ViewComponent::Preview
5
+ # @label Default
6
+ def default
7
+ render BetterUi::ButtonComponent.new do
8
+ "Click me"
9
+ end
10
+ end
11
+
12
+ # @label All Variants
13
+ # @display bg_color #f5f5f5
14
+ def all_variants
15
+ @variants = ApplicationComponent::VARIANTS.keys
16
+ @styles = [ :solid, :outline, :ghost, :soft ]
17
+ render_with_template
18
+ end
19
+
20
+ # @label All Sizes
21
+ def all_sizes
22
+ @sizes = [ :xs, :sm, :md, :lg, :xl ]
23
+ render_with_template
24
+ end
25
+
26
+ # @label With Icons
27
+ def with_icons
28
+ @sizes = [ :xs, :sm, :md, :lg, :xl ]
29
+ @icon_sizes = {
30
+ xs: "w-3 h-3",
31
+ sm: "w-4 h-4",
32
+ md: "w-5 h-5",
33
+ lg: "w-6 h-6",
34
+ xl: "w-7 h-7"
35
+ }
36
+ render_with_template
37
+ end
38
+
39
+ # @label Loading States
40
+ def loading_states
41
+ @variants = ApplicationComponent::VARIANTS.keys
42
+ @sizes = [ :xs, :sm, :md, :lg, :xl ]
43
+ render_with_template
44
+ end
45
+
46
+ # @label Form Integration
47
+ def form_integration
48
+ render_with_template
49
+ end
50
+
51
+ # @label Interactive Demo
52
+ def interactive
53
+ @variants = ApplicationComponent::VARIANTS.keys.take(5)
54
+ render_with_template
55
+ end
56
+
57
+ # @label Playground
58
+ # @param variant select { choices: [primary, secondary, accent, success, danger, warning, info, light, dark] }
59
+ # @param style select { choices: [solid, outline, ghost, soft] }
60
+ # @param size select { choices: [xs, sm, md, lg, xl] }
61
+ # @param show_loader toggle
62
+ # @param show_loader_on_click toggle
63
+ # @param disabled toggle
64
+ # @param icon_position select { choices: [none, before, after, both] }
65
+ def playground(variant: :primary, style: :solid, size: :md, show_loader: false, show_loader_on_click: false, disabled: false, icon_position: :none)
66
+ render BetterUi::ButtonComponent.new(
67
+ variant: variant,
68
+ style: style,
69
+ size: size,
70
+ show_loader: show_loader,
71
+ show_loader_on_click: show_loader_on_click,
72
+ disabled: disabled
73
+ ) do |button|
74
+ # Add icons based on selection
75
+ if [ :before, :both ].include?(icon_position)
76
+ button.with_icon_before do
77
+ '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
78
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
79
+ </svg>'.html_safe
80
+ end
81
+ end
82
+
83
+ if [ :after, :both ].include?(icon_position)
84
+ button.with_icon_after do
85
+ '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
86
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"></path>
87
+ </svg>'.html_safe
88
+ end
89
+ end
90
+
91
+ "Interactive Button"
92
+ end
93
+ end
94
+
95
+ # @label Auto-Loading Submit Button
96
+ def auto_loading_submit
97
+ render_with_template
98
+ end
99
+
100
+ # @label As Links
101
+ # @display bg_color #f5f5f5
102
+ def as_links
103
+ render_with_template
104
+ end
105
+
106
+ # @label Link States
107
+ def link_states
108
+ render_with_template
109
+ end
110
+
111
+ # @label External Links
112
+ def external_links
113
+ render_with_template
114
+ end
115
+
116
+ # @label Turbo Method Links
117
+ def turbo_method_links
118
+ render_with_template
119
+ end
120
+
121
+ # @label Link Playground
122
+ # @param variant select { choices: [primary, secondary, accent, success, danger, warning, info, light, dark] }
123
+ # @param style select { choices: [solid, outline, ghost, soft] }
124
+ # @param size select { choices: [xs, sm, md, lg, xl] }
125
+ # @param disabled toggle
126
+ # @param show_loader toggle
127
+ # @param show_loader_on_click toggle
128
+ # @param target select { choices: [none, blank, self] }
129
+ def link_playground(variant: :primary, style: :solid, size: :md, disabled: false, show_loader: false, show_loader_on_click: false, target: :none)
130
+ target_val = case target
131
+ when :blank then "_blank"
132
+ when :self then "_self"
133
+ else nil
134
+ end
135
+
136
+ # Convert to boolean - handles both true/false and "true"/"false"
137
+ disabled_bool = ActiveModel::Type::Boolean.new.cast(disabled)
138
+ show_loader_bool = ActiveModel::Type::Boolean.new.cast(show_loader)
139
+ show_loader_on_click_bool = ActiveModel::Type::Boolean.new.cast(show_loader_on_click)
140
+
141
+ render BetterUi::ButtonComponent.new(
142
+ href: "#",
143
+ variant: variant,
144
+ style: style,
145
+ size: size,
146
+ target: target_val,
147
+ disabled: disabled_bool,
148
+ show_loader: show_loader_bool,
149
+ show_loader_on_click: show_loader_on_click_bool
150
+ ) do
151
+ "Link Button"
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,10 @@
1
+ <div class="space-y-4 p-4">
2
+ <% [:xs, :sm, :md, :lg, :xl].each do |size| %>
3
+ <div>
4
+ <%= render BetterUi::CardComponent.new(size: size) do |card| %>
5
+ <% card.with_header { "Size: #{size.to_s.upcase}" } %>
6
+ <% card.with_body { "This card demonstrates the #{size} size variant with appropriate padding and text sizing." } %>
7
+ <% end %>
8
+ </div>
9
+ <% end %>
10
+ </div>
@@ -0,0 +1,22 @@
1
+ <div class="space-y-4 p-4">
2
+ <% [:solid, :outline, :ghost, :soft, :bordered].each do |style| %>
3
+ <div>
4
+ <%= render BetterUi::CardComponent.new(variant: :primary, style: style) do |card| %>
5
+ <% card.with_header { "#{style.to_s.capitalize} Style" } %>
6
+ <% card.with_body do %>
7
+ <% if style == :solid %>
8
+ Filled background with subtle border and dark text. Best for emphasis.
9
+ <% elsif style == :outline %>
10
+ White background with colored border. Clean and minimal.
11
+ <% elsif style == :ghost %>
12
+ Transparent background with colored text. Most subtle.
13
+ <% elsif style == :soft %>
14
+ Light colored background with subtle border. Gentle appearance.
15
+ <% elsif style == :bordered %>
16
+ White background with variant-colored border. Defaults to light variant.
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
20
+ </div>
21
+ <% end %>
22
+ </div>
@@ -0,0 +1,10 @@
1
+ <div class="space-y-4 p-4">
2
+ <% BetterUi::ApplicationComponent::VARIANTS.keys.each do |variant| %>
3
+ <div>
4
+ <%= render BetterUi::CardComponent.new(variant: variant, style: :solid) do |card| %>
5
+ <% card.with_header { variant.to_s.capitalize } %>
6
+ <% card.with_body { "This is a #{variant} card demonstrating the color variant with solid style." } %>
7
+ <% end %>
8
+ </div>
9
+ <% end %>
10
+ </div>