pixelize-design-library 2.4.2-beta.58 → 2.4.2-beta.59
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/CHANGELOG.md +75 -0
- package/COMPONENT-GOTCHAS.md +88 -2
- package/DESIGN-GUIDE.md +26 -3
- package/dist/Components/AppSwitcher/AppSwitcher.js +5 -1
- package/dist/Components/AppSwitcher/AppSwitcherProps.d.ts +2 -0
- package/dist/Components/AppSwitcher/AppSwitcherProps.js +7 -4
- package/dist/Components/Breadcrumbs/Breadcrumbs.d.ts +1 -1
- package/dist/Components/Breadcrumbs/Breadcrumbs.js +33 -10
- package/dist/Components/Breadcrumbs/BreadcrumbsProps.d.ts +16 -2
- package/dist/Components/Card/Card.d.ts +3 -1
- package/dist/Components/Card/Card.js +12 -2
- package/dist/Components/Card/CardProps.d.ts +5 -1
- package/dist/Components/Editor/Editor.js +50 -2
- package/dist/Components/Editor/EditorFieldBar.d.ts +15 -0
- package/dist/Components/Editor/EditorFieldBar.js +19 -0
- package/dist/Components/Editor/EditorProps.d.ts +74 -1
- package/dist/Components/Editor/MakeDynamicDialog.d.ts +12 -0
- package/dist/Components/Editor/MakeDynamicDialog.js +59 -0
- package/dist/Components/Editor/editorLabels.d.ts +4 -0
- package/dist/Components/Editor/editorLabels.js +24 -0
- package/dist/Components/Editor/editorTokenStyles.d.ts +28 -0
- package/dist/Components/Editor/editorTokenStyles.js +30 -0
- package/dist/Components/Editor/editorTokens.d.ts +26 -0
- package/dist/Components/Editor/editorTokens.js +134 -0
- package/dist/Components/Editor/useEditorBridge.d.ts +40 -0
- package/dist/Components/Editor/useEditorBridge.js +141 -0
- package/dist/Components/Kbd/Kbd.d.ts +6 -0
- package/dist/Components/Kbd/Kbd.js +33 -0
- package/dist/Components/Kbd/KbdProps.d.ts +17 -0
- package/dist/Components/Kbd/KbdProps.js +2 -0
- package/dist/Components/LauncherTrigger/LauncherTrigger.d.ts +4 -0
- package/dist/Components/LauncherTrigger/LauncherTrigger.js +41 -0
- package/dist/Components/LauncherTrigger/LauncherTriggerProps.d.ts +16 -0
- package/dist/Components/LauncherTrigger/LauncherTriggerProps.js +2 -0
- package/dist/Components/NavLauncher/NavLauncher.d.ts +3 -0
- package/dist/Components/NavLauncher/NavLauncher.js +395 -0
- package/dist/Components/NavLauncher/NavLauncherProps.d.ts +187 -0
- package/dist/Components/NavLauncher/NavLauncherProps.js +2 -0
- package/dist/Components/NavLauncher/NewTabLink.d.ts +3 -0
- package/dist/Components/NavLauncher/NewTabLink.js +14 -0
- package/dist/Components/NavLauncher/PinToggle.d.ts +3 -0
- package/dist/Components/NavLauncher/PinToggle.js +21 -0
- package/dist/Components/NavLauncher/launcherParts.d.ts +93 -0
- package/dist/Components/NavLauncher/launcherParts.js +130 -0
- package/dist/Components/NavLauncher/match.d.ts +53 -0
- package/dist/Components/NavLauncher/match.js +178 -0
- package/dist/Components/NavLauncher/revealStyles.d.ts +4 -0
- package/dist/Components/NavLauncher/revealStyles.js +19 -0
- package/dist/Components/NavigationBar/NavigationBar.d.ts +1 -1
- package/dist/Components/NavigationBar/NavigationBar.js +2 -2
- package/dist/Components/NavigationBar/NavigationBarProps.d.ts +2 -0
- package/dist/Components/PageHeader/PageHeader.d.ts +1 -1
- package/dist/Components/PageHeader/PageHeader.js +9 -2
- package/dist/Components/PageHeader/PageHeaderProps.d.ts +8 -0
- package/dist/Components/PdfViewer/PdfViewer.d.ts +21 -4
- package/dist/Components/PdfViewer/PdfViewer.js +124 -3
- package/dist/Components/Search/Search.js +2 -1
- package/dist/Components/SideBar/SecondaryBar.js +4 -3
- package/dist/Components/SideBar/SideBar.d.ts +1 -1
- package/dist/Components/SideBar/SideBar.js +30 -9
- package/dist/Components/SideBar/SideBarProps.d.ts +32 -0
- package/dist/Components/SideBar/components/MenuItemBox.d.ts +6 -0
- package/dist/Components/SideBar/components/MenuItemBox.js +8 -4
- package/dist/Components/SideBar/components/MenuPopoverContent.js +3 -2
- package/dist/Components/SideBar/components/OtherApps.js +2 -1
- package/dist/Components/SideBar/components/PinnedSection.d.ts +17 -0
- package/dist/Components/SideBar/components/PinnedSection.js +98 -0
- package/dist/Components/SideBar/sidebarFocus.d.ts +23 -0
- package/dist/Components/SideBar/sidebarFocus.js +20 -0
- package/dist/Components/StageProgress/StageProgress.js +2 -2
- package/dist/Components/StageProgress/StageProgressProps.d.ts +2 -0
- package/dist/Components/StageProgress/StepperStage.js +5 -2
- package/dist/Components/WizardDialog/WizardDialog.d.ts +9 -0
- package/dist/Components/WizardDialog/WizardDialog.js +102 -0
- package/dist/Components/WizardDialog/WizardDialogProps.d.ts +48 -0
- package/dist/Components/WizardDialog/WizardDialogProps.js +2 -0
- package/dist/Components/WizardDialog/useWizardFlow.d.ts +22 -0
- package/dist/Components/WizardDialog/useWizardFlow.js +47 -0
- package/dist/Components/WizardDialog/wizardSteps.d.ts +31 -0
- package/dist/Components/WizardDialog/wizardSteps.js +40 -0
- package/dist/esm/Components/AppSwitcher/AppSwitcher.js +2 -1
- package/dist/esm/Components/AppSwitcher/AppSwitcherProps.js +5 -3
- package/dist/esm/Components/Breadcrumbs/Breadcrumbs.js +35 -12
- package/dist/esm/Components/Card/Card.js +12 -2
- package/dist/esm/Components/Editor/Editor.js +48 -3
- package/dist/esm/Components/Editor/EditorFieldBar.js +13 -0
- package/dist/esm/Components/Editor/MakeDynamicDialog.js +20 -0
- package/dist/esm/Components/Editor/editorLabels.js +20 -0
- package/dist/esm/Components/Editor/editorTokenStyles.js +25 -0
- package/dist/esm/Components/Editor/editorTokens.js +121 -0
- package/dist/esm/Components/Editor/useEditorBridge.js +138 -0
- package/dist/esm/Components/Kbd/Kbd.js +28 -0
- package/dist/esm/Components/Kbd/KbdProps.js +1 -0
- package/dist/esm/Components/LauncherTrigger/LauncherTrigger.js +37 -0
- package/dist/esm/Components/LauncherTrigger/LauncherTriggerProps.js +1 -0
- package/dist/esm/Components/NavLauncher/NavLauncher.js +356 -0
- package/dist/esm/Components/NavLauncher/NavLauncherProps.js +1 -0
- package/dist/esm/Components/NavLauncher/NewTabLink.js +11 -0
- package/dist/esm/Components/NavLauncher/PinToggle.js +18 -0
- package/dist/esm/Components/NavLauncher/launcherParts.js +110 -0
- package/dist/esm/Components/NavLauncher/match.js +172 -0
- package/dist/esm/Components/NavLauncher/revealStyles.js +15 -0
- package/dist/esm/Components/NavigationBar/NavigationBar.js +3 -3
- package/dist/esm/Components/PageHeader/PageHeader.js +9 -2
- package/dist/esm/Components/PdfViewer/PdfViewer.js +123 -2
- package/dist/esm/Components/Search/Search.js +3 -2
- package/dist/esm/Components/SideBar/SecondaryBar.js +4 -3
- package/dist/esm/Components/SideBar/SideBar.js +31 -10
- package/dist/esm/Components/SideBar/components/MenuItemBox.js +4 -2
- package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +3 -2
- package/dist/esm/Components/SideBar/components/OtherApps.js +2 -1
- package/dist/esm/Components/SideBar/components/PinnedSection.js +92 -0
- package/dist/esm/Components/SideBar/sidebarFocus.js +15 -0
- package/dist/esm/Components/StageProgress/StageProgress.js +2 -2
- package/dist/esm/Components/StageProgress/StepperStage.js +5 -2
- package/dist/esm/Components/WizardDialog/WizardDialog.js +58 -0
- package/dist/esm/Components/WizardDialog/WizardDialogProps.js +1 -0
- package/dist/esm/Components/WizardDialog/useWizardFlow.js +44 -0
- package/dist/esm/Components/WizardDialog/wizardSteps.js +35 -0
- package/dist/esm/index.js +7 -0
- package/dist/index.d.ts +19 -4
- package/dist/index.js +21 -1
- package/package.json +1 -1
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.default = NavLauncher;
|
|
40
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
41
|
+
const react_1 = require("@chakra-ui/react");
|
|
42
|
+
const lucide_react_1 = require("lucide-react");
|
|
43
|
+
const react_2 = require("react");
|
|
44
|
+
const useMatchMedia_1 = require("../../Hooks/useMatchMedia");
|
|
45
|
+
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
46
|
+
const chartColorsFromTheme_1 = require("../../Theme/tokens/builders/chartColorsFromTheme");
|
|
47
|
+
const placeholderInk_1 = require("../../Theme/tokens/builders/placeholderInk");
|
|
48
|
+
const filledSurface_1 = require("../../Theme/tokens/builders/filledSurface");
|
|
49
|
+
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
50
|
+
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
51
|
+
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
52
|
+
const AppMark_1 = require("../AppSwitcher/AppMark");
|
|
53
|
+
const Drawer_1 = __importStar(require("../Drawer/Drawer"));
|
|
54
|
+
const EmptyState_1 = __importDefault(require("../EmptyState/EmptyState"));
|
|
55
|
+
const Kbd_1 = __importDefault(require("../Kbd/Kbd"));
|
|
56
|
+
const Skeletons_1 = __importDefault(require("../Skeletons/Skeletons"));
|
|
57
|
+
const launcherParts_1 = require("./launcherParts");
|
|
58
|
+
const match_1 = require("./match");
|
|
59
|
+
const NewTabLink_1 = __importDefault(require("./NewTabLink"));
|
|
60
|
+
const PinToggle_1 = __importDefault(require("./PinToggle"));
|
|
61
|
+
const NO_APPS = [];
|
|
62
|
+
const MD_QUERY = "(min-width: 48em)";
|
|
63
|
+
const XL_QUERY = "(min-width: 80em)";
|
|
64
|
+
const CELL_SELECTOR = "[data-nav-cell]";
|
|
65
|
+
const DEFAULT_LABELS = {
|
|
66
|
+
title: "Menu",
|
|
67
|
+
searchPlaceholder: "Search menus and screens",
|
|
68
|
+
clearSearch: "Clear search",
|
|
69
|
+
close: "Close menu",
|
|
70
|
+
pinned: "Pinned",
|
|
71
|
+
pinnedEmpty: "Pin the screens you use most. Pins follow your account.",
|
|
72
|
+
frequent: "Frequently visited",
|
|
73
|
+
recent: "Recent",
|
|
74
|
+
screensGroup: "Screens",
|
|
75
|
+
actionsGroup: "Actions",
|
|
76
|
+
pin: (label) => `Pin ${label}`,
|
|
77
|
+
unpin: (label) => `Unpin ${label}`,
|
|
78
|
+
openInNewTab: (label) => `Open ${label} in a new tab`,
|
|
79
|
+
noResults: (query) => `Nothing called “${query}”`,
|
|
80
|
+
noResultsHint: "Try a module name, or another word for it.",
|
|
81
|
+
searching: "Searching…",
|
|
82
|
+
loading: "Loading menu",
|
|
83
|
+
empty: "Nothing to show yet",
|
|
84
|
+
hintMove: "move",
|
|
85
|
+
hintOpen: "open here",
|
|
86
|
+
hintNewTab: "new tab",
|
|
87
|
+
hintPin: "pin",
|
|
88
|
+
hintClose: "close",
|
|
89
|
+
hintOpenMenu: "open",
|
|
90
|
+
apps: "Your apps",
|
|
91
|
+
found: (count) => `${count} found`,
|
|
92
|
+
pinTip: "Pin to quick access",
|
|
93
|
+
unpinTip: "Pinned — click to unpin",
|
|
94
|
+
quickAccess: "Quick access",
|
|
95
|
+
newBadge: "New",
|
|
96
|
+
screens: "screens",
|
|
97
|
+
pinShort: "Pin",
|
|
98
|
+
footerNote: "Pins follow your account",
|
|
99
|
+
suggestionsHint: "Try one of these",
|
|
100
|
+
};
|
|
101
|
+
const isApplePlatform = () => typeof navigator !== "undefined" &&
|
|
102
|
+
/Mac|iPhone|iPad/.test(navigator.platform);
|
|
103
|
+
const visibleSections = (sections) => sections
|
|
104
|
+
.map((section) => ({
|
|
105
|
+
...section,
|
|
106
|
+
modules: section.modules.filter((m) => m.screens.length > 0),
|
|
107
|
+
}))
|
|
108
|
+
.filter((section) => section.modules.length > 0);
|
|
109
|
+
const indexScreens = (sections) => {
|
|
110
|
+
const map = new Map();
|
|
111
|
+
for (const section of sections) {
|
|
112
|
+
for (const module of section.modules) {
|
|
113
|
+
for (const screen of module.screens)
|
|
114
|
+
if (!map.has(screen.id))
|
|
115
|
+
map.set(screen.id, { screen, module });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return map;
|
|
119
|
+
};
|
|
120
|
+
const GRID_COLUMNS = {
|
|
121
|
+
base: "minmax(0, 1fr)",
|
|
122
|
+
md: "repeat(2, minmax(0, 1fr))",
|
|
123
|
+
xl: "repeat(3, minmax(0, 1fr))",
|
|
124
|
+
};
|
|
125
|
+
function ItemGrid({ children }) {
|
|
126
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Box, { "data-nav-grid": true, display: "grid", gridTemplateColumns: GRID_COLUMNS, gap: "1px 8px", alignItems: "start", children: children }));
|
|
127
|
+
}
|
|
128
|
+
function Pane({ children }) {
|
|
129
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Flex, { direction: "column", gap: "14px", flex: 1, minW: 0, px: "18px", pt: "12px", pb: "16px", overflowY: { base: "visible", lg: "auto" }, children: children }));
|
|
130
|
+
}
|
|
131
|
+
function Rail({ label, children }) {
|
|
132
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
133
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Flex, { as: "aside", "aria-label": label, direction: "column", gap: "16px", w: { base: "100%", lg: "300px" }, flexShrink: 0, px: "14px", pt: "12px", pb: "16px", bg: colors.backgroundColor.subtle, borderTopWidth: { base: "1px", lg: 0 }, borderLeftWidth: { base: 0, lg: "1px" }, borderColor: colors.boxborder[200], overflowY: { base: "visible", lg: "auto" }, children: children }));
|
|
134
|
+
}
|
|
135
|
+
function SkeletonLayout({ label }) {
|
|
136
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { role: "status", "aria-busy": "true", "aria-label": label, direction: { base: "column", lg: "row" }, flex: 1, minH: 0, children: [(0, jsx_runtime_1.jsx)(Pane, { children: [0, 1, 2].map((section) => ((0, jsx_runtime_1.jsxs)(react_1.Box, { children: [(0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: "0.75rem", width: "6rem" }), (0, jsx_runtime_1.jsx)(ItemGrid, { children: [0, 1, 2, 3].map((row) => ((0, jsx_runtime_1.jsxs)(react_1.Flex, { align: "center", gap: "10px", h: "34px", mt: 2, children: [(0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: "1rem", width: "1rem" }), (0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: "0.875rem", width: "8rem" })] }, row))) })] }, section))) }), (0, jsx_runtime_1.jsx)(Rail, { label: label, children: [0, 1].map((group) => ((0, jsx_runtime_1.jsxs)(react_1.Box, { children: [(0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: "0.75rem", width: "5rem" }), [0, 1, 2].map((row) => ((0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: "1.5rem", width: "100%", mt: 2 }, row)))] }, group))) })] }));
|
|
137
|
+
}
|
|
138
|
+
function SearchField({ inputRef, value, onChange, onKeyDown, labels, hint, listboxId, activeId, isExpanded, hasResults, }) {
|
|
139
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
140
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { flex: 1, maxW: "520px", h: "38px", align: "center", gap: "10px", pl: "12px", pr: "8px", borderRadius: "10px", borderWidth: "1px", borderColor: colors.primary[500], bg: colors.surfaceRaised, boxShadow: `0 0 0 3px ${colors.primary.opacity[16]}`, children: [(0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", color: colors.accentText, display: "inline-flex", flexShrink: 0, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 16, strokeWidth: 2.2, "aria-hidden": true }) }), (0, jsx_runtime_1.jsx)(react_1.Box, { as: "input", ref: inputRef, value: value, onChange: (event) => onChange(event.target.value), onKeyDown: onKeyDown, placeholder: labels.searchPlaceholder, "aria-label": labels.searchPlaceholder, role: "combobox", "aria-expanded": isExpanded, "aria-controls": isExpanded && hasResults ? listboxId : undefined, "aria-autocomplete": "list", "aria-activedescendant": activeId, autoComplete: "off", flex: 1, minW: 0, h: "34px", border: 0, outline: "none", bg: "transparent", fontSize: "14.5px", color: colors.text[900], _placeholder: { color: (0, placeholderInk_1.placeholderInk)(colors), opacity: 1 } }), value ? ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "button", type: "button", "aria-label": labels.clearSearch, onClick: () => {
|
|
141
|
+
var _a;
|
|
142
|
+
onChange("");
|
|
143
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
144
|
+
}, boxSize: "24px", borderRadius: "6px", bg: colors.backgroundColor.subtle, color: colors.textMuted, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, sx: (0, focusRing_styles_1.focusVisibleStyles)(colors), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 12, strokeWidth: 2.4, "aria-hidden": true }) })) : null, hint ? ((0, jsx_runtime_1.jsx)(react_1.Box, { "aria-hidden": true, display: { base: "none", md: "block" }, children: (0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: hint }) })) : null] }));
|
|
145
|
+
}
|
|
146
|
+
function SearchRow({ item, query, isActive, link, labels, optionId, isPinned, canPin, onPin, onOpen, onHover, }) {
|
|
147
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
148
|
+
const isScreen = item.kind === "screen";
|
|
149
|
+
const hasStar = Boolean(item.screenId) && canPin;
|
|
150
|
+
const base = item.badge ? 24 : 0;
|
|
151
|
+
const open = base + (hasStar ? 46 : item.href ? 24 : 0);
|
|
152
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { role: "presentation", "data-group": true, "data-reveal": isActive, position: "relative", align: "flex-start", gap: "10px", minH: "34px", pl: "8px", pr: "4px", pt: "7px", pb: "5px", borderRadius: "8px", bg: isActive ? colors.primary[50] : "transparent", boxShadow: isActive ? (0, focusRing_styles_1.focusRingShadow)(colors) : "none", transition: (0, motion_1.transitionFor)(["background-color"]), onMouseMove: onHover, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { id: optionId, role: "option", "aria-selected": isActive, "data-active": isActive, title: isScreen ? `${item.label} — ${item.context}` : item.label, flex: 1, minW: 0, align: "flex-start", gap: "10px", cursor: "pointer", onClick: () => {
|
|
153
|
+
if (!item.href)
|
|
154
|
+
onOpen();
|
|
155
|
+
}, children: [(0, jsx_runtime_1.jsx)(launcherParts_1.RowIcon, { icon: item.icon, size: 16, mt: "2px" }), (0, jsx_runtime_1.jsxs)(react_1.Flex, { direction: "column", flex: 1, minW: 0, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { align: "center", gap: "6px", minW: 0, h: "19px", pr: `${isActive ? open + 26 : base}px`, _groupHover: { pr: `${isActive ? open + 26 : open}px` }, children: [(0, jsx_runtime_1.jsxs)(react_1.Text, { fontSize: "13.5px", fontWeight: 500, color: colors.text[900], noOfLines: 1, minW: 0, children: [(0, jsx_runtime_1.jsx)(launcherParts_1.Highlight, { text: item.label, query: query }), isScreen ? ((0, jsx_runtime_1.jsx)(react_1.Text, { as: "span", fontWeight: 400, color: colors.textMuted, children: ` — ${item.context}` })) : null] }), item.isNew ? (0, jsx_runtime_1.jsx)(launcherParts_1.NewTag, { label: labels.newBadge }) : null, item.keywordHint ? ((0, jsx_runtime_1.jsx)(react_1.Text, { as: "span", fontSize: "11.5px", color: colors.textMuted, whiteSpace: "nowrap", children: ` · ${item.keywordHint}` })) : null, item.hint ? ((0, jsx_runtime_1.jsx)(react_1.Text, { as: "span", fontSize: "11.5px", color: colors.textMuted, whiteSpace: "nowrap", children: item.hint })) : null] }), item.context && !isScreen ? ((0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "11.5px", lineHeight: "15px", color: colors.textMuted, noOfLines: 1, children: item.context })) : null] })] }), item.href ? ((0, jsx_runtime_1.jsx)(react_1.Box, { as: link.linkAs, href: item.href, tabIndex: -1, "aria-hidden": true, onClick: link.onPlainClick, position: "absolute", inset: 0, borderRadius: "8px" })) : null, (0, jsx_runtime_1.jsx)(launcherParts_1.RowActions, { children: (0, jsx_runtime_1.jsxs)(react_1.Box, { "aria-hidden": true, display: "contents", children: [(0, jsx_runtime_1.jsx)(launcherParts_1.CountBadge, { value: item.badge }), isActive ? (0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: "\u21B5" }) : null, hasStar ? ((0, jsx_runtime_1.jsx)(PinToggle_1.default, { isPinned: isPinned, label: isPinned ? labels.unpin(item.label) : labels.pin(item.label), title: "", onToggle: onPin, tabIndex: -1 })) : null, item.href ? ((0, jsx_runtime_1.jsx)(NewTabLink_1.default, { href: item.href, label: labels.openInNewTab(isScreen ? `${item.label} — ${item.context}` : item.label), linkAs: link.linkAs, tabIndex: -1 })) : null] }) }), hasStar ? ((0, jsx_runtime_1.jsx)(launcherParts_1.RowTip, { text: isPinned ? labels.unpinTip : labels.pinTip })) : null] }));
|
|
156
|
+
}
|
|
157
|
+
/** Left-drawer navigation launcher: browse every module, pins, Frequent, Recent, search and (compact) the suite's apps. */
|
|
158
|
+
function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, frequentIds, recentIds, onTogglePin, onNavigate, linkAs = "a", actions, extraResults, query: controlledQuery, onQueryChange, isSearching = false, isLoading = false, keyScreenLimit = 3, frequentLimit = 4, suggestions, searchHint, openShortcutHint, modKeyLabel, maxWidth = "1180px", labels: labelOverrides, apps, currentAppId, onAppSelect, appsVisibility = "apps-on-compact", "data-testid": testId, }) {
|
|
159
|
+
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
160
|
+
const { colors } = theme;
|
|
161
|
+
const inputRef = (0, react_2.useRef)(null);
|
|
162
|
+
const bodyRef = (0, react_2.useRef)(null);
|
|
163
|
+
const baseId = (0, react_2.useId)();
|
|
164
|
+
const isDesktop = (0, useMatchMedia_1.useMatchMedia)(MD_QUERY, true);
|
|
165
|
+
const isWide = (0, useMatchMedia_1.useMatchMedia)(XL_QUERY, false);
|
|
166
|
+
const columns = isWide ? 3 : isDesktop ? 2 : 1;
|
|
167
|
+
const [innerQuery, setInnerQuery] = (0, react_2.useState)("");
|
|
168
|
+
const [activeIndex, setActiveIndex] = (0, react_2.useState)(0);
|
|
169
|
+
const labels = (0, react_2.useMemo)(() => ({ ...DEFAULT_LABELS, ...labelOverrides }), [labelOverrides]);
|
|
170
|
+
const isControlled = controlledQuery !== undefined;
|
|
171
|
+
const query = isControlled ? controlledQuery : innerQuery;
|
|
172
|
+
const setQuery = (0, react_2.useCallback)((next) => {
|
|
173
|
+
if (!isControlled)
|
|
174
|
+
setInnerQuery(next);
|
|
175
|
+
onQueryChange === null || onQueryChange === void 0 ? void 0 : onQueryChange(next);
|
|
176
|
+
}, [isControlled, onQueryChange]);
|
|
177
|
+
const latest = (0, react_2.useRef)({ query, setQuery });
|
|
178
|
+
latest.current = { query, setQuery };
|
|
179
|
+
(0, react_2.useEffect)(() => {
|
|
180
|
+
if (!isOpen && latest.current.query !== "")
|
|
181
|
+
latest.current.setQuery("");
|
|
182
|
+
}, [isOpen]);
|
|
183
|
+
(0, react_2.useEffect)(() => setActiveIndex(0), [query]);
|
|
184
|
+
const sections = (0, react_2.useMemo)(() => visibleSections(rawSections), [rawSections]);
|
|
185
|
+
const byId = (0, react_2.useMemo)(() => indexScreens(sections), [sections]);
|
|
186
|
+
const pinned = (0, react_2.useMemo)(() => new Set(pinnedIds), [pinnedIds]);
|
|
187
|
+
const pinnedItems = (0, react_2.useMemo)(() => pinnedIds.flatMap((id) => { var _a; return (_a = byId.get(id)) !== null && _a !== void 0 ? _a : []; }), [pinnedIds, byId]);
|
|
188
|
+
const frequentItems = (0, react_2.useMemo)(() => frequentIds.flatMap((id) => { var _a; return (_a = byId.get(id)) !== null && _a !== void 0 ? _a : []; }), [frequentIds, byId]);
|
|
189
|
+
const recentItems = (0, react_2.useMemo)(() => recentIds.flatMap((id) => { var _a; return (_a = byId.get(id)) !== null && _a !== void 0 ? _a : []; }), [recentIds, byId]);
|
|
190
|
+
const found = (0, react_2.useMemo)(() => (0, match_1.searchLauncherSections)({
|
|
191
|
+
query,
|
|
192
|
+
sections,
|
|
193
|
+
actions,
|
|
194
|
+
extraResults,
|
|
195
|
+
actionsLabel: labels.actionsGroup,
|
|
196
|
+
}), [query, sections, actions, extraResults, labels.actionsGroup]);
|
|
197
|
+
const groups = (0, react_2.useMemo)(() => [
|
|
198
|
+
...found.sections.map((s) => ({
|
|
199
|
+
id: `section:${s.id}`,
|
|
200
|
+
label: s.label,
|
|
201
|
+
meta: labels.found(s.items.length),
|
|
202
|
+
items: s.items,
|
|
203
|
+
})),
|
|
204
|
+
...found.groups,
|
|
205
|
+
], [found, labels]);
|
|
206
|
+
const results = (0, react_2.useMemo)(() => groups.flatMap((g) => g.items), [groups]);
|
|
207
|
+
const groupStarts = (0, react_2.useMemo)(() => groups.map((_, i) => groups.slice(0, i).reduce((n, g) => n + g.items.length, 0)), [groups]);
|
|
208
|
+
const isSearchView = query.trim().length > 0;
|
|
209
|
+
const link = (0, react_2.useMemo)(() => ({
|
|
210
|
+
linkAs,
|
|
211
|
+
onPlainClick: (event) => {
|
|
212
|
+
if ((0, launcherParts_1.isPlainClick)(event))
|
|
213
|
+
onClose();
|
|
214
|
+
},
|
|
215
|
+
}), [linkAs, onClose]);
|
|
216
|
+
const open = (0, react_2.useCallback)((item, newTab) => {
|
|
217
|
+
if (item.run) {
|
|
218
|
+
onClose();
|
|
219
|
+
item.run();
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (!item.href)
|
|
223
|
+
return;
|
|
224
|
+
if (!newTab)
|
|
225
|
+
onClose();
|
|
226
|
+
onNavigate(item.href, { newTab });
|
|
227
|
+
}, [onClose, onNavigate]);
|
|
228
|
+
const moveFocus = (delta) => {
|
|
229
|
+
var _a, _b;
|
|
230
|
+
const items = Array.from((_b = (_a = bodyRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll(launcherParts_1.ITEM_SELECTOR)) !== null && _b !== void 0 ? _b : []);
|
|
231
|
+
if (items.length === 0)
|
|
232
|
+
return;
|
|
233
|
+
const current = items.indexOf(document.activeElement);
|
|
234
|
+
const next = delta === "first"
|
|
235
|
+
? 0
|
|
236
|
+
: delta === "last"
|
|
237
|
+
? items.length - 1
|
|
238
|
+
: Math.min(items.length - 1, Math.max(0, current + delta));
|
|
239
|
+
items[next].focus();
|
|
240
|
+
};
|
|
241
|
+
const onInputKeyDown = (event) => {
|
|
242
|
+
if (!isSearchView) {
|
|
243
|
+
if (event.key === "ArrowDown") {
|
|
244
|
+
event.preventDefault();
|
|
245
|
+
moveFocus("first");
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (results.length === 0)
|
|
250
|
+
return;
|
|
251
|
+
if (event.key === "ArrowDown") {
|
|
252
|
+
event.preventDefault();
|
|
253
|
+
setActiveIndex((i) => (i + 1) % results.length);
|
|
254
|
+
}
|
|
255
|
+
else if (event.key === "ArrowUp") {
|
|
256
|
+
event.preventDefault();
|
|
257
|
+
setActiveIndex((i) => (i - 1 + results.length) % results.length);
|
|
258
|
+
}
|
|
259
|
+
else if (event.key === "Enter") {
|
|
260
|
+
event.preventDefault();
|
|
261
|
+
open(results[activeIndex], event.metaKey || event.ctrlKey);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const moveInGrid = (event, cell) => {
|
|
265
|
+
var _a, _b, _c, _d;
|
|
266
|
+
const grid = cell.closest("[data-nav-grid]");
|
|
267
|
+
const cells = Array.from((_a = grid === null || grid === void 0 ? void 0 : grid.querySelectorAll(CELL_SELECTOR)) !== null && _a !== void 0 ? _a : []);
|
|
268
|
+
const index = cells.indexOf(cell);
|
|
269
|
+
const focusCell = (target) => { var _a; return (_a = target === null || target === void 0 ? void 0 : target.querySelector(launcherParts_1.ITEM_SELECTOR)) === null || _a === void 0 ? void 0 : _a.focus(); };
|
|
270
|
+
if (event.key === "ArrowRight" || event.key === "ArrowLeft") {
|
|
271
|
+
focusCell(cells[index + (event.key === "ArrowRight" ? 1 : -1)]);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const down = event.key === "ArrowDown";
|
|
275
|
+
const within = cells[index + (down ? columns : -columns)];
|
|
276
|
+
if (within)
|
|
277
|
+
return focusCell(within);
|
|
278
|
+
const hasRowAfter = down &&
|
|
279
|
+
Math.floor(index / columns) < Math.floor((cells.length - 1) / columns);
|
|
280
|
+
if (hasRowAfter)
|
|
281
|
+
return focusCell(cells[cells.length - 1]);
|
|
282
|
+
const grids = Array.from((_c = (_b = bodyRef.current) === null || _b === void 0 ? void 0 : _b.querySelectorAll("[data-nav-grid]")) !== null && _c !== void 0 ? _c : []);
|
|
283
|
+
const neighbour = grids[grids.indexOf(grid) + (down ? 1 : -1)];
|
|
284
|
+
const neighbourCells = Array.from((_d = neighbour === null || neighbour === void 0 ? void 0 : neighbour.querySelectorAll(CELL_SELECTOR)) !== null && _d !== void 0 ? _d : []);
|
|
285
|
+
if (neighbourCells.length === 0)
|
|
286
|
+
return moveFocus(down ? 1 : -1);
|
|
287
|
+
const column = index % columns;
|
|
288
|
+
const row = down ? 0 : Math.floor((neighbourCells.length - 1) / columns);
|
|
289
|
+
focusCell(neighbourCells[Math.min(neighbourCells.length - 1, row * columns + column)]);
|
|
290
|
+
};
|
|
291
|
+
const pinFromKeyboard = (event) => {
|
|
292
|
+
var _a, _b;
|
|
293
|
+
const target = event.target;
|
|
294
|
+
const pinId = target.matches(launcherParts_1.ITEM_SELECTOR)
|
|
295
|
+
? target.getAttribute("data-pin-id")
|
|
296
|
+
: null;
|
|
297
|
+
if (!pinId || !onTogglePin)
|
|
298
|
+
return false;
|
|
299
|
+
event.preventDefault();
|
|
300
|
+
const before = Array.from((_b = (_a = bodyRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll(launcherParts_1.ITEM_SELECTOR)) !== null && _b !== void 0 ? _b : []);
|
|
301
|
+
const position = before.indexOf(target);
|
|
302
|
+
onTogglePin(pinId);
|
|
303
|
+
setTimeout(() => {
|
|
304
|
+
var _a, _b, _c;
|
|
305
|
+
if ((_a = bodyRef.current) === null || _a === void 0 ? void 0 : _a.contains(document.activeElement))
|
|
306
|
+
return;
|
|
307
|
+
const after = (_b = bodyRef.current) === null || _b === void 0 ? void 0 : _b.querySelectorAll(launcherParts_1.ITEM_SELECTOR);
|
|
308
|
+
(_c = after === null || after === void 0 ? void 0 : after[Math.min(position, after.length - 1)]) === null || _c === void 0 ? void 0 : _c.focus();
|
|
309
|
+
}, 0);
|
|
310
|
+
return true;
|
|
311
|
+
};
|
|
312
|
+
const onBodyKeyDown = (event) => {
|
|
313
|
+
var _a;
|
|
314
|
+
if (isSearchView || event.target === inputRef.current)
|
|
315
|
+
return;
|
|
316
|
+
const isArrow = event.key.startsWith("Arrow");
|
|
317
|
+
const cell = event.target.closest(CELL_SELECTOR);
|
|
318
|
+
if (isArrow && cell && !event.altKey && !event.metaKey && !event.ctrlKey) {
|
|
319
|
+
event.preventDefault();
|
|
320
|
+
moveInGrid(event, cell);
|
|
321
|
+
}
|
|
322
|
+
else if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
323
|
+
event.preventDefault();
|
|
324
|
+
moveFocus(event.key === "ArrowDown" ? 1 : -1);
|
|
325
|
+
}
|
|
326
|
+
else if (event.key === "Home" || event.key === "End") {
|
|
327
|
+
event.preventDefault();
|
|
328
|
+
moveFocus(event.key === "Home" ? "first" : "last");
|
|
329
|
+
}
|
|
330
|
+
else if (event.key.length === 1 &&
|
|
331
|
+
!event.metaKey &&
|
|
332
|
+
!event.ctrlKey &&
|
|
333
|
+
!event.altKey &&
|
|
334
|
+
event.key !== " ") {
|
|
335
|
+
if ((event.key === "p" || event.key === "P") && pinFromKeyboard(event))
|
|
336
|
+
return;
|
|
337
|
+
event.preventDefault();
|
|
338
|
+
setQuery(query + event.key);
|
|
339
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
(0, react_2.useEffect)(() => {
|
|
343
|
+
var _a, _b, _c;
|
|
344
|
+
(_c = (_b = (_a = bodyRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('[data-active="true"]')) === null || _b === void 0 ? void 0 : _b.scrollIntoView) === null || _c === void 0 ? void 0 : _c.call(_b, { block: "nearest" });
|
|
345
|
+
}, [activeIndex, results]);
|
|
346
|
+
const appList = apps !== null && apps !== void 0 ? apps : NO_APPS;
|
|
347
|
+
const hues = (0, react_2.useMemo)(() => (0, AppMark_1.assignAppHues)(appList, (0, chartColorsFromTheme_1.chartMode)(colors)), [appList, colors]);
|
|
348
|
+
const showApps = appList.some((app) => app.id !== currentAppId) &&
|
|
349
|
+
(appsVisibility === "always" || !isDesktop);
|
|
350
|
+
const handleAppSelect = (app, event) => {
|
|
351
|
+
onAppSelect === null || onAppSelect === void 0 ? void 0 : onAppSelect(app, event);
|
|
352
|
+
if (!event.defaultPrevented)
|
|
353
|
+
onClose();
|
|
354
|
+
};
|
|
355
|
+
const appsBlock = showApps ? ((0, jsx_runtime_1.jsx)(launcherParts_1.AppsSection, { apps: appList, hues: hues, currentAppId: currentAppId, heading: labels.apps, onSelect: handleAppSelect })) : null;
|
|
356
|
+
const listboxId = `${baseId}-results`;
|
|
357
|
+
const optionId = (index) => `${baseId}-opt-${index}`;
|
|
358
|
+
const togglePinFor = (id) => onTogglePin ? () => onTogglePin(id) : undefined;
|
|
359
|
+
const hasResults = results.length > 0;
|
|
360
|
+
const trimmed = query.trim();
|
|
361
|
+
const railFrequent = frequentItems
|
|
362
|
+
.filter(({ screen }) => !pinned.has(screen.id))
|
|
363
|
+
.slice(0, frequentLimit);
|
|
364
|
+
const rail = ((0, jsx_runtime_1.jsxs)(Rail, { label: labels.quickAccess, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "section", direction: "column", gap: "6px", children: [(0, jsx_runtime_1.jsx)(launcherParts_1.SectionHeader, { label: labels.pinned, meta: `(${pinnedItems.length})`, isRail: true }), pinnedItems.length === 0 ? ((0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "13px", color: colors.textMuted, px: "4px", children: labels.pinnedEmpty })) : ((0, jsx_runtime_1.jsx)(react_1.Box, { display: "grid", gridTemplateColumns: "repeat(2, minmax(0, 1fr))", gap: "6px", children: pinnedItems.map(({ screen, module }) => ((0, jsx_runtime_1.jsx)(launcherParts_1.PinnedTile, { screen: screen, module: module, link: link, labels: labels, onTogglePin: togglePinFor(screen.id) }, screen.id))) }))] }), railFrequent.length ? ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "section", direction: "column", gap: "2px", children: [(0, jsx_runtime_1.jsx)(launcherParts_1.SectionHeader, { label: labels.frequent, isRail: true, hasCount: false }), railFrequent.map(({ screen, module }) => ((0, jsx_runtime_1.jsx)(launcherParts_1.RailRow, { screen: screen, module: module, link: link, isStrong: true, pinId: onTogglePin ? screen.id : undefined, trailing: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [onTogglePin ? ((0, jsx_runtime_1.jsx)(launcherParts_1.PinTextButton, { label: labels.pin(screen.label), text: labels.pinShort, onToggle: () => onTogglePin(screen.id) })) : null, (0, jsx_runtime_1.jsx)(NewTabLink_1.default, { href: screen.href, label: labels.openInNewTab(screen.label), linkAs: link.linkAs })] }) }, screen.id)))] })) : null, recentItems.length ? ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "section", direction: "column", gap: "2px", children: [(0, jsx_runtime_1.jsx)(launcherParts_1.SectionHeader, { label: labels.recent, isRail: true, hasCount: false }), recentItems.map(({ screen, module }) => ((0, jsx_runtime_1.jsx)(launcherParts_1.RailRow, { screen: screen, module: module, link: link, trailing: (0, jsx_runtime_1.jsx)(NewTabLink_1.default, { href: screen.href, label: labels.openInNewTab(screen.label), linkAs: link.linkAs }) }, screen.id)))] })) : null, appsBlock] }));
|
|
365
|
+
const browsePane = sections.length === 0 ? ((0, jsx_runtime_1.jsx)(EmptyState_1.default, { title: labels.empty, titleAs: "h3", size: "sm" })) : (sections.map((section) => ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "section", "aria-label": section.label, direction: "column", gap: "3px", children: [(0, jsx_runtime_1.jsx)(launcherParts_1.SectionHeader, { label: section.label, meta: `(${section.modules.length})` }), (0, jsx_runtime_1.jsx)(ItemGrid, { children: section.modules.map((module) => {
|
|
366
|
+
const pinId = module.screens[0].id;
|
|
367
|
+
return ((0, jsx_runtime_1.jsx)(launcherParts_1.ModuleRow, { module: module, keyScreens: module.screens
|
|
368
|
+
.filter((s) => s.isKey && s.href !== module.href)
|
|
369
|
+
.slice(0, keyScreenLimit), isPinned: pinned.has(pinId), onTogglePin: togglePinFor(pinId), pinId: pinId, labels: labels, link: link }, module.id));
|
|
370
|
+
}) })] }, section.id))));
|
|
371
|
+
const pickSuggestion = (value) => {
|
|
372
|
+
var _a;
|
|
373
|
+
setQuery(value);
|
|
374
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
375
|
+
};
|
|
376
|
+
const noResults = ((0, jsx_runtime_1.jsxs)(react_1.Flex, { direction: "column", align: "center", gap: "12px", py: "48px", px: "20px", textAlign: "center", role: "status", children: [(0, jsx_runtime_1.jsx)(react_1.Flex, { boxSize: "40px", borderRadius: "12px", bg: colors.backgroundColor.subtle, color: colors.textMuted, align: "center", justify: "center", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 18, strokeWidth: 2, "aria-hidden": true }) }), (0, jsx_runtime_1.jsxs)(react_1.Flex, { direction: "column", gap: "3px", children: [(0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "14px", fontWeight: 600, color: colors.text[900], children: isSearching ? labels.searching : labels.noResults(trimmed) }), isSearching ? null : ((0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "12.5px", color: colors.textMuted, children: (suggestions === null || suggestions === void 0 ? void 0 : suggestions.length)
|
|
377
|
+
? labels.suggestionsHint
|
|
378
|
+
: labels.noResultsHint }))] }), !isSearching && (suggestions === null || suggestions === void 0 ? void 0 : suggestions.length) ? ((0, jsx_runtime_1.jsx)(react_1.Flex, { wrap: "wrap", justify: "center", gap: "6px", children: suggestions.map((suggestion) => ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "button", type: "button", onClick: () => pickSuggestion(suggestion), h: "28px", px: "11px", borderRadius: "full", borderWidth: "1px", borderColor: colors.boxborder[200], bg: colors.surfaceRaised, fontSize: "12.5px", fontWeight: 500, color: colors.text[900], transition: (0, motion_1.transitionFor)(["background-color"]), _hover: { bg: (0, rowStates_1.listRowBackgrounds)(colors).active }, sx: (0, focusRing_styles_1.focusVisibleStyles)(colors), children: suggestion }, suggestion))) })) : null] }));
|
|
379
|
+
const searchPane = hasResults ? ((0, jsx_runtime_1.jsx)(react_1.Flex, { role: "listbox", id: listboxId, "aria-label": labels.title, "aria-busy": isSearching || undefined, direction: "column", gap: "14px", children: groups.map((group, groupIndex) => ((0, jsx_runtime_1.jsxs)(react_1.Flex, { role: "group", "aria-label": group.meta ? `${group.label}, ${group.meta}` : group.label, direction: "column", gap: "3px", children: [(0, jsx_runtime_1.jsx)(launcherParts_1.SectionHeader, { label: group.label, meta: group.meta ? `(${group.meta})` : undefined, isDecorative: true }), (0, jsx_runtime_1.jsx)(ItemGrid, { children: group.items.map((item, itemIndex) => {
|
|
380
|
+
const index = groupStarts[groupIndex] + itemIndex;
|
|
381
|
+
return ((0, jsx_runtime_1.jsx)(SearchRow, { item: item, query: query, isActive: index === activeIndex, link: link, labels: labels, optionId: optionId(index), isPinned: item.screenId ? pinned.has(item.screenId) : false, canPin: Boolean(onTogglePin) && item.kind === "module", onPin: () => item.screenId && (onTogglePin === null || onTogglePin === void 0 ? void 0 : onTogglePin(item.screenId)), onOpen: () => open(item, false), onHover: () => setActiveIndex(index) }, item.id));
|
|
382
|
+
}) })] }, group.id))) })) : (noResults);
|
|
383
|
+
const body = isLoading ? ((0, jsx_runtime_1.jsx)(SkeletonLayout, { label: labels.loading })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Pane, { children: isSearchView ? searchPane : browsePane }), rail] }));
|
|
384
|
+
const pill = (0, filledSurface_1.resolveFilledSurface)(colors.primary, colors);
|
|
385
|
+
return ((0, jsx_runtime_1.jsxs)(Drawer_1.default, { isOpen: isOpen, onClose: onClose, placement: "left", size: "full", initialFocusRef: inputRef, hideCloseButton: true, contentProps: {
|
|
386
|
+
maxW: { base: "100vw", lg: `min(${maxWidth}, calc(100vw - 56px))` },
|
|
387
|
+
h: { base: "100%", lg: "auto" },
|
|
388
|
+
maxH: "100%",
|
|
389
|
+
bg: colors.surfaceRaised,
|
|
390
|
+
borderBottomRightRadius: { base: 0, lg: "16px" },
|
|
391
|
+
boxShadow: colors.boxShadow.default,
|
|
392
|
+
overflow: "hidden",
|
|
393
|
+
...{ "data-testid": testId },
|
|
394
|
+
}, children: [(0, jsx_runtime_1.jsx)(Drawer_1.DrawerHeader, { p: 0, flexShrink: 0, children: (0, jsx_runtime_1.jsxs)(react_1.Flex, { align: "center", gap: "12px", h: "60px", pl: "18px", pr: "12px", children: [(0, jsx_runtime_1.jsxs)(react_1.Heading, { as: "h2", display: "inline-flex", alignItems: "center", gap: "7px", h: "32px", pl: "9px", pr: "11px", borderRadius: "9px", bg: pill.fill, color: pill.color, fontSize: "13px", fontWeight: 600, flexShrink: 0, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Compass, { size: 16, strokeWidth: 2, "aria-hidden": true }), labels.title] }), (0, jsx_runtime_1.jsx)(SearchField, { inputRef: inputRef, value: query, onChange: setQuery, onKeyDown: onInputKeyDown, labels: labels, hint: searchHint, listboxId: listboxId, activeId: isSearchView && hasResults ? optionId(activeIndex) : undefined, isExpanded: isSearchView, hasResults: hasResults }), (0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": labels.close, icon: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 18, "aria-hidden": true }), variant: "ghost", boxSize: "34px", minW: "34px", borderRadius: "9px", color: colors.textMuted, ml: "auto", onClick: onClose })] }) }), (0, jsx_runtime_1.jsx)(Drawer_1.DrawerBody, { p: 0, display: "flex", flexDirection: { base: "column", lg: "row" }, overflowY: { base: "auto", lg: "hidden" }, minH: 0, children: (0, jsx_runtime_1.jsx)(react_1.Flex, { ref: bodyRef, onKeyDown: onBodyKeyDown, direction: { base: "column", lg: "row" }, flex: 1, minH: 0, minW: 0, children: body }) }), (0, jsx_runtime_1.jsxs)(Drawer_1.DrawerFooter, { p: 0, minH: "32px", flexShrink: 0, px: "18px", py: "6px", gap: "6px 14px", flexWrap: "wrap", bg: colors.backgroundColor.subtle, fontSize: "11.5px", color: colors.textMuted, whiteSpace: "nowrap", justifyContent: "flex-start", children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { gap: "6px 14px", align: "center", wrap: "wrap", "aria-hidden": true, children: [openShortcutHint ? ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: openShortcutHint }), labels.hintOpenMenu] })) : null, (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: "\u2191\u2193\u2190\u2192" }), labels.hintMove] }), (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: "\u21B5" }), labels.hintOpen] }), (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: `${modKeyLabel !== null && modKeyLabel !== void 0 ? modKeyLabel : (isApplePlatform() ? "⌘" : "Ctrl")}↵` }), labels.hintNewTab] }), onTogglePin ? ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: "P" }), labels.hintPin] })) : null, (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: "esc" }), labels.hintClose] })] }), (0, jsx_runtime_1.jsx)(react_1.Text, { as: "span", ml: "auto", children: labels.footerNote })] })] }));
|
|
395
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { ElementType, KeyboardEvent, MouseEvent } from "react";
|
|
2
|
+
import type { AppOption } from "../AppSwitcher/AppSwitcherProps";
|
|
3
|
+
/** Rendered as `<Icon size={16} aria-hidden />`, so any lucide-compatible component works. */
|
|
4
|
+
export type LauncherIcon = ElementType;
|
|
5
|
+
/** One destination the launcher can open; screens are what pins, Frequent and Recent refer to. */
|
|
6
|
+
export type LauncherScreen = {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
href: string;
|
|
10
|
+
icon?: LauncherIcon;
|
|
11
|
+
badge?: number | string;
|
|
12
|
+
keywords?: readonly string[];
|
|
13
|
+
/** Listed on the module's second line (first `keyScreenLimit` of them). */
|
|
14
|
+
isKey?: boolean;
|
|
15
|
+
};
|
|
16
|
+
/** A group of screens shown as one row in the browse view. */
|
|
17
|
+
export type LauncherModule = {
|
|
18
|
+
id: string;
|
|
19
|
+
label: string;
|
|
20
|
+
/** Normally its first visible screen's href. */
|
|
21
|
+
href: string;
|
|
22
|
+
icon?: LauncherIcon;
|
|
23
|
+
badge?: number | string;
|
|
24
|
+
/** Shows a small "New" tag after the name. */
|
|
25
|
+
isNew?: boolean;
|
|
26
|
+
keywords?: readonly string[];
|
|
27
|
+
/** Pinning the module pins `screens[0]`; a module with no screens is not rendered. */
|
|
28
|
+
screens: readonly LauncherScreen[];
|
|
29
|
+
};
|
|
30
|
+
/** A labelled run of modules; a section with no renderable module is dropped. */
|
|
31
|
+
export type LauncherSection = {
|
|
32
|
+
id: string;
|
|
33
|
+
label: string;
|
|
34
|
+
modules: readonly LauncherModule[];
|
|
35
|
+
};
|
|
36
|
+
/** A search row that is not a screen: an action (`run`) or an entity result (`href`). */
|
|
37
|
+
export type LauncherSearchItem = {
|
|
38
|
+
id: string;
|
|
39
|
+
label: string;
|
|
40
|
+
/** Second line, e.g. the module a result belongs to. */
|
|
41
|
+
context?: string;
|
|
42
|
+
icon?: LauncherIcon;
|
|
43
|
+
/** Extra terms the query may match; never rendered. */
|
|
44
|
+
keywords?: string;
|
|
45
|
+
hint?: string;
|
|
46
|
+
href?: string;
|
|
47
|
+
run?: () => void;
|
|
48
|
+
};
|
|
49
|
+
/** A titled batch of remote results, rendered after Screens in the order given. */
|
|
50
|
+
export type LauncherResultGroup = {
|
|
51
|
+
id: string;
|
|
52
|
+
label: string;
|
|
53
|
+
items: readonly LauncherSearchItem[];
|
|
54
|
+
};
|
|
55
|
+
/** Every visible string and accessible name; pass any subset, the rest fall back to English. */
|
|
56
|
+
export type LauncherLabels = {
|
|
57
|
+
title: string;
|
|
58
|
+
searchPlaceholder: string;
|
|
59
|
+
clearSearch: string;
|
|
60
|
+
close: string;
|
|
61
|
+
pinned: string;
|
|
62
|
+
pinnedEmpty: string;
|
|
63
|
+
frequent: string;
|
|
64
|
+
recent: string;
|
|
65
|
+
screensGroup: string;
|
|
66
|
+
actionsGroup: string;
|
|
67
|
+
pin: (label: string) => string;
|
|
68
|
+
unpin: (label: string) => string;
|
|
69
|
+
openInNewTab: (label: string) => string;
|
|
70
|
+
noResults: (query: string) => string;
|
|
71
|
+
noResultsHint: string;
|
|
72
|
+
searching: string;
|
|
73
|
+
/** Accessible name of the skeleton region while `isLoading`. */
|
|
74
|
+
loading: string;
|
|
75
|
+
/** Heading of the empty state shown when `sections` has nothing to render. */
|
|
76
|
+
empty: string;
|
|
77
|
+
/** Footer hint after the ↑↓←→ key cap. */
|
|
78
|
+
hintMove: string;
|
|
79
|
+
/** Footer hint after the ↵ key cap. */
|
|
80
|
+
hintOpen: string;
|
|
81
|
+
/** Footer hint after the mod+↵ key cap. */
|
|
82
|
+
hintNewTab: string;
|
|
83
|
+
/** Footer hint after the P key cap. */
|
|
84
|
+
hintPin: string;
|
|
85
|
+
/** Footer hint after the esc key cap. */
|
|
86
|
+
hintClose: string;
|
|
87
|
+
/** Footer hint after `openShortcutHint`; only rendered when that prop is set. */
|
|
88
|
+
hintOpenMenu: string;
|
|
89
|
+
/** Heading and list name of the apps section. */
|
|
90
|
+
apps: string;
|
|
91
|
+
/** Per-section count shown while searching, e.g. "3 found". */
|
|
92
|
+
found: (count: number) => string;
|
|
93
|
+
/** Accessible name of the right-hand quick access rail. */
|
|
94
|
+
quickAccess: string;
|
|
95
|
+
/** Tag after a module marked `isNew`. */
|
|
96
|
+
newBadge: string;
|
|
97
|
+
/** Noun used in a module's key-screens group name, e.g. "Leave screens". */
|
|
98
|
+
screens: string;
|
|
99
|
+
/** Text on the Pin button of Frequently visited rows. */
|
|
100
|
+
pinShort: string;
|
|
101
|
+
/** Right-hand footer note. */
|
|
102
|
+
footerNote: string;
|
|
103
|
+
/** Line above the suggestion chips of the no-results state. */
|
|
104
|
+
suggestionsHint: string;
|
|
105
|
+
/** Tooltip on an unpinned star. */
|
|
106
|
+
pinTip: string;
|
|
107
|
+
/** Tooltip on a pinned star. */
|
|
108
|
+
unpinTip: string;
|
|
109
|
+
};
|
|
110
|
+
/** Passed to `onNavigate`; `newTab` is true for mod+Enter. */
|
|
111
|
+
export type LauncherNavigateOptions = {
|
|
112
|
+
newTab: boolean;
|
|
113
|
+
};
|
|
114
|
+
/** Full-height navigation launcher: browse every screen, search, pins, Frequent, Recent and optionally the suite's apps. */
|
|
115
|
+
export interface NavLauncherProps {
|
|
116
|
+
isOpen: boolean;
|
|
117
|
+
onClose: () => void;
|
|
118
|
+
/** Already permission-filtered by the app. Empty sections/modules are dropped, never rendered as gaps. */
|
|
119
|
+
sections: readonly LauncherSection[];
|
|
120
|
+
/** Screen ids in display order; ids not in `sections` are ignored silently. */
|
|
121
|
+
pinnedIds: readonly string[];
|
|
122
|
+
frequentIds: readonly string[];
|
|
123
|
+
recentIds: readonly string[];
|
|
124
|
+
/** Omit to hide every pin toggle (read-only launcher). */
|
|
125
|
+
onTogglePin?: (screenId: string) => void;
|
|
126
|
+
/** Keyboard opens only (Enter / mod+Enter); pointer clicks follow the rendered links natively. */
|
|
127
|
+
onNavigate: (href: string, options: LauncherNavigateOptions) => void;
|
|
128
|
+
/** Anchor-like component receiving `href`, `target`, `rel`, `onClick`, `ref`. Defaults to `"a"`. */
|
|
129
|
+
linkAs?: ElementType;
|
|
130
|
+
actions?: readonly LauncherSearchItem[];
|
|
131
|
+
/** Remote results, already filtered by the app; rendered after Screens in the given order. */
|
|
132
|
+
extraResults?: readonly LauncherResultGroup[];
|
|
133
|
+
/** Controlled query; omit for uncontrolled. Reset to "" on close either way. */
|
|
134
|
+
query?: string;
|
|
135
|
+
/** Fires on every change, including the reset on close. */
|
|
136
|
+
onQueryChange?: (query: string) => void;
|
|
137
|
+
/** Remote search in flight: shows the searching state when nothing local matched. */
|
|
138
|
+
isSearching?: boolean;
|
|
139
|
+
/** Sections not known yet (access loading): skeleton browse view. */
|
|
140
|
+
isLoading?: boolean;
|
|
141
|
+
/** Defaults to 3. */
|
|
142
|
+
keyScreenLimit?: number;
|
|
143
|
+
/** Most Frequently visited rows shown (pinned ones are skipped); defaults to 4. */
|
|
144
|
+
frequentLimit?: number;
|
|
145
|
+
/** Query chips offered by the no-results state; clicking one sets the query. */
|
|
146
|
+
suggestions?: readonly string[];
|
|
147
|
+
/** Key cap shown at the end of the search field, e.g. "/" when the app opens the launcher with it. */
|
|
148
|
+
searchHint?: string;
|
|
149
|
+
/** Chord that opens the launcher in the host app, e.g. "⌘K or /"; shown as the first footer hint. Omit when the app does not own one. */
|
|
150
|
+
openShortcutHint?: string;
|
|
151
|
+
/** Printed on the mod+Enter hint; defaults to "⌘" on Apple platforms, "Ctrl" elsewhere. */
|
|
152
|
+
modKeyLabel?: string;
|
|
153
|
+
/** Panel width at `lg`+; always full width below `lg`. Defaults to "1180px". */
|
|
154
|
+
maxWidth?: string;
|
|
155
|
+
labels?: Partial<LauncherLabels>;
|
|
156
|
+
/** Other Pixelize apps, shown as an "Apps" section at the bottom of the right rail. Same shape as AppSwitcher. */
|
|
157
|
+
apps?: AppOption[];
|
|
158
|
+
/** The app this UI runs inside: its row is marked and never a link. */
|
|
159
|
+
currentAppId?: string;
|
|
160
|
+
/** Same contract as AppSwitcher.onSelect; preventDefault keeps the app's own routing. */
|
|
161
|
+
onAppSelect?: (app: AppOption, event: MouseEvent | KeyboardEvent) => void;
|
|
162
|
+
/** Default "apps-on-compact": the section shows only below `md`, where the 9-dot is usually hidden. */
|
|
163
|
+
appsVisibility?: "always" | "apps-on-compact";
|
|
164
|
+
"data-testid"?: string;
|
|
165
|
+
}
|
|
166
|
+
/** Props for the star pin toggle used on launcher rows and reusable on a landing page; shows only while its row is hovered or focused. */
|
|
167
|
+
export interface PinToggleProps {
|
|
168
|
+
isPinned: boolean;
|
|
169
|
+
/** Accessible name and tooltip, e.g. "Pin Leave". */
|
|
170
|
+
label: string;
|
|
171
|
+
onToggle: () => void;
|
|
172
|
+
/** Tooltip text; defaults to `label`. */
|
|
173
|
+
title?: string;
|
|
174
|
+
/** Star size in px; defaults to 13. */
|
|
175
|
+
size?: number;
|
|
176
|
+
/** Pass -1 where the row itself is the tab stop. */
|
|
177
|
+
tabIndex?: number;
|
|
178
|
+
}
|
|
179
|
+
/** Props for the small "open in new tab" anchor beside a launcher row. */
|
|
180
|
+
export interface NewTabLinkProps {
|
|
181
|
+
href: string;
|
|
182
|
+
/** Accessible name and tooltip, e.g. "Open Leave in a new tab". */
|
|
183
|
+
label: string;
|
|
184
|
+
/** Anchor-like component receiving `href`, `target`, `rel`; defaults to `"a"`. */
|
|
185
|
+
linkAs?: ElementType;
|
|
186
|
+
tabIndex?: number;
|
|
187
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { NewTabLinkProps } from "./NavLauncherProps";
|
|
2
|
+
/** "Open in a new tab" anchor; visible on row hover, focus-within or its own focus only. */
|
|
3
|
+
export default function NewTabLink({ href, label, linkAs, tabIndex }: NewTabLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = NewTabLink;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("@chakra-ui/react");
|
|
6
|
+
const lucide_react_1 = require("lucide-react");
|
|
7
|
+
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
8
|
+
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
9
|
+
const revealStyles_1 = require("./revealStyles");
|
|
10
|
+
/** "Open in a new tab" anchor; visible on row hover, focus-within or its own focus only. */
|
|
11
|
+
function NewTabLink({ href, label, linkAs = "a", tabIndex }) {
|
|
12
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
13
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Box, { as: linkAs, href: href, target: "_blank", rel: "noopener noreferrer", "aria-label": label, title: label, tabIndex: tabIndex, display: "inline-flex", alignItems: "center", justifyContent: "center", w: "22px", h: "22px", flexShrink: 0, borderRadius: "md", color: colors.textMuted, opacity: 0, transition: (0, motion_1.transitionFor)(["opacity", "background-color"]), sx: (0, revealStyles_1.revealStyles)(colors), _hover: { bg: colors.backgroundColor.subtle }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpRight, { size: 13, "aria-hidden": true }) }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { PinToggleProps } from "./NavLauncherProps";
|
|
2
|
+
/** Star toggle: outline when unpinned, filled in the accent when pinned; appears on row hover or focus and never follows the row link. */
|
|
3
|
+
export default function PinToggle({ isPinned, label, onToggle, title, size, tabIndex }: PinToggleProps): import("react/jsx-runtime").JSX.Element;
|