@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 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.1",
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",
@@ -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
- // Foreground: serve from cache if present (instant, no shimmer).
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
- setFetchedData(hit.rows as T[]);
536
- setFetchedTotal(hit.total);
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
- setFetchedData(res.rows);
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;