taiga-family / taiga-ui

Angular UI Kit and components library for awesome people
https://taiga-ui.dev
Apache License 2.0
3.29k stars 463 forks source link

šŸš€ - Improve portals tech #950

Closed EricPoul closed 2 years ago

EricPoul commented 3 years ago

Which @taiga-ui/* package(s) are relevant/releated to the feature request?

No response

Description

There is a case when I'd like to open a dialog or show a notification while the sidebar or my custom portal is opened. Right now I can do this but tui-dialog-host and tui-notifications-host are placed in tui-portal-host so that sidebar(custom portals) will be created over any dialogs or notifications.

Suggestion:

export class TuiPortalService {
    add<C>(componentFactory: ComponentFactory<C>, injector: Injector, innerContent: boolean = false): ComponentRef<C> {
        return this.safeHost.addComponentChild(componentFactory, injector, innerContent);
    }

    addTemplate<C>(templateRef: TemplateRef<C>, context?: C, innerContent: boolean = false): EmbeddedViewRef<C> {
        return this.safeHost.addTemplateChild(templateRef, context, innerContent);
    }
}

Add an ng-container after ng-content so we can add custom portals over main content but under dialogs, etc.

<!-- TuiPortalHostComponent template -->
<div #positionFixedOffset class="position-fixed-offset"></div>

<!-- maybe it makes sense for z-index -->
<div class="content">
  <ng-content></ng-content>
</div>

<div class="portals">
  <ng-container #vcr></ng-container>
</div>

read ng-container as ViewContainerRef; pass third(optional) argument to the addComponentChild;

export class TuiPortalHostComponent {
   @ViewChild('vcr', { read: ViewContainerRef }) vcr: ViewContainerRef;

   addComponentChild(
       componentFactory: ComponentFactory<MguiSidePanelComponent>,
       injector: Injector,
       innerContent: boolean = false,
   ): ComponentRef<MguiSidePanelComponent> {
       return this.getViewContainerRef(innerContent).createComponent<MguiSidePanelComponent>(
       ...
       );
   }

   addTemplateChild<C>(templateRef: TemplateRef<C>, context?: C, innerContent: boolean = false): EmbeddedViewRef<C> {
        return this.getViewContainerRef(innerContent).createEmbeddedView(templateRef, context);
   }

   private getViewContainerRef(innerContent: boolean): ViewContainerRef {
       return innerContent ? this.vcr : this.viewContainerRef;
   }
}
<!-- TuiRootComponent template -->
<tui-scroll-controls
    *ngIf="scrollbars$ | async"
    class="scrollbar"
></tui-scroll-controls>
<tui-svg-defs-host></tui-svg-defs-host>
<tui-portal-host>
    <div class="content">
        <ng-content></ng-content>

       <!-- Portals with passed `innerContent` will be created here -->
    </div>
    <ng-content select="tuiOverContent"></ng-content>
    <tui-dialog-host *ngIf="dialogs && dialogs.length"></tui-dialog-host>
    <ng-content select="tuiOverDialogs"></ng-content>
    <ng-container
        *ngIf="notificationsHost"
        [ngComponentOutlet]="notificationsHost"
    ></ng-container>
    <ng-content select="tuiOverNotifications"></ng-content>
</tui-portal-host>
<ng-content select="tuiOverPortals"></ng-content>
<tui-hints-host></tui-hints-host>
<ng-content select="tuiOverHints"></ng-content>
waterplea commented 3 years ago

I think it makes sense for sidebars to be below dialogs. Looks like another host is a reasonable solution. Perhaps we need to deprecate current sidebars and make a new implementation with this and customization in mind: https://github.com/TinkoffCreditSystems/taiga-ui/issues/553

EricPoul commented 3 years ago

I'd suggest keeping portal-host for sidebars or custom(my own) similar stuff that creates children below dialogs and hosted-dropdown-host that creates hosted-dropdowns above dialogs and notifications and below tooltips. And it's a little strange that host creates children, not inside its template but after itself. It's just thoughts of improvements.

*below and above from end-user perspective

waterplea commented 2 years ago

Renamed the issue to more general title. We have a low level API for portals, but we only use it for dropdowns and sidebars. Looks like a good idea would be to just add ability to make multiple portal hosts, put them inside tui-root with ngProjectAs and then use as people like, keeping dropdowns above everything else.

waterplea commented 2 years ago

Custom portal hosts are now supported: https://github.com/Tinkoff/taiga-ui/pull/1485 https://taiga-ui.dev/portals