# `hb-sidebar-cards-navigator` (sidebar-cards-navigator)

**Category:** layout  
**Tags:** layout, navigation  
**Package:** `@htmlbricks/hb-sidebar-cards-navigator`

## Overview

`hb-sidebar-cards-navigator` is a stacked sidebar layout that drives navigation from a JSON `panels` tree. It resolves one **active panel**, renders a compact **`hb-navbar`** header for that panel, then lists each card’s rows as **`hb-sidenav-button`** entries. Row clicks emit a single **`itemClick`** custom event with full context (panel, card, row). Rows may optionally change the active panel via **`switchToPanelId`**.

Nested **`hb-navbar`** and **`hb-sidenav-button`** are registered at runtime (same package version as this component). Bootstrap Icons are loaded from jsDelivr in the component head for row icons.

## Active panel resolution

The visible panel is chosen in this order:

1. If an internal **`switchToPanelId`** state is set (after a row with `switchToPanelId` was clicked), the panel whose **`id`** matches that value is shown.
2. Otherwise the first panel with **`main: true`** is used.
3. If none is marked `main`, the first panel **without** **`parentPanelId`** is used.

If no panel matches, nothing is rendered inside the host.

## UI behavior

- **`hb-navbar`**: **`companybrandname`** is bound to the active panel’s **`title`**. The **`nav-switcher`** slot shows a **list** icon for root-level panels and a **back** arrow when **`parentPanelId`** is set. **`navmenuswitch`** switches the active panel to **`parentPanelId`** when a parent exists (back navigation).
- **Cards**: For each card on the active panel, if **`rows`** is non-empty, each row becomes one **`hb-sidenav-button`** with **`navlink`** serialized from the row (`text`, `key`, optional `bootstrapIcon`, optional `badge`).
- **Empty cards**: Cards with no rows or an empty **`rows`** array are skipped (no placeholder block).

## Custom element

```html
<hb-sidebar-cards-navigator …></hb-sidebar-cards-navigator>
```

## Attributes (snake_case; string values from HTML)

Web component attributes are strings. Pass structured data as a **JSON string** on **`panels`**.

| Attribute | Required | Description |
|-----------|----------|-------------|
| **`id`** | No | Host identifier; echoed on emitted **`itemClick`** detail when set. |
| **`style`** | No | Standard inline host styles (string). |
| **`panels`** | No | JSON string representing **`Panel[]`** (see data model below). If the runtime value is a string, the component parses it with **`JSON.parse`**; invalid JSON is logged and parsing is skipped. |

### Boolean and numbers in JSON

Inside the **`panels`** JSON, use normal JSON booleans and numbers (e.g. **`"main": true`**). Those rules apply to the JSON payload, not to HTML boolean attributes on this tag.

## Data model (`panels` JSON)

Types live in **`types/webcomponent.type.d.ts`**. Shapes below match authoring / TypeScript names; serialize the whole array as one string for the **`panels`** attribute.

### `Panel`

| Field | Type | Notes |
|-------|------|--------|
| **`id`** | string | Panel identifier; used with **`switchToPanelId`** and hierarchy. |
| **`title`** | string (optional) | Shown in **`hb-navbar`**. |
| **`icon`** | string (optional) | Available on the type; not read directly by this host’s markup. |
| **`sort`** | number (optional) | Available on nested card types; not used for panel ordering in the current template. |
| **`parentPanelId`** | string (optional) | If set, navbar shows back and **`navmenuswitch`** returns to this parent panel id. |
| **`main`** | boolean (optional) | When **`true`**, this panel is preferred when no **`switchToPanelId`** override is active. |
| **`cards`** | array | List of **`CardNavigator`** objects. |

### `CardNavigator` (card under a panel)

| Field | Type | Notes |
|-------|------|--------|
| **`id`** | string | Card id; used when resolving clicks. |
| **`title`** | string (optional) | Card-level metadata (not rendered as a separate heading in the current template). |
| **`icon`** | string (optional) | Metadata. |
| **`sort`** | number (optional) | Metadata. |
| **`rows`** | **`CardRow[]`** | Rows rendered as **`hb-sidenav-button`** instances. |

### `CardRow`

