@san-siva/blogkit 1.1.17 → 1.1.18
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/dist/cjs/dynamicComponents/BlogDynamic.js +36 -26
- package/dist/cjs/dynamicComponents/BlogDynamic.js.map +1 -1
- package/dist/esm/dynamicComponents/BlogDynamic.js +36 -26
- package/dist/esm/dynamicComponents/BlogDynamic.js.map +1 -1
- package/dist/types/dynamicComponents/BlogDynamic.d.ts.map +1 -1
- package/dist/types/dynamicComponents/BlogSectionDynamic.d.ts.map +1 -1
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/lockScrollUpdates.d.ts +4 -0
- package/dist/types/utils/lockScrollUpdates.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/dynamicComponents/BlogDynamic.tsx +58 -31
- package/src/styles/BlogSection.module.scss.d.ts +1 -0
|
@@ -16,9 +16,9 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
16
16
|
const [showTOC, setShowTOC] = react.useState(false);
|
|
17
17
|
const updateTimerRef = react.useRef(null);
|
|
18
18
|
const showTOCTimerRef = react.useRef(null);
|
|
19
|
-
const hasScrolledToInitialSection = react.useRef(false);
|
|
20
19
|
const isClickScrolling = react.useRef(false);
|
|
21
20
|
const scrollEndHandlerRef = react.useRef(null);
|
|
21
|
+
const intersectionObserversRef = react.useRef(new Map());
|
|
22
22
|
const sortByDomPosition = react.useCallback(([, a], [, b]) => {
|
|
23
23
|
const position = a.el.compareDocumentPosition(b.el);
|
|
24
24
|
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
@@ -66,13 +66,14 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
66
66
|
}, 200);
|
|
67
67
|
}, [updateCategoryTitles]);
|
|
68
68
|
react.useEffect(() => {
|
|
69
|
-
const observers = new Map();
|
|
70
69
|
for (const [id, { el }] of categoryTitles) {
|
|
71
70
|
const observer = new IntersectionObserver(([entry]) => {
|
|
72
71
|
if (!entry.isIntersecting)
|
|
73
72
|
return;
|
|
74
73
|
if (isClickScrolling.current)
|
|
75
74
|
return;
|
|
75
|
+
if (document.body.scrollTop === 0)
|
|
76
|
+
return;
|
|
76
77
|
setVisibleTitle(visibleId => {
|
|
77
78
|
if (visibleId === id && !entry.isIntersecting)
|
|
78
79
|
return null;
|
|
@@ -84,14 +85,9 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
84
85
|
url.searchParams.set('section', id);
|
|
85
86
|
window.history.replaceState({}, '', url.toString());
|
|
86
87
|
}, { threshold: 0.1 });
|
|
87
|
-
|
|
88
|
+
intersectionObserversRef.current.set(id, observer);
|
|
88
89
|
observer.observe(el);
|
|
89
90
|
}
|
|
90
|
-
return () => {
|
|
91
|
-
for (const observer of observers.values()) {
|
|
92
|
-
observer.disconnect();
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
91
|
}, [categoryTitles.size]);
|
|
96
92
|
react.useEffect(() => {
|
|
97
93
|
return () => {
|
|
@@ -104,26 +100,46 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
104
100
|
if (scrollEndHandlerRef.current) {
|
|
105
101
|
document.body.removeEventListener('scrollend', scrollEndHandlerRef.current);
|
|
106
102
|
}
|
|
103
|
+
if (intersectionObserversRef.current) {
|
|
104
|
+
for (const observer of intersectionObserversRef.current.values()) {
|
|
105
|
+
observer.disconnect();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
107
108
|
};
|
|
108
109
|
}, []);
|
|
110
|
+
const getSectionFromUrl = () => {
|
|
111
|
+
const url = new URL(window.location.href);
|
|
112
|
+
const section = url.searchParams.get('section');
|
|
113
|
+
if (!section)
|
|
114
|
+
return null;
|
|
115
|
+
return section;
|
|
116
|
+
};
|
|
117
|
+
const updateUrl = (id) => {
|
|
118
|
+
const url = new URL(window.location.href);
|
|
119
|
+
url.searchParams.set('section', id);
|
|
120
|
+
window.history.replaceState({}, '', url.toString());
|
|
121
|
+
};
|
|
122
|
+
const scrollIntoView = (element) => {
|
|
123
|
+
if (!element)
|
|
124
|
+
return;
|
|
125
|
+
const top = element.getBoundingClientRect().top + document.body.scrollTop - 100;
|
|
126
|
+
document.body.scrollTo({ top, behavior: 'smooth' });
|
|
127
|
+
};
|
|
109
128
|
// On initial load, scroll to section specified in URL
|
|
110
129
|
react.useEffect(() => {
|
|
111
|
-
if (hasScrolledToInitialSection.current)
|
|
112
|
-
return;
|
|
113
130
|
if (categoryTitles.size === 0)
|
|
114
131
|
return;
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
if (!section)
|
|
132
|
+
const section = getSectionFromUrl();
|
|
133
|
+
if (!section) {
|
|
118
134
|
return;
|
|
135
|
+
}
|
|
119
136
|
const entry = categoryTitles.get(section);
|
|
120
|
-
if (!entry)
|
|
137
|
+
if (!entry) {
|
|
121
138
|
return;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
document.body.scrollTo({ top, behavior: 'smooth' });
|
|
139
|
+
}
|
|
140
|
+
scrollIntoView(entry.el);
|
|
125
141
|
lockScrollUpdates.default(section, isClickScrolling, scrollEndHandlerRef, setVisibleTitle);
|
|
126
|
-
}, [categoryTitles]);
|
|
142
|
+
}, [categoryTitles.size]);
|
|
127
143
|
const handleSectionReference = react.useCallback((element) => {
|
|
128
144
|
if (!element)
|
|
129
145
|
return;
|
|
@@ -166,14 +182,8 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
166
182
|
const { el } = categoryTitles.get(id) || {};
|
|
167
183
|
if (!el)
|
|
168
184
|
return;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
url.searchParams.set('section', id);
|
|
172
|
-
window.history.replaceState({}, '', url.toString());
|
|
173
|
-
document.body.scrollTo({
|
|
174
|
-
top,
|
|
175
|
-
behavior: 'smooth',
|
|
176
|
-
});
|
|
185
|
+
updateUrl(id);
|
|
186
|
+
scrollIntoView(el);
|
|
177
187
|
lockScrollUpdates.default(id, isClickScrolling, scrollEndHandlerRef, setVisibleTitle);
|
|
178
188
|
};
|
|
179
189
|
const sidebarStyle = web.useSpring({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlogDynamic.js","sources":["../../../src/dynamicComponents/BlogDynamic.tsx"],"sourcesContent":["'use client';\n\nimport {\n\tChildren,\n\tcloneElement,\n\tisValidElement,\n\tuseCallback,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react';\nimport { useSpring, animated, config } from '@react-spring/web';\n\nimport type { MouseEvent, ReactNode, RefAttributes } from 'react';\nimport type { Thing, WithContext } from 'schema-dts';\n\nimport styles from '../styles/Blog.module.scss';\nimport lockScrollUpdates from '../utils/lockScrollUpdates';\n\ninterface BlogProperties {\n\tchildren: ReactNode;\n\ttitle?: string;\n\tjsonLd?: WithContext<Thing>;\n}\n\nexport interface ForwardedReference {\n\tparentRef: HTMLDivElement;\n\tchildRefs: HTMLDivElement[];\n}\n\ninterface SectionReferenceValue {\n\tel: HTMLElement;\n\ttitle: string;\n\tisSubSection: boolean;\n}\n\ntype SectionReference = Map<string, SectionReferenceValue>;\n\ninterface CategoryTitleValue extends SectionReferenceValue {\n\tlastUpdatedAt: number;\n}\n\ntype CategoryTitle = Map<string, CategoryTitleValue>;\n\nconst Blog = ({\n\tchildren,\n\ttitle = 'In this article',\n\tjsonLd,\n}: BlogProperties) => {\n\tconst sectionReferences = useRef<SectionReference>(new Map());\n\tconst [categoryTitles, setCategoryTitles] = useState<CategoryTitle>(\n\t\tnew Map()\n\t);\n\tconst [visibleTitle, setVisibleTitle] = useState<string | null>(null);\n\tconst [showTOC, setShowTOC] = useState(false);\n\n\tconst updateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst showTOCTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst hasScrolledToInitialSection = useRef(false);\n\tconst isClickScrolling = useRef(false);\n\tconst scrollEndHandlerRef = useRef<(() => void) | null>(null);\n\n\tconst sortByDomPosition = useCallback(\n\t\t(\n\t\t\t[, a]: [string, SectionReferenceValue],\n\t\t\t[, b]: [string, SectionReferenceValue]\n\t\t) => {\n\t\t\tconst position = a.el.compareDocumentPosition(b.el);\n\t\t\tif (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n\t\t\t\treturn -1; // a comes before b\n\t\t\t} else if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n\t\t\t\treturn 1; // b comes before a\n\t\t\t}\n\t\t\treturn 0;\n\t\t},\n\t\t[]\n\t);\n\n\tconst updateCategoryTitles = useCallback(() => {\n\t\tconst now = Date.now();\n\t\tconst newCategoryTitles = new Map<string, CategoryTitleValue>();\n\n\t\t// Sort sections by their DOM position to maintain correct order\n\t\tconst sectionsArray = Array.from(sectionReferences.current.entries());\n\t\tsectionsArray.sort(sortByDomPosition);\n\n\t\tlet firstSectionId: string | null = null;\n\t\tfor (const [id, { title, el, isSubSection }] of sectionsArray) {\n\t\t\tif (!firstSectionId) {\n\t\t\t\tfirstSectionId = id;\n\t\t\t}\n\t\t\tnewCategoryTitles.set(id, {\n\t\t\t\tel,\n\t\t\t\ttitle,\n\t\t\t\tlastUpdatedAt: now,\n\t\t\t\tisSubSection,\n\t\t\t});\n\t\t}\n\n\t\tif (newCategoryTitles.size === 0) return;\n\n\t\tsetCategoryTitles(newCategoryTitles);\n\t\tif (!showTOC) setShowTOC(true);\n\n\t\tif (visibleTitle) return;\n\t\tsetVisibleTitle(firstSectionId);\n\t}, [visibleTitle, sortByDomPosition, showTOC, setShowTOC]);\n\n\tconst debounceUpdateCategoryTitles = useCallback(() => {\n\t\t// Clear existing timer and set a new one to batch updates\n\t\tif (updateTimerRef.current) {\n\t\t\tclearTimeout(updateTimerRef.current);\n\t\t}\n\t\tupdateTimerRef.current = setTimeout(() => {\n\t\t\tupdateCategoryTitles();\n\t\t}, 200);\n\t}, [updateCategoryTitles]);\n\n\tuseEffect(() => {\n\t\tconst observers = new Map<string, IntersectionObserver>();\n\t\tfor (const [id, { el }] of categoryTitles) {\n\t\t\tconst observer = new IntersectionObserver(\n\t\t\t\t([entry]) => {\n\t\t\t\t\tif (!entry.isIntersecting) return;\n\t\t\t\t\tif (isClickScrolling.current) return;\n\t\t\t\t\tsetVisibleTitle(visibleId => {\n\t\t\t\t\t\tif (visibleId === id && !entry.isIntersecting) return null;\n\t\t\t\t\t\tif (entry.isIntersecting) return id;\n\t\t\t\t\t\treturn visibleId;\n\t\t\t\t\t});\n\t\t\t\t\tconst url = new URL(window.location.href);\n\t\t\t\t\turl.searchParams.set('section', id);\n\t\t\t\t\twindow.history.replaceState({}, '', url.toString());\n\t\t\t\t},\n\t\t\t\t{ threshold: 0.1 }\n\t\t\t);\n\t\t\tobservers.set(id, observer);\n\t\t\tobserver.observe(el);\n\t\t}\n\t\treturn () => {\n\t\t\tfor (const observer of observers.values()) {\n\t\t\t\tobserver.disconnect();\n\t\t\t}\n\t\t};\n\t}, [categoryTitles.size]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (updateTimerRef.current) {\n\t\t\t\tclearTimeout(updateTimerRef.current);\n\t\t\t}\n\t\t\tif (showTOCTimerRef.current) {\n\t\t\t\tclearTimeout(showTOCTimerRef.current);\n\t\t\t}\n\t\t\tif (scrollEndHandlerRef.current) {\n\t\t\t\tdocument.body.removeEventListener('scrollend', scrollEndHandlerRef.current);\n\t\t\t}\n\t\t};\n\t}, []);\n\n\t// On initial load, scroll to section specified in URL\n\tuseEffect(() => {\n\t\tif (hasScrolledToInitialSection.current) return;\n\t\tif (categoryTitles.size === 0) return;\n\t\tconst url = new URL(window.location.href);\n\t\tconst section = url.searchParams.get('section');\n\t\tif (!section) return;\n\t\tconst entry = categoryTitles.get(section);\n\t\tif (!entry) return;\n\t\thasScrolledToInitialSection.current = true;\n\t\tconst top =\n\t\t\tentry.el.getBoundingClientRect().top + document.body.scrollTop - 100;\n\t\tdocument.body.scrollTo({ top, behavior: 'smooth' });\n\t\tlockScrollUpdates(section, isClickScrolling, scrollEndHandlerRef, setVisibleTitle);\n\t}, [categoryTitles]);\n\n\tconst handleSectionReference = useCallback(\n\t\t(element: ForwardedReference) => {\n\t\t\tif (!element) return;\n\t\t\tconst { parentRef, childRefs } = element;\n\n\t\t\t// Add parent section reference\n\t\t\tif (parentRef) {\n\t\t\t\tconst id = parentRef.dataset.id;\n\t\t\t\tconst title = parentRef.dataset.title;\n\t\t\t\tif (id && title) {\n\t\t\t\t\tsectionReferences.current.set(id, {\n\t\t\t\t\t\tel: parentRef,\n\t\t\t\t\t\ttitle,\n\t\t\t\t\t\tisSubSection: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add child section references\n\t\t\tif (Array.isArray(childRefs)) {\n\t\t\t\tfor (const childRef of childRefs) {\n\t\t\t\t\tif (!childRef) continue;\n\t\t\t\t\tconst id = childRef.dataset.id;\n\t\t\t\t\tconst title = childRef.dataset.title;\n\t\t\t\t\tif (id && title) {\n\t\t\t\t\t\tsectionReferences.current.set(id, {\n\t\t\t\t\t\t\tel: childRef,\n\t\t\t\t\t\t\ttitle,\n\t\t\t\t\t\t\tisSubSection: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdebounceUpdateCategoryTitles();\n\t\t},\n\t\t[debounceUpdateCategoryTitles]\n\t);\n\n\tconst handleClickCategoryTitle = (\n\t\terror: MouseEvent<HTMLParagraphElement>\n\t) => {\n\t\tconst id = error.currentTarget.dataset.id;\n\t\tconst index = error.currentTarget.dataset.idx;\n\t\tif (!id || !index) return;\n\n\t\tconst { el } = categoryTitles.get(id) || {};\n\t\tif (!el) return;\n\n\t\tconst top = el.getBoundingClientRect().top + document.body.scrollTop - 100;\n\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('section', id);\n\t\twindow.history.replaceState({}, '', url.toString());\n\n\t\tdocument.body.scrollTo({\n\t\t\ttop,\n\t\t\tbehavior: 'smooth',\n\t\t});\n\n\t\tlockScrollUpdates(id, isClickScrolling, scrollEndHandlerRef, setVisibleTitle);\n\t};\n\n\tconst sidebarStyle = useSpring({\n\t\topacity: showTOC ? 1 : 0,\n\t\ttransform: showTOC ? 'translateX(0)' : 'translateX(40px)',\n\t\tconfig: config.gentle,\n\t});\n\n\treturn (\n\t\t<div className={styles.blog}>\n\t\t\t{jsonLd && (\n\t\t\t\t<script\n\t\t\t\t\ttype=\"application/ld+json\"\n\t\t\t\t\tdangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<div className={styles['blog__content']}>\n\t\t\t\t{Children.map(children, child => {\n\t\t\t\t\tif (!isValidElement(child)) return child;\n\t\t\t\t\treturn cloneElement(child, {\n\t\t\t\t\t\tref: handleSectionReference,\n\t\t\t\t\t} as RefAttributes<ForwardedReference>);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t\t<animated.div className={styles['blog__sidebar']} style={sidebarStyle}>\n\t\t\t\t<p\n\t\t\t\t\tclassName={`${styles['margin-bottom--3']} ${styles['category__header']}`}\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</p>\n\t\t\t\t{[...categoryTitles].map(\n\t\t\t\t\t([id, { title, isSubSection }], index, array) => {\n\t\t\t\t\t\tconst isNextSectionSubSection = array[index + 1]?.[1]?.isSubSection;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\tkey={id}\n\t\t\t\t\t\t\t\tdata-idx={index}\n\t\t\t\t\t\t\t\tdata-id={id}\n\t\t\t\t\t\t\t\tclassName={`${styles['category__title']} ${\n\t\t\t\t\t\t\t\t\tid === visibleTitle ? styles['category__title--active'] : ''\n\t\t\t\t\t\t\t\t} ${isSubSection ? styles['category__title--sub'] : ''} ${\n\t\t\t\t\t\t\t\t\tisSubSection && !isNextSectionSubSection\n\t\t\t\t\t\t\t\t\t\t? styles['margin-bottom-imp--2']\n\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\tonClick={handleClickCategoryTitle}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t)}\n\t\t\t</animated.div>\n\t\t</div>\n\t);\n};\n\nexport default Blog;\n"],"names":["useRef","useState","useCallback","useEffect","lockScrollUpdates","useSpring","config","_jsxs","styles","_jsx","Children","isValidElement","cloneElement","animated"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BlogDynamic.js","sources":["../../../src/dynamicComponents/BlogDynamic.tsx"],"sourcesContent":["'use client';\n\nimport {\n\tChildren,\n\tcloneElement,\n\tisValidElement,\n\tuseCallback,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react';\nimport { useSpring, animated, config } from '@react-spring/web';\n\nimport type { MouseEvent, ReactNode, RefAttributes } from 'react';\nimport type { Thing, WithContext } from 'schema-dts';\n\nimport styles from '../styles/Blog.module.scss';\nimport lockScrollUpdates from '../utils/lockScrollUpdates';\n\ninterface BlogProperties {\n\tchildren: ReactNode;\n\ttitle?: string;\n\tjsonLd?: WithContext<Thing>;\n}\n\nexport interface ForwardedReference {\n\tparentRef: HTMLDivElement;\n\tchildRefs: HTMLDivElement[];\n}\n\ninterface SectionReferenceValue {\n\tel: HTMLElement;\n\ttitle: string;\n\tisSubSection: boolean;\n}\n\ntype SectionReference = Map<string, SectionReferenceValue>;\n\ninterface CategoryTitleValue extends SectionReferenceValue {\n\tlastUpdatedAt: number;\n}\n\ntype CategoryTitle = Map<string, CategoryTitleValue>;\n\nconst Blog = ({\n\tchildren,\n\ttitle = 'In this article',\n\tjsonLd,\n}: BlogProperties) => {\n\tconst sectionReferences = useRef<SectionReference>(new Map());\n\tconst [categoryTitles, setCategoryTitles] = useState<CategoryTitle>(\n\t\tnew Map()\n\t);\n\tconst [visibleTitle, setVisibleTitle] = useState<string | null>(null);\n\tconst [showTOC, setShowTOC] = useState(false);\n\n\tconst updateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst showTOCTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst isClickScrolling = useRef(false);\n\tconst scrollEndHandlerRef = useRef<(() => void) | null>(null);\n\tconst intersectionObserversRef = useRef<Map<string, IntersectionObserver>>(\n\t\tnew Map()\n\t);\n\n\tconst sortByDomPosition = useCallback(\n\t\t(\n\t\t\t[, a]: [string, SectionReferenceValue],\n\t\t\t[, b]: [string, SectionReferenceValue]\n\t\t) => {\n\t\t\tconst position = a.el.compareDocumentPosition(b.el);\n\t\t\tif (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n\t\t\t\treturn -1; // a comes before b\n\t\t\t} else if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n\t\t\t\treturn 1; // b comes before a\n\t\t\t}\n\t\t\treturn 0;\n\t\t},\n\t\t[]\n\t);\n\n\tconst updateCategoryTitles = useCallback(() => {\n\t\tconst now = Date.now();\n\t\tconst newCategoryTitles = new Map<string, CategoryTitleValue>();\n\n\t\t// Sort sections by their DOM position to maintain correct order\n\t\tconst sectionsArray = Array.from(sectionReferences.current.entries());\n\t\tsectionsArray.sort(sortByDomPosition);\n\n\t\tlet firstSectionId: string | null = null;\n\t\tfor (const [id, { title, el, isSubSection }] of sectionsArray) {\n\t\t\tif (!firstSectionId) {\n\t\t\t\tfirstSectionId = id;\n\t\t\t}\n\t\t\tnewCategoryTitles.set(id, {\n\t\t\t\tel,\n\t\t\t\ttitle,\n\t\t\t\tlastUpdatedAt: now,\n\t\t\t\tisSubSection,\n\t\t\t});\n\t\t}\n\n\t\tif (newCategoryTitles.size === 0) return;\n\n\t\tsetCategoryTitles(newCategoryTitles);\n\t\tif (!showTOC) setShowTOC(true);\n\n\t\tif (visibleTitle) return;\n\t\tsetVisibleTitle(firstSectionId);\n\t}, [visibleTitle, sortByDomPosition, showTOC, setShowTOC]);\n\n\tconst debounceUpdateCategoryTitles = useCallback(() => {\n\t\t// Clear existing timer and set a new one to batch updates\n\t\tif (updateTimerRef.current) {\n\t\t\tclearTimeout(updateTimerRef.current);\n\t\t}\n\t\tupdateTimerRef.current = setTimeout(() => {\n\t\t\tupdateCategoryTitles();\n\t\t}, 200);\n\t}, [updateCategoryTitles]);\n\n\tuseEffect(() => {\n\t\tfor (const [id, { el }] of categoryTitles) {\n\t\t\tconst observer = new IntersectionObserver(\n\t\t\t\t([entry]) => {\n\t\t\t\t\tif (!entry.isIntersecting) return;\n\t\t\t\t\tif (isClickScrolling.current) return;\n\t\t\t\t\tif (document.body.scrollTop === 0) return;\n\t\t\t\t\tsetVisibleTitle(visibleId => {\n\t\t\t\t\t\tif (visibleId === id && !entry.isIntersecting) return null;\n\t\t\t\t\t\tif (entry.isIntersecting) return id;\n\t\t\t\t\t\treturn visibleId;\n\t\t\t\t\t});\n\t\t\t\t\tconst url = new URL(window.location.href);\n\t\t\t\t\turl.searchParams.set('section', id);\n\t\t\t\t\twindow.history.replaceState({}, '', url.toString());\n\t\t\t\t},\n\t\t\t\t{ threshold: 0.1 }\n\t\t\t);\n\t\t\tintersectionObserversRef.current.set(id, observer);\n\t\t\tobserver.observe(el as HTMLElement);\n\t\t}\n\t}, [categoryTitles.size]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (updateTimerRef.current) {\n\t\t\t\tclearTimeout(updateTimerRef.current);\n\t\t\t}\n\t\t\tif (showTOCTimerRef.current) {\n\t\t\t\tclearTimeout(showTOCTimerRef.current);\n\t\t\t}\n\t\t\tif (scrollEndHandlerRef.current) {\n\t\t\t\tdocument.body.removeEventListener(\n\t\t\t\t\t'scrollend',\n\t\t\t\t\tscrollEndHandlerRef.current\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (intersectionObserversRef.current) {\n\t\t\t\tfor (const observer of intersectionObserversRef.current.values()) {\n\t\t\t\t\tobserver.disconnect();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}, []);\n\n\tconst getSectionFromUrl = () => {\n\t\tconst url = new URL(window.location.href);\n\t\tconst section = url.searchParams.get('section');\n\t\tif (!section) return null;\n\t\treturn section;\n\t};\n\n\tconst updateUrl = (id: string) => {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('section', id);\n\t\twindow.history.replaceState({}, '', url.toString());\n\t};\n\n\tconst scrollIntoView = (element: HTMLElement) => {\n\t\tif (!element) return;\n\t\tconst top =\n\t\t\telement.getBoundingClientRect().top + document.body.scrollTop - 100;\n\t\tdocument.body.scrollTo({ top, behavior: 'smooth' });\n\t};\n\n\t// On initial load, scroll to section specified in URL\n\tuseEffect(() => {\n\t\tif (categoryTitles.size === 0) return;\n\t\tconst section = getSectionFromUrl();\n\t\tif (!section) {\n\t\t\treturn;\n\t\t}\n\t\tconst entry = categoryTitles.get(section);\n\t\tif (!entry) {\n\t\t\treturn;\n\t\t}\n\t\tscrollIntoView(entry.el);\n\t\tlockScrollUpdates(\n\t\t\tsection,\n\t\t\tisClickScrolling,\n\t\t\tscrollEndHandlerRef,\n\t\t\tsetVisibleTitle\n\t\t);\n\t}, [categoryTitles.size]);\n\n\tconst handleSectionReference = useCallback(\n\t\t(element: ForwardedReference) => {\n\t\t\tif (!element) return;\n\t\t\tconst { parentRef, childRefs } = element;\n\n\t\t\t// Add parent section reference\n\t\t\tif (parentRef) {\n\t\t\t\tconst id = parentRef.dataset.id;\n\t\t\t\tconst title = parentRef.dataset.title;\n\t\t\t\tif (id && title) {\n\t\t\t\t\tsectionReferences.current.set(id, {\n\t\t\t\t\t\tel: parentRef,\n\t\t\t\t\t\ttitle,\n\t\t\t\t\t\tisSubSection: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add child section references\n\t\t\tif (Array.isArray(childRefs)) {\n\t\t\t\tfor (const childRef of childRefs) {\n\t\t\t\t\tif (!childRef) continue;\n\t\t\t\t\tconst id = childRef.dataset.id;\n\t\t\t\t\tconst title = childRef.dataset.title;\n\t\t\t\t\tif (id && title) {\n\t\t\t\t\t\tsectionReferences.current.set(id, {\n\t\t\t\t\t\t\tel: childRef,\n\t\t\t\t\t\t\ttitle,\n\t\t\t\t\t\t\tisSubSection: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdebounceUpdateCategoryTitles();\n\t\t},\n\t\t[debounceUpdateCategoryTitles]\n\t);\n\n\tconst handleClickCategoryTitle = (\n\t\terror: MouseEvent<HTMLParagraphElement>\n\t) => {\n\t\tconst id = error.currentTarget.dataset.id;\n\t\tconst index = error.currentTarget.dataset.idx;\n\t\tif (!id || !index) return;\n\n\t\tconst { el } = categoryTitles.get(id) || {};\n\t\tif (!el) return;\n\n\t\tupdateUrl(id);\n\n\t\tscrollIntoView(el);\n\n\t\tlockScrollUpdates(\n\t\t\tid,\n\t\t\tisClickScrolling,\n\t\t\tscrollEndHandlerRef,\n\t\t\tsetVisibleTitle\n\t\t);\n\t};\n\n\tconst sidebarStyle = useSpring({\n\t\topacity: showTOC ? 1 : 0,\n\t\ttransform: showTOC ? 'translateX(0)' : 'translateX(40px)',\n\t\tconfig: config.gentle,\n\t});\n\n\treturn (\n\t\t<div className={styles.blog}>\n\t\t\t{jsonLd && (\n\t\t\t\t<script\n\t\t\t\t\ttype=\"application/ld+json\"\n\t\t\t\t\tdangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<div className={styles['blog__content']}>\n\t\t\t\t{Children.map(children, child => {\n\t\t\t\t\tif (!isValidElement(child)) return child;\n\t\t\t\t\treturn cloneElement(child, {\n\t\t\t\t\t\tref: handleSectionReference,\n\t\t\t\t\t} as RefAttributes<ForwardedReference>);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t\t<animated.div className={styles['blog__sidebar']} style={sidebarStyle}>\n\t\t\t\t<p\n\t\t\t\t\tclassName={`${styles['margin-bottom--3']} ${styles['category__header']}`}\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</p>\n\t\t\t\t{[...categoryTitles].map(\n\t\t\t\t\t([id, { title, isSubSection }], index, array) => {\n\t\t\t\t\t\tconst isNextSectionSubSection = array[index + 1]?.[1]?.isSubSection;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\tkey={id}\n\t\t\t\t\t\t\t\tdata-idx={index}\n\t\t\t\t\t\t\t\tdata-id={id}\n\t\t\t\t\t\t\t\tclassName={`${styles['category__title']} ${\n\t\t\t\t\t\t\t\t\tid === visibleTitle ? styles['category__title--active'] : ''\n\t\t\t\t\t\t\t\t} ${isSubSection ? styles['category__title--sub'] : ''} ${\n\t\t\t\t\t\t\t\t\tisSubSection && !isNextSectionSubSection\n\t\t\t\t\t\t\t\t\t\t? styles['margin-bottom-imp--2']\n\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\tonClick={handleClickCategoryTitle}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t)}\n\t\t\t</animated.div>\n\t\t</div>\n\t);\n};\n\nexport default Blog;\n"],"names":["useRef","useState","useCallback","useEffect","lockScrollUpdates","useSpring","config","_jsxs","styles","_jsx","Children","isValidElement","cloneElement","animated"],"mappings":"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"}
|
|
@@ -12,9 +12,9 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
12
12
|
const [showTOC, setShowTOC] = useState(false);
|
|
13
13
|
const updateTimerRef = useRef(null);
|
|
14
14
|
const showTOCTimerRef = useRef(null);
|
|
15
|
-
const hasScrolledToInitialSection = useRef(false);
|
|
16
15
|
const isClickScrolling = useRef(false);
|
|
17
16
|
const scrollEndHandlerRef = useRef(null);
|
|
17
|
+
const intersectionObserversRef = useRef(new Map());
|
|
18
18
|
const sortByDomPosition = useCallback(([, a], [, b]) => {
|
|
19
19
|
const position = a.el.compareDocumentPosition(b.el);
|
|
20
20
|
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
@@ -62,13 +62,14 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
62
62
|
}, 200);
|
|
63
63
|
}, [updateCategoryTitles]);
|
|
64
64
|
useEffect(() => {
|
|
65
|
-
const observers = new Map();
|
|
66
65
|
for (const [id, { el }] of categoryTitles) {
|
|
67
66
|
const observer = new IntersectionObserver(([entry]) => {
|
|
68
67
|
if (!entry.isIntersecting)
|
|
69
68
|
return;
|
|
70
69
|
if (isClickScrolling.current)
|
|
71
70
|
return;
|
|
71
|
+
if (document.body.scrollTop === 0)
|
|
72
|
+
return;
|
|
72
73
|
setVisibleTitle(visibleId => {
|
|
73
74
|
if (visibleId === id && !entry.isIntersecting)
|
|
74
75
|
return null;
|
|
@@ -80,14 +81,9 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
80
81
|
url.searchParams.set('section', id);
|
|
81
82
|
window.history.replaceState({}, '', url.toString());
|
|
82
83
|
}, { threshold: 0.1 });
|
|
83
|
-
|
|
84
|
+
intersectionObserversRef.current.set(id, observer);
|
|
84
85
|
observer.observe(el);
|
|
85
86
|
}
|
|
86
|
-
return () => {
|
|
87
|
-
for (const observer of observers.values()) {
|
|
88
|
-
observer.disconnect();
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
87
|
}, [categoryTitles.size]);
|
|
92
88
|
useEffect(() => {
|
|
93
89
|
return () => {
|
|
@@ -100,26 +96,46 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
100
96
|
if (scrollEndHandlerRef.current) {
|
|
101
97
|
document.body.removeEventListener('scrollend', scrollEndHandlerRef.current);
|
|
102
98
|
}
|
|
99
|
+
if (intersectionObserversRef.current) {
|
|
100
|
+
for (const observer of intersectionObserversRef.current.values()) {
|
|
101
|
+
observer.disconnect();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
103
104
|
};
|
|
104
105
|
}, []);
|
|
106
|
+
const getSectionFromUrl = () => {
|
|
107
|
+
const url = new URL(window.location.href);
|
|
108
|
+
const section = url.searchParams.get('section');
|
|
109
|
+
if (!section)
|
|
110
|
+
return null;
|
|
111
|
+
return section;
|
|
112
|
+
};
|
|
113
|
+
const updateUrl = (id) => {
|
|
114
|
+
const url = new URL(window.location.href);
|
|
115
|
+
url.searchParams.set('section', id);
|
|
116
|
+
window.history.replaceState({}, '', url.toString());
|
|
117
|
+
};
|
|
118
|
+
const scrollIntoView = (element) => {
|
|
119
|
+
if (!element)
|
|
120
|
+
return;
|
|
121
|
+
const top = element.getBoundingClientRect().top + document.body.scrollTop - 100;
|
|
122
|
+
document.body.scrollTo({ top, behavior: 'smooth' });
|
|
123
|
+
};
|
|
105
124
|
// On initial load, scroll to section specified in URL
|
|
106
125
|
useEffect(() => {
|
|
107
|
-
if (hasScrolledToInitialSection.current)
|
|
108
|
-
return;
|
|
109
126
|
if (categoryTitles.size === 0)
|
|
110
127
|
return;
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
if (!section)
|
|
128
|
+
const section = getSectionFromUrl();
|
|
129
|
+
if (!section) {
|
|
114
130
|
return;
|
|
131
|
+
}
|
|
115
132
|
const entry = categoryTitles.get(section);
|
|
116
|
-
if (!entry)
|
|
133
|
+
if (!entry) {
|
|
117
134
|
return;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
document.body.scrollTo({ top, behavior: 'smooth' });
|
|
135
|
+
}
|
|
136
|
+
scrollIntoView(entry.el);
|
|
121
137
|
lockScrollUpdates(section, isClickScrolling, scrollEndHandlerRef, setVisibleTitle);
|
|
122
|
-
}, [categoryTitles]);
|
|
138
|
+
}, [categoryTitles.size]);
|
|
123
139
|
const handleSectionReference = useCallback((element) => {
|
|
124
140
|
if (!element)
|
|
125
141
|
return;
|
|
@@ -162,14 +178,8 @@ const Blog = ({ children, title = 'In this article', jsonLd, }) => {
|
|
|
162
178
|
const { el } = categoryTitles.get(id) || {};
|
|
163
179
|
if (!el)
|
|
164
180
|
return;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
url.searchParams.set('section', id);
|
|
168
|
-
window.history.replaceState({}, '', url.toString());
|
|
169
|
-
document.body.scrollTo({
|
|
170
|
-
top,
|
|
171
|
-
behavior: 'smooth',
|
|
172
|
-
});
|
|
181
|
+
updateUrl(id);
|
|
182
|
+
scrollIntoView(el);
|
|
173
183
|
lockScrollUpdates(id, isClickScrolling, scrollEndHandlerRef, setVisibleTitle);
|
|
174
184
|
};
|
|
175
185
|
const sidebarStyle = useSpring({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlogDynamic.js","sources":["../../../src/dynamicComponents/BlogDynamic.tsx"],"sourcesContent":["'use client';\n\nimport {\n\tChildren,\n\tcloneElement,\n\tisValidElement,\n\tuseCallback,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react';\nimport { useSpring, animated, config } from '@react-spring/web';\n\nimport type { MouseEvent, ReactNode, RefAttributes } from 'react';\nimport type { Thing, WithContext } from 'schema-dts';\n\nimport styles from '../styles/Blog.module.scss';\nimport lockScrollUpdates from '../utils/lockScrollUpdates';\n\ninterface BlogProperties {\n\tchildren: ReactNode;\n\ttitle?: string;\n\tjsonLd?: WithContext<Thing>;\n}\n\nexport interface ForwardedReference {\n\tparentRef: HTMLDivElement;\n\tchildRefs: HTMLDivElement[];\n}\n\ninterface SectionReferenceValue {\n\tel: HTMLElement;\n\ttitle: string;\n\tisSubSection: boolean;\n}\n\ntype SectionReference = Map<string, SectionReferenceValue>;\n\ninterface CategoryTitleValue extends SectionReferenceValue {\n\tlastUpdatedAt: number;\n}\n\ntype CategoryTitle = Map<string, CategoryTitleValue>;\n\nconst Blog = ({\n\tchildren,\n\ttitle = 'In this article',\n\tjsonLd,\n}: BlogProperties) => {\n\tconst sectionReferences = useRef<SectionReference>(new Map());\n\tconst [categoryTitles, setCategoryTitles] = useState<CategoryTitle>(\n\t\tnew Map()\n\t);\n\tconst [visibleTitle, setVisibleTitle] = useState<string | null>(null);\n\tconst [showTOC, setShowTOC] = useState(false);\n\n\tconst updateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst showTOCTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst hasScrolledToInitialSection = useRef(false);\n\tconst isClickScrolling = useRef(false);\n\tconst scrollEndHandlerRef = useRef<(() => void) | null>(null);\n\n\tconst sortByDomPosition = useCallback(\n\t\t(\n\t\t\t[, a]: [string, SectionReferenceValue],\n\t\t\t[, b]: [string, SectionReferenceValue]\n\t\t) => {\n\t\t\tconst position = a.el.compareDocumentPosition(b.el);\n\t\t\tif (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n\t\t\t\treturn -1; // a comes before b\n\t\t\t} else if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n\t\t\t\treturn 1; // b comes before a\n\t\t\t}\n\t\t\treturn 0;\n\t\t},\n\t\t[]\n\t);\n\n\tconst updateCategoryTitles = useCallback(() => {\n\t\tconst now = Date.now();\n\t\tconst newCategoryTitles = new Map<string, CategoryTitleValue>();\n\n\t\t// Sort sections by their DOM position to maintain correct order\n\t\tconst sectionsArray = Array.from(sectionReferences.current.entries());\n\t\tsectionsArray.sort(sortByDomPosition);\n\n\t\tlet firstSectionId: string | null = null;\n\t\tfor (const [id, { title, el, isSubSection }] of sectionsArray) {\n\t\t\tif (!firstSectionId) {\n\t\t\t\tfirstSectionId = id;\n\t\t\t}\n\t\t\tnewCategoryTitles.set(id, {\n\t\t\t\tel,\n\t\t\t\ttitle,\n\t\t\t\tlastUpdatedAt: now,\n\t\t\t\tisSubSection,\n\t\t\t});\n\t\t}\n\n\t\tif (newCategoryTitles.size === 0) return;\n\n\t\tsetCategoryTitles(newCategoryTitles);\n\t\tif (!showTOC) setShowTOC(true);\n\n\t\tif (visibleTitle) return;\n\t\tsetVisibleTitle(firstSectionId);\n\t}, [visibleTitle, sortByDomPosition, showTOC, setShowTOC]);\n\n\tconst debounceUpdateCategoryTitles = useCallback(() => {\n\t\t// Clear existing timer and set a new one to batch updates\n\t\tif (updateTimerRef.current) {\n\t\t\tclearTimeout(updateTimerRef.current);\n\t\t}\n\t\tupdateTimerRef.current = setTimeout(() => {\n\t\t\tupdateCategoryTitles();\n\t\t}, 200);\n\t}, [updateCategoryTitles]);\n\n\tuseEffect(() => {\n\t\tconst observers = new Map<string, IntersectionObserver>();\n\t\tfor (const [id, { el }] of categoryTitles) {\n\t\t\tconst observer = new IntersectionObserver(\n\t\t\t\t([entry]) => {\n\t\t\t\t\tif (!entry.isIntersecting) return;\n\t\t\t\t\tif (isClickScrolling.current) return;\n\t\t\t\t\tsetVisibleTitle(visibleId => {\n\t\t\t\t\t\tif (visibleId === id && !entry.isIntersecting) return null;\n\t\t\t\t\t\tif (entry.isIntersecting) return id;\n\t\t\t\t\t\treturn visibleId;\n\t\t\t\t\t});\n\t\t\t\t\tconst url = new URL(window.location.href);\n\t\t\t\t\turl.searchParams.set('section', id);\n\t\t\t\t\twindow.history.replaceState({}, '', url.toString());\n\t\t\t\t},\n\t\t\t\t{ threshold: 0.1 }\n\t\t\t);\n\t\t\tobservers.set(id, observer);\n\t\t\tobserver.observe(el);\n\t\t}\n\t\treturn () => {\n\t\t\tfor (const observer of observers.values()) {\n\t\t\t\tobserver.disconnect();\n\t\t\t}\n\t\t};\n\t}, [categoryTitles.size]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (updateTimerRef.current) {\n\t\t\t\tclearTimeout(updateTimerRef.current);\n\t\t\t}\n\t\t\tif (showTOCTimerRef.current) {\n\t\t\t\tclearTimeout(showTOCTimerRef.current);\n\t\t\t}\n\t\t\tif (scrollEndHandlerRef.current) {\n\t\t\t\tdocument.body.removeEventListener('scrollend', scrollEndHandlerRef.current);\n\t\t\t}\n\t\t};\n\t}, []);\n\n\t// On initial load, scroll to section specified in URL\n\tuseEffect(() => {\n\t\tif (hasScrolledToInitialSection.current) return;\n\t\tif (categoryTitles.size === 0) return;\n\t\tconst url = new URL(window.location.href);\n\t\tconst section = url.searchParams.get('section');\n\t\tif (!section) return;\n\t\tconst entry = categoryTitles.get(section);\n\t\tif (!entry) return;\n\t\thasScrolledToInitialSection.current = true;\n\t\tconst top =\n\t\t\tentry.el.getBoundingClientRect().top + document.body.scrollTop - 100;\n\t\tdocument.body.scrollTo({ top, behavior: 'smooth' });\n\t\tlockScrollUpdates(section, isClickScrolling, scrollEndHandlerRef, setVisibleTitle);\n\t}, [categoryTitles]);\n\n\tconst handleSectionReference = useCallback(\n\t\t(element: ForwardedReference) => {\n\t\t\tif (!element) return;\n\t\t\tconst { parentRef, childRefs } = element;\n\n\t\t\t// Add parent section reference\n\t\t\tif (parentRef) {\n\t\t\t\tconst id = parentRef.dataset.id;\n\t\t\t\tconst title = parentRef.dataset.title;\n\t\t\t\tif (id && title) {\n\t\t\t\t\tsectionReferences.current.set(id, {\n\t\t\t\t\t\tel: parentRef,\n\t\t\t\t\t\ttitle,\n\t\t\t\t\t\tisSubSection: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add child section references\n\t\t\tif (Array.isArray(childRefs)) {\n\t\t\t\tfor (const childRef of childRefs) {\n\t\t\t\t\tif (!childRef) continue;\n\t\t\t\t\tconst id = childRef.dataset.id;\n\t\t\t\t\tconst title = childRef.dataset.title;\n\t\t\t\t\tif (id && title) {\n\t\t\t\t\t\tsectionReferences.current.set(id, {\n\t\t\t\t\t\t\tel: childRef,\n\t\t\t\t\t\t\ttitle,\n\t\t\t\t\t\t\tisSubSection: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdebounceUpdateCategoryTitles();\n\t\t},\n\t\t[debounceUpdateCategoryTitles]\n\t);\n\n\tconst handleClickCategoryTitle = (\n\t\terror: MouseEvent<HTMLParagraphElement>\n\t) => {\n\t\tconst id = error.currentTarget.dataset.id;\n\t\tconst index = error.currentTarget.dataset.idx;\n\t\tif (!id || !index) return;\n\n\t\tconst { el } = categoryTitles.get(id) || {};\n\t\tif (!el) return;\n\n\t\tconst top = el.getBoundingClientRect().top + document.body.scrollTop - 100;\n\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('section', id);\n\t\twindow.history.replaceState({}, '', url.toString());\n\n\t\tdocument.body.scrollTo({\n\t\t\ttop,\n\t\t\tbehavior: 'smooth',\n\t\t});\n\n\t\tlockScrollUpdates(id, isClickScrolling, scrollEndHandlerRef, setVisibleTitle);\n\t};\n\n\tconst sidebarStyle = useSpring({\n\t\topacity: showTOC ? 1 : 0,\n\t\ttransform: showTOC ? 'translateX(0)' : 'translateX(40px)',\n\t\tconfig: config.gentle,\n\t});\n\n\treturn (\n\t\t<div className={styles.blog}>\n\t\t\t{jsonLd && (\n\t\t\t\t<script\n\t\t\t\t\ttype=\"application/ld+json\"\n\t\t\t\t\tdangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<div className={styles['blog__content']}>\n\t\t\t\t{Children.map(children, child => {\n\t\t\t\t\tif (!isValidElement(child)) return child;\n\t\t\t\t\treturn cloneElement(child, {\n\t\t\t\t\t\tref: handleSectionReference,\n\t\t\t\t\t} as RefAttributes<ForwardedReference>);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t\t<animated.div className={styles['blog__sidebar']} style={sidebarStyle}>\n\t\t\t\t<p\n\t\t\t\t\tclassName={`${styles['margin-bottom--3']} ${styles['category__header']}`}\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</p>\n\t\t\t\t{[...categoryTitles].map(\n\t\t\t\t\t([id, { title, isSubSection }], index, array) => {\n\t\t\t\t\t\tconst isNextSectionSubSection = array[index + 1]?.[1]?.isSubSection;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\tkey={id}\n\t\t\t\t\t\t\t\tdata-idx={index}\n\t\t\t\t\t\t\t\tdata-id={id}\n\t\t\t\t\t\t\t\tclassName={`${styles['category__title']} ${\n\t\t\t\t\t\t\t\t\tid === visibleTitle ? styles['category__title--active'] : ''\n\t\t\t\t\t\t\t\t} ${isSubSection ? styles['category__title--sub'] : ''} ${\n\t\t\t\t\t\t\t\t\tisSubSection && !isNextSectionSubSection\n\t\t\t\t\t\t\t\t\t\t? styles['margin-bottom-imp--2']\n\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\tonClick={handleClickCategoryTitle}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t)}\n\t\t\t</animated.div>\n\t\t</div>\n\t);\n};\n\nexport default Blog;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BlogDynamic.js","sources":["../../../src/dynamicComponents/BlogDynamic.tsx"],"sourcesContent":["'use client';\n\nimport {\n\tChildren,\n\tcloneElement,\n\tisValidElement,\n\tuseCallback,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react';\nimport { useSpring, animated, config } from '@react-spring/web';\n\nimport type { MouseEvent, ReactNode, RefAttributes } from 'react';\nimport type { Thing, WithContext } from 'schema-dts';\n\nimport styles from '../styles/Blog.module.scss';\nimport lockScrollUpdates from '../utils/lockScrollUpdates';\n\ninterface BlogProperties {\n\tchildren: ReactNode;\n\ttitle?: string;\n\tjsonLd?: WithContext<Thing>;\n}\n\nexport interface ForwardedReference {\n\tparentRef: HTMLDivElement;\n\tchildRefs: HTMLDivElement[];\n}\n\ninterface SectionReferenceValue {\n\tel: HTMLElement;\n\ttitle: string;\n\tisSubSection: boolean;\n}\n\ntype SectionReference = Map<string, SectionReferenceValue>;\n\ninterface CategoryTitleValue extends SectionReferenceValue {\n\tlastUpdatedAt: number;\n}\n\ntype CategoryTitle = Map<string, CategoryTitleValue>;\n\nconst Blog = ({\n\tchildren,\n\ttitle = 'In this article',\n\tjsonLd,\n}: BlogProperties) => {\n\tconst sectionReferences = useRef<SectionReference>(new Map());\n\tconst [categoryTitles, setCategoryTitles] = useState<CategoryTitle>(\n\t\tnew Map()\n\t);\n\tconst [visibleTitle, setVisibleTitle] = useState<string | null>(null);\n\tconst [showTOC, setShowTOC] = useState(false);\n\n\tconst updateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst showTOCTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst isClickScrolling = useRef(false);\n\tconst scrollEndHandlerRef = useRef<(() => void) | null>(null);\n\tconst intersectionObserversRef = useRef<Map<string, IntersectionObserver>>(\n\t\tnew Map()\n\t);\n\n\tconst sortByDomPosition = useCallback(\n\t\t(\n\t\t\t[, a]: [string, SectionReferenceValue],\n\t\t\t[, b]: [string, SectionReferenceValue]\n\t\t) => {\n\t\t\tconst position = a.el.compareDocumentPosition(b.el);\n\t\t\tif (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n\t\t\t\treturn -1; // a comes before b\n\t\t\t} else if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n\t\t\t\treturn 1; // b comes before a\n\t\t\t}\n\t\t\treturn 0;\n\t\t},\n\t\t[]\n\t);\n\n\tconst updateCategoryTitles = useCallback(() => {\n\t\tconst now = Date.now();\n\t\tconst newCategoryTitles = new Map<string, CategoryTitleValue>();\n\n\t\t// Sort sections by their DOM position to maintain correct order\n\t\tconst sectionsArray = Array.from(sectionReferences.current.entries());\n\t\tsectionsArray.sort(sortByDomPosition);\n\n\t\tlet firstSectionId: string | null = null;\n\t\tfor (const [id, { title, el, isSubSection }] of sectionsArray) {\n\t\t\tif (!firstSectionId) {\n\t\t\t\tfirstSectionId = id;\n\t\t\t}\n\t\t\tnewCategoryTitles.set(id, {\n\t\t\t\tel,\n\t\t\t\ttitle,\n\t\t\t\tlastUpdatedAt: now,\n\t\t\t\tisSubSection,\n\t\t\t});\n\t\t}\n\n\t\tif (newCategoryTitles.size === 0) return;\n\n\t\tsetCategoryTitles(newCategoryTitles);\n\t\tif (!showTOC) setShowTOC(true);\n\n\t\tif (visibleTitle) return;\n\t\tsetVisibleTitle(firstSectionId);\n\t}, [visibleTitle, sortByDomPosition, showTOC, setShowTOC]);\n\n\tconst debounceUpdateCategoryTitles = useCallback(() => {\n\t\t// Clear existing timer and set a new one to batch updates\n\t\tif (updateTimerRef.current) {\n\t\t\tclearTimeout(updateTimerRef.current);\n\t\t}\n\t\tupdateTimerRef.current = setTimeout(() => {\n\t\t\tupdateCategoryTitles();\n\t\t}, 200);\n\t}, [updateCategoryTitles]);\n\n\tuseEffect(() => {\n\t\tfor (const [id, { el }] of categoryTitles) {\n\t\t\tconst observer = new IntersectionObserver(\n\t\t\t\t([entry]) => {\n\t\t\t\t\tif (!entry.isIntersecting) return;\n\t\t\t\t\tif (isClickScrolling.current) return;\n\t\t\t\t\tif (document.body.scrollTop === 0) return;\n\t\t\t\t\tsetVisibleTitle(visibleId => {\n\t\t\t\t\t\tif (visibleId === id && !entry.isIntersecting) return null;\n\t\t\t\t\t\tif (entry.isIntersecting) return id;\n\t\t\t\t\t\treturn visibleId;\n\t\t\t\t\t});\n\t\t\t\t\tconst url = new URL(window.location.href);\n\t\t\t\t\turl.searchParams.set('section', id);\n\t\t\t\t\twindow.history.replaceState({}, '', url.toString());\n\t\t\t\t},\n\t\t\t\t{ threshold: 0.1 }\n\t\t\t);\n\t\t\tintersectionObserversRef.current.set(id, observer);\n\t\t\tobserver.observe(el as HTMLElement);\n\t\t}\n\t}, [categoryTitles.size]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (updateTimerRef.current) {\n\t\t\t\tclearTimeout(updateTimerRef.current);\n\t\t\t}\n\t\t\tif (showTOCTimerRef.current) {\n\t\t\t\tclearTimeout(showTOCTimerRef.current);\n\t\t\t}\n\t\t\tif (scrollEndHandlerRef.current) {\n\t\t\t\tdocument.body.removeEventListener(\n\t\t\t\t\t'scrollend',\n\t\t\t\t\tscrollEndHandlerRef.current\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (intersectionObserversRef.current) {\n\t\t\t\tfor (const observer of intersectionObserversRef.current.values()) {\n\t\t\t\t\tobserver.disconnect();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}, []);\n\n\tconst getSectionFromUrl = () => {\n\t\tconst url = new URL(window.location.href);\n\t\tconst section = url.searchParams.get('section');\n\t\tif (!section) return null;\n\t\treturn section;\n\t};\n\n\tconst updateUrl = (id: string) => {\n\t\tconst url = new URL(window.location.href);\n\t\turl.searchParams.set('section', id);\n\t\twindow.history.replaceState({}, '', url.toString());\n\t};\n\n\tconst scrollIntoView = (element: HTMLElement) => {\n\t\tif (!element) return;\n\t\tconst top =\n\t\t\telement.getBoundingClientRect().top + document.body.scrollTop - 100;\n\t\tdocument.body.scrollTo({ top, behavior: 'smooth' });\n\t};\n\n\t// On initial load, scroll to section specified in URL\n\tuseEffect(() => {\n\t\tif (categoryTitles.size === 0) return;\n\t\tconst section = getSectionFromUrl();\n\t\tif (!section) {\n\t\t\treturn;\n\t\t}\n\t\tconst entry = categoryTitles.get(section);\n\t\tif (!entry) {\n\t\t\treturn;\n\t\t}\n\t\tscrollIntoView(entry.el);\n\t\tlockScrollUpdates(\n\t\t\tsection,\n\t\t\tisClickScrolling,\n\t\t\tscrollEndHandlerRef,\n\t\t\tsetVisibleTitle\n\t\t);\n\t}, [categoryTitles.size]);\n\n\tconst handleSectionReference = useCallback(\n\t\t(element: ForwardedReference) => {\n\t\t\tif (!element) return;\n\t\t\tconst { parentRef, childRefs } = element;\n\n\t\t\t// Add parent section reference\n\t\t\tif (parentRef) {\n\t\t\t\tconst id = parentRef.dataset.id;\n\t\t\t\tconst title = parentRef.dataset.title;\n\t\t\t\tif (id && title) {\n\t\t\t\t\tsectionReferences.current.set(id, {\n\t\t\t\t\t\tel: parentRef,\n\t\t\t\t\t\ttitle,\n\t\t\t\t\t\tisSubSection: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add child section references\n\t\t\tif (Array.isArray(childRefs)) {\n\t\t\t\tfor (const childRef of childRefs) {\n\t\t\t\t\tif (!childRef) continue;\n\t\t\t\t\tconst id = childRef.dataset.id;\n\t\t\t\t\tconst title = childRef.dataset.title;\n\t\t\t\t\tif (id && title) {\n\t\t\t\t\t\tsectionReferences.current.set(id, {\n\t\t\t\t\t\t\tel: childRef,\n\t\t\t\t\t\t\ttitle,\n\t\t\t\t\t\t\tisSubSection: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdebounceUpdateCategoryTitles();\n\t\t},\n\t\t[debounceUpdateCategoryTitles]\n\t);\n\n\tconst handleClickCategoryTitle = (\n\t\terror: MouseEvent<HTMLParagraphElement>\n\t) => {\n\t\tconst id = error.currentTarget.dataset.id;\n\t\tconst index = error.currentTarget.dataset.idx;\n\t\tif (!id || !index) return;\n\n\t\tconst { el } = categoryTitles.get(id) || {};\n\t\tif (!el) return;\n\n\t\tupdateUrl(id);\n\n\t\tscrollIntoView(el);\n\n\t\tlockScrollUpdates(\n\t\t\tid,\n\t\t\tisClickScrolling,\n\t\t\tscrollEndHandlerRef,\n\t\t\tsetVisibleTitle\n\t\t);\n\t};\n\n\tconst sidebarStyle = useSpring({\n\t\topacity: showTOC ? 1 : 0,\n\t\ttransform: showTOC ? 'translateX(0)' : 'translateX(40px)',\n\t\tconfig: config.gentle,\n\t});\n\n\treturn (\n\t\t<div className={styles.blog}>\n\t\t\t{jsonLd && (\n\t\t\t\t<script\n\t\t\t\t\ttype=\"application/ld+json\"\n\t\t\t\t\tdangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<div className={styles['blog__content']}>\n\t\t\t\t{Children.map(children, child => {\n\t\t\t\t\tif (!isValidElement(child)) return child;\n\t\t\t\t\treturn cloneElement(child, {\n\t\t\t\t\t\tref: handleSectionReference,\n\t\t\t\t\t} as RefAttributes<ForwardedReference>);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t\t<animated.div className={styles['blog__sidebar']} style={sidebarStyle}>\n\t\t\t\t<p\n\t\t\t\t\tclassName={`${styles['margin-bottom--3']} ${styles['category__header']}`}\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</p>\n\t\t\t\t{[...categoryTitles].map(\n\t\t\t\t\t([id, { title, isSubSection }], index, array) => {\n\t\t\t\t\t\tconst isNextSectionSubSection = array[index + 1]?.[1]?.isSubSection;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\tkey={id}\n\t\t\t\t\t\t\t\tdata-idx={index}\n\t\t\t\t\t\t\t\tdata-id={id}\n\t\t\t\t\t\t\t\tclassName={`${styles['category__title']} ${\n\t\t\t\t\t\t\t\t\tid === visibleTitle ? styles['category__title--active'] : ''\n\t\t\t\t\t\t\t\t} ${isSubSection ? styles['category__title--sub'] : ''} ${\n\t\t\t\t\t\t\t\t\tisSubSection && !isNextSectionSubSection\n\t\t\t\t\t\t\t\t\t\t? styles['margin-bottom-imp--2']\n\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\tonClick={handleClickCategoryTitle}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t)}\n\t\t\t</animated.div>\n\t\t</div>\n\t);\n};\n\nexport default Blog;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlogDynamic.d.ts","sourceRoot":"","sources":["../../../src/dynamicComponents/BlogDynamic.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAc,SAAS,EAAiB,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAKrD,UAAU,cAAc;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,cAAc,EAAE,CAAC;CAC5B;AAgBD,QAAA,MAAM,IAAI,GAAI,8BAIX,cAAc,
|
|
1
|
+
{"version":3,"file":"BlogDynamic.d.ts","sourceRoot":"","sources":["../../../src/dynamicComponents/BlogDynamic.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAc,SAAS,EAAiB,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAKrD,UAAU,cAAc;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,cAAc,EAAE,CAAC;CAC5B;AAgBD,QAAA,MAAM,IAAI,GAAI,8BAIX,cAAc,4CAoPhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlogSectionDynamic.d.ts","sourceRoot":"","sources":["../../../src/dynamicComponents/BlogSectionDynamic.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAItD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGxD,UAAU,cAAc;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,QAAA,MAAM,WAAW,+
|
|
1
|
+
{"version":3,"file":"BlogSectionDynamic.d.ts","sourceRoot":"","sources":["../../../src/dynamicComponents/BlogSectionDynamic.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAItD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGxD,UAAU,cAAc;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,QAAA,MAAM,WAAW,+FAkEhB,CAAC;AAIF,eAAe,WAAW,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,GAAI,OAAO,MAAM,WAAiD,CAAC;AAEtG,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,WAAoE,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,GAAI,OAAO,MAAM,WAAiD,CAAC;AAEtG,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,WAAoE,CAAC;AAE1H,eAAO,MAAM,mBAAmB,GAAI,IAAI,MAAM,WAAqB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { MutableRefObject } from 'react';
|
|
2
|
+
declare const lockScrollUpdates: (id: string, isClickScrolling: MutableRefObject<boolean>, scrollEndHandlerRef: MutableRefObject<(() => void) | null>, setVisibleTitle: (id: string) => void) => void;
|
|
3
|
+
export default lockScrollUpdates;
|
|
4
|
+
//# sourceMappingURL=lockScrollUpdates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockScrollUpdates.d.ts","sourceRoot":"","sources":["../../../src/utils/lockScrollUpdates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAE9C,QAAA,MAAM,iBAAiB,GACtB,IAAI,MAAM,EACV,kBAAkB,gBAAgB,CAAC,OAAO,CAAC,EAC3C,qBAAqB,gBAAgB,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,EAC1D,iBAAiB,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,SAoBrC,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@san-siva/blogkit",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.18",
|
|
4
4
|
"description": "A reusable blog component library for React/Next.js applications with code highlighting, diagrams, and rich content features",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -56,9 +56,11 @@ const Blog = ({
|
|
|
56
56
|
|
|
57
57
|
const updateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
58
58
|
const showTOCTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
59
|
-
const hasScrolledToInitialSection = useRef(false);
|
|
60
59
|
const isClickScrolling = useRef(false);
|
|
61
60
|
const scrollEndHandlerRef = useRef<(() => void) | null>(null);
|
|
61
|
+
const intersectionObserversRef = useRef<Map<string, IntersectionObserver>>(
|
|
62
|
+
new Map()
|
|
63
|
+
);
|
|
62
64
|
|
|
63
65
|
const sortByDomPosition = useCallback(
|
|
64
66
|
(
|
|
@@ -117,12 +119,12 @@ const Blog = ({
|
|
|
117
119
|
}, [updateCategoryTitles]);
|
|
118
120
|
|
|
119
121
|
useEffect(() => {
|
|
120
|
-
const observers = new Map<string, IntersectionObserver>();
|
|
121
122
|
for (const [id, { el }] of categoryTitles) {
|
|
122
123
|
const observer = new IntersectionObserver(
|
|
123
124
|
([entry]) => {
|
|
124
125
|
if (!entry.isIntersecting) return;
|
|
125
126
|
if (isClickScrolling.current) return;
|
|
127
|
+
if (document.body.scrollTop === 0) return;
|
|
126
128
|
setVisibleTitle(visibleId => {
|
|
127
129
|
if (visibleId === id && !entry.isIntersecting) return null;
|
|
128
130
|
if (entry.isIntersecting) return id;
|
|
@@ -134,14 +136,9 @@ const Blog = ({
|
|
|
134
136
|
},
|
|
135
137
|
{ threshold: 0.1 }
|
|
136
138
|
);
|
|
137
|
-
|
|
138
|
-
observer.observe(el);
|
|
139
|
+
intersectionObserversRef.current.set(id, observer);
|
|
140
|
+
observer.observe(el as HTMLElement);
|
|
139
141
|
}
|
|
140
|
-
return () => {
|
|
141
|
-
for (const observer of observers.values()) {
|
|
142
|
-
observer.disconnect();
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
142
|
}, [categoryTitles.size]);
|
|
146
143
|
|
|
147
144
|
useEffect(() => {
|
|
@@ -153,26 +150,58 @@ const Blog = ({
|
|
|
153
150
|
clearTimeout(showTOCTimerRef.current);
|
|
154
151
|
}
|
|
155
152
|
if (scrollEndHandlerRef.current) {
|
|
156
|
-
document.body.removeEventListener(
|
|
153
|
+
document.body.removeEventListener(
|
|
154
|
+
'scrollend',
|
|
155
|
+
scrollEndHandlerRef.current
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (intersectionObserversRef.current) {
|
|
159
|
+
for (const observer of intersectionObserversRef.current.values()) {
|
|
160
|
+
observer.disconnect();
|
|
161
|
+
}
|
|
157
162
|
}
|
|
158
163
|
};
|
|
159
164
|
}, []);
|
|
160
165
|
|
|
161
|
-
|
|
162
|
-
useEffect(() => {
|
|
163
|
-
if (hasScrolledToInitialSection.current) return;
|
|
164
|
-
if (categoryTitles.size === 0) return;
|
|
166
|
+
const getSectionFromUrl = () => {
|
|
165
167
|
const url = new URL(window.location.href);
|
|
166
168
|
const section = url.searchParams.get('section');
|
|
167
|
-
if (!section) return;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
if (!section) return null;
|
|
170
|
+
return section;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const updateUrl = (id: string) => {
|
|
174
|
+
const url = new URL(window.location.href);
|
|
175
|
+
url.searchParams.set('section', id);
|
|
176
|
+
window.history.replaceState({}, '', url.toString());
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const scrollIntoView = (element: HTMLElement) => {
|
|
180
|
+
if (!element) return;
|
|
171
181
|
const top =
|
|
172
|
-
|
|
182
|
+
element.getBoundingClientRect().top + document.body.scrollTop - 100;
|
|
173
183
|
document.body.scrollTo({ top, behavior: 'smooth' });
|
|
174
|
-
|
|
175
|
-
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// On initial load, scroll to section specified in URL
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
if (categoryTitles.size === 0) return;
|
|
189
|
+
const section = getSectionFromUrl();
|
|
190
|
+
if (!section) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const entry = categoryTitles.get(section);
|
|
194
|
+
if (!entry) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
scrollIntoView(entry.el);
|
|
198
|
+
lockScrollUpdates(
|
|
199
|
+
section,
|
|
200
|
+
isClickScrolling,
|
|
201
|
+
scrollEndHandlerRef,
|
|
202
|
+
setVisibleTitle
|
|
203
|
+
);
|
|
204
|
+
}, [categoryTitles.size]);
|
|
176
205
|
|
|
177
206
|
const handleSectionReference = useCallback(
|
|
178
207
|
(element: ForwardedReference) => {
|
|
@@ -223,18 +252,16 @@ const Blog = ({
|
|
|
223
252
|
const { el } = categoryTitles.get(id) || {};
|
|
224
253
|
if (!el) return;
|
|
225
254
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const url = new URL(window.location.href);
|
|
229
|
-
url.searchParams.set('section', id);
|
|
230
|
-
window.history.replaceState({}, '', url.toString());
|
|
255
|
+
updateUrl(id);
|
|
231
256
|
|
|
232
|
-
|
|
233
|
-
top,
|
|
234
|
-
behavior: 'smooth',
|
|
235
|
-
});
|
|
257
|
+
scrollIntoView(el);
|
|
236
258
|
|
|
237
|
-
lockScrollUpdates(
|
|
259
|
+
lockScrollUpdates(
|
|
260
|
+
id,
|
|
261
|
+
isClickScrolling,
|
|
262
|
+
scrollEndHandlerRef,
|
|
263
|
+
setVisibleTitle
|
|
264
|
+
);
|
|
238
265
|
};
|
|
239
266
|
|
|
240
267
|
const sidebarStyle = useSpring({
|