pixelize-design-library 2.4.2-beta.60 → 2.4.2-beta.61
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 +38 -10
- package/COMPONENT-GOTCHAS.md +41 -4
- package/DESIGN-GUIDE.md +9 -1
- package/dist/Components/NavLauncher/LauncherTabs.d.ts +18 -0
- package/dist/Components/NavLauncher/LauncherTabs.js +64 -0
- package/dist/Components/NavLauncher/NavLauncher.d.ts +1 -1
- package/dist/Components/NavLauncher/NavLauncher.js +203 -53
- package/dist/Components/NavLauncher/NavLauncherProps.d.ts +23 -4
- package/dist/Components/NavLauncher/NewTabLink.d.ts +2 -2
- package/dist/Components/NavLauncher/NewTabLink.js +7 -3
- package/dist/Components/NavLauncher/launcherParts.d.ts +4 -1
- package/dist/Components/NavLauncher/launcherParts.js +16 -8
- package/dist/Components/NavLauncher/resultNavigation.d.ts +3 -0
- package/dist/Components/NavLauncher/resultNavigation.js +65 -0
- package/dist/Components/NavLauncher/revealStyles.d.ts +1 -1
- package/dist/Components/NavLauncher/revealStyles.js +2 -2
- package/dist/Components/NavigationBar/NavigationBar.js +2 -1
- package/dist/Components/NavigationBar/NavigationBarProps.d.ts +1 -0
- package/dist/Components/OrgSwitcher/OrgSwitcher.js +18 -6
- package/dist/Components/OrgSwitcher/OrgSwitcherProps.d.ts +4 -0
- package/dist/esm/Components/NavLauncher/LauncherTabs.js +58 -0
- package/dist/esm/Components/NavLauncher/NavLauncher.js +204 -54
- package/dist/esm/Components/NavLauncher/NewTabLink.js +4 -3
- package/dist/esm/Components/NavLauncher/launcherParts.js +15 -8
- package/dist/esm/Components/NavLauncher/resultNavigation.js +61 -0
- package/dist/esm/Components/NavLauncher/revealStyles.js +2 -2
- package/dist/esm/Components/NavigationBar/NavigationBar.js +2 -1
- package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +16 -7
- package/dist/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -43,11 +43,11 @@ const lucide_react_1 = require("lucide-react");
|
|
|
43
43
|
const react_2 = require("react");
|
|
44
44
|
const useMatchMedia_1 = require("../../Hooks/useMatchMedia");
|
|
45
45
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
46
|
+
const motion_1 = require("../../Theme/chakra/motion");
|
|
46
47
|
const chartColorsFromTheme_1 = require("../../Theme/tokens/builders/chartColorsFromTheme");
|
|
47
48
|
const placeholderInk_1 = require("../../Theme/tokens/builders/placeholderInk");
|
|
48
|
-
const filledSurface_1 = require("../../Theme/tokens/builders/filledSurface");
|
|
49
49
|
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
50
|
-
const
|
|
50
|
+
const motion_2 = require("../../Theme/tokens/primitives/motion");
|
|
51
51
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
52
52
|
const AppMark_1 = require("../AppSwitcher/AppMark");
|
|
53
53
|
const Drawer_1 = __importStar(require("../Drawer/Drawer"));
|
|
@@ -58,11 +58,14 @@ const useIsTruncated_1 = require("../../Hooks/useIsTruncated");
|
|
|
58
58
|
const AppSwitcherProps_1 = require("../AppSwitcher/AppSwitcherProps");
|
|
59
59
|
const Skeletons_1 = __importDefault(require("../Skeletons/Skeletons"));
|
|
60
60
|
const launcherParts_1 = require("./launcherParts");
|
|
61
|
+
const LauncherTabs_1 = require("./LauncherTabs");
|
|
61
62
|
const match_1 = require("./match");
|
|
62
63
|
const NewTabLink_1 = __importDefault(require("./NewTabLink"));
|
|
63
64
|
const PinToggle_1 = __importDefault(require("./PinToggle"));
|
|
65
|
+
const resultNavigation_1 = require("./resultNavigation");
|
|
64
66
|
const NO_APPS = [];
|
|
65
67
|
const MD_QUERY = "(min-width: 48em)";
|
|
68
|
+
const LG_QUERY = "(min-width: 62em)";
|
|
66
69
|
const XL_QUERY = "(min-width: 80em)";
|
|
67
70
|
const CELL_SELECTOR = "[data-nav-cell]";
|
|
68
71
|
const RAIL_WIDTH = "clamp(20rem, 30%, 26rem)";
|
|
@@ -70,6 +73,19 @@ const PINNED_COLUMNS = 2;
|
|
|
70
73
|
const ENTER_CHIP_WIDTH = 50;
|
|
71
74
|
const ARROW_KEYS = ["↑", "↓", "←", "→"];
|
|
72
75
|
const ENTER_KEY = (0, AppSwitcherProps_1.shortcutKeys)("enter")[0];
|
|
76
|
+
const MOD_KEY = (0, AppSwitcherProps_1.shortcutKeys)("mod+enter")[0];
|
|
77
|
+
const RESULT_KEYS = [
|
|
78
|
+
"ArrowUp",
|
|
79
|
+
"ArrowDown",
|
|
80
|
+
"ArrowLeft",
|
|
81
|
+
"ArrowRight",
|
|
82
|
+
"Home",
|
|
83
|
+
"End",
|
|
84
|
+
"PageUp",
|
|
85
|
+
"PageDown",
|
|
86
|
+
];
|
|
87
|
+
const TEXT_TARGET = "input, textarea, select, [contenteditable=true]";
|
|
88
|
+
const ROW_HEIGHT = 46;
|
|
73
89
|
const DEFAULT_LABELS = {
|
|
74
90
|
title: "Menu",
|
|
75
91
|
searchPlaceholder: "Search menus and screens",
|
|
@@ -95,6 +111,13 @@ const DEFAULT_LABELS = {
|
|
|
95
111
|
hintPin: "pin",
|
|
96
112
|
hintClose: "close",
|
|
97
113
|
hintOpenMenu: "open",
|
|
114
|
+
hintClickNewTab: "+ click for new tab",
|
|
115
|
+
newTabTip: "Open in new tab",
|
|
116
|
+
resultsCount: (count) => `${count} ${count === 1 ? "result" : "results"}`,
|
|
117
|
+
tabsLabel: "Menu sections",
|
|
118
|
+
tabMenu: "Menu",
|
|
119
|
+
tabQuickAccess: "Quick access",
|
|
120
|
+
tabApps: "Apps",
|
|
98
121
|
apps: "Your apps",
|
|
99
122
|
found: (count) => `${count} found`,
|
|
100
123
|
pinTip: "Pin to quick access",
|
|
@@ -131,19 +154,19 @@ const GRID_COLUMNS = {
|
|
|
131
154
|
function ItemGrid({ children, columns, }) {
|
|
132
155
|
return ((0, jsx_runtime_1.jsx)(react_1.Box, { "data-nav-grid": true, "data-nav-columns": columns, display: "grid", gridTemplateColumns: GRID_COLUMNS, gap: "1px 8px", alignItems: "start", children: children }));
|
|
133
156
|
}
|
|
134
|
-
function Pane({ children }) {
|
|
135
|
-
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 }));
|
|
157
|
+
function Pane({ children, scrolls = false, }) {
|
|
158
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Flex, { "data-scroll-region": scrolls || undefined, direction: "column", gap: "14px", flex: 1, minW: 0, px: "18px", pt: "12px", pb: "16px", overflowY: { base: "visible", lg: "auto" }, children: children }));
|
|
136
159
|
}
|
|
137
|
-
function Rail({ label, children }) {
|
|
160
|
+
function Rail({ label, children, scrolls = false, }) {
|
|
138
161
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
139
|
-
return ((0, jsx_runtime_1.jsx)(react_1.Flex, { as: "aside", "aria-label": label, direction: "column", gap: "16px", w: { base: "100%", lg: RAIL_WIDTH }, 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 }));
|
|
162
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Flex, { "data-scroll-region": scrolls || undefined, as: "aside", "aria-label": label, direction: "column", gap: "16px", w: { base: "100%", lg: RAIL_WIDTH }, 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 }));
|
|
140
163
|
}
|
|
141
164
|
function SkeletonLayout({ label }) {
|
|
142
165
|
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))) })] }));
|
|
143
166
|
}
|
|
144
167
|
function SearchField({ inputRef, value, onChange, onKeyDown, labels, hint, listboxId, activeId, isExpanded, hasResults, }) {
|
|
145
168
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
146
|
-
return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { flex: 1,
|
|
169
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { flex: 1, minW: 0, h: { base: "40px", md: "44px" }, align: "center", gap: "10px", pl: "14px", pr: "10px", borderRadius: { base: "10px", md: "12px" }, 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: 18, 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: "100%", border: 0, outline: "none", bg: "transparent", fontSize: "16px", 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: () => {
|
|
147
170
|
var _a;
|
|
148
171
|
onChange("");
|
|
149
172
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
@@ -151,6 +174,7 @@ function SearchField({ inputRef, value, onChange, onKeyDown, labels, hint, listb
|
|
|
151
174
|
}
|
|
152
175
|
function SearchRow({ item, query, isActive, link, labels, optionId, isPinned, canPin, onPin, onOpen, onHover, }) {
|
|
153
176
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
177
|
+
const isTouch = (0, launcherParts_1.useTouchDensity)();
|
|
154
178
|
const isScreen = item.kind === "screen";
|
|
155
179
|
const hasStar = Boolean(item.screenId) && canPin;
|
|
156
180
|
const base = item.badge ? 24 : 0;
|
|
@@ -161,7 +185,7 @@ function SearchRow({ item, query, isActive, link, labels, optionId, isPinned, ca
|
|
|
161
185
|
const titleRef = title.ref;
|
|
162
186
|
const contextRef = context.ref;
|
|
163
187
|
const tipLabel = isScreen || !item.context ? fullLabel : `${fullLabel} · ${item.context}`;
|
|
164
|
-
return ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: tipLabel, placement: "top-start", isDisabled: !title.isTruncated && !context.isTruncated, children: (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", transition: (0,
|
|
188
|
+
return ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: tipLabel, placement: "top-start", isDisabled: !title.isTruncated && !context.isTruncated, children: (0, jsx_runtime_1.jsxs)(react_1.Flex, { role: "presentation", "data-group": true, "data-reveal": isActive, position: "relative", align: "flex-start", gap: "10px", minH: isTouch ? "44px" : "34px", pl: "8px", pr: "4px", pt: isTouch ? "12px" : "7px", pb: isTouch ? "8px" : "5px", borderRadius: "8px", transition: (0, motion_2.transitionFor)(["background-color"]), onMouseMove: onHover, sx: isActive
|
|
165
189
|
? { ...launcherParts_1.ROW_CONTAINER, ...(0, launcherParts_1.activeRowSurface)(colors) }
|
|
166
190
|
: launcherParts_1.ROW_CONTAINER, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { id: optionId, role: "option", "aria-selected": isActive, "data-active": isActive, flex: 1, minW: 0, align: "flex-start", gap: "10px", cursor: "pointer", onClick: () => {
|
|
167
191
|
if (!item.href)
|
|
@@ -170,20 +194,23 @@ function SearchRow({ item, query, isActive, link, labels, optionId, isPinned, ca
|
|
|
170
194
|
display: "none",
|
|
171
195
|
[launcherParts_1.WIDE_ROW]: { display: "inline-flex" },
|
|
172
196
|
"@media (hover: none)": { display: "none" },
|
|
173
|
-
}, children: (0, jsx_runtime_1.jsx)(Kbd_1.KbdShortcut, { shortcut: "enter", size: "xs" }) })) : null, (0, jsx_runtime_1.jsx)(launcherParts_1.CountBadge, { value: item.badge }), 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] }) }));
|
|
197
|
+
}, children: (0, jsx_runtime_1.jsx)(Kbd_1.KbdShortcut, { shortcut: "enter", size: "xs" }) })) : null, (0, jsx_runtime_1.jsx)(launcherParts_1.CountBadge, { value: item.badge }), 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), tip: labels.newTabTip, linkAs: link.linkAs, tabIndex: -1 })) : null] }) }), hasStar ? ((0, jsx_runtime_1.jsx)(launcherParts_1.RowTip, { text: isPinned ? labels.unpinTip : labels.pinTip })) : null] }) }));
|
|
174
198
|
}
|
|
175
199
|
/** Left-drawer navigation launcher: browse every module, pins, Frequent, Recent, search and (compact) the suite's apps. */
|
|
176
|
-
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,
|
|
200
|
+
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, modKeyLabel, headerActions, maxWidth = "1080px", labels: labelOverrides, apps, currentAppId, onAppSelect, appsVisibility = "apps-on-compact", "data-testid": testId, }) {
|
|
177
201
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
178
202
|
const { colors } = theme;
|
|
179
203
|
const inputRef = (0, react_2.useRef)(null);
|
|
180
204
|
const bodyRef = (0, react_2.useRef)(null);
|
|
181
205
|
const baseId = (0, react_2.useId)();
|
|
182
206
|
const isDesktop = (0, useMatchMedia_1.useMatchMedia)(MD_QUERY, true);
|
|
207
|
+
const isLg = (0, useMatchMedia_1.useMatchMedia)(LG_QUERY, true);
|
|
183
208
|
const isWide = (0, useMatchMedia_1.useMatchMedia)(XL_QUERY, false);
|
|
184
209
|
const columns = isWide ? 3 : isDesktop ? 2 : 1;
|
|
185
210
|
const [innerQuery, setInnerQuery] = (0, react_2.useState)("");
|
|
186
211
|
const [activeIndex, setActiveIndex] = (0, react_2.useState)(0);
|
|
212
|
+
const [tab, setTab] = (0, react_2.useState)("menu");
|
|
213
|
+
const navigating = (0, react_2.useRef)(false);
|
|
187
214
|
const labels = (0, react_2.useMemo)(() => ({ ...DEFAULT_LABELS, ...labelOverrides }), [labelOverrides]);
|
|
188
215
|
const isControlled = controlledQuery !== undefined;
|
|
189
216
|
const query = isControlled ? controlledQuery : innerQuery;
|
|
@@ -195,10 +222,16 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
195
222
|
const latest = (0, react_2.useRef)({ query, setQuery });
|
|
196
223
|
latest.current = { query, setQuery };
|
|
197
224
|
(0, react_2.useEffect)(() => {
|
|
198
|
-
if (
|
|
225
|
+
if (isOpen)
|
|
226
|
+
return;
|
|
227
|
+
setTab("menu");
|
|
228
|
+
if (latest.current.query !== "")
|
|
199
229
|
latest.current.setQuery("");
|
|
200
230
|
}, [isOpen]);
|
|
201
|
-
(0, react_2.useEffect)(() =>
|
|
231
|
+
(0, react_2.useEffect)(() => {
|
|
232
|
+
navigating.current = false;
|
|
233
|
+
setActiveIndex(0);
|
|
234
|
+
}, [query]);
|
|
202
235
|
const sections = (0, react_2.useMemo)(() => visibleSections(rawSections), [rawSections]);
|
|
203
236
|
const byId = (0, react_2.useMemo)(() => indexScreens(sections), [sections]);
|
|
204
237
|
const pinned = (0, react_2.useMemo)(() => new Set(pinnedIds), [pinnedIds]);
|
|
@@ -225,6 +258,26 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
225
258
|
const groupStarts = (0, react_2.useMemo)(() => groups.map((_, i) => groups.slice(0, i).reduce((n, g) => n + g.items.length, 0)), [groups]);
|
|
226
259
|
const isSearchView = query.trim().length > 0;
|
|
227
260
|
const active = Math.min(activeIndex, Math.max(results.length - 1, 0));
|
|
261
|
+
const appList = apps !== null && apps !== void 0 ? apps : NO_APPS;
|
|
262
|
+
const showApps = appList.some((app) => app.id !== currentAppId) &&
|
|
263
|
+
(appsVisibility === "always" || !isDesktop);
|
|
264
|
+
const railFrequent = frequentItems
|
|
265
|
+
.filter(({ screen }) => !pinned.has(screen.id))
|
|
266
|
+
.slice(0, frequentLimit);
|
|
267
|
+
const hasQuick = pinnedItems.length > 0 || railFrequent.length > 0 || recentItems.length > 0;
|
|
268
|
+
const tabs = (0, react_2.useMemo)(() => [
|
|
269
|
+
{ id: "menu", label: labels.tabMenu },
|
|
270
|
+
...(hasQuick
|
|
271
|
+
? [{ id: "quick", label: labels.tabQuickAccess }]
|
|
272
|
+
: []),
|
|
273
|
+
...(showApps ? [{ id: "apps", label: labels.tabApps }] : []),
|
|
274
|
+
], [hasQuick, showApps, labels]);
|
|
275
|
+
const isTabbed = !isLg && !isSearchView && !isLoading && tabs.length > 1;
|
|
276
|
+
const activeTab = tabs.some((item) => item.id === tab) ? tab : "menu";
|
|
277
|
+
const countText = isSearchView && results.length > 0
|
|
278
|
+
? labels.resultsCount(results.length)
|
|
279
|
+
: "";
|
|
280
|
+
const groupSizes = (0, react_2.useMemo)(() => groups.map((g) => g.items.length), [groups]);
|
|
228
281
|
const link = (0, react_2.useMemo)(() => ({
|
|
229
282
|
linkAs,
|
|
230
283
|
onPlainClick: (event) => {
|
|
@@ -244,14 +297,31 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
244
297
|
onClose();
|
|
245
298
|
onNavigate(item.href, { newTab });
|
|
246
299
|
}, [onClose, onNavigate]);
|
|
300
|
+
const focusField = (shouldSelect = false) => {
|
|
301
|
+
var _a, _b;
|
|
302
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
303
|
+
if (shouldSelect)
|
|
304
|
+
(_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.select();
|
|
305
|
+
};
|
|
306
|
+
const scrollHeight = (from) => {
|
|
307
|
+
var _a, _b, _c, _d, _e;
|
|
308
|
+
return (_e = (_b = (_a = from === null || from === void 0 ? void 0 : from.closest("[data-scroll-region]")) === null || _a === void 0 ? void 0 : _a.clientHeight) !== null && _b !== void 0 ? _b : (_d = (_c = bodyRef.current) === null || _c === void 0 ? void 0 : _c.querySelector("[data-scroll-region]")) === null || _d === void 0 ? void 0 : _d.clientHeight) !== null && _e !== void 0 ? _e : 0;
|
|
309
|
+
};
|
|
310
|
+
const leaveTop = () => {
|
|
311
|
+
var _a;
|
|
312
|
+
const selected = isTabbed
|
|
313
|
+
? document.getElementById((0, LauncherTabs_1.tabId)(baseId, activeTab))
|
|
314
|
+
: null;
|
|
315
|
+
(_a = (selected !== null && selected !== void 0 ? selected : inputRef.current)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
316
|
+
};
|
|
247
317
|
const moveFocus = (delta) => {
|
|
248
|
-
var _a, _b
|
|
318
|
+
var _a, _b;
|
|
249
319
|
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 : []);
|
|
250
320
|
if (items.length === 0)
|
|
251
321
|
return;
|
|
252
322
|
const current = items.indexOf(document.activeElement);
|
|
253
323
|
if (typeof delta === "number" && current + delta < 0) {
|
|
254
|
-
(
|
|
324
|
+
leaveTop();
|
|
255
325
|
return;
|
|
256
326
|
}
|
|
257
327
|
const next = delta === "first"
|
|
@@ -261,6 +331,22 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
261
331
|
: Math.min(items.length - 1, current + delta);
|
|
262
332
|
items[next].focus();
|
|
263
333
|
};
|
|
334
|
+
const pageFocus = (direction) => {
|
|
335
|
+
var _a, _b;
|
|
336
|
+
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 : []);
|
|
337
|
+
const from = document.activeElement;
|
|
338
|
+
const current = items.indexOf(from);
|
|
339
|
+
if (items.length === 0 || current < 0)
|
|
340
|
+
return;
|
|
341
|
+
const height = scrollHeight(from);
|
|
342
|
+
const goal = from.getBoundingClientRect().top + direction * height * 0.85;
|
|
343
|
+
const byDistance = items
|
|
344
|
+
.filter((_, i) => (direction > 0 ? i > current : i < current))
|
|
345
|
+
.sort((a, b) => Math.abs(a.getBoundingClientRect().top - goal) -
|
|
346
|
+
Math.abs(b.getBoundingClientRect().top - goal));
|
|
347
|
+
const fallback = items[Math.min(items.length - 1, Math.max(0, current + direction * 3))];
|
|
348
|
+
(height > 0 && byDistance.length ? byDistance[0] : fallback).focus();
|
|
349
|
+
};
|
|
264
350
|
const onInputKeyDown = (event) => {
|
|
265
351
|
if (!isSearchView) {
|
|
266
352
|
if (event.key === "ArrowDown") {
|
|
@@ -269,20 +355,32 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
269
355
|
}
|
|
270
356
|
return;
|
|
271
357
|
}
|
|
272
|
-
if (results.length === 0)
|
|
358
|
+
if (results.length === 0 || event.nativeEvent.isComposing)
|
|
273
359
|
return;
|
|
274
|
-
if (event.key === "
|
|
275
|
-
event.preventDefault();
|
|
276
|
-
setActiveIndex((active + 1) % results.length);
|
|
277
|
-
}
|
|
278
|
-
else if (event.key === "ArrowUp") {
|
|
279
|
-
event.preventDefault();
|
|
280
|
-
setActiveIndex((active - 1 + results.length) % results.length);
|
|
281
|
-
}
|
|
282
|
-
else if (event.key === "Enter" && !event.nativeEvent.isComposing) {
|
|
360
|
+
if (event.key === "Enter") {
|
|
283
361
|
event.preventDefault();
|
|
284
362
|
open(results[active], event.metaKey || event.ctrlKey);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const hasModifier = event.altKey || event.metaKey || event.ctrlKey || event.shiftKey;
|
|
366
|
+
if (!RESULT_KEYS.includes(event.key) || hasModifier)
|
|
367
|
+
return;
|
|
368
|
+
const input = event.currentTarget;
|
|
369
|
+
const atEnd = input.selectionStart === input.value.length &&
|
|
370
|
+
input.selectionEnd === input.value.length;
|
|
371
|
+
const isSideways = event.key === "ArrowLeft" || event.key === "ArrowRight";
|
|
372
|
+
const isEdit = isSideways || event.key === "Home" || event.key === "End";
|
|
373
|
+
if (isEdit && !navigating.current) {
|
|
374
|
+
const entersGrid = event.key === "ArrowRight" && atEnd && columns > 1;
|
|
375
|
+
if (!entersGrid)
|
|
376
|
+
return;
|
|
285
377
|
}
|
|
378
|
+
if (isSideways && columns === 1)
|
|
379
|
+
return;
|
|
380
|
+
event.preventDefault();
|
|
381
|
+
navigating.current = true;
|
|
382
|
+
const pageRows = Math.max(3, Math.floor(scrollHeight(input) / ROW_HEIGHT) - 1);
|
|
383
|
+
setActiveIndex((0, resultNavigation_1.moveResult)(groupSizes, columns, active, event.key, pageRows));
|
|
286
384
|
};
|
|
287
385
|
const moveInGrid = (event, cell) => {
|
|
288
386
|
var _a, _b, _c, _d;
|
|
@@ -334,27 +432,39 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
334
432
|
return true;
|
|
335
433
|
};
|
|
336
434
|
const onBodyKeyDown = (event) => {
|
|
337
|
-
var _a
|
|
338
|
-
|
|
435
|
+
var _a;
|
|
436
|
+
const target = event.target;
|
|
437
|
+
if (target.closest(TEXT_TARGET))
|
|
339
438
|
return;
|
|
439
|
+
const inBody = Boolean((_a = bodyRef.current) === null || _a === void 0 ? void 0 : _a.contains(target));
|
|
340
440
|
const isArrow = event.key.startsWith("Arrow");
|
|
341
|
-
const cell =
|
|
342
|
-
if (
|
|
441
|
+
const cell = target.closest(CELL_SELECTOR);
|
|
442
|
+
if (inBody &&
|
|
443
|
+
isArrow &&
|
|
444
|
+
cell &&
|
|
445
|
+
!event.altKey &&
|
|
446
|
+
!event.metaKey &&
|
|
447
|
+
!event.ctrlKey) {
|
|
343
448
|
event.preventDefault();
|
|
344
449
|
moveInGrid(event, cell);
|
|
345
450
|
}
|
|
346
|
-
else if (
|
|
451
|
+
else if (inBody &&
|
|
452
|
+
(event.key === "ArrowDown" || event.key === "ArrowUp")) {
|
|
347
453
|
event.preventDefault();
|
|
348
454
|
moveFocus(event.key === "ArrowDown" ? 1 : -1);
|
|
349
455
|
}
|
|
350
|
-
else if (event.key === "Home" || event.key === "End") {
|
|
456
|
+
else if (inBody && (event.key === "Home" || event.key === "End")) {
|
|
351
457
|
event.preventDefault();
|
|
352
458
|
moveFocus(event.key === "Home" ? "first" : "last");
|
|
353
459
|
}
|
|
460
|
+
else if (inBody && (event.key === "PageDown" || event.key === "PageUp")) {
|
|
461
|
+
event.preventDefault();
|
|
462
|
+
pageFocus(event.key === "PageDown" ? 1 : -1);
|
|
463
|
+
}
|
|
354
464
|
else if (event.key === "Backspace" && query) {
|
|
355
465
|
event.preventDefault();
|
|
356
466
|
setQuery(query.slice(0, -1));
|
|
357
|
-
(
|
|
467
|
+
focusField();
|
|
358
468
|
}
|
|
359
469
|
else if (event.key.length === 1 &&
|
|
360
470
|
!event.metaKey &&
|
|
@@ -365,9 +475,46 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
365
475
|
return;
|
|
366
476
|
event.preventDefault();
|
|
367
477
|
setQuery(query + event.key);
|
|
368
|
-
(
|
|
478
|
+
focusField();
|
|
369
479
|
}
|
|
370
480
|
};
|
|
481
|
+
const onContentKeyDown = (event) => {
|
|
482
|
+
if (event.key === "Escape") {
|
|
483
|
+
event.stopPropagation();
|
|
484
|
+
if (event.nativeEvent.isComposing)
|
|
485
|
+
return;
|
|
486
|
+
event.preventDefault();
|
|
487
|
+
if (query) {
|
|
488
|
+
setQuery("");
|
|
489
|
+
focusField();
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
onClose();
|
|
493
|
+
}
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const hasMod = event.metaKey || event.ctrlKey;
|
|
497
|
+
if (hasMod &&
|
|
498
|
+
!event.altKey &&
|
|
499
|
+
!event.shiftKey &&
|
|
500
|
+
event.key.toLowerCase() === "k") {
|
|
501
|
+
event.preventDefault();
|
|
502
|
+
focusField(true);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const target = event.target;
|
|
506
|
+
if (target === inputRef.current)
|
|
507
|
+
return;
|
|
508
|
+
if (event.key === "/" &&
|
|
509
|
+
!hasMod &&
|
|
510
|
+
!event.altKey &&
|
|
511
|
+
!target.closest(TEXT_TARGET)) {
|
|
512
|
+
event.preventDefault();
|
|
513
|
+
focusField(true);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
onBodyKeyDown(event);
|
|
517
|
+
};
|
|
371
518
|
(0, react_2.useEffect)(() => {
|
|
372
519
|
var _a, _b, _c;
|
|
373
520
|
(_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" });
|
|
@@ -391,7 +538,6 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
391
538
|
document.removeEventListener("pointerdown", forget);
|
|
392
539
|
};
|
|
393
540
|
}, [isOpen]);
|
|
394
|
-
const appList = apps !== null && apps !== void 0 ? apps : NO_APPS;
|
|
395
541
|
(0, react_2.useEffect)(() => {
|
|
396
542
|
var _a, _b;
|
|
397
543
|
if (lastItemIndex.current < 0 || document.activeElement !== document.body)
|
|
@@ -402,23 +548,20 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
402
548
|
items[Math.min(lastItemIndex.current, items.length - 1)].focus();
|
|
403
549
|
}, [appList]);
|
|
404
550
|
const hues = (0, react_2.useMemo)(() => (0, AppMark_1.assignAppHues)(appList, (0, chartColorsFromTheme_1.chartMode)(colors)), [appList, colors]);
|
|
405
|
-
const showApps = appList.some((app) => app.id !== currentAppId) &&
|
|
406
|
-
(appsVisibility === "always" || !isDesktop);
|
|
407
551
|
const handleAppSelect = (app, event) => {
|
|
408
552
|
onAppSelect === null || onAppSelect === void 0 ? void 0 : onAppSelect(app, event);
|
|
409
553
|
if (!event.defaultPrevented)
|
|
410
554
|
onClose();
|
|
411
555
|
};
|
|
412
|
-
const
|
|
556
|
+
const renderApps = (hideHeading) => ((0, jsx_runtime_1.jsx)(launcherParts_1.AppsSection, { apps: appList, hues: hues, currentAppId: currentAppId, heading: labels.apps, hideHeading: hideHeading, onSelect: handleAppSelect }));
|
|
413
557
|
const listboxId = `${baseId}-results`;
|
|
414
558
|
const optionId = (index) => `${baseId}-opt-${index}`;
|
|
415
559
|
const togglePinFor = (id) => onTogglePin ? () => onTogglePin(id) : undefined;
|
|
416
560
|
const hasResults = results.length > 0;
|
|
417
561
|
const trimmed = query.trim();
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
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, { "data-nav-grid": true, "data-nav-columns": PINNED_COLUMNS, display: "grid", gridTemplateColumns: `repeat(${PINNED_COLUMNS}, 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] }));
|
|
562
|
+
const pinnedColumns = !isLg && isDesktop ? 3 : PINNED_COLUMNS;
|
|
563
|
+
const quickSections = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { 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, { "data-nav-grid": true, "data-nav-columns": pinnedColumns, display: "grid", gridTemplateColumns: `repeat(${pinnedColumns}, 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), tip: labels.newTabTip, 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), tip: labels.newTabTip, linkAs: link.linkAs }) }, screen.id)))] })) : null] }));
|
|
564
|
+
const rail = ((0, jsx_runtime_1.jsxs)(Rail, { label: labels.quickAccess, scrolls: isLg, children: [quickSections, showApps ? renderApps(false) : null] }));
|
|
422
565
|
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, { columns: columns, children: section.modules.map((module) => {
|
|
423
566
|
const pinId = module.screens[0].id;
|
|
424
567
|
return ((0, jsx_runtime_1.jsx)(launcherParts_1.ModuleRow, { module: module, keyScreens: module.screens
|
|
@@ -432,21 +575,28 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
432
575
|
};
|
|
433
576
|
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)
|
|
434
577
|
? labels.suggestionsHint
|
|
435
|
-
: 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,
|
|
578
|
+
: 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_2.transitionFor)(["background-color"]), _hover: { bg: (0, rowStates_1.listRowBackgrounds)(colors).active }, sx: (0, focusRing_styles_1.focusVisibleStyles)(colors), children: suggestion }, suggestion))) })) : null] }));
|
|
436
579
|
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, { columns: columns, children: group.items.map((item, itemIndex) => {
|
|
437
580
|
const index = groupStarts[groupIndex] + itemIndex;
|
|
438
581
|
return ((0, jsx_runtime_1.jsx)(SearchRow, { item: item, query: query, isActive: index === active, 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));
|
|
439
582
|
}) })] }, group.id))) })) : (noResults);
|
|
440
|
-
const
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
583
|
+
const stacked = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Pane, { scrolls: isLg, children: isSearchView ? searchPane : browsePane }), rail] }));
|
|
584
|
+
const body = isLoading ? (0, jsx_runtime_1.jsx)(SkeletonLayout, { label: labels.loading }) : stacked;
|
|
585
|
+
const tabPanels = {
|
|
586
|
+
menu: (0, jsx_runtime_1.jsx)(Pane, { children: browsePane }),
|
|
587
|
+
quick: ((0, jsx_runtime_1.jsx)(react_1.Flex, { direction: "column", gap: "16px", px: "18px", pt: "14px", pb: "16px", children: quickSections })),
|
|
588
|
+
apps: ((0, jsx_runtime_1.jsx)(react_1.Box, { px: "14px", pt: "10px", pb: "16px", children: renderApps(true) })),
|
|
589
|
+
};
|
|
590
|
+
const compactBody = ((0, jsx_runtime_1.jsx)(react_1.Flex, { "data-scroll-region": true, role: isTabbed ? "tabpanel" : undefined, id: isTabbed ? (0, LauncherTabs_1.panelId)(baseId) : undefined, "aria-labelledby": isTabbed ? (0, LauncherTabs_1.tabId)(baseId, activeTab) : undefined, direction: "column", flex: 1, minH: 0, overflowY: "auto", children: isTabbed ? tabPanels[activeTab] : body }));
|
|
591
|
+
return ((0, jsx_runtime_1.jsx)(launcherParts_1.TouchDensity, { value: !isLg, children: (0, jsx_runtime_1.jsxs)(Drawer_1.default, { isOpen: isOpen, onClose: onClose, placement: "left", size: "full", initialFocusRef: inputRef, hideCloseButton: true, closeOnEsc: false, contentProps: {
|
|
592
|
+
maxW: { base: "100vw", lg: `min(${maxWidth}, calc(100vw - 56px))` },
|
|
593
|
+
h: { base: "100%", lg: "auto" },
|
|
594
|
+
maxH: "100%",
|
|
595
|
+
bg: colors.surfaceRaised,
|
|
596
|
+
borderBottomRightRadius: { base: 0, lg: "16px" },
|
|
597
|
+
boxShadow: colors.boxShadow.default,
|
|
598
|
+
overflow: "hidden",
|
|
599
|
+
onKeyDown: onContentKeyDown,
|
|
600
|
+
...{ "data-testid": testId },
|
|
601
|
+
}, 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: { base: "8px", md: "12px" }, minH: { base: "60px", md: "68px" }, pl: { base: "12px", md: "18px" }, pr: { base: "8px", md: "12px" }, children: [(0, jsx_runtime_1.jsx)(react_1.VisuallyHidden, { as: "h2", children: 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(active) : undefined, isExpanded: isSearchView, hasResults: hasResults }), (0, jsx_runtime_1.jsx)(react_1.Box, { "aria-hidden": true, "data-testid": "launcher-count", display: { base: "none", sm: "block" }, flexShrink: 0, overflow: "hidden", w: countText ? "88px" : "0", opacity: countText ? 1 : 0, textAlign: "right", fontSize: "13px", whiteSpace: "nowrap", color: colors.textMuted, transition: (0, motion_2.transitionFor)(["width", "opacity"]), sx: { fontVariantNumeric: "tabular-nums", ...(0, motion_1.reducedMotionSx)() }, children: countText }), headerActions ? ((0, jsx_runtime_1.jsx)(react_1.Flex, { align: "center", gap: "4px", flexShrink: 0, children: headerActions })) : null, (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: { base: "40px", md: "44px" }, minW: { base: "40px", md: "44px" }, borderRadius: "10px", flexShrink: 0, color: colors.textMuted, onClick: onClose })] }) }), (0, jsx_runtime_1.jsx)(Drawer_1.DrawerBody, { p: 0, display: "flex", flexDirection: "column", overflowY: "hidden", minH: 0, children: (0, jsx_runtime_1.jsxs)(react_1.Flex, { ref: bodyRef, direction: "column", flex: 1, minH: 0, minW: 0, children: [(0, jsx_runtime_1.jsx)(react_1.VisuallyHidden, { "data-testid": "launcher-live", "aria-live": "polite", "aria-atomic": "true", children: countText }), isTabbed ? ((0, jsx_runtime_1.jsx)(LauncherTabs_1.LauncherTabs, { tabs: tabs, selected: activeTab, onSelect: setTab, idBase: baseId, label: labels.tabsLabel, onLeaveUp: () => focusField(), onLeaveDown: () => moveFocus("first") })) : null, isLg ? ((0, jsx_runtime_1.jsx)(react_1.Flex, { direction: "row", flex: 1, minH: 0, minW: 0, children: body })) : (compactBody)] }) }), (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, sx: { "@media (hover: none)": { display: "none" } }, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(react_1.Flex, { as: "span", gap: "2px", children: ARROW_KEYS.map((arrow) => ((0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: arrow }, arrow))) }), labels.hintMove] }), (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.KbdShortcut, { shortcut: "enter", size: "xs" }), labels.hintOpen] }), (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [modKeyLabel ? ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", gap: "4px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: modKeyLabel }), (0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: ENTER_KEY })] })) : ((0, jsx_runtime_1.jsx)(Kbd_1.KbdShortcut, { shortcut: "mod+enter", size: "xs" })), labels.hintNewTab] }), (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 : MOD_KEY }), labels.hintClickNewTab] }), 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.KbdShortcut, { shortcut: "esc", size: "xs" }), labels.hintClose] })] }), (0, jsx_runtime_1.jsx)(react_1.Text, { as: "span", ml: "auto", children: labels.footerNote })] })] }) }));
|
|
452
602
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ElementType, KeyboardEvent, MouseEvent } from "react";
|
|
1
|
+
import type { ElementType, KeyboardEvent, MouseEvent, ReactNode } from "react";
|
|
2
2
|
import type { AppOption } from "../AppSwitcher/AppSwitcherProps";
|
|
3
3
|
/** Rendered as `<Icon size={16} aria-hidden />`, so any lucide-compatible component works. */
|
|
4
4
|
export type LauncherIcon = ElementType;
|
|
@@ -54,6 +54,7 @@ export type LauncherResultGroup = {
|
|
|
54
54
|
};
|
|
55
55
|
/** Every visible string and accessible name; pass any subset, the rest fall back to English. */
|
|
56
56
|
export type LauncherLabels = {
|
|
57
|
+
/** Accessible name of the dialog and the results list; no longer drawn as a header pill. */
|
|
57
58
|
title: string;
|
|
58
59
|
searchPlaceholder: string;
|
|
59
60
|
clearSearch: string;
|
|
@@ -84,8 +85,22 @@ export type LauncherLabels = {
|
|
|
84
85
|
hintPin: string;
|
|
85
86
|
/** Footer hint after the ESC key cap. */
|
|
86
87
|
hintClose: string;
|
|
87
|
-
/**
|
|
88
|
+
/** @deprecated No longer rendered: the launcher is already open, so the open chord is not shown. */
|
|
88
89
|
hintOpenMenu: string;
|
|
90
|
+
/** Footer hint after the mod key cap for a modified click, e.g. "+ click for new tab". */
|
|
91
|
+
hintClickNewTab: string;
|
|
92
|
+
/** Tooltip of the arrow link beside a row. */
|
|
93
|
+
newTabTip: string;
|
|
94
|
+
/** Muted count beside the search field and its polite announcement, e.g. "12 results". */
|
|
95
|
+
resultsCount: (count: number) => string;
|
|
96
|
+
/** Name of the Menu / Quick access / Apps tab list shown below `lg`. */
|
|
97
|
+
tabsLabel: string;
|
|
98
|
+
/** Tab showing every module (below `lg`). */
|
|
99
|
+
tabMenu: string;
|
|
100
|
+
/** Tab showing Pinned, Frequently visited and Recent (below `lg`). */
|
|
101
|
+
tabQuickAccess: string;
|
|
102
|
+
/** Tab showing the suite's apps (below `lg`). */
|
|
103
|
+
tabApps: string;
|
|
89
104
|
/** Heading and list name of the apps section. */
|
|
90
105
|
apps: string;
|
|
91
106
|
/** Per-section count shown while searching, e.g. "3 found". */
|
|
@@ -146,10 +161,12 @@ export interface NavLauncherProps {
|
|
|
146
161
|
suggestions?: readonly string[];
|
|
147
162
|
/** Key cap shown at the end of the search field, e.g. "/" when the app opens the launcher with it. */
|
|
148
163
|
searchHint?: string;
|
|
149
|
-
/**
|
|
164
|
+
/** @deprecated No longer rendered: the launcher is already open. Cmd/Ctrl+K and "/" now focus the search field while it is open. */
|
|
150
165
|
openShortcutHint?: string;
|
|
151
166
|
/** Replaces the modifier cap of the mod+Enter hint; defaults to "⌘" on Apple platforms, "Ctrl" elsewhere. */
|
|
152
167
|
modKeyLabel?: string;
|
|
168
|
+
/** Rendered between the search field and the close button, e.g. a help or customise icon button; use icon-only buttons so the dialog name stays "Menu". */
|
|
169
|
+
headerActions?: ReactNode;
|
|
153
170
|
/** Panel width at `lg`+; always full width below `lg`. Defaults to "1080px". */
|
|
154
171
|
maxWidth?: string;
|
|
155
172
|
labels?: Partial<LauncherLabels>;
|
|
@@ -179,8 +196,10 @@ export interface PinToggleProps {
|
|
|
179
196
|
/** Props for the small "open in new tab" anchor beside a launcher row. */
|
|
180
197
|
export interface NewTabLinkProps {
|
|
181
198
|
href: string;
|
|
182
|
-
/** Accessible name
|
|
199
|
+
/** Accessible name, e.g. "Open Leave in a new tab". */
|
|
183
200
|
label: string;
|
|
201
|
+
/** Visible tooltip text; defaults to `label`. */
|
|
202
|
+
tip?: string;
|
|
184
203
|
/** Anchor-like component receiving `href`, `target`, `rel`; defaults to `"a"`. */
|
|
185
204
|
linkAs?: ElementType;
|
|
186
205
|
tabIndex?: number;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { NewTabLinkProps } from "./NavLauncherProps";
|
|
2
|
-
/** "Open in a new tab" anchor; visible on row hover, focus-within or its own focus
|
|
3
|
-
export default function NewTabLink({ href, label, linkAs, tabIndex }: NewTabLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
/** "Open in a new tab" anchor with a tooltip; visible on row hover, keyboard-active or focus-within, or its own focus. */
|
|
3
|
+
export default function NewTabLink({ href, label, tip, linkAs, tabIndex, }: NewTabLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.default = NewTabLink;
|
|
4
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
@@ -6,9 +9,10 @@ const react_1 = require("@chakra-ui/react");
|
|
|
6
9
|
const lucide_react_1 = require("lucide-react");
|
|
7
10
|
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
8
11
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
12
|
+
const ToolTip_1 = __importDefault(require("../ToolTip/ToolTip"));
|
|
9
13
|
const revealStyles_1 = require("./revealStyles");
|
|
10
|
-
/** "Open in a new tab" anchor; visible on row hover, focus-within or its own focus
|
|
11
|
-
function NewTabLink({ href, label, linkAs = "a", tabIndex }) {
|
|
14
|
+
/** "Open in a new tab" anchor with a tooltip; visible on row hover, keyboard-active or focus-within, or its own focus. */
|
|
15
|
+
function NewTabLink({ href, label, tip, linkAs = "a", tabIndex, }) {
|
|
12
16
|
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,
|
|
17
|
+
return ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: tip !== null && tip !== void 0 ? tip : label, placement: "bottom", children: (0, jsx_runtime_1.jsx)(react_1.Box, { as: linkAs, href: href, target: "_blank", rel: "noopener noreferrer", "aria-label": 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
18
|
}
|
|
@@ -3,6 +3,8 @@ import type { PaletteProps } from "../../Theme/tokens/types";
|
|
|
3
3
|
import type { AppOption } from "../AppSwitcher/AppSwitcherProps";
|
|
4
4
|
import type { LauncherLabels, LauncherModule, LauncherScreen } from "./NavLauncherProps";
|
|
5
5
|
export declare const ITEM_SELECTOR = "[data-launcher-item]";
|
|
6
|
+
export declare const TouchDensity: import("react").Provider<boolean>;
|
|
7
|
+
export declare const useTouchDensity: () => boolean;
|
|
6
8
|
export declare const isPlainClick: (event: MouseEvent) => boolean;
|
|
7
9
|
export type LinkCtx = {
|
|
8
10
|
linkAs: ElementType;
|
|
@@ -146,7 +148,8 @@ type AppsSectionProps = {
|
|
|
146
148
|
hues: readonly string[];
|
|
147
149
|
currentAppId?: string;
|
|
148
150
|
heading: string;
|
|
151
|
+
hideHeading?: boolean;
|
|
149
152
|
onSelect: (app: AppOption, event: MouseEvent | KeyboardEvent) => void;
|
|
150
153
|
};
|
|
151
|
-
export declare function AppsSection({ apps, hues, currentAppId, heading, onSelect, }: AppsSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
154
|
+
export declare function AppsSection({ apps, hues, currentAppId, heading, hideHeading, onSelect, }: AppsSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
152
155
|
export {};
|