shadcn_phlexcomponents 0.1.18 → 0.1.19

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.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +321 -23
  3. data/app/javascript/controllers/accordion_controller.js +101 -90
  4. data/app/javascript/controllers/alert_dialog_controller.js +5 -4
  5. data/app/javascript/controllers/avatar_controller.js +12 -11
  6. data/app/javascript/controllers/checkbox_controller.js +26 -26
  7. data/app/javascript/controllers/collapsible_controller.js +35 -36
  8. data/app/javascript/controllers/combobox_controller.js +262 -231
  9. data/app/javascript/controllers/command_controller.js +205 -184
  10. data/app/javascript/controllers/date_picker_controller.js +252 -253
  11. data/app/javascript/controllers/date_range_picker_controller.js +189 -200
  12. data/app/javascript/controllers/dialog_controller.js +79 -78
  13. data/app/javascript/controllers/dropdown_menu_controller.js +229 -208
  14. data/app/javascript/controllers/dropdown_menu_sub_controller.js +111 -97
  15. data/app/javascript/controllers/form_field_controller.js +17 -16
  16. data/app/javascript/controllers/hover_card_controller.js +69 -71
  17. data/app/javascript/controllers/loading_button_controller.js +11 -10
  18. data/app/javascript/controllers/popover_controller.js +85 -78
  19. data/app/javascript/controllers/progress_controller.js +12 -11
  20. data/app/javascript/controllers/radio_group_controller.js +75 -74
  21. data/app/javascript/controllers/select_controller.js +247 -232
  22. data/app/javascript/controllers/sidebar_controller.js +26 -27
  23. data/app/javascript/controllers/sidebar_trigger_controller.js +12 -9
  24. data/app/javascript/controllers/slider_controller.js +74 -74
  25. data/app/javascript/controllers/switch_controller.js +23 -23
  26. data/app/javascript/controllers/tabs_controller.js +61 -61
  27. data/app/javascript/controllers/theme_switcher_controller.js +28 -27
  28. data/app/javascript/controllers/toast_container_controller.js +45 -31
  29. data/app/javascript/controllers/toast_controller.js +19 -18
  30. data/app/javascript/controllers/toggle_controller.js +17 -17
  31. data/app/javascript/controllers/toggle_group_controller.js +17 -17
  32. data/app/javascript/controllers/tooltip_controller.js +75 -77
  33. data/app/javascript/shadcn_phlexcomponents.js +27 -60
  34. data/app/javascript/utils/command.js +390 -334
  35. data/app/javascript/utils/floating_ui.js +139 -107
  36. data/app/javascript/utils/index.js +253 -190
  37. data/app/typescript/controllers/accordion_controller.ts +2 -0
  38. data/app/typescript/controllers/alert_dialog_controller.ts +2 -0
  39. data/app/typescript/controllers/avatar_controller.ts +2 -0
  40. data/app/typescript/controllers/checkbox_controller.ts +2 -0
  41. data/app/typescript/controllers/collapsible_controller.ts +2 -0
  42. data/app/typescript/controllers/combobox_controller.ts +2 -0
  43. data/app/typescript/controllers/command_controller.ts +2 -0
  44. data/app/typescript/controllers/date_picker_controller.ts +2 -0
  45. data/app/typescript/controllers/date_range_picker_controller.ts +2 -0
  46. data/app/typescript/controllers/dialog_controller.ts +2 -0
  47. data/app/typescript/controllers/dropdown_menu_controller.ts +2 -0
  48. data/app/typescript/controllers/dropdown_menu_sub_controller.ts +2 -0
  49. data/app/typescript/controllers/form_field_controller.ts +2 -0
  50. data/app/typescript/controllers/hover_card_controller.ts +2 -0
  51. data/app/typescript/controllers/loading_button_controller.ts +2 -0
  52. data/app/typescript/controllers/popover_controller.ts +2 -0
  53. data/app/typescript/controllers/progress_controller.ts +2 -0
  54. data/app/typescript/controllers/radio_group_controller.ts +2 -0
  55. data/app/typescript/controllers/select_controller.ts +2 -0
  56. data/app/typescript/controllers/slider_controller.ts +2 -0
  57. data/app/typescript/controllers/switch_controller.ts +2 -0
  58. data/app/typescript/controllers/tabs_controller.ts +2 -0
  59. data/app/typescript/controllers/theme_switcher_controller.ts +2 -0
  60. data/app/typescript/controllers/toast_container_controller.ts +2 -0
  61. data/app/typescript/controllers/toast_controller.ts +2 -0
  62. data/app/typescript/controllers/toggle_controller.ts +2 -0
  63. data/app/typescript/controllers/toggle_group_controller.ts +2 -0
  64. data/app/typescript/controllers/tooltip_controller.ts +2 -0
  65. data/app/typescript/shadcn_phlexcomponents.ts +27 -61
  66. data/app/typescript/utils/index.ts +7 -0
  67. data/lib/install/upgrade_shadcn_phlexcomponents.rb +28 -0
  68. data/lib/shadcn_phlexcomponents/components/accordion.rb +55 -12
  69. data/lib/shadcn_phlexcomponents/components/alert.rb +35 -16
  70. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +52 -12
  71. data/lib/shadcn_phlexcomponents/components/aspect_ratio.rb +33 -2
  72. data/lib/shadcn_phlexcomponents/components/avatar.rb +24 -7
  73. data/lib/shadcn_phlexcomponents/components/badge.rb +23 -18
  74. data/lib/shadcn_phlexcomponents/components/breadcrumb.rb +46 -6
  75. data/lib/shadcn_phlexcomponents/components/button.rb +32 -27
  76. data/lib/shadcn_phlexcomponents/components/card.rb +59 -10
  77. data/lib/shadcn_phlexcomponents/components/checkbox.rb +51 -30
  78. data/lib/shadcn_phlexcomponents/components/checkbox_group.rb +24 -4
  79. data/lib/shadcn_phlexcomponents/components/combobox.rb +212 -69
  80. data/lib/shadcn_phlexcomponents/components/command.rb +156 -52
  81. data/lib/shadcn_phlexcomponents/components/date_picker.rb +134 -48
  82. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +20 -42
  83. data/lib/shadcn_phlexcomponents/components/dialog.rb +80 -26
  84. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +74 -25
  85. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +52 -24
  86. data/lib/shadcn_phlexcomponents/components/form/form_checkbox.rb +1 -1
  87. data/lib/shadcn_phlexcomponents/components/form/form_checkbox_group.rb +1 -1
  88. data/lib/shadcn_phlexcomponents/components/form/form_combobox.rb +1 -1
  89. data/lib/shadcn_phlexcomponents/components/form/form_date_picker.rb +1 -1
  90. data/lib/shadcn_phlexcomponents/components/form/form_date_range_picker.rb +1 -1
  91. data/lib/shadcn_phlexcomponents/components/form/form_error.rb +8 -1
  92. data/lib/shadcn_phlexcomponents/components/form/form_helpers.rb +3 -2
  93. data/lib/shadcn_phlexcomponents/components/form/form_hint.rb +8 -1
  94. data/lib/shadcn_phlexcomponents/components/form/form_input.rb +1 -1
  95. data/lib/shadcn_phlexcomponents/components/form/form_radio_group.rb +1 -1
  96. data/lib/shadcn_phlexcomponents/components/form/form_select.rb +1 -1
  97. data/lib/shadcn_phlexcomponents/components/form/form_slider.rb +1 -1
  98. data/lib/shadcn_phlexcomponents/components/form/form_switch.rb +1 -1
  99. data/lib/shadcn_phlexcomponents/components/form/form_textarea.rb +1 -1
  100. data/lib/shadcn_phlexcomponents/components/form.rb +22 -6
  101. data/lib/shadcn_phlexcomponents/components/hover_card.rb +48 -18
  102. data/lib/shadcn_phlexcomponents/components/input.rb +13 -8
  103. data/lib/shadcn_phlexcomponents/components/label.rb +9 -4
  104. data/lib/shadcn_phlexcomponents/components/link.rb +8 -1
  105. data/lib/shadcn_phlexcomponents/components/pagination.rb +34 -6
  106. data/lib/shadcn_phlexcomponents/components/popover.rb +43 -13
  107. data/lib/shadcn_phlexcomponents/components/progress.rb +37 -6
  108. data/lib/shadcn_phlexcomponents/components/radio_group.rb +41 -15
  109. data/lib/shadcn_phlexcomponents/components/select.rb +99 -42
  110. data/lib/shadcn_phlexcomponents/components/separator.rb +9 -4
  111. data/lib/shadcn_phlexcomponents/components/sheet.rb +87 -21
  112. data/lib/shadcn_phlexcomponents/components/skeleton.rb +8 -1
  113. data/lib/shadcn_phlexcomponents/components/switch.rb +45 -17
  114. data/lib/shadcn_phlexcomponents/components/table.rb +84 -17
  115. data/lib/shadcn_phlexcomponents/components/tabs.rb +36 -12
  116. data/lib/shadcn_phlexcomponents/components/textarea.rb +12 -7
  117. data/lib/shadcn_phlexcomponents/components/toast.rb +46 -20
  118. data/lib/shadcn_phlexcomponents/components/toast_container.rb +19 -14
  119. data/lib/shadcn_phlexcomponents/components/toggle.rb +28 -23
  120. data/lib/shadcn_phlexcomponents/components/tooltip.rb +49 -14
  121. data/lib/shadcn_phlexcomponents/configuration.rb +46 -0
  122. data/lib/shadcn_phlexcomponents/initializers/shadcn_phlexcomponents.rb +28 -0
  123. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  124. data/lib/shadcn_phlexcomponents.rb +12 -1
  125. data/lib/tasks/upgrade.rake +10 -0
  126. metadata +15 -14
  127. data/app/typescript/controllers/sidebar_controller.ts +0 -39
  128. data/app/typescript/controllers/sidebar_trigger_controller.ts +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc485b4a5567a237dd094c0dd76b2c1457c57dec5f62fff98718b6b174e07da2
