Now.js Framework Documentation

Now.js Framework Documentation

DropdownPanel

EN 15 Dec 2025 07:09

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

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

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();
  }
});