@libs-ui/components-audio 0.2.356-41 → 0.2.356-43

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
@@ -1,196 +1,349 @@
1
1
  # @libs-ui/components-audio
2
2
 
3
- > Component phát âm thanh với đầy đủ chức năng điều khiển cho Angular.
3
+ > Component audio player đầy đủ tính năng cho Angular hỗ trợ play/pause, seek, volume, mute và download có kiểm soát quyền.
4
4
 
5
5
  ## Giới thiệu
6
6
 
7
- `LibsUiComponentsAudioComponent` là một standalone Angular component cung cấp giao diện phát audio với các tính năng:
7
+ `LibsUiComponentsAudioComponent` là một standalone Angular component cung cấp giao diện phát audio hoàn chỉnh. Component tích hợp thanh tiến độ, điều chỉnh âm lượng dạng hover-reveal, hiển thị thời gian HH:MM:SS và cơ chế download có kiểm soát quyền thông qua callback. Ngoài việc phản ứng trực tiếp qua Output events, component còn cung cấp `IAudioFunctionControlEvent` — một API điều khiển chương trình từ component cha.
8
8
 
9
- - Phát/tạm dừng âm thanh với giao diện trực quan
10
- - ✅ Điều chỉnh âm lượng với thanh trượt ẩn/hiện
11
- - ✅ Hiển thị thời gian theo định dạng HH:MM:SS
12
- - ✅ Thanh tiến độ với chức năng tua nhanh/tua lại
13
- - ✅ Tải xuống audio với kiểm soát quyền
14
- - ✅ Function Control API cho phép điều khiển từ bên ngoài
15
- - ✅ Hỗ trợ OnPush Change Detection cho hiệu năng cao
16
- - ✅ Sử dụng Angular Signals cho state management
9
+ ## Tính năng
10
+
11
+ - ✅ Phát/tạm dừng audio với giao diện icon trực quan
12
+ - ✅ Thanh tiến độ tua nhanh/tua lại (seek 0–100%)
13
+ - ✅ Điều chỉnh âm lượng với slider ẩn/hiện theo hover
14
+ - ✅ Bật/tắt tiếng (mute/unmute) đồng bộ với volume slider
15
+ - ✅ Hiển thị thời gian định dạng HH:MM:SS (currentTime / duration)
16
+ - ✅ Download file audio kiểm soát quyền qua async callback
17
+ - ✅ Function Control API — điều khiển audio từ component cha qua `outFunctionsControl`
18
+ - ✅ Hỗ trợ bàn phím (Enter, Space) cho mọi control
19
+ - ✅ Angular Signals + OnPush Change Detection
20
+
21
+ ## Khi nào sử dụng
22
+
23
+ - Cần nhúng audio player có giao diện chuẩn vào trang chi tiết, comment, hoặc tin nhắn
24
+ - Cần điều khiển audio từ bên ngoài component (play, pause, seek, volume) thông qua API
25
+ - Cần tracking trạng thái audio theo thời gian thực (đang phát, mute, vị trí hiện tại)
26
+ - Cần giới hạn quyền download file audio theo logic nghiệp vụ (kiểm tra permission trước khi cho tải)
27
+ - Phát file MP3, WAV, OGG trong ứng dụng web Angular
17
28
 
18
29
  ## Cài đặt
19
30
 
20
31
  ```bash
21
- # npm
22
32
  npm install @libs-ui/components-audio
33
+ ```
23
34
 
24
- # yarn
25
- yarn add @libs-ui/components-audio
35
+ ## Import
36
+
37
+ ```typescript
38
+ import { LibsUiComponentsAudioComponent, IAudioFunctionControlEvent } from '@libs-ui/components-audio';
26
39
  ```
27
40
 
28
- ## Sử dụng
41
+ ## dụ sử dụng
29
42
 
30
- ### Import Component
43
+ ### dụ 1 — Cơ bản (Minimal)
31
44
 
32
45
  ```typescript
33
46
  import { Component } from '@angular/core';
47
+ import { LibsUiComponentsAudioComponent } from '@libs-ui/components-audio';
48
+
49
+ @Component({
50
+ selector: 'app-audio-basic',
51
+ standalone: true,
52
+ imports: [LibsUiComponentsAudioComponent],
53
+ template: `
54
+ <libs_ui-components-audio
55
+ [fileAudio]="audioUrl"
56
+ [checkPermissionDownloadAudio]="checkDownloadPermission"
57
+ />
58
+ `,
59
+ })
60
+ export class AudioBasicComponent {
61
+ audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';
62
+
63
+ checkDownloadPermission = (): Promise<boolean> => Promise.resolve(true);
64
+ }
65
+ ```
66
+
67
+ ### Ví dụ 2 — Lắng nghe trạng thái qua Output
68
+
69
+ ```typescript
70
+ import { Component, signal } from '@angular/core';
34
71
  import { LibsUiComponentsAudioComponent, IAudioFunctionControlEvent } from '@libs-ui/components-audio';
35
72
 
36
73
  @Component({
37
- selector: 'app-example',
74
+ selector: 'app-audio-status',
38
75
  standalone: true,
39
76
  imports: [LibsUiComponentsAudioComponent],
40
77
  template: `
41
78
  <libs_ui-components-audio
42
79
  [fileAudio]="audioUrl"
