{"version":3,"file":"proangular-ngx-scroll-top.mjs","sources":["../../../src/app/public/ngx-scroll-top.component.ts","../../../src/app/public/ngx-scroll-top.component.html","../../../src/app/public/public.ts","../../../src/app/public/proangular-ngx-scroll-top.ts"],"sourcesContent":["import {\n  Component,\n  HostBinding,\n  HostListener,\n  OnDestroy,\n  OnInit,\n  input,\n  signal,\n} from '@angular/core';\n\nconst animationMs = 200;\n\n@Component({\n  selector: 'ngx-scroll-top',\n  templateUrl: './ngx-scroll-top.component.html',\n  styleUrl: './ngx-scroll-top.component.scss',\n  styles: [\n    `\n      .fade-in {\n        animation: fadeIn ${animationMs}ms ease-in forwards;\n      }\n      .fade-out {\n        animation: fadeOut ${animationMs}ms ease-out forwards;\n      }\n      @keyframes fadeIn {\n        from {\n          opacity: 0;\n        }\n        to {\n          opacity: 1;\n        }\n      }\n      @keyframes fadeOut {\n        from {\n          opacity: 1;\n        }\n        to {\n          opacity: 0;\n        }\n      }\n    `,\n  ],\n  imports: [],\n  standalone: true,\n})\nexport class NgxScrollTopComponent implements OnInit, OnDestroy {\n  /**\n   * Background color of the back to top button (hex string).\n   *\n   * Default: `'#0D58C0'` (dark-blue)\n   *\n   * Example: `'#000'` or `'black'`\n   *\n   * Tip: Define any `'x'` css property available for `'background-color: x'`\n   */\n  public readonly backgroundColor = input('#0D58C0');\n\n  /**\n   * Offset `px` from bottom of page when scrolled to bottom. For example this\n   * can be used to make sure the back to top button never overlaps a footer.\n   *\n   * Default: `'0px'`\n   *\n   * Example: `'250px'` or `250` because my footer is 250px in height\n   */\n  public readonly bottomOffset = input<string | number>('0px');\n\n  /**\n   * The back to top button will not be displayed until the user scrolls to the\n   * provided Y (vertical `px`) coordinate on the page.\n   *\n   * Default: `'420px'`\n   *\n   * Example: `'100px'` or `100`\n   */\n  public readonly displayAtYPosition = input<string | number>('420px');\n\n  /**\n   * The font color for the nested content within the back to top button.\n   *\n   * Default: `'#FFFFFF'` (white)\n   *\n   * Example: `'#000'` or `'black'`\n   *\n   * Tip: Define any `'x'` css property available for `'color: x'`\n   */\n  public readonly fontColor = input('#FFFFFF');\n  @HostBinding('style.color') protected get hostFontColor(): string {\n    return this.fontColor();\n  }\n\n  /**\n   * The font size for the nested content within the back to top button.\n   *\n   * Default: `'16px'`\n   *\n   * Example: `'2rem'` or `'32px'`\n   *\n   * Tip: Define any `'x'` css property available for `'font-size: x'`\n   */\n  public readonly fontSize = input('16px');\n  @HostBinding('style.font-size') protected get hostFontSize(): string {\n    return this.fontSize();\n  }\n\n  /**\n   * Height of back to top button in string format.\n   *\n   * @example `'32px'` or `'2rem'`\n   */\n  public readonly height = input('40px');\n  @HostBinding('style.height') protected get hostHeight(): string {\n    return this.height();\n  }\n\n  /**\n   * Position on-screen where the back to top button is displayed.\n   *\n   * Default: `'right'`\n   */\n  public readonly position = input<'left' | 'right'>('right');\n\n  /**\n   * Width of back to top button in string format.\n   *\n   * @example `'32px'` or `'2rem'`\n   */\n  public readonly width = input('40px');\n  @HostBinding('style.width') protected get hostWidth(): string {\n    return this.width();\n  }\n\n  /**\n   * Style the `z-index` for the back to top button as needed for correct layer\n   * height adjustment. This can be useful when working with sticky headers.\n   *\n   * Default: `999`\n   */\n  public readonly zIndex = input(999);\n  @HostBinding('style.z-index') protected get hostZIndex(): number {\n    return this.zIndex();\n  }\n\n  /**\n   * Whether button should be rendered in DOM.\n   * It stays true during fade-in/fade-out, so animation runs.\n   */\n  protected shouldRenderButton = signal(false);\n\n  /**\n   * State of button animation: \"idle\", \"fading-in\", \"fading-out\".\n   */\n  protected fadeState = signal<'idle' | 'fading-in' | 'fading-out'>('idle');\n\n  /** Default padding from screen edges. */\n  private readonly defaultPadding = '16px';\n\n  /** Duration for fade-in/fade-out animations. */\n  private readonly fadeDuration = animationMs;\n\n  /** Timeout reference for fade completion. */\n  private fadeTimeout?: ReturnType<typeof setTimeout>;\n\n  protected isButtonNotRendered = signal(true);\n\n  private isDestroyed = false;\n\n  private animationFrameId?: number;\n\n  @HostBinding('style.bottom') protected styleBottom = this.defaultPadding;\n  @HostBinding('style.left') protected styleLeft = 'unset';\n  @HostBinding('style.right') protected styleRight = this.defaultPadding;\n\n  /** Scroll event listener */\n  @HostListener('window:scroll', [])\n  protected onWindowScroll(): void {\n    this.updateButtonVisibility();\n    this.updatePosition();\n  }\n\n  public ngOnInit(): void {\n    switch (this.position()) {\n      case 'left':\n        this.styleRight = 'unset';\n        this.styleLeft = this.defaultPadding;\n        break;\n      case 'right':\n      default:\n        this.styleRight = this.defaultPadding;\n        this.styleLeft = 'unset';\n        break;\n    }\n    this.updateButtonVisibility();\n    this.updatePosition();\n  }\n\n  public ngOnDestroy(): void {\n    this.isDestroyed = true;\n    if (this.fadeTimeout) {\n      clearTimeout(this.fadeTimeout);\n      this.fadeTimeout = undefined;\n    }\n    if (this.animationFrameId) {\n      cancelAnimationFrame(this.animationFrameId);\n      this.animationFrameId = undefined;\n    }\n  }\n\n  /** Scroll smoothly to the top of the window. */\n  public scrollTop(): void {\n    window.scroll({\n      top: 0,\n      left: 0,\n      behavior: 'smooth',\n    });\n  }\n\n  /** Dynamically update the button position based on scroll and offsets. */\n  private updatePosition(): void {\n    const useDefaultPosition = (): void => {\n      this.styleBottom = this.defaultPadding;\n    };\n\n    if (!this.shouldRenderButton()) {\n      useDefaultPosition();\n      return;\n    }\n\n    try {\n      const { document, scrollY } = window;\n      const { documentElement: docEl } = document;\n      const bottomY = docEl.scrollHeight - docEl.clientHeight;\n      const bottomOffset = parsePxStringToInt(this.bottomOffset());\n      const distanceFromBottom = bottomY - scrollY;\n      const halfHeight = parsePxStringToInt(this.height()) / 2;\n      const defaultPadding = parsePxStringToInt(this.defaultPadding);\n\n      if (distanceFromBottom + (halfHeight - defaultPadding) < bottomOffset) {\n        // Scroll up button exceeded bottom offset, update position.\n        this.styleBottom = `${\n          bottomOffset - distanceFromBottom + defaultPadding\n        }px`;\n      } else {\n        useDefaultPosition();\n      }\n    } catch (error: unknown) {\n      console.warn(\n        'Failed to update back to top button position dynamically.',\n        error,\n      );\n      useDefaultPosition();\n    }\n  }\n\n  /**\n   * Controls when to show/hide the button and triggers CSS animation.\n   * This version guarantees fade-in/fade-out animation runs using signals.\n   */\n  private updateButtonVisibility(): void {\n    const { scrollY } = window;\n    const displayAtYPosition = parsePxStringToInt(this.displayAtYPosition());\n\n    // Fade in\n    if (!this.shouldRenderButton() && scrollY > displayAtYPosition) {\n      // Cancel any pending fade-out\n      if (this.fadeTimeout) {\n        clearTimeout(this.fadeTimeout);\n        this.fadeTimeout = undefined;\n      }\n      this.fadeState.set('idle'); // Reset fade state\n      this.shouldRenderButton.set(true);\n\n      // Cancel previous animation frame if exists\n      if (this.animationFrameId) {\n        cancelAnimationFrame(this.animationFrameId);\n      }\n\n      // Schedule and track new animation frame\n      const animationFrameId = requestAnimationFrame(() => {\n        if (this.isDestroyed) return;\n        this.fadeState.set('fading-in');\n        this.isButtonNotRendered.set(false);\n        this.animationFrameId = undefined;\n      });\n\n      this.animationFrameId = animationFrameId;\n    }\n\n    // Fade out\n    else if (this.shouldRenderButton() && scrollY <= displayAtYPosition) {\n      this.fadeState.set('fading-out');\n      if (this.fadeTimeout) clearTimeout(this.fadeTimeout);\n      this.fadeTimeout = setTimeout(() => {\n        if (this.isDestroyed) return;\n        this.shouldRenderButton.set(false);\n        this.fadeState.set('idle');\n      }, this.fadeDuration);\n    }\n  }\n}\n\n/**\n * Convert a px string or number into a number, handles conversion safely.\n *\n * @param value - string/number that could be '40px', '16', 32, etc.\n * @returns parsed integer value.\n */\nfunction parsePxStringToInt(value: string | number): number {\n  try {\n    return parseInt(value.toString(), 10);\n  } catch (error: unknown) {\n    throw new Error(\n      `Failed to parse value \"${value}\" with error: ${String(error)}`,\n    );\n  }\n}\n","@if (shouldRenderButton()) {\n  <button\n    (click)=\"scrollTop()\"\n    [class.fade-in]=\"fadeState() === 'fading-in'\"\n    [class.fade-out]=\"fadeState() === 'fading-out'\"\n    [class.not-rendered]=\"isButtonNotRendered()\"\n    [style.background-color]=\"backgroundColor()\"\n    aria-label=\"Scroll to top\"\n  >\n    <div class=\"content\" #content>\n      <ng-content></ng-content>\n    </div>\n    @if (!content?.innerHTML?.length) {\n      <svg\n        aria-hidden=\"true\"\n        class=\"default-content\"\n        fill=\"currentColor\"\n        focusable=\"false\"\n        viewBox=\"0 0 24 24\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <desc>\n          https://fonts.gstatic.com/s/i/materialicons/expand_less/v12/24px.svg\n        </desc>\n        <path d=\"M0 0h24v24H0z\" fill=\"none\" />\n        <path d=\"M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z\" />\n      </svg>\n    }\n  </button>\n}\n","// Components\nexport { NgxScrollTopComponent } from './ngx-scroll-top.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public';\n"],"names":[],"mappings":"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"}