react-arborist 3.10.6 → 3.11.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/README.md CHANGED
@@ -745,6 +745,14 @@ _tree_.**scrollTo**(_id_, _[align]_)
745
745
 
746
746
  Scroll to the node with _id_. If this node is not visible, this method will open all its parents. The align argument can be _"auto" | "smart" | "center" | "end" | "start"_.
747
747
 
748
+ _tree_.**scrollToOffset**(_offset_)
749
+
750
+ Scroll the list vertically to an exact pixel _offset_ from the top — the offset-based counterpart to _scrollTo_, useful for saving and restoring a scroll position. Negative or non-finite values are clamped to the top, and react-window clamps the upper bound to the scrollable range.
751
+
752
+ _tree_.**scrollOffset** : _number_
753
+
754
+ Returns the list's current vertical scroll offset, in pixels from the top. Pairs with _scrollToOffset_ to persist and restore the scroll position.
755
+
748
756
  ### Properties
749
757
 
750
758
  _tree_.**isEditing** : _boolean_
@@ -235,6 +235,15 @@ export declare class TreeApi<T> {
235
235
  * it never disturbs scrolling for trees that fit their width.
236
236
  */
237
237
  private scrollToNodeHorizontally;
238
+ /**
239
+ * Scroll the list vertically to an exact pixel offset from the top. This is
240
+ * the offset-based counterpart to scrollTo(), handy for saving and restoring
241
+ * a scroll position (#194). Negative values are clamped to the top; react-
242
+ * window clamps the upper bound to the scrollable range.
243
+ */
244
+ scrollToOffset(offset: number): void;
245
+ /** The list's current vertical scroll offset, in pixels from the top. */
246
+ get scrollOffset(): number;
238
247
  get isEditing(): boolean;
239
248
  get isFiltered(): boolean;
240
249
  get hasFocus(): boolean;
@@ -715,6 +715,24 @@ class TreeApi {
715
715
  el.scrollLeft = Math.max(0, Math.min(left, maxScroll));
716
716
  }
717
717
  }
718
+ /**
719
+ * Scroll the list vertically to an exact pixel offset from the top. This is
720
+ * the offset-based counterpart to scrollTo(), handy for saving and restoring
721
+ * a scroll position (#194). Negative values are clamped to the top; react-
722
+ * window clamps the upper bound to the scrollable range.
723
+ */
724
+ scrollToOffset(offset) {
725
+ var _a;
726
+ /* Coerce non-finite offsets (NaN/Infinity, easy to get from malformed
727
+ persisted state) to the top rather than forwarding them to the list. */
728
+ const safe = Number.isFinite(offset) ? Math.max(0, offset) : 0;
729
+ (_a = this.list.current) === null || _a === void 0 ? void 0 : _a.scrollTo(safe);
730
+ }
731
+ /** The list's current vertical scroll offset, in pixels from the top. */
732
+ get scrollOffset() {
733
+ var _a, _b;
734
+ return (_b = (_a = this.listEl.current) === null || _a === void 0 ? void 0 : _a.scrollTop) !== null && _b !== void 0 ? _b : 0;
735
+ }
718
736
  /* State Checks */
719
737
  get isEditing() {
720
738
  return this.state.nodes.edit.id !== null;
@@ -219,3 +219,37 @@ describe("scrollTo brings a deeply nested node into view horizontally (#220)", (
219
219
  expect(el.scrollLeft).toBe(0);
220
220
  }));
221
221
  });
