@mohasinac/appkit 3.9.0 → 3.9.1

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 (30) hide show
  1. package/dist/features/admin/components/AdminSectionsView.js +4 -4
  2. package/dist/features/admin/components/sections/adminSectionsBuildParse.js +13 -5
  3. package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -0
  4. package/dist/features/admin/components/sections/adminSectionsTypes.js +9 -5
  5. package/dist/features/homepage/components/BrandsSection.d.ts +4 -1
  6. package/dist/features/homepage/components/BrandsSection.js +2 -2
  7. package/dist/features/homepage/components/CustomCardsSection.js +3 -3
  8. package/dist/features/homepage/components/CustomerReviewsSection.d.ts +4 -1
  9. package/dist/features/homepage/components/CustomerReviewsSection.js +2 -2
  10. package/dist/features/homepage/components/EventsSection.d.ts +4 -1
  11. package/dist/features/homepage/components/EventsSection.js +2 -2
  12. package/dist/features/homepage/components/FeaturedAuctionsSection.d.ts +2 -1
  13. package/dist/features/homepage/components/FeaturedAuctionsSection.js +2 -2
  14. package/dist/features/homepage/components/FeaturedPreOrdersSection.d.ts +2 -1
  15. package/dist/features/homepage/components/FeaturedPreOrdersSection.js +2 -2
  16. package/dist/features/homepage/components/FeaturedProductsSection.d.ts +2 -1
  17. package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
  18. package/dist/features/homepage/components/FeaturedStoresSection.d.ts +4 -1
  19. package/dist/features/homepage/components/FeaturedStoresSection.js +2 -2
  20. package/dist/features/homepage/components/HomepageCustomerReviewsSection.d.ts +4 -1
  21. package/dist/features/homepage/components/HomepageCustomerReviewsSection.js +2 -2
  22. package/dist/features/homepage/components/SectionCarousel.d.ts +3 -1
  23. package/dist/features/homepage/components/SectionCarousel.js +2 -2
  24. package/dist/features/homepage/components/ShopByCategorySection.d.ts +4 -1
  25. package/dist/features/homepage/components/ShopByCategorySection.js +2 -2
  26. package/dist/features/homepage/lib/section-renderer.js +9 -9
  27. package/dist/features/homepage/schemas/firestore.d.ts +8 -0
  28. package/dist/seed/homepage-sections-seed-data.js +10 -0
  29. package/dist/ui/components/HorizontalScroller.js +3 -0
  30. package/package.json +1 -1
@@ -703,7 +703,7 @@ export function AdminSectionsView({ children }) {
703
703
  return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Reviews Section Builder" }), _jsx(Input, { label: "Section title", value: reviewsBuilder.title, onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Select, { label: "Reviews source", value: reviewsBuilder.source, onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, source: v })), options: [
704
704
  { label: "Platform reviews (LetItRip)", value: "platform" },
705
705
  { label: "Google Business Reviews", value: "google" },
706
- ] }), reviewsBuilder.source === "google" ? (_jsx(Input, { label: "Google Place ID", value: reviewsBuilder.placeId, onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, placeId: e.target.value })), placeholder: "ChIJ...", helperText: "Find your Place ID at maps.google.com/placesinventory" })) : null, _jsx(Select, { label: "Max reviews", value: String(reviewsBuilder.maxReviews), onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, maxReviews: Number(v) })), options: [{ label: "5", value: "5" }, { label: "10", value: "10" }, { label: "20", value: "20" }] }), _jsx(Select, { label: "Items per view", value: String(reviewsBuilder.itemsPerView), onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, itemsPerView: Number(v) })), options: [{ label: "1", value: "1" }, { label: "2", value: "2" }, { label: "3", value: "3" }] }), _jsx(Checkbox, { checked: reviewsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(reviewsBuilder.scrollInterval), onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) })] }));
706
+ ] }), reviewsBuilder.source === "google" ? (_jsx(Input, { label: "Google Place ID", value: reviewsBuilder.placeId, onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, placeId: e.target.value })), placeholder: "ChIJ...", helperText: "Find your Place ID at maps.google.com/placesinventory" })) : null, _jsx(Select, { label: "Max reviews", value: String(reviewsBuilder.maxReviews), onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, maxReviews: Number(v) })), options: [{ label: "5", value: "5" }, { label: "10", value: "10" }, { label: "20", value: "20" }] }), _jsx(Select, { label: "Items per view", value: String(reviewsBuilder.itemsPerView), onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, itemsPerView: Number(v) })), options: [{ label: "1", value: "1" }, { label: "2", value: "2" }, { label: "3", value: "3" }] }), _jsx(Checkbox, { checked: reviewsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(reviewsBuilder.scrollInterval), onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) }), _jsx(Checkbox, { checked: reviewsBuilder.loop, label: "Loop carousel", onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, loop: e.target.checked })) })] }));
707
707
  }
708
708
  function renderWhatsAppBuilder() {
709
709
  return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "WhatsApp Community Builder" }), _jsx(Input, { label: "Title", value: whatsappBuilder.title, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Textarea, { label: "Description", value: whatsappBuilder.description, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, description: e.target.value })), rows: 2 }), _jsx(Input, { label: "WhatsApp group link", type: "url", value: whatsappBuilder.groupLink, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, groupLink: e.target.value })), placeholder: "https://chat.whatsapp.com/..." }), _jsx(Input, { label: "Member count", type: "number", min: 0, value: String(whatsappBuilder.memberCount), onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, memberCount: Math.max(0, Number(e.target.value) || 0) })) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Benefits" }), whatsappBuilder.benefits.map((benefit, index) => (_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Input, { value: benefit, onChange: (e) => setWhatsappBuilder((prev) => { const next = [...prev.benefits]; next[index] = e.target.value; return { ...prev, benefits: next }; }), className: "flex-1" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setWhatsappBuilder((prev) => ({ ...prev, benefits: prev.benefits.filter((_, i) => i !== index) })), children: "\u2715" })] }, `wb-${index}`))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setWhatsappBuilder((prev) => ({ ...prev, benefits: [...prev.benefits, ""] })), children: "+ Add benefit" })] }), _jsx(Input, { label: "Button text", value: whatsappBuilder.buttonText, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, buttonText: e.target.value })) }), _jsx(Input, { label: "Testimonial (optional)", value: whatsappBuilder.testimonial, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, testimonial: e.target.value })) })] }));
@@ -715,10 +715,10 @@ export function AdminSectionsView({ children }) {
715
715
  return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Trust Indicators Builder" }), _jsx(Input, { label: "Section title", value: trustIndicatorsBuilder.title, onChange: (e) => setTrustIndicatorsBuilder((prev) => ({ ...prev, title: e.target.value })) }), trustIndicatorsBuilder.indicators.map((ind, index) => (_jsxs(Stack, { className: `${__P.p3}`, gap: "sm", rounded: "md", border: "default", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: ["Indicator ", index + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setTrustIndicatorsBuilder((prev) => ({ ...prev, indicators: prev.indicators.filter((_, i) => i !== index) })), children: "Remove" })] }), _jsx(Input, { label: "Icon (emoji or text)", value: ind.icon, onChange: (e) => setTrustIndicatorsBuilder((prev) => { const next = [...prev.indicators]; next[index] = { ...next[index], icon: e.target.value }; return { ...prev, indicators: next }; }) }), _jsx(Input, { label: "Title", value: ind.title, onChange: (e) => setTrustIndicatorsBuilder((prev) => { const next = [...prev.indicators]; next[index] = { ...next[index], title: e.target.value }; return { ...prev, indicators: next }; }) }), _jsx(Input, { label: "Description", value: ind.description, onChange: (e) => setTrustIndicatorsBuilder((prev) => { const next = [...prev.indicators]; next[index] = { ...next[index], description: e.target.value }; return { ...prev, indicators: next }; }) })] }, ind.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setTrustIndicatorsBuilder((prev) => ({ ...prev, indicators: [...prev.indicators, { id: `ti-${Date.now()}`, icon: "✨", title: "", description: "" }] })), children: "+ Add indicator" })] }));
716
716
  }
717
717
  function renderCategoriesBuilder() {
718
- return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Categories Section Builder" }), _jsx(Input, { label: "Section title", value: categoriesBuilder.title, onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Input, { label: "Max categories (4\u201312)", type: "number", min: 4, max: 12, value: String(categoriesBuilder.maxCategories), onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, maxCategories: Math.min(12, Math.max(4, Number(e.target.value) || 4)) })) }), _jsx(Checkbox, { checked: categoriesBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(categoriesBuilder.scrollInterval), onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) })] }));
718
+ return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Categories Section Builder" }), _jsx(Input, { label: "Section title", value: categoriesBuilder.title, onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Input, { label: "Max categories (4\u201312)", type: "number", min: 4, max: 12, value: String(categoriesBuilder.maxCategories), onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, maxCategories: Math.min(12, Math.max(4, Number(e.target.value) || 4)) })) }), _jsx(Checkbox, { checked: categoriesBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(categoriesBuilder.scrollInterval), onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) }), _jsx(Checkbox, { checked: categoriesBuilder.loop, label: "Loop carousel", onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, loop: e.target.checked })) })] }));
719
719
  }
