bo-grid 0.7.0 → 0.21.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.
Files changed (51) hide show
  1. package/README.md +236 -17
  2. package/dist/bo-grid.FilterMenu-BHI6rILc.js +154 -0
  3. package/dist/bo-grid.ToolPanel-C3u-4YKc.js +34 -0
  4. package/dist/bo-grid.element-DPnHUXMa.js +6623 -0
  5. package/dist/bo-grid.element.js +4 -0
  6. package/dist/charts/BarChart.svelte +50 -0
  7. package/dist/charts/BarChart.svelte.d.ts +16 -0
  8. package/dist/charts/DonutChart.svelte +54 -0
  9. package/dist/charts/DonutChart.svelte.d.ts +18 -0
  10. package/dist/charts/Legend.svelte +47 -0
  11. package/dist/charts/Legend.svelte.d.ts +12 -0
  12. package/dist/charts/LineChart.svelte +59 -0
  13. package/dist/charts/LineChart.svelte.d.ts +14 -0
  14. package/dist/charts/StackedBarChart.svelte +56 -0
  15. package/dist/charts/StackedBarChart.svelte.d.ts +18 -0
  16. package/dist/charts/chart-math.d.ts +57 -0
  17. package/dist/charts/chart-math.js +174 -0
  18. package/dist/charts/index.d.ts +8 -0
  19. package/dist/charts/index.js +11 -0
  20. package/dist/charts/palette.d.ts +4 -0
  21. package/dist/charts/palette.js +14 -0
  22. package/dist/format/format.d.ts +6 -0
  23. package/dist/format/format.js +41 -0
  24. package/dist/grid/Cell.svelte +249 -10
  25. package/dist/grid/Cell.svelte.d.ts +6 -0
  26. package/dist/grid/FilterMenu.svelte +7 -0
  27. package/dist/grid/Grid.svelte +338 -87
  28. package/dist/grid/Grid.svelte.d.ts +19 -0
  29. package/dist/grid/GroupRow.svelte +5 -2
  30. package/dist/grid/Pager.svelte +4 -0
  31. package/dist/grid/RowMenu.svelte +65 -2
  32. package/dist/grid/ToolPanel.svelte +5 -0
  33. package/dist/grid/column.d.ts +133 -0
  34. package/dist/grid/column.js +133 -4
  35. package/dist/grid/colvirt.d.ts +15 -0
  36. package/dist/grid/colvirt.js +43 -0
  37. package/dist/grid/export.js +5 -2
  38. package/dist/grid/filtering.d.ts +5 -2
  39. package/dist/grid/filtering.js +5 -4
  40. package/dist/grid/grouping.d.ts +30 -0
  41. package/dist/grid/grouping.js +33 -0
  42. package/dist/grid/theme.d.ts +25 -0
  43. package/dist/grid/theme.js +84 -0
  44. package/dist/grid/tree.d.ts +19 -7
  45. package/dist/grid/tree.js +16 -11
  46. package/dist/index.d.ts +5 -4
  47. package/dist/index.js +2 -2
  48. package/dist/sparkline/Sparkline.svelte +8 -2
  49. package/dist/sparkline/sparkline-render.d.ts +4 -1
  50. package/dist/sparkline/sparkline-render.js +5 -3
  51. package/package.json +12 -2
package/README.md CHANGED
@@ -16,14 +16,16 @@ matrix, a **Leaderboard** with rank medals and score bars, a **Tree** file
16
16
  explorer, a drag-to-reorder **Tasks** list, and a **1M-row** trade tape windowed
17
17
  from a synthetic source — switch between them with the tabs.
18
18
 
19
- > **Status: v0.2.** Working: config-driven columns, virtual scroll, client
20
- > single/multi/controlled sort + global & per-column filters, multi-cell selection
21
- > + live range aggregation, row grouping (nested, collapsible, sticky headers,
22
- > live subtotals), pivot tables, tree data, master-detail, a server-side
23
- > `RowSource` for huge datasets, CSV/Excel export, drag-to-reorder and
24
- > drag-to-resize columns, pinned columns (left/right), row selection, pagination,
25
- > inline editing with validation, type-to-edit, clipboard copy/paste, sparklines,
26
- > realtime flash, heatmaps, theming, and full keyboard a11y. **SSR/SvelteKit-safe.**
19
+ > **Status: actively developed.** Working: config-driven columns, virtual scroll,
20
+ > sort (single / multi / controlled), filtering (global, per-column row, header
21
+ > filter menus with set / number / date filters, quick search, controlled +
22
+ > server-side), multi-cell selection + live aggregation, grouping (nested, sticky,
23
+ > subtotals), pivot, tree data, master-detail, a server-side `RowSource` for huge
24
+ > datasets, CSV/Excel export, column management (reorder, resize, pin L/R, hide,
25
+ > autosize, tool panel, column menu), spreadsheet editing (inline + typed editors,
26
+ > validation, copy/paste, fill handle, undo/redo), row selection, pagination,
27
+ > sparklines, realtime flash, heatmaps, theming, and full keyboard a11y.
28
+ > **SSR/SvelteKit-safe.**
27
29
  > Unit tests (Vitest), type-check, a headless mount smoke-test, an SSR render
