@seekora-ai/ui-sdk-react 0.2.11 → 0.2.13

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.
Files changed (120) hide show
  1. package/dist/components/InfiniteHits.d.ts +2 -0
  2. package/dist/components/InfiniteHits.d.ts.map +1 -1
  3. package/dist/components/InfiniteHits.js +6 -3
  4. package/dist/components/QuerySuggestions.d.ts +2 -0
  5. package/dist/components/QuerySuggestions.d.ts.map +1 -1
  6. package/dist/components/QuerySuggestions.js +4 -3
  7. package/dist/components/QuerySuggestionsDropdown.d.ts +1 -1
  8. package/dist/components/QuerySuggestionsDropdown.d.ts.map +1 -1
  9. package/dist/components/QuerySuggestionsDropdown.js +4 -4
  10. package/dist/components/Recommendations.d.ts +6 -0
  11. package/dist/components/Recommendations.d.ts.map +1 -1
  12. package/dist/components/Recommendations.js +12 -6
  13. package/dist/components/RichQuerySuggestions.d.ts +4 -0
  14. package/dist/components/RichQuerySuggestions.d.ts.map +1 -1
  15. package/dist/components/RichQuerySuggestions.js +2 -3
  16. package/dist/components/SearchBar.d.ts +2 -0
  17. package/dist/components/SearchBar.d.ts.map +1 -1
  18. package/dist/components/SearchBar.js +5 -9
  19. package/dist/components/SearchResults.d.ts +2 -0
  20. package/dist/components/SearchResults.d.ts.map +1 -1
  21. package/dist/components/SearchResults.js +4 -2
  22. package/dist/components/primitives/ActionButtons.d.ts +27 -0
  23. package/dist/components/primitives/ActionButtons.d.ts.map +1 -0
  24. package/dist/components/primitives/ActionButtons.js +78 -0
  25. package/dist/components/primitives/AnalyticsProvider.d.ts +22 -0
  26. package/dist/components/primitives/AnalyticsProvider.d.ts.map +1 -0
  27. package/dist/components/primitives/AnalyticsProvider.js +87 -0
  28. package/dist/components/primitives/BadgeList.d.ts +14 -0
  29. package/dist/components/primitives/BadgeList.d.ts.map +1 -0
  30. package/dist/components/primitives/BadgeList.js +45 -0
  31. package/dist/components/primitives/ImageDisplay.d.ts +10 -1
  32. package/dist/components/primitives/ImageDisplay.d.ts.map +1 -1
  33. package/dist/components/primitives/ImageDisplay.js +49 -9
  34. package/dist/components/primitives/ImageZoom.d.ts +33 -0
  35. package/dist/components/primitives/ImageZoom.d.ts.map +1 -0
  36. package/dist/components/primitives/ImageZoom.js +357 -0
  37. package/dist/components/primitives/PriceDisplay.d.ts +21 -0
  38. package/dist/components/primitives/PriceDisplay.d.ts.map +1 -0
  39. package/dist/components/primitives/PriceDisplay.js +44 -0
  40. package/dist/components/primitives/RatingDisplay.d.ts +43 -0
  41. package/dist/components/primitives/RatingDisplay.d.ts.map +1 -0
  42. package/dist/components/primitives/RatingDisplay.js +114 -0
  43. package/dist/components/primitives/VariantSelector.d.ts +30 -0
  44. package/dist/components/primitives/VariantSelector.d.ts.map +1 -0
  45. package/dist/components/primitives/VariantSelector.js +162 -0
  46. package/dist/components/primitives/VariantSwatches.d.ts +28 -0
  47. package/dist/components/primitives/VariantSwatches.d.ts.map +1 -0
  48. package/dist/components/primitives/VariantSwatches.js +173 -0
  49. package/dist/components/primitives/index.d.ts +9 -0
  50. package/dist/components/primitives/index.d.ts.map +1 -1
  51. package/dist/components/primitives/index.js +9 -0
  52. package/dist/components/primitives/withAnalytics.d.ts +24 -0
  53. package/dist/components/primitives/withAnalytics.d.ts.map +1 -0
  54. package/dist/components/primitives/withAnalytics.js +73 -0
  55. package/dist/components/product-page/ProductInfo.d.ts +25 -2
  56. package/dist/components/product-page/ProductInfo.d.ts.map +1 -1
  57. package/dist/components/product-page/ProductInfo.js +20 -5
  58. package/dist/components/section-primitives/SectionItemGrid.d.ts +3 -1
  59. package/dist/components/section-primitives/SectionItemGrid.d.ts.map +1 -1
  60. package/dist/components/section-primitives/SectionItemGrid.js +3 -2
  61. package/dist/components/suggestions/AmazonDropdown.d.ts.map +1 -1
  62. package/dist/components/suggestions/AmazonDropdown.js +2 -2
  63. package/dist/components/suggestions/GoogleDropdown.d.ts.map +1 -1
  64. package/dist/components/suggestions/GoogleDropdown.js +2 -2
  65. package/dist/components/suggestions/MinimalDropdown.d.ts.map +1 -1
  66. package/dist/components/suggestions/MinimalDropdown.js +2 -2
  67. package/dist/components/suggestions/MobileSheetDropdown.d.ts.map +1 -1
  68. package/dist/components/suggestions/MobileSheetDropdown.js +2 -2
  69. package/dist/components/suggestions/PinterestDropdown.d.ts.map +1 -1
  70. package/dist/components/suggestions/PinterestDropdown.js +2 -2
  71. package/dist/components/suggestions/ShopifyDropdown.d.ts.map +1 -1
  72. package/dist/components/suggestions/ShopifyDropdown.js +2 -2
  73. package/dist/components/suggestions/SpotlightDropdown.d.ts.map +1 -1
  74. package/dist/components/suggestions/SpotlightDropdown.js +2 -2
  75. package/dist/components/suggestions/SuggestionSearchBar.d.ts.map +1 -1
  76. package/dist/components/suggestions/SuggestionSearchBar.js +1 -0
  77. package/dist/components/suggestions/types.d.ts +26 -0
  78. package/dist/components/suggestions/types.d.ts.map +1 -1
  79. package/dist/components/suggestions/utils.d.ts +37 -0
  80. package/dist/components/suggestions/utils.d.ts.map +1 -1
  81. package/dist/components/suggestions/utils.js +118 -0
  82. package/dist/components/suggestions-primitives/ItemCard.d.ts +10 -1
  83. package/dist/components/suggestions-primitives/ItemCard.d.ts.map +1 -1
  84. package/dist/components/suggestions-primitives/ItemCard.js +20 -6
  85. package/dist/components/suggestions-primitives/ProductCard.d.ts +27 -3
  86. package/dist/components/suggestions-primitives/ProductCard.d.ts.map +1 -1
  87. package/dist/components/suggestions-primitives/ProductCard.js +124 -17
  88. package/dist/components/suggestions-primitives/ProductCardLayouts.d.ts +44 -0
  89. package/dist/components/suggestions-primitives/ProductCardLayouts.d.ts.map +1 -0
  90. package/dist/components/suggestions-primitives/ProductCardLayouts.js +105 -0
  91. package/dist/components/suggestions-primitives/ProductGrid.d.ts +6 -1
  92. package/dist/components/suggestions-primitives/ProductGrid.d.ts.map +1 -1
  93. package/dist/components/suggestions-primitives/ProductGrid.js +2 -2
  94. package/dist/components/suggestions-primitives/SuggestionList.d.ts +8 -1
  95. package/dist/components/suggestions-primitives/SuggestionList.d.ts.map +1 -1
  96. package/dist/components/suggestions-primitives/SuggestionList.js +7 -4
  97. package/dist/components/suggestions-primitives/SuggestionsDropdownComposition.d.ts.map +1 -1
  98. package/dist/components/suggestions-primitives/SuggestionsDropdownComposition.js +0 -2
  99. package/dist/docsearch/components/Results.d.ts +3 -1
  100. package/dist/docsearch/components/Results.d.ts.map +1 -1
  101. package/dist/docsearch/components/Results.js +6 -2
  102. package/dist/hooks/useProductAnalytics.d.ts +49 -0
  103. package/dist/hooks/useProductAnalytics.d.ts.map +1 -0
  104. package/dist/hooks/useProductAnalytics.js +116 -0
  105. package/dist/hooks/useQuerySuggestionsEnhanced.js +2 -1
  106. package/dist/hooks/useSuggestionsAnalytics.d.ts.map +1 -1
  107. package/dist/hooks/useSuggestionsAnalytics.js +6 -0
  108. package/dist/hooks/useVariantSelection.d.ts +28 -0
  109. package/dist/hooks/useVariantSelection.d.ts.map +1 -0
  110. package/dist/hooks/useVariantSelection.js +44 -0
  111. package/dist/index.d.ts +8 -3
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +5 -1
  114. package/dist/index.umd.js +1 -1
  115. package/dist/src/index.d.ts +1138 -681
  116. package/dist/src/index.esm.js +2407 -723
  117. package/dist/src/index.esm.js.map +1 -1
  118. package/dist/src/index.js +2423 -722
  119. package/dist/src/index.js.map +1 -1
  120. package/package.json +3 -3
