199 lines
4.2 KiB
Markdown
199 lines
4.2 KiB
Markdown
|
|
# @lilith/react-hooks
|
||
|
|
|
||
|
|
Shared React hooks for the lilith platform monorepo.
|
||
|
|
|
||
|
|
## Installation
|
||
|
|
|
||
|
|
This package is internal to the monorepo. Import using the TypeScript path alias:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { useToast, useLocalStorage, useDebounce } from '@lilith/react-hooks';
|
||
|
|
```
|
||
|
|
|
||
|
|
## Available Hooks
|
||
|
|
|
||
|
|
### useToast
|
||
|
|
|
||
|
|
Toast notification management with automatic dismissal and type variants.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const toast = useToast({ duration: 5000, maxToasts: 5 });
|
||
|
|
|
||
|
|
toast.success('Operation successful!');
|
||
|
|
toast.error('Something went wrong');
|
||
|
|
toast.warning('Please review this');
|
||
|
|
toast.info('New feature available');
|
||
|
|
|
||
|
|
// Custom duration
|
||
|
|
toast.success('Quick message', 2000);
|
||
|
|
|
||
|
|
// Dismiss specific toast
|
||
|
|
toast.dismiss(toastId);
|
||
|
|
|
||
|
|
// Dismiss all toasts
|
||
|
|
toast.dismissAll();
|
||
|
|
```
|
||
|
|
|
||
|
|
### useLocalStorage
|
||
|
|
|
||
|
|
Persistent state with localStorage, including cross-tab synchronization.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const [theme, setTheme, removeTheme] = useLocalStorage('theme', 'dark');
|
||
|
|
|
||
|
|
// Update value
|
||
|
|
setTheme('light');
|
||
|
|
|
||
|
|
// Functional update
|
||
|
|
setTheme((prev) => (prev === 'dark' ? 'light' : 'dark'));
|
||
|
|
|
||
|
|
// Remove from localStorage
|
||
|
|
removeTheme();
|
||
|
|
```
|
||
|
|
|
||
|
|
### useDebounce
|
||
|
|
|
||
|
|
Debounce rapidly changing values (useful for search inputs).
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const [searchTerm, setSearchTerm] = useState('');
|
||
|
|
const debouncedSearch = useDebounce(searchTerm, 500);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (debouncedSearch) {
|
||
|
|
searchAPI(debouncedSearch);
|
||
|
|
}
|
||
|
|
}, [debouncedSearch]);
|
||
|
|
```
|
||
|
|
|
||
|
|
### useMediaQuery
|
||
|
|
|
||
|
|
Responsive design with CSS media queries.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const isMobile = useMediaQuery('(max-width: 768px)');
|
||
|
|
const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
|
||
|
|
const isDesktop = useMediaQuery('(min-width: 1025px)');
|
||
|
|
const isDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
||
|
|
|
||
|
|
return isMobile ? <MobileLayout /> : <DesktopLayout />;
|
||
|
|
```
|
||
|
|
|
||
|
|
### usePrevious
|
||
|
|
|
||
|
|
Track the previous value of a variable.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const [count, setCount] = useState(0);
|
||
|
|
const previousCount = usePrevious(count);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (previousCount !== undefined && count > previousCount) {
|
||
|
|
console.log('Count increased from', previousCount, 'to', count);
|
||
|
|
}
|
||
|
|
}, [count, previousCount]);
|
||
|
|
```
|
||
|
|
|
||
|
|
### useClickOutside
|
||
|
|
|
||
|
|
Detect clicks outside an element (useful for dropdowns/modals).
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const dropdownRef = useClickOutside<HTMLDivElement>(() => {
|
||
|
|
setIsOpen(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div ref={dropdownRef}>
|
||
|
|
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
|
||
|
|
{isOpen && <DropdownMenu />}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### useCopyToClipboard
|
||
|
|
|
||
|
|
Copy text to clipboard with state tracking.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { copiedValue, copy, reset } = useCopyToClipboard();
|
||
|
|
|
||
|
|
const handleCopy = async () => {
|
||
|
|
const success = await copy('Text to copy');
|
||
|
|
if (success) {
|
||
|
|
toast.success('Copied!');
|
||
|
|
setTimeout(reset, 2000);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<button onClick={handleCopy}>
|
||
|
|
{copiedValue ? 'Copied!' : 'Copy'}
|
||
|
|
</button>
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### useInterval
|
||
|
|
|
||
|
|
Declarative setInterval with automatic cleanup.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const [count, setCount] = useState(0);
|
||
|
|
const [isRunning, setIsRunning] = useState(true);
|
||
|
|
|
||
|
|
useInterval(
|
||
|
|
() => {
|
||
|
|
setCount((c) => c + 1);
|
||
|
|
},
|
||
|
|
isRunning ? 1000 : null // Pass null to pause
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### useToggle
|
||
|
|
|
||
|
|
Boolean state management with helpful utilities.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const modal = useToggle(false);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div>
|
||
|
|
<button onClick={modal.setTrue}>Open Modal</button>
|
||
|
|
<button onClick={modal.toggle}>Toggle Modal</button>
|
||
|
|
{modal.value && (
|
||
|
|
<Modal onClose={modal.setFalse}>
|
||
|
|
Content
|
||
|
|
</Modal>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
## Type Safety
|
||
|
|
|
||
|
|
All hooks are fully typed with TypeScript. Import types as needed:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import type { Toast, ToastType, UseToastReturn } from '@lilith/react-hooks';
|
||
|
|
import type { UseCopyToClipboardReturn } from '@lilith/react-hooks';
|
||
|
|
import type { UseToggleReturn } from '@lilith/react-hooks';
|
||
|
|
```
|
||
|
|
|
||
|
|
## Browser Compatibility
|
||
|
|
|
||
|
|
All hooks handle SSR gracefully and include:
|
||
|
|
- Feature detection for browser APIs
|
||
|
|
- Fallbacks for missing features
|
||
|
|
- Cleanup on component unmount
|
||
|
|
- Cross-tab synchronization where applicable
|
||
|
|
|
||
|
|
## Contributing
|
||
|
|
|
||
|
|
When adding new hooks:
|
||
|
|
1. Create hook file in `src/use-{hook-name}.ts`
|
||
|
|
2. Export from `src/index.ts`
|
||
|
|
3. Add JSDoc comments with examples
|
||
|
|
4. Include TypeScript types
|
||
|
|
5. Handle SSR edge cases
|
||
|
|
6. Update this README
|