43
- [checkPermissionDownloadAudio]="checkPermission"
44
- (outFunctionsControl)="onFunctionsControl($event)"
45
- (outPlay)="onPlayChange($event)"
46
- (outVolumeControl)="onVolumeChange($event)"
47
- (outTimeUpdate)="onTimeUpdate($event)"
48
- (outMute)="onMuteChange($event)"
49
- (outEnded)="onEnded()"></libs_ui-components-audio>
80
+ [checkPermissionDownloadAudio]="checkDownloadPermission"
81
+ (outPlay)="handlerPlay($event)"
82
+ (outMute)="handlerMute($event)"
83
+ (outVolumeControl)="handlerVolume($event)"
84
+ (outTimeUpdate)="handlerTimeUpdate($event)"
85
+ (outEnded)="handlerEnded()"
86
+ />
87
+
88
+ <div class="mt-4 text-sm text-gray-600">
89
+ <p>Đang phát: {{ isPlaying() }}</p>
90
+ <p>Tắt tiếng: {{ isMuted() }}</p>
91
+ <p>Âm lượng: {{ volume() }}%</p>
92
+ <p>Thời gian: {{ currentTime() }} / {{ duration() }}</p>
93
+ </div>
50
94
  `,
51
95
  })
52
- export class ExampleComponent {
53
- audioUrl = 'https://example.com/audio.mp3';
54
- controls: IAudioFunctionControlEvent | null = null;
96
+ export class AudioStatusComponent {
97
+ audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';
55
98
 
56
- checkPermission = (): Promise<boolean> => Promise.resolve(true);
99
+ isPlaying = signal(false);
100
+ isMuted = signal(false);
101
+ volume = signal(100);
102
+ currentTime = signal('00:00:00');
103
+ duration = signal('00:00:00');
57
104
 
58
- onFunctionsControl(event: IAudioFunctionControlEvent) {
59
- this.controls = event;
60
- }
105
+ checkDownloadPermission = (): Promise<boolean> => Promise.resolve(true);
61
106
 
62
- onPlayChange(isPlaying: boolean) {
63
- console.log('Đang phát:', isPlaying);
107
+ handlerPlay(isPlaying: boolean): void {
108
+ this.isPlaying.set(isPlaying);
64
109
  }
65
110
 
66
- onVolumeChange(volume: number) {
67
- console.log('Âm lượng:', volume);
111
+ handlerMute(isMuted: boolean): void {
112
+ this.isMuted.set(isMuted);
68
113
  }
69
114
 
70
- onTimeUpdate(time: { currentTime: string; duration: string }) {
71
- console.log('Thời gian:', time.currentTime, '/', time.duration);
115
+ handlerVolume(vol: number): void {
116
+ this.volume.set(vol);
72
117
  }
73
118
 
74
- onMuteChange(isMuted: boolean) {
75
- console.log('Tắt tiếng:', isMuted);
119
+ handlerTimeUpdate(time: { currentTime: string; duration: string }): void {
120
+ this.currentTime.set(time.currentTime);
121
+ this.duration.set(time.duration);
76
122
  }
77
123
 
78
- onEnded() {
79
- console.log('Audio đã phát xong');
124
+ handlerEnded(): void {
125
+ this.isPlaying.set(false);
80
126
  }
81
127
  }
82
128
  ```
83
129
 
84
- ### Điều khiển từ bên ngoài
85
-
86
- Sử dụng `outFunctionsControl` để lấy reference các hàm điều khiển:
130
+ ### Ví dụ 3 — Điều khiển audio từ bên ngoài (External Control API)
87
131
 
88
132
  ```typescript
89
- // Phát/Tạm dừng
90
- this.controls.playPause();
133
+ import { Component, signal } from '@angular/core';
134
+ import { LibsUiComponentsAudioComponent, IAudioFunctionControlEvent } from '@libs-ui/components-audio';
91
135
 
92
- // Bật/Tắt tiếng
93
- this.controls.toggleMute();
136
+ @Component({
137
+ selector: 'app-audio-external',
138
+ standalone: true,
139
+ imports: [LibsUiComponentsAudioComponent],
140
+ template: `
141
+ <libs_ui-components-audio
142
+ [fileAudio]="audioUrl"
143
+ [checkPermissionDownloadAudio]="checkDownloadPermission"
144
+ (outFunctionsControl)="handlerFunctionsControl($event)"
145
+ (outPlay)="isPlaying.set($event)"
146
+ (outMute)="isMuted.set($event)"
147
+ />
148
+
149
+ <div class="flex gap-2 mt-4">
150
+ <button class="px-4 py-2 bg-blue-500 text-white rounded" (click)="handlerTogglePlay()">
151
+ {{ isPlaying() ? 'Pause' : 'Play' }}
152
+ </button>
153
+ <button class="px-4 py-2 bg-gray-500 text-white rounded" (click)="handlerToggleMute()">
154
+ {{ isMuted() ? 'Unmute' : 'Mute' }}
155
+ </button>
156
+ <button class="px-4 py-2 bg-green-500 text-white rounded" (click)="handlerSetVolume50()">
157
+ Volume 50%
158
+ </button>
159
+ <button class="px-4 py-2 bg-orange-500 text-white rounded" (click)="handlerSeekMidpoint()">
160
+ Seek 50%
161
+ </button>
162
+ </div>
163
+ `,
164
+ })
165
+ export class AudioExternalControlComponent {
166
+ audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';
167
+
168
+ audioControls = signal<IAudioFunctionControlEvent | null>(null);
169
+ isPlaying = signal(false);
170
+ isMuted = signal(false);
171
+
172
+ checkDownloadPermission = (): Promise<boolean> => Promise.resolve(true);
173
+
174
+ handlerFunctionsControl(controls: IAudioFunctionControlEvent): void {
175
+ this.audioControls.set(controls);
176
+ }
94
177
 
95
- // Set âm lượng (0-100)
96
- this.controls.setVolume(50);
178
+ handlerTogglePlay(): void {
179
+ this.audioControls()?.playPause();
180
+ }
97
181
 
98
- // Tua đến vị trí (0-100%)
99
- this.controls.seekTo(25);
182
+ handlerToggleMute(): void {
183
+ this.audioControls()?.toggleMute();
184
+ }
100
185
 
101
- // Download audio
102
- this.controls.download();
186
+ handlerSetVolume50(): void {
187
+ this.audioControls()?.setVolume(50);
188
+ }
103
189
 
104
- // Kiểm tra trạng thái
105
- const isPlaying = this.controls.isPlaying();
106
- const isMuted = this.controls.isMuted();
190
+ handlerSeekMidpoint(): void {
191
+ this.audioControls()?.seekTo(50);
192
+ }
193
+ }
107
194
  ```
108
195
 
109
- ## API Reference
196
+ ### dụ 4 — Kiểm soát quyền download theo nghiệp vụ
110
197
 
