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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9f76007fe9bc9eb3a01775ddfbc29e400884058373a9baeb001ccfff21ef9a7
4
- data.tar.gz: 4d97ba3be89af9227c4b2aa6f38fc8e9926886b6c33225881630356d72576f3d
3
+ metadata.gz: 7447d7154d014a86a19be3f98b6ddefb312845fbb027a9a173da9ea9c6594e8c
4
+ data.tar.gz: d573c40fe6d30de2a42dcab8fcf79c1e5a535b6a3c1af2c26e9ee696f5f38530
5
5
  SHA512:
6
- metadata.gz: 28794887eeb6e5f73ac74768cad06e52b4d5463e5ea436661e07d61a316f1e4a41b1a6607600746153f0b5464004472c1b98982d7770ca947ea03eb714e0f141
7
- data.tar.gz: 88e8f3ab1bb6f2173a1f5aceca7416d8c1c1b4d73608ed78ed97b364f67560792996dcd72e5bcca1f81327bf8fd7aaef318aaab55011772019e9e677fa03f667
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/tree/main/ruby/ariadne_view_components)), you're probably familiar with the situation that you want to change some styles of an existing component:
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 your implementation of `ConfirmEmailComponent`, the input now only renders the classes `border rounded p-5`.
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
- ### Optimized for speed
52
+ ### Merging behavior
53
53
 
54
- - Results get cached by default, so you don't need to worry about wasteful re-renders. The library uses a thread-safe [LRU cache](<https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)>) which stores up to 500 different results. The cache size can be modified via config options.
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
- Watch out when mixing arbitrary properties which could be expressed as Tailwind classes. `tailwind_merge` does not resolve conflicts between arbitrary properties and their matching Tailwind classes.
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
- Similarly to arbitrary properties, `tailwind_merge` does not resolve conflicts between arbitrary variants and their matching predefined modifiers.
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
- ## Usage with custom Tailwind config
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? && char == separator[0]
17
- if separator_length == 1 || class_name[index..(index + separator_length - 1)] == separator
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 class_name[index] == "["
30
+ if char == "["
24
31
  bracket_depth += 1
25
- elsif class_name[index] == "]"
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TailwindMerge
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.1.2"
5
5
  end
@@ -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
- class_group_id = @class_utils.class_group_id(base_class_name)
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.6.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-03-29 00:00:00.000000000 Z
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.3.26
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