4
- data.tar.gz: 816bda190d7fab68ae7b551165b1da2e5cc913fe1a16095fa1b5f966f43b6785
3
+ metadata.gz: 1ff13d0989535d069db406d2c9f930cdae7f68500c828c1027263a053559eefd
4
+ data.tar.gz: 14f27e9c3ab82a23e1dc9e018cf1605703a49a3d43b27291e2cf98917740f042
5
5
  SHA512:
6
- metadata.gz: 19ea1d9584e4d004d8ae4db402f3dea2dc423ad5d893075eff90222214ba6c3cf14e292521e47699c6e9c64718bee03861729f8d9a2ae3729f32f3c49cb36d1b
7
- data.tar.gz: 818a134df559fb86fe46a4bb4d757786a4efe77ff31738c1f59f21f7315321a0f6cd5d5e1814bdbc279c096ceae58200d43ee940492edc0937eaf7f5de52ec15
6
+ metadata.gz: 9f5c507e1b4777d8c0f8f456b5746350fcaadc7590e20e743a3ce70daece80edafb7fda632e369f768d1a9032de6cce0ef913a94c190f9478997dbc5ebb743b5
7
+ data.tar.gz: 68bb596923e049534b656793da8d944802c16da9c248216699cc4c98c6609a45496a8fd5cdb7c42e973d068f2fab3aee401c1c9585cc205eb8289d4a77633eab
data/README.md CHANGED
@@ -1,53 +1,351 @@
1
1
  # ShadcnPhlexcomponents
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
3
+ A modern UI component library for Ruby on Rails applications, built with [Phlex](https://www.phlex.fun/) and styled with [Tailwind CSS](https://tailwindcss.com/). Inspired by [shadcn/ui](https://ui.shadcn.com/), this gem provides beautiful, accessible, and highly customizable components for Ruby developers.
4
4
 
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/shadcn_phlexcomponents`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ ## Features
6
6
 
7
- ## Installation
7
+ - 🎨 **50+ Beautiful Components** - Complete UI component library with consistent design
8
+ - ⚡ **Phlex-Powered** - Fast, type-safe Ruby components
9
+ - 🎯 **Stimulus Integration** - Interactive components with modern JavaScript
10
+ - 🎪 **Tailwind CSS** - Utility-first styling with full customization
11
+ - ♿ **Accessibility First** - ARIA-compliant and keyboard navigation support
12
+ - 🌙 **Dark Mode Ready** - Built-in theme switching support
13
+ - 📱 **Responsive Design** - Mobile-first approach
14
+ - 🔧 **Highly Customizable** - Extensive configuration options
15
+ - 🚀 **Easy Installation** - Simple setup with Rails integration
8
16
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
17
+ ## 📦 Installation
10
18
 
11
- Install the gem and add to the application's Gemfile by executing:
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'shadcn_phlexcomponents'
23
+ ```
24
+
25
+ And then execute:
12
26
 
13
27
  ```bash
14
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
28
+ bundle install
15
29
  ```
16
30
 
17
- If bundler is not being used to manage dependencies, install the gem by executing:
31
+ Or install it yourself as:
18
32
 
19
33
  ```bash
20
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
34
+ gem install shadcn_phlexcomponents
35
+ ```
36
+
37
+ ### Rails Integration
38
+
39
+ After installing the gem, run the installer to set up the necessary files:
40
+
41
+ ```bash
42
+ rails generate install:shadcn_phlexcomponents
43
+ ```
44
+
45
+ This will:
46
+ - Copy JavaScript controllers and utilities
47
+ - Set up Tailwind CSS configuration
48
+ - Add Stimulus integration
49
+ - Configure component stylesheets
50
+
51
+ ## 🚀 Quick Start
52
+
53
+ ### Basic Usage
54
+
55
+ ```ruby
56
+ # In your view or component
57
+ class MyView < Phlex::HTML
58
+ def view_template
59
+ render ShadcnPhlexcomponents::Button.new(variant: :primary) do
60
+ "Click me!"
61
+ end
62
+ end
63
+ end
64
+ ```
65
+
66
+ ### With Rails Helpers
67
+
68
+ ```erb
69
+ <%# In your ERB templates %>
70
+ <%= render ShadcnPhlexcomponents::Card.new do %>
71
+ <%= render ShadcnPhlexcomponents::Card::Header.new do %>
72
+ <h3>Card Title</h3>
73
+ <% end %>
74
+ <%= render ShadcnPhlexcomponents::Card::Content.new do %>
75
+ <p>Card content goes here.</p>
76
+ <% end %>
77
+ <% end %>
78
+ ```
79
+
80
+ ## 🧩 Available Components
81
+
82
+ ### Layout & Structure
83
+ - **Aspect Ratio** - Maintain aspect ratios for media content
84
+ - **Card** - Flexible content containers with header, content, and footer
85
+ - **Separator** - Visual dividers for content sections
86
+ - **Sheet** - Slide-out panels and drawers
87
+ - **Skeleton** - Loading placeholders
88
+
89
+ ### Navigation
90
+ - **Breadcrumb** - Navigation hierarchy display
91
+ - **Pagination** - Page navigation controls
92
+ - **Tabs** - Tabbed content organization
93
+
94
+ ### Form Components
95
+ - **Button** - Primary action triggers with multiple variants
96
+ - **Input** - Text input fields with validation states
97
+ - **Textarea** - Multi-line text input
98
+ - **Label** - Accessible form labels
99
+ - **Checkbox** - Single and grouped checkboxes
100
+ - **Radio Group** - Radio button selections
101
+ - **Select** - Dropdown selections
102
+ - **Switch** - Toggle switches
103
+ - **Slider** - Range input controls
104
+ - **Date Picker** - Single date selection
105
+ - **Date Range Picker** - Date range selection
106
+ - **Combobox** - Searchable select dropdowns
107
+
108
+ ### Interactive Components
109
+ - **Accordion** - Collapsible content sections
110
+ - **Alert Dialog** - Modal confirmations and alerts
111
+ - **Dialog** - Modal dialogs and overlays
112
+ - **Dropdown Menu** - Context menus and actions
113
+ - **Hover Card** - Contextual information on hover
114
+ - **Popover** - Floating content containers
115
+ - **Tooltip** - Helpful text on hover
116
+ - **Command** - Command palette interface
117
+ - **Collapsible** - Expandable content areas
118
+
119
+ ### Feedback & Status
120
+ - **Alert** - Status messages and notifications
121
+ - **Badge** - Labels and status indicators
122
+ - **Progress** - Progress bars and indicators
123
+ - **Toast** - Notification messages
124
+ - **Loading Button** - Buttons with loading states
125
+
126
+ ### Display Components
127
+ - **Avatar** - User profile images with fallbacks
128
+ - **Table** - Data tables with sorting and styling
129
+ - **Toggle** - Binary state toggles
130
+
131
+ ### Utilities
132
+ - **Link** - Styled navigation links
133
+ - **Theme Switcher** - Light/dark mode toggle
134
+
135
+ ## 🎨 Customization
136
+
137
+ ### Component Variants
138
+
139
+ Most components support multiple variants for different use cases:
140
+
141
+ ```ruby
142
+ # Button variants
143
+ render ShadcnPhlexcomponents::Button.new(variant: :default) { "Default" }
144
+ render ShadcnPhlexcomponents::Button.new(variant: :destructive) { "Delete" }
145
+ render ShadcnPhlexcomponents::Button.new(variant: :outline) { "Cancel" }
146
+ render ShadcnPhlexcomponents::Button.new(variant: :ghost) { "Ghost" }
147
+ render ShadcnPhlexcomponents::Button.new(variant: :link) { "Link" }
148
+
149
+ # Button sizes
150
+ render ShadcnPhlexcomponents::Button.new(size: :sm) { "Small" }
151
+ render ShadcnPhlexcomponents::Button.new(size: :default) { "Default" }
152
+ render ShadcnPhlexcomponents::Button.new(size: :lg) { "Large" }
153
+ render ShadcnPhlexcomponents::Button.new(size: :icon) { icon(:plus) }
154
+ ```
155
+
156
+ ### Global Configuration
157
+
158
+ Configure default component styles in an initializer:
159
+
160
+ ```ruby
161
+ # config/initializers/shadcn_phlexcomponents.rb
162
+ ShadcnPhlexcomponents.configure do |config|
163
+ config.button = {
164
+ base: "custom-base-classes",
165
+ variants: {
166
+ variant: {
167
+ primary: "bg-blue-600 text-white hover:bg-blue-700",
168
+ secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300"
169
+ }
170
+ }
171
+ }
172
+ end
173
+ ```
174
+
175
+ ### CSS Customization
176
+
177
+ Override component styles using Tailwind CSS:
178
+
179
+ ```css
180
+ /* app/assets/stylesheets/application.css */
181
+ .shadcn-button {
182
+ @apply font-semibold transition-all duration-200;
183
+ }
184
+
185
+ .shadcn-button--primary {
186
+ @apply bg-brand-primary hover:bg-brand-primary-dark;
187
+ }
188
+ ```
189
+
190
+ ## 🎯 Interactive Components
191
+
192
+ Components with JavaScript functionality automatically include Stimulus controllers:
193
+
194
+ ```ruby
195
+ # Accordion with automatic JavaScript behavior
196
+ render ShadcnPhlexcomponents::Accordion.new do
197
+ render ShadcnPhlexcomponents::Accordion::Item.new(value: "item-1") do
198
+ render ShadcnPhlexcomponents::Accordion::Trigger.new { "Section 1" }
199
+ render ShadcnPhlexcomponents::Accordion::Content.new do
200
+ "Content for section 1"
201
+ end
202
+ end
203
+ end
204
+
205
+ # Dialog with modal behavior
206
+ render ShadcnPhlexcomponents::Dialog.new do
207
+ render ShadcnPhlexcomponents::Dialog::Trigger.new do
208
+ render ShadcnPhlexcomponents::Button.new { "Open Dialog" }
209
+ end
210
+ render ShadcnPhlexcomponents::Dialog::Content.new do
211
+ render ShadcnPhlexcomponents::Dialog::Header.new do
212
+ render ShadcnPhlexcomponents::Dialog::Title.new { "Dialog Title" }
213
+ end
214
+ "Dialog content goes here"
215
+ end
216
+ end
217
+ ```
218
+
219
+ ## 🌙 Dark Mode
220
+
221
+ Enable dark mode support by adding the theme switcher:
222
+
223
+ ```ruby
224
+ # In your layout
225
+ render ShadcnPhlexcomponents::ThemeSwitcher.new
21
226
  ```
22
227
 
23
- ## Usage
228
+ Components automatically adapt to dark mode using Tailwind's `dark:` classes.
24
229
 
25
- TODO: Write usage instructions here
230
+ ## 📋 Form Integration
26
231
 
27
- ## Development
232
+ Components work seamlessly with Rails form helpers:
233
+
234
+ ```ruby
235
+ # Using with Rails forms
236
+ form_with model: @user do |form|
237
+ div(class: "space-y-4") do
238
+ render ShadcnPhlexcomponents::Form::FormField.new(form: form, name: :name) do
239
+ render ShadcnPhlexcomponents::Label.new { "Name" }
240
+ render ShadcnPhlexcomponents::Input.new(
241
+ name: "user[name]",
242
+ value: @user.name,
243
+ placeholder: "Enter your name"
244
+ )
245
+ end
246
+
247
+ render ShadcnPhlexcomponents::Button.new(type: :submit) { "Save" }
248
+ end
249
+ end
250
+ ```
251
+
252
+ ## 🛠️ Development
28
253
 
29
254
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
255
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
256
+ ### Available Commands
257
+
258
+ ```bash
259
+ # Run tests
260
+ rake test
261
+
262
+ # Run linting
263
+ rake rubocop
264
+ rubocop
265
+
266
+ # Build JavaScript assets
267
+ yarn build
268
+
269
+ # Install gem locally
270
+ bundle exec rake install
271
+
272
+ # Release new version
273
+ bundle exec rake release
274
+ ```
275
+
276
+ ### Component Development
277
+
278
+ When creating new components:
279
+
280
+ 1. Inherit from `ShadcnPhlexcomponents::Base`
281
+ 2. Use `class_variants` for styling variations
282
+ 3. Add Stimulus controllers for interactivity
283
+ 4. Include comprehensive tests
284
+ 5. Follow existing naming conventions
285
+
286
+ ## 🧪 Testing
287
+
288
+ The gem includes comprehensive test coverage:
289
+
290
+ ```bash
291
+ # Run all tests
292
+ rake test
293
+
294
+ # Run specific test file
295
+ ruby test/test_button.rb
296
+
297
+ # Run with coverage
298
+ rake test
299
+ ```
300
+
301
+ ## 📚 Dependencies
302
+
303
+ ### Ruby Dependencies
304
+ - **Rails** (~> 8.0) - Web framework
305
+ - **Phlex Rails** (~> 2.1) - Component framework
306
+ - **Class Variants** (~> 1.1) - CSS class management
307
+ - **Lucide Rails** (~> 0.5.1) - Icon library
308
+ - **Tailwind Merge** (~> 1.0) - CSS class merging
309
+
310
+ ### JavaScript Dependencies
311
+ - **Stimulus** (^3.2.2) - JavaScript framework
312
+ - **Floating UI** (^1.7.2) - Positioning library
313
+ - **Day.js** (^1.11.13) - Date manipulation
314
+ - **Fuse.js** (^7.1.0) - Fuzzy searching
315
+ - **DOMPurify** (^3.2.6) - HTML sanitization
316
+
317
+ ## 🤝 Contributing
32
318
 
33
- ## Contributing
319
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sean-yeoh/shadcn_phlexcomponents. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/sean-yeoh/shadcn_phlexcomponents/blob/main/CODE_OF_CONDUCT.md).
34
320
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/shadcn_phlexcomponents. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/shadcn_phlexcomponents/blob/main/CODE_OF_CONDUCT.md).
321
+ ### Contributing Guidelines
36
322
 