111
- ### Inputs
198
+ ```typescript
199
+ import { Component, inject } from '@angular/core';
200
+ import { LibsUiComponentsAudioComponent } from '@libs-ui/components-audio';
112
201
 
113
- | Tên | Kiểu | Mặc định | Mô tả |
114
- | ------------------------------ | ------------------------ | ---------- | --------------------------------------------------------------------- |
115
- | `fileAudio` | `string` | _required_ | URL của file audio cần phát. Hỗ trợ MP3, WAV, OGG. |
116
- | `checkPermissionDownloadAudio` | `() => Promise<boolean>` | _required_ | Callback kiểm tra quyền download. Trả về Promise với kết quả boolean. |
202
+ @Component({
203
+ selector: 'app-audio-permission',
204
+ standalone: true,
205
+ imports: [LibsUiComponentsAudioComponent],
206
+ template: `
207
+ <libs_ui-components-audio
208
+ [fileAudio]="audioUrl"
209
+ [checkPermissionDownloadAudio]="checkDownloadPermission"
210
+ />
211
+ `,
212
+ })
213
+ export class AudioPermissionComponent {
214
+ audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';
215
+
216
+ /**
217
+ * checkPermissionDownloadAudio nhận một factory function (() => Promise<boolean>),
218
+ * không phải Promise<boolean> trực tiếp. Component sẽ gọi hàm này mỗi lần user nhấn download.
219
+ */
220
+ checkDownloadPermission = (): Promise<boolean> => {
221
+ // Ví dụ: kiểm tra role của user trước khi cho phép tải
222
+ const userRole = 'admin'; // lấy từ auth service thực tế
223
+ return Promise.resolve(userRole === 'admin');
224
+ };
225
+ }
226
+ ```
227
+
228
+ ## @Input()
229
+
230
+ | Input | Type | Default | Mô tả | Ví dụ |
231
+ |---|---|---|---|---|
232
+ | `fileAudio` | `string` | **required** | URL của file audio cần phát. Hỗ trợ MP3, WAV, OGG. Khi giá trị thay đổi, audio sẽ tự động reload. | `[fileAudio]="'https://example.com/audio.mp3'"` |
233
+ | `checkPermissionDownloadAudio` | `() => Promise<boolean>` | **required** | Factory function kiểm tra quyền download. Được gọi mỗi lần user nhấn nút tải. Trả về `true` để cho phép, `false` để chặn. | `[checkPermissionDownloadAudio]="checkPermission"` |
117
234
 
118
- ### Outputs
235
+ ## @Output()
119
236
 
120
- | Tên | Kiểu | Mô tả |
121
- | --------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
122
- | `outFunctionsControl` | `IAudioFunctionControlEvent` | Emit object chứa các hàm điều khiển: playPause, toggleMute, seekTo, setVolume, download, isPlaying, isMuted. |
123
- | `outVolumeControl` | `number` | Emit giá trị âm lượng hiện tại từ 0-100. |
124
- | `outTimeUpdate` | `{ currentTime: string, duration: string }` | Emit thông tin thời gian theo định dạng HH:MM:SS. |
125
- | `outEnded` | `void` | Emit khi audio phát xong. |
126
- | `outMute` | `boolean` | Emit trạng thái tắt/bật tiếng. |
127
- | `outPlay` | `boolean` | Emit trạng thái đang phát/tạm dừng. |
237
+ | Output | Type | Mô tả | Handler TS | Binding HTML |
238
+ |---|---|---|---|---|
239
+ | `(outFunctionsControl)` | `IAudioFunctionControlEvent` | Emit ngay sau `ngAfterViewInit` cung cấp API điều khiển audio từ bên ngoài. | `handlerFunctionsControl(e: IAudioFunctionControlEvent): void { e; /* không stopPropagation vì đây là output signal */ this.controls.set(e); }` | `(outFunctionsControl)="handlerFunctionsControl($event)"` |
240
+ | `(outPlay)` | `boolean` | Emit mỗi khi trạng thái play/pause thay đổi. `true` = đang phát, `false` = đang dừng. | `handlerPlay(e: boolean): void { this.isPlaying.set(e); }` | `(outPlay)="handlerPlay($event)"` |
241
+ | `(outMute)` | `boolean` | Emit mỗi khi trạng thái mute thay đổi. `true` = đang tắt tiếng. | `handlerMute(e: boolean): void { this.isMuted.set(e); }` | `(outMute)="handlerMute($event)"` |
242
+ | `(outVolumeControl)` | `number` | Emit giá trị âm lượng hiện tại từ 0 đến 100, mỗi khi volume slider thay đổi. | `handlerVolume(e: number): void { this.volume.set(e); }` | `(outVolumeControl)="handlerVolume($event)"` |
243
+ | `(outTimeUpdate)` | `{ currentTime: string; duration: string }` | Emit theo từng tick cập nhật thời gian. Định dạng: `'HH:MM:SS'`. | `handlerTimeUpdate(e: { currentTime: string; duration: string }): void { this.currentTime.set(e.currentTime); this.duration.set(e.duration); }` | `(outTimeUpdate)="handlerTimeUpdate($event)"` |
244
+ | `(outEnded)` | `void` | Emit khi audio phát hết đến cuối file. | `handlerEnded(): void { this.isPlaying.set(false); }` | `(outEnded)="handlerEnded()"` |
128
245
 
129
- ### Interface: IAudioFunctionControlEvent
246
+ ## Types & Interfaces
247
+
248
+ ```typescript
249
+ import { IAudioFunctionControlEvent } from '@libs-ui/components-audio';
250
+ ```
130
251
 
131
252
  ```typescript
132
253
  interface IAudioFunctionControlEvent {
133
- /**
134
- * Bắt đầu hoặc tạm dừng phát audio
135
- */
254
+ /** Bắt đầu hoặc tạm dừng phát audio */
136
255
  playPause: (event?: Event) => void;
137
256
 
138
- /**
139
- * Bật hoặc tắt âm thanh
140
- */
257
+ /** Bật hoặc tắt âm thanh */
141
258
  toggleMute: (event?: Event) => void;
142
259
 
143
260
  /**
144
- * Điều chỉnh âm lượng (0-100)
261
+ * Điều chỉnh âm lượng
262
+ * @param value Giá trị từ 0 đến 100
145
263
  */
146
264
  setVolume: (value: number) => void;
147
265
 
148
266
  /**
149
- * Di chuyển đến vị trí cụ thể (0-100%)
267
+ * Di chuyển đến vị trí cụ thể trong audio
268
+ * @param value Giá trị phần trăm từ 0 đến 100
150
269
  */
151
270
  seekTo: (value: number) => void;
152
271
 
153
- /**
154
- * Tải xuống file audio
155
- */
272
+ /** Tải xuống file audio (chỉ thực hiện nếu checkPermissionDownloadAudio trả về true) */
156
273
  download: (event?: Event) => void;
157
274
 
158
- /**
159
- * Kiểm tra trạng thái đang phát
160
- */
275
+ /** Trả về true nếu audio đang phát */
161
276
  isPlaying: () => boolean;
162
277
 
163
- /**
164
- * Kiểm tra trạng thái tắt tiếng
165
- */
278
+ /** Trả về true nếu audio đang tắt tiếng */
166
279
  isMuted: () => boolean;
167
280
  }
168
281
  ```
