UNPKG

@vrism/viewer-sdk

Version:

React and Vanilla JavaScript SDK for embedding 3D product viewers powered by Verge3D technology

446 lines (442 loc) 12.3 kB
var h = Object.defineProperty; var c = (n, e, r) => e in n ? h(n, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : n[e] = r; var t = (n, e, r) => c(n, typeof e != "symbol" ? e + "" : e, r); import * as l from "react"; import * as d from "react-dom/client"; import { VrismViewerReact as g } from "../react/index.js"; class f { constructor(e) { t(this, "currentConfig"); t(this, "baseProps"); t(this, "_apiBaseUrl"); this.currentConfig = e.config, this.baseProps = { token: e.token, contentId: e.contentId || "", containerId: e.containerId }, this._apiBaseUrl = e._apiBaseUrl; } updateConfig(e, r) { typeof e == "string" ? this.currentConfig = { ...this.currentConfig, [e]: r } : this.currentConfig = { ...this.currentConfig, ...e }; } /** * 콘텐츠 ID 변경 */ updateContentId(e) { this.baseProps.contentId = e; } /** * 현재 설정 반환 */ getConfig() { return this.currentConfig; } /** * React Props 형태로 변환 */ toReactProps(e = {}) { var i, a; const r = { ...this.baseProps, camera: (i = this.currentConfig) == null ? void 0 : i.camera, ui: (a = this.currentConfig) == null ? void 0 : a.ui, ...e }; return this._apiBaseUrl && (r._apiBaseUrl = this._apiBaseUrl), r; } } class u { constructor() { // 사용자가 설정할 수 있는 이벤트 핸들러들 t(this, "onLoadFinish"); t(this, "onStepChange"); t(this, "onChange"); t(this, "onLoadScene"); t(this, "onLoadUpdate"); t(this, "onFullscreenChange"); t(this, "onError"); } /** * React Props 형태로 이벤트 핸들러 반환 * 바인딩된 this 컨텍스트를 유지 */ getEventHandlers() { const e = {}; return this.onChange && (e.onChange = this.onChange), this.onLoadScene && (e.onLoadScene = this.onLoadScene), this.onLoadUpdate && (e.onLoadUpdate = this.onLoadUpdate), this.onLoadFinish && (e.onLoadFinish = this.onLoadFinish), this.onStepChange && (e.onStepChange = this.onStepChange), this.onFullscreenChange && (e.onFullscreenChange = this.onFullscreenChange), this.onError && (e.onError = this.onError), e; } /** * 모든 이벤트 핸들러 초기화 */ clear() { this.onLoadFinish = void 0, this.onStepChange = void 0, this.onChange = void 0, this.onLoadScene = void 0, this.onLoadUpdate = void 0, this.onFullscreenChange = void 0, this.onError = void 0; } } class s { constructor(e) { t(this, "container"); t(this, "errorElement", null); this.container = e.container, this.createErrorUI(e.error); } /** * 에러 UI 생성 */ createErrorUI(e) { this.container.innerHTML = ""; const r = e instanceof Error ? e.message : String(e); this.errorElement = document.createElement("div"), this.errorElement.className = "vrism-error-display", this.errorElement.innerHTML = ` <div class="vrism-error-container"> <h2 class="vrism-error-title">Error</h2> <p class="vrism-error-message">${this.escapeHtml(r)}</p> </div> `, this.addStyles(), this.container.appendChild(this.errorElement); } /** * HTML 이스케이프 처리 */ escapeHtml(e) { const r = document.createElement("div"); return r.textContent = e, r.innerHTML; } /** * CSS 스타일 추가 */ addStyles() { if (document.getElementById("vrism-error-display-styles")) return; const e = document.createElement("style"); e.id = "vrism-error-display-styles", e.textContent = ` .vrism-error-display { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } .vrism-error-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px; text-align: center; max-width: 400px; } .vrism-error-title { margin: 0; font-size: 36px; line-height: 42px; color: #333333; } .vrism-error-message { margin: 0; font-size: 12px; font-weight: 400; color: #666666; line-height: 1.5; word-break: break-word; } `, document.head.appendChild(e); } /** * 에러 표시 제거 */ destroy() { this.errorElement && this.errorElement.parentNode && (this.errorElement.parentNode.removeChild(this.errorElement), this.errorElement = null); } /** * 정적 메서드: 간편 사용 */ static show(e, r) { return new s({ container: e, error: r }); } } class w { constructor(e) { t(this, "container"); t(this, "reactRoot", null); t(this, "viewerRef", null); t(this, "isInitialized", !1); t(this, "errorDisplay", null); this.container = e; } /** * React 컴포넌트 렌더링 * @param props React 컴포넌트에 전달할 props */ render(e) { this.clearErrorDisplay(), this.reactRoot || (this.reactRoot = d.createRoot(this.container)); const r = e.onError, a = { ...{ ...e, onError: (o) => { this.showErrorDisplay(o), r && r(o); } }, ref: (o) => { this.viewerRef = o, o && !this.isInitialized && (this.isInitialized = !0); } }; this.reactRoot.render(l.createElement(g, a)); } /** * React 컴포넌트의 ref 반환 * 모든 React 뷰어 메서드에 접근 가능 */ getViewerRef() { return this.viewerRef; } /** * 뷰어 초기화 상태 확인 */ isReady() { return this.isInitialized && this.viewerRef !== null; } /** * 초기화 완료까지 대기하는 Promise */ waitForInitialization() { return new Promise((e, r) => { if (this.isReady() && this.viewerRef) { e(this.viewerRef); return; } const i = setTimeout(() => { r(new Error("Viewer initialization timeout")); }, 1e4), a = () => { this.isReady() && this.viewerRef ? (clearTimeout(i), e(this.viewerRef)) : setTimeout(a, 100); }; a(); }); } /** * 에러 UI 표시 * React 컴포넌트를 언마운트하고 Vanilla ErrorDisplay로 대체 */ showErrorDisplay(e) { this.reactRoot && (this.reactRoot.unmount(), this.reactRoot = null), this.errorDisplay = s.show(this.container, e), this.isInitialized = !1, this.viewerRef = null; } /** * 에러 표시 제거 */ clearErrorDisplay() { this.errorDisplay && (this.errorDisplay.destroy(), this.errorDisplay = null); } /** * React 컴포넌트 언마운트 및 정리 */ destroy() { this.clearErrorDisplay(), this.reactRoot && (this.reactRoot.unmount(), this.reactRoot = null), this.viewerRef = null, this.isInitialized = !1; } } class v { constructor(e, r) { t(this, "configManager"); t(this, "eventManager"); t(this, "reactBridge"); this.configManager = new f(r), this.eventManager = new u(), this.reactBridge = new w(e), r.onLoadFinish && (this.eventManager.onLoadFinish = r.onLoadFinish), r.onStepChange && (this.eventManager.onStepChange = r.onStepChange), r.onChange && (this.eventManager.onChange = r.onChange), r.onLoadScene && (this.eventManager.onLoadScene = r.onLoadScene), r.onLoadUpdate && (this.eventManager.onLoadUpdate = r.onLoadUpdate), r.onFullscreenChange && (this.eventManager.onFullscreenChange = r.onFullscreenChange), r.onError && (this.eventManager.onError = r.onError); try { this.render(); } catch (i) { this.handleError(i); } } /** * React 컴포넌트 렌더링 * 설정과 이벤트 핸들러를 React Props로 변환 */ render() { const e = this.eventManager.getEventHandlers(), r = this.configManager.toReactProps(e); this.reactBridge.render(r); } /** * React 뷰어 ref에 안전하게 접근 * 뷰어가 초기화되지 않은 경우 에러 발생 */ async getViewerRef() { return this.reactBridge.isReady() ? this.reactBridge.getViewerRef() : await this.reactBridge.waitForInitialization(); } /** * 에러 처리 헬퍼 메서드 * 콘솔 로그와 사용자 콜백 모두 호출 */ handleError(e) { console.error("VRISM Viewer Error:", e), this.eventManager.onError && this.eventManager.onError(e); } // ==================== 공개 API 메서드들 ==================== /** * 새로운 콘텐츠 로드 */ load(e) { this.configManager.updateContentId(e.contentId), this.render(); } setConfig(e, r) { this.configManager.updateConfig(e, r), this.render(); } /** * 현재 설정 조회 */ getConfig() { return this.configManager.getConfig(); } /** * 뷰어 리로드 */ reload(e) { e && this.configManager.updateConfig(e), this.render(); } /** * 비동기 뷰어 리로드 (React 구현체와 동일한 Promise 기반) */ async reloadAsync(e) { try { return (await this.getViewerRef()).reload(e); } catch (r) { throw this.handleError(r), r; } } /** * 뷰어 내부 설정 조회 (React 구현체에서 제공) */ async getViewerConfig() { try { return (await this.getViewerRef()).getConfig(); } catch (e) { throw this.handleError(e), e; } } /** * 3D 위치 선택 API */ async getClickedPosition() { try { return (await this.getViewerRef()).getClickedPosition(); } catch (e) { throw this.handleError(e), e; } } /** * 제스처 가이드 표시 제어 */ async setGestureGuideShow(e) { try { return (await this.getViewerRef()).setGestureGuideShow(e); } catch (r) { throw this.handleError(r), r; } } /** * 전체화면 모드 제어 */ async setFullscreenOpen(e) { try { return (await this.getViewerRef()).setFullscreenOpen(e); } catch (r) { throw this.handleError(r), r; } } /** * 스텝 설정 */ async setStep(e) { try { return (await this.getViewerRef()).setStep(e); } catch (r) { throw this.handleError(r), r; } } /** * VTO 기능 트리거 */ async onVTOClick() { try { return (await this.getViewerRef()).onVTOClick(); } catch (e) { throw this.handleError(e), e; } } // ==================== 이벤트 핸들러 접근자들 ==================== get onLoadFinish() { return this.eventManager.onLoadFinish; } set onLoadFinish(e) { this.eventManager.onLoadFinish = e; } get onStepChange() { return this.eventManager.onStepChange; } set onStepChange(e) { this.eventManager.onStepChange = e; } get onChange() { return this.eventManager.onChange; } set onChange(e) { this.eventManager.onChange = e; } get onLoadScene() { return this.eventManager.onLoadScene; } set onLoadScene(e) { this.eventManager.onLoadScene = e; } get onLoadUpdate() { return this.eventManager.onLoadUpdate; } set onLoadUpdate(e) { this.eventManager.onLoadUpdate = e; } get onFullscreenChange() { return this.eventManager.onFullscreenChange; } set onFullscreenChange(e) { this.eventManager.onFullscreenChange = e; } get onError() { return this.eventManager.onError; } set onError(e) { this.eventManager.onError = e; } // ==================== 정리 ==================== /** * 뷰어 인스턴스 정리 * React 컴포넌트 언마운트 및 메모리 정리 */ destroy() { this.eventManager.clear(), this.reactBridge.destroy(); } } const C = { /** * 뷰어 인스턴스 초기화 * @param containerId DOM 컨테이너 엘리먼트 ID * @param options 초기화 옵션 * @returns VrismViewerInstance 인스턴스 */ init: (n, e) => { const r = document.getElementById(n); if (!r) throw new Error(`Container element with id "${n}" not found`); const i = { ...e, containerId: n }; return new v(r, i); } }; export { f as ConfigManager, s as ErrorDisplay, u as EventManager, w as ReactBridge, C as VrismViewer, v as VrismViewerInstance, C as default };