coverflow-carousel 0.1.0-dev.0 → 0.1.1-dev.0

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/README.md CHANGED
@@ -11,28 +11,22 @@
11
11
  </div>
12
12
  <br>
13
13
 
14
- **Install**
15
-
14
+ # Install
16
15
  ```console
17
16
  yarn add coverflow-carousel
18
17
  ```
19
18
  <br>
20
19
 
21
- **Import**
22
-
20
+ # Import
23
21
  ```javascript
24
- // Registers <coverflow-carousel> via customElements.define(...)
25
- import 'coverflow-carousel';
26
-
27
- // Optional helper: subscribe to events + apply styles
28
- import { initCoverflowCarousels } from 'coverflow-carousel';
22
+ import { registerCoverflowCarouselElement, initCoverflowCarousels } from 'coverflow-carousel';
23
+ registerCoverflowCarouselElement();
29
24
  ```
30
25
  <br>
31
26
 
32
- **Usage**
33
-
27
+ # Usage
34
28
  ```javascript
35
- // After import 'coverflow-carousel', the element is registered.
29
+ // After registerCoverflowCarouselElement(), the element is registered.
36
30
  // Then just use <coverflow-carousel> in your HTML (see below).
37
31
  ```
38
32
 
@@ -52,7 +46,13 @@ import { initCoverflowCarousels } from 'coverflow-carousel';
52
46
 
53
47
  <sub>JS: subscribe to events + optional styles</sub>
54
48
  ```javascript
55
- import { initCoverflowCarousels, coverflowCarouselCssText } from 'coverflow-carousel';
49
+ import {
50
+ registerCoverflowCarouselElement,
51
+ initCoverflowCarousels,
52
+ coverflowCarouselCssText,
53
+ } from 'coverflow-carousel';
54
+
55
+ registerCoverflowCarouselElement();
56
56
 
57
57
  initCoverflowCarousels({
58
58
  selector: 'coverflow-carousel',
@@ -82,9 +82,11 @@ initCoverflowCarousels({
82
82
 
83
83
  <sub>JS: custom styles from `?raw` (CSS/SCSS)</sub>
84
84
  ```javascript
85
- import { initCoverflowCarousels } from 'coverflow-carousel';
85
+ import { registerCoverflowCarouselElement, initCoverflowCarousels } from 'coverflow-carousel';
86
86
  import MyCfcCss from './assets/cfc.custom.css?raw';
87
87
 
88
+ registerCoverflowCarouselElement();
89
+
88
90
  initCoverflowCarousels({
89
91
  stylesheet: MyCfcCss,
90
92
  });
