@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.
- package/dist/components/InfiniteHits.d.ts +2 -0
- package/dist/components/InfiniteHits.d.ts.map +1 -1
- package/dist/components/InfiniteHits.js +6 -3
- package/dist/components/QuerySuggestions.d.ts +2 -0
- package/dist/components/QuerySuggestions.d.ts.map +1 -1
- package/dist/components/QuerySuggestions.js +4 -3
- package/dist/components/QuerySuggestionsDropdown.d.ts +1 -1
- package/dist/components/QuerySuggestionsDropdown.d.ts.map +1 -1
- package/dist/components/QuerySuggestionsDropdown.js +4 -4
- package/dist/components/Recommendations.d.ts +6 -0
- package/dist/components/Recommendations.d.ts.map +1 -1
- package/dist/components/Recommendations.js +12 -6
- package/dist/components/RichQuerySuggestions.d.ts +4 -0
- package/dist/components/RichQuerySuggestions.d.ts.map +1 -1
- package/dist/components/RichQuerySuggestions.js +2 -3
- package/dist/components/SearchBar.d.ts +2 -0
- package/dist/components/SearchBar.d.ts.map +1 -1
- package/dist/components/SearchBar.js +5 -9
- package/dist/components/SearchResults.d.ts +2 -0
- package/dist/components/SearchResults.d.ts.map +1 -1
- package/dist/components/SearchResults.js +4 -2
- package/dist/components/primitives/ActionButtons.d.ts +27 -0
- package/dist/components/primitives/ActionButtons.d.ts.map +1 -0
- package/dist/components/primitives/ActionButtons.js +78 -0
- package/dist/components/primitives/AnalyticsProvider.d.ts +22 -0
- package/dist/components/primitives/AnalyticsProvider.d.ts.map +1 -0
- package/dist/components/primitives/AnalyticsProvider.js +87 -0
- package/dist/components/primitives/BadgeList.d.ts +14 -0
- package/dist/components/primitives/BadgeList.d.ts.map +1 -0
- package/dist/components/primitives/BadgeList.js +45 -0
- package/dist/components/primitives/ImageDisplay.d.ts +10 -1
- package/dist/components/primitives/ImageDisplay.d.ts.map +1 -1
- package/dist/components/primitives/ImageDisplay.js +49 -9
- package/dist/components/primitives/ImageZoom.d.ts +33 -0
- package/dist/components/primitives/ImageZoom.d.ts.map +1 -0
- package/dist/components/primitives/ImageZoom.js +357 -0
- package/dist/components/primitives/PriceDisplay.d.ts +21 -0
- package/dist/components/primitives/PriceDisplay.d.ts.map +1 -0
- package/dist/components/primitives/PriceDisplay.js +44 -0
- package/dist/components/primitives/RatingDisplay.d.ts +43 -0
- package/dist/components/primitives/RatingDisplay.d.ts.map +1 -0
- package/dist/components/primitives/RatingDisplay.js +114 -0
- package/dist/components/primitives/VariantSelector.d.ts +30 -0
- package/dist/components/primitives/VariantSelector.d.ts.map +1 -0
- package/dist/components/primitives/VariantSelector.js +162 -0
- package/dist/components/primitives/VariantSwatches.d.ts +28 -0
- package/dist/components/primitives/VariantSwatches.d.ts.map +1 -0
- package/dist/components/primitives/VariantSwatches.js +173 -0
- package/dist/components/primitives/index.d.ts +9 -0
- package/dist/components/primitives/index.d.ts.map +1 -1
- package/dist/components/primitives/index.js +9 -0
- package/dist/components/primitives/withAnalytics.d.ts +24 -0
- package/dist/components/primitives/withAnalytics.d.ts.map +1 -0
- package/dist/components/primitives/withAnalytics.js +73 -0
- package/dist/components/product-page/ProductInfo.d.ts +25 -2
- package/dist/components/product-page/ProductInfo.d.ts.map +1 -1
- package/dist/components/product-page/ProductInfo.js +20 -5
- package/dist/components/section-primitives/SectionItemGrid.d.ts +3 -1
- package/dist/components/section-primitives/SectionItemGrid.d.ts.map +1 -1
- package/dist/components/section-primitives/SectionItemGrid.js +3 -2
- package/dist/components/suggestions/AmazonDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/AmazonDropdown.js +2 -2
- package/dist/components/suggestions/GoogleDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/GoogleDropdown.js +2 -2
- package/dist/components/suggestions/MinimalDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/MinimalDropdown.js +2 -2
- package/dist/components/suggestions/MobileSheetDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/MobileSheetDropdown.js +2 -2
- package/dist/components/suggestions/PinterestDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/PinterestDropdown.js +2 -2
- package/dist/components/suggestions/ShopifyDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/ShopifyDropdown.js +2 -2
- package/dist/components/suggestions/SpotlightDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/SpotlightDropdown.js +2 -2
- package/dist/components/suggestions/SuggestionSearchBar.d.ts.map +1 -1
- package/dist/components/suggestions/SuggestionSearchBar.js +1 -0
- package/dist/components/suggestions/types.d.ts +26 -0
- package/dist/components/suggestions/types.d.ts.map +1 -1
- package/dist/components/suggestions/utils.d.ts +37 -0
- package/dist/components/suggestions/utils.d.ts.map +1 -1
- package/dist/components/suggestions/utils.js +118 -0
- package/dist/components/suggestions-primitives/ItemCard.d.ts +10 -1
- package/dist/components/suggestions-primitives/ItemCard.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ItemCard.js +20 -6
- package/dist/components/suggestions-primitives/ProductCard.d.ts +27 -3
- package/dist/components/suggestions-primitives/ProductCard.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ProductCard.js +124 -17
- package/dist/components/suggestions-primitives/ProductCardLayouts.d.ts +44 -0
- package/dist/components/suggestions-primitives/ProductCardLayouts.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/ProductCardLayouts.js +105 -0
- package/dist/components/suggestions-primitives/ProductGrid.d.ts +6 -1
- package/dist/components/suggestions-primitives/ProductGrid.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ProductGrid.js +2 -2
- package/dist/components/suggestions-primitives/SuggestionList.d.ts +8 -1
- package/dist/components/suggestions-primitives/SuggestionList.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/SuggestionList.js +7 -4
- package/dist/components/suggestions-primitives/SuggestionsDropdownComposition.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/SuggestionsDropdownComposition.js +0 -2
- package/dist/docsearch/components/Results.d.ts +3 -1
- package/dist/docsearch/components/Results.d.ts.map +1 -1
- package/dist/docsearch/components/Results.js +6 -2
- package/dist/hooks/useProductAnalytics.d.ts +49 -0
- package/dist/hooks/useProductAnalytics.d.ts.map +1 -0
- package/dist/hooks/useProductAnalytics.js +116 -0
- package/dist/hooks/useQuerySuggestionsEnhanced.js +2 -1
- package/dist/hooks/useSuggestionsAnalytics.d.ts.map +1 -1
- package/dist/hooks/useSuggestionsAnalytics.js +6 -0
- package/dist/hooks/useVariantSelection.d.ts +28 -0
- package/dist/hooks/useVariantSelection.d.ts.map +1 -0
- package/dist/hooks/useVariantSelection.js +44 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.umd.js +1 -1
- package/dist/src/index.d.ts +1138 -681
- package/dist/src/index.esm.js +2407 -723
- package/dist/src/index.esm.js.map +1 -1
- package/dist/src/index.js +2423 -722
- package/dist/src/index.js.map +1 -1
- 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;
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
68
|
-
backgroundColor: '
|
|
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"}
|