@anker-in/headless-ui 1.3.0 → 1.3.2
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/cjs/biz-components/HeroBanner/HeroBanner.d.ts +1 -1
- package/dist/cjs/biz-components/HeroBanner/HeroBanner.js +1 -1
- package/dist/cjs/biz-components/HeroBanner/HeroBanner.js.map +3 -3
- package/dist/cjs/biz-components/HeroBanner/HeroBannerCarousel.d.ts +1 -0
- package/dist/cjs/biz-components/HeroBanner/HeroBannerCarousel.js +1 -1
- package/dist/cjs/biz-components/HeroBanner/HeroBannerCarousel.js.map +3 -3
- package/dist/cjs/biz-components/HeroBanner/types.d.ts +15 -0
- package/dist/cjs/biz-components/HeroBanner/types.js +1 -1
- package/dist/cjs/biz-components/HeroBanner/types.js.map +1 -1
- package/dist/cjs/biz-components/WebPushPopup/index.d.ts +44 -0
- package/dist/cjs/biz-components/WebPushPopup/index.js +2 -0
- package/dist/cjs/biz-components/WebPushPopup/index.js.map +7 -0
- package/dist/cjs/biz-components/index.d.ts +4 -0
- package/dist/cjs/biz-components/index.js +1 -1
- package/dist/cjs/biz-components/index.js.map +3 -3
- package/dist/cjs/hooks/useEmarsysWebPush.d.ts +111 -0
- package/dist/cjs/hooks/useEmarsysWebPush.js +2 -0
- package/dist/cjs/hooks/useEmarsysWebPush.js.map +7 -0
- package/dist/esm/biz-components/HeroBanner/HeroBanner.d.ts +1 -1
- package/dist/esm/biz-components/HeroBanner/HeroBanner.js +1 -1
- package/dist/esm/biz-components/HeroBanner/HeroBanner.js.map +3 -3
- package/dist/esm/biz-components/HeroBanner/HeroBannerCarousel.d.ts +1 -0
- package/dist/esm/biz-components/HeroBanner/HeroBannerCarousel.js +1 -1
- package/dist/esm/biz-components/HeroBanner/HeroBannerCarousel.js.map +3 -3
- package/dist/esm/biz-components/HeroBanner/types.d.ts +15 -0
- package/dist/esm/biz-components/WebPushPopup/index.d.ts +44 -0
- package/dist/esm/biz-components/WebPushPopup/index.js +2 -0
- package/dist/esm/biz-components/WebPushPopup/index.js.map +7 -0
- package/dist/esm/biz-components/index.d.ts +4 -0
- package/dist/esm/biz-components/index.js +1 -1
- package/dist/esm/biz-components/index.js.map +3 -3
- package/dist/esm/hooks/useEmarsysWebPush.d.ts +111 -0
- package/dist/esm/hooks/useEmarsysWebPush.js +2 -0
- package/dist/esm/hooks/useEmarsysWebPush.js.map +7 -0
- package/package.json +4 -2
- package/static/emarsys-service-worker.js +31 -0
- package/style.css +62 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/biz-components/HeroBanner/HeroBanner.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\nimport React, { useImperativeHandle, useRef, useState, useEffect } from 'react'\nimport gsap from 'gsap'\nimport { ScrollTrigger } from 'gsap/dist/ScrollTrigger'\nimport jump from 'jump.js'\nimport type { HeroBannerProps } from './types.js'\nimport { useMediaQuery } from 'react-responsive'\nimport { useInView } from 'react-intersection-observer'\nimport ScrollLoadVideo from '../../helpers/ScrollLoadVideo.js'\nimport { Button, Heading, Picture, Text, Countdown } from '../../components/index.js'\nimport { cn } from '../../helpers/index.js'\nimport { cva } from 'class-variance-authority'\nimport { withLayout } from '../../shared/Styles.js'\nimport { useExposure } from '../../hooks/useExposure.js'\nimport { trackUrlRef } from '../../shared/trackUrlRef.js'\nimport { getLocalizedPath } from '../../helpers/utils.js'\nimport { useAiuiContext } from '../AiuiProvider/index.js'\nimport { sizeMap } from '../../components/button.js'\nimport { VideoModal } from '../VideoModal/index.js'\n// Task 18: import carousel implementation for early-return delegation (DECISION-A002)\nimport HeroBannerCarousel from './HeroBannerCarousel.js'\n\n/**\n * jump.js \u914D\u7F6E\u9009\u9879\n */\nexport interface JumpOptions {\n /** \u52A8\u753B\u6301\u7EED\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09 @default 500 */\n duration?: number\n /** \u504F\u79FB\u91CF\uFF08\u50CF\u7D20\uFF09 @default 0 */\n offset?: number\n /** \u52A8\u753B\u5B8C\u6210\u56DE\u8C03 */\n callback?: () => void\n /** \u7F13\u52A8\u51FD\u6570 */\n easing?: (t: number, b: number, c: number, d: number) => number\n /** \u662F\u5426\u8003\u8651 a11y @default false */\n a11y?: boolean\n}\n\nconst componentType = 'image'\nconst componentName = 'hero_banner'\n\n// CVA variants for align\nconst contentVariants = cva(\n 'hero-banner-content lg-desktop:gap-[32px] absolute top-24 z-10 flex w-full flex-col gap-[24px]',\n {\n variants: {\n align: {\n left: 'tablet:px-[32px] laptop:top-1/2 laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] laptop:-translate-y-1/2 left-0 px-[16px]',\n center:\n 'tablet:top-[84px] laptop:top-[76px] desktop:top-[176px] lg-desktop:top-[192px] left-1/2 top-[84px] -translate-x-1/2 translate-y-0 items-center text-center',\n },\n },\n defaultVariants: {\n align: 'left',\n },\n }\n)\n\nconst textVariants = cva(\n 'hero-banner-wrap-text lg-desktop:max-w-[824px] desktop:max-w-[648px] laptop:max-w-[440px] tablet:max-w-[704px] max-w-[358px]',\n {\n variants: {\n align: {\n left: 'laptop:text-left',\n center: 'text-center',\n },\n },\n defaultVariants: {\n align: 'left',\n },\n }\n)\n\nconst buttonGroupVariants = cva('hero-banner-button-group lg-desktop:gap-3 flex items-center gap-2', {\n variants: {\n align: {\n left: 'laptop:justify-start',\n center: 'justify-center',\n },\n },\n defaultVariants: {\n align: 'left',\n },\n})\n\nconst iconGroupVariants = cva('hero-banner-icon-group flex items-center gap-2', {\n variants: {\n align: {\n left: 'justify-start',\n center: 'justify-center',\n },\n },\n defaultVariants: {\n align: 'left',\n },\n})\n\nexport type HeroBannerSemanticName =\n | 'root'\n | 'title'\n | 'subtitle'\n | 'buttonGroup'\n | 'primaryButton'\n | 'secondaryButton'\n | 'captionGroup'\n\nconst PlayButtonAppendIcon = ({ size = 'base' }: { size: 'base' | 'lg' | 'sm' }) => {\n const { width, height } = sizeMap[size]\n return (\n <svg width={width} height={height} viewBox=\"0 0 20 20\" fill=\"currentcolor\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M13.9599 9.30662C14.4547 9.63647 14.4547 10.3635 13.9599 10.6934L6.29558 15.8029C5.74179 16.1721 5 15.7751 5 15.1096V4.89042C5 4.22484 5.74179 3.82785 6.29558 4.19705L13.9599 9.30662Z\"\n fill=\"currentcolor\"\n />\n </svg>\n )\n}\n\nconst HeroBanner = React.forwardRef<\n HTMLDivElement,\n HeroBannerProps & {\n classNames?: Partial<Record<HeroBannerSemanticName, string>>\n /**\n * \u951A\u70B9\u8DF3\u8F6C\u914D\u7F6E\uFF08\u7528\u4E8E # \u5F00\u5934\u7684\u94FE\u63A5\uFF09\n * @default { duration: 500, offset: 0 }\n */\n jumpOptions?: JumpOptions\n }\n>(({ data, className, classNames = {}, onSecondaryClick, onPrimaryClick, jumpOptions = {}, ...rest }, ref) => {\n const { locale } = useAiuiContext()\n\n // \u9ED8\u8BA4 jump \u914D\u7F6E\n const defaultJumpOptions: JumpOptions = { duration: 500, offset: 0 }\n const mergedJumpOptions = { ...defaultJumpOptions, ...jumpOptions }\n\n // \u5305\u88C5\u94FE\u63A5\uFF0C\u81EA\u52A8\u6DFB\u52A0 locale \u524D\u7F00\n const localizeUrl = (url?: string) => {\n if (!url || !locale) return url\n return getLocalizedPath(url, locale)\n }\n\n const {\n label,\n title,\n subtitle,\n endDate,\n endDate_tz,\n dateFormat,\n pcImage,\n padImage,\n mobileImage,\n pcVideo,\n padVideo,\n mobileVideo,\n isShowVideo,\n isVideoLoop = true,\n primaryButton,\n secondaryButton,\n theme = 'light',\n size = 'default',\n titleSize,\n caption = [],\n blockLink,\n iconArray,\n align = 'left',\n enableParallax = true,\n } = data\n\n const safeCaption = Array.isArray(caption) ? caption : []\n const safeIconArray = Array.isArray(iconArray) ? iconArray : undefined\n\n const isMobile = useMediaQuery({ query: '(max-width: 768px)' })\n const isPad = useMediaQuery({ query: '(max-width: 1024px)' })\n const [visible, setVisible] = useState<boolean>(false)\n const { ref: inViewRef, inView } = useInView()\n const scrollTriggerRef = useRef<ScrollTrigger | null>(null)\n const bgTriggerRef = useRef<ScrollTrigger | null>(null)\n const boxTriggerRef = useRef<ScrollTrigger | null>(null)\n\n const bgRef = useRef<HTMLImageElement>(null)\n const boxRef = useRef<HTMLDivElement>(null)\n\n // Task 7: Compute accessibility / tracking fallback values (Rule 2/3/4 \u2014 DECISION-T3)\n // blockLink aria-label: title \u2192 subtitle \u2192 pcImage.alt \u2192 undefined (absent attr)\n const blockLinkLabel = title?.trim() || subtitle?.trim() || pcImage?.alt?.trim() || undefined\n // componentTitle: title=undefined \u2192 coerce '' via subtitle only (no pcImage.alt for undefined title);\n // title='' (intentional) \u2192 cascade: subtitle \u2192 pcImage.alt \u2192 undefined\n const resolvedComponentTitle =\n title !== undefined\n ? title?.trim() || subtitle?.trim() || pcImage?.alt?.trim() || undefined\n : (subtitle && subtitle.trim()) || ''\n // componentDescription: subtitle with undefined guard\n const resolvedComponentDescription = (subtitle && subtitle.trim()) || ''\n\n useExposure(boxRef, {\n componentType,\n componentName,\n componentTitle: resolvedComponentTitle,\n componentDescription: resolvedComponentDescription,\n })\n\n useImperativeHandle(ref, () => boxRef.current as HTMLDivElement)\n\n useEffect(() => {\n // Task 18: GSAP bypass \u2014 skip ALL GSAP setup in carousel mode (DECISION-A002/DECISION-009)\n if (data.items && data.items.length > 0) return\n if (!enableParallax) return\n gsap.registerPlugin(ScrollTrigger)\n function gsapResize() {\n if (!bgRef.current) return\n const clientHeight = boxRef.current?.clientHeight || 100\n const screenHeight = window.innerHeight\n\n if (screenHeight <= clientHeight) {\n scrollTriggerRef.current = ScrollTrigger.create({\n trigger: boxRef.current,\n start: 'top bottom',\n end: 'bottom top',\n scrub: true,\n onUpdate: (self: any) => {\n const base = 40\n const value = self.progress * base - base / 2\n gsap.set(bgRef.current, { yPercent: value })\n },\n })\n } else {\n boxTriggerRef.current = ScrollTrigger.create({\n trigger: boxRef.current,\n start: 'top bottom',\n end: 'bottom bottom',\n scrub: true,\n onUpdate: (self: any) => {\n const base = 20\n const value = self.progress * base - base\n gsap.set(bgRef.current, { yPercent: value })\n },\n })\n bgTriggerRef.current = ScrollTrigger.create({\n trigger: boxRef.current,\n start: 'top top',\n end: 'bottom top',\n scrub: true,\n onUpdate: (self: any) => {\n const base = 20\n const value = self.progress * base\n gsap.set(bgRef.current, { yPercent: value })\n },\n })\n }\n }\n if (inView) gsapResize()\n return () => {\n // ScrollTrigger.getAll().forEach((t: any) => t.kill())\n scrollTriggerRef.current && scrollTriggerRef.current.kill()\n boxTriggerRef.current && boxTriggerRef.current.kill()\n bgTriggerRef.current && bgTriggerRef.current.kill()\n }\n }, [inView, data.items, enableParallax])\n\n // Task 18: Carousel mode early return (DECISION-A002 / DECISION-011)\n // All hooks above have already run. Delegate entirely to HeroBannerCarousel\n // when data.items is non-empty; GSAP was already bypassed in the useEffect above.\n if (data.items && data.items.length > 0) {\n return (\n <HeroBannerCarousel\n data={data}\n className={className}\n classNames={classNames}\n onPrimaryClick={onPrimaryClick}\n onSecondaryClick={onSecondaryClick}\n ref={ref as React.Ref<HTMLDivElement>}\n {...rest}\n />\n )\n }\n return (\n <div {...rest} ref={inViewRef} data-ui-component-id=\"HeroBanner\">\n <div\n ref={boxRef}\n className={cn(\n theme === 'dark' ? 'aiui-dark' : '',\n 'text-info-primary relative w-full overflow-hidden',\n {\n 'lg-desktop:aspect-[1920/930] desktop:aspect-[1440/700] laptop:aspect-[1024/520] tablet:aspect-[768/660] aspect-[390/660]':\n size === 'default',\n 'lg-desktop:aspect-[1920/800] desktop:aspect-[1440/640] laptop:aspect-[1024/480] tablet:aspect-[768/560] aspect-[390/560]':\n size === 'sm',\n },\n className\n )}\n >\n {blockLink && (\n <a\n className=\"absolute inset-0 z-10\"\n href={trackUrlRef(localizeUrl(blockLink), `${componentType}_${componentName}`)}\n data-headless-type-name={`${componentType}#${componentName}`}\n data-headless-title-desc-button={`${title}#${subtitle}`}\n tabIndex={-1}\n aria-hidden=\"true\"\n aria-label={blockLinkLabel}\n ></a>\n )}\n <div ref={bgRef} className={cn('absolute left-0 top-0 size-full')}>\n {isShowVideo ? (\n <>\n {/* Poster cover layer: eager + high priority, decoupled from JS lazy-load, participates in LCP */}\n {/* Wrap in div so data-video-poster + transition-opacity are on the container, not the <img> */}\n <div\n data-video-poster=\"true\"\n className=\"laptop:w-full absolute inset-0 h-full transition-opacity duration-500\"\n >\n <Picture\n className=\"absolute inset-0 size-full\"\n imgClassName=\"size-full object-cover\"\n loading=\"eager\"\n fetchPriority=\"high\"\n alt={pcImage?.alt || ''}\n source={`${pcImage?.url || ''} , ${padImage?.url ?? (mobileImage?.url || '')} 1024, ${mobileImage?.url || ''} 767`}\n />\n </div>\n {/* Video layer: lazy-loaded, overlays Picture via DOM order (both z-auto, video is after Picture) */}\n <ScrollLoadVideo\n poster={undefined}\n src={\n isMobile\n ? (mobileVideo?.url as string)\n : isPad\n ? (padVideo?.url as string) || (mobileVideo?.url as string)\n : (pcVideo?.url as string)\n }\n className=\"laptop:w-full absolute inset-0 h-full\"\n videoClassName=\"h-full object-cover\"\n muted\n loop={isVideoLoop}\n playsInline\n onPlaying={(e: React.SyntheticEvent<HTMLVideoElement>) => {\n const banner = (e.currentTarget as HTMLVideoElement).closest('[data-ui-component-id=\"HeroBanner\"]')\n banner?.querySelector<HTMLElement>('[data-video-poster=\"true\"]')?.classList.add('opacity-0')\n }}\n />\n </>\n ) : (\n <Picture\n className=\"laptop:w-full h-full\"\n imgClassName=\"h-full object-cover\"\n loading=\"eager\"\n fetchPriority=\"high\"\n alt={pcImage?.alt || ''}\n source={`${pcImage?.url || ''} , ${padImage?.url ?? (mobileImage?.url || '')} 1024, ${mobileImage?.url || ''} 767`}\n />\n )}\n </div>\n\n {/* \u5185\u5BB9\u533A\u57DF */}\n <div className={contentVariants({ align })}>\n <div className={textVariants({ align })}>\n {label && (\n <Text\n size={2}\n as=\"p\"\n className={cn('hero-banner-label font-heading lg-desktop:text-[18px] desktop:text-base text-sm')}\n html={label}\n />\n )}\n {title && (\n <Heading\n as={titleSize === '4' ? 'h1' : 'h2'}\n html={title}\n className={cn('hero-banner-title', classNames.title)}\n size={titleSize ? (Number(titleSize || '5') as any) : size === 'sm' ? 4 : 5}\n />\n )}\n {subtitle && (\n <Text\n as=\"p\"\n size={2}\n className={cn(\n 'hero-banner-subtitle font-heading lg-desktop:text-[18px] desktop:text-base laptop:mt-2 lg-desktop:mt-4 mt-1 text-sm',\n classNames.subtitle\n )}\n html={subtitle}\n />\n )}\n {endDate && (\n <div className=\"mt-3\">\n <Countdown\n endDate={endDate}\n endDate_tz={endDate_tz}\n dateFormat={dateFormat}\n variant=\"spacious\"\n align={align}\n />\n </div>\n )}\n </div>\n {/* \u6309\u94AE\u7EC4 */}\n <div className={cn(buttonGroupVariants({ align }), classNames.buttonGroup)}>\n {secondaryButton?.isShowPlayVideoButton && secondaryButton?.playVideoButtonText ? (\n <Button\n onClick={() => setVisible(true)}\n size=\"lg\"\n variant=\"secondary\"\n className=\"hero-banner-play-video-button\"\n data-headless-type-name={`${componentType}#${componentName}`}\n data-headless-title-desc-button={`${title}#${subtitle}#${secondaryButton?.playVideoButtonText}`}\n >\n {secondaryButton?.playVideoButtonText} <PlayButtonAppendIcon size=\"lg\" />\n </Button>\n ) : secondaryButton?.text ? (\n <Button\n aria-label={title ?? subtitle}\n size=\"lg\"\n variant=\"secondary\"\n className={cn('hero-banner-secondary-button', classNames.secondaryButton)}\n as={secondaryButton?.isCustomSecondaryButton ? 'button' : 'a'}\n href={trackUrlRef(localizeUrl(secondaryButton?.link), `${componentType}_${componentName}`)}\n target={secondaryButton?.target}\n rel={\n secondaryButton?.target === '_blank'\n ? (secondaryButton?.rel ?? 'noopener noreferrer')\n : secondaryButton?.rel\n }\n onClick={e => {\n if (secondaryButton.link?.startsWith('#')) {\n e.preventDefault()\n const element = document.querySelector(secondaryButton.link)\n if (element) {\n jump(element as HTMLElement, mergedJumpOptions)\n }\n }\n secondaryButton?.isCustomSecondaryButton &&\n onSecondaryClick?.(data, e, secondaryButton?.customSecondaryEventId)\n }}\n data-headless-type-name={`${componentType}#${componentName}`}\n data-headless-title-desc-button={`${title}#${subtitle}#${secondaryButton?.text}`}\n >\n {secondaryButton?.text}\n <span className=\"sr-only\">{title ?? subtitle}</span>\n </Button>\n ) : null}\n {primaryButton && primaryButton.text && (\n <Button\n aria-label={title ?? subtitle}\n size=\"lg\"\n variant=\"primary\"\n className={cn('hero-banner-primary-button', classNames.primaryButton)}\n as={primaryButton?.isCustomPrimaryButton ? 'button' : 'a'}\n href={trackUrlRef(localizeUrl(primaryButton.link), `${componentType}_${componentName}`)}\n target={primaryButton?.target}\n rel={\n primaryButton?.target === '_blank'\n ? (primaryButton?.rel ?? 'noopener noreferrer')\n : primaryButton?.rel\n }\n onClick={e => {\n if (primaryButton.link?.startsWith('#')) {\n e.preventDefault()\n const element = document.querySelector(primaryButton.link)\n if (element) {\n jump(element as HTMLElement, mergedJumpOptions)\n }\n }\n primaryButton?.isCustomPrimaryButton && onPrimaryClick?.(data, e, primaryButton?.customPrimaryEventId)\n }}\n data-headless-type-name={`${componentType}#${componentName}`}\n data-headless-title-desc-button={`${title}#${subtitle}#${primaryButton?.text}`}\n >\n {primaryButton.text}\n </Button>\n )}\n </div>\n <div className={iconGroupVariants({ align })}>\n {safeIconArray?.map(icon => (\n <div key={icon?.pcImage?.url || icon?.pcImage?.alt} className=\"h-12\">\n <Picture\n className=\"laptop:w-full h-full\"\n imgClassName=\"h-full object-cover\"\n loading=\"eager\"\n alt={icon?.pcImage?.alt || ''}\n source={icon?.pcImage?.url}\n />\n </div>\n ))}\n </div>\n </div>\n\n {safeCaption.length > 0 && (\n <div\n className={cn(\n 'hero-banner-caption-group laptop:gap-3 tablet:px-[32px] laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] desktop:pb-[24px] absolute bottom-0 z-10 flex items-stretch gap-2 px-[16px] pb-[16px]',\n classNames.captionGroup\n )}\n >\n {safeCaption.map((c, index) => (\n <React.Fragment key={c.title}>\n <Text\n size={2}\n className={cn(\n 'hero-banner-product-text tablet:w-[108px] loptop:w-[150px] desktop:w-[156px] lg-desktop:w-[180px] laptop:text-[14px] flex-1 text-[12px]'\n )}\n html={c.title}\n />\n {index < safeCaption.length - 1 && <div className={cn('bg-info-primary w-px')} />}\n </React.Fragment>\n ))}\n </div>\n )}\n\n {/* \u89C6\u9891\u5F39\u7A97 */}\n {visible && (\n <VideoModal\n visible={visible}\n videoUrl={secondaryButton?.isYoutubeVideo ? undefined : secondaryButton?.videoUrl?.url}\n youTubeId={secondaryButton?.isYoutubeVideo ? secondaryButton?.youtubeId : undefined}\n onCloseModal={() => setVisible(false)}\n />\n )}\n </div>\n </div>\n )\n})\n\nHeroBanner.displayName = 'HeroBanner'\n\nexport default withLayout(HeroBanner)\n"],
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["Fragment", "jsx", "jsxs", "React", "useImperativeHandle", "useRef", "useState", "useEffect", "gsap", "ScrollTrigger", "jump", "useMediaQuery", "useInView", "ScrollLoadVideo", "Button", "Heading", "Picture", "Text", "Countdown", "cn", "cva", "withLayout", "useExposure", "trackUrlRef", "getLocalizedPath", "useAiuiContext", "sizeMap", "VideoModal", "HeroBannerCarousel", "componentType", "componentName", "contentVariants", "textVariants", "buttonGroupVariants", "iconGroupVariants", "PlayButtonAppendIcon", "size", "width", "height", "HeroBanner", "data", "className", "classNames", "onSecondaryClick", "onPrimaryClick", "jumpOptions", "rest", "ref", "locale", "mergedJumpOptions", "localizeUrl", "url", "label", "title", "subtitle", "endDate", "endDate_tz", "dateFormat", "pcImage", "padImage", "mobileImage", "pcVideo", "padVideo", "mobileVideo", "isShowVideo", "isVideoLoop", "primaryButton", "secondaryButton", "theme", "titleSize", "caption", "blockLink", "iconArray", "align", "enableParallax", "safeCaption", "safeIconArray", "isMobile", "isPad", "visible", "setVisible", "inViewRef", "inView", "scrollTriggerRef", "bgTriggerRef", "boxTriggerRef", "bgRef", "boxRef", "blockLinkLabel", "resolvedComponentTitle", "resolvedComponentDescription", "gsapResize", "clientHeight", "self", "value", "e", "element", "icon", "c", "index", "HeroBanner_default"]
|
|
4
|
+
"sourcesContent": ["'use client'\nimport React, { useImperativeHandle, useRef, useState, useEffect } from 'react'\nimport gsap from 'gsap'\nimport { ScrollTrigger } from 'gsap/dist/ScrollTrigger'\nimport jump from 'jump.js'\nimport type { HeroBannerProps } from './types.js'\nimport { useMediaQuery } from 'react-responsive'\nimport { useInView } from 'react-intersection-observer'\nimport ScrollLoadVideo from '../../helpers/ScrollLoadVideo.js'\nimport { Button, Heading, Picture, Text, Countdown } from '../../components/index.js'\nimport { cn } from '../../helpers/index.js'\nimport { cva } from 'class-variance-authority'\nimport { withLayout } from '../../shared/Styles.js'\nimport { useExposure } from '../../hooks/useExposure.js'\nimport { trackUrlRef } from '../../shared/trackUrlRef.js'\nimport { getLocalizedPath } from '../../helpers/utils.js'\nimport { useAiuiContext } from '../AiuiProvider/index.js'\nimport { sizeMap } from '../../components/button.js'\nimport { VideoModal } from '../VideoModal/index.js'\n// Task 18: import carousel implementation for early-return delegation (DECISION-A002)\nimport HeroBannerCarousel from './HeroBannerCarousel.js'\n\n/**\n * jump.js \u914D\u7F6E\u9009\u9879\n */\nexport interface JumpOptions {\n /** \u52A8\u753B\u6301\u7EED\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09 @default 500 */\n duration?: number\n /** \u504F\u79FB\u91CF\uFF08\u50CF\u7D20\uFF09 @default 0 */\n offset?: number\n /** \u52A8\u753B\u5B8C\u6210\u56DE\u8C03 */\n callback?: () => void\n /** \u7F13\u52A8\u51FD\u6570 */\n easing?: (t: number, b: number, c: number, d: number) => number\n /** \u662F\u5426\u8003\u8651 a11y @default false */\n a11y?: boolean\n}\n\nconst componentType = 'image'\nconst componentName = 'hero_banner'\n\n// CVA variants for align\nconst contentVariants = cva(\n 'hero-banner-content lg-desktop:gap-[32px] absolute top-24 z-10 flex w-full flex-col gap-[24px]',\n {\n variants: {\n align: {\n left: 'tablet:px-[32px] laptop:top-1/2 laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] laptop:-translate-y-1/2 left-0 px-[16px]',\n center:\n 'tablet:top-[84px] laptop:top-[76px] desktop:top-[176px] lg-desktop:top-[192px] left-1/2 top-[84px] -translate-x-1/2 translate-y-0 items-center text-center',\n },\n },\n defaultVariants: {\n align: 'left',\n },\n }\n)\n\nconst textVariants = cva(\n 'hero-banner-wrap-text lg-desktop:max-w-[824px] desktop:max-w-[648px] laptop:max-w-[440px] tablet:max-w-[704px] max-w-[358px]',\n {\n variants: {\n align: {\n left: 'laptop:text-left',\n center: 'text-center',\n },\n },\n defaultVariants: {\n align: 'left',\n },\n }\n)\n\nconst buttonGroupVariants = cva(\n 'hero-banner-button-group lg-desktop:gap-x-3 flex flex-wrap items-center gap-x-2 gap-y-3',\n {\n variants: {\n align: {\n left: 'laptop:justify-start',\n center: 'justify-center',\n },\n },\n defaultVariants: {\n align: 'left',\n },\n }\n)\n\nconst iconGroupVariants = cva('hero-banner-icon-group flex items-center gap-2', {\n variants: {\n align: {\n left: 'justify-start',\n center: 'justify-center',\n },\n },\n defaultVariants: {\n align: 'left',\n },\n})\n\nexport type HeroBannerSemanticName =\n | 'root'\n | 'title'\n | 'subtitle'\n | 'buttonGroup'\n | 'primaryButton'\n | 'secondaryButton'\n | 'tertiaryButton'\n | 'captionGroup'\n\nconst PlayButtonAppendIcon = ({ size = 'base' }: { size: 'base' | 'lg' | 'sm' }) => {\n const { width, height } = sizeMap[size]\n return (\n <svg width={width} height={height} viewBox=\"0 0 20 20\" fill=\"currentcolor\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M13.9599 9.30662C14.4547 9.63647 14.4547 10.3635 13.9599 10.6934L6.29558 15.8029C5.74179 16.1721 5 15.7751 5 15.1096V4.89042C5 4.22484 5.74179 3.82785 6.29558 4.19705L13.9599 9.30662Z\"\n fill=\"currentcolor\"\n />\n </svg>\n )\n}\n\nconst HeroBanner = React.forwardRef<\n HTMLDivElement,\n HeroBannerProps & {\n classNames?: Partial<Record<HeroBannerSemanticName, string>>\n /**\n * \u951A\u70B9\u8DF3\u8F6C\u914D\u7F6E\uFF08\u7528\u4E8E # \u5F00\u5934\u7684\u94FE\u63A5\uFF09\n * @default { duration: 500, offset: 0 }\n */\n jumpOptions?: JumpOptions\n }\n>(\n (\n { data, className, classNames = {}, onSecondaryClick, onPrimaryClick, onTertiaryClick, jumpOptions = {}, ...rest },\n ref\n ) => {\n const { locale } = useAiuiContext()\n\n // \u9ED8\u8BA4 jump \u914D\u7F6E\n const defaultJumpOptions: JumpOptions = { duration: 500, offset: 0 }\n const mergedJumpOptions = { ...defaultJumpOptions, ...jumpOptions }\n\n // \u5305\u88C5\u94FE\u63A5\uFF0C\u81EA\u52A8\u6DFB\u52A0 locale \u524D\u7F00\n const localizeUrl = (url?: string) => {\n if (!url || !locale) return url\n return getLocalizedPath(url, locale)\n }\n\n const {\n label,\n title,\n subtitle,\n endDate,\n endDate_tz,\n dateFormat,\n pcImage,\n padImage,\n mobileImage,\n pcVideo,\n padVideo,\n mobileVideo,\n isShowVideo,\n isVideoLoop = true,\n primaryButton,\n secondaryButton,\n tertiaryButton,\n theme = 'light',\n size = 'default',\n titleSize,\n caption = [],\n blockLink,\n iconArray,\n align = 'left',\n enableParallax = true,\n } = data\n\n const safeCaption = Array.isArray(caption) ? caption : []\n const safeIconArray = Array.isArray(iconArray) ? iconArray : undefined\n\n const isMobile = useMediaQuery({ query: '(max-width: 768px)' })\n const isPad = useMediaQuery({ query: '(max-width: 1024px)' })\n const [visible, setVisible] = useState<boolean>(false)\n const { ref: inViewRef, inView } = useInView()\n const scrollTriggerRef = useRef<ScrollTrigger | null>(null)\n const bgTriggerRef = useRef<ScrollTrigger | null>(null)\n const boxTriggerRef = useRef<ScrollTrigger | null>(null)\n\n const bgRef = useRef<HTMLImageElement>(null)\n const boxRef = useRef<HTMLDivElement>(null)\n\n // Task 7: Compute accessibility / tracking fallback values (Rule 2/3/4 \u2014 DECISION-T3)\n // blockLink aria-label: title \u2192 subtitle \u2192 pcImage.alt \u2192 undefined (absent attr)\n const blockLinkLabel = title?.trim() || subtitle?.trim() || pcImage?.alt?.trim() || undefined\n // componentTitle: title=undefined \u2192 coerce '' via subtitle only (no pcImage.alt for undefined title);\n // title='' (intentional) \u2192 cascade: subtitle \u2192 pcImage.alt \u2192 undefined\n const resolvedComponentTitle =\n title !== undefined\n ? title?.trim() || subtitle?.trim() || pcImage?.alt?.trim() || undefined\n : (subtitle && subtitle.trim()) || ''\n // componentDescription: subtitle with undefined guard\n const resolvedComponentDescription = (subtitle && subtitle.trim()) || ''\n\n useExposure(boxRef, {\n componentType,\n componentName,\n componentTitle: resolvedComponentTitle,\n componentDescription: resolvedComponentDescription,\n })\n\n useImperativeHandle(ref, () => boxRef.current as HTMLDivElement)\n\n useEffect(() => {\n // Task 18: GSAP bypass \u2014 skip ALL GSAP setup in carousel mode (DECISION-A002/DECISION-009)\n if (data.items && data.items.length > 0) return\n if (!enableParallax) return\n gsap.registerPlugin(ScrollTrigger)\n function gsapResize() {\n if (!bgRef.current) return\n const clientHeight = boxRef.current?.clientHeight || 100\n const screenHeight = window.innerHeight\n\n if (screenHeight <= clientHeight) {\n scrollTriggerRef.current = ScrollTrigger.create({\n trigger: boxRef.current,\n start: 'top bottom',\n end: 'bottom top',\n scrub: true,\n onUpdate: (self: any) => {\n const base = 40\n const value = self.progress * base - base / 2\n gsap.set(bgRef.current, { yPercent: value })\n },\n })\n } else {\n boxTriggerRef.current = ScrollTrigger.create({\n trigger: boxRef.current,\n start: 'top bottom',\n end: 'bottom bottom',\n scrub: true,\n onUpdate: (self: any) => {\n const base = 20\n const value = self.progress * base - base\n gsap.set(bgRef.current, { yPercent: value })\n },\n })\n bgTriggerRef.current = ScrollTrigger.create({\n trigger: boxRef.current,\n start: 'top top',\n end: 'bottom top',\n scrub: true,\n onUpdate: (self: any) => {\n const base = 20\n const value = self.progress * base\n gsap.set(bgRef.current, { yPercent: value })\n },\n })\n }\n }\n if (inView) gsapResize()\n return () => {\n // ScrollTrigger.getAll().forEach((t: any) => t.kill())\n scrollTriggerRef.current && scrollTriggerRef.current.kill()\n boxTriggerRef.current && boxTriggerRef.current.kill()\n bgTriggerRef.current && bgTriggerRef.current.kill()\n }\n }, [inView, data.items, enableParallax])\n\n // Task 18: Carousel mode early return (DECISION-A002 / DECISION-011)\n // All hooks above have already run. Delegate entirely to HeroBannerCarousel\n // when data.items is non-empty; GSAP was already bypassed in the useEffect above.\n if (data.items && data.items.length > 0) {\n return (\n <HeroBannerCarousel\n data={data}\n className={className}\n classNames={classNames}\n onPrimaryClick={onPrimaryClick}\n onSecondaryClick={onSecondaryClick}\n onTertiaryClick={onTertiaryClick}\n ref={ref as React.Ref<HTMLDivElement>}\n {...rest}\n />\n )\n }\n return (\n <div {...rest} ref={inViewRef} data-ui-component-id=\"HeroBanner\">\n <div\n ref={boxRef}\n className={cn(\n theme === 'dark' ? 'aiui-dark' : '',\n 'text-info-primary relative w-full overflow-hidden',\n {\n 'lg-desktop:aspect-[1920/930] desktop:aspect-[1440/700] laptop:aspect-[1024/520] tablet:aspect-[768/660] aspect-[390/660]':\n size === 'default',\n 'lg-desktop:aspect-[1920/800] desktop:aspect-[1440/640] laptop:aspect-[1024/480] tablet:aspect-[768/560] aspect-[390/560]':\n size === 'sm',\n },\n className\n )}\n >\n {blockLink && (\n <a\n className=\"absolute inset-0 z-10\"\n href={trackUrlRef(localizeUrl(blockLink), `${componentType}_${componentName}`)}\n data-headless-type-name={`${componentType}#${componentName}`}\n data-headless-title-desc-button={`${title}#${subtitle}`}\n tabIndex={-1}\n aria-hidden=\"true\"\n aria-label={blockLinkLabel}\n ></a>\n )}\n <div ref={bgRef} className={cn('absolute left-0 top-0 size-full')}>\n {isShowVideo ? (\n <>\n {/* Poster cover layer: eager + high priority, decoupled from JS lazy-load, participates in LCP */}\n {/* Wrap in div so data-video-poster + transition-opacity are on the container, not the <img> */}\n <div\n data-video-poster=\"true\"\n className=\"laptop:w-full absolute inset-0 h-full transition-opacity duration-500\"\n >\n <Picture\n className=\"absolute inset-0 size-full\"\n imgClassName=\"size-full object-cover\"\n loading=\"eager\"\n fetchPriority=\"high\"\n alt={pcImage?.alt || ''}\n source={`${pcImage?.url || ''} , ${padImage?.url ?? (mobileImage?.url || '')} 1024, ${mobileImage?.url || ''} 767`}\n />\n </div>\n {/* Video layer: lazy-loaded, overlays Picture via DOM order (both z-auto, video is after Picture) */}\n <ScrollLoadVideo\n poster={undefined}\n src={\n isMobile\n ? (mobileVideo?.url as string)\n : isPad\n ? (padVideo?.url as string) || (mobileVideo?.url as string)\n : (pcVideo?.url as string)\n }\n className=\"laptop:w-full absolute inset-0 h-full\"\n videoClassName=\"h-full object-cover\"\n muted\n loop={isVideoLoop}\n playsInline\n onPlaying={(e: React.SyntheticEvent<HTMLVideoElement>) => {\n const banner = (e.currentTarget as HTMLVideoElement).closest('[data-ui-component-id=\"HeroBanner\"]')\n banner?.querySelector<HTMLElement>('[data-video-poster=\"true\"]')?.classList.add('opacity-0')\n }}\n />\n </>\n ) : (\n <Picture\n className=\"laptop:w-full h-full\"\n imgClassName=\"h-full object-cover\"\n loading=\"eager\"\n fetchPriority=\"high\"\n alt={pcImage?.alt || ''}\n source={`${pcImage?.url || ''} , ${padImage?.url ?? (mobileImage?.url || '')} 1024, ${mobileImage?.url || ''} 767`}\n />\n )}\n </div>\n\n {/* \u5185\u5BB9\u533A\u57DF */}\n <div className={contentVariants({ align })}>\n <div className={textVariants({ align })}>\n {label && (\n <Text\n size={2}\n as=\"p\"\n className={cn('hero-banner-label font-heading lg-desktop:text-[18px] desktop:text-base text-sm')}\n html={label}\n />\n )}\n {title && (\n <Heading\n as={titleSize === '4' ? 'h1' : 'h2'}\n html={title}\n className={cn('hero-banner-title', classNames.title)}\n size={titleSize ? (Number(titleSize || '5') as any) : size === 'sm' ? 4 : 5}\n />\n )}\n {subtitle && (\n <Text\n as=\"p\"\n size={2}\n className={cn(\n 'hero-banner-subtitle font-heading lg-desktop:text-[18px] desktop:text-base laptop:mt-2 lg-desktop:mt-4 mt-1 text-sm',\n classNames.subtitle\n )}\n html={subtitle}\n />\n )}\n {endDate && (\n <div className=\"mt-3\">\n <Countdown\n endDate={endDate}\n endDate_tz={endDate_tz}\n dateFormat={dateFormat}\n variant=\"spacious\"\n align={align}\n />\n </div>\n )}\n </div>\n {/* \u6309\u94AE\u7EC4 */}\n <div className={cn(buttonGroupVariants({ align }), classNames.buttonGroup)}>\n {/* secondary + primary \u59CB\u7EC8\u540C\u884C */}\n <div className=\"lg-desktop:gap-3 flex items-center gap-2\">\n {secondaryButton?.isShowPlayVideoButton && secondaryButton?.playVideoButtonText ? (\n <Button\n onClick={() => setVisible(true)}\n size=\"lg\"\n variant=\"secondary\"\n className=\"hero-banner-play-video-button\"\n data-headless-type-name={`${componentType}#${componentName}`}\n data-headless-title-desc-button={`${title}#${subtitle}#${secondaryButton?.playVideoButtonText}`}\n >\n {secondaryButton?.playVideoButtonText} <PlayButtonAppendIcon size=\"lg\" />\n </Button>\n ) : secondaryButton?.text ? (\n <Button\n aria-label={title ?? subtitle}\n size=\"lg\"\n variant=\"secondary\"\n className={cn('hero-banner-secondary-button', classNames.secondaryButton)}\n as={secondaryButton?.isCustomSecondaryButton ? 'button' : 'a'}\n href={trackUrlRef(localizeUrl(secondaryButton?.link), `${componentType}_${componentName}`)}\n target={secondaryButton?.target}\n rel={\n secondaryButton?.target === '_blank'\n ? (secondaryButton?.rel ?? 'noopener noreferrer')\n : secondaryButton?.rel\n }\n onClick={e => {\n if (secondaryButton.link?.startsWith('#')) {\n e.preventDefault()\n const element = document.querySelector(secondaryButton.link)\n if (element) {\n jump(element as HTMLElement, mergedJumpOptions)\n }\n }\n secondaryButton?.isCustomSecondaryButton &&\n onSecondaryClick?.(data, e, secondaryButton?.customSecondaryEventId)\n }}\n data-headless-type-name={`${componentType}#${componentName}`}\n data-headless-title-desc-button={`${title}#${subtitle}#${secondaryButton?.text}`}\n >\n {secondaryButton?.text}\n <span className=\"sr-only\">{title ?? subtitle}</span>\n </Button>\n ) : null}\n {primaryButton && primaryButton.text && (\n <Button\n aria-label={title ?? subtitle}\n size=\"lg\"\n variant=\"primary\"\n className={cn('hero-banner-primary-button', classNames.primaryButton)}\n as={primaryButton?.isCustomPrimaryButton ? 'button' : 'a'}\n href={trackUrlRef(localizeUrl(primaryButton.link), `${componentType}_${componentName}`)}\n target={primaryButton?.target}\n rel={\n primaryButton?.target === '_blank'\n ? (primaryButton?.rel ?? 'noopener noreferrer')\n : primaryButton?.rel\n }\n onClick={e => {\n if (primaryButton.link?.startsWith('#')) {\n e.preventDefault()\n const element = document.querySelector(primaryButton.link)\n if (element) {\n jump(element as HTMLElement, mergedJumpOptions)\n }\n }\n primaryButton?.isCustomPrimaryButton &&\n onPrimaryClick?.(data, e, primaryButton?.customPrimaryEventId)\n }}\n data-headless-type-name={`${componentType}#${componentName}`}\n data-headless-title-desc-button={`${title}#${subtitle}#${primaryButton?.text}`}\n >\n {primaryButton.text}\n </Button>\n )}\n </div>\n {tertiaryButton && tertiaryButton.text && (\n <Button\n aria-label={title ?? subtitle}\n size=\"lg\"\n variant=\"link\"\n className={cn('hero-banner-tertiary-button w-auto p-0', classNames.tertiaryButton)}\n as={tertiaryButton?.isCustomTertiaryButton ? 'button' : 'a'}\n href={trackUrlRef(localizeUrl(tertiaryButton.link), `${componentType}_${componentName}`)}\n target={tertiaryButton?.target}\n rel={\n tertiaryButton?.target === '_blank'\n ? (tertiaryButton?.rel ?? 'noopener noreferrer')\n : tertiaryButton?.rel\n }\n onClick={e => {\n if (tertiaryButton.link?.startsWith('#')) {\n e.preventDefault()\n const element = document.querySelector(tertiaryButton.link)\n if (element) {\n jump(element as HTMLElement, mergedJumpOptions)\n }\n }\n tertiaryButton?.isCustomTertiaryButton &&\n onTertiaryClick?.(data, e, tertiaryButton?.customTertiaryEventId)\n }}\n data-headless-type-name={`${componentType}#${componentName}`}\n data-headless-title-desc-button={`${title}#${subtitle}#${tertiaryButton?.text}`}\n >\n {tertiaryButton.text}\n <span className=\"sr-only\">{title ?? subtitle}</span>\n </Button>\n )}\n </div>\n <div className={iconGroupVariants({ align })}>\n {safeIconArray?.map(icon => (\n <div key={icon?.pcImage?.url || icon?.pcImage?.alt} className=\"h-12\">\n <Picture\n className=\"laptop:w-full h-full\"\n imgClassName=\"h-full object-cover\"\n loading=\"eager\"\n alt={icon?.pcImage?.alt || ''}\n source={icon?.pcImage?.url}\n />\n </div>\n ))}\n </div>\n </div>\n\n {safeCaption.length > 0 && (\n <div\n className={cn(\n 'hero-banner-caption-group laptop:gap-3 tablet:px-[32px] laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] desktop:pb-[24px] absolute bottom-0 z-10 flex items-stretch gap-2 px-[16px] pb-[16px]',\n classNames.captionGroup\n )}\n >\n {safeCaption.map((c, index) => (\n <React.Fragment key={c.title}>\n <Text\n size={2}\n className={cn(\n 'hero-banner-product-text tablet:w-[108px] loptop:w-[150px] desktop:w-[156px] lg-desktop:w-[180px] laptop:text-[14px] flex-1 text-[12px]'\n )}\n html={c.title}\n />\n {index < safeCaption.length - 1 && <div className={cn('bg-info-primary w-px')} />}\n </React.Fragment>\n ))}\n </div>\n )}\n\n {/* \u89C6\u9891\u5F39\u7A97 */}\n {visible && (\n <VideoModal\n visible={visible}\n videoUrl={secondaryButton?.isYoutubeVideo ? undefined : secondaryButton?.videoUrl?.url}\n youTubeId={secondaryButton?.isYoutubeVideo ? secondaryButton?.youtubeId : undefined}\n onCloseModal={() => setVisible(false)}\n />\n )}\n </div>\n </div>\n )\n }\n)\n\nHeroBanner.displayName = 'HeroBanner'\n\nexport default withLayout(HeroBanner)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Fragment", "jsx", "jsxs", "React", "useImperativeHandle", "useRef", "useState", "useEffect", "gsap", "ScrollTrigger", "jump", "useMediaQuery", "useInView", "ScrollLoadVideo", "Button", "Heading", "Picture", "Text", "Countdown", "cn", "cva", "withLayout", "useExposure", "trackUrlRef", "getLocalizedPath", "useAiuiContext", "sizeMap", "VideoModal", "HeroBannerCarousel", "componentType", "componentName", "contentVariants", "textVariants", "buttonGroupVariants", "iconGroupVariants", "PlayButtonAppendIcon", "size", "width", "height", "HeroBanner", "data", "className", "classNames", "onSecondaryClick", "onPrimaryClick", "onTertiaryClick", "jumpOptions", "rest", "ref", "locale", "mergedJumpOptions", "localizeUrl", "url", "label", "title", "subtitle", "endDate", "endDate_tz", "dateFormat", "pcImage", "padImage", "mobileImage", "pcVideo", "padVideo", "mobileVideo", "isShowVideo", "isVideoLoop", "primaryButton", "secondaryButton", "tertiaryButton", "theme", "titleSize", "caption", "blockLink", "iconArray", "align", "enableParallax", "safeCaption", "safeIconArray", "isMobile", "isPad", "visible", "setVisible", "inViewRef", "inView", "scrollTriggerRef", "bgTriggerRef", "boxTriggerRef", "bgRef", "boxRef", "blockLinkLabel", "resolvedComponentTitle", "resolvedComponentDescription", "gsapResize", "clientHeight", "self", "value", "e", "element", "icon", "c", "index", "HeroBanner_default"]
|
|
7
7
|
}
|
|
@@ -8,6 +8,7 @@ interface HeroBannerCarouselProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
8
8
|
classNames?: Record<string, string>;
|
|
9
9
|
onPrimaryClick?: (data: any, e: any, id?: string) => void;
|
|
10
10
|
onSecondaryClick?: (data: any, e: any, id?: string) => void;
|
|
11
|
+
onTertiaryClick?: (data: any, e: any, id?: string) => void;
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
14
|
* HeroBannerCarousel — arrows-only Navigation Swiper wrapping HeroBannerSlide units.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{Fragment as
|
|
1
|
+
"use client";import{Fragment as ne,jsx as e,jsxs as n}from"react/jsx-runtime";import _,{useRef as A,useEffect as $,useCallback as U,useState as O}from"react";import{Swiper as X,SwiperSlide as Y}from"swiper/react";import{Navigation as q,Pagination as F,Autoplay as Z}from"swiper/modules";import{useMediaQuery as W}from"react-responsive";import ee from"../../helpers/ScrollLoadVideo.js";import{cn as p}from"../../helpers/index.js";import{Button as E,Heading as te,Text as I,Countdown as ae,Picture as M}from"../../components/index.js";import"swiper/css";import"swiper/css/navigation";import"swiper/css/pagination";const re=()=>n("svg",{className:"lg-desktop:size-14 size-10",viewBox:"0 0 40 40",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:[e("circle",{cx:"20",cy:"20",r:"20",fill:"currentColor",fillOpacity:"0.2"}),e("path",{d:"M23 13L16 20L23 27",stroke:"white",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]}),ie=()=>n("svg",{className:"lg-desktop:size-14 size-10",viewBox:"0 0 40 40",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:[e("circle",{cx:"20",cy:"20",r:"20",fill:"currentColor",fillOpacity:"0.2"}),e("path",{d:"M17 13L24 20L17 27",stroke:"white",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]}),oe=({data:l,isActive:j,videoRef:R,slideIndex:k,onPrimaryClick:z,onSecondaryClick:B,onTertiaryClick:T})=>{const{pcVideo:v,padVideo:c,mobileVideo:d,pcImage:b,padImage:u,mobileImage:m,isVideoLoop:H=!0,isShowVideo:y,title:w,subtitle:P,label:C,endDate:g,endDate_tz:L,dateFormat:S,iconArray:s,caption:x,blockLink:N,primaryButton:i,secondaryButton:t,tertiaryButton:a,theme:f="light",align:o="left",titleSize:h,size:D="default"}=l,G=W({query:"(max-width: 768px)"}),J=W({query:"(max-width: 1024px)"}),K=G?d?.url||v?.url||"":J?c?.url||v?.url||d?.url||"":v?.url||d?.url||"";return n("div",{"data-ui-component-id":"HeroBannerSlide",className:p("text-info-primary relative w-full overflow-hidden",f==="dark"?"aiui-dark":"",D==="default"?"lg-desktop:aspect-[1920/930] desktop:aspect-[1440/700] laptop:aspect-[1024/520] tablet:aspect-[768/660] aspect-[390/660]":"lg-desktop:aspect-[1920/800] desktop:aspect-[1440/640] laptop:aspect-[1024/480] tablet:aspect-[768/560] aspect-[390/560]"),children:[N&&e("a",{className:"absolute inset-0 z-10",href:N,tabIndex:-1,"aria-hidden":"true"}),y?n(ne,{children:[e("div",{"data-video-poster":"true",className:"absolute inset-0 size-full transition-opacity duration-500",children:e(M,{className:"absolute inset-0 size-full",imgClassName:"size-full object-cover",loading:k===0?"eager":"lazy",fetchPriority:k===0?"high":"auto",alt:b?.alt||"",source:`${b?.url||""} , ${u?.url??(m?.url||"")} 1024, ${m?.url||""} 767`})}),e(ee,{videoRef:R,src:K,poster:void 0,className:"absolute inset-0 size-full",videoClassName:"size-full object-cover",muted:!0,loop:H,playsInline:!0,onPlaying:r=>{r.currentTarget.closest('[data-ui-component-id="HeroBannerSlide"]')?.querySelector('[data-video-poster="true"]')?.classList.add("opacity-0")}})]}):e(M,{className:"absolute inset-0 size-full",imgClassName:"size-full object-cover",loading:"eager",fetchPriority:"high",alt:b?.alt||"",source:`${b?.url||""} , ${u?.url??(m?.url||"")} 1024, ${m?.url||""} 767`}),n("div",{className:p("laptop:top-1/2 laptop:-translate-y-1/2 lg-desktop:gap-[32px] absolute top-24 z-10 flex w-full flex-col gap-[24px]",o==="center"?"left-1/2 -translate-x-1/2 items-center text-center":"tablet:px-[32px] laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] left-0 px-[16px]"),children:[n("div",{className:p("tablet:max-w-[704px] laptop:max-w-[440px] desktop:max-w-[648px] lg-desktop:max-w-[824px] max-w-[358px]",o==="center"?"text-center":"laptop:text-left"),children:[C&&e(I,{size:2,as:"p",className:p("hero-banner-label font-heading lg-desktop:text-[18px] desktop:text-base text-sm"),html:C}),w&&e(te,{as:h==="4"?"h1":"h2",html:w,size:h?Number(h||"5"):D==="sm"?4:5}),P&&e(I,{as:"p",size:2,className:"font-heading lg-desktop:text-[18px] desktop:text-base laptop:mt-2 lg-desktop:mt-4 mt-1 text-sm",html:P}),g&&e("div",{className:"mt-3",children:e(ae,{endDate:g,endDate_tz:L,dateFormat:S,variant:"spacious",align:o})})]}),(i?.text||t?.text||a?.text)&&n("div",{className:p("lg-desktop:gap-3 flex flex-wrap items-center gap-2",o==="center"?"justify-center":"laptop:justify-start"),children:[t?.text&&e(E,{size:"lg",variant:"secondary",as:t?.isCustomSecondaryButton?"button":"a",href:t.link,target:t?.target,rel:t?.target==="_blank"?t?.rel??"noopener noreferrer":t?.rel,onClick:r=>t?.isCustomSecondaryButton&&B?.(l,r,t?.customSecondaryEventId),children:t.text}),i?.text&&e(E,{size:"lg",variant:"primary",as:i?.isCustomPrimaryButton?"button":"a",href:i.link,target:i?.target,rel:i?.target==="_blank"?i?.rel??"noopener noreferrer":i?.rel,onClick:r=>i?.isCustomPrimaryButton&&z?.(l,r,i?.customPrimaryEventId),children:i.text}),a?.text&&e(E,{size:"lg",variant:"link",className:"tablet:basis-auto basis-full",as:a?.isCustomTertiaryButton?"button":"a",href:a.link,target:a?.target,rel:a?.target==="_blank"?a?.rel??"noopener noreferrer":a?.rel,onClick:r=>a?.isCustomTertiaryButton&&T?.(l,r,a?.customTertiaryEventId),children:a.text})]}),s&&s.length>0&&e("div",{className:p("flex items-center gap-2",o==="center"?"justify-center":"justify-start"),children:s.map(r=>e("div",{className:"h-12",children:e(M,{className:"laptop:w-full h-full",imgClassName:"h-full object-cover",loading:"eager",alt:r?.pcImage?.alt||"",source:r?.pcImage?.url})},r?.pcImage?.url||r?.pcImage?.alt))})]}),x&&x.length>0&&e("div",{className:"hero-banner-caption-group laptop:gap-3 tablet:px-[32px] laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] desktop:pb-[24px] absolute bottom-0 z-10 flex items-stretch gap-2 px-[16px] pb-[16px]",children:x.map((r,V)=>n(_.Fragment,{children:[e(I,{size:2,className:"hero-banner-product-text tablet:w-[108px] desktop:w-[156px] lg-desktop:w-[180px] laptop:text-[14px] flex-1 text-[12px]",html:r.title}),V<x.length-1&&e("div",{className:"bg-info-primary w-px"})]},r.title))})]})},Q=_.forwardRef(({data:l,className:j,classNames:R={},onPrimaryClick:k,onSecondaryClick:z,onTertiaryClick:B,...T},v)=>{const c=l.items||[],d=l.carousel,b=l.size??"default",u=A(null),[m,H]=O(0),[y,w]=O(null),C=(c[m]?.theme??"light")==="dark";$(()=>{const t=u.current;!t||!y||t.pagination&&(t.pagination.el=y,t.pagination.init(),t.pagination.render(),t.pagination.update())},[y]);const g=A(c.map(()=>_.createRef())),L=(d?.showArrows??!0)&&c.length>1,S=d?.showDots??!0,s=d?.autoplaySeconds,x=s!==void 0&&s>0?[q,F,Z]:[q,F],N=s!==void 0&&s>0?{delay:Math.max(s*1e3,3e3),disableOnInteraction:!1}:!1;$(()=>{g.current.forEach((t,a)=>{a!==0&&t.current&&(t.current.pause(),t.current.currentTime=0)})},[]);const i=U(t=>{const a=t.realIndex;g.current.forEach((o,h)=>{h!==a&&o.current&&(o.current.pause(),o.current.currentTime=0)});const f=g.current[a];f?.current&&f.current.paused&&f.current.play()?.catch(()=>{}),H(a)},[]);return n("div",{ref:v,className:p("hero-banner-carousel group relative w-full overflow-hidden",b==="default"?"lg-desktop:aspect-[1920/930] desktop:aspect-[1440/700] laptop:aspect-[1024/520] tablet:aspect-[768/660] aspect-[390/660]":"lg-desktop:aspect-[1920/800] desktop:aspect-[1440/640] laptop:aspect-[1024/480] tablet:aspect-[768/560] aspect-[390/560]",{"aiui-dark":c[m]?.theme==="dark"},j),...T,children:[e(X,{onSwiper:t=>u.current=t,onSlideChange:i,modules:x,slidesPerView:1,loop:!0,initialSlide:0,allowTouchMove:!0,autoplay:N,pagination:S?{clickable:!0,el:y,renderBullet:(t,a)=>`<span class="${a} banner-dot"></span>`}:!1,children:c.map((t,a)=>e(Y,{children:({isActive:f})=>e(oe,{data:t,isActive:f,slideIndex:a,videoRef:g.current[a],onPrimaryClick:k,onSecondaryClick:z,onTertiaryClick:B})},a))}),L&&n("div",{className:"pointer-events-none absolute inset-0 z-20 flex items-center justify-between px-4",children:[e("button",{type:"button",className:"tablet:flex pointer-events-auto hidden scale-95 items-center justify-center opacity-0 transition-all duration-300 ease-out hover:scale-105 focus-visible:scale-105 group-focus-within:scale-100 group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100",onClick:()=>u.current?.slidePrev(),"aria-label":"Previous banner",children:e(re,{})}),e("button",{type:"button",className:"tablet:flex pointer-events-auto hidden scale-95 items-center justify-center opacity-0 transition-all duration-300 ease-out hover:scale-105 focus-visible:scale-105 group-focus-within:scale-100 group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100",onClick:()=>u.current?.slideNext(),"aria-label":"Next banner",children:e(ie,{})})]}),S&&e("div",{ref:w,style:{"--swiper-pagination-bottom":"0px"},className:p("hero-banner-pagination swiper-pagination lg-desktop:!bottom-6 [&_.banner-dot.swiper-pagination-bullet-active]:bg-info-primary [&_.banner-dot]:bg-info-quaternary absolute !bottom-4 !left-1/2 z-20 flex !w-auto !-translate-x-1/2 items-center justify-center gap-2 [&_.banner-dot.swiper-pagination-bullet-active]:h-2 [&_.banner-dot.swiper-pagination-bullet-active]:w-8 [&_.banner-dot.swiper-pagination-bullet-active]:rounded-full [&_.banner-dot]:size-2 [&_.banner-dot]:rounded-full [&_.banner-dot]:transition-all [&_.banner-dot]:duration-300 [&_.banner-dot]:ease-out [&_.swiper-pagination-bullet]:m-0 [&_.swiper-pagination-bullet]:shrink-0 [&_.swiper-pagination-bullet]:opacity-100",{"aiui-dark":C})})]})});Q.displayName="HeroBannerCarousel";var ye=Q;export{ye as default};
|
|
2
2
|
//# sourceMappingURL=HeroBannerCarousel.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/biz-components/HeroBanner/HeroBannerCarousel.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\nimport React, { useRef, useEffect, useCallback, useState } from 'react'\nimport { Swiper, SwiperSlide } from 'swiper/react'\nimport { Navigation, Pagination, Autoplay } from 'swiper/modules'\nimport type { Swiper as SwiperType } from 'swiper'\nimport { useMediaQuery } from 'react-responsive'\nimport ScrollLoadVideo from '../../helpers/ScrollLoadVideo.js'\nimport { cn } from '../../helpers/index.js'\nimport type { HeroBannerProps, HeroBannerSlideData } from './types.js'\nimport { Button, Heading, Text, Countdown, Picture } from '../../components/index.js'\n\nimport 'swiper/css'\nimport 'swiper/css/navigation'\nimport 'swiper/css/pagination'\n\n// \u2500\u2500 Arrow icons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst ChevronLeft = () => (\n <svg\n className=\"lg-desktop:size-14 size-10\"\n viewBox=\"0 0 40 40\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <circle cx=\"20\" cy=\"20\" r=\"20\" fill=\"currentColor\" fillOpacity=\"0.2\" />\n <path d=\"M23 13L16 20L23 27\" stroke=\"white\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n)\n\nconst ChevronRight = () => (\n <svg\n className=\"lg-desktop:size-14 size-10\"\n viewBox=\"0 0 40 40\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <circle cx=\"20\" cy=\"20\" r=\"20\" fill=\"currentColor\" fillOpacity=\"0.2\" />\n <path d=\"M17 13L24 20L17 27\" stroke=\"white\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n)\n\n// \u2500\u2500 HeroBannerSlide \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Minimal per-slide content unit. No GSAP, no withLayout, no useExposure.\n// videoRef is the imperative pause/play channel (DECISION-A003).\n\ninterface HeroBannerSlideProps {\n data: HeroBannerSlideData\n isActive: boolean\n videoRef: React.RefObject<HTMLVideoElement>\n slideIndex: number\n onPrimaryClick?: (data: any, e: any, id?: string) => void\n onSecondaryClick?: (data: any, e: any, id?: string) => void\n}\n\nconst HeroBannerSlide = ({\n data,\n isActive: _isActive,\n videoRef,\n slideIndex,\n onPrimaryClick,\n onSecondaryClick,\n}: HeroBannerSlideProps) => {\n const {\n pcVideo,\n padVideo,\n mobileVideo,\n pcImage,\n padImage,\n mobileImage,\n isVideoLoop = true,\n isShowVideo,\n title,\n subtitle,\n label,\n endDate,\n endDate_tz,\n dateFormat,\n iconArray,\n caption,\n blockLink,\n primaryButton,\n secondaryButton,\n theme = 'light',\n align = 'left',\n titleSize,\n size = 'default',\n } = data\n\n const isMobile = useMediaQuery({ query: '(max-width: 768px)' })\n const isPad = useMediaQuery({ query: '(max-width: 1024px)' })\n\n const videoSrc = isMobile\n ? mobileVideo?.url || pcVideo?.url || ''\n : isPad\n ? padVideo?.url || pcVideo?.url || mobileVideo?.url || ''\n : pcVideo?.url || mobileVideo?.url || ''\n\n return (\n <div\n data-ui-component-id=\"HeroBannerSlide\"\n className={cn(\n 'text-info-primary relative w-full overflow-hidden',\n theme === 'dark' ? 'aiui-dark' : '',\n size === 'default'\n ? 'lg-desktop:aspect-[1920/930] desktop:aspect-[1440/700] laptop:aspect-[1024/520] tablet:aspect-[768/660] aspect-[390/660]'\n : 'lg-desktop:aspect-[1920/800] desktop:aspect-[1440/640] laptop:aspect-[1024/480] tablet:aspect-[768/560] aspect-[390/560]'\n )}\n >\n {blockLink && <a className=\"absolute inset-0 z-10\" href={blockLink} tabIndex={-1} aria-hidden=\"true\"></a>}\n {isShowVideo ? (\n <>\n {/* Poster cover layer: slide 0 = eager+high, others = lazy+auto */}\n <div data-video-poster=\"true\" className=\"absolute inset-0 size-full transition-opacity duration-500\">\n <Picture\n className=\"absolute inset-0 size-full\"\n imgClassName=\"size-full object-cover\"\n loading={slideIndex === 0 ? 'eager' : 'lazy'}\n fetchPriority={slideIndex === 0 ? 'high' : 'auto'}\n alt={pcImage?.alt || ''}\n source={`${pcImage?.url || ''} , ${padImage?.url ?? (mobileImage?.url || '')} 1024, ${mobileImage?.url || ''} 767`}\n />\n </div>\n {/* Video layer: lazy-loaded, overlays Picture via DOM order */}\n <ScrollLoadVideo\n videoRef={videoRef}\n src={videoSrc}\n poster={undefined}\n className=\"absolute inset-0 size-full\"\n videoClassName=\"size-full object-cover\"\n muted\n loop={isVideoLoop}\n playsInline\n onPlaying={(e: React.SyntheticEvent<HTMLVideoElement>) => {\n const slide = (e.currentTarget as HTMLVideoElement).closest('[data-ui-component-id=\"HeroBannerSlide\"]')\n slide?.querySelector<HTMLElement>('[data-video-poster=\"true\"]')?.classList.add('opacity-0')\n }}\n />\n </>\n ) : (\n <Picture\n className=\"absolute inset-0 size-full\"\n imgClassName=\"size-full object-cover\"\n loading=\"eager\"\n fetchPriority=\"high\"\n alt={pcImage?.alt || ''}\n source={`${pcImage?.url || ''} , ${padImage?.url ?? (mobileImage?.url || '')} 1024, ${mobileImage?.url || ''} 767`}\n />\n )}\n\n {/* Content Overlay */}\n <div\n className={cn(\n 'laptop:top-1/2 laptop:-translate-y-1/2 lg-desktop:gap-[32px] absolute top-24 z-10 flex w-full flex-col gap-[24px]',\n align === 'center'\n ? 'left-1/2 -translate-x-1/2 items-center text-center'\n : 'tablet:px-[32px] laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] left-0 px-[16px]'\n )}\n >\n <div\n className={cn(\n 'tablet:max-w-[704px] laptop:max-w-[440px] desktop:max-w-[648px] lg-desktop:max-w-[824px] max-w-[358px]',\n align === 'center' ? 'text-center' : 'laptop:text-left'\n )}\n >\n {label && (\n <Text\n size={2}\n as=\"p\"\n className={cn('hero-banner-label font-heading lg-desktop:text-[18px] desktop:text-base text-sm')}\n html={label}\n />\n )}\n {title && (\n <Heading\n as={titleSize === '4' ? 'h1' : 'h2'}\n html={title}\n size={titleSize ? (Number(titleSize || '5') as any) : size === 'sm' ? 4 : 5}\n />\n )}\n {subtitle && (\n <Text\n as=\"p\"\n size={2}\n className=\"font-heading lg-desktop:text-[18px] desktop:text-base laptop:mt-2 lg-desktop:mt-4 mt-1 text-sm\"\n html={subtitle}\n />\n )}\n {endDate && (\n <div className=\"mt-3\">\n <Countdown\n endDate={endDate}\n endDate_tz={endDate_tz}\n dateFormat={dateFormat}\n variant=\"spacious\"\n align={align}\n />\n </div>\n )}\n </div>\n\n {(primaryButton?.text || secondaryButton?.text) && (\n <div\n className={cn(\n 'lg-desktop:gap-3 flex items-center gap-2',\n align === 'center' ? 'justify-center' : 'laptop:justify-start'\n )}\n >\n {secondaryButton?.text && (\n <Button\n size=\"lg\"\n variant=\"secondary\"\n as={secondaryButton?.isCustomSecondaryButton ? 'button' : 'a'}\n href={secondaryButton.link}\n target={secondaryButton?.target}\n rel={\n secondaryButton?.target === '_blank'\n ? (secondaryButton?.rel ?? 'noopener noreferrer')\n : secondaryButton?.rel\n }\n onClick={e =>\n secondaryButton?.isCustomSecondaryButton &&\n onSecondaryClick?.(data, e, secondaryButton?.customSecondaryEventId)\n }\n >\n {secondaryButton.text}\n </Button>\n )}\n {primaryButton?.text && (\n <Button\n size=\"lg\"\n variant=\"primary\"\n as={primaryButton?.isCustomPrimaryButton ? 'button' : 'a'}\n href={primaryButton.link}\n target={primaryButton?.target}\n rel={\n primaryButton?.target === '_blank'\n ? (primaryButton?.rel ?? 'noopener noreferrer')\n : primaryButton?.rel\n }\n onClick={e =>\n primaryButton?.isCustomPrimaryButton && onPrimaryClick?.(data, e, primaryButton?.customPrimaryEventId)\n }\n >\n {primaryButton.text}\n </Button>\n )}\n </div>\n )}\n {iconArray && iconArray.length > 0 && (\n <div className={cn('flex items-center gap-2', align === 'center' ? 'justify-center' : 'justify-start')}>\n {iconArray.map((icon: any) => (\n <div key={icon?.pcImage?.url || icon?.pcImage?.alt} className=\"h-12\">\n <Picture\n className=\"laptop:w-full h-full\"\n imgClassName=\"h-full object-cover\"\n loading=\"eager\"\n alt={icon?.pcImage?.alt || ''}\n source={icon?.pcImage?.url}\n />\n </div>\n ))}\n </div>\n )}\n </div>\n\n {caption && caption.length > 0 && (\n <div className=\"hero-banner-caption-group laptop:gap-3 tablet:px-[32px] laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] desktop:pb-[24px] absolute bottom-0 z-10 flex items-stretch gap-2 px-[16px] pb-[16px]\">\n {caption.map((c: { title: string }, index: number) => (\n <React.Fragment key={c.title}>\n <Text\n size={2}\n className=\"hero-banner-product-text tablet:w-[108px] desktop:w-[156px] lg-desktop:w-[180px] laptop:text-[14px] flex-1 text-[12px]\"\n html={c.title}\n />\n {index < caption.length - 1 && <div className=\"bg-info-primary w-px\" />}\n </React.Fragment>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// \u2500\u2500 HeroBannerCarousel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface HeroBannerCarouselProps extends React.HTMLAttributes<HTMLDivElement> {\n data: HeroBannerProps['data']\n classNames?: Record<string, string>\n onPrimaryClick?: (data: any, e: any, id?: string) => void\n onSecondaryClick?: (data: any, e: any, id?: string) => void\n}\n\n/**\n * HeroBannerCarousel \u2014 arrows-only Navigation Swiper wrapping HeroBannerSlide units.\n *\n * Architecture:\n * - DECISION-A007: Navigation only, loop=false, slidesPerView=1\n * - DECISION-A003: ScrollLoadVideo videoRef as imperative pause channel\n * - DECISION-A010: initial activeIndex=0; useEffect pauses slides 1..N on mount\n * - DECISION-A006: arrow buttons at z-[20], above blockLink overlay z-10\n * - ISSUE-017: post-mount pause wins over IO rootMargin='200px' race\n */\nconst HeroBannerCarousel = React.forwardRef<HTMLDivElement, HeroBannerCarouselProps>(\n (\n {\n data,\n className,\n classNames: _classNames = {},\n onPrimaryClick: _onPrimaryClick,\n onSecondaryClick: _onSecondaryClick,\n ...rest\n },\n ref\n ) => {\n const items = data.items || []\n const carousel = data.carousel\n const containerSize = data.size ?? 'default'\n\n const swiperRef = useRef<SwiperType | null>(null)\n const [activeIndex, setActiveIndex] = useState(0)\n const [paginationEl, setPaginationEl] = useState<HTMLDivElement | null>(null)\n\n const activeTheme = items[activeIndex]?.theme ?? 'light'\n const isDark = activeTheme === 'dark'\n\n useEffect(() => {\n const swiper = swiperRef.current\n if (!swiper || !paginationEl) return\n if (swiper.pagination) {\n swiper.pagination.el = paginationEl\n swiper.pagination.init()\n swiper.pagination.render()\n swiper.pagination.update()\n }\n }, [paginationEl])\n\n const videoRefs = useRef<React.RefObject<HTMLVideoElement>[]>(items.map(() => React.createRef<HTMLVideoElement>()))\n\n const showArrowControls = (carousel?.showArrows ?? true) && items.length > 1\n const showDots = carousel?.showDots ?? true\n\n const autoplaySeconds = carousel?.autoplaySeconds\n const swiperModules =\n autoplaySeconds !== undefined && autoplaySeconds > 0\n ? [Navigation, Pagination, Autoplay]\n : [Navigation, Pagination]\n const autoplayConfig =\n autoplaySeconds !== undefined && autoplaySeconds > 0\n ? { delay: Math.max(autoplaySeconds * 1000, 3000), disableOnInteraction: false }\n : false\n\n useEffect(() => {\n videoRefs.current.forEach((vRef, i) => {\n if (i !== 0 && vRef.current) {\n vRef.current.pause()\n vRef.current.currentTime = 0\n }\n })\n }, [])\n\n const handleSlideChange = useCallback((swiper: SwiperType) => {\n const newIndex = swiper.realIndex\n videoRefs.current.forEach((vRef, i) => {\n if (i !== newIndex && vRef.current) {\n vRef.current.pause()\n vRef.current.currentTime = 0\n }\n })\n const incoming = videoRefs.current[newIndex]\n if (incoming?.current && incoming.current.paused) {\n const p = incoming.current.play()\n p?.catch(() => {})\n }\n setActiveIndex(newIndex)\n }, [])\n\n return (\n <div\n ref={ref}\n className={cn(\n 'hero-banner-carousel group relative w-full overflow-hidden',\n containerSize === 'default'\n ? 'lg-desktop:aspect-[1920/930] desktop:aspect-[1440/700] laptop:aspect-[1024/520] tablet:aspect-[768/660] aspect-[390/660]'\n : 'lg-desktop:aspect-[1920/800] desktop:aspect-[1440/640] laptop:aspect-[1024/480] tablet:aspect-[768/560] aspect-[390/560]',\n { 'aiui-dark': items[activeIndex]?.theme === 'dark' },\n className\n )}\n {...rest}\n >\n <Swiper\n onSwiper={s => (swiperRef.current = s)}\n onSlideChange={handleSlideChange}\n modules={swiperModules}\n slidesPerView={1}\n loop={true}\n initialSlide={0}\n allowTouchMove={true}\n autoplay={autoplayConfig}\n pagination={\n showDots\n ? {\n clickable: true,\n el: paginationEl,\n renderBullet: (_index: number, className: string) => `<span class=\"${className} banner-dot\"></span>`,\n }\n : false\n }\n >\n {items.map((slideData, index) => (\n <SwiperSlide key={index}>\n {({ isActive }) => (\n <HeroBannerSlide\n data={slideData}\n isActive={isActive}\n slideIndex={index}\n videoRef={videoRefs.current[index]}\n onPrimaryClick={_onPrimaryClick}\n onSecondaryClick={_onSecondaryClick}\n />\n )}\n </SwiperSlide>\n ))}\n </Swiper>\n\n {showArrowControls && (\n <div className=\"pointer-events-none absolute inset-0 z-20 flex items-center justify-between px-4\">\n <button\n type=\"button\"\n className=\"tablet:flex pointer-events-auto hidden scale-95 items-center justify-center opacity-0 transition-all duration-300 ease-out hover:scale-105 focus-visible:scale-105 group-focus-within:scale-100 group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100\"\n onClick={() => swiperRef.current?.slidePrev()}\n aria-label=\"Previous banner\"\n >\n <ChevronLeft />\n </button>\n <button\n type=\"button\"\n className=\"tablet:flex pointer-events-auto hidden scale-95 items-center justify-center opacity-0 transition-all duration-300 ease-out hover:scale-105 focus-visible:scale-105 group-focus-within:scale-100 group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100\"\n onClick={() => swiperRef.current?.slideNext()}\n aria-label=\"Next banner\"\n >\n <ChevronRight />\n </button>\n </div>\n )}\n\n {showDots && (\n <div\n ref={setPaginationEl}\n style={{ '--swiper-pagination-bottom': '0px' } as React.CSSProperties}\n className={cn(\n 'hero-banner-pagination swiper-pagination lg-desktop:!bottom-6 [&_.banner-dot.swiper-pagination-bullet-active]:bg-info-primary [&_.banner-dot]:bg-info-quaternary absolute !bottom-4 !left-1/2 z-20 flex !w-auto !-translate-x-1/2 items-center justify-center gap-2 [&_.banner-dot.swiper-pagination-bullet-active]:h-2 [&_.banner-dot.swiper-pagination-bullet-active]:w-8 [&_.banner-dot.swiper-pagination-bullet-active]:rounded-full [&_.banner-dot]:size-2 [&_.banner-dot]:rounded-full [&_.banner-dot]:transition-all [&_.banner-dot]:duration-300 [&_.banner-dot]:ease-out [&_.swiper-pagination-bullet]:m-0 [&_.swiper-pagination-bullet]:shrink-0 [&_.swiper-pagination-bullet]:opacity-100',\n { 'aiui-dark': isDark }\n )}\n />\n )}\n </div>\n )\n }\n)\n\nHeroBannerCarousel.displayName = 'HeroBannerCarousel'\n\nexport default HeroBannerCarousel\n"],
|
|
5
|
-
"mappings": "aAkBE,
|
|
6
|
-
"names": ["Fragment", "jsx", "jsxs", "React", "useRef", "useEffect", "useCallback", "useState", "Swiper", "SwiperSlide", "Navigation", "Pagination", "Autoplay", "useMediaQuery", "ScrollLoadVideo", "cn", "Button", "Heading", "Text", "Countdown", "Picture", "ChevronLeft", "ChevronRight", "HeroBannerSlide", "data", "_isActive", "videoRef", "slideIndex", "onPrimaryClick", "onSecondaryClick", "pcVideo", "padVideo", "mobileVideo", "pcImage", "padImage", "mobileImage", "isVideoLoop", "isShowVideo", "title", "subtitle", "label", "endDate", "endDate_tz", "dateFormat", "iconArray", "caption", "blockLink", "primaryButton", "secondaryButton", "theme", "align", "titleSize", "size", "isMobile", "isPad", "videoSrc", "e", "icon", "c", "index", "HeroBannerCarousel", "className", "_classNames", "_onPrimaryClick", "_onSecondaryClick", "rest", "ref", "items", "carousel", "containerSize", "swiperRef", "activeIndex", "setActiveIndex", "paginationEl", "setPaginationEl", "isDark", "swiper", "videoRefs", "showArrowControls", "showDots", "autoplaySeconds", "swiperModules", "autoplayConfig", "vRef", "i", "handleSlideChange", "newIndex", "incoming", "s", "_index", "slideData", "isActive", "HeroBannerCarousel_default"]
|
|
4
|
+
"sourcesContent": ["'use client'\nimport React, { useRef, useEffect, useCallback, useState } from 'react'\nimport { Swiper, SwiperSlide } from 'swiper/react'\nimport { Navigation, Pagination, Autoplay } from 'swiper/modules'\nimport type { Swiper as SwiperType } from 'swiper'\nimport { useMediaQuery } from 'react-responsive'\nimport ScrollLoadVideo from '../../helpers/ScrollLoadVideo.js'\nimport { cn } from '../../helpers/index.js'\nimport type { HeroBannerProps, HeroBannerSlideData } from './types.js'\nimport { Button, Heading, Text, Countdown, Picture } from '../../components/index.js'\n\nimport 'swiper/css'\nimport 'swiper/css/navigation'\nimport 'swiper/css/pagination'\n\n// \u2500\u2500 Arrow icons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst ChevronLeft = () => (\n <svg\n className=\"lg-desktop:size-14 size-10\"\n viewBox=\"0 0 40 40\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <circle cx=\"20\" cy=\"20\" r=\"20\" fill=\"currentColor\" fillOpacity=\"0.2\" />\n <path d=\"M23 13L16 20L23 27\" stroke=\"white\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n)\n\nconst ChevronRight = () => (\n <svg\n className=\"lg-desktop:size-14 size-10\"\n viewBox=\"0 0 40 40\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <circle cx=\"20\" cy=\"20\" r=\"20\" fill=\"currentColor\" fillOpacity=\"0.2\" />\n <path d=\"M17 13L24 20L17 27\" stroke=\"white\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n)\n\n// \u2500\u2500 HeroBannerSlide \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Minimal per-slide content unit. No GSAP, no withLayout, no useExposure.\n// videoRef is the imperative pause/play channel (DECISION-A003).\n\ninterface HeroBannerSlideProps {\n data: HeroBannerSlideData\n isActive: boolean\n videoRef: React.RefObject<HTMLVideoElement>\n slideIndex: number\n onPrimaryClick?: (data: any, e: any, id?: string) => void\n onSecondaryClick?: (data: any, e: any, id?: string) => void\n onTertiaryClick?: (data: any, e: any, id?: string) => void\n}\n\nconst HeroBannerSlide = ({\n data,\n isActive: _isActive,\n videoRef,\n slideIndex,\n onPrimaryClick,\n onSecondaryClick,\n onTertiaryClick,\n}: HeroBannerSlideProps) => {\n const {\n pcVideo,\n padVideo,\n mobileVideo,\n pcImage,\n padImage,\n mobileImage,\n isVideoLoop = true,\n isShowVideo,\n title,\n subtitle,\n label,\n endDate,\n endDate_tz,\n dateFormat,\n iconArray,\n caption,\n blockLink,\n primaryButton,\n secondaryButton,\n tertiaryButton,\n theme = 'light',\n align = 'left',\n titleSize,\n size = 'default',\n } = data\n\n const isMobile = useMediaQuery({ query: '(max-width: 768px)' })\n const isPad = useMediaQuery({ query: '(max-width: 1024px)' })\n\n const videoSrc = isMobile\n ? mobileVideo?.url || pcVideo?.url || ''\n : isPad\n ? padVideo?.url || pcVideo?.url || mobileVideo?.url || ''\n : pcVideo?.url || mobileVideo?.url || ''\n\n return (\n <div\n data-ui-component-id=\"HeroBannerSlide\"\n className={cn(\n 'text-info-primary relative w-full overflow-hidden',\n theme === 'dark' ? 'aiui-dark' : '',\n size === 'default'\n ? 'lg-desktop:aspect-[1920/930] desktop:aspect-[1440/700] laptop:aspect-[1024/520] tablet:aspect-[768/660] aspect-[390/660]'\n : 'lg-desktop:aspect-[1920/800] desktop:aspect-[1440/640] laptop:aspect-[1024/480] tablet:aspect-[768/560] aspect-[390/560]'\n )}\n >\n {blockLink && <a className=\"absolute inset-0 z-10\" href={blockLink} tabIndex={-1} aria-hidden=\"true\"></a>}\n {isShowVideo ? (\n <>\n {/* Poster cover layer: slide 0 = eager+high, others = lazy+auto */}\n <div data-video-poster=\"true\" className=\"absolute inset-0 size-full transition-opacity duration-500\">\n <Picture\n className=\"absolute inset-0 size-full\"\n imgClassName=\"size-full object-cover\"\n loading={slideIndex === 0 ? 'eager' : 'lazy'}\n fetchPriority={slideIndex === 0 ? 'high' : 'auto'}\n alt={pcImage?.alt || ''}\n source={`${pcImage?.url || ''} , ${padImage?.url ?? (mobileImage?.url || '')} 1024, ${mobileImage?.url || ''} 767`}\n />\n </div>\n {/* Video layer: lazy-loaded, overlays Picture via DOM order */}\n <ScrollLoadVideo\n videoRef={videoRef}\n src={videoSrc}\n poster={undefined}\n className=\"absolute inset-0 size-full\"\n videoClassName=\"size-full object-cover\"\n muted\n loop={isVideoLoop}\n playsInline\n onPlaying={(e: React.SyntheticEvent<HTMLVideoElement>) => {\n const slide = (e.currentTarget as HTMLVideoElement).closest('[data-ui-component-id=\"HeroBannerSlide\"]')\n slide?.querySelector<HTMLElement>('[data-video-poster=\"true\"]')?.classList.add('opacity-0')\n }}\n />\n </>\n ) : (\n <Picture\n className=\"absolute inset-0 size-full\"\n imgClassName=\"size-full object-cover\"\n loading=\"eager\"\n fetchPriority=\"high\"\n alt={pcImage?.alt || ''}\n source={`${pcImage?.url || ''} , ${padImage?.url ?? (mobileImage?.url || '')} 1024, ${mobileImage?.url || ''} 767`}\n />\n )}\n\n {/* Content Overlay */}\n <div\n className={cn(\n 'laptop:top-1/2 laptop:-translate-y-1/2 lg-desktop:gap-[32px] absolute top-24 z-10 flex w-full flex-col gap-[24px]',\n align === 'center'\n ? 'left-1/2 -translate-x-1/2 items-center text-center'\n : 'tablet:px-[32px] laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] left-0 px-[16px]'\n )}\n >\n <div\n className={cn(\n 'tablet:max-w-[704px] laptop:max-w-[440px] desktop:max-w-[648px] lg-desktop:max-w-[824px] max-w-[358px]',\n align === 'center' ? 'text-center' : 'laptop:text-left'\n )}\n >\n {label && (\n <Text\n size={2}\n as=\"p\"\n className={cn('hero-banner-label font-heading lg-desktop:text-[18px] desktop:text-base text-sm')}\n html={label}\n />\n )}\n {title && (\n <Heading\n as={titleSize === '4' ? 'h1' : 'h2'}\n html={title}\n size={titleSize ? (Number(titleSize || '5') as any) : size === 'sm' ? 4 : 5}\n />\n )}\n {subtitle && (\n <Text\n as=\"p\"\n size={2}\n className=\"font-heading lg-desktop:text-[18px] desktop:text-base laptop:mt-2 lg-desktop:mt-4 mt-1 text-sm\"\n html={subtitle}\n />\n )}\n {endDate && (\n <div className=\"mt-3\">\n <Countdown\n endDate={endDate}\n endDate_tz={endDate_tz}\n dateFormat={dateFormat}\n variant=\"spacious\"\n align={align}\n />\n </div>\n )}\n </div>\n\n {(primaryButton?.text || secondaryButton?.text || tertiaryButton?.text) && (\n <div\n className={cn(\n 'lg-desktop:gap-3 flex flex-wrap items-center gap-2',\n align === 'center' ? 'justify-center' : 'laptop:justify-start'\n )}\n >\n {secondaryButton?.text && (\n <Button\n size=\"lg\"\n variant=\"secondary\"\n as={secondaryButton?.isCustomSecondaryButton ? 'button' : 'a'}\n href={secondaryButton.link}\n target={secondaryButton?.target}\n rel={\n secondaryButton?.target === '_blank'\n ? (secondaryButton?.rel ?? 'noopener noreferrer')\n : secondaryButton?.rel\n }\n onClick={e =>\n secondaryButton?.isCustomSecondaryButton &&\n onSecondaryClick?.(data, e, secondaryButton?.customSecondaryEventId)\n }\n >\n {secondaryButton.text}\n </Button>\n )}\n {primaryButton?.text && (\n <Button\n size=\"lg\"\n variant=\"primary\"\n as={primaryButton?.isCustomPrimaryButton ? 'button' : 'a'}\n href={primaryButton.link}\n target={primaryButton?.target}\n rel={\n primaryButton?.target === '_blank'\n ? (primaryButton?.rel ?? 'noopener noreferrer')\n : primaryButton?.rel\n }\n onClick={e =>\n primaryButton?.isCustomPrimaryButton && onPrimaryClick?.(data, e, primaryButton?.customPrimaryEventId)\n }\n >\n {primaryButton.text}\n </Button>\n )}\n {tertiaryButton?.text && (\n <Button\n size=\"lg\"\n variant=\"link\"\n className=\"tablet:basis-auto basis-full\"\n as={tertiaryButton?.isCustomTertiaryButton ? 'button' : 'a'}\n href={tertiaryButton.link}\n target={tertiaryButton?.target}\n rel={\n tertiaryButton?.target === '_blank'\n ? (tertiaryButton?.rel ?? 'noopener noreferrer')\n : tertiaryButton?.rel\n }\n onClick={e =>\n tertiaryButton?.isCustomTertiaryButton &&\n onTertiaryClick?.(data, e, tertiaryButton?.customTertiaryEventId)\n }\n >\n {tertiaryButton.text}\n </Button>\n )}\n </div>\n )}\n {iconArray && iconArray.length > 0 && (\n <div className={cn('flex items-center gap-2', align === 'center' ? 'justify-center' : 'justify-start')}>\n {iconArray.map((icon: any) => (\n <div key={icon?.pcImage?.url || icon?.pcImage?.alt} className=\"h-12\">\n <Picture\n className=\"laptop:w-full h-full\"\n imgClassName=\"h-full object-cover\"\n loading=\"eager\"\n alt={icon?.pcImage?.alt || ''}\n source={icon?.pcImage?.url}\n />\n </div>\n ))}\n </div>\n )}\n </div>\n\n {caption && caption.length > 0 && (\n <div className=\"hero-banner-caption-group laptop:gap-3 tablet:px-[32px] laptop:px-[64px] lg-desktop:px-[calc(50%-832px)] desktop:pb-[24px] absolute bottom-0 z-10 flex items-stretch gap-2 px-[16px] pb-[16px]\">\n {caption.map((c: { title: string }, index: number) => (\n <React.Fragment key={c.title}>\n <Text\n size={2}\n className=\"hero-banner-product-text tablet:w-[108px] desktop:w-[156px] lg-desktop:w-[180px] laptop:text-[14px] flex-1 text-[12px]\"\n html={c.title}\n />\n {index < caption.length - 1 && <div className=\"bg-info-primary w-px\" />}\n </React.Fragment>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// \u2500\u2500 HeroBannerCarousel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface HeroBannerCarouselProps extends React.HTMLAttributes<HTMLDivElement> {\n data: HeroBannerProps['data']\n classNames?: Record<string, string>\n onPrimaryClick?: (data: any, e: any, id?: string) => void\n onSecondaryClick?: (data: any, e: any, id?: string) => void\n onTertiaryClick?: (data: any, e: any, id?: string) => void\n}\n\n/**\n * HeroBannerCarousel \u2014 arrows-only Navigation Swiper wrapping HeroBannerSlide units.\n *\n * Architecture:\n * - DECISION-A007: Navigation only, loop=false, slidesPerView=1\n * - DECISION-A003: ScrollLoadVideo videoRef as imperative pause channel\n * - DECISION-A010: initial activeIndex=0; useEffect pauses slides 1..N on mount\n * - DECISION-A006: arrow buttons at z-[20], above blockLink overlay z-10\n * - ISSUE-017: post-mount pause wins over IO rootMargin='200px' race\n */\nconst HeroBannerCarousel = React.forwardRef<HTMLDivElement, HeroBannerCarouselProps>(\n (\n {\n data,\n className,\n classNames: _classNames = {},\n onPrimaryClick: _onPrimaryClick,\n onSecondaryClick: _onSecondaryClick,\n onTertiaryClick: _onTertiaryClick,\n ...rest\n },\n ref\n ) => {\n const items = data.items || []\n const carousel = data.carousel\n const containerSize = data.size ?? 'default'\n\n const swiperRef = useRef<SwiperType | null>(null)\n const [activeIndex, setActiveIndex] = useState(0)\n const [paginationEl, setPaginationEl] = useState<HTMLDivElement | null>(null)\n\n const activeTheme = items[activeIndex]?.theme ?? 'light'\n const isDark = activeTheme === 'dark'\n\n useEffect(() => {\n const swiper = swiperRef.current\n if (!swiper || !paginationEl) return\n if (swiper.pagination) {\n swiper.pagination.el = paginationEl\n swiper.pagination.init()\n swiper.pagination.render()\n swiper.pagination.update()\n }\n }, [paginationEl])\n\n const videoRefs = useRef<React.RefObject<HTMLVideoElement>[]>(items.map(() => React.createRef<HTMLVideoElement>()))\n\n const showArrowControls = (carousel?.showArrows ?? true) && items.length > 1\n const showDots = carousel?.showDots ?? true\n\n const autoplaySeconds = carousel?.autoplaySeconds\n const swiperModules =\n autoplaySeconds !== undefined && autoplaySeconds > 0\n ? [Navigation, Pagination, Autoplay]\n : [Navigation, Pagination]\n const autoplayConfig =\n autoplaySeconds !== undefined && autoplaySeconds > 0\n ? { delay: Math.max(autoplaySeconds * 1000, 3000), disableOnInteraction: false }\n : false\n\n useEffect(() => {\n videoRefs.current.forEach((vRef, i) => {\n if (i !== 0 && vRef.current) {\n vRef.current.pause()\n vRef.current.currentTime = 0\n }\n })\n }, [])\n\n const handleSlideChange = useCallback((swiper: SwiperType) => {\n const newIndex = swiper.realIndex\n videoRefs.current.forEach((vRef, i) => {\n if (i !== newIndex && vRef.current) {\n vRef.current.pause()\n vRef.current.currentTime = 0\n }\n })\n const incoming = videoRefs.current[newIndex]\n if (incoming?.current && incoming.current.paused) {\n const p = incoming.current.play()\n p?.catch(() => {})\n }\n setActiveIndex(newIndex)\n }, [])\n\n return (\n <div\n ref={ref}\n className={cn(\n 'hero-banner-carousel group relative w-full overflow-hidden',\n containerSize === 'default'\n ? 'lg-desktop:aspect-[1920/930] desktop:aspect-[1440/700] laptop:aspect-[1024/520] tablet:aspect-[768/660] aspect-[390/660]'\n : 'lg-desktop:aspect-[1920/800] desktop:aspect-[1440/640] laptop:aspect-[1024/480] tablet:aspect-[768/560] aspect-[390/560]',\n { 'aiui-dark': items[activeIndex]?.theme === 'dark' },\n className\n )}\n {...rest}\n >\n <Swiper\n onSwiper={s => (swiperRef.current = s)}\n onSlideChange={handleSlideChange}\n modules={swiperModules}\n slidesPerView={1}\n loop={true}\n initialSlide={0}\n allowTouchMove={true}\n autoplay={autoplayConfig}\n pagination={\n showDots\n ? {\n clickable: true,\n el: paginationEl,\n renderBullet: (_index: number, className: string) => `<span class=\"${className} banner-dot\"></span>`,\n }\n : false\n }\n >\n {items.map((slideData, index) => (\n <SwiperSlide key={index}>\n {({ isActive }) => (\n <HeroBannerSlide\n data={slideData}\n isActive={isActive}\n slideIndex={index}\n videoRef={videoRefs.current[index]}\n onPrimaryClick={_onPrimaryClick}\n onSecondaryClick={_onSecondaryClick}\n onTertiaryClick={_onTertiaryClick}\n />\n )}\n </SwiperSlide>\n ))}\n </Swiper>\n\n {showArrowControls && (\n <div className=\"pointer-events-none absolute inset-0 z-20 flex items-center justify-between px-4\">\n <button\n type=\"button\"\n className=\"tablet:flex pointer-events-auto hidden scale-95 items-center justify-center opacity-0 transition-all duration-300 ease-out hover:scale-105 focus-visible:scale-105 group-focus-within:scale-100 group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100\"\n onClick={() => swiperRef.current?.slidePrev()}\n aria-label=\"Previous banner\"\n >\n <ChevronLeft />\n </button>\n <button\n type=\"button\"\n className=\"tablet:flex pointer-events-auto hidden scale-95 items-center justify-center opacity-0 transition-all duration-300 ease-out hover:scale-105 focus-visible:scale-105 group-focus-within:scale-100 group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100\"\n onClick={() => swiperRef.current?.slideNext()}\n aria-label=\"Next banner\"\n >\n <ChevronRight />\n </button>\n </div>\n )}\n\n {showDots && (\n <div\n ref={setPaginationEl}\n style={{ '--swiper-pagination-bottom': '0px' } as React.CSSProperties}\n className={cn(\n 'hero-banner-pagination swiper-pagination lg-desktop:!bottom-6 [&_.banner-dot.swiper-pagination-bullet-active]:bg-info-primary [&_.banner-dot]:bg-info-quaternary absolute !bottom-4 !left-1/2 z-20 flex !w-auto !-translate-x-1/2 items-center justify-center gap-2 [&_.banner-dot.swiper-pagination-bullet-active]:h-2 [&_.banner-dot.swiper-pagination-bullet-active]:w-8 [&_.banner-dot.swiper-pagination-bullet-active]:rounded-full [&_.banner-dot]:size-2 [&_.banner-dot]:rounded-full [&_.banner-dot]:transition-all [&_.banner-dot]:duration-300 [&_.banner-dot]:ease-out [&_.swiper-pagination-bullet]:m-0 [&_.swiper-pagination-bullet]:shrink-0 [&_.swiper-pagination-bullet]:opacity-100',\n { 'aiui-dark': isDark }\n )}\n />\n )}\n </div>\n )\n }\n)\n\nHeroBannerCarousel.displayName = 'HeroBannerCarousel'\n\nexport default HeroBannerCarousel\n"],
|
|
5
|
+
"mappings": "aAkBE,OAiGM,YAAAA,GA1FJ,OAAAC,EAPF,QAAAC,MAAA,oBAjBF,OAAOC,GAAS,UAAAC,EAAQ,aAAAC,EAAW,eAAAC,EAAa,YAAAC,MAAgB,QAChE,OAAS,UAAAC,EAAQ,eAAAC,MAAmB,eACpC,OAAS,cAAAC,EAAY,cAAAC,EAAY,YAAAC,MAAgB,iBAEjD,OAAS,iBAAAC,MAAqB,mBAC9B,OAAOC,OAAqB,mCAC5B,OAAS,MAAAC,MAAU,yBAEnB,OAAS,UAAAC,EAAQ,WAAAC,GAAS,QAAAC,EAAM,aAAAC,GAAW,WAAAC,MAAe,4BAE1D,MAAO,aACP,MAAO,wBACP,MAAO,wBAIP,MAAMC,GAAc,IAClBnB,EAAC,OACC,UAAU,6BACV,QAAQ,YACR,KAAK,OACL,MAAM,6BACN,cAAY,OAEZ,UAAAD,EAAC,UAAO,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,KAAK,eAAe,YAAY,MAAM,EACrEA,EAAC,QAAK,EAAE,qBAAqB,OAAO,QAAQ,YAAY,IAAI,cAAc,QAAQ,eAAe,QAAQ,GAC3G,EAGIqB,GAAe,IACnBpB,EAAC,OACC,UAAU,6BACV,QAAQ,YACR,KAAK,OACL,MAAM,6BACN,cAAY,OAEZ,UAAAD,EAAC,UAAO,GAAG,KAAK,GAAG,KAAK,EAAE,KAAK,KAAK,eAAe,YAAY,MAAM,EACrEA,EAAC,QAAK,EAAE,qBAAqB,OAAO,QAAQ,YAAY,IAAI,cAAc,QAAQ,eAAe,QAAQ,GAC3G,EAiBIsB,GAAkB,CAAC,CACvB,KAAAC,EACA,SAAUC,EACV,SAAAC,EACA,WAAAC,EACA,eAAAC,EACA,iBAAAC,EACA,gBAAAC,CACF,IAA4B,CAC1B,KAAM,CACJ,QAAAC,EACA,SAAAC,EACA,YAAAC,EACA,QAAAC,EACA,SAAAC,EACA,YAAAC,EACA,YAAAC,EAAc,GACd,YAAAC,EACA,MAAAC,EACA,SAAAC,EACA,MAAAC,EACA,QAAAC,EACA,WAAAC,EACA,WAAAC,EACA,UAAAC,EACA,QAAAC,EACA,UAAAC,EACA,cAAAC,EACA,gBAAAC,EACA,eAAAC,EACA,MAAAC,EAAQ,QACR,MAAAC,EAAQ,OACR,UAAAC,EACA,KAAAC,EAAO,SACT,EAAI9B,EAEE+B,EAAW1C,EAAc,CAAE,MAAO,oBAAqB,CAAC,EACxD2C,EAAQ3C,EAAc,CAAE,MAAO,qBAAsB,CAAC,EAEtD4C,EAAWF,EACbtB,GAAa,KAAOF,GAAS,KAAO,GACpCyB,EACExB,GAAU,KAAOD,GAAS,KAAOE,GAAa,KAAO,GACrDF,GAAS,KAAOE,GAAa,KAAO,GAE1C,OACE/B,EAAC,OACC,uBAAqB,kBACrB,UAAWa,EACT,oDACAoC,IAAU,OAAS,YAAc,GACjCG,IAAS,UACL,2HACA,0HACN,EAEC,UAAAP,GAAa9C,EAAC,KAAE,UAAU,wBAAwB,KAAM8C,EAAW,SAAU,GAAI,cAAY,OAAO,EACpGT,EACCpC,EAAAF,GAAA,CAEE,UAAAC,EAAC,OAAI,oBAAkB,OAAO,UAAU,6DACtC,SAAAA,EAACmB,EAAA,CACC,UAAU,6BACV,aAAa,yBACb,QAASO,IAAe,EAAI,QAAU,OACtC,cAAeA,IAAe,EAAI,OAAS,OAC3C,IAAKO,GAAS,KAAO,GACrB,OAAQ,GAAGA,GAAS,KAAO,EAAE,MAAMC,GAAU,MAAQC,GAAa,KAAO,GAAG,UAAUA,GAAa,KAAO,EAAE,OAC9G,EACF,EAEAnC,EAACa,GAAA,CACC,SAAUY,EACV,IAAK+B,EACL,OAAQ,OACR,UAAU,6BACV,eAAe,yBACf,MAAK,GACL,KAAMpB,EACN,YAAW,GACX,UAAYqB,GAA8C,CACzCA,EAAE,cAAmC,QAAQ,0CAA0C,GAC/F,cAA2B,4BAA4B,GAAG,UAAU,IAAI,WAAW,CAC5F,EACF,GACF,EAEAzD,EAACmB,EAAA,CACC,UAAU,6BACV,aAAa,yBACb,QAAQ,QACR,cAAc,OACd,IAAKc,GAAS,KAAO,GACrB,OAAQ,GAAGA,GAAS,KAAO,EAAE,MAAMC,GAAU,MAAQC,GAAa,KAAO,GAAG,UAAUA,GAAa,KAAO,EAAE,OAC9G,EAIFlC,EAAC,OACC,UAAWa,EACT,oHACAqC,IAAU,SACN,qDACA,oFACN,EAEA,UAAAlD,EAAC,OACC,UAAWa,EACT,yGACAqC,IAAU,SAAW,cAAgB,kBACvC,EAEC,UAAAX,GACCxC,EAACiB,EAAA,CACC,KAAM,EACN,GAAG,IACH,UAAWH,EAAG,iFAAiF,EAC/F,KAAM0B,EACR,EAEDF,GACCtC,EAACgB,GAAA,CACC,GAAIoC,IAAc,IAAM,KAAO,KAC/B,KAAMd,EACN,KAAMc,EAAa,OAAOA,GAAa,GAAG,EAAYC,IAAS,KAAO,EAAI,EAC5E,EAEDd,GACCvC,EAACiB,EAAA,CACC,GAAG,IACH,KAAM,EACN,UAAU,iGACV,KAAMsB,EACR,EAEDE,GACCzC,EAAC,OAAI,UAAU,OACb,SAAAA,EAACkB,GAAA,CACC,QAASuB,EACT,WAAYC,EACZ,WAAYC,EACZ,QAAQ,WACR,MAAOQ,EACT,EACF,GAEJ,GAEEJ,GAAe,MAAQC,GAAiB,MAAQC,GAAgB,OAChEhD,EAAC,OACC,UAAWa,EACT,qDACAqC,IAAU,SAAW,iBAAmB,sBAC1C,EAEC,UAAAH,GAAiB,MAChBhD,EAACe,EAAA,CACC,KAAK,KACL,QAAQ,YACR,GAAIiC,GAAiB,wBAA0B,SAAW,IAC1D,KAAMA,EAAgB,KACtB,OAAQA,GAAiB,OACzB,IACEA,GAAiB,SAAW,SACvBA,GAAiB,KAAO,sBACzBA,GAAiB,IAEvB,QAASS,GACPT,GAAiB,yBACjBpB,IAAmBL,EAAMkC,EAAGT,GAAiB,sBAAsB,EAGpE,SAAAA,EAAgB,KACnB,EAEDD,GAAe,MACd/C,EAACe,EAAA,CACC,KAAK,KACL,QAAQ,UACR,GAAIgC,GAAe,sBAAwB,SAAW,IACtD,KAAMA,EAAc,KACpB,OAAQA,GAAe,OACvB,IACEA,GAAe,SAAW,SACrBA,GAAe,KAAO,sBACvBA,GAAe,IAErB,QAASU,GACPV,GAAe,uBAAyBpB,IAAiBJ,EAAMkC,EAAGV,GAAe,oBAAoB,EAGtG,SAAAA,EAAc,KACjB,EAEDE,GAAgB,MACfjD,EAACe,EAAA,CACC,KAAK,KACL,QAAQ,OACR,UAAU,+BACV,GAAIkC,GAAgB,uBAAyB,SAAW,IACxD,KAAMA,EAAe,KACrB,OAAQA,GAAgB,OACxB,IACEA,GAAgB,SAAW,SACtBA,GAAgB,KAAO,sBACxBA,GAAgB,IAEtB,QAASQ,GACPR,GAAgB,wBAChBpB,IAAkBN,EAAMkC,EAAGR,GAAgB,qBAAqB,EAGjE,SAAAA,EAAe,KAClB,GAEJ,EAEDL,GAAaA,EAAU,OAAS,GAC/B5C,EAAC,OAAI,UAAWc,EAAG,0BAA2BqC,IAAU,SAAW,iBAAmB,eAAe,EAClG,SAAAP,EAAU,IAAKc,GACd1D,EAAC,OAAmD,UAAU,OAC5D,SAAAA,EAACmB,EAAA,CACC,UAAU,uBACV,aAAa,sBACb,QAAQ,QACR,IAAKuC,GAAM,SAAS,KAAO,GAC3B,OAAQA,GAAM,SAAS,IACzB,GAPQA,GAAM,SAAS,KAAOA,GAAM,SAAS,GAQ/C,CACD,EACH,GAEJ,EAECb,GAAWA,EAAQ,OAAS,GAC3B7C,EAAC,OAAI,UAAU,iMACZ,SAAA6C,EAAQ,IAAI,CAACc,EAAsBC,IAClC3D,EAACC,EAAM,SAAN,CACC,UAAAF,EAACiB,EAAA,CACC,KAAM,EACN,UAAU,yHACV,KAAM0C,EAAE,MACV,EACCC,EAAQf,EAAQ,OAAS,GAAK7C,EAAC,OAAI,UAAU,uBAAuB,IANlD2D,EAAE,KAOvB,CACD,EACH,GAEJ,CAEJ,EAsBME,EAAqB3D,EAAM,WAC/B,CACE,CACE,KAAAqB,EACA,UAAAuC,EACA,WAAYC,EAAc,CAAC,EAC3B,eAAgBC,EAChB,iBAAkBC,EAClB,gBAAiBC,EACjB,GAAGC,CACL,EACAC,IACG,CACH,MAAMC,EAAQ9C,EAAK,OAAS,CAAC,EACvB+C,EAAW/C,EAAK,SAChBgD,EAAgBhD,EAAK,MAAQ,UAE7BiD,EAAYrE,EAA0B,IAAI,EAC1C,CAACsE,EAAaC,CAAc,EAAIpE,EAAS,CAAC,EAC1C,CAACqE,EAAcC,CAAe,EAAItE,EAAgC,IAAI,EAGtEuE,GADcR,EAAMI,CAAW,GAAG,OAAS,WAClB,OAE/BrE,EAAU,IAAM,CACd,MAAM0E,EAASN,EAAU,QACrB,CAACM,GAAU,CAACH,GACZG,EAAO,aACTA,EAAO,WAAW,GAAKH,EACvBG,EAAO,WAAW,KAAK,EACvBA,EAAO,WAAW,OAAO,EACzBA,EAAO,WAAW,OAAO,EAE7B,EAAG,CAACH,CAAY,CAAC,EAEjB,MAAMI,EAAY5E,EAA4CkE,EAAM,IAAI,IAAMnE,EAAM,UAA4B,CAAC,CAAC,EAE5G8E,GAAqBV,GAAU,YAAc,KAASD,EAAM,OAAS,EACrEY,EAAWX,GAAU,UAAY,GAEjCY,EAAkBZ,GAAU,gBAC5Ba,EACJD,IAAoB,QAAaA,EAAkB,EAC/C,CAACzE,EAAYC,EAAYC,CAAQ,EACjC,CAACF,EAAYC,CAAU,EACvB0E,EACJF,IAAoB,QAAaA,EAAkB,EAC/C,CAAE,MAAO,KAAK,IAAIA,EAAkB,IAAM,GAAI,EAAG,qBAAsB,EAAM,EAC7E,GAEN9E,EAAU,IAAM,CACd2E,EAAU,QAAQ,QAAQ,CAACM,EAAMC,IAAM,CACjCA,IAAM,GAAKD,EAAK,UAClBA,EAAK,QAAQ,MAAM,EACnBA,EAAK,QAAQ,YAAc,EAE/B,CAAC,CACH,EAAG,CAAC,CAAC,EAEL,MAAME,EAAoBlF,EAAayE,GAAuB,CAC5D,MAAMU,EAAWV,EAAO,UACxBC,EAAU,QAAQ,QAAQ,CAACM,EAAMC,IAAM,CACjCA,IAAME,GAAYH,EAAK,UACzBA,EAAK,QAAQ,MAAM,EACnBA,EAAK,QAAQ,YAAc,EAE/B,CAAC,EACD,MAAMI,EAAWV,EAAU,QAAQS,CAAQ,EACvCC,GAAU,SAAWA,EAAS,QAAQ,QAC9BA,EAAS,QAAQ,KAAK,GAC7B,MAAM,IAAM,CAAC,CAAC,EAEnBf,EAAec,CAAQ,CACzB,EAAG,CAAC,CAAC,EAEL,OACEvF,EAAC,OACC,IAAKmE,EACL,UAAWtD,EACT,6DACAyD,IAAkB,UACd,2HACA,2HACJ,CAAE,YAAaF,EAAMI,CAAW,GAAG,QAAU,MAAO,EACpDX,CACF,EACC,GAAGK,EAEJ,UAAAnE,EAACO,EAAA,CACC,SAAUmF,GAAMlB,EAAU,QAAUkB,EACpC,cAAeH,EACf,QAASJ,EACT,cAAe,EACf,KAAM,GACN,aAAc,EACd,eAAgB,GAChB,SAAUC,EACV,WACEH,EACI,CACE,UAAW,GACX,GAAIN,EACJ,aAAc,CAACgB,EAAgB7B,IAAsB,gBAAgBA,CAAS,sBAChF,EACA,GAGL,SAAAO,EAAM,IAAI,CAACuB,EAAWhC,IACrB5D,EAACQ,EAAA,CACE,UAAC,CAAE,SAAAqF,CAAS,IACX7F,EAACsB,GAAA,CACC,KAAMsE,EACN,SAAUC,EACV,WAAYjC,EACZ,SAAUmB,EAAU,QAAQnB,CAAK,EACjC,eAAgBI,EAChB,iBAAkBC,EAClB,gBAAiBC,EACnB,GAVcN,CAYlB,CACD,EACH,EAECoB,GACC/E,EAAC,OAAI,UAAU,mFACb,UAAAD,EAAC,UACC,KAAK,SACL,UAAU,+QACV,QAAS,IAAMwE,EAAU,SAAS,UAAU,EAC5C,aAAW,kBAEX,SAAAxE,EAACoB,GAAA,EAAY,EACf,EACApB,EAAC,UACC,KAAK,SACL,UAAU,+QACV,QAAS,IAAMwE,EAAU,SAAS,UAAU,EAC5C,aAAW,cAEX,SAAAxE,EAACqB,GAAA,EAAa,EAChB,GACF,EAGD4D,GACCjF,EAAC,OACC,IAAK4E,EACL,MAAO,CAAE,6BAA8B,KAAM,EAC7C,UAAW9D,EACT,uqBACA,CAAE,YAAa+D,CAAO,CACxB,EACF,GAEJ,CAEJ,CACF,EAEAhB,EAAmB,YAAc,qBAEjC,IAAOiC,GAAQjC",
|
|
6
|
+
"names": ["Fragment", "jsx", "jsxs", "React", "useRef", "useEffect", "useCallback", "useState", "Swiper", "SwiperSlide", "Navigation", "Pagination", "Autoplay", "useMediaQuery", "ScrollLoadVideo", "cn", "Button", "Heading", "Text", "Countdown", "Picture", "ChevronLeft", "ChevronRight", "HeroBannerSlide", "data", "_isActive", "videoRef", "slideIndex", "onPrimaryClick", "onSecondaryClick", "onTertiaryClick", "pcVideo", "padVideo", "mobileVideo", "pcImage", "padImage", "mobileImage", "isVideoLoop", "isShowVideo", "title", "subtitle", "label", "endDate", "endDate_tz", "dateFormat", "iconArray", "caption", "blockLink", "primaryButton", "secondaryButton", "tertiaryButton", "theme", "align", "titleSize", "size", "isMobile", "isPad", "videoSrc", "e", "icon", "c", "index", "HeroBannerCarousel", "className", "_classNames", "_onPrimaryClick", "_onSecondaryClick", "_onTertiaryClick", "rest", "ref", "items", "carousel", "containerSize", "swiperRef", "activeIndex", "setActiveIndex", "paginationEl", "setPaginationEl", "isDark", "swiper", "videoRefs", "showArrowControls", "showDots", "autoplaySeconds", "swiperModules", "autoplayConfig", "vRef", "i", "handleSlideChange", "newIndex", "incoming", "s", "_index", "slideData", "isActive", "HeroBannerCarousel_default"]
|
|
7
7
|
}
|
|
@@ -55,6 +55,12 @@ export interface HeroBannerSlideData {
|
|
|
55
55
|
isCustomSecondaryButton?: boolean;
|
|
56
56
|
customSecondaryEventId?: string;
|
|
57
57
|
} & Omit<ButtonProps, 'children'>;
|
|
58
|
+
tertiaryButton?: {
|
|
59
|
+
text: string;
|
|
60
|
+
link?: string;
|
|
61
|
+
isCustomTertiaryButton?: boolean;
|
|
62
|
+
customTertiaryEventId?: string;
|
|
63
|
+
} & Omit<ButtonProps, 'children'>;
|
|
58
64
|
theme?: Theme;
|
|
59
65
|
size?: 'default' | 'sm';
|
|
60
66
|
titleSize?: TitleSizeType;
|
|
@@ -111,6 +117,14 @@ export interface HeroBannerProps extends Omit<React.HTMLAttributes<HTMLDivElemen
|
|
|
111
117
|
/** 自定义事件ID,传递给 onSecondaryClick */
|
|
112
118
|
customSecondaryEventId?: string;
|
|
113
119
|
} & Omit<ButtonProps, 'children'>;
|
|
120
|
+
/** 第三按钮文本和配置 */
|
|
121
|
+
tertiaryButton?: {
|
|
122
|
+
text: string;
|
|
123
|
+
link?: string;
|
|
124
|
+
isCustomTertiaryButton?: boolean;
|
|
125
|
+
/** 自定义事件ID,传递给 onTertiaryClick */
|
|
126
|
+
customTertiaryEventId?: string;
|
|
127
|
+
} & Omit<ButtonProps, 'children'>;
|
|
114
128
|
/** 主题 */
|
|
115
129
|
theme?: Theme;
|
|
116
130
|
/** 大小, 默认default,单banner, 可选sm, 用于多banner场景 */
|
|
@@ -141,5 +155,6 @@ export interface HeroBannerProps extends Omit<React.HTMLAttributes<HTMLDivElemen
|
|
|
141
155
|
};
|
|
142
156
|
onSecondaryClick?: (data: any, e: any, customPrimaryEventId?: string) => void;
|
|
143
157
|
onPrimaryClick?: (data: any, e: any, customSecondaryEventId?: string) => void;
|
|
158
|
+
onTertiaryClick?: (data: any, e: any, customTertiaryEventId?: string) => void;
|
|
144
159
|
}
|
|
145
160
|
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface WebPushPopupProps {
|
|
3
|
+
/** 弹窗标题,不传则不渲染标题 */
|
|
4
|
+
title?: string;
|
|
5
|
+
/** 弹窗描述文案,不传则不渲染描述 */
|
|
6
|
+
description?: string;
|
|
7
|
+
/** "稍后"按钮文案,默认 'Later' */
|
|
8
|
+
dismissText?: string;
|
|
9
|
+
/** "订阅"按钮文案,默认 'Allow' */
|
|
10
|
+
subscribeText?: string;
|
|
11
|
+
/** 图标区域内容,默认渲染 🔔 */
|
|
12
|
+
icon?: React.ReactNode;
|
|
13
|
+
/** 用户点击"订阅"按钮时触发 */
|
|
14
|
+
onSubscribe: () => void;
|
|
15
|
+
/** 用户点击"稍后"按钮时触发 */
|
|
16
|
+
onDismiss: () => void;
|
|
17
|
+
/** 额外的容器 className */
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* WebPushPopup
|
|
22
|
+
*
|
|
23
|
+
* Emarsys Web Push 订阅引导弹窗(非侵入式,固定在右上角)。
|
|
24
|
+
* 所有文案、图标均通过 props 传入,不硬编码,支持多品牌自定义。
|
|
25
|
+
*
|
|
26
|
+
* 配合 `useEmarsysWebPush` Hook 使用:
|
|
27
|
+
*
|
|
28
|
+
* ```tsx
|
|
29
|
+
* import { useEmarsysWebPush, WebPushPopup } from '@anker-in/headless-ui/biz'
|
|
30
|
+
*
|
|
31
|
+
* const { shouldShowPopup, subscribe, dismissPopup } = useEmarsysWebPush({ ... })
|
|
32
|
+
*
|
|
33
|
+
* {shouldShowPopup && (
|
|
34
|
+
* <WebPushPopup
|
|
35
|
+
* title="Welcome to Anker"
|
|
36
|
+
* description="Get the latest deals and product updates."
|
|
37
|
+
* onSubscribe={subscribe}
|
|
38
|
+
* onDismiss={dismissPopup}
|
|
39
|
+
* />
|
|
40
|
+
* )}
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
declare const WebPushPopup: React.NamedExoticComponent<WebPushPopupProps>;
|
|
44
|
+
export default WebPushPopup;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as i}from"react/jsx-runtime";import f,{useCallback as n}from"react";import{cn as s}from"../../helpers/utils.js";const u=f.memo(function({title:t,description:o,dismissText:r="Later",subscribeText:p="Allow",icon:d="\u{1F514}",onSubscribe:a,onDismiss:l,className:c}){const b=n(()=>{a()},[a]),m=n(()=>{l()},[l]);return i("div",{role:"dialog","aria-modal":"false","aria-labelledby":t?"web-push-popup-title":void 0,className:s("fixed right-4 top-4 z-[9999]","laptop:w-80 w-72","flex items-start gap-3","rounded-xl bg-white shadow-lg","border border-black/5","p-4",c),children:[e("span",{className:"mt-0.5 shrink-0 text-2xl leading-none","aria-hidden":"true",children:d}),i("div",{className:"flex min-w-0 flex-1 flex-col gap-2",children:[t&&e("p",{id:"web-push-popup-title",className:"truncate text-sm font-semibold leading-snug text-gray-900",children:t}),o&&e("p",{className:"text-xs leading-relaxed text-gray-500",children:o}),i("div",{className:"flex items-center justify-end gap-2 pt-0.5",children:[e("button",{type:"button",onClick:m,className:s("rounded-md px-3 py-1.5","text-xs font-medium text-gray-400","transition-colors duration-150","hover:bg-gray-100 hover:text-gray-600","focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-400"),children:r}),e("button",{type:"button",onClick:b,className:s("rounded-md px-3 py-1.5","text-xs font-medium text-white","bg-gray-900","transition-colors duration-150","hover:bg-gray-700","focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-900"),children:p})]})]})]})});u.displayName="WebPushPopup";var h=u;export{h as default};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/biz-components/WebPushPopup/index.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport React, { useCallback } from 'react'\nimport { cn } from '../../helpers/utils.js'\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Types\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface WebPushPopupProps {\n /** \u5F39\u7A97\u6807\u9898\uFF0C\u4E0D\u4F20\u5219\u4E0D\u6E32\u67D3\u6807\u9898 */\n title?: string\n /** \u5F39\u7A97\u63CF\u8FF0\u6587\u6848\uFF0C\u4E0D\u4F20\u5219\u4E0D\u6E32\u67D3\u63CF\u8FF0 */\n description?: string\n /** \"\u7A0D\u540E\"\u6309\u94AE\u6587\u6848\uFF0C\u9ED8\u8BA4 'Later' */\n dismissText?: string\n /** \"\u8BA2\u9605\"\u6309\u94AE\u6587\u6848\uFF0C\u9ED8\u8BA4 'Allow' */\n subscribeText?: string\n /** \u56FE\u6807\u533A\u57DF\u5185\u5BB9\uFF0C\u9ED8\u8BA4\u6E32\u67D3 \uD83D\uDD14 */\n icon?: React.ReactNode\n /** \u7528\u6237\u70B9\u51FB\"\u8BA2\u9605\"\u6309\u94AE\u65F6\u89E6\u53D1 */\n onSubscribe: () => void\n /** \u7528\u6237\u70B9\u51FB\"\u7A0D\u540E\"\u6309\u94AE\u65F6\u89E6\u53D1 */\n onDismiss: () => void\n /** \u989D\u5916\u7684\u5BB9\u5668 className */\n className?: string\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Component\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * WebPushPopup\n *\n * Emarsys Web Push \u8BA2\u9605\u5F15\u5BFC\u5F39\u7A97\uFF08\u975E\u4FB5\u5165\u5F0F\uFF0C\u56FA\u5B9A\u5728\u53F3\u4E0A\u89D2\uFF09\u3002\n * \u6240\u6709\u6587\u6848\u3001\u56FE\u6807\u5747\u901A\u8FC7 props \u4F20\u5165\uFF0C\u4E0D\u786C\u7F16\u7801\uFF0C\u652F\u6301\u591A\u54C1\u724C\u81EA\u5B9A\u4E49\u3002\n *\n * \u914D\u5408 `useEmarsysWebPush` Hook \u4F7F\u7528\uFF1A\n *\n * ```tsx\n * import { useEmarsysWebPush, WebPushPopup } from '@anker-in/headless-ui/biz'\n *\n * const { shouldShowPopup, subscribe, dismissPopup } = useEmarsysWebPush({ ... })\n *\n * {shouldShowPopup && (\n * <WebPushPopup\n * title=\"Welcome to Anker\"\n * description=\"Get the latest deals and product updates.\"\n * onSubscribe={subscribe}\n * onDismiss={dismissPopup}\n * />\n * )}\n * ```\n */\nconst WebPushPopup = React.memo(function WebPushPopup({\n title,\n description,\n dismissText = 'Later',\n subscribeText = 'Allow',\n icon = '\uD83D\uDD14',\n onSubscribe,\n onDismiss,\n className,\n}: WebPushPopupProps) {\n const handleSubscribe = useCallback(() => {\n onSubscribe()\n }, [onSubscribe])\n\n const handleDismiss = useCallback(() => {\n onDismiss()\n }, [onDismiss])\n\n return (\n <div\n role=\"dialog\"\n aria-modal=\"false\"\n aria-labelledby={title ? 'web-push-popup-title' : undefined}\n className={cn(\n 'fixed right-4 top-4 z-[9999]',\n 'laptop:w-80 w-72',\n 'flex items-start gap-3',\n 'rounded-xl bg-white shadow-lg',\n 'border border-black/5',\n 'p-4',\n className\n )}\n >\n {/* Icon */}\n <span className=\"mt-0.5 shrink-0 text-2xl leading-none\" aria-hidden=\"true\">\n {icon}\n </span>\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n {title && (\n <p id=\"web-push-popup-title\" className=\"truncate text-sm font-semibold leading-snug text-gray-900\">\n {title}\n </p>\n )}\n\n {description && <p className=\"text-xs leading-relaxed text-gray-500\">{description}</p>}\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-2 pt-0.5\">\n <button\n type=\"button\"\n onClick={handleDismiss}\n className={cn(\n 'rounded-md px-3 py-1.5',\n 'text-xs font-medium text-gray-400',\n 'transition-colors duration-150',\n 'hover:bg-gray-100 hover:text-gray-600',\n 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-400'\n )}\n >\n {dismissText}\n </button>\n\n <button\n type=\"button\"\n onClick={handleSubscribe}\n className={cn(\n 'rounded-md px-3 py-1.5',\n 'text-xs font-medium text-white',\n 'bg-gray-900',\n 'transition-colors duration-150',\n 'hover:bg-gray-700',\n 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-900'\n )}\n >\n {subscribeText}\n </button>\n </div>\n </div>\n </div>\n )\n})\n\nWebPushPopup.displayName = 'WebPushPopup'\n\nexport default WebPushPopup\n"],
|
|
5
|
+
"mappings": "aAyFM,cAAAA,EAeE,QAAAC,MAfF,oBAvFN,OAAOC,GAAS,eAAAC,MAAmB,QACnC,OAAS,MAAAC,MAAU,yBAoDnB,MAAMC,EAAeH,EAAM,KAAK,SAAsB,CACpD,MAAAI,EACA,YAAAC,EACA,YAAAC,EAAc,QACd,cAAAC,EAAgB,QAChB,KAAAC,EAAO,YACP,YAAAC,EACA,UAAAC,EACA,UAAAC,CACF,EAAsB,CACpB,MAAMC,EAAkBX,EAAY,IAAM,CACxCQ,EAAY,CACd,EAAG,CAACA,CAAW,CAAC,EAEVI,EAAgBZ,EAAY,IAAM,CACtCS,EAAU,CACZ,EAAG,CAACA,CAAS,CAAC,EAEd,OACEX,EAAC,OACC,KAAK,SACL,aAAW,QACX,kBAAiBK,EAAQ,uBAAyB,OAClD,UAAWF,EACT,+BACA,mBACA,yBACA,gCACA,wBACA,MACAS,CACF,EAGA,UAAAb,EAAC,QAAK,UAAU,wCAAwC,cAAY,OACjE,SAAAU,EACH,EAGAT,EAAC,OAAI,UAAU,qCACZ,UAAAK,GACCN,EAAC,KAAE,GAAG,uBAAuB,UAAU,4DACpC,SAAAM,EACH,EAGDC,GAAeP,EAAC,KAAE,UAAU,wCAAyC,SAAAO,EAAY,EAGlFN,EAAC,OAAI,UAAU,6CACb,UAAAD,EAAC,UACC,KAAK,SACL,QAASe,EACT,UAAWX,EACT,yBACA,oCACA,iCACA,wCACA,6GACF,EAEC,SAAAI,EACH,EAEAR,EAAC,UACC,KAAK,SACL,QAASc,EACT,UAAWV,EACT,yBACA,iCACA,cACA,iCACA,oBACA,6GACF,EAEC,SAAAK,EACH,GACF,GACF,GACF,CAEJ,CAAC,EAEDJ,EAAa,YAAc,eAE3B,IAAOW,EAAQX",
|
|
6
|
+
"names": ["jsx", "jsxs", "React", "useCallback", "cn", "WebPushPopup", "title", "description", "dismissText", "subscribeText", "icon", "onSubscribe", "onDismiss", "className", "handleSubscribe", "handleDismiss", "WebPushPopup_default"]
|
|
7
|
+
}
|
|
@@ -140,6 +140,10 @@ export { default as MiniCartCircleProgress } from './MiniCart/CircleProgress.js'
|
|
|
140
140
|
export type { CircleProgressProps as MiniCartCircleProgressProps } from './MiniCart/CircleProgress.js';
|
|
141
141
|
export { default as MiniCartDialog, MiniCartDialog as MiniCartDialogComponent } from './MiniCart/MiniCartDialog.js';
|
|
142
142
|
export type { MiniCartDialogProps, MiniCartDialogCopy, MiniCartDialogSemanticName, CartData, } from './MiniCart/MiniCartDialog.js';
|
|
143
|
+
export { default as WebPushPopup } from './WebPushPopup/index.js';
|
|
144
|
+
export type { WebPushPopupProps } from './WebPushPopup/index.js';
|
|
145
|
+
export { useEmarsysWebPush } from '../hooks/useEmarsysWebPush.js';
|
|
146
|
+
export type { UseEmarsysWebPushOptions, UseEmarsysWebPushReturn, EmarsysContactInfo, EmarsysInitConfig, } from '../hooks/useEmarsysWebPush.js';
|
|
143
147
|
/**
|
|
144
148
|
* 组件映射对象
|
|
145
149
|
*/
|