@atlaskit/page-layout 1.1.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +3 -3
  3. package/__perf__/utils/perf-example.tsx +24 -17
  4. package/__perf__/utils/product-integration/NotificationsPopup.tsx +6 -5
  5. package/__perf__/utils/product-integration/SampleFooter.tsx +13 -13
  6. package/dist/cjs/common/hooks/index.js +23 -0
  7. package/dist/cjs/common/hooks/use-is-sidebar-collapsing.js +46 -0
  8. package/dist/cjs/common/hooks/use-is-sidebar-dragging.js +46 -0
  9. package/dist/cjs/components/resize-control/grab-area.js +42 -4
  10. package/dist/cjs/components/resize-control/index.js +22 -20
  11. package/dist/cjs/components/resize-control/resize-button.js +59 -4
  12. package/dist/cjs/components/resize-control/shadow.js +48 -0
  13. package/dist/cjs/components/skip-links/skip-link-components.js +49 -5
  14. package/dist/cjs/components/slots/banner.js +27 -11
  15. package/dist/cjs/components/slots/content.js +9 -2
  16. package/dist/cjs/components/slots/internal/left-sidebar-inner.js +65 -0
  17. package/dist/cjs/components/slots/internal/left-sidebar-outer.js +100 -0
  18. package/dist/cjs/components/slots/internal/resizable-children-wrapper.js +63 -0
  19. package/dist/cjs/components/slots/internal/slot-focus-ring.js +61 -0
  20. package/dist/cjs/components/slots/left-panel.js +26 -11
  21. package/dist/cjs/components/slots/left-sidebar-without-resize.js +10 -10
  22. package/dist/cjs/components/slots/left-sidebar.js +21 -16
  23. package/dist/cjs/components/slots/main.js +53 -6
  24. package/dist/cjs/components/slots/page-layout.js +10 -3
  25. package/dist/cjs/components/slots/right-panel.js +26 -11
  26. package/dist/cjs/components/slots/right-sidebar.js +57 -13
  27. package/dist/cjs/components/slots/top-navigation.js +27 -11
  28. package/dist/cjs/controllers/sidebar-resize-context.js +2 -1
  29. package/dist/cjs/controllers/sidebar-resize-controller.js +10 -5
  30. package/dist/cjs/version.json +1 -1
  31. package/dist/es2019/common/hooks/index.js +2 -0
  32. package/dist/es2019/common/hooks/use-is-sidebar-collapsing.js +29 -0
  33. package/dist/es2019/common/hooks/use-is-sidebar-dragging.js +29 -0
  34. package/dist/es2019/components/resize-control/grab-area.js +46 -4
  35. package/dist/es2019/components/resize-control/index.js +12 -9
  36. package/dist/es2019/components/resize-control/resize-button.js +61 -4
  37. package/dist/es2019/components/resize-control/shadow.js +43 -0
  38. package/dist/es2019/components/skip-links/skip-link-components.js +47 -5
  39. package/dist/es2019/components/slots/banner.js +21 -7
  40. package/dist/es2019/components/slots/content.js +8 -2
  41. package/dist/es2019/components/slots/internal/left-sidebar-inner.js +52 -0
  42. package/dist/es2019/components/slots/internal/left-sidebar-outer.js +79 -0
  43. package/dist/es2019/components/slots/internal/resizable-children-wrapper.js +49 -0
  44. package/dist/es2019/components/slots/internal/slot-focus-ring.js +50 -0
  45. package/dist/es2019/components/slots/left-panel.js +20 -7
  46. package/dist/es2019/components/slots/left-sidebar-without-resize.js +10 -11
  47. package/dist/es2019/components/slots/left-sidebar.js +20 -17
  48. package/dist/es2019/components/slots/main.js +46 -6
  49. package/dist/es2019/components/slots/page-layout.js +15 -3
  50. package/dist/es2019/components/slots/right-panel.js +20 -7
  51. package/dist/es2019/components/slots/right-sidebar.js +50 -8
  52. package/dist/es2019/components/slots/top-navigation.js +21 -7
  53. package/dist/es2019/controllers/sidebar-resize-context.js +2 -1
  54. package/dist/es2019/controllers/sidebar-resize-controller.js +10 -5
  55. package/dist/es2019/version.json +1 -1
  56. package/dist/esm/common/hooks/index.js +2 -0
  57. package/dist/esm/common/hooks/use-is-sidebar-collapsing.js +34 -0
  58. package/dist/esm/common/hooks/use-is-sidebar-dragging.js +34 -0
  59. package/dist/esm/components/resize-control/grab-area.js +42 -4
  60. package/dist/esm/components/resize-control/index.js +23 -20
  61. package/dist/esm/components/resize-control/resize-button.js +57 -4
  62. package/dist/esm/components/resize-control/shadow.js +37 -0
  63. package/dist/esm/components/skip-links/skip-link-components.js +47 -5
  64. package/dist/esm/components/slots/banner.js +27 -12
  65. package/dist/esm/components/slots/content.js +8 -2
  66. package/dist/esm/components/slots/internal/left-sidebar-inner.js +53 -0
  67. package/dist/esm/components/slots/internal/left-sidebar-outer.js +82 -0
  68. package/dist/esm/components/slots/internal/resizable-children-wrapper.js +51 -0
  69. package/dist/esm/components/slots/internal/slot-focus-ring.js +51 -0
  70. package/dist/esm/components/slots/left-panel.js +26 -12
  71. package/dist/esm/components/slots/left-sidebar-without-resize.js +10 -10
  72. package/dist/esm/components/slots/left-sidebar.js +20 -16
  73. package/dist/esm/components/slots/main.js +49 -8
  74. package/dist/esm/components/slots/page-layout.js +12 -3
  75. package/dist/esm/components/slots/right-panel.js +26 -12
  76. package/dist/esm/components/slots/right-sidebar.js +57 -14
  77. package/dist/esm/components/slots/top-navigation.js +27 -12
  78. package/dist/esm/controllers/sidebar-resize-context.js +2 -1
  79. package/dist/esm/controllers/sidebar-resize-controller.js +10 -5
  80. package/dist/esm/version.json +1 -1
  81. package/dist/types/common/hooks/index.d.ts +2 -0
  82. package/dist/types/common/hooks/use-is-sidebar-collapsing.d.ts +2 -0
  83. package/dist/types/common/hooks/use-is-sidebar-dragging.d.ts +2 -0
  84. package/dist/types/common/utils.d.ts +0 -1
  85. package/dist/types/components/resize-control/shadow.d.ts +6 -0
  86. package/dist/types/components/slots/internal/left-sidebar-inner.d.ts +9 -0
  87. package/dist/types/components/slots/internal/left-sidebar-outer.d.ts +13 -0
  88. package/dist/types/components/slots/internal/resizable-children-wrapper.d.ts +10 -0
  89. package/dist/types/components/slots/internal/slot-focus-ring.d.ts +19 -0
  90. package/dist/types/controllers/sidebar-resize-context.d.ts +1 -0
  91. package/package.json +10 -7
  92. package/dist/cjs/components/resize-control/styles.js +0 -158
  93. package/dist/cjs/components/skip-links/styles.js +0 -58
  94. package/dist/cjs/components/slots/left-sidebar-styles.js +0 -116
  95. package/dist/cjs/components/slots/styles.js +0 -154
  96. package/dist/es2019/components/resize-control/styles.js +0 -139
  97. package/dist/es2019/components/skip-links/styles.js +0 -41
  98. package/dist/es2019/components/slots/left-sidebar-styles.js +0 -96
  99. package/dist/es2019/components/slots/styles.js +0 -130
  100. package/dist/esm/components/resize-control/styles.js +0 -133
  101. package/dist/esm/components/skip-links/styles.js +0 -45
  102. package/dist/esm/components/slots/left-sidebar-styles.js +0 -94
  103. package/dist/esm/components/slots/styles.js +0 -117
  104. package/dist/types/components/resize-control/styles.d.ts +0 -41
  105. package/dist/types/components/skip-links/styles.d.ts +0 -2
  106. package/dist/types/components/slots/left-sidebar-styles.d.ts +0 -5
  107. package/dist/types/components/slots/styles.d.ts +0 -23
