@queuezero/vue 0.1.9 → 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 CHANGED
@@ -83,6 +83,11 @@ Ready-to-use signup form.
83
83
  display-mode="modal"
84
84
  trigger-text="Join Our Waitlist"
85
85
  modal-size="md"
86
+ <!-- Headless modal mode -->
87
+ <WaitlistForm
88
+ display-mode="modal"
89
+ attach-to-text="Join Waitlist"
90
+ modal-size="md"
86
91
  />
87
92
  </template>
88
93
  ```
@@ -93,6 +98,7 @@ Ready-to-use signup form.
93
98
  |------|------|---------|-------------|
94
99
  | `display-mode` | `'inline' \| 'modal'` | `'inline'` | Display as inline form or modal |
95
100
  | `trigger-text` | `string` | `'Join Waitlist'` | Button text (modal mode only) |
101
+ | `attach-to-text` | `string` | - | Text to match for auto-attaching modal (headless mode) |
96
102
  | `modal-size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Modal width (modal mode only) |
97
103
 
98
104
  The modal automatically uses your campaign's branding (logo, cover image, theme color) from the API.
package/dist/index.d.mts CHANGED
@@ -62,6 +62,10 @@ interface WaitlistFormProps {
62
62
  triggerText?: string;
63
63
  /** Modal size (only used when displayMode is 'modal') */
64
64
  modalSize?: 'sm' | 'md' | 'lg';
65
+ /** CSS selector to attach modal trigger to existing elements */
66
+ triggerSelector?: string;
67
+ /** Text content to match for attaching modal trigger to <a> and <button> elements */
68
+ attachToText?: string;
65
69
  }
66
70
  /**
67
71
  * Props for WaitlistStatus component
@@ -218,11 +222,19 @@ declare const WaitlistForm: vue.DefineComponent<vue.ExtractPropTypes<{
218
222
  type: PropType<"sm" | "md" | "lg">;
219
223
  default: string;
220
224
  };
225
+ triggerSelector: {
226
+ type: StringConstructor;
227
+ default: undefined;
228
+ };
229
+ attachToText: {
230
+ type: StringConstructor;
231
+ default: undefined;
232
+ };
221
233
  }>, () => VNode<vue.RendererNode, vue.RendererElement, {
222
234
  [key: string]: any;
223
235
  }> | VNode<vue.RendererNode, vue.RendererElement, {
224
236
  [key: string]: any;
225
- }>[] | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
237
+ }>[] | null | undefined, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
226
238
  success: (response: SubmitResponse) => true;
227
239
  error: (error: Error) => true;
228
240
  }, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
@@ -266,6 +278,14 @@ declare const WaitlistForm: vue.DefineComponent<vue.ExtractPropTypes<{
266
278
  type: PropType<"sm" | "md" | "lg">;
267
279
  default: string;
268
280
  };
281
+ triggerSelector: {
282
+ type: StringConstructor;
283
+ default: undefined;
284
+ };
285
+ attachToText: {
286
+ type: StringConstructor;
287
+ default: undefined;
288
+ };
269
289
  }>> & Readonly<{
270
290
  onSuccess?: ((response: SubmitResponse) => any) | undefined;
271
291
  onError?: ((error: Error) => any) | undefined;
@@ -280,6 +300,8 @@ declare const WaitlistForm: vue.DefineComponent<vue.ExtractPropTypes<{
280
300
  displayMode: "inline" | "modal";
281
301
  triggerText: string;
282
302
  modalSize: "sm" | "md" | "lg";
303
+ triggerSelector: string;
304
+ attachToText: string;
283
305
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
284
306
 
285
307
  /**
package/dist/index.d.ts CHANGED
@@ -62,6 +62,10 @@ interface WaitlistFormProps {
62
62
  triggerText?: string;
63
63
  /** Modal size (only used when displayMode is 'modal') */
64
64
  modalSize?: 'sm' | 'md' | 'lg';
65
+ /** CSS selector to attach modal trigger to existing elements */
66
+ triggerSelector?: string;
67
+ /** Text content to match for attaching modal trigger to <a> and <button> elements */
68
+ attachToText?: string;
65
69
  }
