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