# `PhoenixKitWeb.Components.Core.TableDefault`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.164/lib/phoenix_kit_web/components/core/table_default.ex#L1)

A basic table component with daisyUI styling.

Supports an optional card/table view toggle for responsive layouts. When `items`
is provided or `toggleable` is true, renders both a table view (desktop default)
and a card view (mobile default) with an optional toggle button.

## Examples

### Basic table (unchanged API)

    <.table_default>
      <.table_default_header>
        <.table_default_row>
          <.table_default_header_cell>Name</.table_default_header_cell>
          <.table_default_header_cell>Email</.table_default_header_cell>
        </.table_default_row>
      </.table_default_header>
      <.table_default_body>
        <.table_default_row>
          <.table_default_cell>John Doe</.table_default_cell>
          <.table_default_cell>john@example.com</.table_default_cell>
        </.table_default_row>
      </.table_default_body>
    </.table_default>

### With card/table toggle

    <.table_default
      id="users-table"
      toggleable
      items={@users}
      card_title={fn user -> user.name end}
      card_fields={fn user -> [
        %{label: "Email", value: user.email},
        %{label: "Role", value: user.role}
      ] end}
    >
      <.table_default_header>...</.table_default_header>
      <.table_default_body>...</.table_default_body>
      <:card_actions :let={user}>
        <.button size="sm" navigate={~p"/users/#{user.id}"}>View</.button>
      </:card_actions>
      <:toolbar_title>
        <span class="text-sm text-base-content/60">{length(@users)} users</span>
      </:toolbar_title>
      <:toolbar_actions>
        <.button size="sm" navigate={~p"/users/new"}>Add User</.button>
      </:toolbar_actions>
    </.table_default>

# `drag_handle_cell`

Drag-handle cell for table-view DnD reorder.

Renders a `<td>` containing the `hero-bars-3` grip icon, with the
classes the SortableGrid hook needs to find it (`.pk-drag-handle`)
plus the canonical hide-until-hover behavior used elsewhere in the
codebase (catalogue category rows, entities card view, etc.):

  * Default: `opacity-0` — icon is invisible.
  * Row hover: `group-hover/row:opacity-100` — icon fades in.

Requires the enclosing `<.table_default_row>` to carry the `group/row`
marker class — which it does by default, so consumers don't have
to wire anything beyond placing this cell + the matching
`<.drag_handle_header_cell>` in the header. The named group keeps the
reveal keyed to row hover without clobbering any unnamed `group-hover:`
utilities a consumer nests inside a cell.

Pair this with a `<tbody phx-hook="SortableGrid">` setup (see the
`<.table_default>` moduledoc / phoenix_kit_projects reference call
sites). The cell only renders the affordance — the hook owns the
drag mechanics.

## Attributes

* `class` (`:any`) - Defaults to `"w-8"`.
* `title` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.

# `drag_handle_header_cell`

Empty `<th>` sized to match the drag-handle column. Sits in the
header row alongside `<.bulk_select_header_cell>` and the regular
`<.table_default_header_cell>`s.

Separate component (rather than a bare `<.table_default_header_cell class="w-8" />`)
so the intent is obvious at the call site, and the width default
matches `<.drag_handle_cell>`'s without consumers having to keep
them in sync.

## Attributes

* `class` (`:any`) - Defaults to `"w-8"`.

# `search_toolbar`

Renders a search input with a magnifying glass icon and debounce.

By default emits `phx-change="search"` with a 300ms debounce. When
`on_submit` is provided the input is wrapped in a `<form>` element.

## Attributes

* `value` (`:string`) (required)
* `on_change` (`:string`) - Defaults to `"search"`.
* `on_submit` (`:string`) - Defaults to `nil`.
* `placeholder` (`:string`) - Defaults to `nil`.
* `debounce` (`:integer`) - Defaults to `300`.
* `name` (`:string`) - Defaults to `"search"`.
* `target` (`:any`) - Defaults to `nil`.
* `class` (`:any`) - Defaults to `""`.

# `sort_header_cell`

Renders a sortable table header cell.

When `sort` is nil, renders an inert `<th>` label. When `sort` is a map,
renders a clickable button emitting `toggle_sort` (or a custom event) with
`phx-value-by` set to the field key.

## States rendered

- **Inactive column** (sortable, not the current sort) — faint
  `hero-chevron-up-down-mini` hint, strengthens on hover via `group-hover`.
- **Active asc** / **Active desc** — solid chevron-up / chevron-down.
- **Loading** (during in-flight click) — all chevrons hide and a
  `loading-spinner` shows; `pointer-events-none` blocks double-clicks;
  button dims via `opacity-60`. Driven by Phoenix's auto-applied
  `.phx-click-loading` class — no consumer wiring needed.
