npm package: @htmlbricks/hb-table
Description: Bulma-styled data table driven by JSON `headers` and `rows` (`_id` required): column sort, header search (text, enum, date range), formatted values (nested keys, datetime via dayjs), copy-to-clipboard, row highlight and `selectrow` clicks. Global `actions
Latest version: 0.71.18

## `hb-table` — table

**Category:** data  
**Tags:** data, table

### What it does

Bulma-styled data table driven by JSON `headers` and `rows` (`_id` required): column sort, header search (text, enum, date range), formatted values (nested keys, datetime via dayjs), copy-to-clipboard, row highlight and `selectrow` clicks. Global `actions` and per-row `_actions` can open confirm or schema dialogs (`hb-dialog` / `hb-dialogform`). Optional multi-select with `selectactions`, `add_item` slot, and `hb-paginate` for page size, sort sync, and server-style `externalfilter` / `total` workflows.

While **`is_loading`** is enabled, the tbody shows **Bulma skeleton** rows (`skeleton-lines`, **`checkbox.is-skeleton`**, **`button.is-skeleton`**; count follows **`size`**, capped at 100). **`hb-paginate`** stays mounted but is hidden; a single **`skeleton-block`** covers the pagination area so **`page` / `pages`** stay in sync. Optional on the host: **`--hb-table-sk-pagination-height`**, **`--hb-table-sk-pagination-min-width`** (default `min(28rem, 100%)`), **`--hb-table-sk-pagination-offset-inline-end`** (extra end margin while loading, nudges skeleton inward; default `0.65rem`). i18n via `i18nlang`.

### Custom element

`hb-table`

### Attributes (snake_case; use string values in HTML)

- `id` (optional), `style` (optional): strings.
- `rows` (required): JSON string — row objects with `_id` and column keys; optional `_actions`, `_evidenced`.
- `headers` (required): JSON string — `ITableHeader[]` (`label`, `key`, optional `type`, `format`, `search`, `click`, `select`, `nosort`, `sortBy`, `truncateAt`, `copyTxt`, `tooltip`).
- `actions` (optional): JSON string — global row `IActionButton[]` (see **Actions** below).
- `selectactions` (optional): JSON string — toolbar buttons when multi-select is enabled (different shape from `actions`; see **Multi-select**).
- `selectrow`, `enableselect`, `selected` (optional): strings (selection mode / ids).
- `size`, `page`, `pages`, `total` (optional): numbers as strings for pagination / server totals.
- `externalfilter` (optional): boolean string — host-driven filters.
- `disablepagination` (optional): boolean string.
- `add_item` (optional): boolean string — show the **Add** button (see **Add button** below).
- `i18nlang` (optional): string (`it`, `en`, … per registered i18n).
- `page_size_type` (optional): `"number"` | `"select"`.
- `page_size_options` (optional): comma-separated sizes, e.g. `"10,25,50,100"`.
- `sort_default` (optional): string — column key for initial sort.
- `sort_default_label` (optional): string — label for default sort option.
- `disable_paginate_sort` (optional): boolean string.
- `sort_strict_direction` (optional): boolean string.
- `sort_direction` (optional): string — current sort direction.
- **`is_loading` (optional):** boolean string (`"yes"` / `"true"`) or boolean in frameworks — Bulma skeleton rows in the tbody (row count = `size`) and one **`skeleton-block`** over hidden **`hb-paginate`**.
- **`fixed_columns` (optional):** `"yes"` or `"no"` (default `"no"`). When `"yes"`, columns use `table-layout: fixed` with equal widths and text overflow is handled with ellipsis. When `"no"`, columns size dynamically based on content (`table-layout: auto`).

### Actions (`actions` and `_actions`)

Use **`actions`** for a column of buttons that is the same for every row (one set per row, same definitions). Use **`_actions`** on a single row object for buttons only on that row (e.g. variable actions per record). Both use the same **`IActionButton`** shape.

Pass **`actions`** / per-row **`_actions`** as JSON (HTML attribute string or parsed array in JS). Each entry can drive a **text** or **icon** button; icon buttons use [Bootstrap Icons](https://icons.getbootstrap.com/) and optional **`btnClass`** for Bulma-coloured hosts (see below).

| Field | Purpose |
| --- | --- |
| `name` | Stable id for the action; emitted in events as `action`. |
| `type` | `"text"` — show `iconOrText` as the button label; `"icon"` — show a Bootstrap Icon (see **Action icons (Bootstrap Icons)**). |
| `iconOrText` | If `type: "text"`, the visible label. If `type: "icon"`, the icon glyph name only (see **Action icons (Bootstrap Icons)**). |
| `btnClass` | Optional colour/style token (see **Button colours (`btnClass`)**). If omitted, the button is **`is-link`** — **outlined** unless `btnFill` is true. |
| `btnFill` | Boolean; when `true` the button is **filled**. When `btnClass` is omitted, default is **`is-link is-outlined`**. For explicit **ghost** / **light** tokens, outlined is not applied. |
| `disabled` | If true, the control is disabled. |
| `confirm` | If set, clicking opens **`hb-dialog`** confirm; on confirm the table emits `tableaction` / related confirm events. |
| `edit` | If set (and no `confirm`), clicking opens **`hb-dialogform`** with the given `schema`. |
| `tooltip` | Optional; passed to **`hb-tooltip`** for icon buttons. |

**Behaviour without `confirm` / `edit`:** the table dispatches **`tableaction`** with `{ itemId, action }` where `action` is the button `name`. For **`_actions`** on a row, the same flow applies but the event **`tableCustomActionClick`** is also fired with `{ itemId, action }` so you can distinguish custom per-row actions if needed.

#### Action icons (Bootstrap Icons)

For `type: "icon"`, set **`iconOrText`** to the **glyph name** from Bootstrap Icons: the segment that appears **after** `bi-` in the official class name.

- Docs show `<i class="bi bi-pencil"></i>` → use `"iconOrText": "pencil"`.
- For hyphenated names (e.g. `house-door`) use the full slug: `"house-door"`.
- Do **not** repeat the `bi-` prefix in the value (use `"pencil"`, not `"bi-pencil"`). The table renders `<i class="bi-{iconOrText}"></i>` (e.g. `class="bi-pencil"`).

**Global `actions` (HTML):**

```html
actions='[{"name":"edit","type":"icon","iconOrText":"pencil","btnClass":"primary"}]'
```

**Per-row `_actions` inside `rows`:**

```json
{
  "_id": "1",
  "name": "Alice",
  "_actions": [
    { "name": "preview", "type": "icon", "iconOrText": "eye", "btnClass": "info" },
    { "name": "remove", "type": "icon", "iconOrText": "trash", "btnClass": "danger" }
  ]
}
```

The component loads Bootstrap Icons CSS via `<svelte:head>` so the font is available for those `<i>` elements.

#### Button colours (`btnClass`)

The table maps **`btnClass`** to **Bulma** `button` modifiers on the hosting control (not literal Bootstrap `btn btn-*` classes in the DOM).

| You pass (`btnClass`) | Button classes applied |
| --- | --- |
| `primary` | `button is-primary` |
| `secondary` | `button is-light` |
| `success` | `button is-success` |
| `danger` | `button is-danger` |
| `warning` | `button is-warning` |
| `info` | `button is-info` |
| `light` | `button is-light` |
| `dark` | `button is-dark` |
| `link` | `button is-link` (outlined when not `btnFill`, like other colours) |

Matching is case-insensitive; an optional `btn-` prefix is stripped (e.g. `btn-danger` and `danger` are equivalent). If **`btnClass` is omitted**, actions use **`button is-link is-outlined`**; with **`btnFill: true`**, **`button is-link`**. By default all colour buttons (except **ghost** and **light**) are **outlined** (`is-outlined`); set **`btnFill: true`** for a filled button.

**Legacy:** if you pass a string that already starts with `button ` (e.g. `button is-warning`), it is used as-is. Strings like `btn btn-danger` are normalised to the same mapping as `danger`.

**Example (icons + colours):**

```html
<hb-table
  headers='[{"label":"Name","key":"name"},{"label":"Tools","key":"x","type":"actions","nosort":true}]'
  rows='[{"_id":"1","name":"Alice","_actions":[{"name":"star","type":"icon","iconOrText":"star","btnClass":"warning"}]}]'
  actions='[
    {"name":"edit","type":"icon","iconOrText":"pencil","btnClass":"primary"},
    {"name":"delete","type":"icon","iconOrText":"trash","btnClass":"danger"}
  ]'