@@ -0,0 +1,87 @@
1
+ /**
2
+ * AnalyticsProvider – context + delegated event listener for data-seekora-* attribute-based analytics
3
+ *
4
+ * Installs a delegated click listener on a container. Any descendant with
5
+ * data-seekora-track="click"|"add-to-cart" and data-seekora-product-id="..."
6
+ * automatically fires analytics events without React wiring.
7
+ */
8
+ import React, { useRef, useEffect, useCallback, createContext, useContext } from 'react';
9
+ import { log } from '@seekora-ai/ui-sdk-core';
10
+ const AnalyticsContext = createContext(null);
11
+ export const useAnalyticsProvider = () => useContext(AnalyticsContext);
12
+ export function AnalyticsProvider({ client, context, children }) {
13
+ const containerRef = useRef(null);
14
+ const sendEvent = useCallback(async (eventName, metadata) => {
15
+ try {
16
+ await client.trackEvent?.({
17
+ event_name: eventName,
18
+ metadata: {
19
+ timestamp: Date.now(),
20
+ source: 'analytics_provider_delegation',
21
+ ...metadata,
22
+ },
23
+ }, context);
24
+ log.verbose(`AnalyticsProvider: ${eventName}`, metadata);
25
+ }
26
+ catch (error) {
27
+ log.warn(`AnalyticsProvider: Failed to track ${eventName}`, { error });
28
+ }
29
+ }, [client, context]);
30
+ useEffect(() => {
31
+ const container = containerRef.current;
32
+ if (!container)
33
+ return;
34
+ const handleClick = (e) => {
35
+ const target = e.target;
36
+ // Walk up from target to find element with data-seekora-track
37
+ const tracked = target.closest('[data-seekora-track]');
38
+ if (!tracked)
39
+ return;
40
+ const trackType = tracked.getAttribute('data-seekora-track');
41
+ const productId = tracked.getAttribute('data-seekora-product-id');
42
+ const position = tracked.getAttribute('data-seekora-position');
43
+ const section = tracked.getAttribute('data-seekora-section');
44
+ const variantSku = tracked.getAttribute('data-seekora-variant-sku');
45
+ const variantOption = tracked.getAttribute('data-seekora-variant-option');
46
+ const variantValue = tracked.getAttribute('data-seekora-variant-value');
47
+ const metadata = {};
48
+ if (productId)
49
+ metadata.product_id = productId;
50
+ if (position)
51
+ metadata.position = parseInt(position, 10);
52
+ if (section)
53
+ metadata.section = section;
54
+ switch (trackType) {
55
+ case 'click':
56
+ sendEvent('product.click', metadata);
57
+ if (client?.trackClick && productId) {
58
+ const pos = position ? parseInt(position, 10) + 1 : 1;
59
+ Promise.resolve(client.trackClick(productId, pos, context)).catch(() => { });
60
+ }
61
+ break;
62
+ case 'add-to-cart':
63
+ if (variantSku)
64
+ metadata.variant_sku = variantSku;
65
+ sendEvent('product.add_to_cart', metadata);
66
+ break;
67
+ case 'variant-select':
68
+ if (variantOption)
69
+ metadata.option_name = variantOption;
70
+ if (variantValue)
71
+ metadata.option_value = variantValue;
72
+ sendEvent('product.variant_select', metadata);
73
+ break;
74
+ default:
75
+ // Custom track type — use it as event name
76
+ if (trackType) {
77
+ sendEvent(trackType, metadata);
78
+ }
79
+ break;
80
+ }
81
+ };
82
+ container.addEventListener('click', handleClick);
83
+ return () => container.removeEventListener('click', handleClick);
84
+ }, [client, context, sendEvent]);
85
+ return (React.createElement(AnalyticsContext.Provider, { value: { client, context } },
86
+ React.createElement("div", { ref: containerRef, style: { display: 'contents' } }, children)));
87
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * BadgeList – renders product badges (sale, new, sold out, custom)
3
+ */
4
+ import React from 'react';
5
+ import type { ProductBadge } from '@seekora-ai/ui-sdk-types';
6
+ export interface BadgeListProps {
7
+ badges: ProductBadge[];
8
+ maxBadges?: number;
9
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'inline';
10
+ className?: string;
11
+ style?: React.CSSProperties;
12
+ }
13
+ export declare function BadgeList({ badges, maxBadges, position, className, style, }: BadgeListProps): React.JSX.Element | null;
14
+ //# sourceMappingURL=BadgeList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BadgeList.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/BadgeList.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,GAAG,QAAQ,CAAC;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAkBD,wBAAgB,SAAS,CAAC,EACxB,MAAM,EACN,SAAS,EACT,QAAqB,EACrB,SAAS,EACT,KAAK,GACN,EAAE,cAAc,4BA4ChB"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * BadgeList – renders product badges (sale, new, sold out, custom)
3
+ */
4
+ import React from 'react';
5
+ import { clsx } from 'clsx';
6
+ const positionStyles = {
7
+ 'top-left': { position: 'absolute', top: 6, left: 6 },
8
+ 'top-right': { position: 'absolute', top: 6, right: 6 },
9
+ 'bottom-left': { position: 'absolute', bottom: 6, left: 6 },
10
+ 'bottom-right': { position: 'absolute', bottom: 6, right: 6 },
11
+ inline: {},
12
+ };
13
+ const typeColors = {
14
+ sale: { bg: '#ef4444', text: '#fff' },
15
+ new: { bg: '#3b82f6', text: '#fff' },
16
+ soldOut: { bg: '#6b7280', text: '#fff' },
17
+ limited: { bg: '#f59e0b', text: '#fff' },
18
+ custom: { bg: '#111827', text: '#fff' },
19
+ };
20
+ export function BadgeList({ badges, maxBadges, position = 'top-left', className, style, }) {
21
+ if (!badges || badges.length === 0)
22
+ return null;
23
+ const visible = maxBadges ? badges.slice(0, maxBadges) : badges;
24
+ return (React.createElement("div", { className: clsx('seekora-badge-list', className), style: {
25
+ display: 'flex',
26
+ flexWrap: 'wrap',
27
+ gap: 4,
28
+ zIndex: 1,
29
+ ...positionStyles[position],
30
+ ...style,
31
+ } }, visible.map((badge, i) => {
32
+ const colors = typeColors[badge.type ?? 'custom'];
33
+ return (React.createElement("span", { key: `${badge.text}-${i}`, className: clsx('seekora-badge', badge.type && `seekora-badge--${badge.type === 'soldOut' ? 'sold-out' : badge.type}`), style: {
34
+ display: 'inline-block',
35
+ padding: '2px 8px',
36
+ borderRadius: 4,
37
+ fontSize: '0.6875rem',
38
+ fontWeight: 600,
39
+ lineHeight: 1.4,
40
+ backgroundColor: badge.color ?? colors.bg,
41
+ color: badge.textColor ?? colors.text,
42
+ whiteSpace: 'nowrap',
43
+ } }, badge.text));
44
+ })));
45
+ }
@@ -5,6 +5,7 @@
5
5
  * ProductCard, ProductGallery. Overridable via className/style.
6
6
  */
7
7
  import React from 'react';
8
+ import type { ImageZoomMode } from './ImageZoom';
8
9
  export type ImageDisplayVariant = 'single' | 'carousel' | 'hover' | 'thumbStrip' | 'thumbList';
9
10
  export interface ImageDisplayProps {
10
11
  images: string[];
@@ -14,6 +15,14 @@ export interface ImageDisplayProps {
14
15
  style?: React.CSSProperties;
15
16
  carouselAutoplay?: boolean;
16
17
  carouselIntervalMs?: number;
18
+ /** Enable zoom functionality */
19
+ enableZoom?: boolean;
20
+ /** Zoom mode: 'hover' (Amazon-style), 'lens', 'click' (lightbox), 'both' (hover + click) */
21
+ zoomMode?: ImageZoomMode;
22
+ /** Zoom magnification level (2 = 200%, 3 = 300%) */
23
+ zoomLevel?: number;
24
+ /** Show navigation dots for carousel */
25
+ showDots?: boolean;
17
26
  }
18
- export declare function ImageDisplay({ images, variant, alt, className, style, carouselAutoplay, carouselIntervalMs, }: ImageDisplayProps): React.JSX.Element;
27
+ export declare function ImageDisplay({ images, variant, alt, className, style, carouselAutoplay, carouselIntervalMs, enableZoom, zoomMode, zoomLevel, showDots, }: ImageDisplayProps): React.JSX.Element;
19
28
  //# sourceMappingURL=ImageDisplay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImageDisplay.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/ImageDisplay.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAgC,MAAM,OAAO,CAAC;AAGrD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,YAAY,GAAG,WAAW,CAAC;AAE/F,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAUD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,OAAkB,EAClB,GAAQ,EACR,SAAS,EACT,KAAK,EACL,gBAAwB,EACxB,kBAAyB,GAC1B,EAAE,iBAAiB,qBAiFnB"}
1
+ {"version":3,"file":"ImageDisplay.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/ImageDisplay.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAgC,MAAM,OAAO,CAAC;AAGrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,YAAY,GAAG,WAAW,CAAC;AAE/F,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gCAAgC;IAChC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAUD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,OAAkB,EAClB,GAAQ,EACR,SAAS,EACT,KAAK,EACL,gBAAwB,EACxB,kBAAyB,EACzB,UAAkB,EAClB,QAAiB,EACjB,SAAe,EACf,QAAe,GAChB,EAAE,iBAAiB,qBAsNnB"}
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import React, { useState } from 'react';
8
8
  import { clsx } from 'clsx';
9
+ import { ImageZoom } from './ImageZoom';
9
10
  const imgBaseStyle = {
10
11
  width: '100%',
11
12
  aspectRatio: '1',
@@ -13,7 +14,7 @@ const imgBaseStyle = {
13
14
  borderRadius: 4,
14
15
  backgroundColor: 'var(--seekora-bg-secondary, #f3f4f6)',
15
16
  };
16
- export function ImageDisplay({ images, variant = 'single', alt = '', className, style, carouselAutoplay = false, carouselIntervalMs = 4000, }) {
17
+ export function ImageDisplay({ images, variant = 'single', alt = '', className, style, carouselAutoplay = false, carouselIntervalMs = 4000, enableZoom = false, zoomMode = 'both', zoomLevel = 2.5, showDots = true, }) {
17
18
  const [index, setIndex] = useState(0);
18
19
  const [hovering, setHovering] = useState(false);
19
20
  const safeImages = Array.isArray(images) ? images.filter(Boolean) : [];
@@ -22,13 +23,21 @@ export function ImageDisplay({ images, variant = 'single', alt = '', className,
22
23
  return React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-placeholder', className), style: { ...imgBaseStyle, ...style }, "aria-hidden": true });
23
24
  }
24
25
  if (variant === 'single') {
26
+ if (enableZoom) {
27
+ return (React.createElement(ImageZoom, { src: safeImages[0], alt: alt, mode: zoomMode, zoomLevel: zoomLevel, images: safeImages, currentIndex: 0, className: clsx('seekora-img-display', 'seekora-img-single', className), style: { ...imgBaseStyle, ...style } }));
28
+ }
25
29
  return (React.createElement("img", { src: safeImages[0], alt: alt, className: clsx('seekora-img-display', 'seekora-img-single', className), style: { ...imgBaseStyle, ...style }, loading: "lazy" }));
26
30
  }
27
31
  if (variant === 'hover') {
28
32
  const showSecond = safeImages.length > 1 && hovering;
29
33
  const src = showSecond ? safeImages[1] : safeImages[0];
34
+ const hoverImgStyle = style?.aspectRatio ? { ...imgBaseStyle, aspectRatio: style.aspectRatio } : imgBaseStyle;
35
+ if (enableZoom) {
36
+ return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-hover', className), style: { position: 'relative', ...style }, onMouseEnter: () => setHovering(true), onMouseLeave: () => setHovering(false) },
37
+ React.createElement(ImageZoom, { src: src, alt: alt, mode: zoomMode, zoomLevel: zoomLevel, images: safeImages, currentIndex: showSecond ? 1 : 0, className: "seekora-img-hover-img", style: hoverImgStyle })));
38
+ }
30
39
  return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-hover', className), style: { position: 'relative', ...style }, onMouseEnter: () => setHovering(true), onMouseLeave: () => setHovering(false) },
31
- React.createElement("img", { src: src, alt: alt, className: "seekora-img-hover-img", style: imgBaseStyle, loading: "lazy" })));
40
+ React.createElement("img", { src: src, alt: alt, className: "seekora-img-hover-img", style: hoverImgStyle, loading: "lazy" })));
32
41
  }
