@adstage/web-sdk 2.5.3 → 2.6.1
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 +59 -0
- package/dist/index.cjs.js +2073 -1045
- package/dist/index.d.ts +55 -12
- package/dist/index.esm.js +2073 -1045
- package/dist/index.standalone.js +2073 -1045
- package/package.json +1 -1
- package/src/constants/endpoints.ts +0 -1
- package/src/core/{AdStage.ts → adstage.ts} +36 -8
- package/src/index.ts +9 -3
- package/src/managers/ads/advertisement-event-tracker.ts +15 -11
- package/src/managers/ads/carousel-slider-manager.ts +90 -12
- package/src/managers/ads/slider-event-tracker.ts +57 -0
- package/src/managers/ads/text-transition-manager.ts +91 -26
- package/src/modules/ads/ad-renderer.ts +259 -0
- package/src/modules/ads/{AdsModule.ts → ads-module.ts} +202 -21
- package/src/modules/ads/interfaces/i-ad-renderer.ts +77 -0
- package/src/modules/ads/renderers/banner-ad-renderer.ts +414 -0
- package/src/modules/ads/renderers/base-ad-renderer.ts +340 -0
- package/src/modules/ads/renderers/interstitial-ad-renderer.ts +256 -0
- package/src/modules/ads/renderers/native-ad-renderer.ts +154 -0
- package/src/modules/ads/renderers/text-ad-renderer.ts +120 -0
- package/src/modules/ads/renderers/video-ad-renderer.ts +433 -0
- package/src/modules/config/{ConfigModule.ts → config-module.ts} +1 -5
- package/src/react/{AdStageProvider.tsx → ad-stage-provider.tsx} +1 -1
- package/src/react/index.ts +2 -2
- package/src/types/config.ts +2 -184
- package/src/utils/ad-click-handler.ts +155 -0
- package/src/utils/text-ad-utils.ts +37 -0
- package/src/dummy/ads_dummy.json +0 -84
- package/src/modules/ads/AdRenderer.ts +0 -735
- package/src/renderers/banner-renderer.ts +0 -35
- package/src/renderers/base-renderer.ts +0 -209
- package/src/renderers/index.ts +0 -71
- package/src/renderers/interstitial-renderer.ts +0 -70
- package/src/renderers/native-renderer.ts +0 -35
- package/src/renderers/text-renderer.ts +0 -94
- package/src/renderers/video-renderer.ts +0 -63
- /package/src/modules/events/{EventsModule.ts → events-module.ts} +0 -0
package/package.json
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { AdStageConfig, ModuleName } from '../types/config';
|
|
7
|
-
import { AdsModule } from '../modules/ads/
|
|
8
|
-
import { ConfigModule } from '../modules/config/
|
|
9
|
-
import { EventsModule } from '../modules/events/
|
|
7
|
+
import { AdsModule } from '../modules/ads/ads-module';
|
|
8
|
+
import { ConfigModule } from '../modules/config/config-module';
|
|
9
|
+
import { EventsModule } from '../modules/events/events-module';
|
|
10
|
+
import { ViewableEventTracker } from '../managers/ads/viewable-event-tracker';
|
|
10
11
|
import { endpoints } from '../constants/endpoints';
|
|
11
12
|
|
|
12
13
|
export class AdStage {
|
|
@@ -46,10 +47,6 @@ export class AdStage {
|
|
|
46
47
|
timeout: 30000,
|
|
47
48
|
debug: false,
|
|
48
49
|
modules: ['ads', 'events', 'config'],
|
|
49
|
-
validateOnInit: false,
|
|
50
|
-
fallbackMode: true,
|
|
51
|
-
offlineMode: false,
|
|
52
|
-
productionMode: false,
|
|
53
50
|
...config
|
|
54
51
|
};
|
|
55
52
|
|
|
@@ -78,7 +75,7 @@ export class AdStage {
|
|
|
78
75
|
version: '2.0.0',
|
|
79
76
|
modules: enabledModules,
|
|
80
77
|
apiKey: config.apiKey.substring(0, 8) + '...',
|
|
81
|
-
mode:
|
|
78
|
+
mode: 'development'
|
|
82
79
|
});
|
|
83
80
|
}
|
|
84
81
|
}
|
|
@@ -131,6 +128,37 @@ export class AdStage {
|
|
|
131
128
|
AdStage.instance._config = null;
|
|
132
129
|
}
|
|
133
130
|
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 디버그용 메서드들
|
|
134
|
+
*/
|
|
135
|
+
public static debug = {
|
|
136
|
+
/**
|
|
137
|
+
* 모든 viewable 추적 데이터 초기화
|
|
138
|
+
*/
|
|
139
|
+
clearAllViewable: (): void => {
|
|
140
|
+
ViewableEventTracker.clear();
|
|
141
|
+
console.log('✅ AdStage Debug: 모든 viewable 추적 데이터 초기화됨');
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 특정 광고의 viewable 추적 초기화
|
|
146
|
+
*/
|
|
147
|
+
clearAdViewable: (adId: string, slotId: string): void => {
|
|
148
|
+
ViewableEventTracker.clearAdViewable(adId, slotId);
|
|
149
|
+
console.log(`✅ AdStage Debug: 광고 ${adId}(${slotId})의 viewable 추적 초기화됨`);
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 현재 추적 중인 viewable 상태 확인
|
|
154
|
+
*/
|
|
155
|
+
getViewableStatus: (): void => {
|
|
156
|
+
console.log('📊 AdStage Debug: 현재 viewable 추적 상태', {
|
|
157
|
+
trackedCount: (ViewableEventTracker as any).viewableTracker.size,
|
|
158
|
+
trackedItems: Array.from((ViewableEventTracker as any).viewableTracker)
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
};
|
|
134
162
|
}
|
|
135
163
|
|
|
136
164
|
// 전역 네임스페이스로 내보내기
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// 메인 네임스페이스 클래스
|
|
7
|
-
export { default as AdStage } from './core/
|
|
7
|
+
export { default as AdStage } from './core/adstage';
|
|
8
|
+
import AdStageCore from './core/adstage';
|
|
8
9
|
|
|
9
10
|
// React 통합
|
|
10
11
|
export { AdStageProvider, useAdStageContext, useAdStageInstance } from './react';
|
|
@@ -13,8 +14,8 @@ export { AdStageProvider, useAdStageContext, useAdStageInstance } from './react'
|
|
|
13
14
|
export type { AdStageConfig, ModuleName, BaseModule, ApiResponse, OrganizationInfo } from './types/config';
|
|
14
15
|
|
|
15
16
|
// 모듈별 타입
|
|
16
|
-
export type { AdOptions } from './modules/ads/
|
|
17
|
-
export type { EventProperties, PageData } from './modules/events/
|
|
17
|
+
export type { AdOptions } from './modules/ads/ads-module';
|
|
18
|
+
export type { EventProperties, PageData } from './modules/events/events-module';
|
|
18
19
|
|
|
19
20
|
// 광고 관련 타입
|
|
20
21
|
export type { AdType, AdEventType, Advertisement, AdSlot } from './types/advertisement';
|
|
@@ -22,3 +23,8 @@ export type { AdType, AdEventType, Advertisement, AdSlot } from './types/adverti
|
|
|
22
23
|
// 버전 정보
|
|
23
24
|
export const SDK_VERSION = '2.0.0';
|
|
24
25
|
export const SUPPORTED_MODULES = ['ads', 'events', 'config'] as const;
|
|
26
|
+
|
|
27
|
+
// 브라우저 환경에서 전역 객체로 노출 (디버깅용)
|
|
28
|
+
if (typeof window !== 'undefined') {
|
|
29
|
+
(window as any).AdStage = AdStageCore;
|
|
30
|
+
}
|
|
@@ -38,16 +38,6 @@ export class AdvertisementEventTracker {
|
|
|
38
38
|
console.log(`🚀 AdvertisementEventTracker: Processing ${eventType} event for ad ${adId} in slot ${slotId}`);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// VIEWABLE 이벤트 중복 확인
|
|
42
|
-
if (eventType === AdEventType.VIEWABLE) {
|
|
43
|
-
if (ViewableEventTracker.isDuplicateViewable(adId, slotId, this.debug)) {
|
|
44
|
-
if (this.debug) {
|
|
45
|
-
console.log(`⏭️ Skipping duplicate viewable event for ad ${adId} in slot ${slotId}`);
|
|
46
|
-
}
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
41
|
// 현재 슬롯 정보 가져오기
|
|
52
42
|
const slot = this.slots.get(slotId);
|
|
53
43
|
|
|
@@ -100,6 +90,12 @@ export class AdvertisementEventTracker {
|
|
|
100
90
|
headers,
|
|
101
91
|
eventData
|
|
102
92
|
});
|
|
93
|
+
console.log(`🌐 Full API call details:`, {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
url,
|
|
96
|
+
hasApiKey: !!this.apiKey,
|
|
97
|
+
bodySize: JSON.stringify(eventData).length
|
|
98
|
+
});
|
|
103
99
|
}
|
|
104
100
|
|
|
105
101
|
const response = await fetch(url, {
|
|
@@ -124,7 +120,15 @@ export class AdvertisementEventTracker {
|
|
|
124
120
|
console.log(`✅ Successfully tracked advertisement event: ${eventType} for ad ${adId}`);
|
|
125
121
|
}
|
|
126
122
|
} catch (error) {
|
|
127
|
-
console.error('Failed to track advertisement event:', error);
|
|
123
|
+
console.error('❌ Failed to track advertisement event:', error);
|
|
124
|
+
console.error('🔍 Debug info:', {
|
|
125
|
+
baseUrl: this.baseUrl,
|
|
126
|
+
apiKey: this.apiKey ? `${this.apiKey.substring(0, 8)}...` : 'NOT_SET',
|
|
127
|
+
url: `${this.baseUrl}/advertisements/events/${adId}/${eventType}`,
|
|
128
|
+
eventType,
|
|
129
|
+
adId,
|
|
130
|
+
slotId
|
|
131
|
+
});
|
|
128
132
|
}
|
|
129
133
|
}
|
|
130
134
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AdType, AdEventType } from '../../types/advertisement';
|
|
2
2
|
import type { AdSlot, Advertisement } from '../../types/advertisement';
|
|
3
|
-
import {
|
|
3
|
+
import { SliderEventTracker } from './slider-event-tracker';
|
|
4
|
+
import { AdClickHandler } from '../../utils/ad-click-handler';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* 캐러셀 슬라이더 관리 클래스
|
|
@@ -10,6 +11,75 @@ import { AdRendererFactory } from '../../renderers';
|
|
|
10
11
|
* - 도트 인디케이터 포함
|
|
11
12
|
*/
|
|
12
13
|
export class CarouselSliderManager {
|
|
14
|
+
/**
|
|
15
|
+
* 간단한 광고 요소 생성 (크기 측정용)
|
|
16
|
+
*/
|
|
17
|
+
private static createSimpleAdElement(slot: AdSlot, advertisement: Advertisement): HTMLElement {
|
|
18
|
+
const adElement = document.createElement('div');
|
|
19
|
+
adElement.className = `adstage-ad adstage-${String(slot.adType).toLowerCase()}`;
|
|
20
|
+
adElement.setAttribute('data-adstage-ad-id', advertisement._id);
|
|
21
|
+
adElement.setAttribute('data-adstage-slot-id', slot.id);
|
|
22
|
+
|
|
23
|
+
// 기본 스타일 설정
|
|
24
|
+
adElement.style.display = 'block';
|
|
25
|
+
adElement.style.width = '100%';
|
|
26
|
+
adElement.style.height = 'auto';
|
|
27
|
+
|
|
28
|
+
// 광고 타입별 기본 컨테이너 설정
|
|
29
|
+
switch (slot.adType) {
|
|
30
|
+
case AdType.BANNER:
|
|
31
|
+
if (advertisement.imageUrl) {
|
|
32
|
+
const img = document.createElement('img');
|
|
33
|
+
img.src = advertisement.imageUrl;
|
|
34
|
+
img.style.width = '100%';
|
|
35
|
+
img.style.height = 'auto';
|
|
36
|
+
img.style.objectFit = 'cover';
|
|
37
|
+
adElement.appendChild(img);
|
|
38
|
+
} else {
|
|
39
|
+
adElement.style.height = '100px';
|
|
40
|
+
adElement.style.backgroundColor = '#f0f0f0';
|
|
41
|
+
adElement.style.border = '1px dashed #ccc';
|
|
42
|
+
adElement.textContent = 'Banner Ad';
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
case AdType.VIDEO:
|
|
46
|
+
if (advertisement.videoUrl) {
|
|
47
|
+
const video = document.createElement('video');
|
|
48
|
+
video.src = advertisement.videoUrl;
|
|
49
|
+
video.style.width = '100%';
|
|
50
|
+
video.style.height = 'auto';
|
|
51
|
+
adElement.appendChild(video);
|
|
52
|
+
} else {
|
|
53
|
+
adElement.style.height = '200px';
|
|
54
|
+
adElement.style.backgroundColor = '#000';
|
|
55
|
+
adElement.style.border = '1px solid #666';
|
|
56
|
+
adElement.textContent = 'Video Ad';
|
|
57
|
+
adElement.style.color = 'white';
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
case AdType.TEXT:
|
|
61
|
+
if (advertisement.textContent) {
|
|
62
|
+
const textDiv = document.createElement('div');
|
|
63
|
+
textDiv.textContent = advertisement.textContent || '';
|
|
64
|
+
textDiv.style.padding = '8px';
|
|
65
|
+
textDiv.style.fontSize = '14px';
|
|
66
|
+
adElement.appendChild(textDiv);
|
|
67
|
+
} else {
|
|
68
|
+
adElement.style.height = '50px';
|
|
69
|
+
adElement.style.padding = '8px';
|
|
70
|
+
adElement.textContent = 'Text Ad';
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
default:
|
|
74
|
+
adElement.style.height = '100px';
|
|
75
|
+
adElement.style.border = '1px dashed #ccc';
|
|
76
|
+
adElement.style.backgroundColor = '#f9f9f9';
|
|
77
|
+
adElement.textContent = `${slot.adType} Ad`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return adElement;
|
|
81
|
+
}
|
|
82
|
+
|
|
13
83
|
/**
|
|
14
84
|
* Create carousel slider container with dot indicators and navigation
|
|
15
85
|
*/
|
|
@@ -17,7 +87,8 @@ export class CarouselSliderManager {
|
|
|
17
87
|
slot: AdSlot,
|
|
18
88
|
advertisements: any[],
|
|
19
89
|
options: any,
|
|
20
|
-
trackEventCallback: (adId: string, slotId: string, eventType: AdEventType) => void
|
|
90
|
+
trackEventCallback: (adId: string, slotId: string, eventType: AdEventType) => void,
|
|
91
|
+
debug: boolean = false
|
|
21
92
|
): HTMLElement {
|
|
22
93
|
const sliderWrapper = document.createElement('div');
|
|
23
94
|
sliderWrapper.className = 'adstage-slider-wrapper';
|
|
@@ -88,11 +159,7 @@ export class CarouselSliderManager {
|
|
|
88
159
|
|
|
89
160
|
// 모든 광고의 크기를 측정하여 최대 크기 찾기
|
|
90
161
|
advertisements.forEach(ad => {
|
|
91
|
-
const measureAdElement =
|
|
92
|
-
ad,
|
|
93
|
-
slot,
|
|
94
|
-
trackEventCallback
|
|
95
|
-
);
|
|
162
|
+
const measureAdElement = this.createSimpleAdElement(slot, ad);
|
|
96
163
|
measureContainer.appendChild(measureAdElement);
|
|
97
164
|
|
|
98
165
|
const rect = measureAdElement.getBoundingClientRect();
|
|
@@ -162,10 +229,16 @@ export class CarouselSliderManager {
|
|
|
162
229
|
});
|
|
163
230
|
|
|
164
231
|
// 광고 렌더링
|
|
165
|
-
const adElement =
|
|
232
|
+
const adElement = this.createSimpleAdElement(slot, ad);
|
|
233
|
+
|
|
234
|
+
// 클릭 이벤트 추가 (공통 컴포넌트 사용)
|
|
235
|
+
AdClickHandler.addClickEventForSlider(
|
|
236
|
+
adElement,
|
|
166
237
|
ad,
|
|
167
238
|
slot,
|
|
168
|
-
trackEventCallback
|
|
239
|
+
trackEventCallback,
|
|
240
|
+
debug,
|
|
241
|
+
String(slot.adType).toLowerCase()
|
|
169
242
|
);
|
|
170
243
|
|
|
171
244
|
slideElement.appendChild(adElement);
|
|
@@ -217,9 +290,14 @@ export class CarouselSliderManager {
|
|
|
217
290
|
});
|
|
218
291
|
}
|
|
219
292
|
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
|
|
293
|
+
// 🎯 공통 슬라이더 이벤트 추적 적용 (모든 슬라이드 포함)
|
|
294
|
+
SliderEventTracker.trackSlideViewable(
|
|
295
|
+
advertisements[actualIndex],
|
|
296
|
+
slot,
|
|
297
|
+
actualIndex,
|
|
298
|
+
trackEventCallback,
|
|
299
|
+
debug // debug 모드
|
|
300
|
+
);
|
|
223
301
|
};
|
|
224
302
|
|
|
225
303
|
// 무한 루프 처리 함수
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { AdEventType } from '../../types/advertisement';
|
|
2
|
+
import type { Advertisement, AdSlot } from '../../types/advertisement';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 슬라이더 이벤트 추적 공통 유틸리티
|
|
6
|
+
* - 모든 슬라이더 타입에서 일관된 VIEWABLE 이벤트 추적
|
|
7
|
+
* - 중복 방지는 상위 레벨에서 처리
|
|
8
|
+
*/
|
|
9
|
+
export class SliderEventTracker {
|
|
10
|
+
/**
|
|
11
|
+
* 슬라이드 변경 시 VIEWABLE 이벤트 추적
|
|
12
|
+
* @param advertisement 현재 슬라이드의 광고
|
|
13
|
+
* @param slot 광고 슬롯
|
|
14
|
+
* @param slideIndex 현재 슬라이드 인덱스
|
|
15
|
+
* @param trackEventCallback 이벤트 추적 콜백
|
|
16
|
+
* @param debug 디버그 모드
|
|
17
|
+
*/
|
|
18
|
+
static trackSlideViewable(
|
|
19
|
+
advertisement: Advertisement,
|
|
20
|
+
slot: AdSlot,
|
|
21
|
+
slideIndex: number,
|
|
22
|
+
trackEventCallback: (adId: string, slotId: string, eventType: AdEventType) => void,
|
|
23
|
+
debug: boolean = false
|
|
24
|
+
): void {
|
|
25
|
+
if (debug) {
|
|
26
|
+
console.log(
|
|
27
|
+
`🎯 Triggering VIEWABLE event for slide change: ad ${advertisement._id} (index: ${slideIndex}) in slot: ${slot.id}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 모든 슬라이드에 대해 VIEWABLE 이벤트 추적 (첫 번째 포함)
|
|
32
|
+
trackEventCallback(advertisement._id, slot.id, AdEventType.VIEWABLE);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 초기 슬라이드 로딩 시 VIEWABLE 이벤트 추적
|
|
37
|
+
* @param advertisement 첫 번째 슬라이드의 광고
|
|
38
|
+
* @param slot 광고 슬롯
|
|
39
|
+
* @param trackEventCallback 이벤트 추적 콜백
|
|
40
|
+
* @param debug 디버그 모드
|
|
41
|
+
*/
|
|
42
|
+
static trackInitialSlideViewable(
|
|
43
|
+
advertisement: Advertisement,
|
|
44
|
+
slot: AdSlot,
|
|
45
|
+
trackEventCallback: (adId: string, slotId: string, eventType: AdEventType) => void,
|
|
46
|
+
debug: boolean = false
|
|
47
|
+
): void {
|
|
48
|
+
if (debug) {
|
|
49
|
+
console.log(
|
|
50
|
+
`🎯 Triggering initial VIEWABLE event: ad ${advertisement._id} (index: 0) in slot: ${slot.id}`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 첫 번째 슬라이드도 동일하게 추적
|
|
55
|
+
trackEventCallback(advertisement._id, slot.id, AdEventType.VIEWABLE);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AdType, AdEventType } from '../../types/advertisement';
|
|
2
2
|
import type { AdSlot, Advertisement } from '../../types/advertisement';
|
|
3
|
-
import {
|
|
3
|
+
import { SliderEventTracker } from './slider-event-tracker';
|
|
4
|
+
import { TextAdUtils } from '../../utils/text-ad-utils';
|
|
5
|
+
import { AdClickHandler } from '../../utils/ad-click-handler';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* 텍스트 전환 효과 관리 클래스
|
|
@@ -10,14 +12,34 @@ import { AdRendererFactory } from '../../renderers';
|
|
|
10
12
|
*/
|
|
11
13
|
export class TextTransitionManager {
|
|
12
14
|
/**
|
|
13
|
-
*
|
|
15
|
+
* 간단한 광고 요소 생성 (크기 측정용)
|
|
16
|
+
*/
|
|
17
|
+
private static createSimpleAdElement(slot: AdSlot, advertisement: Advertisement): HTMLElement {
|
|
18
|
+
const adElement = document.createElement('div');
|
|
19
|
+
adElement.className = `adstage-ad adstage-${String(slot.adType).toLowerCase()}`;
|
|
20
|
+
adElement.setAttribute('data-adstage-ad-id', advertisement._id);
|
|
21
|
+
adElement.setAttribute('data-adstage-slot-id', slot.id);
|
|
22
|
+
|
|
23
|
+
// 🎯 공통 스타일 및 콘텐츠 적용 (중복 제거)
|
|
24
|
+
adElement.style.cssText = TextAdUtils.createTextAdStyles(true);
|
|
25
|
+
TextAdUtils.setTextAdContent(adElement, advertisement);
|
|
26
|
+
|
|
27
|
+
return adElement;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 텍스트 전환 컨테이너 생성
|
|
14
32
|
*/
|
|
15
33
|
static createTextTransitionContainer(
|
|
16
34
|
slot: AdSlot,
|
|
17
35
|
advertisements: Advertisement[],
|
|
18
36
|
options: any,
|
|
19
|
-
trackEventCallback: (adId: string, slotId: string, eventType: AdEventType) => void
|
|
37
|
+
trackEventCallback: (adId: string, slotId: string, eventType: AdEventType) => void,
|
|
38
|
+
debug: boolean = false
|
|
20
39
|
): HTMLElement {
|
|
40
|
+
// 사용자가 높이를 지정했는지 미리 확인 ('auto'나 undefined면 자동 높이)
|
|
41
|
+
const hasUserDefinedHeight = slot.height && slot.height !== 0 && slot.height !== 'auto';
|
|
42
|
+
|
|
21
43
|
const sliderWrapper = document.createElement('div');
|
|
22
44
|
sliderWrapper.className = 'adstage-fade-slider-wrapper';
|
|
23
45
|
|
|
@@ -25,9 +47,15 @@ export class TextTransitionManager {
|
|
|
25
47
|
const containerStyles: Record<string, string> = {
|
|
26
48
|
position: 'relative',
|
|
27
49
|
overflow: 'hidden',
|
|
28
|
-
display: '
|
|
50
|
+
display: 'block',
|
|
29
51
|
};
|
|
30
52
|
|
|
53
|
+
// 높이가 지정되지 않은 경우 자동 높이 사용
|
|
54
|
+
if (!hasUserDefinedHeight) {
|
|
55
|
+
containerStyles.height = 'auto';
|
|
56
|
+
containerStyles.minHeight = 'fit-content';
|
|
57
|
+
}
|
|
58
|
+
|
|
31
59
|
// 사용자가 크기를 지정한 경우
|
|
32
60
|
if (slot.width && slot.width !== 0) {
|
|
33
61
|
let width: string;
|
|
@@ -39,7 +67,8 @@ export class TextTransitionManager {
|
|
|
39
67
|
containerStyles.width = width;
|
|
40
68
|
}
|
|
41
69
|
|
|
42
|
-
|
|
70
|
+
// 사용자가 높이를 명시적으로 지정한 경우에만 적용 ('auto'는 제외)
|
|
71
|
+
if (slot.height && slot.height !== 0 && slot.height !== 'auto' && hasUserDefinedHeight) {
|
|
43
72
|
let height: string;
|
|
44
73
|
if (typeof slot.height === 'string') {
|
|
45
74
|
height = slot.height.includes('px') || slot.height.includes('%') ? slot.height : `${slot.height}px`;
|
|
@@ -57,18 +86,20 @@ export class TextTransitionManager {
|
|
|
57
86
|
// 슬라이드 컨테이너
|
|
58
87
|
const slideContainer = document.createElement('div');
|
|
59
88
|
slideContainer.className = 'adstage-fade-slide-container';
|
|
89
|
+
|
|
60
90
|
slideContainer.style.cssText = `
|
|
61
91
|
position: relative;
|
|
62
92
|
width: 100%;
|
|
63
|
-
height: 100%;
|
|
93
|
+
${hasUserDefinedHeight ? 'height: 100%;' : 'height: auto; min-height: fit-content;'}
|
|
64
94
|
`;
|
|
65
95
|
|
|
66
96
|
// 크기 측정을 위한 임시 컨테이너 (자동 크기 계산이 필요한 경우)
|
|
67
97
|
let measureContainer: HTMLElement | null = null;
|
|
68
98
|
const needsWidthMeasurement = !slot.width || slot.width === 0;
|
|
69
|
-
const needsHeightMeasurement = !slot.height || slot.height === 0;
|
|
99
|
+
const needsHeightMeasurement = !slot.height || slot.height === 0 || slot.height === undefined || slot.height === 'auto';
|
|
70
100
|
|
|
71
|
-
|
|
101
|
+
// 너비 측정만 필요한 경우 (높이는 자동으로 유지)
|
|
102
|
+
if (needsWidthMeasurement || (needsHeightMeasurement && hasUserDefinedHeight)) {
|
|
72
103
|
measureContainer = document.createElement('div');
|
|
73
104
|
measureContainer.style.cssText = `
|
|
74
105
|
position: absolute;
|
|
@@ -97,11 +128,7 @@ export class TextTransitionManager {
|
|
|
97
128
|
|
|
98
129
|
// 모든 광고의 크기를 측정하여 최대 크기 찾기
|
|
99
130
|
advertisements.forEach(ad => {
|
|
100
|
-
const measureAdElement =
|
|
101
|
-
ad,
|
|
102
|
-
slot,
|
|
103
|
-
trackEventCallback
|
|
104
|
-
);
|
|
131
|
+
const measureAdElement = this.createSimpleAdElement(slot, ad);
|
|
105
132
|
measureContainer!.appendChild(measureAdElement);
|
|
106
133
|
|
|
107
134
|
const rect = measureAdElement.getBoundingClientRect();
|
|
@@ -117,7 +144,8 @@ export class TextTransitionManager {
|
|
|
117
144
|
sliderWrapper.style.width = `${maxWidth}px`;
|
|
118
145
|
}
|
|
119
146
|
|
|
120
|
-
|
|
147
|
+
// 사용자가 높이를 지정하지 않은 경우 auto 높이 유지 (측정된 높이 적용하지 않음)
|
|
148
|
+
if (needsHeightMeasurement && maxHeight > 0 && hasUserDefinedHeight) {
|
|
121
149
|
sliderWrapper.style.height = `${maxHeight}px`;
|
|
122
150
|
}
|
|
123
151
|
|
|
@@ -130,26 +158,37 @@ export class TextTransitionManager {
|
|
|
130
158
|
advertisements.forEach((ad, index) => {
|
|
131
159
|
const slideElement = document.createElement('div');
|
|
132
160
|
slideElement.className = 'adstage-fade-slide';
|
|
161
|
+
|
|
162
|
+
// 자동 높이일 때는 첫 번째 슬라이드만 relative로 높이 확보
|
|
163
|
+
const isFirstSlide = index === 0;
|
|
164
|
+
const positionStyle = hasUserDefinedHeight ? 'absolute' : (isFirstSlide ? 'relative' : 'absolute');
|
|
165
|
+
|
|
133
166
|
slideElement.style.cssText = `
|
|
134
|
-
position:
|
|
135
|
-
top: 0;
|
|
136
|
-
left: 0;
|
|
167
|
+
position: ${positionStyle};
|
|
168
|
+
top: ${positionStyle === 'absolute' ? '0' : 'auto'};
|
|
169
|
+
left: ${positionStyle === 'absolute' ? '0' : 'auto'};
|
|
137
170
|
width: 100%;
|
|
138
|
-
height: 100%;
|
|
171
|
+
${hasUserDefinedHeight ? 'height: 100%;' : 'height: auto;'}
|
|
139
172
|
display: flex;
|
|
140
173
|
align-items: center;
|
|
141
|
-
justify-content:
|
|
174
|
+
justify-content: flex-start;
|
|
142
175
|
opacity: ${index === 0 ? '1' : '0'};
|
|
143
176
|
transform: translateY(${index === 0 ? '0' : '20px'});
|
|
144
177
|
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
|
145
178
|
z-index: ${index === 0 ? '2' : '1'};
|
|
146
179
|
`;
|
|
147
180
|
|
|
148
|
-
// 광고 렌더링
|
|
149
|
-
const adElement =
|
|
181
|
+
// 광고 렌더링 - 공통 함수 사용으로 일관성 유지
|
|
182
|
+
const adElement = this.createSimpleAdElement(slot, ad);
|
|
183
|
+
|
|
184
|
+
// 클릭 이벤트 추가 (공통 컴포넌트 사용)
|
|
185
|
+
AdClickHandler.addClickEventForSlider(
|
|
186
|
+
adElement,
|
|
150
187
|
ad,
|
|
151
188
|
slot,
|
|
152
|
-
trackEventCallback
|
|
189
|
+
trackEventCallback,
|
|
190
|
+
debug,
|
|
191
|
+
'Text'
|
|
153
192
|
);
|
|
154
193
|
|
|
155
194
|
slideElement.appendChild(adElement);
|
|
@@ -173,6 +212,21 @@ export class TextTransitionManager {
|
|
|
173
212
|
const previousSlide = slideElements[currentSlide];
|
|
174
213
|
const nextSlide = slideElements[index];
|
|
175
214
|
|
|
215
|
+
// 자동 높이 모드일 때는 위치 변경
|
|
216
|
+
if (!hasUserDefinedHeight) {
|
|
217
|
+
// 이전 슬라이드를 absolute로 변경
|
|
218
|
+
if (previousSlide.style.position === 'relative') {
|
|
219
|
+
previousSlide.style.position = 'absolute';
|
|
220
|
+
previousSlide.style.top = '0';
|
|
221
|
+
previousSlide.style.left = '0';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 다음 슬라이드를 relative로 변경하여 높이 확보
|
|
225
|
+
nextSlide.style.position = 'relative';
|
|
226
|
+
nextSlide.style.top = 'auto';
|
|
227
|
+
nextSlide.style.left = 'auto';
|
|
228
|
+
}
|
|
229
|
+
|
|
176
230
|
// 이전 슬라이드 페이드 아웃 (아래로)
|
|
177
231
|
previousSlide.style.opacity = '0';
|
|
178
232
|
previousSlide.style.transform = 'translateY(-20px)';
|
|
@@ -189,15 +243,26 @@ export class TextTransitionManager {
|
|
|
189
243
|
slide.style.opacity = '0';
|
|
190
244
|
slide.style.transform = 'translateY(20px)';
|
|
191
245
|
slide.style.zIndex = '1';
|
|
246
|
+
|
|
247
|
+
// 자동 높이 모드일 때는 다른 슬라이드들을 absolute로
|
|
248
|
+
if (!hasUserDefinedHeight && slide.style.position === 'relative') {
|
|
249
|
+
slide.style.position = 'absolute';
|
|
250
|
+
slide.style.top = '0';
|
|
251
|
+
slide.style.left = '0';
|
|
252
|
+
}
|
|
192
253
|
}
|
|
193
254
|
});
|
|
194
255
|
|
|
195
256
|
currentSlide = index;
|
|
196
257
|
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
258
|
+
// 🎯 공통 슬라이더 이벤트 추적 적용 (모든 슬라이드 포함)
|
|
259
|
+
SliderEventTracker.trackSlideViewable(
|
|
260
|
+
advertisements[currentSlide],
|
|
261
|
+
slot,
|
|
262
|
+
currentSlide,
|
|
263
|
+
trackEventCallback,
|
|
264
|
+
debug // debug 모드 상속
|
|
265
|
+
);
|
|
201
266
|
};
|
|
202
267
|
|
|
203
268
|
// 자동 슬라이드
|