28
30
  > check, and library + demo bundle-size budgets all run in CI. A formal WCAG audit
29
31
  > is the main thing left — see the roadmap.
@@ -96,9 +98,51 @@ Only on-screen rows render DOM, so off-screen updates cost nothing until they
96
98
  scroll into view. Batch bursty feeds into a `requestAnimationFrame` flush to
97
99
  keep frames smooth.
98
100
 
101
+ ### React, Vue, Angular & vanilla
102
+
103
+ bo-grid also ships a framework-agnostic **custom element**. Import it and drive
104
+ the whole API through a `config` property:
105
+
106
+ ```js
107
+ import 'bo-grid/element'; // registers <bo-grid>, injects styles
108
+
109
+ const el = document.querySelector('bo-grid');
110
+ el.config = { columns, rows, theme: 'dark', height: 520 };
111
+ ```
112
+
113
+ It works in React, Vue, Angular and plain HTML — see
114
+ **[docs/frameworks.md](./docs/frameworks.md)** for per-framework recipes. (Custom
115
+ `cell`/`detail` snippets are Svelte-only; use built-in types, `format`, or computed
116
+ `value` from other frameworks. Native Svelte users should import `Grid` directly —
117
+ smaller, and snippets work.)
118
+
99
119
  ## Column types
100
120
 
101
- `text` · `price` · `percent` · `volume` · `number` · `date` · `heatmap` · `sparkline` · `custom`
121
+ **Data:** `text` · `price` · `percent` · `volume` · `number` · `date` · `currency` ·
122
+ `relative` · `heatmap` · `sparkline`
123
+ **Rich:** `progress` · `rating` · `tags` · `badge` · `boolean` · `avatar` · `link`
124
+ **Escape hatch:** `custom`
125
+
126
+ Rich types render value as a widget — handy well beyond fintech (CRM, projects,
127
+ admin, content). All are themed from the design tokens:
128
+
129
+ ```ts
130
+ const columns: ColumnDef[] = [
131
+ { type: 'avatar', key: 'name', header: 'Member', sub: 'role' },
132
+ { type: 'badge', key: 'status', header: 'Status',
133
+ tones: { Active: 'up', Away: 'amber', Offline: 'neutral' } },
134
+ { type: 'progress', key: 'done', header: 'Progress', min: 0, max: 100 },
135
+ { type: 'rating', key: 'score', header: 'Rating', max: 5 },
136
+ { type: 'tags', key: 'skills', header: 'Skills' }, // value: string[]
137
+ { type: 'boolean', key: 'remote', header: 'Remote', trueLabel: 'Remote', falseLabel: 'Office' },
138
+ { type: 'link', key: 'email', header: 'Email', href: (r) => `mailto:${r.email}` },
139
+ { type: 'relative', key: 'seen', header: 'Last seen' }, // value: epoch ms → "3h ago"
140
+ { type: 'currency', key: 'rate', header: 'Rate', currency: 'USD' },
141
+ ];
142
+ ```
143
+
144
+ `link` sanitizes its href (`javascript:`/`data:` are blocked); `relative` formats
145
+ an epoch-ms value as relative time; `currency` localizes via `Intl.NumberFormat`.
102
146
 
103
147
  Sizing: `width` (px) or `flex` (grow weight). See `ColumnDef` for per-type options.
104
148
 
@@ -115,6 +159,94 @@ buttons, links. The snippet receives `{ row, column, value }`:
115
159
  <Grid {rows} {columns} {cell} height={640} />