222
+ describe("scrollToOffset / scrollOffset set and read the vertical position (#194)", () => {
223
+ const data = [{ id: "a" }, { id: "b" }, { id: "c" }];
224
+ function setup(el) {
225
+ const store = (0, redux_1.createStore)(root_reducer_1.rootReducer);
226
+ const list = { current: { scrollTo: jest.fn() } };
227
+ const listEl = { current: (el !== null && el !== void 0 ? el : null) };
228
+ return { api: new tree_api_1.TreeApi(store, { data }, list, listEl), list, listEl };
229
+ }
230
+ test("scrollToOffset forwards the offset to the underlying list", () => {
231
+ const { api, list } = setup();
232
+ api.scrollToOffset(120);
233
+ expect(list.current.scrollTo).toHaveBeenCalledWith(120);
234
+ });
235
+ test("scrollToOffset clamps negative offsets to the top", () => {
236
+ const { api, list } = setup();
237
+ api.scrollToOffset(-50);
238
+ expect(list.current.scrollTo).toHaveBeenCalledWith(0);
239
+ });
240
+ test("scrollToOffset coerces non-finite offsets to the top", () => {
241
+ const { api, list } = setup();
242
+ api.scrollToOffset(NaN);
243
+ api.scrollToOffset(Infinity);
244
+ expect(list.current.scrollTo).toHaveBeenNthCalledWith(1, 0);
245
+ expect(list.current.scrollTo).toHaveBeenNthCalledWith(2, 0);
246
+ });
247
+ test("scrollOffset reads the list element's scrollTop", () => {
248
+ const { api } = setup({ scrollTop: 80 });
249
+ expect(api.scrollOffset).toBe(80);
250
+ });
251
+ test("scrollOffset is 0 before the list element mounts", () => {
252
+ const { api } = setup();
253
+ expect(api.scrollOffset).toBe(0);
254
+ });
255
+ });
@@ -235,6 +235,15 @@ export declare class TreeApi<T> {
235
235
  * it never disturbs scrolling for trees that fit their width.
236
236
  */
237
237
  private scrollToNodeHorizontally;
238
+ /**
239
+ * Scroll the list vertically to an exact pixel offset from the top. This is
240
+ * the offset-based counterpart to scrollTo(), handy for saving and restoring
241
+ * a scroll position (#194). Negative values are clamped to the top; react-
242
+ * window clamps the upper bound to the scrollable range.
243
+ */
244
+ scrollToOffset(offset: number): void;
245
+ /** The list's current vertical scroll offset, in pixels from the top. */
246
+ get scrollOffset(): number;
238
247
  get isEditing(): boolean;
239
248
  get isFiltered(): boolean;
240
249
  get hasFocus(): boolean;
@@ -689,6 +689,24 @@ export class TreeApi {
689
689
  el.scrollLeft = Math.max(0, Math.min(left, maxScroll));
690
690
  }
691
691
  }
692
+ /**
693
+ * Scroll the list vertically to an exact pixel offset from the top. This is
694
+ * the offset-based counterpart to scrollTo(), handy for saving and restoring
695
+ * a scroll position (#194). Negative values are clamped to the top; react-
696
+ * window clamps the upper bound to the scrollable range.
697
+ */
698
+ scrollToOffset(offset) {
699
+ var _a;
700
+ /* Coerce non-finite offsets (NaN/Infinity, easy to get from malformed
701
+ persisted state) to the top rather than forwarding them to the list. */
702
+ const safe = Number.isFinite(offset) ? Math.max(0, offset) : 0;
703
+ (_a = this.list.current) === null || _a === void 0 ? void 0 : _a.scrollTo(safe);
704
+ }
705
+ /** The list's current vertical scroll offset, in pixels from the top. */
706
+ get scrollOffset() {
707
+ var _a, _b;
708
+ return (_b = (_a = this.listEl.current) === null || _a === void 0 ? void 0 : _a.scrollTop) !== null && _b !== void 0 ? _b : 0;
709
+ }
692
710
  /* State Checks */
693
711
  get isEditing() {
694
712
  return this.state.nodes.edit.id !== null;
@@ -217,3 +217,37 @@ describe("scrollTo brings a deeply nested node into view horizontally (#220)", (
217
217
  expect(el.scrollLeft).toBe(0);
218
218
  }));
219
219
  });
