tailwind_merge 0.6.0 → 0.7.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/CHANGELOG.md +25 -0
- data/README.md +38 -9
- data/lib/tailwind_merge/class_utils.rb +8 -2
- data/lib/tailwind_merge/config.rb +3 -0
- data/lib/tailwind_merge/modifier_utils.rb +13 -5
- data/lib/tailwind_merge/validators.rb +4 -4
- data/lib/tailwind_merge/version.rb +1 -1
- data/lib/tailwind_merge.rb +25 -3
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7447d7154d014a86a19be3f98b6ddefb312845fbb027a9a173da9ea9c6594e8c
|
4
|
+
data.tar.gz: d573c40fe6d30de2a42dcab8fcf79c1e5a535b6a3c1af2c26e9ee696f5f38530
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42727802539a4e2dc123df65265a19523c727eb5b3303689bd0363769e17d6ad998c6b48df52e81bf94d8091867fa9d073eb02cb2b83b31442016bde8ad83385
|
7
|
+
data.tar.gz: f54fcd81cd7d1f58b1aa8e5d0b2b0273d973126ccf82f7913d3e72dafd092a146072585c058fe4303b75d7e0f580b1f866091d209d4a08bd9a924813d2354a8f
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.2.1
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v0.7.1.2](https://github.com/gjtorikian/tailwind_merge/tree/v0.7.1.2) (2023-06-02)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.7.1.1...v0.7.1.2)
|
6
|
+
|
7
|
+
## [v0.7.1.1](https://github.com/gjtorikian/tailwind_merge/tree/v0.7.1.1) (2023-06-02)
|
8
|
+
|
9
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.7.1...v0.7.1.1)
|
10
|
+
|
11
|
+
## [v0.7.1](https://github.com/gjtorikian/tailwind_merge/tree/v0.7.1) (2023-06-02)
|
12
|
+
|
13
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.7.0...v0.7.1)
|
14
|
+
|
15
|
+
**Merged pull requests:**
|
16
|
+
|
17
|
+
- Port updates [\#12](https://github.com/gjtorikian/tailwind_merge/pull/12) ([gjtorikian](https://github.com/gjtorikian))
|
18
|
+
- Add Ruby 3.2 to CI. Minor additional cleanup. [\#11](https://github.com/gjtorikian/tailwind_merge/pull/11) ([petergoldstein](https://github.com/petergoldstein))
|
19
|
+
|
20
|
+
## [v0.7.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.7.0) (2023-04-03)
|
21
|
+
|
22
|
+
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.6.0...v0.7.0)
|
23
|
+
|
24
|
+
**Merged pull requests:**
|
25
|
+
|
26
|
+
- Add postfix support [\#10](https://github.com/gjtorikian/tailwind_merge/pull/10) ([gjtorikian](https://github.com/gjtorikian))
|
27
|
+
|
3
28
|
## [v0.6.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.6.0) (2023-03-29)
|
4
29
|
|
5
30
|
[Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.5.2...v0.6.0)
|
data/README.md
CHANGED
@@ -23,7 +23,7 @@ TailwindMerge::Merger.new.merge("px-2 py-1 bg-red hover:bg-dark-red p-3 bg-[#B91
|
|
23
23
|
|
24
24
|
## What's it for?
|
25
25
|
|
26
|
-
If you use Tailwind with a component-based UI renderer (like [ViewComponent](https://viewcomponent.org) or [Ariadne](https://github.com/yettoapp/ariadne
|
26
|
+
If you use Tailwind with a component-based UI renderer (like [ViewComponent](https://viewcomponent.org) or [Ariadne](https://github.com/yettoapp/ariadne)), you're probably familiar with the situation that you want to change some styles of an existing component:
|
27
27
|
|
28
28
|
```html
|
29
29
|
<!-- app/components/confirm_email_component.html.erb -->
|
@@ -45,15 +45,13 @@ This is where `tailwind_merge` comes in:
|
|
45
45
|
# border rounded p-5
|
46
46
|
```
|
47
47
|
|
48
|
-
tailwind-merge overrides conflicting classes and keeps everything else untouched. In the case of the
|
48
|
+
tailwind-merge overrides conflicting classes and keeps everything else untouched. In the case of the implementation of `ConfirmEmailComponent`, the input now only renders the classes `border rounded p-5`.
|
49
49
|
|
50
50
|
## Features
|
51
51
|
|
52
|
-
###
|
52
|
+
### Merging behavior
|
53
53
|
|
54
|
-
|
55
|
-
- Expensive computations happen upfront so that `merge` calls without a cache hit stay fast.
|
56
|
-
- These computations are called lazily on the first call to `merge` to prevent it from impacting app startup performance if it isn't used initially.
|
54
|
+
`tailwind_merge` is designed to be predictable and intuitive. It follows a set of rules to determine which class "wins" when there are conflicts. Here is a brief overview of the conflict resolutions which `tailwind_merge` can do.
|
57
55
|
|
58
56
|
### Last conflicting class wins
|
59
57
|
|
@@ -84,6 +82,8 @@ tailwind-merge overrides conflicting classes and keeps everything else untouched
|
|
84
82
|
@merger.merge('hover:focus:p-2 focus:hover:p-4') # → 'focus:hover:p-4'
|
85
83
|
```
|
86
84
|
|
85
|
+
The order of standard modifiers does not matter for tailwind-merge.
|
86
|
+
|
87
87
|
### Supports arbitrary values
|
88
88
|
|
89
89
|
```ruby
|
@@ -101,7 +101,8 @@ tailwind-merge overrides conflicting classes and keeps everything else untouched
|
|
101
101
|
@merger.merge('[padding:1rem] p-8') # → '[padding:1rem] p-8'
|
102
102
|
```
|
103
103
|
|
104
|
-
|
104
|
+
> **Warning**
|
105
|
+
> Watch out for using arbitrary properties which could be expressed as Tailwind classes. `tailwind_merge` does not resolve conflicts between arbitrary properties and their matching Tailwind classes to keep the bundle size small.
|
105
106
|
|
106
107
|
### Supports arbitrary variants
|
107
108
|
|
@@ -113,7 +114,9 @@ Watch out when mixing arbitrary properties which could be expressed as Tailwind
|
|
113
114
|
@merger.merge('[&:focus]:ring focus:ring-4') # → '[&:focus]:ring focus:ring-4'
|
114
115
|
```
|
115
116
|
|
116
|
-
|
117
|
+
> **Warning**
|
118
|
+
> Similarly to arbitrary properties, `tailwind_merge` does not resolve conflicts between arbitrary variants and their matching predefined modifiers for bundle size reasons.
|
119
|
+
> The order of standard modifiers before and after an arbitrary variant in isolation (all modifiers before are one group, all modifiers after are another group) does not matter for `tailwind_merge`. However, it _does_ matter whether a standard modifier is before or after an arbitrary variant both for Tailwind CSS and `tailwind_merge` because the resulting CSS selectors are different.
|
117
120
|
|
118
121
|
### Supports important modifier
|
119
122
|
|
@@ -122,6 +125,12 @@ Similarly to arbitrary properties, `tailwind_merge` does not resolve conflicts b
|
|
122
125
|
@merger.merge('!right-2 !-inset-x-1') # → '!-inset-x-1'
|
123
126
|
```
|
124
127
|
|
128
|
+
## Supports postfix modifiers
|
129
|
+
|
130
|
+
```ts
|
131
|
+
twMerge("text-sm leading-6 text-lg/7"); // → 'text-lg/7'
|
132
|
+
```
|
133
|
+
|
125
134
|
### Preserves non-Tailwind classes
|
126
135
|
|
127
136
|
```ruby
|
@@ -142,7 +151,7 @@ If you're using Tailwind CSS without any extra configs, you can use it right awa
|
|
142
151
|
merger = TailwindMerge::Merger.new
|
143
152
|
```
|
144
153
|
|
145
|
-
|
154
|
+
### Usage with custom Tailwind config
|
146
155
|
|
147
156
|
If you're using a custom Tailwind config, you may need to configure tailwind-merge as well to merge classes properly.
|
148
157
|
|
@@ -297,6 +306,22 @@ Here's a brief summary for each validator:
|
|
297
306
|
- `IS_ARBITRARY_SHADOW` checks whether class part is an arbitrary value which starts with the same pattern as a shadow value (`[0_35px_60px_-15px_rgba(0,0,0,0.3)]`), namely with two lengths separated by a underscore.
|
298
307
|
- `IS_ANY` always returns true. Be careful with this validator as it might match unwanted classes. I use it primarily to match colors or when it's certain there are no other class groups in a namespace.
|
299
308
|
|
309
|
+
## Performance
|
310
|
+
|
311
|
+
### Results are cached
|
312
|
+
|
313
|
+
Results are cached by default, so you don't need to worry about wasteful re-renders. The library uses a computationally lightweight [LRU cache](<https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)>) which stores up to 500 different results by default. The cache is applied after all arguments are joined together to a single string. This means that if you call `merge` repeatedly with different arguments that result in the same string when joined, the cache will be hit.
|
314
|
+
|
315
|
+
The cache size can be modified or opted out of by setting the `cache_size` config variable.
|
316
|
+
|
317
|
+
### Data structures are reused between calls
|
318
|
+
|
319
|
+
Expensive computations happen upfront so that `merge` calls without a cache hit stay fast.
|
320
|
+
|
321
|
+
### Lazy initialization
|
322
|
+
|
323
|
+
The initial computations are called lazily on the first call to `merge` to prevent it from impacting startup performance if it isn't used initially.
|
324
|
+
|
300
325
|
## Contributing
|
301
326
|
|
302
327
|
Bug reports and pull requests are welcome on GitHub at https://github.com/gjtorikian/tailwind_merge.
|
@@ -304,3 +329,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/gjtori
|
|
304
329
|
## License
|
305
330
|
|
306
331
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
332
|
+
|
333
|
+
## Acknowledgements
|
334
|
+
|
335
|
+
This gem is pretty much just a port of https://github.com/dcastil/tailwind-merge. Thank them, not me!
|
@@ -51,8 +51,14 @@ module TailwindMerge
|
|
51
51
|
result.nil? ? result : result[:class_group_id]
|
52
52
|
end
|
53
53
|
|
54
|
-
def get_conflicting_class_group_ids(class_group_id)
|
55
|
-
@config[:conflicting_class_groups][class_group_id] || []
|
54
|
+
def get_conflicting_class_group_ids(class_group_id, has_postfix_modifier)
|
55
|
+
conflicts = @config[:conflicting_class_groups][class_group_id] || []
|
56
|
+
|
57
|
+
if has_postfix_modifier && @config[:conflicting_class_group_modifiers][class_group_id]
|
58
|
+
return [...conflicts, ...@config[:conflicting_class_group_modifiers][class_group_id]]
|
59
|
+
end
|
60
|
+
|
61
|
+
conflicts
|
56
62
|
end
|
57
63
|
|
58
64
|
private def create_class_map(config)
|
@@ -1782,6 +1782,9 @@ module TailwindMerge
|
|
1782
1782
|
"scroll-px" => ["scroll-pr", "scroll-pl"],
|
1783
1783
|
"scroll-py" => ["scroll-pt", "scroll-pb"],
|
1784
1784
|
},
|
1785
|
+
conflicting_class_group_modifiers: {
|
1786
|
+
"font-size": ["leading"],
|
1787
|
+
},
|
1785
1788
|
}.freeze
|
1786
1789
|
|
1787
1790
|
def merge_configs(extension_config)
|
@@ -7,22 +7,29 @@ module TailwindMerge
|
|
7
7
|
def split_modifiers(class_name, separator: nil)
|
8
8
|
separator ||= ":"
|
9
9
|
separator_length = separator.length
|
10
|
+
seperator_is_single_char = separator_length == 1
|
11
|
+
first_seperator_char = separator[0]
|
10
12
|
|
11
13
|
modifiers = []
|
12
14
|
bracket_depth = 0
|
13
15
|
modifier_start = 0
|
16
|
+
postfix_modifier_position = 0
|
14
17
|
|
15
18
|
class_name.each_char.with_index do |char, index|
|
16
|
-
if bracket_depth.zero?
|
17
|
-
if
|
19
|
+
if bracket_depth.zero?
|
20
|
+
if char == first_seperator_char && (seperator_is_single_char || class_name[index..(index + separator_length - 1)] == separator)
|
18
21
|
modifiers << class_name[modifier_start..index]
|
19
22
|
modifier_start = index + separator_length
|
23
|
+
next
|
24
|
+
elsif char == "/"
|
25
|
+
postfix_modifier_position = index
|
26
|
+
next
|
20
27
|
end
|
21
28
|
end
|
22
29
|
|
23
|
-
if
|
30
|
+
if char == "["
|
24
31
|
bracket_depth += 1
|
25
|
-
elsif
|
32
|
+
elsif char == "]"
|
26
33
|
bracket_depth -= 1
|
27
34
|
end
|
28
35
|
end
|
@@ -30,8 +37,9 @@ module TailwindMerge
|
|
30
37
|
base_class_name_with_important_modifier = modifiers.empty? ? class_name : class_name[modifier_start..-1]
|
31
38
|
has_important_modifier = base_class_name_with_important_modifier.start_with?(IMPORTANT_MODIFIER)
|
32
39
|
base_class_name = has_important_modifier ? base_class_name_with_important_modifier[1..-1] : base_class_name_with_important_modifier
|
40
|
+
maybe_postfix_modifier_position = postfix_modifier_position && postfix_modifier_position > modifier_start ? postfix_modifier_position - modifier_start : false
|
33
41
|
|
34
|
-
[modifiers, has_important_modifier, base_class_name]
|
42
|
+
[modifiers, has_important_modifier, base_class_name, maybe_postfix_modifier_position]
|
35
43
|
end
|
36
44
|
|
37
45
|
# Sorts modifiers according to following schema:
|
@@ -27,7 +27,7 @@ module TailwindMerge
|
|
27
27
|
|
28
28
|
ARBITRARY_VALUE_REGEX = /^\[(?:([a-z-]+):)?(.+)\]$/i
|
29
29
|
FRACTION_REGEX = %r{^\d+/\d+$}
|
30
|
-
LENGTH_UNIT_REGEX = /\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))
|
30
|
+
LENGTH_UNIT_REGEX = /\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|^0$/
|
31
31
|
TSHIRT_UNIT_REGEX = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/
|
32
32
|
# Shadow always begins with x and y offset separated by underscore
|
33
33
|
SHADOW_REGEX = /^-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/
|
@@ -55,9 +55,9 @@ module TailwindMerge
|
|
55
55
|
}
|
56
56
|
|
57
57
|
IS_LENGTH = ->(value) {
|
58
|
-
numeric?(value) ||
|
59
|
-
STRING_LENGTHS.include?(value) ||
|
60
|
-
FRACTION_REGEX.match?(value) ||
|
58
|
+
numeric?(value) ||
|
59
|
+
STRING_LENGTHS.include?(value) ||
|
60
|
+
FRACTION_REGEX.match?(value) ||
|
61
61
|
IS_ARBITRARY_LENGTH.call(value)
|
62
62
|
}
|
63
63
|
|
data/lib/tailwind_merge.rb
CHANGED
@@ -49,11 +49,31 @@ module TailwindMerge
|
|
49
49
|
class_groups_in_conflict = Set.new
|
50
50
|
|
51
51
|
classes.strip.split(SPLIT_CLASSES_REGEX).map do |original_class_name|
|
52
|
-
modifiers, has_important_modifier, base_class_name = split_modifiers(original_class_name, separator: @config[:separator])
|
52
|
+
modifiers, has_important_modifier, base_class_name, maybe_postfix_modifier_position = split_modifiers(original_class_name, separator: @config[:separator])
|
53
53
|
|
54
|
-
|
54
|
+
actual_base_class_name = maybe_postfix_modifier_position ? base_class_name[0...maybe_postfix_modifier_position] : base_class_name
|
55
|
+
class_group_id = @class_utils.class_group_id(actual_base_class_name)
|
55
56
|
|
56
57
|
unless class_group_id
|
58
|
+
unless maybe_postfix_modifier_position
|
59
|
+
next {
|
60
|
+
is_tailwind_class: false,
|
61
|
+
original_class_name: original_class_name,
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
class_group_id = @class_utils.class_group_id(base_class_name)
|
66
|
+
|
67
|
+
unless class_group_id
|
68
|
+
next {
|
69
|
+
isTailwindClass: false,
|
70
|
+
original_class_name: original_class_name,
|
71
|
+
}
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
has_postfix_modifier = false
|
76
|
+
|
57
77
|
next {
|
58
78
|
is_tailwind_class: false,
|
59
79
|
original_class_name: original_class_name,
|
@@ -69,6 +89,7 @@ module TailwindMerge
|
|
69
89
|
modifier_id: modifier_id,
|
70
90
|
class_group_id: class_group_id,
|
71
91
|
original_class_name: original_class_name,
|
92
|
+
has_postfix_modifier: has_postfix_modifier,
|
72
93
|
}
|
73
94
|
end.reverse # Last class in conflict wins, so filter conflicting classes in reverse order.
|
74
95
|
.select do |parsed|
|
@@ -76,6 +97,7 @@ module TailwindMerge
|
|
76
97
|
|
77
98
|
modifier_id = parsed[:modifier_id]
|
78
99
|
class_group_id = parsed[:class_group_id]
|
100
|
+
has_postfix_modifier = parsed[:has_postfix_modifier]
|
79
101
|
|
80
102
|
class_id = "#{modifier_id}#{class_group_id}"
|
81
103
|
|
@@ -83,7 +105,7 @@ module TailwindMerge
|
|
83
105
|
|
84
106
|
class_groups_in_conflict.add(class_id)
|
85
107
|
|
86
|
-
@class_utils.get_conflicting_class_group_ids(class_group_id).each do |group|
|
108
|
+
@class_utils.get_conflicting_class_group_ids(class_group_id, has_postfix_modifier).each do |group|
|
87
109
|
class_groups_in_conflict.add("#{modifier_id}#{group}")
|
88
110
|
end
|
89
111
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tailwind_merge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Garen J. Torikian
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lru_redux
|
@@ -24,6 +24,7 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.1'
|
27
|
+
force_ruby_platform: false
|
27
28
|
- !ruby/object:Gem::Dependency
|
28
29
|
name: minitest
|
29
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -60,6 +61,7 @@ extensions: []
|
|
60
61
|
extra_rdoc_files: []
|
61
62
|
files:
|
62
63
|
- ".rubocop.yml"
|
64
|
+
- ".ruby-version"
|
63
65
|
- CHANGELOG.md
|
64
66
|
- Gemfile
|
65
67
|
- LICENSE.txt
|
@@ -98,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
100
|
- !ruby/object:Gem::Version
|
99
101
|
version: '0'
|
100
102
|
requirements: []
|
101
|
-
rubygems_version: 3.
|
103
|
+
rubygems_version: 3.4.13
|
102
104
|
signing_key:
|
103
105
|
specification_version: 4
|
104
106
|
summary: Utility function to efficiently merge Tailwind CSS classes without style
|