turbo_boost-streams 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- import invoke from './invoke'
1
+ import { invoke, invokeEvents } from './invoke'
2
2
 
3
3
  if (!self['Turbo'])
4
4
  throw new Error(
@@ -12,8 +12,10 @@ if (!Turbo['StreamActions'])
12
12
 
13
13
  Turbo.StreamActions.invoke = invoke
14
14
  self.TurboBoost = self.TurboBoost || {}
15
- self.TurboBoost.Streams = { invoke }
15
+ self.TurboBoost.Streams = { invoke, invokeEvents }
16
16
 
17
17
  console.info(
18
18
  '@turbo-boost/streams has initialized and registered new stream actions with Turbo.'
19
19
  )
20
+
21
+ export default self.TurboBoost.Streams
@@ -1,38 +1,118 @@
1
- import dispatch from './methods/dispatch'
2
- import mutate from './methods/mutate'
1
+ import morph from './morph'
3
2
 
4
- function perform (method, args, receivers) {
5
- // dispatch / dispatchEvent
3
+ export const invokeEvents = {
4
+ before: 'turbo-boost:stream:before-invoke',
5
+ after: 'turbo-boost:stream:after-invoke',
6
+ finish: 'turbo-boost:stream:finish-invoke'
7
+ }
8
+
9
+ // Invokes the callback on a single receiver with before/after events
10
+ function withInvokeEvents (receiver, detail, callback) {
11
+ const { object, target } = receiver
12
+ detail = detail || {}
13
+ detail = { ...detail, object: receiver.object }
14
+ const options = { detail, bubbles: true }
15
+
16
+ target.dispatchEvent(new CustomEvent(invokeEvents.before, options))
17
+
18
+ const result = callback(object)
19
+ options.detail.result = result
20
+ target.dispatchEvent(new CustomEvent(invokeEvents.after, options))
21
+
22
+ let promise
23
+ if (result instanceof Animation) promise = result.finished
24
+ if (result instanceof Promise) promise = result
25
+
26
+ if (promise)
27
+ promise.then(
28
+ () => {
29
+ options.detail.promise = 'fulfilled'
30
+ target.dispatchEvent(new CustomEvent(invokeEvents.finish, options))
31
+ },
32
+ () => {
33
+ options.detail.promise = 'rejected'
34
+ target.dispatchEvent(new CustomEvent(invokeEvents.finish, options))
35
+ }
36
+ )
37
+ else target.dispatchEvent(new CustomEvent(invokeEvents.finish, options))
38
+ }
39
+
40
+ function invokeDispatchEvent (method, args, receivers) {
41
+ const eventName = args[0]
42
+ const eventOptions = args[1]
43
+ const detail = { method, eventName, eventOptions }
44
+ receivers.forEach(receiver =>
45
+ withInvokeEvents(receiver, detail, object =>
46
+ object.dispatchEvent(new CustomEvent(eventName, eventOptions))
47
+ )
48
+ )
49
+ }
50
+
51
+ function invokeMorph (method, args, receivers) {
52
+ const html = args[0]
53
+ const detail = { method, html }
54
+ receivers.forEach(receiver =>
55
+ withInvokeEvents(receiver, detail, object => morph(object, html))
56
+ )
57
+ }
58
+
59
+ function invokeAssignment (method, args, receivers) {
60
+ const property = method.slice(0, -1).trim()
61
+ const value = args[0]
62
+ const detail = { method, property, value }
63
+ receivers.forEach(receiver =>
64
+ withInvokeEvents(receiver, detail, object => (object[property] = value))
65
+ )
66
+ }
67
+
68
+ function invokeMethod (method, args, receivers) {
69
+ const detail = { method, args }
70
+ receivers.forEach(receiver =>
71
+ withInvokeEvents(receiver, detail, object =>
72
+ object[method].apply(object, args)
73
+ )
74
+ )
75
+ }
76
+
77
+ // Performs an invocation on all receivers for the given method and args
78
+ function performInvoke (method, args, receivers) {
79
+ // dispatch ................................................................................................
6
80
  if (method.match(/^dispatch(Event)?$/))
7
- return dispatch(receivers, args[0], args[1] || {})
81
+ return invokeDispatchEvent(method, args, receivers)
8
82
 
9
- // morph / mutate
10
- if (method.match(/^morph|mutate$/)) return mutate(receivers, args[0])
83
+ // morph ...................................................................................................
84
+ if (method.match(/^morph|mutate$/))
85
+ return invokeMorph(method, args, receivers)
11
86
 
12
- // property assignment
13
- if (method.endsWith('='))
14
- return receivers.forEach(r => (r[method.slice(0, -1).trim()] = args[0]))
87
+ // assignment ..............................................................................................
88
+ if (method.endsWith('=')) return invokeAssignment(method, args, receivers)
15
89
 
16
- // method invocation
17
- receivers.forEach(r => r[method].apply(r, args))
90
+ // method ..................................................................................................
91
+ return invokeMethod(method, args, receivers)
18
92
  }
19
93
 
20
- function invoke () {
94
+ export function invoke () {
21
95
  const payload = JSON.parse(this.templateContent.textContent)
22
- const { id, selector, receiver, method, args } = payload
23
- let receivers = [self]
24
- if (selector) receivers = Array.from(document.querySelectorAll(selector))
96
+ const { id, selector, receiver, method, args, delay } = payload
97
+ let receivers = [{ object: self, target: self }]
98
+ if (selector)
99
+ receivers = Array.from(document.querySelectorAll(selector)).map(el => ({
100
+ object: el,
101
+ target: el
102
+ }))
25
103
 
26
104
  if (receiver) {
27
105
  receivers = receivers.map(r => {
28
- let context = r
106
+ let { object, target } = r
29
107
  const chain = receiver.split('.')
30
- while (chain.length > 0) context = context[chain.shift()]
31
- return context
108
+ while (chain.length > 0) {
109
+ object = object[chain.shift()]
110
+ if (object.dispatchEvent) target = object
111
+ }
112
+ return { object, target }
32
113
  })
33
114
  }
34
115
 
35
- perform(method, args, receivers)
116
+ if (delay > 0) setTimeout(() => performInvoke(method, args, receivers), delay)
117
+ else performInvoke(method, args, receivers)
36
118
  }
37
-
38
- export default invoke
@@ -1,7 +1,7 @@
1
1
  import alpine from 'alpinejs'
2
- import morph from '@alpinejs/morph'
2
+ import alpineMorph from '@alpinejs/morph'
3
3
 
4
- alpine.plugin(morph)
4
+ alpine.plugin(alpineMorph)
5
5
 
6
6
  const input = /INPUT/i
7
7
  const inputTypes = /date|datetime-local|email|month|number|password|range|search|tel|text|time|url|week/i
@@ -18,8 +18,6 @@ function updating (el, toEl, childrenOnly, skip) {
18
18
  if (protect) return skip()
19
19
  }
20
20
 
21
- function mutate (elements, html) {
22
- elements.forEach(element => alpine.morph(element, html, { updating }))
21
+ export default function morph (element, html) {
22
+ alpine.morph(element, html, { updating })
23
23
  }
24
-
25
- export default mutate
@@ -8,8 +8,7 @@ require_relative "../tag_helper"
8
8
  module TurboBoost::Streams::Patches::TagBuilder
9
9
  include TurboBoost::Streams::TagHelper
10
10
 
11
- def invoke(method, args: [], selector: nil, camelize: true, id: nil)
12
- tag = turbo_stream_invoke_tag(method, args: args, selector: selector, camelize: camelize, id: id)
13
- TurboBoost::Streams::StringWrapper.new tag
11
+ def invoke(...)
12
+ TurboBoost::Streams::StringWrapper.new turbo_stream_invoke_tag(...)
14
13
  end
15
14
  end
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TurboBoost::Streams::TagHelper
4
- def turbo_stream_invoke_tag(method, args: [], selector: nil, camelize: true, id: nil)
4
+ def turbo_stream_invoke_tag(method, args: [], delay: 0, selector: nil, camelize: true, id: nil)
5
5
  id = SecureRandom.uuid if id.blank?
6
6
  payload = HashWithIndifferentAccess.new(id: id.to_s, selector: selector)
7
- payload.merge! method_details(method, args: args, camelize: camelize)
7
+ payload.merge! method_details(method, args: args, delay: delay, camelize: camelize)
8
8
  payload.select! { |_, v| v.present? }
9
9
  %(<turbo-stream action="invoke" target="DOM"><template>#{payload.to_json}</template></turbo-stream>).html_safe
10
10
  end
11
11
 
12
12
  private
13
13
 
14
- def method_details(method, args: [], camelize: true)
14
+ def method_details(method, args: [], delay: 0, camelize: true)
15
15
  if camelize
16
16
  method = camelize_method(method)
17
17
  args = camelize_args(args)
@@ -22,7 +22,8 @@ module TurboBoost::Streams::TagHelper
22
22
  HashWithIndifferentAccess.new(
23
23
  receiver: method_parts[0..-2].join("."),
24
24
  method: method_parts.last,
25
- args: args
25
+ args: args,
26
+ delay: delay.to_i
26
27
  )
27
28
  end
28
29
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module TurboBoost
4
4
  module Streams
5
- VERSION = "0.0.5"
5
+ VERSION = "0.0.6"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo_boost-streams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Hopkins (hopsoft)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-05 00:00:00.000000000 Z
11
+ date: 2023-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -311,8 +311,7 @@ files:
311
311
  - app/assets/images/turbo-boost-mark.afdesign
312
312
  - app/javascript/index.js
313
313
  - app/javascript/invoke.js
314
- - app/javascript/methods/dispatch.js
315
- - app/javascript/methods/mutate.js
314
+ - app/javascript/morph.js
316
315
  - app/jobs/turbo_boost/streams/broadcast_invoke_job.rb
317
316
  - lib/turbo_boost/streams.rb
318
317
  - lib/turbo_boost/streams/engine.rb
@@ -338,14 +337,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
338
337
  requirements:
339
338
  - - ">="
340
339
  - !ruby/object:Gem::Version
341
- version: '0'
340
+ version: '2.7'
342
341
  required_rubygems_version: !ruby/object:Gem::Requirement
343
342
  requirements:
344
343
  - - ">="
345
344
  - !ruby/object:Gem::Version
346
345
  version: '0'
347
346
  requirements: []
348
- rubygems_version: 3.3.21
347
+ rubygems_version: 3.4.1
349
348
  signing_key:
350
349
  specification_version: 4
351
350
  summary: Take full control of the DOM with Turbo Streams
@@ -1,6 +0,0 @@
1
- function dispatch (targets, name, options = {}) {
2
- const evt = new CustomEvent(name, options)
3
- targets.forEach(t => t.dispatchEvent(evt))
4
- }
5
-
6
- export default dispatch