m9sh 0.2.1 → 0.2.2

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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/.idea/hotcdn.iml +30 -0
  3. data/.mise.toml +2 -2
  4. data/app/assets/config/manifest.js +4 -0
  5. data/app/assets/images/icons/activity.svg +3 -0
  6. data/app/assets/images/icons/bell.svg +4 -0
  7. data/app/assets/images/icons/book.svg +4 -0
  8. data/app/assets/images/icons/chevron-down.svg +3 -0
  9. data/app/assets/images/icons/chevron-left.svg +3 -0
  10. data/app/assets/images/icons/chevron-right.svg +3 -0
  11. data/app/assets/images/icons/credit-card.svg +4 -0
  12. data/app/assets/images/icons/dollar-sign.svg +3 -0
  13. data/app/assets/images/icons/edit.svg +4 -0
  14. data/app/assets/images/icons/github.svg +3 -0
  15. data/app/assets/images/icons/home.svg +4 -0
  16. data/app/assets/images/icons/info.svg +5 -0
  17. data/app/assets/images/icons/layout.svg +6 -0
  18. data/app/assets/images/icons/logout.svg +5 -0
  19. data/app/assets/images/icons/menu.svg +5 -0
  20. data/app/assets/images/icons/moon.svg +3 -0
  21. data/app/assets/images/icons/paintbrush.svg +6 -0
  22. data/app/assets/images/icons/search.svg +4 -0
  23. data/app/assets/images/icons/settings.svg +4 -0
  24. data/app/assets/images/icons/sun.svg +11 -0
  25. data/app/assets/images/icons/user.svg +4 -0
  26. data/app/assets/images/icons/users.svg +5 -0
  27. data/app/assets/stylesheets/tailwind.css +1180 -0
  28. data/app/components/backdrop_component.rb +103 -0
  29. data/app/components/docs/code_block_component.rb +56 -0
  30. data/app/components/docs/component_api_component.rb +16 -0
  31. data/app/components/docs/component_examples_component.rb +16 -0
  32. data/app/components/docs/component_header_component.html.erb +8 -0
  33. data/app/components/docs/component_header_component.rb +14 -0
  34. data/app/components/docs/component_installation_component.html.erb +15 -0
  35. data/app/components/docs/component_installation_component.rb +13 -0
  36. data/app/components/docs/component_page_component.html.erb +9 -0
  37. data/app/components/docs/component_page_component.rb +19 -0
  38. data/app/components/docs/component_preview_component.rb +318 -0
  39. data/app/components/docs/component_usage_component.rb +18 -0
  40. data/app/components/docs/prop_table_component.rb +64 -0
  41. data/app/controllers/application_controller.rb +3 -0
  42. data/app/controllers/blocks_controller.rb +51 -0
  43. data/app/controllers/docs_controller.rb +162 -0
  44. data/app/controllers/showcase_controller.rb +42 -0
  45. data/app/helpers/blocks_helper.rb +343 -0
  46. data/app/helpers/docs_helper.rb +3807 -0
  47. data/app/helpers/m9sh/toast_helper.rb +46 -0
  48. data/app/helpers/m9sh_helper.rb +343 -0
  49. data/app/javascript/application.js +3 -0
  50. data/app/javascript/controllers/application.js +9 -0
  51. data/app/javascript/controllers/backdrop_controller.js +137 -0
  52. data/app/javascript/controllers/color_customizer_controller.js +569 -0
  53. data/app/javascript/controllers/color_theme_controller.js +120 -0
  54. data/app/javascript/controllers/docs/component_preview_controller.js +149 -0
  55. data/app/javascript/controllers/docs/copy_button_controller.js +20 -0
  56. data/app/javascript/controllers/index.js +6 -0
  57. data/app/javascript/controllers/theme_controller.js +23 -0
  58. data/app/views/blocks/_sidebar.html.erb +31 -0
  59. data/app/views/blocks/_toc.html.erb +29 -0
  60. data/app/views/blocks/examples/dashboard-01.html.erb +180 -0
  61. data/app/views/blocks/examples/dashboard-02.html.erb +190 -0
  62. data/app/views/blocks/examples/dashboard-03.html.erb +210 -0
  63. data/app/views/blocks/examples/settings-01.html.erb +220 -0
  64. data/app/views/blocks/examples/settings-02.html.erb +231 -0
  65. data/app/views/blocks/examples/settings-03.html.erb +340 -0
  66. data/app/views/blocks/index.html.erb +65 -0
  67. data/app/views/docs/_sidebar.html.erb +47 -0
  68. data/app/views/docs/_toc.html.erb +19 -0
  69. data/app/views/docs/about.html.erb +68 -0
  70. data/app/views/docs/components/accordion.html.erb +196 -0
  71. data/app/views/docs/components/alert.html.erb +272 -0
  72. data/app/views/docs/components/alert_dialog.html.erb +232 -0
  73. data/app/views/docs/components/avatar.html.erb +207 -0
  74. data/app/views/docs/components/badge.html.erb +145 -0
  75. data/app/views/docs/components/breadcrumb.html.erb +264 -0
  76. data/app/views/docs/components/button.html.erb +229 -0
  77. data/app/views/docs/components/card.html.erb +378 -0
  78. data/app/views/docs/components/checkbox.html.erb +212 -0
  79. data/app/views/docs/components/collapsible.html.erb +252 -0
  80. data/app/views/docs/components/dialog.html.erb +323 -0
  81. data/app/views/docs/components/dropdown_menu.html.erb +289 -0
  82. data/app/views/docs/components/hover_card.html.erb +220 -0
  83. data/app/views/docs/components/input.html.erb +254 -0
  84. data/app/views/docs/components/label.html.erb +128 -0
  85. data/app/views/docs/components/main.html.erb +352 -0
  86. data/app/views/docs/components/navbar.html.erb +394 -0
  87. data/app/views/docs/components/navigation_menu.html.erb +226 -0
  88. data/app/views/docs/components/popover.html.erb +267 -0
  89. data/app/views/docs/components/progress.html.erb +107 -0
  90. data/app/views/docs/components/radio_group.html.erb +209 -0
  91. data/app/views/docs/components/select.html.erb +260 -0
  92. data/app/views/docs/components/separator.html.erb +162 -0
  93. data/app/views/docs/components/sheet.html.erb +270 -0
  94. data/app/views/docs/components/sidebar.html.erb +597 -0
  95. data/app/views/docs/components/skeleton.html.erb +150 -0
  96. data/app/views/docs/components/slider.html.erb +218 -0
  97. data/app/views/docs/components/spinner.html.erb +132 -0
  98. data/app/views/docs/components/switch.html.erb +148 -0
  99. data/app/views/docs/components/table.html.erb +259 -0
  100. data/app/views/docs/components/tabs.html.erb +225 -0
  101. data/app/views/docs/components/textarea.html.erb +239 -0
  102. data/app/views/docs/components/theme_toggle.html.erb +135 -0
  103. data/app/views/docs/components/toast.html.erb +205 -0
  104. data/app/views/docs/components/toaster.html.erb +227 -0
  105. data/app/views/docs/components/toggle.html.erb +154 -0
  106. data/app/views/docs/components/tooltip.html.erb +216 -0
  107. data/app/views/docs/components/typography.html.erb +180 -0
  108. data/app/views/docs/index.html.erb +143 -0
  109. data/app/views/docs/installation.html.erb +155 -0
  110. data/app/views/docs/simple_test.html.erb +13 -0
  111. data/app/views/docs/test_accordion.html.erb +14 -0
  112. data/app/views/docs/usage.html.erb +272 -0
  113. data/app/views/layouts/application.html.erb +107 -0
  114. data/app/views/layouts/backdrop.html.erb +77 -0
  115. data/app/views/shared/_app_navbar.html.erb +240 -0
  116. data/app/views/shared/_navbar.html.erb +69 -0
  117. data/app/views/showcase/v2/_components_grid.html.erb +38 -0
  118. data/app/views/showcase/v2/_features.html.erb +59 -0
  119. data/app/views/showcase/v2/_forms.html.erb +195 -0
  120. data/app/views/showcase/v2/_hero.html.erb +55 -0
  121. data/app/views/showcase/v2/_metrics.html.erb +107 -0
  122. data/app/views/showcase/v2.html.erb +18 -0
  123. data/lib/m9sh/version.rb +1 -1
  124. data/m9sh.gemspec +1 -1
  125. metadata +120 -1
