@trackunit/react-components 2.2.26-alpha-288640a1f26.0 → 2.2.27-alpha-485e203e548.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.
Files changed (36) hide show
  1. package/index.cjs.js +50 -28
  2. package/index.esm.js +51 -29
  3. package/package.json +6 -6
  4. package/src/hooks/useCursorUrlSync.d.ts +5 -0
  5. /package/{translation10.cjs.js → translation.cjs10.js} +0 -0
  6. /package/{translation11.cjs.js → translation.cjs11.js} +0 -0
  7. /package/{translation12.cjs.js → translation.cjs12.js} +0 -0
  8. /package/{translation13.cjs.js → translation.cjs13.js} +0 -0
  9. /package/{translation14.cjs.js → translation.cjs14.js} +0 -0
  10. /package/{translation15.cjs.js → translation.cjs15.js} +0 -0
  11. /package/{translation16.cjs.js → translation.cjs16.js} +0 -0
  12. /package/{translation17.cjs.js → translation.cjs17.js} +0 -0
  13. /package/{translation2.cjs.js → translation.cjs2.js} +0 -0
  14. /package/{translation3.cjs.js → translation.cjs3.js} +0 -0
  15. /package/{translation4.cjs.js → translation.cjs4.js} +0 -0
  16. /package/{translation5.cjs.js → translation.cjs5.js} +0 -0
  17. /package/{translation6.cjs.js → translation.cjs6.js} +0 -0
  18. /package/{translation7.cjs.js → translation.cjs7.js} +0 -0
  19. /package/{translation8.cjs.js → translation.cjs8.js} +0 -0
  20. /package/{translation9.cjs.js → translation.cjs9.js} +0 -0
  21. /package/{translation10.esm.js → translation.esm10.js} +0 -0
  22. /package/{translation11.esm.js → translation.esm11.js} +0 -0
  23. /package/{translation12.esm.js → translation.esm12.js} +0 -0
  24. /package/{translation13.esm.js → translation.esm13.js} +0 -0
  25. /package/{translation14.esm.js → translation.esm14.js} +0 -0
  26. /package/{translation15.esm.js → translation.esm15.js} +0 -0
  27. /package/{translation16.esm.js → translation.esm16.js} +0 -0
  28. /package/{translation17.esm.js → translation.esm17.js} +0 -0
  29. /package/{translation2.esm.js → translation.esm2.js} +0 -0
  30. /package/{translation3.esm.js → translation.esm3.js} +0 -0
  31. /package/{translation4.esm.js → translation.esm4.js} +0 -0
  32. /package/{translation5.esm.js → translation.esm5.js} +0 -0
  33. /package/{translation6.esm.js → translation.esm6.js} +0 -0
  34. /package/{translation7.esm.js → translation.esm7.js} +0 -0
  35. /package/{translation8.esm.js → translation.esm8.js} +0 -0
  36. /package/{translation9.esm.js → translation.esm9.js} +0 -0
