hotfix(FE): fixing dropdown logout and floating button max size

This commit is contained in:
randy-ar
2025-12-10 13:25:07 +07:00
parent 865b0b3d8f
commit eed142a85f
7 changed files with 381 additions and 47 deletions
+116
View File
@@ -0,0 +1,116 @@
'use client';
import { ReactNode, useRef, useEffect, useState } from 'react';
import { cn } from '@/lib/helper';
interface DropdownProps {
trigger: ReactNode;
children: ReactNode;
position?:
| 'top'
| 'bottom'
| 'left'
| 'right'
| 'top-start'
| 'top-end'
| 'bottom-start'
| 'bottom-end'
| 'left-start'
| 'left-end'
| 'right-start'
| 'right-end';
align?: 'start' | 'center' | 'end';
hover?: boolean;
className?: string;
contentClassName?: string;
}
const Dropdown = ({
trigger,
children,
position = 'bottom',
align = 'start',
hover = false,
className,
contentClassName,
}: DropdownProps) => {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
// Handle click outside to close dropdown
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
if (isOpen) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isOpen]);
// Build position classes
const getPositionClasses = () => {
const classes: string[] = [];
// Handle combined positions like 'top-start'
if (position.includes('-')) {
const [pos, al] = position.split('-');
classes.push(`dropdown-${pos}`);
classes.push(`dropdown-${al}`);
} else {
classes.push(`dropdown-${position}`);
if (align !== 'start') {
classes.push(`dropdown-${align}`);
}
}
return classes.join(' ');
};
const handleToggle = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
// alert('clicked');
setIsOpen(!isOpen);
};
return (
<div
ref={dropdownRef}
className={cn(
'dropdown',
getPositionClasses(),
hover && 'dropdown-hover',
isOpen && 'dropdown-open',
className
)}
>
{/* Trigger Button */}
<div onClick={handleToggle} className='cursor-pointer'>
{trigger}
</div>
{/* Dropdown Content - Only render when open */}
{isOpen && (
<div
tabIndex={-1}
className={cn('dropdown-content z-[10]', contentClassName)}
onClick={() => setIsOpen(false)} // Close on item click
>
{children}
</div>
)}
</div>
);
};
export default Dropdown;
+83
View File
@@ -0,0 +1,83 @@
# Dropdown Component
Komponen Dropdown reusable berdasarkan DaisyUI yang mengatasi issue children component yang ter-render sebelum dropdown dibuka.
## Features
-**Conditional Rendering**: Children hanya di-render ketika dropdown aktif/terbuka
-**Click Outside to Close**: Otomatis menutup dropdown ketika klik di luar area dropdown
-**Multiple Positions**: Support berbagai posisi (top, bottom, left, right) dengan alignment (start, center, end)
-**Hover Support**: Optional hover mode untuk membuka dropdown
-**Customizable**: Mendukung custom className untuk container dan content
## Usage
### Basic Example
```tsx
import Dropdown from '@/components/dropdown/Dropdown';
import Menu from '@/components/menu/Menu';
import MenuItem from '@/components/menu/MenuItem';
<Dropdown
trigger={
<button className="btn">Click Me</button>
}
>
<Menu className="p-2 bg-base-100 shadow rounded-box menu-sm w-52">
<MenuItem title="Item 1" onClick={() => console.log('Item 1')} />
<MenuItem title="Item 2" onClick={() => console.log('Item 2')} />
</Menu>
</Dropdown>
```
### With Position
```tsx
<Dropdown
position="bottom-end"
trigger={<button className="btn">Dropdown</button>}
contentClassName="w-52 mt-3"
>
{/* Your content */}
</Dropdown>
```
### Hover Mode
```tsx
<Dropdown
hover={true}
trigger={<button className="btn">Hover Me</button>}
>
{/* Your content */}
</Dropdown>
```
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `trigger` | `ReactNode` | - | **Required**. Element yang akan men-trigger dropdown |
| `children` | `ReactNode` | - | **Required**. Content dropdown yang akan ditampilkan |
| `position` | `'top' \| 'bottom' \| 'left' \| 'right' \| 'top-start' \| 'top-end' \| 'bottom-start' \| 'bottom-end' \| 'left-start' \| 'left-end' \| 'right-start' \| 'right-end'` | `'bottom'` | Posisi dropdown relatif terhadap trigger |
| `align` | `'start' \| 'center' \| 'end'` | `'start'` | Alignment dropdown (digunakan jika position tidak mengandung alignment) |
| `hover` | `boolean` | `false` | Aktifkan mode hover untuk membuka dropdown |
| `className` | `string` | - | Custom className untuk container dropdown |
| `contentClassName` | `string` | - | Custom className untuk content dropdown |
## Position Examples
- `bottom` - Dropdown muncul di bawah, align ke start
- `bottom-end` - Dropdown muncul di bawah, align ke end
- `bottom-center` - Dropdown muncul di bawah, align ke center
- `top-start` - Dropdown muncul di atas, align ke start
- `left-end` - Dropdown muncul di kiri, align ke end
- Dan seterusnya...
## Key Benefits
1. **Performance**: Children tidak di-render sampai dropdown dibuka, menghemat resources
2. **Clean State**: Setiap kali dropdown dibuka, children di-render fresh
3. **DaisyUI Compatible**: Menggunakan class DaisyUI yang sudah ada
4. **Accessible**: Menggunakan proper ARIA attributes dan keyboard navigation