Now.js Framework Documentation
DropdownPanel
DropdownPanel
Overview
DropdownPanel is a singleton panel for displaying dropdown content in Now.js Framework. It's shared between components like Calendar and ColorPicker.
When to use:
- Need dropdown floating panel
- Need to share panel between components
- Need to avoid z-index issues in modals
- Need popup positioning
Why use it:
- ✅ Singleton pattern (one panel for all)
- ✅ Attached to document.body (no overflow issues)
- ✅ Smart positioning (auto-adjust to viewport)
- ✅ Keyboard support (Escape to close)
- ✅ Click outside to close
- ✅ Scroll/resize repositioning
Basic Usage
Show Panel
const panel = DropdownPanel.getInstance();
// Show with HTML content
panel.show(targetElement, '<div class="picker">Content here</div>');
// Show with DOM element
const content = document.createElement('div');
content.innerHTML = '<p>My content</p>';
panel.show(targetElement, content);
// With options
panel.show(targetElement, content, {
align: 'right',
offsetY: 10,
onClose: () => console.log('Panel closed')
});Hide Panel
const panel = DropdownPanel.getInstance();
panel.hide();Options
panel.show(targetElement, content, {
// Alignment relative to target
align: 'left', // 'left', 'right', 'center'
// Offset from target
offsetY: 5, // Vertical offset (px)
offsetX: 0, // Horizontal offset (px)
// Callback when closed
onClose: () => {}
});API Reference
DropdownPanel.getInstance()
Get singleton instance
Returns: DropdownPanel
const panel = DropdownPanel.getInstance();panel.show(targetElement, content, options?)
Show panel
| Parameter | Type | Description |
|---|---|---|
targetElement |
HTMLElement | Element to position next to |
content |
HTMLElement/string | Content (element or HTML string) |
options.align |
string | 'left', 'right', 'center' |
options.offsetY |
number | Vertical offset (default: 5) |
options.offsetX |
number | Horizontal offset (default: 0) |
options.onClose |
function | Callback when closed |
panel.hide()
Hide panel
panel.isOpen()
Check if panel is open
Returns: boolean
panel.setContent(content)
Change content while visible
| Parameter | Type | Description |
|---|---|---|
content |
HTMLElement/string | New content |
panel.getContentContainer()
Get content container element
Returns: HTMLElement
DropdownPanel.destroy()
Destroy singleton instance
Events
| Event | When Triggered | Detail |
|---|---|---|
dropdown:show |
Panel shown | {target} |
dropdown:hide |
Panel hidden | {target} |
const panel = DropdownPanel.getInstance();
panel.panel.addEventListener('dropdown:show', (e) => {
console.log('Opened for:', e.detail.target);
});CSS Styling
/* Dropdown panel */
.dropdown-panel {
position: fixed;
display: none;
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
z-index: 1000;
overflow: auto;
}
.dropdown-panel-content {
padding: 8px;
}
/* Animation */
.dropdown-panel {
animation: dropdown-fade-in 0.15s ease-out;
}
@keyframes dropdown-fade-in {
from {
opacity: 0;
transform: translateY(-8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}Real-World Examples
Color Picker
function showColorPicker(inputElement) {
const panel = DropdownPanel.getInstance();
const picker = document.createElement('div');
picker.className = 'color-picker-grid';
picker.innerHTML = `
<div class="color" style="background: #ff0000" data-color="#ff0000"></div>
<div class="color" style="background: #00ff00" data-color="#00ff00"></div>
<div class="color" style="background: #0000ff" data-color="#0000ff"></div>
`;
picker.addEventListener('click', (e) => {
const color = e.target.dataset.color;
if (color) {
inputElement.value = color;
inputElement.style.backgroundColor = color;
panel.hide();
}
});
panel.show(inputElement, picker, {
onClose: () => {
// Cleanup if needed
}
});
}Date Picker
function showDatePicker(inputElement, currentDate) {
const panel = DropdownPanel.getInstance();
const calendar = CalendarManager.createCalendarElement({
date: currentDate,
onSelect: (date) => {
inputElement.value = Utils.date.format(date, 'YYYY-MM-DD');
panel.hide();
}
});
panel.show(inputElement, calendar, {
align: 'left',
offsetY: 4
});
}Context Menu
function showContextMenu(event, items) {
event.preventDefault();
const panel = DropdownPanel.getInstance();
const menu = document.createElement('ul');
menu.className = 'context-menu';
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item.label;
li.addEventListener('click', () => {
item.action();
panel.hide();
});
menu.appendChild(li);
});
// Create virtual target at click position
const virtualTarget = document.createElement('span');
virtualTarget.style.cssText = `
position: fixed;
left: ${event.clientX}px;
top: ${event.clientY}px;
`;
document.body.appendChild(virtualTarget);
panel.show(virtualTarget, menu, {
onClose: () => virtualTarget.remove()
});
}Common Pitfalls
⚠️ 1. Singleton - Only One Panel
// ❌ Opening 2 panels simultaneously
panel.show(element1, content1);
panel.show(element2, content2); // First one is closed
// ✅ Understand there's only 1 panel
// If you need multiple popups, use DialogManager⚠️ 2. Target Element Must Exist
// ❌ Target doesn't exist
panel.show(null, content);
// ✅ Check first
if (targetElement) {
panel.show(targetElement, content);
}⚠️ 3. Clean Up When Done
// onClose callback for cleanup
panel.show(target, content, {
onClose: () => {
// Cleanup resources
disposeResources();
}
});Related Documentation
- CalendarManager - Calendar component
- DialogManager - Dialog boxes
- BackdropManager - Backdrops