@queuezero/vue 0.1.3

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/dist/index.js ADDED
@@ -0,0 +1,565 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ InMemoryStorageAdapter: () => import_queuezero3.InMemoryStorageAdapter,
24
+ LocalStorageAdapter: () => import_queuezero3.LocalStorageAdapter,
25
+ QueueZeroClient: () => import_queuezero3.QueueZeroClient,
26
+ ReferralShare: () => ReferralShare,
27
+ WAITLIST_INJECTION_KEY: () => WAITLIST_INJECTION_KEY,
28
+ WaitlistForm: () => WaitlistForm,
29
+ WaitlistStatus: () => WaitlistStatus,
30
+ createWaitlistPlugin: () => createWaitlistPlugin,
31
+ provideWaitlist: () => provideWaitlist,
32
+ useWaitlist: () => useWaitlist,
33
+ useWaitlistContext: () => useWaitlistContext
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/composables.ts
38
+ var import_vue = require("vue");
39
+ var import_queuezero = require("queuezero");
40
+ var WAITLIST_INJECTION_KEY = /* @__PURE__ */ Symbol("queuezero-waitlist");
41
+ function useWaitlist(campaign, config) {
42
+ const injected = (0, import_vue.inject)(WAITLIST_INJECTION_KEY, null);
43
+ if (injected && !campaign) {
44
+ return injected;
45
+ }
46
+ if (!campaign) {
47
+ throw new Error("useWaitlist requires a campaign slug or must be used within WaitlistProvider");
48
+ }
49
+ const client = new import_queuezero.QueueZeroClient(campaign, config);
50
+ const stateManager = new import_queuezero.QueueZeroStateManager(client);
51
+ const loading = (0, import_vue.ref)(stateManager.getState().loading);
52
+ const status = (0, import_vue.ref)(stateManager.getState().status);
53
+ const error = (0, import_vue.ref)(stateManager.getState().error);
54
+ const isJoined = (0, import_vue.ref)(stateManager.getState().isJoined);
55
+ const publicConfig = (0, import_vue.ref)(stateManager.getState().publicConfig);
56
+ stateManager.subscribe((newState) => {
57
+ loading.value = newState.loading;
58
+ status.value = newState.status;
59
+ error.value = newState.error;
60
+ isJoined.value = newState.isJoined;
61
+ publicConfig.value = newState.publicConfig;
62
+ });
63
+ (0, import_vue.onMounted)(async () => {
64
+ await stateManager.fetchPublicConfig();
65
+ if (stateManager.getState().isJoined) {
66
+ stateManager.refresh();
67
+ }
68
+ });
69
+ return {
70
+ loading,
71
+ status,
72
+ error,
73
+ isJoined,
74
+ publicConfig,
75
+ campaign,
76
+ join: stateManager.join.bind(stateManager),
77
+ refresh: stateManager.refresh.bind(stateManager),
78
+ fetchPublicConfig: stateManager.fetchPublicConfig.bind(stateManager),
79
+ getReferralLink: stateManager.getReferralLink.bind(stateManager),
80
+ getReferralCode: stateManager.getReferralCode.bind(stateManager),
81
+ reset: stateManager.reset.bind(stateManager)
82
+ };
83
+ }
84
+ function provideWaitlist(campaign, config) {
85
+ const waitlist = useWaitlist(campaign, config);
86
+ (0, import_vue.provide)(WAITLIST_INJECTION_KEY, waitlist);
87
+ return waitlist;
88
+ }
89
+ function useWaitlistContext() {
90
+ const context = (0, import_vue.inject)(WAITLIST_INJECTION_KEY);
91
+ if (!context) {
92
+ throw new Error("useWaitlistContext must be used within a component that calls provideWaitlist");
93
+ }
94
+ return context;
95
+ }
96
+ function createWaitlistPlugin(options) {
97
+ return {
98
+ install(app) {
99
+ const waitlist = useWaitlist(options.campaign, options.config);
100
+ app.provide(WAITLIST_INJECTION_KEY, waitlist);
101
+ }
102
+ };
103
+ }
104
+
105
+ // src/WaitlistForm.ts
106
+ var import_vue2 = require("vue");
107
+ var import_queuezero2 = require("queuezero");
108
+ var WaitlistForm = (0, import_vue2.defineComponent)({
109
+ name: "WaitlistForm",
110
+ props: {
111
+ referrerCode: {
112
+ type: String,
113
+ default: void 0
114
+ },
115
+ metadata: {
116
+ type: Object,
117
+ default: () => ({})
118
+ },
119
+ placeholder: {
120
+ type: String,
121
+ default: "Enter your email"
122
+ },
123
+ buttonText: {
124
+ type: String,
125
+ default: "Join Waitlist"
126
+ },
127
+ loadingText: {
128
+ type: String,
129
+ default: "Joining..."
130
+ },
131
+ showLabel: {
132
+ type: Boolean,
133
+ default: true
134
+ },
135
+ labelText: {
136
+ type: String,
137
+ default: "Email"
138
+ }
139
+ },
140
+ emits: {
141
+ success: (response) => true,
142
+ error: (error) => true
143
+ },
144
+ setup(props, { emit, slots }) {
145
+ const { join, loading, error: contextError, isJoined, publicConfig } = useWaitlistContext();
146
+ const email = (0, import_vue2.ref)("");
147
+ const localError = (0, import_vue2.ref)(null);
148
+ const formData = (0, import_vue2.reactive)({});
149
+ const displayError = (0, import_vue2.computed)(() => localError.value || contextError.value);
150
+ const formFields = (0, import_vue2.computed)(() => {
151
+ return publicConfig.value?.formFields?.filter((f) => f.enabled) ?? [];
152
+ });
153
+ const controller = (0, import_vue2.computed)(
154
+ () => new import_queuezero2.FormController(join, formFields.value, props.referrerCode)
155
+ );
156
+ async function handleSubmit(e) {
157
+ e?.preventDefault();
158
+ localError.value = null;
159
+ try {
160
+ const result = await controller.value.submit(email.value, formData, props.metadata);
161
+ if (result) {
162
+ email.value = "";
163
+ Object.keys(formData).forEach((key) => delete formData[key]);
164
+ emit("success", result);
165
+ } else if (contextError.value) {
166
+ emit("error", contextError.value);
167
+ }
168
+ } catch (err) {
169
+ const error = err instanceof Error ? err : new Error("Failed to join");
170
+ localError.value = error;
171
+ emit("error", error);
172
+ }
173
+ }
174
+ function renderField(field) {
175
+ const fieldId = `qz-field-${field.key}`;
176
+ const fieldValue = formData[field.key] ?? "";
177
+ const labelNode = (0, import_vue2.h)("label", {
178
+ class: "qz-form-label",
179
+ for: fieldId
180
+ }, [
181
+ field.label,
182
+ field.required && (0, import_vue2.h)("span", { class: "qz-required" }, " *")
183
+ ]);
184
+ let inputNode;
185
+ switch (field.type) {
186
+ case "select":
187
+ inputNode = (0, import_vue2.h)("select", {
188
+ id: fieldId,
189
+ class: "qz-form-select",
190
+ value: fieldValue,
191
+ disabled: loading.value,
192
+ required: field.required,
193
+ onChange: (e) => {
194
+ formData[field.key] = e.target.value;
195
+ }
196
+ }, [
197
+ (0, import_vue2.h)("option", { value: "" }, field.placeholder || `Select ${field.label}`),
198
+ ...(field.options || []).map(
199
+ (opt) => (0, import_vue2.h)("option", { value: opt }, opt)
200
+ )
201
+ ]);
202
+ break;
203
+ case "checkbox":
204
+ inputNode = (0, import_vue2.h)("div", { class: "qz-form-checkbox-wrapper" }, [
205
+ (0, import_vue2.h)("input", {
206
+ id: fieldId,
207
+ type: "checkbox",
208
+ class: "qz-form-checkbox",
209
+ checked: !!formData[field.key],
210
+ disabled: loading.value,
211
+ required: field.required,
212
+ onChange: (e) => {
213
+ formData[field.key] = e.target.checked;
214
+ }
215
+ }),
216
+ (0, import_vue2.h)("label", { for: fieldId, class: "qz-form-checkbox-label" }, field.label)
217
+ ]);
218
+ return (0, import_vue2.h)("div", { class: "qz-form-field" }, [inputNode]);
219
+ case "number":
220
+ inputNode = (0, import_vue2.h)("input", {
221
+ id: fieldId,
222
+ type: "number",
223
+ class: "qz-form-input",
224
+ value: fieldValue,
225
+ placeholder: field.placeholder || "",
226
+ disabled: loading.value,
227
+ required: field.required,
228
+ onInput: (e) => {
229
+ formData[field.key] = e.target.value;
230
+ }
231
+ });
232
+ break;
233
+ default:
234
+ inputNode = (0, import_vue2.h)("input", {
235
+ id: fieldId,
236
+ type: field.type,
237
+ class: "qz-form-input",
238
+ value: fieldValue,
239
+ placeholder: field.placeholder || "",
240
+ disabled: loading.value,
241
+ required: field.required,
242
+ onInput: (e) => {
243
+ formData[field.key] = e.target.value;
244
+ }
245
+ });
246
+ }
247
+ return (0, import_vue2.h)("div", { class: "qz-form-field" }, [labelNode, inputNode]);
248
+ }
249
+ return () => {
250
+ if (isJoined.value) {
251
+ return null;
252
+ }
253
+ if (slots.default) {
254
+ return slots.default({
255
+ email: email.value,
256
+ setEmail: (val) => email.value = val,
257
+ formData,
258
+ formFields: formFields.value,
259
+ submit: handleSubmit,
260
+ loading: loading.value,
261
+ error: displayError.value
262
+ });
263
+ }
264
+ const fieldNodes = [];
265
+ fieldNodes.push(
266
+ (0, import_vue2.h)("div", { class: "qz-form-field" }, [
267
+ props.showLabel && (0, import_vue2.h)("label", { class: "qz-form-label", for: "qz-email" }, [
268
+ props.labelText,
269
+ (0, import_vue2.h)("span", { class: "qz-required" }, " *")
270
+ ]),
271
+ (0, import_vue2.h)("input", {
272
+ id: "qz-email",
273
+ type: "email",
274
+ value: email.value,
275
+ onInput: (e) => email.value = e.target.value,
276
+ placeholder: props.placeholder,
277
+ disabled: loading.value,
278
+ required: true,
279
+ class: "qz-form-input",
280
+ "aria-label": !props.showLabel ? props.labelText : void 0
281
+ })
282
+ ])
283
+ );
284
+ formFields.value.forEach((field) => {
285
+ fieldNodes.push(renderField(field));
286
+ });
287
+ return (0, import_vue2.h)(
288
+ "form",
289
+ {
290
+ class: "qz-form",
291
+ onSubmit: handleSubmit
292
+ },
293
+ [
294
+ ...fieldNodes,
295
+ (0, import_vue2.h)("div", { class: "qz-form-actions" }, [
296
+ (0, import_vue2.h)(
297
+ "button",
298
+ {
299
+ type: "submit",
300
+ disabled: loading.value,
301
+ class: "qz-form-button"
302
+ },
303
+ loading.value ? props.loadingText : props.buttonText
304
+ )
305
+ ]),
306
+ displayError.value && (0, import_vue2.h)(
307
+ "div",
308
+ { class: "qz-form-error", role: "alert" },
309
+ displayError.value.message
310
+ )
311
+ ]
312
+ );
313
+ };
314
+ }
315
+ });
316
+
317
+ // src/WaitlistStatus.ts
318
+ var import_vue3 = require("vue");
319
+ var WaitlistStatus = (0, import_vue3.defineComponent)({
320
+ name: "WaitlistStatus",
321
+ props: {
322
+ showReferrals: {
323
+ type: Boolean,
324
+ default: true
325
+ },
326
+ showScore: {
327
+ type: Boolean,
328
+ default: true
329
+ },
330
+ positionLabel: {
331
+ type: String,
332
+ default: "Position"
333
+ },
334
+ scoreLabel: {
335
+ type: String,
336
+ default: "Score"
337
+ },
338
+ referralsLabel: {
339
+ type: String,
340
+ default: "Referrals"
341
+ }
342
+ },
343
+ setup(props, { slots }) {
344
+ const { status, loading, isJoined } = useWaitlistContext();
345
+ return () => {
346
+ if (!isJoined.value || !status.value) {
347
+ return null;
348
+ }
349
+ if (loading.value && !status.value) {
350
+ return (0, import_vue3.h)("div", { class: "qz-status qz-status-loading" }, "Loading...");
351
+ }
352
+ if (slots.default) {
353
+ return slots.default(status.value);
354
+ }
355
+ return (0, import_vue3.h)("div", { class: "qz-status" }, [
356
+ (0, import_vue3.h)("div", { class: "qz-status-item qz-status-position" }, [
357
+ (0, import_vue3.h)("span", { class: "qz-status-label" }, props.positionLabel),
358
+ (0, import_vue3.h)(
359
+ "span",
360
+ { class: "qz-status-value" },
361
+ `#${status.value.position.toLocaleString()}`
362
+ )
363
+ ]),
364
+ props.showScore && (0, import_vue3.h)("div", { class: "qz-status-item qz-status-score" }, [
365
+ (0, import_vue3.h)("span", { class: "qz-status-label" }, props.scoreLabel),
366
+ (0, import_vue3.h)(
367
+ "span",
368
+ { class: "qz-status-value" },
369
+ status.value.priorityScore.toLocaleString()
370
+ )
371
+ ]),
372
+ props.showReferrals && (0, import_vue3.h)("div", { class: "qz-status-item qz-status-referrals" }, [
373
+ (0, import_vue3.h)("span", { class: "qz-status-label" }, props.referralsLabel),
374
+ (0, import_vue3.h)(
375
+ "span",
376
+ { class: "qz-status-value" },
377
+ status.value.referralCount.toLocaleString()
378
+ )
379
+ ])
380
+ ]);
381
+ };
382
+ }
383
+ });
384
+
385
+ // src/ReferralShare.ts
386
+ var import_vue4 = require("vue");
387
+ var ReferralShare = (0, import_vue4.defineComponent)({
388
+ name: "ReferralShare",
389
+ props: {
390
+ label: {
391
+ type: String,
392
+ default: "Share your referral link:"
393
+ },
394
+ copyButtonText: {
395
+ type: String,
396
+ default: "Copy"
397
+ },
398
+ copiedText: {
399
+ type: String,
400
+ default: "Copied!"
401
+ },
402
+ shareMessage: {
403
+ type: String,
404
+ default: "Join me on the waitlist!"
405
+ },
406
+ showSocialButtons: {
407
+ type: Boolean,
408
+ default: false
409
+ }
410
+ },
411
+ emits: {
412
+ copy: (link) => true
413
+ },
414
+ setup(props, { emit, slots }) {
415
+ const { getReferralLink, getReferralCode, isJoined } = useWaitlistContext();
416
+ const copied = (0, import_vue4.ref)(false);
417
+ async function handleCopy() {
418
+ const link = getReferralLink();
419
+ if (!link) return;
420
+ try {
421
+ await navigator.clipboard.writeText(link);
422
+ copied.value = true;
423
+ emit("copy", link);
424
+ setTimeout(() => copied.value = false, 2e3);
425
+ } catch {
426
+ const textarea = document.createElement("textarea");
427
+ textarea.value = link;
428
+ textarea.style.position = "fixed";
429
+ textarea.style.opacity = "0";
430
+ document.body.appendChild(textarea);
431
+ textarea.select();
432
+ document.execCommand("copy");
433
+ document.body.removeChild(textarea);
434
+ copied.value = true;
435
+ emit("copy", link);
436
+ setTimeout(() => copied.value = false, 2e3);
437
+ }
438
+ }
439
+ function handleTwitterShare() {
440
+ const link = getReferralLink();
441
+ if (!link) return;
442
+ const text = encodeURIComponent(`${props.shareMessage} ${link}`);
443
+ window.open(`https://twitter.com/intent/tweet?text=${text}`, "_blank", "noopener,noreferrer");
444
+ }
445
+ function handleLinkedInShare() {
446
+ const link = getReferralLink();
447
+ if (!link) return;
448
+ const url = encodeURIComponent(link);
449
+ window.open(
450
+ `https://www.linkedin.com/sharing/share-offsite/?url=${url}`,
451
+ "_blank",
452
+ "noopener,noreferrer"
453
+ );
454
+ }
455
+ function handleEmailShare() {
456
+ const link = getReferralLink();
457
+ if (!link) return;
458
+ const subject = encodeURIComponent("Join me on the waitlist!");
459
+ const body = encodeURIComponent(`${props.shareMessage}
460
+
461
+ ${link}`);
462
+ window.location.href = `mailto:?subject=${subject}&body=${body}`;
463
+ }
464
+ return () => {
465
+ const link = getReferralLink();
466
+ const code = getReferralCode();
467
+ if (!isJoined.value || !link || !code) {
468
+ return null;
469
+ }
470
+ if (slots.default) {
471
+ return slots.default({
472
+ link,
473
+ code,
474
+ copy: handleCopy,
475
+ copied: copied.value
476
+ });
477
+ }
478
+ return (0, import_vue4.h)("div", { class: "qz-share" }, [
479
+ props.label && (0, import_vue4.h)("div", { class: "qz-share-label" }, props.label),
480
+ (0, import_vue4.h)("div", { class: "qz-share-link-container" }, [
481
+ (0, import_vue4.h)("input", {
482
+ type: "text",
483
+ value: link,
484
+ readonly: true,
485
+ class: "qz-share-input",
486
+ onClick: (e) => e.target.select()
487
+ }),
488
+ (0, import_vue4.h)(
489
+ "button",
490
+ {
491
+ onClick: handleCopy,
492
+ class: "qz-share-copy-button"
493
+ },
494
+ copied.value ? props.copiedText : props.copyButtonText
495
+ )
496
+ ]),
497
+ props.showSocialButtons && (0, import_vue4.h)("div", { class: "qz-share-social" }, [
498
+ (0, import_vue4.h)(
499
+ "button",
500
+ {
501
+ onClick: handleTwitterShare,
502
+ class: "qz-share-social-button qz-share-twitter",
503
+ "aria-label": "Share on Twitter"
504
+ },
505
+ (0, import_vue4.h)(
506
+ "svg",
507
+ { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor" },
508
+ (0, import_vue4.h)("path", {
509
+ d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
510
+ })
511
+ )
512
+ ),
513
+ (0, import_vue4.h)(
514
+ "button",
515
+ {
516
+ onClick: handleLinkedInShare,
517
+ class: "qz-share-social-button qz-share-linkedin",
518
+ "aria-label": "Share on LinkedIn"
519
+ },
520
+ (0, import_vue4.h)(
521
+ "svg",
522
+ { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor" },
523
+ (0, import_vue4.h)("path", {
524
+ d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"
525
+ })
526
+ )
527
+ ),
528
+ (0, import_vue4.h)(
529
+ "button",
530
+ {
531
+ onClick: handleEmailShare,
532
+ class: "qz-share-social-button qz-share-email",
533
+ "aria-label": "Share via Email"
534
+ },
535
+ (0, import_vue4.h)("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor" }, [
536
+ (0, import_vue4.h)("path", {
537
+ d: "M1.5 8.67v8.58a3 3 0 003 3h15a3 3 0 003-3V8.67l-8.928 5.493a3 3 0 01-3.144 0L1.5 8.67z"
538
+ }),
539
+ (0, import_vue4.h)("path", {
540
+ d: "M22.5 6.908V6.75a3 3 0 00-3-3h-15a3 3 0 00-3 3v.158l9.714 5.978a1.5 1.5 0 001.572 0L22.5 6.908z"
541
+ })
542
+ ])
543
+ )
544
+ ])
545
+ ]);
546
+ };
547
+ }
548
+ });
549
+
550
+ // src/index.ts
551
+ var import_queuezero3 = require("queuezero");
552
+ // Annotate the CommonJS export names for ESM import in node:
553
+ 0 && (module.exports = {
554
+ InMemoryStorageAdapter,
555
+ LocalStorageAdapter,
556
+ QueueZeroClient,
557
+ ReferralShare,
558
+ WAITLIST_INJECTION_KEY,
559
+ WaitlistForm,
560
+ WaitlistStatus,
561
+ createWaitlistPlugin,
562
+ provideWaitlist,
563
+ useWaitlist,
564
+ useWaitlistContext
565
+ });