@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/vanilla.js CHANGED
@@ -155,6 +155,25 @@ function getGradientEnd(color) {
155
155
  return `#${[shiftedR, shiftedG, shiftedB].map((x) => x.toString(16).padStart(2, "0")).join("")}`;
156
156
  }
157
157
 
158
+ // src/plugin.ts
159
+ function createPluginLogger(name) {
160
+ const prefix = `[usero:${name}]`;
161
+ return {
162
+ debug: (...args) => {
163
+ if (typeof console !== "undefined") console.debug(prefix, ...args);
164
+ },
165
+ info: (...args) => {
166
+ if (typeof console !== "undefined") console.info(prefix, ...args);
167
+ },
168
+ warn: (...args) => {
169
+ if (typeof console !== "undefined") console.warn(prefix, ...args);
170
+ },
171
+ error: (...args) => {
172
+ if (typeof console !== "undefined") console.error(prefix, ...args);
173
+ }
174
+ };
175
+ }
176
+
158
177
  // src/validation.ts
159
178
  function validateFeedbackSubmission(data) {
160
179
  const errors = [];
@@ -190,8 +209,8 @@ var FEEDBACK_CSS = `
190
209
  .fb-es {
191
210
  display: flex;
192
211
  justify-content: center;
193
- gap: 15px;
194
- padding-bottom: 10px;
212
+ gap: 12px;
213
+ padding-bottom: 8px;
195
214
  }
196
215
 
197
216
  .fb-ec {
@@ -282,7 +301,7 @@ var FEEDBACK_CSS = `
282
301
 
283
302
  .fb-sub {
284
303
  width: 100%;
285
- padding: 16px 24px;
304
+ padding: 12px 24px;
286
305
  border: none;
287
306
  border-radius: 12px;
288
307
  font-size: 15px;
@@ -310,7 +329,7 @@ var FEEDBACK_CSS = `
310
329
  }
311
330
 
312
331
  .fb-cnt {
313
- padding: 24px;
332
+ padding: 20px 24px 16px;
314
333
  overflow: auto;
315
334
  max-height: calc(90vh - 48px);
316
335
  }
@@ -323,22 +342,29 @@ var FEEDBACK_CSS = `
323
342
 
324
343
  .fb-ta {
325
344
  width: 100%;
326
- min-height: 100px;
327
- padding: 12px;
345
+ min-height: 80px;
346
+ padding: 10px;
328
347
  border-radius: 8px;
329
348
  font-size: 14px;
330
349
  font-family: inherit;
331
350
  outline: none;
332
351
  resize: vertical;
333
352
  transition: border-color 150ms ease;
334
- margin-bottom: 4px;
353
+ margin-bottom: 2px;
335
354
  box-sizing: border-box;
336
355
  }
337
356
 
357
+ .fb-toolrow {
358
+ display: flex;
359
+ align-items: center;
360
+ justify-content: space-between;
361
+ gap: 12px;
362
+ margin-bottom: 8px;
363
+ }
364
+
338
365
  .fb-charcount {
339
366
  font-size: 12px;
340
367
  margin-left: auto;
341
- margin-bottom: 8px;
342
368
  text-align: right;
343
369
  }
344
370
 
@@ -349,8 +375,8 @@ var FEEDBACK_CSS = `
349
375
  .fb-email {
350
376
  display: flex;
351
377
  flex-direction: column;
352
- gap: 8px;
353
- margin-bottom: 16px;
378
+ gap: 6px;
379
+ margin-bottom: 10px;
354
380
  }
355
381
 
356
382
  .fb-email-lbl {
@@ -486,8 +512,8 @@ var FEEDBACK_CSS = `
486
512
  .fb-up {
487
513
  display: flex;
488
514
  flex-direction: column;
489
- gap: 8px;
490
- margin-bottom: 12px;
515
+ gap: 6px;
516
+ margin-bottom: 8px;
491
517
  }
492
518
 
493
519
  .fb-upb {
@@ -589,15 +615,15 @@ var FEEDBACK_CSS = `
589
615
  .fb-pnl-base {
590
616
  width: 100% !important;
591
617
  max-width: none !important;
592
- top: 5vh !important;
593
- max-height: 70vh !important;
618
+ top: 4vh !important;
619
+ max-height: 92vh !important;
594
620
  }
595
- .fb-cnt { padding: 20px !important; max-height: calc(100vh - 80px) !important; }
596
- .fb-ta { font-size: 16px !important; min-height: 80px !important; }
621
+ .fb-cnt { padding: 16px 18px 14px !important; max-height: calc(100vh - 40px) !important; }
622
+ .fb-ta { font-size: 16px !important; min-height: 64px !important; }
597
623
  .fb-ttl { font-size: 18px !important; }
598
624
  .fb-ei { font-size: 24px !important; }
599
625
  .fb-el { font-size: 11px !important; }
600
- .fb-sub { padding: 14px 20px !important; font-size: 16px !important; }
626
+ .fb-sub { padding: 12px 20px !important; font-size: 16px !important; }
601
627
  }
602
628
  `;
603
629
 
@@ -634,6 +660,21 @@ function escapeHtml(value) {
634
660
  }
635
661
  });
636
662
  }
