tailwind_merge 0.6.0 → 0.7.1.2
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/.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
|