view_component_reflex 1.1.1 → 1.6.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: 0d42e80a31d1a4a27c828f02fc584e9c5eb6660a6d5697e0db3a9631be358dec
|
4
|
+
data.tar.gz: 96450604f1d0965c104171a8b1d667f6d4dc38019e0c6434fd974ff80a3d9f26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: deaf8859ae5816796b965ba5c7e1022032253f44fc1e6c94bcdda8dd481d20afb1319a63c99acc98e184908c5c0b299384e1b77cecef4cd5099263bc4f0d4606
|
7
|
+
data.tar.gz: 6da8e3668c5cf6f2dd64c770eada3b381669dd2dac2cf11dc65b2f9e7a94d4e7d450a563506459057642d0f97629d582d1935a9eef7bbf61acfc084402b2e1cd
|
data/README.md
CHANGED
@@ -30,7 +30,7 @@ end
|
|
30
30
|
# counter_component.html.erb
|
31
31
|
<%= component_controller do %>
|
32
32
|
<p><%= @count %></p>
|
33
|
-
|
33
|
+
<%= reflex_tag :increment, :button, "Click" %>
|
34
34
|
<% end %>
|
35
35
|
```
|
36
36
|
|
@@ -39,7 +39,7 @@ end
|
|
39
39
|
In order to reconcile state to components in collections, you can specify a `collection_key` method that returns some
|
40
40
|
value unique to that component.
|
41
41
|
|
42
|
-
```
|
42
|
+
```ruby
|
43
43
|
class TodoComponent < ViewComponentReflex::Component
|
44
44
|
def initialize(todo:)
|
45
45
|
@todo = todo
|
@@ -53,6 +53,134 @@ end
|
|
53
53
|
<%= render(TodoComponent.with_collection(Todo.all)) %>
|
54
54
|
```
|
55
55
|
|
56
|
+
## API
|
57
|
+
|
58
|
+
### permit_parameter?(initial_param, new_params)
|
59
|
+
If a new parameter is passed to the component during rendering, it is used instead of what's in state.
|
60
|
+
If you're storing instances in state, you can use this to properly compare them.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
def permit_parameter?(initial_param, new_param)
|
64
|
+
if new_param.instance_of? MyModel
|
65
|
+
new_param.id == @my_model.id
|
66
|
+
else
|
67
|
+
super
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
### omitted_from_state
|
73
|
+
Return an array of instance variables you want to omit from state. Useful if you have an object
|
74
|
+
that isn't serializable as an instance variable, like a form.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
def omitted_from_state
|
78
|
+
[:@form]
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
### reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
|
83
|
+
This shares the same definition as `content_tag`, except it accepts a reflex as the first parameter.
|
84
|
+
|
85
|
+
```erb
|
86
|
+
<%= reflex_tag :increment, :button, "Click me!" %>
|
87
|
+
```
|
88
|
+
|
89
|
+
Would add a click handler to the `increment` method on your component.
|
90
|
+
|
91
|
+
To use a non-click event, specific that with `->` notiation
|
92
|
+
|
93
|
+
```erb
|
94
|
+
<%= reflex_tag "mouseenter->increment", :button, "Click me!" %>
|
95
|
+
```
|
96
|
+
|
97
|
+
### collection_key
|
98
|
+
If you're rendering a component as a collection with `MyComponent.with_collection(SomeCollection)`, you must define this method to return some unique value for the component.
|
99
|
+
This is used to reconcile state in the background.
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
def initialize
|
103
|
+
@my_model = MyModel.new
|
104
|
+
end
|
105
|
+
|
106
|
+
def collection_key
|
107
|
+
@my_model.id
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
### prevent_refresh!
|
112
|
+
By default, VCR will re-render your component after it executes your method. `revent_refresh!` prevents this from happening.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
def my_method
|
116
|
+
prevent_refresh!
|
117
|
+
@foo = Lbar
|
118
|
+
end # the rendered page will not reflect this change
|
119
|
+
```
|
120
|
+
|
121
|
+
### refresh_all!
|
122
|
+
Refresh the entire body of the page
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
def do_some_global_action
|
126
|
+
prevent_refresh!
|
127
|
+
session[:model] = MyModel.new
|
128
|
+
refresh_all!
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
### key
|
133
|
+
This is a key unique to a particular component. It's used to reconcile state between renders, and should be passed as a data attribute whenever a reflex is called
|
134
|
+
|
135
|
+
```erb
|
136
|
+
<button type="button" data-reflex="click->MyComponent#do_something" data-key="<%= key %>">Click me!</button>
|
137
|
+
```
|
138
|
+
|
139
|
+
### component_controller(options = {}, &blk)
|
140
|
+
This is a view helper to properly connect VCR to the component. It outputs `<div data-controller="my-controller" key=<%= key %></div>`
|
141
|
+
You *must* wrap your component in this for everything to work properly.
|
142
|
+
|
143
|
+
```erb
|
144
|
+
<%= component_controller do %>
|
145
|
+
<p><%= @count %></p
|
146
|
+
<% end %>
|
147
|
+
```
|
148
|
+
|
149
|
+
## Common patterns
|
150
|
+
A lot of the time, you only need to update specific components when changing instance variables. For example, changing `@loading` might only need
|
151
|
+
to display a spinner somewhere on the page. You can define setters to implicitly render the appropriate pieces of dom whenever that variable is set
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
def initialize
|
155
|
+
@loading = false
|
156
|
+
end
|
157
|
+
|
158
|
+
def loading=(new_value)
|
159
|
+
@loading = new_value
|
160
|
+
refresh! '#loader'
|
161
|
+
end
|
162
|
+
|
163
|
+
def do_expensive_action
|
164
|
+
prevent_refresh!
|
165
|
+
|
166
|
+
self.loading = true
|
167
|
+
execute_it
|
168
|
+
self.loading = false
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
```erb
|
173
|
+
<%= component_controller do %>
|
174
|
+
<div id="loader">
|
175
|
+
<% if @loading %>
|
176
|
+
<p>Loading...</p>
|
177
|
+
<% end %>
|
178
|
+
</div>
|
179
|
+
|
180
|
+
<button type="button" data-reflex="click->MyComponent#do_expensive_action" data-key="<%= key %>">Click me!</button>
|
181
|
+
<% end
|
182
|
+
```
|
183
|
+
|
56
184
|
## Custom State Adapters
|
57
185
|
|
58
186
|
ViewComponentReflex uses session for its state by default. To change this, add
|
@@ -76,13 +204,13 @@ class YourAdapter
|
|
76
204
|
end
|
77
205
|
|
78
206
|
##
|
79
|
-
# set_state is used to modify the state.
|
80
|
-
# access to the request, as well as the controller and other useful objects.
|
207
|
+
# set_state is used to modify the state.
|
81
208
|
#
|
82
|
-
#
|
209
|
+
# request - a rails request object
|
210
|
+
# controller - the current controller
|
83
211
|
# key - a unique string that identifies the component
|
84
212
|
# new_state - the new state to set
|
85
|
-
def self.set_state(
|
213
|
+
def self.set_state(request, controller, key, new_state)
|
86
214
|
# update the state
|
87
215
|
end
|
88
216
|
|
@@ -43,7 +43,11 @@ module ViewComponentReflex
|
|
43
43
|
component.instance_variable_set(k, v)
|
44
44
|
end
|
45
45
|
name.to_proc.call(component, *args)
|
46
|
-
refresh!
|
46
|
+
refresh! unless @prevent_refresh
|
47
|
+
end
|
48
|
+
|
49
|
+
def prevent_refresh!
|
50
|
+
@prevent_refresh = true
|
47
51
|
end
|
48
52
|
|
49
53
|
define_method :component_class do
|
@@ -62,7 +66,7 @@ module ViewComponentReflex
|
|
62
66
|
return @component if @component
|
63
67
|
@component = component_class.allocate
|
64
68
|
reflex = self
|
65
|
-
exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller]
|
69
|
+
exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller, :session, :prevent_refresh!]
|
66
70
|
exposed_methods.each do |meth|
|
67
71
|
@component.define_singleton_method(meth) do |*a|
|
68
72
|
reflex.send(meth, *a)
|
@@ -72,7 +76,7 @@ module ViewComponentReflex
|
|
72
76
|
end
|
73
77
|
|
74
78
|
def set_state(new_state = {})
|
75
|
-
ViewComponentReflex::Engine.state_adapter.set_state(
|
79
|
+
ViewComponentReflex::Engine.state_adapter.set_state(request, controller, element.dataset[:key], new_state)
|
76
80
|
end
|
77
81
|
|
78
82
|
def state
|
@@ -98,15 +102,23 @@ module ViewComponentReflex
|
|
98
102
|
helpers.controller.instance_variable_get(:@stimulus_reflex)
|
99
103
|
end
|
100
104
|
|
101
|
-
def component_controller(opts = {}, &blk)
|
105
|
+
def component_controller(opts_or_tag = :div, opts = {}, &blk)
|
102
106
|
self.class.init_stimulus_reflex
|
103
107
|
init_key
|
104
|
-
|
108
|
+
|
109
|
+
tag = :div
|
110
|
+
if opts_or_tag.is_a? Hash
|
111
|
+
options = opts_or_tag
|
112
|
+
else
|
113
|
+
tag = opts_or_tag
|
114
|
+
options = opts
|
115
|
+
end
|
116
|
+
options[:data] = {
|
105
117
|
controller: self.class.stimulus_controller,
|
106
118
|
key: key,
|
107
|
-
**(
|
119
|
+
**(options[:data] || {})
|
108
120
|
}
|
109
|
-
content_tag
|
121
|
+
content_tag tag, capture(&blk), options
|
110
122
|
end
|
111
123
|
|
112
124
|
# key is required if you're using state
|
@@ -121,10 +133,36 @@ module ViewComponentReflex
|
|
121
133
|
@key = key
|
122
134
|
end
|
123
135
|
|
136
|
+
def reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
|
137
|
+
action, method = reflex.to_s.split("->")
|
138
|
+
if method.nil?
|
139
|
+
method = action
|
140
|
+
action = "click"
|
141
|
+
end
|
142
|
+
data_attributes = {
|
143
|
+
reflex: "#{action}->#{self.class.name}##{method}",
|
144
|
+
key: key
|
145
|
+
}
|
146
|
+
if content_or_options_with_block.is_a?(Hash)
|
147
|
+
merge_data_attributes(content_or_options_with_block, data_attributes)
|
148
|
+
else
|
149
|
+
merge_data_attributes(options, data_attributes)
|
150
|
+
end
|
151
|
+
content_tag(name, content_or_options_with_block, options, escape, &block)
|
152
|
+
end
|
153
|
+
|
124
154
|
def collection_key
|
125
155
|
nil
|
126
156
|
end
|
127
157
|
|
158
|
+
def permit_parameter?(initial_param, new_param)
|
159
|
+
initial_param != new_param
|
160
|
+
end
|
161
|
+
|
162
|
+
def omitted_from_state
|
163
|
+
[]
|
164
|
+
end
|
165
|
+
|
128
166
|
def key
|
129
167
|
# initialize session state
|
130
168
|
if !stimulus_reflex? || session[@key].nil?
|
@@ -137,15 +175,30 @@ module ViewComponentReflex
|
|
137
175
|
:@helpers, :@controller, :@request, :@content
|
138
176
|
]
|
139
177
|
instance_variables.reject { |k| blacklist.include?(k) }.each do |k|
|
140
|
-
new_state[k] = instance_variable_get(k)
|
178
|
+
new_state[k] = instance_variable_get(k) unless omitted_from_state.include?(k)
|
141
179
|
end
|
142
180
|
ViewComponentReflex::Engine.state_adapter.store_state(request, @key, new_state)
|
181
|
+
ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", new_state)
|
143
182
|
else
|
183
|
+
initial_state = ViewComponentReflex::Engine.state_adapter.state(request, "#{@key}_initial")
|
144
184
|
ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v|
|
145
|
-
|
185
|
+
unless permit_parameter?(initial_state[k], instance_variable_get(k))
|
186
|
+
instance_variable_set(k, v)
|
187
|
+
end
|
146
188
|
end
|
147
189
|
end
|
148
190
|
@key
|
149
191
|
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def merge_data_attributes(options, attributes)
|
196
|
+
data = options[:data]
|
197
|
+
if data.nil?
|
198
|
+
options[:data] = attributes
|
199
|
+
else
|
200
|
+
options[:data].merge! attributes
|
201
|
+
end
|
202
|
+
end
|
150
203
|
end
|
151
204
|
end
|
@@ -5,12 +5,12 @@ module ViewComponentReflex
|
|
5
5
|
request.session[key] ||= {}
|
6
6
|
end
|
7
7
|
|
8
|
-
def self.set_state(
|
8
|
+
def self.set_state(request, controller, key, new_state)
|
9
9
|
new_state.each do |k, v|
|
10
|
-
state(
|
10
|
+
state(request, key)[k] = v
|
11
11
|
end
|
12
|
-
store =
|
13
|
-
store.commit_session
|
12
|
+
store = request.session.instance_variable_get("@by")
|
13
|
+
store.commit_session request, controller.response
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.store_state(request, key, new_state = {})
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component_reflex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua LeBlanc
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|