406 lines
14 KiB
TypeScript
406 lines
14 KiB
TypeScript
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;
|
||
|
||
const seedWidgets = [
|
||
{
|
||
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>
|
||
`,
|
||
},
|
||
{
|
||
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>
|
||
`,
|
||
},
|
||
{
|
||
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>
|
||
`,
|
||
},
|
||
];
|
||
|
||
grid.widgets = seedWidgets.map(widget => ({ ...widget }));
|
||
grid.cellHeight = 80;
|
||
grid.margin = { top: 10, right: 10, bottom: 10, left: 10 };
|
||
grid.enableAnimation = true;
|
||
grid.showGridLines = false;
|
||
|
||
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();
|
||
};
|
||
if (typeof mediaQuery.addEventListener === 'function') {
|
||
mediaQuery.addEventListener('change', handleBreakpoint);
|
||
} else {
|
||
(mediaQuery as MediaQueryList & {
|
||
addListener?: (listener: (this: MediaQueryList, ev: MediaQueryListEvent) => void) => void;
|
||
}).addListener?.(handleBreakpoint);
|
||
}
|
||
handleBreakpoint();
|
||
|
||
let widgetCounter = 4;
|
||
|
||
const buttons = elementArg.querySelectorAll('dees-button');
|
||
buttons.forEach(button => {
|
||
const text = button.textContent?.trim();
|
||
|
||
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;
|
||
}
|
||
});
|
||
|
||
// 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');
|
||
|
||
grid.addEventListener('widget-move', (e: CustomEvent) => {
|
||
logGridState('Widget Move', {
|
||
widget: e.detail.widget,
|
||
displaced: e.detail.displaced,
|
||
swappedWith: e.detail.swappedWith
|
||
});
|
||
});
|
||
|
||
grid.addEventListener('widget-resize', (e: CustomEvent) => {
|
||
logGridState('Widget Resize', {
|
||
widget: e.detail.widget,
|
||
displaced: e.detail.displaced,
|
||
swappedWith: e.detail.swappedWith
|
||
});
|
||
});
|
||
|
||
grid.addEventListener('widget-remove', (e: CustomEvent) => {
|
||
logGridState('Widget Remove', {
|
||
removedWidget: e.detail.widget
|
||
});
|
||
updateStatus();
|
||
});
|
||
|
||
grid.addEventListener('layout-change', () => {
|
||
logGridState('Layout Change');
|
||
updateStatus();
|
||
});
|
||
|
||
// 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;
|
||
};
|
||
|
||
updateStatus();
|
||
}}>
|
||
<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;
|
||
}
|
||
|
||
.demo-controls {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
}
|
||
|
||
.demo-controls dees-button {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.grid-container-wrapper {
|
||
flex: 1;
|
||
min-height: 600px;
|
||
position: relative;
|
||
}
|
||
|
||
.info {
|
||
color: ${cssManager.bdTheme('#71717a', '#71717a')};
|
||
font-size: 12px;
|
||
font-family: 'Geist Sans', sans-serif;
|
||
text-align: center;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
}
|
||
|
||
#dashboardLayoutStatus {
|
||
font-weight: 600;
|
||
color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
|
||
}
|
||
`}
|
||
</style>
|
||
<div class="demoBox">
|
||
<div class="demo-controls">
|
||
<dees-button-group label="Animation:">
|
||
<dees-button>Toggle Animation</dees-button>
|
||
</dees-button-group>
|
||
|
||
<dees-button-group label="Display:">
|
||
<dees-button>Toggle Grid Lines</dees-button>
|
||
</dees-button-group>
|
||
|
||
<dees-button-group label="Actions:">
|
||
<dees-button>Add Widget</dees-button>
|
||
<dees-button>Compact Grid</dees-button>
|
||
<dees-button>Reset Layout</dees-button>
|
||
</dees-button-group>
|
||
|
||
<dees-button-group label="Mode:">
|
||
<dees-button>Toggle Edit Mode</dees-button>
|
||
</dees-button-group>
|
||
</div>
|
||
|
||
<div class="grid-container-wrapper">
|
||
<dees-dashboardgrid id="dashboardGrid"></dees-dashboardgrid>
|
||
</div>
|
||
|
||
<div class="info">
|
||
<div>Drag to reposition, resize from handles, or right-click a header to delete a tile.</div>
|
||
<div id="dashboardLayoutStatus"></div>
|
||
</div>
|
||
</div>
|
||
</dees-demowrapper>
|
||
`;
|
||
};
|