react-os-shell 4.12.0 → 4.14.0
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/README.md +28 -0
- package/dist/{Browser-QKF75A7I.js → Browser-SFNEZ6KW.js} +6 -6
- package/dist/{Browser-QKF75A7I.js.map → Browser-SFNEZ6KW.js.map} +1 -1
- package/dist/{Calculator-U7JLNROS.js → Calculator-SJBZNZWR.js} +5 -5
- package/dist/{Calculator-U7JLNROS.js.map → Calculator-SJBZNZWR.js.map} +1 -1
- package/dist/ConfirmDialog-LZ2JQBHA.js +4 -0
- package/dist/{ConfirmDialog-Y4BYT77R.js.map → ConfirmDialog-LZ2JQBHA.js.map} +1 -1
- package/dist/{CurrencyConverter-2UFQ75WE.js → CurrencyConverter-5OUQSITK.js} +5 -5
- package/dist/{CurrencyConverter-2UFQ75WE.js.map → CurrencyConverter-5OUQSITK.js.map} +1 -1
- package/dist/{Documents-VBUYEZ22.js → Documents-IUV2A2R6.js} +6 -6
- package/dist/{Documents-VBUYEZ22.js.map → Documents-IUV2A2R6.js.map} +1 -1
- package/dist/{Files-4R5TDJSK.js → Files-RZSUSCOS.js} +8 -8
- package/dist/{Files-4R5TDJSK.js.map → Files-RZSUSCOS.js.map} +1 -1
- package/dist/{Notepad-B5ISRSK7.js → Notepad-DFPJKEBG.js} +6 -6
- package/dist/{Notepad-B5ISRSK7.js.map → Notepad-DFPJKEBG.js.map} +1 -1
- package/dist/{PomodoroTimer-Q53KOASQ.js → PomodoroTimer-5H3Q5SNS.js} +6 -6
- package/dist/{PomodoroTimer-Q53KOASQ.js.map → PomodoroTimer-5H3Q5SNS.js.map} +1 -1
- package/dist/{Preview-Z73Q6U7C.js → Preview-JTMOV35N.js} +6 -6
- package/dist/{Preview-Z73Q6U7C.js.map → Preview-JTMOV35N.js.map} +1 -1
- package/dist/{Sidebar-ITLRERXE.js → Sidebar-IMACKSHS.js} +10 -21
- package/dist/Sidebar-IMACKSHS.js.map +1 -0
- package/dist/{Spreadsheet-UPW6EQCY.js → Spreadsheet-2R43CIYR.js} +6 -6
- package/dist/{Spreadsheet-UPW6EQCY.js.map → Spreadsheet-2R43CIYR.js.map} +1 -1
- package/dist/{Stock-SRUFZGQC.js → Stock-FLWCMQZR.js} +5 -5
- package/dist/{Stock-SRUFZGQC.js.map → Stock-FLWCMQZR.js.map} +1 -1
- package/dist/{Weather-47E3IRKF.js → Weather-DKWGAHWC.js} +5 -5
- package/dist/{Weather-47E3IRKF.js.map → Weather-DKWGAHWC.js.map} +1 -1
- package/dist/{WorldClock-7L2YHNC3.js → WorldClock-5UXXPCBB.js} +5 -5
- package/dist/{WorldClock-7L2YHNC3.js.map → WorldClock-5UXXPCBB.js.map} +1 -1
- package/dist/apps/index.js +18 -18
- package/dist/{chunk-ZNGOTHEY.js → chunk-AFOTTLRS.js} +5 -5
- package/dist/{chunk-ZNGOTHEY.js.map → chunk-AFOTTLRS.js.map} +1 -1
- package/dist/{chunk-E2EGGV2Z.js → chunk-CVVS2OJO.js} +4 -4
- package/dist/{chunk-E2EGGV2Z.js.map → chunk-CVVS2OJO.js.map} +1 -1
- package/dist/{chunk-KILLK5UH.js → chunk-H4IEH6FK.js} +3 -3
- package/dist/{chunk-KILLK5UH.js.map → chunk-H4IEH6FK.js.map} +1 -1
- package/dist/{chunk-RVT2A2MO.js → chunk-IVLDAUYM.js} +11 -5
- package/dist/chunk-IVLDAUYM.js.map +1 -0
- package/dist/{chunk-HC6N2OXO.js → chunk-N2UW3CAS.js} +3 -3
- package/dist/{chunk-HC6N2OXO.js.map → chunk-N2UW3CAS.js.map} +1 -1
- package/dist/{chunk-JROEOUJD.js → chunk-UI43AZAK.js} +3 -3
- package/dist/{chunk-JROEOUJD.js.map → chunk-UI43AZAK.js.map} +1 -1
- package/dist/chunk-WDXP2Y4T.js +7 -0
- package/dist/{chunk-7Z2SUHZT.js.map → chunk-WDXP2Y4T.js.map} +1 -1
- package/dist/{chunk-XVHRGMS3.js → chunk-XJCAUC2Y.js} +3 -3
- package/dist/{chunk-XVHRGMS3.js.map → chunk-XJCAUC2Y.js.map} +1 -1
- package/dist/{chunk-H5NDQZ4A.js → chunk-ZPCGIR62.js} +3 -3
- package/dist/{chunk-H5NDQZ4A.js.map → chunk-ZPCGIR62.js.map} +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.js +194 -204
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/ConfirmDialog-Y4BYT77R.js +0 -4
- package/dist/Sidebar-ITLRERXE.js.map +0 -1
- package/dist/chunk-7Z2SUHZT.js +0 -7
- package/dist/chunk-RVT2A2MO.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -32,15 +32,23 @@ interface NavItem {
|
|
|
32
32
|
to: string;
|
|
33
33
|
label: string;
|
|
34
34
|
perms?: string[];
|
|
35
|
+
/** Permissions the user must hold EVERY one of, unlike `perms` which needs
|
|
36
|
+
* only one. For an entry whose page is gated on a narrower permission than
|
|
37
|
+
* the one that puts it in the menu: listing both in `perms` would widen the
|
|
38
|
+
* audience (it's an OR), so the extra one goes here instead. */
|
|
39
|
+
allPerms?: string[];
|
|
35
40
|
dividerAfter?: boolean;
|
|
36
|
-
/** Optional
|
|
37
|
-
*
|
|
41
|
+
/** Optional sub-items, to any depth — a child may have children of its own.
|
|
42
|
+
* Hovering the parent in <StartMenu> opens a nested flyout; in <Sidebar>
|
|
43
|
+
* the parent becomes an inline sub-accordion. */
|
|
38
44
|
children?: NavItem[];
|
|
39
45
|
}
|
|
40
46
|
interface NavSection {
|
|
41
47
|
label: string;
|
|
42
48
|
items: NavItem[];
|
|
43
49
|
perms?: string[];
|
|
50
|
+
/** Same all-of rule as `NavItem.allPerms`, for a whole section. */
|
|
51
|
+
allPerms?: string[];
|
|
44
52
|
/** Optional landing route for clicks on the section title itself
|
|
45
53
|
* (e.g. R&D's `/rd` dashboard). */
|
|
46
54
|
to?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { PREVIEW_OPENED_EVENT, openPreviewFile } from './chunk-DINNWYVB.js';
|
|
2
2
|
export { Breadcrumbs } from './chunk-DINNWYVB.js';
|
|
3
|
-
import { subscribePomo, getPomoSnapshot } from './chunk-
|
|
4
|
-
export { setShellTodoProvider } from './chunk-
|
|
3
|
+
import { subscribePomo, getPomoSnapshot } from './chunk-UI43AZAK.js';
|
|
4
|
+
export { setShellTodoProvider } from './chunk-UI43AZAK.js';
|
|
5
5
|
import { SidebarLayout } from './chunk-YG45DAUT.js';
|
|
6
6
|
export { SidebarLayout } from './chunk-YG45DAUT.js';
|
|
7
7
|
export { ImageAnnotator_default as ImageAnnotator } from './chunk-QU5L5ZYM.js';
|
|
8
8
|
import { toast_default } from './chunk-VENYVK3L.js';
|
|
9
9
|
export { toast_default as toast } from './chunk-VENYVK3L.js';
|
|
10
|
-
import { publishDesktopFolders, requestFilesTrashView, FolderGlyph, requestFilesDesktopFolderView, FileIconTile } from './chunk-
|
|
10
|
+
import { publishDesktopFolders, requestFilesTrashView, FolderGlyph, requestFilesDesktopFolderView, FileIconTile } from './chunk-ZPCGIR62.js';
|
|
11
11
|
import { setPdfPreview } from './chunk-BV6SQ46N.js';
|
|
12
12
|
import { playNotification, playStartup, soundsEnabled, getSoundConfig, SOUND_PACK_KEYS, SOUND_PACKS, SOUND_TYPES, SOUND_TYPE_LABELS, setSoundForType, previewSound, setAllSounds, playLogout } from './chunk-D7PYW2QS.js';
|
|
13
|
-
import { useAuth } from './chunk-
|
|
14
|
-
export { setShellAuthBridge } from './chunk-
|
|
13
|
+
import { useAuth } from './chunk-H4IEH6FK.js';
|
|
14
|
+
export { setShellAuthBridge } from './chunk-H4IEH6FK.js';
|
|
15
15
|
import { withSpareRow, EditableGrid } from './chunk-DC6BYGZ6.js';
|
|
16
16
|
export { EditableGrid } from './chunk-DC6BYGZ6.js';
|
|
17
17
|
import './chunk-4SQU5YV6.js';
|
|
18
|
-
import { APP_VERSION, VERSION } from './chunk-
|
|
19
|
-
export { VERSION } from './chunk-
|
|
20
|
-
import { registerModalEscapeInterceptor, useIsMobile, cancelWindowFront, requestWindowFront, useWindowManager, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, WINDOW_REGISTRY, isPageEntry, Modal, setPerfCollecting, drainPerfEvents, useShellPrefs, markMenuOpen, SIDEBAR_STRIP_W, forgetMaximizedWindowBoxes, ModalActions, useModalActive, client_default, LoadingSpinner, isShellApiClientConfigured, useUndo, CancelButton, setWindowPosition } from './chunk-
|
|
21
|
-
export { CancelButton, ConfirmProvider, CopyButton, DocFavStar, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ShellPrefsProvider, UndoProvider, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, beginWindowGesture, commitExposeHighlight, confirm, confirmDestructive, exitExposeMode, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, markMenuOpen, prompt, registerModalEscapeInterceptor, requestWindowFront, setExposeHighlight, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useIsActiveWindow, useLocalStoragePrefs, useModalActive, useShellPrefs, useUndo, useUndoable, useUndoableState, useWidgetSettings, useWindowDirty, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-
|
|
22
|
-
import { glassStyle, startMenuCategories, navSections, isSection, GLASS_INPUT_BG, navIcons, isReachable, sectionIcons, useShellAuth, visibleChildren } from './chunk-
|
|
23
|
-
export { GLASS_DIVIDER, GLASS_INPUT_BG, ShellAuthProvider, glassStyle, setShellNavIcons, useShellAuth } from './chunk-
|
|
18
|
+
import { APP_VERSION, VERSION } from './chunk-WDXP2Y4T.js';
|
|
19
|
+
export { VERSION } from './chunk-WDXP2Y4T.js';
|
|
20
|
+
import { registerModalEscapeInterceptor, useIsMobile, cancelWindowFront, requestWindowFront, useWindowManager, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, WINDOW_REGISTRY, isPageEntry, Modal, setPerfCollecting, drainPerfEvents, useShellPrefs, markMenuOpen, SIDEBAR_STRIP_W, forgetMaximizedWindowBoxes, ModalActions, useModalActive, client_default, LoadingSpinner, isShellApiClientConfigured, useUndo, CancelButton, setWindowPosition } from './chunk-CVVS2OJO.js';
|
|
21
|
+
export { CancelButton, ConfirmProvider, CopyButton, DocFavStar, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ShellPrefsProvider, UndoProvider, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, beginWindowGesture, commitExposeHighlight, confirm, confirmDestructive, exitExposeMode, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, markMenuOpen, prompt, registerModalEscapeInterceptor, requestWindowFront, setExposeHighlight, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useIsActiveWindow, useLocalStoragePrefs, useModalActive, useShellPrefs, useUndo, useUndoable, useUndoableState, useWidgetSettings, useWindowDirty, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-CVVS2OJO.js';
|
|
22
|
+
import { glassStyle, startMenuCategories, navVisible, navSections, isSection, GLASS_INPUT_BG, navIcons, isReachable, sectionIcons, useShellAuth, visibleChildren } from './chunk-IVLDAUYM.js';
|
|
23
|
+
export { GLASS_DIVIDER, GLASS_INPUT_BG, ShellAuthProvider, glassStyle, setShellNavIcons, useShellAuth } from './chunk-IVLDAUYM.js';
|
|
24
24
|
import { createContext, lazy, forwardRef, useCallback, useState, useRef, useId, useEffect, useLayoutEffect, useSyncExternalStore, useMemo, useContext, isValidElement, cloneElement, Suspense, Fragment as Fragment$1, Children } from 'react';
|
|
25
25
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
26
26
|
import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react';
|
|
@@ -3575,6 +3575,67 @@ function LogoutAnimation({ onComplete, subtitle }) {
|
|
|
3575
3575
|
}
|
|
3576
3576
|
);
|
|
3577
3577
|
}
|
|
3578
|
+
|
|
3579
|
+
// src/shell/menuPath.ts
|
|
3580
|
+
function openMenuLevel(path, depth, anchor) {
|
|
3581
|
+
if (path.length === depth + 1 && path[depth].key === anchor.key) return path;
|
|
3582
|
+
return [...path.slice(0, depth), anchor];
|
|
3583
|
+
}
|
|
3584
|
+
function closeMenuBelow(path, depth) {
|
|
3585
|
+
const keep = Math.max(0, depth);
|
|
3586
|
+
return path.length > keep ? path.slice(0, keep) : path;
|
|
3587
|
+
}
|
|
3588
|
+
function resolveMenuLevels(rootItems, path, childrenOf) {
|
|
3589
|
+
if (path.length === 0 || rootItems.length === 0) return [];
|
|
3590
|
+
const levels = [rootItems];
|
|
3591
|
+
for (let i = 1; i < path.length; i++) {
|
|
3592
|
+
const parent = levels[i - 1].find((item) => item.to === path[i].key);
|
|
3593
|
+
const kids = parent ? childrenOf(parent) : [];
|
|
3594
|
+
if (kids.length === 0) break;
|
|
3595
|
+
levels.push(kids);
|
|
3596
|
+
}
|
|
3597
|
+
return levels;
|
|
3598
|
+
}
|
|
3599
|
+
function clampMenuTop(anchorY, height, minTop, maxBottom) {
|
|
3600
|
+
const span = Math.max(0, maxBottom - minTop);
|
|
3601
|
+
const h = Math.min(height, span);
|
|
3602
|
+
const top = anchorY - h / 2;
|
|
3603
|
+
if (top < minTop) return minTop;
|
|
3604
|
+
if (top + h > maxBottom) return Math.max(minTop, maxBottom - h);
|
|
3605
|
+
return top;
|
|
3606
|
+
}
|
|
3607
|
+
function menuPanelLeft(ownerLeft, ownerRight, width, viewportW, gap, preferLeft = false, margin = 8) {
|
|
3608
|
+
const onRight = ownerRight + gap;
|
|
3609
|
+
const onLeft = ownerLeft - gap - width;
|
|
3610
|
+
const rightFits = onRight + width <= viewportW - margin;
|
|
3611
|
+
const leftFits = onLeft >= margin;
|
|
3612
|
+
if (preferLeft && leftFits) return { left: onLeft, flipped: true };
|
|
3613
|
+
if (rightFits) return { left: onRight, flipped: false };
|
|
3614
|
+
if (leftFits) return { left: onLeft, flipped: true };
|
|
3615
|
+
return preferLeft ? { left: margin, flipped: true } : { left: Math.max(margin, viewportW - margin - width), flipped: false };
|
|
3616
|
+
}
|
|
3617
|
+
var PANEL_GAP = 4;
|
|
3618
|
+
var CLOSE_DELAY = 200;
|
|
3619
|
+
function MenuPanel({ left, anchorY, estHeight, minTop, maxBottom, flipped, className, style, onMouseEnter, onMouseLeave, children }) {
|
|
3620
|
+
const ref = useRef(null);
|
|
3621
|
+
useLayoutEffect(() => {
|
|
3622
|
+
const el = ref.current;
|
|
3623
|
+
if (!el) return;
|
|
3624
|
+
el.style.top = `${clampMenuTop(anchorY, el.offsetHeight, minTop, maxBottom)}px`;
|
|
3625
|
+
});
|
|
3626
|
+
return /* @__PURE__ */ jsx(
|
|
3627
|
+
"div",
|
|
3628
|
+
{
|
|
3629
|
+
ref,
|
|
3630
|
+
"data-menu-panel": true,
|
|
3631
|
+
className: `fixed rounded-2xl overflow-hidden ${className ?? ""}`,
|
|
3632
|
+
style: { left, top: clampMenuTop(anchorY, estHeight, minTop, maxBottom), animation: `${flipped ? "submenu-in-left" : "submenu-in"} 0.1s ease-out`, ...style },
|
|
3633
|
+
onMouseEnter,
|
|
3634
|
+
onMouseLeave,
|
|
3635
|
+
children
|
|
3636
|
+
}
|
|
3637
|
+
);
|
|
3638
|
+
}
|
|
3578
3639
|
function StartMenu({
|
|
3579
3640
|
open,
|
|
3580
3641
|
onClose,
|
|
@@ -3600,55 +3661,28 @@ function StartMenu({
|
|
|
3600
3661
|
virtualSections.map((v) => [v.label, v])
|
|
3601
3662
|
);
|
|
3602
3663
|
const { hasAnyPerm } = useAuth();
|
|
3603
|
-
const footerItems = (categories.footerItems ?? []).filter((item) =>
|
|
3664
|
+
const footerItems = (categories.footerItems ?? []).filter((item) => navVisible(item, hasAnyPerm));
|
|
3604
3665
|
const isMobile = useIsMobile();
|
|
3605
|
-
const [
|
|
3606
|
-
const [hoveredY, setHoveredY] = useState(0);
|
|
3607
|
-
const [hoveredChild, setHoveredChild] = useState(null);
|
|
3608
|
-
const [hoveredChildY, setHoveredChildY] = useState(0);
|
|
3666
|
+
const [openPath, setOpenPath] = useState([]);
|
|
3609
3667
|
const [search, setSearch] = useState("");
|
|
3610
3668
|
const [searchIdx, setSearchIdx] = useState(0);
|
|
3611
3669
|
const menuRef = useRef(null);
|
|
3612
|
-
const
|
|
3613
|
-
const subFlyoutRef = useRef(null);
|
|
3614
|
-
const hoverTimeout = useRef();
|
|
3615
|
-
const childHoverTimeout = useRef();
|
|
3616
|
-
const [measuredFlyout, setMeasuredFlyout] = useState(null);
|
|
3617
|
-
const [measuredSub, setMeasuredSub] = useState(null);
|
|
3670
|
+
const closeTimer = useRef();
|
|
3618
3671
|
useEffect(() => {
|
|
3619
3672
|
if (!open) {
|
|
3620
3673
|
setSearch("");
|
|
3621
|
-
|
|
3622
|
-
setHoveredChild(null);
|
|
3674
|
+
setOpenPath([]);
|
|
3623
3675
|
setSearchIdx(0);
|
|
3624
3676
|
}
|
|
3625
3677
|
}, [open]);
|
|
3626
|
-
useEffect(() =>
|
|
3627
|
-
|
|
3628
|
-
}, [hoveredSection]);
|
|
3678
|
+
useEffect(() => () => clearTimeout(closeTimer.current), []);
|
|
3679
|
+
const deepestOpen = openPath.length > 0 ? openPath[openPath.length - 1].key : null;
|
|
3629
3680
|
useEffect(() => {
|
|
3630
3681
|
if (open) markMenuOpen("menu", "start");
|
|
3631
3682
|
}, [open]);
|
|
3632
3683
|
useEffect(() => {
|
|
3633
|
-
if (
|
|
3634
|
-
}, [
|
|
3635
|
-
useEffect(() => {
|
|
3636
|
-
if (hoveredChild) markMenuOpen("submenu", hoveredChild);
|
|
3637
|
-
}, [hoveredChild]);
|
|
3638
|
-
useLayoutEffect(() => {
|
|
3639
|
-
if (!flyoutRef.current || !hoveredSection || search.length >= 2) return;
|
|
3640
|
-
const h = flyoutRef.current.offsetHeight;
|
|
3641
|
-
if (measuredFlyout?.key !== hoveredSection || measuredFlyout.h !== h) {
|
|
3642
|
-
setMeasuredFlyout({ key: hoveredSection, h });
|
|
3643
|
-
}
|
|
3644
|
-
}, [hoveredSection, search, measuredFlyout]);
|
|
3645
|
-
useLayoutEffect(() => {
|
|
3646
|
-
if (!subFlyoutRef.current || !hoveredChild || search.length >= 2) return;
|
|
3647
|
-
const h = subFlyoutRef.current.offsetHeight;
|
|
3648
|
-
if (measuredSub?.key !== hoveredChild || measuredSub.h !== h) {
|
|
3649
|
-
setMeasuredSub({ key: hoveredChild, h });
|
|
3650
|
-
}
|
|
3651
|
-
}, [hoveredChild, search, measuredSub]);
|
|
3684
|
+
if (deepestOpen) markMenuOpen("submenu", deepestOpen);
|
|
3685
|
+
}, [deepestOpen]);
|
|
3652
3686
|
useEffect(() => {
|
|
3653
3687
|
if (!open) return;
|
|
3654
3688
|
const handler = (e) => {
|
|
@@ -3673,7 +3707,7 @@ function StartMenu({
|
|
|
3673
3707
|
if (isMobile) {
|
|
3674
3708
|
const allItems = [];
|
|
3675
3709
|
const pushItem = (it, sectionLabel) => {
|
|
3676
|
-
if (it
|
|
3710
|
+
if (!navVisible(it, hasAnyPerm)) return;
|
|
3677
3711
|
if (!isReachable(it, hasAnyPerm)) return;
|
|
3678
3712
|
allItems.push({ item: it, sectionLabel });
|
|
3679
3713
|
for (const c of visibleChildren(it, hasAnyPerm)) pushItem(c, it.label);
|
|
@@ -3681,7 +3715,7 @@ function StartMenu({
|
|
|
3681
3715
|
for (const entry of navSections2) {
|
|
3682
3716
|
if (isSection(entry)) {
|
|
3683
3717
|
const sec = entry;
|
|
3684
|
-
if (sec
|
|
3718
|
+
if (!navVisible(sec, hasAnyPerm)) continue;
|
|
3685
3719
|
for (const it of sec.items) pushItem(it, sec.label);
|
|
3686
3720
|
} else {
|
|
3687
3721
|
pushItem(entry);
|
|
@@ -3740,10 +3774,11 @@ function StartMenu({
|
|
|
3740
3774
|
const systemSections = navSections2.filter((item) => isSection(item) && systemLabels.has(item.label));
|
|
3741
3775
|
const footerSections = navSections2.filter((item) => isSection(item) && footerLabels.has(item.label));
|
|
3742
3776
|
const hasAppsGroup = topItems.length > 0 || systemSections.length > 0 || virtualSections.length > 0;
|
|
3743
|
-
const getVisibleItems = (section) => section.items.filter((item) =>
|
|
3777
|
+
const getVisibleItems = (section) => section.items.filter((item) => navVisible(item, hasAnyPerm));
|
|
3744
3778
|
const visibleChildren2 = (item) => visibleChildren(item, hasAnyPerm);
|
|
3779
|
+
const submenuItems = (item) => visibleChildren2(item).filter((child) => isReachable(child, hasAnyPerm));
|
|
3745
3780
|
const matchTree = (it, sectionLabel) => {
|
|
3746
|
-
if (it
|
|
3781
|
+
if (!navVisible(it, hasAnyPerm)) return [];
|
|
3747
3782
|
if (!isReachable(it, hasAnyPerm)) return [];
|
|
3748
3783
|
const kids = visibleChildren2(it);
|
|
3749
3784
|
const hits = [];
|
|
@@ -3774,78 +3809,69 @@ function StartMenu({
|
|
|
3774
3809
|
if (icon && isValidElement(icon)) return cloneElement(icon, { className: "h-4 w-4" });
|
|
3775
3810
|
return null;
|
|
3776
3811
|
};
|
|
3812
|
+
const hoveredSection = openPath.length > 0 ? openPath[0].key : null;
|
|
3777
3813
|
const hoveredVirtual = hoveredSection ? virtualByLabel[hoveredSection] : void 0;
|
|
3778
3814
|
const hoveredData = hoveredVirtual ? null : hoveredSection ? [...erpSections, ...systemSections, ...footerSections].find((s) => s.label === hoveredSection) : null;
|
|
3779
3815
|
const flyoutItems = (hoveredVirtual ? hoveredVirtual.items : hoveredData ? getVisibleItems(hoveredData) : []).filter((item) => isReachable(item, hasAnyPerm));
|
|
3780
3816
|
const menuDensity = typeof document !== "undefined" ? getComputedStyle(document.documentElement).getPropertyValue("--menu-density")?.trim() || "normal" : "normal";
|
|
3781
3817
|
const density = menuDensity === "tight" || menuDensity === "large" ? menuDensity : "normal";
|
|
3782
3818
|
const sizeConfigByDensity = {
|
|
3783
|
-
tight: { small: { w: "w-52", fw: "w-44", text: "text-xs", py: "py-1", px: "px-3", mw: 208, itemH: 24 }, medium: { w: "w-56", fw: "w-48", text: "text-xs", py: "py-1", px: "px-3", mw: 224, itemH: 26 }, large: { w: "w-64", fw: "w-52", text: "text-sm", py: "py-1.5", px: "px-3", mw: 256, itemH: 30 } },
|
|
3784
|
-
normal: { small: { w: "w-56", fw: "w-48", text: "text-xs", py: "py-1.5", px: "px-3", mw: 224, itemH: 30 }, medium: { w: "w-64", fw: "w-56", text: "text-sm", py: "py-1.5", px: "px-4", mw: 256, itemH: 32 }, large: { w: "w-72", fw: "w-60", text: "text-sm", py: "py-2", px: "px-4", mw: 288, itemH: 36 } },
|
|
3785
|
-
large: { small: { w: "w-56", fw: "w-48", text: "text-xs", py: "py-2", px: "px-3", mw: 224, itemH: 32 }, medium: { w: "w-64", fw: "w-56", text: "text-sm", py: "py-2", px: "px-4", mw: 256, itemH: 36 }, large: { w: "w-72", fw: "w-60", text: "text-sm", py: "py-2", px: "px-4", mw: 288, itemH: 36 } }
|
|
3819
|
+
tight: { small: { w: "w-52", fw: "w-44", fwPx: 176, text: "text-xs", py: "py-1", px: "px-3", mw: 208, itemH: 24 }, medium: { w: "w-56", fw: "w-48", fwPx: 192, text: "text-xs", py: "py-1", px: "px-3", mw: 224, itemH: 26 }, large: { w: "w-64", fw: "w-52", fwPx: 208, text: "text-sm", py: "py-1.5", px: "px-3", mw: 256, itemH: 30 } },
|
|
3820
|
+
normal: { small: { w: "w-56", fw: "w-48", fwPx: 192, text: "text-xs", py: "py-1.5", px: "px-3", mw: 224, itemH: 30 }, medium: { w: "w-64", fw: "w-56", fwPx: 224, text: "text-sm", py: "py-1.5", px: "px-4", mw: 256, itemH: 32 }, large: { w: "w-72", fw: "w-60", fwPx: 240, text: "text-sm", py: "py-2", px: "px-4", mw: 288, itemH: 36 } },
|
|
3821
|
+
large: { small: { w: "w-56", fw: "w-48", fwPx: 192, text: "text-xs", py: "py-2", px: "px-3", mw: 224, itemH: 32 }, medium: { w: "w-64", fw: "w-56", fwPx: 224, text: "text-sm", py: "py-2", px: "px-4", mw: 256, itemH: 36 }, large: { w: "w-72", fw: "w-60", fwPx: 240, text: "text-sm", py: "py-2", px: "px-4", mw: 288, itemH: 36 } }
|
|
3786
3822
|
};
|
|
3787
3823
|
const sizeConfig = sizeConfigByDensity[density][size];
|
|
3788
3824
|
const menuGlass = glassStyle();
|
|
3789
3825
|
const itemCls = `w-full flex items-center gap-2 rounded-lg ${sizeConfig.px} ${sizeConfig.py} ${sizeConfig.text}`;
|
|
3790
|
-
const flyoutEstH = flyoutItems.length * sizeConfig.itemH + 12;
|
|
3791
|
-
const flyoutH = measuredFlyout?.key === hoveredSection ? measuredFlyout.h : flyoutEstH;
|
|
3792
|
-
const menuWidth = sizeConfig.mw;
|
|
3793
3826
|
const viewportH = typeof window !== "undefined" ? window.innerHeight : 800;
|
|
3827
|
+
const viewportW = typeof window !== "undefined" ? window.innerWidth : 1280;
|
|
3794
3828
|
const minTop = (taskbarPosition === "top" ? taskbarH : 0) + 8;
|
|
3795
3829
|
const maxBottom = viewportH - (taskbarPosition === "bottom" ? taskbarH : 0) - 8;
|
|
3796
3830
|
const availH = Math.max(0, maxBottom - minTop);
|
|
3797
|
-
const
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
const
|
|
3804
|
-
|
|
3805
|
-
|
|
3831
|
+
const levels = resolveMenuLevels(flyoutItems, openPath, submenuItems);
|
|
3832
|
+
const cancelClose = () => clearTimeout(closeTimer.current);
|
|
3833
|
+
const nextPanelLeft = (depth, row) => {
|
|
3834
|
+
const owner = row.closest("[data-menu-panel]");
|
|
3835
|
+
if (!owner) return { left: 0, flipped: false };
|
|
3836
|
+
const ownerLeft = depth === 0 ? owner.getBoundingClientRect().left : openPath[depth - 1]?.left ?? 0;
|
|
3837
|
+
const ownerRight = depth === 0 ? owner.getBoundingClientRect().right : ownerLeft + owner.offsetWidth;
|
|
3838
|
+
const preferLeft = depth > 0 && (openPath[depth - 1]?.flipped ?? false);
|
|
3839
|
+
return menuPanelLeft(ownerLeft, ownerRight, sizeConfig.fwPx, viewportW, PANEL_GAP, preferLeft);
|
|
3840
|
+
};
|
|
3841
|
+
const openSubmenu = (depth, key, e) => {
|
|
3842
|
+
cancelClose();
|
|
3843
|
+
const row = e.currentTarget;
|
|
3844
|
+
const rect = row.getBoundingClientRect();
|
|
3845
|
+
const anchor = { key, y: rect.top + rect.height / 2, ...nextPanelLeft(depth, row) };
|
|
3846
|
+
setOpenPath((prev) => openMenuLevel(prev, depth, anchor));
|
|
3806
3847
|
};
|
|
3848
|
+
const scheduleClose = (depth) => {
|
|
3849
|
+
cancelClose();
|
|
3850
|
+
closeTimer.current = setTimeout(() => setOpenPath((prev) => closeMenuBelow(prev, depth)), CLOSE_DELAY);
|
|
3851
|
+
};
|
|
3852
|
+
const chevron = /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5 ml-auto text-gray-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) });
|
|
3807
3853
|
const renderSection = (section, isErp) => {
|
|
3808
|
-
if (section
|
|
3854
|
+
if (!navVisible(section, hasAnyPerm)) return null;
|
|
3809
3855
|
const items = getVisibleItems(section);
|
|
3810
3856
|
if (items.length === 0) return null;
|
|
3811
3857
|
const isHovered = hoveredSection === section.label;
|
|
3812
|
-
return /* @__PURE__ */ jsxs(
|
|
3813
|
-
"
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
/* @__PURE__ */ jsxs("button", { className: `${itemCls} transition-colors ${isHovered ? "bg-blue-50 text-blue-700" : "text-gray-700 hover:bg-blue-50 hover:text-blue-700"}`, children: [
|
|
3821
|
-
secIcon(section.label),
|
|
3822
|
-
/* @__PURE__ */ jsx("span", { className: isErp ? "font-medium" : "", children: section.label }),
|
|
3823
|
-
/* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5 ml-auto text-gray-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) })
|
|
3824
|
-
] }),
|
|
3825
|
-
section.dividerAfter && /* @__PURE__ */ jsx("div", { className: "border-t border-white/20 my-1.5 mx-2" })
|
|
3826
|
-
]
|
|
3827
|
-
},
|
|
3828
|
-
section.label
|
|
3829
|
-
);
|
|
3858
|
+
return /* @__PURE__ */ jsxs("div", { onMouseEnter: (e) => openSubmenu(0, section.label, e), children: [
|
|
3859
|
+
/* @__PURE__ */ jsxs("button", { className: `${itemCls} transition-colors ${isHovered ? "bg-blue-50 text-blue-700" : "text-gray-700 hover:bg-blue-50 hover:text-blue-700"}`, children: [
|
|
3860
|
+
secIcon(section.label),
|
|
3861
|
+
/* @__PURE__ */ jsx("span", { className: isErp ? "font-medium" : "", children: section.label }),
|
|
3862
|
+
chevron
|
|
3863
|
+
] }),
|
|
3864
|
+
section.dividerAfter && /* @__PURE__ */ jsx("div", { className: "border-t border-white/20 my-1.5 mx-2" })
|
|
3865
|
+
] }, section.label);
|
|
3830
3866
|
};
|
|
3831
3867
|
const renderVirtualSection = (v) => {
|
|
3832
3868
|
if (v.items.length === 0) return null;
|
|
3833
3869
|
const isHovered = hoveredSection === v.label;
|
|
3834
|
-
return /* @__PURE__ */ jsx(
|
|
3835
|
-
|
|
3836
|
-
{
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
hoverTimeout.current = setTimeout(() => setHoveredSection(null), 200);
|
|
3840
|
-
},
|
|
3841
|
-
children: /* @__PURE__ */ jsxs("button", { className: `${itemCls} transition-colors ${isHovered ? "bg-blue-50 text-blue-700" : "text-gray-700 hover:bg-blue-50 hover:text-blue-700"}`, children: [
|
|
3842
|
-
v.icon,
|
|
3843
|
-
/* @__PURE__ */ jsx("span", { children: v.label }),
|
|
3844
|
-
/* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5 ml-auto text-gray-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) })
|
|
3845
|
-
] })
|
|
3846
|
-
},
|
|
3847
|
-
v.label
|
|
3848
|
-
);
|
|
3870
|
+
return /* @__PURE__ */ jsx("div", { onMouseEnter: (e) => openSubmenu(0, v.label, e), children: /* @__PURE__ */ jsxs("button", { className: `${itemCls} transition-colors ${isHovered ? "bg-blue-50 text-blue-700" : "text-gray-700 hover:bg-blue-50 hover:text-blue-700"}`, children: [
|
|
3871
|
+
v.icon,
|
|
3872
|
+
/* @__PURE__ */ jsx("span", { children: v.label }),
|
|
3873
|
+
chevron
|
|
3874
|
+
] }) }, v.label);
|
|
3849
3875
|
};
|
|
3850
3876
|
return (
|
|
3851
3877
|
/* z-[1100]: sit above the entire window stack — normal windows climb to
|
|
@@ -3859,40 +3885,50 @@ function StartMenu({
|
|
|
3859
3885
|
/* @__PURE__ */ jsxs(
|
|
3860
3886
|
"div",
|
|
3861
3887
|
{
|
|
3888
|
+
"data-menu-panel": true,
|
|
3862
3889
|
className: `${sizeConfig.w} rounded-2xl flex ${isVertical ? "flex-col-reverse" : "flex-col"} overflow-hidden`,
|
|
3863
3890
|
style: { animation: "menu-in 0.15s ease-out", ...menuGlass },
|
|
3891
|
+
onMouseEnter: () => scheduleClose(0),
|
|
3892
|
+
onMouseLeave: () => scheduleClose(0),
|
|
3864
3893
|
children: [
|
|
3865
|
-
/* @__PURE__ */ jsx(
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
"
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3894
|
+
/* @__PURE__ */ jsx(
|
|
3895
|
+
"div",
|
|
3896
|
+
{
|
|
3897
|
+
className: `px-3 ${isVertical ? "pb-3 pt-2 border-t border-white/20" : "pt-3 pb-2"}`,
|
|
3898
|
+
onMouseEnter: () => scheduleClose(0),
|
|
3899
|
+
children: /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-2 ${GLASS_INPUT_BG} rounded-lg px-2.5 py-1.5`, children: [
|
|
3900
|
+
/* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5 text-gray-400 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" }) }),
|
|
3901
|
+
/* @__PURE__ */ jsx(
|
|
3902
|
+
"input",
|
|
3903
|
+
{
|
|
3904
|
+
value: search,
|
|
3905
|
+
onChange: (e) => {
|
|
3906
|
+
setSearch(e.target.value);
|
|
3907
|
+
setOpenPath([]);
|
|
3908
|
+
setSearchIdx(0);
|
|
3909
|
+
},
|
|
3910
|
+
onKeyDown: (e) => {
|
|
3911
|
+
if (search.length >= 2 && searchResults.length > 0) {
|
|
3912
|
+
if (e.key === "ArrowDown") {
|
|
3913
|
+
e.preventDefault();
|
|
3914
|
+
setSearchIdx((i) => Math.min(i + 1, searchResults.length - 1));
|
|
3915
|
+
} else if (e.key === "ArrowUp") {
|
|
3916
|
+
e.preventDefault();
|
|
3917
|
+
setSearchIdx((i) => Math.max(i - 1, 0));
|
|
3918
|
+
} else if (e.key === "Enter") {
|
|
3919
|
+
e.preventDefault();
|
|
3920
|
+
handleClick(searchResults[searchIdx].to);
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
},
|
|
3924
|
+
placeholder: "Search...",
|
|
3925
|
+
className: "flex-1 bg-transparent text-xs outline-none placeholder-gray-400",
|
|
3926
|
+
autoFocus: true
|
|
3888
3927
|
}
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
}
|
|
3894
|
-
)
|
|
3895
|
-
] }) }),
|
|
3928
|
+
)
|
|
3929
|
+
] })
|
|
3930
|
+
}
|
|
3931
|
+
),
|
|
3896
3932
|
search.length >= 2 ? /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-1 pb-2 max-h-[400px]", children: searchResults.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 text-center py-4", children: "No results" }) : searchResults.map((r, i) => /* @__PURE__ */ jsxs(
|
|
3897
3933
|
"button",
|
|
3898
3934
|
{
|
|
@@ -3912,6 +3948,7 @@ function StartMenu({
|
|
|
3912
3948
|
"button",
|
|
3913
3949
|
{
|
|
3914
3950
|
onClick: () => handleClick(item.to),
|
|
3951
|
+
onMouseEnter: () => scheduleClose(0),
|
|
3915
3952
|
className: `${itemCls} text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition-colors`,
|
|
3916
3953
|
children: [
|
|
3917
3954
|
iconEl(item.to),
|
|
@@ -3924,7 +3961,7 @@ function StartMenu({
|
|
|
3924
3961
|
(footerSections.length > 0 || footerItems.length > 0) && /* @__PURE__ */ jsx("div", { className: "border-t border-white/20 my-1.5 mx-2" }),
|
|
3925
3962
|
erpSections.map((s) => renderSection(s, true)),
|
|
3926
3963
|
erpSections.length > 0 && hasAppsGroup && /* @__PURE__ */ jsx("div", { className: "border-t border-white/20 my-1.5 mx-2" }),
|
|
3927
|
-
topItems.map((item) => /* @__PURE__ */ jsxs("div", { children: [
|
|
3964
|
+
topItems.map((item) => /* @__PURE__ */ jsxs("div", { onMouseEnter: () => scheduleClose(0), children: [
|
|
3928
3965
|
/* @__PURE__ */ jsxs(
|
|
3929
3966
|
"button",
|
|
3930
3967
|
{
|
|
@@ -3942,7 +3979,7 @@ function StartMenu({
|
|
|
3942
3979
|
virtualSections.map((v) => renderVirtualSection(v))
|
|
3943
3980
|
] }),
|
|
3944
3981
|
!isVertical && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3945
|
-
topItems.map((item) => /* @__PURE__ */ jsxs("div", { children: [
|
|
3982
|
+
topItems.map((item) => /* @__PURE__ */ jsxs("div", { onMouseEnter: () => scheduleClose(0), children: [
|
|
3946
3983
|
/* @__PURE__ */ jsxs(
|
|
3947
3984
|
"button",
|
|
3948
3985
|
{
|
|
@@ -3966,6 +4003,7 @@ function StartMenu({
|
|
|
3966
4003
|
"button",
|
|
3967
4004
|
{
|
|
3968
4005
|
onClick: () => handleClick(item.to),
|
|
4006
|
+
onMouseEnter: () => scheduleClose(0),
|
|
3969
4007
|
className: `${itemCls} text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition-colors`,
|
|
3970
4008
|
children: [
|
|
3971
4009
|
iconEl(item.to),
|
|
@@ -3976,7 +4014,7 @@ function StartMenu({
|
|
|
3976
4014
|
))
|
|
3977
4015
|
] })
|
|
3978
4016
|
] }),
|
|
3979
|
-
/* @__PURE__ */ jsx("div", { className: `${isVertical ? "border-b" : "border-t"} border-white/20 p-1`, children: /* @__PURE__ */ jsxs(
|
|
4017
|
+
/* @__PURE__ */ jsx("div", { className: `${isVertical ? "border-b" : "border-t"} border-white/20 p-1`, onMouseEnter: () => scheduleClose(0), children: /* @__PURE__ */ jsxs(
|
|
3980
4018
|
"div",
|
|
3981
4019
|
{
|
|
3982
4020
|
onClick: () => handleClick("/profile"),
|
|
@@ -4003,43 +4041,36 @@ function StartMenu({
|
|
|
4003
4041
|
]
|
|
4004
4042
|
}
|
|
4005
4043
|
),
|
|
4006
|
-
|
|
4007
|
-
|
|
4044
|
+
search.length < 2 && levels.map((items, i) => /* @__PURE__ */ jsx(
|
|
4045
|
+
MenuPanel,
|
|
4008
4046
|
{
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
children: /* @__PURE__ */ jsx("div", { className: "py-1 px-1 overflow-y-auto overscroll-contain", style: { maxHeight: availH }, children:
|
|
4020
|
-
const
|
|
4021
|
-
const
|
|
4047
|
+
left: openPath[i].left,
|
|
4048
|
+
anchorY: openPath[i].y,
|
|
4049
|
+
estHeight: items.length * sizeConfig.itemH + 12,
|
|
4050
|
+
minTop,
|
|
4051
|
+
maxBottom,
|
|
4052
|
+
flipped: openPath[i].flipped,
|
|
4053
|
+
className: sizeConfig.fw,
|
|
4054
|
+
style: menuGlass,
|
|
4055
|
+
onMouseEnter: () => scheduleClose(i + 1),
|
|
4056
|
+
onMouseLeave: () => scheduleClose(i),
|
|
4057
|
+
children: /* @__PURE__ */ jsx("div", { className: "py-1 px-1 overflow-y-auto overscroll-contain", style: { maxHeight: availH }, children: items.map((item) => {
|
|
4058
|
+
const kids = submenuItems(item);
|
|
4059
|
+
const isOpen = openPath[i + 1]?.key === item.to;
|
|
4022
4060
|
return /* @__PURE__ */ jsxs(
|
|
4023
4061
|
"div",
|
|
4024
4062
|
{
|
|
4025
|
-
onMouseEnter:
|
|
4026
|
-
clearTimeout(childHoverTimeout.current);
|
|
4027
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
4028
|
-
setHoveredChildY(rect.top + rect.height / 2);
|
|
4029
|
-
setHoveredChild(item.to);
|
|
4030
|
-
} : () => {
|
|
4031
|
-
childHoverTimeout.current = setTimeout(() => setHoveredChild(null), 200);
|
|
4032
|
-
},
|
|
4063
|
+
onMouseEnter: kids.length > 0 ? (e) => openSubmenu(i + 1, item.to, e) : () => scheduleClose(i + 1),
|
|
4033
4064
|
children: [
|
|
4034
4065
|
/* @__PURE__ */ jsxs(
|
|
4035
4066
|
"button",
|
|
4036
4067
|
{
|
|
4037
|
-
onClick: () => handleClick(item.to),
|
|
4038
|
-
className: `${itemCls} transition-colors ${
|
|
4068
|
+
onClick: kids.length > 0 ? (e) => openSubmenu(i + 1, item.to, e) : () => handleClick(item.to),
|
|
4069
|
+
className: `${itemCls} transition-colors ${isOpen ? "bg-blue-50 text-blue-700" : "text-gray-700 hover:bg-blue-50 hover:text-blue-700"}`,
|
|
4039
4070
|
children: [
|
|
4040
4071
|
iconEl(item.to),
|
|
4041
4072
|
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
4042
|
-
|
|
4073
|
+
kids.length > 0 && chevron
|
|
4043
4074
|
]
|
|
4044
4075
|
}
|
|
4045
4076
|
),
|
|
@@ -4049,55 +4080,14 @@ function StartMenu({
|
|
|
4049
4080
|
item.to
|
|
4050
4081
|
);
|
|
4051
4082
|
}) })
|
|
4052
|
-
}
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
if (search.length >= 2 || !hoveredChild) return null;
|
|
4056
|
-
const parent = flyoutItems.find((it) => it.to === hoveredChild);
|
|
4057
|
-
const kids = parent ? visibleChildren2(parent) : [];
|
|
4058
|
-
if (!parent || kids.length === 0) return null;
|
|
4059
|
-
const flyoutRect = flyoutRef.current?.getBoundingClientRect();
|
|
4060
|
-
const subLeft = flyoutRect ? flyoutRect.right + 4 : 0;
|
|
4061
|
-
const subEstH = kids.length * sizeConfig.itemH + 12;
|
|
4062
|
-
const subH = measuredSub?.key === hoveredChild ? measuredSub.h : subEstH;
|
|
4063
|
-
const subBoxH = Math.min(subH, availH);
|
|
4064
|
-
let subTop = hoveredChildY - subBoxH / 2;
|
|
4065
|
-
if (subTop < minTop) subTop = minTop;
|
|
4066
|
-
if (subTop + subBoxH > maxBottom) subTop = Math.max(minTop, maxBottom - subBoxH);
|
|
4067
|
-
return /* @__PURE__ */ jsx(
|
|
4068
|
-
"div",
|
|
4069
|
-
{
|
|
4070
|
-
ref: subFlyoutRef,
|
|
4071
|
-
className: `fixed ${sizeConfig.fw} rounded-2xl overflow-hidden`,
|
|
4072
|
-
style: { left: subLeft, top: subTop, animation: "submenu-in 0.1s ease-out", ...menuGlass },
|
|
4073
|
-
onMouseEnter: () => {
|
|
4074
|
-
clearTimeout(hoverTimeout.current);
|
|
4075
|
-
clearTimeout(childHoverTimeout.current);
|
|
4076
|
-
},
|
|
4077
|
-
onMouseLeave: () => {
|
|
4078
|
-
childHoverTimeout.current = setTimeout(() => setHoveredChild(null), 200);
|
|
4079
|
-
},
|
|
4080
|
-
children: /* @__PURE__ */ jsx("div", { className: "py-1 px-1 overflow-y-auto overscroll-contain", style: { maxHeight: availH }, children: kids.map((child) => /* @__PURE__ */ jsxs("div", { children: [
|
|
4081
|
-
/* @__PURE__ */ jsxs(
|
|
4082
|
-
"button",
|
|
4083
|
-
{
|
|
4084
|
-
onClick: () => handleClick(child.to),
|
|
4085
|
-
className: `${itemCls} text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition-colors`,
|
|
4086
|
-
children: [
|
|
4087
|
-
iconEl(child.to),
|
|
4088
|
-
/* @__PURE__ */ jsx("span", { children: child.label })
|
|
4089
|
-
]
|
|
4090
|
-
}
|
|
4091
|
-
),
|
|
4092
|
-
child.dividerAfter && /* @__PURE__ */ jsx("div", { className: "border-t border-white/20 my-1.5 mx-2" })
|
|
4093
|
-
] }, child.to)) })
|
|
4094
|
-
}
|
|
4095
|
-
);
|
|
4096
|
-
})()
|
|
4083
|
+
},
|
|
4084
|
+
`${i}:${openPath[i].key}`
|
|
4085
|
+
))
|
|
4097
4086
|
] }),
|
|
4098
4087
|
/* @__PURE__ */ jsx("style", { children: `
|
|
4099
4088
|
@keyframes menu-in { from { opacity: 0; transform: scale(0.95) translateY(8px); } to { opacity: 1; transform: scale(1) translateY(0); } }
|
|
4100
4089
|
@keyframes submenu-in { from { opacity: 0; transform: translateX(-4px); } to { opacity: 1; transform: translateX(0); } }
|
|
4090
|
+
@keyframes submenu-in-left { from { opacity: 0; transform: translateX(4px); } to { opacity: 1; transform: translateX(0); } }
|
|
4101
4091
|
` })
|
|
4102
4092
|
] })
|
|
4103
4093
|
);
|
|
@@ -4144,7 +4134,7 @@ function MobileAppLanding({
|
|
|
4144
4134
|
}
|
|
4145
4135
|
);
|
|
4146
4136
|
}
|
|
4147
|
-
var Sidebar = lazy(() => import('./Sidebar-
|
|
4137
|
+
var Sidebar = lazy(() => import('./Sidebar-IMACKSHS.js'));
|
|
4148
4138
|
function useFavorites(wallpapers) {
|
|
4149
4139
|
const { prefs, save } = useShellPrefs();
|
|
4150
4140
|
const favorites = prefs.favorite_pages || [];
|