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,267 @@
|
|
1
|
+
<%= render Docs::ComponentPageComponent.new(title: "Popover") do |page| %>
|
2
|
+
<% page.with_header(
|
3
|
+
name: "Popover",
|
4
|
+
description: "Displays rich content in a portal, triggered by a button or other element."
|
5
|
+
) %>
|
6
|
+
|
7
|
+
<% page.with_installation(component_name: "popover") %>
|
8
|
+
|
9
|
+
<% page.with_usage do %>
|
10
|
+
<%= render Docs::CodeBlockComponent.new(
|
11
|
+
code: popover_usage_code,
|
12
|
+
language: "erb"
|
13
|
+
) %>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<% page.with_examples do %>
|
17
|
+
<!-- Default Example -->
|
18
|
+
<% default_popover_html = capture do %>
|
19
|
+
<%= render M9sh::PopoverComponent.new do |popover| %>
|
20
|
+
<% popover.with_trigger do %>
|
21
|
+
<%= render M9sh::ButtonComponent.new(variant: :outline) do %>
|
22
|
+
Open Popover
|
23
|
+
<% end %>
|
24
|
+
<% end %>
|
25
|
+
<% popover.with_popover_content do %>
|
26
|
+
<div class="space-y-2">
|
27
|
+
<h4 class="font-medium text-sm">Popover Content</h4>
|
28
|
+
<p class="text-sm text-muted-foreground">
|
29
|
+
This is a simple popover with some text content. Popovers are great for displaying additional information without navigating away.
|
30
|
+
</p>
|
31
|
+
</div>
|
32
|
+
<% end %>
|
33
|
+
<% end %>
|
34
|
+
<% end %>
|
35
|
+
|
36
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
37
|
+
title: "Default",
|
38
|
+
preview_content: default_popover_html,
|
39
|
+
code: popover_usage_code,
|
40
|
+
ai_command: popover_usage_code
|
41
|
+
) %>
|
42
|
+
|
43
|
+
<!-- With Form Example -->
|
44
|
+
<% form_popover_html = capture do %>
|
45
|
+
<%= render M9sh::PopoverComponent.new do |popover| %>
|
46
|
+
<% popover.with_trigger do %>
|
47
|
+
<%= render M9sh::ButtonComponent.new(variant: :outline) do %>
|
48
|
+
<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="mr-2">
|
49
|
+
<circle cx="12" cy="12" r="3"/>
|
50
|
+
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
51
|
+
</svg>
|
52
|
+
Settings
|
53
|
+
<% end %>
|
54
|
+
<% end %>
|
55
|
+
<% popover.with_popover_content do %>
|
56
|
+
<div class="space-y-4">
|
57
|
+
<h4 class="font-medium text-sm">Dimensions</h4>
|
58
|
+
<div class="space-y-2">
|
59
|
+
<%= render M9sh::LabelComponent.new(for_id: "popover-width") do %>
|
60
|
+
Width
|
61
|
+
<% end %>
|
62
|
+
<%= render M9sh::InputComponent.new(
|
63
|
+
id: "popover-width",
|
64
|
+
type: "number",
|
65
|
+
placeholder: "100",
|
66
|
+
value: "300"
|
67
|
+
) %>
|
68
|
+
</div>
|
69
|
+
<div class="space-y-2">
|
70
|
+
<%= render M9sh::LabelComponent.new(for_id: "popover-height") do %>
|
71
|
+
Height
|
72
|
+
<% end %>
|
73
|
+
<%= render M9sh::InputComponent.new(
|
74
|
+
id: "popover-height",
|
75
|
+
type: "number",
|
76
|
+
placeholder: "100",
|
77
|
+
value: "200"
|
78
|
+
) %>
|
79
|
+
</div>
|
80
|
+
<%= render M9sh::ButtonComponent.new(class: "w-full") do %>
|
81
|
+
Save Changes
|
82
|
+
<% end %>
|
83
|
+
</div>
|
84
|
+
<% end %>
|
85
|
+
<% end %>
|
86
|
+
<% end %>
|
87
|
+
|
88
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
89
|
+
title: "With Form",
|
90
|
+
preview_content: form_popover_html,
|
91
|
+
code: popover_with_form_code,
|
92
|
+
ai_command: popover_with_form_code
|
93
|
+
) %>
|
94
|
+
|
95
|
+
<!-- With Rich Content Example -->
|
96
|
+
<% rich_popover_html = capture do %>
|
97
|
+
<%= render M9sh::PopoverComponent.new do |popover| %>
|
98
|
+
<% popover.with_trigger do %>
|
99
|
+
<%= render M9sh::ButtonComponent.new(variant: :outline) do %>
|
100
|
+
<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="mr-2">
|
101
|
+
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
|
102
|
+
<circle cx="12" cy="7" r="4"/>
|
103
|
+
</svg>
|
104
|
+
User Info
|
105
|
+
<% end %>
|
106
|
+
<% end %>
|
107
|
+
<% popover.with_popover_content do %>
|
108
|
+
<div class="space-y-3">
|
109
|
+
<div class="flex items-center gap-3">
|
110
|
+
<div class="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center font-semibold text-primary">
|
111
|
+
JD
|
112
|
+
</div>
|
113
|
+
<div>
|
114
|
+
<h4 class="font-semibold text-sm">John Doe</h4>
|
115
|
+
<p class="text-xs text-muted-foreground">john@example.com</p>
|
116
|
+
</div>
|
117
|
+
</div>
|
118
|
+
<div class="border-t pt-3">
|
119
|
+
<p class="text-xs text-muted-foreground mb-2">Bio</p>
|
120
|
+
<p class="text-sm">Software developer passionate about building great user experiences.</p>
|
121
|
+
</div>
|
122
|
+
<div class="flex gap-2 pt-2">
|
123
|
+
<%= render M9sh::ButtonComponent.new(variant: :outline, size: :sm, class: "flex-1") do %>
|
124
|
+
View Profile
|
125
|
+
<% end %>
|
126
|
+
<%= render M9sh::ButtonComponent.new(size: :sm, class: "flex-1") do %>
|
127
|
+
Message
|
128
|
+
<% end %>
|
129
|
+
</div>
|
130
|
+
</div>
|
131
|
+
<% end %>
|
132
|
+
<% end %>
|
133
|
+
<% end %>
|
134
|
+
|
135
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
136
|
+
title: "With Rich Content",
|
137
|
+
preview_content: rich_popover_html,
|
138
|
+
code: popover_rich_content_code,
|
139
|
+
ai_command: popover_rich_content_code
|
140
|
+
) %>
|
141
|
+
|
142
|
+
<!-- Different Positions Example -->
|
143
|
+
<% positions_popover_html = capture do %>
|
144
|
+
<div class="flex flex-col items-center gap-4">
|
145
|
+
<div>
|
146
|
+
<%= render M9sh::PopoverComponent.new(side: "top") do |popover| %>
|
147
|
+
<% popover.with_trigger do %>
|
148
|
+
<%= render M9sh::ButtonComponent.new(variant: :outline) do %>
|
149
|
+
Top
|
150
|
+
<% end %>
|
151
|
+
<% end %>
|
152
|
+
<% popover.with_popover_content do %>
|
153
|
+
<p class="text-sm">This popover appears at the top.</p>
|
154
|
+
<% end %>
|
155
|
+
<% end %>
|
156
|
+
</div>
|
157
|
+
<div class="flex gap-4">
|
158
|
+
<%= render M9sh::PopoverComponent.new(side: "left") do |popover| %>
|
159
|
+
<% popover.with_trigger do %>
|
160
|
+
<%= render M9sh::ButtonComponent.new(variant: :outline) do %>
|
161
|
+
Left
|
162
|
+
<% end %>
|
163
|
+
<% end %>
|
164
|
+
<% popover.with_popover_content do %>
|
165
|
+
<p class="text-sm">This popover appears on the left.</p>
|
166
|
+
<% end %>
|
167
|
+
<% end %>
|
168
|
+
|
169
|
+
<%= render M9sh::PopoverComponent.new(side: "right") do |popover| %>
|
170
|
+
<% popover.with_trigger do %>
|
171
|
+
<%= render M9sh::ButtonComponent.new(variant: :outline) do %>
|
172
|
+
Right
|
173
|
+
<% end %>
|
174
|
+
<% end %>
|
175
|
+
<% popover.with_popover_content do %>
|
176
|
+
<p class="text-sm">This popover appears on the right.</p>
|
177
|
+
<% end %>
|
178
|
+
<% end %>
|
179
|
+
</div>
|
180
|
+
<div>
|
181
|
+
<%= render M9sh::PopoverComponent.new(side: "bottom") do |popover| %>
|
182
|
+
<% popover.with_trigger do %>
|
183
|
+
<%= render M9sh::ButtonComponent.new(variant: :outline) do %>
|
184
|
+
Bottom
|
185
|
+
<% end %>
|
186
|
+
<% end %>
|
187
|
+
<% popover.with_popover_content do %>
|
188
|
+
<p class="text-sm">This popover appears at the bottom.</p>
|
189
|
+
<% end %>
|
190
|
+
<% end %>
|
191
|
+
</div>
|
192
|
+
</div>
|
193
|
+
<% end %>
|
194
|
+
|
195
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
196
|
+
title: "Different Positions",
|
197
|
+
preview_content: positions_popover_html,
|
198
|
+
code: nil,
|
199
|
+
ai_command: nil
|
200
|
+
) %>
|
201
|
+
<% end %>
|
202
|
+
|
203
|
+
<% page.with_api do %>
|
204
|
+
<h3 class="text-lg font-semibold">Popover Component</h3>
|
205
|
+
|
206
|
+
<%= render Docs::PropTableComponent.new(
|
207
|
+
props: [
|
208
|
+
{
|
209
|
+
name: "align",
|
210
|
+
type: "String",
|
211
|
+
default: '"center"',
|
212
|
+
description: "The alignment of the popover relative to the trigger. Options: 'start', 'center', 'end'"
|
213
|
+
},
|
214
|
+
{
|
215
|
+
name: "side",
|
216
|
+
type: "String",
|
217
|
+
default: '"bottom"',
|
218
|
+
description: "The side of the trigger where the popover appears. Options: 'top', 'right', 'bottom', 'left'"
|
219
|
+
}
|
220
|
+
]
|
221
|
+
) %>
|
222
|
+
|
223
|
+
<h3 class="text-lg font-semibold mt-6">Popover Slots</h3>
|
224
|
+
<%= render Docs::PropTableComponent.new(
|
225
|
+
props: [
|
226
|
+
{
|
227
|
+
name: "trigger",
|
228
|
+
type: "Slot",
|
229
|
+
default: nil,
|
230
|
+
description: "The element that triggers the popover to open. Typically a button or link."
|
231
|
+
},
|
232
|
+
{
|
233
|
+
name: "popover_content",
|
234
|
+
type: "Slot",
|
235
|
+
default: nil,
|
236
|
+
description: "The content displayed inside the popover. Can contain any HTML including forms, text, images, etc."
|
237
|
+
}
|
238
|
+
]
|
239
|
+
) %>
|
240
|
+
|
241
|
+
<div class="mt-6 space-y-2">
|
242
|
+
<h3 class="text-lg font-semibold">Features</h3>
|
243
|
+
<ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
|
244
|
+
<li>Can be positioned on any side of the trigger (top, right, bottom, left)</li>
|
245
|
+
<li>Supports alignment options for fine-tuned positioning</li>
|
246
|
+
<li>Toggles on click - clicking the trigger again closes the popover</li>
|
247
|
+
<li>Automatically closes when clicking outside the popover</li>
|
248
|
+
<li>Smooth fade and zoom animations for opening and closing</li>
|
249
|
+
<li>Fixed width (w-72 / 18rem) by default for consistent sizing</li>
|
250
|
+
<li>Supports rich content including forms, images, and interactive elements</li>
|
251
|
+
<li>Includes subtle border and shadow for visual depth</li>
|
252
|
+
</ul>
|
253
|
+
</div>
|
254
|
+
|
255
|
+
<div class="mt-6 space-y-2">
|
256
|
+
<h3 class="text-lg font-semibold">Notes</h3>
|
257
|
+
<ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
|
258
|
+
<li>The popover uses Stimulus controllers for interactive behavior</li>
|
259
|
+
<li>Click outside or click the trigger again to close the popover</li>
|
260
|
+
<li>The popover content has a fixed width - use custom classes to override if needed</li>
|
261
|
+
<li>Both trigger and popover_content slots are required for the component to function</li>
|
262
|
+
<li>The popover is positioned absolutely relative to its inline-block container</li>
|
263
|
+
<li>Consider using Sheet component for larger amounts of content or complex interactions</li>
|
264
|
+
</ul>
|
265
|
+
</div>
|
266
|
+
<% end %>
|
267
|
+
<% end %>
|
@@ -0,0 +1,107 @@
|
|
1
|
+
<%= render Docs::ComponentPageComponent.new(title: "Progress") do |page| %>
|
2
|
+
<% page.with_header(
|
3
|
+
name: "Progress",
|
4
|
+
description: "Displays an indicator showing the completion progress of a task, typically displayed as a progress bar."
|
5
|
+
) %>
|
6
|
+
|
7
|
+
<% page.with_installation(component_name: "progress") %>
|
8
|
+
|
9
|
+
<% page.with_usage do %>
|
10
|
+
<%= render Docs::CodeBlockComponent.new(
|
11
|
+
code: progress_usage_code,
|
12
|
+
language: "erb"
|
13
|
+
) %>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<% page.with_examples do %>
|
17
|
+
<!-- Default Example -->
|
18
|
+
<% default_html = capture do %>
|
19
|
+
<%= render M9sh::ProgressComponent.new(value: 60) %>
|
20
|
+
<% end %>
|
21
|
+
|
22
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
23
|
+
title: "Default",
|
24
|
+
preview_content: default_html,
|
25
|
+
code: progress_usage_code,
|
26
|
+
ai_command: progress_usage_code
|
27
|
+
) %>
|
28
|
+
|
29
|
+
<!-- Different Values Example -->
|
30
|
+
<% values_html = capture do %>
|
31
|
+
<div class="space-y-4 w-full">
|
32
|
+
<div>
|
33
|
+
<p class="text-sm text-muted-foreground mb-2">25%</p>
|
34
|
+
<%= render M9sh::ProgressComponent.new(value: 25) %>
|
35
|
+
</div>
|
36
|
+
<div>
|
37
|
+
<p class="text-sm text-muted-foreground mb-2">50%</p>
|
38
|
+
<%= render M9sh::ProgressComponent.new(value: 50) %>
|
39
|
+
</div>
|
40
|
+
<div>
|
41
|
+
<p class="text-sm text-muted-foreground mb-2">75%</p>
|
42
|
+
<%= render M9sh::ProgressComponent.new(value: 75) %>
|
43
|
+
</div>
|
44
|
+
<div>
|
45
|
+
<p class="text-sm text-muted-foreground mb-2">100%</p>
|
46
|
+
<%= render M9sh::ProgressComponent.new(value: 100) %>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
<% end %>
|
50
|
+
|
51
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
52
|
+
title: "Different Values",
|
53
|
+
preview_content: values_html,
|
54
|
+
code: progress_values_code,
|
55
|
+
ai_command: progress_values_code
|
56
|
+
) %>
|
57
|
+
|
58
|
+
<!-- Indeterminate Example -->
|
59
|
+
<% indeterminate_html = capture do %>
|
60
|
+
<div class="space-y-2 w-full">
|
61
|
+
<%= render M9sh::ProgressComponent.new(value: 0) %>
|
62
|
+
<p class="text-sm text-muted-foreground">Loading...</p>
|
63
|
+
</div>
|
64
|
+
<% end %>
|
65
|
+
|
66
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
67
|
+
title: "Indeterminate",
|
68
|
+
preview_content: indeterminate_html,
|
69
|
+
code: progress_indeterminate_code,
|
70
|
+
ai_command: progress_indeterminate_code
|
71
|
+
) %>
|
72
|
+
<% end %>
|
73
|
+
|
74
|
+
<% page.with_api do %>
|
75
|
+
<%= render Docs::PropTableComponent.new(
|
76
|
+
props: [
|
77
|
+
{
|
78
|
+
name: "value",
|
79
|
+
type: "Number",
|
80
|
+
default: "0",
|
81
|
+
description: "The current progress value. Should be between 0 and max."
|
82
|
+
},
|
83
|
+
{
|
84
|
+
name: "max",
|
85
|
+
type: "Number",
|
86
|
+
default: "100",
|
87
|
+
description: "The maximum progress value. Used to calculate the percentage."
|
88
|
+
}
|
89
|
+
]
|
90
|
+
) %>
|
91
|
+
|
92
|
+
<div class="mt-6 space-y-2">
|
93
|
+
<h3 class="text-lg font-semibold">Accessibility</h3>
|
94
|
+
<ul class="list-disc list-inside space-y-1 text-sm text-muted-foreground">
|
95
|
+
<li>Uses proper ARIA attributes (<code>role="progressbar"</code>, <code>aria-valuenow</code>, <code>aria-valuemin</code>, <code>aria-valuemax</code>)</li>
|
96
|
+
<li>Works with screen readers to announce progress updates</li>
|
97
|
+
<li>Follows WAI-ARIA design pattern for progress bars</li>
|
98
|
+
</ul>
|
99
|
+
</div>
|
100
|
+
|
101
|
+
<div class="mt-4 p-4 border rounded-lg bg-muted">
|
102
|
+
<p class="text-sm text-muted-foreground">
|
103
|
+
<strong>Note:</strong> The progress component automatically calculates the percentage based on the <code class="text-sm">value</code> and <code class="text-sm">max</code> props. The visual indicator will fill from left to right as the value increases.
|
104
|
+
</p>
|
105
|
+
</div>
|
106
|
+
<% end %>
|
107
|
+
<% end %>
|
@@ -0,0 +1,209 @@
|
|
1
|
+
<%= render Docs::ComponentPageComponent.new(title: "Radio Group") do |page| %>
|
2
|
+
<% page.with_header(name: "Radio Group", description: "A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.") %>
|
3
|
+
<% page.with_installation(component_name: "radio_group") %>
|
4
|
+
<% page.with_usage do %>
|
5
|
+
<%= render Docs::CodeBlockComponent.new(code: radio_group_usage_code, language: "erb") %>
|
6
|
+
<% end %>
|
7
|
+
<% page.with_examples do %>
|
8
|
+
<!-- Default Example -->
|
9
|
+
<% default_html = capture do %>
|
10
|
+
<%= render M9sh::RadioGroupComponent.new(name: "default") do |radio_group| %>
|
11
|
+
<% radio_group.with_item(value: "option1", label: "Option 1", checked: true) %>
|
12
|
+
<% radio_group.with_item(value: "option2", label: "Option 2") %>
|
13
|
+
<% radio_group.with_item(value: "option3", label: "Option 3") %>
|
14
|
+
<% end %>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
18
|
+
title: "Default",
|
19
|
+
preview_content: default_html,
|
20
|
+
code: radio_group_default_code,
|
21
|
+
ai_command: radio_group_default_code
|
22
|
+
) %>
|
23
|
+
|
24
|
+
<!-- With Labels Example -->
|
25
|
+
<% with_labels_html = capture do %>
|
26
|
+
<div class="space-y-4">
|
27
|
+
<%= render M9sh::LabelComponent.new do %>
|
28
|
+
Select your display preference
|
29
|
+
<% end %>
|
30
|
+
<%= render M9sh::RadioGroupComponent.new(name: "display") do |radio_group| %>
|
31
|
+
<% radio_group.with_item(value: "default", label: "Default", checked: true) %>
|
32
|
+
<% radio_group.with_item(value: "comfortable", label: "Comfortable") %>
|
33
|
+
<% radio_group.with_item(value: "compact", label: "Compact") %>
|
34
|
+
<% end %>
|
35
|
+
</div>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
39
|
+
title: "With Labels",
|
40
|
+
preview_content: with_labels_html,
|
41
|
+
code: radio_group_with_label_code,
|
42
|
+
ai_command: radio_group_with_label_code
|
43
|
+
) %>
|
44
|
+
|
45
|
+
<!-- With Description Example -->
|
46
|
+
<% with_description_html = capture do %>
|
47
|
+
<%= render M9sh::RadioGroupComponent.new(name: "plan") do |radio_group| %>
|
48
|
+
<% radio_group.with_item(
|
49
|
+
value: "free",
|
50
|
+
label: "Free",
|
51
|
+
description: "Perfect for personal use",
|
52
|
+
checked: true
|
53
|
+
) %>
|
54
|
+
<% radio_group.with_item(
|
55
|
+
value: "pro",
|
56
|
+
label: "Pro",
|
57
|
+
description: "Best for professionals"
|
58
|
+
) %>
|
59
|
+
<% radio_group.with_item(
|
60
|
+
value: "enterprise",
|
61
|
+
label: "Enterprise",
|
62
|
+
description: "For large organizations"
|
63
|
+
) %>
|
64
|
+
<% end %>
|
65
|
+
<% end %>
|
66
|
+
|
67
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
68
|
+
title: "With Description",
|
69
|
+
preview_content: with_description_html,
|
70
|
+
code: radio_group_with_description_code,
|
71
|
+
ai_command: radio_group_with_description_code
|
72
|
+
) %>
|
73
|
+
|
74
|
+
<!-- Disabled State Example -->
|
75
|
+
<% disabled_html = capture do %>
|
76
|
+
<%= render M9sh::RadioGroupComponent.new(name: "disabled-group") do |radio_group| %>
|
77
|
+
<% radio_group.with_item(value: "option1", label: "Available option", checked: true) %>
|
78
|
+
<% radio_group.with_item(value: "option2", label: "Disabled option", disabled: true) %>
|
79
|
+
<% radio_group.with_item(value: "option3", label: "Another available option") %>
|
80
|
+
<% end %>
|
81
|
+
<% end %>
|
82
|
+
|
83
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
84
|
+
title: "Disabled State",
|
85
|
+
preview_content: disabled_html,
|
86
|
+
code: radio_group_disabled_code,
|
87
|
+
ai_command: radio_group_disabled_code
|
88
|
+
) %>
|
89
|
+
|
90
|
+
<!-- Horizontal Layout Example -->
|
91
|
+
<% horizontal_html = capture do %>
|
92
|
+
<%= render M9sh::RadioGroupComponent.new(name: "horizontal", class: "flex flex-row space-x-4") do |radio_group| %>
|
93
|
+
<% radio_group.with_item(value: "yes", label: "Yes", checked: true) %>
|
94
|
+
<% radio_group.with_item(value: "no", label: "No") %>
|
95
|
+
<% radio_group.with_item(value: "maybe", label: "Maybe") %>
|
96
|
+
<% end %>
|
97
|
+
<% end %>
|
98
|
+
|
99
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
100
|
+
title: "Horizontal Layout",
|
101
|
+
preview_content: horizontal_html,
|
102
|
+
code: radio_group_horizontal_code,
|
103
|
+
ai_command: radio_group_horizontal_code
|
104
|
+
) %>
|
105
|
+
|
106
|
+
<!-- Form Integration Example -->
|
107
|
+
<% form_html = capture do %>
|
108
|
+
<div class="border rounded-lg p-6 max-w-md">
|
109
|
+
<h3 class="text-lg font-semibold mb-4">Notification Settings</h3>
|
110
|
+
<div class="space-y-6">
|
111
|
+
<div class="space-y-3">
|
112
|
+
<div class="font-medium">Email frequency</div>
|
113
|
+
<%= render M9sh::RadioGroupComponent.new(name: "email_frequency") do |radio_group| %>
|
114
|
+
<% radio_group.with_item(
|
115
|
+
value: "realtime",
|
116
|
+
label: "Realtime",
|
117
|
+
description: "Get notified immediately",
|
118
|
+
checked: true
|
119
|
+
) %>
|
120
|
+
<% radio_group.with_item(
|
121
|
+
value: "daily",
|
122
|
+
label: "Daily digest",
|
123
|
+
description: "Once per day at 9 AM"
|
124
|
+
) %>
|
125
|
+
<% radio_group.with_item(
|
126
|
+
value: "weekly",
|
127
|
+
label: "Weekly digest",
|
128
|
+
description: "Every Monday morning"
|
129
|
+
) %>
|
130
|
+
<% radio_group.with_item(
|
131
|
+
value: "never",
|
132
|
+
label: "Never",
|
133
|
+
description: "Disable email notifications"
|
134
|
+
) %>
|
135
|
+
<% end %>
|
136
|
+
</div>
|
137
|
+
</div>
|
138
|
+
</div>
|
139
|
+
<% end %>
|
140
|
+
|
141
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
142
|
+
title: "Form Integration",
|
143
|
+
preview_content: form_html,
|
144
|
+
code: radio_group_form_integration_code,
|
145
|
+
ai_command: radio_group_form_integration_code
|
146
|
+
) %>
|
147
|
+
<% end %>
|
148
|
+
<% page.with_api do %>
|
149
|
+
<h3 class="text-lg font-semibold">RadioGroupComponent</h3>
|
150
|
+
<%= render Docs::PropTableComponent.new(
|
151
|
+
props: [
|
152
|
+
{
|
153
|
+
name: "name",
|
154
|
+
type: "String",
|
155
|
+
default: "nil",
|
156
|
+
description: "The name attribute for the radio group (required for form submissions)"
|
157
|
+
}
|
158
|
+
]
|
159
|
+
) %>
|
160
|
+
|
161
|
+
<h3 class="text-lg font-semibold mt-6">ItemComponent (via with_item)</h3>
|
162
|
+
<%= render Docs::PropTableComponent.new(
|
163
|
+
props: [
|
164
|
+
{
|
165
|
+
name: "value",
|
166
|
+
type: "String",
|
167
|
+
default: "nil",
|
168
|
+
description: "The value for this radio button (required)"
|
169
|
+
},
|
170
|
+
{
|
171
|
+
name: "label",
|
172
|
+
type: "String",
|
173
|
+
default: "nil",
|
174
|
+
description: "The label text for this radio button (required)"
|
175
|
+
},
|
176
|
+
{
|
177
|
+
name: "description",
|
178
|
+
type: "String",
|
179
|
+
default: "nil",
|
180
|
+
description: "Optional description text shown below the label"
|
181
|
+
},
|
182
|
+
{
|
183
|
+
name: "checked",
|
184
|
+
type: "Boolean",
|
185
|
+
default: "false",
|
186
|
+
description: "Whether this radio button is checked by default"
|
187
|
+
},
|
188
|
+
{
|
189
|
+
name: "disabled",
|
190
|
+
type: "Boolean",
|
191
|
+
default: "false",
|
192
|
+
description: "Whether this radio button is disabled"
|
193
|
+
}
|
194
|
+
]
|
195
|
+
) %>
|
196
|
+
|
197
|
+
<div class="mt-6 space-y-2">
|
198
|
+
<h3 class="text-lg font-semibold">Additional Notes</h3>
|
199
|
+
<ul class="list-disc list-inside space-y-2 text-sm text-muted-foreground">
|
200
|
+
<li>The radio group component uses a Stimulus controller (m9sh--radio) for state management</li>
|
201
|
+
<li>Only one radio button in a group can be selected at a time</li>
|
202
|
+
<li>The component automatically manages ARIA attributes for accessibility</li>
|
203
|
+
<li>By default, radio buttons are displayed vertically. Use the class parameter with flex utilities for horizontal layout</li>
|
204
|
+
<li>All additional HTML attributes can be passed via extra_attrs and will be applied to the container element</li>
|
205
|
+
<li>The radio group follows the WAI-ARIA design pattern for radio groups</li>
|
206
|
+
</ul>
|
207
|
+
</div>
|
208
|
+
<% end %>
|
209
|
+
<% end %>
|