{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "exit-intent-offer",
  "title": "Exit Intent — Offer",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/exit-intent-offer/exit-intent-offer.component.ts",
      "content": "// Extended-trial offer fired once per session when the pointer leaves toward the browser\n// chrome (top edge only — sideways exits are usually the scrollbar or another window).\n// Port of the Vue/React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit, signal } from '@angular/core'\nimport { ArrowRight, Clock, LucideAngularModule } from 'lucide-angular'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiDialogComponent,\n  UiDialogContentComponent,\n  UiDialogDescriptionComponent,\n  UiDialogFooterComponent,\n  UiDialogHeaderComponent,\n  UiDialogTitleComponent,\n} from '@/ui/dialog/dialog.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-exit-intent-offer, [ui-exit-intent-offer]',\n  standalone: true,\n  // The block renders its own root <div> (and the dialog portals to body): keep the host out of layout.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiDialogComponent,\n    UiDialogContentComponent,\n    UiDialogDescriptionComponent,\n    UiDialogFooterComponent,\n    UiDialogHeaderComponent,\n    UiDialogTitleComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <div data-slot=\"exit-intent-offer\">\n      <ui-dialog [open]=\"open()\" (openChange)=\"open.set($event)\">\n        <ui-dialog-content class=\"sm:max-w-md\">\n          <div ui-dialog-header>\n            <span ui-badge variant=\"secondary\" class=\"w-fit gap-1.5\">\n              <lucide-icon [img]=\"Clock\" class=\"size-3\" aria-hidden=\"true\" />\n              Before you go\n            </span>\n            <h2 ui-dialog-title class=\"mt-3 text-xl leading-snug text-balance\">Take 30 days instead of 14</h2>\n            <p ui-dialog-description class=\"leading-relaxed\">\n              A full close cycle fits in 30 days and 14 does not, which is the actual reason most trials stall. No card,\n              no call, cancel from the dashboard.\n            </p>\n          </div>\n\n          <ui-separator />\n\n          <ul class=\"text-muted-foreground space-y-1.5 text-sm\">\n            <li>· Everything on the Business plan</li>\n            <li>· Connect your real warehouse, read-only</li>\n            <li>· Keep the definitions you author either way</li>\n          </ul>\n\n          <div ui-dialog-footer class=\"sm:justify-start\">\n            <button ui-button>\n              Start the 30-day trial\n              <lucide-icon [img]=\"ArrowRight\" class=\"ml-2 size-4\" aria-hidden=\"true\" />\n            </button>\n            <button ui-button variant=\"ghost\" (click)=\"open.set(false)\">No thanks</button>\n          </div>\n        </ui-dialog-content>\n      </ui-dialog>\n    </div>\n  `,\n})\nexport class UiExitIntentOfferComponent implements OnInit, OnDestroy {\n  protected readonly ArrowRight = ArrowRight\n  protected readonly Clock = Clock\n\n  @Input() storageKey = 'uipkge:exit-intent-seen'\n\n  readonly open = signal(false)\n\n  private readonly onPointerOut = (event: MouseEvent): void => {\n    if (event.relatedTarget || event.clientY > 4) return\n    if (this.show()) document.removeEventListener('mouseout', this.onPointerOut)\n  }\n\n  ngOnInit(): void {\n    document.addEventListener('mouseout', this.onPointerOut)\n  }\n\n  ngOnDestroy(): void {\n    document.removeEventListener('mouseout', this.onPointerOut)\n  }\n\n  show(): boolean {\n    try {\n      if (window.sessionStorage.getItem(this.storageKey) === '1') return false\n      window.sessionStorage.setItem(this.storageKey, '1')\n    } catch {\n      // Blocked storage: it can show once more this page view, never in a loop.\n    }\n    this.open.set(true)\n    return true\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/exit-intent-offer/exit-intent-offer.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/exit-intent-offer/index.ts",
      "content": "export { UiExitIntentOfferComponent } from './exit-intent-offer.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/exit-intent-offer/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/dialog.json",
    "https://uipkge.dev/r/angular/separator.json"
  ],
  "description": "Dialog triggered when the pointer leaves toward the browser chrome, offering an extended trial once per session, with a dismissal that is remembered.",
  "categories": [
    "marketing"
  ],
  "deprecated": true,
  "replacedBy": "exit-intent"
}