@foxpixel/react 0.2.2 → 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.d.mts CHANGED
@@ -22,6 +22,10 @@ interface FoxPixelConfig {
22
22
  * Tenant ID (optional, can be set per request)
23
23
  */
24
24
  tenantId?: string;
25
+ /**
26
+ * Current locale for site content i18n (e.g. "en", "pt"). Omit for default.
27
+ */
28
+ locale?: string;
25
29
  }
26
30
  interface Service {
27
31
  id: string;
@@ -140,6 +144,8 @@ interface FoxPixelContextValue {
140
144
  config: FoxPixelConfig;
141
145
  /** Optional: pass QueryClient so edit-mode messaging can invalidate cache without requiring useQueryClient() in SDK (avoids "No QueryClient set" in iframe). */
142
146
  queryClient?: QueryClient | null;
147
+ /** Current locale for site content i18n (e.g. "en", "pt"). From config.locale. */
148
+ locale?: string;
143
149
  }
144
150
  interface FoxPixelProviderProps {
145
151
  children: ReactNode;
@@ -274,8 +280,10 @@ interface EditableProps {
274
280
  as?: keyof JSX.IntrinsicElements;
275
281
  multiline?: boolean;
276
282
  className?: string;
283
+ /** Override locale for this editable (default: from FoxPixelProvider config.locale). */
284
+ locale?: string;
277
285
  }
278
- declare function Editable({ contentKey, defaultValue, as, multiline, className, }: EditableProps): React.ReactElement<{
286
+ declare function Editable({ contentKey, defaultValue, as, multiline, className, locale: localeProp, }: EditableProps): React.ReactElement<{
279
287
  className: string;
280
288
  'aria-busy': boolean;
281
289
  'aria-label': string;
@@ -291,7 +299,7 @@ declare function Editable({ contentKey, defaultValue, as, multiline, className,
291
299
  /**
292
300
  * Renders rich HTML content from the CMS. Content is sanitized before rendering.
293
301
  */
294
- declare function EditableHTML({ contentKey, defaultValue, as, className, }: Omit<EditableProps, 'multiline'>): React.ReactElement<{
302
+ declare function EditableHTML({ contentKey, defaultValue, as, className, locale: localeProp, }: Omit<EditableProps, 'multiline'>): React.ReactElement<{
295
303
  className: string;
296
304
  'aria-busy': boolean;
297
305
  }, string | React.JSXElementConstructor<any>> | React.DetailedReactHTMLElement<{
@@ -311,11 +319,12 @@ interface EditableImageProps {
311
319
  width?: number;
312
320
  height?: number;
313
321
  priority?: boolean;
322
+ locale?: string;
314
323
  }
315
324
  /**
316
325
  * Renders an image from a CMS-managed URL. Uses native img (no Next.js Image in SDK).
317
326
  */
318
- declare function EditableImage({ contentKey, defaultValue, alt, className, width, height, priority, }: EditableImageProps): react_jsx_runtime.JSX.Element;
327
+ declare function EditableImage({ contentKey, defaultValue, alt, className, width, height, priority, locale: localeProp, }: EditableImageProps): react_jsx_runtime.JSX.Element;
319
328
 
320
329
  /**
321
330
  * Hook to fetch and manage services (Projects module)
@@ -470,6 +479,7 @@ declare function useContactCapture(): UseContactCaptureReturn;
470
479
  interface SiteContent {
471
480
  id: string;
472
481
  contentKey: string;
482
+ locale?: string;
473
483
  value: string | null;
474
484
  contentType: string;
475
485
  defaultValue: string | null;
@@ -488,6 +498,10 @@ interface UseSiteContentOptions {
488
498
  * Whether to fetch on mount (default: true)
489
499
  */
490
500
  fetchOnMount?: boolean;
501
+ /**
502
+ * Locale for i18n (e.g. "en", "pt"). Omit for default.
503
+ */
504
+ locale?: string;
491
505
  }
492
506
  interface UseSiteContentReturn {
493
507
  /**
@@ -561,6 +575,10 @@ interface UseSiteContentsOptions {
561
575
  * Default values map
562
576
  */
563
577
  defaults?: Record<string, string>;
578
+ /**
579
+ * Locale for i18n (e.g. "en", "pt"). Omit for default.
580
+ */
581
+ locale?: string;
564
582
  }
565
583
  interface UseSiteContentsReturn {
566
584
  /**
@@ -647,7 +665,10 @@ interface UseSiteContentSectionReturn {
647
665
  * }
648
666
  * ```
649
667
  */
650
- declare function useSiteContentSection(section: string): UseSiteContentSectionReturn;
668
+ interface UseSiteContentSectionOptions {
669
+ locale?: string;
670
+ }
671
+ declare function useSiteContentSection(section: string, options?: UseSiteContentSectionOptions): UseSiteContentSectionReturn;
651
672
 
652
673
  /**
653
674
  * Edit-mode hooks for CMS Visual (iframe communication with Tenant Admin).
@@ -671,7 +692,7 @@ declare function useEditModeMessaging(): boolean;
671
692
  * Returns a callback to send an edit request to the parent iframe.
672
693
  * Only has effect when in edit mode.
673
694
  */
674
- declare function useSendEditRequest(): (contentKey: string, currentValue: string, contentType?: string, section?: string, description?: string) => void;
695
+ declare function useSendEditRequest(): (contentKey: string, currentValue: string, contentType?: string, section?: string, description?: string, locale?: string) => void;
675
696
 
676
697
  /**
677
698
  * Site content hook for Editable components.
@@ -680,6 +701,8 @@ declare function useSendEditRequest(): (contentKey: string, currentValue: string
680
701
  */
681
702
  interface UseSiteContentQueryOptions {
682
703
  defaultValue: string;
704
+ /** Locale for i18n (e.g. "en", "pt"). Omit for default. */
705
+ locale?: string;
683
706
  }
684
707
  interface UseSiteContentQueryReturn {
685
708
  value: string;
@@ -702,6 +725,8 @@ interface PrefetchSiteContentOptions {
702
725
  apiKey?: string;
703
726
  tenantId?: string;
704
727
  contentKeys: string[];
728
+ /** Locale for i18n (e.g. "en", "pt"). Can pass multiple to prefetch several locales. */
729
+ locale?: string | string[];
705
730
  }
706
731
  /**
707
732
  * Prefetch site content keys and set them on the given QueryClient.
package/dist/index.d.ts CHANGED
@@ -22,6 +22,10 @@ interface FoxPixelConfig {
22
22
  * Tenant ID (optional, can be set per request)
23
23
  */
24
24
  tenantId?: string;
25
+ /**
26
+ * Current locale for site content i18n (e.g. "en", "pt"). Omit for default.
27
+ */
28
+ locale?: string;
25
29
  }
26
30
  interface Service {
27
31
  id: string;
@@ -140,6 +144,8 @@ interface FoxPixelContextValue {
140
144
  config: FoxPixelConfig;
141
145
  /** Optional: pass QueryClient so edit-mode messaging can invalidate cache without requiring useQueryClient() in SDK (avoids "No QueryClient set" in iframe). */
142
146
  queryClient?: QueryClient | null;
147
+ /** Current locale for site content i18n (e.g. "en", "pt"). From config.locale. */
148
+ locale?: string;
143
149
  }
144
150
  interface FoxPixelProviderProps {
145
151
  children: ReactNode;
@@ -274,8 +280,10 @@ interface EditableProps {
274
280
  as?: keyof JSX.IntrinsicElements;
275
281
  multiline?: boolean;
276
282
  className?: string;
283
+ /** Override locale for this editable (default: from FoxPixelProvider config.locale). */
284
+ locale?: string;
277
285
  }
278
- declare function Editable({ contentKey, defaultValue, as, multiline, className, }: EditableProps): React.ReactElement<{
286
+ declare function Editable({ contentKey, defaultValue, as, multiline, className, locale: localeProp, }: EditableProps): React.ReactElement<{
279
287
  className: string;
280
288
  'aria-busy': boolean;
281
289
  'aria-label': string;
@@ -291,7 +299,7 @@ declare function Editable({ contentKey, defaultValue, as, multiline, className,
291
299
  /**
292
300
  * Renders rich HTML content from the CMS. Content is sanitized before rendering.
293
301
  */
294
- declare function EditableHTML({ contentKey, defaultValue, as, className, }: Omit<EditableProps, 'multiline'>): React.ReactElement<{
302
+ declare function EditableHTML({ contentKey, defaultValue, as, className, locale: localeProp, }: Omit<EditableProps, 'multiline'>): React.ReactElement<{
295
303
  className: string;
296
304
  'aria-busy': boolean;
297
305
  }, string | React.JSXElementConstructor<any>> | React.DetailedReactHTMLElement<{
@@ -311,11 +319,12 @@ interface EditableImageProps {
311
319
  width?: number;
312
320
  height?: number;
313
321
  priority?: boolean;
322
+ locale?: string;
314
323
  }
315
324
  /**
316
325
  * Renders an image from a CMS-managed URL. Uses native img (no Next.js Image in SDK).
317
326
  */
318
- declare function EditableImage({ contentKey, defaultValue, alt, className, width, height, priority, }: EditableImageProps): react_jsx_runtime.JSX.Element;
327
+ declare function EditableImage({ contentKey, defaultValue, alt, className, width, height, priority, locale: localeProp, }: EditableImageProps): react_jsx_runtime.JSX.Element;
319
328
 
320
329
  /**
321
330
  * Hook to fetch and manage services (Projects module)
@@ -470,6 +479,7 @@ declare function useContactCapture(): UseContactCaptureReturn;
470
479
  interface SiteContent {
471
480
  id: string;
472
481
  contentKey: string;
482
+ locale?: string;
473
483
  value: string | null;
474
484
  contentType: string;
475
485
  defaultValue: string | null;
@@ -488,6 +498,10 @@ interface UseSiteContentOptions {
488
498
  * Whether to fetch on mount (default: true)
489
499
  */
490
500
  fetchOnMount?: boolean;
501
+ /**
502
+ * Locale for i18n (e.g. "en", "pt"). Omit for default.
503
+ */
504
+ locale?: string;
491
505
  }
492
506
  interface UseSiteContentReturn {
493
507
  /**
@@ -561,6 +575,10 @@ interface UseSiteContentsOptions {
561
575
  * Default values map
562
576
  */
563
577
  defaults?: Record<string, string>;
578
+ /**
579
+ * Locale for i18n (e.g. "en", "pt"). Omit for default.
580
+ */
581
+ locale?: string;
564
582
  }
565
583
  interface UseSiteContentsReturn {
566
584
  /**
@@ -647,7 +665,10 @@ interface UseSiteContentSectionReturn {
647
665
  * }
648
666
  * ```
649
667
  */
650
- declare function useSiteContentSection(section: string): UseSiteContentSectionReturn;
668
+ interface UseSiteContentSectionOptions {
669
+ locale?: string;
670
+ }
671
+ declare function useSiteContentSection(section: string, options?: UseSiteContentSectionOptions): UseSiteContentSectionReturn;
651
672
 
652
673
  /**
653
674
  * Edit-mode hooks for CMS Visual (iframe communication with Tenant Admin).
@@ -671,7 +692,7 @@ declare function useEditModeMessaging(): boolean;
671
692
  * Returns a callback to send an edit request to the parent iframe.
672
693
  * Only has effect when in edit mode.
673
694
  */
674
- declare function useSendEditRequest(): (contentKey: string, currentValue: string, contentType?: string, section?: string, description?: string) => void;
695
+ declare function useSendEditRequest(): (contentKey: string, currentValue: string, contentType?: string, section?: string, description?: string, locale?: string) => void;
675
696
 
676
697
  /**
677
698
  * Site content hook for Editable components.
@@ -680,6 +701,8 @@ declare function useSendEditRequest(): (contentKey: string, currentValue: string
680
701
  */
681
702
  interface UseSiteContentQueryOptions {
682
703
  defaultValue: string;
704
+ /** Locale for i18n (e.g. "en", "pt"). Omit for default. */
705
+ locale?: string;
683
706
  }
684
707
  interface UseSiteContentQueryReturn {
685
708
  value: string;
@@ -702,6 +725,8 @@ interface PrefetchSiteContentOptions {
702
725
  apiKey?: string;
703
726
  tenantId?: string;
704
727
  contentKeys: string[];
728
+ /** Locale for i18n (e.g. "en", "pt"). Can pass multiple to prefetch several locales. */
729
+ locale?: string | string[];
705
730
  }
706
731
  /**
707
732
  * Prefetch site content keys and set them on the given QueryClient.
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
  }
@@ -477,19 +478,18 @@ function useEditModeMessaging() {
477
478
  if (!queryClient) return;
478
479
  const { type, payload } = event.data || {};
479
480
  if (type !== "FOXPIXEL_CONTENT_UPDATED" || !payload?.contentKey) return;
480
- 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];
481
483
  if (typeof newValue === "string") {
482
484
  queryClient.setQueryData(
483
- [SITE_CONTENT_QUERY_KEY, contentKey],
485
+ cacheKey2,
484
486
  (prev) => ({
485
487
  value: newValue,
486
488
  contentType: prev?.contentType ?? "TEXT"
487
489
  })
488
490
  );
489
491
  }
490
- queryClient.invalidateQueries({
491
- queryKey: [SITE_CONTENT_QUERY_KEY, contentKey]
492
- });
492
+ queryClient.invalidateQueries({ queryKey: cacheKey2 });
493
493
  };
494
494
  window.addEventListener("message", handleMessage);
495
495
  return () => window.removeEventListener("message", handleMessage);
@@ -499,20 +499,19 @@ function useEditModeMessaging() {
499
499
  function useSendEditRequest() {
500
500
  const isEditMode = useEditMode();
501
501
  return (0, import_react6.useCallback)(
502
- (contentKey, currentValue, contentType = "text", section, description) => {
502
+ (contentKey, currentValue, contentType = "text", section, description, locale) => {
503
503
  if (!isEditMode) return;
504
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;
505
513
  window.parent.postMessage(
506
- {
507
- type: "FOXPIXEL_EDIT_CONTENT",
508
- payload: {
509
- contentKey,
510
- currentValue,
511
- contentType,
512
- section,
513
- description
514
- }
515
- },
514
+ { type: "FOXPIXEL_EDIT_CONTENT", payload },
516
515
  "*"
517
516
  );
518
517
  }
@@ -523,20 +522,23 @@ function useSendEditRequest() {
523
522
 
524
523
  // src/hooks/useSiteContentQuery.ts
525
524
  var import_react7 = require("react");
526
- function getCached(queryClient, contentKey) {
527
- const data = queryClient.getQueryData([
528
- SITE_CONTENT_QUERY_KEY,
529
- contentKey
530
- ]);
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
+ );
531
532
  if (data == null) return void 0;
532
533
  return { value: data.value ?? "", contentType: data.contentType ?? "TEXT" };
533
534
  }
534
535
  function useSiteContentQuery(contentKey, options) {
535
- const { defaultValue } = options;
536
+ const { defaultValue, locale } = options;
537
+ const loc = locale != null && locale !== "" ? locale : void 0;
536
538
  const { client, queryClient } = useFoxPixelContext();
537
539
  const [state, setState] = (0, import_react7.useState)(() => {
538
540
  if (queryClient) {
539
- const cached = getCached(queryClient, contentKey);
541
+ const cached = getCached(queryClient, contentKey, loc);
540
542
  if (cached) {
541
543
  return { value: cached.value, isLoading: false, contentType: cached.contentType };
542
544
  }
@@ -545,18 +547,20 @@ function useSiteContentQuery(contentKey, options) {
545
547
  });
546
548
  const contentKeyRef = (0, import_react7.useRef)(contentKey);
547
549
  contentKeyRef.current = contentKey;
550
+ const localeRef = (0, import_react7.useRef)(loc);
551
+ localeRef.current = loc;
548
552
  (0, import_react7.useEffect)(() => {
549
553
  if (!queryClient) {
550
554
  setState((s) => ({ ...s, value: defaultValue, isLoading: false }));
551
555
  return;
552
556
  }
553
557
  const key = contentKeyRef.current;
554
- const queryKey = [SITE_CONTENT_QUERY_KEY, key];
558
+ const locCurrent = localeRef.current;
559
+ const qKey = queryKey(key, locCurrent);
555
560
  const queryFn = async () => {
556
561
  try {
557
- const content = await client.get(
558
- `/api/site/content/${encodeURIComponent(key)}`
559
- );
562
+ const url = `/api/site/content/${encodeURIComponent(key)}` + (locCurrent ? `?locale=${encodeURIComponent(locCurrent)}` : "");
563
+ const content = await client.get(url);
560
564
  if (!content) return null;
561
565
  return {
562
566
  value: content.value ?? "",
@@ -570,7 +574,7 @@ function useSiteContentQuery(contentKey, options) {
570
574
  };
571
575
  let cancelled = false;
572
576
  queryClient.fetchQuery({
573
- queryKey,
577
+ queryKey: qKey,
574
578
  queryFn,
575
579
  staleTime: 1e3 * 60 * 5,
576
580
  retry: 1
@@ -586,8 +590,11 @@ function useSiteContentQuery(contentKey, options) {
586
590
  setState((s) => ({ ...s, value: defaultValue, isLoading: false }));
587
591
  });
588
592
  const unsub = queryClient.getQueryCache().subscribe((event) => {
589
- if (event?.type === "updated" && event?.query?.queryKey[1] === key) {
590
- 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);
591
598
  if (cached && !cancelled) {
592
599
  setState({
593
600
  value: cached.value,
@@ -601,7 +608,7 @@ function useSiteContentQuery(contentKey, options) {
601
608
  cancelled = true;
602
609
  unsub();
603
610
  };
604
- }, [queryClient, contentKey, defaultValue, client]);
611
+ }, [queryClient, contentKey, defaultValue, client, loc]);
605
612
  return state;
606
613
  }
607
614
 
@@ -679,13 +686,17 @@ function Editable({
679
686
  defaultValue,
680
687
  as = "span",
681
688
  multiline = false,
682
- className
689
+ className,
690
+ locale: localeProp
683
691
  }) {
684
692
  const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
693
+ const { locale: contextLocale } = useFoxPixelContext();
694
+ const locale = localeProp ?? contextLocale;
685
695
  const isEditMode = useEditModeMessaging();
686
696
  const sendEditRequest = useSendEditRequest();
687
697
  const { value, isLoading, contentType } = useSiteContentQuery(contentKey, {
688
- defaultValue
698
+ defaultValue,
699
+ locale
689
700
  });
690
701
  const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
691
702
  (0, import_react8.useEffect)(() => {
@@ -703,11 +714,13 @@ function Editable({
703
714
  contentKey,
704
715
  value,
705
716
  contentType?.toLowerCase() || "text",
706
- section
717
+ section,
718
+ void 0,
719
+ locale
707
720
  );
708
721
  }
709
722
  },
710
- [isEditMode, contentKey, value, contentType, section, sendEditRequest]
723
+ [isEditMode, contentKey, value, contentType, section, sendEditRequest, locale]
711
724
  );
712
725
  if (isLoading) {
713
726
  return (0, import_react8.createElement)(as, {
@@ -780,13 +793,17 @@ function EditableHTML({
780
793
  contentKey,
781
794
  defaultValue,
782
795
  as = "div",
783
- className
796
+ className,
797
+ locale: localeProp
784
798
  }) {
785
799
  const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
800
+ const { locale: contextLocale } = useFoxPixelContext();
801
+ const locale = localeProp ?? contextLocale;
786
802
  const isEditMode = useEditModeMessaging();
787
803
  const sendEditRequest = useSendEditRequest();
788
804
  const { value, isLoading } = useSiteContentQuery(contentKey, {
789
- defaultValue
805
+ defaultValue,
806
+ locale
790
807
  });
791
808
  const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
792
809
  (0, import_react8.useEffect)(() => {
@@ -800,10 +817,10 @@ function EditableHTML({
800
817
  if (isEditMode) {
801
818
  e.preventDefault();
802
819
  e.stopPropagation();
803
- sendEditRequest(contentKey, value, "html", section);
820
+ sendEditRequest(contentKey, value, "html", section, void 0, locale);
804
821
  }
805
822
  },
806
- [isEditMode, contentKey, value, section, sendEditRequest]
823
+ [isEditMode, contentKey, value, section, sendEditRequest, locale]
807
824
  );
808
825
  if (isLoading) {
809
826
  return (0, import_react8.createElement)(as, {
@@ -852,13 +869,17 @@ function EditableImage({
852
869
  className,
853
870
  width,
854
871
  height,
855
- priority = false
872
+ priority = false,
873
+ locale: localeProp
856
874
  }) {
857
875
  const [isHovered, setIsHovered] = (0, import_react8.useState)(false);
876
+ const { locale: contextLocale } = useFoxPixelContext();
877
+ const locale = localeProp ?? contextLocale;
858
878
  const isEditMode = useEditModeMessaging();
859
879
  const sendEditRequest = useSendEditRequest();
860
880
  const { value: src, isLoading } = useSiteContentQuery(contentKey, {
861
- defaultValue
881
+ defaultValue,
882
+ locale
862
883
  });
863
884
  const section = contentKey.includes(".") ? contentKey.split(".")[0] : void 0;
864
885
  (0, import_react8.useEffect)(() => {
@@ -872,10 +893,10 @@ function EditableImage({
872
893
  if (isEditMode) {
873
894
  e.preventDefault();
874
895
  e.stopPropagation();
875
- sendEditRequest(contentKey, src, "image", section);
896
+ sendEditRequest(contentKey, src, "image", section, void 0, locale);
876
897
  }
877
898
  },
878
- [isEditMode, contentKey, src, section, sendEditRequest]
899
+ [isEditMode, contentKey, src, section, sendEditRequest, locale]
879
900
  );
880
901
  if (isLoading) {
881
902
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -1021,20 +1042,20 @@ function useContactCapture() {
1021
1042
  // src/hooks/useSiteContent.ts
1022
1043
  var import_react12 = require("react");
1023
1044
  function useSiteContent(contentKey, options = {}) {
1024
- const { defaultValue = "", fetchOnMount = true } = options;
1045
+ const { defaultValue = "", fetchOnMount = true, locale } = options;
1025
1046
  const { client } = useFoxPixelContext();
1026
1047
  const { user, hasPermission } = useAuth();
1027
1048
  const [data, setData] = (0, import_react12.useState)(null);
1028
1049
  const [isLoading, setIsLoading] = (0, import_react12.useState)(fetchOnMount);
1029
1050
  const [error, setError] = (0, import_react12.useState)(null);
1030
1051
  const canEdit = user !== null && hasPermission("site:content:update");
1052
+ const queryLocale = locale != null && locale !== "" ? locale : void 0;
1031
1053
  const fetchContent = (0, import_react12.useCallback)(async () => {
1032
1054
  try {
1033
1055
  setIsLoading(true);
1034
1056
  setError(null);
1035
- const content = await client.get(
1036
- `/api/site/content/${encodeURIComponent(contentKey)}`
1037
- );
1057
+ const url = `/api/site/content/${encodeURIComponent(contentKey)}` + (queryLocale ? `?locale=${encodeURIComponent(queryLocale)}` : "");
1058
+ const content = await client.get(url);
1038
1059
  setData(content);
1039
1060
  } catch (err) {
1040
1061
  if (err?.status === 404) {
@@ -1045,20 +1066,22 @@ function useSiteContent(contentKey, options = {}) {
1045
1066
  } finally {
1046
1067
  setIsLoading(false);
1047
1068
  }
1048
- }, [client, contentKey]);
1069
+ }, [client, contentKey, queryLocale]);
1049
1070
  const updateContent = (0, import_react12.useCallback)(async (newValue) => {
1050
1071
  try {
1051
1072
  setError(null);
1073
+ const body = { value: newValue };
1074
+ if (queryLocale) body.locale = queryLocale;
1052
1075
  const updated = await client.put(
1053
1076
  `/api/site/content/${encodeURIComponent(contentKey)}`,
1054
- { value: newValue }
1077
+ body
1055
1078
  );
1056
1079
  setData(updated);
1057
1080
  } catch (err) {
1058
1081
  setError(err);
1059
1082
  throw err;
1060
1083
  }
1061
- }, [client, contentKey]);
1084
+ }, [client, contentKey, queryLocale]);
1062
1085
  (0, import_react12.useEffect)(() => {
1063
1086
  if (fetchOnMount) {
1064
1087
  fetchContent();
@@ -1076,11 +1099,12 @@ function useSiteContent(contentKey, options = {}) {
1076
1099
  };
1077
1100
  }
1078
1101
  function useSiteContents(contentKeys, options = {}) {
1079
- const { defaults = {} } = options;
1102
+ const { defaults = {}, locale } = options;
1080
1103
  const { client } = useFoxPixelContext();
1081
1104
  const [data, setData] = (0, import_react12.useState)({});
1082
1105
  const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
1083
1106
  const [error, setError] = (0, import_react12.useState)(null);
1107
+ const body = locale != null && locale !== "" ? { keys: contentKeys, locale } : contentKeys;
1084
1108
  const fetchContents = (0, import_react12.useCallback)(async () => {
1085
1109
  if (contentKeys.length === 0) {
1086
1110
  setData({});
@@ -1092,7 +1116,7 @@ function useSiteContents(contentKeys, options = {}) {
1092
1116
  setError(null);
1093
1117
  const contents = await client.post(
1094
1118
  "/api/site/content/batch",
1095
- contentKeys
1119
+ body
1096
1120
  );
1097
1121
  setData(contents);
1098
1122
  } catch (err) {
@@ -1100,7 +1124,7 @@ function useSiteContents(contentKeys, options = {}) {
1100
1124
  } finally {
1101
1125
  setIsLoading(false);
1102
1126
  }
1103
- }, [client, contentKeys.join(",")]);
1127
+ }, [client, contentKeys.join(","), locale]);
1104
1128
  (0, import_react12.useEffect)(() => {
1105
1129
  fetchContents();
1106
1130
  }, [fetchContents]);
@@ -1119,25 +1143,25 @@ function useSiteContents(contentKeys, options = {}) {
1119
1143
  refetch: fetchContents
1120
1144
  };
1121
1145
  }
1122
- function useSiteContentSection(section) {
1146
+ function useSiteContentSection(section, options = {}) {
1147
+ const { locale } = options;
1123
1148
  const { client } = useFoxPixelContext();
1124
1149
  const [contents, setContents] = (0, import_react12.useState)([]);
1125
1150
  const [isLoading, setIsLoading] = (0, import_react12.useState)(true);
1126
1151
  const [error, setError] = (0, import_react12.useState)(null);
1152
+ const url = `/api/site/content/section/${encodeURIComponent(section)}` + (locale != null && locale !== "" ? `?locale=${encodeURIComponent(locale)}` : "");
1127
1153
  const fetchContents = (0, import_react12.useCallback)(async () => {
1128
1154
  try {
1129
1155
  setIsLoading(true);
1130
1156
  setError(null);
1131
- const data = await client.get(
1132
- `/api/site/content/section/${encodeURIComponent(section)}`
1133
- );
1157
+ const data = await client.get(url);
1134
1158
  setContents(data);
1135
1159
  } catch (err) {
1136
1160
  setError(err);
1137
1161
  } finally {
1138
1162
  setIsLoading(false);
1139
1163
  }
1140
- }, [client, section]);
1164
+ }, [client, section, locale]);
1141
1165
  (0, import_react12.useEffect)(() => {
1142
1166
  fetchContents();
1143
1167
  }, [fetchContents]);
@@ -1150,33 +1174,39 @@ function useSiteContentSection(section) {
1150
1174
  }
1151
1175
 
1152
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
+ }
1153
1180
  async function prefetchSiteContent(queryClient, options) {
1154
- const { apiUrl, apiKey, tenantId, contentKeys } = options;
1181
+ const { apiUrl, apiKey, tenantId, contentKeys, locale } = options;
1155
1182
  const client = new FoxPixelHttpClient({ apiUrl, apiKey, tenantId });
1156
- await Promise.all(
1157
- contentKeys.map(async (contentKey) => {
1158
- try {
1159
- const content = await client.get(
1160
- `/api/site/content/${encodeURIComponent(contentKey)}`
1161
- );
1162
- queryClient.setQueryData(
1163
- [SITE_CONTENT_QUERY_KEY, contentKey],
1164
- {
1165
- value: content?.value ?? "",
1166
- 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
+ }
1167
1204
  }
1168
- );
1169
- } catch (err) {
1170
- const status = err?.response?.status;
1171
- if (status === 404) {
1172
- queryClient.setQueryData([SITE_CONTENT_QUERY_KEY, contentKey], {
1173
- value: "",
1174
- contentType: "TEXT"
1175
- });
1176
- }
1177
- }
1178
- })
1179
- );
1205
+ })()
1206
+ );
1207
+ }
1208
+ }
1209
+ await Promise.all(tasks);
1180
1210
  }
1181
1211
 
1182
1212
  // src/blog/hooks.ts