Skip to content

Commit e0e2eac

Browse files
committed
feat(resize): add slots for resize adorners
1 parent 247430d commit e0e2eac

File tree

4 files changed

+124
-3
lines changed

4 files changed

+124
-3
lines changed

src/components/resize-container/resize-container.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export interface IgcResizeContainerComponentEventMap {
3232
* @element igc-resize
3333
*
3434
* @slot - renders the element(s) that should be resized
35+
* @slot side-adorner - renders the side resize handle.
36+
* @slot corner-adorner - renders the corner resize handle.
37+
* @slot bottom-adorner - renders the bottom resize handle.
38+
*
3539
*/
3640
@themes(all)
3741
export default class IgcResizeContainerComponent extends EventEmitterMixin<
@@ -196,9 +200,21 @@ export default class IgcResizeContainerComponent extends EventEmitterMixin<
196200
}
197201

198202
return html`
199-
<div ${ref(this._adorners.side)} part="trigger-side"></div>
200-
<div ${ref(this._adorners.corner)} part="trigger"></div>
201-
<div ${ref(this._adorners.bottom)} part="trigger-bottom"></div>
203+
<slot
204+
${ref(this._adorners.side)}
205+
part="trigger-side"
206+
name="side-adorner"
207+
></slot>
208+
<slot
209+
${ref(this._adorners.corner)}
210+
part="trigger"
211+
name="corner-adorner"
212+
></slot>
213+
<slot
214+
${ref(this._adorners.bottom)}
215+
part="trigger-bottom"
216+
name="bottom-adorner"
217+
></slot>
202218
`;
203219
}
204220

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,55 @@ describe('Tile Manager component', () => {
418418
});
419419
});
420420

421+
describe('Resize adorners slot assignment', () => {
422+
beforeEach(async () => {
423+
tileManager = await fixture<IgcTileManagerComponent>(html`
424+
<igc-tile-manager resize-mode="always">
425+
<igc-tile>
426+
<div slot="side-adorner">Side Adorner</div>
427+
<div slot="corner-adorner">Corner Adorner</div>
428+
<div slot="bottom-adorner">Bottom Adorner</div>
429+
</igc-tile>
430+
<igc-tile id="tile2"></igc-tile>
431+
</igc-tile-manager>
432+
`);
433+
});
434+
435+
it('should correctly project adorners into the igc-resize component and not render in igc-tile', async () => {
436+
const tile1 = tileManager.tiles[0];
437+
const resize = tile1.shadowRoot?.querySelector(
438+
'igc-resize'
439+
) as HTMLElement;
440+
441+
const sideAdorner = resize.querySelector('[slot="side-adorner"]');
442+
const cornerAdorner = resize.querySelector('[slot="corner-adorner"]');
443+
const bottomAdorner = resize.querySelector('[slot="bottom-adorner"]');
444+
445+
// Assert the adorners are projected inside igc-resize
446+
expect(sideAdorner?.textContent).to.equal('Side Adorner');
447+
expect(cornerAdorner?.textContent).to.equal('Corner Adorner');
448+
expect(bottomAdorner?.textContent).to.equal('Bottom Adorner');
449+
450+
// Assert that the adorners are not rendered in the igc-tile itself
451+
expect(tile1.querySelector('[slot="side-adorner"]')).to.be.null;
452+
expect(tile1.querySelector('[slot="corner-adorner"]')).to.be.null;
453+
expect(tile1.querySelector('[slot="bottom-adorner"]')).to.be.null;
454+
});
455+
456+
it('should hide igc-resize component and not render adorners in igc-tile when resize mode is "none"', async () => {
457+
const tile = tileManager.tiles[0];
458+
459+
tileManager.resizeMode = 'none';
460+
await elementUpdated(tileManager);
461+
462+
const resize = tile.shadowRoot?.querySelector('igc-resize');
463+
const adorners = tile.shadowRoot?.querySelectorAll('[slot$="-adorner"]');
464+
465+
expect(resize).to.be.null;
466+
expect(adorners).lengthOf(0);
467+
});
468+
});
469+
421470
describe('Tile state change behavior', () => {
422471
let tile: any;
423472

src/components/tile-manager/tile.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ export interface IgcTileComponentEventMap {
7070
* @slot fullscreen-action - Renders the fullscreen action element.
7171
* @slot actions - Renders items after the default actions.
7272
* @slot Default slot for the tile's content.
73+
* @slot side-adorner - Renders the side resize handle.
74+
* @slot corner-adorner - Renders the corner resize handle.
75+
* @slot bottom-adorner - Renders the bottom resize handle.
7376
*
7477
* @csspart base - The wrapper for the entire tile.
7578
* @csspart header - The container for the header, including title and actions.
@@ -118,6 +121,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
118121
private _position = -1;
119122
private _resizeState = createTileResizeState();
120123
private _dragStack = createTileDragStack();
124+
private _mutationObserver: MutationObserver | null = null;
121125

122126
// Tile manager context properties and helpers
123127

@@ -348,6 +352,42 @@ export default class IgcTileComponent extends EventEmitterMixin<
348352

349353
this.style.viewTransitionName =
350354
this.style.viewTransitionName || `tile-transition-${this.tileId}`;
355+
356+
this._observeResizeElement();
357+
}
358+
359+
/** @internal */
360+
public override disconnectedCallback() {
361+
super.disconnectedCallback();
362+
this._mutationObserver?.disconnect();
363+
}
364+
365+
private _observeResizeElement() {
366+
if (!this.shadowRoot) return;
367+
368+
this._mutationObserver = new MutationObserver(() => {
369+
const resize = this.shadowRoot?.querySelector('igc-resize');
370+
if (resize) {
371+
this._assignAdornersToResize();
372+
}
373+
});
374+
375+
this._mutationObserver.observe(this.shadowRoot, {
376+
childList: true, // Detects when `igc-resize` is added or removed
377+
subtree: true,
378+
});
379+
}
380+
381+
private _assignAdornersToResize() {
382+
const resize = this.shadowRoot?.querySelector('igc-resize');
383+
if (!resize) return;
384+
385+
['side-adorner', 'corner-adorner', 'bottom-adorner'].forEach((slotName) => {
386+
const assigned = this.querySelector(`[slot="${slotName}"]`);
387+
if (assigned) {
388+
resize.appendChild(assigned); // Move the adorner inside `igc-resize`
389+
}
390+
});
351391
}
352392

353393
private _setDragState(state = true) {
@@ -594,6 +634,18 @@ export default class IgcTileComponent extends EventEmitterMixin<
594634
@igcResizeCancel=${this._handleResizeCancel}
595635
>
596636
${this._renderContent()}
637+
<slot
638+
name="side-adorner"
639+
@slotchange=${this._assignAdornersToResize}
640+
></slot>
641+
<slot
642+
name="corner-adorner"
643+
@slotchange=${this._assignAdornersToResize}
644+
></slot>
645+
<slot
646+
name="bottom-adorner"
647+
@slotchange=${this._assignAdornersToResize}
648+
></slot>
597649
</igc-resize>
598650
`;
599651
}

stories/tile-manager.stories.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,10 @@ export const CustomActions: Story = {
858858
>
859859
<igc-tile disable-fullscreen disable-maximize>
860860
<h3 slot="title">Custom Actions</h3>
861+
<!-- <div slot="side-adorner">🟨</div> -->
862+
<div slot="corner-adorner">🟩</div>
863+
<div slot="bottom-adorner">🟦</div>
864+
861865
<igc-icon-button
862866
slot="actions"
863867
variant="flat"

0 commit comments

Comments
 (0)