view_component_reflex 2.2.2 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 884879671f11072c36d18205c1bb452c8b0ff9e2e50c6e57b70e1bfb602f1b87
4
- data.tar.gz: dd3ac067aaa30fa45785b6abd1b609b7a09fadf9c11ca272d829271675750fb9
3
+ metadata.gz: c450de20f09931a19ed083a213190bc953da24a68f7bbdd206c99d72294f0d87
4
+ data.tar.gz: f15753fabf85eca4311812d34f5a2577f9eac7db56130411b21f138e4ef9bbc7
5
5
  SHA512:
6
- metadata.gz: 6740d002a2fae276c4ff60433485461cb43a90ac4a6fdb2431fa757b326fc2f882f86b662d7b23ba735f65ca89d87a047ebdb5f7874f3db08826fe3cb880d39c
7
- data.tar.gz: fc558e67327441ea6c1f813f7d005e57ce6b127221e4ab34835c78e1a7d6a0b2fd25e406de405ae615a6d204d691e8a94d669eb28df7bb64192e770bd88f06df
6
+ metadata.gz: 02a5e16f23f9abed074098ae02b7b269d8104a05ffdd6c159ca44212bc319dde77a68744b0e5e47b7ae31f08bd4e8ab43f6bcbcbc9a68e50ac331de48dd59b58
7
+ data.tar.gz: 993f7ded6b8807f552981b87a99f3560d2e01ee482ea61538d9b1fa033907915c1900a3f080a56281994b35bd8afef0778d4a1df22a40cd2c3acefd02edc1d17
data/README.md CHANGED
@@ -1,317 +1,332 @@
1
- # ViewComponentReflex
2
-
3
- ViewComponentReflex allows you to write reflexes right in your view component code.
4
-
5
- It builds upon [stimulus_reflex](https://github.com/hopsoft/stimulus_reflex) and [view_component](https://github.com/github/view_component)
6
-
7
- ## Usage
8
-
9
- You can add reflexes to your component by adding inheriting from `ViewComponentReflex::Component`.
10
-
11
- This will act as if you created a reflex with the method `my_cool_stuff`. To call this reflex, add `data-reflex="click->MyComponentReflex#my_cool_reflex"`, just like you're
12
- using stimulus reflex.
13
-
14
- ViewComponentReflex will maintain your component's instance variables between renders. You need to include `data-key=<%= key %>` on your root element, as well
15
- as any element that stimulates a reflex. ViewComponent is inherently state-less, so the key is used to reconcile state to its respective component.
16
-
17
- ### Example
18
- ```ruby
19
- # counter_component.rb
20
- class CounterComponent < ViewComponentReflex::Component
21
- def initialize
22
- @count = 0
23
- end
24
-
25
- def increment
26
- @count += 1
27
- end
28
- end
29
- ```
30
-
31
- ```erb
32
- # counter_component.html.erb
33
- <%= component_controller do %>
34
- <p><%= @count %></p>
35
- <%= reflex_tag :increment, :button, "Click" %>
36
- <% end %>
37
- ```
38
-
39
- ## Collections
40
-
41
- In order to reconcile state to components in collections, you can specify a `collection_key` method that returns some
42
- value unique to that component.
43
-
44
- ```ruby
45
- class TodoComponent < ViewComponentReflex::Component
46
- def initialize(todo:)
47
- @todo = todo
48
- end
49
-
50
- def collection_key
51
- @todo.id
52
- end
53
- end
54
- #
55
- <%= render(TodoComponent.with_collection(Todo.all)) %>
56
- ```
57
-
58
- ## API
59
-
60
- ### permit_parameter?(initial_param, new_params)
61
- If a new parameter is passed to the component during rendering, it is used instead of what's in state.
62
- If you're storing instances in state, you can use this to properly compare them.
63
-
64
- ```ruby
65
- def permit_parameter?(initial_param, new_param)
66
- if new_param.instance_of? MyModel
67
- new_param.id == @my_model.id
68
- else
69
- super
70
- end
71
- end
72
- ```
73
-
74
- ### omitted_from_state
75
- Return an array of instance variables you want to omit from state. Only really useful if you're using the session state
76
- adapter, and you have an instance variable that can't be serialized.
77
-
78
- ```ruby
79
- def omitted_from_state
80
- [:@form]
81
- end
82
- ```
83
-
84
- ### reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
85
- This shares the same definition as `content_tag`, except it accepts a reflex as the first parameter.
86
-
87
- ```erb
88
- <%= reflex_tag :increment, :button, "Click me!" %>
89
- ```
90
-
91
- Would add a click handler to the `increment` method on your component.
92
-
93
- To use a non-click event, specific that with `->` notation
94
-
95
- ```erb
96
- <%= reflex_tag "mouseenter->increment", :button, "Click me!" %>
97
- ```
98
-
99
- ### reflex_data_attributes(reflex)
100
-
101
- This helper will give you the data attributes used in the reflex_tag above if you want to build your own elements.
102
-
103
- Build your own tag:
104
-
105
- ```erb
106
- <%= link_to (image_tag photo.image.url(:medium)), data: reflex_data_attributes(:increment) %>
107
- ```
108
-
109
- Render a ViewComponent
110
-
111
- ```erb
112
- <%= render ButtonComponent.new(data: reflex_data_attributes("mouseenter->increment")) %>
113
- ```
114
-
115
- Make sure that you assign the reflex_data_attributes to the correct element in your component.
116
-
117
- ### collection_key
118
- 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.
119
- This is used to reconcile state in the background.
120
-
121
- ```ruby
122
- def initialize
123
- @my_model = MyModel.new
124
- end
125
-
126
- def collection_key
127
- @my_model.id
128
- end
129
- ```
130
-
131
- ### stimulate(target, data)
132
- Stimulate another reflex from within your component. This typically requires the key of the component you're stimulating
133
- which can be passed in via parameters.
134
-
135
- ```ruby
136
- def initialize(parent_key)
137
- @parent_key = parent_key
138
- end
139
-
140
- def stimulate_other
141
- stimulate("OtherComponent#method", { key: @parent_key })
142
- end
143
- ```
144
-
145
- ### refresh!(selectors)
146
- Refresh a specific element on the page. Using this will implicitly run `prevent_render!`.
147
- If you want to render a specific element, as well as the component, a common pattern would be to pass `selector` as one of the parameters
148
-
149
- ```
150
- def my_method
151
- refresh! '#my-special-element', selector
152
- end
153
- ```
154
-
155
- ### selector
156
- Returns the unique selector for this component. Useful to pass to `refresh!` when refreshing custom elements.
157
-
158
- ### prevent_refresh!
159
- By default, VCR will re-render your component after it executes your method. `prevent_refresh!` prevents this from happening.
160
-
161
- ```ruby
162
- def my_method
163
- prevent_refresh!
164
- @foo = Lbar
165
- end # the rendered page will not reflect this change
166
- ```
167
-
168
- ### refresh_all!
169
- Refresh the entire body of the page
170
-
171
- ```ruby
172
- def do_some_global_action
173
- prevent_refresh!
174
- session[:model] = MyModel.new
175
- refresh_all!
176
- end
177
- ```
178
-
179
- ### key
180
- 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
181
-
182
- ```erb
183
- <button type="button" data-reflex="click->MyComponent#do_something" data-key="<%= key %>">Click me!</button>
184
- ```
185
-
186
- ### component_controller(options = {}, &blk)
187
- This is a view helper to properly connect VCR to the component. It outputs `<div data-controller="my-controller" key=<%= key %></div>`
188
- You *must* wrap your component in this for everything to work properly.
189
-
190
- ```erb
191
- <%= component_controller do %>
192
- <p><%= @count %></p
193
- <% end %>
194
- ```
195
-
196
- ## Common patterns
197
- A lot of the time, you only need to update specific components when changing instance variables. For example, changing `@loading` might only need
198
- 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
199
-
200
- ```ruby
201
- def initialize
202
- @loading = false
203
- end
204
-
205
- def loading=(new_value)
206
- @loading = new_value
207
- refresh! '#loader'
208
- end
209
-
210
- def do_expensive_action
211
- prevent_refresh!
212
-
213
- self.loading = true
214
- execute_it
215
- self.loading = false
216
- end
217
- ```
218
-
219
- ```erb
220
- <%= component_controller do %>
221
- <div id="loader">
222
- <% if @loading %>
223
- <p>Loading...</p>
224
- <% end %>
225
- </div>
226
-
227
- <button type="button" data-reflex="click->MyComponent#do_expensive_action" data-key="<%= key %>">Click me!</button>
228
- <% end
229
- ```
230
-
231
- ## State
232
-
233
- By default, view_component_reflex stores component state in memory. You can optionally set the state adapter
234
- to use the session by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Session`
235
-
236
- ## Custom State Adapters
237
-
238
- ViewComponentReflex uses session for its state by default. To change this, add
239
- an initializer to `config/initializers/view_component_reflex.rb`.
240
-
241
- ```ruby
242
- ViewComponentReflex::Engine.configure do |config|
243
- config.state_adapter = YourAdapter
244
- end
245
- ```
246
-
247
- `YourAdapter` should implement
248
-
249
- ```ruby
250
- class YourAdapter
251
- ##
252
- # request - a rails request object
253
- # key - a unique string that identifies the component instance
254
- def self.state(request, key)
255
- # Return state for a given key
256
- end
257
-
258
- ##
259
- # set_state is used to modify the state.
260
- #
261
- # request - a rails request object
262
- # controller - the current controller
263
- # key - a unique string that identifies the component
264
- # new_state - the new state to set
265
- def self.set_state(request, controller, key, new_state)
266
- # update the state
267
- end
268
-
269
-
270
- ##
271
- # store_state is used to replace the state entirely. It only accepts
272
- # a request object, rather than a reflex because it's called from the component's
273
- # side with the component's instance variables.
274
- #
275
- # request - a rails request object
276
- # key - a unique string that identifies the component instance
277
- # new_state - a hash containing the component state
278
- def self.store_state(request, key, new_state = {})
279
- # replace the state
280
- end
281
- end
282
- ```
283
-
284
-
285
- ## Installation
286
- Add this line to your application's Gemfile:
287
-
288
- ```ruby
289
- gem 'view_component_reflex'
290
- ```
291
-
292
- And then execute:
293
- ```bash
294
- $ bundle
295
- ```
296
-
297
- Or install it yourself as:
298
- ```bash
299
- $ gem install view_component_reflex
300
- ```
301
-
302
- # Common problems
303
-
304
- ## Uninitialized constants \<component\>Reflex
305
- A component needs to be wrapped in `<%= component_controller do %>` in order to properly initialize, otherwise the Reflex class won't get created.
306
-
307
- ## License
308
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
309
-
310
- ## Caveats
311
-
312
- State uses session to maintain state as of right now. It also assumes your component view is written with the file extension `.html.erb`
313
-
314
- ## Support me
315
-
316
- <a href="https://www.buymeacoffee.com/jleblanc" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="40" ></a>
317
-
1
+ # ViewComponentReflex
2
+
3
+ ViewComponentReflex allows you to write reflexes right in your view component code.
4
+
5
+ It builds upon [stimulus_reflex](https://github.com/hopsoft/stimulus_reflex) and [view_component](https://github.com/github/view_component)
6
+
7
+ ## Usage
8
+
9
+ You can add reflexes to your component by adding inheriting from `ViewComponentReflex::Component`.
10
+
11
+ This will act as if you created a reflex with the method `my_cool_stuff`. To call this reflex, add `data-reflex="click->MyComponentReflex#my_cool_reflex"`, just like you're
12
+ using stimulus reflex.
13
+
14
+ ViewComponentReflex will maintain your component's instance variables between renders. You need to include `data-key=<%= key %>` on your root element, as well
15
+ as any element that stimulates a reflex. ViewComponent is inherently state-less, so the key is used to reconcile state to its respective component.
16
+
17
+ ### Example
18
+ ```ruby
19
+ # counter_component.rb
20
+ class CounterComponent < ViewComponentReflex::Component
21
+ def initialize
22
+ @count = 0
23
+ end
24
+
25
+ def increment
26
+ @count += 1
27
+ end
28
+ end
29
+ ```
30
+
31
+ ```erb
32
+ # counter_component.html.erb
33
+ <%= component_controller do %>
34
+ <p><%= @count %></p>
35
+ <%= reflex_tag :increment, :button, "Click" %>
36
+ <% end %>
37
+ ```
38
+
39
+ ## Collections
40
+
41
+ In order to reconcile state to components in collections, you can specify a `collection_key` method that returns some
42
+ value unique to that component.
43
+
44
+ ```ruby
45
+ class TodoComponent < ViewComponentReflex::Component
46
+ def initialize(todo:)
47
+ @todo = todo
48
+ end
49
+
50
+ def collection_key
51
+ @todo.id
52
+ end
53
+ end
54
+ #
55
+ <%= render(TodoComponent.with_collection(Todo.all)) %>
56
+ ```
57
+
58
+ ## API
59
+
60
+ ### permit_parameter?(initial_param, new_params)
61
+ If a new parameter is passed to the component during rendering, it is used instead of what's in state.
62
+ If you're storing instances in state, you can use this to properly compare them.
63
+
64
+ ```ruby
65
+ def permit_parameter?(initial_param, new_param)
66
+ if new_param.instance_of? MyModel
67
+ new_param.id == @my_model.id
68
+ else
69
+ super
70
+ end
71
+ end
72
+ ```
73
+
74
+ ### omitted_from_state
75
+ Return an array of instance variables you want to omit from state. Only really useful if you're using the session state
76
+ adapter, and you have an instance variable that can't be serialized.
77
+
78
+ ```ruby
79
+ def omitted_from_state
80
+ [:@form]
81
+ end
82
+ ```
83
+
84
+ ### reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
85
+ This shares the same definition as `content_tag`, except it accepts a reflex as the first parameter.
86
+
87
+ ```erb
88
+ <%= reflex_tag :increment, :button, "Click me!" %>
89
+ ```
90
+
91
+ Would add a click handler to the `increment` method on your component.
92
+
93
+ To use a non-click event, specific that with `->` notation
94
+
95
+ ```erb
96
+ <%= reflex_tag "mouseenter->increment", :button, "Click me!" %>
97
+ ```
98
+
99
+ ### reflex_data_attributes(reflex)
100
+
101
+ This helper will give you the data attributes used in the reflex_tag above if you want to build your own elements.
102
+
103
+ Build your own tag:
104
+
105
+ ```erb
106
+ <%= link_to (image_tag photo.image.url(:medium)), data: reflex_data_attributes(:increment) %>
107
+ ```
108
+
109
+ Render a ViewComponent
110
+
111
+ ```erb
112
+ <%= render ButtonComponent.new(data: reflex_data_attributes("mouseenter->increment")) %>
113
+ ```
114
+
115
+ Make sure that you assign the reflex_data_attributes to the correct element in your component.
116
+
117
+ ### collection_key
118
+ 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.
119
+ This is used to reconcile state in the background.
120
+
121
+ ```ruby
122
+ def initialize
123
+ @my_model = MyModel.new
124
+ end
125
+
126
+ def collection_key
127
+ @my_model.id
128
+ end
129
+ ```
130
+
131
+ ### stimulate(target, data)
132
+ Stimulate another reflex from within your component. This typically requires the key of the component you're stimulating
133
+ which can be passed in via parameters.
134
+
135
+ ```ruby
136
+ def initialize(parent_key)
137
+ @parent_key = parent_key
138
+ end
139
+
140
+ def stimulate_other
141
+ stimulate("OtherComponent#method", { key: @parent_key })
142
+ end
143
+ ```
144
+
145
+ ### refresh!(selectors)
146
+ Refresh a specific element on the page. Using this will implicitly run `prevent_render!`.
147
+ If you want to render a specific element, as well as the component, a common pattern would be to pass `selector` as one of the parameters
148
+
149
+ ```
150
+ def my_method
151
+ refresh! '#my-special-element', selector
152
+ end
153
+ ```
154
+
155
+ ### selector
156
+ Returns the unique selector for this component. Useful to pass to `refresh!` when refreshing custom elements.
157
+
158
+ ### prevent_refresh!
159
+ By default, VCR will re-render your component after it executes your method. `prevent_refresh!` prevents this from happening.
160
+
161
+ ```ruby
162
+ def my_method
163
+ prevent_refresh!
164
+ @foo = Lbar
165
+ end # the rendered page will not reflect this change
166
+ ```
167
+
168
+ ### refresh_all!
169
+ Refresh the entire body of the page
170
+
171
+ ```ruby
172
+ def do_some_global_action
173
+ prevent_refresh!
174
+ session[:model] = MyModel.new
175
+ refresh_all!
176
+ end
177
+ ```
178
+
179
+ ### key
180
+ 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
181
+
182
+ ```erb
183
+ <button type="button" data-reflex="click->MyComponent#do_something" data-key="<%= key %>">Click me!</button>
184
+ ```
185
+
186
+ ### component_controller(options = {}, &blk)
187
+ This is a view helper to properly connect VCR to the component. It outputs `<div data-controller="my-controller" key=<%= key %></div>`
188
+ You *must* wrap your component in this for everything to work properly.
189
+
190
+ ```erb
191
+ <%= component_controller do %>
192
+ <p><%= @count %></p
193
+ <% end %>
194
+ ```
195
+
196
+ ## Custom reflex base class
197
+ Reflexes typically inherit from a base ApplicationReflex. You can define the base class for a view_component_reflex by using the `reflex_base_class` method.
198
+ The parent class must inherit ViewComponentReflex::Reflex, and will throw an error if it does not.
199
+
200
+ ```ruby
201
+ class ApplicationReflex < ViewComponentReflex::Reflex
202
+
203
+ end
204
+
205
+
206
+ class MyComponent < ViewComponentReflex::Component
207
+ reflex_base_class ApplicationReflex
208
+ end
209
+ ```
210
+
211
+ ## Common patterns
212
+ A lot of the time, you only need to update specific components when changing instance variables. For example, changing `@loading` might only need
213
+ 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
214
+
215
+ ```ruby
216
+ def initialize
217
+ @loading = false
218
+ end
219
+
220
+ def loading=(new_value)
221
+ @loading = new_value
222
+ refresh! '#loader'
223
+ end
224
+
225
+ def do_expensive_action
226
+ prevent_refresh!
227
+
228
+ self.loading = true
229
+ execute_it
230
+ self.loading = false
231
+ end
232
+ ```
233
+
234
+ ```erb
235
+ <%= component_controller do %>
236
+ <div id="loader">
237
+ <% if @loading %>
238
+ <p>Loading...</p>
239
+ <% end %>
240
+ </div>
241
+
242
+ <button type="button" data-reflex="click->MyComponent#do_expensive_action" data-key="<%= key %>">Click me!</button>
243
+ <% end
244
+ ```
245
+
246
+ ## State
247
+
248
+ By default, view_component_reflex stores component state in memory. You can optionally set the state adapter
249
+ to use the session by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Session`
250
+
251
+ ## Custom State Adapters
252
+
253
+ ViewComponentReflex uses session for its state by default. To change this, add
254
+ an initializer to `config/initializers/view_component_reflex.rb`.
255
+
256
+ ```ruby
257
+ ViewComponentReflex::Engine.configure do |config|
258
+ config.state_adapter = YourAdapter
259
+ end
260
+ ```
261
+
262
+ `YourAdapter` should implement
263
+
264
+ ```ruby
265
+ class YourAdapter
266
+ ##
267
+ # request - a rails request object
268
+ # key - a unique string that identifies the component instance
269
+ def self.state(request, key)
270
+ # Return state for a given key
271
+ end
272
+
273
+ ##
274
+ # set_state is used to modify the state.
275
+ #
276
+ # request - a rails request object
277
+ # controller - the current controller
278
+ # key - a unique string that identifies the component
279
+ # new_state - the new state to set
280
+ def self.set_state(request, controller, key, new_state)
281
+ # update the state
282
+ end
283
+
284
+
285
+ ##
286
+ # store_state is used to replace the state entirely. It only accepts
287
+ # a request object, rather than a reflex because it's called from the component's
288
+ # side with the component's instance variables.
289
+ #
290
+ # request - a rails request object
291
+ # key - a unique string that identifies the component instance
292
+ # new_state - a hash containing the component state
293
+ def self.store_state(request, key, new_state = {})
294
+ # replace the state
295
+ end
296
+ end
297
+ ```
298
+
299
+
300
+ ## Installation
301
+ Add this line to your application's Gemfile:
302
+
303
+ ```ruby
304
+ gem 'view_component_reflex'
305
+ ```
306
+
307
+ And then execute:
308
+ ```bash
309
+ $ bundle
310
+ ```
311
+
312
+ Or install it yourself as:
313
+ ```bash
314
+ $ gem install view_component_reflex
315
+ ```
316
+
317
+ # Common problems
318
+
319
+ ## Uninitialized constants \<component\>Reflex
320
+ A component needs to be wrapped in `<%= component_controller do %>` in order to properly initialize, otherwise the Reflex class won't get created.
321
+
322
+ ## License
323
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
324
+
325
+ ## Caveats
326
+
327
+ State uses session to maintain state as of right now. It also assumes your component view is written with the file extension `.html.erb`
328
+
329
+ ## Support me
330
+
331
+ <a href="https://www.buymeacoffee.com/jleblanc" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="40" ></a>
332
+