169
282
 
170
- ## Công nghệ sử dụng
283
+ ### Cách sử dụng IAudioFunctionControlEvent
171
284
 
172
- - **Angular 18+** - Standalone Components
173
- - **Angular Signals** - Reactive state management
174
- - **RxJS** - Event handling với `takeUntilDestroyed`
175
- - **TailwindCSS** - Styling
285
+ ```typescript
286
+ // Nhận controls qua outFunctionsControl
287
+ private audioControls = signal<IAudioFunctionControlEvent | null>(null);
176
288
 
177
- ## Demo
289
+ handlerFunctionsControl(controls: IAudioFunctionControlEvent): void {
290
+ this.audioControls.set(controls);
291
+ }
292
+
293
+ // Sau đó gọi từ bất kỳ đâu trong component cha
294
+ handlerPlayPause(): void {
295
+ this.audioControls()?.playPause();
296
+ }
297
+
298
+ handlerSetVolumeTo30(): void {
299
+ this.audioControls()?.setVolume(30);
300
+ }
301
+
302
+ handlerSeekToBeginning(): void {
303
+ this.audioControls()?.seekTo(0);
304
+ }
305
+
306
+ handlerCheckState(): void {
307
+ const playing = this.audioControls()?.isPlaying(); // boolean
308
+ const muted = this.audioControls()?.isMuted(); // boolean
309
+ }
310
+ ```
311
+
312
+ ## Lưu ý quan trọng
313
+
314
+ ⚠️ **checkPermissionDownloadAudio là factory function, không phải Promise**: Input này nhận `() => Promise<boolean>`, không phải `Promise<boolean>` trực tiếp. Component gọi hàm này khi user nhấn download, không gọi trước. Đảm bảo truyền vào một hàm (arrow function hoặc method reference), không truyền kết quả gọi hàm.
178
315
 
179
- Demo có sẵn trong ứng dụng `core-ui`:
316
+ ```typescript
317
+ // ❌ SAI — truyền Promise trực tiếp
318
+ [checkPermissionDownloadAudio]="checkPermission()"
319
+
320
+ // ✅ ĐÚNG — truyền function reference
321
+ [checkPermissionDownloadAudio]="checkPermission"
322
+
323
+ // ✅ ĐÚNG — truyền inline arrow function
324
+ [checkPermissionDownloadAudio]="() => permissionService.canDownloadAudio()"
325
+ ```
326
+
327
+ ⚠️ **outFunctionsControl emit một lần sau ngAfterViewInit**: Output này chỉ emit một lần khi component khởi tạo xong. Lưu lại giá trị vào signal hoặc biến class để dùng về sau. Không cần subscribe lại khi `fileAudio` thay đổi — cùng một controls object vẫn hoạt động với file mới.
328
+
329
+ ⚠️ **Định dạng thời gian outTimeUpdate luôn là HH:MM:SS**: Ngay cả khi duration dưới 1 giờ, output vẫn là `'00:03:45'` (không phải `'3:45'`). Lưu ý điều này khi so sánh hoặc hiển thị thời gian.
330
+
331
+ ⚠️ **Audio không tự phát khi fileAudio thay đổi**: Khi `fileAudio` input thay đổi, component reload audio nhưng không tự động phát. Gọi `controls.playPause()` sau khi cần thiết.
332
+
333
+ ## Demo
180
334
 
181
335
  ```bash
182
336
  npx nx serve core-ui
183
337
  ```
184
338
 
185
- **File demo:** `apps/core-ui/src/app/components/audio/audio.component.ts`
186
-
187
339
  Truy cập: `http://localhost:4500/audio`
188
340
 
189
- ### Tính năng Demo
341
+ File demo: `apps/core-ui/src/app/components/audio/audio.component.ts`
190
342
 
191
- - 🎮 **Basic Example**: Cách sử dụng cơ bản
192
- - 🎛️ **External Controls**: Điều khiển audio từ component cha
193
- - 📊 **Real-time Status**: Hiển thị trạng thái play, mute, time, volume
343
+ Các dụ trong demo:
344
+ - Basic Usage cách dùng tối thiểu
345
+ - Status Tracking theo dõi trạng thái play, mute, volume, time
346
+ - External Control — điều khiển audio hoàn toàn từ bên ngoài component
194
347
 
195
348
  ## Unit Tests
196
349
 
@@ -198,13 +351,9 @@ Truy cập: `http://localhost:4500/audio`
198
351
  # Chạy tests
199
352
  npx nx test components-audio
200
353
 
201
- # Chạy tests với coverage
354
+ # Chạy với coverage
202
355
  npx nx test components-audio --coverage
203
356
 
