tuikit-atomicx-vue3 3.3.1 → 3.3.2-beta.1

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.
Files changed (44) hide show
  1. package/dist/components/ChatSetting/GroupChatSetting/GroupActions/GroupActions.js +1 -4
  2. package/dist/components/ChatSetting/GroupChatSetting/GroupChatSetting.js +1 -2
  3. package/dist/components/ChatSetting/GroupChatSetting/GroupManagement/GroupManagement.js +1 -2
  4. package/dist/components/ContactList/ContactInfo/GroupInfo/GroupInfo.js +1 -2
  5. package/dist/components/ConversationList/ConversationCreate/ConversationCreate.js +1 -2
  6. package/dist/components/ConversationList/ConversationSearch/ConversationSearch.js +0 -1
  7. package/dist/components/LiveCoreView/PlayerControl/AudioControl.js +251 -0
  8. package/dist/components/LiveCoreView/PlayerControl/AudioControl.vue.d.ts +38 -0
  9. package/dist/components/LiveCoreView/PlayerControl/PlayerControl.js +271 -0
  10. package/dist/components/LiveCoreView/PlayerControl/PlayerControl.vue.d.ts +2 -0
  11. package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.d.ts +27 -0
  12. package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.js +407 -0
  13. package/dist/components/LiveCoreView/PlayerControl/index.d.ts +3 -0
  14. package/dist/components/LiveCoreView/PlayerControl/index.js +8 -0
  15. package/dist/components/LiveCoreView/PlayerControl/utils/deviceDetection.d.ts +85 -0
  16. package/dist/components/LiveCoreView/PlayerControl/utils/deviceDetection.js +129 -0
  17. package/dist/components/LiveCoreView/PlayerControl/utils/domHelpers.d.ts +75 -0
  18. package/dist/components/LiveCoreView/PlayerControl/utils/domHelpers.js +120 -0
  19. package/dist/components/LiveCoreView/PlayerControl/utils/fullscreenManager.d.ts +120 -0
  20. package/dist/components/LiveCoreView/PlayerControl/utils/fullscreenManager.js +311 -0
  21. package/dist/components/LiveCoreView/i18n/en-US/index.d.ts +9 -0
  22. package/dist/components/LiveCoreView/i18n/en-US/index.js +10 -1
  23. package/dist/components/LiveCoreView/i18n/zh-CN/index.d.ts +9 -0
  24. package/dist/components/LiveCoreView/i18n/zh-CN/index.js +10 -1
  25. package/dist/components/LiveCoreView/index.js +23 -3
  26. package/dist/styles/index.css +302 -30
  27. package/package.json +2 -2
  28. package/src/components/ChatSetting/GroupChatSetting/GroupActions/GroupActions.vue +0 -3
  29. package/src/components/ChatSetting/GroupChatSetting/GroupChatSetting.vue +0 -1
  30. package/src/components/ChatSetting/GroupChatSetting/GroupManagement/GroupManagement.vue +0 -1
  31. package/src/components/ContactList/ContactInfo/GroupInfo/GroupInfo.vue +0 -1
  32. package/src/components/ConversationList/ConversationCreate/ConversationCreate.vue +0 -1
  33. package/src/components/ConversationList/ConversationSearch/ConversationSearch.vue +0 -1
  34. package/src/components/LiveCoreView/PlayerControl/AudioControl.vue +456 -0
  35. package/src/components/LiveCoreView/PlayerControl/PlayerControl.module.scss +52 -0
  36. package/src/components/LiveCoreView/PlayerControl/PlayerControl.vue +429 -0
  37. package/src/components/LiveCoreView/PlayerControl/PlayerControlState.ts +599 -0
  38. package/src/components/LiveCoreView/PlayerControl/index.ts +3 -0
  39. package/src/components/LiveCoreView/PlayerControl/utils/deviceDetection.ts +234 -0
  40. package/src/components/LiveCoreView/PlayerControl/utils/domHelpers.ts +145 -0
  41. package/src/components/LiveCoreView/PlayerControl/utils/fullscreenManager.ts +417 -0
  42. package/src/components/LiveCoreView/i18n/en-US/index.ts +9 -0
  43. package/src/components/LiveCoreView/i18n/zh-CN/index.ts +9 -0
  44. package/src/components/LiveCoreView/index.vue +13 -2
