view_component 2.41.0 → 2.42.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of view_component might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/docs/CHANGELOG.md +26 -2
- data/lib/rails/generators/stimulus/component_generator.rb +11 -0
- data/lib/rails/generators/stimulus/templates/component_controller.js.tt +1 -1
- data/lib/view_component/base.rb +15 -2
- data/lib/view_component/polymorphic_slots.rb +74 -0
- data/lib/view_component/slot_v2.rb +3 -8
- data/lib/view_component/slotable_v2.rb +32 -20
- data/lib/view_component/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec259ffe370fa7001ca5669ff70484a339564e113b5adcde81862873375a91f3
|
4
|
+
data.tar.gz: a1ffead994abcb10cf05a39a5d6cdadbfcc7987c19682858356b16574cda5bc9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43b9b1daceb39a542bf2923bf33420c3c3bce34188190393f54982eef65fa3f6bd27315b07ac86fc5fef95847182eeae1994f32e68611b08839287569808e43b
|
7
|
+
data.tar.gz: 761538016609ef056dd3940bc84e2fa425c42a98bfd860f9123b368a75c2c3a6ece671c3fd8454a5e2e754a34d10b30448e5f41dc4f679ab7e38ef7e9b0277a4
|
data/README.md
CHANGED
data/docs/CHANGELOG.md
CHANGED
@@ -5,7 +5,31 @@ title: Changelog
|
|
5
5
|
|
6
6
|
# Changelog
|
7
7
|
|
8
|
-
##
|
8
|
+
## 2.42.0
|
9
|
+
|
10
|
+
* Add logo files and page to docs.
|
11
|
+
|
12
|
+
*Dylan Smith*
|
13
|
+
|
14
|
+
* Add `ViewComponents in practice` documentation.
|
15
|
+
|
16
|
+
*Joel Hawksley*
|
17
|
+
|
18
|
+
* Fix bug where calling lambda slots without arguments would break in Ruby < 2.7.
|
19
|
+
|
20
|
+
*Manuel Puyol*
|
21
|
+
|
22
|
+
* Improve Stimulus controller template to import from `stimulus` or `@hotwired/stimulus`.
|
23
|
+
|
24
|
+
*Mario Schüttel*
|
25
|
+
|
26
|
+
* Fix bug where `helpers` would instantiate and use a new `view_context` in each component.
|
27
|
+
|
28
|
+
*Blake Williams*, *Ian C. Anderson*
|
29
|
+
|
30
|
+
* Implement polymorphic slots as experimental feature. See the Slots documentation to learn more.
|
31
|
+
|
32
|
+
*Cameron Dutro*
|
9
33
|
|
10
34
|
## 2.41.0
|
11
35
|
|
@@ -42,7 +66,7 @@ title: Changelog
|
|
42
66
|
|
43
67
|
*Hans Lemuet*
|
44
68
|
|
45
|
-
* Forward keyword arguments from slot wrapper to component instance.
|
69
|
+
* Forward keyword arguments from slot wrapper to component instance using ruby2_keywords.
|
46
70
|
|
47
71
|
*Cameron Dutro*
|
48
72
|
|
@@ -12,6 +12,12 @@ module Stimulus
|
|
12
12
|
template "component_controller.js", destination
|
13
13
|
end
|
14
14
|
|
15
|
+
def stimulus_module
|
16
|
+
return "stimulus" if legacy_stimulus?
|
17
|
+
|
18
|
+
"@hotwired/stimulus"
|
19
|
+
end
|
20
|
+
|
15
21
|
private
|
16
22
|
|
17
23
|
def destination
|
@@ -21,6 +27,11 @@ module Stimulus
|
|
21
27
|
File.join(component_path, class_path, "#{file_name}_component_controller.js")
|
22
28
|
end
|
23
29
|
end
|
30
|
+
|
31
|
+
def legacy_stimulus?
|
32
|
+
package_json_pathname = Rails.root.join("package.json")
|
33
|
+
package_json_pathname.exist? && JSON.parse(package_json_pathname.read).dig("dependencies", "stimulus").present?
|
34
|
+
end
|
24
35
|
end
|
25
36
|
end
|
26
37
|
end
|
data/lib/view_component/base.rb
CHANGED
@@ -5,6 +5,7 @@ require "active_support/configurable"
|
|
5
5
|
require "view_component/collection"
|
6
6
|
require "view_component/compile_cache"
|
7
7
|
require "view_component/content_areas"
|
8
|
+
require "view_component/polymorphic_slots"
|
8
9
|
require "view_component/previewable"
|
9
10
|
require "view_component/slotable"
|
10
11
|
require "view_component/slotable_v2"
|
@@ -28,6 +29,8 @@ module ViewComponent
|
|
28
29
|
class_attribute :content_areas
|
29
30
|
self.content_areas = [] # class_attribute:default doesn't work until Rails 5.2
|
30
31
|
|
32
|
+
attr_accessor :original_view_context
|
33
|
+
|
31
34
|
# EXPERIMENTAL: This API is experimental and may be removed at any time.
|
32
35
|
# Hook for allowing components to do work as part of the compilation process.
|
33
36
|
#
|
@@ -49,6 +52,8 @@ module ViewComponent
|
|
49
52
|
self.class.compile(raise_errors: true)
|
50
53
|
|
51
54
|
@view_context = view_context
|
55
|
+
self.original_view_context ||= view_context
|
56
|
+
|
52
57
|
@lookup_context ||= view_context.lookup_context
|
53
58
|
|
54
59
|
# required for path helpers in older Rails versions
|
@@ -132,9 +137,10 @@ module ViewComponent
|
|
132
137
|
# @private
|
133
138
|
def render(options = {}, args = {}, &block)
|
134
139
|
if options.is_a? ViewComponent::Base
|
140
|
+
options.original_view_context = original_view_context
|
135
141
|
super
|
136
142
|
else
|
137
|
-
|
143
|
+
original_view_context.render(options, args, &block)
|
138
144
|
end
|
139
145
|
end
|
140
146
|
|
@@ -173,7 +179,14 @@ module ViewComponent
|
|
173
179
|
)
|
174
180
|
end
|
175
181
|
|
176
|
-
|
182
|
+
# Attempt to re-use the original view_context passed to the first
|
183
|
+
# component rendered in the rendering pipeline. This prevents the
|
184
|
+
# instantiation of a new view_context via `controller.view_context` which
|
185
|
+
# always returns a new instance of the view context class.
|
186
|
+
#
|
187
|
+
# This allows ivars to remain persisted when using the same helper via
|
188
|
+
# `helpers` across multiple components and partials.
|
189
|
+
@__vc_helpers ||= original_view_context || controller.view_context
|
177
190
|
end
|
178
191
|
|
179
192
|
# Exposes .virtual_path as an instance method
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module PolymorphicSlots
|
5
|
+
# In older rails versions, using a concern isn't a good idea here because they appear to not work with
|
6
|
+
# Module#prepend and class methods.
|
7
|
+
def self.included(base)
|
8
|
+
base.singleton_class.prepend(ClassMethods)
|
9
|
+
base.include(InstanceMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def renders_one(slot_name, callable = nil)
|
14
|
+
return super unless callable.is_a?(Hash) && callable.key?(:types)
|
15
|
+
|
16
|
+
validate_singular_slot_name(slot_name)
|
17
|
+
register_polymorphic_slot(slot_name, callable[:types], collection: false)
|
18
|
+
end
|
19
|
+
|
20
|
+
def renders_many(slot_name, callable = nil)
|
21
|
+
return super unless callable.is_a?(Hash) && callable.key?(:types)
|
22
|
+
|
23
|
+
validate_plural_slot_name(slot_name)
|
24
|
+
register_polymorphic_slot(slot_name, callable[:types], collection: true)
|
25
|
+
end
|
26
|
+
|
27
|
+
def register_polymorphic_slot(slot_name, types, collection:)
|
28
|
+
renderable_hash = types.each_with_object({}) do |(poly_type, poly_callable), memo|
|
29
|
+
memo[poly_type] = define_slot(
|
30
|
+
"#{slot_name}_#{poly_type}", collection: collection, callable: poly_callable
|
31
|
+
)
|
32
|
+
|
33
|
+
getter_name = slot_name
|
34
|
+
setter_name =
|
35
|
+
if collection
|
36
|
+
"#{ActiveSupport::Inflector.singularize(slot_name)}_#{poly_type}"
|
37
|
+
else
|
38
|
+
"#{slot_name}_#{poly_type}"
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method(getter_name) do
|
42
|
+
get_slot(slot_name)
|
43
|
+
end
|
44
|
+
ruby2_keywords(getter_name.to_sym) if respond_to?(:ruby2_keywords, true)
|
45
|
+
|
46
|
+
define_method(setter_name) do |*args, &block|
|
47
|
+
set_polymorphic_slot(slot_name, poly_type, *args, &block)
|
48
|
+
end
|
49
|
+
ruby2_keywords(setter_name.to_sym) if respond_to?(:ruby2_keywords, true)
|
50
|
+
end
|
51
|
+
|
52
|
+
self.registered_slots[slot_name] = {
|
53
|
+
collection: collection,
|
54
|
+
renderable_hash: renderable_hash
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module InstanceMethods
|
60
|
+
def set_polymorphic_slot(slot_name, poly_type = nil, *args, &block)
|
61
|
+
slot_definition = self.class.registered_slots[slot_name]
|
62
|
+
|
63
|
+
if !slot_definition[:collection] && get_slot(slot_name)
|
64
|
+
raise ArgumentError, "content for slot '#{slot_name}' has already been provided"
|
65
|
+
end
|
66
|
+
|
67
|
+
poly_def = slot_definition[:renderable_hash][poly_type]
|
68
|
+
|
69
|
+
set_slot(slot_name, poly_def, *args, &block)
|
70
|
+
end
|
71
|
+
ruby2_keywords(:set_polymorphic_slot) if respond_to?(:ruby2_keywords, true)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -86,15 +86,10 @@ module ViewComponent
|
|
86
86
|
# end
|
87
87
|
# end
|
88
88
|
#
|
89
|
-
|
90
|
-
|
91
|
-
@__vc_component_instance.public_send(symbol, *args, **kwargs, &block)
|
92
|
-
end
|
93
|
-
else
|
94
|
-
def method_missing(symbol, *args, &block)
|
95
|
-
@__vc_component_instance.public_send(symbol, *args, &block)
|
96
|
-
end
|
89
|
+
def method_missing(symbol, *args, &block)
|
90
|
+
@__vc_component_instance.public_send(symbol, *args, &block)
|
97
91
|
end
|
92
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
98
93
|
|
99
94
|
def html_safe?
|
100
95
|
to_s.html_safe?
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/concern"
|
4
|
-
|
5
4
|
require "view_component/slot_v2"
|
6
5
|
|
7
6
|
module ViewComponent
|
@@ -67,13 +66,14 @@ module ViewComponent
|
|
67
66
|
def renders_one(slot_name, callable = nil)
|
68
67
|
validate_singular_slot_name(slot_name)
|
69
68
|
|
70
|
-
define_method slot_name do |*args,
|
71
|
-
if args.empty? &&
|
69
|
+
define_method slot_name do |*args, &block|
|
70
|
+
if args.empty? && block.nil?
|
72
71
|
get_slot(slot_name)
|
73
72
|
else
|
74
|
-
set_slot(slot_name, *args,
|
73
|
+
set_slot(slot_name, nil, *args, &block)
|
75
74
|
end
|
76
75
|
end
|
76
|
+
ruby2_keywords(slot_name.to_sym) if respond_to?(:ruby2_keywords, true)
|
77
77
|
|
78
78
|
register_slot(slot_name, collection: false, callable: callable)
|
79
79
|
end
|
@@ -123,9 +123,10 @@ module ViewComponent
|
|
123
123
|
# Define setter for singular names
|
124
124
|
# e.g. `renders_many :items` allows fetching all tabs with
|
125
125
|
# `component.tabs` and setting a tab with `component.tab`
|
126
|
-
define_method singular_name do |*args,
|
127
|
-
set_slot(slot_name, *args,
|
126
|
+
define_method singular_name do |*args, &block|
|
127
|
+
set_slot(slot_name, nil, *args, &block)
|
128
128
|
end
|
129
|
+
ruby2_keywords(singular_name.to_sym) if respond_to?(:ruby2_keywords, true)
|
129
130
|
|
130
131
|
# Instantiates and and adds multiple slots forwarding the first
|
131
132
|
# argument to each slot constructor
|
@@ -134,7 +135,7 @@ module ViewComponent
|
|
134
135
|
get_slot(slot_name)
|
135
136
|
else
|
136
137
|
collection_args.map do |args|
|
137
|
-
set_slot(slot_name, **args, &block)
|
138
|
+
set_slot(slot_name, nil, **args, &block)
|
138
139
|
end
|
139
140
|
end
|
140
141
|
end
|
@@ -162,27 +163,37 @@ module ViewComponent
|
|
162
163
|
|
163
164
|
private
|
164
165
|
|
165
|
-
def register_slot(slot_name,
|
166
|
+
def register_slot(slot_name, **kwargs)
|
167
|
+
self.registered_slots[slot_name] = define_slot(slot_name, **kwargs)
|
168
|
+
end
|
169
|
+
|
170
|
+
def define_slot(slot_name, collection:, callable:)
|
166
171
|
# Setup basic slot data
|
167
172
|
slot = {
|
168
173
|
collection: collection,
|
169
174
|
}
|
175
|
+
return slot unless callable
|
176
|
+
|
170
177
|
# If callable responds to `render_in`, we set it on the slot as a renderable
|
171
|
-
if callable
|
178
|
+
if callable.respond_to?(:method_defined?) && callable.method_defined?(:render_in)
|
172
179
|
slot[:renderable] = callable
|
173
180
|
elsif callable.is_a?(String)
|
174
181
|
# If callable is a string, we assume it's referencing an internal class
|
175
182
|
slot[:renderable_class_name] = callable
|
176
|
-
elsif callable
|
183
|
+
elsif callable.respond_to?(:call)
|
177
184
|
# If slot does not respond to `render_in`, we assume it's a proc,
|
178
185
|
# define a method, and save a reference to it to call when setting
|
179
186
|
method_name = :"_call_#{slot_name}"
|
180
187
|
define_method method_name, &callable
|
181
188
|
slot[:renderable_function] = instance_method(method_name)
|
189
|
+
else
|
190
|
+
raise(
|
191
|
+
ArgumentError,
|
192
|
+
"invalid slot definition. Please pass a class, string, or callable (i.e. proc, lambda, etc)"
|
193
|
+
)
|
182
194
|
end
|
183
195
|
|
184
|
-
|
185
|
-
self.registered_slots[slot_name] = slot
|
196
|
+
slot
|
186
197
|
end
|
187
198
|
|
188
199
|
def validate_plural_slot_name(slot_name)
|
@@ -235,9 +246,8 @@ module ViewComponent
|
|
235
246
|
end
|
236
247
|
end
|
237
248
|
|
238
|
-
def set_slot(slot_name, *args,
|
239
|
-
slot_definition
|
240
|
-
|
249
|
+
def set_slot(slot_name, slot_definition = nil, *args, &block)
|
250
|
+
slot_definition ||= self.class.registered_slots[slot_name]
|
241
251
|
slot = SlotV2.new(self)
|
242
252
|
|
243
253
|
# Passing the block to the sub-component wrapper like this has two
|
@@ -253,23 +263,24 @@ module ViewComponent
|
|
253
263
|
|
254
264
|
# If class
|
255
265
|
if slot_definition[:renderable]
|
256
|
-
slot.__vc_component_instance = slot_definition[:renderable].new(*args
|
266
|
+
slot.__vc_component_instance = slot_definition[:renderable].new(*args)
|
257
267
|
# If class name as a string
|
258
268
|
elsif slot_definition[:renderable_class_name]
|
259
269
|
slot.__vc_component_instance =
|
260
|
-
self.class.const_get(slot_definition[:renderable_class_name]).new(*args
|
270
|
+
self.class.const_get(slot_definition[:renderable_class_name]).new(*args)
|
261
271
|
# If passed a lambda
|
262
272
|
elsif slot_definition[:renderable_function]
|
263
273
|
# Use `bind(self)` to ensure lambda is executed in the context of the
|
264
274
|
# current component. This is necessary to allow the lambda to access helper
|
265
275
|
# methods like `content_tag` as well as parent component state.
|
276
|
+
renderable_function = slot_definition[:renderable_function].bind(self)
|
266
277
|
renderable_value =
|
267
278
|
if block_given?
|
268
|
-
|
269
|
-
view_context.capture(*args,
|
279
|
+
renderable_function.call(*args) do |*args|
|
280
|
+
view_context.capture(*args, &block)
|
270
281
|
end
|
271
282
|
else
|
272
|
-
|
283
|
+
renderable_function.call(*args)
|
273
284
|
end
|
274
285
|
|
275
286
|
# Function calls can return components, so if it's a component handle it specially
|
@@ -291,5 +302,6 @@ module ViewComponent
|
|
291
302
|
|
292
303
|
slot
|
293
304
|
end
|
305
|
+
ruby2_keywords(:set_slot) if respond_to?(:ruby2_keywords, true)
|
294
306
|
end
|
295
307
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.42.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-10-
|
11
|
+
date: 2021-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -314,6 +314,7 @@ files:
|
|
314
314
|
- lib/view_component/content_areas.rb
|
315
315
|
- lib/view_component/engine.rb
|
316
316
|
- lib/view_component/instrumentation.rb
|
317
|
+
- lib/view_component/polymorphic_slots.rb
|
317
318
|
- lib/view_component/preview.rb
|
318
319
|
- lib/view_component/preview_template_error.rb
|
319
320
|
- lib/view_component/previewable.rb
|
@@ -355,7 +356,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
355
356
|
- !ruby/object:Gem::Version
|
356
357
|
version: '0'
|
357
358
|
requirements: []
|
358
|
-
rubygems_version: 3.1.
|
359
|
+
rubygems_version: 3.1.2
|
359
360
|
signing_key:
|
360
361
|
specification_version: 4
|
361
362
|
summary: View components for Rails
|