File

src/components/sq-overlay-base/sq-overlay-base.component.ts

Description

Overlay base component for displaying side panel dialogs. Extends SqDialogCore for shared functionality.

Can be used directly in templates or opened programmatically via SqModalService.

Example :
Direct usage in template:
```html
<sq-overlay-base [open]="isOpen" direction="right" width="500px" (dialogClose)="onClose()">
  <ng-template #headerOverlay>
    <h2>Panel Title</h2>
  </ng-template>
  <p>Panel content goes here</p>
  <ng-template #footerOverlay>
    <button (click)="onClose()">Close</button>
  </ng-template>
</sq-overlay-base>
Example :
```html
Programmatic usage via service:
```typescript
const ref = this.modalService.openOverlay({
  direction: 'right',
  width: '500px',
  body: MyContentComponent,
  data: { items: [...] }
});

ref.afterClosed().subscribe(result => { console.log('Overlay closed with:', result); });

Example :

Extends

SqDialogCore

Metadata

Index

Properties
Methods
Inputs
Outputs
Accessors

Inputs

borderless
Type : boolean
Default value : false

Whether to hide borders in the overlay.

direction
Type : OverlayDirectionType
Default value : 'right'

Direction from which the overlay slides in.

height
Type : string
Default value : '300px'

Height of the overlay panel (used for top/bottom directions).

width
Type : string
Default value : '475px'

Width of the overlay panel (used for left/right directions).

backdrop
Type : "static" |
Default value : 'static'
Inherited from SqDialogCore
Defined in SqDialogCore:71

Backdrop behavior.

  • 'static': Clicking outside does not close the dialog
  • true: Clicking outside closes the dialog
bodyContent
Type : TemplateRef<any> | Type<any>
Inherited from SqDialogCore
Defined in SqDialogCore:119

Custom body content (TemplateRef or Component type). If a Component is passed, its headerTemplate and footerTemplate properties will be used.

cancelText
Type : string
Default value : 'Cancelar'
Inherited from SqDialogCore
Defined in SqDialogCore:141

Text for the cancel button in the default footer.

confirmText
Type : string
Default value : 'Confirmar'
Inherited from SqDialogCore
Defined in SqDialogCore:146

Text for the confirm button in the default footer.

contentData
Type : any
Inherited from SqDialogCore
Defined in SqDialogCore:130

Data to pass to dynamic content components.

contentOutputs
Type : literal type
Inherited from SqDialogCore
Defined in SqDialogCore:136

Output handlers for the body component (when body is a Component). Keys must match the body component's @Output() EventEmitter names.

customClass
Type : string
Default value : ''
Inherited from SqDialogCore
Defined in SqDialogCore:91

Additional CSS class(es) to apply to the dialog container.

dataTest
Type : string
Default value : 'sq-dialog'
Inherited from SqDialogCore
Defined in SqDialogCore:96

Data-test attribute for testing purposes.

dialogRef
Type : SqDialogRef<any | any>
Inherited from SqDialogCore
Defined in SqDialogCore:102

Reference to the dialog for programmatic control. Set by SqModalService when opening programmatically.

footerContent
Type : TemplateRef<any>
Inherited from SqDialogCore
Defined in SqDialogCore:125

Custom footer content (string or TemplateRef). If body is a Component with footerTemplate property, it will be used automatically.

headerContent
Type : string | TemplateRef<any>
Inherited from SqDialogCore
Defined in SqDialogCore:113

Custom header content. Can be a string (used as title) or TemplateRef. If body is a Component with headerTemplate property, it will be used automatically.

id
Type : string
Default value : `sq-dialog-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`
Inherited from SqDialogCore
Defined in SqDialogCore:59

Unique identifier for the dialog instance.

open
Type : boolean
Default value : false
Inherited from SqDialogCore
Defined in SqDialogCore:64

Controls whether the dialog is open or closed.

showCloseButton
Type : boolean
Default value : true
Inherited from SqDialogCore
Defined in SqDialogCore:76

Whether to show the close button.

showFooterInput
Type : boolean
Default value : true
Inherited from SqDialogCore
Defined in SqDialogCore:86

Whether to show the footer section.

showHeaderInput
Type : boolean
Default value : true
Inherited from SqDialogCore
Defined in SqDialogCore:81

Whether to show the header section.

Outputs

dialogClose
Type : EventEmitter
Inherited from SqDialogCore
Defined in SqDialogCore:155

Emitted when the dialog is closed.

Methods

getDialogClasses
getDialogClasses()
Inherited from SqDialogCore
Defined in SqDialogCore:96

Get CSS classes for the overlay container.

Returns : Record<string, boolean>

Object with CSS class names as keys and boolean values

getModalDialogClasses
getModalDialogClasses()
Inherited from SqDialogCore
Defined in SqDialogCore:111

Get CSS classes for the modal-dialog element.

Returns : Record<string, boolean>

Object with CSS class names as keys and boolean values

Private injectWidthCss
injectWidthCss()

Inject dynamic CSS for overlay size based on direction. For left/right: applies width For top/bottom: applies height

Returns : void
Private isHorizontalDirection
isHorizontalDirection()

Check if the overlay direction is horizontal (left or right).

Returns : boolean

True if direction is left or right

Protected onDialogClose
onDialogClose()
Inherited from SqDialogCore
Defined in SqDialogCore:149

Hook called when the overlay closes.

Returns : void
Protected onDialogOpen
onDialogOpen()
Inherited from SqDialogCore
Defined in SqDialogCore:132

Hook called when the overlay opens.

Returns : void
Private removeWidthCss
removeWidthCss()

Remove the dynamic CSS for overlay size.

Returns : void
applyContentDataToBody
applyContentDataToBody()
Inherited from SqDialogCore
Defined in SqDialogCore:615

Re-applies contentData to the body component's inputs. Usa setInput para que ngOnChanges seja disparado no body.

Returns : void
closeDialog
closeDialog()
Inherited from SqDialogCore
Defined in SqDialogCore:404

Programmatically close the dialog. Used by SqModalService.

Returns : void
Protected destroyContentComponents
destroyContentComponents()
Inherited from SqDialogCore
Defined in SqDialogCore:633

Destroy all dynamically created content components.

Returns : void
Protected extractTemplatesFromBodyComponent
extractTemplatesFromBodyComponent(componentInstance: any)
Inherited from SqDialogCore
Defined in SqDialogCore:599

Extract headerTemplate and footerTemplate from body component if they exist.

Parameters :
Name Type Optional Description
componentInstance any No
  • The body component instance
Returns : void
Protected isTemplateRef
isTemplateRef(content: TemplateRef | Type)
Inherited from SqDialogCore
Defined in SqDialogCore:648

Type guard to check if content is a TemplateRef.

Parameters :
Name Type Optional Description
content TemplateRef<any> | Type<any> No
  • The content to check
Returns : TemplateRef<any>

True if content is a TemplateRef

Async ngOnChanges
ngOnChanges(changes: SimpleChanges)
Inherited from SqDialogCore
Defined in SqDialogCore:289

Responds to input changes, opening or closing the dialog based on 'open' property.

Parameters :
Name Type Optional Description
changes SimpleChanges No
  • The changed properties
Returns : Promise<void>
ngOnDestroy
ngOnDestroy()
Inherited from SqDialogCore
Defined in SqDialogCore:302

Cleans up resources when the component is destroyed.

Returns : void
Protected observeRouter
observeRouter()
Inherited from SqDialogCore
Defined in SqDialogCore:488

Subscribe to router events to close on navigation.

Returns : void
onCancel
onCancel()
Inherited from SqDialogCore
Defined in SqDialogCore:461

Handle cancel button click in default footer. Closes without returning a result (void/undefined).

Returns : void
onClickOutside
onClickOutside()
Inherited from SqDialogCore
Defined in SqDialogCore:451

Handle click outside the dialog content.

Returns : void
onConfirm
onConfirm()
Inherited from SqDialogCore
Defined in SqDialogCore:473

Handle confirm button click in default footer. Closes returning true as confirmation.

Returns : void
Protected onKeydown
onKeydown(event: KeyboardEvent)
Inherited from SqDialogCore
Defined in SqDialogCore:506

Handle keyboard events.

Parameters :
Name Type Optional Description
event KeyboardEvent No
  • The keyboard event
Returns : void
Async openDialog
openDialog()
Inherited from SqDialogCore
Defined in SqDialogCore:344

Programmatically open the dialog. Used by SqModalService.

Returns : Promise<void>
Protected renderContent
renderContent(content: TemplateRef | Type, container: ViewContainerRef, slot: "header" | "body" | "footer")
Inherited from SqDialogCore
Defined in SqDialogCore:538

Render a single content item (TemplateRef or Component).

Parameters :
Name Type Optional Description
content TemplateRef<any> | Type<any> No
  • The content to render (TemplateRef or Component type)
container ViewContainerRef No
  • The ViewContainerRef to render into
slot "header" | "body" | "footer" No
  • The slot identifier ('header', 'body', or 'footer')
Returns : void
Protected renderDynamicContent
renderDynamicContent()
Inherited from SqDialogCore
Defined in SqDialogCore:524

Render dynamic content (TemplateRef or Component) into containers. Only body supports Component type. Header and footer support only string/TemplateRef. If body is a Component with headerTemplate/footerTemplate properties, they will be extracted.

Returns : void

Properties

finishOpening
Default value : false

Whether the overlay has finished opening animation.

Optional footerTemplate
Type : TemplateRef<ElementRef>
Decorators :
@ContentChild('footerOverlay')
Inherited from SqDialogCore
Defined in SqDialogCore:84

Footer template from content projection.

Optional headerTemplate
Type : TemplateRef<ElementRef>
Decorators :
@ContentChild('headerOverlay')
Inherited from SqDialogCore
Defined in SqDialogCore:79

Header template from content projection.

Protected styleId
Default value : `overlay-style-${Date.now()}-${Math.random().toString(36).substring(7)}`
Inherited from SqDialogCore
Defined in SqDialogCore:54

Unique style ID for dynamic CSS injection.

Protected Optional bodyComponentFooterTemplate
Type : TemplateRef<any>
Inherited from SqDialogCore
Defined in SqDialogCore:203

Footer template extracted from body component (if any).

Protected Optional bodyComponentHeaderTemplate
Type : TemplateRef<any>
Inherited from SqDialogCore
Defined in SqDialogCore:198

Header template extracted from body component (if any).

Optional bodyContainer
Type : ViewContainerRef
Decorators :
@ViewChild('bodyContainer', {read: ViewContainerRef})
Inherited from SqDialogCore
Defined in SqDialogCore:169

Container for dynamic body content.

Protected contentComponentRefs
Type : literal type
Default value : {}
Inherited from SqDialogCore
Defined in SqDialogCore:241

References to dynamically created content components.

Protected contentOutputSubscriptions
Type : Subscription[]
Default value : []
Inherited from SqDialogCore
Defined in SqDialogCore:250

Subscriptions to body component outputs (for cleanup on destroy).

Optional dialogElement
Type : ElementRef<HTMLElement>
Decorators :
@ViewChild('dialogElement')
Inherited from SqDialogCore
Defined in SqDialogCore:164

Reference to the dialog container element.

Protected document
Default value : inject(DOCUMENT)
Inherited from SqDialogCore
Defined in SqDialogCore:40

Reference to the document object.

Protected getWindow
Default value : inject(GetWindow)
Inherited from SqDialogCore
Defined in SqDialogCore:50

Window helper service for browser APIs.

hasFooter
Default value : false
Inherited from SqDialogCore
Defined in SqDialogCore:193

Whether the dialog has footer content.

hasHeader
Default value : false
Inherited from SqDialogCore
Defined in SqDialogCore:188

Whether the dialog has header content.

Protected isClosing
Default value : false
Inherited from SqDialogCore
Defined in SqDialogCore:223

Flag to prevent duplicate close operations.

Protected localized
Type : URL
Inherited from SqDialogCore
Defined in SqDialogCore:213

URL where the dialog was opened (for route change detection).

managedByService
Default value : false
Inherited from SqDialogCore
Defined in SqDialogCore:230

Whether this dialog is managed by SqModalService. When true, DOM cleanup is handled by the service.

Protected modalNumber
Type : number
Default value : 0
Inherited from SqDialogCore
Defined in SqDialogCore:183

Number of open modals (for stacking z-index).

Protected Optional modals
Type : HTMLCollectionOf<Element>
Inherited from SqDialogCore
Defined in SqDialogCore:178

Collection of open modal elements in the DOM.

Protected router
Default value : inject(Router)
Inherited from SqDialogCore
Defined in SqDialogCore:45

Angular Router for navigation events.

Protected Optional routerSubscription
Type : Subscription
Inherited from SqDialogCore
Defined in SqDialogCore:218

Subscription to router events.

Protected Optional scrollY
Type : number
Inherited from SqDialogCore
Defined in SqDialogCore:208

Original scroll position to restore on close.

Accessors

showFooter
getshowFooter()

Override showFooter for overlay - only show when template or component is provided. Unlike modal, overlay does not have default footer buttons.

Returns : boolean
import { NgClass, NgTemplateOutlet } from '@angular/common';
import { Component, ContentChild, ElementRef, Input, TemplateRef } from '@angular/core';
import { SqDialogCore } from '../../classes/sq-dialog-core.class';
import { SqClickOutsideDirective } from '../../directives/sq-click-outside/sq-click-outside.directive';
import { SqDataTestDirective } from '../../directives/sq-data-test/sq-data-test.directive';
import { OverlayDirectionType } from '../../interfaces/modal.interface';

/**
 * Overlay base component for displaying side panel dialogs.
 * Extends SqDialogCore for shared functionality.
 *
 * Can be used directly in templates or opened programmatically via SqModalService.
 *
 * @example
 * Direct usage in template:
 * ```html
 * <sq-overlay-base [open]="isOpen" direction="right" width="500px" (dialogClose)="onClose()">
 *   <ng-template #headerOverlay>
 *     <h2>Panel Title</h2>
 *   </ng-template>
 *   <p>Panel content goes here</p>
 *   <ng-template #footerOverlay>
 *     <button (click)="onClose()">Close</button>
 *   </ng-template>
 * </sq-overlay-base>
 * ```
 *
 * @example
 * Programmatic usage via service:
 * ```typescript
 * const ref = this.modalService.openOverlay({
 *   direction: 'right',
 *   width: '500px',
 *   body: MyContentComponent,
 *   data: { items: [...] }
 * });
 *
 * ref.afterClosed().subscribe(result => {
 *   console.log('Overlay closed with:', result);
 * });
 * ```
 */
@Component({
  selector: 'sq-overlay-base',
  templateUrl: './sq-overlay-base.component.html',
  styleUrls: ['./sq-overlay-base.component.scss'],
  standalone: true,
  imports: [NgClass, NgTemplateOutlet, SqClickOutsideDirective, SqDataTestDirective],
})
export class SqOverlayBaseComponent extends SqDialogCore {
  /**
   * Unique style ID for dynamic CSS injection.
   */
  protected override styleId = `overlay-style-${Date.now()}-${Math.random().toString(36).substring(7)}`;

  /**
   * Direction from which the overlay slides in.
   */
  @Input() direction: OverlayDirectionType = 'right';

  /**
   * Width of the overlay panel (used for left/right directions).
   */
  @Input() width = '475px';

  /**
   * Height of the overlay panel (used for top/bottom directions).
   */
  @Input() height = '300px';

  /**
   * Whether to hide borders in the overlay.
   */
  @Input() borderless = false;

  /**
   * Header template from content projection.
   */
  @ContentChild('headerOverlay') override headerTemplate?: TemplateRef<ElementRef>;

  /**
   * Footer template from content projection.
   */
  @ContentChild('footerOverlay') override footerTemplate?: TemplateRef<ElementRef>;

  /**
   * Whether the overlay has finished opening animation.
   */
  finishOpening = false;

  /**
   * Get CSS classes for the overlay container.
   *
   * @returns Object with CSS class names as keys and boolean values
   */
  override getDialogClasses(): Record<string, boolean> {
    return {
      modal: true,
      overlay: true,
      open: this.open,
      [this.direction]: true,
      [this.customClass]: !!this.customClass,
    };
  }

  /**
   * Get CSS classes for the modal-dialog element.
   *
   * @returns Object with CSS class names as keys and boolean values
   */
  override getModalDialogClasses(): Record<string, boolean> {
    return {
      'modal-dialog': true,
      [this.direction]: true,
      opened: this.finishOpening,
    };
  }

  /**
   * Override showFooter for overlay - only show when template or component is provided.
   * Unlike modal, overlay does not have default footer buttons.
   *
   * @returns True if footer should be displayed
   */
  override get showFooter(): boolean {
    return !!this.effectiveFooterTemplate || this.hasFooterComponent;
  }

  /**
   * Hook called when the overlay opens.
   */
  protected override onDialogOpen(): void {
    // Update header/footer detection for content-projected templates
    this.hasHeader = this.showHeader;
    this.hasFooter = this.showFooter;

    // Inject dynamic width CSS
    this.injectWidthCss();

    // Set finish opening flag after a short delay for animation
    setTimeout(() => {
      this.finishOpening = true;
    }, 10);
  }

  /**
   * Hook called when the overlay closes.
   */
  protected override onDialogClose(): void {
    this.finishOpening = false;
    this.removeWidthCss();
  }

  /**
   * Check if the overlay direction is horizontal (left or right).
   *
   * @returns True if direction is left or right
   */
  private isHorizontalDirection(): boolean {
    return this.direction === 'left' || this.direction === 'right';
  }

  /**
   * Inject dynamic CSS for overlay size based on direction.
   * For left/right: applies width
   * For top/bottom: applies height
   */
  private injectWidthCss(): void {
    const isHorizontal = this.isHorizontalDirection();
    const sizeProperty = isHorizontal ? 'width' : 'height';
    const sizeValue = isHorizontal ? this.width : this.height;

    // Use specific selector with direction class to override the width: 0 / height: 0
    const css = `
      .overlay.open .modal-dialog.${this.direction}.opened {
        ${sizeProperty}: ${sizeValue} !important;
      }
    `;
    const head = this.document.getElementsByTagName('head')[0];
    let style = this.document.getElementById(this.styleId);

    if (!style) {
      style = this.document.createElement('style');
      style.setAttribute('id', this.styleId);
      style.appendChild(this.document.createTextNode(css));
      head.appendChild(style);
    } else {
      style.innerHTML = '';
      style.appendChild(this.document.createTextNode(css));
    }
  }

  /**
   * Remove the dynamic CSS for overlay size.
   */
  private removeWidthCss(): void {
    const style = this.document.getElementById(this.styleId);
    if (style?.parentNode) {
      style.parentNode.removeChild(style);
    }
  }
}
<div [id]="id" [ngClass]="getDialogClasses()" #dialogElement [dataTest]="dataTest">
  <div
    class="modal-dialog"
    [ngClass]="getModalDialogClasses()"
    [clickOutsideEnabled]="!!(open && backdrop !== 'static' && dialogElement)"
    (clickOutside)="onClickOutside()"
  >
    <div [dataTest]="dataTest + '-content'" class="modal-content scrollbar">
      <!-- Header -->
      @if (showHeader) {
        <div
          [dataTest]="dataTest + '-header'"
          class="modal-header"
          [ngClass]="{
            borderless: borderless,
          }"
        >
          <!-- String header (title) -->
          @if (headerTitle) {
            <h3 class="modal-title">{{ headerTitle }}</h3>
          }

          <!-- Template header (from content projection, input, or body component) -->
          @if (effectiveHeaderTemplate) {
            <ng-container *ngTemplateOutlet="effectiveHeaderTemplate; context: templateContext"></ng-container>
          }

          <!-- Close button -->
          @if (showCloseButton) {
            <button type="button" class="close button-close" aria-label="Close" (click)="dialogClose.emit()">
              <i class="fa-solid fa-xmark"></i>
            </button>
          }
        </div>
      }

      <!-- Body -->
      <div [dataTest]="dataTest + '-body'" class="modal-body scrollbar" [ngClass]="{ 'without-footer': !showFooter }">
        <!-- Template body (from input) -->
        @if (bodyTemplate) {
          <ng-container *ngTemplateOutlet="bodyTemplate; context: templateContext"></ng-container>
        }

        <!-- Component body (dynamic) -->
        @if (hasBodyComponent) {
          <ng-container #bodyContainer></ng-container>
        }

        <!-- Content projection (default slot) -->
        <ng-content></ng-content>
      </div>

      <!-- Footer -->
      @if (showFooter) {
        <div [dataTest]="dataTest + '-footer'" class="modal-footer" [ngClass]="{ borderless: borderless }">
          <!-- Template footer (from content projection, input, or body component) -->
          @if (effectiveFooterTemplate) {
            <ng-container *ngTemplateOutlet="effectiveFooterTemplate; context: templateContext"></ng-container>
          }
        </div>
      }
    </div>
  </div>
</div>

./sq-overlay-base.component.scss

:host {
  display: contents;
}

.overlay {
  padding: 0;
  border-radius: 0;

  &.left {
    justify-content: flex-start;
  }

  &.right {
    justify-content: flex-end;
  }

  &.top {
    align-items: flex-start;
  }

  &.bottom {
    align-items: flex-end;
  }

  .modal-dialog {
    margin: 0;
    position: fixed;
    max-width: 100dvw;
    max-height: 100dvh;
    transition:
      width 0.3s ease-out,
      height 0.3s ease-out !important;
    transform: translate3d(0, 0, 0) !important;

    &.left,
    &.right {
      height: 100%;
      width: 0;
      top: 0;

      // On mobile, lateral overlays expand to full or near-full width
      @media (max-width: 576px) {
        &.opened {
          width: 100dvw !important;
        }
      }
    }

    &.left {
      left: 0;
    }

    &.right {
      right: 0;
    }

    &.top,
    &.bottom {
      width: 100%;
      height: 0;
      left: 0;
    }

    &.top {
      top: 0;
    }

    &.bottom {
      bottom: 0;
    }

    .modal-content {
      height: 100%;
      border-radius: 0;
      border: none;
    }
  }
}

// Header adjustments for overlay
.modal-header {
  min-height: 60px;
  display: flex;

  h3.modal-title {
    margin: 0;
    font-size: 1.25rem;

    // Slightly smaller title on mobile
    @media (max-width: 576px) {
      font-size: 1.125rem;
    }
  }

  .button-close {
    position: unset;
    height: 30px;
    line-height: 30px;
    opacity: .7;
  }

  &.without-header {
    border-bottom: none;
  }

  // Reduce padding on mobile
  @media (max-width: 576px) {
    min-height: 50px;
    padding: 0.75rem 1rem;
  }
}

// Body scroll behavior
.modal-body {
  flex: 1;
  overflow-y: auto;

  &.without-footer {
    height: calc(100dvh - 60px);
  }

  // Adjust padding on mobile
  @media (max-width: 576px) {
    padding: 0.75rem 1rem;

    &.without-footer {
      height: calc(100dvh - 50px);
    }
  }
}

// Footer adjustments
.modal-footer {
  min-height: 60px;

  // Reduce padding on mobile
  @media (max-width: 576px) {
    min-height: 50px;
    padding: 0.5rem 1rem;
    gap: 0.5rem;
  }

  // Stack buttons on very small screens
  @media (max-width: 360px) {
    flex-direction: column;
    width: 100%;

    sq-button,
    button {
      width: 100%;
    }
  }
}

// Borderless variant
.borderless {
  border-color: transparent;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""