@csedl/svelte-on-rails 2.0.2 → 5.0.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.
package/README.md CHANGED
@@ -20,13 +20,27 @@ Add to your entrypoint file, like `application.js`
20
20
  import '@csedl/svelte-on-rails';
21
21
  ```
22
22
 
23
- ## How it works
23
+ For configs you can do
24
24
 
25
- It includes a stimulus controller that handles the initializing event of the svelte component,
25
+ ```javascript
26
+ import { SvelteOnRails } from '@csedl/svelte-on-rails'
27
+ SvelteOnRails.debug = true
28
+ ```
29
+
30
+ The `SvelteOnRails` Object is accessible by `window.SvelteOnRails`
31
+
32
+ ## What it contains or does
33
+
34
+ - It includes a stimulus controller that handles the initializing event of the svelte component,
26
35
  rendered by the ruby gem. Initializing means that the component is mounted or hydrated, together
27
36
  with the properties given by the ruby gem.
37
+ - After disappearing, the component is unmounted by the disconnect event of stimulus.
38
+
39
+ **Turbo-Streams**
28
40
 
29
- After disappearing, the component is unmounted by the disconnect event of stimulus.
41
+ - For working with `Turbo Streams` it contains a stimulus controller that dispatches actions
42
+ on Svelte Components.
43
+ - Helper for adding eventListeners on Svelte Components.
30
44
 
31
45
  ## Options
32
46
 
package/index.js CHANGED
@@ -1,9 +1,20 @@
1
- import { Application } from "@hotwired/stimulus"
1
+ import {Application} from "@hotwired/stimulus"
2
+ import SvelteOnRails from "./src/config.js";
2
3
 
3
- window.Stimulus = Application.start()
4
+ if (!window.Stimulus) {
5
+ window.Stimulus = Application.start()
6
+ }
4
7
 
5
- import controller from "./src/svelte-on-rails-controller";
8
+ // initialize svelte-on-rails
9
+
10
+ import controller from "./src/svelte-on-rails-controller.js";
6
11
 
7
12
  Stimulus.register('svelte-on-rails', controller)
8
13
 
14
+ // initialize stream actions
15
+
16
+ import turboStreamController from "./src/turbo-stream-controller.js";
17
+
18
+ Stimulus.register('svelte-on-rails-turbo-stream', turboStreamController)
9
19
 
20
+ export { SvelteOnRails };
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@csedl/svelte-on-rails",
3
- "version": "2.0.2",
4
- "description": "Hydrates or mounts Svelte components rendered by the svelte-on-rails Ruby gem.",
3
+ "version": "5.0.0",
4
+ "description": "Mounts Svelte components from the svelte-on-rails gem and handles their Turbo Stream Actions.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "exports": {
8
- ".": "./index.js"
8
+ ".": "./index.js",
9
+ "./src/componentStreamListener": "./src/componentStreamListener.js"
9
10
  },
10
11
  "peerDependencies": {
11
12
  "@hotwired/stimulus": "^3.2.2",
@@ -1,4 +1,5 @@
1
1
  import { unmount } from 'svelte';
2
+ import { debugLog } from "./logger.js";
2
3
 
3
4
  // Store for tracking initialized Svelte component instances
4
5
  const svelteInstances = new WeakMap();
@@ -11,11 +12,11 @@ export function cleanupSvelteComponent(element, debug = false) {
11
12
  const instance = svelteInstances.get(element);
12
13
  if (instance) {
13
14
  unmount(instance); // Destroy the Svelte component instance in Svelte 5
14
- debugLog2(`Successfully unmounted Svelte component for element:`, element, debug);
15
+ debugLog(`Successfully unmounted Svelte component for element:`, element, debug);
15
16
  element.removeAttribute('data-svelte-initialized'); // Reset initialized state
16
17
  svelteInstances.delete(element); // Remove from WeakMap
17
18
  } else {
18
- debugLog2(`No Svelte instance found for element:`, element, debug);
19
+ debugLog(`No Svelte instance found for element:`, element, debug);
19
20
  }
20
21
  } catch (e) {
21
22
  console.error(`[cleanupSvelteComponents] Error unmounting Svelte component for element:`, e);
@@ -23,10 +24,3 @@ export function cleanupSvelteComponent(element, debug = false) {
23
24
  element.innerHTML = '';
24
25
  }
25
26
  }
26
-
27
-
28
- export function debugLog2(msg, object) {
29
- if (window.debugSvelteOnRailsComponents) {
30
- console.log(`[cleanupSvelteComponent] ${msg}`, object);
31
- }
32
- }
@@ -0,0 +1,28 @@
1
+ ///import {receiveStreamEventDebugLog} from "./logger.js";
2
+
3
+ // componentStreamListener.js (in your npm package)
4
+ export function addComponentStreamListener(node, callback) {
5
+
6
+ // Find the wrapping element
7
+ const wrapper = node.closest('.svelte-component');
8
+
9
+ if (!wrapper) {
10
+ console.error('[svelte-on-rails:svelte-component] No wrapping element with class .svelte-component found!');
11
+ return;
12
+ }
13
+
14
+ // Event handler for Turbo::StreamsChannel events
15
+ const handleAction = (event) => {
16
+ callback(event); // Call the provided callback with the event
17
+ };
18
+
19
+ // Attach the event listener to the wrapper
20
+ wrapper.addEventListener('stream-action', handleAction);
21
+
22
+ // Cleanup function
23
+ return {
24
+ destroy() {
25
+ wrapper.removeEventListener('stream-action', handleAction);
26
+ }
27
+ };
28
+ }
package/src/config.js ADDED
@@ -0,0 +1,19 @@
1
+ // src/config.js
2
+ let _debug = false; // Internal state to track debug
3
+
4
+ import {debugLog} from "./logger.js";
5
+
6
+ const SvelteOnRails = {
7
+ get debug() {
8
+ return _debug;
9
+ },
10
+ set debug(value) {
11
+ _debug = value;
12
+
13
+ debugLog('debugging active')
14
+ },
15
+ };
16
+
17
+ window.SvelteOnRails = SvelteOnRails;
18
+
19
+ export default SvelteOnRails;
@@ -1,4 +1,5 @@
1
1
  import {mount, hydrate} from 'svelte';
2
+ import { debugLog } from "./logger.js";
2
3
 
3
4
  // Store for tracking initialized Svelte component instances
4
5
  const svelteInstances = new WeakMap();
@@ -21,7 +22,7 @@ export function initializeSvelteComponent(element, debug = false) {
21
22
  for (const cmp in allComponents) {
22
23
  if (cmp === componentPath) {
23
24
  component = allComponents[cmp].default;
24
- debugLog2(`found component «${cmp}»`, component)
25
+ debugLog(`found component «${cmp}»`, component)
25
26
  } else {
26
27
  }
27
28
  }
@@ -48,14 +49,14 @@ export function initializeSvelteComponent(element, debug = false) {
48
49
  props,
49
50
  hydrate: false,
50
51
  });
51
- debugLog2(`Mounted successfully ${componentName}, with parsed props:`, props, debug);
52
+ debugLog(`Mounted successfully ${componentName}, with parsed props:`, props, debug);
52
53
  } else if (action === 'hydrate') {
53
54
  instance = hydrate(component, {
54
55
  target: element,
55
56
  hydrate: true,
56
57
  props: props,
57
58
  });
58
- debugLog2(`Hydrated successfully ${componentName}, with parsed props:`, props, debug);
59
+ debugLog(`Hydrated successfully ${componentName}, with parsed props:`, props, debug);
59
60
  } else {
60
61
  console.error(`[initializeSvelteComponents] Unknown action: "${action}" for component ${componentName}`);
61
62
  }
@@ -68,9 +69,3 @@ export function initializeSvelteComponent(element, debug = false) {
68
69
  console.error(`[initializeSvelteComponents] Error ${action} ${componentName}:`, e);
69
70
  }
70
71
  }
71
-
72
- export function debugLog2(msg, object) {
73
- if (window.debugSvelteOnRailsComponents) {
74
- console.log(`[initializeSvelteComponents] ${msg}`, object);
75
- }
76
- }
package/src/logger.js ADDED
@@ -0,0 +1,28 @@
1
+ import SvelteOnRails from './config.js'
2
+
3
+ export function debugLog(message, object, object2) {
4
+ log( null, message, object, object2);
5
+ }
6
+
7
+ export function receiveTurboStreamDebugLog(message, object, object2) {
8
+ log('receive-turbo-stream', message, object, object2);
9
+ }
10
+
11
+ export function receiveStreamEventDebugLog(message, object, object2) {
12
+ log( 'receive-stream-event', message, object, object2);
13
+ }
14
+
15
+ function log(namespace, message, object, object2) {
16
+ if (window.SvelteOnRails.debug) {
17
+
18
+ const nmsp = (namespace ? `:${namespace}` : '');
19
+
20
+ if (object2) {
21
+ console.log(`[svelte-on-rails${nmsp}] ${message}`, object, object2);
22
+ } else if (object) {
23
+ console.log(`[svelte-on-rails${nmsp}] ${message}`, object);
24
+ } else {
25
+ console.log(`[svelte-on-rails${nmsp}] ${message}`);
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,56 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+ import {receiveTurboStreamDebugLog} from "./logger.js";
3
+
4
+ export default class extends Controller {
5
+
6
+ connect() {
7
+
8
+ const argsBase64 = this.element.getAttribute('data-args')
9
+ receiveTurboStreamDebugLog('turbo-stream received with base-64 encoded string:', argsBase64)
10
+
11
+ const decodedString = this.decodeBase64(argsBase64)
12
+
13
+ let args = JSON.parse(decodedString)
14
+
15
+ receiveTurboStreamDebugLog(`parsed arguments:`, args)
16
+ let components
17
+ if (args.component) {
18
+ const componentsSelector = `[data-svelte-component="${args.component}"]`
19
+ components = document.querySelectorAll(componentsSelector)
20
+ receiveTurboStreamDebugLog(`found ${components.length} components by selector «${componentsSelector}»`, components)
21
+ } else {
22
+ components = Array.from(document.getElementsByClassName('svelte-component'))
23
+ receiveTurboStreamDebugLog(`found ${components.length} components by class-name «svelte-component» (no components selector given)`, components)
24
+ }
25
+
26
+ const customEvent = new CustomEvent(args.event, {detail: args['eventDetail']})
27
+
28
+ components.forEach(component => {
29
+
30
+ if (args.selector) {
31
+ const elements = component.querySelectorAll(args.selector)
32
+ if (elements.length === 0) {
33
+ receiveTurboStreamDebugLog(`no elements found by selector «${args.selector}», within:`, component)
34
+ }
35
+ elements.forEach(element => {
36
+ receiveTurboStreamDebugLog(`dispatch event «${args.event}» on element:`, element)
37
+ element.dispatchEvent(customEvent)
38
+ })
39
+ } else {
40
+ receiveTurboStreamDebugLog(`dispatch event «${args.event}» on components wrapper:`, component)
41
+ component.dispatchEvent(customEvent)
42
+ }
43
+
44
+
45
+ })
46
+
47
+ this.element.remove()
48
+ }
49
+
50
+ decodeBase64(base64String) {
51
+ const binaryString = atob(base64String.trim())
52
+ const bytes = new Uint8Array([...binaryString].map(char => char.charCodeAt(0)))
53
+ return new TextDecoder("utf-8").decode(bytes)
54
+ }
55
+
56
+ }