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.
Files changed (30) hide show
  1. package/CHANGELOG.md +38 -10
  2. package/COMPONENT-GOTCHAS.md +41 -4
  3. package/DESIGN-GUIDE.md +9 -1
  4. package/dist/Components/NavLauncher/LauncherTabs.d.ts +18 -0
  5. package/dist/Components/NavLauncher/LauncherTabs.js +64 -0
  6. package/dist/Components/NavLauncher/NavLauncher.d.ts +1 -1
  7. package/dist/Components/NavLauncher/NavLauncher.js +203 -53
  8. package/dist/Components/NavLauncher/NavLauncherProps.d.ts +23 -4
  9. package/dist/Components/NavLauncher/NewTabLink.d.ts +2 -2
  10. package/dist/Components/NavLauncher/NewTabLink.js +7 -3
  11. package/dist/Components/NavLauncher/launcherParts.d.ts +4 -1
  12. package/dist/Components/NavLauncher/launcherParts.js +16 -8
  13. package/dist/Components/NavLauncher/resultNavigation.d.ts +3 -0
  14. package/dist/Components/NavLauncher/resultNavigation.js +65 -0
  15. package/dist/Components/NavLauncher/revealStyles.d.ts +1 -1
  16. package/dist/Components/NavLauncher/revealStyles.js +2 -2
  17. package/dist/Components/NavigationBar/NavigationBar.js +2 -1
  18. package/dist/Components/NavigationBar/NavigationBarProps.d.ts +1 -0
  19. package/dist/Components/OrgSwitcher/OrgSwitcher.js +18 -6
  20. package/dist/Components/OrgSwitcher/OrgSwitcherProps.d.ts +4 -0
  21. package/dist/esm/Components/NavLauncher/LauncherTabs.js +58 -0
  22. package/dist/esm/Components/NavLauncher/NavLauncher.js +204 -54
  23. package/dist/esm/Components/NavLauncher/NewTabLink.js +4 -3
  24. package/dist/esm/Components/NavLauncher/launcherParts.js +15 -8
  25. package/dist/esm/Components/NavLauncher/resultNavigation.js +61 -0
  26. package/dist/esm/Components/NavLauncher/revealStyles.js +2 -2
  27. package/dist/esm/Components/NavigationBar/NavigationBar.js +2 -1
  28. package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +16 -7
  29. package/dist/index.d.ts +1 -0
  30. package/package.json +1 -1
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.useRowHover = exports.stretchedLink = exports.useRowFocus = exports.titleInset = exports.activeRowSurface = exports.activeRowStyle = exports.ROW_CONTAINER = exports.WIDE_ROW = exports.isPlainClick = exports.ITEM_SELECTOR = void 0;
6
+ exports.useRowHover = exports.stretchedLink = exports.useRowFocus = exports.titleInset = exports.activeRowSurface = exports.activeRowStyle = exports.ROW_CONTAINER = exports.WIDE_ROW = exports.isPlainClick = exports.useTouchDensity = exports.TouchDensity = exports.ITEM_SELECTOR = void 0;
7
7
  exports.CountBadge = CountBadge;
8
8
  exports.NewTag = NewTag;
9
9
  exports.SectionHeader = SectionHeader;
@@ -33,6 +33,10 @@ const NewTabLink_1 = __importDefault(require("./NewTabLink"));
33
33
  const PinToggle_1 = __importDefault(require("./PinToggle"));
34
34
  const revealStyles_1 = require("./revealStyles");
35
35
  exports.ITEM_SELECTOR = "[data-launcher-item]";
36
+ const TouchDensityContext = (0, react_2.createContext)(false);
37
+ exports.TouchDensity = TouchDensityContext.Provider;
38
+ const useTouchDensity = () => (0, react_2.useContext)(TouchDensityContext);
39
+ exports.useTouchDensity = useTouchDensity;
36
40
  const isPlainClick = (event) => event.button === 0 &&
37
41
  !event.metaKey &&
38
42
  !event.ctrlKey &&
@@ -138,6 +142,7 @@ function Highlight({ text, query }) {
138
142
  }
139
143
  function RowActions({ children }) {
140
144
  const { colors } = (0, useCustomTheme_1.useCustomTheme)();
145
+ const isTouch = (0, exports.useTouchDensity)();
141
146
  const revealed = REVEALED.replace(/ &/g, " > &");
142
147
  return ((0, jsx_runtime_1.jsx)(react_1.Flex, { sx: {
143
148
  [NARROW_ROW]: {
@@ -146,7 +151,7 @@ function RowActions({ children }) {
146
151
  borderRadius: "6px",
147
152
  },
148
153
  },
149
- }, position: "absolute", top: "7px", right: "4px", h: "19px", align: "center", gap: "2px", zIndex: 1, children: children }));
154
+ }, position: "absolute", top: isTouch ? "12px" : "7px", right: "4px", h: "19px", align: "center", gap: "2px", zIndex: 1, children: children }));
150
155
  }
