Skip to main content

Overview

The WindowDropDowns component creates the classic Windows XP menu bar found at the top of applications like Internet Explorer, Notepad, and Minesweeper. It handles keyboard navigation, hover interactions, and nested submenus.

Component Structure

Main Component: WindowDropDowns

Location: src/components/WindowDropDowns/index.jsx Manages the top-level menu bar with labels like “File”, “Edit”, “View”.

Sub Component: WindowDropDown

Location: src/components/WindowDropDowns/WindowDropDown.jsx Renders individual dropdown menus with items, separators, and nested submenus.

Props API

WindowDropDowns

items
object
required
Menu structure object where keys are menu labels (e.g., “File”, “Edit”) and values are arrays of menu items
onClickItem
function
required
Callback function called when a menu item is clicked
(itemName: string) => void
className
string
Additional CSS class for styling
height
number
default:"20"
Height of the menu bar in pixels

WindowDropDown

items
array
required
Array of menu item objects (see Item Structure below)
position
object
default:"{}"
CSS positioning object (top, left, right, bottom)
onClick
function
required
Click handler for menu items

Item Structure

Menu items can be one of three types:

Type: “item”

Standard clickable menu item:
{
  type: 'item',
  text: 'Close',
  symbol: 'check',      // Optional: 'check', 'circle', 'ie-paper', 'ie-book', 'folder'
  hotkey: 'Ctrl+W',     // Optional keyboard shortcut display
  disable: false        // Optional: disable the item
}

Type: “separator”

Visual separator line:
{
  type: 'separator'
}

Type: “menu”

Nested submenu:
{
  type: 'menu',
  text: 'Send To',
  symbol: 'folder',
  items: [...],         // Array of nested items
  position: { top: '0', left: '100%' }  // Optional submenu positioning
}

Usage Example

Basic Implementation

import { WindowDropDowns } from 'components';

const menuItems = {
  File: [
    { type: 'item', text: 'New', hotkey: 'Ctrl+N' },
    { type: 'item', text: 'Open', hotkey: 'Ctrl+O' },
    { type: 'separator' },
    { type: 'item', text: 'Save', hotkey: 'Ctrl+S' },
    { type: 'item', text: 'Save As...', hotkey: 'Ctrl+Shift+S' },
    { type: 'separator' },
    { type: 'item', text: 'Exit', hotkey: 'Alt+F4' }
  ],
  Edit: [
    { type: 'item', text: 'Undo', hotkey: 'Ctrl+Z', disable: true },
    { type: 'separator' },
    { type: 'item', text: 'Cut', hotkey: 'Ctrl+X' },
    { type: 'item', text: 'Copy', hotkey: 'Ctrl+C' },
    { type: 'item', text: 'Paste', hotkey: 'Ctrl+V' }
  ]
};

function MyApp() {
  const handleMenuClick = (itemName) => {
    console.log('Clicked:', itemName);
    switch(itemName) {
      case 'Exit':
        // Handle exit
        break;
      case 'Save':
        // Handle save
        break;
    }
  };

  return (
    <WindowDropDowns 
      items={menuItems}
      onClickItem={handleMenuClick}
      height={21}
    />
  );
}

Real-World Example: Internet Explorer

Source: src/WinXP/apps/InternetExplorer/index.jsx:271-276
import { WindowDropDowns } from 'components';
import dropDownData from './dropDownData';

function onClickOptionItem(item) {
  switch (item) {
    case 'Close':
      onClose();
      break;
    case 'Home Page':
      goHome();
      break;
    case 'Back':
      goBack();
      break;
    case 'Forward':
      goForward();
      break;
    case 'Refresh':
      handleRefresh();
      break;
  }
}

<section className="ie__toolbar">
  <div className="ie__options">
    <WindowDropDowns
      items={dropDownData}
      onClickItem={onClickOptionItem}
      height={21}
    />
  </div>
</section>

Styled-Components Implementation

WindowDropDowns Styling

Source: src/components/WindowDropDowns/index.jsx:58-79
export default styled(WindowDropDowns)`
  display: inline-flex;
  height: ${({ height }) => height || 20}px;
  line-height: ${({ height }) => height || 20}px;
  position: relative;
  
  .drop-down {
    font-size: 11px;
    height: 100%;
    position: relative;
  }
  
  .drop-down__label--active {
    background-color: #1660e8;
    color: #fff;
  }
  
  .drop-down__label {
    padding: 0 7px;
    &:hover {
      background-color: #1660e8;
      color: #fff;
    }
  }
`;

WindowDropDown Styling

Source: src/components/WindowDropDowns/WindowDropDown.jsx:99-207 Key styling features:
  • Grid Layout: Uses CSS Grid for precise menu item alignment
  • Hover Effects: Blue highlight (#e99f17) with invert filter
  • Shadows: Drop shadows for depth
  • Arrow Indicators: CSS triangles for submenu indicators
.drop-down__menu {
  background-color: #fff;
  z-index: 1;
  padding: 2px;
  line-height: 18px;
  display: grid;
  position: absolute;
  box-shadow: 2px 2px 1px rgb(100, 100, 100);
  border: 1px solid gray;
  grid-template-columns: 16px auto auto 15px 0px;
}

Behavior Details

Hover State Management

Source: src/components/WindowDropDowns/index.jsx:12-14
const [openOption, setOpenOption] = useState('');

function hoverOption(option) {
  if (openOption) setOpenOption(option);
}
Menus only switch on hover after a menu is already open (click-to-open, hover-to-switch).

Click Outside Detection

Source: src/components/WindowDropDowns/index.jsx:20-28
function onMouseUp(e) {
  if (!dropDown.current.contains(e.target)) {
    setOpenOption('');
  }
}

useEffect(() => {
  window.addEventListener('mouseup', onMouseUp);
  return () => window.removeEventListener('mouseup', onMouseUp);
}, []);
Clicking outside any dropdown closes all menus.

Symbol Icons

Source: src/components/WindowDropDowns/WindowDropDown.jsx:73-97 Supported symbol types:
SymbolDescriptionUsage
ie-paperInternet Explorer document iconDocument actions
ie-bookInternet Explorer book iconHelp, documentation
folderFolder iconDirectory operations
checkCheckmarkSelected/enabled state
circleFilled circleRadio selection

Applications Using WindowDropDowns

  • Internet Explorer (src/WinXP/apps/InternetExplorer/index.jsx)
  • Notepad (src/WinXP/apps/Notepad/index.jsx)
  • Minesweeper (src/WinXP/apps/Minesweeper/MinesweeperView.jsx)
  • My Computer (src/WinXP/apps/MyComputer/index.jsx)

Best Practices

  1. Consistent Hotkey Formatting: Use Windows-style shortcuts (e.g., “Ctrl+S”, “Alt+F4”)
  2. Separator Usage: Group related menu items with separators
  3. Disable State: Use disable: true for unavailable actions rather than hiding items
  4. Nested Menus: Limit nesting to 2-3 levels for usability
  5. Click Handler: Use a switch statement for clean action routing

See Also

Build docs developers (and LLMs) love