@bitrise/bitkit-v2 0.3.478 → 0.3.479

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.
@@ -95,6 +95,14 @@ export interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {
95
95
  * Pair it with `size="sm"` actions — see `children`.
96
96
  */
97
97
  isSticky?: boolean;
98
+ /**
99
+ * Opens the page's sidebar on mobile. Its presence switches the header to the design's
100
+ * `hasSidebar` layout below `tablet`: the title shrinks beside a menu button, and the
101
+ * `sectionTitle` takes over as the large heading under a rule, with the actions below it. Wire it
102
+ * to a `BitkitPageSidebar`'s `open` / `onOpenChange`. From `tablet` the button is hidden and the
103
+ * header is the ordinary one — the sidebar is beside the page there.
104
+ */
105
+ onMenuClick?: () => void;
98
106
  /**
99
107
  * Descriptive text below the title. Hidden in the collapsed layout.
100
108
  *
@@ -116,6 +124,12 @@ export interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {
116
124
  * the tab list from `tablet`: the line stays although no tabs show above it.
117
125
  */
118
126
  showDivider?: boolean;
127
+ /**
128
+ * The sidebar section the page is showing, for the `hasSidebar` layout — see `onMenuClick`. It
129
+ * is the page's visible `h2` on mobile, where the sidebar that would name it is behind the menu
130
+ * button. Not rendered from `tablet`, and never without `onMenuClick`.
131
+ */
132
+ sectionTitle?: string;
119
133
  /**
120
134
  * Tab bar along the bottom edge — pass a `BitkitTabs.List`. Hidden in the collapsed layout.
121
135
  *
@@ -1,4 +1,6 @@
1
+ import IconMenuHamburger from "../../icons/IconMenuHamburger.js";
1
2
  import { isEmptyNode } from "../../utilities/isEmptyNode.js";
3
+ import BitkitIconButton from "../BitkitIconButton/BitkitIconButton.js";
2
4
  import BitkitOverflowTooltip from "../BitkitOverflowTooltip/BitkitOverflowTooltip.js";
3
5
  import useIsTruncated from "../../utilities/useIsTruncated.js";
4
6
  import { Box } from "@chakra-ui/react/box";
@@ -8,12 +10,14 @@ import { jsx, jsxs } from "react/jsx-runtime";
8
10
  import { Text } from "@chakra-ui/react/text";
9
11
  //#region lib/components/BitkitPageHeader/BitkitPageHeader.tsx
10
12
  var BitkitPageHeader = forwardRef((props, ref) => {
11
- const { avatar, badge, breadcrumb, children, contentAlign, contentVariant, contentWidth, controls, css, isSticky, secondaryText, showDivider = true, tabs, title, variant, ...rest } = props;
13
+ const { avatar, badge, breadcrumb, children, contentAlign, contentVariant, contentWidth, controls, css, isSticky, onMenuClick, secondaryText, sectionTitle, showDivider = true, tabs, title, variant, ...rest } = props;
12
14
  const hasAvatar = !isEmptyNode(avatar);
13
15
  const hasBadge = !isEmptyNode(badge);
14
16
  const hasBreadcrumb = !isEmptyNode(breadcrumb);
15
17
  const hasActions = !isEmptyNode(children);
16
18
  const hasSecondaryText = !isEmptyNode(secondaryText);
19
+ const hasSidebar = onMenuClick !== void 0;
20
+ const hasSectionTitle = hasSidebar && !isEmptyNode(sectionTitle);
17
21
  const hasTabs = !isEmptyNode(tabs);
18
22
  const { isTruncated: isTitleTruncated, ref: titleRef } = useIsTruncated(title);
19
23
  const { isTruncated: isSecondaryTruncated, ref: secondaryRef } = useIsTruncated(secondaryText);
@@ -25,6 +29,7 @@ var BitkitPageHeader = forwardRef((props, ref) => {
25
29
  hasBreadcrumb,
26
30
  hasControls: showControls,
27
31
  hasDivider: showDivider || hasTabs,
32
+ hasSidebar,
28
33
  alignsToContent: Boolean(contentWidth),
29
34
  contentAlign,
30
35
  isSticky,
@@ -34,6 +39,43 @@ var BitkitPageHeader = forwardRef((props, ref) => {
34
39
  css: styles.controls,
35
40
  children: controls
36
41
  }) : null;
42
+ const titleBlock = /* @__PURE__ */ jsxs(Box, {
43
+ css: styles.titleBlock,
44
+ children: [hasAvatar && /* @__PURE__ */ jsx(Box, {
45
+ css: styles.avatar,
46
+ children: avatar
47
+ }), /* @__PURE__ */ jsxs(Box, {
48
+ css: styles.textBlock,
49
+ children: [/* @__PURE__ */ jsxs(Box, {
50
+ css: styles.titleRow,
51
+ children: [/* @__PURE__ */ jsx(BitkitOverflowTooltip, {
52
+ disabled: !canShowTitleTooltip,
53
+ placement: "bottom-start",
54
+ text: title,
55
+ children: /* @__PURE__ */ jsx(Text, {
56
+ as: "h1",
57
+ css: styles.title,
58
+ ref: titleRef,
59
+ tabIndex: canShowTitleTooltip ? 0 : void 0,
60
+ children: title
61
+ })
62
+ }), hasBadge && /* @__PURE__ */ jsx(Box, {
63
+ css: styles.badge,
64
+ children: badge
65
+ })]
66
+ }), hasSecondaryText && secondaryText !== void 0 && /* @__PURE__ */ jsx(BitkitOverflowTooltip, {
67
+ disabled: !isSecondaryTruncated,
68
+ placement: "bottom-start",
69
+ text: secondaryText,
70
+ children: /* @__PURE__ */ jsx(Text, {
71
+ css: styles.secondaryText,
72
+ ref: secondaryRef,
73
+ tabIndex: isSecondaryTruncated ? 0 : void 0,
74
+ children: secondaryText
75
+ })
76
+ })]
77
+ })]
78
+ });
37
79
  return /* @__PURE__ */ jsxs(Box, {
38
80
  as: "header",
39
81
  css: [styles.root, css],
@@ -51,42 +93,26 @@ var BitkitPageHeader = forwardRef((props, ref) => {
51
93
  /* @__PURE__ */ jsxs(Box, {
52
94
  css: styles.contentBlock,
53
95
  children: [
54
- /* @__PURE__ */ jsxs(Box, {
55
- css: styles.titleBlock,
56
- children: [hasAvatar && /* @__PURE__ */ jsx(Box, {
57
- css: styles.avatar,
58
- children: avatar
59
- }), /* @__PURE__ */ jsxs(Box, {
60
- css: styles.textBlock,
61
- children: [/* @__PURE__ */ jsxs(Box, {
62
- css: styles.titleRow,
63
- children: [/* @__PURE__ */ jsx(BitkitOverflowTooltip, {
64
- disabled: !canShowTitleTooltip,
65
- placement: "bottom-start",
66
- text: title,
67
- children: /* @__PURE__ */ jsx(Text, {
68
- as: "h1",
69
- css: styles.title,
70
- ref: titleRef,
71
- tabIndex: canShowTitleTooltip ? 0 : void 0,
72
- children: title
73
- })
74
- }), hasBadge && /* @__PURE__ */ jsx(Box, {
75
- css: styles.badge,
76
- children: badge
77
- })]
78
- }), hasSecondaryText && secondaryText !== void 0 && /* @__PURE__ */ jsx(BitkitOverflowTooltip, {
79
- disabled: !isSecondaryTruncated,
80
- placement: "bottom-start",
81
- text: secondaryText,
82
- children: /* @__PURE__ */ jsx(Text, {
83
- css: styles.secondaryText,
84
- ref: secondaryRef,
85
- tabIndex: isSecondaryTruncated ? 0 : void 0,
86
- children: secondaryText
87
- })
88
- })]
96
+ hasSidebar ? /* @__PURE__ */ jsxs(Box, {
97
+ css: styles.titleBar,
98
+ children: [titleBlock, /* @__PURE__ */ jsx(Box, {
99
+ css: styles.menuButton,
100
+ children: /* @__PURE__ */ jsx(BitkitIconButton, {
101
+ icon: IconMenuHamburger,
102
+ label: "Open navigation",
103
+ onClick: onMenuClick,
104
+ size: "sm",
105
+ variant: "secondary"
106
+ })
89
107
  })]
108
+ }) : titleBlock,
109
+ hasSectionTitle && /* @__PURE__ */ jsx(Box, {
110
+ css: styles.sectionBlock,
111
+ children: /* @__PURE__ */ jsx(Text, {
112
+ as: "h2",
113
+ css: styles.sectionTitle,
114
+ children: sectionTitle
115
+ })
90
116
  }),
91
117
  !hasBreadcrumb && controlsNode,
92
118
  hasActions && /* @__PURE__ */ jsx(Box, {
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitPageHeader.js","names":[],"sources":["../../../lib/components/BitkitPageHeader/BitkitPageHeader.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type ReactNode } from 'react';\n\nimport { isEmptyNode } from '../../utilities/isEmptyNode';\nimport useIsTruncated from '../../utilities/useIsTruncated';\nimport BitkitOverflowTooltip from '../BitkitOverflowTooltip/BitkitOverflowTooltip';\n\nexport interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {\n /**\n * Avatar, app icon or thumbnail rendered before the title. Hidden in the collapsed layout.\n *\n * Belongs to the sub-page layouts: the design's main-page header is a title and its subtitle with\n * no avatar, so pass this alongside a `breadcrumb`.\n *\n * Size it to match the design's two sub-page cards, since the node is yours to build: `size=\"40\"`\n * (8px radius) against a bare title, and `size=\"48\"` (10px radius) once `secondaryText` is\n * present — the same avatar the `app` and `user` content shapes use.\n */\n avatar?: ReactNode;\n /** Badge rendered next to the title. Hidden in the collapsed layout. */\n badge?: ReactNode;\n /**\n * Breadcrumb trail above the title — pass a `BitkitBreadcrumb`. Its presence is what switches the\n * header from the design's main-page layout to a sub-page one, so there is no separate page-type\n * prop: the recipe's own `hasBreadcrumb` variant is derived from it.\n *\n * The two layouts carry more than the trail. A main-page header is a title and its subtitle —\n * top-level pages and product overviews — with no avatar and no `app`/`user` content shape. Those\n * belong to sub-pages, so pass them together with a trail.\n */\n breadcrumb?: ReactNode;\n /**\n * Trailing action buttons. Stacked full-width below the title on mobile, primary action on top.\n *\n * The collapsed row is built for `size=\"sm\"` buttons and the expanded header for `size=\"md\"`.\n * Actions arrive as children, so the component cannot set that for you — pass a flat `size=\"sm\"`\n * whenever `minimal` or `isSticky` is in play.\n *\n * Flat, not responsive: Bitkit icons render a different SVG asset per size, so `BitkitButton`\n * resolves `size` in JS and a `{ base, tablet }` object matches no key, falling through to a 24px\n * icon in a 32px button. Since the header collapses in CSS from `tablet` up, a `sm` action is a\n * little small in the expanded mobile layout — but the design draws no mobile frame for the\n * collapsed variants, so `sm` is the closer of the two.\n */\n children?: ReactNode;\n /**\n * Where the content block sits when `contentWidth` caps it: `center` over a centred content\n * column, `start` over one that starts against a sidebar beside the header. Pass the same\n * alignment as that column, so the title and the content share their left edge at every width.\n *\n * No effect with `contentWidth=\"full\"` or without `contentWidth`, where nothing is capped.\n */\n contentAlign?: 'center' | 'start';\n /**\n * Shape of the title block, mirroring the design's `content` property. `title` is a page title\n * with an optional subtitle; `app` and `user` are the tighter entity layouts — a larger avatar\n * centred against a smaller title and 14px secondary text. They differ only in the gap between\n * the avatar and the text, 16 and 12 respectively.\n */\n contentVariant?: 'title' | 'app' | 'user';\n /**\n * Lines the header's content up with the page content below it. The root stays full-width, so\n * the background and the bottom border still run edge to edge — which a wrapper around the whole\n * header could not do.\n *\n * Pass the same value as that column's `max-width`, as rems — `rem()` builds one from a pixel\n * figure — and the content is capped with it, centred unless `contentAlign` says otherwise. Box-sizing is border-box throughout\n * Bitkit, so the value includes the header's own inline padding, which this layout puts at 32 on\n * both sides rather than the default 24 and 32.\n *\n * `'full'` is the same layout without the cap, for a content column that runs the full width: the\n * 32 on both sides, nothing to centre. The default 24 is for a header that runs over a sidebar and\n * lines up with its items instead.\n *\n * Its presence is the toggle: without it the header keeps the default inset.\n *\n * The cap applies at every breakpoint, deliberately — a content column's own `max-width` is not\n * breakpoint-scoped either, so scoping this one would misalign the two wherever the column is\n * narrow enough to bite on mobile. What does change with the breakpoint is the inline padding,\n * 16 below `tablet` and 32 above, which is the same pair such a column uses.\n */\n contentWidth?: `${number}rem` | 'full';\n /**\n * Inline controls between the trail and the actions — a view switcher, filters, a settings menu.\n * Their presence spreads the row into three groups instead of the usual trail-and-actions pair,\n * which is the shape the design file calls `PageHeader-WFE`.\n *\n * Belongs to the collapsed row, so it is honoured by `variant=\"minimal\"` *and* by `isSticky` —\n * the two produce the same row — and ignored in the expanded layouts, which the design draws no\n * controls in.\n */\n controls?: ReactNode;\n /**\n * Renders the compact single-row layout used while the header is stuck to the top of the viewport.\n * Appearance only — the consumer owns scroll detection and any portalling, since the sticky offset\n * and header container are application concerns.\n *\n * Ignored below the `tablet` breakpoint, where the header always stays expanded.\n *\n * Pair it with `size=\"sm\"` actions — see `children`.\n */\n isSticky?: boolean;\n /**\n * Descriptive text below the title. Hidden in the collapsed layout.\n *\n * A string, like `title`, so it can be truncated to one line and carried in full by the tooltip\n * that appears when it does not fit.\n */\n secondaryText?: string;\n /**\n * Draws the line along the header's bottom edge. Set it to `false` for a header that flows\n * straight into the page, and the bottom padding goes with the line: the header's background is\n * the page's, so without the line that padding would only be a margin the page cannot shrink. The\n * page spaces its content from the header itself.\n *\n * The collapsed row keeps its padding, which centres the row rather than spacing the page, and from\n * `tablet` a header with `isSticky` keeps its line as well, since content scrolls under it. Below\n * `tablet` `isSticky` is ignored, so there the line goes too.\n *\n * Ignored with `tabs`, whose list sits on that edge. That includes `variant=\"minimal\"`, which hides\n * the tab list from `tablet`: the line stays although no tabs show above it.\n */\n showDivider?: boolean;\n /**\n * Tab bar along the bottom edge — pass a `BitkitTabs.List`. Hidden in the collapsed layout.\n *\n * The node is rendered inside the header, so tab panels cannot be siblings of the row. To drive\n * page content from it, put `BitkitTabs.Root` around the header *and* the content, and pass only\n * the list here — `BitkitTabs.List` reads its Root from context and throws without an ancestor:\n *\n * ```tsx\n * <BitkitTabs.Root defaultValue=\"overview\">\n * <BitkitPageHeader tabs={<BitkitTabs.List>…</BitkitTabs.List>} title=\"Build Hub\" />\n * <BitkitTabs.ContentGroup>\n * <BitkitTabs.Content padding=\"32\" value=\"overview\">…</BitkitTabs.Content>\n * </BitkitTabs.ContentGroup>\n * </BitkitTabs.Root>\n * ```\n *\n * Only the row is hidden when the header collapses — the panels live outside it and keep\n * rendering, so the page never blanks.\n */\n tabs?: ReactNode;\n /**\n * The page title, as a string. Everything passed here lands inside the `h1`, so it is the\n * heading's accessible name and nothing else belongs in it — the avatar, badge and secondary\n * text each have their own slot. Required: it is what gives the header a page heading at every\n * breakpoint.\n *\n * Truncated to a single line with an ellipsis. When it does not fit, hovering or focusing it\n * shows the full string in a tooltip — so a long title never grows the header.\n *\n * In the collapsed layout (`minimal` or `isSticky`) *with* a `breadcrumb`, the design shows the\n * title in the trail rather than as a heading, so end the breadcrumb with a\n * `BitkitBreadcrumb.CurrentItem` carrying it — the title is then rendered as a visually hidden\n * `h1`, since `Breadcrumb.CurrentLink` is a link and not a heading. Below `tablet` the header\n * never collapses and the visible heading is used instead (`BitkitBreadcrumb` drops its\n * `CurrentItem` on mobile).\n */\n title: string;\n /**\n * `minimal` collapses the header to the single breadcrumb row used by sub-pages that don't repeat\n * their title as a heading. Like `isSticky`, it only collapses from `tablet` up, and expects\n * `size=\"sm\"` actions — see `children`.\n */\n variant?: 'default' | 'minimal';\n}\n\nconst BitkitPageHeader = forwardRef<HTMLElement, BitkitPageHeaderProps>((props, ref) => {\n const {\n avatar,\n badge,\n breadcrumb,\n children,\n contentAlign,\n contentVariant,\n contentWidth,\n controls,\n css,\n isSticky,\n secondaryText,\n showDivider = true,\n tabs,\n title,\n variant,\n ...rest\n } = props;\n\n // Which slots the collapsed row drops, and whether it collapses at all, is decided entirely by\n // the recipe's `tablet`-scoped styles. Nothing here branches on the viewport: a breakpoint hook\n // would render the wrong layout on first paint, since Chakra's `useMediaQuery` returns its\n // fallback until after mount.\n\n // A slot's presence is its own toggle, so all of them share one emptiness test rather than plain\n // truthiness: an array whose entries all render nothing — `items.map()` where every branch is\n // `null` — is still truthy, and would otherwise render a wrapper that only contributes gap. For\n // `controls` it would also spread the row into three groups around an empty middle one.\n const hasAvatar = !isEmptyNode(avatar);\n const hasBadge = !isEmptyNode(badge);\n const hasBreadcrumb = !isEmptyNode(breadcrumb);\n const hasActions = !isEmptyNode(children);\n const hasSecondaryText = !isEmptyNode(secondaryText);\n const hasTabs = !isEmptyNode(tabs);\n\n // `BitkitOverflowTooltip` does no measuring of its own — it shows the tooltip unless told not to,\n // so each truncating text reports whether it is actually clipped and suppresses its own tooltip\n // when it is not. Keyed on the text so a change re-measures without waiting for a resize.\n const { isTruncated: isTitleTruncated, ref: titleRef } = useIsTruncated<HTMLHeadingElement>(title);\n const { isTruncated: isSecondaryTruncated, ref: secondaryRef } = useIsTruncated<HTMLParagraphElement>(secondaryText);\n\n // The design only draws inline controls in the collapsed row, so they are ignored in the\n // expanded layouts rather than left to land somewhere undesigned. `minimal` and `isSticky`\n // produce that row through the same `collapsedSlots`, so both have to honour them — gating on\n // `minimal` alone left `isSticky` rendering the identical row with the middle group missing.\n const isCollapsed = variant === 'minimal' || Boolean(isSticky);\n const showControls = !isEmptyNode(controls) && isCollapsed;\n\n // The collapsed row with a trail makes the title block `srOnly`, which is a 1px box — so the\n // measurement reports \"truncated\" for a heading nobody can see. Left alone that puts a tab stop\n // on an invisible element and opens a tooltip against it. The trail already carries the title as\n // its current item in exactly that layout, so there is nothing to surface anyway.\n const canShowTitleTooltip = isTitleTruncated && !(isCollapsed && hasBreadcrumb);\n\n const recipe = useSlotRecipe({ key: 'pageHeader' });\n const styles = recipe({\n contentVariant,\n hasBreadcrumb,\n hasControls: showControls,\n hasDivider: showDivider || hasTabs,\n // The cap is a length the consumer chooses, so it cannot be a recipe variant -- the variant\n // carries the layout switch and the `maxWidth` below carries the value.\n alignsToContent: Boolean(contentWidth),\n contentAlign,\n isSticky,\n variant,\n });\n\n // The controls are the middle of three groups, so they follow whichever element holds the left\n // slot: the trail when there is one, otherwise the title — which lives inside `contentBlock`, so\n // they have to go in there with it rather than beside it.\n const controlsNode = showControls ? <Box css={styles.controls}>{controls}</Box> : null;\n\n return (\n <Box as=\"header\" css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.headerBlock} maxWidth={contentWidth === 'full' ? undefined : contentWidth}>\n {hasBreadcrumb && <Box css={styles.breadcrumb}>{breadcrumb}</Box>}\n\n {hasBreadcrumb && controlsNode}\n\n <Box css={styles.contentBlock}>\n <Box css={styles.titleBlock}>\n {hasAvatar && <Box css={styles.avatar}>{avatar}</Box>}\n\n <Box css={styles.textBlock}>\n <Box css={styles.titleRow}>\n {/*\n `bottom-start` rather than the tooltip's default `right`: these texts are\n full-width rows, so a right-hand tooltip lands over the actions. Opening below and\n aligned to the text's start keeps it clear of them and lines it up with the text\n it repeats.\n */}\n <BitkitOverflowTooltip disabled={!canShowTitleTooltip} placement=\"bottom-start\" text={title}>\n {/*\n `tabIndex` only while truncated: the tooltip trigger is rendered `asChild`, so an\n `h1` stays unfocusable and the full title would be mouse-only. Adding the tab\n stop unconditionally would put one in every header for no benefit.\n */}\n <Text as=\"h1\" css={styles.title} ref={titleRef} tabIndex={canShowTitleTooltip ? 0 : undefined}>\n {title}\n </Text>\n </BitkitOverflowTooltip>\n {hasBadge && <Box css={styles.badge}>{badge}</Box>}\n </Box>\n\n {hasSecondaryText && secondaryText !== undefined && (\n <BitkitOverflowTooltip disabled={!isSecondaryTruncated} placement=\"bottom-start\" text={secondaryText}>\n <Text css={styles.secondaryText} ref={secondaryRef} tabIndex={isSecondaryTruncated ? 0 : undefined}>\n {secondaryText}\n </Text>\n </BitkitOverflowTooltip>\n )}\n </Box>\n </Box>\n\n {!hasBreadcrumb && controlsNode}\n\n {hasActions && <Box css={styles.actionBlock}>{children}</Box>}\n </Box>\n </Box>\n\n {hasTabs && <Box css={styles.tabs}>{tabs}</Box>}\n </Box>\n );\n});\n\nBitkitPageHeader.displayName = 'BitkitPageHeader';\n\nexport default BitkitPageHeader;\n"],"mappings":";;;;;;;;;AA0KA,IAAM,mBAAmB,YAAgD,OAAO,QAAQ;CACtF,MAAM,EACJ,QACA,OACA,YACA,UACA,cACA,gBACA,cACA,UACA,KACA,UACA,eACA,cAAc,MACd,MACA,OACA,SACA,GAAG,SACD;CAWJ,MAAM,YAAY,CAAC,YAAY,MAAM;CACrC,MAAM,WAAW,CAAC,YAAY,KAAK;CACnC,MAAM,gBAAgB,CAAC,YAAY,UAAU;CAC7C,MAAM,aAAa,CAAC,YAAY,QAAQ;CACxC,MAAM,mBAAmB,CAAC,YAAY,aAAa;CACnD,MAAM,UAAU,CAAC,YAAY,IAAI;CAKjC,MAAM,EAAE,aAAa,kBAAkB,KAAK,aAAa,eAAmC,KAAK;CACjG,MAAM,EAAE,aAAa,sBAAsB,KAAK,iBAAiB,eAAqC,aAAa;CAMnH,MAAM,cAAc,YAAY,aAAa,QAAQ,QAAQ;CAC7D,MAAM,eAAe,CAAC,YAAY,QAAQ,KAAK;CAM/C,MAAM,sBAAsB,oBAAoB,EAAE,eAAe;CAGjE,MAAM,SADS,cAAc,EAAE,KAAK,aAAa,CAClC,CAAA,CAAO;EACpB;EACA;EACA,aAAa;EACb,YAAY,eAAe;EAG3B,iBAAiB,QAAQ,YAAY;EACrC;EACA;EACA;CACF,CAAC;CAKD,MAAM,eAAe,eAAe,oBAAC,KAAD;EAAK,KAAK,OAAO;EAAW,UAAA;CAAc,CAAA,IAAI;CAElF,OACE,qBAAC,KAAD;EAAK,IAAG;EAAS,KAAK,CAAC,OAAO,MAAM,GAAG;EAAQ;EAAK,GAAI;EAAxD,UAAA,CACE,qBAAC,KAAD;GAAK,KAAK,OAAO;GAAa,UAAU,iBAAiB,SAAS,KAAA,IAAY;GAA9E,UAAA;IACG,iBAAiB,oBAAC,KAAD;KAAK,KAAK,OAAO;KAAa,UAAA;IAAgB,CAAA;IAE/D,iBAAiB;IAElB,qBAAC,KAAD;KAAK,KAAK,OAAO;KAAjB,UAAA;MACE,qBAAC,KAAD;OAAK,KAAK,OAAO;OAAjB,UAAA,CACG,aAAa,oBAAC,KAAD;QAAK,KAAK,OAAO;QAAS,UAAA;OAAY,CAAA,GAEpD,qBAAC,KAAD;QAAK,KAAK,OAAO;QAAjB,UAAA,CACE,qBAAC,KAAD;SAAK,KAAK,OAAO;SAAjB,UAAA,CAOE,oBAAC,uBAAD;UAAuB,UAAU,CAAC;UAAqB,WAAU;UAAe,MAAM;UAMpF,UAAA,oBAAC,MAAD;WAAM,IAAG;WAAK,KAAK,OAAO;WAAO,KAAK;WAAU,UAAU,sBAAsB,IAAI,KAAA;WACjF,UAAA;UACG,CAAA;SACe,CAAA,GACtB,YAAY,oBAAC,KAAD;UAAK,KAAK,OAAO;UAAQ,UAAA;SAAW,CAAA,CAC9C;QAEJ,CAAA,GAAA,oBAAoB,kBAAkB,KAAA,KACrC,oBAAC,uBAAD;SAAuB,UAAU,CAAC;SAAsB,WAAU;SAAe,MAAM;SACrF,UAAA,oBAAC,MAAD;UAAM,KAAK,OAAO;UAAe,KAAK;UAAc,UAAU,uBAAuB,IAAI,KAAA;UACtF,UAAA;SACG,CAAA;QACe,CAAA,CAEtB;OACF,CAAA,CAAA;;MAEJ,CAAC,iBAAiB;MAElB,cAAc,oBAAC,KAAD;OAAK,KAAK,OAAO;OAAc;MAAc,CAAA;KACzD;;GACF;EAEJ,CAAA,GAAA,WAAW,oBAAC,KAAD;GAAK,KAAK,OAAO;GAAO,UAAA;EAAU,CAAA,CAC3C;;AAET,CAAC;AAED,iBAAiB,cAAc"}
1
+ {"version":3,"file":"BitkitPageHeader.js","names":[],"sources":["../../../lib/components/BitkitPageHeader/BitkitPageHeader.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type ReactNode } from 'react';\n\nimport IconMenuHamburger from '../../icons/IconMenuHamburger';\nimport { isEmptyNode } from '../../utilities/isEmptyNode';\nimport useIsTruncated from '../../utilities/useIsTruncated';\nimport BitkitIconButton from '../BitkitIconButton/BitkitIconButton';\nimport BitkitOverflowTooltip from '../BitkitOverflowTooltip/BitkitOverflowTooltip';\n\nexport interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {\n /**\n * Avatar, app icon or thumbnail rendered before the title. Hidden in the collapsed layout.\n *\n * Belongs to the sub-page layouts: the design's main-page header is a title and its subtitle with\n * no avatar, so pass this alongside a `breadcrumb`.\n *\n * Size it to match the design's two sub-page cards, since the node is yours to build: `size=\"40\"`\n * (8px radius) against a bare title, and `size=\"48\"` (10px radius) once `secondaryText` is\n * present — the same avatar the `app` and `user` content shapes use.\n */\n avatar?: ReactNode;\n /** Badge rendered next to the title. Hidden in the collapsed layout. */\n badge?: ReactNode;\n /**\n * Breadcrumb trail above the title — pass a `BitkitBreadcrumb`. Its presence is what switches the\n * header from the design's main-page layout to a sub-page one, so there is no separate page-type\n * prop: the recipe's own `hasBreadcrumb` variant is derived from it.\n *\n * The two layouts carry more than the trail. A main-page header is a title and its subtitle —\n * top-level pages and product overviews — with no avatar and no `app`/`user` content shape. Those\n * belong to sub-pages, so pass them together with a trail.\n */\n breadcrumb?: ReactNode;\n /**\n * Trailing action buttons. Stacked full-width below the title on mobile, primary action on top.\n *\n * The collapsed row is built for `size=\"sm\"` buttons and the expanded header for `size=\"md\"`.\n * Actions arrive as children, so the component cannot set that for you — pass a flat `size=\"sm\"`\n * whenever `minimal` or `isSticky` is in play.\n *\n * Flat, not responsive: Bitkit icons render a different SVG asset per size, so `BitkitButton`\n * resolves `size` in JS and a `{ base, tablet }` object matches no key, falling through to a 24px\n * icon in a 32px button. Since the header collapses in CSS from `tablet` up, a `sm` action is a\n * little small in the expanded mobile layout — but the design draws no mobile frame for the\n * collapsed variants, so `sm` is the closer of the two.\n */\n children?: ReactNode;\n /**\n * Where the content block sits when `contentWidth` caps it: `center` over a centred content\n * column, `start` over one that starts against a sidebar beside the header. Pass the same\n * alignment as that column, so the title and the content share their left edge at every width.\n *\n * No effect with `contentWidth=\"full\"` or without `contentWidth`, where nothing is capped.\n */\n contentAlign?: 'center' | 'start';\n /**\n * Shape of the title block, mirroring the design's `content` property. `title` is a page title\n * with an optional subtitle; `app` and `user` are the tighter entity layouts — a larger avatar\n * centred against a smaller title and 14px secondary text. They differ only in the gap between\n * the avatar and the text, 16 and 12 respectively.\n */\n contentVariant?: 'title' | 'app' | 'user';\n /**\n * Lines the header's content up with the page content below it. The root stays full-width, so\n * the background and the bottom border still run edge to edge — which a wrapper around the whole\n * header could not do.\n *\n * Pass the same value as that column's `max-width`, as rems — `rem()` builds one from a pixel\n * figure — and the content is capped with it, centred unless `contentAlign` says otherwise. Box-sizing is border-box throughout\n * Bitkit, so the value includes the header's own inline padding, which this layout puts at 32 on\n * both sides rather than the default 24 and 32.\n *\n * `'full'` is the same layout without the cap, for a content column that runs the full width: the\n * 32 on both sides, nothing to centre. The default 24 is for a header that runs over a sidebar and\n * lines up with its items instead.\n *\n * Its presence is the toggle: without it the header keeps the default inset.\n *\n * The cap applies at every breakpoint, deliberately — a content column's own `max-width` is not\n * breakpoint-scoped either, so scoping this one would misalign the two wherever the column is\n * narrow enough to bite on mobile. What does change with the breakpoint is the inline padding,\n * 16 below `tablet` and 32 above, which is the same pair such a column uses.\n */\n contentWidth?: `${number}rem` | 'full';\n /**\n * Inline controls between the trail and the actions — a view switcher, filters, a settings menu.\n * Their presence spreads the row into three groups instead of the usual trail-and-actions pair,\n * which is the shape the design file calls `PageHeader-WFE`.\n *\n * Belongs to the collapsed row, so it is honoured by `variant=\"minimal\"` *and* by `isSticky` —\n * the two produce the same row — and ignored in the expanded layouts, which the design draws no\n * controls in.\n */\n controls?: ReactNode;\n /**\n * Renders the compact single-row layout used while the header is stuck to the top of the viewport.\n * Appearance only — the consumer owns scroll detection and any portalling, since the sticky offset\n * and header container are application concerns.\n *\n * Ignored below the `tablet` breakpoint, where the header always stays expanded.\n *\n * Pair it with `size=\"sm\"` actions — see `children`.\n */\n isSticky?: boolean;\n /**\n * Opens the page's sidebar on mobile. Its presence switches the header to the design's\n * `hasSidebar` layout below `tablet`: the title shrinks beside a menu button, and the\n * `sectionTitle` takes over as the large heading under a rule, with the actions below it. Wire it\n * to a `BitkitPageSidebar`'s `open` / `onOpenChange`. From `tablet` the button is hidden and the\n * header is the ordinary one — the sidebar is beside the page there.\n */\n onMenuClick?: () => void;\n /**\n * Descriptive text below the title. Hidden in the collapsed layout.\n *\n * A string, like `title`, so it can be truncated to one line and carried in full by the tooltip\n * that appears when it does not fit.\n */\n secondaryText?: string;\n /**\n * Draws the line along the header's bottom edge. Set it to `false` for a header that flows\n * straight into the page, and the bottom padding goes with the line: the header's background is\n * the page's, so without the line that padding would only be a margin the page cannot shrink. The\n * page spaces its content from the header itself.\n *\n * The collapsed row keeps its padding, which centres the row rather than spacing the page, and from\n * `tablet` a header with `isSticky` keeps its line as well, since content scrolls under it. Below\n * `tablet` `isSticky` is ignored, so there the line goes too.\n *\n * Ignored with `tabs`, whose list sits on that edge. That includes `variant=\"minimal\"`, which hides\n * the tab list from `tablet`: the line stays although no tabs show above it.\n */\n showDivider?: boolean;\n /**\n * The sidebar section the page is showing, for the `hasSidebar` layout — see `onMenuClick`. It\n * is the page's visible `h2` on mobile, where the sidebar that would name it is behind the menu\n * button. Not rendered from `tablet`, and never without `onMenuClick`.\n */\n sectionTitle?: string;\n /**\n * Tab bar along the bottom edge — pass a `BitkitTabs.List`. Hidden in the collapsed layout.\n *\n * The node is rendered inside the header, so tab panels cannot be siblings of the row. To drive\n * page content from it, put `BitkitTabs.Root` around the header *and* the content, and pass only\n * the list here — `BitkitTabs.List` reads its Root from context and throws without an ancestor:\n *\n * ```tsx\n * <BitkitTabs.Root defaultValue=\"overview\">\n * <BitkitPageHeader tabs={<BitkitTabs.List>…</BitkitTabs.List>} title=\"Build Hub\" />\n * <BitkitTabs.ContentGroup>\n * <BitkitTabs.Content padding=\"32\" value=\"overview\">…</BitkitTabs.Content>\n * </BitkitTabs.ContentGroup>\n * </BitkitTabs.Root>\n * ```\n *\n * Only the row is hidden when the header collapses — the panels live outside it and keep\n * rendering, so the page never blanks.\n */\n tabs?: ReactNode;\n /**\n * The page title, as a string. Everything passed here lands inside the `h1`, so it is the\n * heading's accessible name and nothing else belongs in it — the avatar, badge and secondary\n * text each have their own slot. Required: it is what gives the header a page heading at every\n * breakpoint.\n *\n * Truncated to a single line with an ellipsis. When it does not fit, hovering or focusing it\n * shows the full string in a tooltip — so a long title never grows the header.\n *\n * In the collapsed layout (`minimal` or `isSticky`) *with* a `breadcrumb`, the design shows the\n * title in the trail rather than as a heading, so end the breadcrumb with a\n * `BitkitBreadcrumb.CurrentItem` carrying it — the title is then rendered as a visually hidden\n * `h1`, since `Breadcrumb.CurrentLink` is a link and not a heading. Below `tablet` the header\n * never collapses and the visible heading is used instead (`BitkitBreadcrumb` drops its\n * `CurrentItem` on mobile).\n */\n title: string;\n /**\n * `minimal` collapses the header to the single breadcrumb row used by sub-pages that don't repeat\n * their title as a heading. Like `isSticky`, it only collapses from `tablet` up, and expects\n * `size=\"sm\"` actions — see `children`.\n */\n variant?: 'default' | 'minimal';\n}\n\nconst BitkitPageHeader = forwardRef<HTMLElement, BitkitPageHeaderProps>((props, ref) => {\n const {\n avatar,\n badge,\n breadcrumb,\n children,\n contentAlign,\n contentVariant,\n contentWidth,\n controls,\n css,\n isSticky,\n onMenuClick,\n secondaryText,\n sectionTitle,\n showDivider = true,\n tabs,\n title,\n variant,\n ...rest\n } = props;\n\n // Which slots the collapsed row drops, and whether it collapses at all, is decided entirely by\n // the recipe's `tablet`-scoped styles. Nothing here branches on the viewport: a breakpoint hook\n // would render the wrong layout on first paint, since Chakra's `useMediaQuery` returns its\n // fallback until after mount.\n\n // A slot's presence is its own toggle, so all of them share one emptiness test rather than plain\n // truthiness: an array whose entries all render nothing — `items.map()` where every branch is\n // `null` — is still truthy, and would otherwise render a wrapper that only contributes gap. For\n // `controls` it would also spread the row into three groups around an empty middle one.\n const hasAvatar = !isEmptyNode(avatar);\n const hasBadge = !isEmptyNode(badge);\n const hasBreadcrumb = !isEmptyNode(breadcrumb);\n const hasActions = !isEmptyNode(children);\n const hasSecondaryText = !isEmptyNode(secondaryText);\n const hasSidebar = onMenuClick !== undefined;\n const hasSectionTitle = hasSidebar && !isEmptyNode(sectionTitle);\n const hasTabs = !isEmptyNode(tabs);\n\n // `BitkitOverflowTooltip` does no measuring of its own — it shows the tooltip unless told not to,\n // so each truncating text reports whether it is actually clipped and suppresses its own tooltip\n // when it is not. Keyed on the text so a change re-measures without waiting for a resize.\n const { isTruncated: isTitleTruncated, ref: titleRef } = useIsTruncated<HTMLHeadingElement>(title);\n const { isTruncated: isSecondaryTruncated, ref: secondaryRef } = useIsTruncated<HTMLParagraphElement>(secondaryText);\n\n // The design only draws inline controls in the collapsed row, so they are ignored in the\n // expanded layouts rather than left to land somewhere undesigned. `minimal` and `isSticky`\n // produce that row through the same `collapsedSlots`, so both have to honour them — gating on\n // `minimal` alone left `isSticky` rendering the identical row with the middle group missing.\n const isCollapsed = variant === 'minimal' || Boolean(isSticky);\n const showControls = !isEmptyNode(controls) && isCollapsed;\n\n // The collapsed row with a trail makes the title block `srOnly`, which is a 1px box — so the\n // measurement reports \"truncated\" for a heading nobody can see. Left alone that puts a tab stop\n // on an invisible element and opens a tooltip against it. The trail already carries the title as\n // its current item in exactly that layout, so there is nothing to surface anyway.\n const canShowTitleTooltip = isTitleTruncated && !(isCollapsed && hasBreadcrumb);\n\n const recipe = useSlotRecipe({ key: 'pageHeader' });\n const styles = recipe({\n contentVariant,\n hasBreadcrumb,\n hasControls: showControls,\n hasDivider: showDivider || hasTabs,\n hasSidebar,\n // The cap is a length the consumer chooses, so it cannot be a recipe variant -- the variant\n // carries the layout switch and the `maxWidth` below carries the value.\n alignsToContent: Boolean(contentWidth),\n contentAlign,\n isSticky,\n variant,\n });\n\n // The controls are the middle of three groups, so they follow whichever element holds the left\n // slot: the trail when there is one, otherwise the title — which lives inside `contentBlock`, so\n // they have to go in there with it rather than beside it.\n const controlsNode = showControls ? <Box css={styles.controls}>{controls}</Box> : null;\n\n // With a sidebar the menu button shares the title's row, so the block gets a wrapper the content\n // row treats as the title block. Other headers keep their DOM.\n const titleBlock = (\n <Box css={styles.titleBlock}>\n {hasAvatar && <Box css={styles.avatar}>{avatar}</Box>}\n\n <Box css={styles.textBlock}>\n <Box css={styles.titleRow}>\n {/*\n `bottom-start` rather than the tooltip's default `right`: these texts are\n full-width rows, so a right-hand tooltip lands over the actions. Opening below and\n aligned to the text's start keeps it clear of them and lines it up with the text\n it repeats.\n */}\n <BitkitOverflowTooltip disabled={!canShowTitleTooltip} placement=\"bottom-start\" text={title}>\n {/*\n `tabIndex` only while truncated: the tooltip trigger is rendered `asChild`, so an\n `h1` stays unfocusable and the full title would be mouse-only. Adding the tab\n stop unconditionally would put one in every header for no benefit.\n */}\n <Text as=\"h1\" css={styles.title} ref={titleRef} tabIndex={canShowTitleTooltip ? 0 : undefined}>\n {title}\n </Text>\n </BitkitOverflowTooltip>\n {hasBadge && <Box css={styles.badge}>{badge}</Box>}\n </Box>\n\n {hasSecondaryText && secondaryText !== undefined && (\n <BitkitOverflowTooltip disabled={!isSecondaryTruncated} placement=\"bottom-start\" text={secondaryText}>\n <Text css={styles.secondaryText} ref={secondaryRef} tabIndex={isSecondaryTruncated ? 0 : undefined}>\n {secondaryText}\n </Text>\n </BitkitOverflowTooltip>\n )}\n </Box>\n </Box>\n );\n\n return (\n <Box as=\"header\" css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.headerBlock} maxWidth={contentWidth === 'full' ? undefined : contentWidth}>\n {hasBreadcrumb && <Box css={styles.breadcrumb}>{breadcrumb}</Box>}\n\n {hasBreadcrumb && controlsNode}\n\n <Box css={styles.contentBlock}>\n {hasSidebar ? (\n <Box css={styles.titleBar}>\n {titleBlock}\n <Box css={styles.menuButton}>\n <BitkitIconButton\n icon={IconMenuHamburger}\n label=\"Open navigation\"\n onClick={onMenuClick}\n size=\"sm\"\n variant=\"secondary\"\n />\n </Box>\n </Box>\n ) : (\n titleBlock\n )}\n\n {hasSectionTitle && (\n <Box css={styles.sectionBlock}>\n <Text as=\"h2\" css={styles.sectionTitle}>\n {sectionTitle}\n </Text>\n </Box>\n )}\n\n {!hasBreadcrumb && controlsNode}\n\n {hasActions && <Box css={styles.actionBlock}>{children}</Box>}\n </Box>\n </Box>\n\n {hasTabs && <Box css={styles.tabs}>{tabs}</Box>}\n </Box>\n );\n});\n\nBitkitPageHeader.displayName = 'BitkitPageHeader';\n\nexport default BitkitPageHeader;\n"],"mappings":"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"}
@@ -7,6 +7,21 @@ import { BitkitColorToken } from '../../theme/themeUtils';
7
7
  import { BitkitGroupHeadingProps } from '../BitkitGroupHeading/BitkitGroupHeading';
8
8
  export interface BitkitPageSidebarProps extends BoxProps {
9
9
  children: ReactNode;
10
+ /**
11
+ * Header of the mobile drawer panel, naming what the navigation belongs to — pass a
12
+ * `BitkitPageSidebar.DrawerHeader`. Only rendered in drawer mode.
13
+ */
14
+ drawerHeader?: ReactNode;
15
+ /**
16
+ * Mobile drawer visibility. Controlled, because the page header's menu button is what opens it.
17
+ * Passing `onOpenChange` is what opts the sidebar into the drawer below `tablet`; without it the
18
+ * sidebar stays in the flow at full width there. Both are ignored from `tablet` up.
19
+ *
20
+ * Style props and `css` apply to the sidebar beside the page only — the drawer panel sizes
21
+ * itself, so a desktop `width` or padding never reaches the navigation inside it.
22
+ */
23
+ onOpenChange?: (open: boolean) => void;
24
+ open?: boolean;
10
25
  }
11
26
  interface BitkitPageSidebarItemCommonProps {
12
27
  badge?: ReactNode;
@@ -105,10 +120,19 @@ export interface BitkitPageSidebarProjectProps extends BoxProps {
105
120
  value: string;
106
121
  }
107
122
  export type BitkitPageSidebarGroupHeadingProps = BitkitGroupHeadingProps;
123
+ export interface BitkitPageSidebarDrawerHeaderProps extends Omit<BoxProps, 'title'> {
124
+ /** App icon or user avatar before the title — a 32px `BitkitAvatar`. */
125
+ avatar?: ReactNode;
126
+ /** The entity's secondary line, an app's bundle id or a user's email. Its presence selects the entity layout. */
127
+ secondaryText?: string;
128
+ /** What the navigation belongs to: the page, app or user. */
129
+ title: string;
130
+ }
108
131
  export type BitkitPageSidebarDividerProps = SeparatorProps;
109
132
  declare const _default: import('react').ForwardRefExoticComponent<BitkitPageSidebarProps & import('react').RefAttributes<HTMLElement>> & {
110
133
  Body: import('react').ForwardRefExoticComponent<BitkitPageSidebarBodyProps & import('react').RefAttributes<HTMLDivElement>>;
111
134
  Divider: import('react').ForwardRefExoticComponent<SeparatorProps & import('react').RefAttributes<HTMLDivElement>>;
135
+ DrawerHeader: import('react').ForwardRefExoticComponent<BitkitPageSidebarDrawerHeaderProps & import('react').RefAttributes<HTMLDivElement>>;
112
136
  Footer: import('react').ForwardRefExoticComponent<BitkitPageSidebarFooterProps & import('react').RefAttributes<HTMLDivElement>>;
113
137
  GroupHeading: import('react').ForwardRefExoticComponent<BitkitGroupHeadingProps & import('react').RefAttributes<HTMLDivElement>>;
114
138
  Item: import('react').ForwardRefExoticComponent<BitkitPageSidebarItemProps & import('react').RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
@@ -1,15 +1,19 @@
1
1
  import { withSubComponents } from "../../utilities/withSubComponents.js";
2
2
  import IconChevronLeft from "../../icons/IconChevronLeft.js";
3
+ import BitkitCloseButton from "../BitkitCloseButton/BitkitCloseButton.js";
3
4
  import BitkitControlButton from "../BitkitControlButton/BitkitControlButton.js";
4
5
  import BitkitGroupHeading from "../BitkitGroupHeading/BitkitGroupHeading.js";
5
6
  import BitkitOverflowTooltip from "../BitkitOverflowTooltip/BitkitOverflowTooltip.js";
6
7
  import useIsTruncated from "../../utilities/useIsTruncated.js";
8
+ import useResponsive from "../../hooks/useResponsive.js";
7
9
  import { Box } from "@chakra-ui/react/box";
8
- import { chakra, useSlotRecipe } from "@chakra-ui/react/styled-system";
10
+ import { chakra, useChakraContext, useSlotRecipe } from "@chakra-ui/react/styled-system";
9
11
  import { forwardRef } from "react";
10
12
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
+ import { Portal } from "@chakra-ui/react/portal";
11
14
  import { Separator } from "@chakra-ui/react/separator";
12
15
  import { Text } from "@chakra-ui/react/text";
16
+ import { Drawer } from "@chakra-ui/react/drawer";
13
17
  //#region lib/components/BitkitPageSidebar/BitkitPageSidebar.tsx
14
18
  var showAtDesktop = { display: {
15
19
  base: "none",
@@ -44,13 +48,20 @@ var showAtMobile = { display: {
44
48
  * opts out with `minHeight="auto"`.
45
49
  *
46
50
  * Below the `tablet` breakpoint it switches to the mobile design on its own — full width, 48px
47
- * rows, larger icons and labels, a squared-off selection edge. There is no prop for it: a sidebar
48
- * that narrow is a sidebar filling a phone.
51
+ * rows, larger icons and labels, a squared-off selection edge. A sidebar that sits beside page
52
+ * content has nowhere to go at that width, so it can become a drawer instead: pass `open` and
53
+ * `onOpenChange` (opened by `BitkitPageHeader`'s menu button) and below `tablet` it slides in
54
+ * from the trailing edge over a backdrop, with its close button on the backdrop beside the panel
55
+ * and a `drawerHeader` naming the page. Both props are ignored from `tablet` up.
49
56
  */
50
57
  var BitkitPageSidebar = forwardRef((props, ref) => {
51
- const { children, css, ...rest } = props;
52
- const styles = useSlotRecipe({ key: "pageSidebar" })();
53
- return /* @__PURE__ */ jsx(Box, {
58
+ const { children, css, drawerHeader, onOpenChange, open = false, ...rest } = props;
59
+ const { isMobile } = useResponsive();
60
+ const { splitCssProps } = useChakraContext();
61
+ const hasDrawer = onOpenChange !== void 0;
62
+ const isDrawer = isMobile && hasDrawer;
63
+ const styles = useSlotRecipe({ key: "pageSidebar" })({ layout: isDrawer ? "drawer" : hasDrawer ? "hidden" : "static" });
64
+ if (!isDrawer) return /* @__PURE__ */ jsx(Box, {
54
65
  ref,
55
66
  "aria-label": "Page navigation",
56
67
  asChild: true,
@@ -58,6 +69,35 @@ var BitkitPageSidebar = forwardRef((props, ref) => {
58
69
  ...rest,
59
70
  children: /* @__PURE__ */ jsx("nav", { children })
60
71
  });
72
+ const [, navProps] = splitCssProps(rest);
73
+ return /* @__PURE__ */ jsx(Drawer.Root, {
74
+ onOpenChange: (details) => onOpenChange(details.open),
75
+ open,
76
+ children: /* @__PURE__ */ jsxs(Portal, { children: [/* @__PURE__ */ jsx(Drawer.Backdrop, { css: styles.drawerBackdrop }), /* @__PURE__ */ jsx(Drawer.Positioner, { children: /* @__PURE__ */ jsxs(Drawer.Content, {
77
+ "aria-label": "Navigation",
78
+ css: styles.drawerContent,
79
+ children: [
80
+ drawerHeader,
81
+ /* @__PURE__ */ jsx(Box, {
82
+ ref,
83
+ "aria-label": "Page navigation",
84
+ asChild: true,
85
+ css: styles.root,
86
+ ...navProps,
87
+ children: /* @__PURE__ */ jsx("nav", { children })
88
+ }),
89
+ /* @__PURE__ */ jsx(Box, {
90
+ css: styles.drawerCloseButton,
91
+ children: /* @__PURE__ */ jsx(BitkitCloseButton, {
92
+ "aria-label": "Close navigation",
93
+ colorVariant: "white",
94
+ onClick: () => onOpenChange(false),
95
+ size: "md"
96
+ })
97
+ })
98
+ ]
99
+ }) })] })
100
+ });
61
101
  });
62
102
  BitkitPageSidebar.displayName = "BitkitPageSidebar";
63
103
  var BitkitPageSidebarItem = forwardRef((props, ref) => {
@@ -318,6 +358,33 @@ var BitkitPageSidebarGroupHeading = forwardRef((props, ref) => {
318
358
  });
319
359
  });
320
360
  BitkitPageSidebarGroupHeading.displayName = "BitkitPageSidebarGroupHeading";
361
+ /**
362
+ * Header of the sidebar's mobile drawer (Figma `mobilePageSidebarHeader`). Pass it as the
363
+ * sidebar's `drawerHeader`; it is what the panel opens with, above the navigation.
364
+ */
365
+ var BitkitPageSidebarDrawerHeader = forwardRef((props, ref) => {
366
+ const { avatar, css, secondaryText, title, ...rest } = props;
367
+ const styles = useSlotRecipe({ key: "pageSidebar" })({ hasSecondaryText: secondaryText !== void 0 });
368
+ return /* @__PURE__ */ jsxs(Box, {
369
+ ref,
370
+ css: [styles.drawerHeader, css],
371
+ ...rest,
372
+ children: [avatar !== void 0 && /* @__PURE__ */ jsx(Box, {
373
+ css: styles.drawerHeaderAvatar,
374
+ children: avatar
375
+ }), /* @__PURE__ */ jsxs(Box, {
376
+ css: styles.drawerHeaderTextBlock,
377
+ children: [/* @__PURE__ */ jsx(Text, {
378
+ css: styles.drawerHeaderTitle,
379
+ children: title
380
+ }), secondaryText !== void 0 && /* @__PURE__ */ jsx(Text, {
381
+ css: styles.drawerHeaderSecondaryText,
382
+ children: secondaryText
383
+ })]
384
+ })]
385
+ });
386
+ });
387
+ BitkitPageSidebarDrawerHeader.displayName = "BitkitPageSidebarDrawerHeader";
321
388
  var BitkitPageSidebarDivider = forwardRef((props, ref) => {
322
389
  const { css, ...rest } = props;
323
390
  const styles = useSlotRecipe({ key: "pageSidebar" })();
@@ -331,6 +398,7 @@ BitkitPageSidebarDivider.displayName = "BitkitPageSidebarDivider";
331
398
  var BitkitPageSidebar_default = withSubComponents(BitkitPageSidebar, {
332
399
  Body: BitkitPageSidebarBody,
333
400
  Divider: BitkitPageSidebarDivider,
401
+ DrawerHeader: BitkitPageSidebarDrawerHeader,
334
402
  Footer: BitkitPageSidebarFooter,
335
403
  GroupHeading: BitkitPageSidebarGroupHeading,
336
404
  Item: BitkitPageSidebarItem,
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitPageSidebar.js","names":["_Fragment"],"sources":["../../../lib/components/BitkitPageSidebar/BitkitPageSidebar.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Separator, type SeparatorProps } from '@chakra-ui/react/separator';\nimport { chakra, type HTMLChakraProps, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type KeyboardEvent, type MouseEvent, type ReactNode, type Ref } from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\nimport IconChevronLeft from '../../icons/IconChevronLeft';\nimport { type BitkitColorToken } from '../../theme/themeUtils';\nimport useIsTruncated from '../../utilities/useIsTruncated';\nimport { withSubComponents } from '../../utilities/withSubComponents';\nimport BitkitControlButton from '../BitkitControlButton/BitkitControlButton';\nimport BitkitGroupHeading, { type BitkitGroupHeadingProps } from '../BitkitGroupHeading/BitkitGroupHeading';\nimport BitkitOverflowTooltip from '../BitkitOverflowTooltip/BitkitOverflowTooltip';\n\n// The 16 and 24 icons are separate drawings, not one scaled to two sizes, so the breakpoint swap\n// cannot live in the recipe — both are rendered and one is hidden. A `useBreakpointValue` would\n// render a single icon, but it only re-reads on mount: resize a live page across `tablet` and the\n// row resizes underneath an icon that keeps its old size.\nconst showAtDesktop = { display: { base: 'none', tablet: 'block' } };\nconst showAtMobile = { display: { base: 'block', tablet: 'none' } };\n\n// ----- BitkitPageSidebar (Root) -----\n\nexport interface BitkitPageSidebarProps extends BoxProps {\n children: ReactNode;\n}\n\n/**\n * Page-level navigation sidebar. It expects three children, in this order:\n *\n * ```tsx\n * <BitkitPageSidebar>\n * <BitkitPageSidebar.Title>Title</BitkitPageSidebar.Title>\n * <BitkitPageSidebar.Body>\n * <BitkitPageSidebar.Item icon={IconWorkflow}>Workflows</BitkitPageSidebar.Item>\n * </BitkitPageSidebar.Body>\n * <BitkitPageSidebar.Footer>…</BitkitPageSidebar.Footer>\n * </BitkitPageSidebar>\n * ```\n *\n * `Title` and `Footer` are optional. The sidebar scrolls as a single block: when the navigation is\n * taller than the viewport the whole thing — footer included — grows and scrolls with the page,\n * which is why `Body` keeps a minimum gap above the footer so the two blocks never touch.\n *\n * The `<nav>` carries a default `aria-label` so it is not announced as an unnamed landmark. A page\n * with more than one sidebar should name each one — pass your own `aria-label` (or\n * `aria-labelledby`), which overrides the default.\n *\n * It stands at least viewport-tall by default. An embedded panel that should size to its content\n * opts out with `minHeight=\"auto\"`.\n *\n * Below the `tablet` breakpoint it switches to the mobile design on its own — full width, 48px\n * rows, larger icons and labels, a squared-off selection edge. There is no prop for it: a sidebar\n * that narrow is a sidebar filling a phone.\n */\nconst BitkitPageSidebar = forwardRef<HTMLElement, BitkitPageSidebarProps>((props, ref) => {\n const { children, css, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe();\n\n return (\n <Box ref={ref} aria-label=\"Page navigation\" asChild css={[styles.root, css]} {...rest}>\n <nav>{children}</nav>\n </Box>\n );\n});\n\nBitkitPageSidebar.displayName = 'BitkitPageSidebar';\n\n// ----- BitkitPageSidebar.Item -----\n\ninterface BitkitPageSidebarItemCommonProps {\n badge?: ReactNode;\n /**\n * The item's label. Typed as a string rather than `ReactNode` because the row truncates it with\n * an ellipsis, and the overflow tooltip needs the full text to reveal.\n */\n children: string;\n icon?: BitkitIconComponent;\n /**\n * Marks the item as the current page. Mutually exclusive with `state=\"disabled\"` — you cannot be\n * on a page whose nav item is unavailable — but the two are not type-checked against each other:\n * the combination only ever arrives from a computed `selected`, which no union can catch, and\n * checking it would break the ordinary `selected={item.id === currentId}` mapping. If both do\n * turn up, disabled wins, since that is the one of the two the design actually defines.\n */\n selected?: boolean;\n state?: 'disabled';\n suffixIcon?: BitkitIconComponent;\n}\n\ninterface BitkitPageSidebarItemLinkProps\n extends Omit<HTMLChakraProps<'a'>, 'children'>, BitkitPageSidebarItemCommonProps {\n href: string;\n}\n\ninterface BitkitPageSidebarItemButtonProps\n extends Omit<HTMLChakraProps<'button'>, 'children' | 'disabled'>, BitkitPageSidebarItemCommonProps {\n href?: never;\n}\n\nexport type BitkitPageSidebarItemProps = BitkitPageSidebarItemButtonProps | BitkitPageSidebarItemLinkProps;\n\nconst BitkitPageSidebarItem = forwardRef<HTMLButtonElement | HTMLAnchorElement, BitkitPageSidebarItemProps>(\n (props, ref) => {\n const { badge, children, icon: Icon, selected, state, suffixIcon: SuffixIcon, ...rest } = props;\n\n const isDisabled = state === 'disabled';\n // `selected` and `state=\"disabled\"` are not checked against each other in the types, so this is\n // the single place the overlap is resolved: disabled wins, and the two recipe variants can never\n // both claim the same slot.\n const isSelected = selected === true && !isDisabled;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe({ isDisabled, selected: isSelected });\n\n // The design gives neither a selected row nor a disabled one a trailing icon — the desktop\n // (`20014:6498`, `20014:6494`) and mobile (`9221:4417`) items drop it in those states.\n const VisibleSuffixIcon = isSelected || isDisabled ? undefined : SuffixIcon;\n\n const { isTruncated, ref: labelRef } = useIsTruncated<HTMLSpanElement>(children);\n\n const itemContent = (\n <>\n {/* `span`, not the default `div`: in button mode these sit inside a `<button>`, whose\n content model allows phrasing content only. */}\n <Box as=\"span\" css={styles.contentBlock}>\n {Icon && (\n <>\n <Icon css={[styles.itemIcon, showAtDesktop]} size=\"16\" />\n <Icon css={[styles.itemIcon, showAtMobile]} size=\"24\" />\n </>\n )}\n <Text as=\"span\" css={styles.itemLabel} ref={labelRef}>\n {children}\n </Text>\n </Box>\n {badge}\n {VisibleSuffixIcon && (\n <>\n <VisibleSuffixIcon css={[styles.suffixIcon, showAtDesktop]} size=\"16\" />\n <VisibleSuffixIcon css={[styles.suffixIcon, showAtMobile]} size=\"24\" />\n </>\n )}\n {isSelected && <Box as=\"span\" css={styles.selectionMarker} />}\n </>\n );\n\n // The overflow tooltip anchors to the row itself, which is the focusable element: anchored to an\n // inner node it would only ever open on hover, leaving keyboard users unable to read a truncated\n // label. `Tooltip.Trigger` renders `asChild` and composes refs, so the consumer's forwarded ref\n // survives, and the ref measuring the truncation sits on the label further in.\n const withTooltip = (row: ReactNode) => (\n <BitkitOverflowTooltip disabled={!isTruncated} text={children}>\n {row}\n </BitkitOverflowTooltip>\n );\n\n // `chakra.a` / `chakra.button` rather than a named primitive: the item carries its own slot recipe,\n // and Chakra's `Button`/`Link` would layer their own recipe styles on top of it.\n if (rest.href !== undefined) {\n const { className, href, onClick, onKeyDown, ...linkRest } = rest;\n const handleClick = isDisabled\n ? (event: MouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n event.stopPropagation();\n }\n : onClick;\n const handleKeyDown = isDisabled\n ? (event: KeyboardEvent<HTMLAnchorElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n : onKeyDown;\n\n return withTooltip(\n <chakra.a\n ref={ref as Ref<HTMLAnchorElement>}\n {...linkRest}\n aria-current={isSelected ? 'page' : undefined}\n aria-disabled={isDisabled || undefined}\n className={className ? `group ${className}` : 'group'}\n css={styles.item}\n // Dropping the attribute is what actually disables the link, the way `BitkitLink` and\n // `BitkitControlButton` do it. The handlers below only block `click` and `Enter`/`Space`;\n // a middle click fires `auxclick`, and \"Open link in new tab\" never reaches the page at\n // all, so both would still navigate while an `href` is there to follow.\n href={isDisabled ? undefined : href}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n >\n {itemContent}\n </chakra.a>,\n );\n }\n\n const { className, href: _href, ...buttonRest } = rest;\n\n return withTooltip(\n <chakra.button\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n aria-current={isSelected ? 'page' : undefined}\n className={className ? `group ${className}` : 'group'}\n css={styles.item}\n disabled={isDisabled}\n type=\"button\"\n >\n {itemContent}\n </chakra.button>,\n );\n },\n);\n\nBitkitPageSidebarItem.displayName = 'BitkitPageSidebarItem';\n\n// ----- BitkitPageSidebar.ProgressItem -----\n\nexport interface BitkitPageSidebarProgressItemProps extends Omit<HTMLChakraProps<'button'>, 'children' | 'disabled'> {\n /**\n * Trailing content between the label and the connector — a `BitkitBadge` counting what the stage\n * is waiting on. Sized to itself; the connector takes the rest.\n *\n * The design draws it 20px tall, which is `size=\"xs\"`. The badge arrives as a node, so the\n * component cannot set that for you — a taller one grows the row past its 40px.\n */\n badge?: ReactNode;\n /** The row's label. */\n children: ReactNode;\n /**\n * The status icon. Unlike `Item`, whose icon colour the sidebar owns, this one carries the\n * stage's status, so `iconColor` is the consumer's to set — `selected`, `notRequired` and\n * `state=\"disabled\"` still win over it, since those describe the row rather than the status.\n */\n icon: BitkitIconComponent;\n /**\n * A semantic token for the status the icon is showing. Left out, the icon takes the sidebar's own\n * `icon/secondary` — which is what the design gives a stage whose icon carries no status, such as\n * an approvals step.\n *\n * Narrowed to the `icon/*` and `color/*` tokens, so a name that does not exist is a compile error\n * rather than a colour that silently does not apply. Both families are here because the statuses\n * need both: the design takes success and failure from `icon/*` but in-progress from\n * `color/orange/bold`, which the `icon/*` set has no equivalent of.\n */\n iconColor?: BitkitColorToken<'color' | 'icon'>;\n /**\n * A stage the release does not have to clear. Steps the icon and the label down one level, so the\n * row reads as present but not awaited.\n */\n notRequired?: boolean;\n /** Follows the label after a middle dot — `auto-upload`, `not required`. */\n secondaryText?: string;\n selected?: boolean;\n state?: 'disabled';\n /**\n * Draws the dashed rule that runs from the badge to the trailing edge, tying one stage to the\n * next. The last stage in a list has nothing to tie to, so it goes without.\n */\n withConnector?: boolean;\n}\n\n/**\n * A stage of something that progresses, as a row of the page sidebar — a release running through\n * its steps, and whatever comes to look like it.\n *\n * It is a sibling of `Item` rather than a variant of it: the row reports where a stage stands\n * rather than navigating, so its icon is status-coloured, its label does not grow, and a dashed\n * connector runs to the trailing edge between the stages.\n *\n * Always a `<button>`, and always interactive — it carries the shared row's pointer cursor and\n * hover background, so a row without an `onClick` looks actionable and is not. `Item`'s\n * button-or-anchor union is deliberately not here: a stage is selected within the page it is\n * already on rather than navigated to.\n */\nconst BitkitPageSidebarProgressItem = forwardRef<HTMLButtonElement, BitkitPageSidebarProgressItemProps>(\n (props, ref) => {\n const {\n badge,\n children,\n className,\n css,\n icon: Icon,\n iconColor,\n notRequired,\n secondaryText,\n selected,\n state,\n withConnector,\n ...rest\n } = props;\n\n const isDisabled = state === 'disabled';\n // Disabled is the whole state of the row, so the other two are dropped rather than merged: the\n // recipe resolves a combination in its own order, and a disabled row came out of it carrying\n // the selected icon colour. Nothing about a disabled stage is selected or not-required.\n const isSelected = selected && !isDisabled;\n // And `selected` wins over `notRequired`, which the design decides rather than declaration\n // order: its `selected=yes, notRequired=true` variant draws the selected row's icon and label\n // and keeps `not required` in the secondary text -- which is what dropping this produces.\n const isNotRequired = notRequired && !isDisabled && !isSelected;\n\n const statusColor = isDisabled || isNotRequired || isSelected ? undefined : iconColor;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe({ isDisabled, notRequired: isNotRequired, selected: isSelected });\n\n return (\n <chakra.button\n ref={ref}\n {...rest}\n // `step` rather than the `page` an `Item` carries: these are stages of one thing, not\n // separate pages.\n aria-current={isSelected ? 'step' : undefined}\n className={className ? `group ${className}` : 'group'}\n css={[styles.item, styles.progressRow, css]}\n disabled={isDisabled}\n type=\"button\"\n >\n <Box as=\"span\" css={[styles.contentBlock, styles.progressContentBlock]}>\n {/* Two icons, one per size, for the reason `Item` has two -- see `showAtDesktop`.\n The status colour applies while the row is ordinary: `selected`, `notRequired` and\n disabled are states of the row rather than of the status, and the design recolours the\n icon in all three, so the recipe takes it back there. */}\n <Icon color={statusColor} css={[styles.progressIcon, showAtDesktop]} size=\"16\" />\n <Icon color={statusColor} css={[styles.progressIcon, showAtMobile]} size=\"24\" />\n <Box as=\"span\" css={styles.progressTextBlock}>\n <Text as=\"span\" css={styles.progressLabel}>\n {children}\n </Text>\n {secondaryText && (\n <Text as=\"span\" css={styles.progressSecondaryText}>\n {`\\u00b7 ${secondaryText}`}\n </Text>\n )}\n </Box>\n {(badge || withConnector) && (\n <Box as=\"span\" css={styles.progressBlocker}>\n {badge}\n {/* Decorative: it ties the stages together visually and has nothing to separate in\n the button's accessibility tree. */}\n {withConnector && <Separator aria-hidden css={styles.progressConnector} />}\n </Box>\n )}\n </Box>\n {isSelected && <Box as=\"span\" css={styles.selectionMarker} />}\n </chakra.button>\n );\n },\n);\n\nBitkitPageSidebarProgressItem.displayName = 'BitkitPageSidebarProgressItem';\n\n// ----- BitkitPageSidebar.Title -----\n\ninterface BitkitPageSidebarTitleCommonProps extends BoxProps {\n backLabel?: string;\n children: ReactNode;\n}\n\ninterface BitkitPageSidebarTitleLinkProps extends BitkitPageSidebarTitleCommonProps {\n backHref: string;\n onBackClick?: never;\n}\n\ninterface BitkitPageSidebarTitleButtonProps extends BitkitPageSidebarTitleCommonProps {\n backHref?: never;\n onBackClick: () => void;\n}\n\ninterface BitkitPageSidebarTitlePlainProps extends BitkitPageSidebarTitleCommonProps {\n backHref?: never;\n onBackClick?: never;\n}\n\nexport type BitkitPageSidebarTitleProps =\n BitkitPageSidebarTitleButtonProps | BitkitPageSidebarTitleLinkProps | BitkitPageSidebarTitlePlainProps;\n\nconst BitkitPageSidebarTitle = forwardRef<HTMLDivElement, BitkitPageSidebarTitleProps>((props, ref) => {\n const { backHref, backLabel = 'Back', children, css, onBackClick, ...rest } = props;\n\n const hasBack = backHref !== undefined || onBackClick !== undefined;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe({ hasBack });\n\n return (\n <Box ref={ref} css={[styles.title, css]} {...rest}>\n <Box css={styles.titleBlock}>\n {/* The label tooltip opens to the right. Its default `top` has nowhere to go here — the\n button sits at the very top of the sidebar, so the tooltip jams against the viewport\n edge and covers the header instead of flipping. */}\n {hasBack &&\n (backHref !== undefined ? (\n <BitkitControlButton\n href={backHref}\n icon={IconChevronLeft}\n label={backLabel}\n size=\"xs\"\n tooltipProps={{ placement: 'right' }}\n />\n ) : (\n <BitkitControlButton\n icon={IconChevronLeft}\n label={backLabel}\n onClick={onBackClick}\n size=\"xs\"\n tooltipProps={{ placement: 'right' }}\n />\n ))}\n <Text as=\"span\" css={styles.titleText}>\n {children}\n </Text>\n </Box>\n </Box>\n );\n});\n\nBitkitPageSidebarTitle.displayName = 'BitkitPageSidebarTitle';\n\n// ----- BitkitPageSidebar.Body -----\n\nexport interface BitkitPageSidebarBodyProps extends BoxProps {\n children: ReactNode;\n}\n\n/**\n * Holds the navigation items. It absorbs the space left between `Title` and `Footer`, so the footer\n * sits at the bottom of the viewport on a short navigation, and keeps a minimum gap above the\n * footer so the two blocks stay visually separate however long the list gets.\n */\nconst BitkitPageSidebarBody = forwardRef<HTMLDivElement, BitkitPageSidebarBodyProps>((props, ref) => {\n const { children, css, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe();\n\n return (\n <Box ref={ref} css={[styles.body, css]} {...rest}>\n {children}\n </Box>\n );\n});\n\nBitkitPageSidebarBody.displayName = 'BitkitPageSidebarBody';\n\n// ----- BitkitPageSidebar.Footer -----\n\nexport interface BitkitPageSidebarFooterProps extends BoxProps {\n children: ReactNode;\n}\n\nconst BitkitPageSidebarFooter = forwardRef<HTMLDivElement, BitkitPageSidebarFooterProps>((props, ref) => {\n const { children, css, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe();\n\n return (\n <Box ref={ref} css={[styles.footer, css]} {...rest}>\n {children}\n </Box>\n );\n});\n\nBitkitPageSidebarFooter.displayName = 'BitkitPageSidebarFooter';\n\n// ----- BitkitPageSidebar.Project -----\n\nexport interface BitkitPageSidebarProjectProps extends BoxProps {\n label: string;\n value: string;\n}\n\nconst BitkitPageSidebarProject = forwardRef<HTMLDivElement, BitkitPageSidebarProjectProps>((props, ref) => {\n const { css, label, value, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe();\n\n const { isTruncated, ref: valueRef } = useIsTruncated<HTMLSpanElement>(value);\n\n return (\n <Box ref={ref} css={[styles.project, css]} {...rest}>\n <Text as=\"span\" css={styles.projectLabel}>\n {label}\n </Text>\n {/* Wrapper for the same reason as in Item: the trigger cannot be the element being measured. */}\n <BitkitOverflowTooltip disabled={!isTruncated} text={value}>\n <Box css={styles.projectValueWrapper}>\n <Text as=\"span\" css={styles.projectValue} ref={valueRef}>\n {value}\n </Text>\n </Box>\n </BitkitOverflowTooltip>\n </Box>\n );\n});\n\nBitkitPageSidebarProject.displayName = 'BitkitPageSidebarProject';\n\n// ----- BitkitPageSidebar.GroupHeading -----\n\nexport type BitkitPageSidebarGroupHeadingProps = BitkitGroupHeadingProps;\n\nconst BitkitPageSidebarGroupHeading = forwardRef<HTMLDivElement, BitkitPageSidebarGroupHeadingProps>((props, ref) => {\n return <BitkitGroupHeading ref={ref} paddingBlockEnd=\"8\" paddingBlockStart=\"16\" paddingInline=\"24\" {...props} />;\n});\n\nBitkitPageSidebarGroupHeading.displayName = 'BitkitPageSidebarGroupHeading';\n\n// ----- BitkitPageSidebar.Divider -----\n\nexport type BitkitPageSidebarDividerProps = SeparatorProps;\n\nconst BitkitPageSidebarDivider = forwardRef<HTMLDivElement, BitkitPageSidebarDividerProps>((props, ref) => {\n const { css, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe();\n\n return <Separator ref={ref} css={[styles.divider, css]} {...rest} />;\n});\n\nBitkitPageSidebarDivider.displayName = 'BitkitPageSidebarDivider';\n\nexport default withSubComponents(BitkitPageSidebar, {\n Body: BitkitPageSidebarBody,\n Divider: BitkitPageSidebarDivider,\n Footer: BitkitPageSidebarFooter,\n GroupHeading: BitkitPageSidebarGroupHeading,\n Item: BitkitPageSidebarItem,\n ProgressItem: BitkitPageSidebarProgressItem,\n Project: BitkitPageSidebarProject,\n Title: BitkitPageSidebarTitle,\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"BitkitPageSidebar.js","names":["_Fragment"],"sources":["../../../lib/components/BitkitPageSidebar/BitkitPageSidebar.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Drawer } from '@chakra-ui/react/drawer';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { Separator, type SeparatorProps } from '@chakra-ui/react/separator';\nimport { chakra, type HTMLChakraProps, useChakraContext, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type KeyboardEvent, type MouseEvent, type ReactNode, type Ref } from 'react';\n\nimport useResponsive from '../../hooks/useResponsive';\nimport { type BitkitIconComponent } from '../../icons';\nimport IconChevronLeft from '../../icons/IconChevronLeft';\nimport { type BitkitColorToken } from '../../theme/themeUtils';\nimport useIsTruncated from '../../utilities/useIsTruncated';\nimport { withSubComponents } from '../../utilities/withSubComponents';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitControlButton from '../BitkitControlButton/BitkitControlButton';\nimport BitkitGroupHeading, { type BitkitGroupHeadingProps } from '../BitkitGroupHeading/BitkitGroupHeading';\nimport BitkitOverflowTooltip from '../BitkitOverflowTooltip/BitkitOverflowTooltip';\n\n// The 16 and 24 icons are separate drawings, not one scaled to two sizes, so the breakpoint swap\n// cannot live in the recipe — both are rendered and one is hidden. A `useBreakpointValue` would\n// render a single icon, but it only re-reads on mount: resize a live page across `tablet` and the\n// row resizes underneath an icon that keeps its old size.\nconst showAtDesktop = { display: { base: 'none', tablet: 'block' } };\nconst showAtMobile = { display: { base: 'block', tablet: 'none' } };\n\n// ----- BitkitPageSidebar (Root) -----\n\nexport interface BitkitPageSidebarProps extends BoxProps {\n children: ReactNode;\n /**\n * Header of the mobile drawer panel, naming what the navigation belongs to — pass a\n * `BitkitPageSidebar.DrawerHeader`. Only rendered in drawer mode.\n */\n drawerHeader?: ReactNode;\n /**\n * Mobile drawer visibility. Controlled, because the page header's menu button is what opens it.\n * Passing `onOpenChange` is what opts the sidebar into the drawer below `tablet`; without it the\n * sidebar stays in the flow at full width there. Both are ignored from `tablet` up.\n *\n * Style props and `css` apply to the sidebar beside the page only — the drawer panel sizes\n * itself, so a desktop `width` or padding never reaches the navigation inside it.\n */\n onOpenChange?: (open: boolean) => void;\n open?: boolean;\n}\n\n/**\n * Page-level navigation sidebar. It expects three children, in this order:\n *\n * ```tsx\n * <BitkitPageSidebar>\n * <BitkitPageSidebar.Title>Title</BitkitPageSidebar.Title>\n * <BitkitPageSidebar.Body>\n * <BitkitPageSidebar.Item icon={IconWorkflow}>Workflows</BitkitPageSidebar.Item>\n * </BitkitPageSidebar.Body>\n * <BitkitPageSidebar.Footer>…</BitkitPageSidebar.Footer>\n * </BitkitPageSidebar>\n * ```\n *\n * `Title` and `Footer` are optional. The sidebar scrolls as a single block: when the navigation is\n * taller than the viewport the whole thing — footer included — grows and scrolls with the page,\n * which is why `Body` keeps a minimum gap above the footer so the two blocks never touch.\n *\n * The `<nav>` carries a default `aria-label` so it is not announced as an unnamed landmark. A page\n * with more than one sidebar should name each one — pass your own `aria-label` (or\n * `aria-labelledby`), which overrides the default.\n *\n * It stands at least viewport-tall by default. An embedded panel that should size to its content\n * opts out with `minHeight=\"auto\"`.\n *\n * Below the `tablet` breakpoint it switches to the mobile design on its own — full width, 48px\n * rows, larger icons and labels, a squared-off selection edge. A sidebar that sits beside page\n * content has nowhere to go at that width, so it can become a drawer instead: pass `open` and\n * `onOpenChange` (opened by `BitkitPageHeader`'s menu button) and below `tablet` it slides in\n * from the trailing edge over a backdrop, with its close button on the backdrop beside the panel\n * and a `drawerHeader` naming the page. Both props are ignored from `tablet` up.\n */\nconst BitkitPageSidebar = forwardRef<HTMLElement, BitkitPageSidebarProps>((props, ref) => {\n const { children, css, drawerHeader, onOpenChange, open = false, ...rest } = props;\n\n const { isMobile } = useResponsive();\n const { splitCssProps } = useChakraContext();\n const hasDrawer = onOpenChange !== undefined;\n const isDrawer = isMobile && hasDrawer;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe({ layout: isDrawer ? 'drawer' : hasDrawer ? 'hidden' : 'static' });\n\n if (!isDrawer) {\n return (\n <Box ref={ref} aria-label=\"Page navigation\" asChild css={[styles.root, css]} {...rest}>\n <nav>{children}</nav>\n </Box>\n );\n }\n\n // The consumer's sizing is for the sidebar beside the page; inside the panel only the recipe\n // lays the navigation out.\n const [, navProps] = splitCssProps(rest);\n\n return (\n <Drawer.Root onOpenChange={(details) => onOpenChange(details.open)} open={open}>\n <Portal>\n <Drawer.Backdrop css={styles.drawerBackdrop} />\n <Drawer.Positioner>\n <Drawer.Content aria-label=\"Navigation\" css={styles.drawerContent}>\n {drawerHeader}\n <Box ref={ref} aria-label=\"Page navigation\" asChild css={styles.root} {...navProps}>\n <nav>{children}</nav>\n </Box>\n <Box css={styles.drawerCloseButton}>\n <BitkitCloseButton\n aria-label=\"Close navigation\"\n colorVariant=\"white\"\n onClick={() => onOpenChange(false)}\n size=\"md\"\n />\n </Box>\n </Drawer.Content>\n </Drawer.Positioner>\n </Portal>\n </Drawer.Root>\n );\n});\n\nBitkitPageSidebar.displayName = 'BitkitPageSidebar';\n\n// ----- BitkitPageSidebar.Item -----\n\ninterface BitkitPageSidebarItemCommonProps {\n badge?: ReactNode;\n /**\n * The item's label. Typed as a string rather than `ReactNode` because the row truncates it with\n * an ellipsis, and the overflow tooltip needs the full text to reveal.\n */\n children: string;\n icon?: BitkitIconComponent;\n /**\n * Marks the item as the current page. Mutually exclusive with `state=\"disabled\"` — you cannot be\n * on a page whose nav item is unavailable — but the two are not type-checked against each other:\n * the combination only ever arrives from a computed `selected`, which no union can catch, and\n * checking it would break the ordinary `selected={item.id === currentId}` mapping. If both do\n * turn up, disabled wins, since that is the one of the two the design actually defines.\n */\n selected?: boolean;\n state?: 'disabled';\n suffixIcon?: BitkitIconComponent;\n}\n\ninterface BitkitPageSidebarItemLinkProps\n extends Omit<HTMLChakraProps<'a'>, 'children'>, BitkitPageSidebarItemCommonProps {\n href: string;\n}\n\ninterface BitkitPageSidebarItemButtonProps\n extends Omit<HTMLChakraProps<'button'>, 'children' | 'disabled'>, BitkitPageSidebarItemCommonProps {\n href?: never;\n}\n\nexport type BitkitPageSidebarItemProps = BitkitPageSidebarItemButtonProps | BitkitPageSidebarItemLinkProps;\n\nconst BitkitPageSidebarItem = forwardRef<HTMLButtonElement | HTMLAnchorElement, BitkitPageSidebarItemProps>(\n (props, ref) => {\n const { badge, children, icon: Icon, selected, state, suffixIcon: SuffixIcon, ...rest } = props;\n\n const isDisabled = state === 'disabled';\n // `selected` and `state=\"disabled\"` are not checked against each other in the types, so this is\n // the single place the overlap is resolved: disabled wins, and the two recipe variants can never\n // both claim the same slot.\n const isSelected = selected === true && !isDisabled;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe({ isDisabled, selected: isSelected });\n\n // The design gives neither a selected row nor a disabled one a trailing icon — the desktop\n // (`20014:6498`, `20014:6494`) and mobile (`9221:4417`) items drop it in those states.\n const VisibleSuffixIcon = isSelected || isDisabled ? undefined : SuffixIcon;\n\n const { isTruncated, ref: labelRef } = useIsTruncated<HTMLSpanElement>(children);\n\n const itemContent = (\n <>\n {/* `span`, not the default `div`: in button mode these sit inside a `<button>`, whose\n content model allows phrasing content only. */}\n <Box as=\"span\" css={styles.contentBlock}>\n {Icon && (\n <>\n <Icon css={[styles.itemIcon, showAtDesktop]} size=\"16\" />\n <Icon css={[styles.itemIcon, showAtMobile]} size=\"24\" />\n </>\n )}\n <Text as=\"span\" css={styles.itemLabel} ref={labelRef}>\n {children}\n </Text>\n </Box>\n {badge}\n {VisibleSuffixIcon && (\n <>\n <VisibleSuffixIcon css={[styles.suffixIcon, showAtDesktop]} size=\"16\" />\n <VisibleSuffixIcon css={[styles.suffixIcon, showAtMobile]} size=\"24\" />\n </>\n )}\n {isSelected && <Box as=\"span\" css={styles.selectionMarker} />}\n </>\n );\n\n // The overflow tooltip anchors to the row itself, which is the focusable element: anchored to an\n // inner node it would only ever open on hover, leaving keyboard users unable to read a truncated\n // label. `Tooltip.Trigger` renders `asChild` and composes refs, so the consumer's forwarded ref\n // survives, and the ref measuring the truncation sits on the label further in.\n const withTooltip = (row: ReactNode) => (\n <BitkitOverflowTooltip disabled={!isTruncated} text={children}>\n {row}\n </BitkitOverflowTooltip>\n );\n\n // `chakra.a` / `chakra.button` rather than a named primitive: the item carries its own slot recipe,\n // and Chakra's `Button`/`Link` would layer their own recipe styles on top of it.\n if (rest.href !== undefined) {\n const { className, href, onClick, onKeyDown, ...linkRest } = rest;\n const handleClick = isDisabled\n ? (event: MouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n event.stopPropagation();\n }\n : onClick;\n const handleKeyDown = isDisabled\n ? (event: KeyboardEvent<HTMLAnchorElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n : onKeyDown;\n\n return withTooltip(\n <chakra.a\n ref={ref as Ref<HTMLAnchorElement>}\n {...linkRest}\n aria-current={isSelected ? 'page' : undefined}\n aria-disabled={isDisabled || undefined}\n className={className ? `group ${className}` : 'group'}\n css={styles.item}\n // Dropping the attribute is what actually disables the link, the way `BitkitLink` and\n // `BitkitControlButton` do it. The handlers below only block `click` and `Enter`/`Space`;\n // a middle click fires `auxclick`, and \"Open link in new tab\" never reaches the page at\n // all, so both would still navigate while an `href` is there to follow.\n href={isDisabled ? undefined : href}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n >\n {itemContent}\n </chakra.a>,\n );\n }\n\n const { className, href: _href, ...buttonRest } = rest;\n\n return withTooltip(\n <chakra.button\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n aria-current={isSelected ? 'page' : undefined}\n className={className ? `group ${className}` : 'group'}\n css={styles.item}\n disabled={isDisabled}\n type=\"button\"\n >\n {itemContent}\n </chakra.button>,\n );\n },\n);\n\nBitkitPageSidebarItem.displayName = 'BitkitPageSidebarItem';\n\n// ----- BitkitPageSidebar.ProgressItem -----\n\nexport interface BitkitPageSidebarProgressItemProps extends Omit<HTMLChakraProps<'button'>, 'children' | 'disabled'> {\n /**\n * Trailing content between the label and the connector — a `BitkitBadge` counting what the stage\n * is waiting on. Sized to itself; the connector takes the rest.\n *\n * The design draws it 20px tall, which is `size=\"xs\"`. The badge arrives as a node, so the\n * component cannot set that for you — a taller one grows the row past its 40px.\n */\n badge?: ReactNode;\n /** The row's label. */\n children: ReactNode;\n /**\n * The status icon. Unlike `Item`, whose icon colour the sidebar owns, this one carries the\n * stage's status, so `iconColor` is the consumer's to set — `selected`, `notRequired` and\n * `state=\"disabled\"` still win over it, since those describe the row rather than the status.\n */\n icon: BitkitIconComponent;\n /**\n * A semantic token for the status the icon is showing. Left out, the icon takes the sidebar's own\n * `icon/secondary` — which is what the design gives a stage whose icon carries no status, such as\n * an approvals step.\n *\n * Narrowed to the `icon/*` and `color/*` tokens, so a name that does not exist is a compile error\n * rather than a colour that silently does not apply. Both families are here because the statuses\n * need both: the design takes success and failure from `icon/*` but in-progress from\n * `color/orange/bold`, which the `icon/*` set has no equivalent of.\n */\n iconColor?: BitkitColorToken<'color' | 'icon'>;\n /**\n * A stage the release does not have to clear. Steps the icon and the label down one level, so the\n * row reads as present but not awaited.\n */\n notRequired?: boolean;\n /** Follows the label after a middle dot — `auto-upload`, `not required`. */\n secondaryText?: string;\n selected?: boolean;\n state?: 'disabled';\n /**\n * Draws the dashed rule that runs from the badge to the trailing edge, tying one stage to the\n * next. The last stage in a list has nothing to tie to, so it goes without.\n */\n withConnector?: boolean;\n}\n\n/**\n * A stage of something that progresses, as a row of the page sidebar — a release running through\n * its steps, and whatever comes to look like it.\n *\n * It is a sibling of `Item` rather than a variant of it: the row reports where a stage stands\n * rather than navigating, so its icon is status-coloured, its label does not grow, and a dashed\n * connector runs to the trailing edge between the stages.\n *\n * Always a `<button>`, and always interactive — it carries the shared row's pointer cursor and\n * hover background, so a row without an `onClick` looks actionable and is not. `Item`'s\n * button-or-anchor union is deliberately not here: a stage is selected within the page it is\n * already on rather than navigated to.\n */\nconst BitkitPageSidebarProgressItem = forwardRef<HTMLButtonElement, BitkitPageSidebarProgressItemProps>(\n (props, ref) => {\n const {\n badge,\n children,\n className,\n css,\n icon: Icon,\n iconColor,\n notRequired,\n secondaryText,\n selected,\n state,\n withConnector,\n ...rest\n } = props;\n\n const isDisabled = state === 'disabled';\n // Disabled is the whole state of the row, so the other two are dropped rather than merged: the\n // recipe resolves a combination in its own order, and a disabled row came out of it carrying\n // the selected icon colour. Nothing about a disabled stage is selected or not-required.\n const isSelected = selected && !isDisabled;\n // And `selected` wins over `notRequired`, which the design decides rather than declaration\n // order: its `selected=yes, notRequired=true` variant draws the selected row's icon and label\n // and keeps `not required` in the secondary text -- which is what dropping this produces.\n const isNotRequired = notRequired && !isDisabled && !isSelected;\n\n const statusColor = isDisabled || isNotRequired || isSelected ? undefined : iconColor;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe({ isDisabled, notRequired: isNotRequired, selected: isSelected });\n\n return (\n <chakra.button\n ref={ref}\n {...rest}\n // `step` rather than the `page` an `Item` carries: these are stages of one thing, not\n // separate pages.\n aria-current={isSelected ? 'step' : undefined}\n className={className ? `group ${className}` : 'group'}\n css={[styles.item, styles.progressRow, css]}\n disabled={isDisabled}\n type=\"button\"\n >\n <Box as=\"span\" css={[styles.contentBlock, styles.progressContentBlock]}>\n {/* Two icons, one per size, for the reason `Item` has two -- see `showAtDesktop`.\n The status colour applies while the row is ordinary: `selected`, `notRequired` and\n disabled are states of the row rather than of the status, and the design recolours the\n icon in all three, so the recipe takes it back there. */}\n <Icon color={statusColor} css={[styles.progressIcon, showAtDesktop]} size=\"16\" />\n <Icon color={statusColor} css={[styles.progressIcon, showAtMobile]} size=\"24\" />\n <Box as=\"span\" css={styles.progressTextBlock}>\n <Text as=\"span\" css={styles.progressLabel}>\n {children}\n </Text>\n {secondaryText && (\n <Text as=\"span\" css={styles.progressSecondaryText}>\n {`\\u00b7 ${secondaryText}`}\n </Text>\n )}\n </Box>\n {(badge || withConnector) && (\n <Box as=\"span\" css={styles.progressBlocker}>\n {badge}\n {/* Decorative: it ties the stages together visually and has nothing to separate in\n the button's accessibility tree. */}\n {withConnector && <Separator aria-hidden css={styles.progressConnector} />}\n </Box>\n )}\n </Box>\n {isSelected && <Box as=\"span\" css={styles.selectionMarker} />}\n </chakra.button>\n );\n },\n);\n\nBitkitPageSidebarProgressItem.displayName = 'BitkitPageSidebarProgressItem';\n\n// ----- BitkitPageSidebar.Title -----\n\ninterface BitkitPageSidebarTitleCommonProps extends BoxProps {\n backLabel?: string;\n children: ReactNode;\n}\n\ninterface BitkitPageSidebarTitleLinkProps extends BitkitPageSidebarTitleCommonProps {\n backHref: string;\n onBackClick?: never;\n}\n\ninterface BitkitPageSidebarTitleButtonProps extends BitkitPageSidebarTitleCommonProps {\n backHref?: never;\n onBackClick: () => void;\n}\n\ninterface BitkitPageSidebarTitlePlainProps extends BitkitPageSidebarTitleCommonProps {\n backHref?: never;\n onBackClick?: never;\n}\n\nexport type BitkitPageSidebarTitleProps =\n BitkitPageSidebarTitleButtonProps | BitkitPageSidebarTitleLinkProps | BitkitPageSidebarTitlePlainProps;\n\nconst BitkitPageSidebarTitle = forwardRef<HTMLDivElement, BitkitPageSidebarTitleProps>((props, ref) => {\n const { backHref, backLabel = 'Back', children, css, onBackClick, ...rest } = props;\n\n const hasBack = backHref !== undefined || onBackClick !== undefined;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe({ hasBack });\n\n return (\n <Box ref={ref} css={[styles.title, css]} {...rest}>\n <Box css={styles.titleBlock}>\n {/* The label tooltip opens to the right. Its default `top` has nowhere to go here — the\n button sits at the very top of the sidebar, so the tooltip jams against the viewport\n edge and covers the header instead of flipping. */}\n {hasBack &&\n (backHref !== undefined ? (\n <BitkitControlButton\n href={backHref}\n icon={IconChevronLeft}\n label={backLabel}\n size=\"xs\"\n tooltipProps={{ placement: 'right' }}\n />\n ) : (\n <BitkitControlButton\n icon={IconChevronLeft}\n label={backLabel}\n onClick={onBackClick}\n size=\"xs\"\n tooltipProps={{ placement: 'right' }}\n />\n ))}\n <Text as=\"span\" css={styles.titleText}>\n {children}\n </Text>\n </Box>\n </Box>\n );\n});\n\nBitkitPageSidebarTitle.displayName = 'BitkitPageSidebarTitle';\n\n// ----- BitkitPageSidebar.Body -----\n\nexport interface BitkitPageSidebarBodyProps extends BoxProps {\n children: ReactNode;\n}\n\n/**\n * Holds the navigation items. It absorbs the space left between `Title` and `Footer`, so the footer\n * sits at the bottom of the viewport on a short navigation, and keeps a minimum gap above the\n * footer so the two blocks stay visually separate however long the list gets.\n */\nconst BitkitPageSidebarBody = forwardRef<HTMLDivElement, BitkitPageSidebarBodyProps>((props, ref) => {\n const { children, css, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe();\n\n return (\n <Box ref={ref} css={[styles.body, css]} {...rest}>\n {children}\n </Box>\n );\n});\n\nBitkitPageSidebarBody.displayName = 'BitkitPageSidebarBody';\n\n// ----- BitkitPageSidebar.Footer -----\n\nexport interface BitkitPageSidebarFooterProps extends BoxProps {\n children: ReactNode;\n}\n\nconst BitkitPageSidebarFooter = forwardRef<HTMLDivElement, BitkitPageSidebarFooterProps>((props, ref) => {\n const { children, css, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe();\n\n return (\n <Box ref={ref} css={[styles.footer, css]} {...rest}>\n {children}\n </Box>\n );\n});\n\nBitkitPageSidebarFooter.displayName = 'BitkitPageSidebarFooter';\n\n// ----- BitkitPageSidebar.Project -----\n\nexport interface BitkitPageSidebarProjectProps extends BoxProps {\n label: string;\n value: string;\n}\n\nconst BitkitPageSidebarProject = forwardRef<HTMLDivElement, BitkitPageSidebarProjectProps>((props, ref) => {\n const { css, label, value, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe();\n\n const { isTruncated, ref: valueRef } = useIsTruncated<HTMLSpanElement>(value);\n\n return (\n <Box ref={ref} css={[styles.project, css]} {...rest}>\n <Text as=\"span\" css={styles.projectLabel}>\n {label}\n </Text>\n {/* Wrapper for the same reason as in Item: the trigger cannot be the element being measured. */}\n <BitkitOverflowTooltip disabled={!isTruncated} text={value}>\n <Box css={styles.projectValueWrapper}>\n <Text as=\"span\" css={styles.projectValue} ref={valueRef}>\n {value}\n </Text>\n </Box>\n </BitkitOverflowTooltip>\n </Box>\n );\n});\n\nBitkitPageSidebarProject.displayName = 'BitkitPageSidebarProject';\n\n// ----- BitkitPageSidebar.GroupHeading -----\n\nexport type BitkitPageSidebarGroupHeadingProps = BitkitGroupHeadingProps;\n\nconst BitkitPageSidebarGroupHeading = forwardRef<HTMLDivElement, BitkitPageSidebarGroupHeadingProps>((props, ref) => {\n return <BitkitGroupHeading ref={ref} paddingBlockEnd=\"8\" paddingBlockStart=\"16\" paddingInline=\"24\" {...props} />;\n});\n\nBitkitPageSidebarGroupHeading.displayName = 'BitkitPageSidebarGroupHeading';\n\n// ----- BitkitPageSidebar.DrawerHeader -----\n\nexport interface BitkitPageSidebarDrawerHeaderProps extends Omit<BoxProps, 'title'> {\n /** App icon or user avatar before the title — a 32px `BitkitAvatar`. */\n avatar?: ReactNode;\n /** The entity's secondary line, an app's bundle id or a user's email. Its presence selects the entity layout. */\n secondaryText?: string;\n /** What the navigation belongs to: the page, app or user. */\n title: string;\n}\n\n/**\n * Header of the sidebar's mobile drawer (Figma `mobilePageSidebarHeader`). Pass it as the\n * sidebar's `drawerHeader`; it is what the panel opens with, above the navigation.\n */\nconst BitkitPageSidebarDrawerHeader = forwardRef<HTMLDivElement, BitkitPageSidebarDrawerHeaderProps>((props, ref) => {\n const { avatar, css, secondaryText, title, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe({ hasSecondaryText: secondaryText !== undefined });\n\n return (\n <Box ref={ref} css={[styles.drawerHeader, css]} {...rest}>\n {avatar !== undefined && <Box css={styles.drawerHeaderAvatar}>{avatar}</Box>}\n <Box css={styles.drawerHeaderTextBlock}>\n <Text css={styles.drawerHeaderTitle}>{title}</Text>\n {secondaryText !== undefined && <Text css={styles.drawerHeaderSecondaryText}>{secondaryText}</Text>}\n </Box>\n </Box>\n );\n});\n\nBitkitPageSidebarDrawerHeader.displayName = 'BitkitPageSidebarDrawerHeader';\n\n// ----- BitkitPageSidebar.Divider -----\n\nexport type BitkitPageSidebarDividerProps = SeparatorProps;\n\nconst BitkitPageSidebarDivider = forwardRef<HTMLDivElement, BitkitPageSidebarDividerProps>((props, ref) => {\n const { css, ...rest } = props;\n\n const recipe = useSlotRecipe({ key: 'pageSidebar' });\n const styles = recipe();\n\n return <Separator ref={ref} css={[styles.divider, css]} {...rest} />;\n});\n\nBitkitPageSidebarDivider.displayName = 'BitkitPageSidebarDivider';\n\nexport default withSubComponents(BitkitPageSidebar, {\n Body: BitkitPageSidebarBody,\n Divider: BitkitPageSidebarDivider,\n DrawerHeader: BitkitPageSidebarDrawerHeader,\n Footer: BitkitPageSidebarFooter,\n GroupHeading: BitkitPageSidebarGroupHeading,\n Item: BitkitPageSidebarItem,\n ProgressItem: BitkitPageSidebarProgressItem,\n Project: BitkitPageSidebarProject,\n Title: BitkitPageSidebarTitle,\n});\n"],"mappings":"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"}
@@ -50,7 +50,7 @@ export { default as BitkitOverflowContent, type BitkitOverflowContentProps, } fr
50
50
  export { default as BitkitOverflowTooltip, type BitkitOverflowTooltipProps, } from './BitkitOverflowTooltip/BitkitOverflowTooltip';
51
51
  export { default as BitkitPageFooter, type BitkitPageFooterItemProps, type BitkitPageFooterProps, } from './BitkitPageFooter/BitkitPageFooter';
52
52
  export { default as BitkitPageHeader, type BitkitPageHeaderProps } from './BitkitPageHeader/BitkitPageHeader';
53
- export { default as BitkitPageSidebar, type BitkitPageSidebarBodyProps, type BitkitPageSidebarDividerProps, type BitkitPageSidebarFooterProps, type BitkitPageSidebarGroupHeadingProps, type BitkitPageSidebarItemProps, type BitkitPageSidebarProgressItemProps, type BitkitPageSidebarProjectProps, type BitkitPageSidebarProps, type BitkitPageSidebarTitleProps, } from './BitkitPageSidebar/BitkitPageSidebar';
53
+ export { default as BitkitPageSidebar, type BitkitPageSidebarBodyProps, type BitkitPageSidebarDividerProps, type BitkitPageSidebarDrawerHeaderProps, type BitkitPageSidebarFooterProps, type BitkitPageSidebarGroupHeadingProps, type BitkitPageSidebarItemProps, type BitkitPageSidebarProgressItemProps, type BitkitPageSidebarProjectProps, type BitkitPageSidebarProps, type BitkitPageSidebarTitleProps, } from './BitkitPageSidebar/BitkitPageSidebar';
54
54
  export { default as BitkitPagination, type BitkitPaginationLabels, type BitkitPaginationProps, } from './BitkitPagination/BitkitPagination';
55
55
  export { default as BitkitPaginationLoadMore, type BitkitPaginationLoadMoreProps, } from './BitkitPaginationLoadMore/BitkitPaginationLoadMore';
56
56
  export { default as BitkitPopover, type BitkitPopoverProps } from './BitkitPopover/BitkitPopover';
package/dist/main.js CHANGED
@@ -352,6 +352,7 @@ import BitkitOverflowContent from "./components/BitkitOverflowContent/BitkitOver
352
352
  import BitkitOverflowTooltip from "./components/BitkitOverflowTooltip/BitkitOverflowTooltip.js";
353
353
  import BitkitPageFooter_default from "./components/BitkitPageFooter/BitkitPageFooter.js";
354
354
  import BitkitPageHeader from "./components/BitkitPageHeader/BitkitPageHeader.js";
355
+ import useResponsive, { ResponsiveProvider } from "./hooks/useResponsive.js";
355
356
  import BitkitPageSidebar_default from "./components/BitkitPageSidebar/BitkitPageSidebar.js";
356
357
  import BitkitPagination from "./components/BitkitPagination/BitkitPagination.js";
357
358
  import BitkitPaginationLoadMore from "./components/BitkitPaginationLoadMore/BitkitPaginationLoadMore.js";
@@ -383,7 +384,6 @@ import createBitkitToast, { dismissBitkitToast } from "./components/BitkitToast/
383
384
  import BitkitToggleButton from "./components/BitkitToggleButton/BitkitToggleButton.js";
384
385
  import BitkitTour, { useBitkitTour } from "./components/BitkitTour/BitkitTour.js";
385
386
  import BitkitTreeView, { createTreeCollection } from "./components/BitkitTreeView/BitkitTreeView.js";
386
- import useResponsive, { ResponsiveProvider } from "./hooks/useResponsive.js";
387
387
  import { useUISideNav } from "./components/BitkitUISideNav/UISideNavContext.js";
388
388
  import { uiSideNavRailWidth } from "./theme/slot-recipes/UISideNav.recipe.js";
389
389
  import BitkitUISideNav_default from "./components/BitkitUISideNav/BitkitUISideNav.js";