tailwind_merge 0.3.1 → 0.4.0

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: b6224f510ea4b41c69dcfd5afebd2275a160f35a4bd0d566abf939de17467e73
4
- data.tar.gz: 703cde0f5f4995926c368abd799db4a2b39a495ad8563958d6efe6a5b10c1e1b
3
+ metadata.gz: 5e16a945fd3a3872687ebacc6fa3a18ca60424e735e31f28461e500116c7fd5f
4
+ data.tar.gz: 3b8b7ef9495914b7004cada2918c3ecb22099f1279607fe6dc1cd47db3018be6
5
5
  SHA512:
6
- metadata.gz: 20a49d6ed7f51d50c9397e187ec1ebd5244b9ad3757008d7dadbdae866a2660d29b2cec4b55e39f1daea444ba3f2acec1a0d3a24217d5a0f4888ba932c7b259a
7
- data.tar.gz: c11f641d5314e975bfd0cdfe7e25e5b21245645bec46649b7ffbe1c35dedeff94951a3984bb6dce9f99852e6c393f4a1e402489b1d7286466aacbe11991de8f4
6
+ metadata.gz: f13054805b9efa04fa50dd84f4ffb57823f026d865e050f7617a89bc4374cd520950887defe714c49e1d50ed9a88e8a0bb27383b0a67f4ba4cdba6f8b8918667
7
+ data.tar.gz: af31f90a35a33964f9fc5f0e141310c5437a322be4b906123de557e9c1a925bcea8709dc6888035905bb9eb3dbf29fb822ae0835e0529fbf36d354928445b538
data/CHANGELOG.md CHANGED
@@ -1,4 +1,54 @@
1
- **Full Changelog**: https://github.com/gjtorikian/tailwind_merge/compare/v0.3.0...v0.3.1
1
+ # Changelog
2
2
 
3
+ ## [v0.4.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.4.0) (2022-11-11)
3
4
 
5
+ [Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.3.1...v0.4.0)
4
6
 