116
160
  ```
117
161
 
162
+ ## Conditional formatting
163
+
164
+ Paint analytics cues straight into numeric cells — no custom snippet needed.
165
+
166
+ **Data bars** (`dataBar`): an in-cell bar behind the value, scaled across the
167
+ column's range. The range auto-computes over the current view, or set `min`/`max`
168
+ (`min: 0` gives absolute proportional bars). When the range spans negatives, bars
169
+ diverge left/right around a zero baseline. `color`/`negative` override the default
170
+ up/down theme colours.
171
+
172
+ **Icon sets** (`icons`): an icon beside the value, chosen by the highest threshold
173
+ `at` that is ≤ the value. Each rule carries a semantic `tone` (`up` · `down` ·
174
+ `amber` · `info` · `neutral`) for its colour.
175
+
176
+ **Colour scales** (`colorScale`): tint the cell background across the value range —
177
+ a soft, themed heat ramp. Auto-ranges over the view (or set `min`/`max`); pass
178
+ `mid` for a 3-stop diverging scale; `colors` overrides the stops. Works on any
179
+ numeric column (the fixed `heatmap` type still exists for an absolute ramp).
180
+
181
+ ```ts
182
+ const columns: ColumnDef[] = [
183
+ // Proportional bar from zero:
184
+ { type: 'volume', key: 'marketValue', header: 'Mkt Value', dataBar: { min: 0 } },
185
+ // Diverging bar (auto-ranged) + an icon keyed by sign:
186
+ { type: 'number', key: 'pnl', header: 'P&L', decimals: 0,
187
+ dataBar: {},
188
+ icons: [
189
+ { at: -Infinity, icon: '▼', tone: 'down' },
190
+ { at: 0, icon: '▲', tone: 'up' },
191
+ ] },
192
+ // Diverging colour scale around zero (auto-ranged):
193
+ { type: 'number', key: 'pnlPct', header: 'P&L %', decimals: 1, colorScale: { mid: 0 } },
194
+ ];
195
+ ```
196
+
197
+ All three compose with flashing/live cells and add nothing to the core for grids
198
+ that don't use them. (In-memory auto-ranging; pass explicit `min`/`max` in source
199
+ mode.)
200
+
201
+ ## Computed columns
202
+
203
+ Derive a column's value from the whole row with `value: (row) => …` — KPIs,
204
+ ratios, deltas. The derived value flows through display, sort, filter, group/footer
205
+ aggregation, conditional formatting, export and copy, just like a real column.
206
+ `key` still names the column (and is the sort/filter key) but need not be a real
207
+ field. Computed columns aren't editable (there's no field to write back).
208
+
209
+ ```ts
210
+ const columns: ColumnDef[] = [
211
+ { type: 'number', key: 'qty', header: 'Qty' },
212
+ { type: 'price', key: 'price', header: 'Price' },
213
+ // No `total` field on the row — derived, and still sortable/filterable/exportable:
214
+ { type: 'price', key: 'total', header: 'Total', value: (row) => row.qty * row.price,
215
+ groupAgg: 'sum' },
216
+ ];
217
+ ```
218
+
219
+ In-memory mode (a server `source` owns its own derivations). Keep `value()` cheap
220
+ and pure — it's called during sort and filter.
221
+
222
+ ## Charts (companion)
223
+
224
+ For dashboards, `bo-grid/charts` ships tiny, dependency-free SVG charts —
225
+ `LineChart`, `BarChart`, `DonutChart`, `StackedBarChart` (stacked or `grouped`
226
+ multi-series), and a `Legend`. They're a **separate import**, so they add nothing
227
+ to the grid core (~3 KB gzip on their own). Use them standalone, or inside a grid
228
+ cell via a `custom` column. Bar/stacked/donut elements carry an SVG `<title>`, so
229
+ hovering shows the value (accessible, zero-JS).
230
+
231
+ ```svelte
232
+ <script>
233
+ import { LineChart, BarChart, DonutChart, StackedBarChart, Legend } from 'bo-grid/charts';
234
+ </script>
235
+
236
+ <LineChart data={[3, 5, 4, 8, 6, 9]} width={160} height={40} area />
237
+ <BarChart data={[4, 8, 6, 9, 7]} color="var(--up)" />
238
+ <DonutChart data={[{ value: 5, label: 'A' }, { value: 3, label: 'B' }]} />
239
+
240
+ <!-- data[series][category]; stacked by default, `grouped` for side-by-side -->
241
+ <StackedBarChart data={[[3, 5, 2], [4, 1, 6]]} seriesLabels={['Q1', 'Q2']} />
242
+ <Legend items={[{ label: 'Q1' }, { label: 'Q2' }]} />
243
+ ```
244
+
245
+ Theme them with `color` / `colors` props, or by setting `--boc-color` and
246
+ `--boc-1`…`--boc-6` CSS vars on any ancestor. The geometry helpers (`linePoints`,
247
+ `barRects`, `donutArcs`, …) are exported too, for rolling your own SVG charts. See
248
+ the **Dashboard** example for charts inside grid cells.
249
+
118
250
  ## Row height
119
251
 
120
252
  Uniform 36px by default. Pass `rowHeight` as a number for a different density, or
@@ -267,6 +399,26 @@ Group headers are the same height as data rows, so virtual scrolling stays smoot
267
399
  over very large grouped sets. Subtotals recompute live as the feed ticks, and the
268
400
  current group's header stays pinned to the top as you scroll within it.
269
401
 
402
+ ### Server-side grouping
403
+
404
+ When the data is grouped on the server (or too large to fetch upfront), pass
405
+ **`lazyGroups`** (the group summaries) and **`loadGroup`** (load a group's rows on
406
+ expand). Headers show the server-provided count and preformatted aggregates; rows
407
+ load lazily with a loading row, then cache. See the **Server groups** example.
408
+
409
+ ```svelte
410
+ <Grid
411
+ rows={[]}
412
+ {columns}
413
+ lazyGroups={[
414
+ { key: 'North America', count: 142, agg: { amount: '$2.4M' } },
415
+ { key: 'Europe', count: 98, agg: { amount: '$1.8M' } },
416
+ ]}
417
+ loadGroup={(key) => fetch(`/api/orders?group=${key}`).then((r) => r.json())}
418
+ height={520}
419
+ />
420
+ ```
421
+
270
422
  ## Theming
271
423
 
272
424
  Dark-first and self-contained — no CSS import required. Use the `theme` prop with
@@ -277,9 +429,20 @@ a built-in preset or a custom token map:
277
429
  <Grid {rows} {columns} theme={{ bg: '#0b1020', up: '#22d3ee' }} height={640} />
278
430
  ```
