tuikit-atomicx-vue3 3.3.1 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) 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/CoGuestPanel/CoGuestPanel.js +6 -6
  5. package/dist/components/ContactList/ContactInfo/GroupInfo/GroupInfo.js +1 -2
  6. package/dist/components/ConversationList/ConversationCreate/ConversationCreate.js +1 -2
  7. package/dist/components/ConversationList/ConversationSearch/ConversationSearch.js +0 -1
  8. package/dist/components/LiveCoreView/PlayerControl/AudioControl.js +252 -0
  9. package/dist/components/LiveCoreView/PlayerControl/AudioControl.vue.d.ts +38 -0
  10. package/dist/components/LiveCoreView/PlayerControl/PlayerControl.js +279 -0
  11. package/dist/components/LiveCoreView/PlayerControl/PlayerControl.vue.d.ts +15 -0
  12. package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.d.ts +29 -0
  13. package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.js +412 -0
  14. package/dist/components/LiveCoreView/PlayerControl/index.d.ts +3 -0
  15. package/dist/components/LiveCoreView/PlayerControl/index.js +8 -0
  16. package/dist/components/LiveCoreView/PlayerControl/utils/deviceDetection.d.ts +85 -0
  17. package/dist/components/LiveCoreView/PlayerControl/utils/deviceDetection.js +129 -0
  18. package/dist/components/LiveCoreView/PlayerControl/utils/domHelpers.d.ts +75 -0
  19. package/dist/components/LiveCoreView/PlayerControl/utils/domHelpers.js +120 -0
  20. package/dist/components/LiveCoreView/PlayerControl/utils/fullscreenManager.d.ts +120 -0
  21. package/dist/components/LiveCoreView/PlayerControl/utils/fullscreenManager.js +311 -0
  22. package/dist/components/LiveCoreView/i18n/en-US/index.d.ts +9 -0
  23. package/dist/components/LiveCoreView/i18n/en-US/index.js +10 -1
  24. package/dist/components/LiveCoreView/i18n/zh-CN/index.d.ts +9 -0
  25. package/dist/components/LiveCoreView/i18n/zh-CN/index.js +10 -1
  26. package/dist/components/LiveCoreView/index.js +30 -4
  27. package/dist/components/StreamView/Layout/CustomLayout.js +2 -2
  28. package/dist/components/StreamView/Layout/GridLayout.js +2 -2
  29. package/dist/components/StreamView/common/StreamList/index.js +2 -2
  30. package/dist/styles/index.css +336 -31
  31. package/package.json +3 -3
  32. package/src/components/ChatSetting/GroupChatSetting/GroupActions/GroupActions.vue +0 -3
  33. package/src/components/ChatSetting/GroupChatSetting/GroupChatSetting.vue +0 -1
  34. package/src/components/ChatSetting/GroupChatSetting/GroupManagement/GroupManagement.vue +0 -1
  35. package/src/components/ContactList/ContactInfo/GroupInfo/GroupInfo.vue +0 -1
  36. package/src/components/ConversationList/ConversationCreate/ConversationCreate.vue +0 -1
  37. package/src/components/ConversationList/ConversationSearch/ConversationSearch.vue +0 -1
  38. package/src/components/LiveCoreView/PlayerControl/AudioControl.vue +434 -0
  39. package/src/components/LiveCoreView/PlayerControl/PlayerControl.module.scss +52 -0
  40. package/src/components/LiveCoreView/PlayerControl/PlayerControl.vue +484 -0
  41. package/src/components/LiveCoreView/PlayerControl/PlayerControlState.ts +602 -0
  42. package/src/components/LiveCoreView/PlayerControl/index.ts +3 -0
  43. package/src/components/LiveCoreView/PlayerControl/utils/deviceDetection.ts +234 -0
  44. package/src/components/LiveCoreView/PlayerControl/utils/domHelpers.ts +145 -0
  45. package/src/components/LiveCoreView/PlayerControl/utils/fullscreenManager.ts +417 -0
  46. package/src/components/LiveCoreView/i18n/en-US/index.ts +9 -0
  47. package/src/components/LiveCoreView/i18n/zh-CN/index.ts +9 -0
  48. package/src/components/LiveCoreView/index.vue +14 -3
