maquina-components 0.1.1 → 0.2.0
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/README.md +410 -13
- data/app/assets/images/maquina.svg +1 -0
- data/app/assets/stylesheets/alert.css +143 -0
- data/app/assets/stylesheets/badge.css +145 -0
- data/app/assets/stylesheets/breadcrumbs.css +163 -0
- data/app/assets/stylesheets/card.css +128 -0
- data/app/assets/stylesheets/dropdown_menu.css +248 -0
- data/app/assets/stylesheets/empty.css +133 -0
- data/app/assets/stylesheets/form.css +617 -0
- data/app/assets/stylesheets/header.css +61 -0
- data/app/assets/stylesheets/maquina_components.css +178 -0
- data/app/assets/stylesheets/pagination.css +154 -0
- data/app/assets/stylesheets/sidebar.css +477 -0
- data/app/assets/stylesheets/table.css +205 -0
- data/app/assets/stylesheets/toggle_group.css +151 -0
- data/app/assets/tailwind/maquina_components_engine/engine.css +16 -0
- data/app/helpers/maquina_components/breadcrumbs_helper.rb +118 -0
- data/app/helpers/maquina_components/dropdown_menu_helper.rb +249 -0
- data/app/helpers/maquina_components/empty_helper.rb +102 -0
- data/app/helpers/maquina_components/icons_helper.rb +161 -0
- data/app/helpers/maquina_components/pagination_helper.rb +153 -0
- data/app/helpers/maquina_components/sidebar_helper.rb +63 -0
- data/app/helpers/maquina_components/table_helper.rb +144 -0
- data/app/helpers/maquina_components/toggle_group_helper.rb +172 -0
- data/app/javascript/controllers/breadcrumb_controller.js +71 -0
- data/app/javascript/controllers/dropdown_menu_controller.js +203 -0
- data/app/javascript/controllers/menu_button_controller.js +59 -0
- data/app/javascript/controllers/sidebar_controller.js +316 -0
- data/app/javascript/controllers/sidebar_trigger_controller.js +32 -0
- data/app/javascript/controllers/toggle_group_controller.js +178 -0
- data/app/views/components/_alert.html.erb +12 -0
- data/app/views/components/_badge.html.erb +10 -0
- data/app/views/components/_breadcrumbs.html.erb +16 -0
- data/app/views/components/_card.html.erb +6 -0
- data/app/views/components/_dropdown.html.erb +25 -0
- data/app/views/components/_dropdown_menu.html.erb +9 -0
- data/app/views/components/_empty.html.erb +10 -0
- data/app/views/components/_header.html.erb +8 -0
- data/app/views/components/_menu_button.html.erb +44 -0
- data/app/views/components/_pagination.html.erb +13 -0
- data/app/views/components/_separator.html.erb +11 -0
- data/app/views/components/_sidebar.html.erb +40 -0
- data/app/views/components/_simple_table.html.erb +49 -0
- data/app/views/components/_table.html.erb +21 -0
- data/app/views/components/_toggle_group.html.erb +24 -0
- data/app/views/components/alert/_description.html.erb +6 -0
- data/app/views/components/alert/_title.html.erb +6 -0
- data/app/views/components/breadcrumbs/_ellipsis.html.erb +9 -0
- data/app/views/components/breadcrumbs/_item.html.erb +8 -0
- data/app/views/components/breadcrumbs/_link.html.erb +8 -0
- data/app/views/components/breadcrumbs/_list.html.erb +8 -0
- data/app/views/components/breadcrumbs/_page.html.erb +8 -0
- data/app/views/components/breadcrumbs/_separator.html.erb +17 -0
- data/app/views/components/card/_action.html.erb +6 -0
- data/app/views/components/card/_content.html.erb +9 -0
- data/app/views/components/card/_description.html.erb +6 -0
- data/app/views/components/card/_footer.html.erb +17 -0
- data/app/views/components/card/_header.html.erb +9 -0
- data/app/views/components/card/_title.html.erb +9 -0
- data/app/views/components/dropdown_menu/_content.html.erb +20 -0
- data/app/views/components/dropdown_menu/_group.html.erb +12 -0
- data/app/views/components/dropdown_menu/_item.html.erb +29 -0
- data/app/views/components/dropdown_menu/_label.html.erb +13 -0
- data/app/views/components/dropdown_menu/_separator.html.erb +11 -0
- data/app/views/components/dropdown_menu/_shortcut.html.erb +12 -0
- data/app/views/components/dropdown_menu/_trigger.html.erb +24 -0
- data/app/views/components/empty/_content.html.erb +8 -0
- data/app/views/components/empty/_description.html.erb +12 -0
- data/app/views/components/empty/_header.html.erb +8 -0
- data/app/views/components/empty/_media.html.erb +13 -0
- data/app/views/components/empty/_title.html.erb +12 -0
- data/app/views/components/pagination/_content.html.erb +8 -0
- data/app/views/components/pagination/_ellipsis.html.erb +28 -0
- data/app/views/components/pagination/_item.html.erb +8 -0
- data/app/views/components/pagination/_link.html.erb +23 -0
- data/app/views/components/pagination/_next.html.erb +57 -0
- data/app/views/components/pagination/_previous.html.erb +57 -0
- data/app/views/components/sidebar/_content.html.erb +8 -0
- data/app/views/components/sidebar/_footer.html.erb +8 -0
- data/app/views/components/sidebar/_group.html.erb +12 -0
- data/app/views/components/sidebar/_header.html.erb +8 -0
- data/app/views/components/sidebar/_inset.html.erb +8 -0
- data/app/views/components/sidebar/_menu.html.erb +8 -0
- data/app/views/components/sidebar/_menu_button.html.erb +14 -0
- data/app/views/components/sidebar/_menu_item.html.erb +7 -0
- data/app/views/components/sidebar/_menu_link.html.erb +32 -0
- data/app/views/components/sidebar/_provider.html.erb +16 -0
- data/app/views/components/sidebar/_trigger.html.erb +12 -0
- data/app/views/components/stats/_stats_card.html.erb +100 -0
- data/app/views/components/stats/_stats_grid.html.erb +38 -0
- data/app/views/components/table/_body.html.erb +5 -0
- data/app/views/components/table/_caption.html.erb +5 -0
- data/app/views/components/table/_cell.html.erb +5 -0
- data/app/views/components/table/_footer.html.erb +5 -0
- data/app/views/components/table/_head.html.erb +8 -0
- data/app/views/components/table/_header.html.erb +8 -0
- data/app/views/components/table/_row.html.erb +8 -0
- data/app/views/components/toggle_group/_item.html.erb +19 -0
- data/config/importmap.rb +1 -0
- data/lib/generators/maquina_components/install/USAGE +39 -0
- data/lib/generators/maquina_components/install/install_generator.rb +123 -0
- data/lib/generators/maquina_components/install/templates/maquina_components_helper.rb.tt +68 -0
- data/lib/generators/maquina_components/install/templates/theme.css.tt +179 -0
- data/lib/maquina_components/engine.rb +10 -0
- data/lib/maquina_components/version.rb +1 -1
- metadata +121 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cdbbf854a700d7b0fa55c3b91f1975d7fd53282ddeb0a14db55011d055db96a3
|
|
4
|
+
data.tar.gz: 1b69e2f23f390efa573e3192410125e8945ec182001ca8f2db431fee72523efc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 07a7b92b85cdaf2987c2a816133bd174e1d838ff9af4ce16166ea218729c0896f7f4bbe71c7c8caaa3f175196dbe097d6f426c28db96e0cdd703054a788ba4f9
|
|
7
|
+
data.tar.gz: 53c9a63b128ed2cef878c3935185ed559d3e01274f1354ab34f438926a8e06cdac3980eabdd4c962e9a02765f7102c1694a106c9775cc05ce7f7358ed89add10
|
data/README.md
CHANGED
|
@@ -1,28 +1,425 @@
|
|
|
1
|
-
#
|
|
2
|
-
Short description and motivation.
|
|
1
|
+
# Maquina Components
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
How to use my plugin.
|
|
3
|
+
UI components for Ruby on Rails, built with ERB, TailwindCSS 4.0, and Stimulus.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why This Exists
|
|
8
|
+
|
|
9
|
+
I started building components inspired by [shadcn/ui](https://ui.shadcn.com/) for production Rails applications—mainly dashboards and admin interfaces. Over time, I iterated on these components across multiple projects, and they became inconsistent: different APIs, different styling approaches, different levels of completeness.
|
|
10
|
+
|
|
11
|
+
It was time to extract the elements I use most and give them a **cohesive API and consistent styling**.
|
|
12
|
+
|
|
13
|
+
### The Technical Choices
|
|
14
|
+
|
|
15
|
+
I chose **ERB partials** with **TailwindCSS** and **Stimulus controllers** for interactive elements. For static components like form inputs, pure CSS with data attributes is enough.
|
|
16
|
+
|
|
17
|
+
I'm aware of alternatives like [ViewComponent](https://viewcomponent.org/) and [Phlex](https://www.phlex.fun/). The projects I extracted these components from didn't use them. I see the benefits of using a Ruby class to render the UI, but bringing in any of these libraries into a project is a big commitment, and not all projects and teams are open to doing it. The reason is not technical; it is the feeling of moving away from "the Rails way." So I kept it simple: ERB partials that any Rails developer can understand immediately.
|
|
18
|
+
|
|
19
|
+
### Composability Over Convenience
|
|
20
|
+
|
|
21
|
+
These components are built to be **composable**. They are built of many small ERB partials to render. But that's intentional—you can take these partials and compose them into larger, application-specific components. There are no limits, and you have a standard API to guide you.
|
|
22
|
+
|
|
23
|
+
I didn't copy shadcn/ui one-to-one. I extracted only the components I actually use in my applications. This is a practical toolkit, not a complete port.
|
|
24
|
+
|
|
25
|
+
### One Approach Among Many
|
|
26
|
+
|
|
27
|
+
There's no single UI kit that rules Rails development. If this approach doesn't resonate with you, here are excellent alternatives:
|
|
28
|
+
|
|
29
|
+
- [RailsUI](https://railsui.com) — Premium UI templates and components
|
|
30
|
+
- [RailsBlocks](https://railsblocks.com) — Copy-paste components for Rails
|
|
31
|
+
- [shadcn-rails](https://shadcn.rails-components.com) — Another shadcn/ui port for Rails
|
|
32
|
+
- [Inertia Rails + shadcn Starter](https://evilmartians.com/opensource/inertia-rails-shadcn-starter) — React/Vue components with Inertia
|
|
33
|
+
|
|
34
|
+
If you're open to trying maquina_components and providing feedback, you're welcome to do so. If this isn't for you, that's okay too.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **ERB partials** with strict locals (`locals:` magic comments)
|
|
41
|
+
- **TailwindCSS 4.0** with CSS custom properties for theming
|
|
42
|
+
- **Data attributes** (`data-component`, `data-*-part`) for CSS styling
|
|
43
|
+
- **Stimulus controllers** only used where interactivity is needed
|
|
44
|
+
- **Dark mode** support via CSS variables
|
|
45
|
+
- **shadcn/ui theming** convention (works with their color system)
|
|
46
|
+
- **Composable** — small partials you can combine freely
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
### 1. Add the Gem
|
|
9
57
|
|
|
10
58
|
```ruby
|
|
11
|
-
|
|
59
|
+
# Gemfile
|
|
60
|
+
gem "maquina-components"
|
|
12
61
|
```
|
|
13
62
|
|
|
14
|
-
And then execute:
|
|
15
63
|
```bash
|
|
16
|
-
|
|
64
|
+
bundle install
|
|
17
65
|
```
|
|
18
66
|
|
|
19
|
-
|
|
67
|
+
### 2. Run the Install Generator
|
|
68
|
+
|
|
20
69
|
```bash
|
|
21
|
-
|
|
70
|
+
bin/rails generate maquina_components:install
|
|
22
71
|
```
|
|
23
72
|
|
|
73
|
+
This will:
|
|
74
|
+
|
|
75
|
+
- Add the engine CSS import to your Tailwind file
|
|
76
|
+
- Add theme variables (light + dark mode)
|
|
77
|
+
- Create a helper file for icon customization
|
|
78
|
+
|
|
79
|
+
### 3. Use Components
|
|
80
|
+
|
|
81
|
+
```erb
|
|
82
|
+
<%= render "components/card" do %>
|
|
83
|
+
<%= render "components/card/header" do %>
|
|
84
|
+
<%= render "components/card/title", text: "Account Settings" %>
|
|
85
|
+
<%= render "components/card/description", text: "Manage your preferences" %>
|
|
86
|
+
<% end %>
|
|
87
|
+
<%= render "components/card/content" do %>
|
|
88
|
+
<!-- Your content -->
|
|
89
|
+
<% end %>
|
|
90
|
+
<% end %>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For form elements, use data attributes with Rails helpers:
|
|
94
|
+
|
|
95
|
+
```erb
|
|
96
|
+
<%= form_with model: @user do |f| %>
|
|
97
|
+
<%= f.text_field :name, data: { component: "input" } %>
|
|
98
|
+
<%= f.email_field :email, data: { component: "input" } %>
|
|
99
|
+
<%= f.submit "Save", data: { component: "button", variant: "primary" } %>
|
|
100
|
+
<% end %>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**[Full Getting Started Guide](https://maquina.app/documentation/components/)**
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Generator Options
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Default: adds everything
|
|
111
|
+
bin/rails generate maquina_components:install
|
|
112
|
+
|
|
113
|
+
# Skip theme variables (if you have your own)
|
|
114
|
+
bin/rails generate maquina_components:install --skip-theme
|
|
115
|
+
|
|
116
|
+
# Skip helper file
|
|
117
|
+
bin/rails generate maquina_components:install --skip-helper
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Prerequisite:** [tailwindcss-rails](https://github.com/rails/tailwindcss-rails) must be installed first.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Available Components
|
|
125
|
+
|
|
126
|
+
### Layout Components
|
|
127
|
+
|
|
128
|
+
| Component | Description | Documentation |
|
|
129
|
+
|-----------|-------------|---------------|
|
|
130
|
+
| **Sidebar** | Collapsible navigation with cookie persistence | [Sidebar](https://maquina.app/documentation/components/sidebar/) |
|
|
131
|
+
| **Header** | Top navigation bar | [Header](https://maquina.app/documentation/components/header/) |
|
|
132
|
+
|
|
133
|
+
### Content Components
|
|
134
|
+
|
|
135
|
+
| Component | Description | Documentation |
|
|
136
|
+
|-----------|-------------|---------------|
|
|
137
|
+
| **Card** | Content container with header, content, footer | [Card](https://maquina.app/documentation/components/card/) |
|
|
138
|
+
| **Alert** | Callout messages (info, warning, error) | [Alert](https://maquina.app/documentation/components/alert/) |
|
|
139
|
+
| **Badge** | Status indicators and labels | [Badge](https://maquina.app/documentation/components/badge/) |
|
|
140
|
+
| **Table** | Data tables with sorting support | [Table](https://maquina.app/documentation/components/table/) |
|
|
141
|
+
| **Empty State** | Placeholder for empty lists | [Empty State](https://maquina.app/documentation/components/empty/) |
|
|
142
|
+
|
|
143
|
+
### Navigation Components
|
|
144
|
+
|
|
145
|
+
| Component | Description | Documentation |
|
|
146
|
+
|-----------|-------------|---------------|
|
|
147
|
+
| **Breadcrumbs** | Navigation trail with overflow handling | [Breadcrumbs](https://maquina.app/documentation/components/breadcrumbs/) |
|
|
148
|
+
| **Dropdown Menu** | Accessible dropdown with keyboard navigation | [Dropdown Menu](https://maquina.app/documentation/components/dropdown-menu/) |
|
|
149
|
+
| **Pagination** | Page navigation with Pagy integration | [Pagination](https://maquina.app/documentation/components/pagination/) |
|
|
150
|
+
|
|
151
|
+
### Interactive Components
|
|
152
|
+
|
|
153
|
+
| Component | Description | Documentation |
|
|
154
|
+
|-----------|-------------|---------------|
|
|
155
|
+
| **Toggle Group** | Single/multiple selection button group | [Toggle Group](https://maquina.app/documentation/components/toggle-group/) |
|
|
156
|
+
|
|
157
|
+
### Form Components
|
|
158
|
+
|
|
159
|
+
| Component | Data Attribute | Variants |
|
|
160
|
+
|-----------|----------------|----------|
|
|
161
|
+
| **Button** | `data-component="button"` | default, primary, secondary, destructive, outline, ghost, link |
|
|
162
|
+
| **Input** | `data-component="input"` | — |
|
|
163
|
+
| **Textarea** | `data-component="textarea"` | — |
|
|
164
|
+
| **Select** | `data-component="select"` | — |
|
|
165
|
+
| **Checkbox** | `data-component="checkbox"` | — |
|
|
166
|
+
| **Radio** | `data-component="radio"` | — |
|
|
167
|
+
| **Switch** | `data-component="switch"` | — |
|
|
168
|
+
|
|
169
|
+
**[Form Components Guide](https://maquina.app/documentation/components/form/)**
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Examples
|
|
174
|
+
|
|
175
|
+
### Cards with Actions
|
|
176
|
+
|
|
177
|
+
```erb
|
|
178
|
+
<%= render "components/card" do %>
|
|
179
|
+
<%= render "components/card/header", layout: :row do %>
|
|
180
|
+
<div>
|
|
181
|
+
<%= render "components/card/title", text: "Team Members" %>
|
|
182
|
+
<%= render "components/card/description", text: "Manage your team" %>
|
|
183
|
+
</div>
|
|
184
|
+
<%= render "components/card/action" do %>
|
|
185
|
+
<%= link_to "Add Member", new_member_path,
|
|
186
|
+
data: { component: "button", variant: "primary", size: "sm" } %>
|
|
187
|
+
<% end %>
|
|
188
|
+
<% end %>
|
|
189
|
+
<%= render "components/card/content" do %>
|
|
190
|
+
<!-- Table or list -->
|
|
191
|
+
<% end %>
|
|
192
|
+
<% end %>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Alerts
|
|
196
|
+
|
|
197
|
+
```erb
|
|
198
|
+
<%= render "components/alert", variant: :destructive do %>
|
|
199
|
+
<%= render "components/alert/title", text: "Error" %>
|
|
200
|
+
<%= render "components/alert/description" do %>
|
|
201
|
+
Your session has expired. Please log in again.
|
|
202
|
+
<% end %>
|
|
203
|
+
<% end %>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Badges
|
|
207
|
+
|
|
208
|
+
```erb
|
|
209
|
+
<%= render "components/badge", variant: :success do %>Active<% end %>
|
|
210
|
+
<%= render "components/badge", variant: :warning do %>Pending<% end %>
|
|
211
|
+
<%= render "components/badge", variant: :destructive do %>Failed<% end %>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Toggle Group
|
|
215
|
+
|
|
216
|
+
```erb
|
|
217
|
+
<%= render "components/toggle_group", type: :single, variant: :outline do %>
|
|
218
|
+
<%= render "components/toggle_group/item", value: "left", aria_label: "Align left" do %>
|
|
219
|
+
<%= icon_for :align_left %>
|
|
220
|
+
<% end %>
|
|
221
|
+
<%= render "components/toggle_group/item", value: "center", aria_label: "Align center" do %>
|
|
222
|
+
<%= icon_for :align_center %>
|
|
223
|
+
<% end %>
|
|
224
|
+
<%= render "components/toggle_group/item", value: "right", aria_label: "Align right" do %>
|
|
225
|
+
<%= icon_for :align_right %>
|
|
226
|
+
<% end %>
|
|
227
|
+
<% end %>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Dropdown Menu
|
|
231
|
+
|
|
232
|
+
```erb
|
|
233
|
+
<%= render "components/dropdown_menu" do %>
|
|
234
|
+
<%= render "components/dropdown_menu/trigger" do %>Options<% end %>
|
|
235
|
+
<%= render "components/dropdown_menu/content" do %>
|
|
236
|
+
<%= render "components/dropdown_menu/item", href: profile_path do %>
|
|
237
|
+
<%= icon_for :user %>
|
|
238
|
+
Profile
|
|
239
|
+
<% end %>
|
|
240
|
+
<%= render "components/dropdown_menu/separator" %>
|
|
241
|
+
<%= render "components/dropdown_menu/item", href: logout_path, method: :delete, variant: :destructive do %>
|
|
242
|
+
<%= icon_for :log_out %>
|
|
243
|
+
Logout
|
|
244
|
+
<% end %>
|
|
245
|
+
<% end %>
|
|
246
|
+
<% end %>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Pagination
|
|
250
|
+
|
|
251
|
+
```erb
|
|
252
|
+
<%= pagination_nav(@pagy, :users_path) %>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Tables
|
|
256
|
+
|
|
257
|
+
```erb
|
|
258
|
+
<%= render "components/table" do %>
|
|
259
|
+
<%= render "components/table/header" do %>
|
|
260
|
+
<%= render "components/table/row" do %>
|
|
261
|
+
<%= render "components/table/head" do %>Name<% end %>
|
|
262
|
+
<%= render "components/table/head" do %>Email<% end %>
|
|
263
|
+
<%= render "components/table/head" do %>Role<% end %>
|
|
264
|
+
<% end %>
|
|
265
|
+
<% end %>
|
|
266
|
+
<%= render "components/table/body" do %>
|
|
267
|
+
<% @users.each do |user| %>
|
|
268
|
+
<%= render "components/table/row" do %>
|
|
269
|
+
<%= render "components/table/cell" do %><%= user.name %><% end %>
|
|
270
|
+
<%= render "components/table/cell" do %><%= user.email %><% end %>
|
|
271
|
+
<%= render "components/table/cell" do %>
|
|
272
|
+
<%= render "components/badge", variant: :outline do %><%= user.role %><% end %>
|
|
273
|
+
<% end %>
|
|
274
|
+
<% end %>
|
|
275
|
+
<% end %>
|
|
276
|
+
<% end %>
|
|
277
|
+
<% end %>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Sidebar Layout
|
|
281
|
+
|
|
282
|
+
```erb
|
|
283
|
+
<%= render "components/sidebar/provider", default_open: app_sidebar_open? do %>
|
|
284
|
+
<%= render "components/sidebar" do %>
|
|
285
|
+
<%= render "components/sidebar/header" do %>
|
|
286
|
+
<span class="font-semibold">My App</span>
|
|
287
|
+
<% end %>
|
|
288
|
+
|
|
289
|
+
<%= render "components/sidebar/content" do %>
|
|
290
|
+
<%= render "components/sidebar/group", title: "Navigation" do %>
|
|
291
|
+
<%= render "components/sidebar/menu" do %>
|
|
292
|
+
<%= render "components/sidebar/menu_item" do %>
|
|
293
|
+
<%= render "components/sidebar/menu_button",
|
|
294
|
+
title: "Dashboard",
|
|
295
|
+
url: dashboard_path,
|
|
296
|
+
icon_name: :home,
|
|
297
|
+
active: current_page?(dashboard_path) %>
|
|
298
|
+
<% end %>
|
|
299
|
+
<% end %>
|
|
300
|
+
<% end %>
|
|
301
|
+
<% end %>
|
|
302
|
+
<% end %>
|
|
303
|
+
|
|
304
|
+
<%= render "components/sidebar/inset" do %>
|
|
305
|
+
<%= render "components/header" do %>
|
|
306
|
+
<%= render "components/sidebar/trigger", icon_name: :panel_left %>
|
|
307
|
+
<% end %>
|
|
308
|
+
|
|
309
|
+
<main class="flex-1 p-6">
|
|
310
|
+
<%= yield %>
|
|
311
|
+
</main>
|
|
312
|
+
<% end %>
|
|
313
|
+
<% end %>
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Theming
|
|
319
|
+
|
|
320
|
+
Components use CSS variables following the [shadcn/ui theming convention](https://ui.shadcn.com/docs/theming).
|
|
321
|
+
|
|
322
|
+
The install generator adds default theme variables. Customize them in `app/assets/tailwind/application.css`:
|
|
323
|
+
|
|
324
|
+
```css
|
|
325
|
+
:root {
|
|
326
|
+
/* Change primary to blue */
|
|
327
|
+
--primary: oklch(0.488 0.243 264.376);
|
|
328
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
329
|
+
|
|
330
|
+
/* Add custom colors */
|
|
331
|
+
--success: oklch(0.6 0.2 145);
|
|
332
|
+
--success-foreground: oklch(0.985 0 0);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
@theme {
|
|
336
|
+
--color-success: var(--success);
|
|
337
|
+
--color-success-foreground: var(--success-foreground);
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Helper Methods
|
|
344
|
+
|
|
345
|
+
| Helper | Purpose |
|
|
346
|
+
|--------|---------|
|
|
347
|
+
| `icon_for(name, options)` | Render an SVG icon |
|
|
348
|
+
| `sidebar_state(cookie_name)` | Get sidebar state (`:expanded` or `:collapsed`) |
|
|
349
|
+
| `sidebar_open?(cookie_name)` | Check if the sidebar is expanded |
|
|
350
|
+
| `pagination_nav(pagy, route)` | Render pagination from Pagy object |
|
|
351
|
+
| `pagination_simple(pagy, route)` | Render simple Previous/Next pagination |
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Documentation
|
|
356
|
+
|
|
357
|
+
### Getting Started
|
|
358
|
+
|
|
359
|
+
- **[Getting Started](https://maquina.app/documentation/components/)** — Installation and setup
|
|
360
|
+
|
|
361
|
+
### Layout
|
|
362
|
+
|
|
363
|
+
- **[Sidebar](https://maquina.app/documentation/components/sidebar/)** — Navigation sidebar
|
|
364
|
+
- **[Header](https://maquina.app/documentation/components/header/)** — Top navigation bar
|
|
365
|
+
|
|
366
|
+
### Content
|
|
367
|
+
|
|
368
|
+
- **[Card](https://maquina.app/documentation/components/card/)** — Content containers
|
|
369
|
+
- **[Alert](https://maquina.app/documentation/components/alert/)** — Callout messages
|
|
370
|
+
- **[Badge](https://maquina.app/documentation/components/badge/)** — Status indicators
|
|
371
|
+
- **[Table](https://maquina.app/documentation/components/table/)** — Data tables
|
|
372
|
+
- **[Empty State](https://maquina.app/documentation/components/empty/)** — Empty state placeholders
|
|
373
|
+
|
|
374
|
+
### Navigation
|
|
375
|
+
|
|
376
|
+
- **[Breadcrumbs](https://maquina.app/documentation/components/breadcrumbs/)** — Navigation trails
|
|
377
|
+
- **[Dropdown Menu](https://maquina.app/documentation/components/dropdown-menu/)** — Dropdown menus
|
|
378
|
+
- **[Pagination](https://maquina.app/documentation/components/pagination/)** — Page navigation
|
|
379
|
+
|
|
380
|
+
### Interactive
|
|
381
|
+
|
|
382
|
+
- **[Toggle Group](https://maquina.app/documentation/components/toggle-group/)** — Toggle button groups
|
|
383
|
+
|
|
384
|
+
### Forms
|
|
385
|
+
|
|
386
|
+
- **[Form Components](https://maquina.app/documentation/components/form/)** — Buttons, inputs, and form styling
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Development
|
|
391
|
+
|
|
392
|
+
Run the dummy app:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
cd test/dummy
|
|
396
|
+
bin/rails server
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Run tests:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
bin/rails test
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
24
407
|
## Contributing
|
|
25
|
-
|
|
408
|
+
|
|
409
|
+
Bug reports and pull requests are welcome on GitHub at [github.com/maquina-app/maquina_components](https://github.com/maquina-app/maquina_components).
|
|
410
|
+
|
|
411
|
+
---
|
|
26
412
|
|
|
27
413
|
## License
|
|
28
|
-
|
|
414
|
+
|
|
415
|
+
Copyright (c) [Mario Alberto Chávez Cárdenas](https://mariochavez.io)
|
|
416
|
+
|
|
417
|
+
The gem is available as open source under the terms of the [MIT License](MIT-LICENSE).
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Credits
|
|
422
|
+
|
|
423
|
+
- Design patterns from [shadcn/ui](https://ui.shadcn.com/)
|
|
424
|
+
- Built with [TailwindCSS](https://tailwindcss.com/)
|
|
425
|
+
- Powered by [Ruby on Rails](https://rubyonrails.org/)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1268" height="326"><defs><linearGradient id="b" x1="21.152%" x2="100%" y1="21.848%" y2="93.468%"><stop offset="0%" stop-color="#50B1FD" stop-opacity=".96"/><stop offset="100%" stop-color="#DE77DE" stop-opacity=".96"/></linearGradient><filter id="a" width="109.8%" height="109.8%" x="-4.9%" y="-4.3%" filterUnits="objectBoundingBox"><feOffset dy="2" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="5"/><feColorMatrix in="shadowBlurOuter1" result="shadowMatrixOuter1" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g fill="none" fill-rule="evenodd"><g fill="url(#b)" filter="url(#a)"><path d="M163 0c90.022 0 163 72.978 163 163s-72.978 163-163 163S0 253.022 0 163 72.978 0 163 0Zm-3.035 32.001-2.3.033C82.529 33.836 7.373 106.038 39.71 209.826l.128-.06.17-.085c.666-.337 2.097-1.105 4.294-2.305l.648-.355c7.567-4.142 23.12-12.79 46.658-25.942 8.07 31.143 36.39 54.144 70.088 54.144 11.29 0 21.975-2.582 31.498-7.186l38.287 21.06c-67.582 40.567-122.657 31.134-165.224-28.3l-17.485 10.496c38.51 66.185 141.812 96.167 215.685 13.364-1.55-1.057-18.929-11.076-52.136-30.058 13.432-13.13 21.768-31.441 21.768-51.698 0-24.865-12.561-46.8-31.69-59.816l-.125-44.132c68.28 39.383 86.722 92.078 55.326 158.086l17.666 10.189c39.273-65.736 15.44-170.532-92.866-194.936-.176 1.88-.526 22.209-1.052 60.983a72.517 72.517 0 0 0-19.652-2.696c-38.356 0-69.744 29.799-72.234 67.487L52.886 179.3c0-78.766 36.46-121.07 109.378-126.91V32.012l-2.3-.01Zm2.067 100.096c17.214 0 31.17 13.941 31.17 31.139 0 17.197-13.956 31.138-31.17 31.138-17.215 0-31.17-13.94-31.17-31.138s13.955-31.139 31.17-31.139Z"/></g><g fill="#50B1FD" fill-rule="nonzero"><path d="M424.59 217V102.97h23.1v48.93h-2.1c0-11.48 1.47-21.105 4.41-28.875 2.94-7.77 7.315-13.65 13.125-17.64S476.18 99.4 484.86 99.4h1.26c8.82 0 16.135 1.995 21.945 5.985 5.81 3.99 10.15 9.87 13.02 17.64 2.87 7.77 4.305 17.395 4.305 28.875h-7.35c0-11.48 1.505-21.105 4.515-28.875s7.42-13.65 13.23-17.64c5.81-3.99 13.055-5.985 21.735-5.985h1.26c8.82 0 16.17 1.995 22.05 5.985 5.88 3.99 10.325 9.87 13.335 17.64 3.01 7.77 4.515 17.395 4.515 28.875V217h-29.19v-67.83c0-7.14-1.82-12.845-5.46-17.115-3.64-4.27-8.82-6.405-15.54-6.405-6.72 0-12.11 2.205-16.17 6.615-4.06 4.41-6.09 10.325-6.09 17.745V217h-29.19v-67.83c0-7.14-1.82-12.845-5.46-17.115-3.64-4.27-8.82-6.405-15.54-6.405-6.72 0-12.11 2.205-16.17 6.615-4.06 4.41-6.09 10.325-6.09 17.745V217h-29.19ZM688.56 217v-33.81h-4.83V145.6c0-6.58-1.61-11.48-4.83-14.7-3.22-3.22-8.19-4.83-14.91-4.83-3.5 0-7.7.07-12.6.21-4.9.14-9.835.315-14.805.525-4.97.21-9.415.455-13.335.735v-24.78c3.22-.28 6.86-.56 10.92-.84 4.06-.28 8.225-.455 12.495-.525s8.295-.105 12.075-.105c11.76 0 21.525 1.54 29.295 4.62 7.77 3.08 13.65 7.91 17.64 14.49s5.985 15.19 5.985 25.83V217h-23.1Zm-36.75 2.94c-8.26 0-15.505-1.47-21.735-4.41-6.23-2.94-11.06-7.14-14.49-12.6-3.43-5.46-5.145-12.04-5.145-19.74 0-8.4 2.065-15.26 6.195-20.58 4.13-5.32 9.94-9.31 17.43-11.97s16.275-3.99 26.355-3.99h26.46v17.43H660c-6.72 0-11.865 1.645-15.435 4.935-3.57 3.29-5.355 7.525-5.355 12.705s1.785 9.38 5.355 12.6c3.57 3.22 8.715 4.83 15.435 4.83 4.06 0 7.805-.735 11.235-2.205 3.43-1.47 6.3-3.99 8.61-7.56 2.31-3.57 3.605-8.435 3.885-14.595l7.14 8.19c-.7 7.98-2.625 14.7-5.775 20.16-3.15 5.46-7.49 9.625-13.02 12.495-5.53 2.87-12.285 4.305-20.265 4.305ZM816.87 259v-75.81l8.61-13.44c-.42 10.92-2.66 20.195-6.72 27.825-4.06 7.63-9.52 13.405-16.38 17.325-6.86 3.92-14.77 5.88-23.73 5.88-8.12 0-15.47-1.505-22.05-4.515s-12.215-7.21-16.905-12.6c-4.69-5.39-8.33-11.62-10.92-18.69-2.59-7.07-3.885-14.735-3.885-22.995v-4.41c0-8.12 1.33-15.715 3.99-22.785s6.405-13.23 11.235-18.48c4.83-5.25 10.57-9.38 17.22-12.39 6.65-3.01 14.105-4.515 22.365-4.515 9.52 0 17.78 2.03 24.78 6.09 7 4.06 12.495 9.975 16.485 17.745 3.99 7.77 6.195 17.255 6.615 28.455h-4.62v-48.72h23.1V259h-29.19Zm-30.66-63.21c5.74 0 10.99-1.295 15.75-3.885s8.575-6.37 11.445-11.34c2.87-4.97 4.305-10.815 4.305-17.535v-8.4c0-6.72-1.47-12.355-4.41-16.905-2.94-4.55-6.79-8.05-11.55-10.5-4.76-2.45-9.94-3.675-15.54-3.675-6.3 0-11.865 1.505-16.695 4.515s-8.61 7.28-11.34 12.81c-2.73 5.53-4.095 11.865-4.095 19.005 0 7.14 1.4 13.405 4.2 18.795 2.8 5.39 6.615 9.59 11.445 12.6s10.325 4.515 16.485 4.515ZM907.59 220.57c-13.16 0-23.345-4.34-30.555-13.02-7.21-8.68-10.815-21.56-10.815-38.64v-66.15h29.19v68.67c0 7 1.96 12.565 5.88 16.695 3.92 4.13 9.24 6.195 15.96 6.195 6.72 0 12.215-2.17 16.485-6.51s6.405-10.22 6.405-17.64v-67.41h29.19V217h-23.1v-48.51h2.31c0 11.48-1.47 21.07-4.41 28.77-2.94 7.7-7.35 13.51-13.23 17.43-5.88 3.92-13.23 5.88-22.05 5.88h-1.26ZM997.89 217V102.97h29.19V217h-29.19Zm-15.96-92.19v-21.84h45.15v21.84h-45.15Zm26.67-35.49c-5.74 0-9.975-1.505-12.705-4.515-2.73-3.01-4.095-6.825-4.095-11.445s1.365-8.4 4.095-11.34c2.73-2.94 6.965-4.41 12.705-4.41s9.94 1.47 12.6 4.41c2.66 2.94 3.99 6.72 3.99 11.34s-1.33 8.435-3.99 11.445-6.86 4.515-12.6 4.515ZM1050.18 217V102.97h23.1v48.93h-2.1c0-11.62 1.54-21.315 4.62-29.085s7.665-13.615 13.755-17.535c6.09-3.92 13.685-5.88 22.785-5.88h1.26c13.58 0 23.87 4.375 30.87 13.125s10.5 21.805 10.5 39.165V217h-29.19v-67.83c0-7-1.995-12.67-5.985-17.01-3.99-4.34-9.485-6.51-16.485-6.51-7.14 0-12.915 2.205-17.325 6.615s-6.615 10.325-6.615 17.745V217h-29.19ZM1244.85 217v-33.81h-4.83V145.6c0-6.58-1.61-11.48-4.83-14.7-3.22-3.22-8.19-4.83-14.91-4.83-3.5 0-7.7.07-12.6.21-4.9.14-9.835.315-14.805.525-4.97.21-9.415.455-13.335.735v-24.78c3.22-.28 6.86-.56 10.92-.84 4.06-.28 8.225-.455 12.495-.525s8.295-.105 12.075-.105c11.76 0 21.525 1.54 29.295 4.62 7.77 3.08 13.65 7.91 17.64 14.49s5.985 15.19 5.985 25.83V217h-23.1Zm-36.75 2.94c-8.26 0-15.505-1.47-21.735-4.41-6.23-2.94-11.06-7.14-14.49-12.6-3.43-5.46-5.145-12.04-5.145-19.74 0-8.4 2.065-15.26 6.195-20.58 4.13-5.32 9.94-9.31 17.43-11.97s16.275-3.99 26.355-3.99h26.46v17.43h-26.88c-6.72 0-11.865 1.645-15.435 4.935-3.57 3.29-5.355 7.525-5.355 12.705s1.785 9.38 5.355 12.6c3.57 3.22 8.715 4.83 15.435 4.83 4.06 0 7.805-.735 11.235-2.205 3.43-1.47 6.3-3.99 8.61-7.56 2.31-3.57 3.605-8.435 3.885-14.595l7.14 8.19c-.7 7.98-2.625 14.7-5.775 20.16-3.15 5.46-7.49 9.625-13.02 12.495-5.53 2.87-12.285 4.305-20.265 4.305Z"/></g></g></svg>
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* ===== Alert Component Styles ===== */
|
|
2
|
+
/*
|
|
3
|
+
* Alert component for displaying callouts, messages, and notifications.
|
|
4
|
+
* Uses data attributes for styling to maintain consistency with other components.
|
|
5
|
+
* Fully compatible with dark mode via CSS variables.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* ===== Base Alert Styles ===== */
|
|
9
|
+
[data-component="alert"] {
|
|
10
|
+
position: relative;
|
|
11
|
+
display: grid;
|
|
12
|
+
grid-template-columns: 1fr;
|
|
13
|
+
@apply w-full rounded-lg border p-4 text-sm;
|
|
14
|
+
|
|
15
|
+
/* Default colors */
|
|
16
|
+
background-color: var(--background);
|
|
17
|
+
color: var(--foreground);
|
|
18
|
+
border-color: var(--border);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Alert with icon - add left padding for icon space */
|
|
22
|
+
[data-component="alert"][data-has-icon="true"] {
|
|
23
|
+
grid-template-columns: auto 1fr;
|
|
24
|
+
@apply gap-3;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* ===== Icon Support ===== */
|
|
28
|
+
[data-component="alert"] > svg:first-child,
|
|
29
|
+
[data-component="alert"] [data-alert-part="icon"] {
|
|
30
|
+
@apply size-4 shrink-0;
|
|
31
|
+
color: var(--foreground);
|
|
32
|
+
/* Align with first line of text */
|
|
33
|
+
margin-top: 0.125rem;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* ===== Alert Title ===== */
|
|
37
|
+
[data-component="alert"] [data-alert-part="title"] {
|
|
38
|
+
@apply font-medium leading-none tracking-tight;
|
|
39
|
+
color: var(--foreground);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Title followed by description needs margin */
|
|
43
|
+
[data-component="alert"] [data-alert-part="title"]:has(+ [data-alert-part="description"]) {
|
|
44
|
+
@apply mb-1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ===== Alert Description ===== */
|
|
48
|
+
[data-component="alert"] [data-alert-part="description"] {
|
|
49
|
+
@apply text-sm;
|
|
50
|
+
color: var(--muted-foreground);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Nested paragraphs */
|
|
54
|
+
[data-component="alert"] [data-alert-part="description"] p {
|
|
55
|
+
@apply leading-relaxed;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Lists inside description */
|
|
59
|
+
[data-component="alert"] [data-alert-part="description"] ul {
|
|
60
|
+
@apply mt-2 list-inside list-disc;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* ===== Variant: Default ===== */
|
|
64
|
+
[data-component="alert"][data-variant="default"] {
|
|
65
|
+
background-color: var(--background);
|
|
66
|
+
color: var(--foreground);
|
|
67
|
+
border-color: var(--border);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
[data-component="alert"][data-variant="default"] > svg:first-child,
|
|
71
|
+
[data-component="alert"][data-variant="default"] [data-alert-part="icon"] {
|
|
72
|
+
color: var(--foreground);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* ===== Variant: Destructive ===== */
|
|
76
|
+
[data-component="alert"][data-variant="destructive"] {
|
|
77
|
+
background-color: var(--destructive);
|
|
78
|
+
color: var(--destructive-foreground);
|
|
79
|
+
border-color: var(--destructive);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
[data-component="alert"][data-variant="destructive"] [data-alert-part="title"] {
|
|
83
|
+
color: var(--destructive-foreground);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
[data-component="alert"][data-variant="destructive"] [data-alert-part="description"] {
|
|
87
|
+
color: var(--destructive-foreground);
|
|
88
|
+
opacity: 0.9;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
[data-component="alert"][data-variant="destructive"] > svg:first-child,
|
|
92
|
+
[data-component="alert"][data-variant="destructive"] [data-alert-part="icon"] {
|
|
93
|
+
color: var(--destructive-foreground);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ===== Variant: Success ===== */
|
|
97
|
+
[data-component="alert"][data-variant="success"] {
|
|
98
|
+
background-color: var(--success);
|
|
99
|
+
color: var(--success-foreground);
|
|
100
|
+
border-color: var(--success);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-component="alert"][data-variant="success"] [data-alert-part="title"] {
|
|
104
|
+
color: var(--success-foreground);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
[data-component="alert"][data-variant="success"] [data-alert-part="description"] {
|
|
108
|
+
color: var(--success-foreground);
|
|
109
|
+
opacity: 0.9;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
[data-component="alert"][data-variant="success"] > svg:first-child,
|
|
113
|
+
[data-component="alert"][data-variant="success"] [data-alert-part="icon"] {
|
|
114
|
+
color: var(--success-foreground);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* ===== Variant: Warning ===== */
|
|
118
|
+
[data-component="alert"][data-variant="warning"] {
|
|
119
|
+
background-color: var(--warning);
|
|
120
|
+
color: var(--warning-foreground);
|
|
121
|
+
border-color: var(--warning);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
[data-component="alert"][data-variant="warning"] [data-alert-part="title"] {
|
|
125
|
+
color: var(--warning-foreground);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
[data-component="alert"][data-variant="warning"] [data-alert-part="description"] {
|
|
129
|
+
color: var(--warning-foreground);
|
|
130
|
+
opacity: 0.9;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
[data-component="alert"][data-variant="warning"] > svg:first-child,
|
|
134
|
+
[data-component="alert"][data-variant="warning"] [data-alert-part="icon"] {
|
|
135
|
+
color: var(--warning-foreground);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* ===== Dark Mode ===== */
|
|
139
|
+
/*
|
|
140
|
+
* Dark mode is handled automatically through CSS variables.
|
|
141
|
+
* The theme variables change based on the .dark class on html/body.
|
|
142
|
+
* No additional dark mode styles needed here.
|
|
143
|
+
*/
|