@alex-fitzgerald/parchment
v0.0.5
Published
I love little scrollspies, particularly `notion's`. While they're simple as anything, I wanted to make my own.
Readme
📜 Parchment
I love little scrollspies, particularly notion's. While they're simple as anything, I wanted to make my own.
Parchment is a simple wee handful of headless components to help compose scrollspies. No dependencies other than the usual react
_Scroll_able... Parchment... 📜
Check out the demo here
Installation
npm install parchmentDone. No dependencies outside of react and react-dom.
Usage
First off the bat: we're using Context, so you'll need to wrap any parchment fellas with the ParchmentProvider.
Parchment
The scroll, which will have sections that can be scrolled to. Can accept any ReactNode children.
Only ParchmentSections are observed by our parchment scrollspy.
import { Parchment } from 'parchment';
interface ParchmentOptions {
/**
* Sets the intersection threshold for all parchment sections.
*
* @default 0.5
*/
intersectionThreshold?: number;
/**
* Sets options for the `scrollIntoView` method, when a ParchmentButton is clicked.
*
* @default { behavior: 'smooth', block: 'center' }
*/
scrollIntoViewOptions?: ScrollIntoViewOptions;
/**
* Whether to enable scroll-snapping on the parchment container.
*
* @default false
*/
snap?: boolean;
}
return (
<Parchment snap={true} scrollIntoViewOptions={{ block: 'start' }} intersectionThreshold={0.3}>
{...sections}
</Parchment>
)
Sections
Any 'scrollable' sections should be wrapped in a ParchmentSection component. This will register the section with the context.
import { Parchment, ParchmentSection } from 'parchment';
function MyScrollableSections() {
return (
<Parchment>
<ParchmentSection section="my-section">
{/* Your content here */}
</ParchmentSection>
</Parchment>
);
}Buttons
Parchment buttons are wired up to know when their associated section is in view. An 'active' class is applied to the button, or passed as a boolean argument to a function.
.active class
import ParchmentButton from 'parchment';
/*
* .parchment-button.active {
* color: rebeccapurple;
* }
*/
function MyButton() {
return (
<ParchmentButton section="my-section">
My Section Name
</ParchmentButton>
);
}active argument
/**
* If the `ParchmentButton` child is a function, it is called
* with a boolean reflecting whether the section is in view.
*/
function MyButton() {
return (
<ParchmentButton section="my-section">
{(active) => (
<div style={{ color: active ? 'rebeccapurple' : 'blue' }}>
My Section Name
</div>
)}
</ParchmentButton>
);
}So, at the end of the day, we're up and away with the following:
import { Parchment, ParchmentSection, ParchmentButton } from 'parchment';
function MyScrollableSections() {
return (
<ParchmentProvider>
<ParchmentButton section="my-section">
My Section Name
</ParchmentButton>
<Parchment>
<ParchmentSection section="my-section">
{/* Your content here */}
</ParchmentSection>
</Parchment>
</ParchmentProvider>
);
}Other APIS
useParchment
The useParchment hook provides a few useful functions for interacting with the Parchment context.
scrollTo
Uses the window's native scrollIntoView method to scroll to a section by key, but with options loaded in the parchment context.
inView
Returns the key of the section currently in viewport, e.g.:
import { useParchment } from 'parchment';
function MyComponent() {
const { scrollTo, inView } = useParchment();
console.log(inView); // 'my-section'
return (
<button onClick={() => scrollTo('my-section')}>
Scroll to my section
</button>
);
}ScrollOptions
Parchment accepts the global ScrollIntoViewOptions object as a prop.
Contributions
Feel free to contribute. Has the default vite scripts, so to get up and running after cloning just run npm run dev.
TODO:
- [ ] Add tests
- [ ] Add more features
- [ ] Add horizontal/vertical option
- [ ] Add
asChildprop toParchmentButtonto allow for custom components - [ ] Add
thresholdprop toParchmentButtonto allow for custom scroll thresholds - [ ] Add
percentInViewoption? - [ ] Review from performance perspective - what needs to be memoised?
