turbo_live 0.1.2 → 0.1.3

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: b117e175ea920117e11f0cd6c3bab5cafc0ca7c1860fb6b890c632a37efe1509
4
- data.tar.gz: ca56b1155834dbeb85a29112a2f85b0901dbf14d61909587f698f45cfc25bb34
3
+ metadata.gz: 5c9a249ff75e9eed862b6d2ab3402d5d867368b4d43e5e1f6679ac9bcabfb3eb
4
+ data.tar.gz: 9d77ef656d61442697be5fb9f060145193636f439e971dd62af36b019354dc22
5
5
  SHA512:
6
- metadata.gz: fce48eb8c3ae06f14fb6bb365004cf399c4f85d4f147776a7b05d6d7ec4b2833182fe6e4fd5d99781ef555daaad2d0bcd4b7b757378c645a7486138c2d356809
7
- data.tar.gz: f0b1875a9143f5ca1ca218c05b1dbd3439ff7ebb13b8aabe9d72fcd58746fe41e8cde508f3a41f138ae3acfb25c66247594c1856397eaa89d2b9f6aaf170bea4
6
+ metadata.gz: 6efe19e261f10de079be70b72404fc441d02e34cb795a8a6a632a3390d5afaeb603c2bf43754ade0fafda03013cf947d9bc284fd0b2945e25306e1a73cb66890
7
+ data.tar.gz: 4abda82e8d71fbcc3275cfec205d0ea4197c814710b7f12e5cae69a5e359455996e6eb207b0035be6e6ad8156aafc6797c854f33d4f25a93e22f9222e8913011
data/README.md CHANGED
@@ -214,6 +214,7 @@ Common issues and their solutions:
214
214
  1. **Component not updating**: Ensure that your `update` method is correctly handling the event and modifying the state.
215
215
  2. **WebSocket connection failing**: Check your ActionCable configuration and ensure that your server supports WebSocket connections.
216
216
  3. **JavaScript errors**: Make sure you've correctly set up the TurboLive JavaScript integration in your application.
217
+ 3. **My timed events won't go away**: Due to the use of morphing, there might be instances where your some meta attributes are not removed.
217
218
 