33
42
  if (variant === 'carousel') {
34
43
  const go = (delta) => {
@@ -41,16 +50,41 @@ export function ImageDisplay({ images, variant = 'single', alt = '', className,
41
50
  return next;
42
51
  });
43
52
  };
53
+ const carouselImgStyle = style?.aspectRatio ? { ...imgBaseStyle, aspectRatio: style.aspectRatio } : imgBaseStyle;
54
+ const mainImage = enableZoom ? (React.createElement(ImageZoom, { src: current, alt: alt, mode: zoomMode, zoomLevel: zoomLevel, images: safeImages, currentIndex: index, className: "seekora-img-carousel-main", style: carouselImgStyle })) : (React.createElement("img", { src: current, alt: alt, className: "seekora-img-carousel-main", style: carouselImgStyle, loading: "lazy" }));
44
55
  return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-carousel', className), style: { position: 'relative', ...style } },
45
- React.createElement("img", { src: current, alt: alt, className: "seekora-img-carousel-main", style: imgBaseStyle, loading: "lazy" }),
56
+ mainImage,
46
57
  safeImages.length > 1 && (React.createElement(React.Fragment, null,
47
- React.createElement("button", { type: "button", "aria-label": "Previous", className: "seekora-img-carousel-prev", style: arrowStyle(true), onMouseDown: () => go(-1) }),
48
- React.createElement("button", { type: "button", "aria-label": "Next", className: "seekora-img-carousel-next", style: arrowStyle(false), onMouseDown: () => go(1) })))));
58
+ React.createElement("button", { type: "button", "aria-label": "Previous", className: "seekora-img-carousel-prev", style: arrowStyle(true), onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); go(-1); }, onClick: (e) => e.stopPropagation(), onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = 'rgba(255,255,255,1)'; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.9)'; } }, "\u2039"),
59
+ React.createElement("button", { type: "button", "aria-label": "Next", className: "seekora-img-carousel-next", style: arrowStyle(false), onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); go(1); }, onClick: (e) => e.stopPropagation(), onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = 'rgba(255,255,255,1)'; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.9)'; } }, "\u203A"),
60
+ showDots && (React.createElement("div", { className: "seekora-img-carousel-dots", style: {
61
+ position: 'absolute',
62
+ bottom: 8,
63
+ left: '50%',
64
+ transform: 'translateX(-50%)',
65
+ display: 'flex',
66
+ gap: 6,
67
+ padding: '6px 12px',
68
+ backgroundColor: 'rgba(0,0,0,0.5)',
69
+ borderRadius: 12,
70
+ zIndex: 10,
71
+ } }, safeImages.map((_, i) => (React.createElement("button", { key: i, type: "button", "aria-label": `Go to image ${i + 1}`, onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); }, onClick: (e) => { e.stopPropagation(); setIndex(i); }, style: {
72
+ width: 8,
73
+ height: 8,
74
+ borderRadius: '50%',
75
+ border: 'none',
76
+ padding: 0,
77
+ backgroundColor: i === index ? '#fff' : 'rgba(255,255,255,0.5)',
78
+ cursor: 'pointer',
79
+ transition: 'all 150ms ease',
80
+ } })))))))));
49
81
  }
