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,252 @@
1
+ <%= render Docs::ComponentPageComponent.new(title: "Collapsible") do |page| %>
2
+ <% page.with_header(
3
+ name: "Collapsible",
4
+ description: "An interactive component that expands and collapses content."
5
+ ) %>
6
+
7
+ <% page.with_installation(component_name: "collapsible") %>
8
+
9
+ <% page.with_usage do %>
10
+ <%= render Docs::CodeBlockComponent.new(
11
+ code: collapsible_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="w-full max-w-md">
20
+ <%= render M9sh::CollapsibleComponent.new(class: "space-y-2") do |c| %>
21
+ <% c.with_trigger do %>
22
+ <button class="flex w-full items-center justify-between rounded-md bg-muted px-4 py-2 text-sm font-medium transition-colors hover:bg-muted">
23
+ <span>@peduarte starred 3 repositories</span>
24
+ <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" class="transition-transform">
25
+ <polyline points="6 9 12 15 18 9"/>
26
+ </svg>
27
+ </button>
28
+ <% end %>
29
+ <% c.with_collapsible_content do %>
30
+ <div class="space-y-2 pt-2">
31
+ <div class="rounded-md border px-4 py-3 text-sm">
32
+ @radix-ui/primitives
33
+ </div>
34
+ <div class="rounded-md border px-4 py-3 text-sm">
35
+ @radix-ui/colors
36
+ </div>
37
+ <div class="rounded-md border px-4 py-3 text-sm">
38
+ @stitches/react
39
+ </div>
40
+ </div>
41
+ <% end %>
42
+ <% end %>
43
+ </div>
44
+ <% end %>
45
+
46
+ <%= render Docs::ComponentPreviewComponent.new(
47
+ title: "Default",
48
+ preview_content: default_html,
49
+ code: collapsible_default_code,
50
+ ai_command: collapsible_default_code
51
+ ) %>
52
+
53
+ <!-- Open by Default Example -->
54
+ <% open_html = capture do %>
55
+ <div class="w-full max-w-md">
56
+ <%= render M9sh::CollapsibleComponent.new(open: true, class: "space-y-2") do |c| %>
57
+ <% c.with_trigger do %>
58
+ <button class="flex w-full items-center justify-between rounded-md bg-muted px-4 py-2 text-sm font-medium transition-colors hover:bg-muted">
59
+ <span>Project Information</span>
60
+ <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" class="transition-transform">
61
+ <polyline points="6 9 12 15 18 9"/>
62
+ </svg>
63
+ </button>
64
+ <% end %>
65
+ <% c.with_collapsible_content do %>
66
+ <div class="space-y-2 pt-2">
67
+ <div class="rounded-md border px-4 py-3 text-sm">
68
+ <strong>Name:</strong> m9sh Components
69
+ </div>
70
+ <div class="rounded-md border px-4 py-3 text-sm">
71
+ <strong>Status:</strong> Active Development
72
+ </div>
73
+ <div class="rounded-md border px-4 py-3 text-sm">
74
+ <strong>Version:</strong> 1.0.0
75
+ </div>
76
+ </div>
77
+ <% end %>
78
+ <% end %>
79
+ </div>
80
+ <% end %>
81
+
82
+ <%= render Docs::ComponentPreviewComponent.new(
83
+ title: "Open by Default",
84
+ preview_content: open_html,
85
+ code: collapsible_open_code,
86
+ ai_command: collapsible_open_code
87
+ ) %>
88
+
89
+ <!-- With Button Example -->
90
+ <% button_html = capture do %>
91
+ <div class="w-full max-w-md">
92
+ <%= render M9sh::CollapsibleComponent.new(class: "space-y-2") do |c| %>
93
+ <% c.with_trigger do %>
94
+ <%= render M9sh::ButtonComponent.new(variant: :outline, class: "w-full justify-between") do %>
95
+ <span>Show more details</span>
96
+ <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">
97
+ <polyline points="6 9 12 15 18 9"/>
98
+ </svg>
99
+ <% end %>
100
+ <% end %>
101
+ <% c.with_collapsible_content do %>
102
+ <div class="rounded-md border p-4 text-sm">
103
+ <p class="text-muted-foreground">
104
+ This is additional content that can be toggled. The collapsible component
105
+ works with any trigger element, including buttons, links, or custom elements.
106
+ </p>
107
+ </div>
108
+ <% end %>
109
+ <% end %>
110
+ </div>
111
+ <% end %>
112
+
113
+ <%= render Docs::ComponentPreviewComponent.new(
114
+ title: "With Button Component",
115
+ preview_content: button_html,
116
+ code: collapsible_button_code,
117
+ ai_command: collapsible_button_code
118
+ ) %>
119
+
120
+ <!-- FAQ Example -->
121
+ <% faq_html = capture do %>
122
+ <div class="w-full max-w-md space-y-4">
123
+ <%= render M9sh::CollapsibleComponent.new(class: "space-y-2") do |c| %>
124
+ <% c.with_trigger do %>
125
+ <button class="flex w-full items-center justify-between rounded-md border px-4 py-2 text-sm font-medium transition-colors hover:bg-muted">
126
+ <span>What is m9sh Components?</span>
127
+ <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">
128
+ <polyline points="6 9 12 15 18 9"/>
129
+ </svg>
130
+ </button>
131
+ <% end %>
132
+ <% c.with_collapsible_content do %>
133
+ <div class="border-x border-b rounded-b-md px-4 py-3 text-sm text-muted-foreground">
134
+ m9sh Components is a collection of reusable UI components for Rails applications,
135
+ built with ViewComponent and styled with Tailwind CSS.
136
+ </div>
137
+ <% end %>
138
+ <% end %>
139
+
140
+ <%= render M9sh::CollapsibleComponent.new(class: "space-y-2") do |c| %>
141
+ <% c.with_trigger do %>
142
+ <button class="flex w-full items-center justify-between rounded-md border px-4 py-2 text-sm font-medium transition-colors hover:bg-muted">
143
+ <span>How do I install it?</span>
144
+ <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">
145
+ <polyline points="6 9 12 15 18 9"/>
146
+ </svg>
147
+ </button>
148
+ <% end %>
149
+ <% c.with_collapsible_content do %>
150
+ <div class="border-x border-b rounded-b-md px-4 py-3 text-sm text-muted-foreground">
151
+ Simply copy the component files into your Rails application and start using them
152
+ in your views. Each component is self-contained and easy to customize.
153
+ </div>
154
+ <% end %>
155
+ <% end %>
156
+ </div>
157
+ <% end %>
158
+
159
+ <%= render Docs::ComponentPreviewComponent.new(
160
+ title: "FAQ Style",
161
+ preview_content: faq_html,
162
+ code: collapsible_faq_code,
163
+ ai_command: collapsible_faq_code
164
+ ) %>
165
+ <% end %>
166
+
167
+ <% page.with_api do %>
168
+ <h3 class="text-lg font-semibold">CollapsibleComponent</h3>
169
+
170
+ <%= render Docs::PropTableComponent.new(
171
+ props: [
172
+ {
173
+ name: "open",
174
+ type: "Boolean",
175
+ default: "false",
176
+ description: "Whether the collapsible is open by default"
177
+ }
178
+ ]
179
+ ) %>
180
+
181
+ <h3 class="text-lg font-semibold mt-6">Slots</h3>
182
+
183
+ <%= render Docs::PropTableComponent.new(
184
+ props: [
185
+ {
186
+ name: "trigger",
187
+ type: "Slot",
188
+ default: "-",
189
+ description: "The element that toggles the collapsible when clicked. Can be any clickable element like a button, link, or div."
190
+ },
191
+ {
192
+ name: "collapsible_content",
193
+ type: "Slot",
194
+ default: "-",
195
+ description: "The content that will be shown or hidden when the trigger is clicked"
196
+ }
197
+ ]
198
+ ) %>
199
+
200
+ <div class="mt-6 space-y-4">
201
+ <h3 class="text-lg font-semibold">Accessibility</h3>
202
+
203
+ <div class="space-y-3 text-sm">
204
+ <p>
205
+ The Collapsible component uses Stimulus to manage state and animations:
206
+ </p>
207
+
208
+ <ul class="list-disc list-inside space-y-2 text-muted-foreground">
209
+ <li>
210
+ Animated height transitions provide smooth visual feedback when expanding/collapsing
211
+ </li>
212
+ <li>
213
+ The trigger can be any interactive element - ensure it's keyboard accessible
214
+ </li>
215
+ <li>
216
+ Consider adding <code class="text-xs bg-muted px-1 py-0.5 rounded">aria-expanded</code> and <code class="text-xs bg-muted px-1 py-0.5 rounded">aria-controls</code> attributes to the trigger for better screen reader support
217
+ </li>
218
+ <li>
219
+ The component uses <code class="text-xs bg-muted px-1 py-0.5 rounded">data-state</code> attributes for styling based on open/closed state
220
+ </li>
221
+ <li>
222
+ Transitions are handled via CSS with a 300ms duration for smooth animations
223
+ </li>
224
+ </ul>
225
+ </div>
226
+ </div>
227
+
228
+ <div class="mt-6 space-y-4">
229
+ <h3 class="text-lg font-semibold">Notes</h3>
230
+
231
+ <div class="space-y-3 text-sm">
232
+ <ul class="list-disc list-inside space-y-2 text-muted-foreground">
233
+ <li>
234
+ The trigger slot is required and must contain a clickable element
235
+ </li>
236
+ <li>
237
+ The component automatically attaches a click handler to the trigger element
238
+ </li>
239
+ <li>
240
+ Use the <code class="text-xs bg-muted px-1 py-0.5 rounded">open</code> parameter to set the initial state
241
+ </li>
242
+ <li>
243
+ Multiple collapsible components can exist on the same page independently
244
+ </li>
245
+ <li>
246
+ The component works well with other m9sh components like Button, Card, etc.
247
+ </li>
248
+ </ul>
249
+ </div>
250
+ </div>
251
+ <% end %>
252
+ <% end %>
@@ -0,0 +1,323 @@
1
+ <%= render Docs::ComponentPageComponent.new(title: "Dialog") do |page| %>
2
+ <% page.with_header(
3
+ name: "Dialog",
4
+ description: "A modal dialog that interrupts the user with important content and expects a response."
5
+ ) %>
6
+
7
+ <% page.with_installation(component_name: "dialog") %>
8
+
9
+ <% page.with_usage do %>
10
+ <%= render Docs::CodeBlockComponent.new(
11
+ code: dialog_usage_code,
12
+ language: "erb"
13
+ ) %>
14
+ <% end %>
15
+
16
+ <% page.with_examples do %>
17
+ <!-- Default Example -->
18
+ <% default_dialog_html = capture do %>
19
+ <%= render M9sh::DialogComponent.new do |dialog| %>
20
+ <% dialog.with_dialog_trigger do %>
21
+ <%= render M9sh::ButtonComponent.new do %>
22
+ Open Dialog
23
+ <% end %>
24
+ <% end %>
25
+ <% dialog.with_dialog_content do |content| %>
26
+ <% content.with_dialog_header do |header| %>
27
+ <% header.with_dialog_title do %>
28
+ Are you absolutely sure?
29
+ <% end %>
30
+ <% header.with_dialog_description do %>
31
+ This action cannot be undone. This will permanently delete your account
32
+ and remove your data from our servers.
33
+ <% end %>
34
+ <% end %>
35
+ <% end %>
36
+ <% end %>
37
+ <% end %>
38
+
39
+ <%= render Docs::ComponentPreviewComponent.new(
40
+ title: "Default",
41
+ preview_content: default_dialog_html,
42
+ code: dialog_default_code,
43
+ ai_command: dialog_default_code
44
+ ) %>
45
+
46
+ <!-- With Header Example -->
47
+ <% with_header_dialog_html = capture do %>
48
+ <%= render M9sh::DialogComponent.new do |dialog| %>
49
+ <% dialog.with_dialog_trigger do %>
50
+ <%= render M9sh::ButtonComponent.new(variant: :outline) do %>
51
+ Edit Profile
52
+ <% end %>
53
+ <% end %>
54
+ <% dialog.with_dialog_content do |content| %>
55
+ <% content.with_dialog_header do |header| %>
56
+ <% header.with_dialog_title do %>
57
+ Edit profile
58
+ <% end %>
59
+ <% header.with_dialog_description do %>
60
+ Make changes to your profile here. Click save when you're done.
61
+ <% end %>
62
+ <% end %>
63
+ <div class="space-y-4">
64
+ <div class="space-y-2">
65
+ <%= render M9sh::LabelComponent.new(text: "Name") %>
66
+ <%= render M9sh::InputComponent.new(type: "text", placeholder: "Pedro Duarte") %>
67
+ </div>
68
+ <div class="space-y-2">
69
+ <%= render M9sh::LabelComponent.new(text: "Username") %>
70
+ <%= render M9sh::InputComponent.new(type: "text", placeholder: "@peduarte") %>
71
+ </div>
72
+ </div>
73
+ <% content.with_dialog_footer do %>
74
+ <%= render M9sh::ButtonComponent.new(type: "submit") do %>
75
+ Save changes
76
+ <% end %>
77
+ <% end %>
78
+ <% end %>
79
+ <% end %>
80
+ <% end %>
81
+
82
+ <%= render Docs::ComponentPreviewComponent.new(
83
+ title: "With Header",
84
+ preview_content: with_header_dialog_html,
85
+ code: dialog_with_header_code,
86
+ ai_command: dialog_with_header_code
87
+ ) %>
88
+
89
+ <!-- With Footer Example -->
90
+ <% with_footer_dialog_html = capture do %>
91
+ <%= render M9sh::DialogComponent.new do |dialog| %>
92
+ <% dialog.with_dialog_trigger do %>
93
+ <%= render M9sh::ButtonComponent.new do %>
94
+ Open Dialog
95
+ <% end %>
96
+ <% end %>
97
+ <% dialog.with_dialog_content do |content| %>
98
+ <% content.with_dialog_header do |header| %>
99
+ <% header.with_dialog_title do %>
100
+ Confirm Action
101
+ <% end %>
102
+ <% header.with_dialog_description do %>
103
+ This is a dialog with a footer containing action buttons.
104
+ <% end %>
105
+ <% end %>
106
+ <p class="text-sm">Are you sure you want to proceed with this action?</p>
107
+ <% content.with_dialog_footer do %>
108
+ <%= render M9sh::DialogCloseComponent.new(as_child: true) do %>
109
+ <%= render M9sh::ButtonComponent.new(variant: :outline) do %>
110
+ Cancel
111
+ <% end %>
112
+ <% end %>
113
+ <%= render M9sh::ButtonComponent.new do %>
114
+ Continue
115
+ <% end %>
116
+ <% end %>
117
+ <% end %>
118
+ <% end %>
119
+ <% end %>
120
+
121
+ <%= render Docs::ComponentPreviewComponent.new(
122
+ title: "With Footer",
123
+ preview_content: with_footer_dialog_html,
124
+ code: dialog_with_footer_code,
125
+ ai_command: dialog_with_footer_code
126
+ ) %>
127
+
128
+ <!-- Form Dialog Example -->
129
+ <% form_dialog_html = capture do %>
130
+ <%= render M9sh::DialogComponent.new do |dialog| %>
131
+ <% dialog.with_dialog_trigger do %>
132
+ <%= render M9sh::ButtonComponent.new(variant: :outline) do %>
133
+ Share
134
+ <% end %>
135
+ <% end %>
136
+ <% dialog.with_dialog_content do |content| %>
137
+ <% content.with_dialog_header do |header| %>
138
+ <% header.with_dialog_title do %>
139
+ Share link
140
+ <% end %>
141
+ <% header.with_dialog_description do %>
142
+ Anyone who has this link will be able to view this.
143
+ <% end %>
144
+ <% end %>
145
+ <div class="flex items-center space-x-2">
146
+ <%= render M9sh::InputComponent.new(
147
+ value: "https://ui.shadcn.com/docs/installation",
148
+ readonly: true,
149
+ class: "flex-1"
150
+ ) %>
151
+ <%= render M9sh::ButtonComponent.new(type: "button", variant: :secondary, size: :sm) do %>
152
+ Copy
153
+ <% end %>
154
+ </div>
155
+ <% content.with_dialog_footer do %>
156
+ <%= render M9sh::DialogCloseComponent.new %>
157
+ <% end %>
158
+ <% end %>
159
+ <% end %>
160
+ <% end %>
161
+
162
+ <%= render Docs::ComponentPreviewComponent.new(
163
+ title: "Form Dialog",
164
+ preview_content: form_dialog_html,
165
+ code: dialog_form_code,
166
+ ai_command: dialog_form_code
167
+ ) %>
168
+
169
+ <!-- Scrollable Content Example -->
170
+ <% scrollable_dialog_html = capture do %>
171
+ <%= render M9sh::DialogComponent.new do |dialog| %>
172
+ <% dialog.with_dialog_trigger do %>
173
+ <%= render M9sh::ButtonComponent.new do %>
174
+ View Terms
175
+ <% end %>
176
+ <% end %>
177
+ <% dialog.with_dialog_content do |content| %>
178
+ <% content.with_dialog_header do |header| %>
179
+ <% header.with_dialog_title do %>
180
+ Terms and Conditions
181
+ <% end %>
182
+ <% header.with_dialog_description do %>
183
+ Please read and accept our terms and conditions.
184
+ <% end %>
185
+ <% end %>
186
+ <div class="max-h-[300px] overflow-y-auto space-y-4">
187
+ <p class="text-sm">
188
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
189
+ </p>
190
+ <p class="text-sm">
191
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
192
+ </p>
193
+ <p class="text-sm">
194
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
195
+ </p>
196
+ <p class="text-sm">
197
+ Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
198
+ </p>
199
+ </div>
200
+ <% content.with_dialog_footer do %>
201
+ <%= render M9sh::DialogCloseComponent.new(as_child: true) do %>
202
+ <%= render M9sh::ButtonComponent.new(variant: :outline) do %>
203
+ Decline
204
+ <% end %>
205
+ <% end %>
206
+ <%= render M9sh::ButtonComponent.new do %>
207
+ Accept
208
+ <% end %>
209
+ <% end %>
210
+ <% end %>
211
+ <% end %>
212
+ <% end %>
213
+
214
+ <%= render Docs::ComponentPreviewComponent.new(
215
+ title: "Scrollable Content",
216
+ preview_content: scrollable_dialog_html,
217
+ code: dialog_scrollable_code,
218
+ ai_command: dialog_scrollable_code
219
+ ) %>
220
+ <% end %>
221
+
222
+ <% page.with_api do %>
223
+ <h3 class="text-lg font-semibold">Dialog Component Props</h3>
224
+ <%= render Docs::PropTableComponent.new(
225
+ props: [
226
+ {
227
+ name: "open",
228
+ type: "Boolean",
229
+ default: "false",
230
+ description: "Controls whether the dialog is open by default"
231
+ }
232
+ ]
233
+ ) %>
234
+
235
+ <h3 class="text-lg font-semibold mt-6">Dialog Slots</h3>
236
+ <%= render Docs::PropTableComponent.new(
237
+ props: [
238
+ {
239
+ name: "dialog_trigger",
240
+ type: "Slot",
241
+ default: nil,
242
+ description: "The element that triggers the dialog to open. Use with_dialog_trigger. Supports as_child pattern."
243
+ },
244
+ {
245
+ name: "dialog_content",
246
+ type: "Slot",
247
+ default: nil,
248
+ description: "The main dialog content container. Use with_dialog_content. Contains header, body content, and footer."
249
+ }
250
+ ]
251
+ ) %>
252
+
253
+ <h3 class="text-lg font-semibold mt-6">Dialog Content Slots</h3>
254
+ <%= render Docs::PropTableComponent.new(
255
+ props: [
256
+ {
257
+ name: "dialog_header",
258
+ type: "Slot",
259
+ default: nil,
260
+ description: "Header container with title and description. Use with_dialog_header."
261
+ },
262
+ {
263
+ name: "dialog_footer",
264
+ type: "Slot",
265
+ default: nil,
266
+ description: "Footer container for action buttons. Use with_dialog_footer."
267
+ }
268
+ ]
269
+ ) %>
270
+
271
+ <h3 class="text-lg font-semibold mt-6">Dialog Header Slots</h3>
272
+ <%= render Docs::PropTableComponent.new(
273
+ props: [
274
+ {
275
+ name: "dialog_title",
276
+ type: "Slot",
277
+ default: nil,
278
+ description: "The title/heading of the dialog. Use with_dialog_title within dialog_header."
279
+ },
280
+ {
281
+ name: "dialog_description",
282
+ type: "Slot",
283
+ default: nil,
284
+ description: "The description text below the title. Use with_dialog_description within dialog_header."
285
+ }
286
+ ]
287
+ ) %>
288
+
289
+ <h3 class="text-lg font-semibold mt-6">Additional Components</h3>
290
+ <%= render Docs::PropTableComponent.new(
291
+ props: [
292
+ {
293
+ name: "DialogCloseComponent",
294
+ type: "Component",
295
+ default: nil,
296
+ description: "A close button component. Supports as_child: true to wrap custom elements with close functionality."
297
+ }
298
+ ]
299
+ ) %>
300
+
301
+ <div class="mt-6 space-y-2">
302
+ <h3 class="text-lg font-semibold">JavaScript Actions</h3>
303
+ <ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
304
+ <li><code>data-action="click->m9sh--dialog#open"</code> - Opens the dialog</li>
305
+ <li><code>data-action="click->m9sh--dialog#close"</code> - Closes the dialog</li>
306
+ <li>The dialog automatically closes when clicking outside (on the overlay)</li>
307
+ <li>The close button (X) is automatically rendered in the top-right corner</li>
308
+ </ul>
309
+ </div>
310
+
311
+ <div class="mt-6 space-y-2">
312
+ <h3 class="text-lg font-semibold">Notes</h3>
313
+ <ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
314
+ <li>The dialog is centered on the screen with a dark overlay backdrop</li>
315
+ <li>Includes smooth animations for opening and closing</li>
316
+ <li>Automatically manages focus and accessibility with proper ARIA attributes</li>
317
+ <li>The dialog can be closed by clicking the backdrop or the close button</li>
318
+ <li>Content is automatically scrollable if it exceeds the dialog height</li>
319
+ <li>Use the footer slot for action buttons like Cancel and Confirm</li>
320
+ </ul>
321
+ </div>
322
+ <% end %>
323
+ <% end %>