From d7288835fa5fb6651e935789a42c2b503c882ef1 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 10 Jun 2025 13:00:33 +0530 Subject: [PATCH] feat: vue component for PoC --- .../src/components/VueWebComponentWrapper.jsx | 106 ++++++++++++++ app/javascript/react-components/src/index.jsx | 3 +- .../src/vue-components/VueHelloWorld.vue | 134 ++++++++++++++++++ .../vue-components/registerWebComponents.js | 18 +++ 4 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 app/javascript/react-components/src/components/VueWebComponentWrapper.jsx create mode 100644 app/javascript/react-components/src/vue-components/VueHelloWorld.vue create mode 100644 app/javascript/react-components/src/vue-components/registerWebComponents.js diff --git a/app/javascript/react-components/src/components/VueWebComponentWrapper.jsx b/app/javascript/react-components/src/components/VueWebComponentWrapper.jsx new file mode 100644 index 000000000..43a4d1fb8 --- /dev/null +++ b/app/javascript/react-components/src/components/VueWebComponentWrapper.jsx @@ -0,0 +1,106 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { registerVueWebComponents } from '../vue-components/registerWebComponents'; + +export const VueWebComponentWrapper = ({ + initialCount = 0, + title = 'Hello from Vue in React!', + color = '#42b883', + ...otherProps +}) => { + const elementRef = useRef(); + const [isReady, setIsReady] = useState(false); + const [currentCount, setCurrentCount] = useState(initialCount); + + // Register Web Components on mount + useEffect(() => { + registerVueWebComponents(); + setIsReady(true); + + // Listen for custom events from Vue component + const handleCountChange = event => { + setCurrentCount(event.detail.count); + console.log('Vue component count changed:', event.detail.count); + }; + + document.addEventListener('vue-counter-changed', handleCountChange); + + return () => { + document.removeEventListener('vue-counter-changed', handleCountChange); + }; + }, []); + + // Update Web Component properties when React props change + useEffect(() => { + if (!isReady || !elementRef.current) return; + + const element = elementRef.current; + + // Set properties on the Web Component + // Vue converts camelCase props to kebab-case attributes + element.setAttribute('initial-count', initialCount); + element.setAttribute('title', title); + element.setAttribute('color', color); + + console.log('Updated Vue Web Component props:', { + initialCount, + title, + color, + }); + }, [isReady, initialCount, title, color]); + + if (!isReady) { + return ( +
+ Loading Vue Web Component... +
+ ); + } + + return ( +
+
+ React Wrapper Info: Current count tracked by React:{' '} + {currentCount} +
+ + {/* This is the Vue Web Component rendered inside React */} + + +
+ ✅ This Vue component is running as a Web Component inside React! +
+
+ ); +}; diff --git a/app/javascript/react-components/src/index.jsx b/app/javascript/react-components/src/index.jsx index 45893072f..c2f0dcd63 100644 --- a/app/javascript/react-components/src/index.jsx +++ b/app/javascript/react-components/src/index.jsx @@ -1,2 +1,3 @@ // Export all React components for the Chatwoot React Components library -export { HelloWorld } from './components/HelloWorld'; \ No newline at end of file +export { HelloWorld } from './components/HelloWorld'; +export { VueWebComponentWrapper } from './components/VueWebComponentWrapper'; \ No newline at end of file diff --git a/app/javascript/react-components/src/vue-components/VueHelloWorld.vue b/app/javascript/react-components/src/vue-components/VueHelloWorld.vue new file mode 100644 index 000000000..aba34307d --- /dev/null +++ b/app/javascript/react-components/src/vue-components/VueHelloWorld.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/app/javascript/react-components/src/vue-components/registerWebComponents.js b/app/javascript/react-components/src/vue-components/registerWebComponents.js new file mode 100644 index 000000000..7d6837966 --- /dev/null +++ b/app/javascript/react-components/src/vue-components/registerWebComponents.js @@ -0,0 +1,18 @@ +import { defineCustomElement } from 'vue'; +import VueHelloWorld from './VueHelloWorld.vue'; + +// Convert Vue component to Web Component +const VueHelloWorldElement = defineCustomElement(VueHelloWorld); + +// Register Web Components +export const registerVueWebComponents = () => { + // Check if already registered to prevent errors + if (!customElements.get('vue-hello-world')) { + customElements.define('vue-hello-world', VueHelloWorldElement); + // eslint-disable-next-line no-console + console.log('✅ Registered vue-hello-world Web Component'); + } +}; + +// Export for manual registration if needed +export { VueHelloWorldElement };