@tekibo/feedpulse-sdk 0.4.0

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 ADDED
@@ -0,0 +1,145 @@
1
+ # @tekibo/feedpulse-sdk
2
+
3
+ Developer-first embeddable behavior analytics and feedback SDK for Nuxt and React.
4
+
5
+ - Tracks user interactions from elements tagged with `data-fp-id`
6
+ - Sends event batches to your FeedPulse ingest endpoint
7
+ - Includes ready-to-use feedback widgets for Nuxt and React
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @tekibo/feedpulse-sdk
13
+ ```
14
+
15
+ ## What this SDK tracks
16
+
17
+ The SDK automatically tracks:
18
+
19
+ - `click`: when users click an element that has `data-fp-id`
20
+ - `hover`: when users hover an element for more than 500ms
21
+ - `scroll_into_view`: when tagged elements enter viewport
22
+ - `feedback`: explicit rating/message submissions from widget or manual API
23
+
24
+ ## Quick start (Nuxt)
25
+
26
+ Import from `@tekibo/feedpulse-sdk/nuxt` and wrap your app:
27
+
28
+ ```vue
29
+ <script setup lang="ts">
30
+ import { FeedPulseProvider, FeedPulseWidget } from "@tekibo/feedpulse-sdk/nuxt";
31
+ </script>
32
+
33
+ <template>
34
+ <FeedPulseProvider api-key="fp_live_your_project_key">
35
+ <NuxtPage />
36
+ <FeedPulseWidget id="global-feedback" position="bottom-right" />
37
+ </FeedPulseProvider>
38
+ </template>
39
+ ```
40
+
41
+ The SDK uses `https://feed-pulse.vercel.app/api/ingest` by default, so only `api-key` is required.
42
+ Tag any interactive element with `data-fp-id`:
43
+
44
+ ```vue
45
+ <template>
46
+ <UButton data-fp-id="hero-cta">
47
+ Start free trial
48
+ </UButton>
49
+ </template>
50
+ ```
51
+
52
+ Use the composable for manual feedback/event calls:
53
+
54
+ ```vue
55
+ <script setup lang="ts">
56
+ import { useFeedPulse } from "@tekibo/feedpulse-sdk/nuxt";
57
+
58
+ const tracker = useFeedPulse();
59
+
60
+ function sendNps() {
61
+ tracker?.trackFeedback("nps-card", 5, "Great onboarding flow");
62
+ }
63
+ </script>
64
+ ```
65
+
66
+ ## Quick start (React)
67
+
68
+ Import from `@tekibo/feedpulse-sdk/react`:
69
+
70
+ ```tsx
71
+ import { FeedPulseProvider, FeedPulseWidget, useFeedPulse } from "@tekibo/feedpulse-sdk/react";
72
+
73
+ function SaveButton() {
74
+ const { tracker } = useFeedPulse();
75
+
76
+ return (
77
+ <button
78
+ data-fp-id="settings-save"
79
+ onClick={() => tracker?.trackFeedback("settings-save", 5, "Saved successfully")}
80
+ >
81
+ Save
82
+ </button>
83
+ );
84
+ }
85
+
86
+ export default function App() {
87
+ return (
88
+ <FeedPulseProvider apiKey="fp_live_your_project_key">
89
+ <button data-fp-id="hero-cta">Get Started</button>
90
+ <SaveButton />
91
+ <FeedPulseWidget id="global-feedback" position="bottom-right" />
92
+ </FeedPulseProvider>
93
+ );
94
+ }
95
+ ```
96
+
97
+ ## Core API
98
+
99
+ Import from `@tekibo/feedpulse-sdk`:
100
+
101
+ ```ts
102
+ import { FeedPulseTracker } from "@tekibo/feedpulse-sdk";
103
+ ```
104
+
105
+ `FeedPulseTracker` configuration:
106
+
107
+ - `apiKey: string` required
108
+ - `endpoint?: string` optional, defaults to `https://feed-pulse.vercel.app/api/ingest`
109
+ - `batchInterval?: number` optional, defaults to `5000` ms
110
+
111
+ Key methods:
112
+
113
+ - `init()` start listeners and batching
114
+ - `track(eventType, elementId, metadata?)` enqueue an event
115
+ - `trackFeedback(elementId, rating, message)` send feedback immediately
116
+ - `destroy()` stop listeners and flush queue
117
+
118
+ ## Widget props
119
+
120
+ Nuxt and React widgets support:
121
+
122
+ - `id?: string` element id used in feedback event
123
+ - `placeholder?: string` prompt text
124
+ - `position?: "bottom-right" | "bottom-left" | "inline"` default is `"bottom-right"`
125
+
126
+ ## Optional endpoint override
127
+
128
+ You can override the default endpoint when needed:
129
+
130
+ - Nuxt: `<FeedPulseProvider api-key="..." endpoint="https://your-domain.com/api/ingest">`
131
+ - React: `<FeedPulseProvider apiKey="..." endpoint="https://your-domain.com/api/ingest">`
132
+
133
+ ## Integration checklist for your app
134
+
135
+ 1. Create a project in FeedPulse dashboard and copy the project API key
136
+ 2. Install package and add provider at root layout/app level
137
+ 3. Add `data-fp-id` to primary buttons, links, forms, and sections
138
+ 4. Optionally mount `FeedPulseWidget` for direct user feedback
139
+ 5. Verify data appears in FeedPulse dashboard analytics and feedback pages
140
+
141
+ ## Package exports
142
+
143
+ - `@tekibo/feedpulse-sdk` → core types + tracker
144
+ - `@tekibo/feedpulse-sdk/nuxt` → `FeedPulseProvider`, `FeedPulseWidget`, `useFeedPulse`
145
+ - `@tekibo/feedpulse-sdk/react` → `FeedPulseProvider`, `FeedPulseWidget`, `useFeedPulse`
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class d{constructor(e){this.queue=[],this.flushInterval=null,this.observer=null,this.intersectionObserver=null,this.scrollFallbackListener=null,this.resizeFallbackListener=null,this.seenInView=new Set;const i="https://feedpulse-workers.YOUR_SUBDOMAIN.workers.dev/ingest";this.config={apiKey:e.apiKey,endpoint:e.endpoint??i,batchInterval:e.batchInterval??5e3,debug:e.debug??!1},this.visitorId="",this.sessionId=""}init(){this.isBrowser()&&(this.visitorId=this.getOrCreateVisitorId(),this.sessionId=this.generateSessionId(),this.startFlushInterval(),this.attachGlobalListeners(),this.observeDOM())}attachGlobalListeners(){document.addEventListener("click",t=>{const s=t.target.closest("[data-fp-id]");s&&this.track("click",s.getAttribute("data-fp-id"),{x:t.clientX,y:t.clientY})},{passive:!0});let e=0,i=null;document.addEventListener("mouseover",t=>{const s=t.target.closest("[data-fp-id]");s&&(e=Date.now(),i=s.getAttribute("data-fp-id"))},{passive:!0}),document.addEventListener("mouseout",t=>{if(!t.target.closest("[data-fp-id]")||!i)return;const r=Date.now()-e;r>500&&this.track("hover",i,{hoverDuration:r}),i=null},{passive:!0})}observeDOM(){const e=s=>{this.seenInView.has(s)||(this.seenInView.add(s),this.track("scroll_into_view",s,{scrollDepth:Math.round(window.scrollY/Math.max(1,document.body.scrollHeight)*100)}))};this.intersectionObserver=new IntersectionObserver(s=>{for(const r of s){const n=r.target.dataset.fpId;!n||!r.isIntersecting||e(n)}},{threshold:.1});const i=()=>{document.querySelectorAll("[data-fp-id]").forEach(s=>{this.intersectionObserver?.observe(s)})},t=()=>{const s=window.innerHeight*.1,r=window.innerHeight*.9;document.querySelectorAll("[data-fp-id]").forEach(n=>{const o=n.dataset.fpId;if(!o||this.seenInView.has(o))return;const a=n.getBoundingClientRect();a.bottom>=s&&a.top<=r&&e(o)})};i(),t(),this.scrollFallbackListener=()=>t(),this.resizeFallbackListener=()=>t(),window.addEventListener("scroll",this.scrollFallbackListener,{passive:!0}),window.addEventListener("resize",this.resizeFallbackListener,{passive:!0}),this.observer=new MutationObserver(()=>i()),this.observer.observe(document.body,{childList:!0,subtree:!0})}track(e,i,t){this.isBrowser()&&this.queue.push({projectApiKey:this.config.apiKey,elementId:i,eventType:e,sessionId:this.sessionId,visitorId:this.visitorId,page:window.location.pathname,referrer:document.referrer,device:this.getDeviceType(),browser:this.getBrowser(),os:this.getOS(),metadata:t,timestamp:new Date().toISOString()})}trackFeedback(e,i,t){this.isBrowser()&&(!i&&!t||this.sendImmediate([{projectApiKey:this.config.apiKey,elementId:e??"__page__",eventType:"feedback",sessionId:this.sessionId,visitorId:this.visitorId,page:window.location.pathname,device:this.getDeviceType(),metadata:{rating:i,message:t},timestamp:new Date().toISOString()}]))}async flush(){if(this.queue.length===0)return;const e=[...this.queue];this.queue=[],await this.sendImmediate(e)}async sendImmediate(e){try{const i=await fetch(this.config.endpoint,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0});if(this.config.debug&&i.ok){const t=await i.json().catch(()=>null);t?.ingested!==void 0&&t?.pipeline&&globalThis.console.debug(`[FeedPulse] ${t.ingested} events via ${t.pipeline} pipeline`)}}catch{}}startFlushInterval(){this.flushInterval=setInterval(()=>this.flush(),this.config.batchInterval),window.addEventListener("beforeunload",()=>this.flush()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()})}getOrCreateVisitorId(){const e="__fp_vid",i=this.generateId();try{let t=localStorage.getItem(e);return t||(t=i,localStorage.setItem(e,t)),t}catch{return i}}generateId(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).slice(2)}`}isBrowser(){return typeof window<"u"&&typeof document<"u"}generateSessionId(){return`${Date.now()}-${Math.random().toString(36).slice(2)}`}getDeviceType(){const e=navigator.userAgent;return/Mobi|Android/i.test(e)?"mobile":/Tablet|iPad/i.test(e)?"tablet":"desktop"}getBrowser(){const e=navigator.userAgent;return e.includes("Edg")?"Edge":e.includes("Chrome")?"Chrome":e.includes("Firefox")?"Firefox":e.includes("Safari")?"Safari":"Other"}getOS(){const e=navigator.userAgent;return e.includes("Windows")?"Windows":e.includes("Mac")?"macOS":e.includes("Linux")?"Linux":e.includes("Android")?"Android":/iPhone|iOS/.test(e)?"iOS":"Other"}destroy(){this.flushInterval&&clearInterval(this.flushInterval),this.scrollFallbackListener&&window.removeEventListener("scroll",this.scrollFallbackListener),this.resizeFallbackListener&&window.removeEventListener("resize",this.resizeFallbackListener),this.observer?.disconnect(),this.intersectionObserver?.disconnect(),this.flush()}}exports.FeedPulseTracker=d;
package/dist/index.mjs ADDED
@@ -0,0 +1,153 @@
1
+ class l {
2
+ constructor(e) {
3
+ this.queue = [], this.flushInterval = null, this.observer = null, this.intersectionObserver = null, this.scrollFallbackListener = null, this.resizeFallbackListener = null, this.seenInView = /* @__PURE__ */ new Set();
4
+ const i = "https://feedpulse-workers.YOUR_SUBDOMAIN.workers.dev/ingest";
5
+ this.config = {
6
+ apiKey: e.apiKey,
7
+ endpoint: e.endpoint ?? i,
8
+ batchInterval: e.batchInterval ?? 5e3,
9
+ debug: e.debug ?? !1
10
+ }, this.visitorId = "", this.sessionId = "";
11
+ }
12
+ init() {
13
+ this.isBrowser() && (this.visitorId = this.getOrCreateVisitorId(), this.sessionId = this.generateSessionId(), this.startFlushInterval(), this.attachGlobalListeners(), this.observeDOM());
14
+ }
15
+ attachGlobalListeners() {
16
+ document.addEventListener("click", (t) => {
17
+ const s = t.target.closest("[data-fp-id]");
18
+ s && this.track("click", s.getAttribute("data-fp-id"), {
19
+ x: t.clientX,
20
+ y: t.clientY
21
+ });
22
+ }, { passive: !0 });
23
+ let e = 0, i = null;
24
+ document.addEventListener("mouseover", (t) => {
25
+ const s = t.target.closest("[data-fp-id]");
26
+ s && (e = Date.now(), i = s.getAttribute("data-fp-id"));
27
+ }, { passive: !0 }), document.addEventListener("mouseout", (t) => {
28
+ if (!t.target.closest("[data-fp-id]") || !i)
29
+ return;
30
+ const r = Date.now() - e;
31
+ r > 500 && this.track("hover", i, { hoverDuration: r }), i = null;
32
+ }, { passive: !0 });
33
+ }
34
+ observeDOM() {
35
+ const e = (s) => {
36
+ this.seenInView.has(s) || (this.seenInView.add(s), this.track("scroll_into_view", s, {
37
+ scrollDepth: Math.round(window.scrollY / Math.max(1, document.body.scrollHeight) * 100)
38
+ }));
39
+ };
40
+ this.intersectionObserver = new IntersectionObserver((s) => {
41
+ for (const r of s) {
42
+ const n = r.target.dataset.fpId;
43
+ !n || !r.isIntersecting || e(n);
44
+ }
45
+ }, { threshold: 0.1 });
46
+ const i = () => {
47
+ document.querySelectorAll("[data-fp-id]").forEach((s) => {
48
+ this.intersectionObserver?.observe(s);
49
+ });
50
+ }, t = () => {
51
+ const s = window.innerHeight * 0.1, r = window.innerHeight * 0.9;
52
+ document.querySelectorAll("[data-fp-id]").forEach((n) => {
53
+ const o = n.dataset.fpId;
54
+ if (!o || this.seenInView.has(o))
55
+ return;
56
+ const a = n.getBoundingClientRect();
57
+ a.bottom >= s && a.top <= r && e(o);
58
+ });
59
+ };
60
+ i(), t(), this.scrollFallbackListener = () => t(), this.resizeFallbackListener = () => t(), window.addEventListener("scroll", this.scrollFallbackListener, { passive: !0 }), window.addEventListener("resize", this.resizeFallbackListener, { passive: !0 }), this.observer = new MutationObserver(() => i()), this.observer.observe(document.body, { childList: !0, subtree: !0 });
61
+ }
62
+ track(e, i, t) {
63
+ this.isBrowser() && this.queue.push({
64
+ projectApiKey: this.config.apiKey,
65
+ elementId: i,
66
+ eventType: e,
67
+ sessionId: this.sessionId,
68
+ visitorId: this.visitorId,
69
+ page: window.location.pathname,
70
+ referrer: document.referrer,
71
+ device: this.getDeviceType(),
72
+ browser: this.getBrowser(),
73
+ os: this.getOS(),
74
+ metadata: t,
75
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
76
+ });
77
+ }
78
+ trackFeedback(e, i, t) {
79
+ this.isBrowser() && (!i && !t || this.sendImmediate([{
80
+ projectApiKey: this.config.apiKey,
81
+ elementId: e ?? "__page__",
82
+ eventType: "feedback",
83
+ sessionId: this.sessionId,
84
+ visitorId: this.visitorId,
85
+ page: window.location.pathname,
86
+ device: this.getDeviceType(),
87
+ metadata: { rating: i, message: t },
88
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
89
+ }]));
90
+ }
91
+ async flush() {
92
+ if (this.queue.length === 0)
93
+ return;
94
+ const e = [...this.queue];
95
+ this.queue = [], await this.sendImmediate(e);
96
+ }
97
+ async sendImmediate(e) {
98
+ try {
99
+ const i = await fetch(this.config.endpoint, {
100
+ method: "POST",
101
+ headers: { "content-type": "application/json" },
102
+ body: JSON.stringify({ events: e }),
103
+ keepalive: !0
104
+ });
105
+ if (this.config.debug && i.ok) {
106
+ const t = await i.json().catch(() => null);
107
+ t?.ingested !== void 0 && t?.pipeline && globalThis.console.debug(`[FeedPulse] ${t.ingested} events via ${t.pipeline} pipeline`);
108
+ }
109
+ } catch {
110
+ }
111
+ }
112
+ startFlushInterval() {
113
+ this.flushInterval = setInterval(() => this.flush(), this.config.batchInterval), window.addEventListener("beforeunload", () => this.flush()), document.addEventListener("visibilitychange", () => {
114
+ document.visibilityState === "hidden" && this.flush();
115
+ });
116
+ }
117
+ getOrCreateVisitorId() {
118
+ const e = "__fp_vid", i = this.generateId();
119
+ try {
120
+ let t = localStorage.getItem(e);
121
+ return t || (t = i, localStorage.setItem(e, t)), t;
122
+ } catch {
123
+ return i;
124
+ }
125
+ }
126
+ generateId() {
127
+ return typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
128
+ }
129
+ isBrowser() {
130
+ return typeof window < "u" && typeof document < "u";
131
+ }
132
+ generateSessionId() {
133
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
134
+ }
135
+ getDeviceType() {
136
+ const e = navigator.userAgent;
137
+ return /Mobi|Android/i.test(e) ? "mobile" : /Tablet|iPad/i.test(e) ? "tablet" : "desktop";
138
+ }
139
+ getBrowser() {
140
+ const e = navigator.userAgent;
141
+ return e.includes("Edg") ? "Edge" : e.includes("Chrome") ? "Chrome" : e.includes("Firefox") ? "Firefox" : e.includes("Safari") ? "Safari" : "Other";
142
+ }
143
+ getOS() {
144
+ const e = navigator.userAgent;
145
+ return e.includes("Windows") ? "Windows" : e.includes("Mac") ? "macOS" : e.includes("Linux") ? "Linux" : e.includes("Android") ? "Android" : /iPhone|iOS/.test(e) ? "iOS" : "Other";
146
+ }
147
+ destroy() {
148
+ this.flushInterval && clearInterval(this.flushInterval), this.scrollFallbackListener && window.removeEventListener("scroll", this.scrollFallbackListener), this.resizeFallbackListener && window.removeEventListener("resize", this.resizeFallbackListener), this.observer?.disconnect(), this.intersectionObserver?.disconnect(), this.flush();
149
+ }
150
+ }
151
+ export {
152
+ l as FeedPulseTracker
153
+ };
package/dist/nuxt.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("vue"),g=require("./index.js"),c=Symbol("feedpulse");function p(){return e.inject(c,null)}const b=e.defineComponent({__name:"FeedPulseProvider",props:{apiKey:{},endpoint:{},debug:{type:Boolean}},setup(t){const o=t,d=new g.FeedPulseTracker({apiKey:o.apiKey,endpoint:o.endpoint,debug:o.debug});return e.provide(c,d),e.onMounted(()=>d.init()),e.onBeforeUnmount(()=>d.destroy()),(r,n)=>e.renderSlot(r.$slots,"default")}}),x={key:1,style:{background:"#fff",border:"1px solid #e5e7eb","border-radius":"12px",padding:"16px",width:"280px","box-shadow":"0 10px 25px rgba(0,0,0,0.15)"}},k={style:{margin:"0 0 12px","font-weight":"600",color:"#111"}},v={style:{display:"flex",gap:"6px","margin-bottom":"12px"}},y=["onClick"],h={key:1,style:{"text-align":"center",padding:"16px",color:"#111"}},E=e.defineComponent({__name:"FeedPulseWidget",props:{id:{},placeholder:{},position:{default:"bottom-right"}},setup(t){const o=t,d=p(),r=e.ref(o.position==="inline"),n=e.ref(null),s=e.ref(""),a=e.ref(!1);function m(u){n.value=u}function f(){!n.value&&!s.value.trim()||(d?.trackFeedback(o.id??null,n.value,s.value||null),a.value=!0,setTimeout(()=>{r.value=o.position==="inline",a.value=!1,n.value=null,s.value=""},2e3))}return(u,l)=>(e.openBlock(),e.createElementBlock("div",{class:"feedpulse-widget",style:e.normalizeStyle(t.position!=="inline"?`position:fixed; ${t.position==="bottom-right"?"right:24px":"left:24px"}; bottom:24px; z-index:9999;`:"")},[t.position!=="inline"?(e.openBlock(),e.createElementBlock("button",{key:0,style:{background:"#6366f1",color:"#fff",border:"none","border-radius":"50%",width:"48px",height:"48px","font-size":"20px",cursor:"pointer"},onClick:l[0]||(l[0]=i=>r.value=!r.value)}," 💬 ")):e.createCommentVNode("",!0),r.value?(e.openBlock(),e.createElementBlock("div",x,[a.value?(e.openBlock(),e.createElementBlock("div",h,[...l[2]||(l[2]=[e.createElementVNode("p",{style:{"font-size":"28px",margin:"0"}}," 🎉 ",-1),e.createElementVNode("p",{style:{margin:"8px 0 0","font-weight":"600"}}," Thanks for your feedback! ",-1)])])):(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createElementVNode("p",k,e.toDisplayString(t.placeholder??"How was your experience?"),1),e.createElementVNode("div",v,[(e.openBlock(),e.createElementBlock(e.Fragment,null,e.renderList(5,i=>e.createElementVNode("button",{key:i,style:e.normalizeStyle({background:"none",border:"none",cursor:"pointer",fontSize:"24px",opacity:n.value!==null&&i<=n.value?1:.3}),onClick:w=>m(i)}," ⭐ ",12,y)),64))]),e.withDirectives(e.createElementVNode("textarea",{"onUpdate:modelValue":l[1]||(l[1]=i=>s.value=i),placeholder:"Tell us more (optional)...",rows:"3",style:{width:"100%",padding:"8px",border:"1px solid #d1d5db","border-radius":"8px",resize:"none","font-size":"14px"}},null,512),[[e.vModelText,s.value]]),e.createElementVNode("button",{style:{"margin-top":"10px",width:"100%",background:"#6366f1",color:"#fff",border:"none","border-radius":"8px",padding:"10px","font-weight":"600",cursor:"pointer"},onClick:f}," Submit Feedback ")],64))])):e.createCommentVNode("",!0)],4))}});exports.FeedPulseProvider=b;exports.FeedPulseWidget=E;exports.useFeedPulse=p;
package/dist/nuxt.mjs ADDED
@@ -0,0 +1,88 @@
1
+ import { inject as h, defineComponent as m, provide as w, onMounted as F, onBeforeUnmount as P, renderSlot as _, ref as p, createElementBlock as s, openBlock as d, normalizeStyle as x, createCommentVNode as b, Fragment as g, createElementVNode as n, withDirectives as z, toDisplayString as S, renderList as C, vModelText as E } from "vue";
2
+ import { FeedPulseTracker as T } from "./index.mjs";
3
+ const v = /* @__PURE__ */ Symbol("feedpulse");
4
+ function $() {
5
+ return h(v, null);
6
+ }
7
+ const W = /* @__PURE__ */ m({
8
+ __name: "FeedPulseProvider",
9
+ props: {
10
+ apiKey: {},
11
+ endpoint: {},
12
+ debug: { type: Boolean }
13
+ },
14
+ setup(e) {
15
+ const t = e, u = new T({
16
+ apiKey: t.apiKey,
17
+ endpoint: t.endpoint,
18
+ debug: t.debug
19
+ });
20
+ return w(v, u), F(() => u.init()), P(() => u.destroy()), (i, o) => _(i.$slots, "default");
21
+ }
22
+ }), B = {
23
+ key: 1,
24
+ style: { background: "#fff", border: "1px solid #e5e7eb", "border-radius": "12px", padding: "16px", width: "280px", "box-shadow": "0 10px 25px rgba(0,0,0,0.15)" }
25
+ }, K = { style: { margin: "0 0 12px", "font-weight": "600", color: "#111" } }, D = { style: { display: "flex", gap: "6px", "margin-bottom": "12px" } }, U = ["onClick"], V = {
26
+ key: 1,
27
+ style: { "text-align": "center", padding: "16px", color: "#111" }
28
+ }, j = /* @__PURE__ */ m({
29
+ __name: "FeedPulseWidget",
30
+ props: {
31
+ id: {},
32
+ placeholder: {},
33
+ position: { default: "bottom-right" }
34
+ },
35
+ setup(e) {
36
+ const t = e, u = $(), i = p(t.position === "inline"), o = p(null), a = p(""), c = p(!1);
37
+ function y(f) {
38
+ o.value = f;
39
+ }
40
+ function k() {
41
+ !o.value && !a.value.trim() || (u?.trackFeedback(t.id ?? null, o.value, a.value || null), c.value = !0, setTimeout(() => {
42
+ i.value = t.position === "inline", c.value = !1, o.value = null, a.value = "";
43
+ }, 2e3));
44
+ }
45
+ return (f, r) => (d(), s("div", {
46
+ class: "feedpulse-widget",
47
+ style: x(e.position !== "inline" ? `position:fixed; ${e.position === "bottom-right" ? "right:24px" : "left:24px"}; bottom:24px; z-index:9999;` : "")
48
+ }, [
49
+ e.position !== "inline" ? (d(), s("button", {
50
+ key: 0,
51
+ style: { background: "#6366f1", color: "#fff", border: "none", "border-radius": "50%", width: "48px", height: "48px", "font-size": "20px", cursor: "pointer" },
52
+ onClick: r[0] || (r[0] = (l) => i.value = !i.value)
53
+ }, " 💬 ")) : b("", !0),
54
+ i.value ? (d(), s("div", B, [
55
+ c.value ? (d(), s("div", V, [...r[2] || (r[2] = [
56
+ n("p", { style: { "font-size": "28px", margin: "0" } }, " 🎉 ", -1),
57
+ n("p", { style: { margin: "8px 0 0", "font-weight": "600" } }, " Thanks for your feedback! ", -1)
58
+ ])])) : (d(), s(g, { key: 0 }, [
59
+ n("p", K, S(e.placeholder ?? "How was your experience?"), 1),
60
+ n("div", D, [
61
+ (d(), s(g, null, C(5, (l) => n("button", {
62
+ key: l,
63
+ style: x({ background: "none", border: "none", cursor: "pointer", fontSize: "24px", opacity: o.value !== null && l <= o.value ? 1 : 0.3 }),
64
+ onClick: (L) => y(l)
65
+ }, " ⭐ ", 12, U)), 64))
66
+ ]),
67
+ z(n("textarea", {
68
+ "onUpdate:modelValue": r[1] || (r[1] = (l) => a.value = l),
69
+ placeholder: "Tell us more (optional)...",
70
+ rows: "3",
71
+ style: { width: "100%", padding: "8px", border: "1px solid #d1d5db", "border-radius": "8px", resize: "none", "font-size": "14px" }
72
+ }, null, 512), [
73
+ [E, a.value]
74
+ ]),
75
+ n("button", {
76
+ style: { "margin-top": "10px", width: "100%", background: "#6366f1", color: "#fff", border: "none", "border-radius": "8px", padding: "10px", "font-weight": "600", cursor: "pointer" },
77
+ onClick: k
78
+ }, " Submit Feedback ")
79
+ ], 64))
80
+ ])) : b("", !0)
81
+ ], 4));
82
+ }
83
+ });
84
+ export {
85
+ W as FeedPulseProvider,
86
+ j as FeedPulseWidget,
87
+ $ as useFeedPulse
88
+ };
package/dist/react.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("react"),ne=require("./index.js");var S={exports:{}},g={};var z;function oe(){if(z)return g;z=1;var i=Symbol.for("react.transitional.element"),f=Symbol.for("react.fragment");function s(d,n,u){var c=null;if(u!==void 0&&(c=""+u),n.key!==void 0&&(c=""+n.key),"key"in n){u={};for(var b in n)b!=="key"&&(u[b]=n[b])}else u=n;return n=u.ref,{$$typeof:i,type:d,key:c,ref:n!==void 0?n:null,props:u}}return g.Fragment=f,g.jsx=s,g.jsxs=s,g}var x={};var D;function ae(){return D||(D=1,process.env.NODE_ENV!=="production"&&(function(){function i(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===ee?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case P:return"Fragment";case V:return"Profiler";case J:return"StrictMode";case H:return"Suspense";case Z:return"SuspenseList";case K:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case U:return"Portal";case X:return e.displayName||"Context";case G:return(e._context.displayName||"Context")+".Consumer";case B:var r=e.render;return e=e.displayName,e||(e=r.displayName||r.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Q:return r=e.displayName||null,r!==null?r:i(e.type)||"Memo";case j:r=e._payload,e=e._init;try{return i(e(r))}catch{}}return null}function f(e){return""+e}function s(e){try{f(e);var r=!1}catch{r=!0}if(r){r=console;var t=r.error,o=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return t.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",o),f(e)}}function d(e){if(e===P)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===j)return"<...>";try{var r=i(e);return r?"<"+r+">":"<...>"}catch{return"<...>"}}function n(){var e=w.A;return e===null?null:e.getOwner()}function u(){return Error("react-stack-top-frame")}function c(e){if(N.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function b(e,r){function t(){F||(F=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",r))}t.isReactWarning=!0,Object.defineProperty(e,"key",{get:t,configurable:!0})}function R(){var e=i(this.type);return Y[e]||(Y[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function T(e,r,t,o,y,A){var a=t.ref;return e={$$typeof:p,type:e,key:r,props:t,_owner:o},(a!==void 0?a:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:R}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:y}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:A}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function k(e,r,t,o,y,A){var a=r.children;if(a!==void 0)if(o)if(re(a)){for(o=0;o<a.length;o++)v(a[o]);Object.freeze&&Object.freeze(a)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else v(a);if(N.call(r,"key")){a=i(e);var _=Object.keys(r).filter(function(te){return te!=="key"});o=0<_.length?"{key: someKey, "+_.join(": ..., ")+": ...}":"{key: someKey}",W[a+o]||(_=0<_.length?"{"+_.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
2
+ let props = %s;
3
+ <%s {...props} />
4
+ React keys must be passed directly to JSX without using spread:
5
+ let props = %s;
6
+ <%s key={someKey} {...props} />`,o,a,_,a),W[a+o]=!0)}if(a=null,t!==void 0&&(s(t),a=""+t),c(r)&&(s(r.key),a=""+r.key),"key"in r){t={};for(var C in r)C!=="key"&&(t[C]=r[C])}else t=r;return a&&b(t,typeof e=="function"?e.displayName||e.name||"Unknown":e),T(e,a,t,n(),y,A)}function v(e){h(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===j&&(e._payload.status==="fulfilled"?h(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function h(e){return typeof e=="object"&&e!==null&&e.$$typeof===p}var E=m,p=Symbol.for("react.transitional.element"),U=Symbol.for("react.portal"),P=Symbol.for("react.fragment"),J=Symbol.for("react.strict_mode"),V=Symbol.for("react.profiler"),G=Symbol.for("react.consumer"),X=Symbol.for("react.context"),B=Symbol.for("react.forward_ref"),H=Symbol.for("react.suspense"),Z=Symbol.for("react.suspense_list"),Q=Symbol.for("react.memo"),j=Symbol.for("react.lazy"),K=Symbol.for("react.activity"),ee=Symbol.for("react.client.reference"),w=E.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,N=Object.prototype.hasOwnProperty,re=Array.isArray,O=console.createTask?console.createTask:function(){return null};E={react_stack_bottom_frame:function(e){return e()}};var F,Y={},I=E.react_stack_bottom_frame.bind(E,u)(),$=O(d(u)),W={};x.Fragment=P,x.jsx=function(e,r,t){var o=1e4>w.recentlyCreatedOwnerStacks++;return k(e,r,t,!1,o?Error("react-stack-top-frame"):I,o?O(d(e)):$)},x.jsxs=function(e,r,t){var o=1e4>w.recentlyCreatedOwnerStacks++;return k(e,r,t,!0,o?Error("react-stack-top-frame"):I,o?O(d(e)):$)}})()),x}var M;function se(){return M||(M=1,process.env.NODE_ENV==="production"?S.exports=oe():S.exports=ae()),S.exports}var l=se();const L=m.createContext({tracker:null});function le({apiKey:i,endpoint:f,debug:s,children:d}){const n=m.useRef(null);return m.useEffect(()=>(n.current=new ne.FeedPulseTracker({apiKey:i,endpoint:f,debug:s}),n.current.init(),()=>n.current?.destroy()),[i,f,s]),l.jsx(L.Provider,{value:{tracker:n.current},children:d})}function q(){return m.useContext(L)}function ue({id:i,placeholder:f,position:s="bottom-right"}){const{tracker:d}=q(),[n,u]=m.useState(s==="inline"),[c,b]=m.useState(null),[R,T]=m.useState(""),[k,v]=m.useState(!1),h=()=>{!c&&!R.trim()||(d?.trackFeedback(i??null,c,R||null),v(!0),setTimeout(()=>{u(s==="inline"),v(!1),b(null),T("")},2e3))},E=s!=="inline"?{position:"fixed",bottom:24,[s==="bottom-right"?"right":"left"]:24,zIndex:9999}:{};return l.jsxs("div",{style:E,children:[s!=="inline"&&l.jsx("button",{onClick:()=>u(!n),style:{background:"#6366f1",color:"#fff",border:"none",borderRadius:"50%",width:48,height:48,fontSize:20,cursor:"pointer"},children:"💬"}),n&&l.jsx("div",{style:{background:"#fff",border:"1px solid #e5e7eb",borderRadius:12,padding:16,width:280,boxShadow:"0 10px 25px rgba(0,0,0,0.15)"},children:k?l.jsxs("div",{style:{textAlign:"center",padding:16},children:[l.jsx("p",{style:{fontSize:28,margin:0},children:"🎉"}),l.jsx("p",{style:{margin:"8px 0 0",fontWeight:600},children:"Thanks for your feedback!"})]}):l.jsxs(l.Fragment,{children:[l.jsx("p",{style:{margin:"0 0 12px",fontWeight:600},children:f??"How was your experience?"}),l.jsx("div",{style:{display:"flex",gap:6,marginBottom:12},children:[1,2,3,4,5].map(p=>l.jsx("button",{onClick:()=>b(p),style:{background:"none",border:"none",cursor:"pointer",fontSize:24,opacity:c!==null&&p<=c?1:.3},children:"⭐"},p))}),l.jsx("textarea",{value:R,onChange:p=>T(p.target.value),placeholder:"Tell us more (optional)...",rows:3,style:{width:"100%",padding:8,border:"1px solid #d1d5db",borderRadius:8,resize:"none",fontSize:14}}),l.jsx("button",{onClick:h,style:{marginTop:10,width:"100%",background:"#6366f1",color:"#fff",border:"none",borderRadius:8,padding:10,fontWeight:600,cursor:"pointer"},children:"Submit Feedback"})]})})]})}exports.FeedPulseProvider=le;exports.FeedPulseWidget=ue;exports.useFeedPulse=q;
package/dist/react.mjs ADDED
@@ -0,0 +1,330 @@
1
+ import te, { createContext as ne, useRef as oe, useEffect as ae, useContext as se, useState as y } from "react";
2
+ import { FeedPulseTracker as le } from "./index.mjs";
3
+ var S = { exports: {} }, v = {};
4
+ var z;
5
+ function ie() {
6
+ if (z) return v;
7
+ z = 1;
8
+ var u = /* @__PURE__ */ Symbol.for("react.transitional.element"), f = /* @__PURE__ */ Symbol.for("react.fragment");
9
+ function s(d, n, i) {
10
+ var c = null;
11
+ if (i !== void 0 && (c = "" + i), n.key !== void 0 && (c = "" + n.key), "key" in n) {
12
+ i = {};
13
+ for (var m in n)
14
+ m !== "key" && (i[m] = n[m]);
15
+ } else i = n;
16
+ return n = i.ref, {
17
+ $$typeof: u,
18
+ type: d,
19
+ key: c,
20
+ ref: n !== void 0 ? n : null,
21
+ props: i
22
+ };
23
+ }
24
+ return v.Fragment = f, v.jsx = s, v.jsxs = s, v;
25
+ }
26
+ var x = {};
27
+ var D;
28
+ function ue() {
29
+ return D || (D = 1, process.env.NODE_ENV !== "production" && (function() {
30
+ function u(e) {
31
+ if (e == null) return null;
32
+ if (typeof e == "function")
33
+ return e.$$typeof === K ? null : e.displayName || e.name || null;
34
+ if (typeof e == "string") return e;
35
+ switch (e) {
36
+ case j:
37
+ return "Fragment";
38
+ case J:
39
+ return "Profiler";
40
+ case q:
41
+ return "StrictMode";
42
+ case B:
43
+ return "Suspense";
44
+ case H:
45
+ return "SuspenseList";
46
+ case Q:
47
+ return "Activity";
48
+ }
49
+ if (typeof e == "object")
50
+ switch (typeof e.tag == "number" && console.error(
51
+ "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
52
+ ), e.$$typeof) {
53
+ case U:
54
+ return "Portal";
55
+ case G:
56
+ return e.displayName || "Context";
57
+ case V:
58
+ return (e._context.displayName || "Context") + ".Consumer";
59
+ case X:
60
+ var r = e.render;
61
+ return e = e.displayName, e || (e = r.displayName || r.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
62
+ case Z:
63
+ return r = e.displayName || null, r !== null ? r : u(e.type) || "Memo";
64
+ case w:
65
+ r = e._payload, e = e._init;
66
+ try {
67
+ return u(e(r));
68
+ } catch {
69
+ }
70
+ }
71
+ return null;
72
+ }
73
+ function f(e) {
74
+ return "" + e;
75
+ }
76
+ function s(e) {
77
+ try {
78
+ f(e);
79
+ var r = !1;
80
+ } catch {
81
+ r = !0;
82
+ }
83
+ if (r) {
84
+ r = console;
85
+ var t = r.error, o = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
86
+ return t.call(
87
+ r,
88
+ "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
89
+ o
90
+ ), f(e);
91
+ }
92
+ }
93
+ function d(e) {
94
+ if (e === j) return "<>";
95
+ if (typeof e == "object" && e !== null && e.$$typeof === w)
96
+ return "<...>";
97
+ try {
98
+ var r = u(e);
99
+ return r ? "<" + r + ">" : "<...>";
100
+ } catch {
101
+ return "<...>";
102
+ }
103
+ }
104
+ function n() {
105
+ var e = P.A;
106
+ return e === null ? null : e.getOwner();
107
+ }
108
+ function i() {
109
+ return Error("react-stack-top-frame");
110
+ }
111
+ function c(e) {
112
+ if (N.call(e, "key")) {
113
+ var r = Object.getOwnPropertyDescriptor(e, "key").get;
114
+ if (r && r.isReactWarning) return !1;
115
+ }
116
+ return e.key !== void 0;
117
+ }
118
+ function m(e, r) {
119
+ function t() {
120
+ F || (F = !0, console.error(
121
+ "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
122
+ r
123
+ ));
124
+ }
125
+ t.isReactWarning = !0, Object.defineProperty(e, "key", {
126
+ get: t,
127
+ configurable: !0
128
+ });
129
+ }
130
+ function _() {
131
+ var e = u(this.type);
132
+ return Y[e] || (Y[e] = !0, console.error(
133
+ "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
134
+ )), e = this.props.ref, e !== void 0 ? e : null;
135
+ }
136
+ function g(e, r, t, o, h, A) {
137
+ var a = t.ref;
138
+ return e = {
139
+ $$typeof: b,
140
+ type: e,
141
+ key: r,
142
+ props: t,
143
+ _owner: o
144
+ }, (a !== void 0 ? a : null) !== null ? Object.defineProperty(e, "ref", {
145
+ enumerable: !1,
146
+ get: _
147
+ }) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
148
+ configurable: !1,
149
+ enumerable: !1,
150
+ writable: !0,
151
+ value: 0
152
+ }), Object.defineProperty(e, "_debugInfo", {
153
+ configurable: !1,
154
+ enumerable: !1,
155
+ writable: !0,
156
+ value: null
157
+ }), Object.defineProperty(e, "_debugStack", {
158
+ configurable: !1,
159
+ enumerable: !1,
160
+ writable: !0,
161
+ value: h
162
+ }), Object.defineProperty(e, "_debugTask", {
163
+ configurable: !1,
164
+ enumerable: !1,
165
+ writable: !0,
166
+ value: A
167
+ }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
168
+ }
169
+ function k(e, r, t, o, h, A) {
170
+ var a = r.children;
171
+ if (a !== void 0)
172
+ if (o)
173
+ if (ee(a)) {
174
+ for (o = 0; o < a.length; o++)
175
+ R(a[o]);
176
+ Object.freeze && Object.freeze(a);
177
+ } else
178
+ console.error(
179
+ "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
180
+ );
181
+ else R(a);
182
+ if (N.call(r, "key")) {
183
+ a = u(e);
184
+ var E = Object.keys(r).filter(function(re) {
185
+ return re !== "key";
186
+ });
187
+ o = 0 < E.length ? "{key: someKey, " + E.join(": ..., ") + ": ...}" : "{key: someKey}", W[a + o] || (E = 0 < E.length ? "{" + E.join(": ..., ") + ": ...}" : "{}", console.error(
188
+ `A props object containing a "key" prop is being spread into JSX:
189
+ let props = %s;
190
+ <%s {...props} />
191
+ React keys must be passed directly to JSX without using spread:
192
+ let props = %s;
193
+ <%s key={someKey} {...props} />`,
194
+ o,
195
+ a,
196
+ E,
197
+ a
198
+ ), W[a + o] = !0);
199
+ }
200
+ if (a = null, t !== void 0 && (s(t), a = "" + t), c(r) && (s(r.key), a = "" + r.key), "key" in r) {
201
+ t = {};
202
+ for (var C in r)
203
+ C !== "key" && (t[C] = r[C]);
204
+ } else t = r;
205
+ return a && m(
206
+ t,
207
+ typeof e == "function" ? e.displayName || e.name || "Unknown" : e
208
+ ), g(
209
+ e,
210
+ a,
211
+ t,
212
+ n(),
213
+ h,
214
+ A
215
+ );
216
+ }
217
+ function R(e) {
218
+ T(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === w && (e._payload.status === "fulfilled" ? T(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
219
+ }
220
+ function T(e) {
221
+ return typeof e == "object" && e !== null && e.$$typeof === b;
222
+ }
223
+ var p = te, b = /* @__PURE__ */ Symbol.for("react.transitional.element"), U = /* @__PURE__ */ Symbol.for("react.portal"), j = /* @__PURE__ */ Symbol.for("react.fragment"), q = /* @__PURE__ */ Symbol.for("react.strict_mode"), J = /* @__PURE__ */ Symbol.for("react.profiler"), V = /* @__PURE__ */ Symbol.for("react.consumer"), G = /* @__PURE__ */ Symbol.for("react.context"), X = /* @__PURE__ */ Symbol.for("react.forward_ref"), B = /* @__PURE__ */ Symbol.for("react.suspense"), H = /* @__PURE__ */ Symbol.for("react.suspense_list"), Z = /* @__PURE__ */ Symbol.for("react.memo"), w = /* @__PURE__ */ Symbol.for("react.lazy"), Q = /* @__PURE__ */ Symbol.for("react.activity"), K = /* @__PURE__ */ Symbol.for("react.client.reference"), P = p.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, N = Object.prototype.hasOwnProperty, ee = Array.isArray, O = console.createTask ? console.createTask : function() {
224
+ return null;
225
+ };
226
+ p = {
227
+ react_stack_bottom_frame: function(e) {
228
+ return e();
229
+ }
230
+ };
231
+ var F, Y = {}, I = p.react_stack_bottom_frame.bind(
232
+ p,
233
+ i
234
+ )(), $ = O(d(i)), W = {};
235
+ x.Fragment = j, x.jsx = function(e, r, t) {
236
+ var o = 1e4 > P.recentlyCreatedOwnerStacks++;
237
+ return k(
238
+ e,
239
+ r,
240
+ t,
241
+ !1,
242
+ o ? Error("react-stack-top-frame") : I,
243
+ o ? O(d(e)) : $
244
+ );
245
+ }, x.jsxs = function(e, r, t) {
246
+ var o = 1e4 > P.recentlyCreatedOwnerStacks++;
247
+ return k(
248
+ e,
249
+ r,
250
+ t,
251
+ !0,
252
+ o ? Error("react-stack-top-frame") : I,
253
+ o ? O(d(e)) : $
254
+ );
255
+ };
256
+ })()), x;
257
+ }
258
+ var M;
259
+ function ce() {
260
+ return M || (M = 1, process.env.NODE_ENV === "production" ? S.exports = ie() : S.exports = ue()), S.exports;
261
+ }
262
+ var l = ce();
263
+ const L = ne({ tracker: null });
264
+ function be({
265
+ apiKey: u,
266
+ endpoint: f,
267
+ debug: s,
268
+ children: d
269
+ }) {
270
+ const n = oe(null);
271
+ return ae(() => (n.current = new le({ apiKey: u, endpoint: f, debug: s }), n.current.init(), () => n.current?.destroy()), [u, f, s]), /* @__PURE__ */ l.jsx(L.Provider, { value: { tracker: n.current }, children: d });
272
+ }
273
+ function fe() {
274
+ return se(L);
275
+ }
276
+ function pe({ id: u, placeholder: f, position: s = "bottom-right" }) {
277
+ const { tracker: d } = fe(), [n, i] = y(s === "inline"), [c, m] = y(null), [_, g] = y(""), [k, R] = y(!1), T = () => {
278
+ !c && !_.trim() || (d?.trackFeedback(u ?? null, c, _ || null), R(!0), setTimeout(() => {
279
+ i(s === "inline"), R(!1), m(null), g("");
280
+ }, 2e3));
281
+ }, p = s !== "inline" ? { position: "fixed", bottom: 24, [s === "bottom-right" ? "right" : "left"]: 24, zIndex: 9999 } : {};
282
+ return /* @__PURE__ */ l.jsxs("div", { style: p, children: [
283
+ s !== "inline" && /* @__PURE__ */ l.jsx(
284
+ "button",
285
+ {
286
+ onClick: () => i(!n),
287
+ style: { background: "#6366f1", color: "#fff", border: "none", borderRadius: "50%", width: 48, height: 48, fontSize: 20, cursor: "pointer" },
288
+ children: "💬"
289
+ }
290
+ ),
291
+ n && /* @__PURE__ */ l.jsx("div", { style: { background: "#fff", border: "1px solid #e5e7eb", borderRadius: 12, padding: 16, width: 280, boxShadow: "0 10px 25px rgba(0,0,0,0.15)" }, children: k ? /* @__PURE__ */ l.jsxs("div", { style: { textAlign: "center", padding: 16 }, children: [
292
+ /* @__PURE__ */ l.jsx("p", { style: { fontSize: 28, margin: 0 }, children: "🎉" }),
293
+ /* @__PURE__ */ l.jsx("p", { style: { margin: "8px 0 0", fontWeight: 600 }, children: "Thanks for your feedback!" })
294
+ ] }) : /* @__PURE__ */ l.jsxs(l.Fragment, { children: [
295
+ /* @__PURE__ */ l.jsx("p", { style: { margin: "0 0 12px", fontWeight: 600 }, children: f ?? "How was your experience?" }),
296
+ /* @__PURE__ */ l.jsx("div", { style: { display: "flex", gap: 6, marginBottom: 12 }, children: [1, 2, 3, 4, 5].map((b) => /* @__PURE__ */ l.jsx(
297
+ "button",
298
+ {
299
+ onClick: () => m(b),
300
+ style: { background: "none", border: "none", cursor: "pointer", fontSize: 24, opacity: c !== null && b <= c ? 1 : 0.3 },
301
+ children: "⭐"
302
+ },
303
+ b
304
+ )) }),
305
+ /* @__PURE__ */ l.jsx(
306
+ "textarea",
307
+ {
308
+ value: _,
309
+ onChange: (b) => g(b.target.value),
310
+ placeholder: "Tell us more (optional)...",
311
+ rows: 3,
312
+ style: { width: "100%", padding: 8, border: "1px solid #d1d5db", borderRadius: 8, resize: "none", fontSize: 14 }
313
+ }
314
+ ),
315
+ /* @__PURE__ */ l.jsx(
316
+ "button",
317
+ {
318
+ onClick: T,
319
+ style: { marginTop: 10, width: "100%", background: "#6366f1", color: "#fff", border: "none", borderRadius: 8, padding: 10, fontWeight: 600, cursor: "pointer" },
320
+ children: "Submit Feedback"
321
+ }
322
+ )
323
+ ] }) })
324
+ ] });
325
+ }
326
+ export {
327
+ be as FeedPulseProvider,
328
+ pe as FeedPulseWidget,
329
+ fe as useFeedPulse
330
+ };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@tekibo/feedpulse-sdk",
3
+ "version": "0.4.0",
4
+ "description": "Embeddable analytics and feedback SDK for FeedPulse",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/index.mjs",
8
+ "require": "./dist/index.js"
9
+ },
10
+ "./nuxt": {
11
+ "import": "./dist/nuxt.mjs",
12
+ "require": "./dist/nuxt.js"
13
+ },
14
+ "./react": {
15
+ "import": "./dist/react.mjs",
16
+ "require": "./dist/react.js"
17
+ }
18
+ },
19
+ "main": "./dist/index.js",
20
+ "module": "./dist/index.mjs",
21
+ "types": "./dist/index.d.ts",
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "build": "vite build",
27
+ "dev": "vite build --watch"
28
+ },
29
+ "peerDependencies": {
30
+ "react": ">=18.0.0",
31
+ "vue": ">=3.0.0"
32
+ },
33
+ "peerDependenciesMeta": {
34
+ "react": {
35
+ "optional": true
36
+ },
37
+ "vue": {
38
+ "optional": true
39
+ }
40
+ },
41
+ "devDependencies": {
42
+ "@vitejs/plugin-react": "^4.7.0",
43
+ "@vitejs/plugin-vue": "^6.0.1",
44
+ "react": "^19.1.1",
45
+ "typescript": "^5.9.3",
46
+ "vite": "^7.1.12",
47
+ "vue": "^3.5.26"
48
+ }
49
+ }