@atlaskit/page-layout 1.6.5 → 1.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/common/constants.js +5 -1
  3. package/dist/cjs/components/resize-control/index.js +16 -24
  4. package/dist/cjs/components/resize-control/resize-button.js +10 -4
  5. package/dist/cjs/components/skip-links/skip-link-components.js +1 -1
  6. package/dist/cjs/components/slots/internal/left-sidebar-inner.js +29 -2
  7. package/dist/cjs/components/slots/internal/left-sidebar-outer.js +40 -7
  8. package/dist/cjs/components/slots/internal/resizable-children-wrapper.js +1 -1
  9. package/dist/cjs/components/slots/left-sidebar.js +137 -39
  10. package/dist/cjs/components/slots/main.js +1 -1
  11. package/dist/cjs/components/slots/page-layout.js +10 -1
  12. package/dist/cjs/components/slots/slot-dimensions.js +5 -2
  13. package/dist/cjs/controllers/sidebar-resize-context.js +2 -1
  14. package/dist/cjs/controllers/sidebar-resize-controller.js +96 -35
  15. package/dist/cjs/version.json +1 -1
  16. package/dist/es2019/common/constants.js +2 -0
  17. package/dist/es2019/components/resize-control/index.js +15 -23
  18. package/dist/es2019/components/resize-control/resize-button.js +13 -4
  19. package/dist/es2019/components/skip-links/skip-link-components.js +1 -1
  20. package/dist/es2019/components/slots/internal/left-sidebar-inner.js +34 -3
  21. package/dist/es2019/components/slots/internal/left-sidebar-outer.js +51 -8
  22. package/dist/es2019/components/slots/internal/resizable-children-wrapper.js +1 -1
  23. package/dist/es2019/components/slots/left-sidebar.js +142 -42
  24. package/dist/es2019/components/slots/main.js +1 -1
  25. package/dist/es2019/components/slots/page-layout.js +17 -1
  26. package/dist/es2019/components/slots/slot-dimensions.js +5 -2
  27. package/dist/es2019/controllers/sidebar-resize-context.js +2 -1
  28. package/dist/es2019/controllers/sidebar-resize-controller.js +96 -38
  29. package/dist/es2019/version.json +1 -1
  30. package/dist/esm/common/constants.js +2 -0
  31. package/dist/esm/components/resize-control/index.js +15 -23
  32. package/dist/esm/components/resize-control/resize-button.js +10 -4
  33. package/dist/esm/components/skip-links/skip-link-components.js +1 -1
  34. package/dist/esm/components/slots/internal/left-sidebar-inner.js +29 -3
  35. package/dist/esm/components/slots/internal/left-sidebar-outer.js +42 -9
  36. package/dist/esm/components/slots/internal/resizable-children-wrapper.js +1 -1
  37. package/dist/esm/components/slots/left-sidebar.js +140 -42
  38. package/dist/esm/components/slots/main.js +1 -1
  39. package/dist/esm/components/slots/page-layout.js +10 -1
  40. package/dist/esm/components/slots/slot-dimensions.js +5 -2
  41. package/dist/esm/controllers/sidebar-resize-context.js +2 -1
  42. package/dist/esm/controllers/sidebar-resize-controller.js +96 -35
  43. package/dist/esm/version.json +1 -1
  44. package/dist/types/common/constants.d.ts +2 -0
  45. package/dist/types/components/resize-control/types.d.ts +0 -2
  46. package/dist/types/components/slots/internal/left-sidebar-outer.d.ts +1 -1
  47. package/dist/types/components/slots/left-sidebar.d.ts +6 -0
  48. package/dist/types/components/slots/slot-dimensions.d.ts +2 -1
  49. package/dist/types/controllers/sidebar-resize-context.d.ts +10 -0
  50. package/dist/types-ts4.5/common/constants.d.ts +2 -0
  51. package/dist/types-ts4.5/components/resize-control/types.d.ts +0 -2
  52. package/dist/types-ts4.5/components/slots/internal/left-sidebar-outer.d.ts +1 -1
  53. package/dist/types-ts4.5/components/slots/left-sidebar.d.ts +6 -0
  54. package/dist/types-ts4.5/components/slots/slot-dimensions.d.ts +2 -1
  55. package/dist/types-ts4.5/controllers/sidebar-resize-context.d.ts +10 -0
  56. package/package.json +12 -6
  57. package/report.api.md +7 -6
  58. package/tmp/api-report-tmp.d.ts +174 -0
@@ -11,6 +11,8 @@ var _react = _interopRequireWildcard(require("react"));
11
11
  var _bindEventListener = require("bind-event-listener");
12
12
  var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
13
13
  var _motion = require("@atlaskit/motion");
