|
1 | 1 | # @pretable/react |
2 | 2 |
|
| 3 | +## 0.7.0 |
| 4 | + |
| 5 | +### Minor Changes |
| 6 | + |
| 7 | +- Add `defaultSaveFile`, `toCsvBlob` and `buildExportFileName` — the delivery half ([#357](https://github.com/cacheplane/pretable/pull/357)) |
| 8 | + of CSV export. |
| 9 | + |
| 10 | + Blob + `<a download>`, chosen over `showSaveFilePicker` for one decisive reason: |
| 11 | + `<a download>` has no user-activation requirement, so it still works after an |
| 12 | + `await`, while the picker is transient-activation-gated and throws |
| 13 | + `SecurityError` once any async work has happened. Chrome's own guidance is to |
| 14 | + open the picker _before_ doing the work, which would make the user name a file |
| 15 | + before knowing whether the export succeeded. |
| 16 | + |
| 17 | + `buildExportFileName` is pure and sanitizes for the union of all three |
| 18 | + platforms, because everything the browser would otherwise do to a name is lossy, |
| 19 | + silent, and differs by OS — Chromium replaces `:` with `_` on _every_ platform, |
| 20 | + strips leading dots, and diverges between Windows and POSIX on trailing dots. |
| 21 | + |
| 22 | + An incomplete export is marked in the **filename** (`-PARTIAL`). The signal |
| 23 | + cannot go in the file: RFC 4180 has no comment syntax, so a marker row is a data |
| 24 | + row. A filename travels with the artifact when it is emailed onward and costs |
| 25 | + the bytes nothing. |
| 26 | + |
| 27 | +- Add `serializeCsv`, the pure CSV serializer behind file export. ([#357](https://github.com/cacheplane/pretable/pull/357)) |
| 28 | + |
| 29 | + It reuses the clipboard's value pipeline — the same `formatDataCellValue`, |
| 30 | + `formatAggregateValue` and number-formatter registry — so a CSV agrees with the |
| 31 | + screen, and it resolves columns against the **drawn** order rather than the |
| 32 | + `columns` prop, so reordering and pinning are reflected in the file. |
| 33 | + |
| 34 | + Two decisions worth knowing: |
| 35 | + |
| 36 | + - **Formula escaping is on by default and vouches on the RUNTIME VALUE**, not on |
| 37 | + the leading character and not on `column.type`. Escaping from the first |
| 38 | + character corrupts negative numbers — a shipped bug in Jira (`-1000` exported |
| 39 | + as `'-1000` across 9.9.0–9.12.2), in MUI X today, and in CsvHelper. Gating on |
| 40 | + the declared type instead has the opposite failure: `PretableRow` is |
| 41 | + `Record<string, unknown>`, so a string from an API sits happily in a |
| 42 | + `type: "number"` column and its formula ships unescaped. Exempting genuine |
| 43 | + numbers, bigints, booleans and Dates by their JavaScript type keeps the |
| 44 | + anti-Jira property while closing that hole. |
| 45 | + - **The file reports WHY it is incomplete, not merely that it is.** `omissions` |
| 46 | + is a discriminated union — `unloaded-rows` carries the scope that proved it, |
| 47 | + `collapsed-groups` carries the expansion override count — and `complete` is |
| 48 | + derived from it rather than maintained beside it. A boolean was the wrong |
| 49 | + shape: "is this complete" is an open question, and the flag grew a term per |
| 50 | + review round. A union closes it differently — a new reason is a new variant, |
| 51 | + so an exhaustive consumer gets a compile error rather than a silently wrong |
| 52 | + `true`. The shape is borrowed from `@hashbrownai/core`'s frame union. The marker is |
| 53 | + deliberately not written into the CSV: RFC 4180 has no comment syntax, so a |
| 54 | + marker row is a data row, and trading a silent short file for a silently |
| 55 | + corrupted one is not an improvement. |
| 56 | + |
| 57 | + `scope` is a **required** argument, not an optional one. Defaulting it to |
| 58 | + `"all"` would have made the honesty reporting opt-in: a caller who simply forgot |
| 59 | + it would get a confidently-labelled complete file over a partial window, which |
| 60 | + is the behaviour this exists to refuse. |
| 61 | + |
| 62 | +### Patch Changes |
| 63 | + |
| 64 | +- Row height estimates now read the row box from CSS instead of inferring it. ([#363](https://github.com/cacheplane/pretable/pull/363)) |
| 65 | + |
| 66 | + `getThemeBoxMetrics()` resolves line height, cell padding and rule width off a |
| 67 | + rendered cell and threads them to the estimator. Wrapped text is measured |
| 68 | + against `columnWidth − 2 × paddingX` — the text box — rather than the full |
| 69 | + column, which had been fitting more characters onto a line than a cell can |
| 70 | + hold. Padding is per-theme and per-density (Excel 6/8/12px, Material 16px), so |
| 71 | + on a 320px column this was worth up to 10% of the line. |
| 72 | + |
| 73 | + The least-squares fit that had been learning "line height" and "chrome" from |
| 74 | + measured rows is removed. It was inferring two numbers the browser reports |
| 75 | + directly, and it had been absorbing the padding error rather than modelling |
| 76 | + anything. The learned floor — what a custom `render` prop contributes, which |
| 77 | + nothing else can observe — is kept. |
| 78 | + |
| 79 | + Measured against 48 rows captured from a real Chromium session: line-count |
| 80 | + prediction 37/48 → 47/48, mean height error 8.69px → 3.50px. |
| 81 | + |
| 82 | + An unthemed grid is unchanged: the fallbacks compute to exactly the previous |
| 83 | + constants. |
| 84 | + |
| 85 | +- Updated dependencies []: |
| 86 | + - @pretable/core@0.7.0 |
| 87 | + - @pretable/ui@0.7.0 |
| 88 | + |
3 | 89 | ## 0.6.2 |
4 | 90 |
|
5 | 91 | ### Patch Changes |
|
0 commit comments