66
70
  /**
67
71
  * Props for WaitlistStatus component
@@ -218,11 +222,19 @@ declare const WaitlistForm: vue.DefineComponent<vue.ExtractPropTypes<{
218
222
  type: PropType<"sm" | "md" | "lg">;
219
223
  default: string;
220
224
  };
225
+ triggerSelector: {
226
+ type: StringConstructor;
227
+ default: undefined;
228
+ };
229
+ attachToText: {
230
+ type: StringConstructor;
231
+ default: undefined;
232
+ };
221
233
  }>, () => VNode<vue.RendererNode, vue.RendererElement, {
222
234
  [key: string]: any;
223
235
  }> | VNode<vue.RendererNode, vue.RendererElement, {
224
236
  [key: string]: any;
225
- }>[] | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
237
+ }>[] | null | undefined, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
226
238
  success: (response: SubmitResponse) => true;
227
239
  error: (error: Error) => true;
228
240
  }, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
@@ -266,6 +278,14 @@ declare const WaitlistForm: vue.DefineComponent<vue.ExtractPropTypes<{
266
278
  type: PropType<"sm" | "md" | "lg">;
267
279
  default: string;
268
280
  };
281
+ triggerSelector: {
282
+ type: StringConstructor;
283
+ default: undefined;
284
+ };
285
+ attachToText: {
286
+ type: StringConstructor;
287
+ default: undefined;
288
+ };
269
289
  }>> & Readonly<{
270
290
  onSuccess?: ((response: SubmitResponse) => any) | undefined;
271
291
  onError?: ((error: Error) => any) | undefined;
@@ -280,6 +300,8 @@ declare const WaitlistForm: vue.DefineComponent<vue.ExtractPropTypes<{
280
300
  displayMode: "inline" | "modal";
281
301
  triggerText: string;
282
302
  modalSize: "sm" | "md" | "lg";
303
+ triggerSelector: string;
304
+ attachToText: string;
283
305
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
284
306
 
285
307
  /**
package/dist/index.js CHANGED
@@ -153,6 +153,14 @@ var WaitlistForm = (0, import_vue2.defineComponent)({
153
153
  modalSize: {
154
154
  type: String,
155
155
  default: "md"
156
+ },
157
+ triggerSelector: {
158
+ type: String,
159
+ default: void 0
160
+ },
161
+ attachToText: {
162
+ type: String,
163
+ default: void 0
156
164
  }
157
165
  },
158
166
  emits: {
@@ -176,6 +184,32 @@ var WaitlistForm = (0, import_vue2.defineComponent)({
176
184
  const controller = (0, import_vue2.computed)(
177
185
  () => new import_queuezero2.FormController(join, formFields.value, props.referrerCode)
178
186
  );
187
+ const cleanupFns = [];
188
+ (0, import_vue2.onMounted)(() => {
189
+ if (props.displayMode !== "modal") return;
190
+ const attach = (element) => {
191
+ const handler = (e) => {
192
+ e.preventDefault();
193
+ isModalOpen.value = true;
194
+ };
195
+ element.addEventListener("click", handler);
196
+ cleanupFns.push(() => element.removeEventListener("click", handler));
197
+ };
198
+ if (props.triggerSelector) {
199
+ document.querySelectorAll(props.triggerSelector).forEach(attach);
200
+ }
201
+ if (props.attachToText) {
202
+ const lowerText = props.attachToText.toLowerCase();
203
+ document.querySelectorAll("a, button").forEach((el) => {
204
+ if (el.textContent?.toLowerCase().includes(lowerText)) {
205
+ attach(el);
206
+ }
207
+ });
208
+ }
209
+ });
210
+ (0, import_vue2.onUnmounted)(() => {
211
+ cleanupFns.forEach((fn) => fn());
212
+ });
179
213
  async function handleSubmit(e) {
180
214
  e?.preventDefault();
181
215
  localError.value = null;
@@ -351,9 +385,10 @@ var WaitlistForm = (0, import_vue2.defineComponent)({
351
385
  if (props.displayMode === "inline") {
352
386
  return formContent;
353
387
  }
354
- return (0, import_vue2.h)("div", { class: "qz-waitlist-modal-wrapper" }, [
355
- // Trigger button
356
- (0, import_vue2.h)(
388
+ const isHeadless = !!(props.triggerSelector || props.attachToText);
389
+ const children = [];
390
+ if (!isHeadless) {
391
+ children.push((0, import_vue2.h)(
357
392
  "button",
358
393
  {
359
394
  type: "button",
@@ -364,9 +399,10 @@ var WaitlistForm = (0, import_vue2.defineComponent)({
364
399
  }
365
400
  },
366
401
  props.triggerText
367
- ),
368
- // Modal overlay (only when open)
369
- isModalOpen.value && (0, import_vue2.h)(
402
+ ));
403
+ }
404
+ if (isModalOpen.value) {
405
+ children.push((0, import_vue2.h)(
370
406
  "div",
371
407
  {
372
408
  class: "qz-modal-overlay",
@@ -414,8 +450,8 @@ var WaitlistForm = (0, import_vue2.defineComponent)({
414
450
  ])
415
451
  ])
416
452
  ]
417
- )
418
- ]);
453
+ ));
454
+ }
419
455
  };
420
456
  }
421
457
  });
package/dist/index.mjs CHANGED
@@ -73,7 +73,7 @@ function createWaitlistPlugin(options) {
73
73
  }
74
74
 
75
75
  // src/WaitlistForm.ts
76
- import { defineComponent, ref as ref2, computed as computed2, h, reactive } from "vue";
76
+ import { defineComponent, ref as ref2, computed as computed2, h, reactive, onMounted as onMounted2, onUnmounted } from "vue";
77
77
  import { FormController } from "queuezero";
78
78
  var WaitlistForm = defineComponent({
79
79
  name: "WaitlistForm",
@@ -117,6 +117,14 @@ var WaitlistForm = defineComponent({
117
117
  modalSize: {
118
118
  type: String,
119
119
  default: "md"
120
+ },
121
+ triggerSelector: {
122
+ type: String,
123
+ default: void 0
124
+ },
125
+ attachToText: {
126
+ type: String,
127
+ default: void 0
120
128
  }
121
129
  },
122
130
  emits: {
@@ -140,6 +148,32 @@ var WaitlistForm = defineComponent({
140
148
  const controller = computed2(
141
149
  () => new FormController(join, formFields.value, props.referrerCode)
142
150
  );
151
+ const cleanupFns = [];
152
+ onMounted2(() => {
153
+ if (props.displayMode !== "modal") return;
154
+ const attach = (element) => {
155
+ const handler = (e) => {
156
+ e.preventDefault();
157
+ isModalOpen.value = true;
158
+ };
159
+ element.addEventListener("click", handler);
160
+ cleanupFns.push(() => element.removeEventListener("click", handler));
161
+ };
162
+ if (props.triggerSelector) {
163
+ document.querySelectorAll(props.triggerSelector).forEach(attach);
164
+ }
165
+ if (props.attachToText) {
166
+ const lowerText = props.attachToText.toLowerCase();
167
+ document.querySelectorAll("a, button").forEach((el) => {
168
+ if (el.textContent?.toLowerCase().includes(lowerText)) {
169
+ attach(el);
170
+ }
171
+ });
172
+ }
173
+ });
174
+ onUnmounted(() => {
175
+ cleanupFns.forEach((fn) => fn());
176
+ });
143
177
  async function handleSubmit(e) {
144
178
  e?.preventDefault();
145
179
  localError.value = null;
@@ -315,9 +349,10 @@ var WaitlistForm = defineComponent({
315
349
  if (props.displayMode === "inline") {
316
350
  return formContent;
317
351
  }
318
- return h("div", { class: "qz-waitlist-modal-wrapper" }, [
319
- // Trigger button
320
- h(
352
+ const isHeadless = !!(props.triggerSelector || props.attachToText);
353
+ const children = [];
354
+ if (!isHeadless) {
355
+ children.push(h(
321
356
  "button",
322
357
  {
323
358
  type: "button",
@@ -328,9 +363,10 @@ var WaitlistForm = defineComponent({
328
363
  }
329
364
  },
330
365
  props.triggerText
331
- ),
332
- // Modal overlay (only when open)
333
- isModalOpen.value && h(
366
+ ));
367
+ }
368
+ if (isModalOpen.value) {
369
+ children.push(h(
334
370
  "div",
335
371
  {
336
372
  class: "qz-modal-overlay",
@@ -378,8 +414,8 @@ var WaitlistForm = defineComponent({
378
414
  ])
379
415
  ])
380
416
  ]
381
- )
382
- ]);
417
+ ));
418
+ }
383
419
  };
384
420
  }
385
421
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@queuezero/vue",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Vue components and composables for QueueZero viral waitlists",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",