package/index.cjs.js CHANGED
@@ -45,22 +45,22 @@ const translations = {
45
45
  default: defaultTranslations,
46
46
  languages: {
47
47
  de: () => Promise.resolve().then(function () { return require('./translation.cjs.js'); }),
48
- da: () => Promise.resolve().then(function () { return require('./translation2.cjs.js'); }),
49
- cs: () => Promise.resolve().then(function () { return require('./translation3.cjs.js'); }),
50
- nl: () => Promise.resolve().then(function () { return require('./translation4.cjs.js'); }),
51
- fr: () => Promise.resolve().then(function () { return require('./translation5.cjs.js'); }),
52
- fi: () => Promise.resolve().then(function () { return require('./translation6.cjs.js'); }),
53
- hu: () => Promise.resolve().then(function () { return require('./translation7.cjs.js'); }),
54
- it: () => Promise.resolve().then(function () { return require('./translation8.cjs.js'); }),
55
- nb: () => Promise.resolve().then(function () { return require('./translation9.cjs.js'); }),
56
- pl: () => Promise.resolve().then(function () { return require('./translation10.cjs.js'); }),
57
- pt: () => Promise.resolve().then(function () { return require('./translation11.cjs.js'); }),
58
- ru: () => Promise.resolve().then(function () { return require('./translation12.cjs.js'); }),
59
- ro: () => Promise.resolve().then(function () { return require('./translation13.cjs.js'); }),
60
- es: () => Promise.resolve().then(function () { return require('./translation14.cjs.js'); }),
61
- sv: () => Promise.resolve().then(function () { return require('./translation15.cjs.js'); }),
62
- ja: () => Promise.resolve().then(function () { return require('./translation16.cjs.js'); }),
63
- th: () => Promise.resolve().then(function () { return require('./translation17.cjs.js'); }),
48
+ da: () => Promise.resolve().then(function () { return require('./translation.cjs2.js'); }),
49
+ cs: () => Promise.resolve().then(function () { return require('./translation.cjs3.js'); }),
50
+ nl: () => Promise.resolve().then(function () { return require('./translation.cjs4.js'); }),
51
+ fr: () => Promise.resolve().then(function () { return require('./translation.cjs5.js'); }),
52
+ fi: () => Promise.resolve().then(function () { return require('./translation.cjs6.js'); }),
53
+ hu: () => Promise.resolve().then(function () { return require('./translation.cjs7.js'); }),
54
+ it: () => Promise.resolve().then(function () { return require('./translation.cjs8.js'); }),
55
+ nb: () => Promise.resolve().then(function () { return require('./translation.cjs9.js'); }),
56
+ pl: () => Promise.resolve().then(function () { return require('./translation.cjs10.js'); }),
57
+ pt: () => Promise.resolve().then(function () { return require('./translation.cjs11.js'); }),
58
+ ru: () => Promise.resolve().then(function () { return require('./translation.cjs12.js'); }),
59
+ ro: () => Promise.resolve().then(function () { return require('./translation.cjs13.js'); }),
60
+ es: () => Promise.resolve().then(function () { return require('./translation.cjs14.js'); }),
61
+ sv: () => Promise.resolve().then(function () { return require('./translation.cjs15.js'); }),
62
+ ja: () => Promise.resolve().then(function () { return require('./translation.cjs16.js'); }),
63
+ th: () => Promise.resolve().then(function () { return require('./translation.cjs17.js'); }),
64
64
  },
65
65
  };
66
66
  /**
@@ -12474,24 +12474,40 @@ const DEFAULT_DEBOUNCE_MS = 300;
12474
12474
  *
12475
12475
  * Wire `initialCursor` into `usePaginationQuery` and `updateCursor` into
12476
12476
  * `useBidirectionalScroll`'s `onTopItemChange` callback (mapping index to cursor).
12477
+ *
12478
+ * A pending write is dropped whenever a path-changing navigation starts or the
12479
+ * location has moved on: the write targets `to: "."`, so landing it after the
12480
+ * user opened a row would replace their destination with the list they came
12481
+ * from.
12477
12482
  */