@@ -0,0 +1,456 @@
1
+ <template>
2
+ <div class="audio-control" :style="iconSizeStyle" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
3
+ <span class="control-btn volume-btn" :title="isMuted ? t('Unmute') : t('Mute')" @click="handleVolumeIconClick">
4
+ <IconSpeakerOff size="20" v-if="isMuted" />
5
+ <IconSpeakerOn size="20" v-else />
6
+ </span>
7
+ <div v-show="isVolumeSliderVisible" class="volume-slider-container">
8
+ <div
9
+ class="volume-slider-wrapper"
10
+ @mouseenter="handleVolumeSliderMouseEnter"
11
+ @mouseleave="handleVolumeSliderMouseLeave"
12
+ >
13
+ <div class="volume-slider-wrapper-inner">
14
+ <div
15
+ ref="volumeSliderElement"
16
+ class="custom-volume-slider"
17
+ @mousedown="handleSliderMouseDown"
18
+ @touchstart="handleSliderTouchStart"
19
+ @click="handleVolumeSliderAreaClick"
20
+ >
21
+ <div class="slider-track">
22
+ <div
23
+ class="slider-progress"
24
+ :style="{ height: `${volumePercentage}%` }"
25
+ ></div>
26
+ <div
27
+ class="slider-thumb"
28
+ :class="{ 'no-transition': isDragging }"
29
+ :style="{ bottom: `${volumePercentage}%` }"
30
+ ></div>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ <div class="volume-value">{{ volumePercentage }}</div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </template>
39
+
40
+ <script setup lang="ts">
41
+ import { computed, ref, onUnmounted } from 'vue';
42
+ import { IconSpeakerOn, IconSpeakerOff, useUIKit } from '@tencentcloud/uikit-base-component-vue3';
43
+ import { isMobile } from '../../../utils';
44
+
45
+ interface AudioControlEmits {
46
+ (e: 'volume-change', value: number): void;
47
+ (e: 'muted-change', value: boolean): void;
48
+ }
49
+
50
+ interface AudioControlProps {
51
+ iconSize?: number;
52
+ enableVolumeControl?: boolean;
53
+ }
54
+
55
+ const props = withDefaults(defineProps<AudioControlProps>(), {
56
+ iconSize: 20,
57
+ enableVolumeControl: true,
58
+ });
59
+
60
+ const emit = defineEmits<AudioControlEmits>();
61
+
62
+ const { t } = useUIKit();
63
+
64
+ // Volume state - merged into single object
65
+ const volumeState = ref({
66
+ current: 1,
67
+ previous: 1
68
+ });
69
+
70
+ const isMuted = ref(false);
71
+ const isVolumeSliderVisible = ref(false);
72
+ const isDragging = ref(false);
73
+ const volumeSliderElement = ref<HTMLElement>();
74
+ const volumeSliderAutoHideTimer = ref<number | null>(null);
75
+
76
+ // Auto-hide delay for different platforms
77
+ const AUTO_HIDE_DELAY = {
78
+ PC: 500, // 0.5 seconds for PC
79
+ MOBILE: 3000 // 3 seconds for mobile
80
+ };
81
+
82
+ // Simplified computed property - directly use isVolumeSliderVisible
83
+ const volumePercentage = computed(() => {
84
+ if (props.enableVolumeControl === false) {
85
+ return isMuted.value ? 0 : 100;
86
+ }
87
+ return Math.round(volumeState.value.current * 100);
88
+ });
89
+
90
+ const iconSizeStyle = computed(() => ({
91
+ width: `${props.iconSize || 20}px`,
92
+ height: `${props.iconSize || 20}px`,
93
+ }));
94
+
95
+ const updateVolume = (newVolume: number) => {
96
+ volumeState.value.current = newVolume;
97
+ volumeState.value.previous = newVolume;
98
+ isMuted.value = newVolume === 0;
99
+ emit('volume-change', newVolume);
100
+ };
101
+
102
+ const toggleMute = () => {
103
+ if (isMuted.value) {
104
+ // Unmute
105
+ isMuted.value = false;
106
+ volumeState.value.current = volumeState.value.previous || 0.2;
107
+ emit('muted-change', false);
108
+ } else {
109
+ // Mute
110
+ isMuted.value = true;
111
+ volumeState.value.previous = volumeState.value.current;
112
+ volumeState.value.current = 0;
113
+ emit('muted-change', true);
114
+ }
115
+ updateVolume(volumeState.value.current);
116
+ };
117
+
118
+ const handleVolumeIconClick = () => {
119
+ if (props.enableVolumeControl === false) {
120
+ // When volume control is disabled, handle mute/unmute on all platforms
121
+ toggleMute();
122
+ } else {
123
+ // When volume control is enabled
124
+ if (isMobile) {
125
+ // On mobile: toggle volume slider visibility
126
+ isVolumeSliderVisible.value = !isVolumeSliderVisible.value;
127
+
128
+ // Start auto-hide timer when showing volume slider
129
+ if (isVolumeSliderVisible.value) {
130
+ startVolumeSliderAutoHideTimer();
131
+ } else {
132
+ stopVolumeSliderAutoHideTimer();
133
+ }
134
+ } else {
135
+ // On PC: handle mute/unmute, volume slider will show on mouse hover
136
+ toggleMute();
137
+ }
138
+ }
139
+ };
140
+
141
+ const startVolumeSliderAutoHideTimer = () => {
142
+ stopVolumeSliderAutoHideTimer();
143
+ const delay = isMobile ? AUTO_HIDE_DELAY.MOBILE : AUTO_HIDE_DELAY.PC;
144
+ volumeSliderAutoHideTimer.value = window.setTimeout(() => {
145
+ if (props.enableVolumeControl) {
146
+ isVolumeSliderVisible.value = false;
147
+ }
148
+ }, delay);
149
+ };
150
+
151
+ const stopVolumeSliderAutoHideTimer = () => {
152
+ if (volumeSliderAutoHideTimer.value) {
153
+ clearTimeout(volumeSliderAutoHideTimer.value);
154
+ volumeSliderAutoHideTimer.value = null;
155
+ }
156
+ };
157
+
158
+ const handleMouseEnter = () => {
159
+ if (props.enableVolumeControl === false) return;
160
+
161
+ // Only handle mouse events on PC
162
+ if (isMobile) return;
163
+
164
+ // On PC, show volume slider and start auto-hide timer
165
+ isVolumeSliderVisible.value = true;
166
+ startVolumeSliderAutoHideTimer();
167
+ };
168
+
169
+ const handleMouseLeave = () => {
170
+ if (props.enableVolumeControl === false) return;
171
+
172
+ // Only handle mouse events on PC
173
+ if (isMobile) return;
174
+
175
+ // On PC, start auto-hide timer when mouse leaves icon area
176
+ // But don't start if currently dragging
177
+ if (!isDragging.value) {
178
+ startVolumeSliderAutoHideTimer();
179
+ }
180
+ };
181
+
182
+ const calculateVolumeFromPosition = (clientY: number, target: HTMLElement): number => {
183
+ const rect = target.getBoundingClientRect();
184
+ const clickY = clientY - rect.top;
185
+ const height = rect.height;
186
+ return Math.max(0, Math.min(1, 1 - (clickY / height)));
187
+ };
188
+
189
+ const addGlobalEventListeners = () => {
190
+ document.addEventListener('mousemove', handleSliderMove);
191
+ document.addEventListener('mouseup', handleSliderEnd);
192
+ document.addEventListener('touchmove', handleSliderMove);
193
+ document.addEventListener('touchend', handleSliderEnd);
194
+ };
195
+
196
+ const removeGlobalEventListeners = () => {
197
+ document.removeEventListener('mousemove', handleSliderMove);
198
+ document.removeEventListener('mouseup', handleSliderEnd);
199
+ document.removeEventListener('touchmove', handleSliderMove);
200
+ document.removeEventListener('touchend', handleSliderEnd);
201
+ };
202
+
203
+ const startDragging = () => {
204
+ isDragging.value = true;
205
+
206
+ // Stop auto-hide timer when dragging starts
207
+ if (props.enableVolumeControl) {
208
+ stopVolumeSliderAutoHideTimer();
209
+ }
210
+
211
+ addGlobalEventListeners();
212
+ };
213
+
214
+ const handleSliderMove = (event: MouseEvent | TouchEvent) => {
215
+ if (!isDragging.value) return;
216
+
217
+ event.preventDefault();
218
+
219
+ let clientY: number;
220
+ if (event instanceof MouseEvent) {
221
+ clientY = event.clientY;
222
+ } else {
223
+ clientY = event.touches[0].clientY;
224
+ }
225
+
226
+ const volumeValue = calculateVolumeFromPosition(clientY, volumeSliderElement.value as HTMLElement);
227
+ updateVolume(volumeValue);
228
+ };
229
+
230
+ const handleSliderEnd = () => {
231
+ isDragging.value = false;
232
+
233
+ // Restart auto-hide timer when dragging ends
234
+ if (props.enableVolumeControl && isVolumeSliderVisible.value) {
235
+ startVolumeSliderAutoHideTimer();
236
+ }
237
+
238
+ removeGlobalEventListeners();
239
+ };
240
+
241
+ const handleSliderMouseDown = (event: MouseEvent) => {
242
+ if (props.enableVolumeControl === false) return;
243
+
244
+ startDragging();
245
+ event.preventDefault();
246
+ };
247
+
248
+ const handleSliderTouchStart = (event: TouchEvent) => {
249
+ if (props.enableVolumeControl === false) return;
250
+
251
+ startDragging();
252
+ event.preventDefault();
253
+ };
254
+
255
+ const handleVolumeSliderAreaClick = () => {
256
+ if (props.enableVolumeControl === false) return;
257
+
258
+ if (isMobile) {
259
+ // On mobile, toggle volume slider visibility
260
+ isVolumeSliderVisible.value = !isVolumeSliderVisible.value;
261
+
262
+ // Start auto-hide timer when showing volume slider
263
+ if (isVolumeSliderVisible.value) {
264
+ startVolumeSliderAutoHideTimer();
265
+ } else {
266
+ stopVolumeSliderAutoHideTimer();
267
+ }
268
+ }
269
+ };
270
+
271
+ const handleVolumeSliderMouseEnter = () => {
272
+ if (props.enableVolumeControl === false) return;
273
+
274
+ // Only handle mouse events on PC
275
+ if (isMobile) return;
276
+
277
+ // On PC, stop auto-hide timer when mouse enters slider area
278
+ stopVolumeSliderAutoHideTimer();
279
+ };
280
+
281
+ const handleVolumeSliderMouseLeave = () => {
282
+ if (props.enableVolumeControl === false) return;
283
+
284
+ // Only handle mouse events on PC
285
+ if (isMobile) return;
286
+
287
+ // On PC, start auto-hide timer when mouse leaves slider area
288
+ // But don't start if currently dragging
289
+ if (!isDragging.value) {
290
+ startVolumeSliderAutoHideTimer();
291
+ }
292
+ };
293
+
294
+ onUnmounted(() => {
295
+ removeGlobalEventListeners();
296
+
297
+ // Clean up timers
298
+ if (volumeSliderAutoHideTimer.value) {
299
+ clearTimeout(volumeSliderAutoHideTimer.value);
300
+ }
301
+ });
302
+ </script>
303
+
304
+ <style scoped lang="scss">
305
+ .audio-control {
306
+ --volume-control-primary: rgb(255, 255, 255);
307
+ --volume-control-primary-hover: rgba(255, 255, 255, 0.1);
308
+ --volume-control-background: rgba(0, 0, 0, 0.8);
309
+ --volume-control-background-light: rgba(0, 0, 0, 0.5);
310
+ --volume-control-border: rgba(255, 255, 255, 0.1);
311
+ --volume-control-shadow: rgba(0, 0, 0, 0.2);
312
+
313
+ position: relative;
314
+ display: flex;
315
+ align-items: center;
316
+ }
317
+
318
+ .volume-btn {
319
+ width: 100%;
320
+ height: 100%;
321
+ flex-shrink: 0;
322
+ cursor: pointer;
323
+ transition: background-color 0.2s ease;
324
+ }
325
+
326
+ .volume-slider-container {
327
+ position: absolute;
328
+ bottom: 100%;
329
+ left: 50%;
330
+ transform: translateX(-50%);
331
+ margin-bottom: 12px;
332
+ z-index: 100;
333
+ }
334
+
335
+ .volume-slider-wrapper {
336
+ position: relative;
337
+ display: flex;
338
+ flex-direction: column;
339
+ align-items: center;
340
+ gap: 8px;
341
+ background: var(--volume-control-background);
342
+ padding: 12px 8px;
343
+ border-radius: 8px;
344
+ backdrop-filter: blur(10px);
345
+ border: 1px solid var(--volume-control-border);
346
+ cursor: pointer;
347
+ user-select: none;
348
+ -webkit-user-select: none;
349
+ -moz-user-select: none;
350
+ -ms-user-select: none;
351
+
352
+ @media (hover: none) and (pointer: coarse) {
353
+ cursor: grab;
354
+
355
+ &:active {
356
+ cursor: grabbing;
357
+ }
358
+ }
359
+ }
360
+
361
+ .volume-slider-wrapper-inner {
362
+ position: relative;
363
+ width: 20px;
364
+ height: 80px;
365
+ display: flex;
366
+ align-items: center;
367
+ justify-content: center;
368
+ margin-top: 12px;
369
+ }
370
+
371
+ .custom-volume-slider {
372
+ position: relative;
373
+ width: 4px;
374
+ height: 80px;
375
+ cursor: pointer;
376
+ z-index: 2;
377
+ margin: 0;
378
+ }
379
+
380
+ .slider-track {
381
+ position: absolute;
382
+ top: 0;
383
+ left: 0;
384
+ width: 100%;
385
+ height: 100%;
386
+ border-radius: 2px;
387
+ background: rgba(255, 255, 255, 0.2);
388
+ border: none;
389
+ }
390
+
391
+ .slider-progress {
392
+ position: absolute;
393
+ bottom: 0;
394
+ left: 0;
395
+ width: 100%;
396
+ background: #ffffff;
397
+ border-radius: 2px;
398
+ }
399
+
400
+ .slider-thumb {
401
+ $thumb-size: 12px;
402
+
403
+ position: absolute;
404
+ left: 50%;
405
+ transform: translateX(-50%);
406
+ width: $thumb-size;
407
+ height: $thumb-size;
408
+ background: #ffffff;
409
+ border-radius: 50%;
410
+ border: 2px solid rgba(255, 255, 255, 0.8);
411
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
412
+ transition: transform 0.1s ease;
413
+ z-index: 3;
414
+ cursor: grab;
415
+
416
+ &.no-transition {
417
+ transition: none;
418
+ }
419
+
420
+ &:active {
421
+ cursor: grabbing;
422
+ transform: translateX(-50%) scale(1.1);
423
+ }
424
+
425
+ &:hover {
426
+ transform: translateX(-50%) scale(1.1);
427
+ }
428
+ }
429
+
430
+ .volume-value {
431
+ color: var(--volume-control-primary);
432
+ font-size: 12px;
433
+ font-weight: 500;
434
+ text-align: center;
435
+ min-width: 32px;
436
+ padding: 2px 6px;
437
+ border-radius: 4px;
438
+ pointer-events: none;
439
+ }
440
+
441
+ @media (hover: none) and (pointer: coarse) {
442
+ .volume-slider-wrapper {
443
+ padding: 16px 12px;
444
+
445
+ &:active {
446
+ background: var(--volume-control-background-light);
447
+ transform: scale(0.98);
448
+ transition: all 0.1s ease;
449
+ }
450
+ }
451
+
452
+ .volume-slider-wrapper-inner {
453
+ height: 100px;
454
+ }
455
+ }
456
+ </style>
@@ -0,0 +1,52 @@
1
+ // Fullscreen mode base styles
2
+ .fullscreen-mode {
3
+ width: 100vw !important;
4
+ height: 100vh !important;
5
+ z-index: 9999 !important;
6
+ background-color: #000 !important;
7
+ }
8
+
9
+ // H5 portrait stream fullscreen mode (preserve original width/height, no forced modification)
10
+ .fullscreen-mode-portrait {
11
+ z-index: 9999 !important;
12
+ background-color: #000 !important;
13
+ // Note: Do not modify width and height, preserve original styles
14
+ }
15
+
16
+ // Landscape mode styles - Only apply CSS rotation when device is in portrait
17
+ .landscape-mode {
18
+ transform: rotate(90deg) !important;
19
+ transform-origin: center center !important;
20
+ width: 100vh !important;
21
+ height: 100vw !important;
22
+ overflow: hidden !important;
23
+ // Add smooth transition effect
24
+ transition: transform 0.3s ease-in-out !important;
25
+ }
26
+
27
+ // When device is in landscape, remove CSS rotation to avoid double rotation
28
+ @media screen and (orientation: landscape) {
29
+ .landscape-mode {
30
+ transform: none !important;
31
+ width: 100vw !important;
32
+ height: 100vh !important;
33
+ }
34
+ }
35
+
36
+ // Mobile device landscape optimization
37
+ @media screen and (orientation: landscape) and (max-width: 768px) {
38
+ .live-core-view.landscape-mode {
39
+ // Ensure correct display in landscape
40
+ background-color: #000;
41
+ transform: none !important;
42
+ }
43
+ }
44
+
45
+ // Force landscape display on mobile portrait
46
+ @media screen and (orientation: portrait) and (max-width: 768px) {
47
+ .live-core-view.landscape-mode {
48
+ // Force landscape display in portrait
49
+ transform: rotate(90deg) !important;
50
+ transform-origin: center center !important;
51
+ }
52
+ }