Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion projects/core/browser/fullscreen/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, ElementRef, signal, viewChild } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { fullscreen } from './index';

Expand Down Expand Up @@ -137,4 +137,55 @@ describe(fullscreen.name, () => {
expect(requestFullscreenSpy).not.toHaveBeenCalled();
});
});

describe('deferred target', () => {
@Component({ template: '<div #box></div>{{ fs.isActive() }}' })
class RequiredTargetComponent {
readonly box = viewChild.required<ElementRef<HTMLElement>>('box');
readonly fs = fullscreen({ target: this.box });
}

it('should not read a required query target during class field initialization', () => {
// The eager read only happened when something was already fullscreen at creation time,
// because `document.fullscreenElement != null` short-circuited the comparison.
fullscreenElementValue = document.documentElement;

expect(() => TestBed.createComponent(RequiredTargetComponent)).not.toThrow();
});

it('should resolve a required query target once the view is created', () => {
fullscreenElementValue = document.documentElement;

const fixture = TestBed.createComponent(RequiredTargetComponent);
fixture.detectChanges();

const component = fixture.componentInstance;

fullscreenElementValue = component.box().nativeElement;
document.dispatchEvent(new Event('fullscreenchange'));

expect(component.fs.isActive()).toBe(true);
});

it('should recompute isActive when the target changes', () => {
@Component({ template: '{{ fs.isActive() }}' })
class SwappingTargetComponent {
readonly target = signal<Element>(document.createElement('div'));
readonly fs = fullscreen({ target: this.target });
}

const fixture = TestBed.createComponent(SwappingTargetComponent);
fixture.detectChanges();

const component = fixture.componentInstance;
const other = document.createElement('section');

fullscreenElementValue = other;
document.dispatchEvent(new Event('fullscreenchange'));
expect(component.fs.isActive()).toBe(false);

component.target.set(other);
expect(component.fs.isActive()).toBe(true);
});
});
});
16 changes: 10 additions & 6 deletions projects/core/browser/fullscreen/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { signal, type Signal, untracked } from '@angular/core';
import { computed, signal, type Signal, untracked } from '@angular/core';
import { assertElement, constSignal, NOOP_ASYNC_FN, setupContext } from '@signality/core/internal';
import { toElement } from '@signality/core/utilities';
import type { MaybeElementSignal, WithInjector } from '@signality/core/types';
Expand Down Expand Up @@ -105,10 +105,12 @@ export function fullscreen(options?: FullscreenOptions): FullscreenRef {

const target = options?.target ?? document.documentElement;

const isTargetFullscreen = () =>
document.fullscreenElement != null && document.fullscreenElement === toElement(target);
const fullscreenElement = signal<Element | null>(document.fullscreenElement);

const isActive = signal(isTargetFullscreen());
const isActive = computed(() => {
const current = fullscreenElement();
return current != null && current === toElement(target);
});

const enter = async (): Promise<void> => {
const el = toElement.untracked(target);
Expand All @@ -135,12 +137,14 @@ export function fullscreen(options?: FullscreenOptions): FullscreenRef {
};

setupSync(() => {
listener(document, 'fullscreenchange', () => isActive.set(isTargetFullscreen()));
listener(document, 'fullscreenchange', () =>
fullscreenElement.set(document.fullscreenElement)
);
});

return {
isSupported,
isActive: isActive.asReadonly(),
isActive,
enter,
exit,
toggle,
Expand Down
Loading