720
720
  function renderBrandsBuilder() {
721
- return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Brands Section Builder" }), _jsx(Input, { label: "Section title", value: brandsBuilder.title, onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Input, { label: "Subtitle", value: brandsBuilder.subtitle, onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, subtitle: e.target.value })) }), _jsx(Input, { label: "Max brands", type: "number", min: 1, max: 30, value: String(brandsBuilder.maxBrands), onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, maxBrands: Math.max(1, Number(e.target.value) || 1) })) }), _jsx(Checkbox, { checked: brandsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(brandsBuilder.scrollInterval), onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) })] }));
721
+ return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Brands Section Builder" }), _jsx(Input, { label: "Section title", value: brandsBuilder.title, onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Input, { label: "Subtitle", value: brandsBuilder.subtitle, onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, subtitle: e.target.value })) }), _jsx(Input, { label: "Max brands", type: "number", min: 1, max: 30, value: String(brandsBuilder.maxBrands), onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, maxBrands: Math.max(1, Number(e.target.value) || 1) })) }), _jsx(Checkbox, { checked: brandsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(brandsBuilder.scrollInterval), onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) }), _jsx(Checkbox, { checked: brandsBuilder.loop, label: "Loop carousel", onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, loop: e.target.checked })) })] }));
722
722
  }
723
723
  function renderCarouselBuilder() {
724
724
  return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Carousel Section Builder" }), _jsx(Input, { label: "Section title", value: carouselBuilder.title, onChange: (e) => setCarouselBuilder((prev) => ({ ...prev, title: e.target.value })), placeholder: "Hero Carousel" }), _jsx(Select, { label: "Slide height", value: carouselBuilder.height, onValueChange: (value) => setCarouselBuilder((prev) => ({ ...prev, height: value })), options: [
@@ -786,7 +786,7 @@ export function AdminSectionsView({ children }) {
786
786
  ] }), _jsx(Checkbox, { checked: customCardsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setCustomCardsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(customCardsBuilder.scrollIntervalMs), onChange: (e) => setCustomCardsBuilder((prev) => ({
787
787
  ...prev,
788
788
  scrollIntervalMs: Math.max(1000, Number(e.target.value) || 4000),
789
- })) }), _jsxs(Stack, { gap: "3", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: ["Cards (", customCardsBuilder.cards.length, ")"] }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addCard, children: "Add card" })] }), customCardsBuilder.cards.map((card, index) => renderCardFields(card, index)), customCardsBuilder.cards.length === 0 ? (_jsx(Text, { size: "sm", color: "muted", children: "No cards added yet. Click \"Add card\" to start." })) : null] })] }));
789
+ })) }), _jsx(Checkbox, { checked: customCardsBuilder.loop, label: "Loop carousel", onChange: (e) => setCustomCardsBuilder((prev) => ({ ...prev, loop: e.target.checked })) }), _jsxs(Stack, { gap: "3", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: ["Cards (", customCardsBuilder.cards.length, ")"] }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addCard, children: "Add card" })] }), customCardsBuilder.cards.map((card, index) => renderCardFields(card, index)), customCardsBuilder.cards.length === 0 ? (_jsx(Text, { size: "sm", color: "muted", children: "No cards added yet. Click \"Add card\" to start." })) : null] })] }));
790
790
  }
791
791
  function renderGoogleReviewsBuilder() {
792
792
  return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Google Reviews Builder" }), _jsx(Input, { label: "Google Place ID", value: googleReviewsBuilder.placeId, onChange: (e) => setGoogleReviewsBuilder((prev) => ({ ...prev, placeId: e.target.value })), placeholder: "ChIJ...", helperText: "Find your Place ID at developers.google.com/maps/documentation/places/web-service/place-id" }), _jsx(Input, { label: "Max reviews to show", type: "number", min: 1, max: 20, value: String(googleReviewsBuilder.maxReviews), onChange: (e) => setGoogleReviewsBuilder((prev) => ({
@@ -190,7 +190,7 @@ export function parseProductsBuilder(config) {
190
190
  manualResourceIds: toStringArray(resources.ids).join(", "),
191
191
  filterByCategory: toStringValue(config.filterByCategory),
192
192
  maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
193
- loop: toBooleanValue(config.loop, false),
193
+ loop: toBooleanValue(config.loop, DEFAULT_PRODUCTS_BUILDER.loop),
194
194
  };
195
195
  }
196
196
  export function parseAuctionsBuilder(config) {
@@ -210,7 +210,7 @@ export function parseAuctionsBuilder(config) {
210
210
  manualResourceIds: toStringArray(resources.ids).join(", "),
211
211
  filterByCategory: toStringValue(config.filterByCategory),
212
212
  maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
213
- loop: toBooleanValue(config.loop, false),
213
+ loop: toBooleanValue(config.loop, DEFAULT_AUCTIONS_BUILDER.loop),
214
214
  };
215
215
  }
216
216
  export function parseStatsBuilder(config) {
@@ -271,7 +271,7 @@ export function parsePreOrdersBuilder(config) {
271
271
  manualResourceIds: toStringArray(resources.ids).join(", "),
272
272
  filterByCategory: toStringValue(config.filterByCategory),
273
273
  maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
274
- loop: toBooleanValue(config.loop, false),
274
+ loop: toBooleanValue(config.loop, DEFAULT_PRE_ORDERS_BUILDER.loop),
275
275
  };
276
276
  }
277
277
  export function parseStoresBuilder(config) {
@@ -292,7 +292,7 @@ export function parseStoresBuilder(config) {
292
292
  manualResourceIds: toStringArray(resources.ids).join(", "),
293
293
  filterByCategory: toStringValue(config.filterByCategory),
294
294
  maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
295
- loop: toBooleanValue(config.loop, false),
295
+ loop: toBooleanValue(config.loop, DEFAULT_STORES_BUILDER.loop),
296
296
  };
297
297
  }
298
298
  export function parseEventsBuilder(config) {
@@ -313,7 +313,7 @@ export function parseEventsBuilder(config) {
313
313
  manualResourceIds: toStringArray(resources.ids).join(", "),
314
314
  filterByCategory: toStringValue(config.filterByCategory),
315
315
  maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
316
- loop: toBooleanValue(config.loop, false),
316
+ loop: toBooleanValue(config.loop, DEFAULT_EVENTS_BUILDER.loop),
317
317
  };
318
318
  }
319
319
  export function buildSocialFeedConfig(builder) {
@@ -395,6 +395,7 @@ export function buildCategoriesConfig(builder) {
395
395
  maxCategories: builder.maxCategories,
396
396
  autoScroll: builder.autoScroll,
397
397
  scrollInterval: builder.scrollInterval,
398
+ loop: builder.loop,
398
399
  };
399
400
  }
400
401
  export function parseCategoriesBuilder(config) {
@@ -403,6 +404,7 @@ export function parseCategoriesBuilder(config) {
403
404
  maxCategories: toNumberValue(config.maxCategories, DEFAULT_CATEGORIES_BUILDER.maxCategories),
404
405
  autoScroll: toBooleanValue(config.autoScroll, DEFAULT_CATEGORIES_BUILDER.autoScroll),
405
406
  scrollInterval: toNumberValue(config.scrollInterval, DEFAULT_CATEGORIES_BUILDER.scrollInterval),
407
+ loop: toBooleanValue(config.loop, DEFAULT_CATEGORIES_BUILDER.loop),
406
408
  };
407
409
  }
408
410
  export function buildBrandsConfig(builder) {
@@ -412,6 +414,7 @@ export function buildBrandsConfig(builder) {
412
414
  maxBrands: builder.maxBrands,
413
415
  autoScroll: builder.autoScroll,
414
416
  scrollInterval: builder.scrollInterval,
417
+ loop: builder.loop,
415
418
  };
416
419
  }
417
420
  export function parseBrandsBuilder(config) {
@@ -421,6 +424,7 @@ export function parseBrandsBuilder(config) {
421
424
  maxBrands: toNumberValue(config.maxBrands, DEFAULT_BRANDS_BUILDER.maxBrands),
422
425
  autoScroll: toBooleanValue(config.autoScroll, DEFAULT_BRANDS_BUILDER.autoScroll),
423
426
  scrollInterval: toNumberValue(config.scrollInterval, DEFAULT_BRANDS_BUILDER.scrollInterval),
427
+ loop: toBooleanValue(config.loop, DEFAULT_BRANDS_BUILDER.loop),
424
428
  };
425
429
  }