151
156
  function RowTip({ text }) {
152
157
  const { colors } = (0, useCustomTheme_1.useCustomTheme)();
@@ -157,19 +162,21 @@ function ModuleRow({ module, keyScreens, isPinned, onTogglePin, pinId, labels, l
157
162
  const hover = (0, exports.useRowHover)();
158
163
  const focus = (0, exports.useRowFocus)();
159
164
  const ring = (0, focusRing_styles_1.focusVisibleStyles)(colors);
165
+ const isTouch = (0, exports.useTouchDensity)();
160
166
  const base = typeof module.badge === "number" || module.badge ? 24 : 0;
161
167
  const open = base + (onTogglePin ? 46 : 24);
162
- return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { "data-group": true, "data-nav-cell": true, position: "relative", align: "flex-start", gap: "10px", minH: "34px", pl: "8px", pr: "4px", pt: "7px", pb: "5px", borderRadius: "8px", transition: (0, motion_1.transitionFor)(["background-color"]), _hover: hover._hover, _focusWithin: hover._focusWithin, sx: { ...focus.sx, ...exports.ROW_CONTAINER }, children: [(0, jsx_runtime_1.jsx)(RowIcon, { icon: module.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", h: "19px", minW: 0, sx: (0, exports.titleInset)(base, open), children: [(0, jsx_runtime_1.jsx)(OverflowToolTip_1.default, { label: module.label, placement: "top-start", children: (0, jsx_runtime_1.jsx)(react_1.Box, { as: link.linkAs, href: module.href, "data-launcher-item": true, ...focus.primary, "data-pin-id": onTogglePin ? pinId : undefined, onClick: link.onPlainClick, minW: 0, fontSize: "13.5px", fontWeight: 500, color: colors.text[900], noOfLines: 1, borderRadius: "sm", sx: (0, exports.stretchedLink)("8px"), children: module.label }) }), module.isNew ? (0, jsx_runtime_1.jsx)(NewTag, { label: labels.newBadge }) : null] }), keyScreens.length ? ((0, jsx_runtime_1.jsx)(OverflowToolTip_1.default, { label: keyScreens.map((screen) => screen.label).join(" · "), placement: "bottom-start", children: (0, jsx_runtime_1.jsx)(react_1.Box, { role: "group", "aria-label": `${module.label} ${labels.screens}`, position: "relative", zIndex: 1, w: "fit-content", maxW: "100%", fontSize: "11.5px", lineHeight: "15px", color: colors.textMuted, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", children: keyScreens.map((screen, i) => ((0, jsx_runtime_1.jsxs)(react_2.Fragment, { children: [i > 0 ? ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", "aria-hidden": true, mx: "5px", opacity: 0.7, children: "\u00B7" })) : null, (0, jsx_runtime_1.jsx)(react_1.Box, { as: link.linkAs, href: screen.href, "data-launcher-item": true, onClick: link.onPlainClick, color: "inherit", borderRadius: "sm", position: "relative", zIndex: 1, _hover: {
168
+ return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { "data-group": true, "data-nav-cell": true, 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_1.transitionFor)(["background-color"]), _hover: hover._hover, _focusWithin: hover._focusWithin, sx: { ...focus.sx, ...exports.ROW_CONTAINER }, children: [(0, jsx_runtime_1.jsx)(RowIcon, { icon: module.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", h: "19px", minW: 0, sx: (0, exports.titleInset)(base, open), children: [(0, jsx_runtime_1.jsx)(OverflowToolTip_1.default, { label: module.label, placement: "top-start", children: (0, jsx_runtime_1.jsx)(react_1.Box, { as: link.linkAs, href: module.href, "data-launcher-item": true, ...focus.primary, "data-pin-id": onTogglePin ? pinId : undefined, onClick: link.onPlainClick, minW: 0, fontSize: "13.5px", fontWeight: 500, color: colors.text[900], noOfLines: 1, borderRadius: "sm", sx: (0, exports.stretchedLink)("8px"), children: module.label }) }), module.isNew ? (0, jsx_runtime_1.jsx)(NewTag, { label: labels.newBadge }) : null] }), keyScreens.length ? ((0, jsx_runtime_1.jsx)(OverflowToolTip_1.default, { label: keyScreens.map((screen) => screen.label).join(" · "), placement: "bottom-start", children: (0, jsx_runtime_1.jsx)(react_1.Box, { role: "group", "aria-label": `${module.label} ${labels.screens}`, position: "relative", zIndex: 1, w: "fit-content", maxW: "100%", fontSize: "11.5px", lineHeight: "15px", color: colors.textMuted, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", children: keyScreens.map((screen, i) => ((0, jsx_runtime_1.jsxs)(react_2.Fragment, { children: [i > 0 ? ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", "aria-hidden": true, mx: "5px", opacity: 0.7, children: "\u00B7" })) : null, (0, jsx_runtime_1.jsx)(react_1.Box, { as: link.linkAs, href: screen.href, "data-launcher-item": true, onClick: link.onPlainClick, color: "inherit", borderRadius: "sm", position: "relative", zIndex: 1, _hover: {
163
169
  textDecoration: "underline",
164
170
  textUnderlineOffset: "2px",
165
- }, sx: ring, children: screen.label })] }, screen.id))) }) })) : null] }), (0, jsx_runtime_1.jsxs)(RowActions, { children: [(0, jsx_runtime_1.jsx)(CountBadge, { value: module.badge }), onTogglePin ? ((0, jsx_runtime_1.jsx)(PinToggle_1.default, { isPinned: isPinned, label: isPinned ? labels.unpin(module.label) : labels.pin(module.label), title: "", onToggle: onTogglePin })) : null, (0, jsx_runtime_1.jsx)(NewTabLink_1.default, { href: module.href, label: labels.openInNewTab(module.label), linkAs: link.linkAs })] }), onTogglePin ? ((0, jsx_runtime_1.jsx)(RowTip, { text: isPinned ? labels.unpinTip : labels.pinTip })) : null] }));
171
+ }, sx: ring, children: screen.label })] }, screen.id))) }) })) : null] }), (0, jsx_runtime_1.jsxs)(RowActions, { children: [(0, jsx_runtime_1.jsx)(CountBadge, { value: module.badge }), onTogglePin ? ((0, jsx_runtime_1.jsx)(PinToggle_1.default, { isPinned: isPinned, label: isPinned ? labels.unpin(module.label) : labels.pin(module.label), title: "", onToggle: onTogglePin })) : null, (0, jsx_runtime_1.jsx)(NewTabLink_1.default, { href: module.href, label: labels.openInNewTab(module.label), tip: labels.newTabTip, linkAs: link.linkAs })] }), onTogglePin ? ((0, jsx_runtime_1.jsx)(RowTip, { text: isPinned ? labels.unpinTip : labels.pinTip })) : null] }));
166
172
  }
