@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/fonts/65e59833.woff +0 -0
- package/dist/fonts/6fb9ec6b.woff +0 -0
- package/dist/fonts/9f492b6d.woff2 +0 -0
- package/dist/fonts/e45380e5.woff2 +0 -0
- package/dist/index.cjs +100 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +50 -9
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +42 -2
- package/dist/index.js +100 -27
- package/dist/index.js.map +1 -1
- package/dist/types/components/MenuBar/MenuDropdown.d.ts +36 -0
- package/dist/types/components/MenuBar/MenuItem.d.ts +4 -0
- package/dist/types/components/MenuBar/index.d.ts +1 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/fonts/2bc2468e.woff +0 -0
- package/dist/fonts/3306dfd5.woff +0 -0
- package/dist/fonts/b9e24cd1.woff2 +0 -0
- package/dist/fonts/cf596b2d.woff2 +0 -0
- package/dist/fonts/pixelOperator/PixelOperator-Bold.ttf +0 -0
- package/dist/fonts/pixelOperator/PixelOperator.ttf +0 -0
|
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 (
|
|
992
|
+
if (activeOpenIndex === undefined || !menuBarElement)
|
|
978
993
|
return;
|
|
979
994
|
const handleClickOutside = (event) => {
|
|
980
995
|
if (menuBarElement && !menuBarElement.contains(event.target)) {
|
|
981
|
-
|
|
996
|
+
handleMenuCloseInternal();
|
|
982
997
|
}
|
|
983
998
|
};
|
|
984
999
|
document.addEventListener('mousedown', handleClickOutside);
|
|
985
1000
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
986
|
-
}, [
|
|
1001
|
+
}, [activeOpenIndex, onMenuClose, menuBarElement, isControlled]);
|
|
987
1002
|
// Handle Escape key to close menu
|
|
988
1003
|
React.useEffect(() => {
|
|
989
|
-
if (
|
|
1004
|
+
if (activeOpenIndex === undefined)
|
|
990
1005
|
return;
|
|
991
1006
|
const handleEscape = (event) => {
|
|
992
1007
|
if (event.key === 'Escape') {
|
|
993
1008
|
event.preventDefault();
|
|
994
|
-
|
|
1009
|
+
handleMenuCloseInternal();
|
|
995
1010
|
}
|
|
996
1011
|
};
|
|
997
1012
|
document.addEventListener('keydown', handleEscape);
|
|
998
1013
|
return () => document.removeEventListener('keydown', handleEscape);
|
|
999
|
-
}, [
|
|
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 (
|
|
1020
|
+
if (activeOpenIndex !== undefined) {
|
|
1006
1021
|
// Move to previous menu
|
|
1007
|
-
const prevIndex =
|
|
1022
|
+
const prevIndex = activeOpenIndex > 0 ? activeOpenIndex - 1 : menus.length - 1;
|
|
1008
1023
|
if (!menus[prevIndex]?.disabled) {
|
|
1009
|
-
|
|
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 (
|
|
1033
|
+
if (activeOpenIndex !== undefined) {
|
|
1019
1034
|
// Move to next menu
|
|
1020
|
-
const nextIndex =
|
|
1035
|
+
const nextIndex = activeOpenIndex < menus.length - 1 ? activeOpenIndex + 1 : 0;
|
|
1021
1036
|
if (!menus[nextIndex]?.disabled) {
|
|
1022
|
-
|
|
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 (
|
|
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
|
-
|
|
1050
|
+
handleMenuOpenInternal(focusedIndex);
|
|
1036
1051
|
}
|
|
1037
1052
|
}
|
|
1038
1053
|
break;
|
|
1039
1054
|
case 'Enter':
|
|
1040
1055
|
case ' ':
|
|
1041
1056
|
event.preventDefault();
|
|
1042
|
-
if (
|
|
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
|
-
|
|
1066
|
+
handleMenuOpenInternal(focusedIndex);
|
|
1052
1067
|
}
|
|
1053
1068
|
}
|
|
1054
1069
|
}
|
|
1055
1070
|
break;
|
|
1056
1071
|
}
|
|
1057
|
-
}, [
|
|
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 (
|
|
1083
|
+
if (activeOpenIndex === index) {
|
|
1069
1084
|
// Clicking the same menu closes it
|
|
1070
|
-
|
|
1085
|
+
handleMenuCloseInternal();
|
|
1071
1086
|
}
|
|
1072
1087
|
else {
|
|
1073
1088
|
// Open the clicked menu
|
|
1074
|
-
|
|
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 =
|
|
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(
|
|
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;
|