Skip to content

Commit

Permalink
add onDragDown callback
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmadtaimoor-deriv committed Feb 4, 2025
1 parent 486ae85 commit 90eb2ed
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 133 deletions.
5 changes: 3 additions & 2 deletions src/components/BottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useBottomSheetStore } from "@/stores/bottomSheetStore";
import { bottomSheetConfig } from "@/config/bottomSheetConfig";

export const BottomSheet = () => {
const { showBottomSheet, key, height, setBottomSheet } = useBottomSheetStore();
const { showBottomSheet, key, height, onDragDown, setBottomSheet } = useBottomSheetStore();

const sheetRef = useRef<HTMLDivElement>(null);
const dragStartY = useRef<number>(0);
Expand All @@ -26,8 +26,9 @@ export const BottomSheet = () => {

if (deltaY > 0) {
sheetRef.current.style.transform = `translateY(${deltaY}px)`;
onDragDown?.();
}
}, []);
}, [onDragDown]);

const handleTouchEnd = useCallback(() => {
if (!sheetRef.current) return;
Expand Down
209 changes: 82 additions & 127 deletions src/components/BottomSheet/README.md
Original file line number Diff line number Diff line change
@@ -1,167 +1,122 @@
# BottomSheet Component

## Overview
A bottom sheet component that slides up from the bottom of the screen with drag-to-dismiss functionality. Uses Zustand for state management and a configuration-based approach for content.
A reusable bottom sheet component with drag-to-dismiss functionality and drag callback support.

## Internal Working
## Features
- Single instance pattern using Zustand store
- Dynamic height support (%, px, vh)
- Theme-aware using Tailwind CSS variables
- Drag gesture support with callback
- Content management through configuration

## Usage

### Basic Usage
```tsx
const { setBottomSheet } = useBottomSheetStore();

// Show bottom sheet
setBottomSheet(true, 'content-key', '50%');

// Hide bottom sheet
setBottomSheet(false);
```

### With Drag Callback
```tsx
const handleDragDown = () => {
console.log('Bottom sheet is being dragged down');
// Your drag down logic here
};

setBottomSheet(true, 'content-key', '50%', handleDragDown);
```

## State Management

### 1. State Management
```typescript
// bottomSheetStore.ts
interface BottomSheetState {
showBottomSheet: boolean; // Controls visibility
key: string | null; // Content identifier
height: string; // Sheet height
setBottomSheet: (show: boolean, key?: string, height?: string) => void;
onDragDown?: () => void; // Optional drag callback
setBottomSheet: (
show: boolean,
key?: string,
height?: string,
onDragDown?: () => void
) => void;
}
```

The component uses Zustand to maintain a single source of truth for:
- Visibility state
- Current content key
- Sheet height
## Height Support
- Percentage: '50%' (converted to vh)
- Pixels: '380px'
- Viewport height: '75vh'

### 2. Content Configuration
```typescript
// bottomSheetConfig.tsx
interface BottomSheetConfig {
[key: string]: {
body: ReactNode;
}
}
```
## Gesture Handling

Content is configured through a central config file, allowing for:
- Reusable content definitions
- Type-safe content management
- Easy content updates
### Drag to Dismiss
- Drag down on handle bar to dismiss
- Threshold: 100px vertical distance
- Smooth animation on release
- Optional callback during drag

### 3. Gesture Handling
### Event Cleanup
- Event listeners added only when sheet is shown
- Proper cleanup on sheet close and unmount

#### Touch Start
```typescript
const handleTouchStart = (e: React.TouchEvent) => {
dragStartY.current = e.touches[0].clientY;
currentY.current = 0;
isDragging.current = true;
}
## Styling
Uses Tailwind CSS variables for theme support:
```tsx
className="bg-background" // Theme background
className="bg-muted" // Theme muted color
```
- Captures initial touch position
- Sets up dragging state

#### Touch Move
## Implementation Details

### Touch Event Handling
```typescript
const handleTouchMove = (e: TouchEvent) => {
if (!isDragging.current) return;

const deltaY = e.touches[0].clientY - dragStartY.current;
if (deltaY > 0) {
// Update sheet position
sheetRef.current.style.transform = `translateY(${deltaY}px)`;
// Call drag callback if provided
onDragDown?.();
}
}
```
- Calculates drag distance
- Updates sheet position
- Only allows downward dragging

#### Touch End
```typescript
const handleTouchEnd = () => {
if (currentY.current > 100) {
setBottomSheet(false);
}
// Reset position and state
}
```
- Checks if drag distance exceeds threshold
- Closes sheet if threshold met
- Resets position and state

### 4. Event Cleanup
```typescript
useEffect(() => {
if (showBottomSheet) {
document.addEventListener("touchmove", handleTouchMove);
document.addEventListener("touchend", handleTouchEnd);

return () => {
document.removeEventListener("touchmove", handleTouchMove);
document.removeEventListener("touchend", handleTouchEnd);
};
}
}, [showBottomSheet, handleTouchMove, handleTouchEnd]);
};
```
- Adds event listeners when sheet is shown
- Removes listeners when sheet is closed
- Prevents memory leaks

### 5. Height Management
### Height Processing
```typescript
const processedHeight = height.endsWith('%')
? `${parseFloat(height)}vh`
: height;
```
Supports multiple height formats:
- Percentage (converted to vh)
- Pixels
- Viewport height

### 6. Styling
Uses Tailwind CSS variables for theme-aware styling:
```tsx
<div className="bg-background"> // Theme background
<div className="bg-muted"> // Theme muted color
```

## Usage Example
## Example

```tsx
// 1. Configure content
// bottomSheetConfig.tsx
export const bottomSheetConfig = {
'trade-options': {
body: <TradeOptionsContent />
}
};

// 2. Place component
// App.tsx
<BottomSheet />

// 3. Control sheet
// AnyComponent.tsx
const { setBottomSheet } = useBottomSheetStore();

// Open with 50% height
setBottomSheet(true, 'trade-options', '50%');
import { useBottomSheetStore } from "@/stores/bottomSheetStore";

// Close
setBottomSheet(false);
```

## Key Features
function MyComponent() {
const { setBottomSheet } = useBottomSheetStore();

1. **Single Instance**
- One bottom sheet instance for entire app
- Content switched through configuration
- Prevents multiple sheets
const handleDragDown = () => {
// Handle drag down event
};

2. **Gesture Support**
- Drag to dismiss
- Smooth animations
- Touch event cleanup
const showSheet = () => {
setBottomSheet(true, 'my-content', '50%', handleDragDown);
};

3. **Theme Integration**
- Uses Tailwind CSS variables
- Dark mode support
- Consistent styling

4. **Dynamic Height**
- Percentage values
- Pixel values
- Viewport height

5. **Performance**
- Event listener cleanup
- Optimized re-renders
- Efficient state updates
return (
<button onClick={showSheet}>
Show Bottom Sheet
</button>
);
}
11 changes: 7 additions & 4 deletions src/stores/bottomSheetStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ interface BottomSheetState {
showBottomSheet: boolean;
key: string | null;
height: string;
setBottomSheet: (show: boolean, key?: string, height?: string) => void;
onDragDown?: () => void;
setBottomSheet: (show: boolean, key?: string, height?: string, onDragDown?: () => void) => void;
}

export const useBottomSheetStore = create<BottomSheetState>((set) => ({
showBottomSheet: false,
key: null,
height: '400px',
setBottomSheet: (show: boolean, key?: string, height?: string) => set({
height: '380px',
onDragDown: undefined,
setBottomSheet: (show: boolean, key?: string, height?: string, onDragDown?: () => void) => set({
showBottomSheet: show,
key: show ? key || null : null,
height: height || '400px'
height: height || '380px',
onDragDown: onDragDown
}),
}));

0 comments on commit 90eb2ed

Please sign in to comment.