view_component-scoped_styles 0.1.0 → 0.2.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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65712096bb4c02068da2dca36b66bad2d4dffc773234d42910dcadc5ef354a21
|
|
4
|
+
data.tar.gz: 12d6dc8afdc1b530a749810a3fb32b13545c6a3421dd8e21c7c8f85c76b1539d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6c7b9ede8f02445070c51dbe34e935ff43e9cbc60c3594d270915454cb6e583b02748b21b1531209fdef17b99d5654815333997fb6d3bd9fe5f46336957899ba
|
|
7
|
+
data.tar.gz: 239fe37c5445b1aac40e589da187643a37c66d9b8cdf0f9ffbaae51049d8778bea1a86cab5fa38307a3022070140df2e39d393b76f266a97434cb2ecfbe6b106
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ViewComponent::ScopedStyles
|
|
1
|
+
# ViewComponent::ScopedStyles [](https://badge.fury.io/rb/view_component-scoped_styles)
|
|
2
2
|
|
|
3
3
|
Scoped, colocated CSS for [ViewComponent](https://viewcomponent.org/).
|
|
4
4
|
|
|
@@ -8,13 +8,14 @@ E.g. `.button` becomes `.c-a1b2c3d4`
|
|
|
8
8
|
|
|
9
9
|
## Table of Contents
|
|
10
10
|
|
|
11
|
-
- [ViewComponent::ScopedStyles](#viewcomponentscopedstyles)
|
|
11
|
+
- [ViewComponent::ScopedStyles ](#viewcomponentscopedstyles-)
|
|
12
12
|
- [Table of Contents](#table-of-contents)
|
|
13
13
|
- [Installation](#installation)
|
|
14
14
|
- [Usage](#usage)
|
|
15
15
|
- [1. Using a sidecar stylesheet](#1-using-a-sidecar-stylesheet)
|
|
16
16
|
- [2. Using a styles block in the component](#2-using-a-styles-block-in-the-component)
|
|
17
17
|
- [Referencing classes](#referencing-classes)
|
|
18
|
+
- [Ignoring classes](#ignoring-classes)
|
|
18
19
|
- [Using the scoped CSS](#using-the-scoped-css)
|
|
19
20
|
- [Configuration](#configuration)
|
|
20
21
|
- [Related projects](#related-projects)
|
|
@@ -160,6 +161,33 @@ end
|
|
|
160
161
|
</div>
|
|
161
162
|
```
|
|
162
163
|
|
|
164
|
+
### Ignoring classes
|
|
165
|
+
|
|
166
|
+
Ignored classes are left unchanged in generated CSS:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
class ExampleComponent < ViewComponent::Base
|
|
170
|
+
include ViewComponent::ScopedStyles
|
|
171
|
+
|
|
172
|
+
ignored_css_classes "is-open", "active"
|
|
173
|
+
|
|
174
|
+
styles do
|
|
175
|
+
<<~CSS
|
|
176
|
+
.component { ... }
|
|
177
|
+
.is-open { ... } # stays .is-open in components.scoped.css
|
|
178
|
+
CSS
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
In your view, you can either reference the class directly:
|
|
183
|
+
```erb
|
|
184
|
+
<div class="<%= component_class %> is-open">
|
|
185
|
+
```
|
|
186
|
+
or via the `component_class` helper:
|
|
187
|
+
```erb
|
|
188
|
+
<div class="<%= component_class %> <%= component_class("is-open") %>">
|
|
189
|
+
```
|
|
190
|
+
|
|
163
191
|
### Using the scoped CSS
|
|
164
192
|
|
|
165
193
|
All scoped CSS will be compiled into `app/assets/stylesheets/components.scoped.css`.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
3
5
|
module ViewComponent
|
|
4
6
|
module ScopedStyles
|
|
5
7
|
extend ActiveSupport::Concern
|
|
@@ -9,11 +11,12 @@ module ViewComponent
|
|
|
9
11
|
|
|
10
12
|
# Default root class for +component_class+ when it matches a selector in the CSS.
|
|
11
13
|
COMPONENT_CSS_CLASS = "component".freeze
|
|
14
|
+
IGNORED_CSS_CLASSES = [].freeze
|
|
12
15
|
|
|
13
16
|
class_methods do
|
|
14
17
|
# Sets which CSS class is the root for +component_class+ (no argument).
|
|
15
|
-
# Also triggers style registration when Rails is loaded
|
|
16
|
-
# runs via the Railtie for all styled components).
|
|
18
|
+
# Also triggers style registration in development when Rails is loaded
|
|
19
|
+
# (registration also runs via the Railtie for all styled components).
|
|
17
20
|
#
|
|
18
21
|
# All class selectors are rewritten to scoped names in +components.scoped.css+.
|
|
19
22
|
# The primary class uses an id from the full stylesheet (e.g. +.icon+ → +.c-99d08d5a+);
|
|
@@ -26,7 +29,24 @@ module ViewComponent
|
|
|
26
29
|
# @param name [String, nil] selector name without a leading dot (e.g. +"icon"+)
|
|
27
30
|
def component_css_class(name = nil)
|
|
28
31
|
if name
|
|
29
|
-
const_set(:COMPONENT_CSS_CLASS, name)
|
|
32
|
+
const_set(:COMPONENT_CSS_CLASS, name.to_s)
|
|
33
|
+
clear_component_style_cache
|
|
34
|
+
end
|
|
35
|
+
register_styles_if_rails_loaded
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Declares CSS class selectors that are not rewritten to scoped names.
|
|
39
|
+
#
|
|
40
|
+
# Ignored classes stay in +components.scoped.css+ as written (e.g. +.is-open+).
|
|
41
|
+
# +component_class("is-open")+ returns the original name.
|
|
42
|
+
#
|
|
43
|
+
# Clears cached generated styles when the list changes.
|
|
44
|
+
#
|
|
45
|
+
# @param classes [String, Symbol] selector names without a leading dot
|
|
46
|
+
def ignored_css_classes(*classes)
|
|
47
|
+
if classes.any?
|
|
48
|
+
names = classes.flatten.map { |css_class| css_class.to_s.delete_prefix(".") }
|
|
49
|
+
const_set(:IGNORED_CSS_CLASSES, names.freeze)
|
|
30
50
|
clear_component_style_cache
|
|
31
51
|
end
|
|
32
52
|
register_styles_if_rails_loaded
|
|
@@ -40,8 +60,10 @@ module ViewComponent
|
|
|
40
60
|
generate_component_styles
|
|
41
61
|
end
|
|
42
62
|
|
|
43
|
-
# Writes this component's processed styles to
|
|
63
|
+
# Writes this component's processed styles to +components.scoped.css+.
|
|
64
|
+
# Only runs in development; production and test use the committed stylesheet.
|
|
44
65
|
def register_styles
|
|
66
|
+
return unless register_styles_to_stylesheet?
|
|
45
67
|
return unless @styles_block || has_stylesheet?
|
|
46
68
|
|
|
47
69
|
Stylist.register(self)
|
|
@@ -66,18 +88,22 @@ module ViewComponent
|
|
|
66
88
|
end
|
|
67
89
|
|
|
68
90
|
def register_styles_if_rails_loaded
|
|
69
|
-
return unless
|
|
91
|
+
return unless register_styles_to_stylesheet?
|
|
70
92
|
return unless defined?(Rails::Server) # only web server boot path
|
|
71
93
|
|
|
72
94
|
register_styles
|
|
73
95
|
end
|
|
74
96
|
|
|
97
|
+
def register_styles_to_stylesheet?
|
|
98
|
+
defined?(Rails::Application) && Rails.application && Rails.env.development?
|
|
99
|
+
end
|
|
100
|
+
|
|
75
101
|
def generate_component_styles
|
|
76
102
|
styles_content = generate_styles_content
|
|
77
103
|
css_classes = extract_css_classes(styles_content)
|
|
78
104
|
primary_class = primary_css_class(css_classes)
|
|
79
105
|
|
|
80
|
-
@component_id =
|
|
106
|
+
@component_id = component_id_for(primary_class, styles_content)
|
|
81
107
|
@component_class_map = build_component_class_map(styles_content, css_classes, primary_class)
|
|
82
108
|
@component_styles = replace_css_classes(styles_content, @component_class_map)
|
|
83
109
|
end
|
|
@@ -97,28 +123,42 @@ module ViewComponent
|
|
|
97
123
|
|
|
98
124
|
def build_component_class_map(styles_content, css_classes, primary_class)
|
|
99
125
|
css_classes.index_with do |css_class|
|
|
100
|
-
if css_class
|
|
101
|
-
|
|
126
|
+
if ignored_css_class?(css_class)
|
|
127
|
+
css_class
|
|
102
128
|
else
|
|
103
|
-
generate_scoped_class_id(styles_content, css_class)
|
|
129
|
+
generate_scoped_class_id(styles_content, css_class, primary_class)
|
|
104
130
|
end
|
|
105
131
|
end
|
|
106
132
|
end
|
|
107
133
|
|
|
108
134
|
def replace_css_classes(styles_content, class_map)
|
|
109
|
-
class_map.
|
|
135
|
+
scoped_map = class_map.reject do |css_class, scoped|
|
|
136
|
+
css_class == scoped
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
sorted_classes = scoped_map.keys.sort_by(&:length).reverse
|
|
140
|
+
|
|
141
|
+
sorted_classes.reduce(styles_content) do |content, css_class|
|
|
110
142
|
content.gsub(/\.#{Regexp.escape(css_class)}\b/, ".#{class_map[css_class]}")
|
|
111
143
|
end
|
|
112
144
|
end
|
|
113
145
|
|
|
114
|
-
def
|
|
115
|
-
|
|
146
|
+
def component_id_for(primary_class, styles_content)
|
|
147
|
+
if ignored_css_class?(primary_class)
|
|
148
|
+
primary_class
|
|
149
|
+
else
|
|
150
|
+
generate_scoped_class_id(styles_content, primary_class, primary_class)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
116
153
|
|
|
117
|
-
|
|
154
|
+
def ignored_css_class?(css_class)
|
|
155
|
+
self::IGNORED_CSS_CLASSES.include?(css_class)
|
|
118
156
|
end
|
|
119
157
|
|
|
120
|
-
def generate_scoped_class_id(styles_content, css_class)
|
|
121
|
-
|
|
158
|
+
def generate_scoped_class_id(styles_content, css_class, primary_class)
|
|
159
|
+
is_primary = css_class == primary_class
|
|
160
|
+
input = is_primary ? styles_content : "#{styles_content}:#{css_class}"
|
|
161
|
+
hash = ::Digest::MD5.hexdigest(input)[0..7]
|
|
122
162
|
|
|
123
163
|
"c-#{hash}"
|
|
124
164
|
end
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/core_ext/module/delegation"
|
|
3
4
|
require "rails/railtie"
|
|
4
5
|
|
|
5
6
|
module ViewComponent
|
|
6
7
|
module ScopedStyles
|
|
7
8
|
class Railtie < Rails::Railtie
|
|
8
9
|
config.after_initialize do
|
|
10
|
+
next unless Rails.env.development?
|
|
11
|
+
|
|
9
12
|
ViewComponent::ScopedStyles::Railtie.load_and_register_components
|
|
10
13
|
end
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
initializer "view_component.scoped_styles.reload" do |app|
|
|
16
|
+
next unless Rails.env.development?
|
|
17
|
+
|
|
18
|
+
app.config.to_prepare do
|
|
14
19
|
ViewComponent::ScopedStyles::Railtie.register_components
|
|
15
20
|
end
|
|
16
21
|
end
|