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