@liiift-studio/mac-os9-ui 0.2.23 → 0.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -718,7 +718,45 @@ const Tabs = ({ children, defaultActiveTab = 0, activeTab: controlledActiveTab,
718
718
  };
719
719
  Tabs.displayName = 'Tabs';
720
720
 
721
- var styles$6 = {"window":"Window-module_window","window--active":"Window-module_window--active","window--inactive":"Window-module_window--inactive","titleBar":"Window-module_titleBar","titleCenter":"Window-module_titleCenter","controls":"Window-module_controls","controlButton":"Window-module_controlButton","closeBox":"Window-module_closeBox","minimizeBox":"Window-module_minimizeBox","maximizeBox":"Window-module_maximizeBox","titleText":"Window-module_titleText","content":"Window-module_content","resizeHandle":"Window-module_resizeHandle"};
721
+ // Utility for merging CSS class names
722
+ // Filters out falsy values and joins valid class names with spaces
723
+ /**
724
+ * Merges multiple class names into a single string
725
+ * Filters out undefined, null, false, and empty strings
726
+ *
727
+ * @param classes - Class names to merge
728
+ * @returns Merged class name string
729
+ *
730
+ * @example
731
+ * ```ts
732
+ * mergeClasses('base', isActive && 'active', undefined, 'custom')
733
+ * // Returns: "base active custom"
734
+ * ```
735
+ */
736
+ const mergeClasses = (...classes) => {
737
+ return classes.filter(Boolean).join(' ');
738
+ };
739
+ /**
740
+ * Creates a class name builder function with a base class
741
+ * Useful for component-level class management
742
+ *
743
+ * @param baseClass - Base class name
744
+ * @returns Function that merges additional classes with base
745
+ *
746
+ * @example
747
+ * ```ts
748
+ * const cn = createClassBuilder('button');
749
+ * cn('primary', isDisabled && 'disabled')
750
+ * // Returns: "button primary disabled"
751
+ * ```
752
+ */
753
+ const createClassBuilder = (baseClass) => {
754
+ return (...additionalClasses) => {
755
+ return mergeClasses(baseClass, ...additionalClasses);
756
+ };
757
+ };
758
+
759
+ var styles$6 = {"window":"Window-module_window","window--active":"Window-module_window--active","window--inactive":"Window-module_window--inactive","window--draggable":"Window-module_window--draggable","titleBar":"Window-module_titleBar","titleCenter":"Window-module_titleCenter","titleBar--draggable":"Window-module_titleBar--draggable","titleBar--dragging":"Window-module_titleBar--dragging","controls":"Window-module_controls","controlButton":"Window-module_controlButton","closeBox":"Window-module_closeBox","minimizeBox":"Window-module_minimizeBox","maximizeBox":"Window-module_maximizeBox","titleText":"Window-module_titleText","content":"Window-module_content","resizeHandle":"Window-module_resizeHandle"};
722
760
 
723
761
  /**
724
762
  * Mac OS 9 style Window component
@@ -731,6 +769,7 @@ var styles$6 = {"window":"Window-module_window","window--active":"Window-module_
731
769
  * - Active/inactive states
732
770
  * - Composable with custom TitleBar component
733
771
  * - Flexible sizing
772
+ * - Draggable windows (optional) - drag by title bar
734
773
  *
735
774
  * @example
736
775
  * ```tsx
@@ -752,18 +791,120 @@ var styles$6 = {"window":"Window-module_window","window--active":"Window-module_
752
791
  * >
753
792
  * <p>Content</p>
754
793
  * </Window>
794
+ *
795
+ * // Draggable window (uncontrolled)
796
+ * <Window title="Draggable" draggable>
797
+ * <p>Drag me by the title bar!</p>
798
+ * </Window>
799
+ *
800
+ * // Draggable window with initial position
801
+ * <Window
802
+ * title="Positioned"
803
+ * draggable
804
+ * defaultPosition={{ x: 100, y: 100 }}
805
+ * >
806
+ * <p>Starts at a specific position</p>
807
+ * </Window>
808
+ *
809
+ * // Controlled draggable window
810
+ * const [pos, setPos] = useState({ x: 0, y: 0 });
811
+ * <Window
812
+ * title="Controlled"
813
+ * draggable
814
+ * position={pos}
815
+ * onPositionChange={setPos}
816
+ * >
817
+ * <p>Parent controls position</p>
818
+ * </Window>
755
819
  * ```
756
820
  */
757
- const Window = React.forwardRef(({ children, title, titleBar, active = true, width = 'auto', height = 'auto', className = '', contentClassName = '', showControls = true, onClose, onMinimize, onMaximize, onMouseEnter, resizable = false, }, ref) => {
821
+ const Window = React.forwardRef(({ children, title, titleBar, active = true, width = 'auto', height = 'auto', className = '', contentClassName = '', classes, showControls = true, onClose, onMinimize, onMaximize, onMouseEnter, resizable = false, draggable = false, defaultPosition, position: controlledPosition, onPositionChange, }, ref) => {
822
+ // Drag state management
823
+ const [internalPosition, setInternalPosition] = React.useState(defaultPosition || null);
824
+ const [isDragging, setIsDragging] = React.useState(false);
825
+ const [hasBeenDragged, setHasBeenDragged] = React.useState(!!(defaultPosition || controlledPosition));
826
+ const dragStartRef = React.useRef(null);
827
+ // Use controlled position if provided, otherwise use internal state
828
+ const currentPosition = controlledPosition || internalPosition;
829
+ // Handle mouse down on title bar to start dragging
830
+ const handleTitleBarMouseDown = React.useCallback((event) => {
831
+ if (!draggable)
832
+ return;
833
+ // Don't start drag if clicking on buttons
834
+ if (event.target.closest('button')) {
835
+ return;
836
+ }
837
+ event.preventDefault();
838
+ const windowElement = event.currentTarget.closest(`.${styles$6.window}`);
839
+ if (!windowElement)
840
+ return;
841
+ const rect = windowElement.getBoundingClientRect();
842
+ // Get the parent container to calculate position relative to it
843
+ const parent = windowElement.offsetParent;
844
+ const parentRect = parent ? parent.getBoundingClientRect() : { left: 0, top: 0 };
845
+ // Store drag start info - offset from mouse to window position within parent
846
+ // This accounts for the parent's coordinate system
847
+ dragStartRef.current = {
848
+ x: event.clientX - (rect.left - parentRect.left),
849
+ y: event.clientY - (rect.top - parentRect.top),
850
+ };
851
+ setIsDragging(true);
852
+ }, [draggable]);
853
+ // Handle mouse move during drag
854
+ React.useEffect(() => {
855
+ if (!isDragging || !dragStartRef.current)
856
+ return;
857
+ const handleMouseMove = (event) => {
858
+ event.preventDefault();
859
+ if (!dragStartRef.current)
860
+ return;
861
+ // Get the window element to find its parent
862
+ const windowElements = document.querySelectorAll(`.${styles$6.window}`);
863
+ let windowElement = null;
864
+ // Find the dragging window (the one with position absolute or the first one)
865
+ for (const el of Array.from(windowElements)) {
866
+ const htmlEl = el;
867
+ if (htmlEl.style.position === 'absolute' || windowElements.length === 1) {
868
+ windowElement = htmlEl;
869
+ break;
870
+ }
871
+ }
872
+ if (!windowElement)
873
+ return;
874
+ // Get parent container to calculate position relative to it
875
+ const parent = windowElement.offsetParent;
876
+ const parentRect = parent ? parent.getBoundingClientRect() : { left: 0, top: 0 };
877
+ const newPosition = {
878
+ x: event.clientX - parentRect.left - dragStartRef.current.x,
879
+ y: event.clientY - parentRect.top - dragStartRef.current.y,
880
+ };
881
+ // Update position
882
+ if (controlledPosition && onPositionChange) {
883
+ onPositionChange(newPosition);
884
+ }
885
+ else {
886
+ setInternalPosition(newPosition);
887
+ }
888
+ // Mark as dragged
889
+ if (!hasBeenDragged) {
890
+ setHasBeenDragged(true);
891
+ }
892
+ };
893
+ const handleMouseUp = () => {
894
+ setIsDragging(false);
895
+ dragStartRef.current = null;
896
+ };
897
+ document.addEventListener('mousemove', handleMouseMove);
898
+ document.addEventListener('mouseup', handleMouseUp);
899
+ return () => {
900
+ document.removeEventListener('mousemove', handleMouseMove);
901
+ document.removeEventListener('mouseup', handleMouseUp);
902
+ };
903
+ }, [isDragging, controlledPosition, onPositionChange, hasBeenDragged]);
758
904
  // Class names
759
- const windowClassNames = [
760
- styles$6.window,
761
- active ? styles$6['window--active'] : styles$6['window--inactive'],
762
- className,
763
- ]
764
- .filter(Boolean)
765
- .join(' ');
766
- const contentClassNames = [styles$6.content, contentClassName].filter(Boolean).join(' ');
905
+ const windowClassNames = mergeClasses(styles$6.window, active ? styles$6['window--active'] : styles$6['window--inactive'], draggable && hasBeenDragged && styles$6['window--draggable'], className, classes?.root);
906
+ const contentClassNames = mergeClasses(styles$6.content, contentClassName, classes?.content);
907
+ const titleBarClassNames = mergeClasses(styles$6.titleBar, draggable && styles$6['titleBar--draggable'], isDragging && styles$6['titleBar--dragging'], classes?.titleBar);
767
908
  // Window style
768
909
  const windowStyle = {};
769
910
  if (width !== 'auto') {
@@ -772,13 +913,19 @@ const Window = React.forwardRef(({ children, title, titleBar, active = true, wid
772
913
  if (height !== 'auto') {
773
914
  windowStyle.height = typeof height === 'number' ? `${height}px` : height;
774
915
  }
916
+ // Apply position if draggable and has been dragged
917
+ if (draggable && hasBeenDragged && currentPosition) {
918
+ windowStyle.position = 'absolute';
919
+ windowStyle.left = `${currentPosition.x}px`;
920
+ windowStyle.top = `${currentPosition.y}px`;
921
+ }
775
922
  // Render title bar if title provided and no custom titleBar
776
923
  const renderTitleBar = () => {
777
924
  if (titleBar) {
778
925
  return titleBar;
779
926
  }
780
927
  if (title) {
781
- return (jsxRuntime.jsxs("div", { className: styles$6.titleBar, "data-numControls": [onClose, onMinimize, onMaximize].filter(Boolean).length, children: [showControls && (jsxRuntime.jsxs("div", { className: styles$6.controls, children: [onClose && (jsxRuntime.jsx("button", { type: "button", className: styles$6.controlButton, onClick: onClose, "aria-label": "Close", title: "Close", children: jsxRuntime.jsx("div", { className: styles$6.closeBox }) })), onMinimize && (jsxRuntime.jsx("button", { type: "button", className: styles$6.controlButton, onClick: onMinimize, "aria-label": "Minimize", title: "Minimize", children: jsxRuntime.jsx("div", { className: styles$6.minimizeBox }) })), onMaximize && (jsxRuntime.jsx("button", { type: "button", className: styles$6.controlButton, onClick: onMaximize, "aria-label": "Maximize", title: "Maximize", children: jsxRuntime.jsx("div", { className: styles$6.maximizeBox }) }))] })), jsxRuntime.jsxs("div", { className: styles$6.titleCenter, children: [jsxRuntime.jsxs("svg", { width: "132", height: "13", viewBox: "0 0 132 13", fill: "none", preserveAspectRatio: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("rect", { width: "130.517", height: "13", fill: "#DDDDDD" }), jsxRuntime.jsx("rect", { width: "1", height: "13", fill: "#EEEEEE" }), jsxRuntime.jsx("rect", { x: "130", width: "1", height: "13", fill: "#C5C5C5" }), jsxRuntime.jsx("rect", { y: "1", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "5", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "9", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "3", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "7", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "11", width: "131.268", height: "1", fill: "#999999" })] }), jsxRuntime.jsx("div", { className: `${styles$6.titleText} bold`, children: title }), jsxRuntime.jsxs("svg", { width: "132", height: "13", viewBox: "0 0 132 13", fill: "none", preserveAspectRatio: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("rect", { width: "130.517", height: "13", fill: "#DDDDDD" }), jsxRuntime.jsx("rect", { width: "1", height: "13", fill: "#EEEEEE" }), jsxRuntime.jsx("rect", { x: "130", width: "1", height: "13", fill: "#C5C5C5" }), jsxRuntime.jsx("rect", { y: "1", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "5", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "9", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "3", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "7", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "11", width: "131.268", height: "1", fill: "#999999" })] })] })] }));
928
+ return (jsxRuntime.jsxs("div", { className: titleBarClassNames, "data-numControls": [onClose, onMinimize, onMaximize].filter(Boolean).length, onMouseDown: handleTitleBarMouseDown, children: [showControls && (jsxRuntime.jsxs("div", { className: mergeClasses(styles$6.controls, classes?.controls), children: [onClose && (jsxRuntime.jsx("button", { type: "button", className: mergeClasses(styles$6.controlButton, classes?.controlButton), onClick: onClose, "aria-label": "Close", title: "Close", children: jsxRuntime.jsx("div", { className: styles$6.closeBox }) })), onMinimize && (jsxRuntime.jsx("button", { type: "button", className: mergeClasses(styles$6.controlButton, classes?.controlButton), onClick: onMinimize, "aria-label": "Minimize", title: "Minimize", children: jsxRuntime.jsx("div", { className: styles$6.minimizeBox }) })), onMaximize && (jsxRuntime.jsx("button", { type: "button", className: mergeClasses(styles$6.controlButton, classes?.controlButton), onClick: onMaximize, "aria-label": "Maximize", title: "Maximize", children: jsxRuntime.jsx("div", { className: styles$6.maximizeBox }) }))] })), jsxRuntime.jsxs("div", { className: styles$6.titleCenter, children: [jsxRuntime.jsxs("svg", { width: "132", height: "13", viewBox: "0 0 132 13", fill: "none", preserveAspectRatio: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("rect", { width: "130.517", height: "13", fill: "#DDDDDD" }), jsxRuntime.jsx("rect", { width: "1", height: "13", fill: "#EEEEEE" }), jsxRuntime.jsx("rect", { x: "130", width: "1", height: "13", fill: "#C5C5C5" }), jsxRuntime.jsx("rect", { y: "1", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "5", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "9", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "3", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "7", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "11", width: "131.268", height: "1", fill: "#999999" })] }), jsxRuntime.jsx("div", { className: mergeClasses(styles$6.titleText, classes?.titleText, 'bold'), children: title }), jsxRuntime.jsxs("svg", { width: "132", height: "13", viewBox: "0 0 132 13", fill: "none", preserveAspectRatio: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("rect", { width: "130.517", height: "13", fill: "#DDDDDD" }), jsxRuntime.jsx("rect", { width: "1", height: "13", fill: "#EEEEEE" }), jsxRuntime.jsx("rect", { x: "130", width: "1", height: "13", fill: "#C5C5C5" }), jsxRuntime.jsx("rect", { y: "1", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "5", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "9", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "3", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "7", width: "131.268", height: "1", fill: "#999999" }), jsxRuntime.jsx("rect", { y: "11", width: "131.268", height: "1", fill: "#999999" })] })] })] }));
782
929
  }
783
930
  return null;
784
931
  };
@@ -1384,11 +1531,13 @@ var styles$1 = {"listView":"ListView-module_listView","header":"ListView-module_
1384
1531
  * />
1385
1532
  * ```
1386
1533
  */
1387
- const ListView = React.forwardRef(({ columns, items, selectedIds = [], onSelectionChange, onItemOpen, onItemMouseEnter, onSort, className = '', height = 'auto', }, ref) => {
1534
+ const ListView = React.forwardRef(({ columns, items, selectedIds = [], onSelectionChange, onItemOpen, onItemMouseEnter, onItemMouseLeave, onSort, className = '', height = 'auto', classes, renderRow, renderCell, renderHeaderCell, onCellClick, onCellMouseEnter, onCellMouseLeave, }, ref) => {
1388
1535
  const [sortColumn, setSortColumn] = React.useState(null);
1389
1536
  const [sortDirection, setSortDirection] = React.useState('asc');
1537
+ const [hoveredRow, setHoveredRow] = React.useState(null);
1538
+ const [hoveredCell, setHoveredCell] = React.useState(null);
1390
1539
  // Class names
1391
- const classNames = [styles$1.listView, className].filter(Boolean).join(' ');
1540
+ const classNames = mergeClasses(styles$1.listView, className, classes?.root);
1392
1541
  // Handle column header click
1393
1542
  const handleColumnClick = React.useCallback((columnKey, sortable = true) => {
1394
1543
  if (!sortable || !onSort)
@@ -1433,7 +1582,7 @@ const ListView = React.forwardRef(({ columns, items, selectedIds = [], onSelecti
1433
1582
  }
1434
1583
  }, [onItemOpen]);
1435
1584
  // Handle row mouse enter
1436
- const handleRowMouseEnter = React.useCallback((item) => {
1585
+ React.useCallback((item) => {
1437
1586
  if (onItemMouseEnter) {
1438
1587
  onItemMouseEnter(item);
1439
1588
  }
@@ -1443,17 +1592,108 @@ const ListView = React.forwardRef(({ columns, items, selectedIds = [], onSelecti
1443
1592
  if (height !== 'auto') {
1444
1593
  containerStyle.height = typeof height === 'number' ? `${height}px` : height;
1445
1594
  }
1446
- return (jsxRuntime.jsxs("div", { ref: ref, className: classNames, style: containerStyle, children: [jsxRuntime.jsx("div", { className: styles$1.header, children: columns.map((column) => (jsxRuntime.jsxs("div", { className: `${styles$1.headerCell} ${column.sortable !== false ? styles$1.sortable : ''}`, style: {
1447
- width: typeof column.width === 'number'
1448
- ? `${column.width}px`
1449
- : column.width,
1450
- }, onClick: () => handleColumnClick(column.key, column.sortable), children: [column.label, sortColumn === column.key && (jsxRuntime.jsx("span", { className: styles$1.sortIndicator, children: sortDirection === 'asc' ? '▲' : '▼' }))] }, column.key))) }), jsxRuntime.jsx("div", { className: styles$1.body, children: items.map((item) => {
1595
+ return (jsxRuntime.jsxs("div", { ref: ref, className: classNames, style: containerStyle, children: [jsxRuntime.jsx("div", { className: mergeClasses(styles$1.header, classes?.header), children: columns.map((column) => {
1596
+ const isSorted = sortColumn === column.key;
1597
+ const headerState = {
1598
+ isSorted,
1599
+ sortDirection: isSorted ? sortDirection : undefined,
1600
+ };
1601
+ const headerDefaultProps = {
1602
+ key: column.key,
1603
+ className: mergeClasses(styles$1.headerCell, column.sortable !== false && styles$1.sortable, classes?.headerCell),
1604
+ style: {
1605
+ width: typeof column.width === 'number'
1606
+ ? `${column.width}px`
1607
+ : column.width,
1608
+ },
1609
+ onClick: () => handleColumnClick(column.key, column.sortable),
1610
+ 'data-column': column.key,
1611
+ 'data-sortable': column.sortable !== false,
1612
+ ...(isSorted && {
1613
+ 'data-sorted': true,
1614
+ 'data-sort-direction': sortDirection,
1615
+ }),
1616
+ };
1617
+ // Use custom render or default
1618
+ if (renderHeaderCell) {
1619
+ return renderHeaderCell(column, headerState, headerDefaultProps);
1620
+ }
1621
+ // Default header cell rendering
1622
+ return (jsxRuntime.jsxs("div", { ...headerDefaultProps, children: [column.label, isSorted && (jsxRuntime.jsx("span", { className: styles$1.sortIndicator, children: sortDirection === 'asc' ? '▲' : '▼' }))] }));
1623
+ }) }), jsxRuntime.jsx("div", { className: mergeClasses(styles$1.body, classes?.body), children: items.map((item, rowIndex) => {
1451
1624
  const isSelected = selectedIds.includes(item.id);
1452
- return (jsxRuntime.jsx("div", { className: `${styles$1.row} ${isSelected ? styles$1.selected : ''}`, onClick: (e) => handleRowClick(item.id, e), onDoubleClick: () => handleRowDoubleClick(item), onMouseEnter: () => handleRowMouseEnter(item), children: columns.map((column, index) => (jsxRuntime.jsxs("div", { className: styles$1.cell, style: {
1453
- width: typeof column.width === 'number'
1454
- ? `${column.width}px`
1455
- : column.width,
1456
- }, children: [index === 0 && item.icon && (jsxRuntime.jsx("span", { className: styles$1.icon, children: item.icon })), item[column.key]] }, column.key))) }, item.id));
1625
+ const isHovered = hoveredRow === item.id;
1626
+ const rowState = {
1627
+ isSelected,
1628
+ isHovered,
1629
+ index: rowIndex,
1630
+ };
1631
+ const rowDefaultProps = {
1632
+ key: item.id,
1633
+ className: mergeClasses(styles$1.row, isSelected && styles$1.selected, classes?.row),
1634
+ onClick: (e) => handleRowClick(item.id, e),
1635
+ onDoubleClick: () => handleRowDoubleClick(item),
1636
+ onMouseEnter: () => {
1637
+ setHoveredRow(item.id);
1638
+ onItemMouseEnter?.(item);
1639
+ },
1640
+ onMouseLeave: () => {
1641
+ setHoveredRow(null);
1642
+ setHoveredCell(null);
1643
+ onItemMouseLeave?.(item);
1644
+ },
1645
+ 'data-selected': isSelected,
1646
+ 'data-index': rowIndex,
1647
+ 'data-item-id': item.id,
1648
+ };
1649
+ // Use custom row render or default
1650
+ if (renderRow) {
1651
+ return renderRow(item, rowState, rowDefaultProps);
1652
+ }
1653
+ // Default row rendering
1654
+ return (jsxRuntime.jsx("div", { ...rowDefaultProps, children: columns.map((column, columnIndex) => {
1655
+ const value = item[column.key];
1656
+ const isCellHovered = hoveredCell?.rowId === item.id &&
1657
+ hoveredCell?.columnKey === column.key;
1658
+ const cellState = {
1659
+ isHovered: isCellHovered,
1660
+ isRowSelected: isSelected,
1661
+ columnIndex,
1662
+ rowIndex,
1663
+ };
1664
+ // Cell event handlers
1665
+ const handleCellClick = (e) => {
1666
+ if (onCellClick) {
1667
+ onCellClick(item, column, e);
1668
+ }
1669
+ };
1670
+ const handleCellMouseEnter = () => {
1671
+ setHoveredCell({ rowId: item.id, columnKey: column.key });
1672
+ if (onCellMouseEnter) {
1673
+ onCellMouseEnter(item, column);
1674
+ }
1675
+ };
1676
+ const handleCellMouseLeave = () => {
1677
+ setHoveredCell(null);
1678
+ if (onCellMouseLeave) {
1679
+ onCellMouseLeave(item, column);
1680
+ }
1681
+ };
1682
+ // Use custom cell render or default
1683
+ if (renderCell) {
1684
+ return (jsxRuntime.jsx("div", { className: mergeClasses(styles$1.cell, classes?.cell), style: {
1685
+ width: typeof column.width === 'number'
1686
+ ? `${column.width}px`
1687
+ : column.width,
1688
+ }, "data-column": column.key, "data-hovered": isCellHovered, onClick: handleCellClick, onMouseEnter: handleCellMouseEnter, onMouseLeave: handleCellMouseLeave, children: renderCell(value, item, column, cellState) }, column.key));
1689
+ }
1690
+ // Default cell rendering
1691
+ return (jsxRuntime.jsxs("div", { className: mergeClasses(styles$1.cell, classes?.cell), style: {
1692
+ width: typeof column.width === 'number'
1693
+ ? `${column.width}px`
1694
+ : column.width,
1695
+ }, "data-column": column.key, "data-hovered": isCellHovered, onClick: handleCellClick, onMouseEnter: handleCellMouseEnter, onMouseLeave: handleCellMouseLeave, children: [columnIndex === 0 && item.icon && (jsxRuntime.jsx("span", { className: styles$1.icon, children: item.icon })), value] }, column.key));
1696
+ }) }));
1457
1697
  }) })] }));
1458
1698
  });
1459
1699
  ListView.displayName = 'ListView';
@@ -1470,6 +1710,7 @@ var styles = {"folderListContent":"FolderList-module_folderListContent","listVie
1470
1710
  *
1471
1711
  * @example
1472
1712
  * ```tsx
1713
+ * // Basic folder list
1473
1714
  * <FolderList
1474
1715
  * title="My Documents"
1475
1716
  * items={[
@@ -1479,8 +1720,14 @@ var styles = {"folderListContent":"FolderList-module_folderListContent","listVie
1479
1720
  * selectedIds={['1']}
1480
1721
  * onSelectionChange={(ids) => console.log('Selected:', ids)}
1481
1722
  * onItemOpen={(item) => console.log('Open:', item.name)}
1482
- * onItemMouseEnter={(item) => console.log('Hovering:', item.name)}
1483
- * onMouseEnter={(e) => console.log('Mouse entered folder list')}
1723
+ * />
1724
+ *
1725
+ * // Draggable folder list
1726
+ * <FolderList
1727
+ * title="My Documents"
1728
+ * items={items}
1729
+ * draggable
1730
+ * defaultPosition={{ x: 100, y: 100 }}
1484
1731
  * />
1485
1732
  * ```
1486
1733
  */
@@ -1488,9 +1735,18 @@ const FolderList = React.forwardRef(({ columns = [
1488
1735
  { key: 'name', label: 'Name', width: '40%' },
1489
1736
  { key: 'modified', label: 'Date Modified', width: '30%' },
1490
1737
  { key: 'size', label: 'Size', width: '30%' },
1491
- ], items, selectedIds, onSelectionChange, onItemOpen, onItemMouseEnter, onSort, onMouseEnter, listHeight = 400, ...windowProps }, ref) => {
1738
+ ], items, selectedIds, onSelectionChange, onItemOpen, onItemMouseEnter, onItemMouseLeave, onSort, onMouseEnter, listHeight = 400, classes, renderRow, renderCell, renderHeaderCell, onCellClick, onCellMouseEnter, onCellMouseLeave, ...windowProps }, ref) => {
1739
+ // Build ListView classes from FolderList classes
1740
+ const listViewClasses = classes ? {
1741
+ root: classes.listView,
1742
+ header: classes.header,
1743
+ headerCell: classes.headerCell,
1744
+ body: classes.body,
1745
+ row: classes.row,
1746
+ cell: classes.cell,
1747
+ } : undefined;
1492
1748
  // Window content with ListView
1493
- return (jsxRuntime.jsx(Window, { ref: ref, contentClassName: styles.folderListContent, onMouseEnter: onMouseEnter, ...windowProps, children: jsxRuntime.jsx(ListView, { columns: columns, items: items, selectedIds: selectedIds, onSelectionChange: onSelectionChange, onItemOpen: onItemOpen, onItemMouseEnter: onItemMouseEnter, onSort: onSort, height: listHeight, className: styles.listView }) }));
1749
+ return (jsxRuntime.jsx(Window, { ref: ref, contentClassName: styles.folderListContent, onMouseEnter: onMouseEnter, className: classes?.root, ...windowProps, children: jsxRuntime.jsx(ListView, { columns: columns, items: items, selectedIds: selectedIds, onSelectionChange: onSelectionChange, onItemOpen: onItemOpen, onItemMouseEnter: onItemMouseEnter, onItemMouseLeave: onItemMouseLeave, onSort: onSort, height: listHeight, className: styles.listView, classes: listViewClasses, renderRow: renderRow, renderCell: renderCell, renderHeaderCell: renderHeaderCell, onCellClick: onCellClick, onCellMouseEnter: onCellMouseEnter, onCellMouseLeave: onCellMouseLeave }) }));
1494
1750
  });
1495
1751
  FolderList.displayName = 'FolderList';
1496
1752
 
@@ -1733,6 +1989,8 @@ exports.TextField = TextField;
1733
1989
  exports.Window = Window;
1734
1990
  exports.borders = borders;
1735
1991
  exports.colors = colors;
1992
+ exports.createClassBuilder = createClassBuilder;
1993
+ exports.mergeClasses = mergeClasses;
1736
1994
  exports.shadows = shadows;
1737
1995
  exports.spacing = spacing;
1738
1996
  exports.tokens = tokens;