@coxwave/tap-kit 2.0.1 → 2.0.2
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 +0 -1
- package/dist/index.d.cts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.cts +41 -55
- package/dist/react.d.ts +41 -55
- package/dist/react.js +373 -2
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +366 -2
- package/dist/react.mjs.map +1 -1
- package/package.json +2 -2
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { TapKitElement
|
|
2
|
-
import React
|
|
1
|
+
import { TapKitElement } from '@coxwave/tap-kit-types';
|
|
2
|
+
import React from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* React-specific type definitions for TapKit
|
|
@@ -66,6 +66,12 @@ interface TapKitControl<T> {
|
|
|
66
66
|
* Separated from options for easier event listener management.
|
|
67
67
|
*/
|
|
68
68
|
handlers: TapKitEventHandlers;
|
|
69
|
+
/**
|
|
70
|
+
* Whether CDN is loaded and Web Component is available
|
|
71
|
+
*
|
|
72
|
+
* @internal Used by TapKit component to delay rendering
|
|
73
|
+
*/
|
|
74
|
+
isCdnLoaded: boolean;
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
/**
|
|
@@ -110,24 +116,18 @@ interface TapKitControl<T> {
|
|
|
110
116
|
/**
|
|
111
117
|
* Props for TapKit React component
|
|
112
118
|
*/
|
|
113
|
-
interface TapKitProps extends Omit<React
|
|
119
|
+
interface TapKitProps extends Omit<React.HTMLAttributes<TapKitElement>, "children" | "dangerouslySetInnerHTML"> {
|
|
114
120
|
/**
|
|
115
121
|
* Control object from useTapKit hook
|
|
116
122
|
*
|
|
117
123
|
* Provides instance management, configuration, and event handlers.
|
|
118
124
|
*/
|
|
119
125
|
control: TapKitControl<any>;
|
|
120
|
-
/**
|
|
121
|
-
* Custom container element ID (optional)
|
|
122
|
-
*
|
|
123
|
-
* If provided, TapKit will use this element as container.
|
|
124
|
-
*/
|
|
125
|
-
containerId?: string;
|
|
126
126
|
}
|
|
127
127
|
declare global {
|
|
128
128
|
namespace JSX {
|
|
129
129
|
interface IntrinsicElements {
|
|
130
|
-
"tap-kit": React
|
|
130
|
+
"tap-kit": React.DetailedHTMLProps<React.HTMLAttributes<TapKitElement>, TapKitElement> & {
|
|
131
131
|
"api-key"?: string;
|
|
132
132
|
"user-id"?: string;
|
|
133
133
|
"course-id"?: string;
|
|
@@ -135,7 +135,6 @@ declare global {
|
|
|
135
135
|
"clip-play-head"?: number;
|
|
136
136
|
language?: "ko" | "en";
|
|
137
137
|
"button-id"?: string;
|
|
138
|
-
"container-id"?: string;
|
|
139
138
|
mode?: "inline" | "floating" | "sidebar";
|
|
140
139
|
debug?: boolean;
|
|
141
140
|
"tap-url"?: string;
|
|
@@ -158,60 +157,45 @@ declare global {
|
|
|
158
157
|
* // tapkitRef.current?.show()
|
|
159
158
|
* ```
|
|
160
159
|
*/
|
|
161
|
-
declare const TapKit: React
|
|
160
|
+
declare const TapKit: React.ForwardRefExoticComponent<TapKitProps & React.RefAttributes<TapKitElement>>;
|
|
162
161
|
|
|
163
162
|
/**
|
|
164
|
-
* useTapKit Hook -
|
|
165
|
-
*
|
|
166
|
-
* This hook provides direct access to the TapKitElement instance and full
|
|
167
|
-
* control over its lifecycle. Use this when you need:
|
|
168
|
-
* - Direct element manipulation
|
|
169
|
-
* - Custom rendering logic
|
|
170
|
-
* - Imperative control over Web Component behavior
|
|
163
|
+
* useTapKit Hook - ChatKit-style control for TapKit Web Component
|
|
171
164
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
165
|
+
* This hook provides a control object to manage TapKit through the
|
|
166
|
+
* <TapKit /> component. Inspired by OpenAI's ChatKit pattern.
|
|
174
167
|
*
|
|
175
|
-
* @
|
|
176
|
-
* @returns Object with element reference, state, and control methods
|
|
177
|
-
*
|
|
178
|
-
* @example Advanced control with custom rendering
|
|
168
|
+
* @example
|
|
179
169
|
* ```tsx
|
|
180
170
|
* 'use client';
|
|
181
171
|
*
|
|
182
|
-
* import { useTapKit } from '@coxwave/tap-kit/react';
|
|
172
|
+
* import { TapKit, useTapKit } from '@coxwave/tap-kit/react';
|
|
183
173
|
*
|
|
184
|
-
* function
|
|
185
|
-
* const
|
|
174
|
+
* function MyApp() {
|
|
175
|
+
* const tapkit = useTapKit({
|
|
186
176
|
* apiKey: 'your-key',
|
|
187
177
|
* userId: 'user-123',
|
|
188
178
|
* courseId: 'course-456',
|
|
189
179
|
* clipId: 'clip-789',
|
|
180
|
+
* onReady: () => console.log('Ready!'),
|
|
181
|
+
* onError: (error) => console.error(error),
|
|
190
182
|
* });
|
|
191
183
|
*
|
|
192
|
-
* // Direct element access for advanced operations
|
|
193
|
-
* useEffect(() => {
|
|
194
|
-
* if (element) {
|
|
195
|
-
* // Direct manipulation of TapKitElement
|
|
196
|
-
* console.log('Element mounted:', element);
|
|
197
|
-
* }
|
|
198
|
-
* }, [element]);
|
|
199
|
-
*
|
|
200
184
|
* return (
|
|
201
185
|
* <div>
|
|
202
|
-
* <button onClick={show} disabled={!isReady}>
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* <
|
|
186
|
+
* <button onClick={tapkit.show} disabled={!tapkit.isReady}>
|
|
187
|
+
* Ask AI Tutor
|
|
188
|
+
* </button>
|
|
189
|
+
* <TapKit control={tapkit.control} />
|
|
206
190
|
* </div>
|
|
207
191
|
* );
|
|
208
192
|
* }
|
|
209
193
|
* ```
|
|
210
|
-
*
|
|
211
|
-
* @see TapKit - Use this component for simpler declarative API
|
|
212
194
|
*/
|
|
213
195
|
|
|
214
|
-
interface UseTapKitOptions extends
|
|
196
|
+
interface UseTapKitOptions extends TapKitEventHandlers {
|
|
197
|
+
/** API Key (required) */
|
|
198
|
+
apiKey: string;
|
|
215
199
|
/** User ID */
|
|
216
200
|
userId?: string;
|
|
217
201
|
/** Course ID */
|
|
@@ -240,13 +224,7 @@ interface UseTapKitOptions extends TapKitConfig, TapKitEventHandlers {
|
|
|
240
224
|
*/
|
|
241
225
|
type TapKitOptions = Omit<UseTapKitOptions, keyof TapKitEventHandlers>;
|
|
242
226
|
interface UseTapKitReturn {
|
|
243
|
-
/**
|
|
244
|
-
element: TapKitElement | null;
|
|
245
|
-
/** Ref object for direct element access */
|
|
246
|
-
ref: React.RefObject<TapKitElement | null>;
|
|
247
|
-
/** Container ref to attach element */
|
|
248
|
-
elementRef: React.RefCallback<HTMLDivElement>;
|
|
249
|
-
/** Control object for TapKit component */
|
|
227
|
+
/** Control object for <TapKit /> component */
|
|
250
228
|
control: TapKitControl<TapKitOptions>;
|
|
251
229
|
/** Whether TapKit is ready */
|
|
252
230
|
isReady: boolean;
|
|
@@ -263,14 +241,22 @@ interface UseTapKitReturn {
|
|
|
263
241
|
userId?: string;
|
|
264
242
|
clipPlayHead?: number;
|
|
265
243
|
}) => void;
|
|
244
|
+
/** Video adapter control */
|
|
245
|
+
video: {
|
|
246
|
+
/** Bind video player for timeline synchronization */
|
|
247
|
+
bind: (adapter: {
|
|
248
|
+
getCurrentTime: () => number;
|
|
249
|
+
setCurrentTime: (time: number) => void;
|
|
250
|
+
}, clipId: string) => void;
|
|
251
|
+
/** Unbind current video player */
|
|
252
|
+
unbind: () => void;
|
|
253
|
+
};
|
|
266
254
|
}
|
|
267
255
|
/**
|
|
268
|
-
* Hook for managing TapKit Web Component
|
|
269
|
-
*
|
|
270
|
-
* Automatically loads CDN, creates Web Component, and provides control methods.
|
|
256
|
+
* Hook for managing TapKit Web Component (ChatKit Pattern)
|
|
271
257
|
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
258
|
+
* Returns a control object to pass to <TapKit /> component.
|
|
259
|
+
* The component handles rendering, this hook handles state and methods.
|
|
274
260
|
*/
|
|
275
261
|
declare function useTapKit(options: UseTapKitOptions): UseTapKitReturn;
|
|
276
262
|
|
package/dist/react.js
CHANGED
|
@@ -1,3 +1,374 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
9
|
+
|
|
10
|
+
// src/react/TapKit.tsx
|
|
11
|
+
|
|
12
|
+
// src/react/types.ts
|
|
13
|
+
var EVENT_HANDLER_MAP = {
|
|
14
|
+
"tap-kit:ready": "onReady",
|
|
15
|
+
"tap-kit:error": "onError"
|
|
16
|
+
};
|
|
17
|
+
var EVENT_NAMES = Object.keys(
|
|
18
|
+
EVENT_HANDLER_MAP
|
|
19
|
+
);
|
|
20
|
+
var TapKit = React__default.default.forwardRef(
|
|
21
|
+
function TapKit2({ control, ...htmlProps }, forwardedRef) {
|
|
22
|
+
const elementRef = React.useRef(null);
|
|
23
|
+
React.useImperativeHandle(
|
|
24
|
+
forwardedRef,
|
|
25
|
+
() => elementRef.current,
|
|
26
|
+
[]
|
|
27
|
+
);
|
|
28
|
+
const setElementRef = React.useCallback(
|
|
29
|
+
(element) => {
|
|
30
|
+
elementRef.current = element;
|
|
31
|
+
if (element && control.options.apiKey) {
|
|
32
|
+
element.apiKey = control.options.apiKey;
|
|
33
|
+
}
|
|
34
|
+
control.setInstance(element);
|
|
35
|
+
},
|
|
36
|
+
[control.setInstance, control.options.apiKey]
|
|
37
|
+
);
|
|
38
|
+
const handlersRef = React.useRef(control.handlers);
|
|
39
|
+
handlersRef.current = control.handlers;
|
|
40
|
+
React.useEffect(() => {
|
|
41
|
+
const element = elementRef.current;
|
|
42
|
+
if (!element) return;
|
|
43
|
+
const listeners = [];
|
|
44
|
+
for (const eventName of EVENT_NAMES) {
|
|
45
|
+
const handlerKey = EVENT_HANDLER_MAP[eventName];
|
|
46
|
+
const listener = (e) => {
|
|
47
|
+
const handler = handlersRef.current[handlerKey];
|
|
48
|
+
if (!handler) return;
|
|
49
|
+
const customEvent = e;
|
|
50
|
+
if (handlerKey === "onError" && customEvent.detail?.error) {
|
|
51
|
+
handler(customEvent.detail.error);
|
|
52
|
+
} else if (handlerKey === "onReady") {
|
|
53
|
+
handler();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
element.addEventListener(eventName, listener);
|
|
57
|
+
listeners.push({ event: eventName, handler: listener });
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
for (const { event, handler } of listeners) {
|
|
61
|
+
element.removeEventListener(event, handler);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}, []);
|
|
65
|
+
const { options, isCdnLoaded } = control;
|
|
66
|
+
if (!isCdnLoaded) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
70
|
+
"tap-kit",
|
|
71
|
+
{
|
|
72
|
+
ref: setElementRef,
|
|
73
|
+
"user-id": options.userId,
|
|
74
|
+
"course-id": options.courseId,
|
|
75
|
+
"clip-id": options.clipId,
|
|
76
|
+
"clip-play-head": options.clipPlayHead,
|
|
77
|
+
language: options.language,
|
|
78
|
+
"button-id": options.buttonId,
|
|
79
|
+
mode: options.mode,
|
|
80
|
+
debug: options.debug,
|
|
81
|
+
"tap-url": options.tapUrl,
|
|
82
|
+
"api-url": options.apiUrl,
|
|
83
|
+
environment: options.environment,
|
|
84
|
+
...htmlProps
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// src/loader.ts
|
|
91
|
+
var DEFAULT_CDN_LOADER_URL = typeof __DEFAULT_CDN_LOADER_URL__ !== "undefined" ? __DEFAULT_CDN_LOADER_URL__ : "https://files.edutap.ai/tap-sdk/loader.js";
|
|
92
|
+
var DEFAULT_TIMEOUT_MS = 4e3;
|
|
93
|
+
var IDLE_CALLBACK_TIMEOUT_MS = 500;
|
|
94
|
+
function getLoaderURL() {
|
|
95
|
+
return window?.__TAP_KIT_LOADER_URL__ ? window.__TAP_KIT_LOADER_URL__ : DEFAULT_CDN_LOADER_URL;
|
|
96
|
+
}
|
|
97
|
+
function isLocalCoreMode() {
|
|
98
|
+
return typeof window !== "undefined" && !!window.__TAP_KIT_CORE_URL__;
|
|
99
|
+
}
|
|
100
|
+
function getLocalCoreURL() {
|
|
101
|
+
return window.__TAP_KIT_CORE_URL__ || "";
|
|
102
|
+
}
|
|
103
|
+
function createSDKChecker(resolve, reject, timeoutMs) {
|
|
104
|
+
const startTime = Date.now();
|
|
105
|
+
const checkSDK = () => {
|
|
106
|
+
if (window.TapKit && window.TapKitLoaded === true) {
|
|
107
|
+
window.__TAP_KIT_LOADER_LOADED__ = true;
|
|
108
|
+
window.__TAP_KIT_LOADER_LOADING__ = void 0;
|
|
109
|
+
resolve();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const elapsed = Date.now() - startTime;
|
|
113
|
+
if (elapsed > timeoutMs) {
|
|
114
|
+
window.__TAP_KIT_LOADER_LOADING__ = void 0;
|
|
115
|
+
reject(
|
|
116
|
+
new Error(
|
|
117
|
+
`TapKit loader timeout: SDK not available after ${timeoutMs}ms`
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (typeof requestIdleCallback !== "undefined") {
|
|
123
|
+
requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });
|
|
124
|
+
} else {
|
|
125
|
+
setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
return checkSDK;
|
|
129
|
+
}
|
|
130
|
+
function loadCDNLoader(timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
131
|
+
if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {
|
|
132
|
+
return Promise.resolve();
|
|
133
|
+
}
|
|
134
|
+
if (window.__TAP_KIT_LOADER_LOADING__) {
|
|
135
|
+
return window.__TAP_KIT_LOADER_LOADING__;
|
|
136
|
+
}
|
|
137
|
+
const loadingPromise = new Promise((resolve, reject) => {
|
|
138
|
+
if (typeof document === "undefined") {
|
|
139
|
+
reject(
|
|
140
|
+
new Error(
|
|
141
|
+
"TapKit requires browser environment (document is undefined)"
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (isLocalCoreMode()) {
|
|
147
|
+
if (window.TapKit && window.TapKitLoaded === true) {
|
|
148
|
+
window.__TAP_KIT_LOADER_LOADED__ = true;
|
|
149
|
+
window.__TAP_KIT_LOADER_LOADING__ = void 0;
|
|
150
|
+
resolve();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const coreURL = getLocalCoreURL();
|
|
154
|
+
const script2 = document.createElement("script");
|
|
155
|
+
script2.src = coreURL;
|
|
156
|
+
script2.async = true;
|
|
157
|
+
script2.onload = () => {
|
|
158
|
+
if (window.TapKit) {
|
|
159
|
+
window.TapKitLoaded = true;
|
|
160
|
+
window.__TAP_KIT_LOADER_LOADED__ = true;
|
|
161
|
+
window.__TAP_KIT_LOADER_LOADING__ = void 0;
|
|
162
|
+
resolve();
|
|
163
|
+
} else {
|
|
164
|
+
window.__TAP_KIT_LOADER_LOADING__ = void 0;
|
|
165
|
+
reject(new Error("TapKit not available after loading local core"));
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
script2.onerror = () => {
|
|
169
|
+
window.__TAP_KIT_LOADER_LOADING__ = void 0;
|
|
170
|
+
reject(new Error(`Failed to load local TapKit core: ${coreURL}`));
|
|
171
|
+
};
|
|
172
|
+
document.head.appendChild(script2);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const loaderURL = getLoaderURL();
|
|
176
|
+
const script = document.createElement("script");
|
|
177
|
+
script.src = loaderURL;
|
|
178
|
+
script.async = true;
|
|
179
|
+
script.onload = () => {
|
|
180
|
+
const checkSDK = createSDKChecker(resolve, reject, timeoutMs);
|
|
181
|
+
checkSDK();
|
|
182
|
+
};
|
|
183
|
+
script.onerror = () => {
|
|
184
|
+
window.__TAP_KIT_LOADER_LOADING__ = void 0;
|
|
185
|
+
reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));
|
|
186
|
+
};
|
|
187
|
+
const existingScript = document.querySelector(`script[src="${loaderURL}"]`);
|
|
188
|
+
if (existingScript) {
|
|
189
|
+
existingScript.addEventListener("load", () => {
|
|
190
|
+
const checkSDK = createSDKChecker(resolve, reject, timeoutMs);
|
|
191
|
+
checkSDK();
|
|
192
|
+
});
|
|
193
|
+
existingScript.addEventListener(
|
|
194
|
+
"error",
|
|
195
|
+
() => reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`))
|
|
196
|
+
);
|
|
197
|
+
} else {
|
|
198
|
+
document.head.appendChild(script);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;
|
|
202
|
+
return loadingPromise;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/react/useTapKit.ts
|
|
206
|
+
function extractHandlers(options) {
|
|
207
|
+
return {
|
|
208
|
+
onReady: options.onReady,
|
|
209
|
+
onError: options.onError,
|
|
210
|
+
onTimelineSeek: options.onTimelineSeek,
|
|
211
|
+
onAlarmFadeIn: options.onAlarmFadeIn
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function useTapKit(options) {
|
|
215
|
+
const elementRef = React.useRef(null);
|
|
216
|
+
const [isCdnLoaded, setIsCdnLoaded] = React.useState(false);
|
|
217
|
+
const [isReady, setIsReady] = React.useState(false);
|
|
218
|
+
const [error, setError] = React.useState(null);
|
|
219
|
+
const handlersRef = React.useRef(extractHandlers(options));
|
|
220
|
+
handlersRef.current = extractHandlers(options);
|
|
221
|
+
React.useEffect(() => {
|
|
222
|
+
loadCDNLoader().then(() => {
|
|
223
|
+
setIsCdnLoaded(true);
|
|
224
|
+
}).catch((err) => {
|
|
225
|
+
const loadError = err instanceof Error ? err : new Error("Failed to load TapKit CDN");
|
|
226
|
+
setError(loadError);
|
|
227
|
+
handlersRef.current.onError?.(loadError);
|
|
228
|
+
});
|
|
229
|
+
}, []);
|
|
230
|
+
const setInstance = React.useCallback((instance) => {
|
|
231
|
+
elementRef.current = instance;
|
|
232
|
+
if (!instance) {
|
|
233
|
+
setIsReady(false);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const handleReady = () => {
|
|
237
|
+
setIsReady(true);
|
|
238
|
+
handlersRef.current.onReady?.();
|
|
239
|
+
};
|
|
240
|
+
const handleError = (e) => {
|
|
241
|
+
const customEvent = e;
|
|
242
|
+
const err = customEvent.detail?.error ?? new Error("Unknown error");
|
|
243
|
+
setError(err);
|
|
244
|
+
handlersRef.current.onError?.(err);
|
|
245
|
+
};
|
|
246
|
+
instance.addEventListener("tap-kit:ready", handleReady);
|
|
247
|
+
instance.addEventListener("tap-kit:error", handleError);
|
|
248
|
+
if (instance.isInitialized) {
|
|
249
|
+
setIsReady(true);
|
|
250
|
+
}
|
|
251
|
+
const setupEventHandlers = () => {
|
|
252
|
+
if (instance.events && typeof instance.events.onTimelineSeek === "function") {
|
|
253
|
+
if (handlersRef.current.onTimelineSeek) {
|
|
254
|
+
instance.events.onTimelineSeek(handlersRef.current.onTimelineSeek);
|
|
255
|
+
}
|
|
256
|
+
if (handlersRef.current.onAlarmFadeIn) {
|
|
257
|
+
instance.events.onAlarmFadeIn(handlersRef.current.onAlarmFadeIn);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
if (instance.ready && typeof instance.ready.then === "function") {
|
|
262
|
+
instance.ready.then(setupEventHandlers).catch(() => {
|
|
263
|
+
});
|
|
264
|
+
} else if (instance.isInitialized) {
|
|
265
|
+
setupEventHandlers();
|
|
266
|
+
} else {
|
|
267
|
+
instance.addEventListener("tap-kit:ready", setupEventHandlers, {
|
|
268
|
+
once: true
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}, []);
|
|
272
|
+
const configOptions = React.useMemo(
|
|
273
|
+
() => ({
|
|
274
|
+
apiKey: options.apiKey,
|
|
275
|
+
userId: options.userId,
|
|
276
|
+
courseId: options.courseId,
|
|
277
|
+
clipId: options.clipId,
|
|
278
|
+
clipPlayHead: options.clipPlayHead,
|
|
279
|
+
language: options.language,
|
|
280
|
+
buttonId: options.buttonId,
|
|
281
|
+
mode: options.mode,
|
|
282
|
+
debug: options.debug,
|
|
283
|
+
tapUrl: options.tapUrl,
|
|
284
|
+
apiUrl: options.apiUrl,
|
|
285
|
+
environment: options.environment
|
|
286
|
+
}),
|
|
287
|
+
[
|
|
288
|
+
options.apiKey,
|
|
289
|
+
options.userId,
|
|
290
|
+
options.courseId,
|
|
291
|
+
options.clipId,
|
|
292
|
+
options.clipPlayHead,
|
|
293
|
+
options.language,
|
|
294
|
+
options.buttonId,
|
|
295
|
+
options.mode,
|
|
296
|
+
options.debug,
|
|
297
|
+
options.tapUrl,
|
|
298
|
+
options.apiUrl,
|
|
299
|
+
options.environment
|
|
300
|
+
]
|
|
301
|
+
);
|
|
302
|
+
const handlers = React.useMemo(
|
|
303
|
+
() => ({
|
|
304
|
+
onReady: handlersRef.current.onReady,
|
|
305
|
+
onError: handlersRef.current.onError,
|
|
306
|
+
onTimelineSeek: handlersRef.current.onTimelineSeek,
|
|
307
|
+
onAlarmFadeIn: handlersRef.current.onAlarmFadeIn
|
|
308
|
+
}),
|
|
309
|
+
// Empty deps - handlers are accessed via ref which is always up-to-date
|
|
310
|
+
[]
|
|
311
|
+
);
|
|
312
|
+
const control = React.useMemo(
|
|
313
|
+
() => ({
|
|
314
|
+
setInstance,
|
|
315
|
+
options: configOptions,
|
|
316
|
+
handlers,
|
|
317
|
+
isCdnLoaded
|
|
318
|
+
}),
|
|
319
|
+
[setInstance, configOptions, handlers, isCdnLoaded]
|
|
320
|
+
);
|
|
321
|
+
const show = React.useCallback(() => {
|
|
322
|
+
if (!elementRef.current) {
|
|
323
|
+
console.warn("[useTapKit] Cannot show: element not mounted");
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
elementRef.current.show();
|
|
327
|
+
}, []);
|
|
328
|
+
const hide = React.useCallback(() => {
|
|
329
|
+
if (!elementRef.current) {
|
|
330
|
+
console.warn("[useTapKit] Cannot hide: element not mounted");
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
elementRef.current.hide();
|
|
334
|
+
}, []);
|
|
335
|
+
const setCourse = React.useCallback(
|
|
336
|
+
(course) => {
|
|
337
|
+
if (!elementRef.current) {
|
|
338
|
+
console.warn("[useTapKit] Cannot setCourse: element not mounted");
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
elementRef.current.setCourse(course);
|
|
342
|
+
},
|
|
343
|
+
[]
|
|
344
|
+
);
|
|
345
|
+
const video = React.useMemo(
|
|
346
|
+
() => ({
|
|
347
|
+
bind: (adapter, clipId) => {
|
|
348
|
+
if (!elementRef.current) {
|
|
349
|
+
console.warn("[useTapKit] Cannot bind video: element not mounted");
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
elementRef.current.video.bind(adapter, clipId);
|
|
353
|
+
},
|
|
354
|
+
unbind: () => {
|
|
355
|
+
elementRef.current?.video?.unbind();
|
|
356
|
+
}
|
|
357
|
+
}),
|
|
358
|
+
[]
|
|
359
|
+
);
|
|
360
|
+
return {
|
|
361
|
+
control,
|
|
362
|
+
isReady,
|
|
363
|
+
error,
|
|
364
|
+
show,
|
|
365
|
+
hide,
|
|
366
|
+
setCourse,
|
|
367
|
+
video
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
exports.TapKit = TapKit;
|
|
372
|
+
exports.useTapKit = useTapKit;
|
|
373
|
+
//# sourceMappingURL=react.js.map
|
|
3
374
|
//# sourceMappingURL=react.js.map
|