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.
- package/README.md +146 -31
- package/dist/{bo-grid.FilterMenu-BHI6rILc.js → bo-grid.FilterMenu-BBxlxrnZ.js} +1 -1
- package/dist/{bo-grid.ToolPanel-C3u-4YKc.js → bo-grid.ToolPanel-CCBi982x.js} +1 -1
- package/dist/bo-grid.element-BZGnfKB_.js +6898 -0
- package/dist/bo-grid.element.d.ts +17 -0
- package/dist/bo-grid.element.js +3 -2
- package/dist/grid/Cell.svelte +61 -9
- package/dist/grid/Grid.svelte +213 -19
- package/dist/grid/Grid.svelte.d.ts +21 -1
- package/dist/grid/Pager.svelte +45 -0
- package/dist/grid/Pager.svelte.d.ts +3 -0
- package/dist/grid/column.d.ts +36 -3
- package/dist/grid/column.js +17 -0
- package/dist/grid/export.d.ts +33 -0
- package/dist/grid/export.js +153 -0
- package/dist/grid/print.d.ts +17 -0
- package/dist/grid/print.js +55 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -3
- package/package.json +4 -2
- package/dist/bo-grid.element-DPnHUXMa.js +0 -6623
package/README.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# bo-grid
|
|
2
2
|
|
|
3
3
|
Tiny, fast **Svelte 5** data grid for fintech UIs — canvas sparklines, batched
|
|
4
|
-
realtime cell updates, and virtual scrolling
|
|
5
|
-
A free alternative to the heavyweight
|
|
4
|
+
realtime cell updates, and virtual scrolling, with a core that gzips to ~32 KB
|
|
5
|
+
(Svelte external; unused exports tree-shake). A free alternative to the heavyweight
|
|
6
|
+
grids that paywall these features.
|
|
6
7
|
|
|
7
8
|
**[Live demo](https://bonguynvan.github.io/bo-grid/)** ·
|
|
8
9
|
**[API reference](https://bonguynvan.github.io/bo-grid/api.html)** ·
|
|
9
10
|
**[Benchmarks](./BENCHMARKS.md)** ·
|
|
10
11
|
**[Roadmap](./ROADMAP.md)**
|
|
11
12
|
|
|
12
|
-
The demo is a
|
|
13
|
-
|
|
14
|
-
**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
from a synthetic source — switch between them with the tabs.
|
|
13
|
+
The demo is a gallery of grid types — a realtime **Trading desk**, a grouped
|
|
14
|
+
**Portfolio** with subtotals and pivot, an editable **Spreadsheet**, a live
|
|
15
|
+
**Order book**, a **Correlation** heatmap, a **Dashboard** with in-cell charts, a
|
|
16
|
+
**Wide** 60-column grid, a server-backed **Lazy tree**, and more — all on one
|
|
17
|
+
page, each grid lazy-mounting as you scroll (jump between them from the side rail).
|
|
18
18
|
|
|
19
19
|
> **Status: actively developed.** Working: config-driven columns, virtual scroll,
|
|
20
20
|
> sort (single / multi / controlled), filtering (global, per-column row, header
|
|
@@ -37,13 +37,12 @@ from a synthetic source — switch between them with the tabs.
|
|
|
37
37
|
| Price | $$$ / dev / year | Free (MIT) |
|
|
38
38
|
| Sparklines | paid tier | built in |
|
|
39
39
|
| Realtime cell updates | DIY / complex | built-in primitive |
|
|
40
|
-
| Bundle | hundreds of KB | **~
|
|
40
|
+
| Bundle | hundreds of KB | **~32 KB gzip core** ([benchmarks](./BENCHMARKS.md)) |
|
|
41
41
|
| Svelte | wrapper | native Svelte 5 |
|
|
42
42
|
|
|
43
|
-
bo-grid ships most of
|
|
44
|
-
tree data, master-detail, range selection, Excel export,
|
|
45
|
-
|
|
46
|
-
what it doesn't do) to decide.
|
|
43
|
+
bo-grid ships most of the features other grids put behind a **paid (Enterprise)**
|
|
44
|
+
tier — grouping, pivot, tree data, master-detail, range selection, Excel export,
|
|
45
|
+
sparklines — for free, and runs in any framework via a [custom element](./docs/frameworks.md).
|
|
47
46
|
|
|
48
47
|
## Install
|
|
49
48
|
|
|
@@ -56,7 +55,8 @@ Works with **SvelteKit / SSR** out of the box — `<Grid>` server-renders to HTM
|
|
|
56
55
|
without touching `window`/`document`/`localStorage` (a CI gate, `pnpm ssr`,
|
|
57
56
|
proves it). The package is `sideEffects: false`, so unused exports tree-shake
|
|
58
57
|
away. See the **[SvelteKit guide](./docs/sveltekit.md)** for `load`-function data,
|
|
59
|
-
realtime feeds,
|
|
58
|
+
server-side / lazy loading, realtime feeds, import helpers, charts, printing, and
|
|
59
|
+
layout persistence.
|
|
60
60
|
|
|
61
61
|
## Usage
|
|
62
62
|
|
|
@@ -100,21 +100,26 @@ keep frames smooth.
|
|
|
100
100
|
|
|
101
101
|
### React, Vue, Angular & vanilla
|
|
102
102
|
|
|
103
|
-
bo-grid also ships a framework-agnostic **custom element
|
|
104
|
-
the whole API through a `config` property:
|
|
103
|
+
bo-grid also ships a framework-agnostic **custom element**, fully typed. Import it
|
|
104
|
+
and drive the whole API through a `config` property:
|
|
105
105
|
|
|
106
|
-
```
|
|
107
|
-
import 'bo-grid/element'; // registers <bo-grid>, injects styles
|
|
106
|
+
```ts
|
|
107
|
+
import { createBoGrid } from 'bo-grid/element'; // registers <bo-grid>, injects styles
|
|
108
|
+
import type { BoGridConfig } from 'bo-grid/element';
|
|
108
109
|
|
|
109
|
-
const
|
|
110
|
-
|
|
110
|
+
const config: BoGridConfig = { columns, rows, theme: 'dark', height: 520 };
|
|
111
|
+
document.body.append(createBoGrid(config)); // or set `el.config` on an existing element
|
|
111
112
|
```
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
`bo-grid/element` exports `BoGridConfig` (every `<Grid>` prop), a typed
|
|
115
|
+
`BoGridElement`, and the `createBoGrid` helper. `config` is safe to set **after**
|
|
116
|
+
the element attaches (the React `ref` + `useEffect` pattern won't crash). It works
|
|
117
|
+
in React, Vue, Angular and plain HTML — see
|
|
118
|
+
**[docs/frameworks.md](./docs/frameworks.md)** for per-framework recipes and
|
|
119
|
+
**[examples/](./examples/)** for runnable, build-free starters. (Svelte `cell`/
|
|
120
|
+
`detail` snippets are Svelte-only; from other frameworks use a column's
|
|
121
|
+
`render(ctx)` hook, built-in types, or `format`. Native Svelte users should import
|
|
122
|
+
`Grid` directly — smaller, and snippets work.)
|
|
118
123
|
|
|
119
124
|
## Column types
|
|
120
125
|
|
|
@@ -159,6 +164,52 @@ buttons, links. The snippet receives `{ row, column, value }`:
|
|
|
159
164
|
<Grid {rows} {columns} {cell} height={640} />
|
|
160
165
|
```
|
|
161
166
|
|
|
167
|
+
**From plain JS (React/Vue/vanilla, `<bo-grid>`):** snippets are Svelte-only, so
|
|
168
|
+
give a column a **`render(ctx)`** function instead. Return an `HTMLElement`/`Node`
|
|
169
|
+
(appended as-is — safe) or an HTML **string** (inserted as innerHTML — sanitize
|
|
170
|
+
any untrusted data yourself; prefer a Node for that). `ctx` is `{ value, row,
|
|
171
|
+
column }`. Display only — sort, filter, tooltip, copy and export still use the
|
|
172
|
+
value / `format`:
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
{ type: 'custom', key: 'trend', header: 'Trend', render: ({ row }) => {
|
|
176
|
+
const el = document.createElement('span');
|
|
177
|
+
el.textContent = row.changePct > 0 ? '▲' : '▼';
|
|
178
|
+
el.style.color = row.changePct > 0 ? 'var(--bo-grid-up)' : 'var(--bo-grid-down)';
|
|
179
|
+
return el;
|
|
180
|
+
} }
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Tooltips & truncation
|
|
184
|
+
|
|
185
|
+
Long cell values truncate with an **ellipsis** by default. Set `tooltip` on a
|
|
186
|
+
column to reveal the full text in a **styled floating tooltip** on hover (themed,
|
|
187
|
+
instant — not the native `title`). `tooltip: true` shows the formatted value;
|
|
188
|
+
pass a function for custom text built from the whole row (works for any type,
|
|
189
|
+
including `custom`):
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const columns: ColumnDef[] = [
|
|
193
|
+
{ type: 'text', key: 'note', header: 'Notes', width: 220, tooltip: true },
|
|
194
|
+
{ type: 'badge', key: 'status', header: 'Status',
|
|
195
|
+
tooltip: (value, row) => `${value} · ${row.role}` }, // custom text
|
|
196
|
+
{ type: 'text', key: 'bio', header: 'Bio', flex: 1, wrap: true }, // wrap, no ellipsis
|
|
197
|
+
];
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Set `wrap: true` to let a column wrap onto multiple lines instead of truncating
|
|
201
|
+
(pair with a taller `rowHeight`).
|
|
202
|
+
|
|
203
|
+
**Header tooltips:** `headerTooltip` shows the same styled bubble on the column
|
|
204
|
+
header (describe the metric, units, caveats); add `headerInfo: true` for a small
|
|
205
|
+
ⓘ cue:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
{ type: 'progress', key: 'workload', header: 'Workload', min: 0, max: 100,
|
|
209
|
+
headerTooltip: 'Share of capacity allocated this sprint (0–100%).',
|
|
210
|
+
headerInfo: true }
|
|
211
|
+
```
|
|
212
|
+
|
|
162
213
|
## Conditional formatting
|
|
163
214
|
|
|
164
215
|
Paint analytics cues straight into numeric cells — no custom snippet needed.
|
|
@@ -260,13 +311,28 @@ a function for variable per-row heights (in-memory mode):
|
|
|
260
311
|
Variable heights use a prefix-sum + binary-search virtualizer, so scrolling stays
|
|
261
312
|
O(log n). Source mode is uniform-only (unloaded row heights aren't known).
|
|
262
313
|
|
|
314
|
+
### Sizing the grid
|
|
315
|
+
|
|
316
|
+
`height` as a **number** is the scroll viewport's pixel height (the element is
|
|
317
|
+
that plus header/pager/footer chrome). Pass a **CSS string** to size the whole
|
|
318
|
+
element instead and let the viewport auto-fit the space left over — ideal for
|
|
319
|
+
filling a flex/grid cell:
|
|
320
|
+
|
|
321
|
+
```svelte
|
|
322
|
+
<Grid {rows} {columns} height={640} /> <!-- 640px viewport -->
|
|
323
|
+
<Grid {rows} {columns} height="100%" /> <!-- fill a sized parent -->
|
|
324
|
+
<Grid {rows} {columns} height="80vh" />
|
|
325
|
+
```
|
|
326
|
+
|
|
263
327
|
## Pagination
|
|
264
328
|
|
|
265
329
|
Prefer pages over one long scroll? Set `pageSize` (> 0) for a paged view with a
|
|
266
|
-
first/prev/next/last pager; rows still virtualize within each page.
|
|
330
|
+
first/prev/next/last pager; rows still virtualize within each page. Add
|
|
331
|
+
`pageSizeOptions` for a **rows-per-page dropdown** in the pager (observe changes
|
|
332
|
+
via `onPageSizeChange`). In-memory mode.
|
|
267
333
|
|
|
268
334
|
```svelte
|
|
269
|
-
<Grid {rows} {columns} height={640} pageSize={25} />
|
|
335
|
+
<Grid {rows} {columns} height={640} pageSize={25} pageSizeOptions={[25, 50, 100]} />
|
|
270
336
|
```
|
|
271
337
|
|
|
272
338
|
## Sort & filter
|
|
@@ -329,6 +395,22 @@ value(s) across the extended range (editable columns; multi-cell selections tile
|
|
|
329
395
|
Edits, paste and fill are **undoable** with <kbd>Ctrl/⌘</kbd>+<kbd>Z</kbd> (redo
|
|
330
396
|
with <kbd>Ctrl/⌘</kbd>+<kbd>Y</kbd>). A paste or fill undoes as a single step.
|
|
331
397
|
|
|
398
|
+
For a read-only / display grid, set `cellSelection={false}` to drop the blue
|
|
399
|
+
range-selection highlight (and fill handle) entirely — clicks pass straight
|
|
400
|
+
through to `onRowClick` / `onCellClick`:
|
|
401
|
+
|
|
402
|
+
```svelte
|
|
403
|
+
<Grid {rows} {columns} height={640} cellSelection={false} onRowClick={open} />
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
For a **list-detail / master-detail** layout, drive `selectedRowId` (keyed by
|
|
407
|
+
`getRowId`) to highlight the active row — independent of the checkbox selection:
|
|
408
|
+
|
|
409
|
+
```svelte
|
|
410
|
+
<Grid {rows} {columns} height={640}
|
|
411
|
+
selectedRowId={active?.id} onRowClick={(r) => (active = r)} />
|
|
412
|
+
```
|
|
413
|
+
|
|
332
414
|
When more than one cell is selected, a footer bar shows live **Sum / Avg / Count /
|
|
333
415
|
Min / Max** over the numeric cells in the range — and it keeps updating as a
|
|
334
416
|
realtime feed ticks. Choose which stats to show:
|
|
@@ -710,17 +792,38 @@ const columns = [
|
|
|
710
792
|
];
|
|
711
793
|
```
|
|
712
794
|
|
|
713
|
-
## Export
|
|
795
|
+
## Export & import
|
|
714
796
|
|
|
715
|
-
CSV export
|
|
797
|
+
CSV export — and import — are dependency-free:
|
|
716
798
|
|
|
717
799
|
```ts
|
|
718
|
-
import { exportCSV, toCSV } from 'bo-grid';
|
|
800
|
+
import { exportCSV, toCSV, parseCSV } from 'bo-grid';
|
|
719
801
|
|
|
720
802
|
exportCSV('tickers.csv', rows, columns); // triggers a download
|
|
721
803
|
const text = toCSV(rows, columns, { formatted: true }); // or get the string
|
|
804
|
+
const rows = parseCSV(text, columns); // …and back to rows (round-trip)
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
`parseCSV` is RFC4180-aware (quoted fields, embedded commas/quotes/newlines), maps
|
|
808
|
+
headers to columns, coerces numeric/`date` columns, and stamps `id` + flash fields
|
|
809
|
+
so the result drops straight into `<Grid rows={…}>`. There's also `parseTSV` (tab-
|
|
810
|
+
separated — what Ctrl/⌘+C copies), and for JSON/API data, `rowsFromObjects(objects)`
|
|
811
|
+
/ `parseJSON(text)`:
|
|
812
|
+
|
|
813
|
+
```ts
|
|
814
|
+
import { rowsFromObjects } from 'bo-grid';
|
|
815
|
+
const rows = rowsFromObjects(await (await fetch('/api/rows')).json());
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
Not sure what you'll get? **`parseRows(text, columns?)`** auto-detects JSON / TSV /
|
|
819
|
+
CSV — perfect for a paste handler:
|
|
820
|
+
|
|
821
|
+
```svelte
|
|
822
|
+
<div onpaste={(e) => (rows = parseRows(e.clipboardData.getData('text'), columns))}>…</div>
|
|
722
823
|
```
|
|
723
824
|
|
|
825
|
+
See the **CSV import** demo (CSV / TSV / JSON / Auto-detect).
|
|
826
|
+
|
|
724
827
|
Excel export loads SheetJS via **dynamic import**, so it lands in its own lazy
|
|
725
828
|
chunk and never bloats your core bundle. `xlsx` is an **optional peer dependency**
|
|
726
829
|
— install it only if you use this:
|
|
@@ -734,11 +837,23 @@ Sparkline columns are skipped; numeric columns export as raw numbers so
|
|
|
734
837
|
spreadsheets can compute on them (pass `{ formatted: true }` for display strings).
|
|
735
838
|
Ctrl/⌘+C still copies the current selection as TSV.
|
|
736
839
|
|
|
840
|
+
**Printing.** The grid virtualizes, so printing it directly drops off-screen rows.
|
|
841
|
+
`printTable(rows, columns, { title })` opens a print window with **all** rows as a
|
|
842
|
+
clean table (Save-as-PDF from the dialog); `toHTMLTable(rows, columns)` returns
|
|
843
|
+
that table as an HTML string to embed. See the **Print** demo.
|
|
844
|
+
|
|
845
|
+
```ts
|
|
846
|
+
import { printTable } from 'bo-grid';
|
|
847
|
+
printTable(rows, columns, { title: 'Sales report' });
|
|
848
|
+
```
|
|
849
|
+
|
|
737
850
|
## Also exported
|
|
738
851
|
|
|
739
852
|
`Sparkline` component · `drawCandles` / `setupHiDpiCanvas` (draw on your own
|
|
740
853
|
canvas) · `fmtPrice` / `fmtPercent` / `fmtVolume` / `fmtDate` · `heatColor` ·
|
|
741
|
-
`Selection` · `aggregate` · `toCSV` / `exportCSV` / `exportXLSX` / `rowsToMatrix
|
|
854
|
+
`Selection` · `aggregate` · `toCSV` / `exportCSV` / `exportXLSX` / `rowsToMatrix` ·
|
|
855
|
+
`parseCSV` / `parseCSVMatrix` / `parseTSV` / `parseJSON` / `parseRows` /
|
|
856
|
+
`rowsFromObjects` · `toHTMLTable` / `printTable`.
|
|
742
857
|
|
|
743
858
|
## Pivot tables
|
|
744
859
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { p as ce, a as xe, v as me, w as _e, n as x, x as _, s as p, y as L, t as N, z as he, j as ke, f as C, h as w, i as u, m as ge, c as h, A as D, e as F, g as t, B as U, k as S, d as ye, l as f, C as we, o as k, D as Ae, u as Oe, q as Se, r as d, b as A } from "./bo-grid.element-
|
|
1
|
+
import { p as ce, a as xe, v as me, w as _e, n as x, x as _, s as p, y as L, t as N, z as he, j as ke, f as C, h as w, i as u, m as ge, c as h, A as D, e as F, g as t, B as U, k as S, d as ye, l as f, C as we, o as k, D as Ae, u as Oe, q as Se, r as d, b as A } from "./bo-grid.element-BZGnfKB_.js";
|
|
2
2
|
var G = k("<option> </option>"), Ne = k('<input class="bo-fm-in svelte-f7i4av" type="number" placeholder="and" aria-label="Upper value"/>'), Ce = k('<select class="bo-fm-op svelte-f7i4av" aria-label="Operator"></select> <input class="bo-fm-in svelte-f7i4av" type="number" placeholder="value" aria-label="Value"/> <!>', 1), Ee = k('<input class="bo-fm-in svelte-f7i4av" type="date" aria-label="End date"/>'), qe = k('<select class="bo-fm-op svelte-f7i4av" aria-label="Operator"></select> <input class="bo-fm-in svelte-f7i4av" type="date" aria-label="Date"/> <!>', 1), Be = k('<label class="bo-fm-opt svelte-f7i4av"><input type="checkbox"/> <span class="svelte-f7i4av"> </span></label>'), De = k('<input class="bo-fm-in svelte-f7i4av" type="search" placeholder="search…" aria-label="Search values"/> <div class="bo-fm-setbar svelte-f7i4av"><button type="button" class="bo-fm-link svelte-f7i4av">All</button> <button type="button" class="bo-fm-link svelte-f7i4av">None</button></div> <div class="bo-fm-list svelte-f7i4av"></div>', 1), Fe = k('<select class="bo-fm-op svelte-f7i4av" aria-label="Operator"></select> <input class="bo-fm-in svelte-f7i4av" type="text" placeholder="filter…" aria-label="Value"/>', 1), ze = k('<div class="bo-filtermenu svelte-f7i4av" role="dialog" tabindex="-1"><div class="bo-fm-head svelte-f7i4av"> </div> <!> <div class="bo-fm-actions svelte-f7i4av"><button class="bo-fm-btn svelte-f7i4av" type="button">Clear</button> <button class="bo-fm-btn bo-fm-apply svelte-f7i4av" type="button">Apply</button></div></div>');
|
|
3
3
|
const Pe = {
|
|
4
4
|
hash: "svelte-f7i4av",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { p as z, a as S, c as d, s as r, r as l, b as f, e as j, t as g, g as s, d as L, f as P, h as i, i as w, j as T, k as q, l as A, m as E, n as B, u as D, o as k, q as F } from "./bo-grid.element-
|
|
1
|
+
import { p as z, a as S, c as d, s as r, r as l, b as f, e as j, t as g, g as s, d as L, f as P, h as i, i as w, j as T, k as q, l as A, m as E, n as B, u as D, o as k, q as F } from "./bo-grid.element-BZGnfKB_.js";
|
|
2
2
|
var G = k('<label class="bo-tp-opt svelte-7xdno"><input type="checkbox"/> <span class="svelte-7xdno"> </span></label>'), H = k('<div class="bo-toolpanel svelte-7xdno" role="dialog" tabindex="-1" aria-label="Columns"><div class="bo-tp-head svelte-7xdno"><span>Columns</span> <button type="button" class="bo-tp-link svelte-7xdno">Show all</button></div> <input class="bo-tp-search svelte-7xdno" type="search" placeholder="search…" aria-label="Search columns"/> <div class="bo-tp-list svelte-7xdno"></div></div>');
|
|
3
3
|
const I = {
|
|
4
4
|
hash: "svelte-7xdno",
|