@usero/sdk 0.2.0 → 0.3.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 +86 -0
- package/dist/all-M6KEAHE5.cjs +9110 -0
- package/dist/all-M6KEAHE5.cjs.map +1 -0
- package/dist/all-T4CCPHSL.js +9095 -0
- package/dist/all-T4CCPHSL.js.map +1 -0
- package/dist/chunk-5BLDMQED.cjs +18 -0
- package/dist/chunk-5BLDMQED.cjs.map +1 -0
- package/dist/chunk-NSBPE2FW.js +15 -0
- package/dist/chunk-NSBPE2FW.js.map +1 -0
- package/dist/plugins/session-replay.cjs +180 -0
- package/dist/plugins/session-replay.cjs.map +1 -0
- package/dist/plugins/session-replay.d.cts +75 -0
- package/dist/plugins/session-replay.d.ts +75 -0
- package/dist/plugins/session-replay.js +177 -0
- package/dist/plugins/session-replay.js.map +1 -0
- package/dist/react.cjs +138 -35
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +23 -0
- package/dist/react.d.ts +23 -0
- package/dist/react.js +138 -35
- package/dist/react.js.map +1 -1
- package/dist/usero.iife.js +59 -52
- package/dist/usero.iife.js.map +1 -1
- package/dist/vanilla.cjs +139 -35
- package/dist/vanilla.cjs.map +1 -1
- package/dist/vanilla.d.cts +25 -1
- package/dist/vanilla.d.ts +25 -1
- package/dist/vanilla.js +139 -36
- package/dist/vanilla.js.map +1 -1
- package/package.json +9 -1
package/dist/react.js
CHANGED
|
@@ -159,6 +159,25 @@ function getGradientEnd(color) {
|
|
|
159
159
|
return `#${[shiftedR, shiftedG, shiftedB].map((x) => x.toString(16).padStart(2, "0")).join("")}`;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
// src/plugin.ts
|
|
163
|
+
function createPluginLogger(name) {
|
|
164
|
+
const prefix = `[usero:${name}]`;
|
|
165
|
+
return {
|
|
166
|
+
debug: (...args) => {
|
|
167
|
+
if (typeof console !== "undefined") console.debug(prefix, ...args);
|
|
168
|
+
},
|
|
169
|
+
info: (...args) => {
|
|
170
|
+
if (typeof console !== "undefined") console.info(prefix, ...args);
|
|
171
|
+
},
|
|
172
|
+
warn: (...args) => {
|
|
173
|
+
if (typeof console !== "undefined") console.warn(prefix, ...args);
|
|
174
|
+
},
|
|
175
|
+
error: (...args) => {
|
|
176
|
+
if (typeof console !== "undefined") console.error(prefix, ...args);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
162
181
|
// src/validation.ts
|
|
163
182
|
function validateFeedbackSubmission(data) {
|
|
164
183
|
const errors = [];
|
|
@@ -194,8 +213,8 @@ var FEEDBACK_CSS = `
|
|
|
194
213
|
.fb-es {
|
|
195
214
|
display: flex;
|
|
196
215
|
justify-content: center;
|
|
197
|
-
gap:
|
|
198
|
-
padding-bottom:
|
|
216
|
+
gap: 12px;
|
|
217
|
+
padding-bottom: 8px;
|
|
199
218
|
}
|
|
200
219
|
|
|
201
220
|
.fb-ec {
|
|
@@ -286,7 +305,7 @@ var FEEDBACK_CSS = `
|
|
|
286
305
|
|
|
287
306
|
.fb-sub {
|
|
288
307
|
width: 100%;
|
|
289
|
-
padding:
|
|
308
|
+
padding: 12px 24px;
|
|
290
309
|
border: none;
|
|
291
310
|
border-radius: 12px;
|
|
292
311
|
font-size: 15px;
|
|
@@ -314,7 +333,7 @@ var FEEDBACK_CSS = `
|
|
|
314
333
|
}
|
|
315
334
|
|
|
316
335
|
.fb-cnt {
|
|
317
|
-
padding: 24px;
|
|
336
|
+
padding: 20px 24px 16px;
|
|
318
337
|
overflow: auto;
|
|
319
338
|
max-height: calc(90vh - 48px);
|
|
320
339
|
}
|
|
@@ -327,22 +346,29 @@ var FEEDBACK_CSS = `
|
|
|
327
346
|
|
|
328
347
|
.fb-ta {
|
|
329
348
|
width: 100%;
|
|
330
|
-
min-height:
|
|
331
|
-
padding:
|
|
349
|
+
min-height: 80px;
|
|
350
|
+
padding: 10px;
|
|
332
351
|
border-radius: 8px;
|
|
333
352
|
font-size: 14px;
|
|
334
353
|
font-family: inherit;
|
|
335
354
|
outline: none;
|
|
336
355
|
resize: vertical;
|
|
337
356
|
transition: border-color 150ms ease;
|
|
338
|
-
margin-bottom:
|
|
357
|
+
margin-bottom: 2px;
|
|
339
358
|
box-sizing: border-box;
|
|
340
359
|
}
|
|
341
360
|
|
|
361
|
+
.fb-toolrow {
|
|
362
|
+
display: flex;
|
|
363
|
+
align-items: center;
|
|
364
|
+
justify-content: space-between;
|
|
365
|
+
gap: 12px;
|
|
366
|
+
margin-bottom: 8px;
|
|
367
|
+
}
|
|
368
|
+
|
|
342
369
|
.fb-charcount {
|
|
343
370
|
font-size: 12px;
|
|
344
371
|
margin-left: auto;
|
|
345
|
-
margin-bottom: 8px;
|
|
346
372
|
text-align: right;
|
|
347
373
|
}
|
|
348
374
|
|
|
@@ -353,8 +379,8 @@ var FEEDBACK_CSS = `
|
|
|
353
379
|
.fb-email {
|
|
354
380
|
display: flex;
|
|
355
381
|
flex-direction: column;
|
|
356
|
-
gap:
|
|
357
|
-
margin-bottom:
|
|
382
|
+
gap: 6px;
|
|
383
|
+
margin-bottom: 10px;
|
|
358
384
|
}
|
|
359
385
|
|
|
360
386
|
.fb-email-lbl {
|
|
@@ -490,8 +516,8 @@ var FEEDBACK_CSS = `
|
|
|
490
516
|
.fb-up {
|
|
491
517
|
display: flex;
|
|
492
518
|
flex-direction: column;
|
|
493
|
-
gap:
|
|
494
|
-
margin-bottom:
|
|
519
|
+
gap: 6px;
|
|
520
|
+
margin-bottom: 8px;
|
|
495
521
|
}
|
|
496
522
|
|
|
497
523
|
.fb-upb {
|
|
@@ -593,15 +619,15 @@ var FEEDBACK_CSS = `
|
|
|
593
619
|
.fb-pnl-base {
|
|
594
620
|
width: 100% !important;
|
|
595
621
|
max-width: none !important;
|
|
596
|
-
top:
|
|
597
|
-
max-height:
|
|
622
|
+
top: 4vh !important;
|
|
623
|
+
max-height: 92vh !important;
|
|
598
624
|
}
|
|
599
|
-
.fb-cnt { padding:
|
|
600
|
-
.fb-ta { font-size: 16px !important; min-height:
|
|
625
|
+
.fb-cnt { padding: 16px 18px 14px !important; max-height: calc(100vh - 40px) !important; }
|
|
626
|
+
.fb-ta { font-size: 16px !important; min-height: 64px !important; }
|
|
601
627
|
.fb-ttl { font-size: 18px !important; }
|
|
602
628
|
.fb-ei { font-size: 24px !important; }
|
|
603
629
|
.fb-el { font-size: 11px !important; }
|
|
604
|
-
.fb-sub { padding:
|
|
630
|
+
.fb-sub { padding: 12px 20px !important; font-size: 16px !important; }
|
|
605
631
|
}
|
|
606
632
|
`;
|
|
607
633
|
|
|
@@ -638,6 +664,21 @@ function escapeHtml(value) {
|
|
|
638
664
|
}
|
|
639
665
|
});
|
|
640
666
|
}
|
|
667
|
+
function mergePluginPatches(base, patches) {
|
|
668
|
+
let result = base;
|
|
669
|
+
for (const patch of patches) {
|
|
670
|
+
if (!patch || typeof patch !== "object") continue;
|
|
671
|
+
const { metadata: patchMetadata, ...rest } = patch;
|
|
672
|
+
result = { ...result, ...rest };
|
|
673
|
+
if (patchMetadata && typeof patchMetadata === "object") {
|
|
674
|
+
result.metadata = {
|
|
675
|
+
...result.metadata ?? {},
|
|
676
|
+
...patchMetadata
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return result;
|
|
681
|
+
}
|
|
641
682
|
function readStoredEmail() {
|
|
642
683
|
if (typeof window === "undefined") return "";
|
|
643
684
|
try {
|
|
@@ -662,7 +703,8 @@ function initUseroFeedbackWidget(props) {
|
|
|
662
703
|
close: () => {
|
|
663
704
|
},
|
|
664
705
|
update: () => {
|
|
665
|
-
}
|
|
706
|
+
},
|
|
707
|
+
whenReady: () => Promise.resolve()
|
|
666
708
|
};
|
|
667
709
|
}
|
|
668
710
|
const { clientId, baseUrl } = props;
|
|
@@ -677,7 +719,8 @@ function initUseroFeedbackWidget(props) {
|
|
|
677
719
|
close: () => {
|
|
678
720
|
},
|
|
679
721
|
update: () => {
|
|
680
|
-
}
|
|
722
|
+
},
|
|
723
|
+
whenReady: () => Promise.resolve()
|
|
681
724
|
};
|
|
682
725
|
}
|
|
683
726
|
let position = props.position ?? "right";
|
|
@@ -694,6 +737,34 @@ function initUseroFeedbackWidget(props) {
|
|
|
694
737
|
let onOpen = props.onOpen;
|
|
695
738
|
let onClose = props.onClose;
|
|
696
739
|
const apiClient = new FeedbackApiClient(baseUrl);
|
|
740
|
+
const pluginList = props.plugins ?? [];
|
|
741
|
+
const pluginStores = /* @__PURE__ */ new Map();
|
|
742
|
+
const pluginContexts = /* @__PURE__ */ new Map();
|
|
743
|
+
const initPromises = [];
|
|
744
|
+
for (const plugin of pluginList) {
|
|
745
|
+
const ctx = {
|
|
746
|
+
clientId,
|
|
747
|
+
baseUrl: baseUrl ?? DEFAULT_API_URL,
|
|
748
|
+
logger: createPluginLogger(plugin.name),
|
|
749
|
+
getStore: () => pluginStores.get(plugin.name),
|
|
750
|
+
setStore: (value) => {
|
|
751
|
+
pluginStores.set(plugin.name, value);
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
pluginContexts.set(plugin.name, ctx);
|
|
755
|
+
if (plugin.onInit) {
|
|
756
|
+
const settled = (async () => {
|
|
757
|
+
try {
|
|
758
|
+
await plugin.onInit?.(ctx);
|
|
759
|
+
} catch (err) {
|
|
760
|
+
ctx.logger.error("onInit threw", err);
|
|
761
|
+
}
|
|
762
|
+
})();
|
|
763
|
+
initPromises.push(settled);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
const readyPromise = initPromises.length === 0 ? Promise.resolve() : Promise.all(initPromises).then(() => {
|
|
767
|
+
});
|
|
697
768
|
let isOpen = false;
|
|
698
769
|
let selectedRating = void 0;
|
|
699
770
|
let comment = "";
|
|
@@ -812,8 +883,24 @@ function initUseroFeedbackWidget(props) {
|
|
|
812
883
|
setSubmitMessage({ type: "error", text: validation.errors.join(", ") });
|
|
813
884
|
return;
|
|
814
885
|
}
|
|
886
|
+
let enrichedSubmission = submission;
|
|
887
|
+
if (pluginList.length > 0) {
|
|
888
|
+
const patchPromises = pluginList.map(async (plugin) => {
|
|
889
|
+
if (!plugin.onFeedbackSubmit) return void 0;
|
|
890
|
+
const ctx = pluginContexts.get(plugin.name);
|
|
891
|
+
if (!ctx) return void 0;
|
|
892
|
+
try {
|
|
893
|
+
return await plugin.onFeedbackSubmit(ctx, submission);
|
|
894
|
+
} catch (err) {
|
|
895
|
+
ctx.logger.error("onFeedbackSubmit threw", err);
|
|
896
|
+
return void 0;
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
const patches = await Promise.all(patchPromises);
|
|
900
|
+
enrichedSubmission = mergePluginPatches(submission, patches);
|
|
901
|
+
}
|
|
815
902
|
try {
|
|
816
|
-
const response = await apiClient.submitFeedback(
|
|
903
|
+
const response = await apiClient.submitFeedback(enrichedSubmission);
|
|
817
904
|
if (response.success) {
|
|
818
905
|
if (shareEmail && userEmail) writeStoredEmail(userEmail);
|
|
819
906
|
onSubmit?.(feedbackData);
|
|
@@ -870,17 +957,26 @@ function initUseroFeedbackWidget(props) {
|
|
|
870
957
|
const cls = ["fb-ec", sel && "fb-ec--sel"].filter(Boolean).join(" ");
|
|
871
958
|
return `
|
|
872
959
|
<div class="${cls}" style="background:${bg}">
|
|
873
|
-
<button type="button" class="fb-eb" data-rating="${r}" role="radio" aria-checked="${sel}" aria-label="${r}: ${RATING_LABELS[r]}">
|
|
960
|
+
<button type="button" class="fb-eb" data-rating="${r}" role="radio" aria-checked="${sel}" aria-label="${r}: ${RATING_LABELS[r]}" style="color:${theme.text}">
|
|
874
961
|
<div class="fb-ei"><span role="img" aria-label="${RATING_LABELS[r]}">${EMOJI_MAP[r]}</span></div>
|
|
875
|
-
<div class="fb-el">${RATING_LABELS[r]}</div>
|
|
962
|
+
<div class="fb-el" style="color:${theme.text}">${RATING_LABELS[r]}</div>
|
|
876
963
|
</button>
|
|
877
964
|
</div>
|
|
878
965
|
`;
|
|
879
966
|
}).join("");
|
|
880
967
|
const messageHtml = submitMessage ? `<div class="fb-msg fb-msg--header ${submitMessage.type === "success" ? "fb-msg--ok" : "fb-msg--err"}">${submitMessage.type === "success" ? "\u2713" : "\u26A0"} ${escapeHtml(submitMessage.text)}</div>` : "";
|
|
881
|
-
const
|
|
968
|
+
const uploadBtnHtml = showScreenshotOption ? (() => {
|
|
882
969
|
const atMax = screenshots.length >= MAX_SCREENSHOTS;
|
|
883
970
|
const btnDisabled = isUploadingScreenshot || atMax;
|
|
971
|
+
return `
|
|
972
|
+
<input type="file" accept="image/*" data-role="screenshot-input" style="display:none;" aria-label="Choose screenshot" />
|
|
973
|
+
<button type="button" class="fb-upb ${btnDisabled ? "fb-upb--dis" : ""}" data-role="screenshot-pick" ${btnDisabled ? "disabled" : ""} style="border:1px solid ${theme.border};color:${theme.text};">
|
|
974
|
+
${isUploadingScreenshot ? '<span class="fb-ups"></span> Uploading...' : "\u{1F4F7} Add screenshot"}
|
|
975
|
+
</button>
|
|
976
|
+
`;
|
|
977
|
+
})() : "";
|
|
978
|
+
const uploadExtrasHtml = showScreenshotOption ? (() => {
|
|
979
|
+
const atMax = screenshots.length >= MAX_SCREENSHOTS;
|
|
884
980
|
const previewsHtml = screenshots.map(
|
|
885
981
|
(shot, i) => `
|
|
886
982
|
<div class="fb-sp">
|
|
@@ -891,16 +987,7 @@ function initUseroFeedbackWidget(props) {
|
|
|
891
987
|
).join("");
|
|
892
988
|
const errorHtml = screenshotError ? `<div class="fb-upe">\u26A0 ${escapeHtml(screenshotError)}</div>` : "";
|
|
893
989
|
const limitHtml = atMax ? `<div class="fb-sl">Max ${MAX_SCREENSHOTS}</div>` : "";
|
|
894
|
-
|
|
895
|
-
return `
|
|
896
|
-
<div class="fb-up">
|
|
897
|
-
<input type="file" accept="image/*" data-role="screenshot-input" style="display:none;" aria-label="Choose screenshot" />
|
|
898
|
-
<button type="button" class="fb-upb ${btnDisabled ? "fb-upb--dis" : ""}" data-role="screenshot-pick" ${btnDisabled ? "disabled" : ""} style="border:1px solid ${theme.border};color:${theme.text};">
|
|
899
|
-
${isUploadingScreenshot ? '<span class="fb-ups"></span> Uploading...' : "\u{1F4F7} Add screenshot"}
|
|
900
|
-
</button>
|
|
901
|
-
${extrasHtml}
|
|
902
|
-
</div>
|
|
903
|
-
`;
|
|
990
|
+
return screenshotError || screenshots.length > 0 || atMax ? `<div class="fb-up-extras">${errorHtml}${screenshots.length > 0 ? `<div class="fb-ss">${previewsHtml}</div>` : ""}${limitHtml}</div>` : "";
|
|
904
991
|
})() : "";
|
|
905
992
|
const emailBlockHtml = showEmailOption ? `
|
|
906
993
|
<div class="fb-email">
|
|
@@ -923,8 +1010,11 @@ function initUseroFeedbackWidget(props) {
|
|
|
923
1010
|
<form data-role="form">
|
|
924
1011
|
<div class="fb-es" role="radiogroup" aria-label="Rate experience">${ratingsHtml}</div>
|
|
925
1012
|
<textarea class="fb-ta" data-role="comment" placeholder="${escapeHtml(placeholder)}" aria-label="Comments" maxlength="1000" rows="2" style="border:1px solid ${theme.border};color:${theme.text};background-color:${theme.background};">${escapeHtml(comment)}</textarea>
|
|
926
|
-
<div class="fb-
|
|
927
|
-
|
|
1013
|
+
<div class="fb-toolrow">
|
|
1014
|
+
${uploadBtnHtml}
|
|
1015
|
+
<div class="fb-charcount${lowChars ? " fb-charcount--low" : ""}" data-role="charcount" style="color:${lowChars ? "#dc2626" : theme.text};opacity:${lowChars ? 1 : 0.6};">${remaining} chars remaining</div>
|
|
1016
|
+
</div>
|
|
1017
|
+
${uploadExtrasHtml ? `<div class="fb-up">${uploadExtrasHtml}</div>` : ""}
|
|
928
1018
|
${emailBlockHtml}
|
|
929
1019
|
<button class="fb-sub ${submitDisabled ? "fb-sub--dis" : ""}" type="submit" aria-label="Submit" ${submitDisabled ? "disabled" : ""} style="${submitStyle}">
|
|
930
1020
|
${isSubmitting ? '<span class="fb-spin"></span>' : ""}
|
|
@@ -1055,10 +1145,23 @@ function initUseroFeedbackWidget(props) {
|
|
|
1055
1145
|
destroyed = true;
|
|
1056
1146
|
document.removeEventListener("keydown", onKeyDown);
|
|
1057
1147
|
detachMqlListener();
|
|
1148
|
+
for (const plugin of pluginList) {
|
|
1149
|
+
if (!plugin.onDestroy) continue;
|
|
1150
|
+
const ctx = pluginContexts.get(plugin.name);
|
|
1151
|
+
if (!ctx) continue;
|
|
1152
|
+
try {
|
|
1153
|
+
plugin.onDestroy(ctx);
|
|
1154
|
+
} catch (err) {
|
|
1155
|
+
ctx.logger.error("onDestroy threw", err);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
pluginStores.clear();
|
|
1159
|
+
pluginContexts.clear();
|
|
1058
1160
|
host.remove();
|
|
1059
1161
|
},
|
|
1060
1162
|
open,
|
|
1061
1163
|
close,
|
|
1164
|
+
whenReady: () => readyPromise,
|
|
1062
1165
|
update: (next) => {
|
|
1063
1166
|
if (destroyed) return;
|
|
1064
1167
|
let needsRender = false;
|