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 +4 -4
- data/README.md +35 -7
- data/lib/generators/view_component/scoped_styles/templates/view_component_scoped_styles.rb.tt +1 -1
- data/lib/view_component/scoped_styles/concern.rb +20 -4
- data/lib/view_component/scoped_styles/configuration.rb +14 -3
- data/lib/view_component/scoped_styles/css_class_prefix.rb +38 -0
- data/lib/view_component/scoped_styles/version.rb +1 -1
- data/lib/view_component/scoped_styles.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6eabd4a24031f8acbe3e73fba03f5bd1d8263feee45a8836e827fe4ce3e2bcf3
|
|
4
|
+
data.tar.gz: 632ae34483c93fdadb82fac21ac8b0a409ff9ae6b07098ff080c5e32a935e8fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 `.
|
|
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. `
|
|
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 { ... }
|
|
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
|
|
246
|
-
config.css_class_prefix = "
|
|
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` | `"
|
|
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
|
|
data/lib/generators/view_component/scoped_styles/templates/view_component_scoped_styles.rb.tt
CHANGED
|
@@ -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
|
|
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
|
|
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. +"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
39
|
+
# Prefix prepended to scoped class names.
|
|
40
40
|
#
|
|
41
|
-
#
|
|
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 = "
|
|
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
|
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
|
+
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
|