@liiift-studio/mac-os9-ui 0.2.21 → 0.2.24

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,100 @@ 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
+ // Store drag start info
843
+ dragStartRef.current = {
844
+ x: event.clientX - rect.left,
845
+ y: event.clientY - rect.top,
846
+ };
847
+ setIsDragging(true);
848
+ }, [draggable]);
849
+ // Handle mouse move during drag
850
+ React.useEffect(() => {
851
+ if (!isDragging || !dragStartRef.current)
852
+ return;
853
+ const handleMouseMove = (event) => {
854
+ event.preventDefault();
855
+ if (!dragStartRef.current)
856
+ return;
857
+ const newPosition = {
858
+ x: event.clientX - dragStartRef.current.x,
859
+ y: event.clientY - dragStartRef.current.y,
860
+ };
861
+ // Update position
862
+ if (controlledPosition && onPositionChange) {
863
+ onPositionChange(newPosition);
864
+ }
865
+ else {
866
+ setInternalPosition(newPosition);
867
+ }
868
+ // Mark as dragged
869
+ if (!hasBeenDragged) {
870
+ setHasBeenDragged(true);
871
+ }
872
+ };
873
+ const handleMouseUp = () => {
874
+ setIsDragging(false);
875
+ dragStartRef.current = null;
876
+ };
877
+ document.addEventListener('mousemove', handleMouseMove);
878
+ document.addEventListener('mouseup', handleMouseUp);
879
+ return () => {
880
+ document.removeEventListener('mousemove', handleMouseMove);
881
+ document.removeEventListener('mouseup', handleMouseUp);
882
+ };
883
+ }, [isDragging, controlledPosition, onPositionChange, hasBeenDragged]);
758
884
  // 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(' ');
885
+ const windowClassNames = mergeClasses(styles$6.window, active ? styles$6['window--active'] : styles$6['window--inactive'], draggable && hasBeenDragged && styles$6['window--draggable'], className, classes?.root);
886
+ const contentClassNames = mergeClasses(styles$6.content, contentClassName, classes?.content);
887
+ const titleBarClassNames = mergeClasses(styles$6.titleBar, draggable && styles$6['titleBar--draggable'], isDragging && styles$6['titleBar--dragging'], classes?.titleBar);
767
888
  // Window style
768
889
  const windowStyle = {};