- **Active column with an unrecognised `sort.dir`** (defensive) — falls
  back to the inactive up-down hint rather than rendering no icon.

Atom or string `sort.dir` values are accepted (`:asc`/`:desc`/`"asc"`/`"desc"`).

## Attributes

* `field` (`:atom`) (required)
* `sort` (`:map`) - Current sort: %{by: atom, dir: :asc | :desc}. When nil, renders inert label. Defaults to `nil`.
* `event` (`:string`) - Defaults to `"toggle_sort"`.
* `target` (`:any`) - Defaults to `nil`.
* `align` (`:atom`) - Defaults to `:left`. Must be one of `:left`, `:right`, or `:center`.
* `class` (`:any`) - Defaults to `""`.
* Global attributes are accepted. Supports all globals plus: `["colspan", "rowspan"]`.
## Slots

* `inner_block` (required)

# `table_default`

Renders a table with daisyUI styling.

When `items` is provided or `toggleable` is true, renders a responsive wrapper
with both table and card views, plus an optional desktop toggle button.
Otherwise renders the classic table-only layout (fully backward compatible).

## Attributes

* `id` - Element ID, required when using card/table toggle (optional)
* `class` - Additional CSS classes (optional)
* `variant` - Table variant: "default", "zebra", "pin-rows", "pin-cols" (optional, default: "default")
* `size` - Table size: "xs", "sm", "md", "lg" (optional, default: "md")
* `toggleable` - Show card/table toggle buttons on desktop (optional, default: false)
* `items` - List of items for card view rendering (optional, default: [])
* `card_title` - Function that returns the card title for an item (optional)
* `card_fields` - Function that returns a list of `%{label: string, value: any}` for an item (optional)
* `card_class` - Per-card wrapper class. String OR 1-arity function `(item) -> string`.
  Default `"card card-sm bg-base-200 shadow-sm"`. Override when the consumer needs a
  different look (e.g. larger padding, conditional opacity for disabled rows).
* `storage_key` - localStorage key for persisting view preference, falls back to `id` in JS (optional)
* `rest` - Additional HTML attributes (optional)

## Slots

* `inner_block` - Table content (thead, tbody, etc.)
* `card_body` - Fully-custom card content (receives item via `:let`). When provided,
  REPLACES the prescribed `card_header` + `card_title` + `card_fields` rendering — the
  consumer owns the inside of `<div class="card-body">`. `card_actions` footer still
  renders if provided. Use for rich cards with badges, icon-prefixed rows, custom layouts.
* `card_media` - Media region (image/thumbnail/video preview) rendered ABOVE the card
  body, receives item via `:let`. Use for thumbnails, cover images, document previews.
  The slot owns its own padding/background — wrap content in a styled container.
* `card_actions` - Action buttons rendered in each card footer (receives item via :let)
* `toolbar_title` - Title/content rendered at the start of the toolbar row
* `toolbar_actions` - Buttons rendered in the toolbar before the view toggle

## Controlled view mode

By default the card/table toggle is driven entirely client-side (JS hook + localStorage).
Pass `view_mode="card"` or `view_mode="table"` to take control from the assigns side —
the component then renders ONLY that view (no JS toggle) and the toolbar buttons emit
`phx-click={view_event}` with `phx-value-mode="card"|"table"` so the consumer can drive
state via `push_patch` (URL-backed) or `assign`. Use this when the view choice must
survive across LV navigation or be part of the URL.

## Attributes

