Vue actions and exports
The Vue binding, Unstyled kit and seven styled adapters are public
0.1.0 packages on npm. Install your chosen kit using
Get started with Vue before using these examples.
The action factories are opt-in: bulkActions, commandPalette, contextMenu,
sidePanel, undoRedoButtons, exportCsv and print are available through each
adapter’s focused entries. Dedicated exportPdf and exportXlsx factories are
exported by Unstyled, Reka UI, shadcn-vue and Quasar; the other kits accept those
writers through their own exportCsv factory.
The binding owns structural Chrome and reads the neutral engine’s state. A kit supplies every visible control through required typed slots. The native adapter fills these slots with native buttons, inputs, a dialog and a manual popover. No visible HTML-control fallback is installed by the binding.
Bulk actions receive selected IDs plus { allMatching, total }. The all-matching banner is offered only when the source can speak for the complete filtered set. Host confirmation receives the count of that scope. The host performs mutations; a disabled reason prevents the action and appears on the control. Rejections appear in an alert and a later click can retry.
The command palette offers current print, export, clear-filter and registered commands. It supports a controlled open value, configurable shortcuts, type-to-filter, keyboard highlighting and focus restoration. Shortcuts belong to the focused table, including nested tables. Context menus reuse neutral column/cell targets for mouse, keyboard and touch; grid Copy/Cut consume the existing navigation model, while a table without grid selection copies the target cell. Side panels are controlled by the host and use an RTL-aware tablist. Mobile panels stack below the table.
Import exportCsv from @adapttable/vue-unstyled/export. The existing
/export-csv compatibility path is deprecated and continues to work. For a built-in binary format, import
exportPdf from /export-pdf or exportXlsx from /export-xlsx. Each factory
installs the native toolbar button and progress controls over the same export
controller. It accepts true, false, or typed scope, columns, filename and
host callbacks. false removes the export action; PDF and XLSX options fix the
writer. The writer utilities remain available for custom compositions such as
exportCsv({ writer: pdfWriter({ direction: "rtl" }) }).
ExportPdfOptions<TRow> and ExportXlsxOptions<TRow> are each
Omit<ExportCsvOptions<TRow>, "writer">. They preserve typed hook rows and the
shared scope, columns, filename, onBeforeExport and onAfterExport
options while fixing the document writer. Import each type from its matching
@adapttable/vue/pdf or @adapttable/vue/xlsx binding entry, or the matching
adapter /export-pdf or /export-xlsx entry where it exists. See the
export options for the shared fields and server routes.
A table has one current export format. These factories share the export-csv
feature identity, so a later declaration replaces an earlier one. For example,
[...standardFeatures(), exportXlsx()] replaces the preset’s CSV button with
XLSX. The export-csv-button part and exportCsvButton class hook stay stable
across formats. The command palette uses the same current export action.
The base table and /export do not import PDF or XLSX writers. The compatibility
/export-csv path and /preset, where exported, also keep those writers optional. Import a format entry only when needed. The optional /features barrel
exposes all three factories and is broader; individual entries provide the
smallest import graph.
Selection, range, hidden columns, grouping, tree entries, spans and summary data are supplied at click time to the neutral export route. Unsupported all-row exports are disabled with a localized reason. Server jobs support progress, cancellation, failure, retry and a host-provided download URL. Cancellation signals cooperative server work and drops late UI outcomes; it cannot undo a download or host action already admitted. A suspended non-cancelable request resets the UI without claiming that request was canceled.
print(onPrint, true) renders the button and print(onPrint) makes printing available to the command palette. The host owns that callback. The existing print helpers are available through the print/PDF paths. History buttons read the existing editing history and remain unavailable without an edit history model.
All new captions reuse existing localized labels. Host-authored action names and panel content are localized by the host. No action state introduces a new persisted URL field: command/menu visibility and export progress are transient; side-panel selection is host-owned.
Use the controls from your adapter
Section titled “Use the controls from your adapter”Use the same package for DataTable and its action factories. A Nuxt UI table
uses Nuxt modal, input, button, popover and panel controls; a shadcn-vue table uses
its copied shadcn components. The binding supplies shared actions, state,
localized labels and lifecycle guards. It does not silently draw a different
kit’s controls. The feature entry map
lists each package’s optional surfaces.
For Element Plus, Vuetify, Naive UI and Nuxt UI, keep the adapter’s export feature and select the optional binding writer explicitly:
import { exportCsv } from "@adapttable/nuxt-ui/export";import { pdfWriter } from "@adapttable/vue/pdf";
interface Person { id: string; name: string;}const features = [ exportCsv<Person>({ filename: "people.pdf", scope: "page", writer: pdfWriter({ direction: "rtl" }), }),];xlsxWriter from @adapttable/vue/xlsx follows the same pattern. Writer choice
changes the output, while the adapter retains its own button, progress, retry
and cancellation controls. Do not add binding-only export factories to a kit
table and expect them to install those controls.
Command palettes, context menus and export progress are transient; none creates a second query or persistence model. Side-panel selection remains host-controlled. Mobile panels stack beneath cards, and contextual actions use the current row or cell target. Desktop grid actions remain subject to the active navigation model. Overlay ownership follows the table’s fullscreen container and current lifetime; deactivation or replacement retires stale focus and close callbacks.
Compose the controls
Section titled “Compose the controls”The selected row IDs come from the table’s selection model for every format.
import { commandPalette } from "@adapttable/vue-unstyled/command-palette";import { exportXlsx } from "@adapttable/vue-unstyled/export-xlsx";import { print } from "@adapttable/vue-unstyled/print";
interface Person { id: string; name: string;}
const features = [ commandPalette({ button: true }), exportXlsx<Person>({ scope: "selected", filename: "people.xlsx" }), print(() => window.print(), true),];Pass features to the native DataTable<Person> together with your rows, columns and row key. The print callback above prints the host page; pass your own layout callback when the host needs a dedicated paper layout.
For source replacement, an active export follows the built-in source’s stable
tableEngine, or its stable setPage mutator when there is no engine. Ordinary
snapshot refreshes and wrappers that preserve this signal keep the job active.
A custom source that replaces its page mutator conservatively retires the job;
unrelated custom sources that share the same engine or callback cannot be
distinguished by this signal. Preserve source-owned callbacks across refreshes.
Retirement discards late success/failure updates and signals cooperative server
cancellation. It cannot undo host side effects already started.
Render export progress with the kit’s controls
Section titled “Render export progress with the kit’s controls”ExportChrome from @adapttable/vue/adapter accepts an ExportChromeProps
model and an ExportSlots object with Button and Surface. Button receives the
localized export caption and complete disabled, busy and click attributes.
Surface receives the neutral progress view, including only the actions currently
available. The binding creates no fallback button or progress control.
For a standalone progress surface, call ExportProgressChrome with its current
progress state, labels and an ExportProgressSlots fill. This native-control
example shows the complete state and action wiring; another kit substitutes its
own progress, button and surface components.
import { ExportProgressChrome } from "@adapttable/vue/adapter";import type { ExportProgressChromeProps, ExportProgressSlots,} from "@adapttable/vue/adapter";import { h } from "vue";
const progressSlots = { Surface: (view) => { const actions = [view.cancel, view.retry, view.dismiss].flatMap((action) => action ? [ h( "button", { type: "button", onClick: action.onAction }, action.label ), ] : [] ); return h("section", { "aria-label": view.heading }, [ h("h3", view.heading), h("p", { role: "status" }, view.message), view.status === "busy" ? h("progress", { max: 100, value: view.progress, "aria-label": view.progressLabel, }) : null, view.error ? h("p", { role: "alert" }, view.error) : null, ...actions, view.download ? h("a", { href: view.download.url }, view.download.label) : null, ]); },} satisfies ExportProgressSlots;
export function renderExportProgress( props: Omit<ExportProgressChromeProps, "slots">) { return ExportProgressChrome({ ...props, slots: progressSlots });}An undefined progress value leaves the progress element indeterminate. Pass
progress: null when no server progress surface exists. Call the supplied
cancel, retry and dismiss actions rather than inventing a second job lifecycle.
The host still owns the export job and its resulting download. The composed
feature handles table-local focus restoration when the surface closes.
Build action controls for another Vue kit
Section titled “Build action controls for another Vue kit”Application code uses the factories from @adapttable/vue-unstyled. A kit author
instead imports the binding factory from @adapttable/vue/features,
then fills its required control channel with extendFeature and slotRender.
The factory owns the controller and publishes the current model; the shell
passes that model and its presentation to the registered control. Do not create
a second controller in the slot, call a setup composable from a render callback,
or cache a model snapshot across renders.
ActionPresentation carries resolved labels, dir, optional classNames
and the table’s optional overlay container. ActionButton carries label,
optional Vue icon, and complete semantic attrs. ActionButtonSlots requires
Button(props: ActionButton): VNodeChild. Forward those attributes, including
disabled state and event handlers, to the kit’s actual button and render both
the icon and label. These presentation contracts, runtime state keys and control keys come from
@adapttable/vue/adapter.
This factory lets an adapter supply its own button component while reusing the complete bulk-action behavior:
import { extendFeature, slotRender } from "@adapttable/vue/adapter";import { BULK_ACTIONS_CONTROL, BulkActionsChrome,} from "@adapttable/vue/adapter";import { bulkActions } from "@adapttable/vue/features";import type { BulkAction } from "@adapttable/vue";import type { BulkActionsSlots } from "@adapttable/vue/adapter";
export function kitBulkActions( actions: readonly BulkAction[], slots: BulkActionsSlots) { return extendFeature(bulkActions(actions), [ slotRender(BULK_ACTIONS_CONTROL, (props) => BulkActionsChrome({ ...props, slots }) ), ]);}BulkActionsSlots is the action-button slot contract. BulkActionsChrome takes
BulkActionsChromeProps: ActionPresentation, model: BulkActionsModel and
those slots. It renders the selected count, available actions, all-matching
banner, pending status and error; it returns nothing when the count is zero.
BulkActionsModel contains count, pending action key or null, error text or
null, the neutral banner, and actions. Its disabledReason(action),
run(action), clear() and selectAllMatching() methods operate on the current
selection and respect the feature lifetime.
State and control channels
Section titled “State and control channels”A state key is read with context.state.get(key) in a feature or
useFeatureState(key) in a descendant component; both return a readonly shallow
ref whose value can be undefined before the feature is available. A control key
is filled with slotRender(key, render). Reading a key does not install its
feature. The following pairs are exported at runtime by /adapter:
| Binding entry | State key and value | Required control key and input |
|---|---|---|
/adapter |
BULK_ACTIONS_MODEL: BulkActionsModel |
BULK_ACTIONS_CONTROL: presentation plus model; render BulkActionsChrome. |
/adapter |
COMMAND_PALETTE_MODEL: CommandPaletteModel, with open, toolbar button, current commands, show() and close(). |
COMMAND_PALETTE_CONTROL: presentation plus model; render CommandPaletteChrome with the kit’s controls. |
/adapter |
CONTEXT_MENU_MODEL: ContextMenuModel, with target point at or null, current items and close(). |
CONTEXT_MENU_CONTROL: presentation plus model; render ContextMenuChrome. |
/adapter |
SIDE_PANEL_MODEL: SidePanelControlModel, with resolved open panel key or null, panels, optional logical side and host onOpenChange(key). |
SIDE_PANEL_CONTROL: presentation plus model; render SidePanelChrome. |
/adapter |
EXPORT_MODEL: ExportHandlerState, including export action, busy/disabled state, caption, announcement and progress. |
EXPORT_CONTROL: presentation plus model; render ExportChrome with Button and Surface. |
/adapter |
PRINT_MODEL: the guarded print callback, present when the print button is enabled. |
PRINT_CONTROL: presentation plus onPrint; render PrintChrome. |
The side-panel model resolves the reactive SidePanelOptions.open input; it
does not turn controlled state into local state. SidePanelLayoutChrome, from
/adapter, wraps the default table slot with the supplied
panel(): VNodeChild when open is true. Its optional side: "start" | "end"
sets desktop placement and mobile stacks the panel below the table. The panel
itself still requires the Frame, Tab and Close controls of SidePanelChrome.
PrintChrome(props: PrintChromeProps): VNodeChild accepts the shared
presentation, an onPrint callback and ActionButtonSlots. It renders only the
kit’s print button; the host callback decides what gets printed.
UNDO_REDO_CONTROL, exported by /adapter, is a control channel
for ToolbarExtrasSlotProps, not a second history model. Fill it using
HistoryButtonsChrome from /adapter. Its HistoryButtonsChromeProps adds
ActionButtonSlots to those toolbar props, including onUndo, onRedo,
canUndo, canRedo and optional operation labels. The Chrome renders only the
available actions, disables unavailable operations, and returns nothing without
either callback. Compose the editing history and its host replay callback as
shown in the editing guide.