# `hb-messages-topics-card`

**Category:** messaging · **Tags:** messaging, chat · **Package:** `@htmlbricks/hb-messages-topics-card`

## Description

Renders a vertical list of chat or channel previews from a **`chats`** collection. Each row shows a rounded avatar, a title, a short message preview, a time label, and an unread counter rendered as a Bulma tag. Clicking a row updates internal selection state (`_selected`), highlights the row, and dispatches a **`select`** custom event with the clicked chat object so the host can open a thread or switch context.

## Styling (Bulma)

The component bundles **Bulma 1.x** inside the shadow root: **`layout/media`**, **`layout/level`**, **`elements/image`**, **`elements/tag`**, and typography / spacing helpers, with the light theme applied on `:host`. Public **`--bulma-*`** variables can be set on `body`, `:root`, or the host so they cascade consistently with the rest of the app — see [Bulma CSS variables](https://bulma.io/documentation/features/css-variables/) and the `styleSetup.vars` entries in `extra/docs.ts`.

| Variable | Purpose |
|----------|---------|
| `--bulma-text` | Title and preview snippet text. |
| `--bulma-text-strong` | Strong title weight color (falls back to `--bulma-text`). |
| `--bulma-text-weak` | Muted time label beside each preview. |
| `--bulma-border` | Divider between rows. |
| `--bulma-scheme-main-bis` | Hover background for rows that are not selected. |
| `--bulma-scheme-main-ter` | Selected row background. |
| `--bulma-radius-rounded` | Pill shape for the unread counter tag. |

**Shadow markup (simplified):**

- **`article.media.hb-topics-item`** — clickable row; **`hb-topics-item--selected`** when that chat is selected.
- **`figure.media-left`** + **`p.image.is-64x64`** + **`img.is-rounded`** — avatar from `img_uri`.
- **`div.media-content`** — Bulma **`level`** rows for title + time, then preview + counter tag.

## CSS parts

None.

## HTML slots

None.

## Custom element

`hb-messages-topics-card`

## Attributes (snake_case; string values in HTML)

Per the web component contract, attributes are strings. The primary payload is JSON on **`chats`**.

| Attribute | Required | Description |
|-----------|----------|-------------|
| `id` | No | Optional host identifier. |
| `style` | No | Optional inline host styles (typed on the component surface). |
| `chats` | No* | JSON string: array of chat objects (`IChat[]`). Defaults to an empty list if omitted. |

\* In markup you may omit **`chats`** until you set the attribute; the component defaults to an empty list.

### `chats` JSON shape (`IChat`)

Fields used for layout and labels in the current implementation:

| Field | Role |
|-------|------|
| `chat_id` | Stable key for each row (also used in `{#each}` keyed updates). |
| `title` | Primary line (semibold). |
| `text` | Preview line under the title. |
| `img_uri` | Avatar URL (`img` `src`). |
| `time` | Source for the time label when `localeTimeString` is not set; use an **ISO 8601** string in JSON so it parses as a date. |
| `localeTimeString` | If set, shown as-is instead of deriving `HH:MM` from `time`. |
| `counter` | Unread count; rendered in the tag (missing or falsy shows **`0`**). |
| `_selected` | When `true`, that row starts selected and receives the selected background. |

Additional optional fields (`is_group`, `chat_name`, `chat_img`, `last_message_*`, and so on) are part of the **`IChat`** type so you can carry richer metadata; they are not read by the template today but are still present on objects emitted by **`select`**.

If the **`chats`** prop arrives as a string (for example from an HTML attribute), the component parses it with **`JSON.parse`**; invalid JSON is logged to the console and parsing is skipped.

## Events

| Event | `detail` |
|-------|----------|
| `select` | The clicked **`IChat`** object (same reference the list uses after selection is updated). |

Listen in plain JavaScript:

```javascript
const el = document.querySelector("hb-messages-topics-card");
el.addEventListener("select", (e) => {
  const chat = e.detail;
  console.log(chat.chat_id, chat.title);
});
```

## Usage notes

- **Initial selection:** set **`_selected`** on at most one item in the JSON if you want a default active row.
- **Time display:** without **`localeTimeString`**, the component fills it from **`time`** using the **local** hours and minutes (`HH:MM`). Provide **`localeTimeString`** yourself for relative times or locale-specific formatting.
- **Unread badge:** the tag always shows a value; **`0`** is shown when there is no positive counter.
- **Avatars:** the template sets **`alt=""`** on images; if you need accessible names, extend your integration (for example wrapping context or contributing upstream) because the component does not derive labels from `title` today.

## Types

```typescript
export type IChat = {
  time: Date;
  title: string;
  text: string;
  img_uri: string;
  is_group?: boolean;
  chat_name?: string;
  chat_img?: string;
  chat_id: string;
  last_message_author?: string;
  last_message_author_img?: string;
  last_message_time?: Date;
  last_message_text?: string;
  counter?: number;
  localeTimeString?: string;
  _selected?: boolean;
};

export type Component = {
  id?: string;
  style?: string;
  chats?: IChat[];
};

export type Events = {
  select: IChat;
};
```

## Minimal HTML example

```html
<hb-messages-topics-card
  chats='[{"chat_id":"1","title":"Team","text":"Hello","time":"2026-01-01T12:00:00.000Z","img_uri":"https://example.com/a.png","counter":2}]'
></hb-messages-topics-card>
```

## Storybook / metadata

Argument controls and additional named examples (selected row, unread badges, single chat) live in `extra/docs.ts`.