@@ -2,12 +2,23 @@ import _extends from "@babel/runtime/helpers/extends";
2
2
 
3
3
  /** @jsx jsx */
4
4
  import { useEffect } from 'react';
5
- import { jsx } from '@emotion/core';
6
- import { DEFAULT_BANNER_HEIGHT, VAR_BANNER_HEIGHT } from '../../common/constants';
5
+ import { css, jsx } from '@emotion/core';
6
+ import { BANNER, BANNER_HEIGHT, DEFAULT_BANNER_HEIGHT, LEFT_PANEL_WIDTH, RIGHT_PANEL_WIDTH, VAR_BANNER_HEIGHT } from '../../common/constants';
7
7
  import { getPageLayoutSlotSelector, resolveDimension } from '../../common/utils';
8
8
  import { publishGridState, useSkipLink } from '../../controllers';
9
+ import SlotFocusRing from './internal/slot-focus-ring';
9
10
  import SlotDimensions from './slot-dimensions';
10
- import { bannerStyles } from './styles';
11
+ const bannerStyles = css({
12
+ height: BANNER_HEIGHT,
13
+ gridArea: BANNER
14
+ });
15
+ const bannerFixedStyles = css({
16
+ position: 'fixed',
17
+ zIndex: 2,
18
+ top: 0,
19
+ right: RIGHT_PANEL_WIDTH,
20
+ left: LEFT_PANEL_WIDTH
21
+ });
11
22
 
