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