@jaaq/jaaq-sdk-js 1.0.0 → 1.0.1-dev-8b8d9e4-20251211

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 CHANGED
@@ -49,83 +49,770 @@ const video = await client.videos.getById('video-id');
49
49
  const collections = await client.collections.list();
50
50
  ```
51
51
 
52
- ### React Example: Basic Video
52
+ ## UI Components
53
+
54
+ The SDK includes ready-to-use embeddable video player components with custom controls, HLS support, and modern design.
55
+
56
+ **Architecture:** The SDK uses a layered approach:
57
+
58
+ - **Web Components** (`<jaaq-video-player>`) - Primary, framework-agnostic implementation
59
+ - **React Component** - Thin wrapper around web component with React-friendly API
60
+ - **Vanilla Player** - Internal implementation used by web components
61
+
62
+ **Recommended:** Use web components for all new integrations. They work everywhere (React, Vue, Angular, vanilla JS) and provide the best developer experience.
63
+
64
+ ### Web Components (Recommended)
65
+
66
+ Web components are the primary way to use the video player. They work in any framework or vanilla JavaScript.
67
+
68
+ See the [Web Components section](#web-components-native-custom-elements) below for installation, usage examples, and full API documentation.
69
+
70
+ ### React Video Player
71
+
72
+ The React component is a thin wrapper around the web component, providing React-friendly props and callbacks.
73
+
74
+ ```bash
75
+ npm install @jaaq/jaaq-sdk-js
76
+ ```
53
77
 
54
78
  ```typescript
55
- import { useEffect, useRef } from "react";
56
- import { createJaaqClient } from "@jaaq/jaaq-sdk-js";
79
+ import { VideoPlayer } from '@jaaq/jaaq-sdk-js/ui/react';
57
80
 
58
- const API_KEY = import.meta.env.VITE_JAAQ_API_KEY;
59
- const CLIENT_ID = import.meta.env.VITE_JAAQ_CLIENT_ID;
81
+ function App() {
82
+ return (
83
+ <VideoPlayer
84
+ videoId="your-video-id"
85
+ apiKey="your-api-key"
86
+ clientId="your-client-id"
87
+ autoplay={false}
88
+ showInfo={true}
89
+ onPlay={() => console.log('Playing')}
90
+ onPause={() => console.log('Paused')}
91
+ onError={(error) => console.error(error)}
92
+ onLoaded={(video) => console.log('Loaded:', video)}
93
+ />
94
+ );
95
+ }
96
+ ```
97
+
98
+ **Or use the web component directly in React:**
99
+
100
+ ```typescript
101
+ import '@jaaq/jaaq-sdk-js/ui/webcomponents';
102
+ import type { JaaqVideoPlayerElement } from '@jaaq/jaaq-sdk-js/ui/webcomponents';
103
+
104
+ function App() {
105
+ const playerRef = useRef<JaaqVideoPlayerElement>(null);
106
+
107
+ return (
108
+ <jaaq-video-player
109
+ ref={playerRef}
110
+ video-id="your-video-id"
111
+ api-key="your-api-key"
112
+ client-id="your-client-id"
113
+ autoplay="false"
114
+ />
115
+ );
116
+ }
117
+ ```
118
+
119
+ #### React Player Props
120
+
121
+ | Prop | Type | Required | Default | Description |
122
+ | -------------------- | --------------------------- | ----------- | ----------- | ----------------------------------------------- |
123
+ | `videoId` | `string` | Yes | - | Video ID to load |
124
+ | `apiKey` | `string` | Conditional | - | API key (required if no client) |
125
+ | `clientId` | `string` | Conditional | - | Client ID (required if no client) |
126
+ | `client` | `JaaqClient` | Conditional | - | Existing SDK client instance |
127
+ | `baseUrl` | `string` | No | API default | Custom API base URL |
128
+ | `autoplay` | `boolean` | No | `false` | Auto-play video on load |
129
+ | `width` | `string` | No | `'100%'` | Player width |
130
+ | `height` | `string` | No | `'auto'` | Player height |
131
+ | `className` | `string` | No | `''` | Additional CSS class |
132
+ | `showInfo` | `boolean` | No | `false` | Show video metadata below player |
133
+ | `onPlay` | `() => void` | No | - | Called when video starts playing |
134
+ | `onPause` | `() => void` | No | - | Called when video pauses |
135
+ | `onError` | `(error: Error) => void` | No | - | Called on error |
136
+ | `onLoaded` | `(video: VideoDTO) => void` | No | - | Called when video loads |
137
+ | `onTimeUpdate` | `(time: number) => void` | No | - | Called on time update (current time in seconds) |
138
+ | `onVolumeChange` | `(volume: number) => void` | No | - | Called on volume change (0-1) |
139
+ | `onEnded` | `() => void` | No | - | Called when video finishes |
140
+ | `onFullscreenChange` | `(isFull: boolean) => void` | No | - | Called on fullscreen state change |
141
+
142
+ **Ref Methods:**
143
+
144
+ ```typescript
145
+ const playerRef = useRef<VideoPlayerHandle>(null);
146
+
147
+ playerRef.current?.play();
148
+ playerRef.current?.pause();
149
+ playerRef.current?.setVolume(0.5);
150
+ playerRef.current?.toggleMute();
151
+ playerRef.current?.seek(30);
152
+ playerRef.current?.getState();
153
+ playerRef.current?.destroy();
154
+ ```
155
+
156
+ ### Web Components (Native Custom Elements)
157
+
158
+ The SDK provides a native web component `<jaaq-video-player>` with Shadow DOM encapsulation. Perfect for framework-agnostic applications or when you need style isolation.
159
+
160
+ #### Installation & Registration
161
+
162
+ **For ES modules (npm/import):**
163
+
164
+ ```bash
165
+ npm install @jaaq/jaaq-sdk-js hls.js
166
+ ```
167
+
168
+ **Auto-registration (default)** - Components register automatically when imported:
169
+
170
+ ```javascript
171
+ import '@jaaq/jaaq-sdk-js/ui/webcomponents';
172
+ ```
173
+
174
+ **For browser/CDN (no build step):**
175
+
176
+ Use the bundled version (includes hls.js, auto-registers):
177
+
178
+ ```html
179
+ <script src="https://cdn.jaaq.app/jaaq-sdk-js/latest/ui/webcomponents/jaaq-webcomponents-bundled.min.js"></script>
180
+ ```
181
+
182
+ **Manual registration** (for advanced use cases):
183
+
184
+ ```javascript
185
+ import { registerJaaqComponents } from '@jaaq/jaaq-sdk-js/ui/webcomponents';
60
186
 
