view_component_reflex 2.3.14 → 2.5.0

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: 3ad19988c11a33e1cc66d09f768f2d74218db3c04e16c8625c4624ce5a223c9f
4
- data.tar.gz: b994c4d403c7d24be9152f543f9b5e257f11405e8fd6d671921e28f3aaa61d2f
3
+ metadata.gz: 52a985ff5f053dfb2efd1d2d14fd95e288df76bddc2a217cc8f77a2c03d411d8
4
+ data.tar.gz: 3708aadf74378afd1f1e86f5fdbf7628e246a5f573714684754d08c77685b987
5
5
  SHA512:
6
- metadata.gz: b4bd5cf4fd21a5dad48247bc6bdc4cd3d149ae67851298a4ed43d747f80ebeb3447589711e16648b8d00fa823e43d18a378caea4d658d35673ac84bebb6a6050
7
- data.tar.gz: 1d5c9965e17767d5c44f6f6b8034569232179008780a0a3f870bf848c81572c70ec747f6acc50579da86a9c16f80eba2d22cfb01037462626cc93d8f04fc5770
6
+ metadata.gz: 42ea4a59d6705a4a34fa90a274e31785ab6cdc70184239063921de8050168021e54b43fac3a1504ce0f749171b776800cd0eaaffbb1d0f629e71e9c1397f7508
7
+ data.tar.gz: 1699da2877f8eddc8abb9f7cf8b48feab0283aaad2f031090f4c04677e2cbc0997eef6521c244d50b0abaa3f0746baac9d10d86ecc60021c91c7bd191342aa00
data/README.md CHANGED
@@ -1,332 +1,345 @@
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 (since version `2.3.2`), view_component_reflex stores component state in session. You can optionally set the state adapter
249
- to use the memory by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Memory`.
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 a file extension of either `.html.erb`, `.html.haml` or `.html.slim`.
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
-
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 = :bar
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
+ ### after_state_initialized(parameters_changed)
197
+
198
+ This is called after the state has been inserted in the component. You can use this to run conditional functions after
199
+ some parameter has superseeded whatever's in state
200
+
201
+ ```
202
+ def after_state_initialized(parameters_changed)
203
+ if parameters_changed.include?(:@filter)
204
+ calculate_visible_rows
205
+ end
206
+ end
207
+ ```
208
+
209
+ ## Custom reflex base class
210
+ 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.
211
+ The parent class must inherit ViewComponentReflex::Reflex, and will throw an error if it does not.
212
+
213
+ ```ruby
214
+ class ApplicationReflex < ViewComponentReflex::Reflex
215
+
216
+ end
217
+
218
+
219
+ class MyComponent < ViewComponentReflex::Component
220
+ reflex_base_class ApplicationReflex
221
+ end
222
+ ```
223
+
224
+ ## Common patterns
225
+ A lot of the time, you only need to update specific components when changing instance variables. For example, changing `@loading` might only need
226
+ 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
227
+
228
+ ```ruby
229
+ def initialize
230
+ @loading = false
231
+ end
232
+
233
+ def loading=(new_value)
234
+ @loading = new_value
235
+ refresh! '#loader'
236
+ end
237
+
238
+ def do_expensive_action
239
+ prevent_refresh!
240
+
241
+ self.loading = true
242
+ execute_it
243
+ self.loading = false
244
+ end
245
+ ```
246
+
247
+ ```erb
248
+ <%= component_controller do %>
249
+ <div id="loader">
250
+ <% if @loading %>
251
+ <p>Loading...</p>
252
+ <% end %>
253
+ </div>
254
+
255
+ <button type="button" data-reflex="click->MyComponent#do_expensive_action" data-key="<%= key %>">Click me!</button>
256
+ <% end
257
+ ```
258
+
259
+ ## State
260
+
261
+ By default (since version `2.3.2`), view_component_reflex stores component state in session. You can optionally set the state adapter
262
+ to use the memory by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Memory`.
263
+
264
+ ## Custom State Adapters
265
+
266
+ ViewComponentReflex uses session for its state by default. To change this, add
267
+ an initializer to `config/initializers/view_component_reflex.rb`.
268
+
269
+ ```ruby
270
+ ViewComponentReflex::Engine.configure do |config|
271
+ config.state_adapter = YourAdapter
272
+ end
273
+ ```
274
+
275
+ `YourAdapter` should implement
276
+
277
+ ```ruby
278
+ class YourAdapter
279
+ ##
280
+ # request - a rails request object
281
+ # key - a unique string that identifies the component instance
282
+ def self.state(request, key)
283
+ # Return state for a given key
284
+ end
285
+
286
+ ##
287
+ # set_state is used to modify the state.
288
+ #
289
+ # request - a rails request object
290
+ # controller - the current controller
291
+ # key - a unique string that identifies the component
292
+ # new_state - the new state to set
293
+ def self.set_state(request, controller, key, new_state)
294
+ # update the state
295
+ end
296
+
297
+
298
+ ##
299
+ # store_state is used to replace the state entirely. It only accepts
300
+ # a request object, rather than a reflex because it's called from the component's
301
+ # side with the component's instance variables.
302
+ #
303
+ # request - a rails request object
304
+ # key - a unique string that identifies the component instance
305
+ # new_state - a hash containing the component state
306
+ def self.store_state(request, key, new_state = {})
307
+ # replace the state
308
+ end
309
+ end
310
+ ```
311
+
312
+
313
+ ## Installation
314
+ Add this line to your application's Gemfile:
315
+
316
+ ```ruby
317
+ gem 'view_component_reflex'
318
+ ```
319
+
320
+ And then execute:
321
+ ```bash
322
+ $ bundle
323
+ ```
324
+
325
+ Or install it yourself as:
326
+ ```bash
327
+ $ gem install view_component_reflex
328
+ ```
329
+
330
+ # Common problems
331
+
332
+ ## Uninitialized constants \<component\>Reflex
333
+ A component needs to be wrapped in `<%= component_controller do %>` in order to properly initialize, otherwise the Reflex class won't get created.
334
+
335
+ ## License
336
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
337
+
338
+ ## Caveats
339
+
340
+ State uses session to maintain state as of right now. It also assumes your component view is written with a file extension of either `.html.erb`, `.html.haml` or `.html.slim`.
341
+
342
+ ## Support me
343
+
344
+ <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>
345
+