Class: ViewComponentReflex::Component
- Inherits:
-
ViewComponent::Base
- Object
- ViewComponent::Base
- ViewComponentReflex::Component
- Defined in:
- app/components/view_component_reflex/component.rb
Class Method Summary collapse
Instance Method Summary collapse
- #collection_key ⇒ Object
- #component_controller(opts = {}, &blk) ⇒ Object
-
#init_key ⇒ Object
key is required if you're using state We can't initialize the session state in the initial method because it doesn't have a view_context yet This is the next best place to do it.
- #key ⇒ Object
- #permit_parameter?(initial_param, new_param) ⇒ Boolean
- #stimulus_reflex? ⇒ Boolean
Class Method Details
.init_stimulus_reflex ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'app/components/view_component_reflex/component.rb', line 4 def init_stimulus_reflex klass = self @stimulus_reflex ||= Object.const_set(name + "Reflex", Class.new(StimulusReflex::Reflex) { def refresh!(primary_selector = "[data-controller~=\"#{stimulus_controller}\"][data-key=\"#{element.dataset[:key]}\"]", *selectors) save_state @channel.send :render_page_and_broadcast_morph, self, [primary_selector, *selectors], { "dataset" => element.dataset.to_h, "args" => [], "attrs" => element.attributes.to_h, "selectors" => ["body"], "target" => "#{self.class.name}##{method_name}", "url" => request.url, "permanent_attribute_name" => "data-reflex-permanent" } end def refresh_all! refresh!("body") end # SR's delegate_call_to_reflex in channel.rb # uses method to gather the method parameters, but since we're abusing # method_missing here, that'll always fail def method(name) name.to_sym.to_proc end def respond_to_missing?(name, _ = false) !!name.to_proc end before_reflex do |a| a.send a.method_name throw :abort end def method_missing(name, *args) super unless respond_to_missing?(name) state.each do |k, v| component.instance_variable_set(k, v) end name.to_proc.call(component, *args) refresh! end define_method :component_class do @component_class ||= klass end private :component_class private def stimulus_controller component_class.stimulus_controller end def component return @component if @component @component = component_class.allocate reflex = self exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller] exposed_methods.each do |meth| @component.define_singleton_method(meth) do |*a| reflex.send(meth, *a) end end @component end def set_state(new_state = {}) ViewComponentReflex::Engine.state_adapter.set_state(request, controller, element.dataset[:key], new_state) end def state ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key]) end def save_state new_state = {} component.instance_variables.each do |k| new_state[k] = component.instance_variable_get(k) end set_state(new_state) end }) end |
.stimulus_controller ⇒ Object
93 94 95 |
# File 'app/components/view_component_reflex/component.rb', line 93 def self.stimulus_controller name.chomp("Component").underscore.dasherize end |
Instance Method Details
#collection_key ⇒ Object
124 125 126 |
# File 'app/components/view_component_reflex/component.rb', line 124 def collection_key nil end |
#component_controller(opts = {}, &blk) ⇒ Object
101 102 103 104 105 106 107 108 109 110 |
# File 'app/components/view_component_reflex/component.rb', line 101 def component_controller(opts = {}, &blk) self.class.init_stimulus_reflex init_key opts[:data] = { controller: self.class.stimulus_controller, key: key, **(opts[:data] || {}) } content_tag :div, capture(&blk), opts end |
#init_key ⇒ Object
key is required if you're using state We can't initialize the session state in the initial method because it doesn't have a view_context yet This is the next best place to do it
116 117 118 119 120 121 122 |
# File 'app/components/view_component_reflex/component.rb', line 116 def init_key # we want the erb file that renders the component. `caller` gives the file name, # and line number, which should be unique. We hash it to make it a nice number key = caller.select { |p| p.include? ".html.erb" }[1]&.hash.to_s key += collection_key.to_s if collection_key @key = key end |
#key ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'app/components/view_component_reflex/component.rb', line 132 def key # initialize session state if !stimulus_reflex? || session[@key].nil? new_state = {} # this will almost certainly break blacklist = [ :@view_context, :@lookup_context, :@view_renderer, :@view_flow, :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key, :@helpers, :@controller, :@request, :@content ] instance_variables.reject { |k| blacklist.include?(k) }.each do |k| new_state[k] = instance_variable_get(k) end ViewComponentReflex::Engine.state_adapter.store_state(request, @key, new_state) ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", new_state) else initial_state = ViewComponentReflex::Engine.state_adapter.state(request, "#{@key}_initial") ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v| unless permit_parameter?(initial_state[k], instance_variable_get(k)) instance_variable_set(k, v) end end end @key end |
#permit_parameter?(initial_param, new_param) ⇒ Boolean
128 129 130 |
# File 'app/components/view_component_reflex/component.rb', line 128 def permit_parameter?(initial_param, new_param) initial_param != new_param end |
#stimulus_reflex? ⇒ Boolean
97 98 99 |
# File 'app/components/view_component_reflex/component.rb', line 97 def stimulus_reflex? helpers.controller.instance_variable_get(:@stimulus_reflex) end |