@box/blueprint-web 16.1.4 → 16.3.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.
@@ -9384,7 +9384,8 @@ table.bp_inline_table_module_inlineTable--b2041[data-modern=true] td:last-child,
9384
9384
  .bp_navigation_menu_module_navigationMenuSeparator--8f207{
9385
9385
  margin-block:var(--space-2);
9386
9386
  }
9387
- .bp_page_module_page--f0685[data-modern=false]{
9387
+
9388
+ .bp_page_module_page--32d56[data-modern=false]{
9388
9389
  --page-navigation-width-collapsed:var(--space-20);
9389
9390
  --page-navigation-width-expanded:calc(var(--space-20)*3);
9390
9391
  --page-viewport-inline-space:var(--space-8);
@@ -9398,9 +9399,10 @@ table.bp_inline_table_module_inlineTable--b2041[data-modern=true] td:last-child,
9398
9399
  --page-medium-global-header-fixed-size-section-height:var(--space-16);
9399
9400
  --page-medium-global-header-fixed-size-section-collapsed:var(--space-16);
9400
9401
  --page-medium-page-main-padding-inline:var(--space-4);
9402
+ --page-side-panel-width:calc(var(--space-20)*4);
9401
9403
  }
9402
9404
 
9403
- .bp_page_module_page--f0685[data-modern=true]{
9405
+ .bp_page_module_page--32d56[data-modern=true]{
9404
9406
  --page-navigation-width-collapsed:var(--bp-space-200);
9405
9407
  --page-navigation-width-expanded:calc(var(--bp-space-200)*3);
9406
9408
  --page-viewport-inline-space:var(--bp-space-080);
@@ -9413,19 +9415,71 @@ table.bp_inline_table_module_inlineTable--b2041[data-modern=true] td:last-child,
9413
9415
  --page-medium-global-header-fixed-size-section-height:var(--bp-space-160);
9414
9416
  --page-medium-global-header-fixed-size-section-collapsed:var(--bp-space-160);
9415
9417
  --page-medium-page-main-padding-inline:var(--bp-space-040);
9418
+ --page-side-panel-width:calc(var(--bp-space-200)*4);
9416
9419
  }
9417
9420
 
9418
- .bp_page_module_page--f0685{
9421
+ .bp_page_module_page--32d56{
9419
9422
  box-sizing:border-box;
9420
9423
  display:flex;
9421
9424
  flex-direction:column;
9422
9425
  height:var(--page-height);
9423
9426
  overflow:hidden;
9424
- padding-block-start:var(--page-padding-block-start);
9425
9427
  padding-inline-start:var(--page-navigation-width-collapsed);
9426
9428
  position:relative;
9427
9429
  }
9428
- .bp_page_module_page--f0685 .bp_page_module_globalHeader--f0685{
9430
+ .bp_page_module_page--32d56 .bp_page_module_pageBody--32d56{
9431
+ display:flex;
9432
+ flex:1;
9433
+ flex-direction:row;
9434
+ overflow:hidden;
9435
+ position:relative;
9436
+ }
9437
+ .bp_page_module_page--32d56 .bp_page_module_pageBody--32d56 .bp_page_module_pageBodyContent--32d56{
9438
+ display:flex;
9439
+ flex:1;
9440
+ flex-direction:column;
9441
+ height:100%;
9442
+ min-width:0;
9443
+ overflow:hidden;
9444
+ padding-block-start:var(--page-padding-block-start);
9445
+ }
9446
+ .bp_page_module_page--32d56 .bp_page_module_sidePanel--32d56{
9447
+ display:none;
9448
+ flex-shrink:0;
9449
+ height:100%;
9450
+ left:0;
9451
+ position:absolute;
9452
+ top:0;
9453
+ width:100%;
9454
+ z-index:var(--page-medium-side-panel-portal-overlay-z-index, 1);
9455
+ }
9456
+ .bp_page_module_page--32d56 .bp_page_module_sidePanel--32d56.bp_page_module_expanded--32d56{
9457
+ display:block;
9458
+ }
9459
+ @media (width <= 767px){
9460
+ .bp_page_module_page--32d56 .bp_page_module_sidePanel--32d56.bp_page_module_expanded--32d56{
9461
+ height:100vh;
9462
+ inset:0;
9463
+ position:fixed;
9464
+ width:100vw;
9465
+ }
9466
+ }
9467
+ @media (width > 767px){
9468
+ .bp_page_module_page--32d56 .bp_page_module_sidePanel--32d56{
9469
+ width:var(--page-side-panel-width);
9470
+ }
9471
+ }
9472
+ @media (width > 1219px){
9473
+ .bp_page_module_page--32d56 .bp_page_module_sidePanel--32d56{
9474
+ position:static;
9475
+ z-index:unset;
9476
+ }
9477
+ }
9478
+ .bp_page_module_page--32d56 .bp_page_module_sidePanel--32d56:empty{
9479
+ min-width:0;
9480
+ width:0;
9481
+ }
9482
+ .bp_page_module_page--32d56 .bp_page_module_globalHeader--32d56{
9429
9483
  align-items:center;
9430
9484
  display:flex;
9431
9485
  flex-direction:row;
@@ -9433,11 +9487,11 @@ table.bp_inline_table_module_inlineTable--b2041[data-modern=true] td:last-child,
9433
9487
  padding-inline:var(--page-viewport-inline-space);
9434
9488
  position:relative;
9435
9489
  }
9436
- .bp_page_module_page--f0685 .bp_page_module_globalHeader--f0685 .bp_page_module_searchContainer--f0685,.bp_page_module_page--f0685 .bp_page_module_globalHeader--f0685 .bp_page_module_sideContentContainer--f0685{
9490
+ .bp_page_module_page--32d56 .bp_page_module_globalHeader--32d56 .bp_page_module_searchContainer--32d56,.bp_page_module_page--32d56 .bp_page_module_globalHeader--32d56 .bp_page_module_sideContentContainer--32d56{
9437
9491
  display:flex;
9438
9492
  flex:none;
9439
9493
  }
9440
- .bp_page_module_page--f0685 .bp_page_module_pageMain--f0685{
9494
+ .bp_page_module_page--32d56 .bp_page_module_pageMain--32d56{
9441
9495
  display:flex;
9442
9496
  flex-direction:column;
9443
9497
  height:inherit;
@@ -9445,32 +9499,32 @@ table.bp_inline_table_module_inlineTable--b2041[data-modern=true] td:last-child,
9445
9499
  padding-block-start:var(--page-padding-block-start);
9446
9500
  padding-inline:var(--page-viewport-inline-space);
9447
9501
  }
9448
- .bp_page_module_page--f0685 .bp_page_module_pageMain--f0685 .bp_page_module_topSection--f0685{
9502
+ .bp_page_module_page--32d56 .bp_page_module_pageMain--32d56 .bp_page_module_topSection--32d56{
9449
9503
  display:flex;
9450
9504
  flex-direction:column;
9451
9505
  flex-shrink:0;
9452
9506
  gap:var(--page-top-section-gap);
9453
9507
  overflow:var(--page-top-section-overflow, hidden);
9454
9508
  }
9455
- .bp_page_module_page--f0685 .bp_page_module_pageMain--f0685 .bp_page_module_mainSection--f0685{
9509
+ .bp_page_module_page--32d56 .bp_page_module_pageMain--32d56 .bp_page_module_mainSection--32d56{
9456
9510
  display:flex;
9457
9511
  flex-direction:row;
9458
9512
  height:inherit;
9459
9513
  overflow:hidden;
9460
9514
  }
9461
- .bp_page_module_page--f0685 .bp_page_module_pageMain--f0685 .bp_page_module_mainSection--f0685 .bp_page_module_content--f0685{
9515
+ .bp_page_module_page--32d56 .bp_page_module_pageMain--32d56 .bp_page_module_mainSection--32d56 .bp_page_module_content--32d56{
9462
9516
  flex-grow:1;
9463
9517
  margin-block-start:var(--page-content-margin-block-start);
9464
9518
  overflow-y:auto;
9465
9519
  }
9466
- .bp_page_module_page--f0685 .bp_page_module_pageMain--f0685 .bp_page_module_mainSection--f0685 .bp_page_module_paddingWithSidebarVisible--f0685{
9520
+ .bp_page_module_page--32d56 .bp_page_module_pageMain--32d56 .bp_page_module_mainSection--32d56 .bp_page_module_paddingWithSidebarVisible--32d56{
9467
9521
  padding-inline-end:var(--page-padding-with-sidebar-inline-end);
9468
9522
  }
9469
- .bp_page_module_page--f0685 .bp_page_module_pageMain--f0685 .bp_page_module_mainSection--f0685 .bp_page_module_sidebar--f0685{
9523
+ .bp_page_module_page--32d56 .bp_page_module_pageMain--32d56 .bp_page_module_mainSection--32d56 .bp_page_module_sidebar--32d56{
9470
9524
  flex-shrink:0;
9471
9525
  margin-inline-start:var(--page-sidebar-margin-inline-start);
9472
9526
  }
9473
- .bp_page_module_page--f0685 .bp_page_module_navigationPortal--f0685{
9527
+ .bp_page_module_page--32d56 .bp_page_module_navigationPortal--32d56{
9474
9528
  background-color:var(--page-navigation-portal-background-color);
9475
9529
  height:100%;
9476
9530
  left:0;
@@ -9478,26 +9532,26 @@ table.bp_inline_table_module_inlineTable--b2041[data-modern=true] td:last-child,
9478
9532
  top:0;
9479
9533
  width:var(--navigation-width);
9480
9534
  }
9481
- .bp_page_module_page--f0685 .bp_page_module_navigationPortal--f0685 .bp_page_module_navigationContent--f0685{
9535
+ .bp_page_module_page--32d56 .bp_page_module_navigationPortal--32d56 .bp_page_module_navigationContent--32d56{
9482
9536
  height:100%;
9483
9537
  }
9484
- .bp_page_module_page--f0685 .bp_page_module_navigationPortalLargeCollapsed--f0685{
9538
+ .bp_page_module_page--32d56 .bp_page_module_navigationPortalLargeCollapsed--32d56{
9485
9539
  --navigation-width:var(--page-navigation-width-collapsed);
9486
9540
  }
9487
- .bp_page_module_page--f0685 .bp_page_module_navigationPortalLargeExpanded--f0685{
9541
+ .bp_page_module_page--32d56 .bp_page_module_navigationPortalLargeExpanded--32d56{
9488
9542
  --navigation-width:var(--page-navigation-width-expanded);
9489
9543
  }
9490
9544
 
9491
- .bp_page_module_hide--f0685{
9545
+ .bp_page_module_hide--32d56{
9492
9546
  visibility:hidden;
9493
9547
  }
9494
9548
 
9495
- .bp_page_module_pageMediumOrLessViewport--f0685{
9549
+ .bp_page_module_pageMediumOrLessViewport--32d56{
9496
9550
  height:100vh;
9497
9551
  padding-block-start:var(--page-medium-global-header-fixed-size-section-height);
9498
9552
  padding-inline-start:0;
9499
9553
  }
9500
- .bp_page_module_pageMediumOrLessViewport--f0685 .bp_page_module_globalHeader--f0685{
9554
+ .bp_page_module_pageMediumOrLessViewport--32d56 .bp_page_module_globalHeader--32d56{
9501
9555
  height:var(--page-medium-global-header-fixed-size-section-height);
9502
9556
  left:0;
9503
9557
  padding-inline:0;
@@ -9506,43 +9560,46 @@ table.bp_inline_table_module_inlineTable--b2041[data-modern=true] td:last-child,
9506
9560
  top:0;
9507
9561
  width:calc(100vw - var(--page-medium-global-header-fixed-size-section-collapsed));
9508
9562
  }
9509
- .bp_page_module_pageMediumOrLessViewport--f0685 .bp_page_module_globalHeader--f0685 .bp_page_module_search--f0685{
9563
+ .bp_page_module_pageMediumOrLessViewport--32d56 .bp_page_module_globalHeader--32d56 .bp_page_module_search--32d56{
9510
9564
  width:var(--page-medium-global-header-fixed-size-section-collapsed);
9511
9565
  }
9512
- .bp_page_module_pageMediumOrLessViewport--f0685 .bp_page_module_pageMain--f0685{
9566
+ .bp_page_module_pageMediumOrLessViewport--32d56 .bp_page_module_pageBody--32d56 .bp_page_module_pageBodyContent--32d56{
9567
+ padding-block-start:0;
9568
+ }
9569
+ .bp_page_module_pageMediumOrLessViewport--32d56 .bp_page_module_pageMain--32d56{
9513
9570
  overflow:var(--page-medium-page-main-overflow-x, hidden) auto;
9514
9571
  padding-inline:var(--page-medium-page-main-padding-inline);
9515
9572
  }
9516
- .bp_page_module_pageMediumOrLessViewport--f0685 .bp_page_module_pageMain--f0685 .bp_page_module_mainSection--f0685{
9573
+ .bp_page_module_pageMediumOrLessViewport--32d56 .bp_page_module_pageMain--32d56 .bp_page_module_mainSection--32d56{
9517
9574
  overflow:unset;
9518
9575
  }
9519
- .bp_page_module_pageMediumOrLessViewport--f0685 .bp_page_module_pageMain--f0685 .bp_page_module_mainSection--f0685 .bp_page_module_content--f0685{
9576
+ .bp_page_module_pageMediumOrLessViewport--32d56 .bp_page_module_pageMain--32d56 .bp_page_module_mainSection--32d56 .bp_page_module_content--32d56{
9520
9577
  overflow-y:unset;
9521
9578
  padding-inline-end:unset;
9522
9579
  }
9523
- .bp_page_module_pageMediumOrLessViewport--f0685 .bp_page_module_navigationPortalOverlayCollapsed--f0685{
9580
+ .bp_page_module_pageMediumOrLessViewport--32d56 .bp_page_module_navigationPortalOverlayCollapsed--32d56{
9524
9581
  height:100%;
9525
9582
  }
9526
- .bp_page_module_pageMediumOrLessViewport--f0685 .bp_page_module_navigationPortalOverlayCollapsed--f0685 .bp_page_module_navigationContent--f0685{
9583
+ .bp_page_module_pageMediumOrLessViewport--32d56 .bp_page_module_navigationPortalOverlayCollapsed--32d56 .bp_page_module_navigationContent--32d56{
9527
9584
  width:var(--page-medium-global-header-fixed-size-section-collapsed);
9528
9585
  }
9529
- .bp_page_module_pageMediumOrLessViewport--f0685 .bp_page_module_navigationPortalOverlayExpanded--f0685{
9586
+ .bp_page_module_pageMediumOrLessViewport--32d56 .bp_page_module_navigationPortalOverlayExpanded--32d56{
9530
9587
  height:100vh;
9531
9588
  width:100vw;
9532
9589
  z-index:var(--page-medium-navigation-portal-overlay-z-index, 1);
9533
9590
  }
9534
9591
 
9535
- .bp_page_module_pageNavigationXXLargeCollapsed--f0685{
9592
+ .bp_page_module_pageNavigationXXLargeCollapsed--32d56{
9536
9593
  --navigation-width:var(--page-navigation-width-collapsed);
9537
9594
  padding-inline-start:var(--page-navigation-width-collapsed);
9538
9595
  }
9539
9596
 
9540
- .bp_page_module_pageNavigationXXLargeExpanded--f0685{
9597
+ .bp_page_module_pageNavigationXXLargeExpanded--32d56{
9541
9598
  --navigation-width:var(--page-navigation-width-expanded);
9542
9599
  padding-inline-start:var(--page-navigation-width-expanded);
9543
9600
  }
9544
9601
 
9545
- .bp_page_module_portalEntry--f0685{
9602
+ .bp_page_module_portalEntry--32d56{
9546
9603
  display:contents;
9547
9604
  }
9548
9605
  .bp_page_header_module_pageHeader--28186[data-modern=false]{
@@ -117,7 +117,7 @@ export { useFullTextTooltip } from './utils/useFullTextTooltip.js';
117
117
  export { useIsEllipsized } from './utils/useIsEllipsized.js';
118
118
  export { getUniqueId, useUniqueId } from './utils/useUniqueId.js';
119
119
  export { VisuallyHidden } from './visually-hidden/visually-hidden.js';
120
- export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSubNavigationPortal } from './page/page-portal.js';
121
- export { useMainSectionSidebar, usePageNavigation } from './page/page-context.js';
120
+ export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal } from './page/page-portal.js';
121
+ export { useMainSectionSidebar, usePageNavigation, useSidePanel } from './page/page-context.js';
122
122
  export { useNotification } from './primitives/notification/notification-provider.js';
123
123
  export { useTabs } from './primitives/tabs/use-tabs.js';
@@ -1,6 +1,6 @@
1
- export { useMainSectionSidebar, usePageNavigation } from './page-context';
1
+ export { useMainSectionSidebar, usePageNavigation, useSidePanel } from './page-context';
2
2
  export type { PagePortalRefs } from './page-context';
3
- export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSubNavigationPortal } from './page-portal';
3
+ export { 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>> & {
@@ -18,6 +18,11 @@ export declare const Page: import("react").ForwardRefExoticComponent<import("./t
18
18
  SearchContainer: import("react").ForwardRefExoticComponent<import("react").HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
19
19
  SideContentContainer: import("react").ForwardRefExoticComponent<import("react").HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
20
20
  };
21
+ /**
22
+ * Complementary panel to the left of the main page content (right of navigation).
23
+ * Content can be portaled here using PageSidePanelPortal from child routes.
24
+ */
25
+ SidePanel: import("react").ForwardRefExoticComponent<import("./types").PageSidePanelProps & import("react").RefAttributes<HTMLDivElement>>;
21
26
  /**
22
27
  * Main content area of the page.
23
28
  * Contains two subcomponents:
@@ -3,10 +3,12 @@ import { MainSection } from './page-main-section.js';
3
3
  import { PageNavigation } from './page-navigation.js';
4
4
  import { PagePageHeader } from './page-page-header.js';
5
5
  import { PageRoot } from './page-root.js';
6
+ import { PageSidePanel } from './page-side-panel.js';
6
7
  import { PageSubNavigation } from './page-subnavigation.js';
7
8
  import 'react';
8
9
  import 'react/jsx-runtime';
9
- export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSubNavigationPortal } from './page-portal.js';
10
+ import '../utils/useBreakpoint.js';
11
+ export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal } from './page-portal.js';
10
12
 
11
13
  const Page = Object.assign(PageRoot, {
12
14
  /**
@@ -20,6 +22,11 @@ const Page = Object.assign(PageRoot, {
20
22
  * - PageGlobalHeader.SideContentContainer
21
23
  */
22
24
  GlobalHeader: PageGlobalHeader,
25
+ /**
26
+ * Complementary panel to the left of the main page content (right of navigation).
27
+ * Content can be portaled here using PageSidePanelPortal from child routes.
28
+ */
29
+ SidePanel: PageSidePanel,
23
30
  /**
24
31
  * Main content area of the page.
25
32
  * Contains two subcomponents:
@@ -6,12 +6,19 @@ export interface PagePortalRefs {
6
6
  pageHeaderEndElementsRef: HTMLDivElement | null;
7
7
  /** Reference to the PageSubNavigation container */
8
8
  pageSubNavigationRef: HTMLDivElement | null;
9
+ /** Reference to the Page.SidePanel container */
10
+ pageSidePanelRef: HTMLDivElement | null;
9
11
  }
10
12
  export interface PageFastContextValuesType extends PagePortalRefs {
13
+ defaultNavigationExpanded?: boolean;
14
+ defaultSidePanelExpanded?: boolean;
11
15
  navigationExpanded: boolean;
16
+ navigationExpandedUserControlled: boolean;
12
17
  pageContainer: HTMLElement | null;
13
18
  globalHeaderContainer: HTMLDivElement | null;
14
19
  mainSectionSidebarVisible: boolean;
20
+ sidePanelExpanded: boolean;
21
+ sidePanelExpandedUserControlled: boolean;
15
22
  }
16
23
  export declare const PageProvider: ({ children }: {
17
24
  children: React.ReactNode;
@@ -26,6 +33,11 @@ export declare const useMainSectionSidebar: () => {
26
33
  setMainSectionSidebarVisible: (visible: boolean) => void;
27
34
  toggleMainSectionSidebarVisible: () => void;
28
35
  };
36
+ export declare const useSidePanel: () => {
37
+ sidePanelExpanded: boolean;
38
+ setSidePanelExpanded: (expanded: boolean) => void;
39
+ toggleSidePanelExpanded: () => void;
40
+ };
29
41
  /**
30
42
  * Hook that creates a callback ref which stores the node in the PageStore
31
43
  * and forwards it to an optional forwardedRef.
@@ -1,5 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  import { createFastContext } from '../utils/fast-context.js';
3
+ import { useBreakpoint, Breakpoint } from '../utils/useBreakpoint.js';
3
4
 
4
5
  const {
5
6
  Provider: PageProvider,
@@ -10,23 +11,34 @@ const {
10
11
  } = createFastContext({
11
12
  globalHeaderContainer: null,
12
13
  mainSectionSidebarVisible: true,
14
+ sidePanelExpanded: false,
15
+ sidePanelExpandedUserControlled: false,
13
16
  navigationExpanded: true,
17
+ navigationExpandedUserControlled: false,
14
18
  pageContainer: null,
15
19
  pageHeaderStartElementsRef: null,
16
20
  pageHeaderEndElementsRef: null,
17
- pageSubNavigationRef: null
21
+ pageSubNavigationRef: null,
22
+ pageSidePanelRef: null
18
23
  });
19
24
  const usePageNavigation = () => {
20
25
  const [navigationExpanded, setPageStore] = usePageStore(store => store.navigationExpanded);
26
+ const breakpoint = useBreakpoint();
21
27
  const setNavigationExpanded = expanded => {
22
28
  setPageStore({
23
- navigationExpanded: expanded
29
+ navigationExpanded: expanded,
30
+ navigationExpandedUserControlled: true
24
31
  });
32
+ // Prevent double-drawer effect when both navigation panel and side panel are expanded
33
+ const shouldCollapseSidePanel = expanded && breakpoint <= Breakpoint.XLarge;
34
+ if (shouldCollapseSidePanel) {
35
+ setPageStore({
36
+ sidePanelExpanded: false
37
+ });
38
+ }
25
39
  };
26
40
  const toggleNavigationExpanded = () => {
27
- setPageStore({
28
- navigationExpanded: !navigationExpanded
29
- });
41
+ setNavigationExpanded(!navigationExpanded);
30
42
  };
31
43
  return {
32
44
  navigationExpanded,
@@ -52,6 +64,31 @@ const useMainSectionSidebar = () => {
52
64
  toggleMainSectionSidebarVisible
53
65
  };
54
66
  };
67
+ const useSidePanel = () => {
68
+ const [sidePanelExpanded, setPageStore] = usePageStore(store => store.sidePanelExpanded);
69
+ const breakpoint = useBreakpoint();
70
+ const setSidePanelExpanded = expanded => {
71
+ setPageStore({
72
+ sidePanelExpanded: expanded,
73
+ sidePanelExpandedUserControlled: true
74
+ });
75
+ // Prevent double-drawer effect when both navigation panel and side panel are expanded
76
+ const shouldCollapseNavigation = expanded && breakpoint <= Breakpoint.XLarge;
77
+ if (shouldCollapseNavigation) {
78
+ setPageStore({
79
+ navigationExpanded: false
80
+ });
81
+ }
82
+ };
83
+ const toggleSidePanelExpanded = () => {
84
+ setSidePanelExpanded(!sidePanelExpanded);
85
+ };
86
+ return {
87
+ sidePanelExpanded,
88
+ setSidePanelExpanded,
89
+ toggleSidePanelExpanded
90
+ };
91
+ };
55
92
  /**
56
93
  * Hook that creates a callback ref which stores the node in the PageStore
57
94
  * and forwards it to an optional forwardedRef.
@@ -73,4 +110,4 @@ function usePageStoreRef(storeKey, forwardedRef) {
73
110
  }, [forwardedRef, setPageStore, storeKey]);
74
111
  }
75
112
 
76
- export { PageProvider, useMainSectionSidebar, usePageNavigation, usePageStore, usePageStoreRef, usePageStoreSafe, usePageStoreSetter, usePageStoreSetterSafe };
113
+ export { PageProvider, useMainSectionSidebar, usePageNavigation, usePageStore, usePageStoreRef, usePageStoreSafe, usePageStoreSetter, usePageStoreSetterSafe, useSidePanel };
@@ -1,11 +1,57 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { Portal, FocusTrapRegion } from '@ariakit/react';
3
3
  import clsx from 'clsx';
4
- import { forwardRef, useRef, useCallback } from 'react';
4
+ import { forwardRef, useRef, useCallback, useEffect } from 'react';
5
5
  import { useBreakpoint, Breakpoint } from '../utils/useBreakpoint.js';
6
6
  import { usePageStore } from './page-context.js';
7
7
  import styles from './page.module.js';
8
8
 
9
+ const useNavigationBreakpointSync = () => {
10
+ const breakpoint = useBreakpoint();
11
+ const [{
12
+ navigationExpandedUserControlled,
13
+ defaultNavigationExpanded,
14
+ sidePanelExpanded,
15
+ navigationExpanded
16
+ }, setPageStore] = usePageStore(store => ({
17
+ navigationExpandedUserControlled: store.navigationExpandedUserControlled,
18
+ defaultNavigationExpanded: store.defaultNavigationExpanded,
19
+ sidePanelExpanded: store.sidePanelExpanded,
20
+ navigationExpanded: store.navigationExpanded
21
+ }));
22
+ useEffect(() => {
23
+ // Prevent double-drawer effect when both navigation panel and side panel are expanded
24
+ const isOverlay = breakpoint <= Breakpoint.XLarge;
25
+ const isDoubleDrawerEffectActive = isOverlay && navigationExpanded && sidePanelExpanded;
26
+ if (isDoubleDrawerEffectActive) {
27
+ setPageStore({
28
+ navigationExpanded: false
29
+ });
30
+ return;
31
+ }
32
+ if (navigationExpandedUserControlled) {
33
+ return;
34
+ }
35
+ if (defaultNavigationExpanded !== undefined) {
36
+ if (defaultNavigationExpanded && isOverlay && sidePanelExpanded) {
37
+ return;
38
+ }
39
+ setPageStore({
40
+ navigationExpanded: defaultNavigationExpanded
41
+ });
42
+ return;
43
+ }
44
+ if (breakpoint < Breakpoint.XXLarge) {
45
+ setPageStore({
46
+ navigationExpanded: false
47
+ });
48
+ } else {
49
+ setPageStore({
50
+ navigationExpanded: true
51
+ });
52
+ }
53
+ }, [breakpoint, defaultNavigationExpanded, navigationExpanded, navigationExpandedUserControlled, setPageStore, sidePanelExpanded]);
54
+ };
9
55
  const createPortalElementDiv = () => {
10
56
  const div = document.createElement('div');
11
57
  // Adds className with display:contents property.
@@ -19,6 +65,7 @@ const PageNavigation = /*#__PURE__*/forwardRef(({
19
65
  children,
20
66
  ...rest
21
67
  }, forwardedRef) => {
68
+ useNavigationBreakpointSync();
22
69
  const [{
23
70
  navigationExpanded,
24
71
  globalHeaderContainer,
@@ -50,3 +50,15 @@ export declare const PageHeaderEndElementsPortal: import("react").ForwardRefExot
50
50
  * ```
51
51
  */
52
52
  export declare const PageSubNavigationPortal: import("react").ForwardRefExoticComponent<PagePortalProps & import("react").RefAttributes<HTMLDivElement>>;
53
+ /**
54
+ * Portal to Page.SidePanel
55
+ * Use this to render side panel content from child routes.
56
+ *
57
+ * @example Conditionally enable portal based on feature flag
58
+ * ```tsx
59
+ * <PageSidePanelPortal isPortalActive={featureEnabled}>
60
+ * <SidePanelContent />
61
+ * </PageSidePanelPortal>
62
+ * ```
63
+ */
64
+ export declare const PageSidePanelPortal: import("react").ForwardRefExoticComponent<PagePortalProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -79,5 +79,17 @@ const PageHeaderEndElementsPortal = createPagePortal('pageHeaderEndElementsRef',
79
79
  * ```
80
80
  */
81
81
  const PageSubNavigationPortal = createPagePortal('pageSubNavigationRef', 'PageSubNavigationPortal');
82
+ /**
83
+ * Portal to Page.SidePanel
84
+ * Use this to render side panel content from child routes.
85
+ *
86
+ * @example Conditionally enable portal based on feature flag
87
+ * ```tsx
88
+ * <PageSidePanelPortal isPortalActive={featureEnabled}>
89
+ * <SidePanelContent />
90
+ * </PageSidePanelPortal>
91
+ * ```
92
+ */
93
+ const PageSidePanelPortal = createPagePortal('pageSidePanelRef', 'PageSidePanelPortal');
82
94
 
83
- export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSubNavigationPortal };
95
+ export { PageHeaderEndElementsPortal, PageHeaderStartElementsPortal, PageSidePanelPortal, PageSubNavigationPortal };
@@ -9,11 +9,13 @@ import { PageGlobalHeader } from './page-global-header.js';
9
9
  import { MainSection } from './page-main-section.js';
10
10
  import { PageNavigation } from './page-navigation.js';
11
11
  import { PagePageHeader } from './page-page-header.js';
12
+ import { PageSidePanel } from './page-side-panel.js';
12
13
  import { PageSubNavigation } from './page-subnavigation.js';
13
14
  import styles from './page.module.js';
14
15
 
15
16
  const PageRoot = /*#__PURE__*/forwardRef(({
16
17
  defaultNavigationExpanded,
18
+ defaultSidePanelExpanded,
17
19
  children,
18
20
  ...rest
19
21
  }, forwardedRef) => {
@@ -22,6 +24,7 @@ const PageRoot = /*#__PURE__*/forwardRef(({
22
24
  ...rest,
23
25
  ref: forwardedRef,
24
26
  defaultNavigationExpanded: defaultNavigationExpanded,
27
+ defaultSidePanelExpanded: defaultSidePanelExpanded,
25
28
  children: children
26
29
  })
27
30
  });
@@ -29,6 +32,7 @@ const PageRoot = /*#__PURE__*/forwardRef(({
29
32
  PageRoot.displayName = 'PageRoot';
30
33
  const PageLayout = /*#__PURE__*/forwardRef(({
31
34
  defaultNavigationExpanded,
35
+ defaultSidePanelExpanded,
32
36
  className,
33
37
  children,
34
38
  ...rest
@@ -43,8 +47,12 @@ const PageLayout = /*#__PURE__*/forwardRef(({
43
47
  }));
44
48
  // this effect has to fire once to set initial state of the navigationExpanded
45
49
  useEffect(() => {
50
+ const sidePanelIsOverlay = breakpoint < Breakpoint.XXLarge;
46
51
  setPageStoreData({
47
- navigationExpanded: defaultNavigationExpanded ?? xxLargeBreakpointActive
52
+ defaultNavigationExpanded,
53
+ defaultSidePanelExpanded,
54
+ navigationExpanded: defaultNavigationExpanded ?? xxLargeBreakpointActive,
55
+ sidePanelExpanded: defaultSidePanelExpanded ?? !sidePanelIsOverlay
48
56
  });
49
57
  // eslint-disable-next-line react-hooks/exhaustive-deps
50
58
  }, []);
@@ -61,16 +69,22 @@ const PageLayout = /*#__PURE__*/forwardRef(({
61
69
  [styles.pageMediumOrLessViewport]: breakpoint <= Breakpoint.Medium
62
70
  }, className),
63
71
  "data-modern": enableModernizedComponents ? 'true' : 'false',
64
- children: [Slots.Navigation, Slots.GlobalHeader, jsxs("div", {
65
- // hide fixed focus trap region problem and overlaying content on the navigation
66
- className: clsx(styles.pageMain, {
67
- [styles.hide]: isFullscreenNavigation
68
- }, className),
69
- role: "main",
70
- children: [jsx("div", {
71
- className: clsx(styles.topSection),
72
- children: Slots.TopSection
73
- }), Slots.MainSection]
72
+ children: [Slots.Navigation, jsxs("div", {
73
+ className: styles.pageBody,
74
+ children: [Slots.SidePanel, jsxs("div", {
75
+ className: styles.pageBodyContent,
76
+ children: [Slots.GlobalHeader, jsxs("div", {
77
+ // hide fixed focus trap region problem and overlaying content on the navigation
78
+ className: clsx(styles.pageMain, {
79
+ [styles.hide]: isFullscreenNavigation
80
+ }, className),
81
+ role: "main",
82
+ children: [jsx("div", {
83
+ className: clsx(styles.topSection),
84
+ children: Slots.TopSection
85
+ }), Slots.MainSection]
86
+ })]
87
+ })]
74
88
  })]
75
89
  });
76
90
  });
@@ -82,6 +96,8 @@ const getChildrenForLandmarks = children => {
82
96
  }
83
97
  if (child.type === PageNavigation) {
84
98
  acc.Navigation = child;
99
+ } else if (child.type === PageSidePanel) {
100
+ acc.SidePanel = child;
85
101
  } else if (child.type === PageGlobalHeader) {
86
102
  acc.GlobalHeader = child;
87
103
  } else if (child.type === MainSection) {
@@ -96,6 +112,7 @@ const getChildrenForLandmarks = children => {
96
112
  Navigation: null,
97
113
  MainSection: null,
98
114
  GlobalHeader: null,
115
+ SidePanel: null,
99
116
  TopSection: []
100
117
  });
101
118
  };
@@ -0,0 +1,2 @@
1
+ import { type PageSidePanelProps } from './types';
2
+ export declare const PageSidePanel: import("react").ForwardRefExoticComponent<PageSidePanelProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,62 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { FocusTrapRegion } from '@ariakit/react';
3
+ import clsx from 'clsx';
4
+ import { forwardRef, useEffect } from 'react';
5
+ import { useBreakpoint, Breakpoint } from '../utils/useBreakpoint.js';
6
+ import { usePageStore, usePageStoreRef } from './page-context.js';
7
+ import styles from './page.module.js';
8
+
9
+ const useSidePanelBreakpointSync = () => {
10
+ const breakpoint = useBreakpoint();
11
+ const [{
12
+ sidePanelExpandedUserControlled,
13
+ defaultSidePanelExpanded
14
+ }, setPageStore] = usePageStore(store => ({
15
+ sidePanelExpandedUserControlled: store.sidePanelExpandedUserControlled,
16
+ defaultSidePanelExpanded: store.defaultSidePanelExpanded
17
+ }));
18
+ useEffect(() => {
19
+ if (sidePanelExpandedUserControlled) {
20
+ return;
21
+ }
22
+ if (defaultSidePanelExpanded !== undefined) {
23
+ setPageStore({
24
+ sidePanelExpanded: defaultSidePanelExpanded
25
+ });
26
+ return;
27
+ }
28
+ if (breakpoint < Breakpoint.XXLarge) {
29
+ setPageStore({
30
+ sidePanelExpanded: false
31
+ });
32
+ } else if (breakpoint >= Breakpoint.XXLarge) {
33
+ setPageStore({
34
+ sidePanelExpanded: true
35
+ });
36
+ }
37
+ }, [breakpoint, defaultSidePanelExpanded, setPageStore, sidePanelExpandedUserControlled]);
38
+ };
39
+ const PageSidePanel = /*#__PURE__*/forwardRef(({
40
+ className,
41
+ children,
42
+ ...rest
43
+ }, forwardedRef) => {
44
+ useSidePanelBreakpointSync();
45
+ const [sidePanelExpanded] = usePageStore(store => store.sidePanelExpanded);
46
+ const setRef = usePageStoreRef('pageSidePanelRef', forwardedRef);
47
+ const breakpoint = useBreakpoint();
48
+ const isFullScreen = breakpoint < Breakpoint.Large && sidePanelExpanded;
49
+ return jsx(FocusTrapRegion, {
50
+ ...rest,
51
+ ref: setRef,
52
+ "aria-expanded": sidePanelExpanded,
53
+ className: clsx(styles.sidePanel, className, {
54
+ [styles.expanded]: sidePanelExpanded
55
+ }),
56
+ enabled: isFullScreen,
57
+ children: children
58
+ });
59
+ });
60
+ PageSidePanel.displayName = 'SidePanel';
61
+
62
+ export { PageSidePanel };
@@ -1,4 +1,4 @@
1
1
  import '../index.css';
2
- var styles = {"page":"bp_page_module_page--f0685","globalHeader":"bp_page_module_globalHeader--f0685","sideContentContainer":"bp_page_module_sideContentContainer--f0685","searchContainer":"bp_page_module_searchContainer--f0685","pageMain":"bp_page_module_pageMain--f0685","topSection":"bp_page_module_topSection--f0685","mainSection":"bp_page_module_mainSection--f0685","content":"bp_page_module_content--f0685","paddingWithSidebarVisible":"bp_page_module_paddingWithSidebarVisible--f0685","sidebar":"bp_page_module_sidebar--f0685","navigationPortal":"bp_page_module_navigationPortal--f0685","navigationContent":"bp_page_module_navigationContent--f0685","navigationPortalLargeCollapsed":"bp_page_module_navigationPortalLargeCollapsed--f0685","navigationPortalLargeExpanded":"bp_page_module_navigationPortalLargeExpanded--f0685","hide":"bp_page_module_hide--f0685","pageMediumOrLessViewport":"bp_page_module_pageMediumOrLessViewport--f0685","navigationPortalOverlayCollapsed":"bp_page_module_navigationPortalOverlayCollapsed--f0685","navigationPortalOverlayExpanded":"bp_page_module_navigationPortalOverlayExpanded--f0685","pageNavigationXXLargeCollapsed":"bp_page_module_pageNavigationXXLargeCollapsed--f0685","pageNavigationXXLargeExpanded":"bp_page_module_pageNavigationXXLargeExpanded--f0685","portalEntry":"bp_page_module_portalEntry--f0685"};
2
+ var styles = {"page":"bp_page_module_page--32d56","pageBody":"bp_page_module_pageBody--32d56","pageBodyContent":"bp_page_module_pageBodyContent--32d56","sidePanel":"bp_page_module_sidePanel--32d56","expanded":"bp_page_module_expanded--32d56","globalHeader":"bp_page_module_globalHeader--32d56","sideContentContainer":"bp_page_module_sideContentContainer--32d56","searchContainer":"bp_page_module_searchContainer--32d56","pageMain":"bp_page_module_pageMain--32d56","topSection":"bp_page_module_topSection--32d56","mainSection":"bp_page_module_mainSection--32d56","content":"bp_page_module_content--32d56","paddingWithSidebarVisible":"bp_page_module_paddingWithSidebarVisible--32d56","sidebar":"bp_page_module_sidebar--32d56","navigationPortal":"bp_page_module_navigationPortal--32d56","navigationContent":"bp_page_module_navigationContent--32d56","navigationPortalLargeCollapsed":"bp_page_module_navigationPortalLargeCollapsed--32d56","navigationPortalLargeExpanded":"bp_page_module_navigationPortalLargeExpanded--32d56","hide":"bp_page_module_hide--32d56","pageMediumOrLessViewport":"bp_page_module_pageMediumOrLessViewport--32d56","navigationPortalOverlayCollapsed":"bp_page_module_navigationPortalOverlayCollapsed--32d56","navigationPortalOverlayExpanded":"bp_page_module_navigationPortalOverlayExpanded--32d56","pageNavigationXXLargeCollapsed":"bp_page_module_pageNavigationXXLargeCollapsed--32d56","pageNavigationXXLargeExpanded":"bp_page_module_pageNavigationXXLargeExpanded--32d56","portalEntry":"bp_page_module_portalEntry--32d56"};
3
3
 
4
4
  export { styles as default };
@@ -11,6 +11,11 @@ export interface PageLayoutProps extends React.HTMLAttributes<HTMLDivElement> {
11
11
  * @default true
12
12
  */
13
13
  defaultMainContentSidebarVisible?: boolean;
14
+ /**
15
+ * Whether the page side panel is initially expanded.
16
+ * When not specified, expanded state depends on the viewport width.
17
+ */
18
+ defaultSidePanelExpanded?: boolean;
14
19
  }
15
20
  export type PageGlobalHeaderProps = React.HTMLAttributes<HTMLDivElement>;
16
21
  export type PagePageHeaderProps = Omit<PageHeaderProps<'div'>, 'variant' | 'sticky' | 'as'>;
@@ -18,6 +23,7 @@ export type PageMainProps = React.HTMLAttributes<HTMLDivElement>;
18
23
  export type PageMainSectionProps = React.HTMLAttributes<HTMLDivElement>;
19
24
  export type PageMainSectionSidebarProps = React.HTMLAttributes<HTMLDivElement>;
20
25
  export type PageSubNavigationProps = React.HTMLAttributes<HTMLDivElement>;
26
+ export type PageSidePanelProps = React.HTMLAttributes<HTMLDivElement>;
21
27
  export type PageProps = PageLayoutProps;
22
28
  export interface PageNavigationProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
23
29
  /**
@@ -42,7 +42,7 @@ const useBreakpoint = () => {
42
42
  return Breakpoint.Large;
43
43
  }
44
44
  if (isXLarge) {
45
- // min 1023px - max 1219px
45
+ // min 1024px - max 1219px
46
46
  return Breakpoint.XLarge;
47
47
  }
48
48
  // min 1220px
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@box/blueprint-web",
3
- "version": "16.1.4",
3
+ "version": "16.3.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.2.2",
50
+ "@box/blueprint-web-assets": "^5.2.4",
51
51
  "@internationalized/date": "^3.12.0",
52
52
  "@radix-ui/primitive": "1.0.1",
53
53
  "@radix-ui/react-accordion": "1.1.2",
@@ -78,7 +78,7 @@
78
78
  "type-fest": "^3.2.0"
79
79
  },
80
80
  "devDependencies": {
81
- "@box/storybook-utils": "^1.1.4",
81
+ "@box/storybook-utils": "^1.1.6",
82
82
  "@figma/code-connect": "1.4.4",
83
83
  "@types/react": "^18.0.0",
84
84
  "@types/react-dom": "^18.0.0",