50
82
  if (variant === 'thumbStrip' || variant === 'thumbList') {
83
+ const thumbMainStyle = style?.aspectRatio ? { ...imgBaseStyle, aspectRatio: style.aspectRatio } : imgBaseStyle;
84
+ const mainImage = enableZoom ? (React.createElement(ImageZoom, { src: current, alt: alt, mode: zoomMode, zoomLevel: zoomLevel, images: safeImages, currentIndex: index, className: "seekora-img-thumb-main", style: thumbMainStyle })) : (React.createElement("img", { src: current, alt: alt, className: "seekora-img-thumb-main", style: thumbMainStyle, loading: "lazy" }));
51
85
  return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-thumbstrip', className), style: { display: 'flex', flexDirection: 'column', gap: 8, ...style } },
52
- React.createElement("img", { src: current, alt: alt, className: "seekora-img-thumb-main", style: imgBaseStyle, loading: "lazy" }),
53
- React.createElement("div", { className: "seekora-img-thumbs", style: { display: 'flex', gap: 4, overflowX: 'auto', paddingBottom: 4 } }, safeImages.map((src, i) => (React.createElement("button", { type: "button", key: i, className: clsx('seekora-img-thumb', i === index && 'seekora-img-thumb--active'), style: { flexShrink: 0, width: 48, height: 48, padding: 0, border: i === index ? '2px solid var(--seekora-primary)' : '1px solid transparent', borderRadius: 4, overflow: 'hidden', cursor: 'pointer', background: 'none' }, onMouseDown: () => setIndex(i) },
86
+ mainImage,
87
+ React.createElement("div", { className: "seekora-img-thumbs", style: { display: 'flex', gap: 4, overflowX: 'auto', paddingBottom: 4 } }, safeImages.map((src, i) => (React.createElement("button", { type: "button", key: i, className: clsx('seekora-img-thumb', i === index && 'seekora-img-thumb--active'), style: { flexShrink: 0, width: 48, height: 48, padding: 0, border: i === index ? '2px solid var(--seekora-primary)' : '1px solid transparent', borderRadius: 4, overflow: 'hidden', cursor: 'pointer', background: 'none' }, onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); setIndex(i); }, onClick: (e) => e.stopPropagation() },
54
88
  React.createElement("img", { src: src, alt: "", style: { width: '100%', height: '100%', objectFit: 'cover' } })))))));
55
89
  }
