reactive_component 0.8.4 → 0.8.5

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: 77acde881234cd7b6013ab8618efd2cbeb1ffd1a4c6232199517a9b14f4aa9ce
4
- data.tar.gz: 1854830f220dd61bb49625a1a2a69e29d4e42ce96a40cf371586d3dd898bcbfc
3
+ metadata.gz: 5ec78650feb0396bbe85c2adc0994209dc4d1ab91a6cf0ca49d9692b17e0b316
4
+ data.tar.gz: c2d54ef039a0ea502c593a5eb1e173f509955e3e37b0f6eea1cd78c05ed365d5
5
5
  SHA512:
6
- metadata.gz: 2721d5f6252603a600a4b297276a460cece0d4216303d40b6daa193b9d141a745c7024c347f6109463016f0f21d2beef01a083fa11d4fc3d294125973fa98630
7
- data.tar.gz: 78a474176edff971af3979ca7c0b02d667c5be59e89f19b2dda28b770184f651799f0220eb2c65f5dfb47e938c4307253b52ad0bdf3df09a16a9679039bb832a
6
+ metadata.gz: caf86cb2646eea037defb27405b486555e70377351c6fe3a61b7d1b00aa33cf330d6ace77a42936e9c93c2bbb992026d0a5a2138137ac6591ff6efd1a45f6c21
7
+ data.tar.gz: fc02fd8569b770ff6b6ed6583cdfbc4fb3dfb2630768eaa7d4f095ce755cc7ecf7617721fa542ca5f76a7a149984cbd5549e4f959911e67b739f96a4e2aa3a22
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.5] - 2026-09-16
4
+
5
+ ### Added
6
+ - `morphHooks`: register Idiomorph callbacks that run inside every component's
7
+ morph, for flashing changed values and similar page-wide behaviour, without
8
+ subclassing the controller. See Hooking into the morph.
9
+
10
+ ### Fixed
11
+ - An app that does not eager load (development, test) broadcast nothing from a
12
+ process that had not rendered the subscribing component yet, such as a job or
13
+ a turbo-stream request. The engine now loads the components under
14
+ `app/components` that include `ReactiveComponent` on boot and after each
15
+ reload, and only those, so the rest of the app keeps lazy loading. Set
16
+ `ReactiveComponent::SubscriberLoader.paths` to look elsewhere.
17
+
3
18
  ## [0.8.4] - 2026-09-16
4
19
 
5
20
  ### Added
@@ -34,6 +34,21 @@ export async function decompress(base64) {
34
34
  return new Response(stream).json()
35
35
  }
36
36
 
37
+ // Idiomorph's callbacks, open for the app to hook into: push an object with any
38
+ // of them (a flash on changed numbers, a sound, a counter) and it runs for every
39
+ // component. A hook returning false cancels that node, as Idiomorph expects.
40
+ export const morphHooks = []
41
+
42
+ const HOOK_NAMES = [
43
+ 'beforeNodeAdded', 'afterNodeAdded', 'beforeNodeMorphed', 'afterNodeMorphed',
44
+ 'beforeNodeRemoved', 'afterNodeRemoved', 'beforeAttributeUpdated'
45
+ ]
46
+
47
+ const morphCallbacks = Object.fromEntries(HOOK_NAMES.map(name => [
48
+ name,
49
+ (...args) => morphHooks.reduce((ok, hook) => (hook[name]?.(...args) === false ? false : ok), true)
50
+ ]))
51
+
37
52
  export function morphElement(element, newHtml) {
38
53
  const parser = new DOMParser()
39
54
  const doc = parser.parseFromString(`<div>${newHtml}</div>`, "text/html")
@@ -42,7 +57,8 @@ export function morphElement(element, newHtml) {
42
57
  if (typeof Idiomorph !== "undefined") {
43
58
  Idiomorph.morph(element, newContent, {
44
59
  morphStyle: "innerHTML",
45
- ignoreActiveValue: true
60
+ ignoreActiveValue: true,
61
+ callbacks: morphCallbacks
46
62
  })
47
63
  } else {
48
64
  element.innerHTML = newContent.innerHTML
@@ -15,6 +15,16 @@ module ReactiveComponent
15
15
  ReactiveComponent::DataEvaluator.finalize!
16
16
  end
17
17
 
18
+ # A component wires its model up when the class loads, so an app that does
19
+ # not eager load (development, test) would broadcast nothing from a process
20
+ # that has not rendered that component yet: a job, or a turbo-stream
21
+ # request. Load the subscribing components on boot and after each reload.
22
+ initializer 'reactive_component.load_subscribers' do |app|
23
+ next if app.config.eager_load
24
+
25
+ app.config.to_prepare { ReactiveComponent::SubscriberLoader.load_all }
26
+ end
27
+
18
28
  initializer 'reactive_component.importmap', before: 'importmap' do |app|
19
29
  if defined?(Importmap)
20
30
  app.config.importmap.paths <<
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ReactiveComponent
4
+ # A component wires its model up when the class loads, so an app that does
5
+ # not eager load broadcasts nothing from a process that has not rendered that
6
+ # component yet. This loads the components that subscribe, and nothing else.
7
+ module SubscriberLoader
8
+ mattr_accessor :paths, default: ['app/components']
9
+
10
+ SUBSCRIBER = /include\s+ReactiveComponent\b/
11
+
12
+ module_function
13
+
14
+ def load_all
15
+ loader = Rails.autoloaders.main
16
+
17
+ files.each { |file| loader.cpath_expected_at(file)&.safe_constantize }
18
+ end
19
+
20
+ # The files under `paths` that include the concern, read rather than
21
+ # loaded, so an app keeps lazy loading for everything else.
22
+ def files
23
+ paths.flat_map do |path|
24
+ dir = Rails.root.join(path)
25
+ next [] unless Dir.exist?(dir)
26
+
27
+ Dir.glob(dir.join('**', '*.rb')).select { |file| File.read(file).match?(SUBSCRIBER) }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReactiveComponent
4
- VERSION = '0.8.4'
4
+ VERSION = '0.8.5'
5
5
  end
@@ -8,6 +8,7 @@ require_relative 'reactive_component/compiler'
8
8
  require_relative 'reactive_component/data_evaluator'
9
9
  require_relative 'reactive_component/wrapper'
10
10
  require_relative 'reactive_component/broadcastable'
11
+ require_relative 'reactive_component/subscriber_loader'
11
12
  require_relative 'reactive_component/entity'
12
13
  require_relative 'reactive_component/engine' if defined?(Rails::Engine)
13
14
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reactive_component
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.4
4
+ version: 0.8.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Przemyslaw Lusar
@@ -115,6 +115,7 @@ files:
115
115
  - lib/reactive_component/engine.rb
116
116
  - lib/reactive_component/entity.rb
117
117
  - lib/reactive_component/erubi.rb
118
+ - lib/reactive_component/subscriber_loader.rb
118
119
  - lib/reactive_component/transpiler.rb
119
120
  - lib/reactive_component/version.rb
120
121
  - lib/reactive_component/wrapper.rb