Skip to content

Commit b818f11

Browse files
author
jie
committed
feat(camera-control): implement fullscreen toggle functionality
1 parent 28a0fd5 commit b818f11

3 files changed

Lines changed: 67 additions & 3 deletions

File tree

packages/widgets/media/camera-control/index.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ describe('media/camera-control widget', () => {
1818

1919
afterEach(() => {
2020
document.body.innerHTML = '';
21+
delete (HTMLElement.prototype as { requestFullscreen?: unknown }).requestFullscreen;
22+
delete (document as { exitFullscreen?: unknown }).exitFullscreen;
23+
Object.defineProperty(document, 'fullscreenElement', {
24+
configurable: true,
25+
value: null,
26+
});
2127
vi.unstubAllGlobals();
2228
});
2329

@@ -67,6 +73,46 @@ describe('media/camera-control widget', () => {
6773
harness.destroy();
6874
});
6975

76+
it('toggles the camera shell fullscreen state from the toolbar button', async () => {
77+
const { default: Main } = await import('./src/index');
78+
const requestFullscreen = vi.fn();
79+
const exitFullscreen = vi.fn();
80+
Object.defineProperty(HTMLElement.prototype, 'requestFullscreen', {
81+
configurable: true,
82+
value: requestFullscreen,
83+
});
84+
Object.defineProperty(document, 'exitFullscreen', {
85+
configurable: true,
86+
value: exitFullscreen,
87+
});
88+
Object.defineProperty(document, 'fullscreenElement', {
89+
configurable: true,
90+
value: null,
91+
});
92+
93+
const harness = mountWidget(Main, { locale: 'en', mode: 'view' });
94+
const fullscreenButton = () =>
95+
Array.from(harness.element.querySelectorAll('button')).find(
96+
(button) => button.title === 'Fullscreen' || button.title === 'Exit fullscreen',
97+
);
98+
99+
fullscreenButton()?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
100+
expect(requestFullscreen).toHaveBeenCalledTimes(1);
101+
102+
const shell = harness.element.children.item(2);
103+
Object.defineProperty(document, 'fullscreenElement', {
104+
configurable: true,
105+
value: shell,
106+
});
107+
document.dispatchEvent(new Event('fullscreenchange'));
108+
109+
expect(fullscreenButton()?.title).toBe('Exit fullscreen');
110+
fullscreenButton()?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
111+
expect(exitFullscreen).toHaveBeenCalledTimes(1);
112+
113+
harness.destroy();
114+
});
115+
70116
it('emits optional advanced control payloads when enabled', async () => {
71117
const { default: Main } = await import('./src/index');
72118
const emit = vi.fn();

packages/widgets/media/camera-control/src/index.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,20 @@ export const Main = defineWidget({
301301
});
302302
};
303303

304+
const isShellFullscreen = () => document.fullscreenElement === shell;
305+
306+
const toggleFullscreen = () => {
307+
if (isShellFullscreen()) {
308+
void document.exitFullscreen?.();
309+
return;
310+
}
311+
void shell.requestFullscreen?.();
312+
};
313+
314+
const handleFullscreenChange = () => {
315+
renderControls();
316+
};
317+
304318
const renderStatusBar = () => {
305319
const messages = getMessages(currentLocale).runtime;
306320
statusBar.innerHTML = '';
@@ -432,9 +446,10 @@ export const Main = defineWidget({
432446
}
433447

434448
if (currentProps.showFullscreen) {
435-
addAction(buttonTitle('fullscreen', 'Fullscreen'), buttonTitle('fullscreen', 'Fullscreen'), () => {
436-
void shell.requestFullscreen?.();
437-
});
449+
const fullscreenLabel = isShellFullscreen()
450+
? buttonTitle('exitFullscreen', currentLocale?.startsWith('zh') ? '退出全屏' : 'Exit fullscreen')
451+
: buttonTitle('fullscreen', 'Fullscreen');
452+
addAction(fullscreenLabel, fullscreenLabel, toggleFullscreen);
438453
}
439454

440455
if (currentProps.showPlaybackControls) {
@@ -559,6 +574,7 @@ export const Main = defineWidget({
559574
updateView();
560575

561576
window.addEventListener('blur', sendStop);
577+
document.addEventListener('fullscreenchange', handleFullscreenChange);
562578

563579
return {
564580
update: (newProps: Props, newCtx: WidgetOverlayContext) => {
@@ -572,6 +588,7 @@ export const Main = defineWidget({
572588
destroy: () => {
573589
sendStop();
574590
window.removeEventListener('blur', sendStop);
591+
document.removeEventListener('fullscreenchange', handleFullscreenChange);
575592
if (attachVideoRaf) cancelAnimationFrame(attachVideoRaf);
576593
stopReadyPoll();
577594
element.innerHTML = '';

packages/widgets/media/camera-control/src/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"preset": "Go to preset",
2525
"snapshot": "Snapshot",
2626
"fullscreen": "Fullscreen",
27+
"exitFullscreen": "Exit fullscreen",
2728
"playbackRequest": "Request playback",
2829
"mute": "Mute",
2930
"unmute": "Unmute"

0 commit comments

Comments
 (0)