663
+ function mergePluginPatches(base, patches) {
664
+ let result = base;
665
+ for (const patch of patches) {
666
+ if (!patch || typeof patch !== "object") continue;
667
+ const { metadata: patchMetadata, ...rest } = patch;
668
+ result = { ...result, ...rest };
669
+ if (patchMetadata && typeof patchMetadata === "object") {
670
+ result.metadata = {
671
+ ...result.metadata ?? {},
672
+ ...patchMetadata
673
+ };
674
+ }
675
+ }
676
+ return result;
677
+ }
637
678
  function readStoredEmail() {
638
679
  if (typeof window === "undefined") return "";
639
680
  try {
@@ -658,7 +699,8 @@ function initUseroFeedbackWidget(props) {
658
699
  close: () => {
659
700
  },
660
701
  update: () => {
661
- }
702
+ },
703
+ whenReady: () => Promise.resolve()
662
704
  };
663
705
  }
664
706
  const { clientId, baseUrl } = props;
@@ -673,7 +715,8 @@ function initUseroFeedbackWidget(props) {
673
715
  close: () => {
674
716
  },
675
717
  update: () => {
676
- }
718
+ },
719
+ whenReady: () => Promise.resolve()
677
720
  };
678
721
  }
679
722
  let position = props.position ?? "right";
@@ -690,6 +733,34 @@ function initUseroFeedbackWidget(props) {
690
733
  let onOpen = props.onOpen;
691
734
  let onClose = props.onClose;
692
735
  const apiClient = new FeedbackApiClient(baseUrl);
736
+ const pluginList = props.plugins ?? [];
737
+ const pluginStores = /* @__PURE__ */ new Map();
738
+ const pluginContexts = /* @__PURE__ */ new Map();
739
+ const initPromises = [];
740
+ for (const plugin of pluginList) {
741
+ const ctx = {
742
+ clientId,
743
+ baseUrl: baseUrl ?? DEFAULT_API_URL,
744
+ logger: createPluginLogger(plugin.name),
745
+ getStore: () => pluginStores.get(plugin.name),
746
+ setStore: (value) => {
747
+ pluginStores.set(plugin.name, value);
748
+ }
749
+ };
750
+ pluginContexts.set(plugin.name, ctx);
751
+ if (plugin.onInit) {
752
+ const settled = (async () => {
753
+ try {
754
+ await plugin.onInit?.(ctx);
755
+ } catch (err) {
756
+ ctx.logger.error("onInit threw", err);
757
+ }
758
+ })();
759
+ initPromises.push(settled);
760
+ }
761
+ }
762
+ const readyPromise = initPromises.length === 0 ? Promise.resolve() : Promise.all(initPromises).then(() => {
763
+ });
693
764
  let isOpen = false;
694
765
  let selectedRating = void 0;
695
766
  let comment = "";
@@ -808,8 +879,24 @@ function initUseroFeedbackWidget(props) {
808
879
  setSubmitMessage({ type: "error", text: validation.errors.join(", ") });
809
880
  return;
810
881
  }
882
+ let enrichedSubmission = submission;
883
+ if (pluginList.length > 0) {
884
+ const patchPromises = pluginList.map(async (plugin) => {
885
+ if (!plugin.onFeedbackSubmit) return void 0;
886
+ const ctx = pluginContexts.get(plugin.name);
887
+ if (!ctx) return void 0;
888
+ try {
889
+ return await plugin.onFeedbackSubmit(ctx, submission);
890
+ } catch (err) {
891
+ ctx.logger.error("onFeedbackSubmit threw", err);
892
+ return void 0;
893
+ }
894
+ });
895
+ const patches = await Promise.all(patchPromises);
896
+ enrichedSubmission = mergePluginPatches(submission, patches);
897
+ }
811
898
  try {
812
- const response = await apiClient.submitFeedback(submission);
899
+ const response = await apiClient.submitFeedback(enrichedSubmission);
813
900
  if (response.success) {
814
901
  if (shareEmail && userEmail) writeStoredEmail(userEmail);
815
902
  onSubmit?.(feedbackData);
@@ -866,17 +953,26 @@ function initUseroFeedbackWidget(props) {
866
953
  const cls = ["fb-ec", sel && "fb-ec--sel"].filter(Boolean).join(" ");
867
954
  return `
868
955
  <div class="${cls}" style="background:${bg}">
869
- <button type="button" class="fb-eb" data-rating="${r}" role="radio" aria-checked="${sel}" aria-label="${r}: ${RATING_LABELS[r]}">
956
+ <button type="button" class="fb-eb" data-rating="${r}" role="radio" aria-checked="${sel}" aria-label="${r}: ${RATING_LABELS[r]}" style="color:${theme.text}">
870
957
  <div class="fb-ei"><span role="img" aria-label="${RATING_LABELS[r]}">${EMOJI_MAP[r]}</span></div>
871
- <div class="fb-el">${RATING_LABELS[r]}</div>
958
+ <div class="fb-el" style="color:${theme.text}">${RATING_LABELS[r]}</div>
872
959
  </button>
873
960
  </div>
874
961
  `;
875
962
  }).join("");
876
963
  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>` : "";
877
- const screenshotBlockHtml = showScreenshotOption ? (() => {
964
+ const uploadBtnHtml = showScreenshotOption ? (() => {
878
965
  const atMax = screenshots.length >= MAX_SCREENSHOTS;
879
966
  const btnDisabled = isUploadingScreenshot || atMax;
967
+ return `
968
+ <input type="file" accept="image/*" data-role="screenshot-input" style="display:none;" aria-label="Choose screenshot" />
969
+ <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};">
970
+ ${isUploadingScreenshot ? '<span class="fb-ups"></span> Uploading...' : "\u{1F4F7} Add screenshot"}
971
+ </button>
972
+ `;
973
+ })() : "";
974
+ const uploadExtrasHtml = showScreenshotOption ? (() => {
975
+ const atMax = screenshots.length >= MAX_SCREENSHOTS;
880
976
  const previewsHtml = screenshots.map(
881
977
  (shot, i) => `
882
978
  <div class="fb-sp">
@@ -887,16 +983,7 @@ function initUseroFeedbackWidget(props) {
887
983
  ).join("");
888
984
  const errorHtml = screenshotError ? `<div class="fb-upe">\u26A0 ${escapeHtml(screenshotError)}</div>` : "";
889
985
  const limitHtml = atMax ? `<div class="fb-sl">Max ${MAX_SCREENSHOTS}</div>` : "";
890
- const extrasHtml = screenshotError || screenshots.length > 0 || atMax ? `<div class="fb-up-extras">${errorHtml}${screenshots.length > 0 ? `<div class="fb-ss">${previewsHtml}</div>` : ""}${limitHtml}</div>` : "";
891
- return `
892
- <div class="fb-up">
893
- <input type="file" accept="image/*" data-role="screenshot-input" style="display:none;" aria-label="Choose screenshot" />
894
- <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};">
895
- ${isUploadingScreenshot ? '<span class="fb-ups"></span> Uploading...' : "\u{1F4F7} Add screenshot"}
896
- </button>
897
- ${extrasHtml}
898
- </div>
899
- `;
986
+ return screenshotError || screenshots.length > 0 || atMax ? `<div class="fb-up-extras">${errorHtml}${screenshots.length > 0 ? `<div class="fb-ss">${previewsHtml}</div>` : ""}${limitHtml}</div>` : "";
900
987
  })() : "";
