classlist 0.1.0 → 1.1.0

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: 0623abc0509ba65bd66bc3147ee059f7d6424f082e21867031998fa66469b1f7
4
- data.tar.gz: b9c6713cebf59a58ab5a436db88249601e04fe24b8bc553775675ffdebb44361
3
+ metadata.gz: 13b03b38d0d38240cdabd34d10b534b811b5b292eea5c4e43eabd24f5ab199dc
4
+ data.tar.gz: d83e6fac0aacdb375188e2d5b633bc0a3e7f2f758ef1ff6262ac93ca544b8b5a
5
5
  SHA512:
6
- metadata.gz: 4fec82f539a407cf44e97beb4b83d60caf72c48ed3818e56c3900e19d53a31497729887a1f14f62fe4ffd68a7e7bb32665893d15d2fe2d9777b7469f7f9fcbb1
7
- data.tar.gz: c8bc4baae23c82f8291fb78ed0da3a251aace747016a510304021413b8e6855bcf192fe38703eb47fd213107ae6dd82b02509b3be2bf6ebf34346c963a773af7
6
+ metadata.gz: 00bff560b1cd9064b353dd1979f0d5644bbe1e861d6ea0d646e4a25cb16c7eb3ae51b66e49bbcf13fb15531065a6b9d5d4818879088ceca8434174bffc082075
7
+ data.tar.gz: 418764901609311aea4f245cda2d297110a932402a61aa4999f79955e37e4221f8509b6a91a5f86e168ca7cff62c8b1455b8156be3a97b517d4c81ec3ec92311
data/CHANGELOG.md CHANGED
@@ -9,4 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
- - Everything :)
12
+ - Support for chains of operations longer than the most simple cases.
13
+ - Introduce Classlist::Operation as a common super class for Classlist::Add, Classlist::Remove, Classlist::Reset.
14
+ - Classlist::Add that adds all entries when merged
15
+
16
+ ## [1.0.0]
17
+
18
+ ### Added
19
+
20
+ - Classlist::Reset that replaces all entries when merged
21
+ - Classlist::Remove that removes entries when merged
22
+ - DOMTokenList compatible Classlist class.
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Classlist
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/classlist`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Serverside manipulation of CSS class lists. Works especially well with Tailwind and View Components.
6
4
 
7
5
  ## Installation
8
6
 
@@ -10,6 +8,8 @@ Add this line to your application's Gemfile:
10
8
 
11
9
  ```ruby
12
10
  gem 'classlist'
