useDocyrusDataGallery
One-call wiring of a Docyrus data source to a fully configured DataGallery + toolbar (DataGridViewSelect, search, filters, sort, group, gallery display menu, card config menu) — including row fetching, saved views, pivot filters, and automatic card field detection.
Installation
pnpm dlx @docyrus/cli add @docyrus/hooks-use-docyrus-data-gallerypnpm add @docyrus/app-utils @docyrus/api-client @tanstack/react-query @tanstack/react-table @tanstack/react-virtual react-querybuilderThis hook is distributed as source. It requires an authenticated RestApiClient from @docyrus/api-client and a QueryClientProvider from @tanstack/react-query somewhere above your component tree.
Overview
useDocyrusDataGallery composes useDocyrusDataGrid and layers a gallery-specific presentation on top. The result is a ready-to-render setup with:
- Backend wiring — data source schema fetch, items query, saved views, side filters, pivot filters (all from the data-grid hook).
- Tenant-aware formatting — inherited from the composed grid hook. Date / datetime / number cells pick up the formatters installed by
<DocyrusTenantProvider>automatically; explicitformatDate/formatDateTime/formatNumberprops still win when supplied. - Gallery toolbar —
DataGridViewSelect(server-backed saved views), debounced search,DataGridFilterMenu/SortMenu/GroupMenu, plus the gallery-specificDataGalleryDisplayMenu(variant / cover style / column count / density) andDataGalleryCardConfigMenu(field bindings). - Auto-detected card config — title, description, avatar, cover image, badge, and timeline field bindings are picked from data source metadata via
detectCardConfigFromFields. - Virtualization — independent card grid virtualizer, responsive column count.
import { useDocyrusDataGallery } from '@/hooks/use-docyrus-data-gallery';
import { DataGallery } from '@/components/docyrus/data-gallery';
function ProductGallery() {
const {
galleryProps, toolbar, cardConfig, items, reload
} = useDocyrusDataGallery({
appSlug: 'sales',
dataSourceSlug: 'products',
collection: useProductCollection()
});
return (
<div className="flex flex-col gap-3">
{toolbar}
<DataGallery {...galleryProps} cardConfig={cardConfig} />
</div>
);
}Auto-Detected Card Config
When cardConfig is omitted, the hook scans the data source's field metadata and picks the first matching field for each slot. The detection prefers slugs that match known hints (e.g. name, cover, status) and falls back to field-type heuristics:
| Slot | Slug hints | Field types |
|---|---|---|
titleField | name, title, subject, label | field-text, field-identity, field-textarea, field-display, field-number |
descriptionField | description, summary, bio, tagline, subtitle | field-textarea, field-text |
coverImageField | cover, cover_image, image, thumbnail, banner, photo | field-image |
avatarField | avatar, profile_photo, profile_picture, profile_image | field-avatar |
badgeField | status, state, priority, stage, phase | field-status, field-select, field-enum, field-radioGroup, field-tagSelect |
timelineField | due, due_date, deadline, start, end, launch | field-date, field-dateTime |
bodyFields | — | All visible field slugs minus the ones already bound to a slot above minus the SYSTEM_AUDIT_SLUGS set (see below) |
Override any slot via cardConfig. The hook merges the override on top of the auto-detection — supplying cardConfig={{ titleField: 'project_code' }} overrides only the title binding and lets the other slots auto-fill from metadata.
Conservative avatar picking
avatarField deliberately only matches explicit avatar slugs / field-avatar types. Audit columns like record_owner, created_by, last_modified_by aren't candidates even though they're user fields — in many tenants a single admin owns every record, so picking one of them as the avatar would put the same user on every card. Users can still bind any column to the avatar slot manually via the Card fields menu.
Body field auto-fill skips system audit slugs
SYSTEM_AUDIT_SLUGS keeps record-metadata columns out of the default body list:
id, created_at, created_on, updated_at, last_modified_on,
created_by, last_modified_by, record_owner,
parent_data_source_id, parent_record_id,
tenant_view_id, tenant_data_source_id, editor_view_id, sort_order,
data, document, mentions, followersautonumber_id is also skipped. These columns stay accessible via the Card fields menu / TanStack column visibility — they're just dropped from the default body layout so the first paint reads as a curated card rather than a raw record dump.
Saved Views
Gallery and data-grid share saved view storage. The hook wires DataGridViewSelect against useDocyrusDataViewSelect, so:
- Switching between table and gallery views uses the same view tabs.
- A view's
columnVisibility,columnOrder,sorting,columnFilters,grouping,filterQueryall apply identically. - The view's
displayModefield is honored if you flip between<DataGrid>and<DataGallery>at the app level. - Gallery-specific state (
cardConfig,displayConfig) is persisted to the view'sgalleryCardConfig/galleryDisplayConfigfields onSavedDataGridView. Saving a view from the gallery captures the current variant, cover style, column count, card field bindings, etc.; switching back to that view later re-hydrates the gallery to match.
Hydration order
When the active view changes the hook hydrates gallery state in this order:
- View's saved gallery state (
activeView.galleryCardConfig/galleryDisplayConfig) — applies as-is. - Caller-supplied props (
cardConfig/displayConfigoptions) — applied when the view didn't save anything for a slot. - Auto-detected defaults —
detectCardConfigFromFieldsruns against the data source schema.
The hook tracks the last-applied view id internally, so re-renders with the same active view never re-run hydration (your toolbar edits stay sticky). Switching views resets the gallery state to the new view's saved snapshot.
Saving
The view-select toolbar (DataGridViewSelect) builds the standard view payload (columns, sorting, filters, …). useDocyrusDataGallery wraps onViewSave and onViewCreate to inject the current cardConfig and displayConfig before the payload reaches the backend. Both the built-in toolbar and the externally-rendered gridViewSelectProps use the wrapped callbacks, so consumers building custom toolbars get the same persistence behavior automatically.
Pivot Filters
Pass pivotFilters to render <DocyrusPivotFilterGroup> strips above the toolbar. The hook AND-merges every pivot's combined rule into the items query, identically to useDocyrusDataGrid:
const { galleryProps, toolbar } = useDocyrusDataGallery({
appSlug: 'sales',
dataSourceSlug: 'products',
collection,
pivotFilters: [
{ fieldSlug: 'category' },
{ fieldSlug: 'status' }
]
});Side Filters
Pass enableSideFilters + sideFiltersConfig to add a left-rail filter panel. The hook exposes sideFilters, sideFiltersExpanded, setSideFiltersExpanded for layout integration — same wiring as the data grid.
Stale Schema Escape Hatch
excludeFieldSlugs drops a list of field slugs from the data source metadata before the columns, list params, view editor, filter menu, and exports consume it. Use it when the backend metadata still advertises a field that the underlying DB column no longer has — those requests return column "X" does not exist 500s otherwise. Inherited from useDocyrusDataGrid:
const galleryHook = useDocyrusDataGallery({
appSlug: 'base',
dataSourceSlug: 'organization',
/*
* Backend metadata cleanup is the proper fix — this is the
* frontend bypass while you wait for that PR to land.
*/
excludeFieldSlugs: ['single_selection', 'legacy_text']
});The hook memoizes the exclusion by content (joined slug string), not array identity, so passing inline literals doesn't trigger re-fetches.
Search Pipeline
The gallery owns its own search input and debounces locally (250 ms). The debounced keyword is forwarded to the items query via listParams.filterKeyword — the inner useDocyrusDataGrid is configured with enableSearch: false so the table doesn't also run a client-side globalFilter on top of the server-filtered rows (double-filter regression).
The hook exposes both the immediate (searchInput) and debounced (searchKeyword) values for consumers building a custom toolbar.
Pivot Strip Composition
useDocyrusDataGrid exposes its interactive <DocyrusPivotFilterGroup> element as pivotFiltersStrip so downstream hooks can compose it without re-running pivot state. useDocyrusDataGallery stacks that element above the gallery's controls toolbar automatically — pivot pill counts and cross-filter behavior are identical to the grid.
API Reference
Options
The hook composes useDocyrusDataGrid internally, so it also accepts every option that hook forwards — including the DB-free metadata options below — alongside the gallery-specific props in this table.
DB-free metadata. Inherited through useDocyrusDataGrid → useDocyrusDataViewSelect: dataSource (inject a pre-resolved schema → skips the getBySlug fetch), enableDataViews: false (skip the /views fetch), and dataSourceExpand (tune/drop the schema expand param). Pass dataSource together with data (pre-resolved rows) to render cards with no metadata requests. See DB-free metadata.
| Prop | Type | Default | Description |
|---|---|---|---|
collection | DocyrusDataGridCollection<TData> | — | TanStack DB collection. The hook calls collection.list(listParams) to fetch rows. |
data | Array<TData> | — | Pre-resolved rows. Skips the internal items query. |
appSlug | string | — | Required for data source schema and view fetches. |
dataSourceSlug | string | — | Required for data source schema and view fetches. |
listParams | DocyrusDataGridListParams | — | Extra query params merged on top of view-derived ones. |
pivotFilters | ReadonlyArray<DocyrusPivotFilterGroupField> | — | Render pivot strips above the toolbar. |
enableSideFilters | boolean | false | Enable the side-panel filter rail. |
sideFiltersConfig | DocyrusDataGridSideFiltersConfig<TData> | — | Side panel config. |
cardConfig | DataGalleryCardConfigSerializable | auto-detected | Initial card field bindings. |
onCardConfigChange | (config) => void | — | Fires whenever the card config changes. |
displayConfig | Partial<DataGalleryDisplayConfig> | — | Initial display config. |
onDisplayConfigChange | (config) => void | — | Fires whenever the display config changes. |
enableSearchInput | boolean | true | Toggle the toolbar search input. |
enableFilterMenu | boolean | true | Toggle the filter menu. |
enableSortMenu | boolean | true | Toggle the sort menu. |
enableGroupMenu | boolean | true | Toggle the group menu. |
enableDisplayMenu | boolean | true | Toggle the gallery display menu. |
enableCardConfigMenu | boolean | true | Toggle the card config menu. |
enableViewSelect | boolean | true | Toggle the view picker. |
enableReloadButton | boolean | true | Toggle the reload button. |
viewSelectVariant | 'horizontal-tabs' | 'dropdown' | 'vertical-tabs' | 'horizontal-tabs' | View picker variant. |
onAdd | () => void | — | When supplied, renders a primary "Add" button on the toolbar's right edge. |
addLabel | string | 'Add' | Label for the add button. |
minCardWidth | number | 260 | Min card width for the 'flex' column count auto-fit. |
estimatedCardHeight | number | 360 | Initial virtualizer estimate — measureElement replaces per-row after first paint. |
excludeFieldSlugs | ReadonlyArray<string> | — | Drop stale field slugs from the data source schema before downstream consumers see it. See the Stale Schema Escape Hatch section. |
overscan | number | 3 | Virtualizer overscan rows. |
users | ReadonlyArray<CellUserOption> | — | Tenant users for field-userSelect cells. Forwarded to the grid. |
formatDate, formatDateTime, formatNumber | functions | — | Tenant-aware formatters. Forwarded to the grid. |
bulkActions | false | ReadonlyArray<DocyrusDataGridBulkAction> | ['update', 'delete', 'export'] | Action bar bulk actions. |
extraBulkActions | Array<DataGalleryAction<TData>> | — | Extra row-selection actions. |
Returns
| Key | Type | Description |
|---|---|---|
galleryProps | DocyrusDataGalleryProps<TData> | Spread onto <DataGallery>. Includes table, displayConfig, virtualizer, containerRef, etc. |
toolbar | ReactNode | Pre-wired gallery toolbar element. Render above <DataGallery>. |
cardConfig | DataGalleryCardConfigSerializable | Current card field bindings. |
setCardConfig | (updater) => void | Card config setter. |
displayConfig | DataGalleryDisplayConfig | Current display config. |
setDisplayConfig | (updater) => void | Display config setter. |
searchInput | string | Immediate search input value. |
searchKeyword | string | Debounced search keyword sent to the backend. |
setSearchInput | (value) => void | Search input setter. |
items | Array<TData> | Resolved rows. |
fields | Array<FullField> | Data source fields (filter-friendly shape). |
dataSource | DataSource | undefined | Resolved data source metadata. |
activeViewId | string | Current saved view id. |
setActiveViewId | (viewId) => void | View setter. |
views | Array<SavedDataGridView> | Available saved views. |
pivotFilterRule | Array<PivotFilterRule> | null | Combined pivot filter rule. |
sideFilters | ReactNode | Side panel element (or null). |
sideFiltersExpanded, setSideFiltersExpanded, sideFiltersQuery | — | Side filter wiring. |
resolvedListParams | DocyrusDataGridListParams | The list params actually sent to the backend. |
pagingMode | 'standard' | 'virtual-scroll' | undefined | Resolved paging mode for the active view. |
reload | () => void | Refetch metadata + items. |
refetch | () => void | Alias of reload. |
isLoading | boolean | Initial load indicator. |
error | Error | null | Schema or items error. |
gridViewSelectProps | Pick<DataGridViewSelectProps, ManagedProps> | View-select props with the hook's onViewSave / onViewCreate wrappers already attached — saving a view from a custom toolbar still persists gallery state. |
pivotFiltersStrip | ReactNode | The pivot strip element (or null when no pivotFilters configured) — render before the toolbar when composing a custom layout. |
Helper Exports
detectCardConfigFromFields(fields)
Build a DataGalleryCardConfigSerializable from a DataSourceField[]. Used internally; exported so consumers can re-run the detection after schema changes or build their own card configs.
import { detectCardConfigFromFields } from '@/hooks/use-docyrus-data-gallery';
const cardConfig = detectCardConfigFromFields(dataSource.fields);Related
DataGallery— the standalone presentation component.useDocyrusDataGrid— table-mode sibling; gallery composes it internally.useDocyrusDataViewSelect— shared saved view storage.
useDocyrusDataExport
Server-side export for Docyrus data sources — POSTs a query payload to the export edge function and streams the result file straight to the browser.
useDocyrusDataGrid
One-call wiring of a Docyrus data source to a fully configured DataGrid + toolbar (DataGridViewSelect, search, filters, group, sort, row height, display) — including row fetching with view-derived query parameters.