220
+ describe("scrollToOffset / scrollOffset set and read the vertical position (#194)", () => {
221
+ const data = [{ id: "a" }, { id: "b" }, { id: "c" }];
222
+ function setup(el) {
223
+ const store = createStore(rootReducer);
224
+ const list = { current: { scrollTo: jest.fn() } };
225
+ const listEl = { current: (el !== null && el !== void 0 ? el : null) };
226
+ return { api: new TreeApi(store, { data }, list, listEl), list, listEl };
227
+ }
228
+ test("scrollToOffset forwards the offset to the underlying list", () => {
229
+ const { api, list } = setup();
230
+ api.scrollToOffset(120);
231
+ expect(list.current.scrollTo).toHaveBeenCalledWith(120);
232
+ });
233
+ test("scrollToOffset clamps negative offsets to the top", () => {
234
+ const { api, list } = setup();
235
+ api.scrollToOffset(-50);
236
+ expect(list.current.scrollTo).toHaveBeenCalledWith(0);
237
+ });
238
+ test("scrollToOffset coerces non-finite offsets to the top", () => {
239
+ const { api, list } = setup();
240
+ api.scrollToOffset(NaN);
241
+ api.scrollToOffset(Infinity);
242
+ expect(list.current.scrollTo).toHaveBeenNthCalledWith(1, 0);
243
+ expect(list.current.scrollTo).toHaveBeenNthCalledWith(2, 0);
244
+ });
245
+ test("scrollOffset reads the list element's scrollTop", () => {
246
+ const { api } = setup({ scrollTop: 80 });
247
+ expect(api.scrollOffset).toBe(80);
248
+ });
249
+ test("scrollOffset is 0 before the list element mounts", () => {
250
+ const { api } = setup();
251
+ expect(api.scrollOffset).toBe(0);
252
+ });
253
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-arborist",
3
- "version": "3.10.6",
3
+ "version": "3.11.0",
4
4
  "keywords": [
5
5
  "arborist",
6
6
  "dnd",
@@ -244,3 +244,44 @@ describe("scrollTo brings a deeply nested node into view horizontally (#220)", (
244
244
  expect(el.scrollLeft).toBe(0);
245
245
  });
246
246
  });
247
+
248
+ describe("scrollToOffset / scrollOffset set and read the vertical position (#194)", () => {
249
+ const data = [{ id: "a" }, { id: "b" }, { id: "c" }];
250
+
251
+ function setup(el?: Partial<HTMLDivElement>) {
252
+ const store = createStore(rootReducer);
253
+ const list = { current: { scrollTo: jest.fn() } as any };
254
+ const listEl = { current: (el ?? null) as HTMLDivElement | null };
255
+ return { api: new TreeApi(store, { data }, list, listEl), list, listEl };
256
+ }
257
+
258
+ test("scrollToOffset forwards the offset to the underlying list", () => {
259
+ const { api, list } = setup();
260
+ api.scrollToOffset(120);
261
+ expect(list.current.scrollTo).toHaveBeenCalledWith(120);
262
+ });
263
+
264
+ test("scrollToOffset clamps negative offsets to the top", () => {
265
+ const { api, list } = setup();
266
+ api.scrollToOffset(-50);
267
+ expect(list.current.scrollTo).toHaveBeenCalledWith(0);
268
+ });
269
+
270
+ test("scrollToOffset coerces non-finite offsets to the top", () => {
271
+ const { api, list } = setup();
272
+ api.scrollToOffset(NaN);
273
+ api.scrollToOffset(Infinity);
274
+ expect(list.current.scrollTo).toHaveBeenNthCalledWith(1, 0);
275
+ expect(list.current.scrollTo).toHaveBeenNthCalledWith(2, 0);
276
+ });
277
+
278
+ test("scrollOffset reads the list element's scrollTop", () => {
279
+ const { api } = setup({ scrollTop: 80 });
280
+ expect(api.scrollOffset).toBe(80);
281
+ });
282
+
283
+ test("scrollOffset is 0 before the list element mounts", () => {
284
+ const { api } = setup();
285
+ expect(api.scrollOffset).toBe(0);
286
+ });
287
+ });
@@ -707,6 +707,24 @@ export class TreeApi<T> {
707
707
  }
708
708
  }
709
709
 
710
+ /**
711
+ * Scroll the list vertically to an exact pixel offset from the top. This is
712
+ * the offset-based counterpart to scrollTo(), handy for saving and restoring
713
+ * a scroll position (#194). Negative values are clamped to the top; react-
714
+ * window clamps the upper bound to the scrollable range.
715
+ */
716
+ scrollToOffset(offset: number) {
717
+ /* Coerce non-finite offsets (NaN/Infinity, easy to get from malformed
718
+ persisted state) to the top rather than forwarding them to the list. */
719
+ const safe = Number.isFinite(offset) ? Math.max(0, offset) : 0;
720
+ this.list.current?.scrollTo(safe);
721
+ }
722
+
723
+ /** The list's current vertical scroll offset, in pixels from the top. */
724
+ get scrollOffset(): number {
725
+ return this.listEl.current?.scrollTop ?? 0;
726
+ }
727
+
710
728
  /* State Checks */
711
729
 
712
730
  get isEditing() {