12
23
  const Banner = props => {
13
24
  const {
@@ -28,17 +39,20 @@ const Banner = props => {
28
39
  publishGridState({
29
40
  [VAR_BANNER_HEIGHT]: 0
30
41
  });
31
- }; // eslint-disable-next-line react-hooks/exhaustive-deps
42
+ };
32
43
  }, [bannerHeight]);
33
44
  useSkipLink(id, skipLinkTitle);
34
- return jsx("div", _extends({
35
- css: bannerStyles(isFixed),
45
+ return jsx(SlotFocusRing, null, ({
46
+ className
47
+ }) => jsx("div", _extends({
48
+ css: [bannerStyles, isFixed && bannerFixedStyles],
49
+ className: className,
36
50
  "data-testid": testId,
37
51
  id: id
38
52
  }, getPageLayoutSlotSelector('banner')), jsx(SlotDimensions, {
39
53
  variableName: VAR_BANNER_HEIGHT,
40
54
  value: bannerHeight
41
- }), children);
55
+ }), children));
42
56
  };
43
57
 
44
58
  export default Banner;
@@ -1,6 +1,12 @@
1
1
  /** @jsx jsx */
2
- import { jsx } from '@emotion/core';
3
- import { contentStyles } from './styles';
2
+ import { css, jsx } from '@emotion/core';
3
+ import { CONTENT } from '../../common/constants';
4
+ const contentStyles = css({
5
+ display: 'flex',
6
+ height: '100%',
7
+ position: 'relative',
8
+ gridArea: CONTENT
9
+ });
4
10
 