37
- ## Code of Conduct
323
+ 1. Fork the repository
324
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
325
+ 3. Add tests for your changes
326
+ 4. Ensure all tests pass (`rake test`)
327
+ 5. Run the linter (`rake rubocop`)
328
+ 6. Commit your changes (`git commit -am 'Add some feature'`)
329
+ 7. Push to the branch (`git push origin my-new-feature`)
330
+ 8. Create a new Pull Request
38
331
 
39
- Everyone interacting in the ShadcnPhlexcomponents project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/shadcn_phlexcomponents/blob/main/CODE_OF_CONDUCT.md).
332
+ ## 📄 License
40
333
 
41
- ## [Unreleased] - YYYY-MM-DD
334
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
42
335
 
43
- ### Added
336
+ ## 🙏 Acknowledgments
44
337
 
45
- ### Changed
338
+ - Inspired by [shadcn/ui](https://ui.shadcn.com/) - The original React component library
339
+ - Built with [Phlex](https://www.phlex.fun/) - Ruby HTML components
340
+ - Styled with [Tailwind CSS](https://tailwindcss.com/) - Utility-first CSS framework
341
+ - Icons provided by [Lucide](https://lucide.dev/) - Beautiful open source icons
46
342
 
47
- ### Deprecated
343
+ ## 📞 Support
48
344
 
49
- ### Removed
345
+ - 📖 [Documentation](https://github.com/sean-yeoh/shadcn_phlexcomponents)
346
+ - 🐛 [Issues](https://github.com/sean-yeoh/shadcn_phlexcomponents/issues)
347
+ - 💬 [Discussions](https://github.com/sean-yeoh/shadcn_phlexcomponents/discussions)
50
348
 
51
- ### Fixed
349
+ ---
52
350
 
53
- ### Security
351
+ Made with ❤️ by [Sean Yeoh](https://github.com/sean-yeoh)
@@ -1,97 +1,108 @@
1
- import { Controller } from '@hotwired/stimulus';
2
- import { showContent, hideContent, getNextEnabledIndex, getPreviousEnabledIndex, } from '../utils';
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import {
3
+ showContent,
4
+ hideContent,
5
+ getNextEnabledIndex,
6
+ getPreviousEnabledIndex,
7
+ } from "../utils";
3
8
  const AccordionController = class extends Controller {
4
- // targets
5
- static targets = ['item', 'trigger', 'content'];
6
- // values
7
- static values = { openItems: Array };
8
- connect() {
9
- this.multiple = this.element.dataset.multiple === 'true';
9
+ static name = "accordion";
10
+ // targets
11
+ static targets = ["item", "trigger", "content"];
12
+ // values
13
+ static values = { openItems: Array };
14
+ connect() {
15
+ this.multiple = this.element.dataset.multiple === "true";
16
+ }
17
+ contentTargetConnected(content) {
18
+ setTimeout(() => {
19
+ this.setContentHeight(content);
20
+ }, 100);
21
+ }
22
+ toggle(event) {
23
+ const trigger = event.currentTarget;
24
+ const item = this.itemTargets.find((item) => {
25
+ return item.contains(trigger);
26
+ });
27
+ if (!item) return;
28
+ const value = item.dataset.value;
29
+ const isOpen = this.openItemsValue.includes(value);
30
+ if (isOpen) {
31
+ this.openItemsValue = this.openItemsValue.filter((v) => v !== value);
32
+ } else {
33
+ if (this.multiple) {
34
+ this.openItemsValue = [...this.openItemsValue, value];
35
+ } else {
36
+ this.openItemsValue = [value];
37
+ }
10
38
  }
11
- contentTargetConnected(content) {
12
- setTimeout(() => {
13
- this.setContentHeight(content);
14
- }, 100);
39
+ }
40
+ focusTrigger(event) {
41
+ const trigger = event.currentTarget;
42
+ const key = event.key;
43
+ const focusableTriggers = this.triggerTargets.filter(
44
+ (trigger) => !trigger.disabled,
45
+ );
46
+ const index = focusableTriggers.indexOf(trigger);
47
+ let newIndex = 0;
48
+ if (key === "ArrowUp") {
49
+ newIndex = getPreviousEnabledIndex({
50
+ items: focusableTriggers,
51
+ currentIndex: index,
52
+ wrapAround: true,
53
+ });
54
+ } else {
55
+ newIndex = getNextEnabledIndex({
56
+ items: focusableTriggers,
57
+ currentIndex: index,
58
+ wrapAround: true,
59
+ });
15
60
  }
16
- toggle(event) {
17
- const trigger = event.currentTarget;
18
- const item = this.itemTargets.find((item) => {
19
- return item.contains(trigger);
61
+ focusableTriggers[newIndex].focus();
62
+ }
63
+ openItemsValueChanged(openItems) {
64
+ this.itemTargets.forEach((item) => {
65
+ const itemValue = item.dataset.value;
66
+ const trigger = this.triggerTargets.find((trigger) =>
67
+ item.contains(trigger),
68
+ );
69
+ const content = this.contentTargets.find((content) =>
70
+ item.contains(content),
71
+ );
72
+ if (openItems.includes(itemValue)) {
73
+ showContent({
74
+ trigger,
75
+ content: content,
76
+ contentContainer: content,
20
77
  });
21
- if (!item)
22
- return;
23
- const value = item.dataset.value;
24
- const isOpen = this.openItemsValue.includes(value);
25
- if (isOpen) {
26
- this.openItemsValue = this.openItemsValue.filter((v) => v !== value);
27
- }
28
- else {
29
- if (this.multiple) {
30
- this.openItemsValue = [...this.openItemsValue, value];
31
- }
32
- else {
33
- this.openItemsValue = [value];
34
- }
35
- }
36
- }
37
- focusTrigger(event) {
38
- const trigger = event.currentTarget;
39
- const key = event.key;
40
- const focusableTriggers = this.triggerTargets.filter((trigger) => !trigger.disabled);
41
- const index = focusableTriggers.indexOf(trigger);
42
- let newIndex = 0;
43
- if (key === 'ArrowUp') {
44
- newIndex = getPreviousEnabledIndex({
45
- items: focusableTriggers,
46
- currentIndex: index,
47
- wrapAround: true,
48
- });
49
- }
50
- else {
51
- newIndex = getNextEnabledIndex({
52
- items: focusableTriggers,
53
- currentIndex: index,
54
- wrapAround: true,
55
- });
56
- }
57
- focusableTriggers[newIndex].focus();
58
- }
59
- openItemsValueChanged(openItems) {
60
- this.itemTargets.forEach((item) => {
61
- const itemValue = item.dataset.value;
62
- const trigger = this.triggerTargets.find((trigger) => item.contains(trigger));
63
- const content = this.contentTargets.find((content) => item.contains(content));
64
- if (openItems.includes(itemValue)) {
65
- showContent({
66
- trigger,
67
- content: content,
68
- contentContainer: content,
69
- });
70
- }
71
- else {
72
- hideContent({
73
- trigger,
74
- content: content,
75
- contentContainer: content,
76
- });
77
- }
78
- });
79
- }
80
- setContentHeight(element) {
81
- const height = this.getContentHeight(element) || element.getBoundingClientRect().height;
82
- element.style.setProperty('--radix-accordion-content-height', `${height}px`);
83
- }
84
- getContentHeight(el) {
85
- const clone = el.cloneNode(true);
86
- Object.assign(clone.style, {
87
- display: 'block',
88
- position: 'absolute',
89
- visibility: 'hidden',
78
+ } else {
79
+ hideContent({
80
+ trigger,
81
+ content: content,
82
+ contentContainer: content,
90
83
  });
91
- document.body.appendChild(clone);
92
- const height = clone.getBoundingClientRect().height;
93
- document.body.removeChild(clone);
94
- return height;
95
- }
84
+ }
85
+ });
86
+ }
87
+ setContentHeight(element) {
88
+ const height =
89
+ this.getContentHeight(element) || element.getBoundingClientRect().height;
90
+ element.style.setProperty(
91
+ "--radix-accordion-content-height",
92
+ `${height}px`,
93
+ );
94
+ }
95
+ getContentHeight(el) {
96
+ const clone = el.cloneNode(true);
97
+ Object.assign(clone.style, {
98
+ display: "block",
99
+ position: "absolute",
100
+ visibility: "hidden",
101
+ });
102
+ document.body.appendChild(clone);
103
+ const height = clone.getBoundingClientRect().height;
104
+ document.body.removeChild(clone);
105
+ return height;
106
+ }
96
107
  };
97
108
  export { AccordionController };
@@ -1,7 +1,8 @@
1
- import { DialogController } from './dialog_controller';
1
+ import { DialogController } from "./dialog_controller";
2
2
  const AlertDialogController = class extends DialogController {
3
- onDOMClick() {
4
- return;
5
- }
3
+ static name = "alert-dialog";
4
+ onDOMClick() {
5
+ return;
6
+ }
6
7
  };
7
8
  export { AlertDialogController };
@@ -1,14 +1,15 @@
1
- import { Controller } from '@hotwired/stimulus';
1
+ import { Controller } from "@hotwired/stimulus";
2
2
  const AvatarController = class extends Controller {
3
- // targets
4
- static targets = ['image', 'fallback'];
5
- connect() {
6
- this.imageTarget.onerror = () => {
7
- if (this.hasFallbackTarget) {
8
- this.fallbackTarget.classList.remove('hidden');
9
- }
10
- this.imageTarget.classList.add('hidden');
11
- };
12
- }
3
+ static name = "avatar";
4
+ // targets
5
+ static targets = ["image", "fallback"];
6
+ connect() {
7
+ this.imageTarget.onerror = () => {
8
+ if (this.hasFallbackTarget) {
9
+ this.fallbackTarget.classList.remove("hidden");
10
+ }
11
+ this.imageTarget.classList.add("hidden");
12
+ };
13
+ }
13
14
  };
14
15
  export { AvatarController };
@@ -1,30 +1,30 @@
1
- import { Controller } from '@hotwired/stimulus';
1
+ import { Controller } from "@hotwired/stimulus";
2
2
  const CheckboxController = class extends Controller {
3
- // targets
4
- static targets = ['input', 'indicator'];
5
- // values
6
- static values = {
7
- isChecked: Boolean,
8
- };
9
- toggle() {
10
- this.isCheckedValue = !this.isCheckedValue;
11
- }
12
- preventDefault(event) {
13
- event.preventDefault();
14
- }
15
- isCheckedValueChanged(isChecked) {
16
- if (isChecked) {
17
- this.element.ariaChecked = 'true';
18
- this.element.dataset.state = 'checked';
19
- this.inputTarget.checked = true;
20
- this.indicatorTarget.classList.remove('hidden');
21
- }
22
- else {
23
- this.element.ariaChecked = 'false';
24
- this.element.dataset.state = 'unchecked';
25
- this.inputTarget.checked = false;
26
- this.indicatorTarget.classList.add('hidden');
27
- }
3
+ static name = "checkbox";
4
+ // targets
5
+ static targets = ["input", "indicator"];
6
+ // values
7
+ static values = {
8
+ isChecked: Boolean,
9
+ };
10
+ toggle() {
11
+ this.isCheckedValue = !this.isCheckedValue;
12
+ }
13
+ preventDefault(event) {
14
+ event.preventDefault();
15
+ }
16
+ isCheckedValueChanged(isChecked) {
17
+ if (isChecked) {
18
+ this.element.ariaChecked = "true";
19
+ this.element.dataset.state = "checked";
20
+ this.inputTarget.checked = true;
21
+ this.indicatorTarget.classList.remove("hidden");
22
+ } else {
23
+ this.element.ariaChecked = "false";
24
+ this.element.dataset.state = "unchecked";
25
+ this.inputTarget.checked = false;
26
+ this.indicatorTarget.classList.add("hidden");
28
27
  }
28
+ }
29
29
  };
30
30
  export { CheckboxController };