@box/blueprint-web 16.6.5 → 16.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Internal geometry for ElementalBreadcrumb — path `d` strings exported verbatim
3
+ * from Figma (Elemental breadcrumb node 670:45540, file xZ1gAXsZ1rEAEGwv1whhel).
4
+ * The silhouettes are baked vector paths (12px rounded outer corners + a rounded
5
+ * chevron apex), NOT parametric rectangles, so they are copied as-is. They feed
6
+ * the SVG `viewBox` (unitless px); CSS box sizes convert to rem in the SCSS module.
7
+ *
8
+ * These are implementation details of this one component (not part of the public
9
+ * `@box/blueprint-web` surface), so they live here rather than in `types.ts`.
10
+ *
11
+ * Composition: the trailing (current) step is a plain rounded-right rectangle
12
+ * that sits BEHIND the leading (back) step. The leading step is rounded on the
13
+ * left with a chevron POINT on the right; it overlaps the trailing rectangle so
14
+ * the point reads as an interlocking seam.
15
+ */
16
+ export declare const STEP_H = 40;
17
+ /** Leading (back) step art: 56×40. Rounded-left corners; chevron POINT on the right. */
18
+ export declare const LEADING_W = 56;
19
+ /** Trailing (current) step art: 54×40. Plain rounded-right rectangle, square left edge. */
20
+ export declare const TRAILING_W = 54;
21
+ /** Leading step fill: rounded-left corners, SHARP chevron point at (56,20). */
22
+ export declare const LEADING_PATH = "M40 0H12C5.37259 0 0 5.37258 0 12V28C0 34.6274 5.37259 40 12 40H40L56 20L40 0Z";
23
+ /** Leading step hairline stroke (inside-stroke, inset 0.5px), SHARP apex at (55.36,20). */
24
+ export declare const LEADING_STROKE_PATH = "M12 0.5C5.64873 0.5 0.5 5.64873 0.5 12V28C0.5 34.3513 5.64873 39.5 12 39.5H39.7598L55.3597 20L39.7598 0.5H12Z";
25
+ /** Trailing step fill: plain rounded-right rectangle with a square left edge (viewBox 54). */
26
+ export declare const TRAILING_PATH = "M0 0H42C48.6274 0 54 5.37258 54 12V28C54 34.6274 48.6274 40 42 40H0V0Z";
27
+ /** SVG <linearGradient> handle coords per direction. */
28
+ export declare const GRADIENT_COORDS: {
29
+ readonly vertical: {
30
+ readonly x1: 0;
31
+ readonly y1: 0;
32
+ readonly x2: 0;
33
+ readonly y2: 1;
34
+ };
35
+ readonly horizontal: {
36
+ readonly x1: 0;
37
+ readonly y1: 0;
38
+ readonly x2: 1;
39
+ readonly y2: 0;
40
+ };
41
+ readonly diagonal: {
42
+ readonly x1: 0;
43
+ readonly y1: 0;
44
+ readonly x2: 1;
45
+ readonly y2: 1;
46
+ };
47
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Internal geometry for ElementalBreadcrumb — path `d` strings exported verbatim
3
+ * from Figma (Elemental breadcrumb node 670:45540, file xZ1gAXsZ1rEAEGwv1whhel).
4
+ * The silhouettes are baked vector paths (12px rounded outer corners + a rounded
5
+ * chevron apex), NOT parametric rectangles, so they are copied as-is. They feed
6
+ * the SVG `viewBox` (unitless px); CSS box sizes convert to rem in the SCSS module.
7
+ *
8
+ * These are implementation details of this one component (not part of the public
9
+ * `@box/blueprint-web` surface), so they live here rather than in `types.ts`.
10
+ *
11
+ * Composition: the trailing (current) step is a plain rounded-right rectangle
12
+ * that sits BEHIND the leading (back) step. The leading step is rounded on the
13
+ * left with a chevron POINT on the right; it overlaps the trailing rectangle so
14
+ * the point reads as an interlocking seam.
15
+ */
16
+ const STEP_H = 40;
17
+ /** Leading (back) step art: 56×40. Rounded-left corners; chevron POINT on the right. */
18
+ const LEADING_W = 56;
19
+ /** Trailing (current) step art: 54×40. Plain rounded-right rectangle, square left edge. */
20
+ const TRAILING_W = 54;
21
+ // Leading step: rounded-left with a chevron POINT on the right. Figma's own export
22
+ // rounds the apex (a cubic Bézier), leaving a ~2px notch where the point meets the
23
+ // trailing step. We replace that curve with a SHARP vertex at the apex (56,20 for the
24
+ // fill; ~55.36,20 for the inside-stroke) so the silhouette runs solid through the corner.
25
+ /** Leading step fill: rounded-left corners, SHARP chevron point at (56,20). */
26
+ const LEADING_PATH = 'M40 0H12C5.37259 0 0 5.37258 0 12V28C0 34.6274 5.37259 40 12 40H40L56 20L40 0Z';
27
+ /** Leading step hairline stroke (inside-stroke, inset 0.5px), SHARP apex at (55.36,20). */
28
+ const LEADING_STROKE_PATH = 'M12 0.5C5.64873 0.5 0.5 5.64873 0.5 12V28C0.5 34.3513 5.64873 39.5 12 39.5H39.7598L55.3597 20L39.7598 0.5H12Z';
29
+ /** Trailing step fill: plain rounded-right rectangle with a square left edge (viewBox 54). */
30
+ const TRAILING_PATH = 'M0 0H42C48.6274 0 54 5.37258 54 12V28C54 34.6274 48.6274 40 42 40H0V0Z';
31
+ /** SVG <linearGradient> handle coords per direction. */
32
+ const GRADIENT_COORDS = {
33
+ vertical: {
34
+ x1: 0,
35
+ y1: 0,
36
+ x2: 0,
37
+ y2: 1
38
+ },
39
+ horizontal: {
40
+ x1: 0,
41
+ y1: 0,
42
+ x2: 1,
43
+ y2: 0
44
+ },
45
+ diagonal: {
46
+ x1: 0,
47
+ y1: 0,
48
+ x2: 1,
49
+ y2: 1
50
+ }
51
+ };
52
+
53
+ export { GRADIENT_COORDS, LEADING_PATH, LEADING_STROKE_PATH, LEADING_W, STEP_H, TRAILING_PATH, TRAILING_W };
@@ -0,0 +1,2 @@
1
+ import { type ElementalBreadcrumbProps } from './types';
2
+ export declare const ElementalBreadcrumb: import("react").ForwardRefExoticComponent<Omit<ElementalBreadcrumbProps, "ref"> & import("react").RefAttributes<HTMLElement>>;
@@ -0,0 +1,134 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { bpIconIconOnDark, bpSize060, bpIconIconOnLightSecondary } from '@box/blueprint-web-assets/tokens/tokens';
3
+ import clsx from 'clsx';
4
+ import { forwardRef, useId, createElement } from 'react';
5
+ import { VisuallyHidden } from '../visually-hidden/visually-hidden.js';
6
+ import { LEADING_PATH, LEADING_STROKE_PATH, TRAILING_PATH, GRADIENT_COORDS, LEADING_W, STEP_H, TRAILING_W } from './constants.js';
7
+ import styles from './elemental-breadcrumb.module.js';
8
+
9
+ // The leading chevron apex is a SHARP point at the art's right edge (x = LEADING_W).
10
+ // It must map flush to the box's right edge so the point stays sharp and butts against
11
+ // the trailing step — so DON'T pad the right side. Pad only left/top/bottom by 1px so the
12
+ // 1px inside hairline stroke isn't clipped by the SVG's overflow box.
13
+ // (`preserveAspectRatio="none"` stretches viewBox → box, so any right padding would pull
14
+ // the apex inward and let edge antialiasing round it off.)
15
+ const LEADING_VIEW_BOX = `-1 -1 ${LEADING_W + 1} ${STEP_H + 2}`;
16
+ const TRAILING_VIEW_BOX = `-1 -1 ${TRAILING_W + 2} ${STEP_H + 2}`;
17
+ const renderCurrentStepPaint = (fill, gradientId) => {
18
+ if (fill.type === 'solid') {
19
+ return {
20
+ paint: fill.color,
21
+ defs: null
22
+ };
23
+ }
24
+ const {
25
+ x1,
26
+ y1,
27
+ x2,
28
+ y2
29
+ } = GRADIENT_COORDS[fill.direction ?? 'vertical'];
30
+ const lastStopIndex = fill.stops.length - 1;
31
+ return {
32
+ paint: `url(#${gradientId})`,
33
+ defs: jsx("linearGradient", {
34
+ id: gradientId,
35
+ x1: x1,
36
+ x2: x2,
37
+ y1: y1,
38
+ y2: y2,
39
+ children: fill.stops.map((stopColor, index) =>
40
+ // Key by index: `stops` may legitimately repeat a color, so the color alone is not unique.
41
+ jsx("stop", {
42
+ offset: index / lastStopIndex,
43
+ stopColor: stopColor
44
+ }, `${index}-${stopColor}`))
45
+ })
46
+ };
47
+ };
48
+ const ElementalBreadcrumb = /*#__PURE__*/forwardRef((props, forwardedRef) => {
49
+ const {
50
+ navAriaLabel,
51
+ backAriaLabel,
52
+ currentStepLabel,
53
+ backIcon,
54
+ currentIcon,
55
+ currentStepFill,
56
+ currentIconColor = bpIconIconOnDark,
57
+ onBack,
58
+ className,
59
+ ...rest
60
+ } = props;
61
+ const gradientId = useId();
62
+ const {
63
+ paint,
64
+ defs
65
+ } = renderCurrentStepPaint(currentStepFill, gradientId);
66
+ const backIconElement = /*#__PURE__*/createElement(backIcon, {
67
+ 'aria-hidden': true,
68
+ className: clsx(styles.icon, styles.leadingIcon),
69
+ color: bpIconIconOnLightSecondary,
70
+ height: bpSize060,
71
+ width: bpSize060
72
+ });
73
+ const currentIconElement = /*#__PURE__*/createElement(currentIcon, {
74
+ 'aria-hidden': true,
75
+ className: clsx(styles.icon, styles.trailingIcon),
76
+ color: currentIconColor,
77
+ height: bpSize060,
78
+ width: bpSize060
79
+ });
80
+ return jsx("nav", {
81
+ ref: forwardedRef,
82
+ "aria-label": navAriaLabel,
83
+ className: clsx(styles.nav, className),
84
+ ...rest,
85
+ children: jsxs("ol", {
86
+ className: styles.list,
87
+ children: [jsx("li", {
88
+ className: clsx(styles.step, styles.leadingStep),
89
+ children: jsxs("button", {
90
+ "aria-label": backAriaLabel,
91
+ className: styles.backButton,
92
+ onClick: onBack,
93
+ type: "button",
94
+ children: [jsxs("svg", {
95
+ "aria-hidden": "true",
96
+ className: styles.shape,
97
+ preserveAspectRatio: "none",
98
+ viewBox: LEADING_VIEW_BOX,
99
+ children: [jsx("path", {
100
+ className: styles.leadingShape,
101
+ d: LEADING_PATH
102
+ }), jsx("path", {
103
+ className: styles.leadingStroke,
104
+ d: LEADING_STROKE_PATH,
105
+ fill: "none",
106
+ strokeWidth: 1,
107
+ vectorEffect: "non-scaling-stroke"
108
+ })]
109
+ }), backIconElement]
110
+ })
111
+ }), jsxs("li", {
112
+ "aria-current": "page",
113
+ className: clsx(styles.step, styles.trailingStep),
114
+ children: [jsxs("svg", {
115
+ "aria-hidden": "true",
116
+ className: styles.shape,
117
+ preserveAspectRatio: "none",
118
+ viewBox: TRAILING_VIEW_BOX,
119
+ children: [defs && jsx("defs", {
120
+ children: defs
121
+ }), jsx("path", {
122
+ d: TRAILING_PATH,
123
+ fill: paint
124
+ })]
125
+ }), currentIconElement, jsx(VisuallyHidden, {
126
+ children: currentStepLabel
127
+ })]
128
+ })]
129
+ })
130
+ });
131
+ });
132
+ ElementalBreadcrumb.displayName = 'ElementalBreadcrumb';
133
+
134
+ export { ElementalBreadcrumb };
@@ -0,0 +1,4 @@
1
+ import '../index.css';
2
+ var styles = {"nav":"bp_elemental_breadcrumb_module_nav--06561","list":"bp_elemental_breadcrumb_module_list--06561","step":"bp_elemental_breadcrumb_module_step--06561","leadingStep":"bp_elemental_breadcrumb_module_leadingStep--06561","trailingStep":"bp_elemental_breadcrumb_module_trailingStep--06561","shape":"bp_elemental_breadcrumb_module_shape--06561","leadingShape":"bp_elemental_breadcrumb_module_leadingShape--06561","leadingStroke":"bp_elemental_breadcrumb_module_leadingStroke--06561","backButton":"bp_elemental_breadcrumb_module_backButton--06561","icon":"bp_elemental_breadcrumb_module_icon--06561","leadingIcon":"bp_elemental_breadcrumb_module_leadingIcon--06561","trailingIcon":"bp_elemental_breadcrumb_module_trailingIcon--06561"};
3
+
4
+ export { styles as default };
@@ -0,0 +1,2 @@
1
+ export { ElementalBreadcrumb } from './elemental-breadcrumb';
2
+ export type { CurrentStepFill, ElementalBreadcrumbProps } from './types';
@@ -0,0 +1,57 @@
1
+ import { type ComponentProps, type FunctionComponent, type PropsWithChildren, type SVGProps } from 'react';
2
+ type BlueprintIcon = FunctionComponent<PropsWithChildren<SVGProps<SVGSVGElement>>>;
3
+ /**
4
+ * Fill for the current (trailing, product) step. Each product supplies its own
5
+ * brand fill — the component bakes in no colors. Modeled as a discriminated
6
+ * union because products differ in BOTH fill type AND gradient direction:
7
+ * - Extract: diagonal gradient #FE03DC → #2784FA
8
+ * - Forms: SOLID #20A56E (no gradient)
9
+ * - Automate: vertical gradient #F34936 → #F27621
10
+ *
11
+ * Values are rendered as SVG paint (a solid `fill` or an SVG `<linearGradient>`
12
+ * built from `stops`), NOT as a CSS `linear-gradient()` string — an SVG `fill`
13
+ * cannot consume one.
14
+ */
15
+ export type CurrentStepFill = {
16
+ type: 'solid';
17
+ color: string;
18
+ } | {
19
+ type: 'gradient';
20
+ /** 2+ gradient stops in paint order. Typed as a non-empty pair to keep offsets well-defined. */
21
+ stops: [string, string, ...string[]];
22
+ /**
23
+ * Gradient axis. Maps to SVG <linearGradient> handle coords.
24
+ * @default 'vertical'
25
+ */
26
+ direction?: 'vertical' | 'horizontal' | 'diagonal';
27
+ };
28
+ export type ElementalBreadcrumbProps = Omit<ComponentProps<'nav'>, 'aria-label'> & {
29
+ /** Accessible label for the <nav> landmark. e.g. "Breadcrumb". Required — Blueprint is globalization-agnostic. */
30
+ navAriaLabel: string;
31
+ /**
32
+ * Accessible label for the interactive leading (back) step. REQUIRED — the
33
+ * component never derives it. The consumer must pass a meaningful, context-
34
+ * specific label that names where the back step leads (e.g. "Go back to apps",
35
+ * "Go back to Documents"), because the leading step only renders an icon and
36
+ * has no visible text. Blueprint is globalization-agnostic, so this is a plain
37
+ * (already-localized) string, not an i18n key.
38
+ */
39
+ backAriaLabel: string;
40
+ /** Accessible name for the current (trailing, non-interactive) step. e.g. "Extract Agent". */
41
+ currentStepLabel: string;
42
+ /** Icon rendered inside the leading (back) step. */
43
+ backIcon: BlueprintIcon;
44
+ /** Icon rendered inside the trailing (current) step. */
45
+ currentIcon: BlueprintIcon;
46
+ /** Product-specific fill for the current step (solid or gradient). See CurrentStepFill. */
47
+ currentStepFill: CurrentStepFill;
48
+ /**
49
+ * Icon color on the current step. Choose for contrast against
50
+ * `currentStepFill` (consumer's responsibility — axe checks in tests).
51
+ * @default '#fff'
52
+ */
53
+ currentIconColor?: string;
54
+ /** Invoked when the user activates the leading step (behaves as a back button). */
55
+ onBack: () => void;
56
+ };
57
+ export {};
@@ -7065,6 +7065,84 @@
7065
7065
  .bp_date_picker_module_datePickerPopoverContent--ee85f{
7066
7066
  z-index:380;
7067
7067
  }
7068
+
7069
+ .bp_elemental_breadcrumb_module_nav--06561{
7070
+ display:inline-flex;
7071
+ }
7072
+
7073
+ .bp_elemental_breadcrumb_module_list--06561{
7074
+ height:2.5rem;
7075
+ list-style:none;
7076
+ margin:0;
7077
+ padding:0;
7078
+ position:relative;
7079
+ width:5.875rem;
7080
+ }
7081
+
7082
+ .bp_elemental_breadcrumb_module_step--06561{
7083
+ height:2.5rem;
7084
+ position:absolute;
7085
+ top:0;
7086
+ }
7087
+
7088
+ .bp_elemental_breadcrumb_module_leadingStep--06561{
7089
+ left:0;
7090
+ width:3.5rem;
7091
+ z-index:1;
7092
+ }
7093
+
7094
+ .bp_elemental_breadcrumb_module_trailingStep--06561{
7095
+ left:2.5rem;
7096
+ width:3.375rem;
7097
+ }
7098
+
7099
+ .bp_elemental_breadcrumb_module_shape--06561{
7100
+ height:100%;
7101
+ inset:0;
7102
+ position:absolute;
7103
+ width:100%;
7104
+ }
7105
+
7106
+ .bp_elemental_breadcrumb_module_leadingShape--06561{
7107
+ fill:var(--bp-surface-surface);
7108
+ }
7109
+
7110
+ .bp_elemental_breadcrumb_module_leadingStroke--06561{
7111
+ stroke:var(--blueprint-elemental-breadcrumb-hairline, #0000001f);
7112
+ }
7113
+
7114
+ .bp_elemental_breadcrumb_module_backButton--06561{
7115
+ background:none;
7116
+ border:none;
7117
+ cursor:pointer;
7118
+ display:block;
7119
+ height:100%;
7120
+ padding:0;
7121
+ width:100%;
7122
+ }
7123
+ .bp_elemental_breadcrumb_module_backButton--06561:focus-visible{
7124
+ border-radius:var(--bp-radius-05);
7125
+ outline:var(--bp-border-02) solid var(--bp-outline-focus-on-light);
7126
+ outline-offset:var(--bp-space-005);
7127
+ }
7128
+ .bp_elemental_breadcrumb_module_backButton--06561:hover .bp_elemental_breadcrumb_module_leadingShape--06561{
7129
+ fill:var(--bp-surface-surface-hover);
7130
+ }
7131
+
7132
+ .bp_elemental_breadcrumb_module_icon--06561{
7133
+ pointer-events:none;
7134
+ position:absolute;
7135
+ top:50%;
7136
+ transform:translateY(-50%);
7137
+ }
7138
+
7139
+ .bp_elemental_breadcrumb_module_leadingIcon--06561{
7140
+ left:var(--bp-space-030);
7141
+ }
7142
+
7143
+ .bp_elemental_breadcrumb_module_trailingIcon--06561{
7144
+ right:var(--bp-space-030);
7145
+ }
7068
7146
  .bp_empty_state_module_emptyState--dd02f{
7069
7147
  align-items:center;
7070
7148
  display:flex;
@@ -19,6 +19,7 @@ export * from './content-card';
19
19
  export * from './data-table/data-table';
20
20
  export * from './date-picker';
21
21
  export * from './divider';
22
+ export * from './elemental-breadcrumb';
22
23
  export * from './ellipsizable-text';
23
24
  export * from './empty-state';
24
25
  export * from './focusable';
@@ -28,6 +28,7 @@ export { ContentCard } from './content-card/content-card.js';
28
28
  export { DataTableWrapper, StickyCell } from './data-table/data-table.js';
29
29
  export { DatePicker } from './date-picker/date-picker.js';
30
30
  export { Divider } from './divider/divider.js';
31
+ export { ElementalBreadcrumb } from './elemental-breadcrumb/elemental-breadcrumb.js';
31
32
  export { EllipsizableText } from './ellipsizable-text/ellipsizable-text.js';
32
33
  export { EmptyState } from './empty-state/empty-state.js';
33
34
  export { Focusable } from './focusable/focusable.js';
@@ -117,7 +118,7 @@ export { useFullTextTooltip } from './utils/useFullTextTooltip.js';
117
118
  export { useIsEllipsized } from './utils/useIsEllipsized.js';
118
119
  export { getUniqueId, useUniqueId } from './utils/useUniqueId.js';
119
120
  export { VisuallyHidden } from './visually-hidden/visually-hidden.js';
120
- export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal } from './page/page-portal.js';
121
+ export { PageGlobalHeaderSearchContainerPortal, PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal } from './page/page-portal.js';
121
122
  export { useMainSectionSidebar, usePageNavigation, useSidePanel } from './page/page-context.js';
122
123
  export { useNotification } from './primitives/notification/notification-provider.js';
123
124
  export { useTabs } from './primitives/tabs/use-tabs.js';
@@ -1,6 +1,6 @@
1
1
  export { useMainSectionSidebar, usePageNavigation, useSidePanel } from './page-context';
2
2
  export type { PagePortalRefs } from './page-context';
3
- export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal, } from './page-portal';
3
+ export { PageGlobalHeaderSearchContainerPortal, PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal, } from './page-portal';
4
4
  export type { PagePortalProps } from './page-portal';
5
5
  export * from './types';
6
6
  export declare const Page: import("react").ForwardRefExoticComponent<import("./types").PageLayoutProps & import("react").RefAttributes<HTMLDivElement>> & {
@@ -11,7 +11,7 @@ export declare const Page: import("react").ForwardRefExoticComponent<import("./t
11
11
  /**
12
12
  * Top level header landmark placed at the top of the page.
13
13
  * It should contain two subcomponents:
14
- * - PageGlobalHeader.SearchContainer
14
+ * - PageGlobalHeader.SearchContainer (content can be portaled here using PageGlobalHeaderSearchContainerPortal)
15
15
  * - PageGlobalHeader.SideContentContainer
16
16
  */
17
17
  GlobalHeader: import("react").ForwardRefExoticComponent<import("./types").PageGlobalHeaderProps & import("react").RefAttributes<HTMLDivElement>> & {
@@ -8,7 +8,7 @@ import { PageSubNavigation } from './page-subnavigation.js';
8
8
  import 'react';
9
9
  import 'react/jsx-runtime';
10
10
  import '../utils/useBreakpoint.js';
11
- export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal } from './page-portal.js';
11
+ export { PageGlobalHeaderSearchContainerPortal, PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal } from './page-portal.js';
12
12
 
13
13
  const Page = Object.assign(PageRoot, {
14
14
  /**
@@ -18,7 +18,7 @@ const Page = Object.assign(PageRoot, {
18
18
  /**
19
19
  * Top level header landmark placed at the top of the page.
20
20
  * It should contain two subcomponents:
21
- * - PageGlobalHeader.SearchContainer
21
+ * - PageGlobalHeader.SearchContainer (content can be portaled here using PageGlobalHeaderSearchContainerPortal)
22
22
  * - PageGlobalHeader.SideContentContainer
23
23
  */
24
24
  GlobalHeader: PageGlobalHeader,
@@ -4,6 +4,8 @@ export interface PagePortalRefs {
4
4
  pageHeaderStartElementsRef: HTMLDivElement | null;
5
5
  /** Reference to the PageHeader.EndElements container */
6
6
  pageHeaderEndElementsRef: HTMLDivElement | null;
7
+ /** Reference to the Page.GlobalHeader.SearchContainer */
8
+ globalHeaderSearchContainerRef: HTMLDivElement | null;
7
9
  /** Reference to the PageSubNavigation container */
8
10
  pageSubNavigationRef: HTMLDivElement | null;
9
11
  /** Reference to the Page.SidePanel container */
@@ -18,6 +18,7 @@ const {
18
18
  pageContainer: null,
19
19
  pageHeaderStartElementsRef: null,
20
20
  pageHeaderEndElementsRef: null,
21
+ globalHeaderSearchContainerRef: null,
21
22
  pageSubNavigationRef: null,
22
23
  pageSidePanelRef: null
23
24
  });
@@ -1,15 +1,17 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import clsx from 'clsx';
3
3
  import { forwardRef } from 'react';
4
+ import { usePageStoreRef } from './page-context.js';
4
5
  import styles from './page.module.js';
5
6
 
6
7
  const SearchContainer = /*#__PURE__*/forwardRef(({
7
8
  className,
8
9
  ...rest
9
- }, ref) => {
10
+ }, forwardedRef) => {
11
+ const setRef = usePageStoreRef('globalHeaderSearchContainerRef', forwardedRef);
10
12
  return jsx("div", {
11
13
  ...rest,
12
- ref: ref,
14
+ ref: setRef,
13
15
  className: clsx(styles.searchContainer, className),
14
16
  role: "search"
15
17
  });
@@ -62,3 +62,15 @@ export declare const PageSubNavigationPortal: import("react").ForwardRefExoticCo
62
62
  * ```
63
63
  */
64
64
  export declare const PageSidePanelPortal: import("react").ForwardRefExoticComponent<PagePortalProps & import("react").RefAttributes<HTMLDivElement>>;
65
+ /**
66
+ * Portal to Page.GlobalHeader.SearchContainer
67
+ * Use this to render global search from child routes.
68
+ *
69
+ * @example Conditionally enable portal based on feature flag
70
+ * ```tsx
71
+ * <PageGlobalHeaderSearchContainerPortal isPortalActive={featureEnabled}>
72
+ * <QuickSearch />
73
+ * </PageGlobalHeaderSearchContainerPortal>
74
+ * ```
75
+ */
76
+ export declare const PageGlobalHeaderSearchContainerPortal: import("react").ForwardRefExoticComponent<PagePortalProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -91,5 +91,17 @@ const PageSubNavigationPortal = createPagePortal('pageSubNavigationRef', 'PageSu
91
91
  * ```
92
92
  */
93
93
  const PageSidePanelPortal = createPagePortal('pageSidePanelRef', 'PageSidePanelPortal');
94
+ /**
95
+ * Portal to Page.GlobalHeader.SearchContainer
96
+ * Use this to render global search from child routes.
97
+ *
98
+ * @example Conditionally enable portal based on feature flag
99
+ * ```tsx
100
+ * <PageGlobalHeaderSearchContainerPortal isPortalActive={featureEnabled}>
101
+ * <QuickSearch />
102
+ * </PageGlobalHeaderSearchContainerPortal>
103
+ * ```
104
+ */
105
+ const PageGlobalHeaderSearchContainerPortal = createPagePortal('globalHeaderSearchContainerRef', 'PageGlobalHeaderSearchContainerPortal');
94
106
 
95
- export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal };
107
+ export { PageGlobalHeaderSearchContainerPortal, PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@box/blueprint-web",
3
- "version": "16.6.5",
3
+ "version": "16.7.0",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": {
@@ -47,7 +47,7 @@
47
47
  "dependencies": {
48
48
  "@ariakit/react": "0.4.21",
49
49
  "@ariakit/react-core": "0.4.21",
50
- "@box/blueprint-web-assets": "^5.4.0",
50
+ "@box/blueprint-web-assets": "^5.4.1",
51
51
  "@internationalized/date": "^3.12.0",
52
52
  "@radix-ui/react-accordion": "1.1.2",
53
53
  "@radix-ui/react-checkbox": "1.0.4",
@@ -77,7 +77,7 @@
77
77
  "type-fest": "^3.2.0"
78
78
  },
79
79
  "devDependencies": {
80
- "@box/storybook-utils": "^1.1.19",
80
+ "@box/storybook-utils": "^1.1.20",
81
81
  "@figma/code-connect": "1.4.4",
82
82
  "@types/react": "^18.0.0",
83
83
  "@types/react-dom": "^18.0.0",