5
11
  const Content = props => {
6
12
  const {
@@ -0,0 +1,52 @@
1
+ /** @jsx jsx */
2
+ import { css, jsx } from '@emotion/core';
3
+ import { easeOut, prefersReducedMotion } from '@atlaskit/motion';
4
+ import { BANNER_HEIGHT, LEFT_PANEL_WIDTH, LEFT_SIDEBAR_FLYOUT_WIDTH, LEFT_SIDEBAR_WIDTH, TOP_NAVIGATION_HEIGHT, TRANSITION_DURATION } from '../../../common/constants';
5
+ import { useIsSidebarDragging } from '../../../common/hooks';
6
+ // eslint-disable-next-line @repo/internal/react/consistent-css-prop-usage
7
+ const prefersReducedMotionStyles = css(prefersReducedMotion());
8
+ /**
9
+ * This inner wrapper is required to allow the sidebar to be `position: fixed`.
10
+ *
11
+ * If we were to apply `position: fixed` to the outer wrapper, it will be popped
12
+ * out of its flex container and Main would stretch to occupy all the space.
13
+ */
14
+
15
+ const fixedInnerStyles = css({
16
+ width: `${LEFT_SIDEBAR_WIDTH}`,
17
+ position: 'fixed',
18
+ top: `calc(${BANNER_HEIGHT} + ${TOP_NAVIGATION_HEIGHT})`,
19
+ bottom: 0,
20
+ left: `${LEFT_PANEL_WIDTH}`,
21
+ transition: `width ${TRANSITION_DURATION}ms ${easeOut} 0s`
22
+ });
23
+ const fixedInnerFlyoutStyles = css({
24
+ width: LEFT_SIDEBAR_FLYOUT_WIDTH
25
+ });
26
+ /**
27
+ * Static in the sense of `position: static`.
28
+ *
29
+ * It will expand the page height to fit its content.
30
+ */
31
+
32
+ const staticInnerStyles = css({
33
+ height: '100%'
34
+ });
35
+ const draggingStyles = css({
36
+ cursor: 'ew-resize',
37
+ // Make sure drag to resize does not animate as the user drags
38
+ transition: 'none'
39
+ });
40
+
41
+ const LeftSidebarInner = ({
42
+ children,
43
+ isFixed = false,
44
+ isFlyoutOpen = false
45
+ }) => {
46
+ const isDragging = useIsSidebarDragging();
47
+ return jsx("div", {
48
+ css: [!isFixed && staticInnerStyles, isFixed && fixedInnerStyles, isFixed && isFlyoutOpen && fixedInnerFlyoutStyles, isDragging && draggingStyles, prefersReducedMotionStyles]
49
+ }, children);
50
+ };
51
+
52
+ export default LeftSidebarInner;
@@ -0,0 +1,79 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+
3
+ /** @jsx jsx */
4
+ import { forwardRef } from 'react';
5
+ import { css, jsx } from '@emotion/core';
6
+ import { easeOut, prefersReducedMotion } from '@atlaskit/motion';
7
+ import { COLLAPSED_LEFT_SIDEBAR_WIDTH, LEFT_SIDEBAR_FLYOUT_WIDTH, LEFT_SIDEBAR_WIDTH, TRANSITION_DURATION } from '../../../common/constants';
8
+ import { useIsSidebarDragging } from '../../../common/hooks';
9
+ import { getPageLayoutSlotSelector } from '../../../common/utils';
10
+ import SlotFocusRing from './slot-focus-ring';
11
+ // eslint-disable-next-line @repo/internal/react/consistent-css-prop-usage
12
+ const prefersReducedMotionStyles = css(prefersReducedMotion());
13
+ const outerStyles = css({
14
+ width: LEFT_SIDEBAR_WIDTH,
15
+ marginLeft: 0,
16
+ position: 'relative',
17
+ zIndex: 1,
18
+ transition: `width ${TRANSITION_DURATION}ms ${easeOut} 0s`,
19
+ ':hover': {
20
+ '--ds--resize-button--opacity': 1
21
+ }
22
+ });
23
+ const draggingStyles = css({
24
+ cursor: 'ew-resize',
25
+ // Make sure drag to resize does not animate as the user drags
26
+ transition: 'none'
27
+ });
28
+ /**
29
+ * In fixed mode this element's child is taken out of the document flow.
30
+ * It doesn't take up the width as expected,
31
+ * so the pseudo element forces it to take up the necessary width.
32
+ */
33
+
34
+ const fixedStyles = css({
35
+ '::after': {
36
+ display: 'inline-block',
37
+ width: `${LEFT_SIDEBAR_WIDTH}`,
38
+ content: "''"
39
+ }
40
+ });
41
+ const flyoutStyles = css({
42
+ width: LEFT_SIDEBAR_FLYOUT_WIDTH
43
+ });
44
+ const flyoutFixedStyles = css({
45
+ width: COLLAPSED_LEFT_SIDEBAR_WIDTH
46
+ });
47
+ const selector = getPageLayoutSlotSelector('left-sidebar');
48
+
49
+ const LeftSidebarOuter = ({
50
+ children,
51
+ isFixed = false,
52
+ isFlyoutOpen = false,
53
+ testId,
54
+ onMouseLeave,
55
+ onMouseOver,
56
+ id
57
+ }, ref) => {
58
+ const isDragging = useIsSidebarDragging();
59
+ return jsx(SlotFocusRing, {
60
+ isSidebar: true
61
+ }, ({
62
+ className
63
+ }) =>
64
+ /**
65
+ * The mouse handlers control flyout behavior, a mouse-only experience.
66
+ */
67
+ // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
68
+ jsx("div", _extends({
69
+ css: [outerStyles, isFixed && fixedStyles, isFlyoutOpen && flyoutStyles, isFlyoutOpen && isFixed && flyoutFixedStyles, isDragging && draggingStyles, prefersReducedMotionStyles],
70
+ className: className,
71
+ "data-testid": testId,
72
+ id: id,
73
+ onMouseOver: onMouseOver,
74
+ onMouseLeave: onMouseLeave,
75
+ ref: ref
76
+ }, selector), children));
77
+ };
78
+
79
+ export default /*#__PURE__*/forwardRef(LeftSidebarOuter);
@@ -0,0 +1,49 @@
1
+ /** @jsx jsx */
2
+ import { css, jsx } from '@emotion/core';
3
+ import { prefersReducedMotion } from '@atlaskit/motion';
4
+ import { TRANSITION_DURATION } from '../../../common/constants';
5
+ import { useIsSidebarCollapsing } from '../../../common/hooks';
6
+ // eslint-disable-next-line @repo/internal/react/consistent-css-prop-usage
7
+ const prefersReducedMotionStyles = css(prefersReducedMotion());
8
+ /**
9
+ * The transition duration is intentionally set to 0ms.
10
+ *
11
+ * A transition is being used here to delay the setting of
12
+ * opacity and visibility so that it syncs collapsing sidebar.
13
+ */
14
+
15
+ const hideLeftSidebarContentsStyles = css({
16
+ opacity: 0,
17
+ transition: `opacity 0ms linear, visibility 0ms linear`,
18
+ transitionDelay: `${TRANSITION_DURATION - 100}ms`,
19
+ visibility: 'hidden'
20
+ });
21
+ const resizableChildrenWrapperStyles = css({
22
+ height: '100%',
23
+ opacity: 1,
24
+ overflow: 'hidden auto',
25
+ transition: 'none',
26
+ visibility: 'visible'
27
+ });
28
+ const fixedChildrenWrapperStyles = css({
29
+ minWidth: 240,
30
+ height: '100%'
31
+ });
32
+
33
+ const ResizableChildrenWrapper = ({
34
+ children,
35
+ isLeftSidebarCollapsed = false,
36
+ hasCollapsedState = false,
37
+ isFlyoutOpen = false
38
+ }) => {
39
+ const isCollapsing = useIsSidebarCollapsing();
40
+ const isCollapsed = isLeftSidebarCollapsed || hasCollapsedState;
41
+ const isHidden = isCollapsing || isCollapsed && !isFlyoutOpen;
42
+ return jsx("div", {
43
+ css: [resizableChildrenWrapperStyles, isHidden && hideLeftSidebarContentsStyles, prefersReducedMotionStyles]
44
+ }, jsx("div", {
45
+ css: fixedChildrenWrapperStyles
46
+ }, children));
47
+ };
48
+
49
+ export default ResizableChildrenWrapper;
@@ -0,0 +1,50 @@
1
+ /** @jsx jsx */
2
+ import { ClassNames, css, jsx } from '@emotion/core';
3
+ import { B100 } from '@atlaskit/theme/colors';
4
+ const focusStyles = css({
5
+ ':focus': {
6
+ outline: 'none',
7
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
8
+ '> div': {
9
+ boxShadow: `0px 0px 0px 2px inset ${`var(--ds-border-focused, ${B100})`}`,
10
+ outline: 'none'
11
+ }
12
+ }
13
+ });
14
+ /**
15
+ * Sidebars have an outer and inner component,
16
+ * so the nested selector needs to target an extra level deeper.
17
+ */
18
+
19
+ const sidebarFocusStyles = css({
20
+ ':focus': {
21
+ outline: 'none',
22
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
23
+ '> div > div': {
24
+ boxShadow: `0px 0px 0px 2px inset ${`var(--ds-border-focused, ${B100})`}`,
25
+ outline: 'none'
26
+ }
27
+ }
28
+ });
29
+ /**
30
+ * We don't use `@atlaskit/focus-ring` here,
31
+ * because we need inset focus styles and:
32
+ *
33
+ * 1. If we set them directly to the layout element,
34
+ * then any child element's background will cover the shadow.
35
+ * 2. We cannot wrap `children` in `FocusRing`,
36
+ * because there's no guarantee the passed child takes `className`.
37
+ */
38
+
39
+ const SlotFocusRing = ({
40
+ children,
41
+ isSidebar = false
42
+ }) => {
43
+ return jsx(ClassNames, null, ({
44
+ css
45
+ }) => children({
46
+ className: isSidebar ? css(sidebarFocusStyles) : css(focusStyles)
47
+ }));
48
+ };
49
+
50
+ export default SlotFocusRing;
@@ -2,12 +2,22 @@ import _extends from "@babel/runtime/helpers/extends";
2
2
 
3
3
  /** @jsx jsx */
4
4
  import { useEffect } from 'react';
5
- import { jsx } from '@emotion/core';
6
- import { DEFAULT_LEFT_PANEL_WIDTH, VAR_LEFT_PANEL_WIDTH } from '../../common/constants';
5
+ import { css, jsx } from '@emotion/core';
6
+ import { DEFAULT_LEFT_PANEL_WIDTH, LEFT_PANEL, LEFT_PANEL_WIDTH, VAR_LEFT_PANEL_WIDTH } from '../../common/constants';
7
7
  import { getPageLayoutSlotSelector, resolveDimension } from '../../common/utils';
8
8
  import { publishGridState, useSkipLink } from '../../controllers';
9
+ import SlotFocusRing from './internal/slot-focus-ring';
9
10
  import SlotDimensions from './slot-dimensions';
10
- import { leftPanelStyles } from './styles';
11
+ const leftPanelStyles = css({
12
+ gridArea: LEFT_PANEL
13
+ });
14
+ const leftPanelFixedStyles = css({
15
+ width: LEFT_PANEL_WIDTH,
16
+ position: 'fixed',
17
+ top: 0,
18
+ bottom: 0,
19
+ left: 0
20
+ });
11
21
 
12
22
  const LeftPanel = props => {
13
23
  const {
@@ -28,17 +38,20 @@ const LeftPanel = props => {
28
38
  publishGridState({
29
39
  [VAR_LEFT_PANEL_WIDTH]: 0
30
40
  });
31
- }; // eslint-disable-next-line react-hooks/exhaustive-deps
41
+ };
32
42
  }, [leftPanelWidth]);
33
43
  useSkipLink(id, skipLinkTitle);
34
- return jsx("div", _extends({
35
- css: leftPanelStyles(isFixed),
44
+ return jsx(SlotFocusRing, null, ({
45
+ className
46
+ }) => jsx("div", _extends({
47
+ css: [leftPanelStyles, isFixed && leftPanelFixedStyles],
48
+ className: className,
36
49
  "data-testid": testId,
37
50
  id: id
38
51
  }, getPageLayoutSlotSelector('left-panel')), jsx(SlotDimensions, {
39
52
  variableName: VAR_LEFT_PANEL_WIDTH,
40
53
  value: leftPanelWidth
41
- }), children);
54
+ }), children));
42
55
  };
