view_component-scoped_styles 0.4.1 → 0.5.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: 6a23828ee0e2dafc7e52ccb67a8a319391af70a50777701d5090e66626d159c3
4
- data.tar.gz: b5a87e35910c84ae1a95914af878506b6fdd3c155071fca950f2fa8d76b61a8c
3
+ metadata.gz: 6eabd4a24031f8acbe3e73fba03f5bd1d8263feee45a8836e827fe4ce3e2bcf3
4
+ data.tar.gz: 632ae34483c93fdadb82fac21ac8b0a409ff9ae6b07098ff080c5e32a935e8fc
5
5
  SHA512:
6
- metadata.gz: 026faad956f24fd8638fa4834b3d428d9363ed9e9d5d43de724df68e189edc998cd268140a3094881fb51b77677c5cb1e627b1819bbd8d3d4dfa5d349a778604
7
- data.tar.gz: d947bece5e61141621b4674c91b947afcb8782103af7938ca5b3a2f57208258dea9333af44e2c8d85b2b67e354bd0710bbd8c33eef47518b411776d7ec99e5de
6
+ metadata.gz: cdc5ed3dc3056702683c153d9399f4aa7f89e82806e3be2f5d079da8d0ba2ae6aaf06be57fa54d1fbcf7461cdf2579b54b04a006d06d0827a0ef17845c7ed450
7
+ data.tar.gz: 3c647e2d17bdbf084cd20abb5cf1da61cea6c097930bfb6374d4917680cbc3f71cb16db5912e9cdb91fe52a6f91a976a49b1b7b76b10dde53d7d19c125705ec3
data/README.md CHANGED
@@ -4,7 +4,7 @@ Scoped, colocated CSS for [ViewComponent](https://viewcomponent.org/).
4
4
 
5
5
  Avoids collisions by rewriting class selectors to stable, content-derived names.
6
6
 
7
- E.g. `.button` becomes `.c-a1b2c3d4`
7
+ E.g. `.button` becomes `.button_a1b2c3d4`
8
8
 
9
9
  ## Table of Contents
10
10
 
@@ -161,22 +161,50 @@ end
161
161
  </div>
162
162
  ```
163
163
 
164
- Scoped class names are prefixed by default (e.g. `c-a1b2c3d4`). Set a global prefix in configuration, or override per component with `css_class_prefix`:
164
+ Scoped class names are prefixed by default using the class name (e.g. `.component``component_a1b2c3d4`). Set a global prefix in configuration, or override per component with `css_class_prefix`.
165
+
166
+ The prefix is a template string. Use `{class_name}` for the CSS class being scoped and `{component_name}` for the component name (namespaces joined by `/`, with a trailing `Component` suffix removed):
167
+
168
+ ```ruby
169
+ ViewComponent::ScopedStyles.configure do |config|
170
+ config.css_class_prefix = "{class_name}_" # default
171
+ end
172
+ ```
165
173
 
166
174
  ```ruby
167
175
  class ExampleComponent < ViewComponent::Base
168
176
  include ViewComponent::ScopedStyles
169
177
 
170
- css_class_prefix "vc-"
178
+ css_class_prefix "vc-{class_name}_"
179
+
180
+ styles do
181
+ <<~CSS
182
+ .component { ... } # becomes .vc-component_a1b2c3d4 in components.scoped.css
183
+ CSS
184
+ end
185
+ end
186
+ ```
187
+
188
+ Namespaced components can include the component name in the prefix:
189
+
190
+ ```ruby
191
+ class Admin::UserCardComponent < ViewComponent::Base
192
+ include ViewComponent::ScopedStyles
193
+
194
+ css_class_prefix "{component_name}_{class_name}_"
171
195
 
172
196
  styles do
173
197
  <<~CSS
174
- .component { ... } # becomes .vc-a1b2c3d4 in components.scoped.css
198
+ .component { ... }
199
+ # Admin/UserCardComponent_component_a1b2c3d4 in templates;
200
+ # Admin\/UserCardComponent_component_a1b2c3d4 in components.scoped.css
175
201
  CSS
176
202
  end
177
203
  end
178
204
  ```
179
205
 
206
+ **Upgrading from 0.4.x:** set `config.css_class_prefix = "c-"` (and per-component overrides) to keep the previous `c-a1b2c3d4` class names without updating templates.
207
+
180
208
  ### Ignoring classes
181
209
 
182
210
  Ignored classes are left unchanged in generated CSS:
@@ -242,8 +270,8 @@ ViewComponent::ScopedStyles.configure do |config|
242
270
  # Optional @layer name for the bundled scoped stylesheet (e.g. "components"). Default: nil.
243
271
  config.components_layer = nil
244
272
 
245
- # Prefix for scoped class names (e.g. "c-" produces "c-a1b2c3d4"). Default: "c-"
246
- config.css_class_prefix = "c-"
273
+ # Prefix for scoped class names. Supports {component_name} and {class_name}. Default: "{class_name}_"
274
+ config.css_class_prefix = "{class_name}_"
247
275
  end
248
276
  ```
249
277
 
@@ -253,7 +281,7 @@ end
253
281
  | `assets_path` | `"app/assets/stylesheets"` | Directory where the bundled scoped stylesheet is written, relative to `Rails.root`. |
254
282
  | `stylesheet_name` | `"components.scoped.css"` | Filename of the bundled scoped stylesheet within `assets_path`. |
255
283
  | `components_layer` | `nil` | When set, wraps generated CSS in `@layer <name> { ... }` for cascade control. |
256
- | `css_class_prefix` | `"c-"` | Prefix prepended to scoped class names (e.g. `"vc-"` → `"vc-a1b2c3d4"`). |
284
+ | `css_class_prefix` | `"{class_name}_"` | Prefix template for scoped class names. Supports `{class_name}` and `{component_name}` (namespaces joined by `/`, `Component` suffix stripped; e.g. `.component` → `component_a1b2c3d4`). Use `"c-"` to match 0.4.x behavior. |
257
285
 
258
286
  ## Related projects
259
287
 
@@ -13,6 +13,6 @@ ViewComponent::ScopedStyles.configure do |config|
13
13
  # Optional @layer name for the bundled scoped stylesheet (e.g. "components"). Default: nil.
14
14
  config.components_layer = <%= components_layer_value %>
15
15
 
16
- # Prefix for scoped class names (e.g. "c-" produces "c-a1b2c3d4"). Default: "c-"
16
+ # Prefix for scoped class names. Supports {component_name} and {class_name} variables. Default: "{class_name}_"
17
17
  config.css_class_prefix = <%= css_class_prefix_value %>
18
18
  end
@@ -52,13 +52,14 @@ module ViewComponent
52
52
  register_styles_if_rails_loaded
53
53
  end
54
54
 
55
- # Sets the prefix for scoped class names on this component (e.g. +"vc-"+ → +"vc-a1b2c3d4"+).
55
+ # Sets the prefix template for scoped class names on this component.
56
56
  #
57
57
  # Overrides {ViewComponent::ScopedStyles.configuration}.css_class_prefix for this component.
58
+ # Supports +{component_name}+ and +{class_name}+ template variables.
58
59
  #
59
60
  # Clears cached generated styles when +prefix+ changes.
60
61
  #
61
- # @param prefix [String, nil] prefix without a hash suffix (e.g. +"c-"+, +"vc-"+)
62
+ # @param prefix [String, nil] prefix template without a hash suffix (e.g. +"{class_name}_"+)
62
63
  def css_class_prefix(prefix = nil)
63
64
  if prefix
64
65
  const_set(:CSS_CLASS_PREFIX, prefix.to_s)
@@ -154,7 +155,9 @@ module ViewComponent
154
155
  sorted_classes = scoped_map.keys.sort_by(&:length).reverse
155
156
 
156
157
  sorted_classes.reduce(styles_content) do |content, css_class|
157
- content.gsub(/\.#{Regexp.escape(css_class)}\b/, ".#{class_map[css_class]}")
158
+ scoped = class_map[css_class]
159
+ escaped = CssClassPrefix.escape_for_css_selector(scoped)
160
+ content.gsub(/\.#{Regexp.escape(css_class)}\b/, ".#{escaped}")
158
161
  end
159
162
  end
160
163
 
@@ -175,7 +178,20 @@ module ViewComponent
175
178
  input = is_primary ? styles_content : "#{styles_content}:#{css_class}"
176
179
  hash = ::Digest::MD5.hexdigest(input)[0..7]
177
180
 
178
- "#{scoped_css_class_prefix}#{hash}"
181
+ prefix = CssClassPrefix.interpolate(
182
+ scoped_css_class_prefix,
183
+ component_name: scoped_component_name,
184
+ class_name: css_class
185
+ )
186
+ "#{prefix}#{hash}"
187
+ end
188
+
189
+ def scoped_component_name
190
+ return "" unless name
191
+
192
+ parts = name.split("::")
193
+ parts[-1] = parts[-1].delete_suffix("Component")
194
+ parts.join("/")
179
195
  end
180
196
 
181
197
  def scoped_css_class_prefix
@@ -36,9 +36,20 @@ module ViewComponent
36
36
  # @return [String, nil] default: +nil+ (no layer wrapper)
37
37
  attr_accessor :components_layer
38
38
 
39
- # Prefix prepended to scoped class names (e.g. +"c-"+ → +"c-a1b2c3d4"+).
39
+ # Prefix prepended to scoped class names.
40
40
  #
41
- # @return [String] default: +"c-"+
41
+ # Supports template variables:
42
+ #
43
+ # * +{component_name}+ — component name with namespaces joined by +/+,
44
+ # and a trailing +Component+ suffix removed
45
+ # * +{class_name}+ — the CSS class being scoped
46
+ #
47
+ # Example: +"{class_name}_"+ with +.component+ → +"component_a1b2c3d4"+.
48
+ #
49
+ # To maintain compatibility with versions < 0.5.0 and avoid rewriting
50
+ # existing stylesheets, set this to +"c-"+.
51
+ #
52
+ # @return [String] default: +"{class_name}_"+
42
53
  attr_accessor :css_class_prefix
43
54
 
44
55
  def initialize
@@ -46,7 +57,7 @@ module ViewComponent
46
57
  @assets_path = File.join("app", "assets", "stylesheets")
47
58
  @stylesheet_name = "components.scoped.css"
48
59
  @components_layer = nil
49
- @css_class_prefix = "c-"
60
+ @css_class_prefix = "{class_name}_"
50
61
  end
51
62
  end
52
63
 
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module ScopedStyles
5
+ # Interpolates +css_class_prefix+ template variables and escapes scoped class
6
+ # names for use in compiled CSS selectors.
7
+ class CssClassPrefix
8
+ VARIABLES = %w[component_name class_name].freeze
9
+ VARIABLE_PATTERN = /\{(#{VARIABLES.join("|")})\}/
10
+
11
+ class << self
12
+ # Replaces +{component_name}+ and +{class_name}+ in +template+.
13
+ #
14
+ # @param template [String] prefix template (e.g. +"{class_name}_"+)
15
+ # @param component_name [String] component name with namespaces joined by +/+,
16
+ # and a trailing +Component+ suffix removed
17
+ # @param class_name [String] CSS class being scoped
18
+ # @return [String]
19
+ def interpolate(template, component_name:, class_name:)
20
+ template.gsub(VARIABLE_PATTERN) do
21
+ case ::Regexp.last_match(1)
22
+ when "component_name" then component_name
23
+ when "class_name" then class_name
24
+ end
25
+ end
26
+ end
27
+
28
+ # Escapes characters that are invalid in unescaped CSS class selectors.
29
+ #
30
+ # @param class_name [String]
31
+ # @return [String]
32
+ def escape_for_css_selector(class_name)
33
+ class_name.gsub("/", '\/')
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ViewComponent
4
4
  module ScopedStyles
5
- VERSION = "0.4.1"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "scoped_styles/version"
4
4
  require_relative "scoped_styles/configuration"
5
+ require_relative "scoped_styles/css_class_prefix"
5
6
  require "active_support/concern"
6
7
  require_relative "scoped_styles/concern"
7
8
  require_relative "scoped_styles/stylist"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component-scoped_styles
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Edwards
@@ -115,6 +115,7 @@ files:
115
115
  - lib/view_component/scoped_styles.rb
116
116
  - lib/view_component/scoped_styles/concern.rb
117
117
  - lib/view_component/scoped_styles/configuration.rb
118
+ - lib/view_component/scoped_styles/css_class_prefix.rb
118
119
  - lib/view_component/scoped_styles/railtie.rb
119
120
  - lib/view_component/scoped_styles/stylist.rb
120
121
  - lib/view_component/scoped_styles/stylist/writer.rb