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 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