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.
- checksums.yaml +4 -4
- data/.idea/hotcdn.iml +30 -0
- data/.mise.toml +2 -2
- data/app/assets/config/manifest.js +4 -0
- data/app/assets/images/icons/activity.svg +3 -0
- data/app/assets/images/icons/bell.svg +4 -0
- data/app/assets/images/icons/book.svg +4 -0
- data/app/assets/images/icons/chevron-down.svg +3 -0
- data/app/assets/images/icons/chevron-left.svg +3 -0
- data/app/assets/images/icons/chevron-right.svg +3 -0
- data/app/assets/images/icons/credit-card.svg +4 -0
- data/app/assets/images/icons/dollar-sign.svg +3 -0
- data/app/assets/images/icons/edit.svg +4 -0
- data/app/assets/images/icons/github.svg +3 -0
- data/app/assets/images/icons/home.svg +4 -0
- data/app/assets/images/icons/info.svg +5 -0
- data/app/assets/images/icons/layout.svg +6 -0
- data/app/assets/images/icons/logout.svg +5 -0
- data/app/assets/images/icons/menu.svg +5 -0
- data/app/assets/images/icons/moon.svg +3 -0
- data/app/assets/images/icons/paintbrush.svg +6 -0
- data/app/assets/images/icons/search.svg +4 -0
- data/app/assets/images/icons/settings.svg +4 -0
- data/app/assets/images/icons/sun.svg +11 -0
- data/app/assets/images/icons/user.svg +4 -0
- data/app/assets/images/icons/users.svg +5 -0
- data/app/assets/stylesheets/tailwind.css +1180 -0
- data/app/components/backdrop_component.rb +103 -0
- data/app/components/docs/code_block_component.rb +56 -0
- data/app/components/docs/component_api_component.rb +16 -0
- data/app/components/docs/component_examples_component.rb +16 -0
- data/app/components/docs/component_header_component.html.erb +8 -0
- data/app/components/docs/component_header_component.rb +14 -0
- data/app/components/docs/component_installation_component.html.erb +15 -0
- data/app/components/docs/component_installation_component.rb +13 -0
- data/app/components/docs/component_page_component.html.erb +9 -0
- data/app/components/docs/component_page_component.rb +19 -0
- data/app/components/docs/component_preview_component.rb +318 -0
- data/app/components/docs/component_usage_component.rb +18 -0
- data/app/components/docs/prop_table_component.rb +64 -0
- data/app/controllers/application_controller.rb +3 -0
- data/app/controllers/blocks_controller.rb +51 -0
- data/app/controllers/docs_controller.rb +162 -0
- data/app/controllers/showcase_controller.rb +42 -0
- data/app/helpers/blocks_helper.rb +343 -0
- data/app/helpers/docs_helper.rb +3807 -0
- data/app/helpers/m9sh/toast_helper.rb +46 -0
- data/app/helpers/m9sh_helper.rb +343 -0
- data/app/javascript/application.js +3 -0
- data/app/javascript/controllers/application.js +9 -0
- data/app/javascript/controllers/backdrop_controller.js +137 -0
- data/app/javascript/controllers/color_customizer_controller.js +569 -0
- data/app/javascript/controllers/color_theme_controller.js +120 -0
- data/app/javascript/controllers/docs/component_preview_controller.js +149 -0
- data/app/javascript/controllers/docs/copy_button_controller.js +20 -0
- data/app/javascript/controllers/index.js +6 -0
- data/app/javascript/controllers/theme_controller.js +23 -0
- data/app/views/blocks/_sidebar.html.erb +31 -0
- data/app/views/blocks/_toc.html.erb +29 -0
- data/app/views/blocks/examples/dashboard-01.html.erb +180 -0
- data/app/views/blocks/examples/dashboard-02.html.erb +190 -0
- data/app/views/blocks/examples/dashboard-03.html.erb +210 -0
- data/app/views/blocks/examples/settings-01.html.erb +220 -0
- data/app/views/blocks/examples/settings-02.html.erb +231 -0
- data/app/views/blocks/examples/settings-03.html.erb +340 -0
- data/app/views/blocks/index.html.erb +65 -0
- data/app/views/docs/_sidebar.html.erb +47 -0
- data/app/views/docs/_toc.html.erb +19 -0
- data/app/views/docs/about.html.erb +68 -0
- data/app/views/docs/components/accordion.html.erb +196 -0
- data/app/views/docs/components/alert.html.erb +272 -0
- data/app/views/docs/components/alert_dialog.html.erb +232 -0
- data/app/views/docs/components/avatar.html.erb +207 -0
- data/app/views/docs/components/badge.html.erb +145 -0
- data/app/views/docs/components/breadcrumb.html.erb +264 -0
- data/app/views/docs/components/button.html.erb +229 -0
- data/app/views/docs/components/card.html.erb +378 -0
- data/app/views/docs/components/checkbox.html.erb +212 -0
- data/app/views/docs/components/collapsible.html.erb +252 -0
- data/app/views/docs/components/dialog.html.erb +323 -0
- data/app/views/docs/components/dropdown_menu.html.erb +289 -0
- data/app/views/docs/components/hover_card.html.erb +220 -0
- data/app/views/docs/components/input.html.erb +254 -0
- data/app/views/docs/components/label.html.erb +128 -0
- data/app/views/docs/components/main.html.erb +352 -0
- data/app/views/docs/components/navbar.html.erb +394 -0
- data/app/views/docs/components/navigation_menu.html.erb +226 -0
- data/app/views/docs/components/popover.html.erb +267 -0
- data/app/views/docs/components/progress.html.erb +107 -0
- data/app/views/docs/components/radio_group.html.erb +209 -0
- data/app/views/docs/components/select.html.erb +260 -0
- data/app/views/docs/components/separator.html.erb +162 -0
- data/app/views/docs/components/sheet.html.erb +270 -0
- data/app/views/docs/components/sidebar.html.erb +597 -0
- data/app/views/docs/components/skeleton.html.erb +150 -0
- data/app/views/docs/components/slider.html.erb +218 -0
- data/app/views/docs/components/spinner.html.erb +132 -0
- data/app/views/docs/components/switch.html.erb +148 -0
- data/app/views/docs/components/table.html.erb +259 -0
- data/app/views/docs/components/tabs.html.erb +225 -0
- data/app/views/docs/components/textarea.html.erb +239 -0
- data/app/views/docs/components/theme_toggle.html.erb +135 -0
- data/app/views/docs/components/toast.html.erb +205 -0
- data/app/views/docs/components/toaster.html.erb +227 -0
- data/app/views/docs/components/toggle.html.erb +154 -0
- data/app/views/docs/components/tooltip.html.erb +216 -0
- data/app/views/docs/components/typography.html.erb +180 -0
- data/app/views/docs/index.html.erb +143 -0
- data/app/views/docs/installation.html.erb +155 -0
- data/app/views/docs/simple_test.html.erb +13 -0
- data/app/views/docs/test_accordion.html.erb +14 -0
- data/app/views/docs/usage.html.erb +272 -0
- data/app/views/layouts/application.html.erb +107 -0
- data/app/views/layouts/backdrop.html.erb +77 -0
- data/app/views/shared/_app_navbar.html.erb +240 -0
- data/app/views/shared/_navbar.html.erb +69 -0
- data/app/views/showcase/v2/_components_grid.html.erb +38 -0
- data/app/views/showcase/v2/_features.html.erb +59 -0
- data/app/views/showcase/v2/_forms.html.erb +195 -0
- data/app/views/showcase/v2/_hero.html.erb +55 -0
- data/app/views/showcase/v2/_metrics.html.erb +107 -0
- data/app/views/showcase/v2.html.erb +18 -0
- data/lib/m9sh/version.rb +1 -1
- data/m9sh.gemspec +1 -1
- 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 %>
|