@anker-in/headless-ui 1.3.78 → 1.3.79
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.js +1 -1
- package/dist/cjs/biz-components/HeroBanner/HeroBannerCarousel.d.ts +9 -10
- package/dist/cjs/biz-components/HeroBanner/HeroBannerCarousel.js +1 -1
- package/dist/cjs/biz-components/HeroBanner/index.d.ts +3 -0
- package/dist/cjs/biz-components/HeroBanner/index.js +1 -1
- package/dist/cjs/biz-components/HeroBanner/mediaResolver.d.ts +58 -0
- package/dist/cjs/biz-components/HeroBanner/mediaResolver.js +1 -0
- package/dist/cjs/biz-components/HeroBanner/types.d.ts +116 -8
- package/dist/cjs/biz-components/HeroBanner/types.js +1 -1
- package/dist/cjs/biz-components/SecondaryBanner/SecondaryBannerCarousel.d.ts +9 -9
- package/dist/cjs/biz-components/SecondaryBanner/SecondaryBannerCarousel.js +1 -1
- package/dist/cjs/biz-components/SecondaryBanner/index.d.ts +53 -6
- package/dist/cjs/biz-components/SecondaryBanner/index.js +1 -1
- package/dist/cjs/biz-components/index.d.ts +1 -0
- package/dist/cjs/biz-components/index.js +1 -1
- package/dist/cjs/helpers/ScrollLoadVideo.js +1 -1
- package/dist/cjs/hooks/useCarouselPlayback.d.ts +110 -0
- package/dist/cjs/hooks/useCarouselPlayback.js +1 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/shared/CarouselControls.d.ts +75 -0
- package/dist/cjs/shared/CarouselControls.js +1 -0
- package/dist/esm/biz-components/HeroBanner/HeroBanner.js +1 -1
- package/dist/esm/biz-components/HeroBanner/HeroBannerCarousel.d.ts +9 -10
- package/dist/esm/biz-components/HeroBanner/HeroBannerCarousel.js +1 -1
- package/dist/esm/biz-components/HeroBanner/index.d.ts +3 -0
- package/dist/esm/biz-components/HeroBanner/index.js +1 -1
- package/dist/esm/biz-components/HeroBanner/mediaResolver.d.ts +58 -0
- package/dist/esm/biz-components/HeroBanner/mediaResolver.js +1 -0
- package/dist/esm/biz-components/HeroBanner/types.d.ts +116 -8
- package/dist/esm/biz-components/SecondaryBanner/SecondaryBannerCarousel.d.ts +9 -9
- package/dist/esm/biz-components/SecondaryBanner/SecondaryBannerCarousel.js +1 -1
- package/dist/esm/biz-components/SecondaryBanner/index.d.ts +53 -6
- package/dist/esm/biz-components/SecondaryBanner/index.js +1 -1
- package/dist/esm/biz-components/index.d.ts +1 -0
- package/dist/esm/biz-components/index.js +1 -1
- package/dist/esm/helpers/ScrollLoadVideo.js +1 -1
- package/dist/esm/hooks/useCarouselPlayback.d.ts +110 -0
- package/dist/esm/hooks/useCarouselPlayback.js +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/shared/CarouselControls.d.ts +75 -0
- package/dist/esm/shared/CarouselControls.js +1 -0
- package/package.json +1 -1
- package/style.css +32 -23
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { type MutableRefObject } from 'react';
|
|
2
|
+
import type { Swiper as SwiperType } from 'swiper';
|
|
3
|
+
/**
|
|
4
|
+
* 2-state carousel playback machine (final interaction matrix 2026-10-12):
|
|
5
|
+
* - AUTOPLAYING: the hook's own countdown timer drives slideNext; progress fill runs
|
|
6
|
+
* - STOPPED: manual pause via the ⏸/▶ button; resumed via ▶ — OR implicitly
|
|
7
|
+
* reset to AUTOPLAYING by ANY manual navigation (arrow / swipe / replay)
|
|
8
|
+
* PC hover has NO effect on the carousel (removed in the interaction-matrix revision).
|
|
9
|
+
*
|
|
10
|
+
* 2026-10-10 beta-bugfix round: the countdown timer is SELF-OWNED
|
|
11
|
+
* (setTimeout + remaining-ms bookkeeping) instead of delegating to Swiper's
|
|
12
|
+
* Autoplay module, whose internal transitionend/touchEnd resume() resurrected
|
|
13
|
+
* stopped carousels (CODING #1452/#1460) and fell back to the module default
|
|
14
|
+
* delay on frames without data-swiper-autoplay (#1453). Keyboard focus within
|
|
15
|
+
* the carousel freezes the countdown until focus leaves (WCAG 2.2.2, #1461).
|
|
16
|
+
*
|
|
17
|
+
* 2026-10-12 final matrix: navigation no longer stops autoplay — the earlier
|
|
18
|
+
* swipe→STOPPED (D4) / arrow-keeps-STOPPED semantics were superseded by the
|
|
19
|
+
* product matrix (arrow/swipe/replay all reset to AUTOPLAYING + fresh budget).
|
|
20
|
+
*/
|
|
21
|
+
export type CarouselPlaybackState = 'AUTOPLAYING' | 'STOPPED';
|
|
22
|
+
/**
|
|
23
|
+
* What caused a slide change. `initial` is reserved for the consumer's mount
|
|
24
|
+
* emission; the hook itself only produces autoplay / arrow / swipe.
|
|
25
|
+
*/
|
|
26
|
+
export type CarouselSlideTrigger = 'initial' | 'autoplay' | 'arrow' | 'swipe';
|
|
27
|
+
export interface UseCarouselPlaybackOptions {
|
|
28
|
+
/** Total slide count */
|
|
29
|
+
frameCount: number;
|
|
30
|
+
/** Resolve the effective autoplay interval (seconds) for a frame; undefined = the frame holds */
|
|
31
|
+
resolveFrameIntervalSec: (index: number) => number | undefined;
|
|
32
|
+
/** Master gate: autoplay runs only when true (valid intervals AND frameCount > 1) */
|
|
33
|
+
isAutoplayEnabled: boolean;
|
|
34
|
+
/** Ref holding the live Swiper instance (populated via handleSwiperInit) */
|
|
35
|
+
swiperRef: MutableRefObject<SwiperType | null>;
|
|
36
|
+
/** Noun used in the aria-live announcement: `${slideStatusLabel} ${n} / ${total}` */
|
|
37
|
+
slideStatusLabel: string;
|
|
38
|
+
/**
|
|
39
|
+
* Optional media choreography on slide change (e.g. pause outgoing videos,
|
|
40
|
+
* play the incoming one). Image-only carousels omit this.
|
|
41
|
+
*/
|
|
42
|
+
onSlideMediaSync?: (newIndex: number) => void;
|
|
43
|
+
/** Fired when the active slide changes, with the cause of the change */
|
|
44
|
+
onSlideChange?: (newIndex: number, trigger: CarouselSlideTrigger) => void;
|
|
45
|
+
/** Fired on arrow navigation, before Swiper confirms the transition */
|
|
46
|
+
onArrowNavigate?: (direction: 'prev' | 'next', fromIndex: number, toIndex: number) => void;
|
|
47
|
+
/** Fired when the pause/play button toggles playback (paused = entered STOPPED) */
|
|
48
|
+
onPauseToggle?: (paused: boolean, activeIndex: number) => void;
|
|
49
|
+
/**
|
|
50
|
+
* 1488: per-frame gate — video frames return true so the countdown RESTARTS
|
|
51
|
+
* with a fresh full budget when the frame's video actually starts playing
|
|
52
|
+
* (slow networks: the poster window no longer eats the frame's interval).
|
|
53
|
+
* If the video never plays, the activation-time budget still ticks away as
|
|
54
|
+
* the fallback deadline, so the frame can never stall forever.
|
|
55
|
+
*/
|
|
56
|
+
delaysCountdownForVideo?: (index: number) => boolean;
|
|
57
|
+
}
|
|
58
|
+
export interface UseCarouselPlaybackResult {
|
|
59
|
+
/** Attach to the carousel root element (merge with any forwarded ref) */
|
|
60
|
+
rootRef: MutableRefObject<Element | null>;
|
|
61
|
+
activeIndex: number;
|
|
62
|
+
playbackState: CarouselPlaybackState;
|
|
63
|
+
/** Bump-key for the progress-fill CSS animation; changes restart the fill */
|
|
64
|
+
resetNonce: number;
|
|
65
|
+
/** aria-live announcement text for manual slide switches (D16) */
|
|
66
|
+
announcement: string;
|
|
67
|
+
/** Effective autoplay interval (seconds) of the active frame; undefined = hold */
|
|
68
|
+
activeFrameIntervalSec: number | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* True while the frame countdown is actually ticking. Drives the progress
|
|
71
|
+
* fill's animationPlayState so the visual fill and the real timer can never
|
|
72
|
+
* desync (unlike the old Swiper-module design).
|
|
73
|
+
*/
|
|
74
|
+
isTimerRunning: boolean;
|
|
75
|
+
/** Wire to Swiper's onSwiper — stores the instance and syncs slide a11y */
|
|
76
|
+
handleSwiperInit: (swiper: SwiperType) => void;
|
|
77
|
+
handleSlideChange: (swiper: SwiperType) => void;
|
|
78
|
+
/** Wire to Swiper's onSlideChangeTransitionEnd — re-syncs inert/aria-hidden after loopFix */
|
|
79
|
+
handleTransitionEnd: (swiper: SwiperType) => void;
|
|
80
|
+
handleSliderFirstMove: (swiper: SwiperType, event: TouchEvent) => void;
|
|
81
|
+
goToSlide: (direction: 'prev' | 'next') => void;
|
|
82
|
+
/** Jump to an arbitrary frame (progress-segment click). Same reset semantics as arrows. */
|
|
83
|
+
goToSlideIndex: (index: number) => void;
|
|
84
|
+
handlePausePlayClick: () => void;
|
|
85
|
+
/**
|
|
86
|
+
* Replay path (final matrix): reset to AUTOPLAYING and restart the current
|
|
87
|
+
* frame with a fresh full countdown (进度圆点从0重新填充).
|
|
88
|
+
*/
|
|
89
|
+
resumeWithFreshCountdown: () => void;
|
|
90
|
+
/** 1488: 视频帧开始真实播放时调用——重启该帧倒计时(每次进场仅生效一次) */
|
|
91
|
+
notifyVideoPlaying: (index: number) => void;
|
|
92
|
+
/** Attach to the carousel root: keyboard focus freezes the countdown (#1461) */
|
|
93
|
+
handleFocus: () => void;
|
|
94
|
+
handleBlur: (e: React.FocusEvent<HTMLElement>) => void;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* useCarouselPlayback — shared carousel interaction state machine + countdown.
|
|
98
|
+
*
|
|
99
|
+
* Owns: playback state machine, self-managed countdown timer (with true
|
|
100
|
+
* freeze/resume), pause/play toggle, IntersectionObserver viewport gating
|
|
101
|
+
* (D17), keyboard-focus gating (WCAG 2.2.2), touch-swipe detection (D4),
|
|
102
|
+
* aria-live announcements (D16), slide inert/aria-hidden sync (#1461) and the
|
|
103
|
+
* progress-fill restart nonce.
|
|
104
|
+
*
|
|
105
|
+
* Video-specific behavior (play-once on STOPPED, replay button, multi-theme)
|
|
106
|
+
* is intentionally NOT here — consumers wire it off the returned
|
|
107
|
+
* playbackState/activeIndex so image-only carousels get a clean subset.
|
|
108
|
+
*/
|
|
109
|
+
export declare const useCarouselPlayback: (options: UseCarouselPlaybackOptions) => UseCarouselPlaybackResult;
|
|
110
|
+
export default useCarouselPlayback;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{useCallback as o,useEffect as u,useRef as t,useState as y}from"react";const ue=x=>{const{frameCount:R,resolveFrameIntervalSec:B,isAutoplayEnabled:S,swiperRef:s,slideStatusLabel:Y}=x,C=t(null),E=t(!1),T=t(!1),[c,L]=y(0),[$,g]=y(0),[H,k]=y(""),[w,p]=y(S?"AUTOPLAYING":"STOPPED"),[q,K]=y(!0),[z,D]=y(!1),O=t(w);u(()=>{O.current=w},[w]);const a=t(c);u(()=>{a.current=c},[c]);const h=t(R);u(()=>{h.current=R});const v=t(S);u(()=>{v.current=S});const I=t(Y);u(()=>{I.current=Y});const l=t(x);u(()=>{l.current=x});const d=B(c),N=S&&R>1&&w==="AUTOPLAYING"&&q&&!z&&d!==void 0,m=t(null),V=t(0),W=t(0),F=t(null),b=t({index:c,interval:d}),M=t(null),_=o(e=>{l.current.delaysCountdownForVideo?.(e)&&e===a.current&&M.current!==e&&(M.current=e,b.current={index:-1,interval:void 0},g(n=>n+1))},[]),A=o(()=>{m.current!=null&&(clearTimeout(m.current),m.current=null)},[]),U=o(()=>{if(m.current!=null){const e=Date.now()-V.current;F.current=Math.max(0,W.current-e),A()}},[A]),j=o(()=>{m.current=null;const e=s.current;!e||e.destroyed||e.slideNext()},[s]);u(()=>{const e=b.current.index===-1;if((b.current.index!==c||b.current.interval!==d)&&(b.current={index:c,interval:d},F.current=null,e||(M.current=null)),!N||d===void 0){U();return}const n=F.current??d*1e3;return W.current=n,V.current=Date.now(),m.current=setTimeout(j,n),U},[N,c,d,$,j,U]),u(()=>A,[A]),u(()=>{const e=C.current;if(!e||typeof IntersectionObserver>"u")return;const n=new IntersectionObserver(r=>{const i=r[0];i&&K(i.isIntersecting)},{threshold:.1});return n.observe(e),()=>n.disconnect()},[]);const f=o(()=>{const e=C.current;if(!e||typeof e.querySelectorAll!="function")return;const n=e.querySelectorAll(".swiper-slide");!n.length||!Array.from(n).some(i=>i.classList.contains("swiper-slide-active"))||n.forEach(i=>{const P=i.classList.contains("swiper-slide-active");i.toggleAttribute("inert",!P),P?i.removeAttribute("aria-hidden"):i.setAttribute("aria-hidden","true")})},[]),J=o(e=>{s.current=e,f()},[s,f]);u(()=>{f()},[f]);const Q=o(()=>{f()},[f]),X=o(e=>{const n=e.realIndex;l.current.onSlideMediaSync?.(n);let r=null;E.current?(E.current=!1,k(`${I.current} ${n+1} / ${h.current}`),v.current&&p("AUTOPLAYING"),r="swipe"):T.current?(T.current=!1,r="arrow"):O.current==="AUTOPLAYING"&&(r="autoplay"),L(n),r&&l.current.onSlideChange?.(n,r),f()},[f]),Z=o(e=>{const n=h.current;if(n<2)return;const r=a.current,i=e==="next"?(r+1)%n:(r-1+n)%n;k(`${I.current} ${i+1} / ${n}`),L(i),T.current=!0,e==="next"?s.current?.slideNext():s.current?.slidePrev(),v.current&&(p("AUTOPLAYING"),g(P=>P+1)),l.current.onArrowNavigate?.(e,r,i)},[s]),ee=o(e=>{const n=h.current;n<2||e===a.current||e<0||e>=n||(k(`${I.current} ${e+1} / ${n}`),L(e),T.current=!0,s.current?.slideToLoop(e),v.current&&(p("AUTOPLAYING"),g(r=>r+1)),l.current.onArrowNavigate?.(e>a.current?"next":"prev",a.current,e))},[s]),ne=o(()=>{v.current&&(b.current={index:-1,interval:void 0},p("AUTOPLAYING"),g(e=>e+1))},[]),re=o(()=>{v.current&&(O.current==="STOPPED"?(p("AUTOPLAYING"),l.current.onPauseToggle?.(!1,a.current)):(p("STOPPED"),l.current.onPauseToggle?.(!0,a.current)))},[]),te=o((e,n)=>{const r=n.pointerType;E.current=r!=="mouse"},[]),G=t(!1);u(()=>{const e=r=>{r.key==="Tab"&&(G.current=!0)},n=()=>{G.current=!1};return window.addEventListener("keydown",e),window.addEventListener("pointerdown",n),()=>{window.removeEventListener("keydown",e),window.removeEventListener("pointerdown",n)}},[]);const oe=o(()=>{G.current&&D(!0)},[]),ie=o(e=>{e.currentTarget.contains(e.relatedTarget)||D(!1)},[]);return{rootRef:C,activeIndex:c,playbackState:w,resetNonce:$,announcement:H,activeFrameIntervalSec:d,isTimerRunning:N,handleSwiperInit:J,handleSlideChange:X,handleTransitionEnd:Q,handleSliderFirstMove:te,goToSlide:Z,goToSlideIndex:ee,handlePausePlayClick:re,resumeWithFreshCountdown:ne,notifyVideoPlaying:_,handleFocus:oe,handleBlur:ie}};var ce=ue;export{ce as default,ue as useCarouselPlayback};
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export { useGridRowCount } from './hooks/useGridRowCount.js';
|
|
|
10
10
|
export { useIntersectionObserverDelay } from './hooks/useIntersectionObserver.js';
|
|
11
11
|
export { useRollout } from './hooks/useRollout.js';
|
|
12
12
|
export { useViewItemList } from './hooks/useViewItemList.js';
|
|
13
|
+
export { useCarouselPlayback } from './hooks/useCarouselPlayback.js';
|
|
14
|
+
export type { CarouselPlaybackState, CarouselSlideTrigger, UseCarouselPlaybackOptions, UseCarouselPlaybackResult, } from './hooks/useCarouselPlayback.js';
|
|
13
15
|
export { cn, getLocalizedPath, noop } from './helpers/index.js';
|
|
14
16
|
export { formatPrice, languageTerritory, PRICE_SYMBOL } from './helpers/index.js';
|
|
15
17
|
export { LocalizedLink, type LocalizedLinkProps } from './shared/LocalizedLink.js';
|
|
18
|
+
export { CarouselArrows, CarouselProgressControls } from './shared/CarouselControls.js';
|
|
19
|
+
export type { CarouselArrowsProps, CarouselProgressControlsProps } from './shared/CarouselControls.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./components/index.js";import{gaTrack as
|
|
1
|
+
export*from"./components/index.js";import{gaTrack as s}from"./shared/track.js";import{trackUrlRef as t,buildTrackingRef as p}from"./shared/trackUrlRef.js";import{default as u}from"./hooks/useCountDown.js";import{useDraggableScroll as m}from"./hooks/useDraggableScroll.js";import{useExposure as f}from"./hooks/useExposure.js";import{useGridRowCount as C}from"./hooks/useGridRowCount.js";import{useIntersectionObserverDelay as P}from"./hooks/useIntersectionObserver.js";import{useRollout as d}from"./hooks/useRollout.js";import{useViewItemList as k}from"./hooks/useViewItemList.js";import{useCarouselPlayback as L}from"./hooks/useCarouselPlayback.js";import{cn as T,getLocalizedPath as h,noop as w}from"./helpers/index.js";import{formatPrice as j,languageTerritory as z,PRICE_SYMBOL as A}from"./helpers/index.js";import{LocalizedLink as E}from"./shared/LocalizedLink.js";import{CarouselArrows as O,CarouselProgressControls as v}from"./shared/CarouselControls.js";export{O as CarouselArrows,v as CarouselProgressControls,E as LocalizedLink,A as PRICE_SYMBOL,p as buildTrackingRef,T as cn,j as formatPrice,s as gaTrack,h as getLocalizedPath,z as languageTerritory,w as noop,t as trackUrlRef,L as useCarouselPlayback,u as useCountDown,m as useDraggableScroll,f as useExposure,C as useGridRowCount,P as useIntersectionObserverDelay,d as useRollout,k as useViewItemList};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export declare const CarouselChevronLeft: () => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const CarouselChevronRight: () => import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare const PauseIcon: () => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export declare const PlayIcon: () => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export interface CarouselArrowsProps {
|
|
6
|
+
onPrev: () => void;
|
|
7
|
+
onNext: () => void;
|
|
8
|
+
prevLabel: string;
|
|
9
|
+
nextLabel: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Prev/next arrow overlay. Visible ≥1025 only (D12): hidden below laptop,
|
|
13
|
+
* revealed on group hover / focus-within above it. Rendered at z-20, above
|
|
14
|
+
* any blockLink overlay (z-10).
|
|
15
|
+
*/
|
|
16
|
+
export declare const CarouselArrows: ({ onPrev, onNext, prevLabel, nextLabel }: CarouselArrowsProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export interface CarouselCornerToggleProps {
|
|
18
|
+
isPaused: boolean;
|
|
19
|
+
onToggle: () => void;
|
|
20
|
+
/** aria-label while paused (shows ▶) */
|
|
21
|
+
pausedLabel: string;
|
|
22
|
+
/** aria-label while playing (shows ⏸) */
|
|
23
|
+
playingLabel: string;
|
|
24
|
+
/** Class prefix, e.g. 'hero-banner' / 'secondary-banner' */
|
|
25
|
+
classPrefix: string;
|
|
26
|
+
/** Same theme axis as the progress controls; defaults to 'light' */
|
|
27
|
+
theme?: 'light' | 'dark';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 合并后的暂停/播放按钮(交互收敛 2026-10):轮播与视频共用一个右下角
|
|
31
|
+
* 圆钮——尺寸与样式与原 dots 区 ⏸ 完全一致(size-8 玻璃材质)。调用方按
|
|
32
|
+
* 当前帧语义决定 isPaused 的状态源(轮播状态机 或 视频真实状态)与点击
|
|
33
|
+
* 行为(同停同播 或 纯视频直控)。
|
|
34
|
+
*/
|
|
35
|
+
export declare const CarouselCornerToggle: ({ isPaused, onToggle, pausedLabel, playingLabel, classPrefix, theme, }: CarouselCornerToggleProps) => import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
export interface CarouselProgressControlsProps {
|
|
37
|
+
frameCount: number;
|
|
38
|
+
activeIndex: number;
|
|
39
|
+
/** Change restarts the active segment's fill animation */
|
|
40
|
+
resetNonce: number;
|
|
41
|
+
/** Active frame interval (seconds); undefined = static full segment */
|
|
42
|
+
activeFrameIntervalSec: number | undefined;
|
|
43
|
+
isAutoplayEnabled: boolean;
|
|
44
|
+
/** True while the countdown timer is actually ticking (drives fill playState) */
|
|
45
|
+
isTimerRunning: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Optional segment click-to-jump handler (1467): when provided, progress
|
|
48
|
+
* segments become focusable buttons that jump to the frame (SecondaryBanner
|
|
49
|
+
* legacy behavior). Omit to keep segments display-only (HeroBanner).
|
|
50
|
+
*/
|
|
51
|
+
onSegmentClick?: (index: number) => void;
|
|
52
|
+
/** Accessible label per segment, e.g. (i) => `Go to slide ${i + 1}` */
|
|
53
|
+
segmentLabel?: (index: number) => string;
|
|
54
|
+
/** Class/keyframes prefix, e.g. 'hero-banner' / 'secondary-banner' */
|
|
55
|
+
classPrefix: string;
|
|
56
|
+
/**
|
|
57
|
+
* Frame theme (same axis as the banner content): 'light' = dark text over
|
|
58
|
+
* light media → dark-glass controls; 'dark' = white text over dark media
|
|
59
|
+
* → light-glass controls. Defaults to 'light'.
|
|
60
|
+
*/
|
|
61
|
+
theme?: 'light' | 'dark';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Segmented progress capsule, centered at the bottom (Figma 6349:5115:
|
|
65
|
+
* capsule 144×32 with an inset 112×8 indicator row, 8px gap). Material
|
|
66
|
+
* follows the frame theme: black-20% mask + dark icon on light media,
|
|
67
|
+
* white-20% mask + white icon on dark media. The active segment is a
|
|
68
|
+
* stretched white bar whose fill is a CSS animation keyed by
|
|
69
|
+
* `${activeIndex}-${resetNonce}`; animationPlayState freezes it outside
|
|
70
|
+
* AUTOPLAYING so the fill and the Swiper timer stay in sync — resume paths
|
|
71
|
+
* never bump resetNonce, so progress is never lost.
|
|
72
|
+
* 暂停/播放按钮已迁移至右下角 CarouselCornerToggle(交互收敛 2026-10)。
|
|
73
|
+
*/
|
|
74
|
+
export declare const CarouselProgressControls: ({ frameCount, activeIndex, resetNonce, activeFrameIntervalSec, isAutoplayEnabled, isTimerRunning, onSegmentClick, segmentLabel, classPrefix, theme, }: CarouselProgressControlsProps) => import("react/jsx-runtime").JSX.Element;
|
|
75
|
+
export default CarouselProgressControls;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as c}from"react/jsx-runtime";import{cn as a}from"../helpers/index.js";const y=()=>c("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:"black",fillOpacity:"0.5"}),e("path",{d:"M23 14L17 19.5L23 25",stroke:"white",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]}),k=()=>c("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:"black",fillOpacity:"0.5"}),e("path",{d:"M17 14L23 19.5L17 25",stroke:"white",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]}),x=()=>c("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:[e("rect",{x:"4",y:"3",width:"2.6",height:"10",rx:"1",fill:"currentColor"}),e("rect",{x:"9.4",y:"3",width:"2.6",height:"10",rx:"1",fill:"currentColor"})]}),C=()=>e("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",children:e("path",{d:"M5 3.5v9c0 .8.9 1.3 1.6.9l7-4.5c.6-.4.6-1.4 0-1.8l-7-4.5c-.7-.4-1.6.1-1.6.9Z",fill:"currentColor"})}),z=({onPrev:i,onNext:r,prevLabel:n,nextLabel:l})=>c("div",{className:"lg-desktop:px-8 pointer-events-none absolute inset-0 z-20 flex items-center justify-between px-4",children:[e("button",{type:"button",className:"laptop: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 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 active:scale-95 group-focus-within:scale-100 group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100",onClick:i,"aria-label":n,children:e(y,{})}),e("button",{type:"button",className:"laptop: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 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 active:scale-95 group-focus-within:scale-100 group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100",onClick:r,"aria-label":l,children:e(k,{})})]}),P=({isPaused:i,onToggle:r,pausedLabel:n,playingLabel:l,classPrefix:u,theme:g="light"})=>{const o=g==="light";return e("button",{type:"button",className:a(`${u}-corner-toggle lg-desktop:bottom-6 lg-desktop:right-8 absolute bottom-4 right-4 z-20 flex size-8 items-center justify-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80`,o?"bg-black/20 text-black/60 hover:bg-black/30 active:bg-black/40":"bg-white/20 text-white hover:bg-white/30 active:bg-white/40"),"aria-label":i?n:l,onClick:r,children:i?e(C,{}):e(x,{})})},N=({frameCount:i,activeIndex:r,resetNonce:n,activeFrameIntervalSec:l,isAutoplayEnabled:u,isTimerRunning:g,onSegmentClick:o,segmentLabel:v,classPrefix:t,theme:m="light"})=>{const w=`@keyframes ${t}-progress-fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}`,p=m==="light";return c("div",{className:a(`${t}-controls lg-desktop:bottom-6 absolute bottom-4 left-1/2 z-20 flex -translate-x-1/2 items-center gap-2`),children:[e("style",{children:w}),e("div",{className:a(`${t}-progress flex h-8 items-center gap-2 rounded-full px-4`,p?"bg-black/20":"bg-white/20"),"aria-hidden":o?void 0:"true",role:o?"group":void 0,"aria-label":o?"Slides":void 0,children:Array.from({length:i}).map((L,s)=>{const b=s===r,d=a(`${t}-progress-segment block size-2 rounded-full`,p?"bg-black/40":"bg-white/40"),f=a(`${t}-progress-segment block h-2 w-8 overflow-hidden rounded-full`,p?"bg-black/40":"bg-white/40"),h=u&&l!==void 0?e("span",{className:`${t}-progress-fill block size-full origin-left rounded-full bg-white`,style:{animation:`${t}-progress-fill ${l*1e3}ms linear forwards`,animationPlayState:g?"running":"paused"}},`${r}-${n}`):e("span",{className:`${t}-progress-fill block size-full rounded-full bg-white`});return o?e("button",{type:"button","aria-label":v?.(s)??`Go to slide ${s+1}`,"aria-current":b?"true":void 0,onClick:()=>o(s),className:"flex items-center justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80",children:b?e("span",{className:f,children:h}):e("span",{className:d})},s):b?e("span",{className:f,children:h},s):e("span",{className:d},s)})})]})};var j=N;export{z as CarouselArrows,y as CarouselChevronLeft,k as CarouselChevronRight,P as CarouselCornerToggle,N as CarouselProgressControls,x as PauseIcon,C as PlayIcon,j as default};
|
package/package.json
CHANGED
package/style.css
CHANGED
|
@@ -545,9 +545,6 @@ video {
|
|
|
545
545
|
.\!bottom-0 {
|
|
546
546
|
bottom: 0px !important;
|
|
547
547
|
}
|
|
548
|
-
.\!bottom-4 {
|
|
549
|
-
bottom: 1rem !important;
|
|
550
|
-
}
|
|
551
548
|
.\!bottom-\[70px\] {
|
|
552
549
|
bottom: 70px !important;
|
|
553
550
|
}
|
|
@@ -776,9 +773,6 @@ video {
|
|
|
776
773
|
.z-\[200\] {
|
|
777
774
|
z-index: 200;
|
|
778
775
|
}
|
|
779
|
-
.z-\[20\] {
|
|
780
|
-
z-index: 20;
|
|
781
|
-
}
|
|
782
776
|
.z-\[210\] {
|
|
783
777
|
z-index: 210;
|
|
784
778
|
}
|
|
@@ -1255,6 +1249,9 @@ video {
|
|
|
1255
1249
|
.aspect-\[358\/480\] {
|
|
1256
1250
|
aspect-ratio: 358/480;
|
|
1257
1251
|
}
|
|
1252
|
+
.aspect-\[390\/1440\] {
|
|
1253
|
+
aspect-ratio: 390/1440;
|
|
1254
|
+
}
|
|
1258
1255
|
.aspect-\[390\/240\] {
|
|
1259
1256
|
aspect-ratio: 390/240;
|
|
1260
1257
|
}
|
|
@@ -5636,6 +5633,9 @@ video {
|
|
|
5636
5633
|
.tablet\:aspect-\[704\/480\] {
|
|
5637
5634
|
aspect-ratio: 704/480;
|
|
5638
5635
|
}
|
|
5636
|
+
.tablet\:aspect-\[768\/1320\] {
|
|
5637
|
+
aspect-ratio: 768/1320;
|
|
5638
|
+
}
|
|
5639
5639
|
.tablet\:aspect-\[768\/240\] {
|
|
5640
5640
|
aspect-ratio: 768/240;
|
|
5641
5641
|
}
|
|
@@ -6195,6 +6195,9 @@ video {
|
|
|
6195
6195
|
.laptop\:top-\[76px\] {
|
|
6196
6196
|
top: 76px;
|
|
6197
6197
|
}
|
|
6198
|
+
.laptop\:top-\[min\(50\%\2c 50vh\)\] {
|
|
6199
|
+
top: min(50%, 50vh);
|
|
6200
|
+
}
|
|
6198
6201
|
.laptop\:-order-1 {
|
|
6199
6202
|
order: -1;
|
|
6200
6203
|
}
|
|
@@ -6333,20 +6336,20 @@ video {
|
|
|
6333
6336
|
.laptop\:aspect-\[1024\/384\] {
|
|
6334
6337
|
aspect-ratio: 1024/384;
|
|
6335
6338
|
}
|
|
6339
|
+
.laptop\:aspect-\[1024\/400\] {
|
|
6340
|
+
aspect-ratio: 1024/400;
|
|
6341
|
+
}
|
|
6342
|
+
.laptop\:aspect-\[1024\/432\] {
|
|
6343
|
+
aspect-ratio: 1024/432;
|
|
6344
|
+
}
|
|
6336
6345
|
.laptop\:aspect-\[1024\/480\] {
|
|
6337
6346
|
aspect-ratio: 1024/480;
|
|
6338
6347
|
}
|
|
6339
6348
|
.laptop\:aspect-\[1024\/520\] {
|
|
6340
6349
|
aspect-ratio: 1024/520;
|
|
6341
6350
|
}
|
|
6342
|
-
.laptop\:aspect-\[
|
|
6343
|
-
aspect-ratio:
|
|
6344
|
-
}
|
|
6345
|
-
.laptop\:aspect-\[1920\/720\] {
|
|
6346
|
-
aspect-ratio: 1920/720;
|
|
6347
|
-
}
|
|
6348
|
-
.laptop\:aspect-\[1920\/930\] {
|
|
6349
|
-
aspect-ratio: 1920/930;
|
|
6351
|
+
.laptop\:aspect-\[1024\/864\] {
|
|
6352
|
+
aspect-ratio: 1024/864;
|
|
6350
6353
|
}
|
|
6351
6354
|
.laptop\:aspect-\[2\/1\] {
|
|
6352
6355
|
aspect-ratio: 2/1;
|
|
@@ -7545,12 +7548,24 @@ video {
|
|
|
7545
7548
|
.desktop\:aspect-\[1312\/512\] {
|
|
7546
7549
|
aspect-ratio: 1312/512;
|
|
7547
7550
|
}
|
|
7551
|
+
.desktop\:aspect-\[1440\/1152\] {
|
|
7552
|
+
aspect-ratio: 1440/1152;
|
|
7553
|
+
}
|
|
7548
7554
|
.desktop\:aspect-\[1440\/384\] {
|
|
7549
7555
|
aspect-ratio: 1440/384;
|
|
7550
7556
|
}
|
|
7557
|
+
.desktop\:aspect-\[1440\/480\] {
|
|
7558
|
+
aspect-ratio: 1440/480;
|
|
7559
|
+
}
|
|
7551
7560
|
.desktop\:aspect-\[1440\/512\] {
|
|
7552
7561
|
aspect-ratio: 1440/512;
|
|
7553
7562
|
}
|
|
7563
|
+
.desktop\:aspect-\[1440\/560\] {
|
|
7564
|
+
aspect-ratio: 1440/560;
|
|
7565
|
+
}
|
|
7566
|
+
.desktop\:aspect-\[1440\/576\] {
|
|
7567
|
+
aspect-ratio: 1440/576;
|
|
7568
|
+
}
|
|
7554
7569
|
.desktop\:aspect-\[1440\/640\] {
|
|
7555
7570
|
aspect-ratio: 1440/640;
|
|
7556
7571
|
}
|
|
@@ -7563,15 +7578,6 @@ video {
|
|
|
7563
7578
|
.desktop\:aspect-\[19\/9\] {
|
|
7564
7579
|
aspect-ratio: 19/9;
|
|
7565
7580
|
}
|
|
7566
|
-
.desktop\:aspect-\[1920\/480\] {
|
|
7567
|
-
aspect-ratio: 1920/480;
|
|
7568
|
-
}
|
|
7569
|
-
.desktop\:aspect-\[1920\/720\] {
|
|
7570
|
-
aspect-ratio: 1920/720;
|
|
7571
|
-
}
|
|
7572
|
-
.desktop\:aspect-\[1920\/930\] {
|
|
7573
|
-
aspect-ratio: 1920/930;
|
|
7574
|
-
}
|
|
7575
7581
|
.desktop\:aspect-\[316\/384\] {
|
|
7576
7582
|
aspect-ratio: 316/384;
|
|
7577
7583
|
}
|
|
@@ -8281,6 +8287,9 @@ video {
|
|
|
8281
8287
|
.lg-desktop\:aspect-\[1664\/640\] {
|
|
8282
8288
|
aspect-ratio: 1664/640;
|
|
8283
8289
|
}
|
|
8290
|
+
.lg-desktop\:aspect-\[1920\/1440\] {
|
|
8291
|
+
aspect-ratio: 1920/1440;
|
|
8292
|
+
}
|
|
8284
8293
|
.lg-desktop\:aspect-\[1920\/480\] {
|
|
8285
8294
|
aspect-ratio: 1920/480;
|
|
8286
8295
|
}
|