import { Localization, PivotEngine, PivotGrid, PivotConfiguration, PivotEventName, PivotEventMap, HeaderNode, MeasureConfig } from '@pvotly/core'; export * from '@pvotly/core'; /** Built-in theme names. Custom themes can be applied via any string + CSS. */ type ThemeName = 'light' | 'dark' | (string & {}); /** Apply a theme by setting the `data-ph-theme` attribute on the root element. */ declare function applyTheme(root: HTMLElement, theme: ThemeName): void; /** * Override theme tokens at runtime by writing CSS custom properties onto the * root. Keys are token names without the `--ph-` prefix (e.g. `accent`). */ declare function setThemeTokens(root: HTMLElement, tokens: Record): void; /** * Strongly-typed style tokens. Pass these as the `tokens` prop/option to restyle * the widget without writing any CSS. Each maps to a `--ph-*` custom property. */ interface ThemeTokens { /** Primary accent (buttons, links, active states). */ accent?: string; /** Foreground/text color on accent surfaces. */ accentForeground?: string; /** Body text color. */ foreground?: string; /** Muted/secondary text color. */ mutedForeground?: string; /** Grid background. */ background?: string; /** Alternate background (toolbar, panels). */ altBackground?: string; /** Header cell background. */ headerBackground?: string; /** Border color. */ border?: string; /** Stronger border (hover/focus). */ borderStrong?: string; /** Subtotal cell background. */ subtotalBackground?: string; /** Grand-total cell background. */ grandTotalBackground?: string; /** Row/cell hover background. */ hoverBackground?: string; /** Popover/dialog shadow. */ shadow?: string; /** Font family. */ fontFamily?: string; /** Base font size (e.g. '13px'). */ fontSize?: string; /** Corner radius — number is treated as px. */ radius?: string | number; } /** Apply a typed {@link ThemeTokens} object onto the root as CSS variables. */ declare function applyTokens(root: HTMLElement, tokens: ThemeTokens): void; /** * All user-facing UI strings used by the toolbar, field list and the four * dialogs (fields / filter / format / conditional). Every key maps to a single * visible label so the whole widget can be localized via the report's * `localization` object (see {@link resolveStrings}). * * The {@link DEFAULT_STRINGS} values are the canonical English UI text. They are * deliberately byte-identical to the literals previously hardcoded in the view * modules so existing tests/e2e assertions on exact text keep passing. */ interface UIStrings { fields: string; format: string; formatTitle: string; conditional: string; conditionalTitle: string; fullscreen: string; layout: string; compact: string; classic: string; flat: string; export: string; toCSV: string; toExcel: string; toHTML: string; toJSON: string; printPdf: string; reportFilters: string; columns: string; rows: string; values: string; fieldsTitle: string; allFields: string; expandAll: string; collapseAll: string; search: string; searchFields: string; dropFieldHere: string; dragAndDrop: string; noMatchingFields: string; expand: string; collapse: string; remove: string; moveUp: string; moveDown: string; measure: string; dragToArea: string; sort: string; filter: string; aggregation: string; filterTitle: string; members: string; value: string; searchMembers: string; selectAll: string; clear: string; keepMembersByValue: string; by: string; apply: string; clearFilter: string; cancel: string; formatTitle2: string; applyTo: string; defaultAll: string; decimalPlaces: string; thousandsSeparator: string; decimalSeparator: string; currencySymbol: string; currencyPosition: string; left: string; right: string; negatives: string; negativeMinus: string; negativeParentheses: string; negativeRedMinus: string; showAsPercent: string; blankNullText: string; conditionalDialogTitle: string; noRulesYet: string; allMeasures: string; valuePlaceholder: string; toPlaceholder: string; addRule: string; fill: string; text: string; opGreaterThan: string; opGreaterOrEqual: string; opLessThan: string; opLessOrEqual: string; opEquals: string; opNotEquals: string; opBetween: string; opContains: string; noData: string; sortAscending: string; sortDescending: string; removeSort: string; resizeColumn: string; freezeColumns: string; copy: string; valuesAxis: string; valuesAsColumns: string; valuesAsRows: string; undo: string; redo: string; } /** * Canonical English strings. CRITICAL: keep these byte-identical to the * historical hardcoded literals — tests assert exact text and labels. */ declare const DEFAULT_STRINGS: UIStrings; /** * Merge {@link DEFAULT_STRINGS} with the report's `localization` object. Any * `UIStrings` key present on `localization` overrides the default; a handful of * well-known {@link Localization} fields are also mapped onto matching UI keys. * * When `localization` is undefined (the default), this returns the canonical * English strings so existing behavior is preserved. */ declare function resolveStrings(localization?: Localization | undefined): UIStrings; /** Layout direction. */ type Direction = 'ltr' | 'rtl'; /** * Resolve the desired layout direction from the report's `localization` object. * Honors either `localization.direction: 'rtl'` or a boolean `localization.rtl`. * Defaults to `'ltr'`. */ declare function resolveDirection(localization?: Localization | undefined): Direction; /** * Renderer-level UI state that lives alongside the report but is *not* part of * the core {@link PivotConfiguration}: column/row sizes, the frozen-column count * and the persisted vertical row height used by virtualization. * * It is intentionally a plain, JSON-serializable object so it can be saved to * `localStorage` / the URL hash next to the report (see {@link ./persistence}). */ interface UIStateData { /** Per-column pixel widths, keyed by the renderer's stable column id. */ columnWidths?: Record; /** Per-row-header pixel heights, keyed by the renderer's stable row id. */ rowHeights?: Record; /** Number of leading columns kept pinned during horizontal scroll. */ freezeColumns?: number; /** Uniform body row height (px) used when virtualization is active. */ rowHeight?: number; } /** * Small mutable holder for {@link UIStateData}. The widget owns one instance and * hands it to the grid renderer through the {@link PivotContext}. Mutations are * in place; persistence reads {@link UIState.data} as a snapshot. */ declare class UIState { columnWidths: Record; rowHeights: Record; freezeColumns: number; rowHeight: number; constructor(initial?: UIStateData); /** Serializable snapshot of the current UI state. */ get data(): UIStateData; /** Replace the entire UI state (used by restore). */ set(data: UIStateData): void; /** Forget all stored sizes (keeps the freeze count). */ clearSizes(): void; } /** * Shared context handed to every view module (toolbar, field list, dialogs). * It is the stable internal contract the UI pieces program against. */ interface PivotContext { readonly engine: PivotEngine; readonly table: PivotTable; /** Renderer-level UI state (column sizes, freeze count). */ readonly ui: UIState; /** Request a full grid + field-list re-render from current engine state. */ refresh(): void; /** Mount a transient modal element over the table. */ openDialog(content: HTMLElement, title?: string, opts?: { width?: string; }): void; /** Close any open modal. */ closeDialog(): void; } type ExportFormat = 'csv' | 'html' | 'json' | 'excel'; interface ExportOptions { filename?: string; /** Use raw numeric values instead of formatted strings. */ raw?: boolean; } /** Build a 2D matrix (header row + body rows) of the visible grid. */ declare function gridToMatrix(grid: PivotGrid, raw?: boolean): (string | number | null)[][]; declare function exportToCSV(grid: PivotGrid, options?: ExportOptions): string; declare function exportToJSON(grid: PivotGrid, options?: ExportOptions): string; declare function exportToHTML(grid: PivotGrid, options?: ExportOptions): string; /** Excel-compatible export using the SpreadsheetML / HTML-table format (.xls). */ declare function exportToExcel(grid: PivotGrid, options?: ExportOptions): string; declare function serializeExport(grid: PivotGrid, format: ExportFormat, options?: ExportOptions): string; /** Trigger a browser download (no-op outside the browser). */ declare function downloadExport(grid: PivotGrid, format: ExportFormat, options?: ExportOptions): void; /** Open the browser print dialog scoped to the table (for PDF export). */ declare function printGrid(grid: PivotGrid, title?: string): void; /** The built-in action ids that can be toggled / customized. */ type ActionBarAction = 'fields' | 'format' | 'conditional' | 'layout' | 'export' | 'fullscreen' | 'undo' | 'redo' | 'copy'; /** Per-button override. `false` hides it; an object customizes it. */ interface ActionBarButtonConfig { visible?: boolean; /** Override the label text. */ label?: string; /** Override the icon (raw SVG/HTML string). */ icon?: string; /** Tooltip. */ title?: string; /** Extra class name(s) added to the button. */ className?: string; /** Replace the default click behavior. Receives the widget instance. */ onClick?: (table: PivotTable) => void; } /** A fully custom button added to the action bar. */ interface ActionBarItem { id: string; label?: string; icon?: string; title?: string; className?: string; onClick: (table: PivotTable) => void; } /** * Configure / style the action bar (toolbar). Each built-in action can be a * boolean (show/hide) or an object override; add your own buttons via `custom`. * * @example * ```ts * actionBar: { * className: 'my-bar', * fields: { label: 'Configure', icon: gearSvg }, // restyle a built-in * conditional: false, // hide one * custom: [{ id: 'refresh', label: 'Reload', onClick: (t) => t.refresh() }], * } * ``` */ interface ActionBarConfig { /** Hide the whole bar. */ visible?: boolean; /** Class name(s) added to the bar element. */ className?: string; /** Inline styles for the bar element. */ style?: string | Partial; /** Where custom buttons go relative to the built-ins (default 'end'). */ customPosition?: 'start' | 'end'; /** Extra custom buttons. */ custom?: ActionBarItem[]; fields?: boolean | ActionBarButtonConfig; format?: boolean | ActionBarButtonConfig; conditional?: boolean | ActionBarButtonConfig; layout?: boolean | ActionBarButtonConfig; export?: boolean | ActionBarButtonConfig; fullscreen?: boolean | ActionBarButtonConfig; /** Undo button (hidden unless enabled). */ undo?: boolean | ActionBarButtonConfig; /** Redo button (hidden unless enabled). */ redo?: boolean | ActionBarButtonConfig; /** Copy-selection button (hidden unless enabled). */ copy?: boolean | ActionBarButtonConfig; } /** Render the toolbar into `container`, honoring the table's actionBar config. */ declare function mountToolbar(ctx: PivotContext, container: HTMLElement): void; /** A complete, serializable snapshot of a pivot widget (report + UI state). */ interface PivotSnapshot { /** Schema version, for forward-compatible restores. */ version: 1; /** The full report. Bulk data is stripped unless `includeData` is set. */ report: PivotConfiguration; /** Renderer UI state (sizes / freeze). */ ui?: UIStateData; } interface SerializeOptions { /** * Include the raw `dataSource` rows (`data` / `matrix` / `csv`). Off by * default — snapshots store the report *shape*, not the (potentially huge) * dataset, so URLs / localStorage stay small. The `mapping` is always kept. */ includeData?: boolean; } /** Build a {@link PivotSnapshot} from a report + UI state. */ declare function createSnapshot(report: PivotConfiguration, ui?: UIStateData, options?: SerializeOptions): PivotSnapshot; /** JSON-encode a snapshot. */ declare function serializeSnapshot(report: PivotConfiguration, ui?: UIStateData, options?: SerializeOptions): string; /** Parse a JSON snapshot string. Returns `null` on malformed input. */ declare function parseSnapshot(json: string | null | undefined): PivotSnapshot | null; declare function saveToLocalStorage(report: PivotConfiguration, ui?: UIStateData, options?: SerializeOptions & { key?: string; }): boolean; declare function loadFromLocalStorage(key?: string): PivotSnapshot | null; declare function clearLocalStorage(key?: string): void; /** Encode a snapshot into a URL hash fragment value (base64url). */ declare function encodeSnapshotToHash(report: PivotConfiguration, ui?: UIStateData, options?: SerializeOptions): string; /** Persist a snapshot into `location.hash` under `key` (default `pivot`). */ declare function saveToUrlHash(report: PivotConfiguration, ui?: UIStateData, options?: SerializeOptions & { key?: string; }): boolean; /** Restore a snapshot previously stored in `location.hash`. */ declare function loadFromUrlHash(key?: string): PivotSnapshot | null; /** Values-axis position. Mirrors core's `valuesAxis` contract. */ type ValuesAxis = 'columns' | 'rows'; interface PivotTableOptions extends PivotConfiguration { /** Show the toolbar (default true). */ toolbar?: boolean; /** Show the drag-and-drop field-list panel (default true). */ fieldList?: boolean; /** Theme name (default 'light'). */ theme?: ThemeName; /** Typed style tokens applied as CSS variables — restyle without writing CSS. */ tokens?: ThemeTokens; /** Configure / style the action bar (toggle built-ins, add custom buttons). */ actionBar?: ActionBarConfig; /** CSS height for the widget (e.g. 500 or '60vh'). */ height?: string | number; /** CSS width. */ width?: string | number; /** * Show the horizontal report-filter bar above the grid. When omitted it * defaults to `true` if `slice.reportFilters` is non-empty, else `false`. * Each report-filter field renders a chip that opens its filter dialog. */ reportFilterBar?: boolean; /** Number of leading value columns to freeze (pin) during horizontal scroll. */ freezeColumns?: number; /** Initial renderer UI state (column/row sizes, freeze count). */ uiState?: UIStateData; /** Maximum number of undo/redo states retained (default 50). */ historyLimit?: number; } /** * The all-in-one pivot table widget. Owns a {@link PivotEngine}, builds the * layout (toolbar / field list / grid), and re-renders on every state change. * * @example * ```ts * import { PivotTable } from '@pvotly/web'; * import '@pvotly/web/styles.css'; * * const pivot = new PivotTable('#app', { * dataSource: { data }, * slice: { rows: [{ uniqueName: 'Country' }], measures: [{ uniqueName: 'Sales' }] }, * }); * ``` */ declare class PivotTable { readonly engine: PivotEngine; private readonly root; private readonly toolbarEl; private readonly reportBarEl; private readonly mainEl; private readonly fieldListEl; private readonly gridEl; private readonly dialogLayer; private readonly ctx; private showFieldList; private readonly showToolbar; private readonly reportFilterBar?; /** Action-bar configuration (mutable; see {@link setActionBar}). */ actionBar?: ActionBarConfig; /** Renderer-level UI state (column sizes, freeze count). */ readonly uiState: UIState; private readonly history; private applyingHistory; private unsubscribe; private renderScheduled; constructor(target: string | HTMLElement, options: PivotTableOptions); /** Reflect the localization layout direction onto the root element. */ private applyDirection; /** The current layout direction. */ get direction(): Direction; private scheduleRender; private render; /** Render the horizontal report-filter chip bar above the grid. */ private renderReportBar; /** Force an immediate re-render. */ refresh(): void; private openDialog; closeDialog(): void; getConfiguration(): PivotConfiguration; setConfiguration(config: PivotConfiguration): void; /** Current values-axis position (`'columns'` default, or `'rows'`). */ getValuesAxis(): ValuesAxis; /** * Move the Values (measures) between the column axis and the row axis. Sets * both the core `valuesAxis` contract field and the renderer's * `options.grid.measurePosition`, so it works against the current renderer and * forward-compatibly once core honors `valuesAxis`. */ setValuesAxis(axis: ValuesAxis): void; /** Freeze (pin) the first `count` value columns during horizontal scroll. */ freezeColumns(count: number): void; /** Set the width (px) of a column by its renderer id (or reset with null). */ setColumnWidth(columnId: string, width: number | null): void; /** Forget all user resize/size overrides. */ resetSizes(): void; /** Snapshot of the renderer UI state. */ getUIState(): UIStateData; /** Replace the renderer UI state and re-render. */ setUIState(data: UIStateData): void; private captureEntry; private applyEntry; canUndo(): boolean; canRedo(): boolean; /** Revert to the previous report/UI state. Returns false when at the start. */ undo(): boolean; /** Re-apply the next report/UI state. Returns false when at the end. */ redo(): boolean; /** Serialize the full report + UI state to a JSON string. */ serializeState(options?: SerializeOptions): string; /** A structured snapshot of the report + UI state. */ getSnapshot(options?: SerializeOptions): PivotSnapshot; /** Restore from a snapshot object or JSON string (keeps the current data). */ restoreState(source: PivotSnapshot | string): boolean; /** Persist the current state to `localStorage`. */ saveToLocalStorage(options?: SerializeOptions & { key?: string; }): boolean; /** Restore state previously written with {@link saveToLocalStorage}. */ loadFromLocalStorage(key?: string): boolean; /** Persist the current state into `location.hash`. */ saveToUrlHash(options?: SerializeOptions & { key?: string; }): boolean; /** Restore state previously written with {@link saveToUrlHash}. */ loadFromUrlHash(key?: string): boolean; /** Copy the current grid selection (or focused cell) to the clipboard as TSV. */ copySelection(): Promise; on(event: K, handler: (payload: PivotEventMap[K]) => void): () => void; off(event: K, handler: (payload: PivotEventMap[K]) => void): void; setTheme(theme: ThemeName): void; setThemeTokens(tokens: Record): void; /** Apply typed style tokens (the same shape as the `tokens` option). */ setTokens(tokens: ThemeTokens): void; toggleFieldList(show?: boolean): void; /** Open the modal "Fields" configurator (drag-drop + checklist + Apply/Cancel). */ openFieldsDialog(): void; /** Open the number-format dialog (wire this to your own action-bar button). */ openFormatDialog(): void; /** Open the conditional-formatting dialog. */ openConditionalDialog(): void; /** Open the filter dialog for a specific field. */ openFilterDialog(uniqueName: string): void; /** Update the action-bar configuration and re-render the toolbar. */ setActionBar(config: ActionBarConfig): void; toggleFullscreen(): Promise; exportTo(format: ExportFormat, options?: ExportOptions): void; print(title?: string): void; /** The root element of the widget. */ get element(): HTMLElement; destroy(): void; } /** Render (or update) the crosstab into `container`. Public, stable entry point. */ declare function renderGrid(ctx: PivotContext, container: HTMLElement): void; /** Copy the current selection (or focused cell) of a rendered grid as TSV. */ declare function copyGridSelection(container: HTMLElement): Promise; /** * A flattened, windowing-friendly view of a {@link PivotGrid}. This is the * single source of truth the renderer (table + virtualized paths) programs * against: ordered value columns, ordered body rows and the spanning column * header matrix — all carrying stable ids so resized widths survive rebuilds. */ interface ValueColumn { /** Stable id (column-leaf path + measure) used to persist widths. */ id: string; leaf: HeaderNode; /** The measure rendered in this column, or null when there are no measures. */ measure: MeasureConfig | null; measureIndex: number; } interface BodyRow { /** Stable id (row-node path + measure index). */ id: string; node: HeaderNode; /** For measures-on-rows: the measure rendered by this sub-row. */ measure?: MeasureConfig; measureIndex: number; /** Whether this row prints the member caption (false for measure sub-rows > 0). */ showLabel: boolean; /** Whether this row prints the gutter expander button. */ showGutterButton: boolean; isTotal: boolean; isGrandTotal: boolean; } interface HeaderCell { node: HeaderNode; caption: string; /** Span measured in column *leaves* (not value columns). */ colspan: number; rowspan: number; /** First value-column index covered by this header cell (inclusive). */ valueStart: number; /** Last value-column index covered by this header cell (exclusive). */ valueEnd: number; } interface GridModel { grid: PivotGrid; measures: MeasureConfig[]; colLevels: number; rowFieldCount: number; gutter: boolean; measuresOnRows: boolean; showMeasureRow: boolean; rowLabelsCaption: string; gtCaption: string; /** Number of leading (row-header) columns: gutter ? 2 : 1. */ leadingCols: number; colLeaves: HeaderNode[]; /** Spanning member header rows (length === colLevels). */ headerRows: HeaderCell[][]; /** The real body columns (one per measure per column-leaf, or per leaf). */ valueColumns: ValueColumn[]; bodyRows: BodyRow[]; empty: boolean; } /** Compute the unified, windowing-friendly model from a computed grid. */ declare function buildGridModel(grid: PivotGrid, config: PivotConfiguration): GridModel; /** A logical body-cell coordinate: row index + value-column index. */ interface CellCoord { r: number; c: number; } /** A normalized inclusive rectangle of body cells. */ interface CellRange { r0: number; c0: number; r1: number; c1: number; } interface TSVOptions { /** Prepend the row-header caption column(s) and a column-header row. */ includeHeaders?: boolean; } /** * Serialize a body-cell rectangle as TSV (tab-separated, `\n` rows) — the * clipboard format Excel / Google Sheets paste natively. */ declare function buildTSV(model: GridModel, range: CellRange, options?: TSVOptions): string; /** Write text to the clipboard, falling back to a hidden textarea + execCommand. */ declare function writeClipboard(text: string): Promise; /** A single point-in-time state captured on the undo/redo stack. */ interface HistoryEntry { report: PivotConfiguration; ui?: UIStateData; } interface HistoryOptions { /** Maximum number of retained states (default 50). Oldest are dropped. */ limit?: number; } /** * A linear undo/redo stack of {@link HistoryEntry} snapshots. Entries are deep * cloned on the way in and out so callers can mutate freely without corrupting * history. Pushing a new state after an undo discards the redo branch. */ declare class UndoRedoStack { private stack; private index; private readonly limit; constructor(options?: HistoryOptions); /** Discard all history and seed it with a single baseline state. */ reset(entry: HistoryEntry): void; /** Record a new state, dropping any redo branch and trimming to the limit. */ push(entry: HistoryEntry): void; canUndo(): boolean; canRedo(): boolean; /** Step back one state and return it (or `null` if at the beginning). */ undo(): HistoryEntry | null; /** Step forward one state and return it (or `null` if at the end). */ redo(): HistoryEntry | null; /** The current state, or `null` when empty. */ current(): HistoryEntry | null; /** Number of states retained. */ get size(): number; } /** Render the drag-and-drop configurator panel. */ declare function mountFieldList(ctx: PivotContext, container: HTMLElement): void; /** Open a member-checklist + value/label filter dialog for a field. */ declare function openFilterDialog(ctx: PivotContext, uniqueName: string): void; /** Open the number-format editor for the default (and per-measure) format. */ declare function openFormatDialog(ctx: PivotContext): void; /** Open the conditional-formatting rules editor. */ declare function openConditionalDialog(ctx: PivotContext): void; /** * The modal "Fields" configurator: an all-fields checklist (with expandable * date parts, measure markers and a search box) plus the four drop zones * (Report Filters / Columns / Rows / Values) with drag-drop + reorder, and * Apply/Cancel that commit or discard against a working copy of the slice. */ declare function openFieldsDialog(ctx: PivotContext): void; export { type ActionBarAction, type ActionBarButtonConfig, type ActionBarConfig, type ActionBarItem, type BodyRow, type CellCoord, type CellRange, DEFAULT_STRINGS, type Direction, type ExportFormat, type ExportOptions, type GridModel, type HistoryEntry, type PivotContext, type PivotSnapshot, PivotTable, type PivotTableOptions, type SerializeOptions, type ThemeName, type ThemeTokens, UIState, type UIStateData, type UIStrings, UndoRedoStack, type ValueColumn, type ValuesAxis, applyTheme, applyTokens, buildGridModel, buildTSV, clearLocalStorage, copyGridSelection, createSnapshot, downloadExport, encodeSnapshotToHash, exportToCSV, exportToExcel, exportToHTML, exportToJSON, gridToMatrix, loadFromLocalStorage, loadFromUrlHash, mountFieldList, mountToolbar, openConditionalDialog, openFieldsDialog, openFilterDialog, openFormatDialog, parseSnapshot, printGrid, renderGrid, resolveDirection, resolveStrings, saveToLocalStorage, saveToUrlHash, serializeExport, serializeSnapshot, setThemeTokens, writeClipboard };