classlist 0.1.0 → 1.1.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: 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