| Field | Type | Notes |
|-------|------|--------|
| **`key`** | string | Stable row id; matched on click. |
| **`text`** | string | Primary label (passed to **`hb-sidenav-button`** **`navlink`**). |
| **`bootstrapIcon`** | string (optional) | Bootstrap Icons name without the **`bi-`** prefix (e.g. **`house-door`**). |
| **`badge`** | object (optional) | **`text`**, optional **`class`**, **`classcolor`** — forwarded to **`navlink`**. |
| **`subtext`** | string (optional) | On the type for richer payloads; **not** forwarded to **`navlink`** in the current implementation. |
| **`value`** | string, number, or boolean (optional) | Available in **`itemClick`** detail’s **`row`**; not sent to **`navlink`**. |
| **`selected`** | boolean (optional) | Available on **`row`** in **`itemClick`**; not sent to **`navlink`**. |
| **`type`** | **`"switch"` \| `"range"` \| `"radio"` \| `"checkbox"` \| `"button"`** (optional) | Available on **`row`** in **`itemClick`**; not sent to **`navlink`**. |
| **`switchToPanelId`** | string (optional) | After **`itemClick`**, the navigator switches the active panel to this **`id`**. |

## Events

### `itemClick`

Fired when the user activates a row (via **`hb-sidenav-button`** **`pageChange`**). **`event.detail`** matches **`CardRowSelected`**:

| Field | Meaning |
|-------|---------|
| **`id`** | Host **`id`** prop value (may be empty). |
| **`panel`** | Active panel fields **without** the **`cards`** array (plain object clone). |
| **`card`** | Selected card fields **without** the **`rows`** array. |
| **`row`** | The full **`CardRow`** that was clicked. |

If **`row.switchToPanelId`** is set, the internal active panel updates after the event is dispatched.

## Styling (Bulma)

The component uses Bulma layout utilities and theme variables. Prefer **`--bulma-*`** on **`body`**, **`:root`**, or a parent that pierces into shadow DOM only if your bundler setup allows; otherwise set variables on elements that apply to this tree per your integration.

Documented variables (see **`extra/docs.ts`** / **`styleSetup`**):

| Variable | Role |
|----------|------|
| **`--bulma-block-spacing`** | Vertical gap between stacked groups of rows. |
| **`--bulma-text`** | Default text (rows, navbar). |
| **`--bulma-text-strong`** | Strong headings (titles). |
| **`--bulma-link`** | Interactive accents from nested navigation components. |
| **`--bulma-scheme-main`** | Surfaces behind stacks. |

Reference: [Bulma CSS variables](https://bulma.io/documentation/features/css-variables/).

## Slots and CSS parts

- **Slots:** none on this host.
- **`::part`:** none declared for this host; nested **`hb-*`** children may expose their own documented parts.

## Dependencies

Declared in **`extra/docs.ts`**:

- **`hb-navbar`** (which depends on **`hb-dropdown-simple`**)
- **`hb-sidenav-button`**

Ensure these custom elements are defined (for example via **`@htmlbricks/hb-bundle`** or the matching standalone packages) before using this navigator.

## Examples

### Minimal single panel

```html
<hb-sidebar-cards-navigator
  panels='[{"id":"home","title":"Home","cards":[{"id":"menu","rows":[{"key":"dash","text":"Dashboard","bootstrapIcon":"speedometer2"}]}]}]'
></hb-sidebar-cards-navigator>
```

### Main panel, child panel with back, and panel switch from a row

```html
<hb-sidebar-cards-navigator
  id="app-nav"
  panels='[
    {
      "id": "root",
      "main": true,
      "title": "Overview",
      "cards": [
        {
          "id": "account",
          "title": "Account",
          "rows": [
            { "key": "profile", "text": "Profile", "bootstrapIcon": "person" },
            {
              "key": "advanced",
              "text": "Advanced",
              "bootstrapIcon": "gear",
              "switchToPanelId": "advanced-panel"
            }
          ]
        }
      ]
    },
    {
      "id": "advanced-panel",
      "parentPanelId": "root",
      "title": "Advanced",
      "cards": [
        {
          "id": "opts",
          "rows": [
            { "key": "security", "text": "Security", "bootstrapIcon": "shield-lock" }
          ]
        }
      ]
    }
  ]'
></hb-sidebar-cards-navigator>
```

### Listening from JavaScript

```js
const el = document.querySelector("hb-sidebar-cards-navigator");
el.addEventListener("itemClick", (e) => {
  const { panel, card, row, id } = e.detail;
  console.log(panel.id, card.id, row.key, id);
});
```

## TypeScript

Authoring types for this package: **`types/webcomponent.type.d.ts`** — **`Component`**, **`Panel`**, **`CardNavigator`**, **`CardRow`**, **`CardRowSelected`**, and **`Events`** (including **`itemClick`** detail typing for Svelte / wrappers).

## License

Package metadata references **Apache-2.0** (see `LICENSE.md` in the published package when consuming from npm).
