@queuezero/embed 0.1.7 → 0.1.9
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 +23 -0
- package/dist/index.global.js +196 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,29 @@ Mounts a complete waitlist widget (form + status + referral share) to the specif
|
|
|
41
41
|
| `elementId` | `string` | ID of the container element |
|
|
42
42
|
| `campaignSlug` | `string` | Your campaign identifier |
|
|
43
43
|
| `options.apiUrl` | `string` | API endpoint URL |
|
|
44
|
+
| `options.displayMode` | `'inline' \| 'modal'` | Display as inline form or modal (default: `'inline'`) |
|
|
45
|
+
| `options.modalOptions` | `object` | Modal configuration (only when `displayMode: 'modal'`) |
|
|
46
|
+
| `options.modalOptions.triggerText` | `string` | Button text (default: `'Join Waitlist'`) |
|
|
47
|
+
| `options.modalOptions.size` | `'sm' \| 'md' \| 'lg'` | Modal width (default: `'md'`) |
|
|
48
|
+
|
|
49
|
+
### Modal Mode Example
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<div id="queuezero-waitlist"></div>
|
|
53
|
+
<script src="https://cdn.jsdelivr.net/npm/@queuezero/embed@latest/dist/index.global.js"></script>
|
|
54
|
+
<script>
|
|
55
|
+
QueueZero.init('queuezero-waitlist', 'your-campaign-slug', {
|
|
56
|
+
apiUrl: 'https://api.queuezero.io',
|
|
57
|
+
displayMode: 'modal',
|
|
58
|
+
modalOptions: {
|
|
59
|
+
triggerText: 'Join Our Waitlist',
|
|
60
|
+
size: 'md'
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
</script>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The modal automatically uses your campaign's branding (logo, cover image, theme color) from the API.
|
|
44
67
|
|
|
45
68
|
## Styling
|
|
46
69
|
|
package/dist/index.global.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
|
-
var QueueZero=(()=>{var
|
|
1
|
+
var QueueZero=(()=>{var h=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var b=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,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of b(e))!v.call(a,o)&&o!==t&&h(a,o,{get:()=>e[o],enumerable:!(r=y(e,o))||r.enumerable});return a};var $=a=>x(h({},"__esModule",{value:!0}),a);var d=(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:
|
|
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 u=Object.defineProperty;var b=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 u=Object.defineProperty;var b=Object.getOwnPropertyDescr
|
|
|
25
42
|
box-sizing: border-box;
|
|
26
43
|
transition: border-color 0.15s;
|
|
27
44
|
}
|
|
28
|
-
.qz-
|
|
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 u=Object.defineProperty;var b=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,69 +103,204 @@ var QueueZero=(()=>{var u=Object.defineProperty;var b=Object.getOwnPropertyDescr
|
|
|
79
103
|
padding: 24px;
|
|
80
104
|
color: #6c7086;
|
|
81
105
|
}
|
|
82
|
-
|
|
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,r){d(this,"container");d(this,"campaign","");d(this,"options",{apiUrl:""});d(this,"configData",null);d(this,"submitted",!1);d(this,"submittedData",null);d(this,"modalElement",null);d(this,"modalOverlay",null);let o=document.getElementById(e);if(!o){console.error(`QueueZero: Element with ID "${e}" not found.`),this.container=document.createElement("div");return}this.container=o,this.campaign=t,this.options=r,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",r=e.triggerClass||"";this.container.innerHTML=`
|
|
221
|
+
<button class="qz-trigger ${r}" 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||{},r=`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 o="";e.logoUrl?o+=`<img src="${e.logoUrl}" alt="Logo" class="qz-modal-logo" />`:o+="<span></span>",o+=`
|
|
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 s="";e.coverImageUrl&&(s=`<img src="${e.coverImageUrl}" alt="" class="qz-modal-cover" />`),this.modalOverlay.innerHTML=`
|
|
231
|
+
<div class="qz-modal ${r}">
|
|
232
|
+
<div class="qz-modal-header">
|
|
233
|
+
${o}
|
|
234
|
+
</div>
|
|
235
|
+
${s}
|
|
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>
|
|
240
|
+
</div>
|
|
241
|
+
`,document.body.appendChild(this.modalOverlay),this.modalOverlay.querySelector(".qz-modal-close")?.addEventListener("click",()=>this.closeModal()),this.modalOverlay.addEventListener("click",i=>{i.target===this.modalOverlay&&this.closeModal()}),document.addEventListener("keydown",i=>{i.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 r=(this.configData.formFields||[]).filter(i=>i.enabled);r.length===0&&r.push({key:"email",label:"Email Address",type:"email",enabled:!0,required:!0});let o=i=>{let m=`qz-${i.key}`,g=`<label class="qz-label" for="${m}">${i.label}${i.required?" *":""}</label>`,n=i.required?"required":"",l=i.placeholder||"";switch(i.type){case"select":let p=(i.options||[]).map(f=>`<option value="${f}">${f}</option>`).join("");return`
|
|
83
242
|
<div class="qz-form-group">
|
|
84
|
-
${
|
|
85
|
-
<select class="qz-input qz-select" id="${
|
|
86
|
-
|
|
87
|
-
${
|
|
243
|
+
${g}
|
|
244
|
+
<select class="qz-input qz-select" id="${m}" name="${i.key}" ${n}>
|
|
245
|
+
<option value="">Select an option</option>
|
|
246
|
+
${p}
|
|
88
247
|
</select>
|
|
89
248
|
</div>
|
|
90
249
|
`;case"checkbox":return`
|
|
91
250
|
<div class="qz-form-group qz-checkbox-group">
|
|
92
251
|
<label class="qz-checkbox-label">
|
|
93
|
-
<input type="checkbox" class="qz-checkbox" id="${
|
|
94
|
-
<span>${
|
|
252
|
+
<input type="checkbox" class="qz-checkbox" id="${m}" name="${i.key}" ${n} />
|
|
253
|
+
<span>${i.label}${i.required?" *":""}</span>
|
|
95
254
|
</label>
|
|
96
255
|
</div>
|
|
97
256
|
`;case"textarea":return`
|
|
98
257
|
<div class="qz-form-group">
|
|
99
|
-
${
|
|
258
|
+
${g}
|
|
100
259
|
<textarea
|
|
101
260
|
class="qz-input qz-textarea"
|
|
102
|
-
id="${
|
|
103
|
-
name="${
|
|
104
|
-
placeholder="${
|
|
261
|
+
id="${m}"
|
|
262
|
+
name="${i.key}"
|
|
263
|
+
placeholder="${l}"
|
|
105
264
|
rows="3"
|
|
106
|
-
${
|
|
265
|
+
${n}
|
|
107
266
|
></textarea>
|
|
108
267
|
</div>
|
|
109
268
|
`;case"number":return`
|
|
110
269
|
<div class="qz-form-group">
|
|
111
|
-
${
|
|
270
|
+
${g}
|
|
112
271
|
<input
|
|
113
272
|
class="qz-input"
|
|
114
|
-
id="${
|
|
115
|
-
name="${
|
|
273
|
+
id="${m}"
|
|
274
|
+
name="${i.key}"
|
|
116
275
|
type="number"
|
|
117
|
-
placeholder="${
|
|
118
|
-
${
|
|
276
|
+
placeholder="${l}"
|
|
277
|
+
${n}
|
|
119
278
|
/>
|
|
120
279
|
</div>
|
|
121
280
|
`;default:return`
|
|
122
281
|
<div class="qz-form-group">
|
|
123
|
-
${
|
|
282
|
+
${g}
|
|
124
283
|
<input
|
|
125
284
|
class="qz-input"
|
|
126
|
-
id="${
|
|
127
|
-
name="${
|
|
128
|
-
type="${
|
|
129
|
-
placeholder="${
|
|
130
|
-
${
|
|
285
|
+
id="${m}"
|
|
286
|
+
name="${i.key}"
|
|
287
|
+
type="${i.type==="email"?"email":"text"}"
|
|
288
|
+
placeholder="${l}"
|
|
289
|
+
${n}
|
|
131
290
|
/>
|
|
132
291
|
</div>
|
|
133
|
-
`}},
|
|
292
|
+
`}},s=r.map(o).join("");e.innerHTML=`
|
|
134
293
|
<form class="qz-form" id="qz-form-${this.campaign}">
|
|
135
|
-
${
|
|
294
|
+
${s}
|
|
136
295
|
<button type="submit" class="qz-button">Join Waitlist</button>
|
|
137
296
|
</form>
|
|
138
|
-
`,
|
|
297
|
+
`,e.querySelector("form")?.addEventListener("submit",i=>this.handleSubmit(i))}async handleSubmit(e){e.preventDefault();let t=e.target,r=t.querySelector("button"),o=new FormData(t),s={},c="";if(o.forEach((n,l)=>{let p=n.toString();l==="email"&&(c=p),s[l]=p}),!c){this.showError(t,"Email is required");return}let m=new URLSearchParams(window.location.search).get("ref"),g={campaign:this.campaign,email:c,metadata:s,referrer_code:m||null};r&&(r.disabled=!0,r.textContent="Joining...");try{let n=await fetch(`${this.options.apiUrl}/v1/submit`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(g)});if(!n.ok){let p=await n.json();throw new Error(p.message||p.error||"Failed to join")}let l=await n.json();this.submitted=!0,this.submittedData=l,this.renderSuccess(),this.options.onSuccess?.(l)}catch(n){this.showError(t,n.message||"Something went wrong"),r&&(r.disabled=!1,r.textContent="Join Waitlist"),this.options.onError?.(n)}}showError(e,t){let r=e.querySelector(".qz-error");r&&r.remove();let o=document.createElement("div");o.className="qz-error",o.textContent=t,e.insertBefore(o,e.firstChild)}renderSuccess(){let e=this.submittedData?.position||"?",t=this.submittedData?.referralCode,r=window.location.origin+window.location.pathname,o=t?`${r}?ref=${t}`:null,s=`
|
|
139
298
|
<div class="qz-success">
|
|
140
299
|
<h3>You're on the list!</h3>
|
|
141
300
|
<p>You're #${e} in line.</p>
|
|
142
|
-
`;
|
|
301
|
+
`;if(o&&(s+=`
|
|
143
302
|
<div style="margin-top: 20px;">
|
|
144
303
|
<p style="margin-bottom: 8px; color: #a6e3a1;">Refer friends to move up:</p>
|
|
145
|
-
<input class="qz-input" readonly value="${
|
|
304
|
+
<input class="qz-input" readonly value="${o}" onclick="this.select()" style="text-align: center; cursor: pointer;">
|
|
146
305
|
</div>
|
|
147
|
-
`),
|
|
306
|
+
`),s+="</div>",this.options.displayMode==="modal"){let c=document.getElementById(`qz-modal-form-container-${this.campaign}`);if(c){c.innerHTML=s;return}}this.container.innerHTML=s}};function k(a,e,t){return new u(a,e,t)}return $(C);})();
|