shadcn-rails 0.1.0 → 0.2.1
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/CHANGELOG.md +69 -2
- data/README.md +102 -1398
- data/__mocks__/@floating-ui/dom.js +67 -0
- data/app/assets/javascripts/shadcn/controllers/base_menu_controller.js +266 -0
- data/app/assets/javascripts/shadcn/controllers/combobox_controller.js +34 -8
- data/app/assets/javascripts/shadcn/controllers/command_controller.js +5 -1
- data/app/assets/javascripts/shadcn/controllers/context_menu_controller.js +64 -135
- data/app/assets/javascripts/shadcn/controllers/dropdown_controller.js +56 -186
- data/app/assets/javascripts/shadcn/controllers/hover_card_controller.js +29 -55
- data/app/assets/javascripts/shadcn/controllers/menubar_controller.js +10 -7
- data/app/assets/javascripts/shadcn/controllers/navigation_menu_controller.js +10 -6
- data/app/assets/javascripts/shadcn/controllers/popover_controller.js +35 -60
- data/app/assets/javascripts/shadcn/controllers/select_controller.js +37 -17
- data/app/assets/javascripts/shadcn/controllers/sidebar_controller.js +24 -14
- data/app/assets/javascripts/shadcn/controllers/tooltip_controller.js +28 -59
- data/app/assets/javascripts/shadcn/index.js +9 -1
- data/app/assets/javascripts/shadcn/utils/floating.js +179 -0
- data/app/assets/stylesheets/shadcn/base.css +32 -0
- data/app/assets/stylesheets/shadcn/components.css +12 -0
- data/app/components/shadcn/accordion_component.html.erb +8 -0
- data/app/components/shadcn/accordion_component.rb +6 -15
- data/app/components/shadcn/alert_component.html.erb +6 -0
- data/app/components/shadcn/alert_component.rb +0 -18
- data/app/components/shadcn/alert_dialog_component.html.erb +12 -0
- data/app/components/shadcn/alert_dialog_component.rb +7 -27
- data/app/components/shadcn/aspect_ratio_component.html.erb +7 -0
- data/app/components/shadcn/aspect_ratio_component.rb +4 -19
- data/app/components/shadcn/avatar_component.html.erb +20 -0
- data/app/components/shadcn/avatar_component.rb +8 -36
- data/app/components/shadcn/badge_component.html.erb +1 -0
- data/app/components/shadcn/badge_component.rb +0 -11
- data/app/components/shadcn/base_component.rb +15 -2
- data/app/components/shadcn/breadcrumb_component.html.erb +5 -0
- data/app/components/shadcn/breadcrumb_component.rb +6 -16
- data/app/components/shadcn/button_component.html.erb +18 -0
- data/app/components/shadcn/button_component.rb +1 -41
- data/app/components/shadcn/card_component.html.erb +8 -0
- data/app/components/shadcn/card_component.rb +2 -6
- data/app/components/shadcn/checkbox_component.html.erb +32 -0
- data/app/components/shadcn/checkbox_component.rb +4 -43
- data/app/components/shadcn/collapsible_component.html.erb +8 -0
- data/app/components/shadcn/collapsible_component.rb +6 -15
- data/app/components/shadcn/command_list_component.rb +29 -14
- data/app/components/shadcn/context_menu_checkbox_item_component.rb +76 -0
- data/app/components/shadcn/context_menu_component.html.erb +11 -0
- data/app/components/shadcn/context_menu_component.rb +6 -26
- data/app/components/shadcn/context_menu_content_component.rb +37 -14
- data/app/components/shadcn/context_menu_item_component.rb +3 -2
- data/app/components/shadcn/context_menu_radio_group_component.rb +42 -0
- data/app/components/shadcn/context_menu_radio_item_component.rb +76 -0
- data/app/components/shadcn/dialog_component.html.erb +14 -0
- data/app/components/shadcn/dialog_component.rb +8 -29
- data/app/components/shadcn/drawer_component.html.erb +12 -0
- data/app/components/shadcn/drawer_component.rb +7 -27
- data/app/components/shadcn/dropdown_menu_checkbox_item_component.rb +76 -0
- data/app/components/shadcn/dropdown_menu_component.html.erb +14 -0
- data/app/components/shadcn/dropdown_menu_component.rb +9 -29
- data/app/components/shadcn/dropdown_menu_content_component.rb +45 -16
- data/app/components/shadcn/dropdown_menu_radio_group_component.rb +42 -0
- data/app/components/shadcn/dropdown_menu_radio_item_component.rb +76 -0
- data/app/components/shadcn/field_component.rb +7 -8
- data/app/components/shadcn/hover_card_component.html.erb +12 -0
- data/app/components/shadcn/hover_card_component.rb +7 -26
- data/app/components/shadcn/input_component.html.erb +18 -0
- data/app/components/shadcn/input_component.rb +2 -27
- data/app/components/shadcn/input_otp_component.rb +3 -3
- data/app/components/shadcn/kbd_component.html.erb +1 -0
- data/app/components/shadcn/kbd_component.rb +3 -10
- data/app/components/shadcn/label_component.html.erb +3 -0
- data/app/components/shadcn/label_component.rb +2 -18
- data/app/components/shadcn/menubar_component.html.erb +6 -0
- data/app/components/shadcn/menubar_component.rb +4 -15
- data/app/components/shadcn/menubar_content_component.rb +45 -20
- data/app/components/shadcn/menubar_sub_content_component.rb +21 -8
- data/app/components/shadcn/native_select_component.html.erb +22 -0
- data/app/components/shadcn/native_select_component.rb +9 -39
- data/app/components/shadcn/navigation_menu_component.html.erb +6 -0
- data/app/components/shadcn/navigation_menu_component.rb +4 -15
- data/app/components/shadcn/pagination_component.html.erb +5 -0
- data/app/components/shadcn/pagination_component.rb +11 -15
- data/app/components/shadcn/popover_component.html.erb +15 -0
- data/app/components/shadcn/popover_component.rb +10 -30
- data/app/components/shadcn/progress_component.html.erb +13 -0
- data/app/components/shadcn/progress_component.rb +6 -26
- data/app/components/shadcn/radio_group_component.html.erb +8 -0
- data/app/components/shadcn/radio_group_component.rb +12 -26
- data/app/components/shadcn/radio_group_item_component.rb +32 -6
- data/app/components/shadcn/resizable_panel_group_component.rb +27 -16
- data/app/components/shadcn/scroll_area_component.html.erb +7 -0
- data/app/components/shadcn/scroll_area_component.rb +4 -16
- data/app/components/shadcn/select_component.html.erb +46 -0
- data/app/components/shadcn/select_component.rb +29 -86
- data/app/components/shadcn/separator_component.html.erb +5 -0
- data/app/components/shadcn/separator_component.rb +6 -14
- data/app/components/shadcn/sheet_component.html.erb +12 -0
- data/app/components/shadcn/sheet_component.rb +7 -27
- data/app/components/shadcn/sidebar_component.rb +2 -2
- data/app/components/shadcn/skeleton_component.html.erb +1 -0
- data/app/components/shadcn/skeleton_component.rb +4 -2
- data/app/components/shadcn/slider_component.html.erb +12 -0
- data/app/components/shadcn/slider_component.rb +2 -21
- data/app/components/shadcn/spinner_component.html.erb +18 -0
- data/app/components/shadcn/spinner_component.rb +2 -30
- data/app/components/shadcn/switch_component.html.erb +72 -0
- data/app/components/shadcn/switch_component.rb +4 -82
- data/app/components/shadcn/table_component.html.erb +9 -0
- data/app/components/shadcn/table_component.rb +2 -10
- data/app/components/shadcn/tabs_component.html.erb +8 -0
- data/app/components/shadcn/tabs_component.rb +4 -17
- data/app/components/shadcn/textarea_component.html.erb +13 -0
- data/app/components/shadcn/textarea_component.rb +6 -22
- data/app/components/shadcn/toast_component.html.erb +36 -0
- data/app/components/shadcn/toast_component.rb +6 -54
- data/app/components/shadcn/toggle_component.html.erb +12 -0
- data/app/components/shadcn/toggle_component.rb +6 -21
- data/app/components/shadcn/toggle_group_component.html.erb +14 -0
- data/app/components/shadcn/toggle_group_component.rb +6 -29
- data/app/components/shadcn/tooltip_component.html.erb +20 -0
- data/app/components/shadcn/tooltip_component.rb +13 -38
- data/lib/generators/shadcn/add/USAGE +24 -0
- data/lib/generators/shadcn/add/add_generator.rb +279 -0
- data/lib/generators/shadcn/install/USAGE +22 -0
- data/lib/generators/shadcn/install/install_generator.rb +8 -3
- data/lib/generators/shadcn/install/templates/initializer.rb.tt +7 -27
- data/lib/generators/shadcn/install/templates/shadcn.yml.tt +15 -31
- data/lib/shadcn/rails/version.rb +1 -1
- metadata +54 -42
- data/.dockerignore +0 -40
- data/CLAUDE.md +0 -463
- data/PROGRESS.md +0 -485
- data/Rakefile +0 -29
- data/__tests__/controllers/__snapshots__/calendar_controller.test.js.snap +0 -13
- data/__tests__/controllers/__snapshots__/popover_controller.test.js.snap +0 -46
- data/__tests__/controllers/__snapshots__/sheet_controller.test.js.snap +0 -111
- data/__tests__/controllers/__snapshots__/tabs_controller.test.js.snap +0 -27
- data/__tests__/controllers/accordion_controller.test.js +0 -904
- data/__tests__/controllers/calendar_controller.test.js +0 -1370
- data/__tests__/controllers/carousel_controller.test.js +0 -912
- data/__tests__/controllers/checkbox_controller.test.js +0 -454
- data/__tests__/controllers/collapsible_controller.test.js +0 -407
- data/__tests__/controllers/combobox_controller.test.js +0 -966
- data/__tests__/controllers/context_menu_controller.test.js +0 -627
- data/__tests__/controllers/date_picker_controller.test.js +0 -636
- data/__tests__/controllers/dialog_controller.test.js +0 -878
- data/__tests__/controllers/drawer_controller.test.js +0 -995
- data/__tests__/controllers/menubar_controller.test.js +0 -736
- data/__tests__/controllers/navigation_menu_controller.test.js +0 -598
- data/__tests__/controllers/popover_controller.test.js +0 -1007
- data/__tests__/controllers/radio_group_controller.test.js +0 -640
- data/__tests__/controllers/resizable_controller.test.js +0 -680
- data/__tests__/controllers/select_controller.test.js +0 -674
- data/__tests__/controllers/sheet_controller.test.js +0 -986
- data/__tests__/controllers/slider_controller.test.js +0 -1036
- data/__tests__/controllers/switch_controller.test.js +0 -424
- data/__tests__/controllers/tabs_controller.test.js +0 -907
- data/__tests__/controllers/toggle_group_controller.test.js +0 -839
- data/__tests__/controllers/tooltip_controller.test.js +0 -808
- data/__tests__/helpers/stimulus-test-helper.js +0 -203
- data/babel.config.cjs +0 -5
- data/bin/console +0 -11
- data/bin/setup +0 -8
- data/jest.config.js +0 -19
- data/jest.setup.js +0 -8
- data/lib/generators/shadcn/component/component_generator.rb +0 -188
- data/lib/generators/shadcn/theme/theme_generator.rb +0 -128
- data/package-lock.json +0 -7415
- data/package.json +0 -68
- data/rollup.config.js +0 -29
data/CLAUDE.md
DELETED
|
@@ -1,463 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md - Development Guide for shadcn-rails
|
|
2
|
-
|
|
3
|
-
This document contains learnings, patterns, and guidelines for future development sessions on this project.
|
|
4
|
-
|
|
5
|
-
## Project Overview
|
|
6
|
-
|
|
7
|
-
shadcn-rails is a Ruby port of [shadcn/ui](https://ui.shadcn.com) for Rails applications. It uses:
|
|
8
|
-
- **ViewComponent** for Ruby-based component architecture
|
|
9
|
-
- **Stimulus** for JavaScript interactivity
|
|
10
|
-
- **Tailwind CSS** for styling
|
|
11
|
-
- **CSS Custom Properties** for theming
|
|
12
|
-
|
|
13
|
-
## Architecture Patterns
|
|
14
|
-
|
|
15
|
-
### Component Structure
|
|
16
|
-
|
|
17
|
-
All components inherit from `Shadcn::BaseComponent` which provides:
|
|
18
|
-
- `cn(*classes)` - Class name merging utility (like clsx + tailwind-merge)
|
|
19
|
-
- `stimulus_data(controller, values, actions)` - Stimulus data attribute builder
|
|
20
|
-
- `build_html_attributes(**attrs)` - HTML attribute builder with class merging
|
|
21
|
-
|
|
22
|
-
```ruby
|
|
23
|
-
# app/components/shadcn/base_component.rb
|
|
24
|
-
class Shadcn::BaseComponent < ViewComponent::Base
|
|
25
|
-
include Shadcn::Rails::Helpers::ClassNameHelper
|
|
26
|
-
|
|
27
|
-
def cn(*classes)
|
|
28
|
-
Shadcn::Rails.cn(*classes)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Component Location
|
|
34
|
-
|
|
35
|
-
Components are located in `app/components/shadcn/`:
|
|
36
|
-
- Each component is a single Ruby file (e.g., `button_component.rb`)
|
|
37
|
-
- Complex components use ViewComponent slots for composition
|
|
38
|
-
- ERB templates are inline using `erb_template` when simple, or separate `.html.erb` files when complex
|
|
39
|
-
|
|
40
|
-
### Stimulus Controllers
|
|
41
|
-
|
|
42
|
-
Controllers are in `app/assets/javascripts/shadcn/controllers/`:
|
|
43
|
-
- Named `{component}_controller.js`
|
|
44
|
-
- Use Stimulus naming convention: `shadcn--{component}`
|
|
45
|
-
- Handle interactivity: open/close states, keyboard navigation, focus management
|
|
46
|
-
|
|
47
|
-
```javascript
|
|
48
|
-
// Example controller pattern
|
|
49
|
-
import { Controller } from "@hotwired/stimulus"
|
|
50
|
-
|
|
51
|
-
export default class extends Controller {
|
|
52
|
-
static targets = ["trigger", "content"]
|
|
53
|
-
static values = { open: Boolean }
|
|
54
|
-
|
|
55
|
-
toggle() {
|
|
56
|
-
this.openValue = !this.openValue
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
openValueChanged() {
|
|
60
|
-
// React to state changes
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### CSS Theming
|
|
66
|
-
|
|
67
|
-
CSS variables are defined in `app/assets/stylesheets/shadcn/base.css`:
|
|
68
|
-
- All colors use HSL format without `hsl()` wrapper: `--primary: 0 0% 9%;`
|
|
69
|
-
- Components use: `hsl(var(--primary))`
|
|
70
|
-
- Dark mode uses `.dark` class on `<html>` element
|
|
71
|
-
|
|
72
|
-
Theme presets are in `lib/shadcn/rails/configuration.rb` with full HSL values for:
|
|
73
|
-
- neutral, slate, stone, zinc, gray
|
|
74
|
-
|
|
75
|
-
## Key Files
|
|
76
|
-
|
|
77
|
-
### Core Library
|
|
78
|
-
- `lib/shadcn/rails.rb` - Main module, configuration, `cn()` helper
|
|
79
|
-
- `lib/shadcn/rails/engine.rb` - Rails engine setup
|
|
80
|
-
- `lib/shadcn/rails/configuration.rb` - Theme configuration with CSS variables
|
|
81
|
-
- `lib/shadcn/rails/class_merger.rb` - Tailwind class conflict resolution
|
|
82
|
-
|
|
83
|
-
### Components (app/components/shadcn/)
|
|
84
|
-
|
|
85
|
-
The library includes 47 components organized by category:
|
|
86
|
-
|
|
87
|
-
**Buttons & Actions**
|
|
88
|
-
| File | Description |
|
|
89
|
-
|------|-------------|
|
|
90
|
-
| `button_component.rb` | 6 variants (default, secondary, destructive, outline, ghost, link), 6 sizes |
|
|
91
|
-
| `toggle_component.rb` | Two-state button with pressed/unpressed states |
|
|
92
|
-
| `toggle_group_component.rb` | Group of toggle buttons (single/multiple selection) |
|
|
93
|
-
| `button_group_component.rb` | Group of related buttons |
|
|
94
|
-
|
|
95
|
-
**Form Inputs**
|
|
96
|
-
| File | Description |
|
|
97
|
-
|------|-------------|
|
|
98
|
-
| `input_component.rb` | Text input with various types |
|
|
99
|
-
| `textarea_component.rb` | Multi-line text input |
|
|
100
|
-
| `label_component.rb` | Form label element |
|
|
101
|
-
| `checkbox_component.rb` | Checkbox with indeterminate state support |
|
|
102
|
-
| `switch_component.rb` | Toggle switch control |
|
|
103
|
-
| `slider_component.rb` | Range slider input |
|
|
104
|
-
| `select_component.rb` | Custom styled select dropdown |
|
|
105
|
-
| `native_select_component.rb` | Native HTML select with styling |
|
|
106
|
-
| `radio_group_component.rb` | Radio button group |
|
|
107
|
-
| `field_component.rb` | Form field wrapper with label/error slots |
|
|
108
|
-
| `input_group_component.rb` | Input with prefix/suffix addons |
|
|
109
|
-
| `input_otp_component.rb` | One-time password input |
|
|
110
|
-
|
|
111
|
-
**Data Display**
|
|
112
|
-
| File | Description |
|
|
113
|
-
|------|-------------|
|
|
114
|
-
| `badge_component.rb` | Labels with variants (default, secondary, destructive, outline) |
|
|
115
|
-
| `avatar_component.rb` | User avatar with image/fallback |
|
|
116
|
-
| `card_component.rb` | Header, content, footer slots |
|
|
117
|
-
| `table_component.rb` | Styled table with header/body/footer |
|
|
118
|
-
| `progress_component.rb` | Progress bar indicator |
|
|
119
|
-
| `skeleton_component.rb` | Loading placeholder |
|
|
120
|
-
| `spinner_component.rb` | Loading spinner animation |
|
|
121
|
-
| `kbd_component.rb` | Keyboard key display |
|
|
122
|
-
| `typography_component.rb` | Text styling utilities |
|
|
123
|
-
| `aspect_ratio_component.rb` | Maintains aspect ratio for media content |
|
|
124
|
-
| `scroll_area_component.rb` | Custom scrollbar container |
|
|
125
|
-
|
|
126
|
-
**Feedback**
|
|
127
|
-
| File | Description |
|
|
128
|
-
|------|-------------|
|
|
129
|
-
| `alert_component.rb` | Callout/notification with variants (default, destructive) |
|
|
130
|
-
| `tooltip_component.rb` | Hover tooltip popup |
|
|
131
|
-
| `toast_component.rb` | Temporary notification messages |
|
|
132
|
-
|
|
133
|
-
**Overlays**
|
|
134
|
-
| File | Description |
|
|
135
|
-
|------|-------------|
|
|
136
|
-
| `dialog_component.rb` | Modal with portal, focus trap |
|
|
137
|
-
| `alert_dialog_component.rb` | Confirmation dialog with cancel/confirm actions |
|
|
138
|
-
| `sheet_component.rb` | Slide-out panel from any edge |
|
|
139
|
-
| `drawer_component.rb` | Bottom sheet drawer |
|
|
140
|
-
| `popover_component.rb` | Floating content panel |
|
|
141
|
-
| `hover_card_component.rb` | Card that appears on hover |
|
|
142
|
-
| `dropdown_menu_component.rb` | Dropdown menu with items, separators, keyboard shortcuts |
|
|
143
|
-
|
|
144
|
-
**Navigation**
|
|
145
|
-
| File | Description |
|
|
146
|
-
|------|-------------|
|
|
147
|
-
| `tabs_component.rb` | Tabbed interface with URL sync support |
|
|
148
|
-
| `accordion_component.rb` | Collapsible sections (single/multiple mode) |
|
|
149
|
-
| `breadcrumb_component.rb` | Navigation breadcrumb trail |
|
|
150
|
-
| `pagination_component.rb` | Page navigation with Kaminari/Pagy/WillPaginate support |
|
|
151
|
-
| `collapsible_component.rb` | Show/hide content section |
|
|
152
|
-
| `separator_component.rb` | Visual divider line |
|
|
153
|
-
| `context_menu_component.rb` | Right-click context menu |
|
|
154
|
-
| `menubar_component.rb` | Horizontal menu bar with dropdowns |
|
|
155
|
-
| `navigation_menu_component.rb` | Navigation with dropdown content |
|
|
156
|
-
| `resizable_component.rb` | Resizable panel groups |
|
|
157
|
-
|
|
158
|
-
### Generators (lib/generators/shadcn/)
|
|
159
|
-
- `install/` - Initial setup generator
|
|
160
|
-
- `component/` - Add individual components
|
|
161
|
-
- `theme/` - Switch color themes
|
|
162
|
-
|
|
163
|
-
### Tests
|
|
164
|
-
- `test/components/` - Component unit tests
|
|
165
|
-
- `test/components/previews/` - Lookbook previews
|
|
166
|
-
- `test/dummy/` - Rails dummy app for integration testing
|
|
167
|
-
|
|
168
|
-
## Development Commands
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
# Install dependencies
|
|
172
|
-
bundle install
|
|
173
|
-
|
|
174
|
-
# Run tests
|
|
175
|
-
bundle exec rake test
|
|
176
|
-
|
|
177
|
-
# Run component tests only
|
|
178
|
-
bundle exec rake test_components
|
|
179
|
-
|
|
180
|
-
# Start dummy app
|
|
181
|
-
cd test/dummy && rails server
|
|
182
|
-
|
|
183
|
-
# Start Lookbook previews
|
|
184
|
-
cd test/dummy && rails lookbook:preview
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Patterns to Follow
|
|
188
|
-
|
|
189
|
-
### 1. Component Variants
|
|
190
|
-
|
|
191
|
-
Use symbols for variant options and define them as constants:
|
|
192
|
-
|
|
193
|
-
```ruby
|
|
194
|
-
VARIANTS = {
|
|
195
|
-
default: "bg-primary text-primary-foreground",
|
|
196
|
-
destructive: "bg-destructive text-destructive-foreground",
|
|
197
|
-
outline: "border border-input bg-background",
|
|
198
|
-
secondary: "bg-secondary text-secondary-foreground",
|
|
199
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
200
|
-
link: "text-primary underline-offset-4 hover:underline"
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
def initialize(variant: :default, **attrs)
|
|
204
|
-
@variant = variant.to_sym
|
|
205
|
-
end
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
### 2. Compound Components with Slots
|
|
209
|
-
|
|
210
|
-
Use ViewComponent's `renders_one` and `renders_many` for composition:
|
|
211
|
-
|
|
212
|
-
```ruby
|
|
213
|
-
renders_one :header, "HeaderComponent"
|
|
214
|
-
renders_many :items, "ItemComponent"
|
|
215
|
-
|
|
216
|
-
class HeaderComponent < ViewComponent::Base
|
|
217
|
-
renders_one :title
|
|
218
|
-
renders_one :description
|
|
219
|
-
end
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### 3. Stimulus Integration
|
|
223
|
-
|
|
224
|
-
Components that need JavaScript should:
|
|
225
|
-
1. Define a Stimulus controller
|
|
226
|
-
2. Use `stimulus_data()` helper in the component
|
|
227
|
-
3. Follow the `shadcn--{name}` naming convention
|
|
228
|
-
|
|
229
|
-
```ruby
|
|
230
|
-
def stimulus_attributes
|
|
231
|
-
stimulus_data(
|
|
232
|
-
"shadcn--dialog",
|
|
233
|
-
{ open: @open },
|
|
234
|
-
{ "trigger->click": "toggle" }
|
|
235
|
-
)
|
|
236
|
-
end
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### 4. Accessibility (ARIA)
|
|
240
|
-
|
|
241
|
-
All interactive components must include proper ARIA attributes:
|
|
242
|
-
- `role` attributes where needed
|
|
243
|
-
- `aria-expanded`, `aria-hidden`, `aria-controls`
|
|
244
|
-
- `aria-labelledby`, `aria-describedby` for relationships
|
|
245
|
-
- Keyboard navigation support
|
|
246
|
-
|
|
247
|
-
### 5. CSS Class Merging
|
|
248
|
-
|
|
249
|
-
Always use `cn()` for class composition to handle Tailwind conflicts:
|
|
250
|
-
|
|
251
|
-
```ruby
|
|
252
|
-
def classes
|
|
253
|
-
cn(
|
|
254
|
-
"base-classes here",
|
|
255
|
-
VARIANTS[@variant],
|
|
256
|
-
SIZES[@size],
|
|
257
|
-
@class_name
|
|
258
|
-
)
|
|
259
|
-
end
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### 6. Test Coverage
|
|
263
|
-
|
|
264
|
-
For each component, create tests in `test/components/`:
|
|
265
|
-
- Test default rendering
|
|
266
|
-
- Test all variants
|
|
267
|
-
- Test all sizes (if applicable)
|
|
268
|
-
- Test slot rendering
|
|
269
|
-
- Test accessibility attributes
|
|
270
|
-
|
|
271
|
-
```ruby
|
|
272
|
-
class ButtonComponentTest < ViewComponent::TestCase
|
|
273
|
-
def test_renders_button
|
|
274
|
-
render_inline(Shadcn::ButtonComponent.new) { "Click" }
|
|
275
|
-
assert_selector "button", text: "Click"
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
def test_renders_variants
|
|
279
|
-
Shadcn::ButtonComponent::VARIANTS.keys.each do |variant|
|
|
280
|
-
render_inline(Shadcn::ButtonComponent.new(variant: variant)) { "Test" }
|
|
281
|
-
assert_selector "button"
|
|
282
|
-
end
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
## Adding New Components
|
|
288
|
-
|
|
289
|
-
1. Create component file in `app/components/shadcn/{name}_component.rb`
|
|
290
|
-
2. Inherit from `Shadcn::BaseComponent`
|
|
291
|
-
3. If interactive, create Stimulus controller in `app/assets/javascripts/shadcn/controllers/{name}_controller.js`
|
|
292
|
-
4. Add controller export to `app/assets/javascripts/shadcn/index.js`
|
|
293
|
-
5. Create test in `test/components/{name}_component_test.rb`
|
|
294
|
-
6. Create Lookbook preview in `test/components/previews/{name}_component_preview.rb`
|
|
295
|
-
7. Add example to dummy app views
|
|
296
|
-
|
|
297
|
-
## Common Issues & Solutions
|
|
298
|
-
|
|
299
|
-
### ViewComponent Preview Nested Render Issue
|
|
300
|
-
|
|
301
|
-
**Problem**: When using nested `render()` calls inside ViewComponent preview slots, the output is a hash like `{args: {}, block: #<Proc:...>, component: #<...>}` instead of rendered HTML.
|
|
302
|
-
|
|
303
|
-
**Solution**: Use raw HTML strings instead of nested render calls in previews:
|
|
304
|
-
|
|
305
|
-
```ruby
|
|
306
|
-
# BAD - nested render returns hash, not HTML
|
|
307
|
-
def default
|
|
308
|
-
render(Shadcn::DialogComponent.new) do |dialog|
|
|
309
|
-
dialog.with_trigger do
|
|
310
|
-
render(Shadcn::ButtonComponent.new(variant: :outline)) { "Open" } # Returns hash!
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
# GOOD - use raw HTML strings or helper methods
|
|
316
|
-
def default
|
|
317
|
-
render(Shadcn::DialogComponent.new) do |dialog|
|
|
318
|
-
dialog.with_trigger do
|
|
319
|
-
button_html(:outline, "Open") # Returns HTML string
|
|
320
|
-
end
|
|
321
|
-
end
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
private
|
|
325
|
-
|
|
326
|
-
def button_html(variant, text, extra_class = nil)
|
|
327
|
-
# Generate HTML string directly
|
|
328
|
-
%(<button class="#{classes}">#{text}</button>).html_safe
|
|
329
|
-
end
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
### Superclass Mismatch Error
|
|
333
|
-
|
|
334
|
-
**Problem**: Rails throws "superclass mismatch for class X" error for nested component classes (e.g., `DialogContentComponent`).
|
|
335
|
-
|
|
336
|
-
**Cause**: Rails Zeitwerk autoloading gets confused when nested classes are reloaded and the parent class definition has changed.
|
|
337
|
-
|
|
338
|
-
**Solution**: Restart the Rails server to clear cached class definitions. This commonly happens when modifying component files with nested classes.
|
|
339
|
-
|
|
340
|
-
### Tailwind Class Conflicts
|
|
341
|
-
|
|
342
|
-
The `ClassMerger` utility handles conflicts like `p-2 p-4` (p-4 wins). If you encounter unresolved conflicts, add the pattern to `CLASS_GROUPS` or `CONFLICT_PATTERNS` in `lib/shadcn/rails/class_merger.rb`.
|
|
343
|
-
|
|
344
|
-
**Pattern Ordering Issue**: When adding regex patterns to `CONFLICT_PATTERNS`, be careful of pattern matching order. The `find_conflict_group` method returns on the first match, so patterns must use negative lookaheads to exclude overlapping cases.
|
|
345
|
-
|
|
346
|
-
Example fix for border classes:
|
|
347
|
-
```ruby
|
|
348
|
-
# PROBLEM: border-0 was matching border_color before border_width
|
|
349
|
-
/^border-(?!solid|dashed|dotted|double|none)/ => :border_color,
|
|
350
|
-
|
|
351
|
-
# SOLUTION: Add [0-9] to the negative lookahead to exclude border widths
|
|
352
|
-
/^border-(?!solid|dashed|dotted|double|none|[0-9])/ => :border_color,
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
This ensures `border-0`, `border-2`, etc. are categorized as `:border_width` (not `:border_color`) so they properly conflict with and override `border`.
|
|
356
|
-
|
|
357
|
-
### Stimulus Controller Not Loading
|
|
358
|
-
|
|
359
|
-
Ensure:
|
|
360
|
-
1. Controller is exported in `index.js`
|
|
361
|
-
2. Controller name matches: `shadcn--{name}`
|
|
362
|
-
3. User has called `registerShadcnControllers(application)` or manually registered
|
|
363
|
-
|
|
364
|
-
### Preview Layout Requirements
|
|
365
|
-
|
|
366
|
-
The `component_preview.html.erb` layout must include:
|
|
367
|
-
1. **Tailwind CSS** via CDN with custom shadcn color tokens
|
|
368
|
-
2. **Stimulus application** initialized and connected
|
|
369
|
-
3. **Inline Stimulus controllers** for all interactive components (Dialog, Sheet, Tabs, Accordion, Checkbox, Switch, Popover, Tooltip)
|
|
370
|
-
4. **CSS animations** for transitions (dialog fade-in, sheet slide-in, etc.)
|
|
371
|
-
|
|
372
|
-
For previews, Stimulus controllers are inlined directly in the layout since the JavaScript bundling isn't available. This is different from production where controllers come from the npm package.
|
|
373
|
-
|
|
374
|
-
### Tabs Controller Method Names
|
|
375
|
-
|
|
376
|
-
The Tabs Stimulus controller uses `selectTab` for the click action, not `select`:
|
|
377
|
-
|
|
378
|
-
```javascript
|
|
379
|
-
// In tabs_controller.js
|
|
380
|
-
selectTab(event) {
|
|
381
|
-
const trigger = event.currentTarget
|
|
382
|
-
const value = trigger.dataset.value
|
|
383
|
-
this.valueValue = value
|
|
384
|
-
}
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
Ensure your triggers use: `data-action="click->shadcn--tabs#selectTab"`
|
|
388
|
-
|
|
389
|
-
### Dark Mode Not Working
|
|
390
|
-
|
|
391
|
-
Check:
|
|
392
|
-
1. CSS variables are loaded
|
|
393
|
-
2. `.dark` class is on `<html>` element (not `<body>`)
|
|
394
|
-
3. Tailwind dark mode is configured as `class` strategy
|
|
395
|
-
|
|
396
|
-
## Documentation Site
|
|
397
|
-
|
|
398
|
-
The `test/dummy` app serves as both a testing environment and documentation site. It includes:
|
|
399
|
-
- Full documentation for all 47 components at `/docs`
|
|
400
|
-
- Live interactive examples that demonstrate each component
|
|
401
|
-
- Code examples stored in `app/code_examples/{component}/` as `.txt` files
|
|
402
|
-
- `erb_example` helper to display code examples with syntax highlighting
|
|
403
|
-
- Showcase page at `/showcase`
|
|
404
|
-
|
|
405
|
-
### Documentation Structure
|
|
406
|
-
|
|
407
|
-
```
|
|
408
|
-
test/dummy/
|
|
409
|
-
├── app/
|
|
410
|
-
│ ├── views/docs/ # Component documentation pages
|
|
411
|
-
│ │ ├── button.html.erb
|
|
412
|
-
│ │ ├── dialog.html.erb
|
|
413
|
-
│ │ └── ...
|
|
414
|
-
│ ├── code_examples/ # Code examples displayed in docs
|
|
415
|
-
│ │ ├── button/
|
|
416
|
-
│ │ │ └── default.txt
|
|
417
|
-
│ │ └── pagination/
|
|
418
|
-
│ │ ├── kaminari_view.txt
|
|
419
|
-
│ │ └── pagy_view.txt
|
|
420
|
-
│ └── helpers/
|
|
421
|
-
│ └── docs_helper.rb # erb_example helper method
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
## Future Improvements
|
|
425
|
-
|
|
426
|
-
**Missing Components** (from shadcn/ui):
|
|
427
|
-
- [ ] Command palette
|
|
428
|
-
- [ ] Combobox
|
|
429
|
-
- [ ] Date Picker
|
|
430
|
-
- [ ] Calendar
|
|
431
|
-
- [ ] Sonner (toast alternative)
|
|
432
|
-
- [ ] Carousel
|
|
433
|
-
- [ ] Chart
|
|
434
|
-
- [ ] Data Table
|
|
435
|
-
- [ ] Sidebar
|
|
436
|
-
|
|
437
|
-
**Enhancements**:
|
|
438
|
-
- [ ] Form builder integration (Rails form helpers)
|
|
439
|
-
- [ ] Hotwire/Turbo Stream integration helpers
|
|
440
|
-
- [ ] RTL support
|
|
441
|
-
- [ ] Visual regression tests with Percy or similar
|
|
442
|
-
- [ ] Dropdown Menu submenus and checkbox/radio items
|
|
443
|
-
- [ ] Drawer gesture support (touch drag to dismiss)
|
|
444
|
-
|
|
445
|
-
**Completed**:
|
|
446
|
-
- [x] 47 core components implemented
|
|
447
|
-
- [x] Full documentation site with live examples
|
|
448
|
-
- [x] Stimulus controllers for all interactive components
|
|
449
|
-
- [x] Pagination with Kaminari/Pagy/WillPaginate adapters
|
|
450
|
-
- [x] CSS animations for dialogs, sheets, toasts, accordions
|
|
451
|
-
- [x] Context Menu component
|
|
452
|
-
- [x] Menubar component
|
|
453
|
-
- [x] Navigation Menu component
|
|
454
|
-
- [x] Resizable panels component
|
|
455
|
-
- [x] Input OTP with group separators
|
|
456
|
-
|
|
457
|
-
## Resources
|
|
458
|
-
|
|
459
|
-
- [shadcn/ui Documentation](https://ui.shadcn.com)
|
|
460
|
-
- [ViewComponent Guide](https://viewcomponent.org)
|
|
461
|
-
- [Stimulus Handbook](https://stimulus.hotwired.dev/handbook/introduction)
|
|
462
|
-
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
|
|
463
|
-
- [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/)
|