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

# `lr-dashboard-grid`

- **Import** `import '@aceshooting/lyra-ui/components/lr-dashboard-grid.js';` (stable tag alias; registers the tag)
- **Class** `LyraDashboardGrid`, also available unregistered from `@aceshooting/lyra-ui/components/layout/dashboard-grid/dashboard-grid.class.js`
- **Family** `components/layout/` — see `llms/index.md` for its siblings
- **Status** `stable` since `4.1.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** `dompurify`, `katex`, `marked`, `shiki` — see `llms/peers.md`
- **Themeable via** 5 parts, 6 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-dashboard-grid`

Responsive, keyboard-accessible controlled widget grid. It positions layout entries and emits
move, resize, collision, and layout-change requests; the host owns persistence and applies updates.

Pointer move and resize gestures measure the rendered column/row pitch, including the gutter, so
movement by four painted tracks proposes four logical columns in either LTR or RTL. Row-height and
gap overrides also govern vertical pointer snapping. The component continues to emit controlled
layout proposals; the caller accepts them by assigning `layout`.

**Properties:** `layout: readonly LyraDashboardCell[] = []` (attribute: false, never mutated by the component),
`columns: number = 12`, `rowHeight: number = 80` (px, also the row snap pitch), `gap: number = 8`
(px, both axes), `collision: 'reject' | 'push' | 'overlap' = 'reject'`, `cellsDraggable: boolean = false`
(attribute `cells-draggable` — pointer drag plus Ctrl/Cmd+Arrow), `cellsResizable: boolean = false`
(attribute `cells-resizable` — the resize handle plus Ctrl/Cmd+Shift+Arrow), `locked: boolean =
false` (reflected — disables every gesture grid-wide), `accessibleLabel: string | null = null`
(attribute `aria-label`, falls back to a localized grid name).

**Events:** `lr-cell-move` (`detail: { cellId, position, previous }`), `lr-cell-resize`
(`detail: { cellId, size, previous }`), `lr-collision`
(`detail: { cellId, collidedCellIds, policy, accepted }`),
`lr-layout-change` (`detail: { layout }`, the full proposed layout after an accepted change).
The collection-bearing collision and layout-change details are detached and recursively frozen;
listeners apply changes by creating and assigning a new layout.
Rejected-collision feedback is appended immediately to the shared light-DOM polite announcement
sink. Accepted move/resize success is appended only after a later controlled `layout` assignment
contains the requested target geometry; ignoring a request never announces a change that did not
happen. All feedback remains silent while the grid or a composed ancestor is excluded from the
accessibility tree.
**Slots:** `cell-{cellId}`. **CSS parts:** `base`, `cell`, `empty`, `resize-handle`, `live-region` (an
`aria-hidden` shadow mirror of the latest spoken message).

`layout` is normalized into an immutable snapshot before rendering. Reads are bounded to the first
1,000 positions; foreign-realm arrays are accepted; malformed records, hostile accessors, and later
duplicate cell IDs are skipped without discarding valid neighbors. Geometry and min/max constraints
are finite and consistent, and neither the returned array nor its cells alias caller-owned objects.
Each admitted `cell.widget` is also copied immediately through the canonical bounded widget-document
factory: its node records, child arrays, and prop records are frozen without cloning opaque prop or
payload leaves. A hostile or malformed widget is omitted while its otherwise-valid cell remains.
Direct light-DOM children with `cell-id` remain the authored source of truth across insertion,
removal, cell-ID retargeting, reconnect, and document adoption. The first authored child for a cell
ID wins; when it disappears, the default cell is restored without mistaking a forged marker
attribute for a library-owned node.

The default content assigns a version-two document created from `cell.widget` to
`<lr-widget-renderer>.document`; it never uses the legacy `tree` input. Pointer gestures admit only
the primary button/pointer and ignore controls, links, labels, editable content, and interactive
roles in the composed path. Keyboard resizing uses physical directions in both LTR and RTL:
Right/Down grow and Left/Up shrink, while pointer resizing retains the logical inline-end handle.

In the narrow stacked layout, a cell that currently owns a resize handle keeps at least the shared
interactive-action block-size (`--lr-icon-button-size`). The handle is absolutely positioned and
cannot contribute intrinsic size itself; the state-aware floor prevents it from overlapping the
preceding cell or gap while readonly and locked short cells retain content-derived sizing.
Host, grid, cell, and direct slotted-content boundaries also permit intrinsic inline shrinkage and
inherit `overflow-wrap: anywhere`, so an unbroken consumer-authored text run cannot widen a 320px
stack. This does not seize overflow from child-owned widgets: custom content can still declare
`overflow: auto` and `white-space: nowrap` to retain a contained internal scrollport.

**Themeable custom properties:** `--lr-dashboard-grid-columns`, `--lr-dashboard-grid-row-height`,
and `--lr-dashboard-grid-gap` back the CSS Grid's `grid-template-columns`/`grid-auto-rows`/`gap`.
They are real cascade-authoritative public hooks: the `columns`/`rowHeight`/`gap` properties supply
private computed fallbacks rather than overwriting these public variables inline.
`--lr-dashboard-grid-cell-hover-outline-color`
(default `var(--lr-color-border-strong)`) retints the mouse-hover outline on `[part='cell']` — a
preview of its own `:focus-visible` ring, shown because every cell is a real focusable,
draggable/resizable target; set it to `transparent` to opt out of the hover treatment entirely.

**Additional API surface:**

- `LyraDashboardCell`, `LyraDashboardCollisionPolicy`, and
  `LyraDashboardPlacementResult` — readonly public authoring/result types.
- `resolveLyraDashboardPlacement(layout, candidateCellId, requested, columns, policy)` — the only public
  runtime layout utility. It returns an immutable normalized result; collision indexing,
  clamping, sorting, and push-cascade helpers are intentionally implementation-private.
- `LyraDashboardCellMoveDetail`, `LyraDashboardCellResizeDetail`,
  `LyraDashboardCollisionDetail`, and `LyraDashboardLayoutChangeDetail` — readonly event-detail
  interfaces used by `LyraDashboardGridEventMap`.
- `--lr-dashboard-grid-collision-outline-color` — Outline color of a cell whose current drag/resize preview collides with another cell. Default: `var(--lr-color-danger)`.
- `--lr-dashboard-grid-interaction-shadow` — Box shadow applied during a cell drag or resize. Default: `var(--lr-shadow-m)`.
