@foxpixel/react 0.2.1 → 0.2.3

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/index.js CHANGED
@@ -202,7 +202,8 @@ function FoxPixelProvider({ children, config = {}, queryClient }) {
202
202
  const value = (0, import_react.useMemo)(() => ({
203
203
  client,
204
204
  config,
205
- queryClient: queryClient ?? null
205
+ queryClient: queryClient ?? null,
206
+ locale: config?.locale
206
207
  }), [client, config, queryClient]);
207
208
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FoxPixelContext.Provider, { value, children });
208
209
  }
@@ -435,6 +436,28 @@ var import_react8 = require("react");
435
436
  // src/hooks/useEditMode.ts
436
437
  var import_react6 = require("react");
437
438
  var SITE_CONTENT_QUERY_KEY = "siteContent";
439
+ var contentKeysOnPage = /* @__PURE__ */ new Set();
440
+ var flushTimer = null;
441
+ function scheduleFlushContentKeys() {
442
+ if (flushTimer) return;
443
+ flushTimer = setTimeout(() => {
444
+ flushTimer = null;
445
+ if (typeof window !== "undefined" && window.parent !== window) {
446
+ window.parent.postMessage(
447
+ { type: "FOXPIXEL_READY", payload: { contentKeys: Array.from(contentKeysOnPage) } },
448
+ "*"
449
+ );
450
+ }
451
+ }, 150);
452
+ }
453
+ function registerContentKey(key) {
454
+ contentKeysOnPage.add(key);
455
+ scheduleFlushContentKeys();
456
+ }
457
+ function unregisterContentKey(key) {
458
+ contentKeysOnPage.delete(key);
459
+ scheduleFlushContentKeys();
460
+ }
438
461
  function useEditMode() {
439
462
  const [isEditMode, setIsEditMode] = (0, import_react6.useState)(false);
440
463
  (0, import_react6.useEffect)(() => {
@@ -455,19 +478,18 @@ function useEditModeMessaging() {
455
478
  if (!queryClient) return;
456
479
  const { type, payload } = event.data || {};
457
480
  if (type !== "FOXPIXEL_CONTENT_UPDATED" || !payload?.contentKey) return;
458
- const { contentKey, newValue } = payload;
481
+ const { contentKey, newValue, locale } = payload;
482
+ const cacheKey2 = locale != null && locale !== "" ? [SITE_CONTENT_QUERY_KEY, contentKey, locale] : [SITE_CONTENT_QUERY_KEY, contentKey];
459
483
  if (typeof newValue === "string") {
460
484
  queryClient.setQueryData(
461
- [SITE_CONTENT_QUERY_KEY, contentKey],
485
+ cacheKey2,
462
486
  (prev) => ({
463
487
  value: newValue,
464
488
  contentType: prev?.contentType ?? "TEXT"
465
489
  })
466
490
  );
467
491
  }
468
- queryClient.invalidateQueries({
469
- queryKey: [SITE_CONTENT_QUERY_KEY, contentKey]
470
- });
492
+ queryClient.invalidateQueries({ queryKey: cacheKey2 });
471
493
  };
472
494
  window.addEventListener("message", handleMessage);
473
495
  return () => window.removeEventListener("message", handleMessage);
@@ -477,20 +499,19 @@ function useEditModeMessaging() {
477
499
  function useSendEditRequest() {
478
500
  const isEditMode = useEditMode();
479
501
  return (0, import_react6.useCallback)(
480
- (contentKey, currentValue, contentType = "text", section, description) => {
502
+ (contentKey, currentValue, contentType = "text", section, description, locale) => {
481
503
  if (!isEditMode) return;
482
504
  if (typeof window !== "undefined" && window.parent !== window) {
505
+ const payload = {
506
+ contentKey,
507
+ currentValue,
508
+ contentType,
509
+ section,
510
+ description
511
+ };
512
+ if (locale != null && locale !== "") payload.locale = locale;
483
513
  window.parent.postMessage(
484
- {
485
- type: "FOXPIXEL_EDIT_CONTENT",
486
- payload: {
487
- contentKey,
488
- currentValue,
489
- contentType,
490
- section,
491
- description
492
- }
493
- },
514
+ { type: "FOXPIXEL_EDIT_CONTENT", payload },
494
515
  "*"
495
516
  );
496
517
  }
@@ -501,20 +522,23 @@ function useSendEditRequest() {
501
522
 
502
523
  // src/hooks/useSiteContentQuery.ts
503
524
  var import_react7 = require("react");
504
- function getCached(queryClient, contentKey) {
505
- const data = queryClient.getQueryData([
506
- SITE_CONTENT_QUERY_KEY,
507
- contentKey
508
- ]);
525
+ function queryKey(contentKey, locale) {
526
+ return locale != null && locale !== "" ? [SITE_CONTENT_QUERY_KEY, contentKey, locale] : [SITE_CONTENT_QUERY_KEY, contentKey];
527
+ }
528
+ function getCached(queryClient, contentKey, locale) {
529
+ const data = queryClient.getQueryData(
530
+ queryKey(contentKey, locale)
531
+ );
509
532
  if (data == null) return void 0;
510
533
  return { value: data.value ?? "", contentType: data.contentType ?? "TEXT" };
511
534
  }
512
535
  function useSiteContentQuery(contentKey, options) {
513
- const { defaultValue } = options;
536
+ const { defaultValue, locale } = options;
537
+ const loc = locale != null && locale !== "" ? locale : void 0;
514
538
  const { client, queryClient } = useFoxPixelContext();
515
539
  const [state, setState] = (0, import_react7.useState)(() => {
516
540
  if (queryClient) {
517
- const cached = getCached(queryClient, contentKey);
541
+ const cached = getCached(queryClient, contentKey, loc);
518
542
  if (cached) {
519
543
  return { value: cached.value, isLoading: false, contentType: cached.contentType };
520
544
  }
@@ -523,18 +547,20 @@ function useSiteContentQuery(contentKey, options) {
523
547
  });
524
548
  const contentKeyRef = (0, import_react7.useRef)(contentKey);
525
549
  contentKeyRef.current = contentKey;
550
+ const localeRef = (0, import_react7.useRef)(loc);
551
+ localeRef.current = loc;
526
552
  (0, import_react7.useEffect)(() => {
527
553
  if (!queryClient) {
528
554
  setState((s) => ({ ...s, value: defaultValue, isLoading: false }));
529
555
  return;
530
556
  }
531
557
  const key = contentKeyRef.current;
532
- const queryKey = [SITE_CONTENT_QUERY_KEY, key];
558
+ const locCurrent = localeRef.current;
559
+ const qKey = queryKey(key, locCurrent);
533
560
  const queryFn = async () => {
534
561
  try {
535
- const content = await client.get(
536
- `/api/site/content/${encodeURIComponent(key)}`
537
- );
562
+ const url = `/api/site/content/${encodeURIComponent(key)}` + (locCurrent ? `?locale=${encodeURIComponent(locCurrent)}` : "");
563
+ const content = await client.get(url);
538
564
  if (!content) return null;
539
565
  return {
540
566
  value: content.value ?? "",
@@ -548,7 +574,7 @@ function useSiteContentQuery(contentKey, options) {
548
574
  };
549
575
  let cancelled = false;
550
576
  queryClient.fetchQuery({
551
- queryKey,
577
+ queryKey: qKey,
552
578
  queryFn,
553
579
  staleTime: 1e3 * 60 * 5,
554
580
  retry: 1
@@ -564,8 +590,11 @@ function useSiteContentQuery(contentKey, options) {
564
590
  setState((s) => ({ ...s, value: defaultValue, isLoading: false }));
565
591
  });
566
592
  const unsub = queryClient.getQueryCache().subscribe((event) => {
567
- if (event?.type === "updated" && event?.query?.queryKey[1] === key) {
568
- const cached = getCached(queryClient, key);
593
+ const q = event?.query;
594
+ const keyMatch = q?.queryKey?.[1] === key;
595
+ const locMatch = (q?.queryKey?.[2] ?? void 0) === locCurrent;
596
+ if (event?.type === "updated" && q && keyMatch && locMatch) {
597
+ const cached = getCached(queryClient, key, locCurrent);
569
598
  if (cached && !cancelled) {
570
599
  setState({
571
600
  value: cached.value,
@@ -579,7 +608,7 @@ function useSiteContentQuery(contentKey, options) {
579
608
  cancelled = true;
580
609
  unsub();
581
610
  };
582
- }, [queryClient, contentKey, defaultValue, client]);
611
+ }, [queryClient, contentKey, defaultValue, client, loc]);
583
612
  return state;
584
613
  }
585
614
 
@@ -657,15 +686,25 @@ function Editable({
657
686
  defaultValue,
658
687
  as = "span",
659
688
  multiline = false,
660
- className
689
+ className,
690
+ locale: localeProp
661
691
  }) {
662
692
  const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
693
+ const { locale: contextLocale } = useFoxPixelContext();
694
+ const locale = localeProp ?? contextLocale;
663
695
  const isEditMode = useEditModeMessaging();
664
696
  const sendEditRequest = useSendEditRequest();
665
697
  const { value, isLoading, contentType } = useSiteContentQuery(contentKey, {
666
- defaultValue
698
+ defaultValue,
699
+ locale
667
700
  });
668
701
  const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
702
+ (0, import_react8.useEffect)(() => {
703
+ if (isEditMode) {
704
+ registerContentKey(contentKey);
705
+ return () => unregisterContentKey(contentKey);
706
+ }
707
+ }, [isEditMode, contentKey]);
669
708
  const handleClick = (0, import_react8.useCallback)(
670
709
  (e) => {
671
710
  if (isEditMode) {
@@ -675,11 +714,13 @@ function Editable({
675
714
  contentKey,
676
715
  value,
677
716
  contentType?.toLowerCase() || "text",
678
- section
717
+ section,
718
+ void 0,
719
+ locale
679
720
  );
680
721
  }
681
722
  },
682
- [isEditMode, contentKey, value, contentType, section, sendEditRequest]
723
+ [isEditMode, contentKey, value, contentType, section, sendEditRequest, locale]
683
724
  );
684
725
  if (isLoading) {
685
726
  return (0, import_react8.createElement)(as, {
@@ -752,24 +793,34 @@ function EditableHTML({
752
793
  contentKey,
753
794
  defaultValue,
754
795
  as = "div",
755
- className
796
+ className,
797
+ locale: localeProp
756
798
  }) {
757
799
  const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
800
+ const { locale: contextLocale } = useFoxPixelContext();
801
+ const locale = localeProp ?? contextLocale;
758
802
  const isEditMode = useEditModeMessaging();
759
803
  const sendEditRequest = useSendEditRequest();
760
804
  const { value, isLoading } = useSiteContentQuery(contentKey, {
761
- defaultValue
805
+ defaultValue,
806
+ locale
762
807
  });
763
808
  const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
809
+ (0, import_react8.useEffect)(() => {
810
+ if (isEditMode) {
811
+ registerContentKey(contentKey);
812
+ return () => unregisterContentKey(contentKey);
813
+ }
814
+ }, [isEditMode, contentKey]);
764
815
  const handleClick = (0, import_react8.useCallback)(
765
816
  (e) => {
766
817
  if (isEditMode) {
767
818
  e.preventDefault();
768
819
  e.stopPropagation();
769
- sendEditRequest(contentKey, value, "html", section);
820
+ sendEditRequest(contentKey, value, "html", section, void 0, locale);
770
821
  }
771
822
  },
772
- [isEditMode, contentKey, value, section, sendEditRequest]
823
+ [isEditMode, contentKey, value, section, sendEditRequest, locale]
773
824
  );
774
825
  if (isLoading) {
775
826
  return (0, import_react8.createElement)(as, {
@@ -818,24 +869,34 @@ function EditableImage({
818
869
  className,
819
870
  width,
820
871
  height,
821
- priority = false
872
+ priority = false,
873
+ locale: localeProp
822
874
  }) {
823
875
  const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
876
+ const { locale: contextLocale } = useFoxPixelContext();
877
+ const locale = localeProp ?? contextLocale;
824
878
  const isEditMode = useEditModeMessaging();
825
879
  const sendEditRequest = useSendEditRequest();
826
880
  const { value: src, isLoading } = useSiteContentQuery(contentKey, {
827
- defaultValue
881
+ defaultValue,
882
+ locale
828
883
  });
829
884
  const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
885
+ (0, import_react8.useEffect)(() => {
886
+ if (isEditMode) {
887
+ registerContentKey(contentKey);
888
+ return () => unregisterContentKey(contentKey);
889
+ }
890
+ }, [isEditMode, contentKey]);
830
891
  const handleClick = (0, import_react8.useCallback)(
831
892
  (e) => {
832
893
  if (isEditMode) {
833
894
  e.preventDefault();
834
895
  e.stopPropagation();
835
- sendEditRequest(contentKey, src, "image", section);
896
+ sendEditRequest(contentKey, src, "image", section, void 0, locale);
836
897
  }
837
898
  },
838
- [isEditMode, contentKey, src, section, sendEditRequest]
899
+ [isEditMode, contentKey, src, section, sendEditRequest, locale]
839
900
  );
840
901
  if (isLoading) {
841
902
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -981,20 +1042,20 @@ function useContactCapture() {
981
1042
  // src/hooks/useSiteContent.ts
982
1043
  var import_react12 = require("react");
983
1044
  function useSiteContent(contentKey, options = {}) {
984
- const { defaultValue = "", fetchOnMount = true } = options;
1045
+ const { defaultValue = "", fetchOnMount = true, locale } = options;
985
1046
  const { client } = useFoxPixelContext();
986
1047
  const { user, hasPermission } = useAuth();
987
1048
  const [data, setData] = (0, import_react12.useState)(null);
988
1049
  const [isLoading, setIsLoading] = (0, import_react12.useState)(fetchOnMount);
989
1050
  const [error, setError] = (0, import_react12.useState)(null);
990
1051
  const canEdit = user !== null && hasPermission("site:content:update");
1052
+ const queryLocale = locale != null && locale !== "" ? locale : void 0;
991
1053
  const fetchContent = (0, import_react12.useCallback)(async () => {
992
1054
  try {
993
1055
  setIsLoading(true);
994
1056
  setError(null);
995
- const content = await client.get(
996
- `/api/site/content/${encodeURIComponent(contentKey)}`
997
- );
1057
+ const url = `/api/site/content/${encodeURIComponent(contentKey)}` + (queryLocale ? `?locale=${encodeURIComponent(queryLocale)}` : "");
1058
+ const content = await client.get(url);
998
1059
  setData(content);
999
1060
  } catch (err) {
1000
1061
  if (err?.status === 404) {
@@ -1005,20 +1066,22 @@ function useSiteContent(contentKey, options = {}) {
1005
1066
  } finally {
1006
1067
  setIsLoading(false);
1007
1068
  }
1008
- }, [client, contentKey]);
1069
+ }, [client, contentKey, queryLocale]);
1009
1070
  const updateContent = (0, import_react12.useCallback)(async (newValue) => {
1010
1071
  try {
1011
1072
  setError(null);
1073
+ const body = { value: newValue };
1074
+ if (queryLocale) body.locale = queryLocale;
1012
1075
  const updated = await client.put(
1013
1076
  `/api/site/content/${encodeURIComponent(contentKey)}`,
1014
- { value: newValue }
1077
+ body
1015
1078
  );
1016
1079
  setData(updated);
1017
1080
  } catch (err) {
1018
1081
  setError(err);
1019
1082
  throw err;
1020
1083
  }
1021
- }, [client, contentKey]);
1084
+ }, [client, contentKey, queryLocale]);
1022
1085
  (0, import_react12.useEffect)(() => {
1023
1086
  if (fetchOnMount) {
1024
1087
  fetchContent();
@@ -1036,11 +1099,12 @@ function useSiteContent(contentKey, options = {}) {
1036
1099
  };
1037
1100
  }
1038
1101
  function useSiteContents(contentKeys, options = {}) {
1039
- const { defaults = {} } = options;
1102
+ const { defaults = {}, locale } = options;
1040
1103
  const { client } = useFoxPixelContext();
1041
1104
  const [data, setData] = (0, import_react12.useState)({});
1042
1105
  const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
1043
1106
  const [error, setError] = (0, import_react12.useState)(null);
1107
+ const body = locale != null && locale !== "" ? { keys: contentKeys, locale } : contentKeys;
1044
1108
  const fetchContents = (0, import_react12.useCallback)(async () => {
1045
1109
  if (contentKeys.length === 0) {
1046
1110
  setData({});
@@ -1052,7 +1116,7 @@ function useSiteContents(contentKeys, options = {}) {
1052
1116
  setError(null);
1053
1117
  const contents = await client.post(
1054
1118
  "/api/site/content/batch",
1055
- contentKeys
1119
+ body
1056
1120
  );
1057
1121
  setData(contents);
1058
1122
  } catch (err) {
@@ -1060,7 +1124,7 @@ function useSiteContents(contentKeys, options = {}) {
1060
1124
  } finally {
1061
1125
  setIsLoading(false);
1062
1126
  }
1063
- }, [client, contentKeys.join(",")]);
1127
+ }, [client, contentKeys.join(","), locale]);
1064
1128
  (0, import_react12.useEffect)(() => {
1065
1129
  fetchContents();
1066
1130
  }, [fetchContents]);
@@ -1079,25 +1143,25 @@ function useSiteContents(contentKeys, options = {}) {
1079
1143
  refetch: fetchContents
1080
1144
  };
1081
1145
  }
1082
- function useSiteContentSection(section) {
1146
+ function useSiteContentSection(section, options = {}) {
1147
+ const { locale } = options;
1083
1148
  const { client } = useFoxPixelContext();
1084
1149
  const [contents, setContents] = (0, import_react12.useState)([]);
1085
1150
  const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
1086
1151
  const [error, setError] = (0, import_react12.useState)(null);
1152
+ const url = `/api/site/content/section/${encodeURIComponent(section)}` + (locale != null && locale !== "" ? `?locale=${encodeURIComponent(locale)}` : "");
1087
1153
  const fetchContents = (0, import_react12.useCallback)(async () => {
1088
1154
  try {
1089
1155
  setIsLoading(true);
1090
1156
  setError(null);
1091
- const data = await client.get(
1092
- `/api/site/content/section/${encodeURIComponent(section)}`
1093
- );
1157
+ const data = await client.get(url);
1094
1158
  setContents(data);
1095
1159
  } catch (err) {
1096
1160
  setError(err);
1097
1161
  } finally {
1098
1162
  setIsLoading(false);
1099
1163
  }
1100
- }, [client, section]);
1164
+ }, [client, section, locale]);
1101
1165
  (0, import_react12.useEffect)(() => {
1102
1166
  fetchContents();
1103
1167
  }, [fetchContents]);
@@ -1110,33 +1174,39 @@ function useSiteContentSection(section) {
1110
1174
  }
1111
1175
 
1112
1176
  // src/prefetchSiteContent.ts
1177
+ function cacheKey(contentKey, locale) {
1178
+ return locale != null && locale !== "" ? [SITE_CONTENT_QUERY_KEY, contentKey, locale] : [SITE_CONTENT_QUERY_KEY, contentKey];
1179
+ }
1113
1180
  async function prefetchSiteContent(queryClient, options) {
1114
- const { apiUrl, apiKey, tenantId, contentKeys } = options;
1181
+ const { apiUrl, apiKey, tenantId, contentKeys, locale } = options;
1115
1182
  const client = new FoxPixelHttpClient({ apiUrl, apiKey, tenantId });
1116
- await Promise.all(
1117
- contentKeys.map(async (contentKey) => {
1118
- try {
1119
- const content = await client.get(
1120
- `/api/site/content/${encodeURIComponent(contentKey)}`
1121
- );
1122
- queryClient.setQueryData(
1123
- [SITE_CONTENT_QUERY_KEY, contentKey],
1124
- {
1125
- value: content?.value ?? "",
1126
- contentType: content?.contentType ?? "TEXT"
1183
+ const locales = locale == null ? [void 0] : Array.isArray(locale) ? locale : [locale];
1184
+ const tasks = [];
1185
+ for (const contentKey of contentKeys) {
1186
+ for (const loc of locales) {
1187
+ tasks.push(
1188
+ (async () => {
1189
+ try {
1190
+ const url = `/api/site/content/${encodeURIComponent(contentKey)}` + (loc != null && loc !== "" ? `?locale=${encodeURIComponent(loc)}` : "");
1191
+ const content = await client.get(url);
1192
+ queryClient.setQueryData(cacheKey(contentKey, loc), {
1193
+ value: content?.value ?? "",
1194
+ contentType: content?.contentType ?? "TEXT"
1195
+ });
1196
+ } catch (err) {
1197
+ const status = err?.response?.status;
1198
+ if (status === 404) {
1199
+ queryClient.setQueryData(cacheKey(contentKey, loc), {
1200
+ value: "",
1201
+ contentType: "TEXT"
1202
+ });
1203
+ }
1127
1204
  }
1128
- );
1129
- } catch (err) {
1130
- const status = err?.response?.status;
1131
- if (status === 404) {
1132
- queryClient.setQueryData([SITE_CONTENT_QUERY_KEY, contentKey], {
1133
- value: "",
1134
- contentType: "TEXT"
1135
- });
1136
- }
1137
- }
1138
- })
1139
- );
1205
+ })()
1206
+ );
1207
+ }
1208
+ }
1209
+ await Promise.all(tasks);
1140
1210
  }
1141
1211
 
1142
1212
  // src/blog/hooks.ts