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,239 @@
1
+ <%= render Docs::ComponentPageComponent.new(title: "Textarea") do |page| %>
2
+ <% page.with_header(
3
+ name: "Textarea",
4
+ description: "A multi-line text input field for capturing longer form content."
5
+ ) %>
6
+
7
+ <% page.with_installation(component_name: "textarea") %>
8
+
9
+ <% page.with_usage do %>
10
+ <%= render Docs::CodeBlockComponent.new(
11
+ code: textarea_usage_code,
12
+ language: "erb"
13
+ ) %>
14
+ <% end %>
15
+
16
+ <% page.with_examples do %>
17
+ <!-- Default Textarea Example -->
18
+ <% default_html = capture do %>
19
+ <%= render M9sh::TextareaComponent.new(
20
+ name: "message",
21
+ placeholder: "Type your message here."
22
+ ) %>
23
+ <% end %>
24
+
25
+ <%= render Docs::ComponentPreviewComponent.new(
26
+ title: "Default",
27
+ preview_content: default_html,
28
+ code: textarea_usage_code,
29
+ ai_command: textarea_usage_code
30
+ ) %>
31
+
32
+ <!-- Textarea with Label Example -->
33
+ <% with_label_html = capture do %>
34
+ <div class="space-y-2 w-full">
35
+ <%= render M9sh::LabelComponent.new(for_id: "message") do %>
36
+ Your message
37
+ <% end %>
38
+ <%= render M9sh::TextareaComponent.new(
39
+ id: "message",
40
+ name: "message",
41
+ placeholder: "Type your message here."
42
+ ) %>
43
+ </div>
44
+ <% end %>
45
+
46
+ <%= render Docs::ComponentPreviewComponent.new(
47
+ title: "With Label",
48
+ preview_content: with_label_html,
49
+ code: textarea_with_label_code,
50
+ ai_command: textarea_with_label_code
51
+ ) %>
52
+
53
+ <!-- Disabled Textarea Example -->
54
+ <% disabled_html = capture do %>
55
+ <%= render M9sh::TextareaComponent.new(
56
+ name: "disabled-textarea",
57
+ placeholder: "This textarea is disabled",
58
+ disabled: true
59
+ ) %>
60
+ <% end %>
61
+
62
+ <%= render Docs::ComponentPreviewComponent.new(
63
+ title: "Disabled",
64
+ preview_content: disabled_html,
65
+ code: textarea_disabled_code,
66
+ ai_command: textarea_disabled_code
67
+ ) %>
68
+
69
+ <!-- With Placeholder Example -->
70
+ <% placeholder_html = capture do %>
71
+ <%= render M9sh::TextareaComponent.new(
72
+ name: "bio",
73
+ placeholder: "Tell us a little bit about yourself"
74
+ ) %>
75
+ <% end %>
76
+
77
+ <%= render Docs::ComponentPreviewComponent.new(
78
+ title: "With Placeholder",
79
+ preview_content: placeholder_html,
80
+ code: textarea_with_placeholder_code,
81
+ ai_command: textarea_with_placeholder_code
82
+ ) %>
83
+
84
+ <!-- Different Sizes (Rows) Example -->
85
+ <% sizes_html = capture do %>
86
+ <div class="space-y-4 w-full">
87
+ <div class="space-y-2">
88
+ <%= render M9sh::LabelComponent.new(for_id: "small-textarea") do %>
89
+ Small (2 rows)
90
+ <% end %>
91
+ <%= render M9sh::TextareaComponent.new(
92
+ id: "small-textarea",
93
+ name: "small",
94
+ placeholder: "Smaller textarea",
95
+ rows: 2
96
+ ) %>
97
+ </div>
98
+
99
+ <div class="space-y-2">
100
+ <%= render M9sh::LabelComponent.new(for_id: "medium-textarea") do %>
101
+ Medium (3 rows - default)
102
+ <% end %>
103
+ <%= render M9sh::TextareaComponent.new(
104
+ id: "medium-textarea",
105
+ name: "medium",
106
+ placeholder: "Default textarea",
107
+ rows: 3
108
+ ) %>
109
+ </div>
110
+
111
+ <div class="space-y-2">
112
+ <%= render M9sh::LabelComponent.new(for_id: "large-textarea") do %>
113
+ Large (6 rows)
114
+ <% end %>
115
+ <%= render M9sh::TextareaComponent.new(
116
+ id: "large-textarea",
117
+ name: "large",
118
+ placeholder: "Larger textarea",
119
+ rows: 6
120
+ ) %>
121
+ </div>
122
+ </div>
123
+ <% end %>
124
+
125
+ <%= render Docs::ComponentPreviewComponent.new(
126
+ title: "Different Sizes",
127
+ preview_content: sizes_html,
128
+ code: textarea_sizes_code,
129
+ ai_command: textarea_sizes_code
130
+ ) %>
131
+
132
+ <!-- With Value Example -->
133
+ <% with_value_html = capture do %>
134
+ <%= render M9sh::TextareaComponent.new(
135
+ name: "content",
136
+ value: "This textarea has pre-filled content that can be edited.",
137
+ placeholder: "Enter content"
138
+ ) %>
139
+ <% end %>
140
+
141
+ <%= render Docs::ComponentPreviewComponent.new(
142
+ title: "With Value",
143
+ preview_content: with_value_html,
144
+ code: textarea_with_value_code,
145
+ ai_command: textarea_with_value_code
146
+ ) %>
147
+
148
+ <!-- Read-only Example -->
149
+ <% readonly_html = capture do %>
150
+ <%= render M9sh::TextareaComponent.new(
151
+ name: "readonly-textarea",
152
+ value: "This textarea is read-only and cannot be edited.",
153
+ readonly: true
154
+ ) %>
155
+ <% end %>
156
+
157
+ <%= render Docs::ComponentPreviewComponent.new(
158
+ title: "Read-only",
159
+ preview_content: readonly_html,
160
+ code: textarea_readonly_code,
161
+ ai_command: textarea_readonly_code
162
+ ) %>
163
+ <% end %>
164
+
165
+ <% page.with_api do %>
166
+ <%= render Docs::PropTableComponent.new(
167
+ props: [
168
+ {
169
+ name: "name",
170
+ type: "String",
171
+ default: "nil",
172
+ description: "The name attribute for the textarea field, used for form submission."
173
+ },
174
+ {
175
+ name: "value",
176
+ type: "String",
177
+ default: "nil",
178
+ description: "The initial value/content of the textarea."
179
+ },
180
+ {
181
+ name: "placeholder",
182
+ type: "String",
183
+ default: "nil",
184
+ description: "Placeholder text displayed when the textarea is empty."
185
+ },
186
+ {
187
+ name: "rows",
188
+ type: "Integer",
189
+ default: "3",
190
+ description: "The number of visible text rows. Controls the initial height of the textarea."
191
+ },
192
+ {
193
+ name: "disabled",
194
+ type: "Boolean",
195
+ default: "false",
196
+ description: "If true, the textarea is disabled and cannot be interacted with."
197
+ },
198
+ {
199
+ name: "readonly",
200
+ type: "Boolean",
201
+ default: "false",
202
+ description: "If true, the textarea is read-only and its value cannot be changed."
203
+ },
204
+ {
205
+ name: "**extra_attrs",
206
+ type: "Hash",
207
+ default: "{}",
208
+ description: "Additional HTML attributes to apply to the textarea element (e.g., id, class, data attributes)."
209
+ }
210
+ ]
211
+ ) %>
212
+
213
+ <div class="mt-6 space-y-2">
214
+ <h3 class="text-lg font-semibold">Styling</h3>
215
+ <p class="text-sm text-muted-foreground">
216
+ The textarea component comes with default styles that match the design system:
217
+ </p>
218
+ <ul class="list-disc list-inside space-y-1 text-sm text-muted-foreground">
219
+ <li>Border and background colors that adapt to light/dark themes</li>
220
+ <li>Focus states with visible ring for keyboard navigation</li>
221
+ <li>Invalid state styling via <code class="text-xs bg-muted px-1.5 py-0.5 rounded">aria-invalid</code> attribute</li>
222
+ <li>Smooth transitions for interactive states</li>
223
+ <li>Disabled state with reduced opacity and cursor changes</li>
224
+ <li>Minimum height of 16 (4rem) with flexible width</li>
225
+ </ul>
226
+ </div>
227
+
228
+ <div class="mt-6 space-y-2">
229
+ <h3 class="text-lg font-semibold">Accessibility</h3>
230
+ <ul class="list-disc list-inside space-y-1 text-sm text-muted-foreground">
231
+ <li>Use with <code class="text-xs bg-muted px-1.5 py-0.5 rounded">LabelComponent</code> for proper form field labeling</li>
232
+ <li>Supports <code class="text-xs bg-muted px-1.5 py-0.5 rounded">disabled</code> and <code class="text-xs bg-muted px-1.5 py-0.5 rounded">readonly</code> states</li>
233
+ <li>Includes <code class="text-xs bg-muted px-1.5 py-0.5 rounded">data-slot="textarea"</code> attribute for component composition</li>
234
+ <li>Keyboard accessible with visible focus indicators</li>
235
+ <li>Supports ARIA attributes via <code class="text-xs bg-muted px-1.5 py-0.5 rounded">extra_attrs</code></li>
236
+ </ul>
237
+ </div>
238
+ <% end %>
239
+ <% end %>
@@ -0,0 +1,135 @@
1
+ <%= render Docs::ComponentPageComponent.new(title: "ThemeToggle") do |page| %>
2
+ <% page.with_header(
3
+ name: "ThemeToggle",
4
+ description: "A specialized toggle button for switching between light and dark themes."
5
+ ) %>
6
+
7
+ <% page.with_installation(component_name: "theme_toggle") do %>
8
+ <p class="text-sm text-muted-foreground">
9
+ This component requires the Stimulus controller:
10
+ </p>
11
+
12
+ <%= render Docs::CodeBlockComponent.new(
13
+ code: <<~JAVASCRIPT,
14
+ # app/javascript/controllers/m9sh/theme_controller.js
15
+ JAVASCRIPT
16
+ language: "bash"
17
+ ) %>
18
+ <% end %>
19
+
20
+ <% page.with_usage do %>
21
+ <%= render Docs::CodeBlockComponent.new(
22
+ code: theme_toggle_usage_code,
23
+ language: "erb"
24
+ ) %>
25
+ <% end %>
26
+
27
+ <% page.with_examples do %>
28
+ <!-- Default Example -->
29
+ <% theme_toggle_html = capture do %>
30
+ <%= render M9sh::ThemeToggleComponent.new %>
31
+ <% end %>
32
+
33
+ <%= render Docs::ComponentPreviewComponent.new(
34
+ title: "Default",
35
+ preview_content: theme_toggle_html,
36
+ code: theme_toggle_default_code,
37
+ ai_command: theme_toggle_default_code
38
+ ) %>
39
+
40
+ <!-- With Context Example -->
41
+ <% with_context_html = capture do %>
42
+ <div class="flex items-center gap-3">
43
+ <span class="text-sm text-muted-foreground">Theme:</span>
44
+ <%= render M9sh::ThemeToggleComponent.new %>
45
+ </div>
46
+ <% end %>
47
+
48
+ <%= render Docs::ComponentPreviewComponent.new(
49
+ title: "With Label",
50
+ preview_content: with_context_html,
51
+ code: theme_toggle_with_label_code,
52
+ ai_command: theme_toggle_with_label_code
53
+ ) %>
54
+
55
+ <!-- In Header Example -->
56
+ <% in_header_html = capture do %>
57
+ <div class="flex items-center justify-between border rounded-lg p-4 bg-card">
58
+ <div>
59
+ <h3 class="text-lg font-semibold">Settings</h3>
60
+ <p class="text-sm text-muted-foreground">Manage your preferences</p>
61
+ </div>
62
+ <%= render M9sh::ThemeToggleComponent.new %>
63
+ </div>
64
+ <% end %>
65
+
66
+ <%= render Docs::ComponentPreviewComponent.new(
67
+ title: "In Header",
68
+ preview_content: in_header_html,
69
+ code: theme_toggle_in_header_code,
70
+ ai_command: theme_toggle_in_header_code
71
+ ) %>
72
+
73
+ <!-- In Navigation Example -->
74
+ <% in_nav_html = capture do %>
75
+ <nav class="flex items-center justify-between border rounded-lg p-4 bg-card">
76
+ <div class="flex items-center gap-4">
77
+ <span class="font-semibold">My App</span>
78
+ <a href="#" class="text-sm text-muted-foreground hover:text-foreground">Home</a>
79
+ <a href="#" class="text-sm text-muted-foreground hover:text-foreground">About</a>
80
+ <a href="#" class="text-sm text-muted-foreground hover:text-foreground">Contact</a>
81
+ </div>
82
+ <%= render M9sh::ThemeToggleComponent.new %>
83
+ </nav>
84
+ <% end %>
85
+
86
+ <%= render Docs::ComponentPreviewComponent.new(
87
+ title: "In Navigation",
88
+ preview_content: in_nav_html,
89
+ code: theme_toggle_in_nav_code,
90
+ ai_command: theme_toggle_in_nav_code
91
+ ) %>
92
+ <% end %>
93
+
94
+ <% page.with_api do %>
95
+ <%= render Docs::PropTableComponent.new(
96
+ props: [
97
+ {
98
+ name: "**extra_attrs",
99
+ type: "Hash",
100
+ default: "{}",
101
+ description: "Additional HTML attributes to pass to the component wrapper (class, data attributes, etc.)"
102
+ }
103
+ ]
104
+ ) %>
105
+
106
+ <div class="mt-6 space-y-2">
107
+ <h3 class="text-lg font-semibold">Features</h3>
108
+ <ul class="list-disc list-inside space-y-2 text-sm text-muted-foreground">
109
+ <li>Automatic theme persistence using localStorage</li>
110
+ <li>Smooth icon transitions between light and dark modes</li>
111
+ <li>Respects system theme preferences</li>
112
+ <li>Accessible with proper ARIA labels</li>
113
+ <li>Animated icon transitions using Tailwind CSS</li>
114
+ </ul>
115
+ </div>
116
+
117
+ <div class="mt-6 space-y-2">
118
+ <h3 class="text-lg font-semibold">Stimulus Controller</h3>
119
+ <p class="text-sm text-muted-foreground">
120
+ The theme toggle functionality is powered by a Stimulus controller that:
121
+ </p>
122
+
123
+ <ul class="list-disc list-inside space-y-2 text-sm text-muted-foreground ml-4">
124
+ <li>Loads the saved theme preference from localStorage on connect</li>
125
+ <li>Applies the <code>dark</code> class to the document root element</li>
126
+ <li>Saves theme changes to localStorage</li>
127
+ <li>Listens for system theme preference changes</li>
128
+ </ul>
129
+
130
+ <p class="text-sm text-muted-foreground mt-4">
131
+ The controller exposes a <code>toggle</code> action that can be triggered by clicking the button.
132
+ </p>
133
+ </div>
134
+ <% end %>
135
+ <% end %>
@@ -0,0 +1,205 @@
1
+ <%= render Docs::ComponentPageComponent.new(title: "Toast - m9sh Components") do |page| %>
2
+ <% page.with_header(
3
+ name: "Toast",
4
+ description: "A succinct message that is displayed temporarily at the corner of the screen."
5
+ ) %>
6
+
7
+ <% page.with_installation(component_name: "toast") %>
8
+
9
+ <% page.with_usage do %>
10
+ <%= render Docs::CodeBlockComponent.new(
11
+ code: toast_usage_code,
12
+ language: "erb"
13
+ ) %>
14
+ <% end %>
15
+
16
+ <% page.with_examples do %>
17
+ <!-- Default Example -->
18
+ <% default_toast_html = capture do %>
19
+ <%= render M9sh::ToastComponent.new(
20
+ title: "Notification",
21
+ description: "Your changes have been saved successfully."
22
+ ) %>
23
+ <% end %>
24
+
25
+ <%= render Docs::ComponentPreviewComponent.new(
26
+ title: "Default",
27
+ preview_content: default_toast_html,
28
+ code: toast_default_code,
29
+ ai_command: toast_default_code
30
+ ) %>
31
+
32
+ <!-- Success Example -->
33
+ <% success_toast_html = capture do %>
34
+ <%= render M9sh::ToastComponent.new(
35
+ title: "Success",
36
+ description: "Your profile has been updated.",
37
+ variant: :success
38
+ ) %>
39
+ <% end %>
40
+
41
+ <%= render Docs::ComponentPreviewComponent.new(
42
+ title: "Success",
43
+ preview_content: success_toast_html,
44
+ code: toast_success_code,
45
+ ai_command: toast_success_code
46
+ ) %>
47
+
48
+ <!-- Error Example -->
49
+ <% error_toast_html = capture do %>
50
+ <%= render M9sh::ToastComponent.new(
51
+ title: "Error",
52
+ description: "Unable to save changes. Please try again.",
53
+ variant: :error
54
+ ) %>
55
+ <% end %>
56
+
57
+ <%= render Docs::ComponentPreviewComponent.new(
58
+ title: "Error",
59
+ preview_content: error_toast_html,
60
+ code: toast_error_code,
61
+ ai_command: toast_error_code
62
+ ) %>
63
+
64
+ <!-- Warning Example -->
65
+ <% warning_toast_html = capture do %>
66
+ <%= render M9sh::ToastComponent.new(
67
+ title: "Warning",
68
+ description: "Your session will expire in 5 minutes.",
69
+ variant: :warning
70
+ ) %>
71
+ <% end %>
72
+
73
+ <%= render Docs::ComponentPreviewComponent.new(
74
+ title: "Warning",
75
+ preview_content: warning_toast_html,
76
+ code: toast_warning_code,
77
+ ai_command: toast_warning_code
78
+ ) %>
79
+
80
+ <!-- Title Only Example -->
81
+ <% title_only_html = capture do %>
82
+ <%= render M9sh::ToastComponent.new(
83
+ title: "Message sent successfully!",
84
+ variant: :success
85
+ ) %>
86
+ <% end %>
87
+
88
+ <%= render Docs::ComponentPreviewComponent.new(
89
+ title: "Title Only",
90
+ preview_content: title_only_html,
91
+ code: toast_title_only_code,
92
+ ai_command: toast_title_only_code
93
+ ) %>
94
+
95
+ <!-- Custom Duration Example -->
96
+ <% custom_duration_html = capture do %>
97
+ <%= render M9sh::ToastComponent.new(
98
+ title: "Quick notification",
99
+ description: "This will disappear in 2 seconds.",
100
+ duration: 2000
101
+ ) %>
102
+ <% end %>
103
+
104
+ <%= render Docs::ComponentPreviewComponent.new(
105
+ title: "Custom Duration",
106
+ preview_content: custom_duration_html,
107
+ code: toast_custom_duration_code,
108
+ ai_command: toast_custom_duration_code
109
+ ) %>
110
+
111
+ <!-- Variants Example -->
112
+ <% variants_toast_html = capture do %>
113
+ <div class="space-y-4">
114
+ <%= render M9sh::ToastComponent.new(
115
+ title: "Default",
116
+ description: "This is a default toast.",
117
+ variant: :default
118
+ ) %>
119
+
120
+ <%= render M9sh::ToastComponent.new(
121
+ title: "Success",
122
+ description: "Your action was successful.",
123
+ variant: :success
124
+ ) %>
125
+
126
+ <%= render M9sh::ToastComponent.new(
127
+ title: "Error",
128
+ description: "An error occurred.",
129
+ variant: :error
130
+ ) %>
131
+
132
+ <%= render M9sh::ToastComponent.new(
133
+ title: "Warning",
134
+ description: "Please review your changes.",
135
+ variant: :warning
136
+ ) %>
137
+
138
+ <%= render M9sh::ToastComponent.new(
139
+ title: "Info",
140
+ description: "Here's some information.",
141
+ variant: :info
142
+ ) %>
143
+ </div>
144
+ <% end %>
145
+
146
+ <%= render Docs::ComponentPreviewComponent.new(
147
+ title: "All Variants",
148
+ preview_content: variants_toast_html,
149
+ code: toast_variants_code,
150
+ ai_command: toast_variants_code
151
+ ) %>
152
+ <% end %>
153
+
154
+ <% page.with_api do %>
155
+ <h3 class="text-lg font-semibold">Toast Component Props</h3>
156
+ <%= render Docs::PropTableComponent.new(
157
+ props: [
158
+ {
159
+ name: "title",
160
+ type: "String",
161
+ default: nil,
162
+ description: "The title of the toast notification (required)"
163
+ },
164
+ {
165
+ name: "description",
166
+ type: "String",
167
+ default: "nil",
168
+ description: "Optional description text for the toast"
169
+ },
170
+ {
171
+ name: "variant",
172
+ type: "Symbol",
173
+ default: ":default",
174
+ description: "The visual style variant. Options: :default, :success, :error, :warning, :info"
175
+ },
176
+ {
177
+ name: "duration",
178
+ type: "Integer",
179
+ default: "5000",
180
+ description: "Duration in milliseconds before the toast auto-dismisses. Set to 0 to prevent auto-dismiss"
181
+ }
182
+ ]
183
+ ) %>
184
+
185
+ <div class="mt-6 space-y-2">
186
+ <h3 class="text-lg font-semibold">Notes</h3>
187
+ <ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
188
+ <li>The toast automatically slides in from the right and animates when dismissed</li>
189
+ <li>Each toast includes a close button for manual dismissal</li>
190
+ <li>The description is optional - you can use title-only toasts for simpler messages</li>
191
+ <li>The component uses Stimulus controllers for animation and auto-dismiss functionality</li>
192
+ <li>Toasts are positioned using fixed positioning and should be used with the ToasterComponent container</li>
193
+ <li>The component includes proper ARIA attributes for accessibility</li>
194
+ </ul>
195
+ </div>
196
+
197
+ <div class="mt-6 space-y-2">
198
+ <h3 class="text-lg font-semibold">Integration with Toaster</h3>
199
+ <p class="text-sm text-muted-foreground">
200
+ While you can render Toast components directly in your views, it's recommended to use them with the ToasterComponent
201
+ for better positioning and dynamic toast management. See the Toaster component documentation for more information.
202
+ </p>
203
+ </div>
204
+ <% end %>
205
+ <% end %>