279
431
 
280
- Built-in `darkTheme` / `lightTheme` are exported (`GridTheme`). Or set any
281
- `--bo-grid-*` custom property on an ancestor the prop is just a convenience over
282
- these:
432
+ Six built-in presets are exported (`GridTheme`): `darkTheme`, `lightTheme`,
433
+ `highContrastDark`, `highContrastLight`, `midnightTheme`, `terminalTheme`plus a
434
+ `themePresets` name→preset map (and a `ThemePreset` type) for a theme picker:
435
+
436
+ ```svelte
437
+ <script>
438
+ import { Grid, themePresets, type ThemePreset } from 'bo-grid';
439
+ let preset: ThemePreset = 'midnight';
440
+ </script>
441
+ <Grid {rows} {columns} theme={themePresets[preset]} height={640} />
442
+ ```
443
+
444
+ Or set any `--bo-grid-*` custom property on an ancestor — the prop is just a
445
+ convenience over these:
283
446
 
284
447
  ```css
285
448
  .my-app {
@@ -290,6 +453,30 @@ these:
290
453
  }
291
454
  ```
292
455
 
456
+ Native form controls (checkboxes, date pickers, number spinners, search inputs,
457
+ scrollbars) follow the theme automatically via `color-scheme` + `accent-color`.
458
+ A custom theme defaults to dark; set `scheme: 'light'` (or `--bo-grid-scheme:
459
+ light`) for a light one.
460
+
461
+ **Tokens** cover colour, typography (`mono`/`sans`/`fontSize`), shape (`radius`),
462
+ and density (`cellPad`, plus the `rowHeight` prop) — so the whole look is yours.
463
+ Numeric columns use tabular figures so digits line up. A few looks:
464
+
465
+ ```svelte
466
+ <!-- Compact / dense -->
467
+ <Grid {rows} {columns} rowHeight={28}
468
+ theme={{ fontSize: '12px', cellPad: '6px' }} height={640} />
469
+
470
+ <!-- Roomy & rounded -->
471
+ <Grid {rows} {columns} rowHeight={44}
472
+ theme={{ radius: '16px', cellPad: '16px', fontSize: '14px' }} height={640} />
473
+
474
+ <!-- Branded -->
475
+ <Grid {rows} {columns}
476
+ theme={{ bg: '#0b1020', headerBg: '#0d1226', up: '#22d3ee', down: '#fb7185',
477
+ selBorder: '#22d3ee', radius: '12px' }} height={640} />
478
+ ```
479
+
293
480
  ## Server-side / large datasets
