clsx-rails 2.0.0 → 3.0.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/CHANGELOG.md +26 -26
- data/README.md +122 -92
- data/lib/clsx/{version.rb → rails/version.rb} +3 -1
- data/lib/clsx-rails.rb +4 -3
- metadata +23 -10
- data/CLAUDE.md +0 -54
- data/lib/clsx/helper.rb +0 -120
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2da83a9921c3d24cf5c9743bcaf1a11a63bae43f38f7ba197116436f8b2471d5
|
|
4
|
+
data.tar.gz: 280c9dfc3fc501ce352d755d620f49ec4e58b8c6de7e976487cd80ccf358e700
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 33c8950cb1a10e94b22fc4a442dba3181dab708f7d1d6abec2bbd0b42aae009832aebcbd1c963c6050fa0ba397f4e4f960743675679d7ad46c507a3b2d9efb52
|
|
7
|
+
data.tar.gz: 47bb02df172c9c3d98251234112eaaef97b150d39b49c1e51a1fc5caf6d41c310719af75de32be86a15e7cb3a6062dd67fdbbe90e2c9765d9f3907f51399737f
|
data/CHANGELOG.md
CHANGED
|
@@ -4,54 +4,54 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and to [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
6
6
|
|
|
7
|
-
## Unreleased
|
|
7
|
+
## Unreleased
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## v3.0.0 (2026-02-13)
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Codecov badge and configuration
|
|
10
13
|
|
|
11
|
-
###
|
|
12
|
-
-
|
|
14
|
+
### Changed
|
|
15
|
+
- BREAKING: Use clsx-ruby gem as core engine instead of bundled implementation
|
|
16
|
+
- BREAKING: Require Ruby 3.2+ (was 3.1+) and Rails 7.2+ (was 7.1+)
|
|
17
|
+
- Version constant moved from `Clsx::VERSION` to `Clsx::Rails::VERSION`
|
|
18
|
+
- Benchmarks now compare against Rails `class_names` instead of internal versions
|
|
13
19
|
|
|
14
|
-
###
|
|
15
|
-
-
|
|
16
|
-
- Add Rails 8.0, 8.1, and edge support
|
|
20
|
+
### Removed
|
|
21
|
+
- Bundled clsx algorithm (now provided by clsx-ruby dependency)
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
## v2.0.0 (2025-01-11)
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- BREAKING: Drop Rails 6.1 and 7.0 support, require Rails 7.1+
|
|
19
27
|
- Rewrite algorithm for 2-5x performance improvement
|
|
20
28
|
- Add fast-paths for single string, string array, and simple hash
|
|
21
29
|
- Use Hash-based deduplication instead of Array + `uniq!`
|
|
22
30
|
- Use `Symbol#name` instead of `to_s` for faster symbol conversion
|
|
23
31
|
- Use direct class comparison for type checking
|
|
24
32
|
- Remove unused `require 'set'`
|
|
25
|
-
|
|
26
|
-
### Chore
|
|
27
33
|
- Refactor benchmark infrastructure (data.rb, original.rb, quick.rb, run.rb)
|
|
28
|
-
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- Ruby 3.4 support
|
|
37
|
+
- Rails 8.0, 8.1, and edge support
|
|
38
|
+
- CLAUDE.md for AI coding assistants
|
|
39
|
+
|
|
40
|
+
### Tests
|
|
41
|
+
- Improve test coverage to 100% line and 100% branch coverage
|
|
29
42
|
|
|
30
43
|
## v1.0.1 (2024-03-04)
|
|
31
44
|
|
|
32
|
-
###
|
|
45
|
+
### Changed
|
|
33
46
|
- Speeds up the performance by 2x [`32236ed`](https://github.com/svyatov/clsx-rails/commit/32236ed)
|
|
34
|
-
|
|
35
|
-
### Chore
|
|
36
47
|
- Fixes CI action [`f1b948c`](https://github.com/svyatov/clsx-rails/commit/f1b948c)
|
|
37
48
|
- Upload code coverage to CodeCov for the latest combination of Ruby and ActionView only [`4e5d768`](https://github.com/svyatov/clsx-rails/commit/4e5d768)
|
|
38
49
|
|
|
39
|
-
###
|
|
40
|
-
- Changelog update + version bump [`13b408d`](https://github.com/svyatov/clsx-rails/commit/13b408d)
|
|
50
|
+
### Added
|
|
41
51
|
- Adds information about supported Ruby and Rails version [skip ci] [`2e6483f`](https://github.com/svyatov/clsx-rails/commit/2e6483f)
|
|
42
52
|
- Adds link to the CodeCov badge, switch to Conventional Commits [`b48cc84`](https://github.com/svyatov/clsx-rails/commit/b48cc84)
|
|
43
|
-
|
|
44
|
-
### Other
|
|
45
|
-
- Updates CI badge [skip ci] [`a829613`](https://github.com/svyatov/clsx-rails/commit/a829613)
|
|
46
53
|
- Adds code coverage tracking [`0c5d34c`](https://github.com/svyatov/clsx-rails/commit/0c5d34c)
|
|
47
|
-
- Ignore ruby-head in the CI matrix [`f3ab4df`](https://github.com/svyatov/clsx-rails/commit/f3ab4df)
|
|
48
|
-
- Better name for the GitHub Action job [`a28adb7`](https://github.com/svyatov/clsx-rails/commit/a28adb7)
|
|
49
|
-
- Adds badges, fixes rubocop configuration for CI [`56fab44`](https://github.com/svyatov/clsx-rails/commit/56fab44)
|
|
50
|
-
- Create dependabot.yml [`ed1e0eb`](https://github.com/svyatov/clsx-rails/commit/ed1e0eb)
|
|
51
|
-
- Fixes GitHub Actions [`f58a4b2`](https://github.com/svyatov/clsx-rails/commit/f58a4b2)
|
|
52
54
|
|
|
53
55
|
## v1.0.0 (2024-03-03)
|
|
54
56
|
|
|
55
|
-
### Other
|
|
56
57
|
- Initial commit [`f65b9b8`](https://github.com/svyatov/clsx-rails/commit/f65b9b8)
|
|
57
|
-
|
data/README.md
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
# clsx-rails [](https://rubygems.org/gems/clsx-rails) [](https://app.codecov.io/gh/svyatov/clsx-rails) [](https://github.com/svyatov/clsx-rails/actions?query=workflow%3ACI) [](LICENSE.txt)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Rails view helper for constructing CSS class strings conditionally. Powered by [clsx-ruby](https://github.com/svyatov/clsx-ruby).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
It provides a simple way to conditionally apply classes to HTML elements in Rails views.
|
|
7
|
-
It is especially useful when you have a lot of conditional classes and you want to keep your views clean and readable.
|
|
5
|
+
Automatically adds `clsx` and `cn` helpers to all Rails views. The fastest alternative to Rails `class_names`.
|
|
8
6
|
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
Ruby 3.1+ and Rails 7.1+ are supported.
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
Install the gem and add to the application's Gemfile by executing:
|
|
7
|
+
## Quick Start
|
|
16
8
|
|
|
17
9
|
```bash
|
|
18
10
|
bundle add clsx-rails
|
|
@@ -21,14 +13,46 @@ bundle add clsx-rails
|
|
|
21
13
|
Or add it manually to the Gemfile:
|
|
22
14
|
|
|
23
15
|
```ruby
|
|
24
|
-
gem 'clsx-rails', '~>
|
|
16
|
+
gem 'clsx-rails', '~> 3.0'
|
|
25
17
|
```
|
|
26
18
|
|
|
27
|
-
|
|
19
|
+
That's it — `clsx` and `cn` are now available in all your views:
|
|
20
|
+
|
|
21
|
+
```erb
|
|
22
|
+
<%= tag.div class: clsx('btn', 'btn-primary', active: @active) do %>
|
|
23
|
+
Click me
|
|
24
|
+
<% end %>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Why clsx-rails over Rails `class_names`?
|
|
28
|
+
|
|
29
|
+
### Faster
|
|
30
|
+
|
|
31
|
+
**3-8x faster** than Rails `class_names` across every scenario:
|
|
32
|
+
|
|
33
|
+
| Scenario | clsx | Rails `class_names` | Speedup |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| Single string | 7.6M i/s | 911K i/s | **8.5x** |
|
|
36
|
+
| String + hash | 2.4M i/s | 580K i/s | **4.1x** |
|
|
37
|
+
| String array | 1.4M i/s | 357K i/s | **4.0x** |
|
|
38
|
+
| Multiple strings | 1.5M i/s | 414K i/s | **3.7x** |
|
|
39
|
+
| Hash | 2.2M i/s | 670K i/s | **3.3x** |
|
|
40
|
+
| Mixed types | 852K i/s | 367K i/s | **2.3x** |
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
<sup>Ruby 4.0.1, Apple M1 Pro. Reproduce: `bundle exec ruby benchmark/run.rb`</sup>
|
|
43
|
+
|
|
44
|
+
### More features
|
|
45
|
+
|
|
46
|
+
| Feature | clsx-rails | Rails `class_names` |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| Conditional classes | yes | yes |
|
|
49
|
+
| Auto-deduplication | yes | yes |
|
|
50
|
+
| 3-8x faster | yes | no |
|
|
51
|
+
| Returns `nil` when empty | yes | no (returns `""`) |
|
|
52
|
+
| Complex hash keys | yes | no |
|
|
53
|
+
| Short `cn` alias | yes | no |
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
32
56
|
|
|
33
57
|
```ruby
|
|
34
58
|
# Strings (variadic)
|
|
@@ -36,7 +60,7 @@ clsx('foo', true && 'bar', 'baz')
|
|
|
36
60
|
# => 'foo bar baz'
|
|
37
61
|
|
|
38
62
|
# Hashes
|
|
39
|
-
cn(
|
|
63
|
+
cn(foo: true, bar: false, baz: a_method_that_returns_true)
|
|
40
64
|
# => 'foo baz'
|
|
41
65
|
|
|
42
66
|
# Hashes (variadic)
|
|
@@ -52,124 +76,130 @@ clsx(['foo'], ['', nil, false, 'bar'], [['baz', [['hello'], 'there']]])
|
|
|
52
76
|
# => 'foo bar baz hello there'
|
|
53
77
|
|
|
54
78
|
# Kitchen sink (with nesting)
|
|
55
|
-
cn('foo', ['bar', { baz: false, bat: nil }, ['hello', ['world']]], 'cya')
|
|
79
|
+
cn('foo', ['bar', { baz: false, bat: nil }, ['hello', ['world']]], 'cya')
|
|
56
80
|
# => 'foo bar hello world cya'
|
|
57
81
|
```
|
|
58
82
|
|
|
83
|
+
### ERB
|
|
84
|
+
|
|
59
85
|
```erb
|
|
60
|
-
<%= tag.div class: clsx('foo', 'baz', 'is-active'
|
|
86
|
+
<%= tag.div class: clsx('foo', 'baz', 'is-active': @active) do %>
|
|
61
87
|
Hello, world!
|
|
62
88
|
<% end %>
|
|
63
89
|
|
|
64
|
-
<div class="<%= clsx('foo', 'baz', 'is-active'
|
|
90
|
+
<div class="<%= clsx('foo', 'baz', 'is-active': @active) %>">
|
|
65
91
|
Hello, world!
|
|
66
92
|
</div>
|
|
67
93
|
```
|
|
68
94
|
|
|
95
|
+
### HAML
|
|
96
|
+
|
|
69
97
|
```haml
|
|
70
|
-
%div{class: clsx('foo', 'baz', 'is-active'
|
|
98
|
+
%div{class: clsx('foo', 'baz', 'is-active': @active)}
|
|
71
99
|
Hello, world!
|
|
72
100
|
```
|
|
73
101
|
|
|
102
|
+
### Slim
|
|
103
|
+
|
|
74
104
|
```slim
|
|
75
|
-
div class=clsx('foo', 'baz', 'is-active'
|
|
105
|
+
div class=clsx('foo', 'baz', 'is-active': @active)
|
|
76
106
|
| Hello, world!
|
|
77
107
|
```
|
|
78
108
|
|
|
79
|
-
|
|
109
|
+
## Framework Examples
|
|
80
110
|
|
|
81
|
-
|
|
82
|
-
<% classes = ['foo', 'baz'] %>
|
|
83
|
-
<% classes << 'is-active' if @active %>
|
|
111
|
+
### ViewComponent
|
|
84
112
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
113
|
+
```ruby
|
|
114
|
+
class AlertComponent < ViewComponent::Base
|
|
115
|
+
def initialize(variant: :info, dismissible: false)
|
|
116
|
+
@variant = variant
|
|
117
|
+
@dismissible = dismissible
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def classes
|
|
121
|
+
clsx("alert", "alert-#{@variant}", dismissible: @dismissible)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
88
124
|
```
|
|
89
125
|
|
|
90
|
-
|
|
126
|
+
### Phlex
|
|
91
127
|
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
128
|
+
```ruby
|
|
129
|
+
class Badge < Phlex::HTML
|
|
130
|
+
include Clsx::Helper
|
|
131
|
+
|
|
132
|
+
def initialize(color: :blue, pill: false)
|
|
133
|
+
@color = color
|
|
134
|
+
@pill = pill
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def view_template
|
|
138
|
+
span(class: clsx("badge", "badge-#{@color}", pill: @pill)) { yield }
|
|
139
|
+
end
|
|
140
|
+
end
|
|
96
141
|
```
|
|
97
142
|
|
|
98
|
-
|
|
143
|
+
### Tailwind CSS
|
|
99
144
|
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
145
|
+
```ruby
|
|
146
|
+
class NavLink < ViewComponent::Base
|
|
147
|
+
def initialize(active: false)
|
|
148
|
+
@active = active
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def classes
|
|
152
|
+
clsx(
|
|
153
|
+
'px-3 py-2 rounded-md text-sm font-medium transition-colors',
|
|
154
|
+
'text-white bg-indigo-600': @active,
|
|
155
|
+
'text-gray-300 hover:text-white hover:bg-gray-700': !@active
|
|
156
|
+
)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
104
159
|
```
|
|
105
160
|
|
|
106
|
-
## Differences from
|
|
107
|
-
|
|
108
|
-
This gem reproduces the functionality of the original `clsx` package, but with nuances of Ruby and Rails in mind.
|
|
161
|
+
## Differences from JavaScript clsx
|
|
109
162
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
1. falsy values in Ruby are only `false` and `nil`, so the `clsx` method will not accept `0`, `''`, `[]`, `{}`, etc. as falsy values.
|
|
113
|
-
```ruby
|
|
114
|
-
clsx('foo' => 0, bar: []) # => 'foo bar'
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
2. `clsx-rails` supports complex hash keys, like `{ %[foo bar] => true }`, which will be converted to `foo bar` in the resulting string.
|
|
118
|
-
Meaning, if it's a valid input for the `clsx-rails`, it's a valid hash key.
|
|
163
|
+
1. **Returns `nil`** when no classes apply (not an empty string). Rails tag helpers skip `nil`, preventing empty `class=""` attributes:
|
|
119
164
|
```ruby
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
```ruby
|
|
125
|
-
clsx('', [], {}, proc {}, -> {}, nil, false, true) # => nil
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
4. `clsx-rails` returns `nil` if there are no classes to apply, instead of an empty string.
|
|
129
|
-
The reason for that is not to pollute the HTML with empty `class` attributes when using Rails tag helpers: Rails will not render the `class` attribute if it's `nil`.
|
|
165
|
+
clsx(nil, false) # => nil
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
2. **Deduplication** — duplicate classes are automatically removed:
|
|
130
169
|
```ruby
|
|
131
|
-
|
|
170
|
+
clsx('foo', 'foo') # => 'foo'
|
|
132
171
|
```
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
</div>
|
|
172
|
+
|
|
173
|
+
3. **Falsy values** — in Ruby only `false` and `nil` are falsy, so `0`, `''`, `[]`, `{}` are all truthy:
|
|
174
|
+
```ruby
|
|
175
|
+
clsx('foo' => 0, bar: []) # => 'foo bar'
|
|
138
176
|
```
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
5. `clsx-rails` eliminates duplicate classes:
|
|
177
|
+
|
|
178
|
+
4. **Complex hash keys** — any valid `clsx` input works as a hash key:
|
|
142
179
|
```ruby
|
|
143
|
-
|
|
180
|
+
clsx([{ foo: true }, 'bar'] => true) # => 'foo bar'
|
|
144
181
|
```
|
|
145
182
|
|
|
146
|
-
|
|
183
|
+
5. **Ignored values** — boolean `true` and `Proc`/lambda objects are silently ignored:
|
|
184
|
+
```ruby
|
|
185
|
+
clsx('', proc {}, -> {}, nil, false, true) # => nil
|
|
186
|
+
```
|
|
147
187
|
|
|
148
|
-
|
|
149
|
-
`rake test` to run the tests. You can also run `bin/console` for an interactive
|
|
150
|
-
prompt that will allow you to experiment.
|
|
188
|
+
## Looking for a framework-agnostic version?
|
|
151
189
|
|
|
152
|
-
|
|
153
|
-
release a new version, update the version number in `version.rb`, and then run
|
|
154
|
-
`bundle exec rake release`, which will create a git tag for the version, push
|
|
155
|
-
git commits and the created tag, and push the `.gem` file to
|
|
156
|
-
[rubygems.org](https://rubygems.org).
|
|
190
|
+
See [clsx-ruby](https://github.com/svyatov/clsx-ruby) — works with Rails, Sinatra, Hanami, or plain Ruby.
|
|
157
191
|
|
|
158
|
-
|
|
159
|
-
You can run it with `bundle exec ruby benchmark/run.rb`.
|
|
160
|
-
I've added it for easier performance testing when making changes to the gem.
|
|
192
|
+
## Supported Versions
|
|
161
193
|
|
|
162
|
-
|
|
194
|
+
Ruby 3.2+ and Rails 7.2+.
|
|
163
195
|
|
|
164
|
-
|
|
196
|
+
## Development
|
|
165
197
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
- `docs`: changes to documentation
|
|
172
|
-
- `refactor`: refactoring code
|
|
198
|
+
```bash
|
|
199
|
+
bin/setup # install dependencies
|
|
200
|
+
bundle exec rake test # run tests
|
|
201
|
+
bundle exec ruby benchmark/run.rb # run benchmarks
|
|
202
|
+
```
|
|
173
203
|
|
|
174
204
|
## Contributing
|
|
175
205
|
|
data/lib/clsx-rails.rb
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'clsx'
|
|
3
4
|
require 'active_support'
|
|
4
5
|
require 'action_view'
|
|
5
6
|
|
|
6
|
-
require_relative 'clsx/version'
|
|
7
|
-
require_relative 'clsx/helper'
|
|
7
|
+
require_relative 'clsx/rails/version'
|
|
8
8
|
|
|
9
9
|
# :nodoc:
|
|
10
10
|
module Clsx
|
|
11
|
-
|
|
11
|
+
module Rails; end # :nodoc:
|
|
12
|
+
ActiveSupport.on_load(:action_view) { include Clsx::Helper }
|
|
12
13
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: clsx-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Leonid Svyatov
|
|
@@ -15,15 +15,30 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '7.
|
|
18
|
+
version: '7.2'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '7.
|
|
26
|
-
|
|
25
|
+
version: '7.2'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: clsx-ruby
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.1'
|
|
40
|
+
description: Adds clsx and cn helpers to all Rails views for constructing CSS class
|
|
41
|
+
strings conditionally
|
|
27
42
|
email:
|
|
28
43
|
- leonid@svyatov.com
|
|
29
44
|
executables: []
|
|
@@ -31,12 +46,10 @@ extensions: []
|
|
|
31
46
|
extra_rdoc_files: []
|
|
32
47
|
files:
|
|
33
48
|
- CHANGELOG.md
|
|
34
|
-
- CLAUDE.md
|
|
35
49
|
- LICENSE.txt
|
|
36
50
|
- README.md
|
|
37
51
|
- lib/clsx-rails.rb
|
|
38
|
-
- lib/clsx/
|
|
39
|
-
- lib/clsx/version.rb
|
|
52
|
+
- lib/clsx/rails/version.rb
|
|
40
53
|
homepage: https://github.com/svyatov/clsx-rails
|
|
41
54
|
licenses:
|
|
42
55
|
- MIT
|
|
@@ -52,14 +65,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
52
65
|
requirements:
|
|
53
66
|
- - ">="
|
|
54
67
|
- !ruby/object:Gem::Version
|
|
55
|
-
version: 3.
|
|
68
|
+
version: 3.2.0
|
|
56
69
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
70
|
requirements:
|
|
58
71
|
- - ">="
|
|
59
72
|
- !ruby/object:Gem::Version
|
|
60
73
|
version: '0'
|
|
61
74
|
requirements: []
|
|
62
|
-
rubygems_version: 4.0.
|
|
75
|
+
rubygems_version: 4.0.6
|
|
63
76
|
specification_version: 4
|
|
64
|
-
summary:
|
|
77
|
+
summary: Rails view helper integration for clsx-ruby
|
|
65
78
|
test_files: []
|
data/CLAUDE.md
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Project Overview
|
|
6
|
-
|
|
7
|
-
clsx-rails is a Ruby gem that provides a Rails view helper (`clsx`/`cn`) for constructing CSS class strings conditionally. It's a Ruby port of the JavaScript [clsx](https://github.com/lukeed/clsx) package, adapted for Rails conventions.
|
|
8
|
-
|
|
9
|
-
## Common Commands
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# Run all tests and linting (default rake task)
|
|
13
|
-
bundle exec rake
|
|
14
|
-
|
|
15
|
-
# Run tests only
|
|
16
|
-
bundle exec rake test
|
|
17
|
-
|
|
18
|
-
# Run a single test file
|
|
19
|
-
bundle exec ruby -Itest test/clsx/helper_test.rb
|
|
20
|
-
|
|
21
|
-
# Run a specific test method
|
|
22
|
-
bundle exec ruby -Itest test/clsx/helper_test.rb -n test_with_strings
|
|
23
|
-
|
|
24
|
-
# Run linter
|
|
25
|
-
bundle exec rake rubocop
|
|
26
|
-
|
|
27
|
-
# Run benchmark
|
|
28
|
-
bundle exec ruby benchmark/run.rb
|
|
29
|
-
|
|
30
|
-
# Install dependencies
|
|
31
|
-
bin/setup
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Architecture
|
|
35
|
-
|
|
36
|
-
The gem has a minimal structure:
|
|
37
|
-
|
|
38
|
-
- `lib/clsx-rails.rb` - Entry point that auto-includes the helper into ActionView via `ActiveSupport.on_load`
|
|
39
|
-
- `lib/clsx/helper.rb` - Core implementation with `clsx` method and `cn` alias
|
|
40
|
-
- `lib/clsx/version.rb` - Version constant
|
|
41
|
-
|
|
42
|
-
The helper uses an optimized algorithm with fast-paths for common cases (single string, string array, simple hash) and Hash-based deduplication for complex inputs.
|
|
43
|
-
|
|
44
|
-
## Key Behaviors
|
|
45
|
-
|
|
46
|
-
- Returns `nil` (not empty string) when no classes apply - this prevents Rails from rendering empty `class=""` attributes
|
|
47
|
-
- Eliminates duplicate classes automatically
|
|
48
|
-
- Ruby falsy values are only `false` and `nil` (unlike JS, `0`, `''`, `[]`, `{}` are truthy)
|
|
49
|
-
- Ignores `Proc`/lambda objects and boolean `true` values
|
|
50
|
-
- Supports complex hash keys like `{ %w[foo bar] => true }` which resolve recursively
|
|
51
|
-
|
|
52
|
-
## Commit Convention
|
|
53
|
-
|
|
54
|
-
Uses [Conventional Commits](https://www.conventionalcommits.org/): `feat`, `fix`, `perf`, `chore`, `docs`, `refactor`
|
data/lib/clsx/helper.rb
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# :nodoc:
|
|
4
|
-
module Clsx
|
|
5
|
-
# :nodoc:
|
|
6
|
-
module Helper
|
|
7
|
-
# The clsx function can take any number of arguments,
|
|
8
|
-
# each of which can be Hash, Array, Boolean, String, or Symbol.
|
|
9
|
-
#
|
|
10
|
-
# **Important**
|
|
11
|
-
# Any falsy values are discarded! Standalone Boolean values are discarded as well.
|
|
12
|
-
#
|
|
13
|
-
# @param [Mixed] args
|
|
14
|
-
#
|
|
15
|
-
# @return [String] the joined class names
|
|
16
|
-
#
|
|
17
|
-
# @example
|
|
18
|
-
# clsx('foo', 'bar') # => 'foo bar'
|
|
19
|
-
# clsx(true, { bar: true }) # => 'bar'
|
|
20
|
-
# clsx('foo', { bar: false }) # => 'foo'
|
|
21
|
-
# clsx({ bar: true }, 'baz', { bat: false }) # => 'bar baz'
|
|
22
|
-
#
|
|
23
|
-
# @example within a view
|
|
24
|
-
# <div class="<%= clsx('foo', 'bar') %>">
|
|
25
|
-
# <div class="<%= clsx('foo', active: @is_active, 'another-class' => @condition) %>">
|
|
26
|
-
# <%= tag.div class: clsx(%w[foo bar], hidden: @condition) do ... end %>
|
|
27
|
-
#
|
|
28
|
-
# @note Implementation prioritizes performance over readability.
|
|
29
|
-
# Direct class comparisons and explicit conditionals are used
|
|
30
|
-
# instead of more idiomatic Ruby patterns for speed.
|
|
31
|
-
|
|
32
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
33
|
-
def clsx(*args)
|
|
34
|
-
return nil if args.empty?
|
|
35
|
-
|
|
36
|
-
# Fast path: single argument (most common cases)
|
|
37
|
-
if args.size == 1
|
|
38
|
-
arg = args[0]
|
|
39
|
-
klass = arg.class
|
|
40
|
-
|
|
41
|
-
if klass == String
|
|
42
|
-
return arg.empty? ? nil : arg
|
|
43
|
-
elsif klass == Symbol
|
|
44
|
-
return arg.name
|
|
45
|
-
elsif klass == Array && arg.all?(String)
|
|
46
|
-
seen = {}
|
|
47
|
-
arg.each { |s| seen[s] = true unless s.empty? || seen.key?(s) }
|
|
48
|
-
return seen.empty? ? nil : seen.keys.join(' ')
|
|
49
|
-
elsif klass == Hash
|
|
50
|
-
return clsx_simple_hash(arg)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
seen = {}
|
|
55
|
-
clsx_process(args, seen)
|
|
56
|
-
seen.empty? ? nil : seen.keys.join(' ')
|
|
57
|
-
end
|
|
58
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
59
|
-
|
|
60
|
-
alias cn clsx
|
|
61
|
-
|
|
62
|
-
private
|
|
63
|
-
|
|
64
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
65
|
-
def clsx_simple_hash(hash)
|
|
66
|
-
return nil if hash.empty?
|
|
67
|
-
|
|
68
|
-
seen = {}
|
|
69
|
-
hash.each do |key, value|
|
|
70
|
-
next unless value
|
|
71
|
-
|
|
72
|
-
klass = key.class
|
|
73
|
-
|
|
74
|
-
if klass == Symbol
|
|
75
|
-
seen[key.name] = true
|
|
76
|
-
elsif klass == String
|
|
77
|
-
seen[key] = true unless key.empty?
|
|
78
|
-
else
|
|
79
|
-
# Complex key - fall back to full processing
|
|
80
|
-
seen = {}
|
|
81
|
-
clsx_process([hash], seen)
|
|
82
|
-
return seen.empty? ? nil : seen.keys.join(' ')
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
seen.empty? ? nil : seen.keys.join(' ')
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# rubocop:disable Style/MultipleComparison
|
|
90
|
-
def clsx_process(args, seen)
|
|
91
|
-
deferred = nil
|
|
92
|
-
|
|
93
|
-
args.each do |arg|
|
|
94
|
-
klass = arg.class
|
|
95
|
-
|
|
96
|
-
if klass == String
|
|
97
|
-
seen[arg] = true unless arg.empty? || seen.key?(arg)
|
|
98
|
-
elsif klass == Symbol
|
|
99
|
-
str = arg.name
|
|
100
|
-
seen[str] = true unless seen.key?(str)
|
|
101
|
-
elsif klass == Array
|
|
102
|
-
clsx_process(arg, seen)
|
|
103
|
-
elsif klass == Hash
|
|
104
|
-
arg.each { |key, value| (deferred ||= []) << key if value }
|
|
105
|
-
elsif klass == Integer || klass == Float
|
|
106
|
-
str = arg.to_s
|
|
107
|
-
seen[str] = true unless seen.key?(str)
|
|
108
|
-
elsif klass == NilClass || klass == FalseClass || klass == TrueClass || klass == Proc
|
|
109
|
-
next
|
|
110
|
-
else
|
|
111
|
-
str = arg.to_s
|
|
112
|
-
seen[str] = true unless str.empty? || seen.key?(str)
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
clsx_process(deferred, seen) if deferred
|
|
117
|
-
end
|
|
118
|
-
# rubocop:enable Style/MultipleComparison, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
119
|
-
end
|
|
120
|
-
end
|