901
988
  const emailBlockHtml = showEmailOption ? `
902
989
  <div class="fb-email">
@@ -919,8 +1006,11 @@ function initUseroFeedbackWidget(props) {
919
1006
  <form data-role="form">
920
1007
  <div class="fb-es" role="radiogroup" aria-label="Rate experience">${ratingsHtml}</div>
921
1008
  <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>
922
- <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>
923
- ${screenshotBlockHtml}
1009
+ <div class="fb-toolrow">
1010
+ ${uploadBtnHtml}
1011
+ <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>
1012
+ </div>
1013
+ ${uploadExtrasHtml ? `<div class="fb-up">${uploadExtrasHtml}</div>` : ""}
924
1014
  ${emailBlockHtml}
925
1015
  <button class="fb-sub ${submitDisabled ? "fb-sub--dis" : ""}" type="submit" aria-label="Submit" ${submitDisabled ? "disabled" : ""} style="${submitStyle}">
926
1016
  ${isSubmitting ? '<span class="fb-spin"></span>' : ""}
@@ -1051,10 +1141,23 @@ function initUseroFeedbackWidget(props) {
1051
1141
  destroyed = true;
1052
1142
  document.removeEventListener("keydown", onKeyDown);
1053
1143
  detachMqlListener();
1144
+ for (const plugin of pluginList) {
1145
+ if (!plugin.onDestroy) continue;
1146
+ const ctx = pluginContexts.get(plugin.name);
1147
+ if (!ctx) continue;
1148
+ try {
1149
+ plugin.onDestroy(ctx);
1150
+ } catch (err) {
1151
+ ctx.logger.error("onDestroy threw", err);
1152
+ }
1153
+ }
1154
+ pluginStores.clear();
1155
+ pluginContexts.clear();
1054
1156
  host.remove();
1055
1157
  },
1056
1158
  open,
1057
1159
  close,
1160
+ whenReady: () => readyPromise,
1058
1161
  update: (next) => {
1059
1162
  if (destroyed) return;
1060
1163
  let needsRender = false;
@@ -1096,6 +1199,6 @@ function initUseroFeedbackWidget(props) {
1096
1199
  };
1097
1200
  }
1098
1201
 
1099
- export { DARK_THEME, DEFAULT_THEME, initUseroFeedbackWidget, mergeTheme, resolveTheme };
1202
+ export { DARK_THEME, DEFAULT_THEME, initUseroFeedbackWidget, mergePluginPatches, mergeTheme, resolveTheme };
1100
1203
  //# sourceMappingURL=vanilla.js.map
1101
1204
  //# sourceMappingURL=vanilla.js.map