426
430
  export function buildBannerConfig(builder) {
@@ -486,6 +490,7 @@ export function buildReviewsConfig(builder) {
486
490
  mobileItemsPerView: 1,
487
491
  autoScroll: builder.autoScroll,
488
492
  scrollInterval: builder.scrollInterval,
493
+ loop: builder.loop,
489
494
  source: builder.source,
490
495
  placeId: builder.source === "google" ? builder.placeId || undefined : undefined,
491
496
  };
@@ -497,6 +502,7 @@ export function parseReviewsBuilder(config) {
497
502
  itemsPerView: toNumberValue(config.itemsPerView, DEFAULT_REVIEWS_BUILDER.itemsPerView),
498
503
  autoScroll: toBooleanValue(config.autoScroll, DEFAULT_REVIEWS_BUILDER.autoScroll),
499
504
  scrollInterval: toNumberValue(config.scrollInterval, DEFAULT_REVIEWS_BUILDER.scrollInterval),
505
+ loop: toBooleanValue(config.loop, DEFAULT_REVIEWS_BUILDER.loop),
500
506
  source: toStringValue(config.source, "platform"),
501
507
  placeId: toStringValue(config.placeId),
502
508
  };
@@ -613,6 +619,7 @@ export function buildCustomCardsConfig(builder) {
613
619
  columns: builder.columns,
614
620
  autoScroll: builder.autoScroll,
615
621
  scrollIntervalMs: builder.scrollIntervalMs,
622
+ loop: builder.loop,
616
623
  cards: builder.cards.map((card, index) => ({
617
624
  id: card.id || `card-${index + 1}`,
618
625
  image: card.image || undefined,
@@ -639,6 +646,7 @@ export function parseCustomCardsBuilder(config) {
639
646
  columns: ([1, 2, 3, 4].includes(cols) ? cols : DEFAULT_CUSTOM_CARDS_BUILDER.columns),
640
647
  autoScroll: toBooleanValue(config.autoScroll, DEFAULT_CUSTOM_CARDS_BUILDER.autoScroll),
641
648
  scrollIntervalMs: toNumberValue(config.scrollIntervalMs, DEFAULT_CUSTOM_CARDS_BUILDER.scrollIntervalMs),
649
+ loop: toBooleanValue(config.loop, DEFAULT_CUSTOM_CARDS_BUILDER.loop),
642
650
  cards: cardsRaw.map((item, index) => {
643
651
  const c = (item ?? {});
644
652
  const br = toStringValue(c.borderRadius, "none");
@@ -150,6 +150,7 @@ export interface CategoriesBuilderState {
150
150
  maxCategories: number;
151
151
  autoScroll: boolean;
152
152
  scrollInterval: number;
153
+ loop: boolean;
153
154
  }
154
155
  export interface BrandsBuilderState {
155
156
  title: string;
@@ -157,6 +158,7 @@ export interface BrandsBuilderState {
157
158
  maxBrands: number;
158
159
  autoScroll: boolean;
159
160
  scrollInterval: number;
161
+ loop: boolean;
160
162
  }
161
163
  export interface BannerBuilderState {
162
164
  height: "sm" | "md" | "lg" | "xl";
@@ -185,6 +187,7 @@ export interface ReviewsBuilderState {
185
187
  itemsPerView: number;
186
188
  autoScroll: boolean;
187
189
  scrollInterval: number;
190
+ loop: boolean;
188
191
  source: "platform" | "google";
189
192
  placeId: string;
190
193
  }
@@ -249,6 +252,7 @@ export interface CustomCardsBuilderState {
249
252
  columns: 1 | 2 | 3 | 4;
250
253
  autoScroll: boolean;
251
254
  scrollIntervalMs: number;
255
+ loop: boolean;
252
256
  cards: CustomCardsCardBuilderEntry[];
253
257
  }
254
258
  export interface GoogleReviewsBuilderState {
@@ -42,7 +42,7 @@ export const DEFAULT_PRODUCTS_BUILDER = {
42
42
  manualResourceIds: "",
43
43
  filterByCategory: "",
44
44
  maxCount: 10,
45
- loop: false,
45
+ loop: true,
46
46
  };
47
47
  export const DEFAULT_AUCTIONS_BUILDER = {
48
48
  title: "Live Auctions",
@@ -57,7 +57,7 @@ export const DEFAULT_AUCTIONS_BUILDER = {
57
57
  manualResourceIds: "",
58
58
  filterByCategory: "",
59
59
  maxCount: 10,
60
- loop: false,
60
+ loop: true,
61
61
  };
62
62
  const DEFAULT_STAT_ROW = {
63
63
  source: "static",
@@ -89,7 +89,7 @@ export const DEFAULT_PRE_ORDERS_BUILDER = {
89
89
  manualResourceIds: "",
90
90
  filterByCategory: "",
91
91
  maxCount: 10,
92
- loop: false,
92
+ loop: true,
93
93
  };
94
94
  export const DEFAULT_STORES_BUILDER = {
95
95
  title: "Featured Stores",
@@ -105,7 +105,7 @@ export const DEFAULT_STORES_BUILDER = {
105
105
  manualResourceIds: "",
106
106
  filterByCategory: "",
107
107
  maxCount: 10,
108
- loop: false,
108
+ loop: true,
109
109
  };
110
110
  export const DEFAULT_EVENTS_BUILDER = {
111
111
  title: "Events & Offers",
@@ -121,7 +121,7 @@ export const DEFAULT_EVENTS_BUILDER = {
121
121
  manualResourceIds: "",
122
122
  filterByCategory: "",
123
123
  maxCount: 10,
124
- loop: false,
124
+ loop: true,
125
125
  };
126
126
  export const DEFAULT_SOCIAL_FEED_BUILDER = {
127
127
  title: "",
@@ -156,6 +156,7 @@ export const DEFAULT_CATEGORIES_BUILDER = {
156
156
  maxCategories: 8,
157
157
  autoScroll: false,
158
158
  scrollInterval: 5000,
159
+ loop: true,
159
160
  };
160
161
  export const DEFAULT_BRANDS_BUILDER = {
161
162
  title: "Top Brands",
@@ -163,6 +164,7 @@ export const DEFAULT_BRANDS_BUILDER = {
163
164
  maxBrands: 13,
164
165
  autoScroll: true,
165
166
  scrollInterval: 4000,
167
+ loop: true,
166
168
  };
167
169
  export const DEFAULT_BANNER_BUILDER = {
168
170
  height: "lg",
@@ -187,6 +189,7 @@ export const DEFAULT_REVIEWS_BUILDER = {
187
189
  itemsPerView: 3,
188
190
  autoScroll: true,
189
191
  scrollInterval: 5000,
192
+ loop: true,
190
193
  source: "platform",
191
194
  placeId: "",
192
195
  };
@@ -237,6 +240,7 @@ export const DEFAULT_CUSTOM_CARDS_BUILDER = {
237
240
  columns: 3,
238
241
  autoScroll: false,
239
242
  scrollIntervalMs: 4000,
243
+ loop: true,
240
244
  cards: [],
241
245
  };
242
246
  export const DEFAULT_GOOGLE_REVIEWS_BUILDER = {
@@ -13,5 +13,8 @@ export interface BrandsSectionProps {
13
13
  featuredOnly?: boolean;
14
14
  byCountry?: string;
15
15
  };
16
+ autoScroll?: boolean;
17
+ scrollInterval?: number;
18
+ loop?: boolean;
16
19
  }
17
- export declare function BrandsSection({ title, subtitle, limit, viewMoreHref, viewMoreLabel, className, initialItems, cta, filters, }: BrandsSectionProps): import("react").JSX.Element | null;
20
+ export declare function BrandsSection({ title, subtitle, limit, viewMoreHref, viewMoreLabel, className, initialItems, cta, filters, autoScroll, scrollInterval, loop, }: BrandsSectionProps): import("react").JSX.Element | null;
@@ -29,7 +29,7 @@ function BrandLogo({ brand }) {
29
29
  const initial = brand.name[0]?.toUpperCase() ?? "?";
30
30
  return (_jsxs(Link, { href: ROUTES.PUBLIC.CATEGORY_DETAIL(brand.slug), className: "flex h-32 w-36 flex-col items-center justify-center gap-[var(--appkit-space-2)] rounded-xl border border-zinc-200 bg-[var(--appkit-color-surface)] p-[var(--appkit-space-3)] shadow-sm transition-all hover:border-primary-300 hover:shadow-md border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] md:h-40", children: [iconSrc || coverImage ? (_jsx(Image, { src: iconSrc ?? coverImage, alt: brand.name, width: 64, height: 64, className: "h-16 w-16 rounded object-contain" })) : (_jsx(Row, { textWeight: "bold", textSize: "sm", className: "h-16 w-16 bg-primary-100 text-primary-700 dark:bg-primary-900 dark:text-primary-300", align: "center", justify: "center", rounded: "lg", children: initial })), _jsx(Text, { className: "w-full truncate", color: "muted", size: "xs", weight: "medium", align: "start", children: brand.name })] }));
31
31
  }
32
- export function BrandsSection({ title = "Top Brands", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "All brands →", className = "", initialItems, cta, filters, }) {
32
+ export function BrandsSection({ title = "Top Brands", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "All brands →", className = "", initialItems, cta, filters, autoScroll = true, scrollInterval = 4000, loop = true, }) {
33
33
  const themed = { textPrimary: THEMED_TEXT_PRIMARY };
34
34
  const { data: allBrands = [], isLoading } = useTopBrands(limit, { initialData: initialItems });
35
35
  const [activeFilter, setActiveFilter] = useState("all");
@@ -42,5 +42,5 @@ export function BrandsSection({ title = "Top Brands", subtitle, limit = 12, view
42
42
  });
43
43
  if (!isLoading && allBrands.length === 0)
44
44
  return null;
45
- return (_jsx(Section, { className: `${className}`, surface: "subtle", paddingY: "y-3xl", paddingX: "x-md", children: _jsxs(Div, { className: "mx-auto max-w-7xl", children: [_jsxs(Row, { className: "mb-6", align: "center", justify: "between", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, className: themed.textPrimary, size: "2xl", mdSize: "3xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { variant: "secondary", className: "mt-1", size: "sm", children: subtitle }))] }), !cta && viewMoreHref && (_jsx(Link, { href: viewMoreHref, className: "text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-primary)] hover:underline", children: viewMoreLabel }))] }), showFeaturedChip && !isLoading && (_jsxs(Row, { wrap: true, gap: "sm", className: "mb-4", children: [_jsx(BrandFilterChip, { label: "All", active: activeFilter === "all", onClick: () => setActiveFilter("all") }), _jsx(BrandFilterChip, { label: "Featured", active: activeFilter === "featured", onClick: () => setActiveFilter(activeFilter === "featured" ? "all" : "featured") })] })), isLoading ? (_jsx(Div, { layout: "flex", gap: "3", className: `${__O.hidden}`, children: Array.from({ length: 8 }).map((_, i) => (_jsx(Div, { className: "h-32 w-36 flex-none animate-pulse md:h-40", surface: "subtle", rounded: "xl" }, i))) })) : (_jsx(HorizontalScroller, { items: brands, renderItem: (brand) => _jsx(BrandLogo, { brand: brand }), keyExtractor: (brand) => brand.id, gap: 12, showArrows: true, showScrollbar: false, loop: true })), cta && !isLoading && (_jsx(Div, { className: "mt-6 text-left", children: _jsx(Link, { href: cta.href, className: CTA_CLASSES[cta.variant], children: cta.label }) }))] }) }));
45
+ return (_jsx(Section, { className: `${className}`, surface: "subtle", paddingY: "y-3xl", paddingX: "x-md", children: _jsxs(Div, { className: "mx-auto max-w-7xl", children: [_jsxs(Row, { className: "mb-6", align: "center", justify: "between", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, className: themed.textPrimary, size: "2xl", mdSize: "3xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { variant: "secondary", className: "mt-1", size: "sm", children: subtitle }))] }), !cta && viewMoreHref && (_jsx(Link, { href: viewMoreHref, className: "text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-primary)] hover:underline", children: viewMoreLabel }))] }), showFeaturedChip && !isLoading && (_jsxs(Row, { wrap: true, gap: "sm", className: "mb-4", children: [_jsx(BrandFilterChip, { label: "All", active: activeFilter === "all", onClick: () => setActiveFilter("all") }), _jsx(BrandFilterChip, { label: "Featured", active: activeFilter === "featured", onClick: () => setActiveFilter(activeFilter === "featured" ? "all" : "featured") })] })), isLoading ? (_jsx(Div, { layout: "flex", gap: "3", className: `${__O.hidden}`, children: Array.from({ length: 8 }).map((_, i) => (_jsx(Div, { className: "h-32 w-36 flex-none animate-pulse md:h-40", surface: "subtle", rounded: "xl" }, i))) })) : (_jsx(HorizontalScroller, { items: brands, renderItem: (brand) => _jsx(BrandLogo, { brand: brand }), keyExtractor: (brand) => brand.id, gap: 12, showArrows: true, showScrollbar: false, autoScroll: autoScroll, autoScrollInterval: scrollInterval, pauseOnHover: true, loop: loop })), cta && !isLoading && (_jsx(Div, { className: "mt-6 text-left", children: _jsx(Link, { href: cta.href, className: CTA_CLASSES[cta.variant], children: cta.label }) }))] }) }));
46
46
  }
@@ -55,16 +55,16 @@ function SectionHeader({ title }) {
55
55
  return (_jsx(Div, { className: "mb-6", children: _jsx(Heading, { level: 2, className: themed.textPrimary, children: title }) }));
56
56
  }
57
57
  export function CustomCardsSection(config) {
58
- const { title, layout, columns = 3, cards, autoScroll, scrollIntervalMs } = config;
58
+ const { title, layout, columns = 3, cards, autoScroll, scrollIntervalMs, loop = true } = config;
59
59
  if (!cards?.length)
60
60
  return null;
61
61
  // autoScroll: wrap all cards in SectionCarousel (client carousel)
62
62
  if (autoScroll) {
63
- return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsx(Div, { className: CLS_CONTAINER, children: _jsx(SectionCarousel, { title: title ?? "", items: cards, renderItem: (card) => _jsx(CardItem, { card: card }), keyExtractor: (card) => card.id, autoScroll: true, autoScrollInterval: scrollIntervalMs ?? 3500, perView: { base: 1, sm: 2, md: columns } }) }) }));
63
+ return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsx(Div, { className: CLS_CONTAINER, children: _jsx(SectionCarousel, { title: title ?? "", items: cards, renderItem: (card) => _jsx(CardItem, { card: card }), keyExtractor: (card) => card.id, autoScroll: true, autoScrollInterval: scrollIntervalMs ?? 3500, loop: loop, perView: { base: 1, sm: 2, md: columns } }) }) }));
64
64
  }
65
65
  // Row layout: horizontal scroller
66
66
  if (layout === "row") {
67
- return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsxs(Div, { className: CLS_CONTAINER, children: [_jsx(SectionHeader, { title: title }), _jsx(HorizontalScroller, { gap: 16, showArrows: true, snapToItems: true, loop: true, items: cards, keyExtractor: (card) => card.id, renderItem: (card) => (_jsx(Div, { className: "w-72 flex-shrink-0", children: _jsx(CardItem, { card: card }) })) })] }) }));
67
+ return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsxs(Div, { className: CLS_CONTAINER, children: [_jsx(SectionHeader, { title: title }), _jsx(HorizontalScroller, { gap: 16, showArrows: true, snapToItems: true, loop: loop, items: cards, keyExtractor: (card) => card.id, renderItem: (card) => (_jsx(Div, { className: "w-72 flex-shrink-0", children: _jsx(CardItem, { card: card }) })) })] }) }));
68
68
  }
69
69
  // Masonry layout: CSS columns
70
70
  if (layout === "masonry") {
@@ -9,5 +9,8 @@ export interface CustomerReviewsSectionProps<T = unknown> {
9
9
  isLoading?: boolean;
10
10
  keyExtractor?: (item: T) => string;
11
11
  className?: string;
12
+ autoScroll?: boolean;
13
+ scrollInterval?: number;
14
+ loop?: boolean;
12
15
  }
13
- export declare function CustomerReviewsSection<T = unknown>({ title, subtitle, items, renderItem, viewMoreHref, viewMoreLabel, isLoading, keyExtractor, className, }: CustomerReviewsSectionProps<T>): React.JSX.Element | null;
16
+ export declare function CustomerReviewsSection<T = unknown>({ title, subtitle, items, renderItem, viewMoreHref, viewMoreLabel, isLoading, keyExtractor, className, autoScroll, scrollInterval, loop, }: CustomerReviewsSectionProps<T>): React.JSX.Element | null;
@@ -10,12 +10,12 @@ const __O = {
10
10
  hidden: "overflow-hidden",
11
11
  };
12
12
  // --- Section -----------------------------------------------------------------
13
- export function CustomerReviewsSection({ title, subtitle, items, renderItem, viewMoreHref, viewMoreLabel = "See all reviews", isLoading = false, keyExtractor, className = "", }) {
13
+ export function CustomerReviewsSection({ title, subtitle, items, renderItem, viewMoreHref, viewMoreLabel = "See all reviews", isLoading = false, keyExtractor, className = "", autoScroll = true, scrollInterval = 4500, loop = true, }) {
14
14
  const themed = { textPrimary: THEMED_TEXT_PRIMARY, textSecondary: THEMED_TEXT_SECONDARY };
15
15
  if (isLoading) {
16
16
  return (_jsx(Section, { className: `${__P.p8} ${className}`, surface: "muted", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto", children: [_jsx(Div, { className: `h-8 ${skeleton.base} mb-8 max-w-xs mx-auto` }), _jsx(Div, { layout: "flex", gap: "6", className: `${__O.hidden}`, children: [...Array(3)].map((_, i) => (_jsx(Div, { className: `${skeleton.card} h-[clamp(180px,26vh,260px)] min-w-[clamp(150px,18vw,260px)] max-w-[clamp(240px,36vw,380px)] flex-none` }, i))) })] }) }));
17
17
  }
18
18
  if (items.length === 0)
19
19
  return null;
20
- return (_jsx(Section, { className: `${className}`, surface: "muted", padding: "y-3xl", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto md:px-[2rem]", padding: "x-md", children: [_jsxs(Div, { className: "text-left mb-10", children: [_jsx(Heading, { level: 2, className: `${themed.textPrimary} mb-3`, size: "3xl", mdSize: "4xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { className: `${themed.textSecondary}`, size: "base", children: subtitle }))] }), _jsx(HorizontalScroller, { items: items, renderItem: (item) => renderItem(item), perView: CAROUSEL_PER_VIEW.reviews, gap: 24, autoScroll: true, autoScrollInterval: 4500, pauseOnHover: true, snapToItems: true, showArrows: true, keyExtractor: keyExtractor, loop: true }), viewMoreHref && (_jsx(Div, { className: "text-left mt-8", children: _jsx(TextLink, { href: viewMoreHref, className: "text-primary hover:text-primary/80", size: "sm", weight: "medium", children: viewMoreLabel }) }))] }) }));
20
+ return (_jsx(Section, { className: `${className}`, surface: "muted", padding: "y-3xl", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto md:px-[2rem]", padding: "x-md", children: [_jsxs(Div, { className: "text-left mb-10", children: [_jsx(Heading, { level: 2, className: `${themed.textPrimary} mb-3`, size: "3xl", mdSize: "4xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { className: `${themed.textSecondary}`, size: "base", children: subtitle }))] }), _jsx(HorizontalScroller, { items: items, renderItem: (item) => renderItem(item), perView: CAROUSEL_PER_VIEW.reviews, gap: 24, autoScroll: autoScroll, autoScrollInterval: scrollInterval, pauseOnHover: true, snapToItems: true, showArrows: true, keyExtractor: keyExtractor, loop: loop }), viewMoreHref && (_jsx(Div, { className: "text-left mt-8", children: _jsx(TextLink, { href: viewMoreHref, className: "text-primary hover:text-primary/80", size: "sm", weight: "medium", children: viewMoreLabel }) }))] }) }));
21
21
  }
@@ -8,5 +8,8 @@ export interface EventsSectionProps {
8
8
  limit?: number;
9
9
  className?: string;
10
10
  initialItems?: EventItem[];
11
+ autoScroll?: boolean;
12
+ scrollInterval?: number;
13
+ loop?: boolean;
11
14
  }
12
- export declare function EventsSection({ title, description, viewMoreHref, viewMoreLabel, limit, className, initialItems, }: EventsSectionProps): React.JSX.Element;
15
+ export declare function EventsSection({ title, description, viewMoreHref, viewMoreLabel, limit, className, initialItems, autoScroll, scrollInterval, loop, }: EventsSectionProps): React.JSX.Element;
@@ -4,7 +4,7 @@ import { SectionCarousel } from "./SectionCarousel";
4
4
  import { useHomepageEvents } from "../hooks/useHomepageEvents";
5
5
  import { EventCard } from "../../events/components/EventCard";
6
6
  import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
7
- export function EventsSection({ title = "Events & Offers", description, viewMoreHref, viewMoreLabel = "View all events →", limit = 6, className = "", initialItems, }) {
7
+ export function EventsSection({ title = "Events & Offers", description, viewMoreHref, viewMoreLabel = "View all events →", limit = 6, className = "", initialItems, autoScroll = false, scrollInterval = 5000, loop = true, }) {
8
8
  const { data: items = [], isLoading } = useHomepageEvents(limit, { initialData: initialItems });
9
- return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Events & Offers", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 3, perView: CAROUSEL_PER_VIEW.events, gap: 16, keyExtractor: (event) => event.id, renderItem: (event) => (_jsx(EventCard, { event: event })), className: className }));
9
+ return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Events & Offers", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 3, perView: CAROUSEL_PER_VIEW.events, gap: 16, autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (event) => event.id, renderItem: (event) => (_jsx(EventCard, { event: event })), className: className }));
10
10
  }
@@ -11,5 +11,6 @@ export interface FeaturedAuctionsSectionProps {
11
11
  rows?: number;
12
12
  autoScroll?: boolean;
13
13
  scrollInterval?: number;
14
+ loop?: boolean;
14
15
  }
15
- export declare function FeaturedAuctionsSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, }: FeaturedAuctionsSectionProps): React.JSX.Element;
16
+ export declare function FeaturedAuctionsSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, loop, }: FeaturedAuctionsSectionProps): React.JSX.Element;
@@ -4,7 +4,7 @@ import { SectionCarousel } from "./SectionCarousel";
4
4
  import { useFeaturedAuctions } from "../hooks/useFeaturedAuctions";
5
5
  import { MarketplaceAuctionCard } from "../../auctions/components/MarketplaceAuctionCard";
6
6
  import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
7
- export function FeaturedAuctionsSection({ title = "Live Auctions", description, viewMoreHref, viewMoreLabel = "View all auctions →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, }) {
7
+ export function FeaturedAuctionsSection({ title = "Live Auctions", description, viewMoreHref, viewMoreLabel = "View all auctions →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, loop = true, }) {
8
8
  const { data: items = [], isLoading } = useFeaturedAuctions({ filterByBrand, initialData: initialItems });
9
- return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Live Auctions", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(MarketplaceAuctionCard, { product: product })), className: className }));
9
+ return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Live Auctions", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(MarketplaceAuctionCard, { product: product })), className: className }));
10
10
  }
@@ -11,5 +11,6 @@ export interface FeaturedPreOrdersSectionProps {
11
11
  rows?: number;
12
12
  autoScroll?: boolean;
13
13
  scrollInterval?: number;
14
+ loop?: boolean;
14
15
  }
15
- export declare function FeaturedPreOrdersSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, }: FeaturedPreOrdersSectionProps): React.JSX.Element;
16
+ export declare function FeaturedPreOrdersSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, loop, }: FeaturedPreOrdersSectionProps): React.JSX.Element;
@@ -4,7 +4,7 @@ import { SectionCarousel } from "./SectionCarousel";
4
4
  import { useFeaturedPreOrders } from "../hooks/useFeaturedPreOrders";
5
5
  import { MarketplacePreorderCard } from "../../pre-orders/components/MarketplacePreorderCard";
6
6
  import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
7
- export function FeaturedPreOrdersSection({ title = "Reserve Before It Ships", description, viewMoreHref, viewMoreLabel = "View all pre-orders →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, }) {
7
+ export function FeaturedPreOrdersSection({ title = "Reserve Before It Ships", description, viewMoreHref, viewMoreLabel = "View all pre-orders →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, loop = true, }) {
8
8
  const { data: items = [], isLoading } = useFeaturedPreOrders({ filterByBrand, initialData: initialItems });
9
- return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Pre-Order Incoming", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(MarketplacePreorderCard, { product: product })), className: className }));
9
+ return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Pre-Order Incoming", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(MarketplacePreorderCard, { product: product })), className: className }));
10
10
  }
@@ -11,5 +11,6 @@ export interface FeaturedProductsSectionProps {
11
11
  maxItems?: number;
12
12
  autoScroll?: boolean;
13
13
  scrollInterval?: number;
14
+ loop?: boolean;
14
15
  }
15
- export declare function FeaturedProductsSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, }: FeaturedProductsSectionProps): import("react").JSX.Element;
16
+ export declare function FeaturedProductsSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, loop, }: FeaturedProductsSectionProps): import("react").JSX.Element;
@@ -5,7 +5,7 @@ import { useFeaturedProducts } from "../hooks/useFeaturedProducts";
5
5
  import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
6
6
  import { ROUTES } from "../../../next";
7
7
  import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
8
- export function FeaturedProductsSection({ title = "Featured Products", description, viewMoreHref, viewMoreLabel = "View all products →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, }) {
8
+ export function FeaturedProductsSection({ title = "Featured Products", description, viewMoreHref, viewMoreLabel = "View all products →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, loop = true, }) {
9
9
  const { data, isLoading } = useFeaturedProducts({
10
10
  filterByBrand,
11
11
  initialData: initialItems?.length
@@ -13,5 +13,5 @@ export function FeaturedProductsSection({ title = "Featured Products", descripti
13
13
  : undefined,
14
14
  });
15
15
  const items = data?.items ?? [];
16
- return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Featured Products", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(product.slug ?? product.id ?? "")) })), className: className }));
16
+ return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Featured Products", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(product.slug ?? product.id ?? "")) })), className: className }));
17
17
  }
@@ -8,5 +8,8 @@ export interface FeaturedStoresSectionProps {
8
8
  limit?: number;
9
9
  className?: string;
10
10
  initialItems?: StoreListItem[];
11
+ autoScroll?: boolean;
12
+ scrollInterval?: number;
13
+ loop?: boolean;
11
14
  }
12
- export declare function FeaturedStoresSection({ title, description, viewMoreHref, viewMoreLabel, limit, className, initialItems, }: FeaturedStoresSectionProps): React.JSX.Element;
15
+ export declare function FeaturedStoresSection({ title, description, viewMoreHref, viewMoreLabel, limit, className, initialItems, autoScroll, scrollInterval, loop, }: FeaturedStoresSectionProps): React.JSX.Element;
@@ -5,7 +5,7 @@ import { useFeaturedStores } from "../hooks/useFeaturedStores";
5
5
  import { InteractiveStoreCard } from "../../stores/components/InteractiveStoreCard";
6
6
  import { ROUTES } from "../../../next";
7
7
  import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
8
- export function FeaturedStoresSection({ title = "Featured Stores", description, viewMoreHref, viewMoreLabel = "View all stores →", limit = 8, className = "", initialItems, }) {
8
+ export function FeaturedStoresSection({ title = "Featured Stores", description, viewMoreHref, viewMoreLabel = "View all stores →", limit = 8, className = "", initialItems, autoScroll = false, scrollInterval = 5000, loop = true, }) {
9
9
  const { data: items = [], isLoading } = useFeaturedStores(limit, { initialData: initialItems });
10
- return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Top Stores", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, keyExtractor: (store) => store.id, renderItem: (store) => (_jsx(InteractiveStoreCard, { store: store, href: ROUTES.PUBLIC.STORE_DETAIL(store.storeSlug) })), className: className }));
10
+ return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Top Stores", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (store) => store.id, renderItem: (store) => (_jsx(InteractiveStoreCard, { store: store, href: ROUTES.PUBLIC.STORE_DETAIL(store.storeSlug) })), className: className }));
11
11
  }
@@ -5,5 +5,8 @@ export interface HomepageCustomerReviewsSectionProps {
5
5
  viewMoreHref?: string;
6
6
  viewMoreLabel?: string;
7
7
  className?: string;
8
+ autoScroll?: boolean;
9
+ scrollInterval?: number;
10
+ loop?: boolean;
8
11
  }
9
- export declare function HomepageCustomerReviewsSection({ title, subtitle, viewMoreHref, viewMoreLabel, className, }: HomepageCustomerReviewsSectionProps): React.JSX.Element;
12
+ export declare function HomepageCustomerReviewsSection({ title, subtitle, viewMoreHref, viewMoreLabel, className, autoScroll, scrollInterval, loop, }: HomepageCustomerReviewsSectionProps): React.JSX.Element;
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { CustomerReviewsSection } from "./CustomerReviewsSection";
4
4
  import { useHomepageReviews } from "../hooks/useHomepageReviews";
5
5
  import { ReviewCard } from "../../reviews/components";
6
- export function HomepageCustomerReviewsSection({ title = "What Our Customers Say", subtitle, viewMoreHref, viewMoreLabel = "See all reviews →", className = "", }) {
6
+ export function HomepageCustomerReviewsSection({ title = "What Our Customers Say", subtitle, viewMoreHref, viewMoreLabel = "See all reviews →", className = "", autoScroll = true, scrollInterval = 4500, loop = true, }) {
7
7
  const { data: reviews = [], isLoading } = useHomepageReviews();
8
- return (_jsx(CustomerReviewsSection, { title: title, subtitle: subtitle, items: reviews, renderItem: (review) => (_jsx(ReviewCard, { review: review, className: "h-full border-zinc-200 shadow-sm border-[var(--appkit-color-border)]" })), viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, isLoading: isLoading, keyExtractor: (review) => review.id, className: className }));
8
+ return (_jsx(CustomerReviewsSection, { title: title, subtitle: subtitle, items: reviews, renderItem: (review) => (_jsx(ReviewCard, { review: review, className: "h-full border-zinc-200 shadow-sm border-[var(--appkit-color-border)]" })), viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, isLoading: isLoading, keyExtractor: (review) => review.id, className: className, autoScroll: autoScroll, scrollInterval: scrollInterval, loop: loop }));
9
9
  }
@@ -41,6 +41,8 @@ export interface SectionCarouselProps<T = unknown> {
41
41
  autoScroll?: boolean;
42
42
  /** Interval between auto-scroll steps in ms. Default: 3500 */
43
43
  autoScrollInterval?: number;
44
+ /** Circular auto-rotate at the carousel edges. Default: true */
45
+ loop?: boolean;
44
46
  /** Key extractor for the HorizontalScroller. */
45
47
  keyExtractor?: (item: T, index: number) => string;
46
48
  /**
@@ -72,4 +74,4 @@ export interface SectionCarouselProps<T = unknown> {
72
74
  */
73
75
  minItemWidth?: number;
74
76
  }
75
- export declare function SectionCarousel<T = unknown>({ title, description, headingVariant, pillLabel, backgroundImage, viewMoreHref, viewMoreLabel, items, renderItem, perView, gap, autoScroll, autoScrollInterval, keyExtractor, rows, className, isLoading, skeletonCount, lightText, showPeek: _showPeek, minItemWidth, }: SectionCarouselProps<T>): React.JSX.Element | null;
77
+ export declare function SectionCarousel<T = unknown>({ title, description, headingVariant, pillLabel, backgroundImage, viewMoreHref, viewMoreLabel, items, renderItem, perView, gap, autoScroll, autoScrollInterval, loop, keyExtractor, rows, className, isLoading, skeletonCount, lightText, showPeek: _showPeek, minItemWidth, }: SectionCarouselProps<T>): React.JSX.Element | null;
@@ -12,7 +12,7 @@ function CarouselSkeleton({ count }) {
12
12
  return (_jsx(Div, { layout: "flex", gap: "4", className: `${__O.hidden}`, padding: "x-md", children: Array.from({ length: count }).map((_, i) => (_jsxs(Stack, { className: "flex-none min-w-[clamp(150px,18vw,260px)] max-w-[clamp(240px,36vw,380px)] h-[clamp(180px,26vh,260px)]", gap: "sm", children: [_jsx(Div, { className: `aspect-square ${skeleton.image}`, rounded: "xl" }), _jsx(Div, { className: `${skeleton.text} w-3/4` }), _jsx(Div, { className: `${skeleton.text} w-1/2` })] }, i))) }));
13
13
  }
14
14
  // --- Component ---------------------------------------------------------------
15
- export function SectionCarousel({ title, description, headingVariant = "editorial", pillLabel, backgroundImage, viewMoreHref, viewMoreLabel = "View all →", items, renderItem, perView = CAROUSEL_PER_VIEW.standard, gap = 16, autoScroll = false, autoScrollInterval = 3500, keyExtractor, rows = 1, className = "", isLoading = false, skeletonCount = 4, lightText, showPeek: _showPeek = false, minItemWidth = 220, }) {
15
+ export function SectionCarousel({ title, description, headingVariant = "editorial", pillLabel, backgroundImage, viewMoreHref, viewMoreLabel = "View all →", items, renderItem, perView = CAROUSEL_PER_VIEW.standard, gap = 16, autoScroll = false, autoScrollInterval = 3500, loop = true, keyExtractor, rows = 1, className = "", isLoading = false, skeletonCount = 4, lightText, showPeek: _showPeek = false, minItemWidth = 220, }) {
16
16
  if (!isLoading && items.length === 0)
17
17
  return null;
18
18
  const hasBg = Boolean(backgroundImage);
@@ -41,5 +41,5 @@ export function SectionCarousel({ title, description, headingVariant = "editoria
41
41
  headingClass,
42
42
  ]
43
43
  .filter(Boolean)
44
- .join(" "), children: title }), headingVariant === "editorial" && (_jsxs(Row, { align: "center", justify: "center", gap: "sm", textSize: "xs", color: "faint", className: "mt-1 select-none", "aria-hidden": "true", children: [_jsx(Span, { className: "h-px w-6 bg-current" }), _jsx(Span, { size: "xs", children: "\u2736" }), _jsx(Span, { className: "h-px w-6 bg-current" })] })), description && (_jsx(Text, { className: `${descVariant} mt-2`, size: "base", children: description }))] }), isLoading ? (_jsx(CarouselSkeleton, { count: skeletonCount })) : (_jsx(HorizontalScroller, { items: items, renderItem: renderItem, perView: perView, gap: gap, autoScroll: autoScroll, autoScrollInterval: autoScrollInterval, loop: true, keyExtractor: keyExtractor, rows: rows, minItemWidth: minItemWidth, showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, pauseOnHover: true })), viewMoreHref && !isLoading && (_jsx(Row, { className: "mt-6", justify: "start", children: _jsx(TextLink, { rounded: "lg", paddingX: "xl", paddingY: "sm", href: viewMoreHref, className: `inline-flex items-[center] gap-[0.375rem] border transition-colors ${useLightText ? "border-white/40 text-white hover:bg-[rgba(255,255,255,0.1)]" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)]"}`, size: "sm", weight: "medium", children: viewMoreLabel }) }))] })] }));
44
+ .join(" "), children: title }), headingVariant === "editorial" && (_jsxs(Row, { align: "center", justify: "center", gap: "sm", textSize: "xs", color: "faint", className: "mt-1 select-none", "aria-hidden": "true", children: [_jsx(Span, { className: "h-px w-6 bg-current" }), _jsx(Span, { size: "xs", children: "\u2736" }), _jsx(Span, { className: "h-px w-6 bg-current" })] })), description && (_jsx(Text, { className: `${descVariant} mt-2`, size: "base", children: description }))] }), isLoading ? (_jsx(CarouselSkeleton, { count: skeletonCount })) : (_jsx(HorizontalScroller, { items: items, renderItem: renderItem, perView: perView, gap: gap, autoScroll: autoScroll, autoScrollInterval: autoScrollInterval, loop: loop, keyExtractor: keyExtractor, rows: rows, minItemWidth: minItemWidth, showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, pauseOnHover: true })), viewMoreHref && !isLoading && (_jsx(Row, { className: "mt-6", justify: "start", children: _jsx(TextLink, { rounded: "lg", paddingX: "xl", paddingY: "sm", href: viewMoreHref, className: `inline-flex items-[center] gap-[0.375rem] border transition-colors ${useLightText ? "border-white/40 text-white hover:bg-[rgba(255,255,255,0.1)]" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)]"}`, size: "sm", weight: "medium", children: viewMoreLabel }) }))] })] }));
45
45
  }
