@farming-labs/next 0.2.47 → 0.2.48

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.
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import Link from "next/link";
4
- import { useEffect, useMemo, useState } from "react";
4
+ import { useEffect, useMemo, useRef, useState } from "react";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
 
7
7
  //#region src/changelog-rail-search.tsx
@@ -106,13 +106,10 @@ function getHashTargetElement(href) {
106
106
  function ChangelogTOC({ title, items, variant = "releases" }) {
107
107
  const tocId = `fd-changelog-toc-${variant}-${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
108
108
  const [activeHref, setActiveHref] = useState(items[0]?.href ?? "");
109
+ const activeSourceRef = useRef("hash");
109
110
  useEffect(() => {
110
- if (items.length === 0) {
111
- setActiveHref("");
112
- return;
113
- }
114
111
  let frameId = 0;
115
- const updateActiveHref = () => {
112
+ const updateActiveHrefFromScroll = () => {
116
113
  const threshold = Math.max(140, window.innerHeight * .18);
117
114
  let nextActiveHref = items[0]?.href ?? "";
118
115
  for (const item of items) {
@@ -122,30 +119,60 @@ function ChangelogTOC({ title, items, variant = "releases" }) {
122
119
  if (top <= threshold) nextActiveHref = item.href;
123
120
  else break;
124
121
  }
125
- if (window.location.hash) {
126
- const hashHref = `#${decodeHashValue(window.location.hash.slice(1))}`;
127
- if (items.some((item) => item.href === hashHref)) nextActiveHref = hashHref;
122
+ const scrollingElement = document.scrollingElement ?? document.documentElement;
123
+ if (scrollingElement.scrollTop > 0 && scrollingElement.scrollHeight - scrollingElement.scrollTop - scrollingElement.clientHeight <= 2) for (let index = items.length - 1; index >= 0; index -= 1) {
124
+ const item = items[index];
125
+ if (item && getHashTargetElement(item.href)) {
126
+ nextActiveHref = item.href;
127
+ break;
128
+ }
128
129
  }
129
130
  setActiveHref(nextActiveHref);
130
131
  };
131
- const scheduleUpdate = () => {
132
+ const updateActiveHrefFromHash = () => {
133
+ if (!window.location.hash) return false;
134
+ const hashHref = `#${decodeHashValue(window.location.hash.slice(1))}`;
135
+ if (items.some((item) => item.href === hashHref)) {
136
+ setActiveHref(hashHref);
137
+ return true;
138
+ }
139
+ return false;
140
+ };
141
+ const syncActiveHref = () => {
142
+ if (activeSourceRef.current === "hash" && updateActiveHrefFromHash()) return;
143
+ activeSourceRef.current = "scroll";
144
+ updateActiveHrefFromScroll();
145
+ };
146
+ const scheduleActiveHrefUpdate = () => {
132
147
  if (frameId !== 0) return;
133
148
  frameId = window.requestAnimationFrame(() => {
134
149
  frameId = 0;
135
- updateActiveHref();
150
+ syncActiveHref();
136
151
  });
137
152
  };
138
- updateActiveHref();
139
- window.setTimeout(updateActiveHref, 0);
140
- window.setTimeout(updateActiveHref, 250);
141
- window.addEventListener("scroll", scheduleUpdate, { passive: true });
142
- window.addEventListener("resize", scheduleUpdate);
143
- window.addEventListener("hashchange", scheduleUpdate);
153
+ const handleScroll = () => {
154
+ activeSourceRef.current = "scroll";
155
+ scheduleActiveHrefUpdate();
156
+ };
157
+ const handleHashChange = () => {
158
+ activeSourceRef.current = "hash";
159
+ if (updateActiveHrefFromHash()) return;
160
+ activeSourceRef.current = "scroll";
161
+ scheduleActiveHrefUpdate();
162
+ };
163
+ syncActiveHref();
164
+ const initialUpdateId = window.setTimeout(syncActiveHref, 0);
165
+ const settledUpdateId = window.setTimeout(syncActiveHref, 250);
166
+ window.addEventListener("scroll", handleScroll, { passive: true });
167
+ window.addEventListener("resize", scheduleActiveHrefUpdate);
168
+ window.addEventListener("hashchange", handleHashChange);
144
169
  return () => {
170
+ window.clearTimeout(initialUpdateId);
171
+ window.clearTimeout(settledUpdateId);
145
172
  if (frameId !== 0) window.cancelAnimationFrame(frameId);
146
- window.removeEventListener("scroll", scheduleUpdate);
147
- window.removeEventListener("resize", scheduleUpdate);
148
- window.removeEventListener("hashchange", scheduleUpdate);
173
+ window.removeEventListener("scroll", handleScroll);
174
+ window.removeEventListener("resize", scheduleActiveHrefUpdate);
175
+ window.removeEventListener("hashchange", handleHashChange);
149
176
  };
150
177
  }, [items]);
151
178
  if (items.length === 0) return null;
@@ -403,10 +430,10 @@ function ChangelogDirectory({ title, description, entries, searchEnabled, action
403
430
  entry,
404
431
  isLast: index === filteredEntries.length - 1
405
432
  }, entry.slug))] }) : /* @__PURE__ */ jsx(EmptyResults, { query })
406
- }), hasResults ? /* @__PURE__ */ jsx(ChangelogTOC, {
433
+ }), /* @__PURE__ */ jsx(ChangelogTOC, {
407
434
  title: "Releases",
408
435
  items: tocItems
409
- }) : null]
436
+ })]
410
437
  })
411
438
  ]
412
439
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/next",
3
- "version": "0.2.47",
3
+ "version": "0.2.48",
4
4
  "description": "Next.js adapter for @farming-labs/docs — MDX config wrapper",
5
5
  "keywords": [
6
6
  "docs",
@@ -106,8 +106,8 @@
106
106
  "tsdown": "^0.20.3",
107
107
  "typescript": "^5.9.3",
108
108
  "vitest": "^4.1.8",
109
- "@farming-labs/docs": "0.2.47",
110
- "@farming-labs/theme": "0.2.47"
109
+ "@farming-labs/docs": "0.2.48",
110
+ "@farming-labs/theme": "0.2.48"
111
111
  },
112
112
  "peerDependencies": {
113
113
  "@farming-labs/docs": ">=0.0.1",