- Fix Muuri integration: add data-draggable and improve DOM sync - Fix Drag Visuals: remove opacity/rotation/scale in NoteCard and CSS to prevent 'gray/crooked' look - Feat(layout): switch to padding-based spacing strategy (16px gap) - Fix(css): correct media queries to maintain consistent spacing
149 lines
4.3 KiB
TypeScript
149 lines
4.3 KiB
TypeScript
/**
|
|
* Masonry Layout Configuration
|
|
*
|
|
* Configuration for responsive masonry grid layout similar to Google Keep
|
|
* Defines breakpoints, columns, and note sizes for different screen sizes
|
|
*/
|
|
|
|
export interface MasonryLayoutConfig {
|
|
breakpoints: {
|
|
mobile: number; // < 480px
|
|
smallTablet: number; // 480px - 768px
|
|
tablet: number; // 768px - 1024px
|
|
desktop: number; // 1024px - 1280px
|
|
largeDesktop: number; // 1280px - 1600px
|
|
extraLarge: number; // > 1600px
|
|
};
|
|
columns: {
|
|
mobile: number;
|
|
smallTablet: number;
|
|
tablet: number;
|
|
desktop: number;
|
|
largeDesktop: number;
|
|
extraLarge: number;
|
|
};
|
|
noteSizes: {
|
|
small: { minHeight: number; width: number };
|
|
medium: { minHeight: number; width: number };
|
|
large: { minHeight: number; width: number };
|
|
};
|
|
gap: number;
|
|
gutter: number;
|
|
}
|
|
|
|
/**
|
|
* Default layout configuration based on Google Keep's behavior
|
|
*
|
|
* Responsive breakpoints:
|
|
* - Mobile (< 480px): 1 column
|
|
* - Small Tablet (480px - 768px): 2 columns
|
|
* - Tablet (768px - 1024px): 2 columns
|
|
* - Desktop (1024px - 1280px): 3 columns
|
|
* - Large Desktop (1280px - 1600px): 4 columns
|
|
* - Extra Large Desktop (> 1600px): 5 columns
|
|
*
|
|
* Note sizes:
|
|
* - Small: Compact cards (150px min height)
|
|
* - Medium: Standard cards (200px min height)
|
|
* - Large: Expanded cards (300px min height)
|
|
*/
|
|
export const DEFAULT_LAYOUT: MasonryLayoutConfig = {
|
|
breakpoints: {
|
|
mobile: 480,
|
|
smallTablet: 768,
|
|
tablet: 1024,
|
|
desktop: 1280,
|
|
largeDesktop: 1600,
|
|
extraLarge: 1920,
|
|
},
|
|
columns: {
|
|
mobile: 1,
|
|
smallTablet: 2,
|
|
tablet: 2,
|
|
desktop: 3,
|
|
largeDesktop: 4,
|
|
extraLarge: 5, // This is just a fallback, calculation is dynamic now
|
|
},
|
|
noteSizes: {
|
|
small: { minHeight: 150, width: 210 }, // Narrower for better density
|
|
medium: { minHeight: 200, width: 240 },
|
|
large: { minHeight: 300, width: 240 },
|
|
},
|
|
gap: 24, // Further increased horizontal gap
|
|
gutter: 24,
|
|
};
|
|
|
|
/**
|
|
* Calculate the number of columns based on container width
|
|
*
|
|
* @param width - Container width in pixels
|
|
* @returns Number of columns to use
|
|
*/
|
|
export function calculateColumns(width: number): number {
|
|
const { noteSizes, gap, breakpoints } = DEFAULT_LAYOUT;
|
|
// Use small note width (240px) as minimum column width basis
|
|
const minColumnWidth = noteSizes.small.width;
|
|
|
|
// For very small screens (mobile), force 1 column
|
|
if (width < breakpoints.mobile) return 1;
|
|
|
|
// For larger screens, calculate max columns that fit
|
|
// Formula: (Width + Gap) / (ColumnWidth + Gap)
|
|
const columns = Math.floor((width + gap) / (minColumnWidth + gap));
|
|
|
|
// Ensure at least 1 column
|
|
return Math.max(1, columns);
|
|
}
|
|
|
|
/**
|
|
* Calculate item width based on container width and number of columns
|
|
*
|
|
* @param containerWidth - Total container width in pixels
|
|
* @param columns - Number of columns to use
|
|
* @returns Item width in pixels
|
|
*/
|
|
export function calculateItemWidth(containerWidth: number, columns: number): number {
|
|
// Return full column width
|
|
// Gaps are now handled by padding inside the masonry-item CSS
|
|
return containerWidth / columns;
|
|
}
|
|
|
|
/**
|
|
* Get note size dimensions (height and width) based on size type
|
|
*
|
|
* @param size - Note size ('small' | 'medium' | 'large')
|
|
* @returns Note dimensions in pixels
|
|
*/
|
|
export function getNoteSizeDimensions(size: 'small' | 'medium' | 'large') {
|
|
return DEFAULT_LAYOUT.noteSizes[size];
|
|
}
|
|
|
|
/**
|
|
* Check if current viewport is mobile
|
|
*
|
|
* @returns true if viewport width is less than mobile breakpoint
|
|
*/
|
|
export function isMobileViewport(): boolean {
|
|
return typeof window !== 'undefined' && window.innerWidth < DEFAULT_LAYOUT.breakpoints.mobile;
|
|
}
|
|
|
|
/**
|
|
* Check if current viewport is tablet
|
|
*
|
|
* @returns true if viewport width is between mobile and desktop breakpoints
|
|
*/
|
|
export function isTabletViewport(): boolean {
|
|
if (typeof window === 'undefined') return false;
|
|
const width = window.innerWidth;
|
|
return width >= DEFAULT_LAYOUT.breakpoints.mobile && width < DEFAULT_LAYOUT.breakpoints.tablet;
|
|
}
|
|
|
|
/**
|
|
* Check if current viewport is desktop
|
|
*
|
|
* @returns true if viewport width is greater than or equal to tablet breakpoint
|
|
*/
|
|
export function isDesktopViewport(): boolean {
|
|
return typeof window !== 'undefined' && window.innerWidth >= DEFAULT_LAYOUT.breakpoints.tablet;
|
|
}
|