@adstage/web-sdk 2.2.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +110 -40
- package/dist/index.d.ts +31 -5
- package/dist/index.esm.js +108 -41
- package/dist/index.standalone.js +180 -41
- package/package.json +2 -2
- package/src/core/AdStage.ts +0 -44
- package/src/index.ts +3 -0
- package/src/managers/ads/carousel-slider-manager.ts +31 -1
- package/src/react/AdStageProvider.tsx +117 -0
- package/src/react/index.ts +11 -0
package/dist/index.cjs.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
|
|
3
6
|
// 광고 타입 정의
|
|
4
7
|
var AdType;
|
|
5
8
|
(function (AdType) {
|
|
@@ -916,7 +919,7 @@ class CarouselSliderManager {
|
|
|
916
919
|
let currentSlide = 0;
|
|
917
920
|
const totalSlides = advertisements.length;
|
|
918
921
|
const autoSlideInterval = (options?.autoSlideInterval || 3) * 1000; // 기본 3초
|
|
919
|
-
// 슬라이드 이동 함수 (무한 루프 지원)
|
|
922
|
+
// 슬라이드 이동 함수 (무한 루프 지원 + 동적 높이 조정)
|
|
920
923
|
const moveToSlide = (index, instant = false) => {
|
|
921
924
|
currentSlide = index;
|
|
922
925
|
// 애니메이션 임시 비활성화 (무한 루프용)
|
|
@@ -928,6 +931,33 @@ class CarouselSliderManager {
|
|
|
928
931
|
}
|
|
929
932
|
// 항상 퍼센트 기반으로 이동
|
|
930
933
|
slideContainer.style.transform = `translateX(-${(100 / extendedAds.length) * currentSlide}%)`;
|
|
934
|
+
// 🆕 동적 높이 조정: 현재 슬라이드의 이미지 높이에 맞춰 컨테이너 높이 조정
|
|
935
|
+
if (!instant && !slot.height && !slot.width) { // 사용자가 크기를 지정하지 않은 경우에만
|
|
936
|
+
const currentSlideElement = slideContainer.children[currentSlide];
|
|
937
|
+
if (currentSlideElement) {
|
|
938
|
+
const currentAdElement = currentSlideElement.children[0];
|
|
939
|
+
if (currentAdElement) {
|
|
940
|
+
// 이미지 요소 찾기
|
|
941
|
+
const imgElement = currentAdElement.querySelector('img');
|
|
942
|
+
if (imgElement) {
|
|
943
|
+
// 이미지 로드 완료 후 높이 조정
|
|
944
|
+
const adjustHeight = () => {
|
|
945
|
+
const imgHeight = imgElement.getBoundingClientRect().height;
|
|
946
|
+
if (imgHeight > 0) {
|
|
947
|
+
sliderWrapper.style.height = `${imgHeight}px`;
|
|
948
|
+
sliderWrapper.style.transition = instant ? 'none' : 'height 0.4s ease-out';
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
if (imgElement.complete && imgElement.naturalHeight > 0) {
|
|
952
|
+
adjustHeight();
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
imgElement.addEventListener('load', adjustHeight, { once: true });
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
931
961
|
// 도트 업데이트 (무채색 스타일) - 실제 광고 인덱스 기준, 텍스트 광고가 아닐 때만
|
|
932
962
|
const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
|
|
933
963
|
if (dotContainer) {
|
|
@@ -3252,53 +3282,14 @@ class AdStage {
|
|
|
3252
3282
|
* 편의성을 위한 정적 모듈 접근자들
|
|
3253
3283
|
*/
|
|
3254
3284
|
static get ads() {
|
|
3255
|
-
// 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
|
|
3256
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3257
|
-
AdStage.autoInit();
|
|
3258
|
-
}
|
|
3259
3285
|
return AdStage.getInstance().ads;
|
|
3260
3286
|
}
|
|
3261
3287
|
static get events() {
|
|
3262
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3263
|
-
AdStage.autoInit();
|
|
3264
|
-
}
|
|
3265
3288
|
return AdStage.getInstance().events;
|
|
3266
3289
|
}
|
|
3267
3290
|
static get config() {
|
|
3268
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3269
|
-
AdStage.autoInit();
|
|
3270
|
-
}
|
|
3271
3291
|
return AdStage.getInstance().config;
|
|
3272
3292
|
}
|
|
3273
|
-
/**
|
|
3274
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
3275
|
-
*/
|
|
3276
|
-
static autoInit() {
|
|
3277
|
-
if (typeof window === 'undefined')
|
|
3278
|
-
return; // 서버 사이드에서는 실행 안함
|
|
3279
|
-
try {
|
|
3280
|
-
// 환경변수에서 API 키 자동 로드
|
|
3281
|
-
const apiKey = window.__ADSTAGE_API_KEY__ || // 런타임 설정
|
|
3282
|
-
process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
|
|
3283
|
-
process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
|
|
3284
|
-
process.env.VITE_ADSTAGE_API_KEY || // Vite
|
|
3285
|
-
'demo-api-key'; // 개발용 fallback
|
|
3286
|
-
const isDevelopment = "production" === 'development' ||
|
|
3287
|
-
window.__ADSTAGE_DEBUG__ === true;
|
|
3288
|
-
AdStage.init({
|
|
3289
|
-
apiKey: apiKey,
|
|
3290
|
-
debug: isDevelopment,
|
|
3291
|
-
modules: ['ads', 'config']
|
|
3292
|
-
});
|
|
3293
|
-
if (isDevelopment) {
|
|
3294
|
-
console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
catch (error) {
|
|
3298
|
-
console.warn('AdStage auto-initialization failed:', error);
|
|
3299
|
-
console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
|
|
3300
|
-
}
|
|
3301
|
-
}
|
|
3302
3293
|
/**
|
|
3303
3294
|
* SDK 리셋 (테스트용)
|
|
3304
3295
|
*/
|
|
@@ -3310,6 +3301,82 @@ class AdStage {
|
|
|
3310
3301
|
}
|
|
3311
3302
|
}
|
|
3312
3303
|
|
|
3304
|
+
const AdStageContext = react.createContext(null);
|
|
3305
|
+
function AdStageProvider({ children, config }) {
|
|
3306
|
+
const [isInitialized, setIsInitialized] = react.useState(false);
|
|
3307
|
+
const [currentConfig, setCurrentConfig] = react.useState(null);
|
|
3308
|
+
const [error, setError] = react.useState(null);
|
|
3309
|
+
const initialize = (newConfig) => {
|
|
3310
|
+
try {
|
|
3311
|
+
setError(null);
|
|
3312
|
+
// 기존 인스턴스가 있으면 리셋
|
|
3313
|
+
if (isInitialized) {
|
|
3314
|
+
AdStage.reset();
|
|
3315
|
+
}
|
|
3316
|
+
AdStage.init(newConfig);
|
|
3317
|
+
setCurrentConfig(newConfig);
|
|
3318
|
+
setIsInitialized(true);
|
|
3319
|
+
if (newConfig.debug) {
|
|
3320
|
+
console.log('✅ AdStage SDK initialized successfully via React Provider');
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
catch (err) {
|
|
3324
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
3325
|
+
setError(errorMessage);
|
|
3326
|
+
console.error('❌ AdStage SDK initialization failed:', err);
|
|
3327
|
+
setIsInitialized(false);
|
|
3328
|
+
setCurrentConfig(null);
|
|
3329
|
+
}
|
|
3330
|
+
};
|
|
3331
|
+
const reset = () => {
|
|
3332
|
+
try {
|
|
3333
|
+
AdStage.reset();
|
|
3334
|
+
setIsInitialized(false);
|
|
3335
|
+
setCurrentConfig(null);
|
|
3336
|
+
setError(null);
|
|
3337
|
+
}
|
|
3338
|
+
catch (err) {
|
|
3339
|
+
console.error('❌ AdStage SDK reset failed:', err);
|
|
3340
|
+
}
|
|
3341
|
+
};
|
|
3342
|
+
// 자동 초기화
|
|
3343
|
+
react.useEffect(() => {
|
|
3344
|
+
if (config && !isInitialized) {
|
|
3345
|
+
initialize(config);
|
|
3346
|
+
}
|
|
3347
|
+
}, [config, isInitialized]);
|
|
3348
|
+
const contextValue = {
|
|
3349
|
+
isInitialized,
|
|
3350
|
+
config: currentConfig,
|
|
3351
|
+
initialize,
|
|
3352
|
+
reset,
|
|
3353
|
+
error
|
|
3354
|
+
};
|
|
3355
|
+
return (jsxRuntime.jsx(AdStageContext.Provider, { value: contextValue, children: children }));
|
|
3356
|
+
}
|
|
3357
|
+
/**
|
|
3358
|
+
* AdStage Context Hook
|
|
3359
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
3360
|
+
*/
|
|
3361
|
+
function useAdStage() {
|
|
3362
|
+
const context = react.useContext(AdStageContext);
|
|
3363
|
+
if (!context) {
|
|
3364
|
+
throw new Error('useAdStage must be used within an AdStageProvider');
|
|
3365
|
+
}
|
|
3366
|
+
return context;
|
|
3367
|
+
}
|
|
3368
|
+
/**
|
|
3369
|
+
* AdStage SDK Hook
|
|
3370
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
3371
|
+
*/
|
|
3372
|
+
function useAdStageSDK() {
|
|
3373
|
+
const { isInitialized } = useAdStage();
|
|
3374
|
+
if (!isInitialized) {
|
|
3375
|
+
throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
|
|
3376
|
+
}
|
|
3377
|
+
return AdStage;
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3313
3380
|
/**
|
|
3314
3381
|
* AdStage Web SDK
|
|
3315
3382
|
* 네임스페이스 아키텍처 기반 SDK
|
|
@@ -3320,5 +3387,8 @@ const SDK_VERSION = '2.0.0';
|
|
|
3320
3387
|
const SUPPORTED_MODULES = ['ads', 'events', 'config'];
|
|
3321
3388
|
|
|
3322
3389
|
exports.AdStage = AdStage;
|
|
3390
|
+
exports.AdStageProvider = AdStageProvider;
|
|
3323
3391
|
exports.SDK_VERSION = SDK_VERSION;
|
|
3324
3392
|
exports.SUPPORTED_MODULES = SUPPORTED_MODULES;
|
|
3393
|
+
exports.useAdStage = useAdStage;
|
|
3394
|
+
exports.useAdStageSDK = useAdStageSDK;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* AdStage SDK 모듈 타입
|
|
3
6
|
*/
|
|
@@ -513,16 +516,39 @@ declare class AdStage {
|
|
|
513
516
|
static get ads(): AdsModule;
|
|
514
517
|
static get events(): EventsModule;
|
|
515
518
|
static get config(): ConfigModule;
|
|
516
|
-
/**
|
|
517
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
518
|
-
*/
|
|
519
|
-
private static autoInit;
|
|
520
519
|
/**
|
|
521
520
|
* SDK 리셋 (테스트용)
|
|
522
521
|
*/
|
|
523
522
|
static reset(): void;
|
|
524
523
|
}
|
|
525
524
|
|
|
525
|
+
interface AdStageContextType {
|
|
526
|
+
isInitialized: boolean;
|
|
527
|
+
config: AdStageConfig | null;
|
|
528
|
+
initialize: (config: AdStageConfig) => void;
|
|
529
|
+
reset: () => void;
|
|
530
|
+
error: string | null;
|
|
531
|
+
}
|
|
532
|
+
interface AdStageProviderProps {
|
|
533
|
+
children: ReactNode;
|
|
534
|
+
/**
|
|
535
|
+
* 자동 초기화를 위한 설정 (선택사항)
|
|
536
|
+
* 제공되면 Provider 마운트 시 자동으로 초기화됩니다.
|
|
537
|
+
*/
|
|
538
|
+
config?: AdStageConfig;
|
|
539
|
+
}
|
|
540
|
+
declare function AdStageProvider({ children, config }: AdStageProviderProps): react_jsx_runtime.JSX.Element;
|
|
541
|
+
/**
|
|
542
|
+
* AdStage Context Hook
|
|
543
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
544
|
+
*/
|
|
545
|
+
declare function useAdStage(): AdStageContextType;
|
|
546
|
+
/**
|
|
547
|
+
* AdStage SDK Hook
|
|
548
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
549
|
+
*/
|
|
550
|
+
declare function useAdStageSDK(): typeof AdStage;
|
|
551
|
+
|
|
526
552
|
/**
|
|
527
553
|
* AdStage Web SDK
|
|
528
554
|
* 네임스페이스 아키텍처 기반 SDK
|
|
@@ -531,4 +557,4 @@ declare class AdStage {
|
|
|
531
557
|
declare const SDK_VERSION = "2.0.0";
|
|
532
558
|
declare const SUPPORTED_MODULES: readonly ["ads", "events", "config"];
|
|
533
559
|
|
|
534
|
-
export { AdEventType, AdOptions, AdSlot, AdStage, AdStageConfig, AdType, Advertisement, ApiResponse, BaseModule, EventProperties, ModuleName, OrganizationInfo, PageData, SDK_VERSION, SUPPORTED_MODULES };
|
|
560
|
+
export { AdEventType, AdOptions, AdSlot, AdStage, AdStageConfig, AdStageProvider, AdType, Advertisement, ApiResponse, BaseModule, EventProperties, ModuleName, OrganizationInfo, PageData, SDK_VERSION, SUPPORTED_MODULES, useAdStage, useAdStageSDK };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { createContext, useState, useEffect, useContext } from 'react';
|
|
3
|
+
|
|
1
4
|
// 광고 타입 정의
|
|
2
5
|
var AdType;
|
|
3
6
|
(function (AdType) {
|
|
@@ -914,7 +917,7 @@ class CarouselSliderManager {
|
|
|
914
917
|
let currentSlide = 0;
|
|
915
918
|
const totalSlides = advertisements.length;
|
|
916
919
|
const autoSlideInterval = (options?.autoSlideInterval || 3) * 1000; // 기본 3초
|
|
917
|
-
// 슬라이드 이동 함수 (무한 루프 지원)
|
|
920
|
+
// 슬라이드 이동 함수 (무한 루프 지원 + 동적 높이 조정)
|
|
918
921
|
const moveToSlide = (index, instant = false) => {
|
|
919
922
|
currentSlide = index;
|
|
920
923
|
// 애니메이션 임시 비활성화 (무한 루프용)
|
|
@@ -926,6 +929,33 @@ class CarouselSliderManager {
|
|
|
926
929
|
}
|
|
927
930
|
// 항상 퍼센트 기반으로 이동
|
|
928
931
|
slideContainer.style.transform = `translateX(-${(100 / extendedAds.length) * currentSlide}%)`;
|
|
932
|
+
// 🆕 동적 높이 조정: 현재 슬라이드의 이미지 높이에 맞춰 컨테이너 높이 조정
|
|
933
|
+
if (!instant && !slot.height && !slot.width) { // 사용자가 크기를 지정하지 않은 경우에만
|
|
934
|
+
const currentSlideElement = slideContainer.children[currentSlide];
|
|
935
|
+
if (currentSlideElement) {
|
|
936
|
+
const currentAdElement = currentSlideElement.children[0];
|
|
937
|
+
if (currentAdElement) {
|
|
938
|
+
// 이미지 요소 찾기
|
|
939
|
+
const imgElement = currentAdElement.querySelector('img');
|
|
940
|
+
if (imgElement) {
|
|
941
|
+
// 이미지 로드 완료 후 높이 조정
|
|
942
|
+
const adjustHeight = () => {
|
|
943
|
+
const imgHeight = imgElement.getBoundingClientRect().height;
|
|
944
|
+
if (imgHeight > 0) {
|
|
945
|
+
sliderWrapper.style.height = `${imgHeight}px`;
|
|
946
|
+
sliderWrapper.style.transition = instant ? 'none' : 'height 0.4s ease-out';
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
if (imgElement.complete && imgElement.naturalHeight > 0) {
|
|
950
|
+
adjustHeight();
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
imgElement.addEventListener('load', adjustHeight, { once: true });
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
929
959
|
// 도트 업데이트 (무채색 스타일) - 실제 광고 인덱스 기준, 텍스트 광고가 아닐 때만
|
|
930
960
|
const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
|
|
931
961
|
if (dotContainer) {
|
|
@@ -3250,53 +3280,14 @@ class AdStage {
|
|
|
3250
3280
|
* 편의성을 위한 정적 모듈 접근자들
|
|
3251
3281
|
*/
|
|
3252
3282
|
static get ads() {
|
|
3253
|
-
// 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
|
|
3254
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3255
|
-
AdStage.autoInit();
|
|
3256
|
-
}
|
|
3257
3283
|
return AdStage.getInstance().ads;
|
|
3258
3284
|
}
|
|
3259
3285
|
static get events() {
|
|
3260
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3261
|
-
AdStage.autoInit();
|
|
3262
|
-
}
|
|
3263
3286
|
return AdStage.getInstance().events;
|
|
3264
3287
|
}
|
|
3265
3288
|
static get config() {
|
|
3266
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3267
|
-
AdStage.autoInit();
|
|
3268
|
-
}
|
|
3269
3289
|
return AdStage.getInstance().config;
|
|
3270
3290
|
}
|
|
3271
|
-
/**
|
|
3272
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
3273
|
-
*/
|
|
3274
|
-
static autoInit() {
|
|
3275
|
-
if (typeof window === 'undefined')
|
|
3276
|
-
return; // 서버 사이드에서는 실행 안함
|
|
3277
|
-
try {
|
|
3278
|
-
// 환경변수에서 API 키 자동 로드
|
|
3279
|
-
const apiKey = window.__ADSTAGE_API_KEY__ || // 런타임 설정
|
|
3280
|
-
process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
|
|
3281
|
-
process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
|
|
3282
|
-
process.env.VITE_ADSTAGE_API_KEY || // Vite
|
|
3283
|
-
'demo-api-key'; // 개발용 fallback
|
|
3284
|
-
const isDevelopment = "production" === 'development' ||
|
|
3285
|
-
window.__ADSTAGE_DEBUG__ === true;
|
|
3286
|
-
AdStage.init({
|
|
3287
|
-
apiKey: apiKey,
|
|
3288
|
-
debug: isDevelopment,
|
|
3289
|
-
modules: ['ads', 'config']
|
|
3290
|
-
});
|
|
3291
|
-
if (isDevelopment) {
|
|
3292
|
-
console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
|
|
3293
|
-
}
|
|
3294
|
-
}
|
|
3295
|
-
catch (error) {
|
|
3296
|
-
console.warn('AdStage auto-initialization failed:', error);
|
|
3297
|
-
console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
|
|
3298
|
-
}
|
|
3299
|
-
}
|
|
3300
3291
|
/**
|
|
3301
3292
|
* SDK 리셋 (테스트용)
|
|
3302
3293
|
*/
|
|
@@ -3308,6 +3299,82 @@ class AdStage {
|
|
|
3308
3299
|
}
|
|
3309
3300
|
}
|
|
3310
3301
|
|
|
3302
|
+
const AdStageContext = createContext(null);
|
|
3303
|
+
function AdStageProvider({ children, config }) {
|
|
3304
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
3305
|
+
const [currentConfig, setCurrentConfig] = useState(null);
|
|
3306
|
+
const [error, setError] = useState(null);
|
|
3307
|
+
const initialize = (newConfig) => {
|
|
3308
|
+
try {
|
|
3309
|
+
setError(null);
|
|
3310
|
+
// 기존 인스턴스가 있으면 리셋
|
|
3311
|
+
if (isInitialized) {
|
|
3312
|
+
AdStage.reset();
|
|
3313
|
+
}
|
|
3314
|
+
AdStage.init(newConfig);
|
|
3315
|
+
setCurrentConfig(newConfig);
|
|
3316
|
+
setIsInitialized(true);
|
|
3317
|
+
if (newConfig.debug) {
|
|
3318
|
+
console.log('✅ AdStage SDK initialized successfully via React Provider');
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
catch (err) {
|
|
3322
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
3323
|
+
setError(errorMessage);
|
|
3324
|
+
console.error('❌ AdStage SDK initialization failed:', err);
|
|
3325
|
+
setIsInitialized(false);
|
|
3326
|
+
setCurrentConfig(null);
|
|
3327
|
+
}
|
|
3328
|
+
};
|
|
3329
|
+
const reset = () => {
|
|
3330
|
+
try {
|
|
3331
|
+
AdStage.reset();
|
|
3332
|
+
setIsInitialized(false);
|
|
3333
|
+
setCurrentConfig(null);
|
|
3334
|
+
setError(null);
|
|
3335
|
+
}
|
|
3336
|
+
catch (err) {
|
|
3337
|
+
console.error('❌ AdStage SDK reset failed:', err);
|
|
3338
|
+
}
|
|
3339
|
+
};
|
|
3340
|
+
// 자동 초기화
|
|
3341
|
+
useEffect(() => {
|
|
3342
|
+
if (config && !isInitialized) {
|
|
3343
|
+
initialize(config);
|
|
3344
|
+
}
|
|
3345
|
+
}, [config, isInitialized]);
|
|
3346
|
+
const contextValue = {
|
|
3347
|
+
isInitialized,
|
|
3348
|
+
config: currentConfig,
|
|
3349
|
+
initialize,
|
|
3350
|
+
reset,
|
|
3351
|
+
error
|
|
3352
|
+
};
|
|
3353
|
+
return (jsx(AdStageContext.Provider, { value: contextValue, children: children }));
|
|
3354
|
+
}
|
|
3355
|
+
/**
|
|
3356
|
+
* AdStage Context Hook
|
|
3357
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
3358
|
+
*/
|
|
3359
|
+
function useAdStage() {
|
|
3360
|
+
const context = useContext(AdStageContext);
|
|
3361
|
+
if (!context) {
|
|
3362
|
+
throw new Error('useAdStage must be used within an AdStageProvider');
|
|
3363
|
+
}
|
|
3364
|
+
return context;
|
|
3365
|
+
}
|
|
3366
|
+
/**
|
|
3367
|
+
* AdStage SDK Hook
|
|
3368
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
3369
|
+
*/
|
|
3370
|
+
function useAdStageSDK() {
|
|
3371
|
+
const { isInitialized } = useAdStage();
|
|
3372
|
+
if (!isInitialized) {
|
|
3373
|
+
throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
|
|
3374
|
+
}
|
|
3375
|
+
return AdStage;
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3311
3378
|
/**
|
|
3312
3379
|
* AdStage Web SDK
|
|
3313
3380
|
* 네임스페이스 아키텍처 기반 SDK
|
|
@@ -3317,4 +3384,4 @@ class AdStage {
|
|
|
3317
3384
|
const SDK_VERSION = '2.0.0';
|
|
3318
3385
|
const SUPPORTED_MODULES = ['ads', 'events', 'config'];
|
|
3319
3386
|
|
|
3320
|
-
export { AdStage, SDK_VERSION, SUPPORTED_MODULES };
|
|
3387
|
+
export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStage, useAdStageSDK };
|
package/dist/index.standalone.js
CHANGED
|
@@ -914,7 +914,7 @@ class CarouselSliderManager {
|
|
|
914
914
|
let currentSlide = 0;
|
|
915
915
|
const totalSlides = advertisements.length;
|
|
916
916
|
const autoSlideInterval = (options?.autoSlideInterval || 3) * 1000; // 기본 3초
|
|
917
|
-
// 슬라이드 이동 함수 (무한 루프 지원)
|
|
917
|
+
// 슬라이드 이동 함수 (무한 루프 지원 + 동적 높이 조정)
|
|
918
918
|
const moveToSlide = (index, instant = false) => {
|
|
919
919
|
currentSlide = index;
|
|
920
920
|
// 애니메이션 임시 비활성화 (무한 루프용)
|
|
@@ -926,6 +926,33 @@ class CarouselSliderManager {
|
|
|
926
926
|
}
|
|
927
927
|
// 항상 퍼센트 기반으로 이동
|
|
928
928
|
slideContainer.style.transform = `translateX(-${(100 / extendedAds.length) * currentSlide}%)`;
|
|
929
|
+
// 🆕 동적 높이 조정: 현재 슬라이드의 이미지 높이에 맞춰 컨테이너 높이 조정
|
|
930
|
+
if (!instant && !slot.height && !slot.width) { // 사용자가 크기를 지정하지 않은 경우에만
|
|
931
|
+
const currentSlideElement = slideContainer.children[currentSlide];
|
|
932
|
+
if (currentSlideElement) {
|
|
933
|
+
const currentAdElement = currentSlideElement.children[0];
|
|
934
|
+
if (currentAdElement) {
|
|
935
|
+
// 이미지 요소 찾기
|
|
936
|
+
const imgElement = currentAdElement.querySelector('img');
|
|
937
|
+
if (imgElement) {
|
|
938
|
+
// 이미지 로드 완료 후 높이 조정
|
|
939
|
+
const adjustHeight = () => {
|
|
940
|
+
const imgHeight = imgElement.getBoundingClientRect().height;
|
|
941
|
+
if (imgHeight > 0) {
|
|
942
|
+
sliderWrapper.style.height = `${imgHeight}px`;
|
|
943
|
+
sliderWrapper.style.transition = instant ? 'none' : 'height 0.4s ease-out';
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
if (imgElement.complete && imgElement.naturalHeight > 0) {
|
|
947
|
+
adjustHeight();
|
|
948
|
+
}
|
|
949
|
+
else {
|
|
950
|
+
imgElement.addEventListener('load', adjustHeight, { once: true });
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
929
956
|
// 도트 업데이트 (무채색 스타일) - 실제 광고 인덱스 기준, 텍스트 광고가 아닐 때만
|
|
930
957
|
const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
|
|
931
958
|
if (dotContainer) {
|
|
@@ -3250,53 +3277,14 @@ class AdStage {
|
|
|
3250
3277
|
* 편의성을 위한 정적 모듈 접근자들
|
|
3251
3278
|
*/
|
|
3252
3279
|
static get ads() {
|
|
3253
|
-
// 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
|
|
3254
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3255
|
-
AdStage.autoInit();
|
|
3256
|
-
}
|
|
3257
3280
|
return AdStage.getInstance().ads;
|
|
3258
3281
|
}
|
|
3259
3282
|
static get events() {
|
|
3260
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3261
|
-
AdStage.autoInit();
|
|
3262
|
-
}
|
|
3263
3283
|
return AdStage.getInstance().events;
|
|
3264
3284
|
}
|
|
3265
3285
|
static get config() {
|
|
3266
|
-
if (!AdStage.instance?._isInitialized) {
|
|
3267
|
-
AdStage.autoInit();
|
|
3268
|
-
}
|
|
3269
3286
|
return AdStage.getInstance().config;
|
|
3270
3287
|
}
|
|
3271
|
-
/**
|
|
3272
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
3273
|
-
*/
|
|
3274
|
-
static autoInit() {
|
|
3275
|
-
if (typeof window === 'undefined')
|
|
3276
|
-
return; // 서버 사이드에서는 실행 안함
|
|
3277
|
-
try {
|
|
3278
|
-
// 환경변수에서 API 키 자동 로드
|
|
3279
|
-
const apiKey = window.__ADSTAGE_API_KEY__ || // 런타임 설정
|
|
3280
|
-
process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
|
|
3281
|
-
process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
|
|
3282
|
-
process.env.VITE_ADSTAGE_API_KEY || // Vite
|
|
3283
|
-
'demo-api-key'; // 개발용 fallback
|
|
3284
|
-
const isDevelopment = "production" === 'development' ||
|
|
3285
|
-
window.__ADSTAGE_DEBUG__ === true;
|
|
3286
|
-
AdStage.init({
|
|
3287
|
-
apiKey: apiKey,
|
|
3288
|
-
debug: isDevelopment,
|
|
3289
|
-
modules: ['ads', 'config']
|
|
3290
|
-
});
|
|
3291
|
-
if (isDevelopment) {
|
|
3292
|
-
console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
|
|
3293
|
-
}
|
|
3294
|
-
}
|
|
3295
|
-
catch (error) {
|
|
3296
|
-
console.warn('AdStage auto-initialization failed:', error);
|
|
3297
|
-
console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
|
|
3298
|
-
}
|
|
3299
|
-
}
|
|
3300
3288
|
/**
|
|
3301
3289
|
* SDK 리셋 (테스트용)
|
|
3302
3290
|
*/
|
|
@@ -3308,6 +3296,157 @@ class AdStage {
|
|
|
3308
3296
|
}
|
|
3309
3297
|
}
|
|
3310
3298
|
|
|
3299
|
+
var jsxRuntime = {exports: {}};
|
|
3300
|
+
|
|
3301
|
+
var reactJsxRuntime_production_min = {};
|
|
3302
|
+
|
|
3303
|
+
var react = {exports: {}};
|
|
3304
|
+
|
|
3305
|
+
var react_production_min = {};
|
|
3306
|
+
|
|
3307
|
+
/**
|
|
3308
|
+
* @license React
|
|
3309
|
+
* react.production.min.js
|
|
3310
|
+
*
|
|
3311
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3312
|
+
*
|
|
3313
|
+
* This source code is licensed under the MIT license found in the
|
|
3314
|
+
* LICENSE file in the root directory of this source tree.
|
|
3315
|
+
*/
|
|
3316
|
+
|
|
3317
|
+
var hasRequiredReact_production_min;
|
|
3318
|
+
|
|
3319
|
+
function requireReact_production_min () {
|
|
3320
|
+
if (hasRequiredReact_production_min) return react_production_min;
|
|
3321
|
+
hasRequiredReact_production_min = 1;
|
|
3322
|
+
var l=Symbol.for("react.element"),n=Symbol.for("react.portal"),p=Symbol.for("react.fragment"),q=Symbol.for("react.strict_mode"),r=Symbol.for("react.profiler"),t=Symbol.for("react.provider"),u=Symbol.for("react.context"),v=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),x=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),z=Symbol.iterator;function A(a){if(null===a||"object"!==typeof a)return null;a=z&&a[z]||a["@@iterator"];return "function"===typeof a?a:null}
|
|
3323
|
+
var B={isMounted:function(){return !1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},C=Object.assign,D={};function E(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B;}E.prototype.isReactComponent={};
|
|
3324
|
+
E.prototype.setState=function(a,b){if("object"!==typeof a&&"function"!==typeof a&&null!=a)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,a,b,"setState");};E.prototype.forceUpdate=function(a){this.updater.enqueueForceUpdate(this,a,"forceUpdate");};function F(){}F.prototype=E.prototype;function G(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B;}var H=G.prototype=new F;
|
|
3325
|
+
H.constructor=G;C(H,E.prototype);H.isPureReactComponent=!0;var I=Array.isArray,J=Object.prototype.hasOwnProperty,K={current:null},L={key:!0,ref:!0,__self:!0,__source:!0};
|
|
3326
|
+
function M(a,b,e){var d,c={},k=null,h=null;if(null!=b)for(d in void 0!==b.ref&&(h=b.ref),void 0!==b.key&&(k=""+b.key),b)J.call(b,d)&&!L.hasOwnProperty(d)&&(c[d]=b[d]);var g=arguments.length-2;if(1===g)c.children=e;else if(1<g){for(var f=Array(g),m=0;m<g;m++)f[m]=arguments[m+2];c.children=f;}if(a&&a.defaultProps)for(d in g=a.defaultProps,g)void 0===c[d]&&(c[d]=g[d]);return {$$typeof:l,type:a,key:k,ref:h,props:c,_owner:K.current}}
|
|
3327
|
+
function N(a,b){return {$$typeof:l,type:a.type,key:b,ref:a.ref,props:a.props,_owner:a._owner}}function O(a){return "object"===typeof a&&null!==a&&a.$$typeof===l}function escape(a){var b={"=":"=0",":":"=2"};return "$"+a.replace(/[=:]/g,function(a){return b[a]})}var P=/\/+/g;function Q(a,b){return "object"===typeof a&&null!==a&&null!=a.key?escape(""+a.key):b.toString(36)}
|
|
3328
|
+
function R(a,b,e,d,c){var k=typeof a;if("undefined"===k||"boolean"===k)a=null;var h=!1;if(null===a)h=!0;else switch(k){case "string":case "number":h=!0;break;case "object":switch(a.$$typeof){case l:case n:h=!0;}}if(h)return h=a,c=c(h),a=""===d?"."+Q(h,0):d,I(c)?(e="",null!=a&&(e=a.replace(P,"$&/")+"/"),R(c,b,e,"",function(a){return a})):null!=c&&(O(c)&&(c=N(c,e+(!c.key||h&&h.key===c.key?"":(""+c.key).replace(P,"$&/")+"/")+a)),b.push(c)),1;h=0;d=""===d?".":d+":";if(I(a))for(var g=0;g<a.length;g++){k=
|
|
3329
|
+
a[g];var f=d+Q(k,g);h+=R(k,b,e,f,c);}else if(f=A(a),"function"===typeof f)for(a=f.call(a),g=0;!(k=a.next()).done;)k=k.value,f=d+Q(k,g++),h+=R(k,b,e,f,c);else if("object"===k)throw b=String(a),Error("Objects are not valid as a React child (found: "+("[object Object]"===b?"object with keys {"+Object.keys(a).join(", ")+"}":b)+"). If you meant to render a collection of children, use an array instead.");return h}
|
|
3330
|
+
function S(a,b,e){if(null==a)return a;var d=[],c=0;R(a,d,"","",function(a){return b.call(e,a,c++)});return d}function T(a){if(-1===a._status){var b=a._result;b=b();b.then(function(b){if(0===a._status||-1===a._status)a._status=1,a._result=b;},function(b){if(0===a._status||-1===a._status)a._status=2,a._result=b;});-1===a._status&&(a._status=0,a._result=b);}if(1===a._status)return a._result.default;throw a._result;}
|
|
3331
|
+
var U={current:null},V={transition:null},W={ReactCurrentDispatcher:U,ReactCurrentBatchConfig:V,ReactCurrentOwner:K};function X(){throw Error("act(...) is not supported in production builds of React.");}
|
|
3332
|
+
react_production_min.Children={map:S,forEach:function(a,b,e){S(a,function(){b.apply(this,arguments);},e);},count:function(a){var b=0;S(a,function(){b++;});return b},toArray:function(a){return S(a,function(a){return a})||[]},only:function(a){if(!O(a))throw Error("React.Children.only expected to receive a single React element child.");return a}};react_production_min.Component=E;react_production_min.Fragment=p;react_production_min.Profiler=r;react_production_min.PureComponent=G;react_production_min.StrictMode=q;react_production_min.Suspense=w;
|
|
3333
|
+
react_production_min.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=W;react_production_min.act=X;
|
|
3334
|
+
react_production_min.cloneElement=function(a,b,e){if(null===a||void 0===a)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+a+".");var d=C({},a.props),c=a.key,k=a.ref,h=a._owner;if(null!=b){void 0!==b.ref&&(k=b.ref,h=K.current);void 0!==b.key&&(c=""+b.key);if(a.type&&a.type.defaultProps)var g=a.type.defaultProps;for(f in b)J.call(b,f)&&!L.hasOwnProperty(f)&&(d[f]=void 0===b[f]&&void 0!==g?g[f]:b[f]);}var f=arguments.length-2;if(1===f)d.children=e;else if(1<f){g=Array(f);
|
|
3335
|
+
for(var m=0;m<f;m++)g[m]=arguments[m+2];d.children=g;}return {$$typeof:l,type:a.type,key:c,ref:k,props:d,_owner:h}};react_production_min.createContext=function(a){a={$$typeof:u,_currentValue:a,_currentValue2:a,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null};a.Provider={$$typeof:t,_context:a};return a.Consumer=a};react_production_min.createElement=M;react_production_min.createFactory=function(a){var b=M.bind(null,a);b.type=a;return b};react_production_min.createRef=function(){return {current:null}};
|
|
3336
|
+
react_production_min.forwardRef=function(a){return {$$typeof:v,render:a}};react_production_min.isValidElement=O;react_production_min.lazy=function(a){return {$$typeof:y,_payload:{_status:-1,_result:a},_init:T}};react_production_min.memo=function(a,b){return {$$typeof:x,type:a,compare:void 0===b?null:b}};react_production_min.startTransition=function(a){var b=V.transition;V.transition={};try{a();}finally{V.transition=b;}};react_production_min.unstable_act=X;react_production_min.useCallback=function(a,b){return U.current.useCallback(a,b)};react_production_min.useContext=function(a){return U.current.useContext(a)};
|
|
3337
|
+
react_production_min.useDebugValue=function(){};react_production_min.useDeferredValue=function(a){return U.current.useDeferredValue(a)};react_production_min.useEffect=function(a,b){return U.current.useEffect(a,b)};react_production_min.useId=function(){return U.current.useId()};react_production_min.useImperativeHandle=function(a,b,e){return U.current.useImperativeHandle(a,b,e)};react_production_min.useInsertionEffect=function(a,b){return U.current.useInsertionEffect(a,b)};react_production_min.useLayoutEffect=function(a,b){return U.current.useLayoutEffect(a,b)};
|
|
3338
|
+
react_production_min.useMemo=function(a,b){return U.current.useMemo(a,b)};react_production_min.useReducer=function(a,b,e){return U.current.useReducer(a,b,e)};react_production_min.useRef=function(a){return U.current.useRef(a)};react_production_min.useState=function(a){return U.current.useState(a)};react_production_min.useSyncExternalStore=function(a,b,e){return U.current.useSyncExternalStore(a,b,e)};react_production_min.useTransition=function(){return U.current.useTransition()};react_production_min.version="18.3.1";
|
|
3339
|
+
return react_production_min;
|
|
3340
|
+
}
|
|
3341
|
+
|
|
3342
|
+
{
|
|
3343
|
+
react.exports = requireReact_production_min();
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3346
|
+
var reactExports = react.exports;
|
|
3347
|
+
|
|
3348
|
+
/**
|
|
3349
|
+
* @license React
|
|
3350
|
+
* react-jsx-runtime.production.min.js
|
|
3351
|
+
*
|
|
3352
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3353
|
+
*
|
|
3354
|
+
* This source code is licensed under the MIT license found in the
|
|
3355
|
+
* LICENSE file in the root directory of this source tree.
|
|
3356
|
+
*/
|
|
3357
|
+
|
|
3358
|
+
var hasRequiredReactJsxRuntime_production_min;
|
|
3359
|
+
|
|
3360
|
+
function requireReactJsxRuntime_production_min () {
|
|
3361
|
+
if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min;
|
|
3362
|
+
hasRequiredReactJsxRuntime_production_min = 1;
|
|
3363
|
+
var f=reactExports,k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};
|
|
3364
|
+
function q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=""+g);void 0!==a.key&&(e=""+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return {$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}reactJsxRuntime_production_min.Fragment=l;reactJsxRuntime_production_min.jsx=q;reactJsxRuntime_production_min.jsxs=q;
|
|
3365
|
+
return reactJsxRuntime_production_min;
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
{
|
|
3369
|
+
jsxRuntime.exports = requireReactJsxRuntime_production_min();
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3372
|
+
var jsxRuntimeExports = jsxRuntime.exports;
|
|
3373
|
+
|
|
3374
|
+
const AdStageContext = reactExports.createContext(null);
|
|
3375
|
+
function AdStageProvider({ children, config }) {
|
|
3376
|
+
const [isInitialized, setIsInitialized] = reactExports.useState(false);
|
|
3377
|
+
const [currentConfig, setCurrentConfig] = reactExports.useState(null);
|
|
3378
|
+
const [error, setError] = reactExports.useState(null);
|
|
3379
|
+
const initialize = (newConfig) => {
|
|
3380
|
+
try {
|
|
3381
|
+
setError(null);
|
|
3382
|
+
// 기존 인스턴스가 있으면 리셋
|
|
3383
|
+
if (isInitialized) {
|
|
3384
|
+
AdStage.reset();
|
|
3385
|
+
}
|
|
3386
|
+
AdStage.init(newConfig);
|
|
3387
|
+
setCurrentConfig(newConfig);
|
|
3388
|
+
setIsInitialized(true);
|
|
3389
|
+
if (newConfig.debug) {
|
|
3390
|
+
console.log('✅ AdStage SDK initialized successfully via React Provider');
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
catch (err) {
|
|
3394
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
3395
|
+
setError(errorMessage);
|
|
3396
|
+
console.error('❌ AdStage SDK initialization failed:', err);
|
|
3397
|
+
setIsInitialized(false);
|
|
3398
|
+
setCurrentConfig(null);
|
|
3399
|
+
}
|
|
3400
|
+
};
|
|
3401
|
+
const reset = () => {
|
|
3402
|
+
try {
|
|
3403
|
+
AdStage.reset();
|
|
3404
|
+
setIsInitialized(false);
|
|
3405
|
+
setCurrentConfig(null);
|
|
3406
|
+
setError(null);
|
|
3407
|
+
}
|
|
3408
|
+
catch (err) {
|
|
3409
|
+
console.error('❌ AdStage SDK reset failed:', err);
|
|
3410
|
+
}
|
|
3411
|
+
};
|
|
3412
|
+
// 자동 초기화
|
|
3413
|
+
reactExports.useEffect(() => {
|
|
3414
|
+
if (config && !isInitialized) {
|
|
3415
|
+
initialize(config);
|
|
3416
|
+
}
|
|
3417
|
+
}, [config, isInitialized]);
|
|
3418
|
+
const contextValue = {
|
|
3419
|
+
isInitialized,
|
|
3420
|
+
config: currentConfig,
|
|
3421
|
+
initialize,
|
|
3422
|
+
reset,
|
|
3423
|
+
error
|
|
3424
|
+
};
|
|
3425
|
+
return (jsxRuntimeExports.jsx(AdStageContext.Provider, { value: contextValue, children: children }));
|
|
3426
|
+
}
|
|
3427
|
+
/**
|
|
3428
|
+
* AdStage Context Hook
|
|
3429
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
3430
|
+
*/
|
|
3431
|
+
function useAdStage() {
|
|
3432
|
+
const context = reactExports.useContext(AdStageContext);
|
|
3433
|
+
if (!context) {
|
|
3434
|
+
throw new Error('useAdStage must be used within an AdStageProvider');
|
|
3435
|
+
}
|
|
3436
|
+
return context;
|
|
3437
|
+
}
|
|
3438
|
+
/**
|
|
3439
|
+
* AdStage SDK Hook
|
|
3440
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
3441
|
+
*/
|
|
3442
|
+
function useAdStageSDK() {
|
|
3443
|
+
const { isInitialized } = useAdStage();
|
|
3444
|
+
if (!isInitialized) {
|
|
3445
|
+
throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
|
|
3446
|
+
}
|
|
3447
|
+
return AdStage;
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3311
3450
|
/**
|
|
3312
3451
|
* AdStage Web SDK
|
|
3313
3452
|
* 네임스페이스 아키텍처 기반 SDK
|
|
@@ -3317,4 +3456,4 @@ class AdStage {
|
|
|
3317
3456
|
const SDK_VERSION = '2.0.0';
|
|
3318
3457
|
const SUPPORTED_MODULES = ['ads', 'events', 'config'];
|
|
3319
3458
|
|
|
3320
|
-
export { AdStage, SDK_VERSION, SUPPORTED_MODULES };
|
|
3459
|
+
export { AdStage, AdStageProvider, SDK_VERSION, SUPPORTED_MODULES, useAdStage, useAdStageSDK };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adstage/web-sdk",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "AdStage Web SDK - Production-ready marketing platform SDK with
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "AdStage Web SDK - Production-ready marketing platform SDK with React Provider support for seamless integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs.js",
|
|
7
7
|
"module": "dist/index.esm.js",
|
package/src/core/AdStage.ts
CHANGED
|
@@ -102,61 +102,17 @@ export class AdStage {
|
|
|
102
102
|
* 편의성을 위한 정적 모듈 접근자들
|
|
103
103
|
*/
|
|
104
104
|
public static get ads() {
|
|
105
|
-
// 🚀 자동 초기화: 아직 초기화되지 않았으면 환경변수로 자동 초기화
|
|
106
|
-
if (!AdStage.instance?._isInitialized) {
|
|
107
|
-
AdStage.autoInit();
|
|
108
|
-
}
|
|
109
105
|
return AdStage.getInstance().ads;
|
|
110
106
|
}
|
|
111
107
|
|
|
112
108
|
public static get events() {
|
|
113
|
-
if (!AdStage.instance?._isInitialized) {
|
|
114
|
-
AdStage.autoInit();
|
|
115
|
-
}
|
|
116
109
|
return AdStage.getInstance().events;
|
|
117
110
|
}
|
|
118
111
|
|
|
119
112
|
public static get config() {
|
|
120
|
-
if (!AdStage.instance?._isInitialized) {
|
|
121
|
-
AdStage.autoInit();
|
|
122
|
-
}
|
|
123
113
|
return AdStage.getInstance().config;
|
|
124
114
|
}
|
|
125
115
|
|
|
126
|
-
/**
|
|
127
|
-
* 🆕 자동 초기화 - 환경변수 기반
|
|
128
|
-
*/
|
|
129
|
-
private static autoInit(): void {
|
|
130
|
-
if (typeof window === 'undefined') return; // 서버 사이드에서는 실행 안함
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
// 환경변수에서 API 키 자동 로드
|
|
134
|
-
const apiKey =
|
|
135
|
-
(window as any).__ADSTAGE_API_KEY__ || // 런타임 설정
|
|
136
|
-
process.env.NEXT_PUBLIC_ADSTAGE_API_KEY || // Next.js
|
|
137
|
-
process.env.REACT_APP_ADSTAGE_API_KEY || // Create React App
|
|
138
|
-
process.env.VITE_ADSTAGE_API_KEY || // Vite
|
|
139
|
-
'demo-api-key'; // 개발용 fallback
|
|
140
|
-
|
|
141
|
-
const isDevelopment =
|
|
142
|
-
process.env.NODE_ENV === 'development' ||
|
|
143
|
-
(window as any).__ADSTAGE_DEBUG__ === true;
|
|
144
|
-
|
|
145
|
-
AdStage.init({
|
|
146
|
-
apiKey: apiKey,
|
|
147
|
-
debug: isDevelopment,
|
|
148
|
-
modules: ['ads', 'config']
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (isDevelopment) {
|
|
152
|
-
console.log('🔄 AdStage SDK auto-initialized with API key:', apiKey.substring(0, 8) + '...');
|
|
153
|
-
}
|
|
154
|
-
} catch (error) {
|
|
155
|
-
console.warn('AdStage auto-initialization failed:', error);
|
|
156
|
-
console.warn('Please call AdStage.init() manually or set NEXT_PUBLIC_ADSTAGE_API_KEY environment variable');
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
116
|
/**
|
|
161
117
|
* SDK 리셋 (테스트용)
|
|
162
118
|
*/
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
// 메인 네임스페이스 클래스
|
|
7
7
|
export { default as AdStage } from './core/AdStage';
|
|
8
8
|
|
|
9
|
+
// React 통합 (선택적)
|
|
10
|
+
export { AdStageProvider, useAdStage, useAdStageSDK } from './react';
|
|
11
|
+
|
|
9
12
|
// 설정 및 타입
|
|
10
13
|
export type { AdStageConfig, ModuleName, BaseModule, ApiResponse, OrganizationInfo } from './types/config';
|
|
11
14
|
|
|
@@ -184,7 +184,7 @@ export class CarouselSliderManager {
|
|
|
184
184
|
const totalSlides = advertisements.length;
|
|
185
185
|
const autoSlideInterval = (options?.autoSlideInterval || 3) * 1000; // 기본 3초
|
|
186
186
|
|
|
187
|
-
// 슬라이드 이동 함수 (무한 루프 지원)
|
|
187
|
+
// 슬라이드 이동 함수 (무한 루프 지원 + 동적 높이 조정)
|
|
188
188
|
const moveToSlide = (index: number, instant = false) => {
|
|
189
189
|
currentSlide = index;
|
|
190
190
|
|
|
@@ -198,6 +198,36 @@ export class CarouselSliderManager {
|
|
|
198
198
|
// 항상 퍼센트 기반으로 이동
|
|
199
199
|
slideContainer.style.transform = `translateX(-${(100 / extendedAds.length) * currentSlide}%)`;
|
|
200
200
|
|
|
201
|
+
// 🆕 동적 높이 조정: 현재 슬라이드의 이미지 높이에 맞춰 컨테이너 높이 조정
|
|
202
|
+
if (!instant && !slot.height && !slot.width) { // 사용자가 크기를 지정하지 않은 경우에만
|
|
203
|
+
const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
|
|
204
|
+
const currentSlideElement = slideContainer.children[currentSlide] as HTMLElement;
|
|
205
|
+
|
|
206
|
+
if (currentSlideElement) {
|
|
207
|
+
const currentAdElement = currentSlideElement.children[0] as HTMLElement;
|
|
208
|
+
if (currentAdElement) {
|
|
209
|
+
// 이미지 요소 찾기
|
|
210
|
+
const imgElement = currentAdElement.querySelector('img');
|
|
211
|
+
if (imgElement) {
|
|
212
|
+
// 이미지 로드 완료 후 높이 조정
|
|
213
|
+
const adjustHeight = () => {
|
|
214
|
+
const imgHeight = imgElement.getBoundingClientRect().height;
|
|
215
|
+
if (imgHeight > 0) {
|
|
216
|
+
sliderWrapper.style.height = `${imgHeight}px`;
|
|
217
|
+
sliderWrapper.style.transition = instant ? 'none' : 'height 0.4s ease-out';
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
if (imgElement.complete && imgElement.naturalHeight > 0) {
|
|
222
|
+
adjustHeight();
|
|
223
|
+
} else {
|
|
224
|
+
imgElement.addEventListener('load', adjustHeight, { once: true });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
201
231
|
// 도트 업데이트 (무채색 스타일) - 실제 광고 인덱스 기준, 텍스트 광고가 아닐 때만
|
|
202
232
|
const actualIndex = currentSlide === totalSlides ? 0 : currentSlide;
|
|
203
233
|
if (dotContainer) {
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdStage SDK - React Provider
|
|
3
|
+
* React 애플리케이션에서 AdStage SDK를 쉽게 사용할 수 있도록 하는 Provider 컴포넌트
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
|
7
|
+
import AdStage from '../core/AdStage';
|
|
8
|
+
import { AdStageConfig } from '../types/config';
|
|
9
|
+
|
|
10
|
+
interface AdStageContextType {
|
|
11
|
+
isInitialized: boolean;
|
|
12
|
+
config: AdStageConfig | null;
|
|
13
|
+
initialize: (config: AdStageConfig) => void;
|
|
14
|
+
reset: () => void;
|
|
15
|
+
error: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const AdStageContext = createContext<AdStageContextType | null>(null);
|
|
19
|
+
|
|
20
|
+
interface AdStageProviderProps {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* 자동 초기화를 위한 설정 (선택사항)
|
|
24
|
+
* 제공되면 Provider 마운트 시 자동으로 초기화됩니다.
|
|
25
|
+
*/
|
|
26
|
+
config?: AdStageConfig;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function AdStageProvider({ children, config }: AdStageProviderProps) {
|
|
30
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
31
|
+
const [currentConfig, setCurrentConfig] = useState<AdStageConfig | null>(null);
|
|
32
|
+
const [error, setError] = useState<string | null>(null);
|
|
33
|
+
|
|
34
|
+
const initialize = (newConfig: AdStageConfig) => {
|
|
35
|
+
try {
|
|
36
|
+
setError(null);
|
|
37
|
+
|
|
38
|
+
// 기존 인스턴스가 있으면 리셋
|
|
39
|
+
if (isInitialized) {
|
|
40
|
+
AdStage.reset();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
AdStage.init(newConfig);
|
|
44
|
+
|
|
45
|
+
setCurrentConfig(newConfig);
|
|
46
|
+
setIsInitialized(true);
|
|
47
|
+
|
|
48
|
+
if (newConfig.debug) {
|
|
49
|
+
console.log('✅ AdStage SDK initialized successfully via React Provider');
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
53
|
+
setError(errorMessage);
|
|
54
|
+
console.error('❌ AdStage SDK initialization failed:', err);
|
|
55
|
+
setIsInitialized(false);
|
|
56
|
+
setCurrentConfig(null);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const reset = () => {
|
|
61
|
+
try {
|
|
62
|
+
AdStage.reset();
|
|
63
|
+
setIsInitialized(false);
|
|
64
|
+
setCurrentConfig(null);
|
|
65
|
+
setError(null);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error('❌ AdStage SDK reset failed:', err);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// 자동 초기화
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (config && !isInitialized) {
|
|
74
|
+
initialize(config);
|
|
75
|
+
}
|
|
76
|
+
}, [config, isInitialized]);
|
|
77
|
+
|
|
78
|
+
const contextValue: AdStageContextType = {
|
|
79
|
+
isInitialized,
|
|
80
|
+
config: currentConfig,
|
|
81
|
+
initialize,
|
|
82
|
+
reset,
|
|
83
|
+
error
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<AdStageContext.Provider value={contextValue}>
|
|
88
|
+
{children}
|
|
89
|
+
</AdStageContext.Provider>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* AdStage Context Hook
|
|
95
|
+
* AdStageProvider 내에서 SDK 상태에 접근할 수 있습니다.
|
|
96
|
+
*/
|
|
97
|
+
export function useAdStage(): AdStageContextType {
|
|
98
|
+
const context = useContext(AdStageContext);
|
|
99
|
+
if (!context) {
|
|
100
|
+
throw new Error('useAdStage must be used within an AdStageProvider');
|
|
101
|
+
}
|
|
102
|
+
return context;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* AdStage SDK Hook
|
|
107
|
+
* 초기화된 AdStage 인스턴스에 직접 접근할 수 있습니다.
|
|
108
|
+
*/
|
|
109
|
+
export function useAdStageSDK() {
|
|
110
|
+
const { isInitialized } = useAdStage();
|
|
111
|
+
|
|
112
|
+
if (!isInitialized) {
|
|
113
|
+
throw new Error('AdStage SDK is not initialized. Please call initialize() first or provide config to AdStageProvider.');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return AdStage;
|
|
117
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdStage Web SDK - React Integration
|
|
3
|
+
* React 애플리케이션을 위한 컴포넌트와 훅들
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { AdStageProvider, useAdStage, useAdStageSDK } from './AdStageProvider';
|
|
7
|
+
|
|
8
|
+
// 타입들도 재export
|
|
9
|
+
export type { AdStageConfig, ModuleName, BaseModule } from '../types/config';
|
|
10
|
+
export type { AdOptions } from '../modules/ads/AdsModule';
|
|
11
|
+
export type { AdType, AdEventType, Advertisement, AdSlot } from '../types/advertisement';
|