@hitslop/slurp-sdk 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/event.d.ts +1 -1
- package/dist/event.js +1 -1
- package/dist/feedback.d.ts +2 -0
- package/dist/feedback.js +17 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,7 +111,7 @@ init({ key: 'slurp_pk_…', endpoint: 'https://slurp.YOUR-SUBDOMAIN.workers.dev/
|
|
|
111
111
|
mountFeedback({ askEmail: true, data: () => ({ plan: currentPlan() }) });
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
The event is `{ event: 'feedback', data: { message, rating?, email?, ...data() } }` with the usual page context, so in SQL: `SELECT timestamp, data->>'message', data->>'rating' FROM events WHERE event = 'feedback'`. Options: `client` (default: the client from `init`), `label`, `title`, `placeholder`, `ratings` (default true), `askEmail` (default false), `position` (`bottom-right` or `bottom-left`), `accent`, `theme` (`auto` follows the visitor's dark mode; `light` or `dark` match your page), `event`, `data`, `button: false` (open it from your own button with the returned `open()`), `container
|
|
114
|
+
The event is `{ event: 'feedback', data: { message, rating?, email?, ...data() } }` with the usual page context, so in SQL: `SELECT timestamp, data->>'message', data->>'rating' FROM events WHERE event = 'feedback'`. Options: `client` (default: the client from `init`), `label`, `title`, `placeholder`, `ratings` (default true), `askEmail` (default false), `position` (`bottom-right` or `bottom-left`), `accent`, `theme` (`auto` follows the visitor's dark mode; `light` or `dark` match your page), `event`, `data`, `button: false` (open it from your own button with the returned `open()`), `container`, `poweredBy` (default true: a small "Powered by Slurp" link under the form, opening slurp.run in a new tab without sending your site's address; `false` hides it). It returns `{ open, close, destroy }`. Text a person types is only ever set as text, never parsed as HTML, and the styles live in a shadow root so they neither affect nor inherit from your page. Browser only.
|
|
115
115
|
|
|
116
116
|
## Contributing
|
|
117
117
|
|
package/dist/event.d.ts
CHANGED
package/dist/event.js
CHANGED
package/dist/feedback.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface FeedbackOptions {
|
|
|
26
26
|
button?: boolean;
|
|
27
27
|
/** Where to attach the widget. Default `document.body`. */
|
|
28
28
|
container?: HTMLElement;
|
|
29
|
+
/** Show the small "Powered by Slurp" link under the form. Default true; set false to hide it. */
|
|
30
|
+
poweredBy?: boolean;
|
|
29
31
|
}
|
|
30
32
|
export interface FeedbackHandle {
|
|
31
33
|
open(): void;
|
package/dist/feedback.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { track as defaultTrack } from "./index.js";
|
|
2
2
|
const MAX_MESSAGE = 5000;
|
|
3
3
|
const HOST_ATTRIBUTE = 'data-slurp-feedback';
|
|
4
|
+
/** The "Powered by" link opens in a new tab without telling slurp.run which site it came from; the utm tag says only that it was this widget. */
|
|
5
|
+
const POWERED_BY_URL = 'https://slurp.run/?utm_source=feedback-widget';
|
|
4
6
|
const STYLE = `
|
|
5
7
|
:host { all: initial; }
|
|
6
8
|
* { box-sizing: border-box; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
|
|
@@ -22,17 +24,24 @@ textarea:focus-visible, input:focus-visible, button:focus-visible { outline: 2px
|
|
|
22
24
|
.row button { border-radius: 8px; padding: 8px 14px; font-size: 14px; font-weight: 600; cursor: pointer; border: 1px solid #c9ced6; background: #fff; color: #17191c; }
|
|
23
25
|
.row .send { background: var(--accent); border-color: var(--accent); color: #fff; }
|
|
24
26
|
.error { margin: 8px 0 0; font-size: 13px; color: #b3001f; }
|
|
27
|
+
.powered { margin: 12px 0 0; text-align: center; font-size: 11px; color: #8a919b; }
|
|
28
|
+
.powered a { color: inherit; text-decoration: none; }
|
|
29
|
+
.powered a:hover, .powered a:focus-visible { color: var(--accent); text-decoration: underline; }
|
|
25
30
|
.thanks { margin: 6px 0 2px; font-size: 15px; }
|
|
26
31
|
@media (prefers-color-scheme: dark) {
|
|
27
32
|
:host([data-theme=auto]) .panel { background: #1f2327; color: #f2f4f6; border-color: #3a4048; }
|
|
28
33
|
:host([data-theme=auto]) textarea, :host([data-theme=auto]) input[type=email] { background: #15181b; border-color: #3a4048; }
|
|
29
34
|
:host([data-theme=auto]) .rating button, :host([data-theme=auto]) .row button { background: #15181b; border-color: #3a4048; color: #f2f4f6; }
|
|
30
35
|
:host([data-theme=auto]) .rating legend { color: #9aa1aa; }
|
|
36
|
+
:host([data-theme=auto]) .row .send { background: var(--accent); border-color: var(--accent); color: #fff; }
|
|
37
|
+
:host([data-theme=auto]) .powered { color: #7d858f; }
|
|
31
38
|
}
|
|
32
39
|
:host([data-theme=dark]) .panel { background: #1f2327; color: #f2f4f6; border-color: #3a4048; }
|
|
33
40
|
:host([data-theme=dark]) textarea, :host([data-theme=dark]) input[type=email] { background: #15181b; border-color: #3a4048; }
|
|
34
41
|
:host([data-theme=dark]) .rating button, :host([data-theme=dark]) .row button { background: #15181b; border-color: #3a4048; color: #f2f4f6; }
|
|
35
42
|
:host([data-theme=dark]) .rating legend { color: #9aa1aa; }
|
|
43
|
+
:host([data-theme=dark]) .row .send { background: var(--accent); border-color: var(--accent); color: #fff; }
|
|
44
|
+
:host([data-theme=dark]) .powered { color: #7d858f; }
|
|
36
45
|
`;
|
|
37
46
|
function element(tag, attributes = {}, text) {
|
|
38
47
|
const node = document.createElement(tag);
|
|
@@ -50,7 +59,7 @@ function element(tag, attributes = {}, text) {
|
|
|
50
59
|
export function mountFeedback(options = {}) {
|
|
51
60
|
if (typeof document === 'undefined')
|
|
52
61
|
throw new Error('mountFeedback needs a browser document');
|
|
53
|
-
const { label = 'Feedback', title = 'Send feedback', placeholder = 'What is working, and what is not?', ratings = true, askEmail = false, position = 'bottom-right', accent = '#e8002a', event = 'feedback', button = true, theme = 'auto', } = options;
|
|
62
|
+
const { label = 'Feedback', title = 'Send feedback', placeholder = 'What is working, and what is not?', ratings = true, askEmail = false, position = 'bottom-right', accent = '#e8002a', event = 'feedback', button = true, theme = 'auto', poweredBy = true, } = options;
|
|
54
63
|
const container = options.container ?? document.body;
|
|
55
64
|
for (const old of container.querySelectorAll(`[${HOST_ATTRIBUTE}]`))
|
|
56
65
|
old.remove(); // mounting twice replaces, never stacks
|
|
@@ -88,6 +97,12 @@ export function mountFeedback(options = {}) {
|
|
|
88
97
|
row.append(cancel, send);
|
|
89
98
|
form.append(...[ratingGroup, message, email, error, row].filter((node) => node !== undefined));
|
|
90
99
|
panel.append(heading, form);
|
|
100
|
+
let credit;
|
|
101
|
+
if (poweredBy) {
|
|
102
|
+
credit = element('p', { class: 'powered' });
|
|
103
|
+
credit.append('Powered by ', element('a', { href: POWERED_BY_URL, target: '_blank', rel: 'noopener noreferrer' }, 'Slurp'));
|
|
104
|
+
panel.append(credit);
|
|
105
|
+
}
|
|
91
106
|
root.append(...(button ? [fab, panel] : [panel]));
|
|
92
107
|
let timer;
|
|
93
108
|
let thanks;
|
|
@@ -158,7 +173,7 @@ export function mountFeedback(options = {}) {
|
|
|
158
173
|
}
|
|
159
174
|
form.setAttribute('hidden', '');
|
|
160
175
|
thanks = element('p', { class: 'thanks', role: 'status' }, 'Thank you! We read every message.');
|
|
161
|
-
panel.
|
|
176
|
+
panel.insertBefore(thanks, credit ?? null); // the thank-you sits above the credit line
|
|
162
177
|
timer = setTimeout(() => { show(false); reset(); }, 2500);
|
|
163
178
|
});
|
|
164
179
|
container.appendChild(host);
|
package/package.json
CHANGED