204
- # Watch mode
205
- npx nx test components-audio --watch
357
+ # Chạy một file spec cụ thể
358
+ npx nx test components-audio --testFile=libs-ui/components/audio/src/audio.component.spec.ts
206
359
  ```
207
-
208
- ## License
209
-
210
- MIT
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, input, viewChild, output, inject, DestroyRef, effect, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { signal, input, viewChild, output, inject, DestroyRef, effect, Component, ChangeDetectionStrategy } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import { LibsUiComponentsInputsRangeSliderComponent } from '@libs-ui/components-inputs-range-slider';
5
5
  import { merge, tap, fromEvent } from 'rxjs';
@@ -1 +1 @@
1
- {"version":3,"file":"libs-ui-components-audio.mjs","sources":["../../../../../libs-ui/components/audio/src/audio.component.ts","../../../../../libs-ui/components/audio/src/audio.component.html","../../../../../libs-ui/components/audio/src/libs-ui-components-audio.ts"],"sourcesContent":["import { AfterViewInit, ChangeDetectionStrategy, Component, DestroyRef, effect, ElementRef, inject, input, output, signal, viewChild } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { LibsUiComponentsInputsRangeSliderComponent } from '@libs-ui/components-inputs-range-slider';\nimport { fromEvent, merge, Observable, tap } from 'rxjs';\nimport { IAudioFunctionControlEvent } from './interfaces/function-control-event.interface';\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: 'libs_ui-components-audio',\n templateUrl: './audio.component.html',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LibsUiComponentsInputsRangeSliderComponent],\n})\nexport class LibsUiComponentsAudioComponent implements AfterViewInit {\n // =========================================\n // INTERNAL SIGNALS\n // =========================================\n\n protected audioRatioValue = signal<number>(0);\n\n protected volumeRatioValue = signal<number>(100);\n\n protected isPlay = signal<boolean>(false);\n\n protected isMute = signal<boolean>(false);\n\n protected isSliderAudioPress = signal<boolean>(false);\n\n protected isDisable = signal<boolean>(true);\n\n protected audioTimeCurrent = signal<string>('_:_:_');\n\n protected audioTimeDuration = signal<string>('_:_:_');\n\n protected showFullControlVolume = signal<boolean>(false);\n\n // =========================================\n // INPUTS\n // =========================================\n\n readonly fileAudio = input.required<string>();\n\n readonly checkPermissionDownloadAudio = input.required<() => Promise<boolean>>();\n\n // =========================================\n // VIEW CHILDREN\n // =========================================\n\n readonly audioRef = viewChild.required<ElementRef>('audioRef');\n\n readonly volumeControlRef = viewChild.required<ElementRef>('volumeControlRef');\n\n // =========================================\n // OUTPUTS\n // =========================================\n\n readonly outFunctionsControl = output<IAudioFunctionControlEvent>();\n\n readonly outVolumeControl = output<number>();\n\n readonly outTimeUpdate = output<{ currentTime: string; duration: string }>();\n\n readonly outEnded = output<void>();\n\n readonly outMute = output<boolean>();\n\n readonly outPlay = output<boolean>();\n\n // =========================================\n // PRIVATE PROPERTIES\n // =========================================\n\n private readonly destroyRef = inject(DestroyRef);\n\n // =========================================\n // CONSTRUCTOR\n // =========================================\n\n constructor() {\n // Watch for file audio changes\n effect(() => {\n if (this.fileAudio() && this.audioRef()) {\n setTimeout(() => {\n this.audioRef().nativeElement.load();\n }, 0);\n }\n });\n\n effect(() => {\n this.outVolumeControl.emit(this.volumeRatioValue());\n });\n\n effect(() => {\n this.outTimeUpdate.emit({ currentTime: this.audioTimeCurrent(), duration: this.audioTimeDuration() });\n });\n\n effect(() => {\n this.outMute.emit(this.isMute());\n });\n\n effect(() => {\n this.outPlay.emit(this.isPlay());\n });\n }\n\n // =========================================\n // LIFECYCLE HOOKS\n // =========================================\n\n ngAfterViewInit(): void {\n merge(\n this.initObservable(this.volumeControlRef().nativeElement, 'mouseenter').pipe(tap(() => this.showFullControlVolume.set(true))),\n this.initObservable(this.volumeControlRef().nativeElement, 'mouseleave').pipe(tap(() => this.showFullControlVolume.set(false)))\n )\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe();\n\n // Emit function control event after view is initialized\n this.outFunctionsControl.emit(this.FunctionsControl);\n }\n\n // =========================================\n // PUBLIC API\n // =========================================\n\n public get FunctionsControl(): IAudioFunctionControlEvent {\n return {\n playPause: (event?: Event) => void this.handlerAudioPausePlay(event),\n toggleMute: (event?: Event) => void this.handlerAudioMuteMuted(event),\n seekTo: this.handlerChangeAudio.bind(this),\n setVolume: this.handlerChangeVolume.bind(this),\n download: (event?: Event) => void this.handlerDownload(event),\n isPlaying: () => this.isPlay(),\n isMuted: () => this.isMute(),\n };\n }\n\n // =========================================\n // PRIVATE METHODS\n // =========================================\n\n private initObservable(el: HTMLElement, eventName: string): Observable<MouseEvent> {\n return fromEvent<MouseEvent>(el, eventName).pipe(\n tap((e) => e.stopPropagation()),\n takeUntilDestroyed(this.destroyRef)\n );\n }\n\n protected async handlerKeyPressAudio(): Promise<void> {\n this.isSliderAudioPress.set(true);\n }\n\n protected async handlerAudioMuteMuted(event?: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n if (this.audioRef().nativeElement.muted === true) {\n this.audioRef().nativeElement.muted = false;\n this.isMute.set(false);\n this.volumeRatioValue.set(50);\n return;\n }\n\n this.volumeRatioValue.set(0);\n this.isMute.set(true);\n this.audioRef().nativeElement.muted = true;\n }\n\n protected async handlerAudioPausePlay(event?: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n const audioElement = this.audioRef().nativeElement;\n if (!audioElement.paused) {\n audioElement.pause();\n this.isPlay.set(false);\n return;\n }\n\n try {\n await audioElement.play();\n this.isPlay.set(true);\n } catch (error) {\n console.error('Error playing audio:', error);\n }\n }\n\n protected async handlerLoadedData(event: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n if (this.audioRef().nativeElement) {\n this.audioTimeDuration.set(await this.toHHMMSS(Math.floor(this.audioRef().nativeElement.duration)));\n this.audioTimeCurrent.set(await this.toHHMMSS(Math.floor(this.audioRef().nativeElement.currentTime || 0)));\n this.isDisable.set(false);\n this.isPlay.set(false);\n this.audioRatioValue.set(0);\n this.audioRef().nativeElement.pause();\n }\n }\n\n protected async handlerTimeUpdate(event?: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n this.isDisable.set(!(this.audioRef().nativeElement.duration || 0));\n\n if (!this.audioRef().nativeElement) {\n return;\n }\n\n this.audioTimeDuration.set(await this.toHHMMSS(Math.floor(this.audioRef().nativeElement.duration)));\n this.audioTimeCurrent.set(await this.toHHMMSS(Math.floor(this.audioRef().nativeElement.currentTime || 0)));\n\n if (this.isSliderAudioPress()) {\n this.audioRef().nativeElement.currentTime = (this.audioRatioValue() * Math.floor(this.audioRef().nativeElement.duration || 0)) / 100;\n return;\n }\n\n this.audioRatioValue.set(Math.floor(((this.audioRef().nativeElement.currentTime || 0) / (this.audioRef().nativeElement.duration || 1)) * 100));\n }\n\n /**\n * Format seconds -> HH:MM:SS.\n * Used for both `currentTime` and `duration` outputs to keep output stable.\n */\n private async toHHMMSS(time: number): Promise<string> {\n const hours = Math.floor(time / 3600);\n const minutes = Math.floor((time - hours * 3600) / 60);\n const seconds = time - hours * 3600 - minutes * 60;\n\n const getLabel = (val: number) => {\n val = val || 0;\n return `${val < 10 ? '0' : ''}${val}`;\n };\n\n return `${getLabel(hours)}:${getLabel(minutes)}:${getLabel(seconds)}`;\n }\n\n protected async handlerChangeAudio(value: number): Promise<void> {\n if (value === this.audioRatioValue()) {\n return;\n }\n\n this.audioRef().nativeElement.currentTime = ((value || 0) * (this.audioRef().nativeElement.duration || 0)) / 100;\n this.audioRatioValue.set(value);\n this.isSliderAudioPress.set(false);\n }\n\n protected async handlerChangeVolume(value: number): Promise<void> {\n this.audioRef().nativeElement.volume = value / 100;\n this.volumeRatioValue.set(value);\n\n if (this.audioRef().nativeElement.volume) {\n this.audioRef().nativeElement.muted = false;\n this.isMute.set(false);\n return;\n }\n\n this.audioRef().nativeElement.muted = true;\n this.isMute.set(true);\n }\n\n protected async handlerEnded(event: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n this.isPlay.set(false);\n this.outEnded.emit();\n }\n\n /**\n * Download chỉ được thực hiện nếu callback permission trả về `true`.\n * Lưu ý: `checkPermissionDownloadAudio` là input function (factory), nên cần gọi 2 lần:\n * - lần 1: lấy function\n * - lần 2: execute function để lấy Promise<boolean>\n */\n protected async handlerDownload(e?: Event): Promise<void> {\n if (!this.checkPermissionDownloadAudio() || !(await this.checkPermissionDownloadAudio()())) {\n return;\n }\n\n if (e) {\n e.stopPropagation();\n }\n\n if (!this.fileAudio()) {\n return;\n }\n\n window.open(this.fileAudio(), '_blank');\n }\n}\n","<audio\n controls\n #audioRef\n class=\"hidden\"\n (timeupdate)=\"handlerTimeUpdate($event)\"\n (loadeddata)=\"handlerLoadedData($event)\"\n (ended)=\"handlerEnded($event)\">\n <source\n [src]=\"fileAudio()\"\n type=\"audio/mpeg\" />\n</audio>\n<div\n [class.libs-ui-disable]=\"isDisable()\"\n [class.pointer-events-none]=\"isDisable()\">\n <div class=\"flex justify-between items-center\">\n <div class=\"w-[70%] flex p-0 items-center\">\n <div\n class=\"flex mr-[16px] cursor-pointer\"\n tabindex=\"0\"\n (click)=\"handlerAudioPausePlay($event)\"\n (keydown.enter)=\"handlerAudioPausePlay($event)\"\n (keydown.space)=\"handlerAudioPausePlay($event)\">\n <i\n class=\"text-[16px]\"\n [class.libs-ui-icon-play-solid]=\"!isPlay()\"\n [class.libs-ui-icon-pause-solid]=\"isPlay()\"></i>\n </div>\n <div class=\"libs-ui-font-h5r mr-[16px]\">{{ audioTimeCurrent() }} /{{ audioTimeDuration() }}</div>\n </div>\n <div class=\"w-[30%] flex p-0 items-center justify-end\">\n <div\n #volumeControlRef\n class=\"flex py-[3px] items-center rounded-[12px] h-[28px]\"\n [class.bg-[#e6e7ea]]=\"showFullControlVolume()\"\n [class.px-[12px]]=\"showFullControlVolume()\">\n <i\n class=\"text-[16px] cursor-pointer\"\n tabindex=\"0\"\n [class.libs-ui-icon-speaker-on-solid]=\"!isMute()\"\n [class.libs-ui-icon-speaker-off-solid]=\"isMute()\"\n (click)=\"handlerAudioMuteMuted($event)\"\n (keydown.enter)=\"handlerAudioMuteMuted($event)\"\n (keydown.space)=\"handlerAudioMuteMuted($event)\"></i>\n <libs_ui-components-inputs-range_slider\n [class.hidden]=\"!showFullControlVolume()\"\n [mode]=\"'audio'\"\n classInclude=\"flex items-center !w-[54px] cursor-pointer ml-[8px]\"\n [value]=\"volumeRatioValue()\"\n (outChange)=\"handlerChangeVolume($event)\" />\n </div>\n\n <i\n class=\"libs-ui-icon-download-solid ml-[16px] cursor-pointer\"\n tabindex=\"0\"\n (click)=\"handlerDownload($event)\"\n (keydown.enter)=\"handlerDownload($event)\"\n (keydown.space)=\"handlerDownload($event)\"></i>\n </div>\n </div>\n <div class=\"h-[24px]\">\n <libs_ui-components-inputs-range_slider\n [mode]=\"'audio'\"\n [value]=\"audioRatioValue()\"\n [disable]=\"isDisable()\"\n (outChange)=\"handlerChangeAudio($event)\" />\n </div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"libs-ui-components-audio.mjs","sources":["../../../../../libs-ui/components/audio/src/audio.component.ts","../../../../../libs-ui/components/audio/src/audio.component.html","../../../../../libs-ui/components/audio/src/libs-ui-components-audio.ts"],"sourcesContent":["import { AfterViewInit, ChangeDetectionStrategy, Component, DestroyRef, effect, ElementRef, inject, input, output, signal, viewChild } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { LibsUiComponentsInputsRangeSliderComponent } from '@libs-ui/components-inputs-range-slider';\nimport { fromEvent, merge, Observable, tap } from 'rxjs';\nimport { IAudioFunctionControlEvent } from './interfaces/function-control-event.interface';\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: 'libs_ui-components-audio',\n templateUrl: './audio.component.html',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LibsUiComponentsInputsRangeSliderComponent],\n})\nexport class LibsUiComponentsAudioComponent implements AfterViewInit {\n // =========================================\n // INTERNAL SIGNALS\n // =========================================\n\n protected audioRatioValue = signal<number>(0);\n\n protected volumeRatioValue = signal<number>(100);\n\n protected isPlay = signal<boolean>(false);\n\n protected isMute = signal<boolean>(false);\n\n protected isSliderAudioPress = signal<boolean>(false);\n\n protected isDisable = signal<boolean>(true);\n\n protected audioTimeCurrent = signal<string>('_:_:_');\n\n protected audioTimeDuration = signal<string>('_:_:_');\n\n protected showFullControlVolume = signal<boolean>(false);\n\n // =========================================\n // INPUTS\n // =========================================\n\n readonly fileAudio = input.required<string>();\n\n readonly checkPermissionDownloadAudio = input.required<() => Promise<boolean>>();\n\n // =========================================\n // VIEW CHILDREN\n // =========================================\n\n readonly audioRef = viewChild.required<ElementRef>('audioRef');\n\n readonly volumeControlRef = viewChild.required<ElementRef>('volumeControlRef');\n\n // =========================================\n // OUTPUTS\n // =========================================\n\n readonly outFunctionsControl = output<IAudioFunctionControlEvent>();\n\n readonly outVolumeControl = output<number>();\n\n readonly outTimeUpdate = output<{ currentTime: string; duration: string }>();\n\n readonly outEnded = output<void>();\n\n readonly outMute = output<boolean>();\n\n readonly outPlay = output<boolean>();\n\n // =========================================\n // PRIVATE PROPERTIES\n // =========================================\n\n private readonly destroyRef = inject(DestroyRef);\n\n // =========================================\n // CONSTRUCTOR\n // =========================================\n\n constructor() {\n // Watch for file audio changes\n effect(() => {\n if (this.fileAudio() && this.audioRef()) {\n setTimeout(() => {\n this.audioRef().nativeElement.load();\n }, 0);\n }\n });\n\n effect(() => {\n this.outVolumeControl.emit(this.volumeRatioValue());\n });\n\n effect(() => {\n this.outTimeUpdate.emit({ currentTime: this.audioTimeCurrent(), duration: this.audioTimeDuration() });\n });\n\n effect(() => {\n this.outMute.emit(this.isMute());\n });\n\n effect(() => {\n this.outPlay.emit(this.isPlay());\n });\n }\n\n // =========================================\n // LIFECYCLE HOOKS\n // =========================================\n\n ngAfterViewInit(): void {\n merge(\n this.initObservable(this.volumeControlRef().nativeElement, 'mouseenter').pipe(tap(() => this.showFullControlVolume.set(true))),\n this.initObservable(this.volumeControlRef().nativeElement, 'mouseleave').pipe(tap(() => this.showFullControlVolume.set(false)))\n )\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe();\n\n // Emit function control event after view is initialized\n this.outFunctionsControl.emit(this.FunctionsControl);\n }\n\n // =========================================\n // PUBLIC API\n // =========================================\n\n public get FunctionsControl(): IAudioFunctionControlEvent {\n return {\n playPause: (event?: Event) => void this.handlerAudioPausePlay(event),\n toggleMute: (event?: Event) => void this.handlerAudioMuteMuted(event),\n seekTo: this.handlerChangeAudio.bind(this),\n setVolume: this.handlerChangeVolume.bind(this),\n download: (event?: Event) => void this.handlerDownload(event),\n isPlaying: () => this.isPlay(),\n isMuted: () => this.isMute(),\n };\n }\n\n // =========================================\n // PRIVATE METHODS\n // =========================================\n\n private initObservable(el: HTMLElement, eventName: string): Observable<MouseEvent> {\n return fromEvent<MouseEvent>(el, eventName).pipe(\n tap((e) => e.stopPropagation()),\n takeUntilDestroyed(this.destroyRef)\n );\n }\n\n protected async handlerKeyPressAudio(): Promise<void> {\n this.isSliderAudioPress.set(true);\n }\n\n protected async handlerAudioMuteMuted(event?: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n if (this.audioRef().nativeElement.muted === true) {\n this.audioRef().nativeElement.muted = false;\n this.isMute.set(false);\n this.volumeRatioValue.set(50);\n return;\n }\n\n this.volumeRatioValue.set(0);\n this.isMute.set(true);\n this.audioRef().nativeElement.muted = true;\n }\n\n protected async handlerAudioPausePlay(event?: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n const audioElement = this.audioRef().nativeElement;\n if (!audioElement.paused) {\n audioElement.pause();\n this.isPlay.set(false);\n return;\n }\n\n try {\n await audioElement.play();\n this.isPlay.set(true);\n } catch (error) {\n console.error('Error playing audio:', error);\n }\n }\n\n protected async handlerLoadedData(event: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n if (this.audioRef().nativeElement) {\n this.audioTimeDuration.set(await this.toHHMMSS(Math.floor(this.audioRef().nativeElement.duration)));\n this.audioTimeCurrent.set(await this.toHHMMSS(Math.floor(this.audioRef().nativeElement.currentTime || 0)));\n this.isDisable.set(false);\n this.isPlay.set(false);\n this.audioRatioValue.set(0);\n this.audioRef().nativeElement.pause();\n }\n }\n\n protected async handlerTimeUpdate(event?: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n this.isDisable.set(!(this.audioRef().nativeElement.duration || 0));\n\n if (!this.audioRef().nativeElement) {\n return;\n }\n\n this.audioTimeDuration.set(await this.toHHMMSS(Math.floor(this.audioRef().nativeElement.duration)));\n this.audioTimeCurrent.set(await this.toHHMMSS(Math.floor(this.audioRef().nativeElement.currentTime || 0)));\n\n if (this.isSliderAudioPress()) {\n this.audioRef().nativeElement.currentTime = (this.audioRatioValue() * Math.floor(this.audioRef().nativeElement.duration || 0)) / 100;\n return;\n }\n\n this.audioRatioValue.set(Math.floor(((this.audioRef().nativeElement.currentTime || 0) / (this.audioRef().nativeElement.duration || 1)) * 100));\n }\n\n /**\n * Format seconds -> HH:MM:SS.\n * Used for both `currentTime` and `duration` outputs to keep output stable.\n */\n private async toHHMMSS(time: number): Promise<string> {\n const hours = Math.floor(time / 3600);\n const minutes = Math.floor((time - hours * 3600) / 60);\n const seconds = time - hours * 3600 - minutes * 60;\n\n const getLabel = (val: number) => {\n val = val || 0;\n return `${val < 10 ? '0' : ''}${val}`;\n };\n\n return `${getLabel(hours)}:${getLabel(minutes)}:${getLabel(seconds)}`;\n }\n\n protected async handlerChangeAudio(value: number): Promise<void> {\n if (value === this.audioRatioValue()) {\n return;\n }\n\n this.audioRef().nativeElement.currentTime = ((value || 0) * (this.audioRef().nativeElement.duration || 0)) / 100;\n this.audioRatioValue.set(value);\n this.isSliderAudioPress.set(false);\n }\n\n protected async handlerChangeVolume(value: number): Promise<void> {\n this.audioRef().nativeElement.volume = value / 100;\n this.volumeRatioValue.set(value);\n\n if (this.audioRef().nativeElement.volume) {\n this.audioRef().nativeElement.muted = false;\n this.isMute.set(false);\n return;\n }\n\n this.audioRef().nativeElement.muted = true;\n this.isMute.set(true);\n }\n\n protected async handlerEnded(event: Event): Promise<void> {\n if (event) {\n event.stopPropagation();\n }\n\n this.isPlay.set(false);\n this.outEnded.emit();\n }\n\n /**\n * Download chỉ được thực hiện nếu callback permission trả về `true`.\n * Lưu ý: `checkPermissionDownloadAudio` là input function (factory), nên cần gọi 2 lần:\n * - lần 1: lấy function\n * - lần 2: execute function để lấy Promise<boolean>\n */\n protected async handlerDownload(e?: Event): Promise<void> {\n if (!this.checkPermissionDownloadAudio() || !(await this.checkPermissionDownloadAudio()())) {\n return;\n }\n\n if (e) {\n e.stopPropagation();\n }\n\n if (!this.fileAudio()) {\n return;\n }\n\n window.open(this.fileAudio(), '_blank');\n }\n}\n","<audio\n controls\n #audioRef\n class=\"hidden\"\n (timeupdate)=\"handlerTimeUpdate($event)\"\n (loadeddata)=\"handlerLoadedData($event)\"\n (ended)=\"handlerEnded($event)\">\n <source\n [src]=\"fileAudio()\"\n type=\"audio/mpeg\" />\n</audio>\n<div\n [class.libs-ui-disable]=\"isDisable()\"\n [class.pointer-events-none]=\"isDisable()\">\n <div class=\"flex justify-between items-center\">\n <div class=\"w-[70%] flex p-0 items-center\">\n <div\n class=\"flex mr-[16px] cursor-pointer\"\n tabindex=\"0\"\n (click)=\"handlerAudioPausePlay($event)\"\n (keydown.enter)=\"handlerAudioPausePlay($event)\"\n (keydown.space)=\"handlerAudioPausePlay($event)\">\n <i\n class=\"text-[16px]\"\n [class.libs-ui-icon-play-solid]=\"!isPlay()\"\n [class.libs-ui-icon-pause-solid]=\"isPlay()\"></i>\n </div>\n <div class=\"libs-ui-font-h5r mr-[16px]\">{{ audioTimeCurrent() }} /{{ audioTimeDuration() }}</div>\n </div>\n <div class=\"w-[30%] flex p-0 items-center justify-end\">\n <div\n #volumeControlRef\n class=\"flex py-[3px] items-center rounded-[12px] h-[28px]\"\n [class.bg-[#e6e7ea]]=\"showFullControlVolume()\"\n [class.px-[12px]]=\"showFullControlVolume()\">\n <i\n class=\"text-[16px] cursor-pointer\"\n tabindex=\"0\"\n [class.libs-ui-icon-speaker-on-solid]=\"!isMute()\"\n [class.libs-ui-icon-speaker-off-solid]=\"isMute()\"\n (click)=\"handlerAudioMuteMuted($event)\"\n (keydown.enter)=\"handlerAudioMuteMuted($event)\"\n (keydown.space)=\"handlerAudioMuteMuted($event)\"></i>\n <libs_ui-components-inputs-range_slider\n [class.hidden]=\"!showFullControlVolume()\"\n [mode]=\"'audio'\"\n classInclude=\"flex items-center !w-[54px] cursor-pointer ml-[8px]\"\n [value]=\"volumeRatioValue()\"\n (outChange)=\"handlerChangeVolume($event)\" />\n </div>\n\n <i\n class=\"libs-ui-icon-download-solid ml-[16px] cursor-pointer\"\n tabindex=\"0\"\n (click)=\"handlerDownload($event)\"\n (keydown.enter)=\"handlerDownload($event)\"\n (keydown.space)=\"handlerDownload($event)\"></i>\n </div>\n </div>\n <div class=\"h-[24px]\">\n <libs_ui-components-inputs-range_slider\n [mode]=\"'audio'\"\n [value]=\"audioRatioValue()\"\n [disable]=\"isDisable()\"\n (outChange)=\"handlerChangeAudio($event)\" />\n </div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@libs-ui/components-audio",
3
- "version": "0.2.356-41",
3
+ "version": "0.2.356-43",
4
4
  "peerDependencies": {
5
5
  "@angular/core": ">=18.0.0",
6
- "@libs-ui/components-inputs-range-slider": "0.2.356-41",
6
+ "@libs-ui/components-inputs-range-slider": "0.2.356-43",
7
7
  "rxjs": "~7.8.0"
8
8
  },
9
9
  "sideEffects": false,