61
- export default function VideoExample({ id }: { id: string }) {
62
- const videoRef = useRef<HTMLVideoElement | null>(null);
187
+ registerJaaqComponents();
188
+ ```
189
+
190
+ To disable auto-registration with the bundled script:
191
+
192
+ ```html
193
+ <script src="..." data-auto-register="false"></script>
194
+ <script>
195
+ JaaqWebComponents.registerJaaqComponents();
196
+ </script>
197
+ ```
198
+
199
+ #### Declarative Usage
200
+
201
+ ```html
202
+ <!DOCTYPE html>
203
+ <html>
204
+ <head>
205
+ <script src="https://cdn.jaaq.app/jaaq-sdk-js/latest/ui/webcomponents/jaaq-webcomponents-bundled.min.js"></script>
206
+ </head>
207
+ <body>
208
+ <jaaq-video-player video-id="your-video-id" api-key="your-api-key" client-id="your-client-id" autoplay="false" width="100%">
209
+ </jaaq-video-player>
210
+ </body>
211
+ </html>
212
+ ```
213
+
214
+ #### Programmatic Usage
215
+
216
+ ```html
217
+ <script type="module">
218
+ import '@jaaq/jaaq-sdk-js/ui/webcomponents';
219
+
220
+ const player = document.createElement('jaaq-video-player');
221
+ player.setAttribute('video-id', 'your-video-id');
222
+ player.setAttribute('api-key', 'your-api-key');
223
+ player.setAttribute('client-id', 'your-client-id');
224
+
225
+ player.addEventListener('jaaq:loaded', (e) => {
226
+ console.log('Video loaded:', e.detail);
227
+ });
228
+
229
+ player.addEventListener('jaaq:play', () => {
230
+ console.log('Playing');
231
+ });
232
+
233
+ player.addEventListener('jaaq:error', (e) => {
234
+ console.error('Error:', e.detail);
235
+ });
236
+
237
+ document.body.appendChild(player);
238
+ </script>
239
+ ```
240
+
241
+ #### Web Component Attributes
242
+
243
+ | Attribute | Type | Required | Default | Description |
244
+ | ----------- | --------- | ----------- | ----------- | ------------------------------------------ |
245
+ | `video-id` | `string` | Yes | - | Video ID to load |
246
+ | `api-key` | `string` | Conditional | - | API key (required if no client property) |
247
+ | `client-id` | `string` | Conditional | - | Client ID (required if no client property) |
248
+ | `base-url` | `string` | No | API default | Custom API base URL |
249
+ | `autoplay` | `boolean` | No | `false` | Auto-play video on load |
250
+ | `width` | `string` | No | `'100%'` | Player width |
251
+ | `height` | `string` | No | `'auto'` | Player height |
252
+
253
+ #### Web Component Properties & Methods
254
+
255
+ ```javascript
256
+ const player = document.querySelector('jaaq-video-player');
257
+
258
+ player.client = existingJaaqClient;
259
+
260
+ player.play();
261
+ player.pause();
262
+ player.setVolume(0.5);
263
+ player.toggleMute();
264
+ player.seek(30);
265
+ const state = player.getState();
266
+ player.destroy();
267
+ ```
268
+
269
+ #### Web Component Custom Events
270
+
271
+ All events are prefixed with `jaaq:` and use the standard `CustomEvent` API:
272
+
273
+ ```javascript
274
+ player.addEventListener('jaaq:play', () => {});
275
+ player.addEventListener('jaaq:pause', () => {});
276
+ player.addEventListener('jaaq:timeupdate', (e) => {
277
+ console.log('Current time:', e.detail);
278
+ });
279
+ player.addEventListener('jaaq:volumechange', (e) => {
280
+ console.log('Volume:', e.detail);
281
+ });
282
+ player.addEventListener('jaaq:ended', () => {});
283
+ player.addEventListener('jaaq:error', (e) => {
284
+ console.error('Error:', e.detail);
285
+ });
286
+ player.addEventListener('jaaq:loaded', (e) => {
287
+ console.log('Video:', e.detail);
288
+ });
289
+ player.addEventListener('jaaq:fullscreenchange', (e) => {
290
+ console.log('Fullscreen:', e.detail);
291
+ });
292
+ ```
293
+
294
+ #### Using Web Components in React
295
+
296
+ Web components work seamlessly with React. Import the component registration and use it as a JSX element:
297
+
298
+ ```tsx
299
+ import { useEffect, useRef } from 'react';
300
+ import '@jaaq/jaaq-sdk-js/ui/webcomponents';
301
+ import type { JaaqVideoPlayerElement } from '@jaaq/jaaq-sdk-js/ui/webcomponents';
302
+
303
+ function VideoComponent() {
304
+ const playerRef = useRef<JaaqVideoPlayerElement>(null);
63
305
 
64
306
  useEffect(() => {
65
- (async () => {
66
- const client = createJaaqClient({
67
- apiKey: API_KEY,
68
- clientId: CLIENT_ID
307
+ const player = playerRef.current;
308
+ if (!player) return;
309
+
310
+ const handlePlay = () => console.log('Playing');
311
+ const handleLoaded = (e: Event) => {
312
+ console.log('Loaded:', (e as CustomEvent).detail);
313
+ };
314
+
315
+ player.addEventListener('jaaq:play', handlePlay);
316
+ player.addEventListener('jaaq:loaded', handleLoaded);
317
+
318
+ return () => {
319
+ player.removeEventListener('jaaq:play', handlePlay);
320
+ player.removeEventListener('jaaq:loaded', handleLoaded);
321
+ };
322
+ }, []);
323
+
324
+ return (
325
+ <jaaq-video-player
326
+ ref={playerRef}
327
+ video-id="your-video-id"
328
+ api-key="your-api-key"
329
+ client-id="your-client-id"
330
+ autoplay="false"
331
+ width="100%"
332
+ />
333
+ );
334
+ }
335
+ ```
336
+
337
+ Programmatic control in React:
338
+
339
+ ```tsx
340
+ playerRef.current?.play();
341
+ playerRef.current?.pause();
342
+ playerRef.current?.setVolume(0.5);
343
+ const state = playerRef.current?.getState();
344
+ ```
345
+
346
+ #### Why Use Web Components?
347
+
348
+ - 🔒 **Shadow DOM** - Style encapsulation, no CSS conflicts
349
+ - 🌐 **Framework-agnostic** - Works with React, Vue, Angular, vanilla JS, any framework
350
+ - 📦 **Standards-based** - W3C Custom Elements API, no framework overhead
351
+ - ♿ **Declarative** - Use as simple HTML elements
352
+ - 🎯 **Clean API** - Attributes for configuration, properties for objects
353
+ - 🔧 **Single implementation** - One codebase maintained in one place
354
+ - 🚀 **Future-proof** - Native browser standard, here to stay
355
+
356
+ ### UI Features
357
+
358
+ All video players include:
359
+
360
+ - ▶️ Custom video controls (play/pause, progress bar, volume, fullscreen)
361
+ - 🎬 HLS streaming support (adaptive bitrate with hls.js)
362
+ - 📹 MP4 playback support
363
+ - 🎨 Modern, responsive design
364
+ - ⚡ Loading states and error handling
365
+ - 📊 Event system for tracking player state
366
+ - 📱 Mobile-friendly touch controls
367
+
368
+ ## CDN Usage (No Build Step)
369
+
370
+ Use JAAQ SDK directly in the browser without npm or build tools. All files are available from our CDN:
371
+
372
+ **Base URL:** `https://cdn.jaaq.app/jaaq-sdk-js/latest/`
373
+
374
+ ### Web Components (Recommended)
375
+
376
+ The easiest way to embed video players. Bundled version includes hls.js:
377
+
378
+ ```html
379
+ <!DOCTYPE html>
380
+ <html>
381
+ <head>
382
+ <script src="https://cdn.jaaq.app/jaaq-sdk-js/latest/ui/webcomponents/jaaq-webcomponents-bundled.min.js"></script>
383
+ </head>
384
+ <body>
385
+ <jaaq-video-player video-id="your-video-id" api-key="your-api-key" client-id="your-client-id"> </jaaq-video-player>
386
+ </body>
387
+ </html>
388
+ ```
389
+
390
+ See [Web Components section](#web-components-native-custom-elements) for full documentation.
391
+
392
+ ### Vanilla UI Player
393
+
394
+ For programmatic control with the vanilla JavaScript player:
395
+
396
+ ```html
397
+ <!DOCTYPE html>
398
+ <html>
399
+ <head>
400
+ <script src="https://cdn.jaaq.app/jaaq-sdk-js/latest/ui/jaaq-ui-bundled.min.js"></script>
401
+ </head>
402
+ <body>
403
+ <div id="player-container"></div>
404
+ <script>
405
+ const container = document.getElementById('player-container');
406
+ const player = new JaaqUI.JaaqVideoPlayer(container, {
407
+ apiKey: 'your-api-key',
408
+ clientId: 'your-client-id',
409
+ videoId: 'your-video-id',
410
+ autoplay: false,
69
411
  });
70
- const resp = await client.videos.getById(id);
71
- const url = resp.video.videoUrl;
72
- videoRef.current.src = url;
73
- })();
74
- }, [id]);
75
412
 
413
+ player.on('loaded', (video) => console.log('Video loaded:', video));
414
+ player.on('play', () => console.log('Playing'));
415
+ </script>
416
+ </body>
417
+ </html>
418
+ ```
419
+
420
+ See [Vanilla UI Player Configuration](#vanilla-ui-player-configuration) for all options.
421
+
422
+ ### Core SDK
423
+
424
+ Use the core SDK for API access without UI components:
425
+
426
+ ```html
427
+ <!DOCTYPE html>
428
+ <html>
429
+ <head>
430
+ <script src="https://cdn.jaaq.app/jaaq-sdk-js/latest/jaaq-sdk.min.js"></script>
431
+ </head>
432
+ <body>
433
+ <script>
434
+ const client = JaaqSDK.createJaaqClient({
435
+ apiKey: 'your-api-key',
436
+ clientId: 'your-client-id',
437
+ });
438
+
439
+ async function loadVideo() {
440
+ const response = await client.videos.getById('video-id');
441
+ console.log('Video:', response.video);
442
+ }
443
+
444
+ loadVideo();
445
+ </script>
446
+ </body>
447
+ </html>
448
+ ```
449
+
450
+ ### React Components (UMD)
451
+
452
+ Use React components from CDN without npm:
453
+
454
+ ```html
455
+ <!DOCTYPE html>
456
+ <html>
457
+ <head>
458
+ <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
459
+ <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
460
+ <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
461
+ <script src="https://cdn.jaaq.app/jaaq-sdk-js/latest/ui/react/jaaq-ui-react.min.js"></script>
462
+ </head>
463
+ <body>
464
+ <div id="root"></div>
465
+ <script>
466
+ const { VideoPlayer } = JaaqUIReact;
467
+ const root = ReactDOM.createRoot(document.getElementById('root'));
468
+
469
+ root.render(
470
+ React.createElement(VideoPlayer, {
471
+ videoId: 'your-video-id',
472
+ apiKey: 'your-api-key',
473
+ clientId: 'your-client-id',
474
+ autoplay: false,
475
+ }),
476
+ );
477
+ </script>
478
+ </body>
479
+ </html>
480
+ ```
481
+
482
+ See [React Video Player](#react-video-player) for component props and examples.
483
+
484
+ ### Embed via Iframe
485
+
486
+ The simplest integration - just use an iframe:
487
+
488
+ ```html
489
+ <iframe
490
+ src="https://cdn.jaaq.app/jaaq-sdk-js/latest/embed/embed.html?apiKey=YOUR_API_KEY&clientId=YOUR_CLIENT_ID&videoId=YOUR_VIDEO_ID"
491
+ width="800"
492
+ height="450"
493
+ frameborder="0"
494
+ allowfullscreen
495
+ >
496
+ </iframe>
497
+ ```
498
+
499
+ **Control via postMessage:**
500
+
501
+ ```javascript
502
+ const iframe = document.querySelector('iframe');
503
+
504
+ iframe.contentWindow.postMessage(
505
+ {
506
+ type: 'jaaq-player-command',
507
+ command: 'play',
508
+ },
509
+ '*',
510
+ );
511
+
512
+ window.addEventListener('message', (event) => {
513
+ if (event.data?.type === 'jaaq-player-event') {
514
+ console.log('Event:', event.data.event, event.data.data);
515
+ }
516
+ });
517
+ ```
518
+
519
+ See [Embed iframe URL Parameters](#embed-iframe-url-parameters) for all available options.
520
+
521
+ ### Available CDN Files
522
+
523
+ | File | Description | Global Variable |
524
+ | ---------------------------------------------------- | -------------------------------- | ------------------- |
525
+ | `jaaq-sdk.min.js` | Core SDK | `JaaqSDK` |
526
+ | `ui/jaaq-ui-bundled.min.js` | Vanilla UI + hls.js | `JaaqUI` |
527
+ | `ui/jaaq-ui.min.js` | Vanilla UI (requires hls.js) | `JaaqUI` |
528
+ | `ui/react/jaaq-ui-react.min.js` | React components | `JaaqUIReact` |
529
+ | `ui/webcomponents/jaaq-webcomponents-bundled.min.js` | Web components + hls.js | `JaaqWebComponents` |
530
+ | `ui/webcomponents/jaaq-webcomponents.min.js` | Web components (requires hls.js) | `JaaqWebComponents` |
531
+ | `embed/embed.html` | Embeddable iframe player | N/A |
532
+
533
+ **Note:** Bundled versions include hls.js and have no external dependencies. Non-bundled versions require loading hls.js separately.
534
+
535
+ ## API Reference
536
+
537
+ Complete reference for all SDK components and their configuration options.
538
+
539
+ ### Vanilla UI Player Configuration
540
+
541
+ The `JaaqVideoPlayer` class accepts these configuration options:
542
+
543
+ | Option | Type | Required | Default | Description |
544
+ | ----------- | ------------ | ----------- | -------- | ------------------------------------------ |
545
+ | `videoId` | `string` | Yes | - | Video ID to load |
546
+ | `apiKey` | `string` | Conditional | - | API key (required if no client provided) |
547
+ | `clientId` | `string` | Conditional | - | Client ID (required if no client provided) |
548
+ | `client` | `JaaqClient` | Conditional | - | Existing JaaqClient instance |
549
+ | `baseUrl` | `string` | No | API URL | Custom API base URL |
550
+ | `autoplay` | `boolean` | No | `false` | Auto-play video on load |
551
+ | `width` | `string` | No | `'100%'` | CSS width of player container |
552
+ | `height` | `string` | No | `'auto'` | CSS height of player container |
553
+ | `className` | `string` | No | `''` | Additional CSS class name(s) for player |
554
+
555
+ **Usage:**
556
+
557
+ ```javascript
558
+ import { JaaqVideoPlayer } from '@jaaq/jaaq-sdk-js/ui';
559
+
560
+ const player = new JaaqVideoPlayer('#container', {
561
+ videoId: 'your-video-id',
562
+ apiKey: 'your-api-key',
563
+ clientId: 'your-client-id',
564
+ autoplay: false,
565
+ width: '800px',
566
+ height: '450px',
567
+ className: 'my-custom-player',
568
+ });
569
+
570
+ player.on('loaded', (video) => console.log('Video loaded:', video));
571
+ player.on('play', () => console.log('Playing'));
572
+ player.play();
573
+ ```
574
+
575
+ ### Embed iframe URL Parameters
576
+
577
+ The embeddable iframe player accepts these URL query parameters:
578
+
579
+ | Parameter | Type | Required | Default | Description |
580
+ | ---------- | -------- | -------- | ------- | ----------------------------------- |
581
+ | `apiKey` | `string` | Yes | - | Your JAAQ API key |
582
+ | `clientId` | `string` | Yes | - | Your client identifier |
583
+ | `videoId` | `string` | Yes | - | Video ID to load |
584
+ | `autoplay` | `string` | No | `false` | Auto-play video ('true' or 'false') |
585
+ | `width` | `string` | No | `100%` | CSS width of player |
586
+ | `height` | `string` | No | `100%` | CSS height of player |
587
+ | `baseUrl` | `string` | No | API URL | Custom API base URL |
588
+
589
+ **Usage:**
590
+
591
+ ```html
592
+ <iframe
593
+ src="https://cdn.jaaq.app/jaaq-sdk-js/latest/embed/embed.html?apiKey=YOUR_API_KEY&clientId=YOUR_CLIENT_ID&videoId=YOUR_VIDEO_ID&autoplay=false"
594
+ width="800"
595
+ height="450"
596
+ frameborder="0"
597
+ allowfullscreen
598
+ >
599
+ </iframe>
600
+ ```
601
+
602
+ ### Player Methods
603
+
604
+ All player implementations (Vanilla, Web Component, React) provide these methods:
605
+
606
+ | Method | Parameters | Returns | Description |
607
+ | ------------------- | ---------------- | ------------- | ------------------------------------------ |
608
+ | `play()` | - | `void` | Start or resume video playback |
609
+ | `pause()` | - | `void` | Pause video playback |
610
+ | `seek(time)` | `time: number` | `void` | Seek to specific time in seconds |
611
+ | `setVolume(volume)` | `volume: number` | `void` | Set volume level (0-1) |
612
+ | `toggleMute()` | - | `void` | Toggle audio mute state |
613
+ | `getState()` | - | `PlayerState` | Get current player state object |
614
+ | `destroy()` | - | `void` | Clean up player and remove event listeners |
615
+
616
+ **Vanilla Player Example:**
617
+
618
+ ```javascript
619
+ player.play();
620
+ player.seek(30);
621
+ player.setVolume(0.5);
622
+ const state = player.getState();
623
+ ```
624
+
625
+ **Web Component Example:**
626
+
627
+ ```javascript
628
+ const player = document.querySelector('jaaq-video-player');
629
+ player.play();
630
+ player.seek(30);
631
+ player.setVolume(0.5);
632
+ const state = player.getState();
633
+ ```
634
+
635
+ **React Example:**
636
+
637
+ ```typescript
638
+ const playerRef = useRef<VideoPlayerHandle>(null);
639
+ playerRef.current?.play();
640
+ playerRef.current?.seek(30);
641
+ playerRef.current?.setVolume(0.5);
642
+ const state = playerRef.current?.getState();
643
+ ```
644
+
645
+ ### Player Events
646
+
647
+ All player implementations emit these events:
648
+
649
+ | Event | Payload Type | Description |
650
+ | ------------------ | ------------ | ------------------------------------------------ |
651
+ | `play` | `void` | Fired when video playback starts |
652
+ | `pause` | `void` | Fired when video playback pauses |
653
+ | `timeupdate` | `number` | Fired during playback, provides current time (s) |
654
+ | `volumechange` | `number` | Fired when volume changes, provides level (0-1) |
655
+ | `ended` | `void` | Fired when video playback completes |
656
+ | `error` | `Error` | Fired when an error occurs |
657
+ | `loaded` | `VideoDTO` | Fired when video metadata loads successfully |
658
+ | `fullscreenchange` | `boolean` | Fired when fullscreen state changes |
659
+
660
+ **Vanilla Player Event Handling:**
661
+
662
+ ```javascript
663
+ player.on('play', () => console.log('Playing'));
664
+ player.on('pause', () => console.log('Paused'));
665
+ player.on('timeupdate', (time) => console.log('Current time:', time));
666
+ player.on('loaded', (video) => console.log('Video:', video));
667
+ player.on('error', (error) => console.error('Error:', error));
668
+ ```
669
+
670
+ **Web Component Event Handling:**
671
+
672
+ ```javascript
673
+ const player = document.querySelector('jaaq-video-player');
674
+ player.addEventListener('jaaq:play', () => console.log('Playing'));
675
+ player.addEventListener('jaaq:pause', () => console.log('Paused'));
676
+ player.addEventListener('jaaq:timeupdate', (e) => console.log('Time:', e.detail));
677
+ player.addEventListener('jaaq:loaded', (e) => console.log('Video:', e.detail));
678
+ player.addEventListener('jaaq:error', (e) => console.error('Error:', e.detail));
679
+ ```
680
+
681
+ **React Event Handling:**
682
+
683
+ ```typescript
684
+ <VideoPlayer
685
+ videoId="video-id"
686
+ apiKey="api-key"
687
+ clientId="client-id"
688
+ onPlay={() => console.log('Playing')}
689
+ onPause={() => console.log('Paused')}
690
+ onTimeUpdate={(time) => console.log('Time:', time)}
691
+ onLoaded={(video) => console.log('Video:', video)}
692
+ onError={(error) => console.error('Error:', error)}
693
+ />
694
+ ```
695
+
696
+ **Embed iframe Event Handling (postMessage):**
697
+
698
+ ```javascript
699
+ window.addEventListener('message', (event) => {
700
+ if (event.data?.type === 'jaaq-player-event') {
701
+ console.log('Event:', event.data.event, event.data.data);
702
+ }
703
+ });
704
+ ```
705
+
706
+ ### Player State Object
707
+
708
+ The `getState()` method returns a `PlayerState` object with these properties:
709
+
710
+ | Property | Type | Description |
711
+ | -------------- | ------------------ | ----------------------------------------------- |
712
+ | `isPlaying` | `boolean` | Whether video is currently playing |
713
+ | `currentTime` | `number` | Current playback time in seconds |
714
+ | `duration` | `number` | Total video duration in seconds |
715
+ | `volume` | `number` | Current volume level (0-1) |
716
+ | `isMuted` | `boolean` | Whether audio is muted |
717
+ | `isFullscreen` | `boolean` | Whether player is in fullscreen mode |
718
+ | `isLoading` | `boolean` | Whether video is currently loading |
719
+ | `error` | `string \| null` | Error message if error occurred, null otherwise |
720
+ | `videoData` | `VideoDTO \| null` | Loaded video metadata, null if not loaded |
721
+
722
+ **Example:**
723
+
724
+ ```javascript
725
+ const state = player.getState();
726
+ console.log('Playing:', state.isPlaying);
727
+ console.log('Current time:', state.currentTime);
728
+ console.log('Duration:', state.duration);
729
+ console.log('Volume:', state.volume);
730
+ console.log('Muted:', state.isMuted);
731
+ console.log('Fullscreen:', state.isFullscreen);
732
+ console.log('Loading:', state.isLoading);
733
+ if (state.error) {
734
+ console.error('Error:', state.error);
735
+ }
736
+ if (state.videoData) {
737
+ console.log('Video ID:', state.videoData.id);
738
+ console.log('Title:', state.videoData.question || state.videoData.description);
739
+ }
740
+ ```
741
+
742
+ ### React Example: Using VideoPlayer Component
743
+
744
+ The recommended approach is to use the `VideoPlayer` component, which handles all video playback, HLS streaming, and provides custom controls:
745
+
746
+ ```typescript
747
+ import { VideoPlayer } from '@jaaq/jaaq-sdk-js/ui/react';
748
+
749
+ const API_KEY = import.meta.env.VITE_JAAQ_API_KEY;
750
+ const CLIENT_ID = import.meta.env.VITE_JAAQ_CLIENT_ID;
751
+
752
+ export default function VideoExample({ videoId }: { videoId: string }) {
76
753
  return (
77
- <video
78
- ref={videoRef}
79
- autoPlay
80
- controls
754
+ <VideoPlayer
755
+ videoId={videoId}
756
+ apiKey={API_KEY}
757
+ clientId={CLIENT_ID}
758
+ autoplay={false}
759
+ showInfo={true}
760
+ onPlay={() => console.log('Playing')}
761
+ onPause={() => console.log('Paused')}
762
+ onError={(error) => console.error('Error:', error)}
763
+ onLoaded={(video) => console.log('Loaded:', video)}
81
764
  />
82
765
  );
83
766
  }
84
767
  ```
85
768
 
86
- ### React Example: HLS Streaming (.m3u8)
769
+ ### React Example: Using Web Component
770
+
771
+ You can also use the native web component directly in React:
87
772
 
88
773
  ```typescript
89
- import { useEffect, useRef } from "react";
90
- import { createJaaqClient } from "@jaaq/jaaq-sdk-js";
91
- import Hls from "hls.js";
774
+ import { useRef, useEffect } from 'react';
775
+ import '@jaaq/jaaq-sdk-js/ui/webcomponents';
776
+ import type { JaaqVideoPlayerElement } from '@jaaq/jaaq-sdk-js/ui/webcomponents';
92
777
 
93
- const API_KEY = import.meta.env.VITE_JAAQ_API_KEY!;
94
- const CLIENT_ID = import.meta.env.VITE_JAAQ_CLIENT_ID!;
778
+ const API_KEY = import.meta.env.VITE_JAAQ_API_KEY;
779
+ const CLIENT_ID = import.meta.env.VITE_JAAQ_CLIENT_ID;
95
780
 
96
- export default function VideoExample({ id }: { id: string }) {
97
- const videoRef = useRef<HTMLVideoElement | null>(null);
781
+ export default function VideoExample({ videoId }: { videoId: string }) {
782
+ const playerRef = useRef<JaaqVideoPlayerElement>(null);
98
783
 
99
784
  useEffect(() => {
100
- let hls: Hls | undefined;
101
-
102
- (async () => {
103
- const client = createJaaqClient({ apiKey: API_KEY, clientId: CLIENT_ID });
104
- const resp = await client.videos.getById(id);
105
- const url = resp.video.videoUrl;
106
- const video = videoRef.current;
107
- if (!video || !url) return;
108
-
109
- if (Hls.isSupported()) {
110
- hls = new Hls();
111
- hls.loadSource(url);
112
- hls.attachMedia(video);
113
- }
114
- })();
785
+ const player = playerRef.current;
786
+ if (!player) return;
787
+
788
+ const handlePlay = () => console.log('Playing');
789
+ const handleLoaded = (e: Event) => {
790
+ console.log('Loaded:', (e as CustomEvent).detail);
791
+ };
792
+
793
+ player.addEventListener('jaaq:play', handlePlay);
794
+ player.addEventListener('jaaq:loaded', handleLoaded);
115
795
 
116
- return () => hls?.destroy();
117
- }, [id]);
796
+ return () => {
797
+ player.removeEventListener('jaaq:play', handlePlay);
798
+ player.removeEventListener('jaaq:loaded', handleLoaded);
799
+ };
800
+ }, []);
118
801
 
119
802
  return (
120
- <video
121
- ref={videoRef}
122
- autoPlay
123
- controls
803
+ <jaaq-video-player
804
+ ref={playerRef}
805
+ video-id={videoId}
806
+ api-key={API_KEY}
807
+ client-id={CLIENT_ID}
808
+ autoplay="false"
124
809
  />
125
810
  );
126
811
  }
127
812
  ```
128
813
 
814
+ **For more examples**, see the [React examples directory](./examples/react-vite/) which includes working implementations with Vite.
815
+
129
816
  ## Configuration
130
817
 
131
818
  | Option | Type | Required | Default | Description |
@@ -136,8 +823,25 @@ export default function VideoExample({ id }: { id: string }) {
136
823
  | `fetch` | `Function` | No | `globalThis.fetch` | Custom fetch implementation |
137
824
  | `timeoutMs` | `number` | No | - | Request timeout in milliseconds |
138
825
 
826
+ ## Architecture
827
+
828
+ The SDK has a layered UI architecture:
829
+
830
+ 1. **Vanilla Player** (Internal) - Core implementation with HLS support, controls, events
831
+ 2. **Web Components** - Wraps vanilla player with Shadow DOM, primary public API
832
+ 3. **React Component** - Thin wrapper around web component for React-friendly API
833
+
834
+ This architecture ensures:
835
+
836
+ - Single source of truth for player logic
837
+ - Framework-agnostic distribution via web components
838
+ - Consistent behavior across all integrations
839
+ - Easy maintenance and updates
840
+
139
841
  ## Resources
140
842
 
843
+ - [Browser Examples](./examples/browser/README.md) - HTML examples for web components and vanilla JS
844
+ - [React Examples](./examples/react-vite/README.md) - React + Vite integration examples
141
845
  - [Development Guide](./DEVELOPMENT.md) - For SDK contributors and developers
142
846
  - [Release Notes](./RELEASE.md) - Version history and changelog
143
847