14
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
15
+ var _responsive = require("@atlaskit/primitives/responsive");
14
16
  var _constants = require("../common/constants");
15
17
  var _utils = require("../common/utils");
16
18
  var _sidebarResizeContext = require("./sidebar-resize-context");
@@ -18,8 +20,7 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio
18
20
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
19
21
  var handleDataAttributesAndCb = function handleDataAttributesAndCb() {
20
22
  var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _noop.default;
21
- var isLeftSidebarCollapsed = arguments.length > 1 ? arguments[1] : undefined;
22
- var leftSidebarState = arguments.length > 2 ? arguments[2] : undefined;
23
+ var leftSidebarState = arguments.length > 1 ? arguments[1] : undefined;
23
24
  document.documentElement.removeAttribute(_constants.IS_SIDEBAR_COLLAPSING);
24
25
  callback(leftSidebarState);
25
26
  };
@@ -41,7 +42,13 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
41
42
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
42
43
  leftSidebarState = _useState2[0],
43
44
  setLeftSidebarState = _useState2[1];
44
- var isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed;
45
+ var leftSidebarWidth = leftSidebarState.leftSidebarWidth,
46
+ lastLeftSidebarWidth = leftSidebarState.lastLeftSidebarWidth,
47
+ isResizing = leftSidebarState.isResizing,
48
+ flyoutLockCount = leftSidebarState.flyoutLockCount,
49
+ isFixed = leftSidebarState.isFixed,
50
+ isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed,
51
+ isFlyoutOpen = leftSidebarState.isFlyoutOpen;
45
52
 
46
53
  // We put the latest callbacks into a ref so we can always have the latest
47
54
  // functions in our transitionend listeners
@@ -54,23 +61,46 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
54
61
  onExpand: onExpand,
55
62
  onCollapse: onCollapse
56
63
  };
57
- });
64
+ }, [onExpand, onCollapse]);
58
65
  var transition = (0, _react.useRef)(null);
66
+ var mobileMediaQuery = (0, _platformFeatureFlags.getBooleanFF)('platform.design-system-team.responsive-page-layout-left-sidebar_p8r7g') ?
67
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- With the feature flag, this does not apply as it should be static.
68
+ (0, _responsive.UNSAFE_useMediaQuery)('below.md') : null;
69
+ var isOpen = mobileMediaQuery !== null && mobileMediaQuery !== void 0 && mobileMediaQuery.matches ? isFlyoutOpen : !isLeftSidebarCollapsed;
59
70
  var expandLeftSidebar = (0, _react.useCallback)(function () {
60
- var _transition$current, _transition$current2;
61
- var lastLeftSidebarWidth = leftSidebarState.lastLeftSidebarWidth,
62
- isResizing = leftSidebarState.isResizing,
63
- flyoutLockCount = leftSidebarState.flyoutLockCount,
64
- isFixed = leftSidebarState.isFixed,
65
- isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed;
71
+ var _transition$current2, _transition$current3;
72
+ if (isOpen) {
73
+ return;
74
+ }
75
+
76
+ // If the user is at a mobile viewport when this runs, we handle it differently
77
+ // We don't expand at mobile widths; instead we use a flyout which is treated the same otherwise
78
+ if (mobileMediaQuery !== null && mobileMediaQuery !== void 0 && mobileMediaQuery.matches) {
79
+ var _transition$current;
80
+ var flyoutOpenSidebarState = {
81
+ isResizing: false,
82
+ isLeftSidebarCollapsed: true,
83
+ leftSidebarWidth: _constants.COLLAPSED_LEFT_SIDEBAR_WIDTH,
84
+ lastLeftSidebarWidth: leftSidebarWidth,
85
+ isFlyoutOpen: true,
86
+ flyoutLockCount: 0,
87
+ isFixed: isFixed
88
+ };
89
+ setLeftSidebarState(flyoutOpenSidebarState);
90
+
91
+ // Flush the desktop transitions, cleanup, and call the `onExpand` still
92
+ (_transition$current = transition.current) === null || _transition$current === void 0 ? void 0 : _transition$current.complete();
93
+ handleDataAttributesAndCb(stableRef.current.onExpand, flyoutOpenSidebarState);
94
+ return;
95
+ }
66
96
  if (isResizing || !isLeftSidebarCollapsed ||
67
97
  // already expanding
68
- ((_transition$current = transition.current) === null || _transition$current === void 0 ? void 0 : _transition$current.action) === 'expand') {
98
+ ((_transition$current2 = transition.current) === null || _transition$current2 === void 0 ? void 0 : _transition$current2.action) === 'expand') {
69
99
  return;
70
100
  }
71
101
 
72
102
  // flush existing transition
73
- (_transition$current2 = transition.current) === null || _transition$current2 === void 0 ? void 0 : _transition$current2.complete();
103
+ (_transition$current3 = transition.current) === null || _transition$current3 === void 0 ? void 0 : _transition$current3.complete();
74
104
  var width = Math.max(lastLeftSidebarWidth, _constants.DEFAULT_LEFT_SIDEBAR_WIDTH);
75
105
  var updatedLeftSidebarState = {
76
106
  isLeftSidebarCollapsed: false,
@@ -83,9 +113,7 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
83
113
  };
84
114
  setLeftSidebarState(updatedLeftSidebarState);
85
115
  function finish() {
86
- handleDataAttributesAndCb(stableRef.current.onExpand, false,
87
- // isCollapsed
88
- updatedLeftSidebarState);
116
+ handleDataAttributesAndCb(stableRef.current.onExpand, updatedLeftSidebarState);
89
117
  }
90
118
  var sidebar = document.querySelector(leftSidebarSelector);
91
119
  // onTransitionEnd isn't triggered when a user prefers reduced motion
@@ -97,8 +125,8 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
97
125
  type: 'transitionend',
98
126
  listener: function listener(event) {
99
127
  if (event.target === sidebar && event.propertyName === 'width') {
100
- var _transition$current3;
101
- (_transition$current3 = transition.current) === null || _transition$current3 === void 0 ? void 0 : _transition$current3.complete();
128
+ var _transition$current4;
129
+ (_transition$current4 = transition.current) === null || _transition$current4 === void 0 ? void 0 : _transition$current4.complete();
102
130
  }
103
131
  }
104
132
  });
