@nok-integration/storefront-react 0.19.25 → 0.19.27

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.ts CHANGED
@@ -3537,11 +3537,23 @@ export declare interface Market {
3537
3537
  * Swipeable image gallery with dot indicators. Reduced-motion aware (smooth-scroll is
3538
3538
  * disabled globally under prefers-reduced-motion). Keyboard: dots are real buttons.
3539
3539
  */
3540
- export declare function MediaGallery({ media, alt }: MediaGalleryProps): JSX.Element;
3540
+ export declare function MediaGallery({ media, alt, focus }: MediaGalleryProps): JSX.Element;
3541
3541
 
3542
3542
  export declare interface MediaGalleryProps {
3543
3543
  media: string[];
3544
3544
  alt: string;
3545
+ /**
3546
+ * Jump to this slide whenever the value changes.
3547
+ *
3548
+ * How the PDP's colour tiles move the gallery: the set of images does not change when a
3549
+ * colour is picked, so the gallery is not rebuilt and nothing is hidden — it simply
3550
+ * lands on the shot of the colour that was chosen.
3551
+ *
3552
+ * Instant rather than animated, deliberately. Animating means scrolling THROUGH the
3553
+ * other colourways' photographs to reach the chosen one, so picking White would sweep
3554
+ * past the black shirts on the way. That is the very thing this is here to stop.
3555
+ */
3556
+ focus?: number;
3545
3557
  }
3546
3558
 
3547
3559
  export declare function minorUnitExponent(currency: string): number;
package/dist/index.mjs CHANGED
@@ -9316,7 +9316,7 @@ const styles$i = {
9316
9316
  dot: dot$2,
9317
9317
  dotActive: dotActive$2
9318
9318
  };
9319
- function MediaGallery({ media: media2, alt }) {
9319
+ function MediaGallery({ media: media2, alt, focus }) {
9320
9320
  const t = useT();
9321
9321
  const trackRef = useRef(null);
9322
9322
  const [active, setActive] = useState(0);
@@ -9339,6 +9339,13 @@ function MediaGallery({ media: media2, alt }) {
9339
9339
  track2.scrollLeft = 0;
9340
9340
  setActive(0);
9341
9341
  }, [mediaKey]);
9342
+ useEffect(() => {
9343
+ if (focus === void 0 || focus < 0) return;
9344
+ const track2 = trackRef.current;
9345
+ if (!track2) return;
9346
+ track2.scrollLeft = focus * track2.clientWidth;
9347
+ setActive(focus);
9348
+ }, [focus]);
9342
9349
  function goTo(index) {
9343
9350
  const track2 = trackRef.current;
9344
9351
  if (!track2) return;
@@ -10037,14 +10044,15 @@ function ProductDetail({ product, inventory: inventory2, recommendations = [] })
10037
10044
  const colourSoldOut = hasColours && selectedColour ? !colours.find((c) => c.value === selectedColour)?.available : false;
10038
10045
  const purchasable = anyInStock && !colourSoldOut;
10039
10046
  const colourImage = useMemo(() => selectedColour ? baseColours.find((c) => c.value === selectedColour)?.image : void 0, [baseColours, selectedColour]);
10040
- const shootColour = baseColours[0]?.value;
10041
10047
  const media2 = useMemo(() => {
10042
- if (!colourImage) return product.media;
10043
- if (selectedColour === shootColour) {
10044
- return [colourImage, ...product.media.filter((m) => m !== colourImage)];
10045
- }
10046
- return [colourImage];
10047
- }, [colourImage, product.media, selectedColour, shootColour]);
10048
+ const swatches2 = baseColours.map((c) => c.image).filter((src) => typeof src === "string" && src.length > 0);
10049
+ return [...swatches2, ...product.media.filter((m) => !swatches2.includes(m))];
10050
+ }, [baseColours, product.media]);
10051
+ const focus = useMemo(() => {
10052
+ if (!colourImage) return void 0;
10053
+ const at = media2.indexOf(colourImage);
10054
+ return at >= 0 ? at : void 0;
10055
+ }, [colourImage, media2]);
10048
10056
  function selectSize(value2) {
10049
10057
  setSelectedSize(value2);
10050
10058
  setSizeError(false);
@@ -10191,7 +10199,10 @@ function ProductDetail({ product, inventory: inventory2, recommendations = [] })
10191
10199
  className: styles$a.page,
10192
10200
  children: [jsx(MediaGallery, {
10193
10201
  media: media2,
10194
- alt: product.title
10202
+ alt: product.title,
10203
+ ...focus !== void 0 ? {
10204
+ focus
10205
+ } : {}
10195
10206
  }), jsxs("div", {
10196
10207
  className: styles$a.content,
10197
10208
  children: [jsxs("div", {
@@ -10666,37 +10677,50 @@ function CategoryView({ code, initialData, onNotFound }) {
10666
10677
  function ProductView({ sku, initialData, showRecommendations = true, onNotFound }) {
10667
10678
  const t = useT();
10668
10679
  const { api, market, emit } = useShop();
10669
- const [product, setProduct] = useState(initialData?.product ?? null);
10670
- const [inventory2, setInventory] = useState(initialData?.inventory ?? null);
10671
- const [recommendations, setRecommendations] = useState([]);
10672
- const [error2, setError] = useState();
10673
- const [loading, setLoading] = useState(!initialData);
10680
+ const [loaded, setLoaded] = useState(initialData ? {
10681
+ sku,
10682
+ product: initialData.product,
10683
+ inventory: initialData.inventory ?? null,
10684
+ recommendations: []
10685
+ } : null);
10686
+ const [failure, setFailure] = useState(null);
10687
+ const current2 = loaded?.sku === sku ? loaded : null;
10688
+ const error2 = failure?.sku === sku ? failure.code : void 0;
10674
10689
  useEffect(() => {
10675
10690
  let live = true;
10676
- setError(void 0);
10677
10691
  (async () => {
10678
10692
  try {
10679
10693
  const p = await api.catalog.getProduct(sku, market);
10680
10694
  if (!live) return;
10681
- setProduct(p);
10695
+ setLoaded({
10696
+ sku,
10697
+ product: p,
10698
+ inventory: null,
10699
+ recommendations: []
10700
+ });
10682
10701
  const hasVariants = (p.variants?.length ?? 0) > 0;
10683
10702
  const [inv, recs] = await Promise.allSettled([hasVariants ? Promise.resolve(null) : api.inventory.get(sku), showRecommendations ? api.catalog.getRecommendations(sku, market) : Promise.resolve({
10684
10703
  data: []
10685
10704
  })]);
10686
10705
  if (!live) return;
10687
- setInventory(inv.status === "fulfilled" ? inv.value : null);
10688
- setRecommendations(recs.status === "fulfilled" ? recs.value.data : []);
10706
+ setLoaded({
10707
+ sku,
10708
+ product: p,
10709
+ inventory: inv.status === "fulfilled" ? inv.value : null,
10710
+ recommendations: recs.status === "fulfilled" ? recs.value.data : []
10711
+ });
10689
10712
  } catch (err) {
10690
10713
  if (!live) return;
10691
10714
  const c = codeOf(err);
10692
- setError(c);
10715
+ setFailure({
10716
+ sku,
10717
+ code: c
10718
+ });
10693
10719
  emit({
10694
10720
  type: "error",
10695
10721
  code: c,
10696
10722
  scope: "product"
10697
10723
  });
10698
- } finally {
10699
- if (live) setLoading(false);
10700
10724
  }
10701
10725
  })();
10702
10726
  return () => {
@@ -10717,26 +10741,23 @@ function ProductView({ sku, initialData, showRecommendations = true, onNotFound
10717
10741
  });
10718
10742
  return unavailable;
10719
10743
  }
10720
- if (product) {
10744
+ if (current2) {
10721
10745
  return jsx(ProductDetail, {
10722
- product,
10723
- inventory: inventory2,
10724
- recommendations
10746
+ product: current2.product,
10747
+ inventory: current2.inventory,
10748
+ recommendations: current2.recommendations
10725
10749
  });
10726
10750
  }
10727
- if (loading) return jsx(ScreenSkeleton, {
10751
+ if (!error2) return jsx(ScreenSkeleton, {
10728
10752
  tone: "light"
10729
10753
  });
10730
- if (error2) {
10731
- return jsx(ShopSurface, {
10732
- tone: "light",
10733
- fill: true,
10734
- children: jsx(ErrorState, {
10735
- code: error2
10736
- })
10737
- });
10738
- }
10739
- return unavailable;
10754
+ return jsx(ShopSurface, {
10755
+ tone: "light",
10756
+ fill: true,
10757
+ children: jsx(ErrorState, {
10758
+ code: error2
10759
+ })
10760
+ });
10740
10761
  }
10741
10762
  const showcase = "_showcase_1qnt2_44";
10742
10763
  const panel = "_panel_1qnt2_48";
@@ -9316,7 +9316,7 @@ const styles$i = {
9316
9316
  dot: dot$2,
9317
9317
  dotActive: dotActive$2
9318
9318
  };
9319
- function MediaGallery({ media: media2, alt }) {
9319
+ function MediaGallery({ media: media2, alt, focus }) {
9320
9320
  const t = useT();
9321
9321
  const trackRef = useRef(null);
9322
9322
  const [active, setActive] = useState(0);
@@ -9339,6 +9339,13 @@ function MediaGallery({ media: media2, alt }) {
9339
9339
  track2.scrollLeft = 0;
9340
9340
  setActive(0);
9341
9341
  }, [mediaKey]);
9342
+ useEffect(() => {
9343
+ if (focus === void 0 || focus < 0) return;
9344
+ const track2 = trackRef.current;
9345
+ if (!track2) return;
9346
+ track2.scrollLeft = focus * track2.clientWidth;
9347
+ setActive(focus);
9348
+ }, [focus]);
9342
9349
  function goTo(index) {
9343
9350
  const track2 = trackRef.current;
9344
9351
  if (!track2) return;
@@ -10037,14 +10044,15 @@ function ProductDetail({ product, inventory: inventory2, recommendations = [] })
10037
10044
  const colourSoldOut = hasColours && selectedColour ? !colours.find((c) => c.value === selectedColour)?.available : false;
10038
10045
  const purchasable = anyInStock && !colourSoldOut;
10039
10046
  const colourImage = useMemo(() => selectedColour ? baseColours.find((c) => c.value === selectedColour)?.image : void 0, [baseColours, selectedColour]);
10040
- const shootColour = baseColours[0]?.value;
10041
10047
  const media2 = useMemo(() => {
10042
- if (!colourImage) return product.media;
10043
- if (selectedColour === shootColour) {
10044
- return [colourImage, ...product.media.filter((m) => m !== colourImage)];
10045
- }
10046
- return [colourImage];
10047
- }, [colourImage, product.media, selectedColour, shootColour]);
10048
+ const swatches2 = baseColours.map((c) => c.image).filter((src) => typeof src === "string" && src.length > 0);
10049
+ return [...swatches2, ...product.media.filter((m) => !swatches2.includes(m))];
10050
+ }, [baseColours, product.media]);
10051
+ const focus = useMemo(() => {
10052
+ if (!colourImage) return void 0;
10053
+ const at = media2.indexOf(colourImage);
10054
+ return at >= 0 ? at : void 0;
10055
+ }, [colourImage, media2]);
10048
10056
  function selectSize(value2) {
10049
10057
  setSelectedSize(value2);
10050
10058
  setSizeError(false);
@@ -10191,7 +10199,10 @@ function ProductDetail({ product, inventory: inventory2, recommendations = [] })
10191
10199
  className: styles$a.page,
10192
10200
  children: [jsx(MediaGallery, {
10193
10201
  media: media2,
10194
- alt: product.title
10202
+ alt: product.title,
10203
+ ...focus !== void 0 ? {
10204
+ focus
10205
+ } : {}
10195
10206
  }), jsxs("div", {
10196
10207
  className: styles$a.content,
10197
10208
  children: [jsxs("div", {
@@ -10666,37 +10677,50 @@ function CategoryView({ code, initialData, onNotFound }) {
10666
10677
  function ProductView({ sku, initialData, showRecommendations = true, onNotFound }) {
10667
10678
  const t = useT();
10668
10679
  const { api, market, emit } = useShop();
10669
- const [product, setProduct] = useState(initialData?.product ?? null);
10670
- const [inventory2, setInventory] = useState(initialData?.inventory ?? null);
10671
- const [recommendations, setRecommendations] = useState([]);
10672
- const [error2, setError] = useState();
10673
- const [loading, setLoading] = useState(!initialData);
10680
+ const [loaded, setLoaded] = useState(initialData ? {
10681
+ sku,
10682
+ product: initialData.product,
10683
+ inventory: initialData.inventory ?? null,
10684
+ recommendations: []
10685
+ } : null);
10686
+ const [failure, setFailure] = useState(null);
10687
+ const current2 = loaded?.sku === sku ? loaded : null;
10688
+ const error2 = failure?.sku === sku ? failure.code : void 0;
10674
10689
  useEffect(() => {
10675
10690
  let live = true;
10676
- setError(void 0);
10677
10691
  (async () => {
10678
10692
  try {
10679
10693
  const p = await api.catalog.getProduct(sku, market);
10680
10694
  if (!live) return;
10681
- setProduct(p);
10695
+ setLoaded({
10696
+ sku,
10697
+ product: p,
10698
+ inventory: null,
10699
+ recommendations: []
10700
+ });
10682
10701
  const hasVariants = (p.variants?.length ?? 0) > 0;
10683
10702
  const [inv, recs] = await Promise.allSettled([hasVariants ? Promise.resolve(null) : api.inventory.get(sku), showRecommendations ? api.catalog.getRecommendations(sku, market) : Promise.resolve({
10684
10703
  data: []
10685
10704
  })]);
10686
10705
  if (!live) return;
10687
- setInventory(inv.status === "fulfilled" ? inv.value : null);
10688
- setRecommendations(recs.status === "fulfilled" ? recs.value.data : []);
10706
+ setLoaded({
10707
+ sku,
10708
+ product: p,
10709
+ inventory: inv.status === "fulfilled" ? inv.value : null,
10710
+ recommendations: recs.status === "fulfilled" ? recs.value.data : []
10711
+ });
10689
10712
  } catch (err) {
10690
10713
  if (!live) return;
10691
10714
  const c = codeOf(err);
10692
- setError(c);
10715
+ setFailure({
10716
+ sku,
10717
+ code: c
10718
+ });
10693
10719
  emit({
10694
10720
  type: "error",
10695
10721
  code: c,
10696
10722
  scope: "product"
10697
10723
  });
10698
- } finally {
10699
- if (live) setLoading(false);
10700
10724
  }
10701
10725
  })();
10702
10726
  return () => {
@@ -10717,26 +10741,23 @@ function ProductView({ sku, initialData, showRecommendations = true, onNotFound
10717
10741
  });
10718
10742
  return unavailable;
10719
10743
  }
10720
- if (product) {
10744
+ if (current2) {
10721
10745
  return jsx(ProductDetail, {
10722
- product,
10723
- inventory: inventory2,
10724
- recommendations
10746
+ product: current2.product,
10747
+ inventory: current2.inventory,
10748
+ recommendations: current2.recommendations
10725
10749
  });
10726
10750
  }
10727
- if (loading) return jsx(ScreenSkeleton, {
10751
+ if (!error2) return jsx(ScreenSkeleton, {
10728
10752
  tone: "light"
10729
10753
  });
10730
- if (error2) {
10731
- return jsx(ShopSurface, {
10732
- tone: "light",
10733
- fill: true,
10734
- children: jsx(ErrorState, {
10735
- code: error2
10736
- })
10737
- });
10738
- }
10739
- return unavailable;
10754
+ return jsx(ShopSurface, {
10755
+ tone: "light",
10756
+ fill: true,
10757
+ children: jsx(ErrorState, {
10758
+ code: error2
10759
+ })
10760
+ });
10740
10761
  }
10741
10762
  const showcase = "_showcase_1qnt2_44";
10742
10763
  const panel = "_panel_1qnt2_48";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nok-integration/storefront-react",
3
- "version": "0.19.25",
3
+ "version": "0.19.27",
4
4
  "description": "Mountable React storefront components: catalogue, product detail and cart, mounted directly into a host DOM tree. No iframe.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {