gotcha-feedback 1.0.6 → 1.0.8

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
@@ -40,6 +40,8 @@ function FeatureCard() {
40
40
 
41
41
  - **Feedback Mode** - Star rating + text input
42
42
  - **Vote Mode** - Thumbs up/down
43
+ - **User Segmentation** - Analyze feedback by custom user attributes
44
+ - **Edit Support** - Users can update their previous submissions
43
45
  - **Customizable** - Themes, sizes, positions
44
46
  - **Accessible** - Full keyboard navigation and screen reader support
45
47
  - **Animated** - Smooth enter/exit animations with CSS transitions
@@ -68,6 +70,7 @@ function FeatureCard() {
68
70
  | `theme` | `'light' \| 'dark' \| 'auto'` | `'light'` | Color theme |
69
71
  | `showOnHover` | `boolean` | `true` | Only show on hover |
70
72
  | `promptText` | `string` | Mode-specific | Custom prompt text |
73
+ | `user` | `object` | - | User metadata for segmentation |
71
74
  | `onSubmit` | `function` | - | Callback after submission |
72
75
  | `onOpen` | `function` | - | Callback when modal opens |
73
76
  | `onClose` | `function` | - | Callback when modal closes |
@@ -104,7 +107,10 @@ function FeatureCard() {
104
107
 
105
108
  ### With User Data
106
109
 
110
+ Pass user metadata for segmentation and analytics. When a `user.id` is provided, users can also edit their previous submissions.
111
+
107
112
  ```tsx
113
+ // Set default user data at the provider level
108
114
  <GotchaProvider
109
115
  apiKey="your-api-key"
110
116
  defaultUser={{ plan: 'pro', role: 'admin' }}
@@ -113,6 +119,36 @@ function FeatureCard() {
113
119
  </GotchaProvider>
114
120
  ```
115
121
 
122
+ ```tsx
123
+ // Pass dynamic user data from your app
124
+ <Gotcha
125
+ elementId="feature-card"
126
+ user={{
127
+ id: currentUser.id, // Required for edit functionality
128
+ email: currentUser.email,
129
+ plan: currentUser.subscription,
130
+ age: currentUser.age,
131
+ country: currentUser.country,
132
+ company: currentUser.company
133
+ }}
134
+ />
135
+ ```
136
+
137
+ ```tsx
138
+ // Or capture device/browser info
139
+ <Gotcha
140
+ elementId="checkout"
141
+ user={{
142
+ id: user.id,
143
+ deviceType: isMobile ? 'mobile' : 'desktop',
144
+ browser: navigator.userAgent.includes('Chrome') ? 'Chrome' : 'Other',
145
+ locale: navigator.language
146
+ }}
147
+ />
148
+ ```
149
+
150
+ Pass any attributes relevant to your use case. Supported value types: `string`, `number`, `boolean`, or `null`.
151
+
116
152
  ### Custom Callbacks
117
153
 
118
154
  ```tsx
@@ -153,6 +189,58 @@ function MyComponent() {
153
189
  }
154
190
  ```
155
191
 
192
+ ## User Metadata & Segmentation
193
+
194
+ When you pass custom attributes in the `user` prop, Gotcha automatically tracks them and enables segmentation in your dashboard.
195
+
196
+ ### How It Works
197
+
198
+ 1. **Pass user attributes** when rendering the Gotcha component
199
+ 2. **View segmented analytics** in your dashboard under Analytics > Segments
200
+ 3. **Filter and compare** feedback by user attributes (plan, age, location, etc.)
201
+
202
+ ### Example Use Cases
203
+
204
+ ```tsx
205
+ // Segment by subscription plan
206
+ <Gotcha elementId="pricing" user={{ id: user.id, plan: user.plan }} />
207
+ // → Compare how free vs. pro users feel about pricing
208
+
209
+ // Segment by device type
210
+ <Gotcha elementId="checkout" user={{ id: user.id, device: isMobile ? 'mobile' : 'desktop' }} />
211
+ // → See if mobile users have different pain points
212
+
213
+ // Segment by country
214
+ <Gotcha elementId="shipping" user={{ id: user.id, country: user.country }} />
215
+ // → Understand regional differences in feedback
216
+
217
+ // Segment by user tenure
218
+ <Gotcha elementId="dashboard" user={{ id: user.id, accountAge: user.daysActive > 30 ? 'established' : 'new' }} />
219
+ // → Compare new user vs. power user sentiment
220
+ ```
221
+
222
+ In your dashboard under Analytics > Segments, you can group responses by any of these attributes.
223
+
224
+ ## Edit Previous Submissions
225
+
226
+ When you provide a `user.id`, users can update their previous feedback instead of creating duplicates.
227
+
228
+ ### How It Works
229
+
230
+ 1. User submits feedback for an element
231
+ 2. User returns later and opens the same feedback modal
232
+ 3. Their previous response is automatically loaded
233
+ 4. They can modify and re-submit to update their feedback
234
+
235
+ ```tsx
236
+ <Gotcha
237
+ elementId="feature-card"
238
+ user={{ id: 'user_123' }} // Required for edit functionality
239
+ />
240
+ ```
241
+
242
+ The modal will show "Update" instead of "Submit" when editing, and previous values will be pre-filled.
243
+
156
244
  ## TypeScript
157
245
 
158
246
  The package includes full TypeScript definitions:
package/dist/index.d.mts CHANGED
@@ -36,10 +36,21 @@ interface SubmitResponsePayload {
36
36
  }
37
37
  interface GotchaResponse {
38
38
  id: string;
39
- status: 'created' | 'duplicate';
39
+ status: 'created' | 'duplicate' | 'updated';
40
40
  createdAt: string;
41
41
  results?: PollResults;
42
42
  }
43
+ interface ExistingResponse {
44
+ id: string;
45
+ mode: ResponseMode;
46
+ content?: string | null;
47
+ title?: string | null;
48
+ rating?: number | null;
49
+ vote?: VoteType | null;
50
+ pollOptions?: string[] | null;
51
+ pollSelected?: string[] | null;
52
+ createdAt: string;
53
+ }
43
54
  interface PollResults {
44
55
  [option: string]: number;
45
56
  }
@@ -124,6 +135,14 @@ declare function useGotcha(): {
124
135
  /** The API client for manual submissions */
125
136
  client: {
126
137
  submitResponse(payload: Omit<SubmitResponsePayload, "context">): Promise<GotchaResponse>;
138
+ checkExistingResponse(elementId: string, userId: string): Promise<ExistingResponse | null>;
139
+ updateResponse(id: string, payload: {
140
+ content?: string;
141
+ title?: string;
142
+ rating?: number;
143
+ vote?: VoteType;
144
+ pollSelected?: string[];
145
+ }, userId?: string): Promise<GotchaResponse>;
127
146
  getBaseUrl(): string;
128
147
  };
129
148
  /** Whether Gotcha is globally disabled */
package/dist/index.d.ts CHANGED
@@ -36,10 +36,21 @@ interface SubmitResponsePayload {
36
36
  }
37
37
  interface GotchaResponse {
38
38
  id: string;
39
- status: 'created' | 'duplicate';
39
+ status: 'created' | 'duplicate' | 'updated';
40
40
  createdAt: string;
41
41
  results?: PollResults;
42
42
  }
43
+ interface ExistingResponse {
44
+ id: string;
45
+ mode: ResponseMode;
46
+ content?: string | null;
47
+ title?: string | null;
48
+ rating?: number | null;
49
+ vote?: VoteType | null;
50
+ pollOptions?: string[] | null;
51
+ pollSelected?: string[] | null;
52
+ createdAt: string;
53
+ }
43
54
  interface PollResults {
44
55
  [option: string]: number;
45
56
  }
@@ -124,6 +135,14 @@ declare function useGotcha(): {
124
135
  /** The API client for manual submissions */
125
136
  client: {
126
137
  submitResponse(payload: Omit<SubmitResponsePayload, "context">): Promise<GotchaResponse>;
138
+ checkExistingResponse(elementId: string, userId: string): Promise<ExistingResponse | null>;
139
+ updateResponse(id: string, payload: {
140
+ content?: string;
141
+ title?: string;
142
+ rating?: number;
143
+ vote?: VoteType;
144
+ pollSelected?: string[];
145
+ }, userId?: string): Promise<GotchaResponse>;
127
146
  getBaseUrl(): string;
128
147
  };
129
148
  /** Whether Gotcha is globally disabled */
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
- 'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime'),reactDom=require('react-dom');var pe="https://api.gotcha.cx/v1";var Z={ANONYMOUS_ID:"gotcha_anonymous_id"},w={POSITION:"top-right",SIZE:"md",THEME:"light",SHOW_ON_HOVER:true,TOUCH_BEHAVIOR:"always-visible",SUBMIT_TEXT:"Submit",THANK_YOU_MESSAGE:"Thanks for your feedback!"};var F={MAX_RETRIES:2,BASE_DELAY_MS:500,MAX_DELAY_MS:5e3};function me(){if(typeof window>"u")return `anon_${crypto.randomUUID()}`;let e=localStorage.getItem(Z.ANONYMOUS_ID);if(e)return e;let s=`anon_${crypto.randomUUID()}`;return localStorage.setItem(Z.ANONYMOUS_ID,s),s}var fe={maxRetries:F.MAX_RETRIES,baseDelayMs:F.BASE_DELAY_MS,maxDelayMs:F.MAX_DELAY_MS};async function Ie(e,s,r=fe,o=false){let c=null;for(let a=0;a<=r.maxRetries;a++){try{o&&a>0&&console.log(`[Gotcha] Retry attempt ${a}/${r.maxRetries}`);let t=await fetch(e,s);if(t.status>=400&&t.status<500&&t.status!==429||t.ok)return t;c=new Error(`HTTP ${t.status}`);}catch(t){c=t,o&&console.log(`[Gotcha] Network error: ${c.message}`);}if(a<r.maxRetries){let t=Math.min(r.baseDelayMs*Math.pow(2,a),r.maxDelayMs);await new Promise(d=>setTimeout(d,t));}}throw c}function he(e){let{apiKey:s,baseUrl:r=pe,debug:o=false}=e,c={"Content-Type":"application/json",Authorization:`Bearer ${s}`};async function a(t,d,n){let l=`${r}${d}`,i=crypto.randomUUID();o&&console.log(`[Gotcha] ${t} ${d}`,n);let p=await Ie(l,{method:t,headers:{...c,"Idempotency-Key":i},body:n?JSON.stringify(n):void 0},fe,o),u=await p.json();if(!p.ok){let f=u.error;throw o&&console.error(`[Gotcha] Error: ${f.code} - ${f.message}`),f}return o&&console.log("[Gotcha] Response:",u),u}return {async submitResponse(t){let d=t.user||{};d.id||(d.id=me());let n={...t,user:d,context:{url:typeof window<"u"?window.location.href:void 0,userAgent:typeof navigator<"u"?navigator.userAgent:void 0}};return a("POST","/responses",n)},getBaseUrl(){return r}}}var ve=react.createContext(null);function _e({apiKey:e,children:s,baseUrl:r,debug:o=false,disabled:c=false,defaultUser:a={}}){let[t,d]=react.useState(null),n=react.useMemo(()=>he({apiKey:e,baseUrl:r,debug:o}),[e,r,o]),l=react.useCallback(u=>{d(u);},[]),i=react.useCallback(()=>{d(null);},[]),p=react.useMemo(()=>({client:n,disabled:c,defaultUser:a,debug:o,activeModalId:t,openModal:l,closeModal:i}),[n,c,a,o,t,l,i]);return jsxRuntime.jsx(ve.Provider,{value:p,children:s})}function A(){let e=react.useContext(ve);if(!e)throw new Error("useGotchaContext must be used within a GotchaProvider");return e}function Se(e){let{client:s,defaultUser:r}=A(),[o,c]=react.useState(false),[a,t]=react.useState(null);return {submit:react.useCallback(async n=>{c(true),t(null);try{let l=await s.submitResponse({elementId:e.elementId,mode:e.mode,content:n.content,title:n.title,rating:n.rating,vote:n.vote,pollOptions:e.pollOptions,pollSelected:n.pollSelected,experimentId:e.experimentId,variant:e.variant,user:{...r,...e.user}});return e.onSuccess?.(l),l}catch(l){let i=l instanceof Error?l.message:"Something went wrong";throw t(i),e.onError?.(l instanceof Error?l:new Error(i)),l}finally{c(false);}},[s,r,e]),isLoading:o,error:a,clearError:()=>t(null)}}function $(...e){return e.filter(Boolean).join(" ")}var _=()=>typeof window>"u"?false:"ontouchstart"in window||navigator.maxTouchPoints>0,xe=(e,s)=>{let r={sm:{desktop:24,mobile:32},md:{desktop:32,mobile:36},lg:{desktop:40,mobile:40}};return s?r[e].mobile:r[e].desktop};function Ee({size:e,theme:s,customStyles:r,showOnHover:o,touchBehavior:c,onClick:a,isOpen:t,isParentHovered:d=false}){let[n,l]=react.useState(false),[i,p]=react.useState(false),[u,f]=react.useState("light");react.useEffect(()=>{l(_());let G=window.matchMedia("(prefers-color-scheme: dark)");f(G.matches?"dark":"light");let R=T=>f(T.matches?"dark":"light");return G.addEventListener("change",R),()=>G.removeEventListener("change",R)},[]);let y=t?true:!n&&o?d:n&&c==="tap-to-reveal"?i:true,g=()=>{if(n&&c==="tap-to-reveal"&&!i){p(true);return}a();},b=xe(e,n),m=s==="auto"?u:s,C={width:b,height:b,borderRadius:"50%",border:"none",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:m==="dark"?"#374151":"#c7d2dc",color:m==="dark"?"#e5e7eb":"#4b5563",boxShadow:"0 1px 3px rgba(0, 0, 0, 0.1)",transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:y?1:0,transform:y?"scale(1)":"scale(0.6)",pointerEvents:y?"auto":"none",...r?.button};return jsxRuntime.jsx("button",{type:"button",onClick:g,style:C,className:$("gotcha-button",t&&"gotcha-button--open"),"aria-label":"Give feedback on this feature","aria-expanded":t,"aria-haspopup":"dialog",children:jsxRuntime.jsx(Le,{size:b*.75})})}function Le({size:e}){return jsxRuntime.jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:jsxRuntime.jsx("text",{x:"50%",y:"50%",dominantBaseline:"central",textAnchor:"middle",fontSize:"16",fontWeight:"bold",fill:"currentColor",fontFamily:"system-ui, -apple-system, sans-serif",children:"G"})})}function Re({theme:e,placeholder:s,submitText:r,isLoading:o,onSubmit:c,customStyles:a}){let[t,d]=react.useState(""),[n,l]=react.useState(null),[i,p]=react.useState(false);react.useEffect(()=>{p(_());},[]);let u=e==="dark",f=b=>{b.preventDefault(),!(!t.trim()&&n===null)&&c({content:t.trim()||void 0,rating:n??void 0});},y={width:"100%",padding:i?"12px 14px":"10px 12px",border:`1px solid ${u?"#374151":"#d1d5db"}`,borderRadius:i?8:6,backgroundColor:u?"#374151":"#ffffff",color:u?"#f9fafb":"#111827",fontSize:i?16:14,resize:"vertical",minHeight:i?100:80,fontFamily:"inherit",...a?.input},g={width:"100%",padding:i?"14px 16px":"10px 16px",border:"none",borderRadius:i?8:6,backgroundColor:o?u?"#4b5563":"#9ca3af":"#6366f1",color:"#ffffff",fontSize:i?16:14,fontWeight:500,cursor:o?"not-allowed":"pointer",transition:"background-color 150ms ease",...a?.submitButton};return jsxRuntime.jsxs("form",{onSubmit:f,children:[jsxRuntime.jsx("div",{style:{marginBottom:i?16:12},children:jsxRuntime.jsx(Ne,{value:n,onChange:l,isDark:u,isTouch:i})}),jsxRuntime.jsx("textarea",{value:t,onChange:b=>d(b.target.value),placeholder:s||"Share your thoughts...",style:y,disabled:o,"aria-label":"Your feedback"}),jsxRuntime.jsx("button",{type:"submit",disabled:o||!t.trim()&&n===null,style:{...g,marginTop:12,opacity:!t.trim()&&n===null?.5:1},children:o?"Submitting...":r})]})}function Ne({value:e,onChange:s,isDark:r,isTouch:o}){let[c,a]=react.useState(null),t=o?32:20,d=o?6:2;return jsxRuntime.jsx("div",{style:{display:"flex",gap:o?8:4},role:"group","aria-label":"Rating",children:[1,2,3,4,5].map(n=>{let l=(c??e??0)>=n;return jsxRuntime.jsx("button",{type:"button",onClick:()=>s(n),onMouseEnter:()=>a(n),onMouseLeave:()=>a(null),"aria-label":`Rate ${n} out of 5`,"aria-pressed":e===n,style:{background:"none",border:"none",cursor:"pointer",padding:d,color:l?"#f59e0b":r?"#4b5563":"#d1d5db",transition:"color 150ms ease"},children:jsxRuntime.jsx("svg",{width:t,height:t,viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"})})},n)})})}function Te({theme:e,isLoading:s,onSubmit:r}){let[o,c]=react.useState(false);react.useEffect(()=>{c(_());},[]);let a=e==="dark",t={flex:1,padding:o?"16px 20px":"12px 16px",border:`1px solid ${a?"#374151":"#e5e7eb"}`,borderRadius:o?12:8,backgroundColor:a?"#374151":"#f9fafb",color:a?"#f9fafb":"#111827",fontSize:o?28:24,cursor:s?"not-allowed":"pointer",transition:"all 150ms ease",display:"flex",alignItems:"center",justifyContent:"center",gap:o?10:8},d=o?28:24;return jsxRuntime.jsxs("div",{style:{display:"flex",gap:o?16:12},role:"group","aria-label":"Vote",children:[jsxRuntime.jsxs("button",{type:"button",onClick:()=>r({vote:"up"}),disabled:s,style:t,"aria-label":"Vote up - I like this",children:[jsxRuntime.jsx(Ve,{size:d}),jsxRuntime.jsx("span",{style:{fontSize:o?16:14,fontWeight:500},children:"Like"})]}),jsxRuntime.jsxs("button",{type:"button",onClick:()=>r({vote:"down"}),disabled:s,style:t,"aria-label":"Vote down - I don't like this",children:[jsxRuntime.jsx(Ye,{size:d}),jsxRuntime.jsx("span",{style:{fontSize:o?16:14,fontWeight:500},children:"Dislike"})]})]})}function Ve({size:e=24}){return jsxRuntime.jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:jsxRuntime.jsx("path",{d:"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"})})}function Ye({size:e=24}){return jsxRuntime.jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:jsxRuntime.jsx("path",{d:"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"})})}function oe({mode:e,theme:s,customStyles:r,promptText:o,placeholder:c,submitText:a,thankYouMessage:t,isLoading:d,isSubmitted:n,error:l,onSubmit:i,onClose:p,anchorRect:u}){let f=react.useRef(null),y=react.useRef(null),[g,b]=react.useState(false),[m,C]=react.useState(false),[G,R]=react.useState("light");react.useEffect(()=>{C(window.innerWidth<640);let S=window.matchMedia("(prefers-color-scheme: dark)");R(S.matches?"dark":"light");let k=v=>R(v.matches?"dark":"light");return S.addEventListener("change",k),()=>S.removeEventListener("change",k)},[]),react.useEffect(()=>{let S=requestAnimationFrame(()=>b(true));return ()=>cancelAnimationFrame(S)},[]);let T=s==="auto"?G:s,h=T==="dark",P=(u?window.innerHeight-u.bottom:window.innerHeight/2)<280+20;react.useEffect(()=>{let S=f.current;if(!S)return;y.current?.focus();let k=v=>{if(v.key==="Escape"){p();return}if(v.key==="Tab"){let U=S.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),N=U[0],H=U[U.length-1];v.shiftKey&&document.activeElement===N?(v.preventDefault(),H?.focus()):!v.shiftKey&&document.activeElement===H&&(v.preventDefault(),N?.focus());}};return document.addEventListener("keydown",k),()=>document.removeEventListener("keydown",k)},[p]);let O=e==="vote"?"What do you think?":"What do you think of this feature?",D=m?20:16,L=m?{position:"fixed",left:"50%",top:"50%",width:"calc(100vw - 32px)",maxWidth:320,padding:D,borderRadius:12,backgroundColor:h?"#1f2937":"#ffffff",color:h?"#f9fafb":"#111827",boxShadow:"0 10px 25px rgba(0, 0, 0, 0.2)",border:`1px solid ${h?"#374151":"#e5e7eb"}`,zIndex:9999,transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:g?1:0,transform:g?"translate(-50%, -50%) scale(1)":"translate(-50%, -50%) scale(0.95)",...r?.modal}:{position:"absolute",left:"50%",width:320,padding:D,borderRadius:8,backgroundColor:h?"#1f2937":"#ffffff",color:h?"#f9fafb":"#111827",boxShadow:"0 10px 25px rgba(0, 0, 0, 0.15)",border:`1px solid ${h?"#374151":"#e5e7eb"}`,zIndex:9999,...P?{bottom:"100%",marginBottom:8}:{top:"100%",marginTop:8},transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:g?1:0,transform:g?"translateX(-50%) scale(1) translateY(0)":`translateX(-50%) scale(0.95) translateY(${P?"10px":"-10px"})`,...r?.modal};return jsxRuntime.jsxs("div",{ref:f,role:"dialog","aria-modal":"true","aria-labelledby":"gotcha-modal-title",style:L,className:$("gotcha-modal",h&&"gotcha-modal--dark"),children:[jsxRuntime.jsx("button",{ref:y,type:"button",onClick:p,"aria-label":"Close feedback form",style:{position:"absolute",top:m?12:8,right:m?12:8,width:m?36:24,height:m?36:24,border:"none",background:"none",cursor:"pointer",color:h?"#9ca3af":"#6b7280",display:"flex",alignItems:"center",justifyContent:"center",borderRadius:4},children:jsxRuntime.jsx("svg",{width:m?18:14,height:m?18:14,viewBox:"0 0 14 14",fill:"none",children:jsxRuntime.jsx("path",{d:"M1 1L13 13M1 13L13 1",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})})}),jsxRuntime.jsx("h2",{id:"gotcha-modal-title",style:{margin:"0 0 12px 0",fontSize:m?16:14,fontWeight:500,paddingRight:m?40:24},children:o||O}),n&&jsxRuntime.jsxs("div",{style:{textAlign:"center",padding:"20px 0",color:h?"#10b981":"#059669"},children:[jsxRuntime.jsx("svg",{width:"32",height:"32",viewBox:"0 0 24 24",fill:"none",style:{margin:"0 auto 8px"},children:jsxRuntime.jsx("path",{d:"M20 6L9 17L4 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})}),jsxRuntime.jsx("p",{style:{margin:0,fontSize:14},children:t})]}),l&&!n&&jsxRuntime.jsx("div",{style:{padding:8,marginBottom:12,borderRadius:4,backgroundColor:h?"#7f1d1d":"#fef2f2",color:h?"#fecaca":"#dc2626",fontSize:13},children:l}),!n&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[e==="feedback"&&jsxRuntime.jsx(Re,{theme:T,placeholder:c,submitText:a,isLoading:d,onSubmit:i,customStyles:r}),e==="vote"&&jsxRuntime.jsx(Te,{theme:T,isLoading:d,onSubmit:i})]}),jsxRuntime.jsxs("div",{"aria-live":"polite",className:"sr-only",style:{position:"absolute",left:-9999},children:[n&&"Thank you! Your feedback has been submitted.",l&&`Error: ${l}`]})]})}function Ke({elementId:e,user:s,mode:r="feedback",experimentId:o,variant:c,options:a,allowMultiple:t=false,showResults:d=true,position:n=w.POSITION,size:l=w.SIZE,theme:i=w.THEME,customStyles:p,visible:u=true,showOnHover:f=w.SHOW_ON_HOVER,touchBehavior:y=w.TOUCH_BEHAVIOR,promptText:g,placeholder:b,submitText:m=w.SUBMIT_TEXT,thankYouMessage:C=w.THANK_YOU_MESSAGE,onSubmit:G,onOpen:R,onClose:T,onError:h}){let{disabled:re,activeModalId:se,openModal:P,closeModal:O}=A(),[D,L]=react.useState(false),[S,k]=react.useState(false),[v,U]=react.useState(null),[N,H]=react.useState(false),V=react.useRef(null);react.useEffect(()=>{H(window.innerWidth<640);},[]);let Y=se===e;react.useEffect(()=>{if(!f)return;let x=V.current;if(!x)return;let B=x.parentElement;if(!B)return;let de=()=>k(true),ue=()=>k(false);return B.addEventListener("mouseenter",de),B.addEventListener("mouseleave",ue),()=>{B.removeEventListener("mouseenter",de),B.removeEventListener("mouseleave",ue);}},[f]);let{submit:ae,isLoading:ie,error:le}=Se({elementId:e,mode:r,experimentId:o,variant:c,pollOptions:a,user:s,onSuccess:x=>{L(true),G?.(x),setTimeout(()=>{O(),L(false);},2500);},onError:x=>{h?.(x);}}),Me=react.useCallback(()=>{V.current&&U(V.current.getBoundingClientRect()),P(e),R?.();},[e,P,R]),q=react.useCallback(()=>{O(),L(false),T?.();},[O,T]),ce=react.useCallback(x=>{ae(x);},[ae]);return re||!u?null:jsxRuntime.jsxs("div",{ref:V,style:{...{"top-right":{position:"absolute",top:0,right:0,transform:"translate(50%, -50%)"},"top-left":{position:"absolute",top:0,left:0,transform:"translate(-50%, -50%)"},"bottom-right":{position:"absolute",bottom:0,right:0,transform:"translate(50%, 50%)"},"bottom-left":{position:"absolute",bottom:0,left:0,transform:"translate(-50%, 50%)"},inline:{position:"relative",display:"inline-flex"}}[n],zIndex:Y?1e4:"auto"},className:"gotcha-container","data-gotcha-element":e,children:[jsxRuntime.jsx(Ee,{size:l,theme:i,customStyles:p,showOnHover:f,touchBehavior:y,onClick:Me,isOpen:Y,isParentHovered:S}),Y&&!N&&jsxRuntime.jsx(oe,{mode:r,theme:i,customStyles:p,promptText:g,placeholder:b,submitText:m,thankYouMessage:C,isLoading:ie,isSubmitted:D,error:le,onSubmit:ce,onClose:q,anchorRect:v||void 0}),Y&&N&&reactDom.createPortal(jsxRuntime.jsx("div",{style:{position:"fixed",inset:0,zIndex:99999,backgroundColor:"rgba(0, 0, 0, 0.3)"},onClick:q,"aria-hidden":"true",children:jsxRuntime.jsx("div",{onClick:x=>x.stopPropagation(),children:jsxRuntime.jsx(oe,{mode:r,theme:i,customStyles:p,promptText:g,placeholder:b,submitText:m,thankYouMessage:C,isLoading:ie,isSubmitted:D,error:le,onSubmit:ce,onClose:q,anchorRect:v||void 0})})}),document.body)]})}function Qe(){let{client:e,disabled:s,defaultUser:r,debug:o}=A();return {client:e,disabled:s,defaultUser:r,debug:o,submitFeedback:e.submitResponse.bind(e)}}
2
- exports.Gotcha=Ke;exports.GotchaProvider=_e;exports.useGotcha=Qe;//# sourceMappingURL=index.js.map
1
+ 'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime'),reactDom=require('react-dom');var Ee="https://api.gotcha.cx/v1";var se={ANONYMOUS_ID:"gotcha_anonymous_id"},T={POSITION:"top-right",SIZE:"md",THEME:"light",SHOW_ON_HOVER:true,TOUCH_BEHAVIOR:"always-visible",SUBMIT_TEXT:"Submit",THANK_YOU_MESSAGE:"Thanks for your feedback!"};var X={MAX_RETRIES:2,BASE_DELAY_MS:500,MAX_DELAY_MS:5e3};function Re(){if(typeof window>"u")return `anon_${crypto.randomUUID()}`;let e=localStorage.getItem(se.ANONYMOUS_ID);if(e)return e;let s=`anon_${crypto.randomUUID()}`;return localStorage.setItem(se.ANONYMOUS_ID,s),s}var Q={maxRetries:X.MAX_RETRIES,baseDelayMs:X.BASE_DELAY_MS,maxDelayMs:X.MAX_DELAY_MS};async function ie(e,s,i=Q,n=false){let p=null;for(let a=0;a<=i.maxRetries;a++){try{n&&a>0&&console.log(`[Gotcha] Retry attempt ${a}/${i.maxRetries}`);let t=await fetch(e,s);if(t.status>=400&&t.status<500&&t.status!==429||t.ok)return t;p=new Error(`HTTP ${t.status}`);}catch(t){p=t,n&&console.log(`[Gotcha] Network error: ${p.message}`);}if(a<i.maxRetries){let t=Math.min(i.baseDelayMs*Math.pow(2,a),i.maxDelayMs);await new Promise(u=>setTimeout(u,t));}}throw p}function ke(e){let{apiKey:s,baseUrl:i=Ee,debug:n=false}=e,p={"Content-Type":"application/json",Authorization:`Bearer ${s}`};async function a(t,u,r){let c=`${i}${u}`,l=crypto.randomUUID();n&&console.log(`[Gotcha] ${t} ${u}`,r);let f=await ie(c,{method:t,headers:{...p,"Idempotency-Key":l},body:r?JSON.stringify(r):void 0},Q,n),o=await f.json();if(!f.ok){let d=o.error;throw n&&console.error(`[Gotcha] Error: ${d.code} - ${d.message}`),d}return n&&console.log("[Gotcha] Response:",o),o}return {async submitResponse(t){let u=t.user||{};u.id||(u.id=Re());let r={...t,user:u,context:{url:typeof window<"u"?window.location.href:void 0,userAgent:typeof navigator<"u"?navigator.userAgent:void 0}};return a("POST","/responses",r)},async checkExistingResponse(t,u){let r=`${i}/responses/check?elementId=${encodeURIComponent(t)}&userId=${encodeURIComponent(u)}`;n&&console.log("[Gotcha] GET /responses/check");let c=await ie(r,{method:"GET",headers:p},Q,n),l=await c.json();if(!c.ok){let f=l.error;throw n&&console.error(`[Gotcha] Error: ${f.code} - ${f.message}`),f}return l.exists?(n&&console.log("[Gotcha] Found existing response:",l.response),l.response):null},async updateResponse(t,u,r){let c=`${i}/responses/${t}${r?`?userId=${encodeURIComponent(r)}`:""}`;n&&console.log(`[Gotcha] PATCH /responses/${t}`,u);let l=await ie(c,{method:"PATCH",headers:p,body:JSON.stringify(u)},Q,n),f=await l.json();if(!l.ok){let o=f.error;throw n&&console.error(`[Gotcha] Error: ${o.code} - ${o.message}`),o}return n&&console.log("[Gotcha] Response updated:",f),f},getBaseUrl(){return i}}}var Ge=react.createContext(null);function He({apiKey:e,children:s,baseUrl:i,debug:n=false,disabled:p=false,defaultUser:a={}}){let[t,u]=react.useState(null),r=react.useMemo(()=>ke({apiKey:e,baseUrl:i,debug:n}),[e,i,n]),c=react.useCallback(o=>{u(o);},[]),l=react.useCallback(()=>{u(null);},[]),f=react.useMemo(()=>({client:r,disabled:p,defaultUser:a,debug:n,activeModalId:t,openModal:c,closeModal:l}),[r,p,a,n,t,c,l]);return jsxRuntime.jsx(Ge.Provider,{value:f,children:s})}function _(){let e=react.useContext(Ge);if(!e)throw new Error("useGotchaContext must be used within a GotchaProvider");return e}function Ie(e){let{client:s,defaultUser:i}=_(),[n,p]=react.useState(false),[a,t]=react.useState(false),[u,r]=react.useState(null),[c,l]=react.useState(null);return react.useEffect(()=>{let o=e.user?.id||i?.id;if(!o){l(null);return}let d=false;return (async()=>{t(true);try{let h=await s.checkExistingResponse(e.elementId,o);d||l(h);}catch{d||l(null);}finally{d||t(false);}})(),()=>{d=true;}},[s,e.elementId,e.user?.id,i?.id]),{submit:react.useCallback(async o=>{p(true),r(null);try{let d=e.user?.id||i?.id,m;return c&&d?m=await s.updateResponse(c.id,{content:o.content,title:o.title,rating:o.rating,vote:o.vote,pollSelected:o.pollSelected},d):m=await s.submitResponse({elementId:e.elementId,mode:e.mode,content:o.content,title:o.title,rating:o.rating,vote:o.vote,pollOptions:e.pollOptions,pollSelected:o.pollSelected,experimentId:e.experimentId,variant:e.variant,user:{...i,...e.user}}),e.onSuccess?.(m),m}catch(d){let m=d instanceof Error?d.message:"Something went wrong";throw r(m),e.onError?.(d instanceof Error?d:new Error(m)),d}finally{p(false);}},[s,i,e,c]),isLoading:n,isCheckingExisting:a,error:u,existingResponse:c,isEditing:!!c,clearError:()=>r(null)}}function j(...e){return e.filter(Boolean).join(" ")}var O=()=>typeof window>"u"?false:"ontouchstart"in window||navigator.maxTouchPoints>0,Me=(e,s)=>{let i={sm:{desktop:24,mobile:32},md:{desktop:32,mobile:36},lg:{desktop:40,mobile:40}};return s?i[e].mobile:i[e].desktop};function Ce({size:e,theme:s,customStyles:i,showOnHover:n,touchBehavior:p,onClick:a,isOpen:t,isParentHovered:u=false}){let[r,c]=react.useState(false),[l,f]=react.useState(false),[o,d]=react.useState("light");react.useEffect(()=>{c(O());let g=window.matchMedia("(prefers-color-scheme: dark)");d(g.matches?"dark":"light");let I=P=>d(P.matches?"dark":"light");return g.addEventListener("change",I),()=>g.removeEventListener("change",I)},[]);let m=t?true:!r&&n?u:r&&p==="tap-to-reveal"?l:true,h=()=>{if(r&&p==="tap-to-reveal"&&!l){f(true);return}a();},b=Me(e,r),S=s==="auto"?o:s,R={width:b,height:b,borderRadius:"50%",border:"none",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:S==="dark"?"#374151":"#c7d2dc",color:S==="dark"?"#e5e7eb":"#4b5563",boxShadow:"0 1px 3px rgba(0, 0, 0, 0.1)",transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:m?1:0,transform:m?"scale(1)":"scale(0.6)",pointerEvents:m?"auto":"none",...i?.button};return jsxRuntime.jsx("button",{type:"button",onClick:h,style:R,className:j("gotcha-button",t&&"gotcha-button--open"),"aria-label":"Give feedback on this feature","aria-expanded":t,"aria-haspopup":"dialog",children:jsxRuntime.jsx(We,{size:b*.75})})}function We({size:e}){return jsxRuntime.jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:jsxRuntime.jsx("text",{x:"50%",y:"50%",dominantBaseline:"central",textAnchor:"middle",fontSize:"16",fontWeight:"bold",fill:"currentColor",fontFamily:"system-ui, -apple-system, sans-serif",children:"G"})})}function B({size:e=16,color:s="currentColor"}){return jsxRuntime.jsxs("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",style:{animation:"gotcha-spin 1s linear infinite"},children:[jsxRuntime.jsx("style",{children:`
2
+ @keyframes gotcha-spin {
3
+ from { transform: rotate(0deg); }
4
+ to { transform: rotate(360deg); }
5
+ }
6
+ `}),jsxRuntime.jsx("circle",{cx:"12",cy:"12",r:"10",stroke:s,strokeWidth:"3",strokeOpacity:"0.25"}),jsxRuntime.jsx("path",{d:"M12 2a10 10 0 0 1 10 10",stroke:s,strokeWidth:"3",strokeLinecap:"round"})]})}function _e({theme:e,placeholder:s,submitText:i,isLoading:n,onSubmit:p,customStyles:a,initialValues:t,isEditing:u=false}){let[r,c]=react.useState(t?.content||""),[l,f]=react.useState(t?.rating??null),[o,d]=react.useState(false);react.useEffect(()=>{d(O());},[]),react.useEffect(()=>{t?.content!==void 0&&c(t.content||""),t?.rating!==void 0&&f(t.rating??null);},[t?.content,t?.rating]);let m=e==="dark",h=R=>{R.preventDefault(),!(!r.trim()&&l===null)&&p({content:r.trim()||void 0,rating:l??void 0});},b={width:"100%",padding:o?"12px 14px":"10px 12px",border:`1px solid ${m?"#374151":"#d1d5db"}`,borderRadius:o?8:6,backgroundColor:m?"#374151":"#ffffff",color:m?"#f9fafb":"#111827",fontSize:o?16:14,resize:"vertical",minHeight:o?100:80,fontFamily:"inherit",...a?.input},S={width:"100%",padding:o?"14px 16px":"10px 16px",border:"none",borderRadius:o?8:6,backgroundColor:n?m?"#4b5563":"#9ca3af":"#6366f1",color:"#ffffff",fontSize:o?16:14,fontWeight:500,cursor:n?"not-allowed":"pointer",transition:"background-color 150ms ease",...a?.submitButton};return jsxRuntime.jsxs("form",{onSubmit:h,children:[jsxRuntime.jsx("div",{style:{marginBottom:o?16:12},children:jsxRuntime.jsx(Xe,{value:l,onChange:f,isDark:m,isTouch:o})}),jsxRuntime.jsx("textarea",{value:r,onChange:R=>c(R.target.value),placeholder:s||"Share your thoughts...",style:b,disabled:n,"aria-label":"Your feedback"}),jsxRuntime.jsxs("button",{type:"submit",disabled:n||!r.trim()&&l===null,style:{...S,marginTop:12,opacity:!r.trim()&&l===null?.5:1,display:"flex",alignItems:"center",justifyContent:"center",gap:8},children:[n&&jsxRuntime.jsx(B,{size:o?18:16,color:"#ffffff"}),n?u?"Updating...":"Submitting...":u?"Update":i]})]})}function Xe({value:e,onChange:s,isDark:i,isTouch:n}){let[p,a]=react.useState(null),t=n?32:20,u=n?6:2;return jsxRuntime.jsx("div",{style:{display:"flex",gap:n?8:4},role:"group","aria-label":"Rating",children:[1,2,3,4,5].map(r=>{let c=(p??e??0)>=r;return jsxRuntime.jsx("button",{type:"button",onClick:()=>s(r),onMouseEnter:()=>a(r),onMouseLeave:()=>a(null),"aria-label":`Rate ${r} out of 5`,"aria-pressed":e===r,style:{background:"none",border:"none",cursor:"pointer",padding:u,color:c?"#f59e0b":i?"#4b5563":"#d1d5db",transition:"color 150ms ease"},children:jsxRuntime.jsx("svg",{width:t,height:t,viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"})})},r)})})}function Oe({theme:e,isLoading:s,onSubmit:i,initialVote:n,isEditing:p=false}){let[a,t]=react.useState(false),[u,r]=react.useState(n||null),[c,l]=react.useState(n||null);react.useEffect(()=>{t(O());},[]),react.useEffect(()=>{n!==void 0&&(l(n),r(n));},[n]),react.useEffect(()=>{!s&&!p&&r(null);},[s,p]);let f=h=>{r(h),i({vote:h});},o=e==="dark",d=h=>{let b=c===h;return {flex:1,padding:a?"16px 20px":"12px 16px",border:b?`2px solid ${h==="up"?"#22c55e":"#ef4444"}`:`1px solid ${o?"#374151":"#e5e7eb"}`,borderRadius:a?12:8,backgroundColor:b?h==="up"?o?"#14532d":"#dcfce7":o?"#450a0a":"#fee2e2":o?"#374151":"#f9fafb",color:o?"#f9fafb":"#111827",fontSize:a?28:24,cursor:s?"not-allowed":"pointer",transition:"all 150ms ease",display:"flex",alignItems:"center",justifyContent:"center",gap:a?10:8}},m=a?28:24;return jsxRuntime.jsxs("div",{style:{display:"flex",gap:a?16:12},role:"group","aria-label":"Vote",children:[jsxRuntime.jsx("button",{type:"button",onClick:()=>f("up"),disabled:s,style:d("up"),"aria-label":"Vote up - I like this","aria-pressed":c==="up",children:s&&u==="up"?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(B,{size:m,color:o?"#f9fafb":"#111827"}),jsxRuntime.jsx("span",{style:{fontSize:a?16:14,fontWeight:500},children:p?"Updating...":"Sending..."})]}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(Qe,{size:m}),jsxRuntime.jsx("span",{style:{fontSize:a?16:14,fontWeight:500},children:c==="up"?"Liked":"Like"})]})}),jsxRuntime.jsx("button",{type:"button",onClick:()=>f("down"),disabled:s,style:d("down"),"aria-label":"Vote down - I don't like this","aria-pressed":c==="down",children:s&&u==="down"?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(B,{size:m,color:o?"#f9fafb":"#111827"}),jsxRuntime.jsx("span",{style:{fontSize:a?16:14,fontWeight:500},children:p?"Updating...":"Sending..."})]}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(qe,{size:m}),jsxRuntime.jsx("span",{style:{fontSize:a?16:14,fontWeight:500},children:c==="down"?"Disliked":"Dislike"})]})})]})}function Qe({size:e=24}){return jsxRuntime.jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:jsxRuntime.jsx("path",{d:"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"})})}function qe({size:e=24}){return jsxRuntime.jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:jsxRuntime.jsx("path",{d:"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"})})}function fe({mode:e,theme:s,customStyles:i,promptText:n,placeholder:p,submitText:a,thankYouMessage:t,isLoading:u,isSubmitted:r,error:c,existingResponse:l,isEditing:f=false,onSubmit:o,onClose:d,anchorRect:m}){let h=react.useRef(null),b=react.useRef(null),[S,R]=react.useState(false),[g,I]=react.useState(false),[P,Y]=react.useState("light");react.useEffect(()=>{I(window.innerWidth<640);let x=window.matchMedia("(prefers-color-scheme: dark)");Y(x.matches?"dark":"light");let M=E=>Y(E.matches?"dark":"light");return x.addEventListener("change",M),()=>x.removeEventListener("change",M)},[]),react.useEffect(()=>{let x=requestAnimationFrame(()=>R(true));return ()=>cancelAnimationFrame(x)},[]);let D=s==="auto"?P:s,y=D==="dark",U=(m?window.innerHeight-m.bottom:window.innerHeight/2)<280+20;react.useEffect(()=>{let x=h.current;if(!x)return;b.current?.focus();let M=E=>{if(E.key==="Escape"){d();return}if(E.key==="Tab"){let N=x.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),C=N[0],A=N[N.length-1];E.shiftKey&&document.activeElement===C?(E.preventDefault(),A?.focus()):!E.shiftKey&&document.activeElement===A&&(E.preventDefault(),C?.focus());}};return document.addEventListener("keydown",M),()=>document.removeEventListener("keydown",M)},[d]);let L=e==="vote"?"What do you think?":"What do you think of this feature?",F=g?20:16,W=g?{position:"fixed",left:"50%",top:"50%",width:"calc(100vw - 32px)",maxWidth:320,padding:F,borderRadius:12,backgroundColor:y?"#1f2937":"#ffffff",color:y?"#f9fafb":"#111827",boxShadow:"0 10px 25px rgba(0, 0, 0, 0.2)",border:`1px solid ${y?"#374151":"#e5e7eb"}`,zIndex:9999,transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:S?1:0,transform:S?"translate(-50%, -50%) scale(1)":"translate(-50%, -50%) scale(0.95)",...i?.modal}:{position:"absolute",left:"50%",width:320,padding:F,borderRadius:8,backgroundColor:y?"#1f2937":"#ffffff",color:y?"#f9fafb":"#111827",boxShadow:"0 10px 25px rgba(0, 0, 0, 0.15)",border:`1px solid ${y?"#374151":"#e5e7eb"}`,zIndex:9999,...U?{bottom:"100%",marginBottom:8}:{top:"100%",marginTop:8},transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:S?1:0,transform:S?"translateX(-50%) scale(1) translateY(0)":`translateX(-50%) scale(0.95) translateY(${U?"10px":"-10px"})`,...i?.modal};return jsxRuntime.jsxs("div",{ref:h,role:"dialog","aria-modal":"true","aria-labelledby":"gotcha-modal-title",style:W,className:j("gotcha-modal",y&&"gotcha-modal--dark"),children:[jsxRuntime.jsx("button",{ref:b,type:"button",onClick:d,"aria-label":"Close feedback form",style:{position:"absolute",top:g?12:8,right:g?12:8,width:g?36:24,height:g?36:24,border:"none",background:"none",cursor:"pointer",color:y?"#9ca3af":"#6b7280",display:"flex",alignItems:"center",justifyContent:"center",borderRadius:4},children:jsxRuntime.jsx("svg",{width:g?18:14,height:g?18:14,viewBox:"0 0 14 14",fill:"none",children:jsxRuntime.jsx("path",{d:"M1 1L13 13M1 13L13 1",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})})}),jsxRuntime.jsx("h2",{id:"gotcha-modal-title",style:{margin:"0 0 12px 0",fontSize:g?16:14,fontWeight:500,paddingRight:g?40:24},children:n||L}),r&&jsxRuntime.jsxs("div",{style:{textAlign:"center",padding:"20px 0",color:y?"#10b981":"#059669"},children:[jsxRuntime.jsx("svg",{width:"32",height:"32",viewBox:"0 0 24 24",fill:"none",style:{margin:"0 auto 8px"},children:jsxRuntime.jsx("path",{d:"M20 6L9 17L4 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})}),jsxRuntime.jsx("p",{style:{margin:0,fontSize:14},children:t})]}),c&&!r&&jsxRuntime.jsx("div",{style:{padding:8,marginBottom:12,borderRadius:4,backgroundColor:y?"#7f1d1d":"#fef2f2",color:y?"#fecaca":"#dc2626",fontSize:13},children:c}),!r&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[e==="feedback"&&jsxRuntime.jsx(_e,{theme:D,placeholder:p,submitText:a,isLoading:u,onSubmit:o,customStyles:i,initialValues:l?{content:l.content,rating:l.rating}:void 0,isEditing:f}),e==="vote"&&jsxRuntime.jsx(Oe,{theme:D,isLoading:u,onSubmit:o,initialVote:l?.vote||void 0,isEditing:f})]}),jsxRuntime.jsxs("div",{"aria-live":"polite",className:"sr-only",style:{position:"absolute",left:-9999},children:[r&&"Thank you! Your feedback has been submitted.",c&&`Error: ${c}`]})]})}function et({elementId:e,user:s,mode:i="feedback",experimentId:n,variant:p,options:a,allowMultiple:t=false,showResults:u=true,position:r=T.POSITION,size:c=T.SIZE,theme:l=T.THEME,customStyles:f,visible:o=true,showOnHover:d=T.SHOW_ON_HOVER,touchBehavior:m=T.TOUCH_BEHAVIOR,promptText:h,placeholder:b,submitText:S=T.SUBMIT_TEXT,thankYouMessage:R=T.THANK_YOU_MESSAGE,onSubmit:g,onOpen:I,onClose:P,onError:Y}){let{disabled:D,activeModalId:y,openModal:ne,closeModal:V}=_(),[U,L]=react.useState(false),[F,W]=react.useState(false),[x,M]=react.useState(null),[E,N]=react.useState(false),C=react.useRef(null);react.useEffect(()=>{N(window.innerWidth<640);},[]);let A=y===e;react.useEffect(()=>{if(!d)return;let k=C.current;if(!k)return;let z=k.parentElement;if(!z)return;let Se=()=>W(true),xe=()=>W(false);return z.addEventListener("mouseenter",Se),z.addEventListener("mouseleave",xe),()=>{z.removeEventListener("mouseenter",Se),z.removeEventListener("mouseleave",xe);}},[d]);let{submit:he,isLoading:ge,error:be,existingResponse:ve,isEditing:K}=Ie({elementId:e,mode:i,experimentId:n,variant:p,pollOptions:a,user:s,onSuccess:k=>{L(true),g?.(k),setTimeout(()=>{V(),L(false);},2500);},onError:k=>{Y?.(k);}}),Le=react.useCallback(()=>{C.current&&M(C.current.getBoundingClientRect()),ne(e),I?.();},[e,ne,I]),re=react.useCallback(()=>{V(),L(false),P?.();},[V,P]),ye=react.useCallback(k=>{he(k);},[he]);return D||!o?null:jsxRuntime.jsxs("div",{ref:C,style:{...{"top-right":{position:"absolute",top:0,right:0,transform:"translate(50%, -50%)"},"top-left":{position:"absolute",top:0,left:0,transform:"translate(-50%, -50%)"},"bottom-right":{position:"absolute",bottom:0,right:0,transform:"translate(50%, 50%)"},"bottom-left":{position:"absolute",bottom:0,left:0,transform:"translate(-50%, 50%)"},inline:{position:"relative",display:"inline-flex"}}[r],zIndex:A?1e4:"auto"},className:"gotcha-container","data-gotcha-element":e,children:[jsxRuntime.jsx(Ce,{size:c,theme:l,customStyles:f,showOnHover:d,touchBehavior:m,onClick:Le,isOpen:A,isParentHovered:F}),A&&!E&&jsxRuntime.jsx(fe,{mode:i,theme:l,customStyles:f,promptText:h,placeholder:b,submitText:S,thankYouMessage:K?"Your feedback has been updated!":R,isLoading:ge,isSubmitted:U,error:be,existingResponse:ve,isEditing:K,onSubmit:ye,onClose:re,anchorRect:x||void 0}),A&&E&&reactDom.createPortal(jsxRuntime.jsx("div",{style:{position:"fixed",inset:0,zIndex:99999,backgroundColor:"rgba(0, 0, 0, 0.3)"},onClick:re,"aria-hidden":"true",children:jsxRuntime.jsx("div",{onClick:k=>k.stopPropagation(),children:jsxRuntime.jsx(fe,{mode:i,theme:l,customStyles:f,promptText:h,placeholder:b,submitText:S,thankYouMessage:K?"Your feedback has been updated!":R,isLoading:ge,isSubmitted:U,error:be,existingResponse:ve,isEditing:K,onSubmit:ye,onClose:re,anchorRect:x||void 0})})}),document.body)]})}function ot(){let{client:e,disabled:s,defaultUser:i,debug:n}=_();return {client:e,disabled:s,defaultUser:i,debug:n,submitFeedback:e.submitResponse.bind(e)}}exports.Gotcha=et;exports.GotchaProvider=He;exports.useGotcha=ot;//# sourceMappingURL=index.js.map
3
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/utils/anonymous.ts","../src/api/client.ts","../src/components/GotchaProvider.tsx","../src/hooks/useSubmit.ts","../src/utils/cn.ts","../src/utils/device.ts","../src/components/GotchaButton.tsx","../src/components/modes/FeedbackMode.tsx","../src/components/modes/VoteMode.tsx","../src/components/GotchaModal.tsx","../src/components/Gotcha.tsx","../src/hooks/useGotcha.ts"],"names":["API_BASE_URL","STORAGE_KEYS","DEFAULTS","RETRY_CONFIG","getAnonymousId","stored","id","DEFAULT_RETRY_CONFIG","fetchWithRetry","url","options","config","debug","lastError","attempt","response","error","delay","resolve","createApiClient","apiKey","baseUrl","headers","request","method","endpoint","body","idempotencyKey","data","payload","user","fullPayload","GotchaContext","createContext","GotchaProvider","children","disabled","defaultUser","activeModalId","setActiveModalId","useState","client","useMemo","openModal","useCallback","elementId","closeModal","value","jsx","useGotchaContext","context","useContext","useSubmit","isLoading","setIsLoading","setError","err","errorMessage","cn","classes","isTouchDevice","getResponsiveSize","size","isTouch","sizes","GotchaButton","theme","customStyles","showOnHover","touchBehavior","onClick","isOpen","isParentHovered","setIsTouch","tapRevealed","setTapRevealed","systemTheme","setSystemTheme","useEffect","darkQuery","handler","e","shouldShow","handleClick","buttonSize","resolvedTheme","baseStyles","GotchaIcon","FeedbackMode","placeholder","submitText","onSubmit","content","setContent","rating","setRating","isDark","handleSubmit","inputStyles","buttonStyles","jsxs","StarRating","onChange","hovered","setHovered","starSize","buttonPadding","star","isFilled","VoteMode","buttonBase","iconSize","ThumbsUpIcon","ThumbsDownIcon","GotchaModal","mode","promptText","thankYouMessage","isSubmitted","onClose","anchorRect","modalRef","useRef","firstFocusableRef","isVisible","setIsVisible","isMobile","setIsMobile","timer","showAbove","modal","handleKeyDown","focusableElements","firstElement","lastElement","defaultPrompt","modalPadding","modalStyles","Fragment","Gotcha","experimentId","variant","allowMultiple","showResults","position","visible","onOpen","onError","setIsSubmitted","setIsParentHovered","setAnchorRect","containerRef","container","parent","handleMouseEnter","handleMouseLeave","submit","handleOpen","handleClose","createPortal","useGotcha"],"mappings":"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","file":"index.js","sourcesContent":["// API URLs\nexport const API_BASE_URL = 'https://api.gotcha.cx/v1';\nexport const API_STAGING_URL = 'https://api.staging.gotcha.cx/v1';\n\n// Error codes\nexport const ERROR_CODES = {\n INVALID_API_KEY: 'INVALID_API_KEY',\n ORIGIN_NOT_ALLOWED: 'ORIGIN_NOT_ALLOWED',\n RATE_LIMITED: 'RATE_LIMITED',\n QUOTA_EXCEEDED: 'QUOTA_EXCEEDED',\n INVALID_REQUEST: 'INVALID_REQUEST',\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n} as const;\n\n// Local storage keys\nexport const STORAGE_KEYS = {\n ANONYMOUS_ID: 'gotcha_anonymous_id',\n OFFLINE_QUEUE: 'gotcha_offline_queue',\n} as const;\n\n// Default values\nexport const DEFAULTS = {\n POSITION: 'top-right' as const,\n SIZE: 'md' as const,\n THEME: 'light' as const,\n SHOW_ON_HOVER: true,\n TOUCH_BEHAVIOR: 'always-visible' as const,\n SUBMIT_TEXT: 'Submit',\n THANK_YOU_MESSAGE: 'Thanks for your feedback!',\n} as const;\n\n// Size mappings (desktop/mobile in pixels)\nexport const SIZE_MAP = {\n sm: { desktop: 24, mobile: 44 },\n md: { desktop: 32, mobile: 44 },\n lg: { desktop: 40, mobile: 48 },\n} as const;\n\n// Retry config\nexport const RETRY_CONFIG = {\n MAX_RETRIES: 2,\n BASE_DELAY_MS: 500,\n MAX_DELAY_MS: 5000,\n} as const;\n","import { STORAGE_KEYS } from '../constants';\n\n/**\n * Get or create an anonymous user ID\n * Stored in localStorage for consistency across sessions\n */\nexport function getAnonymousId(): string {\n if (typeof window === 'undefined') {\n // SSR fallback - generate but don't persist\n return `anon_${crypto.randomUUID()}`;\n }\n\n const stored = localStorage.getItem(STORAGE_KEYS.ANONYMOUS_ID);\n if (stored) return stored;\n\n const id = `anon_${crypto.randomUUID()}`;\n localStorage.setItem(STORAGE_KEYS.ANONYMOUS_ID, id);\n return id;\n}\n\n/**\n * Clear the anonymous ID (useful for testing)\n */\nexport function clearAnonymousId(): void {\n if (typeof window !== 'undefined') {\n localStorage.removeItem(STORAGE_KEYS.ANONYMOUS_ID);\n }\n}\n","import { API_BASE_URL, RETRY_CONFIG } from '../constants';\nimport { SubmitResponsePayload, GotchaResponse, GotchaError } from '../types';\nimport { getAnonymousId } from '../utils/anonymous';\n\ninterface ApiClientConfig {\n apiKey: string;\n baseUrl?: string;\n debug?: boolean;\n}\n\ninterface RetryConfig {\n maxRetries: number;\n baseDelayMs: number;\n maxDelayMs: number;\n}\n\nconst DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxRetries: RETRY_CONFIG.MAX_RETRIES,\n baseDelayMs: RETRY_CONFIG.BASE_DELAY_MS,\n maxDelayMs: RETRY_CONFIG.MAX_DELAY_MS,\n};\n\n/**\n * Fetch with automatic retry and exponential backoff\n */\nasync function fetchWithRetry(\n url: string,\n options: RequestInit,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n debug: boolean = false\n): Promise<Response> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= config.maxRetries; attempt++) {\n try {\n if (debug && attempt > 0) {\n console.log(`[Gotcha] Retry attempt ${attempt}/${config.maxRetries}`);\n }\n\n const response = await fetch(url, options);\n\n // Don't retry client errors (4xx) except 429 (rate limit)\n if (response.status >= 400 && response.status < 500 && response.status !== 429) {\n return response;\n }\n\n // Success - return immediately\n if (response.ok) {\n return response;\n }\n\n lastError = new Error(`HTTP ${response.status}`);\n } catch (error) {\n // Network error - retry\n lastError = error as Error;\n if (debug) {\n console.log(`[Gotcha] Network error: ${lastError.message}`);\n }\n }\n\n // Don't delay after last attempt\n if (attempt < config.maxRetries) {\n const delay = Math.min(\n config.baseDelayMs * Math.pow(2, attempt),\n config.maxDelayMs\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n}\n\nexport function createApiClient(config: ApiClientConfig) {\n const { apiKey, baseUrl = API_BASE_URL, debug = false } = config;\n\n const headers = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n };\n\n async function request<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<T> {\n const url = `${baseUrl}${endpoint}`;\n const idempotencyKey = crypto.randomUUID();\n\n if (debug) {\n console.log(`[Gotcha] ${method} ${endpoint}`, body);\n }\n\n const response = await fetchWithRetry(\n url,\n {\n method,\n headers: {\n ...headers,\n 'Idempotency-Key': idempotencyKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n },\n DEFAULT_RETRY_CONFIG,\n debug\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = data.error as GotchaError;\n if (debug) {\n console.error(`[Gotcha] Error: ${error.code} - ${error.message}`);\n }\n throw error;\n }\n\n if (debug) {\n console.log(`[Gotcha] Response:`, data);\n }\n\n return data as T;\n }\n\n return {\n /**\n * Submit a response (feedback, vote, etc.)\n */\n async submitResponse(\n payload: Omit<SubmitResponsePayload, 'context'>\n ): Promise<GotchaResponse> {\n // Ensure user has an ID (anonymous if not provided)\n const user = payload.user || {};\n if (!user.id) {\n user.id = getAnonymousId();\n }\n\n const fullPayload: SubmitResponsePayload = {\n ...payload,\n user,\n context: {\n url: typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n },\n };\n\n return request<GotchaResponse>('POST', '/responses', fullPayload);\n },\n\n /**\n * Get the base URL (for debugging)\n */\n getBaseUrl(): string {\n return baseUrl;\n },\n };\n}\n\nexport type ApiClient = ReturnType<typeof createApiClient>;\n","import React, { createContext, useContext, useMemo, useState, useCallback } from 'react';\nimport { createApiClient, ApiClient } from '../api/client';\nimport { GotchaUser } from '../types';\n\nexport interface GotchaProviderProps {\n /** Your Gotcha API key */\n apiKey: string;\n /** React children */\n children: React.ReactNode;\n /** Override the API base URL (for testing/staging) */\n baseUrl?: string;\n /** Enable debug logging */\n debug?: boolean;\n /** Disable all Gotcha buttons globally */\n disabled?: boolean;\n /** Default user metadata applied to all submissions */\n defaultUser?: GotchaUser;\n}\n\nexport interface GotchaContextValue {\n client: ApiClient;\n disabled: boolean;\n defaultUser: GotchaUser;\n debug: boolean;\n // Modal management - only one open at a time\n activeModalId: string | null;\n openModal: (elementId: string) => void;\n closeModal: () => void;\n}\n\nconst GotchaContext = createContext<GotchaContextValue | null>(null);\n\nexport function GotchaProvider({\n apiKey,\n children,\n baseUrl,\n debug = false,\n disabled = false,\n defaultUser = {},\n}: GotchaProviderProps) {\n const [activeModalId, setActiveModalId] = useState<string | null>(null);\n\n const client = useMemo(\n () => createApiClient({ apiKey, baseUrl, debug }),\n [apiKey, baseUrl, debug]\n );\n\n const openModal = useCallback((elementId: string) => {\n setActiveModalId(elementId);\n }, []);\n\n const closeModal = useCallback(() => {\n setActiveModalId(null);\n }, []);\n\n const value: GotchaContextValue = useMemo(\n () => ({\n client,\n disabled,\n defaultUser,\n debug,\n activeModalId,\n openModal,\n closeModal,\n }),\n [client, disabled, defaultUser, debug, activeModalId, openModal, closeModal]\n );\n\n return (\n <GotchaContext.Provider value={value}>{children}</GotchaContext.Provider>\n );\n}\n\nexport function useGotchaContext(): GotchaContextValue {\n const context = useContext(GotchaContext);\n if (!context) {\n throw new Error('useGotchaContext must be used within a GotchaProvider');\n }\n return context;\n}\n","import { useState, useCallback } from 'react';\nimport { useGotchaContext } from '../components/GotchaProvider';\nimport { ResponseMode, GotchaUser, GotchaResponse, VoteType } from '../types';\n\ninterface UseSubmitOptions {\n elementId: string;\n mode: ResponseMode;\n experimentId?: string;\n variant?: string;\n pollOptions?: string[];\n user?: GotchaUser;\n onSuccess?: (response: GotchaResponse) => void;\n onError?: (error: Error) => void;\n}\n\ninterface SubmitData {\n content?: string;\n title?: string;\n rating?: number;\n vote?: VoteType;\n pollSelected?: string[];\n}\n\nexport function useSubmit(options: UseSubmitOptions) {\n const { client, defaultUser } = useGotchaContext();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const submit = useCallback(\n async (data: SubmitData) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const response = await client.submitResponse({\n elementId: options.elementId,\n mode: options.mode,\n content: data.content,\n title: data.title,\n rating: data.rating,\n vote: data.vote,\n pollOptions: options.pollOptions,\n pollSelected: data.pollSelected,\n experimentId: options.experimentId,\n variant: options.variant,\n user: { ...defaultUser, ...options.user },\n });\n\n options.onSuccess?.(response);\n return response;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Something went wrong';\n setError(errorMessage);\n options.onError?.(err instanceof Error ? err : new Error(errorMessage));\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [client, defaultUser, options]\n );\n\n return {\n submit,\n isLoading,\n error,\n clearError: () => setError(null),\n };\n}\n","/**\n * Simple class name utility\n * Combines class names, filtering out falsy values\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n","/**\n * Detect if the device supports touch\n */\nexport const isTouchDevice = (): boolean => {\n if (typeof window === 'undefined') return false;\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0;\n};\n\n/**\n * Get the appropriate size based on device type\n */\nexport const getResponsiveSize = (\n size: 'sm' | 'md' | 'lg',\n isTouch: boolean\n): number => {\n const sizes = {\n sm: { desktop: 24, mobile: 32 },\n md: { desktop: 32, mobile: 36 },\n lg: { desktop: 40, mobile: 40 },\n };\n\n return isTouch ? sizes[size].mobile : sizes[size].desktop;\n};\n","import React, { useState, useEffect } from 'react';\nimport { Size, Theme, GotchaStyles } from '../types';\nimport { cn } from '../utils/cn';\nimport { isTouchDevice, getResponsiveSize } from '../utils/device';\n\nexport interface GotchaButtonProps {\n size: Size;\n theme: Theme;\n customStyles?: GotchaStyles;\n showOnHover: boolean;\n touchBehavior: 'always-visible' | 'tap-to-reveal';\n onClick: () => void;\n isOpen: boolean;\n isParentHovered?: boolean;\n}\n\nexport function GotchaButton({\n size,\n theme,\n customStyles,\n showOnHover,\n touchBehavior,\n onClick,\n isOpen,\n isParentHovered = false,\n}: GotchaButtonProps) {\n const [isTouch, setIsTouch] = useState(false);\n const [tapRevealed, setTapRevealed] = useState(false);\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n // Detect system theme preference\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemTheme(darkQuery.matches ? 'dark' : 'light');\n\n // Listen for theme changes\n const handler = (e: MediaQueryListEvent) => setSystemTheme(e.matches ? 'dark' : 'light');\n darkQuery.addEventListener('change', handler);\n return () => darkQuery.removeEventListener('change', handler);\n }, []);\n\n // Determine visibility\n const shouldShow = (() => {\n // Always show if modal is open\n if (isOpen) return true;\n\n // Desktop with showOnHover: only show when parent is hovered\n if (!isTouch && showOnHover) {\n return isParentHovered;\n }\n\n // Touch device with tap-to-reveal: show only after first tap\n if (isTouch && touchBehavior === 'tap-to-reveal') {\n return tapRevealed;\n }\n\n // All other cases: always visible\n return true;\n })();\n\n const handleClick = () => {\n // For tap-to-reveal on touch: first tap reveals, second tap opens\n if (isTouch && touchBehavior === 'tap-to-reveal' && !tapRevealed) {\n setTapRevealed(true);\n return;\n }\n onClick();\n };\n\n const buttonSize = getResponsiveSize(size, isTouch);\n\n // Determine theme colors\n const resolvedTheme = theme === 'auto' ? systemTheme : theme;\n\n const baseStyles: React.CSSProperties = {\n width: buttonSize,\n height: buttonSize,\n borderRadius: '50%',\n border: 'none',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: resolvedTheme === 'dark' ? '#374151' : '#c7d2dc',\n color: resolvedTheme === 'dark' ? '#e5e7eb' : '#4b5563',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)',\n // CSS transition for animations\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: shouldShow ? 1 : 0,\n transform: shouldShow ? 'scale(1)' : 'scale(0.6)',\n pointerEvents: shouldShow ? 'auto' : 'none',\n ...customStyles?.button,\n };\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n style={baseStyles}\n className={cn('gotcha-button', isOpen && 'gotcha-button--open')}\n aria-label=\"Give feedback on this feature\"\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n >\n <GotchaIcon size={buttonSize * 0.75} />\n </button>\n );\n}\n\nfunction GotchaIcon({ size }: { size: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <text\n x=\"50%\"\n y=\"50%\"\n dominantBaseline=\"central\"\n textAnchor=\"middle\"\n fontSize=\"16\"\n fontWeight=\"bold\"\n fill=\"currentColor\"\n fontFamily=\"system-ui, -apple-system, sans-serif\"\n >\n G\n </text>\n </svg>\n );\n}\n","import React, { useState, useEffect } from 'react';\nimport { GotchaStyles } from '../../types';\nimport { isTouchDevice } from '../../utils/device';\n\ninterface FeedbackModeProps {\n theme: 'light' | 'dark' | 'custom';\n placeholder?: string;\n submitText: string;\n isLoading: boolean;\n onSubmit: (data: { content?: string; rating?: number }) => void;\n customStyles?: GotchaStyles;\n}\n\nexport function FeedbackMode({\n theme,\n placeholder,\n submitText,\n isLoading,\n onSubmit,\n customStyles,\n}: FeedbackModeProps) {\n const [content, setContent] = useState('');\n const [rating, setRating] = useState<number | null>(null);\n const [isTouch, setIsTouch] = useState(false);\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n }, []);\n\n const isDark = theme === 'dark';\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!content.trim() && rating === null) return;\n onSubmit({ content: content.trim() || undefined, rating: rating ?? undefined });\n };\n\n const inputStyles: React.CSSProperties = {\n width: '100%',\n padding: isTouch ? '12px 14px' : '10px 12px',\n border: `1px solid ${isDark ? '#374151' : '#d1d5db'}`,\n borderRadius: isTouch ? 8 : 6,\n backgroundColor: isDark ? '#374151' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n fontSize: isTouch ? 16 : 14, // 16px prevents iOS zoom on focus\n resize: 'vertical',\n minHeight: isTouch ? 100 : 80,\n fontFamily: 'inherit',\n ...customStyles?.input,\n };\n\n const buttonStyles: React.CSSProperties = {\n width: '100%',\n padding: isTouch ? '14px 16px' : '10px 16px',\n border: 'none',\n borderRadius: isTouch ? 8 : 6,\n backgroundColor: isLoading ? (isDark ? '#4b5563' : '#9ca3af') : '#6366f1',\n color: '#ffffff',\n fontSize: isTouch ? 16 : 14,\n fontWeight: 500,\n cursor: isLoading ? 'not-allowed' : 'pointer',\n transition: 'background-color 150ms ease',\n ...customStyles?.submitButton,\n };\n\n return (\n <form onSubmit={handleSubmit}>\n {/* Rating (optional) */}\n <div style={{ marginBottom: isTouch ? 16 : 12 }}>\n <StarRating value={rating} onChange={setRating} isDark={isDark} isTouch={isTouch} />\n </div>\n\n {/* Text input */}\n <textarea\n value={content}\n onChange={(e) => setContent(e.target.value)}\n placeholder={placeholder || 'Share your thoughts...'}\n style={inputStyles}\n disabled={isLoading}\n aria-label=\"Your feedback\"\n />\n\n {/* Submit button */}\n <button\n type=\"submit\"\n disabled={isLoading || (!content.trim() && rating === null)}\n style={{\n ...buttonStyles,\n marginTop: 12,\n opacity: (!content.trim() && rating === null) ? 0.5 : 1,\n }}\n >\n {isLoading ? 'Submitting...' : submitText}\n </button>\n </form>\n );\n}\n\ninterface StarRatingProps {\n value: number | null;\n onChange: (rating: number) => void;\n isDark: boolean;\n isTouch: boolean;\n}\n\nfunction StarRating({ value, onChange, isDark, isTouch }: StarRatingProps) {\n const [hovered, setHovered] = useState<number | null>(null);\n const starSize = isTouch ? 32 : 20;\n const buttonPadding = isTouch ? 6 : 2;\n\n return (\n <div\n style={{\n display: 'flex',\n gap: isTouch ? 8 : 4,\n }}\n role=\"group\"\n aria-label=\"Rating\"\n >\n {[1, 2, 3, 4, 5].map((star) => {\n const isFilled = (hovered ?? value ?? 0) >= star;\n return (\n <button\n key={star}\n type=\"button\"\n onClick={() => onChange(star)}\n onMouseEnter={() => setHovered(star)}\n onMouseLeave={() => setHovered(null)}\n aria-label={`Rate ${star} out of 5`}\n aria-pressed={value === star}\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: buttonPadding,\n color: isFilled ? '#f59e0b' : (isDark ? '#4b5563' : '#d1d5db'),\n transition: 'color 150ms ease',\n }}\n >\n <svg width={starSize} height={starSize} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\" />\n </svg>\n </button>\n );\n })}\n </div>\n );\n}\n","import React, { useEffect, useState } from 'react';\nimport { isTouchDevice } from '../../utils/device';\n\ninterface VoteModeProps {\n theme: 'light' | 'dark' | 'custom';\n isLoading: boolean;\n onSubmit: (data: { vote: 'up' | 'down' }) => void;\n}\n\nexport function VoteMode({ theme, isLoading, onSubmit }: VoteModeProps) {\n const [isTouch, setIsTouch] = useState(false);\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n }, []);\n\n const isDark = theme === 'dark';\n\n const buttonBase: React.CSSProperties = {\n flex: 1,\n padding: isTouch ? '16px 20px' : '12px 16px',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n borderRadius: isTouch ? 12 : 8,\n backgroundColor: isDark ? '#374151' : '#f9fafb',\n color: isDark ? '#f9fafb' : '#111827',\n fontSize: isTouch ? 28 : 24,\n cursor: isLoading ? 'not-allowed' : 'pointer',\n transition: 'all 150ms ease',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: isTouch ? 10 : 8,\n };\n\n const iconSize = isTouch ? 28 : 24;\n\n return (\n <div\n style={{\n display: 'flex',\n gap: isTouch ? 16 : 12,\n }}\n role=\"group\"\n aria-label=\"Vote\"\n >\n <button\n type=\"button\"\n onClick={() => onSubmit({ vote: 'up' })}\n disabled={isLoading}\n style={buttonBase}\n aria-label=\"Vote up - I like this\"\n >\n <ThumbsUpIcon size={iconSize} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>Like</span>\n </button>\n\n <button\n type=\"button\"\n onClick={() => onSubmit({ vote: 'down' })}\n disabled={isLoading}\n style={buttonBase}\n aria-label=\"Vote down - I don't like this\"\n >\n <ThumbsDownIcon size={iconSize} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>Dislike</span>\n </button>\n </div>\n );\n}\n\nfunction ThumbsUpIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\" />\n </svg>\n );\n}\n\nfunction ThumbsDownIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\" />\n </svg>\n );\n}\n","import React, { useRef, useEffect, useState } from 'react';\nimport { Theme, GotchaStyles, ResponseMode } from '../types';\nimport { cn } from '../utils/cn';\nimport { FeedbackMode } from './modes/FeedbackMode';\nimport { VoteMode } from './modes/VoteMode';\n\nexport interface GotchaModalProps {\n mode: ResponseMode;\n theme: Theme;\n customStyles?: GotchaStyles;\n promptText?: string;\n placeholder?: string;\n submitText: string;\n thankYouMessage: string;\n // State\n isLoading: boolean;\n isSubmitted: boolean;\n error: string | null;\n // Handlers\n onSubmit: (data: { content?: string; rating?: number; vote?: 'up' | 'down' }) => void;\n onClose: () => void;\n // Position info from parent\n anchorRect?: DOMRect;\n}\n\nexport function GotchaModal({\n mode,\n theme,\n customStyles,\n promptText,\n placeholder,\n submitText,\n thankYouMessage,\n isLoading,\n isSubmitted,\n error,\n onSubmit,\n onClose,\n anchorRect,\n}: GotchaModalProps) {\n const modalRef = useRef<HTMLDivElement>(null);\n const firstFocusableRef = useRef<HTMLButtonElement>(null);\n const [isVisible, setIsVisible] = useState(false);\n const [isMobile, setIsMobile] = useState(false);\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');\n\n // Detect mobile and system theme after mount (SSR-safe)\n useEffect(() => {\n setIsMobile(window.innerWidth < 640);\n\n // Detect system theme preference\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemTheme(darkQuery.matches ? 'dark' : 'light');\n\n // Listen for theme changes\n const handler = (e: MediaQueryListEvent) => setSystemTheme(e.matches ? 'dark' : 'light');\n darkQuery.addEventListener('change', handler);\n return () => darkQuery.removeEventListener('change', handler);\n }, []);\n\n // Trigger animation after mount\n useEffect(() => {\n const timer = requestAnimationFrame(() => setIsVisible(true));\n return () => cancelAnimationFrame(timer);\n }, []);\n\n // Resolve theme\n const resolvedTheme = theme === 'auto' ? systemTheme : theme;\n\n const isDark = resolvedTheme === 'dark';\n\n // Determine if modal should appear above or below\n const modalHeight = 280; // approximate modal height\n const spaceBelow = anchorRect\n ? window.innerHeight - anchorRect.bottom\n : window.innerHeight / 2;\n const showAbove = spaceBelow < modalHeight + 20;\n\n // Focus trap\n useEffect(() => {\n const modal = modalRef.current;\n if (!modal) return;\n\n // Focus first element\n firstFocusableRef.current?.focus();\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose();\n return;\n }\n\n if (e.key === 'Tab') {\n const focusableElements = modal.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n );\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault();\n lastElement?.focus();\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault();\n firstElement?.focus();\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [onClose]);\n\n const defaultPrompt = mode === 'vote'\n ? 'What do you think?'\n : 'What do you think of this feature?';\n\n // Responsive sizing - on mobile, use fixed positioning centered on screen\n const modalPadding = isMobile ? 20 : 16;\n\n const modalStyles: React.CSSProperties = isMobile\n ? {\n // Mobile: fixed position, centered on screen\n position: 'fixed',\n left: '50%',\n top: '50%',\n width: 'calc(100vw - 32px)',\n maxWidth: 320,\n padding: modalPadding,\n borderRadius: 12,\n backgroundColor: isDark ? '#1f2937' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.2)',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n zIndex: 9999,\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: isVisible ? 1 : 0,\n transform: isVisible\n ? 'translate(-50%, -50%) scale(1)'\n : 'translate(-50%, -50%) scale(0.95)',\n ...customStyles?.modal,\n }\n : {\n // Desktop: absolute position relative to button\n position: 'absolute',\n left: '50%',\n width: 320,\n padding: modalPadding,\n borderRadius: 8,\n backgroundColor: isDark ? '#1f2937' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15)',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n zIndex: 9999,\n ...(showAbove\n ? { bottom: '100%', marginBottom: 8 }\n : { top: '100%', marginTop: 8 }),\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: isVisible ? 1 : 0,\n transform: isVisible\n ? 'translateX(-50%) scale(1) translateY(0)'\n : `translateX(-50%) scale(0.95) translateY(${showAbove ? '10px' : '-10px'})`,\n ...customStyles?.modal,\n };\n\n return (\n <div\n ref={modalRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"gotcha-modal-title\"\n style={modalStyles}\n className={cn('gotcha-modal', isDark && 'gotcha-modal--dark')}\n >\n {/* Close button - larger on touch devices */}\n <button\n ref={firstFocusableRef}\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close feedback form\"\n style={{\n position: 'absolute',\n top: isMobile ? 12 : 8,\n right: isMobile ? 12 : 8,\n width: isMobile ? 36 : 24,\n height: isMobile ? 36 : 24,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: isDark ? '#9ca3af' : '#6b7280',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 4,\n }}\n >\n <svg width={isMobile ? 18 : 14} height={isMobile ? 18 : 14} viewBox=\"0 0 14 14\" fill=\"none\">\n <path\n d=\"M1 1L13 13M1 13L13 1\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n\n {/* Title */}\n <h2\n id=\"gotcha-modal-title\"\n style={{\n margin: '0 0 12px 0',\n fontSize: isMobile ? 16 : 14,\n fontWeight: 500,\n paddingRight: isMobile ? 40 : 24,\n }}\n >\n {promptText || defaultPrompt}\n </h2>\n\n {/* Success state */}\n {isSubmitted && (\n <div\n style={{\n textAlign: 'center',\n padding: '20px 0',\n color: isDark ? '#10b981' : '#059669',\n }}\n >\n <svg\n width=\"32\"\n height=\"32\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n style={{ margin: '0 auto 8px' }}\n >\n <path\n d=\"M20 6L9 17L4 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n <p style={{ margin: 0, fontSize: 14 }}>{thankYouMessage}</p>\n </div>\n )}\n\n {/* Error state */}\n {error && !isSubmitted && (\n <div\n style={{\n padding: 8,\n marginBottom: 12,\n borderRadius: 4,\n backgroundColor: isDark ? '#7f1d1d' : '#fef2f2',\n color: isDark ? '#fecaca' : '#dc2626',\n fontSize: 13,\n }}\n >\n {error}\n </div>\n )}\n\n {/* Form content based on mode */}\n {!isSubmitted && (\n <>\n {mode === 'feedback' && (\n <FeedbackMode\n theme={resolvedTheme}\n placeholder={placeholder}\n submitText={submitText}\n isLoading={isLoading}\n onSubmit={onSubmit}\n customStyles={customStyles}\n />\n )}\n {mode === 'vote' && (\n <VoteMode\n theme={resolvedTheme}\n isLoading={isLoading}\n onSubmit={onSubmit}\n />\n )}\n </>\n )}\n\n {/* Screen reader announcement */}\n <div aria-live=\"polite\" className=\"sr-only\" style={{ position: 'absolute', left: -9999 }}>\n {isSubmitted && 'Thank you! Your feedback has been submitted.'}\n {error && `Error: ${error}`}\n </div>\n </div>\n );\n}\n","import React, { useState, useCallback, useEffect, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n ResponseMode,\n GotchaUser,\n Position,\n Size,\n Theme,\n TouchBehavior,\n GotchaStyles,\n GotchaResponse,\n GotchaError,\n} from '../types';\nimport { DEFAULTS } from '../constants';\nimport { useGotchaContext } from './GotchaProvider';\nimport { useSubmit } from '../hooks/useSubmit';\nimport { GotchaButton } from './GotchaButton';\nimport { GotchaModal } from './GotchaModal';\n\nexport interface GotchaProps {\n /** Unique identifier for this element */\n elementId: string;\n\n // User data\n /** User metadata for segmentation */\n user?: GotchaUser;\n\n // Behavior\n /** Feedback mode */\n mode?: ResponseMode;\n /** Required if mode is 'ab' */\n experimentId?: string;\n /** Current A/B variant shown to user */\n variant?: string;\n\n // Poll mode specific (Phase 2)\n /** Required if mode is 'poll' (2-6 options) */\n options?: string[];\n /** Allow selecting multiple options */\n allowMultiple?: boolean;\n /** Show results after voting */\n showResults?: boolean;\n\n // Appearance\n /** Button position relative to parent */\n position?: Position;\n /** Button size */\n size?: Size;\n /** Color theme */\n theme?: Theme;\n /** Custom style overrides */\n customStyles?: GotchaStyles;\n /** Control visibility programmatically */\n visible?: boolean;\n /** Only show when parent is hovered (default: true) */\n showOnHover?: boolean;\n /** Mobile behavior (default: 'always-visible') */\n touchBehavior?: TouchBehavior;\n\n // Content\n /** Custom prompt text */\n promptText?: string;\n /** Input placeholder text */\n placeholder?: string;\n /** Submit button text */\n submitText?: string;\n /** Post-submission message */\n thankYouMessage?: string;\n\n // Callbacks\n /** Called after successful submission */\n onSubmit?: (response: GotchaResponse) => void;\n /** Called when modal opens */\n onOpen?: () => void;\n /** Called when modal closes */\n onClose?: () => void;\n /** Called on error */\n onError?: (error: GotchaError) => void;\n}\n\nexport function Gotcha({\n elementId,\n user,\n mode = 'feedback',\n experimentId,\n variant,\n options,\n allowMultiple = false,\n showResults = true,\n position = DEFAULTS.POSITION,\n size = DEFAULTS.SIZE,\n theme = DEFAULTS.THEME,\n customStyles,\n visible = true,\n showOnHover = DEFAULTS.SHOW_ON_HOVER,\n touchBehavior = DEFAULTS.TOUCH_BEHAVIOR,\n promptText,\n placeholder,\n submitText = DEFAULTS.SUBMIT_TEXT,\n thankYouMessage = DEFAULTS.THANK_YOU_MESSAGE,\n onSubmit,\n onOpen,\n onClose,\n onError,\n}: GotchaProps) {\n const { disabled, activeModalId, openModal, closeModal } = useGotchaContext();\n const [isSubmitted, setIsSubmitted] = useState(false);\n const [isParentHovered, setIsParentHovered] = useState(false);\n const [anchorRect, setAnchorRect] = useState<DOMRect | null>(null);\n const [isMobile, setIsMobile] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Detect mobile for portal rendering (SSR-safe)\n useEffect(() => {\n setIsMobile(window.innerWidth < 640);\n }, []);\n\n // This instance's modal is open if activeModalId matches our elementId\n const isOpen = activeModalId === elementId;\n\n // Attach hover listeners to the parent element (not the button container)\n useEffect(() => {\n if (!showOnHover) return;\n\n const container = containerRef.current;\n if (!container) return;\n\n // Find the parent element with position: relative\n const parent = container.parentElement;\n if (!parent) return;\n\n const handleMouseEnter = () => setIsParentHovered(true);\n const handleMouseLeave = () => setIsParentHovered(false);\n\n parent.addEventListener('mouseenter', handleMouseEnter);\n parent.addEventListener('mouseleave', handleMouseLeave);\n\n return () => {\n parent.removeEventListener('mouseenter', handleMouseEnter);\n parent.removeEventListener('mouseleave', handleMouseLeave);\n };\n }, [showOnHover]);\n\n const { submit, isLoading, error } = useSubmit({\n elementId,\n mode,\n experimentId,\n variant,\n pollOptions: options,\n user,\n onSuccess: (response) => {\n setIsSubmitted(true);\n onSubmit?.(response);\n // Auto-close after 2.5 seconds\n setTimeout(() => {\n closeModal();\n setIsSubmitted(false);\n }, 2500);\n },\n onError: (err) => {\n onError?.(err as unknown as GotchaError);\n },\n });\n\n const handleOpen = useCallback(() => {\n if (containerRef.current) {\n setAnchorRect(containerRef.current.getBoundingClientRect());\n }\n openModal(elementId);\n onOpen?.();\n }, [elementId, openModal, onOpen]);\n\n const handleClose = useCallback(() => {\n closeModal();\n setIsSubmitted(false);\n onClose?.();\n }, [closeModal, onClose]);\n\n const handleSubmit = useCallback(\n (data: { content?: string; rating?: number; vote?: 'up' | 'down' }) => {\n submit(data);\n },\n [submit]\n );\n\n // Don't render if disabled or not visible\n if (disabled || !visible) return null;\n\n // Position styles\n const positionStyles: Record<Position, React.CSSProperties> = {\n 'top-right': { position: 'absolute', top: 0, right: 0, transform: 'translate(50%, -50%)' },\n 'top-left': { position: 'absolute', top: 0, left: 0, transform: 'translate(-50%, -50%)' },\n 'bottom-right': { position: 'absolute', bottom: 0, right: 0, transform: 'translate(50%, 50%)' },\n 'bottom-left': { position: 'absolute', bottom: 0, left: 0, transform: 'translate(-50%, 50%)' },\n 'inline': { position: 'relative', display: 'inline-flex' },\n };\n\n return (\n <div\n ref={containerRef}\n style={{\n ...positionStyles[position],\n zIndex: isOpen ? 10000 : 'auto',\n }}\n className=\"gotcha-container\"\n data-gotcha-element={elementId}\n >\n <GotchaButton\n size={size}\n theme={theme}\n customStyles={customStyles}\n showOnHover={showOnHover}\n touchBehavior={touchBehavior}\n onClick={handleOpen}\n isOpen={isOpen}\n isParentHovered={isParentHovered}\n />\n\n {isOpen && !isMobile && (\n <GotchaModal\n mode={mode}\n theme={theme}\n customStyles={customStyles}\n promptText={promptText}\n placeholder={placeholder}\n submitText={submitText}\n thankYouMessage={thankYouMessage}\n isLoading={isLoading}\n isSubmitted={isSubmitted}\n error={error}\n onSubmit={handleSubmit}\n onClose={handleClose}\n anchorRect={anchorRect || undefined}\n />\n )}\n\n {/* On mobile, render modal via portal to escape parent transform */}\n {isOpen && isMobile && createPortal(\n <div\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 99999,\n backgroundColor: 'rgba(0, 0, 0, 0.3)',\n }}\n onClick={handleClose}\n aria-hidden=\"true\"\n >\n <div onClick={(e) => e.stopPropagation()}>\n <GotchaModal\n mode={mode}\n theme={theme}\n customStyles={customStyles}\n promptText={promptText}\n placeholder={placeholder}\n submitText={submitText}\n thankYouMessage={thankYouMessage}\n isLoading={isLoading}\n isSubmitted={isSubmitted}\n error={error}\n onSubmit={handleSubmit}\n onClose={handleClose}\n anchorRect={anchorRect || undefined}\n />\n </div>\n </div>,\n document.body\n )}\n </div>\n );\n}\n","import { useGotchaContext } from '../components/GotchaProvider';\n\n/**\n * Hook to access Gotcha context\n * Must be used within a GotchaProvider\n */\nexport function useGotcha() {\n const { client, disabled, defaultUser, debug } = useGotchaContext();\n\n return {\n /** The API client for manual submissions */\n client,\n /** Whether Gotcha is globally disabled */\n disabled,\n /** Default user metadata */\n defaultUser,\n /** Whether debug mode is enabled */\n debug,\n /** Submit feedback programmatically */\n submitFeedback: client.submitResponse.bind(client),\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/utils/anonymous.ts","../src/api/client.ts","../src/components/GotchaProvider.tsx","../src/hooks/useSubmit.ts","../src/utils/cn.ts","../src/utils/device.ts","../src/components/GotchaButton.tsx","../src/components/Spinner.tsx","../src/components/modes/FeedbackMode.tsx","../src/components/modes/VoteMode.tsx","../src/components/GotchaModal.tsx","../src/components/Gotcha.tsx","../src/hooks/useGotcha.ts"],"names":["API_BASE_URL","STORAGE_KEYS","DEFAULTS","RETRY_CONFIG","getAnonymousId","stored","id","DEFAULT_RETRY_CONFIG","fetchWithRetry","url","options","config","debug","lastError","attempt","response","error","delay","resolve","createApiClient","apiKey","baseUrl","headers","request","method","endpoint","body","idempotencyKey","data","payload","user","fullPayload","elementId","userId","GotchaContext","createContext","GotchaProvider","children","disabled","defaultUser","activeModalId","setActiveModalId","useState","client","useMemo","openModal","useCallback","closeModal","value","jsx","useGotchaContext","context","useContext","useSubmit","isLoading","setIsLoading","isCheckingExisting","setIsCheckingExisting","setError","existingResponse","setExistingResponse","useEffect","cancelled","existing","err","errorMessage","cn","classes","isTouchDevice","getResponsiveSize","size","isTouch","sizes","GotchaButton","theme","customStyles","showOnHover","touchBehavior","onClick","isOpen","isParentHovered","setIsTouch","tapRevealed","setTapRevealed","systemTheme","setSystemTheme","darkQuery","handler","e","shouldShow","handleClick","buttonSize","resolvedTheme","baseStyles","GotchaIcon","Spinner","color","jsxs","FeedbackMode","placeholder","submitText","onSubmit","initialValues","isEditing","content","setContent","rating","setRating","isDark","handleSubmit","inputStyles","buttonStyles","StarRating","onChange","hovered","setHovered","starSize","buttonPadding","star","isFilled","VoteMode","initialVote","activeVote","setActiveVote","previousVote","setPreviousVote","handleVote","vote","getButtonStyles","voteType","isSelected","iconSize","Fragment","ThumbsUpIcon","ThumbsDownIcon","GotchaModal","mode","promptText","thankYouMessage","isSubmitted","onClose","anchorRect","modalRef","useRef","firstFocusableRef","isVisible","setIsVisible","isMobile","setIsMobile","timer","showAbove","modal","handleKeyDown","focusableElements","firstElement","lastElement","defaultPrompt","modalPadding","modalStyles","Gotcha","experimentId","variant","allowMultiple","showResults","position","visible","onOpen","onError","setIsSubmitted","setIsParentHovered","setAnchorRect","containerRef","container","parent","handleMouseEnter","handleMouseLeave","submit","handleOpen","handleClose","createPortal","useGotcha"],"mappings":"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","file":"index.js","sourcesContent":["// API URLs\nexport const API_BASE_URL = 'https://api.gotcha.cx/v1';\nexport const API_STAGING_URL = 'https://api.staging.gotcha.cx/v1';\n\n// Error codes\nexport const ERROR_CODES = {\n INVALID_API_KEY: 'INVALID_API_KEY',\n ORIGIN_NOT_ALLOWED: 'ORIGIN_NOT_ALLOWED',\n RATE_LIMITED: 'RATE_LIMITED',\n QUOTA_EXCEEDED: 'QUOTA_EXCEEDED',\n INVALID_REQUEST: 'INVALID_REQUEST',\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n} as const;\n\n// Local storage keys\nexport const STORAGE_KEYS = {\n ANONYMOUS_ID: 'gotcha_anonymous_id',\n OFFLINE_QUEUE: 'gotcha_offline_queue',\n} as const;\n\n// Default values\nexport const DEFAULTS = {\n POSITION: 'top-right' as const,\n SIZE: 'md' as const,\n THEME: 'light' as const,\n SHOW_ON_HOVER: true,\n TOUCH_BEHAVIOR: 'always-visible' as const,\n SUBMIT_TEXT: 'Submit',\n THANK_YOU_MESSAGE: 'Thanks for your feedback!',\n} as const;\n\n// Size mappings (desktop/mobile in pixels)\nexport const SIZE_MAP = {\n sm: { desktop: 24, mobile: 44 },\n md: { desktop: 32, mobile: 44 },\n lg: { desktop: 40, mobile: 48 },\n} as const;\n\n// Retry config\nexport const RETRY_CONFIG = {\n MAX_RETRIES: 2,\n BASE_DELAY_MS: 500,\n MAX_DELAY_MS: 5000,\n} as const;\n","import { STORAGE_KEYS } from '../constants';\n\n/**\n * Get or create an anonymous user ID\n * Stored in localStorage for consistency across sessions\n */\nexport function getAnonymousId(): string {\n if (typeof window === 'undefined') {\n // SSR fallback - generate but don't persist\n return `anon_${crypto.randomUUID()}`;\n }\n\n const stored = localStorage.getItem(STORAGE_KEYS.ANONYMOUS_ID);\n if (stored) return stored;\n\n const id = `anon_${crypto.randomUUID()}`;\n localStorage.setItem(STORAGE_KEYS.ANONYMOUS_ID, id);\n return id;\n}\n\n/**\n * Clear the anonymous ID (useful for testing)\n */\nexport function clearAnonymousId(): void {\n if (typeof window !== 'undefined') {\n localStorage.removeItem(STORAGE_KEYS.ANONYMOUS_ID);\n }\n}\n","import { API_BASE_URL, RETRY_CONFIG } from '../constants';\nimport { SubmitResponsePayload, GotchaResponse, GotchaError, ExistingResponse, VoteType } from '../types';\nimport { getAnonymousId } from '../utils/anonymous';\n\ninterface ApiClientConfig {\n apiKey: string;\n baseUrl?: string;\n debug?: boolean;\n}\n\ninterface RetryConfig {\n maxRetries: number;\n baseDelayMs: number;\n maxDelayMs: number;\n}\n\nconst DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxRetries: RETRY_CONFIG.MAX_RETRIES,\n baseDelayMs: RETRY_CONFIG.BASE_DELAY_MS,\n maxDelayMs: RETRY_CONFIG.MAX_DELAY_MS,\n};\n\n/**\n * Fetch with automatic retry and exponential backoff\n */\nasync function fetchWithRetry(\n url: string,\n options: RequestInit,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n debug: boolean = false\n): Promise<Response> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= config.maxRetries; attempt++) {\n try {\n if (debug && attempt > 0) {\n console.log(`[Gotcha] Retry attempt ${attempt}/${config.maxRetries}`);\n }\n\n const response = await fetch(url, options);\n\n // Don't retry client errors (4xx) except 429 (rate limit)\n if (response.status >= 400 && response.status < 500 && response.status !== 429) {\n return response;\n }\n\n // Success - return immediately\n if (response.ok) {\n return response;\n }\n\n lastError = new Error(`HTTP ${response.status}`);\n } catch (error) {\n // Network error - retry\n lastError = error as Error;\n if (debug) {\n console.log(`[Gotcha] Network error: ${lastError.message}`);\n }\n }\n\n // Don't delay after last attempt\n if (attempt < config.maxRetries) {\n const delay = Math.min(\n config.baseDelayMs * Math.pow(2, attempt),\n config.maxDelayMs\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n}\n\nexport function createApiClient(config: ApiClientConfig) {\n const { apiKey, baseUrl = API_BASE_URL, debug = false } = config;\n\n const headers = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n };\n\n async function request<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<T> {\n const url = `${baseUrl}${endpoint}`;\n const idempotencyKey = crypto.randomUUID();\n\n if (debug) {\n console.log(`[Gotcha] ${method} ${endpoint}`, body);\n }\n\n const response = await fetchWithRetry(\n url,\n {\n method,\n headers: {\n ...headers,\n 'Idempotency-Key': idempotencyKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n },\n DEFAULT_RETRY_CONFIG,\n debug\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = data.error as GotchaError;\n if (debug) {\n console.error(`[Gotcha] Error: ${error.code} - ${error.message}`);\n }\n throw error;\n }\n\n if (debug) {\n console.log(`[Gotcha] Response:`, data);\n }\n\n return data as T;\n }\n\n return {\n /**\n * Submit a response (feedback, vote, etc.)\n */\n async submitResponse(\n payload: Omit<SubmitResponsePayload, 'context'>\n ): Promise<GotchaResponse> {\n // Ensure user has an ID (anonymous if not provided)\n const user = payload.user || {};\n if (!user.id) {\n user.id = getAnonymousId();\n }\n\n const fullPayload: SubmitResponsePayload = {\n ...payload,\n user,\n context: {\n url: typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n },\n };\n\n return request<GotchaResponse>('POST', '/responses', fullPayload);\n },\n\n /**\n * Check if a user has an existing response for an element\n */\n async checkExistingResponse(\n elementId: string,\n userId: string\n ): Promise<ExistingResponse | null> {\n const url = `${baseUrl}/responses/check?elementId=${encodeURIComponent(elementId)}&userId=${encodeURIComponent(userId)}`;\n\n if (debug) {\n console.log(`[Gotcha] GET /responses/check`);\n }\n\n const response = await fetchWithRetry(\n url,\n {\n method: 'GET',\n headers,\n },\n DEFAULT_RETRY_CONFIG,\n debug\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = data.error as GotchaError;\n if (debug) {\n console.error(`[Gotcha] Error: ${error.code} - ${error.message}`);\n }\n throw error;\n }\n\n if (data.exists) {\n if (debug) {\n console.log(`[Gotcha] Found existing response:`, data.response);\n }\n return data.response as ExistingResponse;\n }\n\n return null;\n },\n\n /**\n * Update an existing response\n */\n async updateResponse(\n id: string,\n payload: {\n content?: string;\n title?: string;\n rating?: number;\n vote?: VoteType;\n pollSelected?: string[];\n },\n userId?: string\n ): Promise<GotchaResponse> {\n const url = `${baseUrl}/responses/${id}${userId ? `?userId=${encodeURIComponent(userId)}` : ''}`;\n\n if (debug) {\n console.log(`[Gotcha] PATCH /responses/${id}`, payload);\n }\n\n const response = await fetchWithRetry(\n url,\n {\n method: 'PATCH',\n headers,\n body: JSON.stringify(payload),\n },\n DEFAULT_RETRY_CONFIG,\n debug\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = data.error as GotchaError;\n if (debug) {\n console.error(`[Gotcha] Error: ${error.code} - ${error.message}`);\n }\n throw error;\n }\n\n if (debug) {\n console.log(`[Gotcha] Response updated:`, data);\n }\n\n return data as GotchaResponse;\n },\n\n /**\n * Get the base URL (for debugging)\n */\n getBaseUrl(): string {\n return baseUrl;\n },\n };\n}\n\nexport type ApiClient = ReturnType<typeof createApiClient>;\n","import React, { createContext, useContext, useMemo, useState, useCallback } from 'react';\nimport { createApiClient, ApiClient } from '../api/client';\nimport { GotchaUser } from '../types';\n\nexport interface GotchaProviderProps {\n /** Your Gotcha API key */\n apiKey: string;\n /** React children */\n children: React.ReactNode;\n /** Override the API base URL (for testing/staging) */\n baseUrl?: string;\n /** Enable debug logging */\n debug?: boolean;\n /** Disable all Gotcha buttons globally */\n disabled?: boolean;\n /** Default user metadata applied to all submissions */\n defaultUser?: GotchaUser;\n}\n\nexport interface GotchaContextValue {\n client: ApiClient;\n disabled: boolean;\n defaultUser: GotchaUser;\n debug: boolean;\n // Modal management - only one open at a time\n activeModalId: string | null;\n openModal: (elementId: string) => void;\n closeModal: () => void;\n}\n\nconst GotchaContext = createContext<GotchaContextValue | null>(null);\n\nexport function GotchaProvider({\n apiKey,\n children,\n baseUrl,\n debug = false,\n disabled = false,\n defaultUser = {},\n}: GotchaProviderProps) {\n const [activeModalId, setActiveModalId] = useState<string | null>(null);\n\n const client = useMemo(\n () => createApiClient({ apiKey, baseUrl, debug }),\n [apiKey, baseUrl, debug]\n );\n\n const openModal = useCallback((elementId: string) => {\n setActiveModalId(elementId);\n }, []);\n\n const closeModal = useCallback(() => {\n setActiveModalId(null);\n }, []);\n\n const value: GotchaContextValue = useMemo(\n () => ({\n client,\n disabled,\n defaultUser,\n debug,\n activeModalId,\n openModal,\n closeModal,\n }),\n [client, disabled, defaultUser, debug, activeModalId, openModal, closeModal]\n );\n\n return (\n <GotchaContext.Provider value={value}>{children}</GotchaContext.Provider>\n );\n}\n\nexport function useGotchaContext(): GotchaContextValue {\n const context = useContext(GotchaContext);\n if (!context) {\n throw new Error('useGotchaContext must be used within a GotchaProvider');\n }\n return context;\n}\n","import { useState, useCallback, useEffect } from 'react';\nimport { useGotchaContext } from '../components/GotchaProvider';\nimport { ResponseMode, GotchaUser, GotchaResponse, VoteType, ExistingResponse } from '../types';\n\ninterface UseSubmitOptions {\n elementId: string;\n mode: ResponseMode;\n experimentId?: string;\n variant?: string;\n pollOptions?: string[];\n user?: GotchaUser;\n onSuccess?: (response: GotchaResponse) => void;\n onError?: (error: Error) => void;\n}\n\ninterface SubmitData {\n content?: string;\n title?: string;\n rating?: number;\n vote?: VoteType;\n pollSelected?: string[];\n}\n\nexport function useSubmit(options: UseSubmitOptions) {\n const { client, defaultUser } = useGotchaContext();\n const [isLoading, setIsLoading] = useState(false);\n const [isCheckingExisting, setIsCheckingExisting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [existingResponse, setExistingResponse] = useState<ExistingResponse | null>(null);\n\n // Check for existing response when user ID is provided\n useEffect(() => {\n const userId = options.user?.id || defaultUser?.id;\n if (!userId) {\n setExistingResponse(null);\n return;\n }\n\n let cancelled = false;\n\n const checkExisting = async () => {\n setIsCheckingExisting(true);\n try {\n const existing = await client.checkExistingResponse(options.elementId, userId);\n if (!cancelled) {\n setExistingResponse(existing);\n }\n } catch {\n // Ignore errors - just means no existing response found\n if (!cancelled) {\n setExistingResponse(null);\n }\n } finally {\n if (!cancelled) {\n setIsCheckingExisting(false);\n }\n }\n };\n\n checkExisting();\n\n return () => {\n cancelled = true;\n };\n }, [client, options.elementId, options.user?.id, defaultUser?.id]);\n\n const submit = useCallback(\n async (data: SubmitData) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const userId = options.user?.id || defaultUser?.id;\n let response: GotchaResponse;\n\n // If we have an existing response and a user ID, update instead of create\n if (existingResponse && userId) {\n response = await client.updateResponse(\n existingResponse.id,\n {\n content: data.content,\n title: data.title,\n rating: data.rating,\n vote: data.vote,\n pollSelected: data.pollSelected,\n },\n userId\n );\n } else {\n response = await client.submitResponse({\n elementId: options.elementId,\n mode: options.mode,\n content: data.content,\n title: data.title,\n rating: data.rating,\n vote: data.vote,\n pollOptions: options.pollOptions,\n pollSelected: data.pollSelected,\n experimentId: options.experimentId,\n variant: options.variant,\n user: { ...defaultUser, ...options.user },\n });\n }\n\n options.onSuccess?.(response);\n return response;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Something went wrong';\n setError(errorMessage);\n options.onError?.(err instanceof Error ? err : new Error(errorMessage));\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [client, defaultUser, options, existingResponse]\n );\n\n return {\n submit,\n isLoading,\n isCheckingExisting,\n error,\n existingResponse,\n isEditing: !!existingResponse,\n clearError: () => setError(null),\n };\n}\n","/**\n * Simple class name utility\n * Combines class names, filtering out falsy values\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n","/**\n * Detect if the device supports touch\n */\nexport const isTouchDevice = (): boolean => {\n if (typeof window === 'undefined') return false;\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0;\n};\n\n/**\n * Get the appropriate size based on device type\n */\nexport const getResponsiveSize = (\n size: 'sm' | 'md' | 'lg',\n isTouch: boolean\n): number => {\n const sizes = {\n sm: { desktop: 24, mobile: 32 },\n md: { desktop: 32, mobile: 36 },\n lg: { desktop: 40, mobile: 40 },\n };\n\n return isTouch ? sizes[size].mobile : sizes[size].desktop;\n};\n","import React, { useState, useEffect } from 'react';\nimport { Size, Theme, GotchaStyles } from '../types';\nimport { cn } from '../utils/cn';\nimport { isTouchDevice, getResponsiveSize } from '../utils/device';\n\nexport interface GotchaButtonProps {\n size: Size;\n theme: Theme;\n customStyles?: GotchaStyles;\n showOnHover: boolean;\n touchBehavior: 'always-visible' | 'tap-to-reveal';\n onClick: () => void;\n isOpen: boolean;\n isParentHovered?: boolean;\n}\n\nexport function GotchaButton({\n size,\n theme,\n customStyles,\n showOnHover,\n touchBehavior,\n onClick,\n isOpen,\n isParentHovered = false,\n}: GotchaButtonProps) {\n const [isTouch, setIsTouch] = useState(false);\n const [tapRevealed, setTapRevealed] = useState(false);\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n // Detect system theme preference\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemTheme(darkQuery.matches ? 'dark' : 'light');\n\n // Listen for theme changes\n const handler = (e: MediaQueryListEvent) => setSystemTheme(e.matches ? 'dark' : 'light');\n darkQuery.addEventListener('change', handler);\n return () => darkQuery.removeEventListener('change', handler);\n }, []);\n\n // Determine visibility\n const shouldShow = (() => {\n // Always show if modal is open\n if (isOpen) return true;\n\n // Desktop with showOnHover: only show when parent is hovered\n if (!isTouch && showOnHover) {\n return isParentHovered;\n }\n\n // Touch device with tap-to-reveal: show only after first tap\n if (isTouch && touchBehavior === 'tap-to-reveal') {\n return tapRevealed;\n }\n\n // All other cases: always visible\n return true;\n })();\n\n const handleClick = () => {\n // For tap-to-reveal on touch: first tap reveals, second tap opens\n if (isTouch && touchBehavior === 'tap-to-reveal' && !tapRevealed) {\n setTapRevealed(true);\n return;\n }\n onClick();\n };\n\n const buttonSize = getResponsiveSize(size, isTouch);\n\n // Determine theme colors\n const resolvedTheme = theme === 'auto' ? systemTheme : theme;\n\n const baseStyles: React.CSSProperties = {\n width: buttonSize,\n height: buttonSize,\n borderRadius: '50%',\n border: 'none',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: resolvedTheme === 'dark' ? '#374151' : '#c7d2dc',\n color: resolvedTheme === 'dark' ? '#e5e7eb' : '#4b5563',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)',\n // CSS transition for animations\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: shouldShow ? 1 : 0,\n transform: shouldShow ? 'scale(1)' : 'scale(0.6)',\n pointerEvents: shouldShow ? 'auto' : 'none',\n ...customStyles?.button,\n };\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n style={baseStyles}\n className={cn('gotcha-button', isOpen && 'gotcha-button--open')}\n aria-label=\"Give feedback on this feature\"\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n >\n <GotchaIcon size={buttonSize * 0.75} />\n </button>\n );\n}\n\nfunction GotchaIcon({ size }: { size: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <text\n x=\"50%\"\n y=\"50%\"\n dominantBaseline=\"central\"\n textAnchor=\"middle\"\n fontSize=\"16\"\n fontWeight=\"bold\"\n fill=\"currentColor\"\n fontFamily=\"system-ui, -apple-system, sans-serif\"\n >\n G\n </text>\n </svg>\n );\n}\n","import React from 'react';\n\ninterface SpinnerProps {\n size?: number;\n color?: string;\n}\n\nexport function Spinner({ size = 16, color = 'currentColor' }: SpinnerProps) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n style={{\n animation: 'gotcha-spin 1s linear infinite',\n }}\n >\n <style>\n {`\n @keyframes gotcha-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `}\n </style>\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke={color}\n strokeWidth=\"3\"\n strokeOpacity=\"0.25\"\n />\n <path\n d=\"M12 2a10 10 0 0 1 10 10\"\n stroke={color}\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n","import React, { useState, useEffect } from 'react';\nimport { GotchaStyles } from '../../types';\nimport { isTouchDevice } from '../../utils/device';\nimport { Spinner } from '../Spinner';\n\ninterface FeedbackModeProps {\n theme: 'light' | 'dark' | 'custom';\n placeholder?: string;\n submitText: string;\n isLoading: boolean;\n onSubmit: (data: { content?: string; rating?: number }) => void;\n customStyles?: GotchaStyles;\n initialValues?: {\n content?: string | null;\n rating?: number | null;\n };\n isEditing?: boolean;\n}\n\nexport function FeedbackMode({\n theme,\n placeholder,\n submitText,\n isLoading,\n onSubmit,\n customStyles,\n initialValues,\n isEditing = false,\n}: FeedbackModeProps) {\n const [content, setContent] = useState(initialValues?.content || '');\n const [rating, setRating] = useState<number | null>(initialValues?.rating ?? null);\n const [isTouch, setIsTouch] = useState(false);\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n }, []);\n\n // Update local state when initialValues change (e.g., after loading existing response)\n useEffect(() => {\n if (initialValues?.content !== undefined) {\n setContent(initialValues.content || '');\n }\n if (initialValues?.rating !== undefined) {\n setRating(initialValues.rating ?? null);\n }\n }, [initialValues?.content, initialValues?.rating]);\n\n const isDark = theme === 'dark';\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!content.trim() && rating === null) return;\n onSubmit({ content: content.trim() || undefined, rating: rating ?? undefined });\n };\n\n const inputStyles: React.CSSProperties = {\n width: '100%',\n padding: isTouch ? '12px 14px' : '10px 12px',\n border: `1px solid ${isDark ? '#374151' : '#d1d5db'}`,\n borderRadius: isTouch ? 8 : 6,\n backgroundColor: isDark ? '#374151' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n fontSize: isTouch ? 16 : 14, // 16px prevents iOS zoom on focus\n resize: 'vertical',\n minHeight: isTouch ? 100 : 80,\n fontFamily: 'inherit',\n ...customStyles?.input,\n };\n\n const buttonStyles: React.CSSProperties = {\n width: '100%',\n padding: isTouch ? '14px 16px' : '10px 16px',\n border: 'none',\n borderRadius: isTouch ? 8 : 6,\n backgroundColor: isLoading ? (isDark ? '#4b5563' : '#9ca3af') : '#6366f1',\n color: '#ffffff',\n fontSize: isTouch ? 16 : 14,\n fontWeight: 500,\n cursor: isLoading ? 'not-allowed' : 'pointer',\n transition: 'background-color 150ms ease',\n ...customStyles?.submitButton,\n };\n\n return (\n <form onSubmit={handleSubmit}>\n {/* Rating (optional) */}\n <div style={{ marginBottom: isTouch ? 16 : 12 }}>\n <StarRating value={rating} onChange={setRating} isDark={isDark} isTouch={isTouch} />\n </div>\n\n {/* Text input */}\n <textarea\n value={content}\n onChange={(e) => setContent(e.target.value)}\n placeholder={placeholder || 'Share your thoughts...'}\n style={inputStyles}\n disabled={isLoading}\n aria-label=\"Your feedback\"\n />\n\n {/* Submit button */}\n <button\n type=\"submit\"\n disabled={isLoading || (!content.trim() && rating === null)}\n style={{\n ...buttonStyles,\n marginTop: 12,\n opacity: (!content.trim() && rating === null) ? 0.5 : 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n }}\n >\n {isLoading && <Spinner size={isTouch ? 18 : 16} color=\"#ffffff\" />}\n {isLoading ? (isEditing ? 'Updating...' : 'Submitting...') : (isEditing ? 'Update' : submitText)}\n </button>\n </form>\n );\n}\n\ninterface StarRatingProps {\n value: number | null;\n onChange: (rating: number) => void;\n isDark: boolean;\n isTouch: boolean;\n}\n\nfunction StarRating({ value, onChange, isDark, isTouch }: StarRatingProps) {\n const [hovered, setHovered] = useState<number | null>(null);\n const starSize = isTouch ? 32 : 20;\n const buttonPadding = isTouch ? 6 : 2;\n\n return (\n <div\n style={{\n display: 'flex',\n gap: isTouch ? 8 : 4,\n }}\n role=\"group\"\n aria-label=\"Rating\"\n >\n {[1, 2, 3, 4, 5].map((star) => {\n const isFilled = (hovered ?? value ?? 0) >= star;\n return (\n <button\n key={star}\n type=\"button\"\n onClick={() => onChange(star)}\n onMouseEnter={() => setHovered(star)}\n onMouseLeave={() => setHovered(null)}\n aria-label={`Rate ${star} out of 5`}\n aria-pressed={value === star}\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: buttonPadding,\n color: isFilled ? '#f59e0b' : (isDark ? '#4b5563' : '#d1d5db'),\n transition: 'color 150ms ease',\n }}\n >\n <svg width={starSize} height={starSize} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\" />\n </svg>\n </button>\n );\n })}\n </div>\n );\n}\n","import React, { useEffect, useState } from 'react';\nimport { isTouchDevice } from '../../utils/device';\nimport { Spinner } from '../Spinner';\n\ninterface VoteModeProps {\n theme: 'light' | 'dark' | 'custom';\n isLoading: boolean;\n onSubmit: (data: { vote: 'up' | 'down' }) => void;\n initialVote?: 'up' | 'down' | null;\n isEditing?: boolean;\n}\n\nexport function VoteMode({ theme, isLoading, onSubmit, initialVote, isEditing = false }: VoteModeProps) {\n const [isTouch, setIsTouch] = useState(false);\n const [activeVote, setActiveVote] = useState<'up' | 'down' | null>(initialVote || null);\n const [previousVote, setPreviousVote] = useState<'up' | 'down' | null>(initialVote || null);\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n }, []);\n\n // Update when initialVote changes (e.g., after loading existing response)\n useEffect(() => {\n if (initialVote !== undefined) {\n setPreviousVote(initialVote);\n setActiveVote(initialVote);\n }\n }, [initialVote]);\n\n // Reset active vote when loading completes (but keep previous vote for edit mode)\n useEffect(() => {\n if (!isLoading && !isEditing) {\n setActiveVote(null);\n }\n }, [isLoading, isEditing]);\n\n const handleVote = (vote: 'up' | 'down') => {\n setActiveVote(vote);\n onSubmit({ vote });\n };\n\n const isDark = theme === 'dark';\n\n const getButtonStyles = (voteType: 'up' | 'down'): React.CSSProperties => {\n const isSelected = previousVote === voteType;\n return {\n flex: 1,\n padding: isTouch ? '16px 20px' : '12px 16px',\n border: isSelected\n ? `2px solid ${voteType === 'up' ? '#22c55e' : '#ef4444'}`\n : `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n borderRadius: isTouch ? 12 : 8,\n backgroundColor: isSelected\n ? (voteType === 'up' ? (isDark ? '#14532d' : '#dcfce7') : (isDark ? '#450a0a' : '#fee2e2'))\n : (isDark ? '#374151' : '#f9fafb'),\n color: isDark ? '#f9fafb' : '#111827',\n fontSize: isTouch ? 28 : 24,\n cursor: isLoading ? 'not-allowed' : 'pointer',\n transition: 'all 150ms ease',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: isTouch ? 10 : 8,\n };\n };\n\n const iconSize = isTouch ? 28 : 24;\n\n return (\n <div\n style={{\n display: 'flex',\n gap: isTouch ? 16 : 12,\n }}\n role=\"group\"\n aria-label=\"Vote\"\n >\n <button\n type=\"button\"\n onClick={() => handleVote('up')}\n disabled={isLoading}\n style={getButtonStyles('up')}\n aria-label=\"Vote up - I like this\"\n aria-pressed={previousVote === 'up'}\n >\n {isLoading && activeVote === 'up' ? (\n <>\n <Spinner size={iconSize} color={isDark ? '#f9fafb' : '#111827'} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>\n {isEditing ? 'Updating...' : 'Sending...'}\n </span>\n </>\n ) : (\n <>\n <ThumbsUpIcon size={iconSize} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>\n {previousVote === 'up' ? 'Liked' : 'Like'}\n </span>\n </>\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={() => handleVote('down')}\n disabled={isLoading}\n style={getButtonStyles('down')}\n aria-label=\"Vote down - I don't like this\"\n aria-pressed={previousVote === 'down'}\n >\n {isLoading && activeVote === 'down' ? (\n <>\n <Spinner size={iconSize} color={isDark ? '#f9fafb' : '#111827'} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>\n {isEditing ? 'Updating...' : 'Sending...'}\n </span>\n </>\n ) : (\n <>\n <ThumbsDownIcon size={iconSize} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>\n {previousVote === 'down' ? 'Disliked' : 'Dislike'}\n </span>\n </>\n )}\n </button>\n </div>\n );\n}\n\nfunction ThumbsUpIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\" />\n </svg>\n );\n}\n\nfunction ThumbsDownIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\" />\n </svg>\n );\n}\n","import React, { useRef, useEffect, useState } from 'react';\nimport { Theme, GotchaStyles, ResponseMode, ExistingResponse } from '../types';\nimport { cn } from '../utils/cn';\nimport { FeedbackMode } from './modes/FeedbackMode';\nimport { VoteMode } from './modes/VoteMode';\n\nexport interface GotchaModalProps {\n mode: ResponseMode;\n theme: Theme;\n customStyles?: GotchaStyles;\n promptText?: string;\n placeholder?: string;\n submitText: string;\n thankYouMessage: string;\n // State\n isLoading: boolean;\n isSubmitted: boolean;\n error: string | null;\n // Edit mode\n existingResponse?: ExistingResponse | null;\n isEditing?: boolean;\n // Handlers\n onSubmit: (data: { content?: string; rating?: number; vote?: 'up' | 'down' }) => void;\n onClose: () => void;\n // Position info from parent\n anchorRect?: DOMRect;\n}\n\nexport function GotchaModal({\n mode,\n theme,\n customStyles,\n promptText,\n placeholder,\n submitText,\n thankYouMessage,\n isLoading,\n isSubmitted,\n error,\n existingResponse,\n isEditing = false,\n onSubmit,\n onClose,\n anchorRect,\n}: GotchaModalProps) {\n const modalRef = useRef<HTMLDivElement>(null);\n const firstFocusableRef = useRef<HTMLButtonElement>(null);\n const [isVisible, setIsVisible] = useState(false);\n const [isMobile, setIsMobile] = useState(false);\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');\n\n // Detect mobile and system theme after mount (SSR-safe)\n useEffect(() => {\n setIsMobile(window.innerWidth < 640);\n\n // Detect system theme preference\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemTheme(darkQuery.matches ? 'dark' : 'light');\n\n // Listen for theme changes\n const handler = (e: MediaQueryListEvent) => setSystemTheme(e.matches ? 'dark' : 'light');\n darkQuery.addEventListener('change', handler);\n return () => darkQuery.removeEventListener('change', handler);\n }, []);\n\n // Trigger animation after mount\n useEffect(() => {\n const timer = requestAnimationFrame(() => setIsVisible(true));\n return () => cancelAnimationFrame(timer);\n }, []);\n\n // Resolve theme\n const resolvedTheme = theme === 'auto' ? systemTheme : theme;\n\n const isDark = resolvedTheme === 'dark';\n\n // Determine if modal should appear above or below\n const modalHeight = 280; // approximate modal height\n const spaceBelow = anchorRect\n ? window.innerHeight - anchorRect.bottom\n : window.innerHeight / 2;\n const showAbove = spaceBelow < modalHeight + 20;\n\n // Focus trap\n useEffect(() => {\n const modal = modalRef.current;\n if (!modal) return;\n\n // Focus first element\n firstFocusableRef.current?.focus();\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose();\n return;\n }\n\n if (e.key === 'Tab') {\n const focusableElements = modal.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n );\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault();\n lastElement?.focus();\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault();\n firstElement?.focus();\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [onClose]);\n\n const defaultPrompt = mode === 'vote'\n ? 'What do you think?'\n : 'What do you think of this feature?';\n\n // Responsive sizing - on mobile, use fixed positioning centered on screen\n const modalPadding = isMobile ? 20 : 16;\n\n const modalStyles: React.CSSProperties = isMobile\n ? {\n // Mobile: fixed position, centered on screen\n position: 'fixed',\n left: '50%',\n top: '50%',\n width: 'calc(100vw - 32px)',\n maxWidth: 320,\n padding: modalPadding,\n borderRadius: 12,\n backgroundColor: isDark ? '#1f2937' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.2)',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n zIndex: 9999,\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: isVisible ? 1 : 0,\n transform: isVisible\n ? 'translate(-50%, -50%) scale(1)'\n : 'translate(-50%, -50%) scale(0.95)',\n ...customStyles?.modal,\n }\n : {\n // Desktop: absolute position relative to button\n position: 'absolute',\n left: '50%',\n width: 320,\n padding: modalPadding,\n borderRadius: 8,\n backgroundColor: isDark ? '#1f2937' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15)',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n zIndex: 9999,\n ...(showAbove\n ? { bottom: '100%', marginBottom: 8 }\n : { top: '100%', marginTop: 8 }),\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: isVisible ? 1 : 0,\n transform: isVisible\n ? 'translateX(-50%) scale(1) translateY(0)'\n : `translateX(-50%) scale(0.95) translateY(${showAbove ? '10px' : '-10px'})`,\n ...customStyles?.modal,\n };\n\n return (\n <div\n ref={modalRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"gotcha-modal-title\"\n style={modalStyles}\n className={cn('gotcha-modal', isDark && 'gotcha-modal--dark')}\n >\n {/* Close button - larger on touch devices */}\n <button\n ref={firstFocusableRef}\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close feedback form\"\n style={{\n position: 'absolute',\n top: isMobile ? 12 : 8,\n right: isMobile ? 12 : 8,\n width: isMobile ? 36 : 24,\n height: isMobile ? 36 : 24,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: isDark ? '#9ca3af' : '#6b7280',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 4,\n }}\n >\n <svg width={isMobile ? 18 : 14} height={isMobile ? 18 : 14} viewBox=\"0 0 14 14\" fill=\"none\">\n <path\n d=\"M1 1L13 13M1 13L13 1\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n\n {/* Title */}\n <h2\n id=\"gotcha-modal-title\"\n style={{\n margin: '0 0 12px 0',\n fontSize: isMobile ? 16 : 14,\n fontWeight: 500,\n paddingRight: isMobile ? 40 : 24,\n }}\n >\n {promptText || defaultPrompt}\n </h2>\n\n {/* Success state */}\n {isSubmitted && (\n <div\n style={{\n textAlign: 'center',\n padding: '20px 0',\n color: isDark ? '#10b981' : '#059669',\n }}\n >\n <svg\n width=\"32\"\n height=\"32\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n style={{ margin: '0 auto 8px' }}\n >\n <path\n d=\"M20 6L9 17L4 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n <p style={{ margin: 0, fontSize: 14 }}>{thankYouMessage}</p>\n </div>\n )}\n\n {/* Error state */}\n {error && !isSubmitted && (\n <div\n style={{\n padding: 8,\n marginBottom: 12,\n borderRadius: 4,\n backgroundColor: isDark ? '#7f1d1d' : '#fef2f2',\n color: isDark ? '#fecaca' : '#dc2626',\n fontSize: 13,\n }}\n >\n {error}\n </div>\n )}\n\n {/* Form content based on mode */}\n {!isSubmitted && (\n <>\n {mode === 'feedback' && (\n <FeedbackMode\n theme={resolvedTheme}\n placeholder={placeholder}\n submitText={submitText}\n isLoading={isLoading}\n onSubmit={onSubmit}\n customStyles={customStyles}\n initialValues={existingResponse ? {\n content: existingResponse.content,\n rating: existingResponse.rating,\n } : undefined}\n isEditing={isEditing}\n />\n )}\n {mode === 'vote' && (\n <VoteMode\n theme={resolvedTheme}\n isLoading={isLoading}\n onSubmit={onSubmit}\n initialVote={existingResponse?.vote || undefined}\n isEditing={isEditing}\n />\n )}\n </>\n )}\n\n {/* Screen reader announcement */}\n <div aria-live=\"polite\" className=\"sr-only\" style={{ position: 'absolute', left: -9999 }}>\n {isSubmitted && 'Thank you! Your feedback has been submitted.'}\n {error && `Error: ${error}`}\n </div>\n </div>\n );\n}\n","import React, { useState, useCallback, useEffect, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n ResponseMode,\n GotchaUser,\n Position,\n Size,\n Theme,\n TouchBehavior,\n GotchaStyles,\n GotchaResponse,\n GotchaError,\n} from '../types';\nimport { DEFAULTS } from '../constants';\nimport { useGotchaContext } from './GotchaProvider';\nimport { useSubmit } from '../hooks/useSubmit';\nimport { GotchaButton } from './GotchaButton';\nimport { GotchaModal } from './GotchaModal';\n\nexport interface GotchaProps {\n /** Unique identifier for this element */\n elementId: string;\n\n // User data\n /** User metadata for segmentation */\n user?: GotchaUser;\n\n // Behavior\n /** Feedback mode */\n mode?: ResponseMode;\n /** Required if mode is 'ab' */\n experimentId?: string;\n /** Current A/B variant shown to user */\n variant?: string;\n\n // Poll mode specific (Phase 2)\n /** Required if mode is 'poll' (2-6 options) */\n options?: string[];\n /** Allow selecting multiple options */\n allowMultiple?: boolean;\n /** Show results after voting */\n showResults?: boolean;\n\n // Appearance\n /** Button position relative to parent */\n position?: Position;\n /** Button size */\n size?: Size;\n /** Color theme */\n theme?: Theme;\n /** Custom style overrides */\n customStyles?: GotchaStyles;\n /** Control visibility programmatically */\n visible?: boolean;\n /** Only show when parent is hovered (default: true) */\n showOnHover?: boolean;\n /** Mobile behavior (default: 'always-visible') */\n touchBehavior?: TouchBehavior;\n\n // Content\n /** Custom prompt text */\n promptText?: string;\n /** Input placeholder text */\n placeholder?: string;\n /** Submit button text */\n submitText?: string;\n /** Post-submission message */\n thankYouMessage?: string;\n\n // Callbacks\n /** Called after successful submission */\n onSubmit?: (response: GotchaResponse) => void;\n /** Called when modal opens */\n onOpen?: () => void;\n /** Called when modal closes */\n onClose?: () => void;\n /** Called on error */\n onError?: (error: GotchaError) => void;\n}\n\nexport function Gotcha({\n elementId,\n user,\n mode = 'feedback',\n experimentId,\n variant,\n options,\n allowMultiple = false,\n showResults = true,\n position = DEFAULTS.POSITION,\n size = DEFAULTS.SIZE,\n theme = DEFAULTS.THEME,\n customStyles,\n visible = true,\n showOnHover = DEFAULTS.SHOW_ON_HOVER,\n touchBehavior = DEFAULTS.TOUCH_BEHAVIOR,\n promptText,\n placeholder,\n submitText = DEFAULTS.SUBMIT_TEXT,\n thankYouMessage = DEFAULTS.THANK_YOU_MESSAGE,\n onSubmit,\n onOpen,\n onClose,\n onError,\n}: GotchaProps) {\n const { disabled, activeModalId, openModal, closeModal } = useGotchaContext();\n const [isSubmitted, setIsSubmitted] = useState(false);\n const [isParentHovered, setIsParentHovered] = useState(false);\n const [anchorRect, setAnchorRect] = useState<DOMRect | null>(null);\n const [isMobile, setIsMobile] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Detect mobile for portal rendering (SSR-safe)\n useEffect(() => {\n setIsMobile(window.innerWidth < 640);\n }, []);\n\n // This instance's modal is open if activeModalId matches our elementId\n const isOpen = activeModalId === elementId;\n\n // Attach hover listeners to the parent element (not the button container)\n useEffect(() => {\n if (!showOnHover) return;\n\n const container = containerRef.current;\n if (!container) return;\n\n // Find the parent element with position: relative\n const parent = container.parentElement;\n if (!parent) return;\n\n const handleMouseEnter = () => setIsParentHovered(true);\n const handleMouseLeave = () => setIsParentHovered(false);\n\n parent.addEventListener('mouseenter', handleMouseEnter);\n parent.addEventListener('mouseleave', handleMouseLeave);\n\n return () => {\n parent.removeEventListener('mouseenter', handleMouseEnter);\n parent.removeEventListener('mouseleave', handleMouseLeave);\n };\n }, [showOnHover]);\n\n const { submit, isLoading, error, existingResponse, isEditing } = useSubmit({\n elementId,\n mode,\n experimentId,\n variant,\n pollOptions: options,\n user,\n onSuccess: (response) => {\n setIsSubmitted(true);\n onSubmit?.(response);\n // Auto-close after 2.5 seconds\n setTimeout(() => {\n closeModal();\n setIsSubmitted(false);\n }, 2500);\n },\n onError: (err) => {\n onError?.(err as unknown as GotchaError);\n },\n });\n\n const handleOpen = useCallback(() => {\n if (containerRef.current) {\n setAnchorRect(containerRef.current.getBoundingClientRect());\n }\n openModal(elementId);\n onOpen?.();\n }, [elementId, openModal, onOpen]);\n\n const handleClose = useCallback(() => {\n closeModal();\n setIsSubmitted(false);\n onClose?.();\n }, [closeModal, onClose]);\n\n const handleSubmit = useCallback(\n (data: { content?: string; rating?: number; vote?: 'up' | 'down' }) => {\n submit(data);\n },\n [submit]\n );\n\n // Don't render if disabled or not visible\n if (disabled || !visible) return null;\n\n // Position styles\n const positionStyles: Record<Position, React.CSSProperties> = {\n 'top-right': { position: 'absolute', top: 0, right: 0, transform: 'translate(50%, -50%)' },\n 'top-left': { position: 'absolute', top: 0, left: 0, transform: 'translate(-50%, -50%)' },\n 'bottom-right': { position: 'absolute', bottom: 0, right: 0, transform: 'translate(50%, 50%)' },\n 'bottom-left': { position: 'absolute', bottom: 0, left: 0, transform: 'translate(-50%, 50%)' },\n 'inline': { position: 'relative', display: 'inline-flex' },\n };\n\n return (\n <div\n ref={containerRef}\n style={{\n ...positionStyles[position],\n zIndex: isOpen ? 10000 : 'auto',\n }}\n className=\"gotcha-container\"\n data-gotcha-element={elementId}\n >\n <GotchaButton\n size={size}\n theme={theme}\n customStyles={customStyles}\n showOnHover={showOnHover}\n touchBehavior={touchBehavior}\n onClick={handleOpen}\n isOpen={isOpen}\n isParentHovered={isParentHovered}\n />\n\n {isOpen && !isMobile && (\n <GotchaModal\n mode={mode}\n theme={theme}\n customStyles={customStyles}\n promptText={promptText}\n placeholder={placeholder}\n submitText={submitText}\n thankYouMessage={isEditing ? 'Your feedback has been updated!' : thankYouMessage}\n isLoading={isLoading}\n isSubmitted={isSubmitted}\n error={error}\n existingResponse={existingResponse}\n isEditing={isEditing}\n onSubmit={handleSubmit}\n onClose={handleClose}\n anchorRect={anchorRect || undefined}\n />\n )}\n\n {/* On mobile, render modal via portal to escape parent transform */}\n {isOpen && isMobile && createPortal(\n <div\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 99999,\n backgroundColor: 'rgba(0, 0, 0, 0.3)',\n }}\n onClick={handleClose}\n aria-hidden=\"true\"\n >\n <div onClick={(e) => e.stopPropagation()}>\n <GotchaModal\n mode={mode}\n theme={theme}\n customStyles={customStyles}\n promptText={promptText}\n placeholder={placeholder}\n submitText={submitText}\n thankYouMessage={isEditing ? 'Your feedback has been updated!' : thankYouMessage}\n isLoading={isLoading}\n isSubmitted={isSubmitted}\n error={error}\n existingResponse={existingResponse}\n isEditing={isEditing}\n onSubmit={handleSubmit}\n onClose={handleClose}\n anchorRect={anchorRect || undefined}\n />\n </div>\n </div>,\n document.body\n )}\n </div>\n );\n}\n","import { useGotchaContext } from '../components/GotchaProvider';\n\n/**\n * Hook to access Gotcha context\n * Must be used within a GotchaProvider\n */\nexport function useGotcha() {\n const { client, disabled, defaultUser, debug } = useGotchaContext();\n\n return {\n /** The API client for manual submissions */\n client,\n /** Whether Gotcha is globally disabled */\n disabled,\n /** Default user metadata */\n defaultUser,\n /** Whether debug mode is enabled */\n debug,\n /** Submit feedback programmatically */\n submitFeedback: client.submitResponse.bind(client),\n };\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,3 +1,7 @@
1
- import {createContext,useState,useMemo,useCallback,useRef,useEffect,useContext}from'react';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {createPortal}from'react-dom';var pe="https://api.gotcha.cx/v1";var Z={ANONYMOUS_ID:"gotcha_anonymous_id"},w={POSITION:"top-right",SIZE:"md",THEME:"light",SHOW_ON_HOVER:true,TOUCH_BEHAVIOR:"always-visible",SUBMIT_TEXT:"Submit",THANK_YOU_MESSAGE:"Thanks for your feedback!"};var F={MAX_RETRIES:2,BASE_DELAY_MS:500,MAX_DELAY_MS:5e3};function me(){if(typeof window>"u")return `anon_${crypto.randomUUID()}`;let e=localStorage.getItem(Z.ANONYMOUS_ID);if(e)return e;let s=`anon_${crypto.randomUUID()}`;return localStorage.setItem(Z.ANONYMOUS_ID,s),s}var fe={maxRetries:F.MAX_RETRIES,baseDelayMs:F.BASE_DELAY_MS,maxDelayMs:F.MAX_DELAY_MS};async function Ie(e,s,r=fe,o=false){let c=null;for(let a=0;a<=r.maxRetries;a++){try{o&&a>0&&console.log(`[Gotcha] Retry attempt ${a}/${r.maxRetries}`);let t=await fetch(e,s);if(t.status>=400&&t.status<500&&t.status!==429||t.ok)return t;c=new Error(`HTTP ${t.status}`);}catch(t){c=t,o&&console.log(`[Gotcha] Network error: ${c.message}`);}if(a<r.maxRetries){let t=Math.min(r.baseDelayMs*Math.pow(2,a),r.maxDelayMs);await new Promise(d=>setTimeout(d,t));}}throw c}function he(e){let{apiKey:s,baseUrl:r=pe,debug:o=false}=e,c={"Content-Type":"application/json",Authorization:`Bearer ${s}`};async function a(t,d,n){let l=`${r}${d}`,i=crypto.randomUUID();o&&console.log(`[Gotcha] ${t} ${d}`,n);let p=await Ie(l,{method:t,headers:{...c,"Idempotency-Key":i},body:n?JSON.stringify(n):void 0},fe,o),u=await p.json();if(!p.ok){let f=u.error;throw o&&console.error(`[Gotcha] Error: ${f.code} - ${f.message}`),f}return o&&console.log("[Gotcha] Response:",u),u}return {async submitResponse(t){let d=t.user||{};d.id||(d.id=me());let n={...t,user:d,context:{url:typeof window<"u"?window.location.href:void 0,userAgent:typeof navigator<"u"?navigator.userAgent:void 0}};return a("POST","/responses",n)},getBaseUrl(){return r}}}var ve=createContext(null);function _e({apiKey:e,children:s,baseUrl:r,debug:o=false,disabled:c=false,defaultUser:a={}}){let[t,d]=useState(null),n=useMemo(()=>he({apiKey:e,baseUrl:r,debug:o}),[e,r,o]),l=useCallback(u=>{d(u);},[]),i=useCallback(()=>{d(null);},[]),p=useMemo(()=>({client:n,disabled:c,defaultUser:a,debug:o,activeModalId:t,openModal:l,closeModal:i}),[n,c,a,o,t,l,i]);return jsx(ve.Provider,{value:p,children:s})}function A(){let e=useContext(ve);if(!e)throw new Error("useGotchaContext must be used within a GotchaProvider");return e}function Se(e){let{client:s,defaultUser:r}=A(),[o,c]=useState(false),[a,t]=useState(null);return {submit:useCallback(async n=>{c(true),t(null);try{let l=await s.submitResponse({elementId:e.elementId,mode:e.mode,content:n.content,title:n.title,rating:n.rating,vote:n.vote,pollOptions:e.pollOptions,pollSelected:n.pollSelected,experimentId:e.experimentId,variant:e.variant,user:{...r,...e.user}});return e.onSuccess?.(l),l}catch(l){let i=l instanceof Error?l.message:"Something went wrong";throw t(i),e.onError?.(l instanceof Error?l:new Error(i)),l}finally{c(false);}},[s,r,e]),isLoading:o,error:a,clearError:()=>t(null)}}function $(...e){return e.filter(Boolean).join(" ")}var _=()=>typeof window>"u"?false:"ontouchstart"in window||navigator.maxTouchPoints>0,xe=(e,s)=>{let r={sm:{desktop:24,mobile:32},md:{desktop:32,mobile:36},lg:{desktop:40,mobile:40}};return s?r[e].mobile:r[e].desktop};function Ee({size:e,theme:s,customStyles:r,showOnHover:o,touchBehavior:c,onClick:a,isOpen:t,isParentHovered:d=false}){let[n,l]=useState(false),[i,p]=useState(false),[u,f]=useState("light");useEffect(()=>{l(_());let G=window.matchMedia("(prefers-color-scheme: dark)");f(G.matches?"dark":"light");let R=T=>f(T.matches?"dark":"light");return G.addEventListener("change",R),()=>G.removeEventListener("change",R)},[]);let y=t?true:!n&&o?d:n&&c==="tap-to-reveal"?i:true,g=()=>{if(n&&c==="tap-to-reveal"&&!i){p(true);return}a();},b=xe(e,n),m=s==="auto"?u:s,C={width:b,height:b,borderRadius:"50%",border:"none",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:m==="dark"?"#374151":"#c7d2dc",color:m==="dark"?"#e5e7eb":"#4b5563",boxShadow:"0 1px 3px rgba(0, 0, 0, 0.1)",transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:y?1:0,transform:y?"scale(1)":"scale(0.6)",pointerEvents:y?"auto":"none",...r?.button};return jsx("button",{type:"button",onClick:g,style:C,className:$("gotcha-button",t&&"gotcha-button--open"),"aria-label":"Give feedback on this feature","aria-expanded":t,"aria-haspopup":"dialog",children:jsx(Le,{size:b*.75})})}function Le({size:e}){return jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:jsx("text",{x:"50%",y:"50%",dominantBaseline:"central",textAnchor:"middle",fontSize:"16",fontWeight:"bold",fill:"currentColor",fontFamily:"system-ui, -apple-system, sans-serif",children:"G"})})}function Re({theme:e,placeholder:s,submitText:r,isLoading:o,onSubmit:c,customStyles:a}){let[t,d]=useState(""),[n,l]=useState(null),[i,p]=useState(false);useEffect(()=>{p(_());},[]);let u=e==="dark",f=b=>{b.preventDefault(),!(!t.trim()&&n===null)&&c({content:t.trim()||void 0,rating:n??void 0});},y={width:"100%",padding:i?"12px 14px":"10px 12px",border:`1px solid ${u?"#374151":"#d1d5db"}`,borderRadius:i?8:6,backgroundColor:u?"#374151":"#ffffff",color:u?"#f9fafb":"#111827",fontSize:i?16:14,resize:"vertical",minHeight:i?100:80,fontFamily:"inherit",...a?.input},g={width:"100%",padding:i?"14px 16px":"10px 16px",border:"none",borderRadius:i?8:6,backgroundColor:o?u?"#4b5563":"#9ca3af":"#6366f1",color:"#ffffff",fontSize:i?16:14,fontWeight:500,cursor:o?"not-allowed":"pointer",transition:"background-color 150ms ease",...a?.submitButton};return jsxs("form",{onSubmit:f,children:[jsx("div",{style:{marginBottom:i?16:12},children:jsx(Ne,{value:n,onChange:l,isDark:u,isTouch:i})}),jsx("textarea",{value:t,onChange:b=>d(b.target.value),placeholder:s||"Share your thoughts...",style:y,disabled:o,"aria-label":"Your feedback"}),jsx("button",{type:"submit",disabled:o||!t.trim()&&n===null,style:{...g,marginTop:12,opacity:!t.trim()&&n===null?.5:1},children:o?"Submitting...":r})]})}function Ne({value:e,onChange:s,isDark:r,isTouch:o}){let[c,a]=useState(null),t=o?32:20,d=o?6:2;return jsx("div",{style:{display:"flex",gap:o?8:4},role:"group","aria-label":"Rating",children:[1,2,3,4,5].map(n=>{let l=(c??e??0)>=n;return jsx("button",{type:"button",onClick:()=>s(n),onMouseEnter:()=>a(n),onMouseLeave:()=>a(null),"aria-label":`Rate ${n} out of 5`,"aria-pressed":e===n,style:{background:"none",border:"none",cursor:"pointer",padding:d,color:l?"#f59e0b":r?"#4b5563":"#d1d5db",transition:"color 150ms ease"},children:jsx("svg",{width:t,height:t,viewBox:"0 0 24 24",fill:"currentColor",children:jsx("path",{d:"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"})})},n)})})}function Te({theme:e,isLoading:s,onSubmit:r}){let[o,c]=useState(false);useEffect(()=>{c(_());},[]);let a=e==="dark",t={flex:1,padding:o?"16px 20px":"12px 16px",border:`1px solid ${a?"#374151":"#e5e7eb"}`,borderRadius:o?12:8,backgroundColor:a?"#374151":"#f9fafb",color:a?"#f9fafb":"#111827",fontSize:o?28:24,cursor:s?"not-allowed":"pointer",transition:"all 150ms ease",display:"flex",alignItems:"center",justifyContent:"center",gap:o?10:8},d=o?28:24;return jsxs("div",{style:{display:"flex",gap:o?16:12},role:"group","aria-label":"Vote",children:[jsxs("button",{type:"button",onClick:()=>r({vote:"up"}),disabled:s,style:t,"aria-label":"Vote up - I like this",children:[jsx(Ve,{size:d}),jsx("span",{style:{fontSize:o?16:14,fontWeight:500},children:"Like"})]}),jsxs("button",{type:"button",onClick:()=>r({vote:"down"}),disabled:s,style:t,"aria-label":"Vote down - I don't like this",children:[jsx(Ye,{size:d}),jsx("span",{style:{fontSize:o?16:14,fontWeight:500},children:"Dislike"})]})]})}function Ve({size:e=24}){return jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:jsx("path",{d:"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"})})}function Ye({size:e=24}){return jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:jsx("path",{d:"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"})})}function oe({mode:e,theme:s,customStyles:r,promptText:o,placeholder:c,submitText:a,thankYouMessage:t,isLoading:d,isSubmitted:n,error:l,onSubmit:i,onClose:p,anchorRect:u}){let f=useRef(null),y=useRef(null),[g,b]=useState(false),[m,C]=useState(false),[G,R]=useState("light");useEffect(()=>{C(window.innerWidth<640);let S=window.matchMedia("(prefers-color-scheme: dark)");R(S.matches?"dark":"light");let k=v=>R(v.matches?"dark":"light");return S.addEventListener("change",k),()=>S.removeEventListener("change",k)},[]),useEffect(()=>{let S=requestAnimationFrame(()=>b(true));return ()=>cancelAnimationFrame(S)},[]);let T=s==="auto"?G:s,h=T==="dark",P=(u?window.innerHeight-u.bottom:window.innerHeight/2)<280+20;useEffect(()=>{let S=f.current;if(!S)return;y.current?.focus();let k=v=>{if(v.key==="Escape"){p();return}if(v.key==="Tab"){let U=S.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),N=U[0],H=U[U.length-1];v.shiftKey&&document.activeElement===N?(v.preventDefault(),H?.focus()):!v.shiftKey&&document.activeElement===H&&(v.preventDefault(),N?.focus());}};return document.addEventListener("keydown",k),()=>document.removeEventListener("keydown",k)},[p]);let O=e==="vote"?"What do you think?":"What do you think of this feature?",D=m?20:16,L=m?{position:"fixed",left:"50%",top:"50%",width:"calc(100vw - 32px)",maxWidth:320,padding:D,borderRadius:12,backgroundColor:h?"#1f2937":"#ffffff",color:h?"#f9fafb":"#111827",boxShadow:"0 10px 25px rgba(0, 0, 0, 0.2)",border:`1px solid ${h?"#374151":"#e5e7eb"}`,zIndex:9999,transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:g?1:0,transform:g?"translate(-50%, -50%) scale(1)":"translate(-50%, -50%) scale(0.95)",...r?.modal}:{position:"absolute",left:"50%",width:320,padding:D,borderRadius:8,backgroundColor:h?"#1f2937":"#ffffff",color:h?"#f9fafb":"#111827",boxShadow:"0 10px 25px rgba(0, 0, 0, 0.15)",border:`1px solid ${h?"#374151":"#e5e7eb"}`,zIndex:9999,...P?{bottom:"100%",marginBottom:8}:{top:"100%",marginTop:8},transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:g?1:0,transform:g?"translateX(-50%) scale(1) translateY(0)":`translateX(-50%) scale(0.95) translateY(${P?"10px":"-10px"})`,...r?.modal};return jsxs("div",{ref:f,role:"dialog","aria-modal":"true","aria-labelledby":"gotcha-modal-title",style:L,className:$("gotcha-modal",h&&"gotcha-modal--dark"),children:[jsx("button",{ref:y,type:"button",onClick:p,"aria-label":"Close feedback form",style:{position:"absolute",top:m?12:8,right:m?12:8,width:m?36:24,height:m?36:24,border:"none",background:"none",cursor:"pointer",color:h?"#9ca3af":"#6b7280",display:"flex",alignItems:"center",justifyContent:"center",borderRadius:4},children:jsx("svg",{width:m?18:14,height:m?18:14,viewBox:"0 0 14 14",fill:"none",children:jsx("path",{d:"M1 1L13 13M1 13L13 1",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})})}),jsx("h2",{id:"gotcha-modal-title",style:{margin:"0 0 12px 0",fontSize:m?16:14,fontWeight:500,paddingRight:m?40:24},children:o||O}),n&&jsxs("div",{style:{textAlign:"center",padding:"20px 0",color:h?"#10b981":"#059669"},children:[jsx("svg",{width:"32",height:"32",viewBox:"0 0 24 24",fill:"none",style:{margin:"0 auto 8px"},children:jsx("path",{d:"M20 6L9 17L4 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})}),jsx("p",{style:{margin:0,fontSize:14},children:t})]}),l&&!n&&jsx("div",{style:{padding:8,marginBottom:12,borderRadius:4,backgroundColor:h?"#7f1d1d":"#fef2f2",color:h?"#fecaca":"#dc2626",fontSize:13},children:l}),!n&&jsxs(Fragment,{children:[e==="feedback"&&jsx(Re,{theme:T,placeholder:c,submitText:a,isLoading:d,onSubmit:i,customStyles:r}),e==="vote"&&jsx(Te,{theme:T,isLoading:d,onSubmit:i})]}),jsxs("div",{"aria-live":"polite",className:"sr-only",style:{position:"absolute",left:-9999},children:[n&&"Thank you! Your feedback has been submitted.",l&&`Error: ${l}`]})]})}function Ke({elementId:e,user:s,mode:r="feedback",experimentId:o,variant:c,options:a,allowMultiple:t=false,showResults:d=true,position:n=w.POSITION,size:l=w.SIZE,theme:i=w.THEME,customStyles:p,visible:u=true,showOnHover:f=w.SHOW_ON_HOVER,touchBehavior:y=w.TOUCH_BEHAVIOR,promptText:g,placeholder:b,submitText:m=w.SUBMIT_TEXT,thankYouMessage:C=w.THANK_YOU_MESSAGE,onSubmit:G,onOpen:R,onClose:T,onError:h}){let{disabled:re,activeModalId:se,openModal:P,closeModal:O}=A(),[D,L]=useState(false),[S,k]=useState(false),[v,U]=useState(null),[N,H]=useState(false),V=useRef(null);useEffect(()=>{H(window.innerWidth<640);},[]);let Y=se===e;useEffect(()=>{if(!f)return;let x=V.current;if(!x)return;let B=x.parentElement;if(!B)return;let de=()=>k(true),ue=()=>k(false);return B.addEventListener("mouseenter",de),B.addEventListener("mouseleave",ue),()=>{B.removeEventListener("mouseenter",de),B.removeEventListener("mouseleave",ue);}},[f]);let{submit:ae,isLoading:ie,error:le}=Se({elementId:e,mode:r,experimentId:o,variant:c,pollOptions:a,user:s,onSuccess:x=>{L(true),G?.(x),setTimeout(()=>{O(),L(false);},2500);},onError:x=>{h?.(x);}}),Me=useCallback(()=>{V.current&&U(V.current.getBoundingClientRect()),P(e),R?.();},[e,P,R]),q=useCallback(()=>{O(),L(false),T?.();},[O,T]),ce=useCallback(x=>{ae(x);},[ae]);return re||!u?null:jsxs("div",{ref:V,style:{...{"top-right":{position:"absolute",top:0,right:0,transform:"translate(50%, -50%)"},"top-left":{position:"absolute",top:0,left:0,transform:"translate(-50%, -50%)"},"bottom-right":{position:"absolute",bottom:0,right:0,transform:"translate(50%, 50%)"},"bottom-left":{position:"absolute",bottom:0,left:0,transform:"translate(-50%, 50%)"},inline:{position:"relative",display:"inline-flex"}}[n],zIndex:Y?1e4:"auto"},className:"gotcha-container","data-gotcha-element":e,children:[jsx(Ee,{size:l,theme:i,customStyles:p,showOnHover:f,touchBehavior:y,onClick:Me,isOpen:Y,isParentHovered:S}),Y&&!N&&jsx(oe,{mode:r,theme:i,customStyles:p,promptText:g,placeholder:b,submitText:m,thankYouMessage:C,isLoading:ie,isSubmitted:D,error:le,onSubmit:ce,onClose:q,anchorRect:v||void 0}),Y&&N&&createPortal(jsx("div",{style:{position:"fixed",inset:0,zIndex:99999,backgroundColor:"rgba(0, 0, 0, 0.3)"},onClick:q,"aria-hidden":"true",children:jsx("div",{onClick:x=>x.stopPropagation(),children:jsx(oe,{mode:r,theme:i,customStyles:p,promptText:g,placeholder:b,submitText:m,thankYouMessage:C,isLoading:ie,isSubmitted:D,error:le,onSubmit:ce,onClose:q,anchorRect:v||void 0})})}),document.body)]})}function Qe(){let{client:e,disabled:s,defaultUser:r,debug:o}=A();return {client:e,disabled:s,defaultUser:r,debug:o,submitFeedback:e.submitResponse.bind(e)}}
2
- export{Ke as Gotcha,_e as GotchaProvider,Qe as useGotcha};//# sourceMappingURL=index.mjs.map
1
+ import {createContext,useState,useMemo,useCallback,useRef,useEffect,useContext}from'react';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {createPortal}from'react-dom';var Ee="https://api.gotcha.cx/v1";var se={ANONYMOUS_ID:"gotcha_anonymous_id"},T={POSITION:"top-right",SIZE:"md",THEME:"light",SHOW_ON_HOVER:true,TOUCH_BEHAVIOR:"always-visible",SUBMIT_TEXT:"Submit",THANK_YOU_MESSAGE:"Thanks for your feedback!"};var X={MAX_RETRIES:2,BASE_DELAY_MS:500,MAX_DELAY_MS:5e3};function Re(){if(typeof window>"u")return `anon_${crypto.randomUUID()}`;let e=localStorage.getItem(se.ANONYMOUS_ID);if(e)return e;let s=`anon_${crypto.randomUUID()}`;return localStorage.setItem(se.ANONYMOUS_ID,s),s}var Q={maxRetries:X.MAX_RETRIES,baseDelayMs:X.BASE_DELAY_MS,maxDelayMs:X.MAX_DELAY_MS};async function ie(e,s,i=Q,n=false){let p=null;for(let a=0;a<=i.maxRetries;a++){try{n&&a>0&&console.log(`[Gotcha] Retry attempt ${a}/${i.maxRetries}`);let t=await fetch(e,s);if(t.status>=400&&t.status<500&&t.status!==429||t.ok)return t;p=new Error(`HTTP ${t.status}`);}catch(t){p=t,n&&console.log(`[Gotcha] Network error: ${p.message}`);}if(a<i.maxRetries){let t=Math.min(i.baseDelayMs*Math.pow(2,a),i.maxDelayMs);await new Promise(u=>setTimeout(u,t));}}throw p}function ke(e){let{apiKey:s,baseUrl:i=Ee,debug:n=false}=e,p={"Content-Type":"application/json",Authorization:`Bearer ${s}`};async function a(t,u,r){let c=`${i}${u}`,l=crypto.randomUUID();n&&console.log(`[Gotcha] ${t} ${u}`,r);let f=await ie(c,{method:t,headers:{...p,"Idempotency-Key":l},body:r?JSON.stringify(r):void 0},Q,n),o=await f.json();if(!f.ok){let d=o.error;throw n&&console.error(`[Gotcha] Error: ${d.code} - ${d.message}`),d}return n&&console.log("[Gotcha] Response:",o),o}return {async submitResponse(t){let u=t.user||{};u.id||(u.id=Re());let r={...t,user:u,context:{url:typeof window<"u"?window.location.href:void 0,userAgent:typeof navigator<"u"?navigator.userAgent:void 0}};return a("POST","/responses",r)},async checkExistingResponse(t,u){let r=`${i}/responses/check?elementId=${encodeURIComponent(t)}&userId=${encodeURIComponent(u)}`;n&&console.log("[Gotcha] GET /responses/check");let c=await ie(r,{method:"GET",headers:p},Q,n),l=await c.json();if(!c.ok){let f=l.error;throw n&&console.error(`[Gotcha] Error: ${f.code} - ${f.message}`),f}return l.exists?(n&&console.log("[Gotcha] Found existing response:",l.response),l.response):null},async updateResponse(t,u,r){let c=`${i}/responses/${t}${r?`?userId=${encodeURIComponent(r)}`:""}`;n&&console.log(`[Gotcha] PATCH /responses/${t}`,u);let l=await ie(c,{method:"PATCH",headers:p,body:JSON.stringify(u)},Q,n),f=await l.json();if(!l.ok){let o=f.error;throw n&&console.error(`[Gotcha] Error: ${o.code} - ${o.message}`),o}return n&&console.log("[Gotcha] Response updated:",f),f},getBaseUrl(){return i}}}var Ge=createContext(null);function He({apiKey:e,children:s,baseUrl:i,debug:n=false,disabled:p=false,defaultUser:a={}}){let[t,u]=useState(null),r=useMemo(()=>ke({apiKey:e,baseUrl:i,debug:n}),[e,i,n]),c=useCallback(o=>{u(o);},[]),l=useCallback(()=>{u(null);},[]),f=useMemo(()=>({client:r,disabled:p,defaultUser:a,debug:n,activeModalId:t,openModal:c,closeModal:l}),[r,p,a,n,t,c,l]);return jsx(Ge.Provider,{value:f,children:s})}function _(){let e=useContext(Ge);if(!e)throw new Error("useGotchaContext must be used within a GotchaProvider");return e}function Ie(e){let{client:s,defaultUser:i}=_(),[n,p]=useState(false),[a,t]=useState(false),[u,r]=useState(null),[c,l]=useState(null);return useEffect(()=>{let o=e.user?.id||i?.id;if(!o){l(null);return}let d=false;return (async()=>{t(true);try{let h=await s.checkExistingResponse(e.elementId,o);d||l(h);}catch{d||l(null);}finally{d||t(false);}})(),()=>{d=true;}},[s,e.elementId,e.user?.id,i?.id]),{submit:useCallback(async o=>{p(true),r(null);try{let d=e.user?.id||i?.id,m;return c&&d?m=await s.updateResponse(c.id,{content:o.content,title:o.title,rating:o.rating,vote:o.vote,pollSelected:o.pollSelected},d):m=await s.submitResponse({elementId:e.elementId,mode:e.mode,content:o.content,title:o.title,rating:o.rating,vote:o.vote,pollOptions:e.pollOptions,pollSelected:o.pollSelected,experimentId:e.experimentId,variant:e.variant,user:{...i,...e.user}}),e.onSuccess?.(m),m}catch(d){let m=d instanceof Error?d.message:"Something went wrong";throw r(m),e.onError?.(d instanceof Error?d:new Error(m)),d}finally{p(false);}},[s,i,e,c]),isLoading:n,isCheckingExisting:a,error:u,existingResponse:c,isEditing:!!c,clearError:()=>r(null)}}function j(...e){return e.filter(Boolean).join(" ")}var O=()=>typeof window>"u"?false:"ontouchstart"in window||navigator.maxTouchPoints>0,Me=(e,s)=>{let i={sm:{desktop:24,mobile:32},md:{desktop:32,mobile:36},lg:{desktop:40,mobile:40}};return s?i[e].mobile:i[e].desktop};function Ce({size:e,theme:s,customStyles:i,showOnHover:n,touchBehavior:p,onClick:a,isOpen:t,isParentHovered:u=false}){let[r,c]=useState(false),[l,f]=useState(false),[o,d]=useState("light");useEffect(()=>{c(O());let g=window.matchMedia("(prefers-color-scheme: dark)");d(g.matches?"dark":"light");let I=P=>d(P.matches?"dark":"light");return g.addEventListener("change",I),()=>g.removeEventListener("change",I)},[]);let m=t?true:!r&&n?u:r&&p==="tap-to-reveal"?l:true,h=()=>{if(r&&p==="tap-to-reveal"&&!l){f(true);return}a();},b=Me(e,r),S=s==="auto"?o:s,R={width:b,height:b,borderRadius:"50%",border:"none",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:S==="dark"?"#374151":"#c7d2dc",color:S==="dark"?"#e5e7eb":"#4b5563",boxShadow:"0 1px 3px rgba(0, 0, 0, 0.1)",transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:m?1:0,transform:m?"scale(1)":"scale(0.6)",pointerEvents:m?"auto":"none",...i?.button};return jsx("button",{type:"button",onClick:h,style:R,className:j("gotcha-button",t&&"gotcha-button--open"),"aria-label":"Give feedback on this feature","aria-expanded":t,"aria-haspopup":"dialog",children:jsx(We,{size:b*.75})})}function We({size:e}){return jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:jsx("text",{x:"50%",y:"50%",dominantBaseline:"central",textAnchor:"middle",fontSize:"16",fontWeight:"bold",fill:"currentColor",fontFamily:"system-ui, -apple-system, sans-serif",children:"G"})})}function B({size:e=16,color:s="currentColor"}){return jsxs("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",style:{animation:"gotcha-spin 1s linear infinite"},children:[jsx("style",{children:`
2
+ @keyframes gotcha-spin {
3
+ from { transform: rotate(0deg); }
4
+ to { transform: rotate(360deg); }
5
+ }
6
+ `}),jsx("circle",{cx:"12",cy:"12",r:"10",stroke:s,strokeWidth:"3",strokeOpacity:"0.25"}),jsx("path",{d:"M12 2a10 10 0 0 1 10 10",stroke:s,strokeWidth:"3",strokeLinecap:"round"})]})}function _e({theme:e,placeholder:s,submitText:i,isLoading:n,onSubmit:p,customStyles:a,initialValues:t,isEditing:u=false}){let[r,c]=useState(t?.content||""),[l,f]=useState(t?.rating??null),[o,d]=useState(false);useEffect(()=>{d(O());},[]),useEffect(()=>{t?.content!==void 0&&c(t.content||""),t?.rating!==void 0&&f(t.rating??null);},[t?.content,t?.rating]);let m=e==="dark",h=R=>{R.preventDefault(),!(!r.trim()&&l===null)&&p({content:r.trim()||void 0,rating:l??void 0});},b={width:"100%",padding:o?"12px 14px":"10px 12px",border:`1px solid ${m?"#374151":"#d1d5db"}`,borderRadius:o?8:6,backgroundColor:m?"#374151":"#ffffff",color:m?"#f9fafb":"#111827",fontSize:o?16:14,resize:"vertical",minHeight:o?100:80,fontFamily:"inherit",...a?.input},S={width:"100%",padding:o?"14px 16px":"10px 16px",border:"none",borderRadius:o?8:6,backgroundColor:n?m?"#4b5563":"#9ca3af":"#6366f1",color:"#ffffff",fontSize:o?16:14,fontWeight:500,cursor:n?"not-allowed":"pointer",transition:"background-color 150ms ease",...a?.submitButton};return jsxs("form",{onSubmit:h,children:[jsx("div",{style:{marginBottom:o?16:12},children:jsx(Xe,{value:l,onChange:f,isDark:m,isTouch:o})}),jsx("textarea",{value:r,onChange:R=>c(R.target.value),placeholder:s||"Share your thoughts...",style:b,disabled:n,"aria-label":"Your feedback"}),jsxs("button",{type:"submit",disabled:n||!r.trim()&&l===null,style:{...S,marginTop:12,opacity:!r.trim()&&l===null?.5:1,display:"flex",alignItems:"center",justifyContent:"center",gap:8},children:[n&&jsx(B,{size:o?18:16,color:"#ffffff"}),n?u?"Updating...":"Submitting...":u?"Update":i]})]})}function Xe({value:e,onChange:s,isDark:i,isTouch:n}){let[p,a]=useState(null),t=n?32:20,u=n?6:2;return jsx("div",{style:{display:"flex",gap:n?8:4},role:"group","aria-label":"Rating",children:[1,2,3,4,5].map(r=>{let c=(p??e??0)>=r;return jsx("button",{type:"button",onClick:()=>s(r),onMouseEnter:()=>a(r),onMouseLeave:()=>a(null),"aria-label":`Rate ${r} out of 5`,"aria-pressed":e===r,style:{background:"none",border:"none",cursor:"pointer",padding:u,color:c?"#f59e0b":i?"#4b5563":"#d1d5db",transition:"color 150ms ease"},children:jsx("svg",{width:t,height:t,viewBox:"0 0 24 24",fill:"currentColor",children:jsx("path",{d:"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"})})},r)})})}function Oe({theme:e,isLoading:s,onSubmit:i,initialVote:n,isEditing:p=false}){let[a,t]=useState(false),[u,r]=useState(n||null),[c,l]=useState(n||null);useEffect(()=>{t(O());},[]),useEffect(()=>{n!==void 0&&(l(n),r(n));},[n]),useEffect(()=>{!s&&!p&&r(null);},[s,p]);let f=h=>{r(h),i({vote:h});},o=e==="dark",d=h=>{let b=c===h;return {flex:1,padding:a?"16px 20px":"12px 16px",border:b?`2px solid ${h==="up"?"#22c55e":"#ef4444"}`:`1px solid ${o?"#374151":"#e5e7eb"}`,borderRadius:a?12:8,backgroundColor:b?h==="up"?o?"#14532d":"#dcfce7":o?"#450a0a":"#fee2e2":o?"#374151":"#f9fafb",color:o?"#f9fafb":"#111827",fontSize:a?28:24,cursor:s?"not-allowed":"pointer",transition:"all 150ms ease",display:"flex",alignItems:"center",justifyContent:"center",gap:a?10:8}},m=a?28:24;return jsxs("div",{style:{display:"flex",gap:a?16:12},role:"group","aria-label":"Vote",children:[jsx("button",{type:"button",onClick:()=>f("up"),disabled:s,style:d("up"),"aria-label":"Vote up - I like this","aria-pressed":c==="up",children:s&&u==="up"?jsxs(Fragment,{children:[jsx(B,{size:m,color:o?"#f9fafb":"#111827"}),jsx("span",{style:{fontSize:a?16:14,fontWeight:500},children:p?"Updating...":"Sending..."})]}):jsxs(Fragment,{children:[jsx(Qe,{size:m}),jsx("span",{style:{fontSize:a?16:14,fontWeight:500},children:c==="up"?"Liked":"Like"})]})}),jsx("button",{type:"button",onClick:()=>f("down"),disabled:s,style:d("down"),"aria-label":"Vote down - I don't like this","aria-pressed":c==="down",children:s&&u==="down"?jsxs(Fragment,{children:[jsx(B,{size:m,color:o?"#f9fafb":"#111827"}),jsx("span",{style:{fontSize:a?16:14,fontWeight:500},children:p?"Updating...":"Sending..."})]}):jsxs(Fragment,{children:[jsx(qe,{size:m}),jsx("span",{style:{fontSize:a?16:14,fontWeight:500},children:c==="down"?"Disliked":"Dislike"})]})})]})}function Qe({size:e=24}){return jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:jsx("path",{d:"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"})})}function qe({size:e=24}){return jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:jsx("path",{d:"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"})})}function fe({mode:e,theme:s,customStyles:i,promptText:n,placeholder:p,submitText:a,thankYouMessage:t,isLoading:u,isSubmitted:r,error:c,existingResponse:l,isEditing:f=false,onSubmit:o,onClose:d,anchorRect:m}){let h=useRef(null),b=useRef(null),[S,R]=useState(false),[g,I]=useState(false),[P,Y]=useState("light");useEffect(()=>{I(window.innerWidth<640);let x=window.matchMedia("(prefers-color-scheme: dark)");Y(x.matches?"dark":"light");let M=E=>Y(E.matches?"dark":"light");return x.addEventListener("change",M),()=>x.removeEventListener("change",M)},[]),useEffect(()=>{let x=requestAnimationFrame(()=>R(true));return ()=>cancelAnimationFrame(x)},[]);let D=s==="auto"?P:s,y=D==="dark",U=(m?window.innerHeight-m.bottom:window.innerHeight/2)<280+20;useEffect(()=>{let x=h.current;if(!x)return;b.current?.focus();let M=E=>{if(E.key==="Escape"){d();return}if(E.key==="Tab"){let N=x.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),C=N[0],A=N[N.length-1];E.shiftKey&&document.activeElement===C?(E.preventDefault(),A?.focus()):!E.shiftKey&&document.activeElement===A&&(E.preventDefault(),C?.focus());}};return document.addEventListener("keydown",M),()=>document.removeEventListener("keydown",M)},[d]);let L=e==="vote"?"What do you think?":"What do you think of this feature?",F=g?20:16,W=g?{position:"fixed",left:"50%",top:"50%",width:"calc(100vw - 32px)",maxWidth:320,padding:F,borderRadius:12,backgroundColor:y?"#1f2937":"#ffffff",color:y?"#f9fafb":"#111827",boxShadow:"0 10px 25px rgba(0, 0, 0, 0.2)",border:`1px solid ${y?"#374151":"#e5e7eb"}`,zIndex:9999,transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:S?1:0,transform:S?"translate(-50%, -50%) scale(1)":"translate(-50%, -50%) scale(0.95)",...i?.modal}:{position:"absolute",left:"50%",width:320,padding:F,borderRadius:8,backgroundColor:y?"#1f2937":"#ffffff",color:y?"#f9fafb":"#111827",boxShadow:"0 10px 25px rgba(0, 0, 0, 0.15)",border:`1px solid ${y?"#374151":"#e5e7eb"}`,zIndex:9999,...U?{bottom:"100%",marginBottom:8}:{top:"100%",marginTop:8},transition:"opacity 0.2s ease-out, transform 0.2s ease-out",opacity:S?1:0,transform:S?"translateX(-50%) scale(1) translateY(0)":`translateX(-50%) scale(0.95) translateY(${U?"10px":"-10px"})`,...i?.modal};return jsxs("div",{ref:h,role:"dialog","aria-modal":"true","aria-labelledby":"gotcha-modal-title",style:W,className:j("gotcha-modal",y&&"gotcha-modal--dark"),children:[jsx("button",{ref:b,type:"button",onClick:d,"aria-label":"Close feedback form",style:{position:"absolute",top:g?12:8,right:g?12:8,width:g?36:24,height:g?36:24,border:"none",background:"none",cursor:"pointer",color:y?"#9ca3af":"#6b7280",display:"flex",alignItems:"center",justifyContent:"center",borderRadius:4},children:jsx("svg",{width:g?18:14,height:g?18:14,viewBox:"0 0 14 14",fill:"none",children:jsx("path",{d:"M1 1L13 13M1 13L13 1",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})})}),jsx("h2",{id:"gotcha-modal-title",style:{margin:"0 0 12px 0",fontSize:g?16:14,fontWeight:500,paddingRight:g?40:24},children:n||L}),r&&jsxs("div",{style:{textAlign:"center",padding:"20px 0",color:y?"#10b981":"#059669"},children:[jsx("svg",{width:"32",height:"32",viewBox:"0 0 24 24",fill:"none",style:{margin:"0 auto 8px"},children:jsx("path",{d:"M20 6L9 17L4 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})}),jsx("p",{style:{margin:0,fontSize:14},children:t})]}),c&&!r&&jsx("div",{style:{padding:8,marginBottom:12,borderRadius:4,backgroundColor:y?"#7f1d1d":"#fef2f2",color:y?"#fecaca":"#dc2626",fontSize:13},children:c}),!r&&jsxs(Fragment,{children:[e==="feedback"&&jsx(_e,{theme:D,placeholder:p,submitText:a,isLoading:u,onSubmit:o,customStyles:i,initialValues:l?{content:l.content,rating:l.rating}:void 0,isEditing:f}),e==="vote"&&jsx(Oe,{theme:D,isLoading:u,onSubmit:o,initialVote:l?.vote||void 0,isEditing:f})]}),jsxs("div",{"aria-live":"polite",className:"sr-only",style:{position:"absolute",left:-9999},children:[r&&"Thank you! Your feedback has been submitted.",c&&`Error: ${c}`]})]})}function et({elementId:e,user:s,mode:i="feedback",experimentId:n,variant:p,options:a,allowMultiple:t=false,showResults:u=true,position:r=T.POSITION,size:c=T.SIZE,theme:l=T.THEME,customStyles:f,visible:o=true,showOnHover:d=T.SHOW_ON_HOVER,touchBehavior:m=T.TOUCH_BEHAVIOR,promptText:h,placeholder:b,submitText:S=T.SUBMIT_TEXT,thankYouMessage:R=T.THANK_YOU_MESSAGE,onSubmit:g,onOpen:I,onClose:P,onError:Y}){let{disabled:D,activeModalId:y,openModal:ne,closeModal:V}=_(),[U,L]=useState(false),[F,W]=useState(false),[x,M]=useState(null),[E,N]=useState(false),C=useRef(null);useEffect(()=>{N(window.innerWidth<640);},[]);let A=y===e;useEffect(()=>{if(!d)return;let k=C.current;if(!k)return;let z=k.parentElement;if(!z)return;let Se=()=>W(true),xe=()=>W(false);return z.addEventListener("mouseenter",Se),z.addEventListener("mouseleave",xe),()=>{z.removeEventListener("mouseenter",Se),z.removeEventListener("mouseleave",xe);}},[d]);let{submit:he,isLoading:ge,error:be,existingResponse:ve,isEditing:K}=Ie({elementId:e,mode:i,experimentId:n,variant:p,pollOptions:a,user:s,onSuccess:k=>{L(true),g?.(k),setTimeout(()=>{V(),L(false);},2500);},onError:k=>{Y?.(k);}}),Le=useCallback(()=>{C.current&&M(C.current.getBoundingClientRect()),ne(e),I?.();},[e,ne,I]),re=useCallback(()=>{V(),L(false),P?.();},[V,P]),ye=useCallback(k=>{he(k);},[he]);return D||!o?null:jsxs("div",{ref:C,style:{...{"top-right":{position:"absolute",top:0,right:0,transform:"translate(50%, -50%)"},"top-left":{position:"absolute",top:0,left:0,transform:"translate(-50%, -50%)"},"bottom-right":{position:"absolute",bottom:0,right:0,transform:"translate(50%, 50%)"},"bottom-left":{position:"absolute",bottom:0,left:0,transform:"translate(-50%, 50%)"},inline:{position:"relative",display:"inline-flex"}}[r],zIndex:A?1e4:"auto"},className:"gotcha-container","data-gotcha-element":e,children:[jsx(Ce,{size:c,theme:l,customStyles:f,showOnHover:d,touchBehavior:m,onClick:Le,isOpen:A,isParentHovered:F}),A&&!E&&jsx(fe,{mode:i,theme:l,customStyles:f,promptText:h,placeholder:b,submitText:S,thankYouMessage:K?"Your feedback has been updated!":R,isLoading:ge,isSubmitted:U,error:be,existingResponse:ve,isEditing:K,onSubmit:ye,onClose:re,anchorRect:x||void 0}),A&&E&&createPortal(jsx("div",{style:{position:"fixed",inset:0,zIndex:99999,backgroundColor:"rgba(0, 0, 0, 0.3)"},onClick:re,"aria-hidden":"true",children:jsx("div",{onClick:k=>k.stopPropagation(),children:jsx(fe,{mode:i,theme:l,customStyles:f,promptText:h,placeholder:b,submitText:S,thankYouMessage:K?"Your feedback has been updated!":R,isLoading:ge,isSubmitted:U,error:be,existingResponse:ve,isEditing:K,onSubmit:ye,onClose:re,anchorRect:x||void 0})})}),document.body)]})}function ot(){let{client:e,disabled:s,defaultUser:i,debug:n}=_();return {client:e,disabled:s,defaultUser:i,debug:n,submitFeedback:e.submitResponse.bind(e)}}export{et as Gotcha,He as GotchaProvider,ot as useGotcha};//# sourceMappingURL=index.mjs.map
3
7
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/utils/anonymous.ts","../src/api/client.ts","../src/components/GotchaProvider.tsx","../src/hooks/useSubmit.ts","../src/utils/cn.ts","../src/utils/device.ts","../src/components/GotchaButton.tsx","../src/components/modes/FeedbackMode.tsx","../src/components/modes/VoteMode.tsx","../src/components/GotchaModal.tsx","../src/components/Gotcha.tsx","../src/hooks/useGotcha.ts"],"names":["API_BASE_URL","STORAGE_KEYS","DEFAULTS","RETRY_CONFIG","getAnonymousId","stored","id","DEFAULT_RETRY_CONFIG","fetchWithRetry","url","options","config","debug","lastError","attempt","response","error","delay","resolve","createApiClient","apiKey","baseUrl","headers","request","method","endpoint","body","idempotencyKey","data","payload","user","fullPayload","GotchaContext","createContext","GotchaProvider","children","disabled","defaultUser","activeModalId","setActiveModalId","useState","client","useMemo","openModal","useCallback","elementId","closeModal","value","jsx","useGotchaContext","context","useContext","useSubmit","isLoading","setIsLoading","setError","err","errorMessage","cn","classes","isTouchDevice","getResponsiveSize","size","isTouch","sizes","GotchaButton","theme","customStyles","showOnHover","touchBehavior","onClick","isOpen","isParentHovered","setIsTouch","tapRevealed","setTapRevealed","systemTheme","setSystemTheme","useEffect","darkQuery","handler","e","shouldShow","handleClick","buttonSize","resolvedTheme","baseStyles","GotchaIcon","FeedbackMode","placeholder","submitText","onSubmit","content","setContent","rating","setRating","isDark","handleSubmit","inputStyles","buttonStyles","jsxs","StarRating","onChange","hovered","setHovered","starSize","buttonPadding","star","isFilled","VoteMode","buttonBase","iconSize","ThumbsUpIcon","ThumbsDownIcon","GotchaModal","mode","promptText","thankYouMessage","isSubmitted","onClose","anchorRect","modalRef","useRef","firstFocusableRef","isVisible","setIsVisible","isMobile","setIsMobile","timer","showAbove","modal","handleKeyDown","focusableElements","firstElement","lastElement","defaultPrompt","modalPadding","modalStyles","Fragment","Gotcha","experimentId","variant","allowMultiple","showResults","position","visible","onOpen","onError","setIsSubmitted","setIsParentHovered","setAnchorRect","containerRef","container","parent","handleMouseEnter","handleMouseLeave","submit","handleOpen","handleClose","createPortal","useGotcha"],"mappings":"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","file":"index.mjs","sourcesContent":["// API URLs\nexport const API_BASE_URL = 'https://api.gotcha.cx/v1';\nexport const API_STAGING_URL = 'https://api.staging.gotcha.cx/v1';\n\n// Error codes\nexport const ERROR_CODES = {\n INVALID_API_KEY: 'INVALID_API_KEY',\n ORIGIN_NOT_ALLOWED: 'ORIGIN_NOT_ALLOWED',\n RATE_LIMITED: 'RATE_LIMITED',\n QUOTA_EXCEEDED: 'QUOTA_EXCEEDED',\n INVALID_REQUEST: 'INVALID_REQUEST',\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n} as const;\n\n// Local storage keys\nexport const STORAGE_KEYS = {\n ANONYMOUS_ID: 'gotcha_anonymous_id',\n OFFLINE_QUEUE: 'gotcha_offline_queue',\n} as const;\n\n// Default values\nexport const DEFAULTS = {\n POSITION: 'top-right' as const,\n SIZE: 'md' as const,\n THEME: 'light' as const,\n SHOW_ON_HOVER: true,\n TOUCH_BEHAVIOR: 'always-visible' as const,\n SUBMIT_TEXT: 'Submit',\n THANK_YOU_MESSAGE: 'Thanks for your feedback!',\n} as const;\n\n// Size mappings (desktop/mobile in pixels)\nexport const SIZE_MAP = {\n sm: { desktop: 24, mobile: 44 },\n md: { desktop: 32, mobile: 44 },\n lg: { desktop: 40, mobile: 48 },\n} as const;\n\n// Retry config\nexport const RETRY_CONFIG = {\n MAX_RETRIES: 2,\n BASE_DELAY_MS: 500,\n MAX_DELAY_MS: 5000,\n} as const;\n","import { STORAGE_KEYS } from '../constants';\n\n/**\n * Get or create an anonymous user ID\n * Stored in localStorage for consistency across sessions\n */\nexport function getAnonymousId(): string {\n if (typeof window === 'undefined') {\n // SSR fallback - generate but don't persist\n return `anon_${crypto.randomUUID()}`;\n }\n\n const stored = localStorage.getItem(STORAGE_KEYS.ANONYMOUS_ID);\n if (stored) return stored;\n\n const id = `anon_${crypto.randomUUID()}`;\n localStorage.setItem(STORAGE_KEYS.ANONYMOUS_ID, id);\n return id;\n}\n\n/**\n * Clear the anonymous ID (useful for testing)\n */\nexport function clearAnonymousId(): void {\n if (typeof window !== 'undefined') {\n localStorage.removeItem(STORAGE_KEYS.ANONYMOUS_ID);\n }\n}\n","import { API_BASE_URL, RETRY_CONFIG } from '../constants';\nimport { SubmitResponsePayload, GotchaResponse, GotchaError } from '../types';\nimport { getAnonymousId } from '../utils/anonymous';\n\ninterface ApiClientConfig {\n apiKey: string;\n baseUrl?: string;\n debug?: boolean;\n}\n\ninterface RetryConfig {\n maxRetries: number;\n baseDelayMs: number;\n maxDelayMs: number;\n}\n\nconst DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxRetries: RETRY_CONFIG.MAX_RETRIES,\n baseDelayMs: RETRY_CONFIG.BASE_DELAY_MS,\n maxDelayMs: RETRY_CONFIG.MAX_DELAY_MS,\n};\n\n/**\n * Fetch with automatic retry and exponential backoff\n */\nasync function fetchWithRetry(\n url: string,\n options: RequestInit,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n debug: boolean = false\n): Promise<Response> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= config.maxRetries; attempt++) {\n try {\n if (debug && attempt > 0) {\n console.log(`[Gotcha] Retry attempt ${attempt}/${config.maxRetries}`);\n }\n\n const response = await fetch(url, options);\n\n // Don't retry client errors (4xx) except 429 (rate limit)\n if (response.status >= 400 && response.status < 500 && response.status !== 429) {\n return response;\n }\n\n // Success - return immediately\n if (response.ok) {\n return response;\n }\n\n lastError = new Error(`HTTP ${response.status}`);\n } catch (error) {\n // Network error - retry\n lastError = error as Error;\n if (debug) {\n console.log(`[Gotcha] Network error: ${lastError.message}`);\n }\n }\n\n // Don't delay after last attempt\n if (attempt < config.maxRetries) {\n const delay = Math.min(\n config.baseDelayMs * Math.pow(2, attempt),\n config.maxDelayMs\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n}\n\nexport function createApiClient(config: ApiClientConfig) {\n const { apiKey, baseUrl = API_BASE_URL, debug = false } = config;\n\n const headers = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n };\n\n async function request<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<T> {\n const url = `${baseUrl}${endpoint}`;\n const idempotencyKey = crypto.randomUUID();\n\n if (debug) {\n console.log(`[Gotcha] ${method} ${endpoint}`, body);\n }\n\n const response = await fetchWithRetry(\n url,\n {\n method,\n headers: {\n ...headers,\n 'Idempotency-Key': idempotencyKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n },\n DEFAULT_RETRY_CONFIG,\n debug\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = data.error as GotchaError;\n if (debug) {\n console.error(`[Gotcha] Error: ${error.code} - ${error.message}`);\n }\n throw error;\n }\n\n if (debug) {\n console.log(`[Gotcha] Response:`, data);\n }\n\n return data as T;\n }\n\n return {\n /**\n * Submit a response (feedback, vote, etc.)\n */\n async submitResponse(\n payload: Omit<SubmitResponsePayload, 'context'>\n ): Promise<GotchaResponse> {\n // Ensure user has an ID (anonymous if not provided)\n const user = payload.user || {};\n if (!user.id) {\n user.id = getAnonymousId();\n }\n\n const fullPayload: SubmitResponsePayload = {\n ...payload,\n user,\n context: {\n url: typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n },\n };\n\n return request<GotchaResponse>('POST', '/responses', fullPayload);\n },\n\n /**\n * Get the base URL (for debugging)\n */\n getBaseUrl(): string {\n return baseUrl;\n },\n };\n}\n\nexport type ApiClient = ReturnType<typeof createApiClient>;\n","import React, { createContext, useContext, useMemo, useState, useCallback } from 'react';\nimport { createApiClient, ApiClient } from '../api/client';\nimport { GotchaUser } from '../types';\n\nexport interface GotchaProviderProps {\n /** Your Gotcha API key */\n apiKey: string;\n /** React children */\n children: React.ReactNode;\n /** Override the API base URL (for testing/staging) */\n baseUrl?: string;\n /** Enable debug logging */\n debug?: boolean;\n /** Disable all Gotcha buttons globally */\n disabled?: boolean;\n /** Default user metadata applied to all submissions */\n defaultUser?: GotchaUser;\n}\n\nexport interface GotchaContextValue {\n client: ApiClient;\n disabled: boolean;\n defaultUser: GotchaUser;\n debug: boolean;\n // Modal management - only one open at a time\n activeModalId: string | null;\n openModal: (elementId: string) => void;\n closeModal: () => void;\n}\n\nconst GotchaContext = createContext<GotchaContextValue | null>(null);\n\nexport function GotchaProvider({\n apiKey,\n children,\n baseUrl,\n debug = false,\n disabled = false,\n defaultUser = {},\n}: GotchaProviderProps) {\n const [activeModalId, setActiveModalId] = useState<string | null>(null);\n\n const client = useMemo(\n () => createApiClient({ apiKey, baseUrl, debug }),\n [apiKey, baseUrl, debug]\n );\n\n const openModal = useCallback((elementId: string) => {\n setActiveModalId(elementId);\n }, []);\n\n const closeModal = useCallback(() => {\n setActiveModalId(null);\n }, []);\n\n const value: GotchaContextValue = useMemo(\n () => ({\n client,\n disabled,\n defaultUser,\n debug,\n activeModalId,\n openModal,\n closeModal,\n }),\n [client, disabled, defaultUser, debug, activeModalId, openModal, closeModal]\n );\n\n return (\n <GotchaContext.Provider value={value}>{children}</GotchaContext.Provider>\n );\n}\n\nexport function useGotchaContext(): GotchaContextValue {\n const context = useContext(GotchaContext);\n if (!context) {\n throw new Error('useGotchaContext must be used within a GotchaProvider');\n }\n return context;\n}\n","import { useState, useCallback } from 'react';\nimport { useGotchaContext } from '../components/GotchaProvider';\nimport { ResponseMode, GotchaUser, GotchaResponse, VoteType } from '../types';\n\ninterface UseSubmitOptions {\n elementId: string;\n mode: ResponseMode;\n experimentId?: string;\n variant?: string;\n pollOptions?: string[];\n user?: GotchaUser;\n onSuccess?: (response: GotchaResponse) => void;\n onError?: (error: Error) => void;\n}\n\ninterface SubmitData {\n content?: string;\n title?: string;\n rating?: number;\n vote?: VoteType;\n pollSelected?: string[];\n}\n\nexport function useSubmit(options: UseSubmitOptions) {\n const { client, defaultUser } = useGotchaContext();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const submit = useCallback(\n async (data: SubmitData) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const response = await client.submitResponse({\n elementId: options.elementId,\n mode: options.mode,\n content: data.content,\n title: data.title,\n rating: data.rating,\n vote: data.vote,\n pollOptions: options.pollOptions,\n pollSelected: data.pollSelected,\n experimentId: options.experimentId,\n variant: options.variant,\n user: { ...defaultUser, ...options.user },\n });\n\n options.onSuccess?.(response);\n return response;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Something went wrong';\n setError(errorMessage);\n options.onError?.(err instanceof Error ? err : new Error(errorMessage));\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [client, defaultUser, options]\n );\n\n return {\n submit,\n isLoading,\n error,\n clearError: () => setError(null),\n };\n}\n","/**\n * Simple class name utility\n * Combines class names, filtering out falsy values\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n","/**\n * Detect if the device supports touch\n */\nexport const isTouchDevice = (): boolean => {\n if (typeof window === 'undefined') return false;\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0;\n};\n\n/**\n * Get the appropriate size based on device type\n */\nexport const getResponsiveSize = (\n size: 'sm' | 'md' | 'lg',\n isTouch: boolean\n): number => {\n const sizes = {\n sm: { desktop: 24, mobile: 32 },\n md: { desktop: 32, mobile: 36 },\n lg: { desktop: 40, mobile: 40 },\n };\n\n return isTouch ? sizes[size].mobile : sizes[size].desktop;\n};\n","import React, { useState, useEffect } from 'react';\nimport { Size, Theme, GotchaStyles } from '../types';\nimport { cn } from '../utils/cn';\nimport { isTouchDevice, getResponsiveSize } from '../utils/device';\n\nexport interface GotchaButtonProps {\n size: Size;\n theme: Theme;\n customStyles?: GotchaStyles;\n showOnHover: boolean;\n touchBehavior: 'always-visible' | 'tap-to-reveal';\n onClick: () => void;\n isOpen: boolean;\n isParentHovered?: boolean;\n}\n\nexport function GotchaButton({\n size,\n theme,\n customStyles,\n showOnHover,\n touchBehavior,\n onClick,\n isOpen,\n isParentHovered = false,\n}: GotchaButtonProps) {\n const [isTouch, setIsTouch] = useState(false);\n const [tapRevealed, setTapRevealed] = useState(false);\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n // Detect system theme preference\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemTheme(darkQuery.matches ? 'dark' : 'light');\n\n // Listen for theme changes\n const handler = (e: MediaQueryListEvent) => setSystemTheme(e.matches ? 'dark' : 'light');\n darkQuery.addEventListener('change', handler);\n return () => darkQuery.removeEventListener('change', handler);\n }, []);\n\n // Determine visibility\n const shouldShow = (() => {\n // Always show if modal is open\n if (isOpen) return true;\n\n // Desktop with showOnHover: only show when parent is hovered\n if (!isTouch && showOnHover) {\n return isParentHovered;\n }\n\n // Touch device with tap-to-reveal: show only after first tap\n if (isTouch && touchBehavior === 'tap-to-reveal') {\n return tapRevealed;\n }\n\n // All other cases: always visible\n return true;\n })();\n\n const handleClick = () => {\n // For tap-to-reveal on touch: first tap reveals, second tap opens\n if (isTouch && touchBehavior === 'tap-to-reveal' && !tapRevealed) {\n setTapRevealed(true);\n return;\n }\n onClick();\n };\n\n const buttonSize = getResponsiveSize(size, isTouch);\n\n // Determine theme colors\n const resolvedTheme = theme === 'auto' ? systemTheme : theme;\n\n const baseStyles: React.CSSProperties = {\n width: buttonSize,\n height: buttonSize,\n borderRadius: '50%',\n border: 'none',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: resolvedTheme === 'dark' ? '#374151' : '#c7d2dc',\n color: resolvedTheme === 'dark' ? '#e5e7eb' : '#4b5563',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)',\n // CSS transition for animations\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: shouldShow ? 1 : 0,\n transform: shouldShow ? 'scale(1)' : 'scale(0.6)',\n pointerEvents: shouldShow ? 'auto' : 'none',\n ...customStyles?.button,\n };\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n style={baseStyles}\n className={cn('gotcha-button', isOpen && 'gotcha-button--open')}\n aria-label=\"Give feedback on this feature\"\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n >\n <GotchaIcon size={buttonSize * 0.75} />\n </button>\n );\n}\n\nfunction GotchaIcon({ size }: { size: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <text\n x=\"50%\"\n y=\"50%\"\n dominantBaseline=\"central\"\n textAnchor=\"middle\"\n fontSize=\"16\"\n fontWeight=\"bold\"\n fill=\"currentColor\"\n fontFamily=\"system-ui, -apple-system, sans-serif\"\n >\n G\n </text>\n </svg>\n );\n}\n","import React, { useState, useEffect } from 'react';\nimport { GotchaStyles } from '../../types';\nimport { isTouchDevice } from '../../utils/device';\n\ninterface FeedbackModeProps {\n theme: 'light' | 'dark' | 'custom';\n placeholder?: string;\n submitText: string;\n isLoading: boolean;\n onSubmit: (data: { content?: string; rating?: number }) => void;\n customStyles?: GotchaStyles;\n}\n\nexport function FeedbackMode({\n theme,\n placeholder,\n submitText,\n isLoading,\n onSubmit,\n customStyles,\n}: FeedbackModeProps) {\n const [content, setContent] = useState('');\n const [rating, setRating] = useState<number | null>(null);\n const [isTouch, setIsTouch] = useState(false);\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n }, []);\n\n const isDark = theme === 'dark';\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!content.trim() && rating === null) return;\n onSubmit({ content: content.trim() || undefined, rating: rating ?? undefined });\n };\n\n const inputStyles: React.CSSProperties = {\n width: '100%',\n padding: isTouch ? '12px 14px' : '10px 12px',\n border: `1px solid ${isDark ? '#374151' : '#d1d5db'}`,\n borderRadius: isTouch ? 8 : 6,\n backgroundColor: isDark ? '#374151' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n fontSize: isTouch ? 16 : 14, // 16px prevents iOS zoom on focus\n resize: 'vertical',\n minHeight: isTouch ? 100 : 80,\n fontFamily: 'inherit',\n ...customStyles?.input,\n };\n\n const buttonStyles: React.CSSProperties = {\n width: '100%',\n padding: isTouch ? '14px 16px' : '10px 16px',\n border: 'none',\n borderRadius: isTouch ? 8 : 6,\n backgroundColor: isLoading ? (isDark ? '#4b5563' : '#9ca3af') : '#6366f1',\n color: '#ffffff',\n fontSize: isTouch ? 16 : 14,\n fontWeight: 500,\n cursor: isLoading ? 'not-allowed' : 'pointer',\n transition: 'background-color 150ms ease',\n ...customStyles?.submitButton,\n };\n\n return (\n <form onSubmit={handleSubmit}>\n {/* Rating (optional) */}\n <div style={{ marginBottom: isTouch ? 16 : 12 }}>\n <StarRating value={rating} onChange={setRating} isDark={isDark} isTouch={isTouch} />\n </div>\n\n {/* Text input */}\n <textarea\n value={content}\n onChange={(e) => setContent(e.target.value)}\n placeholder={placeholder || 'Share your thoughts...'}\n style={inputStyles}\n disabled={isLoading}\n aria-label=\"Your feedback\"\n />\n\n {/* Submit button */}\n <button\n type=\"submit\"\n disabled={isLoading || (!content.trim() && rating === null)}\n style={{\n ...buttonStyles,\n marginTop: 12,\n opacity: (!content.trim() && rating === null) ? 0.5 : 1,\n }}\n >\n {isLoading ? 'Submitting...' : submitText}\n </button>\n </form>\n );\n}\n\ninterface StarRatingProps {\n value: number | null;\n onChange: (rating: number) => void;\n isDark: boolean;\n isTouch: boolean;\n}\n\nfunction StarRating({ value, onChange, isDark, isTouch }: StarRatingProps) {\n const [hovered, setHovered] = useState<number | null>(null);\n const starSize = isTouch ? 32 : 20;\n const buttonPadding = isTouch ? 6 : 2;\n\n return (\n <div\n style={{\n display: 'flex',\n gap: isTouch ? 8 : 4,\n }}\n role=\"group\"\n aria-label=\"Rating\"\n >\n {[1, 2, 3, 4, 5].map((star) => {\n const isFilled = (hovered ?? value ?? 0) >= star;\n return (\n <button\n key={star}\n type=\"button\"\n onClick={() => onChange(star)}\n onMouseEnter={() => setHovered(star)}\n onMouseLeave={() => setHovered(null)}\n aria-label={`Rate ${star} out of 5`}\n aria-pressed={value === star}\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: buttonPadding,\n color: isFilled ? '#f59e0b' : (isDark ? '#4b5563' : '#d1d5db'),\n transition: 'color 150ms ease',\n }}\n >\n <svg width={starSize} height={starSize} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\" />\n </svg>\n </button>\n );\n })}\n </div>\n );\n}\n","import React, { useEffect, useState } from 'react';\nimport { isTouchDevice } from '../../utils/device';\n\ninterface VoteModeProps {\n theme: 'light' | 'dark' | 'custom';\n isLoading: boolean;\n onSubmit: (data: { vote: 'up' | 'down' }) => void;\n}\n\nexport function VoteMode({ theme, isLoading, onSubmit }: VoteModeProps) {\n const [isTouch, setIsTouch] = useState(false);\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n }, []);\n\n const isDark = theme === 'dark';\n\n const buttonBase: React.CSSProperties = {\n flex: 1,\n padding: isTouch ? '16px 20px' : '12px 16px',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n borderRadius: isTouch ? 12 : 8,\n backgroundColor: isDark ? '#374151' : '#f9fafb',\n color: isDark ? '#f9fafb' : '#111827',\n fontSize: isTouch ? 28 : 24,\n cursor: isLoading ? 'not-allowed' : 'pointer',\n transition: 'all 150ms ease',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: isTouch ? 10 : 8,\n };\n\n const iconSize = isTouch ? 28 : 24;\n\n return (\n <div\n style={{\n display: 'flex',\n gap: isTouch ? 16 : 12,\n }}\n role=\"group\"\n aria-label=\"Vote\"\n >\n <button\n type=\"button\"\n onClick={() => onSubmit({ vote: 'up' })}\n disabled={isLoading}\n style={buttonBase}\n aria-label=\"Vote up - I like this\"\n >\n <ThumbsUpIcon size={iconSize} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>Like</span>\n </button>\n\n <button\n type=\"button\"\n onClick={() => onSubmit({ vote: 'down' })}\n disabled={isLoading}\n style={buttonBase}\n aria-label=\"Vote down - I don't like this\"\n >\n <ThumbsDownIcon size={iconSize} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>Dislike</span>\n </button>\n </div>\n );\n}\n\nfunction ThumbsUpIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\" />\n </svg>\n );\n}\n\nfunction ThumbsDownIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\" />\n </svg>\n );\n}\n","import React, { useRef, useEffect, useState } from 'react';\nimport { Theme, GotchaStyles, ResponseMode } from '../types';\nimport { cn } from '../utils/cn';\nimport { FeedbackMode } from './modes/FeedbackMode';\nimport { VoteMode } from './modes/VoteMode';\n\nexport interface GotchaModalProps {\n mode: ResponseMode;\n theme: Theme;\n customStyles?: GotchaStyles;\n promptText?: string;\n placeholder?: string;\n submitText: string;\n thankYouMessage: string;\n // State\n isLoading: boolean;\n isSubmitted: boolean;\n error: string | null;\n // Handlers\n onSubmit: (data: { content?: string; rating?: number; vote?: 'up' | 'down' }) => void;\n onClose: () => void;\n // Position info from parent\n anchorRect?: DOMRect;\n}\n\nexport function GotchaModal({\n mode,\n theme,\n customStyles,\n promptText,\n placeholder,\n submitText,\n thankYouMessage,\n isLoading,\n isSubmitted,\n error,\n onSubmit,\n onClose,\n anchorRect,\n}: GotchaModalProps) {\n const modalRef = useRef<HTMLDivElement>(null);\n const firstFocusableRef = useRef<HTMLButtonElement>(null);\n const [isVisible, setIsVisible] = useState(false);\n const [isMobile, setIsMobile] = useState(false);\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');\n\n // Detect mobile and system theme after mount (SSR-safe)\n useEffect(() => {\n setIsMobile(window.innerWidth < 640);\n\n // Detect system theme preference\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemTheme(darkQuery.matches ? 'dark' : 'light');\n\n // Listen for theme changes\n const handler = (e: MediaQueryListEvent) => setSystemTheme(e.matches ? 'dark' : 'light');\n darkQuery.addEventListener('change', handler);\n return () => darkQuery.removeEventListener('change', handler);\n }, []);\n\n // Trigger animation after mount\n useEffect(() => {\n const timer = requestAnimationFrame(() => setIsVisible(true));\n return () => cancelAnimationFrame(timer);\n }, []);\n\n // Resolve theme\n const resolvedTheme = theme === 'auto' ? systemTheme : theme;\n\n const isDark = resolvedTheme === 'dark';\n\n // Determine if modal should appear above or below\n const modalHeight = 280; // approximate modal height\n const spaceBelow = anchorRect\n ? window.innerHeight - anchorRect.bottom\n : window.innerHeight / 2;\n const showAbove = spaceBelow < modalHeight + 20;\n\n // Focus trap\n useEffect(() => {\n const modal = modalRef.current;\n if (!modal) return;\n\n // Focus first element\n firstFocusableRef.current?.focus();\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose();\n return;\n }\n\n if (e.key === 'Tab') {\n const focusableElements = modal.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n );\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault();\n lastElement?.focus();\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault();\n firstElement?.focus();\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [onClose]);\n\n const defaultPrompt = mode === 'vote'\n ? 'What do you think?'\n : 'What do you think of this feature?';\n\n // Responsive sizing - on mobile, use fixed positioning centered on screen\n const modalPadding = isMobile ? 20 : 16;\n\n const modalStyles: React.CSSProperties = isMobile\n ? {\n // Mobile: fixed position, centered on screen\n position: 'fixed',\n left: '50%',\n top: '50%',\n width: 'calc(100vw - 32px)',\n maxWidth: 320,\n padding: modalPadding,\n borderRadius: 12,\n backgroundColor: isDark ? '#1f2937' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.2)',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n zIndex: 9999,\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: isVisible ? 1 : 0,\n transform: isVisible\n ? 'translate(-50%, -50%) scale(1)'\n : 'translate(-50%, -50%) scale(0.95)',\n ...customStyles?.modal,\n }\n : {\n // Desktop: absolute position relative to button\n position: 'absolute',\n left: '50%',\n width: 320,\n padding: modalPadding,\n borderRadius: 8,\n backgroundColor: isDark ? '#1f2937' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15)',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n zIndex: 9999,\n ...(showAbove\n ? { bottom: '100%', marginBottom: 8 }\n : { top: '100%', marginTop: 8 }),\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: isVisible ? 1 : 0,\n transform: isVisible\n ? 'translateX(-50%) scale(1) translateY(0)'\n : `translateX(-50%) scale(0.95) translateY(${showAbove ? '10px' : '-10px'})`,\n ...customStyles?.modal,\n };\n\n return (\n <div\n ref={modalRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"gotcha-modal-title\"\n style={modalStyles}\n className={cn('gotcha-modal', isDark && 'gotcha-modal--dark')}\n >\n {/* Close button - larger on touch devices */}\n <button\n ref={firstFocusableRef}\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close feedback form\"\n style={{\n position: 'absolute',\n top: isMobile ? 12 : 8,\n right: isMobile ? 12 : 8,\n width: isMobile ? 36 : 24,\n height: isMobile ? 36 : 24,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: isDark ? '#9ca3af' : '#6b7280',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 4,\n }}\n >\n <svg width={isMobile ? 18 : 14} height={isMobile ? 18 : 14} viewBox=\"0 0 14 14\" fill=\"none\">\n <path\n d=\"M1 1L13 13M1 13L13 1\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n\n {/* Title */}\n <h2\n id=\"gotcha-modal-title\"\n style={{\n margin: '0 0 12px 0',\n fontSize: isMobile ? 16 : 14,\n fontWeight: 500,\n paddingRight: isMobile ? 40 : 24,\n }}\n >\n {promptText || defaultPrompt}\n </h2>\n\n {/* Success state */}\n {isSubmitted && (\n <div\n style={{\n textAlign: 'center',\n padding: '20px 0',\n color: isDark ? '#10b981' : '#059669',\n }}\n >\n <svg\n width=\"32\"\n height=\"32\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n style={{ margin: '0 auto 8px' }}\n >\n <path\n d=\"M20 6L9 17L4 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n <p style={{ margin: 0, fontSize: 14 }}>{thankYouMessage}</p>\n </div>\n )}\n\n {/* Error state */}\n {error && !isSubmitted && (\n <div\n style={{\n padding: 8,\n marginBottom: 12,\n borderRadius: 4,\n backgroundColor: isDark ? '#7f1d1d' : '#fef2f2',\n color: isDark ? '#fecaca' : '#dc2626',\n fontSize: 13,\n }}\n >\n {error}\n </div>\n )}\n\n {/* Form content based on mode */}\n {!isSubmitted && (\n <>\n {mode === 'feedback' && (\n <FeedbackMode\n theme={resolvedTheme}\n placeholder={placeholder}\n submitText={submitText}\n isLoading={isLoading}\n onSubmit={onSubmit}\n customStyles={customStyles}\n />\n )}\n {mode === 'vote' && (\n <VoteMode\n theme={resolvedTheme}\n isLoading={isLoading}\n onSubmit={onSubmit}\n />\n )}\n </>\n )}\n\n {/* Screen reader announcement */}\n <div aria-live=\"polite\" className=\"sr-only\" style={{ position: 'absolute', left: -9999 }}>\n {isSubmitted && 'Thank you! Your feedback has been submitted.'}\n {error && `Error: ${error}`}\n </div>\n </div>\n );\n}\n","import React, { useState, useCallback, useEffect, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n ResponseMode,\n GotchaUser,\n Position,\n Size,\n Theme,\n TouchBehavior,\n GotchaStyles,\n GotchaResponse,\n GotchaError,\n} from '../types';\nimport { DEFAULTS } from '../constants';\nimport { useGotchaContext } from './GotchaProvider';\nimport { useSubmit } from '../hooks/useSubmit';\nimport { GotchaButton } from './GotchaButton';\nimport { GotchaModal } from './GotchaModal';\n\nexport interface GotchaProps {\n /** Unique identifier for this element */\n elementId: string;\n\n // User data\n /** User metadata for segmentation */\n user?: GotchaUser;\n\n // Behavior\n /** Feedback mode */\n mode?: ResponseMode;\n /** Required if mode is 'ab' */\n experimentId?: string;\n /** Current A/B variant shown to user */\n variant?: string;\n\n // Poll mode specific (Phase 2)\n /** Required if mode is 'poll' (2-6 options) */\n options?: string[];\n /** Allow selecting multiple options */\n allowMultiple?: boolean;\n /** Show results after voting */\n showResults?: boolean;\n\n // Appearance\n /** Button position relative to parent */\n position?: Position;\n /** Button size */\n size?: Size;\n /** Color theme */\n theme?: Theme;\n /** Custom style overrides */\n customStyles?: GotchaStyles;\n /** Control visibility programmatically */\n visible?: boolean;\n /** Only show when parent is hovered (default: true) */\n showOnHover?: boolean;\n /** Mobile behavior (default: 'always-visible') */\n touchBehavior?: TouchBehavior;\n\n // Content\n /** Custom prompt text */\n promptText?: string;\n /** Input placeholder text */\n placeholder?: string;\n /** Submit button text */\n submitText?: string;\n /** Post-submission message */\n thankYouMessage?: string;\n\n // Callbacks\n /** Called after successful submission */\n onSubmit?: (response: GotchaResponse) => void;\n /** Called when modal opens */\n onOpen?: () => void;\n /** Called when modal closes */\n onClose?: () => void;\n /** Called on error */\n onError?: (error: GotchaError) => void;\n}\n\nexport function Gotcha({\n elementId,\n user,\n mode = 'feedback',\n experimentId,\n variant,\n options,\n allowMultiple = false,\n showResults = true,\n position = DEFAULTS.POSITION,\n size = DEFAULTS.SIZE,\n theme = DEFAULTS.THEME,\n customStyles,\n visible = true,\n showOnHover = DEFAULTS.SHOW_ON_HOVER,\n touchBehavior = DEFAULTS.TOUCH_BEHAVIOR,\n promptText,\n placeholder,\n submitText = DEFAULTS.SUBMIT_TEXT,\n thankYouMessage = DEFAULTS.THANK_YOU_MESSAGE,\n onSubmit,\n onOpen,\n onClose,\n onError,\n}: GotchaProps) {\n const { disabled, activeModalId, openModal, closeModal } = useGotchaContext();\n const [isSubmitted, setIsSubmitted] = useState(false);\n const [isParentHovered, setIsParentHovered] = useState(false);\n const [anchorRect, setAnchorRect] = useState<DOMRect | null>(null);\n const [isMobile, setIsMobile] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Detect mobile for portal rendering (SSR-safe)\n useEffect(() => {\n setIsMobile(window.innerWidth < 640);\n }, []);\n\n // This instance's modal is open if activeModalId matches our elementId\n const isOpen = activeModalId === elementId;\n\n // Attach hover listeners to the parent element (not the button container)\n useEffect(() => {\n if (!showOnHover) return;\n\n const container = containerRef.current;\n if (!container) return;\n\n // Find the parent element with position: relative\n const parent = container.parentElement;\n if (!parent) return;\n\n const handleMouseEnter = () => setIsParentHovered(true);\n const handleMouseLeave = () => setIsParentHovered(false);\n\n parent.addEventListener('mouseenter', handleMouseEnter);\n parent.addEventListener('mouseleave', handleMouseLeave);\n\n return () => {\n parent.removeEventListener('mouseenter', handleMouseEnter);\n parent.removeEventListener('mouseleave', handleMouseLeave);\n };\n }, [showOnHover]);\n\n const { submit, isLoading, error } = useSubmit({\n elementId,\n mode,\n experimentId,\n variant,\n pollOptions: options,\n user,\n onSuccess: (response) => {\n setIsSubmitted(true);\n onSubmit?.(response);\n // Auto-close after 2.5 seconds\n setTimeout(() => {\n closeModal();\n setIsSubmitted(false);\n }, 2500);\n },\n onError: (err) => {\n onError?.(err as unknown as GotchaError);\n },\n });\n\n const handleOpen = useCallback(() => {\n if (containerRef.current) {\n setAnchorRect(containerRef.current.getBoundingClientRect());\n }\n openModal(elementId);\n onOpen?.();\n }, [elementId, openModal, onOpen]);\n\n const handleClose = useCallback(() => {\n closeModal();\n setIsSubmitted(false);\n onClose?.();\n }, [closeModal, onClose]);\n\n const handleSubmit = useCallback(\n (data: { content?: string; rating?: number; vote?: 'up' | 'down' }) => {\n submit(data);\n },\n [submit]\n );\n\n // Don't render if disabled or not visible\n if (disabled || !visible) return null;\n\n // Position styles\n const positionStyles: Record<Position, React.CSSProperties> = {\n 'top-right': { position: 'absolute', top: 0, right: 0, transform: 'translate(50%, -50%)' },\n 'top-left': { position: 'absolute', top: 0, left: 0, transform: 'translate(-50%, -50%)' },\n 'bottom-right': { position: 'absolute', bottom: 0, right: 0, transform: 'translate(50%, 50%)' },\n 'bottom-left': { position: 'absolute', bottom: 0, left: 0, transform: 'translate(-50%, 50%)' },\n 'inline': { position: 'relative', display: 'inline-flex' },\n };\n\n return (\n <div\n ref={containerRef}\n style={{\n ...positionStyles[position],\n zIndex: isOpen ? 10000 : 'auto',\n }}\n className=\"gotcha-container\"\n data-gotcha-element={elementId}\n >\n <GotchaButton\n size={size}\n theme={theme}\n customStyles={customStyles}\n showOnHover={showOnHover}\n touchBehavior={touchBehavior}\n onClick={handleOpen}\n isOpen={isOpen}\n isParentHovered={isParentHovered}\n />\n\n {isOpen && !isMobile && (\n <GotchaModal\n mode={mode}\n theme={theme}\n customStyles={customStyles}\n promptText={promptText}\n placeholder={placeholder}\n submitText={submitText}\n thankYouMessage={thankYouMessage}\n isLoading={isLoading}\n isSubmitted={isSubmitted}\n error={error}\n onSubmit={handleSubmit}\n onClose={handleClose}\n anchorRect={anchorRect || undefined}\n />\n )}\n\n {/* On mobile, render modal via portal to escape parent transform */}\n {isOpen && isMobile && createPortal(\n <div\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 99999,\n backgroundColor: 'rgba(0, 0, 0, 0.3)',\n }}\n onClick={handleClose}\n aria-hidden=\"true\"\n >\n <div onClick={(e) => e.stopPropagation()}>\n <GotchaModal\n mode={mode}\n theme={theme}\n customStyles={customStyles}\n promptText={promptText}\n placeholder={placeholder}\n submitText={submitText}\n thankYouMessage={thankYouMessage}\n isLoading={isLoading}\n isSubmitted={isSubmitted}\n error={error}\n onSubmit={handleSubmit}\n onClose={handleClose}\n anchorRect={anchorRect || undefined}\n />\n </div>\n </div>,\n document.body\n )}\n </div>\n );\n}\n","import { useGotchaContext } from '../components/GotchaProvider';\n\n/**\n * Hook to access Gotcha context\n * Must be used within a GotchaProvider\n */\nexport function useGotcha() {\n const { client, disabled, defaultUser, debug } = useGotchaContext();\n\n return {\n /** The API client for manual submissions */\n client,\n /** Whether Gotcha is globally disabled */\n disabled,\n /** Default user metadata */\n defaultUser,\n /** Whether debug mode is enabled */\n debug,\n /** Submit feedback programmatically */\n submitFeedback: client.submitResponse.bind(client),\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/utils/anonymous.ts","../src/api/client.ts","../src/components/GotchaProvider.tsx","../src/hooks/useSubmit.ts","../src/utils/cn.ts","../src/utils/device.ts","../src/components/GotchaButton.tsx","../src/components/Spinner.tsx","../src/components/modes/FeedbackMode.tsx","../src/components/modes/VoteMode.tsx","../src/components/GotchaModal.tsx","../src/components/Gotcha.tsx","../src/hooks/useGotcha.ts"],"names":["API_BASE_URL","STORAGE_KEYS","DEFAULTS","RETRY_CONFIG","getAnonymousId","stored","id","DEFAULT_RETRY_CONFIG","fetchWithRetry","url","options","config","debug","lastError","attempt","response","error","delay","resolve","createApiClient","apiKey","baseUrl","headers","request","method","endpoint","body","idempotencyKey","data","payload","user","fullPayload","elementId","userId","GotchaContext","createContext","GotchaProvider","children","disabled","defaultUser","activeModalId","setActiveModalId","useState","client","useMemo","openModal","useCallback","closeModal","value","jsx","useGotchaContext","context","useContext","useSubmit","isLoading","setIsLoading","isCheckingExisting","setIsCheckingExisting","setError","existingResponse","setExistingResponse","useEffect","cancelled","existing","err","errorMessage","cn","classes","isTouchDevice","getResponsiveSize","size","isTouch","sizes","GotchaButton","theme","customStyles","showOnHover","touchBehavior","onClick","isOpen","isParentHovered","setIsTouch","tapRevealed","setTapRevealed","systemTheme","setSystemTheme","darkQuery","handler","e","shouldShow","handleClick","buttonSize","resolvedTheme","baseStyles","GotchaIcon","Spinner","color","jsxs","FeedbackMode","placeholder","submitText","onSubmit","initialValues","isEditing","content","setContent","rating","setRating","isDark","handleSubmit","inputStyles","buttonStyles","StarRating","onChange","hovered","setHovered","starSize","buttonPadding","star","isFilled","VoteMode","initialVote","activeVote","setActiveVote","previousVote","setPreviousVote","handleVote","vote","getButtonStyles","voteType","isSelected","iconSize","Fragment","ThumbsUpIcon","ThumbsDownIcon","GotchaModal","mode","promptText","thankYouMessage","isSubmitted","onClose","anchorRect","modalRef","useRef","firstFocusableRef","isVisible","setIsVisible","isMobile","setIsMobile","timer","showAbove","modal","handleKeyDown","focusableElements","firstElement","lastElement","defaultPrompt","modalPadding","modalStyles","Gotcha","experimentId","variant","allowMultiple","showResults","position","visible","onOpen","onError","setIsSubmitted","setIsParentHovered","setAnchorRect","containerRef","container","parent","handleMouseEnter","handleMouseLeave","submit","handleOpen","handleClose","createPortal","useGotcha"],"mappings":"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","file":"index.mjs","sourcesContent":["// API URLs\nexport const API_BASE_URL = 'https://api.gotcha.cx/v1';\nexport const API_STAGING_URL = 'https://api.staging.gotcha.cx/v1';\n\n// Error codes\nexport const ERROR_CODES = {\n INVALID_API_KEY: 'INVALID_API_KEY',\n ORIGIN_NOT_ALLOWED: 'ORIGIN_NOT_ALLOWED',\n RATE_LIMITED: 'RATE_LIMITED',\n QUOTA_EXCEEDED: 'QUOTA_EXCEEDED',\n INVALID_REQUEST: 'INVALID_REQUEST',\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n} as const;\n\n// Local storage keys\nexport const STORAGE_KEYS = {\n ANONYMOUS_ID: 'gotcha_anonymous_id',\n OFFLINE_QUEUE: 'gotcha_offline_queue',\n} as const;\n\n// Default values\nexport const DEFAULTS = {\n POSITION: 'top-right' as const,\n SIZE: 'md' as const,\n THEME: 'light' as const,\n SHOW_ON_HOVER: true,\n TOUCH_BEHAVIOR: 'always-visible' as const,\n SUBMIT_TEXT: 'Submit',\n THANK_YOU_MESSAGE: 'Thanks for your feedback!',\n} as const;\n\n// Size mappings (desktop/mobile in pixels)\nexport const SIZE_MAP = {\n sm: { desktop: 24, mobile: 44 },\n md: { desktop: 32, mobile: 44 },\n lg: { desktop: 40, mobile: 48 },\n} as const;\n\n// Retry config\nexport const RETRY_CONFIG = {\n MAX_RETRIES: 2,\n BASE_DELAY_MS: 500,\n MAX_DELAY_MS: 5000,\n} as const;\n","import { STORAGE_KEYS } from '../constants';\n\n/**\n * Get or create an anonymous user ID\n * Stored in localStorage for consistency across sessions\n */\nexport function getAnonymousId(): string {\n if (typeof window === 'undefined') {\n // SSR fallback - generate but don't persist\n return `anon_${crypto.randomUUID()}`;\n }\n\n const stored = localStorage.getItem(STORAGE_KEYS.ANONYMOUS_ID);\n if (stored) return stored;\n\n const id = `anon_${crypto.randomUUID()}`;\n localStorage.setItem(STORAGE_KEYS.ANONYMOUS_ID, id);\n return id;\n}\n\n/**\n * Clear the anonymous ID (useful for testing)\n */\nexport function clearAnonymousId(): void {\n if (typeof window !== 'undefined') {\n localStorage.removeItem(STORAGE_KEYS.ANONYMOUS_ID);\n }\n}\n","import { API_BASE_URL, RETRY_CONFIG } from '../constants';\nimport { SubmitResponsePayload, GotchaResponse, GotchaError, ExistingResponse, VoteType } from '../types';\nimport { getAnonymousId } from '../utils/anonymous';\n\ninterface ApiClientConfig {\n apiKey: string;\n baseUrl?: string;\n debug?: boolean;\n}\n\ninterface RetryConfig {\n maxRetries: number;\n baseDelayMs: number;\n maxDelayMs: number;\n}\n\nconst DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxRetries: RETRY_CONFIG.MAX_RETRIES,\n baseDelayMs: RETRY_CONFIG.BASE_DELAY_MS,\n maxDelayMs: RETRY_CONFIG.MAX_DELAY_MS,\n};\n\n/**\n * Fetch with automatic retry and exponential backoff\n */\nasync function fetchWithRetry(\n url: string,\n options: RequestInit,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n debug: boolean = false\n): Promise<Response> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= config.maxRetries; attempt++) {\n try {\n if (debug && attempt > 0) {\n console.log(`[Gotcha] Retry attempt ${attempt}/${config.maxRetries}`);\n }\n\n const response = await fetch(url, options);\n\n // Don't retry client errors (4xx) except 429 (rate limit)\n if (response.status >= 400 && response.status < 500 && response.status !== 429) {\n return response;\n }\n\n // Success - return immediately\n if (response.ok) {\n return response;\n }\n\n lastError = new Error(`HTTP ${response.status}`);\n } catch (error) {\n // Network error - retry\n lastError = error as Error;\n if (debug) {\n console.log(`[Gotcha] Network error: ${lastError.message}`);\n }\n }\n\n // Don't delay after last attempt\n if (attempt < config.maxRetries) {\n const delay = Math.min(\n config.baseDelayMs * Math.pow(2, attempt),\n config.maxDelayMs\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n}\n\nexport function createApiClient(config: ApiClientConfig) {\n const { apiKey, baseUrl = API_BASE_URL, debug = false } = config;\n\n const headers = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n };\n\n async function request<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<T> {\n const url = `${baseUrl}${endpoint}`;\n const idempotencyKey = crypto.randomUUID();\n\n if (debug) {\n console.log(`[Gotcha] ${method} ${endpoint}`, body);\n }\n\n const response = await fetchWithRetry(\n url,\n {\n method,\n headers: {\n ...headers,\n 'Idempotency-Key': idempotencyKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n },\n DEFAULT_RETRY_CONFIG,\n debug\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = data.error as GotchaError;\n if (debug) {\n console.error(`[Gotcha] Error: ${error.code} - ${error.message}`);\n }\n throw error;\n }\n\n if (debug) {\n console.log(`[Gotcha] Response:`, data);\n }\n\n return data as T;\n }\n\n return {\n /**\n * Submit a response (feedback, vote, etc.)\n */\n async submitResponse(\n payload: Omit<SubmitResponsePayload, 'context'>\n ): Promise<GotchaResponse> {\n // Ensure user has an ID (anonymous if not provided)\n const user = payload.user || {};\n if (!user.id) {\n user.id = getAnonymousId();\n }\n\n const fullPayload: SubmitResponsePayload = {\n ...payload,\n user,\n context: {\n url: typeof window !== 'undefined' ? window.location.href : undefined,\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n },\n };\n\n return request<GotchaResponse>('POST', '/responses', fullPayload);\n },\n\n /**\n * Check if a user has an existing response for an element\n */\n async checkExistingResponse(\n elementId: string,\n userId: string\n ): Promise<ExistingResponse | null> {\n const url = `${baseUrl}/responses/check?elementId=${encodeURIComponent(elementId)}&userId=${encodeURIComponent(userId)}`;\n\n if (debug) {\n console.log(`[Gotcha] GET /responses/check`);\n }\n\n const response = await fetchWithRetry(\n url,\n {\n method: 'GET',\n headers,\n },\n DEFAULT_RETRY_CONFIG,\n debug\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = data.error as GotchaError;\n if (debug) {\n console.error(`[Gotcha] Error: ${error.code} - ${error.message}`);\n }\n throw error;\n }\n\n if (data.exists) {\n if (debug) {\n console.log(`[Gotcha] Found existing response:`, data.response);\n }\n return data.response as ExistingResponse;\n }\n\n return null;\n },\n\n /**\n * Update an existing response\n */\n async updateResponse(\n id: string,\n payload: {\n content?: string;\n title?: string;\n rating?: number;\n vote?: VoteType;\n pollSelected?: string[];\n },\n userId?: string\n ): Promise<GotchaResponse> {\n const url = `${baseUrl}/responses/${id}${userId ? `?userId=${encodeURIComponent(userId)}` : ''}`;\n\n if (debug) {\n console.log(`[Gotcha] PATCH /responses/${id}`, payload);\n }\n\n const response = await fetchWithRetry(\n url,\n {\n method: 'PATCH',\n headers,\n body: JSON.stringify(payload),\n },\n DEFAULT_RETRY_CONFIG,\n debug\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n const error = data.error as GotchaError;\n if (debug) {\n console.error(`[Gotcha] Error: ${error.code} - ${error.message}`);\n }\n throw error;\n }\n\n if (debug) {\n console.log(`[Gotcha] Response updated:`, data);\n }\n\n return data as GotchaResponse;\n },\n\n /**\n * Get the base URL (for debugging)\n */\n getBaseUrl(): string {\n return baseUrl;\n },\n };\n}\n\nexport type ApiClient = ReturnType<typeof createApiClient>;\n","import React, { createContext, useContext, useMemo, useState, useCallback } from 'react';\nimport { createApiClient, ApiClient } from '../api/client';\nimport { GotchaUser } from '../types';\n\nexport interface GotchaProviderProps {\n /** Your Gotcha API key */\n apiKey: string;\n /** React children */\n children: React.ReactNode;\n /** Override the API base URL (for testing/staging) */\n baseUrl?: string;\n /** Enable debug logging */\n debug?: boolean;\n /** Disable all Gotcha buttons globally */\n disabled?: boolean;\n /** Default user metadata applied to all submissions */\n defaultUser?: GotchaUser;\n}\n\nexport interface GotchaContextValue {\n client: ApiClient;\n disabled: boolean;\n defaultUser: GotchaUser;\n debug: boolean;\n // Modal management - only one open at a time\n activeModalId: string | null;\n openModal: (elementId: string) => void;\n closeModal: () => void;\n}\n\nconst GotchaContext = createContext<GotchaContextValue | null>(null);\n\nexport function GotchaProvider({\n apiKey,\n children,\n baseUrl,\n debug = false,\n disabled = false,\n defaultUser = {},\n}: GotchaProviderProps) {\n const [activeModalId, setActiveModalId] = useState<string | null>(null);\n\n const client = useMemo(\n () => createApiClient({ apiKey, baseUrl, debug }),\n [apiKey, baseUrl, debug]\n );\n\n const openModal = useCallback((elementId: string) => {\n setActiveModalId(elementId);\n }, []);\n\n const closeModal = useCallback(() => {\n setActiveModalId(null);\n }, []);\n\n const value: GotchaContextValue = useMemo(\n () => ({\n client,\n disabled,\n defaultUser,\n debug,\n activeModalId,\n openModal,\n closeModal,\n }),\n [client, disabled, defaultUser, debug, activeModalId, openModal, closeModal]\n );\n\n return (\n <GotchaContext.Provider value={value}>{children}</GotchaContext.Provider>\n );\n}\n\nexport function useGotchaContext(): GotchaContextValue {\n const context = useContext(GotchaContext);\n if (!context) {\n throw new Error('useGotchaContext must be used within a GotchaProvider');\n }\n return context;\n}\n","import { useState, useCallback, useEffect } from 'react';\nimport { useGotchaContext } from '../components/GotchaProvider';\nimport { ResponseMode, GotchaUser, GotchaResponse, VoteType, ExistingResponse } from '../types';\n\ninterface UseSubmitOptions {\n elementId: string;\n mode: ResponseMode;\n experimentId?: string;\n variant?: string;\n pollOptions?: string[];\n user?: GotchaUser;\n onSuccess?: (response: GotchaResponse) => void;\n onError?: (error: Error) => void;\n}\n\ninterface SubmitData {\n content?: string;\n title?: string;\n rating?: number;\n vote?: VoteType;\n pollSelected?: string[];\n}\n\nexport function useSubmit(options: UseSubmitOptions) {\n const { client, defaultUser } = useGotchaContext();\n const [isLoading, setIsLoading] = useState(false);\n const [isCheckingExisting, setIsCheckingExisting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [existingResponse, setExistingResponse] = useState<ExistingResponse | null>(null);\n\n // Check for existing response when user ID is provided\n useEffect(() => {\n const userId = options.user?.id || defaultUser?.id;\n if (!userId) {\n setExistingResponse(null);\n return;\n }\n\n let cancelled = false;\n\n const checkExisting = async () => {\n setIsCheckingExisting(true);\n try {\n const existing = await client.checkExistingResponse(options.elementId, userId);\n if (!cancelled) {\n setExistingResponse(existing);\n }\n } catch {\n // Ignore errors - just means no existing response found\n if (!cancelled) {\n setExistingResponse(null);\n }\n } finally {\n if (!cancelled) {\n setIsCheckingExisting(false);\n }\n }\n };\n\n checkExisting();\n\n return () => {\n cancelled = true;\n };\n }, [client, options.elementId, options.user?.id, defaultUser?.id]);\n\n const submit = useCallback(\n async (data: SubmitData) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const userId = options.user?.id || defaultUser?.id;\n let response: GotchaResponse;\n\n // If we have an existing response and a user ID, update instead of create\n if (existingResponse && userId) {\n response = await client.updateResponse(\n existingResponse.id,\n {\n content: data.content,\n title: data.title,\n rating: data.rating,\n vote: data.vote,\n pollSelected: data.pollSelected,\n },\n userId\n );\n } else {\n response = await client.submitResponse({\n elementId: options.elementId,\n mode: options.mode,\n content: data.content,\n title: data.title,\n rating: data.rating,\n vote: data.vote,\n pollOptions: options.pollOptions,\n pollSelected: data.pollSelected,\n experimentId: options.experimentId,\n variant: options.variant,\n user: { ...defaultUser, ...options.user },\n });\n }\n\n options.onSuccess?.(response);\n return response;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Something went wrong';\n setError(errorMessage);\n options.onError?.(err instanceof Error ? err : new Error(errorMessage));\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [client, defaultUser, options, existingResponse]\n );\n\n return {\n submit,\n isLoading,\n isCheckingExisting,\n error,\n existingResponse,\n isEditing: !!existingResponse,\n clearError: () => setError(null),\n };\n}\n","/**\n * Simple class name utility\n * Combines class names, filtering out falsy values\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n","/**\n * Detect if the device supports touch\n */\nexport const isTouchDevice = (): boolean => {\n if (typeof window === 'undefined') return false;\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0;\n};\n\n/**\n * Get the appropriate size based on device type\n */\nexport const getResponsiveSize = (\n size: 'sm' | 'md' | 'lg',\n isTouch: boolean\n): number => {\n const sizes = {\n sm: { desktop: 24, mobile: 32 },\n md: { desktop: 32, mobile: 36 },\n lg: { desktop: 40, mobile: 40 },\n };\n\n return isTouch ? sizes[size].mobile : sizes[size].desktop;\n};\n","import React, { useState, useEffect } from 'react';\nimport { Size, Theme, GotchaStyles } from '../types';\nimport { cn } from '../utils/cn';\nimport { isTouchDevice, getResponsiveSize } from '../utils/device';\n\nexport interface GotchaButtonProps {\n size: Size;\n theme: Theme;\n customStyles?: GotchaStyles;\n showOnHover: boolean;\n touchBehavior: 'always-visible' | 'tap-to-reveal';\n onClick: () => void;\n isOpen: boolean;\n isParentHovered?: boolean;\n}\n\nexport function GotchaButton({\n size,\n theme,\n customStyles,\n showOnHover,\n touchBehavior,\n onClick,\n isOpen,\n isParentHovered = false,\n}: GotchaButtonProps) {\n const [isTouch, setIsTouch] = useState(false);\n const [tapRevealed, setTapRevealed] = useState(false);\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n // Detect system theme preference\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemTheme(darkQuery.matches ? 'dark' : 'light');\n\n // Listen for theme changes\n const handler = (e: MediaQueryListEvent) => setSystemTheme(e.matches ? 'dark' : 'light');\n darkQuery.addEventListener('change', handler);\n return () => darkQuery.removeEventListener('change', handler);\n }, []);\n\n // Determine visibility\n const shouldShow = (() => {\n // Always show if modal is open\n if (isOpen) return true;\n\n // Desktop with showOnHover: only show when parent is hovered\n if (!isTouch && showOnHover) {\n return isParentHovered;\n }\n\n // Touch device with tap-to-reveal: show only after first tap\n if (isTouch && touchBehavior === 'tap-to-reveal') {\n return tapRevealed;\n }\n\n // All other cases: always visible\n return true;\n })();\n\n const handleClick = () => {\n // For tap-to-reveal on touch: first tap reveals, second tap opens\n if (isTouch && touchBehavior === 'tap-to-reveal' && !tapRevealed) {\n setTapRevealed(true);\n return;\n }\n onClick();\n };\n\n const buttonSize = getResponsiveSize(size, isTouch);\n\n // Determine theme colors\n const resolvedTheme = theme === 'auto' ? systemTheme : theme;\n\n const baseStyles: React.CSSProperties = {\n width: buttonSize,\n height: buttonSize,\n borderRadius: '50%',\n border: 'none',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: resolvedTheme === 'dark' ? '#374151' : '#c7d2dc',\n color: resolvedTheme === 'dark' ? '#e5e7eb' : '#4b5563',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)',\n // CSS transition for animations\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: shouldShow ? 1 : 0,\n transform: shouldShow ? 'scale(1)' : 'scale(0.6)',\n pointerEvents: shouldShow ? 'auto' : 'none',\n ...customStyles?.button,\n };\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n style={baseStyles}\n className={cn('gotcha-button', isOpen && 'gotcha-button--open')}\n aria-label=\"Give feedback on this feature\"\n aria-expanded={isOpen}\n aria-haspopup=\"dialog\"\n >\n <GotchaIcon size={buttonSize * 0.75} />\n </button>\n );\n}\n\nfunction GotchaIcon({ size }: { size: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <text\n x=\"50%\"\n y=\"50%\"\n dominantBaseline=\"central\"\n textAnchor=\"middle\"\n fontSize=\"16\"\n fontWeight=\"bold\"\n fill=\"currentColor\"\n fontFamily=\"system-ui, -apple-system, sans-serif\"\n >\n G\n </text>\n </svg>\n );\n}\n","import React from 'react';\n\ninterface SpinnerProps {\n size?: number;\n color?: string;\n}\n\nexport function Spinner({ size = 16, color = 'currentColor' }: SpinnerProps) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n style={{\n animation: 'gotcha-spin 1s linear infinite',\n }}\n >\n <style>\n {`\n @keyframes gotcha-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `}\n </style>\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke={color}\n strokeWidth=\"3\"\n strokeOpacity=\"0.25\"\n />\n <path\n d=\"M12 2a10 10 0 0 1 10 10\"\n stroke={color}\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n","import React, { useState, useEffect } from 'react';\nimport { GotchaStyles } from '../../types';\nimport { isTouchDevice } from '../../utils/device';\nimport { Spinner } from '../Spinner';\n\ninterface FeedbackModeProps {\n theme: 'light' | 'dark' | 'custom';\n placeholder?: string;\n submitText: string;\n isLoading: boolean;\n onSubmit: (data: { content?: string; rating?: number }) => void;\n customStyles?: GotchaStyles;\n initialValues?: {\n content?: string | null;\n rating?: number | null;\n };\n isEditing?: boolean;\n}\n\nexport function FeedbackMode({\n theme,\n placeholder,\n submitText,\n isLoading,\n onSubmit,\n customStyles,\n initialValues,\n isEditing = false,\n}: FeedbackModeProps) {\n const [content, setContent] = useState(initialValues?.content || '');\n const [rating, setRating] = useState<number | null>(initialValues?.rating ?? null);\n const [isTouch, setIsTouch] = useState(false);\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n }, []);\n\n // Update local state when initialValues change (e.g., after loading existing response)\n useEffect(() => {\n if (initialValues?.content !== undefined) {\n setContent(initialValues.content || '');\n }\n if (initialValues?.rating !== undefined) {\n setRating(initialValues.rating ?? null);\n }\n }, [initialValues?.content, initialValues?.rating]);\n\n const isDark = theme === 'dark';\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!content.trim() && rating === null) return;\n onSubmit({ content: content.trim() || undefined, rating: rating ?? undefined });\n };\n\n const inputStyles: React.CSSProperties = {\n width: '100%',\n padding: isTouch ? '12px 14px' : '10px 12px',\n border: `1px solid ${isDark ? '#374151' : '#d1d5db'}`,\n borderRadius: isTouch ? 8 : 6,\n backgroundColor: isDark ? '#374151' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n fontSize: isTouch ? 16 : 14, // 16px prevents iOS zoom on focus\n resize: 'vertical',\n minHeight: isTouch ? 100 : 80,\n fontFamily: 'inherit',\n ...customStyles?.input,\n };\n\n const buttonStyles: React.CSSProperties = {\n width: '100%',\n padding: isTouch ? '14px 16px' : '10px 16px',\n border: 'none',\n borderRadius: isTouch ? 8 : 6,\n backgroundColor: isLoading ? (isDark ? '#4b5563' : '#9ca3af') : '#6366f1',\n color: '#ffffff',\n fontSize: isTouch ? 16 : 14,\n fontWeight: 500,\n cursor: isLoading ? 'not-allowed' : 'pointer',\n transition: 'background-color 150ms ease',\n ...customStyles?.submitButton,\n };\n\n return (\n <form onSubmit={handleSubmit}>\n {/* Rating (optional) */}\n <div style={{ marginBottom: isTouch ? 16 : 12 }}>\n <StarRating value={rating} onChange={setRating} isDark={isDark} isTouch={isTouch} />\n </div>\n\n {/* Text input */}\n <textarea\n value={content}\n onChange={(e) => setContent(e.target.value)}\n placeholder={placeholder || 'Share your thoughts...'}\n style={inputStyles}\n disabled={isLoading}\n aria-label=\"Your feedback\"\n />\n\n {/* Submit button */}\n <button\n type=\"submit\"\n disabled={isLoading || (!content.trim() && rating === null)}\n style={{\n ...buttonStyles,\n marginTop: 12,\n opacity: (!content.trim() && rating === null) ? 0.5 : 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n }}\n >\n {isLoading && <Spinner size={isTouch ? 18 : 16} color=\"#ffffff\" />}\n {isLoading ? (isEditing ? 'Updating...' : 'Submitting...') : (isEditing ? 'Update' : submitText)}\n </button>\n </form>\n );\n}\n\ninterface StarRatingProps {\n value: number | null;\n onChange: (rating: number) => void;\n isDark: boolean;\n isTouch: boolean;\n}\n\nfunction StarRating({ value, onChange, isDark, isTouch }: StarRatingProps) {\n const [hovered, setHovered] = useState<number | null>(null);\n const starSize = isTouch ? 32 : 20;\n const buttonPadding = isTouch ? 6 : 2;\n\n return (\n <div\n style={{\n display: 'flex',\n gap: isTouch ? 8 : 4,\n }}\n role=\"group\"\n aria-label=\"Rating\"\n >\n {[1, 2, 3, 4, 5].map((star) => {\n const isFilled = (hovered ?? value ?? 0) >= star;\n return (\n <button\n key={star}\n type=\"button\"\n onClick={() => onChange(star)}\n onMouseEnter={() => setHovered(star)}\n onMouseLeave={() => setHovered(null)}\n aria-label={`Rate ${star} out of 5`}\n aria-pressed={value === star}\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: buttonPadding,\n color: isFilled ? '#f59e0b' : (isDark ? '#4b5563' : '#d1d5db'),\n transition: 'color 150ms ease',\n }}\n >\n <svg width={starSize} height={starSize} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\" />\n </svg>\n </button>\n );\n })}\n </div>\n );\n}\n","import React, { useEffect, useState } from 'react';\nimport { isTouchDevice } from '../../utils/device';\nimport { Spinner } from '../Spinner';\n\ninterface VoteModeProps {\n theme: 'light' | 'dark' | 'custom';\n isLoading: boolean;\n onSubmit: (data: { vote: 'up' | 'down' }) => void;\n initialVote?: 'up' | 'down' | null;\n isEditing?: boolean;\n}\n\nexport function VoteMode({ theme, isLoading, onSubmit, initialVote, isEditing = false }: VoteModeProps) {\n const [isTouch, setIsTouch] = useState(false);\n const [activeVote, setActiveVote] = useState<'up' | 'down' | null>(initialVote || null);\n const [previousVote, setPreviousVote] = useState<'up' | 'down' | null>(initialVote || null);\n\n useEffect(() => {\n setIsTouch(isTouchDevice());\n }, []);\n\n // Update when initialVote changes (e.g., after loading existing response)\n useEffect(() => {\n if (initialVote !== undefined) {\n setPreviousVote(initialVote);\n setActiveVote(initialVote);\n }\n }, [initialVote]);\n\n // Reset active vote when loading completes (but keep previous vote for edit mode)\n useEffect(() => {\n if (!isLoading && !isEditing) {\n setActiveVote(null);\n }\n }, [isLoading, isEditing]);\n\n const handleVote = (vote: 'up' | 'down') => {\n setActiveVote(vote);\n onSubmit({ vote });\n };\n\n const isDark = theme === 'dark';\n\n const getButtonStyles = (voteType: 'up' | 'down'): React.CSSProperties => {\n const isSelected = previousVote === voteType;\n return {\n flex: 1,\n padding: isTouch ? '16px 20px' : '12px 16px',\n border: isSelected\n ? `2px solid ${voteType === 'up' ? '#22c55e' : '#ef4444'}`\n : `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n borderRadius: isTouch ? 12 : 8,\n backgroundColor: isSelected\n ? (voteType === 'up' ? (isDark ? '#14532d' : '#dcfce7') : (isDark ? '#450a0a' : '#fee2e2'))\n : (isDark ? '#374151' : '#f9fafb'),\n color: isDark ? '#f9fafb' : '#111827',\n fontSize: isTouch ? 28 : 24,\n cursor: isLoading ? 'not-allowed' : 'pointer',\n transition: 'all 150ms ease',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: isTouch ? 10 : 8,\n };\n };\n\n const iconSize = isTouch ? 28 : 24;\n\n return (\n <div\n style={{\n display: 'flex',\n gap: isTouch ? 16 : 12,\n }}\n role=\"group\"\n aria-label=\"Vote\"\n >\n <button\n type=\"button\"\n onClick={() => handleVote('up')}\n disabled={isLoading}\n style={getButtonStyles('up')}\n aria-label=\"Vote up - I like this\"\n aria-pressed={previousVote === 'up'}\n >\n {isLoading && activeVote === 'up' ? (\n <>\n <Spinner size={iconSize} color={isDark ? '#f9fafb' : '#111827'} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>\n {isEditing ? 'Updating...' : 'Sending...'}\n </span>\n </>\n ) : (\n <>\n <ThumbsUpIcon size={iconSize} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>\n {previousVote === 'up' ? 'Liked' : 'Like'}\n </span>\n </>\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={() => handleVote('down')}\n disabled={isLoading}\n style={getButtonStyles('down')}\n aria-label=\"Vote down - I don't like this\"\n aria-pressed={previousVote === 'down'}\n >\n {isLoading && activeVote === 'down' ? (\n <>\n <Spinner size={iconSize} color={isDark ? '#f9fafb' : '#111827'} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>\n {isEditing ? 'Updating...' : 'Sending...'}\n </span>\n </>\n ) : (\n <>\n <ThumbsDownIcon size={iconSize} />\n <span style={{ fontSize: isTouch ? 16 : 14, fontWeight: 500 }}>\n {previousVote === 'down' ? 'Disliked' : 'Dislike'}\n </span>\n </>\n )}\n </button>\n </div>\n );\n}\n\nfunction ThumbsUpIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\" />\n </svg>\n );\n}\n\nfunction ThumbsDownIcon({ size = 24 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\" />\n </svg>\n );\n}\n","import React, { useRef, useEffect, useState } from 'react';\nimport { Theme, GotchaStyles, ResponseMode, ExistingResponse } from '../types';\nimport { cn } from '../utils/cn';\nimport { FeedbackMode } from './modes/FeedbackMode';\nimport { VoteMode } from './modes/VoteMode';\n\nexport interface GotchaModalProps {\n mode: ResponseMode;\n theme: Theme;\n customStyles?: GotchaStyles;\n promptText?: string;\n placeholder?: string;\n submitText: string;\n thankYouMessage: string;\n // State\n isLoading: boolean;\n isSubmitted: boolean;\n error: string | null;\n // Edit mode\n existingResponse?: ExistingResponse | null;\n isEditing?: boolean;\n // Handlers\n onSubmit: (data: { content?: string; rating?: number; vote?: 'up' | 'down' }) => void;\n onClose: () => void;\n // Position info from parent\n anchorRect?: DOMRect;\n}\n\nexport function GotchaModal({\n mode,\n theme,\n customStyles,\n promptText,\n placeholder,\n submitText,\n thankYouMessage,\n isLoading,\n isSubmitted,\n error,\n existingResponse,\n isEditing = false,\n onSubmit,\n onClose,\n anchorRect,\n}: GotchaModalProps) {\n const modalRef = useRef<HTMLDivElement>(null);\n const firstFocusableRef = useRef<HTMLButtonElement>(null);\n const [isVisible, setIsVisible] = useState(false);\n const [isMobile, setIsMobile] = useState(false);\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');\n\n // Detect mobile and system theme after mount (SSR-safe)\n useEffect(() => {\n setIsMobile(window.innerWidth < 640);\n\n // Detect system theme preference\n const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemTheme(darkQuery.matches ? 'dark' : 'light');\n\n // Listen for theme changes\n const handler = (e: MediaQueryListEvent) => setSystemTheme(e.matches ? 'dark' : 'light');\n darkQuery.addEventListener('change', handler);\n return () => darkQuery.removeEventListener('change', handler);\n }, []);\n\n // Trigger animation after mount\n useEffect(() => {\n const timer = requestAnimationFrame(() => setIsVisible(true));\n return () => cancelAnimationFrame(timer);\n }, []);\n\n // Resolve theme\n const resolvedTheme = theme === 'auto' ? systemTheme : theme;\n\n const isDark = resolvedTheme === 'dark';\n\n // Determine if modal should appear above or below\n const modalHeight = 280; // approximate modal height\n const spaceBelow = anchorRect\n ? window.innerHeight - anchorRect.bottom\n : window.innerHeight / 2;\n const showAbove = spaceBelow < modalHeight + 20;\n\n // Focus trap\n useEffect(() => {\n const modal = modalRef.current;\n if (!modal) return;\n\n // Focus first element\n firstFocusableRef.current?.focus();\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose();\n return;\n }\n\n if (e.key === 'Tab') {\n const focusableElements = modal.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n );\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault();\n lastElement?.focus();\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault();\n firstElement?.focus();\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [onClose]);\n\n const defaultPrompt = mode === 'vote'\n ? 'What do you think?'\n : 'What do you think of this feature?';\n\n // Responsive sizing - on mobile, use fixed positioning centered on screen\n const modalPadding = isMobile ? 20 : 16;\n\n const modalStyles: React.CSSProperties = isMobile\n ? {\n // Mobile: fixed position, centered on screen\n position: 'fixed',\n left: '50%',\n top: '50%',\n width: 'calc(100vw - 32px)',\n maxWidth: 320,\n padding: modalPadding,\n borderRadius: 12,\n backgroundColor: isDark ? '#1f2937' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.2)',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n zIndex: 9999,\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: isVisible ? 1 : 0,\n transform: isVisible\n ? 'translate(-50%, -50%) scale(1)'\n : 'translate(-50%, -50%) scale(0.95)',\n ...customStyles?.modal,\n }\n : {\n // Desktop: absolute position relative to button\n position: 'absolute',\n left: '50%',\n width: 320,\n padding: modalPadding,\n borderRadius: 8,\n backgroundColor: isDark ? '#1f2937' : '#ffffff',\n color: isDark ? '#f9fafb' : '#111827',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15)',\n border: `1px solid ${isDark ? '#374151' : '#e5e7eb'}`,\n zIndex: 9999,\n ...(showAbove\n ? { bottom: '100%', marginBottom: 8 }\n : { top: '100%', marginTop: 8 }),\n transition: 'opacity 0.2s ease-out, transform 0.2s ease-out',\n opacity: isVisible ? 1 : 0,\n transform: isVisible\n ? 'translateX(-50%) scale(1) translateY(0)'\n : `translateX(-50%) scale(0.95) translateY(${showAbove ? '10px' : '-10px'})`,\n ...customStyles?.modal,\n };\n\n return (\n <div\n ref={modalRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"gotcha-modal-title\"\n style={modalStyles}\n className={cn('gotcha-modal', isDark && 'gotcha-modal--dark')}\n >\n {/* Close button - larger on touch devices */}\n <button\n ref={firstFocusableRef}\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close feedback form\"\n style={{\n position: 'absolute',\n top: isMobile ? 12 : 8,\n right: isMobile ? 12 : 8,\n width: isMobile ? 36 : 24,\n height: isMobile ? 36 : 24,\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n color: isDark ? '#9ca3af' : '#6b7280',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 4,\n }}\n >\n <svg width={isMobile ? 18 : 14} height={isMobile ? 18 : 14} viewBox=\"0 0 14 14\" fill=\"none\">\n <path\n d=\"M1 1L13 13M1 13L13 1\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n\n {/* Title */}\n <h2\n id=\"gotcha-modal-title\"\n style={{\n margin: '0 0 12px 0',\n fontSize: isMobile ? 16 : 14,\n fontWeight: 500,\n paddingRight: isMobile ? 40 : 24,\n }}\n >\n {promptText || defaultPrompt}\n </h2>\n\n {/* Success state */}\n {isSubmitted && (\n <div\n style={{\n textAlign: 'center',\n padding: '20px 0',\n color: isDark ? '#10b981' : '#059669',\n }}\n >\n <svg\n width=\"32\"\n height=\"32\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n style={{ margin: '0 auto 8px' }}\n >\n <path\n d=\"M20 6L9 17L4 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n <p style={{ margin: 0, fontSize: 14 }}>{thankYouMessage}</p>\n </div>\n )}\n\n {/* Error state */}\n {error && !isSubmitted && (\n <div\n style={{\n padding: 8,\n marginBottom: 12,\n borderRadius: 4,\n backgroundColor: isDark ? '#7f1d1d' : '#fef2f2',\n color: isDark ? '#fecaca' : '#dc2626',\n fontSize: 13,\n }}\n >\n {error}\n </div>\n )}\n\n {/* Form content based on mode */}\n {!isSubmitted && (\n <>\n {mode === 'feedback' && (\n <FeedbackMode\n theme={resolvedTheme}\n placeholder={placeholder}\n submitText={submitText}\n isLoading={isLoading}\n onSubmit={onSubmit}\n customStyles={customStyles}\n initialValues={existingResponse ? {\n content: existingResponse.content,\n rating: existingResponse.rating,\n } : undefined}\n isEditing={isEditing}\n />\n )}\n {mode === 'vote' && (\n <VoteMode\n theme={resolvedTheme}\n isLoading={isLoading}\n onSubmit={onSubmit}\n initialVote={existingResponse?.vote || undefined}\n isEditing={isEditing}\n />\n )}\n </>\n )}\n\n {/* Screen reader announcement */}\n <div aria-live=\"polite\" className=\"sr-only\" style={{ position: 'absolute', left: -9999 }}>\n {isSubmitted && 'Thank you! Your feedback has been submitted.'}\n {error && `Error: ${error}`}\n </div>\n </div>\n );\n}\n","import React, { useState, useCallback, useEffect, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n ResponseMode,\n GotchaUser,\n Position,\n Size,\n Theme,\n TouchBehavior,\n GotchaStyles,\n GotchaResponse,\n GotchaError,\n} from '../types';\nimport { DEFAULTS } from '../constants';\nimport { useGotchaContext } from './GotchaProvider';\nimport { useSubmit } from '../hooks/useSubmit';\nimport { GotchaButton } from './GotchaButton';\nimport { GotchaModal } from './GotchaModal';\n\nexport interface GotchaProps {\n /** Unique identifier for this element */\n elementId: string;\n\n // User data\n /** User metadata for segmentation */\n user?: GotchaUser;\n\n // Behavior\n /** Feedback mode */\n mode?: ResponseMode;\n /** Required if mode is 'ab' */\n experimentId?: string;\n /** Current A/B variant shown to user */\n variant?: string;\n\n // Poll mode specific (Phase 2)\n /** Required if mode is 'poll' (2-6 options) */\n options?: string[];\n /** Allow selecting multiple options */\n allowMultiple?: boolean;\n /** Show results after voting */\n showResults?: boolean;\n\n // Appearance\n /** Button position relative to parent */\n position?: Position;\n /** Button size */\n size?: Size;\n /** Color theme */\n theme?: Theme;\n /** Custom style overrides */\n customStyles?: GotchaStyles;\n /** Control visibility programmatically */\n visible?: boolean;\n /** Only show when parent is hovered (default: true) */\n showOnHover?: boolean;\n /** Mobile behavior (default: 'always-visible') */\n touchBehavior?: TouchBehavior;\n\n // Content\n /** Custom prompt text */\n promptText?: string;\n /** Input placeholder text */\n placeholder?: string;\n /** Submit button text */\n submitText?: string;\n /** Post-submission message */\n thankYouMessage?: string;\n\n // Callbacks\n /** Called after successful submission */\n onSubmit?: (response: GotchaResponse) => void;\n /** Called when modal opens */\n onOpen?: () => void;\n /** Called when modal closes */\n onClose?: () => void;\n /** Called on error */\n onError?: (error: GotchaError) => void;\n}\n\nexport function Gotcha({\n elementId,\n user,\n mode = 'feedback',\n experimentId,\n variant,\n options,\n allowMultiple = false,\n showResults = true,\n position = DEFAULTS.POSITION,\n size = DEFAULTS.SIZE,\n theme = DEFAULTS.THEME,\n customStyles,\n visible = true,\n showOnHover = DEFAULTS.SHOW_ON_HOVER,\n touchBehavior = DEFAULTS.TOUCH_BEHAVIOR,\n promptText,\n placeholder,\n submitText = DEFAULTS.SUBMIT_TEXT,\n thankYouMessage = DEFAULTS.THANK_YOU_MESSAGE,\n onSubmit,\n onOpen,\n onClose,\n onError,\n}: GotchaProps) {\n const { disabled, activeModalId, openModal, closeModal } = useGotchaContext();\n const [isSubmitted, setIsSubmitted] = useState(false);\n const [isParentHovered, setIsParentHovered] = useState(false);\n const [anchorRect, setAnchorRect] = useState<DOMRect | null>(null);\n const [isMobile, setIsMobile] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Detect mobile for portal rendering (SSR-safe)\n useEffect(() => {\n setIsMobile(window.innerWidth < 640);\n }, []);\n\n // This instance's modal is open if activeModalId matches our elementId\n const isOpen = activeModalId === elementId;\n\n // Attach hover listeners to the parent element (not the button container)\n useEffect(() => {\n if (!showOnHover) return;\n\n const container = containerRef.current;\n if (!container) return;\n\n // Find the parent element with position: relative\n const parent = container.parentElement;\n if (!parent) return;\n\n const handleMouseEnter = () => setIsParentHovered(true);\n const handleMouseLeave = () => setIsParentHovered(false);\n\n parent.addEventListener('mouseenter', handleMouseEnter);\n parent.addEventListener('mouseleave', handleMouseLeave);\n\n return () => {\n parent.removeEventListener('mouseenter', handleMouseEnter);\n parent.removeEventListener('mouseleave', handleMouseLeave);\n };\n }, [showOnHover]);\n\n const { submit, isLoading, error, existingResponse, isEditing } = useSubmit({\n elementId,\n mode,\n experimentId,\n variant,\n pollOptions: options,\n user,\n onSuccess: (response) => {\n setIsSubmitted(true);\n onSubmit?.(response);\n // Auto-close after 2.5 seconds\n setTimeout(() => {\n closeModal();\n setIsSubmitted(false);\n }, 2500);\n },\n onError: (err) => {\n onError?.(err as unknown as GotchaError);\n },\n });\n\n const handleOpen = useCallback(() => {\n if (containerRef.current) {\n setAnchorRect(containerRef.current.getBoundingClientRect());\n }\n openModal(elementId);\n onOpen?.();\n }, [elementId, openModal, onOpen]);\n\n const handleClose = useCallback(() => {\n closeModal();\n setIsSubmitted(false);\n onClose?.();\n }, [closeModal, onClose]);\n\n const handleSubmit = useCallback(\n (data: { content?: string; rating?: number; vote?: 'up' | 'down' }) => {\n submit(data);\n },\n [submit]\n );\n\n // Don't render if disabled or not visible\n if (disabled || !visible) return null;\n\n // Position styles\n const positionStyles: Record<Position, React.CSSProperties> = {\n 'top-right': { position: 'absolute', top: 0, right: 0, transform: 'translate(50%, -50%)' },\n 'top-left': { position: 'absolute', top: 0, left: 0, transform: 'translate(-50%, -50%)' },\n 'bottom-right': { position: 'absolute', bottom: 0, right: 0, transform: 'translate(50%, 50%)' },\n 'bottom-left': { position: 'absolute', bottom: 0, left: 0, transform: 'translate(-50%, 50%)' },\n 'inline': { position: 'relative', display: 'inline-flex' },\n };\n\n return (\n <div\n ref={containerRef}\n style={{\n ...positionStyles[position],\n zIndex: isOpen ? 10000 : 'auto',\n }}\n className=\"gotcha-container\"\n data-gotcha-element={elementId}\n >\n <GotchaButton\n size={size}\n theme={theme}\n customStyles={customStyles}\n showOnHover={showOnHover}\n touchBehavior={touchBehavior}\n onClick={handleOpen}\n isOpen={isOpen}\n isParentHovered={isParentHovered}\n />\n\n {isOpen && !isMobile && (\n <GotchaModal\n mode={mode}\n theme={theme}\n customStyles={customStyles}\n promptText={promptText}\n placeholder={placeholder}\n submitText={submitText}\n thankYouMessage={isEditing ? 'Your feedback has been updated!' : thankYouMessage}\n isLoading={isLoading}\n isSubmitted={isSubmitted}\n error={error}\n existingResponse={existingResponse}\n isEditing={isEditing}\n onSubmit={handleSubmit}\n onClose={handleClose}\n anchorRect={anchorRect || undefined}\n />\n )}\n\n {/* On mobile, render modal via portal to escape parent transform */}\n {isOpen && isMobile && createPortal(\n <div\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 99999,\n backgroundColor: 'rgba(0, 0, 0, 0.3)',\n }}\n onClick={handleClose}\n aria-hidden=\"true\"\n >\n <div onClick={(e) => e.stopPropagation()}>\n <GotchaModal\n mode={mode}\n theme={theme}\n customStyles={customStyles}\n promptText={promptText}\n placeholder={placeholder}\n submitText={submitText}\n thankYouMessage={isEditing ? 'Your feedback has been updated!' : thankYouMessage}\n isLoading={isLoading}\n isSubmitted={isSubmitted}\n error={error}\n existingResponse={existingResponse}\n isEditing={isEditing}\n onSubmit={handleSubmit}\n onClose={handleClose}\n anchorRect={anchorRect || undefined}\n />\n </div>\n </div>,\n document.body\n )}\n </div>\n );\n}\n","import { useGotchaContext } from '../components/GotchaProvider';\n\n/**\n * Hook to access Gotcha context\n * Must be used within a GotchaProvider\n */\nexport function useGotcha() {\n const { client, disabled, defaultUser, debug } = useGotchaContext();\n\n return {\n /** The API client for manual submissions */\n client,\n /** Whether Gotcha is globally disabled */\n disabled,\n /** Default user metadata */\n defaultUser,\n /** Whether debug mode is enabled */\n debug,\n /** Submit feedback programmatically */\n submitFeedback: client.submitResponse.bind(client),\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gotcha-feedback",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Developer-first contextual feedback SDK",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",