@@ -92,7 +94,13 @@ initCoverflowCarousels({
92
94
 
93
95
  <sub>JS: default styles shipped with the package</sub>
94
96
  ```javascript
95
- import { initCoverflowCarousels, coverflowCarouselCssText } from 'coverflow-carousel';
97
+ import {
98
+ registerCoverflowCarouselElement,
99
+ initCoverflowCarousels,
100
+ coverflowCarouselCssText,
101
+ } from 'coverflow-carousel';
102
+
103
+ registerCoverflowCarouselElement();
96
104
 
97
105
  initCoverflowCarousels({
98
106
  stylesheet: coverflowCarouselCssText,
@@ -101,6 +109,10 @@ initCoverflowCarousels({
101
109
 
102
110
  <sub>JS: manual control (prev/next/goTo/refresh)</sub>
103
111
  ```javascript
112
+ import { registerCoverflowCarouselElement } from 'coverflow-carousel';
113
+
114
+ registerCoverflowCarouselElement();
115
+
104
116
  const el = document.querySelector('coverflow-carousel');
105
117
  // @ts-expect-error: methods exist on the custom element instance after import
106
118
  el?.prev();
@@ -113,8 +125,7 @@ el?.refresh();
113
125
  ```
114
126
  <br>
115
127
 
116
- **Options**
117
-
128
+ # Options
118
129
  | Option (attribute) | Type | Default | Description |
119
130
  |:--------------------:|:-----------------------:|:------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------|
120
131
  | `start-index` | `number` | `0` | Start index if `index` is not set. |
@@ -125,8 +136,7 @@ el?.refresh();
125
136
 
126
137
  <br>
127
138
 
128
- **Events**
129
-
139
+ # Events
130
140
  | Event | Detail | Description |
131
141
  |-------------------|------------------------------|--------------------------------------------------------------------------------------------------|
132
142
  | `coverflow-carousel:ready` | `{ index: number, length: number }` | Fired after the first `refresh()` (when slides are built and layout/a11y is applied). |
@@ -135,8 +145,7 @@ el?.refresh();
135
145
 
136
146
  <br>
137
147
 
138
- **API Methods**
139
-
148
+ # API Methods
140
149
  | Method | Description |
141
150
  |-------------------|--------------------------------------------------------------------------------------------------|
142
151
  | `initCoverflowCarousels({ selector?, onReady?, onChange?, onScratchComplete?, stylesheet? }): HTMLElement[]` | Finds elements by `selector` (default: `coverflow-carousel`), optionally subscribes to `ready/change/scratch-complete` events, and optionally applies styles to each element (`stylesheet?: CSSStyleSheet \| string \| null`). |
@@ -150,8 +159,7 @@ el?.refresh();
150
159
 
151
160
  <br>
152
161
 
153
- **Notes**
154
-
162
+ # Notes
155
163
  - Slides come from *light DOM*: all children are moved into cards inside the shadow DOM (nodes are moved — this is expected).
156
164
  - For the coverflow effect, it’s recommended to have **at least 3** slides.
157
165
  - While a transition is running, new transitions are ignored (anti-spam guard).
@@ -162,6 +170,5 @@ el?.refresh();
162
170
 
163
171
  <br>
164
172
 
165
- **License**
166
-
167
- coverflow-carousel is released under MIT license
173
+ # License
174
+ MIT
package/dist/dev.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.cjs.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=`:host {
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=`:host {
2
2
  display: block;
3
3
  width: 100%;
4
4
  height: 100%;
@@ -164,4 +164,4 @@
164
164
  white-space: nowrap;
165
165
  border: 0;
166
166
  }
167
- `,S=v;function f(a){return"adoptedStyleSheets"in a}function b(a){try{const t=new CSSStyleSheet;return t.replaceSync(a),t}catch{return null}}const I=b(v),_=v;function p(a){return a.split(",").map(t=>t.trim()).filter(Boolean)}function m(a,t){const e=a.trim();if(!e)return t;if(e.endsWith("ms")){const n=Number(e.slice(0,-2).trim());return Number.isFinite(n)?n:t}if(e.endsWith("s")){const n=Number(e.slice(0,-1).trim());return Number.isFinite(n)?n*1e3:t}const i=Number(e);return Number.isFinite(i)?i:t}function k(a,t){const e=p(a.transitionProperty),i=p(a.transitionDuration),n=p(a.transitionDelay);if(!e.length)return 0;const r=(A=>{const x=e.indexOf(A);if(x>=0)return x;const g=e.indexOf("all");return g>=0?g:-1})(t);if(r<0)return 0;const c=i[Math.min(r,i.length-1)]??"0s",o=n[Math.min(r,n.length-1)]??"0s",w=m(c,0),E=m(o,0);return Math.max(0,w+E)}function C(){return window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches??!1}function l(a,t,e){if(!a.hasAttribute(t))return e;const i=a.getAttribute(t);return i==null||i===""?!0:i!=="false"}function T(a,t,e){const i=a.getAttribute(t);if(i==null)return e;const n=Number(i.trim());return Number.isFinite(n)?Math.trunc(n):e}function y(a,t){const e=a.getAttribute(t);if(e==null)return null;const i=e.trim();return i||null}function d(a,t){return t<=0?0:(a%t+t)%t}function L(a,t,e){const i=t-a,n=e/2;return i>n?i-e:i<-n?i+e:i}const h=1;let D=0;class u extends HTMLElement{static defaultStylesheet=I;static observedAttributes=["start-index","index","show-dots","show-arrows","announce-changes"];shadow=this.attachShadow({mode:"open"});instanceId=`cfc-${++D}`;rootEl;trackEl;dotsEl;prevBtn;nextBtn;liveRegion;styleEl=null;cards=[];dots=[];currentIndex=0;isAnimating=!1;hasAppliedInitialLayout=!1;lastLayoutIndex=null;lastVisibleSet=new Set;pendingAnimToken=0;animFallbackTimerId=null;cleanup=[];reflectGuard=!1;connectedCallback(){this.render(),this.readAttributes({isInit:!0}),this.refresh()}disconnectedCallback(){this.destroy()}attributeChangedCallback(t,e,i){if(!this.isConnected||this.reflectGuard)return;this.readAttributes({isInit:!1});const n=y(this,"index");if(n!=null){const s=d(Number(n),this.cards.length);if(Number.isFinite(s)&&s!==this.currentIndex){this.goTo(s);return}}this.applyLayoutAndA11y({announce:!0,emitChange:!1})}next(){this.goTo(this.currentIndex+1)}prev(){this.goTo(this.currentIndex-1)}goTo(t){if(this.isAnimating)return;const e=d(t,this.cards.length);e!==this.currentIndex&&(this.isAnimating=!0,this.currentIndex=e,this.reflectIndexAttr(),this.applyLayoutAndA11y({announce:!0,emitChange:!0}),this.lockUntilTransitionEnd())}refresh(){this.rebuildCardsFromLightDom();const t=T(this,"start-index",0),e=y(this,"index"),i=e!=null?Number(e):t,n=Number.isFinite(i)?i:0;this.currentIndex=d(n,this.cards.length),this.reflectIndexAttr(),this.buildDots(),this.hasAppliedInitialLayout=!1,this.lastLayoutIndex=null,this.lastVisibleSet=new Set,this.applyLayoutAndA11y({announce:!0,emitChange:!1}),this.dispatchReady()}destroy(){this.animFallbackTimerId!==null&&(window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=null),this.cleanup.forEach(t=>{t()}),this.cleanup=[],this.isAnimating=!1}adoptStylesheet(t){this.applyStyles(t)}adoptStyles(t){this.applyStyles(t)}readAttributes(t){this.rootEl||this.render();const e=l(this,"show-arrows",!1),i=l(this,"show-dots",!1);this.prevBtn.style.display=e?"":"none",this.nextBtn.style.display=e?"":"none",this.dotsEl.style.display=i?"":"none";const n=this.getAttribute("aria-label");n&&this.rootEl.setAttribute("aria-label",n),t.isInit&&this.setAttribute("aria-roledescription","carousel")}render(){this.applyStyles(null),this.rootEl=document.createElement("div"),this.rootEl.className="cfc",this.trackEl=document.createElement("div"),this.trackEl.className="cfc__track",this.prevBtn=document.createElement("button"),this.prevBtn.type="button",this.prevBtn.className="cfc__arrow cfc__arrow--left",this.prevBtn.setAttribute("aria-label","Previous"),this.prevBtn.textContent="‹",this.nextBtn=document.createElement("button"),this.nextBtn.type="button",this.nextBtn.className="cfc__arrow cfc__arrow--right",this.nextBtn.setAttribute("aria-label","Next"),this.nextBtn.textContent="›",this.dotsEl=document.createElement("div"),this.dotsEl.className="cfc__dots",this.liveRegion=document.createElement("div"),this.liveRegion.className="cfc__sr",this.liveRegion.setAttribute("aria-live","polite"),this.liveRegion.setAttribute("aria-atomic","true"),this.rootEl.append(this.trackEl,this.prevBtn,this.nextBtn,this.dotsEl,this.liveRegion),this.shadow.innerHTML="",this.styleEl&&this.shadow.append(this.styleEl),this.shadow.append(this.rootEl),this.bindEvents()}bindEvents(){this.cleanup.forEach(n=>{n()}),this.cleanup=[];const t=()=>this.prev(),e=()=>this.next();this.prevBtn.addEventListener("click",t),this.nextBtn.addEventListener("click",e),this.cleanup.push(()=>this.prevBtn.removeEventListener("click",t)),this.cleanup.push(()=>this.nextBtn.removeEventListener("click",e));const i=n=>{if(!this.isAnimating||n.propertyName!=="transform"||!(n.target instanceof HTMLElement))return;const s=this.cards[this.currentIndex];s&&n.target===s&&this.unlockAnimation()};this.rootEl.addEventListener("transitionend",i),this.cleanup.push(()=>this.rootEl.removeEventListener("transitionend",i))}rebuildCardsFromLightDom(){const t=Array.from(this.trackEl.children).filter(r=>r instanceof HTMLElement&&r.classList.contains("cfc__card")),e=[];t.forEach(r=>{const c=r.firstElementChild;c instanceof HTMLElement&&e.push(c)});const i=Array.from(this.children).filter(r=>r instanceof HTMLElement),n=[...e,...i],s=[];for(let r=0;r<n.length;r++){const c=n[r],o=t[r]??document.createElement("div");t[r]||(o.className="cfc__card",this.trackEl.append(o)),o.firstElementChild!==c&&o.replaceChildren(c),s.push(o)}for(let r=n.length;r<t.length;r++)t[r]?.remove();this.cards=s,this.hasAppliedInitialLayout=!1,this.lastLayoutIndex=null,this.lastVisibleSet=new Set}getVisibleSet(){const t=this.cards.length,e=new Set;if(t<=0)return e;const i=Math.floor(t/2);if(h>=i){for(let n=0;n<t;n++)e.add(n);return e}for(let n=-h;n<=h;n++)e.add(d(this.currentIndex+n,t));return e}buildDots(){const t=Array.from(this.dotsEl.children).filter(s=>s instanceof HTMLElement&&s.classList.contains("cfc__dot"));if(!l(this,"show-dots",!1)){t.forEach(s=>{s.remove()}),this.dots=[];return}const i=[],n=this.cards.length;for(let s=0;s<n;s++){const r=t[s]??document.createElement("span");t[s]||this.dotsEl.append(r),r.className="cfc__dot",r.setAttribute("aria-hidden","true"),i.push(r)}for(let s=n;s<t.length;s++)t[s]?.remove();this.dots=i,this.updateDotsVisualState()}computeCardState(t){const e=this.cards.length,i=L(this.currentIndex,t,e),n=Math.abs(i);return{index:t,delta:i,abs:n,isVisible:n<=h,isActive:t===this.currentIndex}}applyCardState(t,e){t.setAttribute("aria-hidden",e.isVisible?"false":"true"),t.dataset.active=e.isActive?"true":"false",t.setAttribute("role","group"),t.setAttribute("aria-roledescription","slide"),t.setAttribute("aria-setsize",String(this.cards.length)),t.setAttribute("aria-posinset",String(e.index+1));const i=`${this.instanceId}-slide-${e.index}`;t.id=i;const n=String(e.delta),s=String(e.abs),r=t.dataset.cfcDelta,c=t.dataset.cfcAbs;r!==n&&(t.style.setProperty("--cfc-delta",n),t.dataset.cfcDelta=n),c!==s&&(t.style.setProperty("--cfc-abs",s),t.dataset.cfcAbs=s)}applyLayoutAndA11y(t){if(this.cards.length){if(this.hasAppliedInitialLayout){const e=this.getVisibleSet(),i=new Set;this.lastVisibleSet.forEach(n=>{i.add(n)}),e.forEach(n=>{i.add(n)}),this.lastLayoutIndex!=null&&i.add(this.lastLayoutIndex),i.add(this.currentIndex),i.forEach(n=>{const s=this.cards[n];if(!s)return;const r=this.computeCardState(n);this.applyCardState(s,r)}),this.lastLayoutIndex=this.currentIndex,this.lastVisibleSet=e}else{for(let e=0;e<this.cards.length;e++){const i=this.cards[e];if(!i)continue;const n=this.computeCardState(e);this.applyCardState(i,n)}this.hasAppliedInitialLayout=!0,this.lastLayoutIndex=this.currentIndex,this.lastVisibleSet=this.getVisibleSet()}this.updateDotsVisualState(),t.announce&&l(this,"announce-changes",!0)&&this.announce(`Slide ${this.currentIndex+1} of ${this.cards.length}`),t.emitChange&&this.emitChange()}}updateDotsVisualState(){if(this.dots.length)for(let t=0;t<this.dots.length;t++){const e=this.dots[t],i=t===this.currentIndex;e.dataset.active=i?"true":"false"}}lockUntilTransitionEnd(){this.pendingAnimToken++;const t=this.pendingAnimToken;if(C()){this.unlockAnimation();return}const e=this.cards[this.currentIndex];if(!e){this.unlockAnimation();return}const i=getComputedStyle(e),n=k(i,"transform");if(n<=0){this.unlockAnimation();return}const s=m(i.getPropertyValue("--cfc-transition-ms").trim(),400),c=Math.max(n,s)+60;this.animFallbackTimerId!==null&&window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=window.setTimeout(()=>{this.pendingAnimToken===t&&this.unlockAnimation()},c)}unlockAnimation(){this.isAnimating=!1,this.animFallbackTimerId!==null&&(window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=null)}emitChange(){this.dispatchEvent(new CustomEvent("coverflow-carousel:change",{detail:{index:this.currentIndex,length:this.cards.length}}))}dispatchReady(){this.dispatchEvent(new CustomEvent("coverflow-carousel:ready",{detail:{index:this.currentIndex,length:this.cards.length}}))}announce(t){this.liveRegion.textContent="",queueMicrotask(()=>{this.liveRegion.textContent=t})}reflectIndexAttr(){this.reflectGuard=!0;try{this.setAttribute("index",String(this.currentIndex))}finally{this.reflectGuard=!1}}applyStyles(t){if(typeof t=="string"){if(f(this.shadow)){const e=b(t);if(e){this.shadow.adoptedStyleSheets=[e],this.styleEl=null;return}}this.styleEl||(this.styleEl=document.createElement("style")),this.styleEl.textContent=t;return}if(t&&f(this.shadow)){this.shadow.adoptedStyleSheets=[t],this.styleEl=null;return}if(u.defaultStylesheet&&f(this.shadow)){this.shadow.adoptedStyleSheets=[u.defaultStylesheet],this.styleEl=null;return}this.styleEl||(this.styleEl=document.createElement("style")),this.styleEl.textContent=_}}function N(a={}){const{selector:t="coverflow-carousel",onReady:e,onChange:i,stylesheet:n}=a,s=Array.from(document.querySelectorAll(t));return(e||i)&&s.forEach(r=>{e&&r.addEventListener("coverflow-carousel:ready",c=>{e(r,c.detail)}),i&&r.addEventListener("coverflow-carousel:change",c=>{i(r,c.detail)})}),n&&s.forEach(r=>{const c=r;typeof n=="string"?c.adoptStyles(n):c.adoptStylesheet(n)}),s}customElements.get("coverflow-carousel")||customElements.define("coverflow-carousel",u);exports.coverflowCarouselCssText=S;exports.initCoverflowCarousels=N;
167
+ `,A=b;function v(a){return"adoptedStyleSheets"in a}function S(a){try{const t=new CSSStyleSheet;return t.replaceSync(a),t}catch{return null}}const I=S(b),C=b;function x(a){return a.split(",").map(t=>t.trim()).filter(Boolean)}function g(a,t){const e=a.trim();if(!e)return t;if(e.endsWith("ms")){const n=Number(e.slice(0,-2).trim());return Number.isFinite(n)?n:t}if(e.endsWith("s")){const n=Number(e.slice(0,-1).trim());return Number.isFinite(n)?n*1e3:t}const i=Number(e);return Number.isFinite(i)?i:t}function _(a,t){const e=x(a.transitionProperty),i=x(a.transitionDuration),n=x(a.transitionDelay);if(!e.length)return 0;const r=(m=>{const d=e.indexOf(m);if(d>=0)return d;const E=e.indexOf("all");return E>=0?E:-1})(t);if(r<0)return 0;const c=i[Math.min(r,i.length-1)]??"0s",o=n[Math.min(r,n.length-1)]??"0s",l=g(c,0),y=g(o,0);return Math.max(0,l+y)}function k(){return window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches??!1}function u(a,t,e){if(!a.hasAttribute(t))return e;const i=a.getAttribute(t);return i==null||i===""?!0:i!=="false"}function L(a,t,e){const i=a.getAttribute(t);if(i==null)return e;const n=Number(i.trim());return Number.isFinite(n)?Math.trunc(n):e}function w(a,t){const e=a.getAttribute(t);if(e==null)return null;const i=e.trim();return i||null}function f(a,t){return t<=0?0:(a%t+t)%t}function T(a,t,e){const i=t-a,n=e/2;return i>n?i-e:i<-n?i+e:i}const p=1;let N=0;class h extends HTMLElement{static defaultStylesheet=I;static observedAttributes=["start-index","index","show-dots","show-arrows","announce-changes"];shadow=this.attachShadow({mode:"open"});instanceId=`cfc-${++N}`;rootEl;trackEl;dotsEl;prevBtn;nextBtn;liveRegion;styleEl=null;cards=[];dots=[];currentIndex=0;isAnimating=!1;hasAppliedInitialLayout=!1;lastLayoutIndex=null;lastVisibleSet=new Set;pendingAnimToken=0;animFallbackTimerId=null;cleanup=[];reflectGuard=!1;connectedCallback(){this.render(),this.readAttributes({isInit:!0}),this.refresh()}disconnectedCallback(){this.destroy()}attributeChangedCallback(t,e,i){if(!this.isConnected||this.reflectGuard)return;this.readAttributes({isInit:!1});const n=w(this,"index");if(n!=null){const s=f(Number(n),this.cards.length);if(Number.isFinite(s)&&s!==this.currentIndex){this.goTo(s);return}}this.applyLayoutAndA11y({announce:!0,emitChange:!1})}next(){this.goTo(this.currentIndex+1)}prev(){this.goTo(this.currentIndex-1)}goTo(t){if(this.isAnimating)return;const e=f(t,this.cards.length);e!==this.currentIndex&&(this.isAnimating=!0,this.currentIndex=e,this.reflectIndexAttr(),this.applyLayoutAndA11y({announce:!0,emitChange:!0}),this.lockUntilTransitionEnd())}refresh(){this.rebuildCardsFromLightDom();const t=L(this,"start-index",0),e=w(this,"index"),i=e!=null?Number(e):t,n=Number.isFinite(i)?i:0;this.currentIndex=f(n,this.cards.length),this.reflectIndexAttr(),this.buildDots(),this.hasAppliedInitialLayout=!1,this.lastLayoutIndex=null,this.lastVisibleSet=new Set,this.applyLayoutAndA11y({announce:!0,emitChange:!1}),this.dispatchReady()}destroy(){this.animFallbackTimerId!==null&&(window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=null),this.cleanup.forEach(t=>{t()}),this.cleanup=[],this.isAnimating=!1}adoptStylesheet(t){this.applyStyles(t)}adoptStyles(t){this.applyStyles(t)}readAttributes(t){this.rootEl||this.render();const e=u(this,"show-arrows",!1),i=u(this,"show-dots",!1);this.prevBtn.style.display=e?"":"none",this.nextBtn.style.display=e?"":"none",this.dotsEl.style.display=i?"":"none";const n=this.getAttribute("aria-label");n&&this.rootEl.setAttribute("aria-label",n),t.isInit&&this.setAttribute("aria-roledescription","carousel")}render(){this.applyStyles(null),this.rootEl=document.createElement("div"),this.rootEl.className="cfc",this.trackEl=document.createElement("div"),this.trackEl.className="cfc__track",this.prevBtn=document.createElement("button"),this.prevBtn.type="button",this.prevBtn.className="cfc__arrow cfc__arrow--left",this.prevBtn.setAttribute("aria-label","Previous"),this.prevBtn.textContent="‹",this.nextBtn=document.createElement("button"),this.nextBtn.type="button",this.nextBtn.className="cfc__arrow cfc__arrow--right",this.nextBtn.setAttribute("aria-label","Next"),this.nextBtn.textContent="›",this.dotsEl=document.createElement("div"),this.dotsEl.className="cfc__dots",this.liveRegion=document.createElement("div"),this.liveRegion.className="cfc__sr",this.liveRegion.setAttribute("aria-live","polite"),this.liveRegion.setAttribute("aria-atomic","true"),this.rootEl.append(this.trackEl,this.prevBtn,this.nextBtn,this.dotsEl,this.liveRegion),this.shadow.innerHTML="",this.styleEl&&this.shadow.append(this.styleEl),this.shadow.append(this.rootEl),this.bindEvents()}bindEvents(){this.cleanup.forEach(s=>{s()}),this.cleanup=[];const t=()=>this.prev(),e=()=>this.next();this.prevBtn.addEventListener("click",t),this.nextBtn.addEventListener("click",e),this.cleanup.push(()=>this.prevBtn.removeEventListener("click",t)),this.cleanup.push(()=>this.nextBtn.removeEventListener("click",e));const i=s=>{if(!this.isAnimating||s.propertyName!=="transform"||!(s.target instanceof HTMLElement))return;const r=this.cards[this.currentIndex];r&&s.target===r&&this.unlockAnimation()};this.rootEl.addEventListener("transitionend",i),this.cleanup.push(()=>this.rootEl.removeEventListener("transitionend",i));const n=s=>{const r=s.target;if(!(r instanceof HTMLElement)||r.tagName!=="SCRATCH-REVEAL")return;const c=s.composedPath?.();if(!c?.length)return;const o=c.find(d=>d instanceof HTMLElement&&d.classList.contains("cfc__card")&&d.dataset.cfcIndex!=null);if(!o)return;const l=Number(o.dataset.cfcIndex);if(!Number.isFinite(l))return;const m=s.detail?.percent??100;this.dispatchEvent(new CustomEvent("coverflow-carousel:scratch-complete",{detail:{index:l,length:this.cards.length,percent:m},bubbles:!0,composed:!0}))};this.rootEl.addEventListener("complete",n),this.cleanup.push(()=>this.rootEl.removeEventListener("complete",n))}rebuildCardsFromLightDom(){const t=Array.from(this.trackEl.children).filter(r=>r instanceof HTMLElement&&r.classList.contains("cfc__card")),e=[];t.forEach(r=>{const c=r.firstElementChild;c instanceof HTMLElement&&e.push(c)});const i=Array.from(this.children).filter(r=>r instanceof HTMLElement),n=[...e,...i],s=[];for(let r=0;r<n.length;r++){const c=n[r],o=t[r]??document.createElement("div");t[r]||(o.className="cfc__card",this.trackEl.append(o)),o.firstElementChild!==c&&o.replaceChildren(c),s.push(o)}for(let r=n.length;r<t.length;r++)t[r]?.remove();this.cards=s,this.hasAppliedInitialLayout=!1,this.lastLayoutIndex=null,this.lastVisibleSet=new Set}getVisibleSet(){const t=this.cards.length,e=new Set;if(t<=0)return e;const i=Math.floor(t/2);if(p>=i){for(let n=0;n<t;n++)e.add(n);return e}for(let n=-p;n<=p;n++)e.add(f(this.currentIndex+n,t));return e}buildDots(){const t=Array.from(this.dotsEl.children).filter(s=>s instanceof HTMLElement&&s.classList.contains("cfc__dot"));if(!u(this,"show-dots",!1)){t.forEach(s=>{s.remove()}),this.dots=[];return}const i=[],n=this.cards.length;for(let s=0;s<n;s++){const r=t[s]??document.createElement("span");t[s]||this.dotsEl.append(r),r.className="cfc__dot",r.setAttribute("aria-hidden","true"),i.push(r)}for(let s=n;s<t.length;s++)t[s]?.remove();this.dots=i,this.updateDotsVisualState()}computeCardState(t){const e=this.cards.length,i=T(this.currentIndex,t,e),n=Math.abs(i);return{index:t,delta:i,abs:n,isVisible:n<=p,isActive:t===this.currentIndex}}applyCardState(t,e){t.setAttribute("aria-hidden",e.isVisible?"false":"true"),t.dataset.active=e.isActive?"true":"false",t.dataset.cfcIndex=String(e.index),t.setAttribute("role","group"),t.setAttribute("aria-roledescription","slide"),t.setAttribute("aria-setsize",String(this.cards.length)),t.setAttribute("aria-posinset",String(e.index+1));const i=`${this.instanceId}-slide-${e.index}`;t.id=i;const n=String(e.delta),s=String(e.abs),r=t.dataset.cfcDelta,c=t.dataset.cfcAbs;r!==n&&(t.style.setProperty("--cfc-delta",n),t.dataset.cfcDelta=n),c!==s&&(t.style.setProperty("--cfc-abs",s),t.dataset.cfcAbs=s)}applyLayoutAndA11y(t){if(this.cards.length){if(this.hasAppliedInitialLayout){const e=this.getVisibleSet(),i=new Set;this.lastVisibleSet.forEach(n=>{i.add(n)}),e.forEach(n=>{i.add(n)}),this.lastLayoutIndex!=null&&i.add(this.lastLayoutIndex),i.add(this.currentIndex),i.forEach(n=>{const s=this.cards[n];if(!s)return;const r=this.computeCardState(n);this.applyCardState(s,r)}),this.lastLayoutIndex=this.currentIndex,this.lastVisibleSet=e}else{for(let e=0;e<this.cards.length;e++){const i=this.cards[e];if(!i)continue;const n=this.computeCardState(e);this.applyCardState(i,n)}this.hasAppliedInitialLayout=!0,this.lastLayoutIndex=this.currentIndex,this.lastVisibleSet=this.getVisibleSet()}this.updateDotsVisualState(),t.announce&&u(this,"announce-changes",!0)&&this.announce(`Slide ${this.currentIndex+1} of ${this.cards.length}`),t.emitChange&&this.emitChange()}}updateDotsVisualState(){if(this.dots.length)for(let t=0;t<this.dots.length;t++){const e=this.dots[t],i=t===this.currentIndex;e.dataset.active=i?"true":"false"}}lockUntilTransitionEnd(){this.pendingAnimToken++;const t=this.pendingAnimToken;if(k()){this.unlockAnimation();return}const e=this.cards[this.currentIndex];if(!e){this.unlockAnimation();return}const i=getComputedStyle(e),n=_(i,"transform");if(n<=0){this.unlockAnimation();return}const s=g(i.getPropertyValue("--cfc-transition-ms").trim(),400),c=Math.max(n,s)+60;this.animFallbackTimerId!==null&&window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=window.setTimeout(()=>{this.pendingAnimToken===t&&this.unlockAnimation()},c)}unlockAnimation(){this.isAnimating=!1,this.animFallbackTimerId!==null&&(window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=null)}emitChange(){this.dispatchEvent(new CustomEvent("coverflow-carousel:change",{detail:{index:this.currentIndex,length:this.cards.length}}))}dispatchReady(){this.dispatchEvent(new CustomEvent("coverflow-carousel:ready",{detail:{index:this.currentIndex,length:this.cards.length}}))}announce(t){this.liveRegion.textContent="",queueMicrotask(()=>{this.liveRegion.textContent=t})}reflectIndexAttr(){this.reflectGuard=!0;try{this.setAttribute("index",String(this.currentIndex))}finally{this.reflectGuard=!1}}applyStyles(t){if(typeof t=="string"){if(v(this.shadow)){const e=S(t);if(e){this.shadow.adoptedStyleSheets=[e],this.styleEl=null;return}}this.styleEl||(this.styleEl=document.createElement("style")),this.styleEl.textContent=t;return}if(t&&v(this.shadow)){this.shadow.adoptedStyleSheets=[t],this.styleEl=null;return}if(h.defaultStylesheet&&v(this.shadow)){this.shadow.adoptedStyleSheets=[h.defaultStylesheet],this.styleEl=null;return}this.styleEl||(this.styleEl=document.createElement("style")),this.styleEl.textContent=C}}function F(a={}){const{selector:t="coverflow-carousel",onReady:e,onChange:i,onScratchComplete:n,stylesheet:s}=a,r=Array.from(document.querySelectorAll(t));return(e||i||n)&&r.forEach(c=>{e&&c.addEventListener("coverflow-carousel:ready",o=>{e(c,o.detail)}),i&&c.addEventListener("coverflow-carousel:change",o=>{i(c,o.detail)}),n&&c.addEventListener("coverflow-carousel:scratch-complete",o=>{n(c,o.detail)})}),s&&r.forEach(c=>{const o=c;typeof s=="string"?o.adoptStyles(s):o.adoptStylesheet(s)}),r}function D(a="coverflow-carousel"){typeof window>"u"||!("customElements"in window)||customElements.get(a)||customElements.define(a,h)}exports.CoverflowCarouselElement=h;exports.coverflowCarouselCssText=A;exports.initCoverflowCarousels=F;exports.registerCoverflowCarouselElement=D;
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { CoverflowCarouselElement } from './element/CoverflowCarouselElement';
2
2
  import { initCoverflowCarousels } from './init';
3
3
  import { coverflowCarouselCssText } from './styles';
4
+ export declare function registerCoverflowCarouselElement(tagName?: string): void;
4
5
  declare global {
5
6
  interface HTMLElementTagNameMap {
6
7
  'coverflow-carousel': CoverflowCarouselElement;
7
8
  }
8
9
  }
9
- export { coverflowCarouselCssText, initCoverflowCarousels };
10
+ export { coverflowCarouselCssText, initCoverflowCarousels, CoverflowCarouselElement };
package/dist/index.es.js CHANGED
@@ -1,4 +1,4 @@
1
- const v = `:host {
1
+ const b = `:host {
2
2
  display: block;
3
3
  width: 100%;
4
4
  height: 100%;
@@ -164,11 +164,11 @@ const v = `:host {
164
164
  white-space: nowrap;
165
165
  border: 0;
166
166
  }
167
- `, D = v;
168
- function f(a) {
167
+ `, N = b;
168
+ function v(a) {
169
169
  return "adoptedStyleSheets" in a;
170
170
  }
171
- function b(a) {
171
+ function S(a) {
172
172
  try {
173
173
  const t = new CSSStyleSheet();
174
174
  return t.replaceSync(a), t;
@@ -176,11 +176,11 @@ function b(a) {
176
176
  return null;
177
177
  }
178
178
  }
179
- const S = b(v), I = v;
180
- function p(a) {
179
+ const A = S(b), I = b;
180
+ function x(a) {
181
181
  return a.split(",").map((t) => t.trim()).filter(Boolean);
182
182
  }
183
- function m(a, t) {
183
+ function g(a, t) {
184
184
  const e = a.trim();
185
185
  if (!e) return t;
186
186
  if (e.endsWith("ms")) {
@@ -195,22 +195,22 @@ function m(a, t) {
195
195
  return Number.isFinite(i) ? i : t;
196
196
  }
197
197
  function _(a, t) {
198
- const e = p(a.transitionProperty), i = p(a.transitionDuration), n = p(a.transitionDelay);
198
+ const e = x(a.transitionProperty), i = x(a.transitionDuration), n = x(a.transitionDelay);
199
199
  if (!e.length) return 0;
200
- const r = ((A) => {
201
- const x = e.indexOf(A);
202
- if (x >= 0) return x;
203
- const g = e.indexOf("all");
204
- return g >= 0 ? g : -1;
200
+ const r = ((m) => {
201
+ const d = e.indexOf(m);
202
+ if (d >= 0) return d;
203
+ const E = e.indexOf("all");
204
+ return E >= 0 ? E : -1;
205
205
  })(t);
206
206
  if (r < 0) return 0;
207
- const c = i[Math.min(r, i.length - 1)] ?? "0s", o = n[Math.min(r, n.length - 1)] ?? "0s", w = m(c, 0), E = m(o, 0);
208
- return Math.max(0, w + E);
207
+ const c = i[Math.min(r, i.length - 1)] ?? "0s", o = n[Math.min(r, n.length - 1)] ?? "0s", l = g(c, 0), y = g(o, 0);
208
+ return Math.max(0, l + y);
209
209
  }
210
210
  function k() {
211
211
  return window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches ?? !1;
212
212
  }
213
- function l(a, t, e) {
213
+ function h(a, t, e) {
214
214
  if (!a.hasAttribute(t)) return e;
215
215
  const i = a.getAttribute(t);
216
216
  return i == null || i === "" ? !0 : i !== "false";
@@ -221,23 +221,23 @@ function C(a, t, e) {
221
221
  const n = Number(i.trim());
222
222
  return Number.isFinite(n) ? Math.trunc(n) : e;
223
223
  }
224
- function y(a, t) {
224
+ function w(a, t) {
225
225
  const e = a.getAttribute(t);
226
226
  if (e == null) return null;
227
227
  const i = e.trim();
228
228
  return i || null;
229
229
  }
230
- function d(a, t) {
230
+ function u(a, t) {
231
231
  return t <= 0 ? 0 : (a % t + t) % t;
232
232
  }
233
- function T(a, t, e) {
233
+ function L(a, t, e) {
234
234
  const i = t - a, n = e / 2;
235
235
  return i > n ? i - e : i < -n ? i + e : i;
236
236
  }
237
- const h = 1;
238
- let L = 0;
239
- class u extends HTMLElement {
240
- static defaultStylesheet = S;
237
+ const f = 1;
238
+ let T = 0;
239
+ class p extends HTMLElement {
240
+ static defaultStylesheet = A;
241
241
  static observedAttributes = [
242
242
  "start-index",
243
243
  "index",
@@ -246,7 +246,7 @@ class u extends HTMLElement {
246
246
  "announce-changes"
247
247
  ];
248
248
  shadow = this.attachShadow({ mode: "open" });
249
- instanceId = `cfc-${++L}`;
249
+ instanceId = `cfc-${++T}`;
250
250
  rootEl;
251
251
  trackEl;
252
252
  dotsEl;
@@ -274,9 +274,9 @@ class u extends HTMLElement {
274
274
  attributeChangedCallback(t, e, i) {
275
275
  if (!this.isConnected || this.reflectGuard) return;
276
276
  this.readAttributes({ isInit: !1 });
277
- const n = y(this, "index");
277
+ const n = w(this, "index");
278
278
  if (n != null) {
279
- const s = d(Number(n), this.cards.length);
279
+ const s = u(Number(n), this.cards.length);
280
280
  if (Number.isFinite(s) && s !== this.currentIndex) {
281
281
  this.goTo(s);
282
282
  return;
@@ -292,13 +292,13 @@ class u extends HTMLElement {
292
292
  }
293
293
  goTo(t) {
294
294
  if (this.isAnimating) return;
295
- const e = d(t, this.cards.length);
295
+ const e = u(t, this.cards.length);
296
296
  e !== this.currentIndex && (this.isAnimating = !0, this.currentIndex = e, this.reflectIndexAttr(), this.applyLayoutAndA11y({ announce: !0, emitChange: !0 }), this.lockUntilTransitionEnd());
297
297
  }
298
298
  refresh() {
299
299
  this.rebuildCardsFromLightDom();
300
- const t = C(this, "start-index", 0), e = y(this, "index"), i = e != null ? Number(e) : t, n = Number.isFinite(i) ? i : 0;
301
- this.currentIndex = d(n, this.cards.length), this.reflectIndexAttr(), this.buildDots(), this.hasAppliedInitialLayout = !1, this.lastLayoutIndex = null, this.lastVisibleSet = /* @__PURE__ */ new Set(), this.applyLayoutAndA11y({ announce: !0, emitChange: !1 }), this.dispatchReady();
300
+ const t = C(this, "start-index", 0), e = w(this, "index"), i = e != null ? Number(e) : t, n = Number.isFinite(i) ? i : 0;
301
+ this.currentIndex = u(n, this.cards.length), this.reflectIndexAttr(), this.buildDots(), this.hasAppliedInitialLayout = !1, this.lastLayoutIndex = null, this.lastVisibleSet = /* @__PURE__ */ new Set(), this.applyLayoutAndA11y({ announce: !0, emitChange: !1 }), this.dispatchReady();
302
302
  }
303
303
  destroy() {
304
304
  this.animFallbackTimerId !== null && (window.clearTimeout(this.animFallbackTimerId), this.animFallbackTimerId = null), this.cleanup.forEach((t) => {
@@ -313,7 +313,7 @@ class u extends HTMLElement {
313
313
  }
314
314
  readAttributes(t) {
315
315
  this.rootEl || this.render();
316
- const e = l(this, "show-arrows", !1), i = l(this, "show-dots", !1);
316
+ const e = h(this, "show-arrows", !1), i = h(this, "show-dots", !1);
317
317
  this.prevBtn.style.display = e ? "" : "none", this.nextBtn.style.display = e ? "" : "none", this.dotsEl.style.display = i ? "" : "none";
318
318
  const n = this.getAttribute("aria-label");
319
319
  n && this.rootEl.setAttribute("aria-label", n), t.isInit && this.setAttribute("aria-roledescription", "carousel");
@@ -322,17 +322,40 @@ class u extends HTMLElement {
322
322
  this.applyStyles(null), this.rootEl = document.createElement("div"), this.rootEl.className = "cfc", this.trackEl = document.createElement("div"), this.trackEl.className = "cfc__track", this.prevBtn = document.createElement("button"), this.prevBtn.type = "button", this.prevBtn.className = "cfc__arrow cfc__arrow--left", this.prevBtn.setAttribute("aria-label", "Previous"), this.prevBtn.textContent = "‹", this.nextBtn = document.createElement("button"), this.nextBtn.type = "button", this.nextBtn.className = "cfc__arrow cfc__arrow--right", this.nextBtn.setAttribute("aria-label", "Next"), this.nextBtn.textContent = "›", this.dotsEl = document.createElement("div"), this.dotsEl.className = "cfc__dots", this.liveRegion = document.createElement("div"), this.liveRegion.className = "cfc__sr", this.liveRegion.setAttribute("aria-live", "polite"), this.liveRegion.setAttribute("aria-atomic", "true"), this.rootEl.append(this.trackEl, this.prevBtn, this.nextBtn, this.dotsEl, this.liveRegion), this.shadow.innerHTML = "", this.styleEl && this.shadow.append(this.styleEl), this.shadow.append(this.rootEl), this.bindEvents();
323
323
  }
324
324
  bindEvents() {
325
- this.cleanup.forEach((n) => {
326
- n();
325
+ this.cleanup.forEach((s) => {
326
+ s();
327
327
  }), this.cleanup = [];
328
328
  const t = () => this.prev(), e = () => this.next();
329
329
  this.prevBtn.addEventListener("click", t), this.nextBtn.addEventListener("click", e), this.cleanup.push(() => this.prevBtn.removeEventListener("click", t)), this.cleanup.push(() => this.nextBtn.removeEventListener("click", e));
330
- const i = (n) => {
331
- if (!this.isAnimating || n.propertyName !== "transform" || !(n.target instanceof HTMLElement)) return;
332
- const s = this.cards[this.currentIndex];
333
- s && n.target === s && this.unlockAnimation();
330
+ const i = (s) => {
331
+ if (!this.isAnimating || s.propertyName !== "transform" || !(s.target instanceof HTMLElement)) return;
332
+ const r = this.cards[this.currentIndex];
333
+ r && s.target === r && this.unlockAnimation();
334
334
  };
335
335
  this.rootEl.addEventListener("transitionend", i), this.cleanup.push(() => this.rootEl.removeEventListener("transitionend", i));
336
+ const n = (s) => {
337
+ const r = s.target;
338
+ if (!(r instanceof HTMLElement) || r.tagName !== "SCRATCH-REVEAL") return;
339
+ const c = s.composedPath?.();
340
+ if (!c?.length) return;
341
+ const o = c.find(
342
+ (d) => d instanceof HTMLElement && d.classList.contains("cfc__card") && d.dataset.cfcIndex != null
343
+ );
344
+ if (!o) return;
345
+ const l = Number(o.dataset.cfcIndex);
346
+ if (!Number.isFinite(l)) return;
347
+ const m = s.detail?.percent ?? 100;
348
+ this.dispatchEvent(
349
+ new CustomEvent("coverflow-carousel:scratch-complete", {
350
+ detail: { index: l, length: this.cards.length, percent: m },
351
+ bubbles: !0,
352
+ composed: !0
353
+ })
354
+ );
355
+ };
356
+ this.rootEl.addEventListener("complete", n), this.cleanup.push(
357
+ () => this.rootEl.removeEventListener("complete", n)
358
+ );
336
359
  }
337
360
  rebuildCardsFromLightDom() {
338
361
  const t = Array.from(this.trackEl.children).filter(
@@ -357,19 +380,19 @@ class u extends HTMLElement {
357
380
  const t = this.cards.length, e = /* @__PURE__ */ new Set();
358
381
  if (t <= 0) return e;
359
382
  const i = Math.floor(t / 2);
360
- if (h >= i) {
383
+ if (f >= i) {
361
384
  for (let n = 0; n < t; n++) e.add(n);
362
385
  return e;
363
386
  }
364
- for (let n = -h; n <= h; n++)
365
- e.add(d(this.currentIndex + n, t));
387
+ for (let n = -f; n <= f; n++)
388
+ e.add(u(this.currentIndex + n, t));
366
389
  return e;
367
390
  }
368
391
  buildDots() {
369
392
  const t = Array.from(this.dotsEl.children).filter(
370
393
  (s) => s instanceof HTMLElement && s.classList.contains("cfc__dot")
371
394
  );
372
- if (!l(this, "show-dots", !1)) {
395
+ if (!h(this, "show-dots", !1)) {
373
396
  t.forEach((s) => {
374
397
  s.remove();
375
398
  }), this.dots = [];
@@ -385,17 +408,17 @@ class u extends HTMLElement {
385
408
  this.dots = i, this.updateDotsVisualState();
386
409
  }
387
410
  computeCardState(t) {
388
- const e = this.cards.length, i = T(this.currentIndex, t, e), n = Math.abs(i);
411
+ const e = this.cards.length, i = L(this.currentIndex, t, e), n = Math.abs(i);
389
412
  return {
390
413
  index: t,
391
414
  delta: i,
392
415
  abs: n,
393
- isVisible: n <= h,
416
+ isVisible: n <= f,
394
417
  isActive: t === this.currentIndex
395
418
  };
396
419
  }
397
420
  applyCardState(t, e) {
398
- t.setAttribute("aria-hidden", e.isVisible ? "false" : "true"), t.dataset.active = e.isActive ? "true" : "false", t.setAttribute("role", "group"), t.setAttribute("aria-roledescription", "slide"), t.setAttribute("aria-setsize", String(this.cards.length)), t.setAttribute("aria-posinset", String(e.index + 1));
421
+ t.setAttribute("aria-hidden", e.isVisible ? "false" : "true"), t.dataset.active = e.isActive ? "true" : "false", t.dataset.cfcIndex = String(e.index), t.setAttribute("role", "group"), t.setAttribute("aria-roledescription", "slide"), t.setAttribute("aria-setsize", String(this.cards.length)), t.setAttribute("aria-posinset", String(e.index + 1));
399
422
  const i = `${this.instanceId}-slide-${e.index}`;
400
423
  t.id = i;
401
424
  const n = String(e.delta), s = String(e.abs), r = t.dataset.cfcDelta, c = t.dataset.cfcAbs;
@@ -424,7 +447,7 @@ class u extends HTMLElement {
424
447
  }
425
448
  this.hasAppliedInitialLayout = !0, this.lastLayoutIndex = this.currentIndex, this.lastVisibleSet = this.getVisibleSet();
426
449
  }
427
- this.updateDotsVisualState(), t.announce && l(this, "announce-changes", !0) && this.announce(`Slide ${this.currentIndex + 1} of ${this.cards.length}`), t.emitChange && this.emitChange();
450
+ this.updateDotsVisualState(), t.announce && h(this, "announce-changes", !0) && this.announce(`Slide ${this.currentIndex + 1} of ${this.cards.length}`), t.emitChange && this.emitChange();
428
451
  }
429
452
  }
430
453
  updateDotsVisualState() {
@@ -451,7 +474,7 @@ class u extends HTMLElement {
451
474
  this.unlockAnimation();
452
475
  return;
453
476
  }
454
- const s = m(i.getPropertyValue("--cfc-transition-ms").trim(), 400), c = Math.max(n, s) + 60;
477
+ const s = g(i.getPropertyValue("--cfc-transition-ms").trim(), 400), c = Math.max(n, s) + 60;
455
478
  this.animFallbackTimerId !== null && window.clearTimeout(this.animFallbackTimerId), this.animFallbackTimerId = window.setTimeout(() => {
456
479
  this.pendingAnimToken === t && this.unlockAnimation();
457
480
  }, c);
@@ -488,8 +511,8 @@ class u extends HTMLElement {
488
511
  }
489
512
  applyStyles(t) {
490
513
  if (typeof t == "string") {
491
- if (f(this.shadow)) {
492
- const e = b(t);
514
+ if (v(this.shadow)) {
515
+ const e = S(t);
493
516
  if (e) {
494
517
  this.shadow.adoptedStyleSheets = [e], this.styleEl = null;
495
518
  return;
@@ -498,32 +521,44 @@ class u extends HTMLElement {
498
521
  this.styleEl || (this.styleEl = document.createElement("style")), this.styleEl.textContent = t;
499
522
  return;
500
523
  }
501
- if (t && f(this.shadow)) {
524
+ if (t && v(this.shadow)) {
502
525
  this.shadow.adoptedStyleSheets = [t], this.styleEl = null;
503
526
  return;
504
527
  }
505
- if (u.defaultStylesheet && f(this.shadow)) {
506
- this.shadow.adoptedStyleSheets = [u.defaultStylesheet], this.styleEl = null;
528
+ if (p.defaultStylesheet && v(this.shadow)) {
529
+ this.shadow.adoptedStyleSheets = [p.defaultStylesheet], this.styleEl = null;
507
530
  return;
508
531
  }
509
532
  this.styleEl || (this.styleEl = document.createElement("style")), this.styleEl.textContent = I;
510
533
  }
511
534
  }
512
- function N(a = {}) {
513
- const { selector: t = "coverflow-carousel", onReady: e, onChange: i, stylesheet: n } = a, s = Array.from(document.querySelectorAll(t));
514
- return (e || i) && s.forEach((r) => {
515
- e && r.addEventListener("coverflow-carousel:ready", (c) => {
516
- e(r, c.detail);
517
- }), i && r.addEventListener("coverflow-carousel:change", (c) => {
518
- i(r, c.detail);
535
+ function F(a = {}) {
536
+ const {
537
+ selector: t = "coverflow-carousel",
538
+ onReady: e,
539
+ onChange: i,
540
+ onScratchComplete: n,
541
+ stylesheet: s
542
+ } = a, r = Array.from(document.querySelectorAll(t));
543
+ return (e || i || n) && r.forEach((c) => {
544
+ e && c.addEventListener("coverflow-carousel:ready", (o) => {
545
+ e(c, o.detail);
546
+ }), i && c.addEventListener("coverflow-carousel:change", (o) => {
547
+ i(c, o.detail);
548
+ }), n && c.addEventListener("coverflow-carousel:scratch-complete", (o) => {
549
+ n(c, o.detail);
519
550
  });
520
- }), n && s.forEach((r) => {
521
- const c = r;
522
- typeof n == "string" ? c.adoptStyles(n) : c.adoptStylesheet(n);
523
- }), s;
551
+ }), s && r.forEach((c) => {
552
+ const o = c;
553
+ typeof s == "string" ? o.adoptStyles(s) : o.adoptStylesheet(s);
554
+ }), r;
555
+ }
556
+ function D(a = "coverflow-carousel") {
557
+ typeof window > "u" || !("customElements" in window) || customElements.get(a) || customElements.define(a, p);
524
558
  }
525
- customElements.get("coverflow-carousel") || customElements.define("coverflow-carousel", u);
526
559
  export {
527
- D as coverflowCarouselCssText,
528
- N as initCoverflowCarousels
560
+ p as CoverflowCarouselElement,
561
+ N as coverflowCarouselCssText,
562
+ F as initCoverflowCarousels,
563
+ D as registerCoverflowCarouselElement
529
564
  };
package/dist/index.umd.js CHANGED
@@ -164,4 +164,4 @@
164
164
  white-space: nowrap;
165
165
  border: 0;
166
166
  }
167
- `,E=d;function m(a){return"adoptedStyleSheets"in a}function g(a){try{const t=new CSSStyleSheet;return t.replaceSync(a),t}catch{return null}}const A=g(d),S=d;function v(a){return a.split(",").map(t=>t.trim()).filter(Boolean)}function x(a,t){const e=a.trim();if(!e)return t;if(e.endsWith("ms")){const n=Number(e.slice(0,-2).trim());return Number.isFinite(n)?n:t}if(e.endsWith("s")){const n=Number(e.slice(0,-1).trim());return Number.isFinite(n)?n*1e3:t}const i=Number(e);return Number.isFinite(i)?i:t}function I(a,t){const e=v(a.transitionProperty),i=v(a.transitionDuration),n=v(a.transitionDelay);if(!e.length)return 0;const r=(F=>{const b=e.indexOf(F);if(b>=0)return b;const w=e.indexOf("all");return w>=0?w:-1})(t);if(r<0)return 0;const c=i[Math.min(r,i.length-1)]??"0s",o=n[Math.min(r,n.length-1)]??"0s",D=x(c,0),N=x(o,0);return Math.max(0,D+N)}function _(){return window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches??!1}function h(a,t,e){if(!a.hasAttribute(t))return e;const i=a.getAttribute(t);return i==null||i===""?!0:i!=="false"}function k(a,t,e){const i=a.getAttribute(t);if(i==null)return e;const n=Number(i.trim());return Number.isFinite(n)?Math.trunc(n):e}function y(a,t){const e=a.getAttribute(t);if(e==null)return null;const i=e.trim();return i||null}function u(a,t){return t<=0?0:(a%t+t)%t}function C(a,t,e){const i=t-a,n=e/2;return i>n?i-e:i<-n?i+e:i}const f=1;let T=0;class p extends HTMLElement{static defaultStylesheet=A;static observedAttributes=["start-index","index","show-dots","show-arrows","announce-changes"];shadow=this.attachShadow({mode:"open"});instanceId=`cfc-${++T}`;rootEl;trackEl;dotsEl;prevBtn;nextBtn;liveRegion;styleEl=null;cards=[];dots=[];currentIndex=0;isAnimating=!1;hasAppliedInitialLayout=!1;lastLayoutIndex=null;lastVisibleSet=new Set;pendingAnimToken=0;animFallbackTimerId=null;cleanup=[];reflectGuard=!1;connectedCallback(){this.render(),this.readAttributes({isInit:!0}),this.refresh()}disconnectedCallback(){this.destroy()}attributeChangedCallback(t,e,i){if(!this.isConnected||this.reflectGuard)return;this.readAttributes({isInit:!1});const n=y(this,"index");if(n!=null){const s=u(Number(n),this.cards.length);if(Number.isFinite(s)&&s!==this.currentIndex){this.goTo(s);return}}this.applyLayoutAndA11y({announce:!0,emitChange:!1})}next(){this.goTo(this.currentIndex+1)}prev(){this.goTo(this.currentIndex-1)}goTo(t){if(this.isAnimating)return;const e=u(t,this.cards.length);e!==this.currentIndex&&(this.isAnimating=!0,this.currentIndex=e,this.reflectIndexAttr(),this.applyLayoutAndA11y({announce:!0,emitChange:!0}),this.lockUntilTransitionEnd())}refresh(){this.rebuildCardsFromLightDom();const t=k(this,"start-index",0),e=y(this,"index"),i=e!=null?Number(e):t,n=Number.isFinite(i)?i:0;this.currentIndex=u(n,this.cards.length),this.reflectIndexAttr(),this.buildDots(),this.hasAppliedInitialLayout=!1,this.lastLayoutIndex=null,this.lastVisibleSet=new Set,this.applyLayoutAndA11y({announce:!0,emitChange:!1}),this.dispatchReady()}destroy(){this.animFallbackTimerId!==null&&(window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=null),this.cleanup.forEach(t=>{t()}),this.cleanup=[],this.isAnimating=!1}adoptStylesheet(t){this.applyStyles(t)}adoptStyles(t){this.applyStyles(t)}readAttributes(t){this.rootEl||this.render();const e=h(this,"show-arrows",!1),i=h(this,"show-dots",!1);this.prevBtn.style.display=e?"":"none",this.nextBtn.style.display=e?"":"none",this.dotsEl.style.display=i?"":"none";const n=this.getAttribute("aria-label");n&&this.rootEl.setAttribute("aria-label",n),t.isInit&&this.setAttribute("aria-roledescription","carousel")}render(){this.applyStyles(null),this.rootEl=document.createElement("div"),this.rootEl.className="cfc",this.trackEl=document.createElement("div"),this.trackEl.className="cfc__track",this.prevBtn=document.createElement("button"),this.prevBtn.type="button",this.prevBtn.className="cfc__arrow cfc__arrow--left",this.prevBtn.setAttribute("aria-label","Previous"),this.prevBtn.textContent="‹",this.nextBtn=document.createElement("button"),this.nextBtn.type="button",this.nextBtn.className="cfc__arrow cfc__arrow--right",this.nextBtn.setAttribute("aria-label","Next"),this.nextBtn.textContent="›",this.dotsEl=document.createElement("div"),this.dotsEl.className="cfc__dots",this.liveRegion=document.createElement("div"),this.liveRegion.className="cfc__sr",this.liveRegion.setAttribute("aria-live","polite"),this.liveRegion.setAttribute("aria-atomic","true"),this.rootEl.append(this.trackEl,this.prevBtn,this.nextBtn,this.dotsEl,this.liveRegion),this.shadow.innerHTML="",this.styleEl&&this.shadow.append(this.styleEl),this.shadow.append(this.rootEl),this.bindEvents()}bindEvents(){this.cleanup.forEach(n=>{n()}),this.cleanup=[];const t=()=>this.prev(),e=()=>this.next();this.prevBtn.addEventListener("click",t),this.nextBtn.addEventListener("click",e),this.cleanup.push(()=>this.prevBtn.removeEventListener("click",t)),this.cleanup.push(()=>this.nextBtn.removeEventListener("click",e));const i=n=>{if(!this.isAnimating||n.propertyName!=="transform"||!(n.target instanceof HTMLElement))return;const s=this.cards[this.currentIndex];s&&n.target===s&&this.unlockAnimation()};this.rootEl.addEventListener("transitionend",i),this.cleanup.push(()=>this.rootEl.removeEventListener("transitionend",i))}rebuildCardsFromLightDom(){const t=Array.from(this.trackEl.children).filter(r=>r instanceof HTMLElement&&r.classList.contains("cfc__card")),e=[];t.forEach(r=>{const c=r.firstElementChild;c instanceof HTMLElement&&e.push(c)});const i=Array.from(this.children).filter(r=>r instanceof HTMLElement),n=[...e,...i],s=[];for(let r=0;r<n.length;r++){const c=n[r],o=t[r]??document.createElement("div");t[r]||(o.className="cfc__card",this.trackEl.append(o)),o.firstElementChild!==c&&o.replaceChildren(c),s.push(o)}for(let r=n.length;r<t.length;r++)t[r]?.remove();this.cards=s,this.hasAppliedInitialLayout=!1,this.lastLayoutIndex=null,this.lastVisibleSet=new Set}getVisibleSet(){const t=this.cards.length,e=new Set;if(t<=0)return e;const i=Math.floor(t/2);if(f>=i){for(let n=0;n<t;n++)e.add(n);return e}for(let n=-f;n<=f;n++)e.add(u(this.currentIndex+n,t));return e}buildDots(){const t=Array.from(this.dotsEl.children).filter(s=>s instanceof HTMLElement&&s.classList.contains("cfc__dot"));if(!h(this,"show-dots",!1)){t.forEach(s=>{s.remove()}),this.dots=[];return}const i=[],n=this.cards.length;for(let s=0;s<n;s++){const r=t[s]??document.createElement("span");t[s]||this.dotsEl.append(r),r.className="cfc__dot",r.setAttribute("aria-hidden","true"),i.push(r)}for(let s=n;s<t.length;s++)t[s]?.remove();this.dots=i,this.updateDotsVisualState()}computeCardState(t){const e=this.cards.length,i=C(this.currentIndex,t,e),n=Math.abs(i);return{index:t,delta:i,abs:n,isVisible:n<=f,isActive:t===this.currentIndex}}applyCardState(t,e){t.setAttribute("aria-hidden",e.isVisible?"false":"true"),t.dataset.active=e.isActive?"true":"false",t.setAttribute("role","group"),t.setAttribute("aria-roledescription","slide"),t.setAttribute("aria-setsize",String(this.cards.length)),t.setAttribute("aria-posinset",String(e.index+1));const i=`${this.instanceId}-slide-${e.index}`;t.id=i;const n=String(e.delta),s=String(e.abs),r=t.dataset.cfcDelta,c=t.dataset.cfcAbs;r!==n&&(t.style.setProperty("--cfc-delta",n),t.dataset.cfcDelta=n),c!==s&&(t.style.setProperty("--cfc-abs",s),t.dataset.cfcAbs=s)}applyLayoutAndA11y(t){if(this.cards.length){if(this.hasAppliedInitialLayout){const e=this.getVisibleSet(),i=new Set;this.lastVisibleSet.forEach(n=>{i.add(n)}),e.forEach(n=>{i.add(n)}),this.lastLayoutIndex!=null&&i.add(this.lastLayoutIndex),i.add(this.currentIndex),i.forEach(n=>{const s=this.cards[n];if(!s)return;const r=this.computeCardState(n);this.applyCardState(s,r)}),this.lastLayoutIndex=this.currentIndex,this.lastVisibleSet=e}else{for(let e=0;e<this.cards.length;e++){const i=this.cards[e];if(!i)continue;const n=this.computeCardState(e);this.applyCardState(i,n)}this.hasAppliedInitialLayout=!0,this.lastLayoutIndex=this.currentIndex,this.lastVisibleSet=this.getVisibleSet()}this.updateDotsVisualState(),t.announce&&h(this,"announce-changes",!0)&&this.announce(`Slide ${this.currentIndex+1} of ${this.cards.length}`),t.emitChange&&this.emitChange()}}updateDotsVisualState(){if(this.dots.length)for(let t=0;t<this.dots.length;t++){const e=this.dots[t],i=t===this.currentIndex;e.dataset.active=i?"true":"false"}}lockUntilTransitionEnd(){this.pendingAnimToken++;const t=this.pendingAnimToken;if(_()){this.unlockAnimation();return}const e=this.cards[this.currentIndex];if(!e){this.unlockAnimation();return}const i=getComputedStyle(e),n=I(i,"transform");if(n<=0){this.unlockAnimation();return}const s=x(i.getPropertyValue("--cfc-transition-ms").trim(),400),c=Math.max(n,s)+60;this.animFallbackTimerId!==null&&window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=window.setTimeout(()=>{this.pendingAnimToken===t&&this.unlockAnimation()},c)}unlockAnimation(){this.isAnimating=!1,this.animFallbackTimerId!==null&&(window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=null)}emitChange(){this.dispatchEvent(new CustomEvent("coverflow-carousel:change",{detail:{index:this.currentIndex,length:this.cards.length}}))}dispatchReady(){this.dispatchEvent(new CustomEvent("coverflow-carousel:ready",{detail:{index:this.currentIndex,length:this.cards.length}}))}announce(t){this.liveRegion.textContent="",queueMicrotask(()=>{this.liveRegion.textContent=t})}reflectIndexAttr(){this.reflectGuard=!0;try{this.setAttribute("index",String(this.currentIndex))}finally{this.reflectGuard=!1}}applyStyles(t){if(typeof t=="string"){if(m(this.shadow)){const e=g(t);if(e){this.shadow.adoptedStyleSheets=[e],this.styleEl=null;return}}this.styleEl||(this.styleEl=document.createElement("style")),this.styleEl.textContent=t;return}if(t&&m(this.shadow)){this.shadow.adoptedStyleSheets=[t],this.styleEl=null;return}if(p.defaultStylesheet&&m(this.shadow)){this.shadow.adoptedStyleSheets=[p.defaultStylesheet],this.styleEl=null;return}this.styleEl||(this.styleEl=document.createElement("style")),this.styleEl.textContent=S}}function L(a={}){const{selector:t="coverflow-carousel",onReady:e,onChange:i,stylesheet:n}=a,s=Array.from(document.querySelectorAll(t));return(e||i)&&s.forEach(r=>{e&&r.addEventListener("coverflow-carousel:ready",c=>{e(r,c.detail)}),i&&r.addEventListener("coverflow-carousel:change",c=>{i(r,c.detail)})}),n&&s.forEach(r=>{const c=r;typeof n=="string"?c.adoptStyles(n):c.adoptStylesheet(n)}),s}customElements.get("coverflow-carousel")||customElements.define("coverflow-carousel",p),l.coverflowCarouselCssText=E,l.initCoverflowCarousels=L,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
167
+ `,I=d;function x(a){return"adoptedStyleSheets"in a}function E(a){try{const t=new CSSStyleSheet;return t.replaceSync(a),t}catch{return null}}const C=E(d),_=d;function g(a){return a.split(",").map(t=>t.trim()).filter(Boolean)}function b(a,t){const e=a.trim();if(!e)return t;if(e.endsWith("ms")){const n=Number(e.slice(0,-2).trim());return Number.isFinite(n)?n:t}if(e.endsWith("s")){const n=Number(e.slice(0,-1).trim());return Number.isFinite(n)?n*1e3:t}const i=Number(e);return Number.isFinite(i)?i:t}function k(a,t){const e=g(a.transitionProperty),i=g(a.transitionDuration),n=g(a.transitionDelay);if(!e.length)return 0;const r=(y=>{const u=e.indexOf(y);if(u>=0)return u;const A=e.indexOf("all");return A>=0?A:-1})(t);if(r<0)return 0;const c=i[Math.min(r,i.length-1)]??"0s",o=n[Math.min(r,n.length-1)]??"0s",h=b(c,0),S=b(o,0);return Math.max(0,h+S)}function T(){return window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches??!1}function p(a,t,e){if(!a.hasAttribute(t))return e;const i=a.getAttribute(t);return i==null||i===""?!0:i!=="false"}function L(a,t,e){const i=a.getAttribute(t);if(i==null)return e;const n=Number(i.trim());return Number.isFinite(n)?Math.trunc(n):e}function w(a,t){const e=a.getAttribute(t);if(e==null)return null;const i=e.trim();return i||null}function m(a,t){return t<=0?0:(a%t+t)%t}function N(a,t,e){const i=t-a,n=e/2;return i>n?i-e:i<-n?i+e:i}const v=1;let F=0;class f extends HTMLElement{static defaultStylesheet=C;static observedAttributes=["start-index","index","show-dots","show-arrows","announce-changes"];shadow=this.attachShadow({mode:"open"});instanceId=`cfc-${++F}`;rootEl;trackEl;dotsEl;prevBtn;nextBtn;liveRegion;styleEl=null;cards=[];dots=[];currentIndex=0;isAnimating=!1;hasAppliedInitialLayout=!1;lastLayoutIndex=null;lastVisibleSet=new Set;pendingAnimToken=0;animFallbackTimerId=null;cleanup=[];reflectGuard=!1;connectedCallback(){this.render(),this.readAttributes({isInit:!0}),this.refresh()}disconnectedCallback(){this.destroy()}attributeChangedCallback(t,e,i){if(!this.isConnected||this.reflectGuard)return;this.readAttributes({isInit:!1});const n=w(this,"index");if(n!=null){const s=m(Number(n),this.cards.length);if(Number.isFinite(s)&&s!==this.currentIndex){this.goTo(s);return}}this.applyLayoutAndA11y({announce:!0,emitChange:!1})}next(){this.goTo(this.currentIndex+1)}prev(){this.goTo(this.currentIndex-1)}goTo(t){if(this.isAnimating)return;const e=m(t,this.cards.length);e!==this.currentIndex&&(this.isAnimating=!0,this.currentIndex=e,this.reflectIndexAttr(),this.applyLayoutAndA11y({announce:!0,emitChange:!0}),this.lockUntilTransitionEnd())}refresh(){this.rebuildCardsFromLightDom();const t=L(this,"start-index",0),e=w(this,"index"),i=e!=null?Number(e):t,n=Number.isFinite(i)?i:0;this.currentIndex=m(n,this.cards.length),this.reflectIndexAttr(),this.buildDots(),this.hasAppliedInitialLayout=!1,this.lastLayoutIndex=null,this.lastVisibleSet=new Set,this.applyLayoutAndA11y({announce:!0,emitChange:!1}),this.dispatchReady()}destroy(){this.animFallbackTimerId!==null&&(window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=null),this.cleanup.forEach(t=>{t()}),this.cleanup=[],this.isAnimating=!1}adoptStylesheet(t){this.applyStyles(t)}adoptStyles(t){this.applyStyles(t)}readAttributes(t){this.rootEl||this.render();const e=p(this,"show-arrows",!1),i=p(this,"show-dots",!1);this.prevBtn.style.display=e?"":"none",this.nextBtn.style.display=e?"":"none",this.dotsEl.style.display=i?"":"none";const n=this.getAttribute("aria-label");n&&this.rootEl.setAttribute("aria-label",n),t.isInit&&this.setAttribute("aria-roledescription","carousel")}render(){this.applyStyles(null),this.rootEl=document.createElement("div"),this.rootEl.className="cfc",this.trackEl=document.createElement("div"),this.trackEl.className="cfc__track",this.prevBtn=document.createElement("button"),this.prevBtn.type="button",this.prevBtn.className="cfc__arrow cfc__arrow--left",this.prevBtn.setAttribute("aria-label","Previous"),this.prevBtn.textContent="‹",this.nextBtn=document.createElement("button"),this.nextBtn.type="button",this.nextBtn.className="cfc__arrow cfc__arrow--right",this.nextBtn.setAttribute("aria-label","Next"),this.nextBtn.textContent="›",this.dotsEl=document.createElement("div"),this.dotsEl.className="cfc__dots",this.liveRegion=document.createElement("div"),this.liveRegion.className="cfc__sr",this.liveRegion.setAttribute("aria-live","polite"),this.liveRegion.setAttribute("aria-atomic","true"),this.rootEl.append(this.trackEl,this.prevBtn,this.nextBtn,this.dotsEl,this.liveRegion),this.shadow.innerHTML="",this.styleEl&&this.shadow.append(this.styleEl),this.shadow.append(this.rootEl),this.bindEvents()}bindEvents(){this.cleanup.forEach(s=>{s()}),this.cleanup=[];const t=()=>this.prev(),e=()=>this.next();this.prevBtn.addEventListener("click",t),this.nextBtn.addEventListener("click",e),this.cleanup.push(()=>this.prevBtn.removeEventListener("click",t)),this.cleanup.push(()=>this.nextBtn.removeEventListener("click",e));const i=s=>{if(!this.isAnimating||s.propertyName!=="transform"||!(s.target instanceof HTMLElement))return;const r=this.cards[this.currentIndex];r&&s.target===r&&this.unlockAnimation()};this.rootEl.addEventListener("transitionend",i),this.cleanup.push(()=>this.rootEl.removeEventListener("transitionend",i));const n=s=>{const r=s.target;if(!(r instanceof HTMLElement)||r.tagName!=="SCRATCH-REVEAL")return;const c=s.composedPath?.();if(!c?.length)return;const o=c.find(u=>u instanceof HTMLElement&&u.classList.contains("cfc__card")&&u.dataset.cfcIndex!=null);if(!o)return;const h=Number(o.dataset.cfcIndex);if(!Number.isFinite(h))return;const y=s.detail?.percent??100;this.dispatchEvent(new CustomEvent("coverflow-carousel:scratch-complete",{detail:{index:h,length:this.cards.length,percent:y},bubbles:!0,composed:!0}))};this.rootEl.addEventListener("complete",n),this.cleanup.push(()=>this.rootEl.removeEventListener("complete",n))}rebuildCardsFromLightDom(){const t=Array.from(this.trackEl.children).filter(r=>r instanceof HTMLElement&&r.classList.contains("cfc__card")),e=[];t.forEach(r=>{const c=r.firstElementChild;c instanceof HTMLElement&&e.push(c)});const i=Array.from(this.children).filter(r=>r instanceof HTMLElement),n=[...e,...i],s=[];for(let r=0;r<n.length;r++){const c=n[r],o=t[r]??document.createElement("div");t[r]||(o.className="cfc__card",this.trackEl.append(o)),o.firstElementChild!==c&&o.replaceChildren(c),s.push(o)}for(let r=n.length;r<t.length;r++)t[r]?.remove();this.cards=s,this.hasAppliedInitialLayout=!1,this.lastLayoutIndex=null,this.lastVisibleSet=new Set}getVisibleSet(){const t=this.cards.length,e=new Set;if(t<=0)return e;const i=Math.floor(t/2);if(v>=i){for(let n=0;n<t;n++)e.add(n);return e}for(let n=-v;n<=v;n++)e.add(m(this.currentIndex+n,t));return e}buildDots(){const t=Array.from(this.dotsEl.children).filter(s=>s instanceof HTMLElement&&s.classList.contains("cfc__dot"));if(!p(this,"show-dots",!1)){t.forEach(s=>{s.remove()}),this.dots=[];return}const i=[],n=this.cards.length;for(let s=0;s<n;s++){const r=t[s]??document.createElement("span");t[s]||this.dotsEl.append(r),r.className="cfc__dot",r.setAttribute("aria-hidden","true"),i.push(r)}for(let s=n;s<t.length;s++)t[s]?.remove();this.dots=i,this.updateDotsVisualState()}computeCardState(t){const e=this.cards.length,i=N(this.currentIndex,t,e),n=Math.abs(i);return{index:t,delta:i,abs:n,isVisible:n<=v,isActive:t===this.currentIndex}}applyCardState(t,e){t.setAttribute("aria-hidden",e.isVisible?"false":"true"),t.dataset.active=e.isActive?"true":"false",t.dataset.cfcIndex=String(e.index),t.setAttribute("role","group"),t.setAttribute("aria-roledescription","slide"),t.setAttribute("aria-setsize",String(this.cards.length)),t.setAttribute("aria-posinset",String(e.index+1));const i=`${this.instanceId}-slide-${e.index}`;t.id=i;const n=String(e.delta),s=String(e.abs),r=t.dataset.cfcDelta,c=t.dataset.cfcAbs;r!==n&&(t.style.setProperty("--cfc-delta",n),t.dataset.cfcDelta=n),c!==s&&(t.style.setProperty("--cfc-abs",s),t.dataset.cfcAbs=s)}applyLayoutAndA11y(t){if(this.cards.length){if(this.hasAppliedInitialLayout){const e=this.getVisibleSet(),i=new Set;this.lastVisibleSet.forEach(n=>{i.add(n)}),e.forEach(n=>{i.add(n)}),this.lastLayoutIndex!=null&&i.add(this.lastLayoutIndex),i.add(this.currentIndex),i.forEach(n=>{const s=this.cards[n];if(!s)return;const r=this.computeCardState(n);this.applyCardState(s,r)}),this.lastLayoutIndex=this.currentIndex,this.lastVisibleSet=e}else{for(let e=0;e<this.cards.length;e++){const i=this.cards[e];if(!i)continue;const n=this.computeCardState(e);this.applyCardState(i,n)}this.hasAppliedInitialLayout=!0,this.lastLayoutIndex=this.currentIndex,this.lastVisibleSet=this.getVisibleSet()}this.updateDotsVisualState(),t.announce&&p(this,"announce-changes",!0)&&this.announce(`Slide ${this.currentIndex+1} of ${this.cards.length}`),t.emitChange&&this.emitChange()}}updateDotsVisualState(){if(this.dots.length)for(let t=0;t<this.dots.length;t++){const e=this.dots[t],i=t===this.currentIndex;e.dataset.active=i?"true":"false"}}lockUntilTransitionEnd(){this.pendingAnimToken++;const t=this.pendingAnimToken;if(T()){this.unlockAnimation();return}const e=this.cards[this.currentIndex];if(!e){this.unlockAnimation();return}const i=getComputedStyle(e),n=k(i,"transform");if(n<=0){this.unlockAnimation();return}const s=b(i.getPropertyValue("--cfc-transition-ms").trim(),400),c=Math.max(n,s)+60;this.animFallbackTimerId!==null&&window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=window.setTimeout(()=>{this.pendingAnimToken===t&&this.unlockAnimation()},c)}unlockAnimation(){this.isAnimating=!1,this.animFallbackTimerId!==null&&(window.clearTimeout(this.animFallbackTimerId),this.animFallbackTimerId=null)}emitChange(){this.dispatchEvent(new CustomEvent("coverflow-carousel:change",{detail:{index:this.currentIndex,length:this.cards.length}}))}dispatchReady(){this.dispatchEvent(new CustomEvent("coverflow-carousel:ready",{detail:{index:this.currentIndex,length:this.cards.length}}))}announce(t){this.liveRegion.textContent="",queueMicrotask(()=>{this.liveRegion.textContent=t})}reflectIndexAttr(){this.reflectGuard=!0;try{this.setAttribute("index",String(this.currentIndex))}finally{this.reflectGuard=!1}}applyStyles(t){if(typeof t=="string"){if(x(this.shadow)){const e=E(t);if(e){this.shadow.adoptedStyleSheets=[e],this.styleEl=null;return}}this.styleEl||(this.styleEl=document.createElement("style")),this.styleEl.textContent=t;return}if(t&&x(this.shadow)){this.shadow.adoptedStyleSheets=[t],this.styleEl=null;return}if(f.defaultStylesheet&&x(this.shadow)){this.shadow.adoptedStyleSheets=[f.defaultStylesheet],this.styleEl=null;return}this.styleEl||(this.styleEl=document.createElement("style")),this.styleEl.textContent=_}}function D(a={}){const{selector:t="coverflow-carousel",onReady:e,onChange:i,onScratchComplete:n,stylesheet:s}=a,r=Array.from(document.querySelectorAll(t));return(e||i||n)&&r.forEach(c=>{e&&c.addEventListener("coverflow-carousel:ready",o=>{e(c,o.detail)}),i&&c.addEventListener("coverflow-carousel:change",o=>{i(c,o.detail)}),n&&c.addEventListener("coverflow-carousel:scratch-complete",o=>{n(c,o.detail)})}),s&&r.forEach(c=>{const o=c;typeof s=="string"?o.adoptStyles(s):o.adoptStylesheet(s)}),r}function B(a="coverflow-carousel"){typeof window>"u"||!("customElements"in window)||customElements.get(a)||customElements.define(a,f)}l.CoverflowCarouselElement=f,l.coverflowCarouselCssText=I,l.initCoverflowCarousels=D,l.registerCoverflowCarouselElement=B,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
package/dist/init.d.ts CHANGED
@@ -8,6 +8,11 @@ export type InitCoverflowCarouselsOptions = {
8
8
  index: number;
9
9
  length: number;
10
10
  }) => void;
11
+ onScratchComplete?: (el: HTMLElement, detail: {
12
+ index: number;
13
+ length: number;
14
+ percent: number;
15
+ }) => void;
11
16
  stylesheet?: CSSStyleSheet | string | null;
12
17
  };
13
18
  export declare function initCoverflowCarousels(options?: InitCoverflowCarouselsOptions): HTMLElement[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "coverflow-carousel",
3
- "version": "0.1.0-dev.0",
4
- "description": "Tiny coverflow carousel Web Component. Registers <coverflow-carousel> and exposes API via attributes + events.",
3
+ "version": "0.1.1-dev.0",
4
+ "description": "Tiny coverflow carousel Web Component. Exposes API via attributes + events (explicit element registration).",
5
5
  "author": "ux-ui.pro",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -12,7 +12,7 @@
12
12
  "url": "https://github.com/ux-ui-pro/coverflow-carousel/issues"
13
13
  },
14
14
  "homepage": "https://github.com/ux-ui-pro/coverflow-carousel",
15
- "sideEffects": true,
15
+ "sideEffects": false,
16
16
  "scripts": {
17
17
  "clean": "rimraf dist",
18
18
  "build": "vite build",