Skip to content

Commit 87e0fa1

Browse files
committed
fix: Context request event timing between slotted consumers/providers
Fixed an issue with fullscreen event handler when the tile manager and its tiles are in a Shadow DOM region.
1 parent 63548e6 commit 87e0fa1

File tree

5 files changed

+88
-37
lines changed

5 files changed

+88
-37
lines changed

src/components/common/context.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createContext } from '@lit/context';
22
import type IgcCarouselComponent from '../carousel/carousel.js';
3+
import type { addFullscreenController } from '../tile-manager/controllers/fullscreen.js';
34
import type IgcTileManagerComponent from '../tile-manager/tile-manager.js';
45
import type IgcTileComponent from '../tile-manager/tile.js';
56

@@ -12,8 +13,8 @@ export type TileManagerContext = {
1213
export type TileContext = {
1314
/** The igc-tile component instance. */
1415
instance: IgcTileComponent;
15-
/** Sets the current fullscreen state of the igc-tile element. */
16-
setFullscreenState: (fullscreen: boolean) => unknown;
16+
/** The fullscreen controller of the igc-tile instance. */
17+
fullscreenController: ReturnType<typeof addFullscreenController>;
1718
};
1819

1920
const carouselContext = createContext<IgcCarouselComponent>(
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { type Context, ContextConsumer, type ContextType } from '@lit/context';
2+
import type {
3+
LitElement,
4+
ReactiveController,
5+
ReactiveControllerHost,
6+
} from 'lit';
7+
8+
type AsyncContextOptions<T extends Context<unknown, unknown>> = {
9+
context: T;
10+
subscribe?: boolean;
11+
};
12+
13+
/* blazorSuppress */
14+
export class AsyncContextConsumer<
15+
T extends Context<unknown, unknown>,
16+
Host extends ReactiveControllerHost & HTMLElement,
17+
> implements ReactiveController
18+
{
19+
protected _host: Host;
20+
protected _options: AsyncContextOptions<T>;
21+
protected _consumer?: ContextConsumer<T, Host>;
22+
23+
constructor(host: Host, options: AsyncContextOptions<T>) {
24+
this._host = host;
25+
this._options = options;
26+
27+
this._host.addController(this);
28+
}
29+
30+
public get value(): ContextType<T> | undefined {
31+
return this._consumer?.value;
32+
}
33+
34+
public async hostConnected(): Promise<void> {
35+
await this._host.updateComplete;
36+
37+
// If there is already an instance of a consumer (because of an attach/detach cycle),
38+
// skip creating a new instance for this host.
39+
if (!this._consumer) {
40+
this._consumer = new ContextConsumer(this._host, {
41+
context: this._options.context,
42+
subscribe: this._options.subscribe,
43+
});
44+
}
45+
}
46+
}
47+
48+
export function createAsyncContext<
49+
T extends Context<unknown, unknown>,
50+
Host extends ReactiveControllerHost & LitElement,
51+
>(host: Host, context: T): AsyncContextConsumer<T, Host> {
52+
return new AsyncContextConsumer(host, {
53+
context,
54+
subscribe: true,
55+
}) as AsyncContextConsumer<T, Host>;
56+
}

src/components/tile-manager/controllers/fullscreen.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ class FullscreenController implements ReactiveController {
6565

6666
/** @internal */
6767
public handleEvent(): void {
68-
const isFullscreen = document.fullscreenElement === this._host;
69-
if (!isFullscreen && this._fullscreen) {
68+
if (!document.fullscreenElement && this._fullscreen) {
7069
this.setState(false);
7170
}
7271
}

src/components/tile-manager/tile-header.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { consume } from '@lit/context';
21
import { LitElement, html, nothing } from 'lit';
32
import { themes } from '../../theming/theming-decorator.js';
43
import IgcIconButtonComponent from '../button/icon-button.js';
5-
import { type TileContext, tileContext } from '../common/context.js';
4+
import { tileContext } from '../common/context.js';
5+
import { createAsyncContext } from '../common/controllers/async-consumer.js';
66
import { registerComponent } from '../common/definitions/register.js';
77
import IgcDividerComponent from '../divider/divider.js';
88
import { all } from './themes/header.js';
@@ -33,11 +33,10 @@ export default class IgcTileHeaderComponent extends LitElement {
3333
);
3434
}
3535

36-
@consume({ context: tileContext, subscribe: true })
37-
private _tileContext?: TileContext;
36+
private _context = createAsyncContext(this, tileContext);
3837

3938
private get _tile() {
40-
return this._tileContext?.instance;
39+
return this._context?.value?.instance;
4140
}
4241

4342
private get _isMaximized() {
@@ -55,8 +54,8 @@ export default class IgcTileHeaderComponent extends LitElement {
5554
}
5655

5756
private handleFullscreen() {
58-
if (this._tileContext) {
59-
this._tileContext.setFullscreenState(!this._isFullscreen);
57+
if (this._context) {
58+
this._context.value!.fullscreenController.setState(!this._isFullscreen);
6059
}
6160
}
6261

src/components/tile-manager/tile.ts

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { ContextProvider, consume } from '@lit/context';
1+
import { ContextProvider } from '@lit/context';
22
import { LitElement, type PropertyValues, html } from 'lit';
33
import { property, query, state } from 'lit/decorators.js';
44
import { styleMap } from 'lit/directives/style-map.js';
55
import { themes } from '../../theming/theming-decorator.js';
66
import {
77
type TileContext,
8-
type TileManagerContext,
98
tileContext,
109
tileManagerContext,
1110
} from '../common/context.js';
11+
import { createAsyncContext } from '../common/controllers/async-consumer.js';
1212
import { registerComponent } from '../common/definitions/register.js';
1313
import type { Constructor } from '../common/mixins/constructor.js';
1414
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
@@ -113,14 +113,16 @@ export default class IgcTileComponent extends EventEmitterMixin<
113113

114114
// Tile manager context properties and helpers
115115

116-
@consume({ context: tileManagerContext, subscribe: true })
117-
private _managerContext?: TileManagerContext;
116+
private _managerContext = createAsyncContext(this, tileManagerContext);
117+
118+
private get _tileManager() {
119+
return this._managerContext.value;
120+
}
118121

119122
private _createContext(): TileContext {
120123
return {
121124
instance: this,
122-
setFullscreenState: (fullscreen) =>
123-
this._fullscreenController.setState(fullscreen),
125+
fullscreenController: this._fullscreenController,
124126
};
125127
}
126128

@@ -134,12 +136,12 @@ export default class IgcTileComponent extends EventEmitterMixin<
134136
}
135137

136138
private get _draggedItem(): IgcTileComponent | null {
137-
return this._managerContext?.draggedItem ?? null;
139+
return this._tileManager?.draggedItem ?? null;
138140
}
139141

140142
private get _isSlideMode(): boolean {
141-
return this._managerContext
142-
? this._managerContext.instance.dragMode === 'slide'
143+
return this._tileManager
144+
? this._tileManager.instance.dragMode === 'slide'
143145
: true;
144146
}
145147

@@ -227,8 +229,8 @@ export default class IgcTileComponent extends EventEmitterMixin<
227229
this._maximized = value;
228230
this._setTileContext();
229231

230-
if (this._managerContext) {
231-
this._managerContext.instance.requestUpdate();
232+
if (this._tileManager) {
233+
this._tileManager.instance.requestUpdate();
232234
}
233235
}
234236

@@ -274,9 +276,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
274276

275277
protected get gridColumnWidth(): number {
276278
const tileManager =
277-
this._managerContext!.instance!.shadowRoot!.querySelector(
278-
"[part~='base']"
279-
)!;
279+
this._tileManager!.instance!.shadowRoot!.querySelector("[part~='base']")!;
280280

281281
const gridTemplateColumns = getComputedStyle(tileManager).getPropertyValue(
282282
'grid-template-columns'
@@ -365,26 +365,22 @@ export default class IgcTileComponent extends EventEmitterMixin<
365365
return;
366366
}
367367

368+
const tileManager = this._tileManager!;
369+
368370
if (isSameTile(this, this._draggedItem)) {
369371
this._tileContent.style.visibility = 'hidden';
370372
if (this._dragGhost) {
371373
Object.assign(this._dragGhost.style, {
372374
visibility: 'visible',
373375
});
374376
}
375-
if (this._managerContext) {
376-
this._managerContext.lastSwapTile = null;
377-
}
377+
tileManager.lastSwapTile = null;
378378
} else if (this._isSlideMode) {
379379
if (
380-
this._managerContext &&
381-
(!this._managerContext.lastSwapTile ||
382-
this.hasPointerLeftLastSwapTile(
383-
event,
384-
this._managerContext.lastSwapTile
385-
))
380+
!tileManager.lastSwapTile ||
381+
this.hasPointerLeftLastSwapTile(event, tileManager.lastSwapTile)
386382
) {
387-
this._managerContext.lastSwapTile = this;
383+
tileManager.lastSwapTile = this;
388384
swapTiles(this, this._draggedItem!);
389385
}
390386
}
@@ -435,8 +431,8 @@ export default class IgcTileComponent extends EventEmitterMixin<
435431
pointerY < rect.top ||
436432
pointerY > rect.bottom;
437433

438-
if (outsideBoundaries && this._managerContext) {
439-
this._managerContext.lastSwapTile = null;
434+
if (outsideBoundaries) {
435+
this._tileManager!.lastSwapTile = null;
440436
}
441437

442438
return outsideBoundaries;

0 commit comments

Comments
 (0)