@drippr/embed-react 0.2.0 → 0.2.2

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
@@ -158,6 +158,7 @@ function FeedbackDrawer() {
158
158
  | `style` | `React.CSSProperties` | No | - | Inline styles for the container |
159
159
  | `height` | `number` | No | `700` | Fallback height for the iframe (px) |
160
160
  | `baseUrl` | `string` | No | `"https://app.drippr.io"` | Base URL for the embed |
161
+ | `prefetch` | `boolean` | No | `true` | Prefetch page content for faster loading (modal/drawer only). Set to `false` if it causes issues with your implementation. |
161
162
 
162
163
  ### PrefillData
163
164
 
@@ -199,6 +200,7 @@ interface DripprFlowApi {
199
200
  - **Three Display Modes**: Inline, modal, and drawer
200
201
  - **Flexible Triggers**: Use render props or controlled state with external buttons
201
202
  - **Automatic Z-Index**: High z-index values prevent CSS framework conflicts (Tailwind, etc.)
203
+ - **Smart Preloading**: Iframe and page content are preloaded for instant opening (modal/drawer modes)
202
204
  - **Prefill Support**: Pre-populate form fields with user data
203
205
  - **Auto-resize**: Iframe automatically resizes based on content
204
206
  - **TypeScript**: Full TypeScript support with exported types
@@ -210,6 +212,7 @@ interface DripprFlowApi {
210
212
  - Render prop `children` pattern, OR
211
213
  - External button with controlled `open`/`onOpenChange` props
212
214
  - **Z-Index**: Automatically set to high values (99999+) to prevent conflicts with CSS frameworks like Tailwind CSS
215
+ - **Prefetching**: Enabled by default (`prefetch={true}`) for faster loading. The component automatically prefetches the page content when mounted (modal/drawer modes only). If this causes issues with your implementation (e.g., unwanted network requests, conflicts with your routing), you can disable it with `prefetch={false}`.
213
216
 
214
217
  ## Requirements
215
218
 
package/dist/index.d.mts CHANGED
@@ -54,8 +54,10 @@ interface DripprFlowProps {
54
54
  height?: number;
55
55
  /** Base URL for the embed (defaults to production) */
56
56
  baseUrl?: string;
57
+ /** Prefetch page content for faster loading (default: true). Set to false if it causes issues with your implementation. */
58
+ prefetch?: boolean;
57
59
  }
58
60
 
59
- declare function DripprFlow({ flowId, mode, open: controlledOpen, onOpenChange, prefill, children, onSubmitted, className, style, height: fallbackHeight, baseUrl, }: DripprFlowProps): react_jsx_runtime.JSX.Element | null;
61
+ declare function DripprFlow({ flowId, mode, open: controlledOpen, onOpenChange, prefill, children, onSubmitted, className, style, height: fallbackHeight, baseUrl, prefetch, }: DripprFlowProps): react_jsx_runtime.JSX.Element | null;
60
62
 
61
63
  export { DripprFlow, type DripprFlowApi, type DripprFlowProps, type DripprMessage, type DripprMessageFromChild, type DripprMessageToChild, type EmbedMode, type PrefillData };
package/dist/index.d.ts CHANGED
@@ -54,8 +54,10 @@ interface DripprFlowProps {
54
54
  height?: number;
55
55
  /** Base URL for the embed (defaults to production) */
56
56
  baseUrl?: string;
57
+ /** Prefetch page content for faster loading (default: true). Set to false if it causes issues with your implementation. */
58
+ prefetch?: boolean;
57
59
  }
58
60
 
59
- declare function DripprFlow({ flowId, mode, open: controlledOpen, onOpenChange, prefill, children, onSubmitted, className, style, height: fallbackHeight, baseUrl, }: DripprFlowProps): react_jsx_runtime.JSX.Element | null;
61
+ declare function DripprFlow({ flowId, mode, open: controlledOpen, onOpenChange, prefill, children, onSubmitted, className, style, height: fallbackHeight, baseUrl, prefetch, }: DripprFlowProps): react_jsx_runtime.JSX.Element | null;
60
62
 
61
63
  export { DripprFlow, type DripprFlowApi, type DripprFlowProps, type DripprMessage, type DripprMessageFromChild, type DripprMessageToChild, type EmbedMode, type PrefillData };
package/dist/index.js CHANGED
@@ -204,22 +204,37 @@ function ModalContainer({
204
204
  borderRadius: "2px",
205
205
  margin: "6px auto 2px"
206
206
  };
207
- if (!mounted) {
208
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: trigger });
209
- }
207
+ const iframeStyles = {
208
+ width: "100%",
209
+ height: isMobile ? `${Math.min(height, window.innerHeight - 80)}px` : `${Math.min(height, window.innerHeight * 0.85)}px`,
210
+ border: "none",
211
+ display: "block"
212
+ };
213
+ const containerStyles = mounted ? {} : {
214
+ position: "absolute",
215
+ left: "-9999px",
216
+ width: "1px",
217
+ height: "1px",
218
+ visibility: "hidden",
219
+ pointerEvents: "none",
220
+ overflow: "hidden"
221
+ };
210
222
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
211
223
  trigger,
212
224
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
213
225
  "div",
214
226
  {
215
- style: overlayStyles,
227
+ style: {
228
+ ...overlayStyles,
229
+ ...containerStyles
230
+ },
216
231
  onClick: (e) => {
217
- if (e.target === e.currentTarget) {
232
+ if (mounted && e.target === e.currentTarget) {
218
233
  onOpenChange(false);
219
234
  }
220
235
  },
221
236
  role: "dialog",
222
- "aria-modal": "true",
237
+ "aria-modal": mounted ? "true" : "false",
223
238
  children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
224
239
  "div",
225
240
  {
@@ -247,13 +262,8 @@ function ModalContainer({
247
262
  {
248
263
  ref: iframeRef,
249
264
  src,
250
- style: {
251
- width: "100%",
252
- height: isMobile ? `${Math.min(height, window.innerHeight - 80)}px` : `${Math.min(height, window.innerHeight * 0.85)}px`,
253
- border: "none",
254
- display: "block"
255
- },
256
- loading: "lazy",
265
+ style: iframeStyles,
266
+ loading: "eager",
257
267
  title: "Drippr Feedback"
258
268
  }
259
269
  )
@@ -406,17 +416,37 @@ function DrawerContainer({
406
416
  borderRadius: "2px",
407
417
  margin: "6px auto 2px"
408
418
  };
409
- if (!mounted) {
410
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: trigger });
411
- }
419
+ const iframeStyles = {
420
+ width: "100%",
421
+ height: isMobile ? `${Math.min(height, window.innerHeight - 80)}px` : `${height}px`,
422
+ minHeight: isMobile ? void 0 : "100%",
423
+ border: "none",
424
+ display: "block"
425
+ };
426
+ const containerStyles = mounted ? {} : {
427
+ position: "absolute",
428
+ left: "-9999px",
429
+ width: "1px",
430
+ height: "1px",
431
+ visibility: "hidden",
432
+ pointerEvents: "none",
433
+ overflow: "hidden"
434
+ };
412
435
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
413
436
  trigger,
414
437
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
415
438
  "div",
416
439
  {
417
- style: overlayStyles,
418
- onClick: () => onOpenChange(false),
419
- "aria-hidden": "true"
440
+ style: {
441
+ ...overlayStyles,
442
+ ...containerStyles
443
+ },
444
+ onClick: () => {
445
+ if (mounted) {
446
+ onOpenChange(false);
447
+ }
448
+ },
449
+ "aria-hidden": mounted ? "false" : "true"
420
450
  }
421
451
  ),
422
452
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
@@ -426,11 +456,12 @@ function DrawerContainer({
426
456
  style: {
427
457
  ...drawerStyles,
428
458
  ...style,
459
+ ...containerStyles,
429
460
  // Ensure z-index is always set (user's style prop shouldn't override it)
430
461
  zIndex: style?.zIndex ?? drawerStyles.zIndex
431
462
  },
432
463
  role: "dialog",
433
- "aria-modal": "true",
464
+ "aria-modal": mounted ? "true" : "false",
434
465
  children: [
435
466
  isMobile && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: handleStyles }),
436
467
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: headerStyles, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -448,14 +479,8 @@ function DrawerContainer({
448
479
  {
449
480
  ref: iframeRef,
450
481
  src,
451
- style: {
452
- width: "100%",
453
- height: isMobile ? `${Math.min(height, window.innerHeight - 80)}px` : `${height}px`,
454
- minHeight: isMobile ? void 0 : "100%",
455
- border: "none",
456
- display: "block"
457
- },
458
- loading: "lazy",
482
+ style: iframeStyles,
483
+ loading: "eager",
459
484
  title: "Drippr Feedback"
460
485
  }
461
486
  ) })
@@ -483,7 +508,8 @@ function DripprFlow({
483
508
  className,
484
509
  style,
485
510
  height: fallbackHeight = DEFAULT_HEIGHT,
486
- baseUrl = DEFAULT_BASE_URL
511
+ baseUrl = DEFAULT_BASE_URL,
512
+ prefetch = true
487
513
  }) {
488
514
  const [instanceId] = React3.useState(generateInstanceId);
489
515
  const [height, setHeight] = React3.useState(fallbackHeight);
@@ -573,6 +599,19 @@ function DripprFlow({
573
599
  );
574
600
  }
575
601
  }, [mode, children, isControlled]);
602
+ React3.useEffect(() => {
603
+ if (!prefetch || mode === "inline") return;
604
+ const link = document.createElement("link");
605
+ link.rel = "prefetch";
606
+ link.href = src;
607
+ link.as = "document";
608
+ document.head.appendChild(link);
609
+ return () => {
610
+ if (document.head.contains(link)) {
611
+ document.head.removeChild(link);
612
+ }
613
+ };
614
+ }, [src, mode, prefetch]);
576
615
  const trigger = mode !== "inline" && children ? children(api) : null;
577
616
  if (mode === "inline") {
578
617
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
package/dist/index.mjs CHANGED
@@ -168,22 +168,37 @@ function ModalContainer({
168
168
  borderRadius: "2px",
169
169
  margin: "6px auto 2px"
170
170
  };
171
- if (!mounted) {
172
- return /* @__PURE__ */ jsx2(Fragment, { children: trigger });
173
- }
171
+ const iframeStyles = {
172
+ width: "100%",
173
+ height: isMobile ? `${Math.min(height, window.innerHeight - 80)}px` : `${Math.min(height, window.innerHeight * 0.85)}px`,
174
+ border: "none",
175
+ display: "block"
176
+ };
177
+ const containerStyles = mounted ? {} : {
178
+ position: "absolute",
179
+ left: "-9999px",
180
+ width: "1px",
181
+ height: "1px",
182
+ visibility: "hidden",
183
+ pointerEvents: "none",
184
+ overflow: "hidden"
185
+ };
174
186
  return /* @__PURE__ */ jsxs(Fragment, { children: [
175
187
  trigger,
176
188
  /* @__PURE__ */ jsx2(
177
189
  "div",
178
190
  {
179
- style: overlayStyles,
191
+ style: {
192
+ ...overlayStyles,
193
+ ...containerStyles
194
+ },
180
195
  onClick: (e) => {
181
- if (e.target === e.currentTarget) {
196
+ if (mounted && e.target === e.currentTarget) {
182
197
  onOpenChange(false);
183
198
  }
184
199
  },
185
200
  role: "dialog",
186
- "aria-modal": "true",
201
+ "aria-modal": mounted ? "true" : "false",
187
202
  children: /* @__PURE__ */ jsxs(
188
203
  "div",
189
204
  {
@@ -211,13 +226,8 @@ function ModalContainer({
211
226
  {
212
227
  ref: iframeRef,
213
228
  src,
214
- style: {
215
- width: "100%",
216
- height: isMobile ? `${Math.min(height, window.innerHeight - 80)}px` : `${Math.min(height, window.innerHeight * 0.85)}px`,
217
- border: "none",
218
- display: "block"
219
- },
220
- loading: "lazy",
229
+ style: iframeStyles,
230
+ loading: "eager",
221
231
  title: "Drippr Feedback"
222
232
  }
223
233
  )
@@ -370,17 +380,37 @@ function DrawerContainer({
370
380
  borderRadius: "2px",
371
381
  margin: "6px auto 2px"
372
382
  };
373
- if (!mounted) {
374
- return /* @__PURE__ */ jsx3(Fragment2, { children: trigger });
375
- }
383
+ const iframeStyles = {
384
+ width: "100%",
385
+ height: isMobile ? `${Math.min(height, window.innerHeight - 80)}px` : `${height}px`,
386
+ minHeight: isMobile ? void 0 : "100%",
387
+ border: "none",
388
+ display: "block"
389
+ };
390
+ const containerStyles = mounted ? {} : {
391
+ position: "absolute",
392
+ left: "-9999px",
393
+ width: "1px",
394
+ height: "1px",
395
+ visibility: "hidden",
396
+ pointerEvents: "none",
397
+ overflow: "hidden"
398
+ };
376
399
  return /* @__PURE__ */ jsxs2(Fragment2, { children: [
377
400
  trigger,
378
401
  /* @__PURE__ */ jsx3(
379
402
  "div",
380
403
  {
381
- style: overlayStyles,
382
- onClick: () => onOpenChange(false),
383
- "aria-hidden": "true"
404
+ style: {
405
+ ...overlayStyles,
406
+ ...containerStyles
407
+ },
408
+ onClick: () => {
409
+ if (mounted) {
410
+ onOpenChange(false);
411
+ }
412
+ },
413
+ "aria-hidden": mounted ? "false" : "true"
384
414
  }
385
415
  ),
386
416
  /* @__PURE__ */ jsxs2(
@@ -390,11 +420,12 @@ function DrawerContainer({
390
420
  style: {
391
421
  ...drawerStyles,
392
422
  ...style,
423
+ ...containerStyles,
393
424
  // Ensure z-index is always set (user's style prop shouldn't override it)
394
425
  zIndex: style?.zIndex ?? drawerStyles.zIndex
395
426
  },
396
427
  role: "dialog",
397
- "aria-modal": "true",
428
+ "aria-modal": mounted ? "true" : "false",
398
429
  children: [
399
430
  isMobile && /* @__PURE__ */ jsx3("div", { style: handleStyles }),
400
431
  /* @__PURE__ */ jsx3("div", { style: headerStyles, children: /* @__PURE__ */ jsx3(
@@ -412,14 +443,8 @@ function DrawerContainer({
412
443
  {
413
444
  ref: iframeRef,
414
445
  src,
415
- style: {
416
- width: "100%",
417
- height: isMobile ? `${Math.min(height, window.innerHeight - 80)}px` : `${height}px`,
418
- minHeight: isMobile ? void 0 : "100%",
419
- border: "none",
420
- display: "block"
421
- },
422
- loading: "lazy",
446
+ style: iframeStyles,
447
+ loading: "eager",
423
448
  title: "Drippr Feedback"
424
449
  }
425
450
  ) })
@@ -447,7 +472,8 @@ function DripprFlow({
447
472
  className,
448
473
  style,
449
474
  height: fallbackHeight = DEFAULT_HEIGHT,
450
- baseUrl = DEFAULT_BASE_URL
475
+ baseUrl = DEFAULT_BASE_URL,
476
+ prefetch = true
451
477
  }) {
452
478
  const [instanceId] = React3.useState(generateInstanceId);
453
479
  const [height, setHeight] = React3.useState(fallbackHeight);
@@ -537,6 +563,19 @@ function DripprFlow({
537
563
  );
538
564
  }
539
565
  }, [mode, children, isControlled]);
566
+ React3.useEffect(() => {
567
+ if (!prefetch || mode === "inline") return;
568
+ const link = document.createElement("link");
569
+ link.rel = "prefetch";
570
+ link.href = src;
571
+ link.as = "document";
572
+ document.head.appendChild(link);
573
+ return () => {
574
+ if (document.head.contains(link)) {
575
+ document.head.removeChild(link);
576
+ }
577
+ };
578
+ }, [src, mode, prefetch]);
540
579
  const trigger = mode !== "inline" && children ? children(api) : null;
541
580
  if (mode === "inline") {
542
581
  return /* @__PURE__ */ jsx4(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drippr/embed-react",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "React component for embedding Drippr feedback flows",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -20,8 +20,7 @@
20
20
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
21
21
  "lint": "eslint src/",
22
22
  "typecheck": "tsc --noEmit",
23
- "prepublishOnly": "npm run build",
24
- "publish": "npm publish --access public"
23
+ "prepublishOnly": "npm run build"
25
24
  },
26
25
  "peerDependencies": {
27
26
  "react": ">=18.0.0",