@fluid-app/portal-sdk 0.1.363 → 0.1.365
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/{AddressAutocompleteInput-cy8R9p6W.mjs → AddressAutocompleteInput-Cdzkv9pG.mjs} +2 -2
- package/dist/{AddressAutocompleteInput-cy8R9p6W.mjs.map → AddressAutocompleteInput-Cdzkv9pG.mjs.map} +1 -1
- package/dist/{AddressAutocompleteInput-BW0MZsjD.cjs → AddressAutocompleteInput-DSnPRJC3.cjs} +2 -2
- package/dist/{AddressAutocompleteInput-BW0MZsjD.cjs.map → AddressAutocompleteInput-DSnPRJC3.cjs.map} +1 -1
- package/dist/{EmbedWidget-V-pGzMRC.mjs → EmbedWidget-DptTEIxf.mjs} +2 -3
- package/dist/{EmbedWidget-V-pGzMRC.mjs.map → EmbedWidget-DptTEIxf.mjs.map} +1 -1
- package/dist/{EmbedWidget-BMy0C8ik.cjs → EmbedWidget-DytP-YjE.cjs} +2 -3
- package/dist/{EmbedWidget-BMy0C8ik.cjs.map → EmbedWidget-DytP-YjE.cjs.map} +1 -1
- package/dist/{FluidProvider-JmlsI08v.cjs → FluidProvider-BCFoYrBk.cjs} +11 -11
- package/dist/{FluidProvider-JmlsI08v.cjs.map → FluidProvider-BCFoYrBk.cjs.map} +1 -1
- package/dist/{FluidProvider-Cwnzfzi6.mjs → FluidProvider-C_JQFKTO.mjs} +11 -11
- package/dist/{FluidProvider-Cwnzfzi6.mjs.map → FluidProvider-C_JQFKTO.mjs.map} +1 -1
- package/dist/{ImageWidget-B6DESmDt.mjs → ImageWidget-B2FEHT-v.mjs} +2 -3
- package/dist/ImageWidget-B2FEHT-v.mjs.map +1 -0
- package/dist/{ImageWidget-Cz1W-CaS.cjs → ImageWidget-r9sPbyeC.cjs} +2 -3
- package/dist/ImageWidget-r9sPbyeC.cjs.map +1 -0
- package/dist/{ListWidget-CRsc53kj.mjs → ListWidget-C1_Fd58S.mjs} +5 -4
- package/dist/ListWidget-C1_Fd58S.mjs.map +1 -0
- package/dist/{ListWidget-IxrLNslF.cjs → ListWidget-D_C0p7HR.cjs} +5 -4
- package/dist/ListWidget-D_C0p7HR.cjs.map +1 -0
- package/dist/{ListWidget-BOH7TJJj.cjs → ListWidget-hz_xueC7.cjs} +1 -1
- package/dist/{MessagingScreen-CgIlbc7d.cjs → MessagingScreen-BoCpuHlw.cjs} +7 -7
- package/dist/{MessagingScreen-BJr7zF2A.mjs → MessagingScreen-CRZNnglE.mjs} +2 -2
- package/dist/{MessagingScreen-BJr7zF2A.mjs.map → MessagingScreen-CRZNnglE.mjs.map} +1 -1
- package/dist/{MessagingScreen-DVDFx_8s.cjs → MessagingScreen-DuRXxDyH.cjs} +2 -2
- package/dist/{MessagingScreen-DVDFx_8s.cjs.map → MessagingScreen-DuRXxDyH.cjs.map} +1 -1
- package/dist/{MessagingScreen-BPJLhWGi.mjs → MessagingScreen-d5VcvmQ8.mjs} +7 -7
- package/dist/{NestedWidget-BPTo7RQZ.mjs → NestedWidget-9eJaAdz9.mjs} +3 -3
- package/dist/{NestedWidget-BPTo7RQZ.mjs.map → NestedWidget-9eJaAdz9.mjs.map} +1 -1
- package/dist/{NestedWidget-Bg6vB_A6.cjs → NestedWidget-COMzekmy.cjs} +3 -3
- package/dist/{NestedWidget-Bg6vB_A6.cjs.map → NestedWidget-COMzekmy.cjs.map} +1 -1
- package/dist/{NestedWidget-Bep9VZwW.cjs → NestedWidget-DzBrds9R.cjs} +1 -1
- package/dist/{OrdersScreen-BC95_oBa.cjs → OrdersScreen-B5wbSKFG.cjs} +2 -2
- package/dist/{OrdersScreen-BC95_oBa.cjs.map → OrdersScreen-B5wbSKFG.cjs.map} +1 -1
- package/dist/{OrdersScreen-ClfjwlXi.mjs → OrdersScreen-COyosXSI.mjs} +7 -7
- package/dist/{OrdersScreen-Ch9ia2Ga.mjs → OrdersScreen-DGyvgnv6.mjs} +2 -2
- package/dist/{OrdersScreen-Ch9ia2Ga.mjs.map → OrdersScreen-DGyvgnv6.mjs.map} +1 -1
- package/dist/{OrdersScreen-BvI394f1.cjs → OrdersScreen-k1Aj4gpU.cjs} +7 -7
- package/dist/{ProfileScreen-Bzfqc83K.cjs → ProfileScreen-DKcQ5XLk.cjs} +3 -3
- package/dist/{ProfileScreen-Bzfqc83K.cjs.map → ProfileScreen-DKcQ5XLk.cjs.map} +1 -1
- package/dist/{ProfileScreen-G3djGmU2.cjs → ProfileScreen-DzRJDoXb.cjs} +8 -8
- package/dist/{ProfileScreen-CEylhJU6.mjs → ProfileScreen-G_dAZ4R1.mjs} +3 -3
- package/dist/{ProfileScreen-CEylhJU6.mjs.map → ProfileScreen-G_dAZ4R1.mjs.map} +1 -1
- package/dist/{ProfileScreen-DpUI78i1.mjs → ProfileScreen-VG2dU7CZ.mjs} +8 -8
- package/dist/{ShopScreen-CtglELsz.mjs → ShopScreen-BJGOS9FV.mjs} +8 -12
- package/dist/ShopScreen-BJGOS9FV.mjs.map +1 -0
- package/dist/{ShopScreen-g_umkXKo.cjs → ShopScreen-Bf9Mi1AO.cjs} +7 -7
- package/dist/{ShopScreen-CPeoDfhv.cjs → ShopScreen-Bkz-pjcY.cjs} +8 -12
- package/dist/ShopScreen-Bkz-pjcY.cjs.map +1 -0
- package/dist/{ShopScreen-B1HjIt1A.mjs → ShopScreen-CLYRD_-M.mjs} +7 -7
- package/dist/{SubscriptionsScreen-C6iOeMz4.mjs → SubscriptionsScreen--1yYkN6q.mjs} +8 -8
- package/dist/{SubscriptionsScreen-DyZAbOFn.mjs → SubscriptionsScreen-BS1HtEba.mjs} +3 -3
- package/dist/{SubscriptionsScreen-DyZAbOFn.mjs.map → SubscriptionsScreen-BS1HtEba.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-BQZQNzIu.cjs → SubscriptionsScreen-C2O6VOBn.cjs} +8 -8
- package/dist/{SubscriptionsScreen-mAXtV4Oy.cjs → SubscriptionsScreen-k3sVg7uw.cjs} +3 -3
- package/dist/{SubscriptionsScreen-mAXtV4Oy.cjs.map → SubscriptionsScreen-k3sVg7uw.cjs.map} +1 -1
- package/dist/{VideoWidget-DEG652tg.mjs → VideoWidget-CCTaBwub.mjs} +3 -3
- package/dist/{VideoWidget-DEG652tg.mjs.map → VideoWidget-CCTaBwub.mjs.map} +1 -1
- package/dist/{VideoWidget-CTByie5N.cjs → VideoWidget-rnVfjbuI.cjs} +3 -3
- package/dist/{VideoWidget-CTByie5N.cjs.map → VideoWidget-rnVfjbuI.cjs.map} +1 -1
- package/dist/index.cjs +22 -22
- package/dist/index.d.cts +9 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +9 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +22 -22
- package/dist/registries-BWdQbKof.cjs.map +1 -1
- package/dist/registries-DT36l-bR.mjs.map +1 -1
- package/package.json +19 -19
- package/dist/ImageWidget-B6DESmDt.mjs.map +0 -1
- package/dist/ImageWidget-Cz1W-CaS.cjs.map +0 -1
- package/dist/ListWidget-CRsc53kj.mjs.map +0 -1
- package/dist/ListWidget-IxrLNslF.cjs.map +0 -1
- package/dist/ShopScreen-CPeoDfhv.cjs.map +0 -1
- package/dist/ShopScreen-CtglELsz.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ListWidget-CRsc53kj.mjs","names":["DOMPurify","getImageUrl"],"sources":["../../widgets/src/widgets/list-widget/helpers.ts","../../widgets/src/widgets/list-widget/FeaturedSection.tsx","../../widgets/src/widgets/list-widget/ListItemCard.tsx","../../widgets/src/widgets/list-widget/UnorderedList.tsx","../../widgets/src/widgets/list-widget/OrderedList.tsx","../../widgets/src/widgets/ListWidget.tsx"],"sourcesContent":["import type { BorderRadiusOptions } from \"@fluid-app/portal-core/types\";\n\n/**\n * Maps the schema's BorderRadiusOptions to a Tailwind `rounded-*` class.\n * `\"full\"` is converted to `rounded-2xl` because the list item cards and\n * featured section read better as large-radius rectangles than as\n * pill-shaped containers.\n */\nexport const cardRadiusClass = (radius: BorderRadiusOptions): string =>\n radius === \"full\" ? \"rounded-2xl\" : `rounded-${radius}`;\n","import type React from \"react\";\nimport type {\n BorderRadiusOptions,\n FontSizeOptions,\n ColorOptions,\n} from \"@fluid-app/portal-core/types\";\nimport { MediaRenderer } from \"../../components/MediaRenderer\";\nimport { cardRadiusClass } from \"./helpers\";\n\ninterface FeaturedSectionProps {\n borderRadius: BorderRadiusOptions;\n titleSize: FontSizeOptions;\n featuredTitle?: string | undefined;\n featuredSubtitle?: string | undefined;\n featuredButtonText?: string | undefined;\n featuredButtonUrl?: string | undefined;\n featuredSubtitleColor: ColorOptions;\n featuredSubtitleSize: FontSizeOptions;\n asset: { url: string; isVideo: boolean };\n}\n\nexport function FeaturedSection({\n borderRadius,\n titleSize,\n featuredTitle,\n featuredSubtitle,\n featuredButtonText,\n featuredButtonUrl,\n featuredSubtitleColor,\n featuredSubtitleSize,\n asset,\n}: FeaturedSectionProps): React.JSX.Element {\n return (\n <div\n className={`group relative h-full min-h-[320px] w-full overflow-hidden ${cardRadiusClass(borderRadius)}`}\n >\n <div className=\"absolute inset-0 h-full w-full\">\n <MediaRenderer\n src={asset.url}\n mediaType={asset.isVideo ? \"video\" : \"image\"}\n alt={featuredTitle || \"Featured\"}\n objectFit=\"cover\"\n autoplay={asset.isVideo}\n loop={asset.isVideo}\n muted={asset.isVideo}\n controls={false}\n />\n </div>\n\n <div\n className=\"pointer-events-none absolute inset-0\"\n style={{\n background:\n \"linear-gradient(180deg, rgba(0,0,0,0) 45%, rgba(0,0,0,0.55) 100%)\",\n }}\n />\n\n <div className=\"absolute inset-0 flex flex-col items-start justify-end p-10\">\n {featuredTitle && (\n <h3\n className={`font-header mb-3 font-medium tracking-tight text-white text-${titleSize === \"md\" ? \"base\" : titleSize}`}\n >\n {featuredTitle}\n </h3>\n )}\n {featuredSubtitle && (\n <p\n className={`mb-6 max-w-md text-${featuredSubtitleColor}/80 text-${featuredSubtitleSize === \"md\" ? \"base\" : featuredSubtitleSize} leading-relaxed`}\n >\n {featuredSubtitle}\n </p>\n )}\n {featuredButtonText && (\n <a\n href={featuredButtonUrl || \"#\"}\n className={`text-foreground inline-flex items-center gap-2 bg-white px-6 py-3 text-sm font-medium tracking-tight transition-opacity hover:opacity-90 ${cardRadiusClass(borderRadius)}`}\n >\n {featuredButtonText}\n <span\n aria-hidden=\"true\"\n className=\"transition-transform group-hover:translate-x-0.5\"\n >\n →\n </span>\n </a>\n )}\n </div>\n </div>\n );\n}\n","import type React from \"react\";\nimport DOMPurify from \"dompurify\";\nimport type {\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\n\ntype ListItem = {\n id: string;\n image?: string;\n imageUrl?: string;\n videoUrl?: string;\n title?: string;\n description?: string;\n price?: string;\n display_price?: string;\n originalPrice?: string;\n discount?: string;\n qv?: string;\n cv?: string;\n [key: string]: unknown;\n};\n\nfunction getStringValue(value: unknown): string {\n if (!value) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\") return String(value);\n if (typeof value === \"object\" && value !== null) {\n if (\"body\" in value) {\n const body = (value as { body: unknown }).body;\n if (typeof body === \"string\") return body;\n }\n if (\"text\" in value) {\n const text = (value as { text: unknown }).text;\n if (typeof text === \"string\") return text;\n }\n if (\"value\" in value) {\n const val = (value as { value: unknown }).value;\n if (typeof val === \"string\") return val;\n if (typeof val === \"number\") return String(val);\n }\n }\n return \"\";\n}\n\nfunction stripParentheticalText(text: string): string {\n return text.replace(/\\s*\\([^)]*\\)/g, \"\").trim();\n}\n\nfunction formatPrice(value: unknown): string {\n const str = getStringValue(value);\n if (!str) return \"\";\n return stripParentheticalText(str);\n}\n\nexport interface ListItemCardContentProps {\n item: ListItem;\n padding: PaddingOptions;\n itemTitleColor: ColorOptions;\n itemTitleSize: FontSizeOptions;\n descriptionColor: ColorOptions;\n descriptionSize: FontSizeOptions;\n priceColor: ColorOptions;\n priceSize: FontSizeOptions;\n originalPriceColor: ColorOptions;\n metaTextColor: ColorOptions;\n metaTextSize: FontSizeOptions;\n showMetaText: boolean;\n}\n\nexport function ListItemCardContent({\n item,\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n showMetaText,\n}: ListItemCardContentProps): React.JSX.Element {\n const hasPrice = Boolean(item.display_price || item.price);\n const hasOriginal = Boolean(item.originalPrice);\n\n return (\n <div className={`flex flex-1 flex-col p-${padding}`}>\n {item.title && (\n <h3\n className={`text-${itemTitleColor} text-${itemTitleSize === \"md\" ? \"base\" : itemTitleSize} font-header mb-1.5 leading-snug font-medium tracking-tight`}\n >\n {getStringValue(item.title)}\n </h3>\n )}\n {item.description && (\n <div\n className={`text-${descriptionColor}/60 text-${descriptionSize === \"md\" ? \"base\" : descriptionSize} mb-3 line-clamp-2 overflow-hidden leading-relaxed`}\n dangerouslySetInnerHTML={{\n __html: DOMPurify.sanitize(item.description ?? \"\", {\n ALLOWED_TAGS: [\n \"br\",\n \"strong\",\n \"em\",\n \"b\",\n \"i\",\n \"ul\",\n \"ol\",\n \"li\",\n \"p\",\n ],\n ALLOWED_ATTR: [],\n }),\n }}\n />\n )}\n {(hasPrice || hasOriginal) && (\n <div className=\"mt-auto flex items-baseline gap-2\">\n {hasPrice && (\n <span\n className={`text-${priceColor} text-${priceSize === \"md\" ? \"base\" : priceSize} font-semibold tracking-tight tabular-nums`}\n >\n {formatPrice(item.display_price || item.price)}\n </span>\n )}\n {hasOriginal && (\n <span\n className={`text-${originalPriceColor}/50 text-${descriptionSize === \"md\" ? \"base\" : descriptionSize} tabular-nums line-through`}\n >\n {formatPrice(item.originalPrice)}\n </span>\n )}\n </div>\n )}\n {showMetaText && (item.qv || item.cv) && (\n <div\n className={`mt-2 flex gap-4 text-${metaTextColor}/70 text-${metaTextSize === \"md\" ? \"base\" : metaTextSize} font-medium tracking-wide uppercase`}\n >\n {item.qv && (\n <span>\n <span className=\"opacity-50\">QV</span>{\" \"}\n <span className=\"tabular-nums\">{getStringValue(item.qv)}</span>\n </span>\n )}\n {item.cv && (\n <span>\n <span className=\"opacity-50\">CV</span>{\" \"}\n <span className=\"tabular-nums\">{getStringValue(item.cv)}</span>\n </span>\n )}\n </div>\n )}\n </div>\n );\n}\n\nexport function DiscountBadge({\n discount,\n}: {\n discount: string;\n}): React.JSX.Element {\n return (\n <div className=\"bg-background text-foreground absolute top-3 left-3 z-10 rounded-full px-2.5 py-1 text-[10px] font-semibold tracking-[0.08em] uppercase\">\n {getStringValue(discount)}\n </div>\n );\n}\n\nexport { getStringValue };\nexport type { ListItem };\n","import type React from \"react\";\nimport type {\n ColorOptions,\n FontSizeOptions,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n} from \"@fluid-app/portal-core/types\";\nimport { gapValues } from \"../../core/fields\";\nimport { useWidgetInteraction } from \"../../contexts/WidgetInteractionContext\";\nimport {\n ListItemCardContent,\n DiscountBadge,\n type ListItem,\n} from \"./ListItemCard\";\nimport { cardRadiusClass } from \"./helpers\";\n\ntype ImageAspectRatio = \"square\" | \"landscape\" | \"portrait\";\n\nconst getImageUrl = (item: ListItem): string | undefined => {\n return item.imageUrl || item.image;\n};\n\nconst getAspectRatioClass = (ratio: ImageAspectRatio): string => {\n const ratios = {\n square: \"aspect-square\",\n landscape: \"aspect-video\",\n portrait: \"aspect-[3/4]\",\n };\n return ratios[ratio];\n};\n\nconst getResponsiveGridClass = (columns: number): string => {\n const responsiveClasses: Record<number, string> = {\n 1: \"grid-cols-2 @lg:grid-cols-1\",\n 2: \"grid-cols-2 @lg:grid-cols-2\",\n 3: \"grid-cols-2 @lg:grid-cols-3\",\n 4: \"grid-cols-2 @lg:grid-cols-4\",\n 5: \"grid-cols-2 @lg:grid-cols-5\",\n 6: \"grid-cols-2 @lg:grid-cols-6\",\n };\n return responsiveClasses[columns] || \"grid-cols-2 @lg:grid-cols-3\";\n};\n\nexport interface UnorderedListProps {\n items: ListItem[];\n columns: number;\n gap: GapOptions;\n borderRadius: BorderRadiusOptions;\n imageAspectRatio: ImageAspectRatio;\n showBadge: boolean;\n padding: PaddingOptions;\n itemTitleColor: ColorOptions;\n itemTitleSize: FontSizeOptions;\n descriptionColor: ColorOptions;\n descriptionSize: FontSizeOptions;\n priceColor: ColorOptions;\n priceSize: FontSizeOptions;\n originalPriceColor: ColorOptions;\n metaTextColor: ColorOptions;\n metaTextSize: FontSizeOptions;\n showMetaText: boolean;\n}\n\nexport function UnorderedList({\n items,\n columns,\n gap,\n borderRadius,\n imageAspectRatio,\n showBadge,\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n showMetaText,\n}: UnorderedListProps): React.JSX.Element {\n const gridClass = getResponsiveGridClass(columns);\n const { onItemClick } = useWidgetInteraction();\n\n return (\n <div className={`grid ${gridClass} gap-${gapValues[gap]}`}>\n {items.map((item) => {\n const imageUrl = getImageUrl(item);\n const aspectRatioClass = getAspectRatioClass(imageAspectRatio);\n\n return (\n <div\n key={item.id}\n className={`group relative flex flex-col ${onItemClick ? \"cursor-pointer\" : \"\"}`}\n {...(onItemClick\n ? {\n role: \"button\",\n tabIndex: 0,\n onClick: () => onItemClick(item),\n onKeyDown: (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onItemClick(item);\n }\n },\n }\n : {})}\n >\n {imageUrl && (\n <div\n className={`relative w-full ${aspectRatioClass} bg-muted/30 overflow-hidden ${cardRadiusClass(borderRadius)}`}\n >\n {showBadge && item.discount && (\n <DiscountBadge discount={item.discount} />\n )}\n <img\n src={imageUrl}\n alt={item.title || \"Product\"}\n className=\"h-full w-full object-cover transition-opacity duration-300 group-hover:opacity-90\"\n />\n </div>\n )}\n <div className=\"mt-4\">\n <ListItemCardContent\n item={item}\n padding={padding}\n itemTitleColor={itemTitleColor}\n itemTitleSize={itemTitleSize}\n descriptionColor={descriptionColor}\n descriptionSize={descriptionSize}\n priceColor={priceColor}\n priceSize={priceSize}\n originalPriceColor={originalPriceColor}\n metaTextColor={metaTextColor}\n metaTextSize={metaTextSize}\n showMetaText={showMetaText}\n />\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n","import { useRef } from \"react\";\nimport type React from \"react\";\nimport type { RefObject } from \"react\";\nimport type {\n ColorOptions,\n FontSizeOptions,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n} from \"@fluid-app/portal-core/types\";\nimport { gapValues } from \"../../core/fields\";\nimport { useWidgetInteraction } from \"../../contexts/WidgetInteractionContext\";\nimport {\n ListItemCardContent,\n DiscountBadge,\n getStringValue,\n type ListItem,\n} from \"./ListItemCard\";\nimport { cardRadiusClass } from \"./helpers\";\n\ntype ImageAspectRatio = \"square\" | \"landscape\" | \"portrait\";\ntype ScrollAxis = \"horizontal\" | \"vertical\";\n\nconst getImageUrl = (item: ListItem): string | undefined => {\n return item.imageUrl || item.image;\n};\n\nexport interface OrderedListProps {\n items: ListItem[];\n scrollAxis: ScrollAxis;\n gap: GapOptions;\n borderRadius: BorderRadiusOptions;\n imageAspectRatio: ImageAspectRatio;\n showBadge: boolean;\n padding: PaddingOptions;\n itemTitleColor: ColorOptions;\n itemTitleSize: FontSizeOptions;\n descriptionColor: ColorOptions;\n descriptionSize: FontSizeOptions;\n priceColor: ColorOptions;\n priceSize: FontSizeOptions;\n originalPriceColor: ColorOptions;\n metaTextColor: ColorOptions;\n metaTextSize: FontSizeOptions;\n numberColor: ColorOptions;\n numberSize: FontSizeOptions;\n showMetaText: boolean;\n /** Optional ref from the parent to control horizontal scroll externally */\n scrollContainerRef?: RefObject<HTMLDivElement | null> | undefined;\n}\n\nconst formatRank = (n: number): string => (n < 10 ? `0${n}` : String(n));\n\nexport function OrderedList({\n items,\n scrollAxis,\n gap,\n borderRadius,\n // imageAspectRatio is intentionally not consumed — ordered layouts use a\n // fixed aspect ratio per axis (3:4 poster horizontal, 16x16 thumb vertical)\n // by design. The schema gates this field to unordered lists.\n imageAspectRatio: _imageAspectRatio,\n showBadge,\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n numberColor,\n numberSize,\n showMetaText,\n scrollContainerRef: externalScrollRef,\n}: OrderedListProps): React.JSX.Element | null {\n const internalScrollRef = useRef<HTMLDivElement>(null);\n const scrollContainerRef = externalScrollRef ?? internalScrollRef;\n\n const { onItemClick } = useWidgetInteraction();\n\n const interactionProps = (item: ListItem) =>\n onItemClick\n ? ({\n role: \"button\" as const,\n tabIndex: 0,\n onClick: () => onItemClick(item),\n onKeyDown: (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onItemClick(item);\n }\n },\n } as const)\n : {};\n\n const contentProps = {\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n showMetaText,\n };\n\n if (scrollAxis === \"horizontal\") {\n return (\n <div className=\"relative\">\n <div\n ref={scrollContainerRef}\n className={`flex gap-${gapValues[gap]} scrollbar-hide overflow-x-auto overflow-y-hidden scroll-smooth`}\n >\n {items.map((item, index) => {\n const imageUrl = getImageUrl(item);\n const rank = index + 1;\n\n return (\n <div\n key={item.id}\n className={`group relative flex flex-shrink-0 flex-col ${onItemClick ? \"cursor-pointer\" : \"\"}`}\n {...interactionProps(item)}\n >\n <div className=\"flex items-end\">\n <span className=\"sr-only\">Rank {rank}: </span>\n <span\n className={`text-${numberColor} pointer-events-none flex-shrink-0 font-black italic tabular-nums select-none`}\n style={{\n fontSize: \"11rem\",\n lineHeight: \"0.82\",\n letterSpacing: \"-0.06em\",\n }}\n aria-hidden=\"true\"\n >\n {rank}\n </span>\n {imageUrl && (\n <div\n className={`relative aspect-[3/4] w-[200px] flex-shrink-0 ${cardRadiusClass(borderRadius)} bg-muted/30 overflow-hidden`}\n >\n {showBadge && item.discount && (\n <DiscountBadge discount={item.discount} />\n )}\n <img\n src={imageUrl}\n alt={item.title || \"Product\"}\n className=\"h-full w-full object-cover\"\n />\n </div>\n )}\n </div>\n <div className=\"mt-5 ml-auto w-[200px]\">\n <ListItemCardContent item={item} {...contentProps} />\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n }\n\n if (scrollAxis === \"vertical\") {\n return (\n <div className={`flex flex-col gap-${gapValues[gap]}`}>\n {items.map((item, index) => {\n const imageUrl = getImageUrl(item);\n const rank = index + 1;\n const isLast = index === items.length - 1;\n\n return (\n <div\n key={item.id}\n className={`group relative flex items-center gap-${gapValues[gap]} py-6 ${!isLast ? \"border-b\" : \"\"} ${onItemClick ? \"cursor-pointer\" : \"\"}`}\n style={\n !isLast\n ? {\n borderColor:\n \"color-mix(in oklch, var(--color-foreground) 8%, transparent)\",\n }\n : undefined\n }\n {...interactionProps(item)}\n >\n <span className=\"sr-only\">Rank {rank}: </span>\n <span\n className={`text-${numberColor} font-header w-14 flex-shrink-0 text-${numberSize === \"md\" ? \"base\" : numberSize} leading-none font-light tracking-tight italic`}\n aria-hidden=\"true\"\n >\n {formatRank(rank)}\n </span>\n {imageUrl && (\n <div\n className={`relative h-16 w-16 flex-shrink-0 overflow-hidden ${cardRadiusClass(borderRadius)} bg-muted/30`}\n >\n <img\n src={imageUrl}\n alt={item.title || \"Product\"}\n className=\"h-full w-full object-cover\"\n />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <ListItemCardContent item={item} {...contentProps} />\n </div>\n {showBadge && item.discount && (\n <span className=\"text-muted ml-2 flex-shrink-0 text-[10px] font-semibold tracking-[0.08em] uppercase\">\n {getStringValue(item.discount)}\n </span>\n )}\n </div>\n );\n })}\n </div>\n );\n }\n\n return null;\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ComponentProps,\n} from \"react\";\nimport type React from \"react\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport type {\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n BackgroundValue,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n borderWidthClasses,\n borderColorClasses,\n getColorField,\n getFontSizeField,\n getPaddingField,\n getGapField,\n} from \"../core/fields\";\nimport { FeaturedSection } from \"./list-widget/FeaturedSection\";\nimport { UnorderedList } from \"./list-widget/UnorderedList\";\nimport { OrderedList } from \"./list-widget/OrderedList\";\n\nconst DEFAULT_ITEMS: ListItem[] = [];\n\ntype ListItem = {\n id: string;\n image?: string;\n imageUrl?: string;\n videoUrl?: string;\n title?: string;\n description?: string;\n price?: string;\n originalPrice?: string;\n discount?: string;\n qv?: string;\n cv?: string;\n [key: string]: unknown;\n};\n\ntype ImageAspectRatio = \"square\" | \"landscape\" | \"portrait\";\ntype ListType = \"ordered\" | \"unordered\";\ntype ScrollAxis = \"horizontal\" | \"vertical\";\n\ntype ListWidgetProps = ComponentProps<\"div\"> & {\n // List configuration\n listType?: ListType;\n scrollAxis?: ScrollAxis;\n titleEnabled?: boolean;\n title?: string;\n items?: ListItem[];\n\n // Text styling\n titleColor?: ColorOptions;\n titleSize?: FontSizeOptions;\n itemTitleColor?: ColorOptions;\n itemTitleSize?: FontSizeOptions;\n descriptionColor?: ColorOptions;\n descriptionSize?: FontSizeOptions;\n priceColor?: ColorOptions;\n priceSize?: FontSizeOptions;\n originalPriceColor?: ColorOptions;\n metaTextColor?: ColorOptions;\n metaTextSize?: FontSizeOptions;\n\n // Ordered list styling\n numberColor?: ColorOptions;\n numberSize?: FontSizeOptions;\n\n // Layout\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n padding?: PaddingOptions;\n gap?: GapOptions;\n columns?: number;\n imageAspectRatio?: ImageAspectRatio;\n background?: BackgroundValue;\n\n // Behavior\n showBadge?: boolean;\n showMetaText?: boolean;\n maxItems?: number;\n\n // Featured section\n showFeaturedSection?: boolean;\n featuredAsset?: string | { [key: string]: unknown };\n featuredTitle?: string;\n featuredSubtitle?: string;\n featuredButtonText?: string;\n featuredButtonUrl?: string;\n featuredSubtitleColor?: ColorOptions;\n featuredSubtitleSize?: FontSizeOptions;\n};\n\nfunction getFeaturedAssetUrl(\n value: string | { [key: string]: unknown } | undefined,\n): { url: string; isVideo: boolean } | undefined {\n if (!value) return undefined;\n\n // Handle string URLs\n if (typeof value === \"string\") {\n const isVideo = /\\.(mp4|webm|ogg|mov)$/i.test(value);\n return { url: value, isVideo };\n }\n\n // Handle ShareableItem objects\n if (typeof value === \"object\") {\n // Check for video URL first\n const videoUrl =\n (value.videoUrl as string) ||\n (value.video_url as string) ||\n (value.url as string);\n if (videoUrl && /\\.(mp4|webm|ogg|mov)$/i.test(videoUrl)) {\n return { url: videoUrl, isVideo: true };\n }\n\n // Fall back to image URL\n const imageUrl =\n (value.imageUrl as string) ||\n (value.image_url as string) ||\n (value.url as string);\n if (imageUrl) {\n return { url: imageUrl, isVideo: false };\n }\n }\n\n return undefined;\n}\n\nexport function ListWidget({\n listType = \"unordered\",\n scrollAxis = \"horizontal\",\n titleEnabled = true,\n title,\n items = DEFAULT_ITEMS,\n titleColor = \"foreground\",\n titleSize = \"lg\",\n itemTitleColor = \"foreground\",\n itemTitleSize = \"sm\",\n descriptionColor = \"foreground\",\n descriptionSize = \"sm\",\n priceColor = \"foreground\",\n priceSize = \"md\",\n originalPriceColor = \"muted\",\n metaTextColor = \"muted\",\n metaTextSize = \"xs\",\n numberColor = \"primary\",\n numberSize = \"2xl\",\n borderRadius = \"md\",\n borderWidth = \"none\",\n borderColor = \"muted\",\n padding = 4,\n gap = \"md\",\n columns = 3,\n imageAspectRatio = \"square\",\n background = { type: \"solid\", color: \"background\" },\n showBadge = true,\n showMetaText = true,\n maxItems = 12,\n showFeaturedSection = false,\n featuredAsset,\n featuredTitle,\n featuredSubtitle,\n featuredButtonText,\n featuredButtonUrl,\n featuredSubtitleColor = \"background\",\n featuredSubtitleSize = \"md\",\n className,\n ...props\n}: ListWidgetProps): React.JSX.Element {\n const displayItems = maxItems ? items.slice(0, maxItems) : items;\n const hasItems = displayItems.length > 0;\n\n const itemStyleProps = {\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n showMetaText,\n showBadge,\n borderRadius,\n imageAspectRatio,\n gap,\n };\n\n const hasFeaturedAsset = getFeaturedAssetUrl(featuredAsset);\n const shouldShowFeaturedSection = showFeaturedSection && hasFeaturedAsset;\n\n // Background styling\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n const isOrderedHorizontal =\n listType === \"ordered\" && scrollAxis === \"horizontal\";\n const showScrollArrows =\n isOrderedHorizontal && hasItems && displayItems.length > 1;\n\n const { scrollContainerRef, canScrollPrev, canScrollNext, scrollByAmount } =\n useHorizontalScrollState({\n enabled: showScrollArrows,\n contentLength: displayItems.length,\n });\n\n const hasTitle = titleEnabled && title;\n const showTitleRow = hasTitle || showScrollArrows;\n\n return (\n <div\n className={`@container bg-${backgroundColor} p-${padding} rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n {showTitleRow && (\n <div className=\"mb-8 flex items-center justify-between gap-4\">\n {hasTitle ? (\n <h2\n className={`text-${titleColor} text-${titleSize === \"md\" ? \"base\" : titleSize} font-header leading-tight font-medium tracking-tight`}\n >\n {title}\n </h2>\n ) : (\n <span />\n )}\n {showScrollArrows && (\n <div className=\"flex shrink-0 items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => scrollByAmount(\"prev\")}\n disabled={!canScrollPrev}\n aria-label=\"Previous items\"\n className={`text-${numberColor} flex size-9 items-center justify-center rounded-full transition-opacity hover:opacity-60 disabled:cursor-not-allowed disabled:opacity-20`}\n style={{\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${numberColor}) 25%, transparent)`,\n }}\n >\n <ChevronLeft aria-hidden=\"true\" className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => scrollByAmount(\"next\")}\n disabled={!canScrollNext}\n aria-label=\"Next items\"\n className={`text-${numberColor} flex size-9 items-center justify-center rounded-full transition-opacity hover:opacity-60 disabled:cursor-not-allowed disabled:opacity-20`}\n style={{\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${numberColor}) 25%, transparent)`,\n }}\n >\n <ChevronRight aria-hidden=\"true\" className=\"size-4\" />\n </button>\n </div>\n )}\n </div>\n )}\n {!hasItems ? (\n <div className=\"flex items-center justify-center py-20 text-center\">\n <p className=\"text-muted/70 text-sm tracking-tight\">\n No items to display\n </p>\n </div>\n ) : shouldShowFeaturedSection ? (\n <div className=\"flex flex-col gap-4 @lg:flex-row @lg:gap-6\">\n <div className=\"w-full @lg:w-[45%]\">\n <FeaturedSection\n borderRadius={borderRadius}\n titleSize={titleSize}\n featuredTitle={featuredTitle}\n featuredSubtitle={featuredSubtitle}\n featuredButtonText={featuredButtonText}\n featuredButtonUrl={featuredButtonUrl}\n featuredSubtitleColor={featuredSubtitleColor}\n featuredSubtitleSize={featuredSubtitleSize}\n asset={hasFeaturedAsset!}\n />\n </div>\n <div className=\"w-full @lg:w-[55%]\">\n {listType === \"unordered\" ? (\n <UnorderedList\n items={displayItems}\n columns={columns}\n {...itemStyleProps}\n />\n ) : (\n <OrderedList\n items={displayItems}\n scrollAxis={scrollAxis}\n numberColor={numberColor}\n numberSize={numberSize}\n scrollContainerRef={\n isOrderedHorizontal ? scrollContainerRef : undefined\n }\n {...itemStyleProps}\n />\n )}\n </div>\n </div>\n ) : listType === \"unordered\" ? (\n <UnorderedList\n items={displayItems}\n columns={columns}\n {...itemStyleProps}\n />\n ) : (\n <OrderedList\n items={displayItems}\n scrollAxis={scrollAxis}\n numberColor={numberColor}\n numberSize={numberSize}\n scrollContainerRef={\n isOrderedHorizontal ? scrollContainerRef : undefined\n }\n {...itemStyleProps}\n />\n )}\n </div>\n );\n}\n\n// ----- Hooks -----\n\nconst SCROLL_BUTTON_AMOUNT = 300;\nconst SCROLL_EDGE_TOLERANCE = 4;\nconst useIsomorphicLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\ntype HorizontalScrollState = {\n scrollContainerRef: React.RefObject<HTMLDivElement | null>;\n canScrollPrev: boolean;\n canScrollNext: boolean;\n scrollByAmount: (direction: \"prev\" | \"next\") => void;\n};\n\n// Tracks horizontal scroll position of an external container so the parent\n// can drive prev/next arrow disabled states from a single source of truth.\n// Both edges initialize to `false` and are populated by the post-mount\n// measurement, avoiding a brief enabled-then-disabled flicker when content\n// fits entirely within the viewport.\nfunction useHorizontalScrollState({\n enabled,\n contentLength,\n}: {\n enabled: boolean;\n contentLength: number;\n}): HorizontalScrollState {\n const scrollContainerRef = useRef<HTMLDivElement>(null);\n const [canScrollPrev, setCanScrollPrev] = useState(false);\n const [canScrollNext, setCanScrollNext] = useState(false);\n\n const resetScrollState = useCallback(() => {\n setCanScrollPrev(false);\n setCanScrollNext(false);\n }, []);\n\n const updateScrollState = useCallback(() => {\n const el = scrollContainerRef.current;\n if (!enabled || !el) {\n resetScrollState();\n return;\n }\n setCanScrollPrev(el.scrollLeft > SCROLL_EDGE_TOLERANCE);\n setCanScrollNext(\n el.scrollLeft + el.clientWidth < el.scrollWidth - SCROLL_EDGE_TOLERANCE,\n );\n }, [enabled, resetScrollState]);\n\n // Fires synchronously before paint so the arrows reflect post-layout\n // geometry without a flicker. `updateScrollState` is memoized on\n // `enabled`, so this only fires on mount and when toggling enablement —\n // not on every render.\n useIsomorphicLayoutEffect(() => {\n updateScrollState();\n }, [updateScrollState]);\n\n useEffect(() => {\n if (!enabled) {\n resetScrollState();\n return undefined;\n }\n\n const el = scrollContainerRef.current;\n if (!el) {\n resetScrollState();\n return undefined;\n }\n\n updateScrollState();\n el.addEventListener(\"scroll\", updateScrollState, { passive: true });\n const resize =\n typeof ResizeObserver !== \"undefined\"\n ? new ResizeObserver(updateScrollState)\n : undefined;\n resize?.observe(el);\n\n return () => {\n el.removeEventListener(\"scroll\", updateScrollState);\n resize?.disconnect();\n };\n }, [enabled, resetScrollState, updateScrollState, contentLength]);\n\n const scrollByAmount = useCallback((direction: \"prev\" | \"next\") => {\n const el = scrollContainerRef.current;\n if (!el) return;\n const computedGap = parseFloat(getComputedStyle(el).gap) || 0;\n const amount = SCROLL_BUTTON_AMOUNT + computedGap;\n el.scrollTo({\n left: el.scrollLeft + (direction === \"next\" ? amount : -amount),\n behavior: \"smooth\",\n });\n }, []);\n\n return { scrollContainerRef, canScrollPrev, canScrollNext, scrollByAmount };\n}\n\nexport const listWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"ListWidget\",\n displayName: \"List\",\n tabsConfig: [\n { id: \"styling\", label: \"Styling\" },\n { id: \"data\", label: \"Data\" },\n ],\n dataSourceTargetProps: [\"items\"],\n fields: [\n // Styling tab - Title group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the list\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"title\",\n label: \"Title\",\n type: \"text\",\n description: \"Header text for the list\",\n defaultValue: \"List\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleSize\",\n label: \"Title Font Size\",\n description: \"Size of the list title\",\n defaultValue: \"xl\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color for the list title\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n\n // Styling tab - Design group\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding inside the container\",\n defaultValue: 4,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Rounded corners for the container and images\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Border width for the widget\",\n defaultValue: \"none\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderColorField({\n key: \"borderColor\",\n label: \"Border Color\",\n description: \"Border color for the widget\",\n defaultValue: \"muted\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getGapField({\n key: \"gap\",\n label: \"Gap\",\n description: \"Spacing between items\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n type: \"background\",\n defaultValue: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background color or image for the widget\",\n tab: \"styling\",\n group: \"Design\",\n },\n\n // Styling tab - List Configuration group\n {\n key: \"listType\",\n label: \"List Type\",\n type: \"select\",\n description: \"Type of list layout\",\n defaultValue: \"unordered\",\n options: [\n { label: \"Unordered (Grid)\", value: \"unordered\" },\n { label: \"Ordered (Numbered)\", value: \"ordered\" },\n ],\n tab: \"styling\",\n group: \"List Configuration\",\n },\n {\n key: \"maxItems\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of items to display\",\n min: 1,\n max: 100,\n step: 1,\n defaultValue: 12,\n tab: \"styling\",\n group: \"List Configuration\",\n },\n {\n key: \"imageAspectRatio\",\n label: \"Image Aspect Ratio\",\n type: \"buttonGroup\",\n description:\n \"Aspect ratio for item images (unordered list — ordered layouts use a fixed ratio per axis)\",\n defaultValue: \"square\",\n options: [\n { label: \"Square\", value: \"square\" },\n { label: \"Landscape\", value: \"landscape\" },\n { label: \"Portrait\", value: \"portrait\" },\n ],\n tab: \"styling\",\n group: \"List Configuration\",\n requiresKeyValue: { key: \"listType\", value: \"unordered\" },\n },\n {\n key: \"showBadge\",\n label: \"Show Discount Badge\",\n type: \"boolean\",\n description: \"Display discount badge on images\",\n defaultValue: true,\n tab: \"styling\",\n group: \"List Configuration\",\n },\n {\n key: \"showMetaText\",\n label: \"Show QV/CV Text\",\n type: \"boolean\",\n description: \"Display QV and CV values\",\n defaultValue: true,\n tab: \"styling\",\n group: \"List Configuration\",\n },\n\n // Styling tab - Unordered List Configuration\n {\n key: \"columns\",\n label: \"Grid Columns\",\n type: \"number\",\n description: \"Number of columns in the grid (unordered list only)\",\n min: 1,\n max: 6,\n step: 1,\n defaultValue: 3,\n tab: \"styling\",\n group: \"Unordered List Configuration\",\n requiresKeyValue: { key: \"listType\", value: \"unordered\" },\n },\n\n // Styling tab - Ordered List Configuration\n {\n key: \"scrollAxis\",\n label: \"Scroll Direction\",\n type: \"select\",\n description: \"Direction for ordered list scrolling\",\n defaultValue: \"horizontal\",\n options: [\n { label: \"Horizontal\", value: \"horizontal\" },\n { label: \"Vertical\", value: \"vertical\" },\n ],\n tab: \"styling\",\n group: \"Ordered List Configuration\",\n requiresKeyValue: { key: \"listType\", value: \"ordered\" },\n },\n getColorField({\n key: \"numberColor\",\n label: \"Number Color\",\n description: \"Color for ordered list numbers\",\n defaultValue: \"primary\",\n tab: \"styling\",\n group: \"Ordered List Configuration\",\n requiresKeyValue: { key: \"listType\", value: \"ordered\" },\n }),\n getFontSizeField({\n key: \"numberSize\",\n label: \"Number Font Size\",\n description:\n \"Size of ordered list numbers (vertical scroll only — horizontal uses a fixed editorial size)\",\n defaultValue: \"2xl\",\n tab: \"styling\",\n group: \"Ordered List Configuration\",\n requiresKeyValue: [\n { key: \"listType\", value: \"ordered\" },\n { key: \"scrollAxis\", value: \"vertical\" },\n ],\n }),\n\n // Styling tab - Item Styling group\n getColorField({\n key: \"itemTitleColor\",\n label: \"Item Title Color\",\n description: \"Color for item titles\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getFontSizeField({\n key: \"itemTitleSize\",\n label: \"Item Title Font Size\",\n description: \"Size of item titles\",\n defaultValue: \"sm\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n {\n key: \"separator2\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Item Styling\",\n },\n getColorField({\n key: \"descriptionColor\",\n label: \"Description Color\",\n description: \"Color for descriptions\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getFontSizeField({\n key: \"descriptionSize\",\n label: \"Description Font Size\",\n description: \"Size of descriptions\",\n defaultValue: \"sm\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n {\n key: \"separator3\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Item Styling\",\n },\n getColorField({\n key: \"priceColor\",\n label: \"Price Color\",\n description: \"Color for prices\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getFontSizeField({\n key: \"priceSize\",\n label: \"Price Font Size\",\n description: \"Size of prices\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getColorField({\n key: \"originalPriceColor\",\n label: \"Original Price Color\",\n description: \"Color for crossed-out original prices\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n {\n key: \"separator4\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Item Styling\",\n },\n getColorField({\n key: \"metaTextColor\",\n label: \"Meta Text Color\",\n description: \"Color for QV/CV text\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getFontSizeField({\n key: \"metaTextSize\",\n label: \"Meta Text Font Size\",\n description: \"Size of QV/CV text\",\n defaultValue: \"xs\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n\n // Styling tab - Featured Section group\n {\n key: \"showFeaturedSection\",\n label: \"Show Featured Section\",\n type: \"boolean\",\n description: \"Display a featured content section\",\n defaultValue: false,\n tab: \"styling\",\n group: \"Featured Section\",\n },\n {\n key: \"featuredAsset\",\n label: \"Featured Asset\",\n type: \"resource\",\n description:\n \"Select a single image or video resource for the featured section\",\n tab: \"styling\",\n group: \"Featured Section\",\n allowedTypes: [\"Medium\"],\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n {\n key: \"featuredTitle\",\n label: \"Featured Title\",\n type: \"text\",\n description: \"Title for featured section\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n {\n key: \"featuredSubtitle\",\n label: \"Featured Subtitle\",\n type: \"text\",\n description: \"Subtitle for featured section\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n {\n key: \"featuredButtonText\",\n label: \"Featured Button Text\",\n type: \"text\",\n description: \"Text for featured section button\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n {\n key: \"featuredButtonUrl\",\n label: \"Featured Button URL\",\n type: \"text\",\n description: \"URL for featured section button\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n getColorField({\n key: \"featuredSubtitleColor\",\n label: \"Featured Subtitle Color\",\n description: \"Color for featured subtitle\",\n defaultValue: \"background\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n }),\n getFontSizeField({\n key: \"featuredSubtitleSize\",\n label: \"Featured Subtitle Font Size\",\n description: \"Size of featured subtitle\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n }),\n\n // Data tab - Data Configuration\n {\n key: \"dataSource\",\n label: \"Data Source\",\n type: \"dataSource\",\n description: \"\",\n tab: \"data\",\n group: \"Data Configuration\",\n },\n ],\n itemConfigSchema: {\n description: \"Configure settings for this list item\",\n fields: [\n {\n key: \"title\",\n label: \"Custom Title\",\n type: \"text\",\n description: \"Override the item's title\",\n },\n {\n key: \"description\",\n label: \"Custom Description\",\n type: \"textarea\",\n description: \"Override the item's description\",\n rows: 3,\n },\n {\n key: \"price\",\n label: \"Price\",\n type: \"text\",\n description: \"Current price\",\n },\n {\n key: \"originalPrice\",\n label: \"Original Price\",\n type: \"text\",\n description: \"Original price (will be crossed out)\",\n },\n {\n key: \"discount\",\n label: \"Discount Badge\",\n type: \"text\",\n description: \"Discount text (e.g., '40% Off')\",\n },\n {\n key: \"qv\",\n label: \"QV Value\",\n type: \"text\",\n description: \"Qualifying Volume value\",\n },\n {\n key: \"cv\",\n label: \"CV Value\",\n type: \"text\",\n description: \"Commission Volume value\",\n },\n {\n key: \"image\",\n label: \"Image URL\",\n type: \"text\",\n description: \"Custom image URL for this item\",\n },\n ],\n },\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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F,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACP,kBAAkB,CAChB;IAAE,KAAK;IAAY,OAAO;IAAW,EACrC;IAAE,KAAK;IAAc,OAAO;IAAY,CACzC;GACF,CAAC;EAGF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,KAAK;GACL,OAAO;GACP,cAAc,CAAC,SAAS;GACxB,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EACF,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACR;EACF;CACD,kBAAkB;EAChB,aAAa;EACb,QAAQ;GACN;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,MAAM;IACP;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACF;EACF;CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ListWidget-IxrLNslF.cjs","names":["MediaRenderer","DOMPurify","getImageUrl","useWidgetInteraction","gapValues","useWidgetInteraction","gapValues","borderWidthClasses","borderColorClasses","ChevronLeft","ChevronRight","useLayoutEffect","useEffect","getFontSizeField","getColorField","getPaddingField","getBorderRadiusField","getBorderWidthField","getBorderColorField","getGapField"],"sources":["../../widgets/src/widgets/list-widget/helpers.ts","../../widgets/src/widgets/list-widget/FeaturedSection.tsx","../../widgets/src/widgets/list-widget/ListItemCard.tsx","../../widgets/src/widgets/list-widget/UnorderedList.tsx","../../widgets/src/widgets/list-widget/OrderedList.tsx","../../widgets/src/widgets/ListWidget.tsx"],"sourcesContent":["import type { BorderRadiusOptions } from \"@fluid-app/portal-core/types\";\n\n/**\n * Maps the schema's BorderRadiusOptions to a Tailwind `rounded-*` class.\n * `\"full\"` is converted to `rounded-2xl` because the list item cards and\n * featured section read better as large-radius rectangles than as\n * pill-shaped containers.\n */\nexport const cardRadiusClass = (radius: BorderRadiusOptions): string =>\n radius === \"full\" ? \"rounded-2xl\" : `rounded-${radius}`;\n","import type React from \"react\";\nimport type {\n BorderRadiusOptions,\n FontSizeOptions,\n ColorOptions,\n} from \"@fluid-app/portal-core/types\";\nimport { MediaRenderer } from \"../../components/MediaRenderer\";\nimport { cardRadiusClass } from \"./helpers\";\n\ninterface FeaturedSectionProps {\n borderRadius: BorderRadiusOptions;\n titleSize: FontSizeOptions;\n featuredTitle?: string | undefined;\n featuredSubtitle?: string | undefined;\n featuredButtonText?: string | undefined;\n featuredButtonUrl?: string | undefined;\n featuredSubtitleColor: ColorOptions;\n featuredSubtitleSize: FontSizeOptions;\n asset: { url: string; isVideo: boolean };\n}\n\nexport function FeaturedSection({\n borderRadius,\n titleSize,\n featuredTitle,\n featuredSubtitle,\n featuredButtonText,\n featuredButtonUrl,\n featuredSubtitleColor,\n featuredSubtitleSize,\n asset,\n}: FeaturedSectionProps): React.JSX.Element {\n return (\n <div\n className={`group relative h-full min-h-[320px] w-full overflow-hidden ${cardRadiusClass(borderRadius)}`}\n >\n <div className=\"absolute inset-0 h-full w-full\">\n <MediaRenderer\n src={asset.url}\n mediaType={asset.isVideo ? \"video\" : \"image\"}\n alt={featuredTitle || \"Featured\"}\n objectFit=\"cover\"\n autoplay={asset.isVideo}\n loop={asset.isVideo}\n muted={asset.isVideo}\n controls={false}\n />\n </div>\n\n <div\n className=\"pointer-events-none absolute inset-0\"\n style={{\n background:\n \"linear-gradient(180deg, rgba(0,0,0,0) 45%, rgba(0,0,0,0.55) 100%)\",\n }}\n />\n\n <div className=\"absolute inset-0 flex flex-col items-start justify-end p-10\">\n {featuredTitle && (\n <h3\n className={`font-header mb-3 font-medium tracking-tight text-white text-${titleSize === \"md\" ? \"base\" : titleSize}`}\n >\n {featuredTitle}\n </h3>\n )}\n {featuredSubtitle && (\n <p\n className={`mb-6 max-w-md text-${featuredSubtitleColor}/80 text-${featuredSubtitleSize === \"md\" ? \"base\" : featuredSubtitleSize} leading-relaxed`}\n >\n {featuredSubtitle}\n </p>\n )}\n {featuredButtonText && (\n <a\n href={featuredButtonUrl || \"#\"}\n className={`text-foreground inline-flex items-center gap-2 bg-white px-6 py-3 text-sm font-medium tracking-tight transition-opacity hover:opacity-90 ${cardRadiusClass(borderRadius)}`}\n >\n {featuredButtonText}\n <span\n aria-hidden=\"true\"\n className=\"transition-transform group-hover:translate-x-0.5\"\n >\n →\n </span>\n </a>\n )}\n </div>\n </div>\n );\n}\n","import type React from \"react\";\nimport DOMPurify from \"dompurify\";\nimport type {\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\n\ntype ListItem = {\n id: string;\n image?: string;\n imageUrl?: string;\n videoUrl?: string;\n title?: string;\n description?: string;\n price?: string;\n display_price?: string;\n originalPrice?: string;\n discount?: string;\n qv?: string;\n cv?: string;\n [key: string]: unknown;\n};\n\nfunction getStringValue(value: unknown): string {\n if (!value) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\") return String(value);\n if (typeof value === \"object\" && value !== null) {\n if (\"body\" in value) {\n const body = (value as { body: unknown }).body;\n if (typeof body === \"string\") return body;\n }\n if (\"text\" in value) {\n const text = (value as { text: unknown }).text;\n if (typeof text === \"string\") return text;\n }\n if (\"value\" in value) {\n const val = (value as { value: unknown }).value;\n if (typeof val === \"string\") return val;\n if (typeof val === \"number\") return String(val);\n }\n }\n return \"\";\n}\n\nfunction stripParentheticalText(text: string): string {\n return text.replace(/\\s*\\([^)]*\\)/g, \"\").trim();\n}\n\nfunction formatPrice(value: unknown): string {\n const str = getStringValue(value);\n if (!str) return \"\";\n return stripParentheticalText(str);\n}\n\nexport interface ListItemCardContentProps {\n item: ListItem;\n padding: PaddingOptions;\n itemTitleColor: ColorOptions;\n itemTitleSize: FontSizeOptions;\n descriptionColor: ColorOptions;\n descriptionSize: FontSizeOptions;\n priceColor: ColorOptions;\n priceSize: FontSizeOptions;\n originalPriceColor: ColorOptions;\n metaTextColor: ColorOptions;\n metaTextSize: FontSizeOptions;\n showMetaText: boolean;\n}\n\nexport function ListItemCardContent({\n item,\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n showMetaText,\n}: ListItemCardContentProps): React.JSX.Element {\n const hasPrice = Boolean(item.display_price || item.price);\n const hasOriginal = Boolean(item.originalPrice);\n\n return (\n <div className={`flex flex-1 flex-col p-${padding}`}>\n {item.title && (\n <h3\n className={`text-${itemTitleColor} text-${itemTitleSize === \"md\" ? \"base\" : itemTitleSize} font-header mb-1.5 leading-snug font-medium tracking-tight`}\n >\n {getStringValue(item.title)}\n </h3>\n )}\n {item.description && (\n <div\n className={`text-${descriptionColor}/60 text-${descriptionSize === \"md\" ? \"base\" : descriptionSize} mb-3 line-clamp-2 overflow-hidden leading-relaxed`}\n dangerouslySetInnerHTML={{\n __html: DOMPurify.sanitize(item.description ?? \"\", {\n ALLOWED_TAGS: [\n \"br\",\n \"strong\",\n \"em\",\n \"b\",\n \"i\",\n \"ul\",\n \"ol\",\n \"li\",\n \"p\",\n ],\n ALLOWED_ATTR: [],\n }),\n }}\n />\n )}\n {(hasPrice || hasOriginal) && (\n <div className=\"mt-auto flex items-baseline gap-2\">\n {hasPrice && (\n <span\n className={`text-${priceColor} text-${priceSize === \"md\" ? \"base\" : priceSize} font-semibold tracking-tight tabular-nums`}\n >\n {formatPrice(item.display_price || item.price)}\n </span>\n )}\n {hasOriginal && (\n <span\n className={`text-${originalPriceColor}/50 text-${descriptionSize === \"md\" ? \"base\" : descriptionSize} tabular-nums line-through`}\n >\n {formatPrice(item.originalPrice)}\n </span>\n )}\n </div>\n )}\n {showMetaText && (item.qv || item.cv) && (\n <div\n className={`mt-2 flex gap-4 text-${metaTextColor}/70 text-${metaTextSize === \"md\" ? \"base\" : metaTextSize} font-medium tracking-wide uppercase`}\n >\n {item.qv && (\n <span>\n <span className=\"opacity-50\">QV</span>{\" \"}\n <span className=\"tabular-nums\">{getStringValue(item.qv)}</span>\n </span>\n )}\n {item.cv && (\n <span>\n <span className=\"opacity-50\">CV</span>{\" \"}\n <span className=\"tabular-nums\">{getStringValue(item.cv)}</span>\n </span>\n )}\n </div>\n )}\n </div>\n );\n}\n\nexport function DiscountBadge({\n discount,\n}: {\n discount: string;\n}): React.JSX.Element {\n return (\n <div className=\"bg-background text-foreground absolute top-3 left-3 z-10 rounded-full px-2.5 py-1 text-[10px] font-semibold tracking-[0.08em] uppercase\">\n {getStringValue(discount)}\n </div>\n );\n}\n\nexport { getStringValue };\nexport type { ListItem };\n","import type React from \"react\";\nimport type {\n ColorOptions,\n FontSizeOptions,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n} from \"@fluid-app/portal-core/types\";\nimport { gapValues } from \"../../core/fields\";\nimport { useWidgetInteraction } from \"../../contexts/WidgetInteractionContext\";\nimport {\n ListItemCardContent,\n DiscountBadge,\n type ListItem,\n} from \"./ListItemCard\";\nimport { cardRadiusClass } from \"./helpers\";\n\ntype ImageAspectRatio = \"square\" | \"landscape\" | \"portrait\";\n\nconst getImageUrl = (item: ListItem): string | undefined => {\n return item.imageUrl || item.image;\n};\n\nconst getAspectRatioClass = (ratio: ImageAspectRatio): string => {\n const ratios = {\n square: \"aspect-square\",\n landscape: \"aspect-video\",\n portrait: \"aspect-[3/4]\",\n };\n return ratios[ratio];\n};\n\nconst getResponsiveGridClass = (columns: number): string => {\n const responsiveClasses: Record<number, string> = {\n 1: \"grid-cols-2 @lg:grid-cols-1\",\n 2: \"grid-cols-2 @lg:grid-cols-2\",\n 3: \"grid-cols-2 @lg:grid-cols-3\",\n 4: \"grid-cols-2 @lg:grid-cols-4\",\n 5: \"grid-cols-2 @lg:grid-cols-5\",\n 6: \"grid-cols-2 @lg:grid-cols-6\",\n };\n return responsiveClasses[columns] || \"grid-cols-2 @lg:grid-cols-3\";\n};\n\nexport interface UnorderedListProps {\n items: ListItem[];\n columns: number;\n gap: GapOptions;\n borderRadius: BorderRadiusOptions;\n imageAspectRatio: ImageAspectRatio;\n showBadge: boolean;\n padding: PaddingOptions;\n itemTitleColor: ColorOptions;\n itemTitleSize: FontSizeOptions;\n descriptionColor: ColorOptions;\n descriptionSize: FontSizeOptions;\n priceColor: ColorOptions;\n priceSize: FontSizeOptions;\n originalPriceColor: ColorOptions;\n metaTextColor: ColorOptions;\n metaTextSize: FontSizeOptions;\n showMetaText: boolean;\n}\n\nexport function UnorderedList({\n items,\n columns,\n gap,\n borderRadius,\n imageAspectRatio,\n showBadge,\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n showMetaText,\n}: UnorderedListProps): React.JSX.Element {\n const gridClass = getResponsiveGridClass(columns);\n const { onItemClick } = useWidgetInteraction();\n\n return (\n <div className={`grid ${gridClass} gap-${gapValues[gap]}`}>\n {items.map((item) => {\n const imageUrl = getImageUrl(item);\n const aspectRatioClass = getAspectRatioClass(imageAspectRatio);\n\n return (\n <div\n key={item.id}\n className={`group relative flex flex-col ${onItemClick ? \"cursor-pointer\" : \"\"}`}\n {...(onItemClick\n ? {\n role: \"button\",\n tabIndex: 0,\n onClick: () => onItemClick(item),\n onKeyDown: (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onItemClick(item);\n }\n },\n }\n : {})}\n >\n {imageUrl && (\n <div\n className={`relative w-full ${aspectRatioClass} bg-muted/30 overflow-hidden ${cardRadiusClass(borderRadius)}`}\n >\n {showBadge && item.discount && (\n <DiscountBadge discount={item.discount} />\n )}\n <img\n src={imageUrl}\n alt={item.title || \"Product\"}\n className=\"h-full w-full object-cover transition-opacity duration-300 group-hover:opacity-90\"\n />\n </div>\n )}\n <div className=\"mt-4\">\n <ListItemCardContent\n item={item}\n padding={padding}\n itemTitleColor={itemTitleColor}\n itemTitleSize={itemTitleSize}\n descriptionColor={descriptionColor}\n descriptionSize={descriptionSize}\n priceColor={priceColor}\n priceSize={priceSize}\n originalPriceColor={originalPriceColor}\n metaTextColor={metaTextColor}\n metaTextSize={metaTextSize}\n showMetaText={showMetaText}\n />\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n","import { useRef } from \"react\";\nimport type React from \"react\";\nimport type { RefObject } from \"react\";\nimport type {\n ColorOptions,\n FontSizeOptions,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n} from \"@fluid-app/portal-core/types\";\nimport { gapValues } from \"../../core/fields\";\nimport { useWidgetInteraction } from \"../../contexts/WidgetInteractionContext\";\nimport {\n ListItemCardContent,\n DiscountBadge,\n getStringValue,\n type ListItem,\n} from \"./ListItemCard\";\nimport { cardRadiusClass } from \"./helpers\";\n\ntype ImageAspectRatio = \"square\" | \"landscape\" | \"portrait\";\ntype ScrollAxis = \"horizontal\" | \"vertical\";\n\nconst getImageUrl = (item: ListItem): string | undefined => {\n return item.imageUrl || item.image;\n};\n\nexport interface OrderedListProps {\n items: ListItem[];\n scrollAxis: ScrollAxis;\n gap: GapOptions;\n borderRadius: BorderRadiusOptions;\n imageAspectRatio: ImageAspectRatio;\n showBadge: boolean;\n padding: PaddingOptions;\n itemTitleColor: ColorOptions;\n itemTitleSize: FontSizeOptions;\n descriptionColor: ColorOptions;\n descriptionSize: FontSizeOptions;\n priceColor: ColorOptions;\n priceSize: FontSizeOptions;\n originalPriceColor: ColorOptions;\n metaTextColor: ColorOptions;\n metaTextSize: FontSizeOptions;\n numberColor: ColorOptions;\n numberSize: FontSizeOptions;\n showMetaText: boolean;\n /** Optional ref from the parent to control horizontal scroll externally */\n scrollContainerRef?: RefObject<HTMLDivElement | null> | undefined;\n}\n\nconst formatRank = (n: number): string => (n < 10 ? `0${n}` : String(n));\n\nexport function OrderedList({\n items,\n scrollAxis,\n gap,\n borderRadius,\n // imageAspectRatio is intentionally not consumed — ordered layouts use a\n // fixed aspect ratio per axis (3:4 poster horizontal, 16x16 thumb vertical)\n // by design. The schema gates this field to unordered lists.\n imageAspectRatio: _imageAspectRatio,\n showBadge,\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n numberColor,\n numberSize,\n showMetaText,\n scrollContainerRef: externalScrollRef,\n}: OrderedListProps): React.JSX.Element | null {\n const internalScrollRef = useRef<HTMLDivElement>(null);\n const scrollContainerRef = externalScrollRef ?? internalScrollRef;\n\n const { onItemClick } = useWidgetInteraction();\n\n const interactionProps = (item: ListItem) =>\n onItemClick\n ? ({\n role: \"button\" as const,\n tabIndex: 0,\n onClick: () => onItemClick(item),\n onKeyDown: (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onItemClick(item);\n }\n },\n } as const)\n : {};\n\n const contentProps = {\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n showMetaText,\n };\n\n if (scrollAxis === \"horizontal\") {\n return (\n <div className=\"relative\">\n <div\n ref={scrollContainerRef}\n className={`flex gap-${gapValues[gap]} scrollbar-hide overflow-x-auto overflow-y-hidden scroll-smooth`}\n >\n {items.map((item, index) => {\n const imageUrl = getImageUrl(item);\n const rank = index + 1;\n\n return (\n <div\n key={item.id}\n className={`group relative flex flex-shrink-0 flex-col ${onItemClick ? \"cursor-pointer\" : \"\"}`}\n {...interactionProps(item)}\n >\n <div className=\"flex items-end\">\n <span className=\"sr-only\">Rank {rank}: </span>\n <span\n className={`text-${numberColor} pointer-events-none flex-shrink-0 font-black italic tabular-nums select-none`}\n style={{\n fontSize: \"11rem\",\n lineHeight: \"0.82\",\n letterSpacing: \"-0.06em\",\n }}\n aria-hidden=\"true\"\n >\n {rank}\n </span>\n {imageUrl && (\n <div\n className={`relative aspect-[3/4] w-[200px] flex-shrink-0 ${cardRadiusClass(borderRadius)} bg-muted/30 overflow-hidden`}\n >\n {showBadge && item.discount && (\n <DiscountBadge discount={item.discount} />\n )}\n <img\n src={imageUrl}\n alt={item.title || \"Product\"}\n className=\"h-full w-full object-cover\"\n />\n </div>\n )}\n </div>\n <div className=\"mt-5 ml-auto w-[200px]\">\n <ListItemCardContent item={item} {...contentProps} />\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n }\n\n if (scrollAxis === \"vertical\") {\n return (\n <div className={`flex flex-col gap-${gapValues[gap]}`}>\n {items.map((item, index) => {\n const imageUrl = getImageUrl(item);\n const rank = index + 1;\n const isLast = index === items.length - 1;\n\n return (\n <div\n key={item.id}\n className={`group relative flex items-center gap-${gapValues[gap]} py-6 ${!isLast ? \"border-b\" : \"\"} ${onItemClick ? \"cursor-pointer\" : \"\"}`}\n style={\n !isLast\n ? {\n borderColor:\n \"color-mix(in oklch, var(--color-foreground) 8%, transparent)\",\n }\n : undefined\n }\n {...interactionProps(item)}\n >\n <span className=\"sr-only\">Rank {rank}: </span>\n <span\n className={`text-${numberColor} font-header w-14 flex-shrink-0 text-${numberSize === \"md\" ? \"base\" : numberSize} leading-none font-light tracking-tight italic`}\n aria-hidden=\"true\"\n >\n {formatRank(rank)}\n </span>\n {imageUrl && (\n <div\n className={`relative h-16 w-16 flex-shrink-0 overflow-hidden ${cardRadiusClass(borderRadius)} bg-muted/30`}\n >\n <img\n src={imageUrl}\n alt={item.title || \"Product\"}\n className=\"h-full w-full object-cover\"\n />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <ListItemCardContent item={item} {...contentProps} />\n </div>\n {showBadge && item.discount && (\n <span className=\"text-muted ml-2 flex-shrink-0 text-[10px] font-semibold tracking-[0.08em] uppercase\">\n {getStringValue(item.discount)}\n </span>\n )}\n </div>\n );\n })}\n </div>\n );\n }\n\n return null;\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ComponentProps,\n} from \"react\";\nimport type React from \"react\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport type {\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n BorderRadiusOptions,\n PaddingOptions,\n GapOptions,\n BackgroundValue,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n borderWidthClasses,\n borderColorClasses,\n getColorField,\n getFontSizeField,\n getPaddingField,\n getGapField,\n} from \"../core/fields\";\nimport { FeaturedSection } from \"./list-widget/FeaturedSection\";\nimport { UnorderedList } from \"./list-widget/UnorderedList\";\nimport { OrderedList } from \"./list-widget/OrderedList\";\n\nconst DEFAULT_ITEMS: ListItem[] = [];\n\ntype ListItem = {\n id: string;\n image?: string;\n imageUrl?: string;\n videoUrl?: string;\n title?: string;\n description?: string;\n price?: string;\n originalPrice?: string;\n discount?: string;\n qv?: string;\n cv?: string;\n [key: string]: unknown;\n};\n\ntype ImageAspectRatio = \"square\" | \"landscape\" | \"portrait\";\ntype ListType = \"ordered\" | \"unordered\";\ntype ScrollAxis = \"horizontal\" | \"vertical\";\n\ntype ListWidgetProps = ComponentProps<\"div\"> & {\n // List configuration\n listType?: ListType;\n scrollAxis?: ScrollAxis;\n titleEnabled?: boolean;\n title?: string;\n items?: ListItem[];\n\n // Text styling\n titleColor?: ColorOptions;\n titleSize?: FontSizeOptions;\n itemTitleColor?: ColorOptions;\n itemTitleSize?: FontSizeOptions;\n descriptionColor?: ColorOptions;\n descriptionSize?: FontSizeOptions;\n priceColor?: ColorOptions;\n priceSize?: FontSizeOptions;\n originalPriceColor?: ColorOptions;\n metaTextColor?: ColorOptions;\n metaTextSize?: FontSizeOptions;\n\n // Ordered list styling\n numberColor?: ColorOptions;\n numberSize?: FontSizeOptions;\n\n // Layout\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n padding?: PaddingOptions;\n gap?: GapOptions;\n columns?: number;\n imageAspectRatio?: ImageAspectRatio;\n background?: BackgroundValue;\n\n // Behavior\n showBadge?: boolean;\n showMetaText?: boolean;\n maxItems?: number;\n\n // Featured section\n showFeaturedSection?: boolean;\n featuredAsset?: string | { [key: string]: unknown };\n featuredTitle?: string;\n featuredSubtitle?: string;\n featuredButtonText?: string;\n featuredButtonUrl?: string;\n featuredSubtitleColor?: ColorOptions;\n featuredSubtitleSize?: FontSizeOptions;\n};\n\nfunction getFeaturedAssetUrl(\n value: string | { [key: string]: unknown } | undefined,\n): { url: string; isVideo: boolean } | undefined {\n if (!value) return undefined;\n\n // Handle string URLs\n if (typeof value === \"string\") {\n const isVideo = /\\.(mp4|webm|ogg|mov)$/i.test(value);\n return { url: value, isVideo };\n }\n\n // Handle ShareableItem objects\n if (typeof value === \"object\") {\n // Check for video URL first\n const videoUrl =\n (value.videoUrl as string) ||\n (value.video_url as string) ||\n (value.url as string);\n if (videoUrl && /\\.(mp4|webm|ogg|mov)$/i.test(videoUrl)) {\n return { url: videoUrl, isVideo: true };\n }\n\n // Fall back to image URL\n const imageUrl =\n (value.imageUrl as string) ||\n (value.image_url as string) ||\n (value.url as string);\n if (imageUrl) {\n return { url: imageUrl, isVideo: false };\n }\n }\n\n return undefined;\n}\n\nexport function ListWidget({\n listType = \"unordered\",\n scrollAxis = \"horizontal\",\n titleEnabled = true,\n title,\n items = DEFAULT_ITEMS,\n titleColor = \"foreground\",\n titleSize = \"lg\",\n itemTitleColor = \"foreground\",\n itemTitleSize = \"sm\",\n descriptionColor = \"foreground\",\n descriptionSize = \"sm\",\n priceColor = \"foreground\",\n priceSize = \"md\",\n originalPriceColor = \"muted\",\n metaTextColor = \"muted\",\n metaTextSize = \"xs\",\n numberColor = \"primary\",\n numberSize = \"2xl\",\n borderRadius = \"md\",\n borderWidth = \"none\",\n borderColor = \"muted\",\n padding = 4,\n gap = \"md\",\n columns = 3,\n imageAspectRatio = \"square\",\n background = { type: \"solid\", color: \"background\" },\n showBadge = true,\n showMetaText = true,\n maxItems = 12,\n showFeaturedSection = false,\n featuredAsset,\n featuredTitle,\n featuredSubtitle,\n featuredButtonText,\n featuredButtonUrl,\n featuredSubtitleColor = \"background\",\n featuredSubtitleSize = \"md\",\n className,\n ...props\n}: ListWidgetProps): React.JSX.Element {\n const displayItems = maxItems ? items.slice(0, maxItems) : items;\n const hasItems = displayItems.length > 0;\n\n const itemStyleProps = {\n padding,\n itemTitleColor,\n itemTitleSize,\n descriptionColor,\n descriptionSize,\n priceColor,\n priceSize,\n originalPriceColor,\n metaTextColor,\n metaTextSize,\n showMetaText,\n showBadge,\n borderRadius,\n imageAspectRatio,\n gap,\n };\n\n const hasFeaturedAsset = getFeaturedAssetUrl(featuredAsset);\n const shouldShowFeaturedSection = showFeaturedSection && hasFeaturedAsset;\n\n // Background styling\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n\n const isOrderedHorizontal =\n listType === \"ordered\" && scrollAxis === \"horizontal\";\n const showScrollArrows =\n isOrderedHorizontal && hasItems && displayItems.length > 1;\n\n const { scrollContainerRef, canScrollPrev, canScrollNext, scrollByAmount } =\n useHorizontalScrollState({\n enabled: showScrollArrows,\n contentLength: displayItems.length,\n });\n\n const hasTitle = titleEnabled && title;\n const showTitleRow = hasTitle || showScrollArrows;\n\n return (\n <div\n className={`@container bg-${backgroundColor} p-${padding} rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${className}`}\n style={{ backgroundImage }}\n {...props}\n >\n {showTitleRow && (\n <div className=\"mb-8 flex items-center justify-between gap-4\">\n {hasTitle ? (\n <h2\n className={`text-${titleColor} text-${titleSize === \"md\" ? \"base\" : titleSize} font-header leading-tight font-medium tracking-tight`}\n >\n {title}\n </h2>\n ) : (\n <span />\n )}\n {showScrollArrows && (\n <div className=\"flex shrink-0 items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => scrollByAmount(\"prev\")}\n disabled={!canScrollPrev}\n aria-label=\"Previous items\"\n className={`text-${numberColor} flex size-9 items-center justify-center rounded-full transition-opacity hover:opacity-60 disabled:cursor-not-allowed disabled:opacity-20`}\n style={{\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${numberColor}) 25%, transparent)`,\n }}\n >\n <ChevronLeft aria-hidden=\"true\" className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => scrollByAmount(\"next\")}\n disabled={!canScrollNext}\n aria-label=\"Next items\"\n className={`text-${numberColor} flex size-9 items-center justify-center rounded-full transition-opacity hover:opacity-60 disabled:cursor-not-allowed disabled:opacity-20`}\n style={{\n boxShadow: `inset 0 0 0 1px color-mix(in oklch, var(--color-${numberColor}) 25%, transparent)`,\n }}\n >\n <ChevronRight aria-hidden=\"true\" className=\"size-4\" />\n </button>\n </div>\n )}\n </div>\n )}\n {!hasItems ? (\n <div className=\"flex items-center justify-center py-20 text-center\">\n <p className=\"text-muted/70 text-sm tracking-tight\">\n No items to display\n </p>\n </div>\n ) : shouldShowFeaturedSection ? (\n <div className=\"flex flex-col gap-4 @lg:flex-row @lg:gap-6\">\n <div className=\"w-full @lg:w-[45%]\">\n <FeaturedSection\n borderRadius={borderRadius}\n titleSize={titleSize}\n featuredTitle={featuredTitle}\n featuredSubtitle={featuredSubtitle}\n featuredButtonText={featuredButtonText}\n featuredButtonUrl={featuredButtonUrl}\n featuredSubtitleColor={featuredSubtitleColor}\n featuredSubtitleSize={featuredSubtitleSize}\n asset={hasFeaturedAsset!}\n />\n </div>\n <div className=\"w-full @lg:w-[55%]\">\n {listType === \"unordered\" ? (\n <UnorderedList\n items={displayItems}\n columns={columns}\n {...itemStyleProps}\n />\n ) : (\n <OrderedList\n items={displayItems}\n scrollAxis={scrollAxis}\n numberColor={numberColor}\n numberSize={numberSize}\n scrollContainerRef={\n isOrderedHorizontal ? scrollContainerRef : undefined\n }\n {...itemStyleProps}\n />\n )}\n </div>\n </div>\n ) : listType === \"unordered\" ? (\n <UnorderedList\n items={displayItems}\n columns={columns}\n {...itemStyleProps}\n />\n ) : (\n <OrderedList\n items={displayItems}\n scrollAxis={scrollAxis}\n numberColor={numberColor}\n numberSize={numberSize}\n scrollContainerRef={\n isOrderedHorizontal ? scrollContainerRef : undefined\n }\n {...itemStyleProps}\n />\n )}\n </div>\n );\n}\n\n// ----- Hooks -----\n\nconst SCROLL_BUTTON_AMOUNT = 300;\nconst SCROLL_EDGE_TOLERANCE = 4;\nconst useIsomorphicLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\ntype HorizontalScrollState = {\n scrollContainerRef: React.RefObject<HTMLDivElement | null>;\n canScrollPrev: boolean;\n canScrollNext: boolean;\n scrollByAmount: (direction: \"prev\" | \"next\") => void;\n};\n\n// Tracks horizontal scroll position of an external container so the parent\n// can drive prev/next arrow disabled states from a single source of truth.\n// Both edges initialize to `false` and are populated by the post-mount\n// measurement, avoiding a brief enabled-then-disabled flicker when content\n// fits entirely within the viewport.\nfunction useHorizontalScrollState({\n enabled,\n contentLength,\n}: {\n enabled: boolean;\n contentLength: number;\n}): HorizontalScrollState {\n const scrollContainerRef = useRef<HTMLDivElement>(null);\n const [canScrollPrev, setCanScrollPrev] = useState(false);\n const [canScrollNext, setCanScrollNext] = useState(false);\n\n const resetScrollState = useCallback(() => {\n setCanScrollPrev(false);\n setCanScrollNext(false);\n }, []);\n\n const updateScrollState = useCallback(() => {\n const el = scrollContainerRef.current;\n if (!enabled || !el) {\n resetScrollState();\n return;\n }\n setCanScrollPrev(el.scrollLeft > SCROLL_EDGE_TOLERANCE);\n setCanScrollNext(\n el.scrollLeft + el.clientWidth < el.scrollWidth - SCROLL_EDGE_TOLERANCE,\n );\n }, [enabled, resetScrollState]);\n\n // Fires synchronously before paint so the arrows reflect post-layout\n // geometry without a flicker. `updateScrollState` is memoized on\n // `enabled`, so this only fires on mount and when toggling enablement —\n // not on every render.\n useIsomorphicLayoutEffect(() => {\n updateScrollState();\n }, [updateScrollState]);\n\n useEffect(() => {\n if (!enabled) {\n resetScrollState();\n return undefined;\n }\n\n const el = scrollContainerRef.current;\n if (!el) {\n resetScrollState();\n return undefined;\n }\n\n updateScrollState();\n el.addEventListener(\"scroll\", updateScrollState, { passive: true });\n const resize =\n typeof ResizeObserver !== \"undefined\"\n ? new ResizeObserver(updateScrollState)\n : undefined;\n resize?.observe(el);\n\n return () => {\n el.removeEventListener(\"scroll\", updateScrollState);\n resize?.disconnect();\n };\n }, [enabled, resetScrollState, updateScrollState, contentLength]);\n\n const scrollByAmount = useCallback((direction: \"prev\" | \"next\") => {\n const el = scrollContainerRef.current;\n if (!el) return;\n const computedGap = parseFloat(getComputedStyle(el).gap) || 0;\n const amount = SCROLL_BUTTON_AMOUNT + computedGap;\n el.scrollTo({\n left: el.scrollLeft + (direction === \"next\" ? amount : -amount),\n behavior: \"smooth\",\n });\n }, []);\n\n return { scrollContainerRef, canScrollPrev, canScrollNext, scrollByAmount };\n}\n\nexport const listWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"ListWidget\",\n displayName: \"List\",\n tabsConfig: [\n { id: \"styling\", label: \"Styling\" },\n { id: \"data\", label: \"Data\" },\n ],\n dataSourceTargetProps: [\"items\"],\n fields: [\n // Styling tab - Title group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the list\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"title\",\n label: \"Title\",\n type: \"text\",\n description: \"Header text for the list\",\n defaultValue: \"List\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleSize\",\n label: \"Title Font Size\",\n description: \"Size of the list title\",\n defaultValue: \"xl\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color for the list title\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n\n // Styling tab - Design group\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding inside the container\",\n defaultValue: 4,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Rounded corners for the container and images\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Border width for the widget\",\n defaultValue: \"none\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderColorField({\n key: \"borderColor\",\n label: \"Border Color\",\n description: \"Border color for the widget\",\n defaultValue: \"muted\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getGapField({\n key: \"gap\",\n label: \"Gap\",\n description: \"Spacing between items\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n type: \"background\",\n defaultValue: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background color or image for the widget\",\n tab: \"styling\",\n group: \"Design\",\n },\n\n // Styling tab - List Configuration group\n {\n key: \"listType\",\n label: \"List Type\",\n type: \"select\",\n description: \"Type of list layout\",\n defaultValue: \"unordered\",\n options: [\n { label: \"Unordered (Grid)\", value: \"unordered\" },\n { label: \"Ordered (Numbered)\", value: \"ordered\" },\n ],\n tab: \"styling\",\n group: \"List Configuration\",\n },\n {\n key: \"maxItems\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of items to display\",\n min: 1,\n max: 100,\n step: 1,\n defaultValue: 12,\n tab: \"styling\",\n group: \"List Configuration\",\n },\n {\n key: \"imageAspectRatio\",\n label: \"Image Aspect Ratio\",\n type: \"buttonGroup\",\n description:\n \"Aspect ratio for item images (unordered list — ordered layouts use a fixed ratio per axis)\",\n defaultValue: \"square\",\n options: [\n { label: \"Square\", value: \"square\" },\n { label: \"Landscape\", value: \"landscape\" },\n { label: \"Portrait\", value: \"portrait\" },\n ],\n tab: \"styling\",\n group: \"List Configuration\",\n requiresKeyValue: { key: \"listType\", value: \"unordered\" },\n },\n {\n key: \"showBadge\",\n label: \"Show Discount Badge\",\n type: \"boolean\",\n description: \"Display discount badge on images\",\n defaultValue: true,\n tab: \"styling\",\n group: \"List Configuration\",\n },\n {\n key: \"showMetaText\",\n label: \"Show QV/CV Text\",\n type: \"boolean\",\n description: \"Display QV and CV values\",\n defaultValue: true,\n tab: \"styling\",\n group: \"List Configuration\",\n },\n\n // Styling tab - Unordered List Configuration\n {\n key: \"columns\",\n label: \"Grid Columns\",\n type: \"number\",\n description: \"Number of columns in the grid (unordered list only)\",\n min: 1,\n max: 6,\n step: 1,\n defaultValue: 3,\n tab: \"styling\",\n group: \"Unordered List Configuration\",\n requiresKeyValue: { key: \"listType\", value: \"unordered\" },\n },\n\n // Styling tab - Ordered List Configuration\n {\n key: \"scrollAxis\",\n label: \"Scroll Direction\",\n type: \"select\",\n description: \"Direction for ordered list scrolling\",\n defaultValue: \"horizontal\",\n options: [\n { label: \"Horizontal\", value: \"horizontal\" },\n { label: \"Vertical\", value: \"vertical\" },\n ],\n tab: \"styling\",\n group: \"Ordered List Configuration\",\n requiresKeyValue: { key: \"listType\", value: \"ordered\" },\n },\n getColorField({\n key: \"numberColor\",\n label: \"Number Color\",\n description: \"Color for ordered list numbers\",\n defaultValue: \"primary\",\n tab: \"styling\",\n group: \"Ordered List Configuration\",\n requiresKeyValue: { key: \"listType\", value: \"ordered\" },\n }),\n getFontSizeField({\n key: \"numberSize\",\n label: \"Number Font Size\",\n description:\n \"Size of ordered list numbers (vertical scroll only — horizontal uses a fixed editorial size)\",\n defaultValue: \"2xl\",\n tab: \"styling\",\n group: \"Ordered List Configuration\",\n requiresKeyValue: [\n { key: \"listType\", value: \"ordered\" },\n { key: \"scrollAxis\", value: \"vertical\" },\n ],\n }),\n\n // Styling tab - Item Styling group\n getColorField({\n key: \"itemTitleColor\",\n label: \"Item Title Color\",\n description: \"Color for item titles\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getFontSizeField({\n key: \"itemTitleSize\",\n label: \"Item Title Font Size\",\n description: \"Size of item titles\",\n defaultValue: \"sm\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n {\n key: \"separator2\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Item Styling\",\n },\n getColorField({\n key: \"descriptionColor\",\n label: \"Description Color\",\n description: \"Color for descriptions\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getFontSizeField({\n key: \"descriptionSize\",\n label: \"Description Font Size\",\n description: \"Size of descriptions\",\n defaultValue: \"sm\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n {\n key: \"separator3\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Item Styling\",\n },\n getColorField({\n key: \"priceColor\",\n label: \"Price Color\",\n description: \"Color for prices\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getFontSizeField({\n key: \"priceSize\",\n label: \"Price Font Size\",\n description: \"Size of prices\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getColorField({\n key: \"originalPriceColor\",\n label: \"Original Price Color\",\n description: \"Color for crossed-out original prices\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n {\n key: \"separator4\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Item Styling\",\n },\n getColorField({\n key: \"metaTextColor\",\n label: \"Meta Text Color\",\n description: \"Color for QV/CV text\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n getFontSizeField({\n key: \"metaTextSize\",\n label: \"Meta Text Font Size\",\n description: \"Size of QV/CV text\",\n defaultValue: \"xs\",\n tab: \"styling\",\n group: \"Item Styling\",\n }),\n\n // Styling tab - Featured Section group\n {\n key: \"showFeaturedSection\",\n label: \"Show Featured Section\",\n type: \"boolean\",\n description: \"Display a featured content section\",\n defaultValue: false,\n tab: \"styling\",\n group: \"Featured Section\",\n },\n {\n key: \"featuredAsset\",\n label: \"Featured Asset\",\n type: \"resource\",\n description:\n \"Select a single image or video resource for the featured section\",\n tab: \"styling\",\n group: \"Featured Section\",\n allowedTypes: [\"Medium\"],\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n {\n key: \"featuredTitle\",\n label: \"Featured Title\",\n type: \"text\",\n description: \"Title for featured section\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n {\n key: \"featuredSubtitle\",\n label: \"Featured Subtitle\",\n type: \"text\",\n description: \"Subtitle for featured section\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n {\n key: \"featuredButtonText\",\n label: \"Featured Button Text\",\n type: \"text\",\n description: \"Text for featured section button\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n {\n key: \"featuredButtonUrl\",\n label: \"Featured Button URL\",\n type: \"text\",\n description: \"URL for featured section button\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n },\n getColorField({\n key: \"featuredSubtitleColor\",\n label: \"Featured Subtitle Color\",\n description: \"Color for featured subtitle\",\n defaultValue: \"background\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n }),\n getFontSizeField({\n key: \"featuredSubtitleSize\",\n label: \"Featured Subtitle Font Size\",\n description: \"Size of featured subtitle\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Featured Section\",\n requiresKeyToBeTrue: \"showFeaturedSection\",\n }),\n\n // Data tab - Data Configuration\n {\n key: \"dataSource\",\n label: \"Data Source\",\n type: \"dataSource\",\n description: \"\",\n tab: \"data\",\n group: \"Data Configuration\",\n },\n ],\n itemConfigSchema: {\n description: \"Configure settings for this list item\",\n fields: [\n {\n key: \"title\",\n label: \"Custom Title\",\n type: \"text\",\n description: \"Override the item's title\",\n },\n {\n key: \"description\",\n label: \"Custom Description\",\n type: \"textarea\",\n description: \"Override the item's description\",\n rows: 3,\n },\n {\n key: \"price\",\n label: \"Price\",\n type: \"text\",\n description: \"Current price\",\n },\n {\n key: \"originalPrice\",\n label: \"Original Price\",\n type: \"text\",\n description: \"Original price (will be crossed out)\",\n },\n {\n key: \"discount\",\n label: \"Discount Badge\",\n type: \"text\",\n description: \"Discount text (e.g., '40% Off')\",\n },\n {\n key: \"qv\",\n label: \"QV Value\",\n type: \"text\",\n description: \"Qualifying Volume value\",\n },\n {\n key: \"cv\",\n label: \"CV Value\",\n type: \"text\",\n description: \"Commission Volume value\",\n },\n {\n key: \"image\",\n label: \"Image URL\",\n type: \"text\",\n description: \"Custom image URL for this item\",\n },\n ],\n },\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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ACR,CAAC;EACFD,mBAAAA,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,KAAK;GACL,OAAO;GACP,cAAc,CAAC,SAAS;GACxB,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACDC,mBAAAA,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EACFD,mBAAAA,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,OAAO;GACR;EACF;CACD,kBAAkB;EAChB,aAAa;EACb,QAAQ;GACN;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,MAAM;IACP;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACF;EACF;CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ShopScreen-CPeoDfhv.cjs","names":["useShopTranslation","isVideoUrl","ChevronLeft","ChevronRight","Button","useShopTranslation","RadioGroup","RadioGroupItem","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Skeleton","useShopTranslation","usePortalProductCatalog","SearchSort","DropdownMenu","DropdownMenuTrigger","Button","ArrowUpDown","DropdownMenuContent","DropdownMenuLabel","DropdownMenuSeparator","DropdownMenuRadioGroup","DropdownMenuRadioItem","ProductCard","tagPortalProduct","usePortalProductDetail","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","ShoppingCart","React","useShopTranslation","Button","ShoppingCart","useFluidContext","useShopTranslation","useStore","useAppNavigation","ScreenHeaderBreadcrumbs","Breadcrumb","BreadcrumbList","BreadcrumbItem","BreadcrumbPage","ScreenHeaderActions","useNavigationParent","usePortalProductDetail","BreadcrumbLink","BreadcrumbSeparator"],"sources":["../../../shop/ui/src/components/image-gallery.tsx","../../../shop/ui/src/components/quantity-selector.tsx","../../../shop/ui/src/components/purchase-options.tsx","../../../shop/ui/src/components/shop-app.tsx","../../../cart/ui/src/components/cart-script.tsx","../../../cart/ui/src/components/cart-widget.tsx","../../../cart/ui/src/components/cart-button.tsx","../../../cart/ui/src/components/shop-container.tsx","../src/screens/ShopScreen.tsx"],"sourcesContent":["import type React from \"react\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\nimport { isVideoUrl } from \"../utils/media-helpers\";\nimport type { RenderImageProps } from \"./product-card\";\n\ninterface ImageGalleryProps {\n images: Array<{\n id: number;\n image_url: string;\n image_path: string | null;\n position: number;\n }>;\n fallbackImageUrl: string;\n productTitle: string;\n renderImage?: (props: RenderImageProps) => ReactNode;\n}\n\nfunction defaultRenderImage({\n src,\n alt,\n fill,\n className,\n onError,\n}: RenderImageProps): ReactNode {\n return (\n <img\n src={src}\n alt={alt}\n className={`${fill ? \"absolute inset-0 h-full w-full\" : \"\"} ${className ?? \"\"}`}\n onError={onError}\n />\n );\n}\n\nexport default function ImageGallery({\n images,\n fallbackImageUrl,\n productTitle,\n renderImage = defaultRenderImage,\n}: ImageGalleryProps): React.JSX.Element {\n const { t } = useShopTranslation();\n const [currentImageIndex, setCurrentImageIndex] = useState(0);\n\n const hasMultipleImages = images && images.length > 0;\n const displayImages = useMemo(\n () =>\n hasMultipleImages\n ? images.toSorted((a, b) => a.position - b.position)\n : [{ id: 0, image_url: fallbackImageUrl, position: 0 }],\n [images, hasMultipleImages, fallbackImageUrl],\n );\n\n // Reset to first image when the images array changes (e.g. variant switch)\n useEffect(() => {\n setCurrentImageIndex(0);\n }, [displayImages]);\n\n const nextImage = () => {\n if (displayImages.length > 1) {\n setCurrentImageIndex((prev) => (prev + 1) % displayImages.length);\n }\n };\n\n const prevImage = () => {\n if (displayImages.length > 1) {\n setCurrentImageIndex(\n (prev) => (prev - 1 + displayImages.length) % displayImages.length,\n );\n }\n };\n\n return (\n <div className=\"space-y-4\">\n {/* Main Image */}\n <div className=\"group relative aspect-square overflow-hidden rounded-sm bg-gray-100\">\n {isVideoUrl(displayImages[currentImageIndex]?.image_url) ? (\n <video\n key={displayImages[currentImageIndex]?.id}\n src={displayImages[currentImageIndex]?.image_url}\n className=\"absolute inset-0 h-full w-full object-cover\"\n controls\n loop\n playsInline\n />\n ) : (\n renderImage({\n src:\n displayImages[currentImageIndex]?.image_url || fallbackImageUrl,\n alt: productTitle,\n fill: true,\n className: \"object-cover\",\n onError: (e) => {\n e.currentTarget.src =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n },\n unoptimized: true,\n })\n )}\n\n {/* Navigation arrows */}\n {displayImages.length > 1 && (\n <>\n <button\n type=\"button\"\n aria-label={t(\"previous_image\")}\n className=\"absolute top-1/2 left-3 z-10 flex size-8 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/40 shadow-sm transition-colors group-hover:bg-white/80 hover:bg-white\"\n onClick={prevImage}\n >\n <ChevronLeft className=\"size-5 text-black/40 transition-colors group-hover:text-black\" />\n </button>\n <button\n type=\"button\"\n aria-label={t(\"next_image\")}\n className=\"absolute top-1/2 right-3 z-10 flex size-8 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/40 shadow-sm transition-colors group-hover:bg-white/80 hover:bg-white\"\n onClick={nextImage}\n >\n <ChevronRight className=\"size-5 text-black/40 transition-colors group-hover:text-black\" />\n </button>\n </>\n )}\n\n {/* Page Indicators - Bottom Center */}\n {displayImages.length > 1 && (\n <div className=\"absolute bottom-3 left-1/2 flex -translate-x-1/2 gap-3\">\n {displayImages.map((_, index) => (\n <button\n type=\"button\"\n key={index}\n className={`h-1.5 w-6 cursor-pointer rounded-lg transition-colors ${\n index === currentImageIndex\n ? \"bg-gray-800\"\n : \"bg-gray-400 hover:bg-gray-600\"\n }`}\n aria-label={t(\"go_to_image\", { index: String(index + 1) })}\n onClick={() => setCurrentImageIndex(index)}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n","import type React from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\n\ninterface QuantitySelectorProps {\n quantity: number;\n setQuantity: (quantity: number) => void;\n}\n\nexport default function QuantitySelector({\n quantity,\n setQuantity,\n}: QuantitySelectorProps): React.JSX.Element {\n return (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center rounded-lg\">\n <Button\n variant=\"default\"\n onClick={() => setQuantity(Math.max(1, quantity - 1))}\n className=\"border-0 px-3 py-2 shadow-none\"\n >\n −\n </Button>\n <span className=\"text-foreground min-w-8 px-4 py-2 text-center text-sm font-medium\">\n {quantity}\n </span>\n <Button\n variant=\"default\"\n onClick={() => setQuantity(quantity + 1)}\n className=\"border-0 px-3 py-2 shadow-none\"\n >\n +\n </Button>\n </div>\n </div>\n );\n}\n","import type React from \"react\";\nimport { useMemo } from \"react\";\nimport type { products } from \"@fluid-app/products-core\";\nimport {\n RadioGroup,\n RadioGroupItem,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@fluid-app/ui-primitives\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\n\ninterface PurchaseOptionsProps {\n showBuyOnce: boolean;\n showSubscribe: boolean;\n isSubscribe: boolean;\n onSubscribeChange: (subscribe: boolean) => void;\n product_subscription_plans: products.ProductSubscriptionPlan[];\n selectedSubscriptionPlan?: products.ProductSubscriptionPlan;\n onSubscriptionPlanChange?: (plan: products.ProductSubscriptionPlan) => void;\n userSelectedSubscribe: boolean;\n wholesalePrice?: number;\n wholesaleSubscriptionPrice?: number;\n}\n\nexport default function PurchaseOptions({\n showBuyOnce,\n showSubscribe,\n isSubscribe,\n onSubscribeChange,\n product_subscription_plans,\n selectedSubscriptionPlan,\n onSubscriptionPlanChange,\n userSelectedSubscribe,\n wholesalePrice,\n wholesaleSubscriptionPrice,\n}: PurchaseOptionsProps): React.JSX.Element | null {\n const { t } = useShopTranslation();\n\n // Find default subscription plan or use first one\n const defaultSubscriptionPlan = useMemo(() => {\n if (!product_subscription_plans?.length) return null;\n return (\n product_subscription_plans.find((plan) => plan.default) ||\n product_subscription_plans[0]\n );\n }, [product_subscription_plans]);\n\n // Use selected plan or default to the default/first plan\n const currentSubscriptionPlan =\n selectedSubscriptionPlan || defaultSubscriptionPlan;\n\n const savingsPercentage = useMemo(() => {\n if (\n wholesalePrice === undefined ||\n wholesaleSubscriptionPrice === undefined ||\n wholesalePrice <= 0\n ) {\n return null;\n }\n\n const savings = wholesalePrice - wholesaleSubscriptionPrice;\n if (savings <= 0) return null;\n\n const pct = Math.round((savings / wholesalePrice) * 100);\n return pct > 0 ? pct : null;\n }, [wholesalePrice, wholesaleSubscriptionPrice]);\n\n // Format subscription plan display name\n const formatSubscriptionPlan = (plan: products.ProductSubscriptionPlan) => {\n const interval = plan.subscription_plan.billing_interval;\n const unit = plan.subscription_plan.billing_interval_unit;\n return `${interval} ${unit} (${plan.subscription_plan.name})`;\n };\n\n // Prepare subscription plan options for Select component\n const subscriptionPlanOptions = useMemo(() => {\n if (!product_subscription_plans?.length) return [];\n\n return product_subscription_plans.map((plan) => ({\n value: plan.subscription_plan.id.toString(),\n label: formatSubscriptionPlan(plan),\n }));\n }, [product_subscription_plans]);\n\n // Don't render if only buy once is shown and subscribe is false\n if (showBuyOnce && !showSubscribe) {\n return null;\n }\n const handleSubscriptionPlanChange = (planId: string) => {\n const selectedPlan = product_subscription_plans?.find(\n (plan) => plan.subscription_plan.id.toString() === planId,\n );\n if (selectedPlan && onSubscriptionPlanChange) {\n onSubscriptionPlanChange(selectedPlan);\n }\n };\n\n const purchaseType = isSubscribe ? \"subscribe\" : \"once\";\n\n return (\n <div className=\"mb-3 flex flex-col\">\n <RadioGroup\n value={purchaseType}\n onValueChange={(value) => onSubscribeChange(value === \"subscribe\")}\n className=\"gap-0 space-y-0\"\n >\n {showSubscribe && (\n <div\n onClick={() => onSubscribeChange(true)}\n className={`cursor-pointer rounded-t-lg p-4 text-left transition-all duration-200 ${showBuyOnce ? \"border border-b-0\" : \"rounded-b-lg\"} ${userSelectedSubscribe ? \"bg-muted\" : \"bg-background\"}`}\n >\n <div className=\"flex items-start gap-x-3\">\n <RadioGroupItem\n value=\"subscribe\"\n onClick={(e) => e.stopPropagation()}\n className={`flex items-center justify-center [&_svg]:h-1.5 [&_svg]:w-1.5 [&_svg]:fill-white ${\n isSubscribe\n ? \"border-contrast bg-contrast text-foreground\"\n : \"border-border bg-background text-muted-foreground\"\n }`}\n />\n <div className=\"flex-1\">\n <div className=\"text-foreground mb-1 text-sm leading-tight font-medium\">\n {savingsPercentage\n ? t(\"subscribe_and_save\", {\n percentage: savingsPercentage,\n })\n : t(\"subscribe\")}\n </div>\n\n {/* Subscription Plan Dropdown*/}\n {product_subscription_plans?.length > 0 && (\n <div\n className=\"bg-muted mt-3 rounded-lg p-2\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"text-foreground mb-1 text-xs font-medium\">\n {t(\"delivery_frequency\")}\n </div>\n <Select\n value={\n currentSubscriptionPlan?.subscription_plan.id.toString() ||\n \"\"\n }\n onValueChange={handleSubscriptionPlanChange}\n disabled={product_subscription_plans?.length === 1}\n >\n <SelectTrigger className=\"bg-background text-foreground! w-full text-xs\">\n <SelectValue\n placeholder={t(\"select_delivery_schedule\")}\n />\n </SelectTrigger>\n <SelectContent>\n {subscriptionPlanOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n {showBuyOnce && (\n <div\n onClick={() => onSubscribeChange(false)}\n className={`cursor-pointer rounded-b-lg border p-4 text-left transition-all duration-200 ${showSubscribe ? \"border-t-0\" : \"rounded-t-lg\"} ${!userSelectedSubscribe ? \"bg-muted\" : \"bg-background\"}`}\n >\n <div className=\"flex items-center gap-x-3\">\n <RadioGroupItem\n value=\"once\"\n onClick={(e) => e.stopPropagation()}\n className={`flex items-center justify-center [&_svg]:h-1.5 [&_svg]:w-1.5 [&_svg]:fill-white ${\n !isSubscribe\n ? \"border-contrast bg-contrast text-foreground\"\n : \"border-border bg-background text-muted-foreground\"\n }`}\n />\n <div className=\"text-foreground mb-1 text-sm leading-tight font-medium\">\n {t(\"one_time_purchase\")}\n </div>\n </div>\n </div>\n )}\n </RadioGroup>\n </div>\n );\n}\n","import type React from \"react\";\nimport {\n useState,\n useMemo,\n useEffect,\n useRef,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport {\n usePortalProductCatalog,\n usePortalProductDetail,\n type PortalProductPageParam,\n type portalProducts,\n type products,\n} from \"@fluid-app/products-core\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport { SearchSort } from \"@fluid-app/ui-components/components/SearchSort\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\nimport { ArrowUpDown, ShoppingCart } from \"lucide-react\";\nimport ProductCard, {\n tagPortalProduct,\n type RenderImageProps,\n} from \"./product-card\";\nimport ImageGallery from \"./image-gallery\";\nimport QuantitySelector from \"./quantity-selector\";\nimport PurchaseOptions from \"./purchase-options\";\n\ninterface ShopAppProps {\n companyLogoUrl?: string | null;\n renderImage?: (props: RenderImageProps) => ReactNode;\n /** When provided, controls which product detail to show (URL-driven routing) */\n productId?: string | null;\n /** Called when a product is selected from the listing */\n onSelectProduct?: (productId: string) => void;\n /** Called when user navigates back from product detail */\n onBack?: () => void;\n /** Optional cart button to render in the header area */\n cartButton?: ReactNode;\n}\n\nconst PAGE_SIZE = 25;\n\nfunction sanitizeHtml(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n for (const el of doc.querySelectorAll(\n \"script, iframe, object, embed, form, base, meta, link, style\",\n )) {\n el.remove();\n }\n for (const el of doc.querySelectorAll(\"*\")) {\n for (const attr of [...el.attributes]) {\n if (\n attr.name.toLowerCase().startsWith(\"on\") ||\n attr.value.toLowerCase().trim().startsWith(\"javascript:\")\n ) {\n el.removeAttribute(attr.name);\n }\n }\n }\n return doc.body.innerHTML;\n}\n\nconst GRID_CLASS =\n \"grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4\";\n\nfunction SkeletonGrid({ count = 8 }: { count?: number }) {\n return (\n <div className={GRID_CLASS}>\n {Array.from({ length: count }, (_, i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n <Skeleton className=\"h-3 w-1/3\" />\n </div>\n ))}\n </div>\n );\n}\n\nfunction ProductListing({\n companyLogoUrl,\n renderImage,\n onSelectProduct,\n cartButton,\n}: {\n companyLogoUrl?: string | null;\n renderImage?: (props: RenderImageProps) => ReactNode;\n onSelectProduct: (productId: string) => void;\n cartButton?: ReactNode;\n}) {\n const observerTarget = useRef<HTMLDivElement>(null);\n const { t } = useShopTranslation();\n\n const catalog = usePortalProductCatalog({ perPage: PAGE_SIZE });\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n isFetched,\n } = useInfiniteQuery({\n queryKey: catalog.queryKey,\n queryFn: ({ pageParam, signal }) =>\n catalog.fetchProducts(pageParam, signal),\n getNextPageParam: catalog.getNextPageParam,\n initialPageParam: undefined as PortalProductPageParam,\n });\n\n const handleIntersect = useCallback(\n (entries: IntersectionObserverEntry[]) => {\n if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n\n const observer = new IntersectionObserver(handleIntersect, {\n threshold: 0.1,\n rootMargin: \"200px\",\n });\n observer.observe(target);\n return () => observer.disconnect();\n }, [handleIntersect]);\n\n const allProducts = data?.pages.flatMap((page) => page.products) ?? [];\n\n const sortOptions = useMemo(\n () => [\n { id: \"title_asc\", label: t(\"sort_title_asc\") },\n { id: \"title_desc\", label: t(\"sort_title_desc\") },\n { id: \"price_asc\", label: t(\"sort_price_asc\") },\n { id: \"price_desc\", label: t(\"sort_price_desc\") },\n { id: \"created_at_desc\", label: t(\"sort_recent\") },\n { id: \"created_at_asc\", label: t(\"sort_oldest\") },\n ],\n [t],\n );\n\n return (\n <div>\n <div className=\"mx-auto px-2 md:px-10\">\n {/* Search + Sort */}\n <div className=\"flex items-center justify-end gap-2 py-4\">\n <div className=\"w-full max-w-sm\">\n <SearchSort\n searchValue={catalog.searchTerm}\n onSearchChange={catalog.setSearchTerm}\n placeholder={t(\"search_placeholder\")}\n />\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"border-foreground/10 shadow-input size-9 shrink-0\"\n >\n <ArrowUpDown className=\"size-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-60\">\n <DropdownMenuLabel>{t(\"sort_by\")}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuRadioGroup\n value={catalog.currentSort}\n onValueChange={catalog.setCurrentSort}\n >\n {sortOptions.map((opt) => (\n <DropdownMenuRadioItem key={opt.id} value={opt.id}>\n {opt.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n {cartButton && (\n <div className=\"flex shrink-0 items-center gap-3\">{cartButton}</div>\n )}\n </div>\n </div>\n\n {/* Product Grid */}\n <div className=\"mx-auto space-y-8 px-2 md:px-10 md:py-8\">\n {isLoading ? (\n <SkeletonGrid />\n ) : error ? (\n <p className=\"mx-auto my-6 rounded-lg bg-red-100 px-3 py-2 text-red-500\">\n {t(\"error_generic\")}\n </p>\n ) : isFetched && allProducts.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-muted-foreground text-sm\">\n {catalog.searchTerm\n ? t(\"no_search_results\", { term: catalog.searchTerm })\n : t(\"no_products\")}\n </p>\n </div>\n ) : (\n <>\n <div className={GRID_CLASS}>\n {allProducts.map((product) => (\n <ProductCard\n key={product.id}\n product={tagPortalProduct(product)}\n {...(companyLogoUrl !== undefined && { companyLogoUrl })}\n {...(renderImage !== undefined && { renderImage })}\n onClick={() => onSelectProduct(String(product.id))}\n />\n ))}\n </div>\n <div ref={observerTarget} />\n {isFetchingNextPage && <SkeletonGrid count={4} />}\n </>\n )}\n </div>\n </div>\n );\n}\n\ninterface OptionGroup {\n optionId: number;\n name: string;\n values: { id: number; name: string }[];\n}\n\n/**\n * Build the variant options map by grouping option_values across non-master variants.\n * Groups by option_id, collecting unique value entries.\n */\nfunction buildOptionGroups(variants: portalProducts.Variant[]): OptionGroup[] {\n const groupMap = new Map<\n number,\n { name: string; values: Map<number, string> }\n >();\n\n for (const variant of variants) {\n if (variant.is_master) continue;\n for (const ov of variant.option_values ?? []) {\n if (ov.option_id == null || ov.id == null) continue;\n if (!groupMap.has(ov.option_id)) {\n groupMap.set(ov.option_id, {\n name: ov.option_name ?? `Option ${ov.option_id}`,\n values: new Map(),\n });\n }\n groupMap.get(ov.option_id)!.values.set(ov.id, ov.name ?? String(ov.id));\n }\n }\n\n return [...groupMap.entries()].map(([optionId, group]) => ({\n optionId,\n name: group.name,\n values: [...group.values.entries()].map(([id, name]) => ({ id, name })),\n }));\n}\n\n/**\n * Find the variant matching a set of selected option value IDs.\n * selections is a map of optionId → selected value id.\n */\nfunction findVariantBySelections(\n variants: portalProducts.Variant[],\n selections: Record<number, number>,\n): portalProducts.Variant | undefined {\n const entries = Object.entries(selections).map(\n ([k, v]) => [Number(k), v] as const,\n );\n if (entries.length === 0) return undefined;\n return variants.find(\n (v) =>\n !v.is_master &&\n entries.every(([optionId, valueId]) =>\n v.option_values?.some(\n (ov) => ov.option_id === optionId && ov.id === valueId,\n ),\n ),\n );\n}\n\n/**\n * Map BFF flat subscription plans to the legacy ProductSubscriptionPlan shape\n * expected by the PurchaseOptions component.\n */\nfunction mapToLegacySubscriptionPlans(\n plans: portalProducts.SubscriptionPlan[],\n): products.ProductSubscriptionPlan[] {\n return plans.map((plan, idx) => ({\n ...(plan.id !== undefined && { id: plan.id }),\n default: idx === 0,\n products_count: null,\n subscribers_count: null,\n active: true,\n subscription_plan: {\n id: plan.id ?? 0,\n name: plan.name ?? \"\",\n billing_interval: plan.billing_interval ?? 1,\n billing_interval_unit: plan.billing_interval_unit ?? \"month\",\n billing_frequency_in_words:\n plan.billing_frequency ??\n `${plan.billing_interval} ${plan.billing_interval_unit}`,\n active: true,\n price_adjustment_amount: null,\n price_adjustment_type: null,\n },\n }));\n}\n\nfunction formatPrice(price: string | undefined, currency: string | undefined) {\n if (!price) return null;\n const numericPrice = Number(price);\n if (Number.isNaN(numericPrice)) return `${currency ?? \"\"}${price}`;\n try {\n return new Intl.NumberFormat(undefined, {\n style: \"currency\",\n currency: currency || \"USD\",\n }).format(numericPrice);\n } catch {\n return `$${price}`;\n }\n}\n\nfunction ProductDetail({\n productId,\n renderImage,\n}: {\n productId: string;\n renderImage?: (props: RenderImageProps) => ReactNode;\n}) {\n const [quantity, setQuantity] = useState(1);\n // selections: optionId → selected value id\n const [selections, setSelections] = useState<Record<number, number>>({});\n const [userSelectedSubscribe, setUserSelectedSubscribe] = useState(false);\n const [selectedSubscriptionPlan, setSelectedSubscriptionPlan] = useState<\n products.ProductSubscriptionPlan | undefined\n >(undefined);\n\n const { t } = useShopTranslation();\n\n const { product, isLoading, error, images } = usePortalProductDetail({\n productId,\n });\n\n const variants = useMemo(() => product?.variants ?? [], [product?.variants]);\n const subscriptionPlans = useMemo(\n () => product?.subscription_plans ?? [],\n [product?.subscription_plans],\n );\n\n // Default to the master variant, or the first available variant\n const masterVariant = useMemo(\n () => variants.find((v) => v.is_master) ?? variants[0],\n [variants],\n );\n\n // Build option groups from non-master variants\n const optionGroups = useMemo(() => buildOptionGroups(variants), [variants]);\n\n // Title-based fallback: products that distinguish variants by `title` only\n // (no Options/OptionValues set up) — list every variant by title.\n const showVariantChoices = optionGroups.length === 0 && variants.length > 1;\n const [selectedVariantId, setSelectedVariantId] = useState<number | null>(\n null,\n );\n\n // Initialise selections from the first non-master variant (or master fallback)\n useEffect(() => {\n if (optionGroups.length === 0) return;\n const firstNonMaster = variants.find((v) => !v.is_master);\n const source = firstNonMaster ?? masterVariant;\n if (!source?.option_values?.length) return;\n const defaults: Record<number, number> = {};\n for (const ov of source.option_values) {\n if (ov.option_id != null && ov.id != null) {\n defaults[ov.option_id] = ov.id;\n }\n }\n setSelections(defaults);\n }, [optionGroups, variants, masterVariant]);\n\n // Default the title-based picker to the master variant\n useEffect(() => {\n if (!showVariantChoices) return;\n if (selectedVariantId != null) return;\n if (masterVariant?.id == null) return;\n setSelectedVariantId(masterVariant.id);\n }, [showVariantChoices, selectedVariantId, masterVariant?.id]);\n\n // Resolve the currently selected variant\n const selectedVariant = useMemo(() => {\n if (showVariantChoices) {\n return variants.find((v) => v.id === selectedVariantId) ?? masterVariant;\n }\n if (optionGroups.length === 0) return masterVariant;\n return findVariantBySelections(variants, selections) ?? masterVariant;\n }, [\n variants,\n optionGroups,\n selections,\n masterVariant,\n showVariantChoices,\n selectedVariantId,\n ]);\n\n // Map BFF subscription plans → legacy ProductSubscriptionPlan shape for PurchaseOptions\n const legacySubscriptionPlans = useMemo(\n () => mapToLegacySubscriptionPlans(subscriptionPlans),\n [subscriptionPlans],\n );\n\n // Derive subscription state from variant\n const isSubscribe =\n selectedVariant?.subscription_only === true || userSelectedSubscribe;\n const showSubscribe = legacySubscriptionPlans.length > 0;\n const showBuyOnce = selectedVariant?.subscription_only !== true;\n\n // Auto-select subscribe when variant is subscription-only\n useEffect(() => {\n if (\n selectedVariant?.subscription_only &&\n legacySubscriptionPlans.length > 0\n ) {\n setUserSelectedSubscribe(true);\n }\n }, [selectedVariant, legacySubscriptionPlans]);\n\n // Prefer variant-specific images when available, fall back to product-level images\n const galleryImages = useMemo(() => {\n const variantImages = selectedVariant?.images;\n if (variantImages && variantImages.length > 0) {\n return variantImages.map((img, idx) => ({\n id: idx,\n image_url: img.url ?? \"\",\n image_path: null as string | null,\n position: idx,\n }));\n }\n return images.map((img, idx) => ({\n id: img.id ?? idx,\n image_url: img.url,\n image_path: null as string | null,\n position: idx,\n }));\n }, [selectedVariant?.images, images]);\n\n const coverImage = galleryImages[0]?.image_url ?? null;\n\n // Pricing: use selected variant's subscription_pricing for wholesale comparison\n const selectedPricing = selectedVariant?.subscription_pricing?.find(\n (sp) =>\n sp.plan_id ===\n (selectedSubscriptionPlan?.subscription_plan.id ??\n legacySubscriptionPlans[0]?.subscription_plan.id),\n );\n const wholesalePrice = selectedVariant?.wholesale_price\n ? Number(selectedVariant.wholesale_price)\n : undefined;\n const wholesaleSubscriptionPrice = selectedPricing?.wholesale_price\n ? Number(selectedPricing.wholesale_price)\n : undefined;\n\n // Display prices\n // price = retail price, wholesale_price = rep/logged-in price\n // subscription_pricing.wholesale_price = subscription wholesale price\n const currency = selectedVariant?.currency ?? product?.currency;\n const displayPrice = formatPrice(\n selectedVariant?.price ?? product?.price,\n currency,\n );\n const displayWholesalePrice = formatPrice(\n selectedVariant?.wholesale_price ??\n selectedVariant?.price ??\n product?.price,\n currency,\n );\n const displayWholesaleSubscriptionPrice = formatPrice(\n selectedPricing?.wholesale_price ??\n selectedPricing?.price ??\n selectedVariant?.wholesale_price ??\n undefined,\n currency,\n );\n\n // Resolve subscription plan ID for the cart SDK\n const resolvedPlanId =\n selectedSubscriptionPlan?.subscription_plan.id ??\n legacySubscriptionPlans.find((p) => p.default)?.subscription_plan.id ??\n legacySubscriptionPlans[0]?.subscription_plan.id;\n\n // Cart data attributes — SDK expects variant ID, not product ID\n const cartVariantId = String(selectedVariant?.id ?? product?.id ?? \"\");\n\n if (isLoading) {\n return (\n <div className=\"mx-auto max-w-7xl py-8 pr-4 pl-0 md:pr-6 lg:pr-8 lg:pl-0\">\n <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <div className=\"space-y-4 pl-2 lg:pl-20\">\n <Skeleton className=\"h-8 w-3/4\" />\n <Skeleton className=\"h-5 w-1/4\" />\n <Skeleton className=\"h-20 w-full\" />\n <Skeleton className=\"h-10 w-1/2\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"text-center\">\n <h3 className=\"text-foreground mb-2 text-lg font-medium\">\n {t(\"error_loading\")}\n </h3>\n <p className=\"text-muted-foreground\">{t(\"error_generic\")}</p>\n </div>\n </div>\n );\n }\n\n if (!product) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"text-center\">\n <h3 className=\"text-foreground mb-2 text-lg font-medium\">\n {t(\"product_not_found\")}\n </h3>\n <p className=\"text-muted-foreground\">\n {t(\"product_not_found_description\")}\n </p>\n </div>\n </div>\n );\n }\n\n const title = product.name || t(\"product_fallback_name\");\n const isBundle = product.bundle === true;\n const bundleUrl =\n isBundle && product.canonical_url ? product.canonical_url : null;\n\n return (\n <div className=\"pb-5 md:pl-8\">\n <div className=\"mx-auto max-w-7xl px-4 py-8 md:pr-6 md:pl-0 lg:pr-8 lg:pl-0\">\n <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-2\">\n {/* Image Gallery */}\n <ImageGallery\n images={galleryImages}\n fallbackImageUrl={\n coverImage ??\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\"\n }\n productTitle={title}\n {...(renderImage !== undefined && { renderImage })}\n />\n\n {/* Product Info */}\n <div className=\"max-w-lg pl-2 lg:pl-20\">\n <h1 className=\"text-foreground text-3xl font-bold\">{title}</h1>\n\n {/* Price — matches admin exactly */}\n {!isBundle && (\n <div className=\"mb-2 flex items-center gap-2\">\n <span className=\"text-foreground text-sm\">\n {isSubscribe\n ? displayWholesaleSubscriptionPrice\n : displayWholesalePrice}\n </span>\n {((isSubscribe &&\n displayWholesaleSubscriptionPrice !==\n displayWholesalePrice) ||\n (!isSubscribe && displayWholesalePrice !== displayPrice)) && (\n <span className=\"text-muted-foreground text-sm line-through\">\n {isSubscribe ? displayWholesalePrice : displayPrice}\n </span>\n )}\n </div>\n )}\n\n {/* Product Description */}\n <div className=\"pt-2\">\n <h3 className=\"text-foreground mb-1 text-sm font-medium\">\n {t(\"product_description\")}\n </h3>\n <div\n className=\"text-foreground mb-3 text-[12px]\"\n dangerouslySetInnerHTML={{\n __html: sanitizeHtml(product.description ?? \"\"),\n }}\n />\n </div>\n\n {product.wholesale_price != null && (\n <div className=\"text-muted-foreground mb-3 text-sm\">\n CV {selectedVariant?.cv ?? \"-\"} | QV{\" \"}\n {selectedVariant?.qv ?? \"-\"}\n </div>\n )}\n\n {isBundle ? (\n <div className=\"pt-4\">\n <Button\n variant=\"default\"\n className=\"w-full gap-2 py-2 text-base font-medium\"\n disabled={!bundleUrl}\n onClick={() => {\n if (bundleUrl)\n window.open(bundleUrl, \"_blank\", \"noopener,noreferrer\");\n }}\n >\n {t(\"purchase_bundle\")}\n </Button>\n {!bundleUrl && (\n <p className=\"text-muted-foreground mt-2 text-center text-xs\">\n {t(\"bundle_unavailable\")}\n </p>\n )}\n </div>\n ) : (\n <>\n {/* Purchase Options — matches admin PurchaseOptions component */}\n <PurchaseOptions\n showBuyOnce={showBuyOnce}\n showSubscribe={showSubscribe}\n isSubscribe={isSubscribe}\n userSelectedSubscribe={userSelectedSubscribe}\n onSubscribeChange={setUserSelectedSubscribe}\n {...(wholesalePrice !== undefined && { wholesalePrice })}\n {...(wholesaleSubscriptionPrice !== undefined && {\n wholesaleSubscriptionPrice,\n })}\n product_subscription_plans={legacySubscriptionPlans}\n {...(selectedSubscriptionPlan !== undefined && {\n selectedSubscriptionPlan,\n })}\n onSubscriptionPlanChange={setSelectedSubscriptionPlan}\n />\n\n {/* Variant Options — matches admin layout */}\n {optionGroups.length > 0 && (\n <div className=\"mb-4 pt-4\">\n {optionGroups.map((group) => (\n <div\n key={group.optionId}\n className=\"mb-3 flex items-center\"\n >\n <h3 className=\"text-md text-foreground w-24 font-bold\">\n {group.name.charAt(0).toUpperCase() +\n group.name.slice(1)}\n </h3>\n <Select\n value={String(selections[group.optionId] ?? \"\")}\n onValueChange={(value) =>\n setSelections((prev) => ({\n ...prev,\n [group.optionId]: Number(value),\n }))\n }\n >\n <SelectTrigger className=\"w-48 max-w-full\">\n <SelectValue\n placeholder={t(\"select_option\", {\n name: group.name,\n })}\n />\n </SelectTrigger>\n <SelectContent position=\"popper\" className=\"max-h-60\">\n {group.values.map((v) => (\n <SelectItem key={v.id} value={String(v.id)}>\n {v.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ))}\n </div>\n )}\n\n {/* Title-based variant picker — for products without Options */}\n {showVariantChoices && (\n <div className=\"mb-4 pt-4\">\n <div className=\"mb-3 flex items-center\">\n <h3 className=\"text-md text-foreground w-24 font-bold\">\n {t(\"variant_label\")}\n </h3>\n <Select\n value={\n selectedVariantId != null\n ? String(selectedVariantId)\n : \"\"\n }\n onValueChange={(value) =>\n setSelectedVariantId(Number(value))\n }\n >\n <SelectTrigger className=\"w-48 max-w-full\">\n <SelectValue placeholder={t(\"select_variant\")} />\n </SelectTrigger>\n <SelectContent position=\"popper\" className=\"max-h-60\">\n {variants.map((v, idx) =>\n v.id == null ? null : (\n <SelectItem key={v.id} value={String(v.id)}>\n {v.title ||\n t(\"variant_fallback_name\", {\n index: String(idx + 1),\n })}\n </SelectItem>\n ),\n )}\n </SelectContent>\n </Select>\n </div>\n </div>\n )}\n\n {/* Unavailable product message */}\n {selectedVariant?.subscription_only &&\n !selectedVariant?.allow_subscription && (\n <div className=\"text-muted-foreground text-sm\">\n {t(\"product_unavailable\")}\n </div>\n )}\n\n {/* Quantity and Add to Cart — matches admin */}\n <div className=\"mt-4 mb-3\" />\n <div className=\"flex items-center gap-3 pb-3\">\n <QuantitySelector\n quantity={quantity}\n setQuantity={setQuantity}\n />\n\n <Button\n variant=\"default\"\n className=\"flex-1 gap-2 py-2 text-base font-medium\"\n disabled={\n selectedVariant?.subscription_only === true &&\n !selectedVariant?.allow_subscription\n }\n data-fluid-add-to-cart={cartVariantId}\n data-fluid-quantity={quantity}\n data-fluid-subscribe={isSubscribe}\n data-fluid-subscription-plan-id={\n isSubscribe ? String(resolvedPlanId ?? \"\") : \"\"\n }\n data-fluid-open-cart-after-add=\"false\"\n >\n <ShoppingCart className=\"size-4\" />\n {isSubscribe ? t(\"subscribe\") : t(\"add_to_cart\")}\n </Button>\n </div>\n </>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nexport default function ShopApp({\n companyLogoUrl,\n renderImage,\n productId: controlledProductId,\n onSelectProduct: onSelectProductProp,\n onBack: _onBack,\n cartButton,\n}: ShopAppProps): React.JSX.Element {\n // Internal state used only when navigation is not controlled externally\n const [internalProductId, setInternalProductId] = useState<string | null>(\n null,\n );\n\n const isControlled = controlledProductId !== undefined;\n const activeProductId = isControlled\n ? controlledProductId\n : internalProductId;\n\n const handleSelectProduct = onSelectProductProp ?? setInternalProductId;\n\n if (activeProductId) {\n return (\n <ProductDetail\n productId={activeProductId}\n {...(renderImage !== undefined && { renderImage })}\n />\n );\n }\n\n return (\n <ProductListing\n {...(companyLogoUrl !== undefined && { companyLogoUrl })}\n {...(renderImage !== undefined && { renderImage })}\n onSelectProduct={handleSelectProduct}\n cartButton={cartButton}\n />\n );\n}\n","import { useEffect, useRef } from \"react\";\n\ninterface CartScriptProps {\n subdomain: string;\n authJwt?: string;\n /** Enable BFF mode — uses portal session cookies instead of JWT for cart auth. */\n bffMode?: boolean;\n /** Override the SDK script URL (e.g. \"http://localhost:4444/index.js\" for local dev). */\n scriptSrc?: string;\n /** Override the API base URL the SDK uses (e.g. \"http://localhost:3000\" for local dev). */\n apiBaseUrl?: string;\n /** Enable SDK debug logging. */\n debug?: boolean;\n}\n\nconst SCRIPT_ID = \"fluid-cdn-script\";\nconst LEAD_CAPTURE_ID = \"fluid-lead-capture-suppress\";\nconst DEFAULT_SCRIPT_SRC =\n \"https://assets.fluid.app/scripts/fluid-sdk/latest/web-widgets/index.js\";\n\nexport default function CartScript({\n subdomain,\n authJwt,\n bffMode,\n scriptSrc,\n apiBaseUrl,\n debug,\n}: CartScriptProps): React.ReactNode {\n // Use a ref so the script is injected once with the initial values.\n // ES modules are cached by URL — re-inserting the same script won't\n // re-execute it, so changing props after the first load has no effect.\n const authJwtRef = useRef(authJwt);\n authJwtRef.current = authJwt;\n\n useEffect(() => {\n if (!subdomain) return;\n\n // Don't add a duplicate script\n if (document.getElementById(SCRIPT_ID)) return;\n\n const script = document.createElement(\"script\");\n script.id = SCRIPT_ID;\n script.src = scriptSrc ?? DEFAULT_SCRIPT_SRC;\n script.type = \"module\";\n script.crossOrigin = \"anonymous\";\n script.dataset.fluidShop = subdomain;\n if (bffMode) {\n script.dataset.bffMode = \"true\";\n } else if (authJwtRef.current) {\n script.dataset.authJwt = authJwtRef.current;\n }\n if (apiBaseUrl) {\n script.dataset.fluidApiBaseUrl = apiBaseUrl;\n }\n if (debug) {\n script.dataset.debug = \"true\";\n }\n document.head.appendChild(script);\n\n // Suppress the SDK's auto-injected lead capture widget.\n // The SDK skips injection when it finds an existing element with hide-widget.\n const leadCapture = document.createElement(\"fluid-lead-capture-widget\");\n leadCapture.id = LEAD_CAPTURE_ID;\n leadCapture.setAttribute(\"hide-widget\", \"true\");\n document.body.appendChild(leadCapture);\n\n return () => {\n const existing = document.getElementById(SCRIPT_ID);\n if (existing) existing.remove();\n const existingLeadCapture = document.getElementById(LEAD_CAPTURE_ID);\n if (existingLeadCapture) existingLeadCapture.remove();\n };\n }, [subdomain, bffMode, scriptSrc, apiBaseUrl, debug]);\n\n return null;\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\ninterface CartWidgetProps {\n theme?: Record<string, string>;\n}\n\nexport default function CartWidget({\n theme,\n}: CartWidgetProps): React.ReactNode {\n const [mounted, setMounted] = useState(false);\n const widgetRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n useEffect(() => {\n if (!mounted) return;\n const el = widgetRef.current;\n if (!el) return;\n if (theme) {\n el.setAttribute(\"theme\", JSON.stringify(theme));\n } else {\n el.removeAttribute(\"theme\");\n }\n }, [theme, mounted]);\n\n const widget = React.createElement(\"fluid-cart-widget\", {\n ref: (el: HTMLElement | null) => {\n widgetRef.current = el;\n },\n \"data-fluid-widget\": \"true\",\n \"hide-widget\": \"true\",\n \"is-primary\": \"true\",\n });\n\n // Portal to document.body so the cart drawer escapes any\n // overflow-hidden / isolation stacking contexts in the layout.\n if (mounted) {\n return createPortal(widget, document.body);\n }\n\n return null;\n}\n","\"use client\";\n\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { ShoppingCart } from \"lucide-react\";\nimport React, { useEffect, useCallback, useState } from \"react\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\n\ndeclare global {\n interface Window {\n FluidCommerceSDK?: {\n getCheckoutUrl: () => string;\n setOnCheckout: (callback: () => void) => void;\n };\n FairShareSDK?: {\n getCartItemCount: () => number;\n isBffMode: () => boolean;\n updateLocaleSettings: (options: {\n language?: string;\n country?: string;\n }) => Promise<void>;\n };\n fluidCart?: {\n open: () => void;\n };\n }\n}\n\ninterface CartButtonProps {\n onCheckout?: (checkoutUrl: string) => void;\n}\n\nconst MAX_SDK_POLL_ATTEMPTS = 50; // 5 seconds at 100ms intervals\n\nexport function CartButton({ onCheckout }: CartButtonProps): React.ReactNode {\n const { t } = useShopTranslation();\n const [initialCount, setInitialCount] = useState(0);\n\n const navigateToCheckout = useCallback(() => {\n if (!window.FluidCommerceSDK) {\n console.error(\"FluidCommerceSDK not available\");\n return;\n }\n\n try {\n const checkoutUrl = window.FluidCommerceSDK.getCheckoutUrl();\n if (!checkoutUrl) {\n console.error(\"No checkout URL available\");\n return;\n }\n onCheckout?.(checkoutUrl);\n } catch (error) {\n console.error(\"Error getting checkout URL:\", error);\n }\n }, [onCheckout]);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let attempts = 0;\n let cancelled = false;\n\n const setupSDK = () => {\n if (cancelled) return;\n if (window.FluidCommerceSDK) {\n if (onCheckout) {\n window.FluidCommerceSDK.setOnCheckout(navigateToCheckout);\n }\n const count = window.FairShareSDK?.getCartItemCount?.();\n if (count != null) {\n setInitialCount(count);\n }\n } else if (attempts < MAX_SDK_POLL_ATTEMPTS) {\n attempts++;\n timeoutId = setTimeout(setupSDK, 100);\n }\n };\n\n setupSDK();\n return () => {\n cancelled = true;\n if (timeoutId) clearTimeout(timeoutId);\n };\n }, [navigateToCheckout, onCheckout]);\n\n return (\n <Button\n className=\"bg-primary text-primary-foreground hover:bg-primary/70 relative flex h-8 items-center gap-2 rounded-sm px-3 py-1 text-xs\"\n onClick={() => {\n window.fluidCart?.open();\n }}\n >\n <div className=\"relative\">\n <ShoppingCart className=\"size-4\" />\n <span\n id=\"fluid-cart-count\"\n className=\"bg-primary-foreground text-primary absolute -top-1 -right-2 flex size-3.5 items-center justify-center rounded-full text-[8px] font-bold\"\n >\n {initialCount}\n </span>\n </div>\n <span>{t(\"cart\")}</span>\n </Button>\n );\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\ninterface ShopContainerProps {\n children: React.ReactNode;\n className?: string;\n cartScript?: React.ReactNode;\n cartWidget?: React.ReactNode;\n}\n\nexport default function ShopContainer({\n children,\n className = \"\",\n cartScript,\n cartWidget,\n}: ShopContainerProps): React.ReactNode {\n const containerRef = useRef<HTMLDivElement>(null);\n const [portalContainer, setPortalContainer] = useState<HTMLDivElement | null>(\n null,\n );\n\n useEffect(() => {\n const currentContainer = containerRef.current;\n if (!currentContainer) return;\n\n const reactContentWrapper = document.createElement(\"div\");\n reactContentWrapper.id = \"react-content-wrapper\";\n reactContentWrapper.style.cssText = `\n position: relative;\n `;\n reactContentWrapper.className = \"min-h-full\";\n\n currentContainer.appendChild(reactContentWrapper);\n\n setPortalContainer(reactContentWrapper);\n\n return () => {\n if (currentContainer && reactContentWrapper) {\n try {\n currentContainer.removeChild(reactContentWrapper);\n } catch (e) {\n console.warn(\"Failed to cleanup isolated container:\", e);\n }\n }\n setPortalContainer(null);\n };\n }, []);\n\n return (\n <>\n <div\n ref={containerRef}\n className={`isolated-shop-wrapper ${className} h-full`}\n >\n {portalContainer &&\n createPortal(\n <>\n {cartScript}\n {cartWidget}\n {children}\n </>,\n portalContainer,\n )}\n </div>\n </>\n );\n}\n","import { type ComponentProps, useEffect } from \"react\";\nimport ShopApp from \"@fluid-app/shop-ui/components/shop-app\";\nimport {\n CartButton,\n CartScript,\n CartWidget,\n ShopContainer,\n} from \"@fluid-app/cart-ui\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"@fluid-app/ui-primitives\";\nimport {\n ScreenHeaderBreadcrumbs,\n ScreenHeaderActions,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\nimport { useNavigationParent } from \"../shell/use-navigation-parent\";\nimport { useStore } from \"../hooks/use-store\";\nimport { usePortalProductDetail } from \"@fluid-app/products-core\";\n\ntype ShopScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\nexport function ShopScreen(props: ShopScreenProps): React.JSX.Element {\n return <ShopScreenContent {...props} />;\n}\n\nfunction ShopScreenContent({\n /* eslint-disable @typescript-eslint/no-unused-vars -- destructured to exclude from divProps spread */\n background,\n textColor,\n accentColor,\n padding,\n borderRadius,\n /* eslint-enable @typescript-eslint/no-unused-vars */\n ...divProps\n}: ShopScreenProps): React.JSX.Element {\n const { config } = useFluidContext();\n const { t } = useShopTranslation();\n const { data: store } = useStore();\n const { currentSlug, navigate } = useAppNavigation();\n const countryCode = config.countryIso ?? \"US\";\n const subdomain = store?.subdomain;\n\n // Parse product ID from slug: \"shop/{productId}\"\n const parts = currentSlug.split(\"/\");\n const productId = parts[1] ?? null;\n\n // Sync country to FairShare SDK so the cart uses the correct country.\n // The SDK loads asynchronously via a script tag and also initializes its\n // session asynchronously after the object appears on window. Poll until\n // updateLocaleSettings resolves successfully (Sentry fix: FLUID-ADMIN-1FD —\n // SDK object present does not mean session is ready).\n useEffect(() => {\n if (!countryCode) return;\n type FairShareWindow = {\n FairShareSDK?: {\n updateLocaleSettings: (opts: { country: string }) => Promise<void>;\n };\n };\n const sdk = () => (window as FairShareWindow).FairShareSDK;\n let done = false;\n\n let inFlight = false;\n const tryUpdate = async () => {\n const fairShareSdk = sdk();\n if (done || inFlight || !fairShareSdk) return;\n inFlight = true;\n try {\n await fairShareSdk.updateLocaleSettings({ country: countryCode });\n done = true;\n clearInterval(id);\n } catch {\n // Session not yet initialized — keep polling until it is\n } finally {\n inFlight = false;\n }\n };\n\n let attempts = 0;\n const id = setInterval(() => {\n if (attempts >= 50) {\n console.warn(\n \"[FairShare] updateLocaleSettings: gave up after 50 attempts — session never became ready\",\n { country: countryCode },\n );\n clearInterval(id);\n return;\n }\n attempts++;\n void tryUpdate();\n }, 100);\n\n return () => {\n done = true;\n clearInterval(id);\n };\n }, [countryCode]);\n\n // TODO(portal-theme): Cart widget theming requires AppShell to expose\n // resolvedTheme via context. Currently it's local state in AppShell.\n\n return (\n <>\n {!productId && (\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n )}\n <ScreenHeaderActions>\n <div className=\"flex items-center gap-4\">\n <CartButton />\n </div>\n </ScreenHeaderActions>\n <div {...divProps} className={divProps.className ?? \"\"}>\n <ShopContainer\n cartScript={\n subdomain ? (\n <CartScript\n subdomain={subdomain}\n bffMode\n scriptSrc={\n import.meta.env.DEV\n ? String(\n import.meta.env.VITE_FAIRSHARE_SDK_URL ||\n \"http://localhost:4444/index.js\",\n )\n : undefined\n }\n apiBaseUrl={\n import.meta.env.DEV\n ? String(\n import.meta.env.VITE_FAIRSHARE_API_BASE_URL ||\n \"http://api.fluid.localhost:3000\",\n )\n : undefined\n }\n debug={import.meta.env.DEV}\n />\n ) : null\n }\n cartWidget={<CartWidget />}\n >\n {productId && <ProductBreadcrumb productId={productId} />}\n <ShopApp\n companyLogoUrl={store?.logo_url ?? undefined}\n productId={productId}\n onSelectProduct={(id) => navigate(`shop/${id}`)}\n onBack={() => navigate(\"shop\")}\n />\n </ShopContainer>\n </div>\n </>\n );\n}\n\nexport const shopScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ShopScreen\",\n displayName: \"Shop Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n\n/**\n * Renders inside PortalProductsCoreProvider to set breadcrumbs with the\n * product name: \"Shop > {product name}\". Overrides the parent's \"Shop\"\n * breadcrumb once product data loads.\n */\nfunction ProductBreadcrumb({ productId }: { productId: string }) {\n const { navigate } = useAppNavigation();\n const parentBreadcrumb = useNavigationParent();\n const { t } = useShopTranslation();\n const { product } = usePortalProductDetail({ productId });\n const productName = product?.name ?? t(\"product_fallback_name\");\n\n const parentLabel = parentBreadcrumb?.label ?? t(\"breadcrumb\");\n const onParentClick = parentBreadcrumb?.onClick ?? (() => navigate(\"shop\"));\n\n return (\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n onClick={onParentClick}\n className=\"cursor-pointer font-semibold\"\n >\n {parentLabel}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {productName}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n );\n}\n"],"mappings":"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"}
|