view_component_reflex 2.3.14 → 2.5.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.
@@ -1,207 +1,214 @@
1
- module ViewComponentReflex
2
- class Component < ViewComponent::Base
3
- class << self
4
- def init_stimulus_reflex
5
- factory = ViewComponentReflex::ReflexFactory.new(self)
6
- @stimulus_reflex ||= factory.reflex
7
- wire_up_callbacks if factory.new?
8
- end
9
-
10
- def reflex_base_class(new_base_class = nil)
11
- if new_base_class.nil?
12
- @reflex_base_class ||= ViewComponentReflex::Reflex
13
- else
14
- if new_base_class <= ViewComponentReflex::Reflex
15
- @reflex_base_class = new_base_class
16
- else
17
- raise StandardError.new("The reflex base class must inherit from ViewComponentReflex::Reflex")
18
- end
19
- end
20
- end
21
-
22
- def queue_callback(key, args, blk)
23
- callbacks(key).push({
24
- args: args,
25
- blk: blk
26
- })
27
- end
28
-
29
- def callbacks(key)
30
- @callbacks ||= {}
31
- @callbacks[key] ||= []
32
- end
33
-
34
- def register_callbacks(key)
35
- callbacks(key).each do |cb|
36
- @stimulus_reflex.send("#{key}_reflex", *cb[:args], &cb[:blk])
37
- end
38
- end
39
-
40
- def before_reflex(*args, &blk)
41
- queue_callback(:before, args, blk)
42
- end
43
-
44
- def after_reflex(*args, &blk)
45
- queue_callback(:after, args, blk)
46
- end
47
-
48
- def around_reflex(*args, &blk)
49
- queue_callback(:around, args, blk)
50
- end
51
-
52
- def wire_up_callbacks
53
- register_callbacks(:before)
54
- register_callbacks(:after)
55
- register_callbacks(:around)
56
- end
57
- end
58
-
59
- def self.stimulus_controller
60
- name.chomp("Component").underscore.dasherize
61
- end
62
-
63
- def stimulus_reflex?
64
- helpers.controller.instance_variable_get(:@stimulus_reflex)
65
- end
66
-
67
- def component_controller(opts_or_tag = :div, opts = {}, &blk)
68
- init_key
69
-
70
- tag = :div
71
- options = if opts_or_tag.is_a? Hash
72
- opts_or_tag
73
- else
74
- tag = opts_or_tag
75
- opts
76
- end
77
-
78
- options[:data] = {
79
- controller: self.class.stimulus_controller,
80
- key: key,
81
- **(options[:data] || {})
82
- }
83
- content_tag tag, capture(&blk), options
84
- end
85
-
86
- def can_render_to_string?
87
- omitted_from_state.empty?
88
- end
89
-
90
- # key is required if you're using state
91
- # We can't initialize the session state in the initial method
92
- # because it doesn't have a view_context yet
93
- # This is the next best place to do it
94
- def init_key
95
- # we want the erb file that renders the component. `caller` gives the file name,
96
- # and line number, which should be unique. We hash it to make it a nice number
97
- erb_file = caller.select { |p| p.match? /.\.html\.(haml|erb|slim)/ }[1]
98
- key = if erb_file
99
- Digest::SHA2.hexdigest(erb_file.split(":in")[0])
100
- else
101
- ""
102
- end
103
- key += collection_key.to_s if collection_key
104
- @key = key
105
- end
106
-
107
- # Helper to use to create the proper reflex data attributes for an element
108
- def reflex_data_attributes(reflex)
109
- action, method = reflex.to_s.split("->")
110
- if method.nil?
111
- method = action
112
- action = "click"
113
- end
114
-
115
- {
116
- reflex: "#{action}->#{self.class.name}##{method}",
117
- key: key
118
- }
119
- end
120
-
121
- def reflex_tag(reflex, name, content_or_options_with_block = {}, options = {}, escape = true, &block)
122
- if content_or_options_with_block.is_a?(Hash)
123
- merge_data_attributes(content_or_options_with_block, reflex_data_attributes(reflex))
124
- else
125
- merge_data_attributes(options, reflex_data_attributes(reflex))
126
- end
127
- content_tag(name, content_or_options_with_block, options, escape, &block)
128
- end
129
-
130
- def collection_key
131
- nil
132
- end
133
-
134
- def permit_parameter?(initial_param, new_param)
135
- initial_param != new_param
136
- end
137
-
138
- def omitted_from_state
139
- []
140
- end
141
-
142
- # def receive_params(old_state, params)
143
- # # no op
144
- # end
145
-
146
- def key
147
- # initialize session state
148
- if !stimulus_reflex? || ViewComponentReflex::Engine.state_adapter.state(request, @key).empty?
149
-
150
- new_state = create_safe_state
151
-
152
- ViewComponentReflex::Engine.state_adapter.store_state(request, @key, new_state)
153
- ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", new_state)
154
- elsif !@initialized_state
155
- initial_state = ViewComponentReflex::Engine.state_adapter.state(request, "#{@key}_initial")
156
-
157
- # incoming_params = safe_instance_variables.each_with_object({}) { |var, obj| obj[var] = instance_variable_get(var) }
158
- # receive_params(ViewComponentReflex::Engine.state_adapter.state(request, @key), incoming_params)
159
-
160
- ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v|
161
- instance_value = instance_variable_get(k)
162
- if permit_parameter?(initial_state[k], instance_value)
163
- ViewComponentReflex::Engine.state_adapter.set_state(request, controller, "#{@key}_initial", {k => instance_value})
164
- ViewComponentReflex::Engine.state_adapter.set_state(request, controller, @key, {k => instance_value})
165
- else
166
- instance_variable_set(k, v)
167
- end
168
- end
169
- @initialized_state = true
170
- end
171
- @key
172
- end
173
-
174
- def safe_instance_variables
175
- instance_variables - unsafe_instance_variables
176
- end
177
-
178
- private
179
-
180
- def unsafe_instance_variables
181
- [
182
- :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
183
- :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
184
- :@helpers, :@controller, :@request, :@tag_builder, :@initialized_state
185
- ]
186
- end
187
-
188
- def create_safe_state
189
- new_state = {}
190
-
191
- # this will almost certainly break
192
- safe_instance_variables.each do |k|
193
- new_state[k] = instance_variable_get(k) unless omitted_from_state.include?(k)
194
- end
195
- new_state
196
- end
197
-
198
- def merge_data_attributes(options, attributes)
199
- data = options[:data]
200
- if data.nil?
201
- options[:data] = attributes
202
- else
203
- options[:data].merge! attributes
204
- end
205
- end
206
- end
207
- end
1
+ module ViewComponentReflex
2
+ class Component < ViewComponent::Base
3
+ class << self
4
+ def init_stimulus_reflex
5
+ factory = ViewComponentReflex::ReflexFactory.new(self)
6
+ @stimulus_reflex ||= factory.reflex
7
+ wire_up_callbacks if factory.new?
8
+ end
9
+
10
+ def reflex_base_class(new_base_class = nil)
11
+ if new_base_class.nil?
12
+ @reflex_base_class ||= ViewComponentReflex::Reflex
13
+ else
14
+ if new_base_class <= ViewComponentReflex::Reflex
15
+ @reflex_base_class = new_base_class
16
+ else
17
+ raise StandardError.new("The reflex base class must inherit from ViewComponentReflex::Reflex")
18
+ end
19
+ end
20
+ end
21
+
22
+ def queue_callback(key, args, blk)
23
+ callbacks(key).push({
24
+ args: args,
25
+ blk: blk
26
+ })
27
+ end
28
+
29
+ def callbacks(key)
30
+ @callbacks ||= {}
31
+ @callbacks[key] ||= []
32
+ end
33
+
34
+ def register_callbacks(key)
35
+ callbacks(key).each do |cb|
36
+ @stimulus_reflex.send("#{key}_reflex", *cb[:args], &cb[:blk])
37
+ end
38
+ end
39
+
40
+ def before_reflex(*args, &blk)
41
+ queue_callback(:before, args, blk)
42
+ end
43
+
44
+ def after_reflex(*args, &blk)
45
+ queue_callback(:after, args, blk)
46
+ end
47
+
48
+ def around_reflex(*args, &blk)
49
+ queue_callback(:around, args, blk)
50
+ end
51
+
52
+ def wire_up_callbacks
53
+ register_callbacks(:before)
54
+ register_callbacks(:after)
55
+ register_callbacks(:around)
56
+ end
57
+ end
58
+
59
+ def self.stimulus_controller
60
+ name.chomp("Component").underscore.dasherize
61
+ end
62
+
63
+ def stimulus_reflex?
64
+ helpers.controller.instance_variable_get(:@stimulus_reflex)
65
+ end
66
+
67
+ def component_controller(opts_or_tag = :div, opts = {}, &blk)
68
+ init_key
69
+
70
+ tag = :div
71
+ options = if opts_or_tag.is_a? Hash
72
+ opts_or_tag
73
+ else
74
+ tag = opts_or_tag
75
+ opts
76
+ end
77
+
78
+ options[:data] = {
79
+ controller: self.class.stimulus_controller,
80
+ key: key,
81
+ **(options[:data] || {})
82
+ }
83
+ content_tag tag, capture(&blk), options
84
+ end
85
+
86
+ def can_render_to_string?
87
+ omitted_from_state.empty?
88
+ end
89
+
90
+ # key is required if you're using state
91
+ # We can't initialize the session state in the initial method
92
+ # because it doesn't have a view_context yet
93
+ # This is the next best place to do it
94
+ def init_key
95
+ # we want the erb file that renders the component. `caller` gives the file name,
96
+ # and line number, which should be unique. We hash it to make it a nice number
97
+ erb_file = caller.select { |p| p.match? /.\.html\.(haml|erb|slim)/ }[1]
98
+ key = if erb_file
99
+ Digest::SHA2.hexdigest(erb_file.split(":in")[0])
100
+ else
101
+ ""
102
+ end
103
+ key += collection_key.to_s if collection_key
104
+ @key = key
105
+ end
106
+
107
+ # Helper to use to create the proper reflex data attributes for an element
108
+ def reflex_data_attributes(reflex)
109
+ action, method = reflex.to_s.split("->")
110
+ if method.nil?
111
+ method = action
112
+ action = "click"
113
+ end
114
+
115
+ {
116
+ reflex: "#{action}->#{self.class.name}##{method}",
117
+ key: key
118
+ }
119
+ end
120
+
121
+ def reflex_tag(reflex, name, content_or_options_with_block = {}, options = {}, escape = true, &block)
122
+ if content_or_options_with_block.is_a?(Hash)
123
+ merge_data_attributes(content_or_options_with_block, reflex_data_attributes(reflex))
124
+ else
125
+ merge_data_attributes(options, reflex_data_attributes(reflex))
126
+ end
127
+ content_tag(name, content_or_options_with_block, options, escape, &block)
128
+ end
129
+
130
+ def collection_key
131
+ nil
132
+ end
133
+
134
+ def permit_parameter?(initial_param, new_param)
135
+ initial_param != new_param
136
+ end
137
+
138
+ def omitted_from_state
139
+ []
140
+ end
141
+
142
+ def after_state_initialized(parameters_changed)
143
+ # called after state component has been hydrated
144
+ end
145
+
146
+ # def receive_params(old_state, params)
147
+ # # no op
148
+ # end
149
+
150
+ def key
151
+ # initialize session state
152
+ if !stimulus_reflex? || ViewComponentReflex::Engine.state_adapter.state(request, @key).empty?
153
+
154
+ new_state = create_safe_state
155
+
156
+ ViewComponentReflex::Engine.state_adapter.store_state(request, @key, new_state)
157
+ ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", new_state)
158
+ elsif !@initialized_state
159
+ initial_state = ViewComponentReflex::Engine.state_adapter.state(request, "#{@key}_initial")
160
+
161
+ # incoming_params = safe_instance_variables.each_with_object({}) { |var, obj| obj[var] = instance_variable_get(var) }
162
+ # receive_params(ViewComponentReflex::Engine.state_adapter.state(request, @key), incoming_params)
163
+
164
+ parameters_changed = []
165
+ ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v|
166
+ instance_value = instance_variable_get(k)
167
+ if permit_parameter?(initial_state[k], instance_value)
168
+ parameters_changed << k
169
+ ViewComponentReflex::Engine.state_adapter.set_state(request, controller, "#{@key}_initial", {k => instance_value})
170
+ ViewComponentReflex::Engine.state_adapter.set_state(request, controller, @key, {k => instance_value})
171
+ else
172
+ instance_variable_set(k, v)
173
+ end
174
+ end
175
+ after_state_initialized(parameters_changed)
176
+ @initialized_state = true
177
+ end
178
+ @key
179
+ end
180
+
181
+ def safe_instance_variables
182
+ instance_variables - unsafe_instance_variables
183
+ end
184
+
185
+ private
186
+
187
+ def unsafe_instance_variables
188
+ [
189
+ :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
190
+ :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
191
+ :@helpers, :@controller, :@request, :@tag_builder, :@initialized_state
192
+ ]
193
+ end
194
+
195
+ def create_safe_state
196
+ new_state = {}
197
+
198
+ # this will almost certainly break
199
+ safe_instance_variables.each do |k|
200
+ new_state[k] = instance_variable_get(k) unless omitted_from_state.include?(k)
201
+ end
202
+ new_state
203
+ end
204
+
205
+ def merge_data_attributes(options, attributes)
206
+ data = options[:data]
207
+ if data.nil?
208
+ options[:data] = attributes
209
+ else
210
+ options[:data].merge! attributes
211
+ end
212
+ end
213
+ end
214
+ end
@@ -1,10 +1,10 @@
1
- require "stimulus_reflex"
2
- require 'view_component_reflex/reflex_factory'
3
- require "view_component_reflex/state_adapter/session"
4
- require "view_component_reflex/state_adapter/memory"
5
- require "view_component_reflex/reflex"
6
- require "view_component_reflex/engine"
7
-
8
- module ViewComponentReflex
9
- # Your code goes here...
10
- end
1
+ require "stimulus_reflex"
2
+ require 'view_component_reflex/reflex_factory'
3
+ require "view_component_reflex/state_adapter/session"
4
+ require "view_component_reflex/state_adapter/memory"
5
+ require "view_component_reflex/reflex"
6
+ require "view_component_reflex/engine"
7
+
8
+ module ViewComponentReflex
9
+ # Your code goes here...
10
+ end
@@ -1,36 +1,36 @@
1
- module ViewComponentReflex
2
- class Engine < ::Rails::Engine
3
- class << self
4
- mattr_accessor :state_adapter
5
-
6
- self.state_adapter = StateAdapter::Session
7
- end
8
-
9
- config.to_prepare do
10
- StimulusReflex::Channel.class_eval do
11
- unless instance_methods.include?(:receive_original)
12
- alias_method :receive_original, :receive
13
- def receive(data)
14
- target = data["target"].to_s
15
- reflex_name, _ = target.split("#")
16
- reflex_name = reflex_name.camelize
17
- component_name = reflex_name.end_with?("Reflex") ? reflex_name[0...-6] : reflex_name
18
- if component_name.end_with?("Component")
19
- begin
20
- component_name.constantize.init_stimulus_reflex
21
- rescue
22
- p "Tried to initialize view_component_reflex on #{component_name}, but it's not a view_component_reflex"
23
- end
24
-
25
- end
26
- receive_original(data)
27
- end
28
- end
29
- end
30
- end
31
-
32
- def self.configure
33
- yield self if block_given?
34
- end
35
- end
36
- end
1
+ module ViewComponentReflex
2
+ class Engine < ::Rails::Engine
3
+ class << self
4
+ mattr_accessor :state_adapter
5
+
6
+ self.state_adapter = StateAdapter::Session
7
+ end
8
+
9
+ config.to_prepare do
10
+ StimulusReflex::Channel.class_eval do
11
+ unless instance_methods.include?(:receive_original)
12
+ alias_method :receive_original, :receive
13
+ def receive(data)
14
+ target = data["target"].to_s
15
+ reflex_name, _ = target.split("#")
16
+ reflex_name = reflex_name.camelize
17
+ component_name = reflex_name.end_with?("Reflex") ? reflex_name[0...-6] : reflex_name
18
+ if component_name.end_with?("Component")
19
+ begin
20
+ component_name.constantize.init_stimulus_reflex
21
+ rescue
22
+ p "Tried to initialize view_component_reflex on #{component_name}, but it's not a view_component_reflex"
23
+ end
24
+
25
+ end
26
+ receive_original(data)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.configure
33
+ yield self if block_given?
34
+ end
35
+ end
36
+ end