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,227 @@
|
|
1
|
+
<%= render Docs::ComponentPageComponent.new(title: "Toaster - m9sh Components") do |page| %>
|
2
|
+
<% page.with_header(
|
3
|
+
name: "Toaster",
|
4
|
+
description: "A container component that manages and displays toast notifications dynamically."
|
5
|
+
) %>
|
6
|
+
|
7
|
+
<% page.with_installation(component_name: "toaster") %>
|
8
|
+
|
9
|
+
<% page.with_usage do %>
|
10
|
+
<p class="text-sm text-muted-foreground">
|
11
|
+
Add the Toaster component to your application layout (typically in <code class="text-xs bg-muted px-1 py-0.5 rounded">app/views/layouts/application.html.erb</code>):
|
12
|
+
</p>
|
13
|
+
|
14
|
+
<%= render Docs::CodeBlockComponent.new(
|
15
|
+
code: toaster_usage_code,
|
16
|
+
language: "erb"
|
17
|
+
) %>
|
18
|
+
|
19
|
+
<p class="text-sm text-muted-foreground mt-4">
|
20
|
+
Then trigger toasts from anywhere in your JavaScript code using custom events:
|
21
|
+
</p>
|
22
|
+
|
23
|
+
<%= render Docs::CodeBlockComponent.new(
|
24
|
+
code: toaster_javascript_code,
|
25
|
+
language: "javascript"
|
26
|
+
) %>
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
<% page.with_examples do %>
|
30
|
+
<!-- Default Setup Example -->
|
31
|
+
<% default_setup_html = capture do %>
|
32
|
+
<div class="space-y-4">
|
33
|
+
<p class="text-sm text-muted-foreground">
|
34
|
+
Add the Toaster component to your layout file:
|
35
|
+
</p>
|
36
|
+
<%= render Docs::CodeBlockComponent.new(
|
37
|
+
code: toaster_usage_code,
|
38
|
+
language: "erb"
|
39
|
+
) %>
|
40
|
+
</div>
|
41
|
+
<% end %>
|
42
|
+
|
43
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
44
|
+
title: "Default Setup",
|
45
|
+
preview_content: default_setup_html,
|
46
|
+
code: nil,
|
47
|
+
ai_command: nil
|
48
|
+
) %>
|
49
|
+
|
50
|
+
<!-- Triggering Toasts Example -->
|
51
|
+
<% trigger_toasts_html = capture do %>
|
52
|
+
<div class="space-y-4">
|
53
|
+
<%= render M9sh::ButtonComponent.new(
|
54
|
+
onclick: "window.dispatchEvent(new CustomEvent('hotcdn:toast', { detail: { title: 'Success', description: 'Operation completed successfully.', variant: 'success' } }))"
|
55
|
+
) do %>
|
56
|
+
Show Success Toast
|
57
|
+
<% end %>
|
58
|
+
|
59
|
+
<%= render M9sh::ButtonComponent.new(
|
60
|
+
variant: :destructive,
|
61
|
+
onclick: "window.dispatchEvent(new CustomEvent('hotcdn:toast', { detail: { title: 'Error', description: 'Something went wrong.', variant: 'error' } }))"
|
62
|
+
) do %>
|
63
|
+
Show Error Toast
|
64
|
+
<% end %>
|
65
|
+
|
66
|
+
<%= render M9sh::ButtonComponent.new(
|
67
|
+
variant: :outline,
|
68
|
+
onclick: "window.dispatchEvent(new CustomEvent('hotcdn:toast', { detail: { title: 'Warning', description: 'Please review your changes.', variant: 'warning' } }))"
|
69
|
+
) do %>
|
70
|
+
Show Warning Toast
|
71
|
+
<% end %>
|
72
|
+
|
73
|
+
<%= render M9sh::ButtonComponent.new(
|
74
|
+
variant: :secondary,
|
75
|
+
onclick: "window.dispatchEvent(new CustomEvent('hotcdn:toast', { detail: { title: 'Information', description: 'Here is some useful information.', variant: 'info' } }))"
|
76
|
+
) do %>
|
77
|
+
Show Info Toast
|
78
|
+
<% end %>
|
79
|
+
</div>
|
80
|
+
<% end %>
|
81
|
+
|
82
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
83
|
+
title: "Triggering Toasts",
|
84
|
+
preview_content: trigger_toasts_html,
|
85
|
+
code: toaster_example_code,
|
86
|
+
ai_command: toaster_example_code
|
87
|
+
) %>
|
88
|
+
|
89
|
+
<!-- JavaScript Integration Example -->
|
90
|
+
<% js_integration_html = capture do %>
|
91
|
+
<div class="space-y-4">
|
92
|
+
<h4 class="text-sm font-semibold">From Stimulus Controllers</h4>
|
93
|
+
<%= render Docs::CodeBlockComponent.new(
|
94
|
+
code: <<~JAVASCRIPT,
|
95
|
+
// In your Stimulus controller
|
96
|
+
export default class extends Controller {
|
97
|
+
showToast() {
|
98
|
+
window.dispatchEvent(new CustomEvent('hotcdn:toast', {
|
99
|
+
detail: {
|
100
|
+
title: 'Success',
|
101
|
+
description: 'Your changes have been saved.',
|
102
|
+
variant: 'success',
|
103
|
+
duration: 5000
|
104
|
+
}
|
105
|
+
}));
|
106
|
+
}
|
107
|
+
}
|
108
|
+
JAVASCRIPT
|
109
|
+
language: "javascript"
|
110
|
+
) %>
|
111
|
+
|
112
|
+
<h4 class="text-sm font-semibold mt-4">From Server Responses (Turbo Streams)</h4>
|
113
|
+
<%= render Docs::CodeBlockComponent.new(
|
114
|
+
code: toaster_turbo_stream_code,
|
115
|
+
language: "erb"
|
116
|
+
) %>
|
117
|
+
</div>
|
118
|
+
<% end %>
|
119
|
+
|
120
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
121
|
+
title: "JavaScript Integration",
|
122
|
+
preview_content: js_integration_html,
|
123
|
+
code: nil,
|
124
|
+
ai_command: nil
|
125
|
+
) %>
|
126
|
+
|
127
|
+
<!-- Configuration Options Example -->
|
128
|
+
<% config_options_html = capture do %>
|
129
|
+
<div class="space-y-4">
|
130
|
+
<p class="text-sm text-muted-foreground">
|
131
|
+
The toaster listens for the <code class="text-xs bg-muted px-1 py-0.5 rounded">hotcdn:toast</code> custom event with the following options:
|
132
|
+
</p>
|
133
|
+
|
134
|
+
<%= render Docs::PropTableComponent.new(
|
135
|
+
props: [
|
136
|
+
{
|
137
|
+
name: "title",
|
138
|
+
type: "String",
|
139
|
+
default: nil,
|
140
|
+
description: "The title of the toast (required)"
|
141
|
+
},
|
142
|
+
{
|
143
|
+
name: "description",
|
144
|
+
type: "String",
|
145
|
+
default: "null",
|
146
|
+
description: "Optional description text"
|
147
|
+
},
|
148
|
+
{
|
149
|
+
name: "variant",
|
150
|
+
type: "String",
|
151
|
+
default: "'default'",
|
152
|
+
description: "Visual variant: 'default', 'success', 'error', 'warning', 'info'"
|
153
|
+
},
|
154
|
+
{
|
155
|
+
name: "duration",
|
156
|
+
type: "Number",
|
157
|
+
default: "5000",
|
158
|
+
description: "Duration in milliseconds before auto-dismiss"
|
159
|
+
}
|
160
|
+
]
|
161
|
+
) %>
|
162
|
+
</div>
|
163
|
+
<% end %>
|
164
|
+
|
165
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
166
|
+
title: "Configuration Options",
|
167
|
+
preview_content: config_options_html,
|
168
|
+
code: nil,
|
169
|
+
ai_command: nil
|
170
|
+
) %>
|
171
|
+
<% end %>
|
172
|
+
|
173
|
+
<% page.with_api do %>
|
174
|
+
<h3 class="text-lg font-semibold">Toaster Component</h3>
|
175
|
+
<p class="text-sm text-muted-foreground">
|
176
|
+
The Toaster component is a container that manages toast notifications. It doesn't accept any props and should be added once to your layout.
|
177
|
+
</p>
|
178
|
+
|
179
|
+
<div class="mt-6 space-y-2">
|
180
|
+
<h3 class="text-lg font-semibold">Event API</h3>
|
181
|
+
<p class="text-sm text-muted-foreground mb-4">
|
182
|
+
Toasts are triggered by dispatching a custom event on the window object:
|
183
|
+
</p>
|
184
|
+
|
185
|
+
<%= render Docs::CodeBlockComponent.new(
|
186
|
+
code: <<~JAVASCRIPT,
|
187
|
+
window.dispatchEvent(new CustomEvent('hotcdn:toast', {
|
188
|
+
detail: {
|
189
|
+
title: 'Title', // Required: The toast title
|
190
|
+
description: 'Message', // Optional: The toast description
|
191
|
+
variant: 'success', // Optional: Visual variant (default, success, error, warning, info)
|
192
|
+
duration: 5000 // Optional: Auto-dismiss duration in ms (default: 5000)
|
193
|
+
}
|
194
|
+
}));
|
195
|
+
JAVASCRIPT
|
196
|
+
language: "javascript"
|
197
|
+
) %>
|
198
|
+
</div>
|
199
|
+
|
200
|
+
<div class="mt-6 space-y-2">
|
201
|
+
<h3 class="text-lg font-semibold">Notes</h3>
|
202
|
+
<ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
|
203
|
+
<li>The Toaster should be added to your application layout once, typically near the end of the body tag</li>
|
204
|
+
<li>Multiple toasts can be displayed simultaneously and are stacked vertically</li>
|
205
|
+
<li>Toasts are positioned at the bottom-right on mobile and can be configured via CSS classes</li>
|
206
|
+
<li>Each toast automatically dismisses after its duration expires (default 5 seconds)</li>
|
207
|
+
<li>Users can manually dismiss toasts by clicking the close button</li>
|
208
|
+
<li>The component uses Stimulus controllers to manage toast creation and removal</li>
|
209
|
+
<li>Toasts include smooth slide-in and slide-out animations</li>
|
210
|
+
<li>The container is accessible with proper ARIA live regions for screen readers</li>
|
211
|
+
</ul>
|
212
|
+
</div>
|
213
|
+
|
214
|
+
<div class="mt-6 space-y-2">
|
215
|
+
<h3 class="text-lg font-semibold">Best Practices</h3>
|
216
|
+
<ul class="list-disc list-inside text-sm text-muted-foreground space-y-1">
|
217
|
+
<li>Use success toasts for confirmations of user actions</li>
|
218
|
+
<li>Use error toasts for operations that failed and need user attention</li>
|
219
|
+
<li>Use warning toasts for important information that doesn't block the user</li>
|
220
|
+
<li>Use info toasts for general informational messages</li>
|
221
|
+
<li>Keep toast messages concise - ideally under two lines</li>
|
222
|
+
<li>Avoid showing too many toasts at once to prevent overwhelming the user</li>
|
223
|
+
<li>For critical errors that require action, consider using a modal or alert dialog instead</li>
|
224
|
+
</ul>
|
225
|
+
</div>
|
226
|
+
<% end %>
|
227
|
+
<% end %>
|
@@ -0,0 +1,154 @@
|
|
1
|
+
<%= render Docs::ComponentPageComponent.new(title: "Toggle") do |page| %>
|
2
|
+
<% page.with_header(
|
3
|
+
name: "Toggle",
|
4
|
+
description: "A two-state button that can be either on or off."
|
5
|
+
) %>
|
6
|
+
|
7
|
+
<% page.with_installation(component_name: "toggle") %>
|
8
|
+
|
9
|
+
<% page.with_usage do %>
|
10
|
+
<%= render Docs::CodeBlockComponent.new(
|
11
|
+
code: toggle_usage_code,
|
12
|
+
language: "erb"
|
13
|
+
) %>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<% page.with_examples do %>
|
17
|
+
<!-- Default Example -->
|
18
|
+
<% toggle = M9sh::ToggleComponent.new %>
|
19
|
+
<% toggle_html = capture do %>
|
20
|
+
<%= render toggle do %>
|
21
|
+
Toggle
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
24
|
+
|
25
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
26
|
+
title: "Default",
|
27
|
+
preview_content: toggle_html,
|
28
|
+
code: toggle_default_code,
|
29
|
+
ai_command: toggle_default_code
|
30
|
+
) %>
|
31
|
+
|
32
|
+
<!-- With Text Example -->
|
33
|
+
<% with_text_html = capture do %>
|
34
|
+
<%= render M9sh::ToggleComponent.new do %>
|
35
|
+
Toggle with text
|
36
|
+
<% end %>
|
37
|
+
<% end %>
|
38
|
+
|
39
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
40
|
+
title: "With Text",
|
41
|
+
preview_content: with_text_html,
|
42
|
+
code: toggle_with_text_code,
|
43
|
+
ai_command: toggle_with_text_code
|
44
|
+
) %>
|
45
|
+
|
46
|
+
<!-- With Icon Example -->
|
47
|
+
<% with_icon_html = capture do %>
|
48
|
+
<%= render M9sh::ToggleComponent.new do %>
|
49
|
+
<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">
|
50
|
+
<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/>
|
51
|
+
<path d="m15 5 4 4"/>
|
52
|
+
</svg>
|
53
|
+
<% end %>
|
54
|
+
<% end %>
|
55
|
+
|
56
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
57
|
+
title: "With Icon",
|
58
|
+
preview_content: with_icon_html,
|
59
|
+
code: toggle_with_icon_code,
|
60
|
+
ai_command: toggle_with_icon_code
|
61
|
+
) %>
|
62
|
+
|
63
|
+
<!-- Disabled Example -->
|
64
|
+
<% disabled_html = capture do %>
|
65
|
+
<div class="flex gap-2">
|
66
|
+
<%= render M9sh::ToggleComponent.new(disabled: true) do %>
|
67
|
+
Disabled
|
68
|
+
<% end %>
|
69
|
+
<%= render M9sh::ToggleComponent.new(pressed: true, disabled: true) do %>
|
70
|
+
Disabled Pressed
|
71
|
+
<% end %>
|
72
|
+
</div>
|
73
|
+
<% end %>
|
74
|
+
|
75
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
76
|
+
title: "Disabled",
|
77
|
+
preview_content: disabled_html,
|
78
|
+
code: toggle_disabled_code,
|
79
|
+
ai_command: toggle_disabled_code
|
80
|
+
) %>
|
81
|
+
|
82
|
+
<!-- Variants Example -->
|
83
|
+
<% variants_html = capture do %>
|
84
|
+
<div class="flex gap-2">
|
85
|
+
<%= render M9sh::ToggleComponent.new(variant: :default) do %>
|
86
|
+
Default
|
87
|
+
<% end %>
|
88
|
+
<%= render M9sh::ToggleComponent.new(variant: :outline) do %>
|
89
|
+
Outline
|
90
|
+
<% end %>
|
91
|
+
</div>
|
92
|
+
<% end %>
|
93
|
+
|
94
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
95
|
+
title: "Variants",
|
96
|
+
preview_content: variants_html,
|
97
|
+
code: toggle_variants_code,
|
98
|
+
ai_command: toggle_variants_code
|
99
|
+
) %>
|
100
|
+
|
101
|
+
<!-- Sizes Example -->
|
102
|
+
<% sizes_html = capture do %>
|
103
|
+
<div class="flex items-center gap-2">
|
104
|
+
<%= render M9sh::ToggleComponent.new(size: :sm) do %>
|
105
|
+
Small
|
106
|
+
<% end %>
|
107
|
+
<%= render M9sh::ToggleComponent.new(size: :default) do %>
|
108
|
+
Default
|
109
|
+
<% end %>
|
110
|
+
<%= render M9sh::ToggleComponent.new(size: :lg) do %>
|
111
|
+
Large
|
112
|
+
<% end %>
|
113
|
+
</div>
|
114
|
+
<% end %>
|
115
|
+
|
116
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
117
|
+
title: "Sizes",
|
118
|
+
preview_content: sizes_html,
|
119
|
+
code: toggle_sizes_code,
|
120
|
+
ai_command: toggle_sizes_code
|
121
|
+
) %>
|
122
|
+
<% end %>
|
123
|
+
|
124
|
+
<% page.with_api do %>
|
125
|
+
<%= render Docs::PropTableComponent.new(
|
126
|
+
props: [
|
127
|
+
{
|
128
|
+
name: "variant",
|
129
|
+
type: "Symbol",
|
130
|
+
default: ":default",
|
131
|
+
description: "The visual style variant. Options: :default, :outline"
|
132
|
+
},
|
133
|
+
{
|
134
|
+
name: "size",
|
135
|
+
type: "Symbol",
|
136
|
+
default: ":default",
|
137
|
+
description: "The size of the toggle. Options: :sm, :default, :lg"
|
138
|
+
},
|
139
|
+
{
|
140
|
+
name: "pressed",
|
141
|
+
type: "Boolean",
|
142
|
+
default: "false",
|
143
|
+
description: "Whether the toggle is in the pressed (on) state"
|
144
|
+
},
|
145
|
+
{
|
146
|
+
name: "disabled",
|
147
|
+
type: "Boolean",
|
148
|
+
default: "false",
|
149
|
+
description: "Whether the toggle is disabled"
|
150
|
+
}
|
151
|
+
]
|
152
|
+
) %>
|
153
|
+
<% end %>
|
154
|
+
<% end %>
|
@@ -0,0 +1,216 @@
|
|
1
|
+
<%= render Docs::ComponentPageComponent.new(title: "Tooltip") do |page| %>
|
2
|
+
<% page.with_header(
|
3
|
+
name: "Tooltip",
|
4
|
+
description: "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it."
|
5
|
+
) %>
|
6
|
+
|
7
|
+
<% page.with_installation(component_name: "tooltip") %>
|
8
|
+
|
9
|
+
<% page.with_usage do %>
|
10
|
+
<%= render Docs::CodeBlockComponent.new(
|
11
|
+
code: tooltip_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="flex justify-center items-center p-8">
|
20
|
+
<%= render M9sh::TooltipComponent.new do |tooltip| %>
|
21
|
+
<% tooltip.with_trigger do %>
|
22
|
+
<button class="px-4 py-2 rounded-md border bg-background hover:bg-muted transition-colors">
|
23
|
+
Hover me
|
24
|
+
</button>
|
25
|
+
<% end %>
|
26
|
+
<% tooltip.with_tooltip_content do %>
|
27
|
+
This is a tooltip
|
28
|
+
<% end %>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
<% end %>
|
32
|
+
|
33
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
34
|
+
title: "Default",
|
35
|
+
preview_content: default_html,
|
36
|
+
code: tooltip_default_code,
|
37
|
+
ai_command: tooltip_default_code
|
38
|
+
) %>
|
39
|
+
|
40
|
+
<!-- Different Positions Example -->
|
41
|
+
<% positions_html = capture do %>
|
42
|
+
<div class="flex justify-center items-center gap-8 p-16">
|
43
|
+
<%= render M9sh::TooltipComponent.new(side: "top") do |tooltip| %>
|
44
|
+
<% tooltip.with_trigger do %>
|
45
|
+
<button class="px-4 py-2 rounded-md border bg-background hover:bg-muted transition-colors">
|
46
|
+
Top
|
47
|
+
</button>
|
48
|
+
<% end %>
|
49
|
+
<% tooltip.with_tooltip_content do %>
|
50
|
+
Tooltip on top
|
51
|
+
<% end %>
|
52
|
+
<% end %>
|
53
|
+
|
54
|
+
<%= render M9sh::TooltipComponent.new(side: "right") do |tooltip| %>
|
55
|
+
<% tooltip.with_trigger do %>
|
56
|
+
<button class="px-4 py-2 rounded-md border bg-background hover:bg-muted transition-colors">
|
57
|
+
Right
|
58
|
+
</button>
|
59
|
+
<% end %>
|
60
|
+
<% tooltip.with_tooltip_content do %>
|
61
|
+
Tooltip on right
|
62
|
+
<% end %>
|
63
|
+
<% end %>
|
64
|
+
|
65
|
+
<%= render M9sh::TooltipComponent.new(side: "bottom") do |tooltip| %>
|
66
|
+
<% tooltip.with_trigger do %>
|
67
|
+
<button class="px-4 py-2 rounded-md border bg-background hover:bg-muted transition-colors">
|
68
|
+
Bottom
|
69
|
+
</button>
|
70
|
+
<% end %>
|
71
|
+
<% tooltip.with_tooltip_content do %>
|
72
|
+
Tooltip on bottom
|
73
|
+
<% end %>
|
74
|
+
<% end %>
|
75
|
+
|
76
|
+
<%= render M9sh::TooltipComponent.new(side: "left") do |tooltip| %>
|
77
|
+
<% tooltip.with_trigger do %>
|
78
|
+
<button class="px-4 py-2 rounded-md border bg-background hover:bg-muted transition-colors">
|
79
|
+
Left
|
80
|
+
</button>
|
81
|
+
<% end %>
|
82
|
+
<% tooltip.with_tooltip_content do %>
|
83
|
+
Tooltip on left
|
84
|
+
<% end %>
|
85
|
+
<% end %>
|
86
|
+
</div>
|
87
|
+
<% end %>
|
88
|
+
|
89
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
90
|
+
title: "Different Positions",
|
91
|
+
preview_content: positions_html,
|
92
|
+
code: tooltip_positions_code,
|
93
|
+
ai_command: tooltip_positions_code
|
94
|
+
) %>
|
95
|
+
|
96
|
+
<!-- Rich Content Example -->
|
97
|
+
<% rich_content_html = capture do %>
|
98
|
+
<div class="flex justify-center items-center p-8">
|
99
|
+
<%= render M9sh::TooltipComponent.new do |tooltip| %>
|
100
|
+
<% tooltip.with_trigger do %>
|
101
|
+
<button class="px-4 py-2 rounded-md border bg-background hover:bg-muted transition-colors">
|
102
|
+
Hover for info
|
103
|
+
</button>
|
104
|
+
<% end %>
|
105
|
+
<% tooltip.with_tooltip_content do %>
|
106
|
+
<div class="space-y-1">
|
107
|
+
<p class="font-semibold">Pro Tip</p>
|
108
|
+
<p class="text-xs">You can use rich content in tooltips</p>
|
109
|
+
</div>
|
110
|
+
<% end %>
|
111
|
+
<% end %>
|
112
|
+
</div>
|
113
|
+
<% end %>
|
114
|
+
|
115
|
+
<%= render Docs::ComponentPreviewComponent.new(
|
116
|
+
title: "With Rich Content",
|
117
|
+
preview_content: rich_content_html,
|
118
|
+
code: tooltip_rich_content_code,
|
119
|
+
ai_command: tooltip_rich_content_code
|
120
|
+
) %>
|
121
|
+
<% end %>
|
122
|
+
|
123
|
+
<% page.with_api do %>
|
124
|
+
<h3 class="text-lg font-semibold">TooltipComponent</h3>
|
125
|
+
|
126
|
+
<%= render Docs::PropTableComponent.new(
|
127
|
+
props: [
|
128
|
+
{
|
129
|
+
name: "side",
|
130
|
+
type: "String",
|
131
|
+
default: '"top"',
|
132
|
+
description: "The preferred side of the trigger to render against when open. Options: 'top', 'right', 'bottom', 'left'"
|
133
|
+
},
|
134
|
+
{
|
135
|
+
name: "delay",
|
136
|
+
type: "Number",
|
137
|
+
default: "200",
|
138
|
+
description: "The duration in milliseconds before the tooltip appears"
|
139
|
+
}
|
140
|
+
]
|
141
|
+
) %>
|
142
|
+
|
143
|
+
<h3 class="text-lg font-semibold mt-6">Slots</h3>
|
144
|
+
|
145
|
+
<%= render Docs::PropTableComponent.new(
|
146
|
+
props: [
|
147
|
+
{
|
148
|
+
name: "trigger",
|
149
|
+
type: "Slot",
|
150
|
+
default: "-",
|
151
|
+
description: "The element that triggers the tooltip on hover. This should be a focusable element."
|
152
|
+
},
|
153
|
+
{
|
154
|
+
name: "tooltip_content",
|
155
|
+
type: "Slot",
|
156
|
+
default: "-",
|
157
|
+
description: "The content to display in the tooltip"
|
158
|
+
}
|
159
|
+
]
|
160
|
+
) %>
|
161
|
+
<% end %>
|
162
|
+
<% end %>
|
163
|
+
|
164
|
+
<!-- Accessibility -->
|
165
|
+
<div class="space-y-4">
|
166
|
+
<h2 class="text-2xl font-semibold border-b pb-2">Accessibility</h2>
|
167
|
+
|
168
|
+
<div class="space-y-3 text-sm">
|
169
|
+
<p>
|
170
|
+
The Tooltip component uses Stimulus to manage visibility and positioning:
|
171
|
+
</p>
|
172
|
+
|
173
|
+
<ul class="list-disc list-inside space-y-2 text-muted-foreground">
|
174
|
+
<li>
|
175
|
+
The tooltip appears after a configurable delay when hovering over the trigger element
|
176
|
+
</li>
|
177
|
+
<li>
|
178
|
+
The tooltip uses the <code class="text-xs bg-muted px-1 py-0.5 rounded">role="tooltip"</code> attribute for proper screen reader support
|
179
|
+
</li>
|
180
|
+
<li>
|
181
|
+
Tooltips should provide supplementary information, not critical content that can't be accessed otherwise
|
182
|
+
</li>
|
183
|
+
<li>
|
184
|
+
The tooltip is positioned dynamically based on the <code class="text-xs bg-muted px-1 py-0.5 rounded">side</code> parameter
|
185
|
+
</li>
|
186
|
+
<li>
|
187
|
+
Smooth animations provide visual feedback when showing and hiding
|
188
|
+
</li>
|
189
|
+
</ul>
|
190
|
+
</div>
|
191
|
+
</div>
|
192
|
+
|
193
|
+
<!-- Notes -->
|
194
|
+
<div class="space-y-4">
|
195
|
+
<h2 class="text-2xl font-semibold border-b pb-2">Notes</h2>
|
196
|
+
|
197
|
+
<div class="space-y-3 text-sm">
|
198
|
+
<ul class="list-disc list-inside space-y-2 text-muted-foreground">
|
199
|
+
<li>
|
200
|
+
Tooltips are triggered on <code class="text-xs bg-muted px-1 py-0.5 rounded">mouseenter</code> and hidden on <code class="text-xs bg-muted px-1 py-0.5 rounded">mouseleave</code>
|
201
|
+
</li>
|
202
|
+
<li>
|
203
|
+
Use the <code class="text-xs bg-muted px-1 py-0.5 rounded">delay</code> parameter to control how quickly the tooltip appears
|
204
|
+
</li>
|
205
|
+
<li>
|
206
|
+
Tooltips are best used for short, informative text. For longer content, consider using a HoverCard or Popover instead
|
207
|
+
</li>
|
208
|
+
<li>
|
209
|
+
The tooltip content is styled with a dark background and light text by default
|
210
|
+
</li>
|
211
|
+
<li>
|
212
|
+
Multiple tooltips can exist on the same page independently
|
213
|
+
</li>
|
214
|
+
</ul>
|
215
|
+
</div>
|
216
|
+
</div>
|