@fluid-app/portal-sdk 0.1.492 → 0.1.494
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/{AnnouncementWidget-CNP37JUB.cjs → AnnouncementWidget--nRHRrQH.cjs} +3 -3
- package/dist/{AnnouncementWidget-93a322Pl.mjs → AnnouncementWidget-BND76Ojg.mjs} +2 -2
- package/dist/{AnnouncementWidget-93a322Pl.mjs.map → AnnouncementWidget-BND76Ojg.mjs.map} +1 -1
- package/dist/{AnnouncementWidget-BIFJ7E6o.cjs → AnnouncementWidget-Dh6nKAqT.cjs} +2 -2
- package/dist/{AnnouncementWidget-BIFJ7E6o.cjs.map → AnnouncementWidget-Dh6nKAqT.cjs.map} +1 -1
- package/dist/{CarouselWidget-fft3Aibh.cjs → CarouselWidget-BETNqs_K.cjs} +3 -3
- package/dist/{CarouselWidget-fft3Aibh.cjs.map → CarouselWidget-BETNqs_K.cjs.map} +1 -1
- package/dist/{CarouselWidget-COavwXvx.mjs → CarouselWidget-CU1oj0q4.mjs} +3 -3
- package/dist/{CarouselWidget-COavwXvx.mjs.map → CarouselWidget-CU1oj0q4.mjs.map} +1 -1
- package/dist/{CarouselWidget-DDKKVEan.cjs → CarouselWidget-D3BODbpO.cjs} +3 -3
- package/dist/{CatchUpWidget-C3HdLA5t.mjs → CatchUpWidget-BtwG2-JD.mjs} +2 -2
- package/dist/{CatchUpWidget-BNEEQNcr.mjs → CatchUpWidget-CLwHp30X.mjs} +5 -4
- package/dist/CatchUpWidget-CLwHp30X.mjs.map +1 -0
- package/dist/{CatchUpWidget-G4txAfv3.cjs → CatchUpWidget-CqAHpCJP.cjs} +5 -4
- package/dist/CatchUpWidget-CqAHpCJP.cjs.map +1 -0
- package/dist/{CatchUpWidget-g9GtsvCy.cjs → CatchUpWidget-ak-oFdKY.cjs} +2 -2
- package/dist/{FluidProvider-DxwbLpfX.mjs → FluidProvider-BRgNyDk3.mjs} +3 -3
- package/dist/{FluidProvider-DxwbLpfX.mjs.map → FluidProvider-BRgNyDk3.mjs.map} +1 -1
- package/dist/{FluidProvider-B7bYw1AY.cjs → FluidProvider-XONLsJbE.cjs} +3 -3
- package/dist/{FluidProvider-B7bYw1AY.cjs.map → FluidProvider-XONLsJbE.cjs.map} +1 -1
- package/dist/{ImageWidget-B3IlWnKJ.cjs → ImageWidget-CksrA9rH.cjs} +2 -2
- package/dist/{ImageWidget-B3IlWnKJ.cjs.map → ImageWidget-CksrA9rH.cjs.map} +1 -1
- package/dist/{ImageWidget-DiWlWvKm.cjs → ImageWidget-DmO84AYo.cjs} +3 -3
- package/dist/{ImageWidget-CovwH8bp.mjs → ImageWidget-I8F34YlI.mjs} +2 -2
- package/dist/{ImageWidget-CovwH8bp.mjs.map → ImageWidget-I8F34YlI.mjs.map} +1 -1
- package/dist/{LinkWidget-StpP6gPq.mjs → LinkWidget-Dg0tHfgD.mjs} +2 -2
- package/dist/{LinkWidget-StpP6gPq.mjs.map → LinkWidget-Dg0tHfgD.mjs.map} +1 -1
- package/dist/{LinkWidget-BTIsGyc5.cjs → LinkWidget-Dvp6-Dow.cjs} +2 -2
- package/dist/{LinkWidget-BTIsGyc5.cjs.map → LinkWidget-Dvp6-Dow.cjs.map} +1 -1
- package/dist/{LinkWidget-ZVFlL178.cjs → LinkWidget-N6yz6w-U.cjs} +3 -3
- package/dist/{ListWidget-ddhJLwxZ.mjs → ListWidget-BAS3SGpD.mjs} +3 -3
- package/dist/{ListWidget-ddhJLwxZ.mjs.map → ListWidget-BAS3SGpD.mjs.map} +1 -1
- package/dist/{ListWidget-6TkLyp00.cjs → ListWidget-CH-zz3sb.cjs} +3 -3
- package/dist/{ListWidget-6TkLyp00.cjs.map → ListWidget-CH-zz3sb.cjs.map} +1 -1
- package/dist/{ListWidget-BNAED5xZ.cjs → ListWidget-vG31UVVS.cjs} +3 -3
- package/dist/{MessagingScreen-CZje-9-C.mjs → MessagingScreen-B8--eInM.mjs} +19 -19
- package/dist/{MessagingScreen-DDVflwCT.cjs → MessagingScreen-D2oC8zGg.cjs} +19 -19
- package/dist/{MessagingScreen-Q0BSmRuJ.mjs → MessagingScreen-DYjU5smt.mjs} +2 -2
- package/dist/{MessagingScreen-Q0BSmRuJ.mjs.map → MessagingScreen-DYjU5smt.mjs.map} +1 -1
- package/dist/{MessagingScreen-D7Yuy_Rn.cjs → MessagingScreen-gPqbIcFd.cjs} +2 -2
- package/dist/{MessagingScreen-D7Yuy_Rn.cjs.map → MessagingScreen-gPqbIcFd.cjs.map} +1 -1
- package/dist/{NestedWidget-orTqukJD.cjs → NestedWidget-B24yMkV4.cjs} +2 -2
- package/dist/{NestedWidget-orTqukJD.cjs.map → NestedWidget-B24yMkV4.cjs.map} +1 -1
- package/dist/{NestedWidget-DMhmCZT6.mjs → NestedWidget-D31rZhTR.mjs} +2 -2
- package/dist/{NestedWidget-DMhmCZT6.mjs.map → NestedWidget-D31rZhTR.mjs.map} +1 -1
- package/dist/{NestedWidget-BZZqukZw.cjs → NestedWidget-DqM5HGFl.cjs} +2 -2
- package/dist/{OrdersScreen-vAO6E8EM.cjs → OrdersScreen-BdLqCq1V.cjs} +19 -19
- package/dist/{OrdersScreen-CXxGN6RY.mjs → OrdersScreen-CHSqar4N.mjs} +3 -3
- package/dist/{OrdersScreen-CXxGN6RY.mjs.map → OrdersScreen-CHSqar4N.mjs.map} +1 -1
- package/dist/{OrdersScreen-DOBWnf_w.mjs → OrdersScreen-DRcdhc7y.mjs} +19 -19
- package/dist/{OrdersScreen-C0TzmZCl.cjs → OrdersScreen-Xs90dlpC.cjs} +3 -3
- package/dist/{OrdersScreen-C0TzmZCl.cjs.map → OrdersScreen-Xs90dlpC.cjs.map} +1 -1
- package/dist/{ProfileScreen-B9NHS4A_.cjs → ProfileScreen-BeaM28Qw.cjs} +20 -20
- package/dist/{ProfileScreen-DWFXnUUU.mjs → ProfileScreen-C-YfTr7l.mjs} +20 -20
- package/dist/{ProfileScreen-BGLpyuef.mjs → ProfileScreen-CpoLji_m.mjs} +3 -3
- package/dist/{ProfileScreen-BGLpyuef.mjs.map → ProfileScreen-CpoLji_m.mjs.map} +1 -1
- package/dist/{ProfileScreen-tq1DJ71b.cjs → ProfileScreen-DdCPrQUL.cjs} +3 -3
- package/dist/{ProfileScreen-tq1DJ71b.cjs.map → ProfileScreen-DdCPrQUL.cjs.map} +1 -1
- package/dist/{QuickLinksWidget-DIkDVuvE.mjs → QuickLinksWidget-BiEUvm_J.mjs} +2 -2
- package/dist/{QuickLinksWidget-DIkDVuvE.mjs.map → QuickLinksWidget-BiEUvm_J.mjs.map} +1 -1
- package/dist/{QuickLinksWidget-DDFtIKqs.cjs → QuickLinksWidget-CLBvaogv.cjs} +2 -2
- package/dist/{QuickLinksWidget-DDFtIKqs.cjs.map → QuickLinksWidget-CLBvaogv.cjs.map} +1 -1
- package/dist/{QuickLinksWidget-HvMQHOkz.cjs → QuickLinksWidget-DzAL_wuz.cjs} +3 -3
- package/dist/{QuickShareWidget-BNf1Un61.cjs → QuickShareWidget-B7mlqgW8.cjs} +2 -2
- package/dist/{QuickShareWidget-lysi8Dhh.mjs → QuickShareWidget-Dzpzis1d.mjs} +2 -2
- package/dist/{QuickShareWidget-lysi8Dhh.mjs.map → QuickShareWidget-Dzpzis1d.mjs.map} +1 -1
- package/dist/{QuickShareWidget-mxiy1zdd.cjs → QuickShareWidget-wxRcBUkf.cjs} +2 -2
- package/dist/{QuickShareWidget-mxiy1zdd.cjs.map → QuickShareWidget-wxRcBUkf.cjs.map} +1 -1
- package/dist/{RecentActivityWidget-DGtKC_9F.mjs → RecentActivityWidget-BzmV4G8e.mjs} +2 -2
- package/dist/{RecentActivityWidget-DGtKC_9F.mjs.map → RecentActivityWidget-BzmV4G8e.mjs.map} +1 -1
- package/dist/{RecentActivityWidget-DcgOzvHx.cjs → RecentActivityWidget-DQcy41-I.cjs} +2 -2
- package/dist/{RecentActivityWidget-Cr1G82Cd.cjs → RecentActivityWidget-zBOY0c_B.cjs} +2 -2
- package/dist/{RecentActivityWidget-Cr1G82Cd.cjs.map → RecentActivityWidget-zBOY0c_B.cjs.map} +1 -1
- package/dist/{ShopWidget-C5hjVwsY.cjs → ShopWidget-CCITGTSs.cjs} +2 -2
- package/dist/{ShopWidget-C5hjVwsY.cjs.map → ShopWidget-CCITGTSs.cjs.map} +1 -1
- package/dist/{ShopWidget-BYQKWyri.mjs → ShopWidget-D98tmUgG.mjs} +2 -2
- package/dist/{ShopWidget-B80w-Drk.mjs → ShopWidget-DHM86LM3.mjs} +2 -2
- package/dist/{ShopWidget-B80w-Drk.mjs.map → ShopWidget-DHM86LM3.mjs.map} +1 -1
- package/dist/{ShopWidget-CoOK6Ctd.cjs → ShopWidget-DXVsF1Jh.cjs} +2 -2
- package/dist/{SubscriptionsScreen-BwcpTAn5.mjs → SubscriptionsScreen-BOM_1txu.mjs} +20 -20
- package/dist/{SubscriptionsScreen-BEzmLp01.cjs → SubscriptionsScreen-DGIEnvLM.cjs} +20 -20
- package/dist/{SubscriptionsScreen-Cy7RtO5K.cjs → SubscriptionsScreen-DzBPA9H8.cjs} +4 -4
- package/dist/{SubscriptionsScreen-Cy7RtO5K.cjs.map → SubscriptionsScreen-DzBPA9H8.cjs.map} +1 -1
- package/dist/{SubscriptionsScreen-C7OOGlBt.mjs → SubscriptionsScreen-cAKZwpIu.mjs} +4 -4
- package/dist/{SubscriptionsScreen-C7OOGlBt.mjs.map → SubscriptionsScreen-cAKZwpIu.mjs.map} +1 -1
- package/dist/{TableWidget-lXrvBX6U.cjs → TableWidget-B8F_uQ92.cjs} +2 -2
- package/dist/{TableWidget-Bxwn3QYs.mjs → TableWidget-CDGdl71J.mjs} +2 -2
- package/dist/{TableWidget-Bxwn3QYs.mjs.map → TableWidget-CDGdl71J.mjs.map} +1 -1
- package/dist/{TableWidget-uoeYCPzf.cjs → TableWidget-DNjUA2Il.cjs} +2 -2
- package/dist/{TableWidget-uoeYCPzf.cjs.map → TableWidget-DNjUA2Il.cjs.map} +1 -1
- package/dist/{VideoWidget-D8FAjnvj.cjs → VideoWidget-UQQoRmwC.cjs} +3 -3
- package/dist/{VideoWidget-DQzYOcFF.mjs → VideoWidget-Wca2-So_.mjs} +2 -2
- package/dist/{VideoWidget-DQzYOcFF.mjs.map → VideoWidget-Wca2-So_.mjs.map} +1 -1
- package/dist/{VideoWidget-BhN4oTGG.cjs → VideoWidget-bf-gkCfx.cjs} +2 -2
- package/dist/{VideoWidget-BhN4oTGG.cjs.map → VideoWidget-bf-gkCfx.cjs.map} +1 -1
- package/dist/{WidgetInteractionContext-Bjy4P_YP.cjs → WidgetInteractionContext-D9dBJ-39.cjs} +5 -3
- package/dist/WidgetInteractionContext-D9dBJ-39.cjs.map +1 -0
- package/dist/{WidgetInteractionContext-Ba_G5sDP.mjs → WidgetInteractionContext-IsMo73Ka.mjs} +5 -3
- package/dist/WidgetInteractionContext-IsMo73Ka.mjs.map +1 -0
- package/dist/{build-widget-registry-DAOb4Muk.mjs → build-widget-registry-70KBtqiR.mjs} +27 -27
- package/dist/{build-widget-registry-DAOb4Muk.mjs.map → build-widget-registry-70KBtqiR.mjs.map} +1 -1
- package/dist/{build-widget-registry-BZeMOKCj.cjs → build-widget-registry-CWQ0YvEy.cjs} +27 -27
- package/dist/{build-widget-registry-BZeMOKCj.cjs.map → build-widget-registry-CWQ0YvEy.cjs.map} +1 -1
- package/dist/index.cjs +55 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +55 -34
- package/dist/index.mjs.map +1 -1
- package/dist/styles/remote-widget-shadow.css +2 -2
- package/dist/{use-internal-link-WJcYzKsi.mjs → use-internal-link-CKMpGvvs.mjs} +2 -2
- package/dist/{use-internal-link-WJcYzKsi.mjs.map → use-internal-link-CKMpGvvs.mjs.map} +1 -1
- package/dist/{use-internal-link-BTIkbJYg.cjs → use-internal-link-IcDHjPGp.cjs} +2 -2
- package/dist/{use-internal-link-BTIkbJYg.cjs.map → use-internal-link-IcDHjPGp.cjs.map} +1 -1
- package/dist/{use-link-action-C32zrsI0.mjs → use-link-action-CtVnC_2G.mjs} +2 -2
- package/dist/{use-link-action-C32zrsI0.mjs.map → use-link-action-CtVnC_2G.mjs.map} +1 -1
- package/dist/{use-link-action-rBy0j6go.cjs → use-link-action-DzA9AQsE.cjs} +2 -2
- package/dist/{use-link-action-rBy0j6go.cjs.map → use-link-action-DzA9AQsE.cjs.map} +1 -1
- package/dist/{use-portal-card-add-3ds-flow-DhfOxgMV.mjs → use-portal-card-add-3ds-flow-BMHRmeuB.mjs} +3 -3
- package/dist/{use-portal-card-add-3ds-flow-DhfOxgMV.mjs.map → use-portal-card-add-3ds-flow-BMHRmeuB.mjs.map} +1 -1
- package/dist/{use-portal-card-add-3ds-flow-DUBFzvAB.cjs → use-portal-card-add-3ds-flow-BsSH1Xks.cjs} +3 -3
- package/dist/{use-portal-card-add-3ds-flow-DUBFzvAB.cjs.map → use-portal-card-add-3ds-flow-BsSH1Xks.cjs.map} +1 -1
- package/dist/utils/build-remote-widget-plugin-manifests.cjs +17 -17
- package/dist/utils/build-remote-widget-plugin-manifests.mjs +17 -17
- package/dist/utils/build-widget-registry.cjs +17 -17
- package/dist/utils/build-widget-registry.mjs +17 -17
- package/package.json +19 -19
- package/dist/CatchUpWidget-BNEEQNcr.mjs.map +0 -1
- package/dist/CatchUpWidget-G4txAfv3.cjs.map +0 -1
- package/dist/WidgetInteractionContext-Ba_G5sDP.mjs.map +0 -1
- package/dist/WidgetInteractionContext-Bjy4P_YP.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CarouselWidget-COavwXvx.mjs","names":["DOMPurify"],"sources":["../../widgets/src/widgets/CarouselWidget.tsx"],"sourcesContent":["import type { ComponentProps, CSSProperties } from \"react\";\nimport type React from \"react\";\nimport { useEffect, useState, useCallback } from \"react\";\nimport DOMPurify from \"dompurify\";\nimport {\n MediaRenderer,\n getMediaPropsFromWidgetSchema,\n} from \"../components/MediaRenderer\";\nimport type {\n WidgetSchema,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n AlignOptions,\n FontSizeOptions,\n PaddingOptions,\n ButtonSizeOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n borderColorClasses,\n borderWidthClasses,\n getBorderColorField,\n getBorderRadiusField,\n getBorderWidthField,\n getButtonSizeField,\n getColorField,\n getFontSizeField,\n getHeightField,\n getPaddingField,\n} from \"../core/fields\";\nimport { useWidgetInteraction } from \"../contexts/WidgetInteractionContext\";\nimport { useRepUser } from \"../contexts/RepUserContext\";\nimport { useWidgetRenderState } from \"../contexts/WidgetRenderStateContext\";\nimport { useInternalLinks } from \"../hooks/use-internal-link\";\nimport { buildItemInteractionProps } from \"./list-widget/helpers\";\nimport { getLinkTypeFields } from \"../lib/link-type-schema-fields\";\n\ntype CarouselSlide = {\n id: string;\n content: WidgetSchema;\n title?: string;\n description?: string;\n // Editorial additions (all optional — existing configs keep working)\n eyebrow?: string;\n meta?: string;\n tag?: string;\n tagColor?: ColorOptions;\n buttonEnabled?: boolean;\n buttonText?: string;\n buttonVariant?:\n | \"default\"\n | \"secondary\"\n | \"outline\"\n | \"destructive\"\n | \"ghost\"\n | \"link\";\n buttonLink?: string;\n secondaryButtonText?: string;\n secondaryButtonLink?: string;\n // Index signature lets data-source-driven slides carry extra fields\n // (e.g. shareable_type, subscription_token) through to onItemClick,\n // which routes based on them — matches ListWidget's ListItem shape.\n [key: string]: unknown;\n};\n\ntype CarouselWidgetProps = ComponentProps<\"div\"> & {\n slides?: CarouselSlide[];\n autoScrollInterval?: number;\n enableAutoScroll?: boolean;\n\n // Layout\n align?: AlignOptions;\n // CSS-unit string (\"400px\", \"30rem\", \"50%\") — kept as string so saved\n // configs from the previous schema continue to render. The schema field\n // is `cssUnit` (via getHeightField), which produces a string with the\n // unit baked in.\n carouselHeight?: string;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n padding?: PaddingOptions;\n\n // Text Styling\n headerSize?: FontSizeOptions;\n headerColor?: ColorOptions;\n textSize?: FontSizeOptions;\n textColor?: ColorOptions;\n textWidth?: string;\n\n // Button\n showButton?: boolean;\n buttonColor?: ColorOptions;\n buttonSize?: ButtonSizeOptions;\n\n // Overlay\n overlayEnabled?: boolean;\n overlayType?: \"solid\" | \"gradient\";\n overlayIntensity?: number;\n\n // Editorial frame (new — opt-out via `editorialFrame={false}`)\n editorialFrame?: boolean;\n frameColor?: ColorOptions;\n};\n\nconst VIDEO_CONTROLS_HEIGHT = 44;\n\nconst HEADER_SIZE_MAP: Record<FontSizeOptions, string> = {\n xs: \"text-base sm:text-lg\",\n sm: \"text-lg sm:text-xl\",\n md: \"text-xl sm:text-2xl\",\n lg: \"text-2xl sm:text-3xl\",\n xl: \"text-3xl sm:text-[32px]\",\n \"2xl\": \"text-[32px] sm:text-[40px]\",\n};\n\nconst BODY_SIZE_MAP: Record<FontSizeOptions, string> = {\n xs: \"text-[12px]\",\n sm: \"text-[13px]\",\n md: \"text-[14px]\",\n lg: \"text-[15px]\",\n xl: \"text-[16px]\",\n \"2xl\": \"text-[18px]\",\n};\n\nfunction useCarouselController({\n slideCount,\n enableAutoScroll,\n autoScrollInterval,\n}: {\n slideCount: number;\n enableAutoScroll: boolean;\n autoScrollInterval: number;\n}) {\n const [currentIndex, setCurrentIndex] = useState(0);\n // Track hover and keyboard focus separately — combining them in one boolean\n // would let `onMouseLeave` clear the paused state even while a child button\n // still has focus.\n const [isHovered, setIsHovered] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const hasSlides = slideCount > 0;\n const isPaused = isHovered || isFocused;\n\n const goToNext = useCallback(() => {\n if (!hasSlides) return;\n setCurrentIndex((prev) => (prev === slideCount - 1 ? 0 : prev + 1));\n }, [hasSlides, slideCount]);\n\n const goToPrevious = useCallback(() => {\n if (!hasSlides) return;\n setCurrentIndex((prev) => (prev === 0 ? slideCount - 1 : prev - 1));\n }, [hasSlides, slideCount]);\n\n const goToSlide = useCallback(\n (index: number) => {\n if (!hasSlides) return;\n if (index >= 0 && index < slideCount) {\n setCurrentIndex(index);\n }\n },\n [hasSlides, slideCount],\n );\n\n useEffect(() => {\n if (!enableAutoScroll || !hasSlides || isPaused) return;\n const intervalId = setInterval(goToNext, autoScrollInterval);\n return () => clearInterval(intervalId);\n }, [enableAutoScroll, autoScrollInterval, hasSlides, isPaused, goToNext]);\n\n useEffect(() => {\n if (currentIndex >= slideCount && slideCount > 0) {\n setCurrentIndex(0);\n }\n }, [slideCount, currentIndex]);\n\n // Pause for a mouse or the keyboard, never for a touch. On iOS a tap fires\n // mouseenter with no mouseleave after it, and it focuses the button too, so\n // either one would leave the carousel stuck paused.\n const handlePointerEnter = useCallback((e: React.PointerEvent) => {\n if (e.pointerType === \"mouse\") setIsHovered(true);\n }, []);\n const handlePointerLeave = useCallback((e: React.PointerEvent) => {\n if (e.pointerType === \"mouse\") setIsHovered(false);\n }, []);\n const handleFocus = useCallback((e: React.FocusEvent<HTMLElement>) => {\n // Set it either way, or a click inside leaves it paused.\n setIsFocused(\n e.target instanceof Element && e.target.matches(\":focus-visible\"),\n );\n }, []);\n const handleBlur = useCallback((e: React.FocusEvent<HTMLElement>) => {\n // Only flip to unfocused when focus actually leaves the carousel.\n // When focus moves between two children (e.g. tabbing from prev to next\n // button), `relatedTarget` is the new focus target and is still contained.\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {\n setIsFocused(false);\n }\n }, []);\n\n return {\n currentIndex,\n hasSlides,\n isPaused,\n goToNext,\n goToPrevious,\n goToSlide,\n handlePointerEnter,\n handlePointerLeave,\n handleFocus,\n handleBlur,\n };\n}\n\nexport function CarouselWidget({\n slides = [],\n autoScrollInterval = 6500,\n enableAutoScroll = false,\n carouselHeight = \"400px\",\n align = { vertical: \"bottom\", horizontal: \"left\" },\n overlayIntensity = 70,\n borderRadius = \"xl\",\n borderWidth = \"none\",\n borderColor = \"muted\",\n padding = 8,\n textWidth = \"620px\",\n headerSize = \"xl\",\n headerColor = \"background\",\n textSize = \"md\",\n textColor = \"background\",\n showButton = true,\n buttonColor = \"background\",\n buttonSize = \"default\",\n overlayEnabled = true,\n overlayType = \"gradient\",\n editorialFrame = true,\n frameColor = \"foreground\",\n className,\n ...props\n}: CarouselWidgetProps): React.JSX.Element {\n const totalSlides = slides.length;\n const {\n currentIndex,\n hasSlides,\n isPaused,\n goToNext,\n goToPrevious,\n goToSlide,\n handlePointerEnter,\n handlePointerLeave,\n handleFocus,\n handleBlur,\n } = useCarouselController({\n slideCount: totalSlides,\n enableAutoScroll,\n autoScrollInterval,\n });\n const { onItemClick, onNavigate, buildHref, resolveInternalSlug } =\n useWidgetInteraction();\n const resolveInternalLink = useInternalLinks();\n const publicId = useRepUser()?.publicId;\n const { editMode } = useWidgetRenderState();\n const verticalClass =\n align.vertical === \"top\"\n ? \"items-start\"\n : align.vertical === \"center\"\n ? \"items-center\"\n : \"items-end\";\n\n const horizontalClass =\n align.horizontal === \"center\"\n ? \"justify-center text-center mx-auto\"\n : align.horizontal === \"right\"\n ? \"justify-end ml-auto text-right\"\n : \"justify-start text-left\";\n\n // Themed dual-shadow (crisp + soft ambient) on the editorial frame —\n // derived from the foreground token so the lift reads in both light\n // and dark themes.\n const frameShadowStyle: CSSProperties | undefined = editorialFrame\n ? {\n boxShadow: `0 1px 2px color-mix(in oklch, var(--color-foreground) 4%, transparent), 0 30px 60px -30px color-mix(in oklch, var(--color-foreground) 40%, transparent)`,\n }\n : undefined;\n\n const frameBg = editorialFrame ? `bg-${frameColor}` : \"\";\n const isActiveSlideVideo =\n slides[currentIndex]?.content.type === \"VideoWidget\";\n\n if (!hasSlides) {\n return (\n <div\n className={`relative w-full overflow-hidden rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${frameBg} ${className ?? \"\"}`}\n style={{ minHeight: carouselHeight, ...frameShadowStyle }}\n {...props}\n >\n <div\n className=\"bg-muted/20 flex h-full w-full items-center justify-center\"\n style={{ minHeight: carouselHeight }}\n >\n <div className=\"text-muted-foreground flex flex-col items-center gap-2\">\n <div className=\"text-4xl\">🎠</div>\n <p className=\"text-sm\">No slides added</p>\n <p className=\"text-muted-foreground/70 text-xs\">\n Add slides to create a carousel\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n className={`relative isolate w-full overflow-hidden rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${frameBg} ${className ?? \"\"}`}\n style={{ minHeight: carouselHeight, ...frameShadowStyle }}\n onPointerEnter={handlePointerEnter}\n onPointerLeave={handlePointerLeave}\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n >\n <div\n className=\"relative w-full\"\n style={{\n minHeight: carouselHeight,\n height: carouselHeight,\n }}\n >\n {slides.map((slide, index) => {\n const isActive = index === currentIndex;\n const isVideoSlide = slide.content.type === \"VideoWidget\";\n // Strip the optional \"%\" suffix, then parse. Use Number.isFinite +\n // clamp so an explicit 0 isn't silently replaced by the 70 fallback\n // (the previous `|| 70` treated 0 as falsy).\n const parsedOverlayIntensity = Number(\n String(overlayIntensity).replace(\"%\", \"\"),\n );\n const overlayOpacity =\n (Number.isFinite(parsedOverlayIntensity)\n ? Math.min(100, Math.max(0, parsedOverlayIntensity))\n : 70) / 100;\n\n // Slide-level tap: if the slide has an explicit linkType from\n // itemConfigSchema, use it; otherwise fall back to onItemClick\n // (which routes based on shareable_type, subscription_token, etc.).\n const slideLinkProps =\n isActive && !isVideoSlide\n ? buildItemInteractionProps(slide, {\n onItemClick,\n onNavigate,\n buildHref,\n resolveInternalSlug,\n publicId,\n editMode,\n })\n : {};\n const isInteractive = Object.keys(slideLinkProps).length > 0;\n\n return (\n <div\n key={slide.id}\n className={`absolute inset-0 transition-opacity duration-700 ease-out${isInteractive ? \" cursor-pointer focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:outline-none focus-visible:ring-inset\" : \"\"}`}\n style={{\n opacity: isActive ? 1 : 0,\n pointerEvents: isActive ? \"auto\" : \"none\",\n }}\n aria-hidden={!isActive}\n {...slideLinkProps}\n >\n {isVideoSlide ? (\n <div className=\"absolute inset-0 h-full w-full\">\n {isActive && (\n <MediaRenderer\n {...getMediaPropsFromWidgetSchema(slide.content)}\n />\n )}\n </div>\n ) : (\n <>\n <div\n className=\"absolute inset-0 h-full w-full\"\n style={{\n transform: isActive ? \"scale(1)\" : \"scale(1.04)\",\n animation: isActive\n ? \"fluidCarouselKenBurns 900ms ease-out both\"\n : undefined,\n }}\n >\n <MediaRenderer\n {...getMediaPropsFromWidgetSchema(slide.content)}\n />\n </div>\n\n {overlayEnabled && editorialFrame && (\n <>\n <div\n className={`pointer-events-none absolute inset-0 bg-gradient-to-t from-${frameColor} via-${frameColor}/65 to-${frameColor}/10`}\n style={{ opacity: overlayOpacity }}\n />\n <div\n className={`pointer-events-none absolute inset-0 bg-gradient-to-r from-${frameColor}/80 via-transparent to-transparent`}\n style={{ opacity: Math.min(1, overlayOpacity + 0.1) }}\n />\n </>\n )}\n\n {overlayEnabled && !editorialFrame && (\n <div\n className={`pointer-events-none absolute inset-0 ${\n overlayType === \"gradient\"\n ? \"bg-gradient-to-t from-black to-transparent\"\n : \"bg-black\"\n }`}\n style={{ opacity: overlayOpacity }}\n />\n )}\n </>\n )}\n\n <div\n className={`pointer-events-none absolute inset-0 flex px-6 sm:px-10 ${verticalClass} ${\n align.horizontal === \"center\"\n ? \"justify-center\"\n : align.horizontal === \"right\"\n ? \"justify-end\"\n : \"justify-start\"\n }`}\n style={{\n paddingTop: padding * 4,\n paddingBottom:\n padding * 4 +\n (totalSlides > 1\n ? 24 + (isVideoSlide ? VIDEO_CONTROLS_HEIGHT : 0)\n : 0),\n }}\n >\n <div\n key={`content-${currentIndex}`}\n className={`${isActive && !isVideoSlide ? \"pointer-events-auto\" : \"pointer-events-none\"} flex w-full flex-col gap-3 ${horizontalClass}`}\n style={{\n maxWidth: textWidth,\n animation: isActive\n ? \"fluidCarouselTextRise 450ms ease-out both\"\n : undefined,\n animationDelay: isActive ? \"120ms\" : undefined,\n }}\n >\n {(slide.eyebrow || slide.tag || slide.meta) && (\n <div\n className={`flex flex-wrap items-center gap-2 ${\n align.horizontal === \"center\"\n ? \"justify-center\"\n : align.horizontal === \"right\"\n ? \"justify-end\"\n : \"justify-start\"\n }`}\n >\n {slide.tag && (\n <span\n className={`rounded-full bg-${slide.tagColor ?? \"background\"} text-${slide.tagColor ?? \"background\"}-foreground px-2.5 py-1 text-[10px] font-bold tracking-[0.14em] uppercase`}\n style={\n slide.tagColor === \"background\" ||\n slide.tagColor === \"foreground\" ||\n !slide.tagColor\n ? tagFallbackStyle(slide.tagColor, frameColor)\n : undefined\n }\n >\n {slide.tag}\n </span>\n )}\n {slide.eyebrow && (\n <span\n className={`text-${headerColor}/70 text-[10px] font-bold tracking-[0.16em] uppercase`}\n >\n {slide.eyebrow}\n </span>\n )}\n {slide.meta && (\n <span\n className={`text-${headerColor}/60 text-[10px] font-bold tracking-[0.16em] uppercase`}\n >\n {slide.meta}\n </span>\n )}\n </div>\n )}\n\n {slide.title && (\n <h2\n className={`text-${headerColor} font-bold ${HEADER_SIZE_MAP[headerSize]} leading-[1.12] tracking-[-0.018em]`}\n >\n {slide.title}\n </h2>\n )}\n\n {slide.description && (\n // Description supports inline HTML for emphasis / line\n // breaks / lists. Sanitized via DOMPurify with the same\n // allowlist as the previous implementation so saved\n // slides containing <strong>, <em>, <br>, <ul>, etc.\n // continue rendering as authored. Element is a <div>\n // (not <p>) to permit block-level allowed tags.\n <div\n className={`text-${textColor}/70 ${BODY_SIZE_MAP[textSize]} leading-[1.55]`}\n dangerouslySetInnerHTML={{\n __html: DOMPurify.sanitize(slide.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\n {(() => {\n const showPrimary =\n slide.buttonEnabled &&\n showButton &&\n Boolean(slide.buttonText) &&\n Boolean(slide.buttonLink);\n const showSecondary =\n Boolean(slide.secondaryButtonText) &&\n Boolean(slide.secondaryButtonLink);\n if (!showPrimary && !showSecondary) return null;\n\n const primaryLink = resolveInternalLink(slide.buttonLink);\n const secondaryLink = resolveInternalLink(\n slide.secondaryButtonLink,\n );\n\n return (\n <div\n className={`mt-3 flex flex-wrap items-center gap-3 ${\n align.horizontal === \"center\"\n ? \"justify-center\"\n : align.horizontal === \"right\"\n ? \"justify-end\"\n : \"justify-start\"\n }`}\n >\n {showPrimary && (\n <a\n href={slide.buttonLink}\n onClick={(e) => {\n e.stopPropagation();\n primaryLink.followInternally(e);\n }}\n className={`inline-flex items-center gap-1.5 rounded-full bg-${buttonColor} text-${buttonColor}-foreground px-5 py-2.5 text-[13px] font-bold transition-transform hover:scale-[1.02] ${buttonSizeClass(buttonSize)}`}\n style={{\n boxShadow: `0 8px 22px -8px color-mix(in oklch, var(--color-foreground) 35%, transparent)`,\n }}\n >\n {slide.buttonText}\n <span aria-hidden=\"true\" className=\"text-[14px]\">\n →\n </span>\n </a>\n )}\n {showSecondary && (\n <a\n href={slide.secondaryButtonLink}\n onClick={(e) => {\n e.stopPropagation();\n secondaryLink.followInternally(e);\n }}\n className={`inline-flex items-center gap-1 text-${headerColor}/80 hover:text-${headerColor} text-[12px] font-bold transition-colors`}\n >\n {slide.secondaryButtonText}\n <span aria-hidden=\"true\">→</span>\n </a>\n )}\n </div>\n );\n })()}\n </div>\n </div>\n </div>\n );\n })}\n\n {totalSlides > 1 && (\n <div className=\"pointer-events-none absolute inset-0 z-20\">\n <div className=\"pointer-events-auto absolute top-6 right-6 flex items-center gap-1.5\">\n <button\n type=\"button\"\n onClick={goToPrevious}\n aria-label=\"Previous slide\"\n className={`flex size-10 items-center justify-center rounded-full bg-${headerColor}/10 text-${headerColor} ring-1 ring-${headerColor}/15 backdrop-blur-sm transition-colors hover:bg-${headerColor}/20`}\n >\n <ChevronLeftIcon />\n </button>\n <button\n type=\"button\"\n onClick={goToNext}\n aria-label=\"Next slide\"\n className={`flex size-10 items-center justify-center rounded-full bg-${headerColor} text-${headerColor}-foreground transition-transform hover:scale-[1.04]`}\n style={{\n // Themed lift shadow (was hardcoded white) so the active\n // nav button reads on both light and dark frames.\n boxShadow: `0 4px 14px -4px color-mix(in oklch, var(--color-${headerColor}) 45%, transparent)`,\n ...nextButtonFallbackStyle(headerColor, frameColor),\n }}\n >\n <ChevronRightIcon />\n </button>\n </div>\n\n <div\n className={`pointer-events-auto absolute inset-x-0 flex items-center gap-1 px-6 pb-2 sm:px-10`}\n style={{\n bottom: isActiveSlideVideo ? VIDEO_CONTROLS_HEIGHT : 0,\n transition: \"bottom 200ms ease-out\",\n }}\n >\n {slides.map((_, i) => {\n const state =\n i < currentIndex\n ? \"done\"\n : i === currentIndex\n ? \"active\"\n : \"upcoming\";\n return (\n <button\n key={`progress-${i}`}\n type=\"button\"\n onClick={() => goToSlide(i)}\n aria-label={`Go to slide ${i + 1}`}\n aria-current={state === \"active\" ? \"true\" : \"false\"}\n className=\"group flex-1 py-2\"\n >\n <span\n className={`block h-[2px] w-full overflow-hidden rounded-full bg-${headerColor}/20`}\n >\n <span\n key={`fill-${i}-${currentIndex}`}\n className={`block h-full rounded-full bg-${headerColor}`}\n style={progressFillStyle(\n state,\n enableAutoScroll,\n autoScrollInterval,\n isPaused,\n )}\n />\n </span>\n </button>\n );\n })}\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction progressFillStyle(\n state: \"done\" | \"active\" | \"upcoming\",\n autoScroll: boolean,\n duration: number,\n paused: boolean,\n): CSSProperties {\n if (state === \"upcoming\") return { width: \"0%\" };\n if (!autoScroll) {\n // Nothing is counting down, so only the current bar fills. Otherwise the\n // last slide has every bar full and you can't tell which one you're on.\n return { width: state === \"active\" ? \"100%\" : \"0%\" };\n }\n if (state === \"done\") return { width: \"100%\" };\n return {\n width: \"0%\",\n animation: `fluidCarouselProgressFill ${duration}ms linear forwards`,\n animationPlayState: paused ? \"paused\" : \"running\",\n };\n}\n\nfunction tagFallbackStyle(\n tagColor: ColorOptions | undefined,\n frameColor: ColorOptions,\n): CSSProperties | undefined {\n // bg-background/foreground don't emit -foreground utilities by default;\n // fall back to contrast-safe inline colors so tag chips always read.\n if (!tagColor || tagColor === \"background\") {\n return { color: `var(--color-${frameColor})` };\n }\n if (tagColor === \"foreground\") {\n return { color: `var(--color-background)` };\n }\n return undefined;\n}\n\nfunction nextButtonFallbackStyle(\n headerColor: ColorOptions,\n frameColor: ColorOptions,\n): CSSProperties | undefined {\n // Same contrast fix as tags — ensure icon color reads on the active button.\n if (headerColor === \"background\") {\n return { color: `var(--color-${frameColor})` };\n }\n if (headerColor === \"foreground\") {\n return { color: `var(--color-background)` };\n }\n return undefined;\n}\n\nfunction buttonSizeClass(size: ButtonSizeOptions): string {\n if (size === \"sm\") return \"px-4 py-1.5 text-[12px]\";\n if (size === \"lg\") return \"px-6 py-3 text-[14px]\";\n if (size === \"xl\") return \"px-7 py-3.5 text-[15px]\";\n return \"\";\n}\n\nfunction ChevronLeftIcon() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M15 18l-6-6 6-6\" />\n </svg>\n );\n}\n\nfunction ChevronRightIcon() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M9 18l6-6-6-6\" />\n </svg>\n );\n}\n\nexport const carouselWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"CarouselWidget\",\n displayName: \"Carousel\",\n tabsConfig: [\n { id: \"styling\", label: \"Styling\" },\n { id: \"behavior\", label: \"Behavior\" },\n { id: \"data\", label: \"Data\" },\n ],\n dataSourceTargetProps: [\"slides\"],\n fields: [\n // Styling tab — Frame group (new)\n {\n key: \"editorialFrame\",\n label: \"Editorial Frame\",\n type: \"boolean\",\n description:\n \"Premium dark canvas with layered shadow and gradient stack. Turn off for a classic transparent carousel.\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Frame\",\n },\n getColorField({\n defaultValue: \"foreground\",\n key: \"frameColor\",\n label: \"Frame Color\",\n description:\n \"Surface color of the carousel canvas. Drives the frame and gradient overlay.\",\n tab: \"styling\",\n group: \"Frame\",\n requiresKeyToBeTrue: \"editorialFrame\",\n }),\n\n // Text Styling group\n getFontSizeField({\n defaultValue: \"xl\",\n key: \"headerSize\",\n label: \"Header Font Size\",\n description: \"Font size for the slide header\",\n tab: \"styling\",\n group: \"Text Styling\",\n }),\n getColorField({\n defaultValue: \"background\",\n key: \"headerColor\",\n label: \"Header Color\",\n description:\n \"Headline color. Also drives eyebrow, controls and progress bar.\",\n tab: \"styling\",\n group: \"Text Styling\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Text Styling\",\n },\n getFontSizeField({\n defaultValue: \"md\",\n key: \"textSize\",\n label: \"Text Font Size\",\n description: \"Font size for the slide text\",\n tab: \"styling\",\n group: \"Text Styling\",\n }),\n getColorField({\n defaultValue: \"background\",\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Color variant for the slide text\",\n tab: \"styling\",\n group: \"Text Styling\",\n }),\n\n // Layout group\n {\n key: \"align\",\n label: \"Alignment\",\n type: \"alignment\",\n description: \"Alignment of the carousel content\",\n defaultValue: { vertical: \"bottom\", horizontal: \"left\" },\n options: {\n verticalEnabled: true,\n horizontalEnabled: true,\n },\n tab: \"styling\",\n group: \"Layout\",\n },\n {\n key: \"textWidth\",\n label: \"Content Width\",\n type: \"cssUnit\",\n description: \"Maximum width for slide content\",\n defaultValue: \"620px\",\n allowedUnits: [\"px\", \"rem\", \"%\"],\n defaultUnit: \"px\",\n minByUnit: { px: 50, rem: 3, \"%\": 10 },\n maxByUnit: { px: 1200, rem: 75, \"%\": 100 },\n stepByUnit: { px: 10, rem: 1, \"%\": 1 },\n tab: \"styling\",\n group: \"Layout\",\n },\n\n // Design group\n getHeightField({\n key: \"carouselHeight\",\n label: \"Carousel Height\",\n description: \"Height of the carousel\",\n defaultValue: \"400px\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"separator2\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Design\",\n },\n getBorderRadiusField({\n defaultValue: \"xl\",\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Rounded corners for the carousel\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Border width for the carousel container\",\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 carousel container\",\n defaultValue: \"muted\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getPaddingField({\n defaultValue: 8,\n key: \"padding\",\n label: \"Padding\",\n description: \"Inner padding around slide content\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"overlayEnabled\",\n label: \"Enable Overlay\",\n type: \"boolean\",\n description: \"Add background overlay to slide content\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Design\",\n },\n {\n key: \"overlayType\",\n label: \"Overlay Type\",\n type: \"buttonGroup\",\n description: \"Overlay style (only applies when editorial frame is off)\",\n defaultValue: \"gradient\",\n options: [\n { label: \"Solid\", value: \"solid\" },\n { label: \"Gradient\", value: \"gradient\" },\n ],\n tab: \"styling\",\n group: \"Design\",\n requiresKeyToBeTrue: \"overlayEnabled\",\n },\n {\n key: \"overlayIntensity\",\n label: \"Overlay Intensity\",\n type: \"slider\",\n description: \"Opacity of the overlay (0-100)\",\n min: 0,\n max: 100,\n step: 5,\n defaultValue: 70,\n unit: \"%\",\n tab: \"styling\",\n group: \"Design\",\n requiresKeyToBeTrue: \"overlayEnabled\",\n },\n {\n key: \"showButton\",\n label: \"Show Primary Button\",\n type: \"boolean\",\n description:\n \"Global show/hide for all slide CTAs. Per-slide button content is set on each item under the Data tab (custom sources only).\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Design\",\n },\n getColorField({\n defaultValue: \"background\",\n key: \"buttonColor\",\n label: \"Button Color\",\n description: \"Color variant for the slide button\",\n tab: \"styling\",\n group: \"Design\",\n requiresKeyToBeTrue: \"showButton\",\n }),\n getButtonSizeField({\n defaultValue: \"default\",\n key: \"buttonSize\",\n label: \"Button Size\",\n description: \"Size of the slide button\",\n tab: \"styling\",\n group: \"Design\",\n requiresKeyToBeTrue: \"showButton\",\n }),\n\n // Behavior tab\n {\n key: \"enableAutoScroll\",\n label: \"Enable Auto-Scroll\",\n type: \"boolean\",\n description:\n \"Automatically advance to the next slide. Progress bar fills at this rate and pauses on hover.\",\n defaultValue: false,\n tab: \"behavior\",\n group: \"Auto-Scroll\",\n },\n {\n key: \"autoScrollInterval\",\n label: \"Auto-Scroll Interval (ms)\",\n type: \"number\",\n description: \"Time between automatic slide transitions\",\n min: 1000,\n max: 15000,\n step: 500,\n defaultValue: 6500,\n tab: \"behavior\",\n group: \"Auto-Scroll\",\n requiresKeyToBeTrue: \"enableAutoScroll\",\n },\n\n // Data tab\n {\n key: \"dataSource\",\n label: \"Data Source\",\n type: \"dataSource\",\n description: \"Configure the slides rendered by the carousel.\",\n targetProps: [\n {\n key: \"slides\",\n description:\n \"Slides rendered by the carousel, usually supplied by a data source.\",\n },\n ],\n tab: \"data\",\n group: \"Data Configuration\",\n },\n ],\n itemConfigSchema: {\n description: \"Configure settings for this slide\",\n fields: [\n {\n key: \"eyebrow\",\n label: \"Eyebrow\",\n type: \"text\",\n description:\n \"Short label above the title (e.g. 'From Fluid · Newsroom')\",\n },\n {\n key: \"tag\",\n label: \"Tag / Category\",\n type: \"text\",\n description: \"Short pill label (e.g. 'Policy', 'New release')\",\n },\n {\n key: \"tagColor\",\n label: \"Tag Color\",\n type: \"colorSelect\",\n description: \"Background color of the tag pill\",\n defaultValue: \"background\",\n },\n {\n key: \"meta\",\n label: \"Meta\",\n type: \"text\",\n description: \"Meta line (e.g. 'Apr 12 · 4 min read')\",\n },\n {\n key: \"separator_slide_1\",\n type: \"separator\",\n label: \"Separator\",\n },\n {\n key: \"title\",\n label: \"Title\",\n type: \"text\",\n description: \"Override the item's title for this slide\",\n },\n {\n key: \"description\",\n label: \"Description\",\n type: \"textarea\",\n description: \"Override the item's description for this slide\",\n rows: 3,\n },\n {\n key: \"separator_slide_2\",\n type: \"separator\",\n label: \"Separator\",\n },\n {\n key: \"buttonEnabled\",\n label: \"Show Primary Button\",\n type: \"boolean\",\n description: \"Show the primary CTA on this slide\",\n defaultValue: false,\n },\n {\n key: \"buttonText\",\n label: \"Button Text\",\n type: \"text\",\n description: \"Text for the primary button\",\n requiresKeyToBeTrue: \"buttonEnabled\",\n },\n {\n key: \"buttonLink\",\n label: \"Button Link\",\n type: \"text\",\n description: \"URL for the primary button\",\n requiresKeyToBeTrue: \"buttonEnabled\",\n },\n {\n key: \"secondaryButtonText\",\n label: \"Secondary Link Text\",\n type: \"text\",\n description:\n \"Optional text-only secondary link (renders with an arrow)\",\n },\n {\n key: \"secondaryButtonLink\",\n label: \"Secondary Link URL\",\n type: \"text\",\n description: \"URL for the secondary link\",\n },\n { key: \"separator_link\", type: \"separator\", label: \"Separator\" },\n ...getLinkTypeFields(),\n ],\n },\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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K;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,KAAK;GACL,MAAM;GACN,cAAc;GACd,MAAM;GACN,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,cAAc;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EACF,mBAAmB;GACjB,cAAc;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,KAAK;GACL,MAAM;GACN,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EAGD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,aAAa,CACX;IACE,KAAK;IACL,aACE;IACH,CACF;GACD,KAAK;GACL,OAAO;GACR;EACF;CACD,kBAAkB;EAChB,aAAa;EACb,QAAQ;GACN;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aACE;IACH;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,cAAc;IACf;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,MAAM;IACN,OAAO;IACR;GACD;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,MAAM;IACN,OAAO;IACR;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,cAAc;IACf;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,qBAAqB;IACtB;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,qBAAqB;IACtB;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aACE;IACH;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IAAE,KAAK;IAAkB,MAAM;IAAa,OAAO;IAAa;GAChE,GAAG,mBAAmB;GACvB;EACF;CACF"}
|
|
1
|
+
{"version":3,"file":"CarouselWidget-CU1oj0q4.mjs","names":["DOMPurify"],"sources":["../../widgets/src/widgets/CarouselWidget.tsx"],"sourcesContent":["import type { ComponentProps, CSSProperties } from \"react\";\nimport type React from \"react\";\nimport { useEffect, useState, useCallback } from \"react\";\nimport DOMPurify from \"dompurify\";\nimport {\n MediaRenderer,\n getMediaPropsFromWidgetSchema,\n} from \"../components/MediaRenderer\";\nimport type {\n WidgetSchema,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n AlignOptions,\n FontSizeOptions,\n PaddingOptions,\n ButtonSizeOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport {\n borderColorClasses,\n borderWidthClasses,\n getBorderColorField,\n getBorderRadiusField,\n getBorderWidthField,\n getButtonSizeField,\n getColorField,\n getFontSizeField,\n getHeightField,\n getPaddingField,\n} from \"../core/fields\";\nimport { useWidgetInteraction } from \"../contexts/WidgetInteractionContext\";\nimport { useRepUser } from \"../contexts/RepUserContext\";\nimport { useWidgetRenderState } from \"../contexts/WidgetRenderStateContext\";\nimport { useInternalLinks } from \"../hooks/use-internal-link\";\nimport { buildItemInteractionProps } from \"./list-widget/helpers\";\nimport { getLinkTypeFields } from \"../lib/link-type-schema-fields\";\n\ntype CarouselSlide = {\n id: string;\n content: WidgetSchema;\n title?: string;\n description?: string;\n // Editorial additions (all optional — existing configs keep working)\n eyebrow?: string;\n meta?: string;\n tag?: string;\n tagColor?: ColorOptions;\n buttonEnabled?: boolean;\n buttonText?: string;\n buttonVariant?:\n | \"default\"\n | \"secondary\"\n | \"outline\"\n | \"destructive\"\n | \"ghost\"\n | \"link\";\n buttonLink?: string;\n secondaryButtonText?: string;\n secondaryButtonLink?: string;\n // Index signature lets data-source-driven slides carry extra fields\n // (e.g. shareable_type, subscription_token) through to onItemClick,\n // which routes based on them — matches ListWidget's ListItem shape.\n [key: string]: unknown;\n};\n\ntype CarouselWidgetProps = ComponentProps<\"div\"> & {\n slides?: CarouselSlide[];\n autoScrollInterval?: number;\n enableAutoScroll?: boolean;\n\n // Layout\n align?: AlignOptions;\n // CSS-unit string (\"400px\", \"30rem\", \"50%\") — kept as string so saved\n // configs from the previous schema continue to render. The schema field\n // is `cssUnit` (via getHeightField), which produces a string with the\n // unit baked in.\n carouselHeight?: string;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n padding?: PaddingOptions;\n\n // Text Styling\n headerSize?: FontSizeOptions;\n headerColor?: ColorOptions;\n textSize?: FontSizeOptions;\n textColor?: ColorOptions;\n textWidth?: string;\n\n // Button\n showButton?: boolean;\n buttonColor?: ColorOptions;\n buttonSize?: ButtonSizeOptions;\n\n // Overlay\n overlayEnabled?: boolean;\n overlayType?: \"solid\" | \"gradient\";\n overlayIntensity?: number;\n\n // Editorial frame (new — opt-out via `editorialFrame={false}`)\n editorialFrame?: boolean;\n frameColor?: ColorOptions;\n};\n\nconst VIDEO_CONTROLS_HEIGHT = 44;\n\nconst HEADER_SIZE_MAP: Record<FontSizeOptions, string> = {\n xs: \"text-base sm:text-lg\",\n sm: \"text-lg sm:text-xl\",\n md: \"text-xl sm:text-2xl\",\n lg: \"text-2xl sm:text-3xl\",\n xl: \"text-3xl sm:text-[32px]\",\n \"2xl\": \"text-[32px] sm:text-[40px]\",\n};\n\nconst BODY_SIZE_MAP: Record<FontSizeOptions, string> = {\n xs: \"text-[12px]\",\n sm: \"text-[13px]\",\n md: \"text-[14px]\",\n lg: \"text-[15px]\",\n xl: \"text-[16px]\",\n \"2xl\": \"text-[18px]\",\n};\n\nfunction useCarouselController({\n slideCount,\n enableAutoScroll,\n autoScrollInterval,\n}: {\n slideCount: number;\n enableAutoScroll: boolean;\n autoScrollInterval: number;\n}) {\n const [currentIndex, setCurrentIndex] = useState(0);\n // Track hover and keyboard focus separately — combining them in one boolean\n // would let `onMouseLeave` clear the paused state even while a child button\n // still has focus.\n const [isHovered, setIsHovered] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const hasSlides = slideCount > 0;\n const isPaused = isHovered || isFocused;\n\n const goToNext = useCallback(() => {\n if (!hasSlides) return;\n setCurrentIndex((prev) => (prev === slideCount - 1 ? 0 : prev + 1));\n }, [hasSlides, slideCount]);\n\n const goToPrevious = useCallback(() => {\n if (!hasSlides) return;\n setCurrentIndex((prev) => (prev === 0 ? slideCount - 1 : prev - 1));\n }, [hasSlides, slideCount]);\n\n const goToSlide = useCallback(\n (index: number) => {\n if (!hasSlides) return;\n if (index >= 0 && index < slideCount) {\n setCurrentIndex(index);\n }\n },\n [hasSlides, slideCount],\n );\n\n useEffect(() => {\n if (!enableAutoScroll || !hasSlides || isPaused) return;\n const intervalId = setInterval(goToNext, autoScrollInterval);\n return () => clearInterval(intervalId);\n }, [enableAutoScroll, autoScrollInterval, hasSlides, isPaused, goToNext]);\n\n useEffect(() => {\n if (currentIndex >= slideCount && slideCount > 0) {\n setCurrentIndex(0);\n }\n }, [slideCount, currentIndex]);\n\n // Pause for a mouse or the keyboard, never for a touch. On iOS a tap fires\n // mouseenter with no mouseleave after it, and it focuses the button too, so\n // either one would leave the carousel stuck paused.\n const handlePointerEnter = useCallback((e: React.PointerEvent) => {\n if (e.pointerType === \"mouse\") setIsHovered(true);\n }, []);\n const handlePointerLeave = useCallback((e: React.PointerEvent) => {\n if (e.pointerType === \"mouse\") setIsHovered(false);\n }, []);\n const handleFocus = useCallback((e: React.FocusEvent<HTMLElement>) => {\n // Set it either way, or a click inside leaves it paused.\n setIsFocused(\n e.target instanceof Element && e.target.matches(\":focus-visible\"),\n );\n }, []);\n const handleBlur = useCallback((e: React.FocusEvent<HTMLElement>) => {\n // Only flip to unfocused when focus actually leaves the carousel.\n // When focus moves between two children (e.g. tabbing from prev to next\n // button), `relatedTarget` is the new focus target and is still contained.\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {\n setIsFocused(false);\n }\n }, []);\n\n return {\n currentIndex,\n hasSlides,\n isPaused,\n goToNext,\n goToPrevious,\n goToSlide,\n handlePointerEnter,\n handlePointerLeave,\n handleFocus,\n handleBlur,\n };\n}\n\nexport function CarouselWidget({\n slides = [],\n autoScrollInterval = 6500,\n enableAutoScroll = false,\n carouselHeight = \"400px\",\n align = { vertical: \"bottom\", horizontal: \"left\" },\n overlayIntensity = 70,\n borderRadius = \"xl\",\n borderWidth = \"none\",\n borderColor = \"muted\",\n padding = 8,\n textWidth = \"620px\",\n headerSize = \"xl\",\n headerColor = \"background\",\n textSize = \"md\",\n textColor = \"background\",\n showButton = true,\n buttonColor = \"background\",\n buttonSize = \"default\",\n overlayEnabled = true,\n overlayType = \"gradient\",\n editorialFrame = true,\n frameColor = \"foreground\",\n className,\n ...props\n}: CarouselWidgetProps): React.JSX.Element {\n const totalSlides = slides.length;\n const {\n currentIndex,\n hasSlides,\n isPaused,\n goToNext,\n goToPrevious,\n goToSlide,\n handlePointerEnter,\n handlePointerLeave,\n handleFocus,\n handleBlur,\n } = useCarouselController({\n slideCount: totalSlides,\n enableAutoScroll,\n autoScrollInterval,\n });\n const { onItemClick, onNavigate, buildHref, resolveInternalSlug } =\n useWidgetInteraction();\n const resolveInternalLink = useInternalLinks();\n const publicId = useRepUser()?.publicId;\n const { editMode } = useWidgetRenderState();\n const verticalClass =\n align.vertical === \"top\"\n ? \"items-start\"\n : align.vertical === \"center\"\n ? \"items-center\"\n : \"items-end\";\n\n const horizontalClass =\n align.horizontal === \"center\"\n ? \"justify-center text-center mx-auto\"\n : align.horizontal === \"right\"\n ? \"justify-end ml-auto text-right\"\n : \"justify-start text-left\";\n\n // Themed dual-shadow (crisp + soft ambient) on the editorial frame —\n // derived from the foreground token so the lift reads in both light\n // and dark themes.\n const frameShadowStyle: CSSProperties | undefined = editorialFrame\n ? {\n boxShadow: `0 1px 2px color-mix(in oklch, var(--color-foreground) 4%, transparent), 0 30px 60px -30px color-mix(in oklch, var(--color-foreground) 40%, transparent)`,\n }\n : undefined;\n\n const frameBg = editorialFrame ? `bg-${frameColor}` : \"\";\n const isActiveSlideVideo =\n slides[currentIndex]?.content.type === \"VideoWidget\";\n\n if (!hasSlides) {\n return (\n <div\n className={`relative w-full overflow-hidden rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${frameBg} ${className ?? \"\"}`}\n style={{ minHeight: carouselHeight, ...frameShadowStyle }}\n {...props}\n >\n <div\n className=\"bg-muted/20 flex h-full w-full items-center justify-center\"\n style={{ minHeight: carouselHeight }}\n >\n <div className=\"text-muted-foreground flex flex-col items-center gap-2\">\n <div className=\"text-4xl\">🎠</div>\n <p className=\"text-sm\">No slides added</p>\n <p className=\"text-muted-foreground/70 text-xs\">\n Add slides to create a carousel\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n className={`relative isolate w-full overflow-hidden rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} ${frameBg} ${className ?? \"\"}`}\n style={{ minHeight: carouselHeight, ...frameShadowStyle }}\n onPointerEnter={handlePointerEnter}\n onPointerLeave={handlePointerLeave}\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n >\n <div\n className=\"relative w-full\"\n style={{\n minHeight: carouselHeight,\n height: carouselHeight,\n }}\n >\n {slides.map((slide, index) => {\n const isActive = index === currentIndex;\n const isVideoSlide = slide.content.type === \"VideoWidget\";\n // Strip the optional \"%\" suffix, then parse. Use Number.isFinite +\n // clamp so an explicit 0 isn't silently replaced by the 70 fallback\n // (the previous `|| 70` treated 0 as falsy).\n const parsedOverlayIntensity = Number(\n String(overlayIntensity).replace(\"%\", \"\"),\n );\n const overlayOpacity =\n (Number.isFinite(parsedOverlayIntensity)\n ? Math.min(100, Math.max(0, parsedOverlayIntensity))\n : 70) / 100;\n\n // Slide-level tap: if the slide has an explicit linkType from\n // itemConfigSchema, use it; otherwise fall back to onItemClick\n // (which routes based on shareable_type, subscription_token, etc.).\n const slideLinkProps =\n isActive && !isVideoSlide\n ? buildItemInteractionProps(slide, {\n onItemClick,\n onNavigate,\n buildHref,\n resolveInternalSlug,\n publicId,\n editMode,\n })\n : {};\n const isInteractive = Object.keys(slideLinkProps).length > 0;\n\n return (\n <div\n key={slide.id}\n className={`absolute inset-0 transition-opacity duration-700 ease-out${isInteractive ? \" cursor-pointer focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:outline-none focus-visible:ring-inset\" : \"\"}`}\n style={{\n opacity: isActive ? 1 : 0,\n pointerEvents: isActive ? \"auto\" : \"none\",\n }}\n aria-hidden={!isActive}\n {...slideLinkProps}\n >\n {isVideoSlide ? (\n <div className=\"absolute inset-0 h-full w-full\">\n {isActive && (\n <MediaRenderer\n {...getMediaPropsFromWidgetSchema(slide.content)}\n />\n )}\n </div>\n ) : (\n <>\n <div\n className=\"absolute inset-0 h-full w-full\"\n style={{\n transform: isActive ? \"scale(1)\" : \"scale(1.04)\",\n animation: isActive\n ? \"fluidCarouselKenBurns 900ms ease-out both\"\n : undefined,\n }}\n >\n <MediaRenderer\n {...getMediaPropsFromWidgetSchema(slide.content)}\n />\n </div>\n\n {overlayEnabled && editorialFrame && (\n <>\n <div\n className={`pointer-events-none absolute inset-0 bg-gradient-to-t from-${frameColor} via-${frameColor}/65 to-${frameColor}/10`}\n style={{ opacity: overlayOpacity }}\n />\n <div\n className={`pointer-events-none absolute inset-0 bg-gradient-to-r from-${frameColor}/80 via-transparent to-transparent`}\n style={{ opacity: Math.min(1, overlayOpacity + 0.1) }}\n />\n </>\n )}\n\n {overlayEnabled && !editorialFrame && (\n <div\n className={`pointer-events-none absolute inset-0 ${\n overlayType === \"gradient\"\n ? \"bg-gradient-to-t from-black to-transparent\"\n : \"bg-black\"\n }`}\n style={{ opacity: overlayOpacity }}\n />\n )}\n </>\n )}\n\n <div\n className={`pointer-events-none absolute inset-0 flex px-6 sm:px-10 ${verticalClass} ${\n align.horizontal === \"center\"\n ? \"justify-center\"\n : align.horizontal === \"right\"\n ? \"justify-end\"\n : \"justify-start\"\n }`}\n style={{\n paddingTop: padding * 4,\n paddingBottom:\n padding * 4 +\n (totalSlides > 1\n ? 24 + (isVideoSlide ? VIDEO_CONTROLS_HEIGHT : 0)\n : 0),\n }}\n >\n <div\n key={`content-${currentIndex}`}\n className={`${isActive && !isVideoSlide ? \"pointer-events-auto\" : \"pointer-events-none\"} flex w-full flex-col gap-3 ${horizontalClass}`}\n style={{\n maxWidth: textWidth,\n animation: isActive\n ? \"fluidCarouselTextRise 450ms ease-out both\"\n : undefined,\n animationDelay: isActive ? \"120ms\" : undefined,\n }}\n >\n {(slide.eyebrow || slide.tag || slide.meta) && (\n <div\n className={`flex flex-wrap items-center gap-2 ${\n align.horizontal === \"center\"\n ? \"justify-center\"\n : align.horizontal === \"right\"\n ? \"justify-end\"\n : \"justify-start\"\n }`}\n >\n {slide.tag && (\n <span\n className={`rounded-full bg-${slide.tagColor ?? \"background\"} text-${slide.tagColor ?? \"background\"}-foreground px-2.5 py-1 text-[10px] font-bold tracking-[0.14em] uppercase`}\n style={\n slide.tagColor === \"background\" ||\n slide.tagColor === \"foreground\" ||\n !slide.tagColor\n ? tagFallbackStyle(slide.tagColor, frameColor)\n : undefined\n }\n >\n {slide.tag}\n </span>\n )}\n {slide.eyebrow && (\n <span\n className={`text-${headerColor}/70 text-[10px] font-bold tracking-[0.16em] uppercase`}\n >\n {slide.eyebrow}\n </span>\n )}\n {slide.meta && (\n <span\n className={`text-${headerColor}/60 text-[10px] font-bold tracking-[0.16em] uppercase`}\n >\n {slide.meta}\n </span>\n )}\n </div>\n )}\n\n {slide.title && (\n <h2\n className={`text-${headerColor} font-bold ${HEADER_SIZE_MAP[headerSize]} leading-[1.12] tracking-[-0.018em]`}\n >\n {slide.title}\n </h2>\n )}\n\n {slide.description && (\n // Description supports inline HTML for emphasis / line\n // breaks / lists. Sanitized via DOMPurify with the same\n // allowlist as the previous implementation so saved\n // slides containing <strong>, <em>, <br>, <ul>, etc.\n // continue rendering as authored. Element is a <div>\n // (not <p>) to permit block-level allowed tags.\n <div\n className={`text-${textColor}/70 ${BODY_SIZE_MAP[textSize]} leading-[1.55]`}\n dangerouslySetInnerHTML={{\n __html: DOMPurify.sanitize(slide.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\n {(() => {\n const showPrimary =\n slide.buttonEnabled &&\n showButton &&\n Boolean(slide.buttonText) &&\n Boolean(slide.buttonLink);\n const showSecondary =\n Boolean(slide.secondaryButtonText) &&\n Boolean(slide.secondaryButtonLink);\n if (!showPrimary && !showSecondary) return null;\n\n const primaryLink = resolveInternalLink(slide.buttonLink);\n const secondaryLink = resolveInternalLink(\n slide.secondaryButtonLink,\n );\n\n return (\n <div\n className={`mt-3 flex flex-wrap items-center gap-3 ${\n align.horizontal === \"center\"\n ? \"justify-center\"\n : align.horizontal === \"right\"\n ? \"justify-end\"\n : \"justify-start\"\n }`}\n >\n {showPrimary && (\n <a\n href={slide.buttonLink}\n onClick={(e) => {\n e.stopPropagation();\n primaryLink.followInternally(e);\n }}\n className={`inline-flex items-center gap-1.5 rounded-full bg-${buttonColor} text-${buttonColor}-foreground px-5 py-2.5 text-[13px] font-bold transition-transform hover:scale-[1.02] ${buttonSizeClass(buttonSize)}`}\n style={{\n boxShadow: `0 8px 22px -8px color-mix(in oklch, var(--color-foreground) 35%, transparent)`,\n }}\n >\n {slide.buttonText}\n <span aria-hidden=\"true\" className=\"text-[14px]\">\n →\n </span>\n </a>\n )}\n {showSecondary && (\n <a\n href={slide.secondaryButtonLink}\n onClick={(e) => {\n e.stopPropagation();\n secondaryLink.followInternally(e);\n }}\n className={`inline-flex items-center gap-1 text-${headerColor}/80 hover:text-${headerColor} text-[12px] font-bold transition-colors`}\n >\n {slide.secondaryButtonText}\n <span aria-hidden=\"true\">→</span>\n </a>\n )}\n </div>\n );\n })()}\n </div>\n </div>\n </div>\n );\n })}\n\n {totalSlides > 1 && (\n <div className=\"pointer-events-none absolute inset-0 z-20\">\n <div className=\"pointer-events-auto absolute top-6 right-6 flex items-center gap-1.5\">\n <button\n type=\"button\"\n onClick={goToPrevious}\n aria-label=\"Previous slide\"\n className={`flex size-10 items-center justify-center rounded-full bg-${headerColor}/10 text-${headerColor} ring-1 ring-${headerColor}/15 backdrop-blur-sm transition-colors hover:bg-${headerColor}/20`}\n >\n <ChevronLeftIcon />\n </button>\n <button\n type=\"button\"\n onClick={goToNext}\n aria-label=\"Next slide\"\n className={`flex size-10 items-center justify-center rounded-full bg-${headerColor} text-${headerColor}-foreground transition-transform hover:scale-[1.04]`}\n style={{\n // Themed lift shadow (was hardcoded white) so the active\n // nav button reads on both light and dark frames.\n boxShadow: `0 4px 14px -4px color-mix(in oklch, var(--color-${headerColor}) 45%, transparent)`,\n ...nextButtonFallbackStyle(headerColor, frameColor),\n }}\n >\n <ChevronRightIcon />\n </button>\n </div>\n\n <div\n className={`pointer-events-auto absolute inset-x-0 flex items-center gap-1 px-6 pb-2 sm:px-10`}\n style={{\n bottom: isActiveSlideVideo ? VIDEO_CONTROLS_HEIGHT : 0,\n transition: \"bottom 200ms ease-out\",\n }}\n >\n {slides.map((_, i) => {\n const state =\n i < currentIndex\n ? \"done\"\n : i === currentIndex\n ? \"active\"\n : \"upcoming\";\n return (\n <button\n key={`progress-${i}`}\n type=\"button\"\n onClick={() => goToSlide(i)}\n aria-label={`Go to slide ${i + 1}`}\n aria-current={state === \"active\" ? \"true\" : \"false\"}\n className=\"group flex-1 py-2\"\n >\n <span\n className={`block h-[2px] w-full overflow-hidden rounded-full bg-${headerColor}/20`}\n >\n <span\n key={`fill-${i}-${currentIndex}`}\n className={`block h-full rounded-full bg-${headerColor}`}\n style={progressFillStyle(\n state,\n enableAutoScroll,\n autoScrollInterval,\n isPaused,\n )}\n />\n </span>\n </button>\n );\n })}\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nfunction progressFillStyle(\n state: \"done\" | \"active\" | \"upcoming\",\n autoScroll: boolean,\n duration: number,\n paused: boolean,\n): CSSProperties {\n if (state === \"upcoming\") return { width: \"0%\" };\n if (!autoScroll) {\n // Nothing is counting down, so only the current bar fills. Otherwise the\n // last slide has every bar full and you can't tell which one you're on.\n return { width: state === \"active\" ? \"100%\" : \"0%\" };\n }\n if (state === \"done\") return { width: \"100%\" };\n return {\n width: \"0%\",\n animation: `fluidCarouselProgressFill ${duration}ms linear forwards`,\n animationPlayState: paused ? \"paused\" : \"running\",\n };\n}\n\nfunction tagFallbackStyle(\n tagColor: ColorOptions | undefined,\n frameColor: ColorOptions,\n): CSSProperties | undefined {\n // bg-background/foreground don't emit -foreground utilities by default;\n // fall back to contrast-safe inline colors so tag chips always read.\n if (!tagColor || tagColor === \"background\") {\n return { color: `var(--color-${frameColor})` };\n }\n if (tagColor === \"foreground\") {\n return { color: `var(--color-background)` };\n }\n return undefined;\n}\n\nfunction nextButtonFallbackStyle(\n headerColor: ColorOptions,\n frameColor: ColorOptions,\n): CSSProperties | undefined {\n // Same contrast fix as tags — ensure icon color reads on the active button.\n if (headerColor === \"background\") {\n return { color: `var(--color-${frameColor})` };\n }\n if (headerColor === \"foreground\") {\n return { color: `var(--color-background)` };\n }\n return undefined;\n}\n\nfunction buttonSizeClass(size: ButtonSizeOptions): string {\n if (size === \"sm\") return \"px-4 py-1.5 text-[12px]\";\n if (size === \"lg\") return \"px-6 py-3 text-[14px]\";\n if (size === \"xl\") return \"px-7 py-3.5 text-[15px]\";\n return \"\";\n}\n\nfunction ChevronLeftIcon() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M15 18l-6-6 6-6\" />\n </svg>\n );\n}\n\nfunction ChevronRightIcon() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M9 18l6-6-6-6\" />\n </svg>\n );\n}\n\nexport const carouselWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"CarouselWidget\",\n displayName: \"Carousel\",\n tabsConfig: [\n { id: \"styling\", label: \"Styling\" },\n { id: \"behavior\", label: \"Behavior\" },\n { id: \"data\", label: \"Data\" },\n ],\n dataSourceTargetProps: [\"slides\"],\n fields: [\n // Styling tab — Frame group (new)\n {\n key: \"editorialFrame\",\n label: \"Editorial Frame\",\n type: \"boolean\",\n description:\n \"Premium dark canvas with layered shadow and gradient stack. Turn off for a classic transparent carousel.\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Frame\",\n },\n getColorField({\n defaultValue: \"foreground\",\n key: \"frameColor\",\n label: \"Frame Color\",\n description:\n \"Surface color of the carousel canvas. Drives the frame and gradient overlay.\",\n tab: \"styling\",\n group: \"Frame\",\n requiresKeyToBeTrue: \"editorialFrame\",\n }),\n\n // Text Styling group\n getFontSizeField({\n defaultValue: \"xl\",\n key: \"headerSize\",\n label: \"Header Font Size\",\n description: \"Font size for the slide header\",\n tab: \"styling\",\n group: \"Text Styling\",\n }),\n getColorField({\n defaultValue: \"background\",\n key: \"headerColor\",\n label: \"Header Color\",\n description:\n \"Headline color. Also drives eyebrow, controls and progress bar.\",\n tab: \"styling\",\n group: \"Text Styling\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Text Styling\",\n },\n getFontSizeField({\n defaultValue: \"md\",\n key: \"textSize\",\n label: \"Text Font Size\",\n description: \"Font size for the slide text\",\n tab: \"styling\",\n group: \"Text Styling\",\n }),\n getColorField({\n defaultValue: \"background\",\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Color variant for the slide text\",\n tab: \"styling\",\n group: \"Text Styling\",\n }),\n\n // Layout group\n {\n key: \"align\",\n label: \"Alignment\",\n type: \"alignment\",\n description: \"Alignment of the carousel content\",\n defaultValue: { vertical: \"bottom\", horizontal: \"left\" },\n options: {\n verticalEnabled: true,\n horizontalEnabled: true,\n },\n tab: \"styling\",\n group: \"Layout\",\n },\n {\n key: \"textWidth\",\n label: \"Content Width\",\n type: \"cssUnit\",\n description: \"Maximum width for slide content\",\n defaultValue: \"620px\",\n allowedUnits: [\"px\", \"rem\", \"%\"],\n defaultUnit: \"px\",\n minByUnit: { px: 50, rem: 3, \"%\": 10 },\n maxByUnit: { px: 1200, rem: 75, \"%\": 100 },\n stepByUnit: { px: 10, rem: 1, \"%\": 1 },\n tab: \"styling\",\n group: \"Layout\",\n },\n\n // Design group\n getHeightField({\n key: \"carouselHeight\",\n label: \"Carousel Height\",\n description: \"Height of the carousel\",\n defaultValue: \"400px\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"separator2\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Design\",\n },\n getBorderRadiusField({\n defaultValue: \"xl\",\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Rounded corners for the carousel\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Border width for the carousel container\",\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 carousel container\",\n defaultValue: \"muted\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getPaddingField({\n defaultValue: 8,\n key: \"padding\",\n label: \"Padding\",\n description: \"Inner padding around slide content\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"overlayEnabled\",\n label: \"Enable Overlay\",\n type: \"boolean\",\n description: \"Add background overlay to slide content\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Design\",\n },\n {\n key: \"overlayType\",\n label: \"Overlay Type\",\n type: \"buttonGroup\",\n description: \"Overlay style (only applies when editorial frame is off)\",\n defaultValue: \"gradient\",\n options: [\n { label: \"Solid\", value: \"solid\" },\n { label: \"Gradient\", value: \"gradient\" },\n ],\n tab: \"styling\",\n group: \"Design\",\n requiresKeyToBeTrue: \"overlayEnabled\",\n },\n {\n key: \"overlayIntensity\",\n label: \"Overlay Intensity\",\n type: \"slider\",\n description: \"Opacity of the overlay (0-100)\",\n min: 0,\n max: 100,\n step: 5,\n defaultValue: 70,\n unit: \"%\",\n tab: \"styling\",\n group: \"Design\",\n requiresKeyToBeTrue: \"overlayEnabled\",\n },\n {\n key: \"showButton\",\n label: \"Show Primary Button\",\n type: \"boolean\",\n description:\n \"Global show/hide for all slide CTAs. Per-slide button content is set on each item under the Data tab (custom sources only).\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Design\",\n },\n getColorField({\n defaultValue: \"background\",\n key: \"buttonColor\",\n label: \"Button Color\",\n description: \"Color variant for the slide button\",\n tab: \"styling\",\n group: \"Design\",\n requiresKeyToBeTrue: \"showButton\",\n }),\n getButtonSizeField({\n defaultValue: \"default\",\n key: \"buttonSize\",\n label: \"Button Size\",\n description: \"Size of the slide button\",\n tab: \"styling\",\n group: \"Design\",\n requiresKeyToBeTrue: \"showButton\",\n }),\n\n // Behavior tab\n {\n key: \"enableAutoScroll\",\n label: \"Enable Auto-Scroll\",\n type: \"boolean\",\n description:\n \"Automatically advance to the next slide. Progress bar fills at this rate and pauses on hover.\",\n defaultValue: false,\n tab: \"behavior\",\n group: \"Auto-Scroll\",\n },\n {\n key: \"autoScrollInterval\",\n label: \"Auto-Scroll Interval (ms)\",\n type: \"number\",\n description: \"Time between automatic slide transitions\",\n min: 1000,\n max: 15000,\n step: 500,\n defaultValue: 6500,\n tab: \"behavior\",\n group: \"Auto-Scroll\",\n requiresKeyToBeTrue: \"enableAutoScroll\",\n },\n\n // Data tab\n {\n key: \"dataSource\",\n label: \"Data Source\",\n type: \"dataSource\",\n description: \"Configure the slides rendered by the carousel.\",\n targetProps: [\n {\n key: \"slides\",\n description:\n \"Slides rendered by the carousel, usually supplied by a data source.\",\n },\n ],\n tab: \"data\",\n group: \"Data Configuration\",\n },\n ],\n itemConfigSchema: {\n description: \"Configure settings for this slide\",\n fields: [\n {\n key: \"eyebrow\",\n label: \"Eyebrow\",\n type: \"text\",\n description:\n \"Short label above the title (e.g. 'From Fluid · Newsroom')\",\n },\n {\n key: \"tag\",\n label: \"Tag / Category\",\n type: \"text\",\n description: \"Short pill label (e.g. 'Policy', 'New release')\",\n },\n {\n key: \"tagColor\",\n label: \"Tag Color\",\n type: \"colorSelect\",\n description: \"Background color of the tag pill\",\n defaultValue: \"background\",\n },\n {\n key: \"meta\",\n label: \"Meta\",\n type: \"text\",\n description: \"Meta line (e.g. 'Apr 12 · 4 min read')\",\n },\n {\n key: \"separator_slide_1\",\n type: \"separator\",\n label: \"Separator\",\n },\n {\n key: \"title\",\n label: \"Title\",\n type: \"text\",\n description: \"Override the item's title for this slide\",\n },\n {\n key: \"description\",\n label: \"Description\",\n type: \"textarea\",\n description: \"Override the item's description for this slide\",\n rows: 3,\n },\n {\n key: \"separator_slide_2\",\n type: \"separator\",\n label: \"Separator\",\n },\n {\n key: \"buttonEnabled\",\n label: \"Show Primary Button\",\n type: \"boolean\",\n description: \"Show the primary CTA on this slide\",\n defaultValue: false,\n },\n {\n key: \"buttonText\",\n label: \"Button Text\",\n type: \"text\",\n description: \"Text for the primary button\",\n requiresKeyToBeTrue: \"buttonEnabled\",\n },\n {\n key: \"buttonLink\",\n label: \"Button Link\",\n type: \"text\",\n description: \"URL for the primary button\",\n requiresKeyToBeTrue: \"buttonEnabled\",\n },\n {\n key: \"secondaryButtonText\",\n label: \"Secondary Link Text\",\n type: \"text\",\n description:\n \"Optional text-only secondary link (renders with an arrow)\",\n },\n {\n key: \"secondaryButtonLink\",\n label: \"Secondary Link URL\",\n type: \"text\",\n description: \"URL for the secondary link\",\n },\n { key: \"separator_link\", type: \"separator\", label: \"Separator\" },\n ...getLinkTypeFields(),\n ],\n },\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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K;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,KAAK;GACL,MAAM;GACN,cAAc;GACd,MAAM;GACN,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,cAAc;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EACF,mBAAmB;GACjB,cAAc;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,KAAK;GACL,MAAM;GACN,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EAGD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,aAAa,CACX;IACE,KAAK;IACL,aACE;IACH,CACF;GACD,KAAK;GACL,OAAO;GACR;EACF;CACD,kBAAkB;EAChB,aAAa;EACb,QAAQ;GACN;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aACE;IACH;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,cAAc;IACf;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IACE,KAAK;IACL,MAAM;IACN,OAAO;IACR;GACD;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,MAAM;IACN,OAAO;IACR;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,cAAc;IACf;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,qBAAqB;IACtB;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACb,qBAAqB;IACtB;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aACE;IACH;GACD;IACE,KAAK;IACL,OAAO;IACP,MAAM;IACN,aAAa;IACd;GACD;IAAE,KAAK;IAAkB,MAAM;IAAa,OAAO;IAAa;GAChE,GAAG,mBAAmB;GACvB;EACF;CACF"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
require("./chunk-9hOWP6kD.cjs");
|
|
2
|
-
require("./WidgetInteractionContext-
|
|
2
|
+
require("./WidgetInteractionContext-D9dBJ-39.cjs");
|
|
3
3
|
require("./WidgetRenderStateContext-C6wVdsJR.cjs");
|
|
4
4
|
require("./registries-MpWTUak_.cjs");
|
|
5
5
|
require("./fields-DbTpcT4E.cjs");
|
|
6
|
-
require("./use-internal-link-
|
|
6
|
+
require("./use-internal-link-IcDHjPGp.cjs");
|
|
7
7
|
require("./helpers-BNPN4U1M.cjs");
|
|
8
8
|
require("./MediaRenderer-m_d4GHu0.cjs");
|
|
9
|
-
const require_CarouselWidget = require("./CarouselWidget-
|
|
9
|
+
const require_CarouselWidget = require("./CarouselWidget-BETNqs_K.cjs");
|
|
10
10
|
exports.carouselWidgetPropertySchema = require_CarouselWidget.carouselWidgetPropertySchema;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./src-BfINy9nC.mjs";
|
|
2
2
|
import "./registry-context-DeQRPEBN.mjs";
|
|
3
|
-
import "./WidgetInteractionContext-
|
|
3
|
+
import "./WidgetInteractionContext-IsMo73Ka.mjs";
|
|
4
4
|
import "./translation-api-context-factory-BTpIuUF0.mjs";
|
|
5
5
|
import "./shell-translation-api-context-CCQ7vCA6.mjs";
|
|
6
6
|
import "./widgets-api-context-6escG2w8.mjs";
|
|
@@ -9,5 +9,5 @@ import "./fields-Ch4XJaCs.mjs";
|
|
|
9
9
|
import "./preview-context-D1c5nVF4.mjs";
|
|
10
10
|
import "./error-state-B7i_iTF6.mjs";
|
|
11
11
|
import "./WidgetLoadingSkeleton-D4Dx1qBJ.mjs";
|
|
12
|
-
import { n as catchUpWidgetPropertySchema } from "./CatchUpWidget-
|
|
12
|
+
import { n as catchUpWidgetPropertySchema } from "./CatchUpWidget-CLwHp30X.mjs";
|
|
13
13
|
export { catchUpWidgetPropertySchema };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { m as fluidToast } from "./src-BfINy9nC.mjs";
|
|
2
2
|
import { n as useDataSourceRegistryConfig } from "./registry-context-DeQRPEBN.mjs";
|
|
3
|
-
import { n as useWidgetInteraction } from "./WidgetInteractionContext-
|
|
3
|
+
import { n as useWidgetInteraction } from "./WidgetInteractionContext-IsMo73Ka.mjs";
|
|
4
4
|
import { n as useWidgetsApi } from "./widgets-api-context-6escG2w8.mjs";
|
|
5
5
|
import { i as getBorderColorField, l as getColorField, m as getPaddingField, n as borderWidthClasses, o as getBorderRadiusField, s as getBorderWidthField, t as borderColorClasses, u as getFontSizeField } from "./registries-B2aW34Dg.mjs";
|
|
6
6
|
import { t as useWidgetPreviewContext } from "./preview-context-D1c5nVF4.mjs";
|
|
@@ -242,7 +242,7 @@ function CatchUpWidget({ titleEnabled = true, titleText = "Catch Ups", titleFont
|
|
|
242
242
|
const { data = [], isLoading, isError } = useCatchUps();
|
|
243
243
|
const dismissAction = useDismissCatchUpAction();
|
|
244
244
|
const { isPreview } = useWidgetPreviewContext();
|
|
245
|
-
const { onNavigate } = useWidgetInteraction();
|
|
245
|
+
const { onNavigate, canNavigateTo } = useWidgetInteraction();
|
|
246
246
|
const [expanded, setExpanded] = useState(false);
|
|
247
247
|
const [dismissingActionIds, setDismissingActionIds] = useState(() => /* @__PURE__ */ new Set());
|
|
248
248
|
const screenSlugs = {
|
|
@@ -299,7 +299,8 @@ function CatchUpWidget({ titleEnabled = true, titleText = "Catch Ups", titleFont
|
|
|
299
299
|
const Icon = getCategoryIcon(catchUp.category);
|
|
300
300
|
const isResolved = catchUp.status === "resolved";
|
|
301
301
|
const primaryAction = catchUp.actions.find((action) => action.status === "pending");
|
|
302
|
-
const
|
|
302
|
+
const resolvedSlug = primaryAction?.destination ? buildDestinationSlug(primaryAction.destination, screenSlugs) : null;
|
|
303
|
+
const destinationSlug = resolvedSlug && (!canNavigateTo || canNavigateTo(resolvedSlug)) ? resolvedSlug : null;
|
|
303
304
|
const isDismissing = primaryAction ? dismissingActionIds.has(primaryAction.id) : false;
|
|
304
305
|
return /* @__PURE__ */ jsxs("div", {
|
|
305
306
|
className: `flex items-start gap-3 rounded-lg border border-${textColor}/10 p-3 transition-colors ${isResolved ? "opacity-55" : `hover:border-${accentColor}/30`}`,
|
|
@@ -545,4 +546,4 @@ const catchUpWidgetPropertySchema = {
|
|
|
545
546
|
//#endregion
|
|
546
547
|
export { catchUpWidgetPropertySchema as n, CatchUpWidget as t };
|
|
547
548
|
|
|
548
|
-
//# sourceMappingURL=CatchUpWidget-
|
|
549
|
+
//# sourceMappingURL=CatchUpWidget-CLwHp30X.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CatchUpWidget-CLwHp30X.mjs","names":[],"sources":["../../widgets/src/hooks/use-catchups.preview.ts","../../widgets/src/hooks/use-catchups.ts","../../widgets/src/widgets/CatchUpWidget.tsx"],"sourcesContent":["import type { CatchUp } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport const PREVIEW_DATA: CatchUp[] = [\n {\n id: 1,\n suggestionTitle: \"Check in with Sarah about her recent order\",\n description:\n \"Her last order shipped 5 days ago — a quick thank-you goes a long way.\",\n category: \"left_a_review\",\n status: \"pending\",\n actionCount: 1,\n actions: [\n {\n id: 11,\n actionType: \"SendDirectMessageCatchUpAction\",\n status: \"pending\",\n contact: { id: 501, name: \"Sarah Chen\" },\n destination: { kind: \"contact\", id: 501 },\n },\n ],\n },\n {\n id: 2,\n suggestionTitle: \"Follow up with Mike on product samples\",\n description: \"Mike asked about samples last week and hasn't heard back.\",\n category: \"unread_messages\",\n status: \"pending\",\n actionCount: 1,\n actions: [\n {\n id: 21,\n actionType: \"UnreadMessagesCatchUpAction\",\n status: \"pending\",\n contact: { id: 502, name: \"Mike Ross\" },\n destination: { kind: \"conversation\", id: 9001 },\n },\n ],\n },\n {\n id: 3,\n suggestionTitle: \"New arrivals just dropped\",\n description: \"Fresh products your customers will love — take a look.\",\n category: \"new_product\",\n status: \"pending\",\n actionCount: 1,\n actions: [\n {\n id: 31,\n actionType: \"NewProductCatchUpAction\",\n status: \"pending\",\n contact: null,\n destination: { kind: \"product\", id: 7001 },\n },\n ],\n },\n {\n id: 4,\n suggestionTitle: \"Thank Alex for their referral last week\",\n description: null,\n category: null,\n status: \"resolved\",\n actionCount: 0,\n actions: [],\n },\n {\n id: 5,\n suggestionTitle: \"New training video posted\",\n description: \"A new video was just added to your library.\",\n category: \"new_media_added\",\n status: \"pending\",\n actionCount: 1,\n actions: [\n {\n id: 51,\n actionType: \"ViewMediaCatchUpAction\",\n status: \"pending\",\n contact: null,\n destination: { kind: \"media\", id: 3001 },\n },\n ],\n },\n];\n","import {\n useMutation,\n useQuery,\n useQueryClient,\n type UseMutationResult,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { PREVIEW_DATA } from \"./use-catchups.preview\";\nimport type { CatchUp } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport type { CatchUp } from \"@fluid-app/portal-core/widgets-api-types\";\n\n// The live (non-preview) query key. Kept as a helper so the query and the\n// mutation that mutates its cache can never drift apart.\nfunction catchUpsQueryKey(baseUrl: string | undefined) {\n return [\"portal-widget-use\", \"catchups\", baseUrl] as const;\n}\n\nexport function useCatchUps(): UseQueryResult<CatchUp[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: [\n \"portal-widget-use\",\n \"catchups\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: ({ signal }) => widgetsApi.fetchCatchUps(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n\nexport type DismissCatchUpActionVars = {\n catchUpId: number;\n actionId: number;\n};\n\n/**\n * Dismisses one of a catch-up's pending actions through the BFF (a safe resolve\n * with no external side effects), then removes it from the cached list.\n *\n * The cache is edited only on confirmed success — never optimistically and\n * without a refetch. So there is nothing to roll back if it fails (a failed\n * dismiss just leaves the row), nothing a concurrent dismiss can clobber (each\n * success applies an independent, idempotent removal to the current cache), and\n * no invalidation whose failure could turn a successful dismiss into an error.\n * The button shows a pending spinner until the request resolves.\n *\n * A failed dismiss (e.g. the BFF endpoint isn't deployed yet) leaves the row in\n * place and surfaces a toast, rather than silently stopping the spinner with no\n * feedback.\n */\nexport function useDismissCatchUpAction(): UseMutationResult<\n void,\n Error,\n DismissCatchUpActionVars\n> {\n const widgetsApi = useWidgetsApi();\n const { baseUrl } = useDataSourceRegistryConfig();\n const queryClient = useQueryClient();\n const queryKey = catchUpsQueryKey(baseUrl);\n\n return useMutation({\n mutationFn: ({ catchUpId, actionId }: DismissCatchUpActionVars) =>\n widgetsApi.dismissCatchUpAction(catchUpId, actionId),\n onSuccess: (_data, { catchUpId, actionId }) => {\n queryClient.setQueryData<CatchUp[]>(queryKey, (old) =>\n (old ?? [])\n .map((catchUp) =>\n catchUp.id === catchUpId\n ? {\n ...catchUp,\n actions: catchUp.actions.map((action) =>\n action.id === actionId\n ? { ...action, status: \"resolved\" as const }\n : action,\n ),\n }\n : catchUp,\n )\n // Only the just-dismissed catch-up may need removing — drop it once\n // none of its actions remain pending. Every other row is left exactly\n // as it was, so an unrelated catch-up with no pending actions (the\n // widget can render resolved rows) doesn't vanish on this dismiss.\n .filter(\n (catchUp) =>\n catchUp.id !== catchUpId ||\n catchUp.actions.some((action) => action.status === \"pending\"),\n ),\n );\n },\n onError: (error, { catchUpId, actionId }) => {\n console.error(\"[portal-widgets] Failed to dismiss catch-up action\", {\n error,\n catchUpId,\n actionId,\n });\n fluidToast({ title: \"Couldn't dismiss catch-up\", type: \"error\" });\n },\n });\n}\n","import type { ComponentProps } from \"react\";\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport type { CatchUpActionDestination } from \"@fluid-app/portal-core/widgets-api-types\";\nimport {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n borderWidthClasses,\n borderColorClasses,\n} from \"../core/fields\";\nimport { useCatchUps, useDismissCatchUpAction } from \"../hooks/use-catchups\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useWidgetInteraction } from \"../contexts/WidgetInteractionContext\";\nimport {\n ArrowRight,\n CalendarClock,\n Check,\n ChevronDown,\n Loader2,\n MessageCircle,\n Package,\n PlayCircle,\n ShoppingCart,\n Sparkles,\n Star,\n X,\n type LucideIcon,\n} from \"lucide-react\";\nimport { ErrorState } from \"../components/error-state\";\nimport { WidgetLoadingSkeleton } from \"../components/WidgetLoadingSkeleton\";\n\n// Font size mapping for title\nconst fontSizeClasses: Record<FontSizeOptions, string> = {\n \"2xl\": \"text-2xl\",\n xl: \"text-xl\",\n lg: \"text-lg\",\n md: \"text-base\",\n sm: \"text-sm\",\n xs: \"text-xs\",\n};\n\n// Maps a catch-up's trigger category (catch_up_class on the BFF) to an icon.\n// Unknown or null categories fall back to the generic Sparkles icon.\nconst categoryIcons: Record<string, LucideIcon> = {\n cart_abandoned: ShoppingCart,\n new_product: Package,\n left_a_review: Star,\n add_review: Star,\n upcoming_subscription_orders: CalendarClock,\n upcoming_event: CalendarClock,\n video_viewed: PlayCircle,\n new_media_added: PlayCircle,\n unread_messages: MessageCircle,\n unresolved_media_comment: MessageCircle,\n};\n\nfunction getCategoryIcon(category: string | null): LucideIcon {\n return (category && categoryIcons[category]) || Sparkles;\n}\n\n// Verb shown on a catch-up's primary action button, keyed on the BFF's STI\n// action class. Unknown types fall back to a generic label so a new backend\n// action type still renders a working button.\nconst actionLabels: Record<string, string> = {\n ResolveConversationCatchUpAction: \"Resolve\",\n UnreadMessagesCatchUpAction: \"Reply\",\n SendDirectMessageCatchUpAction: \"Message\",\n SendSmsCatchUpAction: \"Text\",\n AddReviewCatchUpAction: \"Leave review\",\n AddCommentReactionCatchUpAction: \"React\",\n RsvpCatchUpAction: \"RSVP\",\n ShareSmartLinkCatchUpAction: \"Share\",\n LinkRedirectionCatchUpAction: \"Open\",\n ViewMediaCatchUpAction: \"View\",\n NewProductCatchUpAction: \"View product\",\n MarkAsDoneCatchUpAction: \"Mark done\",\n ToDoCatchUpAction: \"View task\",\n ConfirmSliderValueCatchUpAction: \"Confirm\",\n UpdateContactGroupCatchUpAction: \"Update\",\n};\n\nfunction getActionLabel(actionType: string): string {\n return actionLabels[actionType] ?? \"Do it\";\n}\n\ntype ScreenSlugs = {\n contact: string;\n messaging: string;\n shop: string;\n shareables: string;\n};\n\n// Map a normalized BFF destination to a portal navigation slug. Returns null\n// for kinds the portal can't deep-link (the widget then offers a dismiss).\nfunction buildDestinationSlug(\n destination: CatchUpActionDestination,\n slugs: ScreenSlugs,\n): string | null {\n switch (destination.kind) {\n case \"contact\":\n return `${slugs.contact}/${destination.id}`;\n case \"conversation\":\n return `${slugs.messaging}/conversation/${destination.id}`;\n case \"product\":\n return `${slugs.shop}/${destination.id}`;\n case \"media\":\n // Shareables deep-links a specific medium at \"<share>/media/<id>\".\n return `${slugs.shareables}/media/${destination.id}`;\n default:\n return null;\n }\n}\n\n// Only these semantic colors ship a paired `-foreground` token. For any other\n// ColorOptions (background, foreground, border, transparent) `text-*-foreground`\n// is a class that does not exist, so the filled button's label would fall back\n// to inherited colour. Resolve a legible contrast token instead.\nconst PAIRED_FOREGROUND_COLORS: ReadonlySet<ColorOptions> = new Set([\n \"primary\",\n \"secondary\",\n \"accent\",\n \"muted\",\n \"destructive\",\n]);\n\nfunction accentForegroundClass(accent: ColorOptions): string {\n if (PAIRED_FOREGROUND_COLORS.has(accent)) return `text-${accent}-foreground`;\n return accent === \"foreground\" ? \"text-background\" : \"text-foreground\";\n}\n\ntype CatchUpWidgetProps = ComponentProps<\"div\"> & {\n // Title settings\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n // Design settings\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n // Display settings\n maxItems?: number;\n // Slugs of the screens a catch-up routes to, by destination kind.\n contactScreenSlug?: string;\n messagingScreenSlug?: string;\n shopScreenSlug?: string;\n shareablesScreenSlug?: string;\n};\n\nexport function CatchUpWidget({\n // Title settings with defaults\n titleEnabled = true,\n titleText = \"Catch Ups\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n // Design settings with defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n borderWidth = \"none\",\n borderColor = \"muted\",\n // Display settings with defaults\n maxItems = 5,\n contactScreenSlug = \"contacts\",\n messagingScreenSlug = \"messages\",\n shopScreenSlug = \"shop\",\n shareablesScreenSlug = \"share\",\n className,\n ...props\n}: CatchUpWidgetProps): React.JSX.Element {\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 const { data = [], isLoading, isError } = useCatchUps();\n const dismissAction = useDismissCatchUpAction();\n const { isPreview } = useWidgetPreviewContext();\n const { onNavigate, canNavigateTo } = useWidgetInteraction();\n const [expanded, setExpanded] = useState(false);\n // The dismiss mutation is shared by every row, so its `variables`/`isPending`\n // only reflect the most recent click. Track in-flight actions ourselves so a\n // second dismiss can't clear an earlier row's spinner or re-enable it mid-flight.\n const [dismissingActionIds, setDismissingActionIds] = useState<\n ReadonlySet<number>\n >(() => new Set());\n\n const screenSlugs: ScreenSlugs = {\n contact: contactScreenSlug,\n messaging: messagingScreenSlug,\n shop: shopScreenSlug,\n shareables: shareablesScreenSlug,\n };\n\n // Collapsed shows the first `maxItems`; expanded shows everything. The toggle\n // count is independent of `expanded` so the control stays visible to collapse.\n const catchUpsToShow = expanded ? data : data.slice(0, maxItems);\n const hiddenCount = Math.max(0, data.length - maxItems);\n\n return (\n <div\n className={`flex flex-col rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} bg-${backgroundColor} p-${padding} text-${textColor} ${className || \"\"}`}\n style={{ backgroundImage }}\n {...props}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between gap-3\">\n {titleEnabled && (\n <h3\n className={`${fontSizeClasses[titleFontSize]} font-header font-bold tracking-[-0.01em] text-${titleColor}`}\n >\n {titleText}\n </h3>\n )}\n {!isLoading && !isError && data.length > 0 && (\n <span\n className={`inline-flex min-w-[1.5rem] items-center justify-center rounded-full bg-${accentColor}/10 px-2 py-0.5 text-xs font-bold tabular-nums text-${accentColor}`}\n >\n {data.length}\n </span>\n )}\n </div>\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"mt-3 flex-1\">\n <WidgetLoadingSkeleton minHeight={150} rows={3} />\n </div>\n ) : isError ? (\n /* Error state */\n <div className=\"mt-3 flex-1\">\n <ErrorState />\n </div>\n ) : data.length === 0 ? (\n /* Empty state */\n <div className=\"flex min-h-[150px] flex-1 flex-col items-center justify-center gap-3 py-6\">\n <div\n className={`flex size-12 items-center justify-center rounded-full bg-${accentColor}/10`}\n >\n <Sparkles className={`size-5 text-${accentColor}`} aria-hidden />\n </div>\n <div className=\"text-center\">\n <p className={`text-sm font-semibold text-${textColor}`}>\n You're all caught up\n </p>\n <p className={`mt-0.5 text-xs text-${textColor}/55`}>\n Nothing needs your attention right now.\n </p>\n </div>\n </div>\n ) : (\n /* Default state with catch ups */\n <div className=\"mt-3 flex flex-1 flex-col gap-2\">\n {catchUpsToShow.map((catchUp, index: number) => {\n const Icon = getCategoryIcon(catchUp.category);\n const isResolved = catchUp.status === \"resolved\";\n // The row's primary action is the first still-actionable one.\n const primaryAction = catchUp.actions.find(\n (action) => action.status === \"pending\",\n );\n const resolvedSlug = primaryAction?.destination\n ? buildDestinationSlug(primaryAction.destination, screenSlugs)\n : null;\n // Suppress the deep-link when it would dead-end on \"Page Not Found\"\n // (an opt-in screen like a contact/conversation the portal hasn't\n // added to its nav). The row then falls back to the dismiss control.\n const destinationSlug =\n resolvedSlug && (!canNavigateTo || canNavigateTo(resolvedSlug))\n ? resolvedSlug\n : null;\n const isDismissing = primaryAction\n ? dismissingActionIds.has(primaryAction.id)\n : false;\n return (\n <div\n key={catchUp.id || index}\n className={`flex items-start gap-3 rounded-lg border border-${textColor}/10 p-3 transition-colors ${isResolved ? \"opacity-55\" : `hover:border-${accentColor}/30`}`}\n >\n <div\n className={`flex size-9 shrink-0 items-center justify-center rounded-full ${isResolved ? `bg-${textColor}/10 text-${textColor}/50` : `bg-${accentColor}/10 text-${accentColor}`}`}\n >\n {isResolved ? (\n <Check className=\"size-4\" aria-hidden />\n ) : (\n <Icon className=\"size-4\" aria-hidden />\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p\n className={`text-sm leading-snug font-semibold tracking-[-0.01em] text-${textColor} ${isResolved ? \"line-through\" : \"\"}`}\n >\n {catchUp.suggestionTitle}\n </p>\n {catchUp.description && (\n <p\n className={`mt-0.5 line-clamp-2 text-xs leading-snug text-${textColor}/60`}\n >\n {catchUp.description}\n </p>\n )}\n {/* Primary action: route the member to where they act on it.\n Resolution follows once the member acts and the BFF\n regenerates the list. Rows with no destination rely on the\n dismiss control alone. */}\n {destinationSlug && !isResolved && (\n <button\n type=\"button\"\n disabled={isPreview}\n onClick={() => onNavigate?.(destinationSlug)}\n className={`mt-2 inline-flex items-center gap-1.5 rounded-full bg-${accentColor} px-3 py-1 text-xs font-semibold ${accentForegroundClass(accentColor)} transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-50`}\n >\n {getActionLabel(primaryAction!.actionType)}\n <ArrowRight className=\"size-3.5\" aria-hidden />\n </button>\n )}\n </div>\n {/* Universal dismiss — clears any catch-up without side effects,\n so a nudge the member won't act on can still be cleared. */}\n {primaryAction && !isResolved && (\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n title=\"Dismiss\"\n disabled={isPreview || isDismissing}\n onClick={() => {\n const actionId = primaryAction.id;\n setDismissingActionIds((prev) =>\n new Set(prev).add(actionId),\n );\n dismissAction.mutate(\n { catchUpId: catchUp.id, actionId },\n {\n onSettled: () =>\n setDismissingActionIds((prev) => {\n const next = new Set(prev);\n next.delete(actionId);\n return next;\n }),\n },\n );\n }}\n className={`-m-1 shrink-0 rounded-md p-1 text-${textColor}/40 transition-colors hover:bg-${textColor}/5 hover:text-${textColor}/70 disabled:cursor-default disabled:opacity-50`}\n >\n {isDismissing ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : (\n <X className=\"size-4\" aria-hidden />\n )}\n </button>\n )}\n </div>\n );\n })}\n\n {/* Expand / collapse the overflow */}\n {hiddenCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n className={`mt-1 inline-flex items-center justify-center gap-1 rounded-md py-1.5 text-xs font-semibold text-${accentColor} transition-colors hover:bg-${accentColor}/10`}\n >\n {expanded ? \"Show less\" : `Show ${hiddenCount} more`}\n <ChevronDown\n className={`size-3.5 transition-transform ${expanded ? \"rotate-180\" : \"\"}`}\n aria-hidden\n />\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n\n// Property schema for the widget editor\nexport const catchUpWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"CatchUpWidget\",\n displayName: \"Catch Up Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\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 catch ups\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"titleText\",\n label: \"Title\",\n type: \"text\",\n description: \"Title text displayed above the catch ups\",\n defaultValue: \"Catch Ups\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleFontSize\",\n label: \"Title Font Size\",\n description: \"Font size for the widget 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 widget title\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n\n // Styling Tab - Design Group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the container\",\n defaultValue: { type: \"solid\", color: \"background\" },\n tab: \"styling\",\n group: \"Design\",\n },\n getColorField({\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Default text color for catch up items\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description: \"Color used for count display and icons\",\n defaultValue: \"primary\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Design\",\n },\n {\n key: \"maxItems\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of catch ups to display\",\n defaultValue: 5,\n min: 1,\n max: 10,\n tab: \"styling\",\n group: \"Design\",\n },\n {\n key: \"contactScreenSlug\",\n label: \"Contacts Screen\",\n type: \"screenPicker\",\n description:\n \"Screen to open when a catch-up targets a contact (e.g. reply, message).\",\n defaultValue: \"contacts\",\n includeSystemItems: true,\n tab: \"styling\",\n group: \"Navigation\",\n },\n {\n key: \"messagingScreenSlug\",\n label: \"Messaging Screen\",\n type: \"screenPicker\",\n description: \"Screen to open when a catch-up targets a conversation.\",\n defaultValue: \"messages\",\n includeSystemItems: true,\n tab: \"styling\",\n group: \"Navigation\",\n },\n {\n key: \"shopScreenSlug\",\n label: \"Shop Screen\",\n type: \"screenPicker\",\n description: \"Screen to open when a catch-up targets a product.\",\n defaultValue: \"shop\",\n includeSystemItems: true,\n tab: \"styling\",\n group: \"Navigation\",\n },\n {\n key: \"shareablesScreenSlug\",\n label: \"Media Screen\",\n type: \"screenPicker\",\n description: \"Screen to open when a catch-up targets media.\",\n defaultValue: \"share\",\n includeSystemItems: true,\n tab: \"styling\",\n group: \"Navigation\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding around the container\",\n defaultValue: 4,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the container\",\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 ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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tE,eAAA;KACA,CAAA,CACK;MAEP;KAEJ;;;AAKV,MAAa,8BAAoD;CAC/D,YAAY;CACZ,aAAa;CACb,YAAY,CAAC;EAAE,IAAI;EAAW,OAAO;EAAW,CAAC;CACjD,QAAQ;EAEN;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EACF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;IAAE,MAAM;IAAS,OAAO;IAAc;GACpD,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,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;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,KAAK;GACL,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,cAAc;GACd,oBAAoB;GACpB,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,oBAAoB;GACpB,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,oBAAoB;GACpB,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,oBAAoB;GACpB,KAAK;GACL,OAAO;GACR;EACD,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require("./chunk-9hOWP6kD.cjs");
|
|
2
2
|
const require_registry_context = require("./registry-context-DLWP4Bzy.cjs");
|
|
3
|
-
const require_WidgetInteractionContext = require("./WidgetInteractionContext-
|
|
3
|
+
const require_WidgetInteractionContext = require("./WidgetInteractionContext-D9dBJ-39.cjs");
|
|
4
4
|
const require_widgets_api_context = require("./widgets-api-context-CdgvPvNT.cjs");
|
|
5
5
|
const require_registries = require("./registries-MpWTUak_.cjs");
|
|
6
6
|
const require_preview_context = require("./preview-context-DDYz757l.cjs");
|
|
@@ -243,7 +243,7 @@ function CatchUpWidget({ titleEnabled = true, titleText = "Catch Ups", titleFont
|
|
|
243
243
|
const { data = [], isLoading, isError } = useCatchUps();
|
|
244
244
|
const dismissAction = useDismissCatchUpAction();
|
|
245
245
|
const { isPreview } = require_preview_context.useWidgetPreviewContext();
|
|
246
|
-
const { onNavigate } = require_WidgetInteractionContext.useWidgetInteraction();
|
|
246
|
+
const { onNavigate, canNavigateTo } = require_WidgetInteractionContext.useWidgetInteraction();
|
|
247
247
|
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
248
248
|
const [dismissingActionIds, setDismissingActionIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
249
249
|
const screenSlugs = {
|
|
@@ -300,7 +300,8 @@ function CatchUpWidget({ titleEnabled = true, titleText = "Catch Ups", titleFont
|
|
|
300
300
|
const Icon = getCategoryIcon(catchUp.category);
|
|
301
301
|
const isResolved = catchUp.status === "resolved";
|
|
302
302
|
const primaryAction = catchUp.actions.find((action) => action.status === "pending");
|
|
303
|
-
const
|
|
303
|
+
const resolvedSlug = primaryAction?.destination ? buildDestinationSlug(primaryAction.destination, screenSlugs) : null;
|
|
304
|
+
const destinationSlug = resolvedSlug && (!canNavigateTo || canNavigateTo(resolvedSlug)) ? resolvedSlug : null;
|
|
304
305
|
const isDismissing = primaryAction ? dismissingActionIds.has(primaryAction.id) : false;
|
|
305
306
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
306
307
|
className: `flex items-start gap-3 rounded-lg border border-${textColor}/10 p-3 transition-colors ${isResolved ? "opacity-55" : `hover:border-${accentColor}/30`}`,
|
|
@@ -557,4 +558,4 @@ Object.defineProperty(exports, "catchUpWidgetPropertySchema", {
|
|
|
557
558
|
}
|
|
558
559
|
});
|
|
559
560
|
|
|
560
|
-
//# sourceMappingURL=CatchUpWidget-
|
|
561
|
+
//# sourceMappingURL=CatchUpWidget-CqAHpCJP.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CatchUpWidget-CqAHpCJP.cjs","names":["useWidgetsApi","useWidgetPreviewContext","useDataSourceRegistryConfig","ShoppingCart","Package","Star","CalendarClock","PlayCircle","MessageCircle","Sparkles","useWidgetPreviewContext","useWidgetInteraction","borderWidthClasses","borderColorClasses","WidgetLoadingSkeleton","ErrorState","Check","ArrowRight","Loader2","X","ChevronDown","getFontSizeField","getColorField","getPaddingField","getBorderRadiusField","getBorderWidthField","getBorderColorField"],"sources":["../../widgets/src/hooks/use-catchups.preview.ts","../../widgets/src/hooks/use-catchups.ts","../../widgets/src/widgets/CatchUpWidget.tsx"],"sourcesContent":["import type { CatchUp } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport const PREVIEW_DATA: CatchUp[] = [\n {\n id: 1,\n suggestionTitle: \"Check in with Sarah about her recent order\",\n description:\n \"Her last order shipped 5 days ago — a quick thank-you goes a long way.\",\n category: \"left_a_review\",\n status: \"pending\",\n actionCount: 1,\n actions: [\n {\n id: 11,\n actionType: \"SendDirectMessageCatchUpAction\",\n status: \"pending\",\n contact: { id: 501, name: \"Sarah Chen\" },\n destination: { kind: \"contact\", id: 501 },\n },\n ],\n },\n {\n id: 2,\n suggestionTitle: \"Follow up with Mike on product samples\",\n description: \"Mike asked about samples last week and hasn't heard back.\",\n category: \"unread_messages\",\n status: \"pending\",\n actionCount: 1,\n actions: [\n {\n id: 21,\n actionType: \"UnreadMessagesCatchUpAction\",\n status: \"pending\",\n contact: { id: 502, name: \"Mike Ross\" },\n destination: { kind: \"conversation\", id: 9001 },\n },\n ],\n },\n {\n id: 3,\n suggestionTitle: \"New arrivals just dropped\",\n description: \"Fresh products your customers will love — take a look.\",\n category: \"new_product\",\n status: \"pending\",\n actionCount: 1,\n actions: [\n {\n id: 31,\n actionType: \"NewProductCatchUpAction\",\n status: \"pending\",\n contact: null,\n destination: { kind: \"product\", id: 7001 },\n },\n ],\n },\n {\n id: 4,\n suggestionTitle: \"Thank Alex for their referral last week\",\n description: null,\n category: null,\n status: \"resolved\",\n actionCount: 0,\n actions: [],\n },\n {\n id: 5,\n suggestionTitle: \"New training video posted\",\n description: \"A new video was just added to your library.\",\n category: \"new_media_added\",\n status: \"pending\",\n actionCount: 1,\n actions: [\n {\n id: 51,\n actionType: \"ViewMediaCatchUpAction\",\n status: \"pending\",\n contact: null,\n destination: { kind: \"media\", id: 3001 },\n },\n ],\n },\n];\n","import {\n useMutation,\n useQuery,\n useQueryClient,\n type UseMutationResult,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { PREVIEW_DATA } from \"./use-catchups.preview\";\nimport type { CatchUp } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport type { CatchUp } from \"@fluid-app/portal-core/widgets-api-types\";\n\n// The live (non-preview) query key. Kept as a helper so the query and the\n// mutation that mutates its cache can never drift apart.\nfunction catchUpsQueryKey(baseUrl: string | undefined) {\n return [\"portal-widget-use\", \"catchups\", baseUrl] as const;\n}\n\nexport function useCatchUps(): UseQueryResult<CatchUp[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: [\n \"portal-widget-use\",\n \"catchups\",\n isPreview ? \"preview\" : baseUrl,\n ] as const,\n queryFn: ({ signal }) => widgetsApi.fetchCatchUps(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\n });\n}\n\nexport type DismissCatchUpActionVars = {\n catchUpId: number;\n actionId: number;\n};\n\n/**\n * Dismisses one of a catch-up's pending actions through the BFF (a safe resolve\n * with no external side effects), then removes it from the cached list.\n *\n * The cache is edited only on confirmed success — never optimistically and\n * without a refetch. So there is nothing to roll back if it fails (a failed\n * dismiss just leaves the row), nothing a concurrent dismiss can clobber (each\n * success applies an independent, idempotent removal to the current cache), and\n * no invalidation whose failure could turn a successful dismiss into an error.\n * The button shows a pending spinner until the request resolves.\n *\n * A failed dismiss (e.g. the BFF endpoint isn't deployed yet) leaves the row in\n * place and surfaces a toast, rather than silently stopping the spinner with no\n * feedback.\n */\nexport function useDismissCatchUpAction(): UseMutationResult<\n void,\n Error,\n DismissCatchUpActionVars\n> {\n const widgetsApi = useWidgetsApi();\n const { baseUrl } = useDataSourceRegistryConfig();\n const queryClient = useQueryClient();\n const queryKey = catchUpsQueryKey(baseUrl);\n\n return useMutation({\n mutationFn: ({ catchUpId, actionId }: DismissCatchUpActionVars) =>\n widgetsApi.dismissCatchUpAction(catchUpId, actionId),\n onSuccess: (_data, { catchUpId, actionId }) => {\n queryClient.setQueryData<CatchUp[]>(queryKey, (old) =>\n (old ?? [])\n .map((catchUp) =>\n catchUp.id === catchUpId\n ? {\n ...catchUp,\n actions: catchUp.actions.map((action) =>\n action.id === actionId\n ? { ...action, status: \"resolved\" as const }\n : action,\n ),\n }\n : catchUp,\n )\n // Only the just-dismissed catch-up may need removing — drop it once\n // none of its actions remain pending. Every other row is left exactly\n // as it was, so an unrelated catch-up with no pending actions (the\n // widget can render resolved rows) doesn't vanish on this dismiss.\n .filter(\n (catchUp) =>\n catchUp.id !== catchUpId ||\n catchUp.actions.some((action) => action.status === \"pending\"),\n ),\n );\n },\n onError: (error, { catchUpId, actionId }) => {\n console.error(\"[portal-widgets] Failed to dismiss catch-up action\", {\n error,\n catchUpId,\n actionId,\n });\n fluidToast({ title: \"Couldn't dismiss catch-up\", type: \"error\" });\n },\n });\n}\n","import type { ComponentProps } from \"react\";\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport type { CatchUpActionDestination } from \"@fluid-app/portal-core/widgets-api-types\";\nimport {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n getColorField,\n getFontSizeField,\n getPaddingField,\n borderWidthClasses,\n borderColorClasses,\n} from \"../core/fields\";\nimport { useCatchUps, useDismissCatchUpAction } from \"../hooks/use-catchups\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useWidgetInteraction } from \"../contexts/WidgetInteractionContext\";\nimport {\n ArrowRight,\n CalendarClock,\n Check,\n ChevronDown,\n Loader2,\n MessageCircle,\n Package,\n PlayCircle,\n ShoppingCart,\n Sparkles,\n Star,\n X,\n type LucideIcon,\n} from \"lucide-react\";\nimport { ErrorState } from \"../components/error-state\";\nimport { WidgetLoadingSkeleton } from \"../components/WidgetLoadingSkeleton\";\n\n// Font size mapping for title\nconst fontSizeClasses: Record<FontSizeOptions, string> = {\n \"2xl\": \"text-2xl\",\n xl: \"text-xl\",\n lg: \"text-lg\",\n md: \"text-base\",\n sm: \"text-sm\",\n xs: \"text-xs\",\n};\n\n// Maps a catch-up's trigger category (catch_up_class on the BFF) to an icon.\n// Unknown or null categories fall back to the generic Sparkles icon.\nconst categoryIcons: Record<string, LucideIcon> = {\n cart_abandoned: ShoppingCart,\n new_product: Package,\n left_a_review: Star,\n add_review: Star,\n upcoming_subscription_orders: CalendarClock,\n upcoming_event: CalendarClock,\n video_viewed: PlayCircle,\n new_media_added: PlayCircle,\n unread_messages: MessageCircle,\n unresolved_media_comment: MessageCircle,\n};\n\nfunction getCategoryIcon(category: string | null): LucideIcon {\n return (category && categoryIcons[category]) || Sparkles;\n}\n\n// Verb shown on a catch-up's primary action button, keyed on the BFF's STI\n// action class. Unknown types fall back to a generic label so a new backend\n// action type still renders a working button.\nconst actionLabels: Record<string, string> = {\n ResolveConversationCatchUpAction: \"Resolve\",\n UnreadMessagesCatchUpAction: \"Reply\",\n SendDirectMessageCatchUpAction: \"Message\",\n SendSmsCatchUpAction: \"Text\",\n AddReviewCatchUpAction: \"Leave review\",\n AddCommentReactionCatchUpAction: \"React\",\n RsvpCatchUpAction: \"RSVP\",\n ShareSmartLinkCatchUpAction: \"Share\",\n LinkRedirectionCatchUpAction: \"Open\",\n ViewMediaCatchUpAction: \"View\",\n NewProductCatchUpAction: \"View product\",\n MarkAsDoneCatchUpAction: \"Mark done\",\n ToDoCatchUpAction: \"View task\",\n ConfirmSliderValueCatchUpAction: \"Confirm\",\n UpdateContactGroupCatchUpAction: \"Update\",\n};\n\nfunction getActionLabel(actionType: string): string {\n return actionLabels[actionType] ?? \"Do it\";\n}\n\ntype ScreenSlugs = {\n contact: string;\n messaging: string;\n shop: string;\n shareables: string;\n};\n\n// Map a normalized BFF destination to a portal navigation slug. Returns null\n// for kinds the portal can't deep-link (the widget then offers a dismiss).\nfunction buildDestinationSlug(\n destination: CatchUpActionDestination,\n slugs: ScreenSlugs,\n): string | null {\n switch (destination.kind) {\n case \"contact\":\n return `${slugs.contact}/${destination.id}`;\n case \"conversation\":\n return `${slugs.messaging}/conversation/${destination.id}`;\n case \"product\":\n return `${slugs.shop}/${destination.id}`;\n case \"media\":\n // Shareables deep-links a specific medium at \"<share>/media/<id>\".\n return `${slugs.shareables}/media/${destination.id}`;\n default:\n return null;\n }\n}\n\n// Only these semantic colors ship a paired `-foreground` token. For any other\n// ColorOptions (background, foreground, border, transparent) `text-*-foreground`\n// is a class that does not exist, so the filled button's label would fall back\n// to inherited colour. Resolve a legible contrast token instead.\nconst PAIRED_FOREGROUND_COLORS: ReadonlySet<ColorOptions> = new Set([\n \"primary\",\n \"secondary\",\n \"accent\",\n \"muted\",\n \"destructive\",\n]);\n\nfunction accentForegroundClass(accent: ColorOptions): string {\n if (PAIRED_FOREGROUND_COLORS.has(accent)) return `text-${accent}-foreground`;\n return accent === \"foreground\" ? \"text-background\" : \"text-foreground\";\n}\n\ntype CatchUpWidgetProps = ComponentProps<\"div\"> & {\n // Title settings\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n // Design settings\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n // Display settings\n maxItems?: number;\n // Slugs of the screens a catch-up routes to, by destination kind.\n contactScreenSlug?: string;\n messagingScreenSlug?: string;\n shopScreenSlug?: string;\n shareablesScreenSlug?: string;\n};\n\nexport function CatchUpWidget({\n // Title settings with defaults\n titleEnabled = true,\n titleText = \"Catch Ups\",\n titleFontSize = \"xl\",\n titleColor = \"foreground\",\n // Design settings with defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n borderWidth = \"none\",\n borderColor = \"muted\",\n // Display settings with defaults\n maxItems = 5,\n contactScreenSlug = \"contacts\",\n messagingScreenSlug = \"messages\",\n shopScreenSlug = \"shop\",\n shareablesScreenSlug = \"share\",\n className,\n ...props\n}: CatchUpWidgetProps): React.JSX.Element {\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 const { data = [], isLoading, isError } = useCatchUps();\n const dismissAction = useDismissCatchUpAction();\n const { isPreview } = useWidgetPreviewContext();\n const { onNavigate, canNavigateTo } = useWidgetInteraction();\n const [expanded, setExpanded] = useState(false);\n // The dismiss mutation is shared by every row, so its `variables`/`isPending`\n // only reflect the most recent click. Track in-flight actions ourselves so a\n // second dismiss can't clear an earlier row's spinner or re-enable it mid-flight.\n const [dismissingActionIds, setDismissingActionIds] = useState<\n ReadonlySet<number>\n >(() => new Set());\n\n const screenSlugs: ScreenSlugs = {\n contact: contactScreenSlug,\n messaging: messagingScreenSlug,\n shop: shopScreenSlug,\n shareables: shareablesScreenSlug,\n };\n\n // Collapsed shows the first `maxItems`; expanded shows everything. The toggle\n // count is independent of `expanded` so the control stays visible to collapse.\n const catchUpsToShow = expanded ? data : data.slice(0, maxItems);\n const hiddenCount = Math.max(0, data.length - maxItems);\n\n return (\n <div\n className={`flex flex-col rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} bg-${backgroundColor} p-${padding} text-${textColor} ${className || \"\"}`}\n style={{ backgroundImage }}\n {...props}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between gap-3\">\n {titleEnabled && (\n <h3\n className={`${fontSizeClasses[titleFontSize]} font-header font-bold tracking-[-0.01em] text-${titleColor}`}\n >\n {titleText}\n </h3>\n )}\n {!isLoading && !isError && data.length > 0 && (\n <span\n className={`inline-flex min-w-[1.5rem] items-center justify-center rounded-full bg-${accentColor}/10 px-2 py-0.5 text-xs font-bold tabular-nums text-${accentColor}`}\n >\n {data.length}\n </span>\n )}\n </div>\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"mt-3 flex-1\">\n <WidgetLoadingSkeleton minHeight={150} rows={3} />\n </div>\n ) : isError ? (\n /* Error state */\n <div className=\"mt-3 flex-1\">\n <ErrorState />\n </div>\n ) : data.length === 0 ? (\n /* Empty state */\n <div className=\"flex min-h-[150px] flex-1 flex-col items-center justify-center gap-3 py-6\">\n <div\n className={`flex size-12 items-center justify-center rounded-full bg-${accentColor}/10`}\n >\n <Sparkles className={`size-5 text-${accentColor}`} aria-hidden />\n </div>\n <div className=\"text-center\">\n <p className={`text-sm font-semibold text-${textColor}`}>\n You're all caught up\n </p>\n <p className={`mt-0.5 text-xs text-${textColor}/55`}>\n Nothing needs your attention right now.\n </p>\n </div>\n </div>\n ) : (\n /* Default state with catch ups */\n <div className=\"mt-3 flex flex-1 flex-col gap-2\">\n {catchUpsToShow.map((catchUp, index: number) => {\n const Icon = getCategoryIcon(catchUp.category);\n const isResolved = catchUp.status === \"resolved\";\n // The row's primary action is the first still-actionable one.\n const primaryAction = catchUp.actions.find(\n (action) => action.status === \"pending\",\n );\n const resolvedSlug = primaryAction?.destination\n ? buildDestinationSlug(primaryAction.destination, screenSlugs)\n : null;\n // Suppress the deep-link when it would dead-end on \"Page Not Found\"\n // (an opt-in screen like a contact/conversation the portal hasn't\n // added to its nav). The row then falls back to the dismiss control.\n const destinationSlug =\n resolvedSlug && (!canNavigateTo || canNavigateTo(resolvedSlug))\n ? resolvedSlug\n : null;\n const isDismissing = primaryAction\n ? dismissingActionIds.has(primaryAction.id)\n : false;\n return (\n <div\n key={catchUp.id || index}\n className={`flex items-start gap-3 rounded-lg border border-${textColor}/10 p-3 transition-colors ${isResolved ? \"opacity-55\" : `hover:border-${accentColor}/30`}`}\n >\n <div\n className={`flex size-9 shrink-0 items-center justify-center rounded-full ${isResolved ? `bg-${textColor}/10 text-${textColor}/50` : `bg-${accentColor}/10 text-${accentColor}`}`}\n >\n {isResolved ? (\n <Check className=\"size-4\" aria-hidden />\n ) : (\n <Icon className=\"size-4\" aria-hidden />\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p\n className={`text-sm leading-snug font-semibold tracking-[-0.01em] text-${textColor} ${isResolved ? \"line-through\" : \"\"}`}\n >\n {catchUp.suggestionTitle}\n </p>\n {catchUp.description && (\n <p\n className={`mt-0.5 line-clamp-2 text-xs leading-snug text-${textColor}/60`}\n >\n {catchUp.description}\n </p>\n )}\n {/* Primary action: route the member to where they act on it.\n Resolution follows once the member acts and the BFF\n regenerates the list. Rows with no destination rely on the\n dismiss control alone. */}\n {destinationSlug && !isResolved && (\n <button\n type=\"button\"\n disabled={isPreview}\n onClick={() => onNavigate?.(destinationSlug)}\n className={`mt-2 inline-flex items-center gap-1.5 rounded-full bg-${accentColor} px-3 py-1 text-xs font-semibold ${accentForegroundClass(accentColor)} transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-50`}\n >\n {getActionLabel(primaryAction!.actionType)}\n <ArrowRight className=\"size-3.5\" aria-hidden />\n </button>\n )}\n </div>\n {/* Universal dismiss — clears any catch-up without side effects,\n so a nudge the member won't act on can still be cleared. */}\n {primaryAction && !isResolved && (\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n title=\"Dismiss\"\n disabled={isPreview || isDismissing}\n onClick={() => {\n const actionId = primaryAction.id;\n setDismissingActionIds((prev) =>\n new Set(prev).add(actionId),\n );\n dismissAction.mutate(\n { catchUpId: catchUp.id, actionId },\n {\n onSettled: () =>\n setDismissingActionIds((prev) => {\n const next = new Set(prev);\n next.delete(actionId);\n return next;\n }),\n },\n );\n }}\n className={`-m-1 shrink-0 rounded-md p-1 text-${textColor}/40 transition-colors hover:bg-${textColor}/5 hover:text-${textColor}/70 disabled:cursor-default disabled:opacity-50`}\n >\n {isDismissing ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : (\n <X className=\"size-4\" aria-hidden />\n )}\n </button>\n )}\n </div>\n );\n })}\n\n {/* Expand / collapse the overflow */}\n {hiddenCount > 0 && (\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n className={`mt-1 inline-flex items-center justify-center gap-1 rounded-md py-1.5 text-xs font-semibold text-${accentColor} transition-colors hover:bg-${accentColor}/10`}\n >\n {expanded ? \"Show less\" : `Show ${hiddenCount} more`}\n <ChevronDown\n className={`size-3.5 transition-transform ${expanded ? \"rotate-180\" : \"\"}`}\n aria-hidden\n />\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n\n// Property schema for the widget editor\nexport const catchUpWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"CatchUpWidget\",\n displayName: \"Catch Up Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\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 catch ups\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"titleText\",\n label: \"Title\",\n type: \"text\",\n description: \"Title text displayed above the catch ups\",\n defaultValue: \"Catch Ups\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleFontSize\",\n label: \"Title Font Size\",\n description: \"Font size for the widget 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 widget title\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n\n // Styling Tab - Design Group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the container\",\n defaultValue: { type: \"solid\", color: \"background\" },\n tab: \"styling\",\n group: \"Design\",\n },\n getColorField({\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Default text color for catch up items\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description: \"Color used for count display and icons\",\n defaultValue: \"primary\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Design\",\n },\n {\n key: \"maxItems\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of catch ups to display\",\n defaultValue: 5,\n min: 1,\n max: 10,\n tab: \"styling\",\n group: \"Design\",\n },\n {\n key: \"contactScreenSlug\",\n label: \"Contacts Screen\",\n type: \"screenPicker\",\n description:\n \"Screen to open when a catch-up targets a contact (e.g. reply, message).\",\n defaultValue: \"contacts\",\n includeSystemItems: true,\n tab: \"styling\",\n group: \"Navigation\",\n },\n {\n key: \"messagingScreenSlug\",\n label: \"Messaging Screen\",\n type: \"screenPicker\",\n description: \"Screen to open when a catch-up targets a conversation.\",\n defaultValue: \"messages\",\n includeSystemItems: true,\n tab: \"styling\",\n group: \"Navigation\",\n },\n {\n key: \"shopScreenSlug\",\n label: \"Shop Screen\",\n type: \"screenPicker\",\n description: \"Screen to open when a catch-up targets a product.\",\n defaultValue: \"shop\",\n includeSystemItems: true,\n tab: \"styling\",\n group: \"Navigation\",\n },\n {\n key: \"shareablesScreenSlug\",\n label: \"Media Screen\",\n type: \"screenPicker\",\n description: \"Screen to open when a catch-up targets media.\",\n defaultValue: \"share\",\n includeSystemItems: true,\n tab: \"styling\",\n group: \"Navigation\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding around the container\",\n defaultValue: 4,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the container\",\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 ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;IAAE,MAAM;IAAS,OAAO;IAAc;GACpD,KAAK;GACL,OAAO;GACR;EACDA,mBAAAA,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFA,mBAAAA,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;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,KAAK;GACL,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aACE;GACF,cAAc;GACd,oBAAoB;GACpB,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,oBAAoB;GACpB,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,oBAAoB;GACpB,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,cAAc;GACd,oBAAoB;GACpB,KAAK;GACL,OAAO;GACR;EACDC,mBAAAA,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACFC,mBAAAA,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require("./chunk-9hOWP6kD.cjs");
|
|
2
2
|
require("./registry-context-DLWP4Bzy.cjs");
|
|
3
|
-
require("./WidgetInteractionContext-
|
|
3
|
+
require("./WidgetInteractionContext-D9dBJ-39.cjs");
|
|
4
4
|
require("./translation-api-context-factory-mbsJk2UU.cjs");
|
|
5
5
|
require("./shell-translation-api-context-BxZ1W6Eb.cjs");
|
|
6
6
|
require("./widgets-api-context-CdgvPvNT.cjs");
|
|
@@ -10,5 +10,5 @@ require("./preview-context-DDYz757l.cjs");
|
|
|
10
10
|
require("./error-state-9EyOWmGK.cjs");
|
|
11
11
|
require("./src-B6YzMK6y.cjs");
|
|
12
12
|
require("./WidgetLoadingSkeleton-CyBzfeHa.cjs");
|
|
13
|
-
const require_CatchUpWidget = require("./CatchUpWidget-
|
|
13
|
+
const require_CatchUpWidget = require("./CatchUpWidget-CqAHpCJP.cjs");
|
|
14
14
|
exports.catchUpWidgetPropertySchema = require_CatchUpWidget.catchUpWidgetPropertySchema;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Vn as __exportAll } from "./src-BfINy9nC.mjs";
|
|
2
|
-
import { C as DEFAULT_SDK_WIDGET_REGISTRY, Et as createPersister, G as applyTheme, St as PayApiProvider, W as OrdersCoreProvider, _t as isRemoteDomWidgetPackageDescriptor, bt as LanguagesApiProvider, ft as resolveTheme, n as buildRemoteWidgetRegistry, o as isDevBuild, q as removeTheme, vt as AddressFieldsApiProvider, wt as AppDefinitionApiProvider } from "./build-widget-registry-
|
|
2
|
+
import { C as DEFAULT_SDK_WIDGET_REGISTRY, Et as createPersister, G as applyTheme, St as PayApiProvider, W as OrdersCoreProvider, _t as isRemoteDomWidgetPackageDescriptor, bt as LanguagesApiProvider, ft as resolveTheme, n as buildRemoteWidgetRegistry, o as isDevBuild, q as removeTheme, vt as AddressFieldsApiProvider, wt as AppDefinitionApiProvider } from "./build-widget-registry-70KBtqiR.mjs";
|
|
3
3
|
import { f as DataSourceApiProvider, u as RegistryProvider } from "./ScreenRenderer-saqQPGx2.mjs";
|
|
4
4
|
import { a as AccountApiProvider } from "./use-account-DhZ3gaOS.mjs";
|
|
5
5
|
import { a as StoreApiProvider } from "./use-optional-store-DpB_jK4Q.mjs";
|
|
@@ -10,7 +10,7 @@ import { a as createClient, n as PortalContactsApiProvider, o as createHeyApiErr
|
|
|
10
10
|
import { $ as productsSearch, A as usePortalTenantClient, B as calendarEventsList, D as LegacyPortalTenantClientProvider, F as activitiesList, G as enrollmentPackByVisits, H as catchUpActionsDismiss, I as activitiesRead, J as ordersShow, K as enrollmentPacksShow, L as addressFieldsShow, N as accountShow, O as PortalTenantClientProvider, P as accountUpdate, Q as productsMediaList, R as appManifestShow, U as catchUpsList, W as enrollmentPackByShares, X as productByVisits, Y as productByShares, Z as productsList, _t as todosList, et as productsShow, gt as todosDestroy, ht as todosCreate, j as PortalTenantContentClientProvider, k as useLegacyPortalTenantClient, q as ordersList, s as PortalContentApiProvider, st as subscriptionsList, tt as storeShow, vt as todosUpdate, yt as portalTenantContentTransport, z as appWidgetPackagesList } from "./prop-types-BK4OBoZZ.mjs";
|
|
11
11
|
import { r as SheetSnapHapticProvider } from "./MobileBottomSheet-BKm3ysnd.mjs";
|
|
12
12
|
import { t as DataSourceRegistryProvider } from "./registry-context-DeQRPEBN.mjs";
|
|
13
|
-
import { i as PortalUrlVariablesProvider } from "./QuickLinksWidget-
|
|
13
|
+
import { i as PortalUrlVariablesProvider } from "./QuickLinksWidget-BiEUvm_J.mjs";
|
|
14
14
|
import { a as useActiveLocale, i as LocaleProvider, n as createDomainTranslations, r as useDomainDict, t as createStaticDictAdapter } from "./static-dict-adapter-DUyazFbx.mjs";
|
|
15
15
|
import { C as PortalProductsCoreProvider } from "./SearchSort-Bff2iTtF.mjs";
|
|
16
16
|
import { n as ShellTranslationProvider } from "./shell-translation-api-context-CCQ7vCA6.mjs";
|
|
@@ -8742,4 +8742,4 @@ function FluidDataSourceRegistryProvider({ baseUrl, getApiHeaders, variables, ch
|
|
|
8742
8742
|
//#endregion
|
|
8743
8743
|
export { useHaptics as _, messagingDomain as a, createSubscriptionsTranslationAdapter as c, ordersDomain as d, shellEn as f, useHapticCallback as g, getFluidNativeBridge as h, createMessagingTranslationAdapter as i, subscriptionsDomain as l, useThemeContext as m, SUPPORTED_LANGUAGES as n, createProfileTranslationAdapter as o, FluidThemeProvider as p, resolveLocale as r, profileDomain as s, FluidProvider as t, createOrdersTranslationAdapter as u, useNativeCapabilities as v, useAddressAutocompleteApiOptional as y };
|
|
8744
8744
|
|
|
8745
|
-
//# sourceMappingURL=FluidProvider-
|
|
8745
|
+
//# sourceMappingURL=FluidProvider-BRgNyDk3.mjs.map
|