bo-grid 0.21.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,12 +6,20 @@
6
6
  pageCount,
7
7
  total,
8
8
  onGoto,
9
+ pageSize,
10
+ pageSizeOptions,
11
+ onPageSize,
9
12
  }: {
10
13
  page: number;
11
14
  pageCount: number;
12
15
  total: number;
13
16
  onGoto: (page: number) => void;
17
+ pageSize?: number;
18
+ pageSizeOptions?: number[];
19
+ onPageSize?: (size: number) => void;
14
20
  } = $props();
21
+
22
+ const showSizes = $derived(!!pageSizeOptions && pageSizeOptions.length > 0);
15
23
  </script>
16
24
 
17
25
  <div class="pager" role="navigation" aria-label="Pagination">
@@ -20,6 +28,20 @@
20
28
  <span class="pageinfo">Page {page + 1} of {pageCount} · {total.toLocaleString()} rows</span>
21
29
  <button type="button" class="pg" disabled={page >= pageCount - 1} onclick={() => onGoto(page + 1)}>Next ›</button>
22
30
  <button type="button" class="pg" disabled={page >= pageCount - 1} aria-label="Last page" onclick={() => onGoto(pageCount - 1)}>»</button>
31
+ {#if showSizes}
32
+ <label class="pgsize">
33
+ Rows
34
+ <select
35
+ aria-label="Rows per page"
36
+ value={pageSize}
37
+ onchange={(e) => onPageSize?.(Number((e.currentTarget as HTMLSelectElement).value))}
38
+ >
39
+ {#each pageSizeOptions as opt (opt)}
40
+ <option value={opt}>{opt}</option>
41
+ {/each}
42
+ </select>
43
+ </label>
44
+ {/if}
23
45
  </div>
24
46
 
25
47
  <style>
@@ -60,4 +82,27 @@
60
82
  color: var(--bo-text-dim);
61
83
  font-variant-numeric: tabular-nums;
62
84
  }
85
+ .pgsize {
86
+ display: inline-flex;
87
+ align-items: center;
88
+ gap: 5px;
89
+ margin-left: auto;
90
+ font-family: var(--bo-mono);
91
+ font-size: 11px;
92
+ color: var(--bo-text-dim);
93
+ }
94
+ .pgsize select {
95
+ font: inherit;
96
+ font-size: 11px;
97
+ color: var(--bo-text);
98
+ background: transparent;
99
+ border: 0.5px solid var(--bo-border);
100
+ border-radius: 6px;
101
+ padding: 2px 4px;
102
+ cursor: pointer;
103
+ }
104
+ .pgsize select:focus-visible {
105
+ outline: 2px solid var(--bo-sel-border);
106
+ outline-offset: 1px;
107
+ }
63
108
  </style>
@@ -3,6 +3,9 @@ type $$ComponentProps = {
3
3
  pageCount: number;
4
4
  total: number;
5
5
  onGoto: (page: number) => void;
6
+ pageSize?: number;
7
+ pageSizeOptions?: number[];
8
+ onPageSize?: (size: number) => void;
6
9
  };
7
10
  declare const Pager: import("svelte").Component<$$ComponentProps, {}, "">;
8
11
  type Pager = ReturnType<typeof Pager>;
@@ -28,6 +28,13 @@ interface ColBase {
28
28
  cellClass?: string | ((value: unknown, row: GridRow) => string | undefined);
29
29
  /** Extra class(es) for this column's header. Target via `:global`. */
30
30
  headerClass?: string;
31
+ /** Show a styled tooltip on the column header (the same themed bubble as cell
32
+ `tooltip`) — describe what the column means, units, caveats. Hovering
33
+ anywhere on the header reveals it. */
34
+ headerTooltip?: string;
35
+ /** Render a small ⓘ info icon in the header as a visible cue that a
36
+ `headerTooltip` is available. Ignored without `headerTooltip`. */
37
+ headerInfo?: boolean;
31
38
  /** Amber flash on value change (drives off the row's flashSeq/flashDir). */
32
39
  flash?: boolean;
33
40
  /** Set false to disable header-click sorting on this column. */
@@ -48,13 +55,26 @@ interface ColBase {
48
55
  /** Editable choices: when set, editing renders a `<select>` of these options
49
56
  instead of a text input (enum/status columns). */
50
57
  options?: string[];
51
- /** Set a native `title` tooltip on each cell (the full formatted value) — handy
52
- when content truncates. */
53
- tooltip?: boolean;
58
+ /** Show a styled floating tooltip on each cell (themed, instant replaces the
59
+ native `title`). `true` shows the full formatted value (handy when content
60
+ truncates); a function returns custom text per value/row (any column type,
61
+ including `custom`). Return '' to suppress for a given cell. */
62
+ tooltip?: boolean | ((value: unknown, row: GridRow) => string);
63
+ /** Let long content wrap to multiple lines instead of truncating with an
64
+ ellipsis. Pair with a taller `rowHeight`. Default false (single-line +
65
+ ellipsis). */
66
+ wrap?: boolean;
54
67
  /** Custom display formatter, overriding the built-in type formatter. Applies
55
68
  to display, tooltip, copy and (formatted) export. `row` is absent for
56
69
  aggregate cells. */
57
70
  format?: (value: unknown, row?: GridRow) => string;
71
+ /** JS cell renderer — the framework-agnostic alternative to the `cell`
72
+ snippet, for React/Vue/vanilla (and the `<bo-grid>` web component). Return
73
+ an `HTMLElement`/`Node` (safe — appended as-is) or an HTML **string**
74
+ (inserted as innerHTML — YOU must sanitize any untrusted data; prefer
75
+ returning a Node for that). Overrides the cell's display only — sort,
76
+ filter, tooltip, copy and export still use the value/`format`. */
77
+ render?: (ctx: CellRenderContext) => string | Node | null | undefined;
58
78
  /** Set false to disable drag-to-resize on this column (default on). */
59
79
  resizable?: boolean;
60
80
  /** Parent header label. Consecutive columns sharing a `group` render under a
@@ -117,6 +137,12 @@ export interface DataBarGeom {
117
137
  /** The value is negative (use the bar's `negative` colour). */
118
138
  negative: boolean;
119
139
  }
140
+ /** Context passed to a column's JS `render` hook. */
141
+ export interface CellRenderContext {
142
+ value: unknown;
143
+ row: GridRow;
144
+ column: ColumnDef;
145
+ }
120
146
  export interface CellEditEvent {
121
147
  row: GridRow;
122
148
  column: ColumnDef;
@@ -204,6 +230,13 @@ export interface GridRow {
204
230
  */
205
231
  export declare function cellValue(col: ColumnDef, row: GridRow): unknown;
206
232
  export declare function formatCell(col: ColumnDef, value: unknown, row?: GridRow): string;
233
+ /**
234
+ * Resolve a cell's tooltip text for the styled floating tooltip. A boolean
235
+ * `tooltip` shows the formatted value (skipped for sparkline/custom — no text
236
+ * form); a function returns custom text for any column type. Empty/whitespace →
237
+ * no tooltip. Pure; unit-tested.
238
+ */
239
+ export declare function tooltipText(col: ColumnDef, value: unknown, row: GridRow): string | undefined;
207
240
  export declare function isSortable(col: ColumnDef): boolean;
208
241
  export declare function isEditable(col: ColumnDef): boolean;
209
242
  export declare function compareRows(a: GridRow, b: GridRow, sort: SortState, col?: ColumnDef): number;
@@ -39,6 +39,23 @@ export function formatCell(col, value, row) {
39
39
  return value == null ? '' : String(value);
40
40
  }
41
41
  }
42
+ /**
43
+ * Resolve a cell's tooltip text for the styled floating tooltip. A boolean
44
+ * `tooltip` shows the formatted value (skipped for sparkline/custom — no text
45
+ * form); a function returns custom text for any column type. Empty/whitespace →
46
+ * no tooltip. Pure; unit-tested.
47
+ */
48
+ export function tooltipText(col, value, row) {
49
+ const t = col.tooltip;
50
+ if (!t)
51
+ return undefined;
52
+ const text = typeof t === 'function'
53
+ ? t(value, row)
54
+ : col.type === 'sparkline' || col.type === 'custom'
55
+ ? ''
56
+ : formatCell(col, value, row);
57
+ return text && text.trim() ? text : undefined;
58
+ }
42
59
  export function isSortable(col) {
43
60
  return col.type !== 'sparkline' && col.sortable !== false;
44
61
  }
@@ -16,4 +16,37 @@ export declare function toCSV(rows: readonly GridRow[], columns: readonly Column
16
16
  /** Trigger a browser download of text content. No-op outside the browser. */
17
17
  export declare function download(filename: string, content: string, mime?: string): void;
18
18
  export declare function exportCSV(filename: string, rows: readonly GridRow[], columns: readonly ColumnDef[], opts?: ExportOptions): void;
19
+ /** Parse RFC4180 CSV text into a 2-D string matrix. Pure; inverse of `rowsToMatrix`. */
20
+ export declare function parseCSVMatrix(text: string): string[][];
21
+ /**
22
+ * Parse CSV text into grid rows. The first line is the header (mapped to columns
23
+ * by `header` or `key`); numeric columns coerce to numbers, `date` columns to
24
+ * epoch ms. Rows get `id` + flash fields so they're `GridRow`-ready. Inverse of
25
+ * `toCSV` — round-trips. Pure; unit-tested.
26
+ */
27
+ export declare function parseCSV(text: string, columns?: readonly ColumnDef[]): GridRow[];
28
+ /** Parse TAB-separated text into grid rows (same mapping as `parseCSV`). Handy for
29
+ spreadsheet/clipboard data — `Ctrl/⌘+C` copies the selection as TSV. */
30
+ export declare function parseTSV(text: string, columns?: readonly ColumnDef[]): GridRow[];
31
+ /**
32
+ * Adapt plain objects (e.g. a JSON API response) to grid rows: stamp `id` (the
33
+ * object's own `id`, else the index) and `flashSeq`/`flashDir` if absent, keeping
34
+ * all fields. The cheapest path from `await res.json()` to `<Grid rows>`.
35
+ */
36
+ export declare function rowsFromObjects(objects: readonly Record<string, unknown>[]): GridRow[];
37
+ /** Parse a JSON array of objects into grid rows (`JSON.parse` + `rowsFromObjects`).
38
+ Throws on invalid JSON or a non-array top level. */
39
+ export declare function parseJSON(text: string): GridRow[];
40
+ /**
41
+ * Smart import: detect the format of `text` — a JSON array, TSV, or CSV — and
42
+ * parse it into grid rows. Ideal for a paste handler where the source is unknown:
43
+ *
44
+ * el.addEventListener('paste', (e) => {
45
+ * rows = parseRows(e.clipboardData.getData('text'), columns);
46
+ * });
47
+ *
48
+ * Leading `[` → JSON (falls through to delimited if it isn't valid JSON); a tab in
49
+ * the first line → TSV; otherwise CSV. Pure; unit-tested.
50
+ */
51
+ export declare function parseRows(text: string, columns?: readonly ColumnDef[]): GridRow[];
19
52
  export {};
@@ -49,3 +49,156 @@ export function download(filename, content, mime = 'text/csv;charset=utf-8') {
49
49
  export function exportCSV(filename, rows, columns, opts = {}) {
50
50
  download(filename, toCSV(rows, columns, opts));
51
51
  }
52
+ /**
53
+ * Parse delimited text (CSV/TSV) into a 2-D string matrix — handles quoted fields
54
+ * with embedded delimiters, doubled quotes and newlines, and CRLF or LF endings.
55
+ */
56
+ function parseDelimited(text, delim) {
57
+ const rows = [];
58
+ let row = [];
59
+ let field = '';
60
+ let inQuotes = false;
61
+ let i = 0;
62
+ const endField = () => {
63
+ row.push(field);
64
+ field = '';
65
+ };
66
+ const endRow = () => {
67
+ endField();
68
+ rows.push(row);
69
+ row = [];
70
+ };
71
+ while (i < text.length) {
72
+ const c = text[i];
73
+ if (inQuotes) {
74
+ if (c === '"') {
75
+ if (text[i + 1] === '"') {
76
+ field += '"';
77
+ i += 2;
78
+ }
79
+ else {
80
+ inQuotes = false;
81
+ i++;
82
+ }
83
+ }
84
+ else {
85
+ field += c;
86
+ i++;
87
+ }
88
+ continue;
89
+ }
90
+ if (c === '"') {
91
+ inQuotes = true;
92
+ i++;
93
+ }
94
+ else if (c === delim) {
95
+ endField();
96
+ i++;
97
+ }
98
+ else if (c === '\n') {
99
+ endRow();
100
+ i++;
101
+ }
102
+ else if (c === '\r') {
103
+ i++; // CRLF: the \n ends the row
104
+ }
105
+ else {
106
+ field += c;
107
+ i++;
108
+ }
109
+ }
110
+ if (field !== '' || row.length > 0)
111
+ endRow(); // trailing line without a newline
112
+ return rows;
113
+ }
114
+ /** Parse RFC4180 CSV text into a 2-D string matrix. Pure; inverse of `rowsToMatrix`. */
115
+ export function parseCSVMatrix(text) {
116
+ return parseDelimited(text, ',');
117
+ }
118
+ // Header-row matrix → grid rows: map each header to a column (by `header` or
119
+ // `key`), coerce numeric columns to numbers and `date` columns to epoch ms, leave
120
+ // blanks/unparseable as-is, and stamp id + flash fields. Drops blank lines.
121
+ function matrixToRows(matrix, columns) {
122
+ const m = matrix.filter((r) => !(r.length === 1 && r[0] === ''));
123
+ if (m.length < 1)
124
+ return [];
125
+ const headers = m[0];
126
+ const cols = headers.map((h) => columns.find((c) => c.header === h || c.key === h));
127
+ return m.slice(1).map((cells, i) => {
128
+ const row = { id: i, flashSeq: 0, flashDir: 'up' };
129
+ headers.forEach((h, c) => {
130
+ const col = cols[c];
131
+ const raw = cells[c] ?? '';
132
+ const key = col ? col.key : h;
133
+ if (raw === '') {
134
+ row[key] = '';
135
+ }
136
+ else if (col?.type === 'date') {
137
+ const ms = Date.parse(raw);
138
+ row[key] = Number.isFinite(ms) ? ms : raw;
139
+ }
140
+ else if (col && isNumeric(col)) {
141
+ const n = Number(raw);
142
+ row[key] = Number.isFinite(n) ? n : raw;
143
+ }
144
+ else {
145
+ row[key] = raw;
146
+ }
147
+ });
148
+ return row;
149
+ });
150
+ }
151
+ /**
152
+ * Parse CSV text into grid rows. The first line is the header (mapped to columns
153
+ * by `header` or `key`); numeric columns coerce to numbers, `date` columns to
154
+ * epoch ms. Rows get `id` + flash fields so they're `GridRow`-ready. Inverse of
155
+ * `toCSV` — round-trips. Pure; unit-tested.
156
+ */
157
+ export function parseCSV(text, columns = []) {
158
+ return matrixToRows(parseDelimited(text, ','), columns);
159
+ }
160
+ /** Parse TAB-separated text into grid rows (same mapping as `parseCSV`). Handy for
161
+ spreadsheet/clipboard data — `Ctrl/⌘+C` copies the selection as TSV. */
162
+ export function parseTSV(text, columns = []) {
163
+ return matrixToRows(parseDelimited(text, '\t'), columns);
164
+ }
165
+ /**
166
+ * Adapt plain objects (e.g. a JSON API response) to grid rows: stamp `id` (the
167
+ * object's own `id`, else the index) and `flashSeq`/`flashDir` if absent, keeping
168
+ * all fields. The cheapest path from `await res.json()` to `<Grid rows>`.
169
+ */
170
+ export function rowsFromObjects(objects) {
171
+ return objects.map((o, i) => ({ id: i, flashSeq: 0, flashDir: 'up', ...o }));
172
+ }
173
+ /** Parse a JSON array of objects into grid rows (`JSON.parse` + `rowsFromObjects`).
174
+ Throws on invalid JSON or a non-array top level. */
175
+ export function parseJSON(text) {
176
+ const data = JSON.parse(text);
177
+ if (!Array.isArray(data))
178
+ throw new Error('parseJSON: expected a JSON array of objects');
179
+ return rowsFromObjects(data);
180
+ }
181
+ /**
182
+ * Smart import: detect the format of `text` — a JSON array, TSV, or CSV — and
183
+ * parse it into grid rows. Ideal for a paste handler where the source is unknown:
184
+ *
185
+ * el.addEventListener('paste', (e) => {
186
+ * rows = parseRows(e.clipboardData.getData('text'), columns);
187
+ * });
188
+ *
189
+ * Leading `[` → JSON (falls through to delimited if it isn't valid JSON); a tab in
190
+ * the first line → TSV; otherwise CSV. Pure; unit-tested.
191
+ */
192
+ export function parseRows(text, columns = []) {
193
+ if (text.trimStart().startsWith('[')) {
194
+ try {
195
+ return parseJSON(text);
196
+ }
197
+ catch {
198
+ // Not valid JSON after all — treat it as delimited text below.
199
+ }
200
+ }
201
+ const nl = text.indexOf('\n');
202
+ const firstLine = nl === -1 ? text : text.slice(0, nl);
203
+ return firstLine.includes('\t') ? parseTSV(text, columns) : parseCSV(text, columns);
204
+ }
@@ -0,0 +1,17 @@
1
+ import type { ColumnDef, GridRow } from './column';
2
+ /** Escape a string for safe HTML text/attribute interpolation. */
3
+ export declare function escapeHTML(s: string): string;
4
+ /**
5
+ * Render rows to a semantic `<table>` HTML string (formatted via the column
6
+ * formatters; numeric columns right-aligned; sparkline/custom columns skipped).
7
+ * Values are HTML-escaped. Pure; unit-tested. Embed it, or use `printTable`.
8
+ */
9
+ export declare function toHTMLTable(rows: readonly GridRow[], columns: readonly ColumnDef[]): string;
10
+ /**
11
+ * Open a print window with all rows as a clean table and trigger the print
12
+ * dialog. No-op outside the browser (or if a popup is blocked). For PDFs, users
13
+ * "Save as PDF" from the print dialog.
14
+ */
15
+ export declare function printTable(rows: readonly GridRow[], columns: readonly ColumnDef[], opts?: {
16
+ title?: string;
17
+ }): void;
@@ -0,0 +1,55 @@
1
+ import { formatCell, cellValue, isNumeric } from './column';
2
+ const ESC = {
3
+ '&': '&amp;',
4
+ '<': '&lt;',
5
+ '>': '&gt;',
6
+ '"': '&quot;',
7
+ "'": '&#39;',
8
+ };
9
+ /** Escape a string for safe HTML text/attribute interpolation. */
10
+ export function escapeHTML(s) {
11
+ return s.replace(/[&<>"']/g, (c) => ESC[c]);
12
+ }
13
+ /**
14
+ * Render rows to a semantic `<table>` HTML string (formatted via the column
15
+ * formatters; numeric columns right-aligned; sparkline/custom columns skipped).
16
+ * Values are HTML-escaped. Pure; unit-tested. Embed it, or use `printTable`.
17
+ */
18
+ export function toHTMLTable(rows, columns) {
19
+ const cols = columns.filter((c) => c.type !== 'sparkline' && c.type !== 'custom');
20
+ const align = (c) => (isNumeric(c) ? ' style="text-align:right"' : '');
21
+ const head = cols.map((c) => `<th${align(c)}>${escapeHTML(c.header)}</th>`).join('');
22
+ const body = rows
23
+ .map((row) => '<tr>' +
24
+ cols.map((c) => `<td${align(c)}>${escapeHTML(formatCell(c, cellValue(c, row), row))}</td>`).join('') +
25
+ '</tr>')
26
+ .join('');
27
+ return `<table><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table>`;
28
+ }
29
+ const PRINT_CSS = `
30
+ *{box-sizing:border-box}
31
+ body{font:12px -apple-system,system-ui,sans-serif;color:#111;margin:24px}
32
+ h1{font-size:16px;margin:0 0 12px}
33
+ table{border-collapse:collapse;width:100%}
34
+ th,td{padding:4px 8px;border:1px solid #ddd;white-space:nowrap;text-align:left}
35
+ thead th{background:#f4f4f4;font-weight:600}
36
+ tbody tr:nth-child(even){background:#fafafa}
37
+ @media print{body{margin:0}}`;
38
+ /**
39
+ * Open a print window with all rows as a clean table and trigger the print
40
+ * dialog. No-op outside the browser (or if a popup is blocked). For PDFs, users
41
+ * "Save as PDF" from the print dialog.
42
+ */
43
+ export function printTable(rows, columns, opts = {}) {
44
+ if (typeof window === 'undefined' || typeof window.open !== 'function')
45
+ return;
46
+ const win = window.open('', '_blank');
47
+ if (!win)
48
+ return; // popup blocked
49
+ const title = opts.title ?? 'Grid';
50
+ win.document.write(`<!doctype html><html><head><meta charset="utf-8"><title>${escapeHTML(title)}</title>` +
51
+ `<style>${PRINT_CSS}</style></head><body><h1>${escapeHTML(title)}</h1>` +
52
+ `${toHTMLTable(rows, columns)}` +
53
+ `<script>window.onload=function(){window.print()}<\/script></body></html>`);
54
+ win.document.close();
55
+ }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,12 @@
1
+ import type { ComponentProps } from 'svelte';
2
+ import type GridComponent from './grid/Grid.svelte';
1
3
  export { default as Grid } from './grid/Grid.svelte';
2
4
  export { default as Sparkline } from './sparkline/Sparkline.svelte';
5
+ /** Every `<Grid>` prop, as one object type. Use it to type a config object —
6
+ especially the `<bo-grid>` web component's `config` (see `bo-grid/element`). */
7
+ export type GridProps = ComponentProps<typeof GridComponent>;
3
8
  export type { LazyGroup } from './grid/grouping';
4
- export type { ColumnDef, Align, GridRow, SortDir, SortState, CellEditEvent, BadgeTone, DataBarConfig, IconRule, ColorScaleConfig, } from './grid/column';
9
+ export type { ColumnDef, Align, GridRow, SortDir, SortState, CellEditEvent, CellRenderContext, BadgeTone, DataBarConfig, IconRule, ColorScaleConfig, } from './grid/column';
5
10
  export type { AggKind, AggResult } from './grid/aggregate';
6
11
  export type { ColumnFilter, FilterKind, TextOp, NumberOp, DateOp } from './grid/filtering';
7
12
  export { fmtPrice, fmtPercent, fmtVolume, fmtDate, fmtCurrency, relativeTime } from './format/format';
@@ -13,9 +18,10 @@ export type { PivotConfig, PivotResult } from './grid/pivot';
13
18
  export { themeVars, darkTheme, lightTheme, highContrastDark, highContrastLight, midnightTheme, terminalTheme, themePresets, } from './grid/theme';
14
19
  export type { GridTheme, ThemePreset } from './grid/theme';
15
20
  export { drawCandles, setupHiDpiCanvas } from './sparkline/sparkline-render';
16
- export { toCSV, exportCSV } from './grid/export';
21
+ export { toCSV, exportCSV, parseCSV, parseCSVMatrix, parseTSV, parseJSON, parseRows, rowsFromObjects, } from './grid/export';
17
22
  export { exportXLSX } from './grid/export-xlsx';
18
23
  export type { ExportOptions } from './grid/export';
24
+ export { toHTMLTable, printTable, escapeHTML } from './grid/print';
19
25
  export { createArraySource } from './grid/source';
20
26
  export type { RowSource, RowRange, RowSourceParams, RowSourceResult, ArraySourceOptions, } from './grid/source';
21
27
  export type { Candle } from './types';
package/dist/index.js CHANGED
@@ -4,7 +4,6 @@
4
4
  // This surface is intentionally small — every export is a compatibility promise.
5
5
  // Internal helpers (layout, sorting, grouping, selection internals) are NOT
6
6
  // exported; they can change freely between versions.
7
- // Components
8
7
  export { default as Grid } from './grid/Grid.svelte';
9
8
  export { default as Sparkline } from './sparkline/Sparkline.svelte';
10
9
  // Value formatters (handy when building custom cell content)
@@ -17,8 +16,10 @@ export { pivot } from './grid/pivot';
17
16
  export { themeVars, darkTheme, lightTheme, highContrastDark, highContrastLight, midnightTheme, terminalTheme, themePresets, } from './grid/theme';
18
17
  // Sparkline canvas primitives (draw candlesticks on your own canvas)
19
18
  export { drawCandles, setupHiDpiCanvas } from './sparkline/sparkline-render';
20
- // Export (CSV is dependency-free; XLSX dynamic-imports the optional `xlsx` peer)
21
- export { toCSV, exportCSV } from './grid/export';
19
+ // Export + import (CSV is dependency-free; XLSX dynamic-imports the optional `xlsx` peer)
20
+ export { toCSV, exportCSV, parseCSV, parseCSVMatrix, parseTSV, parseJSON, parseRows, rowsFromObjects, } from './grid/export';
22
21
  export { exportXLSX } from './grid/export-xlsx';
22
+ // Printable / export HTML (renders ALL rows, unlike the virtualized grid)
23
+ export { toHTMLTable, printTable, escapeHTML } from './grid/print';
23
24
  // Server-side / windowed data source
24
25
  export { createArraySource } from './grid/source';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bo-grid",
3
- "version": "0.21.0",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Tiny, fast Svelte 5 data grid: canvas sparklines, batched realtime cell updates, and virtual scrolling. A free, fintech-focused alternative to heavyweight grids.",
@@ -39,6 +39,7 @@
39
39
  "default": "./dist/charts/index.js"
40
40
  },
41
41
  "./element": {
42
+ "types": "./dist/bo-grid.element.d.ts",
42
43
  "default": "./dist/bo-grid.element.js"
43
44
  },
44
45
  "./package.json": "./package.json"
@@ -61,7 +62,7 @@
61
62
  "demo:build": "vite build",
62
63
  "pages:build": "vite build --base=/bo-grid/ --outDir demo-dist",
63
64
  "preview": "vite preview",
64
- "package": "svelte-package -i src/lib -o dist && node scripts/clean-dist.mjs && pnpm run build:wc",
65
+ "package": "svelte-package -i src/lib -o dist && node scripts/clean-dist.mjs && pnpm run build:wc && node scripts/emit-element-dts.mjs",
65
66
  "build:wc": "vite build --config vite.wc.config.ts",
66
67
  "prepublishOnly": "pnpm run package",
67
68
  "check": "svelte-check --tsconfig ./tsconfig.json",
@@ -69,6 +70,7 @@
69
70
  "size:lib": "vite build --config vite.lib.config.ts && node scripts/size-lib.mjs",
70
71
  "smoke": "vite build --base=./ --outDir demo-dist && node scripts/smoke.mjs",
71
72
  "smoke:wc": "node scripts/wc-smoke.mjs",
73
+ "check:examples": "node scripts/check-examples.mjs",
72
74
  "ssr": "node scripts/ssr.mjs",
73
75
  "bench": "node scripts/bench.mjs",
74
76
  "test": "vitest run",