43
56
 
44
57
  export default LeftPanel;
@@ -1,12 +1,11 @@
1
- import _extends from "@babel/runtime/helpers/extends";
2
-
3
1
  /** @jsx jsx */
4
2
  import { useEffect } from 'react';
5
3
  import { jsx } from '@emotion/core';
6
4
  import { VAR_LEFT_SIDEBAR_WIDTH } from '../../common/constants';
7
- import { getPageLayoutSlotSelector, resolveDimension } from '../../common/utils';
5
+ import { resolveDimension } from '../../common/utils';
8
6
  import { publishGridState, useSkipLink } from '../../controllers';
9
- import { fixedLeftSidebarInnerStyles, leftSidebarStyles } from './left-sidebar-styles';
7
+ import LeftSidebarInner from './internal/left-sidebar-inner';
8
+ import LeftSidebarOuter from './internal/left-sidebar-outer';
10
9
  import SlotDimensions from './slot-dimensions';
11
10
 
12
11
  const LeftSidebarWithoutResize = props => {
@@ -28,18 +27,18 @@ const LeftSidebarWithoutResize = props => {
28
27
  publishGridState({
29
28
  [VAR_LEFT_SIDEBAR_WIDTH]: 0
30
29
  });
31
- }; // eslint-disable-next-line react-hooks/exhaustive-deps
30
+ };
32
31
  }, [leftSidebarWidth]);
