# `hb-table` — integrator guide

**Category:** data · **Tags:** data, table · **Package:** `@htmlbricks/hb-table`

## Summary

`hb-table` is a Bulma-styled **data table** web component. You drive it with JSON **`headers`** and **`rows`**. Each row must include **`_id`**. The table can sort columns, filter from the header row (text, enum, date range), format values (nested keys with dot notation, datetimes via dayjs), copy cell text, highlight rows, and handle row / cell clicks. Optional **global `actions`** and per-row **`_actions`** open **`hb-dialog`** (confirm) or **`hb-dialogform`** (schema-driven edit). Multi-select is optional via **`selectactions`**. The footer embeds **`hb-paginate`** for page size, sort controls, and server-style workflows (`externalfilter`, **`total`**). **`is_loading`** shows Bulma skeleton placeholders while keeping pagination props in sync. Internationalization uses **`i18nlang`**.

### Inner components

The implementation registers and embeds **`hb-paginate`**, **`hb-dialog`**, **`hb-dialogform`**, and **`hb-tooltip`**. Bootstrap Icons are loaded for action icons. Theme behaviour follows Bulma **`--bulma-*`** variables; dark mode follows `prefers-color-scheme` unless you override with **`data-theme`** or **`.theme-light` / `.theme-dark`** on **`html`**, **`body`**, or the **`hb-table`** host (inner layout-only `data-theme` is ignored so nested wrappers do not force light mode). Action icons inherit the button foreground colour.

---

## Custom element

| Tag        | Package              |
| ---------- | -------------------- |
| `hb-table` | `@htmlbricks/hb-table` |

---

## HTML integration rules

- **Attributes** use **snake_case** and are **strings** in plain HTML.
- **String flags:** HTML attributes are always strings — e.g. **`externalfilter`** is **`yes`** / **`no`** (or **`true`** / **`false`**); same idea for **`disablepagination`**, **`add_item`**, **`is_loading`**, **`disable_paginate_sort`**, **`sort_strict_direction`** (the component coerces these at runtime).
- **Arrays / objects** (`rows`, `headers`, `actions`, `selectactions`): pass a **JSON string** on the attribute, or assign parsed objects from JavaScript.
- **Numbers** (`size`, `page`, `pages`, `total`, …): pass numeric values as **strings** in HTML (e.g. `page="0"`).
- **Pagination index:** **`page`** and `pageChange` **`detail.page`** are **zero-based** (first page is **`0`**). Align with APIs that use 1-based pages in your host code.

---

## Attributes and props

