{"version":3,"file":"ngx-t-forms-token-binding-menu.component-CLpjkUnf.mjs","sources":["../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/token-binding-menu/token-binding-menu.component.ts","../../../projects/ngx-t-forms/src/lib/components/t-dynamic-data-edit/elements/token-binding-menu/token-binding-menu.component.html"],"sourcesContent":["import {\n  ChangeDetectionStrategy,\n  Component,\n  OnDestroy,\n  TemplateRef,\n  ViewContainerRef,\n  effect,\n  inject,\n  input,\n  output,\n  viewChild,\n} from '@angular/core';\nimport { Overlay, OverlayRef, type FlexibleConnectedPositionStrategy } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { MatIconModule } from '@angular/material/icon';\n\nimport type { EditorBinding } from '../json-editor/json-editor.component';\n\n/** Where the menu is pinned, in viewport coordinates. */\nexport interface TokenMenuAnchor {\n  readonly top: number;\n  readonly left: number;\n  readonly width: number;\n}\n\n/**\n * The `$`-trigger option list, shared by every editor that offers the gesture.\n *\n * Purely presentational: it owns no filtering, no caret maths and no insertion.\n * The host decides WHEN to open it, WHAT to show and WHERE to pin it; this\n * renders the rows and reports the pick. That split is what lets a plain text\n * input and a rich-text editor — which agree on nothing about caret handling —\n * present an identical menu.\n *\n * RENDERED THROUGH A CDK OVERLAY, NOT IN PLACE. The first version positioned a\n * `position: fixed` panel at the anchor point, which is only viewport-relative\n * until an ANCESTOR carries a `transform` — at which point that ancestor\n * becomes the containing block and every coordinate is offset by its\n * translation. The workflow canvas pans and zooms via transforms and the step\n * editor drawer animates with them, so the menu opened far from the caret.\n * A CDK overlay attaches to the body-level overlay container, which no app\n * transform can reach — the same mechanism every Material dropdown relies on\n * inside that exact canvas. The anchor stays a plain viewport point, so hosts\n * are unaffected.\n *\n * The template still belongs to THIS component (a `TemplatePortal` renders the\n * component's own view), so the scoped styles and the reactive `options` /\n * `activeIndex` bindings work unchanged inside the overlay.\n */\n@Component({\n  selector: 'lib-token-binding-menu',\n  imports: [MatIconModule],\n  templateUrl: './token-binding-menu.component.html',\n  styleUrl: './token-binding-menu.component.scss',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TokenBindingMenuComponent implements OnDestroy {\n  /** Rows to render, already filtered by the host. */\n  readonly options = input<readonly EditorBinding[]>([]);\n  /** Index of the highlighted row. */\n  readonly activeIndex = input<number>(0);\n  /** Viewport position. `null` renders nothing. */\n  readonly anchor = input<TokenMenuAnchor | null>(null);\n\n  /**\n   * A row was chosen.\n   *\n   * Raised from `mousedown`, not `click`: the host's input still has focus and a\n   * `click` would arrive after the blur that closes the menu.\n   */\n  readonly pick = output<EditorBinding>();\n\n  private readonly menuTpl = viewChild<TemplateRef<unknown>>('menuTpl');\n\n  readonly #overlay = inject(Overlay);\n  readonly #viewContainerRef = inject(ViewContainerRef);\n  #overlayRef: OverlayRef | null = null;\n  #positionStrategy: FlexibleConnectedPositionStrategy | null = null;\n\n  constructor() {\n    effect(() => {\n      const at = this.anchor();\n      const template = this.menuTpl();\n      if (!at || !template) {\n        this.#detach();\n        return;\n      }\n      this.#show(at, template);\n    });\n  }\n\n  ngOnDestroy(): void {\n    this.#overlayRef?.dispose();\n    this.#overlayRef = null;\n  }\n\n  /** Creates the overlay on first open; re-pins it on every anchor move. */\n  #show(at: TokenMenuAnchor, template: TemplateRef<unknown>): void {\n    if (!this.#overlayRef) {\n      this.#positionStrategy = this.#overlay\n        .position()\n        .flexibleConnectedTo({ x: at.left, y: at.top })\n        .withPositions([\n          // Below the anchor point, then flipped above when the viewport runs out.\n          { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'top' },\n          { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' },\n          { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'top' },\n        ])\n        .withViewportMargin(8);\n      this.#overlayRef = this.#overlay.create({\n        positionStrategy: this.#positionStrategy,\n        scrollStrategy: this.#overlay.scrollStrategies.reposition(),\n        width: at.width,\n      });\n      this.#overlayRef.attach(new TemplatePortal(template, this.#viewContainerRef));\n      return;\n    }\n    this.#overlayRef.updateSize({ width: at.width });\n    this.#positionStrategy?.setOrigin({ x: at.left, y: at.top });\n    this.#overlayRef.updatePosition();\n  }\n\n  #detach(): void {\n    if (this.#overlayRef?.hasAttached()) {\n      this.#overlayRef.detach();\n    }\n  }\n}\n","<ng-template #menuTpl>\n  <div class=\"token-binding-menu\" role=\"listbox\">\n    @for (binding of options(); track binding.insert; let i = $index) {\n      <button\n        type=\"button\"\n        role=\"option\"\n        class=\"token-binding-menu__option\"\n        [class.is-active]=\"i === activeIndex()\"\n        [attr.aria-selected]=\"i === activeIndex()\"\n        (mousedown)=\"pick.emit(binding)\">\n        @if (binding.icon) {\n          <mat-icon class=\"token-binding-menu__icon\">{{ binding.icon }}</mat-icon>\n        }\n        <span class=\"token-binding-menu__labels\">\n          <span class=\"token-binding-menu__label\">{{ binding.label }}</span>\n          @if (binding.detail) {\n            <span class=\"token-binding-menu__detail\">{{ binding.detail }}</span>\n          }\n        </span>\n      </button>\n    } @empty {\n      <p class=\"token-binding-menu__empty\">No matching fields</p>\n    }\n  </div>\n</ng-template>\n"],"names":["i1"],"mappings":"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"}