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

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.
Binary file
Binary file
Binary file
Binary file
package/dist/index.cjs CHANGED
@@ -915,7 +915,7 @@ const Dialog = React.forwardRef(({ open = false, onClose, closeOnBackdropClick =
915
915
  });
916
916
  Dialog.displayName = 'Dialog';
917
917
 
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"};
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","dropdown--right":"MenuBar-module_dropdown--right"};
919
919
 
920
920
  /**
921
921
  * Mac OS 9 style MenuBar component
@@ -972,41 +972,56 @@ var styles$4 = {"menuBar":"MenuBar-module_menuBar","leftContent":"MenuBar-module
972
972
  const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClose, className = '', dropdownClassName = '', leftContent, rightContent, }, ref) => {
973
973
  const [menuBarElement, setMenuBarElement] = React.useState(null);
974
974
  const [focusedIndex, setFocusedIndex] = React.useState(-1);
975
+ const [internalOpenIndex, setInternalOpenIndex] = React.useState(undefined);
976
+ const isControlled = openMenuIndex !== undefined;
977
+ const activeOpenIndex = isControlled ? openMenuIndex : internalOpenIndex;
978
+ const handleMenuOpenInternal = (index) => {
979
+ if (!isControlled) {
980
+ setInternalOpenIndex(index);
981
+ }
982
+ onMenuOpen?.(index);
983
+ };
984
+ const handleMenuCloseInternal = () => {
985
+ if (!isControlled) {
986
+ setInternalOpenIndex(undefined);
987
+ }
988
+ onMenuClose?.();
989
+ };
975
990
  // Handle click outside to close menu
976
991
  React.useEffect(() => {
977
- if (openMenuIndex === undefined || !menuBarElement)
992
+ if (activeOpenIndex === undefined || !menuBarElement)
978
993
  return;
979
994
  const handleClickOutside = (event) => {
980
995
  if (menuBarElement && !menuBarElement.contains(event.target)) {
981
- onMenuClose?.();
996
+ handleMenuCloseInternal();
982
997
  }
983
998
  };
984
999
  document.addEventListener('mousedown', handleClickOutside);
985
1000
  return () => document.removeEventListener('mousedown', handleClickOutside);
986
- }, [openMenuIndex, onMenuClose, menuBarElement]);
1001
+ }, [activeOpenIndex, onMenuClose, menuBarElement, isControlled]);
987
1002
  // Handle Escape key to close menu
988
1003
  React.useEffect(() => {
989
- if (openMenuIndex === undefined)
1004
+ if (activeOpenIndex === undefined)
990
1005
  return;
991
1006
  const handleEscape = (event) => {
992
1007
  if (event.key === 'Escape') {
993
1008
  event.preventDefault();
994
- onMenuClose?.();
1009
+ handleMenuCloseInternal();
995
1010
  }
996
1011
  };
997
1012
  document.addEventListener('keydown', handleEscape);
998
1013
  return () => document.removeEventListener('keydown', handleEscape);
999
- }, [openMenuIndex, onMenuClose]);
1014
+ }, [activeOpenIndex, onMenuClose, isControlled]);
1000
1015
  // Handle keyboard navigation
1001
1016
  const handleKeyDown = React.useCallback((event) => {
1002
1017
  switch (event.key) {
1003
1018
  case 'ArrowLeft':
1004
1019
  event.preventDefault();
1005
- if (openMenuIndex !== undefined) {
1020
+ if (activeOpenIndex !== undefined) {
1006
1021
  // Move to previous menu
1007
- const prevIndex = openMenuIndex > 0 ? openMenuIndex - 1 : menus.length - 1;
1022
+ const prevIndex = activeOpenIndex > 0 ? activeOpenIndex - 1 : menus.length - 1;
1008
1023
  if (!menus[prevIndex]?.disabled) {
1009
- onMenuOpen?.(prevIndex);
1024
+ handleMenuOpenInternal(prevIndex);
1010
1025
  }
1011
1026
  }
1012
1027
  else if (focusedIndex > 0) {
@@ -1015,11 +1030,11 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1015
1030
  break;
1016
1031
  case 'ArrowRight':
1017
1032
  event.preventDefault();
1018
- if (openMenuIndex !== undefined) {
1033
+ if (activeOpenIndex !== undefined) {
1019
1034
  // Move to next menu
1020
- const nextIndex = openMenuIndex < menus.length - 1 ? openMenuIndex + 1 : 0;
1035
+ const nextIndex = activeOpenIndex < menus.length - 1 ? activeOpenIndex + 1 : 0;
1021
1036
  if (!menus[nextIndex]?.disabled) {
1022
- onMenuOpen?.(nextIndex);
1037
+ handleMenuOpenInternal(nextIndex);
1023
1038
  }
1024
1039
  }
1025
1040
  else if (focusedIndex < menus.length - 1) {
@@ -1028,18 +1043,18 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1028
1043
  break;
1029
1044
  case 'ArrowDown':
1030
1045
  event.preventDefault();
1031
- if (openMenuIndex === undefined && focusedIndex >= 0) {
1046
+ if (activeOpenIndex === undefined && focusedIndex >= 0) {
1032
1047
  // Open the focused menu (only if it's a dropdown)
1033
1048
  const menu = menus[focusedIndex];
1034
1049
  if (!menu?.disabled && menu?.type !== 'link') {
1035
- onMenuOpen?.(focusedIndex);
1050
+ handleMenuOpenInternal(focusedIndex);
1036
1051
  }
1037
1052
  }
1038
1053
  break;
1039
1054
  case 'Enter':
1040
1055
  case ' ':
1041
1056
  event.preventDefault();
1042
- if (openMenuIndex === undefined && focusedIndex >= 0) {
1057
+ if (activeOpenIndex === undefined && focusedIndex >= 0) {
1043
1058
  const menu = menus[focusedIndex];
1044
1059
  if (!menu?.disabled) {
1045
1060
  if (menu.type === 'link') {
@@ -1048,13 +1063,13 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1048
1063
  }
1049
1064
  else {
1050
1065
  // Open the focused dropdown menu
1051
- onMenuOpen?.(focusedIndex);
1066
+ handleMenuOpenInternal(focusedIndex);
1052
1067
  }
1053
1068
  }
1054
1069
  }
1055
1070
  break;
1056
1071
  }
1057
- }, [openMenuIndex, focusedIndex, menus, onMenuOpen, onMenuClose]);
1072
+ }, [activeOpenIndex, focusedIndex, menus, onMenuOpen, onMenuClose, isControlled]);
1058
1073
  // Handle menu button click
1059
1074
  const handleMenuClick = (index) => {
1060
1075
  const menu = menus[index];
@@ -1065,13 +1080,13 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1065
1080
  menu.onClick?.();
1066
1081
  return;
1067
1082
  }
1068
- if (openMenuIndex === index) {
1083
+ if (activeOpenIndex === index) {
1069
1084
  // Clicking the same menu closes it
1070
- onMenuClose?.();
1085
+ handleMenuCloseInternal();
1071
1086
  }
1072
1087
  else {
1073
1088
  // Open the clicked menu
1074
- onMenuOpen?.(index);
1089
+ handleMenuOpenInternal(index);
1075
1090
  }
1076
1091
  };
1077
1092
  // Class names
@@ -1088,7 +1103,7 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1088
1103
  }
1089
1104
  }, [ref]);
1090
1105
  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;
1106
+ const isOpen = activeOpenIndex === index;
1092
1107
  const isDropdown = menu.type !== 'link';
1093
1108
  const menuButtonClassNames = [
1094
1109
  styles$4.menuButton,
@@ -1107,14 +1122,14 @@ const MenuBar = React.forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClos
1107
1122
  }, onFocus: () => setFocusedIndex(index), onBlur: () => setFocusedIndex(-1), "aria-disabled": menu.disabled, children: jsxRuntime.jsx("h3", { children: menu.label }) }) }, index));
1108
1123
  }
1109
1124
  // 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));
1125
+ 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
1126
  }) }), rightContent && (jsxRuntime.jsx("div", { className: styles$4.rightContent, children: Array.isArray(rightContent)
1112
1127
  ? rightContent.map((item, index) => (jsxRuntime.jsx(React.Fragment, { children: item }, index)))
1113
1128
  : rightContent }))] }));
1114
1129
  });
1115
1130
  MenuBar.displayName = 'MenuBar';
1116
1131
 
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"};
1132
+ 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
1133
 
1119
1134
  /**
1120
1135
  * Mac OS 9 style MenuItem component
@@ -1153,7 +1168,9 @@ var styles$3 = {"menuItem":"MenuItem-module_menuItem","menuItem--disabled":"Menu
1153
1168
  * <MenuItem label="Recent Files" hasSubmenu />
1154
1169
  * ```
1155
1170
  */
1156
- const MenuItem = React.forwardRef(({ label, shortcut, disabled = false, selected = false, separator = false, checked = false, icon, onClick, onFocus, onBlur, className = '', hasSubmenu = false, }, ref) => {
1171
+ const MenuItem = React.forwardRef(({ label, shortcut, disabled = false, selected = false, separator = false, checked = false, icon, onClick, onFocus, onBlur, className = '', hasSubmenu = false, items, }, ref) => {
1172
+ const [isSubmenuOpen, setIsSubmenuOpen] = React.useState(false);
1173
+ const effectiveHasSubmenu = hasSubmenu || !!items;
1157
1174
  // Class names
1158
1175
  const menuItemClassNames = [
1159
1176
  styles$3.menuItem,
@@ -1172,10 +1189,66 @@ const MenuItem = React.forwardRef(({ label, shortcut, disabled = false, selected
1172
1189
  }
1173
1190
  onClick?.(event);
1174
1191
  };
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" })] }));
1192
+ 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
1193
  });
1177
1194
  MenuItem.displayName = 'MenuItem';
1178
1195
 
1196
+ /**
1197
+ * Mac OS 9 style MenuDropdown component
1198
+ *
1199
+ * A standalone dropdown menu that shares the styling of the MenuBar.
1200
+ * Useful for placing menus in the status area (rightContent) or other parts of the app.
1201
+ */
1202
+ const MenuDropdown = ({ label, items, disabled = false, className = '', dropdownClassName = '', align = 'left', }) => {
1203
+ const [isOpen, setIsOpen] = React.useState(false);
1204
+ const containerRef = React.useRef(null);
1205
+ // Handle click outside to close menu
1206
+ React.useEffect(() => {
1207
+ if (!isOpen)
1208
+ return;
1209
+ const handleClickOutside = (event) => {
1210
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
1211
+ setIsOpen(false);
1212
+ }
1213
+ };
1214
+ document.addEventListener('mousedown', handleClickOutside);
1215
+ return () => document.removeEventListener('mousedown', handleClickOutside);
1216
+ }, [isOpen]);
1217
+ // Handle Escape key to close menu
1218
+ React.useEffect(() => {
1219
+ if (!isOpen)
1220
+ return;
1221
+ const handleEscape = (event) => {
1222
+ if (event.key === 'Escape') {
1223
+ event.preventDefault();
1224
+ setIsOpen(false);
1225
+ }
1226
+ };
1227
+ document.addEventListener('keydown', handleEscape);
1228
+ return () => document.removeEventListener('keydown', handleEscape);
1229
+ }, [isOpen]);
1230
+ const handleToggle = () => {
1231
+ if (!disabled) {
1232
+ setIsOpen(!isOpen);
1233
+ }
1234
+ };
1235
+ const menuContainerClassNames = [
1236
+ styles$4.menuContainer,
1237
+ className
1238
+ ].filter(Boolean).join(' ');
1239
+ const menuButtonClassNames = [
1240
+ styles$4.menuButton,
1241
+ isOpen ? styles$4['menuButton--open'] : '',
1242
+ disabled ? styles$4['menuButton--disabled'] : '',
1243
+ ].filter(Boolean).join(' ');
1244
+ const dropdownClassNames = [
1245
+ styles$4.dropdown,
1246
+ align === 'right' ? styles$4['dropdown--right'] : '',
1247
+ dropdownClassName
1248
+ ].filter(Boolean).join(' ');
1249
+ 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 }))] }));
1250
+ };
1251
+
1179
1252
  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
1253
 
1181
1254
  // Scrollbar component - Mac OS 9 style
@@ -1649,6 +1722,7 @@ exports.IconButton = IconButton;
1649
1722
  exports.IconLibrary = IconLibrary;
1650
1723
  exports.ListView = ListView;
1651
1724
  exports.MenuBar = MenuBar;
1725
+ exports.MenuDropdown = MenuDropdown;
1652
1726
  exports.MenuItem = MenuItem;
1653
1727
  exports.Radio = Radio;
1654
1728
  exports.Scrollbar = Scrollbar;