| Attribute / prop | Required | Description |
| ---------------- | -------- | ----------- |
| `headers` | yes | JSON array of column definitions (`ITableHeader`). See [Column headers](#column-headers). |
| `rows` | yes | JSON array of row objects. Each row **must** have **`_id`**. Optional **`_actions`**, **`_evidenced`**. |
| `id` | no | Host id; echoed on **`addItem`** as `detail.id`. |
| `style` | no | Host inline style string (typed on component; use as needed). |
| `size` | no | Page size (default **12** in implementation). |
| `page` | no | Current page index, **zero-based**. |
| `pages` | no | Total number of pages (non-negative). |
| `total` | no | Total row count for server-side pagination (passed through to pagination when set). |
| `actions` | no | JSON **`IActionButton[]`** rendered as one shared actions column for every row. |
| `selectactions` | no | JSON array of bulk toolbar actions when multi-select is enabled (`{ name, type, iconOrText }`). |
| `selectrow` | no | When enabled (`yes` / `true`), row clicks dispatch **`clickonrow`** (selection / navigation UX). |
| `enableselect` | no | Toggles multi-select mode (gear); when on, row checkboxes appear if **`selectactions`** has entries. |
| `selected` | no | Row **`_id`** to highlight (`::part(selected-row)`). |
| `externalfilter` | no | String **`yes`** / **`true`** → server mode: header filters still emit events but **client-side** filtering / sorting of `rows` is skipped. Omit the attribute or use **`no`** / **`false`** for local filter + sort on `rows`. |
| `disablepagination` | no | Hides **`hb-paginate`**. |
| `add_item` | no | Shows the **Add** control in the footer toolbar when not in multi-select gear mode. |
| `i18nlang` | no | Language code (metadata lists **`en`**, **`it`**). |
| `page_size_type` | no | **`number`** (free input) or **`select`** (dropdown) for page size UI. |
| `page_size_options` | no | Comma-separated sizes for select mode, e.g. **`10,25,50,100`**. |
| `sort_default` | no | Header **`key`** that should match the initial sort column when using pagination sort UI. |
| `sort_default_label` | no | Label for the “default” sort option in the pagination bar (e.g. “Relevance”). |
| `disable_paginate_sort` | no | Hides sort field / direction controls on **`hb-paginate`**. |
| `sort_strict_direction` | no | When set, sort direction toggles **asc** / **desc** only (no “default” cleared state from column cycling). |
| `sort_direction` | no | Current direction: **`asc`**, **`desc`**, or **`default`**. |
| `is_loading` | no | When **`yes`** / **`true`**, tbody shows Bulma skeleton rows (count from **`size`**, max **100**); footer shows a **`skeleton-block`** over hidden **`hb-paginate`** so **`page` / `pages`** stay wired. |
| `fixed_columns` | no | **`yes`**: `table-layout: fixed`, equal column widths, ellipsis overflow. **`no`** (default): auto table layout. |

---

## Column headers (`ITableHeader`)

Each entry describes one column.

| Field | Type / values | Role |
| ----- | -------------- | ---- |
| `label` | string | Header label. |
| `key` | string | Row field key; supports **dot paths** (e.g. `testnested.nested`). |
| `type` | `string` (default), `datetime`, `enum`, `number`, `ip`, `actions` | Affects formatting, search UI, and sort participation. **`ip`**: values `A.B.C.D` or `A.B.C.D/p` (IPv4, optional CIDR `p` 0–32); client-side sort orders by numeric address then prefix length. |
| `format` | string | For **`datetime`**, dayjs format string (e.g. **`DD MMMM YYYY`**). |
| `search` | boolean | Enables header search (text includes, enum select, or date range for searchable datetime). |
| `click` | boolean | Makes cells dispatch **`cellclick`** when clicked (non-actions columns). |
| `select` | string[] | For **`enum`**, allowed values. |
| `nosort` | boolean | Excludes column from header sort cycling. |
| `sortBy` | `asc` \| `desc` \| `none` | Initial sort indicator on column (internal default **`none`**). |
| `truncateAt` | number | Truncate displayed string length (with `...` when trimmed). |
| `copyTxt` | boolean | Shows copy control; successful copy dispatches **`clipboardCopyText`**. |
| `tooltip` | object | Optional tooltip config for the header (see **`hb-tooltip`** / `TTooltip`). |
| `centerCell` | boolean | When **`true`**, body cells (and skeleton placeholders) for this column use **centered** horizontal text/content. |
| `centerHeader` | boolean | When **`true`**, header content for this column is centered: title row centers the **label** (tooltip + text) in the space beside the sort control; sort stays **left**. Filter rows (`search`) also center controls in the cell. |

**`type: "actions"`:** Renders **`_actions`** for that row (per-row buttons). You usually set **`nosort: true`**. A dummy **`key`** is still required for the column definition.

---

## Rows (`IRow`)

- **`_id`:** string, **required**, stable row identifier (events use it as `itemId` / `rowId`).
- **`_actions`:** optional **`IActionButton[]`** for the **`actions`**-type column.
- **`_evidenced`:** optional boolean; marks the row for emphasised styling.
- Other keys are your data fields, aligned with header **`key`** paths.

---

## Filters (`IFilter`)

Emitted on **`changeFilter`** and used internally when not in **`externalfilter`** mode:

- `key`, `type` (`datetime` \| `string` \| `enum` \| `number` \| `ip`), optional `value`, and for datetime range `start` / `end` (**`Date`** in TS; serialized appropriately in JSON props).

---

## Actions (`IActionButton`)

Used for **`actions`** (global column) and **`_actions`** (per row).

| Field | Description |
| ----- | ----------- |
| `name` | Stable id; appears as **`action`** in events. |
| `type` | **`text`** — `iconOrText` is the label; **`icon`** — Bootstrap Icon **glyph** (name after `bi-`, e.g. **`pencil`**, not **`bi-pencil`**). |
| `iconOrText` | Label or icon name. |
| `btnClass` | Optional Bulma colour token (`primary`, `danger`, `link`, …). **Omitted** → **`is-link`**. Case-insensitive; optional **`btn-`** prefix stripped. Strings starting with **`button `** are passed through (legacy). |
| `btnFill` | If **`true`** / **`yes`**, filled button (no outline). If omitted, **`false`**, or **`no`**, the button is **not** filled and **`is-outlined`** only when the **resolved Bulma theme on this host** is dark (reads `--bulma-scheme-brightness` / `--bulma-hb-def-scheme-brightness` / `--bulma-scheme-main-l` from `getComputedStyle(host)`, then falls back to `data-theme` / `.theme-*` / `prefers-color-scheme`). |
| `disabled` | Disables the control. |
| `confirm` | Opens **`hb-dialog`** with `title`, `content`, `confirmLabel`, optional `denyLabel`, `text`. |
| `edit` | Opens **`hb-dialogform`** with `title`, `schema` (form schema), `confirmLabel`, optional `denyLabel`, `description`, `text`. |
| `tooltip` | Optional tooltip on icon buttons. |

**Click behaviour**

- **Global `actions` column:** **`handleClickOnAction`** — if the button has **`confirm`** or **`edit`**, the modal opens; otherwise **`tableaction`** fires with `{ itemId, action }`.
- **Per-row `_actions`:** **`handleClickOnCustomAction`** always emits **`tableCustomActionClick`** with `{ itemId, action }`. If **`confirm`** / **`edit`** is set, the corresponding dialog opens; there is **no** **`tableaction`** for plain per-row clicks (use **`tableCustomActionClick`** only).

---

## Custom events

Listen on the host element. Names are **case-sensitive** as in the table.

| Event | `detail` (TypeScript `Events`) | Notes |
| ----- | ------------------------------ | ----- |
| `pageChange` | `{ page: number; pages: number }` (see typings) | **Zero-based** `page`. The component dispatches **`detail` with at least `{ page }`**; keep total **`pages`** aligned via the **`pages`** attribute (and your server state) when integrating. |
| `changePageSize` | `{ page_size: number }` | Page size changed; table resets to page **0** internally when not external. |
| `removeFilter` | `{ key: string }` | Filter cleared for `key`. |
| `changeFilter` | `{ filter: IFilter }` | Filter added/updated. |
| `tableCustomActionClick` | `{ itemId: string; action: string }` | Per-row **`_actions`** (distinction from global column). |
| `tableaction` | `{ itemId: string; action: string }` | Global **`actions`** column without confirm/edit. |
| `cellclick` | `{ rowId: string; cellId: string }` | Fired for clickable cells; **`cellId`** is the column header **`key`** (TypeScript `Events` uses `cellId`). |
| `actiononselected` | `{ key: string; selectedItems: string[] }` | Bulk toolbar action (`key` = button `name`). |
| `clickonrow` | `{ itemId: string }` | Row click when **`selectrow`** is enabled. |
| `changeSort` | `{ sortedBy?: string; sortedDirection: "asc" \| "desc" \| "default" }` | Column header sort or pagination sort control. |
| `showConfirmModal` | `{ action: string; detail: { id: string; show: boolean } }` | Confirm dialog visibility. |
| `showConfirmModalForm` | `{ action: string; detail: { id: string; show: boolean } }` | Form dialog visibility. |
| `confirmActionModal` | `{ action: string; id: string; confirm: boolean }` | Confirm dialog result. |
| `confirmActionModalForm` | `{ action: string; id: string; confirm: boolean }` | Form dialog result; **`detail`** merges the dialog payload with the table’s action context (see typings — extra keys may appear). |
| `clipboardCopyText` | `{ text: string }` | After copy-to-clipboard. |
| `addItem` | `{ id: string }` | Add control clicked; `id` is the table’s **`id`** attribute or empty string. |

---

## Slots

| Slot | Purpose |
| ---- | ------- |
| `add-button-content` | Replaces the default **plus** icon for **Add** when **`add_item`** is on. |
| `buttons-container` | Extra toolbar controls before **`hb-paginate`** inside **`part="table-actions"`**. |

---

## CSS parts

| Part | Exposes |
| ---- | ------- |
| `table` | Root `<table>` (headers, body, embedded dialogs). |
| `table-actions` | Footer toolbar: settings, add, bulk actions, **`buttons-container`**, **`hb-paginate`**. |
| `selected-row` | `<tr>` when **`selected`** matches the row **`_id`**. |
| `common-row` | Standard data `<tr>`. |

Toolbar order (conceptually): **settings (gear)** → **Add** (when allowed) → **bulk actions** → **`buttons-container`** → **`hb-paginate`**. Spacing can be tuned with **`--hb-table-toolbar-settings-margin-inline-start`** and **`--hb-paginate-list-gap`** on the host where relevant.

---

## CSS custom properties (host)

Dimensional spacing follows **Bulma rhythm variables** (`--bulma-block-spacing`, `--bulma-size-normal`, `--bulma-control-height`, `--bulma-pagination-item-margin`, …) with **unitless `*-mul` knobs** on the host. Override a `--hb-table-*-mul` to tune density, or set the resolved `--hb-table-*` / `--bulma-table-cell-padding` for full control.

| Variable | Role |
| -------- | ---- |
| `--hb-table-*-mul` | Unitless multipliers (see `extra/docs.ts` `styleSetup.vars` for defaults). |
| `--hb-table-footer-margin-inline-end` | `calc(var(--bulma-block-spacing) * mul)` — footer pagination inline-end margin. |
| `--hb-table-sk-pagination-height` | `calc(var(--bulma-control-height) * mul)` — loading skeleton height. |
| `--hb-table-sk-pagination-min-width` | `min(calc(var(--bulma-size-normal) * mul), 100%)` — skeleton min width while loading. |
| `--hb-table-sk-pagination-offset-inline-end` | `calc(var(--bulma-block-spacing) * mul)` — extra inline-end offset when loading. |
| `--hb-table-toolbar-settings-margin-inline-start` | `calc(var(--bulma-block-spacing) * mul)` — space before the first toolbar control group. |
| `--hb-table-actions-gap` | `calc(var(--bulma-block-spacing) * mul)` — gap in the actions flex row. |
| `--bulma-table-cell-padding` | `calc(var(--bulma-size-normal) * block-mul) calc(var(--bulma-size-normal) * inline-mul)` — cell padding (Bulma token). |
| `--hb-table-cell-line-height` | Unitless `line-height` on `td` / `th` (default `1.1`); lower for denser multi-line cells. |

Additional theming uses Bulma **`--bulma-*`** variables on the host / document as for other `hb-*` components.

---

## Behaviour notes for integrators

1. **Client vs server data:** Without **`externalfilter`**, filters and column sort run against the in-memory **`rows`** you pass. With **`externalfilter`**, use **`changeFilter`**, **`changeSort`**, **`pageChange`**, and **`changePageSize`** to query the server and then assign new **`rows` / `total` / `pages` / `page`**.  
2. **Empty rows while loading:** With client-side pagination, clearing **`rows`** during refresh can leave **`pages`** derived from old state until new data arrives—keep **`page`**, **`pages`**, and **`total`** consistent with your API.  
3. **Multi-select:** Checkboxes appear only when **`selectactions`** is a non-empty array and **`enableselect`** is on. The **Add** button is hidden while the gear multi-select mode is active.  
4. **Examples:** See **`extra/docs.ts`** (`examples` array) for Storybook-style datasets: page size modes, sort defaults, strict direction, loading skeleton, fixed columns, tooltips, truncate/copy, and confirm/form actions.

---

## Minimal examples

**Basic table**

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

**Loading state**

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

**Add row entry point**

```html
<hb-table
  id="orders"
  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 item for table id:", e.detail.id);
  });
</script>
```

---

## Types in this repo

Authoritative TypeScript shapes for props and events live in **`types/webcomponent.type.d.ts`** (`Component`, `Events`, `ITableHeader`, `IRow`, `IActionButton`, `IFilter`). Generated consumer typings are produced on **`npm run build:wc`** (`types/html-elements.d.ts`, `types/svelte-elements.d.ts`).
