Dashboard
Interactive dashboard layout with drag-and-drop widgets, resize handles, collision detection, and keyboard navigation. For static dashboard layouts, use Grid + Widget components instead.
Interactive Example
Click "Edit Mode" to enable drag-and-drop. Drag widgets by their headers, resize using the bottom-right handle.
Locked
Usage
import { dashboard } from 'omniui/js/dashboard.js';
// Initialize dashboard
const db = dashboard(document.querySelector('.my-dashboard'), {
columns: 12,
rowHeight: 100,
gap: 16,
collision: 'push'
});
// Enable editing
db.unlock();
// Get current layout
const layout = db.getLayout();
// Restore saved layout
db.setLayout(savedLayout);
// Clean up
db.destroy();
HTML Structure
<div class="my-dashboard">
<div class="omni-widget" id="widget-1" data-x="0" data-y="0" data-w="4" data-h="2">
<div class="omni-widget-header">
<h3 class="omni-widget-title">Widget Title</h3>
</div>
<div class="omni-widget-content">
<!-- Content -->
</div>
<div class="omni-widget-resize" aria-label="Resize widget"></div>
</div>
</div>
Features
- Drag & Drop: Move widgets by dragging their headers
- Resize: Resize widgets via the corner handle
- Collision Detection: Widgets push others out of the way
- Gravity: Widgets pack upward when space is available
- Keyboard Navigation: Full support for arrow keys, Enter, Escape
- ARIA Announcements: Screen reader feedback during operations
- Layout Persistence: Save and restore layouts via
getLayout()/setLayout() - Reduced Motion: Respects
prefers-reduced-motion
API Reference
Options
| Option | Type | Default | Description |
|---|---|---|---|
columns |
number | 12 | Number of grid columns |
rowHeight |
number | 100 | Row height in pixels |
gap |
number | 16 | Gap between widgets in pixels |
widgets |
string | '.omni-widget' | Widget selector |
handle |
string | '.omni-widget-header' | Drag handle selector |
resizeHandle |
string | '.omni-widget-resize' | Resize handle selector |
minW / maxW |
number | 1 / 12 | Min/max width in columns |
minH / maxH |
number | 1 / 8 | Min/max height in rows |
collision |
'push' | 'none' | 'push' | How to handle overlapping widgets |
float |
boolean | false | If true, widgets don't pack upward |
Methods
| Method | Description |
|---|---|
unlock() |
Enable editing mode (drag and resize) |
lock() |
Disable editing mode |
getLayout() |
Returns array of widget positions |
setLayout(layout) |
Restore a saved layout |
compact() |
Pack all widgets upward |
addWidget(element, position) |
Add a new widget to the dashboard |
removeWidget(element) |
Remove a widget from the dashboard |
destroy() |
Clean up event listeners and reset styles |
Callbacks
| Callback | Arguments | Description |
|---|---|---|
onDragStart |
(widget) | Called when drag begins |
onDrag |
(widget, position) | Called during drag |
onDragEnd |
(widget, position) | Called when drag ends |
onResizeStart |
(widget) | Called when resize begins |
onResize |
(widget, position) | Called during resize |
onResizeEnd |
(widget, position) | Called when resize ends |
onChange |
(layout) | Called when layout changes |
Data Attributes
| Attribute | Description |
|---|---|
data-x |
Initial column position (0-based) |
data-y |
Initial row position (0-based) |
data-w |
Width in columns |
data-h |
Height in rows |
Static Layouts
If you don't need drag-and-drop functionality, use Grid with Widget components for a pure CSS solution:
<div class="omni-bento-grid">
<div class="omni-widget bento-span-2">...</div>
<div class="omni-widget">...</div>
<div class="omni-widget bento-span-2 bento-row-2">...</div>
</div>
Accessibility
- Keyboard Navigation: Tab to widget headers, Enter/Space to start drag, arrow keys to move, Escape to cancel
- ARIA Announcements: Screen readers announce when dragging starts, position changes, and operation completes
- Focus Management: Focus returns to widget after operations
- Reduced Motion: Animations disabled when
prefers-reduced-motion: reduceis set - Resize Handles: Include
aria-label="Resize widget"for screen readers
Components Used
Other OmniUI components demonstrated on this page: