{"version":3,"file":"AdaptiveGrid.mjs","sources":["../../../../src/lib/layouts/adaptive/AdaptiveGrid.tsx"],"sourcesContent":["/**\n * @fileoverview AdaptiveGrid Component\n *\n * A self-organizing grid layout component that automatically adjusts columns,\n * gaps, and item sizes based on content and available space. Supports masonry\n * layout for variable height items.\n *\n * @module layouts/adaptive/AdaptiveGrid\n * @version 1.0.0\n */\n\nimport {\n  Children,\n  cloneElement,\n  isValidElement,\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n  type CSSProperties,\n  type ReactElement,\n  type ReactNode,\n} from 'react';\nimport type { AdaptiveGridProps } from './types.ts';\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\n/**\n * Golden ratio for aesthetically pleasing layouts.\n */\n// // const _PHI = 1.618033988749895; // Golden ratio for future use\n\n/**\n * Default minimum column width.\n */\nconst DEFAULT_MIN_COLUMN_WIDTH = 200;\n\n/**\n * Default gap between items.\n */\nconst DEFAULT_GAP = 16;\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\n/**\n * Calculates optimal column count based on container width and minimum column width.\n */\nfunction calculateOptimalColumns(\n  containerWidth: number,\n  minColumnWidth: number,\n  maxColumns: number,\n  gap: number\n): number {\n  if (containerWidth <= 0) return 1;\n\n  // Calculate how many columns can fit\n  const availableWidth = containerWidth + gap;\n  const columnWithGap = minColumnWidth + gap;\n  const possibleColumns = Math.floor(availableWidth / columnWithGap);\n\n  return Math.max(1, Math.min(possibleColumns, maxColumns));\n}\n\n/**\n * Generates a unique item ID.\n */\nfunction generateItemId(index: number): string {\n  return `grid-item-${index}`;\n}\n\n// =============================================================================\n// MASONRY LAYOUT CALCULATION\n// =============================================================================\n\ninterface MasonryPosition {\n  column: number;\n  top: number;\n}\n\n/**\n * Calculates masonry layout positions for items.\n */\nfunction calculateMasonryPositions(\n  itemHeights: number[],\n  columns: number,\n  _columnWidth: number,\n  gap: number\n): Map<number, MasonryPosition> {\n  const positions = new Map<number, MasonryPosition>();\n  const columnHeights: number[] = new Array<number>(columns).fill(0);\n\n  for (let i = 0; i < itemHeights.length; i++) {\n    // Find shortest column\n    let shortestColumn = 0;\n    let shortestHeight = columnHeights[0] ?? 0;\n\n    for (let col = 1; col < columns; col++) {\n      const colHeight = columnHeights[col];\n      if (colHeight !== undefined && colHeight < shortestHeight) {\n        shortestColumn = col;\n        shortestHeight = colHeight;\n      }\n    }\n\n    positions.set(i, {\n      column: shortestColumn,\n      top: shortestHeight,\n    });\n\n    // Update column height\n    const itemHeight = itemHeights[i] ?? 0;\n    columnHeights[shortestColumn] = shortestHeight + itemHeight + gap;\n  }\n\n  return positions;\n}\n\n// =============================================================================\n// ADAPTIVE GRID COMPONENT\n// =============================================================================\n\n/**\n * AdaptiveGrid is a self-organizing grid component that automatically adjusts\n * its layout based on content and available space.\n *\n * @example\n * ```tsx\n * <AdaptiveGrid\n *   minColumnWidth={250}\n *   maxColumns={4}\n *   gap={20}\n *   masonry\n * >\n *   {items.map(item => (\n *     <Card key={item.id}>{item.content}</Card>\n *   ))}\n * </AdaptiveGrid>\n * ```\n */\nexport function AdaptiveGrid({\n  children,\n  minColumnWidth = DEFAULT_MIN_COLUMN_WIDTH,\n  maxColumns = 6,\n  gap = DEFAULT_GAP,\n  autoRows = true,\n  autoFlow = 'row',\n  masonry = false,\n  className,\n  style,\n}: AdaptiveGridProps): ReactNode {\n  // Refs\n  const containerRef = useRef<HTMLDivElement>(null);\n  const itemRefs = useRef<Map<number, HTMLElement>>(new Map());\n\n  // State\n  const [containerWidth, setContainerWidth] = useState(0);\n  const [columns, setColumns] = useState(1);\n  const [itemHeights, setItemHeights] = useState<number[]>([]);\n  const [masonryPositions, setMasonryPositions] = useState<Map<number, MasonryPosition>>(new Map());\n\n  // Calculate column width\n  const columnWidth = useMemo(() => {\n    if (containerWidth <= 0) return minColumnWidth;\n    const totalGaps = (columns - 1) * gap;\n    return (containerWidth - totalGaps) / columns;\n  }, [containerWidth, columns, gap, minColumnWidth]);\n\n  // Observe container size\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n\n    const observer = new ResizeObserver((entries) => {\n      const [entry] = entries;\n      if (entry !== undefined) {\n        const { width } = entry.contentRect;\n        setContainerWidth(width);\n        setColumns(calculateOptimalColumns(width, minColumnWidth, maxColumns, gap));\n      }\n    });\n\n    observer.observe(container);\n\n    return () => observer.disconnect();\n  }, [minColumnWidth, maxColumns, gap]);\n\n  // Measure item heights for masonry\n  const measureItems = useCallback(() => {\n    if (!masonry) return;\n\n    const heights: number[] = [];\n    for (let i = 0; i < itemRefs.current.size; i++) {\n      const element = itemRefs.current.get(i);\n      if (element) {\n        heights.push(element.getBoundingClientRect().height);\n      }\n    }\n\n    setItemHeights(heights);\n  }, [masonry]);\n\n  // Calculate masonry positions when heights change\n  useEffect(() => {\n    if (masonry && itemHeights.length > 0 && columns > 0) {\n      const positions = calculateMasonryPositions(itemHeights, columns, columnWidth, gap);\n      setMasonryPositions(positions);\n    }\n  }, [masonry, itemHeights, columns, columnWidth, gap]);\n\n  // Measure items after mount and on children change\n  useEffect(() => {\n    if (masonry) {\n      // Use requestAnimationFrame to ensure DOM has updated\n      const frameId = requestAnimationFrame(measureItems);\n      return () => cancelAnimationFrame(frameId);\n    }\n    return undefined;\n  }, [masonry, measureItems, children]);\n\n  // Register item ref - use a stable callback that doesn't access refs during render\n  const registerItem = useCallback((index: number, element: HTMLElement | null) => {\n    // This callback will be called by React after render, not during render\n    if (element !== null) {\n      itemRefs.current.set(index, element);\n    } else {\n      itemRefs.current.delete(index);\n    }\n  }, []);\n\n  // Container styles\n  const containerStyles = useMemo<CSSProperties>(() => {\n    if (masonry) {\n      // Calculate total height for masonry container\n      let maxHeight = 0;\n      for (const [index, position] of masonryPositions) {\n        const height = itemHeights[index] ?? 0;\n        const bottom = position.top + height;\n        if (bottom > maxHeight) {\n          maxHeight = bottom;\n        }\n      }\n\n      return {\n        position: 'relative',\n        width: '100%',\n        minHeight: maxHeight > 0 ? `${maxHeight}px` : undefined,\n        ...style,\n      };\n    }\n\n    return {\n      display: 'grid',\n      gridTemplateColumns: `repeat(${columns}, 1fr)`,\n      gridAutoRows: autoRows ? 'auto' : undefined,\n      gridAutoFlow: autoFlow === 'dense' ? 'row dense' : autoFlow,\n      gap: `${gap}px`,\n      width: '100%',\n      ...style,\n    };\n  }, [masonry, masonryPositions, itemHeights, columns, autoRows, autoFlow, gap, style]);\n\n  // Render children with adaptive positioning\n\n  const renderedChildren = useMemo(() => {\n    const childArray = Children.toArray(children);\n\n    return childArray.map(async (child, index) => {\n      if (!isValidElement(child)) return child;\n\n      const itemId = generateItemId(index);\n\n      if (masonry) {\n        const position = masonryPositions.get(index);\n        const itemStyle: CSSProperties = position\n          ? {\n              position: 'absolute',\n              left: `${position.column * (columnWidth + gap)}px`,\n              top: `${position.top}px`,\n              width: `${columnWidth}px`,\n            }\n          : {\n              position: 'absolute',\n              visibility: 'hidden',\n              width: `${columnWidth}px`,\n            };\n\n        return cloneElement(\n          child as ReactElement<{\n            style?: CSSProperties;\n            ref?: (el: HTMLElement | null) => void;\n            'data-layout-id'?: string;\n          }>,\n          {\n            key: itemId,\n            'data-layout-id': itemId,\n            style: { ...(child.props as { style?: CSSProperties }).style, ...itemStyle },\n            ref: (el: HTMLElement | null) => registerItem(index, el),\n          }\n        );\n      }\n\n      return cloneElement(child as ReactElement<{ 'data-layout-id'?: string }>, {\n        key: itemId,\n        'data-layout-id': itemId,\n      });\n    });\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [children, masonry, masonryPositions, columnWidth, gap]);\n\n  return (\n    <div\n      ref={containerRef}\n      className={className}\n      style={containerStyles}\n      data-adaptive-grid\n      data-columns={columns}\n      data-masonry={masonry}\n    >\n      {renderedChildren}\n    </div>\n  );\n}\n\n// =============================================================================\n// EXPORTS\n// =============================================================================\n\nexport type { AdaptiveGridProps };\n"],"names":["DEFAULT_MIN_COLUMN_WIDTH","DEFAULT_GAP","calculateOptimalColumns","containerWidth","minColumnWidth","maxColumns","gap","availableWidth","columnWithGap","possibleColumns","generateItemId","index","calculateMasonryPositions","itemHeights","columns","_columnWidth","positions","columnHeights","i","shortestColumn","shortestHeight","col","colHeight","itemHeight","AdaptiveGrid","children","autoRows","autoFlow","masonry","className","style","containerRef","useRef","itemRefs","setContainerWidth","useState","setColumns","setItemHeights","masonryPositions","setMasonryPositions","columnWidth","useMemo","totalGaps","useEffect","container","observer","entries","entry","width","measureItems","useCallback","heights","element","frameId","registerItem","containerStyles","maxHeight","position","height","bottom","renderedChildren","Children","child","isValidElement","itemId","itemStyle","cloneElement","el","jsx"],"mappings":"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"}