Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"[html]": {
"editor.defaultFormatter": "biomejs.biome"
},
Expand All @@ -23,7 +28,6 @@
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"editor.defaultFormatter": "biomejs.biome",
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
Expand Down
76 changes: 76 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,82 @@ The scroll state provides:
- `scrollPosition`: `'top'`, `'bottom'`, `'middle'`, or `undefined`
- `currentSnap`: Current snap point index (if using snap points)

#### Creating custom scrollers

If you need more control over the scrollable area, eg. when you don't want the whole content area to be scrollable, you can disable the default scrolling behavior of the `Sheet.Content` and implement your own scroller with the help of `useScrollPosition` and `useVirtualKeyboard` utility hooks.

> [!NOTE]
> These hooks are internally used by the `Sheet.Content` component so you shouldn't need to use them unless you are implementing a custom scroller. However they are general purpose hooks so you can use them in other parts of your app as well if you want 😊

```tsx
import {
Sheet,
useScrollPosition,
useVirtualKeyboard,
} from 'react-modal-sheet';

function CustomScrollerExample() {
const [isOpen, setOpen] = useState(false);

const { scrollRef, scrollPosition } = useScrollPosition({
isEnabled: isOpen,
});

const { keyboardHeight } = useVirtualKeyboard({
isEnabled: isOpen,
});

/**
* If you use `var(--keyboard-inset-height)` CSS variable you can just call
* `useVirtualKeyboard()` without destructuring anything from it:
*
* useVirtualKeyboard({ isEnabled: isOpen });
*/

return (
// Disable default keyboard avoidance
<Sheet avoidKeyboard={false} isOpen={isOpen} onClose={() => setOpen(false)}>
<Sheet.Container>
<Sheet.Header />
<Sheet.Content
// Disable default scrolling
disableScroll
// Dragging is still managed by `Sheet.Content` so we control it here
disableDrag={scrollPosition !== 'top'}
>
<div
style={{
// Use CSS variable managed by `useVirtualKeyboard`
paddingBottom: 'var(--keyboard-inset-height)',
// Alternatively manually apply keyboard padding
paddingBottom: keyboardHeight,
// Some layout styles for custom content
height: '100%',
display: 'grid',
gridTemplateRows: 'auto 1fr auto',
}}
>
<div>Some content here...</div>

<div
// Pass ref to the custom scroller so we can track its scroll position
ref={scrollRef}
// Make it scrollable
style={{ overflowY: 'auto' }}
>
<div style={{ height: '400vh' }}>Long content here...</div>
</div>

<div>More content here...</div>
</div>
</Sheet.Content>
</Sheet.Container>
<Sheet.Backdrop />
</Sheet>
);
}
```

### 🪟 iOS Modal View effect

In addition to the `Sheet.Backdrop` it's possible to apply a scaling effect to the main app element to highlight the modality of the bottom sheet. This effect mimics the [iOS Modal View](https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/modality/) presentation style to bring more focus to the sheet and add some delight to the user experience.
Expand Down
7 changes: 6 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
},
"files": {
"ignoreUnknown": false,
"includes": ["**/src/**", "**/test/**"]
"includes": [
"**/src/**",
"**/test/**",
"**/example/src/**",
"**/example-ssr/src/**"
]
},
"formatter": {
"enabled": true,
Expand Down
36 changes: 17 additions & 19 deletions example-ssr/src/components/sheet-example.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use client';

import { useRef, useState } from 'react';
import { styled } from 'styled-components';
import { Sheet, SheetRef } from 'react-modal-sheet';
import { styled } from 'styled-components';

const snapPoints = [-50, 0.5, 200, 0];
const snapPoints = [0, 200, 0.5, -50];
const initialSnap = 1;

export function SheetExample() {
Expand All @@ -31,24 +31,22 @@ export function SheetExample() {
<Sheet.Header />

<Sheet.Content>
<Sheet.Scroller autoPadding draggableAt="both">
<BoxList>
<Controls>
<Button onClick={() => snapTo(0)}>
Snap to -50 (from top)
</Button>
<Button onClick={() => snapTo(1)}>Snap to 50%</Button>
<Button onClick={() => snapTo(2)}>Snap to 200</Button>
<Button onClick={() => snapTo(3)}>Snap to 0 (close)</Button>
</Controls>
<BoxList>
<Controls>
<Button onClick={() => snapTo(0)}>
Snap to -50 (from top)
</Button>
<Button onClick={() => snapTo(1)}>Snap to 50%</Button>
<Button onClick={() => snapTo(2)}>Snap to 200</Button>
<Button onClick={() => snapTo(3)}>Snap to 0 (close)</Button>
</Controls>

{Array.from({ length: 20 })
.fill(1)
.map((_, i) => (
<Box key={i}>{i + 1}</Box>
))}
</BoxList>
</Sheet.Scroller>
{Array.from({ length: 20 })
.fill(1)
.map((_, i) => (
<Box key={i}>{i + 1}</Box>
))}
</BoxList>
</Sheet.Content>
</Sheet.Container>

Expand Down
23 changes: 0 additions & 23 deletions example/biome.json

This file was deleted.

24 changes: 12 additions & 12 deletions example/src/assets.d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
declare module "*.jpg" {
const src: string;
export default src;
declare module '*.jpg' {
const src: string;
export default src;
}
declare module "*.jpeg" {
const src: string;
export default src;
declare module '*.jpeg' {
const src: string;
export default src;
}
declare module "*.png" {
const src: string;
export default src;
declare module '*.png' {
const src: string;
export default src;
}
declare module "*.svg" {
const src: string;
export default src;
declare module '*.svg' {
const src: string;
export default src;
}
Loading