* `id` (`:string`) - Defaults to `nil`.
* `class` (`:any`) - Defaults to `""`.
* `variant` (`:string`) - Defaults to `"default"`. Must be one of `"default"`, `"zebra"`, `"pin-rows"`, or `"pin-cols"`.
* `size` (`:string`) - Defaults to `"md"`. Must be one of `"xs"`, `"sm"`, `"md"`, or `"lg"`.
* `toggleable` (`:boolean`) - Defaults to `false`.
* `show_toggle` (`:boolean`) - Defaults to `true`.
* `items` (`:list`) - Defaults to `[]`.
* `card_title` (`:any`) - Defaults to `nil`.
* `card_fields` (`:any`) - Defaults to `nil`.
* `card_class` (`:any`) - Per-card wrapper class. String or 1-arity fn `(item) -> string`. When fn, called per item. Defaults to `"card card-sm bg-base-200 shadow-sm"`.
* `storage_key` (`:string`) - Defaults to `nil`.
* `wrapper_class` (`:string`) - Defaults to `"rounded-lg shadow-md overflow-x-auto overflow-y-clip"`.
* `card_grid_class` (`:string`) - Layout classes for the card-view grid (column density, gaps). Must NOT include a `display` utility (`grid`/`hidden`) — the component sets `display` per view-mode branch so controlled table mode can emit `hidden` cleanly. Override to change column count, e.g. a denser `gap-4 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5`. Must be a literal in a Tailwind-scanned source so the classes are compiled — a dynamically-built string won't be in the generated CSS. Defaults to `"gap-4 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4"`.
* `view_mode` (`:string`) - Controlled view selector. When set, renders ONLY that view and disables the JS toggle; toggle buttons emit `view_event` with `phx-value-mode`. When nil, falls back to the JS hook + localStorage default. Defaults to `nil`. Must be one of `nil`, `"card"`, or `"table"`.
* `view_event` (`:string`) - Event name emitted by the toggle buttons in controlled mode. Defaults to `"switch_view"`.
* `on_reorder` (`:string`) - When set, the card-view container becomes a SortableGrid hook target. The table-view's tbody is owned by the inner_block — wire that side separately so desktop users get the same DnD as mobile. Defaults to `nil`.
* `reorder_scope` (`:map`) - Map of scope values exposed on the card-view container as data-sortable-scope-* attrs. Keys are lowercased and dasherized for the DOM attr; the JS hook sends them back to LV as camelCase, so an Elixir key `:category_uuid` arrives in the LV handler payload as `"categoryUuid"`. Defaults to `%{}`.
* `reorder_group` (`:string`) - SortableJS group name for cross-container drag (must match the table-view side). Defaults to `nil`.
* `item_id` (`:any`) - 1-arity function returning the data-id for a card. Defaults to `& &1.uuid`. Required when on_reorder is set. Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)
* `card_header` - Custom header for each card (receives item via :let); replaces card_title.
* `card_body` - Fully-custom card body (receives item via :let). When present, replaces card_header + card_title + card_fields rendering.
* `card_media` - Media region (image/thumbnail/video) rendered above the card body. Receives item via :let. Owns its own padding/background.
* `card_actions` - Action buttons in card footer.
* `above_cards` - Content rendered inside the card-view container, above the card grid. Hidden automatically when the JS hook switches to table mode (the wrapper has `md:hidden` toggled on).
* `sort_bar` - Always-visible sort UI rendered in its own row above the table/cards (e.g. a `<.sort_selector>`). Unlike `:above_cards`, this slot renders in both views.
* `toolbar_title` - Title or arbitrary content rendered at the start of the toolbar row.
* `toolbar_actions` - Action buttons rendered in the toolbar, before the view toggle.

# `table_default_body`

Renders a table body section.

Accepts arbitrary HTML attrs via `:rest` so consumers can wire the
`SortableGrid` hook directly onto the `<tbody>`:

    <.table_default_body
      id="endpoints-list-body"
      phx-hook="SortableGrid"
      data-sortable="true"
      data-sortable-event="reorder_endpoints"
      data-sortable-items=".sortable-item"
      data-sortable-handle=".pk-drag-handle"
    >
      ...
    </.table_default_body>

## Attributes

* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `table_default_cell`

Renders a table data cell.

## Attributes

* `class` - Additional CSS classes (optional)
* `colspan` - Number of columns to span (optional)
* `rowspan` - Number of rows to span (optional)
* `rest` - Additional HTML attributes (optional)

## Attributes

* `class` (`:any`) - Defaults to `""`.
* `colspan` (`:integer`) - Defaults to `nil`.
* `rowspan` (`:integer`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `table_default_footer`

Renders a table footer section.

## Slots

* `inner_block` (required)

# `table_default_header`

Renders a table header section.

## Attributes

* `class` - Overrides the default header styling. Default is `"bg-base-300"` — a calm,
  theme-neutral header that reads as a subtle separator from `<tbody>` instead of the
  loud daisyUI primary. Pass `"bg-primary text-primary-content"` to restore the legacy
  look, or `class=""` for a fully bare header. The string fully replaces the default;
  concatenate manually if you want to add classes on top.

## Attributes

* `class` (`:any`) - Defaults to `"bg-base-300"`.
## Slots

* `inner_block` (required)

# `table_default_header_cell`

Renders a table header cell.

`:inner_block` is optional — a self-closing call (`<.table_default_header_cell />`)
renders an empty `<th>`, useful for drag-handle / row-selection columns
that don't need a label.

## Attributes

* `class` - Additional CSS classes (optional)
* `rest` - Additional HTML attributes (optional)

## Attributes

* `class` (`:any`) - Defaults to `""`.
* Global attributes are accepted.
## Slots

* `inner_block`

# `table_default_row`

Renders a table row.

## Attributes

* `class` - Additional CSS classes (optional)
* `hover` - Enable hover effect: true/false (optional, default: true)
* `rest` - Additional HTML attributes (optional)

## Attributes

* `class` (`:any`) - Defaults to `""`.
* `hover` (`:boolean`) - Defaults to `true`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