167
173
  function RailRow({ screen, module, link, isStrong = false, trailing, pinId, }) {
168
174
  var _a;
169
175
  const { colors } = (0, useCustomTheme_1.useCustomTheme)();
170
176
  const hover = (0, exports.useRowHover)();
171
177
  const focus = (0, exports.useRowFocus)();
172
- return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { "data-group": true, position: "relative", align: "center", gap: "10px", h: "32px", pl: "8px", pr: "4px", borderRadius: "8px", transition: (0, motion_1.transitionFor)(["background-color"]), _hover: hover._hover, _focusWithin: hover._focusWithin, sx: focus.sx, children: [(0, jsx_runtime_1.jsx)(RowIcon, { icon: (_a = screen.icon) !== null && _a !== void 0 ? _a : module.icon, size: 15 }), (0, jsx_runtime_1.jsx)(OverflowToolTip_1.default, { label: screen.label, placement: "top-start", children: (0, jsx_runtime_1.jsx)(react_1.Box, { as: link.linkAs, href: screen.href, "data-launcher-item": true, ...focus.primary, "data-pin-id": pinId, onClick: link.onPlainClick, flex: 1, minW: 0, fontSize: "13px", fontWeight: isStrong ? 500 : 400, color: colors.text[900], noOfLines: 1, borderRadius: "sm", sx: (0, exports.stretchedLink)("8px"), children: screen.label }) }), (0, jsx_runtime_1.jsx)(react_1.Flex, { position: "relative", zIndex: 1, align: "center", gap: "2px", children: trailing })] }));
178
+ const isTouch = (0, exports.useTouchDensity)();
179
+ return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { "data-group": true, position: "relative", align: "center", gap: "10px", h: isTouch ? "44px" : "32px", pl: "8px", pr: "4px", borderRadius: "8px", transition: (0, motion_1.transitionFor)(["background-color"]), _hover: hover._hover, _focusWithin: hover._focusWithin, sx: focus.sx, children: [(0, jsx_runtime_1.jsx)(RowIcon, { icon: (_a = screen.icon) !== null && _a !== void 0 ? _a : module.icon, size: 15 }), (0, jsx_runtime_1.jsx)(OverflowToolTip_1.default, { label: screen.label, placement: "top-start", children: (0, jsx_runtime_1.jsx)(react_1.Box, { as: link.linkAs, href: screen.href, "data-launcher-item": true, ...focus.primary, "data-pin-id": pinId, onClick: link.onPlainClick, flex: 1, minW: 0, fontSize: "13px", fontWeight: isStrong ? 500 : 400, color: colors.text[900], noOfLines: 1, borderRadius: "sm", sx: (0, exports.stretchedLink)("8px"), children: screen.label }) }), (0, jsx_runtime_1.jsx)(react_1.Flex, { position: "relative", zIndex: 1, align: "center", gap: "2px", children: trailing })] }));
173
180
  }