12478
12483
  const useCursorUrlSync = ({ paramName = DEFAULT_PARAM_NAME, debounceMs = DEFAULT_DEBOUNCE_MS, } = {}) => {
12479
- const search = reactRouter.useSearch({ strict: false });
12484
+ const router = reactRouter.useRouter();
12480
12485
  const navigate = reactRouter.useNavigate();
12486
+ // Read the cursor off the router rather than `useSearch`, which would
12487
+ // subscribe the table to its own writes and re-render every row mid-scroll.
12481
12488
  const [initialCursor] = react.useState(() => {
12482
- const entry = Object.entries(search).find(([key]) => key === paramName);
12489
+ const entry = Object.entries(router.latestLocation.search).find(([key]) => key === paramName);
12483
12490
  return typeof entry?.[1] === "string" ? entry[1] : undefined;
12484
12491
  });
12485
12492
  const timerRef = react.useRef(undefined);
12493
+ const cancelPendingUpdate = react.useCallback(() => {
12494
+ if (timerRef.current !== undefined) {
12495
+ clearTimeout(timerRef.current);
12496
+ timerRef.current = undefined;
12497
+ }
12498
+ }, []);
12486
12499
  const paramNameRef = react.useRef(paramName);
12487
12500
  react.useEffect(() => {
12488
12501
  paramNameRef.current = paramName;
12489
12502
  }, [paramName]);
12490
12503
  const updateCursor = react.useCallback((cursor) => {
12491
- if (timerRef.current !== undefined) {
12492
- clearTimeout(timerRef.current);
12493
- }
12504
+ cancelPendingUpdate();
12505
+ const scheduledAtPathname = router.latestLocation.pathname;
12494
12506
  timerRef.current = setTimeout(() => {
12507
+ timerRef.current = undefined;
12508
+ if (router.latestLocation.pathname !== scheduledAtPathname) {
12509
+ return;
12510
+ }
12495
12511
  void navigate({
12496
12512
  to: ".",
12497
12513
  search: (prev) => ({
@@ -12501,14 +12517,20 @@ const useCursorUrlSync = ({ paramName = DEFAULT_PARAM_NAME, debounceMs = DEFAULT
12501
12517
  replace: true,
12502
12518
  });
12503
12519
  }, debounceMs);
12504
- }, [debounceMs, navigate]);
12520
+ }, [cancelPendingUpdate, debounceMs, navigate, router]);
12521
+ // Only path-changing navigations invalidate a pending write. Same-pathname
12522
+ // navigations (filter bar, table persistence, map viewBox) share this URL and
12523
+ // their writes must not cancel ours. Cancelling here rather than relying on
12524
+ // the pathname check alone also covers navigating away and back within the
12525
+ // debounce window, which would otherwise land a stale cursor on a fresh list.
12526
+ react.useEffect(() => router.subscribe("onBeforeNavigate", event => {
12527
+ if (event.pathChanged) {
12528
+ cancelPendingUpdate();
12529
+ }
12530
+ }), [cancelPendingUpdate, router]);
12505
12531
  react.useEffect(() => {
12506
- return () => {
12507
- if (timerRef.current !== undefined) {
12508
- clearTimeout(timerRef.current);
12509
- }
12510
- };
12511
- }, []);
12532
+ return cancelPendingUpdate;
12533
+ }, [cancelPendingUpdate]);
12512
12534
  return react.useMemo(() => ({ initialCursor, updateCursor }), [initialCursor, updateCursor]);
12513
12535
  };
12514
12536
 
package/index.esm.js CHANGED
@@ -11,7 +11,7 @@ import IconSpriteOutline from '@trackunit/ui-icons/icons-sprite-outline.svg';
11
11
  import IconSpriteSolid from '@trackunit/ui-icons/icons-sprite-solid.svg';
12
12
  import { snakeCase, titleCase } from 'string-ts';
13
13
  import { cvaMerge } from '@trackunit/css-class-variance-utilities';
14
- import { useFloating, autoUpdate, offset, flip, shift, size, useClick, useDismiss, useHover as useHover$1, safePolygon, useRole, useInteractions, FloatingPortal, useMergeRefs as useMergeRefs$1, FloatingFocusManager, arrow, useTransitionStatus, FloatingArrow } from '@floating-ui/react';
14
+ import { useFloating, offset, flip, shift, size, autoUpdate, useClick, useDismiss, useHover as useHover$1, safePolygon, useRole, useInteractions, FloatingPortal, useMergeRefs as useMergeRefs$1, FloatingFocusManager, arrow, useTransitionStatus, FloatingArrow } from '@floating-ui/react';
15
15
  import { Slot, Slottable } from '@radix-ui/react-slot';
16
16
  import { omit, isEqual } from 'es-toolkit';
17
17
  import { Link, useBlocker, useNavigate, useLocation, useRouter, useSearch } from '@tanstack/react-router';