@@ -114,22 +142,41 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
114
142
  }
115
143
  };
116
144
  transition.current = value;
117
- }, [leftSidebarState]);
145
+ }, [isOpen, mobileMediaQuery, isResizing, isLeftSidebarCollapsed, lastLeftSidebarWidth, flyoutLockCount, isFixed, leftSidebarWidth]);
118
146
  var collapseLeftSidebar = (0, _react.useCallback)(function (event, collapseWithoutTransition) {
119
- var _transition$current4, _transition$current5;
120
- var leftSidebarWidth = leftSidebarState.leftSidebarWidth,
121
- isResizing = leftSidebarState.isResizing,
122
- flyoutLockCount = leftSidebarState.flyoutLockCount,
123
- isFixed = leftSidebarState.isFixed,
124
- isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed;
147
+ var _transition$current6, _transition$current7;
148
+ if (!isOpen) {
149
+ return;
150
+ }
151
+
152
+ // If the user is at a mobile viewport when this runs, we handle it differently
153
+ // We don't collapse at mobile widths; instead we close the flyout.
154
+ if (mobileMediaQuery !== null && mobileMediaQuery !== void 0 && mobileMediaQuery.matches) {
155
+ var _transition$current5;
156
+ var flyoutCloseSidebarState = {
157
+ isResizing: false,
158
+ isLeftSidebarCollapsed: true,
159
+ leftSidebarWidth: _constants.COLLAPSED_LEFT_SIDEBAR_WIDTH,
160
+ lastLeftSidebarWidth: lastLeftSidebarWidth,
161
+ isFlyoutOpen: false,
162
+ flyoutLockCount: 0,
163
+ isFixed: isFixed
164
+ };
165
+ setLeftSidebarState(flyoutCloseSidebarState);
166
+
167
+ // Flush the desktop transitions, cleanup, and call the `onCollapse` still
168
+ (_transition$current5 = transition.current) === null || _transition$current5 === void 0 ? void 0 : _transition$current5.complete();
169
+ handleDataAttributesAndCb(stableRef.current.onCollapse, flyoutCloseSidebarState);
170
+ return;
171
+ }
125
172
  if (isResizing || isLeftSidebarCollapsed ||
126
173
  // already collapsing
127
- ((_transition$current4 = transition.current) === null || _transition$current4 === void 0 ? void 0 : _transition$current4.action) === 'collapse') {
174
+ ((_transition$current6 = transition.current) === null || _transition$current6 === void 0 ? void 0 : _transition$current6.action) === 'collapse') {
128
175
  return;
129
176
  }
130
177
 
131
178
  // flush existing transition
132
- (_transition$current5 = transition.current) === null || _transition$current5 === void 0 ? void 0 : _transition$current5.complete();
179
+ (_transition$current7 = transition.current) === null || _transition$current7 === void 0 ? void 0 : _transition$current7.complete();
133
180
 
134
181
  // data-attribute is used as a CSS selector to sync the hiding/showing
135
182
  // of the nav contents with expand/collapse animation
@@ -145,7 +192,7 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
145
192
  };
146
193
  setLeftSidebarState(updatedLeftSidebarState);
147
194
  function finish() {
148
- handleDataAttributesAndCb(stableRef.current.onCollapse, true, updatedLeftSidebarState);
195
+ handleDataAttributesAndCb(stableRef.current.onCollapse, updatedLeftSidebarState);
149
196
  }
150
197
  var sidebar = document.querySelector(leftSidebarSelector);
151
198
 
@@ -158,8 +205,8 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
158
205
  type: 'transitionend',
159
206
  listener: function listener(event) {
160
207
  if (sidebar === event.target && event.propertyName === 'width') {
161
- var _transition$current6;
162
- (_transition$current6 = transition.current) === null || _transition$current6 === void 0 ? void 0 : _transition$current6.complete();
208
+ var _transition$current8;
209
+ (_transition$current8 = transition.current) === null || _transition$current8 === void 0 ? void 0 : _transition$current8.complete();
163
210
  }
164
211
  }
165
212
  });
