Skip to content
Ndop UI

Dialog

Dialog changelog

Every version of Dialog, newest first. Update with npx @ndop/cli update dialog; it keeps your edits.

Preview an update

Paste your copy of the file to see what updating it to 1.0.0 would do.

Runs in your browser; your file isn't uploaded. If your ndop.json aliases differ from the registry's @/, import paths can show up as changes.

1.0.0 Latest

Stable release; no code changes since 0.2.0.

0.2.0

Every part of the item is now its own directive without the Nd prefix, which is kept for the headless primitives. Renames NdDialog to Dialog, NdDialogClose to DialogClose, NdDialogPortal to DialogPortal and NdDialogTrigger to DialogTrigger. Selectors and inputs are unchanged, and ndop update renames the imports and their uses in your code.

What ndop update does from 0.1.5

  • Updates {ui}/dialog/dialog.ts, keeping your edits.
  • Renames NdDialog to Dialog where your code imports it.
  • Renames NdDialogClose to DialogClose where your code imports it.
  • Renames NdDialogPortal to DialogPortal where your code imports it.
  • Renames NdDialogTrigger to DialogTrigger where your code imports it.
{ui}/dialog/dialog.ts
} from '@ndop/primitives/dialog';
import { cn } from '@/lib/utils';

export { NdDialog, NdDialogClose, NdDialogPortal, NdDialogTrigger };
/**
 * A modal dialog (`@ndop/primitives/dialog`): it renders its portal's
 * content over the page while open, traps focus inside it and returns it on
 * close, and closes on Escape or a backdrop click. Put it on an
 * `ng-container`; `open` is two-way bindable.
 *
 * @example
 * ```html
 * <ng-container ndDialog [(open)]="open">…</ng-container>
 * ```
 */
@Directive({
  selector: '[ndDialog]',
  hostDirectives: [
    {
      directive: NdDialog,
      inputs: ['open', 'defaultOpen', 'closeOnEscape', 'closeOnOverlayClick'],
      outputs: ['openChange'],
    },
  ],
})
export class Dialog {}

/**
 * The button that opens its dialog, with `aria-haspopup`, `aria-expanded`
 * and `aria-controls`.
 *
 * @example
 * ```html
 * <button ndButton ndDialogTrigger>Edit profile</button>
 * ```
 */
@Directive({
  selector: '[ndDialogTrigger]',
  hostDirectives: [NdDialogTrigger],
  host: { 'data-slot': 'dialog-trigger' },
})
export class DialogTrigger {}

/**
 * Marks the `ng-template` its dialog renders while open.
 *
 * @example
 * ```html
 * <ng-template ndDialogPortal>…</ng-template>
 * ```
 */
@Directive({
  selector: 'ng-template[ndDialogPortal]',
  hostDirectives: [NdDialogPortal],
})
export class DialogPortal {}

/**
 * A button that closes its dialog.
 *
 * @example
 * ```html
 * <button ndButton variant="outline" ndDialogClose>Cancel</button>
 * ```
 */
@Directive({
  selector: '[ndDialogClose]',
  hostDirectives: [NdDialogClose],
  host: { 'data-slot': 'dialog-close' },
})
export class DialogClose {}

/**
 * Backdrop behind the dialog; a click on it closes the dialog.
 *
 * @example

0.1.5

Adds the dialog-unsaved-changes example: a dialog that a click outside doesn't close. The files are unchanged.

0.1.4

Lists the item under Overlays in the docs. The files are unchanged.

0.1.3

Mirrors the close button and the header in right-to-left layouts: the close button sits at end-4 instead of right-4, and the header aligns with sm:text-start instead of sm:text-left.

What ndop update does from 0.1.2

  • Updates {ui}/dialog/dialog.ts, keeping your edits.
{ui}/dialog/dialog.ts

  protected readonly classes = computed(() =>
    cn(
      "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
      "absolute top-4 end-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
      this.class(),
    ),
  );
// ⋯
  readonly class = input('');

  protected readonly classes = computed(() =>
    cn('flex flex-col gap-2 text-center sm:text-left', this.class()),
    cn('flex flex-col gap-2 text-center sm:text-start', this.class()),
  );
}

0.1.2

Removes the source notice at the top of dialog.ts. The credit for the shadcn/ui styles it was based on is on the docs site instead.

Lists the main example first.

What ndop update does from 0.1.1

  • Updates {ui}/dialog/dialog.ts, keeping your edits.
{ui}/dialog/dialog.ts
/*
 * Styles from shadcn/ui new-york-v4 "dialog" (MIT License, Copyright (c)
 * 2023 shadcn), copied on 2026-10-02 (ADR-0014). Changes:
 * - shadcn renders the portal, overlay and close button inside
 *   DialogContent. Directives can't render elements, so you write them:
 *   `ndDialogPortal`, `ndDialogOverlay`, and `ndDialogCloseButton` for the
 *   close icon button (its classes are shadcn's), as in the examples;
 * - the close icon is `<svg ndIcon="x">` (ADR-0013);
 * - data-[state=closed] exit animations are kept, but the content is
 *   removed as soon as the dialog closes, so only enter animations play.
 */
import { computed, Directive, input } from '@angular/core';
import {
  NdDialog,

0.1.1

Points the item at its API metadata, metadata/dialog.json, which the docs and the MCP server read. The files are unchanged.

0.1.0

First release.

What ndop update does

  • Adds {ui}/dialog/dialog.ts.
  • Installs @ndop/primitives.
  • Installs the utils item.