218
219
  For more issues, please check our [FAQ](https://github.com/radioactive-labs/turbo_live/wiki/FAQ) or open an issue on GitHub.
219
220
 
@@ -7,8 +7,8 @@ module TurboLive
7
7
  end
8
8
 
9
9
  def receive(params)
10
- stream = Renderer.render params
11
- ActionCable.server.broadcast(stream_name, stream)
10
+ stream = Renderer.render params.symbolize_keys
11
+ ActionCable.server.broadcast(stream_name, stream) if stream
12
12
  end
13
13
 
14
14
  protected
@@ -4,7 +4,7 @@ module TurboLive
4
4
  class ComponentsController < ActionController::API
5
5
  def update
6
6
  stream = Renderer.render params.to_unsafe_hash
7
- render plain: stream
7
+ render plain: stream if stream
8
8
  end
9
9
  end
10
10
  end
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "phlex"
4
+ require "literal"
4
5
 
5
6
  module TurboLive
6
7
  class Component < Phlex::HTML
7
8
  extend Literal::Properties
8
9
 
9
- SUPPORTED_EVENTS = %i[click change].freeze
10
+ SUPPORTED_EVENTS = %i[click change input].freeze
10
11
 
11
12
  def self.state(name, type, **options, &block)
12
13
  options = {reader: :public, writer: :protected}.merge(**options).compact
@@ -52,27 +53,33 @@ module TurboLive
52
53
  end
53
54
 
54
55
  def every(milliseconds, event)
55
- data = {milliseconds => to_verifiable(event)}.to_json
56
- add_data :every, data
56
+ data = {interval: milliseconds, event: to_verifiable(event)}.to_json
57
+ add_meta :interval, data
58
+ end
59
+
60
+ def norender
61
+ SKIP_RENDER
62
+ end
63
+
64
+ def norender!
65
+ raise SkipRender
57
66
  end
58
67
 
59
68
  private
60
69
 
61
- def add_data(type, value)
62
- # Temporary hack to embed data.
63
- # Switch to HTML templates
70
+ def add_meta(type, value)
71
+ # TODO: turbo morph does some wonky things issues here since it doesn't force a replacement everytime
64
72
  div(
65
- class: "turbo-live-data",
66
- data_turbo_live_id: verifiable_live_id,
67
- data_turbo_live_data_type: type,
68
- data_turbo_live_data_value: value,
73
+ data_turbo_live_meta_type: type,
74
+ data_turbo_live_meta_value: value,
75
+ data_turbo_live_target: "meta",
69
76
  style: "display: none;", display: :none
70
77
  ) {}
71
78
  end
72
79
 
73
80
  def serialize
74
81
  state = self.class.literal_properties.map do |prop|
75
- [prop.name, instance_variable_get(:"@#{prop.name}")]
82
+ [prop.name, public_send(prop.name)]
76
83
  end.to_h
77
84
 
78
85
  {klass: self.class.to_s, state: state}
@@ -4,21 +4,24 @@ module TurboLive
4
4
  class Renderer
5
5
  class << self
6
6
  def render(data)
7
- data = data.symbolize_keys
8
7
  # build the payload
9
8
  payload = extract_payload(data)
10
9
  # create the component
11
10
  component = build_component(data)
12
11
  # run the update function
13
- component.update payload
12
+ result = component.update payload
13
+ return if result == TurboLive::SKIP_RENDER
14
+
14
15
  # render the replace stream
15
16
  <<~STREAM
16
- <turbo-stream action="replace" target="#{data[:id]}">
17
+ <turbo-stream action="replace" method="morph" target="#{data[:id]}">
17
18
  <template>
18
19
  #{component.call}
19
20
  </template>
20
21
  </turbo-stream>
21
22
  STREAM
23
+ rescue SkipRender
24
+ nil
22
25
  end
23
26
 
24
27
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TurboLive
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/turbo_live.rb CHANGED
@@ -10,6 +10,10 @@ require_relative "../app/channels/components_channel" if defined?(ActionCable)
10
10
  module TurboLive
11
11
  class Error < StandardError; end
12
12
 
13
+ class SkipRender < StandardError; end
14
+
15
+ SKIP_RENDER = :skip_render
16
+
13
17
  class << self
14
18
  attr_writer :verifier_key
15
19
 
data/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@radioactive-labs/turbo-live",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@radioactive-labs/turbo-live",
9
- "version": "0.1.2",
9
+ "version": "0.1.3",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@hotwired/stimulus": "^3.2.2",
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radioactive-labs/turbo-live",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Async, progressively enhanced, live components for Ruby applications that work over Websockets and HTTP.",
5
5
  "type": "module",
6
6
  "main": "src/js/core.js",
@@ -5,112 +5,132 @@ export default class extends Controller {
5
5
  id: String,
6
6
  component: String,
7
7
  }
8
+ static targets = ["meta"]
8
9
 
9
10
  get component() {
10
11
  return this.componentValue
11
12
  }
12
13
 
14
+ initialize() {
15
+ this.metaTargetsMap = new WeakMap()
16
+ this.metaTargetsCount = 0
17
+ this.intervals = {}
18
+ }
19
+
13
20
  connect() {
14
- console.log("TurboLiveController connected:", this.element.id, this.component)
21
+ console.log("TurboLiveController connected", this.element.id, this.component)
22
+ }
15
23
 
16
- this.intervals = []
24
+ metaTargetConnected(target) {
25
+ console.log("TurboLiveController metaTargetConnected", this.element.id, this.#metaTargetId(target))
26
+ this.#readMetadata(target)
27
+ }
17
28
 
18
- this.#readEmbeddedData()
29
+ metaTargetDisconnected(target) {
30
+ console.log("TurboLiveController metaTargetDisconnected", this.element.id, this.#metaTargetId(target))
31
+ this.#teardownInterval(this.#metaTargetId(target))
19
32
  }
20
33
 
21
34
  disconnect() {
22
- console.log("TurboLiveController disconnected")
23
- this.#clearIntervals()
35
+ console.log("TurboLiveController disconnected", this.element.id)
36
+ this.#cleanup()
24
37
  }
25
38
 
26
39
  dispatch(event, payload) {
27
- console.log("TurboLiveController dispatch:", this.element.id, event, payload)
28
- let data = { id: this.element.id, event: event, payload: payload, component: this.component }
40
+ console.log("TurboLiveController dispatch", this.element.id, event, payload)
41
+ const data = { id: this.element.id, event, payload, component: this.component }
42
+
29
43
  if (window.turboLive) {
30
- console.log("TurboLiveController dispatching via websockets")
44
+ console.log("TurboLiveController dispatching via websockets", this.element.id)
31
45
  window.turboLive.send(data)
32
- }
33
- else {
34
- console.log("TurboLiveController dispatching via HTTP")
35
-
36
- fetch('/turbo_live', {
37
- method: 'POST',
38
- headers: {
39
- 'Content-Type': 'application/json',
40
- },
41
- body: JSON.stringify(data)
42
- })
43
- .then(response => {
44
- if (!response.ok) {
45
- throw new Error(`Network response was not OK`);
46
- }
47
- return response.text();
48
- })
49
- .then(turbo_stream => {
50
- console.log('TurboLiveController dispatch success:', turbo_stream);
51
- Turbo.renderStreamMessage(turbo_stream);
52
- })
53
- .catch((error) => {
54
- console.error('TurboLiveController dispatch error:', error);
55
- });
46
+ } else {
47
+ console.log("TurboLiveController dispatching via HTTP", this.element.id)
48
+ this.#dispatchHTTP(data)
56
49
  }
57
50
  }
58
51
 
59
52
  onClick(event) {
60
- // event.preventDefault();
61
- console.log("TurboLiveController onClick")
53
+ console.log("TurboLiveController onClick", this.element.id)
62
54
  this.#dispatchSimpleEvent("click", event)
63
55
  }
64
56
 
65
57
  onChange(event) {
66
- // event.preventDefault();
67
- console.log("TurboLiveController onChange")
58
+ console.log("TurboLiveController onChange", this.element.id)
68
59
  this.#dispatchValueEvent("change", event)
69
60
  }
70
61
 
71
- #readEmbeddedData() {
72
- this.element.querySelectorAll(".turbo-live-data").forEach((element) => {
73
- if (this.element.id != element.dataset.turboLiveId) return;
62
+ onInput(event) {
63
+ console.log("TurboLiveController onInput", this.element.id)
64
+ this.#dispatchValueEvent("input", event)
65
+ }
74
66
 
75
- let type = element.dataset.turboLiveDataType
76
- let value = JSON.parse(element.dataset.turboLiveDataValue)
77
- switch (type) {
78
- case "every":
79
- this.#setupInterval(value)
80
- break;
81
- }
82
- })
67
+ #metaTargetId(target) {
68
+ if (!this.metaTargetsMap.has(target)) {
69
+ this.metaTargetsMap.set(target, ++this.metaTargetsCount)
70
+ }
71
+ return this.metaTargetsMap.get(target)
83
72
  }
84
73
 
85
- #setupInterval(intervalConfig) {
86
- try {
87
- for (let interval in intervalConfig) {
88
- this.intervals.push(
89
- setInterval(() => {
90
- this.dispatch("every", [intervalConfig[interval]])
91
- }, interval)
92
- )
93
- }
74
+ #readMetadata(element) {
75
+ const type = element.dataset.turboLiveMetaType
76
+ if (type === "interval") {
77
+ this.#setupInterval(element)
94
78
  }
95
- catch (e) {
79
+ }
80
+
81
+ #setupInterval(element) {
82
+ try {
83
+ const config = JSON.parse(element.dataset.turboLiveMetaValue)
84
+ this.intervals[this.#metaTargetId(element)] = setInterval(() => {
85
+ this.dispatch("interval", [config.event])
86
+ }, config.interval)
87
+ } catch (e) {
96
88
  console.error(e)
97
89
  }
98
90
  }
99
91
 
100
- #clearIntervals() {
101
- this.intervals.forEach((interval) => {
102
- clearInterval(interval)
103
- })
92
+ #teardownInterval(id) {
93
+ if (this.intervals[id]) {
94
+ clearInterval(this.intervals[id])
95
+ delete this.intervals[id]
96
+ }
97
+ }
98
+
99
+ #cleanup() {
100
+ Object.keys(this.intervals).forEach(this.#teardownInterval.bind(this))
104
101
  }
