MishkaGervaz.Table.Templates.MediaGallery (MishkaGervaz v0.0.2)

Copy Markdown View Source

Media gallery template for image/file-heavy data.

Optimized for displaying images, videos, and files with:

  • Thumbnail grid with hover preview
  • Lightbox for full-size viewing
  • File type icons for non-image files
  • Quick actions overlay

Features

  • :filter - Filter by file type, date, etc.
  • :select - Multi-select for bulk operations
  • :bulk_actions - Bulk download, delete, move
  • :paginate - Infinite scroll preferred
  • :expand - Lightbox expansion

Column-Based Layout

MediaGallery uses the columns DSL to determine what to display:

  • First visible column becomes the thumbnail image
  • Remaining visible columns are rendered below the thumbnail in order

Use visible fn state -> state.template.name() == :media_gallery end to show columns only in MediaGallery, or visible fn state -> state.template.name() == :table end for Table-only columns.

The card's actions

A card draws its row actions in three parts: a star toggle over the thumbnail, one wide labelled button, and bordered squares beside it, red if they destroy something. split_actions/3 sorts them, and the :overlay_action / :primary_action options below name which action goes where.

The thumbnail that fails to load

A card paints its type glyph behind the thumbnail, so a broken <img> only has to be hidden for the glyph to show through; a thumbnail that does load sits on white, so a transparent PNG does not pick up the tile's tint.

The consuming app must register a MediaThumb JS hook in its admin bundle. It reads data-loaded-background from the hook element for the colour a loaded image sits on, and binds load/error on the <img> inside it.

Options

  • :columns - Number of grid columns (3, 4, 6, or 8)
  • :overlay_action - the action drawn as the star over the thumbnail (default :toggle_featured)
  • :primary_action - the action drawn wide and labelled (default :edit)

Performance

Uses @static.* for columns, ui_adapter, etc. (no re-render on user interaction) Uses @state.* for page, filter_values, etc. (re-renders when changed)

See MishkaGervaz.Table.Behaviours.Template, MishkaGervaz.Table.Templates.Shared, MishkaGervaz.Table.Templates.Table (sibling), and MishkaGervaz.UIAdapters.MediaGallery (paired UI adapter).

Summary

Functions

Whether this file is featured — the star, and the card's gold ring.

The adapter a media card is drawn with, unless the resource named one of its own.

The switcher draws this glyph for the gallery view.

The name of the category a file belongs to, or nil.

The date a file was added, formatted for a card, or nil when it is not loaded.

A file's extension as declared by its format attribute, or nil.

A file's display name, with its extension when the record carries one.

A file's size, humanised, or an em dash when the record has none.

A record's media type — :images, :videos, :documents, or nil.

Whether this record is selected, under either selection mode.

The card's three action groups: the star over the thumbnail, the one wide button, and the squares.

The URL of a record's thumbnail, or nil when it has none to show.

The background wash a file's type gets. See media_type/1.

The glyph a file wears when there is no thumbnail to show instead.

Functions

featured?(record)

@spec featured?(map()) :: boolean()

Whether this file is featured — the star, and the card's gold ring.

icon()

The switcher draws this glyph for the gallery view.

A grid of squares, matching the other card views in the admin.

media_category(record)

The name of the category a file belongs to, or nil.

Only one of the two category relationships is ever loaded — a tenant's own, or the master alias that bypasses tenancy — so both are tried and whichever holds a record wins.

media_date(record)

The date a file was added, formatted for a card, or nil when it is not loaded.

iex> media_date(%{inserted_at: ~U[2026-08-13 10:00:00Z]})
"Aug 13, 2026"

media_ext(record)

A file's extension as declared by its format attribute, or nil.

media_name(record)

A file's display name, with its extension when the record carries one.

iex> media_name(%{name: "cover", format: "png"})
"cover.png"

media_size(record)

A file's size, humanised, or an em dash when the record has none.

iex> media_size(%{size: 2048})
"2.0 KB"

media_type(record)

@spec media_type(map()) :: atom() | nil

A record's media type — :images, :videos, :documents, or nil.

render_bulk_actions(assigns)

Callback implementation for MishkaGervaz.Table.Behaviours.Template.render_bulk_actions/1.

render_error(assigns)

Callback implementation for MishkaGervaz.Table.Behaviours.Template.render_error/1.

render_pagination(assigns)

Callback implementation for MishkaGervaz.Table.Behaviours.Template.render_pagination/1.

render_template_switcher(assigns)

Callback implementation for MishkaGervaz.Table.Behaviours.Template.render_template_switcher/1.

selected?(map, record)

@spec selected?(map(), map()) :: boolean()

Whether this record is selected, under either selection mode.

select_all? inverts the meaning of the set: everything is chosen except what is in excluded_ids.

split_actions(actions, static, featured? \\ false)

@spec split_actions([map()], map(), boolean()) :: %{
  overlay: [map()],
  primary: [map()],
  secondary: [map()]
}

The card's three action groups: the star over the thumbnail, the one wide button, and the squares.

Which action gets which part is a template_options decision, defaulting to the names the media resource uses:

presentation do
  template_options [overlay_action: :pin, primary_action: :open]
end

A resource whose row_actions_layout places an action — inline, or behind a dropdown — gets all of them back under :secondary.

When featured? is true the overlay's star is filled; otherwise it stays an outline.

thumbnail_url(static, state, record)

@spec thumbnail_url(map(), map(), map()) :: String.t() | nil

The URL of a record's thumbnail, or nil when it has none to show.

The thumbnail is the first column visible to this template. Visibility is always asked as :media_gallery, whichever template is drawing, so a template that borrows the card — by delegating render_item/1 or by calling this — still sees the columns a resource gated with visible fn state -> state.template.name() == :media_gallery end.

tint(record)

@spec tint(map()) :: String.t()

The background wash a file's type gets. See media_type/1.

type_glyph(assigns)

The glyph a file wears when there is no thumbnail to show instead.

See MishkaCmsCoreWeb.Templates.AssetsList in the CMS, which draws the same file smaller.

Attributes

  • type (:atom) - Defaults to nil.
  • class (:string) - Defaults to "size-[26px]".