Board View

Kanban-style columnar display with per-column pagination

Board View Demo

Overview

BoardView displays data as a Kanban board with cards organized into columns. It supports two-dimensional grouping: vertical columns and optional accordion rows.

Features

  • Kanban-style vertical columns
  • Per-column pagination (each column loads independently)
  • Optional colored column backgrounds
  • Two-dimensional grouping (columns + accordion rows)
  • Card preview images

Basic Usage

import { useInfiniteController } from "@sparkyidea/dataview/hooks";
import { DataViewProvider } from "@sparkyidea/dataview/providers";
import { NotionToolbar } from "@sparkyidea/dataview/toolbars/notion";
import { BoardView } from "@sparkyidea/dataview/views/board-view";

const columnConfig = { propertyType: "select", propertyId: "category" };

const { controller } = useInfiniteController({
  columnQuery: (params) =>
    trpc.product.getGroup.queryOptions({
      filter: params.filter,
      groupBy: params.columnConfig,
      hideEmpty: params.hideEmpty,
      search: params.search,
    }),

  groupQuery: (params) =>
    trpc.product.getGroup.infiniteQueryOptions({
      filter: params.filter,
      groupBy: params.groupConfig,
      hideEmpty: params.hideEmpty,
      search: params.search,
      sort: params.groupConfig.sort,
      limit: 25,
    }),

  dataQuery: (params) =>
    trpc.product.getManyByColumn.infiniteQueryOptions(
      {
        columnBy: columnConfig,
        filter: params.filter,
        limit: params.limit,
        sort: params.sort,
        search: params.search,
      },
      {
        getNextPageParam: (lastPage) => {
          const hasAnyMore = Object.values(lastPage.hasNextPage).some(Boolean);
          return hasAnyMore ? lastPage.endCursor : undefined;
        },
      }
    ),
});

<DataViewProvider
  controller={controller}
  defaults={{ column: columnConfig, filter, group, limit, search, sort }}
  properties={productProperties}
>
  <NotionToolbar enableSettings>
    <DataViewTab options={productTabOptions} />
  </NotionToolbar>
  <BoardView cardSize="medium" colorColumns pagination="loadMore" />
</DataViewProvider>

Board view uses getManyByColumn for card data and columnQuery/groupQuery for header counts.

Props

PropTypeDefaultDescription
pagination"loadMore" | "infiniteScroll"-Pagination mode (no page-based mode)
cardSize"small" | "medium" | "large""medium"Card size preset
cardPreviewstring-Property ID for card preview image
colorColumnsbooleanfalseColor column backgrounds based on property colors
fitMediabooleantrueFit media to card (cover vs contain)
onCardClick(item: TData) => void-Card click handler
showPropertyNamesbooleanfalseShow property labels on cards
wrapAllPropertiesbooleanfalseWrap property text

Two-Dimensional Grouping

Board view supports both dimensions:

DimensionPurposeExample
ColumnVertical card columnsCategory: Home, Jewelry, Garden
GroupAccordion row groupingRestocked: This Week, Last Week

Data Fetching Pattern

BoardView uses getManyByColumn instead of getMany.

Table/List/Gallery: getMany()
Board:              getManyByColumn()

getManyByColumn returns a flat item list plus per-column cursor maps. Board columns are formed client-side using the active column config.

Response Structure

interface GetManyByColumnResult<T> {
  items: T[];
  startCursor: Record<string, string | null>;
  endCursor: Record<string, string | null>;
  hasNextPage: Record<string, boolean>;
  hasPreviousPage: Record<string, boolean>;
}

Pagination Modes

ModeDescription
loadMoreButton per column to append more cards
infiniteScrollAuto-load when scrolling near column bottom

Page-based pagination (page) is not supported for board views.

Default Limit: 25 cards per column

Colored Columns

<BoardView cardSize="medium" colorColumns pagination="loadMore" />

Color comes from option/status config:

const statusProperty = {
  id: "availability",
  type: "status",
  config: {
    groups: [
      { label: "Available", color: "green", options: ["In stock"] },
      { label: "Warning", color: "yellow", options: ["Low stock"] },
      { label: "Unavailable", color: "red", options: ["Out of stock"] },
    ],
  },
};

Card Click Handler

<BoardView onCardClick={(item) => router.push(`/tasks/${item.id}`)} />

Display Options

<BoardView
  cardSize="large"
  cardPreview="productImage"
  fitMedia
  showPropertyNames
  wrapAllProperties={false}
  colorColumns
  pagination="infiniteScroll"
/>