@@ -14,5 +14,8 @@ export interface ShopByCategorySectionProps {
14
14
  rootOnly?: boolean;
15
15
  rootCategoryId?: string;
16
16
  };
17
+ autoScroll?: boolean;
18
+ scrollInterval?: number;
19
+ loop?: boolean;
17
20
  }
18
- export declare function ShopByCategorySection({ title, subtitle, limit, viewMoreHref, viewMoreLabel, className, initialItems, cta, filters, }: ShopByCategorySectionProps): import("react").JSX.Element | null;
21
+ export declare function ShopByCategorySection({ title, subtitle, limit, viewMoreHref, viewMoreLabel, className, initialItems, cta, filters, autoScroll, scrollInterval, loop, }: ShopByCategorySectionProps): import("react").JSX.Element | null;
@@ -37,7 +37,7 @@ function FilterChip({ label, active, onClick, }) {
37
37
  : "border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)] hover:border-[var(--appkit-color-primary)]",
38
38
  ].join(" "), children: label }));
39
39
  }
40
- export function ShopByCategorySection({ title = "Shop by Category", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "View all categories →", className = "", initialItems, cta, filters, }) {
40
+ export function ShopByCategorySection({ title = "Shop by Category", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "View all categories →", className = "", initialItems, cta, filters, autoScroll = true, scrollInterval = 3500, loop = true, }) {
41
41
  const themed = { textPrimary: THEMED_TEXT_PRIMARY };
42
42
  const { data: allCategories = [], isLoading } = useTopCategories(limit, { initialData: initialItems });
43
43
  const [activeFilter, setActiveFilter] = useState("all");
@@ -68,5 +68,5 @@ export function ShopByCategorySection({ title = "Shop by Category", subtitle, li
68
68
  if (!cat)
69
69
  return null;
70
70
  return (_jsx(FilterChip, { label: cat.name, active: activeFilter === id, onClick: () => setActiveFilter(activeFilter === id ? "all" : id) }, id));
71
- })] })), isLoading ? (_jsx(Div, { layout: "flex", gap: "3", className: `${__O.hidden} px-[0.25rem]`, children: Array.from({ length: 6 }).map((_, i) => (_jsx(Div, { className: "flex-none h-[104px] w-[108px] animate-pulse", surface: "subtle", rounded: "xl" }, i))) })) : (_jsx(HorizontalScroller, { items: categories, renderItem: (cat) => _jsx(CategoryChip, { category: cat }), perView: CAROUSEL_PER_VIEW.standard, gap: 16, keyExtractor: (cat) => cat.id, autoScroll: true, autoScrollInterval: 3500, showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, loop: true, pauseOnHover: true })), cta && !isLoading && (_jsx(Div, { className: "mt-6 text-left", children: _jsx(Link, { href: cta.href, className: CTA_CLASSES[cta.variant], children: cta.label }) })), !cta && viewMoreHref && !isLoading && (_jsx(Div, { className: "mt-6 text-left", children: _jsx(Link, { href: viewMoreHref, className: "inline-flex items-center gap-[var(--appkit-space-1)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-primary)] hover:opacity-80", children: viewMoreLabel }) }))] }) }));
71
+ })] })), isLoading ? (_jsx(Div, { layout: "flex", gap: "3", className: `${__O.hidden} px-[0.25rem]`, children: Array.from({ length: 6 }).map((_, i) => (_jsx(Div, { className: "flex-none h-[104px] w-[108px] animate-pulse", surface: "subtle", rounded: "xl" }, i))) })) : (_jsx(HorizontalScroller, { items: categories, renderItem: (cat) => _jsx(CategoryChip, { category: cat }), perView: CAROUSEL_PER_VIEW.standard, gap: 16, keyExtractor: (cat) => cat.id, autoScroll: autoScroll, autoScrollInterval: scrollInterval, showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, loop: loop, pauseOnHover: true })), cta && !isLoading && (_jsx(Div, { className: "mt-6 text-left", children: _jsx(Link, { href: cta.href, className: CTA_CLASSES[cta.variant], children: cta.label }) })), !cta && viewMoreHref && !isLoading && (_jsx(Div, { className: "mt-6 text-left", children: _jsx(Link, { href: viewMoreHref, className: "inline-flex items-center gap-[var(--appkit-space-1)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-primary)] hover:opacity-80", children: viewMoreLabel }) }))] }) }));
72
72
  }