@@ -0,0 +1,417 @@
1
+ /**
2
+ * Fullscreen management utility module
3
+ */
4
+
5
+ import { DeviceType } from './deviceDetection';
6
+
7
+ // Screen orientation type
8
+ export type ScreenOrientation = 'landscape' | 'portrait';
9
+
10
+ // Fullscreen mode type
11
+ export enum FullscreenMode {
12
+ STANDARD = 'standard', // Standard API fullscreen
13
+ CSS_SIMULATED = 'simulated' // CSS simulated fullscreen
14
+ }
15
+
16
+ // CSS class name constants
17
+ export const CSS_CLASSES = {
18
+ FULLSCREEN: 'fullscreen-mode',
19
+ FULLSCREEN_PORTRAIT: 'fullscreen-mode-portrait',
20
+ LANDSCAPE: 'landscape-mode',
21
+ } as const;
22
+
23
+ // Fullscreen operation result
24
+ export interface FullscreenResult {
25
+ success: boolean;
26
+ mode: FullscreenMode;
27
+ shouldRotateToLandscape: boolean;
28
+ error?: Error;
29
+ }
30
+
31
+ /**
32
+ * Style management class
33
+ */
34
+ export class StyleManager {
35
+ /**
36
+ * Apply fullscreen styles
37
+ */
38
+ static applyFullscreenStyles(element: HTMLElement, isPortrait = false): void {
39
+ const className = isPortrait ? CSS_CLASSES.FULLSCREEN_PORTRAIT : CSS_CLASSES.FULLSCREEN;
40
+ element.classList.add(className);
41
+ }
42
+
43
+ /**
44
+ * Remove fullscreen styles
45
+ */
46
+ static removeFullscreenStyles(element: HTMLElement): void {
47
+ element.classList.remove(CSS_CLASSES.FULLSCREEN);
48
+ element.classList.remove(CSS_CLASSES.FULLSCREEN_PORTRAIT);
49
+ }
50
+
51
+ /**
52
+ * Apply landscape styles
53
+ */
54
+ static applyLandscapeStyles(element: HTMLElement): void {
55
+ element.classList.add(CSS_CLASSES.LANDSCAPE);
56
+ }
57
+
58
+ /**
59
+ * Remove landscape styles
60
+ */
61
+ static removeLandscapeStyles(element: HTMLElement): void {
62
+ element.classList.remove(CSS_CLASSES.LANDSCAPE);
63
+ }
64
+
65
+ /**
66
+ * Smart landscape style adjustment - determine if CSS rotation is needed based on current device orientation
67
+ */
68
+ static smartApplyLandscapeStyles(element: HTMLElement, currentOrientation: 'portrait' | 'landscape' | 'unknown'): void {
69
+ // If device is already in landscape orientation, no need to apply CSS rotation
70
+ if (currentOrientation === 'landscape') {
71
+ this.removeLandscapeStyles(element);
72
+ } else {
73
+ // When device is in portrait, need CSS rotation to display landscape content
74
+ this.applyLandscapeStyles(element);
75
+ }
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Screen orientation management class
81
+ */
82
+ export class OrientationManager {
83
+ private static orientationListeners: Map<string, (orientation: 'portrait' | 'landscape' | 'unknown') => void> = new Map();
84
+
85
+ /**
86
+ * Request screen orientation
87
+ */
88
+ static async requestOrientation(orientation: ScreenOrientation): Promise<boolean> {
89
+ try {
90
+ // Prioritize Screen Orientation API
91
+ if (screen.orientation && (screen.orientation as any).lock) {
92
+ await (screen.orientation as any).lock(orientation);
93
+ return true;
94
+ }
95
+
96
+ // Use webkit prefixed API as fallback
97
+ if ((screen.orientation as any).webkitLockOrientation) {
98
+ const result = (screen.orientation as any).webkitLockOrientation(orientation);
99
+ return !!result;
100
+ }
101
+
102
+ return false;
103
+ } catch (error) {
104
+ console.warn(`Screen orientation setting failed (${orientation}):`, error);
105
+ return false;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Unlock screen orientation
111
+ */
112
+ static async unlockOrientation(): Promise<boolean> {
113
+ try {
114
+ if (screen.orientation && (screen.orientation as any).unlock) {
115
+ (screen.orientation as any).unlock();
116
+ return true;
117
+ }
118
+ return false;
119
+ } catch (error) {
120
+ console.warn('Failed to unlock screen orientation:', error);
121
+ return false;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Get current orientation
127
+ */
128
+ static getCurrentOrientation(): 'portrait' | 'landscape' | 'unknown' {
129
+ // First try the standard Screen Orientation API
130
+ if (screen.orientation) {
131
+ const angle = screen.orientation.angle;
132
+ const type = screen.orientation.type;
133
+
134
+ // Check orientation type first (more reliable)
135
+ if (type.includes('portrait')) {
136
+ return 'portrait';
137
+ } else if (type.includes('landscape')) {
138
+ return 'landscape';
139
+ }
140
+
141
+ // Fallback to angle-based detection
142
+ if (angle === 0 || angle === 180) {
143
+ return 'portrait';
144
+ } else if (angle === 90 || angle === 270) {
145
+ return 'landscape';
146
+ }
147
+ }
148
+
149
+ // Fallback to window dimensions
150
+ if (window.innerHeight > window.innerWidth) {
151
+ return 'portrait';
152
+ } else if (window.innerWidth > window.innerHeight) {
153
+ return 'landscape';
154
+ }
155
+
156
+ return 'unknown';
157
+ }
158
+
159
+ /**
160
+ * Add orientation change listener
161
+ */
162
+ static addOrientationListener(id: string, callback: (orientation: 'portrait' | 'landscape' | 'unknown') => void): void {
163
+ this.orientationListeners.set(id, callback);
164
+
165
+ // If this is the first listener, set up global listening
166
+ if (this.orientationListeners.size === 1) {
167
+ this.setupGlobalOrientationListener();
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Remove orientation change listener
173
+ */
174
+ static removeOrientationListener(id: string): void {
175
+ this.orientationListeners.delete(id);
176
+
177
+ // If no more listeners, clean up global listening
178
+ if (this.orientationListeners.size === 0) {
179
+ this.cleanupGlobalOrientationListener();
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Setup global orientation listening
185
+ */
186
+ private static setupGlobalOrientationListener(): void {
187
+ const handleOrientationChange = () => {
188
+ const currentOrientation = this.getCurrentOrientation();
189
+ console.log('Orientation change detected:', currentOrientation);
190
+
191
+ // Notify all listeners
192
+ this.orientationListeners.forEach((callback) => {
193
+ try {
194
+ callback(currentOrientation);
195
+ } catch (error) {
196
+ console.error('Orientation change callback execution failed:', error);
197
+ }
198
+ });
199
+ };
200
+
201
+ // Listen to multiple orientation change events
202
+ window.addEventListener('orientationchange', handleOrientationChange);
203
+ window.addEventListener('resize', handleOrientationChange);
204
+
205
+ // Also listen to Screen Orientation API if supported
206
+ if (screen.orientation) {
207
+ screen.orientation.addEventListener('change', handleOrientationChange);
208
+ }
209
+
210
+ // Save cleanup function reference
211
+ (this as any)._cleanupOrientationListeners = () => {
212
+ window.removeEventListener('orientationchange', handleOrientationChange);
213
+ window.removeEventListener('resize', handleOrientationChange);
214
+ if (screen.orientation) {
215
+ screen.orientation.removeEventListener('change', handleOrientationChange);
216
+ }
217
+ };
218
+ }
219
+
220
+ /**
221
+ * Cleanup global orientation listening
222
+ */
223
+ private static cleanupGlobalOrientationListener(): void {
224
+ if ((this as any)._cleanupOrientationListeners) {
225
+ (this as any)._cleanupOrientationListeners();
226
+ delete (this as any)._cleanupOrientationListeners;
227
+ }
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Fullscreen API management class
233
+ */
234
+ export class FullscreenAPI {
235
+ /**
236
+ * Request standard fullscreen
237
+ */
238
+ static async requestFullscreen(element: HTMLElement): Promise<boolean> {
239
+ try {
240
+ if (element.requestFullscreen) {
241
+ await element.requestFullscreen();
242
+ } else if ((element as any).webkitRequestFullscreen) {
243
+ await (element as any).webkitRequestFullscreen();
244
+ } else if ((element as any).mozRequestFullScreen) {
245
+ await (element as any).mozRequestFullScreen();
246
+ } else if ((element as any).msRequestFullscreen) {
247
+ await (element as any).msRequestFullscreen();
248
+ } else {
249
+ return false;
250
+ }
251
+ return true;
252
+ } catch (error) {
253
+ console.error('Standard fullscreen request failed:', error);
254
+ return false;
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Exit standard fullscreen
260
+ */
261
+ static async exitFullscreen(): Promise<boolean> {
262
+ try {
263
+ if (document.exitFullscreen) {
264
+ await document.exitFullscreen();
265
+ } else if ((document as any).webkitExitFullscreen) {
266
+ await (document as any).webkitExitFullscreen();
267
+ } else if ((document as any).mozCancelFullScreen) {
268
+ await (document as any).mozCancelFullScreen();
269
+ } else if ((document as any).msExitFullscreen) {
270
+ await (document as any).msExitFullscreen();
271
+ } else {
272
+ return false;
273
+ }
274
+ return true;
275
+ } catch (error) {
276
+ console.error('Exit standard fullscreen failed:', error);
277
+ return false;
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Check if currently in fullscreen state
283
+ */
284
+ static isCurrentlyFullscreen(): boolean {
285
+ return !!document.fullscreenElement;
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Main fullscreen manager class
291
+ */
292
+ export class FullscreenManager {
293
+ /**
294
+ * Smart fullscreen request
295
+ * Choose the best fullscreen method based on device type and support
296
+ */
297
+ static async requestFullscreen(
298
+ containerElement: HTMLElement,
299
+ viewElement: HTMLElement,
300
+ deviceType: DeviceType,
301
+ isPortraitStream: boolean,
302
+ shouldRotateToLandscape: boolean
303
+ ): Promise<FullscreenResult> {
304
+ // iOS devices or devices that don't support standard fullscreen use CSS simulation
305
+ if (!this.isFullscreenSupported(containerElement)) {
306
+ return this.requestCSSFullscreen(viewElement, isPortraitStream, shouldRotateToLandscape);
307
+ }
308
+
309
+ // Other devices try standard fullscreen
310
+ return this.requestStandardFullscreen(containerElement, viewElement, shouldRotateToLandscape);
311
+ }
312
+
313
+ /**
314
+ * Smart fullscreen exit
315
+ */
316
+ static async exitFullscreen(
317
+ viewElement: HTMLElement,
318
+ deviceType: DeviceType,
319
+ hadLandscapeRotation: boolean
320
+ ): Promise<FullscreenResult> {
321
+ // Remove CSS styles
322
+ StyleManager.removeFullscreenStyles(viewElement);
323
+ StyleManager.removeLandscapeStyles(viewElement);
324
+
325
+ // Other devices try to exit standard fullscreen
326
+ const success = await FullscreenAPI.exitFullscreen();
327
+ if (hadLandscapeRotation) {
328
+ await OrientationManager.unlockOrientation();
329
+ }
330
+
331
+ return {
332
+ success,
333
+ mode: success ? FullscreenMode.STANDARD : FullscreenMode.CSS_SIMULATED,
334
+ shouldRotateToLandscape: hadLandscapeRotation
335
+ };
336
+ }
337
+
338
+ /**
339
+ * CSS simulated fullscreen
340
+ */
341
+ private static async requestCSSFullscreen(
342
+ element: HTMLElement,
343
+ isPortraitStream: boolean,
344
+ shouldRotateToLandscape: boolean
345
+ ): Promise<FullscreenResult> {
346
+ try {
347
+ // Apply fullscreen styles
348
+ StyleManager.applyFullscreenStyles(element, isPortraitStream);
349
+
350
+ // Handle landscape rotation
351
+ if (shouldRotateToLandscape) {
352
+ const orientationSuccess = await OrientationManager.requestOrientation('landscape');
353
+ if (!orientationSuccess) {
354
+ // Use smart style adjustment based on current device orientation
355
+ const currentOrientation = OrientationManager.getCurrentOrientation();
356
+ StyleManager.smartApplyLandscapeStyles(element, currentOrientation);
357
+ }
358
+ }
359
+
360
+ return { success: true, mode: FullscreenMode.CSS_SIMULATED, shouldRotateToLandscape };
361
+ } catch (error) {
362
+ return {
363
+ success: false,
364
+ mode: FullscreenMode.CSS_SIMULATED,
365
+ shouldRotateToLandscape,
366
+ error: error as Error
367
+ };
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Standard API fullscreen
373
+ */
374
+ private static async requestStandardFullscreen(
375
+ containerElement: HTMLElement,
376
+ viewElement: HTMLElement,
377
+ shouldRotateToLandscape: boolean
378
+ ): Promise<FullscreenResult> {
379
+ try {
380
+ const success = await FullscreenAPI.requestFullscreen(containerElement);
381
+ if (!success) {
382
+ return { success: false, mode: FullscreenMode.STANDARD, shouldRotateToLandscape };
383
+ }
384
+
385
+ // Handle landscape rotation
386
+ if (shouldRotateToLandscape) {
387
+ const orientationSuccess = await OrientationManager.requestOrientation('landscape');
388
+ if (!orientationSuccess) {
389
+ // Use smart style adjustment based on current device orientation
390
+ const currentOrientation = OrientationManager.getCurrentOrientation();
391
+ StyleManager.smartApplyLandscapeStyles(viewElement, currentOrientation);
392
+ }
393
+ }
394
+
395
+ return { success: true, mode: FullscreenMode.STANDARD, shouldRotateToLandscape };
396
+ } catch (error) {
397
+ return {
398
+ success: false,
399
+ mode: FullscreenMode.STANDARD,
400
+ shouldRotateToLandscape,
401
+ error: error as Error
402
+ };
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Check if fullscreen is supported
408
+ */
409
+ private static isFullscreenSupported(element: HTMLElement): boolean {
410
+ return !!(
411
+ element.requestFullscreen ||
412
+ (element as any).webkitRequestFullscreen ||
413
+ (element as any).mozRequestFullScreen ||
414
+ (element as any).msRequestFullscreen
415
+ );
416
+ }
417
+ }
@@ -1,4 +1,13 @@
1
1
  export const resource = {
2
2
  'No video': 'No video',
3
3
  'Waiting for connection': 'Waiting for connection',
4
+ 'Play': 'Play',
5
+ 'Pause': 'Pause',
6
+ 'Picture in Picture': 'Picture in Picture',
7
+ 'Exit Picture in Picture': 'Exit Picture in Picture',
8
+ 'Fullscreen': 'Fullscreen',
9
+ 'Exit Fullscreen': 'Exit Fullscreen',
10
+ 'Mute': 'Mute',
11
+ 'Unmute': 'Unmute',
12
+ 'The system does not support picture-in-picture mode': 'The system does not support picture-in-picture mode',
4
13
  };
@@ -1,4 +1,13 @@
1
1
  export const resource = {
2
2
  'No video': '暂无画面',
3
3
  'Waiting for connection': '等待连线',
4
+ 'Play': '播放',
5
+ 'Pause': '暂停',
6
+ 'Picture in Picture': '画中画',
7
+ 'Exit Picture in Picture': '退出画中画',
8
+ 'Fullscreen': '全屏',
9
+ 'Exit Fullscreen': '退出全屏',
10
+ 'Mute': '静音',
11
+ 'Unmute': '取消静音',
12
+ 'The system does not support picture-in-picture mode': '系统不支持画中画模式',
4
13
  };
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <div
3
3
  ref="liveCoreViewContainerRef"
4
+ id="live-core-view-container"
4
5
  class="live-core-view-container"
5
6
  :class="{ 'align-center': isAlignCenter }"
6
7
  >
@@ -44,20 +45,27 @@
44
45
  v-bind="{ style: localStreamViewInfo?.region }"
45
46
  />
46
47
  </div>
48
+ <Teleport to="body" v-if="!isFullscreen" :disabled="!isMobile">
49
+ <PlayerControl :isLandscapeStyleMode="isLandscapeStyleMode" v-if="isShowPlayerControl" />
50
+ </Teleport>
51
+ <PlayerControl :isLandscapeStyleMode="isLandscapeStyleMode" v-if="isShowPlayerControl && isFullscreen" />
47
52
  </div>
48
53
  </template>
49
54
 
50
55
  <script setup lang="ts">
51
- import { ref, computed, watch, onMounted, onBeforeUnmount, useSlots } from 'vue';
52
- import type { ComputedRef } from 'vue';
56
+ import { ref, computed, watch, onMounted, onBeforeUnmount, useSlots, type ComputedRef, Teleport } from 'vue';
53
57
  import { useUIKit } from '@tencentcloud/uikit-base-component-vue3';
54
58
  import { useLiveSeatState } from '../../states/LiveSeatState';
55
59
  import { useLiveState } from '../../states/LiveState';
56
60
  import { useLoginState } from '../../states/LoginState';
57
61
  import { getContentSize } from '../../utils/domOperation';
58
62
  import DefaultStreamViewUI from './DefaultStreamViewUI.vue';
63
+ import PlayerControl from './PlayerControl/PlayerControl.vue';
59
64
  import type { SeatInfo, SeatUserInfo } from '../../types';
65
+ import { isMobile } from '../../utils';
66
+ import { usePlayerControlState } from './PlayerControl';
60
67
 
68
+ const { isFullscreen, isLandscapeStyleMode } = usePlayerControlState();
61
69
  const { t } = useUIKit();
62
70
  const { seatList, canvas, startPlayStream, stopPlayStream } = useLiveSeatState();
63
71
  const { currentLive } = useLiveState();
@@ -75,6 +83,9 @@ const isAlignCenter = computed(() => {
75
83
  }
76
84
  return true;
77
85
  });
86
+ const isShowPlayerControl = computed(() => {
87
+ return currentLive.value?.liveId && !seatList.value.some(item => item.userInfo?.userId === loginUserInfo.value?.userId);
88
+ });
78
89
 
79
90
  onMounted(async () => {
80
91
  isMounted.value = true;
@@ -293,7 +304,7 @@ watch(() => [canvas.value, seatList.value], () => {
293
304
  fillMode.value = StreamFillMode.Fit;
294
305
  }
295
306
  handleStreamRegionSize();
296
- });
307
+ }, { deep: true });
297
308
 
298
309
  function handleStreamRegionSize() {
299
310
  if (!liveCoreViewContainerRef.value) {