@@ -0,0 +1,289 @@
1
+ <%= render Docs::ComponentPageComponent.new(title: "Dropdown Menu") do |page| %>
2
+ <% page.with_header(
3
+ name: "Dropdown Menu",
4
+ description: "Displays a menu to the user with a list of actions or options."
5
+ ) %>
6
+
7
+ <% page.with_installation(component_name: "dropdown_menu") %>
8
+
9
+ <% page.with_usage do %>
10
+ <%= render Docs::CodeBlockComponent.new(
11
+ code: dropdown_menu_usage_code,
12
+ language: "erb"
13
+ ) %>
14
+ <% end %>
15
+
16
+ <% page.with_examples do %>
17
+ <!-- Default Example -->
18
+ <% default_html = capture do %>
19
+ <div class="flex justify-center items-center p-8">
20
+ <%= render M9sh::DropdownMenuComponent.new do %>
21
+ <%= render M9sh::DropdownMenuTriggerComponent.new(class: "px-4 py-2 rounded-md border border-border bg-background hover:bg-muted transition-colors") do %>
22
+ Open
23
+ <% end %>
24
+ <%= render M9sh::DropdownMenuContentComponent.new do %>
25
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
26
+ Profile
27
+ <% end %>
28
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
29
+ Settings
30
+ <% end %>
31
+ <%= render M9sh::DropdownMenuSeparatorComponent.new %>
32
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
33
+ Logout
34
+ <% end %>
35
+ <% end %>
36
+ <% end %>
37
+ </div>
38
+ <% end %>
39
+
40
+ <%= render Docs::ComponentPreviewComponent.new(
41
+ title: "Default",
42
+ preview_content: default_html,
43
+ code: dropdown_menu_default_code,
44
+ ai_command: dropdown_menu_default_code
45
+ ) %>
46
+
47
+ <!-- With Icons Example -->
48
+ <% icons_html = capture do %>
49
+ <div class="flex justify-center items-center p-8">
50
+ <%= render M9sh::DropdownMenuComponent.new do %>
51
+ <%= render M9sh::DropdownMenuTriggerComponent.new(class: "px-4 py-2 rounded-md border border-border bg-background hover:bg-muted transition-colors") do %>
52
+ Open Menu
53
+ <% end %>
54
+ <%= render M9sh::DropdownMenuContentComponent.new do %>
55
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
56
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2">
57
+ <path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
58
+ <circle cx="12" cy="7" r="4"/>
59
+ </svg>
60
+ Profile
61
+ <% end %>
62
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
63
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2">
64
+ <path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/>
65
+ <circle cx="12" cy="12" r="3"/>
66
+ </svg>
67
+ Settings
68
+ <% end %>
69
+ <%= render M9sh::DropdownMenuSeparatorComponent.new %>
70
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
71
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2">
72
+ <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
73
+ <polyline points="16 17 21 12 16 7"/>
74
+ <line x1="21" y1="12" x2="9" y2="12"/>
75
+ </svg>
76
+ Logout
77
+ <% end %>
78
+ <% end %>
79
+ <% end %>
80
+ </div>
81
+ <% end %>
82
+
83
+ <%= render Docs::ComponentPreviewComponent.new(
84
+ title: "With Icons",
85
+ preview_content: icons_html,
86
+ code: dropdown_menu_with_icons_code,
87
+ ai_command: dropdown_menu_with_icons_code
88
+ ) %>
89
+
90
+ <!-- With Separators Example -->
91
+ <% separators_html = capture do %>
92
+ <div class="flex justify-center items-center p-8">
93
+ <%= render M9sh::DropdownMenuComponent.new do %>
94
+ <%= render M9sh::DropdownMenuTriggerComponent.new(class: "px-4 py-2 rounded-md border border-border bg-background hover:bg-muted transition-colors") do %>
95
+ Options
96
+ <% end %>
97
+ <%= render M9sh::DropdownMenuContentComponent.new do %>
98
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
99
+ New Tab
100
+ <% end %>
101
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
102
+ New Window
103
+ <% end %>
104
+ <%= render M9sh::DropdownMenuSeparatorComponent.new %>
105
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
106
+ History
107
+ <% end %>
108
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
109
+ Downloads
110
+ <% end %>
111
+ <%= render M9sh::DropdownMenuSeparatorComponent.new %>
112
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
113
+ Settings
114
+ <% end %>
115
+ <% end %>
116
+ <% end %>
117
+ </div>
118
+ <% end %>
119
+
120
+ <%= render Docs::ComponentPreviewComponent.new(
121
+ title: "With Separators",
122
+ preview_content: separators_html,
123
+ code: dropdown_menu_with_separators_code,
124
+ ai_command: dropdown_menu_with_separators_code
125
+ ) %>
126
+
127
+ <!-- Alignment Example -->
128
+ <% alignment_html = capture do %>
129
+ <div class="flex justify-center items-center gap-4 p-8">
130
+ <%= render M9sh::DropdownMenuComponent.new(align: "start") do %>
131
+ <%= render M9sh::DropdownMenuTriggerComponent.new(class: "px-4 py-2 rounded-md border border-border bg-background hover:bg-muted transition-colors text-sm") do %>
132
+ Align Start
133
+ <% end %>
134
+ <%= render M9sh::DropdownMenuContentComponent.new do %>
135
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
136
+ Item 1
137
+ <% end %>
138
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
139
+ Item 2
140
+ <% end %>
141
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
142
+ Item 3
143
+ <% end %>
144
+ <% end %>
145
+ <% end %>
146
+
147
+ <%= render M9sh::DropdownMenuComponent.new(align: "end") do %>
148
+ <%= render M9sh::DropdownMenuTriggerComponent.new(class: "px-4 py-2 rounded-md border border-border bg-background hover:bg-muted transition-colors text-sm") do %>
149
+ Align End
150
+ <% end %>
151
+ <%= render M9sh::DropdownMenuContentComponent.new do %>
152
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
153
+ Item 1
154
+ <% end %>
155
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
156
+ Item 2
157
+ <% end %>
158
+ <%= render M9sh::DropdownMenuItemComponent.new do %>
159
+ Item 3
160
+ <% end %>
161
+ <% end %>
162
+ <% end %>
163
+ </div>
164
+ <% end %>
165
+
166
+ <%= render Docs::ComponentPreviewComponent.new(
167
+ title: "Alignment",
168
+ preview_content: alignment_html,
169
+ code: dropdown_menu_alignment_code,
170
+ ai_command: dropdown_menu_alignment_code
171
+ ) %>
172
+ <% end %>
173
+
174
+ <% page.with_api do %>
175
+ <h3 class="text-lg font-semibold">DropdownMenuComponent</h3>
176
+
177
+ <%= render Docs::PropTableComponent.new(
178
+ props: [
179
+ {
180
+ name: "align",
181
+ type: "String",
182
+ default: '"end"',
183
+ description: "The alignment of the menu relative to the trigger. Options: 'start', 'end'"
184
+ },
185
+ {
186
+ name: "side",
187
+ type: "String",
188
+ default: '"bottom"',
189
+ description: "The preferred side of the trigger to render against. Options: 'top', 'bottom'"
190
+ }
191
+ ]
192
+ ) %>
193
+
194
+ <h3 class="text-lg font-semibold mt-6">DropdownMenuTriggerComponent</h3>
195
+
196
+ <p class="text-sm text-muted-foreground">
197
+ The button that toggles the dropdown menu. Accepts all standard button attributes and can be styled with additional classes.
198
+ </p>
199
+
200
+ <h3 class="text-lg font-semibold mt-6">DropdownMenuContentComponent</h3>
201
+
202
+ <p class="text-sm text-muted-foreground">
203
+ The container for the dropdown menu items. Appears as a popover with shadow and border styling. Automatically positioned based on the parent component's align and side settings.
204
+ </p>
205
+
206
+ <h3 class="text-lg font-semibold mt-6">DropdownMenuItemComponent</h3>
207
+
208
+ <%= render Docs::PropTableComponent.new(
209
+ props: [
210
+ {
211
+ name: "href",
212
+ type: "String",
213
+ default: "nil",
214
+ description: "If provided, renders the item as a link instead of a button"
215
+ },
216
+ {
217
+ name: "method",
218
+ type: "String",
219
+ default: "nil",
220
+ description: "HTTP method for the link (e.g., :delete, :post)"
221
+ }
222
+ ]
223
+ ) %>
224
+
225
+ <h3 class="text-lg font-semibold mt-6">DropdownMenuSeparatorComponent</h3>
226
+
227
+ <p class="text-sm text-muted-foreground">
228
+ A visual separator to group related menu items. Renders as a thin horizontal line.
229
+ </p>
230
+
231
+ <div class="mt-6 space-y-2">
232
+ <h3 class="text-lg font-semibold">Accessibility</h3>
233
+
234
+ <div class="space-y-3 text-sm">
235
+ <p>
236
+ The Dropdown Menu component is built with keyboard navigation and screen reader support:
237
+ </p>
238
+
239
+ <ul class="list-disc list-inside space-y-2 text-muted-foreground">
240
+ <li>
241
+ Toggle the menu by clicking the trigger button
242
+ </li>
243
+ <li>
244
+ The menu automatically closes when clicking outside or selecting an item
245
+ </li>
246
+ <li>
247
+ Menu items can be either buttons or links, supporting different interaction patterns
248
+ </li>
249
+ <li>
250
+ Items are styled with hover and focus states for clear visual feedback
251
+ </li>
252
+ <li>
253
+ Smooth transitions provide polished enter and exit animations
254
+ </li>
255
+ <li>
256
+ The menu is positioned absolutely and uses z-index to appear above other content
257
+ </li>
258
+ </ul>
259
+ </div>
260
+ </div>
261
+
262
+ <div class="mt-6 space-y-2">
263
+ <h3 class="text-lg font-semibold">Notes</h3>
264
+ <ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
265
+ <li>
266
+ Use <code class="text-xs bg-muted px-1 py-0.5 rounded">DropdownMenuItemComponent</code> with the <code class="text-xs bg-muted px-1 py-0.5 rounded">href</code> parameter for navigation
267
+ </li>
268
+ <li>
269
+ Use <code class="text-xs bg-muted px-1 py-0.5 rounded">DropdownMenuItemComponent</code> without <code class="text-xs bg-muted px-1 py-0.5 rounded">href</code> for actions that trigger JavaScript or form submissions
270
+ </li>
271
+ <li>
272
+ Separators help organize menu items into logical groups
273
+ </li>
274
+ <li>
275
+ Icons can be added to menu items by including SVG elements in the item content
276
+ </li>
277
+ <li>
278
+ The menu automatically closes when an item is clicked
279
+ </li>
280
+ <li>
281
+ Use the <code class="text-xs bg-muted px-1 py-0.5 rounded">align</code> parameter to control whether the menu aligns to the start or end of the trigger
282
+ </li>
283
+ <li>
284
+ Multiple dropdown menus can exist on the same page independently
285
+ </li>
286
+ </ul>
287
+ </div>
288
+ <% end %>
289
+ <% end %>
@@ -0,0 +1,220 @@
1
+ <%= render Docs::ComponentPageComponent.new(title: "Hover Card") do |page| %>
2
+ <% page.with_header(
3
+ name: "Hover Card",
4
+ description: "For sighted users to preview content available behind a link in a richer format."
5
+ ) %>
6
+
7
+ <% page.with_installation(component_name: "hover_card") %>
8
+
9
+ <% page.with_usage do %>
10
+ <%= render Docs::CodeBlockComponent.new(
11
+ code: hover_card_usage_code,
12
+ language: "erb"
13
+ ) %>
14
+ <% end %>
15
+
16
+ <% page.with_examples do %>
17
+ <!-- Default Example -->
18
+ <% default_html = capture do %>
19
+ <div class="flex justify-center items-center p-8">
20
+ <%= render M9sh::HoverCardComponent.new do |card| %>
21
+ <% card.with_trigger do %>
22
+ <span class="underline cursor-pointer text-sm font-medium">@username</span>
23
+ <% end %>
24
+ <% card.with_hover_content do %>
25
+ <div class="space-y-2">
26
+ <h4 class="text-sm font-semibold">@username</h4>
27
+ <p class="text-sm text-muted-foreground">
28
+ The hover card component is a rich preview that appears when hovering over a trigger element.
29
+ </p>
30
+ </div>
31
+ <% end %>
32
+ <% end %>
33
+ </div>
34
+ <% end %>
35
+
36
+ <%= render Docs::ComponentPreviewComponent.new(
37
+ title: "Default",
38
+ preview_content: default_html,
39
+ code: hover_card_default_code,
40
+ ai_command: hover_card_default_code
41
+ ) %>
42
+
43
+ <!-- With Avatar Example -->
44
+ <% avatar_html = capture do %>
45
+ <div class="flex justify-center items-center p-8">
46
+ <%= render M9sh::HoverCardComponent.new do |card| %>
47
+ <% card.with_trigger do %>
48
+ <span class="underline cursor-pointer text-sm font-medium">@nextjs</span>
49
+ <% end %>
50
+ <% card.with_hover_content do %>
51
+ <div class="flex gap-4">
52
+ <%= render M9sh::AvatarComponent.new(
53
+ src: "https://github.com/vercel.png",
54
+ fallback: "VC",
55
+ size: :md
56
+ ) %>
57
+ <div class="space-y-1">
58
+ <h4 class="text-sm font-semibold">@nextjs</h4>
59
+ <p class="text-sm text-muted-foreground">
60
+ The React Framework - created and maintained by @vercel.
61
+ </p>
62
+ <div class="flex items-center pt-2">
63
+ <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-1">
64
+ <rect x="2" y="7" width="20" height="14" rx="2" ry="2"/>
65
+ <path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/>
66
+ </svg>
67
+ <span class="text-xs text-muted-foreground">Joined December 2021</span>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ <% end %>
72
+ <% end %>
73
+ </div>
74
+ <% end %>
75
+
76
+ <%= render Docs::ComponentPreviewComponent.new(
77
+ title: "With Avatar and Content",
78
+ preview_content: avatar_html,
79
+ code: hover_card_with_avatar_code,
80
+ ai_command: hover_card_with_avatar_code
81
+ ) %>
82
+
83
+ <!-- Complex Content Example -->
84
+ <% complex_html = capture do %>
85
+ <div class="flex justify-center items-center p-8">
86
+ <%= render M9sh::HoverCardComponent.new(open_delay: 300, close_delay: 400) do |card| %>
87
+ <% card.with_trigger do %>
88
+ <span class="underline cursor-pointer text-sm font-medium">View Details</span>
89
+ <% end %>
90
+ <% card.with_hover_content do %>
91
+ <div class="space-y-3">
92
+ <div>
93
+ <h4 class="text-sm font-semibold mb-1">Product Information</h4>
94
+ <p class="text-xs text-muted-foreground">
95
+ Hover over elements to see detailed information without navigating away from the current page.
96
+ </p>
97
+ </div>
98
+ <div class="border-t pt-3 space-y-2">
99
+ <div class="flex justify-between text-xs">
100
+ <span class="text-muted-foreground">Price:</span>
101
+ <span class="font-medium">$29.99</span>
102
+ </div>
103
+ <div class="flex justify-between text-xs">
104
+ <span class="text-muted-foreground">In Stock:</span>
105
+ <span class="font-medium text-green-600">Yes</span>
106
+ </div>
107
+ <div class="flex justify-between text-xs">
108
+ <span class="text-muted-foreground">Rating:</span>
109
+ <span class="font-medium">4.5/5</span>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ <% end %>
114
+ <% end %>
115
+ </div>
116
+ <% end %>
117
+
118
+ <%= render Docs::ComponentPreviewComponent.new(
119
+ title: "Complex Content",
120
+ preview_content: complex_html,
121
+ code: hover_card_complex_code,
122
+ ai_command: hover_card_complex_code
123
+ ) %>
124
+ <% end %>
125
+
126
+ <% page.with_api do %>
127
+ <h3 class="text-lg font-semibold">HoverCardComponent</h3>
128
+
129
+ <%= render Docs::PropTableComponent.new(
130
+ props: [
131
+ {
132
+ name: "open_delay",
133
+ type: "Number",
134
+ default: "200",
135
+ description: "The duration in milliseconds from when the mouse enters the trigger until the hover card opens"
136
+ },
137
+ {
138
+ name: "close_delay",
139
+ type: "Number",
140
+ default: "300",
141
+ description: "The duration in milliseconds from when the mouse leaves the trigger or content until the hover card closes"
142
+ }
143
+ ]
144
+ ) %>
145
+
146
+ <h3 class="text-lg font-semibold mt-6">Slots</h3>
147
+
148
+ <%= render Docs::PropTableComponent.new(
149
+ props: [
150
+ {
151
+ name: "trigger",
152
+ type: "Slot",
153
+ default: "-",
154
+ description: "The element that triggers the hover card. Typically a link or interactive element."
155
+ },
156
+ {
157
+ name: "hover_content",
158
+ type: "Slot",
159
+ default: "-",
160
+ description: "The rich content to display in the hover card"
161
+ }
162
+ ]
163
+ ) %>
164
+
165
+ <div class="mt-6 space-y-4">
166
+ <h3 class="text-lg font-semibold">Accessibility</h3>
167
+
168
+ <div class="space-y-3 text-sm">
169
+ <p>
170
+ The Hover Card component is designed for sighted users and provides enhanced preview functionality:
171
+ </p>
172
+
173
+ <ul class="list-disc list-inside space-y-2 text-muted-foreground">
174
+ <li>
175
+ The hover card appears after a configurable delay when hovering over the trigger element
176
+ </li>
177
+ <li>
178
+ Users can move their mouse into the hover card content, which prevents it from closing
179
+ </li>
180
+ <li>
181
+ The card automatically closes when the mouse leaves both the trigger and content areas
182
+ </li>
183
+ <li>
184
+ Smooth animations with fade and zoom effects provide polished transitions
185
+ </li>
186
+ <li>
187
+ The content is positioned in a fixed-width container (w-64) by default with a popover style
188
+ </li>
189
+ </ul>
190
+ </div>
191
+ </div>
192
+
193
+ <div class="mt-6 space-y-4">
194
+ <h3 class="text-lg font-semibold">Notes</h3>
195
+
196
+ <div class="space-y-3 text-sm">
197
+ <ul class="list-disc list-inside space-y-2 text-muted-foreground">
198
+ <li>
199
+ Hover cards are ideal for user profiles, product previews, and supplementary information
200
+ </li>
201
+ <li>
202
+ Unlike tooltips, hover cards can contain interactive elements and richer content
203
+ </li>
204
+ <li>
205
+ The <code class="text-xs bg-muted px-1 py-0.5 rounded">open_delay</code> prevents the card from appearing too quickly during casual mouse movements
206
+ </li>
207
+ <li>
208
+ The <code class="text-xs bg-muted px-1 py-0.5 rounded">close_delay</code> gives users time to move their mouse into the card content
209
+ </li>
210
+ <li>
211
+ Hover cards work well with Avatar, Badge, and other components to create rich previews
212
+ </li>
213
+ <li>
214
+ Content should be concise but can include multiple elements like images, text, and metadata
215
+ </li>
216
+ </ul>
217
+ </div>
218
+ </div>
219
+ <% end %>
220
+ <% end %>