@iyulab/flex-table 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.md +20 -1
- package/dist/array/index.js +5 -5
- package/dist/array/use-array-source.hooks.test.d.ts +1 -0
- package/dist/export/export.d.ts +2 -2
- package/dist/export/xlsx-writer.d.ts +1 -1
- package/dist/flex-table.d.ts +1 -1
- package/dist/odata/index.js +1 -1
- package/dist/odata/use-odata-source.hooks.test.d.ts +1 -0
- package/dist/{use-odata-source--wXFYeqA.js → use-odata-source-6fVz-CLu.js} +15 -9
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,70 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.34.0] - 2026-09-08
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Three type errors in the export/import path that the toolchain was hiding.** This package
|
|
8
|
+
pinned TypeScript at `~5.7.0` — a value carried over from its initial commit and never
|
|
9
|
+
revisited — while every sibling package compiled with `^5.9.3`. Aligning the pin surfaced
|
|
10
|
+
errors that had been present but unreported: `Uint8Array` widens to
|
|
11
|
+
`Uint8Array<ArrayBufferLike>`, which the DOM lib rejects wherever an `ArrayBuffer` is
|
|
12
|
+
required (`BlobPart`, `BufferSource`). The XLSX writer/reader and the download helper now
|
|
13
|
+
carry the concrete `Uint8Array<ArrayBuffer>` through, so a `Blob`/`File` built from an export
|
|
14
|
+
typechecks. Runtime behaviour is unchanged — the values were always backed by a real
|
|
15
|
+
`ArrayBuffer`.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **`exportToString()` and `downloadFile()` now declare `Uint8Array<ArrayBuffer>`** instead of
|
|
20
|
+
the wider `Uint8Array`. Callers that pass the result straight to `Blob`/`File` — the
|
|
21
|
+
documented use — are unaffected and now typecheck without a cast. Only a caller that
|
|
22
|
+
deliberately supplied a `SharedArrayBuffer`-backed view is affected, and such a value could
|
|
23
|
+
never have reached `Blob` at runtime anyway.
|
|
24
|
+
|
|
25
|
+
- **`typescript` devDependency `~5.7.0` → `^5.9.3`**, matching every sibling package.
|
|
26
|
+
|
|
27
|
+
## [0.33.0] - 2026-09-08
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **A page could outlive the result set that holds it, leaving an empty table next
|
|
32
|
+
to a non-zero total and a pager pointing at a page that no longer exists.**
|
|
33
|
+
Three axes change the size of a result set, and only two of them moved the page.
|
|
34
|
+
|
|
35
|
+
- **`useODataSource`: changing `fixedFilter` now resets the page to `0`**, the
|
|
36
|
+
same way `setSearch` and `onSortChange` already did. Narrowing a filter while
|
|
37
|
+
on page 5 previously kept the old `$skip` and asked for a range the new set
|
|
38
|
+
does not have. Comparison is by value, so passing a fresh object literal on
|
|
39
|
+
every render does not reset anything on its own, and the reset does not run on
|
|
40
|
+
mount, so it never overrides `initialPage`.
|
|
41
|
+
- **`useODataSource`: a response reporting fewer rows than the current page needs
|
|
42
|
+
falls back to the last page that exists.** A result set can shrink without
|
|
43
|
+
being asked. Reaching that state costs one extra request, only in that case;
|
|
44
|
+
the page only ever moves down, so it cannot loop.
|
|
45
|
+
- **`useArraySource`: paging is clamped to the array passed in.** Shrinking the
|
|
46
|
+
array — the in-memory equivalent of narrowing a filter — falls back to the last
|
|
47
|
+
page that exists instead of rendering nothing.
|
|
48
|
+
|
|
49
|
+
Shrinking clamps rather than resets on purpose: an array or a total can change on
|
|
50
|
+
a routine refresh, and resetting there would move the page out from under the
|
|
51
|
+
reader. The dividing question is whether the change was asked for.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- **`computeArrayView` returns the last page that exists when given a page past the
|
|
56
|
+
end**, instead of an empty array. It is exported, so a direct caller had the same
|
|
57
|
+
defect; returning nothing for an out-of-range page was never the intended
|
|
58
|
+
contract.
|
|
59
|
+
|
|
60
|
+
### Internal
|
|
61
|
+
|
|
62
|
+
- First hook tests for this package — everything here was pure functions until now,
|
|
63
|
+
so contracts the README states had no executed evidence behind them. They cover
|
|
64
|
+
the reset and clamp on both hooks, request abort on re-request and on unmount,
|
|
65
|
+
`onUnauthorized` firing on `401`/`403` but not on `500`, initial state being read
|
|
66
|
+
on first render only, and `refresh` being a no-op on the array source.
|
|
67
|
+
|
|
3
68
|
## [0.32.0] - 2026-09-08
|
|
4
69
|
|
|
5
70
|
### Added
|
package/README.md
CHANGED
|
@@ -587,13 +587,26 @@ const source = useODataSource('/api/orders', {
|
|
|
587
587
|
| `initialPage` | `0` | Initial page, **zero-based** — the same axis as the returned `page`/`setPage`, and `$skip` is `page * pageSize` |
|
|
588
588
|
| `initialSearch` | `''` | Initial search term |
|
|
589
589
|
| `initialSort` | — | Initial sort as `SortCriteria[]`. Takes precedence over `defaultOrderBy` — it is the shape `onSortChange` hands you, so a stored sort round-trips without re-serializing it |
|
|
590
|
-
| `fixedFilter` | — | Filter always applied in addition to search |
|
|
590
|
+
| `fixedFilter` | — | Filter always applied in addition to search. Changing it resets the page to 0 — see below |
|
|
591
591
|
| `baseUrl` | `window.location.origin` | Override the request origin (proxy/BFF setups) |
|
|
592
592
|
| `fetcher` | global `fetch` | Custom transport — pass a wrapper that injects auth headers |
|
|
593
593
|
| `onUnauthorized` | — | Called on `401`/`403` responses, before the generic error is set |
|
|
594
594
|
|
|
595
595
|
`fetcher`/`onUnauthorized` should be stable references (e.g. wrap in `useCallback`) — they are intentionally excluded from the hook's internal effect dependencies to avoid refetch loops on every render.
|
|
596
596
|
|
|
597
|
+
Changing `fixedFilter` resets the page to `0`, the same way `setSearch` and `onSortChange`
|
|
598
|
+
already do. All three change the size of the result set, so keeping the old `$skip` would
|
|
599
|
+
request a range the new set no longer has — a filter applied while on page 5 would come back
|
|
600
|
+
empty. Comparison is by value, not by reference, so passing a fresh object literal on every
|
|
601
|
+
render (`fixedFilter={{ IsActive: true }}`) does not reset anything on its own. The reset
|
|
602
|
+
does not run on mount, so it never overrides `initialPage`.
|
|
603
|
+
|
|
604
|
+
If a response reports fewer rows than the current page needs, the page falls back to the
|
|
605
|
+
last page that exists — a result set can shrink without you asking (another user deleting
|
|
606
|
+
rows, a `refresh` landing after a change), and holding the old `$skip` would leave an empty
|
|
607
|
+
table next to a non-zero total. Reaching that state costs one extra request, only in that
|
|
608
|
+
case; the page only ever moves down, so it cannot loop.
|
|
609
|
+
|
|
597
610
|
The three `initial*` options are read **on the first render only** (the same contract
|
|
598
611
|
`defaultOrderBy` has always had); use `setPage`/`setSearch` to move afterwards. Reach for
|
|
599
612
|
them when a list has to come back the way the user left it — going to a detail screen and
|
|
@@ -650,6 +663,12 @@ returns the **same shape** as `useODataSource` — `data`/`totalCount`/`loading`
|
|
|
650
663
|
`page`/`setPage`/`sortCriteria`/`onSortChange`/`search`/`setSearch`/`refresh` — so the
|
|
651
664
|
same `<FlexTableReact dataMode="server" ...>` binding code works with either source.
|
|
652
665
|
|
|
666
|
+
Paging is clamped to the data you pass: if the array shrinks below the current page — the
|
|
667
|
+
in-memory equivalent of narrowing a filter, usually `rows.filter(...)` upstream — the hook
|
|
668
|
+
falls back to the last page that exists instead of rendering an empty table. It only ever
|
|
669
|
+
moves the page down, so an array that changes identity on every poll without changing length
|
|
670
|
+
leaves the current page alone.
|
|
671
|
+
|
|
653
672
|
Reach for this when the rows come from a client-side join a server query can't express —
|
|
654
673
|
e.g. a lookup table whose display name lives on a different endpoint than the row itself,
|
|
655
674
|
so search/sort has to run after the join, in memory:
|
package/dist/array/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { t as e } from "../sorting-CjfjRxwL.js";
|
|
2
|
-
import { r as t } from "../use-odata-source
|
|
2
|
+
import { r as t } from "../use-odata-source-6fVz-CLu.js";
|
|
3
3
|
import { useCallback as n, useMemo as r, useState as i } from "react";
|
|
4
4
|
//#region src/array/use-array-source.ts
|
|
5
5
|
function a(t, { search: n, sortCriteria: r, page: i, pageSize: a, columns: o, searchFields: s }) {
|
|
6
6
|
let c = n.trim().toLowerCase(), l = c ? t.filter((e) => (s ? s(e) : Object.values(e)).some((e) => e != null && String(e).toLowerCase().includes(c))) : t, u = l;
|
|
7
7
|
r.length > 0 && (u = e(l, r, o ?? []).map((e) => l[e]));
|
|
8
|
-
let d = u.length, f = i * a;
|
|
8
|
+
let d = u.length, f = d === 0 ? 0 : Math.ceil(d / a) - 1, p = Math.min(Math.max(i, 0), f) * a;
|
|
9
9
|
return {
|
|
10
|
-
data: u.slice(
|
|
10
|
+
data: u.slice(p, p + a),
|
|
11
11
|
totalCount: d
|
|
12
12
|
};
|
|
13
13
|
}
|
|
@@ -37,8 +37,8 @@ function o(e, o = {}) {
|
|
|
37
37
|
s,
|
|
38
38
|
f,
|
|
39
39
|
p
|
|
40
|
-
]);
|
|
41
|
-
return {
|
|
40
|
+
]), E = T === 0 ? 0 : Math.ceil(T / s) - 1;
|
|
41
|
+
return h > E && g(E), {
|
|
42
42
|
data: w,
|
|
43
43
|
totalCount: T,
|
|
44
44
|
loading: !1,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/export/export.d.ts
CHANGED
|
@@ -4,10 +4,10 @@ export type ExportFormat = 'csv' | 'tsv' | 'json' | 'xlsx';
|
|
|
4
4
|
* Export data to the specified format.
|
|
5
5
|
* Returns string for text formats (csv/tsv/json) or Uint8Array for xlsx.
|
|
6
6
|
*/
|
|
7
|
-
export declare function exportData(data: DataRow[], columns: ColumnDefinition[], format: ExportFormat): string | Uint8Array
|
|
7
|
+
export declare function exportData(data: DataRow[], columns: ColumnDefinition[], format: ExportFormat): string | Uint8Array<ArrayBuffer>;
|
|
8
8
|
/**
|
|
9
9
|
* Trigger a file download in the browser.
|
|
10
10
|
*/
|
|
11
|
-
export declare function downloadFile(content: string | Uint8Array
|
|
11
|
+
export declare function downloadFile(content: string | Uint8Array<ArrayBuffer>, filename: string, mimeType: string): void;
|
|
12
12
|
export declare function getExportMimeType(format: ExportFormat): string;
|
|
13
13
|
export declare function getExportExtension(format: ExportFormat): string;
|
|
@@ -7,4 +7,4 @@ import type { ColumnDefinition, DataRow } from '../models/types.js';
|
|
|
7
7
|
/**
|
|
8
8
|
* Build an XLSX file as a Uint8Array.
|
|
9
9
|
*/
|
|
10
|
-
export declare function buildXlsx(data: DataRow[], columns: ColumnDefinition[]): Uint8Array
|
|
10
|
+
export declare function buildXlsx(data: DataRow[], columns: ColumnDefinition[]): Uint8Array<ArrayBuffer>;
|
package/dist/flex-table.d.ts
CHANGED
|
@@ -256,7 +256,7 @@ export declare class FlexTable extends LitElement {
|
|
|
256
256
|
*/
|
|
257
257
|
exportToString(format: ExportFormat, options?: {
|
|
258
258
|
selectionOnly?: boolean;
|
|
259
|
-
}): string | Uint8Array
|
|
259
|
+
}): string | Uint8Array<ArrayBuffer>;
|
|
260
260
|
/**
|
|
261
261
|
* Export table data and trigger file download.
|
|
262
262
|
*/
|
package/dist/odata/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as e, n as t, t as n } from "../use-odata-source
|
|
1
|
+
import { i as e, n as t, t as n } from "../use-odata-source-6fVz-CLu.js";
|
|
2
2
|
export { n as buildSearchExpression, t as parseOrderBy, e as useODataSource };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -19,18 +19,20 @@ function s(s, c = {}) {
|
|
|
19
19
|
initialPage: d,
|
|
20
20
|
initialSearch: f,
|
|
21
21
|
initialSort: p
|
|
22
|
-
}), [O, k] = r(D.page), [A, j] = r(D.sortCriteria), [M, N] = r(D.search), [P, F] = r(0), I
|
|
22
|
+
}), [O, k] = r(D.page), [A, j] = r(D.sortCriteria), [M, N] = r(D.search), [P, F] = r(0), [I, L] = r(v);
|
|
23
|
+
v !== I && (L(v), k(0));
|
|
24
|
+
let R = n(null), z = e(() => {
|
|
23
25
|
F((e) => e + 1);
|
|
24
|
-
}, []),
|
|
26
|
+
}, []), B = e((e) => {
|
|
25
27
|
N(e), k(0);
|
|
26
|
-
}, []),
|
|
28
|
+
}, []), V = e((e) => {
|
|
27
29
|
let t = e.detail?.criteria;
|
|
28
30
|
t && (j(t), k(0));
|
|
29
31
|
}, []);
|
|
30
32
|
return t(() => {
|
|
31
|
-
|
|
33
|
+
R.current?.abort();
|
|
32
34
|
let e = new AbortController();
|
|
33
|
-
|
|
35
|
+
R.current = e, w(!0), E(null);
|
|
34
36
|
let t = A.length > 0 ? A.map((e) => `${e.key} ${e.direction}`).join(", ") : u, n = {
|
|
35
37
|
top: l,
|
|
36
38
|
skip: O * l,
|
|
@@ -53,7 +55,11 @@ function s(s, c = {}) {
|
|
|
53
55
|
}
|
|
54
56
|
return e.json();
|
|
55
57
|
}).then((t) => {
|
|
56
|
-
e.signal.aborted
|
|
58
|
+
if (e.signal.aborted) return;
|
|
59
|
+
let n = t["@odata.count"] ?? 0;
|
|
60
|
+
b(t.value ?? t), S(n), E(null);
|
|
61
|
+
let r = n === 0 ? 0 : Math.ceil(n / l) - 1;
|
|
62
|
+
O > r && k(r);
|
|
57
63
|
}).catch((t) => {
|
|
58
64
|
e.signal.aborted || t.name !== "AbortError" && (E(t.message), b([]), S(0));
|
|
59
65
|
}).finally(() => {
|
|
@@ -77,10 +83,10 @@ function s(s, c = {}) {
|
|
|
77
83
|
page: O,
|
|
78
84
|
setPage: k,
|
|
79
85
|
sortCriteria: A,
|
|
80
|
-
onSortChange:
|
|
86
|
+
onSortChange: V,
|
|
81
87
|
search: M,
|
|
82
|
-
setSearch:
|
|
83
|
-
refresh:
|
|
88
|
+
setSearch: B,
|
|
89
|
+
refresh: z
|
|
84
90
|
};
|
|
85
91
|
}
|
|
86
92
|
function c(e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/flex-table",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "A minimalist, input-centric data grid web component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/flex-table.js",
|
|
@@ -88,6 +88,7 @@
|
|
|
88
88
|
"@lit/react": "^1.0.8",
|
|
89
89
|
"@types/node": "^26.1.1",
|
|
90
90
|
"@types/react": "^19.2.17",
|
|
91
|
+
"@types/react-dom": "^19.2.7",
|
|
91
92
|
"@typescript-eslint/eslint-plugin": "^8.64.0",
|
|
92
93
|
"@typescript-eslint/parser": "^8.64.0",
|
|
93
94
|
"eslint": "^10.1.0",
|
|
@@ -95,8 +96,9 @@
|
|
|
95
96
|
"jsdom": "^29.1.1",
|
|
96
97
|
"loupe": "^3.2.1",
|
|
97
98
|
"react": "^19.2.7",
|
|
99
|
+
"react-dom": "^19.2.8",
|
|
98
100
|
"strip-literal": "^3.1.0",
|
|
99
|
-
"typescript": "
|
|
101
|
+
"typescript": "^5.9.3",
|
|
100
102
|
"vite": "^8.1.4",
|
|
101
103
|
"vitest": "^4.1.10"
|
|
102
104
|
}
|