@@ -175,24 +222,38 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
175
222
  }
176
223
  };
177
224
  transition.current = value;
178
- }, [leftSidebarState]);
225
+ }, [isOpen, mobileMediaQuery, isResizing, isLeftSidebarCollapsed, leftSidebarWidth, flyoutLockCount, isFixed, lastLeftSidebarWidth]);
226
+
227
+ /**
228
+ * Conditionally toggle the expanding or collapsing the sidebars.
229
+ * This supports our mobile flyout mode as well.
230
+ */
231
+ var toggleLeftSidebar = (0, _react.useCallback)(function (event, collapseWithoutTransition) {
232
+ if (isOpen) {
233
+ collapseLeftSidebar(event, collapseWithoutTransition);
234
+ } else {
235
+ expandLeftSidebar();
236
+ }
237
+ }, [isOpen, expandLeftSidebar, collapseLeftSidebar]);
179
238
 
180
239
  // Make sure we finish any lingering transitions when unmounting
181
240
  (0, _react.useEffect)(function mount() {
182
241
  return function unmount() {
183
- var _transition$current7;
184
- (_transition$current7 = transition.current) === null || _transition$current7 === void 0 ? void 0 : _transition$current7.abort();
242
+ var _transition$current9;
243
+ (_transition$current9 = transition.current) === null || _transition$current9 === void 0 ? void 0 : _transition$current9.abort();
185
244
  };
186
245
  }, []);
187
246
  var context = (0, _react.useMemo)(function () {
188
247
  return {
189
- isLeftSidebarCollapsed: isLeftSidebarCollapsed,
248
+ isLeftSidebarCollapsed: !isOpen,
249
+ // Technically this isn't quite true, but with mobile it's a bit safer if products are using this to roll their own collapse/expand
190
250
  expandLeftSidebar: expandLeftSidebar,
191
251
  collapseLeftSidebar: collapseLeftSidebar,
192
252
  leftSidebarState: leftSidebarState,
193
- setLeftSidebarState: setLeftSidebarState
253
+ setLeftSidebarState: setLeftSidebarState,
254
+ toggleLeftSidebar: toggleLeftSidebar
194
255
  };
195
- }, [isLeftSidebarCollapsed, expandLeftSidebar, collapseLeftSidebar, leftSidebarState]);
256
+ }, [isOpen, expandLeftSidebar, collapseLeftSidebar, leftSidebarState, toggleLeftSidebar]);
196
257
  return /*#__PURE__*/_react.default.createElement(_sidebarResizeContext.SidebarResizeContext.Provider, {
197
258
  value: context
198
259
  }, children);
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/page-layout",
3
- "version": "1.6.5",
3
+ "version": "1.7.0",
4
4
  "sideEffects": false
5
5
  }
@@ -28,9 +28,11 @@ export const DEFAULT_LEFT_PANEL_WIDTH = 368;
28
28
 
29
29
  // Other constants
30
30
  export const COLLAPSED_LEFT_SIDEBAR_WIDTH = 20;
31
+ export const MOBILE_COLLAPSED_LEFT_SIDEBAR_WIDTH = 16;
31
32
  export const MIN_LEFT_SIDEBAR_WIDTH = 80;
32
33
  export const DEFAULT_LEFT_SIDEBAR_FLYOUT_WIDTH = 240;
33
34
  export const MIN_LEFT_SIDEBAR_DRAG_THRESHOLD = 200;
35
+ export const MAX_MOBILE_SIDEBAR_FLYOUT_WIDTH = 350;
34
36
  export const TRANSITION_DURATION = 300;
35
37
  export const FLYOUT_DELAY = 200;