294
481
 
295
482
  Instead of an in-memory `rows` array, back the grid with a **`RowSource`** — the
@@ -318,6 +505,18 @@ render as skeletons.
318
505
  the same interface (handy for testing the path or client-side data). Grouping is
319
506
  client-only, so it's not applied in source mode.
320
507
 
508
+ ### Wide grids (column virtualization)
509
+
510
+ Rows are virtualized vertically by default. For very wide grids (100+ columns),
511
+ add **`virtualizeColumns`** to also virtualize horizontally — only the columns in
512
+ the scroll window (+ overscan) render, so a 60-column grid costs about the same as
513
+ a handful. It switches the grid to fixed-width horizontal scroll; **pinned columns
514
+ always render**. See the **Wide** example.
515
+
516
+ ```svelte
517
+ <Grid {rows} {columns} virtualizeColumns height={520} />
518
+ ```
519
+
321
520
  ## Column reorder
322
521
 
323
522
  Pass `onRowReorder(from, to)` to enable **drag-to-reorder rows** via a handle in
@@ -392,6 +591,23 @@ In tree mode the grid renders the tree directly (filter/sort/group/paginate are
392
591
  not applied to it). Nodes are keyboard-accessible: **→** expands a collapsed node,
393
592
  **←** collapses an expanded one, and rows expose `aria-level` / `aria-expanded`.
394
593
 
594
+ ### Lazy (server-backed) trees
595
+
596
+ For hierarchies too large to ship upfront, use **`loadChildren`** (async) instead
597
+ of `getChildren`. Children load on first expand — the grid shows a loading row,
598
+ then caches them. Pair it with **`hasChildren`** (a cheap predicate) so the chevron
599
+ shows without loading. See the **Lazy tree** example.
600
+
601
+ ```svelte
602
+ <Grid
603
+ {rows}
604
+ {columns}
605
+ height={520}
606
+ hasChildren={(r) => r.kind === 'folder'}
607
+ loadChildren={(r) => fetch(`/api/children/${r.id}`).then((res) => res.json())}
608
+ />
609
+ ```
610
+
395
611
  ## Master-detail
396
612
 
397
613
  Pass a `detail` snippet to render an expandable panel under each row — the grid
@@ -559,11 +775,14 @@ the real dimensions and positions so assistive tech isn't misled:
559
775
  - `aria-activedescendant` tracks the focused cell for screen readers.
560
776
  - Sparkline cells carry a text `aria-label`; sticky/skeleton duplicates are
561
777
  `aria-hidden`; the aggregation bar is an `aria-live` status region.
