@uservibesos/web-component 1.0.0 → 1.0.1

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.
@@ -0,0 +1,27 @@
1
+ export interface InitConfig {
2
+ projectId: string;
3
+ jwt?: string;
4
+ container: string | HTMLElement;
5
+ theme?: string;
6
+ height?: string;
7
+ mode?: string;
8
+ baseUrl?: string;
9
+ }
10
+
11
+ export interface WidgetInstance {
12
+ element: HTMLElement;
13
+ setJWT: (jwt: string) => void;
14
+ destroy: () => void;
15
+ }
16
+
17
+ export declare function init(config: InitConfig): WidgetInstance | null;
18
+
19
+ export declare class UserVibesWidget extends HTMLElement {
20
+ setJWT(jwt: string): void;
21
+ }
22
+
23
+ export declare const UserVibesOS: {
24
+ init: typeof init;
25
+ };
26
+
27
+ export default UserVibesOS;
package/dist/widget.js CHANGED
@@ -1,15 +1,102 @@
1
- "use strict";(()=>{var i=class extends HTMLElement{constructor(){super();this.iframe=null;this.baseUrl="https://app.uservibesos.com";this.attachShadow({mode:"open"})}static get observedAttributes(){return["project","theme","height","mode","base-url","api-key","jwt-mode","proxy-url"]}connectedCallback(){this.render()}attributeChangedCallback(e,t,o){t!==o&&this.render()}render(){let e=this.getAttribute("project"),t=this.getAttribute("theme")||"light",o=this.getAttribute("height")||"600px",s=this.getAttribute("mode")||"feature",d=this.getAttribute("base-url"),n=this.getAttribute("api-key"),l=this.getAttribute("jwt-mode")==="true",a=this.getAttribute("proxy-url");if(!e){this.renderError('Missing required "project" attribute');return}d?this.baseUrl=d:typeof window<"u"&&window.location.hostname==="localhost"&&(this.baseUrl=window.location.origin);let r=`${this.baseUrl}/widget/${e}`;if(!n&&!l){let h="/api/uservibes-proxy",u=typeof window<"u"?`${window.location.origin}${h}`:h;r+=`?tier=2&proxyUrl=${encodeURIComponent(u)}&mode=${s}`,console.log("[Web Component] TIER 2 auto-detected (no api-key) - using proxy:",u)}else if(l&&a)r+=`?tier=2&proxyUrl=${encodeURIComponent(a)}&mode=${s}`,console.log("[Web Component] TIER 2 legacy mode - using custom proxy:",a);else if(n)r+=`?publicKey=${encodeURIComponent(n)}&mode=${s}`,console.log("[Web Component] TIER 1 mode - using public key");else{this.renderError('Widget requires either an "api-key" attribute for Tier 1 security, or no api-key for Tier 2 security (proxy mode). For Tier 2, ensure you have implemented the proxy at /api/uservibes-proxy.');return}let p=`
1
+ function f(o){if(typeof document>"u"||document.querySelector(`link[href="${o}"]`))return;let s=document.createElement("link");s.rel="preconnect",s.href=o,document.head.appendChild(s)}var c=class extends HTMLElement{constructor(){super();this.baseUrl="https://app.uservibesos.com";this.iframe=null;this.skeleton=null;this.loaded=!1;this.attachShadow({mode:"open"})}static get observedAttributes(){return["project","jwt","theme","height","mode","base-url"]}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}render(){let e=this.getAttribute("project"),t=this.getAttribute("jwt"),r=this.getAttribute("theme")||"light",d=this.getAttribute("height")||"600px",l=this.getAttribute("mode")||"feature-request",a=this.getAttribute("base-url");if(!e){this.renderError('Missing required "project" attribute (project ID)');return}a?this.baseUrl=a:typeof window<"u"&&window.location.hostname==="localhost"&&(this.baseUrl=window.location.origin),f(this.baseUrl);let n=`${this.baseUrl}/embed?projectId=${encodeURIComponent(e)}&mode=${l}`;t&&(n+=`&jwt=${encodeURIComponent(t)}`);let i=`
2
2
  <style>
3
3
  :host {
4
4
  display: block;
5
5
  width: 100%;
6
6
  }
7
+ .widget-container {
8
+ position: relative;
9
+ min-height: ${d};
10
+ }
7
11
  iframe {
8
12
  width: 100%;
9
- height: ${o};
13
+ height: ${d};
10
14
  border: none;
11
15
  border-radius: 8px;
12
16
  overflow: hidden;
17
+ opacity: 0;
18
+ transition: opacity 0.2s ease-in-out;
19
+ }
20
+ iframe.loaded {
21
+ opacity: 1;
22
+ }
23
+ .skeleton {
24
+ position: absolute;
25
+ top: 0;
26
+ left: 0;
27
+ right: 0;
28
+ padding: 1.5rem;
29
+ background: #f9fafb;
30
+ border-radius: 8px;
31
+ font-family: system-ui, sans-serif;
32
+ }
33
+ .skeleton.hidden {
34
+ display: none;
35
+ }
36
+ .skeleton-header {
37
+ display: flex;
38
+ justify-content: space-between;
39
+ align-items: center;
40
+ margin-bottom: 1.5rem;
41
+ padding-bottom: 1rem;
42
+ border-bottom: 1px solid #e5e7eb;
43
+ }
44
+ .skeleton-title {
45
+ height: 1.5rem;
46
+ width: 180px;
47
+ background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
48
+ background-size: 200% 100%;
49
+ animation: shimmer 1.5s infinite;
50
+ border-radius: 4px;
51
+ }
52
+ .skeleton-button {
53
+ height: 2.25rem;
54
+ width: 100px;
55
+ background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
56
+ background-size: 200% 100%;
57
+ animation: shimmer 1.5s infinite;
58
+ border-radius: 6px;
59
+ }
60
+ .skeleton-tabs {
61
+ display: flex;
62
+ gap: 0.5rem;
63
+ margin-bottom: 1rem;
64
+ }
65
+ .skeleton-tab {
66
+ height: 2rem;
67
+ width: 60px;
68
+ background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
69
+ background-size: 200% 100%;
70
+ animation: shimmer 1.5s infinite;
71
+ border-radius: 4px;
72
+ }
73
+ .skeleton-item {
74
+ padding: 1rem;
75
+ margin-bottom: 0.75rem;
76
+ background: white;
77
+ border: 1px solid #e5e7eb;
78
+ border-radius: 8px;
79
+ }
80
+ .skeleton-item-title {
81
+ height: 1rem;
82
+ width: 70%;
83
+ background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
84
+ background-size: 200% 100%;
85
+ animation: shimmer 1.5s infinite;
86
+ border-radius: 4px;
87
+ margin-bottom: 0.5rem;
88
+ }
89
+ .skeleton-item-desc {
90
+ height: 0.75rem;
91
+ width: 90%;
92
+ background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
93
+ background-size: 200% 100%;
94
+ animation: shimmer 1.5s infinite;
95
+ border-radius: 4px;
96
+ }
97
+ @keyframes shimmer {
98
+ 0% { background-position: 200% 0; }
99
+ 100% { background-position: -200% 0; }
13
100
  }
14
101
  .error {
15
102
  padding: 2rem;
@@ -18,16 +105,44 @@
18
105
  border-radius: 8px;
19
106
  background: #fef2f2;
20
107
  color: #991b1b;
108
+ font-family: system-ui, sans-serif;
21
109
  }
22
110
  </style>
23
- `,c=`
24
- <iframe
25
- src="${r}"
26
- title="${e} Feature Requests"
27
- loading="lazy"
28
- allow="clipboard-write"
29
- ></iframe>
30
- `;this.shadowRoot&&(this.shadowRoot.innerHTML=p+c,this.iframe=this.shadowRoot.querySelector("iframe"),this.setupMessageListener())}renderError(e){let t=`
111
+ `,u=`
112
+ <div class="widget-container">
113
+
114
+ <div class="skeleton" id="skeleton">
115
+ <div class="skeleton-header">
116
+ <div class="skeleton-title"></div>
117
+ <div class="skeleton-button"></div>
118
+ </div>
119
+ <div class="skeleton-tabs">
120
+ <div class="skeleton-tab"></div>
121
+ <div class="skeleton-tab"></div>
122
+ <div class="skeleton-tab"></div>
123
+ </div>
124
+ <div class="skeleton-item">
125
+ <div class="skeleton-item-title"></div>
126
+ <div class="skeleton-item-desc"></div>
127
+ </div>
128
+ <div class="skeleton-item">
129
+ <div class="skeleton-item-title"></div>
130
+ <div class="skeleton-item-desc"></div>
131
+ </div>
132
+ <div class="skeleton-item">
133
+ <div class="skeleton-item-title"></div>
134
+ <div class="skeleton-item-desc"></div>
135
+ </div>
136
+ </div>
137
+
138
+ <iframe
139
+ src="${n}"
140
+ title="Feature Requests"
141
+ loading="lazy"
142
+ allow="clipboard-write"
143
+ ></iframe>
144
+ </div>
145
+ `;this.shadowRoot&&(this.shadowRoot.innerHTML=i+u,this.iframe=this.shadowRoot.querySelector("iframe"),this.skeleton=this.shadowRoot.querySelector("#skeleton"),this.setupLoadHandler(),this.setupMessageListener())}setupLoadHandler(){this.iframe&&(this.iframe.addEventListener("load",()=>{this.loaded=!0,this.iframe.classList.add("loaded"),this.skeleton&&this.skeleton.classList.add("hidden"),this.iframe.contentWindow&&this.iframe.contentWindow.postMessage({type:"USERVIBES_INIT"},this.baseUrl)}),this.iframe.addEventListener("error",()=>{this.renderError("Failed to load widget. Please try again later.")}),setTimeout(()=>{!this.loaded&&this.iframe&&(this.iframe.classList.add("loaded"),this.skeleton&&this.skeleton.classList.add("hidden"))},1e4))}renderError(e){let r=`
31
146
  <style>
32
147
  .error {
33
148
  padding: 2rem;
@@ -36,9 +151,10 @@
36
151
  border-radius: 8px;
37
152
  background: #fef2f2;
38
153
  color: #991b1b;
154
+ font-family: system-ui, sans-serif;
39
155
  }
40
156
  </style>
41
157
  <div class="error">
42
- <strong>Widget Error:</strong> ${e}
158
+ <strong>Widget Error:</strong> ${this.escapeHtml(e)}
43
159
  </div>
44
- `;this.shadowRoot&&(this.shadowRoot.innerHTML=t)}setupMessageListener(){window.addEventListener("message",e=>{e.origin===this.baseUrl&&(e.data.type==="USERVIBES_HEIGHT_UPDATE"&&this.iframe&&(this.iframe.style.height=`${e.data.height}px`),e.data.type==="USERVIBES_REQUEST_SUBMITTED"&&this.dispatchEvent(new CustomEvent("request-submitted",{detail:e.data.payload})),e.data.type==="USERVIBES_VOTE_ADDED"&&this.dispatchEvent(new CustomEvent("vote-added",{detail:e.data.payload})))})}};typeof window<"u"&&!customElements.get("uservibes-widget")&&customElements.define("uservibes-widget",i);var b=i;})();
160
+ `;this.shadowRoot&&(this.shadowRoot.innerHTML=r)}escapeHtml(e){if(!e||typeof e!="string")return"";let t=document.createElement("div");return t.textContent=e,t.innerHTML}setupMessageListener(){window.addEventListener("message",e=>{e.origin===this.baseUrl&&(e.data.type==="USERVIBES_HEIGHT_UPDATE"&&this.iframe&&(this.iframe.style.height=`${e.data.height}px`),e.data.type==="USERVIBES_REQUEST_SUBMITTED"&&this.dispatchEvent(new CustomEvent("request-submitted",{detail:e.data.payload})),e.data.type==="USERVIBES_VOTE_ADDED"&&this.dispatchEvent(new CustomEvent("vote-added",{detail:e.data.payload})))})}setJWT(e){this.setAttribute("jwt",e)}};typeof window<"u"&&typeof customElements<"u"&&!customElements.get("uservibes-widget")&&customElements.define("uservibes-widget",c);function b(o){let{projectId:s,jwt:e,container:t,theme:r,height:d,mode:l,baseUrl:a}=o;if(!s)return console.error('[UserVibesOS] Missing required "projectId" in config'),null;if(!t)return console.error('[UserVibesOS] Missing required "container" in config'),null;let n=typeof t=="string"?document.querySelector(t):t;if(!n)return console.error(`[UserVibesOS] Container "${t}" not found`),null;let i=document.createElement("uservibes-widget");return i.setAttribute("project",s),e&&i.setAttribute("jwt",e),r&&i.setAttribute("theme",r),d&&i.setAttribute("height",d),l&&i.setAttribute("mode",l),a&&i.setAttribute("base-url",a),n.innerHTML="",n.appendChild(i),{element:i,setJWT:m=>i.setJWT(m),destroy:()=>n.removeChild(i)}}var h={init:b};var g=h;typeof window<"u"&&(window.UserVibesOS=h);export{h as UserVibesOS,c as UserVibesWidget,g as default,b as init};
package/package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "name": "@uservibesos/web-component",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Feature request widget as a Web Component",
5
+ "type": "module",
5
6
  "main": "dist/widget.js",
7
+ "module": "dist/widget.js",
8
+ "types": "dist/widget.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/widget.js",
12
+ "types": "./dist/widget.d.ts",
13
+ "default": "./dist/widget.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
6
19
  "scripts": {
7
20
  "build": "node build.js",
8
21
  "dev": "node build.js --watch"
@@ -11,7 +24,8 @@
11
24
  "feature-request",
12
25
  "web-component",
13
26
  "custom-element",
14
- "widget"
27
+ "widget",
28
+ "uservibesos"
15
29
  ],
16
30
  "author": "UserVibesOS",
17
31
  "license": "MIT",
package/build.js DELETED
@@ -1,47 +0,0 @@
1
- const esbuild = require('esbuild');
2
- const fs = require('fs');
3
- const path = require('path');
4
-
5
- const isWatch = process.argv.includes('--watch');
6
-
7
- const buildOptions = {
8
- entryPoints: ['src/widget.ts'],
9
- bundle: true,
10
- minify: !isWatch,
11
- sourcemap: isWatch,
12
- format: 'iife',
13
- target: ['es2020'],
14
- outfile: 'dist/widget.js',
15
- platform: 'browser',
16
- logLevel: 'info',
17
- };
18
-
19
- async function build() {
20
- try {
21
- if (isWatch) {
22
- const ctx = await esbuild.context(buildOptions);
23
- await ctx.watch();
24
- console.log('👀 Watching for changes...');
25
- } else {
26
- await esbuild.build(buildOptions);
27
-
28
- // Also copy to public directory for CDN
29
- const publicDir = path.join(__dirname, '../../public/widget-assets/v1.0.0');
30
- if (!fs.existsSync(publicDir)) {
31
- fs.mkdirSync(publicDir, { recursive: true });
32
- }
33
- fs.copyFileSync(
34
- path.join(__dirname, 'dist/widget.js'),
35
- path.join(publicDir, 'widget.js')
36
- );
37
-
38
- console.log('✅ Build complete!');
39
- console.log('📦 Copied to public/widget-assets/v1.0.0/widget.js');
40
- }
41
- } catch (error) {
42
- console.error('❌ Build failed:', error);
43
- process.exit(1);
44
- }
45
- }
46
-
47
- build();
package/src/widget.ts DELETED
@@ -1,244 +0,0 @@
1
- /**
2
- * UserVibesOS Feature Request Widget - Web Component
3
- *
4
- * A custom element that embeds authenticated feature request widgets.
5
- *
6
- * TWO-TIER SECURITY MODEL:
7
- *
8
- * TIER 1: Public Keys (PK) - Recommended for most users
9
- * - Origin enforcement + Rate limiting + Fingerprinting
10
- * - Safe to use with server-side rendering
11
- *
12
- * TIER 2: JWT Tokens - Maximum security
13
- * - Short-lived tokens (5min) + Backend proxy required
14
- * - One-time use + Request signing
15
- * - Auto-detected when no api-key is present
16
- *
17
- * ========================================
18
- * TIER 1 USAGE (Public Keys - Recommended)
19
- * ========================================
20
- *
21
- * Step 1: Create Public Key (PK) in UserVibesOS dashboard with allowed origins
22
- * Step 2: Add to .env.local
23
- * USERVIBES_PUBLIC_KEY=pk_live_your_public_key_here
24
- *
25
- * Step 3: Use in server component
26
- * <script src="https://app.uservibesos.com/widget-assets/v1.0.0/widget.js"></script>
27
- * <uservibes-widget
28
- * project="my-project-slug"
29
- * api-key={process.env.USERVIBES_PUBLIC_KEY}
30
- * ></uservibes-widget>
31
- *
32
- * ========================================
33
- * TIER 2 USAGE (Maximum Security - Auto-detected)
34
- * ========================================
35
- *
36
- * Step 1: Enable Tier 2 mode in project settings
37
- * Step 2: Implement backend proxy at /api/uservibes-proxy
38
- * Step 3: Use widget WITHOUT api-key (auto-detects Tier 2)
39
- *
40
- * <script src="https://app.uservibesos.com/widget-assets/v1.0.0/widget.js"></script>
41
- * <uservibes-widget
42
- * project="my-project-slug"
43
- * ></uservibes-widget>
44
- *
45
- * The widget automatically detects Tier 2 mode when:
46
- * - No api-key attribute is present
47
- * - Makes requests to /api/uservibes-proxy (standard path)
48
- *
49
- * Attributes:
50
- * - project (required): Your project slug
51
- * - api-key (Tier 1 only): Public Key from environment variables
52
- * - mode (optional): "feature" or "roadmap" (default: "feature")
53
- * - theme (optional): "light" or "dark" (default: "light")
54
- * - height (optional): Height of the widget (default: "600px")
55
- * - base-url (optional): Custom base URL (for development/self-hosting)
56
- *
57
- * DEPRECATED (for backward compatibility only):
58
- * - jwt-mode: No longer needed, auto-detected
59
- * - proxy-url: Uses standard /api/uservibes-proxy path
60
- *
61
- * SECURITY:
62
- * - TIER 1: Store Public Key in environment variables
63
- * - TIER 2: No keys in client code, all handled by proxy
64
- * - NEVER commit .env files to version control
65
- */
66
-
67
- class UserVibesWidget extends HTMLElement {
68
- private iframe: HTMLIFrameElement | null = null;
69
- private baseUrl: string = 'https://app.uservibesos.com';
70
-
71
- constructor() {
72
- super();
73
-
74
- // Use Shadow DOM for style isolation
75
- this.attachShadow({ mode: 'open' });
76
- }
77
-
78
- static get observedAttributes() {
79
- return ['project', 'theme', 'height', 'mode', 'base-url', 'api-key', 'jwt-mode', 'proxy-url'];
80
- }
81
-
82
- connectedCallback() {
83
- this.render();
84
- }
85
-
86
- attributeChangedCallback(name: string, oldValue: string, newValue: string) {
87
- if (oldValue !== newValue) {
88
- this.render();
89
- }
90
- }
91
-
92
- private render() {
93
- const project = this.getAttribute('project');
94
- const theme = this.getAttribute('theme') || 'light';
95
- const height = this.getAttribute('height') || '600px';
96
- const mode = this.getAttribute('mode') || 'feature';
97
- const customBaseUrl = this.getAttribute('base-url');
98
- const apiKey = this.getAttribute('api-key');
99
-
100
- // For backward compatibility, check legacy jwt-mode and proxy-url attributes
101
- const legacyJwtMode = this.getAttribute('jwt-mode') === 'true';
102
- const legacyProxyUrl = this.getAttribute('proxy-url');
103
-
104
- if (!project) {
105
- this.renderError('Missing required "project" attribute');
106
- return;
107
- }
108
-
109
- // Use custom base URL if provided, otherwise use local URL in development
110
- if (customBaseUrl) {
111
- this.baseUrl = customBaseUrl;
112
- } else if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
113
- this.baseUrl = window.location.origin;
114
- }
115
-
116
- // Build widget URL - ALWAYS points to UserVibesOS
117
- let widgetUrl = `${this.baseUrl}/widget/${project}`;
118
-
119
- // AUTO-DETECT SECURITY TIER
120
- if (!apiKey && !legacyJwtMode) {
121
- // TIER 2: Auto-detected - No api-key means proxy mode
122
- // Build absolute proxy URL from current page origin
123
- const standardProxyPath = '/api/uservibes-proxy';
124
- const absoluteProxyUrl = typeof window !== 'undefined'
125
- ? `${window.location.origin}${standardProxyPath}`
126
- : standardProxyPath;
127
- widgetUrl += `?tier=2&proxyUrl=${encodeURIComponent(absoluteProxyUrl)}&mode=${mode}`;
128
- console.log('[Web Component] TIER 2 auto-detected (no api-key) - using proxy:', absoluteProxyUrl);
129
- } else if (legacyJwtMode && legacyProxyUrl) {
130
- // LEGACY: Explicit JWT mode with custom proxy URL (backward compatibility)
131
- widgetUrl += `?tier=2&proxyUrl=${encodeURIComponent(legacyProxyUrl)}&mode=${mode}`;
132
- console.log('[Web Component] TIER 2 legacy mode - using custom proxy:', legacyProxyUrl);
133
- } else if (apiKey) {
134
- // TIER 1: Public key mode - pass key directly
135
- widgetUrl += `?publicKey=${encodeURIComponent(apiKey)}&mode=${mode}`;
136
- console.log('[Web Component] TIER 1 mode - using public key');
137
- } else {
138
- // No valid configuration found
139
- this.renderError('Widget requires either an "api-key" attribute for Tier 1 security, or no api-key for Tier 2 security (proxy mode). For Tier 2, ensure you have implemented the proxy at /api/uservibes-proxy.');
140
- return;
141
- }
142
-
143
- const styles = `
144
- <style>
145
- :host {
146
- display: block;
147
- width: 100%;
148
- }
149
- iframe {
150
- width: 100%;
151
- height: ${height};
152
- border: none;
153
- border-radius: 8px;
154
- overflow: hidden;
155
- }
156
- .error {
157
- padding: 2rem;
158
- text-align: center;
159
- border: 1px solid #ef4444;
160
- border-radius: 8px;
161
- background: #fef2f2;
162
- color: #991b1b;
163
- }
164
- </style>
165
- `;
166
-
167
- const iframe = `
168
- <iframe
169
- src="${widgetUrl}"
170
- title="${project} Feature Requests"
171
- loading="lazy"
172
- allow="clipboard-write"
173
- ></iframe>
174
- `;
175
-
176
- if (this.shadowRoot) {
177
- this.shadowRoot.innerHTML = styles + iframe;
178
- this.iframe = this.shadowRoot.querySelector('iframe');
179
-
180
- // Listen for messages from iframe (for height adjustment)
181
- this.setupMessageListener();
182
- }
183
- }
184
-
185
- private renderError(message: string) {
186
- const html = `
187
- <style>
188
- .error {
189
- padding: 2rem;
190
- text-align: center;
191
- border: 1px solid #ef4444;
192
- border-radius: 8px;
193
- background: #fef2f2;
194
- color: #991b1b;
195
- }
196
- </style>
197
- <div class="error">
198
- <strong>Widget Error:</strong> ${message}
199
- </div>
200
- `;
201
-
202
- if (this.shadowRoot) {
203
- this.shadowRoot.innerHTML = html;
204
- }
205
- }
206
-
207
- private setupMessageListener() {
208
- window.addEventListener('message', (event) => {
209
- // Verify origin for security
210
- if (event.origin !== this.baseUrl) {
211
- return;
212
- }
213
-
214
- // Handle height updates from iframe
215
- if (event.data.type === 'USERVIBES_HEIGHT_UPDATE' && this.iframe) {
216
- this.iframe.style.height = `${event.data.height}px`;
217
- }
218
-
219
- // Dispatch custom events for parent page
220
- if (event.data.type === 'USERVIBES_REQUEST_SUBMITTED') {
221
- this.dispatchEvent(
222
- new CustomEvent('request-submitted', {
223
- detail: event.data.payload,
224
- })
225
- );
226
- }
227
-
228
- if (event.data.type === 'USERVIBES_VOTE_ADDED') {
229
- this.dispatchEvent(
230
- new CustomEvent('vote-added', {
231
- detail: event.data.payload,
232
- })
233
- );
234
- }
235
- });
236
- }
237
- }
238
-
239
- // Register the custom element
240
- if (typeof window !== 'undefined' && !customElements.get('uservibes-widget')) {
241
- customElements.define('uservibes-widget', UserVibesWidget);
242
- }
243
-
244
- export default UserVibesWidget;