36
38
  export const LEFT_SIDEBAR_EXPANDED_WIDTH = 'expandedLeftSidebarWidth';
@@ -4,7 +4,9 @@ import { Fragment, useCallback, useContext, useEffect, useMemo, useRef, useState
4
4
  import { css, Global, jsx } from '@emotion/react';
5
5
  import { bindAll } from 'bind-event-listener';
6
6
  import rafSchd from 'raf-schd';
7
- import { COLLAPSED_LEFT_SIDEBAR_WIDTH, DEFAULT_LEFT_SIDEBAR_WIDTH, IS_SIDEBAR_DRAGGING, MIN_LEFT_SIDEBAR_DRAG_THRESHOLD, RESIZE_BUTTON_SELECTOR, RESIZE_CONTROL_SELECTOR, VAR_LEFT_SIDEBAR_WIDTH } from '../../common/constants';
7
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
8
+ import { UNSAFE_useMediaQuery as useMediaQuery } from '@atlaskit/primitives/responsive';
9
+ import { COLLAPSED_LEFT_SIDEBAR_WIDTH, DEFAULT_LEFT_SIDEBAR_WIDTH, IS_SIDEBAR_DRAGGING, MIN_LEFT_SIDEBAR_DRAG_THRESHOLD, RESIZE_CONTROL_SELECTOR, VAR_LEFT_SIDEBAR_WIDTH } from '../../common/constants';
8
10
  import { getLeftPanelWidth, getLeftSidebarPercentage } from '../../common/utils';
9
11
  import { SidebarResizeContext } from '../../controllers/sidebar-resize-context';
10
12
  /* import useUpdateCssVar from '../../controllers/use-update-css-vars'; */
@@ -62,14 +64,13 @@ const ResizeControl = ({
62
64
  onResizeEnd
63
65
  }) => {
64
66
  const {
65
- expandLeftSidebar,
67
+ toggleLeftSidebar,
66
68
  collapseLeftSidebar,
67
69
  leftSidebarState,
68
70
  setLeftSidebarState
69
71
  } = useContext(SidebarResizeContext);
70
72
  const {
71
- isLeftSidebarCollapsed,
72
- isResizing
73
+ isLeftSidebarCollapsed
73
74
  } = leftSidebarState;
74
75
  const sidebarWidth = useRef(leftSidebarState[VAR_LEFT_SIDEBAR_WIDTH]);
75
76
  // Distance of mouse from left sidebar onMouseDown
@@ -77,6 +78,9 @@ const ResizeControl = ({
77
78
  const keyboardEventTimeout = useRef();
78
79
  const [isGrabAreaFocused, setIsGrabAreaFocused] = useState(false);
79
80
  const unbindEvents = useRef(null);
81
+ const mobileMediaQuery = getBooleanFF('platform.design-system-team.responsive-page-layout-left-sidebar_p8r7g') ?
82
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- With the feature flag, this does not apply as it should be static.
83
+ useMediaQuery('below.md') : null;
80
84
 
81
85
  // Used in some cases to ensure function references don't have to change
82
86
  // TODO: more functions could use `stableSidebarState` rather than `leftSidebarState`
@@ -84,23 +88,11 @@ const ResizeControl = ({
84
88
  useEffect(() => {
85
89
  stableSidebarState.current = leftSidebarState;
86
90
  }, [leftSidebarState]);
87
- const toggleSideBar = e => {
88
- if (isResizing) {
89
- return;
90
- }
91
- if (isLeftSidebarCollapsed) {
92
- expandLeftSidebar();
93
- } else {
94
- collapseLeftSidebar();
95
- }
96
-
97
- // Bring focus to the resize button if the grab area is
98
- // "clicked" using enter/space on keyboard.
99
- if (e && e.nativeEvent.detail === 0) {
100
- const resizeButton = document.querySelector(`[${RESIZE_BUTTON_SELECTOR}]`);
101
- resizeButton && resizeButton.focus();
102
- }
103
- };
91
+ const toggleSideBar = useCallback(event => {
92
+ // don't cascade down to the LeftSidebarOuter
93
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
94
+ toggleLeftSidebar();
95
+ }, [toggleLeftSidebar]);
104
96
  const onMouseDown = event => {
105
97
  if (isLeftSidebarCollapsed) {
106
98
  return;
@@ -340,7 +332,7 @@ const ResizeControl = ({
340
332
  css: [resizeControlStyles, (isGrabAreaFocused || isLeftSidebarCollapsed) && showResizeButtonStyles]
341
333
  }), jsx(Shadow, {
342
334
  testId: testId && `${testId}-shadow`
343
- }), jsx(GrabArea, {
335
+ }), !(mobileMediaQuery !== null && mobileMediaQuery !== void 0 && mobileMediaQuery.matches) && jsx(GrabArea, {
344
336
  role: "separator",
345
337
  "aria-label": resizeGrabAreaLabel,
346
338
  "aria-valuenow": leftSidebarPercentageExpanded,
@@ -355,7 +347,7 @@ const ResizeControl = ({
355
347
  isLeftSidebarCollapsed: isLeftSidebarCollapsed,
356
348
  disabled: isLeftSidebarCollapsed
357
349
  }), resizeButton.render(ResizeButton, {
358
- isLeftSidebarCollapsed,
350
+ isLeftSidebarCollapsed: mobileMediaQuery !== null && mobileMediaQuery !== void 0 && mobileMediaQuery.matches ? !leftSidebarState.isFlyoutOpen : isLeftSidebarCollapsed,
359
351
  label: resizeButtonLabel,
360
352
  onClick: toggleSideBar,
361
353
  testId: testId && `${testId}-resize-button`
@@ -5,6 +5,8 @@ import { css, jsx } from '@emotion/react';
5
5
  import ChevronRight from '@atlaskit/icon/glyph/chevron-right';
6
6
  import { easeOut } from '@atlaskit/motion/curves';
7
7
  import { mediumDurationMs, smallDurationMs } from '@atlaskit/motion/durations';
8
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
9
+ import { UNSAFE_media as media } from '@atlaskit/primitives/responsive';
8
10
  import { B100, B200, N0, N200, N30A } from '@atlaskit/theme/colors';
9
11
  import { RESIZE_BUTTON_SELECTOR } from '../../common/constants';
10
12
  const increaseHitAreaStyles = css({
@@ -14,10 +16,18 @@ const increaseHitAreaStyles = css({
14
16
  bottom: `calc(-1 * ${"var(--ds-space-100, 8px)"})`,
15
17
  left: `calc(-1 * ${"var(--ds-space-100, 8px)"})`
16
18
  });
19
+
20
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage -- With a feature flag, this does not apply
21
+ const mobileStyles = getBooleanFF('platform.design-system-team.responsive-page-layout-left-sidebar_p8r7g') ? css({
22
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
23
+ [media.below.md]: {
24
+ opacity: 1
25
+ }
26
+ }) : undefined;
17
27
  const resizeIconButtonStyles = css({
18
28
  width: 24,
19
29
  height: 24,
20
- padding: 0,
30
+ padding: "var(--ds-space-0, 0px)",
21
31
  position: 'absolute',
22
32
  top: "var(--ds-space-400, 32px)",
23
33
  left: 0,
@@ -74,10 +84,9 @@ const ResizeButton = ({
74
84
  "aria-expanded": !isLeftSidebarCollapsed,
75
85
  "aria-label": label,
76
86
  type: "button",
77
- css: [resizeIconButtonStyles, !isLeftSidebarCollapsed && resizeIconButtonExpandedStyles],
87
+ css: [resizeIconButtonStyles, mobileStyles, !isLeftSidebarCollapsed && resizeIconButtonExpandedStyles],
78
88
  "data-testid": testId
79
- // Prevents focus staying attached to the button
80
- // when pressed
89
+ // Prevents focus staying attached to the button when pressed
81
90
  ,
82
91
  onMouseDown: preventDefault
83
92
  // eslint-disable-next-line @repo/internal/react/no-unsafe-spread-props
@@ -6,7 +6,7 @@ import { easeOut, prefersReducedMotion } from '@atlaskit/motion';
6
6
  import { N30A, N60A } from '@atlaskit/theme/colors';
7
7
  import { DEFAULT_I18N_PROPS_SKIP_LINKS, PAGE_LAYOUT_CONTAINER_SELECTOR } from '../../common/constants';
8
8
  import { useSkipLinks } from '../../controllers';
9
- // eslint-disable-next-line @repo/internal/react/consistent-css-prop-usage
9
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
10
10
  const prefersReducedMotionStyles = css(prefersReducedMotion());
11
11
  const skipLinkStyles = css({
12
12
  // eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage-spacing
@@ -2,11 +2,38 @@
2
2
 
3
3
  import { css, jsx } from '@emotion/react';
4
4
  import { easeOut, prefersReducedMotion } from '@atlaskit/motion';
5
- import { BANNER_HEIGHT, LEFT_PANEL_WIDTH, LEFT_SIDEBAR_FLYOUT_WIDTH, LEFT_SIDEBAR_WIDTH, TOP_NAVIGATION_HEIGHT, TRANSITION_DURATION } from '../../../common/constants';
5
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
6
+ import { UNSAFE_media as media } from '@atlaskit/primitives/responsive';
7
+ import { BANNER_HEIGHT, LEFT_PANEL_WIDTH, LEFT_SIDEBAR_FLYOUT_WIDTH, LEFT_SIDEBAR_WIDTH, MAX_MOBILE_SIDEBAR_FLYOUT_WIDTH, MOBILE_COLLAPSED_LEFT_SIDEBAR_WIDTH, TOP_NAVIGATION_HEIGHT, TRANSITION_DURATION } from '../../../common/constants';
6
8
  import { useIsSidebarDragging } from '../../../common/hooks';
7
- // eslint-disable-next-line @repo/internal/react/consistent-css-prop-usage
9
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
8
10
  const prefersReducedMotionStyles = css(prefersReducedMotion());
9
11
 
12
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage -- With a feature flag, this does not apply
13
+ const mobileStyles = getBooleanFF('platform.design-system-team.responsive-page-layout-left-sidebar_p8r7g') ? css({
14
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
15
+ [media.below.md]: {
16
+ width: `${MOBILE_COLLAPSED_LEFT_SIDEBAR_WIDTH}px`,
17
+ position: 'fixed',
18
+ // eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage-spacing
19
+ top: `calc(${BANNER_HEIGHT} + ${TOP_NAVIGATION_HEIGHT})`,
20
+ bottom: 0,
21
+ // eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage-spacing
22
+ left: `${LEFT_PANEL_WIDTH}`,
23
+ transition: `width ${TRANSITION_DURATION}ms ${easeOut} 0s`
24
+ }
25
+ }) : undefined;
26
+
27
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage -- With a feature flag, this does not apply
28
+ const mobileInnerFlyoutStyles = getBooleanFF('platform.design-system-team.responsive-page-layout-left-sidebar_p8r7g') ? css({
29
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
30
+ [media.below.md]: {
31
+ width: `min(90vw, ${MAX_MOBILE_SIDEBAR_FLYOUT_WIDTH}px)`,
32
+ maxWidth: MAX_MOBILE_SIDEBAR_FLYOUT_WIDTH,
33
+ transition: `width ${TRANSITION_DURATION}ms ${easeOut} 0s, box-shadow ${TRANSITION_DURATION}ms ${easeOut} 0s`
34
+ }
35
+ }) : undefined;
36
+
10
37
  /**
11
38
  * This inner wrapper is required to allow the sidebar to be `position: fixed`.
12
39
  *
@@ -47,7 +74,11 @@ const LeftSidebarInner = ({
47
74
  }) => {
48
75
  const isDragging = useIsSidebarDragging();
49
76
  return jsx("div", {
50
- css: [!isFixed && staticInnerStyles, isFixed && fixedInnerStyles, isFixed && isFlyoutOpen && fixedInnerFlyoutStyles, isDragging && draggingStyles, prefersReducedMotionStyles]
77
+ css: [
78
+ // mobile breakpoint styles
79
+ mobileStyles, isFlyoutOpen && mobileInnerFlyoutStyles,
80
+ // generic styles
81
+ !isFixed && staticInnerStyles, isFixed && fixedInnerStyles, isFixed && isFlyoutOpen && fixedInnerFlyoutStyles, isDragging && draggingStyles, prefersReducedMotionStyles]
51
82
  }, children);
52
83
  };
53
84
 
@@ -1,17 +1,44 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  /** @jsx jsx */
3
- import { forwardRef } from 'react';
3
+ import { forwardRef, useContext } from 'react';
4
4
  import { css, jsx } from '@emotion/react';
5
5
  import { easeOut, prefersReducedMotion } from '@atlaskit/motion';
6
- import { COLLAPSED_LEFT_SIDEBAR_WIDTH, LEFT_SIDEBAR_FLYOUT_WIDTH, LEFT_SIDEBAR_WIDTH, TRANSITION_DURATION } from '../../../common/constants';
6
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
7
+ import { UNSAFE_media as media } from '@atlaskit/primitives/responsive';
8
+ import { COLLAPSED_LEFT_SIDEBAR_WIDTH, LEFT_SIDEBAR_FLYOUT_WIDTH, LEFT_SIDEBAR_WIDTH, MAX_MOBILE_SIDEBAR_FLYOUT_WIDTH, MOBILE_COLLAPSED_LEFT_SIDEBAR_WIDTH, TRANSITION_DURATION } from '../../../common/constants';
7
9
  import { useIsSidebarDragging } from '../../../common/hooks';
8
10
  import { getPageLayoutSlotSelector } from '../../../common/utils';
11
+ import { SidebarResizeContext } from '../../../controllers';
9
12
  import SlotFocusRing from './slot-focus-ring';
10
- // eslint-disable-next-line @repo/internal/react/consistent-css-prop-usage
13
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
11
14
  const prefersReducedMotionStyles = css(prefersReducedMotion());
15
+
16
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage -- With a feature flag, this does not apply
17
+ const mobileStyles = getBooleanFF('platform.design-system-team.responsive-page-layout-left-sidebar_p8r7g') ? css({
18
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
19
+ [media.below.md]: {
20
+ width: MOBILE_COLLAPSED_LEFT_SIDEBAR_WIDTH,
21
+ cursor: 'pointer',
22
+ opacity: 1,
23
+ transition: `width ${TRANSITION_DURATION}ms ${easeOut} 0s`,
24
+ '&::after': {
25
+ display: 'inline-block',
26
+ maxWidth: MAX_MOBILE_SIDEBAR_FLYOUT_WIDTH,
27
+ content: "''"
28
+ }
29
+ }
30
+ }) : undefined;
31
+
32
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage -- With a feature flag, this does not apply
33
+ const mobileFlyoutStyles = getBooleanFF('platform.design-system-team.responsive-page-layout-left-sidebar_p8r7g') ? css({
34
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
35
+ [media.below.md]: {
36
+ cursor: 'revert'
37
+ }
38
+ }) : undefined;
12
39
  const outerStyles = css({
13
40
  width: LEFT_SIDEBAR_WIDTH,
14
- marginLeft: 0,
41
+ marginLeft: "var(--ds-space-0, 0px)",
15
42
  position: 'relative',
16
43
  zIndex: 1,
17
44
  transition: `width ${TRANSITION_DURATION}ms ${easeOut} 0s`,
@@ -47,30 +74,46 @@ const selector = getPageLayoutSlotSelector('left-sidebar');
47
74
  const LeftSidebarOuter = ({
48
75
  children,
49
76
  isFixed = false,
50
- isFlyoutOpen = false,
77
+ // NOTE: We explicitly require this via props because of `LeftSidebarWithoutResize`
51
78
  testId,
52
79
  onMouseLeave,
53
80
  onMouseOver,
81
+ onClick,
54
82
  id
55
83
  }, ref) => {
56
84
  const isDragging = useIsSidebarDragging();
85
+ const {
86
+ leftSidebarState: {
87
+ isFlyoutOpen
88
+ }
89
+ } = useContext(SidebarResizeContext);
57
90
  return jsx(SlotFocusRing, {
58
91
  isSidebar: true
59
92
  }, ({
60
93
  className
61
94
  }) =>
62
95
  /**
63
- * The mouse handlers control flyout behavior, a mouse-only experience.
96
+ * On desktop, the `onMouseOver|onMouseLeave` handlers controls the temporary flyout behavior.
97
+ * This is an intentionally mouse-only experience, it may even be disruptive with keyboard navigation.
98
+ *
99
+ * On mobile, the `onClick` handler controls the toggled flyout behaviour.
100
+ * This is not intended to be how you use this with a keyboard, there is a ResizeButton for this intentionally instead.
64
101
  */
65
102
  // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
66
103
  jsx("div", _extends({
67
- css: [outerStyles, isFixed && fixedStyles, isFlyoutOpen && flyoutStyles, isFlyoutOpen && isFixed && flyoutFixedStyles, isDragging && draggingStyles, prefersReducedMotionStyles],
104
+ css: [
105
+ // mobile breakpoint styles
106
+ mobileStyles, isFlyoutOpen && mobileFlyoutStyles,
107
+ // generic styles
108
+ outerStyles, isFixed && fixedStyles, isFlyoutOpen && flyoutStyles, isFlyoutOpen && isFixed && flyoutFixedStyles, isDragging && draggingStyles, prefersReducedMotionStyles],
68
109
  className: className,
69
110
  "data-testid": testId,
70
111
  id: id,
71
112
  onMouseOver: onMouseOver,
72
113
  onMouseLeave: onMouseLeave,
73
- ref: ref
114
+ onClick: onClick,
115
+ ref: ref,
116
+ "aria-hidden": "true"
74
117
  }, selector), children));
75
118
  };
76
119
  export default /*#__PURE__*/forwardRef(LeftSidebarOuter);
@@ -4,7 +4,7 @@ import { css, jsx } from '@emotion/react';
4
4
  import { prefersReducedMotion } from '@atlaskit/motion';
5
5
  import { TRANSITION_DURATION } from '../../../common/constants';
6
6
  import { useIsSidebarCollapsing } from '../../../common/hooks';
7
- // eslint-disable-next-line @repo/internal/react/consistent-css-prop-usage
7
+ // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
8
8
  const prefersReducedMotionStyles = css(prefersReducedMotion());
9
9
 
10
10
  /**