56
90
  return React.createElement("img", { src: current, alt: alt, className: clsx('seekora-img-display', className), style: { ...imgBaseStyle, ...style }, loading: "lazy" });
@@ -64,11 +98,17 @@ function arrowStyle(left) {
64
98
  width: 32,
65
99
  height: 32,
66
100
  borderRadius: '50%',
67
- border: '1px solid var(--seekora-border-color)',
68
- backgroundColor: 'var(--seekora-bg-surface)',
101
+ border: 'none',
102
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
103
+ color: '#111',
104
+ fontSize: '1.25rem',
105
+ fontWeight: 'bold',
69
106
  cursor: 'pointer',
70
107
  display: 'flex',
71
108
  alignItems: 'center',
72
109
  justifyContent: 'center',
110
+ boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
111
+ zIndex: 10,
112
+ transition: 'all 150ms ease',
73
113
  };
74
114
  }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * ImageZoom – zoom on hover and click (Amazon-style magnifier + lightbox)
3
+ *
4
+ * Supports three zoom modes:
5
+ * - hover: Magnified view in a separate panel on the right (Amazon style)
6
+ * - lens: Magnifying glass that follows cursor
7
+ * - click: Full-screen lightbox modal
8
+ */
9
+ import React from 'react';
10
+ export type ImageZoomMode = 'hover' | 'lens' | 'click' | 'both';
11
+ export interface ImageZoomProps {
12
+ src: string;
13
+ alt?: string;
14
+ mode?: ImageZoomMode;
15
+ zoomLevel?: number;
16
+ className?: string;
17
+ style?: React.CSSProperties;
18
+ /** Show zoom indicator icon */
19
+ showZoomIndicator?: boolean;
20
+ /** Lens size in pixels (for lens mode) */
21
+ lensSize?: number;
22
+ /** Zoom panel size (for hover mode) */
23
+ zoomPanelSize?: {
24
+ width: number;
25
+ height: number;
26
+ };
27
+ /** Multiple images for lightbox carousel */
28
+ images?: string[];
29
+ /** Current image index (for multi-image support) */
30
+ currentIndex?: number;
31
+ }
32
+ export declare function ImageZoom({ src, alt, mode, zoomLevel, className, style, showZoomIndicator, lensSize, zoomPanelSize, images, currentIndex, }: ImageZoomProps): React.JSX.Element;
33
+ //# sourceMappingURL=ImageZoom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageZoom.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/ImageZoom.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAGxE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhE,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,+BAA+B;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,aAAa,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,SAAS,CAAC,EACxB,GAAG,EACH,GAAQ,EACR,IAAa,EACb,SAAe,EACf,SAAS,EACT,KAAK,EACL,iBAAwB,EACxB,QAAc,EACd,aAA2C,EAC3C,MAAM,EACN,YAAgB,GACjB,EAAE,cAAc,qBAgehB"}