@viliha/vui-ui 1.14.1 → 1.14.2
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 +16 -0
- package/package.json +1 -1
- package/src/record-view.tsx +26 -8
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ backward-compatible features, **major** for breaking changes.
|
|
|
7
7
|
|
|
8
8
|
To upgrade, see [Upgrading](./AGENT.md#upgrading) in the agent guide.
|
|
9
9
|
|
|
10
|
+
## 1.14.2 — 2026-07-26
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Consistent loading feedback in `fetcher` mode.** A cache hit is served from
|
|
15
|
+
the in-memory cache (no server round-trip), but the loading shimmer now stays
|
|
16
|
+
up for a short minimum (300ms) so a cached load looks the same as a real
|
|
17
|
+
fetch — no confusing "loading but blank" flash on tab switch. Real fetches
|
|
18
|
+
longer than the minimum are unaffected; background (post-mutation) refetches
|
|
19
|
+
stay silent.
|
|
20
|
+
|
|
21
|
+
Note: the `fetcher` cache (added in 1.14.0) stores each page's data in a
|
|
22
|
+
module-scoped `Map` in JS memory, so switching tabs serves from memory and does
|
|
23
|
+
**not** re-hit the server. If you still see a refetch, you're on a build older
|
|
24
|
+
than 1.14.0 (the demo before that had no cache).
|
|
25
|
+
|
|
10
26
|
## 1.14.1 — 2026-07-26
|
|
11
27
|
|
|
12
28
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viliha/vui-ui",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.2",
|
|
4
4
|
"description": "Vui UI — a clean, token-driven React admin/CRM component library built on Tailwind CSS v4, shadcn-style patterns, and Radix Icons. Ships as TypeScript source (Just-in-Time), compiled by the consuming app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Suman Bonakurthi",
|
package/src/record-view.tsx
CHANGED
|
@@ -359,6 +359,11 @@ function resolveOptions<T>(
|
|
|
359
359
|
type RvCacheEntry = { rows: unknown[]; total: number; at: number };
|
|
360
360
|
const RV_CACHE = new Map<string, Map<string, RvCacheEntry>>();
|
|
361
361
|
|
|
362
|
+
// Minimum time the loading shimmer stays up per `fetcher` load, so a cache hit
|
|
363
|
+
// (instant, from memory) shows the same animation as a real fetch — consistent
|
|
364
|
+
// feedback instead of a blank flash. Real fetches longer than this are unaffected.
|
|
365
|
+
const RV_MIN_LOADING_MS = 300;
|
|
366
|
+
|
|
362
367
|
function rvQueryKey<T>(q: ServerQuery<T>): string {
|
|
363
368
|
return JSON.stringify([q.page, q.pageSize, q.sort, q.search, q.filters]);
|
|
364
369
|
}
|
|
@@ -528,17 +533,32 @@ export function RecordView<T extends { id: RowId }>({
|
|
|
528
533
|
const runFetch = React.useCallback(
|
|
529
534
|
(q: ServerQuery<T>, opts?: { background?: boolean }) => {
|
|
530
535
|
if (!fetcher) return;
|
|
531
|
-
|
|
536
|
+
const id = ++reqIdRef.current;
|
|
537
|
+
const started = Date.now();
|
|
538
|
+
// Reveal the data, but hold the shimmer for a consistent minimum so a
|
|
539
|
+
// cache hit (served from memory, no server call) looks the same as a real
|
|
540
|
+
// fetch — same animation every time, never a confusing blank flash.
|
|
541
|
+
const commit = (rows: T[], total: number) => {
|
|
542
|
+
const apply = () => {
|
|
543
|
+
if (id !== reqIdRef.current) return; // superseded
|
|
544
|
+
setFetchedData(rows);
|
|
545
|
+
setFetchedTotal(total);
|
|
546
|
+
setFetchedLoading(false);
|
|
547
|
+
};
|
|
548
|
+
const wait = RV_MIN_LOADING_MS - (Date.now() - started);
|
|
549
|
+
if (opts?.background || wait <= 0) apply();
|
|
550
|
+
else window.setTimeout(apply, wait);
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
// Foreground cache hit: no server round-trip — the data is in memory.
|
|
532
554
|
if (!opts?.background && cacheKey) {
|
|
533
555
|
const hit = rvCacheGet(cacheKey, rvQueryKey(q), ttlMs);
|
|
534
556
|
if (hit) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
setFetchedLoading(false);
|
|
557
|
+
setFetchedLoading(true);
|
|
558
|
+
commit(hit.rows as T[], hit.total);
|
|
538
559
|
return;
|
|
539
560
|
}
|
|
540
561
|
}
|
|
541
|
-
const id = ++reqIdRef.current;
|
|
542
562
|
abortRef.current?.abort();
|
|
543
563
|
const controller = new AbortController();
|
|
544
564
|
abortRef.current = controller;
|
|
@@ -553,9 +573,7 @@ export function RecordView<T extends { id: RowId }>({
|
|
|
553
573
|
{ rows: res.rows, total: res.total, at: Date.now() },
|
|
554
574
|
cacheMax,
|
|
555
575
|
);
|
|
556
|
-
|
|
557
|
-
setFetchedTotal(res.total);
|
|
558
|
-
setFetchedLoading(false);
|
|
576
|
+
commit(res.rows, res.total);
|
|
559
577
|
})
|
|
560
578
|
.catch((err) => {
|
|
561
579
|
if (controller.signal.aborted || id !== reqIdRef.current) return;
|