11
+ # or if you don't want to manually require stuff:
12
+ gem 'classlist', require: 'classlist/all'
13
13
  ```
14
14
 
15
15
  And then execute:
@@ -22,7 +22,93 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
25
+ Imagine having a component that outputs the following markup when you render it:
26
+
27
+ ```erb
28
+ <%= render(CardComponent.new) %>
29
+ ```
30
+
31
+ ```html
32
+ <div class="card float-left">...</div>
33
+ ```
34
+
35
+ Now you're tasked with implementing a card with another background color. That's easy, you think, I'll just add an option that adds more classes to the component:
36
+
37
+ ```erb
38
+ <%= render(CardComponent.new(:classes => "bg-grey")) %>
39
+ <!-- card.html.erb -->
40
+ <div class="card float-left <%= classes %>">
41
+ ```
42
+
43
+ That works and all is well. But next day the task is to make a card that isn't floated left. You could remove `float-left` from the template and move it to all calls to render:
44
+
45
+ ```erb
46
+ <%= render(CardComponent.new(:classes => "float-left bg-grey")) %>
47
+ <!-- card.html.erb -->
48
+ <div class="card <%= classes %>">
49
+ ```
50
+
51
+ Depending on the number of classes and the number of render calls that could work. But how about if you were able to write
52
+
53
+ ```erb
54
+ <%= render(CardComponent.new(:classes => Classlist::Remove.new("float-left"))) %>
55
+ ```
56
+
57
+ With Classlist you can:
58
+
59
+ ```ruby
60
+ # card_component.rb
61
+ def classes
62
+ Classlist.new("card float-left") + @classes
63
+ end
64
+ ```
65
+
66
+ ```erb
67
+ <!-- card.html.erb -->
68
+ <div class="<%= classes %>">
69
+ ```
70
+
71
+ The resulting markup will be
72
+
73
+ ```html
74
+ <div class="card">
75
+ ```
76
+
77
+ because
78
+
79
+ ```ruby
80
+ Classlist.new("card float-left") + Classlist::Remove.new("float-left") == Classlist.new("card")
81
+ ```
82
+
83
+ ### Basic usage
84
+
85
+ ```ruby
86
+ # Create a new classlist - these are equivalent:
87
+ classes = Classlist.new("pt-6 space-y-4")
88
+ classes = Classlist.new(["pt-6", "space-y-4"])
89
+
90
+ # Add classes
91
+ classes.add("md:p-8 text-center")
92
+ classes.to_s #=> "pt-6 space-y-4 md:p-8 text-center"
93
+
94
+ # Remove classes
95
+ classes.remove("md:p-8")
96
+ classes.to_s #=> "pt-6 space-y-4 text-center"
97
+
98
+ # Toggle classes
99
+ classes.toggle("hidden")
100
+ classes.to_s #=> "pt-6 space-y-4 text-center hidden"
101
+ classes.toggle("text-center")
102
+ classes.to_s #=> "pt-6 space-y-4 hidden"
103
+
104
+ # Replace classes
105
+ classes.replace("hidden", "block")
106
+ classes.to_s #=> "pt-6 space-y-4 block"
107
+ ```
108
+
109
+ ## DOM compatibility
110
+
111
+ While Classlist aims to be a feature-compatible version of [`DOMTokenList`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList) that doesn't always make for particularily Ruby'esque methods. In cases where Ruby has similar methods named differently than the DOM, we'll prefer Ruby-style method names while keeping aliases with the names from `DOMTokenList`.
26
112
 
27
113
  ## Development
28
114
 
@@ -32,4 +118,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
118
 
33
119
  ## Contributing
34
120
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/koppen/classlist.
121
+ Bug reports and pull requests are welcome on GitHub at https://github.com/substancelab/classlist.
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "classlist/operation"
4
+
5
+ # Classlist::Add is an operation that adds tokens to the original
6
+ # classlist when added to it.
7
+ class Classlist::Add < Classlist::Operation
8
+ def merge(original)
9
+ original.entries + entries
10
+ end
11
+
12
+ # resolve changes the original classlist
13
+ def resolve(original)
14
+ entries.each do |entry|
15
+ original.add(entry)
16
+ end
17
+
18
+ super
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Require this file to load all of Classlist into your application.
4
+ #
5
+ # For example in a Gemfile:
6
+ #
7
+ # gem "classlist", require: "classlist/all"
8
+ #
9
+ # Or you can just require what you need manually.
10
+
11
+ require "classlist/add"
12
+ require "classlist/remove"
13
+ require "classlist/reset"
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "classlist"
4
+
5
+ # Classlist::Operations modify the original classlist
6
+ class Classlist::Operation < Classlist
7
+ # resolve changes the original classlist
8
+ def resolve(original_classlist)
9
+ resolve_operations(original_classlist)
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "classlist/operation"
4
+
5
+ # Classlist::Remove is an operation that removes tokens from the original
6
+ # classlist when added to it.
7
+ class Classlist::Remove < Classlist::Operation
8
+ def merge(original)
9
+ original.entries - entries
10
+ end
11
+
12
+ # #resolve changes the original classlist
13
+ def resolve(original)
14
+ entries.each do |entry|
15
+ original.remove(entry)
16
+ end
17
+
18
+ super
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "classlist/operation"
4
+
5
+ # Classlist::Reset is an operation that removes all tokens from the original
6
+ # classlist when merged.
7
+ class Classlist::Reset < Classlist::Operation
8
+ def merge(original)
9
+ original.entries.replace(entries)
10
+ end
11
+
12
+ # #resolve changes the original classlist
13
+ def resolve(original)
14
+ original.entries.replace(entries)
15
+
16
+ super
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Classlist
4
- VERSION = "0.1.0"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/classlist.rb CHANGED
@@ -1,11 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+
3
5
  require_relative "classlist/version"
4
6
 
5
7
  class Classlist
8
+ class ArgumentError < ::ArgumentError; end
9
+
6
10
  class Error < StandardError; end
7
11
 
8
- attr_reader :entries
12
+ extend Forwardable
13
+ def_delegators :@entries, :each
14
+
15
+ attr_reader :entries, :operations
16
+
17
+ # Returns the Classlist resulting from adding other to this classlist.
18
+ def +(other)
19
+ case other
20
+ when Classlist::Operation
21
+ add_operation(other)
22
+ self
23
+ when Classlist
24
+ result = other.merge(self)
25
+ Classlist.new(result)
26
+ else
27
+ result = entries + build_entries(other)
28
+ Classlist.new(result)
29
+ end
30
+ end
31
+
32
+ def ==(other)
33
+ return false unless other.is_a?(self.class)
34
+
35
+ resolve_operations(self)
36
+ other.resolve_operations
37
+
38
+ @entries == other.entries
39
+ end
9
40
 
10
41
  # Adds the given tokens to the list, omitting any that are already present.
11
42
  def add(tokens)
@@ -15,8 +46,36 @@ class Classlist
15
46
  end
16
47
  end
17
48
 
49
+ def add_operation(other)
50
+ @operations << other
51
+ end
52
+
53
+ def include?(token)
54
+ entries.include?(token)
55
+ end
56
+ alias_method :contains, :include?
57
+
18
58
  def initialize(entries = [])
19
59
  @entries = build_entries(entries)
60
+ @operations = []
61
+ end
62
+
63
+ # Returns the item in the list by its index, or null if the index is greater
64
+ # than or equal to the list's length.
65
+ def item(index)
66
+ return nil if index.negative?
67
+
68
+ entries[index]
69
+ end
70
+
71
+ # An integer representing the number of objects stored in the object.
72
+ def length
73
+ entries.length
74
+ end
75
+
76
+ # Returns a list of tokens in this classlist merged with the given classlist.
77
+ def merge(classlist)
78
+ (classlist.entries + entries).uniq
20
79
  end
21
80
 
22
81
  # Removes the specified tokens from the classlist, ignoring any that are not
@@ -32,10 +91,10 @@ class Classlist
32
91
  # exist, #replace returns false immediately, without adding the new token to
33
92
  # the token list.
34
93
  def replace(old_token, new_token)
35
- return false unless entries.include?(old_token)
94
+ return false unless include?(old_token)
36
95
 
37
- if entries.include?(new_token)
38
- entries.delete(old_token)
96
+ if include?(new_token)
97
+ remove(old_token)
39
98
  else
40
99
  index = entries.index(old_token)
41
100
  entries[index] = new_token
@@ -44,13 +103,21 @@ class Classlist
44
103
  true
45
104
  end
46
105
 
106
+ def resolve_operations(original_classlist = self)
107
+ operations.each do |operation|
108
+ operation.resolve(original_classlist)
109
+ end
110
+ end
111
+
47
112
  def to_a
48
- entries
113
+ resolve_operations(self)
114
+ @entries
49
115
  end
50
116
 
51
117
  def to_s
52
- entries.join(" ")
118
+ to_a.join(" ")
53
119
  end
120
+ alias_method :value, :to_s
54
121
 
55
122
  # Removes an existing token from the list and returns false. If the token
56
123
  # doesn't exist it's added and the function returns true.
@@ -59,11 +126,13 @@ class Classlist
59
126
  # set to false, then token will only be removed, but not added. If set to
60
127
  # true, then token will only be added, but not removed.
61
128
  def toggle(token, force = nil)
129
+ raise ArgumentError, "The token can not contain whitespace." if token.to_s.include?(" ")
130
+
62
131
  if entries.include?(token)
63
- entries.delete(token) unless force == true
132
+ remove(token) unless force == true
64
133
  result = false
65
134
  else
66
- entries.push(token) unless force == false
135
+ add(token) unless force == false
67
136
  result = true
68
137
  end
69
138
 
@@ -90,6 +159,6 @@ class Classlist
90
159
  entries.split(" ")
91
160
  else
92
161
  raise Error, "Invalid entries: #{entries.inspect}"
93
- end
162
+ end.uniq
94
163
  end
95
164
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: classlist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakob Skjerning
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-29 00:00:00.000000000 Z
11
+ date: 2022-11-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Serverside manipulation of lists of CSS classnames that play nicely with
14
14
  View Components.
@@ -28,6 +28,11 @@ files:
28
28
  - bin/setup
29
29
  - classlist.gemspec
30
30
  - lib/classlist.rb
31
+ - lib/classlist/add.rb
32
+ - lib/classlist/all.rb
33
+ - lib/classlist/operation.rb
34
+ - lib/classlist/remove.rb
35
+ - lib/classlist/reset.rb
31
36
  - lib/classlist/version.rb
32
37
  - sig/classlist.rbs
33
38
  homepage: https://github.com/substancelab/classlist