33
32
  useSkipLink(id, skipLinkTitle);
34
- return jsx("div", _extends({
33
+ return jsx(LeftSidebarOuter, {
35
34
  id: id,
36
- "data-testid": testId,
37
- css: leftSidebarStyles(isFixed)
38
- }, getPageLayoutSlotSelector('left-sidebar')), jsx(SlotDimensions, {
35
+ testId: testId,
36
+ isFixed: isFixed
37
+ }, jsx(SlotDimensions, {
39
38
  variableName: VAR_LEFT_SIDEBAR_WIDTH,
40
39
  value: leftSidebarWidth
41
- }), jsx("div", {
42
- css: fixedLeftSidebarInnerStyles(isFixed)
40
+ }), jsx(LeftSidebarInner, {
41
+ isFixed: isFixed
43
42
  }, children));
44
43
  };
45
44
 
@@ -1,13 +1,13 @@
1
- import _extends from "@babel/runtime/helpers/extends";
2
-
3
1
  /** @jsx jsx */
4
2
  import { useContext, useEffect, useRef } from 'react';
5
3
  import { jsx } from '@emotion/core';
6
4
  import { COLLAPSED_LEFT_SIDEBAR_WIDTH, DEFAULT_LEFT_SIDEBAR_WIDTH, FLYOUT_DELAY, RESIZE_BUTTON_SELECTOR, VAR_LEFT_SIDEBAR_FLYOUT, VAR_LEFT_SIDEBAR_WIDTH } from '../../common/constants';
7
- import { getGridStateFromStorage, getPageLayoutSlotSelector, mergeGridStateIntoStorage, resolveDimension } from '../../common/utils';
5
+ import { getGridStateFromStorage, mergeGridStateIntoStorage, resolveDimension } from '../../common/utils';
8
6
  import { publishGridState, SidebarResizeContext, useSkipLink } from '../../controllers';
9
7
  import ResizeControl from '../resize-control';
10
- import { fixedChildrenWrapperStyle, fixedLeftSidebarInnerStyles, leftSidebarStyles, resizeableChildrenWrapperStyle } from './left-sidebar-styles';
8
+ import LeftSidebarInner from './internal/left-sidebar-inner';
9
+ import LeftSidebarOuter from './internal/left-sidebar-outer';
10
+ import ResizableChildrenWrapper from './internal/resizable-children-wrapper';
11
11
  import SlotDimensions from './slot-dimensions';
12
12
 
13
13
  const LeftSidebar = props => {
@@ -112,7 +112,8 @@ const LeftSidebar = props => {
112
112
  isLeftSidebarCollapsed: cachedCollapsedState,
113
113
  leftSidebarWidth,
114
114
  lastLeftSidebarWidth,
115
- flyoutLockCount: 0
115
+ flyoutLockCount: 0,
116
+ isFixed
116
117
  }); // eslint-disable-next-line react-hooks/exhaustive-deps
117
118
  }, []); // Every time other than mount,
118
119
  // update the local storage and css variables.
@@ -202,23 +203,25 @@ const LeftSidebar = props => {
202
203
  };
203
204
 