@@ -53,7 +53,7 @@ function renderSectionElement(section, newsletterFormSlot, faqItems, slides, liv
53
53
  }
54
54
  case "categories": {
55
55
  const cfg = config;
56
- return (_jsx(ShopByCategorySection, { title: cleanTitle(cfg?.title) || "Shop by Category", viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All categories \u2192", initialItems: sectionData.categories, cta: cfg?.cta, filters: cfg?.filters }));
56
+ return (_jsx(ShopByCategorySection, { title: cleanTitle(cfg?.title) || "Shop by Category", viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All categories \u2192", initialItems: sectionData.categories, cta: cfg?.cta, filters: cfg?.filters, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
57
57
  }
58
58
  case "stats": {
59
59
  const cfg = config;
@@ -84,27 +84,27 @@ function renderSectionElement(section, newsletterFormSlot, faqItems, slides, liv
84
84
  }
85
85
  case "products": {
86
86
  const cfg = config;
87
- return (_jsx(FeaturedProductsSection, { title: cleanTitle(cfg?.title) || "Featured Products", viewMoreHref: ROUTES.PUBLIC.PRODUCTS, viewMoreLabel: "View all products \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.products, rows: cfg?.rows, maxItems: cfg?.maxItems, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval }));
87
+ return (_jsx(FeaturedProductsSection, { title: cleanTitle(cfg?.title) || "Featured Products", viewMoreHref: ROUTES.PUBLIC.PRODUCTS, viewMoreLabel: "View all products \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.products, rows: cfg?.rows, maxItems: cfg?.maxItems, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
88
88
  }
89
89
  case "auctions": {
90
90
  const cfg = config;
91
- return (_jsx(FeaturedAuctionsSection, { title: cleanTitle(cfg?.title) || "Live Auctions", viewMoreHref: ROUTES.PUBLIC.AUCTIONS, viewMoreLabel: "View all auctions \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.auctions, rows: cfg?.rows, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval }));
91
+ return (_jsx(FeaturedAuctionsSection, { title: cleanTitle(cfg?.title) || "Live Auctions", viewMoreHref: ROUTES.PUBLIC.AUCTIONS, viewMoreLabel: "View all auctions \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.auctions, rows: cfg?.rows, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
92
92
  }
93
93
  case "pre-orders": {
94
94
  const cfg = config;
95
- return (_jsx(FeaturedPreOrdersSection, { title: cleanTitle(cfg?.title) || "Reserve Before It Ships", viewMoreHref: ROUTES.PUBLIC.PRE_ORDERS, viewMoreLabel: "View all pre-orders \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.preOrders, rows: cfg?.rows, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval }));
95
+ return (_jsx(FeaturedPreOrdersSection, { title: cleanTitle(cfg?.title) || "Reserve Before It Ships", viewMoreHref: ROUTES.PUBLIC.PRE_ORDERS, viewMoreLabel: "View all pre-orders \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.preOrders, rows: cfg?.rows, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
96
96
  }
97
97
  case "stores": {
98
98
  const cfg = config;
99
- return (_jsx(FeaturedStoresSection, { title: cleanTitle(cfg?.title) || "Featured Stores", viewMoreHref: ROUTES.PUBLIC.STORES, viewMoreLabel: "View all stores \u2192", initialItems: sectionData.stores }));
99
+ return (_jsx(FeaturedStoresSection, { title: cleanTitle(cfg?.title) || "Featured Stores", viewMoreHref: ROUTES.PUBLIC.STORES, viewMoreLabel: "View all stores \u2192", initialItems: sectionData.stores, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
100
100
  }
101
101
  case "events": {
102
102
  const cfg = config;
103
- return (_jsx(EventsSection, { title: cleanTitle(cfg?.title) || "Events & Offers", viewMoreHref: ROUTES.PUBLIC.EVENTS, viewMoreLabel: "View all events \u2192", initialItems: sectionData.events }));
103
+ return (_jsx(EventsSection, { title: cleanTitle(cfg?.title) || "Events & Offers", viewMoreHref: ROUTES.PUBLIC.EVENTS, viewMoreLabel: "View all events \u2192", initialItems: sectionData.events, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
104
104
  }
105
105
  case "reviews": {
106
106
  const cfg = config;
107
- return (_jsx(HomepageCustomerReviewsSection, { title: cleanTitle(cfg?.title) || "What Our Customers Say", viewMoreHref: ROUTES.PUBLIC.REVIEWS, viewMoreLabel: "See all reviews \u2192" }));
107
+ return (_jsx(HomepageCustomerReviewsSection, { title: cleanTitle(cfg?.title) || "What Our Customers Say", viewMoreHref: ROUTES.PUBLIC.REVIEWS, viewMoreLabel: "See all reviews \u2192", autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
108
108
  }
109
109
  case "banner": {
110
110
  const cfg = config;
@@ -156,7 +156,7 @@ function renderSectionElement(section, newsletterFormSlot, faqItems, slides, liv
156
156
  }
157
157
  case "brands": {
158
158
  const cfg = config;
159
- return (_jsx(BrandsSection, { title: cleanTitle(cfg?.title) || "Top Brands", subtitle: cfg?.subtitle, limit: cfg?.maxBrands || 12, viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All brands \u2192", initialItems: sectionData.brands, cta: cfg?.cta, filters: cfg?.filters }));
159
+ return (_jsx(BrandsSection, { title: cleanTitle(cfg?.title) || "Top Brands", subtitle: cfg?.subtitle, limit: cfg?.maxBrands || 12, viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All brands \u2192", initialItems: sectionData.brands, cta: cfg?.cta, filters: cfg?.filters, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
160
160
  }
161
161
  case "social-feed": {
162
162
  const cfg = config;
@@ -169,7 +169,7 @@ function renderSectionElement(section, newsletterFormSlot, faqItems, slides, liv
169
169
  const cfg = config;
170
170
  if (!cfg?.cards?.length)
171
171
  return null;
172
- return (_jsx(CustomCardsSection, { title: cleanTitle(cfg.title), layout: cfg.layout ?? "grid", columns: cfg.columns ?? 3, cards: cfg.cards, autoScroll: cfg.autoScroll, scrollIntervalMs: cfg.scrollIntervalMs }));
172
+ return (_jsx(CustomCardsSection, { title: cleanTitle(cfg.title), layout: cfg.layout ?? "grid", columns: cfg.columns ?? 3, cards: cfg.cards, autoScroll: cfg.autoScroll, scrollIntervalMs: cfg.scrollIntervalMs, loop: cfg.loop }));
173
173
  }
174
174
  case "google-reviews": {
175
175
  const cfg = config;
@@ -196,6 +196,8 @@ export interface CategoriesSectionConfig {
196
196
  maxCategories: 4;
197
197
  autoScroll: boolean;
198
198
  scrollInterval: number;
199
+ /** Circular auto-rotate at the carousel edges. Default: true. */
200
+ loop?: boolean;
199
201
  /** Optional CTA button rendered below the scroller. */
200
202
  cta?: SectionCTA;
201
203
  /** Optional client-side filter chips above the scroller. */
@@ -213,6 +215,8 @@ export interface BrandsSectionConfig {
213
215
  maxBrands: number;
214
216
  autoScroll: boolean;
215
217
  scrollInterval: number;
218
+ /** Circular auto-rotate at the carousel edges. Default: true. */
219
+ loop?: boolean;
216
220
  /** Optional CTA button rendered below the scroller. */
217
221
  cta?: SectionCTA;
218
222
  /** Optional client-side filter chips above the scroller. */
@@ -306,6 +310,8 @@ export interface ReviewsSectionConfig {
306
310
  mobileItemsPerView: 1;
307
311
  autoScroll: boolean;
308
312
  scrollInterval: number;
313
+ /** Circular auto-rotate at the carousel edges. Default: true. */
314
+ loop?: boolean;
309
315
  source?: "platform" | "google";
310
316
  placeId?: string;
311
317
  }
@@ -496,6 +502,8 @@ export interface CustomCardsSectionConfig {
496
502
  cards: CustomCardsCard[];
497
503
  autoScroll?: boolean;
498
504
  scrollIntervalMs?: number;
505
+ /** Circular auto-rotate at the carousel edges. Default: true. */
506
+ loop?: boolean;
499
507
  }
500
508
  export type CollectionCardType = "products" | "auctions" | "pre-orders" | "stores" | "events" | "blog-posts" | "reviews" | "brands" | "categories";
501
509
  export interface CollectionCardsEntry {
@@ -86,6 +86,7 @@ export const homepageSectionsSeedData = [
86
86
  maxCategories: 4,
87
87
  autoScroll: true,
88
88
  scrollInterval: 4000,
89
+ loop: true,
89
90
  },
90
91
  createdAt: daysAgo(90),
91
92
  updatedAt: daysAgo(2),
@@ -101,6 +102,7 @@ export const homepageSectionsSeedData = [
101
102
  maxBrands: 13,
102
103
  autoScroll: true,
103
104
  scrollInterval: 3000,
105
+ loop: true,
104
106
  },
105
107
  createdAt: daysAgo(90),
106
108
  updatedAt: daysAgo(2),
@@ -119,6 +121,7 @@ export const homepageSectionsSeedData = [
119
121
  mobileItemsPerRow: 2,
120
122
  autoScroll: true,
121
123
  scrollInterval: 5000,
124
+ loop: true,
122
125
  },
123
126
  createdAt: daysAgo(90),
124
127
  updatedAt: daysAgo(2),
@@ -137,6 +140,7 @@ export const homepageSectionsSeedData = [
137
140
  mobileItemsPerRow: 1,
138
141
  autoScroll: true,
139
142
  scrollInterval: 5000,
143
+ loop: true,
140
144
  },
141
145
  createdAt: daysAgo(90),
142
146
  updatedAt: daysAgo(2),
@@ -155,6 +159,7 @@ export const homepageSectionsSeedData = [
155
159
  mobileItemsPerRow: 2,
156
160
  autoScroll: true,
157
161
  scrollInterval: 5000,
162
+ loop: true,
158
163
  },
159
164
  createdAt: daysAgo(90),
160
165
  updatedAt: daysAgo(2),
@@ -214,6 +219,7 @@ export const homepageSectionsSeedData = [
214
219
  mobileItemsPerView: 1,
215
220
  autoScroll: true,
216
221
  scrollInterval: 4000,
222
+ loop: true,
217
223
  },
218
224
  createdAt: daysAgo(90),
219
225
  updatedAt: daysAgo(2),
@@ -229,6 +235,7 @@ export const homepageSectionsSeedData = [
229
235
  maxStores: 8,
230
236
  autoScroll: true,
231
237
  scrollInterval: 4000,
238
+ loop: true,
232
239
  },
233
240
  createdAt: daysAgo(90),
234
241
  updatedAt: daysAgo(2),
@@ -244,6 +251,7 @@ export const homepageSectionsSeedData = [
244
251
  maxEvents: 6,
245
252
  autoScroll: true,
246
253
  scrollInterval: 6000,
254
+ loop: true,
247
255
  },
248
256
  createdAt: daysAgo(60),
249
257
  updatedAt: daysAgo(2),
@@ -374,6 +382,7 @@ export const homepageSectionsSeedData = [
374
382
  mobileItemsPerRow: 2,
375
383
  autoScroll: true,
376
384
  scrollInterval: 5000,
385
+ loop: true,
377
386
  filterByBrand: "brand-konami",
378
387
  sortBy: "featured",
379
388
  },
@@ -394,6 +403,7 @@ export const homepageSectionsSeedData = [
394
403
  mobileItemsPerRow: 2,
395
404
  autoScroll: true,
396
405
  scrollInterval: 5000,
406
+ loop: true,
397
407
  filterByBrand: "brand-upper-deck",
398
408
  sortBy: "featured",
399
409
  },
@@ -294,6 +294,9 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
294
294
  ? {
295
295
  onMouseEnter: () => setIsPaused(true),
296
296
  onMouseLeave: () => setIsPaused(false),
297
+ onTouchStart: () => setIsPaused(true),
298
+ onTouchEnd: () => setIsPaused(false),
299
+ onTouchCancel: () => setIsPaused(false),
297
300
  }
298
301
  : {};
299
302
  const combinedOnScroll = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "3.9.0",
3
+ "version": "3.9.1",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"