@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/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: 15px;
198
- padding-bottom: 10px;
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: 16px 24px;
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: 100px;
331
- padding: 12px;
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: 4px;
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: 8px;
357
- margin-bottom: 16px;
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: 8px;
494
- margin-bottom: 12px;
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: 5vh !important;
597
- max-height: 70vh !important;
622
+ top: 4vh !important;
623
+ max-height: 92vh !important;
598
624
  }
599
- .fb-cnt { padding: 20px !important; max-height: calc(100vh - 80px) !important; }
600
- .fb-ta { font-size: 16px !important; min-height: 80px !important; }
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: 14px 20px !important; font-size: 16px !important; }
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(submission);
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 screenshotBlockHtml = showScreenshotOption ? (() => {
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
- const extrasHtml = screenshotError || screenshots.length > 0 || atMax ? `<div class="fb-up-extras">${errorHtml}${screenshots.length > 0 ? `<div class="fb-ss">${previewsHtml}</div>` : ""}${limitHtml}</div>` : "";
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-charcount${lowChars ? " fb-charcount--low" : ""}" data-role="charcount" style="color:${lowChars ? "#dc2626" : theme.text};opacity:${lowChars ? 1 : 0.6};">${remaining} chars remaining</div>
927
- ${screenshotBlockHtml}
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;