@queuezero/embed 0.1.6 → 0.1.8

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.
Files changed (2) hide show
  1. package/dist/index.global.js +224 -26
  2. package/package.json +1 -1
@@ -1,12 +1,29 @@
1
- var QueueZero=(()=>{var d=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var y=(i,t,e)=>t in i?d(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var q=(i,t)=>{for(var e in t)d(i,e,{get:t[e],enumerable:!0})},z=(i,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of f(t))!b.call(i,n)&&n!==e&&d(i,n,{get:()=>t[n],enumerable:!(r=h(t,n))||r.enumerable});return i};var v=i=>z(d({},"__esModule",{value:!0}),i);var s=(i,t,e)=>y(i,typeof t!="symbol"?t+"":t,e);var E={};q(E,{init:()=>w});var x=`
1
+ var QueueZero=(()=>{var h=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var z=(a,e,t)=>e in a?h(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var q=(a,e)=>{for(var t in e)h(a,t,{get:e[t],enumerable:!0})},x=(a,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of y(e))!v.call(a,i)&&i!==t&&h(a,i,{get:()=>e[i],enumerable:!(o=b(e,i))||o.enumerable});return a};var $=a=>x(h({},"__esModule",{value:!0}),a);var l=(a,e,t)=>z(a,typeof e!="symbol"?e+"":e,t);var C={};q(C,{init:()=>k});var w=`
2
+ /* Base Form Styles */
2
3
  .qz-form {
3
- font-family: system-ui, -apple-system, sans-serif;
4
- max-width: 400px;
5
- margin: 0 auto;
4
+ font-family: var(--qz-font-family, system-ui, -apple-system, sans-serif);
5
+ max-width: 100%;
6
6
  }
7
7
  .qz-form-group {
8
8
  margin-bottom: 16px;
9
9
  }
10
+ .qz-checkbox-group {
11
+ display: flex;
12
+ align-items: center;
13
+ }
14
+ .qz-checkbox-label {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: 8px;
18
+ cursor: pointer;
19
+ color: #e0e0e0;
20
+ font-size: 14px;
21
+ }
22
+ .qz-checkbox {
23
+ width: 18px;
24
+ height: 18px;
25
+ accent-color: var(--qz-theme-color, #10B981);
26
+ }
10
27
  .qz-label {
11
28
  display: block;
12
29
  margin-bottom: 6px;
@@ -14,7 +31,7 @@ var QueueZero=(()=>{var d=Object.defineProperty;var h=Object.getOwnPropertyDescr
14
31
  font-weight: 500;
15
32
  color: #e0e0e0;
16
33
  }
17
- .qz-input {
34
+ .qz-input, .qz-select, .qz-textarea {
18
35
  width: 100%;
19
36
  padding: 12px 16px;
20
37
  font-size: 16px;
@@ -25,11 +42,15 @@ var QueueZero=(()=>{var d=Object.defineProperty;var h=Object.getOwnPropertyDescr
25
42
  box-sizing: border-box;
26
43
  transition: border-color 0.15s;
27
44
  }
28
- .qz-input:focus {
45
+ .qz-textarea {
46
+ resize: vertical;
47
+ min-height: 80px;
48
+ }
49
+ .qz-input:focus, .qz-select:focus, .qz-textarea:focus {
29
50
  outline: none;
30
51
  border-color: var(--qz-theme-color, #10B981);
31
52
  }
32
- .qz-input::placeholder {
53
+ .qz-input::placeholder, .qz-textarea::placeholder {
33
54
  color: #6c7086;
34
55
  }
35
56
  .qz-button {
@@ -42,11 +63,14 @@ var QueueZero=(()=>{var d=Object.defineProperty;var h=Object.getOwnPropertyDescr
42
63
  border: none;
43
64
  border-radius: 8px;
44
65
  cursor: pointer;
45
- transition: opacity 0.15s;
66
+ transition: opacity 0.15s, transform 0.1s;
46
67
  }
47
68
  .qz-button:hover {
48
69
  opacity: 0.9;
49
70
  }
71
+ .qz-button:active {
72
+ transform: scale(0.98);
73
+ }
50
74
  .qz-button:disabled {
51
75
  opacity: 0.5;
52
76
  cursor: not-allowed;
@@ -79,30 +103,204 @@ var QueueZero=(()=>{var d=Object.defineProperty;var h=Object.getOwnPropertyDescr
79
103
  padding: 24px;
80
104
  color: #6c7086;
81
105
  }
82
- `,m=class{constructor(t,e,r){s(this,"container");s(this,"campaign");s(this,"options");s(this,"configData",null);s(this,"submitted",!1);s(this,"submittedData",null);let n=document.getElementById(t);if(!n){console.error(`QueueZero: Element with ID "${t}" not found.`),this.container=document.createElement("div");return}this.container=n,this.campaign=e,this.options=r,this.injectStyles(),this.render(),this.loadCampaign()}injectStyles(){if(document.getElementById("qz-styles"))return;let t=document.createElement("style");t.id="qz-styles",t.textContent=x,document.head.appendChild(t)}render(){this.container.innerHTML='<div class="qz-loading">Loading waitlist...</div>'}async loadCampaign(){try{let t=await fetch(`${this.options.apiUrl}/v1/config/${this.campaign}`);if(!t.ok)throw new Error(`Waitlist not found (${t.status})`);this.configData=await t.json(),this.renderForm()}catch(t){this.container.innerHTML=`<div class="qz-error">Failed to load waitlist: ${t.message}</div>`}}renderForm(){if(!this.configData)return;let t=this.configData.branding?.themeColor||"#10B981";this.container.style.setProperty("--qz-theme-color",t);let e=(this.configData.formFields||[]).filter(o=>o.enabled);e.length===0&&e.push({key:"email",label:"Email Address",type:"email",enabled:!0,required:!0});let r=e.map(o=>`
83
- <div class="qz-form-group">
84
- <label class="qz-label" for="qz-${o.key}">${o.label}${o.required?" *":""}</label>
85
- <input
86
- class="qz-input"
87
- id="qz-${o.key}"
88
- name="${o.key}"
89
- type="${o.type==="email"?"email":"text"}"
90
- placeholder="${o.placeholder||""}"
91
- ${o.required?"required":""}
92
- />
106
+
107
+ /* Trigger Button */
108
+ .qz-trigger {
109
+ display: inline-flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ padding: 14px 28px;
113
+ font-size: 16px;
114
+ font-weight: 600;
115
+ color: #fff;
116
+ background: var(--qz-theme-color, #10B981);
117
+ border: none;
118
+ border-radius: 8px;
119
+ cursor: pointer;
120
+ transition: opacity 0.15s, transform 0.1s;
121
+ }
122
+ .qz-trigger:hover {
123
+ opacity: 0.9;
124
+ }
125
+ .qz-trigger:active {
126
+ transform: scale(0.98);
127
+ }
128
+
129
+ /* Modal Overlay */
130
+ .qz-modal-overlay {
131
+ position: fixed;
132
+ inset: 0;
133
+ background: rgba(0, 0, 0, 0.6);
134
+ backdrop-filter: blur(4px);
135
+ display: flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ z-index: 10000;
139
+ opacity: 0;
140
+ visibility: hidden;
141
+ transition: opacity 0.2s, visibility 0.2s;
142
+ }
143
+ .qz-modal-overlay.qz-modal-open {
144
+ opacity: 1;
145
+ visibility: visible;
146
+ }
147
+
148
+ /* Modal Container */
149
+ .qz-modal {
150
+ background: var(--qz-bg-color, #1e1e2e);
151
+ border-radius: 16px;
152
+ max-height: 90vh;
153
+ overflow: hidden;
154
+ display: flex;
155
+ flex-direction: column;
156
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
157
+ transform: scale(0.95) translateY(10px);
158
+ transition: transform 0.2s;
159
+ }
160
+ .qz-modal-overlay.qz-modal-open .qz-modal {
161
+ transform: scale(1) translateY(0);
162
+ }
163
+ .qz-modal-sm { width: 360px; }
164
+ .qz-modal-md { width: 440px; }
165
+ .qz-modal-lg { width: 520px; }
166
+
167
+ /* Modal Header */
168
+ .qz-modal-header {
169
+ position: relative;
170
+ display: flex;
171
+ align-items: center;
172
+ justify-content: space-between;
173
+ padding: 16px 20px;
174
+ border-bottom: 1px solid #3a3a4a;
175
+ }
176
+ .qz-modal-logo {
177
+ height: 32px;
178
+ width: auto;
179
+ }
180
+ .qz-modal-close {
181
+ background: none;
182
+ border: none;
183
+ color: #6c7086;
184
+ cursor: pointer;
185
+ padding: 8px;
186
+ border-radius: 8px;
187
+ transition: background 0.15s, color 0.15s;
188
+ display: flex;
189
+ align-items: center;
190
+ justify-content: center;
191
+ }
192
+ .qz-modal-close:hover {
193
+ background: rgba(255, 255, 255, 0.1);
194
+ color: #fff;
195
+ }
196
+ .qz-modal-close svg {
197
+ width: 20px;
198
+ height: 20px;
199
+ }
200
+
201
+ /* Modal Cover Image */
202
+ .qz-modal-cover {
203
+ width: 100%;
204
+ max-height: 160px;
205
+ object-fit: cover;
206
+ }
207
+
208
+ /* Modal Body */
209
+ .qz-modal-body {
210
+ padding: 24px;
211
+ overflow-y: auto;
212
+ }
213
+ .qz-modal-title {
214
+ font-size: 20px;
215
+ font-weight: 700;
216
+ color: #fff;
217
+ margin: 0 0 20px 0;
218
+ text-align: center;
219
+ }
220
+ `,u=class{constructor(e,t,o){l(this,"container");l(this,"campaign","");l(this,"options",{apiUrl:""});l(this,"configData",null);l(this,"submitted",!1);l(this,"submittedData",null);l(this,"modalElement",null);l(this,"modalOverlay",null);let i=document.getElementById(e);if(!i){console.error(`QueueZero: Element with ID "${e}" not found.`),this.container=document.createElement("div");return}this.container=i,this.campaign=t,this.options=o,this.injectStyles(),this.render(),this.loadCampaign()}injectStyles(){if(document.getElementById("qz-styles"))return;let e=document.createElement("style");e.id="qz-styles",e.textContent=w,document.head.appendChild(e)}render(){this.container.innerHTML='<div class="qz-loading">Loading waitlist...</div>'}async loadCampaign(){try{let e=await fetch(`${this.options.apiUrl}/v1/config/${this.campaign}`);if(!e.ok)throw new Error(`Waitlist not found (${e.status})`);this.configData=await e.json(),this.applyBranding(),this.options.displayMode==="modal"?this.renderTrigger():this.renderInlineForm()}catch(e){this.container.innerHTML=`<div class="qz-error">Failed to load waitlist: ${e.message}</div>`}}applyBranding(){if(!this.configData)return;let e=this.configData.branding||{},t=document.documentElement;e.themeColor&&(t.style.setProperty("--qz-theme-color",e.themeColor),this.container.style.setProperty("--qz-theme-color",e.themeColor)),e.backgroundColor&&t.style.setProperty("--qz-bg-color",e.backgroundColor),e.fontFamily&&t.style.setProperty("--qz-font-family",e.fontFamily)}renderTrigger(){if(!this.configData)return;let e=this.options.modalOptions||{},t=e.triggerText||"Join Waitlist",o=e.triggerClass||"";this.container.innerHTML=`
221
+ <button class="qz-trigger ${o}" id="qz-trigger-${this.campaign}">
222
+ ${t}
223
+ </button>
224
+ `,this.container.querySelector(".qz-trigger")?.addEventListener("click",()=>this.openModal()),this.createModalDOM()}createModalDOM(){if(!this.configData)return;let e=this.configData.branding||{},o=`qz-modal-${(this.options.modalOptions||{}).size||"md"}`;this.modalOverlay=document.createElement("div"),this.modalOverlay.className="qz-modal-overlay",this.modalOverlay.id=`qz-modal-overlay-${this.campaign}`;let i="";e.logoUrl?i+=`<img src="${e.logoUrl}" alt="Logo" class="qz-modal-logo" />`:i+="<span></span>",i+=`
225
+ <button class="qz-modal-close" aria-label="Close">
226
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
227
+ <path d="M18 6L6 18M6 6l12 12" />
228
+ </svg>
229
+ </button>
230
+ `;let d="";e.coverImageUrl&&(d=`<img src="${e.coverImageUrl}" alt="" class="qz-modal-cover" />`),this.modalOverlay.innerHTML=`
231
+ <div class="qz-modal ${o}">
232
+ <div class="qz-modal-header">
233
+ ${i}
234
+ </div>
235
+ ${d}
236
+ <div class="qz-modal-body">
237
+ <h2 class="qz-modal-title">${this.configData.name}</h2>
238
+ <div id="qz-modal-form-container-${this.campaign}"></div>
239
+ </div>
93
240
  </div>
94
- `).join("");this.container.innerHTML=`
241
+ `,document.body.appendChild(this.modalOverlay),this.modalOverlay.querySelector(".qz-modal-close")?.addEventListener("click",()=>this.closeModal()),this.modalOverlay.addEventListener("click",r=>{r.target===this.modalOverlay&&this.closeModal()}),document.addEventListener("keydown",r=>{r.key==="Escape"&&this.modalOverlay?.classList.contains("qz-modal-open")&&this.closeModal()})}openModal(){if(!this.modalOverlay)return;let e=document.getElementById(`qz-modal-form-container-${this.campaign}`);e&&!e.hasChildNodes()&&this.renderFormInto(e),this.modalOverlay.classList.add("qz-modal-open"),document.body.style.overflow="hidden"}closeModal(){this.modalOverlay?.classList.remove("qz-modal-open"),document.body.style.overflow=""}renderInlineForm(){this.renderFormInto(this.container)}renderFormInto(e){if(!this.configData)return;let t=this.configData.branding?.themeColor||"#10B981";e.style.setProperty("--qz-theme-color",t);let o=(this.configData.formFields||[]).filter(r=>r.enabled);o.length===0&&o.push({key:"email",label:"Email Address",type:"email",enabled:!0,required:!0});let i=r=>{let c=`qz-${r.key}`,p=`<label class="qz-label" for="${c}">${r.label}${r.required?" *":""}</label>`,n=r.required?"required":"",s=r.placeholder||"";switch(r.type){case"select":let m=(r.options||[]).map(f=>`<option value="${f}">${f}</option>`).join("");return`
242
+ <div class="qz-form-group">
243
+ ${p}
244
+ <select class="qz-input qz-select" id="${c}" name="${r.key}" ${n}>
245
+ <option value="">${s||`Select ${r.label}`}</option>
246
+ ${m}
247
+ </select>
248
+ </div>
249
+ `;case"checkbox":return`
250
+ <div class="qz-form-group qz-checkbox-group">
251
+ <label class="qz-checkbox-label">
252
+ <input type="checkbox" class="qz-checkbox" id="${c}" name="${r.key}" ${n} />
253
+ <span>${r.label}${r.required?" *":""}</span>
254
+ </label>
255
+ </div>
256
+ `;case"textarea":return`
257
+ <div class="qz-form-group">
258
+ ${p}
259
+ <textarea
260
+ class="qz-input qz-textarea"
261
+ id="${c}"
262
+ name="${r.key}"
263
+ placeholder="${s}"
264
+ rows="3"
265
+ ${n}
266
+ ></textarea>
267
+ </div>
268
+ `;case"number":return`
269
+ <div class="qz-form-group">
270
+ ${p}
271
+ <input
272
+ class="qz-input"
273
+ id="${c}"
274
+ name="${r.key}"
275
+ type="number"
276
+ placeholder="${s}"
277
+ ${n}
278
+ />
279
+ </div>
280
+ `;default:return`
281
+ <div class="qz-form-group">
282
+ ${p}
283
+ <input
284
+ class="qz-input"
285
+ id="${c}"
286
+ name="${r.key}"
287
+ type="${r.type==="email"?"email":"text"}"
288
+ placeholder="${s}"
289
+ ${n}
290
+ />
291
+ </div>
292
+ `}},d=o.map(i).join("");e.innerHTML=`
95
293
  <form class="qz-form" id="qz-form-${this.campaign}">
96
- ${r}
294
+ ${d}
97
295
  <button type="submit" class="qz-button">Join Waitlist</button>
98
296
  </form>
99
- `,this.container.querySelector("form")?.addEventListener("submit",o=>this.handleSubmit(o))}async handleSubmit(t){t.preventDefault();let e=t.target,r=e.querySelector("button"),n=new FormData(e),o={},p="";if(n.forEach((a,l)=>{let c=a.toString();l==="email"&&(p=c),o[l]=c}),!p){this.showError(e,"Email is required");return}let u=new URLSearchParams(window.location.search).get("ref"),g={campaign:this.campaign,email:p,metadata:o,referrer_code:u||null};r&&(r.disabled=!0,r.textContent="Joining...");try{let a=await fetch(`${this.options.apiUrl}/v1/submit`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(g)});if(!a.ok){let c=await a.json();throw new Error(c.message||c.error||"Failed to join")}let l=await a.json();this.submitted=!0,this.submittedData=l,this.renderSuccess(),this.options.onSuccess?.(l)}catch(a){this.showError(e,a.message||"Something went wrong"),r&&(r.disabled=!1,r.textContent="Join Waitlist"),this.options.onError?.(a)}}showError(t,e){let r=t.querySelector(".qz-error");r&&r.remove();let n=document.createElement("div");n.className="qz-error",n.textContent=e,t.insertBefore(n,t.firstChild)}renderSuccess(){let t=this.submittedData?.position||"?",e=this.submittedData?.referralLink,r=`
297
+ `,e.querySelector("form")?.addEventListener("submit",r=>this.handleSubmit(r))}async handleSubmit(e){e.preventDefault();let t=e.target,o=t.querySelector("button"),i=new FormData(t),d={},g="";if(i.forEach((n,s)=>{let m=n.toString();s==="email"&&(g=m),d[s]=m}),!g){this.showError(t,"Email is required");return}let c=new URLSearchParams(window.location.search).get("ref"),p={campaign:this.campaign,email:g,metadata:d,referrer_code:c||null};o&&(o.disabled=!0,o.textContent="Joining...");try{let n=await fetch(`${this.options.apiUrl}/v1/submit`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(p)});if(!n.ok){let m=await n.json();throw new Error(m.message||m.error||"Failed to join")}let s=await n.json();this.submitted=!0,this.submittedData=s,this.renderSuccess(),this.options.onSuccess?.(s)}catch(n){this.showError(t,n.message||"Something went wrong"),o&&(o.disabled=!1,o.textContent="Join Waitlist"),this.options.onError?.(n)}}showError(e,t){let o=e.querySelector(".qz-error");o&&o.remove();let i=document.createElement("div");i.className="qz-error",i.textContent=t,e.insertBefore(i,e.firstChild)}renderSuccess(){let e=this.submittedData?.position||"?",t=this.submittedData?.referralLink,o=`
100
298
  <div class="qz-success">
101
299
  <h3>You're on the list!</h3>
102
- <p>You're #${t} in line.</p>
103
- `;e&&(r+=`
300
+ <p>You're #${e} in line.</p>
301
+ `;t&&(o+=`
104
302
  <div style="margin-top: 20px;">
105
303
  <p style="margin-bottom: 8px; color: #a6e3a1;">Refer friends to move up:</p>
106
- <input class="qz-input" readonly value="${e}" onclick="this.select()" style="text-align: center; cursor: pointer;">
304
+ <input class="qz-input" readonly value="${t}" onclick="this.select()" style="text-align: center; cursor: pointer;">
107
305
  </div>
108
- `),r+="</div>",this.container.innerHTML=r}};function w(i,t,e){return new m(i,t,e)}return v(E);})();
306
+ `),o+="</div>",this.container.innerHTML=o}};function k(a,e,t){return new u(a,e,t)}return $(C);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@queuezero/embed",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Embeddable vanilla JS widget for QueueZero viral waitlists",
5
5
  "main": "./dist/index.global.js",
6
6
  "browser": "./dist/index.global.js",