@readme/markdown 13.8.0 → 13.8.1
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/components/TableOfContents/index.tsx +59 -6
- package/dist/main.js +51 -7
- package/dist/main.node.js +51 -7
- package/dist/main.node.js.map +1 -1
- package/package.json +2 -2
|
@@ -17,6 +17,10 @@ function buildLinkMap(nav: HTMLElement) {
|
|
|
17
17
|
return map;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
const VISIBLE_RATIO = 0.4;
|
|
21
|
+
/** Tolerance for subpixel rounding when checking if scrolled to the bottom. */
|
|
22
|
+
const SCROLL_BOTTOM_TOLERANCE = 1;
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* Watches headings in the viewport and toggles `active` on the
|
|
22
26
|
* corresponding TOC links so the reader always knows where they are.
|
|
@@ -44,8 +48,12 @@ function useScrollHighlight(navRef: React.RefObject<HTMLElement | null>) {
|
|
|
44
48
|
if (headings.length === 0) return undefined;
|
|
45
49
|
|
|
46
50
|
let activeId: string | null = null;
|
|
51
|
+
let clickLocked = false;
|
|
47
52
|
const visible = new Set<string>();
|
|
48
53
|
|
|
54
|
+
const isAtBottom = () =>
|
|
55
|
+
window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - SCROLL_BOTTOM_TOLERANCE;
|
|
56
|
+
|
|
49
57
|
const activate = (id: string | null) => {
|
|
50
58
|
if (id === activeId) return;
|
|
51
59
|
if (activeId) linkMap.get(activeId)?.forEach(a => a.classList.remove('active'));
|
|
@@ -66,22 +74,67 @@ function useScrollHighlight(navRef: React.RefObject<HTMLElement | null>) {
|
|
|
66
74
|
}
|
|
67
75
|
};
|
|
68
76
|
|
|
77
|
+
const updateActive = () => {
|
|
78
|
+
if (clickLocked) return;
|
|
79
|
+
|
|
80
|
+
if (isAtBottom()) {
|
|
81
|
+
activate(headings[headings.length - 1].id);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const topmost = headings.find(el => visible.has(el.id));
|
|
86
|
+
if (topmost) activate(topmost.id);
|
|
87
|
+
};
|
|
88
|
+
|
|
69
89
|
const observer = new IntersectionObserver(
|
|
70
90
|
entries => {
|
|
71
91
|
entries.forEach(e => {
|
|
72
92
|
if (e.isIntersecting) visible.add(e.target.id);
|
|
73
93
|
else visible.delete(e.target.id);
|
|
74
94
|
});
|
|
75
|
-
|
|
76
|
-
// keep the last active one so the user still has context.
|
|
77
|
-
const topmost = headings.find(el => visible.has(el.id));
|
|
78
|
-
if (topmost) activate(topmost.id);
|
|
95
|
+
updateActive();
|
|
79
96
|
},
|
|
80
|
-
{ rootMargin:
|
|
97
|
+
{ rootMargin: `0px 0px -${(1 - VISIBLE_RATIO) * 100}% 0px`, threshold: 0 },
|
|
81
98
|
);
|
|
82
99
|
|
|
100
|
+
// Also check on scroll so bottom-of-page detection works even when
|
|
101
|
+
// no headings are crossing the intersection boundary.
|
|
102
|
+
const onScroll = () => { updateActive(); };
|
|
103
|
+
|
|
104
|
+
// Click a ToC link → immediately activate it, suppress the observer
|
|
105
|
+
// until the smooth scroll finishes, then hand control back.
|
|
106
|
+
const onClick = (e: MouseEvent) => {
|
|
107
|
+
const anchor = (e.target as HTMLElement).closest?.('a[href^="#"]');
|
|
108
|
+
if (!anchor) return;
|
|
109
|
+
const id = decodeURIComponent(anchor.getAttribute('href')!.slice(1));
|
|
110
|
+
if (!linkMap.has(id)) return;
|
|
111
|
+
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
activate(id);
|
|
114
|
+
clickLocked = true;
|
|
115
|
+
|
|
116
|
+
const unlock = () => { clickLocked = false; };
|
|
117
|
+
window.addEventListener('scrollend', unlock, { once: true });
|
|
118
|
+
|
|
119
|
+
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' });
|
|
120
|
+
};
|
|
121
|
+
|
|
83
122
|
headings.forEach(el => { observer.observe(el); });
|
|
84
|
-
|
|
123
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
124
|
+
nav.addEventListener('click', onClick);
|
|
125
|
+
|
|
126
|
+
// Set initial active state for the first heading visible in the viewport
|
|
127
|
+
const initialHeading = headings.find(el => {
|
|
128
|
+
const rect = el.getBoundingClientRect();
|
|
129
|
+
return rect.top >= 0 && rect.top < window.innerHeight * VISIBLE_RATIO;
|
|
130
|
+
});
|
|
131
|
+
if (initialHeading) activate(initialHeading.id);
|
|
132
|
+
|
|
133
|
+
return () => {
|
|
134
|
+
observer.disconnect();
|
|
135
|
+
window.removeEventListener('scroll', onScroll);
|
|
136
|
+
nav.removeEventListener('click', onClick);
|
|
137
|
+
};
|
|
85
138
|
}, [navRef, linkCount]);
|
|
86
139
|
}
|
|
87
140
|
|
package/dist/main.js
CHANGED
|
@@ -12087,6 +12087,9 @@ function buildLinkMap(nav) {
|
|
|
12087
12087
|
});
|
|
12088
12088
|
return map;
|
|
12089
12089
|
}
|
|
12090
|
+
const VISIBLE_RATIO = 0.4;
|
|
12091
|
+
/** Tolerance for subpixel rounding when checking if scrolled to the bottom. */
|
|
12092
|
+
const SCROLL_BOTTOM_TOLERANCE = 1;
|
|
12090
12093
|
/**
|
|
12091
12094
|
* Watches headings in the viewport and toggles `active` on the
|
|
12092
12095
|
* corresponding TOC links so the reader always knows where they are.
|
|
@@ -12113,7 +12116,9 @@ function useScrollHighlight(navRef) {
|
|
|
12113
12116
|
if (headings.length === 0)
|
|
12114
12117
|
return undefined;
|
|
12115
12118
|
let activeId = null;
|
|
12119
|
+
let clickLocked = false;
|
|
12116
12120
|
const visible = new Set();
|
|
12121
|
+
const isAtBottom = () => window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - SCROLL_BOTTOM_TOLERANCE;
|
|
12117
12122
|
const activate = (id) => {
|
|
12118
12123
|
if (id === activeId)
|
|
12119
12124
|
return;
|
|
@@ -12133,6 +12138,17 @@ function useScrollHighlight(navRef) {
|
|
|
12133
12138
|
}
|
|
12134
12139
|
}
|
|
12135
12140
|
};
|
|
12141
|
+
const updateActive = () => {
|
|
12142
|
+
if (clickLocked)
|
|
12143
|
+
return;
|
|
12144
|
+
if (isAtBottom()) {
|
|
12145
|
+
activate(headings[headings.length - 1].id);
|
|
12146
|
+
return;
|
|
12147
|
+
}
|
|
12148
|
+
const topmost = headings.find(el => visible.has(el.id));
|
|
12149
|
+
if (topmost)
|
|
12150
|
+
activate(topmost.id);
|
|
12151
|
+
};
|
|
12136
12152
|
const observer = new IntersectionObserver(entries => {
|
|
12137
12153
|
entries.forEach(e => {
|
|
12138
12154
|
if (e.isIntersecting)
|
|
@@ -12140,14 +12156,42 @@ function useScrollHighlight(navRef) {
|
|
|
12140
12156
|
else
|
|
12141
12157
|
visible.delete(e.target.id);
|
|
12142
12158
|
});
|
|
12143
|
-
|
|
12144
|
-
|
|
12145
|
-
|
|
12146
|
-
|
|
12147
|
-
|
|
12148
|
-
|
|
12159
|
+
updateActive();
|
|
12160
|
+
}, { rootMargin: `0px 0px -${(1 - VISIBLE_RATIO) * 100}% 0px`, threshold: 0 });
|
|
12161
|
+
// Also check on scroll so bottom-of-page detection works even when
|
|
12162
|
+
// no headings are crossing the intersection boundary.
|
|
12163
|
+
const onScroll = () => { updateActive(); };
|
|
12164
|
+
// Click a ToC link → immediately activate it, suppress the observer
|
|
12165
|
+
// until the smooth scroll finishes, then hand control back.
|
|
12166
|
+
const onClick = (e) => {
|
|
12167
|
+
const anchor = e.target.closest?.('a[href^="#"]');
|
|
12168
|
+
if (!anchor)
|
|
12169
|
+
return;
|
|
12170
|
+
const id = decodeURIComponent(anchor.getAttribute('href').slice(1));
|
|
12171
|
+
if (!linkMap.has(id))
|
|
12172
|
+
return;
|
|
12173
|
+
e.preventDefault();
|
|
12174
|
+
activate(id);
|
|
12175
|
+
clickLocked = true;
|
|
12176
|
+
const unlock = () => { clickLocked = false; };
|
|
12177
|
+
window.addEventListener('scrollend', unlock, { once: true });
|
|
12178
|
+
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' });
|
|
12179
|
+
};
|
|
12149
12180
|
headings.forEach(el => { observer.observe(el); });
|
|
12150
|
-
|
|
12181
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
12182
|
+
nav.addEventListener('click', onClick);
|
|
12183
|
+
// Set initial active state for the first heading visible in the viewport
|
|
12184
|
+
const initialHeading = headings.find(el => {
|
|
12185
|
+
const rect = el.getBoundingClientRect();
|
|
12186
|
+
return rect.top >= 0 && rect.top < window.innerHeight * VISIBLE_RATIO;
|
|
12187
|
+
});
|
|
12188
|
+
if (initialHeading)
|
|
12189
|
+
activate(initialHeading.id);
|
|
12190
|
+
return () => {
|
|
12191
|
+
observer.disconnect();
|
|
12192
|
+
window.removeEventListener('scroll', onScroll);
|
|
12193
|
+
nav.removeEventListener('click', onClick);
|
|
12194
|
+
};
|
|
12151
12195
|
}, [navRef, linkCount]);
|
|
12152
12196
|
}
|
|
12153
12197
|
function TableOfContents({ children }) {
|
package/dist/main.node.js
CHANGED
|
@@ -24683,6 +24683,9 @@ function buildLinkMap(nav) {
|
|
|
24683
24683
|
});
|
|
24684
24684
|
return map;
|
|
24685
24685
|
}
|
|
24686
|
+
const VISIBLE_RATIO = 0.4;
|
|
24687
|
+
/** Tolerance for subpixel rounding when checking if scrolled to the bottom. */
|
|
24688
|
+
const SCROLL_BOTTOM_TOLERANCE = 1;
|
|
24686
24689
|
/**
|
|
24687
24690
|
* Watches headings in the viewport and toggles `active` on the
|
|
24688
24691
|
* corresponding TOC links so the reader always knows where they are.
|
|
@@ -24709,7 +24712,9 @@ function useScrollHighlight(navRef) {
|
|
|
24709
24712
|
if (headings.length === 0)
|
|
24710
24713
|
return undefined;
|
|
24711
24714
|
let activeId = null;
|
|
24715
|
+
let clickLocked = false;
|
|
24712
24716
|
const visible = new Set();
|
|
24717
|
+
const isAtBottom = () => window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - SCROLL_BOTTOM_TOLERANCE;
|
|
24713
24718
|
const activate = (id) => {
|
|
24714
24719
|
if (id === activeId)
|
|
24715
24720
|
return;
|
|
@@ -24729,6 +24734,17 @@ function useScrollHighlight(navRef) {
|
|
|
24729
24734
|
}
|
|
24730
24735
|
}
|
|
24731
24736
|
};
|
|
24737
|
+
const updateActive = () => {
|
|
24738
|
+
if (clickLocked)
|
|
24739
|
+
return;
|
|
24740
|
+
if (isAtBottom()) {
|
|
24741
|
+
activate(headings[headings.length - 1].id);
|
|
24742
|
+
return;
|
|
24743
|
+
}
|
|
24744
|
+
const topmost = headings.find(el => visible.has(el.id));
|
|
24745
|
+
if (topmost)
|
|
24746
|
+
activate(topmost.id);
|
|
24747
|
+
};
|
|
24732
24748
|
const observer = new IntersectionObserver(entries => {
|
|
24733
24749
|
entries.forEach(e => {
|
|
24734
24750
|
if (e.isIntersecting)
|
|
@@ -24736,14 +24752,42 @@ function useScrollHighlight(navRef) {
|
|
|
24736
24752
|
else
|
|
24737
24753
|
visible.delete(e.target.id);
|
|
24738
24754
|
});
|
|
24739
|
-
|
|
24740
|
-
|
|
24741
|
-
|
|
24742
|
-
|
|
24743
|
-
|
|
24744
|
-
|
|
24755
|
+
updateActive();
|
|
24756
|
+
}, { rootMargin: `0px 0px -${(1 - VISIBLE_RATIO) * 100}% 0px`, threshold: 0 });
|
|
24757
|
+
// Also check on scroll so bottom-of-page detection works even when
|
|
24758
|
+
// no headings are crossing the intersection boundary.
|
|
24759
|
+
const onScroll = () => { updateActive(); };
|
|
24760
|
+
// Click a ToC link → immediately activate it, suppress the observer
|
|
24761
|
+
// until the smooth scroll finishes, then hand control back.
|
|
24762
|
+
const onClick = (e) => {
|
|
24763
|
+
const anchor = e.target.closest?.('a[href^="#"]');
|
|
24764
|
+
if (!anchor)
|
|
24765
|
+
return;
|
|
24766
|
+
const id = decodeURIComponent(anchor.getAttribute('href').slice(1));
|
|
24767
|
+
if (!linkMap.has(id))
|
|
24768
|
+
return;
|
|
24769
|
+
e.preventDefault();
|
|
24770
|
+
activate(id);
|
|
24771
|
+
clickLocked = true;
|
|
24772
|
+
const unlock = () => { clickLocked = false; };
|
|
24773
|
+
window.addEventListener('scrollend', unlock, { once: true });
|
|
24774
|
+
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' });
|
|
24775
|
+
};
|
|
24745
24776
|
headings.forEach(el => { observer.observe(el); });
|
|
24746
|
-
|
|
24777
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
24778
|
+
nav.addEventListener('click', onClick);
|
|
24779
|
+
// Set initial active state for the first heading visible in the viewport
|
|
24780
|
+
const initialHeading = headings.find(el => {
|
|
24781
|
+
const rect = el.getBoundingClientRect();
|
|
24782
|
+
return rect.top >= 0 && rect.top < window.innerHeight * VISIBLE_RATIO;
|
|
24783
|
+
});
|
|
24784
|
+
if (initialHeading)
|
|
24785
|
+
activate(initialHeading.id);
|
|
24786
|
+
return () => {
|
|
24787
|
+
observer.disconnect();
|
|
24788
|
+
window.removeEventListener('scroll', onScroll);
|
|
24789
|
+
nav.removeEventListener('click', onClick);
|
|
24790
|
+
};
|
|
24747
24791
|
}, [navRef, linkCount]);
|
|
24748
24792
|
}
|
|
24749
24793
|
function TableOfContents({ children }) {
|