@ai-matrx/design-system 0.17.0 → 0.17.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
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.2 — 2026-09-12
4
+
5
+ `MatrxDataTable` now resets the vertical scroll position of both its desktop
6
+ table and mobile card scrollers before paint when a non-initial
7
+ `controlled-append` source key changes. Same-source appends and horizontal
8
+ scroll position remain untouched in both automatic and manual modes.
9
+
10
+ ### Consumer action
11
+
12
+ Update to 0.17.2 so refreshed or replaced append sources begin at their first
13
+ row instead of retaining the previous source's bottom position. No API or
14
+ configuration change is required.
15
+
16
+ ## 0.17.1 — 2026-09-12
17
+
18
+ `useScrollPagination` now retains each container's last observed scroll position
19
+ across listener reattachment. This lets a trusted passive wheel append when
20
+ Chromium's compositor has advanced the scroller before JavaScript receives its
21
+ asynchronous scroll notification, while preserving the fresh-input, actual-
22
+ movement, one-gesture, manual-mode, error, and programmatic-scroll guards.
23
+
24
+ ### Consumer action
25
+
26
+ Update to 0.17.1 for reliable wheel-driven append on `controlled-append` tables.
27
+ No API or configuration change is required.
28
+
3
29
  ## 0.17.0 — 2026-09-12
4
30
 
5
31
  - `MatrxDataTable` accepts opt-in controlled `columnState` and `reorderableColumns`, allowing URL-backed order/visibility and package-owned header drag without a host renderer fork. Header grips and the column manager use the existing pointer sensor for mouse/touch ordering; the manager retains arrow controls and protected visibility. Headers show insertion feedback and cancel edge scrolling when the drag ends or leaves a valid target.
@@ -1197,6 +1197,7 @@ function useScrollPagination({
1197
1197
  const inFlightGenerationRef = (0, import_react.useRef)(null);
1198
1198
  const requestErrorRef = (0, import_react.useRef)(null);
1199
1199
  const mountedRef = (0, import_react.useRef)(false);
1200
+ const observedTopRef = (0, import_react.useRef)(/* @__PURE__ */ new WeakMap());
1200
1201
  const effectiveError = error ?? requestError;
1201
1202
  (0, import_react.useEffect)(() => {
1202
1203
  mountedRef.current = true;
@@ -1230,6 +1231,7 @@ function useScrollPagination({
1230
1231
  };
1231
1232
  (0, import_react.useEffect)(() => {
1232
1233
  intentRef.current = null;
1234
+ observedTopRef.current = /* @__PURE__ */ new WeakMap();
1233
1235
  consumedSequenceRef.current = inputSequenceRef.current;
1234
1236
  requestErrorRef.current = null;
1235
1237
  setRequestError(null);
@@ -1239,14 +1241,18 @@ function useScrollPagination({
1239
1241
  (0, import_react.useEffect)(() => {
1240
1242
  if (mode !== "scroll" || !enabled || typeof window === "undefined") return;
1241
1243
  const attachable = containers.map(resolveContainer).filter((container) => container !== null);
1242
- const setIntent = (container) => {
1244
+ const setIntent = (container, startTop) => {
1243
1245
  const current = latestRef.current;
1244
1246
  if (!isVisibleContainer(container) || current.loading || current.isFetchingNextPage || inFlightGenerationRef.current !== null) return;
1245
1247
  inputSequenceRef.current += 1;
1246
1248
  intentRef.current = {
1247
1249
  queryKey: latestRef.current.queryKey,
1248
1250
  container,
1249
- startTop: container.scrollTop,
1251
+ // The compositor can advance downward before the passive input
1252
+ // listener, while DOM/test callers can move upward before its scroll
1253
+ // notification. Either way, the lower position is the only safe
1254
+ // baseline for proving that this downward intent caused movement.
1255
+ startTop: Math.min(startTop, container.scrollTop),
1250
1256
  sequence: inputSequenceRef.current,
1251
1257
  expiresAt: Date.now() + intentTimeoutMs
1252
1258
  };
@@ -1261,9 +1267,11 @@ function useScrollPagination({
1261
1267
  void requestNextPage();
1262
1268
  };
1263
1269
  const cleanups = attachable.map((container) => {
1270
+ if (!observedTopRef.current.has(container)) observedTopRef.current.set(container, container.scrollTop);
1271
+ const lastObservedTop = () => observedTopRef.current.get(container) ?? container.scrollTop;
1264
1272
  const onWheel = (event) => {
1265
1273
  if (event.deltaY > 0 && Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
1266
- setIntent(container);
1274
+ setIntent(container, lastObservedTop());
1267
1275
  }
1268
1276
  };
1269
1277
  const onTouchStart = (event) => {
@@ -1274,19 +1282,22 @@ function useScrollPagination({
1274
1282
  const touch = event.touches.item(0);
1275
1283
  const startY = Number(container.dataset.matrxPaginationTouchY);
1276
1284
  if (touch && Number.isFinite(startY) && startY - touch.clientY > 0) {
1277
- setIntent(container);
1285
+ setIntent(container, lastObservedTop());
1278
1286
  }
1279
1287
  };
1280
1288
  const onTouchEnd = () => {
1281
1289
  delete container.dataset.matrxPaginationTouchY;
1282
1290
  };
1283
1291
  const onKeyDown = (event) => {
1284
- if (isDownwardKeyboardInput(event)) setIntent(container);
1292
+ if (isDownwardKeyboardInput(event)) setIntent(container, lastObservedTop());
1285
1293
  };
1286
1294
  const onPointerDown = (event) => {
1287
- if (isNativeScrollbarPointer(event, container)) setIntent(container);
1295
+ if (isNativeScrollbarPointer(event, container)) setIntent(container, lastObservedTop());
1296
+ };
1297
+ const onScroll = () => {
1298
+ handleScroll(container);
1299
+ observedTopRef.current.set(container, container.scrollTop);
1288
1300
  };
1289
- const onScroll = () => handleScroll(container);
1290
1301
  container.addEventListener("wheel", onWheel, { passive: true });
1291
1302
  container.addEventListener("touchstart", onTouchStart, { passive: true });
1292
1303
  container.addEventListener("touchmove", onTouchMove, { passive: true });
@@ -5458,6 +5469,16 @@ function MatrxDataTableCore({
5458
5469
  ]);
5459
5470
  const scrollRef = (0, import_react14.useRef)(null);
5460
5471
  const cardsScrollRef = (0, import_react14.useRef)(null);
5472
+ const previousAppendKeyForScrollReset = (0, import_react14.useRef)(appendQueryKey);
5473
+ (0, import_react14.useLayoutEffect)(() => {
5474
+ const previous = previousAppendKeyForScrollReset.current;
5475
+ previousAppendKeyForScrollReset.current = appendQueryKey;
5476
+ if (previous === null || appendQueryKey === null || previous === appendQueryKey) {
5477
+ return;
5478
+ }
5479
+ if (scrollRef.current) scrollRef.current.scrollTop = 0;
5480
+ if (cardsScrollRef.current) cardsScrollRef.current.scrollTop = 0;
5481
+ }, [appendQueryKey]);
5461
5482
  const scrollPagination = useScrollPagination({
5462
5483
  containers: [scrollRef, cardsScrollRef],
5463
5484
  queryKey: appendQuery?.pagination.queryKey ?? "",