562
- - Fully keyboard-operable (see [Selection & keyboard](#selection--keyboard) and
563
- inline editing) and respects `prefers-reduced-motion`.
564
-
565
- A formal WCAG 2.1 AA audit is on the roadmap; the above is a deliberate pass, not
566
- a certification.
778
+ - Fully keyboard-operable: one tab stop with arrow-key navigation, APG-pattern
779
+ menus (focus moves in, arrow-navigable, returns focus on close), a keyboard path
780
+ to filtering (column menu → **Filter…**), and visible `:focus-visible` rings on
781
+ every reachable control. Respects `prefers-reduced-motion`.
782
+
783
+ bo-grid targets **WCAG 2.1 AA**. See **[ACCESSIBILITY.md](./ACCESSIBILITY.md)** for
784
+ the full keyboard map, roles, measured contrast ratios, and conformance notes from
785
+ the audit.
567
786
 
568
787
  ## Develop
569
788
 
@@ -0,0 +1,154 @@
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-DPnHUXMa.js";
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
+ const Pe = {
4
+ hash: "svelte-f7i4av",
5
+ code: `.bo-filtermenu.svelte-f7i4av {position:fixed;z-index:30;display:flex;flex-direction:column;gap:6px;width:200px;padding:10px;background:var(--bo-header-bg);border:0.5px solid var(--bo-border);border-radius:8px;box-shadow:0 10px 30px rgba(0, 0, 0, 0.35);font-size:12px;color:var(--bo-text);}.bo-fm-head.svelte-f7i4av {font-weight:600;color:var(--bo-text-dim);padding-bottom:2px;}.bo-fm-op.svelte-f7i4av,
6
+ .bo-fm-in.svelte-f7i4av {width:100%;padding:5px 7px;font:inherit;color:var(--bo-text);background:var(--bo-bg);border:0.5px solid var(--bo-border);border-radius:5px;}.bo-fm-setbar.svelte-f7i4av {display:flex;gap:12px;}.bo-fm-link.svelte-f7i4av {padding:0;font:inherit;font-size:11px;color:var(--bo-up);background:none;border:0;cursor:pointer;}.bo-fm-link.svelte-f7i4av:hover {text-decoration:underline;}.bo-fm-list.svelte-f7i4av {display:flex;flex-direction:column;max-height:180px;overflow-y:auto;border:0.5px solid var(--bo-border);border-radius:5px;}.bo-fm-opt.svelte-f7i4av {display:flex;align-items:center;gap:7px;padding:4px 7px;cursor:pointer;white-space:nowrap;}.bo-fm-opt.svelte-f7i4av:hover {background:var(--bo-row-hover);}.bo-fm-opt.svelte-f7i4av span:where(.svelte-f7i4av) {overflow:hidden;text-overflow:ellipsis;}.bo-fm-actions.svelte-f7i4av {display:flex;justify-content:flex-end;gap:6px;margin-top:2px;}.bo-fm-btn.svelte-f7i4av {padding:5px 12px;font:inherit;font-size:11px;color:var(--bo-text-dim);background:transparent;border:0.5px solid var(--bo-border);border-radius:5px;cursor:pointer;}.bo-fm-btn.svelte-f7i4av:hover {color:var(--bo-text);}
7
+ /* Visible keyboard focus (WCAG 2.4.7) for the menu's custom buttons. */.bo-fm-btn.svelte-f7i4av:focus-visible,
8
+ .bo-fm-link.svelte-f7i4av:focus-visible {outline:2px solid var(--bo-sel-border);outline-offset:1px;border-radius:5px;}.bo-fm-apply.svelte-f7i4av {color:#0a0a0a;background:var(--bo-up);border-color:var(--bo-up);}`
9
+ };
10
+ function Ve(K, n) {
11
+ ce(n, !0), xe(K, Pe);
12
+ let Q = me(n, "values", 19, () => []);
13
+ const $ = [
14
+ { op: "contains", label: "Contains" },
15
+ { op: "notContains", label: "Not contains" },
16
+ { op: "equals", label: "Equals" },
17
+ { op: "starts", label: "Starts with" },
18
+ { op: "ends", label: "Ends with" }
19
+ ], ee = [
20
+ { op: "eq", label: "=" },
21
+ { op: "ne", label: "≠" },
22
+ { op: "lt", label: "<" },
23
+ { op: "le", label: "≤" },
24
+ { op: "gt", label: ">" },
25
+ { op: "ge", label: "≥" },
26
+ { op: "between", label: "Between" }
27
+ ], ae = [
28
+ { op: "on", label: "On" },
29
+ { op: "before", label: "Before" },
30
+ { op: "after", label: "After" },
31
+ { op: "between", label: "Between" }
32
+ ], R = (a) => a ? Date.parse(`${a}T00:00:00Z`) : NaN, W = (a) => Number.isFinite(a) ? new Date(a).toISOString().slice(0, 10) : "", e = _e(() => n.filter);
33
+ let z = x(_((e == null ? void 0 : e.kind) === "text" ? e.op : "contains")), P = x(_((e == null ? void 0 : e.kind) === "text" ? e.q : "")), E = x(_((e == null ? void 0 : e.kind) === "number" ? e.op : "eq")), T = x(_((e == null ? void 0 : e.kind) === "number" && Number.isFinite(e.a) ? e.a : null)), V = x(_((e == null ? void 0 : e.kind) === "number" && e.b != null && Number.isFinite(e.b) ? e.b : null)), q = x(_((e == null ? void 0 : e.kind) === "date" ? e.op : "on")), M = x(_((e == null ? void 0 : e.kind) === "date" ? W(e.a) : "")), B = x(_((e == null ? void 0 : e.kind) === "date" && e.b != null ? W(e.b) : "")), O = x(_(new Set((e == null ? void 0 : e.kind) === "set" ? e.excluded : []))), j = x("");
34
+ const te = Oe(() => Q().filter((a) => a.toLowerCase().includes(t(j).trim().toLowerCase())));
35
+ function le(a) {
36
+ const s = new Set(t(O));
37
+ s.has(a) ? s.delete(a) : s.add(a), f(O, s, !0);
38
+ }
39
+ function oe() {
40
+ let a;
41
+ return n.kind === "number" ? a = {
42
+ kind: "number",
43
+ op: t(E),
44
+ a: t(T) ?? NaN,
45
+ b: t(V) ?? void 0
46
+ } : n.kind === "date" ? a = {
47
+ kind: "date",
48
+ op: t(q),
49
+ a: R(t(M)),
50
+ b: t(B) ? R(t(B)) : void 0
51
+ } : n.kind === "set" ? a = { kind: "set", excluded: [...t(O)] } : a = { kind: "text", op: t(z), q: t(P) }, Ae(a) ? a : null;
52
+ }
53
+ function X() {
54
+ n.onApply(oe());
55
+ }
56
+ function re() {
57
+ n.onApply(null);
58
+ }
59
+ function ie(a) {
60
+ a.key === "Enter" ? X() : a.key === "Escape" && n.onClose();
61
+ }
62
+ var g = ze(), I = h(g), ne = h(I, !0);
63
+ d(I);
64
+ var Z = p(I, 2);
65
+ {
66
+ var se = (a) => {
67
+ var s = Ce(), i = D(s);
68
+ F(i, 21, () => ee, (l) => l.op, (l, o) => {
69
+ var r = G(), y = h(r, !0);
70
+ d(r);
71
+ var c = {};
72
+ N(() => {
73
+ C(y, t(o).label), c !== (c = t(o).op) && (r.value = (r.__value = t(o).op) ?? "");
74
+ }), u(l, r);
75
+ }), d(i);
76
+ var v = p(i, 2);
77
+ A(v);
78
+ var b = p(v, 2);
79
+ {
80
+ var m = (l) => {
81
+ var o = Ne();
82
+ A(o), S(o, () => t(V), (r) => f(V, r)), u(l, o);
83
+ };
84
+ L(b, (l) => {
85
+ t(E) === "between" && l(m);
86
+ });
87
+ }
88
+ U(i, () => t(E), (l) => f(E, l)), S(v, () => t(T), (l) => f(T, l)), u(a, s);
89
+ }, ve = (a) => {
90
+ var s = qe(), i = D(s);
91
+ F(i, 21, () => ae, (l) => l.op, (l, o) => {
92
+ var r = G(), y = h(r, !0);
93
+ d(r);
94
+ var c = {};
95
+ N(() => {
96
+ C(y, t(o).label), c !== (c = t(o).op) && (r.value = (r.__value = t(o).op) ?? "");
97
+ }), u(l, r);
98
+ }), d(i);
99
+ var v = p(i, 2);
100
+ A(v);
101
+ var b = p(v, 2);
102
+ {
103
+ var m = (l) => {
104
+ var o = Ee();
105
+ A(o), S(o, () => t(B), (r) => f(B, r)), u(l, o);
106
+ };
107
+ L(b, (l) => {
108
+ t(q) === "between" && l(m);
109
+ });
110
+ }
111
+ U(i, () => t(q), (l) => f(q, l)), S(v, () => t(M), (l) => f(M, l)), u(a, s);
112
+ }, be = (a) => {
113
+ var s = De(), i = D(s);
114
+ A(i);
115
+ var v = p(i, 2), b = h(v), m = p(b, 2);
116
+ d(v);
117
+ var l = p(v, 2);
118
+ F(l, 20, () => t(te), (o) => o, (o, r) => {
119
+ var y = Be(), c = h(y);
120
+ A(c);
121
+ var Y = p(c, 2), fe = h(Y, !0);
122
+ d(Y), d(y), N(
123
+ (ue) => {
124
+ ye(c, ue), C(fe, r === "" ? "(blank)" : r);
125
+ },
126
+ [() => !t(O).has(r)]
127
+ ), w("change", c, () => le(r)), u(o, y);
128
+ }), d(l), S(i, () => t(j), (o) => f(j, o)), w("click", b, () => f(O, /* @__PURE__ */ new Set(), !0)), w("click", m, () => f(O, new Set(Q()), !0)), u(a, s);
129
+ }, de = (a) => {
130
+ var s = Fe(), i = D(s);
131
+ F(i, 21, () => $, (b) => b.op, (b, m) => {
132
+ var l = G(), o = h(l, !0);
133
+ d(l);
134
+ var r = {};
135
+ N(() => {
136
+ C(o, t(m).label), r !== (r = t(m).op) && (l.value = (l.__value = t(m).op) ?? "");
137
+ }), u(b, l);
138
+ }), d(i);
139
+ var v = p(i, 2);
140
+ A(v), we(v), U(i, () => t(z), (b) => f(z, b)), S(v, () => t(P), (b) => f(P, b)), u(a, s);
141
+ };
142
+ L(Z, (a) => {
143
+ n.kind === "number" ? a(se) : n.kind === "date" ? a(ve, 1) : n.kind === "set" ? a(be, 2) : a(de, -1);
144
+ });
145
+ }
146
+ var H = p(Z, 2), J = h(H), pe = p(J, 2);
147
+ d(H), d(g), N(() => {
148
+ he(g, "aria-label", `Filter ${n.header ?? ""}`), ke(g, `left:${n.x ?? ""}px;top:${n.y ?? ""}px;`), C(ne, n.header);
149
+ }), w("pointerdown", g, (a) => a.stopPropagation()), w("keydown", g, ie), w("click", J, re), w("click", pe, X), u(K, g), ge();
150
+ }
151
+ Se(["pointerdown", "keydown", "click", "change"]);
152
+ export {
153
+ Ve as default
154
+ };
@@ -0,0 +1,34 @@
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-DPnHUXMa.js";
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
+ const I = {
4
+ hash: "svelte-7xdno",
5
+ code: ".bo-toolpanel.svelte-7xdno {position:fixed;z-index:30;display:flex;flex-direction:column;gap:7px;width:200px;padding:10px;background:var(--bo-header-bg);border:0.5px solid var(--bo-border);border-radius:8px;box-shadow:0 10px 30px rgba(0, 0, 0, 0.35);font-size:12px;color:var(--bo-text);}.bo-tp-head.svelte-7xdno {display:flex;align-items:baseline;justify-content:space-between;font-weight:600;color:var(--bo-text-dim);}.bo-tp-link.svelte-7xdno {padding:0;font:inherit;font-size:11px;color:var(--bo-up);background:none;border:0;cursor:pointer;}.bo-tp-link.svelte-7xdno:hover {text-decoration:underline;}.bo-tp-link.svelte-7xdno:focus-visible {outline:2px solid var(--bo-sel-border);outline-offset:1px;border-radius:4px;}.bo-tp-search.svelte-7xdno {width:100%;padding:5px 7px;font:inherit;color:var(--bo-text);background:var(--bo-bg);border:0.5px solid var(--bo-border);border-radius:5px;}.bo-tp-list.svelte-7xdno {display:flex;flex-direction:column;max-height:220px;overflow-y:auto;}.bo-tp-opt.svelte-7xdno {display:flex;align-items:center;gap:7px;padding:4px 4px;cursor:pointer;white-space:nowrap;}.bo-tp-opt.svelte-7xdno:hover {background:var(--bo-row-hover);}.bo-tp-opt.svelte-7xdno span:where(.svelte-7xdno) {overflow:hidden;text-overflow:ellipsis;}"
6
+ };
7
+ function K(c, o) {
8
+ z(o, !0), S(c, I);
9
+ let p = B("");
10
+ const y = D(() => o.columns.filter((e) => e.header.toLowerCase().includes(s(p).trim().toLowerCase())));
11
+ var t = H(), x = d(t), m = r(d(x), 2);
12
+ l(x);
13
+ var b = r(x, 2);
14
+ f(b);
15
+ var u = r(b, 2);
16
+ j(u, 21, () => s(y), (e) => e.key, (e, a) => {
17
+ var v = G(), n = d(v);
18
+ f(n);
19
+ var h = r(n, 2), _ = d(h, !0);
20
+ l(h), l(v), g(
21
+ (C) => {
22
+ L(n, C), P(_, s(a).header);
23
+ },
24
+ [() => !o.hidden.includes(s(a).key)]
25
+ ), i("change", n, () => o.onToggle(s(a).key)), w(e, v);
26
+ }), l(u), l(t), g(() => T(t, `left:${o.x ?? ""}px;top:${o.y ?? ""}px;`)), i("pointerdown", t, (e) => e.stopPropagation()), i("keydown", t, (e) => e.key === "Escape" && o.onClose()), i("click", m, function(...e) {
27
+ var a;
28
+ (a = o.onShowAll) == null || a.apply(this, e);
29
+ }), q(b, () => s(p), (e) => A(p, e)), w(c, t), E();
30
+ }
31
+ F(["pointerdown", "keydown", "click", "change"]);
32
+ export {
33
+ K as default
34
+ };