<!-- GENERATED by scripts/build-llms.mjs from llms/retrieval.md — do not edit this file. -->

# `lr-neighbor-list`

- **Import** `import '@aceshooting/lyra-ui/components/lr-neighbor-list.js';` (stable tag alias; registers the tag)
- **Class** `LyraNeighborList`, also available unregistered from `@aceshooting/lyra-ui/components/retrieval/neighbor-list/neighbor-list.class.js`
- **Family** `components/retrieval/` — see `llms/index.md` for its siblings
- **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 9 parts, 0 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-neighbor-list`

One entity's relationship rows: relation, direction, neighbor, with per-row navigate and
expand-in-graph affordances. Never computes neighbors itself (the host derives rows from its own
graph data) and never mutates a graph.

**Properties:**

- `rows: LyraNeighborRow[] = []` (attribute: false) — `LyraNeighborRow { relation: string; direction:
'in' | 'out' | 'both'; node: LyraEntity }`
- `groupByRelation: boolean = false` (attribute `group-by-relation`) — inserts a `group-header` row per
  distinct `relation`
- `expandable: boolean = false` — renders a per-row expand-in-graph icon button
- `virtualizeAt: number = 100` (attribute `virtualize-at`) — row count above which the list virtualizes
- `label?: string` — fallback name for the stable group. Omission uses the localized neighbor-list
  label. A non-empty host `aria-label` makes
  the host the sole overall owner; an explicitly empty host label stays empty on the group

**Events:** `lr-entity-select` (`detail: { entityId }`, a row's node button was activated),
`lr-node-expand` (`detail: { nodeId }`, a row's expand button was activated — deliberately the same
name and detail shape as `lr-graph`'s own event, so one host handler serves both).

**Slots:** none.

**CSS parts:** `base` (`role="list"`), `group-header` (only when `groupByRelation`; above
`virtualizeAt` this is the internal virtual-list's own group label, re-exported under the same
name), `row` (`role="listitem"`; above `virtualizeAt` this is the internal virtual-list's own row
wrapper, re-exported under the same name), `direction` (`aria-hidden` glyph), `relation`,
`node-label`, `node-meta` (secondary type/degree text, when present), `expand-button` (only when
`expandable`), `empty` (shown when `rows` is empty). Every part presents identically either side of
`virtualizeAt`.

**Themeable custom properties:** shared tokens only.

**Optional peer deps:** none.

```html
<lr-neighbor-list expandable group-by-relation></lr-neighbor-list>
<script>
  document.querySelector("lr-neighbor-list").rows = [
    {
      relation: "works_for",
      direction: "out",
      node: { id: "e2", label: "Analytical Engine Co." },
    },
  ];
</script>
```

**Known gotchas:**

- `lr-node-expand`'s detail shape is intentionally identical to `lr-graph`'s own event of the
  same name, so a single listener wired to both handles "expand this node's neighborhood" uniformly.
- A row is exactly one `[part="row"]` element in both rendering paths. Above `virtualizeAt` that
  element is the internal virtual-list's own row wrapper (the component renders only the row's
  content into it), so a `::part(row)` rule applies once, not twice.
- Rows with blank neighbor ids and later rows repeating the same node id are omitted first-wins
  before grouping, virtualization, rendering, or events.
- A missing, blank, or nonstring `node.type` is omitted from that row's secondary metadata instead
  of being passed to locale formatting; the neighbor label and any valid degree remain visible.

---