></hb-table>
```

### Multi-select (`enableselect`, `selectactions`)

- Set **`enableselect`** to `"yes"` / `"true"` and pass **`selectactions`**: an array of `{ name, type, iconOrText }` used for bulk actions on checked rows. **`actiononselected`** fires with `{ key: name, selectedItems }`.
- Row checkboxes appear only when **`selectactions`** has entries.

### Add button (`add_item`)

Set **`add_item`** to `"yes"` / `"true"` (or boolean `true` in frameworks) to show an **Add** control in the toolbar below the table (`part="table-actions"`): it is placed **right after the multi-select settings (gear) button** when **`selectactions`** is set; if there is no gear, it is the first control in that strip. Toolbar controls use the same Bulma **`pagination-link`** styling as **`hb-paginate`**. The gear uses **`is-current`** when multi-select mode is on; the **Add** control uses **`is-current`** when visible so its colors match the **current page** link in **`hb-paginate`** (Bulma pagination selected / **`--bulma-link`** chain). It is meant to let the user start creating a **new row**; the table does not insert data by itself.

- **Visibility:** the button is rendered only when **`enableselect`** is *not* active (multi-select gear mode hides it).
- **Click:** dispatches the **`addItem`** custom event with payload **`{ id }`**, where **`id`** is the table’s own **`id`** attribute (empty string if unset). The host should listen for `addItem` and open a form, navigate to a create page, call an API, etc.
- **Label / icon:** default content is a Bootstrap **plus** icon (`bi-plus`). Override with the **`add-button-content`** slot if you need text or a different icon.

```html
<hb-table
  id="my-list"
  add_item="yes"
  headers='[{"label":"Title","key":"title"}]'
  rows='[{"_id":"1","title":"A"}]'
