@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 +17 -3
- package/index.js +14 -3
- package/package.json +4 -3
- package/src/cleanupSvelteComponent.js +3 -9
- package/src/componentStreamListener.js +28 -0
- package/src/config.js +19 -0
- package/src/initializeSvelteComponent.js +4 -9
- package/src/logger.js +28 -0
- package/src/turbo-stream-controller.js +56 -0
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
|
-
|
|
23
|
+
For configs you can do
|
|
24
24
|
|
|
25
|
-
|
|
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
|
-
|
|
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 {
|
|
1
|
+
import {Application} from "@hotwired/stimulus"
|
|
2
|
+
import SvelteOnRails from "./src/config.js";
|
|
2
3
|
|
|
3
|
-
window.Stimulus
|
|
4
|
+
if (!window.Stimulus) {
|
|
5
|
+
window.Stimulus = Application.start()
|
|
6
|
+
}
|
|
4
7
|
|
|
5
|
-
|
|
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": "
|
|
4
|
-
"description": "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|