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,597 @@
1
+ <%= render Docs::ComponentPageComponent.new(title: "Sidebar") do |page| %>
2
+ <% page.with_header(
3
+ name: "Sidebar",
4
+ description: "A composable, themeable sidebar component with collapsible navigation and responsive behavior."
5
+ ) %>
6
+
7
+ <% page.with_installation(component_name: "sidebar") %>
8
+
9
+ <% page.with_usage do %>
10
+ <%= render Docs::CodeBlockComponent.new(
11
+ code: sidebar_usage_code,
12
+ language: "erb"
13
+ ) %>
14
+ <% end %>
15
+
16
+ <% page.with_examples do %>
17
+ <!-- Basic Sidebar -->
18
+ <div class="my-6 space-y-4">
19
+ <h3 class="text-lg font-semibold">Basic Sidebar</h3>
20
+ <div class="rounded-lg border border-border bg-card overflow-hidden">
21
+ <div class="h-[500px] w-full">
22
+ <%= render M9sh::SidebarProviderComponent.new do %>
23
+ <%= render M9sh::SidebarComponent.new(collapsible: :none) do |sidebar| %>
24
+ <% sidebar.with_header do %>
25
+ <div class="flex items-center gap-2">
26
+ <div class="h-8 w-8 rounded-md bg-primary text-primary-foreground flex items-center justify-center font-bold">
27
+ M
28
+ </div>
29
+ <span class="font-semibold">My App</span>
30
+ </div>
31
+ <% end %>
32
+ <% sidebar.with_sidebar_content do %>
33
+ <%= render M9sh::SidebarGroupComponent.new do |group| %>
34
+ <% group.with_label do %>
35
+ Application
36
+ <% end %>
37
+ <% group.with_group_content do %>
38
+ <%= render M9sh::SidebarMenuComponent.new do %>
39
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
40
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#", active: true) do %>
41
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg>
42
+ Dashboard
43
+ <% end %>
44
+ <% end %>
45
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
46
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
47
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9h18v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9Z"/><path d="m3 9 2.45-4.9A2 2 0 0 1 7.24 3h9.52a2 2 0 0 1 1.8 1.1L21 9"/><path d="M12 3v6"/></svg>
48
+ Projects
49
+ <% end %>
50
+ <% end %>
51
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
52
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
53
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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"/><circle cx="12" cy="12" r="3"/></svg>
54
+ Settings
55
+ <% end %>
56
+ <% end %>
57
+ <% end %>
58
+ <% end %>
59
+ <% end %>
60
+ <% end %>
61
+ <% sidebar.with_footer do %>
62
+ <div class="flex items-center gap-2">
63
+ <div class="h-8 w-8 rounded-full bg-muted flex items-center justify-center text-sm font-medium">
64
+ JD
65
+ </div>
66
+ <div class="flex-1 group-data-[state=collapsed]/sidebar:hidden">
67
+ <p class="text-sm font-medium">John Doe</p>
68
+ <p class="text-xs text-muted-foreground">john@example.com</p>
69
+ </div>
70
+ </div>
71
+ <% end %>
72
+ <% end %>
73
+ <%= render M9sh::SidebarInsetComponent.new do %>
74
+ <header class="flex h-16 items-center gap-2 border-b px-4">
75
+ <%= render M9sh::SidebarTriggerComponent.new %>
76
+ <h1 class="text-lg font-semibold">Page Title</h1>
77
+ </header>
78
+ <div class="flex-1 p-4">
79
+ <p>Main content area</p>
80
+ </div>
81
+ <% end %>
82
+ <% end %>
83
+ </div>
84
+ </div>
85
+ <details class="rounded-lg border border-border" open="false">
86
+ <summary class="cursor-pointer px-4 py-2 space-x-1 text-sm font-medium hover:bg-accent">View Code</summary>
87
+ <div class="border-t border-border p-4">
88
+ <%= render Docs::CodeBlockComponent.new(
89
+ code: sidebar_usage_code,
90
+ language: "erb"
91
+ ) %>
92
+ </div>
93
+ </details>
94
+ </div>
95
+
96
+ <!-- Collapsible Icon Mode -->
97
+ <div class="my-6 space-y-4">
98
+ <h3 class="text-lg font-semibold">Collapsible Icon Mode</h3>
99
+ <div class="rounded-lg border border-border bg-card overflow-hidden">
100
+ <div class="h-[500px] w-full">
101
+ <%= render M9sh::SidebarProviderComponent.new(default_open: false) do %>
102
+ <%= render M9sh::SidebarComponent.new(collapsible: :icon) do |sidebar| %>
103
+ <% sidebar.with_header do %>
104
+ <div class="flex items-center gap-2">
105
+ <div class="h-8 w-8 rounded-md bg-primary text-primary-foreground flex items-center justify-center font-bold">
106
+ M
107
+ </div>
108
+ <span class="font-semibold group-data-[state=collapsed]/sidebar:hidden">My App</span>
109
+ </div>
110
+ <% end %>
111
+ <% sidebar.with_sidebar_content do %>
112
+ <%= render M9sh::SidebarGroupComponent.new do |group| %>
113
+ <% group.with_group_content do %>
114
+ <%= render M9sh::SidebarMenuComponent.new do %>
115
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
116
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#", active: true) do %>
117
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg>
118
+ Dashboard
119
+ <% end %>
120
+ <% end %>
121
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
122
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
123
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9h18v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9Z"/><path d="m3 9 2.45-4.9A2 2 0 0 1 7.24 3h9.52a2 2 0 0 1 1.8 1.1L21 9"/><path d="M12 3v6"/></svg>
124
+ Projects
125
+ <% end %>
126
+ <% end %>
127
+ <% end %>
128
+ <% end %>
129
+ <% end %>
130
+ <% end %>
131
+ <% end %>
132
+ <%= render M9sh::SidebarInsetComponent.new do %>
133
+ <header class="flex h-16 items-center gap-2 border-b px-4">
134
+ <%= render M9sh::SidebarTriggerComponent.new %>
135
+ <h1 class="text-lg font-semibold">Collapsible (Icon Mode)</h1>
136
+ </header>
137
+ <div class="flex-1 p-4">
138
+ <p>Click the trigger to collapse the sidebar to icon-only mode.</p>
139
+ </div>
140
+ <% end %>
141
+ <% end %>
142
+ </div>
143
+ </div>
144
+ <details class="rounded-lg border border-border" open="false">
145
+ <summary class="cursor-pointer px-4 py-2 space-x-1 text-sm font-medium hover:bg-accent">View Code</summary>
146
+ <div class="border-t border-border p-4">
147
+ <%= render Docs::CodeBlockComponent.new(
148
+ code: sidebar_collapsible_code,
149
+ language: "erb"
150
+ ) %>
151
+ </div>
152
+ </details>
153
+ </div>
154
+
155
+ <!-- With Groups and Labels -->
156
+ <div class="my-6 space-y-4">
157
+ <h3 class="text-lg font-semibold">With Groups and Labels</h3>
158
+ <div class="rounded-lg border border-border bg-card overflow-hidden">
159
+ <div class="h-[500px] w-full">
160
+ <%= render M9sh::SidebarProviderComponent.new do %>
161
+ <%= render M9sh::SidebarComponent.new(collapsible: :none) do |sidebar| %>
162
+ <% sidebar.with_header do %>
163
+ <div class="flex items-center gap-2">
164
+ <div class="h-8 w-8 rounded-md bg-primary text-primary-foreground flex items-center justify-center font-bold">
165
+ M
166
+ </div>
167
+ <span class="font-semibold">My App</span>
168
+ </div>
169
+ <% end %>
170
+ <% sidebar.with_sidebar_content do %>
171
+ <%= render M9sh::SidebarGroupComponent.new do |group| %>
172
+ <% group.with_label do %>
173
+ Platform
174
+ <% end %>
175
+ <% group.with_group_content do %>
176
+ <%= render M9sh::SidebarMenuComponent.new do %>
177
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
178
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
179
+ Dashboard
180
+ <% end %>
181
+ <% end %>
182
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
183
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
184
+ Analytics
185
+ <% end %>
186
+ <% end %>
187
+ <% end %>
188
+ <% end %>
189
+ <% end %>
190
+
191
+ <%= render M9sh::SidebarGroupComponent.new do |group| %>
192
+ <% group.with_label do %>
193
+ Settings
194
+ <% end %>
195
+ <% group.with_group_content do %>
196
+ <%= render M9sh::SidebarMenuComponent.new do %>
197
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
198
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
199
+ Profile
200
+ <% end %>
201
+ <% end %>
202
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
203
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
204
+ Preferences
205
+ <% end %>
206
+ <% end %>
207
+ <% end %>
208
+ <% end %>
209
+ <% end %>
210
+ <% end %>
211
+ <% end %>
212
+ <%= render M9sh::SidebarInsetComponent.new do %>
213
+ <header class="flex h-16 items-center gap-2 border-b px-4">
214
+ <%= render M9sh::SidebarTriggerComponent.new %>
215
+ <h1 class="text-lg font-semibold">Grouped Navigation</h1>
216
+ </header>
217
+ <div class="flex-1 p-4">
218
+ <p>Sidebar with multiple groups and labels.</p>
219
+ </div>
220
+ <% end %>
221
+ <% end %>
222
+ </div>
223
+ </div>
224
+ <details class="rounded-lg border border-border" open="false">
225
+ <summary class="cursor-pointer px-4 py-2 space-x-1 text-sm font-medium hover:bg-accent">View Code</summary>
226
+ <div class="border-t border-border p-4">
227
+ <%= render Docs::CodeBlockComponent.new(
228
+ code: sidebar_with_groups_code,
229
+ language: "erb"
230
+ ) %>
231
+ </div>
232
+ </details>
233
+ </div>
234
+
235
+ <!-- App Sidebar with M9sh Components -->
236
+ <div class="my-6 space-y-4">
237
+ <h3 class="text-lg font-semibold">App Sidebar with Cards</h3>
238
+ <p class="text-sm text-muted-foreground">A complete example using M9sh components for sidebar navigation and content cards.</p>
239
+ <div class="rounded-lg border border-border bg-card overflow-hidden">
240
+ <div class="h-[700px] w-full">
241
+ <%= render M9sh::SidebarProviderComponent.new(background: :sidebar) do %>
242
+ <%= render M9sh::SidebarComponent.new(collapsible: :icon, show_border: false) do |sidebar| %>
243
+ <% sidebar.with_header do %>
244
+ <div class="flex items-center gap-3 px-3 py-3">
245
+ <%= render M9sh::BadgeComponent.new(variant: :default, class_name: "h-8 w-8 text-base justify-center") do %>
246
+ A
247
+ <% end %>
248
+ <span class="text-base font-semibold group-data-[state=collapsed]/sidebar:hidden">Acme Inc.</span>
249
+ </div>
250
+ <% end %>
251
+
252
+ <% sidebar.with_sidebar_content do %>
253
+ <%= render M9sh::SidebarGroupComponent.new do |group| %>
254
+ <% group.with_group_content do %>
255
+ <%= render M9sh::SidebarMenuComponent.new do %>
256
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
257
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#", active: true) do %>
258
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg>
259
+ Dashboard
260
+ <% end %>
261
+ <% end %>
262
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
263
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
264
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="m19 9-5 5-4-4-3 3"/></svg>
265
+ Analytics
266
+ <% end %>
267
+ <% end %>
268
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
269
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
270
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9h18v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9Z"/><path d="m3 9 2.45-4.9A2 2 0 0 1 7.24 3h9.52a2 2 0 0 1 1.8 1.1L21 9"/><path d="M12 3v6"/></svg>
271
+ Projects
272
+ <% end %>
273
+ <% end %>
274
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
275
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
276
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
277
+ Team
278
+ <% end %>
279
+ <% end %>
280
+ <% end %>
281
+ <% end %>
282
+ <% end %>
283
+
284
+ <%= render M9sh::SidebarGroupComponent.new do |group| %>
285
+ <% group.with_label do %>
286
+ Documents
287
+ <% end %>
288
+ <% group.with_group_content do %>
289
+ <%= render M9sh::SidebarMenuComponent.new do %>
290
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
291
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
292
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20"/></svg>
293
+ Data Library
294
+ <% end %>
295
+ <% end %>
296
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
297
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
298
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg>
299
+ Reports
300
+ <% end %>
301
+ <% end %>
302
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
303
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
304
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/><line x1="16" x2="8" y1="13" y2="13"/><line x1="16" x2="8" y1="17" y2="17"/><line x1="10" x2="8" y1="9" y2="9"/></svg>
305
+ Documentation
306
+ <% end %>
307
+ <% end %>
308
+ <% end %>
309
+ <% end %>
310
+ <% end %>
311
+ <% end %>
312
+
313
+ <% sidebar.with_footer do %>
314
+ <%= render M9sh::SidebarGroupComponent.new do |group| %>
315
+ <% group.with_group_content do %>
316
+ <%= render M9sh::SidebarMenuComponent.new do %>
317
+ <%= render M9sh::SidebarMenuItemComponent.new do %>
318
+ <%= render M9sh::SidebarMenuButtonComponent.new(href: "#") do %>
319
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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"/><circle cx="12" cy="12" r="3"/></svg>
320
+ Settings
321
+ <% end %>
322
+ <% end %>
323
+ <% end %>
324
+ <% end %>
325
+ <% end %>
326
+
327
+ <div class="px-2 py-2 mt-2">
328
+ <div class="flex items-center gap-3 px-2 py-2 rounded-lg hover:bg-sidebar-accent cursor-pointer transition-colors">
329
+ <%= render M9sh::AvatarComponent.new(src: "https://github.com/shadcn.png", alt: "User", class_name: "h-8 w-8") %>
330
+ <div class="flex-1 min-w-0 group-data-[state=collapsed]/sidebar:hidden">
331
+ <p class="text-sm font-medium truncate">John Doe</p>
332
+ <p class="text-xs text-muted-foreground truncate">john@example.com</p>
333
+ </div>
334
+ </div>
335
+ </div>
336
+ <% end %>
337
+ <% end %>
338
+
339
+ <%= render M9sh::SidebarInsetComponent.new do %>
340
+ <%= render M9sh::MainComponent.new do %>
341
+ <header class="flex h-14 shrink-0 items-center gap-2 px-6 border-b">
342
+ <%= render M9sh::SidebarTriggerComponent.new %>
343
+ <%= render M9sh::SeparatorComponent.new(orientation: :vertical, class_name: "h-6 mx-2") %>
344
+ <h1 class="text-base font-semibold">Dashboard</h1>
345
+ <div class="ml-auto flex items-center gap-2">
346
+ <%= render M9sh::BadgeComponent.new(variant: :success) do %>
347
+ Live
348
+ <% end %>
349
+ </div>
350
+ </header>
351
+
352
+ <div class="flex-1 overflow-auto p-6">
353
+ <div class="mx-auto max-w-6xl space-y-6">
354
+ <!-- Revenue Card -->
355
+ <%= render M9sh::CardComponent.new do |card| %>
356
+ <% card.with_header do |header| %>
357
+ <% header.with_title do %>
358
+ Total Revenue
359
+ <% end %>
360
+ <% header.with_description do %>
361
+ Trending up this month
362
+ <% end %>
363
+ <% header.with_action do %>
364
+ <%= render M9sh::BadgeComponent.new(variant: :outline) do %>
365
+ +12.5%
366
+ <% end %>
367
+ <% end %>
368
+ <% end %>
369
+ <% card.with_body do %>
370
+ <p class="text-4xl font-bold">$45,231.89</p>
371
+ <p class="text-xs text-muted-foreground mt-2">+20.1% from last month</p>
372
+ <% end %>
373
+ <% end %>
374
+
375
+ <!-- Stats Grid -->
376
+ <div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
377
+ <%= render M9sh::CardComponent.new do |card| %>
378
+ <% card.with_header do |header| %>
379
+ <% header.with_title class: "text-sm" do %>
380
+ Active Users
381
+ <% end %>
382
+ <% end %>
383
+ <% card.with_body do %>
384
+ <div class="text-2xl font-bold">2,350</div>
385
+ <p class="text-xs text-muted-foreground mt-1">
386
+ <%= render M9sh::BadgeComponent.new(variant: :success, class_name: "text-[10px] px-1.5") do %>
387
+ +12%
388
+ <% end %>
389
+ vs last week
390
+ </p>
391
+ <% end %>
392
+ <% end %>
393
+
394
+ <%= render M9sh::CardComponent.new do |card| %>
395
+ <% card.with_header do |header| %>
396
+ <% header.with_title class: "text-sm" do %>
397
+ Projects
398
+ <% end %>
399
+ <% end %>
400
+ <% card.with_body do %>
401
+ <div class="text-2xl font-bold">18</div>
402
+ <p class="text-xs text-muted-foreground mt-1">
403
+ <%= render M9sh::BadgeComponent.new(variant: :warning, class_name: "text-[10px] px-1.5") do %>
404
+ 3 active
405
+ <% end %>
406
+ </p>
407
+ <% end %>
408
+ <% end %>
409
+
410
+ <%= render M9sh::CardComponent.new do |card| %>
411
+ <% card.with_header do |header| %>
412
+ <% header.with_title class: "text-sm" do %>
413
+ Team Members
414
+ <% end %>
415
+ <% end %>
416
+ <% card.with_body do %>
417
+ <div class="text-2xl font-bold">8</div>
418
+ <p class="text-xs text-muted-foreground mt-1">4 online now</p>
419
+ <% end %>
420
+ <% end %>
421
+ </div>
422
+
423
+ <!-- Recent Activity Card -->
424
+ <%= render M9sh::CardComponent.new do |card| %>
425
+ <% card.with_header do |header| %>
426
+ <% header.with_title do %>
427
+ Recent Activity
428
+ <% end %>
429
+ <% header.with_description do %>
430
+ Your team's latest updates
431
+ <% end %>
432
+ <% end %>
433
+ <% card.with_body do %>
434
+ <div class="space-y-4">
435
+ <div class="flex items-start gap-4">
436
+ <%= render M9sh::AvatarComponent.new(alt: "JD", fallback: "JD", class_name: "h-9 w-9") %>
437
+ <div class="flex-1 space-y-1">
438
+ <p class="text-sm font-medium">John deployed to production</p>
439
+ <p class="text-xs text-muted-foreground">2 minutes ago</p>
440
+ </div>
441
+ <%= render M9sh::BadgeComponent.new(variant: :success) do %>
442
+ Success
443
+ <% end %>
444
+ </div>
445
+
446
+ <div class="flex items-start gap-4">
447
+ <%= render M9sh::AvatarComponent.new(alt: "SK", fallback: "SK", class_name: "h-9 w-9") %>
448
+ <div class="flex-1 space-y-1">
449
+ <p class="text-sm font-medium">Sarah created new project "Dashboard v2"</p>
450
+ <p class="text-xs text-muted-foreground">1 hour ago</p>
451
+ </div>
452
+ <%= render M9sh::BadgeComponent.new(variant: :default) do %>
453
+ New
454
+ <% end %>
455
+ </div>
456
+
457
+ <div class="flex items-start gap-4">
458
+ <%= render M9sh::AvatarComponent.new(alt: "MC", fallback: "MC", class_name: "h-9 w-9") %>
459
+ <div class="flex-1 space-y-1">
460
+ <p class="text-sm font-medium">Mike updated documentation</p>
461
+ <p class="text-xs text-muted-foreground">3 hours ago</p>
462
+ </div>
463
+ <%= render M9sh::BadgeComponent.new(variant: :secondary) do %>
464
+ Docs
465
+ <% end %>
466
+ </div>
467
+ </div>
468
+ <% end %>
469
+ <% end %>
470
+
471
+ <!-- Quick Actions -->
472
+ <%= render M9sh::CardComponent.new do |card| %>
473
+ <% card.with_header do |header| %>
474
+ <% header.with_title do %>
475
+ Quick Actions
476
+ <% end %>
477
+ <% header.with_description do %>
478
+ Common tasks and shortcuts
479
+ <% end %>
480
+ <% end %>
481
+ <% card.with_body do %>
482
+ <div class="flex flex-wrap gap-2">
483
+ <%= render M9sh::ButtonComponent.new(variant: :default) do %>
484
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
485
+ New Project
486
+ <% end %>
487
+ <%= render M9sh::ButtonComponent.new(variant: :outline) do %>
488
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
489
+ Invite Team
490
+ <% end %>
491
+ <%= render M9sh::ButtonComponent.new(variant: :outline) do %>
492
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/></svg>
493
+ Export Data
494
+ <% end %>
495
+ </div>
496
+ <% end %>
497
+ <% end %>
498
+ </div>
499
+ </div>
500
+ <% end %>
501
+ <% end %>
502
+ <% end %>
503
+ </div>
504
+ </div>
505
+ <details class="rounded-lg border border-border" open="false">
506
+ <summary class="cursor-pointer px-4 py-2 space-x-1 text-sm font-medium hover:bg-accent">View Code</summary>
507
+ <div class="border-t border-border p-4">
508
+ <%= render Docs::CodeBlockComponent.new(
509
+ code: sidebar_app_code,
510
+ language: "erb"
511
+ ) %>
512
+ </div>
513
+ </details>
514
+ </div>
515
+ <% end %>
516
+
517
+ <% page.with_api do %>
518
+ <h3 class="text-lg font-semibold">SidebarProvider Component</h3>
519
+ <p class="text-sm text-muted-foreground mb-4">
520
+ Wraps the entire sidebar and main content area, managing the sidebar state.
521
+ </p>
522
+
523
+ <%= render Docs::PropTableComponent.new(
524
+ props: [
525
+ {
526
+ name: "default_open",
527
+ type: "Boolean",
528
+ default: "true",
529
+ description: "Initial open state of the sidebar"
530
+ }
531
+ ]
532
+ ) %>
533
+
534
+ <h3 class="text-lg font-semibold mt-6">Sidebar Component</h3>
535
+ <%= render Docs::PropTableComponent.new(
536
+ props: [
537
+ {
538
+ name: "side",
539
+ type: "Symbol",
540
+ default: ":left",
541
+ description: "Which side the sidebar appears on. Options: :left, :right"
542
+ },
543
+ {
544
+ name: "variant",
545
+ type: "Symbol",
546
+ default: ":sidebar",
547
+ description: "Visual variant of the sidebar. Options: :sidebar, :floating, :inset"
548
+ },
549
+ {
550
+ name: "collapsible",
551
+ type: "Symbol",
552
+ default: ":offcanvas",
553
+ description: "Collapse behavior. Options: :offcanvas (slides out), :icon (icon-only mode), :none (no collapse)"
554
+ }
555
+ ]
556
+ ) %>
557
+
558
+ <h3 class="text-lg font-semibold mt-6">SidebarMenuButton Component</h3>
559
+ <%= render Docs::PropTableComponent.new(
560
+ props: [
561
+ {
562
+ name: "href",
563
+ type: "String",
564
+ default: "nil",
565
+ description: "URL for the menu item. If provided, renders as an anchor tag"
566
+ },
567
+ {
568
+ name: "active",
569
+ type: "Boolean",
570
+ default: "false",
571
+ description: "Whether this menu item is currently active"
572
+ }
573
+ ]
574
+ ) %>
575
+
576
+ <div class="mt-6 space-y-2">
577
+ <h3 class="text-lg font-semibold">Features</h3>
578
+ <ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
579
+ <li>Composable structure with header, content, and footer sections</li>
580
+ <li>Multiple collapsible modes: offcanvas, icon-only, or always visible</li>
581
+ <li>Keyboard shortcut support (Cmd/Ctrl + B to toggle)</li>
582
+ <li>Persistent state across page reloads using localStorage</li>
583
+ <li>Responsive design with mobile detection</li>
584
+ <li>Support for grouped navigation with labels</li>
585
+ <li>Active state management for menu items</li>
586
+ <li>Icon support in menu buttons</li>
587
+ </ul>
588
+ </div>
589
+
590
+ <div class="mt-6 space-y-2">
591
+ <h3 class="text-lg font-semibold">Keyboard Shortcuts</h3>
592
+ <ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
593
+ <li><kbd class="px-2 py-1 text-xs font-semibold bg-muted rounded">⌘ B</kbd> or <kbd class="px-2 py-1 text-xs font-semibold bg-muted rounded">Ctrl B</kbd> - Toggle sidebar open/closed</li>
594
+ </ul>
595
+ </div>
596
+ <% end %>
597
+ <% end %>