Sets up calendar package with core infrastructure
Adds core calendar package components including: - Base services for events, resources, and settings - Calendar app and orchestrator - Build and bundling configuration - IndexedDB storage setup Prepares foundational architecture for calendar functionality
This commit is contained in:
parent
12e7594f30
commit
ceb44446f0
97 changed files with 13858 additions and 1 deletions
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* HeaderDrawerLayoutEngine - Calculates row placement for header items
|
||||
*
|
||||
* Prevents visual overlap by assigning items to different rows when
|
||||
* they occupy the same columns. Uses a track-based algorithm similar
|
||||
* to V1's AllDayLayoutEngine.
|
||||
*
|
||||
* Each row can hold multiple items as long as they don't overlap in columns.
|
||||
* When an item spans columns that are already occupied, it's placed in the
|
||||
* next available row.
|
||||
*/
|
||||
|
||||
export interface IHeaderItemLayout {
|
||||
itemId: string;
|
||||
gridArea: string; // "row / col-start / row+1 / col-end"
|
||||
startColumn: number;
|
||||
endColumn: number;
|
||||
row: number;
|
||||
}
|
||||
|
||||
export interface IHeaderItemInput {
|
||||
id: string;
|
||||
columnStart: number; // 0-based column index
|
||||
columnEnd: number; // 0-based end column (inclusive)
|
||||
}
|
||||
|
||||
export class HeaderDrawerLayoutEngine {
|
||||
private tracks: boolean[][] = [];
|
||||
private columnCount: number;
|
||||
|
||||
constructor(columnCount: number) {
|
||||
this.columnCount = columnCount;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset tracks for new layout calculation
|
||||
*/
|
||||
reset(): void {
|
||||
this.tracks = [new Array(this.columnCount).fill(false)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate layout for all items
|
||||
* Items should be sorted by start column for optimal packing
|
||||
*/
|
||||
calculateLayout(items: IHeaderItemInput[]): IHeaderItemLayout[] {
|
||||
this.reset();
|
||||
const layouts: IHeaderItemLayout[] = [];
|
||||
|
||||
for (const item of items) {
|
||||
const row = this.findAvailableRow(item.columnStart, item.columnEnd);
|
||||
|
||||
// Mark columns as occupied in this row
|
||||
for (let col = item.columnStart; col <= item.columnEnd; col++) {
|
||||
this.tracks[row][col] = true;
|
||||
}
|
||||
|
||||
// gridArea format: "row / col-start / row+1 / col-end"
|
||||
// CSS grid uses 1-based indices
|
||||
layouts.push({
|
||||
itemId: item.id,
|
||||
gridArea: `${row + 1} / ${item.columnStart + 1} / ${row + 2} / ${item.columnEnd + 2}`,
|
||||
startColumn: item.columnStart,
|
||||
endColumn: item.columnEnd,
|
||||
row: row + 1 // 1-based for CSS
|
||||
});
|
||||
}
|
||||
|
||||
return layouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate layout for a single new item
|
||||
* Useful for real-time drag operations
|
||||
*/
|
||||
calculateSingleLayout(item: IHeaderItemInput): IHeaderItemLayout {
|
||||
const row = this.findAvailableRow(item.columnStart, item.columnEnd);
|
||||
|
||||
// Mark columns as occupied
|
||||
for (let col = item.columnStart; col <= item.columnEnd; col++) {
|
||||
this.tracks[row][col] = true;
|
||||
}
|
||||
|
||||
return {
|
||||
itemId: item.id,
|
||||
gridArea: `${row + 1} / ${item.columnStart + 1} / ${row + 2} / ${item.columnEnd + 2}`,
|
||||
startColumn: item.columnStart,
|
||||
endColumn: item.columnEnd,
|
||||
row: row + 1
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first row where all columns in range are available
|
||||
*/
|
||||
private findAvailableRow(startCol: number, endCol: number): number {
|
||||
for (let row = 0; row < this.tracks.length; row++) {
|
||||
if (this.isRowAvailable(row, startCol, endCol)) {
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new row if all existing rows are occupied
|
||||
this.tracks.push(new Array(this.columnCount).fill(false));
|
||||
return this.tracks.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if columns in range are all available in given row
|
||||
*/
|
||||
private isRowAvailable(row: number, startCol: number, endCol: number): boolean {
|
||||
for (let col = startCol; col <= endCol; col++) {
|
||||
if (this.tracks[row][col]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of rows currently in use
|
||||
*/
|
||||
getRowCount(): number {
|
||||
return this.tracks.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update column count (e.g., when view changes)
|
||||
*/
|
||||
setColumnCount(count: number): void {
|
||||
this.columnCount = count;
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue