# ClrPopover Utilities

The `ClrPopover` implementation is a utility infrastructure based on the Angular Overlay and Portals implementation provided in the Angular CDK. It is not a standalone component, but a set of directives and services used to build Clarity overlay components like **Dropdowns**, **Signposts**, and **Tooltips**.

## Goal

The goal is to provide a straightforward utility within the Clarity library that allows for popover-like elements to use a consistent API. It offloads all positioning, z-indexing, and layering logic to the Angular CDK, which is better equipped to handle complex rendering situations.

## Architecture

At its most basic, the popover utility is implemented with a set of directives and a service to track, manage, and subscribe to state change events.

### Service: `ClrPopoverService`

The **`ClrPopoverService`** acts as the central state manager for a specific popover instance. It is **transient** and must be provided at the component level (e.g., inside `ClrDropdown`, `ClrSignpost`, or `ClrTooltip`) to ensure that each popover maintains its own isolated state.

The service is responsible for:

- **State Management**:
  - **`open`**: A boolean property to get or set the current visibility state of the popover. Setting this triggers the `openChange` observable.
  - **`position`**: Tracks the current `ClrPopoverPosition` (e.g., `bottom-left`), triggering the `getPositionChange()` stream when updated.
  - **`popoverType`**: Defines the type of popover (Tooltip, Signpost, etc.) to determine offset calculations.

- **Event Handling**:
  - **`toggleWithEvent(event: any)`**: A helper method used by triggers. It toggles the `open` state, stores the triggering event (to prevent conflicts like "click outside" closing immediately), and prevents default scrolling behavior for arrow keys.
  - **`openEvent`**: Stores the specific browser event that triggered the opening of the popover.

- **Focus & Origin Management**:
  - **`origin`**: Holds the `FlexibleConnectedPositionStrategyOrigin` — either an `ElementRef` (trigger element) or a `ClrPopoverPoint` (`{ x, y }` coordinate). This is used by the CDK to calculate where the overlay should appear.
  - **`originElement`**: Convenience getter that returns the origin as `ElementRef<HTMLElement>` when element-based, or `null` when point-based.
  - **`originPoint`**: Convenience getter that returns the origin as `ClrPopoverPoint` when point-based, or `null` when element-based.
  - **`closeButtonRef`**: Holds the `ElementRef` of the internal close button (if applicable).
  - **`focusOrigin()`**: A utility method to return focus to the origin element (e.g., when the menu closes), ensuring accessibility compliance. No-op for point-based origins.
  - **`focusCloseButton()`**: Moves focus to the close button inside the content when appropriate.

- **Reactive Streams**:
  - **`openChange`**: Observable that emits whenever the open state changes.
  - **`popoverVisible`**: Observable used to coordinate when the DOM is actually ready/visible.
  - **`getEventChange()`**: Observable stream of the events that triggered state changes.

### Directives

1.  **`[clrPopoverOrigin]`**:

- Used on the element that serves as the reference point for positioning.
- Passes an `ElementRef` to the service as the `origin`, which is used for focus management and positioning calculations.

2.  **`*clrPopoverContent`**:

- A structural directive that renders the view and hoists it to an Overlay on the `body` element using a `DomPortal`.
- Accepts configuration arguments for the overlay (open state, position, outside click behavior, scroll behavior).
- Manages "Click Outside" and "Scroll to Close" logic.

3.  **`[clrPopoverCloseButton]`**:

- Should only be used inside popover content containers.
- Calls the toggle method on the service and restores focus to the origin element.

4.  **`[clrPopoverOpenCloseButton]`**:

- Handles toggling the popover content open and closed via the popover service.
- Typically used on triggers (e.g., the button that opens a menu).

### ClrPopoverContent Inputs

The `*clrPopoverContent` directive is the "workhorse" of the utility. It accepts several inputs to configure behavior:

- **`clrPopoverContent`** (boolean):
  - Controls the open/closed state of the popover. When true, the overlay is created and attached; when false, it is detached.
- **`clrPopoverContentAt`** (`ClrPopoverPosition | ConnectedPosition`):
  - Defines the preferred position of the content relative to the origin (e.g., `top-right`, `bottom-left`). It sets the `preferredPosition` for the CDK overlay.
- **`clrPopoverContentAvailablePositions`** (`ConnectedPosition[]`):
  - A prioritized list of fallback positions the overlay can attempt to use if the preferred position does not fit within the viewport.
- **`clrPopoverContentType`** (`ClrPopoverType`):
  - Specifies the type of popover (e.g., `DROPDOWN`, `TOOLTIP`, `SIGNPOST`). This determines specific offsets and behavior variations associated with that type.
- **`clrPopoverContentOutsideClickToClose`** (boolean):
  - Determines if clicking outside the popover content should automatically close it. Defaults to `true`.
- **`clrPopoverContentScrollToClose`** (boolean):
  - Determines if scrolling the page should automatically close the popover. Defaults to `false`.

## Enums

1.  **`ClrPopoverType`**: Describes the popover variation: `SIGNPOST`, `TOOLTIP`, `DROPDOWN`, and `DEFAULT`.
2.  **`ClrPopoverPosition`**: Describes the popover position relative to the origin (e.g., `top-left`, `bottom-right`).
3.  **`ClrPosition`**: Describes the specific point of contact on both the origin and content elements.

---

## Usage Example

To use the popover utility, a host component must provide the `ClrPopoverService`. Components generally provide a `ClrPopoverPosition` via the `at` input to describe the preferred content position.

```html
<button class="btn" clrPopoverOpenCloseButton clrPopoverOrigin [attr.aria-owns]="popoverId">
  <cds-icon shape="home"></cds-icon> Popover Origin
</button>
<div
  [id]="popoverId"
  role="dialog"
  cdkTrapFocus
  *clrPopoverContent="open;
    at: position; availablePositions: [...]; outsideClickToClose: true; scrollToClose: true"
>
  <div role="heading">
    Overlay Header
    <button class="btn btn-sm btn-outline-neutral btn-icon" clrPopoverCloseButton>
      <cds-icon shape="times"></cds-icon>
    </button>
  </div>
  <div>Overlay Content</div>
</div>
```
