pa-floating-window 1.0.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 +52 -0
- package/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +211 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +186 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# pa-floating-window
|
|
2
|
+
|
|
3
|
+
A plug-and-play React floating window widget for feature requests and feedback. Zero configuration, completely unstyled out of the box so it just works!
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install pa-floating-window
|
|
9
|
+
# or
|
|
10
|
+
yarn add pa-floating-window
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Simply import the `FeedbackWidget` into your layout or app root and pass your API endpoint where you want the feedback to be sent.
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { FeedbackWidget } from 'pa-floating-window';
|
|
19
|
+
|
|
20
|
+
export default function App() {
|
|
21
|
+
return (
|
|
22
|
+
<div>
|
|
23
|
+
<h1>My Application</h1>
|
|
24
|
+
|
|
25
|
+
{/* Add the widget anywhere in your dom, it will float automatically */}
|
|
26
|
+
<FeedbackWidget
|
|
27
|
+
apiUrl="https://your-hackbyte-deployment.vercel.app/api/feedback"
|
|
28
|
+
primaryColor="#3b82f6" // Optional: customize the main theme color
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Props
|
|
36
|
+
|
|
37
|
+
| Prop | Type | Default | Description |
|
|
38
|
+
| ---- | ---- | ------- | ----------- |
|
|
39
|
+
| `apiUrl` | string | **Required** | The absolute URL of your REST API to send the `POST` request to. |
|
|
40
|
+
| `primaryColor` | string | `#3b82f6` | Optional hex/rgb value to change the base color of the button and action button. |
|
|
41
|
+
|
|
42
|
+
## payload structure
|
|
43
|
+
|
|
44
|
+
The widget will send a `POST` request to your `apiUrl` containing the following JSON structure:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"title": "Dark Mode Support",
|
|
49
|
+
"description": "It would be great if the app had a dark mode.",
|
|
50
|
+
"email": "user@example.com"
|
|
51
|
+
}
|
|
52
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface FeedbackWidgetProps {
|
|
4
|
+
/**
|
|
5
|
+
* The URL of your API endpoint to send the feedback data to.
|
|
6
|
+
* Example: https://your-domain.com/api/feedback
|
|
7
|
+
*/
|
|
8
|
+
apiUrl: string;
|
|
9
|
+
/**
|
|
10
|
+
* Optional primary color for the button and accents.
|
|
11
|
+
* Defaults to a pleasant blue (#3b82f6)
|
|
12
|
+
*/
|
|
13
|
+
primaryColor?: string;
|
|
14
|
+
}
|
|
15
|
+
declare function FeedbackWidget({ apiUrl, primaryColor }: FeedbackWidgetProps): react_jsx_runtime.JSX.Element;
|
|
16
|
+
|
|
17
|
+
export { FeedbackWidget, type FeedbackWidgetProps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface FeedbackWidgetProps {
|
|
4
|
+
/**
|
|
5
|
+
* The URL of your API endpoint to send the feedback data to.
|
|
6
|
+
* Example: https://your-domain.com/api/feedback
|
|
7
|
+
*/
|
|
8
|
+
apiUrl: string;
|
|
9
|
+
/**
|
|
10
|
+
* Optional primary color for the button and accents.
|
|
11
|
+
* Defaults to a pleasant blue (#3b82f6)
|
|
12
|
+
*/
|
|
13
|
+
primaryColor?: string;
|
|
14
|
+
}
|
|
15
|
+
declare function FeedbackWidget({ apiUrl, primaryColor }: FeedbackWidgetProps): react_jsx_runtime.JSX.Element;
|
|
16
|
+
|
|
17
|
+
export { FeedbackWidget, type FeedbackWidgetProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.tsx
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
FeedbackWidget: () => FeedbackWidget
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var import_react = require("react");
|
|
27
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
28
|
+
function FeedbackWidget({ apiUrl, primaryColor = "#3b82f6" }) {
|
|
29
|
+
const [isOpen, setIsOpen] = (0, import_react.useState)(false);
|
|
30
|
+
const [title, setTitle] = (0, import_react.useState)("");
|
|
31
|
+
const [description, setDescription] = (0, import_react.useState)("");
|
|
32
|
+
const [email, setEmail] = (0, import_react.useState)("");
|
|
33
|
+
const [status, setStatus] = (0, import_react.useState)("idle");
|
|
34
|
+
const handleSubmit = async (e) => {
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
if (!title.trim() || !description.trim() || !email.trim()) return;
|
|
37
|
+
setStatus("loading");
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(apiUrl, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: { "Content-Type": "application/json" },
|
|
42
|
+
body: JSON.stringify({ title, description, email })
|
|
43
|
+
});
|
|
44
|
+
if (response.ok) {
|
|
45
|
+
setStatus("success");
|
|
46
|
+
setTitle("");
|
|
47
|
+
setDescription("");
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
setIsOpen(false);
|
|
50
|
+
setStatus("idle");
|
|
51
|
+
}, 2e3);
|
|
52
|
+
} else {
|
|
53
|
+
setStatus("error");
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("Feedback Widget Error:", error);
|
|
57
|
+
setStatus("error");
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const containerStyle = {
|
|
61
|
+
position: "fixed",
|
|
62
|
+
bottom: "24px",
|
|
63
|
+
right: "24px",
|
|
64
|
+
zIndex: 999999,
|
|
65
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
66
|
+
};
|
|
67
|
+
const buttonStyle = {
|
|
68
|
+
backgroundColor: primaryColor,
|
|
69
|
+
color: "white",
|
|
70
|
+
border: "none",
|
|
71
|
+
borderRadius: "9999px",
|
|
72
|
+
padding: "16px",
|
|
73
|
+
boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
|
|
74
|
+
cursor: "pointer",
|
|
75
|
+
display: "flex",
|
|
76
|
+
alignItems: "center",
|
|
77
|
+
gap: "8px",
|
|
78
|
+
fontWeight: 500,
|
|
79
|
+
fontSize: "16px",
|
|
80
|
+
transition: "transform 0.2s ease-in-out"
|
|
81
|
+
};
|
|
82
|
+
const modalStyle = {
|
|
83
|
+
position: "absolute",
|
|
84
|
+
bottom: "80px",
|
|
85
|
+
right: "0",
|
|
86
|
+
width: "384px",
|
|
87
|
+
// w-96 roughly
|
|
88
|
+
backgroundColor: "white",
|
|
89
|
+
borderRadius: "8px",
|
|
90
|
+
boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
|
|
91
|
+
border: "1px solid #e5e7eb",
|
|
92
|
+
padding: "24px",
|
|
93
|
+
boxSizing: "border-box",
|
|
94
|
+
display: isOpen ? "block" : "none"
|
|
95
|
+
};
|
|
96
|
+
const headerStyle = {
|
|
97
|
+
display: "flex",
|
|
98
|
+
justifyContent: "space-between",
|
|
99
|
+
alignItems: "center",
|
|
100
|
+
marginBottom: "16px"
|
|
101
|
+
};
|
|
102
|
+
const labelStyle = {
|
|
103
|
+
display: "block",
|
|
104
|
+
fontSize: "14px",
|
|
105
|
+
fontWeight: 500,
|
|
106
|
+
color: "#374151",
|
|
107
|
+
marginBottom: "4px"
|
|
108
|
+
};
|
|
109
|
+
const inputStyle = {
|
|
110
|
+
width: "100%",
|
|
111
|
+
padding: "8px 12px",
|
|
112
|
+
border: "1px solid #d1d5db",
|
|
113
|
+
borderRadius: "8px",
|
|
114
|
+
fontSize: "14px",
|
|
115
|
+
boxSizing: "border-box",
|
|
116
|
+
outline: "none",
|
|
117
|
+
fontFamily: "inherit",
|
|
118
|
+
marginBottom: "16px"
|
|
119
|
+
};
|
|
120
|
+
const submitBtnStyle = {
|
|
121
|
+
width: "100%",
|
|
122
|
+
backgroundColor: primaryColor,
|
|
123
|
+
color: "white",
|
|
124
|
+
fontWeight: 500,
|
|
125
|
+
padding: "8px 16px",
|
|
126
|
+
borderRadius: "8px",
|
|
127
|
+
border: "none",
|
|
128
|
+
cursor: status === "loading" ? "not-allowed" : "pointer",
|
|
129
|
+
fontSize: "16px",
|
|
130
|
+
opacity: status === "loading" ? 0.7 : 1
|
|
131
|
+
};
|
|
132
|
+
const statusMsgStyle = {
|
|
133
|
+
fontSize: "14px",
|
|
134
|
+
marginTop: "12px",
|
|
135
|
+
textAlign: "center",
|
|
136
|
+
color: status === "success" ? "#10b981" : "#ef4444",
|
|
137
|
+
display: status === "success" || status === "error" ? "block" : "none"
|
|
138
|
+
};
|
|
139
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: containerStyle, children: [
|
|
140
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: modalStyle, children: [
|
|
141
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: headerStyle, children: [
|
|
142
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: "18px", fontWeight: 600, color: "#1f2937" }, children: "Submit Feature Request" }),
|
|
143
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
144
|
+
"button",
|
|
145
|
+
{
|
|
146
|
+
onClick: () => setIsOpen(false),
|
|
147
|
+
style: { background: "none", border: "none", cursor: "pointer", color: "#9ca3af" },
|
|
148
|
+
"aria-label": "Close",
|
|
149
|
+
type: "button",
|
|
150
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "20", height: "20", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
] }),
|
|
154
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: handleSubmit, children: [
|
|
155
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
156
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: labelStyle, children: "Feature Title" }),
|
|
157
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
158
|
+
"input",
|
|
159
|
+
{
|
|
160
|
+
type: "text",
|
|
161
|
+
required: true,
|
|
162
|
+
value: title,
|
|
163
|
+
onChange: (e) => setTitle(e.target.value),
|
|
164
|
+
placeholder: "Enter feature title",
|
|
165
|
+
style: inputStyle
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
] }),
|
|
169
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
170
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: labelStyle, children: "Description" }),
|
|
171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
172
|
+
"textarea",
|
|
173
|
+
{
|
|
174
|
+
required: true,
|
|
175
|
+
rows: 3,
|
|
176
|
+
value: description,
|
|
177
|
+
onChange: (e) => setDescription(e.target.value),
|
|
178
|
+
placeholder: "Describe the feature you'd like to see...",
|
|
179
|
+
style: { ...inputStyle, resize: "none" }
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
] }),
|
|
183
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
184
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: labelStyle, children: "Enter your Email ID" }),
|
|
185
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
186
|
+
"input",
|
|
187
|
+
{
|
|
188
|
+
type: "email",
|
|
189
|
+
required: true,
|
|
190
|
+
value: email,
|
|
191
|
+
onChange: (e) => setEmail(e.target.value),
|
|
192
|
+
placeholder: "Please enter your email ID",
|
|
193
|
+
style: inputStyle
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
] }),
|
|
197
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", disabled: status === "loading", style: submitBtnStyle, children: status === "loading" ? "Submitting..." : "Submit Request" }),
|
|
198
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: statusMsgStyle, children: status === "success" ? "Thank you for your feedback!" : "Something went wrong. Please try again." })
|
|
199
|
+
] })
|
|
200
|
+
] }),
|
|
201
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setIsOpen(!isOpen), style: buttonStyle, children: isOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "24", height: "24", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
202
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "24", height: "24", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" }) }),
|
|
203
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { display: "inline" }, children: "Request Feature" })
|
|
204
|
+
] }) })
|
|
205
|
+
] });
|
|
206
|
+
}
|
|
207
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
208
|
+
0 && (module.exports = {
|
|
209
|
+
FeedbackWidget
|
|
210
|
+
});
|
|
211
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["import React, { useState, CSSProperties, FormEvent } from \"react\";\n\nexport interface FeedbackWidgetProps {\n /**\n * The URL of your API endpoint to send the feedback data to.\n * Example: https://your-domain.com/api/feedback\n */\n apiUrl: string;\n /**\n * Optional primary color for the button and accents. \n * Defaults to a pleasant blue (#3b82f6)\n */\n primaryColor?: string;\n}\n\nexport function FeedbackWidget({ apiUrl, primaryColor = \"#3b82f6\" }: FeedbackWidgetProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [title, setTitle] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [email, setEmail] = useState(\"\");\n \n const [status, setStatus] = useState<\"idle\" | \"loading\" | \"success\" | \"error\">(\"idle\");\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n if (!title.trim() || !description.trim() || !email.trim()) return;\n\n setStatus(\"loading\");\n\n try {\n const response = await fetch(apiUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ title, description, email }),\n });\n\n if (response.ok) {\n setStatus(\"success\");\n setTitle(\"\");\n setDescription(\"\");\n // Auto-close after a success\n setTimeout(() => {\n setIsOpen(false);\n setStatus(\"idle\");\n }, 2000);\n } else {\n setStatus(\"error\");\n }\n } catch (error) {\n console.error(\"Feedback Widget Error:\", error);\n setStatus(\"error\");\n }\n };\n\n // --- Styles ---\n const containerStyle: CSSProperties = {\n position: \"fixed\",\n bottom: \"24px\",\n right: \"24px\",\n zIndex: 999999,\n fontFamily: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\",\n };\n\n const buttonStyle: CSSProperties = {\n backgroundColor: primaryColor,\n color: \"white\",\n border: \"none\",\n borderRadius: \"9999px\",\n padding: \"16px\",\n boxShadow: \"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)\",\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n gap: \"8px\",\n fontWeight: 500,\n fontSize: \"16px\",\n transition: \"transform 0.2s ease-in-out\",\n };\n\n const modalStyle: CSSProperties = {\n position: \"absolute\",\n bottom: \"80px\",\n right: \"0\",\n width: \"384px\", // w-96 roughly\n backgroundColor: \"white\",\n borderRadius: \"8px\",\n boxShadow: \"0 25px 50px -12px rgba(0, 0, 0, 0.25)\",\n border: \"1px solid #e5e7eb\",\n padding: \"24px\",\n boxSizing: \"border-box\",\n display: isOpen ? \"block\" : \"none\",\n };\n\n const headerStyle: CSSProperties = {\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: \"16px\",\n };\n\n const labelStyle: CSSProperties = {\n display: \"block\",\n fontSize: \"14px\",\n fontWeight: 500,\n color: \"#374151\",\n marginBottom: \"4px\",\n };\n\n const inputStyle: CSSProperties = {\n width: \"100%\",\n padding: \"8px 12px\",\n border: \"1px solid #d1d5db\",\n borderRadius: \"8px\",\n fontSize: \"14px\",\n boxSizing: \"border-box\",\n outline: \"none\",\n fontFamily: \"inherit\",\n marginBottom: \"16px\",\n };\n\n const submitBtnStyle: CSSProperties = {\n width: \"100%\",\n backgroundColor: primaryColor,\n color: \"white\",\n fontWeight: 500,\n padding: \"8px 16px\",\n borderRadius: \"8px\",\n border: \"none\",\n cursor: status === \"loading\" ? \"not-allowed\" : \"pointer\",\n fontSize: \"16px\",\n opacity: status === \"loading\" ? 0.7 : 1,\n };\n\n const statusMsgStyle: CSSProperties = {\n fontSize: \"14px\",\n marginTop: \"12px\",\n textAlign: \"center\",\n color: status === \"success\" ? \"#10b981\" : \"#ef4444\",\n display: status === \"success\" || status === \"error\" ? \"block\" : \"none\",\n };\n\n return (\n <div style={containerStyle}>\n <div style={modalStyle}>\n <div style={headerStyle}>\n <h3 style={{ margin: 0, fontSize: \"18px\", fontWeight: 600, color: \"#1f2937\" }}>\n Submit Feature Request\n </h3>\n <button\n onClick={() => setIsOpen(false)}\n style={{ background: \"none\", border: \"none\", cursor: \"pointer\", color: \"#9ca3af\" }}\n aria-label=\"Close\"\n type=\"button\"\n >\n <svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <form onSubmit={handleSubmit}>\n <div>\n <label style={labelStyle}>Feature Title</label>\n <input\n type=\"text\"\n required\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"Enter feature title\"\n style={inputStyle}\n />\n </div>\n\n <div>\n <label style={labelStyle}>Description</label>\n <textarea\n required\n rows={3}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Describe the feature you'd like to see...\"\n style={{ ...inputStyle, resize: \"none\" }}\n />\n </div>\n\n <div>\n <label style={labelStyle}>Enter your Email ID</label>\n <input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"Please enter your email ID\"\n style={inputStyle}\n />\n </div>\n\n <button type=\"submit\" disabled={status === \"loading\"} style={submitBtnStyle}>\n {status === \"loading\" ? \"Submitting...\" : \"Submit Request\"}\n </button>\n\n <div style={statusMsgStyle}>\n {status === \"success\" ? \"Thank you for your feedback!\" : \"Something went wrong. Please try again.\"}\n </div>\n </form>\n </div>\n\n <button onClick={() => setIsOpen(!isOpen)} style={buttonStyle}>\n {isOpen ? (\n <svg width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n ) : (\n <>\n <svg width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 4v16m8-8H4\" />\n </svg>\n <span style={{ display: \"inline\" }}>Request Feature</span>\n </>\n )}\n </button>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// src/index.tsx
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
function FeedbackWidget({ apiUrl, primaryColor = "#3b82f6" }) {
|
|
5
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
6
|
+
const [title, setTitle] = useState("");
|
|
7
|
+
const [description, setDescription] = useState("");
|
|
8
|
+
const [email, setEmail] = useState("");
|
|
9
|
+
const [status, setStatus] = useState("idle");
|
|
10
|
+
const handleSubmit = async (e) => {
|
|
11
|
+
e.preventDefault();
|
|
12
|
+
if (!title.trim() || !description.trim() || !email.trim()) return;
|
|
13
|
+
setStatus("loading");
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch(apiUrl, {
|
|
16
|
+
method: "POST",
|
|
17
|
+
headers: { "Content-Type": "application/json" },
|
|
18
|
+
body: JSON.stringify({ title, description, email })
|
|
19
|
+
});
|
|
20
|
+
if (response.ok) {
|
|
21
|
+
setStatus("success");
|
|
22
|
+
setTitle("");
|
|
23
|
+
setDescription("");
|
|
24
|
+
setTimeout(() => {
|
|
25
|
+
setIsOpen(false);
|
|
26
|
+
setStatus("idle");
|
|
27
|
+
}, 2e3);
|
|
28
|
+
} else {
|
|
29
|
+
setStatus("error");
|
|
30
|
+
}
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error("Feedback Widget Error:", error);
|
|
33
|
+
setStatus("error");
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const containerStyle = {
|
|
37
|
+
position: "fixed",
|
|
38
|
+
bottom: "24px",
|
|
39
|
+
right: "24px",
|
|
40
|
+
zIndex: 999999,
|
|
41
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
|
|
42
|
+
};
|
|
43
|
+
const buttonStyle = {
|
|
44
|
+
backgroundColor: primaryColor,
|
|
45
|
+
color: "white",
|
|
46
|
+
border: "none",
|
|
47
|
+
borderRadius: "9999px",
|
|
48
|
+
padding: "16px",
|
|
49
|
+
boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
|
|
50
|
+
cursor: "pointer",
|
|
51
|
+
display: "flex",
|
|
52
|
+
alignItems: "center",
|
|
53
|
+
gap: "8px",
|
|
54
|
+
fontWeight: 500,
|
|
55
|
+
fontSize: "16px",
|
|
56
|
+
transition: "transform 0.2s ease-in-out"
|
|
57
|
+
};
|
|
58
|
+
const modalStyle = {
|
|
59
|
+
position: "absolute",
|
|
60
|
+
bottom: "80px",
|
|
61
|
+
right: "0",
|
|
62
|
+
width: "384px",
|
|
63
|
+
// w-96 roughly
|
|
64
|
+
backgroundColor: "white",
|
|
65
|
+
borderRadius: "8px",
|
|
66
|
+
boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
|
|
67
|
+
border: "1px solid #e5e7eb",
|
|
68
|
+
padding: "24px",
|
|
69
|
+
boxSizing: "border-box",
|
|
70
|
+
display: isOpen ? "block" : "none"
|
|
71
|
+
};
|
|
72
|
+
const headerStyle = {
|
|
73
|
+
display: "flex",
|
|
74
|
+
justifyContent: "space-between",
|
|
75
|
+
alignItems: "center",
|
|
76
|
+
marginBottom: "16px"
|
|
77
|
+
};
|
|
78
|
+
const labelStyle = {
|
|
79
|
+
display: "block",
|
|
80
|
+
fontSize: "14px",
|
|
81
|
+
fontWeight: 500,
|
|
82
|
+
color: "#374151",
|
|
83
|
+
marginBottom: "4px"
|
|
84
|
+
};
|
|
85
|
+
const inputStyle = {
|
|
86
|
+
width: "100%",
|
|
87
|
+
padding: "8px 12px",
|
|
88
|
+
border: "1px solid #d1d5db",
|
|
89
|
+
borderRadius: "8px",
|
|
90
|
+
fontSize: "14px",
|
|
91
|
+
boxSizing: "border-box",
|
|
92
|
+
outline: "none",
|
|
93
|
+
fontFamily: "inherit",
|
|
94
|
+
marginBottom: "16px"
|
|
95
|
+
};
|
|
96
|
+
const submitBtnStyle = {
|
|
97
|
+
width: "100%",
|
|
98
|
+
backgroundColor: primaryColor,
|
|
99
|
+
color: "white",
|
|
100
|
+
fontWeight: 500,
|
|
101
|
+
padding: "8px 16px",
|
|
102
|
+
borderRadius: "8px",
|
|
103
|
+
border: "none",
|
|
104
|
+
cursor: status === "loading" ? "not-allowed" : "pointer",
|
|
105
|
+
fontSize: "16px",
|
|
106
|
+
opacity: status === "loading" ? 0.7 : 1
|
|
107
|
+
};
|
|
108
|
+
const statusMsgStyle = {
|
|
109
|
+
fontSize: "14px",
|
|
110
|
+
marginTop: "12px",
|
|
111
|
+
textAlign: "center",
|
|
112
|
+
color: status === "success" ? "#10b981" : "#ef4444",
|
|
113
|
+
display: status === "success" || status === "error" ? "block" : "none"
|
|
114
|
+
};
|
|
115
|
+
return /* @__PURE__ */ jsxs("div", { style: containerStyle, children: [
|
|
116
|
+
/* @__PURE__ */ jsxs("div", { style: modalStyle, children: [
|
|
117
|
+
/* @__PURE__ */ jsxs("div", { style: headerStyle, children: [
|
|
118
|
+
/* @__PURE__ */ jsx("h3", { style: { margin: 0, fontSize: "18px", fontWeight: 600, color: "#1f2937" }, children: "Submit Feature Request" }),
|
|
119
|
+
/* @__PURE__ */ jsx(
|
|
120
|
+
"button",
|
|
121
|
+
{
|
|
122
|
+
onClick: () => setIsOpen(false),
|
|
123
|
+
style: { background: "none", border: "none", cursor: "pointer", color: "#9ca3af" },
|
|
124
|
+
"aria-label": "Close",
|
|
125
|
+
type: "button",
|
|
126
|
+
children: /* @__PURE__ */ jsx("svg", { width: "20", height: "20", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
] }),
|
|
130
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
|
|
131
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
132
|
+
/* @__PURE__ */ jsx("label", { style: labelStyle, children: "Feature Title" }),
|
|
133
|
+
/* @__PURE__ */ jsx(
|
|
134
|
+
"input",
|
|
135
|
+
{
|
|
136
|
+
type: "text",
|
|
137
|
+
required: true,
|
|
138
|
+
value: title,
|
|
139
|
+
onChange: (e) => setTitle(e.target.value),
|
|
140
|
+
placeholder: "Enter feature title",
|
|
141
|
+
style: inputStyle
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
] }),
|
|
145
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
146
|
+
/* @__PURE__ */ jsx("label", { style: labelStyle, children: "Description" }),
|
|
147
|
+
/* @__PURE__ */ jsx(
|
|
148
|
+
"textarea",
|
|
149
|
+
{
|
|
150
|
+
required: true,
|
|
151
|
+
rows: 3,
|
|
152
|
+
value: description,
|
|
153
|
+
onChange: (e) => setDescription(e.target.value),
|
|
154
|
+
placeholder: "Describe the feature you'd like to see...",
|
|
155
|
+
style: { ...inputStyle, resize: "none" }
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
] }),
|
|
159
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
160
|
+
/* @__PURE__ */ jsx("label", { style: labelStyle, children: "Enter your Email ID" }),
|
|
161
|
+
/* @__PURE__ */ jsx(
|
|
162
|
+
"input",
|
|
163
|
+
{
|
|
164
|
+
type: "email",
|
|
165
|
+
required: true,
|
|
166
|
+
value: email,
|
|
167
|
+
onChange: (e) => setEmail(e.target.value),
|
|
168
|
+
placeholder: "Please enter your email ID",
|
|
169
|
+
style: inputStyle
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
] }),
|
|
173
|
+
/* @__PURE__ */ jsx("button", { type: "submit", disabled: status === "loading", style: submitBtnStyle, children: status === "loading" ? "Submitting..." : "Submit Request" }),
|
|
174
|
+
/* @__PURE__ */ jsx("div", { style: statusMsgStyle, children: status === "success" ? "Thank you for your feedback!" : "Something went wrong. Please try again." })
|
|
175
|
+
] })
|
|
176
|
+
] }),
|
|
177
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setIsOpen(!isOpen), style: buttonStyle, children: isOpen ? /* @__PURE__ */ jsx("svg", { width: "24", height: "24", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
178
|
+
/* @__PURE__ */ jsx("svg", { width: "24", height: "24", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" }) }),
|
|
179
|
+
/* @__PURE__ */ jsx("span", { style: { display: "inline" }, children: "Request Feature" })
|
|
180
|
+
] }) })
|
|
181
|
+
] });
|
|
182
|
+
}
|
|
183
|
+
export {
|
|
184
|
+
FeedbackWidget
|
|
185
|
+
};
|
|
186
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["import React, { useState, CSSProperties, FormEvent } from \"react\";\n\nexport interface FeedbackWidgetProps {\n /**\n * The URL of your API endpoint to send the feedback data to.\n * Example: https://your-domain.com/api/feedback\n */\n apiUrl: string;\n /**\n * Optional primary color for the button and accents. \n * Defaults to a pleasant blue (#3b82f6)\n */\n primaryColor?: string;\n}\n\nexport function FeedbackWidget({ apiUrl, primaryColor = \"#3b82f6\" }: FeedbackWidgetProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [title, setTitle] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [email, setEmail] = useState(\"\");\n \n const [status, setStatus] = useState<\"idle\" | \"loading\" | \"success\" | \"error\">(\"idle\");\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n if (!title.trim() || !description.trim() || !email.trim()) return;\n\n setStatus(\"loading\");\n\n try {\n const response = await fetch(apiUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ title, description, email }),\n });\n\n if (response.ok) {\n setStatus(\"success\");\n setTitle(\"\");\n setDescription(\"\");\n // Auto-close after a success\n setTimeout(() => {\n setIsOpen(false);\n setStatus(\"idle\");\n }, 2000);\n } else {\n setStatus(\"error\");\n }\n } catch (error) {\n console.error(\"Feedback Widget Error:\", error);\n setStatus(\"error\");\n }\n };\n\n // --- Styles ---\n const containerStyle: CSSProperties = {\n position: \"fixed\",\n bottom: \"24px\",\n right: \"24px\",\n zIndex: 999999,\n fontFamily: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\",\n };\n\n const buttonStyle: CSSProperties = {\n backgroundColor: primaryColor,\n color: \"white\",\n border: \"none\",\n borderRadius: \"9999px\",\n padding: \"16px\",\n boxShadow: \"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)\",\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n gap: \"8px\",\n fontWeight: 500,\n fontSize: \"16px\",\n transition: \"transform 0.2s ease-in-out\",\n };\n\n const modalStyle: CSSProperties = {\n position: \"absolute\",\n bottom: \"80px\",\n right: \"0\",\n width: \"384px\", // w-96 roughly\n backgroundColor: \"white\",\n borderRadius: \"8px\",\n boxShadow: \"0 25px 50px -12px rgba(0, 0, 0, 0.25)\",\n border: \"1px solid #e5e7eb\",\n padding: \"24px\",\n boxSizing: \"border-box\",\n display: isOpen ? \"block\" : \"none\",\n };\n\n const headerStyle: CSSProperties = {\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: \"16px\",\n };\n\n const labelStyle: CSSProperties = {\n display: \"block\",\n fontSize: \"14px\",\n fontWeight: 500,\n color: \"#374151\",\n marginBottom: \"4px\",\n };\n\n const inputStyle: CSSProperties = {\n width: \"100%\",\n padding: \"8px 12px\",\n border: \"1px solid #d1d5db\",\n borderRadius: \"8px\",\n fontSize: \"14px\",\n boxSizing: \"border-box\",\n outline: \"none\",\n fontFamily: \"inherit\",\n marginBottom: \"16px\",\n };\n\n const submitBtnStyle: CSSProperties = {\n width: \"100%\",\n backgroundColor: primaryColor,\n color: \"white\",\n fontWeight: 500,\n padding: \"8px 16px\",\n borderRadius: \"8px\",\n border: \"none\",\n cursor: status === \"loading\" ? \"not-allowed\" : \"pointer\",\n fontSize: \"16px\",\n opacity: status === \"loading\" ? 0.7 : 1,\n };\n\n const statusMsgStyle: CSSProperties = {\n fontSize: \"14px\",\n marginTop: \"12px\",\n textAlign: \"center\",\n color: status === \"success\" ? \"#10b981\" : \"#ef4444\",\n display: status === \"success\" || status === \"error\" ? \"block\" : \"none\",\n };\n\n return (\n <div style={containerStyle}>\n <div style={modalStyle}>\n <div style={headerStyle}>\n <h3 style={{ margin: 0, fontSize: \"18px\", fontWeight: 600, color: \"#1f2937\" }}>\n Submit Feature Request\n </h3>\n <button\n onClick={() => setIsOpen(false)}\n style={{ background: \"none\", border: \"none\", cursor: \"pointer\", color: \"#9ca3af\" }}\n aria-label=\"Close\"\n type=\"button\"\n >\n <svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <form onSubmit={handleSubmit}>\n <div>\n <label style={labelStyle}>Feature Title</label>\n <input\n type=\"text\"\n required\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"Enter feature title\"\n style={inputStyle}\n />\n </div>\n\n <div>\n <label style={labelStyle}>Description</label>\n <textarea\n required\n rows={3}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Describe the feature you'd like to see...\"\n style={{ ...inputStyle, resize: \"none\" }}\n />\n </div>\n\n <div>\n <label style={labelStyle}>Enter your Email ID</label>\n <input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"Please enter your email ID\"\n style={inputStyle}\n />\n </div>\n\n <button type=\"submit\" disabled={status === \"loading\"} style={submitBtnStyle}>\n {status === \"loading\" ? \"Submitting...\" : \"Submit Request\"}\n </button>\n\n <div style={statusMsgStyle}>\n {status === \"success\" ? \"Thank you for your feedback!\" : \"Something went wrong. Please try again.\"}\n </div>\n </form>\n </div>\n\n <button onClick={() => setIsOpen(!isOpen)} style={buttonStyle}>\n {isOpen ? (\n <svg width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n ) : (\n <>\n <svg width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 4v16m8-8H4\" />\n </svg>\n <span style={{ display: \"inline\" }}>Request Feature</span>\n </>\n )}\n </button>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pa-floating-window",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A plug-and-play React floating window widget for feature requests and feedback.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"react": ">=16.8.0",
|
|
24
|
+
"react-dom": ">=16.8.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/react": "^18.2.0",
|
|
28
|
+
"@types/react-dom": "^18.2.0",
|
|
29
|
+
"react": "^18.2.0",
|
|
30
|
+
"react-dom": "^18.2.0",
|
|
31
|
+
"tsup": "^8.0.2",
|
|
32
|
+
"typescript": "^5.3.3"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"react",
|
|
36
|
+
"feedback",
|
|
37
|
+
"widget",
|
|
38
|
+
"floating",
|
|
39
|
+
"window"
|
|
40
|
+
],
|
|
41
|
+
"author": "HackByte",
|
|
42
|
+
"license": "MIT"
|
|
43
|
+
}
|