2025-06-28 12:27:35 +00:00
|
|
|
|
import { html, css, cssManager } from '@design.estate/dees-element';
|
|
|
|
|
import type { DeesDashboardgrid } from './dees-dashboardgrid.js';
|
|
|
|
|
import '@design.estate/dees-wcctools/demotools';
|
|
|
|
|
|
|
|
|
|
export const demoFunc = () => {
|
|
|
|
|
return html`
|
|
|
|
|
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
|
|
|
|
|
const grid = elementArg.querySelector('#dashboardGrid') as DeesDashboardgrid;
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
|
|
|
|
const seedWidgets = [
|
2025-06-28 12:27:35 +00:00
|
|
|
|
{
|
|
|
|
|
id: 'metrics1',
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
w: 3,
|
|
|
|
|
h: 2,
|
|
|
|
|
title: 'Revenue',
|
|
|
|
|
icon: 'lucide:dollarSign',
|
|
|
|
|
content: html`
|
|
|
|
|
<div style="padding: 20px;">
|
|
|
|
|
<div style="font-size: 32px; font-weight: 700; color: ${cssManager.bdTheme('#09090b', '#fafafa')};">$124,563</div>
|
|
|
|
|
<div style="color: #22c55e; font-size: 14px; margin-top: 8px;">↑ 12.5% from last month</div>
|
|
|
|
|
</div>
|
2025-09-17 21:46:44 +00:00
|
|
|
|
`,
|
2025-06-28 12:27:35 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'metrics2',
|
|
|
|
|
x: 3,
|
|
|
|
|
y: 0,
|
|
|
|
|
w: 3,
|
|
|
|
|
h: 2,
|
|
|
|
|
title: 'Users',
|
|
|
|
|
icon: 'lucide:users',
|
|
|
|
|
content: html`
|
|
|
|
|
<div style="padding: 20px;">
|
|
|
|
|
<div style="font-size: 32px; font-weight: 700; color: ${cssManager.bdTheme('#09090b', '#fafafa')};">8,234</div>
|
|
|
|
|
<div style="color: #3b82f6; font-size: 14px; margin-top: 8px;">↑ 5.2% from last week</div>
|
|
|
|
|
</div>
|
2025-09-17 21:46:44 +00:00
|
|
|
|
`,
|
2025-06-28 12:27:35 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'chart1',
|
|
|
|
|
x: 6,
|
|
|
|
|
y: 0,
|
|
|
|
|
w: 6,
|
|
|
|
|
h: 4,
|
|
|
|
|
title: 'Analytics',
|
|
|
|
|
icon: 'lucide:lineChart',
|
|
|
|
|
content: html`
|
|
|
|
|
<div style="padding: 20px; height: 100%; display: flex; align-items: center; justify-content: center;">
|
|
|
|
|
<div style="text-align: center; color: #71717a;">
|
|
|
|
|
<dees-icon .icon=${'lucide:lineChart'} style="font-size: 48px; margin-bottom: 16px;"></dees-icon>
|
|
|
|
|
<div>Chart visualization area</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-09-17 21:46:44 +00:00
|
|
|
|
`,
|
|
|
|
|
},
|
2025-06-28 12:27:35 +00:00
|
|
|
|
];
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
|
|
|
|
grid.widgets = seedWidgets.map(widget => ({ ...widget }));
|
2025-06-28 12:27:35 +00:00
|
|
|
|
grid.cellHeight = 80;
|
|
|
|
|
grid.margin = { top: 10, right: 10, bottom: 10, left: 10 };
|
|
|
|
|
grid.enableAnimation = true;
|
|
|
|
|
grid.showGridLines = false;
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
|
|
|
|
const baseLayout = grid.getLayout().map(item => ({ ...item }));
|
|
|
|
|
const mobileLayout = grid.widgets.map((widget, index) => ({
|
|
|
|
|
id: widget.id,
|
|
|
|
|
x: 0,
|
|
|
|
|
y: index === 0 ? 0 : grid.widgets.slice(0, index).reduce((acc, prev) => acc + prev.h, 0),
|
|
|
|
|
w: grid.columns,
|
|
|
|
|
h: widget.h,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
grid.layouts = {
|
|
|
|
|
base: baseLayout,
|
|
|
|
|
mobile: mobileLayout,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const statusEl = elementArg.querySelector('#dashboardLayoutStatus') as HTMLElement;
|
|
|
|
|
const updateStatus = () => {
|
|
|
|
|
const layout = grid.getLayout();
|
|
|
|
|
statusEl.textContent = `Active breakpoint: ${grid.activeBreakpoint} • Tiles: ${layout.length}`;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const mediaQuery = window.matchMedia('(max-width: 768px)');
|
|
|
|
|
const handleBreakpoint = () => {
|
|
|
|
|
const target = mediaQuery.matches ? 'mobile' : 'base';
|
|
|
|
|
grid.applyBreakpointLayout(target);
|
|
|
|
|
updateStatus();
|
|
|
|
|
};
|
2025-09-18 08:05:41 +00:00
|
|
|
|
if (typeof mediaQuery.addEventListener === 'function') {
|
2025-09-17 21:46:44 +00:00
|
|
|
|
mediaQuery.addEventListener('change', handleBreakpoint);
|
|
|
|
|
} else {
|
2025-09-18 08:05:41 +00:00
|
|
|
|
(mediaQuery as MediaQueryList & {
|
|
|
|
|
addListener?: (listener: (this: MediaQueryList, ev: MediaQueryListEvent) => void) => void;
|
|
|
|
|
}).addListener?.(handleBreakpoint);
|
2025-09-17 21:46:44 +00:00
|
|
|
|
}
|
|
|
|
|
handleBreakpoint();
|
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
let widgetCounter = 4;
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
const buttons = elementArg.querySelectorAll('dees-button');
|
|
|
|
|
buttons.forEach(button => {
|
|
|
|
|
const text = button.textContent?.trim();
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
|
|
|
|
switch (text) {
|
|
|
|
|
case 'Toggle Animation':
|
|
|
|
|
button.addEventListener('click', () => {
|
|
|
|
|
grid.enableAnimation = !grid.enableAnimation;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'Toggle Grid Lines':
|
|
|
|
|
button.addEventListener('click', () => {
|
|
|
|
|
grid.showGridLines = !grid.showGridLines;
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'Add Widget':
|
|
|
|
|
button.addEventListener('click', () => {
|
|
|
|
|
const newWidget = {
|
|
|
|
|
id: `widget${widgetCounter++}`,
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
w: 3,
|
|
|
|
|
h: 2,
|
|
|
|
|
autoPosition: true,
|
|
|
|
|
title: `Widget ${widgetCounter - 1}`,
|
|
|
|
|
icon: 'lucide:package',
|
|
|
|
|
content: html`
|
|
|
|
|
<div style="padding: 20px; text-align: center;">
|
|
|
|
|
<div style="color: #71717a;">New widget content</div>
|
|
|
|
|
<div style="margin-top: 8px; font-size: 24px; font-weight: 600; color: ${cssManager.bdTheme('#09090b', '#fafafa')};">${Math.floor(
|
|
|
|
|
Math.random() * 1000,
|
|
|
|
|
)}</div>
|
|
|
|
|
</div>
|
|
|
|
|
`,
|
|
|
|
|
};
|
|
|
|
|
grid.addWidget(newWidget, true);
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'Compact Grid':
|
|
|
|
|
button.addEventListener('click', () => {
|
|
|
|
|
grid.compact();
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'Toggle Edit Mode':
|
|
|
|
|
button.addEventListener('click', () => {
|
|
|
|
|
grid.editable = !grid.editable;
|
|
|
|
|
button.textContent = grid.editable ? 'Lock Grid' : 'Unlock Grid';
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'Reset Layout':
|
|
|
|
|
button.addEventListener('click', () => {
|
|
|
|
|
grid.applyBreakpointLayout(grid.activeBreakpoint);
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2025-06-28 12:27:35 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-09-18 10:39:11 +00:00
|
|
|
|
// Enhanced logging for reflow events
|
|
|
|
|
let lastPlaceholderPosition = null;
|
|
|
|
|
let moveEventCounter = 0;
|
|
|
|
|
|
|
|
|
|
// Helper function to log grid state
|
|
|
|
|
const logGridState = (eventName: string, details?: any) => {
|
|
|
|
|
const layout = grid.getLayout();
|
|
|
|
|
console.group(`🔄 ${eventName} [Event #${++moveEventCounter}]`);
|
|
|
|
|
console.log('Timestamp:', new Date().toISOString());
|
|
|
|
|
console.log('Grid Configuration:', {
|
|
|
|
|
columns: grid.columns,
|
|
|
|
|
cellHeight: grid.cellHeight,
|
|
|
|
|
margin: grid.margin,
|
|
|
|
|
editable: grid.editable,
|
|
|
|
|
activeBreakpoint: grid.activeBreakpoint
|
|
|
|
|
});
|
|
|
|
|
console.log('Current Layout:', layout);
|
|
|
|
|
console.log('Widget Count:', layout.length);
|
|
|
|
|
console.log('Grid Bounds:', {
|
|
|
|
|
totalWidgets: grid.widgets.length,
|
|
|
|
|
maxY: Math.max(...layout.map(w => w.y + w.h)),
|
|
|
|
|
occupied: layout.map(w => `${w.id}: (${w.x},${w.y}) ${w.w}x${w.h}`).join(', ')
|
|
|
|
|
});
|
|
|
|
|
if (details) {
|
|
|
|
|
console.log('Event Details:', details);
|
|
|
|
|
}
|
|
|
|
|
console.groupEnd();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Monitor placeholder position changes using MutationObserver
|
|
|
|
|
const placeholderObserver = new MutationObserver(() => {
|
|
|
|
|
const placeholder = grid.shadowRoot?.querySelector('.placeholder') as HTMLElement;
|
|
|
|
|
if (placeholder) {
|
|
|
|
|
const currentPosition = {
|
|
|
|
|
left: placeholder.style.left,
|
|
|
|
|
top: placeholder.style.top,
|
|
|
|
|
width: placeholder.style.width,
|
|
|
|
|
height: placeholder.style.height
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (JSON.stringify(currentPosition) !== JSON.stringify(lastPlaceholderPosition)) {
|
|
|
|
|
console.group('📍 Placeholder Position Changed');
|
|
|
|
|
console.log('Previous:', lastPlaceholderPosition);
|
|
|
|
|
console.log('Current:', currentPosition);
|
|
|
|
|
|
|
|
|
|
// Extract grid coordinates from style
|
|
|
|
|
const gridInfo = grid.shadowRoot?.querySelector('.grid-container');
|
|
|
|
|
if (gridInfo) {
|
|
|
|
|
console.log('Grid Container Dimensions:', {
|
|
|
|
|
width: gridInfo.clientWidth,
|
|
|
|
|
height: gridInfo.clientHeight
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
console.groupEnd();
|
|
|
|
|
lastPlaceholderPosition = currentPosition;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Start observing the shadow DOM for placeholder changes
|
|
|
|
|
if (grid.shadowRoot) {
|
|
|
|
|
placeholderObserver.observe(grid.shadowRoot, {
|
|
|
|
|
childList: true,
|
|
|
|
|
subtree: true,
|
|
|
|
|
attributes: true,
|
|
|
|
|
attributeFilter: ['style']
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Log initial state
|
|
|
|
|
logGridState('Initial Grid State');
|
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
grid.addEventListener('widget-move', (e: CustomEvent) => {
|
2025-09-18 10:39:11 +00:00
|
|
|
|
logGridState('Widget Move', {
|
|
|
|
|
widget: e.detail.widget,
|
|
|
|
|
displaced: e.detail.displaced,
|
|
|
|
|
swappedWith: e.detail.swappedWith
|
|
|
|
|
});
|
2025-06-28 12:27:35 +00:00
|
|
|
|
});
|
2025-09-18 10:39:11 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
grid.addEventListener('widget-resize', (e: CustomEvent) => {
|
2025-09-18 10:39:11 +00:00
|
|
|
|
logGridState('Widget Resize', {
|
|
|
|
|
widget: e.detail.widget,
|
|
|
|
|
displaced: e.detail.displaced,
|
|
|
|
|
swappedWith: e.detail.swappedWith
|
|
|
|
|
});
|
2025-09-17 21:46:44 +00:00
|
|
|
|
});
|
2025-09-18 10:39:11 +00:00
|
|
|
|
|
2025-09-17 21:46:44 +00:00
|
|
|
|
grid.addEventListener('widget-remove', (e: CustomEvent) => {
|
2025-09-18 10:39:11 +00:00
|
|
|
|
logGridState('Widget Remove', {
|
|
|
|
|
removedWidget: e.detail.widget
|
|
|
|
|
});
|
2025-09-17 21:46:44 +00:00
|
|
|
|
updateStatus();
|
|
|
|
|
});
|
2025-09-18 10:39:11 +00:00
|
|
|
|
|
2025-09-17 21:46:44 +00:00
|
|
|
|
grid.addEventListener('layout-change', () => {
|
2025-09-18 10:39:11 +00:00
|
|
|
|
logGridState('Layout Change');
|
2025-09-17 21:46:44 +00:00
|
|
|
|
updateStatus();
|
2025-06-28 12:27:35 +00:00
|
|
|
|
});
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-09-18 10:39:11 +00:00
|
|
|
|
// Monitor during drag/resize operations using pointer events
|
|
|
|
|
grid.addEventListener('pointerdown', (e: PointerEvent) => {
|
|
|
|
|
const isHeader = (e.target as HTMLElement).closest('.widget-header');
|
|
|
|
|
const isResizeHandle = (e.target as HTMLElement).closest('.resize-handle');
|
|
|
|
|
|
|
|
|
|
if (isHeader || isResizeHandle) {
|
|
|
|
|
console.group(`🎯 Interaction Started: ${isHeader ? 'Drag' : 'Resize'}`);
|
|
|
|
|
console.log('Target Widget:', (e.target as HTMLElement).closest('.widget')?.getAttribute('data-widget-id'));
|
|
|
|
|
console.log('Pointer Position:', { x: e.clientX, y: e.clientY });
|
|
|
|
|
console.groupEnd();
|
|
|
|
|
|
|
|
|
|
// Track pointer move during interaction
|
|
|
|
|
const handlePointerMove = (moveEvent: PointerEvent) => {
|
|
|
|
|
const widget = (e.target as HTMLElement).closest('.widget');
|
|
|
|
|
if (widget) {
|
|
|
|
|
console.log(`↔️ Pointer Move:`, {
|
|
|
|
|
widgetId: widget.getAttribute('data-widget-id'),
|
|
|
|
|
position: { x: moveEvent.clientX, y: moveEvent.clientY },
|
|
|
|
|
delta: {
|
|
|
|
|
x: moveEvent.clientX - e.clientX,
|
|
|
|
|
y: moveEvent.clientY - e.clientY
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePointerUp = () => {
|
|
|
|
|
console.group('🏁 Interaction Ended');
|
|
|
|
|
logGridState('Final State After Interaction');
|
|
|
|
|
console.groupEnd();
|
|
|
|
|
document.removeEventListener('pointermove', handlePointerMove);
|
|
|
|
|
document.removeEventListener('pointerup', handlePointerUp);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
document.addEventListener('pointermove', handlePointerMove);
|
|
|
|
|
document.addEventListener('pointerup', handlePointerUp);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Log when widgets are added
|
|
|
|
|
const originalAddWidget = grid.addWidget.bind(grid);
|
|
|
|
|
grid.addWidget = (widget: any, autoPosition?: boolean) => {
|
|
|
|
|
console.group('➕ Adding Widget');
|
|
|
|
|
console.log('New Widget:', widget);
|
|
|
|
|
console.log('Auto Position:', autoPosition);
|
|
|
|
|
const result = originalAddWidget(widget, autoPosition);
|
|
|
|
|
logGridState('After Widget Added');
|
|
|
|
|
console.groupEnd();
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Log compact operations
|
|
|
|
|
const originalCompact = grid.compact.bind(grid);
|
|
|
|
|
grid.compact = (direction?: string) => {
|
|
|
|
|
console.group('🗜️ Compacting Grid');
|
|
|
|
|
console.log('Direction:', direction || 'vertical');
|
|
|
|
|
logGridState('Before Compact');
|
|
|
|
|
const result = originalCompact(direction);
|
|
|
|
|
logGridState('After Compact');
|
|
|
|
|
console.groupEnd();
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-17 21:46:44 +00:00
|
|
|
|
updateStatus();
|
2025-06-28 12:27:35 +00:00
|
|
|
|
}}>
|
|
|
|
|
<style>
|
|
|
|
|
${css`
|
|
|
|
|
.demoBox {
|
|
|
|
|
position: relative;
|
|
|
|
|
background: ${cssManager.bdTheme('#f4f4f5', '#09090b')};
|
|
|
|
|
height: 100%;
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 40px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 24px;
|
|
|
|
|
}
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
.demo-controls {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
.demo-controls dees-button {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
.grid-container-wrapper {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-height: 600px;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
.info {
|
|
|
|
|
color: ${cssManager.bdTheme('#71717a', '#71717a')};
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-family: 'Geist Sans', sans-serif;
|
|
|
|
|
text-align: center;
|
2025-09-17 21:46:44 +00:00
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#dashboardLayoutStatus {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
|
2025-06-28 12:27:35 +00:00
|
|
|
|
}
|
|
|
|
|
`}
|
|
|
|
|
</style>
|
|
|
|
|
<div class="demoBox">
|
|
|
|
|
<div class="demo-controls">
|
|
|
|
|
<dees-button-group label="Animation:">
|
|
|
|
|
<dees-button>Toggle Animation</dees-button>
|
|
|
|
|
</dees-button-group>
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
<dees-button-group label="Display:">
|
|
|
|
|
<dees-button>Toggle Grid Lines</dees-button>
|
|
|
|
|
</dees-button-group>
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
<dees-button-group label="Actions:">
|
|
|
|
|
<dees-button>Add Widget</dees-button>
|
|
|
|
|
<dees-button>Compact Grid</dees-button>
|
2025-09-17 21:46:44 +00:00
|
|
|
|
<dees-button>Reset Layout</dees-button>
|
2025-06-28 12:27:35 +00:00
|
|
|
|
</dees-button-group>
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
<dees-button-group label="Mode:">
|
|
|
|
|
<dees-button>Toggle Edit Mode</dees-button>
|
|
|
|
|
</dees-button-group>
|
|
|
|
|
</div>
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
<div class="grid-container-wrapper">
|
|
|
|
|
<dees-dashboardgrid id="dashboardGrid"></dees-dashboardgrid>
|
|
|
|
|
</div>
|
2025-09-17 21:46:44 +00:00
|
|
|
|
|
2025-06-28 12:27:35 +00:00
|
|
|
|
<div class="info">
|
2025-09-17 21:46:44 +00:00
|
|
|
|
<div>Drag to reposition, resize from handles, or right-click a header to delete a tile.</div>
|
|
|
|
|
<div id="dashboardLayoutStatus"></div>
|
2025-06-28 12:27:35 +00:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</dees-demowrapper>
|
|
|
|
|
`;
|
2025-09-17 21:46:44 +00:00
|
|
|
|
};
|