174
181
  function PinTextButton({ label, text, onToggle }) {
175
182
  const { colors } = (0, useCustomTheme_1.useCustomTheme)();
@@ -184,13 +191,14 @@ function PinnedTile({ screen, module, link, labels, onTogglePin, }) {
184
191
  const { colors } = (0, useCustomTheme_1.useCustomTheme)();
185
192
  const Icon = (_a = screen.icon) !== null && _a !== void 0 ? _a : module.icon;
186
193
  const focus = (0, exports.useRowFocus)(true);
187
- return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { "data-group": true, "data-nav-cell": true, position: "relative", align: "center", gap: "8px", h: "36px", pl: "9px", pr: "4px", borderRadius: "9px", borderWidth: "1px", borderColor: colors.boxborder[200], bg: colors.surfaceRaised, transition: (0, motion_1.transitionFor)(["border-color", "background-color"]), _hover: { borderColor: colors.gray[300] }, sx: focus.sx, children: [Icon ? ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", display: "inline-flex", flexShrink: 0, color: colors.accentText, children: (0, jsx_runtime_1.jsx)(Icon, { size: 15, strokeWidth: 1.8, "aria-hidden": true }) })) : null, (0, jsx_runtime_1.jsx)(OverflowToolTip_1.default, { label: screen.label, placement: "top-start", children: (0, jsx_runtime_1.jsx)(react_1.Box, { as: link.linkAs, href: screen.href, "data-launcher-item": true, ...focus.primary, "data-pin-id": onTogglePin ? screen.id : undefined, onClick: link.onPlainClick, flex: 1, minW: 0, fontSize: "13px", fontWeight: 500, color: colors.text[900], noOfLines: 1, borderRadius: "sm", sx: (0, exports.stretchedLink)("9px"), children: screen.label }) }), (0, jsx_runtime_1.jsx)(CountBadge, { value: screen.badge, size: "tile" }), onTogglePin ? ((0, jsx_runtime_1.jsx)(react_1.Box, { position: "relative", zIndex: 1, flexShrink: 0, w: "0", overflow: "hidden", transition: (0, motion_1.transitionFor)(["width"]), _groupHover: { w: "22px" }, _groupFocusWithin: { w: "22px" }, children: (0, jsx_runtime_1.jsx)(PinToggle_1.default, { isPinned: true, size: 12, label: labels.unpin(screen.label), title: labels.unpinTip, onToggle: onTogglePin }) })) : null] }));
194
+ const isTouch = (0, exports.useTouchDensity)();
195
+ return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { "data-group": true, "data-nav-cell": true, position: "relative", align: "center", gap: "8px", h: isTouch ? "44px" : "36px", pl: "9px", pr: "4px", borderRadius: "9px", borderWidth: "1px", borderColor: colors.boxborder[200], bg: colors.surfaceRaised, transition: (0, motion_1.transitionFor)(["border-color", "background-color"]), _hover: { borderColor: colors.gray[300] }, sx: focus.sx, children: [Icon ? ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", display: "inline-flex", flexShrink: 0, color: colors.accentText, children: (0, jsx_runtime_1.jsx)(Icon, { size: 15, strokeWidth: 1.8, "aria-hidden": true }) })) : null, (0, jsx_runtime_1.jsx)(OverflowToolTip_1.default, { label: screen.label, placement: "top-start", children: (0, jsx_runtime_1.jsx)(react_1.Box, { as: link.linkAs, href: screen.href, "data-launcher-item": true, ...focus.primary, "data-pin-id": onTogglePin ? screen.id : undefined, onClick: link.onPlainClick, flex: 1, minW: 0, fontSize: "13px", fontWeight: 500, color: colors.text[900], noOfLines: 1, borderRadius: "sm", sx: (0, exports.stretchedLink)("9px"), children: screen.label }) }), (0, jsx_runtime_1.jsx)(CountBadge, { value: screen.badge, size: "tile" }), onTogglePin ? ((0, jsx_runtime_1.jsx)(react_1.Box, { position: "relative", zIndex: 1, flexShrink: 0, w: "0", overflow: "hidden", transition: (0, motion_1.transitionFor)(["width"]), _groupHover: { w: "22px" }, _groupFocusWithin: { w: "22px" }, children: (0, jsx_runtime_1.jsx)(PinToggle_1.default, { isPinned: true, size: 12, label: labels.unpin(screen.label), title: labels.unpinTip, onToggle: onTogglePin }) })) : null] }));
188
196
  }
189
- function AppsSection({ apps, hues, currentAppId, heading, onSelect, }) {
197
+ function AppsSection({ apps, hues, currentAppId, heading, hideHeading = false, onSelect, }) {
190
198
  const { colors } = (0, useCustomTheme_1.useCustomTheme)();
191
199
  const rows = (0, rowStates_1.listRowBackgrounds)(colors);
192
200
  const rowRing = (0, exports.activeRowSurface)(colors);
193
- return ((0, jsx_runtime_1.jsxs)(react_1.Box, { children: [(0, jsx_runtime_1.jsx)(SectionHeader, { label: heading, isRail: true, hasCount: false }), (0, jsx_runtime_1.jsx)(react_1.Box, { as: "ul", listStyleType: "none", m: 0, p: 0, "aria-label": heading, children: apps.map((app, index) => {
201
+ return ((0, jsx_runtime_1.jsxs)(react_1.Box, { children: [hideHeading ? null : (0, jsx_runtime_1.jsx)(SectionHeader, { label: heading, isRail: true, hasCount: false }), (0, jsx_runtime_1.jsx)(react_1.Box, { as: "ul", listStyleType: "none", m: 0, p: 0, "aria-label": heading, children: apps.map((app, index) => {
194
202
  var _a;
195
203
  const isCurrent = app.id === currentAppId;
196
204
  const interactive = !isCurrent && !app.locked;
@@ -0,0 +1,3 @@
1
+ export type ResultKey = "ArrowUp" | "ArrowDown" | "ArrowLeft" | "ArrowRight" | "Home" | "End" | "PageUp" | "PageDown";
2
+ /** Next flat result index for a key, following the visual grid: rows by `columns` inside a group, reading order across columns. */
3
+ export declare const moveResult: (sizes: readonly number[], columns: number, index: number, key: ResultKey, pageRows?: number) => number;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.moveResult = void 0;
4
+ const locate = (sizes, index) => {
5
+ let start = 0;
6
+ for (let group = 0; group < sizes.length; group += 1) {
7
+ if (index < start + sizes[group])
8
+ return { group, pos: index - start, start };
9
+ start += sizes[group];
10
+ }
11
+ return {
12
+ group: sizes.length - 1,
13
+ pos: 0,
14
+ start: start - sizes[sizes.length - 1],
15
+ };
16
+ };
17
+ const startOf = (sizes, group) => sizes.slice(0, group).reduce((sum, size) => sum + size, 0);
18
+ const stepVertical = (sizes, columns, index, down, wrap) => {
19
+ const { group, pos, start } = locate(sizes, index);
20
+ const size = sizes[group];
21
+ const column = pos % columns;
22
+ if (down) {
23
+ if (pos + columns < size)
24
+ return index + columns;
25
+ if (Math.floor(pos / columns) < Math.floor((size - 1) / columns))
26
+ return start + size - 1;
27
+ const next = group + 1 < sizes.length ? group + 1 : wrap ? 0 : -1;
28
+ if (next < 0)
29
+ return index;
30
+ return startOf(sizes, next) + Math.min(column, sizes[next] - 1);
31
+ }
32
+ if (pos - columns >= 0)
33
+ return index - columns;
34
+ const prev = group > 0 ? group - 1 : wrap ? sizes.length - 1 : -1;
35
+ if (prev < 0)
36
+ return index;
37
+ const lastRow = Math.floor((sizes[prev] - 1) / columns);
38
+ return (startOf(sizes, prev) + Math.min(sizes[prev] - 1, lastRow * columns + column));
39
+ };
40
+ /** Next flat result index for a key, following the visual grid: rows by `columns` inside a group, reading order across columns. */
41
+ const moveResult = (sizes, columns, index, key, pageRows = 1) => {
42
+ const total = sizes.reduce((sum, size) => sum + size, 0);
43
+ if (total === 0)
44
+ return 0;
45
+ switch (key) {
46
+ case "ArrowDown":
47
+ case "ArrowUp":
48
+ return stepVertical(sizes, columns, index, key === "ArrowDown", true);
49
+ case "ArrowRight":
50
+ return Math.min(total - 1, index + 1);
51
+ case "ArrowLeft":
52
+ return Math.max(0, index - 1);
53
+ case "Home":
54
+ return 0;
55
+ case "End":
56
+ return total - 1;
57
+ default: {
58
+ let next = index;
59
+ for (let i = 0; i < pageRows; i += 1)
60
+ next = stepVertical(sizes, columns, next, key === "PageDown", false);
61
+ return next;
62
+ }
63
+ }
64
+ };
65
+ exports.moveResult = moveResult;
@@ -1,4 +1,4 @@
1
1
  import type { SystemStyleObject } from "@chakra-ui/react";
2
2
  import type { PaletteProps } from "../../Theme/tokens/types";
3
- /** Shows a row affordance on row hover, row focus-within or its own keyboard focus, and always on touch. */
3
+ /** Shows a row affordance on row hover, row focus-within or its own keyboard focus, and always on touch, where it also grows to a 32px hit area. */
4
4
  export declare const revealStyles: (colors: PaletteProps) => SystemStyleObject;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.revealStyles = void 0;
4
4
  const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
5
5
  const motion_1 = require("../../Theme/chakra/motion");
6
- /** Shows a row affordance on row hover, row focus-within or its own keyboard focus, and always on touch. */
6
+ /** Shows a row affordance on row hover, row focus-within or its own keyboard focus, and always on touch, where it also grows to a 32px hit area. */
7
7
  const revealStyles = (colors) => {
8
8
  const ring = (0, focusRing_styles_1.focusVisibleStyles)(colors);
9
9
  return {
@@ -12,7 +12,7 @@ const revealStyles = (colors) => {
12
12
  _groupHover: { opacity: 1 },
13
13
  _groupFocusWithin: { opacity: 1 },
14
14
  "[data-reveal=true] &": { opacity: 1 },
15
- "@media (hover: none)": { opacity: 1 },
15
+ "@media (hover: none)": { opacity: 1, minWidth: "32px", width: "32px", height: "32px" },
16
16
  ...(0, motion_1.reducedMotionSx)(),
17
17
  };
18
18
  };
@@ -12,6 +12,7 @@ const accentText_1 = require("../../Theme/tokens/builders/accentText");
12
12
  const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
13
13
  const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
14
14
  const statusTone_1 = require("../../Theme/tokens/builders/statusTone");
15
+ const ToolTip_1 = __importDefault(require("../ToolTip/ToolTip"));
15
16
  const PersonChip_1 = __importDefault(require("../PersonChip/PersonChip"));
16
17
  const lucide_react_1 = require("lucide-react");
17
18
  // Roomy enough to hover without aiming: the glyph is only 1.25rem.
@@ -52,7 +53,7 @@ function NavBar({ userAvathar, userName, userRole, navMenu, moreIcon, handleNavO
52
53
  _focus: { bg: menuHoverBg },
53
54
  _active: { bg: rowBackgrounds.selectedActive },
54
55
  };
55
- return ((0, jsx_runtime_1.jsx)(react_1.Box, { bg: theme.colors.backgroundColor.light, px: 4, borderBottom: "1px solid", borderColor: theme.colors.boxborder[200], children: (0, jsx_runtime_1.jsxs)(react_1.Flex, { h: 16, alignItems: "center", justifyContent: "space-between", children: [(0, jsx_runtime_1.jsxs)(react_1.Box, { display: "flex", gap: 1.5, alignItems: "center", children: [(0, jsx_runtime_1.jsx)(react_1.Box, { as: "button", type: "button", onClick: () => onSideBarToggole === null || onSideBarToggole === void 0 ? void 0 : onSideBarToggole(), onMouseEnter: onSideBarPeek, onFocus: onSideBarPeek, "aria-label": sideBarToggole ? "Close navigation menu" : "Open navigation menu", "aria-expanded": !!sideBarToggole, ...iconButtonStyle, display: forceSideBarToggle ? "flex" : { base: "flex", lg: "none" }, w: SIDEBAR_TOGGLE_HIT, h: SIDEBAR_TOGGLE_HIT, children: sideBarToggole ? ((0, jsx_runtime_1.jsx)(lucide_react_1.PanelLeftClose, { size: "1.25rem" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.PanelRightClose, { size: "1.25rem" })) }), (navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.onIconClick) && ((0, jsx_runtime_1.jsx)(react_1.Box, { ...iconButtonStyle, as: "button", type: "button", "aria-label": "Go back", display: "flex", onClick: () => { var _a; return (_a = navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.onIconClick) === null || _a === void 0 ? void 0 : _a.call(navigationBarText); }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.CircleArrowLeft, { size: "1.25rem", color: theme.colors.text[500] }) })), leading && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [leading, (0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", "aria-hidden": "true", mx: 2, w: "1px", h: "1.625rem", bg: theme.colors.boxborder[200] })] })), (navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.text) && ((0, jsx_runtime_1.jsx)(react_1.Text, { color: (0, accentText_1.accentTextOnCanvas)(theme.colors.secondary, theme.colors.backgroundColor.light), fontSize: "1.25rem", fontWeight: 600, fontFamily: "open-sans, sans-serif", letterSpacing: "-0.013rem", children: navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.text }))] }), (0, jsx_runtime_1.jsx)(react_1.Flex, { minW: 0, flexShrink: 1, children: (0, jsx_runtime_1.jsxs)(react_1.Stack, { direction: "row", alignItems: "center", spacing: 4, minW: 0, children: [moreIcon && ((0, jsx_runtime_1.jsx)(react_1.Box, { display: "flex", alignItems: "center", minW: 0, children: moreIcon })), (0, jsx_runtime_1.jsxs)(react_1.Menu, { children: [(0, jsx_runtime_1.jsx)(react_1.MenuButton, { as: react_1.Button, rounded: "full", variant: "link", cursor: "pointer", minW: 0, flexShrink: 0, children: (0, jsx_runtime_1.jsx)(Avatar_1.default, { size: "sm", name: userName, src: userAvathar }) }), (0, jsx_runtime_1.jsx)(react_1.MenuList, { alignItems: "center", zIndex: 5, p: 0, py: "0.375rem",
56
+ return ((0, jsx_runtime_1.jsx)(react_1.Box, { bg: theme.colors.backgroundColor.light, px: 4, borderBottom: "1px solid", borderColor: theme.colors.boxborder[200], children: (0, jsx_runtime_1.jsxs)(react_1.Flex, { h: 16, alignItems: "center", justifyContent: "space-between", gap: 2, children: [(0, jsx_runtime_1.jsxs)(react_1.Box, { display: "flex", gap: 1.5, alignItems: "center", flex: "1 1 0%", children: [(0, jsx_runtime_1.jsx)(react_1.Box, { as: "button", type: "button", onClick: () => onSideBarToggole === null || onSideBarToggole === void 0 ? void 0 : onSideBarToggole(), onMouseEnter: onSideBarPeek, onFocus: onSideBarPeek, "aria-label": sideBarToggole ? "Close navigation menu" : "Open navigation menu", "aria-expanded": !!sideBarToggole, ...iconButtonStyle, flexShrink: 0, display: forceSideBarToggle ? "flex" : { base: "flex", lg: "none" }, w: SIDEBAR_TOGGLE_HIT, h: SIDEBAR_TOGGLE_HIT, children: sideBarToggole ? ((0, jsx_runtime_1.jsx)(lucide_react_1.PanelLeftClose, { size: "1.25rem" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.PanelRightClose, { size: "1.25rem" })) }), (navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.onIconClick) && ((0, jsx_runtime_1.jsx)(react_1.Box, { ...iconButtonStyle, as: "button", type: "button", "aria-label": "Go back", display: "flex", flexShrink: 0, onClick: () => { var _a; return (_a = navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.onIconClick) === null || _a === void 0 ? void 0 : _a.call(navigationBarText); }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.CircleArrowLeft, { size: "1.25rem", color: theme.colors.text[500] }) })), leading && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [leading, (0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", "aria-hidden": "true", mx: 2, w: "1px", h: "1.625rem", flexShrink: 0, bg: theme.colors.boxborder[200] })] })), (navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.text) && ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: typeof navigationBarText.text === "string" ? navigationBarText.text : "", overflowOnly: true, placement: "bottom", children: (0, jsx_runtime_1.jsx)(react_1.Text, { flex: "1 1 0%", w: 0, minW: 0, isTruncated: true, color: (0, accentText_1.accentTextOnCanvas)(theme.colors.secondary, theme.colors.backgroundColor.light), fontSize: "1.25rem", fontWeight: 600, fontFamily: "open-sans, sans-serif", letterSpacing: "-0.013rem", children: navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.text }) }))] }), (0, jsx_runtime_1.jsx)(react_1.Flex, { minW: 0, flexShrink: 1, children: (0, jsx_runtime_1.jsxs)(react_1.Stack, { direction: "row", alignItems: "center", spacing: 4, minW: 0, children: [moreIcon && ((0, jsx_runtime_1.jsx)(react_1.Box, { display: "flex", alignItems: "center", minW: 0, sx: { "& > *": { minW: 0 } }, children: moreIcon })), (0, jsx_runtime_1.jsxs)(react_1.Menu, { children: [(0, jsx_runtime_1.jsx)(react_1.MenuButton, { as: react_1.Button, rounded: "full", variant: "link", cursor: "pointer", minW: 0, flexShrink: 0, children: (0, jsx_runtime_1.jsx)(Avatar_1.default, { size: "sm", name: userName, src: userAvathar }) }), (0, jsx_runtime_1.jsx)(react_1.MenuList, { alignItems: "center", zIndex: 5, p: 0, py: "0.375rem",
56
57
  // Without a cap one long title stretches the whole dropdown; titles truncate instead.
57
58
  maxW: "18rem", bg: theme.colors.surfaceRaised,
58
59
  // NOTE: Chakra's MenuItem inherits --menu-bg hardcoded to #fff via dead _dark — set it or items stay white.
@@ -6,6 +6,7 @@ export type NavbarProps = {
6
6
  userName: string;
7
7
  userRole?: string;
8
8
  navMenu: MenuProps[];
9
+ /** Right-cluster slot before the avatar (org switcher, bell, launcher trigger). Its child may shrink; give icon buttons `flexShrink={0}`. */
9
10
  moreIcon?: ReactNode;
10
11
  handleNavOnClick: (url: string) => void;
11
12
  handleLogout: () => void;
@@ -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
  const jsx_runtime_1 = require("react/jsx-runtime");
4
7
  const react_1 = require("react");
@@ -10,12 +13,15 @@ const chartColorsFromTheme_1 = require("../../Theme/tokens/builders/chartColorsF
10
13
  const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
11
14
  const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
12
15
  const useExclusiveOverlay_1 = require("../../Hooks/useExclusiveOverlay");
16
+ const useMatchMedia_1 = require("../../Hooks/useMatchMedia");
17
+ const ToolTip_1 = __importDefault(require("../ToolTip/ToolTip"));
13
18
  const OrgSwitcherProps_1 = require("./OrgSwitcherProps");
14
19
  const sizeMap = {
15
- sm: { h: "32px", avatar: "22px", font: "0.8rem", sub: "0.65rem", px: 2 },
16
- md: { h: "40px", avatar: "28px", font: "0.9rem", sub: "0.7rem", px: 3 },
17
- lg: { h: "48px", avatar: "34px", font: "1rem", sub: "0.78rem", px: 4 },
20
+ sm: { h: "32px", avatarH: "32px", avatar: "22px", font: "0.8rem", sub: "0.65rem", px: 2 },
21
+ md: { h: "40px", avatarH: "36px", avatar: "28px", font: "0.9rem", sub: "0.7rem", px: 3 },
22
+ lg: { h: "48px", avatarH: "48px", avatar: "34px", font: "1rem", sub: "0.78rem", px: 4 },
18
23
  };
24
+ const MD_QUERY = "(min-width: 48em)";
19
25
  const Avatar = ({ org, boxSize, fontSize, }) => {
20
26
  const { colors } = (0, useCustomTheme_1.useCustomTheme)();
21
27
  if (org.logoUrl) {
@@ -24,9 +30,11 @@ const Avatar = ({ org, boxSize, fontSize, }) => {
24
30
  const fill = (0, OrgSwitcherProps_1.avatarColor)(org.name, (0, chartColorsFromTheme_1.chartMode)(colors));
25
31
  return ((0, jsx_runtime_1.jsx)(react_2.Flex, { align: "center", justify: "center", boxSize: boxSize, borderRadius: "lg", flexShrink: 0, bg: fill, color: (0, accentText_1.onFilled)(fill, colors.white, colors.black), fontSize: fontSize, fontWeight: 700, children: (0, OrgSwitcherProps_1.orgInitial)(org.name) }));
26
32
  };
27
- const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSearchable, searchThreshold = 6, triggerVariant = "ghost", size = "md", maxWidth = "200px", menuMinWidth = "280px", placeholder = "Select organization", ariaLabel = "Switch organization", showRole = true, }) => {
28
- var _a;
33
+ const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSearchable, searchThreshold = 6, triggerVariant = "ghost", size = "md", maxWidth = "200px", menuMinWidth = "280px", placeholder = "Select organization", ariaLabel = "Switch organization", showRole = true, display = "full", }) => {
34
+ var _a, _b;
29
35
  const theme = (0, useCustomTheme_1.useCustomTheme)();
36
+ const isMd = (0, useMatchMedia_1.useMatchMedia)(MD_QUERY, true);
37
+ const avatarOnly = display === "avatar" || (display === "auto" && !isMd);
30
38
  const rowBackgrounds = (0, react_1.useMemo)(() => (0, rowStates_1.listRowBackgrounds)(theme.colors), [theme.colors]);
31
39
  const s = sizeMap[size];
32
40
  const { isOpen, onOpen, onClose } = (0, react_2.useDisclosure)();
@@ -87,10 +95,14 @@ const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSear
87
95
  if (org.id !== value)
88
96
  onChange(org.id, org);
89
97
  };
98
+ const avatarNode = selected ? ((0, jsx_runtime_1.jsx)(Avatar, { org: selected, boxSize: s.avatar, fontSize: s.sub })) : ((0, jsx_runtime_1.jsx)(react_2.Flex, { align: "center", justify: "center", boxSize: s.avatar, borderRadius: "lg", bg: theme.colors.gray[100], color: (0, accentText_1.accentTextOnCanvas)(theme.colors.gray, theme.colors.gray[100]), flexShrink: 0, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Building2, { size: 14 }) }));
99
+ const labelGap = s.px === 2 ? 8 : 10;
100
+ const squeezedWidth = `calc(${s.px * 8 + labelGap * 2 + 16}px + ${s.avatar})`;
101
+ const trigger = ((0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "button", type: "button", "aria-label": avatarOnly && selected ? `${ariaLabel}, ${selected.name}` : ariaLabel, align: "center", justify: avatarOnly ? "center" : undefined, gap: avatarOnly ? "4px" : `${labelGap}px`, h: avatarOnly ? s.avatarH : s.h, px: avatarOnly ? "6px" : s.px, minW: avatarOnly ? undefined : squeezedWidth, flexShrink: avatarOnly ? 0 : 1, borderRadius: "lg", cursor: isLoading ? "default" : "pointer", opacity: isLoading ? 0.6 : 1, pointerEvents: isLoading ? "none" : "auto", transition: "background 0.15s ease", _hover: { bg: triggerVariant === "ghost" ? triggerHoverBg : undefined }, ...triggerStyles, children: [avatarNode, !avatarOnly && ((0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: s.font, fontWeight: 600, color: theme.colors.gray[700], maxW: maxWidth, minW: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", textAlign: "left", children: (_a = selected === null || selected === void 0 ? void 0 : selected.name) !== null && _a !== void 0 ? _a : placeholder })), isLoading ? ((0, jsx_runtime_1.jsx)(react_2.Spinner, { size: "xs", thickness: "1.5px", color: theme.colors.accentText, flexShrink: 0 })) : avatarOnly ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.ChevronDown, size: 12, color: theme.colors.textMuted, flexShrink: 0, "aria-hidden": "true" })) : ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.ChevronsUpDown, size: 16, color: theme.colors.textMuted, flexShrink: 0 }))] }));
90
102
  return ((0, jsx_runtime_1.jsxs)(react_2.Popover, { isOpen: isOpen, onOpen: handlePopoverOpen, onClose: () => {
91
103
  onClose();
92
104
  setQuery("");
93
- }, placement: "bottom-start", initialFocusRef: showSearch ? searchRef : firstOptionRef, isLazy: true, children: [(0, jsx_runtime_1.jsx)(react_2.PopoverTrigger, { children: (0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "button", type: "button", "aria-label": ariaLabel, align: "center", gap: s.px === 2 ? "8px" : "10px", h: s.h, px: s.px, borderRadius: "lg", cursor: isLoading ? "default" : "pointer", opacity: isLoading ? 0.6 : 1, pointerEvents: isLoading ? "none" : "auto", transition: "background 0.15s ease", _hover: { bg: triggerVariant === "ghost" ? triggerHoverBg : undefined }, ...triggerStyles, children: [selected ? ((0, jsx_runtime_1.jsx)(Avatar, { org: selected, boxSize: s.avatar, fontSize: s.sub })) : ((0, jsx_runtime_1.jsx)(react_2.Flex, { align: "center", justify: "center", boxSize: s.avatar, borderRadius: "lg", bg: theme.colors.gray[100], color: (0, accentText_1.accentTextOnCanvas)(theme.colors.gray, theme.colors.gray[100]), flexShrink: 0, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Building2, { size: 14 }) })), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: s.font, fontWeight: 600, color: theme.colors.gray[700], maxW: maxWidth, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", textAlign: "left", children: (_a = selected === null || selected === void 0 ? void 0 : selected.name) !== null && _a !== void 0 ? _a : placeholder }), isLoading ? ((0, jsx_runtime_1.jsx)(react_2.Spinner, { size: "xs", thickness: "1.5px", color: theme.colors.accentText, flexShrink: 0 })) : ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.ChevronsUpDown, size: 16, color: theme.colors.textMuted, flexShrink: 0 }))] }) }), (0, jsx_runtime_1.jsx)(react_2.PopoverContent, { minW: menuMinWidth, w: "auto",
105
+ }, placement: "bottom-start", initialFocusRef: showSearch ? searchRef : firstOptionRef, isLazy: true, children: [avatarOnly ? ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: (_b = selected === null || selected === void 0 ? void 0 : selected.name) !== null && _b !== void 0 ? _b : placeholder, placement: "bottom", isDisabled: isOpen, children: (0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexShrink: 0, children: (0, jsx_runtime_1.jsx)(react_2.PopoverTrigger, { children: trigger }) }) })) : ((0, jsx_runtime_1.jsx)(react_2.PopoverTrigger, { children: trigger })), (0, jsx_runtime_1.jsx)(react_2.PopoverContent, { minW: menuMinWidth, maxW: "calc(100vw - 32px)", w: "auto",
94
106
  // NOTE: Chakra's --popper-bg is hardcoded white via dead _dark — without an explicit bg the panel stayed white.
95
107
  bg: theme.colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", boxShadow: `0 10px 30px ${theme.colors.boxShadow.default}`, _focus: { outline: "none", boxShadow: `0 10px 30px ${theme.colors.boxShadow.default}` }, children: (0, jsx_runtime_1.jsxs)(react_2.PopoverBody, { p: 0, children: [showSearch && ((0, jsx_runtime_1.jsx)(react_2.Box, { p: 2, borderBottom: "1px solid", borderColor: theme.colors.gray[100], children: (0, jsx_runtime_1.jsxs)(react_2.InputGroup, { size: "sm", children: [(0, jsx_runtime_1.jsx)(react_2.InputLeftElement, { pointerEvents: "none", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, color: theme.colors.textMuted }) }), (0, jsx_runtime_1.jsx)(react_2.Input, { ref: searchRef, placeholder: "Search organizations", value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: (e) => {
96
108
  if (e.key === "ArrowDown" && filtered.length > 0) {
@@ -11,6 +11,8 @@ export interface OrgOption {
11
11
  }
12
12
  /** Trigger size; looked up in a fixed sizeMap for height, avatar and font sizing. */
13
13
  export type OrgSwitcherSize = "sm" | "md" | "lg";
14
+ /** Trigger layout: avatar + name + chevron ("full"), avatar + chevron only ("avatar"), or "auto" to switch at the `md` breakpoint. */
15
+ export type OrgSwitcherDisplay = "full" | "avatar" | "auto";
14
16
  /** Session-scoped org switcher (trigger+popover) — not an admin screen; see OrganizationDetails for create/rename/delete/default. Closed prop list, no ...rest/ref on the trigger button. */
15
17
  export interface OrgSwitcherProps {
16
18
  /** Organizations to choose from. */
@@ -39,6 +41,8 @@ export interface OrgSwitcherProps {
39
41
  ariaLabel?: string;
40
42
  /** Show the role subtitle inside the dropdown rows. Default true. */
41
43
  showRole?: boolean;
44
+ /** "avatar" shows only the org avatar and a small chevron, name moves to the accessible name and a tooltip; "auto" is avatar below `md`, full from `md` up. Default "full". */
45
+ display?: OrgSwitcherDisplay;
42
46
  }
43
47
  export declare const AVATAR_COLORS: readonly string[];
44
48
  /** Chart hues are tuned to sit apart from each other, not to carry a label. */
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Flex } from "@chakra-ui/react";
3
+ import { useRef } from "react";
4
+ import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
5
+ import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
6
+ import { useCustomTheme } from "../../Theme/useCustomTheme.js";
7
+ export const tabId = (base, id) => `${base}-tab-${id}`;
8
+ export const panelId = (base) => `${base}-panel`;
9
+ export function LauncherTabs({ tabs, selected, onSelect, idBase, label, onLeaveUp, onLeaveDown, }) {
10
+ const { colors } = useCustomTheme();
11
+ const refs = useRef(new Map());
12
+ const ring = focusVisibleStyles(colors);
13
+ const choose = (index) => {
14
+ var _a;
15
+ const next = tabs[(index + tabs.length) % tabs.length];
16
+ onSelect(next.id);
17
+ (_a = refs.current.get(next.id)) === null || _a === void 0 ? void 0 : _a.focus();
18
+ };
19
+ const onKeyDown = (event) => {
20
+ if (event.altKey || event.ctrlKey || event.metaKey)
21
+ return;
22
+ const current = tabs.findIndex((tab) => tab.id === selected);
23
+ const handlers = {
24
+ ArrowRight: () => choose(current + 1),
25
+ ArrowLeft: () => choose(current - 1),
26
+ Home: () => choose(0),
27
+ End: () => choose(tabs.length - 1),
28
+ ArrowUp: onLeaveUp,
29
+ ArrowDown: onLeaveDown,
30
+ };
31
+ const handle = handlers[event.key];
32
+ if (!handle)
33
+ return;
34
+ event.preventDefault();
35
+ event.stopPropagation();
36
+ handle();
37
+ };
38
+ return (_jsx(Flex, { role: "tablist", "aria-label": label, "aria-orientation": "horizontal", flexShrink: 0, px: { base: "10px", md: "16px" }, gap: { base: 0, md: "4px" }, borderBottomWidth: "1px", borderColor: colors.boxborder[200], bg: colors.surfaceRaised, onKeyDown: onKeyDown, children: tabs.map((tab) => {
39
+ const isSelected = tab.id === selected;
40
+ return (_jsx(Box, { as: "button", type: "button", role: "tab", id: tabId(idBase, tab.id), ref: (node) => {
41
+ if (node)
42
+ refs.current.set(tab.id, node);
43
+ else
44
+ refs.current.delete(tab.id);
45
+ }, "aria-selected": isSelected, "aria-controls": isSelected ? panelId(idBase) : undefined, tabIndex: isSelected ? 0 : -1, onClick: () => onSelect(tab.id), position: "relative", flex: { base: 1, md: "none" }, minW: { md: "112px" }, h: "46px", px: { base: "6px", md: "14px" }, whiteSpace: "nowrap", fontSize: "14px", fontWeight: isSelected ? 600 : 500, color: isSelected ? colors.accentText : colors.textMuted, transition: transitionFor(["color"]), _hover: {
46
+ color: isSelected ? colors.accentText : colors.text[900],
47
+ }, _after: {
48
+ content: '""',
49
+ position: "absolute",
50
+ left: "10px",
51
+ right: "10px",
52
+ bottom: "-1px",
53
+ h: "2px",
54
+ borderRadius: "2px 2px 0 0",
55
+ bg: isSelected ? colors.primary[500] : "transparent",
56
+ }, sx: { ...ring, borderRadius: "8px" }, children: tab.label }, tab.id));
57
+ }) }));
58
+ }