204
205
  return (// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
205
- jsx("div", _extends({
206
+ jsx(LeftSidebarOuter, {
206
207
  ref: leftSideBarRef,
207
- css: leftSidebarStyles(isFixed, isFlyoutOpen),
208
- "data-testid": testId,
208
+ testId: testId,
209
209
  onMouseOver: onMouseOver,
210
210
  onMouseLeave: onMouseLeave,
211
- id: id
212
- }, getPageLayoutSlotSelector('left-sidebar')), jsx(SlotDimensions, {
211
+ id: id,
212
+ isFixed: isFixed,
213
+ isFlyoutOpen: isFlyoutOpen
214
+ }, jsx(SlotDimensions, {
213
215
  variableName: VAR_LEFT_SIDEBAR_WIDTH,
214
216
  value: notFirstRun.current ? leftSidebarWidth : leftSidebarWidthOnMount
215
- }), jsx("div", {
216
- css: fixedLeftSidebarInnerStyles(isFixed, isFlyoutOpen)
217
- }, jsx("div", {
218
- css: resizeableChildrenWrapperStyle(isFlyoutOpen, isLeftSidebarCollapsed, !notFirstRun.current && collapsedState === 'collapsed')
219
- }, jsx("div", {
220
- css: fixedChildrenWrapperStyle
221
- }, children)), jsx(ResizeControl, {
217
+ }), jsx(LeftSidebarInner, {
218
+ isFixed: isFixed,
219
+ isFlyoutOpen: isFlyoutOpen
220
+ }, jsx(ResizableChildrenWrapper, {
221
+ isFlyoutOpen: isFlyoutOpen,
222
+ isLeftSidebarCollapsed: isLeftSidebarCollapsed,
223
+ hasCollapsedState: !notFirstRun.current && collapsedState === 'collapsed'
224
+ }, children), jsx(ResizeControl, {
222
225
  testId: testId,
223
226
  resizeGrabAreaLabel: resizeGrabAreaLabel,
224
227
  resizeButtonLabel: resizeButtonLabel,
@@ -1,10 +1,40 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
 
3
3
  /** @jsx jsx */
4
- import { jsx } from '@emotion/core';
4
+ import { useContext } from 'react';
5
+ import { css, jsx } from '@emotion/core';
6
+ import { prefersReducedMotion } from '@atlaskit/motion/accessibility';
7
+ import { easeOut } from '@atlaskit/motion/curves';
8
+ import { COLLAPSED_LEFT_SIDEBAR_WIDTH, DEFAULT_LEFT_SIDEBAR_FLYOUT_WIDTH, TRANSITION_DURATION, VAR_LEFT_SIDEBAR_FLYOUT } from '../../common/constants';
9
+ import { useIsSidebarDragging } from '../../common/hooks';
5
10
  import { getPageLayoutSlotSelector } from '../../common/utils';
6
- import { useSkipLink } from '../../controllers';
7
- import { mainStyles } from './styles';
11
+ import { SidebarResizeContext, useSkipLink } from '../../controllers';
12
+ import SlotFocusRing from './internal/slot-focus-ring'; // eslint-disable-next-line @repo/internal/react/consistent-css-prop-usage
13
+
14
+ const prefersReducedMotionStyles = css(prefersReducedMotion());
15
+ const mainStyles = css({
16
+ minWidth: 0,
17
+ marginLeft: 0,
18
+ // Prevent flex container from blowing up when there's super wide content.
19
+ flexGrow: 1,
20
+ // Transition negative margin on main in sync with the increase in width of leftSidebar.
21
+ transition: `margin-left ${TRANSITION_DURATION}ms ${easeOut} 0s`
22
+ });
23
+ const draggingStyles = css({
24
+ cursor: 'ew-resize',
25
+ // Make sure drag to resize remains snappy.
26
+ transition: 'none'
27
+ });
28
+ /**
29
+ * Adds a negative left margin to main,
30
+ * which transitions at the same speed as the left sidebar's width increase.
31
+ * This give an illusion that the flyout is appearing on top of the main content,
32
+ * while main remains in place.
33
+ */
34
+
35
+ const flyoutStyles = css({
36
+ marginLeft: `calc(-1 * var(--${VAR_LEFT_SIDEBAR_FLYOUT}, ${DEFAULT_LEFT_SIDEBAR_FLYOUT_WIDTH}px) + ${COLLAPSED_LEFT_SIDEBAR_WIDTH}px)`
37
+ });
8
38
 
9
39
  const Main = props => {
10
40
  const {
@@ -14,11 +44,21 @@ const Main = props => {
14
44
  skipLinkTitle
15
45
  } = props;
16
46
  useSkipLink(id, skipLinkTitle);
17
- return jsx("div", _extends({
47
+ const isDragging = useIsSidebarDragging();
48
+ const {
49
+ leftSidebarState: {
50
+ isFlyoutOpen,
51
+ isFixed
52
+ }
53
+ } = useContext(SidebarResizeContext);
54
+ return jsx(SlotFocusRing, null, ({
55
+ className
56
+ }) => jsx("div", _extends({
18
57
  "data-testid": testId,
19
- css: mainStyles,
58
+ css: [mainStyles, isDragging && draggingStyles, isFlyoutOpen && !isFixed && flyoutStyles, prefersReducedMotionStyles],
59
+ className: className,
20
60
  id: id
21
- }, getPageLayoutSlotSelector('main')), children);
61
+ }, getPageLayoutSlotSelector('main')), children));
22
62
  };
23
63
 
24
64
  export default Main;
@@ -2,14 +2,26 @@ import _extends from "@babel/runtime/helpers/extends";
2
2
 
3
3
  /** @jsx jsx */
4
4
  import { Fragment } from 'react';
5
- import { jsx } from '@emotion/core';
6
- import { DEFAULT_I18N_PROPS_SKIP_LINKS, PAGE_LAYOUT_CONTAINER_SELECTOR } from '../../common/constants';
5
+ import { css, jsx } from '@emotion/core';
6
+ import { BANNER, BANNER_HEIGHT, CONTENT, DEFAULT_I18N_PROPS_SKIP_LINKS, LEFT_PANEL, LEFT_PANEL_WIDTH, PAGE_LAYOUT_CONTAINER_SELECTOR, RIGHT_PANEL, RIGHT_PANEL_WIDTH, TOP_NAVIGATION, TOP_NAVIGATION_HEIGHT } from '../../common/constants';
7
7
  import { SidebarResizeController, SkipLinksController } from '../../controllers';
8
8
  import { SkipLinkWrapper } from '../skip-links';
9
- import { gridStyles } from './styles';
10
9
  const pageLayoutSelector = {
11
10
  [PAGE_LAYOUT_CONTAINER_SELECTOR]: true
12
11
  };
12
+ const gridTemplateAreas = `
13
+ "${LEFT_PANEL} ${BANNER} ${RIGHT_PANEL}"
14
+ "${LEFT_PANEL} ${TOP_NAVIGATION} ${RIGHT_PANEL}"
15
+ "${LEFT_PANEL} ${CONTENT} ${RIGHT_PANEL}"
16
+ `;
17
+ const gridStyles = css({
18
+ display: 'grid',
19
+ height: '100%',
20
+ gridTemplateAreas,
21
+ gridTemplateColumns: `${LEFT_PANEL_WIDTH} minmax(0, 1fr) ${RIGHT_PANEL_WIDTH}`,
22
+ gridTemplateRows: `${BANNER_HEIGHT} ${TOP_NAVIGATION_HEIGHT} auto`,
23
+ outline: 'none'
24
+ });
13
25
 
14
26
  const PageLayout = ({
15
27
  skipLinksLabel = DEFAULT_I18N_PROPS_SKIP_LINKS,
@@ -2,12 +2,22 @@ import _extends from "@babel/runtime/helpers/extends";
2
2
 
3
3
  /** @jsx jsx */
4
4
  import { useEffect } from 'react';
5
- import { jsx } from '@emotion/core';
6
- import { DEFAULT_RIGHT_PANEL_WIDTH, VAR_RIGHT_PANEL_WIDTH } from '../../common/constants';
5
+ import { css, jsx } from '@emotion/core';
6
+ import { DEFAULT_RIGHT_PANEL_WIDTH, RIGHT_PANEL, RIGHT_PANEL_WIDTH, VAR_RIGHT_PANEL_WIDTH } from '../../common/constants';
7
7
  import { getPageLayoutSlotSelector, resolveDimension } from '../../common/utils';
8
8
  import { publishGridState, useSkipLink } from '../../controllers';
9
+ import SlotFocusRing from './internal/slot-focus-ring';
9
10
  import SlotDimensions from './slot-dimensions';
10
- import { rightPanelStyles } from './styles';
11
+ const baseStyles = css({
12
+ gridArea: RIGHT_PANEL
13
+ });
14
+ const fixedStyles = css({
15
+ width: RIGHT_PANEL_WIDTH,
16
+ position: 'fixed',
17
+ top: 0,
18
+ right: 0,
19
+ bottom: 0
20
+ });
11
21
 
12
22
  const RightPanel = props => {
13
23
  const {
@@ -28,17 +38,20 @@ const RightPanel = props => {
28
38
  publishGridState({
29
39
  [VAR_RIGHT_PANEL_WIDTH]: 0
30
40
  });
31
- }; // eslint-disable-next-line react-hooks/exhaustive-deps
41
+ };
32
42
  }, [rightPanelWidth]);
33
43
  useSkipLink(id, skipLinkTitle);
34
- return jsx("div", _extends({
35
- css: rightPanelStyles(isFixed),
44
+ return jsx(SlotFocusRing, null, ({
45
+ className
46
+ }) => jsx("div", _extends({
47
+ css: [baseStyles, isFixed && fixedStyles],
48
+ className: className,
36
49
  "data-testid": testId,
37
50
  id: id
38
51
  }, getPageLayoutSlotSelector('right-panel')), jsx(SlotDimensions, {
39
52
  variableName: VAR_RIGHT_PANEL_WIDTH,
40
53
  value: rightPanelWidth
41
- }), children);
54
+ }), children));
42
55
  };
43
56
 
44
57
  export default RightPanel;