7
+ **Merged pull requests:**
8
+
9
+ - Support custom seperators [\#5](https://github.com/gjtorikian/tailwind_merge/pull/5) ([gjtorikian](https://github.com/gjtorikian))
10
+
11
+ ## [v0.3.1](https://github.com/gjtorikian/tailwind_merge/tree/v0.3.1) (2022-11-03)
12
+
13
+ [Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.3.0...v0.3.1)
14
+
15
+ ## [v0.3.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.3.0) (2022-10-20)
16
+
17
+ [Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.2.0...v0.3.0)
18
+
19
+ **Merged pull requests:**
20
+
21
+ - Support Tailwind 3.2 [\#4](https://github.com/gjtorikian/tailwind_merge/pull/4) ([gjtorikian](https://github.com/gjtorikian))
22
+
23
+ ## [v0.2.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.2.0) (2022-10-14)
24
+
25
+ [Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.1.3...v0.2.0)
26
+
27
+ ## [v0.1.3](https://github.com/gjtorikian/tailwind_merge/tree/v0.1.3) (2022-10-11)
28
+
29
+ [Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.1.2...v0.1.3)
30
+
31
+ ## [v0.1.2](https://github.com/gjtorikian/tailwind_merge/tree/v0.1.2) (2022-10-11)
32
+
33
+ [Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.1.1...v0.1.2)
34
+
35
+ ## [v0.1.1](https://github.com/gjtorikian/tailwind_merge/tree/v0.1.1) (2022-10-11)
36
+
37
+ [Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/v0.1.0...v0.1.1)
38
+
39
+ **Closed issues:**
40
+
41
+ - Should arrays be supported? [\#1](https://github.com/gjtorikian/tailwind_merge/issues/1)
42
+
43
+ **Merged pull requests:**
44
+
45
+ - Fix h-min [\#3](https://github.com/gjtorikian/tailwind_merge/pull/3) ([gjtorikian](https://github.com/gjtorikian))
46
+ - Fix example in README [\#2](https://github.com/gjtorikian/tailwind_merge/pull/2) ([marcoroth](https://github.com/marcoroth))
47
+
48
+ ## [v0.1.0](https://github.com/gjtorikian/tailwind_merge/tree/v0.1.0) (2022-08-15)
49
+
50
+ [Full Changelog](https://github.com/gjtorikian/tailwind_merge/compare/e748f00d53e86ece8ce2543735f3327cb30c1959...v0.1.0)
51
+
52
+
53
+
54
+ \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
data/Gemfile CHANGED
@@ -10,3 +10,5 @@ gem "rake", "~> 13.0"
10
10
  gem "minitest", "~> 5.0"
11
11
 
12
12
  gem "rubocop", "~> 1.21"
13
+
14
+ gem "github_changelog_generator", "~> 1.16"
data/README.md CHANGED
@@ -27,9 +27,7 @@ If you use Tailwind with a component-based UI renderer (like [ViewComponent](htt
27
27
 
28
28
  ```html
29
29
  <!-- app/components/confirm_email_component.html.erb -->
30
- <div class="border rounded px-2 py-1">
31
- Please confirm your email address.
32
- </div>
30
+ <div class="border rounded px-2 py-1">Please confirm your email address.</div>
33
31
  ```
34
32
 
35
33
  ```ruby
@@ -53,9 +51,9 @@ tailwind-merge overrides conflicting classes and keeps everything else untouched
53
51
 
54
52
  ### Optimized for speed
55
53
 
56
- - 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.
57
- - Expensive computations happen upfront so that `merge` calls without a cache hit stay fast.
58
- - 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
+ - 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.
59
57
 
60
58
  ### Last conflicting class wins
61
59
 
@@ -150,10 +148,10 @@ If you're using a custom Tailwind config, you may need to configure tailwind-mer
150
148
 
151
149
  The default [`twMerge`](#twmerge) function is configured in a way that you can still use it if all the following points apply to your Tailwind config:
152
150
 
153
- - Only using color names which don't clash with other Tailwind class names
154
- - Only deviating by number values from number-based Tailwind classes
155
- - Only using font-family classes which don't clash with default font-weight classes
156
- - Sticking to default Tailwind config for everything else
151
+ - Only using color names which don't clash with other Tailwind class names
152
+ - Only deviating by number values from number-based Tailwind classes
153
+ - Only using font-family classes which don't clash with default font-weight classes
154
+ - Sticking to default Tailwind config for everything else
157
155
 
158
156
  If some of these points don't apply to you, you can test whether the merge still works as intended with your custom classes. Otherwise, you need create your own custom merge function by either extending the default tailwind-merge config or using a completely custom one.
159
157
 
@@ -165,9 +163,11 @@ The `tailwind_merge` config is an object with several keys:
165
163
 
166
164
  ```ruby
167
165
  tailwindMergeConfig = {
168
- # ↓ Set how many values should be stored in cache.
166
+ # ↓ *Optional* Define how many values should be stored in cache.
169
167
  cache_size: 500,
170
- # ↓ Optional prefix from Tailwind config
168
+ # ↓ *Optional* modifier separator from Tailwind config
169
+ separator: ':',
170
+ # ↓ *Optional* prefix from Tailwind config
171
171
  prefix: 'tw-',
172
172
  theme: {
173
173
  # Theme scales are defined here
@@ -283,17 +283,17 @@ If you modified one of these theme scales in your Tailwind config, you can add a
283
283
 
284
284
  Here's a brief summary for each validator:
285
285
 
286
- - `IS_LENGTH` checks whether a class part is a number (`3`, `1.5`), a fraction (`3/4`), a arbitrary length (`[3%]`, `[4px]`, `[length:var(--my-var)]`), or one of the strings `px`, `full` or `screen`.
287
- - `IS_ARBITRARY_LENGTH` checks for arbitrary length values (`[3%]`, `[4px]`, `[length:var(--my-var)]`).
288
- - `IS_INTEGER` checks for integer values (`3`) and arbitrary integer values (`[3]`).
289
- - `IS_ARBITRARY_VALUE` checks whether the class part is enclosed in brackets (`[something]`)
290
- - `IS_TSHIRT_SIZE`checks whether class part is a T-shirt size (`sm`, `xl`), optionally with a preceding number (`2xl`).
291
- - `IS_ARBITRARY_SIZE` checks whether class part is an arbitrary value which starts with `size:` (`[size:200px_100px]`) which is necessary for background-size classNames.
292
- - `IS_ARBITRARY_POSITION` checks whether class part is an arbitrary value which starts with `position:` (`[position:200px_100px]`) which is necessary for background-position classNames.
293
- - `IS_ARBITRARY_URL` checks whether class part is an arbitrary value which starts with `url:` or `url(` (`[url('/path-to-image.png')]`, `url:var(--maybe-a-url-at-runtime)]`) which is necessary for background-image classNames.
294
- - `IS_ARBITRARY_NUMBER` checks whether class part is an arbitrary value which starts with `number:` or is a number (`[number:var(--value)]`, `[450]`) which is necessary for font-weight classNames.
295
- - `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.
296
- - `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.
286
+ - `IS_LENGTH` checks whether a class part is a number (`3`, `1.5`), a fraction (`3/4`), a arbitrary length (`[3%]`, `[4px]`, `[length:var(--my-var)]`), or one of the strings `px`, `full` or `screen`.
287
+ - `IS_ARBITRARY_LENGTH` checks for arbitrary length values (`[3%]`, `[4px]`, `[length:var(--my-var)]`).
288
+ - `IS_INTEGER` checks for integer values (`3`) and arbitrary integer values (`[3]`).
289
+ - `IS_ARBITRARY_VALUE` checks whether the class part is enclosed in brackets (`[something]`)
290
+ - `IS_TSHIRT_SIZE`checks whether class part is a T-shirt size (`sm`, `xl`), optionally with a preceding number (`2xl`).
291
+ - `IS_ARBITRARY_SIZE` checks whether class part is an arbitrary value which starts with `size:` (`[size:200px_100px]`) which is necessary for background-size classNames.
292
+ - `IS_ARBITRARY_POSITION` checks whether class part is an arbitrary value which starts with `position:` (`[position:200px_100px]`) which is necessary for background-position classNames.
293
+ - `IS_ARBITRARY_URL` checks whether class part is an arbitrary value which starts with `url:` or `url(` (`[url('/path-to-image.png')]`, `url:var(--maybe-a-url-at-runtime)]`) which is necessary for background-image classNames.
294
+ - `IS_ARBITRARY_NUMBER` checks whether class part is an arbitrary value which starts with `number:` or is a number (`[number:var(--value)]`, `[450]`) which is necessary for font-weight classNames.
295
+ - `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.
296
+ - `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.
297
297
 
298
298
  ## Contributing
299
299
 
@@ -106,6 +106,7 @@ module TailwindMerge
106
106
 
107
107
  DEFAULTS = {
108
108
  cache_size: 500,
109
+ separator: ":",
109
110
  theme: {
110
111
  "colors" => [IS_ANY],
111
112
  "spacing" => [IS_LENGTH],
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TailwindMerge
4
+ module ModifierUtils
5
+ IMPORTANT_MODIFIER = "!"
6
+
7
+ def split_modifiers(class_name, separator: nil)
8
+ separator ||= ":"
9
+ separator_length = separator.length
10
+
11
+ modifiers = []
12
+ bracket_depth = 0
13
+ modifier_start = 0
14
+
15
+ 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
18
+ modifiers << class_name[modifier_start..index]
19
+ modifier_start = index + separator_length
20
+ end
21
+ end
22
+
23
+ if class_name[index] == "["
24
+ bracket_depth += 1
25
+ elsif class_name[index] == "]"
26
+ bracket_depth -= 1
27
+ end
28
+ end
29
+
30
+ base_class_name_with_important_modifier = modifiers.empty? ? class_name : class_name[modifier_start..-1]
31
+ has_important_modifier = base_class_name_with_important_modifier.start_with?(IMPORTANT_MODIFIER)
32
+ base_class_name = has_important_modifier ? base_class_name_with_important_modifier[1..-1] : base_class_name_with_important_modifier
33
+
34
+ [modifiers, has_important_modifier, base_class_name]
35
+ end
36
+
37
+ # Sorts modifiers according to following schema:
38
+ # - Predefined modifiers are sorted alphabetically
39
+ # - When an arbitrary variant appears, it must be preserved which modifiers are before and after it
40
+ def sort_modifiers(modifiers)
41
+ if modifiers.length <= 1
42
+ return modifiers
43
+ end
44
+
45
+ sorted_modifiers = []
46
+ unsorted_modifiers = []
47
+
48
+ modifiers.each do |modifier|
49
+ is_arbitrary_variant = modifier[0] == "["
50
+
51
+ if is_arbitrary_variant
52
+ sorted_modifiers.push(unsorted_modifiers.sort, modifier)
53
+ unsorted_modifiers = []
54
+ else
55
+ unsorted_modifiers.push(modifier)
56
+ end
57
+ end
58
+
59
+ sorted_modifiers.push(...unsorted_modifiers.sort)
60
+
61
+ sorted_modifiers
62
+ end
63
+ end
64
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TailwindMerge
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -11,6 +11,7 @@ require_relative "tailwind_merge/version"
11
11
  require_relative "tailwind_merge/validators"
12
12
  require_relative "tailwind_merge/config"
13
13
  require_relative "tailwind_merge/class_utils"
14
+ require_relative "tailwind_merge/modifier_utils"
14
15
 
15
16
  require "strscan"
16
17
  require "set"
@@ -18,9 +19,9 @@ require "set"
18
19
  module TailwindMerge
19
20
  class Merger
20
21
  include Config
22
+ include ModifierUtils
21
23
 
22
24
  SPLIT_CLASSES_REGEX = /\s+/
23
- IMPORTANT_MODIFIER = "!"
24
25
 
25
26
  def initialize(config: {})
26
27
  @config = if config.fetch(:theme, nil)
@@ -48,7 +49,7 @@ module TailwindMerge
48
49
  class_groups_in_conflict = Set.new
49
50
 
50
51
  classes.strip.split(SPLIT_CLASSES_REGEX).map do |original_class_name|
51
- modifiers, has_important_modifier, base_class_name = split_modifiers(original_class_name)
52
+ modifiers, has_important_modifier, base_class_name = split_modifiers(original_class_name, separator: @config[:separator])
52
53
 
53
54
  class_group_id = @class_utils.class_group_id(base_class_name)
54
55
 
@@ -59,7 +60,7 @@ module TailwindMerge
59
60
  }
60
61
  end
61
62
 
62
- variant_modifier = sort_modifiers(modifiers).join("")
63
+ variant_modifier = sort_modifiers(modifiers).join(":")
63
64
 
64
65
  modifier_id = has_important_modifier ? "#{variant_modifier}#{IMPORTANT_MODIFIER}" : variant_modifier
65
66
 
@@ -89,67 +90,5 @@ module TailwindMerge
89
90
  true
90
91
  end.reverse.map { |parsed| parsed[:original_class_name] }.join(" ")
91
92
  end
92
-
93
- SPLIT_MODIFIER_REGEX = /[:\[\]]/
94
- private def split_modifiers(class_name)
95
- modifiers = []
96
-
97
- bracket_depth = 0
98
- modifier_start = 0
99
-
100
- ss = StringScanner.new(class_name)
101
-
102
- until ss.eos?
103
- portion = ss.scan_until(SPLIT_MODIFIER_REGEX)
104
-
105
- if portion.nil?
106
- ss.terminate
107
- next
108
- end
109
- pos = ss.pos - 1
110
- if class_name[pos] == ":" && bracket_depth.zero?
111
- next_modifier_start = pos
112
- modifiers << class_name[modifier_start..next_modifier_start]
113
- modifier_start = next_modifier_start + 1
114
- elsif class_name[pos] == "["
115
- bracket_depth += 1
116
- elsif class_name[pos] == "]"
117
- bracket_depth -= 1
118
- end
119
- end
120
-
121
- base_class_name_with_important_modifier = modifiers.empty? ? class_name : class_name[modifier_start..-1]
122
- has_important_modifier = base_class_name_with_important_modifier.start_with?(IMPORTANT_MODIFIER)
123
- base_class_name = has_important_modifier ? base_class_name_with_important_modifier[1..-1] : base_class_name_with_important_modifier
124
-
125
- [modifiers, has_important_modifier, base_class_name]
126
- end
127
-
128
- # Sorts modifiers according to following schema:
129
- # - Predefined modifiers are sorted alphabetically
130
- # - When an arbitrary variant appears, it must be preserved which modifiers are before and after it
131
- private def sort_modifiers(modifiers)
132
- if modifiers.length <= 1
133
- return modifiers
134
- end
135
-
136
- sorted_modifiers = []
137
- unsorted_modifiers = []
138
-
139
- modifiers.each do |modifier|
140
- is_arbitrary_variant = modifier[0] == "["
141
-
142
- if is_arbitrary_variant
143
- sorted_modifiers.push(unsorted_modifiers.sort, modifier)
144
- unsorted_modifiers = []
145
- else
146
- unsorted_modifiers.push(modifier)
147
- end
148
- end
149
-
150
- sorted_modifiers.push(...unsorted_modifiers.sort)
151
-
152
- sorted_modifiers
153
- end
154
93
  end
155
94
  end
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ CHANGELOG_GITHUB_TOKEN="$GITHUB_TOKEN" bundle exec github_changelog_generator -u gjtorikian -p tailwind_merge
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.3.1
4
+ version: 0.4.0
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: 2022-11-03 00:00:00.000000000 Z
11
+ date: 2022-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lru_redux
@@ -138,8 +138,10 @@ files:
138
138
  - lib/tailwind_merge.rb
139
139
  - lib/tailwind_merge/class_utils.rb
140
140
  - lib/tailwind_merge/config.rb
141
+ - lib/tailwind_merge/modifier_utils.rb
141
142
  - lib/tailwind_merge/validators.rb
142
143
  - lib/tailwind_merge/version.rb
144
+ - script/generate_changelog
143
145
  - tailwind_merge.gemspec
144
146
  homepage: https://www.github.com/gjtorikian/tailwind_merge
145
147
  licenses: