peek-carousel 1.0.2 → 1.0.4
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 +2 -1
- package/dist/assets/share_image.png +0 -0
- package/dist/favicon-16x16.png +0 -0
- package/dist/favicon-32x32.png +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/motivation0.png +0 -0
- package/dist/peek-carousel.esm.js +598 -1054
- package/dist/peek-carousel.esm.js.map +1 -1
- package/dist/peek-carousel.js +5 -1369
- package/dist/peek-carousel.js.map +1 -1
- package/examples/example-built.html +1 -1
- package/examples/example.scss +979 -0
- package/package.json +21 -25
- package/src/modules/Animator.js +96 -11
- package/src/modules/EventHandler.js +19 -5
- package/dist/peek-carousel.esm.min.js +0 -8
- package/dist/peek-carousel.esm.min.js.map +0 -1
- package/dist/peek-carousel.min.js +0 -8
- package/dist/peek-carousel.min.js.map +0 -1
- package/examples/example.css +0 -2216
- package/examples/example.js +0 -404
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peek-carousel",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "A modular carousel library with three layout modes (Stack/Card, Radial, Classic) featuring peek effect, touch/drag support, and full accessibility - inspired by iPhone 17 Pro",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
"style": "dist/peek-carousel.css",
|
|
13
13
|
"exports": {
|
|
14
14
|
".": {
|
|
15
|
+
"types": "./types/index.d.ts",
|
|
15
16
|
"import": "./dist/peek-carousel.esm.js",
|
|
16
|
-
"require": "./dist/peek-carousel.js"
|
|
17
|
-
"types": "./types/index.d.ts"
|
|
17
|
+
"require": "./dist/peek-carousel.js"
|
|
18
18
|
},
|
|
19
19
|
"./dist/peek-carousel.css": "./dist/peek-carousel.css",
|
|
20
20
|
"./dist/peek-carousel.min.css": "./dist/peek-carousel.min.css"
|
|
@@ -29,22 +29,19 @@
|
|
|
29
29
|
"LICENSE"
|
|
30
30
|
],
|
|
31
31
|
"scripts": {
|
|
32
|
-
"
|
|
33
|
-
"build
|
|
34
|
-
"
|
|
35
|
-
"build:css
|
|
32
|
+
"dev": "vite",
|
|
33
|
+
"build": "vite build",
|
|
34
|
+
"preview": "vite preview",
|
|
35
|
+
"build:css": "sass src/styles/peek-carousel.scss dist/peek-carousel.css --no-source-map && postcss dist/peek-carousel.css -o dist/peek-carousel.css",
|
|
36
|
+
"build:css:min": "sass src/styles/peek-carousel.scss dist/peek-carousel.min.css --style=compressed --no-source-map && postcss dist/peek-carousel.min.css -o dist/peek-carousel.min.css",
|
|
36
37
|
"build:example:css": "postcss examples/example.css -o examples/example.min.css",
|
|
37
38
|
"build:example:js": "terser examples/example.js -o examples/example.min.js -c -m",
|
|
38
|
-
"postbuild
|
|
39
|
-
"postbuild:css:min": "postcss dist/peek-carousel.min.css -o dist/peek-carousel.min.css",
|
|
40
|
-
"build": "npm run clean && npm-run-all --parallel build:js build:css build:css:min",
|
|
41
|
-
"build:examples": "npm-run-all --parallel build:example:css build:example:js",
|
|
42
|
-
"watch:js": "rollup -c -w",
|
|
43
|
-
"watch:css": "sass --watch src/styles/peek-carousel.scss dist/peek-carousel.css",
|
|
44
|
-
"watch": "npm-run-all --parallel watch:js watch:css",
|
|
45
|
-
"dev": "npm run watch",
|
|
39
|
+
"postbuild": "npm run build:css && npm run build:css:min",
|
|
46
40
|
"prepublishOnly": "npm run build",
|
|
47
|
-
"test": "
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"test:watch": "vitest",
|
|
43
|
+
"test:ui": "vitest --ui",
|
|
44
|
+
"test:coverage": "vitest run --coverage"
|
|
48
45
|
},
|
|
49
46
|
"repository": {
|
|
50
47
|
"type": "git",
|
|
@@ -74,19 +71,18 @@
|
|
|
74
71
|
"node": ">=14.0.0"
|
|
75
72
|
},
|
|
76
73
|
"devDependencies": {
|
|
77
|
-
"@
|
|
78
|
-
"@
|
|
79
|
-
"@rollup/plugin-babel": "^6.0.4",
|
|
80
|
-
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
81
|
-
"@rollup/plugin-terser": "^0.4.4",
|
|
74
|
+
"@testing-library/dom": "^10.4.1",
|
|
75
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
82
76
|
"autoprefixer": "^10.4.16",
|
|
83
77
|
"cssnano": "^6.0.1",
|
|
84
|
-
"
|
|
78
|
+
"happy-dom": "^20.0.11",
|
|
79
|
+
"jsdom": "^27.3.0",
|
|
85
80
|
"postcss": "^8.4.31",
|
|
86
81
|
"postcss-cli": "^11.0.0",
|
|
87
|
-
"rollup": "^4.1.4",
|
|
88
|
-
"rollup-plugin-postcss": "^4.0.2",
|
|
89
82
|
"sass": "^1.69.5",
|
|
90
|
-
"terser": "^5.44.1"
|
|
83
|
+
"terser": "^5.44.1",
|
|
84
|
+
"vite": "^7.3.1",
|
|
85
|
+
"vite-plugin-banner": "^0.8.1",
|
|
86
|
+
"vitest": "^4.0.16"
|
|
91
87
|
}
|
|
92
88
|
}
|
package/src/modules/Animator.js
CHANGED
|
@@ -35,6 +35,7 @@ export class Animator {
|
|
|
35
35
|
this.carousel = carousel;
|
|
36
36
|
this.momentumAnimation = null;
|
|
37
37
|
this.isAnimating = false;
|
|
38
|
+
this.previousIndex = null;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
normalizeAngleDiff(diff) {
|
|
@@ -72,7 +73,6 @@ export class Animator {
|
|
|
72
73
|
return;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
// [개발참고] 최단 경로 계산: -180 ~ 180 범위로 정규화
|
|
76
76
|
const currentAngle = parseFloat(currentRotation);
|
|
77
77
|
const diff = this.normalizeAngleDiff(targetAngle - currentAngle);
|
|
78
78
|
const finalAngle = currentAngle + diff;
|
|
@@ -157,13 +157,32 @@ export class Animator {
|
|
|
157
157
|
return baseSpacing + additionalSpacing;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
// ==========================================
|
|
161
|
+
// Classic Mode - Seamless Infinite Scroll
|
|
162
|
+
// ==========================================
|
|
163
|
+
|
|
164
|
+
getWrapInfo(prevIndex, currentIndex) {
|
|
165
|
+
if (prevIndex === null) return { isWrap: false, direction: 0 };
|
|
166
|
+
|
|
167
|
+
const total = this.carousel.totalItems;
|
|
168
|
+
const isForwardWrap = prevIndex === total - 1 && currentIndex === 0;
|
|
169
|
+
const isBackwardWrap = prevIndex === 0 && currentIndex === total - 1;
|
|
170
|
+
|
|
171
|
+
if (isForwardWrap) return { isWrap: true, direction: 1 };
|
|
172
|
+
if (isBackwardWrap) return { isWrap: true, direction: -1 };
|
|
173
|
+
|
|
174
|
+
return { isWrap: false, direction: currentIndex > prevIndex ? 1 : -1 };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
getClassicItemPosition(itemIndex, currentIndex, itemSpacing, wrapDirection = 0) {
|
|
161
178
|
const { prev, next } = this.getAdjacentIndices(currentIndex);
|
|
179
|
+
const total = this.carousel.totalItems;
|
|
162
180
|
|
|
163
181
|
if (itemIndex === currentIndex) {
|
|
164
182
|
return {
|
|
165
183
|
x: CLASSIC_POSITIONS.center.x,
|
|
166
184
|
scale: CLASSIC_POSITIONS.center.scale,
|
|
185
|
+
isCenter: true,
|
|
167
186
|
};
|
|
168
187
|
}
|
|
169
188
|
|
|
@@ -171,6 +190,7 @@ export class Animator {
|
|
|
171
190
|
return {
|
|
172
191
|
x: CLASSIC_POSITIONS.center.x - itemSpacing,
|
|
173
192
|
scale: CLASSIC_POSITIONS.peek.scale,
|
|
193
|
+
isPrev: true,
|
|
174
194
|
};
|
|
175
195
|
}
|
|
176
196
|
|
|
@@ -178,15 +198,21 @@ export class Animator {
|
|
|
178
198
|
return {
|
|
179
199
|
x: CLASSIC_POSITIONS.center.x + itemSpacing,
|
|
180
200
|
scale: CLASSIC_POSITIONS.peek.scale,
|
|
201
|
+
isNext: true,
|
|
181
202
|
};
|
|
182
203
|
}
|
|
183
204
|
|
|
184
|
-
|
|
205
|
+
// For other items, calculate based on circular distance
|
|
206
|
+
const forwardDist = (itemIndex - currentIndex + total) % total;
|
|
207
|
+
const backwardDist = (currentIndex - itemIndex + total) % total;
|
|
208
|
+
const isOnRight = forwardDist < backwardDist;
|
|
209
|
+
|
|
185
210
|
return {
|
|
186
|
-
x:
|
|
187
|
-
? CLASSIC_POSITIONS.center.x
|
|
188
|
-
: CLASSIC_POSITIONS.center.x
|
|
211
|
+
x: isOnRight
|
|
212
|
+
? CLASSIC_POSITIONS.center.x + itemSpacing * 2
|
|
213
|
+
: CLASSIC_POSITIONS.center.x - itemSpacing * 2,
|
|
189
214
|
scale: CLASSIC_POSITIONS.hidden.scale,
|
|
215
|
+
isHidden: true,
|
|
190
216
|
};
|
|
191
217
|
}
|
|
192
218
|
|
|
@@ -194,20 +220,79 @@ export class Animator {
|
|
|
194
220
|
const { prev, next } = this.getAdjacentIndices(currentIndex);
|
|
195
221
|
const containerWidth = this.carousel.container.offsetWidth;
|
|
196
222
|
const itemSpacing = this.calculateClassicSpacing(containerWidth);
|
|
223
|
+
const { isWrap, direction } = this.getWrapInfo(this.previousIndex, currentIndex);
|
|
224
|
+
|
|
225
|
+
const items = this.carousel.items;
|
|
226
|
+
|
|
227
|
+
// Get previous prev/next for transition handling
|
|
228
|
+
const prevAdj = this.previousIndex !== null
|
|
229
|
+
? this.getAdjacentIndices(this.previousIndex)
|
|
230
|
+
: { prev: null, next: null };
|
|
231
|
+
|
|
232
|
+
// For wrapping, only disable transitions on items that would cross the screen
|
|
233
|
+
if (isWrap) {
|
|
234
|
+
// Items that should animate: current, prev, next, previousIndex, and previous prev/next
|
|
235
|
+
const animatingItems = new Set([
|
|
236
|
+
currentIndex,
|
|
237
|
+
prev,
|
|
238
|
+
next,
|
|
239
|
+
this.previousIndex,
|
|
240
|
+
prevAdj.prev,
|
|
241
|
+
prevAdj.next,
|
|
242
|
+
].filter(i => i !== null));
|
|
243
|
+
|
|
244
|
+
for (let i = 0; i < items.length; i++) {
|
|
245
|
+
if (!animatingItems.has(i)) {
|
|
246
|
+
items[i].style.transition = 'none';
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// Force reflow
|
|
250
|
+
this.carousel.container.offsetHeight;
|
|
251
|
+
}
|
|
197
252
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
253
|
+
// Update all items to their final positions
|
|
254
|
+
for (let i = 0; i < items.length; i++) {
|
|
255
|
+
const item = items[i];
|
|
256
|
+
const pos = this.getClassicItemPosition(i, currentIndex, itemSpacing);
|
|
201
257
|
|
|
202
258
|
this.setCSSVariables(item, {
|
|
203
|
-
'--item-x': `${this.round(x, 2)}%`,
|
|
204
|
-
'--item-scale': String(scale),
|
|
259
|
+
'--item-x': `${this.round(pos.x, 2)}%`,
|
|
260
|
+
'--item-scale': String(pos.scale),
|
|
205
261
|
});
|
|
262
|
+
|
|
263
|
+
// Update visibility and opacity
|
|
264
|
+
if (pos.isCenter) {
|
|
265
|
+
item.style.opacity = '1';
|
|
266
|
+
item.style.visibility = 'visible';
|
|
267
|
+
item.style.zIndex = '100';
|
|
268
|
+
} else if (pos.isPrev || pos.isNext) {
|
|
269
|
+
item.style.opacity = '0.6';
|
|
270
|
+
item.style.visibility = 'visible';
|
|
271
|
+
item.style.zIndex = '50';
|
|
272
|
+
} else {
|
|
273
|
+
item.style.opacity = '0';
|
|
274
|
+
item.style.visibility = 'hidden';
|
|
275
|
+
item.style.zIndex = '0';
|
|
276
|
+
}
|
|
206
277
|
}
|
|
207
278
|
|
|
279
|
+
// Re-enable transitions
|
|
280
|
+
if (isWrap) {
|
|
281
|
+
requestAnimationFrame(() => {
|
|
282
|
+
for (let i = 0; i < items.length; i++) {
|
|
283
|
+
items[i].style.transition = '';
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this.previousIndex = currentIndex;
|
|
208
289
|
this.carousel.ui.setPeekItems(prev, next);
|
|
209
290
|
}
|
|
210
291
|
|
|
292
|
+
// ==========================================
|
|
293
|
+
// Momentum
|
|
294
|
+
// ==========================================
|
|
295
|
+
|
|
211
296
|
startMomentum(velocity) {
|
|
212
297
|
this.stopMomentum();
|
|
213
298
|
|
|
@@ -15,10 +15,13 @@ const DRAG_CONFIG = Object.freeze({
|
|
|
15
15
|
|
|
16
16
|
const RESIZE_DEBOUNCE = 100;
|
|
17
17
|
|
|
18
|
+
let handlerId = 0;
|
|
19
|
+
|
|
18
20
|
export class EventHandler {
|
|
19
21
|
constructor(carousel) {
|
|
20
22
|
this.carousel = carousel;
|
|
21
23
|
this.boundHandlers = new Map();
|
|
24
|
+
this.resizeTimer = null;
|
|
22
25
|
|
|
23
26
|
this.touch = {
|
|
24
27
|
startX: 0,
|
|
@@ -356,11 +359,12 @@ export class EventHandler {
|
|
|
356
359
|
}
|
|
357
360
|
|
|
358
361
|
initResize() {
|
|
359
|
-
let resizeTimer;
|
|
360
362
|
const handler = () => {
|
|
361
|
-
clearTimeout(resizeTimer);
|
|
362
|
-
resizeTimer = setTimeout(() => {
|
|
363
|
-
this.carousel
|
|
363
|
+
clearTimeout(this.resizeTimer);
|
|
364
|
+
this.resizeTimer = setTimeout(() => {
|
|
365
|
+
if (this.carousel) {
|
|
366
|
+
this.carousel.animator.updateCarousel();
|
|
367
|
+
}
|
|
364
368
|
}, RESIZE_DEBOUNCE);
|
|
365
369
|
};
|
|
366
370
|
|
|
@@ -370,7 +374,7 @@ export class EventHandler {
|
|
|
370
374
|
addHandler(element, event, handler, options) {
|
|
371
375
|
element.addEventListener(event, handler, options);
|
|
372
376
|
|
|
373
|
-
const key = `${event}-${
|
|
377
|
+
const key = `${event}-${++handlerId}`;
|
|
374
378
|
this.boundHandlers.set(key, { element, event, handler, options });
|
|
375
379
|
}
|
|
376
380
|
|
|
@@ -380,11 +384,21 @@ export class EventHandler {
|
|
|
380
384
|
this.wheel.scrollTimeout = null;
|
|
381
385
|
}
|
|
382
386
|
|
|
387
|
+
if (this.resizeTimer) {
|
|
388
|
+
clearTimeout(this.resizeTimer);
|
|
389
|
+
this.resizeTimer = null;
|
|
390
|
+
}
|
|
391
|
+
|
|
383
392
|
for (const { element, event, handler, options } of this.boundHandlers.values()) {
|
|
384
393
|
element.removeEventListener(event, handler, options);
|
|
385
394
|
}
|
|
386
395
|
this.boundHandlers.clear();
|
|
387
396
|
|
|
397
|
+
this.drag.active = false;
|
|
398
|
+
this.drag.velocity = 0;
|
|
399
|
+
this.wheel.isScrolling = false;
|
|
400
|
+
this.wheel.accumulatedDelta = 0;
|
|
401
|
+
|
|
388
402
|
this.carousel = null;
|
|
389
403
|
}
|
|
390
404
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PeekCarousel - Peek 효과를 가진 캐러셀
|
|
3
|
-
* @version 1.0.0
|
|
4
|
-
* @license MIT
|
|
5
|
-
* @author lledellebell
|
|
6
|
-
*/
|
|
7
|
-
const t=Object.freeze({STACK:"stack",RADIAL:"radial",CLASSIC:"classic"}),e=Object.freeze({startIndex:1,layoutMode:t.STACK,autoRotate:!1,autoRotateInterval:2500,preloadRange:2,swipeThreshold:50,dragThreshold:80,enableKeyboard:!0,enableWheel:!0,enableTouch:!0,enableMouse:!0,showNavigation:!0,showCounter:!0,showIndicators:!0,showAutoRotateButton:!0});const s=Object.freeze({carousel:"peek-carousel",track:"peek-carousel__track",item:"peek-carousel__item",itemActive:"peek-carousel__item--active",itemPrev:"peek-carousel__item--prev",itemNext:"peek-carousel__item--next",itemCenter:"peek-carousel__item--center",itemHidden:"peek-carousel__item--hidden",itemDraggingLeft:"peek-carousel__item--dragging-left",itemDraggingRight:"peek-carousel__item--dragging-right",figure:"peek-carousel__figure",image:"peek-carousel__image",caption:"peek-carousel__caption",nav:"peek-carousel__nav",navBtn:"nav-btn",btn:"peek-carousel__btn",prevBtn:"prev-btn",nextBtn:"next-btn",autoRotateBtn:"auto-rotate-btn",btnAutoRotate:"peek-carousel__btn--auto-rotate",btnActive:"peek-carousel__btn--active",controls:"peek-carousel__controls",indicators:"peek-carousel__indicators",indicator:"indicator",indicatorPeek:"peek-carousel__indicator",indicatorActive:"peek-carousel__indicator--active",indicatorProgress:"peek-carousel__indicator--progress",indicatorCompleted:"peek-carousel__indicator--completed",counter:"peek-carousel__counter",counterCurrent:"peek-carousel__counter-current",counterSeparator:"peek-carousel__counter-separator",counterTotal:"peek-carousel__counter-total",playIcon:"play-icon",pauseIcon:"pause-icon"}),a=Object.freeze({carousel:".peek-carousel__track",item:".peek-carousel__item",indicator:".indicator",prevBtn:".prev-btn",nextBtn:".next-btn",autoRotateBtn:".auto-rotate-btn",playIcon:".play-icon",pauseIcon:".pause-icon",image:"img"}),i=Object.freeze({current:"aria-current",selected:"aria-selected",pressed:"aria-pressed",label:"aria-label",tabindex:"tabindex"}),o=Object.freeze({mobile:768}),r=Object.freeze({transition:500,progressReset:10}),n=Object.freeze({arrowLeft:"ArrowLeft",arrowRight:"ArrowRight",home:"Home",end:"End",space:" "});function c(t,...e){t instanceof HTMLElement&&e.length>0&&t.classList.add(...e)}function l(t,...e){t instanceof HTMLElement&&e.length>0&&t.classList.remove(...e)}function u(t,e,s){t instanceof HTMLElement&&e&&t.setAttribute(e,s)}const h=new Map;function d(t){if(!t||0===t.length)return Promise.resolve([]);const e=[...new Set(t)];return Promise.all(e.map(t=>function(t){return new Promise((e,s)=>{if(!t)return void s(new Error("이미지 소스가 제공되지 않았습니다"));if(h.has(t))return h.get(t);const a=new Image,i=new Promise((e,s)=>{a.onload=()=>{h.delete(t),e(a)},a.onerror=()=>{h.delete(t),s(new Error(`이미지 로드 실패: ${t}`))},a.src=t});h.set(t,i),i.then(e,s)})}(t)))}const m=new Map;const p={prev:{path:"M15 18L9 12L15 6",options:{}},next:{path:"M9 18L15 12L9 6",options:{}},play:{path:"M8 6.5v11l9-5.5z",options:{fill:"currentColor",stroke:"currentColor",strokeWidth:0,strokeLinecap:"round",strokeLinejoin:"round"}},pause:{path:"M7 5.5C7 5.22386 7.22386 5 7.5 5H9.5C9.77614 5 10 5.22386 10 5.5V18.5C10 18.7761 9.77614 19 9.5 19H7.5C7.22386 19 7 18.7761 7 18.5V5.5ZM14 5.5C14 5.22386 14.2239 5 14.5 5H16.5C16.7761 5 17 5.22386 17 5.5V18.5C17 18.7761 16.7761 19 16.5 19H14.5C14.2239 19 14 18.7761 14 18.5V5.5Z",options:{fill:"currentColor",stroke:"none"}}};function g(t,e={}){const s=p[t];if(!s)return console.warn(`PeekCarousel: 아이콘 "${t}"을 찾을 수 없습니다`),"";const a={...s.options,...e};return function(t,e={}){const{width:s=24,height:a=24,viewBox:i="0 0 24 24",fill:o="none",stroke:r="currentColor",strokeWidth:n=2,strokeLinecap:c="round",strokeLinejoin:l="round",className:u=""}=e,h=`${t}-${JSON.stringify(e)}`;if(m.has(h))return m.get(h);const d=`<svg width="${s}" height="${a}" viewBox="${i}" fill="${o}" xmlns="http://www.w3.org/2000/svg" class="${u}"><path d="${t}" stroke="${r}" stroke-width="${n}" stroke-linecap="${c}" stroke-linejoin="${l}"/></svg>`;return m.set(h,d),d}(s.path,a)}function v(t,e,s={}){if(!(t&&t instanceof HTMLElement))return;if(t.querySelector("svg"))return;const a=g(e,s);a&&(t.innerHTML=a)}function f(t,e){return e<=0?0:(t%e+e)%e}class b{constructor(t){this.carousel=t}get currentIndex(){return this.carousel.state.currentIndex}set currentIndex(t){this.carousel.state.currentIndex=f(t,this.carousel.totalItems)}getShortestDistance(t,e){const s=this.carousel.totalItems,a=f(e,s),i=f(t,s),o=(a-i+s)%s,r=(i-a+s)%s;return o<=r?o:-r}isNearby(t,e){return Math.abs(this.getShortestDistance(t,e))<=2}updateAfterNavigation(){this.carousel.animator.updateCarousel(),this.carousel.updateCounter(),this.carousel.options.preloadRange>0&&this.carousel.preloadImages()}rotate(t){this.currentIndex=this.currentIndex+t,this.updateAfterNavigation()}next(){this.rotate(1)}prev(){this.rotate(-1)}goTo(t){const e=f(t,this.carousel.totalItems);e!==this.currentIndex&&(this.currentIndex=e,this.updateAfterNavigation())}navigateIfDifferent(t,e){const s=f(t,this.carousel.totalItems);return s!==this.currentIndex&&(e(s),!0)}handleItemClick(t){this.navigateIfDifferent(t,t=>{const{layoutMode:e}=this.carousel.options;"radial"===e?this.handleRadialItemClick(t):this.handleStackItemClick(t)})}handleRadialItemClick(t){const e=this.getShortestDistance(this.currentIndex,t);if(Math.abs(e)>1){const t=e>0?1:-1;this.rotate(t)}else this.rotate(e)}handleStackItemClick(t){if(this.isNearby(this.currentIndex,t)){const e=this.getShortestDistance(this.currentIndex,t);this.rotate(e)}else this.goTo(t)}handleIndicatorClick(t){this.navigateIfDifferent(t,t=>{const{layoutMode:e}=this.carousel.options;if("radial"===e){const e=this.getShortestDistance(this.currentIndex,t);this.rotate(e)}else this.goTo(t)})}}const y=Object.freeze({center:{x:50,scale:1},peek:{scale:1},hidden:{scale:.85}}),I=Object.freeze({gapPercent:5,additionalMobile:40,additionalDesktop:15,mobileBreakpoint:768}),k=Object.freeze({friction:.92,minVelocity:.05,navigationThreshold:1.5,dampingFactor:.6}),x=[s.itemCenter,s.itemPrev,s.itemNext,s.itemHidden];class C{constructor(t){this.carousel=t,this.momentumAnimation=null,this.isAnimating=!1}normalizeAngleDiff(t){return(t+180)%360-180}round(t,e=2){return Math.round(t*10**e)/10**e}getAdjacentIndices(t){return{prev:f(t-1,this.carousel.totalItems),next:f(t+1,this.carousel.totalItems)}}setCarouselRotation(t){const e=this.round(t,2);this.carousel.container.style.setProperty("--carousel-rotation",`${e}deg`)}setCSSVariables(t,e){for(const[s,a]of Object.entries(e))t.style.setProperty(s,a)}updateRadialRotation(t){const e=-this.carousel.state.angleUnit*t,s=this.carousel.container.style.getPropertyValue("--carousel-rotation");if(!s||"0deg"===s)return void this.setCarouselRotation(e);const a=parseFloat(s),i=a+this.normalizeAngleDiff(e-a);this.setCarouselRotation(i)}updateCarousel(){const{currentIndex:t}=this.carousel.state,{layoutMode:e}=this.carousel.options;"stack"===e||"classic"===e?this.setCarouselRotation(0):"radial"===e&&this.updateRadialRotation(t),this.updateActiveItem()}updateActiveItem(){const{currentIndex:t}=this.carousel.state,{layoutMode:e}=this.carousel.options;this.carousel.ui.updateActiveStates(t),"radial"===e?this.updateRadialPositions(t):"classic"===e?this.updateClassicPositions(t):this.updateStackPositions(t)}updateRadialPositions(t){const{angleUnit:e}=this.carousel.state;for(let t=0;t<this.carousel.items.length;t++){const s=this.carousel.items[t],a=e*t;this.setCSSVariables(s,{"--item-angle":`${this.round(a,2)}deg`,"--item-radius":"400px"})}const{prev:s,next:a}=this.getAdjacentIndices(t);this.carousel.ui.setPeekItems(s,a)}updateStackPositions(t){const{prev:e,next:a}=this.getAdjacentIndices(t);for(let i=0;i<this.carousel.items.length;i++){const o=this.carousel.items[i];o.classList.remove(...x),i===t?o.classList.add(s.itemCenter):i===e?o.classList.add(s.itemPrev):i===a?o.classList.add(s.itemNext):o.classList.add(s.itemHidden)}}calculateClassicSpacing(t){const e=Math.max(300,Math.min(.35*t,500)),s=t<=I.mobileBreakpoint;return e/t*50+I.gapPercent+(s?I.additionalMobile:I.additionalDesktop)}getClassicItemPosition(t,e,s){const{prev:a,next:i}=this.getAdjacentIndices(e);if(t===e)return{x:y.center.x,scale:y.center.scale};if(t===a)return{x:y.center.x-s,scale:y.peek.scale};if(t===i)return{x:y.center.x+s,scale:y.peek.scale};return{x:t-e<0?y.center.x-2*s:y.center.x+2*s,scale:y.hidden.scale}}updateClassicPositions(t){const{prev:e,next:s}=this.getAdjacentIndices(t),a=this.carousel.container.offsetWidth,i=this.calculateClassicSpacing(a);for(let e=0;e<this.carousel.items.length;e++){const s=this.carousel.items[e],{x:a,scale:o}=this.getClassicItemPosition(e,t,i);this.setCSSVariables(s,{"--item-x":`${this.round(a,2)}%`,"--item-scale":String(o)})}this.carousel.ui.setPeekItems(e,s)}startMomentum(t){this.stopMomentum();let e=t;const s=()=>{if(e*=k.friction,Math.abs(e)<k.minVelocity)this.stopMomentum();else{if(Math.abs(e)>k.navigationThreshold){const t=e>0?-1:1;this.carousel.navigator.rotate(t),e*=k.dampingFactor}this.momentumAnimation=requestAnimationFrame(s)}};this.isAnimating=!0,this.momentumAnimation=requestAnimationFrame(s)}stopMomentum(){this.momentumAnimation&&(cancelAnimationFrame(this.momentumAnimation),this.momentumAnimation=null),this.isAnimating=!1}}class A{constructor(t){this.carousel=t,this.interval=null,this.isActive=!1}setActiveState(t){this.isActive=t,this.carousel.ui.updateAutoRotateButton(t)}toggle(){this.isActive?this.stop():this.start()}start(){if(this.isActive)return;this.setActiveState(!0);const t=this.carousel.options.autoRotateInterval;this.interval=setInterval(()=>{this.carousel.navigator.next()},t)}stop(){this.isActive&&(this.setActiveState(!1),this.interval&&(clearInterval(this.interval),this.interval=null))}destroy(){this.stop(),this.carousel=null}}const w=Object.freeze({threshold:50,timeout:150,cooldown:100}),R=Object.freeze({touchThreshold:15,mouseThreshold:10,velocityThreshold:.5});class M{constructor(t){this.carousel=t,this.boundHandlers=new Map,this.touch={startX:0,endX:0},this.drag={active:!1,startX:0,currentX:0,lastX:0,lastTime:0,velocity:0},this.wheel={isScrolling:!1,scrollTimeout:null,lastWheelTime:0,accumulatedDelta:0}}init(){this.initNavigationButtons(),this.initKeyboard(),this.initWheel(),this.initItemClick(),this.initIndicatorClick(),this.initTouch(),this.initMouse(),this.initResize()}stopAutoRotateAndNavigate(t){this.completeCurrentIndicator(),this.carousel.autoRotate.stop(),t()}completeCurrentIndicator(){const t=this.carousel.indicators[this.carousel.state.currentIndex];t&&t.classList.contains("peek-carousel__indicator--active")&&t.classList.add("peek-carousel__indicator--completed")}resetDragState(t){this.carousel.ui.removeDraggingClass(t),this.carousel.ui.clearDragTransform()}updateDraggingClass(t,e,s){t>s?this.carousel.ui.addDraggingClass(e,"right"):t<-s&&this.carousel.ui.addDraggingClass(e,"left")}initDragState(t){this.drag.active=!0,this.drag.startX=t,this.drag.currentX=t,this.drag.lastX=t,this.drag.lastTime=Date.now(),this.drag.velocity=0}resetMouseCursor(){this.carousel.elements.carousel.style.cursor="grab"}calculateWheelDelta(t){return Math.abs(t.deltaX)>Math.abs(t.deltaY)?-t.deltaX:t.deltaY}resetWheelState(){this.wheel.isScrolling=!1,this.wheel.accumulatedDelta=0}initNavigationButtons(){const{prevBtn:t,nextBtn:e,autoRotateBtn:s}=this.carousel.elements;t&&this.addHandler(t,"click",()=>{this.stopAutoRotateAndNavigate(()=>this.carousel.navigator.prev())}),e&&this.addHandler(e,"click",()=>{this.stopAutoRotateAndNavigate(()=>this.carousel.navigator.next())}),s&&this.addHandler(s,"click",()=>{this.carousel.autoRotate.toggle()})}initKeyboard(){if(!this.carousel.options.enableKeyboard)return;this.addHandler(document,"keydown",t=>{const{navigator:e,autoRotate:s,totalItems:a}=this.carousel;switch(t.key){case n.arrowLeft:s.stop(),e.prev();break;case n.arrowRight:s.stop(),e.next();break;case n.home:t.preventDefault(),s.stop(),e.goTo(0);break;case n.end:t.preventDefault(),s.stop(),e.goTo(a-1);break;case n.space:t.preventDefault(),s.toggle();break;default:const i=parseInt(t.key);i>=1&&i<=a&&(t.preventDefault(),s.stop(),e.goTo(i-1))}})}initWheel(){if(!this.carousel.options.enableWheel)return;this.addHandler(this.carousel.elements.carousel,"wheel",t=>{const e=Math.abs(t.deltaX),s=Math.abs(t.deltaY);if(e<1&&s<1)return;if(e===s)return;t.preventDefault();const a=Date.now();if(!(a-this.wheel.lastWheelTime<w.cooldown)){if(this.wheel.isScrolling||(this.wheel.isScrolling=!0,this.wheel.accumulatedDelta=0,this.carousel.autoRotate.stop(),this.carousel.animator.stopMomentum()),this.wheel.accumulatedDelta+=this.calculateWheelDelta(t),Math.abs(this.wheel.accumulatedDelta)>=w.threshold){const t=this.wheel.accumulatedDelta>0?1:-1;this.carousel.navigator.rotate(t),this.wheel.accumulatedDelta=0,this.wheel.lastWheelTime=a}clearTimeout(this.wheel.scrollTimeout),this.wheel.scrollTimeout=setTimeout(()=>{this.resetWheelState()},w.timeout)}},{passive:!1})}initItemClick(){const{items:t}=this.carousel;for(let e=0;e<t.length;e++)this.addHandler(t[e],"click",()=>{this.carousel.autoRotate.stop(),this.carousel.navigator.handleItemClick(e)})}initIndicatorClick(){const{indicators:t}=this.carousel;for(let e=0;e<t.length;e++)this.addHandler(t[e],"click",()=>{this.carousel.autoRotate.stop(),this.carousel.navigator.handleIndicatorClick(e)})}initTouch(){this.carousel.options.enableTouch&&(this.addHandler(this.carousel.elements.carousel,"touchstart",t=>{this.touch.startX=t.changedTouches[0].screenX}),this.addHandler(this.carousel.elements.carousel,"touchmove",t=>{const e=t.changedTouches[0].screenX-this.touch.startX,{currentIndex:s}=this.carousel.state;this.carousel.ui.updateDragTransform(e),this.updateDraggingClass(e,s,R.touchThreshold)}),this.addHandler(this.carousel.elements.carousel,"touchend",t=>{this.touch.endX=t.changedTouches[0].screenX;const e=this.touch.endX-this.touch.startX,{swipeThreshold:s}=this.carousel.options,{currentIndex:a}=this.carousel.state;this.resetDragState(a),e<-s?(this.carousel.autoRotate.stop(),this.carousel.navigator.next()):e>s&&(this.carousel.autoRotate.stop(),this.carousel.navigator.prev())}))}initMouse(){this.carousel.options.enableMouse&&(this.addHandler(this.carousel.elements.carousel,"mousedown",t=>{window.innerWidth<=768||(this.initDragState(t.clientX),this.carousel.autoRotate.stop(),this.carousel.animator.stopMomentum(),this.carousel.elements.carousel.style.cursor="grabbing",t.preventDefault())}),this.addHandler(document,"mousemove",t=>{if(!this.drag.active)return;const e=Date.now(),s=e-this.drag.lastTime,a=t.clientX-this.drag.lastX;s>0&&(this.drag.velocity=a/s),this.drag.currentX=t.clientX,this.drag.lastX=t.clientX,this.drag.lastTime=e;const i=this.drag.currentX-this.drag.startX,{currentIndex:o}=this.carousel.state;if(this.carousel.ui.updateDragTransform(i),this.updateDraggingClass(i,o,R.mouseThreshold),Math.abs(i)>this.carousel.options.dragThreshold){const t=i>0?-1:1;this.carousel.navigator.rotate(t),this.drag.startX=this.drag.currentX,this.resetDragState(o)}}),this.addHandler(document,"mouseup",()=>{if(!this.drag.active)return;this.drag.active=!1,this.resetMouseCursor();const{currentIndex:t}=this.carousel.state;this.resetDragState(t),Math.abs(this.drag.velocity)>R.velocityThreshold&&this.carousel.animator.startMomentum(this.drag.velocity)}),this.addHandler(this.carousel.elements.carousel,"mouseleave",()=>{if(this.drag.active){this.drag.active=!1,this.resetMouseCursor();const{currentIndex:t}=this.carousel.state;this.resetDragState(t)}}),window.innerWidth>o.mobile&&this.resetMouseCursor())}initResize(){let t;this.addHandler(window,"resize",()=>{clearTimeout(t),t=setTimeout(()=>{this.carousel.animator.updateCarousel()},100)})}addHandler(t,e,s,a){t.addEventListener(e,s,a);const i=`${e}-${Date.now()}-${Math.random()}`;this.boundHandlers.set(i,{element:t,event:e,handler:s,options:a})}destroy(){this.wheel.scrollTimeout&&(clearTimeout(this.wheel.scrollTimeout),this.wheel.scrollTimeout=null);for(const{element:t,event:e,handler:s,options:a}of this.boundHandlers.values())t.removeEventListener(e,s,a);this.boundHandlers.clear(),this.carousel=null}}const S=Object.freeze({stack:{maxDrag:200,offsetMultiplier:100,rotationMultiplier:3},radial:{rotationSensitivity:.2},classic:{dragSensitivity:.5}});class T{constructor(t){this.carousel=t}updateActiveStates(t){for(let t=0;t<this.carousel.items.length;t++){const e=this.carousel.items[t];l(e,s.itemActive,s.itemPrev,s.itemNext),e.removeAttribute(i.current)}for(let t=0;t<this.carousel.indicators.length;t++){const e=this.carousel.indicators[t];l(e,s.indicatorActive,s.indicatorProgress),u(e,i.selected,"false"),u(e,i.tabindex,"-1")}const e=this.carousel.items[t],a=this.carousel.indicators[t];e&&(c(e,s.itemActive),u(e,i.current,"true")),a&&(l(a,s.indicatorCompleted),c(a,s.indicatorActive),u(a,i.selected,"true"),u(a,i.tabindex,"0"),this.carousel.autoRotate.isActive&&this.updateIndicatorProgress(a))}updateIndicatorProgress(t){var e,a,i;e=t,a="--progress-duration",i=`${this.carousel.options.autoRotateInterval}ms`,e instanceof HTMLElement&&a&&e.style.setProperty(a,i),setTimeout(()=>{t&&c(t,s.indicatorProgress)},r.progressReset)}clearPeekItems(){for(let t=0;t<this.carousel.items.length;t++){l(this.carousel.items[t],s.itemPrev,s.itemNext)}}setPeekItems(t,e){const a=this.carousel.items[t],i=this.carousel.items[e];a&&c(a,s.itemPrev),i&&c(i,s.itemNext)}updateAutoRotateButton(t){const{autoRotateBtn:e}=this.carousel.elements;e&&(t?(c(e,s.btnActive),u(e,i.pressed,"true")):(l(e,s.btnActive),u(e,i.pressed,"false")))}addDraggingClass(t,e){const a=this.carousel.items[t];if(!a)return;const i=s.itemDraggingLeft,o=s.itemDraggingRight;l(a,i,o),"left"===e?c(a,i):"right"===e&&c(a,o)}removeDraggingClass(t){const e=this.carousel.items[t];e&&l(e,s.itemDraggingLeft,s.itemDraggingRight)}round(t,e=2){return Math.round(t*10**e)/10**e}applyEasing(t){return t*(2-Math.abs(t))}updateDragTransform(t){const{layoutMode:e}=this.carousel.options;if("stack"===e){const e=S.stack,s=Math.max(-e.maxDrag,Math.min(e.maxDrag,t))/e.maxDrag,a=this.applyEasing(s),i=this.round(a*e.offsetMultiplier),o=this.round(a*e.rotationMultiplier);this.carousel.container.style.setProperty("--drag-offset",`${i}px`),this.carousel.container.style.setProperty("--drag-rotation",`${o}deg`)}else if("radial"===e){const e=S.radial,s=this.round(t*e.rotationSensitivity);this.carousel.container.style.setProperty("--drag-rotation-y",`${s}deg`)}else if("classic"===e){const e=S.classic,s=this.round(t*e.dragSensitivity);this.carousel.container.style.setProperty("--drag-offset",`${s}px`)}}clearDragTransform(){this.carousel.container.style.setProperty("--drag-offset","0px"),this.carousel.container.style.setProperty("--drag-rotation","0deg"),this.carousel.container.style.setProperty("--drag-rotation-y","0deg")}destroy(){}}class D{constructor(s,a={}){if(this.container=function(t){return"string"==typeof t?document.querySelector(t):t instanceof HTMLElement?t:null}(s),!this.container)throw new Error(`PeekCarousel: 셀렉터 "${s}"에 해당하는 컨테이너를 찾을 수 없습니다`);if(this.options=function(s){const a={...e,...s};return a.startIndex<0&&(console.warn("PeekCarousel: startIndex는 0 이상이어야 합니다. 기본값 1 사용"),a.startIndex=1),Object.values(t).includes(a.layoutMode)||(console.warn(`PeekCarousel: 유효하지 않은 layoutMode "${a.layoutMode}". 기본값 "stack" 사용`),a.layoutMode=t.STACK),a.autoRotateInterval<100&&(console.warn("PeekCarousel: autoRotateInterval은 100ms 이상이어야 합니다. 기본값 2500 사용"),a.autoRotateInterval=2500),a.preloadRange<0&&(console.warn("PeekCarousel: preloadRange는 0 이상이어야 합니다. 기본값 2 사용"),a.preloadRange=2),a}(a),this.initElements(),0===this.items.length)throw new Error("PeekCarousel: 캐러셀 아이템을 찾을 수 없습니다");this.state={currentIndex:this.options.startIndex,angleUnit:360/this.totalItems},this.initModules(),this.init()}initElements(){this.elements={carousel:this.container.querySelector(a.carousel),prevBtn:null,nextBtn:null,autoRotateBtn:null,controls:null,nav:null},this.items=function(t,e=document){return"string"!=typeof t?[]:Array.from(e.querySelectorAll(t))}(a.item,this.container),this.totalItems=this.items.length,this.indicators=[]}initModules(){this.navigator=new b(this),this.animator=new C(this),this.autoRotate=new A(this),this.eventHandler=new M(this),this.ui=new T(this)}init(){this.updateLayoutClass(),this.createNavigation(),this.createControls(),this.injectIcons(),this.createCounter(),this.setImageLoadingAttributes(),this.initCSSVariables(),this.eventHandler.init(),this.animator.updateCarousel(),this.options.autoRotate&&this.autoRotate.start(),this.options.preloadRange>0&&this.preloadImages()}initCSSVariables(){this.container.style.setProperty("--carousel-rotation","0deg"),this.container.style.setProperty("--drag-offset","0px"),this.container.style.setProperty("--drag-rotation","0deg"),this.container.style.setProperty("--drag-rotation-y","0deg")}createNavigation(){if(!this.options.showNavigation)return;const t=this.container.querySelector(`.${s.nav}`);if(t)return this.elements.nav=t,this.elements.prevBtn=t.querySelector(a.prevBtn),void(this.elements.nextBtn=t.querySelector(a.nextBtn));const e=document.createElement("div");e.className=s.nav;const i=document.createElement("button");i.className=`${s.navBtn} ${s.btn} ${s.prevBtn}`,i.setAttribute("aria-label","Previous");const o=document.createElement("button");o.className=`${s.navBtn} ${s.btn} ${s.nextBtn}`,o.setAttribute("aria-label","Next"),e.appendChild(i),e.appendChild(o),this.container.appendChild(e),this.elements.nav=e,this.elements.prevBtn=i,this.elements.nextBtn=o}createControls(){if(!this.options.showIndicators&&!this.options.showAutoRotateButton)return;const t=this.container.querySelector(`.${s.controls}`);if(t){this.elements.controls=t;const e=t.querySelector(`.${s.indicators}`);return e&&this.options.showIndicators&&(e.innerHTML="",this.createIndicators(e)),void(this.elements.autoRotateBtn=t.querySelector(a.autoRotateBtn))}const e=document.createElement("div");if(e.className=s.controls,this.options.showIndicators){const t=document.createElement("div");t.className=s.indicators,this.createIndicators(t),e.appendChild(t)}if(this.options.showAutoRotateButton){const t=document.createElement("button");t.className=`${s.autoRotateBtn} ${s.btn} ${s.btnAutoRotate}`,t.setAttribute("aria-label","Toggle auto-rotate"),t.setAttribute("aria-pressed","false"),e.appendChild(t),this.elements.autoRotateBtn=t}this.container.appendChild(e),this.elements.controls=e}createIndicators(t){this.indicators=[];for(let e=0;e<this.totalItems;e++){const a=document.createElement("button"),i=e===this.state.currentIndex;a.className=s.indicator,a.classList.add(s.indicatorPeek),a.setAttribute("role","tab"),a.setAttribute("aria-label",`Image ${e+1}`),a.setAttribute("aria-selected",i?"true":"false"),a.setAttribute("tabindex",i?"0":"-1"),i&&a.classList.add(s.indicatorActive),t.appendChild(a),this.indicators.push(a)}}injectIcons(){const{prevBtn:t,nextBtn:e,autoRotateBtn:s}=this.elements;t&&v(t,"prev"),e&&v(e,"next"),s&&function(t){if(!(t&&t instanceof HTMLElement))return;if(t.querySelector("svg"))return;const e=g("play",{className:"play-icon"}),s=g("pause",{className:"pause-icon"});e&&s&&(t.innerHTML=e+s)}(s)}createCounter(){if(!this.options.showCounter)return;const t=this.container.querySelector(`.${s.counter}`);if(t)return this.counterElement=t,void this.updateCounter();const e=document.createElement("div");e.className=s.counter,e.setAttribute("aria-live","polite"),e.setAttribute("aria-atomic","true"),e.innerHTML=`\n <span class="${s.counterCurrent}">${this.state.currentIndex+1}</span>\n <span class="${s.counterSeparator}">/</span>\n <span class="${s.counterTotal}">${this.totalItems}</span>\n `,this.container.appendChild(e),this.counterElement=e}updateCounter(){if(!this.counterElement)return;const t=this.counterElement.querySelector(`.${s.counterCurrent}`);t&&(t.textContent=this.state.currentIndex+1)}setImageLoadingAttributes(){const{startIndex:t}=this.options,e=this.options.preloadRange||1;for(let a=0;a<this.items.length;a++){const i=this.items[a].querySelector(`.${s.image}`);if(!i||i.hasAttribute("loading"))continue;const o=Math.abs(a-t)<=e;i.setAttribute("loading",o?"eager":"lazy")}}updateLayoutClass(){const t=this.currentLayoutMode,e=this.options.layoutMode;t&&t!==e&&this.container.classList.remove(`peek-carousel--${t}`),this.container.classList.add(`peek-carousel--${e}`),this.currentLayoutMode=e}preloadImages(){!function(t,e,s){if(!t||0===t.length||s<0)return;const a=t.length,i=new Set;for(let o=1;o<=s;o++){const s=(e-o+a)%a,r=(e+o)%a,n=t[s]?.querySelector("img"),c=t[r]?.querySelector("img");n&&n.src&&!n.complete&&i.add(n.src),c&&c.src&&!c.complete&&i.add(c.src)}i.size>0&&d([...i]).catch(t=>{console.warn("일부 이미지 프리로드 실패:",t)})}(this.items,this.state.currentIndex,this.options.preloadRange)}next(){this.navigator.next()}prev(){this.navigator.prev()}goTo(t){this.navigator.goTo(t)}startAutoRotate(){this.autoRotate.start()}stopAutoRotate(){this.autoRotate.stop()}toggleAutoRotate(){this.autoRotate.toggle()}destroy(){this.autoRotate.destroy(),this.animator.stopMomentum(),this.eventHandler.destroy(),this.ui.destroy()}get currentIndex(){return this.state.currentIndex}get isAutoRotating(){return this.autoRotate.isActive}}export{D as default};
|
|
8
|
-
//# sourceMappingURL=peek-carousel.esm.min.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"peek-carousel.esm.min.js","sources":["../src/core/config.js","../src/core/constants.js","../src/utils/dom.js","../src/utils/preloader.js","../src/utils/icons.js","../src/utils/helpers.js","../src/modules/Navigator.js","../src/modules/Animator.js","../src/modules/AutoRotate.js","../src/modules/EventHandler.js","../src/modules/UIManager.js","../src/core/PeekCarousel.js"],"sourcesContent":["export const LAYOUT_MODES = Object.freeze({\n STACK: 'stack',\n RADIAL: 'radial',\n CLASSIC: 'classic',\n});\n\nexport const DEFAULT_OPTIONS = Object.freeze({\n startIndex: 1,\n layoutMode: LAYOUT_MODES.STACK,\n autoRotate: false,\n autoRotateInterval: 2500,\n preloadRange: 2,\n swipeThreshold: 50,\n dragThreshold: 80,\n enableKeyboard: true,\n enableWheel: true,\n enableTouch: true,\n enableMouse: true,\n showNavigation: true,\n showCounter: true,\n showIndicators: true,\n showAutoRotateButton: true,\n});\n\nexport function validateOptions(options) {\n const validated = { ...DEFAULT_OPTIONS, ...options };\n\n if (validated.startIndex < 0) {\n console.warn('PeekCarousel: startIndex는 0 이상이어야 합니다. 기본값 1 사용');\n validated.startIndex = 1;\n }\n\n if (!Object.values(LAYOUT_MODES).includes(validated.layoutMode)) {\n console.warn(`PeekCarousel: 유효하지 않은 layoutMode \"${validated.layoutMode}\". 기본값 \"stack\" 사용`);\n validated.layoutMode = LAYOUT_MODES.STACK;\n }\n\n if (validated.autoRotateInterval < 100) {\n console.warn('PeekCarousel: autoRotateInterval은 100ms 이상이어야 합니다. 기본값 2500 사용');\n validated.autoRotateInterval = 2500;\n }\n\n if (validated.preloadRange < 0) {\n console.warn('PeekCarousel: preloadRange는 0 이상이어야 합니다. 기본값 2 사용');\n validated.preloadRange = 2;\n }\n\n return validated;\n}\n","export const CLASS_NAMES = Object.freeze({\n carousel: 'peek-carousel',\n track: 'peek-carousel__track',\n item: 'peek-carousel__item',\n itemActive: 'peek-carousel__item--active',\n itemPrev: 'peek-carousel__item--prev',\n itemNext: 'peek-carousel__item--next',\n itemCenter: 'peek-carousel__item--center',\n itemHidden: 'peek-carousel__item--hidden',\n itemDraggingLeft: 'peek-carousel__item--dragging-left',\n itemDraggingRight: 'peek-carousel__item--dragging-right',\n figure: 'peek-carousel__figure',\n image: 'peek-carousel__image',\n caption: 'peek-carousel__caption',\n nav: 'peek-carousel__nav',\n navBtn: 'nav-btn',\n btn: 'peek-carousel__btn',\n prevBtn: 'prev-btn',\n nextBtn: 'next-btn',\n autoRotateBtn: 'auto-rotate-btn',\n btnAutoRotate: 'peek-carousel__btn--auto-rotate',\n btnActive: 'peek-carousel__btn--active',\n controls: 'peek-carousel__controls',\n indicators: 'peek-carousel__indicators',\n indicator: 'indicator',\n indicatorPeek: 'peek-carousel__indicator',\n indicatorActive: 'peek-carousel__indicator--active',\n indicatorProgress: 'peek-carousel__indicator--progress',\n indicatorCompleted: 'peek-carousel__indicator--completed',\n counter: 'peek-carousel__counter',\n counterCurrent: 'peek-carousel__counter-current',\n counterSeparator: 'peek-carousel__counter-separator',\n counterTotal: 'peek-carousel__counter-total',\n playIcon: 'play-icon',\n pauseIcon: 'pause-icon',\n});\n\nexport const SELECTORS = Object.freeze({\n carousel: '.peek-carousel__track',\n item: '.peek-carousel__item',\n indicator: '.indicator',\n prevBtn: '.prev-btn',\n nextBtn: '.next-btn',\n autoRotateBtn: '.auto-rotate-btn',\n playIcon: '.play-icon',\n pauseIcon: '.pause-icon',\n image: 'img',\n});\n\nexport const ARIA = Object.freeze({\n current: 'aria-current',\n selected: 'aria-selected',\n pressed: 'aria-pressed',\n label: 'aria-label',\n tabindex: 'tabindex',\n});\n\nexport const BREAKPOINTS = Object.freeze({\n mobile: 768, // [개발참고] px\n});\n\nexport const DURATIONS = Object.freeze({\n transition: 500, // [개발참고] ms\n progressReset: 10, // [개발참고] ms\n});\n\nexport const KEYS = Object.freeze({\n arrowLeft: 'ArrowLeft',\n arrowRight: 'ArrowRight',\n home: 'Home',\n end: 'End',\n space: ' ',\n});\n","export function getElement(selector) {\n if (typeof selector === 'string') {\n return document.querySelector(selector);\n }\n return selector instanceof HTMLElement ? selector : null;\n}\n\nexport function getElements(selector, parent = document) {\n if (typeof selector !== 'string') return [];\n return Array.from(parent.querySelectorAll(selector));\n}\n\nexport function addClass(element, ...classes) {\n if (element instanceof HTMLElement && classes.length > 0) {\n element.classList.add(...classes);\n }\n}\n\nexport function removeClass(element, ...classes) {\n if (element instanceof HTMLElement && classes.length > 0) {\n element.classList.remove(...classes);\n }\n}\n\nexport function toggleClass(element, className, force) {\n if (element instanceof HTMLElement && className) {\n element.classList.toggle(className, force);\n }\n}\n\nexport function setStyle(element, property, value) {\n if (element instanceof HTMLElement && property) {\n element.style[property] = value;\n }\n}\n\nexport function setCSSVar(element, property, value) {\n if (element instanceof HTMLElement && property) {\n element.style.setProperty(property, value);\n }\n}\n\nexport function setAttribute(element, name, value) {\n if (element instanceof HTMLElement && name) {\n element.setAttribute(name, value);\n }\n}\n\nexport function removeAttribute(element, name) {\n if (element instanceof HTMLElement && name) {\n element.removeAttribute(name);\n }\n}\n","const loadingCache = new Map();\n\nexport function preloadImage(src) {\n return new Promise((resolve, reject) => {\n if (!src) {\n reject(new Error('이미지 소스가 제공되지 않았습니다'));\n return;\n }\n\n if (loadingCache.has(src)) {\n return loadingCache.get(src);\n }\n\n const img = new Image();\n const promise = new Promise((res, rej) => {\n img.onload = () => {\n loadingCache.delete(src);\n res(img);\n };\n img.onerror = () => {\n loadingCache.delete(src);\n rej(new Error(`이미지 로드 실패: ${src}`));\n };\n img.src = src;\n });\n\n loadingCache.set(src, promise);\n promise.then(resolve, reject);\n });\n}\n\nexport function preloadImages(sources) {\n if (!sources || sources.length === 0) {\n return Promise.resolve([]);\n }\n\n const uniqueSources = [...new Set(sources)];\n return Promise.all(uniqueSources.map(src => preloadImage(src)));\n}\n\nexport function preloadImagesInRange(items, currentIndex, range) {\n if (!items || items.length === 0 || range < 0) {\n return;\n }\n\n const totalItems = items.length;\n const imagesToPreload = new Set();\n\n for (let distance = 1; distance <= range; distance++) {\n const prevIndex = (currentIndex - distance + totalItems) % totalItems;\n const nextIndex = (currentIndex + distance) % totalItems;\n\n const prevImg = items[prevIndex]?.querySelector('img');\n const nextImg = items[nextIndex]?.querySelector('img');\n\n if (prevImg && prevImg.src && !prevImg.complete) {\n imagesToPreload.add(prevImg.src);\n }\n if (nextImg && nextImg.src && !nextImg.complete) {\n imagesToPreload.add(nextImg.src);\n }\n }\n\n if (imagesToPreload.size > 0) {\n preloadImages([...imagesToPreload]).catch(err => {\n console.warn('일부 이미지 프리로드 실패:', err);\n });\n }\n}\n","const iconCache = new Map();\n\nfunction createSVGIcon(path, options = {}) {\n const {\n width = 24,\n height = 24,\n viewBox = '0 0 24 24',\n fill = 'none',\n stroke = 'currentColor',\n strokeWidth = 2,\n strokeLinecap = 'round',\n strokeLinejoin = 'round',\n className = '',\n } = options;\n\n const cacheKey = `${path}-${JSON.stringify(options)}`;\n if (iconCache.has(cacheKey)) {\n return iconCache.get(cacheKey);\n }\n\n const svg = `<svg width=\"${width}\" height=\"${height}\" viewBox=\"${viewBox}\" fill=\"${fill}\" xmlns=\"http://www.w3.org/2000/svg\" class=\"${className}\"><path d=\"${path}\" stroke=\"${stroke}\" stroke-width=\"${strokeWidth}\" stroke-linecap=\"${strokeLinecap}\" stroke-linejoin=\"${strokeLinejoin}\"/></svg>`;\n\n iconCache.set(cacheKey, svg);\n return svg;\n}\n\nexport const ICONS = {\n prev: {\n path: 'M15 18L9 12L15 6',\n options: {},\n },\n next: {\n path: 'M9 18L15 12L9 6',\n options: {},\n },\n play: {\n path: 'M8 6.5v11l9-5.5z',\n options: {\n fill: 'currentColor',\n stroke: 'currentColor',\n strokeWidth: 0,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n },\n },\n pause: {\n path: 'M7 5.5C7 5.22386 7.22386 5 7.5 5H9.5C9.77614 5 10 5.22386 10 5.5V18.5C10 18.7761 9.77614 19 9.5 19H7.5C7.22386 19 7 18.7761 7 18.5V5.5ZM14 5.5C14 5.22386 14.2239 5 14.5 5H16.5C16.7761 5 17 5.22386 17 5.5V18.5C17 18.7761 16.7761 19 16.5 19H14.5C14.2239 19 14 18.7761 14 18.5V5.5Z',\n options: {\n fill: 'currentColor',\n stroke: 'none',\n },\n },\n};\n\nexport function getIcon(iconName, customOptions = {}) {\n const icon = ICONS[iconName];\n if (!icon) {\n console.warn(`PeekCarousel: 아이콘 \"${iconName}\"을 찾을 수 없습니다`);\n return '';\n }\n\n const options = { ...icon.options, ...customOptions };\n return createSVGIcon(icon.path, options);\n}\n\nexport function injectIcon(button, iconName, options = {}) {\n if (!button || !(button instanceof HTMLElement)) return;\n\n if (button.querySelector('svg')) {\n return;\n }\n\n const iconHTML = getIcon(iconName, options);\n if (iconHTML) {\n button.innerHTML = iconHTML;\n }\n}\n\nexport function injectAutoRotateIcons(button) {\n if (!button || !(button instanceof HTMLElement)) return;\n\n if (button.querySelector('svg')) {\n return;\n }\n\n const playHTML = getIcon('play', { className: 'play-icon' });\n const pauseHTML = getIcon('pause', { className: 'pause-icon' });\n\n if (playHTML && pauseHTML) {\n button.innerHTML = playHTML + pauseHTML;\n }\n}\n","export function isMobile() {\n return window.innerWidth <= 768;\n}\n\nexport function normalizeIndex(index, length) {\n if (length <= 0) return 0;\n return ((index % length) + length) % length;\n}\n\nexport function clamp(value, min, max) {\n if (min > max) [min, max] = [max, min];\n return Math.min(Math.max(value, min), max);\n}\n\nexport function debounce(func, wait) {\n if (typeof func !== 'function') {\n throw new TypeError('첫 번째 인자는 함수여야 합니다');\n }\n\n let timeout;\n return function executedFunction(...args) {\n const context = this;\n const later = () => {\n clearTimeout(timeout);\n func.apply(context, args);\n };\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n}\n\nexport function throttle(func, limit) {\n if (typeof func !== 'function') {\n throw new TypeError('첫 번째 인자는 함수여야 합니다');\n }\n\n let inThrottle;\n return function executedFunction(...args) {\n const context = this;\n if (!inThrottle) {\n func.apply(context, args);\n inThrottle = true;\n setTimeout(() => (inThrottle = false), limit);\n }\n };\n}\n","import { normalizeIndex } from '../utils/helpers.js';\n\nconst PROXIMITY_THRESHOLD = 2;\n\nexport class Navigator {\n constructor(carousel) {\n this.carousel = carousel;\n }\n\n get currentIndex() {\n return this.carousel.state.currentIndex;\n }\n\n set currentIndex(value) {\n this.carousel.state.currentIndex = normalizeIndex(value, this.carousel.totalItems);\n }\n\n getShortestDistance(from, to) {\n const total = this.carousel.totalItems;\n const normalizedTo = normalizeIndex(to, total);\n const normalizedFrom = normalizeIndex(from, total);\n\n const forwardDist = (normalizedTo - normalizedFrom + total) % total;\n const backwardDist = (normalizedFrom - normalizedTo + total) % total;\n\n return forwardDist <= backwardDist ? forwardDist : -backwardDist;\n }\n\n isNearby(from, to) {\n const distance = Math.abs(this.getShortestDistance(from, to));\n return distance <= PROXIMITY_THRESHOLD;\n }\n\n updateAfterNavigation() {\n this.carousel.animator.updateCarousel();\n this.carousel.updateCounter();\n\n if (this.carousel.options.preloadRange > 0) {\n this.carousel.preloadImages();\n }\n }\n\n rotate(direction) {\n this.currentIndex = this.currentIndex + direction;\n this.updateAfterNavigation();\n }\n\n next() {\n this.rotate(1);\n }\n\n prev() {\n this.rotate(-1);\n }\n\n goTo(index) {\n const normalizedIndex = normalizeIndex(index, this.carousel.totalItems);\n if (normalizedIndex === this.currentIndex) return;\n\n this.currentIndex = normalizedIndex;\n this.updateAfterNavigation();\n }\n\n navigateIfDifferent(targetIndex, callback) {\n const normalizedIndex = normalizeIndex(targetIndex, this.carousel.totalItems);\n if (normalizedIndex === this.currentIndex) return false;\n\n callback(normalizedIndex);\n return true;\n }\n\n handleItemClick(index) {\n this.navigateIfDifferent(index, (normalizedIndex) => {\n const { layoutMode } = this.carousel.options;\n\n if (layoutMode === 'radial') {\n this.handleRadialItemClick(normalizedIndex);\n } else {\n this.handleStackItemClick(normalizedIndex);\n }\n });\n }\n\n handleRadialItemClick(normalizedIndex) {\n const shortestDist = this.getShortestDistance(this.currentIndex, normalizedIndex);\n\n if (Math.abs(shortestDist) > 1) {\n const direction = shortestDist > 0 ? 1 : -1;\n this.rotate(direction);\n } else {\n this.rotate(shortestDist);\n }\n }\n\n handleStackItemClick(normalizedIndex) {\n if (this.isNearby(this.currentIndex, normalizedIndex)) {\n const shortestDist = this.getShortestDistance(this.currentIndex, normalizedIndex);\n this.rotate(shortestDist);\n } else {\n this.goTo(normalizedIndex);\n }\n }\n\n handleIndicatorClick(index) {\n this.navigateIfDifferent(index, (normalizedIndex) => {\n const { layoutMode } = this.carousel.options;\n\n if (layoutMode === 'radial') {\n const shortestDist = this.getShortestDistance(this.currentIndex, normalizedIndex);\n this.rotate(shortestDist);\n } else {\n this.goTo(normalizedIndex);\n }\n });\n }\n}\n","import { normalizeIndex } from '../utils/helpers.js';\nimport { CLASS_NAMES } from '../core/constants.js';\n\nconst RADIAL_RADIUS = 400;\n\nconst CLASSIC_POSITIONS = Object.freeze({\n center: { x: 50, scale: 1 },\n peek: { scale: 1 },\n hidden: { scale: 0.85 },\n});\n\nconst CLASSIC_SPACING = Object.freeze({\n gapPercent: 5,\n additionalMobile: 40,\n additionalDesktop: 15,\n mobileBreakpoint: 768,\n});\n\nconst MOMENTUM_CONFIG = Object.freeze({\n friction: 0.92,\n minVelocity: 0.05,\n navigationThreshold: 1.5,\n dampingFactor: 0.6,\n});\n\nconst STACK_POSITION_CLASSES = [\n CLASS_NAMES.itemCenter,\n CLASS_NAMES.itemPrev,\n CLASS_NAMES.itemNext,\n CLASS_NAMES.itemHidden,\n];\n\nexport class Animator {\n constructor(carousel) {\n this.carousel = carousel;\n this.momentumAnimation = null;\n this.isAnimating = false;\n }\n\n normalizeAngleDiff(diff) {\n return ((diff + 180) % 360) - 180;\n }\n\n round(value, decimals = 2) {\n return Math.round(value * 10 ** decimals) / 10 ** decimals;\n }\n\n getAdjacentIndices(currentIndex) {\n return {\n prev: normalizeIndex(currentIndex - 1, this.carousel.totalItems),\n next: normalizeIndex(currentIndex + 1, this.carousel.totalItems),\n };\n }\n\n setCarouselRotation(angle) {\n const rounded = this.round(angle, 2);\n this.carousel.container.style.setProperty('--carousel-rotation', `${rounded}deg`);\n }\n\n setCSSVariables(element, variables) {\n for (const [key, value] of Object.entries(variables)) {\n element.style.setProperty(key, value);\n }\n }\n\n updateRadialRotation(currentIndex) {\n const targetAngle = -this.carousel.state.angleUnit * currentIndex;\n const currentRotation = this.carousel.container.style.getPropertyValue('--carousel-rotation');\n\n if (!currentRotation || currentRotation === '0deg') {\n this.setCarouselRotation(targetAngle);\n return;\n }\n\n // [개발참고] 최단 경로 계산: -180 ~ 180 범위로 정규화\n const currentAngle = parseFloat(currentRotation);\n const diff = this.normalizeAngleDiff(targetAngle - currentAngle);\n const finalAngle = currentAngle + diff;\n this.setCarouselRotation(finalAngle);\n }\n\n updateCarousel() {\n const { currentIndex } = this.carousel.state;\n const { layoutMode } = this.carousel.options;\n\n if (layoutMode === 'stack' || layoutMode === 'classic') {\n this.setCarouselRotation(0);\n } else if (layoutMode === 'radial') {\n this.updateRadialRotation(currentIndex);\n }\n\n this.updateActiveItem();\n }\n\n updateActiveItem() {\n const { currentIndex } = this.carousel.state;\n const { layoutMode } = this.carousel.options;\n\n this.carousel.ui.updateActiveStates(currentIndex);\n\n if (layoutMode === 'radial') {\n this.updateRadialPositions(currentIndex);\n } else if (layoutMode === 'classic') {\n this.updateClassicPositions(currentIndex);\n } else {\n this.updateStackPositions(currentIndex);\n }\n }\n\n updateRadialPositions(currentIndex) {\n const { angleUnit } = this.carousel.state;\n\n for (let i = 0; i < this.carousel.items.length; i++) {\n const item = this.carousel.items[i];\n const angle = angleUnit * i;\n\n this.setCSSVariables(item, {\n '--item-angle': `${this.round(angle, 2)}deg`,\n '--item-radius': `${RADIAL_RADIUS}px`,\n });\n }\n\n const { prev, next } = this.getAdjacentIndices(currentIndex);\n this.carousel.ui.setPeekItems(prev, next);\n }\n\n updateStackPositions(currentIndex) {\n const { prev, next } = this.getAdjacentIndices(currentIndex);\n\n for (let i = 0; i < this.carousel.items.length; i++) {\n const item = this.carousel.items[i];\n\n item.classList.remove(...STACK_POSITION_CLASSES);\n\n if (i === currentIndex) {\n item.classList.add(CLASS_NAMES.itemCenter);\n } else if (i === prev) {\n item.classList.add(CLASS_NAMES.itemPrev);\n } else if (i === next) {\n item.classList.add(CLASS_NAMES.itemNext);\n } else {\n item.classList.add(CLASS_NAMES.itemHidden);\n }\n }\n }\n\n calculateClassicSpacing(containerWidth) {\n const itemWidth = Math.max(300, Math.min(containerWidth * 0.35, 500));\n const isMobile = containerWidth <= CLASSIC_SPACING.mobileBreakpoint;\n\n const itemHalfPercent = (itemWidth / containerWidth) * 50;\n const baseSpacing = itemHalfPercent + CLASSIC_SPACING.gapPercent;\n const additionalSpacing = isMobile\n ? CLASSIC_SPACING.additionalMobile\n : CLASSIC_SPACING.additionalDesktop;\n\n return baseSpacing + additionalSpacing;\n }\n\n getClassicItemPosition(itemIndex, currentIndex, itemSpacing) {\n const { prev, next } = this.getAdjacentIndices(currentIndex);\n\n if (itemIndex === currentIndex) {\n return {\n x: CLASSIC_POSITIONS.center.x,\n scale: CLASSIC_POSITIONS.center.scale,\n };\n }\n\n if (itemIndex === prev) {\n return {\n x: CLASSIC_POSITIONS.center.x - itemSpacing,\n scale: CLASSIC_POSITIONS.peek.scale,\n };\n }\n\n if (itemIndex === next) {\n return {\n x: CLASSIC_POSITIONS.center.x + itemSpacing,\n scale: CLASSIC_POSITIONS.peek.scale,\n };\n }\n\n const distanceFromCurrent = itemIndex - currentIndex;\n return {\n x: distanceFromCurrent < 0\n ? CLASSIC_POSITIONS.center.x - itemSpacing * 2\n : CLASSIC_POSITIONS.center.x + itemSpacing * 2,\n scale: CLASSIC_POSITIONS.hidden.scale,\n };\n }\n\n updateClassicPositions(currentIndex) {\n const { prev, next } = this.getAdjacentIndices(currentIndex);\n const containerWidth = this.carousel.container.offsetWidth;\n const itemSpacing = this.calculateClassicSpacing(containerWidth);\n\n for (let i = 0; i < this.carousel.items.length; i++) {\n const item = this.carousel.items[i];\n const { x, scale } = this.getClassicItemPosition(i, currentIndex, itemSpacing);\n\n this.setCSSVariables(item, {\n '--item-x': `${this.round(x, 2)}%`,\n '--item-scale': String(scale),\n });\n }\n\n this.carousel.ui.setPeekItems(prev, next);\n }\n\n startMomentum(velocity) {\n this.stopMomentum();\n\n let currentVelocity = velocity;\n\n const momentumStep = () => {\n currentVelocity *= MOMENTUM_CONFIG.friction;\n\n if (Math.abs(currentVelocity) < MOMENTUM_CONFIG.minVelocity) {\n this.stopMomentum();\n return;\n }\n\n if (Math.abs(currentVelocity) > MOMENTUM_CONFIG.navigationThreshold) {\n const direction = currentVelocity > 0 ? -1 : 1;\n this.carousel.navigator.rotate(direction);\n currentVelocity *= MOMENTUM_CONFIG.dampingFactor;\n }\n\n this.momentumAnimation = requestAnimationFrame(momentumStep);\n };\n\n this.isAnimating = true;\n this.momentumAnimation = requestAnimationFrame(momentumStep);\n }\n\n stopMomentum() {\n if (this.momentumAnimation) {\n cancelAnimationFrame(this.momentumAnimation);\n this.momentumAnimation = null;\n }\n this.isAnimating = false;\n }\n}\n","export class AutoRotate {\n constructor(carousel) {\n this.carousel = carousel;\n this.interval = null;\n this.isActive = false;\n }\n\n setActiveState(isActive) {\n this.isActive = isActive;\n this.carousel.ui.updateAutoRotateButton(isActive);\n }\n\n toggle() {\n this.isActive ? this.stop() : this.start();\n }\n\n start() {\n if (this.isActive) return;\n\n this.setActiveState(true);\n\n const rotateInterval = this.carousel.options.autoRotateInterval;\n this.interval = setInterval(() => {\n this.carousel.navigator.next();\n }, rotateInterval);\n }\n\n stop() {\n if (!this.isActive) return;\n\n this.setActiveState(false);\n\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n }\n\n destroy() {\n this.stop();\n this.carousel = null;\n }\n}\n","import { KEYS, BREAKPOINTS } from '../core/constants.js';\nimport { isMobile } from '../utils/helpers.js';\n\nconst WHEEL_CONFIG = Object.freeze({\n threshold: 50,\n timeout: 150,\n cooldown: 100,\n});\n\nconst DRAG_CONFIG = Object.freeze({\n touchThreshold: 15,\n mouseThreshold: 10,\n velocityThreshold: 0.5,\n});\n\nconst RESIZE_DEBOUNCE = 100;\n\nexport class EventHandler {\n constructor(carousel) {\n this.carousel = carousel;\n this.boundHandlers = new Map();\n\n this.touch = {\n startX: 0,\n endX: 0,\n };\n\n this.drag = {\n active: false,\n startX: 0,\n currentX: 0,\n lastX: 0,\n lastTime: 0,\n velocity: 0,\n };\n\n this.wheel = {\n isScrolling: false,\n scrollTimeout: null,\n lastWheelTime: 0,\n accumulatedDelta: 0,\n };\n }\n\n init() {\n this.initNavigationButtons();\n this.initKeyboard();\n this.initWheel();\n this.initItemClick();\n this.initIndicatorClick();\n this.initTouch();\n this.initMouse();\n this.initResize();\n }\n\n stopAutoRotateAndNavigate(navigationFn) {\n this.completeCurrentIndicator();\n this.carousel.autoRotate.stop();\n navigationFn();\n }\n\n completeCurrentIndicator() {\n const currentIndicator = this.carousel.indicators[this.carousel.state.currentIndex];\n if (currentIndicator && currentIndicator.classList.contains('peek-carousel__indicator--active')) {\n currentIndicator.classList.add('peek-carousel__indicator--completed');\n }\n }\n\n resetDragState(index) {\n this.carousel.ui.removeDraggingClass(index);\n this.carousel.ui.clearDragTransform();\n }\n\n updateDraggingClass(dragDistance, currentIndex, threshold) {\n if (dragDistance > threshold) {\n this.carousel.ui.addDraggingClass(currentIndex, 'right');\n } else if (dragDistance < -threshold) {\n this.carousel.ui.addDraggingClass(currentIndex, 'left');\n }\n }\n\n initDragState(clientX) {\n this.drag.active = true;\n this.drag.startX = clientX;\n this.drag.currentX = clientX;\n this.drag.lastX = clientX;\n this.drag.lastTime = Date.now();\n this.drag.velocity = 0;\n }\n\n resetMouseCursor() {\n this.carousel.elements.carousel.style.cursor = 'grab';\n }\n\n calculateWheelDelta(e) {\n const deltaX = Math.abs(e.deltaX);\n const deltaY = Math.abs(e.deltaY);\n const isHorizontal = deltaX > deltaY;\n\n // [개발참고] 수평: 왼쪽(-) = 다음, 오른쪽(+) = 이전\n // 수직: 아래(+) = 다음, 위(-) = 이전\n return isHorizontal ? -e.deltaX : e.deltaY;\n }\n\n resetWheelState() {\n this.wheel.isScrolling = false;\n this.wheel.accumulatedDelta = 0;\n }\n\n initNavigationButtons() {\n const { prevBtn, nextBtn, autoRotateBtn } = this.carousel.elements;\n\n if (prevBtn) {\n this.addHandler(prevBtn, 'click', () => {\n this.stopAutoRotateAndNavigate(() => this.carousel.navigator.prev());\n });\n }\n\n if (nextBtn) {\n this.addHandler(nextBtn, 'click', () => {\n this.stopAutoRotateAndNavigate(() => this.carousel.navigator.next());\n });\n }\n\n if (autoRotateBtn) {\n this.addHandler(autoRotateBtn, 'click', () => {\n this.carousel.autoRotate.toggle();\n });\n }\n }\n\n initKeyboard() {\n if (!this.carousel.options.enableKeyboard) return;\n\n const handler = (e) => {\n const { navigator, autoRotate, totalItems } = this.carousel;\n\n switch (e.key) {\n case KEYS.arrowLeft:\n autoRotate.stop();\n navigator.prev();\n break;\n\n case KEYS.arrowRight:\n autoRotate.stop();\n navigator.next();\n break;\n\n case KEYS.home:\n e.preventDefault();\n autoRotate.stop();\n navigator.goTo(0);\n break;\n\n case KEYS.end:\n e.preventDefault();\n autoRotate.stop();\n navigator.goTo(totalItems - 1);\n break;\n\n case KEYS.space:\n e.preventDefault();\n autoRotate.toggle();\n break;\n\n default:\n const numKey = parseInt(e.key);\n if (numKey >= 1 && numKey <= totalItems) {\n e.preventDefault();\n autoRotate.stop();\n navigator.goTo(numKey - 1);\n }\n }\n };\n\n this.addHandler(document, 'keydown', handler);\n }\n\n initWheel() {\n if (!this.carousel.options.enableWheel) return;\n\n const handler = (e) => {\n const deltaX = Math.abs(e.deltaX);\n const deltaY = Math.abs(e.deltaY);\n\n if (deltaX < 1 && deltaY < 1) {\n return;\n }\n\n if (deltaX === deltaY) {\n return;\n }\n\n e.preventDefault();\n\n const currentTime = Date.now();\n\n if (currentTime - this.wheel.lastWheelTime < WHEEL_CONFIG.cooldown) {\n return;\n }\n\n if (!this.wheel.isScrolling) {\n this.wheel.isScrolling = true;\n this.wheel.accumulatedDelta = 0;\n this.carousel.autoRotate.stop();\n this.carousel.animator.stopMomentum();\n }\n\n this.wheel.accumulatedDelta += this.calculateWheelDelta(e);\n\n if (Math.abs(this.wheel.accumulatedDelta) >= WHEEL_CONFIG.threshold) {\n const direction = this.wheel.accumulatedDelta > 0 ? 1 : -1;\n this.carousel.navigator.rotate(direction);\n\n this.wheel.accumulatedDelta = 0;\n this.wheel.lastWheelTime = currentTime;\n }\n\n clearTimeout(this.wheel.scrollTimeout);\n this.wheel.scrollTimeout = setTimeout(() => {\n this.resetWheelState();\n }, WHEEL_CONFIG.timeout);\n };\n\n this.addHandler(\n this.carousel.elements.carousel,\n 'wheel',\n handler,\n { passive: false }\n );\n }\n\n initItemClick() {\n const { items } = this.carousel;\n for (let i = 0; i < items.length; i++) {\n this.addHandler(items[i], 'click', () => {\n this.carousel.autoRotate.stop();\n this.carousel.navigator.handleItemClick(i);\n });\n }\n }\n\n initIndicatorClick() {\n const { indicators } = this.carousel;\n for (let i = 0; i < indicators.length; i++) {\n this.addHandler(indicators[i], 'click', () => {\n this.carousel.autoRotate.stop();\n this.carousel.navigator.handleIndicatorClick(i);\n });\n }\n }\n\n initTouch() {\n if (!this.carousel.options.enableTouch) return;\n\n this.addHandler(this.carousel.elements.carousel, 'touchstart', (e) => {\n this.touch.startX = e.changedTouches[0].screenX;\n });\n\n this.addHandler(this.carousel.elements.carousel, 'touchmove', (e) => {\n const touchCurrentX = e.changedTouches[0].screenX;\n const dragDistance = touchCurrentX - this.touch.startX;\n const { currentIndex } = this.carousel.state;\n\n this.carousel.ui.updateDragTransform(dragDistance);\n this.updateDraggingClass(dragDistance, currentIndex, DRAG_CONFIG.touchThreshold);\n });\n\n this.addHandler(this.carousel.elements.carousel, 'touchend', (e) => {\n this.touch.endX = e.changedTouches[0].screenX;\n const swipeDistance = this.touch.endX - this.touch.startX;\n const { swipeThreshold } = this.carousel.options;\n const { currentIndex } = this.carousel.state;\n\n this.resetDragState(currentIndex);\n\n if (swipeDistance < -swipeThreshold) {\n this.carousel.autoRotate.stop();\n this.carousel.navigator.next();\n } else if (swipeDistance > swipeThreshold) {\n this.carousel.autoRotate.stop();\n this.carousel.navigator.prev();\n }\n });\n }\n\n initMouse() {\n if (!this.carousel.options.enableMouse) return;\n\n this.addHandler(this.carousel.elements.carousel, 'mousedown', (e) => {\n if (isMobile()) return;\n\n this.initDragState(e.clientX);\n this.carousel.autoRotate.stop();\n this.carousel.animator.stopMomentum();\n this.carousel.elements.carousel.style.cursor = 'grabbing';\n e.preventDefault();\n });\n\n this.addHandler(document, 'mousemove', (e) => {\n if (!this.drag.active) return;\n\n const currentTime = Date.now();\n const deltaTime = currentTime - this.drag.lastTime;\n const deltaX = e.clientX - this.drag.lastX;\n\n if (deltaTime > 0) {\n this.drag.velocity = deltaX / deltaTime;\n }\n\n this.drag.currentX = e.clientX;\n this.drag.lastX = e.clientX;\n this.drag.lastTime = currentTime;\n\n const dragDistance = this.drag.currentX - this.drag.startX;\n const { currentIndex } = this.carousel.state;\n\n this.carousel.ui.updateDragTransform(dragDistance);\n this.updateDraggingClass(dragDistance, currentIndex, DRAG_CONFIG.mouseThreshold);\n\n if (Math.abs(dragDistance) > this.carousel.options.dragThreshold) {\n const direction = dragDistance > 0 ? -1 : 1;\n this.carousel.navigator.rotate(direction);\n this.drag.startX = this.drag.currentX;\n this.resetDragState(currentIndex);\n }\n });\n\n this.addHandler(document, 'mouseup', () => {\n if (!this.drag.active) return;\n\n this.drag.active = false;\n this.resetMouseCursor();\n\n const { currentIndex } = this.carousel.state;\n this.resetDragState(currentIndex);\n\n if (Math.abs(this.drag.velocity) > DRAG_CONFIG.velocityThreshold) {\n this.carousel.animator.startMomentum(this.drag.velocity);\n }\n });\n\n this.addHandler(this.carousel.elements.carousel, 'mouseleave', () => {\n if (this.drag.active) {\n this.drag.active = false;\n this.resetMouseCursor();\n\n const { currentIndex } = this.carousel.state;\n this.resetDragState(currentIndex);\n }\n });\n\n if (window.innerWidth > BREAKPOINTS.mobile) {\n this.resetMouseCursor();\n }\n }\n\n initResize() {\n let resizeTimer;\n const handler = () => {\n clearTimeout(resizeTimer);\n resizeTimer = setTimeout(() => {\n this.carousel.animator.updateCarousel();\n }, RESIZE_DEBOUNCE);\n };\n\n this.addHandler(window, 'resize', handler);\n }\n\n addHandler(element, event, handler, options) {\n element.addEventListener(event, handler, options);\n\n const key = `${event}-${Date.now()}-${Math.random()}`;\n this.boundHandlers.set(key, { element, event, handler, options });\n }\n\n destroy() {\n if (this.wheel.scrollTimeout) {\n clearTimeout(this.wheel.scrollTimeout);\n this.wheel.scrollTimeout = null;\n }\n\n for (const { element, event, handler, options } of this.boundHandlers.values()) {\n element.removeEventListener(event, handler, options);\n }\n this.boundHandlers.clear();\n\n this.carousel = null;\n }\n}\n","import { CLASS_NAMES, ARIA, DURATIONS } from '../core/constants.js';\nimport { addClass, removeClass, setAttribute, setCSSVar } from '../utils/dom.js';\n\nconst DRAG_TRANSFORM_CONFIG = Object.freeze({\n stack: {\n maxDrag: 200,\n offsetMultiplier: 100,\n rotationMultiplier: 3,\n },\n radial: {\n rotationSensitivity: 0.2,\n },\n classic: {\n dragSensitivity: 0.5,\n },\n});\n\nexport class UIManager {\n constructor(carousel) {\n this.carousel = carousel;\n }\n\n updateActiveStates(currentIndex) {\n for (let i = 0; i < this.carousel.items.length; i++) {\n const item = this.carousel.items[i];\n removeClass(item, CLASS_NAMES.itemActive, CLASS_NAMES.itemPrev, CLASS_NAMES.itemNext);\n item.removeAttribute(ARIA.current);\n }\n\n for (let i = 0; i < this.carousel.indicators.length; i++) {\n const indicator = this.carousel.indicators[i];\n removeClass(\n indicator,\n CLASS_NAMES.indicatorActive,\n CLASS_NAMES.indicatorProgress\n );\n setAttribute(indicator, ARIA.selected, 'false');\n setAttribute(indicator, ARIA.tabindex, '-1');\n }\n\n const currentItem = this.carousel.items[currentIndex];\n const currentIndicator = this.carousel.indicators[currentIndex];\n\n if (currentItem) {\n addClass(currentItem, CLASS_NAMES.itemActive);\n setAttribute(currentItem, ARIA.current, 'true');\n }\n\n if (currentIndicator) {\n removeClass(currentIndicator, CLASS_NAMES.indicatorCompleted);\n addClass(currentIndicator, CLASS_NAMES.indicatorActive);\n setAttribute(currentIndicator, ARIA.selected, 'true');\n setAttribute(currentIndicator, ARIA.tabindex, '0');\n\n if (this.carousel.autoRotate.isActive) {\n this.updateIndicatorProgress(currentIndicator);\n }\n }\n }\n\n updateIndicatorProgress(indicator) {\n setCSSVar(\n indicator,\n '--progress-duration',\n `${this.carousel.options.autoRotateInterval}ms`\n );\n\n setTimeout(() => {\n if (indicator) {\n addClass(indicator, CLASS_NAMES.indicatorProgress);\n }\n }, DURATIONS.progressReset);\n }\n\n clearPeekItems() {\n for (let i = 0; i < this.carousel.items.length; i++) {\n const item = this.carousel.items[i];\n removeClass(item, CLASS_NAMES.itemPrev, CLASS_NAMES.itemNext);\n }\n }\n\n setPeekItems(prevIndex, nextIndex) {\n const prevItem = this.carousel.items[prevIndex];\n const nextItem = this.carousel.items[nextIndex];\n\n if (prevItem) addClass(prevItem, CLASS_NAMES.itemPrev);\n if (nextItem) addClass(nextItem, CLASS_NAMES.itemNext);\n }\n\n updateAutoRotateButton(isActive) {\n const { autoRotateBtn } = this.carousel.elements;\n if (!autoRotateBtn) return;\n\n if (isActive) {\n addClass(autoRotateBtn, CLASS_NAMES.btnActive);\n setAttribute(autoRotateBtn, ARIA.pressed, 'true');\n } else {\n removeClass(autoRotateBtn, CLASS_NAMES.btnActive);\n setAttribute(autoRotateBtn, ARIA.pressed, 'false');\n }\n }\n\n addDraggingClass(index, direction) {\n const item = this.carousel.items[index];\n if (!item) return;\n\n const leftClass = CLASS_NAMES.itemDraggingLeft;\n const rightClass = CLASS_NAMES.itemDraggingRight;\n\n removeClass(item, leftClass, rightClass);\n\n if (direction === 'left') {\n addClass(item, leftClass);\n } else if (direction === 'right') {\n addClass(item, rightClass);\n }\n }\n\n removeDraggingClass(index) {\n const item = this.carousel.items[index];\n if (!item) return;\n\n removeClass(item, CLASS_NAMES.itemDraggingLeft, CLASS_NAMES.itemDraggingRight);\n }\n\n round(value, decimals = 2) {\n return Math.round(value * 10 ** decimals) / 10 ** decimals;\n }\n\n applyEasing(progress) {\n return progress * (2 - Math.abs(progress));\n }\n\n updateDragTransform(dragDistance) {\n const { layoutMode } = this.carousel.options;\n\n if (layoutMode === 'stack') {\n // [개발참고] Stack 모드: 탄성 효과 적용 (easeOutQuad)\n const config = DRAG_TRANSFORM_CONFIG.stack;\n const clampedDrag = Math.max(-config.maxDrag, Math.min(config.maxDrag, dragDistance));\n const progress = clampedDrag / config.maxDrag;\n const easedProgress = this.applyEasing(progress);\n\n const dragOffset = this.round(easedProgress * config.offsetMultiplier);\n const dragRotation = this.round(easedProgress * config.rotationMultiplier);\n\n this.carousel.container.style.setProperty('--drag-offset', `${dragOffset}px`);\n this.carousel.container.style.setProperty('--drag-rotation', `${dragRotation}deg`);\n } else if (layoutMode === 'radial') {\n const config = DRAG_TRANSFORM_CONFIG.radial;\n const dragRotation = this.round(dragDistance * config.rotationSensitivity);\n\n this.carousel.container.style.setProperty('--drag-rotation-y', `${dragRotation}deg`);\n } else if (layoutMode === 'classic') {\n const config = DRAG_TRANSFORM_CONFIG.classic;\n const dragOffset = this.round(dragDistance * config.dragSensitivity);\n\n this.carousel.container.style.setProperty('--drag-offset', `${dragOffset}px`);\n }\n }\n\n clearDragTransform() {\n this.carousel.container.style.setProperty('--drag-offset', '0px');\n this.carousel.container.style.setProperty('--drag-rotation', '0deg');\n this.carousel.container.style.setProperty('--drag-rotation-y', '0deg');\n }\n\n destroy() {\n }\n}\n","import { validateOptions } from './config.js';\nimport { SELECTORS, CLASS_NAMES } from './constants.js';\nimport { getElement, getElements } from '../utils/dom.js';\nimport { preloadImagesInRange } from '../utils/preloader.js';\nimport { injectIcon, injectAutoRotateIcons } from '../utils/icons.js';\nimport { Navigator } from '../modules/Navigator.js';\nimport { Animator } from '../modules/Animator.js';\nimport { AutoRotate } from '../modules/AutoRotate.js';\nimport { EventHandler } from '../modules/EventHandler.js';\nimport { UIManager } from '../modules/UIManager.js';\n\nconst FULL_CIRCLE_DEGREES = 360;\n\nclass PeekCarousel {\n constructor(selector, options = {}) {\n this.container = getElement(selector);\n if (!this.container) {\n throw new Error(`PeekCarousel: 셀렉터 \"${selector}\"에 해당하는 컨테이너를 찾을 수 없습니다`);\n }\n\n this.options = validateOptions(options);\n this.initElements();\n\n if (this.items.length === 0) {\n throw new Error('PeekCarousel: 캐러셀 아이템을 찾을 수 없습니다');\n }\n\n this.state = {\n currentIndex: this.options.startIndex,\n angleUnit: FULL_CIRCLE_DEGREES / this.totalItems,\n };\n\n this.initModules();\n this.init();\n }\n\n initElements() {\n this.elements = {\n carousel: this.container.querySelector(SELECTORS.carousel),\n prevBtn: null,\n nextBtn: null,\n autoRotateBtn: null,\n controls: null,\n nav: null,\n };\n\n this.items = getElements(SELECTORS.item, this.container);\n this.totalItems = this.items.length;\n this.indicators = [];\n }\n\n initModules() {\n this.navigator = new Navigator(this);\n this.animator = new Animator(this);\n this.autoRotate = new AutoRotate(this);\n this.eventHandler = new EventHandler(this);\n this.ui = new UIManager(this);\n }\n\n init() {\n this.updateLayoutClass();\n this.createNavigation();\n this.createControls();\n this.injectIcons();\n this.createCounter();\n this.setImageLoadingAttributes();\n this.initCSSVariables();\n\n this.eventHandler.init();\n this.animator.updateCarousel();\n\n if (this.options.autoRotate) {\n this.autoRotate.start();\n }\n\n if (this.options.preloadRange > 0) {\n this.preloadImages();\n }\n }\n\n initCSSVariables() {\n this.container.style.setProperty('--carousel-rotation', '0deg');\n this.container.style.setProperty('--drag-offset', '0px');\n this.container.style.setProperty('--drag-rotation', '0deg');\n this.container.style.setProperty('--drag-rotation-y', '0deg');\n }\n\n createNavigation() {\n if (!this.options.showNavigation) return;\n\n const existingNav = this.container.querySelector(`.${CLASS_NAMES.nav}`);\n if (existingNav) {\n this.elements.nav = existingNav;\n this.elements.prevBtn = existingNav.querySelector(SELECTORS.prevBtn);\n this.elements.nextBtn = existingNav.querySelector(SELECTORS.nextBtn);\n return;\n }\n\n const nav = document.createElement('div');\n nav.className = CLASS_NAMES.nav;\n\n const prevBtn = document.createElement('button');\n prevBtn.className = `${CLASS_NAMES.navBtn} ${CLASS_NAMES.btn} ${CLASS_NAMES.prevBtn}`;\n prevBtn.setAttribute('aria-label', 'Previous');\n\n const nextBtn = document.createElement('button');\n nextBtn.className = `${CLASS_NAMES.navBtn} ${CLASS_NAMES.btn} ${CLASS_NAMES.nextBtn}`;\n nextBtn.setAttribute('aria-label', 'Next');\n\n nav.appendChild(prevBtn);\n nav.appendChild(nextBtn);\n this.container.appendChild(nav);\n\n this.elements.nav = nav;\n this.elements.prevBtn = prevBtn;\n this.elements.nextBtn = nextBtn;\n }\n\n createControls() {\n if (!this.options.showIndicators && !this.options.showAutoRotateButton) return;\n\n const existingControls = this.container.querySelector(`.${CLASS_NAMES.controls}`);\n if (existingControls) {\n this.elements.controls = existingControls;\n const indicatorsWrapper = existingControls.querySelector(`.${CLASS_NAMES.indicators}`);\n if (indicatorsWrapper && this.options.showIndicators) {\n indicatorsWrapper.innerHTML = '';\n this.createIndicators(indicatorsWrapper);\n }\n this.elements.autoRotateBtn = existingControls.querySelector(SELECTORS.autoRotateBtn);\n return;\n }\n\n const controls = document.createElement('div');\n controls.className = CLASS_NAMES.controls;\n\n if (this.options.showIndicators) {\n const indicatorsWrapper = document.createElement('div');\n indicatorsWrapper.className = CLASS_NAMES.indicators;\n this.createIndicators(indicatorsWrapper);\n controls.appendChild(indicatorsWrapper);\n }\n\n if (this.options.showAutoRotateButton) {\n const autoRotateBtn = document.createElement('button');\n autoRotateBtn.className = `${CLASS_NAMES.autoRotateBtn} ${CLASS_NAMES.btn} ${CLASS_NAMES.btnAutoRotate}`;\n autoRotateBtn.setAttribute('aria-label', 'Toggle auto-rotate');\n autoRotateBtn.setAttribute('aria-pressed', 'false');\n controls.appendChild(autoRotateBtn);\n this.elements.autoRotateBtn = autoRotateBtn;\n }\n\n this.container.appendChild(controls);\n this.elements.controls = controls;\n }\n\n createIndicators(wrapper) {\n this.indicators = [];\n\n for (let i = 0; i < this.totalItems; i++) {\n const indicator = document.createElement('button');\n const isActive = i === this.state.currentIndex;\n\n indicator.className = CLASS_NAMES.indicator;\n indicator.classList.add(CLASS_NAMES.indicatorPeek);\n indicator.setAttribute('role', 'tab');\n indicator.setAttribute('aria-label', `Image ${i + 1}`);\n indicator.setAttribute('aria-selected', isActive ? 'true' : 'false');\n indicator.setAttribute('tabindex', isActive ? '0' : '-1');\n\n if (isActive) {\n indicator.classList.add(CLASS_NAMES.indicatorActive);\n }\n\n wrapper.appendChild(indicator);\n this.indicators.push(indicator);\n }\n }\n\n injectIcons() {\n const { prevBtn, nextBtn, autoRotateBtn } = this.elements;\n\n if (prevBtn) injectIcon(prevBtn, 'prev');\n if (nextBtn) injectIcon(nextBtn, 'next');\n if (autoRotateBtn) injectAutoRotateIcons(autoRotateBtn);\n }\n\n createCounter() {\n if (!this.options.showCounter) return;\n\n const existingCounter = this.container.querySelector(`.${CLASS_NAMES.counter}`);\n if (existingCounter) {\n this.counterElement = existingCounter;\n this.updateCounter();\n return;\n }\n\n const counter = document.createElement('div');\n counter.className = CLASS_NAMES.counter;\n counter.setAttribute('aria-live', 'polite');\n counter.setAttribute('aria-atomic', 'true');\n\n counter.innerHTML = `\n <span class=\"${CLASS_NAMES.counterCurrent}\">${this.state.currentIndex + 1}</span>\n <span class=\"${CLASS_NAMES.counterSeparator}\">/</span>\n <span class=\"${CLASS_NAMES.counterTotal}\">${this.totalItems}</span>\n `;\n\n this.container.appendChild(counter);\n this.counterElement = counter;\n }\n\n updateCounter() {\n if (!this.counterElement) return;\n\n const currentSpan = this.counterElement.querySelector(`.${CLASS_NAMES.counterCurrent}`);\n if (currentSpan) {\n currentSpan.textContent = this.state.currentIndex + 1;\n }\n }\n\n setImageLoadingAttributes() {\n const { startIndex } = this.options;\n const preloadRange = this.options.preloadRange || 1;\n\n for (let index = 0; index < this.items.length; index++) {\n const item = this.items[index];\n const img = item.querySelector(`.${CLASS_NAMES.image}`);\n if (!img || img.hasAttribute('loading')) continue;\n\n const distance = Math.abs(index - startIndex);\n const isNearby = distance <= preloadRange;\n img.setAttribute('loading', isNearby ? 'eager' : 'lazy');\n }\n }\n\n updateLayoutClass() {\n const currentMode = this.currentLayoutMode;\n const newMode = this.options.layoutMode;\n\n if (currentMode && currentMode !== newMode) {\n this.container.classList.remove(`peek-carousel--${currentMode}`);\n }\n\n this.container.classList.add(`peek-carousel--${newMode}`);\n this.currentLayoutMode = newMode;\n }\n\n preloadImages() {\n preloadImagesInRange(this.items, this.state.currentIndex, this.options.preloadRange);\n }\n\n // [개발참고] Public API\n next() {\n this.navigator.next();\n }\n\n prev() {\n this.navigator.prev();\n }\n\n goTo(index) {\n this.navigator.goTo(index);\n }\n\n startAutoRotate() {\n this.autoRotate.start();\n }\n\n stopAutoRotate() {\n this.autoRotate.stop();\n }\n\n toggleAutoRotate() {\n this.autoRotate.toggle();\n }\n\n destroy() {\n this.autoRotate.destroy();\n this.animator.stopMomentum();\n this.eventHandler.destroy();\n this.ui.destroy();\n }\n\n get currentIndex() {\n return this.state.currentIndex;\n }\n\n get isAutoRotating() {\n return this.autoRotate.isActive;\n }\n}\n\nexport default PeekCarousel;\n"],"names":["LAYOUT_MODES","Object","freeze","STACK","RADIAL","CLASSIC","DEFAULT_OPTIONS","startIndex","layoutMode","autoRotate","autoRotateInterval","preloadRange","swipeThreshold","dragThreshold","enableKeyboard","enableWheel","enableTouch","enableMouse","showNavigation","showCounter","showIndicators","showAutoRotateButton","CLASS_NAMES","carousel","track","item","itemActive","itemPrev","itemNext","itemCenter","itemHidden","itemDraggingLeft","itemDraggingRight","figure","image","caption","nav","navBtn","btn","prevBtn","nextBtn","autoRotateBtn","btnAutoRotate","btnActive","controls","indicators","indicator","indicatorPeek","indicatorActive","indicatorProgress","indicatorCompleted","counter","counterCurrent","counterSeparator","counterTotal","playIcon","pauseIcon","SELECTORS","ARIA","current","selected","pressed","label","tabindex","BREAKPOINTS","mobile","DURATIONS","transition","progressReset","KEYS","arrowLeft","arrowRight","home","end","space","addClass","element","classes","HTMLElement","length","classList","add","removeClass","remove","setAttribute","name","value","loadingCache","Map","preloadImages","sources","Promise","resolve","uniqueSources","Set","all","map","src","reject","Error","has","get","img","Image","promise","res","rej","onload","delete","onerror","set","then","preloadImage","iconCache","ICONS","prev","path","options","next","play","fill","stroke","strokeWidth","strokeLinecap","strokeLinejoin","pause","getIcon","iconName","customOptions","icon","console","warn","width","height","viewBox","className","cacheKey","JSON","stringify","svg","createSVGIcon","injectIcon","button","querySelector","iconHTML","innerHTML","normalizeIndex","index","Navigator","constructor","this","currentIndex","state","totalItems","getShortestDistance","from","to","total","normalizedTo","normalizedFrom","forwardDist","backwardDist","isNearby","Math","abs","updateAfterNavigation","animator","updateCarousel","updateCounter","rotate","direction","goTo","normalizedIndex","navigateIfDifferent","targetIndex","callback","handleItemClick","handleRadialItemClick","handleStackItemClick","shortestDist","handleIndicatorClick","CLASSIC_POSITIONS","center","x","scale","peek","hidden","CLASSIC_SPACING","gapPercent","additionalMobile","additionalDesktop","mobileBreakpoint","MOMENTUM_CONFIG","friction","minVelocity","navigationThreshold","dampingFactor","STACK_POSITION_CLASSES","Animator","momentumAnimation","isAnimating","normalizeAngleDiff","diff","round","decimals","getAdjacentIndices","setCarouselRotation","angle","rounded","container","style","setProperty","setCSSVariables","variables","key","entries","updateRadialRotation","targetAngle","angleUnit","currentRotation","getPropertyValue","currentAngle","parseFloat","finalAngle","updateActiveItem","ui","updateActiveStates","updateRadialPositions","updateClassicPositions","updateStackPositions","i","items","setPeekItems","calculateClassicSpacing","containerWidth","itemWidth","max","min","isMobile","getClassicItemPosition","itemIndex","itemSpacing","offsetWidth","String","startMomentum","velocity","stopMomentum","currentVelocity","momentumStep","navigator","requestAnimationFrame","cancelAnimationFrame","AutoRotate","interval","isActive","setActiveState","updateAutoRotateButton","toggle","stop","start","rotateInterval","setInterval","clearInterval","destroy","WHEEL_CONFIG","threshold","timeout","cooldown","DRAG_CONFIG","touchThreshold","mouseThreshold","velocityThreshold","EventHandler","boundHandlers","touch","startX","endX","drag","active","currentX","lastX","lastTime","wheel","isScrolling","scrollTimeout","lastWheelTime","accumulatedDelta","init","initNavigationButtons","initKeyboard","initWheel","initItemClick","initIndicatorClick","initTouch","initMouse","initResize","stopAutoRotateAndNavigate","navigationFn","completeCurrentIndicator","currentIndicator","contains","resetDragState","removeDraggingClass","clearDragTransform","updateDraggingClass","dragDistance","addDraggingClass","initDragState","clientX","Date","now","resetMouseCursor","elements","cursor","calculateWheelDelta","e","deltaX","deltaY","resetWheelState","addHandler","document","preventDefault","numKey","parseInt","currentTime","clearTimeout","setTimeout","passive","changedTouches","screenX","updateDragTransform","swipeDistance","window","innerWidth","deltaTime","resizeTimer","handler","event","addEventListener","random","values","removeEventListener","clear","DRAG_TRANSFORM_CONFIG","stack","maxDrag","offsetMultiplier","rotationMultiplier","radial","rotationSensitivity","classic","dragSensitivity","UIManager","removeAttribute","currentItem","updateIndicatorProgress","property","clearPeekItems","prevIndex","nextIndex","prevItem","nextItem","leftClass","rightClass","applyEasing","progress","config","easedProgress","dragOffset","dragRotation","PeekCarousel","selector","getElement","validated","includes","validateOptions","initElements","initModules","parent","Array","querySelectorAll","getElements","eventHandler","updateLayoutClass","createNavigation","createControls","injectIcons","createCounter","setImageLoadingAttributes","initCSSVariables","existingNav","createElement","appendChild","existingControls","indicatorsWrapper","createIndicators","wrapper","push","playHTML","pauseHTML","injectAutoRotateIcons","existingCounter","counterElement","currentSpan","textContent","hasAttribute","currentMode","currentLayoutMode","newMode","range","imagesToPreload","distance","prevImg","nextImg","complete","size","catch","err","preloadImagesInRange","startAutoRotate","stopAutoRotate","toggleAutoRotate","isAutoRotating"],"mappings":"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"}
|