Table of Contents
- Introduction
- Design Philosophy and Architecture
- Development Process
- Core Components Implementation
- Creating an NPM Package
- Documentation Standards
- Testing Strategy
- Publishing and Distribution
- Best Practices and Lessons Learned
Introduction
This article documents the creation of web-window-manager
, a comprehensive JavaScript library that brings desktop-like window management capabilities to web applications. We'll explore the architectural decisions, implementation details, and the process of packaging and distributing the library through NPM.
Project Goals
- Create a lightweight, dependency-free window management system
- Provide an intuitive API that mirrors desktop OS conventions
- Ensure accessibility and performance across modern browsers
- Build a foundation that developers can extend for their specific needs
Design Philosophy and Architecture
Core Design Principles
- Progressive Enhancement: The library should work without any build tools or frameworks
- Composability: Each component should function independently while integrating seamlessly
- Performance First: Minimize reflows and repaints through efficient DOM manipulation
- Accessibility: Ensure keyboard navigation and screen reader compatibility
- Extensibility: Provide hooks and events for custom functionality
Architectural Decisions
Why Vanilla JavaScript?
We chose vanilla JavaScript over a framework-specific implementation for several reasons:
- Universal Compatibility: Works with any framework or no framework at all
- Minimal Overhead: No virtual DOM or framework abstractions
- Learning Curve: Developers familiar with DOM APIs can immediately understand the codebase
- Performance: Direct DOM manipulation when done correctly can outperform framework abstractions
Component Architecture
WindowManager
├── Window System
│ ├── Window Creation
│ ├── Drag & Drop Handler
│ ├── Resize Handler
│ └── Focus Management
├── Context Menu System
│ ├── Menu Renderer
│ └── Position Calculator
├── Modal System
│ ├── Overlay Manager
│ └── Modal Renderer
└── Notification System
├── Queue Manager
└── Animation Controller
Development Process
Step 1: Requirements Gathering
We identified key features by analyzing popular desktop environments:
- Windows: Title bar, control buttons, resize handles
- macOS: Traffic light controls, smooth animations
- Context Menus: Right-click functionality with nested menus
- Modals: Focus trapping and backdrop interaction
- Notifications: Toast-style alerts with auto-dismiss
Step 2: API Design
We followed a builder pattern approach for flexibility:
// Simple usage
wm.createWindow({ title: 'My App' });
// Advanced usage
wm.createWindow({
title: 'Advanced Window',
width: 600,
height: 400,
position: { x: 100, y: 100 },
resizable: true,
onClose: () => saveState(),
content: document.getElementById('app-content')
});
Step 3: Implementation Strategy
- Core Window Class: Established the base window functionality
- Event System: Implemented a pub/sub pattern for window events
- Interaction Handlers: Added drag, resize, and focus management
- Visual Components: Created context menus, modals, and notifications
- Style System: Developed a themeable CSS architecture
Core Components Implementation
Window Management System
The window system uses a Map to track windows and their states:
class WindowManager {
constructor(options = {}) {
this.windows = new Map();
this.config = { ...DEFAULT_CONFIG, ...options };
this.zIndexManager = new ZIndexManager();
this.eventBus = new EventEmitter();
this.init();
}
createWindow(options) {
const window = new Window(this, options);
this.windows.set(window.id, window);
this.eventBus.emit('window:created', window);
return window;
}
}
Drag and Drop Implementation
We implemented drag handling using pointer events for better touch support:
class DragHandler {
constructor(window, handle) {
this.window = window;
this.handle = handle;
this.isDragging = false;
this.dragStart = { x: 0, y: 0 };
this.windowStart = { x: 0, y: 0 };
this.bindEvents();
}
bindEvents() {
this.handle.addEventListener('pointerdown', this.onPointerDown);
document.addEventListener('pointermove', this.onPointerMove);
document.addEventListener('pointerup', this.onPointerUp);
}
onPointerDown = (e) => {
this.isDragging = true;
this.dragStart = { x: e.clientX, y: e.clientY };
this.windowStart = {
x: this.window.position.x,
y: this.window.position.y
};
this.handle.setPointerCapture(e.pointerId);
}
}
Context Menu System
The context menu system calculates optimal positioning:
class ContextMenu {
constructor(items, options = {}) {
this.items = items;
this.options = options;
this.element = this.render();
this.positionMenu();
}
positionMenu() {
const { x, y } = this.options.position;
const menuRect = this.element.getBoundingClientRect();
const viewport = {
width: window.innerWidth,
height: window.innerHeight
};
// Adjust position to keep menu in viewport
const adjustedX = Math.min(x, viewport.width - menuRect.width);
const adjustedY = Math.min(y, viewport.height - menuRect.height);
this.element.style.left = `${Math.max(0, adjustedX)}px`;
this.element.style.top = `${Math.max(0, adjustedY)}px`;
}
}
Creating an NPM Package
Project Structure
web-window-manager/
├── src/
│ ├── core/
│ │ ├── WindowManager.js
│ │ ├── Window.js
│ │ └── EventEmitter.js
│ ├── components/
│ │ ├── ContextMenu.js
│ │ ├── Modal.js
│ │ └── Notification.js
│ ├── utils/
│ │ ├── dom.js
│ │ └── position.js
│ ├── styles/
│ │ ├── base.css
│ │ └── themes/
│ └── index.js
├── dist/
│ ├── web-window-manager.js
│ ├── web-window-manager.min.js
│ └── web-window-manager.css
├── docs/
│ ├── api/
│ ├── examples/
│ └── guides/
├── test/
├── package.json
├── README.md
├── LICENSE
└── rollup.config.js
Package.json Configuration
{
"name": "web-window-manager",
"version": "1.0.0",
"description": "A modern window management library for web applications",
"main": "dist/web-window-manager.js",
"module": "src/index.js",
"style": "dist/web-window-manager.css",
"files": [
"dist",
"src",
"README.md",
"LICENSE"
],
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src/**/*.js",
"docs": "jsdoc -c jsdoc.config.json",
"prepublishOnly": "npm run build && npm test"
},
"keywords": [
"window-manager",
"desktop",
"ui",
"windowing",
"modal",
"context-menu",
"draggable",
"resizable"
],
"author": "Your Name <your.email@example.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/yourusername/web-window-manager.git"
},
"bugs": {
"url": "https://github.com/yourusername/web-window-manager/issues"
},
"homepage": "https://github.com/yourusername/web-window-manager#readme",
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-terser": "^0.4.0",
"eslint": "^8.0.0",
"jest": "^29.0.0",
"jsdoc": "^4.0.0",
"rollup": "^3.0.0",
"rollup-plugin-css-only": "^4.0.0"
},
"peerDependencies": {},
"engines": {
"node": ">=14.0.0"
}
}
Build Configuration (Rollup)
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
import css from 'rollup-plugin-css-only';
export default [
// ES Module build
{
input: 'src/index.js',
output: {
file: 'dist/web-window-manager.esm.js',
format: 'es',
sourcemap: true
},
plugins: [resolve()]
},
// UMD build
{
input: 'src/index.js',
output: {
file: 'dist/web-window-manager.js',
format: 'umd',
name: 'WindowManager',
sourcemap: true
},
plugins: [resolve()]
},
// Minified UMD build
{
input: 'src/index.js',
output: {
file: 'dist/web-window-manager.min.js',
format: 'umd',
name: 'WindowManager',
sourcemap: true
},
plugins: [resolve(), terser()]
},
// CSS
{
input: 'src/styles/index.js',
output: {
file: 'dist/web-window-manager.css',
format: 'es'
},
plugins: [css({ output: 'web-window-manager.css' })]
}
];
Documentation Standards
API Documentation (JSDoc)
/**
* Creates a new window instance
* @param {Object} options - Window configuration options
* @param {string} options.title - Window title
* @param {number} [options.width=400] - Window width in pixels
* @param {number} [options.height=300] - Window height in pixels
* @param {Object} [options.position] - Initial window position
* @param {number} [options.position.x=100] - X coordinate
* @param {number} [options.position.y=100] - Y coordinate
* @param {boolean} [options.resizable=true] - Enable window resizing
* @param {boolean} [options.draggable=true] - Enable window dragging
* @param {Function} [options.onClose] - Close event handler
* @returns {Window} The created window instance
* @fires WindowManager#window:created
* @example
* const window = wm.createWindow({
* title: 'My Application',
* width: 600,
* height: 400,
* onClose: () => console.log('Window closed')
* });
*/
createWindow(options = {}) {
// Implementation
}
README Structure
# Web Window Manager
A modern, lightweight window management library for web applications.
## Features
- 🪟 **Draggable Windows** - Smooth window dragging with touch support
- 📐 **Resizable Windows** - Resize from any edge or corner
- 🎨 **Themeable** - CSS variables for easy customization
- ♿ **Accessible** - Full keyboard navigation and ARIA support
- 📱 **Responsive** - Works on desktop and mobile devices
- 🚀 **Performant** - Optimized rendering and minimal reflows
- 🔧 **Framework Agnostic** - Works with any or no framework
## Installation
bash
npm install web-window-manager
## Quick Start
javascript
import WindowManager from 'web-window-manager';
import 'web-window-manager/dist/web-window-manager.css';
const wm = new WindowManager();
const window = wm.createWindow({
title: 'Hello World',
content: '
Welcome!
'});
## API Reference
See [full API documentation](./docs/api.md).
## Examples
- [Basic Usage](./examples/basic.html)
- [Custom Themes](./examples/themes.html)
- [React Integration](./examples/react.html)
- [Vue Integration](./examples/vue.html)
## Browser Support
- Chrome/Edge 88+
- Firefox 78+
- Safari 14+
- iOS Safari 14+
- Chrome Android 88+
## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
## License
MIT © [Your Name]
Type Definitions
// types/index.d.ts
declare module 'web-window-manager' {
export interface WindowOptions {
title?: string;
width?: number;
height?: number;
position?: { x: number; y: number };
resizable?: boolean;
draggable?: boolean;
closable?: boolean;
minimizable?: boolean;
maximizable?: boolean;
content?: string | HTMLElement;
onClose?: () => void;
onFocus?: () => void;
onBlur?: () => void;
}
export interface ContextMenuItem {
label: string;
icon?: string;
action?: () => void;
disabled?: boolean;
separator?: boolean;
submenu?: ContextMenuItem[];
}
export class Window {
id: string;
title: string;
position: { x: number; y: number };
size: { width: number; height: number };
close(): void;
minimize(): void;
maximize(): void;
focus(): void;
setTitle(title: string): void;
setContent(content: string | HTMLElement): void;
}
export default class WindowManager {
constructor(options?: WindowManagerOptions);
createWindow(options?: WindowOptions): Window;
closeWindow(id: string): void;
getWindow(id: string): Window | undefined;
getAllWindows(): Window[];
showContextMenu(event: MouseEvent, items: ContextMenuItem[]): void;
showModal(options: ModalOptions): void;
showNotification(title: string, message: string, duration?: number): void;
}
}
Testing Strategy
Unit Tests
// test/WindowManager.test.js
import WindowManager from '../src/core/WindowManager';
describe('WindowManager', () => {
let wm;
beforeEach(() => {
document.body.innerHTML = '<div id="desktop"></div>';
wm = new WindowManager({ container: '#desktop' });
});
afterEach(() => {
wm.destroy();
});
describe('createWindow', () => {
test('creates window with default options', () => {
const window = wm.createWindow();
expect(window).toBeDefined();
expect(window.title).toBe('New Window');
expect(wm.windows.size).toBe(1);
});
test('creates window with custom options', () => {
const options = {
title: 'Test Window',
width: 500,
height: 400
};
const window = wm.createWindow(options);
expect(window.title).toBe('Test Window');
expect(window.size.width).toBe(500);
expect(window.size.height).toBe(400);
});
});
describe('window interactions', () => {
test('focuses window on click', () => {
const window1 = wm.createWindow();
const window2 = wm.createWindow();
expect(wm.activeWindow).toBe(window2);
window1.element.dispatchEvent(new MouseEvent('mousedown'));
expect(wm.activeWindow).toBe(window1);
});
});
});
Integration Tests
// test/integration/drag-drop.test.js
import WindowManager from '../src/index';
import { simulateDrag } from './helpers';
describe('Drag and Drop Integration', () => {
test('window can be dragged', async () => {
const wm = new WindowManager();
const window = wm.createWindow({
position: { x: 100, y: 100 }
});
const initialPosition = { ...window.position };
await simulateDrag(window.element.querySelector('.window-header'), {
from: { x: 150, y: 120 },
to: { x: 250, y: 220 }
});
expect(window.position.x).toBe(initialPosition.x + 100);
expect(window.position.y).toBe(initialPosition.y + 100);
});
});
Publishing and Distribution
Pre-publish Checklist
-
Version Bump: Update version in
package.json
following semver -
Changelog: Update
CHANGELOG.md
with new features/fixes -
Tests: Ensure all tests pass (
npm test
) -
Build: Generate distribution files (
npm run build
) -
Documentation: Update API docs (
npm run docs
) - Examples: Test all examples with new version
- Types: Verify TypeScript definitions are accurate
Publishing to NPM
# Login to NPM
npm login
# Publish (runs prepublishOnly script automatically)
npm publish
# Tag release on GitHub
git tag v1.0.0
git push origin v1.0.0
CDN Distribution
The package will be automatically available on CDNs:
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/web-window-manager@1.0.0/dist/web-window-manager.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-window-manager@1.0.0/dist/web-window-manager.css">
<!-- unpkg -->
<script src="https://unpkg.com/web-window-manager@1.0.0/dist/web-window-manager.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/web-window-manager@1.0.0/dist/web-window-manager.css">
Best Practices and Lessons Learned
Performance Optimization
- Batch DOM Operations: Group DOM changes to minimize reflows
- Event Delegation: Use single event listeners on parent elements
- RequestAnimationFrame: Throttle drag/resize operations
-
CSS Containment: Use
contain
property for better rendering performance
Accessibility Considerations
- Keyboard Navigation: Implement focus trapping and tab order
- ARIA Labels: Add appropriate roles and labels
- Screen Reader Support: Announce window state changes
- High Contrast Mode: Ensure visibility in all color modes
Common Pitfalls to Avoid
- Memory Leaks: Always remove event listeners on window close
- Z-Index Wars: Use a centralized z-index management system
- Touch vs Mouse: Handle both pointer types consistently
- CSS Conflicts: Use scoped class names or CSS modules
Future Enhancements
- Window Snapping: Snap to screen edges and other windows
- Persistence: Save/restore window layouts
- Multi-Monitor Support: Handle different screen configurations
- Virtual Desktops: Multiple window spaces
- Plugin System: Allow third-party extensions
Conclusion
Building a window manager library requires careful consideration of performance, accessibility, and developer experience. By following modern web standards and best practices, we've created a foundation that developers can use to build sophisticated desktop-like applications in the browser.
The key to success lies in:
- Starting with a solid architecture
- Prioritizing performance from the beginning
- Writing comprehensive documentation
- Testing across different scenarios
- Listening to community feedback
This library serves as both a practical tool and a learning resource for developers interested in creating complex UI systems for the web.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.