@queuezero/react 0.1.8 → 0.1.10
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.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +59 -4
- package/dist/index.mjs +59 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,8 +78,31 @@ A ready-to-use signup form.
|
|
|
78
78
|
</div>
|
|
79
79
|
)}
|
|
80
80
|
</WaitlistForm>
|
|
81
|
+
|
|
82
|
+
// Modal mode
|
|
83
|
+
<WaitlistForm
|
|
84
|
+
displayMode="modal"
|
|
85
|
+
triggerText="Join Our Waitlist"
|
|
86
|
+
modalSize="md"
|
|
87
|
+
// Headless modal mode (attaches to existing buttons)
|
|
88
|
+
<WaitlistForm
|
|
89
|
+
displayMode="modal"
|
|
90
|
+
attachToText="Join Waitlist"
|
|
91
|
+
modalSize="md"
|
|
92
|
+
/>
|
|
81
93
|
```
|
|
82
94
|
|
|
95
|
+
#### Modal Mode Props
|
|
96
|
+
|
|
97
|
+
| Prop | Type | Default | Description |
|
|
98
|
+
|------|------|---------|-------------|
|
|
99
|
+
| `displayMode` | `'inline' \| 'modal'` | `'inline'` | Display as inline form or modal |
|
|
100
|
+
| `triggerText` | `string` | `'Join Waitlist'` | Button text (modal mode only) |
|
|
101
|
+
| `attachToText` | `string` | - | Text to match for auto-attaching modal (headless mode) |
|
|
102
|
+
| `modalSize` | `'sm' \| 'md' \| 'lg'` | `'md'` | Modal width (modal mode only) |
|
|
103
|
+
|
|
104
|
+
The modal automatically uses your campaign's branding (logo, cover image, theme color) from the API.
|
|
105
|
+
|
|
83
106
|
### WaitlistStatus
|
|
84
107
|
|
|
85
108
|
Displays position, score, and referral count.
|
package/dist/index.d.mts
CHANGED
|
@@ -80,6 +80,10 @@ interface WaitlistFormProps {
|
|
|
80
80
|
triggerText?: string;
|
|
81
81
|
/** Modal size (only used when displayMode is 'modal') */
|
|
82
82
|
modalSize?: 'sm' | 'md' | 'lg';
|
|
83
|
+
/** CSS selector to attach modal trigger to existing elements */
|
|
84
|
+
triggerSelector?: string;
|
|
85
|
+
/** Text content to match for attaching modal trigger to <a> and <button> elements */
|
|
86
|
+
attachToText?: string;
|
|
83
87
|
/** Children for custom rendering */
|
|
84
88
|
children?: React.ReactNode;
|
|
85
89
|
}
|
|
@@ -203,7 +207,7 @@ declare function useWaitlist(campaign: string, config?: WaitlistProviderProps['c
|
|
|
203
207
|
* <WaitlistForm displayMode="modal" triggerText="Join Our Waitlist" />
|
|
204
208
|
* ```
|
|
205
209
|
*/
|
|
206
|
-
declare function WaitlistForm({ onSuccess, onError, referrerCode, metadata, className, placeholder, buttonText, loadingText, showLabel, labelText, displayMode, triggerText, modalSize, children, }: WaitlistFormProps): any;
|
|
210
|
+
declare function WaitlistForm({ onSuccess, onError, referrerCode, metadata, className, placeholder, buttonText, loadingText, showLabel, labelText, displayMode, triggerText, modalSize, triggerSelector, attachToText, children, }: WaitlistFormProps): any;
|
|
207
211
|
|
|
208
212
|
/**
|
|
209
213
|
* WaitlistStatus - Shows current position, score, and referral count
|
package/dist/index.d.ts
CHANGED
|
@@ -80,6 +80,10 @@ interface WaitlistFormProps {
|
|
|
80
80
|
triggerText?: string;
|
|
81
81
|
/** Modal size (only used when displayMode is 'modal') */
|
|
82
82
|
modalSize?: 'sm' | 'md' | 'lg';
|
|
83
|
+
/** CSS selector to attach modal trigger to existing elements */
|
|
84
|
+
triggerSelector?: string;
|
|
85
|
+
/** Text content to match for attaching modal trigger to <a> and <button> elements */
|
|
86
|
+
attachToText?: string;
|
|
83
87
|
/** Children for custom rendering */
|
|
84
88
|
children?: React.ReactNode;
|
|
85
89
|
}
|
|
@@ -203,7 +207,7 @@ declare function useWaitlist(campaign: string, config?: WaitlistProviderProps['c
|
|
|
203
207
|
* <WaitlistForm displayMode="modal" triggerText="Join Our Waitlist" />
|
|
204
208
|
* ```
|
|
205
209
|
*/
|
|
206
|
-
declare function WaitlistForm({ onSuccess, onError, referrerCode, metadata, className, placeholder, buttonText, loadingText, showLabel, labelText, displayMode, triggerText, modalSize, children, }: WaitlistFormProps): any;
|
|
210
|
+
declare function WaitlistForm({ onSuccess, onError, referrerCode, metadata, className, placeholder, buttonText, loadingText, showLabel, labelText, displayMode, triggerText, modalSize, triggerSelector, attachToText, children, }: WaitlistFormProps): any;
|
|
207
211
|
|
|
208
212
|
/**
|
|
209
213
|
* WaitlistStatus - Shows current position, score, and referral count
|
package/dist/index.js
CHANGED
|
@@ -81,7 +81,13 @@ function WaitlistProvider({ campaign, config, children }) {
|
|
|
81
81
|
campaign,
|
|
82
82
|
join: stateManager.join.bind(stateManager),
|
|
83
83
|
refresh: stateManager.refresh.bind(stateManager),
|
|
84
|
-
getReferralLink:
|
|
84
|
+
getReferralLink: () => {
|
|
85
|
+
const code = stateManager.getReferralCode();
|
|
86
|
+
if (code && typeof window !== "undefined") {
|
|
87
|
+
return `${window.location.origin}${window.location.pathname}?ref=${code}`;
|
|
88
|
+
}
|
|
89
|
+
return stateManager.getReferralLink();
|
|
90
|
+
},
|
|
85
91
|
getReferralCode: stateManager.getReferralCode.bind(stateManager),
|
|
86
92
|
reset: stateManager.reset.bind(stateManager),
|
|
87
93
|
config: publicConfig,
|
|
@@ -176,6 +182,8 @@ function WaitlistForm({
|
|
|
176
182
|
displayMode = "inline",
|
|
177
183
|
triggerText = "Join Waitlist",
|
|
178
184
|
modalSize = "md",
|
|
185
|
+
triggerSelector,
|
|
186
|
+
attachToText,
|
|
179
187
|
children
|
|
180
188
|
}) {
|
|
181
189
|
const { join, loading, error, isJoined, config, configLoading } = useWaitlistContext();
|
|
@@ -183,6 +191,7 @@ function WaitlistForm({
|
|
|
183
191
|
const [formData, setFormData] = (0, import_react2.useState)({});
|
|
184
192
|
const [localError, setLocalError] = (0, import_react2.useState)(null);
|
|
185
193
|
const [isModalOpen, setIsModalOpen] = (0, import_react2.useState)(false);
|
|
194
|
+
const [successData, setSuccessData] = (0, import_react2.useState)(null);
|
|
186
195
|
(0, import_react2.useEffect)(() => {
|
|
187
196
|
const handleEsc = (e) => {
|
|
188
197
|
if (e.key === "Escape" && isModalOpen) setIsModalOpen(false);
|
|
@@ -213,6 +222,7 @@ function WaitlistForm({
|
|
|
213
222
|
if (result) {
|
|
214
223
|
setEmail("");
|
|
215
224
|
setFormData({});
|
|
225
|
+
setSuccessData(result);
|
|
216
226
|
onSuccess?.(result);
|
|
217
227
|
} else if (error) {
|
|
218
228
|
onError?.(error);
|
|
@@ -275,7 +285,7 @@ function WaitlistForm({
|
|
|
275
285
|
required: field.required,
|
|
276
286
|
className: "qz-form-select",
|
|
277
287
|
children: [
|
|
278
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "", children:
|
|
288
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "", children: "Select an option" }),
|
|
279
289
|
field.options?.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: opt, children: opt }, opt))
|
|
280
290
|
]
|
|
281
291
|
}
|
|
@@ -340,8 +350,33 @@ function WaitlistForm({
|
|
|
340
350
|
if (displayMode === "inline") {
|
|
341
351
|
return formContent;
|
|
342
352
|
}
|
|
353
|
+
const isHeadless = !!(triggerSelector || attachToText);
|
|
354
|
+
(0, import_react2.useEffect)(() => {
|
|
355
|
+
if (displayMode !== "modal") return;
|
|
356
|
+
const cleanupFns = [];
|
|
357
|
+
const attach = (element) => {
|
|
358
|
+
const handler = (e) => {
|
|
359
|
+
e.preventDefault();
|
|
360
|
+
setIsModalOpen(true);
|
|
361
|
+
};
|
|
362
|
+
element.addEventListener("click", handler);
|
|
363
|
+
cleanupFns.push(() => element.removeEventListener("click", handler));
|
|
364
|
+
};
|
|
365
|
+
if (triggerSelector) {
|
|
366
|
+
document.querySelectorAll(triggerSelector).forEach(attach);
|
|
367
|
+
}
|
|
368
|
+
if (attachToText) {
|
|
369
|
+
const lowerText = attachToText.toLowerCase();
|
|
370
|
+
document.querySelectorAll("a, button").forEach((el) => {
|
|
371
|
+
if (el.textContent?.toLowerCase().includes(lowerText)) {
|
|
372
|
+
attach(el);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return () => cleanupFns.forEach((fn) => fn());
|
|
377
|
+
}, [displayMode, triggerSelector, attachToText]);
|
|
343
378
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
344
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
379
|
+
!isHeadless && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
345
380
|
"button",
|
|
346
381
|
{
|
|
347
382
|
type: "button",
|
|
@@ -373,7 +408,27 @@ function WaitlistForm({
|
|
|
373
408
|
branding.coverImageUrl && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("img", { src: branding.coverImageUrl, alt: "", className: "qz-modal-cover" }),
|
|
374
409
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "qz-modal-body", children: [
|
|
375
410
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { className: "qz-modal-title", children: config?.name || "Join Waitlist" }),
|
|
376
|
-
|
|
411
|
+
successData ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "qz-success", children: [
|
|
412
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { fontSize: "1.25rem", fontWeight: 600, marginBottom: "0.5rem", textAlign: "center" }, children: "You're on the list!" }),
|
|
413
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: { textAlign: "center", marginBottom: "1.5rem", opacity: 0.9 }, children: [
|
|
414
|
+
"You're #",
|
|
415
|
+
successData.position || "?",
|
|
416
|
+
" in line."
|
|
417
|
+
] }),
|
|
418
|
+
successData.referralLink && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginTop: "1.5rem" }, children: [
|
|
419
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { marginBottom: "0.5rem", fontSize: "0.875rem", opacity: 0.9, textAlign: "center" }, children: "Refer friends to move up:" }),
|
|
420
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
421
|
+
"input",
|
|
422
|
+
{
|
|
423
|
+
readOnly: true,
|
|
424
|
+
value: window.location.origin + window.location.pathname + "?ref=" + successData.referralCode,
|
|
425
|
+
onClick: (e) => e.currentTarget.select(),
|
|
426
|
+
className: "qz-form-input",
|
|
427
|
+
style: { textAlign: "center", cursor: "pointer" }
|
|
428
|
+
}
|
|
429
|
+
)
|
|
430
|
+
] })
|
|
431
|
+
] }) : formContent
|
|
377
432
|
] })
|
|
378
433
|
] })
|
|
379
434
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -47,7 +47,13 @@ function WaitlistProvider({ campaign, config, children }) {
|
|
|
47
47
|
campaign,
|
|
48
48
|
join: stateManager.join.bind(stateManager),
|
|
49
49
|
refresh: stateManager.refresh.bind(stateManager),
|
|
50
|
-
getReferralLink:
|
|
50
|
+
getReferralLink: () => {
|
|
51
|
+
const code = stateManager.getReferralCode();
|
|
52
|
+
if (code && typeof window !== "undefined") {
|
|
53
|
+
return `${window.location.origin}${window.location.pathname}?ref=${code}`;
|
|
54
|
+
}
|
|
55
|
+
return stateManager.getReferralLink();
|
|
56
|
+
},
|
|
51
57
|
getReferralCode: stateManager.getReferralCode.bind(stateManager),
|
|
52
58
|
reset: stateManager.reset.bind(stateManager),
|
|
53
59
|
config: publicConfig,
|
|
@@ -142,6 +148,8 @@ function WaitlistForm({
|
|
|
142
148
|
displayMode = "inline",
|
|
143
149
|
triggerText = "Join Waitlist",
|
|
144
150
|
modalSize = "md",
|
|
151
|
+
triggerSelector,
|
|
152
|
+
attachToText,
|
|
145
153
|
children
|
|
146
154
|
}) {
|
|
147
155
|
const { join, loading, error, isJoined, config, configLoading } = useWaitlistContext();
|
|
@@ -149,6 +157,7 @@ function WaitlistForm({
|
|
|
149
157
|
const [formData, setFormData] = useState2({});
|
|
150
158
|
const [localError, setLocalError] = useState2(null);
|
|
151
159
|
const [isModalOpen, setIsModalOpen] = useState2(false);
|
|
160
|
+
const [successData, setSuccessData] = useState2(null);
|
|
152
161
|
useEffect2(() => {
|
|
153
162
|
const handleEsc = (e) => {
|
|
154
163
|
if (e.key === "Escape" && isModalOpen) setIsModalOpen(false);
|
|
@@ -179,6 +188,7 @@ function WaitlistForm({
|
|
|
179
188
|
if (result) {
|
|
180
189
|
setEmail("");
|
|
181
190
|
setFormData({});
|
|
191
|
+
setSuccessData(result);
|
|
182
192
|
onSuccess?.(result);
|
|
183
193
|
} else if (error) {
|
|
184
194
|
onError?.(error);
|
|
@@ -241,7 +251,7 @@ function WaitlistForm({
|
|
|
241
251
|
required: field.required,
|
|
242
252
|
className: "qz-form-select",
|
|
243
253
|
children: [
|
|
244
|
-
/* @__PURE__ */ jsx2("option", { value: "", children:
|
|
254
|
+
/* @__PURE__ */ jsx2("option", { value: "", children: "Select an option" }),
|
|
245
255
|
field.options?.map((opt) => /* @__PURE__ */ jsx2("option", { value: opt, children: opt }, opt))
|
|
246
256
|
]
|
|
247
257
|
}
|
|
@@ -306,8 +316,33 @@ function WaitlistForm({
|
|
|
306
316
|
if (displayMode === "inline") {
|
|
307
317
|
return formContent;
|
|
308
318
|
}
|
|
319
|
+
const isHeadless = !!(triggerSelector || attachToText);
|
|
320
|
+
useEffect2(() => {
|
|
321
|
+
if (displayMode !== "modal") return;
|
|
322
|
+
const cleanupFns = [];
|
|
323
|
+
const attach = (element) => {
|
|
324
|
+
const handler = (e) => {
|
|
325
|
+
e.preventDefault();
|
|
326
|
+
setIsModalOpen(true);
|
|
327
|
+
};
|
|
328
|
+
element.addEventListener("click", handler);
|
|
329
|
+
cleanupFns.push(() => element.removeEventListener("click", handler));
|
|
330
|
+
};
|
|
331
|
+
if (triggerSelector) {
|
|
332
|
+
document.querySelectorAll(triggerSelector).forEach(attach);
|
|
333
|
+
}
|
|
334
|
+
if (attachToText) {
|
|
335
|
+
const lowerText = attachToText.toLowerCase();
|
|
336
|
+
document.querySelectorAll("a, button").forEach((el) => {
|
|
337
|
+
if (el.textContent?.toLowerCase().includes(lowerText)) {
|
|
338
|
+
attach(el);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return () => cleanupFns.forEach((fn) => fn());
|
|
343
|
+
}, [displayMode, triggerSelector, attachToText]);
|
|
309
344
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
310
|
-
/* @__PURE__ */ jsx2(
|
|
345
|
+
!isHeadless && /* @__PURE__ */ jsx2(
|
|
311
346
|
"button",
|
|
312
347
|
{
|
|
313
348
|
type: "button",
|
|
@@ -339,7 +374,27 @@ function WaitlistForm({
|
|
|
339
374
|
branding.coverImageUrl && /* @__PURE__ */ jsx2("img", { src: branding.coverImageUrl, alt: "", className: "qz-modal-cover" }),
|
|
340
375
|
/* @__PURE__ */ jsxs("div", { className: "qz-modal-body", children: [
|
|
341
376
|
/* @__PURE__ */ jsx2("h2", { className: "qz-modal-title", children: config?.name || "Join Waitlist" }),
|
|
342
|
-
|
|
377
|
+
successData ? /* @__PURE__ */ jsxs("div", { className: "qz-success", children: [
|
|
378
|
+
/* @__PURE__ */ jsx2("h3", { style: { fontSize: "1.25rem", fontWeight: 600, marginBottom: "0.5rem", textAlign: "center" }, children: "You're on the list!" }),
|
|
379
|
+
/* @__PURE__ */ jsxs("p", { style: { textAlign: "center", marginBottom: "1.5rem", opacity: 0.9 }, children: [
|
|
380
|
+
"You're #",
|
|
381
|
+
successData.position || "?",
|
|
382
|
+
" in line."
|
|
383
|
+
] }),
|
|
384
|
+
successData.referralLink && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.5rem" }, children: [
|
|
385
|
+
/* @__PURE__ */ jsx2("p", { style: { marginBottom: "0.5rem", fontSize: "0.875rem", opacity: 0.9, textAlign: "center" }, children: "Refer friends to move up:" }),
|
|
386
|
+
/* @__PURE__ */ jsx2(
|
|
387
|
+
"input",
|
|
388
|
+
{
|
|
389
|
+
readOnly: true,
|
|
390
|
+
value: window.location.origin + window.location.pathname + "?ref=" + successData.referralCode,
|
|
391
|
+
onClick: (e) => e.currentTarget.select(),
|
|
392
|
+
className: "qz-form-input",
|
|
393
|
+
style: { textAlign: "center", cursor: "pointer" }
|
|
394
|
+
}
|
|
395
|
+
)
|
|
396
|
+
] })
|
|
397
|
+
] }) : formContent
|
|
343
398
|
] })
|
|
344
399
|
] })
|
|
345
400
|
}
|