@@ -43,22 +43,22 @@ const translations = {
43
43
  default: defaultTranslations,
44
44
  languages: {
45
45
  de: () => import('./translation.esm.js'),
46
- da: () => import('./translation2.esm.js'),
47
- cs: () => import('./translation3.esm.js'),
48
- nl: () => import('./translation4.esm.js'),
49
- fr: () => import('./translation5.esm.js'),
50
- fi: () => import('./translation6.esm.js'),
51
- hu: () => import('./translation7.esm.js'),
52
- it: () => import('./translation8.esm.js'),
53
- nb: () => import('./translation9.esm.js'),
54
- pl: () => import('./translation10.esm.js'),
55
- pt: () => import('./translation11.esm.js'),
56
- ru: () => import('./translation12.esm.js'),
57
- ro: () => import('./translation13.esm.js'),
58
- es: () => import('./translation14.esm.js'),
59
- sv: () => import('./translation15.esm.js'),
60
- ja: () => import('./translation16.esm.js'),
61
- th: () => import('./translation17.esm.js'),
46
+ da: () => import('./translation.esm2.js'),
47
+ cs: () => import('./translation.esm3.js'),
48
+ nl: () => import('./translation.esm4.js'),
49
+ fr: () => import('./translation.esm5.js'),
50
+ fi: () => import('./translation.esm6.js'),
51
+ hu: () => import('./translation.esm7.js'),
52
+ it: () => import('./translation.esm8.js'),
53
+ nb: () => import('./translation.esm9.js'),
54
+ pl: () => import('./translation.esm10.js'),
55
+ pt: () => import('./translation.esm11.js'),
56
+ ru: () => import('./translation.esm12.js'),
57
+ ro: () => import('./translation.esm13.js'),
58
+ es: () => import('./translation.esm14.js'),
59
+ sv: () => import('./translation.esm15.js'),
60
+ ja: () => import('./translation.esm16.js'),
61
+ th: () => import('./translation.esm17.js'),
62
62
  },
63
63
  };
64
64
  /**
@@ -12472,24 +12472,40 @@ const DEFAULT_DEBOUNCE_MS = 300;
12472
12472
  *
12473
12473
  * Wire `initialCursor` into `usePaginationQuery` and `updateCursor` into
12474
12474
  * `useBidirectionalScroll`'s `onTopItemChange` callback (mapping index to cursor).
12475
+ *
12476
+ * A pending write is dropped whenever a path-changing navigation starts or the
12477
+ * location has moved on: the write targets `to: "."`, so landing it after the
12478
+ * user opened a row would replace their destination with the list they came
12479
+ * from.
12475
12480
  */
12476
12481
  const useCursorUrlSync = ({ paramName = DEFAULT_PARAM_NAME, debounceMs = DEFAULT_DEBOUNCE_MS, } = {}) => {
12477
- const search = useSearch({ strict: false });
12482
+ const router = useRouter();
12478
12483
  const navigate = useNavigate();
12484
+ // Read the cursor off the router rather than `useSearch`, which would
12485
+ // subscribe the table to its own writes and re-render every row mid-scroll.
12479
12486
  const [initialCursor] = useState(() => {
12480
- const entry = Object.entries(search).find(([key]) => key === paramName);
12487
+ const entry = Object.entries(router.latestLocation.search).find(([key]) => key === paramName);
12481
12488
  return typeof entry?.[1] === "string" ? entry[1] : undefined;
12482
12489
  });
12483
12490
  const timerRef = useRef(undefined);
12491
+ const cancelPendingUpdate = useCallback(() => {
12492
+ if (timerRef.current !== undefined) {
12493
+ clearTimeout(timerRef.current);
12494
+ timerRef.current = undefined;
12495
+ }
12496
+ }, []);
12484
12497
  const paramNameRef = useRef(paramName);
12485
12498
  useEffect(() => {
12486
12499
  paramNameRef.current = paramName;
12487
12500
  }, [paramName]);
12488
12501
  const updateCursor = useCallback((cursor) => {
12489
- if (timerRef.current !== undefined) {
12490
- clearTimeout(timerRef.current);
12491
- }
12502
+ cancelPendingUpdate();
12503
+ const scheduledAtPathname = router.latestLocation.pathname;
12492
12504
  timerRef.current = setTimeout(() => {
12505
+ timerRef.current = undefined;
12506
+ if (router.latestLocation.pathname !== scheduledAtPathname) {
12507
+ return;
12508
+ }
12493
12509
  void navigate({
12494
12510
  to: ".",
12495
12511
  search: (prev) => ({
@@ -12499,14 +12515,20 @@ const useCursorUrlSync = ({ paramName = DEFAULT_PARAM_NAME, debounceMs = DEFAULT
12499
12515
  replace: true,
12500
12516
  });
12501
12517
  }, debounceMs);
12502
- }, [debounceMs, navigate]);
12518
+ }, [cancelPendingUpdate, debounceMs, navigate, router]);
12519
+ // Only path-changing navigations invalidate a pending write. Same-pathname
12520
+ // navigations (filter bar, table persistence, map viewBox) share this URL and
12521
+ // their writes must not cancel ours. Cancelling here rather than relying on
12522
+ // the pathname check alone also covers navigating away and back within the
12523
+ // debounce window, which would otherwise land a stale cursor on a fresh list.
12524
+ useEffect(() => router.subscribe("onBeforeNavigate", event => {
12525
+ if (event.pathChanged) {
12526
+ cancelPendingUpdate();
12527
+ }
12528
+ }), [cancelPendingUpdate, router]);
12503
12529
  useEffect(() => {
12504
- return () => {
12505
- if (timerRef.current !== undefined) {
12506
- clearTimeout(timerRef.current);
12507
- }
12508
- };
12509
- }, []);
12530
+ return cancelPendingUpdate;
12531
+ }, [cancelPendingUpdate]);
12510
12532
  return useMemo(() => ({ initialCursor, updateCursor }), [initialCursor, updateCursor]);
12511
12533
  };