></hb-table>
<script>
  document.querySelector("hb-table").addEventListener("addItem", (e) => {
    console.log("Create new item for table:", e.detail.id); // "my-list"
  });
</script>
```

### Events

- `pageChange`: `{ page; pages }`.
- `changePageSize`: `{ page_size: number }`.
- `removeFilter`: `{ key: string }`.
- `changeFilter`: `{ filter: IFilter }`.
- `tableCustomActionClick` / `tableaction`: `{ itemId; action }`.
- `cellclick`: `{ rowId; cellId }`.
- `actiononselected`: `{ key; selectedItems: string[] }`.
- `clickonrow`: `{ itemId }`.
- `changeSort`: `{ sortedBy; sortedDirection }`.
- `showConfirmModal` / `showConfirmModalForm`: modal visibility payloads with `action` and `detail: { id; show }`.
- `confirmActionModal` / `confirmActionModalForm`: `{ action; id; confirm }`.
- `clipboardCopyText`: `{ text: string }`.
- **`addItem`:** `{ id: string }` — fired when the **Add** button is clicked; `id` is the table `id` attribute (see **Add button**).

### Usage notes

- **Slots:** `add-button-content` (Add button body), `buttons-container` (extra toolbar content).
- **CSS parts:** `table`, `table-actions`, `selected-row`, `common-row`.
- **Toolbar order:** **settings** (gear) → **Add** (when `add_item` and not in multi-select mode) → bulk **select actions** (when enabled) → **`buttons-container`** slot → **`hb-paginate`** (inline-end). The first **`nav.pagination.is-small`** group uses **`margin-inline-start`** from **`--hb-table-toolbar-settings-margin-inline-start`** (default `--bulma-block-spacing`). Optional **`--hb-paginate-list-gap`** on the host also applies between toolbar **`pagination-link`** items, matching **`hb-paginate`**.
- **i18n:** Italian and English in docs metadata; set `i18nlang` accordingly.
- **`hb-paginate`:** embedded when pagination is enabled; while **`is_loading`**, it stays in the DOM (hidden) under the skeleton overlay so props stay aligned — user interaction is blocked until loading ends.
- **Pagination while loading:** `is_loading` does not recalculate `page` / `pages` by itself. With **client-side** pagination, if you clear `rows` during a refresh, the `$effect` that derives `pages` from row count does not run on an empty list, so **`pages` can stay at the previous value** until new rows arrive — keep `page`, `pages`, and `total` aligned with your API if needed.
- See `extra/docs.ts` for Storybook-style examples.

### Bulma light / dark theme

The shadow root sets Bulma’s palette on `:host`. **Dark mode** follows `prefers-color-scheme: dark`. To **force** a palette when that does not match your app shell, set **`data-theme`** or **`.theme-dark` / `.theme-light`** on **`html` or `body`** (not only an inner layout node — inner `data-theme` is ignored so wrappers do not lock the table to light), or set **`data-theme`** on **`hb-table`** itself. Otherwise the host can keep the light-only `--bulma-*` set and **filled** / default **`is-light`** actions look wrong in real dark UIs. Action icons in cells use **`color: inherit`** so they match the button’s Bulma foreground in both themes.

### Minimal HTML example

```html
<hb-table
  headers='[{"label":"Title","key":"title","type":"string"},{"label":"When","key":"time","type":"datetime"}]'
  rows='[{"_id":"1","title":"Row A","time":"2024-01-01T12:00:00.000Z"}]'
  page="1"
  pages="1"
></hb-table>
```

### Loading example

```html
<hb-table
  is_loading="yes"
  size="10"
  headers='[{"label":"Title","key":"title"}]'
  rows='[]'
  total="0"
  page="0"
  pages="0"
></hb-table>
```

### Actions — handling clicks

Use the **`actions`** / **`_actions`** JSON shape and icon / `btnClass` rules in the **Actions** section above. Listen for **`tableaction`** with `{ itemId, action }`; for per-row **`_actions`**, **`tableCustomActionClick`** is also emitted if you need to separate custom row tools from the global column.