105
102
 
106
103
  #dispatchSimpleEvent(name, { params }) {
107
- let live_event = params[name]
108
- this.dispatch(name, [live_event])
104
+ const liveEvent = params[name]
105
+ this.dispatch(name, [liveEvent])
109
106
  }
110
107
 
111
108
  #dispatchValueEvent(name, { params, target }) {
112
- let value = target.value
113
- let live_event = params[name]
114
- this.dispatch(name, [live_event, value])
109
+ const value = target.value
110
+ const liveEvent = params[name]
111
+ this.dispatch(name, [liveEvent, value])
112
+ }
113
+
114
+ #dispatchHTTP(data) {
115
+ fetch('/turbo_live', {
116
+ method: 'POST',
117
+ headers: {
118
+ 'Content-Type': 'application/json',
119
+ },
120
+ body: JSON.stringify(data)
121
+ })
122
+ .then(response => {
123
+ if (!response.ok) {
124
+ throw new Error(`Network response was not OK`)
125
+ }
126
+ return response.text()
127
+ })
128
+ .then(turboStream => {
129
+ console.log('TurboLiveController dispatch success', this.element.id, turboStream)
130
+ if (turboStream) Turbo.renderStreamMessage(turboStream)
131
+ })
132
+ .catch((error) => {
133
+ console.error('TurboLiveController dispatch error', this.element.id, error)
134
+ })
115
135
  }
116
- }
136
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo_live
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - TheDumbTechGuy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-13 00:00:00.000000000 Z
11
+ date: 2024-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: phlex-rails