769
890
  if (width !== 'auto') {
@@ -772,13 +893,19 @@ const Window = React.forwardRef(({ children, title, titleBar, active = true, wid
772
893
  if (height !== 'auto') {
773
894
  windowStyle.height = typeof height === 'number' ? `${height}px` : height;
774
895
  }
896
+ // Apply position if draggable and has been dragged
897
+ if (draggable && hasBeenDragged && currentPosition) {
898
+ windowStyle.position = 'absolute';
899
+ windowStyle.left = `${currentPosition.x}px`;
900
+ windowStyle.top = `${currentPosition.y}px`;
901
+ }
775
902
  // Render title bar if title provided and no custom titleBar
776
903
  const renderTitleBar = () => {
777
904
  if (titleBar) {
778
905
  return titleBar;
779
906
  }
780
907
  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" })] })] })] }));
908
+ 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
909
  }
783
910
  return null;
784
911
  };
@@ -915,7 +1042,7 @@ const Dialog = React.forwardRef(({ open = false, onClose, closeOnBackdropClick =
915
1042
  });
916
1043
  Dialog.displayName = 'Dialog';
917
1044
 
918
- var styles$4 = {"menuBar":"MenuBar-module_menuBar","leftContent":"MenuBar-module_leftContent","menusContainer":"MenuBar-module_menusContainer","menuContainer":"MenuBar-module_menuContainer","rightContent":"MenuBar-module_rightContent","menuButton":"MenuBar-module_menuButton","menuButton--disabled":"MenuBar-module_menuButton--disabled","menuButton--open":"MenuBar-module_menuButton--open","dropdown":"MenuBar-module_dropdown"};
1045
+ var styles$4 = {"menuBar":"MenuBar-module_menuBar","leftContent":"MenuBar-module_leftContent","menusContainer":"MenuBar-module_menusContainer","menuContainer":"MenuBar-module_menuContainer","rightContent":"MenuBar-module_rightContent","menuButton":"MenuBar-module_menuButton","menuButton--disabled":"MenuBar-module_menuButton--disabled","menuButton--open":"MenuBar-module_menuButton--open","dropdown":"MenuBar-module_dropdown","dropdown--right":"MenuBar-module_dropdown--right"};
919
1046
 
920
1047
  /**
921
1048
  * Mac OS 9 style MenuBar component
@@ -972,41 +1099,56 @@ var styles$4 = {"menuBar":"MenuBar-module_menuBar","leftContent":"MenuBar-module
972
1099
  const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClose, className = '', dropdownClassName = '', leftContent, rightContent, }, ref) => {
973
1100
  const [menuBarElement, setMenuBarElement] = React.useState(null);
974
1101
  const [focusedIndex, setFocusedIndex] = React.useState(-1);
1102
+ const [internalOpenIndex, setInternalOpenIndex] = React.useState(undefined);
1103
+ const isControlled = openMenuIndex !== undefined;
1104
+ const activeOpenIndex = isControlled ? openMenuIndex : internalOpenIndex;
1105
+ const handleMenuOpenInternal = (index) => {
1106
+ if (!isControlled) {
1107
+ setInternalOpenIndex(index);
1108
+ }
1109
+ onMenuOpen?.(index);
1110
+ };
1111
+ const handleMenuCloseInternal = () => {
1112
+ if (!isControlled) {
1113
+ setInternalOpenIndex(undefined);
1114
+ }
1115
+ onMenuClose?.();
1116
+ };
975
1117
  // Handle click outside to close menu
976
1118
  React.useEffect(() => {
977
- if (openMenuIndex === undefined || !menuBarElement)
1119
+ if (activeOpenIndex === undefined || !menuBarElement)
978
1120
  return;
979
1121
  const handleClickOutside = (event) => {
980
1122
  if (menuBarElement && !menuBarElement.contains(event.target)) {
981
- onMenuClose?.();
1123
+ handleMenuCloseInternal();
982
1124
  }
983
1125
  };
984
1126
  document.addEventListener('mousedown', handleClickOutside);
985
1127
  return () => document.removeEventListener('mousedown', handleClickOutside);
986
- }, [openMenuIndex, onMenuClose, menuBarElement]);
1128
+ }, [activeOpenIndex, onMenuClose, menuBarElement, isControlled]);
987
1129
  // Handle Escape key to close menu
988
1130
  React.useEffect(() => {
989
- if (openMenuIndex === undefined)
1131
+ if (activeOpenIndex === undefined)
990
1132
  return;
991
1133
  const handleEscape = (event) => {
992
1134
  if (event.key === 'Escape') {
993
1135
  event.preventDefault();
994
- onMenuClose?.();
1136
+ handleMenuCloseInternal();
995
1137
  }
996
1138
  };
997
1139
  document.addEventListener('keydown', handleEscape);
998
1140
  return () => document.removeEventListener('keydown', handleEscape);
999
- }, [openMenuIndex, onMenuClose]);
1141
+ }, [activeOpenIndex, onMenuClose, isControlled]);
1000
1142
  // Handle keyboard navigation
1001
1143
  const handleKeyDown = React.useCallback((event) => {
1002
1144
  switch (event.key) {
1003
1145
  case 'ArrowLeft':
1004
1146
  event.preventDefault();
1005
- if (openMenuIndex !== undefined) {
1147
+ if (activeOpenIndex !== undefined) {
1006
1148
  // Move to previous menu
1007
- const prevIndex = openMenuIndex > 0 ? openMenuIndex - 1 : menus.length - 1;
1149
+ const prevIndex = activeOpenIndex > 0 ? activeOpenIndex - 1 : menus.length - 1;
1008
1150
  if (!menus[prevIndex]?.disabled) {
1009
- onMenuOpen?.(prevIndex);
1151
+ handleMenuOpenInternal(prevIndex);
1010
1152
  }
1011
1153
  }
1012
1154
  else if (focusedIndex > 0) {
@@ -1015,11 +1157,11 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1015
1157
  break;
1016
1158
  case 'ArrowRight':
1017
1159
  event.preventDefault();
1018
- if (openMenuIndex !== undefined) {
1160
+ if (activeOpenIndex !== undefined) {
1019
1161
  // Move to next menu
1020
- const nextIndex = openMenuIndex < menus.length - 1 ? openMenuIndex + 1 : 0;
1162
+ const nextIndex = activeOpenIndex < menus.length - 1 ? activeOpenIndex + 1 : 0;
1021
1163
  if (!menus[nextIndex]?.disabled) {
1022
- onMenuOpen?.(nextIndex);
1164
+ handleMenuOpenInternal(nextIndex);
1023
1165
  }
1024
1166
  }
1025
1167
  else if (focusedIndex < menus.length - 1) {
@@ -1028,18 +1170,18 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1028
1170
  break;
1029
1171
  case 'ArrowDown':
1030
1172
  event.preventDefault();
1031
- if (openMenuIndex === undefined && focusedIndex >= 0) {
1173
+ if (activeOpenIndex === undefined && focusedIndex >= 0) {
1032
1174
  // Open the focused menu (only if it's a dropdown)
1033
1175
  const menu = menus[focusedIndex];
1034
1176
  if (!menu?.disabled && menu?.type !== 'link') {
1035
- onMenuOpen?.(focusedIndex);
1177
+ handleMenuOpenInternal(focusedIndex);
1036
1178
  }
1037
1179
  }
1038
1180
  break;
1039
1181
  case 'Enter':
1040
1182
  case ' ':
1041
1183
  event.preventDefault();
1042
- if (openMenuIndex === undefined && focusedIndex >= 0) {
1184
+ if (activeOpenIndex === undefined && focusedIndex >= 0) {
1043
1185
  const menu = menus[focusedIndex];
1044
1186
  if (!menu?.disabled) {
1045
1187
  if (menu.type === 'link') {
@@ -1048,13 +1190,13 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1048
1190
  }
1049
1191
  else {
1050
1192
  // Open the focused dropdown menu
1051
- onMenuOpen?.(focusedIndex);
1193
+ handleMenuOpenInternal(focusedIndex);
1052
1194
  }
1053
1195
  }
1054
1196
  }
1055
1197
  break;
1056
1198
  }
1057
- }, [openMenuIndex, focusedIndex, menus, onMenuOpen, onMenuClose]);
1199
+ }, [activeOpenIndex, focusedIndex, menus, onMenuOpen, onMenuClose, isControlled]);
1058
1200
  // Handle menu button click
1059
1201
  const handleMenuClick = (index) => {
1060
1202
  const menu = menus[index];
@@ -1065,13 +1207,13 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1065
1207
  menu.onClick?.();
1066
1208
  return;
1067
1209
  }
1068
- if (openMenuIndex === index) {
1210
+ if (activeOpenIndex === index) {
1069
1211
  // Clicking the same menu closes it
1070
- onMenuClose?.();
1212
+ handleMenuCloseInternal();
1071
1213
  }
1072
1214
  else {
1073
1215
  // Open the clicked menu
1074
- onMenuOpen?.(index);
1216
+ handleMenuOpenInternal(index);
1075
1217
  }
1076
1218
  };
1077
1219
  // Class names
@@ -1088,7 +1230,7 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1088
1230
  }
1089
1231
  }, [ref]);
1090
1232
  return (jsxRuntime.jsxs("div", { ref: handleRef, className: menuBarClassNames, role: "menubar", onKeyDown: handleKeyDown, children: [leftContent && (jsxRuntime.jsx("div", { className: styles$4.leftContent, children: leftContent })), jsxRuntime.jsx("div", { className: styles$4.menusContainer, children: menus.map((menu, index) => {
1091
- const isOpen = openMenuIndex === index;
1233
+ const isOpen = activeOpenIndex === index;
1092
1234
  const isDropdown = menu.type !== 'link';
1093
1235
  const menuButtonClassNames = [
1094
1236
  styles$4.menuButton,
@@ -1107,14 +1249,14 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1107
1249
  }, onFocus: () => setFocusedIndex(index), onBlur: () => setFocusedIndex(-1), "aria-disabled": menu.disabled, children: jsxRuntime.jsx("h3", { children: menu.label }) }) }, index));
1108
1250
  }
1109
1251
  // Standard dropdown menu or link without href
1110
- return (jsxRuntime.jsxs("div", { className: styles$4.menuContainer, children: [jsxRuntime.jsx("button", { type: "button", className: menuButtonClassNames, onClick: () => handleMenuClick(index), onFocus: () => setFocusedIndex(index), onBlur: () => setFocusedIndex(-1), disabled: menu.disabled, "aria-haspopup": isDropdown ? 'true' : undefined, "aria-expanded": isOpen, "aria-disabled": menu.disabled, children: menu.label }), isOpen && isDropdown && menu.items && (jsxRuntime.jsx("div", { className: dropdownClassNames, role: "menu", children: menu.items }))] }, index));
1252
+ return (jsxRuntime.jsxs("div", { className: styles$4.menuContainer, children: [jsxRuntime.jsx("button", { type: "button", className: menuButtonClassNames, onClick: () => handleMenuClick(index), onFocus: () => setFocusedIndex(index), onBlur: () => setFocusedIndex(-1), disabled: menu.disabled, "aria-haspopup": isDropdown ? 'true' : undefined, "aria-expanded": isOpen, "aria-disabled": menu.disabled, children: jsxRuntime.jsx("h3", { children: menu.label }) }), isOpen && isDropdown && menu.items && (jsxRuntime.jsx("div", { className: dropdownClassNames, role: "menu", children: menu.items }))] }, index));
1111
1253
  }) }), rightContent && (jsxRuntime.jsx("div", { className: styles$4.rightContent, children: Array.isArray(rightContent)
1112
1254
  ? rightContent.map((item, index) => (jsxRuntime.jsx(React.Fragment, { children: item }, index)))
1113
1255
  : rightContent }))] }));
1114
1256
  });
1115
1257
  MenuBar.displayName = 'MenuBar';
1116
1258
 
1117
- var styles$3 = {"menuItem":"MenuItem-module_menuItem","menuItem--disabled":"MenuItem-module_menuItem--disabled","menuItem--selected":"MenuItem-module_menuItem--selected","menuItem--separator":"MenuItem-module_menuItem--separator","checkmark":"MenuItem-module_checkmark","icon":"MenuItem-module_icon","label":"MenuItem-module_label","shortcut":"MenuItem-module_shortcut","submenuArrow":"MenuItem-module_submenuArrow","separatorLine":"MenuItem-module_separatorLine"};
1259
+ var styles$3 = {"menuItem":"MenuItem-module_menuItem","menuItem--disabled":"MenuItem-module_menuItem--disabled","menuItem--selected":"MenuItem-module_menuItem--selected","menuItem--separator":"MenuItem-module_menuItem--separator","checkmark":"MenuItem-module_checkmark","icon":"MenuItem-module_icon","label":"MenuItem-module_label","shortcut":"MenuItem-module_shortcut","submenuArrow":"MenuItem-module_submenuArrow","submenu":"MenuItem-module_submenu","separatorLine":"MenuItem-module_separatorLine"};
1118
1260
 
1119
1261
  /**
1120
1262
  * Mac OS 9 style MenuItem component
@@ -1153,7 +1295,9 @@ var styles$3 = {"menuItem":"MenuItem-module_menuItem","menuItem--disabled":"Menu
1153
1295
  * <MenuItem label="Recent Files" hasSubmenu />
1154
1296
  * ```
1155
1297
  */
1156
- const MenuItem = React.forwardRef(({ label, shortcut, disabled = false, selected = false, separator = false, checked = false, icon, onClick, onFocus, onBlur, className = '', hasSubmenu = false, }, ref) => {
1298
+ const MenuItem = React.forwardRef(({ label, shortcut, disabled = false, selected = false, separator = false, checked = false, icon, onClick, onFocus, onBlur, className = '', hasSubmenu = false, items, }, ref) => {
1299
+ const [isSubmenuOpen, setIsSubmenuOpen] = React.useState(false);
1300
+ const effectiveHasSubmenu = hasSubmenu || !!items;
1157
1301
  // Class names
1158
1302
  const menuItemClassNames = [
1159
1303
  styles$3.menuItem,
@@ -1172,10 +1316,66 @@ const MenuItem = React.forwardRef(({ label, shortcut, disabled = false, selected
1172
1316
  }
1173
1317
  onClick?.(event);
1174
1318
  };
1175
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("button", { ref: ref, type: "button", className: menuItemClassNames, onClick: handleClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, role: "menuitem", "aria-disabled": disabled, "aria-checked": checked ? 'true' : undefined, children: [jsxRuntime.jsx("span", { className: styles$3.checkmark, children: checked && '✓' }), icon && jsxRuntime.jsx("span", { className: styles$3.icon, children: icon }), jsxRuntime.jsx("span", { className: styles$3.label, children: label }), shortcut && jsxRuntime.jsx("span", { className: styles$3.shortcut, children: shortcut }), hasSubmenu && jsxRuntime.jsx("span", { className: styles$3.submenuArrow, children: "\u25B6" })] }), separator && jsxRuntime.jsx("div", { className: styles$3.separatorLine, role: "separator" })] }));
1319
+ return (jsxRuntime.jsxs("div", { className: styles$3.menuItemContainer, onMouseEnter: () => setIsSubmenuOpen(true), onMouseLeave: () => setIsSubmenuOpen(false), style: { position: 'relative', width: '100%' }, children: [jsxRuntime.jsxs("button", { ref: ref, type: "button", className: menuItemClassNames, onClick: handleClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, role: "menuitem", "aria-disabled": disabled, "aria-checked": checked ? 'true' : undefined, children: [jsxRuntime.jsx("span", { className: styles$3.checkmark, children: checked && '✓' }), icon && jsxRuntime.jsx("span", { className: styles$3.icon, children: icon }), jsxRuntime.jsx("span", { className: styles$3.label, children: label }), shortcut && jsxRuntime.jsx("span", { className: styles$3.shortcut, children: shortcut }), effectiveHasSubmenu && jsxRuntime.jsx("span", { className: styles$3.submenuArrow, children: "\u25B6" })] }), items && isSubmenuOpen && (jsxRuntime.jsx("div", { className: styles$3.submenu, role: "menu", children: items })), separator && jsxRuntime.jsx("div", { className: styles$3.separatorLine, role: "separator" })] }));
1176
1320
  });
1177
1321
  MenuItem.displayName = 'MenuItem';
1178
1322
 
1323
+ /**
1324
+ * Mac OS 9 style MenuDropdown component
1325
+ *
1326
+ * A standalone dropdown menu that shares the styling of the MenuBar.
1327
+ * Useful for placing menus in the status area (rightContent) or other parts of the app.
1328
+ */
1329
+ const MenuDropdown = ({ label, items, disabled = false, className = '', dropdownClassName = '', align = 'left', }) => {
1330
+ const [isOpen, setIsOpen] = React.useState(false);
1331
+ const containerRef = React.useRef(null);
1332
+ // Handle click outside to close menu
1333
+ React.useEffect(() => {
1334
+ if (!isOpen)
1335
+ return;
1336
+ const handleClickOutside = (event) => {
1337
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
1338
+ setIsOpen(false);
1339
+ }
1340
+ };
1341
+ document.addEventListener('mousedown', handleClickOutside);
1342
+ return () => document.removeEventListener('mousedown', handleClickOutside);
1343
+ }, [isOpen]);
1344
+ // Handle Escape key to close menu
1345
+ React.useEffect(() => {
1346
+ if (!isOpen)
1347
+ return;
1348
+ const handleEscape = (event) => {
1349
+ if (event.key === 'Escape') {
1350
+ event.preventDefault();
1351
+ setIsOpen(false);
1352
+ }
1353
+ };
1354
+ document.addEventListener('keydown', handleEscape);
1355
+ return () => document.removeEventListener('keydown', handleEscape);
1356
+ }, [isOpen]);
1357
+ const handleToggle = () => {
1358
+ if (!disabled) {
1359
+ setIsOpen(!isOpen);
1360
+ }
1361
+ };
1362
+ const menuContainerClassNames = [
1363
+ styles$4.menuContainer,
1364
+ className
1365
+ ].filter(Boolean).join(' ');
1366
+ const menuButtonClassNames = [
1367
+ styles$4.menuButton,
1368
+ isOpen ? styles$4['menuButton--open'] : '',
1369
+ disabled ? styles$4['menuButton--disabled'] : '',
1370
+ ].filter(Boolean).join(' ');
1371
+ const dropdownClassNames = [
1372
+ styles$4.dropdown,
1373
+ align === 'right' ? styles$4['dropdown--right'] : '',
1374
+ dropdownClassName
1375
+ ].filter(Boolean).join(' ');
1376
+ return (jsxRuntime.jsxs("div", { ref: containerRef, className: menuContainerClassNames, children: [jsxRuntime.jsx("button", { type: "button", className: menuButtonClassNames, onClick: handleToggle, disabled: disabled, "aria-haspopup": "true", "aria-expanded": isOpen, "aria-disabled": disabled, children: typeof label === 'string' ? jsxRuntime.jsx("h3", { children: label }) : label }), isOpen && (jsxRuntime.jsx("div", { className: dropdownClassNames, role: "menu", onClick: () => setIsOpen(false), children: items }))] }));
1377
+ };
1378
+
1179
1379
  var styles$2 = {"scrollbar":"Scrollbar-module_scrollbar","scrollbar--vertical":"Scrollbar-module_scrollbar--vertical","scrollbar--horizontal":"Scrollbar-module_scrollbar--horizontal","scrollbar--disabled":"Scrollbar-module_scrollbar--disabled","arrow":"Scrollbar-module_arrow","arrowIcon":"Scrollbar-module_arrowIcon","arrow--start":"Scrollbar-module_arrow--start","arrow--end":"Scrollbar-module_arrow--end","track":"Scrollbar-module_track","thumb":"Scrollbar-module_thumb"};
1180
1380
 
1181
1381
  // Scrollbar component - Mac OS 9 style
@@ -1311,11 +1511,13 @@ var styles$1 = {"listView":"ListView-module_listView","header":"ListView-module_
1311
1511
  * />
1312
1512
  * ```
1313
1513
  */
1314
- const ListView = React.forwardRef(({ columns, items, selectedIds = [], onSelectionChange, onItemOpen, onItemMouseEnter, onSort, className = '', height = 'auto', }, ref) => {
1514
+ const ListView = React.forwardRef(({ columns, items, selectedIds = [], onSelectionChange, onItemOpen, onItemMouseEnter, onItemMouseLeave, onSort, className = '', height = 'auto', classes, renderRow, renderCell, renderHeaderCell, onCellClick, onCellMouseEnter, onCellMouseLeave, }, ref) => {
1315
1515
  const [sortColumn, setSortColumn] = React.useState(null);
1316
1516
  const [sortDirection, setSortDirection] = React.useState('asc');
1517
+ const [hoveredRow, setHoveredRow] = React.useState(null);
1518
+ const [hoveredCell, setHoveredCell] = React.useState(null);
1317
1519
  // Class names
1318
- const classNames = [styles$1.listView, className].filter(Boolean).join(' ');
1520
+ const classNames = mergeClasses(styles$1.listView, className, classes?.root);
1319
1521
  // Handle column header click
1320
1522
  const handleColumnClick = React.useCallback((columnKey, sortable = true) => {
1321
1523
  if (!sortable || !onSort)
@@ -1360,7 +1562,7 @@ const ListView = React.forwardRef(({ columns, items, selectedIds = [], onSelecti
1360
1562
  }
1361
1563
  }, [onItemOpen]);
1362
1564
  // Handle row mouse enter
1363
- const handleRowMouseEnter = React.useCallback((item) => {
1565
+ React.useCallback((item) => {
1364
1566
  if (onItemMouseEnter) {
1365
1567
  onItemMouseEnter(item);
1366
1568
  }
@@ -1370,17 +1572,108 @@ const ListView = React.forwardRef(({ columns, items, selectedIds = [], onSelecti
1370
1572
  if (height !== 'auto') {
1371
1573
  containerStyle.height = typeof height === 'number' ? `${height}px` : height;
1372
1574
  }
1373
- 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: {
1374
- width: typeof column.width === 'number'
1375
- ? `${column.width}px`
1376
- : column.width,
1377
- }, 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) => {
1575
+ 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) => {
1576
+ const isSorted = sortColumn === column.key;
1577
+ const headerState = {
1578
+ isSorted,
1579
+ sortDirection: isSorted ? sortDirection : undefined,
1580
+ };
1581
+ const headerDefaultProps = {
1582
+ key: column.key,
1583
+ className: mergeClasses(styles$1.headerCell, column.sortable !== false && styles$1.sortable, classes?.headerCell),
1584
+ style: {
1585
+ width: typeof column.width === 'number'
1586
+ ? `${column.width}px`
1587
+ : column.width,
1588
+ },
1589
+ onClick: () => handleColumnClick(column.key, column.sortable),
1590
+ 'data-column': column.key,
1591
+ 'data-sortable': column.sortable !== false,
1592
+ ...(isSorted && {
1593
+ 'data-sorted': true,
1594
+ 'data-sort-direction': sortDirection,
1595
+ }),
1596
+ };
1597
+ // Use custom render or default
1598
+ if (renderHeaderCell) {
1599
+ return renderHeaderCell(column, headerState, headerDefaultProps);
1600
+ }
1601
+ // Default header cell rendering
1602
+ return (jsxRuntime.jsxs("div", { ...headerDefaultProps, children: [column.label, isSorted && (jsxRuntime.jsx("span", { className: styles$1.sortIndicator, children: sortDirection === 'asc' ? '▲' : '▼' }))] }));
1603
+ }) }), jsxRuntime.jsx("div", { className: mergeClasses(styles$1.body, classes?.body), children: items.map((item, rowIndex) => {
1378
1604
  const isSelected = selectedIds.includes(item.id);
1379
- 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: {
1380
- width: typeof column.width === 'number'
1381
- ? `${column.width}px`
1382
- : column.width,
1383
- }, children: [index === 0 && item.icon && (jsxRuntime.jsx("span", { className: styles$1.icon, children: item.icon })), item[column.key]] }, column.key))) }, item.id));
1605
+ const isHovered = hoveredRow === item.id;
1606
+ const rowState = {
1607
+ isSelected,
1608
+ isHovered,
1609
+ index: rowIndex,
1610
+ };
1611
+ const rowDefaultProps = {
1612
+ key: item.id,
1613
+ className: mergeClasses(styles$1.row, isSelected && styles$1.selected, classes?.row),
1614
+ onClick: (e) => handleRowClick(item.id, e),
1615
+ onDoubleClick: () => handleRowDoubleClick(item),
1616
+ onMouseEnter: () => {
1617
+ setHoveredRow(item.id);
1618
+ onItemMouseEnter?.(item);
1619
+ },
1620
+ onMouseLeave: () => {
1621
+ setHoveredRow(null);
1622
+ setHoveredCell(null);
1623
+ onItemMouseLeave?.(item);
1624
+ },
1625
+ 'data-selected': isSelected,
1626
+ 'data-index': rowIndex,
1627
+ 'data-item-id': item.id,
1628
+ };
1629
+ // Use custom row render or default
1630
+ if (renderRow) {
1631
+ return renderRow(item, rowState, rowDefaultProps);
1632
+ }
1633
+ // Default row rendering
1634
+ return (jsxRuntime.jsx("div", { ...rowDefaultProps, children: columns.map((column, columnIndex) => {
1635
+ const value = item[column.key];
1636
+ const isCellHovered = hoveredCell?.rowId === item.id &&
1637
+ hoveredCell?.columnKey === column.key;
1638
+ const cellState = {
1639
+ isHovered: isCellHovered,
1640
+ isRowSelected: isSelected,
1641
+ columnIndex,
1642
+ rowIndex,
1643
+ };
1644
+ // Cell event handlers
1645
+ const handleCellClick = (e) => {
1646
+ if (onCellClick) {
1647
+ onCellClick(item, column, e);
1648
+ }
1649
+ };
1650
+ const handleCellMouseEnter = () => {
1651
+ setHoveredCell({ rowId: item.id, columnKey: column.key });
1652
+ if (onCellMouseEnter) {
1653
+ onCellMouseEnter(item, column);
1654
+ }
1655
+ };
1656
+ const handleCellMouseLeave = () => {
1657
+ setHoveredCell(null);
1658
+ if (onCellMouseLeave) {
1659
+ onCellMouseLeave(item, column);
1660
+ }
1661
+ };
1662
+ // Use custom cell render or default
1663
+ if (renderCell) {
1664
+ return (jsxRuntime.jsx("div", { className: mergeClasses(styles$1.cell, classes?.cell), style: {
1665
+ width: typeof column.width === 'number'
1666
+ ? `${column.width}px`
1667
+ : column.width,
1668
+ }, "data-column": column.key, "data-hovered": isCellHovered, onClick: handleCellClick, onMouseEnter: handleCellMouseEnter, onMouseLeave: handleCellMouseLeave, children: renderCell(value, item, column, cellState) }, column.key));
1669
+ }
1670
+ // Default cell rendering
1671
+ return (jsxRuntime.jsxs("div", { className: mergeClasses(styles$1.cell, classes?.cell), style: {
1672
+ width: typeof column.width === 'number'
1673
+ ? `${column.width}px`
1674
+ : column.width,
1675
+ }, "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));
1676
+ }) }));
1384
1677
  }) })] }));
1385
1678
  });
1386
1679
  ListView.displayName = 'ListView';
@@ -1397,6 +1690,7 @@ var styles = {"folderListContent":"FolderList-module_folderListContent","listVie
1397
1690
  *
1398
1691
  * @example
1399
1692
  * ```tsx
1693
+ * // Basic folder list
1400
1694
  * <FolderList
1401
1695
  * title="My Documents"
1402
1696
  * items={[
@@ -1406,8 +1700,14 @@ var styles = {"folderListContent":"FolderList-module_folderListContent","listVie
1406
1700
  * selectedIds={['1']}
1407
1701
  * onSelectionChange={(ids) => console.log('Selected:', ids)}
1408
1702
  * onItemOpen={(item) => console.log('Open:', item.name)}
1409
- * onItemMouseEnter={(item) => console.log('Hovering:', item.name)}
1410
- * onMouseEnter={(e) => console.log('Mouse entered folder list')}
1703
+ * />
1704
+ *
1705
+ * // Draggable folder list
1706
+ * <FolderList
1707
+ * title="My Documents"
1708
+ * items={items}
1709
+ * draggable
1710
+ * defaultPosition={{ x: 100, y: 100 }}
1411
1711
  * />
1412
1712
  * ```
1413
1713
  */
@@ -1415,9 +1715,18 @@ const FolderList = React.forwardRef(({ columns = [
1415
1715
  { key: 'name', label: 'Name', width: '40%' },
1416
1716
  { key: 'modified', label: 'Date Modified', width: '30%' },
1417
1717
  { key: 'size', label: 'Size', width: '30%' },
1418
- ], items, selectedIds, onSelectionChange, onItemOpen, onItemMouseEnter, onSort, onMouseEnter, listHeight = 400, ...windowProps }, ref) => {
1718
+ ], items, selectedIds, onSelectionChange, onItemOpen, onItemMouseEnter, onItemMouseLeave, onSort, onMouseEnter, listHeight = 400, classes, renderRow, renderCell, renderHeaderCell, onCellClick, onCellMouseEnter, onCellMouseLeave, ...windowProps }, ref) => {
1719
+ // Build ListView classes from FolderList classes
1720
+ const listViewClasses = classes ? {
1721
+ root: classes.listView,
1722
+ header: classes.header,
1723
+ headerCell: classes.headerCell,
1724
+ body: classes.body,
1725
+ row: classes.row,
1726
+ cell: classes.cell,
1727
+ } : undefined;
1419
1728
  // Window content with ListView
1420
- 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 }) }));
1729
+ 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 }) }));
1421
1730
  });
1422
1731
  FolderList.displayName = 'FolderList';
1423
1732
 
@@ -1649,6 +1958,7 @@ exports.IconButton = IconButton;
1649
1958
  exports.IconLibrary = IconLibrary;
1650
1959
  exports.ListView = ListView;
1651
1960
  exports.MenuBar = MenuBar;
1961
+ exports.MenuDropdown = MenuDropdown;
1652
1962
  exports.MenuItem = MenuItem;
1653
1963
  exports.Radio = Radio;
1654
1964
  exports.Scrollbar = Scrollbar;
@@ -1659,6 +1969,8 @@ exports.TextField = TextField;
1659
1969
  exports.Window = Window;
1660
1970
  exports.borders = borders;
1661
1971
  exports.colors = colors;
1972
+ exports.createClassBuilder = createClassBuilder;
1973
+ exports.mergeClasses = mergeClasses;
1662
1974
  exports.shadows = shadows;
1663
1975
  exports.spacing = spacing;
1664
1976
  exports.tokens = tokens;