12512
12534
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-components",
3
- "version": "2.2.26-alpha-288640a1f26.0",
3
+ "version": "2.2.27-alpha-485e203e548.0",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "migrations": "./migrations.json",
@@ -14,17 +14,17 @@
14
14
  "@floating-ui/react": "^0.26.25",
15
15
  "string-ts": "^2.0.0",
16
16
  "tailwind-merge": "^2.0.0",
17
- "@trackunit/ui-design-tokens": "1.13.82-alpha-288640a1f26.0",
18
- "@trackunit/css-class-variance-utilities": "1.13.83-alpha-288640a1f26.0",
19
- "@trackunit/shared-utils": "1.15.86-alpha-288640a1f26.0",
20
- "@trackunit/ui-icons": "1.13.84-alpha-288640a1f26.0",
17
+ "@trackunit/ui-design-tokens": "1.13.82-alpha-485e203e548.0",
18
+ "@trackunit/css-class-variance-utilities": "1.13.83-alpha-485e203e548.0",
19
+ "@trackunit/shared-utils": "1.15.86-alpha-485e203e548.0",
20
+ "@trackunit/ui-icons": "1.13.84-alpha-485e203e548.0",
21
21
  "es-toolkit": "^1.39.10",
22
22
  "@tanstack/react-virtual": "^3.14.3",
23
23
  "dequal": "^2.0.3",
24
24
  "fflate": "^0.8.2",
25
25
  "superjson": "^2.2.6",
26
26
  "zod": "^3.25.76",
27
- "@trackunit/i18n-library-translation": "2.1.24-alpha-288640a1f26.0"
27
+ "@trackunit/i18n-library-translation": "2.1.24-alpha-485e203e548.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": "^19.0.0",
@@ -13,6 +13,11 @@ type UseCursorUrlSyncResult = {
13
13
  *
14
14
  * Wire `initialCursor` into `usePaginationQuery` and `updateCursor` into
15
15
  * `useBidirectionalScroll`'s `onTopItemChange` callback (mapping index to cursor).
16
+ *
17
+ * A pending write is dropped whenever a path-changing navigation starts or the
18
+ * location has moved on: the write targets `to: "."`, so landing it after the
19
+ * user opened a row would replace their destination with the list they came
20
+ * from.
16
21
  */
17
22
  export declare const useCursorUrlSync: ({ paramName, debounceMs, }?: UseCursorUrlSyncOptions) => UseCursorUrlSyncResult;
18
23
  export {};
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes