csv-grid 3.3.0__tar.gz → 3.7.1__tar.gz
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.
- {csv_grid-3.3.0 → csv_grid-3.7.1}/.gitignore +2 -0
- {csv_grid-3.3.0 → csv_grid-3.7.1}/PKG-INFO +24 -10
- {csv_grid-3.3.0 → csv_grid-3.7.1}/README.md +23 -9
- {csv_grid-3.3.0 → csv_grid-3.7.1}/pyproject.toml +1 -1
- {csv_grid-3.3.0 → csv_grid-3.7.1}/src/csv_grid/__init__.py +98 -44
- {csv_grid-3.3.0 → csv_grid-3.7.1}/src/csv_grid/assets/csv-grid.css +23 -0
- csv_grid-3.7.1/src/csv_grid/assets/csv-grid.iife.js +6 -0
- {csv_grid-3.3.0 → csv_grid-3.7.1}/uv.lock +1 -1
- csv_grid-3.3.0/src/csv_grid/assets/csv-grid.iife.js +0 -6
- {csv_grid-3.3.0 → csv_grid-3.7.1}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csv-grid
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.7.1
|
|
4
4
|
Summary: Emit csv-viewer's CsvGrid interactive tables from pandas DataFrames (Jupyter / Quarto / static HTML).
|
|
5
5
|
Project-URL: Homepage, https://github.com/mynl/CSV_Viewer
|
|
6
6
|
Project-URL: Live viewer, https://mynl.github.io/CSV_Viewer/
|
|
@@ -62,26 +62,40 @@ show(df, align="llrcr", fmt=[None, None, ",d", "year", ",.2f"])
|
|
|
62
62
|
html = to_html(df, name="results.df", assets="inline") # fragment string
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
- `show(df, **options)` displays via IPython.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
- `show(df, **options)` displays via IPython. Each grid carries the JS +
|
|
66
|
+
CSS via an idempotent `<head>` guard (`assets="inline"`, the default),
|
|
67
|
+
so fragments are self-contained and re-running/clearing a notebook cell
|
|
68
|
+
can't strip a shared stylesheet. Use `assets="https://…/base"` to link
|
|
69
|
+
the assets from a URL instead, or `assets=False` if they are already on
|
|
70
|
+
the page.
|
|
71
|
+
- `to_html(df, **options)` returns a self-contained HTML fragment;
|
|
72
|
+
fragments compose freely (no need to mark a "first" one).
|
|
72
73
|
- `payload(df)` returns the `{records, columns}` dict the grid consumes,
|
|
73
74
|
if you want to ship data yourself.
|
|
74
75
|
- Options mirror the JS API in snake_case: `global_search`,
|
|
75
76
|
`column_filters`, `sortable`, `status_bar`, `expand_buttons`, `align`
|
|
76
77
|
(`'llrcr…'`), `formats`/`fmt` (per-column `[,][.N](f|d|%|e|s)`,
|
|
77
78
|
`'year'`, `'eng'`, None = auto), `width_mode` (`'equal-risk'` default,
|
|
78
|
-
or `'coverage'` to maximize the count of fully-shown cells),
|
|
79
|
+
or `'coverage'` to maximize the count of fully-shown cells),
|
|
80
|
+
`display_mode` (`'auto'` formatted / `'raw'` verbatim), `rows`
|
|
79
81
|
(cap the viewport to ~N rows, vertical scroll for the rest) /
|
|
80
82
|
`max_height` (raw CSS, e.g. `'400px'`), `render_cap`, `eager_cells`,
|
|
81
83
|
`worker` (default False — data is inlined), plus `name` (status line)
|
|
82
84
|
and `index` (include the DataFrame index as leading columns). Dark mode
|
|
83
85
|
follows the host page (`prefers-color-scheme`; JupyterLab dark themes
|
|
84
|
-
included).
|
|
86
|
+
included) unless `theme="light"`/`"dark"` forces it.
|
|
87
|
+
- **Clickable rows/cells** (`selectable=True`): a body click fires a
|
|
88
|
+
bubbling `csvgrid:cellclick` DOM event whose `detail` carries the clicked
|
|
89
|
+
cell and the whole row keyed by column name (raw + formatted) with the
|
|
90
|
+
original row index — wire it to HTMX/JS for drill-down. `select_mode`
|
|
91
|
+
(`'row'`/`'cell'`/`'none'`) controls the highlight; `hidden_columns=[…]`
|
|
92
|
+
ships a key column in the payload without displaying it. No Python
|
|
93
|
+
callback — `to_html` stays a pure string emitter.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
to_html(df, name="transactions", selectable=True,
|
|
97
|
+
select_mode="row", hidden_columns=["trans_id"])
|
|
98
|
+
```
|
|
85
99
|
|
|
86
100
|
Dates are emitted ISO (`yyyy-mm-dd`, with `hh:mm` only when a column has
|
|
87
101
|
non-midnight times); integral float columns are emitted as integers so
|
|
@@ -36,26 +36,40 @@ show(df, align="llrcr", fmt=[None, None, ",d", "year", ",.2f"])
|
|
|
36
36
|
html = to_html(df, name="results.df", assets="inline") # fragment string
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
- `show(df, **options)` displays via IPython.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
- `show(df, **options)` displays via IPython. Each grid carries the JS +
|
|
40
|
+
CSS via an idempotent `<head>` guard (`assets="inline"`, the default),
|
|
41
|
+
so fragments are self-contained and re-running/clearing a notebook cell
|
|
42
|
+
can't strip a shared stylesheet. Use `assets="https://…/base"` to link
|
|
43
|
+
the assets from a URL instead, or `assets=False` if they are already on
|
|
44
|
+
the page.
|
|
45
|
+
- `to_html(df, **options)` returns a self-contained HTML fragment;
|
|
46
|
+
fragments compose freely (no need to mark a "first" one).
|
|
46
47
|
- `payload(df)` returns the `{records, columns}` dict the grid consumes,
|
|
47
48
|
if you want to ship data yourself.
|
|
48
49
|
- Options mirror the JS API in snake_case: `global_search`,
|
|
49
50
|
`column_filters`, `sortable`, `status_bar`, `expand_buttons`, `align`
|
|
50
51
|
(`'llrcr…'`), `formats`/`fmt` (per-column `[,][.N](f|d|%|e|s)`,
|
|
51
52
|
`'year'`, `'eng'`, None = auto), `width_mode` (`'equal-risk'` default,
|
|
52
|
-
or `'coverage'` to maximize the count of fully-shown cells),
|
|
53
|
+
or `'coverage'` to maximize the count of fully-shown cells),
|
|
54
|
+
`display_mode` (`'auto'` formatted / `'raw'` verbatim), `rows`
|
|
53
55
|
(cap the viewport to ~N rows, vertical scroll for the rest) /
|
|
54
56
|
`max_height` (raw CSS, e.g. `'400px'`), `render_cap`, `eager_cells`,
|
|
55
57
|
`worker` (default False — data is inlined), plus `name` (status line)
|
|
56
58
|
and `index` (include the DataFrame index as leading columns). Dark mode
|
|
57
59
|
follows the host page (`prefers-color-scheme`; JupyterLab dark themes
|
|
58
|
-
included).
|
|
60
|
+
included) unless `theme="light"`/`"dark"` forces it.
|
|
61
|
+
- **Clickable rows/cells** (`selectable=True`): a body click fires a
|
|
62
|
+
bubbling `csvgrid:cellclick` DOM event whose `detail` carries the clicked
|
|
63
|
+
cell and the whole row keyed by column name (raw + formatted) with the
|
|
64
|
+
original row index — wire it to HTMX/JS for drill-down. `select_mode`
|
|
65
|
+
(`'row'`/`'cell'`/`'none'`) controls the highlight; `hidden_columns=[…]`
|
|
66
|
+
ships a key column in the payload without displaying it. No Python
|
|
67
|
+
callback — `to_html` stays a pure string emitter.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
to_html(df, name="transactions", selectable=True,
|
|
71
|
+
select_mode="row", hidden_columns=["trans_id"])
|
|
72
|
+
```
|
|
59
73
|
|
|
60
74
|
Dates are emitted ISO (`yyyy-mm-dd`, with `hh:mm` only when a column has
|
|
61
75
|
non-midnight times); integral float columns are emitted as integers so
|
|
@@ -18,7 +18,7 @@ import json
|
|
|
18
18
|
import uuid
|
|
19
19
|
from importlib import resources
|
|
20
20
|
|
|
21
|
-
__version__ = "3.
|
|
21
|
+
__version__ = "3.7.1"
|
|
22
22
|
__all__ = ["show", "to_html", "payload"]
|
|
23
23
|
|
|
24
24
|
# python snake_case -> CsvGrid option names (see src/grid/grid.js)
|
|
@@ -31,11 +31,15 @@ _OPTION_MAP = {
|
|
|
31
31
|
"align": "align",
|
|
32
32
|
"formats": "formats",
|
|
33
33
|
"width_mode": "widthMode",
|
|
34
|
+
"display_mode": "displayMode",
|
|
34
35
|
"rows": "maxRows",
|
|
35
36
|
"max_height": "height",
|
|
36
37
|
"render_cap": "renderCap",
|
|
37
38
|
"eager_cells": "eagerCells",
|
|
38
39
|
"worker": "worker",
|
|
40
|
+
"selectable": "selectable",
|
|
41
|
+
"select_mode": "selectMode",
|
|
42
|
+
"hidden_columns": "hiddenColumns",
|
|
39
43
|
}
|
|
40
44
|
_CAMEL = set(_OPTION_MAP.values())
|
|
41
45
|
|
|
@@ -112,20 +116,49 @@ def _asset_text(fname: str) -> str:
|
|
|
112
116
|
return resources.files("csv_grid").joinpath("assets", fname).read_text(encoding="utf-8")
|
|
113
117
|
|
|
114
118
|
|
|
115
|
-
def
|
|
116
|
-
"""
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
def _js_str(text: str) -> str:
|
|
120
|
+
"""A JS string literal (double-quoted, JSON-escaped) safe inside a
|
|
121
|
+
<script>: the `</` -> `<\\/` guard stops an embedded '</script>' in the
|
|
122
|
+
payload from closing our tag."""
|
|
123
|
+
return json.dumps(text, ensure_ascii=False).replace("</", "<\\/")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _assets_html(assets) -> str:
|
|
127
|
+
"""The CSS + JS the grid needs, emitted with EVERY grid (no per-kernel
|
|
128
|
+
state — that was the notebook bug: assets parked in one cell's output
|
|
129
|
+
vanish when that cell is cleared/re-run, and every grid shares them).
|
|
130
|
+
|
|
131
|
+
'inline' (default): a guard that idempotently injects the CSS and the
|
|
132
|
+
iife into the document <head>. <head> lives outside cell output, so
|
|
133
|
+
clearing/re-running a cell can't strip the assets, and any grid
|
|
134
|
+
re-establishes them if missing. The injected <script> sets its code via
|
|
135
|
+
textContent, so it runs synchronously on insertion — window.CsvGrid is
|
|
136
|
+
defined before the grid-construction script that follows.
|
|
137
|
+
|
|
138
|
+
A base-URL string: plain <link>/<script src> tags (parser-ordered, so
|
|
139
|
+
they run before construction; the browser caches them, so re-emitting
|
|
140
|
+
per grid is cheap). False/None: nothing (assets already on the page).
|
|
141
|
+
|
|
142
|
+
The iife build is used (not umd): Quarto/Jupyter pages can carry
|
|
143
|
+
RequireJS, whose define.amd hijacks a umd wrapper so window.CsvGrid
|
|
144
|
+
never appears."""
|
|
145
|
+
if not assets:
|
|
146
|
+
return ""
|
|
121
147
|
if assets == "inline":
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
css = _js_str(_asset_text("csv-grid.css"))
|
|
149
|
+
js = _js_str(_asset_text("csv-grid.iife.js"))
|
|
150
|
+
body = (
|
|
151
|
+
"if(!document.getElementById('csvgrid-css')){"
|
|
152
|
+
"var s=document.createElement('style');s.id='csvgrid-css';"
|
|
153
|
+
f"s.textContent={css};document.head.appendChild(s);}}"
|
|
154
|
+
"if(!window.CsvGrid){"
|
|
155
|
+
"var j=document.createElement('script');j.id='csvgrid-js';"
|
|
156
|
+
f"j.textContent={js};document.head.appendChild(j);}}"
|
|
157
|
+
)
|
|
158
|
+
return f"<script>(function(){{{body}}})();</script>"
|
|
159
|
+
base = str(assets).rstrip("/")
|
|
160
|
+
return (f'<link rel="stylesheet" href="{base}/csv-grid.css">\n'
|
|
161
|
+
f'<script src="{base}/csv-grid.iife.js"></script>')
|
|
129
162
|
|
|
130
163
|
|
|
131
164
|
def _dump(obj) -> str:
|
|
@@ -134,40 +167,37 @@ def _dump(obj) -> str:
|
|
|
134
167
|
|
|
135
168
|
|
|
136
169
|
def to_html(df, *, name: str | None = None, assets="inline",
|
|
137
|
-
index: bool = False, **options) -> str:
|
|
138
|
-
"""HTML fragment rendering `df` as a CsvGrid
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
index: bool = False, theme: str = "auto", **options) -> str:
|
|
171
|
+
"""HTML fragment rendering `df` as a CsvGrid, self-contained by default.
|
|
172
|
+
|
|
173
|
+
Every fragment carries the assets via an idempotent guard (see
|
|
174
|
+
``assets``), so fragments compose freely — no need to mark a "first"
|
|
175
|
+
one. Options are the grid's, in snake_case (see ``show``); `fmt` is an
|
|
142
176
|
alias for `formats`; `worker` defaults to False (data is inlined).
|
|
177
|
+
`assets='inline'` embeds the CSS + JS (injected once into <head>); a
|
|
178
|
+
base-URL string links them; False omits them. `theme` forces the color
|
|
179
|
+
scheme via ``data-theme`` ('auto' follows prefers-color-scheme).
|
|
143
180
|
"""
|
|
144
181
|
opts = _map_options(options)
|
|
145
182
|
opts.setdefault("worker", False)
|
|
146
183
|
div = f"csvgrid-{uuid.uuid4().hex[:12]}"
|
|
184
|
+
theme_attr = f' data-theme="{theme}"' if theme in ("light", "dark") else ""
|
|
147
185
|
parts = []
|
|
148
|
-
head =
|
|
186
|
+
head = _assets_html(assets)
|
|
149
187
|
if head:
|
|
150
188
|
parts.append(head)
|
|
151
189
|
parts.append(
|
|
152
|
-
f'<div id="{div}"></div>\n'
|
|
190
|
+
f'<div id="{div}"{theme_attr}></div>\n'
|
|
153
191
|
f'<script>new CsvGrid(document.getElementById("{div}"), '
|
|
154
192
|
f'{_dump(payload(df, name, index))}, {_dump(opts)});</script>'
|
|
155
193
|
)
|
|
156
194
|
return "\n".join(parts)
|
|
157
195
|
|
|
158
196
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def show(df, *, name: str | None = None, assets=None,
|
|
163
|
-
index: bool = False, **options) -> None:
|
|
197
|
+
def show(df, *, name: str | None = None, assets="inline",
|
|
198
|
+
index: bool = False, theme: str = "auto", **options) -> None:
|
|
164
199
|
"""Display `df` as a CsvGrid in Jupyter / Quarto.
|
|
165
200
|
|
|
166
|
-
Assets (the grid's JS + CSS) are emitted once per kernel session (=
|
|
167
|
-
once per rendered page); ``assets='inline'`` forces re-emission (e.g.
|
|
168
|
-
after reloading the browser page without restarting the kernel), and a
|
|
169
|
-
base-URL string loads them from there instead of inlining.
|
|
170
|
-
|
|
171
201
|
Parameters
|
|
172
202
|
----------
|
|
173
203
|
df : pandas.DataFrame
|
|
@@ -176,12 +206,17 @@ def show(df, *, name: str | None = None, assets=None,
|
|
|
176
206
|
integral float columns -> ints) exactly as the csv-viewer app does.
|
|
177
207
|
name : str, optional
|
|
178
208
|
Label shown in the grid's status line.
|
|
179
|
-
assets : {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
209
|
+
assets : {'inline', str, False}, default 'inline'
|
|
210
|
+
'inline' embeds the grid's CSS + JS, injected into the document
|
|
211
|
+
<head> by an idempotent guard emitted with every grid — clearing or
|
|
212
|
+
re-running a cell can't strip them, and any grid re-establishes them
|
|
213
|
+
if missing. A base-URL string links the assets from there instead.
|
|
214
|
+
False emits nothing (use only when the assets are already present).
|
|
183
215
|
index : bool, default False
|
|
184
216
|
Include the DataFrame index as leading column(s).
|
|
217
|
+
theme : {'auto', 'light', 'dark'}, default 'auto'
|
|
218
|
+
'auto' follows the host page / OS via prefers-color-scheme; 'light'
|
|
219
|
+
or 'dark' forces the grid's color scheme (sets ``data-theme``).
|
|
185
220
|
|
|
186
221
|
Other options (keyword, snake_case; mirror the JS CsvGrid API)
|
|
187
222
|
--------------------------------------------------------------
|
|
@@ -203,26 +238,45 @@ def show(df, *, name: str | None = None, assets=None,
|
|
|
203
238
|
with equal probability; coverage =
|
|
204
239
|
maximize the count of cells shown
|
|
205
240
|
in full.
|
|
241
|
+
display_mode : {'auto', 'raw'}, default 'auto'
|
|
242
|
+
'auto' = type-aware formatting;
|
|
243
|
+
'raw' = verbatim source text (a view
|
|
244
|
+
lens; types still drive alignment
|
|
245
|
+
and sort).
|
|
206
246
|
rows : int, optional cap the scroll viewport to ~N rows
|
|
207
247
|
(vertical scroll for the rest).
|
|
208
248
|
max_height : str, optional raw CSS max-height (e.g. '400px');
|
|
209
249
|
overrides ``rows`` when set.
|
|
210
|
-
render_cap : int, default
|
|
211
|
-
eager_cells : int, default
|
|
250
|
+
render_cap : int, default 2048 rows rendered before "show all".
|
|
251
|
+
eager_cells : int, default 262144 below this, format everything up
|
|
212
252
|
front (else lazy).
|
|
213
253
|
worker : bool, default False parse worker (off by default here
|
|
214
254
|
— the data is inlined, not fetched).
|
|
255
|
+
selectable : bool, default False emit a bubbling, cancelable
|
|
256
|
+
``csvgrid:cellclick`` DOM event on a
|
|
257
|
+
body click (off = no event, no cost).
|
|
258
|
+
``event.detail`` carries the clicked
|
|
259
|
+
cell and the whole row keyed by column
|
|
260
|
+
name (raw + formatted) with the
|
|
261
|
+
original row index, so an embedder
|
|
262
|
+
(HTMX, JS) can drill down. The grid
|
|
263
|
+
takes no action beyond an optional
|
|
264
|
+
highlight; no Python callback.
|
|
265
|
+
select_mode : {'row', 'cell', 'none'}, default 'row'
|
|
266
|
+
the visual highlight on click ('none'
|
|
267
|
+
still emits the event).
|
|
268
|
+
hidden_columns : list[str], optional header names carried in the event
|
|
269
|
+
payload (and export) but not shown as
|
|
270
|
+
columns — e.g. ship a key column
|
|
271
|
+
without displaying it.
|
|
215
272
|
|
|
216
|
-
Dark mode follows the host page automatically
|
|
217
|
-
JupyterLab dark themes included).
|
|
273
|
+
Dark mode follows the host page automatically unless ``theme`` forces
|
|
274
|
+
it (prefers-color-scheme; JupyterLab dark themes included).
|
|
218
275
|
"""
|
|
219
|
-
global _assets_emitted
|
|
220
276
|
try:
|
|
221
277
|
from IPython.display import HTML, display
|
|
222
278
|
except ImportError as e: # pragma: no cover
|
|
223
279
|
raise ImportError("csv_grid.show() needs IPython; "
|
|
224
280
|
"use to_html() outside Jupyter/Quarto") from e
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
_assets_emitted = True
|
|
228
|
-
display(HTML(to_html(df, name=name, assets=assets, index=index, **options)))
|
|
281
|
+
display(HTML(to_html(df, name=name, assets=assets, index=index,
|
|
282
|
+
theme=theme, **options)))
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
--csvgrid-grip-hover: rgba(13, 110, 253, 0.3);
|
|
22
22
|
--csvgrid-blank: #adb5bd;
|
|
23
23
|
--csvgrid-filter-active-bg: #e7f1ff;
|
|
24
|
+
--csvgrid-selected-bg: rgba(13, 110, 253, 0.12);
|
|
24
25
|
--csvgrid-error-fg: #842029;
|
|
25
26
|
--csvgrid-error-bg: #f8d7da;
|
|
26
27
|
--csvgrid-error-border: #f5c2c7;
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
--csvgrid-grip-hover: rgba(110, 168, 254, 0.4);
|
|
46
47
|
--csvgrid-blank: #6c757d;
|
|
47
48
|
--csvgrid-filter-active-bg: #2b3a55;
|
|
49
|
+
--csvgrid-selected-bg: rgba(110, 168, 254, 0.22);
|
|
48
50
|
--csvgrid-error-fg: #ea868f;
|
|
49
51
|
--csvgrid-error-bg: #2c0b0e;
|
|
50
52
|
--csvgrid-error-border: #842029;
|
|
@@ -62,6 +64,7 @@
|
|
|
62
64
|
--csvgrid-grip-hover: rgba(110, 168, 254, 0.4);
|
|
63
65
|
--csvgrid-blank: #6c757d;
|
|
64
66
|
--csvgrid-filter-active-bg: #2b3a55;
|
|
67
|
+
--csvgrid-selected-bg: rgba(110, 168, 254, 0.22);
|
|
65
68
|
--csvgrid-error-fg: #ea868f;
|
|
66
69
|
--csvgrid-error-bg: #2c0b0e;
|
|
67
70
|
--csvgrid-error-border: #842029;
|
|
@@ -135,6 +138,15 @@
|
|
|
135
138
|
}
|
|
136
139
|
.csvgrid .csvgrid-table tbody tr:hover td { background: var(--csvgrid-row-hover); }
|
|
137
140
|
|
|
141
|
+
/* Windowed-render spacer rows: reserve off-screen height only — no padding,
|
|
142
|
+
* border, hover or selection chrome (they're not real rows). */
|
|
143
|
+
.csvgrid .csvgrid-table tbody tr.csvgrid-spacer td,
|
|
144
|
+
.csvgrid .csvgrid-table tbody tr.csvgrid-spacer:hover td {
|
|
145
|
+
padding: 0;
|
|
146
|
+
border: 0;
|
|
147
|
+
background: none;
|
|
148
|
+
}
|
|
149
|
+
|
|
138
150
|
.csvgrid .csvgrid-table thead th {
|
|
139
151
|
position: sticky;
|
|
140
152
|
top: 0;
|
|
@@ -208,6 +220,17 @@ body.csvgrid-resizing { cursor: col-resize; user-select: none; }
|
|
|
208
220
|
.csvgrid .csvgrid-table .align-right { text-align: right; }
|
|
209
221
|
.csvgrid .csvgrid-table td.blank { color: var(--csvgrid-blank); }
|
|
210
222
|
|
|
223
|
+
/* Clickable rows/cells (opt-in selectable option). The cursor affordance
|
|
224
|
+
* shows only when on; selection rules sit after the hover rule so a selected
|
|
225
|
+
* row stays visibly selected even while hovered. Reads in light and dark. */
|
|
226
|
+
.csvgrid[data-selectable] .csvgrid-table tbody tr { cursor: pointer; }
|
|
227
|
+
.csvgrid .csvgrid-table tbody tr.csvgrid-selected td,
|
|
228
|
+
.csvgrid .csvgrid-table tbody tr.csvgrid-selected-row td { background: var(--csvgrid-selected-bg); }
|
|
229
|
+
.csvgrid .csvgrid-table tbody td.csvgrid-selected {
|
|
230
|
+
outline: 2px solid var(--csvgrid-accent);
|
|
231
|
+
outline-offset: -2px;
|
|
232
|
+
}
|
|
233
|
+
|
|
211
234
|
/* "Showing first N — show all" note below the table */
|
|
212
235
|
.csvgrid-capnote {
|
|
213
236
|
text-align: center;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
var CsvGrid=(function(){var e=typeof document<`u`&&document.currentScript&&document.currentScript.src||(typeof document<`u`?document.baseURI:``);function t(e){return(e??``).replace(/^\uFEFF/,``).replace(/^(?:[ \t]*(?:\r\n|\n|\r))+/,``)}function n(e){let t=[`,`,` `,`;`,`|`],n=e.split(/\r\n|\n|\r/,20).filter(e=>e.length),i=`,`,a=0;for(let e of t){let t=n.map(t=>r(t,e).length),o=t[0];if(o<2)continue;let s=o*(t.every(e=>e===o)?10:1);s>a&&(a=s,i=e)}return i}function r(e,t){let n=[],r=``,i=!1;for(let a=0;a<e.length;a++){let o=e[a];i?o===`"`?i=!1:r+=o:o===`"`?i=!0:o===t?(n.push(r),r=``):r+=o}return n.push(r),n}function i(e,t){let n=[],r=[],i=``,a=!1,o=0,s=e.length;for(;o<s;){let s=e[o];if(a){if(s===`"`){if(e[o+1]===`"`){i+=`"`,o+=2;continue}a=!1,o++;continue}i+=s,o++;continue}if(s===`"`){a=!0,o++;continue}if(s===t){r.push(i),i=``,o++;continue}if(s===`\r`||s===`
|
|
2
|
+
`){r.push(i),i=``,n.push(r),r=[],s===`\r`&&e[o+1]===`
|
|
3
|
+
`&&o++,o++;continue}i+=s,o++}for((i.length||r.length)&&(r.push(i),n.push(r));n.length&&n[n.length-1].every(e=>e.trim()===``);)n.pop();return n}function a(e){e=e.trim(),e.startsWith(`|`)&&(e=e.slice(1)),e.endsWith(`|`)&&!e.endsWith(`\\|`)&&(e=e.slice(0,-1));let t=[],n=``;for(let r=0;r<e.length;r++){let i=e[r];i===`\\`&&e[r+1]===`|`?(n+=`|`,r++):i===`|`?(t.push(n),n=``):n+=i}return t.push(n),t.map(e=>e.trim())}var o=/^:?-+:?$/;function s(e){let t=e.split(/\r\n|\n|\r/).filter(e=>e.trim()!==``);if(t.length<2||!t[0].includes(`|`))return!1;let n=a(t[1]);return n.length>0&&n.every(e=>o.test(e))}function c(e){let t=e.split(/\r\n|\n|\r/).filter(e=>e.trim()!==``),n=a(t[0]).map((e,t)=>e||`col${t+1}`),r=a(t[1]).map(e=>{let t=e.startsWith(`:`),n=e.endsWith(`:`);return t&&n?`center`:n?`right`:t?`left`:null});for(;r.length<n.length;)r.push(null);return{headers:n,rows:t.slice(2).filter(e=>e.includes(`|`)).map(e=>{let t=a(e).slice(0,n.length);for(;t.length<n.length;)t.push(``);return t}),aligns:r}}var l=/[$£€¥¥]/,u=/^\(?(?:[+-]?[$£€¥¥]?|[$£€¥¥][+-]?)(?:[0-9][0-9,]*(?:\.[0-9]+)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?%?\)?$/,ee=/^\(?[+-]?(?:inf(?:inity)?|∞)\)?$/i,d=/^(\d{4})-(\d{1,2})-(\d{1,2})(?:[T ](\d{1,2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?Z?)?$/,f=/^(\d{1,4})([\/\-.])(\d{1,2})\2(\d{1,4})$/,p=/^(\d{1,2})[ \-]([A-Za-z]{3,9})\.?,?[ \-](\d{2,4})$/,te=/^([A-Za-z]{3,9})\.?,?[ \-](\d{1,2}),?[ \-](\d{2,4})$/,ne=[`january`,`february`,`march`,`april`,`may`,`june`,`july`,`august`,`september`,`october`,`november`,`december`],re=new Set([`nan`,`na`,`n/a`,`#n/a`,`null`,`none`,`-`,`--`,`.`]);function m(e){return re.has((e??``).trim().toLowerCase())}function h(e){if(e=e.trim(),ee.test(e)){let t=e;return t.startsWith(`(`)&&t.endsWith(`)`)&&(t=`-`+t.slice(1,-1)),{v:t.startsWith(`-`)?-1/0:1/0,dec:0}}if(!u.test(e))return null;let t=!1;e.startsWith(`(`)&&e.endsWith(`)`)&&(t=!0,e=e.slice(1,-1));let n=``,r=l.exec(e);r&&(n=r[0],e=e.replace(l,``));let i=!1;e.endsWith(`%`)&&(i=!0,e=e.slice(0,-1)),e=e.replace(/,/g,``);let a=parseFloat(e);if(!isFinite(a))return null;t&&(a=-a),i&&(a/=100);let o=/^([^eE]*)[eE]([+-]?\d+)$/.exec(e),s=o?o[1]:e,c=o?+o[2]:0,d=s.indexOf(`.`),f=Math.max(0,(d<0?0:s.length-d-1)-c);return i&&(f+=2),{v:a,dec:f,sym:n}}var g=`9007199254740991`;function _(e){return e=e.trim(),e.endsWith(`%`)||(e.startsWith(`(`)&&e.endsWith(`)`)&&(e=e.slice(1,-1)),e=e.replace(/[$£€¥¥,]/g,``).replace(/^[+-]/,``),!/^\d+$/.test(e))?!1:(e=e.replace(/^0+(?=\d)/,``),e.length>16||e.length===16&&e>g)}function v(e){let t=e.toLowerCase(),n=ne.findIndex(e=>e.startsWith(t)||t===`sept`&&e===`september`);return n<0||t.length<3?null:n+1}function y(e){return e=+e,e<100?e<50?2e3+e:1900+e:e}function b(e,t,n,r=0,i=0,a=0,o=!1){let s=new Date(e,t-1,n,r,i,a);return s.getFullYear()!==e||s.getMonth()!==t-1||s.getDate()!==+n?null:{t:s.getTime(),hasTime:o}}function x(e,t=!1){e=e.trim();let n=d.exec(e);if(n){let[,e,t,r,i,a,o]=n;return b(+e,+t,+r,+(i||0),+(a||0),+(o||0),i!==void 0)}if(n=f.exec(e),n){let[,e,,r,i]=n;if(e.length===4&&i.length<=2)return b(+e,+r,+i);if(e.length<=2&&(i.length===4||i.length===2)){let n=y(i);return+e>12&&+r<=12?b(n,+r,+e):+r>12&&+e<=12?b(n,+e,+r):t?b(n,+r,+e):b(n,+e,+r)}return null}if(n=p.exec(e),n){let e=v(n[2]);return e?b(y(n[3]),e,+n[1]):null}if(n=te.exec(e),n){let e=v(n[1]);return e?b(y(n[3]),e,+n[2]):null}return null}function ie(e){let t=f.exec(e.trim());if(!t)return null;let n=t[1],r=t[3],i=t[4];return n.length===4||!(i.length===4||i.length===2)?null:+n>12&&+r<=12?`day`:+r>12&&+n<=12?`month`:+n<=12&&+r<=12?`ambiguous`:null}function S(e){return e.some(e=>{let t=(e??``).trim();return t!==``&&(h(t)!==null||x(t)!==null)})}function C(e){let t=e=>e.type===`date`?`Date`:e.type===`number`?e.format===`year`?`Year`:`Amount`:`Description`,n={},r={};e.forEach(e=>{let r=t(e);n[r]=(n[r]||0)+1}),e.forEach(e=>{let i=t(e);r[i]=(r[i]||0)+1,e.name=n[i]>1?`${i} ${r[i]}`:i})}var w=/\b(year|yr|vintage|cohort)\b/i,T=/\b(amount|amt|balance|bal|price|cost|fee|fees|charge|paid|payment|debit|credit|total|premium|loss|salary|wage|income|expense|revenue|usd|gbp|eur|cad)\b|[$£€]/i,E=/\b(id|no|num|number|account|acct|code|zip|postal|phone|fax|ssn|ein|tin|invoice|inv|ref|reference|sku|upc|isbn|order|customer|cust|member|policy|claim|seq)\b/i,D=/(?<![a-z])(ratio|rate|roe|roa|coc|lr|elr|plr|margin|yield|return|growth|retention|cede|ceded|discount|apr|apy|coupon|util|utilization|share|pct|percent|frequency)(?![a-z])/i;function O(e,t,n,r=!1){if(r)return{format:`float`,dec:2};let i=t.filter(e=>e!==null);if(i.every(e=>Number.isInteger(e))&&i.length)return w.test(e)||i.every(e=>e>=1800&&e<=2100)?{format:`year`,dec:0}:E.test(e)&&!T.test(e)?{format:`plain`,dec:0}:T.test(e)?{format:`float`,dec:2}:{format:`int`,dec:0};let a=0,o=0,s=1/0,c=0;for(let e of i){if(e===0||!Number.isFinite(e))continue;let t=Math.abs(e);a++,t>o&&(o=t),t<s&&(s=t),c+=t}if(!a)return{format:`float`,dec:Math.min(n,6)};if(D.test(e)&&o<=2)return{format:`pct`,dec:Math.max(1,Math.min(4,n-2))};if(T.test(e)||n<=2&&o<1e5)return{format:`float`,dec:2};if(o/s>1e6)return{format:`eng`,dec:0};let l=c/a;return{format:`float`,dec:Math.max(0,Math.min(n,3-Math.floor(Math.log10(l)),6))}}var k={"-9":`n`,"-6":`µ`,"-3":`m`,0:``,3:`k`,6:`M`,9:`G`,12:`T`};function A(e){if(!Number.isFinite(e))return e>0?`inf`:`-inf`;if(e===0)return`0`;let t=Math.abs(e),n=Math.floor(Math.log10(t)/3)*3;n=Math.max(-9,Math.min(12,n));let r=t/10**n;return(e<0?`-`:``)+Number(r.toPrecision(3))+k[n]}function j(e,t){if(e<=t)return Array.from({length:e},(e,t)=>t);let n=Array(t),r=e/t;for(let e=0;e<t;e++)n[e]=Math.floor(e*r);return n}var M=2048,N=/^-?0\d/;function P(e,t){let n=j(t.length,M);return e.map((e,r)=>{let i=!0,a=!0,o=!1,s=!1,c=0;for(let e of n){let n=(t[e][r]??``).trim();if(!(n===``||m(n))&&(c++,i&&(h(n)===null?i=!1:(!o&&N.test(n)&&(o=!0),!s&&_(n)&&(s=!0))),a&&x(n,!1)===null&&(a=!1),o||s||!i&&!a))break}if(c===0||o||s)return s?{name:e,type:`text`,align:`right`,values:null}:{name:e,type:`text`,values:null};if(i){let n=Array(t.length).fill(null),i=0,a=!1;for(let e=0;e<t.length;e++){let o=(t[e][r]??``).trim();if(o===``||m(o))continue;let s=h(o);s&&(n[e]=s.v,s.dec>i&&(i=s.dec),s.sym&&(a=!0))}let o=O(e,n,i,a);return{name:e,type:`number`,format:o.format,dec:o.dec,hasCurrency:a,values:n}}if(a){let n=!1,i=!1,a=!1,o=!1,s=Array(t.length).fill(null);for(let e=0;e<t.length;e++){let c=(t[e][r]??``).trim();if(c===``||m(c))continue;let l=x(c,!1);l&&(s[e]=l.t,i||=l.hasTime);let u=ie(c);u===`day`?(n=!0,o=!0):u===`month`?o=!0:u===`ambiguous`&&(a=!0)}if(n){s=Array(t.length).fill(null);for(let e=0;e<t.length;e++){let n=(t[e][r]??``).trim();if(n===``||m(n))continue;let i=x(n,!0);i&&(s[e]=i.t)}}return{name:e,type:`date`,hasTime:i,ambiguousOrder:a&&!o,values:s}}return{name:e,type:`text`,values:null}})}function F(e,t=null){let r,a,o=null,l;if(s(e)){if({headers:r,rows:a,aligns:o}=c(e),l=t===!1,l&&(a=[r,...a],r=r.map((e,t)=>`col${t+1}`)),!a.length)throw Error(`Markdown table has no data rows.`)}else{let o=i(e,n(e));if(o.length<2)throw Error(`Need a header row and at least one data row.`);l=t===null?S(o[0]):!t,r=l?o[0].map((e,t)=>`col${t+1}`):o[0].map((e,t)=>e.trim()||`col${t+1}`),a=(l?o:o.slice(1)).map(e=>{if(e.length===r.length)return e;let t=e.slice(0,r.length);for(;t.length<r.length;)t.push(``);return t})}let u=P(r,a);return l&&C(u),o&&u.forEach((e,t)=>{o[t]&&(e.align=o[t])}),{headers:u.map(e=>e.name),rows:a,cols:u,headerless:l}}var I=new Map;function L(e){let t=I.get(e);return t||(t=new Intl.NumberFormat(`en-US`,{minimumFractionDigits:e,maximumFractionDigits:e}),I.set(e,t)),t}function R(e){if(e==null||e===``)return null;if(e===`year`||e===`eng`)return{kind:e};let t=/^(,)?(?:\.(\d+))?([fd%es])$/.exec(e);if(!t)throw Error(`CsvGrid: unrecognized format spec '${e}'`);return{kind:t[3],comma:!!t[1],dec:t[2]===void 0?null:+t[2]}}var z=[[0xe8d4a51000,`T`],[1e9,`G`],[1e6,`M`],[1e3,`k`],[1,``],[.001,`m`],[1e-6,`µ`],[1e-9,`n`]];function B(e,t){switch(t.kind){case`year`:return String(e);case`eng`:return A(e);case`d`:{let n=Math.round(e);return t.comma?L(0).format(n):String(n)}case`f`:{let n=t.dec??2;return t.comma?L(n).format(e):e.toFixed(n)}case`%`:{let n=t.dec??0,r=e*100;return(t.comma?L(n).format(r):r.toFixed(n))+`%`}case`e`:return e.toExponential(t.dec??2);case`s`:{if(t.dec===null||t.dec===void 0)return A(e);if(e===0)return 0 .toFixed(t.dec);let n=Math.abs(e);for(let[r,i]of z)if(n>=r)return(e/r).toFixed(t.dec)+i;return(e/1e-9).toFixed(t.dec)+`n`}}}function V(e){return[...e].map(e=>({l:`left`,r:`right`,c:`center`})[e]??null)}function H(e,t,n,r=`auto`){if(e=(e??``).trim(),e===``)return``;if(r===`raw`)return e;if(t.type===`number`){let r=t.values[n];if(r===null)return m(e)?``:e;if(!Number.isFinite(r))return r>0?`inf`:`-inf`;if(t.fmt)return B(r,t.fmt);if(t.format===`year`||t.format===`plain`)return String(r);if(t.format===`eng`)return A(r);if(t.format===`pct`)return L(t.dec).format(r*100)+`%`;let i=L(t.dec).format(r);if(t.hasCurrency){let t=l.exec(e);if(t)return i[0]===`-`?`-`+t[0]+i.slice(1):t[0]+i}return i}if(t.type===`date`){let r=t.values[n];if(r===null)return m(e)?``:e;let i=new Date(r),a=e=>String(e).padStart(2,`0`),o=`${i.getFullYear()}-${a(i.getMonth()+1)}-${a(i.getDate())}`;return t.hasTime&&(o+=` ${a(i.getHours())}:${a(i.getMinutes())}`),o}return e}function U(e,t){let n=e=>(e=(e??``)+``,/[",\r\n]/.test(e)?`"`+e.replace(/"/g,`""`)+`"`:e),r=e=>e.map(n).join(`,`),i=[r(e)];for(let e of t)i.push(r(e));return i.join(`\r
|
|
4
|
+
`)}function W(e,t,n=[]){let r=e=>((e??``)+``).replace(/\|/g,`\\|`).replace(/\s*\r?\n\s*/g,` `),i=e=>e===`right`?`---:`:e===`center`?`:--:`:e===`left`?`:---`:`---`,a=e=>`| `+e.map(r).join(` | `)+` |`,o=`|`+e.map((e,t)=>i(n[t])).join(`|`)+`|`,s=[a(e),o];for(let e of t)s.push(a(e));return s.join(`
|
|
5
|
+
`)}function G(e,t){if(!Array.isArray(e))throw Error(`CsvGrid: records must be an array.`);let n=e=>e==null||typeof e==`number`&&Number.isNaN(e)?``:String(e),r,i;if(e.length&&Array.isArray(e[0])){if(!t)throw Error(`CsvGrid: columns are required with array-of-arrays records.`);r=t.map(String),i=e.map(e=>r.map((t,r)=>n(e[r])))}else r=(t??Object.keys(e[0]??{})).map(String),i=e.map(e=>r.map(t=>n(e[t])));let a=P(r,i);return{headers:r,rows:i,cols:a,headerless:!1}}function K(e){let t=[];for(let n of e.trim().split(/\s+/)){if(!n)continue;let e={kind:`fuzzy`,negate:!1};n.startsWith(`!`)&&(e.negate=!0,e.kind=`exact`,n=n.slice(1)),n.startsWith(`'`)&&(e.kind=`exact`,n=n.slice(1)),n.startsWith(`^`)&&(e.kind=`prefix`,n=n.slice(1)),n.endsWith(`$`)&&(e.kind=e.kind===`prefix`?`exact`:`suffix`,n=n.slice(0,-1)),n&&(e.cs=/[A-Z]/.test(n),e.str=e.cs?n:n.toLowerCase(),t.push(e))}return t}var q=/[\s_\-\/\\.,:;()[\]{}"']/;function J(e,t){let n=t.length,r=e.length;if(r===0)return 0;if(r>n)return-1;let i=0,a=-1;for(let o=0;o<n;o++)if(t[o]===e[i]&&(i++,i===r)){a=o;break}if(a<0)return-1;i=r-1;let o=a;for(let n=a;n>=0&&!(t[n]===e[i]&&(o=n,i--,i<0));n--);let s=100-3*(a-o+1-r)-Math.min(o,20);i=0;let c=!1;for(let n=o;n<=a&&i<r;n++)t[n]===e[i]?((n===0||q.test(t[n-1]))&&(s+=8),c&&(s+=4),c=!0,i++):c=!1;return s}function Y(e,t,n){let r=e.cs?n:t,i,a=0;switch(e.kind){case`exact`:i=r.includes(e.str);break;case`prefix`:i=r.startsWith(e.str);break;case`suffix`:i=r.endsWith(e.str);break;default:{let t=J(e.str,r);i=t>=0,a=t}}return e.negate&&(i=!i),i?a:-1}function ae(e){return e=(e??``).trim(),e===``?``:(e=e.toLowerCase().normalize(`NFKD`).replace(/\p{Diacritic}/gu,``),e.replace(/\d+/g,e=>(e=e.replace(/^0+(?=\d)/,``),``+String.fromCharCode(e.length)+e)))}function oe(e,t,n,r=`equal-risk`){return r===`coverage`?ce(e,t,n):se(e,t,n)}function se(e,t,n){let r=(e,t)=>e.length?e[Math.floor(t*(e.length-1))]:0,i=n=>e.map((e,i)=>Math.max(t[i],r(e,n))),a=e=>e.reduce((e,t)=>e+t,0),o=i(1);if(a(o)<=n)return o;if(a(i(0))>=n)return i(0);let s=0,c=1;for(let e=0;e<32;e++){let e=(s+c)/2;a(i(e))<=n?s=e:c=e}return i(s)}function ce(e,t,n){let r=e.map((e,n)=>Math.max(t[n],e.length?e[e.length-1]:0)),i=e=>e.reduce((e,t)=>e+t,0);if(i(r)<=n)return r;if(i(t)>=n)return t.slice();let a=t.slice(),o=n-i(t),s=[];for(let n=0;n<e.length;n++){let r=le(e[n],t[n]);for(let e=1;e<r.length;e++){let t=r[e].w-r[e-1].w,i=r[e].cells-r[e-1].cells;t>0&&i>0&&s.push({j:n,dw:t,slope:i/t})}}s.sort((e,t)=>t.slope-e.slope);for(let e of s){if(o<=0)break;let t=Math.min(e.dw,o);a[e.j]+=t,o-=t}return a}function le(e,t){let n=e.length,r=0;for(;r<n&&e[r]<=t;)r++;let i=[{w:t,cells:r}];for(;r<n;){let t=e[r];for(;r<n&&e[r]===t;)r++;i.push({w:t,cells:r})}let a=[];for(let e of i){for(;a.length>=2;){let t=a[a.length-2],n=a[a.length-1];if((n.w-t.w)*(e.cells-t.cells)-(n.cells-t.cells)*(e.w-t.w)>=0)a.pop();else break}a.push(e)}return a}function ue(e,t){let n=e.trim();if(!n)return null;if(t.type===`number`||t.type===`date`){let e=t.type===`number`?e=>{let t=h(e);return t?t.v:NaN}:e=>{let t=x(e);return t?t.t:NaN},r=/^(>=|<=|>|<|=)\s*(.+)$/.exec(n);if(r){let n=e(r[2]);if(!isNaN(n)){let e=r[1];return(r,i)=>{let a=t.values[i];if(a===null)return!1;switch(e){case`>`:return a>n;case`>=`:return a>=n;case`<`:return a<n;case`<=`:return a<=n;default:return a===n}}}}if(r=/^(.+?)\.\.(.+)$/.exec(n),r){let n=e(r[1]),i=e(r[2]);if(!isNaN(n)&&!isNaN(i))return(e,r)=>{let a=t.values[r];return a!==null&&a>=n&&a<=i}}}let r=n.toLowerCase();return(e,t)=>e.toLowerCase().includes(r)}function X(e){return e.align?`col-${e.type} align-${e.align}`:`col-${e.type}`}function Z(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}var de=1e6,fe=2048,pe=1e4,Q=10;function $(e,t){let n=document.createElement(e);return t&&(n.className=t),n}return class n{constructor(e,t,n={}){let r=typeof e==`string`?document.querySelector(e):e;if(!r)throw Error(`CsvGrid: target element not found.`);this.root=r,r.csvgrid=this,this.opts={globalSearch:!0,columnFilters:!0,sortable:!0,statusBar:!0,expandButtons:!0,align:null,formats:null,renderCap:2048,eagerCells:262144,worker:!0,headerMode:`auto`,widthMode:`equal-risk`,maxRows:null,height:null,displayMode:`auto`,selectable:!1,selectMode:`row`,hiddenColumns:null,...n},this.displayMode=this.opts.displayMode===`raw`?`raw`:`auto`,this.fileName=``,this.headers=[],this.rows=[],this.cols=[],this.formatted=[],this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,this.loadGen=0,this.scores=[],this.sortedOrder=null,this.layout=null,this.expandAll=!1,this.manualWidths=new Map,this.guessedHeaders=!1,this.ambiguousDateCols=[],this.view=[],this.sortCol=null,this.sortDir=1,this.globalFilter=``,this.colFilters=[],this.showAll=!1,this.visibleCols=[],this.selected=null,this._rowH=0,this._winStart=0,this._winEnd=0,this._windowed=!1,this._scrollRaf=0,this._worker=void 0,this._pending=new Map,this._buildScaffold(),t&&this.setData(t)}_buildScaffold(){let e=this.opts,t=this.root;if(t.classList.add(`csvgrid`),t.replaceChildren(),this.els={},e.globalSearch||e.expandButtons){let n=$(`div`,`csvgrid-toolbar`);if(e.globalSearch){let e=$(`input`,`csvgrid-search`);e.type=`text`,e.placeholder=`fzf search: term 'exact !not ^pre fix$`,e.title=`Space-separated terms AND together. Fuzzy by default; 'exact, !exclude, ^prefix, suffix$. Uppercase = case-sensitive.`,e.addEventListener(`input`,()=>this.setGlobalFilter(e.value)),e.addEventListener(`keydown`,t=>{t.key===`Escape`&&(t.preventDefault(),e.value=``,e.blur(),this.setGlobalFilter(``))}),n.appendChild(e),this.els.search=e}if(e.expandButtons){let e=$(`button`,`csvgrid-btn`);e.type=`button`,e.textContent=`Expand`,e.title=`Expand all columns to their full natural width (table scrolls horizontally)`,e.addEventListener(`click`,()=>this.expand());let t=$(`button`,`csvgrid-btn`);t.type=`button`,t.textContent=`Contract`,t.title=`Back to fitted widths (equal-risk squeeze); also clears any dragged widths`,t.addEventListener(`click`,()=>this.contract()),n.append(e,t)}t.appendChild(n)}let n=$(`div`,`csvgrid-scroll`);n.addEventListener(`scroll`,()=>this._onScroll());let r=$(`table`,`csvgrid-table`),i=$(`thead`),a=$(`tbody`);r.append(i,a),n.appendChild(r),t.appendChild(n);let o=$(`div`,`csvgrid-capnote csvgrid-hidden`),s=$(`button`,`csvgrid-btn`);s.type=`button`,s.addEventListener(`click`,()=>{this.showAll=!0,this.renderBody(),this.renderStatus()}),o.appendChild(s),t.appendChild(o);let c=$(`div`,`csvgrid-error csvgrid-hidden`);t.appendChild(c);let l=null;e.statusBar===!0?(l=$(`div`,`csvgrid-status`),t.appendChild(l)):e.statusBar&&(l=e.statusBar),Object.assign(this.els,{table:r,head:i,body:a,scroll:n,capNote:o,showAllBtn:s,error:c,status:l}),r.addEventListener(`mouseover`,e=>{let t=e.target.closest(`td, th`);t&&!t.title&&t.scrollWidth>t.clientWidth&&(t.title=t.textContent)}),this.opts.selectable&&(t.dataset.selectable=``,a.addEventListener(`click`,e=>this._onBodyClick(e)))}setData(e){let t=++this.loadGen,n=new Promise((n,r)=>{this._resolveData(e,t).then(({d:e,name:r})=>{t===this.loadGen&&(this._install(e,r),n())},e=>{t===this.loadGen&&(this._showError(e.message||String(e)),r(e))})});return n.catch(()=>{}),n}async _resolveData(e,t){if(!e||typeof e!=`object`)throw Error(`CsvGrid: data must be {csv}, {records[, columns]}, or {url}.`);if(this._headerMode=e.headerMode??this.opts.headerMode,e.url!==void 0){let n=String(e.url),r=e.name??decodeURIComponent(n.split(`/`).pop()||n),i=await fetch(n);if(!i.ok)throw Error(`HTTP ${i.status}`);return{d:await this._parse(await i.text(),t,r),name:r}}if(e.csv!==void 0){let n=e.name??``;return{d:await this._parse(e.csv,t,n),name:n}}if(e.records!==void 0)return{d:G(e.records,e.columns),name:e.name??``};throw Error(`CsvGrid: data must be {csv}, {records[, columns]}, or {url}.`)}_parse(e,n,r){if(e=t(e),!e.trim())throw Error(`No data found.`);let i=this._headerMode===`first-row`?!0:this._headerMode===`headerless`?!1:null,a=this.opts.worker!==!1&&e.length>=de?this._getWorker():null;return a?(this._setStatus(`parsing ${r||`data`} (${(e.length/1e6).toFixed(1)} MB)…`),new Promise((t,r)=>{this._pending.set(n,{resolve:t,reject:r}),a.postMessage({gen:n,text:e,headerOverride:i})})):F(e,i)}_getWorker(){if(this._worker===void 0){this._worker=null;try{let t=typeof this.opts.worker==`string`?new Worker(this.opts.worker):new Worker(new URL(``+new URL(`csv-grid.worker.js`,e).href,``+e),{type:`module`});t.onmessage=e=>{let{gen:t,result:n,error:r}=e.data,i=this._pending.get(t);i&&(this._pending.delete(t),r?i.reject(Error(r)):i.resolve(n))},t.onerror=()=>{let e=[...this._pending.values()];this._pending.clear();for(let t of e)t.reject(Error(`Background parse failed.`))},this._worker=t}catch{}}return this._worker}_install(e,t){let{rows:n,cols:r}=e;if(this.opts.align){let e=V(this.opts.align);r.forEach((t,n)=>{e[n]&&(t.align=e[n])})}this.opts.formats&&r.forEach((e,t)=>{e.fmt=R(this.opts.formats[t])}),this.fileName=t||``,this.guessedHeaders=e.headerless,this.ambiguousDateCols=r.filter(e=>e.ambiguousOrder).map(e=>e.name),this.headers=e.headers,this.rows=n,this.cols=r;let i=Array.isArray(this.opts.hiddenColumns)&&this.opts.hiddenColumns.length?new Set(this.opts.hiddenColumns):null;if(this.visibleCols=r.map((e,t)=>t).filter(e=>!i||!i.has(this.headers[e])),this.selected=null,this.formatted=Array(n.length),this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,n.length*r.length<=this.opts.eagerCells){for(let e=0;e<n.length;e++)this.getFormattedRow(e);this.searchRaw=this.formatted.map((e,t)=>e.join(` `)+` `+n[t].join(` `)),this.searchLow=this.searchRaw.map(e=>e.toLowerCase()),this.searchReady=!0}this.sortCol=null,this.sortDir=1,this.sortedOrder=null,this.globalFilter=``,this.colFilters=Array(r.length).fill(``),this.manualWidths=new Map,this.showAll=!1,this._rowH=0,this._winStart=this._winEnd=0,this._windowed=!1,this.els.search&&(this.els.search.value=``),this.els.error.classList.add(`csvgrid-hidden`),this.renderHead(),this.layout=this.measureLayout(),this.applyLayout(),this.refresh(),this._applyHeight()}_applyHeight(){let e=this.opts,t=!1;if(e.height)this.els.scroll.style.maxHeight=e.height,t=!0;else if(e.maxRows){let n=this.els.body.querySelector(`tr:not(.csvgrid-spacer)`),r=this._rowH||(n?n.offsetHeight:0);if(r){let n=this.els.head.offsetHeight;this.els.scroll.style.maxHeight=Math.ceil(n+r*e.maxRows+2)+`px`,t=!0}}t&&this.renderBody()}_showError(e){this.els.error.textContent=e,this.els.error.classList.remove(`csvgrid-hidden`)}_setStatus(e){this.els.status&&(this.els.status.textContent=e)}destroy(){this.loadGen++,this._pending.clear(),this._scrollRaf&&=(cancelAnimationFrame(this._scrollRaf),0),this._worker&&=(this._worker.terminate(),null),delete this.root.csvgrid,this.root.classList.remove(`csvgrid`),delete this.root.dataset.selectable,this.root.replaceChildren()}setGlobalFilter(e){this.globalFilter=e,this.refresh()}clearFilters(){this.globalFilter=``,this.colFilters=this.colFilters.map(()=>``),this.els.search&&(this.els.search.value=``),this.renderHead(),this.refresh()}expand(){this.expandAll=!0,this.applyLayout()}contract(){this.expandAll=!1,this.manualWidths.clear(),this.applyLayout()}export({scope:e=`view`,format:t=`csv`,values:n=`raw`}={}){let r=e===`all`?this.rows.map((e,t)=>t):this.view,i=n===`formatted`&&r.length<=this.opts.renderCap,a=r.map(e=>i?this.getFormattedRow(e):this.cols.map((t,n)=>this.rows[e][n]??``));if(t===`md`){let e=this.cols.map(e=>e.align||(e.type===`number`?`right`:e.type===`date`?`center`:`left`));return W(this.headers,a,e)}return U(this.headers,a)}setWidthMode(e){this.opts.widthMode=e===`coverage`?`coverage`:`equal-risk`,this.applyLayout()}setDisplayMode(e){if(e=e===`raw`?`raw`:`auto`,e===this.displayMode||!this.cols.length){this.displayMode=e;return}if(this.displayMode=e,this.formatted=Array(this.rows.length),this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,this.rows.length*this.cols.length<=this.opts.eagerCells){for(let e=0;e<this.rows.length;e++)this.getFormattedRow(e);this.searchRaw=this.formatted.map((e,t)=>e.join(` `)+` `+this.rows[t].join(` `)),this.searchLow=this.searchRaw.map(e=>e.toLowerCase()),this.searchReady=!0}this.layout=this.measureLayout(),this.applyLayout(),this.refresh()}measureLayout(){let e=(n._canvas||=document.createElement(`canvas`)).getContext(`2d`),t=getComputedStyle(this.els.table),r=`${t.fontSize} ${t.fontFamily}`,i=j(this.rows.length,fe),a=[],o=[];for(let t of this.visibleCols){e.font=`bold ${r}`,o.push(Math.max(50,Math.ceil(e.measureText(this.cols[t].name).width)+14+18)),e.font=r;let n=[];for(let r of i){let i=this.getFormattedRow(r)[t];i!==``&&n.push(Math.ceil(e.measureText(i).width)+18)}n.sort((e,t)=>e-t),a.push(n)}return{arrays:a,floors:o}}startColResize(e,t){e.preventDefault(),e.stopPropagation();let n=this.els.table,r=n.querySelectorAll(`colgroup col`)[t];if(!r)return;let i=e.clientX,a=parseFloat(r.style.width);document.body.classList.add(`csvgrid-resizing`);let o=()=>{let e=0;n.querySelectorAll(`colgroup col`).forEach(t=>{e+=parseFloat(t.style.width)}),n.style.width=e+`px`},s=e=>{let n=Math.max(24,Math.round(a+e.clientX-i));this.manualWidths.set(t,n),r.style.width=n+`px`,o()},c=()=>{document.body.classList.remove(`csvgrid-resizing`),document.removeEventListener(`mousemove`,s),document.removeEventListener(`mouseup`,c)};document.addEventListener(`mousemove`,s),document.addEventListener(`mouseup`,c)}fitColumn(e){let{arrays:t,floors:n}=this.layout,r=Math.max(n[e],t[e].length?t[e][t[e].length-1]:0);this.manualWidths.set(e,r),this.applyLayout()}applyLayout(){if(!this.layout)return;let e=this.els.table,t=this.expandAll?1/0:e.parentElement.clientWidth;if(!t)return;let n=oe(this.layout.arrays,this.layout.floors,t,this.opts.widthMode);for(let[e,t]of this.manualWidths)e<n.length&&(n[e]=t);let r=e.querySelector(`colgroup`);r&&r.remove(),r=document.createElement(`colgroup`);for(let e of n){let t=document.createElement(`col`);t.style.width=e+`px`,r.appendChild(t)}e.prepend(r),e.style.tableLayout=`fixed`,e.style.width=n.reduce((e,t)=>e+t,0)+`px`,this.view.length&&this.renderBody()}getFormattedRow(e){let t=this.formatted[e];return t||(t=this.cols.map((t,n)=>H(this.rows[e][n],t,e,this.displayMode)),this.formatted[e]=t),t}buildSearchIndexChunked(){let e=this.loadGen,t=this.rows.length,n=Array(t),r=Array(t),i=0;this.indexing=0;let a=()=>{if(e!==this.loadGen)return;let o=Math.min(t,i+pe);for(;i<o;i++){let e=this.getFormattedRow(i).join(` `)+` `+this.rows[i].join(` `);n[i]=e,r[i]=e.toLowerCase()}i<t?(this.indexing=i/t,this.renderStatus(),setTimeout(a,0)):(this.searchRaw=n,this.searchLow=r,this.searchReady=!0,this.indexing=null,this.refresh())};a()}_buildSortOrder(){let e=this.rows.length,t=Array(e);for(let n=0;n<e;n++)t[n]=n;let n=this.sortCol;if(n!==null){let r=this.cols[n],i=this.sortDir;if(r.type===`text`){let r=Array(e);for(let t=0;t<e;t++)r[t]=ae(this.rows[t][n]);t.sort((e,t)=>{let n=r[e],a=r[t];return n===``||a===``?n===a?0:n===``?1:-1:n<a?-i:n>a?i:0})}else{let e=r.values;t.sort((t,n)=>{let r=e[t],a=e[n];return r===null||a===null?r===a?0:r===null?1:-1:i*(r-a)})}}this.sortedOrder=t}rebuildView(){let{rows:e,cols:t}=this,n=K(this.globalFilter);n.length&&!this.searchReady&&(this.indexing===null&&this.buildSearchIndexChunked(),n=[]);let r=n.some(e=>e.kind===`fuzzy`&&!e.negate),i=this.colFilters.map((e,n)=>ue(e||``,t[n])),a=i.some(e=>e)||n.length,o=r&&this.sortCol===null;(!this.sortedOrder||this.sortedOrder.length!==e.length)&&this._buildSortOrder();let s=this.sortedOrder;if(!a){this.view=s.slice();return}let c=[];o&&(this.scores=[]);for(let t=0;t<s.length;t++){let r=s[t],a=!0,l=0;for(let e of n){let t=Y(e,this.searchLow[r],this.searchRaw[r]);if(t<0){a=!1;break}l+=t}if(a){for(let t=0;t<i.length;t++)if(i[t]&&!i[t](e[r][t]??``,r)){a=!1;break}}a&&(o&&(this.scores[r]=l),c.push(r))}o&&c.sort((e,t)=>this.scores[t]-this.scores[e]||e-t),this.view=c}renderHead(){let{cols:e}=this,t=this.els.head;t.innerHTML=``;let n=document.createElement(`tr`);if(this.visibleCols.forEach((t,r)=>{let i=e[t],a=document.createElement(`th`);a.className=X(i),this.opts.sortable?(a.innerHTML=`<span class="sort-arrow">${this.sortCol===t?this.sortDir===1?`▲`:`▼`:``}</span>${Z(i.name)}`,a.title=`${i.name} (${i.type}) — click to sort`,a.addEventListener(`click`,()=>this.onSort(t))):(a.innerHTML=`<span class="sort-arrow"></span>${Z(i.name)}`,a.title=`${i.name} (${i.type})`,a.classList.add(`csvgrid-nosort`));let o=document.createElement(`span`);o.className=`col-resizer`,o.title=`Drag to resize — double-click to fit content`,o.addEventListener(`mousedown`,e=>this.startColResize(e,r)),o.addEventListener(`dblclick`,e=>{e.stopPropagation(),this.fitColumn(r)}),o.addEventListener(`click`,e=>e.stopPropagation()),a.appendChild(o),n.appendChild(a)}),t.appendChild(n),!this.opts.columnFilters)return;let r=document.createElement(`tr`);r.className=`filter-row`,this.visibleCols.forEach(t=>{let n=e[t],i=document.createElement(`th`),a=document.createElement(`input`);a.type=`text`,a.className=`csvgrid-filter`,a.placeholder=n.type===`text`?`filter`:`filter, >, .. `,a.value=this.colFilters[t]||``,a.addEventListener(`input`,()=>{this.colFilters[t]=a.value,a.classList.toggle(`active-filter`,a.value.trim()!==``),this.refresh()}),a.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),a.value=``,this.colFilters[t]=``,a.classList.remove(`active-filter`),a.blur(),this.refresh())}),i.appendChild(a),r.appendChild(i)}),t.appendChild(r)}renderBody(){let{view:e}=this,t=this.els.body,n=this.els.scroll;if(!e.length){t.innerHTML=``,this._winStart=this._winEnd=0,this._windowed=!1,this._updateCapNote();return}if(!this._rowH){this._renderSlice(0,Math.min(e.length,60),0,0);let n=t.querySelector(`tr:not(.csvgrid-spacer)`);if(this._rowH=n?n.offsetHeight:0,!this._rowH){let t=Math.min(e.length,this.opts.renderCap);this._renderSlice(0,t,0,0),this._winStart=0,this._winEnd=t,this._windowed=!1,this._updateCapNote();return}}let r=this._rowH,i=this._computeWindow();if(this._renderSlice(i.start,i.end,i.start*r,(e.length-i.end)*r),n.scrollHeight-n.clientHeight>1)this._windowed=!0,this._winStart=i.start,this._winEnd=i.end;else{this._windowed=!1;let t=this.showAll?e.length:Math.min(e.length,this.opts.renderCap);this._renderSlice(0,t,0,0),this._winStart=0,this._winEnd=t}this._updateCapNote()}_renderSlice(e,t,n,r){let{cols:i,view:a}=this,o=this.opts.selectable,s=this.visibleCols.length,c=[];n>0&&c.push(`<tr class="csvgrid-spacer"><td colspan="${s}" style="height:${n}px"></td></tr>`);for(let n=e;n<t;n++){let e=a[n],t=this.getFormattedRow(e),r=this.visibleCols.map(e=>{let n=i[e],r=t[e];return r===``?`<td class="${X(n)} blank">·</td>`:`<td class="${X(n)}">${Z(r)}</td>`});c.push(`<tr${o?` data-r="${e}"`:``}>${r.join(``)}</tr>`)}r>0&&c.push(`<tr class="csvgrid-spacer"><td colspan="${s}" style="height:${r}px"></td></tr>`),this.els.body.innerHTML=c.join(``),o&&this._paintSelection()}_computeWindow(){let{view:e,_rowH:t}=this,n=this.els.scroll,r=e.length,i=n.clientHeight||r*t,a=Math.max(1,Math.ceil(i/t)),o=Math.max(0,Math.floor(n.scrollTop/t)-Q);return{start:o,end:Math.min(r,o+a+2*Q)}}_onScroll(){!this._windowed||this._scrollRaf||(this._scrollRaf=requestAnimationFrame(()=>{if(this._scrollRaf=0,!this._windowed)return;let{start:e,end:t}=this._computeWindow();e===this._winStart&&t===this._winEnd||(this._winStart=e,this._winEnd=t,this._renderSlice(e,t,e*this._rowH,(this.view.length-t)*this._rowH))}))}_updateCapNote(){let e=this.els.capNote;if(!e)return;let t=this.view.length;!this._windowed&&this._winEnd<t?(e.classList.remove(`csvgrid-hidden`),this.els.showAllBtn.textContent=`Showing first ${this._winEnd.toLocaleString()} of ${t.toLocaleString()} rows — show all`):e.classList.add(`csvgrid-hidden`)}renderStatus(){if(!this.els.status)return;let e=e=>e.toLocaleString(),t=this.rows.length,n=this.view.length,r=this.fileName?this.fileName+` — `:``;r+=n===t?`${e(t)} rows`:`${e(n)} of ${e(t)} rows`,r+=` × ${this.cols.length} cols`,!this._windowed&&this._winEnd<n&&(r+=` — showing rows 1–${e(this._winEnd)}`),this.guessedHeaders&&(r+=` (headers guessed)`),this.indexing!==null&&(r+=` — indexing search ${Math.round(this.indexing*100)}%`),this.els.status.textContent=r}refresh(){this.rebuildView(),this.renderBody(),this.renderStatus()}onSort(e){this.sortCol===e?this.sortDir===1?this.sortDir=-1:(this.sortCol=null,this.sortDir=1):(this.sortCol=e,this.sortDir=1),this._buildSortOrder(),this.renderHead(),this.refresh()}static forElement(e){let t=typeof e==`string`?document.querySelector(e):e;return t&&t.csvgrid||null}_rawValue(e,t){let n=this.cols[t];if(n&&n.type===`number`){let t=n.values[e];if(t!=null)return t}return this.rows[e][t]??null}_rowDetail(e,t,n){let r=this.getFormattedRow(e),i={},a={};return this.headers.forEach((t,n)=>{i[t]=this._rawValue(e,n),a[t]=r[n]??``}),{name:this.fileName,rowIndex:e,viewIndex:this.view.indexOf(e),column:this.headers[t],columnIndex:t,value:this._rawValue(e,t),valueText:r[t]??``,row:i,rowText:a,originalEvent:n}}_onBodyClick(e){let t=e.target.closest(`td`),n=t&&t.parentElement;if(!t||!n||n.dataset.r===void 0)return;let r=+n.dataset.r,i=[...n.children].indexOf(t),a=this.visibleCols[i];if(a===void 0)return;let o=new CustomEvent(`csvgrid:cellclick`,{detail:this._rowDetail(r,a,e),bubbles:!0,composed:!0,cancelable:!0});this.root.dispatchEvent(o)&&this.opts.selectMode!==`none`&&(this.selected={rowIndex:r,columnIndex:a},this._paintSelection())}_paintSelection(){let e=this.els.body;if(e.querySelectorAll(`.csvgrid-selected, .csvgrid-selected-row`).forEach(e=>e.classList.remove(`csvgrid-selected`,`csvgrid-selected-row`)),!this.selected||this.opts.selectMode===`none`)return;let t=e.querySelector(`tr[data-r="${this.selected.rowIndex}"]`);if(t)if(this.opts.selectMode===`cell`){t.classList.add(`csvgrid-selected-row`);let e=t.children[this.visibleCols.indexOf(this.selected.columnIndex)];e&&e.classList.add(`csvgrid-selected`)}else t.classList.add(`csvgrid-selected`)}getSelection(){return this.selected?this._rowDetail(this.selected.rowIndex,this.selected.columnIndex,null):null}clearSelection(){this.selected=null,this.opts.selectable&&this._paintSelection()}selectRow(e){if(!this.opts.selectable)return;this.selected={rowIndex:e,columnIndex:this.selected?this.selected.columnIndex:this.visibleCols[0]};let t=this.view.indexOf(e);if(t<0){this._paintSelection();return}if(this._windowed&&this._rowH){let e=this._rowH,n=this.els.scroll,r=t*e;(r<n.scrollTop||r+e>n.scrollTop+n.clientHeight)&&(n.scrollTop=Math.max(0,r-e*Q)),this.renderBody();return}this._paintSelection();let n=this.els.body.querySelector(`tr[data-r="${e}"]`);n&&n.scrollIntoView({block:`nearest`})}}})();
|
|
6
|
+
//# sourceMappingURL=csv-grid.iife.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
var CsvGrid=(function(){var e=typeof document<`u`&&document.currentScript&&document.currentScript.src||(typeof document<`u`?document.baseURI:``);function t(e){return(e??``).replace(/^\uFEFF/,``).replace(/^(?:[ \t]*(?:\r\n|\n|\r))+/,``)}function n(e){let t=[`,`,` `,`;`,`|`],n=e.split(/\r\n|\n|\r/,20).filter(e=>e.length),i=`,`,a=0;for(let e of t){let t=n.map(t=>r(t,e).length),o=t[0];if(o<2)continue;let s=o*(t.every(e=>e===o)?10:1);s>a&&(a=s,i=e)}return i}function r(e,t){let n=[],r=``,i=!1;for(let a=0;a<e.length;a++){let o=e[a];i?o===`"`?i=!1:r+=o:o===`"`?i=!0:o===t?(n.push(r),r=``):r+=o}return n.push(r),n}function i(e,t){let n=[],r=[],i=``,a=!1,o=0,s=e.length;for(;o<s;){let s=e[o];if(a){if(s===`"`){if(e[o+1]===`"`){i+=`"`,o+=2;continue}a=!1,o++;continue}i+=s,o++;continue}if(s===`"`){a=!0,o++;continue}if(s===t){r.push(i),i=``,o++;continue}if(s===`\r`||s===`
|
|
2
|
-
`){r.push(i),i=``,n.push(r),r=[],s===`\r`&&e[o+1]===`
|
|
3
|
-
`&&o++,o++;continue}i+=s,o++}for((i.length||r.length)&&(r.push(i),n.push(r));n.length&&n[n.length-1].every(e=>e.trim()===``);)n.pop();return n}function a(e){e=e.trim(),e.startsWith(`|`)&&(e=e.slice(1)),e.endsWith(`|`)&&!e.endsWith(`\\|`)&&(e=e.slice(0,-1));let t=[],n=``;for(let r=0;r<e.length;r++){let i=e[r];i===`\\`&&e[r+1]===`|`?(n+=`|`,r++):i===`|`?(t.push(n),n=``):n+=i}return t.push(n),t.map(e=>e.trim())}var o=/^:?-+:?$/;function s(e){let t=e.split(/\r\n|\n|\r/).filter(e=>e.trim()!==``);if(t.length<2||!t[0].includes(`|`))return!1;let n=a(t[1]);return n.length>0&&n.every(e=>o.test(e))}function c(e){let t=e.split(/\r\n|\n|\r/).filter(e=>e.trim()!==``),n=a(t[0]).map((e,t)=>e||`col${t+1}`),r=a(t[1]).map(e=>{let t=e.startsWith(`:`),n=e.endsWith(`:`);return t&&n?`center`:n?`right`:t?`left`:null});for(;r.length<n.length;)r.push(null);return{headers:n,rows:t.slice(2).filter(e=>e.includes(`|`)).map(e=>{let t=a(e).slice(0,n.length);for(;t.length<n.length;)t.push(``);return t}),aligns:r}}var l=/^\(?\$?-?(?:[0-9][0-9,]*(?:\.[0-9]+)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?%?\)?$/,u=/^(\d{4})-(\d{1,2})-(\d{1,2})(?:[T ](\d{1,2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?Z?)?$/,d=/^(\d{1,4})([\/\-.])(\d{1,2})\2(\d{1,4})$/,f=/^(\d{1,2})[ \-]([A-Za-z]{3,9})\.?,?[ \-](\d{2,4})$/,p=/^([A-Za-z]{3,9})\.?,?[ \-](\d{1,2}),?[ \-](\d{2,4})$/,m=[`january`,`february`,`march`,`april`,`may`,`june`,`july`,`august`,`september`,`october`,`november`,`december`],h=new Set([`nan`,`na`,`n/a`,`#n/a`,`null`,`none`,`-`,`--`,`.`]);function g(e){return h.has((e??``).trim().toLowerCase())}function _(e){if(e=e.trim(),!l.test(e))return null;let t=!1;e.startsWith(`(`)&&e.endsWith(`)`)&&(t=!0,e=e.slice(1,-1));let n=!1;e.endsWith(`%`)&&(n=!0,e=e.slice(0,-1)),e=e.replace(/[$,]/g,``);let r=parseFloat(e);if(!isFinite(r))return null;t&&(r=-r),n&&(r/=100);let i=/^([^eE]*)[eE]([+-]?\d+)$/.exec(e),a=i?i[1]:e,o=i?+i[2]:0,s=a.indexOf(`.`),c=Math.max(0,(s<0?0:a.length-s-1)-o);return n&&(c+=2),{v:r,dec:c}}var v=`9007199254740991`;function y(e){return e=e.trim(),e.endsWith(`%`)||(e.startsWith(`(`)&&e.endsWith(`)`)&&(e=e.slice(1,-1)),e=e.replace(/[$,]/g,``).replace(/^[+-]/,``),!/^\d+$/.test(e))?!1:(e=e.replace(/^0+(?=\d)/,``),e.length>16||e.length===16&&e>v)}function b(e){let t=e.toLowerCase(),n=m.findIndex(e=>e.startsWith(t)||t===`sept`&&e===`september`);return n<0||t.length<3?null:n+1}function x(e){return e=+e,e<100?e<50?2e3+e:1900+e:e}function S(e,t,n,r=0,i=0,a=0,o=!1){let s=new Date(e,t-1,n,r,i,a);return s.getFullYear()!==e||s.getMonth()!==t-1||s.getDate()!==+n?null:{t:s.getTime(),hasTime:o}}function C(e,t=!1){e=e.trim();let n=u.exec(e);if(n){let[,e,t,r,i,a,o]=n;return S(+e,+t,+r,+(i||0),+(a||0),+(o||0),i!==void 0)}if(n=d.exec(e),n){let[,e,,r,i]=n;if(e.length===4&&i.length<=2)return S(+e,+r,+i);if(e.length<=2&&(i.length===4||i.length===2)){let n=x(i);return+e>12&&+r<=12?S(n,+r,+e):+r>12&&+e<=12?S(n,+e,+r):t?S(n,+r,+e):S(n,+e,+r)}return null}if(n=f.exec(e),n){let e=b(n[2]);return e?S(x(n[3]),e,+n[1]):null}if(n=p.exec(e),n){let e=b(n[1]);return e?S(x(n[3]),e,+n[2]):null}return null}function ee(e){let t=d.exec(e.trim());if(!t)return null;let n=t[1],r=t[3],i=t[4];return n.length===4||!(i.length===4||i.length===2)?null:+n>12&&+r<=12?`day`:+r>12&&+n<=12?`month`:+n<=12&&+r<=12?`ambiguous`:null}function te(e){return e.some(e=>{let t=(e??``).trim();return t!==``&&(_(t)!==null||C(t)!==null)})}function ne(e){let t=e=>e.type===`date`?`Date`:e.type===`number`?e.format===`year`?`Year`:`Amount`:`Description`,n={},r={};e.forEach(e=>{let r=t(e);n[r]=(n[r]||0)+1}),e.forEach(e=>{let i=t(e);r[i]=(r[i]||0)+1,e.name=n[i]>1?`${i} ${r[i]}`:i})}var re=/\b(year|yr|vintage|cohort)\b/i,w=/\b(amount|amt|balance|bal|price|cost|fee|fees|charge|paid|payment|debit|credit|total|premium|loss|salary|wage|income|expense|revenue|usd|gbp|eur|cad)\b|[$£€]/i,T=/\b(id|no|num|number|account|acct|code|zip|postal|phone|fax|ssn|ein|tin|invoice|inv|ref|reference|sku|upc|isbn|order|customer|cust|member|policy|claim|seq)\b/i,E=/(?<![a-z])(ratio|rate|roe|roa|coc|lr|elr|plr|margin|yield|return|growth|retention|cede|ceded|discount|apr|apy|coupon|util|utilization|share|pct|percent|frequency)(?![a-z])/i;function D(e,t,n){let r=t.filter(e=>e!==null);if(r.every(e=>Number.isInteger(e))&&r.length)return re.test(e)||r.every(e=>e>=1800&&e<=2100)?{format:`year`,dec:0}:T.test(e)&&!w.test(e)?{format:`plain`,dec:0}:w.test(e)?{format:`float`,dec:2}:{format:`int`,dec:0};let i=0,a=0,o=1/0,s=0;for(let e of r){if(e===0)continue;let t=Math.abs(e);i++,t>a&&(a=t),t<o&&(o=t),s+=t}if(!i)return{format:`float`,dec:Math.min(n,6)};if(E.test(e)&&a<=2)return{format:`pct`,dec:Math.max(1,Math.min(4,n-2))};if(w.test(e)||n<=2&&a<1e5)return{format:`float`,dec:2};if(a/o>1e6)return{format:`eng`,dec:0};let c=s/i;return{format:`float`,dec:Math.max(0,Math.min(n,3-Math.floor(Math.log10(c)),6))}}var O={"-9":`n`,"-6":`µ`,"-3":`m`,0:``,3:`k`,6:`M`,9:`G`,12:`T`};function k(e){if(e===0)return`0`;let t=Math.abs(e),n=Math.floor(Math.log10(t)/3)*3;n=Math.max(-9,Math.min(12,n));let r=t/10**n;return(e<0?`-`:``)+Number(r.toPrecision(3))+O[n]}function A(e,t){if(e<=t)return Array.from({length:e},(e,t)=>t);let n=Array(t),r=e/t;for(let e=0;e<t;e++)n[e]=Math.floor(e*r);return n}var j=2048,M=/^-?0\d/;function N(e,t){let n=A(t.length,j);return e.map((e,r)=>{let i=!0,a=!0,o=!1,s=!1,c=0;for(let e of n){let n=(t[e][r]??``).trim();if(!(n===``||g(n))&&(c++,i&&(_(n)===null?i=!1:(!o&&M.test(n)&&(o=!0),!s&&y(n)&&(s=!0))),a&&C(n,!1)===null&&(a=!1),o||s||!i&&!a))break}if(c===0||o||s)return s?{name:e,type:`text`,align:`right`,values:null}:{name:e,type:`text`,values:null};if(i){let n=Array(t.length).fill(null),i=0;for(let e=0;e<t.length;e++){let a=(t[e][r]??``).trim();if(a===``||g(a))continue;let o=_(a);o&&(n[e]=o.v,o.dec>i&&(i=o.dec))}let a=D(e,n,i);return{name:e,type:`number`,format:a.format,dec:a.dec,values:n}}if(a){let n=!1,i=!1,a=!1,o=!1,s=Array(t.length).fill(null);for(let e=0;e<t.length;e++){let c=(t[e][r]??``).trim();if(c===``||g(c))continue;let l=C(c,!1);l&&(s[e]=l.t,i||=l.hasTime);let u=ee(c);u===`day`?(n=!0,o=!0):u===`month`?o=!0:u===`ambiguous`&&(a=!0)}if(n){s=Array(t.length).fill(null);for(let e=0;e<t.length;e++){let n=(t[e][r]??``).trim();if(n===``||g(n))continue;let i=C(n,!0);i&&(s[e]=i.t)}}return{name:e,type:`date`,hasTime:i,ambiguousOrder:a&&!o,values:s}}return{name:e,type:`text`,values:null}})}function P(e,t=null){let r,a,o=null,l;if(s(e)){if({headers:r,rows:a,aligns:o}=c(e),l=t===!1,l&&(a=[r,...a],r=r.map((e,t)=>`col${t+1}`)),!a.length)throw Error(`Markdown table has no data rows.`)}else{let o=i(e,n(e));if(o.length<2)throw Error(`Need a header row and at least one data row.`);l=t===null?te(o[0]):!t,r=l?o[0].map((e,t)=>`col${t+1}`):o[0].map((e,t)=>e.trim()||`col${t+1}`),a=(l?o:o.slice(1)).map(e=>{if(e.length===r.length)return e;let t=e.slice(0,r.length);for(;t.length<r.length;)t.push(``);return t})}let u=N(r,a);return l&&ne(u),o&&u.forEach((e,t)=>{o[t]&&(e.align=o[t])}),{headers:u.map(e=>e.name),rows:a,cols:u,headerless:l}}var F=new Map;function I(e){let t=F.get(e);return t||(t=new Intl.NumberFormat(`en-US`,{minimumFractionDigits:e,maximumFractionDigits:e}),F.set(e,t)),t}function L(e){if(e==null||e===``)return null;if(e===`year`||e===`eng`)return{kind:e};let t=/^(,)?(?:\.(\d+))?([fd%es])$/.exec(e);if(!t)throw Error(`CsvGrid: unrecognized format spec '${e}'`);return{kind:t[3],comma:!!t[1],dec:t[2]===void 0?null:+t[2]}}var R=[[0xe8d4a51000,`T`],[1e9,`G`],[1e6,`M`],[1e3,`k`],[1,``],[.001,`m`],[1e-6,`µ`],[1e-9,`n`]];function z(e,t){switch(t.kind){case`year`:return String(e);case`eng`:return k(e);case`d`:{let n=Math.round(e);return t.comma?I(0).format(n):String(n)}case`f`:{let n=t.dec??2;return t.comma?I(n).format(e):e.toFixed(n)}case`%`:{let n=t.dec??0,r=e*100;return(t.comma?I(n).format(r):r.toFixed(n))+`%`}case`e`:return e.toExponential(t.dec??2);case`s`:{if(t.dec===null||t.dec===void 0)return k(e);if(e===0)return 0 .toFixed(t.dec);let n=Math.abs(e);for(let[r,i]of R)if(n>=r)return(e/r).toFixed(t.dec)+i;return(e/1e-9).toFixed(t.dec)+`n`}}}function ie(e){return[...e].map(e=>({l:`left`,r:`right`,c:`center`})[e]??null)}function B(e,t,n,r=`auto`){if(e=(e??``).trim(),e===``)return``;if(r===`raw`)return e;if(t.type===`number`){let r=t.values[n];return r===null?g(e)?``:e:t.fmt?z(r,t.fmt):t.format===`year`||t.format===`plain`?String(r):t.format===`eng`?k(r):t.format===`pct`?I(t.dec).format(r*100)+`%`:I(t.dec).format(r)}if(t.type===`date`){let r=t.values[n];if(r===null)return g(e)?``:e;let i=new Date(r),a=e=>String(e).padStart(2,`0`),o=`${i.getFullYear()}-${a(i.getMonth()+1)}-${a(i.getDate())}`;return t.hasTime&&(o+=` ${a(i.getHours())}:${a(i.getMinutes())}`),o}return e}function V(e,t){let n=e=>(e=(e??``)+``,/[",\r\n]/.test(e)?`"`+e.replace(/"/g,`""`)+`"`:e),r=e=>e.map(n).join(`,`),i=[r(e)];for(let e of t)i.push(r(e));return i.join(`\r
|
|
4
|
-
`)}function H(e,t,n=[]){let r=e=>((e??``)+``).replace(/\|/g,`\\|`).replace(/\s*\r?\n\s*/g,` `),i=e=>e===`right`?`---:`:e===`center`?`:--:`:e===`left`?`:---`:`---`,a=e=>`| `+e.map(r).join(` | `)+` |`,o=`|`+e.map((e,t)=>i(n[t])).join(`|`)+`|`,s=[a(e),o];for(let e of t)s.push(a(e));return s.join(`
|
|
5
|
-
`)}function U(e,t){if(!Array.isArray(e))throw Error(`CsvGrid: records must be an array.`);let n=e=>e==null||typeof e==`number`&&Number.isNaN(e)?``:String(e),r,i;if(e.length&&Array.isArray(e[0])){if(!t)throw Error(`CsvGrid: columns are required with array-of-arrays records.`);r=t.map(String),i=e.map(e=>r.map((t,r)=>n(e[r])))}else r=(t??Object.keys(e[0]??{})).map(String),i=e.map(e=>r.map(t=>n(e[t])));let a=N(r,i);return{headers:r,rows:i,cols:a,headerless:!1}}function W(e){let t=[];for(let n of e.trim().split(/\s+/)){if(!n)continue;let e={kind:`fuzzy`,negate:!1};n.startsWith(`!`)&&(e.negate=!0,e.kind=`exact`,n=n.slice(1)),n.startsWith(`'`)&&(e.kind=`exact`,n=n.slice(1)),n.startsWith(`^`)&&(e.kind=`prefix`,n=n.slice(1)),n.endsWith(`$`)&&(e.kind=e.kind===`prefix`?`exact`:`suffix`,n=n.slice(0,-1)),n&&(e.cs=/[A-Z]/.test(n),e.str=e.cs?n:n.toLowerCase(),t.push(e))}return t}var G=/[\s_\-\/\\.,:;()[\]{}"']/;function K(e,t){let n=t.length,r=e.length;if(r===0)return 0;if(r>n)return-1;let i=0,a=-1;for(let o=0;o<n;o++)if(t[o]===e[i]&&(i++,i===r)){a=o;break}if(a<0)return-1;i=r-1;let o=a;for(let n=a;n>=0&&!(t[n]===e[i]&&(o=n,i--,i<0));n--);let s=100-3*(a-o+1-r)-Math.min(o,20);i=0;let c=!1;for(let n=o;n<=a&&i<r;n++)t[n]===e[i]?((n===0||G.test(t[n-1]))&&(s+=8),c&&(s+=4),c=!0,i++):c=!1;return s}function q(e,t,n){let r=e.cs?n:t,i,a=0;switch(e.kind){case`exact`:i=r.includes(e.str);break;case`prefix`:i=r.startsWith(e.str);break;case`suffix`:i=r.endsWith(e.str);break;default:{let t=K(e.str,r);i=t>=0,a=t}}return e.negate&&(i=!i),i?a:-1}function J(e,t,n,r=`equal-risk`){return r===`coverage`?ae(e,t,n):Y(e,t,n)}function Y(e,t,n){let r=(e,t)=>e.length?e[Math.floor(t*(e.length-1))]:0,i=n=>e.map((e,i)=>Math.max(t[i],r(e,n))),a=e=>e.reduce((e,t)=>e+t,0),o=i(1);if(a(o)<=n)return o;if(a(i(0))>=n)return i(0);let s=0,c=1;for(let e=0;e<32;e++){let e=(s+c)/2;a(i(e))<=n?s=e:c=e}return i(s)}function ae(e,t,n){let r=e.map((e,n)=>Math.max(t[n],e.length?e[e.length-1]:0)),i=e=>e.reduce((e,t)=>e+t,0);if(i(r)<=n)return r;if(i(t)>=n)return t.slice();let a=t.slice(),o=n-i(t),s=[];for(let n=0;n<e.length;n++){let r=oe(e[n],t[n]);for(let e=1;e<r.length;e++){let t=r[e].w-r[e-1].w,i=r[e].cells-r[e-1].cells;t>0&&i>0&&s.push({j:n,dw:t,slope:i/t})}}s.sort((e,t)=>t.slope-e.slope);for(let e of s){if(o<=0)break;let t=Math.min(e.dw,o);a[e.j]+=t,o-=t}return a}function oe(e,t){let n=e.length,r=0;for(;r<n&&e[r]<=t;)r++;let i=[{w:t,cells:r}];for(;r<n;){let t=e[r];for(;r<n&&e[r]===t;)r++;i.push({w:t,cells:r})}let a=[];for(let e of i){for(;a.length>=2;){let t=a[a.length-2],n=a[a.length-1];if((n.w-t.w)*(e.cells-t.cells)-(n.cells-t.cells)*(e.w-t.w)>=0)a.pop();else break}a.push(e)}return a}function se(e,t){let n=e.trim();if(!n)return null;if(t.type===`number`||t.type===`date`){let e=t.type===`number`?e=>{let t=_(e);return t?t.v:NaN}:e=>{let t=C(e);return t?t.t:NaN},r=/^(>=|<=|>|<|=)\s*(.+)$/.exec(n);if(r){let n=e(r[2]);if(!isNaN(n)){let e=r[1];return(r,i)=>{let a=t.values[i];if(a===null)return!1;switch(e){case`>`:return a>n;case`>=`:return a>=n;case`<`:return a<n;case`<=`:return a<=n;default:return a===n}}}}if(r=/^(.+?)\.\.(.+)$/.exec(n),r){let n=e(r[1]),i=e(r[2]);if(!isNaN(n)&&!isNaN(i))return(e,r)=>{let a=t.values[r];return a!==null&&a>=n&&a<=i}}}let r=n.toLowerCase();return(e,t)=>e.toLowerCase().includes(r)}function X(e){return e.align?`col-${e.type} align-${e.align}`:`col-${e.type}`}function Z(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}var Q=1e6,ce=2048,le=1e4;function $(e,t){let n=document.createElement(e);return t&&(n.className=t),n}return class n{constructor(e,t,n={}){let r=typeof e==`string`?document.querySelector(e):e;if(!r)throw Error(`CsvGrid: target element not found.`);this.root=r,this.opts={globalSearch:!0,columnFilters:!0,sortable:!0,statusBar:!0,expandButtons:!0,align:null,formats:null,renderCap:2048,eagerCells:262144,worker:!0,headerMode:`auto`,widthMode:`equal-risk`,maxRows:null,height:null,displayMode:`auto`,...n},this.displayMode=this.opts.displayMode===`raw`?`raw`:`auto`,this.fileName=``,this.headers=[],this.rows=[],this.cols=[],this.formatted=[],this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,this.loadGen=0,this.scores=[],this.layout=null,this.expandAll=!1,this.manualWidths=new Map,this.guessedHeaders=!1,this.ambiguousDateCols=[],this.view=[],this.sortCol=null,this.sortDir=1,this.globalFilter=``,this.colFilters=[],this.showAll=!1,this._worker=void 0,this._pending=new Map,this._buildScaffold(),t&&this.setData(t)}_buildScaffold(){let e=this.opts,t=this.root;if(t.classList.add(`csvgrid`),t.replaceChildren(),this.els={},e.globalSearch||e.expandButtons){let n=$(`div`,`csvgrid-toolbar`);if(e.globalSearch){let e=$(`input`,`csvgrid-search`);e.type=`text`,e.placeholder=`fzf search: term 'exact !not ^pre fix$`,e.title=`Space-separated terms AND together. Fuzzy by default; 'exact, !exclude, ^prefix, suffix$. Uppercase = case-sensitive.`,e.addEventListener(`input`,()=>this.setGlobalFilter(e.value)),e.addEventListener(`keydown`,t=>{t.key===`Escape`&&(t.preventDefault(),e.value=``,e.blur(),this.setGlobalFilter(``))}),n.appendChild(e),this.els.search=e}if(e.expandButtons){let e=$(`button`,`csvgrid-btn`);e.type=`button`,e.textContent=`Expand`,e.title=`Expand all columns to their full natural width (table scrolls horizontally)`,e.addEventListener(`click`,()=>this.expand());let t=$(`button`,`csvgrid-btn`);t.type=`button`,t.textContent=`Contract`,t.title=`Back to fitted widths (equal-risk squeeze); also clears any dragged widths`,t.addEventListener(`click`,()=>this.contract()),n.append(e,t)}t.appendChild(n)}let n=$(`div`,`csvgrid-scroll`),r=$(`table`,`csvgrid-table`),i=$(`thead`),a=$(`tbody`);r.append(i,a),n.appendChild(r),t.appendChild(n);let o=$(`div`,`csvgrid-capnote csvgrid-hidden`),s=$(`button`,`csvgrid-btn`);s.type=`button`,s.addEventListener(`click`,()=>{this.showAll=!0,this.renderBody(),this.renderStatus()}),o.appendChild(s),t.appendChild(o);let c=$(`div`,`csvgrid-error csvgrid-hidden`);t.appendChild(c);let l=null;e.statusBar===!0?(l=$(`div`,`csvgrid-status`),t.appendChild(l)):e.statusBar&&(l=e.statusBar),Object.assign(this.els,{table:r,head:i,body:a,scroll:n,capNote:o,showAllBtn:s,error:c,status:l}),r.addEventListener(`mouseover`,e=>{let t=e.target.closest(`td, th`);t&&!t.title&&t.scrollWidth>t.clientWidth&&(t.title=t.textContent)})}setData(e){let t=++this.loadGen,n=new Promise((n,r)=>{this._resolveData(e,t).then(({d:e,name:r})=>{t===this.loadGen&&(this._install(e,r),n())},e=>{t===this.loadGen&&(this._showError(e.message||String(e)),r(e))})});return n.catch(()=>{}),n}async _resolveData(e,t){if(!e||typeof e!=`object`)throw Error(`CsvGrid: data must be {csv}, {records[, columns]}, or {url}.`);if(this._headerMode=e.headerMode??this.opts.headerMode,e.url!==void 0){let n=String(e.url),r=e.name??decodeURIComponent(n.split(`/`).pop()||n),i=await fetch(n);if(!i.ok)throw Error(`HTTP ${i.status}`);return{d:await this._parse(await i.text(),t,r),name:r}}if(e.csv!==void 0){let n=e.name??``;return{d:await this._parse(e.csv,t,n),name:n}}if(e.records!==void 0)return{d:U(e.records,e.columns),name:e.name??``};throw Error(`CsvGrid: data must be {csv}, {records[, columns]}, or {url}.`)}_parse(e,n,r){if(e=t(e),!e.trim())throw Error(`No data found.`);let i=this._headerMode===`first-row`?!0:this._headerMode===`headerless`?!1:null,a=this.opts.worker!==!1&&e.length>=Q?this._getWorker():null;return a?(this._setStatus(`parsing ${r||`data`} (${(e.length/1e6).toFixed(1)} MB)…`),new Promise((t,r)=>{this._pending.set(n,{resolve:t,reject:r}),a.postMessage({gen:n,text:e,headerOverride:i})})):P(e,i)}_getWorker(){if(this._worker===void 0){this._worker=null;try{let t=typeof this.opts.worker==`string`?new Worker(this.opts.worker):new Worker(new URL(``+new URL(`csv-grid.worker.js`,e).href,``+e),{type:`module`});t.onmessage=e=>{let{gen:t,result:n,error:r}=e.data,i=this._pending.get(t);i&&(this._pending.delete(t),r?i.reject(Error(r)):i.resolve(n))},t.onerror=()=>{let e=[...this._pending.values()];this._pending.clear();for(let t of e)t.reject(Error(`Background parse failed.`))},this._worker=t}catch{}}return this._worker}_install(e,t){let{rows:n,cols:r}=e;if(this.opts.align){let e=ie(this.opts.align);r.forEach((t,n)=>{e[n]&&(t.align=e[n])})}if(this.opts.formats&&r.forEach((e,t)=>{e.fmt=L(this.opts.formats[t])}),this.fileName=t||``,this.guessedHeaders=e.headerless,this.ambiguousDateCols=r.filter(e=>e.ambiguousOrder).map(e=>e.name),this.headers=e.headers,this.rows=n,this.cols=r,this.formatted=Array(n.length),this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,n.length*r.length<=this.opts.eagerCells){for(let e=0;e<n.length;e++)this.getFormattedRow(e);this.searchRaw=this.formatted.map((e,t)=>e.join(` `)+` `+n[t].join(` `)),this.searchLow=this.searchRaw.map(e=>e.toLowerCase()),this.searchReady=!0}this.sortCol=null,this.sortDir=1,this.globalFilter=``,this.colFilters=Array(r.length).fill(``),this.manualWidths=new Map,this.showAll=!1,this.els.search&&(this.els.search.value=``),this.els.error.classList.add(`csvgrid-hidden`),this.renderHead(),this.layout=this.measureLayout(),this.applyLayout(),this.refresh(),this._applyHeight()}_applyHeight(){let e=this.opts;if(e.height){this.els.scroll.style.maxHeight=e.height;return}if(e.maxRows&&this.els.body.rows.length){let t=this.els.head.offsetHeight,n=this.els.body.rows[0].offsetHeight;this.els.scroll.style.maxHeight=Math.ceil(t+n*e.maxRows+2)+`px`}}_showError(e){this.els.error.textContent=e,this.els.error.classList.remove(`csvgrid-hidden`)}_setStatus(e){this.els.status&&(this.els.status.textContent=e)}destroy(){this.loadGen++,this._pending.clear(),this._worker&&=(this._worker.terminate(),null),this.root.classList.remove(`csvgrid`),this.root.replaceChildren()}setGlobalFilter(e){this.globalFilter=e,this.refresh()}clearFilters(){this.globalFilter=``,this.colFilters=this.colFilters.map(()=>``),this.els.search&&(this.els.search.value=``),this.renderHead(),this.refresh()}expand(){this.expandAll=!0,this.applyLayout()}contract(){this.expandAll=!1,this.manualWidths.clear(),this.applyLayout()}export({scope:e=`view`,format:t=`csv`,values:n=`raw`}={}){let r=e===`all`?this.rows.map((e,t)=>t):this.view,i=n===`formatted`&&r.length<=this.opts.renderCap,a=r.map(e=>i?this.getFormattedRow(e):this.cols.map((t,n)=>this.rows[e][n]??``));if(t===`md`){let e=this.cols.map(e=>e.align||(e.type===`number`?`right`:e.type===`date`?`center`:`left`));return H(this.headers,a,e)}return V(this.headers,a)}setWidthMode(e){this.opts.widthMode=e===`coverage`?`coverage`:`equal-risk`,this.applyLayout()}setDisplayMode(e){if(e=e===`raw`?`raw`:`auto`,e===this.displayMode||!this.cols.length){this.displayMode=e;return}if(this.displayMode=e,this.formatted=Array(this.rows.length),this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,this.rows.length*this.cols.length<=this.opts.eagerCells){for(let e=0;e<this.rows.length;e++)this.getFormattedRow(e);this.searchRaw=this.formatted.map((e,t)=>e.join(` `)+` `+this.rows[t].join(` `)),this.searchLow=this.searchRaw.map(e=>e.toLowerCase()),this.searchReady=!0}this.layout=this.measureLayout(),this.applyLayout(),this.refresh()}measureLayout(){let e=(n._canvas||=document.createElement(`canvas`)).getContext(`2d`),t=getComputedStyle(this.els.table),r=`${t.fontSize} ${t.fontFamily}`,i=A(this.rows.length,ce),a=[],o=[];for(let t=0;t<this.cols.length;t++){e.font=`bold ${r}`,o.push(Math.max(50,Math.ceil(e.measureText(this.cols[t].name).width)+14+18)),e.font=r;let n=[];for(let r of i){let i=this.getFormattedRow(r)[t];i!==``&&n.push(Math.ceil(e.measureText(i).width)+18)}n.sort((e,t)=>e-t),a.push(n)}return{arrays:a,floors:o}}startColResize(e,t){e.preventDefault(),e.stopPropagation();let n=this.els.table,r=n.querySelectorAll(`colgroup col`)[t];if(!r)return;let i=e.clientX,a=parseFloat(r.style.width);document.body.classList.add(`csvgrid-resizing`);let o=()=>{let e=0;n.querySelectorAll(`colgroup col`).forEach(t=>{e+=parseFloat(t.style.width)}),n.style.width=e+`px`},s=e=>{let n=Math.max(24,Math.round(a+e.clientX-i));this.manualWidths.set(t,n),r.style.width=n+`px`,o()},c=()=>{document.body.classList.remove(`csvgrid-resizing`),document.removeEventListener(`mousemove`,s),document.removeEventListener(`mouseup`,c)};document.addEventListener(`mousemove`,s),document.addEventListener(`mouseup`,c)}fitColumn(e){let{arrays:t,floors:n}=this.layout,r=Math.max(n[e],t[e].length?t[e][t[e].length-1]:0);this.manualWidths.set(e,r),this.applyLayout()}applyLayout(){if(!this.layout)return;let e=this.els.table,t=this.expandAll?1/0:e.parentElement.clientWidth;if(!t)return;let n=J(this.layout.arrays,this.layout.floors,t,this.opts.widthMode);for(let[e,t]of this.manualWidths)e<n.length&&(n[e]=t);let r=e.querySelector(`colgroup`);r&&r.remove(),r=document.createElement(`colgroup`);for(let e of n){let t=document.createElement(`col`);t.style.width=e+`px`,r.appendChild(t)}e.prepend(r),e.style.tableLayout=`fixed`,e.style.width=n.reduce((e,t)=>e+t,0)+`px`}getFormattedRow(e){let t=this.formatted[e];return t||(t=this.cols.map((t,n)=>B(this.rows[e][n],t,e,this.displayMode)),this.formatted[e]=t),t}buildSearchIndexChunked(){let e=this.loadGen,t=this.rows.length,n=Array(t),r=Array(t),i=0;this.indexing=0;let a=()=>{if(e!==this.loadGen)return;let o=Math.min(t,i+le);for(;i<o;i++){let e=this.getFormattedRow(i).join(` `)+` `+this.rows[i].join(` `);n[i]=e,r[i]=e.toLowerCase()}i<t?(this.indexing=i/t,this.renderStatus(),setTimeout(a,0)):(this.searchRaw=n,this.searchLow=r,this.searchReady=!0,this.indexing=null,this.refresh())};a()}rebuildView(){let{rows:e,cols:t}=this,n=W(this.globalFilter);n.length&&!this.searchReady&&(this.indexing===null&&this.buildSearchIndexChunked(),n=[]);let r=n.some(e=>e.kind===`fuzzy`&&!e.negate),i=this.colFilters.map((e,n)=>se(e||``,t[n])),a=i.some(e=>e)||n.length,o=[];this.scores=[];for(let t=0;t<e.length;t++){let r=0;if(a){let a=!0;for(let e of n){let n=q(e,this.searchLow[t],this.searchRaw[t]);if(n<0){a=!1;break}r+=n}if(a){for(let n=0;n<i.length;n++)if(i[n]&&!i[n](e[t][n]??``,t)){a=!1;break}}if(!a)continue}this.scores[t]=r,o.push(t)}let s=this.sortCol;if(s===null&&r)o.sort((e,t)=>this.scores[t]-this.scores[e]||e-t);else if(s!==null){let e=this.cols[s],t=this.sortDir;if(e.type===`text`){let e=new Intl.Collator(`en`,{sensitivity:`base`,numeric:!0});o.sort((n,r)=>{let i=(this.rows[n][s]??``).trim(),a=(this.rows[r][s]??``).trim();return i===``||a===``?i===a?0:i===``?1:-1:t*e.compare(i,a)})}else o.sort((n,r)=>{let i=e.values[n],a=e.values[r];return i===null||a===null?i===a?0:i===null?1:-1:t*(i-a)})}this.view=o}renderHead(){let{cols:e}=this,t=this.els.head;t.innerHTML=``;let n=document.createElement(`tr`);if(e.forEach((e,t)=>{let r=document.createElement(`th`);r.className=X(e),this.opts.sortable?(r.innerHTML=`<span class="sort-arrow">${this.sortCol===t?this.sortDir===1?`▲`:`▼`:``}</span>${Z(e.name)}`,r.title=`${e.name} (${e.type}) — click to sort`,r.addEventListener(`click`,()=>this.onSort(t))):(r.innerHTML=`<span class="sort-arrow"></span>${Z(e.name)}`,r.title=`${e.name} (${e.type})`,r.classList.add(`csvgrid-nosort`));let i=document.createElement(`span`);i.className=`col-resizer`,i.title=`Drag to resize — double-click to fit content`,i.addEventListener(`mousedown`,e=>this.startColResize(e,t)),i.addEventListener(`dblclick`,e=>{e.stopPropagation(),this.fitColumn(t)}),i.addEventListener(`click`,e=>e.stopPropagation()),r.appendChild(i),n.appendChild(r)}),t.appendChild(n),!this.opts.columnFilters)return;let r=document.createElement(`tr`);r.className=`filter-row`,e.forEach((e,t)=>{let n=document.createElement(`th`),i=document.createElement(`input`);i.type=`text`,i.className=`csvgrid-filter`,i.placeholder=e.type===`text`?`filter`:`filter, >, .. `,i.value=this.colFilters[t]||``,i.addEventListener(`input`,()=>{this.colFilters[t]=i.value,i.classList.toggle(`active-filter`,i.value.trim()!==``),this.refresh()}),i.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),i.value=``,this.colFilters[t]=``,i.classList.remove(`active-filter`),i.blur(),this.refresh())}),n.appendChild(i),r.appendChild(n)}),t.appendChild(r)}renderBody(){let{cols:e,view:t}=this,n=this.showAll?t.length:Math.min(t.length,this.opts.renderCap),r=[];for(let i=0;i<n;i++){let n=t[i],a=this.getFormattedRow(n),o=e.map((e,t)=>{let n=a[t];return n===``?`<td class="${X(e)} blank">·</td>`:`<td class="${X(e)}">${Z(n)}</td>`});r.push(`<tr>${o.join(``)}</tr>`)}this.els.body.innerHTML=r.join(``);let i=this.els.capNote;t.length>n?(i.classList.remove(`csvgrid-hidden`),this.els.showAllBtn.textContent=`Showing first ${n.toLocaleString()} of ${t.length.toLocaleString()} rows — show all`):i.classList.add(`csvgrid-hidden`)}renderStatus(){if(!this.els.status)return;let e=e=>e.toLocaleString(),t=this.rows.length,n=this.view.length,r=this.showAll?n:Math.min(n,this.opts.renderCap),i=this.fileName?this.fileName+` — `:``;i+=n===t?`${e(t)} rows`:`${e(n)} of ${e(t)} rows`,i+=` × ${this.cols.length} cols`,r<n&&(i+=` — showing rows 1–${e(r)}`),this.guessedHeaders&&(i+=` (headers guessed)`),this.indexing!==null&&(i+=` — indexing search ${Math.round(this.indexing*100)}%`),this.els.status.textContent=i}refresh(){this.rebuildView(),this.renderBody(),this.renderStatus()}onSort(e){this.sortCol===e?this.sortDir===1?this.sortDir=-1:(this.sortCol=null,this.sortDir=1):(this.sortCol=e,this.sortDir=1),this.renderHead(),this.refresh()}}})();
|
|
6
|
-
//# sourceMappingURL=csv-grid.iife.js.map
|
|
File without changes
|