@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.
package/dist/index.js CHANGED
@@ -913,7 +913,7 @@ const Dialog = forwardRef(({ open = false, onClose, closeOnBackdropClick = true,
913
913
  });
914
914
  Dialog.displayName = 'Dialog';
915
915
 
916
- 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"};
916
+ 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"};
917
917
 
918
918
  /**
919
919
  * Mac OS 9 style MenuBar component
@@ -970,41 +970,56 @@ var styles$4 = {"menuBar":"MenuBar-module_menuBar","leftContent":"MenuBar-module
970
970
  const MenuBar = forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClose, className = '', dropdownClassName = '', leftContent, rightContent, }, ref) => {
971
971
  const [menuBarElement, setMenuBarElement] = useState(null);
972
972
  const [focusedIndex, setFocusedIndex] = useState(-1);
973
+ const [internalOpenIndex, setInternalOpenIndex] = useState(undefined);
974
+ const isControlled = openMenuIndex !== undefined;
975
+ const activeOpenIndex = isControlled ? openMenuIndex : internalOpenIndex;
976
+ const handleMenuOpenInternal = (index) => {
977
+ if (!isControlled) {
978
+ setInternalOpenIndex(index);
979
+ }
980
+ onMenuOpen?.(index);
981
+ };
982
+ const handleMenuCloseInternal = () => {
983
+ if (!isControlled) {
984
+ setInternalOpenIndex(undefined);
985
+ }
986
+ onMenuClose?.();
987
+ };
973
988
  // Handle click outside to close menu
974
989
  useEffect(() => {
975
- if (openMenuIndex === undefined || !menuBarElement)
990
+ if (activeOpenIndex === undefined || !menuBarElement)
976
991
  return;
977
992
  const handleClickOutside = (event) => {
978
993
  if (menuBarElement && !menuBarElement.contains(event.target)) {
979
- onMenuClose?.();
994
+ handleMenuCloseInternal();
980
995
  }
981
996
  };
982
997
  document.addEventListener('mousedown', handleClickOutside);
983
998
  return () => document.removeEventListener('mousedown', handleClickOutside);
984
- }, [openMenuIndex, onMenuClose, menuBarElement]);
999
+ }, [activeOpenIndex, onMenuClose, menuBarElement, isControlled]);
985
1000
  // Handle Escape key to close menu
986
1001
  useEffect(() => {
987
- if (openMenuIndex === undefined)
1002
+ if (activeOpenIndex === undefined)
988
1003
  return;
989
1004
  const handleEscape = (event) => {
990
1005
  if (event.key === 'Escape') {
991
1006
  event.preventDefault();
992
- onMenuClose?.();
1007
+ handleMenuCloseInternal();
993
1008
  }
994
1009
  };
995
1010
  document.addEventListener('keydown', handleEscape);
996
1011
  return () => document.removeEventListener('keydown', handleEscape);
997
- }, [openMenuIndex, onMenuClose]);
1012
+ }, [activeOpenIndex, onMenuClose, isControlled]);
998
1013
  // Handle keyboard navigation
999
1014
  const handleKeyDown = useCallback((event) => {
1000
1015
  switch (event.key) {
1001
1016
  case 'ArrowLeft':
1002
1017
  event.preventDefault();
1003
- if (openMenuIndex !== undefined) {
1018
+ if (activeOpenIndex !== undefined) {
1004
1019
  // Move to previous menu
1005
- const prevIndex = openMenuIndex > 0 ? openMenuIndex - 1 : menus.length - 1;
1020
+ const prevIndex = activeOpenIndex > 0 ? activeOpenIndex - 1 : menus.length - 1;
1006
1021
  if (!menus[prevIndex]?.disabled) {
1007
- onMenuOpen?.(prevIndex);
1022
+ handleMenuOpenInternal(prevIndex);
1008
1023
  }
1009
1024
  }
1010
1025
  else if (focusedIndex > 0) {
@@ -1013,11 +1028,11 @@ const MenuBar = forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClose, cla
1013
1028
  break;
1014
1029
  case 'ArrowRight':
1015
1030
  event.preventDefault();
1016
- if (openMenuIndex !== undefined) {
1031
+ if (activeOpenIndex !== undefined) {
1017
1032
  // Move to next menu
1018
- const nextIndex = openMenuIndex < menus.length - 1 ? openMenuIndex + 1 : 0;
1033
+ const nextIndex = activeOpenIndex < menus.length - 1 ? activeOpenIndex + 1 : 0;
1019
1034
  if (!menus[nextIndex]?.disabled) {
1020
- onMenuOpen?.(nextIndex);
1035
+ handleMenuOpenInternal(nextIndex);
1021
1036
  }
1022
1037
  }
1023
1038
  else if (focusedIndex < menus.length - 1) {
@@ -1026,18 +1041,18 @@ const MenuBar = forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClose, cla
1026
1041
  break;
1027
1042
  case 'ArrowDown':
1028
1043
  event.preventDefault();
1029
- if (openMenuIndex === undefined && focusedIndex >= 0) {
1044
+ if (activeOpenIndex === undefined && focusedIndex >= 0) {
1030
1045
  // Open the focused menu (only if it's a dropdown)
1031
1046
  const menu = menus[focusedIndex];
1032
1047
  if (!menu?.disabled && menu?.type !== 'link') {
1033
- onMenuOpen?.(focusedIndex);
1048
+ handleMenuOpenInternal(focusedIndex);
1034
1049
  }
1035
1050
  }
1036
1051
  break;
1037
1052
  case 'Enter':
1038
1053
  case ' ':
1039
1054
  event.preventDefault();
1040
- if (openMenuIndex === undefined && focusedIndex >= 0) {
1055
+ if (activeOpenIndex === undefined && focusedIndex >= 0) {
1041
1056
  const menu = menus[focusedIndex];
1042
1057
  if (!menu?.disabled) {
1043
1058
  if (menu.type === 'link') {
@@ -1046,13 +1061,13 @@ const MenuBar = forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClose, cla
1046
1061
  }
1047
1062
  else {
1048
1063
  // Open the focused dropdown menu
1049
- onMenuOpen?.(focusedIndex);
1064
+ handleMenuOpenInternal(focusedIndex);
1050
1065
  }
1051
1066
  }
1052
1067
  }
1053
1068
  break;
1054
1069
  }
1055
- }, [openMenuIndex, focusedIndex, menus, onMenuOpen, onMenuClose]);
1070
+ }, [activeOpenIndex, focusedIndex, menus, onMenuOpen, onMenuClose, isControlled]);
1056
1071
  // Handle menu button click
1057
1072
  const handleMenuClick = (index) => {
1058
1073
  const menu = menus[index];
@@ -1063,13 +1078,13 @@ const MenuBar = forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClose, cla
1063
1078
  menu.onClick?.();
1064
1079
  return;
1065
1080
  }
1066
- if (openMenuIndex === index) {
1081
+ if (activeOpenIndex === index) {
1067
1082
  // Clicking the same menu closes it
1068
- onMenuClose?.();
1083
+ handleMenuCloseInternal();
1069
1084
  }
1070
1085
  else {
1071
1086
  // Open the clicked menu
1072
- onMenuOpen?.(index);
1087
+ handleMenuOpenInternal(index);
1073
1088
  }
1074
1089
  };
1075
1090
  // Class names
@@ -1086,7 +1101,7 @@ const MenuBar = forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClose, cla
1086
1101
  }
1087
1102
  }, [ref]);
1088
1103
  return (jsxs("div", { ref: handleRef, className: menuBarClassNames, role: "menubar", onKeyDown: handleKeyDown, children: [leftContent && (jsx("div", { className: styles$4.leftContent, children: leftContent })), jsx("div", { className: styles$4.menusContainer, children: menus.map((menu, index) => {
1089
- const isOpen = openMenuIndex === index;
1104
+ const isOpen = activeOpenIndex === index;
1090
1105
  const isDropdown = menu.type !== 'link';
1091
1106
  const menuButtonClassNames = [
1092
1107
  styles$4.menuButton,
@@ -1105,14 +1120,14 @@ const MenuBar = forwardRef(({ menus, openMenuIndex, onMenuOpen, onMenuClose, cla
1105
1120
  }, onFocus: () => setFocusedIndex(index), onBlur: () => setFocusedIndex(-1), "aria-disabled": menu.disabled, children: jsx("h3", { children: menu.label }) }) }, index));
1106
1121
  }
1107
1122
  // Standard dropdown menu or link without href
1108
- return (jsxs("div", { className: styles$4.menuContainer, children: [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 && (jsx("div", { className: dropdownClassNames, role: "menu", children: menu.items }))] }, index));
1123
+ return (jsxs("div", { className: styles$4.menuContainer, children: [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: jsx("h3", { children: menu.label }) }), isOpen && isDropdown && menu.items && (jsx("div", { className: dropdownClassNames, role: "menu", children: menu.items }))] }, index));
1109
1124
  }) }), rightContent && (jsx("div", { className: styles$4.rightContent, children: Array.isArray(rightContent)
1110
1125
  ? rightContent.map((item, index) => (jsx(React.Fragment, { children: item }, index)))
1111
1126
  : rightContent }))] }));
1112
1127
  });
1113
1128
  MenuBar.displayName = 'MenuBar';
1114
1129
 
1115
- 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"};
1130
+ 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"};
1116
1131
 
1117
1132
  /**
1118
1133
  * Mac OS 9 style MenuItem component
@@ -1151,7 +1166,9 @@ var styles$3 = {"menuItem":"MenuItem-module_menuItem","menuItem--disabled":"Menu
1151
1166
  * <MenuItem label="Recent Files" hasSubmenu />
1152
1167
  * ```
1153
1168
  */
1154
- const MenuItem = forwardRef(({ label, shortcut, disabled = false, selected = false, separator = false, checked = false, icon, onClick, onFocus, onBlur, className = '', hasSubmenu = false, }, ref) => {
1169
+ const MenuItem = forwardRef(({ label, shortcut, disabled = false, selected = false, separator = false, checked = false, icon, onClick, onFocus, onBlur, className = '', hasSubmenu = false, items, }, ref) => {
1170
+ const [isSubmenuOpen, setIsSubmenuOpen] = useState(false);
1171
+ const effectiveHasSubmenu = hasSubmenu || !!items;
1155
1172
  // Class names
1156
1173
  const menuItemClassNames = [
1157
1174
  styles$3.menuItem,
@@ -1170,10 +1187,66 @@ const MenuItem = forwardRef(({ label, shortcut, disabled = false, selected = fal
1170
1187
  }
1171
1188
  onClick?.(event);
1172
1189
  };
1173
- return (jsxs(Fragment, { children: [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: [jsx("span", { className: styles$3.checkmark, children: checked && '✓' }), icon && jsx("span", { className: styles$3.icon, children: icon }), jsx("span", { className: styles$3.label, children: label }), shortcut && jsx("span", { className: styles$3.shortcut, children: shortcut }), hasSubmenu && jsx("span", { className: styles$3.submenuArrow, children: "\u25B6" })] }), separator && jsx("div", { className: styles$3.separatorLine, role: "separator" })] }));
1190
+ return (jsxs("div", { className: styles$3.menuItemContainer, onMouseEnter: () => setIsSubmenuOpen(true), onMouseLeave: () => setIsSubmenuOpen(false), style: { position: 'relative', width: '100%' }, children: [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: [jsx("span", { className: styles$3.checkmark, children: checked && '✓' }), icon && jsx("span", { className: styles$3.icon, children: icon }), jsx("span", { className: styles$3.label, children: label }), shortcut && jsx("span", { className: styles$3.shortcut, children: shortcut }), effectiveHasSubmenu && jsx("span", { className: styles$3.submenuArrow, children: "\u25B6" })] }), items && isSubmenuOpen && (jsx("div", { className: styles$3.submenu, role: "menu", children: items })), separator && jsx("div", { className: styles$3.separatorLine, role: "separator" })] }));
1174
1191
  });
1175
1192
  MenuItem.displayName = 'MenuItem';
1176
1193
 
1194
+ /**
1195
+ * Mac OS 9 style MenuDropdown component
1196
+ *
1197
+ * A standalone dropdown menu that shares the styling of the MenuBar.
1198
+ * Useful for placing menus in the status area (rightContent) or other parts of the app.
1199
+ */
1200
+ const MenuDropdown = ({ label, items, disabled = false, className = '', dropdownClassName = '', align = 'left', }) => {
1201
+ const [isOpen, setIsOpen] = useState(false);
1202
+ const containerRef = useRef(null);
1203
+ // Handle click outside to close menu
1204
+ useEffect(() => {
1205
+ if (!isOpen)
1206
+ return;
1207
+ const handleClickOutside = (event) => {
1208
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
1209
+ setIsOpen(false);
1210
+ }
1211
+ };
1212
+ document.addEventListener('mousedown', handleClickOutside);
1213
+ return () => document.removeEventListener('mousedown', handleClickOutside);
1214
+ }, [isOpen]);
1215
+ // Handle Escape key to close menu
1216
+ useEffect(() => {
1217
+ if (!isOpen)
1218
+ return;
1219
+ const handleEscape = (event) => {
1220
+ if (event.key === 'Escape') {
1221
+ event.preventDefault();
1222
+ setIsOpen(false);
1223
+ }
1224
+ };
1225
+ document.addEventListener('keydown', handleEscape);
1226
+ return () => document.removeEventListener('keydown', handleEscape);
1227
+ }, [isOpen]);
1228
+ const handleToggle = () => {
1229
+ if (!disabled) {
1230
+ setIsOpen(!isOpen);
1231
+ }
1232
+ };
1233
+ const menuContainerClassNames = [
1234
+ styles$4.menuContainer,
1235
+ className
1236
+ ].filter(Boolean).join(' ');
1237
+ const menuButtonClassNames = [
1238
+ styles$4.menuButton,
1239
+ isOpen ? styles$4['menuButton--open'] : '',
1240
+ disabled ? styles$4['menuButton--disabled'] : '',
1241
+ ].filter(Boolean).join(' ');
1242
+ const dropdownClassNames = [
1243
+ styles$4.dropdown,
1244
+ align === 'right' ? styles$4['dropdown--right'] : '',
1245
+ dropdownClassName
1246
+ ].filter(Boolean).join(' ');
1247
+ return (jsxs("div", { ref: containerRef, className: menuContainerClassNames, children: [jsx("button", { type: "button", className: menuButtonClassNames, onClick: handleToggle, disabled: disabled, "aria-haspopup": "true", "aria-expanded": isOpen, "aria-disabled": disabled, children: typeof label === 'string' ? jsx("h3", { children: label }) : label }), isOpen && (jsx("div", { className: dropdownClassNames, role: "menu", onClick: () => setIsOpen(false), children: items }))] }));
1248
+ };
1249
+
1177
1250
  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"};
1178
1251
 
1179
1252
  // Scrollbar component - Mac OS 9 style
@@ -1637,5 +1710,5 @@ const tokens = {
1637
1710
  transitions,
1638
1711
  };
1639
1712
 
1640
- export { Button, Checkbox, Dialog, DividerIcon, FolderList, Icon, IconButton, IconLibrary, ListView, MenuBar, MenuItem, Radio, Scrollbar, Select, TabPanel, Tabs, TextField, Window, borders, colors, shadows, spacing, tokens, transitions, typography, zIndex };
1713
+ export { Button, Checkbox, Dialog, DividerIcon, FolderList, Icon, IconButton, IconLibrary, ListView, MenuBar, MenuDropdown, MenuItem, Radio, Scrollbar, Select, TabPanel, Tabs, TextField, Window, borders, colors, shadows, spacing, tokens, transitions, typography, zIndex };
1641
1714
  //# sourceMappingURL=index.js.map