Skip to content

Commit 7c4e0a4

Browse files
committed
created the initial project library
1 parent ecf20fc commit 7c4e0a4

File tree

7 files changed

+254
-0
lines changed

7 files changed

+254
-0
lines changed

.npmignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
src
2+
tsconfig.json

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Ignore artifacts:
2+
dist

.prettierrc.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"arrowParens": "always",
3+
"bracketSameLine": false,
4+
"bracketSpacing": true,
5+
"embeddedLanguageFormatting": "auto",
6+
"htmlWhitespaceSensitivity": "css",
7+
"insertPragma": false,
8+
"jsxSingleQuote": false,
9+
"printWidth": 80,
10+
"proseWrap": "preserve",
11+
"quoteProps": "as-needed",
12+
"requirePragma": false,
13+
"semi": true,
14+
"singleQuote": false,
15+
"tabWidth": 2,
16+
"trailingComma": "es5",
17+
"useTabs": false,
18+
"vueIndentScriptAndStyle": false
19+
}

package.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "use-simple-scroll-spy",
3+
"version": "1.0.0",
4+
"description": "Tiny react hook that provides scroll spying",
5+
"repository": {
6+
"type": "git",
7+
"url": "https://github.com/underfisk/use-simple-scroll-spy"
8+
},
9+
"main": "dist/index.js",
10+
"scripts": {
11+
"build": "tsc",
12+
"prepublish": "pnpm run build"
13+
},
14+
"devDependencies": {
15+
"@types/lodash.throttle": "^4.1.7",
16+
"@types/react": "^18.0.15",
17+
"prettier": "^2.7.1",
18+
"react": "^18.2.0",
19+
"react-dom": "^18.2.0",
20+
"typescript": "^4.7.4"
21+
},
22+
"peerDependencies": {
23+
"react": ">= 16.x"
24+
},
25+
"keywords": [
26+
"react",
27+
"react scroll spy",
28+
"scroll spy",
29+
"scroll"
30+
],
31+
"author": "underfisk",
32+
"license": "MIT",
33+
"dependencies": {
34+
"lodash.throttle": "^4.1.1"
35+
}
36+
}

pnpm-lock.yaml

Lines changed: 103 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { useState, useEffect } from "react";
2+
import throttle from "lodash.throttle";
3+
/**
4+
* Tracks the given element ids and returns the active section
5+
* @param elementIds
6+
* @returns the index of the selected id in the array
7+
*/
8+
export const useSimplyScrollSpy = (elementIds: string[]) => {
9+
const [activeSection, setActiveSection] = useState(() => {
10+
const currentHash = location.hash;
11+
const targetIndex = currentHash
12+
? elementIds.findIndex((id) => `#${id}` === currentHash)
13+
: undefined;
14+
15+
if (targetIndex && targetIndex > 0) {
16+
return targetIndex;
17+
}
18+
return 0;
19+
});
20+
21+
// Sync the URL hash
22+
useEffect(() => {
23+
const currentHash = location.hash;
24+
const sectionHash = `#${elementIds[activeSection]}`;
25+
// If we have the updated hash just ignore
26+
if (currentHash === sectionHash) {
27+
return;
28+
}
29+
history.pushState(undefined, "", sectionHash);
30+
}, [activeSection]);
31+
32+
const handle = throttle(() => {
33+
let currentSectionId = activeSection;
34+
const sectionElements: HTMLElement[] = [];
35+
for (const id of elementIds) {
36+
const ele = document.getElementById(id);
37+
if (ele) {
38+
sectionElements.push(ele);
39+
}
40+
}
41+
42+
for (let i = 0; i < sectionElements.length; i++) {
43+
const section = sectionElements[i];
44+
// Needs to be a valid DOM Element
45+
if (!section || !(section instanceof Element)) continue;
46+
// GetBoundingClientRect returns values relative to viewport
47+
if (section.getBoundingClientRect().top + -80 < 0) {
48+
currentSectionId = i;
49+
continue;
50+
}
51+
// No need to continue loop, if last element has been detected
52+
break;
53+
}
54+
55+
setActiveSection(currentSectionId);
56+
}, 100);
57+
58+
useEffect(() => {
59+
window.addEventListener("scroll", handle);
60+
61+
// Run initially
62+
handle();
63+
64+
return () => {
65+
window.removeEventListener("scroll", handle);
66+
};
67+
}, [elementIds]);
68+
69+
return activeSection;
70+
};

tsconfig.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"useDefineForClassFields": true,
5+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
6+
"allowJs": false,
7+
"skipLibCheck": true,
8+
"esModuleInterop": false,
9+
"allowSyntheticDefaultImports": true,
10+
"strict": true,
11+
"forceConsistentCasingInFileNames": true,
12+
"module": "ESNext",
13+
"moduleResolution": "Node",
14+
"resolveJsonModule": true,
15+
"isolatedModules": true,
16+
"noEmit": false,
17+
"removeComments": false,
18+
"jsx": "react-jsx",
19+
"outDir": "dist"
20+
},
21+
"include": ["src"]
22+
}

0 commit comments

Comments
 (0)