bo-grid 0.25.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 +98 -17
- 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/index.d.ts +6 -1
- package/dist/index.js +0 -1
- package/package.json +3 -2
- package/dist/bo-grid.element-DPnHUXMa.js +0 -6623
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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, with a core that gzips to ~
|
|
4
|
+
realtime cell updates, and virtual scrolling, with a core that gzips to ~32 KB
|
|
5
5
|
(Svelte external; unused exports tree-shake). A free alternative to the heavyweight
|
|
6
6
|
grids that paywall these features.
|
|
7
7
|
|
|
@@ -13,8 +13,8 @@ grids that paywall these features.
|
|
|
13
13
|
The demo is a gallery of grid types — a realtime **Trading desk**, a grouped
|
|
14
14
|
**Portfolio** with subtotals and pivot, an editable **Spreadsheet**, a live
|
|
15
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 —
|
|
17
|
-
them
|
|
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,7 +37,7 @@ 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
43
|
bo-grid ships most of the features other grids put behind a **paid (Enterprise)**
|
|
@@ -100,22 +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
|
+
`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
|
|
114
118
|
**[docs/frameworks.md](./docs/frameworks.md)** for per-framework recipes and
|
|
115
|
-
**[examples/](./examples/)** for runnable, build-free starters. (
|
|
116
|
-
`
|
|
117
|
-
`
|
|
118
|
-
smaller, and snippets work.)
|
|
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.)
|
|
119
123
|
|
|
120
124
|
## Column types
|
|
121
125
|
|
|
@@ -160,6 +164,52 @@ buttons, links. The snippet receives `{ row, column, value }`:
|
|
|
160
164
|
<Grid {rows} {columns} {cell} height={640} />
|
|
161
165
|
```
|
|
162
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
|
+
|
|
163
213
|
## Conditional formatting
|
|
164
214
|
|
|
165
215
|
Paint analytics cues straight into numeric cells — no custom snippet needed.
|
|
@@ -261,13 +311,28 @@ a function for variable per-row heights (in-memory mode):
|
|
|
261
311
|
Variable heights use a prefix-sum + binary-search virtualizer, so scrolling stays
|
|
262
312
|
O(log n). Source mode is uniform-only (unloaded row heights aren't known).
|
|
263
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
|
+
|
|
264
327
|
## Pagination
|
|
265
328
|
|
|
266
329
|
Prefer pages over one long scroll? Set `pageSize` (> 0) for a paged view with a
|
|
267
|
-
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.
|
|
268
333
|
|
|
269
334
|
```svelte
|
|
270
|
-
<Grid {rows} {columns} height={640} pageSize={25} />
|
|
335
|
+
<Grid {rows} {columns} height={640} pageSize={25} pageSizeOptions={[25, 50, 100]} />
|
|
271
336
|
```
|
|
272
337
|
|
|
273
338
|
## Sort & filter
|
|
@@ -330,6 +395,22 @@ value(s) across the extended range (editable columns; multi-cell selections tile
|
|
|
330
395
|
Edits, paste and fill are **undoable** with <kbd>Ctrl/⌘</kbd>+<kbd>Z</kbd> (redo
|
|
331
396
|
with <kbd>Ctrl/⌘</kbd>+<kbd>Y</kbd>). A paste or fill undoes as a single step.
|
|
332
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
|
+
|
|
333
414
|
When more than one cell is selected, a footer bar shows live **Sum / Avg / Count /
|
|
334
415
|
Min / Max** over the numeric cells in the range — and it keeps updating as a
|
|
335
416
|
realtime feed ticks. Choose which stats to show:
|
|
@@ -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",
|